ModBusSlave
Alarus
Posts: 48
Wrote to ModBusSlave object:cool:.
Supports 0x03 0x04 0x06 0x10 function.
Mode ASCII or RTU.
http://obex.parallax.com/object/706
Supports 0x03 0x04 0x06 0x10 function.
Mode ASCII or RTU.
http://obex.parallax.com/object/706
Comments
Many thanks!
Mickster
In this object, he has a variable for number of registers, and then their locations and sizes. Why would there be more than one? How would then be referenced? In my case, I have several status variables that I need to allow to be read over MODBUS (command 0x04) and I am wondering if I can somehow set a pointer to the existing locations, or need to write a routine to copy them over to a new MODBUS specific location...
I think I will just have to establish my own array of longs, point to it in his setup, and then update the values in my software periodically...
MODBUS doesn't have some typical set of four types of registers, or something, does it? So, depending on the command, it would use a different set of registers?
You can create as many registers as you need.
You can read from your modbus registers.
You can write in your modbus registers.
Make modbus register is read-only: constrained by the limits in registers REG_MIN and REG_MAX at a value REG_VAL.
REG_VAL[] stores the register values. REG_MIN[] and REG_MAX[] define legal limits for these values. So, to see the registers, you basically read or write to the REG_VAL[] array.
All the processing for the bus itself are going on auto-magically in the assembly code. It is monitoring the bus, receiving commands, and then responding to them. If so, then this is a nice piece of code, although a little hard to interpret.
Thank you very much!!!!
Yes, it is.
Was really hoping to not get into assembler for this project... ;-)
Function_0x03 mov lenght,#3
mov crc,#$03
add ptr,#3
call #READ
cmp data,reg_quantity wz,wc
if_be cmp data,#$7D wz,wc
if_be cmpsub data,#$01 wc
if_c add data,#$01
if_nc jmp #Exception
mov count,data
shl data,#1
add lenght,data
wrbyte data,ptr
mov crc,#$02
sub ptr,#2
call #READ
mov reg,data
add reg,count
' cmp reg,reg_addr_max wz,wc
' if_be cmpsub data,reg_addr_min wc
' if_c add data,reg_addr_min
' if_nc jmp #Exception
' sub data,reg_addr_min
shl data,#1
add data,ptr_registers
mov reg,data
add ptr,#1
:loop rdword data,reg
call #WRITE
I think these are the lines to remove to remove the size test...
And Cut function 0x06 0x10:
REG_VAL [0] => MODBUS Address = 7
REG_VAL [1] => MODBUS Address = 8
REG_VAL [2] => MODBUS Address = 9
Set up a little debug routine that basically used TRANSMIT to send out a test byte, and then put it into various places in the code. It appears that the problem occurs around here:
RTU mov ptr,ptr_buffer 'Clear buffer pointer
xor lenght,lenght 'Clear lenght
'mov data2,#85
'call #DEBUG2 '**************DEBUG******************
test reg_mode,#%0010000 wz 'Test invert PIN_RX
mov count,reg_cnt_run 'Load counter T3.5
test msk_rx,ina wc 'Waiting for signal change at pin RX
if_z_eq_c djnz count,#$-1 'Decrement counter
if_z_ne_c jmp #RTU 'If there was a change RX signal and the counter is not zero, then the move starting point
mov data2,#2
call #DEBUG '**************DEBUG******************
Next_Byte call #RECIVE 'Recive byte
If I leave that DEBUG2 call in, I get continuous sends of data. When it is commented out, the code never reaches the second DEBUG call. It looks like the T3.5 test never passes. I looked at my incoming signal, and it looks like it doesn't have the full 3.5b preamble, just a standard start bit, so I tried modifying reg_cnt_run from 77 to 22 to make it just a single bit width. Still no joy, execution never seems to get past that jump back to RTU. Any ideas? Does that test for inverted PIN_RX actually do anything? I even tried setting that, as well...
You can view a piece of code which initializes MODBUS?
and dira,NOT msk_rx ' MAKE SURE RX IS IN INPUT MODE
will make sure the pin is in input mode? I am starting to grasp at straws here...
Use the tag "code" in the advanced mode.
RX pin is no longer where it is not running?
[code]
PUB Start(MBS_Structure) : okay
{{
Start ModBus protocol - starts a cog.
Parameters: MBS_Structure = Pointer to a structure ModBus
MBS Structure: MBS_Reg_Address = Slave address
MBS_Reg_Quantity = Quantity registers
MBS_Reg_Offset = Offset starting address registers
MBS_Reg_Mode = Bitwise mode configuration variable, see mode bit description below
Mode bit 0 = Even/Odd parity bit
Mode bit 1 = One/Two stop bits (if use two stop bits then parity bit is disabled)
Mode bit 2 = RS-232/RS-485 interface (use pin RT)
Mode bit 3 = RTU/ASCII coding (8/7 Data bits)
Mode bit 4 = Invert pin RX
Mode bit 5 = Invert pin TX
Mode bit 6 = Open-drain/source pin TX
MBS_Reg_Baudrate = Baud rate to transmit bits at (maximum 230400 bps @ 80 MHz)
MBS_Pin_RX = Propeller pin to set up as RX-ing pin. Range = 0 - 31
MBS_Pin_TX = Propeller pin to set up as TX-ing pin. Range = 0 - 31
MBS_Pin_RT = Propeller pin to set up as RT-ing pin. Range = 0 - 31
MBS_Ptr_Registers = A pointer to an array of data registers
MBS_Ptr_Minimum = A pointer to an array the minimum limits of working registers
MBS_Ptr_Maximum = A pointer to an array the maximum limits of working registers
Return: Numeric value of the cog(1-8) that was started, false(0) if no cog is available.
Example usage: MBS.Start(@MBS_Reg_Address)
}}
Stop 'Make sure the driver isnt already running
reg_address := long[MBS_Structure][0]
reg_quantity := long[MBS_Structure][1]
reg_addr_min := long[MBS_Structure][2]
reg_addr_max := long[MBS_Structure][2] + long[MBS_Structure][1]
reg_mode := long[MBS_Structure][3]
reg_tick := clkfreq / long[MBS_Structure][4] #> 112 'Number of clock ticks per bit for the desired baudrate
' change reg_cnt_run multiplier from 77 to 20 for testing
reg_cnt_run := reg_tick * 18 'Load delay T3.5
reg_cnt_run := reg_cnt_run >> 4
reg_cnt_end := reg_tick * 33 'Load delay T1.5
reg_cnt_end := reg_cnt_end >> 4
msk_rx := |<long[MBS_Structure][5]
msk_tx := |<long[MBS_Structure][6]
msk_rt := |<long[MBS_Structure][7]
ptr_registers := long[MBS_Structure][8]
'ptr_minimum := long[MBS_Structure][9]
'ptr_maximum := long[MBS_Structure][10]
ptr_buffer := @buffer
okay := cog := cognew(@ENTRY, 0) + 1
DAT
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
' Assembly language ModBus protocol '
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
org 0
ENTRY mov add_cnt,reg_tick
shr reg_tick,#1
test reg_mode,#00100 wc 'Init PIN_RT according to mode
if_c or dira,msk_rt
test reg_mode,#00000 wz 'Init PIN_TX according to mode
test reg_mode,#00000 wc
if_z_ne_c or outa,msk_tx
if_z or dira,msk_tx
and dira,NOT msk_rx ' MAKE SURE RX IS IN INPUT MODE
test reg_mode,#01000 wc 'Init bits according to mode
if_nc add bits,#1
sub shift,bits
Error test reg_mode,#01000 wc 'Selecting the RTU / ASCII
if_c jmp #ASCII
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
RTU mov ptr,ptr_buffer 'Clear buffer pointer
xor lenght,lenght 'Clear lenght
'mov data2,#85
'call #DEBUG2 '**************DEBUG******************
test reg_mode,#10000 wz 'Test invert PIN_RX
mov count,reg_cnt_run 'Load counter T3.5
test msk_rx,ina wc 'Waiting for signal change at pin RX
if_z_eq_c djnz count,#$-1 'Decrement counter
if_z_ne_c jmp #RTU 'If there was a change RX signal and the counter is not zero, then the move starting point
mov data2,#2
call #DEBUG '**************DEBUG******************
Next_Byte call #RECIVE 'Recive byte
cmp lenght,#256 wc 'Buffer is full?
if_nc jmp #Error 'If the buffer is full then the error
wrbyte data,ptr 'Write byte to buffer
add lenght,#1 'Increment lenght
add ptr,#1 'Increment pointer
mov count,reg_cnt_end 'Load counter T1.5
test msk_rx,ina wc 'Waiting for signal change at pin RX
if_z_eq_c djnz count,#$-1 'Decrement counter
if_z_ne_c jmp #Next_Byte 'If there was a change in the signal RX and the counter is not zero, then get the next byte
call #CRC16 'The calculation of CRC
cmp lenght,#3 wc 'Checking length
cmp crc,#0 wz 'Checking CRC
if_c_or_nz jmp #Error 'If you do not comply then the error
call #MODBUS 'Processing Modbus packet
cmp lenght,#0 wz 'Checking length
if_e jmp #RTU 'Next packet
call #CRC16 'The calculation of CRC
mov ptr,ptr_buffer 'Clear buffer pointer
:loop rdbyte data,ptr 'Read byte to buffer
add ptr,#1 'Increment pointer
call #TRANSMIT 'Transmit byte
djnz lenght,#:loop 'If the length is zero, then the finish transmit
mov data,crc 'Set data low crc byte
call #TRANSMIT 'Transmit byte
shr data,#8 'Set data high crc byte
call #TRANSMIT 'Transmit byte
jmp #RTU 'Next packet
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
CRC16 mov ptr,ptr_buffer 'Clear buffer pointer
mov count,lenght 'Load count
neg crc,#1 'Set crc $FFFFFFFF
shr crc,#16 'Set crc $0000FFFF
:loop rdbyte data,ptr 'Read byte to buffer
add ptr,#1 'Increment pointer
xor crc,data 'Calculate CRC
mov reg,#8
shr crc,#1 wc
if_c xor crc,poly
djnz reg,#$-2
djnz count,#:loop
CRC16_ret ret
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
ASCII mov ptr,ptr_buffer 'Clear buffer pointer
xor lenght,lenght 'Clear lenght
:loop call #RECIVE 'Recive byte
cmp data,#$3A wz 'Compare byte received with ":"
if_ne jmp #:loop 'Next byte
First_Byte call #RECIVE 'Recive byte
cmp data,#$0D wz 'Compare byte received with "CR"
if_e jmp #Last_Byte 'If it is to go to last byte
call #ASCII2HEX 'Convert ASCII to HEX
shl data,#4
mov reg,data
Second_Byte call #RECIVE 'Recive byte
call #ASCII2HEX 'Convert ASCII to HEX
or data,reg
cmp lenght,#256 wc
if_nc jmp #Error
wrbyte data,ptr 'Write byte to buffer
add lenght,#1 'Increment lenght
add ptr,#1
jmp #First_Byte
Last_Byte call #RECIVE
cmp data,#$0A wz 'Compare byte received with "CR"
if_ne jmp #Error
call #LRC 'Calculate LRC
cmp lenght,#3 wc 'Checking length
cmp crc,#0 wz 'Checking LRC
if_c_or_nz jmp #Error 'If you do not comply then the error
call #MODBUS 'Processing Modbus packet
cmp lenght,#0 wz 'Checking length
if_e jmp #ASCII 'Next packet
call #LRC 'Calculate LRC
mov data,#$3A 'Set data ":"
call #TRANSMIT 'Transmit byte
mov ptr,ptr_buffer 'Clear buffer pointer
:loop rdbyte data,ptr 'Read byte to buffer
add ptr,#1 'Increment pointer
mov reg,data 'Save data in reg
shr data,#4 'Set data high byte
call #HEX2ASCII 'Convert HEX to ASCII
call #TRANSMIT 'Transmit byte
mov data,reg 'Set data low byte
call #HEX2ASCII 'Convert HEX to ASCII
call #TRANSMIT 'Transmit byte
djnz lenght,#:loop 'If the length is zero, then the finish transmit
mov data,crc 'Transmit CRC
shr data,#4 'Set data high crc byte
call #HEX2ASCII 'Convert HEX to ASCII
call #TRANSMIT 'Transmit byte
mov data,crc 'Set data low crc byte
call #HEX2ASCII 'Convert HEX to ASCII
call #TRANSMIT 'Transmit byte
mov data,#$0D 'Set data "CR"
call #TRANSMIT 'Transmit byte
mov data,#$0A 'Set data "LF"
call #TRANSMIT 'Transmit byte
jmp #ASCII 'Next packet
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
ASCII2HEX cmp data,#$47 wc
if_c cmpsub data,#$30 wc
if_nc jmp #Error
cmp data,#$0A wc
muxnc $,$ wc,nr
if_c sub data,#$07
if_c cmp data,#$0A wc
if_c jmp #Error
ASCII2HEX_ret ret
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
HEX2ASCII and data,#$0F
cmp data,#$0A wc
add data,#$30
if_nc add data,#$07
HEX2ASCII_ret ret
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
LRC mov ptr,ptr_buffer 'Clear buffer pointer
mov count,lenght 'Load count
xor crc,crc 'Clear reg_crc
:loop rdbyte data,ptr 'Read byte to buffer
add ptr,#1 'Increment pointer
add crc,data 'Calculate CRC
djnz count,#:loop
neg crc,crc
and crc,#$FF
LRC_ret ret
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
RECIVE 'call #DEBUG '**************DEBUG******************
test reg_mode,#10000 wz
:loop test msk_rx,ina wc
if_z_eq_c jmp #:loop
mov new_cnt,reg_tick
add new_cnt,cnt
waitcnt new_cnt,add_cnt
test msk_rx,ina wc
if_z_eq_c jmp #Error
mov parity,reg_mode
and parity,#1
mov count,bits
:bit waitcnt new_cnt,add_cnt
test msk_rx,ina wc
if_nz muxnc $,$ wc,nr
if_c xor parity,#1
rcr data,#1
djnz count,#:bit
shr data,shift
waitcnt new_cnt,add_cnt
test msk_rx,ina wc
if_nz muxnc $,$ wc,nr
if_c mov count,#1
test reg_mode,#00010 wc
if_c mov parity,#1
xor count,parity wc
if_c jmp #Error
waitcnt new_cnt,#0
test msk_rx,ina wc
if_z_ne_c jmp #Error
RECIVE_ret ret
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
TRANSMIT test reg_mode,#00000 wz
or outa,msk_rt
mov parity,reg_mode
and parity,#1
or parity,#2
shl parity,#1
mov count,bits
mov new_cnt,add_cnt
test reg_mode,#00000 wc
if_z_and_c xor parity,#1
shr parity,#1 wc
if_z muxc outa,msk_tx
if_nz muxnc dira,msk_tx
add new_cnt,cnt
:bit test reg_mode,#00000 wc
waitcnt new_cnt,add_cnt
if_z_and_c xor data,#1
ror data,#1 wc
if_c xor parity,#1
if_z muxc outa,msk_tx
if_nz muxnc dira,msk_tx
djnz count,#:bit
waitcnt new_cnt,add_cnt
test reg_mode,#00010 wc
if_c mov parity,#3
test reg_mode,#00000 wc
if_z_and_c xor parity,#1
shr parity,#1 wc
if_z muxc outa,msk_tx
if_nz muxnc dira,msk_tx
waitcnt new_cnt,add_cnt
test reg_mode,#00000 wc
if_z_and_c xor parity,#1
shr parity,#1 wc
if_z muxc outa,msk_tx
if_nz muxnc dira,msk_tx
ror data,shift
waitcnt new_cnt,add_cnt
andn outa,msk_rt
TRANSMIT_ret ret
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
READ rdbyte parity,ptr
shl parity,#8
add ptr,#1
rdbyte data,ptr
or data,parity
sub ptr,#1
READ_ret ret
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
WRITE ror data,#8
wrbyte data,ptr
rol data,#8
add ptr,#1
wrbyte data,ptr
sub ptr,#1
WRITE_ret ret
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
MODBUS mov ptr,ptr_buffer
rdbyte data,ptr
cmp data,reg_address wz
if_ne cmp data,#$00 wz
if_ne jmp #Error
mov crc,#$01
add ptr,#1
rdbyte data,ptr
mov reg,#Exception
cmp data,#$03 wz
if_e mov reg,#Function_0x03
cmp data,#$04 wz
if_e mov reg,#Function_0x03
' cmp data,#$06 wz
' if_e mov reg,#Function_0x06
' cmp data,#$10 wz
' if_e mov reg,#Function_0x10
jmp reg
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
Exception mov ptr,ptr_buffer 'Clear buffer pointer
mov lenght,#3
add ptr,#1
rdbyte data,ptr
or data,#$80
wrbyte data,ptr
add ptr,#1
wrbyte crc,ptr
jmp #MODBUS_ret
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
Function_0x03 mov lenght,#3
mov crc,#$03
add ptr,#3
call #READ
cmp data,reg_quantity wz,wc
if_be cmp data,#$7D wz,wc
if_be cmpsub data,#$01 wc
if_c add data,#$01
if_nc jmp #Exception
mov count,data
shl data,#1
add lenght,data
wrbyte data,ptr
mov crc,#$02
sub ptr,#2
call #READ
mov reg,data
add reg,count
cmp reg,reg_addr_max wz,wc
if_be cmpsub data,reg_addr_min wc
if_c add data,reg_addr_min
if_nc jmp #Exception
sub data,reg_addr_min
shl data,#1
add data,ptr_registers
mov reg,data
add ptr,#1
:loop rdword data,reg
call #WRITE
add reg,#2
add ptr,#2
djnz count,#:loop
jmp #MODBUS_ret
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
{
Function_0x06 mov lenght,#6
mov crc,#$02
add ptr,#1
call #READ
cmp data,reg_addr_max wz,wc
if_be cmpsub data,reg_addr_min wc
if_c add data,reg_addr_min
if_nc jmp #Exception
sub data,reg_addr_min
shl data,#1
mov reg,data
add data,ptr_minimum
rdword lim_min,data
sub data,ptr_minimum
add data,ptr_maximum
rdword lim_max,data
sub data,ptr_maximum
mov crc,#$03
add ptr,#2
call #READ
cmp data,lim_max wz,wc
if_be cmpsub data,lim_min wc
if_c add data,lim_min
if_nc jmp #Exception
add reg,ptr_registers
wrword data,reg
jmp #Broadcast
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
Function_0x10 mov lenght,#6
mov crc,#$03
add ptr,#3
call #READ
cmp data,reg_quantity wz,wc
if_be cmp data,#$7B wz,wc
if_be cmpsub data,#$01 wc
if_c add data,#$01
if_nc jmp #Exception
mov count,data
shl count,#1
add ptr,#2
rdbyte data,ptr
cmp data,count wz
shr count,#1
if_ne jmp #Exception
mov crc,#$02
sub ptr,#4
call #READ
mov reg,data
add reg,count
cmp reg,reg_addr_max wz,wc
if_be cmpsub data,reg_addr_min wc
if_c add data,reg_addr_min
if_nc jmp #Exception
mov crc,#$03
add ptr,#5
sub data,reg_addr_min
shl data,#1
mov reg,data
test $,#1 wz
:loop add reg,ptr_minimum
rdword lim_min,reg
sub reg,ptr_minimum
add reg,ptr_maximum
rdword lim_max,reg
sub reg,ptr_maximum
call #READ
add lim_max,#1
cmp data,lim_max wc
if_c cmpsub data,lim_min wc
if_c add data,lim_min
if_nc test $,#0 wz
add reg,ptr_registers
wrword data,reg
sub reg,ptr_registers
add reg,#2
add ptr,#2
djnz count,#:loop
if_z jmp #Exception
}
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
Broadcast mov ptr,ptr_buffer
rdbyte data,ptr
cmp data,#$00 wz
if_e mov lenght,#0
MODBUS_ret ret
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
DEBUG
mov data_store,data
'add data2, #1
mov data,data2
call #TRANSMIT
mov data,data_store
DEBUG_ret ret
DEBUG2
mov data,count
call #TRANSMIT
DEBUG2_ret ret
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
poly long $A001
bits long $7
shift long $20
reg_address long 0
reg_quantity long 0
reg_addr_min long 0
reg_addr_max long 0
reg_mode long 0
reg_tick long 0
reg_cnt_run long 0
reg_cnt_end long 0
msk_rx long 0
msk_tx long 0
msk_rt long 0
ptr_registers long 0
'ptr_minimum long 0
'ptr_maximum long 0
ptr_buffer long 0
reg res 1
ptr res 1
crc res 1
data res 1
count res 1
lenght res 1
parity res 1
new_cnt res 1
add_cnt res 1
lim_min res 1
lim_max res 1
data_store res 1 'Store existing value in data
data2 res 1 'Value to send for debug
'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────'
{{
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────
Check you are using PIN 1 in other cog.
I even tried to compile and run the demo program, with the same results. :-(
I used the QuickStart to check the result.
Byte format: 8 E 1? You are using a pin RT?