cancel
Showing results for 
Search instead for 
Did you mean: 

Hi, I am losing data when sending longer as 8 Bytes messages over HAL_UART_Transmit_IT. Only when adding a Hal_delay of 40 ms the correct data will appear. The buffer is and TX/RX are not used in the meantime.

RMvP
Associate

I'm using FDCAN and UART (9bits) on a custom board STM32G0B1RBT6. 

The longer messages from FDCAN are split up in 8 bytes max and combined on the board, which works. I can see the correct array of data until the transmit function, but on the bus not anymore or data is missing.

I already tried to check the UART_FLAG_TXE, but this never changes. How can I be certain my TX buffer is empty/send without having a delay?

1 ACCEPTED SOLUTION

Accepted Solutions
gbm
Lead III

mdb_data[] no longer exists after you return from the transmit_mdb(). To solve the problem quickly, declare it as static with fixed, maximum possible size.

That's a common mistake made by beginners, the same problem was already solved earlier today.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

View solution in original post

5 REPLIES 5
RMvP
Associate
void MX_LPUART2_UART_Init(void)
{
 
  /* USER CODE BEGIN LPUART2_Init 0 */
 
  /* USER CODE END LPUART2_Init 0 */
 
  /* USER CODE BEGIN LPUART2_Init 1 */
 
  /* USER CODE END LPUART2_Init 1 */
  hlpuart2.Instance = LPUART2;
  hlpuart2.Init.BaudRate = 9600;
  hlpuart2.Init.WordLength = UART_WORDLENGTH_9B;
  hlpuart2.Init.StopBits = UART_STOPBITS_1;
  hlpuart2.Init.Parity = UART_PARITY_NONE;
  hlpuart2.Init.Mode = UART_MODE_TX_RX;
  hlpuart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  hlpuart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  hlpuart2.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  hlpuart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_TXINVERT_INIT;
  hlpuart2.AdvancedInit.TxPinLevelInvert = UART_ADVFEATURE_TXINV_ENABLE;
  hlpuart2.FifoMode = UART_FIFOMODE_DISABLE;
  if (HAL_UART_Init(&hlpuart2) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetTxFifoThreshold(&hlpuart2, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetRxFifoThreshold(&hlpuart2, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_DisableFifoMode(&hlpuart2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN LPUART2_Init 2 */
 
  /* USER CODE END LPUART2_Init 2 */
 
}
 
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
 
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
  if(uartHandle->Instance==LPUART2)
  {
  /* USER CODE BEGIN LPUART2_MspInit 0 */
 
  /* USER CODE END LPUART2_MspInit 0 */
 
  /** Initializes the peripherals clocks
  */
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_LPUART2;
    PeriphClkInit.Lpuart2ClockSelection = RCC_LPUART2CLKSOURCE_PCLK1;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
    {
      Error_Handler();
    }
 
    /* LPUART2 clock enable */
    __HAL_RCC_LPUART2_CLK_ENABLE();
 
    __HAL_RCC_GPIOC_CLK_ENABLE();
    /**LPUART2 GPIO Configuration
    PC6     ------> LPUART2_TX
    PC7     ------> LPUART2_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF3_LPUART2;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
 
    /* LPUART2 interrupt Init */
    HAL_NVIC_SetPriority(USART2_LPUART2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART2_LPUART2_IRQn);
  /* USER CODE BEGIN LPUART2_MspInit 1 */
 
  /* USER CODE END LPUART2_MspInit 1 */
  }
}
 
void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{
 
  if(uartHandle->Instance==LPUART2)
  {
  /* USER CODE BEGIN LPUART2_MspDeInit 0 */
 
  /* USER CODE END LPUART2_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_LPUART2_CLK_DISABLE();
 
    /**LPUART2 GPIO Configuration
    PC6     ------> LPUART2_TX
    PC7     ------> LPUART2_RX
    */
    HAL_GPIO_DeInit(GPIOC, GPIO_PIN_6|GPIO_PIN_7);
 
    /* LPUART2 interrupt Deinit */
    HAL_NVIC_DisableIRQ(USART2_LPUART2_IRQn);
  /* USER CODE BEGIN LPUART2_MspDeInit 1 */
 
  /* USER CODE END LPUART2_MspDeInit 1 */
  }
}
 
/* USER CODE BEGIN 1 */
 
void transmit_mdb(mdb_message_t mdb_message)
{
	uint16_t mdb_data[mdb_message.length];
	uint16_t mdb_size = mdb_message.length;
 
	// copy message from the queue
	for (int i = 0; i < mdb_message.length; i++) {
		mdb_data[i] = mdb_message.data[i];
	}
	
	HAL_UART_Transmit_IT(&hlpuart2, (uint8_t *) mdb_data, mdb_size);
}

I am using the latest version of the STM CUBE IDE and the UART code is auto generated in the IDE.

gbm
Lead III

mdb_data[] no longer exists after you return from the transmit_mdb(). To solve the problem quickly, declare it as static with fixed, maximum possible size.

That's a common mistake made by beginners, the same problem was already solved earlier today.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
Karl Yamashita
Lead III

You need to check HAL status. You'll need a ring buffer as the FDCAN messages are coming in faster than you can transmit the UART.

if(HAL_UART_Transmit_IT(&hlpuart2, (uint8_t *) mdb_data, mdb_size) != HAL_OK)
{
// error handler
}

Tips and Tricks with TimerCallback https://www.youtube.com/@eebykarl
If you find my solution useful, please click the Accept as Solution so others see the solution.

The scope collapses before completion.

The routines aren't re-entrant, you need to manage the queuing as only a single buffer can be in-flight at any given moment.

The HAL implementation is awful, would suggest refactoring to suit your actual needs/expectations.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Karl Yamashita
Lead III

Here is an example code that simulates 3 CAN messages that are received back to back. Using a ring buffer you can add that data to a Tx buffer and call your transmit function.

If HAL returns HAL_OK then the pointer will increment to the next message that is available to send. If it HAL_BUSY then the pointer isn't incremented so on the next transmit_mdb() call, it'll try to send the message again until successful.

To see how effective it is to check HAL status, in the transmit_mdb() I have a "#define CHECK_HAL_STATUS". If you comment that line out, then the code doesn't check HAL status, which is what your code is doing. The 1st message gets sent while the other 2 get dropped because HAL was still busy.

You can't check out the full source code here https://github.com/karlyamashita/Nucleo-G431_FDCAN_UART

// main.c
 
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_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  PollingInit();
  while (1)
  {
	  PollingRoutine();
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/*
 * PollingRoutine.c
 *
 *  Created on: Mar 8, 2023
 *      Author: codek
 */
 
 
#include "main.h"
#include "PollingRoutine.h"
 
extern UART_HandleTypeDef huart2;
 
 
// simulated FDCAN data
uint8_t data0[] = {1,2,3,4,5,6,7,8};
uint8_t data1[] = {0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88};
uint8_t data2[] = {0xaa,0xbb,0xcc,0xdd,0xee,0xff,0x10,0x20};
 
 
mdb_message_t mdb_message = {0};
 
int status;
 
 
// called before main while loop
void PollingInit(void)
{
 
	// this is where you are receiving FDCAN messages and adding to a UART tx buffer.
 
	status = UART_Tx_AddData(&mdb_message, data0, 8);
	if (status != 0)
	{
		Error_Handler(); // May need to increase buffer size if this is called
	}
 
	// check status for these as well
	UART_Tx_AddData(&mdb_message, data1, 8);
	UART_Tx_AddData(&mdb_message, data2, 8);
}
 
// called from inside main while loop
void PollingRoutine(void)
{
	transmit_mdb(&mdb_message);
}
 
int UART_Tx_AddData(mdb_message_t *msg, uint8_t *data, uint32_t size)
{
	if(msg->msgPtr.iCnt_OverFlow) return 1;
 
	memcpy(&msg->Queue[msg->msgPtr.iIndexIN].data, data, size);
	msg->Queue[msg->msgPtr.iIndexIN].length = size;
	DRV_RingBuffPtr__Input(&msg->msgPtr, BUFFER_SIZE);
 
	return 0;
}
 
int transmit_mdb(mdb_message_t *msg)
{
	int status = 0;
 
	if(msg->msgPtr.iCnt_Handle)
	{
 
#define CHECK_HAL_STATUS
 
#ifdef CHECK_HAL_STATUS
		// checking for HAL_OK before incrementing pointer to next data
		if(HAL_UART_Transmit_IT(&huart2, &msg->Queue[msg->msgPtr.iIndexOUT].data, msg->Queue[msg->msgPtr.iIndexOUT].length) == HAL_OK)
		{
			DRV_RingBuffPtr__Output(&msg->msgPtr, BUFFER_SIZE); // only increment pointer to next message when status is HAL_OK
		}
#else
		// not checking for HAL_OK.
		HAL_UART_Transmit_IT(&huart2, msg->Queue[msg->msgPtr.iIndexOUT].data, msg->Queue[msg->msgPtr.iIndexOUT].length);
		DRV_RingBuffPtr__Output(&msg->msgPtr, BUFFER_SIZE);
#endif
	}
 
	return status;
}
/*
 * PollingRoutine.h
 *
 *  Created on: Mar 8, 2023
 *      Author: codek
 */
 
#ifndef INC_POLLINGROUTINE_H_
#define INC_POLLINGROUTINE_H_
 
#include "main.h"
 
#define BUFFER_SIZE 4 // increase if msgPtr.iCnt_OverFlow > 0
 
typedef struct
{
	struct
	{
		uint32_t length;
		uint8_t data[8];
	}Queue[BUFFER_SIZE];
	RING_BUFF_INFO msgPtr;
}mdb_message_t ;
 
void PollingInit(void);
void PollingRoutine(void);
 
int UART_Tx_AddData(mdb_message_t *msg, uint8_t *data, uint32_t size);
int transmit_mdb(mdb_message_t *mdb_message);
 
#endif /* INC_POLLINGROUTINE_H_ */
//RingBuff.c
 
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

Tips and Tricks with TimerCallback https://www.youtube.com/@eebykarl
If you find my solution useful, please click the Accept as Solution so others see the solution.