Looking for Spin example to timeout a LCD backlight in background

I'm looking for a way to turn off the backlight on the display when there is no activity from the rotary encoder i.e. knob hasn't been turned.

I tried launching this code into its own cog and calling it but my main program hangs until it times out. Is there a way to have it run in the background without it affecting the main program?

I'm using the Parallax 4x20 LCD. You send a $11 to turn it on and a $12 to turn it off. I'm using Full Duplex Serial to talk to it.

I launch the WatchDog cog like this
cognew(WatchDog, @stack)  

The stack variable is a long[100]
PUB WatchDog | t

  t := cnt - 1776               ' sync with system counter
    repeat 5000                 ' Set delay for 5 seconds
      waitcnt(t += MS_001)




  • Peter JakackiPeter Jakacki Posts: 9,719
    edited 2020-02-07 - 23:14:21
    These days especially, it is advisable to post all your code as a zipped project or at least the objects. But remember that you cannot override a high output from another cog, which is what your Watchdog would be trying to do. You would have to and should handle all LCD.tx from the main cog because you also do not want both cogs trying to transmit at the same time either. Of course you could make it work this way but you have to be careful about conflicts and also release the tx line which would need a pull-up (assuming it is active low).

    (I have the same problem in Tachyon so I have the background timer cog (which constantly maintains runtime and general-purpose countdown timers) set a flag or something for the main program to act on).
  • Actually the code is nothing special as it is just a menu at this point being scrolled up and down with an encoder. I compare the new value of the encoder with previous to see if it has changed. If not then it triggers the watchdog to turn off the back light. So I would just enter the watchdog at the appropriate spot.

    I tried adding that watchdog routine in the full duplex serial object since fsd launches in it's own cog but that didn't work either. It just stalled for the 5 seconds and then continued on.
  • JonnyMacJonnyMac Posts: 7,008
    edited 2020-02-08 - 03:45:01
    The problem is that your foreground and watchdog cogs could collide when communicating with the LCD. Keep the timing in the watchdog cog and let the foreground handle coms with the LCD. In many of my programs I have a background cog that looks like this:
      long  millis
    pri background| t
      t := cnt
        waitcnt(t += MS_001)
        ' do other things that take < 1ms
    You can do a surprising amount of work in 1ms. In your case I would have the foreground set millis (which is global, hence available to both cogs) to -5000 whenever there is activity. During your foreground loop you can look to see if millis is => 0; if it is, send the backlight off command.

    If you don't have anything else to do in the background you can do the same thing with my time object (which uses differential measurements of the cnt register). In that case you would reset the backlight timer with.
    ...and check it with:
      if (time.millis => 0) ...
    Note: The time object isn't running in a cog so it must be accessed periodically (before cnt roll-over).
    The stack variable is a long[100]
    Your stack doesn't need to be that big. I would use 16 or 32.
  • Great solution. Thanks @JonnyMac.
  • cavelambcavelamb Posts: 687
    edited 2020-02-09 - 19:53:49
    I hesitate to follow any of Jon's examples.
    I often have to read them for a few days to get it.

    But this one seems to go directly to the OPs request...
    See if it helps?

    ' Liquid Crystal Display - 2 line'
      LCDpin            =  14
      LCDon1           = $16               ' LCD on; cursor off, blink off
      LCDline1          = $80               ' move to line 1, column 0
      LCDline2          = $94               ' move to line 2, column 0
      LCD_TimeOut   =  10                ' seconds
    PUB Init
    ' Start LCD_Timeout
      LCD_Time := LCD_TimeOut        ' reset LCD_Time each time a key is pressed
      cognew (LCD_Timer, @stack1 )  
    Pub Main
    ' Top of IR Code input loop:
      if IRcog > 0
            If LCD_Time >0
                LCD.backlight(1)            ' turn it on        ' could test to see if it's off, but...
            else                                   ' timed out
               LCD.backlight(0)             ' turn if off
    PUB LCD_Timer
      Repeat                                      ' loop forever
          waitcnt(clkfreq + cnt)            ' wait one second
          if  byte[@LCD_Time] =>   1   ' keep counting        
              byte[@LCD_Time] --          ' down
Sign In or Register to comment.