Using QuickStart Touch Buttons in Tachyon and a Tachyon (5.4) ROM building/usage tutorial

Jeff_BirtJeff_Birt Posts: 25
edited 2018-11-20 - 14:29:57 in Propeller 1

About a week ago I started out trying to figure out how to use the Touch Buttons on the Quickstart board from Tachyon. I started a thread on that subject here: . With a lot of help I was able to get the buttons working both in Forth directly and by using a ‘ROM’. Below is a bit of a ‘how to’ which hopefully will be a help to others.
So, you are using Tachyon Forth but have a bit of code that is so timing critical you need to use PASM. How can you do that? Fortunately, Tachyon includes the ability to do this using the ‘LOADROM’ word. The tricky part is understanding how to set up your PASM code to work with Tachyon. While it is not difficult there was not one thread or post or other source, I could find that described the whole process so I’m hoping this little guide will make it easier for other noobs and maybe save a bit of frustration. I am not an expert by any means but thanks to @Peter Jakacki and others I was able to figure this out.

I just got started with the Propeller 1 with the QuickStart board so after going through Peter’s introduction to tachyon guide, I thought I would figure out how to use the Touch Buttons on the QS board. The buttons are just closely spaced exposed traces to a buffered I/O pin and ground. The basic idea is that you set the pins output register to HI but set the data direction register (DDR) to an input. Then you toggle the DDR to an output then right back to an input. This puts a slight capacitive charge on these inputs. You wait a short period of time and then read the inputs. Touching the buttons will change the decay rate so you can tell which buttons are being touched.
Fortunately, there is some sample code for this written in Spin and PASM and it is fairly easy to understand thanks to the comments in the code. Below you will see the original touch button driver code (this is also loaded on the EEPROM for the QS board from Parallax). Here is a link to the source files:

  BUTTON_PINS   = $FF           ' The QuickStart's touch buttons are on the eight LSBs
  SAMPLES       = 32            ' Require 32 high readings to return true

  long  Results

PUB Start(Rate)

  Results := Rate
  cognew(@Entry, @Results)      ' Launch a new cog to read samples

PUB State | Accumulator

  Accumulator := Results        ' Sample multiple times and return true
  repeat constant(SAMPLES - 1)  '  if every sample was high
    Accumulator &= Results
  return Accumulator


              rdlong    WaitTime, par
              mov       outa, #BUTTON_PINS              ' set TestPins high, but keep as inputs

              mov       Wait, cnt                       ' preset the counter
              add       Wait, WaitTime
              or        dira, #BUTTON_PINS              ' set TestPins as outputs (high)
              andn      dira, #BUTTON_PINS              ' set TestPins as inputs (floating)
              mov       Reading, #BUTTON_PINS           ' create a mask of applicable pins
              waitcnt   Wait, WaitTime                  ' wait for the voltage to decay
              andn      Reading, ina                    ' clear decayed pins from the mask
              wrlong    Reading, par                    ' write the result to RAM
              jmp       #Loop

Reading       res       1
WaitTime      res       1
Wait          res       1


  • Jeff_BirtJeff_Birt Posts: 25
    edited 2018-11-20 - 14:25:30
    Part 2

    First the Forth Touch Button driver

    I tried this basic idea in Forth, but it was too slow, or so I thought. Peter Jakacki, the man who brought us Tachyon, was clever enough to for see the need to twiddle I/O bits quickly, so he added some non-conventional Forth words that are fast as they do not manipulate the stack, i.e. they read the top of the stack but leave it alone. So, before delving into how to do this in a ROM consuming a whole COG let’s see how easy it is to do from Forth itself
    : TOUCH ( pins -- result )
        DUP #32 FOR             ' DUP gives our accumulator area'
         H F DUP 1 ms
    : QDEMO ( -- )
        $00FF0000 OUTPUTS
         $FF TOUCH 8<< 8<< 
         $FF00FFFF P@ AND OR P!

    The idea is that you pass in the pin-mask, which for the QuickStart board’s Touch Buttons is $FF (the lower 8 bits of INA. We DUP the pin-mask which gives us a proper starting point for an accumulator, i.e. we want to AND together 32 consecutive readings and those which remain HIGH the whole time are considered pressed. The FOR loop gives us our 32 consecutive readings.

    The H word is a high-speed way to set the pins DDR to make them outputs and then the F word floats them, that is makes them inputs again. Both H and F do not alter the stack, so they are fast and leave the stack unchanged. We DUP out mask once more to preserve a copy and then wait 1ms before reading the state of the buttons.
    The p@ word reads all of INA and the ANDN uses that DUP of the pin-mask we made to mask off all pins but our buttons. The ROT word brings the third stack item to the top, so we go from “ACCUMULATOR PINMASK READING’ to ‘PINMASK READING ACCUMULATOR’. We then AND the ACCUMULATOR and READING and SWAP their positions on the stack so we are ready for the next loop.

    The QDEMO word accomplishes the same thing as the original Touch Button demo by lighting up the LED corresponding to the button(s) being touched. Peter suggests that could be run in the background as part of a KEYPOLL event or using the TIMER functionality in Tachyon. I plan to investigate both avenues in the near future.

  • Jeff_BirtJeff_Birt Posts: 25
    edited 2018-11-20 - 14:28:59
    Part 3

    A Touch Button driver as a Tachyon (5.4) ROM, a tutorial

    So, back to doing the same task in a ROM. Note, this is not really the best use of a COG or the ROM functionality Tachyon provides, but it is a simple example and good for a tutorial like this. We can start with the touch button driver that Parallax provides. The link to the source for that is above. We just need to massage it a little to ‘ROM’ify it. The basic idea is that we need to add some a header so that Tachyon has some vital information it will need when loading it. We also need to set up a way to pass data between our Forth code and the PASM code in our ROM running in its own COG.

    Peter has some information about ROMs (readme.txt) and samples here:
      BUTTON_PINS   = $FF           ' The QuickStart's touch buttons are on the eight LSBs
      SAMPLES       = 32            ' Require 32 high readings to return true
    PUB Start
                  byte ".ROM"                       ' ROM signature
                  word @TOUCHBTNend-@TOUCHBTN       ' size
                  byte "TOUCHBTN  "                 ' name
                  '     1234567890
                            org 0
                  rdlong    WaitTime, par               ' save value passed in as delay period
                  mov       outa, #BUTTON_PINS          ' set TestPins high, but keep as inputs
                  mov       Wait, cnt                   ' preset the counter
                  add       Wait, WaitTime
                  or        dira, #BUTTON_PINS          ' set TestPins as outputs (high)
                  andn      dira, #BUTTON_PINS          ' set TestPins as inputs (floating)
                  mov       Reading, #BUTTON_PINS       ' create a mask of applicable pins
                  waitcnt   Wait, WaitTime              ' wait for the voltage to decay
                  andn      Reading, ina                ' clear decayed pins from the mask
                  and       Accumulator, Reading        ' accumulate successive samples
    ' maybe preset index to 32, sub 1 and then bez?              
                  add       Index, 1                    ' inc loop index
                  cmp       Index, #SAMPLES             ' enough samples?
     if_b         jmp       #Loop                       ' nope keep going
                  wrlong    Accumulator, par            ' @32 samples, update hub variable  
                  mov       Accumulator, #BUTTON_PINS   ' reset accumulator
                  mov       Index, $01                  ' reset index                                         
                  jmp       #Loop                       ' keep on going
    Index         long      $01                             ' loop index save accumulated value
                                                            ' at $20 (#32), i.e. SAMPLES
    Accumulator   long      $FF
    Reading       res       1
    WaitTime      res       1
    Wait          res       1

    We have a few constants declared up top for out pin mask and number of samples. The ‘PUB Start’ is required by Propeller IDE to properly compile the code. The header that Tachyon’s LOADROM word needs in after ‘DAT’ and between ‘org’ and ‘org 0’. This provides a signature to tell Tachyon this is a ROM, the length of the code by using a couple of labels and ‘word @TOUCHBTNend-@TOUCHBTN’, and the ROM name which must be exactly 10 characters long so pad it with trailing spaces if need be.

    I won’t go through this code line by line, it is only slightly modified from the original demo code but combines the accumulation of 32 samples into the main loop and provides the result via a HUB RAM variable. For this simple example we only need one LONG variable in HUB RAM. We can use it to supply the scanning delay period since that is only needed at initialization, we can also use that same variable to allow the driver code to send the current keypress data back to HUB RAM.

    Now that we have our PASM code with the needed header in place we can compile it with the Propeller IDE or your tool of choice. Then we need to convert it to the Intel HEX format that Tachyon expects, don’t worry it is fairly painless. In Peter’s drop box folder on ROMs linked to above there is a little Python file called ‘’ that will do this for us. The command is: python --binaries=0, TOUCHBTN.binary using our touch button program as an example.
    Now that we have the HEX file, we need to open it in a TEXT EDITOR (not a word processor) and add a line to the top that says, ‘SAVEROM, as shown below. Save the file and now we are ready to load this ROM up in Tachyon.

    The LOADCOG functionality in Tachyon is taking care of starting this code running in the COG we specify, we just have to give it the information it needs. An example for our little touch button driver is:
    // To load and run Touch Button driver in a COG
    long touches --- hold result of touch cog accumulator
    80,000 touches !
    " TOUCHBTN  "  3 touches LOADCOG
    touches @ .

    The ‘80,000’ is the clock frequency divided by 100 and is our button read delay we pass to the driver. To ask Tachyon to load this ROM for us we give it the name (without file type extension), remember it must be 10 characters so pad with trailing spaces if needed, the leading space is a Forth delimiter and not part of the filename. The ‘3’ is the COG to start in (3-7 are usually available in Tachyon, ‘touches’ is our shared HUB RAM variable of course and LOADCOG is what we want Tachyon to do. Now with all that out of the way we can just ask Tachyon to show us the current keypress data stored in ‘touches’ with ‘touches @ .’ .

    Again, this is not a great use of a whole COG, but it is a fairly simple demonstration of the process or building and loading a ROM into Tachyon. If you have more than one variable to pass you can use a structure and pass the starting address of the structure. Also, I’m still a newbie at all of this and while I tried to present accurate information here, I’m sure I made an error or two. If you seen something that looks to be incorrect just shout it out and we’ll get to the bottom of it.

  • Nice post Jeff! I know you have been talking with Peter a lot, and it seems all the work is paying off. Thanks for sharing!
    Infernal Machine
Sign In or Register to comment.