找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1930|回复: 4
收起左侧

51单片机485通讯 MODBUS-RTU程序源码

  [复制链接]
ID:280979 发表于 2023-8-30 22:32 | 显示全部楼层 |阅读模式
GYJ-0025 某一个宝描述451.png

GYJ-0025 某一个宝描述460.png

GYJ-0025 某一个宝描述468.png

//本示例在Keil开发环境下请选择Intel的8058芯片型号进行编译
//若无特别说明,工作频率一般为11.0592MHz

#include "STC89C52RC.h"
#include "intrins.h"
#define uchar unsigned char//宏定义无符号字符型
#define uint unsigned int  //宏定义无符号整型

typedef unsigned char BYTE;
typedef unsigned int WORD;

#define FOSC 11059200L          //系统频率
#define BAUD 9600             //串口波特率

#define NONE_PARITY     0       //无校验
#define ODD_PARITY      1       //奇校验
#define EVEN_PARITY     2       //偶校验
#define MARK_PARITY     3       //标记校验
#define SPACE_PARITY    4       //空白校验

#define PARITYBIT NONE_PARITY   //定义校验位

/*Declare SFR associated with the IAP */
sfr IAP_DATA    =   0xE2;           //Flash data register
sfr IAP_ADDRH   =   0xE3;           //Flash address HIGH
sfr IAP_ADDRL   =   0xE4;           //Flash address LOW
sfr IAP_CMD     =   0xE5;           //Flash command register
sfr IAP_TRIG    =   0xE6;           //Flash command trigger
sfr IAP_CONTR   =   0xE7;           //Flash control register

/*Define ISP/IAP/EEPROM command*/
#define CMD_IDLE    0               //Stand-By
#define CMD_READ    1               //Byte-Read
#define CMD_PROGRAM 2               //Byte-Program
#define CMD_ERASE   3               //Sector-Erase

/*Define ISP/IAP/EEPROM operation const for IAP_CONTR*/
//#define ENABLE_IAP 0x80           //if SYSCLK<40MHz
#define ENABLE_IAP   0x81           //if SYSCLK<20MHz
//#define ENABLE_IAP x82            //if SYSCLK<10MHz
//#define ENABLE_IAP 0x83           //if SYSCLK<5MHz

//Start address for STC89C58xx EEPROM
#define IAP_ADDRESS 0x02000

sbit  IN1= P2^0;
sbit  IN2= P2^1;
sbit  IN3= P2^2;
sbit  IN4= P2^3;

sbit out1= P1^0;
sbit out2= P1^1;
sbit out3= P1^2;
sbit out4= P1^3;


bit flagTxd=0,flagFrame=0,flag=0;
unsigned char cntRxd = 0;   //接收字节计数器
unsigned char xdata bufRxd[10];
unsigned char xdata qjc[22];
bit k1=0,kt=0;
unsigned char addr=0,addr1=0;
void UartWrite(unsigned char *buf, unsigned char len);

unsigned int GetCRC16(unsigned char *ptr,  unsigned char len);
unsigned int  crc;
unsigned char crch, crcl;

bit bz1=0,bz2=0,bz3=0,bz4=0,ks=0,bk1=0,bzw=0;
uchar input=0;
uchar xx_1=0x00,xx_2=0x00,xx_3=0x00,xx_4=0x00;
uchar y_1=0x00,y_2=0x00,y_3=0x00,y_4=0x00;
uchar dat1=0x00,dat2=0x00,yy_1=0,zt1=0x00;
uchar dat4=0,dat5=0,dat6=0,dat7=0,dat8=0,dat9=0;
uint js=0;
/********************************************************************
                                        E2P函数
*********************************************************************/
void IapIdle();
BYTE IapReadByte(WORD addr);
void IapProgramByte(WORD addr, BYTE dat);
void IapEraseSector(WORD addr);
/*******************************************************************
*                        配置串口
********************************************************************/
void ConfigUART(unsigned int baud)
{
#if (PARITYBIT == NONE_PARITY)
SCON = 0x50;                //8位可变波特率
#elif (PARITYBIT == ODD_PARITY) || (PARITYBIT == EVEN_PARITY) || (PARITYBIT == MARK_PARITY)
SCON = 0xda;                //9位可变波特率,校验位初始为1
#elif (PARITYBIT == SPACE_PARITY)
SCON = 0xd2;                //9位可变波特率,校验位初始为0
#endif

TMOD &= 0x0F;  //清零T1的控制位
TMOD |= 0x20;  //配置T1为模式2
TH1 = 256 - (11059200/12/32)/baud;  //计算T1重载值
TL1 = TH1;     //初值等于重载值
ET1 = 0;       //禁止T1中断
ES  = 1;       //使能串口中断
TR1 = 1;       //启动T1
//    SCON = 0x50;                //8位可变波特率
//    T2L = (65536 - (FOSC/4/BAUD));   //设置波特率重装值
//    T2H = (65536 - (FOSC/4/BAUD))>>8;
//    AUXR = 0x14;                //T2为1T模式, 并启动定时器2
//    AUXR |= 0x01;               //选择定时器2为串口1的波特率发生器
}
/*******************************************************************
*                        配置定时器
********************************************************************/
void Timer0Init(void)                //1毫秒@11.0592MHz
{
AUXR |= 0x80;                //定时器时钟1T模式
TMOD &= 0xF0;                //设置定时器模式
TL0 = 0xCD;                //设置定时初值
TH0 = 0xD4;                //设置定时初值
TF0 = 0;                //清除TF0标志
TR0 = 1;                //定时器0开始计时
ET0 = 1;        //使能T0中断
TR0 = 1;        //启动T0
}

/*******************************************************************
*                        串口检测
********************************************************************/
void UartRxMonitor(unsigned char ms){//串口接收监控,由空闲时间判定帧结束,需在定时中断中调用,ms-定时间隔
static unsigned char cntbkp = 0;
static unsigned char idletmr = 0;
if (cntRxd > 0){  //接收计数器大于零时,监控总线空闲时间
if (cntbkp != cntRxd){  //接收计数器改变,即刚接收到数据时,清零空闲计时
cntbkp = cntRxd;
idletmr = 0;
}else{                   //接收计数器未改变,即总线空闲时,累积空闲时间
if (idletmr < 30){  //空闲计时小于30ms时,持续累加
idletmr += ms;
if (idletmr >= 30){  //空闲时间达到30ms时,即判定为一帧接收完毕
        flagFrame = 1;  //设置帧接收完成标志
}
}
}
}else{
cntbkp = 0;
}
}
/*******************************************************************
*                        串口接收函数
********************************************************************/
void UartDriver(){//串口1接收
unsigned char xdata buf[20];
unsigned char len=0,lenx=0,bz=0,i=0;
if(flagFrame==1)
{
flagFrame=0;   //定时器0检测接收的数据
len = cntRxd; //读取接收的字节数量
lenx = cntRxd;
cntRxd = 0;  //接收计数器清零
if(bufRxd[0]==addr)
{
crc = GetCRC16(bufRxd, len-2); //计算CRC校验值
crch = crc >> 8;
crcl = crc & 0xFF;
if ((bufRxd[len-2] == crch) && (bufRxd[len-1] == crcl)) //判断CRC校验是否正确
{
switch (bufRxd[1]) //按功能码执行操作
{                                                 
case 0x01://读取输出状态
if((bufRxd[2]==0x00)&&(bufRxd[4]==0x00)&&(bufRxd[5]==0x01)&&((len-2)==6))
{
switch(bufRxd[3])
{
case 0x00:if(out1==0){yy_1=0x01;}else{yy_1=0x00;}break;
case 0x01:if(out2==0){yy_1=0x01;}else{yy_1=0x00;}break;
case 0x02:if(out3==0){yy_1=0x01;}else{yy_1=0x00;}break;
case 0x03:if(out4==0){yy_1=0x01;}else{yy_1=0x00;}break;
default:break;                                                                 
}
buf[0] = addr;          //地址位
buf[1] = 0x01; //功能位
buf[2] = 0x01; //数据长度位
buf[3] = yy_1;//输出状态2进制表示有输入为1,没输入为0
len = 4;
crc = GetCRC16(buf, len); //计算CRC校验值
buf[len++] = crc >> 8;    //CRC高字节
buf[len++] = crc & 0xFF;  //CRC低字节
UartWrite(buf, len);//发送其他数据用串口1
}

if((bufRxd[2]==0x00)&&(bufRxd[4]==0x00)&&(bufRxd[5]==0x02)&&((len-2)==6))
{
switch(bufRxd[3])
{
case 0x00:if(out1==0){y_1=0x01;}else{y_1=0x00;}
                                if(out2==0){y_2=0x02;}else{y_2=0x00;}
                                yy_1=(y_1|y_2);
                                break;
case 0x01:if(out2==0){y_2=0x01;}else{y_2=0x00;}
                                if(out3==0){y_3=0x02;}else{y_3=0x00;}
                                yy_1=(y_2|y_3);
                                break;                                                                     
case 0x02:if(out3==0){y_3=0x01;}else{y_3=0x00;}
                                if(out4==0){y_4=0x02;}else{y_4=0x00;}
                                yy_1=(y_3|y_4);
                                break;
case 0x03:if(out4==0){y_4=0x01;}else{y_4=0x00;}
                                                                                                                                        yy_1=y_4;                                                               
                                break;
default:break;                                                                 
}
buf[0] = addr;          //地址位
buf[1] = 0x01; //功能位
buf[2] = 0x01; //数据长度位
buf[3] = yy_1;//输出状态2进制表示有输入为1,没输入为0
len = 4;
crc = GetCRC16(buf, len); //计算CRC校验值
buf[len++] = crc >> 8;    //CRC高字节
buf[len++] = crc & 0xFF;  //CRC低字节
UartWrite(buf, len);//发送其他数据用串口1
}

if((bufRxd[2]==0x00)&&(bufRxd[4]==0x00)&&(bufRxd[5]==0x03)&&((len-2)==6))
{
switch(bufRxd[3])
{
case 0x00:if(out1==0){y_1=0x01;}else{y_1=0x00;}
                                if(out2==0){y_2=0x02;}else{y_2=0x00;}
                                if(out3==0){y_3=0x04;}else{y_3=0x00;}
                                yy_1=(y_1|y_2|y_3);
                                break;
case 0x01:if(out2==0){y_2=0x01;}else{y_2=0x00;}
                                if(out3==0){y_3=0x02;}else{y_3=0x00;}
                                if(out4==0){y_4=0x04;}else{y_4=0x00;}
                                yy_1=(y_2|y_3|y_4);
                                break;                                                                     
default:break;                                                                 
}
buf[0] = addr;          //地址位
buf[1] = 0x01; //功能位
buf[2] = 0x01; //数据长度位
buf[3] = yy_1;//输出状态2进制表示有输入为1,没输入为0
len = 4;
crc = GetCRC16(buf, len); //计算CRC校验值
buf[len++] = crc >> 8;    //CRC高字节
buf[len++] = crc & 0xFF;  //CRC低字节
UartWrite(buf, len);//发送其他数据用串口1
}                          

if((bufRxd[2]==0x00)&&(bufRxd[3]==0x00)&&(bufRxd[4]==0x00)&&(bufRxd[5]==0x04)&&((len-2)==6))
{
if(out1==0){y_1=0x01;}else{y_1=0x00;}
if(out2==0){y_2=0x02;}else{y_2=0x00;}
if(out3==0){y_3=0x04;}else{y_3=0x00;}
if(out4==0){y_4=0x08;}else{y_4=0x00;}
dat1=(y_1|y_2|y_3|y_4);
buf[0] = addr;          //地址位
buf[1] = 0x01; //功能位
buf[2] = 0x01; //数据长度位
buf[3] = dat1;//输出状态2进制表示有输入为1,没输入为0                                                
len = 4;
crc = GetCRC16(buf, len); //计算CRC校验值
buf[len++] = crc >> 8;    //CRC高字节
buf[len++] = crc & 0xFF;  //CRC低字节
UartWrite(buf, len);//发送其他数据用串口1
}
break;


case 0x02://查询输入状态
if((bufRxd[2]==0x00)&&(bufRxd[4]==0x00)&&(bufRxd[5]==0x01)&&((len-2)==6))
{
switch(bufRxd[3])
{
case 0x00:if(IN1 == 0){xx_1 = 0x01;}else{xx_1 = 0x00;} dat1=xx_1;break;//查询输入状态打开为1,关闭为0
case 0x01:if(IN2 == 0){xx_2 = 0x01;}else{xx_2 = 0x00;} dat1=xx_2;break;
case 0x02:if(IN3 == 0){xx_3 = 0x01;}else{xx_3 = 0x00;} dat1=xx_3;break;
case 0x03:if(IN4 == 0){xx_4 = 0x01;}else{xx_4 = 0x00;} dat1=xx_4;break;
default:break;
}

buf[0] = addr;          //地址位
buf[1] = 0x02; //功能位
buf[2] = 0x01; //数据长度位
buf[3] = dat1; //输入状态2进制表示有输入为1,没输入为0
len = 4;
crc = GetCRC16(buf, len); //计算CRC校验值
buf[len++] = crc >> 8;    //CRC高字节
buf[len++] = crc & 0xFF;  //CRC低字节
UartWrite(buf, len);//发送其他数据用串口1
}

if((bufRxd[2]==0x00)&&(bufRxd[4]==0x00)&&(bufRxd[5]==0x02)&&((len-2)==6))
{
switch(bufRxd[3])
{
case 0x00:if(IN1 == 0){xx_1 = 0x01;}else{xx_1 = 0x00;} //查询输入状态打开为1,关闭为0
                                 if(IN2 == 0){xx_2 = 0x02;}else{xx_2 = 0x00;}
                                 dat1=(xx_1|xx_2);
                                 break;
case 0x01:if(IN2 == 0){xx_2 = 0x01;}else{xx_2 = 0x00;}
                                 if(IN3 == 0){xx_3 = 0x02;}else{xx_3 = 0x00;}                                                                                                         
                                 dat1=(xx_2|xx_3);
                                 break;
case 0x02:if(IN3 == 0){xx_3 = 0x01;}else{xx_3 = 0x00;}
                                 if(IN4 == 0){xx_4 = 0x02;}else{xx_4 = 0x00;}
                                 dat1=(xx_3|xx_4);
                                 break;
case 0x03:if(IN4 == 0){xx_4 = 0x01;}else{xx_4 = 0x00;} dat1=xx_4;break;
default:break;
}

buf[0] = addr;          //地址位
buf[1] = 0x02; //功能位
buf[2] = 0x01; //数据长度位
buf[3] = dat1; //输入状态2进制表示有输入为1,没输入为0
len = 4;
crc = GetCRC16(buf, len); //计算CRC校验值
buf[len++] = crc >> 8;    //CRC高字节
buf[len++] = crc & 0xFF;  //CRC低字节
UartWrite(buf, len);//发送其他数据用串口1
}

if((bufRxd[2]==0x00)&&(bufRxd[4]==0x00)&&(bufRxd[5]==0x03)&&((len-2)==6))
{
switch(bufRxd[3])
{
case 0x00:if(IN1 == 0){xx_1 = 0x01;}else{xx_1 = 0x00;} //查询输入状态打开为1,关闭为0
                                 if(IN2 == 0){xx_2 = 0x02;}else{xx_2 = 0x00;}
                                 if(IN3 == 0){xx_3 = 0x04;}else{xx_3 = 0x00;}
                                 dat1=(xx_1|xx_2|xx_3);
                                 break;
case 0x01:if(IN2 == 0){xx_2 = 0x01;}else{xx_2 = 0x00;}
                                 if(IN3 == 0){xx_3 = 0x02;}else{xx_3 = 0x00;}
                                 if(IN4 == 0){xx_4 = 0x04;}else{xx_4 = 0x00;}
                                 dat1=(xx_2|xx_3|xx_4);
                                 break;
default:break;
}

buf[0] = addr;          //地址位
buf[1] = 0x02; //功能位
buf[2] = 0x01; //数据长度位
buf[3] = dat1; //输入状态2进制表示有输入为1,没输入为0
len = 4;
crc = GetCRC16(buf, len); //计算CRC校验值
buf[len++] = crc >> 8;    //CRC高字节
buf[len++] = crc & 0xFF;  //CRC低字节
UartWrite(buf, len);//发送其他数据用串口1
}

if((bufRxd[2]==0x00)&&(bufRxd[3]==0x00)&&(bufRxd[4]==0x00)&&(bufRxd[5]==0x04)&&((len-2)==6))
{

if(IN1 == 0){xx_1 = 0x01;}else{xx_1 = 0x00;} //查询输入状态打开为1,关闭为0
if(IN2 == 0){xx_2 = 0x02;}else{xx_2 = 0x00;}
if(IN3 == 0){xx_3 = 0x04;}else{xx_3 = 0x00;}
if(IN4 == 0){xx_4 = 0x08;}else{xx_4 = 0x00;}
dat1=(xx_1|xx_2|xx_3|xx_4);

buf[0] = addr;          //地址位
buf[1] = 0x02; //功能位
buf[2] = 0x01; //数据长度位
buf[3] = dat1; //输入状态2进制表示有输入为1,没输入为0
len = 4;
crc = GetCRC16(buf, len); //计算CRC校验值
buf[len++] = crc >> 8;    //CRC高字节
buf[len++] = crc & 0xFF;  //CRC低字节
UartWrite(buf, len);//发送其他数据用串口1
}
break;

case 0x03://读取寄存器状态
if((bufRxd[2]==0x00)&&(bufRxd[4]==0x00)&&(bufRxd[5]==0x01)&&((len-2)==6))//读取2个字节
{
dat5=addr;
buf[0] = addr;          //地址位
buf[1] = 0x03; //功能位
buf[2] = 0x02; //数据长度位
buf[3] = dat4;//高
buf[4] = dat5;//低                                                               
len = 5;
crc = GetCRC16(buf, len); //计算CRC校验值
buf[len++] = crc >> 8;    //CRC高字节
buf[len++] = crc & 0xFF;  //CRC低字节
UartWrite(buf, len);//发送其他数据用串口1
}                                                       
break;

case 0x04://读取寄存器状态
if((bufRxd[2]==0x00)&&(bufRxd[4]==0x00)&&(bufRxd[5]==0x01))//读取2个字节
{
switch(bufRxd[3])
{
case 0x00: dat4=qjc[0];dat5=qjc[1];break;
case 0x01: dat4=qjc[2];dat5=qjc[3];break;
case 0x02: dat4=qjc[4];dat5=qjc[5];break;
case 0x03: dat4=qjc[6];dat5=qjc[7];break;
case 0x04: dat4=qjc[8];dat5=qjc[9];break;
case 0x05: dat4=qjc[10];dat5=qjc[11];break;
case 0x06: dat4=qjc[12];dat5=qjc[13];break;
case 0x07: dat4=qjc[14];dat5=qjc[15];break;
case 0x08: dat4=qjc[16];dat5=qjc[17];break;
case 0x09: dat4=qjc[18];dat5=qjc[19];break;
case 0x0A: dat4=qjc[20];dat5=qjc[21];break;
default:break;
}
buf[0] = addr;          //地址位
buf[1] = 0x04; //功能位
buf[2] = 0x02; //数据长度位
buf[3] = dat4;//高
buf[4] = dat5;//低                                                               
len = 5;
crc = GetCRC16(buf, len); //计算CRC校验值
        buf[len++] = crc >> 8;    //CRC高字节
        buf[len++] = crc & 0xFF;  //CRC低字节
        UartWrite(buf, len);//发送其他数据用串口1
}
if((bufRxd[2]==0x00)&&(bufRxd[4]==0x00)&&(bufRxd[5]==0x02))//读取4个字节
{
switch(bufRxd[3])
{
case 0x00: dat4=qjc[0];dat5=qjc[1];dat6=qjc[2];dat7=qjc[3];break;
case 0x01: dat4=qjc[2];dat5=qjc[3];dat6=qjc[4];dat7=qjc[5];break;
case 0x02: dat4=qjc[4];dat5=qjc[5];dat6=qjc[6];dat7=qjc[7];break;
case 0x03: dat4=qjc[6];dat5=qjc[7];dat6=qjc[8];dat7=qjc[9];break;
case 0x04: dat4=qjc[8];dat5=qjc[9];dat6=qjc[10];dat7=qjc[11];break;
case 0x05: dat4=qjc[10];dat5=qjc[11];dat6=qjc[12];dat7=qjc[13];break;
case 0x06: dat4=qjc[12];dat5=qjc[13];dat6=qjc[14];dat7=qjc[15];break;
case 0x07: dat4=qjc[14];dat5=qjc[15];dat6=qjc[16];dat7=qjc[17];break;
case 0x08: dat4=qjc[16];dat5=qjc[17];dat6=qjc[18];dat7=qjc[19];break;
case 0x09: dat4=qjc[18];dat5=qjc[19];dat6=qjc[20];dat7=qjc[21];break;
case 0x0A: dat4=qjc[20];dat5=qjc[21];dat6=0x00;dat7=qjc[22];break;
default:break;
}
buf[0] = addr;          //地址位
buf[1] = 0x04; //功能位
buf[2] = 0x04; //数据长度位
buf[3] = dat4;//高
buf[4] = dat5;//低                 
buf[5] = dat6;//高
buf[6] = dat7;//低                                                                       
len = 7;
crc = GetCRC16(buf, len); //计算CRC校验值
        buf[len++] = crc >> 8;    //CRC高字节
        buf[len++] = crc & 0xFF;  //CRC低字节
        UartWrite(buf, len);//发送其他数据用串口1
}
break;


case 0x05://设置单个输出状态
if((bufRxd[2]==0x00)&&(bufRxd[5]==0x00))//输出1
{
if((bufRxd[3]==0x00)&&(bufRxd[4]==0xFF)){out1=0;}//打开第一路输出
if((bufRxd[3]==0x00)&&(bufRxd[4]==0x00)){out1=1;}//关闭第一路输出
if((bufRxd[3]==0x01)&&(bufRxd[4]==0xFF)){out2=0;}//打开第二路输出
if((bufRxd[3]==0x01)&&(bufRxd[4]==0x00)){out2=1;}//关闭第二路输出
if((bufRxd[3]==0x02)&&(bufRxd[4]==0xFF)){out3=0;}//打开第二路输出
if((bufRxd[3]==0x02)&&(bufRxd[4]==0x00)){out3=1;}//关闭第二路输出
if((bufRxd[3]==0x03)&&(bufRxd[4]==0xFF)){out4=0;}//打开第二路输出
if((bufRxd[3]==0x03)&&(bufRxd[4]==0x00)){out4=1;}//关闭第二路输出                                                                   
                         buf[0] = addr;          //地址位
                         buf[1] = 0x05; //功能位
                         buf[2] = 0x00; //线圈地址高
                         buf[3] = bufRxd[3]; //线圈地址低
                         buf[4] = bufRxd[4]; //打开FF/关闭00
                         buf[5] = 0x00;
                         len = 6;
        crc = GetCRC16(buf, len); //计算CRC校验值
        buf[len++] = crc >> 8;    //CRC高字节
        buf[len++] = crc & 0xFF;  //CRC低字节
        UartWrite(buf, len);//发送其他数据用串口1
}
break;

case 0x06://写单个寄存器
if((bufRxd[2]==0x00)&&(bufRxd[3]==0x00)&&(bufRxd[4]==0x00))
{
addr=bufRxd[5];//k1=1;
IapEraseSector(0x2000);
IapProgramByte(0x2001,addr);  //写入扇区
buf[0] = addr;          //地址位
buf[1] = 0x06; //功能位
buf[2] = bufRxd[2]; //线圈地址高
buf[3] = bufRxd[3]; //线圈地址低
buf[4] = bufRxd[4]; //发什么回什么
buf[5] = bufRxd[5];
len = 6;
crc = GetCRC16(buf, len); //计算CRC校验值
buf[len++] = crc >> 8;    //CRC高字节
buf[len++] = crc & 0xFF;  //CRC低字节
UartWrite(buf, len);//发送其他数据用串口1
}

break;

case 0x10://写多个寄存器
if((bufRxd[2]==0x00)&&(bufRxd[4]==0x00)&&(bufRxd[5]==0x02)&&(bufRxd[6]==0x04))
{
switch(bufRxd[3])
{
case 0x00: qjc[0]=bufRxd[7];qjc[1]=bufRxd[8];qjc[2]=bufRxd[9];qjc[3]=bufRxd[10];break;
case 0x01: qjc[2]=bufRxd[4];qjc[3]=bufRxd[5];qjc[4]=bufRxd[9];qjc[5]=bufRxd[10];break;
case 0x02: qjc[4]=bufRxd[4];qjc[5]=bufRxd[5];qjc[6]=bufRxd[9];qjc[7]=bufRxd[10];break;
case 0x03: qjc[6]=bufRxd[4];qjc[7]=bufRxd[5];qjc[8]=bufRxd[9];qjc[9]=bufRxd[10];break;
case 0x04: qjc[8]=bufRxd[4];qjc[9]=bufRxd[5];qjc[10]=bufRxd[9];qjc[11]=bufRxd[10];break;
case 0x05: qjc[10]=bufRxd[4];qjc[11]=bufRxd[5];qjc[12]=bufRxd[9];qjc[13]=bufRxd[10];break;
case 0x06: qjc[12]=bufRxd[4];qjc[13]=bufRxd[5];qjc[14]=bufRxd[9];qjc[15]=bufRxd[10];break;
case 0x07: qjc[14]=bufRxd[4];qjc[15]=bufRxd[5];qjc[16]=bufRxd[9];qjc[17]=bufRxd[10];break;
case 0x08: qjc[16]=bufRxd[4];qjc[17]=bufRxd[5];qjc[18]=bufRxd[9];qjc[19]=bufRxd[10];break;
case 0x09: qjc[18]=bufRxd[4];qjc[19]=bufRxd[5];qjc[20]=bufRxd[9];qjc[21]=bufRxd[10];break;
case 0x0A: qjc[20]=bufRxd[4];qjc[21]=bufRxd[5];qjc[22]=bufRxd[10];break;
default:break;
}
buf[0] = addr;          //地址位
buf[1] = 0x10; //功能位
buf[2] = bufRxd[2]; //线圈地址高
buf[3] = bufRxd[3]; //线圈地址低
buf[4] = bufRxd[4]; //发什么回什么
buf[5] = bufRxd[5];
len = 6;
crc = GetCRC16(buf, len); //计算CRC校验值
        buf[len++] = crc >> 8;    //CRC高字节
        buf[len++] = crc & 0xFF;  //CRC低字节
        UartWrite(buf, len);//发送其他数据用串口1
}

break;

case 0x0F://多路控制
if((bufRxd[2]==0x00)&&(bufRxd[3]==0x00)&&(bufRxd[4]==0x00)&&(bufRxd[5]==0x04)&&(bufRxd[6]==0x01))
{
zt1=bufRxd[7];        //                                          
P1=~zt1;
buf[0] = addr;          //地址位
buf[1] = 0x0F; //功能位
buf[2] = bufRxd[2]; //数据长度位
buf[3] = bufRxd[3];
buf[4] = bufRxd[4];
buf[5] = bufRxd[5];
len = 6;
crc = GetCRC16(buf, len); //计算CRC校验值
        buf[len++] = crc >> 8;    //CRC高字节
        buf[len++] = crc & 0xFF;  //CRC低字节
        UartWrite(buf, len);//发送其他数据用串口1
}

break;
default:break;

}

}               

}  
if((bufRxd[0]==0xAA)&&(bufRxd[1]==0x00)&&(bufRxd[2]==0x00)&&(bufRxd[3]==0x00)&&(bufRxd[4]==0xBB)&&(len==5))
{
addr1=IapReadByte(0x2001);
buf[0]=0xAA;
buf[1]=addr;
buf[2]=addr1;
buf[3]=0x00;
buf[4]=0xBB;
len=5;
UartWrite(buf, len);//发送其他数据用串口1
}
for(bz=0;bz<lenx;bz++){bufRxd[bz] = 0;}         //把串口接收的数据缓存清零
}


}

void senddate()//发送输入状态
{
unsigned char xdata buf[10];
unsigned char len=0;
if((k1==1)&&(bk1==0))       
{
bk1=1;
switch(input)
{
case 1:if(IN1 == 0){xx_1 = 0x01;}else{xx_1 = 0x00;} dat1=xx_1;break;//查询输入状态打开为1,关闭为0
case 2:if(IN2 == 0){xx_2 = 0x01;}else{xx_2 = 0x00;} dat1=xx_2;break;
case 3:if(IN3 == 0){xx_3 = 0x01;}else{xx_3 = 0x00;} dat1=xx_3;break;
case 4:if(IN4 == 0){xx_4 = 0x01;}else{xx_4 = 0x00;} dat1=xx_4;break;
default:break;
}

buf[0] = addr; //地址位
buf[1] = 0x02; //功能位
buf[2] = 0x01; //数据长度位
buf[3] = dat1; //输入状态2进制表示有输入为1,没输入为0
len = 4;
crc = GetCRC16(buf, len); //计算CRC校验值
buf[len++] = crc >> 8;    //CRC高字节
buf[len++] = crc & 0xFF;  //CRC低字节
UartWrite(buf, len);//发送其他数据用串口1
k1=0;
ks=1;
}       
if(bzw==1){bzw=0;bk1=0;}//消抖延时后清零       
}

/*******************************************************************
*                        主函数
********************************************************************/
void main()
{
P1=0XFF;
ES = 1;                     //使能串口1中断
EA = 1;
ConfigUART(9600);
Timer0Init();
if(IapReadByte(0x2001)==0xff){addr=0x01;}//首次读取,如果读到0xFF说明没有存过数据,直接付给00值
else
{
addr=IapReadByte(0x2001);
}
while(1)
{

if((IN1==0)&&(bz1==0)){bz1=1;k1=1;input=1;}
if((IN1==1)&&(bz1==1)){bz1=0;k1=1;input=1;}

if((IN2==0)&&(bz2==0)){bz2=1;k1=1;input=2;}
if((IN2==1)&&(bz2==1)){bz2=0;k1=1;input=2;}

if((IN3==0)&&(bz3==0)){bz3=1;k1=1;input=3;}
if((IN3==1)&&(bz3==1)){bz3=0;k1=1;input=3;}

if((IN4==0)&&(bz4==0)){bz4=1;k1=1;input=4;}
if((IN4==1)&&(bz4==1)){bz4=0;k1=1;input=4;}


senddate();
UartDriver();
//    if(k1==1)
//          {
//           IapEraseSector(0x08000);
//                 IapProgramByte(0x08001,addr);  //写入扇区
//                 k1=0;
//          }
}

}
void InterruptTimer0() interrupt 1{//T0中断服务函数,执行串口接收监控
TL0 = 0xCD;                //设置定时初值
TH0 = 0xD4;
if(ks==1){js++;if(js==150){js=0;bzw=1;ks=0;}}
UartRxMonitor(1);  //串口接收监

}
/*******************************************************************
*                       串口中断
********************************************************************/
void Uart() interrupt 4 using 1
{
if (RI)
{
RI = 0;                 //清除RI位
ACC=SBUF;
if(P==RB8){flag=1;}else{flag=0;}
if (cntRxd < sizeof(bufRxd)) {//接收缓冲区尚未用完时,保存接收字节,并递增计数器
bufRxd[cntRxd++] = SBUF;        // cntRxd++这个很重要,一开始 cntRxd < sizeof(bufRxd)当进入函数的次数增加,cntRxd慢慢变大,当传入的数据不满的时候就        用时间检测,判断是否是传输完成
}
}
if (TI)
{
TI = 0;                 //清除TI位
flagTxd = 1;
}
}
/*******************************************************************
*                        串口发送函数
********************************************************************/
void UartWrite(unsigned char *buf, unsigned char len){//串口数据写入,即串口发送函数,buf-待发送数据的指针,len-指定的发送长度

while (len--){   //循环发送所有字节
flagTxd = 0;      //清零发送标志
ACC = *buf;
if (P)                      //根据P来设置校验位
{
#if (PARITYBIT == ODD_PARITY)
TB8 = 0;                //设置校验位为0
#elif (PARITYBIT == EVEN_PARITY)
TB8 = 1;                //设置校验位为1
#endif
}
else
{
#if (PARITYBIT == ODD_PARITY)
TB8 = 1;                //设置校验位为1
#elif (PARITYBIT == EVEN_PARITY)
TB8 = 0;                //设置校验位为0
#endif
}
SBUF = ACC;    //发送一个字节数据
buf++;
while (!flagTxd); //等待该字节发送完成
}

}
/*----------------------------
Disable ISP/IAP/EEPROM function
Make MCU in a safe state
----------------------------*/
void IapIdle()
{
IAP_CONTR = 0;                  //Close IAP function
IAP_CMD = 0;                    //Clear command to standby
IAP_TRIG = 0;                   //Clear trigger register
IAP_ADDRH = 0x80;               //Data ptr point to non-EEPROM area
IAP_ADDRL = 0;                  //Clear IAP address to prevent misuse
}

/*----------------------------
Read one byte from ISP/IAP/EEPROM area
Input: addr (ISP/IAP/EEPROM address)
Output:Flash data
----------------------------*/
BYTE IapReadByte(WORD addr)
{
BYTE dat;                       //Data buffer

IAP_CONTR = ENABLE_IAP;         //Open IAP function, and set wait time
IAP_CMD = CMD_READ;             //Set ISP/IAP/EEPROM READ command
IAP_ADDRL = addr;               //Set ISP/IAP/EEPROM address low
IAP_ADDRH = addr >> 8;          //Set ISP/IAP/EEPROM address high
IAP_TRIG = 0x46;                //Send trigger command1 (0x46)
IAP_TRIG = 0xb9;                //Send trigger command2 (0xb9)
_nop_();                        //MCU will hold here until ISP/IAP/EEPROM operation complete
dat = IAP_DATA;                 //Read ISP/IAP/EEPROM data
IapIdle();                      //Close ISP/IAP/EEPROM function

return dat;                     //Return Flash data
}

/*----------------------------
Program one byte to ISP/IAP/EEPROM area
Input: addr (ISP/IAP/EEPROM address)
dat (ISP/IAP/EEPROM data)
Output:-
----------------------------*/
void IapProgramByte(WORD addr, BYTE dat)
{
IAP_CONTR = ENABLE_IAP;         //Open IAP function, and set wait time
IAP_CMD = CMD_PROGRAM;          //Set ISP/IAP/EEPROM PROGRAM command
IAP_ADDRL = addr;               //Set ISP/IAP/EEPROM address low
IAP_ADDRH = addr >> 8;          //Set ISP/IAP/EEPROM address high
IAP_DATA = dat;                 //Write ISP/IAP/EEPROM data
IAP_TRIG = 0x46;                //Send trigger command1 (0x46)
IAP_TRIG = 0xb9;                //Send trigger command2 (0xb9)
_nop_();                        //MCU will hold here until ISP/IAP/EEPROM operation complete
IapIdle();
}

/*----------------------------
Erase one sector area
Input: addr (ISP/IAP/EEPROM address)
Output:-
----------------------------*/
void IapEraseSector(WORD addr)
{
IAP_CONTR = ENABLE_IAP;         //Open IAP function, and set wait time
IAP_CMD = CMD_ERASE;            //Set ISP/IAP/EEPROM ERASE command
IAP_ADDRL = addr;               //Set ISP/IAP/EEPROM address low
IAP_ADDRH = addr >> 8;          //Set ISP/IAP/EEPROM address high
IAP_TRIG = 0x46;                //Send trigger command1 (0x46)
IAP_TRIG = 0xb9;                //Send trigger command2 (0xb9)
_nop_();                        //MCU will hold here until ISP/IAP/EEPROM operation complete
IapIdle();
}

/********************************************************************
                                        结束
*********************************************************************/


RS485通信 MODBUS-RTU标准程序.rar

79.45 KB, 下载次数: 127, 下载积分: 黑币 -5

回复

使用道具 举报

ID:137005 发表于 2023-12-18 15:42 | 显示全部楼层
多路输出,不是连续P1,如输出IO是P10,P16,P17,P54,程序要如何修改?
回复

使用道具 举报

ID:1109353 发表于 2024-1-16 09:10 | 显示全部楼层
这是作为从机的代码吗?可以和modbus poll通讯吗
回复

使用道具 举报

ID:933601 发表于 2024-1-16 10:34 | 显示全部楼层
谢谢分享!楼主有没有模拟量采集例程让我们学习学习?
回复

使用道具 举报

ID:1006852 发表于 2024-1-17 12:04 | 显示全部楼层
换STC其它型号改动大吗?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表