Shop OBEX P1 Docs P2 Docs Learn Events
Question on accessing an object across cogs — Parallax Forums

Question on accessing an object across cogs

dharwooddharwood Posts: 12
edited 2011-07-31 09:39 in Propeller 1
I have a DS1302 that has a value (word) stored in the first two bytes of RAM.

If I run the following code as a method on the primary cog, I can retrieve and display it fine.
CON
  _clkmode        = xtal1 + pll16x                      ' Feedback and PLL multiplier
  _xinfreq        = 5_000_000                           ' External oscillator = 5 MHz
VAR
  word  SwitchCount                                     ' Counter to hold total number of switch closures
  byte  cmd
  
OBJ
  pst   : "Parallax Serial Terminal"                    ' Serial Terminal object - requires 1 cog
  rtc   : "DS1302_full"
     
PUB Start                                               ' Main method
  pst.Start(115_200)
  pst.Str(string("Starting up..."))
  rtc.init( 22, 21, 20 )                                'ports Clock, io, chip enable
  rtc.config                                            'Set configuration register
  SwitchWatch(@SwitchCount)
  waitcnt(clkfreq * 2 + cnt)                            ' Wait 1 second -> .5 Hz
  pst.Str(string(pst#NL, "RAM value..."))
  pst.Dec(SwitchCount)
PRI SwitchWatch(CounterAddr)
  cmd:=rtc.command(rtc#ram,rtc#burst,rtc#r)
  rtc.readN(cmd, CounterAddr, 1)

If instead I launch the method on its own cog, it doesn't work.
CON
  _clkmode        = xtal1 + pll16x                      ' Feedback and PLL multiplier
  _xinfreq        = 5_000_000                           ' External oscillator = 5 MHz
VAR
  word  SwitchCount                                     ' Counter to hold total number of switch closures
  long  Stack[20]                                       ' Sets up a stack space
  byte  SwitchCogID                                     ' Cog ID of the SwitchWatch Cog
  byte  cmd
  
OBJ
  pst   : "Parallax Serial Terminal"                    ' Serial Terminal object - requires 1 cog
  rtc   : "DS1302_full"
     
PUB Start                                               ' Main method
  pst.Start(115_200)
  pst.Str(string("Starting up..."))
  rtc.init( 22, 21, 20 )                                'ports Clock, io, chip enable
  rtc.config                                            'Set configuration register
  SwitchCogID := cognew(SwitchWatch(@SwitchCount), @Stack) 'Starts a Cog, calls SwitchWatch, the Cog uses @stack
  waitcnt(clkfreq * 2 + cnt)                            ' Wait 1 second -> .5 Hz
  pst.Str(string(pst#NL, "RAM value..."))
  pst.Dec(SwitchCount)
PRI SwitchWatch(CounterAddr)
  cmd:=rtc.command(rtc#ram, rtc#burst, rtc#r)
  rtc.readN(cmd, CounterAddr, 1)

Being new to Spin and Propeller, I sure I am making a basic blunder but I can't see it. Anyone want to clue me in to what I am doing wrong in the second example?

Thanks!

Comments

  • kuronekokuroneko Posts: 3,623
    edited 2011-07-29 22:05
    The RTC initialisation happens in the main cog (usually #0). Which means the I/O direction registers affected are the ones from said cog #0. Now you call an RTC function in a cog which doesn't have any or only a partial I/O setup. This is due to the way the driver is written. You may find others where it doesn't matter. For your example I suggest moving the initialisation into the SwitchWatch method (which forces the I/O to be upset in the same (cog) context). HTH
  • dharwooddharwood Posts: 12
    edited 2011-07-30 06:35
    kurenko -

    Thanks! I have moved the rtc.init and rtc.config calls into the SwitchWatch method, and as you suggest, it does work.

    Unfortunately, I was hoping to use this object on both cogs. I want to use the clock functions on the main cog and the ram functions on the seperate cog.

    My thinking was that SwitchWatch would run in its own cog and count switch closures via a shared VAR, that also gets persisted to the DS1302 RAM. This way, everytime it restarts, it can pick up where it left off.

    Meanwhile the main cog could be logging the "current" count at set time intervals, and reset it to zero once it is captured.

    Am I barking up the wrong tree here? Is this not going to work using two seperate cogs to access this chip?

    Thanks
  • Mike GreenMike Green Posts: 23,101
    edited 2011-07-30 07:54
    It is always more complicated for two separate processors (the two cogs) to share parts of a single I/O device. There has to be some way to prevent both processors from trying to access the same device (and the I/O pins used) at the same time. In the case of the Propeller, both cogs have to have access to the same I/O pins. All of this is doable, but the RTC object isn't written to do it.

    Say more about how often and how quickly you have to access the DS1302. You will need to be become familiar with the LOCKxxx statements. Have a look at the Propeller Manual starting at page 120.
  • kuronekokuroneko Posts: 3,623
    edited 2011-07-30 07:54
    Don't worry, there are ways to achieve this. What I suggested was only meant as a Q&D solution anyway. I'm a bit short on time tomorrow so a cleaner solution will have to wait unless someone else chimes in which I'm sure will happen.

    Update: Wouldn't you know it, they are already queueing :)
  • dharwooddharwood Posts: 12
    edited 2011-07-30 09:26
    Mike - I do not have to access the DS1302 quickly. The counter function will fire slowly (a few times a minute would be extreme). When it fires I would update the counter and persist it to the RAM registers. On the main cog, I would read DS1302 clock about once per minute. There shouldn't be any negative impact on adding a semiphore lock to ensure they don't do these actions at the same time.

    Question...did I understand you correctly, are you saying I should NOT use the RTC object and do it myself with locking? Or should I still use the RTC object, but use locking?

    Thanks for the help!
  • Mike GreenMike Green Posts: 23,101
    edited 2011-07-30 09:45
    Here's a modified version of the DS1302 object that can be called from more than one cog. RTC.init, as described in the comments, should be called only once from your main cog initialization code. RTC.config should also be called from the main cog initialization code sometime later before any other calls to RTC are made. Each other cog that references RTC should call RTC.ioinit before any other calls to RTC are made. The main cog doesn't have to call RTC.ioinit because RTC.init does so.

    Everything else should work the same as it does with the OBEX RTC except that the other methods can be called from multiple cogs. I strongly suggest you look at the modified code. It does two things differently:

    1) Any operation that accesses the DS1302 uses a "lock" first to prevent any other cogs from accessing the DS1302, then releases the lock before returning from RTC.

    2) The I/O pins (OUTA and DIRA for all cogs) are always left in either input mode or output low. If you look at how the various cogs' I/O registers are wired (in the diagram on the Propeller Downloads webpage), this allows multiple cogs to control the I/O pins, changing the pins to either output low or high (from input mode) or to output high (from output low).

    NOTE: I haven't tested this yet, but it should work

    Update: Corrected RTC.config

    Update: Now object can be declared in multiple objects
  • dharwooddharwood Posts: 12
    edited 2011-07-30 12:00
    Mike,

    Thank you for modifying the DS1302_Full object to work with multiple cogs!

    I tested it and it works, except for one thing....

    I had to comment out the: repeat while lockset(locknum) and lockclr(locknum) lines within the RTC.Config method. While I don't pretend to know why this caused it to freeze, I didn't think they were needed, since the RTC.Config should only be called once from the main cog, therefore shouldn't require locking.

    With that change, I was able to have one instance of the RTC obj defined and access it's methods from both cogs.

    Can't thank you enough!

    Dan
  • StefanL38StefanL38 Posts: 2,292
    edited 2011-07-30 23:37
    I have another idea how to do this.

    There is one cog who is accessing the hardware. This cog does it all the time updating some variables in a DAT-section
    every other cog can access the variables in the DAT-ection thrugh the HUB-RAM-adresses. So other cogs only read the
    information from the variables.

    If any cog wants to change the RTC-registers this would be done over DAT-variables too.

    keep the questions coming
    best regards

    Stefan
  • Mike GreenMike Green Posts: 23,101
    edited 2011-07-30 23:43
    I made a mistake in RTC.config and forgot to change the read( ) calls to readInt( ). The correct code is:
    PUB config
    ''Config DS1302.  Call once after DS1302 power-up.
    ''Usage: rtc.config
    
      repeat while lockset(locknum)
      writeInt(command(clock,ctrl,w),0)                                         'clear write-protect bit
      writeInt(command(clock,sec,w),readInt(command(clock,sec,r)) & %0111_1111) 'clear halt bit
      writeInt(command(clock,hr,w),readInt(command(clock,hr,r)) & %0111_1111)   'set 24hr mode
      writeInt(command(clock,tc,w),0)                                           'disable trickle charger
      lockclr(locknum)
    
    You're correct that the lockset / lockclr is not really necessary since RTC.config is only called once in the main cog's initialization, but this way, you could call RTC.config from any of the cogs to actually initialize the DS1302.

    I've updated the archive on message #7
  • dharwooddharwood Posts: 12
    edited 2011-07-31 08:07
    Ahhh, I see...it was calling lockset twice. I missed that.

    Tried your modified version and naturally, it works like a charm.

    Thanks again!
  • Mike GreenMike Green Posts: 23,101
    edited 2011-07-31 09:39
    I've updated the archive in message #7 again. This version has all global variables in a DAT section so that the RTC object can be declared in more than one object and the I/O pin numbers and lock number saved by RTC.init can be used by all the declared instances. At some point, I will modify the demo to demonstrate this usage and post this version to the OBEX.
Sign In or Register to comment.