Shop OBEX P1 Docs P2 Docs Learn Events
2 Cogs - Sensory and Reactionary. Need advice — Parallax Forums

2 Cogs - Sensory and Reactionary. Need advice

ALIBEALIBE Posts: 299
edited 2006-09-15 18:30 in Propeller 1
I have 2 cogs. Cog#1, doing the sensory fns and the other Cog #2, reacting to the data that was gathered by Cog #1.

Please see code below.
The Main, inits sensors (ds1620) and output devices (LCD) and then sets up Cogs #1 to Sense and #2 to react to the data

However, upon running the app, I am able to see that the Init functions are working fine w/o any issues. The Cog fns, Sense and React do not get invoked.

What am I doing wrong. Please HELP!!

Thanks, Nagi

CON 
  _clkmode = xtal1 + pll16x 
  _xinfreq = 5_000_000 
  T        = 0 
  MAXDATA  = T+1 

VAR 
  Long SensoryCogStack[noparse][[/noparse]30] 
  Long ReactionaryCogStack[noparse][[/noparse]30] 

  Long SensoryData[noparse][[/noparse]MAXDATA] 

 
OBJ 
  DISP : "Serial_LCD" 
  TEMP : "DS1620" 
  NUM : "Simple_Numbers" 

 
PUB MAIN 
  [b]InitReactionaryDevices 
  InitSensoryDevices 
  [/b]
  [b]CogNew(Sense, @SensoryCogStack) 
  CogNew(React, @ReactionaryCogStack) 
[/b]
 
[color=orange][b]PUB Sense 
  Repeat 
   DISP.Str (string("s ")) 
   'Read Temp 
   SensoryData[noparse][[/noparse]T] := Temp.GetTempF 
[/b][/color]
 
[color=green][b]PUB React 
 Repeat 
   DISP.Str (string("r ")) 
   DISP.gotoxy(0,0) 
   DISP.str( string("T =") ) 
   DISP.str( NUM.dec( SensoryData[noparse][[/noparse]T]) ) 
[/b][/color]
PUB InitSensoryDevices 
 ' Init Temp 
  TEMP.Start(1,2,3) 
  DISP.Str (string("i ")) 

 
PUB InitReactionaryDevices 
  DISP.Start(0, 19200, 2) ' 
  DISP.BackLight(1) 
  DISP.Cursor(1) 
  DISP.Cls 
  DISP.str(string("TEST")) 


·

                

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2006-09-13 15:27
    Questions/Comments:
    1) What other cogs are you invoking? How many cogs are used? You're not testing the value returned by COGNEW to be sure those cogs are launched.
    2) Does TEMP involve launching a cog? Maybe Sense is getting hung up in the Temp.GetTempF call.
    3) The DISP object is shared between two cogs. It may get hung up when both Sense and React try to access the LCD at the same time. Best to use a semaphore (LOCKNEW/LOCKSET/LOCKCLR) to make sure only one uses it at a time.
  • David BDavid B Posts: 592
    edited 2006-09-13 15:27
    Maybe you need to have the initialization for each device take place in its spun-off cog instead of in main, because output pins and initialization variables for each cog have to be properly set for that cog to be able to run its device.

    David
  • ALIBEALIBE Posts: 299
    edited 2006-09-13 15:50
    Mike, thanks for the notes

    1) no other cogs just the 2 per above. I am not testing for status since I know that there are free cogs. I probably should still do - what other reasons why ::CogNew() would fail?

    2) I don't fully understand that ?n - TEMP object is used by the ::Sense method. I put a debug msg prior to call to ::GetTempF, still not gettng in.

    3) I will try the ::Lockxxx methods and see if they help

    thanks


    David B,
    thanks. Not clear as to why I would need to init each devices in the cog event. Can you describe why that might be an issue - thanks

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    thanks in advance,


    ALIBE - Artificial LIfe BEing. In search of building autonoumous land robot
  • ALIBEALIBE Posts: 299
    edited 2006-09-13 16:12
    Hello all,
    I added the following code to Sense and React to support LockNew/LockRet. Am I doing this right?

    ran the app, still no luck

    thanks
    PUB Sense [b]| ID[/b]
    [b]  DISP.Str (string("s 1 ")) 
    [/b]  Repeat 
    [b]    ID := LockNew[/b]
    
        DISP.Str (string("s 2 ")) 
        'Read Temp 
        SensoryData[noparse][[/noparse]T] := Temp.GetTempF 
    [b]    LockRet(ID)[/b]
    
    
    PUB React [b]| ID[/b]
    [b]  DISP.Str (string("r 1 ")) 
    [/b]  Repeat 
    [b]   ID := LockNew[/b]
        DISP.Str (string("r 2 ")) 
        DISP.gotoxy(0,0) 
        DISP.str( string("T =") )  
        DISP.str( NUM.dec( SensoryData[noparse][[/noparse]T]) ) 
    [b]   LockRet(ID)
    [/b]
    
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    thanks in advance,


    ALIBE - Artificial LIfe BEing. In search of building autonoumous land robot
  • Mike GreenMike Green Posts: 23,101
    edited 2006-09-13 16:20
    Read the description of LOCKNEW and LOCKSET in the SPIN manual. You use LOCKNEW to get one of 8 free semaphores (in your main initialization routine), then do a "repeat until not LockSet(ID)" before you try to use the DISP object, then do a "LockClr(ID)" after you're done with the DISP object. If you're doing a series of DISP calls, you can do the LockSet before the first call and the LockClr after the last call. Essentially, the cog "owns" the LCD between the LockSet and LockClr.
  • ALIBEALIBE Posts: 299
    edited 2006-09-13 17:01
    Mike,

    thanks for the notes above. I just read thru the manual and have some understanding of how this works. I have made the following changes to my code. Without example (from the manual), I am unable to tell if this is the right way or not.

    Can you share your input, thanks again



    [b]PUB InitSemaphores 
      SenseSemaphoreID := LockNew 
      ReactSemaphoreID := LockNew 
    [/b]
    PUB Sense 
    [b]  Repeat until not LockSet(SenseSemaphoreID) 
    [/b]  DISP.Str (string("s 1 ")) 
    [b]  LockClr(SenseSemaphoreID) 
    [/b]
    Repeat 
     [b] Repeat until not LockSet(SenseSemaphoreID) 
     [/b] DISP.Str (string("s 2 ")) 
     [b] LockClr(SenseSemaphoreID) 
     [/b]  'Read Temp 
      SensoryData[noparse][[/noparse]T] := Temp.GetTempF 
    
     
    PUB React 
    [b]  Repeat until not LockSet(ReactSemaphoreID) 
    [/b]  DISP.Str (string("r 1 ")) 
    [b]  LockClr(ReactSemaphoreID) 
    [/b]
      Repeat 
       [b]Repeat until not LockSet(ReactSemaphoreID) 
       [/b]DISP.Str (string("r 2 ")) 
       
       DISP.gotoxy(0,0) 
       DISP.str( string("T =") ) 
       DISP.str( NUM.dec( SensoryData[noparse][[/noparse]T]) )
       [b]LockClr(ReactSemaphoreID) 
    [/b]
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    ALIBE - Artificial LIfe BEing. In search of building autonoumous land robot

    http://fstop.crosscity.com/
    http://icar.crosscity.com/
    ·
  • David BDavid B Posts: 592
    edited 2006-09-13 17:14
    A while ago I experimented with having a simple Spin LCD display object run in every available cog. Each cog would wait for its lock to be free, then aquire its semaphore lock, send some characters to the LCD then release the lock.

    But it didn't work until each cog set its own "view" of the LCD pin to be output.

    And even then, before each cog released its lock, it had to set the output level to the correct logic in order to allow other cogs to be able to change the logic level of that pin. (If I remember correctly, I even had to add an external inverter in the data line to the LCD so its quiescent state agreed with the Propeller shared output pin quiescent logic state).

    Also, my LCD runs at 9600 baud using a delay that was set in the LCD object initialization. I had to make sure that each cog was using a properly initialized delay variable.

    If your LCD has its data pin set to output in the "DISP" start method, then that is only done in your main cog but not in your Sense cog. It appears that the DISP.start is only run in the main cog in the "InitReactionaryDevices" method call. I see that the "Sense" method calls "DISP", but that cog has not run the "DISP" start method.

    So your sense cog may still have its LCD "output" pin still configured to the default input direction. (And the same goes for the React cog - it too may need to call DISP.start.)

    But I'm far from an expert at this, so listen to Mike's comments as well; he may have better advice than I do.

    David
  • Mike GreenMike Green Posts: 23,101
    edited 2006-09-13 17:35
    Nagi,
    Your Lock code is mostly correct except that there needs to be only one semaphore shared by the two cogs. The semaphore (lock) represents the resource to be locked .. in other words, the DISP routine and its variables, I/O pins, etc. Each cog (Sense and React) have to "ask" for the resource before using it and "return" it when they're done.· So go back to having only one lock called ID.

    David,
    The DISP variables and I/O pins are accessible to any cog and any one of them can initialize them as long as only one cog is accessing them at any time and they're initialized by "someone" before any cog tries to use them. In this case, they're initialized in the InitReactionaryDevices routine which is called by the main routine before anything else. The other cogs don't get started until after the initialization is done.
  • ALIBEALIBE Posts: 299
    edited 2006-09-13 17:46
    Mike,
    AHHHHH - you are correct - it only makes sense to have ONE semaphore - I must have rented my brains out to a local h/w store [noparse]:)[/noparse]

    thanks for that catch. I will give that a shot and circle back.

    thanks again

    Nagi
    (ALIBE)

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    ALIBE - Artificial LIfe BEing. In search of building autonoumous land robot

    http://fstop.crosscity.com/
    http://icar.crosscity.com/
    ·
  • ALIBEALIBE Posts: 299
    edited 2006-09-13 18:06
    Mike,
    still no expected result - can you shed some (more) light please - thanks as always

    [b]PUB InitSemaphores 
     if (LCDSemaphoreID := LockNew) == -1 
       DISP.str(String("err")) 
     else 
       DISP.str(NUM.dec(LCDSemaphoreID)) 
    [/b]
    PUB Sense 
      Repeat until not LockSet(LCDSemaphoreID) 
      DISP.Str (string("s 1 ")) 
      LockClr(LCDSemaphoreID) 
    
      Repeat 
       Repeat until not LockSet(LCDSemaphoreID) 
       DISP.Str (string("s 2 ")) 
       LockClr(LCDSemaphoreID) 
    
     
       SensoryData[noparse][[/noparse]T] := Temp.GetTempF 
     
    PUB React 
      Repeat until not LockSet(LCDSemaphoreID)  
      DISP.Str (string("r 1 ")) 
      LockClr(LCDSemaphoreID) 
    
      Repeat 
       Repeat until not LockSet(LCDSemaphoreID) 
       DISP.Str (string("r 2 ")) 
       DISP.gotoxy(0,0) 
       DISP.str( string("T =") ) 
       DISP.str( NUM.dec( SensoryData[noparse][[/noparse]T]) ) 
       LockClr(LCDSemaphoreID)
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    ALIBE - Artificial LIfe BEing. In search of building autonoumous land robot

    http://fstop.crosscity.com/
    http://icar.crosscity.com/
    ·
  • ALIBEALIBE Posts: 299
    edited 2006-09-13 19:11
    I have looked into a few things into this code.

    I commented out the "React" cog code so what I have is just the "Sensor" cog. The Sensor cog event does not seem to fire. So, there's something else outsde of the Semaphore going on. Could it be the Stack Size?

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    ALIBE - Artificial LIfe BEing. In search of building autonoumous land robot

    http://fstop.crosscity.com/
    http://icar.crosscity.com/
    ·
  • David BDavid B Posts: 592
    edited 2006-09-13 22:04
    I dug out my old multicog flashing LCD demo and have attached it.

    Here it is, and I'm watching it run on my Propeller board right now, with seven separate cogs updating one LCD display.

    This is written for my custom board, so it can't be expected to work as is on any propeller board, but the multi-cogging, locking, pin sharing should be the same.

    This sends incrementing characters to the LCD, spread over its screen positioned according to the cogid doing the sending. As it runs, seven pairs of digits increment across the LCD screen at random times.

    I can't claim that this is written in the best possible Spin style, making the best use of shared variables and other Propeller sources, but it does work flawlessly on my Propeller.

    But it only works if every cog makes its own "dira[noparse]/noparse" initialization to output. It's "DAT" variables are shared among cogs, but I don't believe that the state of each cog's "dira[noparse]/noparse" register are shared.

    Another important item is to look at the polarity of the "tx" data pin. For an output pin to be able to be shared among cogs, the resting state of the pin that is toggled by "tx" must be the correct shared logic state. (I don't remember if it is high or low.) Here I think I had to add the "not" modifier to the "tx" code, and add an external inverter, for the sharing to work.

    So, Nagi, maybe this can give you some tips for how to get your project running.

    Mike, do you see anything in my code that contradicts my understanding of sharing IO pins between cogs?

    David
  • Mike GreenMike Green Posts: 23,101
    edited 2006-09-14 04:06
    David,
    You're right in that each cog has its own DIRA and OUTA registers and they're combined with logic to give the actual I/O pin signals. Sorry, I wasn't thinking. Nagi is going to have to modify the LCD driver code so that the output routines make sure that OUTA and DIRA are initialized properly, but that the variables within DISP are only initialized once. In other words, the start() routine should set up the object including possibly sending data to the LCD to initialize it and leave DIRA either so the I/O pins are inputs or that they are low outputs. The output to LCD routines should set up DIRA and OUTA for their own use. The low outputs allow the I/O pins to be or'd with a high level. The output to LCD routines should leave the output in a low state or an input state, again so another cog can control it.
    Mike
  • Mike GreenMike Green Posts: 23,101
    edited 2006-09-14 04:21
    You could also do something like:
    VAR
      long DISPstring, DISPlock
    PUB main
     ' initialization stuff including for DISP
      DISPstring := 0
      DISPlock := LockNew
      cognew( ***sensor stuff ***)
      cognew( *** reacting stuff ***)
      repeat
        repeat until DISPstring <> 0
        case DISPstring
          1: DISP.gotoxy(0,0)
          other:
            DISP.str(DISPstring)
        DISPstring := 0
    
    PUB WriteDISPstr(str)
      repeat until not LockSet(DISPlock)
      repeat until DISPstring == 0
      DISPstring := str
      LockClr(DISPlock)
    
    PUB Sensor
      WriteDISPstr(1) ' do gotoxy(0,0)
      WriteDISPstr(string(" xx"))
    
    PUB Reactive
      WriteDISPstr(string(" yy"))
    
    


    This lets the main cog continue as a DISP server. There's a shared routine that takes care of the lock stuff and passes the address of a string to the main cog for display or a 1 for a "home" operation. You can add other control codes if you want since a string address will not be in the first few locations of memory. The shared routine only modifies the shared variable when the lock is "in place". The main cog doesn't need to use the lock since it's the only routine that resets the pointer to zero and only one other cog at a time can change the pointer to non-zero.
  • Mike GreenMike Green Posts: 23,101
    edited 2006-09-14 04:30
    Here's another version that doesn't need a lock because each cog has its own pointer.
    VAR
      long DISPsensor, DISPreact
    PUB main | pointer
     ' initialization stuff including for DISP
      DISPsensor := 0
      DISPreact := 0
      pointer := 0
      cognew( ***sensor stuff ***)
      cognew( *** reacting stuff ***)
      repeat
        if DISPsensor <> 0 and pointer == 0
          pointer := DISPsensor
          DISPsensor := 0
        if DISPreact <> 0 and pointer == 0
          pointer := DISPreact
          DISPreact := 0
        case pointer
          1:
            DISP.gotoxy(0,0)
          other:
            DISP.str(pointer)
        pointer := 0
    
    PUB Sensor
      repeat until DISPsensor == 0
      DISPsensor := 1 ' gotoxy(0,0)
      repeat until DISPsensor == 0
      DISPsensor := string(" xx")
    
    PUB Reactive
      repeat until DISPreact == 0
      DISPreact := string(" yy")
    
    
  • ALIBEALIBE Posts: 299
    edited 2006-09-14 13:35
    David, Mike;
    thanks a ton to both of you for your code example and the decriptions around the pin dir setting and sharing.

    this will greatly help me get my project rolling. I will update my code to reflect some of these recommendations.

    Nagi

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    ALIBE - Artificial LIfe BEing. In search of building autonoumous land robot

    http://fstop.crosscity.com/
    http://icar.crosscity.com/
    ·
  • ALIBEALIBE Posts: 299
    edited 2006-09-15 17:18
    David, Mike,
    please see attached file w/ my modifications to reflect the call to "DirA[noparse]/noparse" on each of the Cog's event. I am using Parallax's serial_lcd·whose RX is on pin 0.

    There are a few issues:
    1. The ::Sense event fires and not the ::React, let alone LCD displays
    2. If I changed the cog stack size t0 anything lower than 16 longs, the LCD displays garbage. But, the wierd thing abt this is, I do not have anything that large that calls for a 16 longs size in the stack.

    Can you provide some insight to this

    thanks in advace

    Nagi


    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    ALIBE - Artificial LIfe BEing. In search of building autonoumous land robot

    http://fstop.crosscity.com/
    http://icar.crosscity.com/
  • Mike GreenMike Green Posts: 23,101
    edited 2006-09-15 17:57
    The I/O pins are or'd together (and the direction values are or'd as well). That would be ok if the resting (idle) value of the I/O pin were zero. For normal serial devices, the resting (idle) state is one. The DISP routines leave the DIRA bit set to one and the OUTA bit set to one. Once you try to transmit from a 2nd cog, nothing further goes to the display (the output pin is always set to a one). A simple way to get around this is to use a pullup resistor on the transmit pin (maybe 10K to either 3.3V or 5V). Then always set the DIRA bit at the end of LCDDisplay (before the ReleaseLCDAccess) back to zero (or input). That way, only one cog will actually use the I/O pin, yet the LCD display will always see an idle state of one (from the pullup).

    At the beginning of LCDDisplay, set OUTA[noparse][[/noparse]0] := 1 before you do the DIRA[noparse][[/noparse]0] := 1. That way, on the first call, you won't get a brief pulse of zero (since OUTA is initially zero when a cog starts).
  • Mike GreenMike Green Posts: 23,101
    edited 2006-09-15 18:05
    The minimum stack size is a little vague. Each level (of call) takes at least two longs for a return address and a pointer to the previous stack "frame". Some routines have implicit local variables. When you do a method (function) call, the arguments have to get pushed onto the stack for example. When React calls LCDDisplay and that calls DISP.GotoXY and that calls something else in DISP, you already need 12+ longs at least.
  • ALIBEALIBE Posts: 299
    edited 2006-09-15 18:30
    Mike, the DirA and OutA recommendation above did the trick - thanks a lot for your help again - this will now get me to the next steps in my project.

    thanks, Nagi

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    ALIBE - Artificial LIfe BEing. In search of building autonoumous land robot

    http://fstop.crosscity.com/
    http://icar.crosscity.com/
    ·
Sign In or Register to comment.