2024-03-13 12:40 PM - edited 2024-03-13 12:49 PM
I am trying to implement MODBUS protocol. STM32 receives an initial message of 8 bytes and then send a response of 8 bytes. However, the next message received by the STM32 doesn't trigger the HAL_UART_Receive_IT callback.
uint8_t reception_complete = FALSE; // turns true when a MODBUS command is received
uint8_t response[8];
uint8_t response_ready = FALSE;
int main(void)
{
while (1)
{
// Hang until a MODBUS message is received (8 bytes)
while(reception_complete != TRUE)
{
HAL_UART_Receive_IT(&huart2, &recvd_data, 1);
if (response_ready == TRUE)
{
//send the response back to the master (blocking mode)
uint16_t resp_len = sizeof(response);
HAL_UART_Transmit(&huart2, (uint8_t*)response, resp_len, HAL_MAX_DELAY);
response_ready = FALSE;
}
}
}
}
2024-03-13 12:44 PM
Don't create these false interdependencies...
HAL_UART_Transmit() blocks.
Have your receive callback light off the next HAL_UART_Receive_IT() so you don't miss it, but first move the received data into a holding buffer that's not going to be overwritten by the inbound data should more of that arrive in the mean time while you're ignoring it. ie >resp_len byte times..
2024-03-13 02:49 PM
That's actually how I had it originally but changed it because I thought maybe calling HAL_UART_Transmit() from HAL_UART_Receive_IT() could have been causing the issue.
I put it back to how it was with the exact same result. I placed breakpoints and I can see that execution is making it to the HAL_UART_Receive_IT(&huart2, &recvd_data, 1); but it just isn't jumping to the Interrupt after HAL_UART_Transmit() is called.
//MODBUS params
uint8_t data_buffer[100];
uint8_t recvd_data; //will hold bytes received on RX pin
uint8_t reception_complete = FALSE; // turns true when a MODBUS command is received
uint8_t char_count = 0; // counts up number of bytes received
uint8_t slave_address = 200;
uint8_t modbus_code = 0;
uint8_t response[8];
//values
uint8_t motor1_spd_reg = 0;
uint16_t registers[100];
uint8_t reg_changed[100];
int main(void)
{
HAL_Init();
SystemClock_Config(SYS_CLOCK_FREQ_48_MHZ);
uint32_t clk_freq = Get_PCLK1TIM();
UART2_Init();
while (1)
{
HAL_UART_Receive_IT(&huart2, &recvd_data, 1);
// Check and see if there has been any updates to the register values
if (reg_changed[motor1_spd_reg])
{
// pulse = clk_spd * (1 /(2*freq))
uint32_t pulse_freq = registers[motor1_spd_reg];
uint32_t pulse = clk_freq * (1 / (2 * pulse_freq));
//based on the frequency given by the user
TIMER2_CH1_Init(pulse);
//Start the timer
if(HAL_TIM_OC_Start_IT(&htimer2, TIM_CHANNEL_1) != HAL_OK)
{
Error_handler();
}
reg_changed[motor1_spd_reg] = FALSE;
}
}
}
void UART2_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.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_handler();
}
}
// count number of received bits and after enough, read MODBUS message
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
data_buffer[char_count] = recvd_data;
if(char_count == 1)
{
modbus_code = recvd_data;
}
if (modbus_code < 7 && char_count == 7)
{
//8 bytes
reception_complete = TRUE;
Decode_MODBUS();
} else
{
if (modbus_code >= 7 && char_count >= 6)
{
// For MODBUS messages with codes 7 and up, the 6th byte indicates how many bytes are left in the message
if (char_count == data_buffer[4] + 8)
{
reception_complete = TRUE;
Decode_MODBUS();
}
}
}
if (reception_complete == FALSE)
{
char_count++;
}
else
{
char_count = 0;
reception_complete = FALSE;
}
}
/***********************************STANDARD MODBUS FUNTIONS ********************************************/
void Decode_MODBUS(void)
{
if (data_buffer[0] == slave_address)
{
// The response begins as an echo
for (uint8_t i = 0; i<7; i++)
{
response[i] = data_buffer[i];
}
response[7] = Calculate_checkSum();
// Different actions depending on the MODBUS code (2nd byte)
if (data_buffer[2] == 0)
{
//write GPIO
GPIO_TypeDef *GPIOx;
if (data_buffer[4] == 0)
GPIOx = GPIOA;
if (data_buffer[4] == 1)
GPIOx = GPIOB;
if (data_buffer[4] == 2)
GPIOx = GPIOC;
if (data_buffer[4] == 3)
GPIOx = GPIOD;
uint8_t pinstate;
if (data_buffer[5] == PIN_HIGH)
{
pinstate = GPIO_PIN_SET;
}
if (data_buffer[5] == PIN_LOW)
{
pinstate = GPIO_PIN_RESET;
}
HAL_GPIO_WritePin(GPIOx, data_buffer[3], pinstate);
}
if (data_buffer[2] == 1)
{
//read GPIO
GPIO_TypeDef *GPIOx;
if (data_buffer[4] == 0)
GPIOx = GPIOA;
if (data_buffer[4] == 1)
GPIOx = GPIOB;
if (data_buffer[4] == 2)
GPIOx = GPIOC;
if (data_buffer[4] == 3)
GPIOx = GPIOD;
response[5] = HAL_GPIO_ReadPin(GPIOx, data_buffer[3]);
}
if (data_buffer[2] == 2)
{
//write value
uint8_t reg_num = data_buffer[3];
uint8_t data1 = data_buffer[4];
uint8_t data2 = data_buffer[5];
registers[reg_num] = data2 * 256 + data1;
}
if (data_buffer[2] == 3)
{
//read value
uint8_t reg_num = data_buffer[3];
response[4] = registers[reg_num] / 256;
response[5] = registers[reg_num] - (data_buffer[3] * 256);
reg_changed[reg_num] = TRUE;
}
if (data_buffer[2] == 4)
{
//set pin mode
GPIO_TypeDef *GPIOx;
if (data_buffer[4] == 0)
GPIOx = GPIOA;
if (data_buffer[4] == 1)
GPIOx = GPIOB;
if (data_buffer[4] == 2)
GPIOx = GPIOC;
if (data_buffer[4] == 3)
GPIOx = GPIOD;
if (data_buffer[4] == 4)
GPIOx = GPIOF;
GPIO_Init(GPIOx, data_buffer[3], data_buffer[5]);
}
if (data_buffer[2] == 6)
{
//read analog data
}
//send the response back to the master (blocking mode)
uint16_t resp_len = sizeof(response);
HAL_UART_Transmit(&huart2, (uint8_t*)response, resp_len, HAL_MAX_DELAY);
} else {
//nothing will happen (message is discarded)
}
}
2024-03-13 05:19 PM
After looking closer, it seems like huart->RxState is getting suck in HAL_UART_STATE_BUSY_RX. I don't understand why or how to fix it.
Even if I do 1 Tx/Rx then wait a really long time (a minute) then do another, then I send a Tx to the STM32, the RX gets stuck in this busy state.