Shop OBEX P1 Docs P2 Docs Learn Events
cogspin questions — Parallax Forums

cogspin questions

Hi,
I am confused by what is happening in cogspin. I want to start a new cog that includes JonnyMac's JM_multi_adc.spin2 that starts out as " not a top object" when I start the new cog, call it looper, were all I want to do is to read 8 pins of analog and report it out in a global VAR, the compiler goes to the cogspin routine and then to never, never land. I guess I need more explanation about how to use COGSPIN.
Jim

Comments

  • pik33pik33 Posts: 2,387
    edited 2021-11-18 15:26

    Cogspin calls a Spin method in its own cog. This needs a cog, a method and several bytes of stack.

    Example from my program:

    First, declare the stack in your main program VAR section:

    long sbusstack[64]

    Second, write a method to work in its own cog, which in most cases has to be an infinite loop. In this case it receives and decodes SBUS bus placing results in "channel" which is also global variable array.
    Notice it calls another methods, this will be also done in the method's cog

    pub sbus()
    
    repeat
      waitms(20)
      if (receive_sbus(SBUS_RX, @frame))                          ' get s.bus frame
        pintoggle(56)
        decode_sbus(@frame, @channel)                             ' convert to channel values
    
    

    Now you can call cogspin

    cogspin(6,sbus(),@sbusstack)

    and the cog #6 will be executing sbus() method while your main cog will go to the next instructions.

    Use 16 as a cog number if you want to get a first free available cog.

  • JonnyMacJonnyMac Posts: 9,159
    edited 2021-11-18 16:56

    Keep in mind that Spin cogs have access to global objects and variables in your main application. For example, I'm working on a project that starts two copies of my jm_fullduplexserial and then launches a Spin cog that monitors one of those serial objects for commands coming from a serial HMI.

    I allow the Propeller to manage cog allocation with this syntax (NEWCOG lets the P2 select an available cog).

      ndcog := cogspin(NEWCOG, nextion_display(), @ndstack) + 1     ' load Nextion state processor 
    

    Another habit is to declare the method that will be run by the background cog as PRIvate -- this just reminds me not to call that like a normal method because that method will usually have an infinite repeat loop.

    pri nextion_display() | len
    
      repeat
        len := get_msg(@rxbuf, ND_BUFSIZE)
        if (len > 4)
          get_command()
          get_value()
          check_newpage()
    
          if (newpage)
            term.fstr1(string("Page %d\r"), page)
            if (cmdvalue == 0)
              load_page()
            newpage := false
    
          else
            process_page()
            clear_command()
    
  • RS_JimRS_Jim Posts: 1,768
    edited 2022-01-22 09:56

    ok, it is still not happening for me.
    I want to run the following code in a separate cog:

    {
        --------------------------------------------
        Filename: TX_robot_1.3.spin2
        Author Jim Meek with nrf24l01+ routines developed by:
        Author: Jesse Burt modded by Jim Meek
        Description: nRF24L01+ Transmit  that uses the radio's
            auto-acknowledge function (Enhanced ShockBurst - (TM) Nordic Semi)
            tx-ad version to transmit results from adc smart pins 
        Copyright(c) 2021    
        Copyright (c) 2020
        Started Nov 23, 2019
        Updated Jan 25, 2020
        See end of file for terms of use.
        Note adc test removes most of the nrf code for the purposes of getting adc_multi object up and running
        when adc is running copyright notices will be properly noted and updated.
        --------------------------------------------
        this code has been moded for use with KISS0000_eval and KISS0001_eval
        which uses a 25MH xtal.  Clock freq has also been upped to 200MHz from
        origional 160_000_000
    }
    
    CON
    
        _clkfreq        = cfg#_clkfreq_def
        _xtlfreq        = cfg#_xtlfreq
    
    ' -- User-modifiable constants
        LED             = cfg.LED
    '    SER_BAUD        = 2_000_000
    
    ''Group_Base_pin ==G_BP this allows a device to be positioned on any 8 pin boundry using P0-P7 and adding the G_BP
        g_bp0 = 0
        g_bp1 = 8
        g_bp2 = 16
        g_bp3 = 24
        g_bp4 = 32
        g_bp5 = 40
        g_bp6 = 48
        'g_bp7 = 56
        nrf_gbp = g_bp1                             'group base pin for NRF24L01
    
    'NRF pins added to group base pin to determin io boundry    
        IRQ         = 0                             '
        MISO        = 1                             ' AKA MISO                                                                                                              'form
        MOSI        = 2                             ' AKA MOSI
        SCK         = 3                             '
        CS          = 4                             '
        CE          = 5                             '
        SCK_Pin  = SCK + nrf_gbp
        IRQ_PIN  = IRQ + nrf_gbp
        MISO_PIN = MISO + nrf_gbp
        MOSI_PIN = MOSI + nrf_gbp
        CS_PIN   = CS + nrf_gbp
        CE_PIN   = CE + nrf_gbp
        SCK_FREQ = 5_000_000                ' 10_000_000 max
        CHANNEL         = 113                         ' 0..125
    
        adc_gbp = g_bp4                             'group base pin for adc
        adc_pin_count = 8                           'pin count for ADC pins
        admax = $FFF
        admid = $7FF
        lo    = 0
        hi    = 3.3
    
        SER_RX          = cfg#SER_RX
        SER_TX          = cfg#SER_TX
        SER_BAUD        = 115_200
    
    ' --
    
        CR = $0A
        LF = $0D
        CLEAR           = 1
        CHANNEL         = 113
        PC              = 2
        MAX_CH = 32                                                   ' 32 is absolute maximum
    
    
    
    OBJ
        ser         : "com.serial.terminal.ansi"
        cfg         : "core.con.boardcfg.P2_KISS000_eval"
        io          : "io"
        time        : "time"
        int         : "string.integer"
        nrf24       : "wireless.transceiver.nrf24l01.spi"
    '    adc         : "jm_adc_multi"
    VAR
        long loopCount                                          ' to keep track of how many times the uarts have been restarted                         '
        long errors
        long _nrf24_cog,_ser_cog,adcCog,_adccog,loopercog
        long Serial
        long _pktlen
        long lstack[50]
        long adstack[50]
        long  ap0                                                     ' analog input channel 0 pin
        long  nch                                                     ' number of channels
        long  apg                                                     ' analog pins group
        long  urlo[32]                                                ' scaled user range, low
        long  urhi[32]                                                ' scaled user range, high
        long  callo[32]                                               ' calibration, ground
        long  calhi[32]                                               ' calibration, vio
    
        byte myBuf[34]
        byte _fifo[34]
        byte  cidx, didx, index
    '   byte IRQ_PIN, MISO_PIN, MOSI_PIN, SCK_PIN, CS_PIN, CE_PIN
    
    PUB main |choice, char,char1, flag, idx
    
      bytefill(@myBuf,0,33)
      Setup
      Serial(true)              'to go to standalone(no terminal) set serial to false
    
       pause(100)
       idx := 0
        _pktlen := 8
        repeat  
           ser.position(0,5)
                                                    'P2 fast enough to do adc reads inline rather than looping cog as in P1
          repeat idx from 0 to adc_Pin_Count
               ser.Str(String(CR,LF,"idx = "))
               ser.dec(idx)
               ser.Str(String("  mybuf  = "))
               ser.dec(myBuf[idx])
    
    PUB Setup | okay
       repeat until _ser_cog := ser.StartRXTX (SER_RX, SER_TX, 0, SER_BAUD)
       ser.str(string("SCK =" ))
       ser.dec(SCK_PIN)
       ser.str(string("   IRQ ="))
       ser.dec(IRQ_PIN) 
       ser.str(string("   MISO ="))
       ser.dec(MISO_PIN) 
       ser.str(string("   MOSI ="))
       ser.dec(MOSI_PIN) 
       ser.str(string("   CS ="))
       ser.dec(CS_PIN) 
       ser.str(string("   CE ="))
       ser.dec(CE_PIN) 
       ser.str(string("  adc base pin= "))
       ser.dec(adc_gbp)
       ser.str(string($0A,$0D))
    
       looperCog := CogSpin(NEWCOG, looper(adc_gbp,adc_pin_count,lo,hi),@adstack) + 1   
       ser.str(string(CR,LF,"looper sucessfully started, looper cog number  "))
       ser.dec(looperCog)   
    {
       else
               ser.str(string(CR,LF))
               ser.str(string("adc cog failed to start"))
               FlashLED1(LED,250)
    
    }       
    PUB looper(adc_gbp,adc_pin_count,lo,hi) |bSwap, bs1,bs2,bs3,bs4,idx,aidx,bidx
    
        start(adc_gbp,adc_pin_count,lo,hi)
    
        repeat
          aidx:=0
    
          repeat idx from 0 to 7
            aidx:=(adc_gbp + idx)    
            bswap:= read(aidx)         
            myBuf[idx]:=bswap
    
    pub start(base, count, lo, hi) : result | last
    
    '' Setup pin for analog input on count pins
    '' -- base  is first pin of group
    '' -- count is number of (contiguous) analog input pins
    '' -- lo and hi define user range for the ADC inputs
    ''    * scaled from ground to Vio (3.3v)
    ''    * lo corresponds to 0.0v input, hi corresponds to 3.3v input
    
      if ((base < 0) || (base > 63))                                ' validate base pin
        return false
    
      if ((count < 1) || (count > MAX_CH))                          ' validate channel count
        return false
    
      last := base + count - 1                                      ' last ch pin of group
    
      if ((base < 32) && (last > 31))
        return false                                                ' outa boundary error
      elseif (base < 64) && (last > 63)
        return false                                                ' outb boundary error
    
      ap0, nch := base, count                                       ' save 1st channel and count
    
      apg := ap0 addpins (nch-1)                                    ' create group
    
      longfill(@urlo, lo, count)                                    ' set initial range
      longfill(@urhi, hi, count)
    
      calibrate()                                                   ' calibrate analog input pin
    
      return true
    
    
    pub calibrate() | ch
    
    '' Calibrate analog input pin for 0 to 3.3v input
    '' -- 12 bits, using SINC2, 2048 samples
    
      pinstart(apg, P_ADC | P_ADC_GIO, 12-1, 0)                     ' measure ground
      waitct(getct() + (2048 << 2))                                 ' allow pin to stablize
      repeat ch from 0 to nch-1
        callo[ch] := rdpin(ap0+ch)                                  ' save ground calibration level
    
      pinstart(apg, P_ADC | P_ADC_VIO, 12-1, 0)                     ' measure vio (3.3v)
      waitct(getct() + (2048 << 2))
      repeat ch from 0 to nch-1
        calhi[ch] := rdpin(ap0+ch)                                  ' save 3.3v calibration level
    
      pinstart(apg, P_ADC | P_ADC_1X,  12-1, 0)                     ' set to pins, 1x scale
      waitct(getct() + (2048 << 2))
    
    
    pub set_range(ch, lo, hi)
    
    '' Reset user range for specific channel
    '' -- lo corresponds to 0.0v input
    '' -- hi corresponds to 3.3v input
    
      if ((ch >= 0) && (ch < nch))
        urlo[ch] := lo
        urhi[ch] := hi
    
    
    pub read(ch) : result | clo, chi, ulo, uhi
    
    '' Read channel pin and scale to user range
    '' -- simple mx+b
    
      if ((ch >= 0) && (ch < nch))
        clo, chi := callo[ch], calhi[ch]                            ' get channel calibration
        ulo, uhi := urlo[ch], urhi[ch]                              ' get channel user range
        result := (raw(ch)-clo) * (uhi-ulo) / (chi-clo)             ' mx
        result := ulo #> result + ulo <# uhi                        ' +b (constrain to user range)
    
    
    pub raw(ch) : result
    
    '' Read raw analog level from channel pin
    '' -- not scaled to user range
    
      return ((ch >= 0) && (ch < nch)) ? rdpin(ap0+ch) : 0
    
    
    PUB FlashLED1(led_pin, delay_ms)
    
        io.Output(led_pin)
        repeat
            io.Toggle (led_pin)
            time.MSleep (delay_ms)
    
    '     
    
    PUB HM
        ser.position(0,0)
    
    PRI pause(ms)
    
    
    
    DAT
                RFaddr  BYTE  $E7, $E7, $E7, $E7, $E7                           'address used for transmitter&receiver id
                hello   BYTE "Hello world, this is a tx test!"                  '31 + 1 zero byte string - only used by the Beacon method
    
    {
        --------------------------------------------------------------------------------------------------------
        TERMS OF USE: MIT License
    
        Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
        associated documentation files (the "Software"), to deal in the Software without restriction, including
        without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
        following conditions:
    
        The above copyright notice and this permission notice shall be included in all copies or substantial
        portions of the Software.
    
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
        LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
        IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
        WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
        SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
        --------------------------------------------------------------------------------------------------------
    }
    
    

    I cannot figure out where the wheels have come off the track. When I start the main program (adctest) I have an on screen display of several i.o pins and that works fine but once it calls cogspin, it goes to never never land..
    Jim
    EDIT: I woke up at 3AM this morning remembering how to post code in the forum so I have re inserted the code, maybe it will make more sense.
    EDIT2: I changed the begining of looper code to this:

    PUB looper(adc_gbp,adc_pin_count,lo,hi) 
    
    PUB begin()
        start(adc_gbp,adc_pin_count,lo,hi)
    
        repeat
          aidx:=0
    
          repeat idx from 0 to 7
            aidx:=(adc_gbp + idx)    
            bswap:= read(aidx)         
            myBuf[idx]:=bswap
    
    
  • pik33pik33 Posts: 2,387
    edited 2021-11-21 08:46

    I cannot see any cogspin in this code (and please edit the post so the code will be more readable)

    What you have to start in the cogspin is your main (don't forget about () - main() )

    cogspin(cog#,main(),stackspace)

    and you have to provide stackspace - 64 longs seems to be a safe valua for the start. Too low stack space causes the program to not work as expected.

  • AribaAriba Posts: 2,690

    The question is: Why do you want to start this in a new cog? The AD conversion is anyway made by the smartpins in background continuously. The only gain would be that the scaling calculation of the raw value is made by another cog, this saves a few microseconds when you read the value. But is it worth to waste a second cog for that.

  • I edited post 3 to correct the code posting and to change the beginning of the looper cog to include a
    PUB begin().
    that solved the going off to never never land, but I am still not reading the ADC pins and reporting them to myBuf. I am just reading all 0. So if I can get @Jonny Mac to look at the code I will appreciate him showing me the error of my ways. I am using a stack size of 50 longs, I think that will be enough but will change to 64 to see if that helps.
    Jim

  • @Ariba said:
    The question is: Why do you want to start this in a new cog? The AD conversion is anyway made by the smartpins in background continuously. The only gain would be that the scaling calculation of the raw value is made by another cog, this saves a few microseconds when you read the value. But is it worth to waste a second cog for that.

    No, I had started with an inline read and when it wasn't working,I thought perhaps it needed ti be run in it's own cog. I started it inline online.
    I am still not getting my analog resulta so I have yo go back anda review the adc code.
    Jim

  • pik33pik33 Posts: 2,387
    edited 2021-11-22 21:03

    Here is my simple object doing A/D conversion in a dedicated cog. I wrote this for measuring a battery voltage in a robot. Maybe in the future I will get rid of it and free this cog, but now it works as it is. The battery voltage is divided by 10 via a resistor divider and then it goes to the P2 pin, so this 33000 multiplier gives a milivolt result.

    {{ ADC example}}
    
    
    var
    long aresult
    long adcpin
    long agnd
    long avcc
    
    
    
    pub start(apin)     |iii, m, n
    
    adcpin:=apin
    coginit(16,@mixer,@aresult)
    waitms(100)
    
    pub measure() :voltage  
    
    
    return 33000*(aresult-agnd)/(avcc-agnd)
    
    
    
    DAT
                  org
    mixer         setq #3
                  rdlong aaresult,ptra
    
    
                  wrpin adc_config1, aapin
                  wxpin  #%00_1101, aapin
                  dirh   aapin
                  mov    x, #%001<<6
                  add    x,aapin
                  setse1 x
    
    loop          waitse1
                  rdpin x,aapin
                  waitse1
                  rdpin x,aapin   
                  waitse1
                  rdpin gnd,aapin 
    
                  wrpin adc_config2, aapin
    
                  waitse1
                  rdpin x,aapin
                  waitse1
                  rdpin x,aapin   
                  waitse1
                  rdpin vcc,aapin  
    
                  wrpin adc_config3, aapin
    
                  waitse1
                  rdpin x,aapin
                  waitse1
                  rdpin x,aapin   
                  waitse1
                  rdpin aaresult,aapin  
    
                  wrpin adc_config1, aapin 
                  setq #3
                  wrlong aaresult,ptra
    
                  jmp     #loop          'loop
    
    
    adc_config1      long    %0000_0000_000_100000_0000000_00_11000_0 ' 100000 gnd
    adc_config2      long    %0000_0000_000_100001_0000000_00_11000_0 ' 100001 3v3
    adc_config3      long    %0000_0000_000_100011_0000000_00_11000_0 ' 100011 pin
    
    x                long    0
    
    aaresult long 0
    aapin long 0
    gnd long 0
    vcc long 0
    
  • AribaAriba Posts: 2,690

    @RS_Jim said:

    @Ariba said:
    The question is: Why do you want to start this in a new cog? The AD conversion is anyway made by the smartpins in background continuously. The only gain would be that the scaling calculation of the raw value is made by another cog, this saves a few microseconds when you read the value. But is it worth to waste a second cog for that.

    No, I had started with an inline read and when it wasn't working,I thought perhaps it needed ti be run in it's own cog. I started it inline online.
    I am still not getting my analog resulta so I have yo go back anda review the adc code.
    Jim

    I think this is the problem:

    lo    = 0
    hi    = 3.3
    

    The read() scaling expects an integer value for hi. For float values you would need to modify the scaling code to use the new float operators.

    BTW. For a code block you need 3 backticks before and after the code, not these apostrophes.

  • Hi,
    Well, programing can certainlymake you feel dumb sometimes!. I could not figure out why I could not get Jon's adc code to work. My problem was I was trying to feed the channel number with the pin number! Once setup is run, you feed the read instruction with the channel you want read and the program selects the correct pin based upon the setup information! What I was doing wrong finally hit me like a falling wall of bricks. Thanks to Ariba's comment I went back to my origonal plan to use inline c ode to read the adc..working fine now that i try to read the channel number not the pin number.
    Jim

Sign In or Register to comment.