Teensy 3.2 PWM Frequency & Duty Cycle changes / shifts / varies / drifts / drops out

Status
Not open for further replies.

tazva

Member
Teensy 3.2 PWM Frequency & Duty Cycle changes / shifts / varies / drifts / drops out

Hey all, new to the Teensy, not coding or other stuff. I'm prototyping a project which will use multiple PWM channels controlled by Serial input. I kermudgeoned several bits of code together quickly just for some testing purposes.

Now, I don't have access to any good testing equipment where I'm at, and that won't change anytime soon. I've got a basic DMM with Frequency & Duty cycle, which I've used on other electronics with good-enough accuracy. What led me to even check the output with my DMM was that I'd notice PWM would "get wonky", and didn't seem "right".

For instance, if I command 50% duty cycle at the beginning, I'm seeing it start around 50%, but over about 2 seconds, it shifts to 47.2%. If I go to 25% duty cycle, it starts around 25% but drifts upward into the 30% range. This happens to almost any DC% I command. If I call for 0, or 100, they shift (appropriately), to 0% (<1mv/dc) and 0% (3.1v/dc) respectively. That makes sense for those "end points". The drifting between boggles me.

Also, the frequency drifts. I'm commanding 25000hz, and the readout is 24999 from my meter. Good enough. Now, again, if I play with ranges like 25..50..60..80.. frequency is stable. Push it to 88-99, or something really low, and precisely 4 seconds after commanding the PWM, the frequency drifts all over creation.. 1khz...80khz...etc, and then eventually goes to 0hz. It does this everytime, and each time, the frequency lottery kicks in precisely at 4 seconds.

I have no idea why.. and maybe I am misunderstanding how this all works on the teensy platform. Would love some help. The full code to my test project is below. I'm using pin 10 in the code, but I have tried other pins with the same result. To use it, you connect via serial, and simply issue this command through serial:

<t,10,DC%>

DC% = duty cycle, 0...100. The other parameters are unused at present for this test. Example usage:

<t,10,50> = 50%

Code:

Code:
// Receive with start- and end-markers combined with parsing
const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use by strtok() function

// variables to hold the parsed data
char messageFromPC[numChars] = {0};
int integerFromPC = 0;
float floatFromPC = 0.0;

boolean newData = false;

// channel info
int outPin = 10;
int outPwm = 0; //off
float inPwm = 0.0;
int fanCh = 10; //set default

// pwm settings
int frq = 25000;
int res = 11;

//============

void setup() {
    // use later
    Serial.begin(9600);
    Serial.println("");
    Serial.println();
    
    //setup output
    pinMode(outPin, OUTPUT);

    //setup pwm
    analogWriteFrequency(outPin,frq);
    analogWriteResolution(res);  //ref: https://www.pjrc.com/teensy/td_pulse.html 
    
}

//============
void pwmSet() {

//  messageFromPC
  fanCh=integerFromPC;
  inPwm=floatFromPC;

  // for testing
  switch(res) {
    case 7:
    outPwm = map(inPwm,0,100,0,127);
    break;
    case 8:
    outPwm = map(inPwm,0,100,0,255);
    break;
    case 9:
    outPwm = map(inPwm,0,100,0,511);
    break;
    case 10:
    outPwm = map(inPwm,0,100,0,1023);
    break;
    case 11:
    outPwm = map(inPwm,0,100,0,2047);
    break;
    case 12:
    outPwm = map(inPwm,0,100,0,4095);
    break;
    
  }
  
  // using outPin for testing
  analogWrite(outPin,outPwm);
  //analogWrite(fanCh,outPwm);

  // debug
  Serial.print("Wrote Pin:");
  Serial.println(outPin);
  Serial.print("Wrote PWM");
  Serial.println(outPwm);
  
}

//============

void loop() {
    recvWithStartEndMarkers();
    if (newData == true) { 
        strcpy(tempChars, receivedChars);
            // this temporary copy is necessary to protect the original data
            //   because strtok() replaces the commas with \0
        parseData();
        pwmSet(); 
        showParsedData();
        newData = false;
    }
}

//============

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;

    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

//============

void parseData() {

      // split the data into its parts
    char * strtokIndx; // this is used by strtok() as an index

    strtokIndx = strtok(tempChars,",");      // get the first part - the string
    strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
  
    strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
    integerFromPC = atoi(strtokIndx);     // convert this part to an integer

    strtokIndx = strtok(NULL, ",");
    floatFromPC = atof(strtokIndx);     // convert this part to a float

}

//============

void showParsedData() {
    Serial.print("Message ");
    Serial.println(messageFromPC);
    Serial.print("Integer ");
    Serial.println(integerFromPC);
    Serial.print("Float ");
    Serial.println(floatFromPC);
    Serial.println(outPwm);
}
 
Running it here, measuring with a Fluke 87 multimeter and Keysight DSOX 4024A oscilloscope. Looks very stable. No changes after many minutes. Probably is very likely a limitation in your multimeter.

DSC_0112_web.jpg

file.png

sc.png
 
Thanks for testing & showing the pics. Well even if that is the case, it's not running the fans properly. I use the output to operate 4 pin PWM fans. Raspberry Pi is doing it properly, the Teensy isn't. The fan RPMs go bonkers at 4 seconds, and RPM varies just like the meter shows.

Possible my teensy is just bust? I can get another one.
 
Maybe something else is wrong? I could try another test next week (traveling this weekend), but before I hook up more hardware, would be good to know what you're really doing so I can wire up the same thing. Maybe post some photos and details about the actual stuff you're using?
 
Are you watching serial output on Teensy monitor and are sure you are not getting any garbage from serial port? strcpy() and untested strtok() make me nervous. :D

if i send a string of <nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn> then it sets duty to 0
Code:
Wrote Pin:10
Wrote PWM0
Message nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
Integer 0
Float 0.00
0

and since you don't mess with frequency in sketch, I can't see how PWM frequency would change during the run. Like Paul, looks good on scope for me as well. <test,1234,50.0>
Code:
Wrote Pin:10
Wrote PWM1023
Message tome
Integer 1234
Float 50.00
1023
 
Thanks for the replies guys. I do wish I had a proper scope or something, even USB, all I've got with me is my DMM. Working with this a bit today, it's still not quite right, at least when compared to using a Pi to output the clock.

The end goal, I'm using the teensy to control 4 pin computer fans (a bunch, and the Pi can only do like 2). Right now I'm using some Arctic F9 PWM's. Their control line seems to lack an internal pullup of any kind (5v or 3.3v), but the fans do respond beautifully to PWM control, so I added a pullup using a 3.3v zener. Attached is a crude drawing of what I'm doing for the test. I used some online sketcher as I don't have anything else.

The left side is the fan. Power source is 12v, all grounds are tied together. The transistor is actually a BC337. Far right is the Teensy's PWM input into Q1. Basically sinking Pin 4 on the fan controls the fan's BJT, which controls the speed. Teensy is connected to a computer via USB (hence powered), and I use Putty to control the serial, just pasting my commands over into the window. Again, all grounds tied.

I was just having issues because sometimes the rpm would just go bonkers, or not respond properly, and when I checked output with my meter, I saw the issues states previously. Also posted below is a snippet of code, I sent you a previous version. Only difference is the mapping is inverted.

In doing some testing today, running from 50->97, my meter shows 24999hz. When I hit 98, it reports 24445-24455hz. If I go to 99 it reports 0hz. It's already been at max anyhow since 90, I'm just keeping track. So that's not the real issue. Being able to stop the fans properly is.

If I run down, I'm good to 13. Once I hit 12, reported frequency goes crazy, anywhere from 1000hz to 57000hz. If I step it to 11, it goes to 0hz. I can keep going down and slowing the fan down, but it never stops (should've stopped at 10). Fan control (ability to keep lowering RPM) is lost if I go below 6, at which point the fan shoots to full speed. With the Pi, if I step from normal speeds, down to around 10-15, the fan will slow down and then simply 'halt', as it's supposed to. Then to get it going again you'd set commanded to 20-25 and it would rev back up. Issue is I planned to use the Teensy to control 10 fans, and the Pi can only do 2.

It's entirely possible I've mucked something up, or made it more complex than necessary. I'm open to suggestions. I'm at the point where I almost just want to use the transistors to control the fan's 12v line and skip the Control line entirely. Hopefully my post makes sense?

teensy_fan_pwm_control_minus_lpf.jpg

Code:
//============
void pwmSet() {

//  messageFromPC
  fanCh=integerFromPC;
  inPwm=floatFromPC;

  // for testing
  switch(res) {
    case 7:
    outPwm = map(inPwm,0,100,127,0);
    break;
    case 8:
    outPwm = map(inPwm,0,100,255,0);
    break;
    case 9:
    outPwm = map(inPwm,0,100,511,0);
    break;
    case 10:
    outPwm = map(inPwm,0,100,1023,0);
    break;
    case 11:
    outPwm = map(inPwm,0,100,2047,0);
    break;
    case 12:
    outPwm = map(inPwm,0,100,4095,0);
    break;
    
  }
 
I'm sure the PWM signal from Teensy is correct. Perhaps something about that circuit is messing up the signal? Maybe the diode?
 
The only thing I can add, which may or may not be helpful, is I wouldn't assume the meter can properly detect square wave frequency.

I have a Fluke meter that I use at work, one night while trying to diagnose an intermittently failing cab blower motor in a Freightliner (this model used solid-state HVAC control) I saw the carrier frequency as only being at spec on the lowest speed setting. Above that it was indistinguishable. I diagnosed this as a bad HVAC module and backlogged the repair. Long story short, the new part did not fix the issue and we learned that a co-workers Snap-On meter read a normal carrier frequency on all speed settings.

I brought my meter home and tested it using a scope, and found that the voltage needs to go negative for the Fluke meter to read the frequency unless the duty cycle is very low.

If the fans are doing weird things alongside the abnormal readings returned by the meter, there's surely a real issue, but there you go.
 
I'm sure the PWM signal from Teensy is correct. Perhaps something about that circuit is messing up the signal? Maybe the diode?

Could be, it's a new addition to ensure forced-compliance with the Intel 1.3 PWM fan spec, and keep from having to sink 3-12v. I will bypass it in my next round of testing, and see what happens.

The only thing I can add, which may or may not be helpful, is I wouldn't assume the meter can properly detect square wave frequency.

I have a Fluke meter that I use at work, one night while trying to diagnose an intermittently failing cab blower motor in a Freightliner (this model used solid-state HVAC control) I saw the carrier frequency as only being at spec on the lowest speed setting. Above that it was indistinguishable. I diagnosed this as a bad HVAC module and backlogged the repair. Long story short, the new part did not fix the issue and we learned that a co-workers Snap-On meter read a normal carrier frequency on all speed settings.

I brought my meter home and tested it using a scope, and found that the voltage needs to go negative for the Fluke meter to read the frequency unless the duty cycle is very low.

If the fans are doing weird things alongside the abnormal readings returned by the meter, there's surely a real issue, but there you go.

Great input, I will say my DMM is likely bugger for precision work like this. I was using it, along with an LED flashlight with adjustable modulation, to allow me to confirm the fan blades were changing speed in relation to teensy output. I've verified my tachometer circuitry is working well, and my RPM readings are within +/- 50 of available spec sheets for the fan, which is acceptable to me.

I've got a new in the bag teensy 3.2 I'm going to swap in place later today to see if that makes any difference. It's possible, having used my current teensy for lots of prototyping, I've messed something up. Definitely just want to find the best solution for using the PWM control line, regardless of what the issue may be.

I'll post up more results as I go along, for anyone who may benefit from this.
 
I had some time to do more testing.

First, I bypassed the zener pull-up, using only the transistor to sink the control line. I tested 3 different brand/model fans out. They all worked as equally well as normal. I tried the Arctic out, it reacted as before: worked perfectly, gets down to ~450rpm, but won't ever "stop" like it's supposed to. I may end up removing the zener pull-up altogether since performance wasn't an issue either way.

I popped the new Teensy in place to give it a whirl. The only noticeable change in operation is that the RPMs were about 25-30/rpm closer to spec for all the fans, which just seems to showcase simple variance by nature (in my mind).

Haven't had a chance to pop my Pi back in place to check it out, I hope to do that this week.
 
Status
Not open for further replies.
Back
Top