2023-03-08 07:49 AM
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?
Solved! Go to Solution.
2023-03-08 08:32 AM
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.
2023-03-08 07:51 AM
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.
2023-03-08 08:32 AM
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.
2023-03-08 09:25 AM
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
}
2023-03-08 09:31 AM
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.
2023-03-08 05:53 PM
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