Shop Learn
Button Press to Trigger Event — Parallax Forums

Button Press to Trigger Event

Hi Everyone,

So I think I'm oversimplifying how to use a button on the Propeller Professional Development Board to trigger an event. The idea is to press a button to start a block of code with that block of code lighting an LED and sending a binary number to an SPI to then be outputted to the board. Then, ideally, press that same button that started the block of code to end the process, turning off the LED that was initially lit. This would then repeat with that same button being used to trigger another block of code that would light a different LED and output a different binary number.

Currently I'm side stepping the use of one button for all of this (but please let me know if you have a suggestion on how to make it all work with just one button) and using two buttons. One button starts the block of code and the other ends it.

I'm having trouble getting that first button to actually trigger the event the way I would like. As it is now, using the button connected to pin 8, I have to hold pin 8 down while the code is being loaded to RAM in order to trigger the event. If I don't hold it down, the code doesn't get triggered. I understand that the ina[] function returns the current state of the pin, so do I have to put in some sort of wait time during which I can trigger the condition? Ideally I'd like the button to be pressed at any time to trigger the code.

The repeat until command for pin 7 works well to end the block.

CON
    _clkmode = xtal1 + pll16x
    _xinfreq = 5_000_000

VAR
    byte Data
    long atten
    long decimal
    long dec
    long Response

OBJ

SPI     :       "SPI_Spin"                                      ''The Standalone SPI Spin engine

PUB LED_On_Auto | P8Flag

  SPI.Start(15000, 1)


   'HIGH(8)
   if ina[8] == 0
     Data := %00000001
     LOW(2)
     SPI.SHIFTOUT(0, 1, SPI#LSBFIRST, 8, Data)
     LED(3)



 {   if ina[8] == 0
      Data := %00000010
      LOW(2)
      SPI.SHIFTOUT(0, 1, SPI#LSBFIRST, 8, Data)
      dira[4] := 1
        outa[4] := 1
      repeat until ina[7] == 0       }


PUB HIGH(Pin)
    dira[Pin]~~
    outa[Pin]~~

PUB LOW(Pin)
    dira[Pin]~~
    outa[Pin]~

PUB LED(Pin)
    dira[Pin]~~
    outa[Pin]~~
    repeat until ina[7] == 0

Any thoughts?

Thanks for the help!

Zach

Comments

  • JonnyMacJonnyMac Posts: 7,624
    edited 2021-06-04 17:10

    You might want to create a debounce function; doing a simple one-time check of a button -- even with a slow processor -- is not a good idea. Here's a simple bit of code that will test the state of a pin and verify that it remained in that state for a specified period (milliseconds).

    pub debounce(pin, state, ms) | t
    
      t := cnt
      repeat ms
        if (ina[pin] <> state)
          return false
        waitcnt(t += MS_001)
    
      return true
    

    I have a constant called MS_001 in my files -- this is the number of system ticks in one millisecond. For your convenience, I've attached my template; you might find parts of it helpful.

    If I may... embedding pin numbers -- what we call "magic numbers" into your code is a habit you may come to regret later. It takes very little time to create named pin constants (I have one or two CON blocks devoted to IO pins (depending on the Propeller platform I use) . To give you a real-world example, ATM I'm working on a gaming device that is built using the EFX-TEK HC-8+ (a P1 board I designed for EFX-TEK several years ago). The HC-8+ has fixed IO pins, so you'll see that I define those, then create a block of application IO pins built on that. This doesn't take long to do, makes the code easier to read, and it's definitiely easier to troubleshoot.

    con { fixed io pins }
    
      RX_PGM  = 31  { I }                                           ' serial / programming
      TX_PGM  = 30  { O }
    
      SDA_EE  = 29  { I/O }                                         ' i2c / eeprom
      SCL_EE  = 28  { I/O }
    
      XP0     = 27  { I/O }                                         ' extra port 0 - Rev F
      XP1     = 24  { I/O }                                         ' extra port 1 - Rev F
      XP2     = 19  { I/O }                                         ' extra port 2 - Rev F
      XP3     = 16  { I/O }                                         ' extra port 3 - Rev F
    
      OUT_EN  = 27  { O }                                           ' outputs /enable - Rev E & lower
    
      SIO     = 26  { I/O }                                         ' TTL serial io - Rev E & lower
    
      RX_SIO  = 26  { I/O }                                         ' TTL serial io - Rev F
      TX_SIO  = 25  { I/O }                                         ' TTL serial io - Rev F  (not enabled)
    
      RX_485  = 23  { I }                                           ' RS-485 port
      RXE_485 = 22  { O }
      TXE_485 = 21  { O }
      TX_485  = 20  { O }
    
      G_LED   = 18  { O }                                           ' bi-color LED (Rev F assignments)
      R_LED   = 17  { O }
    
      OUT7    = 15  { O }                                           ' outputs
      OUT6    = 14  { O }
      OUT5    = 13  { O }
      OUT4    = 12  { O }
      OUT3    = 11  { O }
      OUT2    = 10  { O }
      OUT1    =  9  { O }
      OUT0    =  8  { O }
    
      OPT_BR  =  7  { I/O }                                         ' options switches
      OPT_A1  =  6  { I/O }
      OPT_A0  =  5  { I/O }
      OPT_SM  =  4  { I/O }
    
      DMX_A8  =  3  { I }                                           ' high-bit of DMX address
    
      SDI_165 =  2  { I }                                           ' control lines for 74x165
      SCK_165 =  1  { O }
      LD_165  =  0  { O }
    
    con { app io pins }
    
      SPEAKER =  OUT7                                               ' mod speaker with 10uF in + line
    
      RGB1    =  OUT3                                               ' RGB ring (24 pixels)
    
      XB_RX   =  OPT_A0                                             ' XBee coms
      XB_TX   =  OPT_SM
    
  • Hi Jon,

    Thank you for the debounce function and the tips. I now have that bit working so that when I press the button, the block of code executes until the other button is pressed. Now my question is, how do I then use that same button that started that code block to trigger another code block? I was wondering if I'll need a separate method/object that monitors the number of times that button is pressed with each press moving the program to the next block of code.

    Thanks,

    Zach

  • JonnyMacJonnyMac Posts: 7,624

    In the FWIW category.... After looking at spi_spin I decided to update it to the way I would code it (had I been the original author). My version assumes that the data out, data in, and clock pins are shared with one or more devices (which means the clock idle state must be the same for all). I can pass -1 for the data out or data in pin when it is not needed (e.g., I'm connecting to a device that only needs shiftin or only needs shiftout).

    Beau's version has been around a while and allows you to slow the clock -- I have never needed this in all my years of Propeller programming. The advantage of SPI is that it's fast and many advanced programmers who have written SPI routines in assembly have come up with ways to get more speed out of the P1 (e.g., creative use of the counters). The goal of my version is to share pins and be faster, even in Spin. In a quick test I found that new new version using SHIFTOUT was about twice as fast as the order version )when the older version had no clock delay).

    Anyway, you may find this helpful, and it will give you a bit of a speed boost while still using very simple code.

  • JonnyMacJonnyMac Posts: 7,624
    edited 2021-06-04 20:24

    @ZachS said:
    Hi Jon,

    Thank you for the debounce function and the tips. I now have that bit working so that when I press the button, the block of code executes until the other button is pressed. Now my question is, how do I then use that same button that started that code block to trigger another code block? I was wondering if I'll need a separate method/object that monitors the number of times that button is pressed with each press moving the program to the next block of code.

    Are you wanting the button to control the state of a program? I'm not sure I understand exactly how you want the program to behave vis-a-vis button input(s). The hardest part of getting help is describing what you want so that others understand it (been there many times). Maybe a flow-chart would be helpful.

    Here's another kind of debounce routine -- this will block program flow until a pin is at a desired state for a minimum number of milliseconds. This can be used to debounce press and release events from your button.

    pub wait_for_state(pin, state, ms) : statems | t
    
      t := cnt
      repeat
        waitcnt(t += MS_001)
        if (ina[pin] == state)
          if (++statems == ms)
            return
        else
          statems := 0
    
  • Zack, reading your first paragraph at the top of the page, it sounds like you want to make a state machine, where the button advances to the next state. Then each state would do what you need (light an LED, send info over SPI, etc.)

    Take a look at the CASE statement - see the Propeller manual (in the Propeller tool, this is on the Help menu). One approach is to set up a variable that increments each time you press the button, then evaluate the variable and proceed to the next state.

  • JonnyMacJonnyMac Posts: 7,624

    That's a good call, Jeff.

    Zach: State machines are very useful. Here's a very simple example of what Jeff was referring to:

    pub main
    
      setup
    
      repeat
        if (debounce(STATE_CTRL, 1, 25))
          if (++pgmstate == N_STATES)
            pgmstate := 0
    
        case state
          0     : run_state_0
          1     : run_state_1
          2     : run_state_2
          other : state := 0
    

    In this case the program can have three possible states (defined by N_STATES). The case structure will call the code that runs the current state. If the state control variable is global, the state can modify that variable to modify the order of operation.

  • AribaAriba Posts: 2,481

    @ZachS said:
    Thank you for the debounce function and the tips. I now have that bit working so that when I press the button, the block of code executes until the other button is pressed. Now my question is, how do I then use that same button that started that code block to trigger another code block?

    You wait until the button is released and then check again for a button press.
    Here a possible code that looks a bit more like Spin and not PBASIC (not tested):

    CON
        _clkmode = xtal1 + pll16x
        _xinfreq = 5_000_000
    
        LOW    = 0    'states
        HIGH   = 1
    
        LED1   = 3    'pins
        LED2   = 4
        BUTTON = 8
    
    OBJ
        SPI : "SPI_Spin"                 'The SPI object
    
    PUB LED_On_Auto
    
      SPI.Start(15000, 1)
    
      dira[LED2..LED1] := %11            'make LED pins outputs
      dira[2] := 1                       'not sure what this pin is for (CS?)
    
      repeat
        repeat until ina[BUTTON] == 0    'wait until button pressed 
        outa[2] := LOW
        SPI.SHIFTOUT(0, 1, SPI#LSBFIRST, 8, %00000001)
        outa[LED1] := HIGH
        outa[LED2] := LOW
        delay(50)                        'wait debounce time
        repeat until ina[BUTTON] == 1    'wait until button released
        outa[2] := HIGH
        delay(50)                        'debounce again 
    
        repeat until ina[BUTTON] == 0    'wait until button pressed again
        outa[2] := LOW
        SPI.SHIFTOUT(0, 1, SPI#LSBFIRST, 8, %00000010)
        outa[LED2] := HIGH
        outa[LED1] := LOW
        delay(50)
        repeat until ina[BUTTON] == 1    'and released again
        outa[2] := HIGH                  'pin 2 shows recognized button states
        delay(50)
    
    
    PUB delay(ms)
      waitcnt(clkfreq/1000*ms + CNT)
    

    Andy

  • Hi Jon, Jeff, and Andy,

    Yes Jeff, that is what I was attempting to describe to Jon initially. I'm working on creating a test box that will use a P1 chip. The idea is to use one button for everything in the process I'm describing. When the test box is turned on, pressing the button will do two things: one, it will illuminate a series of LED's to represent the starting binary number (through the program it will go from 00000001 to 011111111 in sequential order); and two, it will send out that binary number to a connected part using the SPI.

    My issue, mostly due to my inexperience with SPIN and the P1 chip, at the moment is writing the program so that the button does all of that. So far, I have a decent idea of how to write it so that within each state the LED's light up and the binary gets sent using the SPI, it's just how to go from state to state using one button.

    Thank you Jon and Andy for the pieces of code! I will play around with those to see how they work out.

    Zach

  • Hi Andy,

    I was able to adapt/build off the code you sent and it works really well. Thank you for the help.

    Zach

Sign In or Register to comment.