1. 实现思路
此程序中, 串口通信方式: 115200-n-8-1, modbus协议要求帧与帧之间的间隔必须大于3.5个字符时间间隙作为帧与帧之间的分割.
字符时间计算公式: interval_time = character_interval * 8 / baud_speed * 10 ^ 6 (微秒)
1.1 设置定时器, 超时时间为interval_time.
1.2 设置stm32的uart串口接收数据中断, 每次读取数据都重置定时器计数为0
1.3 定时器超时后, 说明此时modbus帧已经传输结束, 在定时器超时函数中处理响应.
2.编程
2.1 设置定时器超时
使用的波特率为115200bit/s, 则字符间隔时间大约为2431微妙.
在cubemx中选择一个定时器设置超时时间为2431微妙.
这里我们使用的时钟是16MHz, 定时器prescaler设置为16, 每隔1us触发一次计时器计时, 共计时2430次.
2.2 设置UART接收字符中断
- cubemx配置好程序初始化后, 在CR1寄存器中启用RXNEIE标记位, 启用RDR可读中断
- 在USART中断入口判断RDR寄存器是否可读, 可读则将数据加入全局缓冲区, 并修改缓冲区长度, 修改定时器计数寄存器 CNT = 0
//main.c文件中修改
HAL_TIM_Base_Start_IT(&htim14);//启动定时器
huart1.Instance->CR1 |= 1 << 5;//enable reception interruption
//stm32_it.c文件中修改UART1中断入口函数USART1_IRQHandler
if(huart1.Instance->ISR & 1 << 5)
{
// uint8_t data = huart1.Instance->RDR;
// while( (huart1.Instance->ISR & 1 << 7) == 0);
// huart1.Instance->TDR = data;
uart_buf[uart_buf_len++] = huart1.Instance->RDR;;
TIM14->CNT = 0;
}
- 当定时器中断触发后, 说明上一个帧已经接收完成, 在定时器中断超时函数处理帧.
2.3定时器中断函数实现modbusRTU协议解析
2.3.1 crc校验
modbus协议使用crc16-modbus标准校验.
此处通过直接计算方法生成crc.
#define POLYNOMIAL 0xa001
uint16_t generate_crc(uint8_t* buf, uint8_t len)
{
uint16_t crc = 0xffff;
uint8_t i, j;
for(i = 0; i < len; i++)
{
crc ^= buf[i];
for(j = 0; j < 8; j++)
{
uint8_t tmp = crc & 0x0001;
crc >>= 1;
if( tmp == 1)
crc ^= POLYNOMIAL;
}
}
return crc;
}
- Tx:106-01 03 00 00 00 0A C5 CD
这是从modbuspoll中获取到的一个帧序列, 可以通过此序列进行crc校验, 后两个字节为校验码.
低地址在前, 高地址在后. CD C5 为其crc值
2.3.2 判断是否解析帧
首先判断帧中的请求地址是否为此设备, 如果不是, 将第二个字节的高位置1, 重新生成crc, 发送给主机.文章来源:https://www.toymoban.com/news/detail-610972.html
uint8_t check_receive_packet()
{
if( uart_buf[0] != local_address) return 3;
uint16_t crc = generate_crc(uart_buf, uart_buf_size - 2);
if(crc != (uart_buf[uart_buf_size - 1] << 8 | uart_buf[uart_buf_size - 2]) )
return 8;
return 0;
}
2.3.3 帧解析文章来源地址https://www.toymoban.com/news/detail-610972.html
if(uart_buf_size > 0)
{
uint8_t i = 0;
if( (ret = check_receive_packet()) )
{
send_buf[i++] = uart_buf[0];
send_buf[i++] = uart_buf[1] | 0x80;
send_buf[i++] = ret; //exception code
crc = generate_crc(send_buf, i);
send_buf[i++] = crc & 0xff;
send_buf[i++] = crc >> 8;
send_data(send_buf, i);
uart_buf_size = 0;
processed = 1;
return;
}
uint8_t function_code = uart_buf[1];
uint16_t reg_addr = uart_buf[2] << 8 | uart_buf[3];
//uint16_t val;
switch(function_code)
{
case 0x03:
if( reg_addr == 0x01)
{
send_buf[i++] = uart_buf[0];
send_buf[i++] = uart_buf[1];
send_buf[i++] = 2;
send_buf[i++] = (uint8_t)(g_device_id >> 8);
send_buf[i++] = (uint8_t)g_device_id;
crc = generate_crc(send_buf, i);
send_buf[i++] = crc & 0xff;
send_buf[i++] = crc >> 8;
send_data(send_buf, i);
}else if( reg_addr == 0x02)
{
send_buf[i++] = uart_buf[0];
send_buf[i++] = uart_buf[1];
send_buf[i++] = sizeof(uint8_t) * 2;
send_buf[i++] = g_temperature_int;
send_buf[i++] = g_temperature_dot;
crc = generate_crc(send_buf, i);
send_buf[i++] = crc & 0xff;
send_buf[i++] = crc >> 8;
send_data(send_buf, i);
} else if( reg_addr == 0x03)
{
send_buf[i++] = uart_buf[0];
send_buf[i++] = uart_buf[1];
send_buf[i++] = sizeof(uint8_t) * 2;
send_buf[i++] = g_humidity_int;
send_buf[i++] = g_humidity_dot;
crc = generate_crc(send_buf, i);
send_buf[i++] = crc & 0xff;
send_buf[i++] = crc >> 8;
send_data(send_buf, i);
} else if( reg_addr == 0x04)
{
send_buf[i++] = uart_buf[0];
send_buf[i++] = uart_buf[1];
send_buf[i++] = sizeof(uint8_t) * 2;
uint8_t _int = (int)g_humidity_start;
uint8_t x = g_humidity_start - _int;
uint8_t _dot = (int)(x * 100); //keep 2 decimals
send_buf[i++] = _int;
send_buf[i++] = _dot;
crc = generate_crc(send_buf, i);
send_buf[i++] = crc & 0xff;
send_buf[i++] = crc >> 8;
send_data(send_buf, i);
}
else if(reg_addr == 0x05)
{
send_buf[i++] = uart_buf[0];
send_buf[i++] = uart_buf[1];
send_buf[i++] = sizeof(uint8_t) * 2;
uint8_t _int = (int)g_temperature_start;
uint8_t x = g_temperature_start - _int;
uint8_t _dot = (int)(x * 100); //keep 2 decimals
send_buf[i++] = _int;
send_buf[i++] = _dot;
crc = generate_crc(send_buf, i);
send_buf[i++] = crc & 0xff;
send_buf[i++] = crc >> 8;
send_data(send_buf, i);
}
break;
case 0x06:
i = 1;
//val = uart_buf[4] << 8 | uart_buf[5];
if( reg_addr == 0x04)
{
//register_val = val;
//send_data(uart_buf, uart_buf_len);
uint8_t _dot = uart_buf[4];
uint8_t _int = uart_buf[5];
g_humidity_start = uint2float(_int, _dot);
}else if(reg_addr == 0x05)
{
uint8_t _dot = uart_buf[4];
uint8_t _int = uart_buf[5];
g_temperature_start = uint2float(_int, _dot);
}
send_data(uart_buf, uart_buf_size);
break;
}
if(i == 0)
{
while(i < uart_buf_size)
{
fputc(uart_buf[i], (FILE*)100);
i++;
}
}
processed = 1;
uart_buf_size = 0;
}
到了这里,关于stm32基于UART串口实现modbusRTU(软件方式)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!