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
}

If smoke escapes your device, put the smoke back in. It'll still work as a conversation piece. If you find my answers useful, click the Accept as Solution button so that way others can 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

If smoke escapes your device, put the smoke back in. It'll still work as a conversation piece. If you find my answers useful, click the Accept as Solution button so that way others can see the solution.