Skip to content

Instantly share code, notes, and snippets.

@TG9541
Created June 25, 2022 06:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TG9541/9a1137693abecdc8fcb4cc63b7dd0edb to your computer and use it in GitHub Desktop.
Save TG9541/9a1137693abecdc8fcb4cc63b7dd0edb to your computer and use it in GitHub Desktop.
Forth2020 User-Group 2022 #24 Talk: A Modular MODBUS Server with STM8 eForth

The talk in the Forth2020 User-Group Zoom meeting 2022 #24 about using STM8 eForth for MODBUS nodes is here on YouTube.

If you wish to replicate the demo you should clone the STM8 eForth MODBUS code on GitHub and follow the instructions there. The code below should be placed in the root folder of this cloned repository with the indicated names.

If you have questions, please either join the Forth2020 group or open a GitHub Issue in the stm8ef-modbus repository.

This the base code with the MODBUS protocol handler loaded first (save as base):

\ STM8EF-MODBUS base protocol code
 
 \ Show free memory for STM8 devices with 8K Flash memory
 NVM
   : FREE ( -- )
     CR ." Free NVM:" $a000 last 2+ @ - . ;
 RAM
 
 #require MBPROTO
 #require PERSIST
 #require WIPE
 PERSIST
 WIPE
 FREE

The MODBUS FC6 (Write Holding Register) demo handler is below. It uses the STM8 eForth console for debug messages (that's possible because the handler runs in the 'IDLE task and I either operate QModMaster or use the Forth console (save as fc6demo):

\ Minimal MODBUS server with a FC06 "Write Single Register" handler

#require MBPROTO

RAM
#require WIPE
#require MBRESET
MBRESET   \ Reset the MODBUS Function Code table

#require :NVM
#require 'IDLE

NVM
#require MBDUMP
  VARIABLE myreg
 
  \ --- FC06 handler "Write Single Register"
  :NVM  ( -- )
    \ set myreg as FC6 "HOLDING 0", write address and value to the console

    mbp1 0= IF         \ mbp1 contains the "holding register" address
      mbp2 myreg !   \ mbp2 contains the value
      ." myreg "
    ELSE
      ." register " mbp1 .
    THEN
    ." =" mbp2 . CR

    MBWR  \ acknowledge FC06
  ;NVM ( xt ) 6 FC>XT !   \ register the FC handler

  : showfc ( -- )
    rtbuf 1+ C@ ." FC:" . CR
    1 MBEC  \ set error code
  ;

  : init ( -- )
    0 myreg !

    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
    CR ." STM8EF-MODBUS FC6 handler demo"
    hi
  ;

  ' init 'BOOT !
WIPE RAM

The last demo, an IR LED lamp control] through MODBUS uses a version of the STM8 eForth IR-NEC LED lamp control demo slightly modified for the GPIOs available on the terminals of the C0135 relay board (save as irctrl):

\ "W28" IR remote control with NEC protocol for a Chinese "RGBW" LED lamp
\ using an STM8S Low Density device (e.g. STM8S103)
\ IR LED cathode connected to PC6 (TIM1_CH1)

\ C0135 terminal IN4 is PC6, [TIM1_CH1] is an "alternate function
#require WIPE
\res MCU: STM8S103
\res export OPT2
#require OPT!
OPT2 C@ 1 (  )  OR OPT2 OPT!
WIPE

\res export TIM1_CR1 TIM1_IER TIM1_SR1 TIM1_BKR TIM1_CCER1
\res export TIM1_ARRH TIM1_CCMR1 TIM1_CCR1H INT_TIM1 OPT2

\ TIM1 bit constants
0 CONSTANT UIE
0 CONSTANT CC1E
7 CONSTANT MOE

#require WIPE
#require :NVM
#require ALIAS
#require ]B!
#require >REL

\ ( carry-flag ) IF with relative addressing
: ]CFIF ( -- ) $24 C, ] >REL ;  \ JRNC rel

NVM

VARIABLE mrk    \ mark duration in 1/38kHz pulses
VARIABLE spc    \ spc duration in 1/38kHz pulses
VARIABLE bcnt   \ bit counter
VARIABLE sreg 2 ALLOT  \ IR send register for ISR

\ IR-NEC pattern ISR triggered by TIM1 Update Event
:NVM
  SAVEC
  [ 0 TIM1_SR1 UIE ]B!  \ clear interrupt

  mrk @ ?DUP IF
    [  1 TIM1_CCER1 CC1E ]B!  \ mark: enable 38 kHz PWM
    1- mrk !
  ELSE
    [  0 TIM1_CCER1 CC1E ]B!  \ space: disable 38 kHz PWM 
    spc @ ?DUP IF
      1- spc !
    ELSE
      bcnt @ ?DUP IF
        1- bcnt !
        24 mrk !  [  \ 0.65 ms mark
          \ rotate right bit pattern - NEC protocol is LSB first
          $36 C, sreg 3 + C,  \ RRC  sreg+3 ; (zero page addressing)
          $36 C, sreg 2 + C,  \ RRC  sreg+2
          $36 C, sreg 1 + C,  \ RRC  sreg+1)
          $36 C, sreg     C,  \ RRC  sreg
        ]CFIF
          60 spc !   \ carry flag set: spc 1.6ms
        ELSE
          19 spc !   \ carry flag clr: spc 0.5ms
        THEN
      THEN
    THEN
  THEN
  IRET
[ OVERT ( xt ) INT_TIM1 !


\ Init TIM1 for 38kHz ticker interrupt and 50 duty PWM output
: IRINIT ( -- )
  $60 TIM1_CCMR1 C!        \ PWM mode 1
  421 TIM1_ARRH 2C!        \ 38kHz @ 16MHz HSI
  210 TIM1_CCR1H 2C!       \ 50% duty cycle
  [ 1 TIM1_BKR MOE ]B!     \ main output enable
  [ 1 TIM1_CCER1 CC1E ]B!  \ Capture Compare output enable (CC1E)
  [ 1 TIM1_CR1 UIE ]B!     \ enable timer
  [ 1 TIM1_IER UIE ]B!     \ enable timer update interupt
;


\ turn c into MSB, complement LSB
:NVM ( c - n ) [
  \ 256 OVER * SWAP NOT 255 AND OR
  $E601 ,         \ LD   A,(1,X)
  $6301 ,         \ CPL  (1,X)
  $F7 C,          \ LD   (X),A
;NVM ( xt ) ALIAS NECinv


\ send NEC-IR code with W28 remote control timing
: IR ( c -- )
  [ 0 TIM1_CR1 UIE ]B!  \ disable timer interrupt
  0 NECinv sreg !       \ the W28 remote control uses address 0
  ( c ) NECinv sreg 2+ !
  342 mrk !             \ 9ms AGC pulse (first mark)
  171 spc !             \ 4.5ms leader pause (first space)
  33 bcnt !             \ NEC: 33 mark/space events
  [ 1 TIM1_CR1 UIE ]B!  \ enable timer interrupt
;

\ wait until the IR code transmission has ended
: IRW ( -- )
  BEGIN bcnt @ 0= UNTIL
;

RAM

\\ Example:

\ -----------|----------|---------|----------
\   5 bright | 4 dim    | 6 off   | 7 on
\ -----------|----------|---------|----------
\   9 red    | 8 green  |10 blue  |11 white
\ -----------|----------|---------|----------
\  13 red1   |12 green1 |14 blue1 |15 cf-col
\ -----------|----------|---------|----------
\  21 red2   |20 green2 |22 blue2 |23 cr-col
\ -----------|----------|---------|----------
\  25 red3   |24 green3 |26 blue3 |27 flash
\ -----------|----------|---------|----------
\  17 red4   |16 green4 |18 blue4 |19 cs-col
\ -----------|----------|---------|----------

IRCTRL
7  IR  \ lamp on
10 IR IRW 11 IR  \ flash blue, then white
6  IR  \ lamp off
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment