Making better use of COGs

As I see it this program is already using all 8 COGs and I still have things to add. Can I use the CTRx s of a cog to do simple timing tasks simultaneously? Is there a way to get more done with less COGs?
PUB init  
  tv.start(0)	'TV_Text
  ow.start(10)		'One-wire routines
  I2C.init(SCL,SDA)	'for RTC and HUM21D humidity
  
  cognew  (RTCcog,@RTCstack)     'looks like there may be 2 instances of I2C object
  cognew(windSpeed,@stack)	'counts pulses from annemometer
  cognew(counter,@countstack)	'for controlling program events
  cognew(fanTimer,@timeStack)	'tracks furnace fan run time

Thanks
Aaron

Comments

  • Does your main cog have enough spare cycles to use one of the pure-spin OneWire and/or I2C objects, as opposed to the pasm objects (IIRC, there were pure-spin versions of both of those)? That could shave off up to two. They wouldn't be fast, but for RTC and Humidity readings, maybe they don't need to be, depending on your application.

    Cheers,
    Jesse
  • I'm not entirely sure what an annemometer is, but the counters can count pulses for you in the background ("POSEDGE detector" and "NEGEDGE detector" modes), IDK if that applies here.
  • AGCB wrote: »
    Can I use the CTRx s of a cog to do simple timing tasks simultaneously?

    Usually yes.
    AGCB wrote: »
    Is there a way to get more done with less COGs?

    I'd be very surprised if there isn't a way you can combine cogs.

    Do you know the minimun period for the annemometer pulses? Can you combine the annemometer cog with the timekeeping cog?

    If you post an archive of your program, I bet you'd get suggestions on how to combine cogs.
  • Are you saying that your I2C code requires a cog? Unless you're moving a lot of data and need to do it quickly, I've found that running I2C in Spin is fine.

    Are you using an external RTC chip? If yes, no need to use a cog since you have at least one second between any changes in the RTC.

    As Ada pointed out, you may be able to use a counter to handle the events of your anemometer. I have a time object that measures the delta in system clock ticks that I use for scheduling events. I've attached it and my Spin I2C code. I've deployed both these objects in commercial products.

    Let's say you wanted to collect the anemometer counts and the time every second. With my time object you might do something like this
    pub main
    
      setup
    
      repeat
        if (time.millis => 0)
          time.adjust(-1000)
          process_data
    
    
    pub process_data
    
      rtc.rd_time(@secs)
      windCounts := phsa
      phsa := 0
    

    Over the years I have found that even though Spin is supposed to be slow, you can get a lot of work done in what humans consider a very small amount of time.

  • Peter JakackiPeter Jakacki Posts: 9,826
    edited 2020-11-03 - 00:02:35
    In many of my older projects I would dedicate a cog for general timer operation so the app could select a "timer", load it and also set an "alarm" action. So you could get the background cog to maintain the countdown timer values, and automatically execute code if set, maintain a runtime millisecond counter, and also use this together with the RTC value which only needs to be read once at reset or each day. That way apps can access the time of day and date without having to I2C it each and every time. Is that what you want to do. JM is right too btw in that Spin is fine to bit-bash low priority I2C access, no need to dedicate a cog.
  • Looks like -- for the moment, anyway -- you're using two cogs: the main app and the TV driver; everything else is in Spin and running in the main cog.
  • If the anemometer uses a magnet and reed switch (typical), you'll need to read it with Spin code in order to apply debouncing. Chances are that a counter will report too many edges due to contact bounce.

    -Phil
  • If the background timer cog runs every 1ms then it should be able to take care of counting the anemometer easily enough too. If you want some Spin code to kick it off I'm sure I can dig through one of my old projects. I think I first used it in my 12 channel high-speed pill counter project that used 4 Props.
  • Q.1
    Do all objects in the Propeller Library use a COG? If not, how do I know which do or do not?

    Q.2
    I know how to set up the CTRx 's but I've always done it in a separate COG. I tried to do it in the INIT method but it causes the program to hang. How am I wrong?

    @Phil Pilgrim (PhiPi)
    I'm using magnets and a 3144 Hall switch. I assumed this put out a clean signal but see the datasheet says 2 us rise and fall times
    If the background timer cog runs every 1ms then it should be able to take care of counting the anemometer easily enough too. If you want some Spin code to kick it off I'm sure I can dig through one of my old projects....

    @Peter Jakacki
    ....So you could get the background cog to maintain the countdown timer values, and automatically execute code if set, maintain a runtime millisecond counter, and also use this together with the RTC value which only needs to be read once at reset or each day. That way apps can access the time of day and date without having to I2C it each and every time. Is that what you want to do...

    The timer I think I can figure out but I'm not quite understanding the RTC part.

    @JonnyMac
    So I can take the parts (methods) I need from an I2C object like yours and put them in the top object to use as needed?

    Thanks
    Aaron

  • I read the RTC in after reset and once a day, then convert the time to milliseconds which is then maintained by the millisecond background timer tick. When my app wants the "time" all it needs to do is read the millisecond counter and convert that back to time of day and if it wants the date then this is latched once a day so it can just read that. The other useful thing to do is when the RTC is read, is to convert the date to a timestamp format offset from a pivotal date (doesn't have to be 1970, could be 2020) so that if you needed a timestamp you can just read that and add in the current milliseconds, all within microseconds rather than waiting for the I2C bus transfer. i use this same technique in Tachyon too with a background timer cog which also maintains a soft RTC that is updated once a day or at reset.

    The Spin code for a background timer cog is very simple really. I will see if I can post something.
  • AGCB wrote: »
    Q.1
    Do all objects in the Propeller Library use a COG? If not, how do I know which do or do not?


    Just open up the object and see if the start method starts up a cog.
  • So I can take the parts (methods) I need from an I2C object like yours and put them in the top object to use as needed?
    There is no need. I think you're under the impression that any time you declare an object, you're starting a cog. This is not the case. An object must specifically start a cog (with cognew or coginit). You can use a generic Spin I2C object with your program and the code will be run by the master cog.
  • JonnyMac wrote: »
    everything else is in Spin and running in the main cog.

    Did you notice this?
      cognew  (RTCcog,@RTCstack)
      cognew(windSpeed,@stack)
      cognew(counter,@countstack)
      cognew(fanTimer,@timeStack)
    

    I'm pretty sure windSpeed, counter, and fanTimer could be combined to a single cog. You could probably add the RTCcog to the same cog.

    The program should easily be able to handle more tasks.

  • Peter JakackiPeter Jakacki Posts: 9,826
    edited 2020-11-03 - 23:42:24
    Yes, I counted 5 coginits in the compiler listing plus 1 cog for the startup Spin cog. The RTCcog seems totally unnecessary as it waits 1 second just to read the RTC, but then again WindSpeed waits 1 second, so you could combine that into one as does fanTimer and all it does is FT++, as does counter etc. Just put calls to all these simple once every second routines (or code inline) into one cog for starters and then you would have 3 cogs running, the main Spin, the TV, and the one second timer that calls that other stuff loop.

    btw, reading the rtc every second will not always appear smooth because sometimes you read it on the cusp and your display may seem to stall or jump. Better to increment the time of day in software and just read the RTC every day or every hour, but not once a second.

    The windspeed hardware counter may need hardware debouncing if you are using reed switches. Alternatively let your timer cog run much faster and simply prescale the slower events but check the pulse inputs in software which will be slow enough that it will in effect debounce it. However a simple capacitor across the reed switch should help if you are using a pullup/down.
  • OK so I'm going to try to work on this timer thing doing more that is currently being done with what I have. I'll experiment on a different Quickstart board until I get a part to work before adding it to the current program. May take me days I only work on it a few hours per day.

    @Peter Jakacki
    I'd still like to see basically how you do the timer with RTC setup. I think I understand the basics but a snippet would help!

    Thanks to all for your time.
    Aaron
  • Peter JakackiPeter Jakacki Posts: 9,826
    edited 2020-11-04 - 01:23:32
    Here is a "snippet" so you can paste this in and remove the old routines. However I noticed your main routine is one big long trail of code. Factor sections out into logical function (and name them accordingly) so that your main just has a series of calls to these functions and your code will be a lot easier to read and maintain.

    I created an array of pulsecounts with the last and latest reading being named pulsecount itself so it was easy to do a longmove rather than one by one. Remove all your cognew calls that are mentioned in TimerCog routine. There is lots more you can do to your code to make it cleaner and meaner and way easier to maintain. ReadTemperature could be called every second as well from the TimerCog. The granularity might have to be reduced from 1ms to 10ms to allow enough time to do all that processing, so adjust the code accordingly.
    VAR
      long pulsecounts[7],pulsecount
    
    PUB TimerCog | millisec
    ' replace all these cognews with cognew(TimerCog,@stack)
    '  cognew  (RTCcog,@RTCstack)
    '  cognew(windSpeed,@stack)
    '  cognew(counter,@countstack)
    '  cognew(fanTimer,@timeStack)
             'Set up the counters
      ctra := %01110 << 26 + pin1     'counter A in NEG edge detect mode
      frqa  := 1               'increment 1 per pulse
      phsa~
      I2C.init(SCL,SDA)               ' Timer cog has control of I2C
      ReadRTC
      repeat
        waitcnt(clkfreq/1000)
        millisec++
        ifnot millisec//1000              ' once every second
          OneSecond
    
    
    pub OneSecond
          ms++
          if ms == 512
            ms~
          FT++                      ' fan timer
          WindSpeed
          timbuf[seconds]++         ' maintain seconds rather than reading the I2C RTC every time
          if timbuf[seconds] == 60   ' read rtc every time the seconds overflow
            ReadRTC
    
    
    pub WindSpeed | i
      longmove(pulsecounts,pulsecounts+4,7)             ' shift counts for new entry at end of array
      pulsecount := phsa
      phsa~
      speed~
      repeat i from 0 to 7
         speed += pulsecounts[i]
      speed /= 8
    
    pub ReadRTC
      I2C.readbytes(RTC,0,@timbuf,7)
        IF INA[17] ==1               'DST/CST switch for daylight savings time
             timbuf[hours] := timbuf[hours] - 1   'this will be summer (spring ahead)
    
        IF timbuf[hours] == $0A         'correct for these ↓ 3 odd displays (0A, 24, 1A)
           timbuf[hours] := $10
        ELSEIF timbuf[hours] == $24
           timbuf[hours] := $00
        IF timbuf[day] == SAT
          timbuf[day] := SUN
        ELSE
           timbuf[day] +=1  'show as next day++++++++++++added 05:27  10/APR
        IF timbuf[hours] == $1A
           timbuf[hours] := $20
    
  • Did you notice this?
    I was looking at a bit of code that he posted as an attachment. This is a case where it would be better to know the outcome than to deal with the present code. There are probably lots of ways to succeed with fewer cogs; it would be useful for the OP to see and learn from them. Seems like a bit of guessing at the moment.
  • JonnyMac wrote: »
    Did you notice this?
    I was looking at a bit of code that he posted as an attachment. This is a case where it would be better to know the outcome than to deal with the present code. There are probably lots of ways to succeed with fewer cogs; it would be useful for the OP to see and learn from them. Seems like a bit of guessing at the moment.

    Yes, I combined all that into one timer cog etc since it was fairly easy to see that none of this code was doing anything much, but this code is the software version of the ratsnest prototype that really needs to be replaced with a whole new version rather than reworked.
  • JonnyMac wrote: »
    Did you notice this?
    I was looking at a bit of code that he posted as an attachment. This is a case where it would be better to know the outcome than to deal with the present code. There are probably lots of ways to succeed with fewer cogs; it would be useful for the OP to see and learn from them. Seems like a bit of guessing at the moment.

    I do want to learn! I am not a pro, just a hobbyist having fun. I am totally self taught via book or web. I do learn lots from looking at code posted by the pro's though much of it I do not understand. I read the forum often.

    What do you mean by " it would be better to know the outcome"? Are you asking what I am trying to do as an end result? I have attached a screenshot of the monitor which should explain a lot. As you can see there are many pieces of data to be dealt with and inserted yet and a more friendly eye pleasing screen too.

    The last 5 of my discussions were related to this project.

    Thanks for your input
    Aaron
  • Forgot the attachment.
    3045 x 2348 - 1M
  • JonnyMacJonnyMac Posts: 7,112
    edited 2020-11-04 - 15:07:29
    What do you mean by " it would be better to know the outcome"?
    Tell us what you want the program to do -- don't show us how you're doing it. You can then look at the examples you get back and explore the differences between what you and others do. I have always found this process valuable. I learned a lot about coding by studying programs written by a guy who is now a good friend.

    I talked about this in my presentation on the Propeller and programming in Spin. Something I repeated in that presentation is: The root word of specification is SPECIFIC.

    You can see that presentation here. You might pick up one or two useful tips.
    --
    The last 5 of my discussions were related to this project.
    It's usually better to keep a project constrained to a single thread, unless you're isolating something (e.g., a generic object) that can be used across multiple applications.
  • Determining cog usage of an object isn't entirely straightforward, as an object may start its cogs in sub-objects which are less visible. It is possible to write a Spin loop to try to coginit each of the 8 cogs and remember whether they succeed or fail, killing the ones that succeed; the ones that failed did so because they're already running. You can stick this test in your project at a point when it's all started up to see how many cogs are being used.
Sign In or Register to comment.