/** @file serial_stm32.c @brief serial handling using CubeMx @platform developed on STM32F407 using ARM-CC @see for DMA receive see https://my.st.com/public/STe2ecommunities/mcu/Lists/STM32Java/Flat.aspx?RootFolder=https%3a%2f%2fmy.st.com%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fSTM32Java%2fModifying%20STM32FCubeMX%20HAL%20Drivers%20for%20UART%20RX%20%28IT%20mode%29%20with%20unknown%20size%20data%20size&FolderCTID=0x01200200770978C69A1141439FE559EB459D758000F9A0E3A95BA69146A17C2E80209ADC21¤tviews=1320 the library uses TX and RX DMA RX DMA is in circular mode TX DMA is in linear mode the application is responsible for creating huart structures with initialized blocks: gpio, uart, dma what the lib does for you is the application circular buffer on TX and RX NVIC priorities must be: Systick, USART-DMA, USART-ISR; in this ascending order */ #include #include #include "SEGGER_RTT.h" #include "serial_stm32.h" static serial_ctx_t *serial_first = NULL; /// detect responsible serial context from given USART handle static serial_ctx_t *serial_huart_to_ctx(UART_HandleTypeDef *huart) { serial_ctx_t *ctx = serial_first; while(ctx != NULL) { if(ctx->huart == huart) { return ctx; } ctx = ctx->next; } return NULL; } /** status of serial activity @param dirout true for outgoing transfer @return true if there is ongoing transfer */ bool serial_status(serial_ctx_t *ctx, bool dirout) { (void) ctx->huart->Instance->SR; if(dirout != false) { // out if(fifo_isempty(&ctx->fifo_tx) == 0) { return true; } } else { // in if(ctx->rx_capacity != 0) { size_t idx = ctx->rx_capacity - __HAL_DMA_GET_COUNTER(ctx->huart->hdmarx); if(ctx->rx_size != idx) { return true; } } } return false; } /** initialize the structure and start the RX proccess @param ctx pointer to context structure to be filled @param huart pointer to HAL uart structure already initialized @param tx_buffer fifo area for transmitting (via DMA), NULL for using synchronous transmit */ void serial_init(serial_ctx_t *ctx, UART_HandleTypeDef *huart, uint8_t *rx_buffer, serial_size_t rx_len, uint8_t *tx_buffer, serial_size_t tx_len) { // insert ctx to internal list if(serial_huart_to_ctx(huart) == NULL) { ctx->next = serial_first; serial_first = ctx; } ctx->huart = huart; HAL_UART_DMAStop(ctx->huart); // receive ctx->rx_data = rx_buffer; ctx->rx_capacity = rx_len; ctx->rx_size = 0; // transmit if((tx_buffer != NULL) && (tx_len > 1)) { fifo_init(&ctx->fifo_tx, tx_buffer, tx_len - 1); } else { fifo_init(&ctx->fifo_tx, NULL, 0); } /// @todo exclude the rxstart from the init function /// @todo review why? uint32_t val = ctx->huart->Instance->SR; val = ctx->huart->Instance->DR; // dummy write ctx->huart->Instance->DR = 0; // __HAL_UART_FLUSH_DRREGISTER(ctx->huart); int loop = 5; while((loop > 0) && (HAL_UART_Receive_DMA(ctx->huart, rx_buffer, rx_len) != HAL_OK)) { loop--; } if(loop == 0) { loop = 3; } // disable Error interrupt CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE); } /** tune baudrate @note serial_baudrate must be called AFTER any HAL_UART_Init() in order to preset the huart.gState and huart.RxState */ void serial_baudrate(serial_ctx_t *ctx, uint32_t baudrate) { if((ctx == NULL) || (ctx->huart == NULL) || (ctx->huart->gState == HAL_UART_STATE_RESET)) { return; } SEGGER_RTT_printf(0, "\r\nserial_baudrate: old:%d, new:%d, gState:%x, rxState:%x ", ctx->huart->Init.BaudRate, baudrate, ctx->huart->gState, ctx->huart->RxState); if(ctx->huart->Init.BaudRate == baudrate) { return; } // serial bool is_running = (ctx->huart->gState > HAL_UART_STATE_READY) || (ctx->huart->RxState > HAL_UART_STATE_READY); if(is_running) { HAL_UART_DMAStop(ctx->huart); } // this is the only function that set the baudrate ctx->huart->Init.BaudRate = baudrate; HAL_UART_Init(ctx->huart); // restart receiving if it was before if(is_running) { serial_init(ctx, ctx->huart, ctx->rx_data, ctx->rx_capacity, ctx->fifo_tx.data, (ctx->fifo_tx.data==NULL)?0:ctx->fifo_tx.top+1); } } serial_size_t serial_read(serial_ctx_t *ctx, uint8_t *c) { if(ctx->rx_capacity != 0) { uint16_t head = ctx->rx_capacity - __HAL_DMA_GET_COUNTER(ctx->huart->hdmarx); if(ctx->rx_size != head) { *c = ctx->rx_data[ctx->rx_size]; ctx->rx_size++; if(ctx->rx_size >= ctx->rx_capacity) { ctx->rx_size = 0; } return 1; } } return 0; } serial_size_t serial_read_buf(serial_ctx_t *ctx, uint8_t *buffer, serial_size_t len) { if(ctx->rx_data == NULL) { return 0; } serial_size_t count = 0; while(count < len) { if(serial_read(ctx, buffer++) == 0) { break; } count++; } return count; } /** write data to serial interface @param buffer pointer to given data @param len number of bytes to write @return number of bytes written The function caches the data if possible and returns immediately. The size of the chache is given by the init function. */ serial_size_t serial_write_buf(serial_ctx_t *ctx, const uint8_t *buffer, const serial_size_t len) { if(len == 0) { return 0; } if(ctx->fifo_tx.data == NULL) { // if not using buffering, send direct, synchron HAL_UART_Transmit(ctx->huart, (uint8_t *)buffer, len, 100); // cast as that API is not const correct return len; } __HAL_DMA_DISABLE_IT(ctx->huart->hdmatx, DMA_IT_TC); serial_size_t sent = fifo_write_buf(&ctx->fifo_tx, buffer, len); // number of sent bytes // check ongoing traffic if(fifo_islocked(&ctx->fifo_tx) == false) { // activate tx uint8_t *txdata; uint16_t towrite = fifo_read_locked(&ctx->fifo_tx, &txdata); // this reenable txcmplt ISR HAL_UART_Transmit_DMA(ctx->huart, txdata, towrite); } else { // reenable ISR __HAL_DMA_ENABLE_IT(ctx->huart->hdmatx, DMA_IT_TC); } return sent; } /** callback from tx complete the function is used to update the chache pointer */ void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { // detect the responsible context /// @todo is there a better function serial_ctx_t *ctx = serial_huart_to_ctx(huart); if(ctx != NULL) { // release locked buffer fifo_read_unlock(&ctx->fifo_tx); // activate tx uint8_t *txdata; uint16_t towrite = fifo_read_locked(&ctx->fifo_tx, &txdata); if(towrite != 0) { HAL_UART_Transmit_DMA(ctx->huart, txdata, towrite); } } } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { SEGGER_RTT_printf(0, "\nHAL_UART_RxCpltCallback, ERROR:%X, State:%x, RxState:%x. ", (int)huart->ErrorCode, huart->gState, huart->RxState); } void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) { SEGGER_RTT_printf(0, "\nHAL_UART_ErrorCallback., ERROR:%X, rxdma:%x, State:%x, RxState:%x. ", (int)huart->ErrorCode, huart->hdmarx->ErrorCode, huart->gState, huart->RxState); } bool serial_abovewatermark(serial_ctx_t *ctx) { serial_size_t size = (ctx->fifo_tx.in - ctx->fifo_tx.out) & ctx->fifo_tx.top; if(size > (ctx->fifo_tx.top / 3)) { // above watermark return true; } return false; } void serial_close(serial_ctx_t *ctx) { HAL_UART_DMAStop(ctx->huart); }