Reputation: 93
I have a problem with the HAL_UART_Transmit_IT function from the HAL library. I use this function in two places in the program:
In function HAL_UART_RxCpltCallback, where I write back to UART the data received on interruption (just an echo). Here everything works correctly.
In Print_CAN_Frame function where I send every 3s message on UART (from main function). Previously I used the blocking function there (HAL_UART_Transmit) and program prited data correctly. But when I changed it to innterrupt (HAL_UART_Transmit_IT) I get weird characters as in screen here: TeraTerm screen. The most interesting thing is that both functions take the same arguments so they should not differ ... I use Nucleo F303RE board.
#include "main.h"
#include "main.h"
#include "stm32f3xx_hal.h"
#include "stm32f303xe.h"
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <inttypes.h>
void SystemClockConfig(void);
void UART2_Init(void);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void ClearArray(uint8_t array[], uint32_t size);
void Print_CAN_Frame(char CanFrameName[], uint32_t CanID, uint32_t CanDlc, uint8_t CANmsg[]);
void parseFromUART(char CanFrame[]);
uint8_t* convertToHex(char *string);
void saveDataToFrame(CAN_MessageTypeDef canBuffer);
#define TRUE 1
#define FALSE 0
#define FIFO_BUFFER g_rxFifo;
CAN_MessageTypeDef IPC_Ligths =
{
0x2214000, // ID
6, // DLC
{0x00,0x00,0x00,0x00,0x00,0x00}, // TX frame
{0} // RX frame initialization
};
CAN_MessageTypeDef canUartBuffer;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
uint8_t data_buffer[70];
uint32_t count = 0;
uint8_t rcvd_data;
uint8_t flag_UART_TX_COMPLETED = FALSE;
uint8_t flag_UART_RX_COMPLETED = FALSE;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
HAL_UART_Receive_IT(&huart2, &rcvd_data, 1);
while (1)
{
Print_CAN_Frame("Tx", IPC_Ligths.ID, IPC_Ligths.DLC, IPC_Ligths.CAN_Tx);
HAL_Delay(3000);
while((flag_UART_RX_COMPLETED && flag_UART_TX_COMPLETED) == TRUE)
{
ClearArray(data_buffer, 70);
count = 0;
flag_UART_RX_COMPLETED = FALSE;
flag_UART_TX_COMPLETED = FALSE;
}
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2;
PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
static void MX_USART2_UART_Init(void)
{
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
}
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin : B1_Pin */
GPIO_InitStruct.Pin = B1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : LD2_Pin */
GPIO_InitStruct.Pin = LD2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (rcvd_data == '\r') {
data_buffer[count++] = '\r';
flag_UART_RX_COMPLETED = TRUE;
if (HAL_UART_Transmit_IT(&huart2, data_buffer, count) != HAL_OK)
{
Error_Handler();
}
}
else
{
data_buffer[count++] = rcvd_data;
}
HAL_UART_Receive_IT(&huart2, &rcvd_data, 1);
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
flag_UART_TX_COMPLETED = TRUE;
}
void ClearArray(uint8_t array[], uint32_t size)
{
for (int i = 0; i < size; ++i)
{
array[i] = 0;
}
}
void Print_CAN_Frame(char CanFrameName[], uint32_t CanID, uint32_t CanDlc, uint8_t CANmsg[])
{
char buffer[100] = {0};
sprintf(buffer,"CAN_%s| ID:0x%02X| DLC:%d| FRAME: ",CanFrameName,(unsigned int)CanID,(unsigned int)CanDlc);
for (uint8_t i = 0; i<CanDlc; i++)
{
sprintf(buffer+strlen(buffer),"%02X ",*(CANmsg+i)); // print all DATA elements one by one
}
sprintf(buffer+strlen(buffer),"\n\r"); // add in the end of each frame new line and ....
// Problem in here:
HAL_UART_Transmit_IT(&huart2, (uint8_t*)buffer, strlen(buffer));
// HAL_UART_Transmit(&huart2, (uint8_t*)buffer, strlen(buffer),HAL_MAX_DELAY);
}
void Error_Handler(void)
{
while (1)
;
}
Upvotes: 2
Views: 5613
Reputation: 93
I think I found a solution. Data in Print_Can_Frame function after typecasting to uint8 array and sending the array in this form started to work properly:
Clear_Array(uint_8Buffer, 70);
for (uint8_t i = 0U; i<strlen(buffer);i++)
{
uint_8Buffer[i] = (uint8_t)buffer[i];
}
if (HAL_UART_Transmit_IT(&huart2, uint_8Buffer, strlen(buffer)) != HAL_OK)
{
Error_Handler();
}
The solution is not pretty, but it works
Upvotes: 0
Reputation: 33601
Caveat: Not a total solution but some observations.
Race condition
It appears that you have some race conditions between ISRs and main function. When you do something at task level that changes variables that are used in an ISR, the task level has to wrap such access to common variables in (e.g.):
disable_interrupts();
// change variables used by ISR ...
enable_interrupts();
Likewise for any variables changed by an ISR that are read/used by task level. They should also be wrapped (e.g.):
disable_interrupts();
int saved_value_1 = global_variable_1;
int saved_value_2 = global_variable_2;
enable_interrupts();
// do stuff with the saved_value_* variables ...
Your Print_CAN_Frame
is broken.
In either version, buffer
is function scoped (i.e.) it is on the stack. When the function returns the buffer goes out of scope (i.e. It is no longer usable).
HAL_UART_Transmit_IT
is non-blocking. When it returns, nothing has been transmitted. It just sets the buffer address/count into the control struct. It expects that the buffer will remain stable until all chars have been sent [under interrupt control at some later time].
As you have it, after return from your function, the control struct is pointing to stack memory that can have arbitrary values [because other functions may be called and overwrite the stack frame with unrelated values].
Make buffer
in Print_CAN_Frame
a static
variable. Or, better yet, change the buffer name to (e.g.) print_can_buffer
and make it global/file scope as you have done for data_buffer
You're not checking error/return values from the HAL_*
primitives.
You may need to design an "event ring queue" for debugging. I've done this many times in the past and find it extremely useful.
At key points in your code, call an event_add
function that stores in a ring queue of structs that have a timestamp, event type, and a type specific value (e.g.):
event_add(EVENT_TYPE_TXSEND,'\r');
After all is done, you can dump out this ring queue to see what happened at critical times, so you have a history similar to a logic analyzer trace [similar to Sun's dtrace
].
The ring queue primitives [notably, event_add
] should be wrapped in cli/sti
pairs as mentioned above.
Upvotes: 1