modbus 协议 及 modbus RTU 的 C51 单片机 程序
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多机通信实验环境: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通讯
//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软件编程张波,张振仁(第二炮兵工程学院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 单片机程序本文转自:新势力单片机[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通信)
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个流逝时间
#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协议你找一个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校验也比较容易。
51单片机的modbus
#include <intrins.h>
#define uchar unsigned char
#define length 8
unsigned char sum=0;
unsigned int crc;
unsigned char crc_tmp=0;
/* 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,
TH1=0xFA;
TL1=0xFA;
TR1=1;
//PS=1;
}
unsigned char delay(unsigned char j)
{
unsigned char k,l;
for(l=0;l<=j;l++ )
for(k=0;k<=10;k++);
return 0;
typedef unsigned char uint8;
typedef unsigned int uint16;
typedef unsigned long uint32;
uint16 crc16(uint8 *puchMsg, uint16 usDataLen);
sbit Send_Rcv=P3^2;
sbit bdog = P3^4;
51单片机MODBUS协议汇编-蚂蚁叼骨头
51单片机MODBUS协议汇编-蚂蚁叼骨头51单片机汇编—MODBUS协议(带写入功能)//读数据帧:01设备地址03功能码0000起始地址0001数据长度840A校验码//读返回帧:01设备地址03功能码02数据个数12 数据34数据B533校验码//写数据帧:01设备地址10功能码0001起始地址0001数据长度02数据字节数12数据34数据AAF6校验码//写返回帧:以写帧相同。
//带此‘-----’标志,且后无任何备注的可以去除或用空指令代替,此些地方时是为了测试时使用的状态显示位。
ORG 0000HLJMP MAIN;ORG 0023HLJMP PCTX;ORG 0100HMAIN: MOV SP,#30H;MOV P0,#00H;MOV 60H,#12H;---------------MOV 61H,#34H;----------------------MOV 62H,#56H; --------MOV 63H,#78H;---MOV TMOD,#20H;MOV TL1,#0FDH;MOV TH1,#0FDH;MOV SCON,#50H;MOV PCON,#00H;;/MOV R3,#00H;存入数据个数(未用)MOV P1,#00H;MOV R3,#00H;MOV R2,#08H;MOV R1,#40H;数据首地址(未用)//MOV 0B8H,#10H; 串口优先级SETB TR1;SETB ES;//MOV DPTR,#0100H;SETB EA;MOV P0,#00H;MOV R4,#00H;LOO:MOV A,R3;JNZ LOP;MOV A,R4;MOV P1,A;INC R4;LCALL DELAY1S;DJNZ R5,LOO;JMP LOON;PCTX: SETB P0.0;------------------ TX: JB RI,LOOP;LCALL DELAY10;延时10ms等待;JNB RI,LOP;SETB P0.1;----------------- LJMP OUT;LOOP: MOV A,SBUF;MOV 3EH,A;CLR RI;MOV A,R1;ADD A,R3;MOV R0,A;MOV @R0,3EH;INC R3;存入数据个数(未用)MOV A,R3;-------------MOV P2,A;-------------LCALL DELAY10;LJMP OUT;//DJNZ R2,OUT;LOP:CLR ES;//LCALL DELAY1S;延时10msMOV R1,#40H;MOV A,@R1;(数据地址送A比较器)SUBB A,#01H;(地址为01响应)JNZ RESET;SETB P0.2;----------MOV A,41H;SUBB A,#03;功能码为03,对数据读操作。
51单片机新手入门之Modbus通讯
51单片机新手入门之Modbus通讯本文和另一篇----C# WPF新手入门之Modbus通讯为一个系列,所用模块均是气体分析模块,之前用C3做的上位机(主要是方便调试,单片机不好搞),既然电脑上串口通讯代码没问题,那么放到单片机稍加修改也就理所当然肯定可行O(∩_∩)O~。
本文包括单片机的电路设计和软件设计两部分(单片机采用STC12C5A60S2双串口),至此单片机和上位机的串口通讯均成功实现,供需要的同学参考。
1.电路设计Altium Designer原理图如下:电源部分:24V转5V及6VAD及滤波Modbus通讯电脑串口通讯lcd屏显示预留接口,可以另接MAX232用来调试发送给模块数据正确性实际效果如下:电脑串口接收到的数据,和LCD 屏显示的是一样的下载步骤1. 选择芯片下载步骤2. 打开串口 下载步骤3. 打开hex 文件 下载步骤4. MCU 先断电,点击下载,看到提示后上电2.软件设计这里最麻烦的在于模块的Modbus通讯需要偶校验,我是手动添加的校验位,例如字符‘0’换成ASCII码0X30,添加偶校验后就是0X3A,把所有需要发送的数据一个个手动添加了再发送^_^;另外lcd12864屏也搞半天,主要照着时序图写必须sid先发送,然后sclk,不然不行 ̄□ ̄。
气体模块详细的通讯协议参考和上位机通讯那篇,此处不再介绍。
图中电路板没有焊AD芯片和温度芯片,引脚备用,相关代码注释掉了。
代码如下:#include "STC12C5A60S2.H"#include <intrins.H>.//头文件#define uchar unsigned char#define uint unsigned intuchar sendbuffer[17];//发送数据uchar flag=0;uchar re_buffer[32];uchar count=0;uint ad_data=0;double ad_vol=0;uint con_mid=0;uint gascon=0;long sum_o2=0;uint average_counter=20;uint idata oldtemp[21];uchar idata Send_Buff[20]; //moduleuint pre_contemp=0;uint O2_con=0;uchar idata test[21]; //moduleuint temperature=0;uint dat;uint testlcd=0;uchar c[]={0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9};//MAX1241 模数芯片引脚设置,此电路板我没焊^_^//sbit ADC_CS=P0^1;//sbit ADC_CLK=P0^0;//sbit ADC_DOUT=P0^2;sbit ADC_CS=P3^6;sbit ADC_CLK=P3^5;sbit ADC_DOUT=P3^7;//LCD12864 LCD屏幕引脚设置sbit cs=P2^2;sbit sid=P2^1;sbit sclk=P2^0;sbit DQ=P0^1; //DS18B20 温度引脚设置,依然没有…#define N 11#define N2 20void delayms(unsigned char t){unsigned char i;unsigned char j;for(j=t;j;j--)for(i=192;i;i--);/*1ms延时*/}void delayus(uint t){uint i;for(i=0;i<t;i++){_nop_();}}void delay(float sec){unsigned int i;unsigned int j;j=sec*100;while(j--){i=1561;while(--i);}}void UART1_init() //串口1初始化,此串口和电脑通讯{TMOD=0x20;/设置定时器工作方式2TH1=0xfd; //波特率9600TL1=0xfd;TR1=1;REN=1;SM0=0;SM1=1;//ES=1;}void UART2_init()//串口2初始化,和模块通讯 Modbus{S2CON= 0x50; //方式1,允许接收BRT = 0xf4; //波特率2400AUXR = AUXR |0X10; //允许独立波特率允许AUXR1 = AUXR1&0xef; //将uart2切换到P1口IE2 = IE2|0X01; //允许串口2中断}void UART1_Send (unsigned char UART_data)//{//ES=0;SBUF = UART_data; //将接收的数据发送回去while(TI!=1); //检查发送中断标志位TI = 0; //另发送中断标志位为0//ES=1;}void UART2_Send(unsigned char UART_data)//串口2发送{//ES = 0 ;S2BUF = UART_data;while((S2CON&0x02)!=0x02);S2CON &= ~0x02;//ES = 1 ;}void UART1_Send_String (char *str, char len)//串口1发送字符串{unsigned char i;for(i=0;i<=len;i++){UART1_Send(str[i]);}}void UART2_Send_String (char *str, char len) //串口2发送字符串{unsigned char i;for(i=0;i<=len;i++){UART2_Send(str[i]);}}unsigned char Creat_Addr(unsigned char adr, unsigned char position)//计算模块地址{unsigned char hich;unsigned char loch;hich = adr/16;loch = adr%16;if(hich>9)hich+=7;if(loch>9)loch+=7;if(position == 1){return hich+0x30;}else if(position == 0){return loch+0x30;}}unsigned char CheckSum(unsigned char *str, unsigned char position, uchar len)/计算校验码{uchar i;unsigned int sum=0;uchar hi, lo;//uchar len = 12;for(i = 1; i <= len; i ++){*str ++;sum += *str;}sum = 256-(sum%256);hi = sum/16;lo = sum%16;if(hi > 9)hi += 7;if(lo > 9)lo += 7;hi += 0x30;lo += 0x30;if(sum == 256)hi = lo = 0x30;if(position == 1){return hi;}else if(position == 0){return lo;}}void data_init(){sendbuffer[0]=0x5B;sendbuffer[1]=0x30;sendbuffer[2]=0x30;sendbuffer[3]=0x30;sendbuffer[4]=0x30;sendbuffer[5]=0x30;sendbuffer[6]=0x7C;sendbuffer[7]=0x30;sendbuffer[8]=0x30;sendbuffer[9]=0x30;sendbuffer[10]=0x30;sendbuffer[11]=0x30;//TEMsendbuffer[12]=0x30;sendbuffer[13]=0x30;sendbuffer[14]=0x5D;sendbuffer[15]=0x0D;sendbuffer[16]=0x0A;}void calculate_module(unsigned char str[])//lcd屏显示{unsigned int concen;uchar wan,qian,bai,shi,ge;/*uchar d4 = str[7]-48;uchar d3 = str[8]-48;uchar d2 = str[9]-48;uchar d1 = str[10]-48;*/ //浓度只需要后面部分 uchar d4 = str[24]-48;uchar d3 = str[25]-48;uchar d2 = str[26]-48;uchar d1 = str[27]-48;if(d4>9)d4-=7;if(d3>9)d3-=7;if(d2>9)d2-=7;if(d1>9)d1-=7;concen = d4*4096+d3*256+d2*16+d1;gascon=concen;wan=concen/10000;qian=concen%10000/1000;bai=concen%1000/100;shi=concen%100/10;ge=concen%10;//sendbuffer[6]=wan+0x30; //最终显示XXX.X%sendbuffer[7]=qian+0x30;sendbuffer[8]=bai+0x30;sendbuffer[9]=shi+0x30;sendbuffer[10]=ge+0x30;}void module_init()//气体模块初始化{Send_Buff[0] = ':';Send_Buff[3] = '0';Send_Buff[4] = '3';Send_Buff[5] = '0';Send_Buff[6] = '0';Send_Buff[7] = '0';Send_Buff[8] = 'A';Send_Buff[9] = '0';Send_Buff[10] = '0';Send_Buff[11] = '0';Send_Buff[12] = '1';Send_Buff[1] = Creat_Addr(31, 1);Send_Buff[2] = Creat_Addr(31, 0);Send_Buff[13] = CheckSum(Send_Buff, 1, 12);Send_Buff[14] = CheckSum(Send_Buff, 0, 12);Send_Buff[15] = 0x0D;Send_Buff[16] = 0x0A;//手动添加校验,例如字符‘0’换成ASCII码0X30,添加偶校验后就是0X3A,最终发送给模块的以下数据,地址被写死,这个不像C#做的一目了然O(∩_∩)Otest[0]=0X3A;test[1]=0XB1;test[2]=0XC6;test[3]=0X30;test[4]=0X33;test[5]=0X30;test[6]=0X30;test[7]=0X30;test[8]=0X41;test[9]=0X30;test[10]=0X30;test[11]=0X30;test[12]=0XB1;test[13]=0X39;test[14]=0XB4;test[15]=0X8D;test[16]=0X0A;}/*uint read_max1241() AD芯片处理{uint ADC_Data;uchar i;ADC_CLK=0;ADC_CS=0;ADC_Data=0;while(!ADC_DOUT);ADC_CLK=1;ADC_CLK=0;for(i=0;i<12;i++){ADC_CLK=1;ADC_Data<<=1;ADC_Data |= ADC_DOUT;ADC_CLK=0;}ADC_CS=1;ADC_CLK=0;return ADC_Data;}unsigned int ad_filter()//滤波{unsigned int count1,i,j;unsigned int value_buf[N];unsigned int temp;unsigned int sum=0;for (count1=0;count1<N;count1++){value_buf[count1] =read_max1241(); delayms(20);}for (j=0;j<(N-1);j++){for (i=0;i<(N-j);i++){if ( value_buf[i]>value_buf[i+1] ) {temp = value_buf[i];value_buf[i] = value_buf[i+1]; value_buf[i+1] = temp;}}}for(count1=3;count1<(N-3);count1++){sum += value_buf[count1];}return (unsigned int)(sum/(N-6));}void calculate_o2()//测试用{uchar wan,qian,bai,shi,ge;uint con_fin=0;uchar i;ad_data=ad_filter();ad_vol=(ad_data/4096.0)*2500.0;//Voltage ad_data=(uint)ad_vol; //concentrationdelayms(2);if(average_counter>0){sum_o2+= ad_data;oldtemp[average_counter-1]=ad_data;average_counter--;}else{sum_o2 -=oldtemp[19];for(i=20;i>0;i--){oldtemp[i]=oldtemp[i-1];}oldtemp[0]=ad_data;sum_o2+=oldtemp[0];con_fin=(uint)(sum_o2/N2);//O2_con=0.8*con_fin+0.2*pre_contemp;//pre_contemp=O2_con;wan=con_fin/10000;qian=con_fin%10000/1000;bai=con_fin%1000/100;shi=con_fin%100/10;ge=con_fin%10;sendbuffer[1]=wan+0x30;sendbuffer[2]=qian+0x30;sendbuffer[3]=bai+0x30;sendbuffer[4]=shi+0x30;sendbuffer[5]=ge+0x30;delayms(5);}}//-------------18B20 温度显示复位函数--------------- void ow_reset(void){char presence=1;while(presence){while(presence){DQ=1;delayus(2);DQ=0; //delayus(550); // 550usDQ=1; //delayus(66); // 66uspresence=DQ;}delayus(500); //延时500uspresence = ~DQ;}DQ=1;}//-----------18B20写命令函数------------void write_byte(uchar val){uchar i;for (i=8; i>0; i--) //{DQ=1;delayus(2);DQ = 0;delayus(5);//5usDQ = val&0x01;delayus(66); //66usval=val/2;}DQ = 1;delayus(11);}//--------------18B20读一个字节函数---------uchar read_byte(void){uchar i;uchar value = 0;for (i=8;i>0;i--){DQ=1;delayus(2);value>>=1;DQ = 0;delayus(4); //4usDQ = 1;delayus(4); //4usif(DQ)value|=0x80;delayus(66); //66us}DQ=1;return(value);}void Read_Temperature(void){unsigned int Temp1,Temp2;uchar bai,shi,ge;ow_reset(); //DS18B20write_byte(0xCC);write_byte(0x44);ow_reset(); //DS1302复位write_byte(0xCC);write_byte(0xbe);Temp1=read_byte();Temp2=read_byte();ow_reset();temperature=(((Temp2<<8)|Temp1)*0.625); //0.0625=xx, 0.625=xx.x, 6.25=xx.xxbai=temperature/100;shi=temperature%100/10;ge=temperature%10;sendbuffer[11]=bai+0x30;sendbuffer[12]=shi+0x30;sendbuffer[13]=ge+0x30;delayms(5);}* //图中电路板此部分没焊,此部分代码不使用^_^void writecmd_lcd(uchar cmd) //lcd屏写指令{uchar i;uchar cmd1;cmd1=cmd;//----------先写控制,选择写指令,还是写数据11111000 for(i=0;i<5;i++) //必须sid先发送,然后sclk,不然不行 {sid=1;sclk=1;sclk=0;}for(i=0;i<3;i++){sid=0;sclk=1;sclk=0;}//delayms(10);cmd=cmd&0xf0; //先高4位for(i=0;i<8;i++){if(cmd&0x80){sid=1;}else sid=0;sclk=1;sclk=0;cmd=cmd<<1;}//delayms(10);cmd1=((cmd1<<4)&0xf0); //低4位 for(i=0;i<8;i++){if(cmd1&0x80){sid=1;}else sid=0;sclk=1;sclk=0;cmd1=cmd1<<1;}}void writedata_lcd(uchar dat) {uchar i;uchar dat1;dat1=dat;//11111010for(i=0;i<5;i++){sid=1;sclk=1;sclk=0;}sid=0;sclk=1;sclk=0;sid=1;sclk=1;sclk=0;sid=0;sclk=1;sclk=0;//delayms(10);dat=dat&0xf0;for(i=0;i<8;i++)if(dat&0x80){sid=1;}else sid=0;sclk=1;sclk=0;dat=dat<<1;}//delayms(10);dat1=((dat1<<4)&0xf0);for(i=0;i<8;i++){if(dat1&0x80){sid=1;}else sid=0;sclk=1;sclk=0;dat1=dat1<<1;}}void init_lcd()//初始化lcd屏{cs=1;writecmd_lcd(0x30);//设定为8位控制writecmd_lcd(0x0c);//显示打开writecmd_lcd(0x01);//清屏}void gotoxy(uint row, uint col){switch(row){case 1: writecmd_lcd(0x80+col);break; case 2: writecmd_lcd(0x90+col);break; case 3: writecmd_lcd(0x88+col);break; case 4: writecmd_lcd(0x98+col);break;}void clear(){writecmd_lcd(0x01);delayms(10);}void SendStr(uchar *str){uchar i;for(i=0;str[i]!='\0';i++){writedata_lcd(str[i]);}}void lcd_display(uint lcddata)//lcd屏显示浓度{uchar wan,qian,bai,shi,ge;wan=lcddata/10000;qian=lcddata%10000/1000;bai=lcddata%1000/100;shi=lcddata%100/10;ge=lcddata%10;gotoxy(2,1);writedata_lcd(0xa3);writedata_lcd(c[qian]);gotoxy(2,2);writedata_lcd(0xa3);writedata_lcd(c[bai]);gotoxy(2,3);writedata_lcd(0xa3);writedata_lcd(c[shi]);gotoxy(2,4);SendStr(".");gotoxy(2,5);writedata_lcd(0xa3);writedata_lcd(c[ge]);gotoxy(2,6);SendStr("%");}void main()//主函数{delay(2.1);UART1_init();UART2_init();data_init();module_init();init_lcd();EA=1;delay(2.1);while(1){/*calculate_o2();//测试用Read_Temperature();*/ //温度芯片没焊UART2_Send_String(test,16);//串口2:和模块通讯delay(0.8);if(flag==1){calculate_module(re_buffer);delayms(5);UART1_Send_String(sendbuffer,16);//串口1:电脑上可以接收发送的数据 flag=0;delay(0.8);}lcd_display(gascon);//lcd显示浓度delay(0.8);}}/*void uart1_in() interrupt 4/串口1中断,不使用,因为只是发送{RI=0;re_buffer[count]=SBUF;if(re_buffer[0]!=':'){count=0;}else{count++;if(count==10){flag=1;count=0;}}}*/void uart2_in() interrupt 8//串口2中断,需要接受模块返回的数据{if(S2CON&0X01){re_buffer[count]=S2BUF;re_buffer[count]&=0x7f;count++;S2CON&=0XFE;}if(count==32){count=0;flag=1;}}。
modbus协议--51端程序的实现
modbus协议--51端程序的实现文章发表于:2008-08-19 14:46RTU需要一个定时器来判断3.5个流逝时间。
#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来执行帧处理。
Modbus协议下实现单片机与PLC之间的通讯 - 单片机
Modbus协议下实现单片机与PLC之间的通讯 - 单片机1引言 hmi(人机界面)以其体积小,高性能,强实时等特点,越来越多的应用于工业自动化系统和设备中。
它有字母、汉字、图形和图片等不同的显示,界面简单友好。
配有长寿命的薄膜按钮键盘,操作简单。
它一般采用具有集成度高、速度快、高可靠且价格低等优点的单片机[1]作为其核心控制器,以实现实时快速处理。
plc和单片机结合不仅可以提PLC的数据处理能力,还可以给用户带来友好简洁的界面。
本文以Modbus通讯协议为例,详细讨论了一个人机系统中,如何用C51实现单片机和PLC之间通讯的实例。
2Modbus通讯协议 Modbus协议是应用于电子控制器上的一种通用语言。
通过此协议,控制器相互之间、控制器经由网络和其它设备之间可以通信。
Modbus协议提供了主—从原则,即仅一设备(主设备)能初始化传输(查询)。
其它设备(从设备)根据主设备查询提供的数据作出相应反应。
主设备查询的格式:设备地址(或广播,此时不需要回应)、功能代码、所有要发送的数据、和一错误检测域。
从设备回应消息包括确认地址、功能码、任何要返回的数据、和一错误检测域。
如果在消息接收过程中发生一错误,或从设备不能执行其命令,从设备将建立一错误消息并把它作为回应发送出去。
控制器能设置为两种传输模式:ASCII 和RTU,在同样的波特率下,RTU可比ASCII方式传送更多的数据,所以采用KTU模式。
(1)典型的RTU消息帧典型的RTU消息帧如表1所示。
RTU消息帧的地址域包含8bit。
可能的从设备地址是0...127(十进制)。
其中地址0是用作广播地址,以使所有的从设备都能认识。
主设备通过将要联络的从设备的地址放入消息中的地址域来选通从设备。
当从设备发送回应消息时,它把自己的地址放入回应的地址域中,以便主设备知道是哪一个设备作出回应。
RTU消息帧中的功能代码域包含了8bits,当消息从主设备发往从设备时,功能代码域将告之从设备需要执行哪些行为;当从设备回应时,它使用功能代码域来指示是正常回应(无误)还是有某种错误发生(称作异议回应,一般是将功能码的最高位由0改为1)。
modbus协议及modbusRTU的C51单片机程序
modbus协议及modbusRTU的C51单片机程序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协议的可靠性较好。
基于51单片机实现modbus协议通信单片机原理与应用课程设计论文
Ⅰ、课程设计(报告)题目:基于51单片机实现modbus协议通信Ⅱ、课程设计(论文)工作内容一、课程设计目标1、培养综合运用知识和独立开展实践创新的能力;2、通过编写实现modbus协议的程序,学生不但能够更加熟悉modbus协议,而且能够增强编程能力,为以后走上工作岗位奠定基础。
二、研究方法及手段应用1、上网查阅有关MODBUS协议的资料,分层次阅读协议;2、确定系统设计结构和编写方案;3、将功能分解为各个函数,分别编写每个函数。
三、课程设计预期效果1、给开发板上电;2、运行mbpoll调试软件:选择01功能实现读线圈,选择03功能实现读寄存器,05功能实现写单个线圈,06功能实现写单个寄存器,15功能实现写多个线圈,16功能实现写多个寄存器。
摘要目前,工业现场总线使用modbus协议已经非常普遍。
本次课程设计,使用开发板实现modbus协议通信,模拟工业现场。
通过本次试验,我们进一步知道了编程能力的重要性,更好地学习如何使用C语言编写单片机程序。
首先,我先通过网上查资料了解modbus协议的内容,分层次理解协议。
先大体确定整个程序的结构,然后分层次编写,然后将程序烧到单片机上,来实现功能。
老师让我们基于51单片机实现modbus协议通讯的目的就是让同学们在理论学习的基础上,通过对工业现场总线使用的一种协议的理解进而编程实现,使学生不但能够将课堂上学到的理论知识与实际应用结合起来,而且能够对分析、解决实际工业上的通信问题进一步加深认识,为今后能够独立进行实现通信协议程序的编写工作打下一定的基础。
本次课程设计的主要问题在于将modbus协议用C语言解读出来。
在程序开始设计之前,必须反复推敲设计方案并设计每个函数的方框图。
如果一切都设计好之后,底层协议的编写就变得容易得多。
【关键词】modbus协议函数 51单片机程序设计第一章系统设计第一节课题目标及总体方案一、课题目标实现Modbus协议部分内容用ASCII模式完成以上功能是本设计的基本要求。
MODBUS rtu 51单片机程序
/*=========================*/
void AV_in(void)
{
uchar i;
uint result;
for(i=0;i<=3;i++){ad_1[i]=ad_1[i+1];}
ad_1[4]=TLC2543(0);
jd_off(0xff);
P0=0xFF;
while(1)
{
WDT_CONTR = 0x36;
addr=P0; //取地址
AV_in();
if(command_ok==1)
{XJ=0;command();}
extern uint CRC16(uchar *pushMsg,unsigned short usDataLen);//modbus crc 函数
extern uchar *pushMsg; /*要进行CRC校验的消息*/
uchar idata recebuf[16]=0; //接收数据缓冲区
for(i=0;i<4;i++){din=0;clk=1;clk=0;} //补4个时钟
cs=1;delay(1);cs=0; //等待转换
ad_reg=prot; //取通道号
ad_reg=ad_reg<<4;
sbit cs=P3^5;
sbit clk=P3^2;
sbit din=P3^3;
sbit dout=P3^4;
uint code r[8]={128,64,32,16,8,4,2,1};
/*====延时函数=====*/
modbus RTU 的 C51 单片机 程序
modbus RTU 的C51 单片机程序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, 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; //用于测试位地址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,&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){ //设为ONtempData = 1;}//else if(onOff == 0x0000)else if(onOff == 0x00){ //设为OFFtempData = 0;}setCoilVal(tempAddr,tempData);for(i=0;i<receCount;i++){sendBuf = receBuf;}sendCount = receCount;beginSend();}//void forceSingleCoil(void)//设置多个存放器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){//presetSingleRegister();}}}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: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: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)。
Modbus协议51单片机C语言实现
#include <REGX52.H>#include<intrins.h>//_nop_();#define OSFREQ 11059200char TimeInterval;char MyAddress;char data DI[6]={1,2,3,4,5,6};char data DO[6]={6,7,8,9,10,11};char data AI[6]={11,12,13,14,15,16};char data AO[6]={16,17,18,19,20,21};unsigned char data ReceiveData[14];unsigned char data countnumber;sbit Recenable =P1^6;//控制端sbit led =P2^7;//控制端/*---------------------------------------------------------------------------函数说明:CRC 高位字节值表---------------------------------------------------------------------------*/ static unsigned char 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低位字节值表---------------------------------------------------------------------------*/ static unsigned char 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};/*---------------------------------------------------------------------------调用方式:unsigned int CRC16(unsigned char *puchMsg, unsigned int usDataLen) 函数说明:CRC校验---------------------------------------------------------------------------*/unsigned int CRC16(unsigned char *puchMsg, unsigned int usDataLen){unsigned char uchCRCHi = 0xFF ; // 高CRC字节初始化unsigned char uchCRCLo = 0xFF ; // 低CRC 字节初始化unsigned uIndex ; // CRC循环中的索引while (usDataLen--) // 传输消息缓冲区{uIndex = uchCRCHi ^ *puchMsg++ ; // 计算CRCuchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;uchCRCLo = auchCRCLo[uIndex] ;}return (uchCRCHi << 8 | uchCRCLo) ;}/*---------------------------------------------------------------------------调用方式:unsigned char getbit(unsigned int address,unsigned char function)函数说明:取出所给地址的位值---------------------------------------------------------------------------*/unsigned char getbit(unsigned int address,unsigned char function){unsigned char data Offset;unsigned char data temp;unsigned char data *Myaddress;Offset=(char)address&0x07;switch (function) //根据功能码不同进行相应处理{case 2:Myaddress=DI;break;case 1:Myaddress=DO;break;default:return 2;}temp=(char)(address>>3);temp=*(Myaddress+temp);temp>>=(Offset);if (temp&0x01)return 1;elsereturn 0;}/*---------------------------------------------------------------------------调用方式:void SendData(unsigned char *output,unsigned char Outlength) 函数说明:发送数据至窗口---------------------------------------------------------------------------*/void SendData(unsigned char *output,unsigned char Outlength){ES=0;while(Outlength--){TI=0;ACC=*output;TB8=P;SBUF=*(output++);while(!TI);TI=0;}ES=1;}/*---------------------------------------------------------------------------调用方式:void Function12(unsigned char address,unsigned char len)函数说明:功能码1,2处理---------------------------------------------------------------------------*/void Function12(unsigned char address,unsigned char len){unsigned int data i;unsigned char data j;unsigned char data length;unsigned char data *send;unsigned int data temp;unsigned char data function;length=0;send=ReceiveData;function=send[1];for (i=1;i<=len;i++){length++;*(send+2+length)=0;for (j=0;j<8;j++){*(send+2+length)=*(send+2+length)|getbit(address,function)<<j; address++;i++;if(i>len)break;}i--;}*(send+2)=length; //数据长度temp=CRC16(send,length+3); //DI状态数据*(send+3+length)=(char)(temp>>8); //CRC校验高*(send+4+length)=(char)temp; //CRC校验低SendData(send,length+5); //调用A发送程序}/*---------------------------------------------------------------------------调用方式:void Function3(unsigned char address,unsigned char len) 函数说明:功能码3处理-----读取寄存器---------------------------------------------------------------------------*/void Function3(unsigned char address,unsigned char len){unsigned char data i;unsigned char data *send;unsigned int data temp;send=ReceiveData;*(send+2)=2*len; //数据长度高address=2*address;for(i=0;i<len;i++) //取输入寄存器数据{*(send+3+2*i)=AO[address++];*(send+4+2*i)=AO[address++];}temp=CRC16(send,2*len+3); //CRC校验*(send+3+2*len)=(char)(temp>>8);*(send+4+2*len)=(char)temp;SendData(send,2*len+5); //调用A发送程序}/*---------------------------------------------------------------------------调用方式:void Function4(unsigned char address,unsigned char len) 函数说明:功能码处理4----读取输入寄存器---------------------------------------------------------------------------*/void Function4(unsigned char address,unsigned char len){unsigned char data i;unsigned char data *send;unsigned int data temp;send=ReceiveData;*(send+2)=2*len;address=2*address;for(i=0;i<len;i++) //取数据{*(send+4+2*i)=AI[address++];*(send+3+2*i)=AI[address++];}temp=CRC16(send,2*len+3);*(send+3+2*len)=(char)(temp>>8);*(send+4+2*len)=(char)temp;SendData(send,2*len+5);}/*---------------------------------------------------------------------------调用方式:void Function6(unsigned char address)函数说明:写单路寄存器---------------------------------------------------------------------------*/void Function6(unsigned char address){int temp;unsigned char data *WriteData;temp=2*address;WriteData=AO;//将写入的数据进行处理//your code to add here to deal with the write value*(WriteData+temp)=ReceiveData[4];*(WriteData+temp+1)=ReceiveData[5];SendData(ReceiveData,countnumber); //调用发送程序,返回与主机相同的报文}/*---------------------------------------------------------------------------调用方式:void SendError(char ErrorCode)函数说明:---------------------------------------------------------------------------*/void SendError(char ErrorCode){unsigned char data *send;unsigned int data temp;send=ReceiveData;*(send+1)=ReceiveData[1]|0x01;//最高位置1*(send+2)=ErrorCode;temp=CRC16(send,3);*(send+3)=(char)(temp>>8);*(send+4)=(char)temp;SendData(send,countnumber); //调用发送程序,返回错误代码}/*---------------------------------------------------------------------------调用方式:void Deal()函数说明:接收数据处理---------------------------------------------------------------------------*/void Deal(){unsigned int data temp;unsigned char data address;unsigned char data length;unsigned char data counter;unsigned char data *Pointer;Pointer=ReceiveData;counter=countnumber;if (counter<=3 ) return;temp=Pointer[counter-2]<<8;temp= temp|Pointer[counter-1];if( temp==CRC16(Pointer,counter-2)) //较验正确{address=Pointer[3];length=Pointer[5];if(address<48){switch (Pointer[1]) //根据功能码不同进行相应处理*/{case 1:Function12(address,length);break;case 2:Function12(address,length);break;case 3:address=address-1;Function3(address,length);break;case 4:address=address-1;Function4(address,length);break;case 6:address=address-1;Function6(address);default:SendError(0x81);break;}}else{ SendError(0x82);}}countnumber=0;}/*bit Chargetbit(unsigned char character,unsigned char Number){if(character>>Number&0x01==1)return 1;elsereturn 0;}//设定某一位的值*********************************************************** unsigned char SetBit(unsigned char Character,unsigned char num,bit boolen){unsigned char code bit_value[]={1,2,4,8,16,32,64,128};if(boolen)return Character|bit_value[num];elsereturn Character&~bit_value[num];}*//*---------------------------------------------------------------------------调用方式:void Init_timer2(unsigned int baudrate)函数说明:定时器2的初始化---------------------------------------------------------------------------*/void Init_timer2(unsigned int baudrate){unsigned int data TimReg2;T2CON = 0x00;T2MOD = 0x00;TimReg2=65536-(OSFREQ/384/baudrate*11);RCAP2L=TimReg2&0x00ff;RCAP2H=(TimReg2>8); //RCAP2H=TimReg2/256RCAP2L=0;RCAP2H=0;TL2 = RCAP2L; //TH2 = RCAP2H; //TR2=1; // T2CON.2 start timerET2=0; // 开定时器2中断}/*---------------------------------------------------------------------------调用方式:void Init_SerialPort(unsigned int baudrate)函数说明:串口初始化---------------------------------------------------------------------------*/void Init_SerialPort(unsigned int baudrate){unsigned char TimReg2;SCON = 0xd0; // 方式3, 8-bit UART, enable rcvrTMOD = 0x21; // T0方式1(16位), T1方式2,8位重装TimReg2=256-(OSFREQ/384/baudrate);TH1 = TimReg2; // 定时器1,在11.0592MHz下,波特率为9600 TL1 = TH1;TR1= 1; // TR1: 定时开始//PS=1; // 串行中断优先ES=1; // 接收中断}/*---------------------------------------------------------------------------函数说明:主函数---------------------------------------------------------------------------*/main(){EA=1; //开总中断Init_SerialPort(9600);Init_timer2(9600);Recenable=0; //接收允许MyAddress=0x01; //本机地址while(1){}}/*---------------------------------------------------------------------------调用方式:void SeiralA() interrupt 4 using 0函数说明:通讯中断---------------------------------------------------------------------------*/void SeiralA() interrupt 4 using 0{if(RI){if((TimeInterval>2)&&(TimeInterval<=4)){ countnumber=0;}if(countnumber>60){ countnumber=0;}ReceiveData[countnumber++]=SBUF; //将接收到的数据到缓冲区内TimeInterval=0;RI=0;ET2=1; //当接收到数据后,将定时器2开启}}/*---------------------------------------------------------------------------调用方式:void SerialPortTime() interrupt 5 using 0函数说明:定时器2中断程序---------------------------------------------------------------------------*/void SerialPortTime() interrupt 5 using 0{ET2=0; //定时器2中断允许控制TimeInterval=TimeInterval+1;if((TimeInterval>4) && countnumber){Recenable=1; //发送Deal(); //将接收到的数据进行处理Recenable=0; //接收countnumber=0;}else{ ET2=1;} //将定时器2打开TF2=0; //定时器2溢出标志,软件清0}。
51单片机实现Modbus从机程序
自己用单片机做的Modbus从机,可以使用STC89C52。
实现了命令码为1、2、3、4、5、6的功能,程序中有些是我们部分其他功能的函数和数据,希望大家参考下编程的思想.uint Switch=0xbc95;//开关状态uchar bdata Coil1=0xff,Coil2=0xbc;//16位线圈状态sbit Coil1_bit0=Coil1^0;sbit Coil1_bit1=Coil1^1;sbit Coil1_bit2=Coil1^2;sbit Coil1_bit3=Coil1^3;sbit Coil1_bit4=Coil1^4;sbit Coil1_bit5=Coil1^5;sbit Coil1_bit6=Coil1^6;sbit Coil1_bit7=Coil1^7;sbit Coil2_bit8=Coil2^0;sbit Coil2_bit9=Coil2^1;sbit Coil2_bit10=Coil2^2;sbit Coil2_bit11=Coil2^3;sbit Coil2_bit12=Coil2^4;sbit Coil2_bit13=Coil2^5;sbit Coil2_bit14=Coil2^6;sbit Coil2_bit15=Coil2^7;uint idata ReOnlybuf[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f};uint idata ReWrbuf[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f};//CRC校验查表码值const uchar 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};const uchar 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};/********************************************Function:CRC校验子函数Input:要校验的数组起始地址长度Output:16位校验码(高位在前)********************************************/uint crccheck(uchar *puchMsg, uchar usDataLen){uchar uchCRCHi = 0xFF ;uchar uchCRCLo = 0xFF ;uchar uIndex ;while (usDataLen--){uIndex = uchCRCHi ^ *puchMsg++ ;uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex];uchCRCLo = auchCRCLo[uIndex];}return (uchCRCHi 〈〈8 |uchCRCLo);}/******************************************** Function:读线圈状态子函数Input:无Output:无********************************************/void ReadCoil(void){uint StartAddress,tempAddress;uchar CoilNum,i,ByteNum,j;uchar CoilVal;bit exit=0;StartAddress=resvbuf[2];StartAddress=StartAddress|resvbuf[3];tempAddress=StartAddress;CoilNum=resvbuf[5];//读取的位数ByteNum=CoilNum/8;if(CoilNum%8!=0)ByteNum++;Sendbuf[2]=ByteNum;//返回的字节数if(resvbuf[1]==0x01){for(i=0;i〈ByteNum;i++){Sendbuf[i+3]=0;for(j=0;j〈8;j++){CoilVal=GetCoilVal(StartAddress);Sendbuf[i+3]|=CoilVal<〈j;StartAddress++;if(StartAddress〉=tempAddress+CoilNum){exit=1;break;}}if(exit==1)break;}}else if(resvbuf[1]==0x02){for(i=0;i<ByteNum;i++){Sendbuf[i+3]=0;for(j=0;j〈8;j++){CoilVal=GetSWVal(StartAddress);Sendbuf[i+3]|=CoilVal<<j;StartAddress++;if(StartAddress>=tempAddress+CoilNum){exit=1;break;}}if(exit==1)break;}}SendCount=5+ByteNum;SendData();}/********************************************Function:读寄存器状态子函数Input:无Output:无********************************************/void ReadRegisters(void){uint StartAddress;uchar ByteCount,i;StartAddress=resvbuf[2];StartAddress=StartAddress〈〈8|resvbuf[3];ByteCount=resvbuf[5]*2;Sendbuf[2]=resvbuf[5];if(resvbuf[1]==0x03)for(i=0;i<ByteCount;i+=2){Sendbuf[i+4]=ReWrbuf[StartAddress]&0xff;Sendbuf[i+3]=ReWrbuf[StartAddress]>〉8;StartAddress++;}else if(resvbuf[1]==0x04)for(i=0;i〈ByteCount;i+=2){Sendbuf[i+4]=ReOnlybuf[StartAddress];Sendbuf[i+3]=ReOnlybuf[StartAddress];StartAddress++;}SendCount=ByteCount+5;SendData();}/********************************************Function:强制单线圈子函数Input:无Output:无********************************************/ void ForceSingalCoil(void){uint Address,OnOff;bit temp,CoilVal;Address=resvbuf[2]〈<8|resvbuf[3];OnOff=resvbuf[4]<<8|resvbuf[5];if(OnOff==0x0000){CoilVal=0;temp=SetCoilVal(Address,CoilVal);}else if(OnOff==0xFF00){CoilVal=1;temp=SetCoilVal(Address,CoilVal);}if(temp==1){Sendbuf[2]=resvbuf[2];Sendbuf[3]=resvbuf[3];Sendbuf[4]=resvbuf[4];Sendbuf[5]=resvbuf[5];SendCount=8;SendData();}elseError=1;}/********************************************Function:强制单寄存器子函数Input:无Output:无********************************************/void SetOneRegisterVal(){uint R_Address,RegisterVal;bit temp1;R_Address=resvbuf[2];R_Address=R_Address<〈8|resvbuf[3];RegisterVal=resvbuf[4];RegisterVal=RegisterVal<<8|resvbuf[5];temp1=SetRegisterVal(R_Address,RegisterVal);if(temp1==1){Sendbuf[2]=resvbuf[2];Sendbuf[3]=resvbuf[3];Sendbuf[4]=resvbuf[4];Sendbuf[5]=resvbuf[5];SendCount=8;SendData();}elseError=1;}/********************************************Function:读线圈值子函数Input:线圈地址Output:线圈的值********************************************/uchar GetCoilVal(uint Address){uint CoilAddress;uchar CoilVal=0;CoilAddress=Address;switch(CoilAddress&0x0f){case 0:CoilVal=Coil1_bit0;break;case 1:CoilVal=Coil1_bit1;break;case 2: CoilVal=Coil1_bit2;break;case 3:CoilVal=Coil1_bit3;break;case 4: CoilVal=Coil1_bit4;break;case 5: CoilVal=Coil1_bit5;break;case 6:CoilVal=Coil1_bit6;break;case 7: CoilVal=Coil1_bit7;break;case 8:CoilVal=Coil2_bit8;break;case 9: CoilVal=Coil2_bit9;break;case 10: CoilVal=Coil2_bit10;break;case 11: CoilVal=Coil2_bit11;break;case 12: CoilVal=Coil2_bit12;break;case 13: CoilVal=Coil2_bit13;break;case 14: CoilVal=Coil2_bit14;break;case 15: CoilVal=Coil2_bit15;break;default:break;}return CoilVal;}/********************************************Function:读开关值子函数Input:开关地址Output:开关值********************************************/uchar GetSWVal(uint Address1){uchar CoilVal;uchar SW1,SW2=0xff;SW1=P2;Switch=SW2〈〈8|SW1;switch(Address1&0x0f){case 0: CoilVal=Switch&0x01;break;case 1:CoilVal=Switch>>1&0x01;break;case 2:CoilVal=Switch>〉2&0x01;break;case 3:CoilVal=Switch>>3&0x01;break;case 4:CoilVal=Switch〉〉4&0x01;break;case 5: CoilVal=Switch>〉5&0x01;break;case 6: CoilVal=Switch>>6&0x01;break;case 7:CoilVal=Switch〉>7&0x01;break;case 8:CoilVal=Switch>〉8&0x01;break;case 9:CoilVal=Switch〉>9&0x01;break;case 10: CoilVal=Switch>>10&0x01;break;case 11: CoilVal=Switch〉〉11&0x01;break;case 12: CoilVal=Switch>〉12&0x01;break;case 13: CoilVal=Switch〉>13&0x01;break;case 14: CoilVal=Switch>〉14&0x01;break;case 15: CoilVal=Switch〉〉15&0x01;break;default: break;}return CoilVal;}/********************************************Function:强制单线圈值子函数Input:线圈地址,强制值Output:结果值********************************************/bit SetCoilVal(uint Address,bit Val){bit result=1;switch(Address&0x0f){case 0: Coil1_bit0=Val;break;case 1: Coil1_bit1=Val;break;case 2:Coil1_bit2=Val;break;case 3: Coil1_bit3=Val;break;case 4: Coil1_bit4=Val;break;case 5:Coil1_bit5=Val;break;case 6: Coil1_bit6=Val;break;case 7:Coil1_bit7=Val;break;case 8:Coil2_bit8=Val;break;case 9: Coil2_bit9=Val;break;case 10: Coil2_bit10=Val;break;case 11: Coil2_bit11=Val;break;case 12: Coil2_bit12=Val;break;case 13: Coil2_bit13=Val;break;case 14: Coil2_bit14=Val;break;case 15: Coil2_bit15=Val;break;default: result=0;break;}P1=Coil1;return result;}/********************************************Function:设置单寄存器值子函数Input:寄存器地址,值Output:结果值********************************************/bit SetRegisterVal(uint Address1,uint Val1){uchar Addr;bit result1=1;Addr=Address1&0x0f;if(Addr<16)ReWrbuf[Addr]=Val1;elseresult1=0;return result1; }。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 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。