📄 8052.asm
字号:
mov a,#<SIZEOF_MB
add a,dpl
mov dpl,a
mov a,#>SIZEOF_MB
addc a,dph
mov dph,a ;next entry
djnz r5,ci_1
mov EADRL,#<INIT_TOKEN
mov EDATA1,#>INIT_TAG
mov EDATA2,#<INIT_TAG
mov ECON,#05h ;erase page (we sleep for 2mS)
nop ;I'm suspicious!!!
mov ECON,#02h ;write the page (we sleep for 250uS)
nop
ret
;
;
; calculates and appends the CRC for a MODBUS RTU message
; R0->modbus msg
; R6 has the msg length
; return value ==0 if crc on a rx packet was ok else 1 = crc error
; zaps:A,B,R0,R2,R4,R5,R6
;
; crc_lo R4
; crc_hi R5
;
;
;
calculate_crc
mov r4,#0ffh
mov r5,#0ffh ;preload the crc accumulator
cc_lp
mov a,@r0 ;get a byte from the buffer
inc r0
xrl a,r4
mov r4,a ;CRC ^= *buff
mov r2,#8 ;for x=1 to 8
cc_1
clr c
mov a,r5
rrc a
mov r5,a
mov a,r4
rrc a
mov r4,a ;CRC >>=1
jnc cc_2
mov a,#0a0h
xrl a,r5
mov r5,a
mov a,#01h
xrl a,r4
mov r4,a ;if (carry) CRC ^= 0xa001
cc_2
djnz r2,cc_1 ;next x
djnz r6,cc_lp
mov b,#0 ;B has the error status
mov a,@r0
xrl a,r4 ;if crc_lo != *buff then err = 1
jz cc_3
mov b,#1 ;flag error
cc_3
mov a,r4
mov @r0,a
inc r0 ; *buff++ = crc_lo
mov a,@r0
xrl a,r5
jz cc_4
mov b,#1 ;flag error
cc_4
mov a,r5
mov @r0,a
inc r0 ;*buff++ = crc_hi
mov a,b ;get the return result
ret
;----------------------------------------------------------------------------
;
;
; outputs the modbus ascii data. converts the buffer data to ascii and
; appends the modbus LRC and cr/lf
;
; we use register bank 1 exclusively
;
; R0 is tx_ptr
; R1 is rx_ptr
; R2 is tx_state
; R3 is rx_state
; R4 is tx_lrc
; R5 is rx_lrc
; R6 is tx_length
; R7 is rx_length
;
serial_isr
jb MODBUS_TYPE,ascii
jb TI,txrtu
jb RI,rxrtu
sjmp serial_exit
ascii
jb TI,txascii
jnb RI,serial_exit
ljmp rxascii
serial_exit
reti
;
;
; modbus RTU rx code
; we use register bank 1 exclusively
;
; R0 is tx_ptr
; R1 is rx_ptr
; R2 is tx_state
; R3 is rx_state
; R4 is tx_lrc
; R5 is rx_lrc
; R6 is tx_length
; R7 is rx_length
;
;
;
rxrtu
push psw
push a
mov psw,#00001000b ;select register bank#1
mov a,r3 ;get the rx state
cjne a,#0,rxrtu_1
;
; RTU rx state 0, grab the rx data & store into the buffer
;
; mov r5,#3 ;load the timeout with 2 character times
mov a,r7 ;test the rx length
clr c
subb a,#SIZEOF_MODBUS_BUFF-2
jnc rxrtu0_1 ;rx count too large! exit
mov a,SBUF ;rx buff ok, grab the rx char
clr RI
mov @r1,a ;store it
inc r1 ;rx->++
inc r7 ;rx length++
;
; use timer2 for end of packet timeout
;
clr TR2
mov TH2,#0fah
mov TL2,#060h ;3 char times
setb TR2
sjmp rxrtu_x
rxrtu_1
;
; RTU rx state1, ignore any more rx chars for the moment
;
rxrtu0_1
mov a,SBUF ;junk the rx character
clr RI
rxrtu_x
pop a
pop psw
reti
;
;
; the tx code is a little trickier since we use the tx as a timer for the
; rx packet timeout when we're not actually transmitting a packet
;
;
txrtu
push psw
push a
mov psw,#00001000b ;select register bank#1
mov a,r2 ;get the tx state
cjne a,#0,txrtu_1
;
; RTU tx state 0. nothing happening so disable the interrupts
;
setb EN_485 ;don't send anything to the outside!
clr TI
sjmp txrtu_x
; mov a,r5 ;get the rx timer
; jz txrtu_x ;already 0, don't do anything!
; djnz r5,txrtu_x ;dec the timer
;
; timer just hit 0, activate the rx process code
;
; mov r1,#modbus_buff
; mov a,@r1 ;get the first byte (modbus addr)
; xrl a,our_addr ;test with our address
; jnz txrtu0_2 ;if not us
; setb TASK0_RUN ;if a good packet, activate the rx task
; mov r3,#1 ;set state for rx idle, when packet is processed, we are reset
; sjmp txrtu_x
;
; restart the RTU rx code
;
;txrtu0_2
; mov r1,#modbus_buff ;reset the rx->
; mov r7,#0 ;reset the byte count
; mov r3,#0 ;reset the rx state
;
; sjmp txrtu_x
txrtu_1
cjne a,#1,txrtu_x
;
; RTU tx state 1. here we send tx data to the outside world
;
;
clr EN_485
mov a,@r0
mov SBUF,a
clr TI
inc r0
djnz r6,txrtu_x
;
; last character of the packet, next state is 0
;
mov r2,#0
txrtu_x
pop a
pop psw
reti
;
;
; implements the MODBUS ASCII protocol.
; user routine must set tx_ptr (R0), tx_length (R6) and send the ':' start token
; to fire the tx interupt
;
;
txascii
push psw
push a
mov psw,#00001000b ;select register bank#1
mov a,r2 ;get the state into A
cjne a,#0,tx_st1
;
; state = 0
;
mov a,@r0
add a,r4
mov r4,a ;accumulate the checksum
mov a,@r0
swap a ;get hi nibble
anl a,#0fh
cjne a,#10,mti_1
mti_1
jnc L96
add a,#'0' ;ascii offset 0..9
sjmp L97
L96
add a,#'0' + 7 ;ascii offset A..F
L97
mov SBUF,a
clr TI
mov r2,#1 ;next state = 1
sjmp L95_X
tx_st1
cjne a,#1,tx_st2
;
; state = 1. send ascii low nibble
;
mov a,@r0
inc r0
anl a,#0fh ;get low nibble
cjne a,#10,mti_2
mti_2 jnc L96_1
add a,#'0' ;ascii offset 0..9
sjmp L97_1
L96_1
add a,#'0' + 7 ;ascii offset A..F
L97_1
mov SBUF,a
clr TI
dec r6
mov a,r6
cjne a,#1,mti_3 ;length == 1?
sjmp L102 ;yep! calc the lrc
mti_3
jnz L103 ;if the last byte
mov r2,#2 ;next state =2,send CR
sjmp L95_X
L103
mov r2,#0 ;next state = 0,send next byte
sjmp L95_X
L102
mov a,r4 ;get the lrc
cpl a ;ones complement
inc a ;plus 1
mov @r0,a ;store the lrc
mov r2,#0 ;next state =send hi nibble
sjmp L95_X
tx_st2
cjne a,#2,tx_st3
;
; state = 2. send CR
;
mov SBUF,#CR ;send CR
clr TI
mov r2,#3 ;next state = 3 send line feed
sjmp L95_X
tx_st3
cjne a,#3,tx_st4
;
; state = 3. send line feed
;
mov SBUF,#LF ;send LF
clr TI
mov r2,#4 ;next state = 4
sjmp L95_X
tx_st4
cjne a,#4,L95_X
;
; state = 4. end of transmit, set 485 buffer to receive and sit
; here until the tx code re-activates us
;
clr TI
setb EN_485
L95_X
pop a
pop psw
reti
;
;
; implements the MODBUS ASCII receive protocol
;
;
rxascii
push psw
push a
push b
mov psw,#00001000b ;select register bank#1
mov b,SBUF ;get the rx char into B
clr RI
;
; always test for the start token
;
mov a,b
cjne a,#':',L129 ;start token?
mov r5,#0 ;rx_lrc = 0
mov r7,#0 ;rx_length = 0
mov r1,#modbus_buff ;->start of rx buffer
mov r3,#1 ;next state = 1
sjmp L116_X
L129
rx_st1
mov a,r3 ; get the rx state
cjne a,#1,rx_st2
;
; rx_state = 1. expect a hex ascii char or a carriage return
; for end of packet
;
clr c
mov a,b
subb a,#'0' ;minus ascii offset
jc L127 ;if number < '0'
cjne a,#9+1,rx_st1_2
rx_st1_2
jnc L125 ;if rx char > 9
sjmp L126
L125
clr c
subb a,#7
cjne a,#0ah,rx_st1_3
rx_st1_3
jc L127 ;if rx char is < 'A'
cjne a,#0fh+1,rx_st1_4
rx_st1_4
jnc L127 ;if rx char > 'F'
L126
anl a,#0fh
swap a
mov @r1,a
mov r3,#2 ;next state = 2
sjmp L116_X
L127
mov a,b
cjne a,#CR,L124 ;carriage return? (end of packet?)
;
; end of packet
;
mov a,r5 ;get our rx_lrc
jnz L124 ;if lrc was bad!
mov r1,#modbus_buff
mov a,@r1 ;get the first byte (modbus addr)
xrl a,our_addr ;test with our address
jnz L124 ;if not us
setb TASK0_RUN ;if a good packet, activate the rx task
mov r3,#4 ;idle in an illegal state, when packet is processed, we are reset
sjmp L116_X
L124
mov r3,#0 ;next state = 0
sjmp L116_X
rx_st2
cjne a,#2,L116_X
;
; state = 2. expect hex ascii for low byte nibble
;
clr c
mov a,b
subb a,#'0' ;minus ascii offset
jc L137 ;if number < '0'
cjne a,#9+1,rx_st2_2
rx_st2_2
jnc L135 ;if rx char > 9
sjmp L133
L135
clr c
subb a,#7
cjne a,#0ah,rx_st2_3
rx_st2_3
jc L127 ;if rx char is < 'A'
cjne a,#0fh+1,rx_st2_4
rx_st2_4
jnc L127 ;if rx char > 'F'
L133
anl a,#0fh
orl a,@r1 ;'or' in the hi nibble
mov @r1,a
add a,r5 ;get rx_lrc
mov r5,a ;accumulate the lrc
inc r1 ;rx_ptr++
inc r7 ;rx_length++
cjne r7,#SIZEOF_MODBUS_BUFF,rx_st2_5
rx_st2_5
jnc L137 ;if rx packet is too large!
mov r3,#1 ;otherwise next state = 1
sjmp L116_X
L137
mov r3,#0 ;next state = 0
L116_X
pop b
pop a
pop psw
reti
;
;
; timer2 timeout comes here when an end of a modbus rtu packet is detected by 3 char timeout
;
;
t2_isr
clr TR2 ;stop timer2 running
clr TF2 ;reset the interrupt flag
push psw
push a
mov psw,#00001000b ;select register bank#1
mov r1,#modbus_buff
mov a,@r1 ;get the first byte (modbus addr)
xrl a,our_addr ;test with our address
jnz t2_1 ;if not us
setb TASK0_RUN ;if a good packet, activate the rx task
mov r3,#1 ;set state for rx idle, when packet is processed, we are reset
sjmp t2_x
;
; restart the RTU rx code
;
t2_1
mov r1,#modbus_buff ;reset the rx->
mov r7,#0 ;reset the byte count
mov r3,#0 ;reset the rx state
t2_x
pop a
pop psw
reti
;----------------------------------------------------------------------------
;
;
; rom constant data
;
;
;----------------------------------------------------------------------------
; constant structure for the modbus registers. Has flags to determine if the register
; is stored in ram or eeprom and the pointer to the ram/eeprom address where to register
; contents are stored. Also has the default value that is loaded into the registers
; on startup for ram based registers or when first programmed for eeprom based registers.
; There is an entry for every modbus register in order of the modbus register number.
;
;
SIZEOF_MB equ 4 ;structure size
MB_FLAGS equ 0
MB_OFFSET equ 1 ;offset value for index into eeprom OR internal ram (MB_EEPROM if set is eeprom)
MB_DEFAULT equ 2 ;default value for init. 1 word
;
; bits in MB_FLAGS
;
MB_EEPROM equ 0 ;bit 0 of the flags .1 = register read/written to eeprom, 0 =read/write to ram
modbus_options_table
GLOBALS ON
; 0 Software Version Number
MB_VERS equ ($-modbus_options_table)/SIZEOF_MB
db 1.SHL.MB_EEPROM ;options
db <MBEE_VERS ;ram/eeprom addr
dw VERSION ;default value (loaded into eeprom)
modbus_buff ds SIZEOF_MODBUS_BUFF
stack equ $ ;stack starts from here- it grows upwards on a 8051
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -