单片机Modbus实现主从通信汇编程序

Modbus背景及程序框架

在这里插入图片描述
两路差分输入信号PG1、PG2经输入驱动,转化为单端输入MA、MB;板载14路二选一选通开关;以第i路为例,若选通信号Si=1,i路选通输出OZi=MA;否则,选通输出OZi=MB;最终,OZi经输出驱动重新转化为差分信号Zi +,Zi-输出。各路选通信号Si ,由51芯片的P2、P1口输出控制; 其它设备通过485串行接口,可发送Modbus命令帧读写Si值,实现信号的选择输出。

Modbus 协议是应用于电子控制器上的一种通用语言。通过此协议,控制器相互之间、控制器经由网络(例如以太网)和其它设备之间可以通信。
标准的Modbus口是使用一RS-232C兼容串行接口,它定义了连接口的针脚、电缆、信号位、传输波特率、奇偶校验。控制器能直接或经由Modem组网。
控制器通信使用主—从技术,即仅一设备(主设备)能初始化传输(查询)。其它设备(从设备)根据主设备查询提供的数据作出相应反应。程序中,该板卡作为从设备。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

源程序及注释

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; P1.0--7,P2.0--5: SELECT BITS
BSEG
 B_S15	BIT	P1.0
 B_S14	BIT	P1.1
 B_S13	BIT	P1.2
 B_S12	BIT	P1.3
 B_S11	BIT	P1.4
 B_S10	BIT	P1.6
 B_S2	BIT	P2.0
 B_S3	BIT	P2.1
 B_S4	BIT	P2.2
 B_S5	BIT	P2.3
 B_S6	BIT	P2.4
 B_S7	BIT	P2.5
 B_S8	BIT	P1.5
 B_S9	BIT	P1.7
INTRANSMIT	BIT	00H	; 20H                        ;D19发送状态标志位
HAVEDATAINBUF	BIT	01H                          ;缓冲区接收完整数据保标志位
TRANSMITCTRL	BIT	P3.6
RECEIVECTRL	BIT	P3.7                             ;D19方向选择,全0选择接收状态,全1选择发送状态
ENDS
DSEG
;使用寄存器1区
P1S_COUNTER	=	0AH	; R2
MS_COUNTER	=	0BH	; R3
IN_LASTSTABLE	=	0CH	; R4
IN_CHANGEEVER	=	0DH	; R5
INTERVALT	=	0EH	; R6
F_NAP		=	0FH	; R7
;使用寄存器2区
REC_DPOINTER	=	10H	; R0
TRS_DPOINTER	=	11H	; R1
RECBYTECNT	=	12H	; R2
TRSBYTECNT	=	13H	; R3
N_STATIONADDR	=	15H	; R5                     ;从机地址

BIT_MEMORY	DATA	20H	; BIT DATA UNIT
PARASTARTADDR	DATA	28H
 CTRLWRD	=	PARASTARTADDR
 
TOPOFSTACK	=	4FH
BOTTOMOFSTACK	=	5FH                          ;堆栈范围
REC_STATIONADDR	=	60H                          ;缓冲区首地址  存放从机地址
 TRS_STATIONADDR=	REC_STATIONADDR
REC_COMMAND	=	61H                              ;功能码
REC_DATAADDRH	=	62H                          ;寄存器地址高字节
 EXCEPTIOCODE	=	REC_DATAADDRH
 RETURNBYTECNT	=	REC_DATAADDRH
REC_DATAADDRL	=	63H                          ;寄存器地址低字节
REC_DATACNTH	=	64H                          ;寄存器写入值高字节
REC_DATACNTL	=	65H                          ;寄存器写入值低字节
REC_BYTENUM	=	66H                              ;CRC校验高字节
EEPROM_BUF	=	67H                              ;CRC校验低字节
REC_BUFEND	=	80H                              ;接收缓冲区尾部地址                 
ENDS

; ERROR CODE TO RETURN
F_ILLEGALFUNCTION	EQU	01H
F_ILLEGALDATAADDR	EQU	02H
F_ILLEGALDATAVAL	EQU	03H
F_SALVEDEVICE		EQU	04H
F_ACKNOWLEDGE		EQU	05H
F_SLAVEDEVICEBUSY	EQU	06H
F_NEGACKNOWLEDGE	EQU	07H
F_MEMORYPARITY		EQU	08H

ORG	0000H
AJMP	PROGRAMSTART
DB	85H
EXTINT0_ISRP:
AJMP	EXTINT0_ISR
LJMP	IC_CARD_SUBP	; 0005H
LJMP	RETURN_RESULT	; 0008H
TIMER0_ISRP:
AJMP	TIMER0_ISR
T_AT_KEY:
DB	16H,1EH,26H,  5,  6,  4 ; 1 2 3	F1 F2 F3
EXTINT1_ISRP:
AJMP	EXTINT1_ISR
DB	25H,2EH,36H,0CH,  3,0BH ; 4 5 6	F4 F5 F6
TIMER1_ISRP:
AJMP	TIMER1_ISR
DB	3DH,3EH,46H,76H,75H,74H ; 7 8 9	ESC ^  >
SERIAL_ISRP:
AJMP	SERIAL_ISR
DB	14H,45H,5AH,66H,6BH,72H	; R 0 <_| <- < V
TIMER2_ISRP:
AJMP	TIMER2_ISR

CRC_KEYWORD:
DB	0A0H,01H,0

AUCHCRCHI:                                       ;CRC 高字节值表
DB	000H,0C1H,081H,040H,001H,0C0H,080H,041H,001H,0C0H,080H,041H,000H,0C1H,081H,040H
DB	001H,0C0H,080H,041H,000H,0C1H,081H,040H,000H,0C1H,081H,040H,001H,0C0H,080H,041H
DB	001H,0C0H,080H,041H,000H,0C1H,081H,040H,000H,0C1H,081H,040H,001H,0C0H,080H,041H
DB	000H,0C1H,081H,040H,001H,0C0H,080H,041H,001H,0C0H,080H,041H,000H,0C1H,081H,040H
DB	001H,0C0H,080H,041H,000H,0C1H,081H,040H,000H,0C1H,081H,040H,001H,0C0H,080H,041H
DB	000H,0C1H,081H,040H,001H,0C0H,080H,041H,001H,0C0H,080H,041H,000H,0C1H,081H,040H
DB	000H,0C1H,081H,040H,001H,0C0H,080H,041H,001H,0C0H,080H,041H,000H,0C1H,081H,040H
DB	001H,0C0H,080H,041H,000H,0C1H,081H,040H,000H,0C1H,081H,040H,001H,0C0H,080H,041H
DB	001H,0C0H,080H,041H,000H,0C1H,081H,040H,000H,0C1H,081H,040H,001H,0C0H,080H,041H
DB	000H,0C1H,081H,040H,001H,0C0H,080H,041H,001H,0C0H,080H,041H,000H,0C1H,081H,040H
DB	000H,0C1H,081H,040H,001H,0C0H,080H,041H,001H,0C0H,080H,041H,000H,0C1H,081H,040H
DB	001H,0C0H,080H,041H,000H,0C1H,081H,040H,000H,0C1H,081H,040H,001H,0C0H,080H,041H
DB	000H,0C1H,081H,040H,001H,0C0H,080H,041H,001H,0C0H,080H,041H,000H,0C1H,081H,040H
DB	001H,0C0H,080H,041H,000H,0C1H,081H,040H,000H,0C1H,081H,040H,001H,0C0H,080H,041H
DB	001H,0C0H,080H,041H,000H,0C1H,081H,040H,000H,0C1H,081H,040H,001H,0C0H,080H,041H
DB	000H,0C1H,081H,040H,001H,0C0H,080H,041H,001H,0C0H,080H,041H,000H,0C1H,081H,040H

AUCHCRCLO:                                       ;CRC低字节值表
DB	000H,0C0H,0C1H,001H,0C3H,003H,002H,0C2H,0C6H,006H,007H,0C7H,005H,0C5H,0C4H,004H
DB	0CCH,00CH,00DH,0CDH,00FH,0CFH,0CEH,00EH,00AH,0CAH,0CBH,00BH,0C9H,009H,008H,0C8H
DB	0D8H,018H,019H,0D9H,01BH,0DBH,0DAH,01AH,01EH,0DEH,0DFH,01FH,0DDH,01DH,01CH,0DCH
DB	014H,0D4H,0D5H,015H,0D7H,017H,016H,0D6H,0D2H,012H,013H,0D3H,011H,0D1H,0D0H,010H
DB	0F0H,030H,031H,0F1H,033H,0F3H,0F2H,032H,036H,0F6H,0F7H,037H,0F5H,035H,034H,0F4H
DB	03CH,0FCH,0FDH,03DH,0FFH,03FH,03EH,0FEH,0FAH,03AH,03BH,0FBH,039H,0F9H,0F8H,038H
DB	028H,0E8H,0E9H,029H,0EBH,02BH,02AH,0EAH,0EEH,02EH,02FH,0EFH,02DH,0EDH,0ECH,02CH
DB	0E4H,024H,025H,0E5H,027H,0E7H,0E6H,026H,022H,0E2H,0E3H,023H,0E1H,021H,020H,0E0H
DB	0A0H,060H,061H,0A1H,063H,0A3H,0A2H,062H,066H,0A6H,0A7H,067H,0A5H,065H,064H,0A4H
DB	06CH,0ACH,0ADH,06DH,0AFH,06FH,06EH,0AEH,0AAH,06AH,06BH,0ABH,069H,0A9H,0A8H,068H
DB	078H,0B8H,0B9H,079H,0BBH,07BH,07AH,0BAH,0BEH,07EH,07FH,0BFH,07DH,0BDH,0BCH,07CH
DB	0B4H,074H,075H,0B5H,077H,0B7H,0B6H,076H,072H,0B2H,0B3H,073H,0B1H,071H,070H,0B0H
DB	050H,090H,091H,051H,093H,053H,052H,092H,096H,056H,057H,097H,055H,095H,094H,054H
DB	09CH,05CH,05DH,09DH,05FH,09FH,09EH,05EH,05AH,09AH,09BH,05BH,099H,059H,058H,098H
DB	088H,048H,049H,089H,04BH,08BH,08AH,04AH,04EH,08EH,08FH,04FH,08DH,04DH,04CH,08CH
DB	044H,084H,085H,045H,087H,047H,046H,086H,082H,042H,043H,083H,041H,081H,080H,040H

; R0=STR PTR, R2=J=LEN(CRC BYTES INCLUDED), R3=I,R6=CRCHI,R7=CRCLO
MODBUS_CRC_EEPROM_BUF:
MOV	R0,#EEPROM_BUF
MOV	R2,#16
SJMP	MODBUS_CRC
MODBUS_CRC_REC:                                  ;CRC校验
MOV	R0,#REC_STATIONADDR
MODBUS_CRC:
MOV	R6,#0FFH
MOV	R7,#0FFH
DEC	R2
DEC	R2
MODBUS_CRC_L:
MOV	A,@R0
INC	R0
XRL	A,R6
MOV	R3,A
MOV	DPTR,#AUCHCRCHI
MOVC	A,@A+DPTR
XRL	A,R7
MOV	R6,A
MOV	A,R3
MOV	DPTR,#AUCHCRCLO
MOVC	A,@A+DPTR
MOV	R7,A
DJNZ	R2,MODBUS_CRC_L
MOV	A,R6
XCH	A,@R0
XRL	A,@R0
MOV	R3,A
INC	R0
MOV	A,R7
XCH	A,@R0
XRL	A,@R0
ORL	A,R3
INC	R0
RET

;**************************************************
EXTINT0_ISR:                                     ;外部中断0
RETI

TIMER0_ISR:                                      ;定时器T0中断
PUSH	PSW
PUSH	ACC
MOV	PSW,#8

CLR	EA		                                     ;关闭所有中断(很重要)中断禁区
CLR	TR0		                                     ;STOP TIMER0(很重要)硬件计数禁区
MOV	A,#6DH		                                 ;11.0592 MHZ
ADD	A,TL0		                                 ;1 MS,从停止定时器TR0到重置定时器TR0有7条指令
MOV	TL0,A                                        ;为了完成这个,需要把计数器初值加7,因此0FC66H+7=0FC6DH
MOV	A,#0FCH
ADDC	A,TH0                                    ;方式1计数到达时不会自动赋初值,到00继续增加,所以需要把这些考虑进来
MOV	TH0,A                                        ;把从溢出到停止定时器以及这几条指令的执行时间全部考虑进来
SETB	TR0                                      ;重新设置1ms的计数初值
SETB	EA                                       ;开中断

MOV	A,R3		                                 ; MS_COUNTER CLOCK
ADD	A,#1
MOV	R3,A
MOV	A,R2		                                 ; P1S_COUNTER
ADDC	A,#0
MOV	R2,A

MOV	A,R6		                                 ; 每定时时间1ms到,R6-1
JZ	TIMER0_ISR_E                                 ;一定要先判断是否为0,为0就说明没有接收到数据了
DJNZ	R6,TIMER0_ISR_E                          ;减一后不为0,返回
SETB	HAVEDATAINBUF                            ;减为0的时候说明前后两次时间间隔为4ms,一帧结束,设置标志位,缓冲区有数据
INC	R7		                                     ; F_NAP
TIMER0_ISR_E:
POP	ACC
POP	PSW		                                     ; FIELD RESTORE
RETI

EXTINT1_ISR:                                     ;外部中断1
RETI
TIMER1_ISR:                                      ;定时器1
RETI

;*********************串口中断接收********************
SERIAL_ISR:
PUSH	PSW		                                 ; PSW和ACC保护
PUSH	ACC
MOV	PSW,#10H	                                 ; 使用寄存器2区
JNB	RI,SERIAL_ISR_TX                             ;判断接收中断标志位是否有效
CLR	RI                                           ;有效,清除后继续
JB	INTRANSMIT,SERIAL_ISR_TX                     ;由于半双工通信,所以MODBUS发送状态有效则跳转
JB	HAVEDATAINBUF,SERIAL_ISR_TX                  ;前面数据包还没有取走则跳转
MOV	INTERVALT,#4                                 ;使用R6的直接地址启动4ms定时,注意现在寄存器区已经换了不能直接R6          
CJNE	R0,#REC_BUFEND,SERIAL_ISR_RX1         	 ;R0代表接收缓冲区指针,REC是缓冲区尾部,要是相等则溢出,不相等转移继续接收
SJMP	SERIAL_ISR_TX
SERIAL_ISR_RX1:
MOV	A,SBUF                                       ;读接收数据,并在PSW中产生接收数据的奇偶值
MOV	@R0,A                                        ;存接收数据
MOV	C,P          
MOV	ACC.2,C		                                 ; SCON.2 = RB8
XRL	A,SCON                                       ; P和RB8是否相等
JB	ACC.2,SERIAL_ISR_TX	                         ;如果异或后第二位为1则出错,指针不变
INC	R0                                           ;正确,指针加1

;*********************串口中断发送*****************
SERIAL_ISR_TX:
JNB	TI,SERIAL_ISR_E                              ;判断发送中断标志位是否有效
CLR	TI                                           ;如果上次没发送完成,那么TI为0,发送完成后TI会被置1
JNB	INTRANSMIT,SERIAL_ISR_E                      ;不是发送状态则结束
MOV	A,R3		                                 ; TRSBYTECNT发送字节数
JNZ	SERIAL_ISR_TX1                               ; 如果不为0则继续发送
CLR	INTRANSMIT                                   ;为0则已经发完,清除发送状态
CLR	TRANSMITCTRL              
CLR	RECEIVECTRL                                  ;清除P3.6,P3.7,则D19处于接收状态
MOV	REC_DPOINTER,#REC_STATIONADDR                ;接收指针指向接收缓冲区第一个字节
SJMP	SERIAL_ISR_E                             ;中断返回
SERIAL_ISR_TX1:                                
MOV	A,@R1
MOV	C,P
MOV	TB8,C
MOV	SBUF,A		                                 ; TRS_DPOINTER启动发送
INC	R1                                           ;发送指针加1
DEC	R3                                           ;发送字节数减1  
SERIAL_ISR_E:
POP	ACC
POP	PSW			                                 ; FIELD RESTORE
RETI
TIMER2_ISR:
RETI

;*************程序开始(初始化代码)**********************************
PROGRAMSTART:			                      ;:HDW 	RESET
CLR	EA                                        ;关中断
CLR	A
MOV	PSW,A                                     ;状态寄存器清零
MOV	CTRLWRD,A                                 ;28H清零
MOV	CTRLWRD+1,A
CPL	A
MOV	P0,A
MOV	P1,A
MOV	P2,A
MOV	P3,A                                       ;所有输出锁存器全部置1
CLR	TRANSMITCTRL                               ;P3.6=0   写清零
CLR	RECEIVECTRL                                ;P3.7=0   读清零              
MOV	R5,#20                                     ;延时2s   可以由20变为2,即为0.2s
PROGRAMSTART_0:
MOV	R6,#200		                               ; DELAY FOR 100 MILLISECONDS
PROGRAMSTART_1:
MOV	R7,#230                                    ;两指令周期
DJNZ	R7,$		                           ;两指令周期 500US
DJNZ	R6,PROGRAMSTART_1	                   ; 200*0.5MS
DJNZ	R5,PROGRAMSTART_0	                   ; 20*0.1S
CLR	A
MOV	R0,A
PROGRAMSTART_2:
INC	R0
MOV	@R0,A		; 0 ==> 0 -- FFH                 ;00-7FH,内部128RAM清零
CJNE	R0,#80H-1,PROGRAMSTART_2	; CLEAR DATA END

MOV	SP,#TOPOFSTACK                               ;TOPOFSTACKC = 4FH
MOV	TMOD,#21H	                                 ;T0设置为方式1,16位定时器;T1设置位方式2,8位定时器,初值自动重装
MOV	TCON,#55H	                                 ;T1和T0开始计数,INT0和INT1外中断信号设置位下降沿有效 
MOV	TH0,#0FCH	                                 ; T0为1ms定时,3-4ms为帧间隔,通过这个判断帧起始和停止位置,以接受完整数据帧
MOV	TL0,#66H                                     ;计数初值为1111 1100 0110 0110 B = 2^16-1ms*11.059M /12 = 64614
MOV	TH1,#-3	                                     ; T1 3.255 μs ,T1为串行口提供溢出中断,
                                                 ; T1初值= 256 - 11059200/(32*12*9600)= 253 = 0FDH (串行口波特率为9600b/s)
MOV	SCON,#11010000B	                             ;SM1=SM0=1 MODE 3, 9 BITS, 波特率由T1时间常数指定,SM2=0,接受所有数据并触发中断,
                                                 ;REN=1,允许接收,低四位先全部清零
MOV	IP,#00010000B	                             ;设置串行口中断为高优先级中断

MOV	IE,#10010010B	                             ;EA=1,串行口中断允许ES=1,计时器T0中断允许ET0=1
                                                 ;为了避免因T1溢出引起中断,此时应禁止T1中断
READSTATIONADDR:
MOV	N_STATIONADDR,#1                             ;定义从机地址1

;**************************主循环****************************************
WAITCOMMAND:
ACALL	SETPGADDR                                ;28H,29H位处理给P1,P2端口
CLR	HAVEDATAINBUF                                ;清除数据接收标志位
MOV	REC_DPOINTER,#REC_STATIONADDR                ;接收指针指向MODBUS接收区地址60H
MAIN_LOOP:
JNB	HAVEDATAINBUF,MAIN_LOOP                      ;没有接收到数据,标志位为0
CLR	HAVEDATAINBUF                                ;接收到数据,标志位清零后继续
MOV	A,REC_STATIONADDR                            ;把接收区的第一个字节内容给A
	; NOT ACK BROADCAST
CJNE	A,N_STATIONADDR,WAITCOMMAND              ;判断第一个字节是否位从机地址1,如果是1才是发给自己的
MOV	R2,#8                                        ;利用R2判断帧长是不是错误,一个数据包8个字节
MOV	A,REC_COMMAND                                
CJNE A,#16,MAIN_LOOP2                            ;功能码为16对应情况 16号与3,6帧长不同            
MOV	A,REC_BYTENUM
ADD	A,#9
MOV	R2,A                                         ;如果是16号,帧字节加9后再存入R2
MAIN_LOOP2:
MOV	A,REC_DPOINTER
ADD	A,#-REC_STATIONADDR
CJNE	A,R2,WAITCOMMAND                         ;末-头=R2?
ACALL	MODBUS_CRC_REC                           ;判断CRC有没有错误
JNZ	WAITCOMMAND                                  ;出错,重新接收
MOV	A,REC_COMMAND
CJNE	A,#3,$+5
AJMP	CMDM3
CJNE	A,#6,$+5                                 ;跳转到功能码6对应程序
AJMP	CMDM6
CJNE	A,#16,$+5
AJMP	CMDM16
ERRCOMMAND:
MOV	A,#F_ILLEGALFUNCTION
AJMP	EXCEPTRET
EXCEPTRET:
MOV	EXCEPTIOCODE,A
ORL	REC_COMMAND,#80H
MOV	R2,#5
;AJMP	RETURN_RESULT

RETURN_RESULT:                                   ;进行返回
MOV	A,TRS_STATIONADDR                            ;返回60H开始的内容
JZ	WAITCOMMAND
MOV	TRSBYTECNT,R2                                ;发送字节长度
ACALL	MODBUS_CRC_REC                           ;CRC校验
SETB	INTRANSMIT                               ;半双工标志位置发送
SETB	TRANSMITCTRL
SETB	RECEIVECTRL                              ;D19设为发送状态
MOV	TRS_DPOINTER,#TRS_STATIONADDR                ;设置发送首地址
CLR	A
MOV	INTERVALT,A                                  ;定时时间清零
SETB	TI                                       ;发送完成TI置1,下次就能发送了
AJMP	WAITCOMMAND

;*******************功能码3***************************
CMDM3:
MOV	A,REC_DATAADDRH
ORL	A,REC_DATACNTH
JNZ	ERRDATAADDR                                  ;相或不为0报错
MOV	A,REC_DATAADDRL
ADD	A,REC_DATACNTL
JC	ERRDATAADDR                                  ;相加有进位报错
ADD	A,#-20			                             ; MAX DATA ADDR = 19
JC	ERRDATAADDR
MOV	A,REC_DATACNTL
JZ	ERRDATAVAL                                   ;寄存器数量低字节为0报错
ADD	A,#-14			                             ; MAX RETURN DATA LEN 14 = 26/2+1
JC	ERRDATAVAL
MOV	A,REC_DATACNTL                               ;提取待读取的寄存器数量
ADD	A,ACC
MOV	RETURNBYTECNT,A                              ;RETURNBYTECNT=2*REC_DATACNTL,读取数据的字节数,为寄存器数*2         
MOV	R2,A                           
MOV	A,REC_DATAADDRL                              ;对寄存器操作
ADD	A,ACC
ADD	A,#PARASTARTADDR
MOV	R0,A                                         ;R0提取待读取的寄存器地址
MOV	R1,#REC_DATAADDRL                            ;R1提取寄存器地址
CMDM3A:
MOV	A,@R0                                        ;把R0指向寄存器内容读出来
MOV	@R1,A                                        ;放在寄存器R1指向寄存器中
INC	R0
INC	R1
DJNZ	R2,CMDM3A                                ;读取字节数量
MOV	A,RETURNBYTECNT
ADD	A,#5                                         ;加上从机号,功能码,字节数,CRC高低字节
MOV	R2,A
AJMP	RETURN_RESULT

ERRDATAADDR:                                    ;出错返回
MOV	A,#F_ILLEGALDATAADDR
AJMP	EXCEPTRET
ERRDATAVAL:
MOV	A,#F_ILLEGALDATAVAL
AJMP	EXCEPTRET

;******************功能码6************************
CMDM6:
MOV	A,REC_DATAADDRH                              ;寄存器地址高字节应该为0              
JNZ	ERRDATAADDR                                  ;不为零转移
MOV	A,REC_DATAADDRL                              ;寄存器地址低字节  
ADD	A,#-20			                             ; MAX DATA ADDR = 20
JC	ERRDATAADDR                                  ;有进位说明出错,转移,如果要求改第21个寄存器就会出错
MOV	A,REC_DATAADDRL
ADD	A,ACC                                        ;对需要操作的寄存器处理
ADD	A,#PARASTARTADDR                             ;写入地址0X28H
MOV	R0,A                                         ;R0:寄存器地址:PARASTARTADDR+REC_DATAADDRL
MOV	A,REC_DATACNTH                               ;寄存器写入值高字节,放在0X28H
MOV	@R0,A                                        ;这里使用的是寄存器间接寻址,因为这里不止一个寄存器
INC	R0
MOV	A,REC_DATACNTL                               ;寄存器写入值低字节,放在0X29H
MOV	@R0,A                                        ;这里28H,29H是一号寄存器,2AH,2BH是二号,一次类推
MOV	R2,#8                                        ;返回8个字节,这里省略了返回内容,因为6号功能发送和返回内容一致,只是告诉要返回8字节
AJMP	RETURN_RESULT

;******************功能码16***********************
CMDM16:
MOV	A,REC_DATAADDRH
ORL	A,REC_DATACNTH
JNZ	ERRDATAADDR
MOV	A,REC_DATAADDRL
ADD	A,REC_DATACNTL
JC	ERRDATAADDR
ADD	A,#-20			                             ; MAX DATA ADDR = 20
JC	ERRDATAADDR
MOV	A,REC_DATACNTL
JZ	ERRDATAVAL                                   ; 寄存器数量低字节不能为0
MOV	R2,REC_BYTENUM                               ; 字节数
MOV	A,REC_DATAADDRL
ADD	A,ACC
ADD	A,#PARASTARTADDR
MOV	R1,A                                         ; 待预置寄存器的地址
MOV	R0,#REC_BYTENUM+1                            ; 寄存器写入字节地址
CMDM6A:
MOV	A,@R0
MOV	@R1,A
INC	R0
INC	R1
DJNZ	R2,CMDM6A                                ;写完所有字节
MOV	R2,#8                                        ;返回8个字节
AJMP	RETURN_RESULT
IC_CARD_SUBP:
AJMP	WAITCOMMAND

;************************
;28H和29H通用数据存储器这两个端口进行位变换以后送到P1端口和P2端口,
;也就是MODBUS的寄存器0在51单片机中对应的是28H和29H这两个字节构成的16位的字,
;注意到28H和29H存在于可以位寻址的空间,实际上是用这两个构成了16位寄存器。
;单片机启动后接收到两个字节的数据,然后通过位变换到P1.0-P1.7,P2.0-P2.5,进行14个选择。
;************************
SETPGADDR:                                     ;0x28H,0x29H送到P2,P1,一位一位传
MOV	A,CTRLWRD+1                                ;CTRLWRD+1=29H
MOV	C,ACC.0
MOV	B_S2,C                                     ;P2.0
MOV	C,ACC.1
MOV	B_S3,C                                     ;P2.1
MOV	C,ACC.2
MOV	B_S4,C                                     ;P2.2
MOV	C,ACC.3
MOV	B_S5,C                                     ;P2.3
MOV	C,ACC.4
MOV	B_S6,C                                     ;P2.4
MOV	C,ACC.5
MOV	B_S7,C                                     ;P2.5
MOV	C,ACC.6
MOV	B_S8,C                                     ;P1.5
MOV	C,ACC.7
MOV	B_S9,C                                     ;P1.7
MOV	A,CTRLWRD                                  ;CTRLWRD=28H 
MOV	C,ACC.0
MOV	B_S10,C                                     ;P1.6
MOV	C,ACC.1
MOV	B_S11,C                                     ;P1.4
MOV	C,ACC.2
MOV	B_S12,C                                     ;P1.3
MOV	C,ACC.3
MOV	B_S13,C                                     ;P1.2
MOV	C,ACC.4
MOV	B_S14,C                                     ;P1.1
MOV	C,ACC.5
MOV	B_S15,C                                     ;P1.0
RET
DB	'V1.1 2003.02.20'
END


  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值