2025-11-05 10:02 PM
Hello,
I'm doing Modbus communication using RS485. And using MAX485 IC to convert to RS485. and running on Free RTOS CMSIS.
I have tried and the behavior is random for all below, not consistent. Here I have tried with RS232 and it works like charm. The only issue with RS485. Where Modbus slave we have testing separately and it works fine too.
- Transmit and receive blocking call but seen packet drop during receive
- Transmit and receive interrupt with each byte and complete block of bytes, and saw complete frame is missing in reception.
- Transmit blocking and Receive using DMA, saw complete frame missing during reception.
- Transmit DMA and Receive DMA, saw receive frame is missing.
So lets continue discussion on DMA transmit and receive.
Here is the configuration for DMA and the USART2 is running at 16mhz
- Enable DMA for rx and tx and also enable rs485 hardware flow control, so that it can internally handle the DE pin.
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_RS485Ex_Init(&huart2, UART_DE_POLARITY_HIGH, 0, 0) != HAL_OK)
{
Error_Handler();
}
}
So have one modbus thread which is ready to receive request and send response once data is ready. then to send request I have created one more thread, So in that example thread I will sending query continuously, but once previous request data is ready, so all request are going sequential.
So here I was sending request,
typedef struct
{
uint8_t slaveId;
ModbusFunction functionCode;
uint16_t startAddress;
uint16_t quantity;
uint16_t payload[128]; // for write multiple
uint8_t payloadLength;
} ModbusRequest;
uint8_t frame[8];
void ModbusMaster::request_(const ModbusRequest &req)
{
frame[0] = req.slaveId;
frame[1] = (uint8_t)req.functionCode;
frame[2] = req.startAddress >> 8;
frame[3] = req.startAddress & 0xFF;
frame[4] = req.quantity >> 8;
frame[5] = req.quantity & 0xFF;
uint16_t crc = calcCRC_(frame, 6);
frame[6] = crc & 0xFF;
frame[7] = crc >> 8;
//SCB_CleanDCache_by_Addr((uint32_t*)frame, sizeof(frame));
HAL_UART_Transmit_DMA(uart_, frame, sizeof(frame));
}Here transmission doesn't get any issue.
Now here once interrupt occur for Transmit complete then setting flag to true as signal that transmit is complete
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
transmitComplete = true;
}
void ModbusMaster::handleRequest_()
{
const ModbusRequest& req = mbMsg_.payload.request;
request_(req);
while(!transmitComplete)
{
osDelay(1);
}
transmitComplete = false;
receive_(req);#pragma pack(push, 1)
typedef struct
{
uint8_t slaveId;
ModbusFunction functionCode;
uint8_t dataLength;
uint8_t responseData[MODBUS_MAX_RESPONSE];
ModbusErrorCode status; // 0 = OK, 1 = Error
} ModbusResponse;
#pragma pack(pop)
void ModbusMaster::receive_(ModbusResponse &resp)
{
__HAL_UART_ENABLE_IT(uart_, UART_IT_RTO);
uart_->Instance->CR2 |= USART_CR2_RTOEN;
HAL_UART_Receive_DMA(uart_, (uint8_t*)&resp, 7);
} Here I was sending same type of query so the expected response will be 7 or in case of exception will get 5 so thats why use RTOR feature if no byte receive the generate RTOR error to exit the response.
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//Verify response, if success then request for next query.
//Here breakpoit here for wrong data or no data, and once breakpoint here it show hdmarx lock and busy, check the attache image below for uart details.
}hdmarx lock and busy error
I have also tried to use
- HAL_UART_DMAStop(uart_); before request and after response to clear any flags,
- Clear buffer before use and after use
- Also tried to start receive dma before transmit, just to make sure I wont missed any bytes after transmit, but still same error in hdmarx.
- Tried to disable rs485 hardware flow control and make DE pin as simple output GPIO, and control during transmit and receive, but still same errors.
- I have read somewhere it may have issue with dma (D) cache, which I also tried to clear. like below, but still same issue.
- Before transmit --> SCB_CleanDCache_by_Addr((uint32_t*)frame, sizeof(frame));
- After receive --> SCB_InvalidateDCache_by_Addr((uint32_t*)resp, 7);
- Here DE pin is regulating at voltage 3 and the IC may need DE pin with voltage 5, does this may be the issue that it may not correctly toggling it?
- Also just to be sure we have tried to keep same ground so that it should not create any issues.
Please help me out, I'm running out of my thoughts.