Shop OBEX P1 Docs P2 Docs Learn Events
Two cogs controlling one LCD — Parallax Forums

Two cogs controlling one LCD

sylvie369sylvie369 Posts: 1,622
edited 2010-01-01 15:40 in Propeller 1
Short version:
I've got a program I'm running that takes in serial data from an XBee and displays them on an LCD. That part works fine.
I also am reading a pair of XBee DIO pins (which are linked through "digital line passing" to another XBee) into pins 22 and 23 on the Prop Proto board, and displaying their values on the LCD. That part also works fine.

What doesn't work fine is doing both at once. If I run the DIO program in a second cog, it doesn't display to the LCD, and when the serial data come in, they display as gibberish. I've double-checked that they're not trying to write to the same line of the LCD (the serial input program writes to lines 0, 1, and 3, and the DIO program only writes to line 2). When I comment out one or the other program, the one that runs works just fine, whichever one it is.

First question: Is it possible to have two cogs send data to one LCD?
Second question : If so, how do I keep them from interferring with each other? I assume that if it's possible, my problem is that I'm not initializing things in the proper place.

Context:
I'm working on the receiver for a rocket telemetry device. The transmitter is an XBee that uses line-passing to pass along the values of two pull-pin switches which are intended to signal when the drogue and then main parachutes have deployed. The pull-pins are connected by pulldown resistors to ground, but pulled high by the pull-pin until it is pulled out. That part is working reliably, both on the transmitter side and on the corresponding XBee pins on the receiver.

The XBee also receives serial data from an altimeter once the rocket has taken off, and sends those data to the receiver for display as well. That part I've flown successfully 7 times.

I want the pull-pin status to be read constantly beginning at power-up, and displayed on the receiver. The altitude data don't begin until the rocket is in flight. I want the pull-pin status to be read and displayed even while the altitude data are coming in.

This is the first time I'm trying to use a Propeller properly, with separate cogs for functions that I want to have running in parallel. It's quite a leap for me, mentally - I really want that pull-pin checking code to be inside the altitude receiver loop, but of course if I do that, I don't get pull-pin status except while receiving altitude data. I could, I suppose, simply have the pull-pin status displayed separately on LEDs, but that's not as fun as having the LCD show the current altitude, maximum altitude, current flight status (ready, in-flight, landed), and the status of the recovery events (drogue, main).

While I'm asking, let me also ask how I would know how much stack to allocate for the second cog. When I used just 9 longs, it apparently overwrote one of the pull-pin status variables, so that one always indicated that the event had occurred. I checked over the circuit to make sure·the receiver·was properly following the input voltage, and it was, so suspecting that a variable was being overwritten, I increased the stack size a lot (to 59 longs), which worked, then inched it down until it stopped working, then back up again. There must be a better way.

Here is the current version of the code:

''*  XBee-MAWD Receiver with pull-pins                *
''
''XBee RX on pin 0
''XBee TX on pin 1
''LCD connected to pin 8. Parallax 4x16 LCD. 
''Pull-pins from XBee DIO0 to Prop pin 22, XBee DIO1 to Prop pin 23 
 
CON 
    _clkmode = xtal1 + pll16x      '80MHz operating frequency.
    _xinfreq = 5_000_000                      
    Hz    = 100

OBJ 
  XB   : "XBee_Object"       
  Comm : "FullDuplexSerialPlus"  
  Num  : "Numbers"
  LCD  : "Debug_LCD"
  BS2  : "BS2_Functions"

VAR     byte          Alt[noparse][[/noparse]6]       ' Variable to hold the first 6 bytes of altitude data from MAWD. First 5 bytes are 5 digits of altitude, then CR (then LF, unread).
        word          AltN         ' Variable to hold numerical value of altitude, after conversion from string. 
        word          MaxAlt       ' Variable to hold maximum altitude
        byte          Land         ' Has rocket flown and landed?
        byte          Event0       ' Has pull-pin 0 pulled?
        byte          Event1       ' Has pull-pin 1 pulled?
        long          Stack[noparse][[/noparse]25]    ' Stack for cog running PinsCheck (stack size set by trial and error: 9 is too small, and overwrites E1). 

Pub Start 
    XB.start(0,1,0,9600)           ' XBee Comms - RX,TX, Mode, Baud
    XB.AT_init
    XB.AT_Config(String("ATMY 0")) ' Config to receive as Module 0 to match XBee-MAWD telemetry output DL setting.
    DIRA[noparse][[/noparse]22..23]~                  ' Set pins 22 and 23 as inputs from the XBee I/O pins 0 and 1
    XB.AT_Config(String("ATD0 5")) ' Config to use XBee DIO pin 0 as a digital output, default HIGH
    XB.AT_Config(String("ATD1 5")) ' Config to use XBee DIO pin 1 as a digital output, default HIGH
    XB.AT_Config(String("ATIA 1")) ' Config to receive DIO data from module whose MY = 1. 
    XB.AT_Config(String("ATIU 0")) ' Config so that DIO pin states are not sent out UART, where they would interfere with alt data
    
    Num.init                       ' Initialize Numbers object
    LCD.init(8, 9600, 4)           ' Initialize LCD
    LCD.cls                        ' Clear the screen
    BS2.start (31,30)
    
    MaxAlt := 0                    ' Start at 0 altitude
    Land   := FALSE                ' Set the Landing variable
    Event0 := %00000000            ' Set the pull-pin variables 
    Event1 := %00000000            ' Event0 and Event1
    
    Splash                         ' Show startup screens
    LCD.cls
    Screenlabels                   ' Label the values
    LCD.gotoxy(10,3)
    LCD.str(String("Ready     "))
 
'    Repeat                        ' Currently commented out - this is how I checked to see if it works serially. 
'      PinsCheck
      
    Cognew(PinsCheck, @Stack)      ' Run the PinsCheck continuously in a separate cog. 
 
    Repeat while Land == FALSE
      If XMCheck                   ' If Altitude data received
        XMReceive                  ' Get and display the data
      Else
        XMBadData    

Pub XMCheck : Success              ' Check to see if receiving data
  Success := XB.RxCheck 

Pub XMReceive                      ' Uses BS2 function to receive simply because I couldn't get the XBee object working
    BS2.SERIN_STR(0, @Alt, 9600, 1, 8)      ' Receive a CR-terminated string to Alt through pin 0
    Alt[noparse][[/noparse]5] := 0                             ' Convert to z-string for use by LCD and Num objects
    LCD.gotoxy(10,0)
    LCD.str(@Alt)                           ' Display the altitude string
    AltN := Num.FromStr(@Alt, %000_000_000_0_0_000110_01010 ) ' Convert to 5-digit number for use by MaxAlt
    if (AltN > MaxAlt) & (AltN < 50000)                       ' < 50000 check is to avoid bad data at landing
      MaxAlt := AltN
    LCD.gotoxy(10,1)
    LCD.decf(MaxAlt,5)                      ' Display the Maximum altitude
    If AltN < 1                             ' Sometimes the Alt drops below 0 after landing - this accounts for that. 
      Land := TRUE
      LCD.gotoxy(10,3)
      LCD.str(String("Landed   "))
    else
      LCD.gotoxy(10,3)
      LCD.str(String("In flight"))

Pub XMBadData
  LCD.cls
  LCD.gotoxy(10,3)
  LCD.str(String("No data   "))
  Pause(200)

Pub PinsCheck                                ' This will have to go into a different cog to run at the same time at the altitude data receiver.
  Repeat 
    LCD.gotoxy(0,2)
    Event0 := INA[noparse][[/noparse]22]                        ' Get value of input pin 22 
    Event1 := INA[noparse][[/noparse]23]                        ' Get value of input pin 23
    If Event0 <> %00000001
      LCD.str(String("E0 "))
    else
      LCD.str(String("No "))
    If Event1 <> %00000001
      LCD.str(String("E1 "))
    else
      LCD.str(String("No "))

Pub Splash                         ' Display startup screen
  LCD.home
  LCD.backlight(TRUE)
  LCD.gotoxy(0,0)
  LCD.str(@Splash1)
  LCD.gotoxy(0,1)
  LCD.str(@Splash2)
  LCD.gotoxy(0,2)
  LCD.str(@Splash3)
  LCD.gotoxy(0,3)
  LCD.str(@Splash4)
  Pause(1000)
'  LCD.backlight(FALSE)

Pub Screenlabels
  LCD.gotoxy(0,0)
  LCD.str(String("Alt    : "))
  LCD.gotoxy(0,1)
  LCD.str(String("Max Alt: "))
  LCD.gotoxy(0,3)
  LCD.str(String("Status : "))

PUB Pause(mS)
  waitcnt(clkFreq/1000 * ms + cnt)

DAT
  Splash1 byte "XM Telemetry ",0
  Splash2 byte "Version 1.2  ",0
  Splash3 byte "Paul C. Smith",0
  Splash4 byte "Dec 29, 2009 ",0

Comments

  • JonnyMacJonnyMac Posts: 9,208
    edited 2009-12-29 17:19
    You're going to need some mechanism to let one cog know that the other is sending data to the LCD. You could use a LOCK (see LOCKSET, etc.).

    Phil Pilgram (PhiPi) wrote a little utility that helps one empirically determine stack space -- I'd suggest you do a search but we all know how poorly that function works within this forum software....
  • StefanL38StefanL38 Posts: 2,292
    edited 2009-12-29 18:55
    Hello Sylvie,

    edit: forgot the behaviour of this forum-software to eat up square-brackets if written without spaces

    it is the same as if two people talk at the same time: This is hard too understand especially for computers.

    two cogs can't send at the exact same time. If you have some kind of a lock mechanism you can could set the lock
    then cog1 sends clear lock cog2 sets lock sends clear lock etc. etc. If would mean that the other cog has to wait
    until the lock is cleared. If I understand right you want EVERY cog send whenever the cog wants to without waiting.

    This requires a third cog! ??! Why that?

    Cog1 and cog2 write to a bytebuffer NOT direct to the LCD. The third cog does nothing else but sending the content of the buffer
    as soon as the content of the buffer has changed (since last sending) or after a constant amount of time.

    If cog1 and cog2 write to DIFFERENT places in the buffer the data cannot interfere as each data has its onw place in the buffer.
    Practically this would be organised in the same way as the characters on the display.

    Let's assume you have a 4x40 display. This would mean that you send always 4x40 bytes and all the bytes are written sequentially
    to the display. If this takes too long you can short down the datasize you need.
    Example:

    DIO has 3 bytes
    altitude data has 5 bytes then you would declare a bytearray of 3 + 5 = 8 bytes

    VAR
      byte MyByte[noparse][[/noparse] 8 ] 
    
    



    cog1 reads the dio and writes to byte 0 to 2

      MyByte[noparse][[/noparse] 0 ] := DIO1 
      MyByte[noparse][[/noparse] 1 ] := DIO2 
      MyByte[noparse][[/noparse] 2 ] := DIO3 
    
    



    cog2 reads the altitude-data and writes to byte 3-8

      MyByte[noparse][[/noparse] 3 ] := ALT1
      MyByte[noparse][[/noparse] 4 ] := ALT2 
      MyByte[noparse][[/noparse] 5 ] := ALT3 
      MyByte[noparse][[/noparse] 6 ] := ALT4
      MyByte[noparse][[/noparse] 7 ] := ALT5 
    
    



    now cog3 does

      repeat i from 0 to 7
        LCD.tx(MyByte[noparse][[/noparse] i ])  
    
    




    So this way only ONE cog is sending the data but 1 to n cogs can "send" (=write to a sendbuffer) at the same time.
    If the writing to the altitude-bufferbytes hasn't finished yet you will get an inconsistent data displayed for a short time
    then you get the right data.

    If you want to avoid inconsistent data you could implement some kind of flagbytes for DIO-Data and altitude-data
    only if flagbyte is set which means all data has been written to the sendbuffer.

    best regards

    Stefan

    Post Edited (StefanL38) : 12/29/2009 7:33:10 PM GMT
  • JonnyMacJonnyMac Posts: 9,208
    edited 2009-12-29 21:31
    Debug_LCD uses Simple_Serial which means it's not buffered -- I wondered about this, too, after making my post. I just checked, though, and the TX code in Simple_Serial is in Spin and not buffered. I think he can write to *that* LCD with two cogs, but still needs some sort of lock mechanism to prevent one from stomping on the other.
  • StefanL38StefanL38 Posts: 2,292
    edited 2009-12-30 05:37
    @JonnyMac,

    FullDuplexSerial uses a what I call low-level-sendbuffer. What I suggest is a "sort-the-data-to-certain-places-buffer".
    As Sylvie wants to send altitude-data from a rocket speeding into the sky a lock-mechanism is to slow.

    best regards

    Stefan
  • sylvie369sylvie369 Posts: 1,622
    edited 2009-12-30 11:26
    Okay, I think I get it. Essentially what I need is to remove the code that writes to the LCD from where it is, have the current code simply update some variable (the "buffer"), and a separate cog that does all the writing to the LCD.

    I don't need to worry about inconsistent data, at least if·it will·catch up within a fraction of a second anyway. I do hope to eventually send all of the data out the serial port to Excel, where it would matter if the data were inconsistent for any significant length of time, but I imagine we're talking 100ths of a second or better, right? And I imagine I'll have to do something similar for those writes out the serial port.

    Thanks.
  • JonnyMacJonnyMac Posts: 9,208
    edited 2009-12-30 15:43
    @Stephan: I guess we'll agree to disagree. His concern is having two objects write to the LCD -- but not at the same time. The LCD code he's using is not using FDS (I checked) so I believe he can safely use a lock to prevent two objects from writing to the LCD at the same time. The serial transmit code in the LCD object is written in Spin and not buffered. I believe the FDS object he's using is connected to the XBee radio, not the LCD.

    @Sylvie: You'll have to experiment, but I believe you can do something like this:

    if !lockset(0)
      ' write to LCD
      lockclr(0)
    


    Use this at any place where an object needs to write to the LCD. If the lock is clear it will be set, you object can write to the LCD, and then clear the lock so that the other object can have access. Again, as per my exchange with Stephan, this is only valid using the LCD object you presently have employed that does not use a serial buffer; if you change LCD objects to one that buffers serial data, you will, as Stephan correctly suggests, have to create separate object for dealing with LCD messages.
  • kwinnkwinn Posts: 8,697
    edited 2009-12-30 16:20
    I have implemented both methods of having multiple processes update information on one display and in my opinion Stefan's method of having a single process do the updating is the simplest to implement. That process could be an independent cog that only does the screen update or part of another program. The process could use a "screen buffer" method, or read the variables and write them to the screen.
  • sylvie369sylvie369 Posts: 1,622
    edited 2009-12-30 17:15
    Okay, I've got some time to try it this morning. I'm going to try Stefan's buffer method first (no offense, JohnnyMac - I learned a lot from looking into your method as well, and will certainly try it out on something at some point).

    I assume that I define the 8 byte variable up in my public variables definition section, available to the methods running on all of the cogs, and that I do not have to explicitly pass it to the method that is updating the LCD, right?
    Then I'll put the code that initializes the LCD, and the code that displays the splash screen, and the permanent screen labels all into the start of that cog, and then after that code, a repeat loop that just keeps running, comparing the current state of the buffer with the state it was in previously, and if there is any change, writing the new values to the LCD.

    (Trying it...)·· [noparse][[/noparse]Okay, I'm going to think aloud here as I try to figure this out ]

    Yikes. Okay, I added the buffer variable, and moved the LCD init code and splash and labels code into a method named LCDUpdate. When I run that method itself serially (that is, when I just add a line LCDUpdate to my Start method), it runs just fine. But when I try to run it·in a new cog, I get gibberish on the screen. Okay, so maybe the stack is too small again. Make it larger - doesn't help. Larger still - doesn't help. Ridiculously large - doesn't help. So wait...maybe it's running just fine, but then it gets to the code in the Start method and tries to write to the LCD outside of the LCDUpdate method. Put in a long Pause after starting LCDUpdate·to see. Yup, that seems to be the problem. I'll have to change over all the rest of the LCD writes to see if it's working.

    This is really interesting - after 35 years of serial programming, I feel like I'm on acid, what·with this "start a cog and let it run" stuff. I'm going to be obsessively thinking about what I can do with this while I'm on the treadmill this afternoon. I see a lot of "rush home to try something out" days coming up.
  • sylvie369sylvie369 Posts: 1,622
    edited 2009-12-30 18:12
    [noparse][[/noparse]More thinking aloud... bear with me to the end]

    Hmm. I thought that the idea was that no two cogs would try to put data into the same place. That is, the main program puts altitude data into a couple of locations (say, 0 and 1), and updates flight status in another (2), but doesn't touch the others, while the PinCheck method (running in a separate cog) puts the current status of the pull-pins into a couple of other locations (say, 3 and 4) without touching locations 0-2. Like this:
           word          LCDBuffer[noparse][[/noparse]8] ' Variable to hold values to be displayed on the LCD.
                          { 0 - AltN
                            1 - MaxAlt
                            2 - Flight status: 0 = Ready, 1 = In flight, 2 = Landed
                            3 - Event 0: 0 = No event, 1 = Event
                            4 - Event 1: 0 = No event, 1 = Event
                            5-7 (reserved)
                          }
           word          LCDBufOld[noparse][[/noparse]8] ' Temporary buffer to be compared with current contents to determine whether or not to update LCD. 
    

    I still have to work out how the flight status thing is going to work, since that could be determined by both altitude data and pull-pin status (I may just punt on that and leave it as altitude only), but otherwise I think that there's no way for two separate cogs to try to update the same memory location.

    (incidentally, am I able to use this as an array of word variables? That's how large the altitude numbers need to be. )

    Or are you saying that I have to set a lock while writing the data so that the cog running LCDUpdate doesn't try to update while the data are being changed in another cog? I *think* that I'm only *reading* the LCDBuffer while I'm in the LCDUpdate method, though I'll need to double-check that. Is there a risk of corruption from reading it while it's being updated in another cog?

    Here's the code so far, in case it helps. It's not working yet, but you might be able to see how I'm thinking about this (and where I'm going wrong?):

    ''*  XBee-MAWD Receiver with pull-pins                *
    ''
    ''XBee RX on pin 0
    ''XBee TX on pin 1
    ''LCD connected to pin 8. Parallax 4x16 LCD. 
    ''Pull-pins from XBee DIO0 to Prop pin 22, XBee DIO1 to Prop pin 23 
    
    CON 
        _clkmode = xtal1 + pll16x      '80MHz operating frequency.
        _xinfreq = 5_000_000                      
        Hz    = 100
    
    OBJ 
      XB   : "XBee_Object"       
      Comm : "FullDuplexSerialPlus"  
      Num  : "Numbers"
      LCD  : "Debug_LCD"
      BS2  : "BS2_Functions"
    
    VAR     byte          Alt[noparse][[/noparse]6]       ' Variable to hold the first 6 bytes of altitude data from MAWD. First 5 bytes are 5 digits of altitude, then CR (then LF, unread).
            word          AltN         ' Variable to hold numerical value of altitude, after conversion from string. 
            word          MaxAlt       ' Variable to hold maximum altitude
            byte          Land         ' Has rocket flown and landed?
            byte          Event0       ' Has pull-pin 0 pulled?
            byte          Event1       ' Has pull-pin 1 pulled?
            long          PinStack[noparse][[/noparse]25] ' Stack for cog running PinsCheck (stack size set by trial and error: 9 is too small, and overwrites E1).
            long          LCDStack[noparse][[/noparse]25] ' Stack for cog running LCDUpdate
            word          LCDBuffer[noparse][[/noparse]8] ' Variable to hold values to be displayed on the LCD.
                          { 0 - AltN
                            1 - MaxAlt
                            2 - Flight status: 0 = Ready, 1 = In flight, 2 = Landed
                            3 - Event 0: 0 = No event, 1 = Event
                            4 - Event 1: 0 = No event, 1 = Event
                            5-7 (reserved)
                          }
            word          LCDBufOld[noparse][[/noparse]8] ' Temporary buffer to be compared with current contents to determine whether or not to update LCD. 
    
    Pub Start 
        XB.start(0,1,0,9600)           ' XBee Comms - RX,TX, Mode, Baud
        XB.AT_init
        XB.AT_Config(String("ATMY 0")) ' Config to receive as Module 0 to match XBee-MAWD telemetry output DL setting.
        DIRA[noparse][[/noparse]22..23]~                  ' Set pins 22 and 23 as inputs from the XBee I/O pins 0 and 1
        XB.AT_Config(String("ATD0 5")) ' Config to use XBee DIO pin 0 as a digital output, default HIGH
        XB.AT_Config(String("ATD1 5")) ' Config to use XBee DIO pin 1 as a digital output, default HIGH
        XB.AT_Config(String("ATIA 1")) ' Config to receive DIO data from module whose MY = 1. 
        XB.AT_Config(String("ATIU 0")) ' Config so that DIO pin states are not sent out UART, where they would interfere with alt data
        
        Num.init                       ' Initialize Numbers object
        BS2.start (31,30)
     
        Cognew(LCDUpdate, @LCDStack)   ' Start the LCD updating method
        Pause(6000)
     
    ' Initialize Flight Variables
        AltN   := 0                    ' Start at 0 altitude
        MaxAlt := 0                    ' Set max altitude to 0 as well. 
        Land   := FALSE                ' Set the Landing variable
        Event0 := %00000000            ' Set the pull-pin variables 
        Event1 := %00000000            ' Event0 and Event1
        
        Cognew(PinsCheck, @PinStack)   ' Run the PinsCheck continuously in a separate cog. 
     
        Repeat while Land == FALSE
          If XMCheck                   ' If Altitude data received
            XMReceive                  ' Get and display the data
          Else
            XMBadData    
    
     
    Pub LCDUpdate                      ' Method to initialize LCD, display splash screen, screen labels, and then update the LCD display data. 
        LCD.init(8, 9600, 4)           ' Initialize LCD
        LCD.cls                        ' Clear the screen
        Splash                         ' Show startup screens
        LCD.cls
        Screenlabels                   ' Label the values
        LCDBuffer := 0                 ' Zero out the entire LCD buffer  * This may have to be moved outside to prevent conflicts *
        LCDBufOld := 0                 ' Zero out the temporary comparison buffer
        LCD.gotoxy(10,3)
        LCD.str(String("Ready     "))
        Repeat
          If LCDBuffer <> LCDBufOld
            ' Write LCDBuffer values to appropriate screen locations       
            LCD.gotoxy(10,0)
            LCD.decf(LCDBuffer[noparse][[/noparse]0],5)
            LCD.gotoxy(10,1)
            LCD.decf(LCDBuffer[noparse][[/noparse]1],5)
            LCD.gotoxy(10,3)
            LCD.str(String("Ready     "))
            LCD.gotoxy(10,2)
            LCD.decf(LCDBuffer[noparse][[/noparse]2],3)
            LCD.gotoxy(15,2)
            LCD.decf(LCDBuffer[noparse][[/noparse]3],3)
            LCDBufOld := LCDBuffer     ' Set the temporary buffer to reflect the current data
    
    Pub XMCheck : Success              ' Check to see if receiving data
      Success := XB.RxCheck
     
    Pub XMReceive                      ' Uses BS2 function to receive simply because I couldn't get the XBee object working
        BS2.SERIN_STR(0, @Alt, 9600, 1, 8)      ' Receive a CR-terminated string to Alt through pin 0
        Alt[noparse][[/noparse]5] := 0                             ' Convert to z-string for use by LCD and Num objects
    '    LCD.gotoxy(10,0)
    '    LCD.str(@Alt)                           ' Display the altitude string
        AltN := Num.FromStr(@Alt, %000_000_000_0_0_000110_01010 ) ' Convert to 5-digit number for use by MaxAlt
        if (AltN > MaxAlt) & (AltN < 50000)                       ' < 50000 check is to avoid bad data at landing
          MaxAlt := AltN
        LCDBuffer[noparse][[/noparse]0] := AltN
        LCDBuffer[noparse][[/noparse]1] := MaxAlt
     '   LCD.gotoxy(10,1)
     '   LCD.decf(MaxAlt,5)                      ' Display the Maximum altitude
        If AltN < 1                             ' Sometimes the Alt drops below 0 after landing - this accounts for that. 
          Land := TRUE
     '     LCD.gotoxy(10,3)
     '     LCD.str(String("Landed   "))
        else
     '     LCD.gotoxy(10,3)
     '     LCD.str(String("In flight"))
    
    Pub XMBadData
    '  LCD.cls
    '  LCD.gotoxy(10,3)
    '  LCD.str(String("No data   "))
    '  Pause(200)
    
    Pub PinsCheck                                ' This will have to go into a different cog to run at the same time at the altitude data receiver.
      Repeat 
    '    LCD.gotoxy(0,2)
        Event0 := INA[noparse][[/noparse]22]                        ' Get value of input pin 22 
        Event1 := INA[noparse][[/noparse]23]                        ' Get value of input pin 23
        If Event0 <> %00000001
    '      LCD.str(String("E0 "))
          LCDBuffer[noparse][[/noparse]3] := 1
        else
    '      LCD.str(String("No "))
          LCDBuffer[noparse][[/noparse]3] := 0
        If Event1 <> %00000001
    '      LCD.str(String("E1 "))
            LCDBuffer[noparse][[/noparse]4] := 1
        else
    '      LCD.str(String("No "))
          LCDBuffer[noparse][[/noparse]4] := 0
    
    Pub Splash                         ' Display startup screen
      LCD.home
      LCD.backlight(TRUE)
      LCD.gotoxy(0,0)
      LCD.str(@Splash1)
      LCD.gotoxy(0,1)
      LCD.str(@Splash2)
      LCD.gotoxy(0,2)
      LCD.str(@Splash3)
      LCD.gotoxy(0,3)
      LCD.str(@Splash4)
      Pause(1000)
    '  LCD.backlight(FALSE)
    
    Pub Screenlabels
      LCD.gotoxy(0,0)
      LCD.str(String("Alt    : "))
      LCD.gotoxy(0,1)
      LCD.str(String("Max Alt: "))
      LCD.gotoxy(0,3)
      LCD.str(String("Status : "))
    
    PUB Pause(mS)
      waitcnt(clkFreq/1000 * ms + cnt)
    
    DAT
      Splash1 byte "XM Telemetry ",0
      Splash2 byte "Version 1.2  ",0
      Splash3 byte "Paul C. Smith",0
      Splash4 byte "Dec 30, 2009 ",0
    
  • JonnyMacJonnyMac Posts: 9,208
    edited 2009-12-30 19:30
    Right now I can't even get a secondary Spin cog to write to an LCD object that was defined by the main cog. Not sure why (I'm using DEBUG_LCD for my testing). I believe there's a thread on using one FDS object with two cogs -- will track it down as it may provide a missing piece.
  • sylvie369sylvie369 Posts: 1,622
    edited 2009-12-31 17:00
    Um, WOW. It works, mostly.

    Once I sorted everything away correctly, increased stack sizes a bit,·and remembered to put in a pause to let the LCD initialize (oops), the display part of this project is working perfectly.

    I'm still not getting pull-pin data readings until the altitude data start coming in, but while those data are coming in and being displayed, the pull-pin status and the flight status update properly on the LCD. I'm sure it's just a little programming glitch preventing the pull-pins from being updated before takeoff.

    (Later: )

    Okay, I see why it's not displaying before takeoff. My flag to update the display is a line comparing the current LCDBuffer with the old LCDBuffer:

      Repeat
          If LCDBuffer <> LCDBufOld
            ' Write LCDBuffer values to appropriate screen locations       
            LCD.gotoxy(10,0)           ' Write the AltN variable to LCD
            LCD.decf(LCDBuffer[noparse][[/noparse]0],5)
            LCD.gotoxy(10,1)           ' Write the MaxAlt variable to LCD
            LCD.decf(LCDBuffer[noparse][[/noparse]1],5)
            LCD.gotoxy(9,2)            ' Write the flight status
            case LCDBuffer[noparse][[/noparse]2]
              0 : LCD.str(String("Ready     "))
              1 : LCD.str(String("In flight "))
              2 : LCD.str(String("Landed    "))
            LCD.gotoxy(9,3)
            case LCDBuffer[noparse][[/noparse]3]          ' Write the value of pull-pin Event 0
              0 : LCD.str(String("="))
              1 : LCD.str(String("+"))
            LCD.gotoxy(13,3)
            case LCDBuffer[noparse][[/noparse]4]          ' Write the value of pull-pin Event 1
              0 : LCD.str(String("="))
              1 : LCD.str(String("+"))
            LCDBufOld := LCDBuffer     ' Set the temporary buffer to reflect the current data
    

    ·I thought that would check the entire LCDBuffer (which is an array of 8 words) for any changes, but apparently it only looks at LCDBuffer[noparse][[/noparse]0], which is the altitude data. As a result,·it does change when those data change, but not when any of the data further into the array change.
    ·
  • StefanL38StefanL38 Posts: 2,292
    edited 2009-12-31 21:46
    If you avoid value zero you could use the command strcomp to compare the content of LCDBuffer and LCDBufOld

    strcomp(@LCDBuffer,@LCDBufOld)
    As the name says its usually to compare strings that are zeroterminated.

    strcomp compares each byte until it reaches a zero. So both buffers are not allowed to contain a zero except at the end of the buffer
    to sign here is the end.

    As you do already a case I think it will be no problem to just add 1 to each value before storing and increase each case-value by 1 too

    best regards

    Stefan
  • sylvie369sylvie369 Posts: 1,622
    edited 2010-01-01 15:40
    Stefan - Thanks, but it'd be hard to avoid the value 0 in the altitude data. I instead went with a counter stepping through the values of the buffer, first one setting them all to 0 in both the current buffer and the temporary comparison buffer, then at the start of the LCD update loop, stepping through the values looking for any differences (created by actions from the other cogs) between the current buffer (which is written to by other cogs) and the temporary comparison buffer (which is not). I had a few false starts, notably when I tried using "repeat while temp <= 7" to step through the buffer, forgetting that "<=" is an assignment operator, not simply a comparison. Once I got that squared away, though, it worked fine. I really appreciate the help I got here - this is a really useful thing for me to have learned.

    Of course as always in programming, when you pull on a loose thread over here, another one over there moves: I had to change the way that I check to see if the rocket is flying yet, because right after startup·the program·thought that the rocket had already landed because the current altitude was 0. I changed it to require the MaxAlt to be greater than 0 before concluding it was in the air, and that works fine.

    Here is the current code. It all works - it displays the pull-pin status even before receiving any altitude data, and receives and displays altitude data and changes to pull-pin status simultaneously. I believe that this version would do what it's supposed to if I were to fly the setup.

    There are still a few things to add, and some to clean up. I notice that somewhere in the revisions I stopped really using the "Land" flag. The XMBadData method does nothing. I need to document the XBee settings. I also want to add an indicator on the LCD that shows that data are being received - right now you can tell by pulling·a pull-pin and watching for a change in the display, but I would rather know just from looking even when no-one is out at the rocket on the pad.

    ''**  XBee-MAWD Receiver with pull-pins  **
    ''**  Paul C. Smith, January 1st, 2010   **
    ''XBee RX on pin 0
    ''XBee TX on pin 1
    ''LCD connected to pin 8. Parallax 4x20 LCD. 
    ''Pull-pins from XBee DIO0 to Prop pin 22, XBee DIO1 to Prop pin 23 
    
    CON 
        _clkmode = xtal1 + pll16x      '80MHz operating frequency.
        _xinfreq = 5_000_000                      
        Hz    = 100
    
    OBJ 
      XB   : "XBee_Object"       
      Comm : "FullDuplexSerialPlus"  
      Num  : "Numbers"
      LCD  : "Debug_LCD"
      BS2  : "BS2_Functions"
    
    VAR     byte          Alt[noparse][[/noparse]6]       ' Variable to hold the first 6 bytes of altitude data from MAWD. First 5 bytes are 5 digits of altitude, then CR (then LF, unread).
            word          AltN         ' Variable to hold numerical value of altitude, after conversion from string. 
            word          MaxAlt       ' Variable to hold maximum altitude
            byte          Land         ' Has rocket flown and landed?
            byte          Event0       ' Has pull-pin 0 pulled?
            byte          Event1       ' Has pull-pin 1 pulled?
            long          PinStack[noparse][[/noparse]65] ' Stack for cog running PinsCheck (stack size set by trial and error: 9 is too small, and overwrites E1).
            long          LCDStack[noparse][[/noparse]65] ' Stack for cog running LCDUpdate
            word          LCDBuffer[noparse][[/noparse]8] ' Variable to hold values to be displayed on the LCD.
                          { 0 - AltN
                            1 - MaxAlt
                            2 - Flight status: 0 = Ready, 1 = In flight, 2 = Landed
                            3 - Event 0: 0 = No event, 1 = Event
                            4 - Event 1: 0 = No event, 1 = Event
                            5-7 (reserved)
                          }
            word          LCDBufOld[noparse][[/noparse]8] ' Temporary buffer to be compared with current contents to determine whether or not to update LCD.
            byte          temp, flag   ' General purpose variables 
    
    Pub Start 
        XB.start(0,1,0,9600)           ' XBee Comms - RX,TX, Mode, Baud
        XB.AT_init
        XB.AT_Config(String("ATMY 0")) ' Config to receive as Module 0 to match XBee-MAWD telemetry output DL setting.
        DIRA[noparse][[/noparse]22..23]~                  ' Set pins 22 and 23 as inputs from the XBee I/O pins 0 and 1
        XB.AT_Config(String("ATD0 5")) ' Config to use XBee DIO pin 0 as a digital output, default HIGH
        XB.AT_Config(String("ATD1 5")) ' Config to use XBee DIO pin 1 as a digital output, default HIGH
        XB.AT_Config(String("ATIA 1")) ' Config to receive DIO data from module whose MY = 1. 
        XB.AT_Config(String("ATIU 0")) ' Config so that DIO pin states are not sent out UART, where they would interfere with alt data
        
        Num.init                       ' Initialize Numbers object used to convert alt input string to a number
        BS2.start (31,30)              ' Initialize BS2 object used to receive serial data from XBee
     
    ' Zero out the LCD buffers 
        temp := 0                       
        repeat while temp < 8
          LCDBuffer[noparse][[/noparse]temp] := 0         
          LCDBufOld[noparse][[/noparse]temp] := 0         
          temp := temp + 1  
     
        Cognew(LCDUpdate, @LCDStack)   ' Start the LCD updating method continuously in a separate cog. 
     
    ' Initialize Flight Variables
        AltN   := 0                    ' Start at 0 altitude
        MaxAlt := 0                    ' Set max altitude to 0 as well. 
        Land   := FALSE                ' Clear the Landing variable
        Event0 := %00000000            ' Clear the pull-pin variables 
        Event1 := %00000000            ' Event0 and Event1
        
        Cognew(PinsCheck, @PinStack)   ' Run the PinsCheck method continuously in a separate cog.
     
        Repeat while Land == FALSE
          If XMCheck                   ' If data received
            XMReceive                  ' Get and display the data
          Else
            XMBadData    
    
    Pub LCDUpdate                      ' Method to initialize LCD, display splash screen, screen labels, and then update the LCD display data. 
        LCD.init(8, 9600, 4)           ' Initialize LCD
        Pause(1000)                    ' Let the LCD settle in
        LCD.cls                        ' Clear the screen
        Splash                         ' Show startup screens
        LCD.cls
        Screenlabels                   ' Label the values
        LCD.gotoxy(9,2)
        LCD.str(String("Ready     "))
        Repeat                         ' Repeat this part continuously...
          ' See if the LCD Buffer status has changed 
          Flag := 0                    ' Clear change flag
          temp := 0                    ' Start with LCDBuffer[noparse][[/noparse]0]
          repeat while temp <  8       ' Step through the buffer locations
            if LCDBuffer[noparse][[/noparse]temp] <> LCDBufOld[noparse][[/noparse]temp]  ' If there is a change
              Flag := 1                ' Set the change flag
            temp := temp + 1
         ' If the LCD Buffer status has changed, update the LCD     
          If Flag                      ' If the change flag is set
            LCD.gotoxy(10,0)           ' Write the AltN variable to LCD
            LCD.decf(LCDBuffer[noparse][[/noparse]0],5)
            LCD.gotoxy(10,1)           ' Write the MaxAlt variable to LCD
            LCD.decf(LCDBuffer[noparse][[/noparse]1],5)
            LCD.gotoxy(9,2)            ' Write the flight status
            case LCDBuffer[noparse][[/noparse]2]
              0 : LCD.str(String("Ready     "))
              1 : LCD.str(String("In flight "))
              2 : LCD.str(String("Landed    "))
            LCD.gotoxy(9,3)
            case LCDBuffer[noparse][[/noparse]3]          ' Write the value of pull-pin Event 0
              0 : LCD.str(String("No "))
              1 : LCD.str(String("Yes"))
            LCD.gotoxy(15,3)
            case LCDBuffer[noparse][[/noparse]4]          ' Write the value of pull-pin Event 1
              0 : LCD.str(String("No "))
              1 : LCD.str(String("Yes"))
            LCDBufOld := LCDBuffer     ' Set the temporary buffer to reflect the current data
    
    Pub XMCheck : Success              ' Check to see if receiving data
      Success := XB.RxCheck 
    
    Pub XMReceive                      ' Uses BS2 function to receive simply because I couldn't get the XBee object working
        BS2.SERIN_STR(0, @Alt, 9600, 1, 8)      ' Receive a CR-terminated string to Alt through pin 0
        Alt[noparse][[/noparse]5] := 0                             ' Convert to z-string for use by LCD and Num objects
        AltN := Num.FromStr(@Alt, %000_000_000_0_0_000110_01010 ) ' Convert to 5-digit number for use by MaxAlt
        if (AltN > MaxAlt) & (AltN < 50000)                       ' < 50000 check is to avoid bad data at landing
          MaxAlt := AltN
        LCDBuffer[noparse][[/noparse]0] := AltN                    ' Update the LCD buffer
        LCDBuffer[noparse][[/noparse]1] := MaxAlt                  '      with the new values
        If MaxAlt > 0                           ' If the rocket has launched...
          If AltN < 1                           ' Sometimes the Alt drops below 0 after landing - this accounts for that. 
            LCDBuffer[noparse][[/noparse]2] := 2                   ' Change flight status to "Landed". 
          else
            LCDBuffer[noparse][[/noparse]2] := 1                   ' Or to "In flight"
    
    Pub XMBadData
    '  LCD.cls
    '  LCD.gotoxy(10,3)
    '  LCD.str(String("No data   "))
    '  Pause(200)
    
    Pub PinsCheck                                ' This will have to go into a different cog to run at the same time at the altitude data receiver.
      Repeat 
        Event0 := INA[noparse][[/noparse]22]                        ' Get value of input pin 22 
        Event1 := INA[noparse][[/noparse]23]                        ' Get value of input pin 23
        If Event0 <> %00000001                   ' Default is pulled high to 1
          LCDBuffer[noparse][[/noparse]3] := 1                      ' If it's not pulled high, flag that the event has occurred
        else
          LCDBuffer[noparse][[/noparse]3] := 0                      ' Otherwise continue to indicate no event
        If Event1 <> %00000001                   ' Same for the other pull-pin
          LCDBuffer[noparse][[/noparse]4] := 1
        else
          LCDBuffer[noparse][[/noparse]4] := 0
    
    Pub Splash                         ' Display startup screen
      LCD.home
      LCD.backlight(TRUE)
      LCD.gotoxy(0,0)
      LCD.str(@Splash1)
      LCD.gotoxy(0,1)
      LCD.str(@Splash2)
      LCD.gotoxy(0,2)
      LCD.str(@Splash3)
      LCD.gotoxy(0,3)
      LCD.str(@Splash4)
      Pause(1000)
    '  LCD.backlight(FALSE)
    
    Pub Screenlabels
      LCD.gotoxy(0,0)
      LCD.str(String("Alt    : "))
      LCD.gotoxy(0,1)
      LCD.str(String("Max Alt: "))
      LCD.gotoxy(0,2)
      LCD.str(String("Status : "))
      LCD.gotoxy(0,3)
      LCD.str(String("Events 1:    2:   "))
    
     
    PUB Pause(mS)
      waitcnt(clkFreq/1000 * ms + cnt)
    
    DAT
      Splash1 byte "XM Telemetry ",0
      Splash2 byte "Version 1.2  ",0
      Splash3 byte "Paul C. Smith",0
      Splash4 byte "Jan 1, 2010  ",0
    

    Post Edited (sylvie369) : 1/1/2010 5:41:52 PM GMT
Sign In or Register to comment.