Email: Password: Remember Me | Create Account (Free)

Back to Subject List

Old thread has been locked -- no new posts accepted in this thread
???
03/02/08 23:46
Read: times


 
#151767 - So where is the problem?
Responding to: ???'s previous message
You tell us 'it doesn't work' - what doesn't work? Is it the receive? Transmit? Do you really expect us to debug your complete code for you? I'd suggest you use tools like Modscan and Modsim (www.win-tech.com/html/modbus1.htm)to debug your code. I would expect once you start to localise the problem, the solution will be obvious.

Here are some routines to implement the core modbus features.

;
;
;       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  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  _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
;
;       Dallas 1 wire memory read/write routines
;
        extern  read_one_wire_serialno
        extern  read_one_wire_eeprom
        extern  write_one_wire_eeprom
        
;
;	MODBUS command values
;
MODBUS_PRESET_REGS		equ	16
MODBUS_READ_HOLDING_REGS 	equ	3
;
SIZEOF_MODBUS_BUFF	        equ	40	;modbus rx/tx buffer
MAX_RW_REGS                     equ     16+1    ;maximum number of registers than can be read/written at one time

INIT_TAG	                equ	0aa55h	;tag stored in eeprom to see if we're initialised yet

CR                              equ     13
LF                              equ     10
;
;	special Modbus register addresses to read/write the 1 wire memory device
;
MBREG_1WIRE_READ		equ	200
MBREG_1WIRE_WRITE		equ	220
        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
	mov     a,r2                    ;get modbus reg#
        anl     a,#0fch                 ;mask off the lower two bits
        cjne    a,#MBREG_1WIRE_READ,pm2_1
;
;       magic register number for Dallas 1 wire rom read!
;        
        mov     a,r2
        anl     a,#3                    ;get lower two bits for 1wire port address
;        mov     r4,a
        push    _R4
        push    _R2
        mov     r4,a
        lcall    read_one_wire_serialno
        pop     _R2
        pop     _R4
        jc      L51                     ;the read failed - return an error
        sjmp    pm_3                    ;things worked! send reply        
;        
        
        
pm2_1        
        mov     a,r2
        anl     a,#0fch                 ;mask off the lower two bits     
        cjne    a,#MBREG_1WIRE_WRITE,pm2_2
;
;       magic register number for Dallas 1 wire eeprom read!
;        
        mov     a,r2
        anl     a,#3                    ;get lower two bits for 1wire port address
        mov     r4,a
        push    _R2
        mov     r2,#31
        lcall    read_one_wire_eeprom
        pop     _R2
        jc      L51                     ;the read failed - return an error
        sjmp    pm_3                    ;things worked! send reply        
;        
        
        
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
	inc	a			;+1 for msg 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
        
        anl     a,#0fch                 ;clear lower 2 bits
        cjne    a,#MBREG_1WIRE_WRITE,prc_4            ;test for 'magic' modbus register for dallas 1 wire write
;
;       write the dallas DS2430 1 wire eeprom
;        
        mov     a,r2
        anl     a,#3                    ;get lower two bits for 1wire port address
        mov     r4,a

        mov	r0,#modbus_buff+7	;r0 ->modbus_buff[7] point to the data to write
        push    _R2
        mov     r2,#31                  ;# of bytesto write
        lcall   write_one_wire_eeprom
        pop     _R2
        jc      L62                     ;return error packet if fail
        sjmp    prc_3                   ;else return ack packet
;-

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+1
	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,#4    		;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
        mov     bank1_R6,a
        mov     bank1_R0,#modbus_buff
	mov	bank1_R2,#1		;set tx state        
        setb    ea
        sjmp    L72
        	
;
;       use MODBUS ascii as the packet transport
;        
send_ascii        
        clr	EN_485				;set 485 buffer to tx
	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,DPTR
;
;----------------------------------------------------------------------------
write_modbus_reg
	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
;	
        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
	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,DPTR
;
;----------------------------------------------------------------------------
read_modbus_reg
	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
	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++
        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. we keep the tx alive so the rx code can use it as a timer
;       in this instance we send a NULL char. The real world (RS485) doesn't see these
;       characters as the rs485 tx is disabled. 
;        
        setb    EN_485                  ;don't send anything to the outside!
        mov     SBUF,#0                 ;send a null char
        clr     TI
        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	



List of 12 messages in thread
TopicAuthorDate
MODBUS RTU for 8051/DS89C450            01/01/70 00:00      
   How to post source code            01/01/70 00:00      
   Formatted Source Code            01/01/70 00:00      
      Looks like translated PIC code!            01/01/70 00:00      
   So where is the problem?            01/01/70 00:00      
      More details...            01/01/70 00:00      
         How did you test your VC++ app...?            01/01/70 00:00      
         uC -> RTU Slave (OK) ; RTU Slave -> uC (FAIL)            01/01/70 00:00      
            RE:8 bit buffer            01/01/70 00:00      
               Problem?            01/01/70 00:00      
                  RE:8 bit buffer            01/01/70 00:00      
               RTU Communication is working!            01/01/70 00:00      

Back to Subject List