UART bridge on STM32F0: data corruption
I'm trying to link together two UARTs to create a sort of "bridge". Each byte received by one of them is sent to the other and vice-versa. I used this code for 7-8 years on small and slow MCUs like xmega (8-bit and 32 MHz), so I think there should no problem at all with such a beast like an STM32! Hence, I'm sure I did something wrong in my code.
The approach is quite simple: when an rx interrupt fires I store the incoming byte into a circular buffer. Then, inside a low-priority task I pop the byte from this buffer and put it into another, the tx buffer. Here, always under interrupts I send data on the other serial. It works fine (at least on AVR!). Because both serials share the same baud-rate it's almost impossible to fail.
Here the initialization, shown for one UART only, the other is the same:
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart1) != HAL_OK) Error_Handler();
HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);Here the usage::
UART_data_t UART1_data;
UART_data_t UART2_data;
bool repeater = true;
void Init(void)
{
UART_Init(&UART1_data, &huart1);
UART_Init(&UART2_data, &huart2);
}
// called every 1 ms
void ReceiveData(void)
{
while (UART_RxBufferData_Available(&UART1_data))
{
uint8_t ch = UART_RxBuffer_GetByte(&UART1_data);
if (repeater) UART_TxBuffer_PutByte(&UART2_data, ch);
else { // do something }
}
while (UART_RxBufferData_Available(&UART2_data))
{
uint8_t ch = UART_RxBuffer_GetByte(&UART2_data);
if (repeater) UART_TxBuffer_PutByte(&UART1_data, ch);
else { // do something }
}
}
void USART1_IRQHandler(void)
{
UART_IRQHandler(&UART1_data);
}
void USART2_IRQHandler(void)
{
UART_IRQHandler(&UART2_data);
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
UART_data_t *UART_data;
if (huart == UART1_data.huart) UART_data = &UART1_data;
else if (huart == UART2_data.huart) UART_data = &UART2_data;
UART_RxCallback(UART_data);
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
UART_data_t *UART_data;
if (huart == UART1_data.huart) UART_data = &UART1_data;
else if (huart == UART2_data.huart) UART_data = &UART2_data;
UART_TxCallback(UART_data);
}Here the relevant functions of my circular buffer:
#define UART_RX_BUFFER_SIZE 64
#define UART_TX_BUFFER_SIZE 64
#define UART_RX_BUFFER_MASK (UART_RX_BUFFER_SIZE - 1)
#define UART_TX_BUFFER_MASK (UART_TX_BUFFER_SIZE - 1)
typedef struct UART_Buffer_t
{
char ch;
char RX[UART_RX_BUFFER_SIZE];
char TX[UART_TX_BUFFER_SIZE];
uint8_t RX_Head;
uint8_t RX_Tail;
uint8_t TX_Head;
uint8_t TX_Tail;
} UART_Buffer_t;
typedef struct UART_data_t
{
UART_HandleTypeDef *huart;
UART_Buffer_t buffer;
} UART_data_t;
void UART_Init(UART_data_t *uart_data, UART_HandleTypeDef *huart)
{
uart_data->huart = huart;
uart_data->buffer.RX_Tail = 0;
uart_data->buffer.RX_Head = 0;
uart_data->buffer.TX_Tail = 0;
uart_data->buffer.TX_Head = 0;
if (HAL_UART_Receive_IT(uart_data->huart, (uint8_t *) &uart_data->buffer.ch, 1) != HAL_OK) Error_Handler();
}
bool UART_TxBuffer_FreeSpace(UART_data_t *uart_data)
{
uint8_t tempHead = (uart_data->buffer.TX_Head + 1) & UART_TX_BUFFER_MASK;
uint8_t tempTail = uart_data->buffer.TX_Tail;
return (tempHead != tempTail);
}
bool UART_TxBuffer_PutByte(UART_data_t *uart_data, char data)
{
UART_Buffer_t *buffer = &uart_data->buffer;
bool TxBuffer_FreeSpace = UART_TxBuffer_FreeSpace(uart_data);
if (TxBuffer_FreeSpace)
{
uint8_t tempTX_Head = buffer->TX_Head;
buffer->TX[tempTX_Head] = data;
buffer->TX_Head = (tempTX_Head + 1) & UART_TX_BUFFER_MASK;
if (uart_data->huart->gState == HAL_UART_STATE_READY) UART_TxCallback(uart_data);
}
return TxBuffer_FreeSpace;
}
bool UART_RxBufferData_Available(UART_data_t *uart_data)
{
uint8_t tempHead = uart_data->buffer.RX_Head;
uint8_t tempTail = uart_data->buffer.RX_Tail;
return (tempHead != tempTail);
}
uint8_t UART_RxBuffer_GetByte(UART_data_t *uart_data)
{
UART_Buffer_t *buffer = &uart_data->buffer;
char ch = buffer->RX[buffer->RX_Tail];
buffer->RX_Tail = (buffer->RX_Tail + 1) & UART_RX_BUFFER_MASK;
return ch;
}
void UART_IRQHandler(UART_data_t *uart_data)
{
HAL_UART_IRQHandler(uart_data->huart);
}
char UART_RxCallback(UART_data_t *uart_data)
{
UART_Buffer_t *buffer = &uart_data->buffer;
uint8_t tempRX_Head = (buffer->RX_Head + 1) & UART_RX_BUFFER_MASK;
uint8_t tempRX_Tail = buffer->RX_Tail;
char data = buffer->ch;
if (tempRX_Head != tempRX_Tail)
{
uart_data->buffer.RX[uart_data->buffer.RX_Head] = data;
uart_data->buffer.RX_Head = tempRX_Head;
}
HAL_StatusTypeDef res = HAL_UART_Receive_IT(uart_data->huart, (uint8_t *) &uart_data->buffer.ch, 1);
if (res != HAL_OK) Error_Handler();
return data;
}
void UART_TxCallback(UART_data_t *uart_data)
{
UART_Buffer_t *buffer = &uart_data->buffer;
uint8_t tempTX_Tail = uart_data->buffer.TX_Tail;
if (buffer->TX_Head != tempTX_Tail)
{
char data = buffer->TX[tempTX_Tail];
HAL_StatusTypeDef res = HAL_UART_Transmit_IT(uart_data->huart, (uint8_t *) &data, 1);
if (res != HAL_OK) Error_Handler();
buffer->TX_Tail = (tempTX_Tail + 1) & UART_TX_BUFFER_MASK;
}
}If I type anything on a serial terminal I get the same content on the other one. So the whole code is "correct".
The problems rise when I send a bunch of bytes, even 7-8 without any inter-char delay.
In this case I got corrupted data in the rx buffer. It means, it loses some HAL_UART_RxCpltCallback interrupts.
It's hard to believe it cannot execute the few lines of code needed to put the byte inside the buffer, before a new byte arrives. It has about 10 us, plenty of time!
So, would you please help me to understand my mistake?
