cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F105R8T HAL Busy and missing data buffers.

AkilaAwantha
Associate II

I'm using STM32F105R8T to send serial buffer commands to an HMI display.MAX485 chip was used for communication. There are pre-defined command buffers and updatable data buffers. Those buffers are sent through a RS485 bus at the baud rate of 9600. There are lots of commands that I should send to operate the display. I'm using 50ms time interrupt for data transmission. MCU doesn't output correct buffers when there is large amount of data, and it causes to error in the display.

When I check HAL_UART_Transmit function, It begins busy sometimes, and it doesn't want to transmit data. What are the solutions that I can get to drive this display smoothly.

Thanks

Here's my Code

https://github.com/akilawickramasinghe/Vega_ETX_Display_Firmware_V2

17 REPLIES 17
Karl Yamashita
Lead II

Some things I've noticed.

  • You don't check the HAL status to see if it returned HAL_OK. If it didn't, then the transmit had an error.
  • You should only have one HAL_UART_Transmit call for ease of checking for HAL status. Use a buffer queue to hold messages you want to send. Pass only one message to a function that has the HAL_UART_Transmit and only increment the queue pointer if status returned is HAL_OK. Jump out of that routine so you're not blocking other routines from doing their tasks. Then make another call to the Transmit routine to see if there are more messages to send and repeat.
  • Instead of using HAL_UART_Transmit, use HAL_UART_Transmit_IT or HAL_UART_Transmit_DMA instead so other tasks can be done during the clocking out of each data byte and to minimize blocking
I Can't Believe It's Not Butter. If you find my answers useful, click the accept button so that way others can see the solution.

Thank you very much for your support. I'll try this way and let you know the result

Karl Yamashita
Lead II

For anyone else wondering about how to create a queue to transmit UART packets I've mentioned above.

  • I'm using a Nucleo-L432KC for this.
  • I've copied some of @AkilaAwantha​ Drive Mode arrays for this demonstration
  • The full project can be found on GitHub

PollingRoutine.c

/*
 * PollingRoutine.c
 *
 *  Created on: Dec 17, 2022
 *      Author: codek
 */
 
#include "main.h"
#include "PollingRoutine.h"
 
extern UART_HandleTypeDef huart2;
 
UART_QueueStruct txMsg = {0};
 
uint32_t someState;
 
 
//Drive Mode
uint8_t EcoON[8] = { 0x5A, 0xA5, 0x05, 0x82, 0x10, 0x55, 0x01, 0xBA };
uint8_t NormalON[8] = { 0x5A, 0xA5, 0x05, 0x82, 0x10, 0x55, 0x01, 0xBC };
uint8_t SportON[8] = { 0x5A, 0xA5, 0x05, 0x82, 0x10, 0x55, 0x01, 0xBE };
uint8_t modeClear[8] = { 0x5A, 0xA5, 0x05, 0x82, 0x10, 0x55, 0x00, 0x00 };
 
 
// is called before main while loop
void PollingInit(void)
{
	someState = 1; // send EconON
}
 
// main while loop
void PollingRoutine(void)
{
	SomeTask();
	UART_SendMessage();
}
 
// basic example of how to save data to txMsgQueue queue buffer
void SomeTask(void)
{
	int status = 0;
 
	switch(someState)
	{
	case 1:
		status= SaveToUART_TxBuffer(EcoON, sizeof(EcoON));
		break;
	case 2:
		status = SaveToUART_TxBuffer(NormalON, sizeof(NormalON));
		break;
	case 3:
		status = SaveToUART_TxBuffer(SportON, sizeof(SportON));
		break;
	case 4:
		status = SaveToUART_TxBuffer(modeClear, sizeof(modeClear));
		break;
	}
 
	if(status != 0)
	{
		// buffer is full. Use some notification so user is aware and can increase UART_TX_QUEUE_SIZE
	}
 
	someState = 0;
}
 
int SaveToUART_TxBuffer(uint8_t *data, uint32_t dataSize)
{
	if(txMsg.RING_BUFF.ptr.iCnt_OverFlow)
	{
		return 1; // Overflow. Increase UART_TX_QUEUE_SIZE
	}
	memcpy(&txMsg.QUEUE.msg[txMsg.RING_BUFF.ptr.iIndexIN].data, data, dataSize);
	txMsg.QUEUE.msg[txMsg.RING_BUFF.ptr.iIndexIN].dataSize = dataSize;
	DRV_RingBuffPtr__Input(&txMsg.RING_BUFF.ptr, UART_TX_QUEUE_SIZE);
 
	return 0; // no error
}
 
void UART_SendMessage(void)
{
	if(txMsg.RING_BUFF.ptr.iCnt_Handle)
	{
		if(HAL_UART_Transmit_IT(&huart2, txMsg.QUEUE.msg[txMsg.RING_BUFF.ptr.iIndexOUT].data, txMsg.QUEUE.msg[txMsg.RING_BUFF.ptr.iIndexOUT].dataSize) == HAL_OK)
		{
			// transmit is good, increment queue pointer to next available message
			DRV_RingBuffPtr__Output(&txMsg.RING_BUFF.ptr, UART_TX_QUEUE_SIZE);
		}
	}
}
 

PollingRoutine.h

/*
 * PollingRoutine.h
 *
 *  Created on: Dec 17, 2022
 *      Author: codek
 */
 
#ifndef INC_POLLINGROUTINE_H_
#define INC_POLLINGROUTINE_H_
 
 
#define UART_TX_DATA_SIZE 8
#define UART_TX_QUEUE_SIZE 4
 
typedef struct
{
	uint8_t uartPort;
	uint8_t data[UART_TX_DATA_SIZE];
	uint8_t dataSize;
}UART_Data;
 
typedef struct
{
	struct
	{
		UART_Data msg[UART_TX_QUEUE_SIZE];
	}QUEUE;
	struct
	{
		RING_BUFF_INFO ptr;
	}RING_BUFF;
}UART_QueueStruct;
 
void PollingInit(void);
void PollingRoutine(void);
void SomeTask(void);
 
int SaveToUART_TxBuffer(uint8_t *data, uint32_t dataSize);
void UART_SendMessage(void);
 
 
 
#endif /* INC_POLLINGROUTINE_H_ */

RingBuff.c

/*
 * RingBuff.c
 *
 *  Created on: Sep 18, 2019
 *      Author: Karl
 *
 *
*/
 
#include "main.h"
#include "ringBuff.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;
*/
 
//==========================================================
// Layer : DRIVER
//==========================================================
 
void DRV_RingBuffPtr__Clean(RING_BUFF_INFO *ptr) {
	ptr->iIndexIN = 0;
	ptr->iIndexOUT = 0;
 
	ptr->iCnt_Handle = 0;
	ptr->iCnt_OverFlow = 0;
}
 
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--;
	}
}
 
void DRV_RingBuffPtr__Output_JumpToNew(RING_BUFF_INFO *ptr, unsigned int iBufferSize) {
	if (ptr->iCnt_Handle > 1) {
		while (ptr->iCnt_Handle > 1) {
			ptr->iIndexOUT++;
			if (ptr->iIndexOUT >= iBufferSize)
				ptr->iIndexOUT = 0;
			ptr->iCnt_Handle--;
		}
	} else {
		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;
 
 
//================================================
// FUNCTION
//================================================
 
void DRV_RingBuffPtr__Clean(RING_BUFF_INFO *ptr);
 
void DRV_RingBuffPtr__Input(RING_BUFF_INFO *ptr, unsigned int iBufferSize);
 
void DRV_RingBuffPtr__Output(RING_BUFF_INFO *ptr, unsigned int iBufferSize);
void DRV_RingBuffPtr__Output_JumpToNew(RING_BUFF_INFO *ptr, unsigned int iBufferSize);
 
 
#endif // RINGBUFF_H

main.c

// only showing main function to see how I'm calling PollingInit and PollingRoutine function
 
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 */
}

main.h

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.h
  * @brief          : Header for main.c file.
  *                   This file contains the common defines of the application.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 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 */
 
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H
 
#ifdef __cplusplus
extern "C" {
#endif
 
/* Includes ------------------------------------------------------------------*/
#include "stm32l4xx_hal.h"
 
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdlib.h"
#include "stdint.h"
#include "stdbool.h"
#include "stdio.h"
#include "string.h"
 
#include "RingBuff.h"
#include "PollingRoutine.h"
/* USER CODE END Includes */
 
/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */
 
/* USER CODE END ET */
 
/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */
 
/* USER CODE END EC */
 
/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */
 
/* USER CODE END EM */
 
/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);
 
/* USER CODE BEGIN EFP */
 
/* USER CODE END EFP */
 
/* Private defines -----------------------------------------------------------*/
#define MCO_Pin GPIO_PIN_0
#define MCO_GPIO_Port GPIOA
#define VCP_TX_Pin GPIO_PIN_2
#define VCP_TX_GPIO_Port GPIOA
#define SWDIO_Pin GPIO_PIN_13
#define SWDIO_GPIO_Port GPIOA
#define SWCLK_Pin GPIO_PIN_14
#define SWCLK_GPIO_Port GPIOA
#define VCP_RX_Pin GPIO_PIN_15
#define VCP_RX_GPIO_Port GPIOA
#define LD3_Pin GPIO_PIN_3
#define LD3_GPIO_Port GPIOB
 
/* USER CODE BEGIN Private defines */
 
/* USER CODE END Private defines */
 
#ifdef __cplusplus
}
#endif
 
#endif /* __MAIN_H */

I Can't Believe It's Not Butter. If you find my answers useful, click the accept button so that way others can see the solution.

Thank you for the reply. This answer is much appreciated. I'll try this one and let know everyone what is the status of the project

I tested the code. It works like a charm. Thank you very much for your help. I'm currently using the baud rate of 9600. I'm planning to increase it to 115200. Furthermore, I need HMI display configuration files to do that. The Chinese manufacturer still unable to provide that. Because of that reason, I increased the queue size. I'll let know everyone what going on with it. Thanks Everyone.

AkilaAwantha
Associate II

Thanks Everyone who helped me to solve this issue. I marked the best answer.

You're very welcome! I'm glad it worked out and that I was able to help.

If only ST could update their example code so it's not just calling Error_Handler(). In actuality if it doesn't return HAL_OK, then it just returns HAL_BUSY. ST should show how to retransmit the data again and pretty much 99% of these UART issues people post will go away.

Also ST only shows 1 array for transmitting data. The problem with that is if data is still in the process of being transmitted, new data could be copied to the array which messes up the current data. Having a queue for several messages eliminates that issue.

I Can't Believe It's Not Butter. If you find my answers useful, click the accept button so that way others can see the solution.