单片机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)
{
    u16 crc = 0xffff;
    u16 i = 0, j = 0;
    
    for (i = 0; i < len; i++)
    {
        crc ^= buf[i];
        
        for (j = 0; j < 8; j++)
        {
            if (crc & 0x01)
            {
                crc >>= 1;
                crc ^= 0xA001;
            }
            else
            {
                crc >>= 1;
            }				
        }
    }   
    return (crc);
}

/* 地址映射寄存器(结构体), 返回错误码 */
u8 mb_reg_map(u16 addr, u16 count)
{
	u8 i = 0;

	for (i = 0; i < 4; i++)
	{
		if ((addr >= mb_reg_map_table[i].start_addr)
			&& (addr <= mb_reg_map_table[i].start_addr + mb_reg_map_table[i].count))
		{
			if ((addr + count) <= (mb_reg_map_table[i].start_addr + mb_reg_map_table[i].count))
			{
				p_reg = mb_reg_map_table[i].p_reg_struct + addr - mb_reg_map_table[i].start_addr;
				return 0;
			}
			else
			{
				return 0x03;  //请求的数据长度错误
			}
		}
	}
	return 0x02;  //起始地址错误
}

/* 错误响应帧:
 * 1byte 从机地址
 * 1byte 功能码 + 0x80
 * 1byte 错误码: 
 * 		0x01->不支持的功能码
 * 		0x02->起始地址错误
 * 		0x03->请求的数据长度错误
 * 		0x04->数据读写失败
 * 2bytes CRC16
 */
void mb_err_ack(u8 op_code, u8 err_code)
{
	u8 mb_txbuf[5];
	u16 crc16;
	mb_txbuf[0] = mb_salve_addr;
	mb_txbuf[1] = op_code | 0x80;
	mb_txbuf[2] = err_code;
	crc16 = mb_crc16(mb_txbuf, 3);
	mb_txbuf[3] = crc16 >> 8;
	mb_txbuf[4] = crc16;
	uart2_tx(mb_txbuf, 5);
}

//0x03: 读寄存器指令
/* 1byte 从机地址
 * 1byte 功能码:0x03
 * 2bytes 起始寄存器地址
 * 2bytes 寄存器个数N
 * 2bytes CRC16
 */
//响应: 地址 + 0x03 + 字节数(1B) + N*寄存器值 + CRC16
void mb_read_reg(u16 reg_addr, u16 count)
{
	u16 crc16;
	u8 i = 0;
	u8 err_code = 0;

	/* 映射寄存器 */
	err_code = mb_reg_map(reg_addr, count);
	if (err_code)
	{
		mb_err_ack(0x03, err_code);
		return;
	}
	
	mb_buf[0] = mb_salve_addr;
	mb_buf[1] = 0x03;
	mb_buf[2] = 2*count;
	for (i = 0; i < count; i++)
	{
		mb_buf[2*i + 3] = p_reg[i] >> 8;
		mb_buf[2*i + 4] = p_reg[i];
	}
	crc16 = mb_crc16(mb_buf, 2*count + 3);
	mb_buf[3 + 2*count] = crc16 >> 8;
	mb_buf[4 + 2*count] = crc16;
	uart2_tx(mb_buf, 5 + 2*count);
}

//0x06: 写单个寄存器
/* 1byte 从机地址
 * 1byte 功能码:0x06
 * 2bytes 寄存器地址
 * 2bytes 寄存器值
 * 2bytes CRC16
 */
//响应: 同接收
void mb_write_reg(u16 addr, u16 val)
{
	u8 i = 0;
	u16 VPackTemp = 0;
	s16 cur_temp = 0;
	
	/* 映射寄存器 */
	u8 err_code = mb_reg_map(addr, 0);
	if (err_code)
	{
		mb_err_ack(0x06, err_code);
		return;
	}
	
	*p_reg = val;
	
	switch (addr)
	{
		case 0x0300:
			g_bms_info.pack_v = val >> 8 | val << 8;
			for(i=ucCellNumOffset; i<(ucCellNumOffset+ucCellNum); i++)
			{
				VPackTemp += AFE.uiCell[i];
			}
			E2uiVPackGain = (U32)CALIPACKVOL*VPackTemp / val;
		break;
		case 0x0301:
			E2siCadcOffset = AFE.siCurr;
		break;
		case 0x0302:
			cur_temp = val >> 8 | val << 8;
			E2siCadcGain = (S32)CALICUR * (AFE.siCurr - E2siCadcOffset) / cur_temp;
		break;
	}
	
	uart2_tx(mb_buf, 8);
}

//0x10: 写多个寄存器
/* 1byte 从机地址
 * 1byte 功能码:0x10
 * 2bytes 寄存器起始地址
 * 2bytes 寄存器个数 N
 * 1byte 字节数 2*N
 * 2*N bytes 2*N 要写入寄存器的值
 * 2bytes CRC16
 */
//响应: 地址 + 0x10 + 寄存器起始地址 + 寄存器个数N + CRC16
void mb_write_multi_reg(u16 addr, u16 count, u16* val)
{
	u16 crc16;
	u8 i = 0;
	/* 映射寄存器 */
	u8 err_code = mb_reg_map(addr, count);
	if (err_code)
	{
		mb_err_ack(0x10, err_code);
		return;
	}
	
	my_memcpy((u8*)val, (u8*)p_reg, 2 * count);

	mb_buf[0] = mb_salve_addr;
	mb_buf[1] = 0x10;
	mb_buf[2] = addr >> 8;
	mb_buf[3] = addr;
	mb_buf[4] = count >> 8;
	mb_buf[5] = count;
	crc16 = mb_crc16(mb_buf, 6);
	mb_buf[6] = crc16 >> 8;
	mb_buf[7] = crc16;
	uart2_tx(mb_buf, 8);
}

/* crc16校验 */
static u8 mb_crc16_check(void)
{
	u16 recv_crc16 = 0;
	u16 calc_crc16 = 0;
	
	recv_crc16 = mb_buf[mb_cnt - 1] | mb_buf[mb_cnt - 2] << 8;
	calc_crc16 = mb_crc16(mb_buf, mb_cnt - 2);
	
	return (calc_crc16 == recv_crc16);
}

/* 上位机读取数据前,更新modbus协议的数据 */
static void mb_get_data(void)
{
    //禁止uart接收中断
    my_memset(0);
    
    g_bms_info.cell_vol1 = 采集的电压1;
    g_bms_info.cell_vol2 = 采集的电压2;    
    ......
    ......    
    //使能uart接收中断
}

/* 上位机下发设置参数后,写入flash */
static void mb_set_data(void)
{
    //禁止uart接收中断
	E2uiOVvol			= g_protect_para.over_vol;
	E2uiOCcur			= g_protect_para.over_cur;
    ......
    ......
    write_flash_flag = 1;
    //使能uart接收中断
}

/* 解析帧数据 */
void mb_parse_cmd(void)
{
	u8 slave_addr = 0;
	u8 op_code = 0;
	u16 reg_addr = 0;
	u16 reg_cnt = 0;
	u16 reg_val = 0;
	u16* p_val = NULL;
	
	slave_addr = mb_buf[0];
	reg_addr = mb_buf[2] << 8 | mb_buf[3];
	reg_cnt = mb_buf[4] << 8 | mb_buf[5];
	reg_val = mb_buf[4] << 8 | mb_buf[5];
	p_val = (u16*)&mb_buf[7];
	
	if (slave_addr == mb_salve_addr)
	{
		op_code = mb_buf[1];
		
		switch (op_code) 
		{
			case 0x03:
				mb_get_data();
				mb_read_reg(reg_addr, reg_cnt);
			break;
			case 0x06:
				mb_get_data();
				mb_write_reg(reg_addr, reg_val);
				mb_set_data();
			break;
			case 0x10:
				mb_get_data();
				mb_write_multi_reg(reg_addr, reg_cnt, p_val);
				mb_set_data();
			break;
			default:
				mb_err_ack(op_code, 0x01);  /* 不支持的op_code */
			break;
		}
		
	}
	else
	{
		return;
	}
}

void modbus_task(void)
{
	if (mb_frame_end_flag == 1)
	{
		mb_frame_end_flag = 0;
		
		if (mb_crc16_check())
		{	
			mb_parse_cmd();  /* 解析协议 */
		}
		else
		{
			goto frame_err;
		}

frame_err:
	//	memset(mb_buf, mb_cnt, 0);
		mb_cnt = 0;
	}
}
  • main.c
while(1)
{
    modbus_task();
    if (write_flash_flag)
    {
        write_flash_flag = 0//TODO: 写flash
    }
}

你可能感兴趣的:(单片机,串口通信,c语言)