Arduino-Modbus-RTU-从站程序
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
//基本参数
#define baudrate 115200 //定义通讯波特率
#define slaveID 1 //定义modbus RTU从站站号
#define modbusDataSize 100 //定义modbus数据库空间大小,可根据实际情况自行修改大小
unsigned int modbusData[modbusDataSize]={}; //建立modbus数据库
//系统参数
#define bufferSize 255 //一帧数据的最大字节数量
unsigned char frame[bufferSize]; //用于保存接收或发送的数据
HardwareSerial* ModbusPort;
//函数声明
unsigned int calculateCRC(unsigned char* _regs,unsigned char arraySize); //声明CRC校验函数
void modbusRTU_slave(); //声明modbus RTU从站函数
void responseError(unsigned char ID,unsigned char function,unsigned char wrongNumber); //声明错误信息返回函数
void modbusRTU_INI(HardwareSerial *SerialPort); //声明modbus RTU端口初始化函数
//初始化函数
void setup()
{
delay(100);
modbusRTU_INI(&Serial); //定义modbus通讯端口端口0:&Serial 端口1:&Serial1 端口2:&Serial2
}
//主循环
void loop()
{
modbusRTU_slave(); //执行modbus函数
}
//modbus RTU端口初始化函数
//参数:端口号
void modbusRTU_INI(HardwareSerial *SerialPort)
{
ModbusPort = SerialPort;
(*ModbusPort).begin(baudrate);
(*ModbusPort).flush();
}
//modbus RTU从站函数
//支持功能码03,06,16
void modbusRTU_slave()
{
unsigned int characterTime; //字符时间
unsigned char errorFlag=0; //错误标志
unsigned int crc16; //校验位
unsigned char address=0;
if (baudrate > 19200) //波特率大于19200时进入条件
{
characterTime = 750;
}
else
{
characterTime = 15000000/baudrate; //1.5字符时间
}
while((*ModbusPort).available()>0) //如果串口缓冲区数据量大于0进入条件
{
if(address { frame[address]=(*ModbusPort).read(); address++; } else //条件不满足时直接清空缓冲区 { (*ModbusPort).read(); } delayMicroseconds(characterTime); //等待1.5个字符时间 if((*ModbusPort).available()==0) //1.5个字符时间后缓冲区仍然没有收到数据,认为一帧数据已经接收完成,进入条件 { unsigned char function=frame[1]; //读取功能码 if(frame[0]==slaveID||frame[0]==0) //站号匹配或者消息为广播形式,进入条件 { crc16 = ((frame[address - 2] << 8) | frame[address - 1]); if(calculateCRC(&frame[0],address - 2)==crc16) //数据校验通过,进入条件 { if (frame[0]!=0 && (function == 3)) //功能码03不支持广播消息 { unsigned int startData=((frame[2] << 8) | frame[3]); //读取modbus数据库起始地址 unsigned int dataSize=((frame[4] << 8) | frame[5]); //需要读取的modbus数据库数据长度 unsigned int endData=startData+dataSize; //需要读取的modbus数据库数据的结束地址 unsigned char responseSize=5+dataSize*2; //计算应答的数据长度 unsigned int temp1,temp2,temp3; if(dataSize>125 || endData>=modbusDataSize) //读取数据的结束地址超过了modbus数据库的范围或单次读取的数据数量大于125 { errorFlag=0x02; //数据超过范围 responseError(slaveID,function,errorFlag); //返回错误消息 } else { frame[0]=slaveID; //设定站号 frame[1]=function; //设定功能码 frame[2]=dataSize*2; //设定数据长度 temp3=3; for(temp1=startData;temp1 { temp2=modbusData[temp1]; //取出modbus数据库中的数据 frame[temp3]=temp2>>8; temp3++; frame[temp3]=temp2 & 0xFF; temp3++; } crc16 = calculateCRC(&frame[0],responseSize-2); frame[responseSize-2] = crc16 >> 8; //填写校验位 frame[responseSize-1] = crc16 & 0xFF; (*ModbusPort).write(&frame[0],responseSize); //返回功能码03的消息 } } else if(function == 6) //功能码为06时进入条件 { unsigned int startData=((frame[2] << 8) | frame[3]); //写入modbus数据库的地址 unsigned int setData=((frame[4] << 8) | frame[5]); //写入modbus数据库的数值 if(startData>=modbusDataSize) { errorFlag=0x02; //数据超过范围 responseError(slaveID,function,errorFlag); //返回错误消息 } else { modbusData[startData]=setData; //写入数据到modbus数据库 frame[0]=slaveID; //设定站号