Menu

Curious program lock-up: Found solution, but not reason.

Dave B
2025-01-20
2025-01-21
  • Dave B

    Dave B - 2025-01-20

    I put together a simple circuit to sound 3 random beeps using the "Tone()" command and turn on an LED when an input is grounded, and three more beeps with LED turn-off when pin is un-grounded. It is a circuit/program I had built years ago, but for which I'd since lost the code. The circuit was easy to build, very simple. The program is also very straight-forward other than generating suitable random tones. However, every time I run the new code, the program would lock up immediately after the input was grounded and the LED and first tone sounded. After hours of puttering both at my shop and at home, I found that if I simply moved a line of code to a spot before the "Tone ()" command everything worked as it should. The problem is that I have no idea why the fix worked or why the problem was there in the first place. I'm posting the original and fixed code below for reference.

    Here is the original code:

    'declare chip type and speed
    
    #chip 12F683, 8
    
    'force variable declaration before use to avoid confusion and possible programming errors
    
    #option explicit
    
    'define pins
    
    #define TXI gpio.0
    #define TXO gpio.1
    #define SoundOut gpio.2
    
    'set pin directions
    
    dir TXI in
    dir TXO out
    dir SoundOut out
    
    'declare variables
    
    dim freq as integer
    dim oldfreq as integer
    dim rnd as integer
    
    'seed the random number generator, not that it matters for this program's intent
    
    randomize gpio.4
    
    'beginning of main program loop
    
    LOOPSTART:
    
    'initial tx check. we stop here until mic is keyed
    
      do while txi = on
      loop
    
    'We have keyed up. turn on tx, play tones, turn off tx
    
      set TXO on
      playtone
      playtone
      playtone
      set TXO off
    
    'now we wait until mic unkeyed
    
      do while txi = off
      loop
    
    'now unkeyed, so we turn tx back on, play tones, then turn off tx
    
      set txo = on
      playtone
      playtone
      playtone
      set txo = off
    
    'and go back to program start and wait til next keyup
    
    goto LOOPSTART
    
    
    'subroutine to play a random tone
    
    sub playtone
    
    FREQGEN:
    
    'generate the random number > 20 to limit low end of tone range
    
      rnd = Random
      if rnd < 20 then goto FREQGEN
    
    'set freq and check limits and spacing. Generate new freq if needed
    
      freq = ((rnd*10) + 500)
      if freq = oldfreq then goto FREQGEN
      if freq > 2000 then goto FREQGEN
      if oldfreq < freq then
        if (freq - oldfreq) < 300 then goto FREQGEN
      end if
      if oldfreq > freq then
        if (oldfreq - freq) < 300 then goto FREQGEN
      end if
    
    'if freq requirements met, play tone
    
      Tone(freq,5)
    
    'remember the tone to compare later
    
      oldfreq = freq
    
    'then exit subroutine
    
    end sub
    

    Here is the code that works:

    'declare chip type and speed
    
    #chip 12F683, 8
    
    'force variable declaration before use to avoid confusion and possible programming errors
    
    #option explicit
    
    'define pins
    
    #define TXI gpio.0
    #define TXO gpio.1
    #define SoundOut gpio.2
    
    'set pin directions
    
    dir TXI in
    dir TXO out
    dir SoundOut out
    
    'declare variables
    
    dim freq as integer
    dim oldfreq as integer
    dim rnd as integer
    
    'seed the random number generator, not that it matters for this program's intent
    
    randomize gpio.4
    
    'beginning of main program loop
    
    LOOPSTART:
    
    'initial tx check. we stop here until mic is keyed
    
      do while txi = on
      loop
    
    'We have keyed up. turn on tx, play tones, turn off tx
    
      set TXO on
      playtone
      playtone
      playtone
      set TXO off
    
    'now we wait until mic unkeyed
    
      do while txi = off
      loop
    
    'now unkeyed, so we turn tx back on, play tones, then turn off tx
    
      set txo = on
      playtone
      playtone
      playtone
      set txo = off
    
    'and go back to program start and wait til next keyup
    
    goto LOOPSTART
    
    
    'subroutine to play a random tone
    
    sub playtone
    
    FREQGEN:
    
    'generate the random number > 20 to limit low end of tone range
    
      rnd = Random
      if rnd < 20 then goto FREQGEN
    
    'set freq and check limits and spacing. Generate new freq if needed
    
      freq = ((rnd*10) + 500)
      if freq = oldfreq then goto FREQGEN
      if freq > 2000 then goto FREQGEN
      if oldfreq < freq then
        if (freq - oldfreq) < 300 then goto FREQGEN
      end if
      if oldfreq > freq then
        if (oldfreq - freq) < 300 then goto FREQGEN
      end if
    
    'remember the tone to compare later
    
      oldfreq = freq
    
    'if freq requirements met, play tone
    
      Tone(freq,5)
    
    'then exit subroutine
    
    end sub
    

    The only change is moving the line that says "oldfreq = freq" (and associated comment) to just before the "Tone()" command.

    Can someone explain why that simple change fixed the problem, and what the original problem even was? Any informative input will be appreciated.

    I've attached the working GCB code file for reference.

     

    Last edit: Dave B 2025-01-21
  • Anobium

    Anobium - 2025-01-20

    You have resolved by reducing the stacks.

    A quick examination of the ASM shows the way the compiler changes the behaviour from a call to a goto.

    The attachment shows the different.

    While you cannot see the stack issue in the attachment the use of Tone uses a set of complex calls to divide and multiple calcs which must consume the stack.

    There is a now way to inspect the stacks on this specific chip. There are no Status bits to indicate stack overflow or stack underflow conditions. There are no instructions/mnemonics called PUSH or POP. These are actions that occur from the execution of the CALL, RETURN, RETLW and RETFIE instructions or the vectoring to an interrupt address.

    So, you resolved a complex issue and the compiler help you.

     
  • Dave B

    Dave B - 2025-01-21

    Thank you for your reply. I'll have to go look at the assembly file for more detail. The screenshots you posted only show the assembly for the two different operations, IE copying the freq data to a 2nd variable and executing the Tone command. as opposed to showing why putting the command to save the current tone after the tone sub might have caused the stack issue. A stack issue does make perfect sense, however, especially with the limited memory available.

    PS: Just added to the mystery - I decided to tinker one last time and try the program with the command to duplicate the freq data before, then after, the tone command, and now it works fine both ways. Before it would lock it up if the dupe command was executed after the tone command. I do still think you're on the right track with the possible stack issue, but now I don't know why the issue no longer shows up where it did before. More head scratching to come apparently....

     

    Last edit: Dave B 2025-01-21
  • Anobium

    Anobium - 2025-01-21

    There is no mystery to me.

    When you have Tone() as the last operation the compiler optimises the asm to use a GOTO rather than a CALL. This saves one stack usage. This is all it takes to work the stack.

    The compiler will optimise where is can. If the Tone() operation was not the last operation then a CALL has to be used... stack falls over.

     
  • Dave B

    Dave B - 2025-01-21

    Thank you again for your reply and your time!

    Definitely understood about the stack. But, as in my PS from last night, the program started working normally with the Tone call BEFORE the duplicate variable assignment. (oldfreq = freq). Nothing else was changed in the code. THAT one IS a mystery to me at the moment, or at least until I have a chance to go back over the code and make sure I didn't inadvertently change something else and not remember it ciz I was too tired/sleepy. Assuming I didn't change anything else besides the order of those two commands, why would the stack fail before with the variable assignment being the last part of the sub and not fail now with the same code that was crashing before? One thing I didn't try was a few different 12F683 chips to see if maybe it wasn't an intermittent chip fault of some kind.

     
    • Anobium

      Anobium - 2025-01-21

      Pleasure.

      Comparing the. ASM will help you understand. Not the instructions but the flow. The compiler is very clever and it will optimise in different ways and each way may or may impact the stack.

       

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.