| ??? 09/28/06 09:26 Read: times |
#125265 - Modbus code Responding to: ???'s previous message |
Here is some modbus code written in assembler - it was originally written in 'c' but converted to assembler to fit into a small micro. However - the structure is much the same. Modbus sees everything as 16 bit registers or bits. This code only implements the 16 bit registers. These regsters may have configuration or process values. The config is stored in eeprom and the process values are stored in ram. The modbus_config_table structure is used by the routines read_modbus_reg and write_modbus_reg to find out where to read/write the values. This structure also has the initialisation values. The routine cold_init is used to initialise the ram based registers with the default value stored in the structure and tests for a token in the eeprom to see if it needs to be initialised. The serial interrupt code sends/receives the modbus packets. This code translates the ascii packets to/from RTU format. When a packet is received, a flag is set to activate a task that calls the routine process_modbus. This routine checks the received packet and forms the response. This code pretty much follows the logic in the modbus spec. You can add extra commands in here if required. Use the routines read_modbus_reg and write_modbus_reg to access the modbus registers. The CRC routine can be found in 'c' in the modbus spec. You can choose between two methods - looped as is done here - slow but small in code size or table driven - large but fast. Beware of atomicity issues - if you don't know what they are, you'd best learn as they apply here. This code ran under a co-operative tasker so modbus register accesses were mutually exclusive between the process_modbus code and the read/write_modbus_reg code. Ignore the above comments at your own peril. Apart from that, MODBUS is a very simple and widely supported protocol for instrumentation and control applications. Useless for anything else though. Have Fun.
CHIP 8052
;
; Modbus code. (c)2005 Russell Bull.
;
; this module implements the Modbus rx/tx code and the register
; structure.
; In order to save ram, certain modbus registers are only implemented in EEprom, the read/write_modbus_reg
; routines take care of any shenanigans.
;
public t2_isr ; timer2 interrupt code
public read_modbus_reg ; reads a modbus reg into R7:R6
public write_modbus_reg ; writes a modbus reg into R7:R6
public cold_init ; loads defaults into ram based regs & eeprom base regs(if required)
public serial_isr ; int vector for the serial port
public MODBUS_READ_HOLDING_REGS
public modbus_buff
public process_modbus ; task to process modbus messages
public stack
public NUM_MODBUS_REGS
public our_addr
extern MODBUS_TYPE
extern RX_PKT
;
; kernel externals
;
extern TASK0_RUN
extern dispatch
extern kick_dog
;
;
;
extern _R0
extern _R1
extern _R2
extern _R3
extern _R4
extern _R5
extern _R6
extern _R7
extern bank1_R0
extern bank1_R1
extern bank1_R2
extern bank1_R3
extern bank1_R4
extern bank1_R5
extern bank1_R6
extern bank1_R7
;
; MODBUS command values
;
MODBUS_PRESET_REGS equ 16
MODBUS_READ_HOLDING_REGS equ 3
;
MAX_RW_REGS equ 16+1 ;maximum number of registers than can be read/written at one time
SIZEOF_MODBUS_BUFF equ (MAX_RW_REGS *2)+ 10 ;modbus rx/tx buffer
INIT_TAG equ 0aa55h ;tag stored in eeprom to see if we're initialised yet
CR equ 13
LF equ 10
CODE
;----------------------------------------------------------------------------
;
;
; process modbus. decodes the modbus packet in modbus_buff
; and performs the required command
; ** assumes the rx code has checked the modbus address **
; zaps:most regs!
;
;----------------------------------------------------------------------------
process_modbus
jb MODBUS_TYPE,pm_c ;if modbus_type is ascii then we don't need to check the CRC
;
; modbus transport is RTU, check the CRC before continuing
;
mov r0,#modbus_buff
mov a,bank1_R7 ;get the rx packet size
mov bank1_R7,#0 ;reset the packet size
cjne a,#7,pm_1
pm_1 jc pm_z ;ignore packet if it is too small
clr c
subb a,#2 ;-2 for CRC bytes
mov r6,a
lcall calculate_crc
jz pm_c ; if CRC was ok - process the packet
pm_z
ljmp L72 ;bad crc-skip the packet
pm_c
setb RX_PKT ;flash asterisk on the lcd for good rx pkts
mov r0,#modbus_buff+1
mov a,@r0 ;get the modbus command
cjne a,#MODBUS_READ_HOLDING_REGS,L59
;
; read holding regs command
;
mov r0,#modbus_buff+3 ;get the starting reg#
mov a,@r0
mov r2,a
mov r0,#modbus_buff+5 ;get # of regs
mov a,@r0
mov r5,a
clr c
subb a,#MAX_RW_REGS
jc pm_11
ljmp L62 ;if too many registers requested
pm_11
mov a,r5
add a,r5 ;register count times 2
mov r0,#modbus_buff+2
mov @r0,a ;set the reply byte count
inc r0
;
; copy mregs[] into the transmit buffer
;
pm_2
pm2_2
mov a,r2
lcall read_modbus_reg ;A has the modbus register#
jnz L51 ;if register error
mov a,r7
mov @r0,a ;store high
inc r0
mov a,r6
mov @r0,a ;store low
inc r0
inc r2 ;next register#
djnz r5,pm2_2 ;loop until regs copied
pm_3:
clr c
mov a,r0
subb a,#modbus_buff ;calc message length
sjmp send_modbus ;& send it
L51:
;
; register request exceeded the # of registers
;
mov r0,#modbus_buff+1
mov a,@r0 ;get modbus cmd
orl a,#80h ;set error flag
mov @r0,a
mov r0,#modbus_buff+2
mov a,#2 ;bad address
mov @r0,a
mov a,#4 ;length =4 including the LRC
sjmp send_modbus
L59
cjne a,#MODBUS_PRESET_REGS,L44
;
; Modbus preset registers command
;
mov r0,#modbus_buff+3
mov a,@r0 ;get the starting reg#
mov r2,a ;into R2
prc_4
mov r0,#modbus_buff+5
mov a,@r0 ;get # of regs
mov r5,a ;into R5
clr c
subb a,#MAX_RW_REGS
jnc L62 ;if too many registers requested
mov a,r5
add a,r2 ;add + count
clr c
subb a,#NUM_MODBUS_REGS
jnc L62
prc_1
mov a,r5 ;test reg count
jz prc_3 ;if all done
prc_2
mov r0,#modbus_buff+7 ;r0 ->modbus_buff[7]
;
; get the register from the rx buffer into R7:R6
;
mov a,@r0
mov r7,a ;reg high
inc r0
mov a,@r0
mov r6,a ;reg low
inc r0
mov a,r2 ;get modbus reg#
lcall write_modbus_reg
;
; next!!
;
inc r2
djnz r5,prc_1 ;count--
prc_3
mov a,#6 ;set reply pkt length
sjmp send_modbus
;
; bad register passed
;
L62
mov r0,#modbus_buff+1
mov a,@r0
orl a,#080h ;set error flag
mov @r0,a
inc r0
mov a,#2 ;bad address
mov @r0,a
mov a,#4 ;pkt length =4 inc LRC
sjmp send_modbus
;
; catch all for command not supported
;
L44
mov r0,#modbus_buff+1
mov a,@r0
orl a,#080h ;set error flag
mov @r0,a
inc r0
mov a,#1 ;bad command
mov @r0,a
mov a,#4 ;pkt length =4 inc LRC
;
; send the modbus data out
; A has the packet length
; modbus data is assumed to be in modbus_buff
;
send_modbus
orl a,a
jz L72 ;if length ==0, skip transmit
jb MODBUS_TYPE,send_ascii
push a
mov r6,a ;packet length
mov r0,#modbus_buff
call calculate_crc
pop a
add a,#2 ;+crc bytes
;
; use MODBUS RTU as the packet transport
;
clr ea ;no interrupts
dec a ;-1
clr EN_485 ;set 485 buffer to tx
mov bank1_R6,a
mov bank1_R0,#modbus_buff+1
mov bank1_R2,#1 ;set tx state
mov r0,#modbus_buff ;get the first character
mov a,@r0
mov SBUF,a ;and send it to start the tx interrupts
setb ea
sjmp L72
;
; use MODBUS ascii as the packet transport
;
send_ascii
clr EN_485 ;set 485 buffer to tx
inc a ;+1 on the packet size for the LRC
mov bank1_R6,a ;set the packet length
mov bank1_R0,#modbus_buff
mov bank1_R2,#0 ;clear tx state
mov bank1_R4,#0 ;clear LRC
mov SBUF,#':' ;send the start token (fires the tx interrupt)
clr TI
L72
L42
L41
clr TASK0_RUN ;reset task request
mov bank1_R3,#0 ;reset the rx state
ljmp dispatch
;----------------------------------------------------------------------------
;
; write MODBUS register
; entry:
; R7:R6 has the modbus register value to write
; A has the MODBUS register#
; returns: A == 0 if ok, A ==1 if bad register#
; zaps: A,PSW
;
;----------------------------------------------------------------------------
write_modbus_reg
push dph
push dpl
push _R0
push b
cjne a,#NUM_MODBUS_REGS,wmr_1 ;test for max reg#
wmr_1
jnc wmr_bad ;if reg# >=NUM_MODBUS_REGS
;
; dptr->modbus_options_table
;
mov b,#SIZEOF_MB
mul ab ;calc table offset
mov dptr,#modbus_options_table
add a,dpl
mov dpl,a
mov a,b
addc a,dph
mov dph,a ;dptr->modbus_options_table[reg#]
;
; check the register options
;
mov a,#MB_FLAGS
movc a,@a+dptr ;get the options byte
jnb a.MB_EEPROM,wmr_2
;
; EEprom option is set, write the register to eeprom
;
call kick_dog ; keep the dog happy whilst we write to eeprom
mov a,#MB_OFFSET
movc a,@a+dptr ; get the eeprom addr
clr c
rrc a ; calc the page addr
clr c
rrc a
mov EADRL,a ;set the page address
mov ECON,#01h ;read page -'cos we do a read_modify_write
jc wmr_11 ; if we're writing the hi two bytes in the page
;
; write the two low bytes in the page
;
mov a,R7
mov EDATA1,a
mov a,R6
mov EDATA2,a
sjmp wmr_12
;
; write the two low bytes in the page
;
wmr_11
mov a,R7
mov EDATA3,a
mov a,R6
mov EDATA4,a
;
; erase the eeprom page, then write it
;
wmr_12
mov ECON,#05h ;erase page (we sleep for 2mS)
mov ECON,#02h ;write the page (we sleep for 250uS)
sjmp wmr_3
;
; modbus register is in ram
;
wmr_2
mov a,#MB_OFFSET
movc a,@a+dptr
mov r0,a ;r0-> modbus register in ram
mov a,r7
mov @r0,a ;store hi byte
inc r0
mov a,r6
mov @r0,a ;store low byte
wmr_3
clr a
wmr_x
pop b
pop _R0
pop dpl
pop dph
ret
wmr_bad
mov a,1 ;return a bad status
sjmp wmr_x
;----------------------------------------------------------------------------
;
; read MODBUS register
; entry:
; A has the MODBUS register#
; returns: A == 0 if ok, A ==1 if bad register#, R7:R6 with the modbus reg value
; zaps: A,PSW
;
;----------------------------------------------------------------------------
read_modbus_reg
push dph
push dpl
push _R0
push b
cjne a,#NUM_MODBUS_REGS,rmr_1 ;test for max reg#
rmr_1
jnc rmr_bad ;if reg# >=NUM_MODBUS_REGS
;
; check the register options
;
mov b,#SIZEOF_MB
mul ab ;calc table offset
mov dptr,#modbus_options_table
add a,dpl
mov dpl,a
mov a,b
addc a,dph
mov dph,a ;dptr->modbus_options_table[reg#]
mov a,#MB_FLAGS
movc a,@a+dptr ;get the options byte
jnb a.MB_EEPROM,rmr_2
;
; EEprom option is set, read the register from eeprom
;
mov a,#MB_OFFSET
movc a,@a+dptr ; get the eeprom addr
clr c
rrc a ; calc the page addr
clr c
rrc a
mov EADRL,a ;set the page address
mov ECON,#01h ;read page
jc rmr_11 ;if we're reading the hi two bytes in the page
;
; read the low two bytes from the eeprom page
;
mov r7,EDATA1
mov r6,EDATA2
sjmp rmr_3
;
; read the hi two bytes from the eeprom page
;
rmr_11
mov r7,EDATA3
mov r6,EDATA4
sjmp rmr_3
;
; modbus register is in ram
;
rmr_2
mov a,#MB_OFFSET
movc a,@a+dptr
mov r0,a ;r0-> modbus register in ram
mov a,@r0
mov r7,a ;read hi byte
inc r0
mov a,@r0
mov r6,a ;read low byte
rmr_3
clr a
rmr_x
pop b
pop _R0
pop dpl
pop dph
ret
rmr_bad
mov a,1 ;return a bad status
sjmp rmr_x
;----------------------------------------------------------------------------
;
;
; called on power-up to initialise the modbus registers
; reads all the values from the modbus_options_table default values
; into the eeprom if the INIT_TOKEN is not set and reads the defaults
; into the ram based registers
;
;----------------------------------------------------------------------------
cold_init
mov r5,#NUM_MODBUS_REGS
mov dptr,#modbus_options_table
ci_loop
mov a,#MB_FLAGS
movc a,@a+dptr ;get the options var
jb a.MB_EEPROM,ci_2 ;if an eeprom based register, skip as no init required
;
;
;
mov a,#MB_OFFSET
movc a,@a+dptr
mov r0,a ;r0->ram modbus register
mov a,#MB_DEFAULT
movc a,@a+dptr
mov @r0,a ;store the hi default value
inc r0
mov a,#MB_DEFAULT+1
movc a,@a+dptr
mov @r0,a ;store the low default value
ci_2
mov a,#SIZEOF_MB
add a,dpl
mov dpl,a
mov a,#0
addc a,dph
mov dph,a ;->next record
djnz r5,ci_loop
;
; test INIT_TOKEN for the correct value. If not, load the eeprom with the default values from the modbus_options_table
;
mov EADRL,#<INIT_TOKEN
mov ECON,#1
mov a,EDATA1
cjne a,#>INIT_TAG,ci_do
mov a,EDATA2
cjne a,#<INIT_TAG,ci_do
ret
;if init tag was valid
ci_do
mov r5,#NUM_MODBUS_REGS
mov dptr,#modbus_options_table
mov r2,#0 ;modbus register #1
ci_1
mov a,#MB_FLAGS
movc a,@a+dptr
jnb a.MB_EEPROM,ci_3 ;skip init if a ram based register
;
; eeprom based register...init it.
;
mov a,#MB_DEFAULT
movc a,@a+dptr
mov r7,a
mov a,#MB_DEFAULT+1
movc a,@a+dptr
mov r6,a
push dph
push dpl
mov a,r2 ;get modbus reg#
lcall write_modbus_reg
pop dpl
pop dph
;
; next register..
;
ci_3
inc r2
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
|
| Topic | Author | Date |
| Modubus code... Russell Bull | 01/01/70 00:00 | |
| Life's like that! | 01/01/70 00:00 | |
| Tell me more... | 01/01/70 00:00 | |
| commercial application... Yes! | 01/01/70 00:00 | |
| Modbus code | 01/01/70 00:00 | |
| Would have been great if it was in C | 01/01/70 00:00 | |
| Free is what free gets | 01/01/70 00:00 | |
| Thanks anyway | 01/01/70 00:00 | |
| modbus in c | 01/01/70 00:00 | |
| Read the above. | 01/01/70 00:00 | |
| yes, but | 01/01/70 00:00 | |
| I dont mind... | 01/01/70 00:00 | |
| Happiness | 01/01/70 00:00 | |
Oh! yes great joy..... | 01/01/70 00:00 |



