PDA

View Full Version : Two cogs controlling one LCD



sylvie369
12-30-2009, 01:09 AM
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[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[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[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[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[22] ' Get value of input pin 22
Event1 := INA[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

JonnyMac
12-30-2009, 01:19 AM
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....

StefanL38
12-30-2009, 02:55 AM
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[ 8 ]




cog1 reads the dio and writes to byte 0 to 2




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




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




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




now cog3 does




repeat i from 0 to 7
LCD.tx(MyByte[ 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

JonnyMac
12-30-2009, 05:31 AM
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.

StefanL38
12-30-2009, 01:37 PM
@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

sylvie369
12-30-2009, 07:26 PM
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.

JonnyMac
12-30-2009, 11:43 PM
@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.

kwinn
12-31-2009, 12:20 AM
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.

sylvie369
12-31-2009, 01:15 AM
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...)·· [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.

sylvie369
12-31-2009, 02:12 AM
[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[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[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[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[25] ' Stack for cog running PinsCheck (stack size set by trial and error: 9 is too small, and overwrites E1).
long LCDStack[25] ' Stack for cog running LCDUpdate
word LCDBuffer[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[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[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[0],5)
LCD.gotoxy(10,1)
LCD.decf(LCDBuffer[1],5)
LCD.gotoxy(10,3)
LCD.str(String("Ready "))
LCD.gotoxy(10,2)
LCD.decf(LCDBuffer[2],3)
LCD.gotoxy(15,2)
LCD.decf(LCDBuffer[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[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[0] := AltN
LCDBuffer[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[22] ' Get value of input pin 22
Event1 := INA[23] ' Get value of input pin 23
If Event0 <> %00000001
' LCD.str(String("E0 "))
LCDBuffer[3] := 1
else
' LCD.str(String("No "))
LCDBuffer[3] := 0
If Event1 <> %00000001
' LCD.str(String("E1 "))
LCDBuffer[4] := 1
else
' LCD.str(String("No "))
LCDBuffer[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

JonnyMac
12-31-2009, 03:30 AM
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.

sylvie369
01-01-2010, 01:00 AM
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[0],5)
LCD.gotoxy(10,1) ' Write the MaxAlt variable to LCD
LCD.decf(LCDBuffer[1],5)
LCD.gotoxy(9,2) ' Write the flight status
case LCDBuffer[2]
0 : LCD.str(String("Ready "))
1 : LCD.str(String("In flight "))
2 : LCD.str(String("Landed "))
LCD.gotoxy(9,3)
case LCDBuffer[3] ' Write the value of pull-pin Event 0
0 : LCD.str(String("="))
1 : LCD.str(String("+"))
LCD.gotoxy(13,3)
case LCDBuffer[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[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.
·

StefanL38
01-01-2010, 05:46 AM
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

sylvie369
01-01-2010, 11:40 PM
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[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[65] ' Stack for cog running PinsCheck (stack size set by trial and error: 9 is too small, and overwrites E1).
long LCDStack[65] ' Stack for cog running LCDUpdate
word LCDBuffer[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[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[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[temp] := 0
LCDBufOld[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[0]
repeat while temp < 8 ' Step through the buffer locations
if LCDBuffer[temp] <> LCDBufOld[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[0],5)
LCD.gotoxy(10,1) ' Write the MaxAlt variable to LCD
LCD.decf(LCDBuffer[1],5)
LCD.gotoxy(9,2) ' Write the flight status
case LCDBuffer[2]
0 : LCD.str(String("Ready "))
1 : LCD.str(String("In flight "))
2 : LCD.str(String("Landed "))
LCD.gotoxy(9,3)
case LCDBuffer[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[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[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[0] := AltN ' Update the LCD buffer
LCDBuffer[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[2] := 2 ' Change flight status to "Landed".
else
LCDBuffer[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[22] ' Get value of input pin 22
Event1 := INA[23] ' Get value of input pin 23
If Event0 <> %00000001 ' Default is pulled high to 1
LCDBuffer[3] := 1 ' If it's not pulled high, flag that the event has occurred
else
LCDBuffer[3] := 0 ' Otherwise continue to indicate no event
If Event1 <> %00000001 ' Same for the other pull-pin
LCDBuffer[4] := 1
else
LCDBuffer[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