From 649c2e2b24201974449ec3d49711476bf80f5de2 Mon Sep 17 00:00:00 2001 From: Trent Gill Date: Tue, 4 Jun 2024 11:55:43 -0700 Subject: [PATCH] midi in and out working in a very basic form --- Makefile | 1 + lib/midi.c | 23 +++++++++++ lib/midi.h | 14 +++++++ ll/system.c | 56 +++++++++++++++++++++----- ll/timers.c | 1 + ll/uart.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ll/uart.h | 21 ++++++++++ main.c | 57 ++++++++++++++++++--------- 8 files changed, 256 insertions(+), 28 deletions(-) create mode 100644 lib/midi.c create mode 100644 lib/midi.h create mode 100644 ll/uart.c create mode 100644 ll/uart.h diff --git a/Makefile b/Makefile index f0b931f..bda3ac5 100644 --- a/Makefile +++ b/Makefile @@ -114,6 +114,7 @@ SRC = main.c \ lib/metro.c \ lib/shapes.c \ lib/slopes.c \ + lib/midi.c \ $(wildcard ll/*.c) \ $(wildcard usbd/*.c) \ $(USBD)/Core/Src/usbd_core.c \ diff --git a/lib/midi.c b/lib/midi.c new file mode 100644 index 0000000..d81c4cc --- /dev/null +++ b/lib/midi.c @@ -0,0 +1,23 @@ +#include "midi.h" + +#include +#include "caw.h" + +static void MIDI_handler(uint8_t byte); + +void MIDI_init(Midi* m, Uart* transmit){ + m->uart_tx = transmit; +} + +void MIDI_transmit(Midi* m, uint8_t* bytes, int length){ + // FIXME this should be passed in as a protocol + UART_send(m->uart_tx, bytes, length); +} + +U8_Callback MIDI_get_callback(Midi* m){ + return &MIDI_handler; +} + +static void MIDI_handler(uint8_t byte){ + Caw_printf("m 0x%x\n\r", byte); +} diff --git a/lib/midi.h b/lib/midi.h new file mode 100644 index 0000000..09045a4 --- /dev/null +++ b/lib/midi.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include "../ll/uart.h" // this requirement should be dropped by replacing Uart* with a "protocol" of pointer + operations + +typedef struct{ + Uart* uart_tx; +} Midi; + +void MIDI_init(Midi* m, Uart* transmit); + +void MIDI_transmit(Midi* m, uint8_t* bytes, int length); + +U8_Callback MIDI_get_callback(Midi* m); diff --git a/ll/system.c b/ll/system.c index ca4d2fd..f57c923 100644 --- a/ll/system.c +++ b/ll/system.c @@ -82,22 +82,60 @@ static void MPU_Config(void) HAL_MPU_Disable(); - // Configure the MPU attributes as WT for SRAM +/* + // disable speculative access to unused memory region mpu.Enable = MPU_REGION_ENABLE; - // mpu.Enable = MPU_REGION_DISABLE; - mpu.BaseAddress = 0x20020000; - // mpu.BaseAddress = 0x20000000; - mpu.Size = MPU_REGION_SIZE_256KB; - mpu.AccessPermission = MPU_REGION_FULL_ACCESS; - mpu.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; - mpu.IsCacheable = MPU_ACCESS_CACHEABLE; - mpu.IsShareable = MPU_ACCESS_SHAREABLE; mpu.Number = MPU_REGION_NUMBER0; + mpu.BaseAddress = 0; + mpu.Size = MPU_REGION_SIZE_4GB; + mpu.SubRegionDisable = 0x87; mpu.TypeExtField = MPU_TEX_LEVEL0; + mpu.AccessPermission = MPU_REGION_NO_ACCESS; + mpu.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + mpu.IsShareable = MPU_ACCESS_SHAREABLE; + mpu.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; + mpu.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; + HAL_MPU_ConfigRegion(&mpu); + */ + + // disable speculative access to unused memory region + mpu.Enable = MPU_REGION_ENABLE; + mpu.Number = MPU_REGION_NUMBER1; + mpu.BaseAddress = 0x20000000; + mpu.Size = MPU_REGION_SIZE_512KB; mpu.SubRegionDisable = 0x00; + mpu.TypeExtField = MPU_TEX_LEVEL0; + mpu.AccessPermission = MPU_REGION_FULL_ACCESS; mpu.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; + mpu.IsShareable = MPU_ACCESS_SHAREABLE; + mpu.IsCacheable = MPU_ACCESS_CACHEABLE; + mpu.IsBufferable = MPU_ACCESS_BUFFERABLE; HAL_MPU_ConfigRegion(&mpu); +/* + // very conservative (no cache) to try and crush bug + mpu.Enable = MPU_REGION_ENABLE; + mpu.Number = MPU_REGION_NUMBER1; + // mpu.BaseAddress = 0x20020000; + mpu.BaseAddress = 0x20000000; + mpu.Size = MPU_REGION_SIZE_512KB; + mpu.SubRegionDisable = 0x07; + + // 2 options for normal mode with writeback approach + // no write allocate: tex=0, cachable & bufferable, sharable optional + // write & read allocate: tex=1, cachable & bufferable, shareable optional + + // best practice is to place dma buffers at a specific location & set that region only as shareable + // then the remainder of ram can stay as cachable + + mpu.TypeExtField = MPU_TEX_LEVEL0; + mpu.AccessPermission = MPU_REGION_FULL_ACCESS; + mpu.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; + mpu.IsShareable = MPU_ACCESS_NOT_SHAREABLE; // shareable essentially disables cache + mpu.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; + mpu.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; + // HAL_MPU_ConfigRegion(&mpu); +*/ HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); // // Configure the MPU attributes as WT for SRAM diff --git a/ll/timers.c b/ll/timers.c index 63f1191..68fffd8 100644 --- a/ll/timers.c +++ b/ll/timers.c @@ -4,6 +4,7 @@ #include #include "interrupts.h" +#include "../lib/caw.h" #define MAX_LL_TIMERS 11 // tell caller how many timers can be allocated diff --git a/ll/uart.c b/ll/uart.c new file mode 100644 index 0000000..f9f6071 --- /dev/null +++ b/ll/uart.c @@ -0,0 +1,111 @@ +#include "uart.h" + +#include +#include "../lib/caw.h" +#include "debug_pin.h" + +static Uart* selves[2]; // for callbacks +static Uart* _self; // for MSP init + +static uint8_t rx_buffer[1] = {0}; // buffer for reception. TODO extend to circular buffer & pass length + +Uart* UART_init(Uart* self, UART_Direction dir, USART_TypeDef* instance, char* pin){ + selves[dir] = self; // for interrupt lookup + + self->hUart.Instance = instance; + self->hUart.Init.BaudRate = 31250; + self->hUart.Init.WordLength = UART_WORDLENGTH_8B; + self->hUart.Init.StopBits = UART_STOPBITS_1; + self->hUart.Init.Parity = UART_PARITY_NONE; + self->hUart.Init.HwFlowCtl = UART_HWCONTROL_NONE; + self->hUart.Init.OverSampling = UART_OVERSAMPLING_16; + + if(dir == UART_Rx){ + self->hUart.Init.Mode = UART_MODE_RX; + } else if(dir == UART_Tx){ + self->hUart.Init.Mode = UART_MODE_TX; + } + + _self = self; + if(HAL_UART_Init(&self->hUart) != HAL_OK){ + Caw_printf("uart failed to init %i\n\r", dir); + Debug_Pin_Set(2, 1); + } + _self = NULL; + + return self; +} + +void UART_set_callback(Uart* self, U8_Callback callback){ + self->cb_handler = callback; + + // start reception loop once callback is registered + if(HAL_UART_Receive_IT(&self->hUart, rx_buffer, 1) != HAL_OK){ + Caw_printf("failed to start reception\n\r"); + Debug_Pin_Set(2, 1); + } +} + +void UART_send(Uart* self, uint8_t* data, size_t len){ + // FIXME currently blocking! + // TODO use IRQ mode & ignore response, or use DMA mode if we need longer messages (sysex) + HAL_UART_Transmit(&self->hUart, data, len, 1000); +// HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) +// HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) +} + + +// FIXME all the pin mapping is hard coded right now +// FIXME interrupt priorities. only needed on RX channel +void HAL_UART_MspInit(UART_HandleTypeDef* huart){ + GPIO_InitTypeDef g; + g.Mode = GPIO_MODE_AF_PP; + g.Pull = GPIO_PULLUP; + g.Speed = GPIO_SPEED_FAST; + RCC_PeriphCLKInitTypeDef rcc; + + // Select SysClk as source of USART1 clocks + if(huart->Instance == UART5){ + __GPIOB_CLK_ENABLE(); // FIXME auto-select from _self->pinname + rcc.PeriphClockSelection = RCC_PERIPHCLK_UART5; + rcc.Uart5ClockSelection = RCC_UART5CLKSOURCE_SYSCLK; + HAL_RCCEx_PeriphCLKConfig(&rcc); + __UART5_CLK_ENABLE(); + g.Pin = GPIO_PIN_8; // FIXME + g.Alternate = GPIO_AF7_UART5; // FIXME depends on pin mapping + HAL_GPIO_Init(GPIOB, &g); + HAL_NVIC_SetPriority(UART5_IRQn, 3, 1); + HAL_NVIC_EnableIRQ(UART5_IRQn); + } else if(huart->Instance == UART8){ + __GPIOE_CLK_ENABLE(); // FIXME auto-select from _self->pinname + rcc.PeriphClockSelection = RCC_PERIPHCLK_UART8; + rcc.Uart8ClockSelection = RCC_UART8CLKSOURCE_SYSCLK; + HAL_RCCEx_PeriphCLKConfig(&rcc); + __UART8_CLK_ENABLE(); + g.Pin = GPIO_PIN_1; // FIXME + g.Alternate = GPIO_AF8_UART8; // FIXME depends on pin mapping + HAL_GPIO_Init(GPIOE, &g); + // no callback for TX + // HAL_NVIC_SetPriority(UART8_IRQn, 3, 0); + // HAL_NVIC_EnableIRQ(UART8_IRQn); + } +} + +void UART5_IRQHandler(void){ + HAL_UART_IRQHandler(&selves[UART_Rx]->hUart); +} + +void HAL_UART_TxCpltCallback(UART_HandleTypeDef* h){ + // UNUSED +} + +void HAL_UART_RxCpltCallback(UART_HandleTypeDef* h){ + selves[UART_Rx]->cb_handler(rx_buffer[0]); + if(HAL_UART_Receive_IT(&selves[UART_Rx]->hUart, rx_buffer, 1) != HAL_OK){ + Caw_printf("failed to restart reception\n\r"); + } +} + +void HAL_UART_ErrorCallback(UART_HandleTypeDef* h){ + Caw_printf("UART error %i\n\r", h->ErrorCode); +} diff --git a/ll/uart.h b/ll/uart.h new file mode 100644 index 0000000..f862acf --- /dev/null +++ b/ll/uart.h @@ -0,0 +1,21 @@ +#pragma once + +#include "stm32f7xx_hal.h" + +typedef enum{ + UART_Rx, + UART_Tx, +} UART_Direction; + +typedef void (*U8_Callback)(uint8_t); + +typedef struct{ + UART_HandleTypeDef hUart; + U8_Callback cb_handler; +} Uart; + +Uart* UART_init(Uart* self, UART_Direction dir, USART_TypeDef* instance, char* pin); + +void UART_set_callback(Uart* self, U8_Callback callback); + +void UART_send(Uart* self, uint8_t* data, size_t len); diff --git a/main.c b/main.c index 37f993a..eea0a93 100755 --- a/main.c +++ b/main.c @@ -19,6 +19,12 @@ #include "lib/flash.h" // Flash_clear_user_script() #include "stm32f7xx_it.h" // CPU_count; +#include "lib/midi.h" +#include "ll/uart.h" + +static Uart uart_rx; +static Uart uart_tx; +static Midi midi; int main(void) { @@ -27,29 +33,35 @@ int main(void) // Debugging Debug_Pin_Init(); Debug_Pin_Set(0,1); - Debug_USART_Init(); // ignored in TRACE mode + // Debug_USART_Init(); // ignored in TRACE mode // User-readable status led - // status_led_init(); + status_led_init(); // status_led_fast(LED_SLOW); // slow blink until USB connection goes live // status_led_set(1); // set status to ON to show sign of life straight away printf("\n\nhi from crow!\n\r"); - - U_PrintNow(); + + // MIDI + // pass initialized uart handlers to midi system + MIDI_init(&midi, UART_init(&uart_tx, UART_Tx, UART8, "E1")); + // setup midi reception uart + UART_init(&uart_rx, UART_Rx, UART5, "B8"); + // redirect uart interrupts to midi system + UART_set_callback(&uart_rx, MIDI_get_callback(&midi)); // Drivers int max_timers = Timer_Init(); - IO_Init( max_timers-2 ); // use second-last timer - IO_Start(); // must start IO before running lua init() script - events_init(); - Metro_Init( max_timers-2 ); // reserve 2 timers for USB & ADC + // IO_Init( max_timers-2 ); // use second-last timer + // IO_Start(); // must start IO before running lua init() script + // events_init(); + // Metro_Init( max_timers-2 ); // reserve 2 timers for USB & ADC // clock_init( 100 ); // TODO how to pass it the timer? Caw_Init( max_timers-1 ); // use last timer CDC_clear_buffers(); // i2c_hw_pullups_init(); // enable GPIO for v1.1 hardware pullups // ii_init( II_CROW ); - Random_Init(); + // Random_Init(); // REPL_init( Lua_Init() ); @@ -59,6 +71,7 @@ int main(void) uint32_t last_tick = HAL_GetTick(); int saw = 0; int g_state = 0; + int counter = 0; while(1){ CPU_count++; @@ -68,10 +81,14 @@ int main(void) Debug_Pin_Set(1, g_state); g_state ^= 1; // Caw_printf("hi\n\r"); + // Caw_printf("%i\n\r",counter++); + uint8_t midi_msg[3] = {0x90, 0x3c, 0x64}; + MIDI_transmit(&midi, midi_msg, 3); } - U_PrintNow(); - Caw_try_receive(); + // U_PrintNow(); + Caw_try_receive(); // something is broken in the receiver :/ + // prob something to do with the different chip? // switch( Caw_try_receive() ){ // true on pressing 'enter' // case C_repl: REPL_eval( Caw_get_read() // , Caw_get_read_len() @@ -90,14 +107,16 @@ int main(void) // case C_loadFirst: REPL_default_script(); break; // default: break; // 'C_none' does nothing // } - Random_Update(); - uint32_t time_now = HAL_GetTick(); // for running a 1ms-interval tick - if( last_tick != time_now ){ // called on 1ms interval - last_tick = time_now; - // clock_update(time_now); - status_led_tick(time_now); - } - event_next(); // check/execute single event + // Random_Update(); + + // uint32_t time_now = HAL_GetTick(); // for running a 1ms-interval tick + // if( last_tick != time_now ){ // called on 1ms interval + // last_tick = time_now; + // // clock_update(time_now); + // status_led_tick(time_now); + // } + + // event_next(); // check/execute single event // ii_leader_process(); Caw_send_queued(); }