The clasic way:
- Generate project by Cube, enable USART in asynchronous mode and enable global interrupt
- Insert function HAL_UART_Receive_IT(…) to main() function before while() loop.
- Write HAL_UART_RxCpltCallback(…) function for interrupt callback and insert code for processing RX data
This way has a drawback – you have to know the amount of received data. In this case RXBUFFERSIZE 10. Callback function si called only when 10 characters have been received. And in my case it didn’ work well, sometimes interrupt was not executed. (STM32F103C8T6, CubeIDE 1.4.2, CubeMX 6.0.1, FW F1xx 1.8.2)
Although it is possible to set buffer size to 1, interupt callback function is then called after receiving every character. It worked, but sometimes it stuck on interrupt. I didn’t try to solve it, i had read somwhere at st.com forum that this way is not recomended.
main.c
Define buffer size:
/* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ #define RXBUFFERSIZE 10 /* USER CODE END PD */
Define buffer
/* Private variables ---------------------------------------------------------*/ UART_HandleTypeDef huart1; /* USER CODE BEGIN PV */ volatile uint8_t rxBuff[RXBUFFERSIZE] /* USER CODE END PV */
Insert usart IT start function HAL_UART_Receive_IT() before while {}:
int main(void) { . . . MX_USART3_UART_Init(); /* USER CODE BEGIN 2 */ HAL_UART_Receive_IT(&huart1, (uint8_t *)rxBuff, RXBUFFERSIZE); /* USER CODE END 2 */
Write callback function HAL_UART_RxCpltCallback() (which is defined in stm32f1xx_hal_uart.c) and insert code for processing rx data.
/* USER CODE BEGIN 4 */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(UartHandle->Instance == USART1) { // HERE INSERT THE CODE FOR PROCESS RX DATA: YourFunctionForProcessRXData((uint8_t *)rxd); //THIS FUNCTION RE-ENABLES INTERRUPT HAL_UART_Receive_IT(&huart1, (uint8_t *)rxd, 1); } } /* USER CODE END 4 */
The different way
In this way the interrupt is executed when every character is received and doesn’t use HAL_UART_Receive_IT(…) and HAL_UART_RxCpltCallback(…) functions. I found it here:
https://community.st.com/s/question/0D50X00009XkggBSAR/stm32cube-uart-receive-interrupt
- Generate project by CubeMX, enable USART in asynchronous mode and enable global interrupt
- Insert code in main.c before while{} to enable appropriate interupts
- Insert code to stm32f1xx_it into USART1_IRQHandler to handle interrupt
- Write function called from USART1_IRQHandler to process data, which fills receive buffer and sets a flag when buffer is ful or for example ‘\r’ (or ‘\n’ ) is received (enter key)
Step 1 – main.c
int main(void) { . . . MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ // enable interrupts __HAL_UART_ENABLE_IT(&huart3, UART_IT_RXNE); HAL_NVIC_SetPriority(USART3_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART3_IRQn); /* USER CODE BEGIN 2 */
Step 2 – stm32f1xx_it.c
Define global buffer variable and write code inside USART1_IRQHandler:
uint8_t incomingChars; . . void USART1_IRQHandler(void) { /* USER CODE BEGIN USART1_IRQn 0 */ ich = USART1->DR; Interrupt_USART1(&incomingChars,sizeof(incomingChars)); //it is not necessary to call HAL_UART_IRQHandler(), so return. return; /* USER CODE END USART3_IRQn 0 */ HAL_UART_IRQHandler(&huart3); /* USER CODE BEGIN USART3_IRQn 1 */ /* USER CODE END USART1_IRQn 1 */ }
Step 3 – main.c
Write function which will be called by USART1_IRQHandler interupt routine:
void Interrupt_USART3(uint8_t *pData, uint16_t Size) { /* - Insert code for filling rx buffer - Set a flag when Enter is received or buffer is full - Write code in main() while() loop to process data and reset flag */ // if(pData[k] != '\n' && rxDataReceivedFlag == 0) { //skip '\n' if(i < USART_RXBUFFERSIZE && pData[k] != '\r' ) { //check carriage return USARTreceiveBuffer[i] = pData[k]; //add to buffer i++; } else { //in case the buffer is at the end - returns only overlaping characters if(i >= USART_RXBUFFERSIZE) { i = 0; return; } else { //end the bufferr with '\0' and set flag USARTreceiveBuffer[i] = '\0'; rxDataReceivedFlag = i; //flag is used also as length i = 0; } } } }
Step 4 – main.c
Define flag as global variable
/* Private variables ---------------------------------------------------------*/ UART_HandleTypeDef huart1; /* USER CODE BEGIN PV */ volatile uint8_t rxBuff[RXBUFFERSIZE] uint8_t rxDataReceivedFlag = 0; /* USER CODE END PV */
Within main() while() loop check the flag , do something with data and reset the flag
/* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { if(rxCmdReceivedFlag != 0) { HAL_UART_Transmit(&huart3, USARTcommandBuffer,rxCmdReceivedFlag, HAL_MAX_DELAY); rxCmdReceivedFlag = 0; //flag is used also as length } . . . }
Ring Buffer
The best way to handle incoming serial data is to use ring buffer. I did mine solution using this very good article:
http://www.simplyembedded.org/tutorials/interrupt-free-ring-buffer/
//main.c - global variable static rbd_t _rbd; static char _rbmem[32]; //must be a power of 2 (2,4,8,16,32,64...) . . . //main.c - within main() function, before while() - init ring buffer ring_buffer_init(&_rbd, &attr) //main.c - Interrupt_USART1() void Interrupt_USART1(uint8_t *pData, uint16_t Size) { if(USART1->SR & UART_IT_RXNE) // = if (USART1->SR & UART_IT_RXNE) { const char c = USART1->DR; ring_buffer_put(_rbd, &c); } } //main.c - within main() function, inside while() loop - check if there are data... ring_buffer_get(_rbd, &c); //...and print or do somtehing with obtained c character
In other project i used this ring buffer solution, very simple, very good:
http://contiki.sourceforge.net/docs/2.6/a01686.html#ga2972cc12c40c75e5c883e831ff8c14ab
Leave a Reply