Shop OBEX P1 Docs P2 Docs Learn Events
LED Progress Bar — Parallax Forums

LED Progress Bar

BotulismBotulism Posts: 18
edited 2020-10-20 23:32 in Propeller 1
I am making an LED progress where the user holds a button down and every second they hold it an additional bar lights up. If they let go anywhere in that sequence before getting to the end, they all turn off. I immediately went to stacking if statements. Turns out there's a limit of 8 nested items! How do I do this more efficiently?
if ina[RedButton] == 1                     
  outa[7..13] := %1000000  
  waitcnt(clkfreq + cnt)
  if ina[RedButton] == 1
    outa[7..13] := %1100000
    waitcnt(clkfreq + cnt)
    if ina[RedButton] == 1
      outa[7..13] := %1110000
      waitcnt(clkfreq + cnt)
      if ina[RedButton] == 1
        outa[7..13] := %1111000
        waitcnt(clkfreq + cnt)
        if ina[RedButton] == 1
          outa[7..13] := %1111100
          waitcnt(clkfreq + cnt)
          if ina[RedButton] == 1
            outa[7..13] := %1111110
            waitcnt(clkfreq + cnt)
            if ina[RedButton] == 1
              outa[7..13] := %1111111
              RedFlag := 1
 
          else
           outa[7..13] := %0000000
        else
          outa[7..13] := %0000000
      else
        outa[7..13] := %0000000
    else
      outa[7..13] := %0000000
  else
    outa[7..13] := %0000000
else
  outa[7..13] := %0000000

Comments

  • Perhaps the CASE statement would server you better there.

    Reference: Propeller Manual v1.2 · Page 59

    https://www.parallax.com/sites/default/files/downloads/P8X32A-Web-PropellerManual-v1.2.pdf#page59
  • Or as I think about your question more I wonder if WHILE might be better.

    eg:
    while ina[RedButton] == 1

    https://www.parallax.com/sites/default/files/downloads/P8X32A-Web-PropellerManual-v1.2.pdf#page188

  • Duane DegnDuane Degn Posts: 10,588
    edited 2020-10-21 01:03
    Here's my attempt at non-blocking code.
    VAR
    
      long pressTimer, pressPhase, outPattern, RedFlag
    
    PUB Main
    
      dira[7..13] := %1111111
    
      repeat
        CheckButton
        'do other stuff
    
    PUB CheckButton
    
      if ina[RedButton]
        if pressPhase
          if cnt - pressTimer > clkfreq
            pressTimer += clkfreq
            pressPhase++
            if pressPhase > 6
              RedFlag := 1
            outPattern >>= 1
            outPattern |= %100000
            outa[7..13] := outPattern
        else
          outPattern := %100000
          pressPhase := 1
          pressTimer := cnt
          outa[7..13] := outPattern 
      elseif pressPhase
        pressPhase := 0
        outa[7..13] := 0
        'RedFlag := 0 'Set to zero if applicable.
    
    

    You have to define "RedButton".
    I haven't tested this.

    Edit: Set LED pins as outputs. More RedFlag code.
  • doggiedocdoggiedoc Posts: 2,245
    edited 2020-10-21 01:04
    here's some code for the button, still working on the LED part
    {Test for LED Meter while pressing button}
    
    
    CON
            _clkmode = xtal1 + pll16x       'Standard clock mode * crystal frequency = 80 MHz
            _xinfreq = 5_000_000
    
    
            RED_BUTTON = 0
            
    VAR
          
       
    OBJ
        debug : "Parallax Serial Terminal"
        
      
    PUB MAIN
    
    debug.start(115200)
    waitcnt(CLKFREQ/2+CNT)  '' Wait for start up
    debug.str(string($d,"Hello.", $d))
    waitcnt(CLKFREQ+CNT)  
    
    repeat
      debug.Clear
      repeat while ina[RED_BUTTON] == 1
        debug.str(string($d,"Button Pressed."))
        waitcnt(CLKFREQ+CNT)
      
    
  • doggiedocdoggiedoc Posts: 2,245
    edited 2020-10-21 01:14
    I'm thinking the SHIFTL or SHIFTR commands may be the solution … trying to visualize it now.


    SHL and SHR are assembly commands. DOH I'm off track now.
  • lardomlardom Posts: 1,659
    edited 2020-10-21 01:19
    I haven't built the circuit but here's how I would approach it.
    CON
    
      _CLKMODE = XTAL1 + PLL16X        
      _XINFREQ = 5_000_000     
     
    OBJ     
      pst : "Parallax Serial Terminal"     
    
    PUB M  | idx, RedButton, seven_bits
      
      pst.Start(115_200)
      waitcnt(clkfreq * 2 + cnt)
      
      RedButton := 6     'RedButton has to be assigned a pin. Change it to your pin  
      dira[7..13]~~      'Same as :=  1
      repeat
        idx := 7
        repeat 7
          if ina[RedButton]                                           'Same as  ina[RedButton] == 1                            
            waitcnt(clkfreq / 1000 * 3 + cnt)                         'Debounce, let the button settle.
            if ina[RedButton] 
              outa[7..13] :=  seven_bits := seven_bits + (1 << idx--) '<< means shift left,  -- means 'post decrement'  
              waitcnt(clkfreq + cnt)
          else
            outa[7..13]~                                                 'Same as :=  0
          pst.Str(String(pst#NL, "seven_bits = "))
          pst.Bin(seven_bits, 7)       
          pst.Str(String(pst#NL, "idx = "))
          pst.Dec(idx)
    
  • I would turn it into a loop. All start as off. Each time through the loop turn on the next one. If button lets go, use a constant to clear all to off. And exit the loop. Use one of Jonnymac's timer routines for accurate seconds rather than waitcnts.
  • JonnyMacJonnyMac Posts: 9,159
    edited 2020-10-21 03:20
    The fun thing about these forums is asking a question and seeing the variety of workable solutions. Here's my idea; it uses the system counter to measure the duration of the button press.
    pub main | btnstate, t0, t1, level
    
      setup
    
      repeat                                                                ' wait for button release
        btnstate := ((btnstate << 1) | ina[RED_BTN]) & %11
        if (btnstate == %00)
          quit
    
      repeat
        btnstate := ((btnstate << 1) | ina[RED_BTN]) & %11                  ' re-scan
    
        if (btnstate == %11)                                                ' held down?
          t1 := cnt                                                         ' update button timing
          level := ((t1-t0) / clkfreq) <# 8                                 ' get # of seconds pressed
          outa[MSB..LSB] := %11111111 >> (8-level)                          ' update graph
    
        else
          t0 := t1 := cnt                                                   ' reset timing
          outa[MSB..LSB] := %00000000                                       ' clear display
    

    Hint: Don't hard-code pin numbers into your listing except into a CONstant definition that you can easily change later.

    Update: Working code attached.
  • I looked again at the code I wrote. I'm pretty sure "seven_bits := 0" has to be inserted after "idx := 7".
    repeat
        idx := 7
        seven_bits := 0
        repeat 7
          if ina[RedButton]                                           'Same as  ina[RedButton] == 1                            
            waitcnt(clkfreq / 1000 * 3 + cnt)
    
  • This works too.
    {Test for LED Meter while pressing button}
    
    
    CON
            _clkmode = xtal1 + pll16x    'Standard clock mode * crystal frequency = 80 MHz
            _xinfreq = 5_000_000
    
    
        RED_BUTTON = 0
            
    VAR
        long pattern  
       
    OBJ
        debug : "Parallax Serial Terminal"
        
      
    PUB MAIN
    
    debug.start(115200)
    waitcnt(CLKFREQ/2+CNT)  '' Wait for start up
    debug.str(string($d,"Hello.", $d))
    dira[7..13] ~~
    waitcnt(CLKFREQ+CNT)  
    
    repeat
      debug.Clear
      pattern := %11111110000000
      outa[7..13] := pattern
      repeat while ina[RED_BUTTON] == 1
        debug.str(string($d,"Button Pressed."))
        waitcnt(CLKFREQ+CNT)
        pattern >>= %01
        outa [7..13] := pattern 
    
    
    
  • Here's mine again but I reversed the order of the LEDs to make the code a bit cleaner.
    CON
    
      _clkmode = xtal1 + pll16x
      _clkfreq = 80_000_000
    
      BUTTON_PIN = 0
      FIRST_LED = 7
      LEDS_IN_BAR = 7
      LAST_LED = FIRST_LED + LEDS_IN_BAR - 1
    
      BAUD_115200 = 115200
      
    VAR
    
      long pressTimer, pressPhase, outPattern
      long redFlag
    
    OBJ
    
      Pst : "Parallax Serial Terminal"
      
    PUB Main
    
      dira[LAST_LED..FIRST_LED] := |< LEDS_IN_BAR - 1
    
      Pst.Start(BAUD_115200)
      
      repeat
        if redFlag
          DisplayRedFlag
        else
          DisplayNormal
            
        CheckButton
        'do other stuff
    
    PUB CheckButton
    
      if ina[BUTTON_PIN]
        if pressPhase
          if cnt - pressTimer > clkfreq
            pressTimer += clkfreq
            pressPhase++
            if pressPhase => LEDS_IN_BAR
              redFlag := true
            outPattern <<= 1
            outPattern |= 1
            outa[LAST_LED..FIRST_LED] := outPattern
        else
          outPattern := 1
          pressPhase := 1
          pressTimer := cnt
          outa[LAST_LED..FIRST_LED] := outPattern 
      elseif pressPhase
        pressPhase := 0
        redFlag := false
        outa[LAST_LED..FIRST_LED] := 0
    
    PUB DisplayRedFlag
    
      Pst.ClearEnd
      Pst.NewLine
      Pst.ClearBelow
      Pst.Home
      Pst.Str(string(11, 13, "Red Flag Active!"))
    
    PUB DisplayNormal
    
      Pst.ClearEnd
      Pst.NewLine
      Pst.ClearBelow
      Pst.Home
      Pst.Str(string(11, 13, "           LED Bar Program", 11, 13))
      Pst.Str(string(11, 13, "pressPhase = "))
      Pst.Dec(pressPhase)
      if pressPhase
        Pst.Str(string(11, 13, "outPattern = %"))
        Pst.Bin(outPattern, LEDS_IN_BAR)
    


    Make sure you set "BUTTON_PIN" to the pin you are using.

    I switched the value of "FIRST_LED" temporarily to 16 and tested the code on a QuickStart board. It seemed to work as expected.
  • doggiedocdoggiedoc Posts: 2,245
    edited 2020-10-21 02:18
    Is this what you are looking to do?

  • That was fun! I've missed this place. Other's have more elegant solutions but that should give you lots of options.

    Cleaned out the debug code:
    {Test for LED Meter while pressing button}
    
    CON
            _clkmode = xtal1 + pll16x    'Standard clock mode * crystal frequency = 80 MHz
            _xinfreq = 5_000_000
    
        RED_BUTTON = 0
            
    VAR
        long pattern    
      
    PUB MAIN
    
    dira[7..13] ~~
    waitcnt(CLKFREQ+CNT)  
    
    repeat
      pattern := %11111110000000
      outa[7..13] := pattern
      repeat while ina[RED_BUTTON] == 1
        waitcnt(CLKFREQ+CNT)
        pattern >>= 1
        if pattern == %00000000111111
          pattern := %00000001111111
        outa [7..13] := pattern 
    
    
    
  • If you were to stick with your original plan, see if stack size needs to be larger.
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2020-10-21 07:08
    In that case, I would do it like this in Tachyon, and the Spin method would be similar. Use the 0..6 count to set one led high so that next count the next led will be set. Clamp it at 6 so the logic can stay the same and any button release will reset the count and clear all the leds.

    Assume button on P0 active low, leds on P7..P13 active high
    ...........init       no button?    reset cnt and leds        else clamp,turn on led, next cnt, wait 
    
    : DEMO        0 BEGIN 0 PIN@ IF DROP 0 %11111110000000 OUTCLR ELSE 6 MIN DUP 7 + HIGH 1+ THEN 1 s AGAIN ;
    


    This version inits the count to the same as the first led and clamps at the last led.
    : DEMO   7 BEGIN 0 PIN@ IF DROP 7 $3F80 OUTCLR ELSE 13 MIN DUP HIGH 1+ THEN 1 s AGAIN ;
    
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2020-10-21 12:25
    Excuse my rusty Spin, but I think this is the equivalent of my one line Tachyon routine.
    pub demo | i
      i := 7
      dira[7..13]~~
      repeat
        if ina[0]   ' assume button is on P0
          i := 7
          outa[7..13]~
        else
          outa |= i<#13
          i++
        waitcnt(clkfreq+cnt)
    
  • Hi
    Couldn't resist.....some propbasic
    DEVICE P8X32A, XTAL1, PLL16X
    FREQ 80_000_000
    
    swpin	PIN  25 input			'switch input pulled hi 5k active lo 
    ledpins	pin 8..14	output	    '7 x led strip
    
    dlay			con		80_000_000	'1 second of clocks @ 80MHz
    
    pval			VAR long
    temp			var	long
    
    ' ======================================================================
      PROGRAM START
    ' ======================================================================
    
    START:
    do	
      pval=%1111111
      ledpins=pval			'all off
      waitpeq 0,swpin		'wait for switch operation  
      temp=cnt+dlay
      do	
    	waitcnt temp,dlay
    	if swpin=0 then		'switch operated
    		pval=pval<<1
    	else
    		exit
    	endif
    	ledpins=pval
      loop	
    loop
    
    end
    
    

    Dave
  • Call me whatever you like but for me the winner clearly is Peters'

    " : DEMO 7 BEGIN 0 PIN@ IF DROP 7 $3F80 OUTCLR ELSE 13 MIN DUP HIGH 1+ THEN 1 s AGAIN ;"

    It even looks so ....light on the keyboard. Efficient.
    Or maybe my brain is just playing tricks on me, hard to tell :).

  • The amount of support here is insane! Thank you everyone. I have much to learn from all these examples. I am strongest on the hardware front, so this part takes me a bit longer.
    Yes doggiedoc, that video is exactly what I'm looking to do.
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2020-10-23 06:48
    I think the pattern could simply computed in Spin as follows:
    main : idx
      repeat
        idx := (((idx + ina[redbutton]) <# 8 ) * ina[redbutton]) 
        outa[hilight..lolight] := |< idx - 1
        waitcnt(clkfreq+cnt)
    

    The idx variable increments each time around the loop, only up to 8, due to the <#, and always returns to zero if the button is up, due to the muliplication.
    The outputs are set to the decoded value of idx, minus 1. Read |< idx as "idx to the power of 2".
    idx.    |< idx - 1
      0.          0.          %00000000
      1.          1.          %00000001
      2.          3.          %00000011
      3.          7.          %00000111
      4.          15
      5.          31
      6.          63
      7.         127.   
      8.         255.       %11111111
       ...
    

    You can flip the order by outa[lolight..hilight] instead of outa[hilight..lolight].
  • @"Tracy Allen" - that is a very elegant solution!
  • I think the pattern could simply computed in Spin as follows:
    main : idx
      repeat
        idx := (((idx + ina[redbutton]) <# 8 ) * ina[redbutton]) 
        outa[hilight..lolight] := |< idx - 1
        waitcnt(clkfreq+cnt)
    

    The idx variable increments each time around the loop, only up to 8, due to the <#, and always returns to zero if the button is up, due to the muliplication.
    The outputs are set to the decoded value of idx, minus 1. Read |< idx as "idx to the power of 2".
    idx.    |< idx - 1
      0.          0.          %00000000
      1.          1.          %00000001
      2.          3.          %00000011
      3.          7.          %00000111
      4.          15
      5.          31
      6.          63
      7.         127.   
      8.         255.       %11111111
       ...
    

    You can flip the order by outa[lolight..hilight] instead of outa[hilight..lolight].

    Excellent. The first thing I ever learned from Tracy Allen, I think it was in 2008, was scaling values. I learned to move the decimal point to the right to eliminate the decimal point, do integer calculations and then move the point back to the left.
Sign In or Register to comment.