modbus 协议 及 modbus RTU 的 C51 单片机 程序

合集下载

51单片机与力控modbus通信

51单片机与力控modbus通信

网上关于单片机和力控modbus通信的实例很少,关键并不是modbus协议而是力控与单片机连接的设置,下面的程序是基于51单片机做的。

在正个调试过程中由于我们不清楚力控发送给单片机的请求数据格式,我们可以用串口调试工具进行串口调试,将力控发送请求显示在串口调试工具中,其中单片机的程序如下#include<reg52.h>#include<string.h>#define uchar unsigned char //一个字节#define uint unsigned int // 两个字节void send_char(unsigned char txd);uint rece_count=0;uint send_flag=0;uchar rece_buf[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};uint i;/*串行口初始化*/void chushi(){SCON=0x50; //串口工作方式1,即10位异步PCON=0x00;//波特率不倍增TMOD=0x20;TH1=TL1=0xfa;TR1=1;}/*主程序*/main(){chushi();rece_count=0;while(1){if(RI){RI=0;if(rece_count<8){rece_buf[rece_count]=SBUF;rece_count++;}}if(rece_count==8){send_flag=1;}while(send_flag==1){ for(i=0;i<8;i++){SBUF=rece_buf[i];while(TI==0) ;TI=0;}}}}打开力控运行,将发送的数据保存到单片机中,然后打开串口调试工具,显示如下:我们看力控发送请求的格式:01 03 ff ff 00 01 84 2e,最后的两位crc是高位在前,低位在后。

基于51的MODBUS-RTU多机主从通信

基于51的MODBUS-RTU多机主从通信

基于51的MODBUS-RTU多机通信实验环境:Proteus编程语言:汇编编程环境:KEIL单片机: AT89C51,AT89C52晶振:11.0592MHz功能说明:本实验运用了MODBUS-RTU通信协议的功能码03多寄存器读和功能码10多寄存器写,其中主机首先通过03功能码读取1号从机的时,分和秒的值,并将它们用液晶LCD显示器显示出来。

然后利用10功能将读取的值送入2号从机,2号从机在接收到主机送来的数据后再将它们以与1号从机相同的方式显示出来。

为1号从机设置了按钮,可调节它的时间,同是主机和2号从机也随之显示相同的时间。

图示:部分程序源代码:1、主机ORG 000HLJMP MAINORG 000BHLJMP TMR0ORG 0023HLJMP USARTORG 0040HMAIN: ACALL INITHERE: CLR RS0CLR RS1JB FLAG2.7,TXWORKJB UFLAG.7,RXWORK;AJMP HERETXWORK: AJMP TXWORK1RXWORK: CLR UFLAG.7;**********接收完成,显示操作JB UFLAG.3,RXWMB03RXWMB10: JNB UFLAG.5,HEREAJMP RXMBW101RXWMB03: CLR UFLAG.3MOV R0,#HOUR1MOV A,CND3CLR CRRC AMOV R7,AMOV R1,#CND5 RWORK1: MOV A,@R1MOV @R0,AINC R1INC R1INC R0DJNZ R7,RWORK1MOV DSPC,#8 TIMDIS: MOV A,DSPCMOV DPTR,#DISTBMOVC A,@A+DPTRMOV LCDD,AACALL LCDWPMOV A,#HOUR1ADD A,DSPCMOV R1,AMOV A,@R1MOV B,#10DIV ABACALL DISPMOV A,BACALL DISPDEC DSPCJNB DSPC.7,TIMDIS CLR RENSETB URDMOV CND1,#0A1H MOV CND2,#10H MOV CND3,#0MOV CND4,#50H MOV CND5,#0MOV CND6,#3MOV CND7,#6MOV CND8,#0MOV CND9,HOUR1 MOV CND10,#0 MOV CND11,MIN1 MOV CND12,#0 MOV CND13,SEC1MOV CND15,#0A5HMOV CNDT,#0MOV CNDT1,#15SETB TB8MOV SBUF,CND1AJMP HERE;******************************** RXMBW101: CLR UFLAG.5MOV CNDT,#0MOV CNDT1,#8AJMP HERE;************************************** TXWORK1:CLR FLAG2.7;1S定时CLR RENSETB URD/* MOV CND1,#30HMOV CND2,#31HMOV CND3,#32HMOV CND4,#33HMOV CND5,#34HMOV CND6,#35HMOV CND8,#37HMOV CND9,#38H*/MOV CND1,#0A0H;地址MOV CND2,#03H ;功能码MOV CND3,#0 ;起始地址2字节MOV CND4,#3BHMOV CND5,#0 ;数据长度MOV CND6,#3MOV CND7,#5AHMOV CND8,#0A5HMOV CNDT,#0MOV CNDT1,#8SETB TB8MOV SBUF,CND1AJMP HERE;*******************************DISTB: DB81H,84H,87H,0C1H,0C4H,0C7H,91H,94H,97H,0D1H,0D4H,0D 7H;*************USART: PUSH PSWPUSH ACCSETB RS0SETB RS1JBC RI,RXINT;发送中断,清中断标志位并转中断处理TXINT: CLR TIINC CNDTMOV A,CNDTCJNE A,CNDT1,TXGN;必须在发送前检测,若在后的话,则最后一个字节可能不能正确传输TXSTP: SETB UFLAG.6;发送结束标志MOV CNDT,#0MOV CNDT1,#8CLR URDSETB RENAJMP UOUT;********************TXGN: MOV A,#CND1ADD A,CNDTMOV R0,ACLR TB8MOV SBUF,@R0UOUT: POP ACCPOP PSWRETI;*************************RXINT: MOV RXDT,SBUFMOV A,CNDTCJNE A,#1,RXINT1MOV A,RXDTCJNE A,#03H,RXMD10MOV CNDT1,#5SETB UFLAG.3;MODBUS03功能AJMP RXSTOR;********************** RXMD10: CJNE A,#10H,RXERORMOV CNDT1,#8SETB UFLAG.5;MODBUS10功能AJMP RXSTOR;********************** RXINT1: JNB UFLAG.3,RXSTORCJNE A,#2,RXSTORMOV A,RXDTADD A,CNDT1MOV CNDT1,ARXSTOR: MOV A,#CND1ADD A,CNDTMOV R0,AMOV @R0,RXDT;CLR RIINC CNDTMOV A,CNDTCJNE A,CNDT1,RXOUTCRCCK: MOV A,@R0 ;发送来的数据变形与否的最简单校验SWAP ADEC R0XRL A,@R0JNZ RXERORSETB UFLAG.7MOV CNDT1,#16MOV CNTM_OV,#200RXEROR: MOV CNDT,#0AJMP UOUTRXOUT: MOV CNTM_OV,#200AJMP UOUT;****************************** DELA Y: MOV R2,#250DELA Y1: DJNZ R2,DELA Y1RET;***************************;**************定时器0中断处理程序TMR0: PUSH PSWPUSH ACCCLR RS1SETB RS0/*MOV A,CNTM_OVJZ TMR01DEC CNTM_OVMOV A,CNTM_OVJNZ TMR01CLR URDSETB REN*/TMR01: DJNZ TM0T1,RETIFMOV TM0T1,#10 ;1msDJNZ TM0TB,RETIFMOV TM0TB,#100DJNZ TM0TS,RETIFMOV TM0TS,#10SETB FLAG2.7;***************** ;中断返回RETIF: CLR RS0POP ACCPOP PSWRETI;***********HD44780读写子程序BUSY: MOV P0,#0FFHCLR LCDRSSETB LCDRWSETB LCDEMOV BIT0,P0CLR LCDEJB BIT0.7,BUSYRETLCDWP: ACALL BUSYCLR LCDRSSJMP LCD1LCDWD: ACALL BUSYSETB LCDRSLCD1: CLR LCDRWSETB LCDEMOV P0,LCDDNOPCLR LCDESETB LCDRSRETLCDW: MOV A,@R0ACALL LCDWDINC R0DJNZ R2,LCDWRET;***************DISP: MOV DPTR,#TIMETBMOVC A,@A+DPTRMOV LCDD,AACALL LCDWDRETTIMETB: DB "0123456789:/abcd" ;****************初始化子程序INIT: MOV SP,#0EFHMOV R1,#TM0TCLR0: MOV @R1,#0INC R1CJNE R1,#FRAM_OV,CLR0LCDINT: MOV LCDD,#38H;LCD初始化子程序ACALL LCDWPMOV LCDD,#01HACALL LCDWPMOV LCDD,#06HACALL LCDWPMOV LCDD,#0CHACALL LCDWPMOV LCDD,#83HACALL LCDWPMOV A,#10ACALL DISPMOV LCDD,#86HACALL LCDWPMOV A,#10ACALL DISPMOV LCDD,#0C3HACALL LCDWPMOV A,#10ACALL DISPMOV LCDD,#0C6HACALL LCDWPMOV A,#10ACALL DISPMOV LCDD,#93HACALL LCDWPMOV A,#10ACALL DISPMOV LCDD,#96HACALL LCDWPMOV A,#10ACALL DISPMOV LCDD,#0D3HACALL LCDWPMOV A,#10ACALL DISPMOV LCDD,#0D6HACALL LCDWPMOV A,#10ACALL DISP;***********************MOV TMOD,#22H;定时器0和1均工作于方式2,定时器1用于串口波特率MOV TH0, #0A3H;11.0592M,100usMOV TL0,#0A3HMOV TM0T,#1MOV TM0T1,#1MOV TM0TB,#5MOV TM0TS,#10;USART初始化MOV TH1,#0FDHMOV TL1,#0FDH; SETB P3.0MOV SCON,#0D0H;串行工作方式3,9为数据传输MOV PCON,#00HSETB URDMOV CNDT,#0MOV CNDT1,#8MOV DSPC,#8MOV UFLAG,#0MOV FLAG2,#0SETB TR0SETB ET0SETB TR1;运行定时器1,但不开中断SETB ES;开串行口中断SETB PSSETB EARET;*************************** END2、1号从机ORG 000HAJMP MAINORG 000BHAJMP TMR0ORG 0023HAJMP USARTORG 0040HMAIN: ACALL INITHERE: CLR RS0CLR RS1JNB UFLAG.7,TIMDC HERE2: CLR UFLAG.7CLR RENSETB URDMOV A,CND6MOV R7,ACLR CRLC AMOV CND3,AADD A,#5MOV CNDT1,AMOV R0,CND4MOV R1,#CND4 MDBS03T:MOV @R1,#0INC R1MOV A,@R0MOV @R1,AINC R0INC R1DJNZ R7,MDBS03TMOV @R1,#5AHINC R1MOV @R1,#0A5HMOV CNDT,#0ACALL DELA YCLR TB8MOV SBUF,CND1TIMDC: JB FLAG2.7,KEYSCAN TIMEDC: MOV A,HOURMOV B,#10DIV ABJNZ TIMH1MOV DIS7,#11AJMP TIMH2;************************ TIMH1: MOV DIS7,ATIMH2: MOV DIS6,B TIMEM: MOV A,MINUTEMOV B,#10DIV ABMOV DIS4,AMOV DIS3,BTIMES: MOV A,SECONDMOV B,#10DIV ABMOV DIS1,AMOV DIS0,BAJMP HERE;*************************KEYSCAN:CLR FLAG2.7MOV A,P1ORL A,#0E0HMOV P1,ANOPNOPMOV A,P1ANL A,#0E0HXRL A,#0E0HJZ NKEYMOV KTEMP,AAJMP HERENKEY: JB KTEMP.KEY0,KSET JB KTEMP.KEY1,KADDJNB KTEMP.KEY2,KOUTAJMP KSUBKSET: JB FLAG2.0,KSET1SETB FLAG2.0AJMP KOUTKSET1: INC FLAG2SETB FLAG2.0JNB FLAG2.2,KOUTMOV FLAG2,#0 KOUT: MOV KTEMP,#0AJMP TIMEDCKADD: JNB FLAG2.0,KOUT MOV R0,#MINUTEJNB FLAG2.1,KADD1MOV R0,#HOURKADD1: INC @R0JB FLAG2.1,HOURACJNE @R0,#60,KOUT KADD2: MOV @R0,#0AJMP KOUTHOURA: CJNE @R0,#24,KOUT AJMP KADD2KSUB: JNB FLAG2.0,KOUTMOV R0,#MINUTEJNB FLAG2.1,KSUB1MOV R0,#HOURKSUB1: DEC @R0MOV A,@R0JNB ACC.7,KOUTJB FLAG2.1,KSUBH0MOV @R0,#59AJMP KOUTKSUBH0: MOV @R0,#23AJMP KOUT;****************定时器0中断处理TMR0: PUSH PSWPUSH ACCSETB RS0CLR RS1DJNZ TM0T,TIMEMOV TM0T,#20T0DIS: MOV P0,#0MOV A,P1ANL A,#0F8HORL A,DSPCMOV P1,AMOV A,#DIS0ADD A,DSPCMOV R0,AMOV A,@R0MOV DPTR,#LEDTBMOVC A,@A+DPTRMOV P0,ADEC DSPCJNB DSPC.7,TIMEMOV DSPC,#7;********************************************** TIME: DJNZ TM0T1,RETIFMOV TM0T1,#10 ;1msDJNZ TM0TB,RETIFMOV TM0TB,#100SETB FLAG2.7DJNZ TM0TS,RETIFMOV TM0TS,#10INC SECONDMOV A,SECONDCJNE A,#60,TIMEHMOV SECOND,#00HINC MINUTEMOV A,MINUTECJNE A,#60,TIMEHMOV MINUTE,#00HINC HOURMOV A,HOURCJNE A,#24,TIMEHMOV HOUR,#00HTIMEH:RETIF: POP ACCPOP PSWRETI;*********************;***********七段共阴数码管LEDTB: DB3FH,06H,5BH,4FH,66H,6DH,7DH,07H,7FH,6FH,40H,00H ;******************************/*TIMEH1: SETB URDSETB UFLAG.4MOV CND1,#0A0HMOV CND2,#10HMOV CND3,#0MOV CND4,#40HMOV CND5,#0MOV CND6,#3MOV CND7,#6MOV CND8,#0MOV CND9,HOURMOV CND11,MINUTEMOV CND12,0MOV CND13,SECONDMOV CND14,#5AHMOV CND15,#0A5HMOV CNDT,#0MOV CNDT1,#15SETB UFLAG.6CLR UFLAG.5AJMP RETIF;******************/;*************USART: PUSH PSWPUSH ACCSETB RS0SETB RS1JBC RI,RXINT;发送中断,清中断标志位并转中断处理TXINT: CLR TIINC CNDTCJNE A,CNDT1,TXGN;必须在发送前检测,若在后的话,则最后一个字节可能不能正确传输TXSTP: MOV CNDT,#0MOV CNDT1,#8CLR URDSETB RENAJMP UOUT;********************TXGN: MOV A,#CND1ADD A,CNDTMOV R0,ACLR TB8MOV SBUF,@R0UOUT: POP ACCPOP PSWRETIRXAD: MOV A,RXDTCJNE A,#0A0H,RXERORCLR SM2AJMP RXSTORRXINT: MOV RXDT,SBUFJZ RXADCJNE A,#1,RXSTORMOV CNDT1,#8RXSTOR: MOV A,#CND1ADD A,CNDTMOV R0,AMOV @R0,RXDTINC CNDTMOV A,CNDTCJNE A,CNDT1,RXOUTCRCCK: MOV A,@R0 ;接收到的数据变形与否的简单校验SWAP ADEC R0XRL A,@R0JNZ RXERORSETB UFLAG.7MOV CNDT1,#16RXEROR: SETB SM2MOV CNDT,#0RXOUT: AJMP UOUT;***********************DELA Y: MOV R2,#250DELA Y1: DJNZ R2,DELA Y1RET;****************初始化子程序INIT: MOV SP,#5FHMOV R1,#DIS0MOV DSPC,#0CLR URDCLR0: MOV @R1,#0INC R1CJNE R1,#HOUR,CLR0MOV DSPC,#7MOV DIS2,#10MOV DIS5,#10MOV TMOD,#22H;定时器0和1均工作于方式2,定时器1用于串口波特率MOV TH0, #0A3H;11.0592M,100usMOV TL0,#0A3HMOV TM0T,#1MOV TM0T1,#1MOV TM0TB,#5MOV TM0TS,#10;USART初始化MOV TH1,#0FDHMOV TL1,#0FDHMOV SCON,#0F0H;串行工作方式2,9为数据传输MOV PCON,#00HCLR URDMOV CNDT,#0MOV CNDT1,#8MOV UFLAG,#0MOV FLAG2,#0SETB TR0SETB ET0SETB TR1;运行定时器1,但不开中断SETB ES;开串行口中断SETB PSSETB EARET;***************************END3、2号从机ORG 000HLJMP MAINORG 000BHLJMP TMR0ORG 0023HLJMP USARTORG 0040HMAIN: ACALL INITHERE: CLR RS0CLR RS1JB UFLAG.7,HERE2 AHERE: AJMP HEREHERE2: CLR RENCLR UFLAG.7;**********接收完成,显示操作MOV R0,CND4MOV R7,CND6MOV R1,#CND9HERE1: MOV A,@R1MOV @R0,AINC R1INC R1INC R0DJNZ R7,HERE1 TIMEDC: MOV A,HOURMOV B,#10DIV ABJNZ TIMH1MOV DIS7,#11AJMP TIMH2TIMH1: MOV DIS7,A TIMH2: MOV DIS6,B TIMEM: MOV A,MINMOV B,#10DIV ABMOV DIS4,AMOV DIS3,B TIMES: MOV A,SECMOV B,#10DIV ABMOV DIS1,AMOV DIS0,BSETB URD;从机接收指令后回复报文SECHO: MOV CND7,#5AHMOV CNDT,#0MOV CNDT1,#8CLR TB8MOV SBUF,CND1AJMP HERE;*******************************DISTB: DB81H,84H,87H,0C1H,0C4H,0C7H,91H,94H,97H,0D1H,0D4H,0D7H;*************USART: PUSH PSWPUSH ACCSETB RS0SETB RS1JBC RI,RXINT;发送中断,清中断标志位并转中断处理TXINT: CLR TIINC CNDTMOV A,CNDTCJNE A,CNDT1,TXGN;必须在发送前检测,若在后的话,则最后一个字节可能不能正确传输MOV CNDT1,#8CLR URDSETB RENAJMP UOUT;******************** TXGN: MOV A,#CND1ADD A,CNDTMOV R0,ACLR TB8MOV SBUF,@R0 UOUT: POP ACCPOP PSWRETIRXAD: MOV A,RXDTCJNE A,#0A1H,RXERORCLR SM2AJMP RXSTOR RXINT: MOV RXDT,SBUFMOV A,CNDTJZ RXADCJNE A,#1,RXINT1AJMP RXSTORRXINT1: CJNE A,#6,RXSTORMOV A,RXDTADD A,CNDT1MOV CNDT1,ARXSTOR: MOV A,#CND1ADD A,CNDTMOV R0,AMOV @R0,RXDTINC CNDTMOV A,CNDTCJNE A,CNDT1,RXOUTCRCCK: MOV A,@R0 ;接收到的数据变形与否的简单校验SWAP ADEC R0XRL A,@R0JNZ RXERORSETB UFLAG.7MOV CNDT1,#16RXEROR: SETB SM2RXOUT: AJMP UOUT;****************************** DELAY: MOV R2,#250DELAY1: DJNZ R2,DELAY1RET;***************************;**************定时器0中断处理程序TMR0: PUSH PSWPUSH ACCSETB RS0CLR RS1DJNZ TM0T,TIMEMOV TM0T,#20T0DIS: MOV P0,#0MOV A,P1ANL A,#0F8HORL A,DSPCMOV P1,AMOV A,#DIS0ADD A,DSPCMOV R0,AMOV DPTR,#LEDTBMOVC A,@A+DPTRMOV P0,ADEC DSPCJNB DSPC.7,TIMEMOV DSPC,#7;********************************************** TIME: DJNZ TM0T1,RETIFMOV TM0T1,#10 ;1msDJNZ TM0TB,RETIFMOV TM0TB,#100SETB FLAG2.7DJNZ TM0TS,RETIFMOV TM0TS,#10;***************** ;中断返回RETIF: CLR RS0POP ACCPOP PSWRETI;***********七段共阴数码管LEDTB: DB3FH,06H,5BH,4FH,66H,6DH,7DH,07H,7FH,6FH,40H,00H ;****************初始化子程序INIT: MOV SP,#05FHCLR URDMOV R1,#TM0TCLR0: MOV @R1,#0INC R1CJNE R1,#RXDT,CLR0;***********************MOV TMOD,#22H;定时器0和1均工作于方式2,定时器1用于串口波特率MOV TH0, #0A3H;11.0592M,100usMOV TL0,#0A3HMOV TM0T,#1MOV TM0T1,#1MOV TM0TB,#5MOV TM0TS,#10;USART初始化MOV TH1,#0FDHMOV TL1,#0FDH; SETB P3.0MOV SCON,#0F0H;串行工作方式3,9位数据传输MOV PCON,#00HMOV CNDT,#0MOV CNDT1,#16MOV DSPC,#7MOV DIS2,#10MOV DIS5,#10MOV UFLAG,#0SETB TR0SETB ET0SETB TR1;运行定时器1,但不开中断SETB ES;开串行口中断SETB PSSETB EARET;***************************END。

Modbus RTU协议C51通讯

Modbus RTU协议C51通讯

//CRC-L //CRC-H
即 将接收到的数据的后两个字节(CRC值)变为16位CRC;再将前面的6个字节进行CRC 校验后比较。
CRCData=databuf[7]*256+databuf[6]; if(CRCData==CRC_chek(databuf,6))
//将最后两字节合并 //CRC校验比较
{
if(databuf[1]==0x03) send(); if(databuf[1]==0x06) {temp=databuf[5];back();} }
2.1 程序编写流程及思路
由于使用了MAX485芯片,所以要有控制端控制数据的发送和接受本例用 P3.7端口 发送和接受前要对P3.7进行操作。
当在某一Modbus网络上通信时,此协议决定了每个控制器需要知道它们的设备地址,识别 按地址发来的消息,以及决定要产生何种行动。如果需要回应,则控制器将生成反馈信息并用 Modbus协议发出。在其他网络上,包含了M0dhus协议的消息转换为在此网络上使用的帧或包结 构。
1.2 Modbus RTU通信数据传输模式
(3)数据区 数据区根据功能码的不同而不同。数据区包含需要从机执行什么动作,或由从机采集的返 送信息。这些信息可以是实际数值、设置点、主机发送给从机或从机发送给主机的地址等。例 如,功能码告诉从机读取寄存器的值,则数据区必须包含要读取寄存器的起始地址及读取长度 。
对于不同的从机,地址和数据信息都不相同。
p37=1;
delayms(2500);
databuf[0]= 0x01;
databuf[1]= Func;
databuf[2]= 0x02;
databuf[3]= 0x00;
databuf[4]= temp;

基于Modbus协议的C51软件编程

基于Modbus协议的C51软件编程

基于Modbus协议的C51软件编程张波,张振仁(第二炮兵工程学院502教研室,西安710025)摘要:Modbus协议由于其开放性、透明性和低成本等特点,易于开发,已成为当今工业领域通讯协议的首选。

本文介绍通过C51编程实现Modbus的方法,该方法在MCS-51系列单片机上具有通用性,具有一定的借鉴作用。

关键词:ModbusProtocal;RTU;C51;软件编程中图分类号:TP393.04文献标识码:B文章编号:1001-1390(2005)08-0054-03ZHANGBoP ZHANGZhen-ren(No.502staffroom,EngineeringCollegeofPLASecondArtillaryForcesPXi'an710025P China)AbstractZBecauseofitswidelyopeningandeasyperformanceP Modbusprotocolhasbecomethemostprevalentfieldbusintheworld.Akindofcommunicationsoftwaremethodisintroducedinthispaper.ThissoftwareuseC51asprogramlanguage,itisusedbybottomdevicewhichcommunicateswithotherdevicesbymeansofModbusprotocolP anditcanbeusedasreferenceforotherdevice.KeywordsZ Modbusprotocol_RTU_C51_communicationprogrammethodUsingC51LanguagetoProgramCommunicationSoftwareforIntelligentDeviceBasedonModbusProtocol0前言当前,工业控制已从单机控制走向集中监控、集散控制,Modbus作为工业领域最流行的网络应用协议在各仪器仪表和测控系统设计中得到了广泛的应用。

modbus-RTU-的-C51-单片机-程序

modbus-RTU-的-C51-单片机-程序

modbus RTU 的C51 单片机程序本文转自:新势力单片机[url]http://www.XinShiLi.n et[/url]modbus.c#include "main.h"//字地址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,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, 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,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,0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,0x43, 0x83, 0x41, 0x81, 0x80, 0x40} ;uint8 testCoil; //用于测试位地址1uint16 testRegister; //用于测试字址址16uint8 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,&tem pData);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,t empAddr++){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){ //设为ONtempData = 1;}//else if(onOff == 0x0000) else if(onOff == 0x00){ //设为OFFtempData = 0;}setCoilVal(tempAddr,temp Data);for(i=0;i<receCount;i++) {sendBuf = receBuf;}sendCount = receCount; beginSend();}//void forceSingleCoil(void)//设置多个寄存器void presetMultipleRegisters(vo id){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++,te mpAddr++){tempData = (receBuf[i*2+7]<<8) + receBuf[i*2+8];setRegisterVal(tempAddr,t empData);}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(vo id)//检查uart0数据voidcheckComm0Modbus(void ){uint16 crcData;uint16 tempData;if(receCount > 4){switch(receBuf[1]){case 1://读取线圈状态(读取点16位以内)case 3://读取保持寄存器(一个或多个)。

51单片机程序(modbus通信)

51单片机程序(modbus通信)

modbus通信1、LCD.c#include <reg51.h>#include<LCD.h>unsigned char code number_X[]={ //宽x高=8x16,纵向字节倒序0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00, //00x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00, //10x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00, //20x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00, //30x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00, //40x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00, //50x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00, //60x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00, //70x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00, //80x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00, //90x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // .0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00, //-0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //nop 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00, //:0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00};void LCD_WriteCommandE1(unsigned char com) {while(CRADD1 & 0x80);CWADD1 = com;}void LCD_WriteDataE1(unsigned char dat)while(CRADD1 & 0x80);DWADD1 = dat;}void LCD_WriteCommandE2(unsigned char com) {while(CRADD2 & 0x80);CWADD2 = com;}void LCD_WriteDataE2(unsigned char dat){while(CRADD2 & 0x80);DWADD2 = dat;}void LCD_Init(){LCD_WriteCommandE1(0xe2);LCD_WriteCommandE2(0xe2);LCD_WriteCommandE1(0xa4);LCD_WriteCommandE2(0xa4);LCD_WriteCommandE1(0xa9);LCD_WriteCommandE2(0xa9);LCD_WriteCommandE1(0xa0);LCD_WriteCommandE2(0xa0);LCD_WriteCommandE1(0xc0);LCD_WriteCommandE2(0xc0);LCD_WriteCommandE1(0xaf);LCD_WriteCommandE2(0xaf);}void LCD_Clear(void){unsigned char i,j;for(i=0;i<4;i++){LCD_WriteCommandE1(i+0xb8);LCD_WriteCommandE2(i+0xb8);LCD_WriteCommandE1(0x00);LCD_WriteCommandE2(0x00);for(j=0;j<0x50;j++){LCD_WriteDataE1(0x00);LCD_WriteDataE2(0x00);}}void display_cn(unsigned char lin,unsigned int col,unsigned int len,unsigned char *p) {unsigned int seg,i,j;unsigned char a,L,n;switch(lin){case 0: n=0xba;break;case 1: n=0xb8;break;}for(i=0;i<len;i++){for(j=0;j<2;j++){L=col;LCD_WriteCommandE1(n+j);LCD_WriteCommandE2(n+j);for(seg=0;seg<16;seg++){if (L < 61){a = L;LCD_WriteCommandE1(a);LCD_WriteDataE1(*p++);}else{a = L-61;LCD_WriteCommandE2(a);LCD_WriteDataE2(*p++);}L++;}}col=col+16;}}void display_number(unsigned char lin,unsigned int col,unsigned char num){unsigned int seg,i,j;unsigned char a,L,n,k;switch(lin){case 0: n=0xba;break;case 1: n=0xb8;break;}k=num*16;for(j=0;j<2;j++){L=col;LCD_WriteCommandE1(n+j);LCD_WriteCommandE2(n+j);for(seg=0;seg<8;seg++){if (L < 61){a = L;LCD_WriteCommandE1(a);LCD_WriteDataE1(number_X[k++]);}else{a = L-61;LCD_WriteCommandE2(a);LCD_WriteDataE2(number_X[k++]);}L++;}}}void display_unsigned_int(unsigned char lin,unsigned int col,unsigned int dat) {unsigned int seg;unsigned char k[4];k[3]=dat%10;k[2]=((dat/10)%10);k[1]=((dat/100)%10);k[0]=((dat/1000)%10);if(k[0]==0) {k[0]=12;}if((k[0]==12)&&(k[1]==0)){ k[0]=12;k[1]=12;}if((k[0]==12)&&(k[1]==12)&&(k[2]==0)){k[0]=12;k[1]=12;k[2]=12;}for(seg=0;seg<4;seg++){display_number(lin,col,k[seg]);col=col+10;}}void display_signed_int(unsigned char lin,unsigned int col,signed int dat){unsigned int seg;unsigned char k[5],a;k[0]=12;if(dat<0){dat=(~dat)+1;k[0]=11;}k[4]=dat%10;k[3]=((dat/10)%10);k[2]=((dat/100)%10);k[1]=((dat/1000)%10);a=k[0];if(k[1]==0) {k[0]=12;k[1]=a;}if((k[1]==a)&&(k[2]==0)){ k[0]=12;k[1]=12;k[2]=a;}if((k[1]==12)&&(k[2]==a)&&(k[3]==0)){k[0]=12;k[1]=12;k[2]=12;k[3]=a;}for(seg=0;seg<5;seg++){display_number(lin,col,k[seg]);col=col+10;}}void display_unsigned_char(unsigned char lin,unsigned int col,unsigned char dat) {unsigned int seg;unsigned char k[3];k[1]=dat%10;k[0]=((dat/10)%10);for(seg=0;seg<2;seg++){display_number(lin,col,k[seg]);col=col+10;}}2、LCD.h#include <reg51.h>#include <absacc.h>#ifndef __LCD__#define __LCD__#define CWADD1 XBYTE[0x8000]#define DWADD1 XBYTE[0x8001]#define CRADD1 XBYTE[0x8002]#define DRADD1 XBYTE[0x8003]#define CWADD2 XBYTE[0x8004]#define DWADD2 XBYTE[0x8005]#define CRADD2 XBYTE[0x8006]#define DRADD2 XBYTE[0x8007]extern void LCD_Init();extern void display_cn(unsigned char lin,unsigned int col,unsigned int len,unsigned char *p);extern void display_signed_int(unsigned char lin,unsigned int col,signed int dat);extern void display_unsigned_int(unsigned char lin,unsigned int col,unsigned int dat);extern void display_unsigned_char(unsigned char lin,unsigned int col,unsigned char dat);extern void LCD_Clear(void);#endif1、MCU.c#include <reg51.h>#include "MCU.h"#define oscillator 12000000void timer0_init(void){TMOD|=0x01;//Timer0 is 16 bits timer, timer1 is Baud rate generatorTH0=0x97;TL0=0xd5;TR0=1;ET0=1;}void uart_init(unsigned int baud_rate){PCON=0x00;SCON=0x50;TMOD|=0x20;TL1=256-(oscillator/384)/baud_rate;TH1=256-(oscillator/384)/baud_rate;TR1=1;ES=1;}2、MCU.h#ifndef __MCU__#define __MCU__extern void timer0_init(void);extern void uart_init(unsigned int baud_rate);#endif3、MODBUSASC.c#include <reg51.h>unsigned char xdata TX_buf[200]_at_ 0x9100;unsigned char xdata Dat[200] _at_ 0x9200;void Data_ASCII(unsigned char num,unsigned char * buf ){unsigned char a;a=(num>>4)&0x0f;if(a>9) buf[0]=a+0x37;else buf[0]=a+0x30;a=num&0x0f;if(a>9) buf[1]=a+0x37;else buf[1]=a+0x30;}unsigned char ASCII_Data(unsigned char* buf ){unsigned char a,b;a=buf[0]-0x30;if(a>9) a=a-0x07;b=buf[1]-0x30;if(b>9) b=b-0x07;a=(a<<4)|b;return (a);}unsigned char LRC(unsigned char*Msg_buf,unsigned char MsgLen){unsigned char LRC_buf = 0,i ;for(i=0;i<MsgLen;i++){LRC_buf=Msg_buf[i]+LRC_buf;}i=~LRC_buf;LRC_buf=i+1;return LRC_buf ;}void Send_modbusASC_UART(unsigned char* SendData,unsigned char DataLen) {unsigned char buf[2];unsigned int i,j,k;j=1;TX_buf[0]=0x3a;for(i=0;i<DataLen;i++){Data_ASCII(SendData[i],buf );TX_buf[j]=buf[0];j++;TX_buf[j]=buf[1];j++;}TX_buf[j]=0x0d;j++;TX_buf[j]=0x0a;j++;for(i=0;i<j;i++){TI=0;SBUF=TX_buf[i];while(!TI);TI=0;for(k=0;k<30;k++);}}void Send_modbusASC_Err(unsigned char FunNum,unsigned char ErrNum,unsigned char ID) {unsigned char temp[4];temp[0]=ID;temp[1]=FunNum+0x80;temp[2]=ErrNum;temp[3]=LRC(temp,3);Send_modbusASC_UART(temp,4);}void FunctionASC_NO3(unsigned char* Msg_buf,unsigned int* AO_state,unsigned char ID) {unsigned char a,i,j,n,temp[2];unsigned int Fun_addr,Fun_num;j=1;for(i=0;i<7;i++){temp[0]=Msg_buf[j];j++;temp[1]=Msg_buf[j];j++;Dat[i]=ASCII_Data(temp );}a=LRC(Dat,6);if(a!=Dat[6]) return;Fun_addr=(Dat[2]<<8)|Dat[3];Fun_num=(Dat[4]<<8)|Dat[5];if((Fun_num==0)||(Fun_num>64)){Send_modbusASC_Err(1,3,ID);return;}if(Fun_addr>63){Send_modbusASC_Err(1,2,ID);return;}Dat[0]=ID;Dat[1]=3;Dat[2]=Fun_num*2;j=3;n=Fun_addr;for(i=0;i<Fun_num;i++){Dat[j]=(unsigned char)(AO_state[n]>>8);j++;Dat[j]=(unsigned char)AO_state[n];j++;n++;}Dat[j]=LRC(Dat,j);Send_modbusASC_UART(Dat,j+1);}void FunctionASC_NO6(unsigned char* Msg_buf,unsigned int* AO_state,unsigned char ID) {unsigned char a,i,j,temp[2];unsigned int Fun_addr,Fun_out;j=1;for(i=0;i<7;i++){temp[0]=Msg_buf[j];j++;temp[1]=Msg_buf[j];j++;Dat[i]=ASCII_Data(temp );}a=LRC(Dat,6);if(a!=Dat[6]) return;Fun_addr=(Dat[2]<<8)|Dat[3];Fun_out=(Dat[4]<<8)|Dat[5];if(Fun_out>0xff00){Send_modbusASC_Err(1,3,ID);return;}if(Fun_addr>64){Send_modbusASC_Err(1,2,ID);return;}AO_state[Fun_addr]=Fun_out;j=1;for(i=0;i<5;i++){Dat[j]=Msg_buf[j];j++;}Dat[j]=LRC(Dat,j);Send_modbusASC_UART(Msg_buf,j+1);}4、MODBUSASC.h#ifndef _MODBUSASC_#define _MODBUSASC_#ifdef __cplusplusextern "C"{#endifextern void Data_ASCII(unsigned char num,unsigned char * buf );extern unsigned char ASCII_Data(unsigned char* buf );extern void FunctionASC_NO3(unsigned char* Msg_buf,unsigned int* AO_state,unsigned char ID);extern void FunctionASC_NO4(unsigned char* Msg_buf,unsigned int* AI_state,unsigned char ID);extern void FunctionASC_NO6(unsigned char* Msg_buf,unsigned int* AO_state,unsigned char ID);#ifdef __cplusplus}#endif5、MAIN.c#include <reg51.h>#include<LCD.h>#include<main.h>#include "mcu.h"#include "modbusASC.h"void wait(unsigned int x){unsigned int i;i=0;for(i=0;i<x;i++);}void timer0_isr(void)interrupt 1{TH0=0x3c;TL0=0xb0;ms_count++;if(ms_count>=20){ms_count=0;LED=~LED;S_flag=1;}}void UART_isr(void)interrupt 4{unsigned char a;RI=0;a=SBUF;if(a==0x3a)UART_offset=0;if(a==0x0a)UART_message=1;RX_buf[UART_offset]=a;UART_offset++;}void display(void){unsigned char i,col;col=28;display_unsigned_int(0,col,modbus_buf[0]);display_unsigned_int(1,col,modbus_buf[1]); }void ModbusASC_Msg(unsigned char* modmus_buf){unsigned char temp[2];unsigned char a;temp[0]=modmus_buf[1];temp[1]=modmus_buf[2];a=ASCII_Data(temp );if(a!=Mode_ID) return;temp[0]=modmus_buf[3];temp[1]=modmus_buf[4];a=ASCII_Data(temp );switch(a){case 0x03:{FunctionASC_NO3(modmus_buf,modbus_buf,Mode_ID);break;}case 0x06:{FunctionASC_NO6(modmus_buf,modbus_buf,Mode_ID);break;}}}void main(void){unsigned char i;LCD_Init();LCD_Clear();display_cn(0,0,1,RD_dat);display_cn(1,0,1,TD_dat);modbus_buf[0]=18;modbus_buf[1]=28;timer0_init();uart_init(2400);EA=1;Mode_ID=1;while(1){display( );if(UART_message==1){ModbusASC_Msg(RX_buf);UART_message=0;}if(S_flag==1){modbus_buf[0]++;S_flag=0;}}}6、MAIN.h#include <reg51.h>#ifndef MAIN_H__#define MAIN_H__code unsigned char RD_dat[]={ //纵向字节倒序。

modbus协议51端程序的实现RTU需要一个定时器来判断35个流逝时间

modbus协议51端程序的实现RTU需要一个定时器来判断35个流逝时间

#define ENABLE 1#define DISABLE 0#define TRUE 1#define FAULT 0#define RECEIVE_EN 0#define TRANSFER_EN 1#define MAX_RXBUF 0x20extern unsigned char emissivity;extern unsigned char tx_count,txbuf[15];extern unsigned char rx_count,rxbuf[15];extern unsigned char tx_number,rx_number;extern bit rx_ok;unsigned char rx_temp;void InitTimer1() //针对标准8051{TMOD=(TMOD|0xf0)&0x1f; //将T1设为16位定时器TF1=0;TH1=0x62; //设T1位3.5位的接收时间35bit/9600bit/s=3.646msTL1=0x80;//晶振为11.0592MHz,T=65535-3.646ms*11.0592MHz/12=0xf2df //0x6280是22.1184M下LPC9XX下的值。

ET1=1; //允许T1中断TR1=1; //T1开始计数}void timer1() interrupt 3 using 2 //定时器中断{TH1=0x62; //3.646ms interruptTL1=0x80;if(rx_count>=5) //超时后,若接收缓冲区有数则判断为收到一帧{rx_ok=TRUE;}}void scomm() interrupt 4 using 3 //modbus RTU模式{if(TI){TI = 0;if(tx_count < tx_number) //是否发送结束{SBUF = txbuf[tx_count];}tx_count++;}if(RI){rx_temp=SBUF;if(rx_ok==FAULT) //已接收到一帧数据,在未处理之前收到的数舍弃{if(rx_countrxbuf[rx_count]=rx_temp;rx_count++;}TH1=0x62; //timer1 reset,count againTL1=0x80;RI=0;}}在主循环中判断标志rx_ok来执行帧处理。

关于51单片机上实现modbus协议

关于51单片机上实现modbus协议

关于51单片机上实现modbus协议你找一个MODBUS的协议详细资料好好看看,就是一种通讯约定,你按照它规定的格式通讯就可以了协议发送给询问方。

Modbus协议包括ASCII、RTU、TCP等,并没有规定物理层。

此协议定义了控制器能够认识和使用的消息结构,而不管它们是经过何种网络进行通信的。

标准的Modicon控制器使用RS232C实现串行的Modbus。

Modbus的ASCII、RTU协议规定了消息、数据的结构、命令和就答的方式,数据通讯采用Maser/Slave方式,Master端发出数据请求消息,Slave端接收到正确消息后就可以发送数据到Master端以响应请求;Master端也可以直接发消息修改Slave端的数据,实现双向读写。

Modbus协议需要对数据进行校验,串行协议中除有奇偶校验外,ASCII模式采用LRC校验,RTU模式采用16位CRC校验,但TCP模式没有额外规定校验,因为TCP协议是一个面向连接的可靠协议。

另外,Modbus采用主从方式定时收发数据,在实际使用中如果某Slave站点断开后(如故障或关机),Master端可以诊断出来,而当故障修复后,网络又可自动接通。

因此,Modbus协议的可靠性较好。

下面我来简单的给大家介绍一下,对于Modbus的ASCII、RTU和TCP协议来说,其中TCP和RTU协议非常类似,我们只要把RTU协议的两个字节的校验码去掉,然后在RTU协议的开始加上5个0和一个6并通过TCP/IP网络协议发送出去即可。

所以在这里我仅介绍一下Modbus的ASCII和RTU协议。

下表是ASCII协议和RTU协议进行的比较:协议开始标记结束标记校验传输效率程序处理ASCII :(冒号)CR,LF LRC 低直观,简单,易调试RTU 无无CRC 高不直观,稍复杂通过比较可以看到,ASCII协议和RTU协议相比拥有开始和结束标记,因此在进行程序处理时能更加方便,而且由于传输的都是可见的ASCII字符,所以进行调试时就更加的直观,另外它的LRC校验也比较容易。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

modbus 协议及modbus RTU 的C51 单片机程序Modbus通讯协议Modbus协议最初由Modicon公司开发出来,在1979年末该公司成为施耐德自动化(Schneider Automation)部门的一部分,现在Modbus已经是工业领域全球最流行的协议。

此协议支持传统的RS-232、RS-422、RS-485和以太网设备。

许多工业设备,包括PLC,DCS,智能仪表等都在使用Modbus协议作为他们之间的通讯标准。

有了它,不同厂商生产的控制设备可以连成工业网络,进行集中监控。

当在网络上通信时,Modbus协议决定了每个控制器须要知道它们的设备地址,识别按地址发来的消息,决定要产生何种行动。

如果需要回应,控制器将生成应答并使用Modbus协议发送给询问方。

Modbus 协议包括ASCII、RTU、TCP等,并没有规定物理层。

此协议定义了控制器能够认识和使用的消息结构,而不管它们是经过何种网络进行通信的。

标准的Modicon控制器使用RS232C实现串行的Modbus。

Modbus的ASCII、RTU协议规定了消息、数据的结构、命令和就答的方式,数据通讯采用Maser/Slave方式,Master端发出数据请求消息,Slave端接收到正确消息后就可以发送数据到Master端以响应请求;Master端也可以直接发消息修改Slave端的数据,实现双向读写。

Modbus 协议需要对数据进行校验,串行协议中除有奇偶校验外,ASCII模式采用LRC校验,RTU模式采用16位CRC校验,但TCP模式没有额外规定校验,因为TCP协议是一个面向连接的可靠协议。

另外,Modbus采用主从方式定时收发数据,在实际使用中如果某Slave 站点断开后(如故障或关机),Master端可以诊断出来,而当故障修复后,网络又可自动接通。

因此,Modbus协议的可靠性较好。

下面我来简单的给大家介绍一下,对于Modbus的ASCII、RTU和TCP协议来说,其中TCP 和RTU协议非常类似,我们只要把RTU协议的两个字节的校验码去掉,然后在RTU协议的开始加上5个0和一个6并通过TCP/IP网络协议发送出去即可。

所以在这里我仅介绍一下Modbus的ASCII和RTU协议。

下表是ASCII协议和RTU协议进行的比较:协议开始标记结束标记校验传输效率程序处理ASCII:(冒号)CR,LFLRC低直观,简单,易调试RTU无无CRC高不直观,稍复杂通过比较可以看到,ASCII协议和RTU协议相比拥有开始和结束标记,因此在进行程序处理时能更加方便,而且由于传输的都是可见的ASCII字符,所以进行调试时就更加的直观,另外它的LRC校验也比较容易。

但是因为它传输的都是可见的ASCII字符,RTU传输的数据每一个字节ASCII都要用两个字节来传输,比如RTU传输一个十六进制数0xF9,ASCII 就需要传输’F’’9’的ASCII码0x39和0x46两个字节,这样它的传输的效率就比较低。

所以一般来说,如果所需要传输的数据量较小可以考虑使用ASCII协议,如果所需传输的数据量比较大,最好能使用RTU协议。

下面对两种协议的校验进行一下介绍。

1、LRC校验LRC域是一个包含一个8位二进制值的字节。

LRC值由传输设备来计算并放到消息帧中,接收设备在接收消息的过程中计算LRC,并将它和接收到消息中LRC域中的值比较,如果两值不等,说明有错误。

LRC校验比较简单,它在ASCII协议中使用,检测了消息域中除开始的冒号及结束的回车换行号外的内容。

它仅仅是把每一个需要传输的数据按字节叠加后取反加1即可。

下面是它的VC代码:BYTE GetCheckCode(const char * pSendBuf, int nEnd)//获得校验码{BYTE byLrc = 0;char pBuf[4];int nData = 0;for(i=1; i<end; i+=2) //i初始为1,避开“开始标记”冒号{//每两个需要发送的ASCII码转化为一个十六进制数pBuf [0] = pSendBuf ;pBuf [1] = pSendBuf [i+1];pBuf [2] = '\0';sscanf(pBuf,"%x",& nData);byLrc += nData;}byLrc = ~ byLrc;byLrc ++;return byLrc;}2、CRC校验CRC域是两个字节,包含一16位的二进制值。

它由传输设备计算后加入到消息中。

接收设备重新计算收到消息的CRC,并与接收到的CRC域中的值比较,如果两值不同,则有误。

CRC是先调入一值是全“1”的16位寄存器,然后调用一过程将消息中连续的8位字节各当前寄存器中的值进行处理。

仅每个字符中的8Bit数据对CRC有效,起始位和停止位以及奇偶校验位均无效。

CRC 产生过程中,每个8位字符都单独和寄存器内容相或(OR),结果向最低有效位方向移动,最高有效位以0填充。

LSB被提取出来检测,如果LSB为1,寄存器单独和预置的值或一下,如果LSB为0,则不进行。

整个过程要重复8次。

在最后一位(第8位)完成后,下一个8位字节又单独和寄存器的当前值相或。

最终寄存器中的值,是消息中所有的字节都执行之后的CRC值。

CRC添加到消息中时,低字节先加入,然后高字节。

下面是它的VC代码:WORD GetCheckCode(const char * pSendBuf, int nEnd)//获得校验码{WORD wCrc = WORD(0xFFFF);for(int i=0; i<nEnd; i++){wCrc ^= WORD(BYTE(pSendBuf));for(int j=0; j<8; j++){if(wCrc & 1){wCrc >>= 1;wCrc ^= 0xA001;}else{wCrc >>= 1;}}}return wCrc;}对于一条RTU协议的命令可以简单的通过以下的步骤转化为ASCII协议的命令:1、把命令的CRC校验去掉,并且计算出LRC校验取代。

2、把生成的命令串的每一个字节转化成对应的两个字节的ASCII码,比如0x03转化成0x30,0x33(0的ASCII码和3的ASCII码)。

3、在命令的开头加上起始标记“:”,它的ASCII码为0x3A。

4、在命令的尾部加上结束标记CR,LF(0xD,0xA),此处的CR,LF表示回车和换行的ASCII 码。

所以以下我们仅介绍RTU协议即可,对应的ASCII协议可以使用以上的步骤来生成。

下表是Modbus支持的功能码:功能码名称作用01读取线圈状态取得一组逻辑线圈的当前状态(ON/OFF)02读取输入状态取得一组开关输入的当前状态(ON/OFF)03读取保持寄存器在一个或多个保持寄存器中取得当前的二进制值04读取输入寄存器在一个或多个输入寄存器中取得当前的二进制值05强置单线圈强置一个逻辑线圈的通断状态06预置单寄存器把具体二进值装入一个保持寄存器07读取异常状态取得8个内部线圈的通断状态,这8个线圈的地址由控制器决定08回送诊断校验把诊断校验报文送从机,以对通信处理进行评鉴09编程(只用于484)使主机模拟编程器作用,修改PC从机逻辑10控询(只用于484)可使主机与一台正在执行长程序任务从机通信,探询该从机是否已完成其操作任务,仅在含有功能码9的报文发送后,本功能码才发送读取事件计数可使主机发出单询问,并随即判定操作是否成功,尤其是该命令或其他应答产生通信错误时12读取通信事件记录可是主机检索每台从机的ModBus事务处理通信事件记录。

如果某项事务处理完成,记录会给出有关错误13编程(184/384 484 584)可使主机模拟编程器功能修改PC从机逻辑14探询(184/384 484 584)可使主机与正在执行任务的从机通信,定期控询该从机是否已完成其程序操作,仅在含有功能13的报文发送后,本功能码才得发送15强置多线圈强置一串连续逻辑线圈的通断16预置多寄存器把具体的二进制值装入一串连续的保持寄存器17报告从机标识可使主机判断编址从机的类型及该从机运行指示灯的状态18(884和MICRO 84)可使主机模拟编程功能,修改PC状态逻辑19重置通信链路发生非可修改错误后,是从机复位于已知状态,可重置顺序字节20读取通用参数(584L)显示扩展存储器文件中的数据信息21写入通用参数(584L)把通用参数写入扩展存储文件,或修改之22~64保留作扩展功能备用65~72保留以备用户功能所用留作用户功能的扩展编码73~119非法功能120~127保留留作内部作用128~255保留用于异常应答在这些功能码中较长使用的是1、2、3、4、5、6号功能码,使用它们即可实现对下位机的数字量和模拟量的读写操作。

1、读可读写数字量寄存器(线圈状态):计算机发送命令:[设备地址] [命令号01] [起始寄存器地址高8位] [低8位] [读取的寄存器数高8位] [低8位] [CRC校验的低8位] [CRC校验的高8位]例:[11][01][00][13][00][25][CRC低][CRC高]意义如下:<1>设备地址:在一个485总线上可以挂接多个设备,此处的设备地址表示想和哪一个设备通讯。

例子中为想和17号(十进制的17是十六进制的11)通讯。

<2>命令号01:读取数字量的命令号固定为01。

<3>起始地址高8位、低8位:表示想读取的开关量的起始地址(起始地址为0)。

比如例子中的起始地址为19。

<4>寄存器数高8位、低8位:表示从起始地址开始读多少个开关量。

例子中为37个开关量。

<5>CRC校验:是从开头一直校验到此之前。

在此协议的最后再作介绍。

此处需要注意,CRC 校验在命令中的高低字节的顺序和其他的相反。

设备响应:[设备地址] [命令号01] [返回的字节个数][数据1][数据2]...[数据n][CRC校验的低8位] [CRC校验的高8位]例:[11][01][05][CD][6B][B2][0E][1B][CRC低][CRC高]意义如下:<1>设备地址和命令号和上面的相同。

<2>返回的字节个数:表示数据的字节个数,也就是数据1,2...n中的n的值。

<3>数据1...n:由于每一个数据是一个8位的数,所以每一个数据表示8个开关量的值,每一位为0表示对应的开关断开,为1表示闭合。

比如例子中,表示20号(索引号为19)开关闭合,21号断开,22闭合,23闭合,24断开,25断开,26闭合,27闭合...如果询问的开关量不是8的整倍数,那么最后一个字节的高位部分无意义,置为0。

相关文档
最新文档