Shop OBEX P1 Docs P2 Docs Learn Events
Questions about CTR and sound synthesis — Parallax Forums

Questions about CTR and sound synthesis

mrgoshmrgosh Posts: 23
edited 2007-08-31 20:43 in Propeller 1
I have rtfm, and am still not exactly clear on the uses / abilities of CTR(a/b). Is it that for each cog, there is a CTR (and thus, 16 CTR registers total?) or is it that there is a CTR(a/b) register for the prop chip as a whole, and it can be assigned to work with a cog?



Needless to say, I am new to the prop chip, but an averagely experienced programmer. My end goal would be to have a propeller chip act as a polyphonic synthesizer. What I am working on now is having 3 to 8 different tones generated by one chip, played out of 3 to 8 different speakers (pins 0 - 7). I am noticing though that no matter what I try to hack together from the examples, or the simple attempts I have made on my own, that once I get to CTR overlaps, a second register address rewrites the first regardless of cog assignment (example: (pin 0, cog 0, ctra, 800 hz); (pin 1, cog1, ctrb, 900hz); (pin 2, cog2, ctra, 1000hz); i hear 900hz and 1000hz, pin 0 is silent).

So, my guess: there is something I am missing, especially based on this line from the manual:

Each cog has two identical counter modules (A and B) that can perform many repetitive tasks. pg 204


Help?


-M

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2007-08-20 04:41
    Each cog has a pair of CTR registers. Look at the diagram of the Propeller on the Parallax website.

    Each cog can generate two different frequencies, one using each counter. The output of the counter is a square wave pulse stream which may or may not be what you want. If you haven't looked at it yet, download AN001, the application note on the counters.

    Keep in mind that each cog has its own copy of the DIRA and OUTA registers. When using more than one cog, the DIR registers are OR'd together, so any cog that sets a pin as an output will make that pin an output. The OUTA registers are also OR'd together, so any cog that sets a pin to 1 will make that pin a 1. The counter outputs are OR'd with the OUTA register when they're enabled.
  • deSilvadeSilva Posts: 2,967
    edited 2007-08-20 05:59
    There are some highly interesting "music drivers" by Chip Gracey, and Andr
  • m1nutemanm1nuteman Posts: 6
    edited 2007-08-20 10:35
    I'm working through the new 'Counter Modules and Circuit Applications' Education Kit Lab. It's been a great help in understanding the ctra/b·aspect of the Prop.

    You can get it from here: http://forums.parallax.com/showthread.php?p=617192
  • mrgoshmrgosh Posts: 23
    edited 2007-08-20 14:47
    Squarewave / PWM is fine, and I don't think - atleast not yet - I am interested in storing buffer data for playback. Synthesis is giving me plenty to deal with.

    Mike, thanks for the tip on the AN001. Should have been smart enough to find that on my own. I still might not be 100% on your explanation of the DIR / OUTA registers being OR'd, and its correlation to my original question. Or maybe I am just expecting that this is more complicated than it really is? Are you simply saying that if you call cognew and in the parameters a pin # is passed, this pins DIRA will automatically be set to HIGH? This seems strange to me, as you would not always - necessarily - be init'ing a new cog and immediately associating it with a pin output, correct? So maybe I am misreading...


    m1nuteman, thanks for the link to the education kit.


    Thanks for the help, guys.
  • Mike GreenMike Green Posts: 23,101
    edited 2007-08-20 15:33
    mrgosh,
    You're misreading. I was simply trying to restate how the DIRA / OUTA registers in the individual cogs interact to produce the I/O pin behavior. It's described in the Propeller Manual and the datasheet.
  • deSilvadeSilva Posts: 2,967
    edited 2007-08-20 20:10
    @mrgosh
    Well you started this thread with a mysterious problem... Is it solved in the meantime? When you need further help you have to post your code..
  • mrgoshmrgosh Posts: 23
    edited 2007-08-20 20:21
    I'm at work today. Tinkering time will be tonight. I've made some discoveries thanks to yours and some others suggestions, and of course, the results will get posted.
  • mrgoshmrgosh Posts: 23
    edited 2007-08-29 16:18
    Ok. So. Got to messing around, trying to get two frequencies out of one cog and got this far (based on one of the examples).

                      org   0
    entry0         mov dira, diraval0             
                      mov ctra, ctraval0           
                      mov ctrb, ctrbval0
                      mov frqa, #1                  
                      mov frqb, #1 
            
                      mov timea0, cnt              
                      mov timeb0, cnt
                      add timea0, perioda0            
                      add timeb0, periodb0
    
    :loop            cmp   timea0, timeb0    wc
                       if_c  jmp #:do_timea
                       if_nc jmp #:do_timeb
            
    :do_timea     waitcnt timea0, perioda0
                       neg phsa, valuea0
                       jmp #:loop
    
    :do_timeb     waitcnt timeb0, periodb0
                        neg phsb, valueb0
                        jmp #:loop
    
    



    in the entry section of the asm, timea0 and timeb0 have cnt value read into them successively. perioda0 and b0 are arbitrary frequencies (1400 and 1500 right now i think). the A register ouput pin 0, the B pin 1.

    The Problem

    Both frequencies play, simultaneously, from two pins, on the same cog. They only play, however, for about half a second before they stop. After, there emits a small click every minute or so, but nothing else. I am wrecking my brain trying to figure out what I am missing. Hoping it is just another pair of eyes that will do the trick.
  • Tracy AllenTracy Allen Posts: 6,660
    edited 2007-08-29 16:58
    The reason your program devolve to clicks is that sooner or later, a time for one period will fall so close before the second time that the second time will already have passed when the code arrives at its waitcnt. So then it has to wait one minute before the waiitcnt tests true.

    Both counters are intialized to the same value, with no apparent change of pin number.

    If you simply want two symmetric square waves, at two different frequencies, then the counters can do that autonomously, without any program intervention after the initialization. Do you understand how to do that? Then filter it externally if necessary. On the other hand, if you want the Prop itself to generate more complex waveforms on each output pin, it might be better for this application to have the two counters operating in duty mode (%00110), and create two numerical oscillators in pasm to modulate the duty cycle of the output waveform. The duty mode is easy to filter.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Tracy Allen
    www.emesystems.com
  • deSilvadeSilva Posts: 2,967
    edited 2007-08-29 18:01
    On the other hand it is not impossible to handle two (or more if there were more counters to a COG) PWM signals the way you do.

    Your program is basically all right, so it can be diffucult to find the weak spot, as Tracy with his great experience has done immediately.

    To explain it to others: What Mrgosh does is:
    - set the PHSx so the timerx can generate the signal
    - wait to the earliest moment where either this timer or the other timer needs service again (CMP)

    As Tracy pointed out, this time can sometimes be a little bit in the past, thus leading to a "longer" wait..

    A simple fix is to check for this situation before each WAITCNT. Dont fear overflows of CNT, it will always come out correctly smile.gif

    The difference is generally very small, but if you want to be very precise you have to decide, how to keep up the phase (reduce just next "newtime" or reduce pulse and "newtime")

    Post Edited (deSilva) : 8/29/2007 6:06:36 PM GMT
  • mrgoshmrgosh Posts: 23
    edited 2007-08-30 16:44
    excuse me for theorizing, i am at work so cannot experiment on my own, but:

    if 'time' is set as a constant does this situation still arise?
  • deSilvadeSilva Posts: 2,967
    edited 2007-08-30 17:04
    'time' ???
  • mrgoshmrgosh Posts: 23
    edited 2007-08-31 16:02
    I guess I do not understand, exactly, how to account for things like the roll over of the clock, or this phenomena of cycle number running ahead of stored time values (i.e. store a cnt value in timeVar, and by the time you get to a waitcnt using timeVar, the cnt value in timeVar has passed). I have tried some conditionals using wc / wz effects, but they do not seem to be doing the trick.


    In this same project as the code above, I am also running into the fact that sound stops (even just simple sq. wave generation) after about 67 seconds (2^32 / clkfreq). This, I am assuming, has to do with clock roll over from 64,000,000 to 0. If someone could point me to a resource so I might better understand how to program with these facts in mind, it would be greatly appreciated. One goal, lets say, would be to have a frequency emit for 15 minutes, stop, and then start again at 20 minutes. I am finding these things to be a great challenge, so thanks for your input everyone.
  • deSilvadeSilva Posts: 2,967
    edited 2007-08-31 18:10
    :do_timea     waitcnt timea0, perioda0
                       neg phsa, valuea0
    



    should change to:
    :do_timea     
                       CMPS timea0, CNT  WC
                       IF_NC waitcnt timea0, perioda0
                       neg phsa, valuea0
    




    When you want to time things >1 minute you have to construct a "clock"; this is possible, but there are some obstacles... Ariba had a good idea the other day: He suggested using ONE timer to output to a pin, and the OTHER counter to use that pin as input. As you need the counters this means doing it in a different COG,
    But when using a new COG you could find as well a pure software solution...
  • mrgoshmrgosh Posts: 23
    edited 2007-08-31 18:19
    is there a reason one could not set up a cog to count 'seconds', as in

    PUB main | x
     
          x:=0
          cognew(timer(x), @stack)
          
          repeat while seconds < 100
                freq.synth("A", 0, 400)
    
    PUB timer(seconds)
    
          repeat
                seconds++
                waitcnt(64_000_000)      '<---my clock setting
    
    



    or whatever your code which should go for longer than 1 minute and then stop happens to be.


    I cannot seem to make this work, so - once again - there must be something I do not know.



    EDIT:


    or even, somewhat more simply:


    PUB main | x
     
          x:=0
          cognew(timer(x), @stack)
          coginit(2, freq.synth("A", 0, 400))
    
          if seconds > 100
             cogstop(2)
               
    
    PUB timer(seconds)
    
          repeat
                seconds++
                waitcnt(64_000_000)      '<---my clock setting
    
    

    Post Edited (mrgosh) : 8/31/2007 6:29:59 PM GMT
  • deSilvadeSilva Posts: 2,967
    edited 2007-08-31 18:40
    Oops, no it will not work....

    Try this:
    :do_timea     
        MOV     RA, CNT
        SUB      RA, timea0
        CMPS    RA, #0   WC
        IF_C waitcnt timea0, perioda0
        neg phsa, valuea0
    



    I shall find a simpler solution...
    RA is an intermediate cell, use what is free
  • deSilvadeSilva Posts: 2,967
    edited 2007-08-31 18:48
    @ Your CLOCK examples in SPIN.
    Yes these will be examples of what I called ".. a pure software solution..."!


    They however block a COG totally. Aribas idea using two timer/counters would be a fully automated clock....
  • mrgoshmrgosh Posts: 23
    edited 2007-08-31 19:15
    Well apparently theory and practice are very different. What am I missing?

    CON _clkmode = xtal1 + pll16x
            _xinfreq = 4_000_000
    
    VAR
    
    long parm1
    long stack[noparse][[/noparse]9]
    
    PUB go | x
    
      x:=0
         
      parm1 := (clkfreq / 182)
      coginit(1, @entry1, @parm1)
    
      coginit(2, counter(@x), @stack)
    
      if x > 10
        waitcnt((clkfreq / 1) + cnt)
        cogstop(1)
          
    PUB counter(sec)
    
      repeat
         sec++
         waitcnt(64_000_000)
         
        
    DAT
    {PIN 0-----------------------------------------------}
            org   0
    
    entry1  rdlong period1, par
    
                mov dira, diraval1            
                mov ctra, ctraval1             
                mov frqa, #1                   
            
                mov time1, cnt                 
                add time1, period1           
          
    
    :loop     waitcnt time1, period1                   
                neg phsa, value1                             
                jmp #:loop                          
    
    
    diraval1 long %00000000_00000000_0000000_11111111
    ctraval1 long %00100 << 26 + 0        
    value1  long  10000
    period1 res  1
    time1   res  1
    
    
    
  • deSilvadeSilva Posts: 2,967
    edited 2007-08-31 20:43
    Sorry I did not spot this in your first posting.
    WAITCNT waits upto this very number you give... and it has to wait for about a minute smile.gif

    do
    WAITCNT(CNT+64_000_000)
    
Sign In or Register to comment.