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:
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);
}