Shop OBEX P1 Docs P2 Docs Learn Events
Question about signal generation — Parallax Forums

Question about signal generation

ReinhardReinhard Posts: 489
edited 2015-05-04 04:11 in Propeller 1
Hello Forumists
I need 2 signals with 90 degree phase shift, generated by the Propeller.
I know the logic circuit with 2 JK-FF in feedback loop, but I do not want external components.
Further I do not like a software solution like this:
while(1)
{
high(PINA);
usleep(dt);
high(PINB);
usleep(dt);
low(PINA);
usleep(dt);
low(PINB);
usleep(dt);
}
So I can make two signals with maximal 250kHz.
But I need a cog for this job;
My question is, is it possible with tricky usage of counters, with set and forget ?
The range is 10 .. 20 kHz.
Thanks for advices ;-)
Reinhard

Comments

  • AribaAriba Posts: 2,690
    edited 2014-06-10 05:22
    Hello Reinhard

    It should be easy to generate two signals with the same frequency but 90° shifted with two Propeller counters.
    Just set them to the same frequency and before you start the counters set PHSA to 0 and PHSB to 0x40000000.
    (360° is the full 2^32 range of the counter, so 90° is 1/4 = 0x40000000).

    If PropGCC allows to use the counter registers directly then a possible code can look like this:
    PHSA = 0;
      PHSB = 0x40000000;
      FRQA = 54 * <freq_in_Hz>;  // for 80MHz Clock
      FRQB = FRQA;
      CTRA = (4<<26) + <pin1>;   // NCO 0 degree
      CTRB = (4<<26) + <pin2>;   // NCO 90 degree
    
    You will need to replace the values in < > with numbers or variables.
    The FRQx calculation is simplified but the error is under 1% with this methode.

    Andy
  • ReinhardReinhard Posts: 489
    edited 2014-06-10 05:53
    Hello Andy,
    this is exact what I need.
    Thank you
  • AribaAriba Posts: 2,690
    edited 2014-06-10 06:12
    Your welcome

    What I forget: You also need to set the pins to output in the DIRA register, otherwise the counters will not output the signals.

    Andy
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2014-06-10 08:15
    Because your'e starting CTRB late, compared to CTRA, you will need to add a little to PHSB's startup value to account for the overhead.

    -Phil
  • ReinhardReinhard Posts: 489
    edited 2014-06-11 11:58
    @ Phil
    Thank you for the hint
    with a small C snippet I can determine this little
    /**
    
      * This is the main TwoPhase90Degree program file.
      * produce 2 signals with 90 Degree Phase difference
      */
     #include "simpletools.h"
     #define phase1 16
     #define phase2 17
     #define f 1
     int main ()
     {
       DIRA |= 1<<phase1;
       DIRA |= 1<<phase2;
       
       PHSA = 0;
       PHSB = 0x40000000 + 0x4020;
       FRQA = 54 * f;  // for 80MHz Clock
       FRQB = FRQA;
       CTRA = (4<<26) + phase1;   // NCO 0 degree
       CTRB = (4<<26) + phase2;   // NCO 90 degree
       while(1)
       {
         printf("%8X : %8X : %8X\n",PHSA,PHSB,PHSB-PHSA);
       }
     }
    

    the diff is always 0x40000000 ;-))

    @Andy
    Thank's again for the code
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2014-06-11 12:17
    Reinhard,

    Whatever you add to PHSB has to be multiplied by the value in FRQB to account for the overhead. Also, in your printf statement, PHSA and PHSB are not read simultaneously, so the result you see will be misleading.

    The most reliable way to get true I and Q output is to do it in PASM, where the overheads are clearly defined and predictable.

    -Phil
  • ReinhardReinhard Posts: 489
    edited 2014-06-11 12:49
    @Phil

    you are right.
    for a certain frequency I adjust the value with an osci.
  • jmgjmg Posts: 15,173
    edited 2014-06-12 16:15
    To confirm you have 90' you could use a Counter/Timer, or failing that, an XOR gate, a couple of RC's and a multimeter could compare the XOR result of the 2 pins, with same-package copy of each individual pin.
    - be interesting to know what Prop-C 'fixup' offset is needed for PHSB, to give 90.00'
    At 250KHz & 80MHz it is 320 clks per full period.
  • ReinhardReinhard Posts: 489
    edited 2014-06-13 01:26
    Hi jmg
    can you explain me the method with xor-gate and multimeter ?
    phase1 xor phase2 give a signal with double frequency.
    for example
    ...110011001100...
    ...011001100110...
    ...101010101010...
    If the 90 degree shift exact, the duty cycle from new signal is 50%
    with a multimeter I see Vcc/2
    is it this what you mean ?
    Thanks
    Reinhard
  • jmgjmg Posts: 15,173
    edited 2014-06-13 03:58
    Reinhard wrote: »
    If the 90 degree shift exact, the duty cycle from new signal is 50%
    with a multimeter I see Vcc/2
    is it this what you mean ?

    Yes, and feed that thru a RC, just in case the AC disturbs the multimeter more sensitive ranges..
    If you also wire other XOR gates as simple inv or non inv, and do the same RC they also should be 50.00%, and you can scale the meter to get an amplified zero with a differential reading.
    180' span is Vcc sweep, so that gives ~ 20.625mV change per SysCLK step (12.5ns)
  • ReinhardReinhard Posts: 489
    edited 2014-06-13 05:13
    I'll try it as soon as possible
    But my next <project> is (14 days Italy ;-)
    Reinhard
  • ReinhardReinhard Posts: 489
    edited 2014-06-13 08:48
    Before I really go ;-)
    Currently I have nether a multimeter nor an osci here.
    So I use the stereo output from my DemoBoard, connect to the soundcard of my notebook
    and write a short programm for display the two signals.
    At moment this is very raw, but I go on to improve it.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2014-06-13 10:18
    If you have a spare cog, you can use the counters to test your phase shift. Here's a sample program that shows how:
    CON
    
      _clkmode      = xtal1 + pll16x
      _xinfreq      = 5_000_000
    
      IF_PIN        = 24
      QF_PIN        = 25
      TFRQ0         = $20c49b  '40 kHz.
    
    VAR
    
      long  testa, testb
    
    OBJ
    
      sio   :       "Parallax Serial Terminal"
    
    
    PUB start
    
      sio.start(115200)
      cognew(@quadrature, 0)
      cognew(@test_iq, @testa)
      repeat
        sio.dec(testa)
        sio.char(" ")
        sio.dec(testb)
        sio.char(13)
        waitcnt(cnt + clkfreq / 5)         
    
    DAT
    
    '' Generate two square waves, 90 degrees out of phase (< 4 MHz).
    
                  org       0
    
    quadrature    mov       dira,q_dira0
                  mov       frqa,q_freq
                  mov       frqb,q_freq
                  mov       phsa,#0
                  mov       phsb,q_x40000000
                  add       phsb,q_freq
                  add       phsb,q_freq
                  add       phsb,q_freq
                  add       phsb,q_freq
                  mov       ctra,q_ctra0
                  mov       ctrb,q_ctrb0
    :forever      jmp       #:forever
    
    
    '-----------[ Constants & Variables ]------------------------------------------
    
    q_freq        long      TFRQ0
    q_ctra0       long      %00100 << 26 | IF_PIN
    q_ctrb0       long      %00100 << 26 | QF_PIN
    q_dira0       long      1 << IF_PIN | 1 << QF_PIN
    q_x40000000   long      $4000_0000
    
    ''===================================================================
    
    '' Test the 90-degree phase shift between I and Q:
    
    '' I: &#61574;&#61574;&#61574;&#61574;&#61574;&#61573;&#61569;&#61569;&#61569;&#61569;&#61569;&#61570;&#61574;&#61574;&#61574;&#61574;&#61574;&#61573;&#61569;&#61569;&#61569;&#61569;&#61569;
    '' Q: &#61574;&#61574;&#61573;&#61569;&#61569;&#61569;&#61569;&#61569;&#61570;&#61574;&#61574;&#61574;&#61574;&#61574;&#61573;&#61569;&#61569;&#61569;&#61569;&#61569;&#61570;&#61574;&#61574;
    ''     A  B  C  D  A  B  C  D
    
    '' CTRA counts times in states A and C.
    '' CTRB counts times in states B and D.
    
    '' With true quadrature output, the two counts will be equal.
    
                  org       0
    test_iq       mov       t_addra,par
                  mov       t_addrb,par
                  add       t_addrb,#4
                  mov       ctra,t_ctra0
                  mov       ctrb,t_ctrb0
                  waitpne   t_imask,t_imask
                  waitpeq   t_imask,t_imask
                  mov       frqa,#1
                  mov       frqb,#1
                  mov       t_delay,cnt
                  add       t_delay,_80_000_000
                  
    test_lp       waitcnt   t_delay,_80_000_000
                  mov       t_phsa,phsa
                  mov       t_phsb,phsb
                  sub       t_phsa,t_phsap
                  add       t_phsap,t_phsa
                  sub       t_phsb,t_phsbp
                  add       t_phsbp,t_phsb
                  wrlong    t_phsa,t_addra
                  wrlong    t_phsb,t_addrb
                  jmp       #test_lp
    
    t_ctra0       long      %11001 << 26 | IF_PIN << 9 | QF_PIN
    t_ctrb0       long      %10110 << 26 | IF_PIN << 9 | QF_PIN
    _80_000_000   long      80_000_000
    t_phsap       long      0
    t_phsbp       long      0
    t_imask       long      1 << QF_PIN             
    
    t_addra       res       1
    t_addrb       res       1
    t_delay       res       1
    t_phsa        res       1
    t_phsb        res       1
    
    

    If the phase shift is correct, the two numbers displayed will be approximately equal.

    -Phil
  • Akros_10Akros_10 Posts: 6
    edited 2015-04-21 00:34
    Hi, I have been followed this thread and finally, I got two signals with a phase shift of 90º. I can select a frequency and the phase shift is correct, however once I modify the frequency value the phase shift is modified too, how can I modify the frequency without modifying the phase shift??

    And, would it be possible to generate more than 2 sync signals? I mean 3 or more signals with defined phase shifts using different cogs? I can create sync pairs using the same cog, but they are not synchronize with other pairs created using other cogs.

    Many thanks in advance
  • kwinnkwinn Posts: 8,697
    edited 2015-04-21 04:05
    Akros_10 wrote: »
    Hi, I have been followed this thread and finally, I got two signals with a phase shift of 90º. I can select a frequency and the phase shift is correct, however once I modify the frequency value the phase shift is modified too, how can I modify the frequency without modifying the phase shift??

    You can not. The period of the waveform is 1/frequency, and to have a 90 degree phase shift the two signals have to be 1/4 of the period shifted from each other. For producing phase shifted signals in software I find it simpler to calculate the period from the frequency and use one fourth of that value for the timing.
    And, would it be possible to generate more than 2 sync signals? I mean 3 or more signals with defined phase shifts using different cogs? I can create sync pairs using the same cog, but they are not synchronize with other pairs created using other cogs.

    Many thanks in advance

    Yes, that can be done if you synchronize the cogs when you start them.
  • jmgjmg Posts: 15,173
    edited 2015-04-21 13:33
    Akros_10 wrote: »
    however once I modify the frequency value the phase shift is modified too, how can I modify the frequency without modifying the phase shift??
    The Prop thinks in time, not frequency.
    When you modify & reload the frequency, in order to preserve phase you need to also reload the phase-offset
    - ie that's 4 values updates for 2 counters running

    There will always be some disturbance when you change values, but you could reduce that << 1us, with a pre-calculation of the 4 new values, then a simple 4 line copy.

    If you want all COGs aligned on updates, then a shared trigger pin, or a global VAR for waitcnt can have all 'launch' at the same clock edge.
    The trigger-step, should be done last, after any HUB reads.
  • TappermanTapperman Posts: 319
    edited 2015-04-21 16:41
    Akros_10 wrote: »
    would it be possible to generate more than 2 sync signals? I mean 3 or more signals with defined phase shifts using different cogs?

    I'm not sure if I understand the question properly? But, if you mean (as an example only) generate 3 signals on three different pins using 3 different cogs ... then, each one could wait for its linked cogs pin to change then apply a global 'wait' before toggling the next cogs output pin. Also, the duration of the pins 'active' state would have to be related to the wait time ... etc. Change the global 'delay' period and the freq changes ... but without disturbing the phase between them. This would need to be a PASM solution to reach 20KHz (I would think).
    I/O pins --->  7          8          9
                   &#9474;          &#9474;          &#9474;
                &#9484;&#9472;&#9472;&#9524;&#9472;&#9472;&#9472;&#9488;   &#9484;&#9472;&#9472;&#9524;&#9472;&#9472;&#9472;&#9488;   &#9484;&#9472;&#9472;&#9524;&#9472;&#9472;&#9472;&#9488;   
                &#9474;      &#9474;   &#9474;      &#9474;   &#9474;      &#9474;
                &#9474;  2   &#9474;   &#9474;  3   &#9474;   &#9474;  4   &#9474;
                &#9474;      &#9474;   &#9474;      &#9474;   &#9474;      &#9474;
                &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;   &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;   &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
                  cog        cog        cog
    
                &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
                &#9474;  Adjustable Delay  &#9474;
                &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
                    HUB Ram Variable
    
    

    Then, the only other issue would be if you want cog2 to retrigger at the end of cog4s' cycle ... or to trigger off another pin or event?

    Anyway, you have plenty of suggestions now. And, if you don't mean what I described ... ignore post.

    ... Tim
  • Akros_10Akros_10 Posts: 6
    edited 2015-04-22 08:20
    Thanks to all of you. I have managed to synchronize two quadrature pairs using two different cogs, using waitcnt as a trigger. I've been reading how this variable works and I use this code for each cog. Not sure if I get how waitcnt works because it takes too much to start.


    DIRA |= 1<<phase3;
    DIRA |= 1<<phase4;
    PHSA = 0;
    PHSB = 0x40000000 + (0x3620*f1);
    FRQA = 54 * f1; // for 80MHz Clock
    FRQB = FRQA;
    waitcnt(t2); // t2= CNT + 1000;
    CTRA = (4<<26) + phase3;
    CTRB = (4<<26) + phase4;

    In order to vary frequency I re-program FRQA, FRQB. Then, I have been trying to obtain the same difference between PSHB-PSHA that I get in the first programming. But I cant, I have tried using the same values, reading PSHA and adding 0x4000000 and some other combinations. Each update modifies a little bit the phase shift.
    It can be accomplished by stopping the cog, modifying the freq and run the cog again, but I would like to modify it without stopping the cogs.
  • AribaAriba Posts: 2,690
    edited 2015-04-22 10:43
    If the frequencies you need are not too high ( < 100kHz) then a PASM loop can replace the counters.
    The following Assembler code does exactly the same as a counter in NCO mode, but three times and all 3 NCOs use the same FRQ register. So they update always to the same frequency at the same time (that is: in the same PASM loop). Therefore the phase shifts don't change when you change the frequency, and need to be set only once before the loop.

    Here is the Spin+PASM code (just copy it into an empty Spin file):
    '' Triple oscillator with adjustable phase offsets
    
    CON
      _clkmode  = xtal1 + pll16x
      _xinfreq  = 5_000_000
    
      PINA = 16     'pin numbers
      PINB = 17
      PINC = 18 
    
    VAR
      long  freq
      
    PUB Main : i
      freq := 2577 * 100            '100 Hz
      cognew(@pasm,@freq)           'start oscillator cog
    
      repeat                        'freq sweep
        repeat i from 100 to 1000 step 50
          freq := 2577 * i
          waitcnt(clkfreq + cnt)
    
    DAT
    pasm    mov    dira,pin1        'set pins to output
            or     dira,pin2
            or     dira,pin3
            mov    phs1,deg0        'set phase offsets
            mov    phs2,deg1
            mov    phs3,deg2
    loop    rdlong frq,par          'read freq
            add    phs1,frq         '3 NCOs with same freq
            add    phs2,frq
            add    phs3,frq
            mov    phs1,phs1   wc   'copy bit31 to pins
            muxc   outa,pin1
            mov    phs2,phs2   wc
            muxc   outa,pin2
            mov    phs3,phs3   wc
            muxc   outa,pin3
            jmp    #loop            '48cy = 1.67MHz fs
    
    pin1    long   1 << PINA
    pin2    long   1 << PINB
    pin3    long   1 << PINC
    
    deg0    long   11930464 * 0     'phase offsets in degree
    deg1    long   11930464 * 90
    deg2    long   11930464 * 180
    
    frq     res    1                'variables
    phs1    res    1
    phs2    res    1
    phs3    res    1
    

    Andy
  • Akros_10Akros_10 Posts: 6
    edited 2015-04-28 03:17
    Thank you for your answer, I have tried your code and works perfectly in Spin. However, my project has been developed using C and simpleIDE so I guess I could insert some of the PASM loop into it. It has been difficult so far, but I will keep trying.

    Thanks again
  • Akros_10Akros_10 Posts: 6
    edited 2015-04-30 04:10
    Hi again,

    After several tries I might be missing something. I have set a piece code like I wrote here before.
    DIRA |= 1<<pinA;
    DIRA |= 1<<pinB;
    PHSA = 0;
    PHSB = 0x40000000 + (0x3620*freq);
    FRQA = 54 * freq;
    FRQB = FRQA;
    waitcnt(t);
    CTRA = (4<<26) + pinA; // NCO 0 degree
    CTRB = (4<<26) + pinB; // NCO 90 degree

    which give me 2 quadrature signals. However in order to modify the frequency I have added a pasm loop trying to follow your advice.
    Well, I cannot keep the phase shift constant. If a use the following code I have 2 in-phase signals and the frequency varies wothout affecting the phase shift. However once I add something else to PSHB the phase shift varies in every frequency modification.


    __asm__ __volatile__("mov PHSB, PHSA wc \n\t"
    "mov FRQA, %0 wc \n\t"
    "mov FRQB, FRQA wc \n\t"
    : "=r" (out)
    : "r" (fr_sent));
    pause(2000);
    freq=freq+50;
    fr_sent=54 * freq;
  • AribaAriba Posts: 2,690
    edited 2015-04-30 10:24
    You can not write to FRQA and FRQB at the same time, so there will be always a liitle phasesshift because one counter counts with the old rate while the other already counts with the new rate. Do it wit inline assembly is much better, but there is still a small difference.

    Just an idea: If you do the frequency change in two equal steps, and change one time first FRQA and the other time first FRQB then the phase errors may be compensated. Something like that:
    void setFreq(int freq)   //freq in Hz
    {
      int mid;
    
      freq = 537 * freq / 10;    //calc new FRQx value
      mid = (FRQA + freq) / 2;   //middle between old and new FRQx value
      FRQA = mid;                //set mid value A then B
      FRQB = mid;
      FRQB = freq;               //set new value B then A
      FRQA = freq;
    }
    

    Andy
  • Mark_TMark_T Posts: 1,981
    edited 2015-04-30 14:45
    If you always update the two FRQx registers in the same order the errors do not accumulate.
  • Akros_10Akros_10 Posts: 6
    edited 2015-05-04 01:18
    Already tried something like this setfreq function. The phase deviation is very small and with some modifications I could use it, however, the problem is that at first the signals present a perfect quadrature configuration, but once I modified the frequency the phase shift changes to other values and stays (almost) constant to that value.
  • Akros_10Akros_10 Posts: 6
    edited 2015-05-04 02:11
    Nevermind, I used this and it worked.

    void setFreq(int freq) //freq in Hz
    {
    int mid; int out;
    phs_b=0x40000000+ (0x1ec0*freq); //adjust phase
    freq = 537 * freq / 10; //calc new FRQx value
    mid = (FRQA + freq) / 2; //middle between old and new FRQx value
    FRQA = mid; //set mid value A then B
    FRQB = mid;
    FRQB = freq; //set new value B then A
    FRQA = freq;
    //
    Esto modifica la freq en ensamblador
    __asm__ __volatile__("mov PHSB, PHSA wc \n\t"
    "add PHSB, %0 wc \n\t"

    : "=r" (out)
    : "r" (phs_b));
    }
  • kuronekokuroneko Posts: 3,623
    edited 2015-05-04 04:11
    Akros_10 wrote: »
    Nevermind, I used this and it worked.

    void setFreq(int freq) //freq in Hz
    {
    int mid; int out;
    phs_b=0x40000000+ (0x1ec0*freq); //adjust phase
    freq = 537 * freq / 10; //calc new FRQx value
    mid = (FRQA + freq) / 2; //middle between old and new FRQx value
    ...
    Shouldn't the phs_b calculation be based on the final freq instead of its initial Hz valueA? Besides, now that you sync the phsx values in PASM you can simply set frqa and frqb.

    A not that it matters as such - frqx being fn(freq) - it just seems more logical
Sign In or Register to comment.