diff --git a/MBPROTO b/MBPROTO index 07c7e16..1bf5782 100644 --- a/MBPROTO +++ b/MBPROTO @@ -73,14 +73,6 @@ NVM 1- 2* ( [ tab ] ) LITERAL + ; - \ MB loop (xt) from mbp1 to mbp1+mbp2 - : mbloop ( xt -- ) - mbp1 mbp2 OVER + SWAP DO - ( xt ) I OVER EXECUTE - LOOP - ( xt ) DROP - ; - \ true if n in range mbp1,mbp2, else false and MODBUS EC=2 : mbrange? ( n -- b ) -1 SWAP mbp1 mbp2 + - 0< IF @@ -88,15 +80,24 @@ NVM THEN ; - \ Quantity of bits / 8, if the remainder is different of 0 => N = N+1 - : N* ( -- N* ) - 1- 8 / 1+ + \ MB loop (xt) from mbp1 to mbp1+mbp2 + : mbloop ( xt -- ) + mbp1 mbp2 OVER + SWAP DO + ( xt ) I OVER EXECUTE + LOOP + ( xt ) DROP ; - \ MB looped bit read with action xt, build response (FC01, FC02) - : mbrbits ( xt -- ) - ( xt ) mbp2 N* DUP txc+ \ response: number of bytes - ( xt N* ) 1- FOR 0 txc+ NEXT \ init packed bits in response with 0 + \ MB looped bit read with action xt, build response (FC01, FC02, FC03, FC04) + : mbread ( xt bpu -- ) + ( xt bpu ) DUP mbp2 * ( bpu bits ) + \ N* as "quantity of bits / 8, if the remainder is different of 0 => N=N+1" + ( bits ) 1- 8 / 1+ ( N* ) DUP txc+ + ( bpu N* ) SWAP ( bpu ) 1 = IF + ( N* ) 1- FOR 0 txc+ NEXT \ init bytes in response with 0 + ELSE + ( N* ) DROP \ not a bitfield, no memory init + THEN ( xt ) mbloop ; diff --git a/MBSERVER b/MBSERVER index 5e67ce7..5eece14 100644 --- a/MBSERVER +++ b/MBSERVER @@ -29,52 +29,48 @@ NVM \ compile to Flash memory from here on #require BF! #require BF@ + VARIABLE coils COILCELLS 1- 2* ALLOT + VARIABLE inputs INPUTCELLS 1- 2* ALLOT + VARIABLE holding HOLDINGCELLS 1- 2* ALLOT \ --- FC01 "Read Coils" - VARIABLE coils COILCELLS 1- 2* ALLOT - \ FC01 Read Coils transfer :NVM ( i -- ) ( i ) coils OVER ( i a i ) BF@ ( i b ) SWAP mbp1 - ( b i0 ) txbuf 3 + SWAP ( b a i0 ) LEBF! - ;RAM ALIAS FC01ACT NVM + ;RAM ALIAS FC01ACT \ FC01 handler :NVM ( -- ) [ COILCELLS 16 * ] LITERAL mbrange? IF - [ ' FC01ACT ] LITERAL ( xt ) mbrbits + [ ' FC01ACT ] LITERAL 1 ( xt bpu ) mbread THEN ;NVM ( xt ) 1 FC>XT ! \ --- FC02 "Read Discrete Inputs" - VARIABLE inputs INPUTCELLS 1- 2* ALLOT \ FC02 input register transfer :NVM ( i -- ) ( i ) inputs OVER ( i a i ) BF@ ( i b ) SWAP mbp1 - ( b i0 ) txbuf 3 + SWAP ( b a i0 ) LEBF! - ;RAM ALIAS FC02ACT NVM + ;RAM ALIAS FC02ACT \ FC02 handler :NVM ( -- ) [ INPUTCELLS 16 * ] LITERAL mbrange? IF - [ ' FC02ACT ] LITERAL ( xt ) mbrbits + [ ' FC02ACT ] LITERAL 1 ( xt bpu ) mbread THEN ;NVM ( xt ) 2 FC>XT ! \ --- FC03 "Read Holding Registers" - VARIABLE holding HOLDINGCELLS 1- 2* ALLOT - - \ dummy - : mbread ; \ FC03 holding register iterated transfer - :NVM ( i -- 1 ) + :NVM ( i -- ) ( i ) DUP 0 EECELLS WITHIN IF 2* EESTART + @ ELSE @@ -83,8 +79,7 @@ NVM \ compile to Flash memory from here on ELSE ( i ) DROP 0 THEN - THEN - tx+ 1 ( inc ) + THEN tx+ ;NVM ( xt ) \ FC03 handler @@ -96,8 +91,9 @@ NVM \ compile to Flash memory from here on \ --- FC04 "Read Input Registers" \ FC04 input register iterated transfer - :NVM ( i -- 1 ) - 2* inputs + @ tx+ 1 ( inc ) + :NVM ( i -- ) + 2* inputs + @ + tx+ ;NVM ( xt ) \ FC04 handler diff --git a/README.md b/README.md index 46caa44..0296f7e 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ It's easy to build custom targets, e.g. using the $0.80 [MINDEV board](https://g ![MINDEV](https://camo.githubusercontent.com/82bd480f176951de9a469e134f543a6570f48597/68747470733a2f2f616530312e616c6963646e2e636f6d2f6b662f485442314e6642615056585858586263587058587136785846585858362f357063732d6c6f742d53544d3853313033463350362d73797374656d2d626f6172642d53544d38532d53544d382d646576656c6f706d656e742d626f6172642d6d696e696d756d2d636f72652d626f6172642e6a70675f323230783232302e6a7067) -When using PB5 for RS485 direction control (-> `BUSCTRL`) the C0135 code can be used unchanged. +When using PB5 for RS485 direction control (-> `BUSCTRL`) the C0135 code can be used (refer to the [C0135 STM8 eForth Wiki page][C0135]). ## Supported MODBUS Function Codes @@ -47,10 +47,10 @@ When using PB5 for RS485 direction control (-> `BUSCTRL`) the C0135 code can be FC | Description | Support -|-|- -**1**| **Read Coils** | implemented +**1** | **Read Coils** | implemented **2** | **Read Discrete Inputs** | implemented -3 | Read Holding Registers | partial -4 | Read Input Registers | partial +**3** | **Read Holding Registers** | implemented +**4** | **Read Input Registers** | implemented **5** | **Write Single Coil** | implemented **6** | **Write Single (Holding) Register** | implemented **15** | **Write Multiple Coils** | implemented diff --git a/test/FC01 b/test/FC01 index bbe8aaf..7bd4f88 100644 --- a/test/FC01 +++ b/test/FC01 @@ -22,9 +22,9 @@ NVM #require MBDUMP #require BF! #require BF@ - - 8 CONSTANT COILBYTES - VARIABLE coils COILBYTES 2- ALLOT + + 4 CONSTANT COILCELLS + VARIABLE coils COILCELLS 1- 2* ALLOT \ FC01 Read Coils transfer :NVM ( i -- ) @@ -39,10 +39,10 @@ NVM :NVM ( -- ) \ write register address and value to the console ." Read coil bits: A:" mbp1 . ." Q:" mbp2 . CR - ." bits to coils end:" COILBYTES 8 * mbp1 mbp2 + - . CR + ." bits to coils end:" COILCELLS 16 * mbp1 mbp2 + - . CR - COILBYTES 8 * mbrange? IF - [ ' FC01ACT ] LITERAL ( xt ) mbrbits + COILCELLS 16 * mbrange? IF + [ ' FC01ACT ] LITERAL 1 ( xt bpu ) mbread THEN ;NVM ( xt ) 1 FC>XT ! diff --git a/test/FC04 b/test/FC04 new file mode 100644 index 0000000..85022bd --- /dev/null +++ b/test/FC04 @@ -0,0 +1,62 @@ +\ Minimal MODBUS server with FC04 "Read Input Registers" handler +\ Features: +\ - prints debug infos to the console +\ - interactive tests from the console, e.g. set inputs manually "7 inputs !" + +\ check if the MODBUS protocol core is already present +\ hint: the development cycle will be faster if you PERSIST it +#require MBPROTO + +\ Resetting the FC handler table can be helpful for development +#require WIPE +#require MBRESET +MBRESET \ Reset the MODBUS Function Code table + +#require ALIAS +#require :NVM +#require 'IDLE +#require .OK + +NVM + +#require MBDUMP +#require BF! +#require BF@ + + 4 CONSTANT INPUTCELLS + VARIABLE inputs INPUTCELLS 1- 2* ALLOT + + \ --- FC04 "Read Input Registers" + + \ FC04 input register iterated transfer + :NVM ( i -- 1 ) + DUP . + 2* inputs + @ tx+ + ;NVM ( xt ) + + \ FC04 handler + :NVM ( -- ) + \ write register address and value to the console + ." Read input registers: A:" mbp1 . ." Q:" mbp2 . CR + + [ ( xt xth ) SWAP ] LITERAL 16 ( xt bpu ) mbread + ;NVM ( xth ) 4 FC>XT ! + + + \ custom default action handler + : showfc ( -- ) + rxbuf C@ ." FC:" . CR + 1 MBEC \ set error code + ; + + : init ( -- ) + 0 UARTISR \ init UART handler w/ default baud rate + 1 mbnode ! \ set node address + [ ' showfc ] LITERAL mbdef ! \ FC default action (optional feature) + [ ' MBDUMP ] LITERAL mbact ! \ show buffers (debug demo) + [ ' MBPROTO ] LITERAL 'IDLE ! \ run MB protocol handler as idle task + .OK + ; + + ' init 'BOOT ! +WIPE RAM diff --git a/test/FC15 b/test/FC15 index 676425d..36ebbec 100644 --- a/test/FC15 +++ b/test/FC15 @@ -22,29 +22,29 @@ NVM #require MBDUMP #require BF! #require BF@ - - 8 CONSTANT COILBYTES - VARIABLE coils COILBYTES 2- ALLOT + + 4 CONSTANT COILCELLS + VARIABLE coils COILCELLS 1- 2* ALLOT \ --- FC15 "Write Multiple Coils" \ FC15 Write Multiple Coils write transfer :NVM ( i -- ) \ uncomment "DUP ." lines to check index (this can cause a timeout) - \ DUP . + DUP . ( i ) rxbuf 7 + OVER mbp1 - ( i a i0 ) - \ DUP . CR + DUP . CR LEBF@ SWAP ( b i ) - coils SWAP ( b a i ) BF! + coils SWAP ( b a i ) BF! ;RAM ALIAS FC15ACT NVM \ FC15 handler :NVM ( -- ) \ write register address and value to the console ." Write register: A:" mbp1 . ." Q:" mbp2 . CR - ." Write bits from end:" COILBYTES 8 * mbp1 mbp2 + - . CR + ." Write bits from end:" COILCELLS 16 * mbp1 mbp2 + - . CR - COILBYTES 8 * mbrange? IF + COILCELLS 16 * mbrange? IF [ ' FC15ACT ] LITERAL ( xt ) mbloop MBWR \ MODBUS write response THEN