一、简介及进展
modbus的从机就看了不少的文章了,不过主机的文章比较少,在这里我贡献出我的项目经验,自己做的第一个项目。还是需要继续学习。
首先定义一个发送的数组,data[7],data[8]是crc校验码,这个时候有人会有疑问了,为什么不做发送时间的判断,因为发送时间不能超过3.5ms。一旦超过3.5ms的话,modbus主机是不会处理这段数据的。我在这是按实测的来,仿真和实际操作都没问题
首先,看发送和接收的数据格式
主机发送码::地址 03 00 00 00 02 CRC低位 CRC高位(共8字节)
(1) 地址(1字节):与仪器设置地址相同 1~247
(2) 功能码(1字节):03 使用03功能码读数据
(3) 数据寄存器地址(2字节):0000
(4) 数据数量(2字节):0002 读2个16位数据
(5) CRC(2字节):校验码
仪器发送码:地址 03 04 XX XX XX XX CRC低位 CRC高位(共9字节)
(1) 地址(1字节):仪器设置地址1~247
(2) 功能码(1字节):03 使用03功能码读数据
(3) 数量(1字节):4 发送数据字节数
(4) 数据(4字节):32位标准IEEE754浮点数
(5) CRC(2字节):校验码
例:EXAMPLE
逻辑地址为1的表
读电量:10度
发送
01 03 00 00 00 02 c4 0b
接收
01 03 04 00 00 03 e8 crc16
c4 0b就是crc校验位,可以使用CRCTool工具进行计算
下面具体介绍数据的发送和接收,
定义发送的数组
modbus.databuf[0]=0x01;
modbus.databuf[1]=0x03;
modbus.databuf[2]=0x00;
modbus.databuf[3]=0x00;
modbus.databuf[4]=0x00;
modbus.databuf[5]=0x02;
modbus.databuf[6]=0xc4;
modbus.databuf[7]=0x0b;
发送16位数据,这里要用寄存器来操作,不能使用printf来发送16进制的数组。
void UartASendStr (u8 *pucStr, u8 ulNum)
{
u8 i;
for(i = 0;i<ulNum;i++)
{
while ((USART2->SR & USART_FLAG_TC) == (uint16_t)RESET);
USART_SendData(USART2,*pucStr++);
}
}
发送数据,放在了main函数里,目前是调试用。
加了延时,
UartASendStr(modbus.databuf,8);
delay_ms(200);
接收中断
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)
{
modbus.rcbuf[modbus.recount++] = USART_ReceiveData(USART2);
}
}
接收到的数据进行处理
在这里调用了crc校验的算法,可以参考从机的ModbusCRC.c,我是直接用的。
#include "modbusCRC.h"
void Modbud_fun3()
{
u16 Regadd;
u16 Reglen;
u16 i=0;
u16 crc;
u16 rccrc;
Reglen=modbus.rcbuf[2];
if(Reglen==0x04)
{
Regadd=modbus.rcbuf[3]*256+modbus.rcbuf[4];
Regadd=modbus.rcbuf[5]*256+modbus.rcbuf[6];
}
if(Reglen==0x02)
{
Regadd=modbus.rcbuf[3]*256+modbus.rcbuf[4];
Regadd=modbus.rcbuf[5]*256+modbus.rcbuf[6];
}
crc= crc16(&modbus.rcbuf[0], modbus.recount-2);
rccrc=modbus.rcbuf[modbus.recount-2]*256 + modbus.rcbuf[modbus.recount-1];
if(crc == rccrc)
{
i++;//如果校验的crc和发送的数据相同,证明了接收的数据正常,可以使用
}
modbus.recount=0;
modbus.reflag=0;
}
目前已经调通。下面将所有的代码放了上来,供学习用。
主要功能
- 串口一是接GPRS模块的,代码还没有添加进来,已调通,能进中断。
- 串口二接485转ttl的,还不能用于产品用。
- modbusCRC.c是直接使用了从机的代码,可以参考网上的例程。
- dds238-1电表是可以直接使用本代码的。
main函数
#include "bsp_usart.h"
#include "modbus.h"
#include "stm32f10x.h"
int main(void)
{
USART1_Config();
USART2_Config();
shuzu();
//Modbud_fun3();
while(1)
{
// RS485_byte();
UartASendStr(modbus.databuf,8);
delay_ms(200);
Modbud_fun3();
}
}
modbus.c
#include "modbus.h"
#include "modbusCRC.h"
#include "bsp_usart.h"
MODBUS modbus;
void UartASendStr (u8 *pucStr, u8 ulNum)
{
u8 i;
for(i = 0;i<ulNum;i++)
{
while ((USART2->SR & USART_FLAG_TC) == (uint16_t)RESET);
USART_SendData(USART2,*pucStr++);
}
}
void Modbud_fun3()
{
u16 Regadd;
u16 Reglen;
u16 i=0;
u1