MODBUS协议 51端源程序
基于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协议的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协议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}。
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来执行帧处理。
modbus51
单片机2052 11.059MHZ
上位机,通信波特率 9600 8位数据 2位停止位 偶校验 485通位接口
单片机, 9600 9 1
单片机控制板地址 localAddr(变量)
通信可设置数据的地址:
字地址 0 - 255 (只取16位的低8位)
fcode 为命令代码最高位置1
errorcode 具体的错误原因 1-未定义命令代码 2-数据量超界
P1.4 AD,对应过压故障(灯漏气) //P1M1 P1M0 决定是否AD
P1.3 镇流器故障
P1.2 灯未连接(灯丝断或灯连接故障)
P1.1 PFC故障(AD)
P1.0 6574故障(AD)
// 串行中断程序
void commIntProc() interrupt 4
{
// bWatchDog = ~ bWatchDog; //看门狗取反
if(TI)
{
TI = 0;
if(sendPosi < sendCount)
{
sendPosi++;
{
}
//计数器1中断
void counter1IntProc(void) interrupt 3 using 1
{
}
//定时处理
void timeProc(void)
{
uint8 i;
// bWatchDog = ~ bWatchDog; //看门狗取反
RD_buf= shift(); //数据保存在RD_buf
return(RD_buf);
}
易语言串口通讯modbus协议模块上位机必备例子源代码
易语言串口通讯modbus协议模块上位机必备例子源代码1.引言1.1 概述在编写易语言串口通讯modbus协议模块上位机必备例子源代码之前,我们首先需要了解一些基本概念和背景知识。
本文介绍了该例子的目的和结构,以及引言、正文和结论三个主要部分的内容。
1.1概述Modbus协议是一种常用的串行通信协议,广泛应用于工业自动化领域。
它被设计用于在不同设备之间进行数据传输和通信。
Modbus协议简洁明了,易于实现和部署,因此被许多工业设备和上位机所采用。
易语言是一种面向过程的编程语言,易于学习和使用。
它提供了丰富的库和模块,方便我们进行串口通讯编程。
易语言的特点是语法简单易懂,同时也支持调用其他语言编写的DLL函数,可以实现更加复杂的功能。
本例子的目标是演示如何使用易语言编写一个串口通讯的Modbus 协议模块,并结合上位机的必备功能来实现数据的读写和显示。
在正文部分,我们将介绍Modbus协议的简要概述,包括其通信方式、数据格式、功能码等。
同时,我们还将介绍易语言中的串口通讯模块及其基本用法。
在结论部分,我们将提供一些实例源代码示例,以便读者更好地理解和使用这个例子。
此外,我们还将列举一些上位机必备的功能,以供读者参考和扩展应用。
通过这个例子,读者可以学习到如何使用易语言进行串口通讯编程,并了解Modbus协议在实际应用中的运用。
同时,读者也可以根据自己的需求和实际情况,对例子进行二次开发和改进,以适应不同的应用场景。
在下一节中,我们将详细介绍Modbus协议的相关知识,以便读者更好地理解本例子的内容和实现。
文章结构部分主要是对整篇文章的组织和安排进行介绍,以下是1.2 文章结构的内容:1.2 文章结构本文主要分为三个部分,包括引言、正文和结论,具体如下:1. 引言部分介绍了本文的概述、文章结构和目的。
在概述中,我们对易语言串口通讯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校验也比较容易。
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来执行帧处理。
基于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通讯协议及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协议 51端源程序
modbus.c#i nclude "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 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 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;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[i] = receBuf[i];}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:break;case 15:break;case 16:*tempData = testRegister;break;default:break;}return result;}//uint16 getRegisterVal(uint16 addr,uint16 &data) //设置寄存器值返回0表示成功uint16 setRegisterVal(uint16 addr,uint16 tempData) {uint16 result = 0;uint16 tempAddr;tempAddr = addr & 0xfff;switch(tempAddr & 0xff){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;break;case 14:break;case 15:break;case 16:testRegister = tempData;break;default:break;}return result;}//uint8 setRegisterVal(uint16 addr,uint16 data)。
Modbus51程序
Modbus51程序#include#include#include#include/* These macro define simple data type宏定义简单的数据类型*/#define uchar unsigned char#define ulint unsigned long#define uint unsigned int/* These macro for Mudbus function code :mudbus功能码*/#define ReadCoilSta 0x01 // 0X read coil function 读线圈#define ForceSingleCoil 0x05 // 0X force single coil function 写线圈#define ReadHoldReg 0x03 // 4X read input register读寄存器#define PresetMulReg 0x06 // 4X write multi register写寄存器/* These macro describe index of communiction message from start to end */#define CommIndexSlaveID 0 // macro for slave ID 站号#define CommIndexFunction 1 // macro for function code宏的功能码#define CommIndexStartAdrHi 2 // macro for data hight adr 地址高位#define CommIndexStartAdrLo 3 // macro for data low adr 地址地位#define CommIndexNoPointHi 4 // macro for hight quantitydatas数据高位#define CommIndexNoPointLo 5 // macro for low quantity datas 数据低位/* These macro describe recieve message lenght of different function code *//* But not include data lenght ,the data is master write to slave *//* because we don't know the data lenght prior, it depend on ComBuf[ComIndexNoPoint] value */ #define ReadCoilStaIndexEnd 7 // 0~7,0x01,0X#define ForceSingleCoilIndexEnd 7 // 0~7,0x05,0X#define ReadHoldRegIndexEnd 7 // 0~7,0x03,4X#define PresetMulRegIndexEnd 7 // 0~8,0x10,4X/* these macro describe send or recieve allowed */#define SEND 1 // send allowed 允许发送#define RECIEVE 0 // recieve allowed允许接收#define SlaveID 0x01 // the slave's ID#define SendRecieveDelay 50 // Send turn to Recieve or Recieve turn to Send delay time vlaue 发送与接收转换#define MaxDataLen 30 // preset CommBuf array Max length 通讯缓冲区max长度#define MaxRegLen 20 // preset SlaveOutputBuf Max lenght /* variable for modbus is following 变量定义*/bit data RecFinishF;uchar data CommIndex;uchar data writeadr;uchar data CommIndexEnd;uchar idata CommBuf[MaxDataLen];uchar idata SlaveOutputBuf[MaxRegLen]; // Hight 8 bit of word is front,Low 8 bit is back高8位在前低8位在后uchar idata SlaveInputBuf[MaxRegLen];uchar data *port _at_ 0x7F ;/* exp data define *///#define SlaveAdr XBYTE [0xD800]sbit LED0=P1^0;sbit LED1=P1^1;sbit LED2=P1^2;sbit LED3=P1^3;sbit LED4=P1^4;sbit LED5=P1^5;sbit LED6=P1^6;sbit LED7=P1^7;/*后加的P0*/sbit LED00=P0^0;sbit LED01=P0^1;sbit LED02=P0^2;sbit LED03=P0^3;sbit LED04=P0^4;sbit LED05=P0^5;sbit LED06=P0^6;sbit LED07=P0^7;uchar idata ForceCoilBuf[10];uchar count=0;uchar forceval=0x00;//bit forcesendF=0;/* Table Of CRC Values for high-order byte CRC校验高位*/ 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/* Table of CRC values for low-order byte CRC校验低位*/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 } ;uint crc16(uchar *puchMsg,uchar usDataLen) // puchMsg message to calculate CRC upon{ // usDataLen quantity of bytes in messageuchar uchCRCHi = 0xFF ; // high byte of CRC initialized 预设高校验值uchar uchCRCLo = 0xFF ; // low byte of CRC initialized预设低校验值uint uIndex ; // will index into CRC lookup table 将index 在CRC里查找while (usDataLen--) // Pass through message buffer 经过信息缓冲{uIndex = uchCRCHi ^ *puchMsg++ ; // calculate the CRC 计算CRCuchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;uchCRCLo = auchCRCLo[uIndex] ;}return (((uint)(uchCRCLo) << 8) | uchCRCHi) ;}/*------------- init code is following ----------------*/void initSFR(void)//初始化定时器、中断等TH1=0xfa;TL1=0xfa; // botelv set 9600 bps/sTMOD=0x21; // T1 8bit timer for uart band rate, T0 16bit timerPCON=PCON | 0x80; // band rate is 2 timesSCON=0x50; // uart mode is 01,start bit + 8 bit data +stop bit : peer to peer communication PS=1; // uart intrrupt is hightest TR1=1; // start T1ES=1; // serially interrupt allowEA=1; // chief interrupt allow}void initvar(void){uchar i;for (i=0;i<=250;i++)_nop_();T0=RECIEVE;RecFinishF=0;CommIndex=0;CommIndexEnd=0xff; // First enlarge recieve lenghtfor (i=0;i<maxreglen;i++)< bdsfid="178" p=""></maxreglen;i++)<>SlaveOutputBuf[i]=0;}void init(void){initSFR();initvar();}void AnalyzeRecieve(void){uint crc16tem;uchar i;if (CommBuf[CommIndexSlaveID]==SlaveID) // The slave ID is matched 检查站号是否匹配{crc16tem=crc16(CommBuf,CommIndex-1); // calculate CRC check, erase two CRC byteif (crc16tem==(((uint)(CommBuf[CommIndexEnd]) << 8) | CommBuf[CommIndexEnd-1])) // crc16 check is OK{switch (CommBuf[CommIndexFunction]){case ReadCoilSta://--------------------------------------------------------------------------------读线圈CommBuf[CommIndexSlaveID]=SlaveID; // 0 01CommBuf[CommIndexFunction]=ReadCoilSta; // 1 01CommBuf[2]=CommBuf[CommIndexNoPointLo] / 0x08; // 0x16 bit 10转为10进制为16,/8为2个字节//for (i=0;i<="">SlaveOutputBuf[0]=~P1; // send data of master force coil val SlaveOutputBuf[1]=~P0;i=CommBuf[2]+3;for(CommIndex=3;CommIndex<="" p="" reg="" send="" to="">CommBuf[CommIndex]=SlaveOutputBuf[CommIndex-3]; // hight 8 bit is first sendcrc16tem=crc16(CommBuf,CommIndex); // then send low 8 bit dataCommBuf[CommIndex++]=(uchar)(crc16tem & 0x00ff); // send crccheck low 8 bit is front CommBuf[CommIndex]=(uchar)(crc16tem>>8); // then send hight 8 bitCommIndexEnd=CommIndex;CommIndex=0;SBUF=CommBuf[CommIndex++];break;case ForceSingleCoil://----------------------------------------------------------------------------------写线圈// SlaveInputBuf[0]=CommBuf[4]; // get data 0x00 or 0xffif (CommBuf[4]==0xff){switch(CommBuf[3]){case 0:LED0=0 ;break;case 1:LED1=0 ;case 2: LED2=0; ; break; case 3: LED3=0; break; case 4: LED4=0; break; case 5: LED5=0; break; case 6: LED6=0; break; LED7=0; break; case 8: LED00=0; break; case 9: LED01=0; break; case 10: LED02=0; break; case 11: LED03=0; break;LED04=0;break;case 13:LED05=0;break;case 14:LED06=0;break;case 15:LED07=0;break;}}else{switch(CommBuf[3]) { case 0:LED0=1;break;case 1:LED1=1;break;case 2:LED2=1;break;case 3:LED3=1;case 4:LED4=1;case 5: LED5=1; break; case 6: LED6=1; break; case 7: LED7=1; break; case 8: LED00=1; break; case 9: LED01=1; break; case 10: LED02=1; break; case 11: LED03=1; break; case 12: LED04=1; break; case 13: LED05=1; break; case 14: LED06=1;case 15:LED07=1;break;}}// LED0=!LED0;ForceCoilBuf[count]=CommBuf[4]; forceval=CommBuf[4];count++;CommBuf[CommIndexSlaveID]=SlaveID; // 0CommBuf[CommIndexFunction]=ForceSingleCoil; // 1CommIndex=2;// CommBuf[CommIndex++]=0x00;// CommBuf[CommIndex++]=0x01;// CommBuf[CommIndex++]=0x00;// CommBuf[CommIndex++]=0xff;// forcesendF=1;CommBuf[CommIndex++]=CommBuf[CommIndexStartAdr Hi]; // 2CommBuf[CommIndex++]=CommBuf[CommIndexStartAdr Lo]; // 3CommBuf[CommIndex++]=CommBuf[CommIndexNoPoint Hi]; // 4CommBuf[CommIndex++]=CommBuf[CommIndexNoPoint Lo]; // 5crc16tem=crc16(CommBuf,CommIndex); // then send low 8 bit dataCommBuf[CommIndex++]=(uchar)(crc16tem & 0x00ff); // send crccheck low 8 bit is front CommBuf[CommIndex]=(uchar)(crc16tem>>8); // then sendhight 8 bitCommIndexEnd=CommIndex;CommIndex=0;SBUF=CommBuf[CommIndex++];break;case ReadHoldReg://---------------------------------------------------------------------------读积存器writeadr=CommBuf[3];CommBuf[CommIndexSlaveID]=SlaveID; // 0CommBuf[CommIndexFunction]=ReadHoldReg; // 1CommBuf[2]=CommBuf[CommIndexNoPointLo]*2; // 2 Byte Countport=(uchar data *)writeadr;for (i=0;i<="">{SlaveOutputBuf[i]=0;i++;SlaveOutputBuf[i]=*port;port++;}i=CommBuf[2]+3;for(CommIndex=3;CommIndex<="" p="" reg="" send="" to="">CommBuf[CommIndex]=SlaveOutputBuf[CommIndex-3]; // hight 8 bit is first sendcrc16tem=crc16(CommBuf,CommIndex); // then send low 8 bit dataCommBuf[CommIndex++]=(uchar)(crc16tem & 0x00ff); // send crccheck low 8 bit is front CommBuf[CommIndex]=(uchar)(crc16tem>>8); // then send hight 8 bitCommIndexEnd=CommIndex;CommIndex=0;SBUF=CommBuf[CommIndex++];break;case PresetMulReg://----------------------------------------------------------------------------写积存器//writeadr=CommBuf[3]+CommBuf[2]*256;//j=CommBuf[CommIndexNoPointLo]*2;//for (i=0;i<j;i++)< bdsfid="367" p=""></j;i++)<>//{//SlaveOutputBuf[i]=CommBuf[i+7]; // get data that master send start 7th byte//XBYTE[writeadr]=SlaveOutputBuf[i];///writeadr++;//}port=(uchar data *)CommBuf[3];*port=CommBuf[5];CommBuf[CommIndexSlaveID]=SlaveID; // 0CommBuf[CommIndexFunction]=PresetMulReg; // 1CommIndex=2;CommBuf[CommIndex++]=CommBuf[CommIndexStartAdr Hi]; // 2CommBuf[CommIndex++]=CommBuf[CommIndexStartAdr Lo]; // 3CommBuf[CommIndex++]=CommBuf[CommIndexNoPoint Hi]; // 4CommBuf[CommIndex++]=CommBuf[CommIndexNoPoint Lo]; // 5crc16tem=crc16(CommBuf,CommIndex); // then send low 8 bit dataCommBuf[CommIndex++]=(uchar)(crc16tem & 0x00ff); // send crccheck low 8 bit is front CommBuf[CommIndex]=(uchar)(crc16tem>>8); // then send hight 8 bit CommIndexEnd=CommIndex;CommIndex=0;SBUF=CommBuf[CommIndex++];//XBYTE[writeadr]=SlaveOutputBuf[1];break;default:for (i=0;i<=80;i++) // delay // error recieve again_nop_();CommIndex=0;T0=RECIEVE;break;}}else{for (i=0;i<=80;i++) // delay_nop_();CommIndex=0;T0=RECIEVE;}}}/*------------- intr code is following ----------------*/void slavecomm(void) interrupt 4 using 2{uint i;ES=0;if(TI){TI=0;if (CommIndex<=CommIndexEnd){SBUF=CommBuf[CommIndex++];}else{for (i=0;i<=SendRecieveDelay;i++) // delay_nop_();CommIndex=0;T0=RECIEVE;}}if(RI){RI=0;if (CommIndex<="" less="" p="" recieve="" than="">{CommBuf[CommIndex]=SBUF;if (CommIndex==CommIndexFunction){switch (CommBuf[CommIndexFunction]){case ReadCoilSta: CommIndexEnd=ReadCoilStaIndexEnd; break;case ForceSingleCoil: CommIndexEnd=ForceSingleCoilIndexEnd; break;case ReadHoldReg: CommIndexEnd=ReadHoldRegIndexEnd;break;case PresetMulReg: CommIndexEnd=7 ;//6+CommBuf[CommIndexNoPointLo]*2+2;break;default: break;}}CommIndex++;}else // recieve finished CommIndex==CommIndexEnd{CommBuf[CommIndexEnd]=SBUF;RecFinishF=1;for (i=0;i<=SendRecieveDelay;i++) // delay_nop_();T0=SEND;}}ES=1;}/*------------------ main code following -----------------*/ void main(void){uint data i;init();while(1){for(i=0;i<=9;i++)_nop_();if (RecFinishF){ AnalyzeRecieve(); RecFinishF=0;}}}。
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;}}。
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,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,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;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(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.c#i nclude "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; //设为发送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){ //读完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[i] = receBuf[i];}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:}}}//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;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;break;case 8:break;case 9:break;case 10:break;case 11:break;case 12:break;case 13:break;case 14:break;case 15:break;case 16:*tempData = testRegister;break;default:break;}return result;}//uint16 getRegisterVal(uint16 addr,uint16 &data) //设置寄存器值返回0表示成功uint16 setRegisterVal(uint16 addr,uint16 tempData) {uint16 result = 0;uint16 tempAddr;tempAddr = addr & 0xfff;switch(tempAddr & 0xff){case 0:break;case 1:break;case 2:break;case 3:break;case 4:break;case 5:break;case 6:break;case 7:break;case 8:break;case 9:break;case 10:break;case 11:break;case 12:break;case 13:break;case 14:break;case 15:break;case 16:testRegister = tempData;break;default:break;}return result;}//uint8 setRegisterVal(uint16 addr,uint16 data)。