2023-04-25 04:41 AM
I need to receive a six-byte packet via the UART (using an STM32L010F4). There is a SOF and an EOF byte included in these six bytes. There could be long stretches when no data is sent, followed by a stream of these six-byte packets that are 5mS apart. Once a valid packet is received, I need to perform some basic math on the four non-SOF/EOF bytes (some addition and multiplication), re-package the data into another packet, and send it out to a host on a different UART.
Utilizing the DMA seems to cause me to miss-sync with these incoming messages, as has been pointed out to me. What would be the best architecture for this? Utilize a much larger buffer?
2023-04-25 05:11 AM
> Utilizing the DMA seems to cause me to miss-sync with these incoming messages
What does this mean? DMA in cyclic mode allows to receive continuously to a ring buffer.
Use a buffer of reasonable size, parse the data in background.
STM32L010F4 has only two UARTs, one of them is LPUART with baudrate only up to 9600.
If the latter is used to receive, may be interrupts are good enough and you can do processing in the ISR.
2023-04-25 05:17 AM
I was using the DMA in normal mode. I'd get the first message OK, but subsequent messages would have data in the wrong locations (the EOF byte would be first, or I'd get two SOF bytes but no EOF byte). I assume'd this was due to a miss-sync where another message was coming in as I was dealing with the first one.
2023-04-25 05:37 AM
Do you miss data if you just record / observe what's coming in?
Do you take a long time to process and respond?
How frequently does the inbound data arrive?
Use delays or blocking functions?
Try to decouple the buffering and response operations.
2023-04-25 06:23 PM - edited 2023-11-20 07:11 AM
The code below is pretty much the same as this code I posted at the following link
That code uses a message buffer based off strings but you can also do binary data with some modifications. It uses DMA with idle to put each packet in it's own buffer without worrying about new data overwriting the current data that is currently being processed.
So based off some of your requirements, I just redid the code to look at UART2 for data that is 6 bytes and has the SOT and EOT (0x02 and 0x03) bytes to be considered a valid packet. Then I add the 4 bytes in between and send the total as a string to UART1 which has a loopback. That string that gets looped back is sent back on UART2. I also look for a specific packet that controls the LED on/off state.
Blue data is just binary data
Red data is the string
Be sure to enable DMA Tx/RX for both UART ports as well as enable the NVIC interrupt for the UART.
// PollingRoutine.c
/*
* PollingRoutine.c
*
* Created on: Apr 10, 2023
* Author: karl.yamashita
*/
#include "main.h"
extern UART_HandleTypeDef huart1; // loopback
extern UART_HandleTypeDef huart2; // Virtual COM Port
UartDataStruct uart1Rx = {0};
UartDataStruct uart1Tx = {0};
UartDataStruct uart2Rx = {0};
UartDataStruct uart2Tx = {0};
// Is called before main while loop
void PollingInit(void)
{
UART_EnableRx(&huart1, &uart1Rx); // init Rx reception
UART_EnableRx(&huart2, &uart2Rx); // init Rx reception
}
// Is called from inside main while loop
void PollingRoutine(void)
{
UART1_Parse();
UART2_Parse();
UART_Send(&huart1, &uart1Tx);
UART_Send(&huart2, &uart2Tx);
UART_RxStatus(&huart1, &uart1Tx);
UART_RxStatus(&huart2, &uart2Tx);
}
// Enable UART reception. Set flag if failed to enable
void UART_EnableRx(UART_HandleTypeDef *huart, UartDataStruct *msg)
{
if(HAL_UARTEx_ReceiveToIdle_DMA(huart, msg->Queue[msg->ptr.iIndexIN].data, RX_BUFFER_SIZE) != HAL_OK)
{
msg->uartRxIsEnabled = false;
}
else
{
msg->uartRxIsEnabled = true;
}
}
// HAL callback
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if(huart->Instance == huart1.Instance)
{
uart1Rx.Queue[uart1Rx.ptr.iIndexIN].size = Size; // save data length
DRV_RingBuffPtr__Input(&uart1Rx.ptr, UART_QUEUE_SIZE); // increment pointer
UART_EnableRx(&huart1, &uart1Rx); // re-enable UART reception
}
else if(huart->Instance == huart2.Instance)
{
uart2Rx.Queue[uart2Rx.ptr.iIndexIN].size = Size; // save data length
DRV_RingBuffPtr__Input(&uart2Rx.ptr, UART_QUEUE_SIZE); // increment pointer
UART_EnableRx(&huart2, &uart2Rx); // re-enable UART reception
}
}
// TX and RX is in loopback
void UART1_Parse(void)
{
if(uart1Rx.ptr.iCnt_Handle)
{
memcpy(&uart2Tx.Queue[uart2Tx.ptr.iIndexIN].data, uart1Rx.Queue[uart1Rx.ptr.iIndexOUT].data, uart1Rx.Queue[uart1Rx.ptr.iIndexOUT].size); // copy data
uart2Tx.Queue[uart2Tx.ptr.iIndexIN].size = uart1Rx.Queue[uart1Rx.ptr.iIndexOUT].size; // copy data length
DRV_RingBuffPtr__Input(&uart2Tx.ptr, UART_QUEUE_SIZE); // increment tx in pointer
DRV_RingBuffPtr__Output(&uart1Rx.ptr, UART_QUEUE_SIZE); // increment rx out pointer
}
}
// From VCP. Using Docklight to send string messages to this UART.
void UART2_Parse(void)
{
uint8_t sof;
uint8_t eof;
uint32_t total = 0;
int i = 0;
if(uart2Rx.ptr.iCnt_Handle)
{
sof = uart2Rx.Queue[uart2Rx.ptr.iIndexOUT].data[0];
eof = uart2Rx.Queue[uart2Rx.ptr.iIndexOUT].data[5];
if(sof == 0x02 && eof == 0x03) // SOF and EOF valid
{
if(uart2Rx.Queue[uart2Rx.ptr.iIndexOUT].data[1] == 0x01) // 0x01 command for LED control
{
if(uart2Rx.Queue[uart2Rx.ptr.iIndexOUT].data[2] == 0x01)
{
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
}
else if(uart2Rx.Queue[uart2Rx.ptr.iIndexOUT].data[2] == 0x00)
{
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
}
}
else // add data and send to UART1 as a string and will loop back.
{
for(i = 1; i < 5; i++)
{
total += uart2Rx.Queue[uart2Rx.ptr.iIndexOUT].data[i]; // total of the 4 bytes
}
sprintf((char*)uart1Tx.Queue[uart1Tx.ptr.iIndexIN].data, "0x%X + 0x%X + 0x%X + 0x%X = 0x%lX", \
uart2Rx.Queue[uart2Rx.ptr.iIndexOUT].data[1], uart2Rx.Queue[uart2Rx.ptr.iIndexOUT].data[2], \
uart2Rx.Queue[uart2Rx.ptr.iIndexOUT].data[3], uart2Rx.Queue[uart2Rx.ptr.iIndexOUT].data[4], total); // copy total as a string to buffer
uart1Tx.Queue[uart1Tx.ptr.iIndexIN].size = strlen((char*)uart1Tx.Queue[uart1Tx.ptr.iIndexIN].data); // get the total string size
DRV_RingBuffPtr__Input(&uart1Tx.ptr, UART_QUEUE_SIZE);
}
}
DRV_RingBuffPtr__Output(&uart2Rx.ptr, UART_QUEUE_SIZE); // increment rx out pointer
}
}
// Send any available messages in buffer
void UART_Send(UART_HandleTypeDef *huart, UartDataStruct *msg)
{
if(msg->ptr.iCnt_Handle)
{
if(HAL_UART_Transmit_DMA(huart, msg->Queue[msg->ptr.iIndexOUT].data, msg->Queue[msg->ptr.iIndexOUT].size) == HAL_OK)
{
DRV_RingBuffPtr__Output(&msg->ptr, UART_QUEUE_SIZE); // Successful so we can increment pointer
}
}
}
// Poll this to Enable UART RX if not currently enabled
void UART_RxStatus(UART_HandleTypeDef *huart, UartDataStruct *msg)
{
if(!msg->uartRxIsEnabled)
{
UART_EnableRx(huart, msg);
}
}
// PollingRoutine.h
/*
* PollingRoutine.h
*
* Created on: Apr 10, 2023
* Author: karl.yamashita
*/
#ifndef INC_POLLINGROUTINE_H_
#define INC_POLLINGROUTINE_H_
#define RX_BUFFER_SIZE 256
#define UART_QUEUE_SIZE 4
typedef struct
{
struct
{
uint8_t data[RX_BUFFER_SIZE];
uint32_t size;
}Queue[UART_QUEUE_SIZE];
RING_BUFF_INFO ptr;
bool uartRxIsEnabled; // only needed for Rx
}UartDataStruct;
void PollingInit(void);
void PollingRoutine(void);
void UART_EnableRx(UART_HandleTypeDef *huart, UartDataStruct *msg);
void UART1_Parse(void);
void UART2_Parse(void);
void UART_Send(UART_HandleTypeDef *huart, UartDataStruct *msg);
void UART_RxStatus(UART_HandleTypeDef *huart, UartDataStruct *msg);
#endif /* INC_POLLINGROUTINE_H_ */
// RingBuff.c
/*
* RingBuff.c
*
* Created on: Sep 18, 2019
* Author: Karl
*
*
*/
#include "main.h"
#include "ringBuff.h"
void DRV_RingBuffPtr__Input(RING_BUFF_INFO *ptr, unsigned int iBufferSize) {
ptr->iIndexIN++;
if (ptr->iIndexIN >= iBufferSize)
ptr->iIndexIN = 0;
ptr->iCnt_Handle++;
if (ptr->iIndexIN == ptr->iIndexOUT) {
ptr->iCnt_OverFlow++;
if (ptr->iCnt_OverFlow > 50000)
ptr->iCnt_OverFlow = 0;
if (ptr->iIndexIN == 0) {
ptr->iIndexOUT = iBufferSize - 1;
} else {
ptr->iIndexOUT = ptr->iIndexIN - 1;
}
ptr->iCnt_Handle = 1;
}
}
void DRV_RingBuffPtr__Output(RING_BUFF_INFO *ptr, unsigned int iBufferSize) {
if (ptr->iCnt_Handle) {
ptr->iIndexOUT++;
if (ptr->iIndexOUT >= iBufferSize)
ptr->iIndexOUT = 0;
ptr->iCnt_Handle--;
}
}
// RingBuff.h
#ifndef RINGBUFF_H
#define RINGBUFF_H
#include "main.h"
#include <stdint.h>
typedef struct {
uint16_t iIndexIN; // change all these if need more than 16bits.
uint16_t iIndexOUT;
uint16_t iCnt_Handle;
uint16_t iCnt_OverFlow;
}RING_BUFF_INFO;
void DRV_RingBuffPtr__Input(RING_BUFF_INFO *ptr, unsigned int iBufferSize);
void DRV_RingBuffPtr__Output(RING_BUFF_INFO *ptr, unsigned int iBufferSize);
#endif // RINGBUFF_H