Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 24 of 24

Thread: Frequency Locked Sine Wave Generator - Teensy 3.1

  1. #1
    Senior Member Wozzy's Avatar
    Join Date
    Jan 2013
    Location
    Philadelphia, Pennsylvania USA
    Posts
    354

    Frequency Locked Sine Wave Generator - Teensy 3.1

    I developed this frequency locked sine wave generator to emulate a strain gauge signal from a piece of rotating machinery that has a 1/REV encoder output.
    The program is not very elegant. It consists of a mashup between FreqMeasure and IntervalTimer and Pauls analog output example.
    The machine I need to emulate only goes up to 2000 RPM so 0 to 35 Hz was sufficient for my purpose.
    The resultant output wave is quite nice at these low frequencies.

    I'd love to hear any suggestions for other methods to achieve this in a more efficient way.
    I'm sure the Teensy 3.1 is capable of producing smooth frequency and phase locked sine waves at much higher frequencies.
    I'd actually like to generate a more complicated waveform, but I either need limit my frequency, or live with a steppier sine wave output.

    Click image for larger version. 

Name:	FreqLok.jpg 
Views:	1229 
Size:	124.5 KB 
ID:	2672
    Here's a photo showing the Sine wave locked to the input signal
    (Click here for YouTube video)

    Full code is attached below:
    Code:
    /* Frequency Locked Sine Wave Generator
     * R. Wozniak             09/13/2014
     * Generates an analog sine wave that is locked to the frequency of
     * a digital input pulse - Used to simulate strain gage on rotating
     * machinery with 1/REV encoder signal
     *
     * Only runs on Teensy 3.1 which has a true Digital to Analog Converter
     * Compile for 96 MHz  <Tools/CPU Speed: "96MHZ (overclock)">
     * Good for 0 to 35 Hz
     *
     * Wiring:
     * Squarewave input on Teensy 3.1 Pin3    (approx 3.3V - must be less than 5 V}
     * Analog sinewave output on Teensy 3.1 DAC pin    (0 to 3.3V)
     *
     *
     * FreqMeasure Library - Example with serial output
     * http://www.pjrc.com/teensy/td_libs_FreqMeasure.html
     *
     * This example code is in the public domain.
     */
     
    #include <FreqMeasure.h>
    IntervalTimer myTimer;
    float clock = 95.99899; // Clock Calibration Factor for my teensy 3.1 at 96 MHz
    volatile float period;
    
    float phase = 0.0;
    float twopi = 3.14159265 * 2;
    float amplitude;
    int   steps = 500;  // number of phase angle steps for sinewave generation
                        // a smaller number will here allow to go to higher frequency
                        // but will result in a steppier sine curve.
    float phasestep = twopi/(steps);
    
    void setup() {
      analogWriteResolution(12);
      FreqMeasure.begin();
    }
    
    void loop() {
      if (FreqMeasure.available()) {
          period = FreqMeasure.read()/(clock*steps/2);
          myTimer.end();
          phase = 0.0;
          myTimer.begin(sinewave,period);  // create step sinewave
      }  
    }
    
    void sinewave(void) {
      amplitude = (sin(phase) * 2000.0) + 2050.0;
      //amplitude = (cos(phase) * 2000.0) + 2050.0;
      analogWrite(A14, (int)amplitude);
      phase = phase + phasestep;
      if (phase >= twopi) phase = 0;
    }
    Last edited by Wozzy; 09-14-2014 at 06:42 PM. Reason: typo, update video link

  2. #2
    Senior Member Jp3141's Avatar
    Join Date
    Nov 2012
    Posts
    486
    What's best for you depends on your needs. The 'best' performance (lowest distortion, most accurate frequency) would likely be a PLL (phase locked loop) http://en.wikipedia.org/wiki/Phase-locked_loop. This would continually make (small) adjustments to the sinewave frequency to make it track the reference (1/REV) input.

    Your code seems to reset the sine generator at each new frequency measure input value -- given that calculations are not precise (roundoff error etc), it is probable that if you look closely, there might be some small jumps in the sinewave output at each new input signal. Really what a PLL does is (slowly) modify the frequency of the generator to match the input signal.

    Basically the way to fix this is to modify your loop to something like this (I haven't checked it):

    void loop() {
    if (FreqMeasure.available()) {
    period_new = FreqMeasure.read()/(clock*steps/2);
    myTimer.end();
    // phase = 0.0; // delete this
    period += 0.01*(period_new-period);
    myTimer.begin(sinewave,period); // create step sine wave
    }

    The period += ... line basically slowly modifies the period of the output to match the input period.
    Delete the phase=0.0 line to avoid jumps in the output at each new input.

    Also, in the sinewave() function, replace
    if (phase >= twopi) phase = 0;
    with
    if (phase >= twopi) phase -= twopi;

    This also minimized jumps in the output if it doesn't get reset over a large number of cycles.

  3. #3
    Senior Member Wozzy's Avatar
    Join Date
    Jan 2013
    Location
    Philadelphia, Pennsylvania USA
    Posts
    354
    Jp3141,
    Thanks for the reply.

    Here's a little more background on the purpose for this emulator. I am replacing the operator controls and displays on a large 500hp rotating machine. The displays are critical to operating the machine to allow operator visibility of the condition of the load in order to safely operate the machine. I will be running the machine without the load, and simulating a typical output signal to verify that the new displays work properly prior to actually running with the load. The machine varies in RPM from 0 to 2000, but operates mostly in the 1000 to 2000 RPM range. The RPM is changing continuously, albeit relatively slowly compared to the 1/REV signal.

    I see how the changes you suggested can smooth out the sine wave signal at the transition point, but it is at the expense of losing the phase lock. It's interesting to watch it change to catch up to changes in the input frequency. when it finally matches the frequency of the input signal, the phase is in some random location where it finally matched the frequency. I tried different factors in the line
    period += 0.01*(period_new-period);
    which effects how quickly the output changes to match the input.

    Quote Originally Posted by Jp3141 View Post
    What's best for you depends on your needs. The 'best' performance (lowest distortion, most accurate frequency) would likely be a PLL (phase locked loop) http://en.wikipedia.org/wiki/Phase-locked_loop. This would continually make (small) adjustments to the sinewave frequency to make it track the reference (1/REV) input.
    I hesitated to use the term PLL in my original post because the actual output signal of the rotating machine which is a combination of sine and cosine components, changes phase depending on the operator inputs. So the signal is not truly phase locked, but the absolute position is important for the operator. This is why I reset the Phase to zero with each update to the frequency. it also means that I'll have a small error at the end of the trace when the RPM are changing. The operator display resets each revolution, so this will show up a a small jitter at the very end of the trace.

    I'm now trying for the first time, to increase the overclock the Teensy beyond 96MHz to see if I can gain enough overhead to implement a sine + cosine function that more accurately emulates the output frequency. I struggling a little to understand the clock calibration however.
    More info on overclocking Teensy is here: http://forum.pjrc.com/threads/25755-...lock-to-168MHz

  4. #4
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    3,920

    Cool

    Until Paul releases the Teensy 3.1++ Teensy (or perhaps it will be called Teensy 4.0, Teensy 3.2, or Fuzzy Pink Teensy), I suspect your best bet is to either use a Beagle Bone Black, Rasberry Pi, or a NavSpark, since these machines have hardware floating point (Teensy 3.x has software floating point). In addition the Beagle Bone Black and Rasberry Pi are run at much faster clock rates (Navspark is 100Mhz). Otherwise all of the FP arithmetic in your calculation must be done in software emulation which can take hundreds to thousands of machine instructions to do a single operation. If you are controlling devices, etc. you may need to marry a fast processor for calculating sine waves with a Teensy.

    In addition, since you are using float variables, you should use the cosf and sinf functions. If you use cos and sin, the compiler will probably convert the argument to double and do double arithmetic on it, and then afterwards convert the result back to float. In addition, you shoud add a 'f' suffix to ALL floating point constants to keep all calculations in 32-bit single precision. The original C machines (PDP-7, and later PDP-11) did calculations faster in double precision than single precision, so the C language was written to default to double precision. However, if you don't need the precision, the math functions often are faster if done in single precision. In addition, if you do think about using the next generation of Teensy, I believe it will only have hardware support for single precision floating point.

    Alternatively, you can rewrite the code to use integer approximations for sin/cos.

  5. #5
    Senior Member Jp3141's Avatar
    Join Date
    Nov 2012
    Posts
    486
    The code I suggested basically implemented a Frequency Locked Loop. For a PLL (if you need it), you would actually remove the FrequencyMeasure() stuff and have an interrupt on an (rising ?) edge of the 1/REV signal. Compare the phase of the sine generator at this time with 0, accumulate it, and if the result is positive (i.e. the sine generator is 'ahead'), reduce the frequency:

    @ each input edge:
    phase_integrator += phase of sine generator;
    (period += some_constant * phase_integrator);

    etc.

    It's actually a little more complex than this (calculating the appropriate constants to give reasonable response speed, and adding limits to ensure that it eventually does lock) -- the wikipedia article gives some (Matlab) code that you could start from.

  6. #6
    Senior Member Wozzy's Avatar
    Join Date
    Jan 2013
    Location
    Philadelphia, Pennsylvania USA
    Posts
    354
    Thanks Michael,

    Quote Originally Posted by MichaelMeissner View Post
    In addition, since you are using float variables, you should use the cosf and sinf functions. If you use cos and sin, the compiler will probably convert the argument to double and do double arithmetic on it, and then afterwards convert the result back to float. In addition, you should add a 'f' suffix to ALL floating point constants to keep all calculations in 32-bit single precision.
    This helped to speed things up quite a bit. In fact I'm now am able to synthesize the correct sine-cosine waveform. I'm right on the edge of being able to produce a smooth 500 step sine wave at 33 hz. But that was my goal, and this will get me through my testing tomorrow.

    Here's the updated code if anyone is interested:
    Code:
    /* Frequency Locked Sine Wave Generator - V1.1
     * R. Wozniak             09/14/2014
     * Generates an analog sine wave output that is locket to the frequency of
     * a digital input pulse - Used to simulate strain gage on rotating
     * machinery with 1/REV encoder signal
     *
     * Only runs on Teensy 3.1 which has a true Digital to Analog Converter
     * Compile for 96 MHz  <Tools/CPU Speed: "96MHZ (overclock)">
     * Good for 0 to 35 Hz
     *
     * Wiring:
     * Squarewave input on Teensy 3.1 Pin3  (3.3V - do not exceed 5V)
     * Analog sinewave output on Teensy 3.1 DAC pin (0 to 3.3V sine wave)
     */
     
     /* FreqMeasure Library - Example with serial output
     * http://www.pjrc.com/teensy/td_libs_FreqMeasure.html
     *
     * This example code is in the public domain.
     */
     
    #include <FreqMeasure.h>
    IntervalTimer myTimer;
    
     float clock = 95.99899F;  // Clock Calibration Factor for my teensy 3.1 at 96 MHz <Best>
     float twopi = 6.2831853F;
     float sineFactor = 0.85F;
     float cosineFactor = 0.35F;
     int steps = 500;  // number of phase angle steps for sinewave generation
                            // a smaller number will here allow to go to higher frequency
                            // but will result in a steppier sine curve.
    float amplitude;                        
    float phase = 0.0F;
    float phasestep = twopi/(steps);
    volatile float period;
    
    void setup() {
      analogWriteResolution(12);
      FreqMeasure.begin();
    }
    
    void loop() {
      if (FreqMeasure.available()) {
          period = FreqMeasure.read()/(clock*steps/2);
          myTimer.end();
          phase = 0.0;
          myTimer.begin(sinewave,period);  // create step sinewave
      }  
    }
    
    void sinewave(void) {
      amplitude = (((sineFactor*sinf(phase))+(cosineFactor*cosf(phase)))*2000.0)+2050.0;
      analogWrite(A14, (int)amplitude);
      phase = phase + phasestep;
      if (phase >= twopi) phase = 0;
    }

  7. #7
    Senior Member Wozzy's Avatar
    Join Date
    Jan 2013
    Location
    Philadelphia, Pennsylvania USA
    Posts
    354
    JP3141,
    Thanks
    Quote Originally Posted by Jp3141 View Post
    have an interrupt on an (rising ?) edge of the 1/REV signal. Compare the phase of the sine generator at this time with 0, accumulate it, and if the result is positive (i.e. the sine generator is 'ahead'), reduce the frequency ... It's actually a little more complex than this (calculating the appropriate constants to give reasonable response speed, and adding limits to ensure that it eventually does lock) -- the wikipedia article gives some (Matlab) code that you could start from.
    I was originally going down a path similar to this at first, but since my programming skills are weak, and I only had my spare time on the weekend to try and implement this, I went for the *Mashup Solution*. I'll try to see if I can work up something that works at a lower level, because, I'd really like to speed things up fast enough to upbeat the square signal by 2 or 4 (or more) and synthesize the sine-cosine waveform at the up-beated frequency. I am hoping to one-day create a simulator that is precise enough for training the operators.

    Thanks -- Wozzy

  8. #8
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    3,920

    Cool

    You should still add the 'f' suffix to prevent converting the float to double and back again In addition you should use const where possible to allow the compiler to do constant folding optimizations. I'm indicating the changes in red:

    Code:
    /* Frequency Locked Sine Wave Generator - V1.1
     * R. Wozniak             09/14/2014
     * Generates an analog sine wave output that is locket to the frequency of
     * a digital input pulse - Used to simulate strain gage on rotating
     * machinery with 1/REV encoder signal
     *
     * Only runs on Teensy 3.1 which has a true Digital to Analog Converter
     * Compile for 96 MHz  <Tools/CPU Speed: "96MHZ (overclock)">
     * Good for 0 to 35 Hz
     *
     * Wiring:
     * Squarewave input on Teensy 3.1 Pin3  (3.3V - do not exceed 5V)
     * Analog sinewave output on Teensy 3.1 DAC pin (0 to 3.3V sine wave)
     */
     
     /* FreqMeasure Library - Example with serial output
     * http://www.pjrc.com/teensy/td_libs_FreqMeasure.html
     *
     * This example code is in the public domain.
     */
     
    #include <FreqMeasure.h>
    IntervalTimer myTimer;
    
    const float clock = 95.99899F;  // Clock Calibration Factor for my teensy 3.1 at 96 MHz <Best>
    const float twopi = 6.2831853F;
    const float sineFactor = 0.85F;
    const float cosineFactor = 0.35F;
    const int steps = 500;  // number of phase angle steps for sinewave generation
                                  // a smaller number will here allow to go to higher frequency
                                  // but will result in a steppier sine curve.
    const float clock_steps = (clock * (float)steps * 0.5f);
    float amplitude;                        
    float phase = 0.0F;
    const float phasestep = twopi/((float)steps);
    volatile float period;
    
    void setup() {
      analogWriteResolution(12);
      FreqMeasure.begin();
    }
    
    void loop() {
      if (FreqMeasure.available()) {
          period = ((float)FreqMeasure.read()/clock_steps);
          myTimer.end();
          phase = 0.0f;
          myTimer.begin(sinewave,period);  // create step sinewave
      }  
    }
    
    void sinewave(void) {
      amplitude = (((sineFactor*sinf(phase))+(cosineFactor*cosf(phase)))*2000.0f)+2050.0f;
      analogWrite(A14, (int)amplitude);
      phase = phase + phasestep;
      if (phase >= twopi) phase = 0;
    }
    Last edited by MichaelMeissner; 09-15-2014 at 04:53 AM.

  9. #9
    Member
    Join Date
    Sep 2014
    Location
    California
    Posts
    22
    Since amplitude only seems to have 500 possible values and a Tiny 3. has lots of memory, you could generate them in setup and put them in an array, then

    Code:
    amplitude = (((sineFactor*sinf(phase))+(cosineFactor*cosf(phase)))*2000.0)+2050.0;
    analogWrite(A14, (int)amplitude);
    would become
    Code:
    analogWrite(A14, amplitude[phase]);
    Which would likely be a fair amount faster still

  10. #10
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    3,920
    Good catch, Ira. The OP could do the calculation of amplitude in setup, or even do it on a PC ahead of time, and make amplitude an initialized const array. If the OP calculates it ahead of time and uses const, the compiler will put the values into flash memory instead of SRAM. However, if the OP calculates it ahead of time, he/she will need to redo the calculation each time they adjust the number of steps.

    Since analogWrite only takes values to 12 bits, the type of amplitude only needs to be an unsigned short, which would use 1,000 bytes of SRAM (int on Teensy 3.x is 4 bytes, short and unsigned short are 2 bytes).
    Last edited by MichaelMeissner; 09-15-2014 at 04:19 AM.

  11. #11
    Member
    Join Date
    Sep 2014
    Location
    California
    Posts
    22
    Unless the program gets a whole lot bigger than it is now, not much point in making it a const. Also with the generation in code, it's trivial to test to see if 500 can become something larger enough to significantly improve the output.

    Also, I think you only need 1/2 of the entries as 0-250-0 is I think the same as 0-500.

    Ira

  12. #12
    Senior Member Jp3141's Avatar
    Join Date
    Nov 2012
    Posts
    486
    in addition, this line could be optimized:
    amplitude = (((sineFactor*sinf(phase))+(cosineFactor*cosf(phas e)))*2000.0f)+2050.0f;

    using trig identities -- cos(a-b)=cos(a).cos(b)+sin(a).sin(b). Combine the sineFactor, cosFactor, and 2000.0 to get tan(a)=sinFactor/cosFactor; ==> a=1.18 radian, and the 2000 becomes 2000.sinFactor/sin(a) = 1838; so the line becomes:
    amplitude=cos(phase - 1.18)*1838.f + 2050.f;

    which should compute around 2x faster.

  13. #13
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    3,920
    Quote Originally Posted by Jp3141 View Post
    amplitude=cos(phase - 1.18)*1838.f + 2050.f;

    which should compute around 2x faster.
    To be pedantic, it should be:
    Code:
    amplitude=cosf(phase - 1.18f)*1838.f + 2050.f;
    Though if you are finding yourself doing both sine and cosine operations and you can't reduce it to a single call, there are functions sincos, sincosf, and sincosl that calculate both sine and cosine of the same argument at the same time faster than doing two independent calls. It has the calling signature:
    Code:
    void sincosf(float x, float *sin, float *cos);
    Last edited by MichaelMeissner; 09-15-2014 at 04:53 AM.

  14. #14
    Senior Member Wozzy's Avatar
    Join Date
    Jan 2013
    Location
    Philadelphia, Pennsylvania USA
    Posts
    354
    I don't have any of my teensy stuff at work, but I can't wait to get home to try these ideas out tonight.

    In the end we weren't ready to do the operator display test today, so I might have a few more days to spruce up my program.
    I'm hoping to speed it up enough to allow the sinf and cosf amplitude multipliers to be set on the fly from pots or encoders.
    My instrumentation and data folks are quite impressed with the teensy and that I could throw an emulator together over the weekend.

    I learn so much from you guys on this forum.
    Many many thanks.
    Bob Wozniak (Wozzy)

  15. #15
    Senior Member Wozzy's Avatar
    Join Date
    Jan 2013
    Location
    Philadelphia, Pennsylvania USA
    Posts
    354

    This one Works really well

    I want to thank everyone again.
    With the hints you gave me it's now running over 5 times faster, with finer step resolution, and with the sine and cosine factors set from pots on analog inputs. It's now fast enough that I can try to up beat the signal at least 4 times, and probably to 8 times if i give up a little on the step resolution.

    Fixing all the float variables, gave a small speed increase. from 33 to 35Hz

    Trying the sincosf function didn't speed it up by much, but to me, seemed to be very elegant solution for this application. See code snippet below:
    Code:
    void sinewave(void) {
      sincosf(phase,&sin_phase,&cos_phase);
      amplitude = (((sineFactor*sin_phase)+(cosineFactor*cos_phase))*2000.0f)+2050.0f;
      analogWrite(A14, (int)amplitude);
      phase = phase + phasestep;
      if (phase >= twopi) phase = 0;
    }
    By the way, why do I need the & in front of the cos_phase, and sin_phase variables. I found that in an example, and couldn't get the sincosf function to work otherwise

    Ira's suggestion to use an array containing the precalculated sine values, was by far the speedup king. I considered this option briefly, early on while I was planning this project, but admittedly, I chickened out thinking that it would be too difficult. Given the performance boost, I was also able to increase the resolution from 500 to 800 phase steps. I calculated the sine values for 1.25 revolutions (360deg + 90deg), and just shifted the cosine lookup by 90 degrees for a given phase angle.

    Here's the full working code for the best working version so far.
    Code:
    /* Frequency Locked Sine Wave Generator - V1.2
     * R. Wozniak             09/15/2014
     * Generates an analog sine wave output that is locked to the frequency of
     * a digital input pulse - Used to simulate strain gage on rotating
     * machinery with 1/REV encoder signal
     *
     * Thanks to Ira, MichaelM and Jp3141 at PJRC Forums for help in optimizations
     * for more information, see the thread located here:
     * http://forum.pjrc.com/threads/26608-Frequency-Locked-Sine-Wave-Generator-Teensy-3-1
     *
     * This program only runs on Teensy 3.1 which has a true Digital to Analog Converter
     * Compile for 96 MHz  <Tools/CPU Speed: "96MHZ (overclock)">
     * Good for 0 to 180 Hz
     *
     * Compiled with Arduino 1.0.5-r3 and Teensyduino 1.20-RC2
     *
     * Wiring:
     * Squarewave input on Teensy 3.1 Pin3  (3.3V - do not exceed 5V)
     * Analog sinewave output on Teensy 3.1 DAC pin (0 to 3.3V sine wave)
     */
     
     /* FreqMeasure Library - Example with serial output
     * http://www.pjrc.com/teensy/td_libs_FreqMeasure.html
     *
     * This example code is in the public domain.
     */
     
    #include <FreqMeasure.h>
    IntervalTimer myTimer;
    
     float clock = 95.99899F;    // Clock Calibration Factor for my teensy 3.1 at 96 MHz <Best>
     float twopi = 6.2831853F;
     float sineFactor;
     float cosineFactor;
     int steps = 800;            // number of phase angle steps for sinewave generation
                                 // a smaller number will here allow to go to higher frequency
                                 // but will result in a steppier sine curve.
    float clock_steps = (clock * (float)steps * 0.5f);                        
    float amplitude;                        
    float phase;
    float phasestep = twopi/((float)steps);
    volatile float period;
    
    float amplitudeValue[1000]; //Initialize array 1.25 * steps. 
                                //This is allows both for 2PI radians of sine,
                                //and 2PI radians of cosine shifted + PI/2
    int i;
    
    void setup() {
      analogWriteResolution(12);
      FreqMeasure.begin();
      //Serial.begin(57600);
      fillArray();
    }
    
    void fillArray(){
      for (int i=0; i <= 999; i++){
      phase = (float)i*phasestep;
      amplitudeValue[i]=sin(phase);
      }
      
    /* // Print array to Serial Monitor
      delay (5000);
      for (int i=0; i <= 799; i++){
      phase = (float)i*phasestep;  
      Serial.print(phase,8);
      Serial.print(",  ");
      Serial.print(amplitudeValue[i],8);
      Serial.print(",  ");
      Serial.println(amplitudeValue[i+200],8);
      }
    */
    }
    
    void loop() {
      sineFactor = (float)analogRead(0)/1023.0f;      //get analog value from pots scale to between 0 and 1
      cosineFactor = (float)analogRead(1)/1023.0f;   //clipping will occur if both the sine and cosine factors are set too high
      //Print Sine and Cosine Factors to Serial Monitor
      //Serial.print("sineFactor is: ");
      //Serial.print(sineFactor,8);
      //Serial.print(",   ");
      //Serial.print("cosineFactor is: ");
      //Serial.println(cosineFactor,8);
      
      if (FreqMeasure.available()) {
          period = ((float)FreqMeasure.read()/clock_steps);
          myTimer.end();
          i = 0;
          myTimer.begin(sinewave,period);  // create step sinewave
      }  
    }
    
    void sinewave(void) {
      amplitude = (((sineFactor*amplitudeValue[i])+(cosineFactor*amplitudeValue[i+200]))*2000.0F)+2050.0F;
      analogWrite(A14, (int)amplitude);
      i = i + 1;
      if (i >= 799) i = 0;
    }
    Last edited by Wozzy; 09-16-2014 at 12:54 PM.

  16. #16
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    3,920
    Quote Originally Posted by Wozzy View Post
    By the way, why do I need the & in front of the cos_phase, and sin_phase variables. I found that in an example, and couldn't get the sincosf function to work otherwise
    The simple answer is that is the way C is defined. The C language is defined to pass all arguments by value (which means any change to the variable argument within the function is not copied back to the caller). If you want to return a value from a function, you either have to make it the return value, or you pass in the address of a location to store the result. Since sincos returns two values, it doesn't return anything as a function return, but expects the user to pass two locations to receive the value for the sine and cosine.

    Now, in the C++ language, you can declare a function to pass arguments by reference where the compiler internally passes the address of the variable, i.e.
    Code:
    extern void sincosf_cplusplus (float value, float& sin_value, float& cos_value);
    // ...
    sincosf_cplusplus (x, sin_value, cos_value);
    However, since sincosf comes from the C math library, it must be called in the C style.

  17. #17
    Junior Member
    Join Date
    Oct 2016
    Posts
    6
    Hi,

    today I tried to test the LockedFrequencyGenerator with an old Philips function generator. The unit is pretty old and measured with an Osci the max amplitude Vpp is 2V.
    I connected the function generator to CH1 of my Digital Oscilloscope and the DAC/A14 of the Teensy 3.2 to CH2. After sweeping a bit around nothing happens at the DAC. I thought it was the low Vpp, so I scaled the ADC input with a factor to get amplitude 1. Nothing happens.

    The FreqMeasure Library uses for Teensy 3.0, 3.1, 3.2 Pin 3 and Pin 4 is unusable for analogWrite()- now the analogRead(0) for sin and analogRead(1) for cos is used. Is there any conflict with the library possible? The defined(__MK20DX256__) from FreqMeasureCapture.h should work for Teensy 3.2, too.

    Do you have any ideas what I did wrong?

    Thank you and best regards.
    Last edited by pinf; 12-02-2016 at 06:55 PM.

  18. #18
    Junior Member
    Join Date
    Oct 2016
    Posts
    6
    I got it work. The Problem was the cable at the function generator- never use a oscilloskope-clamp because of the high resistance! A regular cable did the 3V and the frequency was recognized.

  19. #19
    Junior Member
    Join Date
    Feb 2017
    Posts
    15

    digital sine wave,

    Quote Originally Posted by Wozzy View Post
    I developed this frequency locked sine wave generator to emulate a strain gauge signal from a piece of rotating machinery that has a 1/REV encoder output.
    The program is not very elegant. It consists of a mashup between FreqMeasure and IntervalTimer and Pauls analog output example.
    The machine I need to emulate only goes up to 2000 RPM so 0 to 35 Hz was sufficient for my purpose.
    The resultant output wave is quite nice at these low frequencies.

    I'd love to hear any suggestions for other methods to achieve this in a more efficient way.
    I'm sure the Teensy 3.1 is capable of producing smooth frequency and phase locked sine waves at much higher frequencies.
    I'd actually like to generate a more complicated waveform, but I either need limit my frequency, or live with a steppier sine wave output.

    Click image for larger version. 

Name:	FreqLok.jpg 
Views:	1229 
Size:	124.5 KB 
ID:	2672
    Here's a photo showing the Sine wave locked to the input signal
    (Click here for YouTube video)

    Full code is attached below:
    Code:
    /* Frequency Locked Sine Wave Generator
     * R. Wozniak             09/13/2014
     * Generates an analog sine wave that is locked to the frequency of
     * a digital input pulse - Used to simulate strain gage on rotating
     * machinery with 1/REV encoder signal
     *
     * Only runs on Teensy 3.1 which has a true Digital to Analog Converter
     * Compile for 96 MHz  <Tools/CPU Speed: "96MHZ (overclock)">
     * Good for 0 to 35 Hz
     *
     * Wiring:
     * Squarewave input on Teensy 3.1 Pin3    (approx 3.3V - must be less than 5 V}
     * Analog sinewave output on Teensy 3.1 DAC pin    (0 to 3.3V)
     *
     *
     * FreqMeasure Library - Example with serial output
     * http://www.pjrc.com/teensy/td_libs_FreqMeasure.html
     *
     * This example code is in the public domain.
     */
     
    #include <FreqMeasure.h>
    IntervalTimer myTimer;
    float clock = 95.99899; // Clock Calibration Factor for my teensy 3.1 at 96 MHz
    volatile float period;
    
    float phase = 0.0;
    float twopi = 3.14159265 * 2;
    float amplitude;
    int   steps = 500;  // number of phase angle steps for sinewave generation
                        // a smaller number will here allow to go to higher frequency
                        // but will result in a steppier sine curve.
    float phasestep = twopi/(steps);
    
    void setup() {
      analogWriteResolution(12);
      FreqMeasure.begin();
    }
    
    void loop() {
      if (FreqMeasure.available()) {
          period = FreqMeasure.read()/(clock*steps/2);
          myTimer.end();
          phase = 0.0;
          myTimer.begin(sinewave,period);  // create step sinewave
      }  
    }
    
    void sinewave(void) {
      amplitude = (sin(phase) * 2000.0) + 2050.0;
      //amplitude = (cos(phase) * 2000.0) + 2050.0;
      analogWrite(A14, (int)amplitude);
      phase = phase + phasestep;
      if (phase >= twopi) phase = 0;
    }
    Hello, is the reference to "Only runs on Teensy 3.1 which has a true Digital to Analog Converter" still relevant or will this run on teensy 3.2 or could this code be used with the addition of the PT2811 digital to analog converter I.C? Many Thanks

  20. #20
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,377
    That's old info. Here are the updated specs:

    https://www.pjrc.com/teensy/techspecs.html

  21. #21
    Junior Member
    Join Date
    Feb 2017
    Posts
    15

    teensy sine wave library

    Quote Originally Posted by PaulStoffregen View Post
    That's old info. Here are the updated specs:

    https://www.pjrc.com/teensy/techspecs.html
    Hi Paul, Thanks for that, could you point me in the direction to a sine wave library that allows multiple frequency, with adjustable duty cycle and programmable amplitude......I don't ask for much!!, got one thread that mentions "Sinwave" but does not indicate the origin, Thanks again

  22. #22
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,377
    Quote Originally Posted by private 1 View Post
    could you point me in the direction to a sine wave library that allows multiple frequency, with adjustable duty cycle and programmable amplitude......
    Perhaps use the Teensy Audio Library?

    https://www.pjrc.com/teensy/td_libs_Audio.html

    You can use the design tool to create as many sine waves as you need (well, within CPU and memory limits). All are independently controllable in frequency, amplitude and phase offset.

    If you're not familiar with the audio lib, I recommend watching the tutorial video, or even better doing the actual tutorial with the PDF printed. In the video, watch the part about using the design tool (starts at 6:59) and maybe skip to the part about oscillators (starts at 25:04).

    I don't ask for much!!
    Well, really it's not much (sine waves are pretty easy), except perhaps "adjustable duty cycle". I have no idea what that means in the context of a sine wave!

    But if you do wish to create strangely shaped waveforms, perhaps similar in shape to sine by highly distorted in some adjustable way, look at the arbitrary waveform capability here: (docs on right-side panel)

    https://www.pjrc.com/teensy/gui/?inf...oSynthWaveform

  23. #23
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    3,920
    Quote Originally Posted by PaulStoffregen View Post
    That's old info. Here are the updated specs:

    https://www.pjrc.com/teensy/techspecs.html
    Yeah, I know the info is obsolete, but it is where I grabbed the simple sine wave example from.

  24. #24
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    3,920
    Quote Originally Posted by PaulStoffregen View Post
    Perhaps use the Teensy Audio Library?

    https://www.pjrc.com/teensy/td_libs_Audio.html
    I didn't mention the Audio library because the OP explicitly said he was thinking about using the LC, and I thought the Audio library used Arm M4 instructions.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •