51单片机MODBUS RTU协议从机

STC89C52RC单片机的MODBUS RTU协议从机的实现

1.波特率9600

2.校验位even

3.数据位8

4.测试的从机设备地址设为0x1,测试的寄存器地址为16,也就是图上的20(8进制)。

现象:

20(8进制)寄存器写入45(16进制),再读20(8进制)寄存器,响应为01 03 02 00 45 79 B7,返回值正确。

源码

在这里插入代码片main.c

/***************************************************************/

uint32     dwTickCount,dwIntTick;       //时钟

uint8       idata sendBuf[16],receBuf[16]; //发送接收缓冲区

uint8       idata checkoutError;     // ==2 偶校验错

uint8       idata receTimeOut;              //接收超时

uint8       idata c10ms;                //10ms 计时

bit          b1ms,bt1ms,b10ms,bt10ms,b100ms,bt100ms;   //定时标志位

// 串行中断程序

void commIntProc() interrupt 4

{

if(TI)

{

TI = 0;

if(sendPosi < sendCount)

{

sendPosi++;

ACC = sendBuf[sendPosi];

TB8 = P; //加上校验位

SBUF = sendBuf[sendPosi];

}

else

{

b485Send = 0;    //发送完后将485置于接收状态

receCount = 0;   //清接收地址偏移寄存器

checkoutError = 0;

}

}

else if(RI)

{

RI = 0;

receTimeOut = 10;    //通讯超时值

receBuf[receCount] = SBUF;

ACC = receBuf[receCount];

if(P != RB8)

checkoutError = 2; //偶校验出错

receCount++;          //接收地址偏移寄存器加1

receCount &= 0x0f;    //最多一次只能接收16个字节

}

}   // void CommIntProc()

//定时器0 1ms 中断

void timer0IntProc() interrupt 1

{

TL0 = TIMER_LOW;

TH0 = TIMER_HIGHT;

dwIntTick++;

bt1ms = 1;

c10ms++;

if(c10ms >= 10)

{

c10ms = 0;      //10ms计时器清零

bt10ms = 1;

}

}   // void Timer0IntProc()

//外部中断0

void intEx0Proc(void) interrupt 0

{

}

//计数器1中断

void counter1IntProc(void) interrupt 3 using 1

{

}

//定时处理

void timeProc(void)

{

static uint8 c200ms;

bWatchDog = ~ bWatchDog;    //看门狗取反

b1ms = 0;

b10ms = 0;

b100ms = 0;

ET0 = 0;

dwTickCount = dwIntTick;

ET0 = 1;

if(bt1ms)

{

bt1ms = 0;

b1ms = 1;

if(receTimeOut>0)

{

receTimeOut–;

if(receTimeOut==0 && receCount>0)   //判断通讯接收是否超时

{

b485Send = 0;       //将485置为接收状态

receCount = 0;      //将接收地址偏移寄存器清零

checkoutError = 0;

}

}

}

if(bt100ms)

{

bt100ms = 0;

b100ms = 1;

}

if(bt10ms)      //判断中断10ms标志位是否1

{

bt10ms = 0;     //清中断10ms标志位

b10ms = 1;

c200ms++;                   //200ms计时器加1

if(c200ms >= 20)            //判断是否计时到200ms

{

c200ms = 0;             //清200ms计时器

//bRunLED = ~bRunLED;     //取反运行指示灯

}

}

}   // void TimerProc(void)

//初始化串口

void initUart(void)

{

//T2 用于波特率 9600

T2CON = 0x30;

RCAP2H = 0xff;

RCAP2L = 0xdc;

TR2 = 1;

//偶校验

SCON = 0xd0;

PCON = 0;

ES = 1;

}//void initUart(void)

//初始化中断

void initInt(void)

{

TMOD = 0x51;

TH0 = TIMER_HIGHT;

TL0 = TIMER_LOW;

TR0 = 1;

ET0 = 1;

TH1 = 0;               //9600

TL1 = 0;

TR1 = 0;                //定时器1用于计数定时器2用于波特

ET1 = 1;

//PT1 = 1;

IT0 = 1;

IT1 = 1;

EX0 = 0;

PX0 = 1;

EX1 = 0;

initUart();

EA = 1;

}   // void initInt(void)

//初始化

void initProg(void)

{

initInt();

b485Send = 0;

}

void main(void)

{

initProg();

while(1)

{

timeProc();

checkComm0Modbus();

}

}

/******************************************************/

在这里插入代码片modbus.c

#include “main.h”

//macros

#define ccts(c1, c2)    ((((c1) << 8) & 0xFF00) + ((c2) & 0x00FF))

//字地址 0 – 255 (只取低8位)

//位地址 0 – 255 (只取低8位)

/* CRC 高位字节值表 */

const uint8 code auchCRCHi[] = {

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,

0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,

0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,

0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,

0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,

0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,

0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,

0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,

0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,

0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40

} ;

/* CRC低位字节值表*/

const uint8 code auchCRCLo[] = {

0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,

0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,

0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,

0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,

0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,

0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,

0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,

0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,

0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,

0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,

0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,

0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,

0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,

0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,

0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,

0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,

0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,

0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,

0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,

0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,

0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,

0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,

0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,

0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,

0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,

0x43, 0x83, 0x41, 0x81, 0x80, 0x40

} ;

uint8   testCoil;           //用于测试 位地址1

uint16  testRegister;    //用于测试 字址址16

uint8       localAddr = 1;       //单片机控制板的地址

uint8       sendCount;           //发送字节个数

uint8       receCount;         //接收到的字节个数

uint8       sendPosi;           //发送位置

uint16 crc16(uint8 *puchMsg, uint16 usDataLen)

{

uint8 uchCRCHi = 0xFF ; /* 高CRC字节初始化 */

uint8 uchCRCLo = 0xFF ; /* 低CRC 字节初始化 */

uint32 uIndex ; /* CRC循环中的索引 */

while (usDataLen–) /* 传输消息缓冲区 */

{

uIndex = uchCRCHi ^ *puchMsg++ ; /* 计算CRC */

uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;

uchCRCLo = auchCRCLo[uIndex] ;

}

return (uchCRCHi << 8 | uchCRCLo) ;

}//uint16 crc16(uint8 *puchMsg, uint16 usDataLen)

//开始发送

void beginSend(void)

{

b485Send = 1;      //设为发送

sendPosi = 0;

if(sendCount > 1)

sendCount–;

ACC = sendBuf[0];

TB8 = P;

SBUF = sendBuf[0];

}//void beginSend(void)

//读线圈状态

void readCoil(void)

{

uint8 addr;

uint8 tempAddr;

uint8 byteCount;

uint8 bitCount;

uint16 crcData;

uint8 position;

uint8 i,k;

uint8  result;

uint16 tempData;

uint8  exit = 0;

//addr = (receBuf[2]<<8) + receBuf[3];

//tempAddr = addr & 0xfff;

addr = receBuf[3];

tempAddr = addr;

//bitCount = (receBuf[4]<<8) + receBuf[5];      //读取的位个数

bitCount = receBuf[5];

byteCount = bitCount / 8;                               //字节个数

if(bitCount%8 != 0)

byteCount++;

for(k=0;k<byteCount;k++)

{//字节位置

position = k + 3;

sendBuf[position] = 0;

for(i=0;i<8;i++)

{

getCoilVal(tempAddr,&tempData);

sendBuf[position] |= tempData << i;

tempAddr++;

if(tempAddr >= addr+bitCount)

{      //读完

exit = 1;

break;

}

}

if(exit == 1)

break;

}

sendBuf[0] = localAddr;

sendBuf[1] = 0x01;

sendBuf[2] = byteCount;

byteCount += 3;

crcData = crc16(sendBuf,byteCount);

sendBuf[byteCount] = crcData >> 8;

byteCount++;

sendBuf[byteCount] = crcData & 0xff;

sendCount = byteCount + 1;

beginSend();

}//void readCoil(void)

//读寄存器

void readRegisters(void)

{

uint8 addr;

uint8 tempAddr;

uint16 result;

uint16 crcData;

uint8 readCount;

uint8 byteCount;

uint8  finsh;  //1完成  0出错

uint16 i;

uint16 tempData = 0;

//addr = (receBuf[2]<<8) + receBuf[3];

//tempAddr = addr & 0xfff;

addr = receBuf[3];

tempAddr = addr;

//readCount = (receBuf[4]<<8) + receBuf[5];    //要读的个数

readCount = receBuf[5];

byteCount = readCount * 2;

for(i=0;i<byteCount;i+=2,tempAddr++)

{

getRegisterVal(tempAddr,&tempData);

sendBuf[i+3] = tempData >> 8;

sendBuf[i+4] = tempData & 0xff;

}

sendBuf[0] = localAddr;

sendBuf[1] = 3;

sendBuf[2] = byteCount;

byteCount += 3;

crcData = crc16(sendBuf,byteCount);

sendBuf[byteCount] = crcData >> 8;

byteCount++;

sendBuf[byteCount] = crcData & 0xff;

sendCount = byteCount + 1;

beginSend();

}//void readRegisters(void)

//强制单个线圈

void forceSingleCoil(void)

{

uint8 addr;

uint8 tempAddr;

uint16 tempData;

uint8  onOff;

uint8 i;

//addr = (receBuf[2]<<8) + receBuf[3];

//tempAddr = addr & 0xfff;

addr = receBuf[3];

tempAddr = addr;

//onOff = (receBuf[4]<<8) + receBuf[5];

onOff = receBuf[4];

//if(onOff == 0xff00)

if(onOff == 0xff)

{      //设为ON

tempData = 1;

}

//else if(onOff == 0x0000)

else if(onOff == 0x00)

{      //设为OFF

tempData = 0;

}

setCoilVal(tempAddr,tempData);

for(i=0;i<receCount;i++)

{

sendBuf[i] = receBuf[i];

}

sendCount = receCount;

beginSend();

}//void forceSingleCoil(void)

//2021年5月23日20:35:26

//

//邱盼

//设置单个寄存器

void presetSingleRegisters(void)

{

uint8 addr;

uint8 tempAddr;

uint8 byteCount;

uint8 setCount;

uint16 crcData;

uint16 tempData;

uint8  finsh;  //为1时完成 为0时出错

uint8 i;

addr = receBuf[3];

tempAddr = addr;

tempData = receBuf[5];

setRegisterVal(tempAddr,tempData);

sendBuf[0] = localAddr;

sendBuf[1] = 6;

sendBuf[2] = addr >> 8;

sendBuf[3] = addr & 0xff;

sendBuf[4] = receBuf[4];

sendBuf[5] = receBuf[5];

crcData = crc16(sendBuf,6);

sendBuf[6] = crcData >> 8;

sendBuf[7] = crcData & 0xff;

sendCount = 8;

beginSend();

}

//设置多个寄存器

void presetMultipleRegisters(void)

{

uint8 addr;

uint8 tempAddr;

uint8 byteCount;

uint8 setCount;

uint16 crcData;

uint16 tempData;

uint8  finsh;  //为1时完成 为0时出错

uint8 i;

//addr = (receBuf[2]<<8) + receBuf[3];

//tempAddr = addr & 0xfff;

addr = receBuf[3];

tempAddr = addr & 0xff;

//setCount = (receBuf[4]<<8) + receBuf[5];

setCount = receBuf[5];

byteCount = receBuf[6];

for(i=0;i<setCount;i++,tempAddr++)

{

tempData = (receBuf[i*2+7]<<8) + receBuf[i*2+8];

setRegisterVal(tempAddr,tempData);

}

sendBuf[0] = localAddr;

sendBuf[1] = 16;

sendBuf[2] = addr >> 8;

sendBuf[3] = addr & 0xff;

sendBuf[4] = setCount >> 8;

sendBuf[5] = setCount & 0xff;

crcData = crc16(sendBuf,6);

sendBuf[6] = crcData >> 8;

sendBuf[7] = crcData & 0xff;

sendCount = 8;

beginSend();

}//void presetMultipleRegisters(void)

//检查uart0数据

void checkComm0Modbus(void)

{

uint16 crcData;

uint16 tempData;

if(receCount > 4)

{

switch(receBuf[1])

{

case 1://读取线圈状态(读取点 16位以内)

case 3://读取保持寄存器(一个或多个)

case 5://强制单个线圈

case 6://设置单个寄存器

if(receCount >= 8)

{//接收完成一组数据

//应该关闭接收中断

if(receBuf[0]==localAddr && checkoutError==0)

{

crcData = crc16(receBuf,6);

if(crcData == receBuf[7]+(receBuf[6]<<8))

{//校验正确

if(receBuf[1] == 1)

{//读取线圈状态(读取点 16位以内)

readCoil();

}

else if(receBuf[1] == 3)

{//读取保持寄存器(一个或多个)

readRegisters();

}

else if(receBuf[1] == 5)

{//强制单个线圈

forceSingleCoil();

}

else if(receBuf[1] == 6)

{

presetSingleRegisters();

}

}

}

receCount = 0;

checkoutError = 0;

}

break;

case 15://设置多个线圈

tempData = receBuf[6];

tempData += 9;    //数据个数

if(receCount >= tempData)

{

if(receBuf[0]==localAddr && checkoutError==0)

{

crcData = crc16(receBuf,tempData-2);

if(crcData == (receBuf[tempData-2]<<8)+ receBuf[tempData-1])

{

//forceMultipleCoils();

}

}

receCount = 0;

checkoutError = 0;

}

break;

case 16://设置多个寄存器

tempData = (receBuf[4]<<8) + receBuf[5];

tempData = tempData * 2;  //数据个数

tempData += 9;

if(receCount >= tempData)

{

if(receBuf[0]==localAddr && checkoutError==0)

{

crcData = crc16(receBuf,tempData-2);

if(crcData == (receBuf[tempData-2]<<8)+ receBuf[tempData-1])

{

presetMultipleRegisters();

}

}

receCount = 0;

checkoutError = 0;

}

break;

default:

break;

}

}

}//void checkComm0(void)

//取线圈状态 返回0表示成功

uint16 getCoilVal(uint16 addr,uint16 *tempData)

{

uint16 result = 0;

uint16 tempAddr;

tempAddr = addr & 0xfff;

//只取低8位地址

switch(tempAddr & 0xff)

{

case 0:

break;

case 1:

*tempData = testCoil;

break;

case 2:

break;

case 3:

break;

case 4:

break;

case 5:

break;

case 6:

break;

case 7:

break;

case 8:

break;

case 9:

break;

case 10:

break;

case 11:

break;

case 12:

break;

case 13:

break;

case 14:

break;

case 15:

break;

case 16:

break;

default:

break;

}

return result;

}//uint16 getCoilVal(uint16 addr,uint16 *data)

//设定线圈状态 返回0表示成功

uint16 setCoilVal(uint16 addr,uint16 tempData)

{

uint16 result = 0;

uint16 tempAddr;

tempAddr = addr & 0xfff;

switch(tempAddr & 0xff)

{

case 0:

break;

case 1:

testCoil = tempData;

break;

case 2:

break;

case 3:

break;

case 4:

break;

case 5:

break;

case 6:

break;

case 7:

break;

case 8:

break;

case 9:

break;

case 10:

break;

case 11:

break;

case 12:

break;

case 13:

break;

case 14:

break;

case 15:

break;

case 16:

break;

default:

break;

}

return result;

}//uint16 setCoilVal(uint16 addr,uint16 data)

//取寄存器值 返回0表示成功

uint16 getRegisterVal(uint16 addr,uint16 *tempData)

{

uint16 result = 0;

uint16 tempAddr;

tempAddr = addr & 0xfff;

switch(tempAddr & 0xff)

{

case 0:

break;

case 1:

break;

case 2:

break;

case 3:

break;

case 4:

break;

case 5:

break;

case 6:

break;

case 7:

break;

case 8:

break;

case 9:

break;

case 10:

break;

case 11:

break;

case 12:

break;

case 13:

break;

case 14:

break;

case 15:

break;

case 16:

*tempData = testRegister;

break;

default:

break;

}

return result;

}//uint16 getRegisterVal(uint16 addr,uint16 &data)

//设置寄存器值 返回0表示成功

uint16 setRegisterVal(uint16 addr,uint16 tempData)

{

uint16 result = 0;

uint16 tempAddr;

tempAddr = addr & 0xfff;

switch(tempAddr & 0xff)

{

case 0:

break;

case 1:

break;

case 2:

break;

case 3:

break;

case 4:

break;

case 5:

break;

case 6:

break;

case 7:

break;

case 8:

break;

case 9:

break;

case 10:

break;

case 11:

break;

case 12:

break;

case 13:

break;

case 14:

break;

case 15:

break;

case 16:

testRegister = tempData;

break;

default:

break;

}

return result;

}//uint8 setRegisterVal(uint16 addr,uint16 data)

备注:实现01、05、03、06的功能码。03,06通过验证。