A little forth music...

Andrew ClappAndrew Clapp wrote 07/14/2020 at 01:05 • 3 min read • Like

This was too fun not to share.  Today I was chipping away at t problem that I've been at for a few days, and it finally came together.

The start, was the need to have a way of assigning the channel for ADC on the STM8L in stm8ef.  This has been done by the stm8ef peripherals provided by eelkhorn.  And Thomas had a suggestion of which way to go with it.  Thanks to both of them.

The original code:

: setch ( ch -- )
    DUP 8 < IF
       1 ADC1_SQR4 ROT B!
       8 - DUP 8 < IF
           1 ADC1_SQR3 ROT B!
           8 - DUP 8 < IF
           1 ADC1_SQR2 ROT B!
               8 - 1 ADC1_SQR1 ROT B!

The ch passed into setch in this case, is a raw numerical channel between 0 and 27.  The ADC1_SQRx registers store the bits (1 on, 0 off) for which ADC channels we're going to read.  This example is pretty easy to follow, and it took me only about a day to get it.  I still have trouble with forth's IF/THEN syntax, but I am getting better at it.

Thomas suggested a totally different approach, which I got immediately.

: setch ( ch -- )

After recovering from my initial "mind-blown" moment in awe of how efficient it was, I groked it and tried it out.  If we figure that the raw number address 0 to 27 is encoded with the register number as an offset (0 to 3) and the bit within that register (0 to 7), it starts to work itself out and so I ran it.

And I could not make it work.  And so I broke it down, and looked at it parts, with a real paper stack, a pen, and then I found it.

ADC1_SQR1 = 0x534A
ADC1_SQR2 = 0x534B
ADC1_SQR3 = 0x534C
ADC1_SQR4 = 0x534D

The first thing is that the /MOD already outputs the remainder and quotient in the order we need so that SWAP is not needed.  I took it out.

    1 SWAP 8 /MOD ADC1_SQR1 + SWAP B!

So I ran that, and it did not work.  And then I was really starting to wonder if I was gonna make this work or not.  Sometimes you have to make changes to your mind in the middle of making changes to a block of code.

Second, the memory addresses are a little quirky, as the addresses increase as the x in ADC1_SQRx decreases.  Thanks ST.  So you need to do a little more to get it to work and reverse those.  So here's the final working function, with some explanations in the comments.  It only takes the single numeric channel (0 to 27) as an argument.  And I think you can use it to set vrefint and temp sensor as well (28 and 29).

  1 SWAP ( 1 n )
  8 /MOD ( 1 ch sqr_off )  \ /MOD returns rem quot, rem=chan quot=sqr_offset
  3 SWAP - ( reverse order )
  ADC1_SQR1 + ( 1 ch SQR# )
  SWAP ( 1 SQR# ch ) B!

 So there you have it.  Plan ahead when writing a forth routine.  Think about where you want to end up.  The 1 SWAP in the very beginning is there so you can use B! on the end, and have access to all that other stuff on the top of the stack in the interim.