More robust HAL USART implementation for RF module AT communication
Hello,
My message is too long so I put the question first and context after:
Any advice regarding the most robust way to implement HAL_UART_ErrorCallback() ?
For now I have:
// UART Error Handle
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
HAL_UART_AbortReceive(&huart2);
HAL_UART_DeInit(&huart2);
MX_USART2_UART_Init();
Ringbuf_Init(); // restart HAL_UARTEx_ReceiveToIdle_DMA(&UART, aRXBufferUser, RxBuf_SIZE);
}
I tried "upseting" the UART by sending data with wrong baudrate and it seems able to recover.
--------- Please don't tell me to trash HAL and go for LL :) you are probably right but it's working well (simple project, no multiple thread) ------------
I have a working configuration based on https://github.com/STMicroelectronics/STM32CubeL4/tree/master/Projects/NUCLEO-L476RG/Examples/UART/UART_ReceptionToIdle_CircularDMA
1) For transmission I use this function:
void send_uart_cellular(char * buffer)
{
uint8_t cpt = 0;
transmit_done = 0;
memset(msg, 0, tx_buffer_size); // Reset transmit buffer
snprintf(msg, tx_buffer_size, "%s\r\n", buffer); // Add CRLF after command
HAL_UART_Transmit_DMA(&huart2, (uint8_t *)msg, strlen(msg));
while((transmit_done != 1) && (cpt < 200)){ // 200ms timeout
cpt++;
HAL_Delay(1);
}
}
with "msg" declared global as
char msg[512] = {0};
and "transmit_done" declared global as
volatile uint8_t transmit_done = 0;
and being modified by
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART2){ // Current UART
transmit_done = 1;
}
}
2) For reception I use these functions:
void Ringbuf_Init (void)
{
pBufferReadyForReception = aRXBufferA;
pBufferReadyForUser = aRXBufferB;
uwNbReceivedChars = 0;
old_pos = 0;
memset(MainBuf, '\0', MainBuf_SIZE);
memset(aRXBufferUser, '\0', RxBuf_SIZE);
memset(aRXBufferA, '\0', RxBuf_SIZE);
memset(aRXBufferB, '\0', RxBuf_SIZE);
Data_received_available_index = 0;
Data_processed_available_index = 0;
HAL_UARTEx_ReceiveToIdle_DMA(&UART, aRXBufferUser, RxBuf_SIZE) == HAL_OK);
}
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
uint8_t *ptemp;
uint8_t i;
/* Check if number of received data in recpetion buffer has changed */
if (Size != old_pos)
{
/* Check if position of index in reception buffer has simply be increased
or if end of buffer has been reached */
if (Size > old_pos)
{
/* Current position is higher than previous one */
uwNbReceivedChars = Size - old_pos;
/* Copy received data in "User" buffer for evacuation */
for (i = 0; i < uwNbReceivedChars; i++)
{
pBufferReadyForUser[i] = aRXBufferUser[old_pos + i];
}
}
else
{
/* Current position is lower than previous one : end of buffer has been reached */
/* First copy data from current position till end of buffer */
uwNbReceivedChars = RxBuf_SIZE - old_pos;
/* Copy received data in "User" buffer for evacuation */
for (i = 0; i < uwNbReceivedChars; i++)
{
pBufferReadyForUser[i] = aRXBufferUser[old_pos + i];
}
/* Check and continue with beginning of buffer */
if (Size > 0)
{
for (i = 0; i < Size; i++)
{
pBufferReadyForUser[uwNbReceivedChars + i] = aRXBufferUser[i];
}
uwNbReceivedChars += Size;
}
}
/* Process received data that has been extracted from Rx User buffer */
UserDataTreatment(huart, pBufferReadyForUser, uwNbReceivedChars);
/* Swap buffers for next bytes to be processed */
ptemp = pBufferReadyForUser;
pBufferReadyForUser = pBufferReadyForReception;
pBufferReadyForReception = ptemp;
}
/* Update old_pos as new reference of position in User Rx buffer that
indicates position to which data have been processed */
old_pos = Size;
}
void UserDataTreatment(UART_HandleTypeDef *huart, uint8_t* pData, uint16_t Size)
{
if (Data_received_available_index + Size >= MainBuf_SIZE) // If Data_received_available_index + new data size is greater than the main buffer size
{
uint16_t nb_datatocopy = MainBuf_SIZE - Data_received_available_index; // Find out how much space is left in the main buffer
memcpy ((uint8_t *)MainBuf + Data_received_available_index, pData, nb_datatocopy); // In that remaining space in MainBuf, copy nb data, from RxBuf + Previous_DrA_RxBuf_index (only new data received)
memcpy ((uint8_t *)MainBuf, pData + nb_datatocopy, Size - nb_datatocopy); // Then copy at the beginning of MainBuf the rest of data (len_RxBuf_data - nb_datatocopy), from RxBuf + Previous_DrA_RxBuf_index + nb_datatocopy
Data_received_available_index = Size - nb_datatocopy; // Update the position of Data_received_available_index to (len_RxBuf_data - nb_datatocopy)
}
else
{
memcpy ((uint8_t *)MainBuf + Data_received_available_index, pData, Size); // Copy new received data at the current position in MainBuf from RxBuf + Previous_DrA_RxBuf_index
Data_received_available_index = Data_received_available_index + Size; // Update the position of Data_received_available_index to Data_received_available_index + len_RxBuf_data
}
new_data_received++; // Flag to notify app that new data is available
}
With following global variables:
uint8_t aRXBufferUser[RxBuf_SIZE]; // 512
uint8_t aRXBufferA[RxBuf_SIZE]; // 512
uint8_t aRXBufferB[RxBuf_SIZE]; // 512
uint8_t MainBuf[MainBuf_SIZE]; // 1024
volatile uint32_t uwNbReceivedChars;
uint8_t *pBufferReadyForUser;
uint8_t *pBufferReadyForReception;
volatile uint16_t old_pos = 0;
volatile uint16_t Data_received_available_index = 0;
volatile uint16_t Data_processed_available_index = 0;
volatile uint16_t Data_processed_available_index_saved = 0;
volatile uint8_t new_data_received = 0;
/* Timeout is in milliseconds */
volatile int32_t TIMEOUT = 0;
and SysTick_Handler being modified in stm32l0xx_it.c to make TIMEOUT variable decrement by 1 every ms.
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
TIMEOUT--;
/* USER CODE END SysTick_IRQn 1 */
}
I also have functions for handling answers from the module:
/* Waits for a particular string to arrive in the incoming buffer... It also increments the Data_processed_available_index
* returns 1, if the string is detected
* return 0, in case of timeout
*/
uint8_t waitFor (char *string, uint32_t Timeout)
{
uint16_t string_len = strlen(string); // Get string length
uint8_t string_found = 0; // Flag to notify that string is found
TIMEOUT = Timeout; // Initialize TIMEOUT (decreases by one each ms (see SysTick_Handler in stm32l0xx_it.c) to Timeout (parameter of this waitFor function)
new_data_received = 0; // Reset new_data_received flag
uint16_t Instant_Data_received_available_index = Data_received_available_index;
uint16_t Instant_Data_processed_available_index = Data_processed_available_index;
if (((Data_processed_available_index < Instant_Data_received_available_index) && ((Instant_Data_received_available_index - Data_processed_available_index) >= string_len)) || ((Data_processed_available_index > Instant_Data_received_available_index) && ((MainBuf_SIZE - Data_processed_available_index + Instant_Data_received_available_index) >= string_len))){
string_found = search_string_in_Mainbuf(string, &Data_processed_available_index, Instant_Data_received_available_index); // Look for string in this unprocessed data
if(0 == string_found){ // If not found reset Data_processed counter to check again after new data received (buffer may have been full and HAL_UARTEx_RxEventCallback is trigged by Transfert Completed event, or data of interest might be preceded by idle event with data of no interest)
Data_processed_available_index = Instant_Data_processed_available_index;
}
}
while ((0 == string_found) && (TIMEOUT > 0)){ // While string is not found in MainBuf (no more than Timeout)
while ((0 == new_data_received) && (TIMEOUT > 0)){ // Wait for new_data_received flag (no more than Timeout)
HAL_Delay(1);
}
if(TIMEOUT > 0){ // If Timeout not reached means new data has been received
Instant_Data_received_available_index = Data_received_available_index;
new_data_received = 0; // Reset new_data_received flag
string_found = search_string_in_Mainbuf(string, &Data_processed_available_index, Instant_Data_received_available_index); // Look for string in this new available data
if(0 == string_found){ // If not found reset Data_processed counter to check again after new data received (buffer may have been full and HAL_UARTEx_RxEventCallback is trigged by Transfert Completed event, or data of interest might be preceded by idle event with data of no interest)
Data_processed_available_index = Instant_Data_processed_available_index;
}
}
}
return string_found;
}
uint8_t search_string_in_Mainbuf(char *string, volatile uint16_t *index, uint16_t index_end)
{
uint16_t so_far = 0; // Reset "so_far" variable that will be used as index for string buffer
uint16_t string_len = strlen(string); // Get string length
while(*index != index_end){
while ((MainBuf[*index] != string[so_far])) // Peek in MainBuf to see if we get the first character of string
{
*index = *index + 1; // While not, increment index
if (*index == MainBuf_SIZE) *index = 0; // If index reaches MainBuf_SIZE, index is reset
if (*index == index_end) return 0; // If index_end reached, return 0
}
while (MainBuf[*index] == string[so_far]) // While character in MainBuf matches character in string
{
so_far++; // Increment index in string buffer
*index = *index + 1; // Increment index in MainBuf
if (*index == MainBuf_SIZE) *index = 0; // If index reaches MainBuf_SIZE, index is reset
if (so_far == string_len) return 1; // If end of string is reached, string is found in MainBuf so return 1
}
so_far = 0; // If first characters in MainBuf was matching string but not entirely, reset string index and look again for string in MainBuf
}
return 0;
}
Any comments? Hints? Recommandation?
