/** * @file bsp_usart.c * @brief USART1 init, printf retarget (fputc, polling TX), DMA TX and * RX interrupt byte queue. */ #include "bsp_usart.h" #include #include "FreeRTOS.h" #include "task.h" #include "queue.h" #include "semphr.h" #define RX_QUEUE_LENGTH 64 static QueueHandle_t s_rxQueue = NULL; static SemaphoreHandle_t s_txMutex = NULL; /** * @brief Initialize USART1 (115200 8N1) on PB6(TX)/PB7(RX), * DMA TX channel and the RX interrupt queue. */ void bsp_usart_init(void) { GPIO_InitType GPIO_InitStructure; USART_InitType USART_InitStructure; NVIC_InitType NVIC_InitStructure; /* Enable GPIOB and USART1 clocks */ RCC_EnableAPB2PeriphClk(BSP_USART_GPIO_CLK, ENABLE); RCC_EnableAPB2PeriphClk(BSP_USART_CLK, ENABLE); /* Configure USART1 TX (PB6) as alternate function push-pull */ GPIO_InitStruct(&GPIO_InitStructure); GPIO_InitStructure.Pin = BSP_USART_TX_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP; GPIO_InitStructure.GPIO_Alternate = BSP_USART_TX_GPIO_AF; GPIO_InitPeripheral(BSP_USART_GPIO, &GPIO_InitStructure); /* Configure USART1 RX (PB7) as alternate function */ GPIO_InitStructure.Pin = BSP_USART_RX_PIN; GPIO_InitStructure.GPIO_Alternate = BSP_USART_RX_GPIO_AF; GPIO_InitPeripheral(BSP_USART_GPIO, &GPIO_InitStructure); USART_InitStructure.BaudRate = BSP_USART_BAUDRATE; USART_InitStructure.WordLength = USART_WL_8B; USART_InitStructure.StopBits = USART_STPB_1; USART_InitStructure.Parity = USART_PE_NO; USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE; USART_InitStructure.Mode = USART_MODE_RX | USART_MODE_TX; USART_Init(BSP_USARTx, &USART_InitStructure); /* USART1 TX DMA: CH1 remapped to USART1_TX, configured per transfer */ RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA, ENABLE); DMA_DeInit(BSP_USART_TX_DMA_CH); DMA_RequestRemap(BSP_USART_TX_DMA_REMAP, DMA, BSP_USART_TX_DMA_CH, ENABLE); USART_EnableDMA(BSP_USARTx, USART_DMAREQ_TX, ENABLE); /* RX byte queue fed by the RXDNE interrupt */ s_rxQueue = xQueueCreate(RX_QUEUE_LENGTH, sizeof(uint8_t)); s_txMutex = xSemaphoreCreateMutex(); USART_ConfigInt(BSP_USARTx, USART_INT_RXDNE, ENABLE); /* Lowest priority: the ISR calls FreeRTOS FromISR APIs */ NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPriority = 3; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); USART_Enable(BSP_USARTx, ENABLE); } /** * @brief Send a buffer over USART1 TX using DMA, polled completion. */ void bsp_usart_write_dma(const uint8_t* data, uint16_t len) { DMA_InitType DMA_InitStructure; if (data == NULL || len == 0) { return; } DMA_EnableChannel(BSP_USART_TX_DMA_CH, DISABLE); DMA_ClearFlag(BSP_USART_TX_DMA_TC, DMA); DMA_InitStructure.PeriphAddr = BSP_USART_DAT_ADDR; DMA_InitStructure.MemAddr = (uint32_t)data; DMA_InitStructure.Direction = DMA_DIR_PERIPH_DST; DMA_InitStructure.BufSize = len; DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE; DMA_InitStructure.DMA_MemoryInc = DMA_MEM_INC_ENABLE; DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_BYTE; DMA_InitStructure.MemDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.CircularMode = DMA_MODE_NORMAL; DMA_InitStructure.Priority = DMA_PRIORITY_VERY_HIGH; DMA_InitStructure.Mem2Mem = DMA_M2M_DISABLE; DMA_Init(BSP_USART_TX_DMA_CH, &DMA_InitStructure); DMA_EnableChannel(BSP_USART_TX_DMA_CH, ENABLE); while (DMA_GetFlagStatus(BSP_USART_TX_DMA_TC, DMA) == RESET) { } DMA_EnableChannel(BSP_USART_TX_DMA_CH, DISABLE); DMA_ClearFlag(BSP_USART_TX_DMA_TC, DMA); /* Wait for the last byte to leave the shift register */ while (USART_GetFlagStatus(BSP_USARTx, USART_FLAG_TXC) == RESET) { } } /** * @brief Fetch one byte received by the RX interrupt. * @param ch output byte * @param timeout_ms BSP_USART_WAIT_FOREVER or a timeout in ms * @return 1 on success, 0 on timeout */ int bsp_usart_read_byte(uint8_t* ch, uint32_t timeout_ms) { TickType_t ticks; if (s_rxQueue == NULL) { return 0; } ticks = (timeout_ms == BSP_USART_WAIT_FOREVER) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms); return (xQueueReceive(s_rxQueue, ch, ticks) == pdPASS) ? 1 : 0; } /** * @brief Take the TX mutex (no-op before the scheduler runs). */ void bsp_usart_tx_lock(void) { if (s_txMutex != NULL && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) { xSemaphoreTake(s_txMutex, portMAX_DELAY); } } /** * @brief Give the TX mutex. */ void bsp_usart_tx_unlock(void) { if (s_txMutex != NULL && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) { xSemaphoreGive(s_txMutex); } } /** * @brief RXDNE interrupt handler, pushes the received byte into the RX queue. * Called from USART1_IRQHandler (see n32wb03x_it.c). */ void bsp_usart_rx_isr_handler(void) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; if (USART_GetIntStatus(BSP_USARTx, USART_INT_RXDNE) != RESET) { /* Reading DAT clears the RXDNE flag */ uint8_t ch = (uint8_t)USART_ReceiveData(BSP_USARTx); if (s_rxQueue != NULL) { xQueueSendFromISR(s_rxQueue, &ch, &xHigherPriorityTaskWoken); } portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } /* Retarget the C library printf function to USART1 (polling TX) */ int fputc(int ch, FILE* f) { /* Send '\r' before '\n' so that serial terminals display correctly */ if (ch == '\n') { USART_SendData(BSP_USARTx, (uint8_t)'\r'); while (USART_GetFlagStatus(BSP_USARTx, USART_FLAG_TXDE) == RESET) { } } USART_SendData(BSP_USARTx, (uint8_t)ch); while (USART_GetFlagStatus(BSP_USARTx, USART_FLAG_TXDE) == RESET) { } return ch; }