Shop OBEX P1 Docs P2 Docs Learn Events
Deploying assembly code across multiple cogs - jm_freqin help — Parallax Forums

Deploying assembly code across multiple cogs - jm_freqin help

Hello Everyone,
This is my first post and it has to do with making the really lovely jm_freqin code work across multiple cogs. I am fairly new to the propeller system and am a biologist by training so please pardon my naivete in all this! So far, I tried just copying every single method and variable and labeled it incrementally from 1 through 6 representing the six signals I would like to read simultaneously. Every time I run the code for a single cog (original jm_freqin code), it works perfectly but when I set it up to initialize 6 cogs, I get zeros across the board as the signal value in hertz. I am using the Parallax Serial Terminal to output six independent values in one line separated by commas and a carriage return and new line so that every second a read of the six signal sources are printed as CSV values down the serial line. My question is: How does one deploy assembly code (specifically the jm_freqin code) properly across multiple cogs such that each cog runs the same method and outputs its own unique value at the same time as the other cogs. Below is the bastardization of the jm_freqin code I thought would "work" as reference. All I need is a way to read six signals at the same time and output their frequency values down the serial line. I am certain there is waaaay more elegant way to do this and any input would be much appreciated!

var
  long  cog1
  long  cog2
  long  cog3
  long  cog4
  long  cog5
  long  cog6
  long  fcPin1
  long  fcPin2
  long  fcPin3
  long  fcPin4
  long  fcPin5
  long  fcPin6                                                   
  long  fcCycles1                                                 
  long  fcCycles2                                                 
  long  fcCycles3                                                 
  long  fcCycles4                                                
  long  fcCycles5                                                 
  long  fcCycles6
  
pub init1(p)
                                   
    fcPin1 := p
    fcCycles1 := 0
    cog1 := cognew(@frcntr1, @fcPin1) + 1


pub init2(p)
                                                     
    fcPin2 := p
    fcCycles2 := 0
    cog2 := cognew(@frcntr2, @fcPin2) + 1
pub init3(p)
                                                     
    fcPin3 := p
    fcCycles3 := 0
    cog3 := cognew(@frcntr3, @fcPin3) + 1
 
pub init4(p)

    fcPin4 := p
    fcCycles4 := 0
    cog4 := cognew(@frcntr4, @fcPin4) + 1
  
pub init5(p)
                                                   
    fcPin5 := p
    fcCycles5 := 0
    cog5 := cognew(@frcntr5, @fcPin5) + 1
  
pub init6(p)
                                                   
    fcPin6 := p
    fcCycles6 := 0
    cog6 := cognew(@frcntr6, @fcPin6) + 1

pub period1
  return fcCycles1
  
pub period2
  return fcCycles2
  
pub period3
  return fcCycles3
  
pub period4
  return fcCycles4
   
pub period5
  return fcCycles5
  
pub period6
  return fcCycles6 

pub freq1 | p1, f1

  p1 := period1
    f1 := clkfreq * 10 / p1                                       
    fcCycles1 := 0
  return f1
    
pub freq2 | p2, f2
    
    p2 := period2
    f2 := clkfreq * 10 / p2                                       
    fcCycles2 := 0                                               
  return f2
  
pub freq3 | p3, f3
    
  p3 := period3
    f3 := clkfreq * 10 / p3                                       ' calculate frequency
    fcCycles3 := 0                                               ' clear for loss of input
  return f3
  
pub freq4 | p4, f4

  p4 := period4
    f4 := clkfreq * 10 / p4                                       ' calculate frequency
    fcCycles4 := 0                                               ' clear for loss of input
  
  return f4
pub freq5 | p5, f5
           
  p5 := period5
  
    f5 := clkfreq * 10 / p5                                       ' calculate frequency
    fcCycles5 := 0                                               ' clear for loss of input
  return f5
  
pub freq6 | p6, f6

p6 := period6
    f6 := clkfreq * 10 / p6                                       ' calculate frequency
    fcCycles6 := 0                                               ' clear for loss of input
  return f6
  
dat

                        org     

frcntr1                 mov     tmp11, par                       ' start of structure
                        rdlong  tmp12, tmp11                      ' get pin#

                        mov     ctra, POS_DETECT1                ' ctra measures high phase
                        add     ctra, tmp12
                        mov     frqa, #1
                        
                        mov     ctrb, NEG_DETECT1                ' ctrb measures low phase
                        add     ctrb, tmp12
                        mov     frqb, #1
                        
                        mov     mask1, #1                        ' create pin mask
                        shl     mask1, tmp12
                        andn    dira, mask1                      ' input in this cog

                        add     tmp11, #4
                        mov     cyclepntr1, tmp11                 ' save address of hub storage

restart1                 waitpne mask1, mask1                      ' wait for 0 phase
                        mov     phsa, #0                        ' clear high phase counter
       
highphase1               waitpeq mask1, mask1                      ' wait for pin == 1
                        mov     phsb, #0                        ' clear low phase counter
                                                
lowphase1                waitpne mask1, mask1                      ' wait for pin == 0
                        mov     cycles1, phsa                    ' capture high phase cycles

endcycle1                waitpeq mask1, mask1                      ' let low phase finish
                        add     cycles1, phsb                    ' add low phase cycles
                        wrlong  cycles1, cyclepntr1               ' update hub

                        jmp     #restart1

' --------------------------------------------------------------------------------------------------

POS_DETECT1              long    %01000 << 26 
NEG_DETECT1              long    %01100 << 26

tmp11                    res     1
tmp12                    res     1

mask1                    res     1                               ' mask for frequency input pin
cyclepntr1               res     1                               ' hub address of cycle count
cycles1                  res     1                               ' cycles in input period

                        fit     
dat

                        org     0

frcntr2                  mov     tmp21, par                       ' start of structure
                        rdlong  tmp22, tmp21                      ' get pin#

                        mov     ctra, POS_DETECT2                ' ctra measures high phase
                        add     ctra, tmp22
                        mov     frqa, #1
                        
                        mov     ctrb, NEG_DETECT2                ' ctrb measures low phase
                        add     ctrb, tmp22
                        mov     frqb, #1
                        
                        mov     mask2, #1                        ' create pin mask
                        shl     mask2, tmp22
                        andn    dira, mask2                      ' input in this cog

                        add     tmp21, #4
                        mov     cyclepntr2, tmp21                 ' save address of hub storage

restart2                 waitpne mask2, mask2                      ' wait for 0 phase
                        mov     phsa, #0                        ' clear high phase counter
       
highphase2               waitpeq mask2, mask2                      ' wait for pin == 1
                        mov     phsb, #0                        ' clear low phase counter
                                                
lowphase2                waitpne mask2, mask2                      ' wait for pin == 0
                        mov     cycles2, phsa                    ' capture high phase cycles

endcycle2                waitpeq mask2, mask2                      ' let low phase finish
                        add     cycles2, phsb                    ' add low phase cycles
                        wrlong  cycles2, cyclepntr2               ' update hub

                        jmp     #restart2

' --------------------------------------------------------------------------------------------------

POS_DETECT2              long    %01000 << 26 
NEG_DETECT2              long    %01100 << 26

tmp21                    res     1
tmp22                    res     1

mask2                    res     1                               ' mask for frequency input pin
cyclepntr2               res     1                               ' hub address of cycle count
cycles2                  res     1                               ' cycles in input period

                        fit     492
dat
                      

Assembly code repeating regions were removed so the code would fit the post. The serial demo side of the code is below:
con

  _clkmode = xtal1 + pll16x
  _xinfreq = 5_000_000

obj

  fc   : "jm_freqin" 
  pst : "Parallax Serial Terminal"

pub main                                                     
  EnablePST

  fc.init1(1)
  fc.init2(2)
  fc.init3(3)
  fc.init4(4)
  fc.init5(5)
  fc.init6(6)

  repeat                                                  
      pst.dec(fc.freq1)
      pst.char(",")
      pst.dec(fc.freq2)
      pst.char(",")
      pst.dec(fc.freq3)
      pst.char(",")
      pst.dec(fc.freq4)
      pst.char(",")
      pst.dec(fc.freq5)
      pst.char(",")
      pst.dec(fc.freq6)    
      pst.NewLine
      pst.LineFeed
    waitcnt(clkfreq + cnt)   


PRI EnablePST
  pst.start(115200)
  pst.Home
  pst.Clear 

Comments

  • What you need is effectively a separate copy of jm_freqin for each of the signals you want to measure. The OBJ directive allows you to create an array of objects, each with its own local variables. Use this:

    obj fc[ 6 ] : "jm_freqin"

    To initialize the six channels, each in its own cog, you'd do this (with i a local variable):
    repeat i from 0 to 5
       fc[ i ].init( i+1 )
    

    Obviously, if you need to use I/O pins other than 1-6, you'd have to change the above.

    To display the results, you'd do something like:
    repeat i from 0 to 4
       pst.dec( fc[ i ].freq )
       pst.char( "," )
    pst.dec( fc[ 5 ].freq )
    pst.NewLine
    pst.LineFeed
    
  • Duane DegnDuane Degn Posts: 10,588
    edited 2015-10-02 16:49
    Mike Green beat me to it.

    Here's my version. It's pretty much the same as what Mike wrote.
    con
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    
    con
    
      #0, CLS, HOME, #8, BKSP, TAB, LF, CLREOL, CLRDN, CR           ' PST formmatting control 
    
    
    obj
    
      fc[6]   : "jm_freqin" 
      term : "jm_txserial"
      
    
    pub main | f, index
    
      repeat index from 0 to 5
        fc[index].init(index)                                                   
        
      term.init(30, 115_200)                                        ' start terminal
      waitcnt(clkfreq/10 + cnt)
      term.tx(CLS)
    
      '' removed frequency generator code
      
      repeat
        term.tx(HOME)
        repeat index from 0 to 5
          term.str(string(11, 13, "Freq["))
          term.dec(index)
          term.str(string("]: "))
          f := fc[index].freq                                                ' get frequency
          if f > 0                                                    ' valid?
            term.dec(f/10)                                            ' print whole part
            term.tx(".")
            term.dec(f//10)                                           ' print fractional part
            term.str(string(" Hz"))
          else
            term.str(string("???"))
          waitcnt(clkfreq + cnt)   
    
    

    One difference with Mike's code is the I/O pins used. Mike used pins 1 through 6, I used 0 through 5. As Mike points out, you'll need to modify the code to use different pins.
  • JonnyMacJonnyMac Posts: 9,105
    edited 2015-10-02 21:04
    Wow, I love having my code referred to as "lovely." I tend to unroll my code an not use indexed objects, but this is a good example (by Mike and Duane) where it's appropriate.
  • The solutions you all proposed work perfectly! Thank you so much for the valuable lesson of launching multiple objects and deploying them on different cogs! And thanks a million to JonnyMac for the great fundamental code. This is going toward a research paper that we hope to publish in the Public Library of Science on bacterial growth using a new device we designed and the entire project will be open source. You will be mentioned in the attributions section for sure. We already have a laundry list of people who put in their two cents and this is worth at least a nickle or two! Now onto the next part of the project, bluetooth!
  • One of the things I do in my listings is keep track of cogs usage in the OBJ declaration section. Here's an example from an actual customer project (Zombie escape room for Halloween).
    obj
    
    ' main                                                           ' * master Spin cog
      time   : "jm_time"                                             '   for timing and delays
      etimer : "jm_time"                                             '   escape (white fade) timing
      ptimer : "jm_time"                                             '   pulse (red light) timing
      btimer : "jm_time"                                             '   button access timing
      io     : "jm_io_basic"                                         '   essential io
      cmd    : "jm_fullduplexserial"                                 ' * for serial commands from terminal/RS-485
      pwm    : "jm_pwm8"                                             ' * pwm driver for LED dimmers
      serial : "jm_fullduplexserial"                                 ' * for serial commands to AP-16+
      dmx    : "jm_dmx_tx"                                           ' * DMX output
      parser : "jm_parser"                                           '   string parser for commands
    
    ' * uses cog when loaded/running
    

    As you can see, I prefer to unroll objects of the same time so that I can name them -- it's more verbose, an I prefer to code this way.

    What I want to point out is that I'm marking cog use. In your case you've got the main Spin cog and six frequency counters; you're down to one cog left for serial communications via Bluetooth - you're out of cogs.
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2015-10-05 17:30
    scocioba, How fast, high and low time, are the signals you're measuring? Thinking bacterial growth, it doesn't seem like nanosecond time scales, in which case the six counters don't need the speed of the cog counters. Maybe all six could fit in one cog.
  • We need solid readings once a second and ideally have them all independently running and in sync with the main cog's waitcnt + cnt delay. I was originally on an arduino due using interrupts but all the edge detection funnels into one main loop method so you would get sporadic interrupts which compounded error over time. I just did a 24hr drift test and saw no sign of drift thus far. I dont mind the cog per sensor setup and that extra cog for bluetooth is all I need. The sensor wires are currently feeding out of an incubator and am getting noise due to the orbit of the shaker, the magnetic clasps holding the device, and the loose wires dangling in the breeze. Next iteration will have some shielded ribbon cable and PCBs dedicated to the system. Currently we are using some 3D printed chassis with through hole components freestyle soldered in place with wires all over...a very hand crafted look. I think with proper shielding and minimal wires hanging out we should have solid readings. The sensors work fine on the arduino system with beautifully thin data lines but we need more sensors per experimental run to meet our end of november deadline for publication. We don't really need lower than 1 second readings but the option for faster reads would be nice.
Sign In or Register to comment.