Shop OBEX P1 Docs P2 Docs Learn Events
Issue for launching of third cog — Parallax Forums

Issue for launching of third cog

JChrisJChris Posts: 58
edited 2016-07-01 16:58 in Propeller 1
Hi,
I use a propeller C3, and I would like to launch 3 programs in 3 cogs (1 program for one cog)
When I launch 2 cogs, there is no problem.
Why there is a problem when I launch the third?
CON
  _clkmode = xtal1 + pll16x
  _xinfreq = 5_000_000


  GPS_PIN_TX       = 0
  GPS_PIN_RX       = 1 
  GPS_PIN_RST      = 2  
  
  GSM_Rx = 3
  GSM_Tx = 4
  GSM_power = 5
  GSM_Baud = 9600

  
  Pol_Rx = 6                 
  Pol_Baud = 9600              

  CLS = 16
  CR = 13
  QUOTE = 34  

OBJ

  pst : "Parallax Serial Terminal"
  GSM : "FullDuplexSerial"
  GPS  : "gps_basic"
  Pol : "FullDuplexSerial" 

Pub main | lat, lon, ok

  pst.start(115200)

  Pol.start(Pol_Rx, -1, 0, Pol_Baud)  

  GSM.start(GSM_Rx, GSM_Tx, 0, GSM_Baud)

  GPS.startx(GPS_PIN_RX, 2, 9600, 2500)  

  cognew(GPS_Start,@stack)

  cognew(Pol_Start,@stack2)

  cognew(GSM_Start, @stack1)

If anyone has a hint I would appreciate

Comments

  • ElectrodudeElectrodude Posts: 1,621
    edited 2016-07-01 17:20
    How big are your stacks? Have you tried making them bigger? I don't see their definitions in your program above. What do GPS_Start, Pol_Start, and GSM_Start look like?


    Are you running out of cogs? PST and each of your two FDSs take up a cog each. If gps_basic also takes up a cog, and there's another cog being used somewhere I don't know about because you didn't post all of your code, then you're be out of cogs.

    You should look into Tracy Allen's FullDuplexSerial4port.spin or one of its derivatives. It can do up to four serial ports in a single cog, instead of hogging up a whole cog for each one. That will save you two or three cogs, depending on whether or not you can get gps_basic to also use FullDuplexSerial4port.spin.
  • The code shown uses at least seven cogs.

    Each of the serial drivers runs in its own cog.

    In general it's a good idea to start any objects being used by a particular cog from the cog which will be calling the object. I don't think the objects you're using will be a problem but other objects need to be called from the same cog as the one which started the object so it's a good idea to make it a general rule to always start an object from the cog which will access the object.

    I don't see an obvious problem with code posted. The problem is likely elsewhere or as Electrodude suggests, there's not enough stack space for a given cog.

    It would be a good idea to post your code as an archive which includes all the objects you're using.
  • Cluso99Cluso99 Posts: 18,069
    edited 2016-07-02 05:09
    JChris wrote: »
    Hi,
    I use a propeller C3, and I would like to launch 3 programs in 3 cogs (1 program for one cog)
    When I launch 2 cogs, there is no problem.
    Why there is a problem when I launch the third?
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    
      GPS_PIN_TX       = 0
      GPS_PIN_RX       = 1 
      GPS_PIN_RST      = 2  
      
      GSM_Rx = 3
      GSM_Tx = 4
      GSM_power = 5
      GSM_Baud = 9600
    
      
      Pol_Rx = 6                 
      Pol_Baud = 9600              
    
      CLS = 16
      CR = 13
      QUOTE = 34  
    
    OBJ
    
      pst : "Parallax Serial Terminal"
      GSM : "FullDuplexSerial"
      GPS  : "gps_basic"
      Pol : "FullDuplexSerial" 
    
    Pub main | lat, lon, ok
    
      pst.start(115200)
    
      Pol.start(Pol_Rx, -1, 0, Pol_Baud)  
    
      GSM.start(GSM_Rx, GSM_Tx, 0, GSM_Baud)
    
      GPS.startx(GPS_PIN_RX, 2, 9600, 2500)  
    
      cognew(GPS_Start,@stack)
    
      cognew(Pol_Start,@stack2)
    
      cognew(GSM_Start, @stack1)
    

    If anyone has a hint I would appreciate

    I presume that the xxx.start routines each start a cog. I know that FullDuplexSerial at least does this, so there is no need for the cognew.
    Is this really what you are intending to do???
  • JChrisJChris Posts: 58
    edited 2016-07-05 14:18
    Pol_Start use a sensor, GSM_start use the SIM900 for AT commands and GPS_Start using GPS sold by parallax.

    I have modified my code for cogs :
    VAR
    
      byte AT_Reponse[150], dataSet[150]
      byte char, ptr, ptr1, ptr2
      Long stack3[1000], stack4[1000], stack6[1000], cog2, cog3, cog4, cog6
      Long UTC_Time, Local_Time, Heure_Local, Minutes_Local, Seconde_Local, jour, mois, annee, Altitude, Degre_latitude, Minute_latitude, Seconde_latitude
      Long Degre_longitude, Minute_longitude, Seconde_longitude, Vitesse, Nbres_satellites
    
    OBJ
    
      pst : "Parallax Serial Terminal"
      GSM : "FullDuplexSerial"
      GPS  : "gps_basic"
      Pol : "FullDuplexSerial" 
    
    Pub main | lat, lon, ok
    
      cog2 := pst.start(115200)
      pst.str(String(pst#NL,"Le cog2 est: ",9))
      pst.dec(Cog2)
    
      cog3 := Pol.start(Pol_Rx, -1, 0, Pol_Baud)
      pst.str(String(pst#NL,"Le cog3 est: ",9))
      pst.dec(Cog3)
    
      cog4 := GPS.startx(GPS_PIN_RX, 2, 9600, 2500)
      pst.str(String(pst#NL,"Le cog4 est: ",9))
      pst.dec(Cog4)
    
      cog6 := GSM.start(GSM_Rx, GSM_Tx, 0, GSM_Baud)
      pst.str(String(pst#NL,"Le cog6 est: ",9)) 
      pst.dec(Cog6)
    
      
      COGINIT(cog3,Pol_Start,@stack3)
      COGINIT(cog4,GPS_Start,@stack4)
      COGINIT(cog6,GSM_Start,@stack6)
    

    In my code, I use one cog for the main (first cog), one cog for pst (second cog), one cog for pol (third cog), two cogs for GPS (fourth and fifth cogs) and one cog for GSM (sixth cog). So I have 6 cogs which are used, Am I right?
    About COGINIT, I launch my spin methods in the associated cog.
    But I have an issue with the GPS because it has two cogs, so when I run my program, I get NO GPS all the time.

    My spin method for GPS_Start is the following :
    PUB GPS_Start | ok   
    
    repeat
      
      if (gps.hasgps == false)                                
          pst.str(string("No GPS"))
      else
        pst.str(String(CR,"OK"))
        pst.dec(gps.s_gpsfix)                                    ' gps quality (fix)
        ok := gps.n_gpsfix                                        ' flag for other fields
        pst.str(String(CR,"Nbre satellite est: "))
        Nbres_satellites := GPS.n_satellites
        pst.dec(Nbres_satellites)
        pst.str(String(CR,"UTC Time: ")) 
        UTC_Time := GPS.fs_utc_time
        pst.str(UTC_Time)
        pst.str(String(CR,"Local Time: "))
        Local_Time := GPS.fs_local_time
        pst.str(Local_Time)
        pst.str(String(CR,"Local Time: ")) 
        Heure_Local := GPS.s_local_hrs
        Heure_Local := StrToDec(Heure_Local)
        pst.dec(Heure_Local)
        Minutes_Local := GPS.s_mins
        Minutes_Local := StrToDec(Minutes_Local)
        pst.dec(Minutes_Local)
        Seconde_Local := GPS.s_secs
        Seconde_Local := StrToDec(Seconde_Local)
        pst.dec(Seconde_Local)
        pst.str(String(CR,"Date: ")) 
        pst.str(GPS.fs_date)
        pst.str(String(CR,"Date: ")) 
        jour := GPS.s_day
        jour := StrToDec(jour)
        pst.dec(jour)
        mois := GPS.s_month
        mois := StrToDec(mois)
        pst.dec(mois)
        annee := GPS.s_year
        annee := StrToDec(annee)
        pst.dec(annee)
        pst.str(String(CR,"La latitude est: "))
        Degre_latitude := GPS.n_latd
        pst.dec(Degre_latitude)
        pst.str(String("°"))
        Minute_latitude := GPS.n_latm
        pst.dec(Minute_latitude)
        pst.str(String("'"))
        Seconde_latitude := GPS.n_lats
        pst.dec(Seconde_latitude)
        pst.str(String(QUOTE)) 
        pst.str(String(CR,"La longitude est: "))
        Degre_longitude := GPS.n_lond
        pst.dec(Degre_longitude)
        pst.str(String("°"))
        Minute_longitude := GPS.n_lonm
        pst.dec(Minute_longitude)
        pst.str(String("'"))
        Seconde_longitude := GPS.n_lons
        pst.dec(Seconde_longitude)
        pst.str(String(QUOTE))
        pst.str(String(CR,"L'altitude est: "))
        Altitude := GPS.n_altm
        pst.dec(Altitude)
        pst.str(String(CR,"La vitesse est de: ")) 
        Vitesse := GPS.n_speedm*1.609344
        pst.dec(Vitesse)
        pst.str(String(" Km/h "))
        waitcnt(clkfreq + cnt)
        pst.Str(String(CLS))
    

    One more thing, When I try to retrieve the cog ID that GPS.startx return, I get (-1), I think it's because its return two number of cog so it doesn't know which cog it has to return? see the following picture


    Electrodude, I don't think the issue it's the stack space but I have maybe wrong.

    240 x 222 - 2K
  • After you've compiled a program you can archive by selecting File\Archive "File_Name"\Project...

    The Propeller Tool will save all the files used as a .zip file. If you upload this archive we can take a look at the program ourselves.
  • ElectrodudeElectrodude Posts: 1,621
    edited 2016-07-07 14:09
    That GPS object returns -1 (which is TRUE in Spin) when it starts both of its cogs sucessfully.

    Why are you using COGINIT? You are starting three objects, getting their cog IDs, and then restarting those cogs with your own cogs. You're effectively stopping those objects by reloading their cogs with your own code. Let the Propeller pick cog IDs for you by using COGNEW instead of COGINIT. There are very few cases - such as purposely restarting cogs, which is not what you want to do - where COGINIT is the right command to use.

    Once you get your cogs running, I'd definitely recommend making your stacks smaller. I've never seen any Spin program that needed 1000 longs of stack.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2016-07-07 23:50
    It appears you have a fundamental misunderstanding concerning the way cogs are used.

    The various objects you use start cogs on their own. Your "COGINIT" calls end up wiping out drivers you previously started.

    Many objects return the cog ID plus one.

    The GPS object uses two cogs. The start method adds one to each of the cog IDs and then returns the value of these two numbers combined with a logical "and".
    rxcog := cognew(@rxserial, @rxpin) + 1                        ' start rx uart cog
    
      if rxcog
        parsecog := cognew(parse_gps, @stack) + 1                   ' start parser cog
    
      return (rxcog) and (parsecog)
    

    I haven't checked, but I believe the object will return either negative one (true) or zero (false). A return value of negative one indicates the object was successfully started.

    I'm not certain, but I believe this return value is one of the things which had confused you.

    I removed all the "COGINIT" statements. You really don't want to use "coginit" unless you know what you're doing. In general "cognew" is a safer way to start a cog since it will automatically select an unused cog.

    In general you only want a single cog calling a serial driver. Your original code had several cogs calling the same serial driver.

    I changed your debug serial driver to fullduplexSerial since you were using this object several times already. Using Parallax Serial Terminal.spin does pretty much the same thing as fullduplexSerial.

    One failing of fullduplexSerial is it doesn't catch framing errors. If a port isn't connected, fullduplexSerial will receive nul characters. I added several checks for these nul characters to the code. If any of your serial devices send raw data (I don't think they do), you'll want to change the way nul characters are treated.

    You used the same global variable "char" as the incoming character in several cogs. The value of "char" would have been very unpredictable. I deleted this global variable and added local variables. Though the way the works now, a global variable would have been okay.

    Rather than multiple loops in the main program, the main program now only has a single main loop. This will keep the debug statements from being corrupted.

    I've attached the code to this post and I'll also embed the code in the next reply.

    Edit: I deleted the code which had been attached to this reply. There were several bugs in the posted code. A more recent version of the program may be found in a later reply.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2016-07-07 23:52
    This was too long to include in my previous post.
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    
      GPS_PIN_TX       = 0
      GPS_PIN_RX       = 1 
      GPS_PIN_RST      = 2  
      
      GSM_Rx = 3
      GSM_Tx = 4
      GSM_power = 5
      GSM_Baud = 9600
    
      
      Pol_Rx = 6                    
      Pol_Baud = 9600              
    
      CLS = 16
      CR = 13
      QUOTE = 34
    
      DP1 = $42
      DP2 = $4d
      LP1 = $00
      LP2 = $14
      
    CON
    
      GSM_TIMEOUT = 1000
      POSO_TIMEOUT = 100  
    
    CON
    ''
    ''     Parallax Serial Terminal
    ''    Control Character Constants
    ''─────────────────────────────────────
      CS = 16  ''CS: Clear Screen      
      CE = 11  ''CE: Clear to End of line     
      CB = 12  ''CB: Clear lines Below 
    
      HM =  1  ''HM: HoMe cursor       
      PC =  2  ''PC: Position Cursor in x,y          
      PX = 14  ''PX: Position cursor in X         
      PY = 15  ''PY: Position cursor in Y         
    
      NL = 13  ''NL: New Line        
      LF = 10  ''LF: Line Feed       
      ML =  3  ''ML: Move cursor Left          
      MR =  4  ''MR: Move cursor Right         
      MU =  5  ''MU: Move cursor Up          
      MD =  6  ''MD: Move cursor Down
      TB =  9  ''TB: TaB          
      BS =  8  ''BS: BackSpace          
               
      BP =  7  ''BP: BeeP speaker          
    
    VAR
    
      byte AT_Reponse[150], dataSet[150]
      byte ptr, ptr1, ptr2
      Long cog2, cog3, cog4, cog6
      Long UTC_Time, Local_Time, Heure_Local, Minutes_Local, Seconde_Local, jour, mois, annee, Altitude, Degre_latitude, Minute_latitude, Seconde_latitude
      Long Degre_longitude, Minute_longitude, Seconde_longitude, Vitesse, Nbres_satellites
      Long a, b,a0,c, d,c0,NU1,NU2,NU3, Checksum   
      
    OBJ
    
      pst : "FullDuplexSerial" ' uses one cog
      GSM : "FullDuplexSerial" ' uses one cog
      GPS  : "gps_basic"       ' uses two cogs
      Pol : "FullDuplexSerial" ' uses one cog
    
    Pub main | lat, lon, ok
    
      cog2 := pst.start(31, 30, 0, 115200)
    
      repeat
        pst.str(String(13, "Press any key to begin."))
        waitcnt(clkfreq / 2 + cnt)
        result := Pst.RxCheck
      while result == -1
        
      pst.str(String(CS, HM, NL,"Le cog2 est: ",9))
      pst.dec(Cog2)
    
      cog3 := Pol.start(Pol_Rx, -1, 0, Pol_Baud)   
      pst.str(String(NL,"Le cog3 est: ",9))
      pst.dec(Cog3)
    
      cog4 := GPS.startx(GPS_PIN_RX, 2, 9600, 2500) ' this returns the value "(rxcog) and (parsecog)" from the GPS object
      pst.str(String(NL,"Le cog4 est: ",9))
      pst.dec(Cog4)
    
      pst.str(String(13, "If cog4 equals -1 then the GPS object launched successfully."))
      pst.str(String(13, "If cog4 equals 0 then the GPS object did not launch correctly."))
      
      cog6 := GSM.start(GSM_Rx, GSM_Tx, 0, GSM_Baud)
      pst.str(String(NL,"Le cog6 est: ",9)) 
      pst.dec(Cog6)
    
      pst.str(String(13, 13, "The GSM will now be initialized."))
      pst.str(String(13, "This will take a while (likely over 10 seconds).", 13))
    
      GSM_Start
    
      pst.str(String(13, "The GSM has been initialized."))
    
      pst.str(String(13, "This program uses cogs #0 through cog #"))
      pst.dec(Cog6 - 1)
      pst.tx(".")
      
      pst.str(String(13, "This program uses "))
      pst.dec(Cog6)
      pst.str(String(" of the 8 cogs."))
    
      pst.str(String(13, 13, "Press any key to begin main program loop.", 7))
    
      repeat
        result := Pst.RxCheck
      while result == -1
          
      MainLoop
    
    PUB MainLoop
    
      repeat
        Poso_Check(POSO_TIMEOUT)
        GPS_Check
        
    PUB GSM_Start
    
      Start_Module
      waitcnt(clkfreq*5+cnt)
    
      ptr := 0
    
      gsm.str(string("AT",13))  
      Rx_GSM(GSM_TIMEOUT)
    
      pst.Str(String(13))  
      pst.str(String("La réponse à ta requete est la suivante 0: ",9))
      pst.Str(@AT_Reponse)
      pst.Str(String(13)) 
      
      gsm.str(string("AT+CPIN=",34,"00000000",34,13))  
      Rx_GSM(GSM_TIMEOUT)
    
      pst.Str(String(13))  
      pst.str(String("La réponse à ta requete est la suivante 0,4: ",9))
      pst.Str(@AT_Reponse)  
      pst.Str(String(13)) 
    
      gsm.str(string("AT+CGDCONT=1,",34,"IP",34,",",34,"orange",34,13))  
      Rx_GSM(GSM_TIMEOUT)
    
      pst.Str(String(13))  
      pst.str(String("La réponse à ta requete est la suivante 2: ",9))
      pst.Str(@AT_Reponse)
      pst.Str(String(13)) 
    
      waitcnt(clkfreq*30+cnt)
    
      gsm.str(string("AT+CGATT=1",13))  
      Rx_GSM(GSM_TIMEOUT)
    
      pst.Str(String(13))  
      pst.str(String("La réponse à ta requete est la suivante 1: ",9))
      pst.Str(@AT_Reponse)
      pst.Str(String(13))  
      
      gsm.str(String("AT+SAPBR=3,1,",34,"Contype",34,",",34,"GPRS",34,13))
      Rx_GSM(GSM_TIMEOUT)
    
      pst.Str(String(13))  
      pst.str(String("La réponse à ta requete est la suivante 3: ",9))
      pst.Str(@AT_Reponse)           
      pst.Str(String(13))   
       
      gsm.str(String("AT+SAPBR=3,1,",34,"APN",34,",",34,"orange.fr",34,13))
      Rx_GSM(GSM_TIMEOUT)
    
      pst.Str(String(13))  
      pst.str(String("La réponse à ta requete est la suivante 4: ",9))
      pst.Str(@AT_Reponse)           
      pst.Str(String(13))   
      
      gsm.str(String("AT+SAPBR=3,1,",34,"USER",34,",",34,"orange",34,13))
      Rx_GSM(GSM_TIMEOUT)
                                   
      pst.Str(String(13))  
      pst.str(String("La réponse à ta requete est la suivante 5: ",9))
      pst.Str(@AT_Reponse)           
      pst.Str(String(13)) 
      
      gsm.str(String("AT+SAPBR=3,1,",34,"PWD",34,",",34,"orange",34,13))
      Rx_GSM(GSM_TIMEOUT)
    
      pst.Str(String(13))  
      pst.str(String("La réponse à ta requete est la suivante 6: ",9))
      pst.Str(@AT_Reponse)           
      pst.Str(String(13))     
    
      gsm.str(String("AT+SAPBR=1,1",13))            
      Rx_GSM(GSM_TIMEOUT)
    
      pst.Str(String(13))  
      pst.str(String("La réponse à ta requete est la suivante 7: ",9))
      pst.Str(@AT_Reponse)           
      pst.Str(String(13))   
    
    PUB Start_Module
    
      dira[GSM_power]~~
      outa[GSM_power]~
      waitcnt(clkfreq/5+cnt)
      outa[GSM_power]~~
     
    Pub Rx_GSM(timeout) | localChar 
    
        GSM.RxFlush
        repeat
          localChar := GSM.RxTime(timeout)
          if localChar <> -1
            SafeTx(localChar)
          else
            pst.str(string(13, "ERROR: Rx_GSM timeout"))
            quit
        while (localChar <> 10) and localChar <> 0
        RxPacketNow_GSM(timeout)
    
    PUB SafeTx(localChar)
    
      case localChar
        " ".."~", 191..255:
          pst.tx(localChar)
        other:
          pst.tx("<")
          pst.tx("$")
          pst.hex(localChar, 2)
          pst.tx(">")
          
    Pri RxPacketNow_GSM(timeout) | valeur, localChar
    
        ptr := 0
        ptr1 := 0
        
        Repeat   
          localChar := GSM.RxTime(timeout)
          if localChar <> -1
            SafeTx(localChar) 
            AT_Reponse[ptr++] := localChar
        while localChar <> 13 and localChar <> -1 and localChar <> 0
    
        if localChar <> -1 
          AT_Reponse[--ptr] := 0
        else
          pst.str(string(13, "ERROR: RxPacketNow_GSM timeout"))
    
    PUB GPS_Check | ok
      
      if (gps.hasgps == false)                                
          pst.str(string("No GPS"))
      else
        pst.str(String(CR,"OK"))
        pst.dec(gps.s_gpsfix)                                    ' gps quality (fix)
        ok := gps.n_gpsfix                                        ' flag for other fields
        pst.str(String(CR,"Nbre satellite est: "))
        Nbres_satellites := GPS.n_satellites
        pst.dec(Nbres_satellites)
        pst.str(String(CR,"UTC Time: ")) 
        UTC_Time := GPS.fs_utc_time
        pst.str(UTC_Time)
        pst.str(String(CR,"Local Time: "))
        Local_Time := GPS.fs_local_time
        pst.str(Local_Time)
        pst.str(String(CR,"Local Time: ")) 
        Heure_Local := GPS.s_local_hrs
        Heure_Local := StrToDec(Heure_Local)
        pst.dec(Heure_Local)
        Minutes_Local := GPS.s_mins
        Minutes_Local := StrToDec(Minutes_Local)
        pst.dec(Minutes_Local)
        Seconde_Local := GPS.s_secs
        Seconde_Local := StrToDec(Seconde_Local)
        pst.dec(Seconde_Local)
        pst.str(String(CR,"Date: ")) 
        pst.str(GPS.fs_date)
        pst.str(String(CR,"Date: ")) 
        jour := GPS.s_day
        jour := StrToDec(jour)
        pst.dec(jour)
        mois := GPS.s_month
        mois := StrToDec(mois)
        pst.dec(mois)
        annee := GPS.s_year
        annee := StrToDec(annee)
        pst.dec(annee)
        pst.str(String(CR,"La latitude est: "))
        Degre_latitude := GPS.n_latd
        pst.dec(Degre_latitude)
        pst.str(String("°"))
        Minute_latitude := GPS.n_latm
        pst.dec(Minute_latitude)
        pst.str(String("'"))
        Seconde_latitude := GPS.n_lats
        pst.dec(Seconde_latitude)
        pst.str(String(QUOTE)) 
        pst.str(String(CR,"La longitude est: "))
        Degre_longitude := GPS.n_lond
        pst.dec(Degre_longitude)
        pst.str(String("°"))
        Minute_longitude := GPS.n_lonm
        pst.dec(Minute_longitude)
        pst.str(String("'"))
        Seconde_longitude := GPS.n_lons
        pst.dec(Seconde_longitude)
        pst.str(String(QUOTE))
        pst.str(String(CR,"L'altitude est: "))
        Altitude := GPS.n_altm
        pst.dec(Altitude)
        pst.str(String(CR,"La vitesse est de: ")) 
        Vitesse := GPS.n_speedm*1.609344
        pst.dec(Vitesse)
        pst.str(String(" Km/h "))
        waitcnt(clkfreq + cnt)
        pst.Str(String(CLS))
    
    PUB StrToDec(stringptr) : value | index, multiply, localChar
    
        '' Converts a zero terminated string representation of a decimal number to a value
    
        value := index := 0
        repeat until ((localChar := byte[stringptr][index++]) == 0)
           if localChar => "0" and localChar =< "9"
              value := value * 10 + (localChar - "0")
        if byte[stringptr] == "-"
           value := - value
    
    PUB Poso_Check(timeout)
    
      result := Poso_Rx(timeout)
    
      pst.str(string(NL,"a = "))
      pst.Dec(a)
      pst.str(string(NL,"b = "))
      pst.Dec(b)
      pst.str(string(NL,"a0 = ")) 
      pst.Dec(a0)
      pst.str(string(NL,"c = "))
      pst.Dec(c)
      pst.str(string(NL,"d = "))
      pst.Dec(d)
      pst.str(string(NL,"c0 = "))
      pst.Dec(c0)
      pst.str(string(NL))
      pst.dec(NU1)
      pst.str(string(NL))
      pst.dec(NU2)
      pst.str(string(NL))
      pst.dec(NU3)
      pst.str(string(NL,"Check Sum:"))
      pst.hex(checksum,4)
    
    Pub Poso_Rx(timeout)| DP_char1, DP_char2, LP_char1, LP_char2
    {{
      Wait for incoming packet until packet identifer found ($7E)
      Then process packet.
      XB.API_RX
      Once data is received, the type of packet can be checked for processing:
      IF XB.RxData == $ 83 ' message string
          ...
      See RxPacket Now for more information
    }}
    
    '    pst.str(string(NL, "Debut reception:", NL))
    
       ' pst.Hex(_SrcAddr16,2) 
        repeat
          DP_char1 := Pol.RxTime(timeout)
          SafeTx(DP_char1)
          if DP_char1 == -1 or DP_char1 == 0
            Roso_Error(DP_char1)
            quit
    '      pst.hex(DP_char1,2)
    '      pst.Str(String(NL,"ok DP_char1", NL)) 
          if ( DP_char1 == DP1)
            DP_char2 := Pol.RxTime(timeout)
            SafeTx(DP_char2)
            if DP_char2 == -1 or DP_char2 == 0
              Roso_Error(DP_char2)
              quit
    '        pst.hex(DP_char2,2)
    '        pst.Str(String(NL,"good DP_char2", NL)) 
            if (DP_char2 == DP2)
              LP_char1 := Pol.RxTime(timeout)
              SafeTx(LP_char1)
              if LP_char1 == -1
                Roso_Error(LP_char1)
                quit
     '         pst.hex(LP_char1,2)
    '          pst.Str(String(NL,"parfait LP_char1", NL))
              if (LP_char1 == LP1)
                 LP_char2 := Pol.RxTime(timeout)
                 SafeTx(LP_char2)
                 if LP_char2 == -1 
                   Roso_Error(LP_char2)
                   quit
    '             pst.hex(LP_char2,2)
    '             pst.Str(String(NL,"genial LP_char2", NL))    
        while (LP_char2 <> LP2)  
    '        pst.Str(String(NL))
    '        pst.str(char)
    '    pst.str(string(NL, "Debut Analyze paquet reçu:", NL))
        RxPacketNow(timeout)                ' Analyze received packet
    
    PRI Roso_Error(localChar)
     
      pst.str(string(NL,"ERROR: Poso_Rx "))
      
      case localChar
        0:
          pst.str(string("received null character (or framing error)"))
        -1:
          pst.str(string("timeout"))
        other:
          pst.str(string("unknown error"))
      
    Pri RxPacketNow(timeout) | chan, prt2, localChar
    {{
      Process incoming frame based on Identifier
      See individual cases for data returned.
      Check ident with :
      IF XB.rxIdent == value
        and process data accordingly as shown below
        
    }}
    '    pst.str(string("Recup caractere transmis : "))
        ptr := 0
        Repeat 20
    '      pst.str(string("Recup caractere transmis : "))
          
          localChar := Pol.RxTime(timeout)    ' accept remainder of data
    '      pst.hex(Char,2)
    '      pst.str(string (NL))
          dataSet[ptr++] := localChar 
    
        ptr := 0
    
        a := dataSet[ptr++] << 8 + dataSet[ptr++]
        b := dataSet[ptr++] << 8 + dataSet[ptr++]
        a0 := dataSet[ptr++] << 8 + dataSet[ptr++]
        c := dataSet[ptr++] << 8 + dataSet[ptr++]
        d := dataSet[ptr++] << 8 + dataSet[ptr++]
        c0 := dataSet[ptr++] << 8 + dataSet[ptr++]
        NU1 := dataSet[ptr++] << 8 + dataSet[ptr++]
        NU2 := dataSet[ptr++] << 8 + dataSet[ptr++]
        NU3 := dataSet[ptr++] << 8 + dataSet[ptr++]
        Checksum := dataSet[ptr++] << 8 + dataSet[ptr++]
    

    Edit: The code I originally embedded here had several bugs. I've replaced the code with the latest version. This latest version is also attached to a later reply.
  • JChrisJChris Posts: 58
    edited 2016-07-08 10:49
    I am using COGINIT for runnig my parts of program into the same cogs what my start functions.

    Yes I know about making my stacks smaller but it was for see if the issue came from that.

    I already tried to code with COGNEW but it doesn't work, the cog for GSM_start doesn't run.
  • The serial objects take care of starting the cog they use. All you have to worry about is making sure there are enough cogs. You don't need to use cognew or coginit to use the objects you're using.
    JChris wrote: »
    the cog for GSM_start don't run.

    The code in "GSM_start" doesn't need to be launched in a new cog. It appears to only run once. The main cog should call this method as part of the start up procedure. The example I posted shows one way of doing this.

  • I tried to add ways the code would timeout rather than just waiting for input from the various serial interfaces.

    I just noticed I didn't handle the timeout option correct in the "Rx_GSM" method.

    The program requires a terminal interface in order to operate correct. I believe the original code also required a terminal interface.

    The program starts by requesting the user to press any key. Once this initial key press has been received the program runs through its initialization process.

    Here's the output of this initialization process without either a GPS module or a GSM module connected to the Propeller.
    Le cog2 est:    2
    Le cog3 est:    3
    Le cog4 est:    -1
    If cog4 equals -1 then the GPS object launched successfully.
    If cog4 equals 0 then the GPS object did not launch correctly.
    Le cog6 est:    6
    
    The GSM will now be initialized.
    This will take a while (likely over 10 seconds).
    <$00><$00>
    La réponse à ta requete est la suivante 0:
    <$00><$00>
    La réponse à ta requete est la suivante 0,4:
    <$00><$00>
    La réponse à ta requete est la suivante 2:
    <$00><$00>
    La réponse à ta requete est la suivante 1:
    <$00><$00>
    La réponse à ta requete est la suivante 3:
    <$00><$00>
    La réponse à ta requete est la suivante 4:
    <$00><$00>
    La réponse à ta requete est la suivante 5:
    <$00><$00>
    La réponse à ta requete est la suivante 6:
    <$00><$00>
    La réponse à ta requete est la suivante 7:
    
    The GSM has been initialized.
    This program uses cogs #0 through cog #5.
    This program uses 6 of the 8 cogs.
    
    Press any key to begin main program loop.
    

    The output will likely be different with GPS and GSM units attached to the Propeller.

    As you can see from the above output, the program currently uses 6 of the Propeller's 8 cogs.

    The code I previously posted had an error in the "Poso_Rx" method. I treated all input data containing a zero as an error. I just noticed one of the expected input values "LP1" is zero.

    I've attached the top object (the only one I changed) to this post.

    Once the program has completed the initialization process it calls the method "MainLoop" which runs continuously in cog #0.

    Here's the output from the program without any modules connected.
    <$00>
    ERROR: Poso_Rx received null character (or framing error)
    a = 0
    b = 0
    a0 = 0
    c = 0
    d = 0
    c0 = 0
    0
    0
    0
    Check Sum:0000
    OK7712
    Nbre satellite est: 0
    UTC Time:
    Local Time:
    Local Time: 000
    Date:
    Date: 000
    La latitude est: 0°0'0"
    La longitude est: 0°0'0"
    L'altitude est: 0
    La vitesse est de: 0 Km/h
    

    I modified the method "SafeTx" to display international characters.

    The method "SafeTx" will display non-ASCII data as hexadecimal values. The output "<$00>" seen above is an example of the output from this method.

  • Duane DegnDuane Degn Posts: 10,588
    edited 2016-07-08 01:04
    Based on the way "coginit" had been used in the code you posted, I think you likely don't understand the way cogs are used in the Propeller.

    As I mentioned in my previous reply, the modified program I posted uses six of the Propeller's eight cogs. I'll attempt to give more detail on how these cogs are used.

    When the Propeller is started only one cog is initially used. This first cog (cog #0) is loaded with the Spin interpreter. The Spin interpreter is an assembly program which loaded from ROM. The Spin interpreter may be loaded to more than one cog. Two of the six cogs used by the program I attached above are running Spin interpreters. The other four cogs in use are running code written in assembly (PASM). Technically the Spin interpreter is also running PASM code but since this code is reading and executing Spin code it can also be said to be running Spin code.

    So when the program begins, it starts out running Spin code in cog #0. The first line of Spin code is:
      cog2 := pst.start(31, 30, 0, 115200)
    

    Since the object "pst" had been defined as "FullDuplexSerial", the "start" method of "FullDuplexSerial" is called.
    OBJ
    
      pst : "FullDuplexSerial" ' uses one cog
    

    Inside the "start" method of "FullDuplexSerial" is the line of code:
    okay := cog := cognew(@entry, @rx_head) + 1
    

    The above code tells the Propeller to load the PASM code starting at "entry" into the next available cog. The address of "rx_head" is passed to the newly started cog so it knows where in hub RAM to read and write data.

    If you look down towards the bottom of "FullDuplexSerial" you can find the PASM code starting at "entry" which will be loaded into the new cog.
    entry                   mov     t1,par
    

    You don't need to worry about the code above, but keep in mind the above code and the code after the above line is loaded into this new cog.

    Since only cog #0 had been in use, the "cognew" statement will cause the PASM code to be loaded into cog #1. If a cog isn't available, the cognew statement will return -1. Many people would rather an object return zero if a cog isn't available so many objects add one to the value returned by the cognew statement. "FullDuplexSerial" follows this "add one" convention so the first call of FullDuplexSerial's start method will return 2 rather than 1. As you can see this return value in the output I posted earlier.
    Le cog2 est:    2
    

    At this point in the program two cogs are active. Cogs number zero and one are these active cogs.

    The second call of FullDuplexSerial's start method returns 3 indicating cog #2 has been started.
    Le cog3 est:    3
    

    At this point we have one cog (#0) running an instance of the Spin interpreter and two cogs (#1 and #2) running PASM code loaded by FullDuplexSerial's start method.

    The next cogs started are started by the GPS object.
      GPS  : "gps_basic"       ' uses two cogs
    

    The "startx" method of the object "gps_basic" contains the following code.
    rxcog := cognew(@rxserial, @rxpin) + 1                        ' start rx uart cog
    
      if rxcog
        parsecog := cognew(parse_gps, @stack) + 1                   ' start parser cog
    
      return (rxcog) and (parsecog)
    

    The first "cognew" statement loads the PASM code beginning at "rxserial" and also passes the address of "rxpin" to this newly started cog. This cog will be #3. Here again we see the "plus one" convention. Since one is added to the return value of the newcog statement, the value of "rxcog" will be 4. If for some reason no cogs were available, the cognew statement would return -1 which would result in "rxcog" being set to zero. In Spin zero is also "false". If "rxcog" had been set to zero the second cognew statement wouldn't be executed. Since "rxcog" is a value other than zero the "if rxcog" condition is met and the second cognew statement is executed.

    This second cognew statement uses a name of a method as the first parameter which indicates another instance of the Spin interpreter should be launched in the next available cog. This next cog will be #4 and "parsecog" will be set to 5.

    The Spin interpreter requires stack space. The second parameter in this last cognew statement contains the address of the hub RAM which should be used as stack space.

    The line "return (rxcog) and (parsecog)" returns the value of "(rxcog) and (parsecog)". If you substitute the values of these variables we get "4 and 5". A number must be zero to evaluate as false and neither 4 nor 5 are zero so the expression "(rxcog) and (parsecog)" evaluates as "true". "true" in Spin is negative one. A 32-bit number with all the bits set to one is -1 using the encoding system used by the Propeller (two's compliment). So the "startx" method of the object "gps_basic" returns -1 when the cogs are successfully launched.

    After the call of GPS.startx we are now up to five cogs in use (#0 through #4).

    The "GSM.start" call adds one more cog (#5).

    In your original code you had multiple "coginit" statements which would have overwritten several of the previously started cogs with Spin interpreters. You do not what to do this.

    At this point in the program we finish initializing devices and then start the main program loop. While the main program loop runs in this top object in cog #0 we have four PASM loops running in cogs #1, 2, 3 and 5. The Spin interpreter running in cog #0 runs executes the code in this main loop. Cog #4 is also running a Spin interpreter which is executing code in the "GPS" object.

    We have all the cogs we need for now and there's no need to use either cognew or coginit.

    Keeping all this stuff, about cogs and objects, straight takes a while but once you understand how these things work programming the Propeller becomes a lot of fun.


  • To clarify what I am trying to achieve :

    I have 3 peripherals that I want to manage in parallel for a real-time application. These peripherals are:
    - a GPS module

    - a GSM module

    - a sensor module


    All items are working using UART communication.

    This is why I am launching 3 cogs to start communication with the 3 peripherals, using FullDuplexSerial libraries.

    The problem is that once I have initiated the Start procedure (an instance of FDSerial) in a COG, say for starting the GPS, I don’t know how to use the same COG to carry on with the rest of my program.
    Indeed, the Start procedure in the FullDuplexSerial library launches a COG using the newcog instruction and returns the COG number in my main program.
    I’d like to use this same COG number to run the rest of the program in charge of managing the GPS device.

    The above occurs 3 times since I am launching in sequence 3 Starts in FDSerial in order to manage 3 peripherals in parallel.
  • kwinnkwinn Posts: 8,697
    I think I understand what you want to do, and it does not work that way. When you launch a cog to run FDSerial it downloads a PASM program to the cog for the serial interface. The Spin code in the FDS object is executed by the cog that launches the PASM portion of FDS.

    If you want to run spin code to deal with each serial object you would have to launch a spin object to do that, and it would then launch the FDS object. That would take 6 cogs and is probably not necessary unless there is a lot of processing to be done for each input device. The serial objects do buffer the inputs, so in most cases each input can be processed in sequence without loosing any data.

  • AribaAriba Posts: 2,682
    If you want to run a serial driver and the code to handle the data in the same cog, you can use the Simple_Serial object. It works only up to 19200 Baud but I think you need 9600.

    But be aware this is half duplex and receiving and transmitting blocks until the data is shifted in/out. If you really want full duplex and data processing in its own cog then you need 2 cogs per device. Or you use the 4 port serial driver and 3 additional cogs for the data processing.

    Andy
  • Duane DegnDuane Degn Posts: 10,588
    edited 2016-07-11 00:19
    JChris wrote: »
    The problem is that once I have initiated the Start procedure (an instance of FDSerial) in a COG, say for starting the GPS, I don’t know how to use the same COG to carry on with the rest of my program.
    Indeed, the Start procedure in the FullDuplexSerial library launches a COG using the newcog instruction and returns the COG number in my main program.
    I’d like to use this same COG number to run the rest of the program in charge of managing the GPS device.

    You can't use any of the cog which have been loaded with FullDuplexSerial for anything else. The cog is busy listening to the serial port and monitoring for commands to send data. The commands given to FullDuplexSerial must come from a different cog.

    The GPS object uses two cogs, one cog is used to run PASM code which monitors incoming serial messages and the other cog parses the received messages. The PASM portion of the GPS object is similar to FullDuplexSerial but only receives data and doesn't send data.

    The code I attached to an early post shows how to use multiple instances of FullDuplexSerial. Cog #0 sends data to the various devices using the "str" , "tx", "dec" and "hex" methods of FullDuplexSerial. This same cog also monitors incoming data using "rxTime". I changed all your calls to the method "rx" to "rxTime" so the program wouldn't stop when there wasn't any data available.

    The object name is used to identify which instance of FullDuplexSerial should be used. For example when "pst" is used, the "str" and "dec" methods called know to place the data in the tx buffer associated with the FullDuplexSerial instance used for terminal communication.
      pst.str(string(NL,"a = "))
      pst.Dec(a)
    

    In the above code the same cog (cog #0) which calls the "str" and "Dec" methods executes these methods. So cog #0 runs the following Spin code:
    PUB Str(stringPtr)
    
      repeat strsize(stringPtr)
        Tx(byte[stringPtr++]) 
    

    Since the "Str" method calls the "Tx" method, cog #0 also runs the Spin code found in the "Tx" method.
    PUB Tx(txByte)
      repeat until (tx_tail <> (tx_head + 1) & $F)          'wait until the buffer has room                        
      tx_buffer[tx_head] := txByte                          'place the byte into the buffer
      tx_head := (tx_head + 1) & $F                         'advance the buffer's pointer
    
      if rxtx_mode & %1000                                  'if ignoring rx echo
        Rx                                                  '   receive the echoed byte and discard
    

    The "Tx" method places the data in the appropriate buffer which will then be read by the cog running the FullDuplexSerial PASM code. Since the "pst" instance of FullDuplexSerial had been loaded into cog #1, cog #1 will process the outgoing data. Cog #1 has to receive instructions from other cogs in order for it to transmit data. Cog #1 is continuously monitoring a variable in hub RAM to see if there is data to transmit or not. Cog #1 will change the value of a variable in hub RAM so other cogs (cog #0 in this example) will know if it's ready for more data or not. IIRC the "tx_tail" and "tx_head" are the variables used by FullDuplex to communicate with other cogs. You don't have to worry about these details, FullDuplexSerial takes care of this for you.

    The following line found in the "GSM_Start" method uses the instance of FullDuplexSerial associated with the "gsm" object name:
    gsm.str(string("AT+CPIN=",34,"00000000",34,13))
    

    Again it's cog #0 which calls the "str" method. The Spin portion of FullDuplexSerial is executed by cog #0. The "str" call places data in the buffer associated with the "gsm" instance of FullDuplexSerial. Cog #5 which had previously been launched running FullDuplexSerial PASM code will take the data from the tx buffer and transmit it on the appropriate I/O pin.

    I've listed which cog does what but in general you don't need to worry about knowing the cog ID numbers associated with the various objects.

    The program I attached earlier is an example of a commonly used strategy for using multiple serial drivers. A single cog (cog #0 in this case) is used to monitor multiple serial drivers.

    Since most of the program outputs data to the terminal, it's a good idea to keep the main loop activities in a single cog. If two cogs attempt to send data to the same serial driver at once, the data will be corrupted and garbled.

    You do have two remaining cogs. These could be used to monitor individual peripherals but I don't see a need to take this approach. The GPS object shows a good example of how to use an additional cog to monitor incoming data from a serial line. You could do something similar with your GSM module but if you try to send data to the terminal from more than one cog, you'll need to use a serial driver which uses locks.

    I think the code I posted earlier does a lot of what you want. The method "MainLoop" checks two of the peripherals. I didn't see any GSM interaction other than the initialization process but if you wanted to send data with your GSM peripheral, you could add a method to do so and call this method from "MainLoop".

    Edit: I hadn't seen kwinn's or Ariba's replies when I wrote this last reply.
  • Ok thank you all for your help, I understand better the functioning of propeller
Sign In or Register to comment.