2023-12-07 04:06 AM
Hi together,
at work I have an Azure RTOS based project with UART communication. The basic implementation of ThreadX is working fine for toggling LEDs, but I need to do some SPI communication to an ePaper display and communicate with the MCU over UART.
So I created two separate threads, one for the UART and one for SPI. The display thread is only doing some stuff when there is new data received over UART or we have a time depended change of the shown information.
The problem is that the UART communication stops working after I received some strings. I created a basic example project that just doing UART stuff, it resends the received bytes.
UART reception is handled via DMA and starts with HAL_UARTEx_ReceiveToIdle_DMA in the thread initialization.
After some bytes are sent fast from my Mac to the board the retransmission is not working and the whole communication stops.
My main() is doing the initialization of the peripherals and everything else is working in the thread and the interrupts:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
MX_RTC_Init();
MX_TIM6_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
MX_ThreadX_Init();
/* We should never get here as control is now taken by the scheduler */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
The app_threadx.c looks like this:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file app_threadx.c
* @author MCD Application Team
* @brief ThreadX applicative file
******************************************************************************
* @attention
*
* Copyright (c) 2020-2021 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "app_threadx.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "usart.h"
#include "stdint.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef struct {
uint16_t startIndex;
uint16_t stopIndex;
uint16_t bufferLen;
uint8_t *buffer;
} data_buffer_struct;
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define CMD_BUFFER_SIZE 256
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
TX_THREAD tx_app_thread;
TX_SEMAPHORE tx_app_semaphore;
/* USER CODE BEGIN PV */
uint8_t uart_rx_buf[CMD_BUFFER_SIZE] = {0};
uint8_t uart_tx_buf[CMD_BUFFER_SIZE] = {0};
uint8_t cmd_buf[CMD_BUFFER_SIZE] = {0};
data_buffer_struct data_buffer = {
.startIndex = 0,
.stopIndex = 0,
.bufferLen = sizeof(cmd_buf),
.buffer = cmd_buf,
};
data_buffer_struct uart_rx_buffer = {
.startIndex = 0,
.stopIndex = 0,
.bufferLen = sizeof(uart_rx_buf),
.buffer = uart_rx_buf,
};
data_buffer_struct uart_tx_buffer = {
.startIndex = 0,
.stopIndex = 0,
.bufferLen = sizeof(uart_tx_buf),
.buffer = uart_tx_buf,
};
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
void uart_tx_messages(void);
/* USER CODE END PFP */
/**
* @brief Application ThreadX Initialization.
* @PAram memory_ptr: memory pointer
* @retval int
*/
UINT App_ThreadX_Init(VOID *memory_ptr)
{
UINT ret = TX_SUCCESS;
TX_BYTE_POOL *byte_pool = (TX_BYTE_POOL*)memory_ptr;
/* USER CODE BEGIN App_ThreadX_MEM_POOL */
/* USER CODE END App_ThreadX_MEM_POOL */
CHAR *pointer;
/* Allocate the stack for tx app thread */
if (tx_byte_allocate(byte_pool, (VOID**) &pointer,
TX_APP_STACK_SIZE, TX_NO_WAIT) != TX_SUCCESS)
{
return TX_POOL_ERROR;
}
/* Create tx app thread. */
if (tx_thread_create(&tx_app_thread, "tx app thread", tx_app_thread_entry, 0, pointer,
TX_APP_STACK_SIZE, TX_APP_THREAD_PRIO, TX_APP_THREAD_PREEMPTION_THRESHOLD,
TX_APP_THREAD_TIME_SLICE, TX_APP_THREAD_AUTO_START) != TX_SUCCESS)
{
return TX_THREAD_ERROR;
}
/* Create tx basic semaphore. */
if (tx_semaphore_create(&tx_app_semaphore, "tx basic semaphore", 0) != TX_SUCCESS)
{
return TX_SEMAPHORE_ERROR;
}
/* USER CODE BEGIN App_ThreadX_Init */
/* USER CODE END App_ThreadX_Init */
return ret;
}
/**
* @brief Function implementing the tx_app_thread_entry thread.
* @PAram thread_input: Hardcoded to 0.
* @retval None
*/
void tx_app_thread_entry(ULONG thread_input)
{
/* USER CODE BEGIN tx_app_thread_entry */
// initialize communication
if (HAL_UARTEx_ReceiveToIdle_DMA(&huart1, uart_rx_buf, sizeof(uart_rx_buf)) != HAL_OK)
{
Error_Handler();
}
while(1)
{
tx_semaphore_get(&tx_app_semaphore, TX_WAIT_FOREVER);
uart_tx_messages();
}
/* USER CODE END tx_app_thread_entry */
}
/**
* @brief Function that implements the kernel's initialization.
* @PAram None
* @retval None
*/
void MX_ThreadX_Init(void)
{
/* USER CODE BEGIN Before_Kernel_Start */
/* USER CODE END Before_Kernel_Start */
tx_kernel_enter();
/* USER CODE BEGIN Kernel_Start_Error */
/* USER CODE END Kernel_Start_Error */
}
/* USER CODE BEGIN 1 */
void uart_tx_add_message(uint8_t* msg, uint16_t len)
{
memcpy(&uart_tx_buffer.buffer[uart_tx_buffer.stopIndex], msg, len);
uart_tx_buffer.stopIndex += len;
tx_semaphore_put(&tx_app_semaphore);
}
void uart_tx_messages(void)
{
if (HAL_UART_Transmit_DMA(&huart1, uart_tx_buffer.buffer, uart_tx_buffer.stopIndex) == TX_SUCCESS)
{
/* anything to do if sending was successful? */
}
}
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
// static uint8_t buf[] = "received bytes: \n";
static uint16_t byteCnt = 0;
HAL_UART_RxEventTypeTypeDef rxEvent = HAL_UARTEx_GetRxEventType(huart);
if (rxEvent == HAL_UART_RXEVENT_IDLE)
{
// update buffer index
uart_rx_buffer.stopIndex = Size;
if (uart_rx_buffer.stopIndex >= uart_rx_buffer.startIndex)
{
byteCnt = uart_rx_buffer.stopIndex - uart_rx_buffer.startIndex;
}
else
{
byteCnt = (uart_rx_buffer.stopIndex + uart_rx_buffer.bufferLen) - uart_rx_buffer.startIndex;
}
// copy the bytes to data buffer
while(byteCnt > 0)
{
data_buffer.buffer[data_buffer.stopIndex++] = uart_rx_buffer.buffer[uart_rx_buffer.startIndex++];
byteCnt--;
// limit buffer index
data_buffer.stopIndex %= data_buffer.bufferLen;
uart_rx_buffer.startIndex %= uart_rx_buffer.bufferLen;
}
// resent received bytes (for debugging)
if (data_buffer.stopIndex >= data_buffer.startIndex)
{
uart_tx_add_message(&data_buffer.buffer[data_buffer.startIndex], data_buffer.stopIndex - data_buffer.startIndex);
}
else
{
uart_tx_add_message(&data_buffer.buffer[data_buffer.startIndex], data_buffer.bufferLen - data_buffer.startIndex);
uart_tx_add_message(&data_buffer.buffer[0], data_buffer.stopIndex);
}
data_buffer.startIndex = data_buffer.stopIndex;
}
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
/* reset index values */
uart_tx_buffer.stopIndex = 0;
}
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
uint8_t buf[] = "ERROR: \n";
uint32_t error = HAL_UART_GetError(huart);
memcpy(&buf[7], &error, 4);
HAL_UART_Transmit(&huart1, (uint8_t*) buf, 12, 1000);
}
/* USER CODE END 1 */
The problem occurs earlier if I have not connected the debugger to the board.
Hope anyone has a suggestion to solve this?!
2023-12-11 05:16 AM
I reduced the UART RX interrupt, but this doesn't help a lot.
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
HAL_UART_RxEventTypeTypeDef rxEvent = HAL_UARTEx_GetRxEventType(huart);
if (rxEvent == HAL_UART_RXEVENT_IDLE)
{
// update buffer index
uart_rx_buffer.stopIndex = Size;
// resent received bytes (for debugging)
if (uart_rx_buffer.stopIndex >= uart_rx_buffer.startIndex)
{
uart_tx_add_message(&uart_rx_buffer.buffer[uart_rx_buffer.startIndex], uart_rx_buffer.stopIndex - uart_rx_buffer.startIndex);
}
else
{
uart_tx_add_message(&uart_rx_buffer.buffer[uart_rx_buffer.startIndex], uart_rx_buffer.bufferLen - uart_rx_buffer.startIndex);
uart_tx_add_message(&uart_rx_buffer.buffer[0], uart_rx_buffer.stopIndex);
}
uart_rx_buffer.startIndex = uart_rx_buffer.stopIndex;
}
}
After some amount of bytes it stops receiving. It feels like it happens when a transmission is ongoing and then the RX interrupt is called. But the transmission is also handled via DMA transfer, so in my opinion it should work.
2023-12-13 01:39 AM
I made some changes to the HAL_UART_ErrorCallback() function to provide more information after I got a message from the callback.
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
char buf[32];
uint32_t error = HAL_UART_GetError(huart);
switch(error)
{
case HAL_UART_ERROR_PE:
strcpy(buf, "Parity Error\n");
break;
case HAL_UART_ERROR_NE:
strcpy(buf, "Noise Error\n");
break;
case HAL_UART_ERROR_FE:
strcpy(buf, "Frame Error\n");
break;
case HAL_UART_ERROR_ORE:
strcpy(buf, "Overrun Error\n");
break;
case HAL_UART_ERROR_DMA:
strcpy(buf, "DMA Transfer Error\n");
break;
case HAL_UART_ERROR_RTO:
strcpy(buf, "Receiver Timeout Error\n");
break;
default:
strcpy(buf, "Unknown Error: \n");
itoa(error, &buf[16], 10);
}
uart_tx_add_message((uint8_t*) buf, strlen(buf));
uart_error = 1;
}
After getting the message "ERROR: 2" in the terminal I figured out a NOISE ERROR. If any error occurs now I re-initialize the peripheral and restart the reception over DMA inside my thread.
void tx_com_thread_entry(ULONG thread_input)
{
// initialize communication
if (HAL_UARTEx_ReceiveToIdle_DMA(&huart1, uartRxBuffer, sizeof(uartRxBuffer)) != HAL_OK)
{
Error_Handler();
}
while(1)
{
if (tx_semaphore_get(&tx_app_semaphore, 100) == TX_SUCCESS)
{
uart_tx_messages();
}
HAL_GPIO_TogglePin(LED_USER3_GPIO_Port, LED_USER3_Pin);
if (uart_error)
{
// re-initialise the peripheral on error
HAL_UART_DeInit(&huart1);
HAL_UART_Init(&huart1);
// initialize communication
if (HAL_UARTEx_ReceiveToIdle_DMA(&huart1, uart_rx_buf, sizeof(uart_rx_buf)) != HAL_OK)
{
Error_Handler();
}
// reset error flag
uart_error = 0;
}
}
}
To reduce the noise on the communication lines I connect the GND from the FTDI Quad UART adapter (build on my own) with the GND of the PCB / power supply. Now it seems to work.
Has anyone another solution to handle errors instead of a full re-init of the peripheral? Maybe I can just flush the buffers and clear the error flag?