#define SLAVE_ID 0x01
#define ERR_FUNC 0x01//
#define ERR_ADDR 0x02//
#define ERR_VALUE 0x03//
#define ERR_FAULT 0x04//
#define ERR_CONFIRM 0x05//
#pragma pack(1)
typedef struct
{
uint8_t slave_id;
uint8_t cmd;
uint8_t data[0];
}modbus_frame_t;
typedef struct
{
uint8_t reg_addr[2];
uint8_t value[2];
}modbus_body_t;
#define MODBUS_FRAME_HEAD 2
#pragma pack()
//modbus crc16校验
uint16_t mobus_crc16(const uint8_t *CRC_Buf,uint8_t CRC_Leni)
{
uint8_t i,j;
uint16_t CRC_Sumx;
CRC_Sumx=0xffff;
for(i=0;i<CRC_Leni;i++)
{
CRC_Sumx^=*(CRC_Buf+i);
for(j=0;j<8;j++)
{
if(CRC_Sumx&0x01)
{
CRC_Sumx>>=1;
CRC_Sumx^=0xA001;
}
else
{
CRC_Sumx>>=1;
}
}
}
return (CRC_Sumx);
}
modbus_frame_t* get_modbus_frame(const uint8_t * in, uint32_t len)
{
int i = 0,dlen =0;
uint16_t crc_rec= 0;
start_lbl:
while (i < len)
{
if ((SLAVE_ID == in[i])||(0x00 == in[i]))
break;
i++;
}
if (len - i < 8)//小于基本长度
return NULL;
modbus_frame_t * pframe = ( modbus_frame_t *) &in[i];
switch(pframe->cmd)
{
case 0x03:
case 0x04:
case 0x06:
dlen = sizeof(modbus_body_t);
break;
case 0x10:
uint16_t value = pframe->data[2]<<8 | pframe->data[3];
if(value != pframe->data[4] >> 1)
return NULL;
dlen = pframe->data[4] + 1 + sizeof(modbus_body_t);
break;
case 0x15:
dlen = pframe->data[0] + 1;
break;
default:
return NULL;
}
if (i + MODBUS_FRAME_HEAD + dlen + 2 > len)
{
i++;
goto start_lbl;
}
crc_rec = mobus_crc16((uint8_t *)pframe,MODBUS_FRAME_HEAD+dlen);
if ((pframe->data[dlen] == (uint8_t)(crc_rec))||
(pframe->data[dlen+1]==((uint8_t)(crc_rec>>8))))
{
pframe = (modbus_frame_t *)&in[i];
return pframe;
}
i++;
goto start_lbl;
}
int code_ret03_body(uint16_t reg_addr,uint16_t value,uint8_t *data)
{
if(value == 0)
return -ERR_VALUE;
int err =0;
uint8_t len = 1;
data[0] = value * 2;
if(value >= 0xff)
return -ERR_VALUE;
memset(data+len ,0x00,value*2);
for(uint16_t i = 0 ; i < value; i++)
{
err = your_func(reg_addr + i,data+len);//放入自己的函数,返回错误码。
len +=2;
}
return err < 0 ? err : len;
}
int code_ret06_body(uint16_t reg_addr,uint16_t value, uint8_t *data)
{
int err = your_func(reg_addr,value);//放入自己的函数,返回错误码。
return err < 0 ? err : 4;
}
int code_ret10_body(uint16_t reg_addr,uint16_t value, uint8_t *data)
{
int err =0;
int len = 5;
if(value >= 0xff)
return -ERR_VALUE;
for(uint8_t i = 0 ; i < value; i++)
{
err = your_func(reg_addr+i,data+len);//放入自己的函数,返回错误码。
len += 2;
}
return err < 0 ? err : 4;
}
int code_modbus_ret_frame(modbus_frame_t *frame)
{
modbus_body_t *body = (modbus_body_t *)frame->data;
uint16_t reg_addr = body->reg_addr[0]<<8|body->reg_addr[1];//大端
uint16_t value = body->value[0] <<8 | body->value[1];//大端
switch(frame->cmd)
{
case 0x03:
case 0x04:
return code_ret03_body(reg_addr,value,frame->data);
case 0x06:
return code_ret06_body(reg_addr,value,frame->data);
case 0x10:
return code_ret10_body(reg_addr,value,frame->data);
default:
return 0;
}
}
int code_modbus_err_body(int err,modbus_frame_t *frame)
{
frame->cmd |=0x80;
frame->data[0] = _abs(err);
return 1;
}
uint32_t modbus_frame_handle(modbus_frame_t *frame)
{
int len = 0;
len = code_modbus_ret_frame(frame);
if(len <= 0)
{
len = code_modbus_err_body(len,frame);
}
if(frame->slave_id == 0x00)
{
return 0;
}
uint16_t crc = mobus_crc16((uint8_t *)frame,len+MODBUS_FRAME_HEAD);
frame->data[len] = (uint8_t)crc;//大端
frame->data[len + 1] = (uint8_t)(crc_rec>>8);//大端
return len+4;
}
接收的数组和回复的数组是使用的同一个,注意防止内存溢出,所以需要一个大数组进行接收。
为什么不直接校验CRC判断等于0 ?因为这样操作可以对付长数组断断续续接收的问题,按需更改校验方式。