单片机uart实现modbus-rtu协议

modbus

uart波特率为9600,modbus帧间隔3.5个字符即3.5ms,开启一个4ms的定时器,在uart接收中断中刷新4ms定时器,定时器超时则表示连续4ms没有接收到数据,认为一帧结束,mb_frame_end_flag = 1;

  • modbus.h
/* 寄存器映射表 */
struct reg_map_table {
   
	u16 start_addr;
	u16 count;
	u16 *p_reg_struct;
};

typedef struct {
   
    u16 cell_vol1;
    u16 cell_vol2;
    u16 cell_vol3;
    u16 cell_vol4;
    s16 current;
    s16 temp1;
    s16 temp2;
    ......
}bms_info_st;

typedef struct {
   
    u16 over_vol;
    s16 over_cur;
    s16 over_temp;
    ......
}protect_para_st;

typedef struct {
   
	u16 hw_ver[10];
	u16 sw_ver[10];
    u16 sys_sn[10];
    ......
}sys_para_st;

typedef struct {
   
    u16 vol_gain;
    s16 cur_offset;
    s16 cur_gain;
    u16 temp_offset;
    ......
}cali_para_st;

/* 4组寄存器 */
extern bms_info_st g_bms_info;  //采集的数据,只读
extern protect_para_st g_protect_para;  //设置的保护参数,可读可写入flash
extern sys_para_st g_sys_para;
extern cali_para_st g_cali_para;

extern u8 mb_buf[256];
extern u16 mb_cnt;
extern u8 mb_frame_end_flag;

extern void modbus_task(void);
  • modbus.c
#include "modbus.h"

const u8 mb_salve_addr = 0x01;  //modbus从机地址
u16* p_reg;  //寄存器映射指针

u8 mb_buf[256] = {
   0};  //uart接收缓冲
u16 mb_cnt = 0;  //uart接收一帧的长度
u8 mb_frame_end_flag = 0;  //帧结束标志

bms_info_st g_bms_info;  //运行数据
protect_para_st g_protect_para;  //保护参数
sys_para_st g_sys_para;  //系统参数
cali_para_st g_cali_para;  //校准参数

struct reg_map_table mb_reg_map_table[4] = {
   
	{
     0, sizeof(g_bms_info)/2, &g_bms_info},
	{
   256, sizeof(g_protect_para)/2, &g_protect_para},
	{
   512, sizeof(g_sys_para)/2, &g_sys_para},
	{
   768, sizeof(g_sys_para)/2, &g_cali_para},
};

/* modbus CRC16 */
u16 mb_crc16(u8 *buf, u16 len
下面是一个简单的W601单片机Modbus-RTU主站C语言程序示例,您可以根据需要进行修改和调整: ``` c #include <reg51.h> #define uchar unsigned char #define uint unsigned int sbit RS485_DIR = P1^0; uchar id = 1; // 主机ID uchar cmd[8] = {0}; // Modbus-RTU命令 uchar buf[8] = {0}; // 数据缓冲区 void init_uart(void) { SCON = 0x50; TMOD &= 0x0F; TMOD |= 0x20; TH1 = 0xFD; TL1 = 0xFD; TR1 = 1; } void send_data(uchar *data, uchar len) { uchar i; for(i = 0; i < len; i++) { SBUF = data[i]; while(!TI); TI = 0; } } void receive_data(uchar *data, uchar len) { uchar i; for(i = 0; i < len; i++) { while(!RI); data[i] = SBUF; RI = 0; } } uchar calc_crc(uchar *data, uchar len) { uchar crc = 0xFF; uchar i, j; for(i = 0; i < len; i++) { crc ^= data[i]; for(j = 0; j < 8; j++) { if(crc & 0x01) { crc = (crc >> 1) ^ 0xA0; } else { crc >>= 1; } } } return crc; } void send_cmd(uchar *data, uchar len) { RS485_DIR = 1; // 发送模式 send_data(data, len); } void receive_cmd(uchar *data, uchar len) { RS485_DIR = 0; // 接收模式 receive_data(data, len); } void main() { init_uart(); while(1) { // 构造Modbus-RTU命令 cmd[0] = id; // 设备ID cmd[1] = 0x03; // 功能码 cmd[2] = 0x00; // 起始地址高位 cmd[3] = 0x00; // 起始地址低位 cmd[4] = 0x00; // 数据长度高位 cmd[5] = 0x01; // 数据长度低位 cmd[6] = calc_crc(cmd, 6); // CRC校验低位 cmd[7] = calc_crc(cmd+6, 2); // CRC校验高位 // 发送Modbus-RTU命令 send_cmd(cmd, 8); // 接收Modbus-RTU响应 receive_cmd(buf, 5); // 解析Modbus-RTU响应 if(buf[0] == id && buf[1] == 0x03 && buf[2] == 0x02) { uint value = buf[3] << 8 | buf[4]; // 处理接收到的数据 // ... } } } ``` 以上是一个简单的W601单片机Modbus-RTU主站C语言程序示例,仅供参考。如果您有更多的需求,可以自行搜索相关资料或者咨询专业人士。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值