Shop OBEX P1 Docs P2 Docs Learn Events
Control Code & LCD Communication Problems — Parallax Forums

Control Code & LCD Communication Problems

hitolisolohitolisolo Posts: 11
edited 2013-10-18 23:12 in BASIC Stamp
Brief summary (you're welcome to read everything, of course)
Control Code
It works like it's supposed to. You're probably not getting what you think you are. Make sure you're not changing the variable before outputting it (e.g. in a DEBUG statement).

LCD
Make sure you aren't accidentally taking the LCD pin low (e.g. OUTS = 0); also, try explicitly setting the cursor position at the beginning of the SEROUT command (e.g. SEROUT pin, baudrate, [$80, other_data] for position 0, 0).

Original Post
Hello everyone,
I'm new here (and to forums in general). I've got a couple problems (and I'm not sure if I should post them separately or not, so I'm putting them both below). I was hoping somebody here would be able to catch what I'm missing.
I'm using the basic/plain BS2 module and the software is version 2.5.3. My program is rather long so I'm only going to post what should be the relevant functions.

Problem 1 - Serial LCD w/ Backlight (#27977)
I got a few LCDs, finally, thanks to Radio Shack. I've tried this code with two different displays and I'm seeing the same thing, so I don't thing it's the LCD modules.
' -----[Initialization  ]--------------------------------------------------------------------------------------------------------

'Start:
HIGH lcdPin                                     ' Set the lcdPin high meaning it's not sending data
                                                ' And wait 100ms for it to initialize
PAUSE 100                                       ' This also prevents the computer from believing it's a mouse
SEROUT lcdPin, lcdBaud, [clearLCD]              ' Make sure the display will be clear
PAUSE 5                                         ' Make sure it has time to clear the screen
SEROUT lcdPin, lcdBaud, [lcdOn, backlightOn]    ' Turn off the cursor and the backlight on.
SEROUT  lcdPin, lcdBaud, [$80, REP " "\32]       ' For some reason the LCD  ignores the first command to it so just manually clear it
ltrCode(0) = "E" : ltrCode(1) = "R" : ltrCode(2) = "R" ' Setup ERR code array
GOSUB Reset

' -----[ Main Routine ]---------------------------------------------------------------------------------------------------------
Listen:
SEROUT lcdPin, lcdBaud, [$80, "Listening", REP "."\3, REP " "\4]       ' State that the program is listening
SERIN SerialPin, BaudMode, [STR cmdCode\4, STR actionCode\1]           ' Listen for command code and action code

CalcCartSize:
'SetMBCType:
  address = $0147                                                      ' Address for MBC Type
  GOSUB ReadByte
  SEROUT lcdPin, lcdBaud, [$80, "M ", HEX2 dataVar, " "]               ' Display the MBC Type on the LCD
  SELECT dataVar                                                       ' Determine MBC Type
    CASE $00, $08 TO $09                                               ' No MBC
      mbcType = 0
    CASE $01 TO $03                                                    ' MBC1
      mbcType = 1
    CASE $05 TO $06                                                    ' MBC2
      mbcType = 2
    'CASE $0B TO $0D                                                   ' MMM01 - let Case Else handle
    CASE $0F TO $13                                                    ' MBC3
      mbcType = 3
    CASE $15 TO $17                                                    ' MBC4
      mbcType = 4
    CASE $19 TO $1E                                                    ' MBC5
      mbcType = 5
    CASE ELSE                                                          ' Report error and reset
      IF NOT (dataVar | $00) THEN
        mbcType = 0
      ELSE
        SEROUT SerialPin, BaudMode, [STR ltrCode\3, "5", dataVar]
        GOSUB Reset
        SEROUT lcdPin, lcdBaud, [$94, STR ltrCode\3, "5 ", HEX2 dataVar]             ' Echo error on LCD
        GOTO Listen
      ENDIF
  ENDSELECT
'  RETURN

'CalROM:
  address = $0148
  GOSUB ReadByte
  SEROUT lcdPin, lcdBaud, ["R ", HEX2 dataVar, " "]                    ' Display the ROM data on the LCD
  IF dataVar <= $07 THEN
    romSize = DCD (dataVar + 1)                                        ' # banks = (2 ^ romSize + 1)
  ELSE
    SELECT dataVar                                                     ' Special cases
      CASE $52
        romSize = 72
      CASE $53
        romSize = 80
      CASE $54
        romSize = 96
      CASE ELSE                                                        ' Report error and reset on unknown size
        SEROUT SerialPin, BaudMode, [STR ltrCode\3, "3", romSize]
        GOSUB Reset
        SEROUT lcdPin, lcdBaud, [$94, STR ltrCode\3, "3 ", HEX2 romSize]           ' Echo error on LCD
        GOTO Listen
    ENDSELECT
  ENDIF
'  RETURN

'CalRAM:
  address = $0149
  GOSUB ReadByte
  SEROUT lcdPin, lcdBaud, ["RAM ", HEX2 dataVar]                       ' Display the RAM data on the LCD
  SELECT dataVar
    'CASE $00 AND (mbcType = 2)                                         ' MBC2 has no external RAM
    '  ramSize = 1                                                      ' But 1 internal bank
    '  ramLength = $A1FF                                                ' With 512 address
    'CASE $00
    '  ramSize = 0                                                      ' No RAM
    '  ramLength = 0
    CASE $01
      ramSize = 1                                                      ' 1 bank
      ramLength = $A7FF                                                ' 2K RAM
    CASE $02
      ramSize = 1                                                      ' 1 bank
      ramLength = $BFFF                                                ' 8K RAM
    CASE $03
      ramSize = 4                                                      ' 4 banks
      ramLength = $BFFF                                                ' 8K RAM each
    CASE ELSE                                                          ' Report error on unknown value and reset
      IF (dataVar = $00) AND (mbcType = 2) THEN                        ' Above case not compiling so this is here
        ramSize = 1
        ramLength = $A1FF                                              ' Here's the error report otherwise
      ELSEIF NOT (dataVar | $00) THEN                                  ' = $00 fails to run correctly both above in SELECT CASE
         ramSize = 0                                                    ' and  here in the IF statement. NOT should dislike anything over 0
         ramLength = 0                                                  ' and  OR-ing dataVar with $00 should only return 0 if it's already 0
      ELSE
        SEROUT SerialPin, BaudMode, [STR ltrCode\3, "4", dataVar]      ' Unknown value, report error
        GOSUB Reset                                                    ' Reset
        SEROUT lcdPin, lcdBaud, [$94, STR ltrCode\3, "4 ", HEX2 dataVar] ' Echo error on LCD
        GOTO Listen
      ENDIF
  ENDSELECT
  RETURN
The documentation says to pause 100ms at the beginning of the program to allow it to initialize and to raise the communication pin high first. Which are the very first things I do.
Next I send $C to clear the screen and pause 5ms as instructed. Then $16 to turn the LCD on (no cursor) and $11 for the backlight.
Before I added SEROUT lcdPin, lcdBaud, [$80, REP " "\32] I would only see "ing..." on the LCD when it first started and the backlight didn't always come on.
However, when I return to Listen: from within the program it displays "Listening..." correctly
I've tried increasing the pause at the beginning of the program up to 1s and I've tried both baud rates with no effect.
I see this again in the bottom function where these three lines occur:
SEROUT lcdPin, lcdBaud, [$80, "M ", HEX2 dataVar, " "]
SEROUT lcdPin, lcdBaud, ["R ", HEX2 dataVar, " "]
SEROUT lcdPin, lcdBaud, ["RAM ", HEX2 dataVar]
However, all I get is "RAM xx" (where xx is the hexidecimal form of dataVar) at position 0, 0 - nothing from the first two lines displays.
Does anybody know what the problem is or how to fix it?

Problem 2 - Control Code (i.e. SELECT CASE, IF...)
SELECT dataVar
    CASE $00
      ramSize = 0                                                      ' No RAM
      ramLength = 0
    CASE ...
    CASE ELSE                                                          ' Report error on unknown value and reset
      IF (dataVar = $00) AND (mbcType = 2) THEN                        ' Above case not compiling so this is here
        ...
      ELSEIF NOT (dataVar | $00) THEN                                  ' = $00 fails to run correctly both above in SELECT CASE
         ramSize = 0                                                    ' and  here in the IF statement. NOT should dislike anything over 0
         ramLength = 0                                                  ' and  OR-ing dataVar with $00 should only return 0 if it's already 0
      ELSE
        ...
      ENDIF
  ENDSELECT
Hopefully that's already clear. Basically CASE 0 and IF (dataVar = 0) THEN will not run their code dispite dataVar being 0.
Am I missing something here (I'm quite positive dataVar is zero) or is it not compiling correctly (or something else)?

Appoligies for the length, but I'd really appreciate any help that can be offered.

Comments

  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2013-10-15 09:08
    Welcome to the forums. In this case I think you should have attached your code because there is some information missing that might help such as baud rate of the LCD. Also, what happens in the Reset routine you call immediately after Initialization? Nonetheless I will copy your Init code and attempt to test it at 19.2K (Both switches ON).
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2013-10-15 09:26
    Okay, let's start with the first part. Here is the code I just wrote using parts of your code. The line:
    [COLOR=#3E3E3E][FONT=Parallax]SEROUT  lcdPin, lcdBaud, [$80, REP " "\32]       ' For some reason the LCD  ignores the first command to it so just manually clear it[/FONT][/COLOR]
    

    Was part of the problem I was having so I removed it. This code displays "Listening..." as you intended, which makes me also wonder what happens in the Reset subroutine.
    ' {$STAMP BS2}
    ' {$PBASIC 2.5}
    
    lcdPin          PIN   15
    lcdBaud         CON   32
    
    LcdCls          CON     $0C             ' clear LCD (use PAUSE 5 after)
    LcdBLon         CON     $11             ' backlight on
    LcdBLoff        CON     $12             ' backlight off
    LcdOff          CON     $15             ' LCD off
    LcdOn1          CON     $16             ' LCD on; cursor off, blink off
    LcdOn2          CON     $17             ' LCD on; cursor off, blink on
    LcdOn3          CON     $18             ' LCD on; cursor on, blink off
    LcdOn4          CON     $19             ' LCD on; cursor on, blink on
    LcdLine1        CON     $80             ' move to line 1, column 0
    LcdLine2        CON     $94             ' move to line 2, column 0
    
    
    ' -----[Initialization  ]--------------------------------------------------------------------------------------------------------
    
    'Start:
    HIGH lcdPin                                     ' Set the lcdPin high meaning it's not sending data
                                                    ' And wait 100ms for it to initialize
    PAUSE 100                                       ' This also prevents the computer from believing it's a mouse
    SEROUT lcdPin, lcdBaud, [LcdCls]                ' Make sure the display will be clear
    PAUSE 5                                         ' Make sure it has time to clear the screen
    SEROUT lcdPin, lcdBaud, [lcdOn1, LcdBLOn]       ' Turn off the cursor and the backlight on.
    
    ' -----[ Main Routine ]---------------------------------------------------------------------------------------------------------
    Listen:
    SEROUT lcdPin, lcdBaud, [$80, "Listening", REP "."\3, REP " "\4]       ' State that the program is listening
    
    STOP
    

    Note that I had to add my own constants as these weren't available to make the code run originally. I will look at the next part in a moment (multi-tasking like crazy today).

    IMG_20131015_093217_728.jpg
    1024 x 577 - 50K
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2013-10-15 09:38
    Okay, as for the second part. I added your code:
    SEROUT lcdPin, lcdBaud, [$80, "M ", HEX2 dataVar, " "]SEROUT lcdPin, lcdBaud, ["R ", HEX2 dataVar, " "]
    SEROUT lcdPin, lcdBaud, ["RAM ", HEX2 dataVar]
    
    
    

    ...after a pause to the original code. I had to declare the variable and guess that it was a byte value. I assigned it 255 which is $FF in HEX. Here's what I get:

    IMG_20131015_093139_580.jpg


    This is exactly what I would expect to see based on the code. So if there is an issue perhaps I am not clear on what you're expecting here versus what I am seeing. I used the code below to get this.
    ' {$STAMP BS2}' {$PBASIC 2.5}
    
    
    lcdPin          PIN   15
    lcdBaud         CON   32
    
    
    LcdCls          CON     $0C             ' clear LCD (use PAUSE 5 after)
    LcdBLon         CON     $11             ' backlight on
    LcdBLoff        CON     $12             ' backlight off
    LcdOff          CON     $15             ' LCD off
    LcdOn1          CON     $16             ' LCD on; cursor off, blink off
    LcdOn2          CON     $17             ' LCD on; cursor off, blink on
    LcdOn3          CON     $18             ' LCD on; cursor on, blink off
    LcdOn4          CON     $19             ' LCD on; cursor on, blink on
    LcdLine1        CON     $80             ' move to line 1, column 0
    LcdLine2        CON     $94             ' move to line 2, column 0
    
    
    dataVar         VAR     Byte            ' Unsure what size this should be?
    dataVar         =       255             ' Test value should give $FF in existing routine.
    
    
    ' -----[Initialization  ]--------------------------------------------------------------------------------------------------------
    
    
    'Start:
    HIGH lcdPin                                     ' Set the lcdPin high meaning it's not sending data
                                                    ' And wait 100ms for it to initialize
    PAUSE 100                                       ' This also prevents the computer from believing it's a mouse
    SEROUT lcdPin, lcdBaud, [LcdCls]                ' Make sure the display will be clear
    PAUSE 5                                         ' Make sure it has time to clear the screen
    SEROUT lcdPin, lcdBaud, [lcdOn1, LcdBLOn]       ' Turn off the cursor and the backlight on.
    
    
    ' -----[ Main Routine ]---------------------------------------------------------------------------------------------------------
    Listen:
    SEROUT lcdPin, lcdBaud, [$80, "Listening", REP "."\3, REP " "\4]       ' State that the program is listening
    
    
    PAUSE 1000
    
    
    SEROUT lcdPin, lcdBaud, [$80, "M ", HEX2 dataVar, " "]
    SEROUT lcdPin, lcdBaud, ["R ", HEX2 dataVar, " "]
    SEROUT lcdPin, lcdBaud, ["RAM ", HEX2 dataVar]
    
    
    STOP
    
    1024 x 577 - 60K
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2013-10-15 09:52
    On the last problem I am unclear what you're trying to do in the code. If dataVar is 0 two variables get set to 0, but nothing else should happen given the current code.

    CASE with ... not sure what that is. And the CASE ELSE relies on two variables, with one evaluation that won't ever get seen. Since the first CASE $00 handles dataVar being 0 then I would never expect the ELSE to be reached and be TRUE for dataVar being 0, since it would never make it there if dataVar was 0. I hope this helps.
  • hitolisolohitolisolo Posts: 11
    edited 2013-10-15 11:50
    Also, what happens in the Reset routine you call immediately after Initialization? Nonetheless I will copy your Init code and attempt to test it at 19.2K (Both switches ON).
    Thanks for the replies. Sorry about the missing info, I though I had included it. I've attached a file you should be able to simply upload to your Stamp this time.
    Okay, let's start with the first part. Here is the code I just wrote using parts of your code. The line:
    [COLOR=#3E3E3E][FONT=Parallax]SEROUT  lcdPin, lcdBaud, [$80, REP " "\32]       ' For some reason the LCD  ignores the first command to it so just manually clear it[/FONT][/COLOR] 
    
    Was part of the problem I was having so I removed it. This code displays "Listening..." as you intended, which makes me also wonder what happens in the Reset subroutine.
    Interesting. I found it was necessary for I'd only get "ing..." on the display, however I just removed the line and it's still displaying correctly (which I don't understand as it should be the same code that was causing problems earlier now).
    Okay, as for the second part. I added your code:
    SEROUT lcdPin, lcdBaud, [$80, "M ", HEX2 dataVar, " "]SEROUT lcdPin, lcdBaud, ["R ", HEX2 dataVar, " "] SEROUT lcdPin, lcdBaud, ["RAM ", HEX2 dataVar] 
    
    
    ...after a pause to the original code. I had to declare the variable and guess that it was a byte value. I assigned it 255 which is $FF in HEX. Here's what I get:

    Attachment 104405

    This is exactly what I would expect to see based on the code. So if there is an issue perhaps I am not clear on what you're expecting here versus what I am seeing. I used the code below to get this.
    That's what I would expect, but I'm only getting "RAM 00" starting at position 0, 0.

    Hopefully that will due for now, because I've got to get to school now.
    I'll address what I wasn't able to here when I get the chance.
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2013-10-15 14:22
    Peter,

    When you run the code I posted do you get what I get or the same thing you got before? As I suspected by looking at your code you're properly initializing the LCD in the section called, "Start", however your "Reset" routine right after toggles the LCD pin which would cause the next command to most likely fail. Here is where this happens:
    OUTS = $0000                                                         ' Clear any outputs  dataDir = $00                                                        ' Set data bus to input
      DIRH = %01111111                                                     ' Set all but unused pin 15 to output
      cmdCode = $0000                                                      ' Clear cmdcode - can only assign 16 bits at a time
      cmdCode(2) = $0000
      actionCode = 0                                                       ' Clear action code
      address = $0000                                                      ' Clear address
      dataVar = $00                                                        ' Clear data
      SendConfirm = 1                                                      ' Send confirmation codes by default
      GOSUB ClearShift                                                     ' Clear the shift registers
      HIGH writePin                                                        ' Disble write
      HIGH readPin                                                         ' Disable read
      HIGH sRAM                                                            ' Disable RAM select
      HIGH lcdPin                                                          ' Default signal for lcdPin
      SEROUT lcdPin, lcdBaud, [clearLCD]                                   ' Clear the LCD
      PAUSE 5                                                              ' Give it time to clear itself
    
    
    

    Two things are happening here...the first is that you're toggling the I/O pin used by the LCD low, and then HIGH again. In addition you've already initialized and cleared the LCD, but in this block you're attempting to clear it again which would most likely fail since the I/O pin was brought low then high immediately before the command. You have initialization code it two entirely different sections of the code which makes it hard to know what's going on during initialization since these blocks are at opposite ends of the source code. I would recommend initializing all your I/O and Variables in the same block of code so you know when you've already set an I/O pin to the state you need it during start up.

    Aside of the problems I saw in your evaluations, I'm guessing you're not getting the data you expect sometimes. I would use a DEBUG statement here and there to see what variables are set to upon entry into a routine. You can also use a DEBUG statement to show when a certain routine is run. DEBUG can be a very helpful troubleshooting aid when things don't seem to be running right.

    I hope this helps.
  • hitolisolohitolisolo Posts: 11
    edited 2013-10-15 23:17
    When you run the code I posted do you get what I get or the same thing you got before?
    I've run your code now and I get the same output as you did.
    As I suspected by looking at your code you're properly initializing the LCD in the section called, "Start", however your "Reset" routine right after toggles the LCD pin which would cause the next command to most likely fail. Here is where this happens:

    ...

    Two things are happening here...the first is that you're toggling the I/O pin used by the LCD low, and then HIGH again. In addition you've already initialized and cleared the LCD, but in this block you're attempting to clear it again which would most likely fail since the I/O pin was brought low then high immediately before the command. You have initialization code it two entirely different sections of the code which makes it hard to know what's going on during initialization since these blocks are at opposite ends of the source code. I would recommend initializing all your I/O and Variables in the same block of code so you know when you've already set an I/O pin to the state you need it during start up.
    Ah, I completely missed that. I originally wrote this with a piezospeaker so clearing everything worked and I missed updating that when I added the LCD. That has been addressed now and the initialization has been fixed so it only clears the screen once.
    The reason for the separate function is the LCD only needs its 100ms pause when the circuit's first powered and the Reset function let's me effectively restart the program without having to cycle the power.
    Now, with the exception of those two commands (HIGH lcdPin : PAUSE 100) all initialization is within the function as I should have had it.

    The file I uploaded, however - have you had a chance to run it? I can't get a clear picture of the screen, but the top line displays "RAM 00eader" rather than the expected "M 00 R 02 RAM 00".
    I've updated it to reflect the above changes (so I'll upload it again), but I'm still getting the same output.

    As for the control code, I'm almost certain I've found my problem. I'm sending data to the computer (and in this specific case) to the LCD as feedback (hence the lack of DEBUG statements - plus the full program doesn't have the extra memory). However, I just realized I'm resetting dataVar before outputting it on the LCD and I never checked the computer record to confirm they agreed - hence why I thought it was 0 yet still running the CASE ELSE code. Now that completely makes sense - I'll just have to rewrite that code. Now if I can get this last piece of code with the LCD to work everything will be great.
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2013-10-16 10:24
    Peter,

    I did look at it, and unfortunately you're still toggling the LCD pin when you call the Reset subroutine, most likely causing the next byte sent to the LCD to be not recognized. This reset routine is really problematic, however I think the bigger issue is that you have multiple exit points from subroutines you've called with a GOSUB. This effectively means there are GOSUB without RETURN issues in the code. For example, at one point you call a routine:
    GOSUB CalcCartSize                                                   ' Calculate info on MBC, ROM, and RAM
    
    

    However, in this subroutine you have code like:
    CASE ELSE                                                          ' Report error and reset      SEROUT SerialPin, BaudMode, [STR ltrCode\3, "5", dataVar]
          GOSUB Reset
          SEROUT lcdPin, lcdBaud, [$94, STR ltrCode\3, "5 ", HEX2 dataVar]             ' Echo error on LCD
          GOTO Listen
    
    
    

    This allows the code to branch without returning from the subroutine, causing an internal Stack Overflow, which can cause undesirable operation. And there are at least two of these within that subroutine.
  • hitolisolohitolisolo Posts: 11
    edited 2013-10-16 18:49
    Peter,

    I did look at it, and unfortunately you're still toggling the LCD pin when you call the Reset subroutine, most likely causing the next byte sent to the LCD to be not recognized.

    Ok. I should have tested my solution code by itself. I tried OUTS = DCD lcdPin so as to clear everything but the lcdPin, however, the compiler apparently thought I wanted to use the pin state rather than the pin number for this calculation (because I declared it with PIN). Is there a way to tell the compiler you want the pin number, rather than its state, without having to use a separate constant? I've replaced it with a constant for now and it's keeping the pin high as I had originally expected.
    However that didn't fix the output.
    For some reason when I change the SEROUT commands to send the correct cursor position at the beginning of all three commands I'm now getting the correct results:
    SEROUT lcdPin, lcdBaud, [$80, "M ", HEX2 dataVar, " "]
    SEROUT lcdPin, lcdBaud, [$85, "R ", HEX2 dataVar, " "]
    SEROUT lcdPin, lcdBaud, [$8A, "RAM ", HEX2 dataVar]
    
    I'm not sure why that fixes it (because the code works without $85, $8A when I run it by itself), but at least it's working correctly now.
    This allows the code to branch without returning from the subroutine, causing an internal Stack Overflow, which can cause undesirable operation.
    I'm aware of that, but the reason is because I wanted to "restart" the program via its software and that was the only way I knew how when using functions. As I understand it, the four most recent function calls can always find there way home, so the overflow doesn't cause a problem (if it works as I understand it to, at least). Is there a better way to restart the program from within itself? (I understand RETURN without a previous GOSUB is supposed to restart the program, but since I'm using GOSUB that doesn't work.)

    I've cleaned up the Reset function and moved it to the top of the program so it's a little nicer and incorporated all the changes above and the program appears to be working now, so I'll upload the file one more time in case you or somebody else would like to see the changes.
    Thank you very much for all your help.
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2013-10-17 08:43
    Peter,

    In looking at your code I can't see any reason why you would have to randomly restart the code. You seem to be trying to simulate a reset of the BASIC Stamp by resetting all the I/O etc. however I don't see a reason why you would need to do that. I've never seen an application where normal usage required a routine to reset everything to startup conditions save a watchdog timer which usually handles an issue where the CPU has gone into an unknown/undesirable state and it is reset. But you're not doing a reset, so internal stack pointers and such are not being reset.
    Peter wrote:
    As I understand it, the four most recent function calls can always find there way home, so the overflow doesn't cause a problem (if it works as I understand it to, at least). Is there a better way to restart the program from within itself? (I understand RETURN without a previous GOSUB is supposed to restart the program, but since I'm using GOSUB that doesn't work.)

    That's just not the case...if you call a subroutine with a GOSUB and then exit that subroutine without a RETURN and then call the same subroutine again from the same point in the code you're never going to even things out. The best I can tell in your code that is what is happening. Good programming practice is to have a declaration section which defines your I/O, constants and variables. In a well-written program this should only need to be done once. There shouldn't be a need to dynamically create new variables and the I/O pins should be fine as far as initial settings unless you're putting the MCU to sleep. After declaration you would initialize any connected hardware and preset I/O pin states and variable values. Your main program should be a loop and in most programs this will all be encompassed within a DO..LOOP construct. Everything that happens inside that loop is your main program and much of it will rely on calls to subroutines using GOSUB..RETURN calls. Within each subroutine a series of things happen terminating with a RETURN. once you use something in there like, IF <condition> THEN GOTO <label> you break the subroutine, make it difficult to follow the code flow and have random, weird and difficult to find issues as you're seeing.

    I used to do this all the time in BASIC languages on the PC. I had spaghetti code and while it seemed logical to me at the time, when I had a large application with issues I understood what people meant when I had trouble tracking down the issues and others who looked at my code didn't even want to try and follow it. While microcontrollers don't usually have such large applications it is easy to start writing spaghetti code. If you tend to think of the program in the manner I described above and perhaps consider using flowcharts to develop it, you could have some really functional programs in the end. It's worth doing this now while things are fresh in your head. There's nothing worse than going back to a program months or years later and not having a clue what you did or why you did it. :innocent: Yes, I've done this. Still do. But it's much easier to follow now just due to formatting and arrangement. I hope this helps.
  • hitolisolohitolisolo Posts: 11
    edited 2013-10-18 23:12
    Sorry for the delay, I was only recently able to catch up here.
    Peter,

    In looking at your code I can't see any reason why you would have to randomly restart the code.You seem to be trying to simulate a reset of the BASIC Stamp by resetting all the I/O etc. however I don't see a reason why you would need to do that. I've never seen an application where normal usage required a routine to reset everything to startup conditions save a watchdog timer which usually handles an issue where the CPU has gone into an unknown/undesirable state and it is reset. But you're not doing a reset, so internal stack pointers and such are not being reset.



    That's just not the case...if you call a subroutine with a GOSUB and then exit that subroutine without a RETURN and then call the same subroutine again from the same point in the code you're never going to even things out. The best I can tell in your code that is what is happening. Good programming practice is to have a declaration section which defines your I/O, constants and variables. In a well-written program this should only need to be done once. There shouldn't be a need to dynamically create new variables and the I/O pins should be fine as far as initial settings unless you're putting the MCU to sleep. After declaration you would initialize any connected hardware and preset I/O pin states and variable values. Your main program should be a loop and in most programs this will all be encompassed within a DO..LOOP construct. Everything that happens inside that loop is your main program and much of it will rely on calls to subroutines using GOSUB..RETURN calls. Within each subroutine a series of things happen terminating with a RETURN. once you use something in there like, IF <condition> THEN GOTO <label> you break the subroutine, make it difficult to follow the code flow and have random, weird and difficult to find issues as you're seeing.

    I used to do this all the time in BASIC languages on the PC. I had spaghetti code and while it seemed logical to me at the time, when I had a large application with issues I understood what people meant when I had trouble tracking down the issues and others who looked at my code didn't even want to try and follow it. While microcontrollers don't usually have such large applications it is easy to start writing spaghetti code. If you tend to think of the program in the manner I described above and perhaps consider using flowcharts to develop it, you could have some really functional programs in the end. It's worth doing this now while things are fresh in your head. There's nothing worse than going back to a program months or years later and not having a clue what you did or why you did it. :innocent: Yes, I've done this. Still do. But it's much easier to follow now just due to formatting and arrangement. I hope this helps.
    Well to start with, it's not random. Notice there are very specific addresses set before I read my data bus. If an unrecognized byte is read at one of these address it's (at this point in the program, at least) being considered an unrecoverable error. And this function is at least two calls deep in the full program (remember I only uploaded the code that was producing the bug), so a simple return statement wouldn't work. GOTO was all I found at the time to return from two-three functions deep and it's the only time GOTO was used (to return to the top of my main program "loop"). (Also the GOTO statements aren't the problem with the LCD not displaying the strings at the right positions, as the entire display would be overwritten if they were executed - and that does function correctly.)
    All that said, I had another look at the program and determined I could set a one bit flag variable if an error occured and use IF statements to check for errors after the function calls in the higher functions and finally replaced my main program loop - GOTO Listen - with a proper DO...LOOP, so everything returns as it should (there's not one GOTO statement left in my code) and the internal stack should always be happy now. And I think I've come to conclude I only really need to reset the error flag at the beginning of the loop instead of trying to reinitialize almost everything. So thank you for encouraging me to write it properly. However, as I expected, that did nothing with the LCD misdisplaying the strings - I still have to explicitly set the cursor position at the beginning of each SEROUT dispite wanting to pick up from where the last command left it. I really don't mind that, however, as I have the 4 extra bytes needed - I just don't understand why I need to do that. And you didn't mention if there actually is a way to truly reset the chip through the program's code.

    Anyway, seeing as everything's working like it should now, I'm going to mark this thread as solved and add a bullet note summary to my first entry.
    And once again thank you for all your help and advice, Chris, I really appreciate you taking the time to go over all of this.
Sign In or Register to comment.