cancel
Showing results for 
Search instead for 
Did you mean: 

Any working code for DMA idle detect UART to interface with module ?

Thomas LB
Associate III

Hello,

This has been asked many times but still can't find easy solution...

I followed this tutorial : https://www.youtube.com/watch?v=ly3_JEC_d9w&ab_channel=ControllersTech

but still have some random failure ;(

Also looked at https://github.com/STMicroelectronics/STM32CubeWB/blob/11042898d71249ed9c698355fd7b7812b741a161/Projects/P-NUCLEO-WB55.Nucleo/Examples/UART/UART_ReceptionToIdle_CircularDMA/Src/main.c#L389

But now I'm not sure how to perform classic buffer dump and then check for keyword, read data from module etc.

Any suggestion will be appreciated 🙂

Thank you,

5 REPLIES 5
Piranha
Chief II

ControllersTech is a site made by an absolutely incompetent amateur and is full of broken code.

In ST's example user data processing is done in UserDataTreatment() function. Replace it with your code.

Honestly, ST's official example solution for UART continuous reception and IDLE interrupt is so "great" that the only sane implementation is tossing it out and replacing with a code made by a competent developer:

https://github.com/MaJerle/stm32-usart-uart-dma-rx-tx

Thomas LB
Associate III

Hello Piranha, thank you for this 🙂

I have already looked at this very well documented and structured code. Didn't go further as I was hoping for HAL only implementation but hey, maybe there is still no 100% working UART solution based on HAL in 2022 ...

IMHO it's a shame that ST doesn't provide full example of module interface with UART with module. Including basing RX string manipulation functions such as waitFor(keyword, timeout), get_data_between(keyword_start, keyword_end, timeout), get_n_char_after_keyword(keyword, n_char, timeout), etc.. That would save a lot of time to a lot of ST MCU users !

Thomas LB
Associate III

I finally tried my own implementation based on HAL only:

1) Config in CubeMX:

UART2 ADD DMA RX Circular mode

Enable USART2 global interrupt

2) Modification in stm32l0xx_it.c: Add "TIMEOUT--;"

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 */
}

3) Variable declaration in ringbuffer.c:

uint8_t RxBuf[RxBuf_SIZE];
uint8_t MainBuf[MainBuf_SIZE];
 
volatile uint16_t End_of_data_received_index = 0;
volatile uint16_t End_of_data_processed_index = 0;
 
volatile uint8_t new_data_received = 0;
 
/* Timeout is in milliseconds */
volatile int32_t TIMEOUT = 0;

4) Variable declaration in ringbuffer.h

#define RxBuf_SIZE 128
#define MainBuf_SIZE 256
 
extern UART_HandleTypeDef huart2;
extern DMA_HandleTypeDef hdma_usart2_rx;
 
extern volatile int32_t TIMEOUT;

5) Start ringbuffer once (as it is circular, no need to reload after each callback)

MX_USART2_UART_Init();
memset(RxBuf, '\0', RxBuf_SIZE);
memset(MainBuf, '\0', MainBuf_SIZE);
End_of_data_received_index = 0;
End_of_data_processed_index = 0;
HAL_UARTEx_ReceiveToIdle_DMA(&huart2, RxBuf, RxBuf_SIZE);
__HAL_DMA_DISABLE_IT(&hdma_usart2_rx, DMA_IT_HT);

6) Callback trigged by : Transfer Completed and IDLE Event (Half Transfert IT being disabled):

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
 new_data_received = 1; // Flag to notify app that new data is ready	
 if (End_of_data_received_index + Size > MainBuf_SIZE)  // If the current position + new data size is greater than the main buffer
 {
  uint16_t datatocopy = MainBuf_SIZE - End_of_data_received_index;  // Find out how much space is left in the main buffer
  memcpy ((uint8_t *)MainBuf + End_of_data_received_index, RxBuf, datatocopy);  // Copy data in that remaining space
  memcpy ((uint8_t *)MainBuf, RxBuf + datatocopy, (Size - datatocopy));  // Copy the remaining data at the beggining of MainBuf
  End_of_data_received_index = (Size - datatocopy);  // Update the position
 }
else
 {
  memcpy ((uint8_t *)MainBuf + End_of_data_received_index, RxBuf, Size); // Copy data at the current position
  End_of_data_received_index = End_of_data_received_index + Size; // Update the position
 }
}

7) Basic usage functions:

/* Waits for a particular string to arrive in the incoming buffer... It also increments the End_of_data_processed_index
 * returns 1, if the string is detected
 * return 0, in case of the timeout
 */
uint8_t waitFor (char *string, uint32_t Timeout)
{
 uint16_t so_far = 0;
 uint16_t len = strlen (string);
 
 TIMEOUT = Timeout;
 
 new_data_received = 0;
 
 /* if the incoming data does not match with the string, we will simply increment the index
  * And wait for the string to arrive in the incoming data
  * */
 while(TIMEOUT > 0){
  while ((MainBuf[End_of_data_processed_index] != string[so_far]))  // peek in the rx_buffer to see if we get the string
  {
   End_of_data_processed_index++;
   if (End_of_data_processed_index == MainBuf_SIZE) End_of_data_processed_index = 0;
  }
 
  /* If the incoming data does match with the string, we will return 1 to indicate this */
  while (MainBuf[End_of_data_processed_index] == string[so_far]) // if we got the first letter of the string
  {
   so_far++;
   End_of_data_processed_index++;
   if (End_of_data_processed_index==MainBuf_SIZE) End_of_data_processed_index = 0;
   if (so_far == len) return 1;
  }
  HAL_Delay(1);
  so_far = 0;
 }
 return 0;
}
 
 
/* Copies the entered number of characters, after the entered string (from the incoming buffer), into the buffer
 * returns 1, if the string is copied
 * returns 0, in case of the timeout
 */
uint8_t getAfter (char *string, uint8_t numberofchars, char *buffertocopyinto, uint32_t Timeout)
{
 if ((waitFor(string, Timeout)) != 1) return 0;
 
 for (int indx=0; indx<numberofchars; indx++)
 {
  buffertocopyinto[indx] = MainBuf[End_of_data_processed_index++];  // save the data into the buffer... increments the End_of_data_processed_index
  if (End_of_data_processed_index==MainBuf_SIZE) End_of_data_processed_index = 0;
 }
 return 1;
}
 
/* Copies the buffer until return after the entered string (from the incoming buffer), into the buffer
 * returns 1, if the string is copied
 * returns 0, in case of the timeout
 */
uint8_t getAftertoreturn (char *string, char *buffertocopyinto, size_t buffertocopyinto_size, uint32_t Timeout)
{
 
 if ((waitFor(string, Timeout)) != 1) return 0;
 
 for (int indx=0; indx<buffertocopyinto_size; indx++)
 {
  buffertocopyinto[indx] = MainBuf[End_of_data_processed_index++];  // save the data into the buffer... increments the End_of_data_processed_index
  if (End_of_data_processed_index==MainBuf_SIZE) End_of_data_processed_index = 0;
 
  if('\r'==buffertocopyinto[indx]){
   buffertocopyinto[indx] = '\0';
   break;
  }
 
 }
 return 1;
}

So far so good but any expert eye on this code is welcome !!

Thomas LB
Associate III

Improved version:

6)

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
  if(Size > Previous_EoD_RxBuf_index){         // If Size > Previous End of Data RxBuf index, means RxBuf is not full yet
   len_RxBuf_data = Size - Previous_EoD_RxBuf_index;  // Get the size of new data received by removing last index to current Size (Size = index of last available data in RxBuf)
  }
  else{                         // If Size < Previous End of Data Rxbuf index, means RxBuf just restarted at the beginning so we can:
   len_RxBuf_data = Size;               // Reset len_Rxbuf_data to Size
   Previous_EoD_RxBuf_index = 0;            // Reset Previous End of Data Rxbuf index to 0
  }
 
  if (End_of_data_received_index + len_RxBuf_data >= MainBuf_SIZE)  // If End_of_data_received_index + new data size is greater than the main buffer size
  {
   uint16_t nb_datatocopy = MainBuf_SIZE - End_of_data_received_index;  // Find out how much space is left in the main buffer
   
   memcpy ((uint8_t *)MainBuf + End_of_data_received_index, RxBuf + Previous_EoD_RxBuf_index, nb_datatocopy);  // In that remaining space in MainBuf, copy nb data, from RxBuf + Previous_EoD_RxBuf_index (only new data received)
   memcpy ((uint8_t *)MainBuf, RxBuf + Previous_EoD_RxBuf_index + nb_datatocopy, (len_RxBuf_data - nb_datatocopy));  // Then copy at the beginning of MainBuf the rest of data (len_RxBuf_data - nb_datatocopy), from RxBuf + Previous_EoD_RxBuf_index + nb_datatocopy
   End_of_data_received_index = (len_RxBuf_data - nb_datatocopy);  // Update the position of End_of_data_received_index to (len_RxBuf_data - nb_datatocopy)
  }
 
  else
  {
   memcpy ((uint8_t *)MainBuf + End_of_data_received_index, RxBuf + Previous_EoD_RxBuf_index, len_RxBuf_data); // Copy new received data at the current position in MainBuf from RxBuf + Previous_EoD_RxBuf_index
   End_of_data_received_index = End_of_data_received_index + len_RxBuf_data; // Update the position of End_of_data_received_index to End_of_data_received_index + len_RxBuf_data
  }
  
  Previous_EoD_RxBuf_index = Size; // Save index from last data received to be able to get new data length next time this callback is triggered
  
  new_data_received++; // Flag to notify app that new data is available
}

7)

/* Waits for a particular string to arrive in the incoming buffer... It also increments the End_of_data_processed_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
 
 if(((End_of_data_processed_index + string_len) % MainBuf_SIZE) <= End_of_data_received_index){ // If unprocessed data of size more than string_len available
  string_found = search_string_in_Mainbuf(string, &End_of_data_processed_index, End_of_data_received_index); // Look for string in this unprocessed data
 }
 
 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
   HAL_Delay(1);                    // Give room to HAL_UARTEx_RxEventCallback to process twice in case of RxBuf overflow (Transfert Completed and IDLE events)
   new_data_received = 0;               // Reset new_data_received flag
   string_found = search_string_in_Mainbuf(string, &End_of_data_processed_index, End_of_data_received_index); // Look for string in this new available data
  }
 }
 
 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;
}
 
 
/* Copies the entered number of characters, after the entered string (from the incoming buffer), into the buffer
 * returns 1, if the string is copied
 * returns 0, in case of timeout
 */
uint8_t getAfter (char *string, uint8_t numberofchars, char *buffertocopyinto, uint32_t Timeout)
{
 if ((waitFor(string, Timeout)) != 1) return 0;
 
 for (int indx=0; indx<numberofchars; indx++)
 {
  buffertocopyinto[indx] = MainBuf[End_of_data_processed_index++];  // save the data into the buffer... increments the End_of_data_processed_index
  if (End_of_data_processed_index == MainBuf_SIZE) End_of_data_processed_index = 0;
 }
 return 1;
}
 
/* Copies the buffer until return after the entered string (from the incoming buffer), into the buffer
 * returns 1, if the string is copied
 * returns 0, in case of timeout or buffertocopyinto_size reached
 */
uint8_t getAftertoreturn (char *string, char *buffertocopyinto, size_t buffertocopyinto_size, uint32_t Timeout)
{
 if ((waitFor(string, Timeout)) != 1) return 0;
 
 for (int indx=0; indx<buffertocopyinto_size; indx++)
 {
  buffertocopyinto[indx] = MainBuf[End_of_data_processed_index++];  // save the data into the buffer... increments the End_of_data_processed_index
  if (End_of_data_processed_index == MainBuf_SIZE) End_of_data_processed_index = 0;
 
  if('\r' == buffertocopyinto[indx]){
   buffertocopyinto[indx] = '\0';
   return 1;
  }
 }
 return 0;
}

Piranha
Chief II

> Including basing RX string manipulation functions such as waitFor(keyword, timeout), get_data_between(keyword_start, keyword_end, timeout), get_n_char_after_keyword(keyword, n_char, timeout), etc.

This belongs to a higher processing layer above UART. Generic UART is just a stream of bytes and the driver should deliver those. Managing messages, lines etc. is a task for next layer and typically that should be implemented as a separate module (file). Though there are some exceptions when UART is used as a message based protocol.