Shop OBEX P1 Docs P2 Docs Learn Events
Help with sample code for a NooB — Parallax Forums

Help with sample code for a NooB

iQuitiQuit Posts: 78
edited 2010-04-21 15:11 in Propeller 1
Just starting with the Propeller; coming over from the BS2 side.
I was wondering if this code could be implemented any better; it does work in its current state. I've tried several versions, and this is my best shot at it.
Any criticism would be appreciated. But go easy, it's my first time.
Data input comes from a phone connected to a 8870 IC on pins 0..3. (DTMF decoder)

CON
 Pin_1 = 1       'Pin code to enter, then #, which = 12
 Pin_2 = 2       'to unLock system. Out of order entry Resets Count
 Pin_3 = 3       'variable to zero. 
 Pin_4 = 4
VAR
 byte Num, Lock, Count
PUB Initiate
 DIRa[noparse][[/noparse]0..4]~
 DIRa~~    'LED on this pin to show success
 Reset
PUB Reset        'initial setup to Lock system
 Lock := 1
 Count := 0
 Scan        
PUB Scan
 repeat
  if INa    'goes high when data is available at 8870 IC output
   Check
  waitcnt(clkfreq/100 + cnt)
  
PUB Check
 Num := INa[noparse][[/noparse]3..0]

 case Num
  11: Reset                     'star  *
  12: checkPIN                  'pound #

 case Count
  0: if Num == Pin_1
      Count += 1
  1: if Num == Pin_2
      Count += 1
  2: if Num == Pin_3
      Count += 1
  3: if Num == Pin_4
      Count += 1
  Other: Reset 
 Scan  
                               
PRI checkPIN 
 if Lock == 1 AND Count == 4
  unLock
 Reset

PRI unLock
 Lock := 0
 Count := 0
 OUTa~~
 waitcnt(clkfreq*2 + cnt)    'make LED light for 2 seconds
 OUTa~
 Reset




Odd thing to note, the case shown in the code works, but if I change it to...

case Count
0: if Num <> Pin_1
Reset
.
.
.
Other: Count += 1

it doesn't work

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
"She may not be very pretty now, but she was somebody's baby once." Bugs Bunny

Post Edited (iQuit) : 4/20/2010 10:30:54 PM GMT

Comments

  • MagIO2MagIO2 Posts: 2,243
    edited 2010-04-20 10:12
    This program won't work very long!

    In Scan you call Check and in Check you call Scan ....
    In Check you call Reset and reset calls Scan and Scan calls Check .....

    that's not a good idea!!!!! You will sooner or later have a stack overflow! This program looks like you were used to do GOTO driven programming before ;o) When you do Scan for example you 'call' the function, which means that a return address is stored on the stack. When the function is done it will return to this address and continue from there, if you allow it to. The important thing here is, that the stack-pointer will be cleaned up again. If you never return the stack will grow unless it wraps around the 32kByte RAM limit and starts from the beginning. And that's where your program will start doing nasty things or hang ;o)
  • MagIO2MagIO2 Posts: 2,243
    edited 2010-04-20 11:39
    CON
    [b][color=red] ' what about the clock settings?? Do you have a crystal attached? Then you need to define [/color][/b]
    [b][color=red] _clkmode        = xtal1 + pll16x
     _xinfreq        = 5_000_000
    [/color][/b]
     Pin_1 = 1       'Pin code to enter, then #, which = 12
     Pin_2 = 2       'to unLock system. Out of order entry Resets Count
     Pin_3 = 3       'variable to zero. 
     Pin_4 = 4
     
    VAR
     byte Num, Lock, Count
     
     
    PUB main
     DIRa[noparse][[/noparse] 0..4 ]~    [color=red]' this is not really needed, as all port pins are defaulted to input[/color]
     DIRa[noparse][[/noparse] 5 ]~~ ' LED on this pin to show success
     Reset
     repeat
       if INA[noparse][[/noparse] 4 ]
         Check
       waitcnt( clkfreq/100 + cnt )
    
    PUB Reset        'initial setup to Lock system
     Lock := 1
     Count := 0
     [color=orange][b]' Scan          -> this one needs to be deleted ... Reset is a function which should return to the caller[/b][/color]
     
    PUB Check
     Num := INA[noparse][[/noparse] 3..0 ]
     case Num
      11: Reset                     'star  *
      12: checkPIN                  'pound #
     
     case Count
      0: if Num == Pin_1
          Count += 1
      1: if Num == Pin_2
          Count += 1
      2: if Num == Pin_3
          Count += 1
      3: if Num == Pin_4
          Count += 1
      Other: Reset  [color=red]' as Reset is now returning, it's fine to call it here
    [/color] [color=orange][b]' Scan   -> this one needs to be deleted ... Check is called by main and should return when it's done[/b][/color]
                                   
    PRI checkPIN 
     if Lock == 1 AND Count == 4
      unLock
     Reset  [color=red]' as Reset is now returning, it's fine to call it here[/color]
     
    PRI unLock
     Lock := 0
     Count := 0
     OUTa[noparse][[/noparse]5]~~
     waitcnt(clkfreq*2 + cnt) 'make LED light for 2 seconds
     OUTa[noparse][[/noparse]5]~
     [color=orange][b]'Reset  ' can be deleted ... unLock is only called by checkPIN and checkPIN is already doing a Reset at the end[/b][/color]
    
    
  • iQuitiQuit Posts: 78
    edited 2010-04-20 16:16
    Thanks guys,
    I thought that there might be some issue with the call stack; and yes, you're correct, having programmed the BS2, I'm used to the Goto
    command. Also, the clock settings were left out for brevity.

    This begs the question, How does one think in terms of the flow of Spin? For example...

    does this work?

    PUB Main
    dosomething
    dosomething else

    PUB MethodA
    domore
    etc.

    PUB MethodB
    yetMoreStuff

    If Main never calls either MethodA, or MethodB, they never will execute, right?
    And if conditions change within another method, causing direction change, does the flow have to return to each previous method in reverse
    order and then finish the remainder of its code? (that one was a little long)

    @ magIO2

    You say, " When you do Scan for example you 'call' the function, which means that a return address is stored on the stack. When the function is done it will return to this address and continue from there, if you allow it to."

    The part about continue from there, if you allow it to. I had a feeling that I was manhandling the flow of the program, but I sometimes need it to go one way only
    if certain conditions exist, without going back and continuing the rest of the code in the previous method. How do I do that? confused.gif

    I will get this stuff, eventually. My main problem, I think, is that I don't fully understand the proper flow of spin. Is there a good statement that can explain
    how to look at this stuff in a way that results in good programming techniques?

    Thank you.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    "She may not be very pretty now, but she was somebody's baby once." Bugs Bunny
  • MagIO2MagIO2 Posts: 2,243
    edited 2010-04-20 18:05
    You should first try to understand the differences in your code compared to the corrected code. It should do the same. Let's have a look at one path through your code: .> means calls

    Scan -> Check -> Scan -> Check -> Scan -> Check -> Scan -> Check ->·Scan -> Check -> CheckPIN -> unLock -> Reset -> go back to the beginning

    all done by gotos which don't exist in SPIN.

    If you have a look at the Scan -> Check sequence .. that's already coded in the repeat loop

    Scan -> Check which then returns to·the end of Scan which is in the repeat, so the Scan starts again

    Scan -> Check with·return

    Scan -> Check with return

    Scan -> Check with return

    Scan -> Check -> CheckPIN -> unLock -> Reset wich will go back the whole call stack to Scan



    To your question about changing the flow: You can change the flow in the caller by using return values which are then checked by if or case statements. If return value equals A, then do this, if return value·equals B then do that, if it equals C do this AND that ....

    A common way to do this for a while and something else if a special condition is met is programming a state-machine.
    The state tells the program what to do and the state can be changed if a condition is met.
  • iQuitiQuit Posts: 78
    edited 2010-04-20 20:09
    MagIO2,

    I think I understand. Here is what I've done to tighten up the code even further. I'll try it tonight to verify that it works. And thanks for your assistance.
    It is greatly appreciated.

    NEW REVISED:
    CON
     Pin_1 = 4    'Pin code to enter: 4341, then #, which = 12
     Pin_2 = 3    'to unLock system. Out of order entry Resets Count
     Pin_3 = 4    'variable to zero. 
     Pin_4 = 1
    VAR
     byte Num, Lock, Count
    
     DIRa[noparse][[/noparse]0..4]~
     Reset        'initial setup to ensure a locked system
    
    PUB Scan
     repeat         'pull up resistor on pin 4, goes low when data available on 8870 IC
      if INa
       Check
     waitcnt(clkfreq/20 + cnt)
    
    PUB Check
     Num := INa[noparse][[/noparse]3..0]
    
     case Num
      11: Reset    'star  *
      12: if Lock == 1 AND Count == 4    'pound #
            unlock        
    
     case Count
      0: if Num == Pin_1
          Count += 1
      1: if Num == Pin_2
          Count += 1
      2: if Num == Pin_3
          Count += 1
      3: if Num == Pin_4
          Count += 1
    
    PRI unLock
     Lock = 0
     Count = 0
     'code here to flash LED on pin 5
    
    PRI Reset
     Lock = 1
     Count = 0
    
    



    Thanks for the fix concerning the large font issue.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    "She may not be very pretty now, but she was somebody's baby once." Bugs Bunny

    Post Edited (iQuit) : 4/20/2010 10:27:51 PM GMT
  • MagIO2MagIO2 Posts: 2,243
    edited 2010-04-20 21:21
    It's because of the arrays. The forum software interprets [noparse][[/noparse]number] as text-size-instruction. You have to put spaces between the brackets and the number : [noparse][[/noparse] 1 ]

    And you should put code in a code section. Then indentation will not be lost. It's the # button above the text-input in case you did a 'Post Reply'. If you use "Quick Reply" you can use [noparse][[/noparse] code ] [noparse][[/noparse] /code ]-tags (but without spaces then)
  • iQuitiQuit Posts: 78
    edited 2010-04-21 02:15
    I've cleaned up some things, and have added some code so that once the system is unlocked, you can send commands--they must be preceded by the * key, which is %1011, Dec 11.
    One of the commands could be to re-lock the system. If a wrong number gets input, Count gets reset to zero. And Count also gets reset to zero after a successful unlock.
    Does it matter where the waitcnt() is located in the scan repeat loop? Before or after the if?

    CON
     _clkmode  = xtal1 + pll16x
     _xinfreq  = 5_000_000
    CON
     Pin_1 = 1       'Pin code to enter: 1234, then #, which = 12
     Pin_2 = 2       'to unLock system. Out of order entry Resets Count
     Pin_3 = 3       'variable to zero. 
     Pin_4 = 4
    
    VAR
     byte Num, Lock, Count, flag
    
    PUB Main
     DIRa[noparse][[/noparse]0..4]~
     DIRa~~                 
     reset
     Scan 
         
    PRI Scan
     repeat
      waitcnt(clkfreq/10 + cnt)
      if INa
       Case Lock
        1: CheckPIN
        0: Command
     
    PRI CheckPIN                 
     Num := INa[noparse][[/noparse]3..0]
    
     case Count
      0: if Num == Pin_1
          Count += 1
          return
      1: if Num == Pin_2
          Count += 1
          return 
      2: if Num == Pin_3
          Count += 1
          return 
      3: if Num == Pin_4
          Count += 1
          return
    
     case Num                   
      12: if Lock == 1 AND Count == 4
           Lock := 0
           Count := 0
           OUTa~~
           waitcnt(clkfreq + cnt)
           OUTa~              
    
     Count := 0    'if Num is not 12 or the Pin_x numbers, in sequence, Count gets reset
    
    PRI Command
     Num := INa[noparse][[/noparse]3..0]
    
     case Num
      11: flag := 1
      1: if flag == 1
          'do something
          flag := 0
      2: if flag == 1
          'do something
          flag := 0
      3: if flag == 1
          'do something
          flag := 0
                              
    PUB reset
     Lock := 1
     Count := 0
     flag := 0
    



    I plan on making this an object that will run in another cog, so most of the methods are PRI. Please feel free to critique. idea.gif

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    "She may not be very pretty now, but she was somebody's baby once." Bugs Bunny
  • MagIO2MagIO2 Posts: 2,243
    edited 2010-04-21 05:43
    If you want to make it an object, then I'd rename main to a more meaningfull name and make the PIN an input parameter of the PUB function. Then you don't have to change the object in case you want to change the pin. Plus it's then possible to have different pin-numbers for different things in the same system. You can pass the PIN in one LONG, using byte 0 as PIN_1, byte 1 as PIN_2 .....

    To make it an object your Scan also needs to return somewhen. Or do you want the PIN object to run in it's own COG the whole time? In which system will this object be used?

    Maybe it's also a good idea to have a timeout version. If the user does not enter the PIN in time you return to the calling function, which then can denies access to the PIN protected function and locks the system if the PIN was not given or correctly given 3 times ... erase the whole EEPROM ;o)
  • iQuitiQuit Posts: 78
    edited 2010-04-21 06:06
    Thanks MagIO2,
    Yes, a better name would be good. When I get closer to that, I'll think of one. Still haven't decided if this will be a separate object, but I do want to have
    this run in a separate cog. Or, is that the same thing? Something like "Phone Handler.spin"

    I'm thinking that the top object will start this when the phone rings, start a timer, and wait for input commands. The top object will stop the cog if Lock hasn't
    been cleared after some time, say a minute. The top object will also be able to initiate a phone call, start this object, and wait for the PIN to clear the Lock,
    and then wait for a command, or beep etc.

    I'm still working out the details--this is fun stuff!

    What if I were to use the following in place of the Scan method shown above?

    PUB Scan
     waitpeq(|< Pin_4, |< Pin_4, 0)
     case Lock
      1: CheckPIN
      0: Command
    repeat
    



    Since this is the only thing that the cog will be doing, what could it hurt? The only fear is that it might lock up. If I were to put a stop method in this object,
    and called it from the top object, would this object see it while in this waitpeq state?
    smilewinkgrin.gif

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    "She may not be very pretty now, but she was somebody's baby once." Bugs Bunny
  • MagIO2MagIO2 Posts: 2,243
    edited 2010-04-21 08:52
    If you want it to run in a different COG and you want to make it an object, you need to do the COGNEW in the init of that object, because you can't start a COG with a function of an objec. For example you call your object pinReader and you have a program using that:
    ·
    CON
     PIN_NUMBER = 1234
    VAR
     long some_stack[noparse][[/noparse]20]
    OBJ
     pin: "pinReader"
    PUB main
     pin.Init( PIN_NUMBER )
     [color=red]cognew( pin.Scan, @some_stack )[/color]
    
    

    This won't work!
    You have to do the cognew inside of the Init.

    The question is: What will your 'top object' do while waiting for the pin-object to release the lock? Really wait or has it other tasks? If it's really waiting, there is no need to start an additional COG! You only need to change the Scan function a bit so that it will return after releasing the lock OR after a timeout.

    Doing a waitpeq is fine if you have it running in a different COG. But what's the repeat at the end good for? It will keep the COG running even if it's done with it's job.

    Waitpeq will halt the COG until the condition is met. So, it won't see anything going on. If your 'top object' calls a stop method, this will run in the same COG as the 'top object' and not in the pin-COG. Stop will then do a COGSTOP with the ID of the pin-COG and the pin-COG will be stopped whatever it's doing. It's not like giving a message 'Could you please stop if possible' ... it's a·forced stop, no chance for the COG to avoid it.
  • iQuitiQuit Posts: 78
    edited 2010-04-21 15:11
    MagIO2,

    The top object will be monitoring other things while the phone handler object is doing its thing; getting a PIN and receiving commands. It will pass these along
    to the top object, or perform the functions itself. The top object will start a timer along with the cog to run phone handler. If, after about a minute, the phone
    handler object hasn't sent the unlock signal, it will do something. Perhaps hang up the phone and try again?

    I see that I don't need the repeat now. Silly me. I'm pretty sure that this will run in another cog.

    Glad to hear that I can stop the running cog from the top object.

    Thank you for all of your help. I'm just getting started with the propeller and can use it. This is a great resource. cool.gif

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    "She may not be very pretty now, but she was somebody's baby once." Bugs Bunny

    Post Edited (iQuit) : 4/21/2010 7:16:09 PM GMT
Sign In or Register to comment.