finnandreh
Member
I'm working with a teensy 4.1 and will operate a led module that has a maximum input of pwm frequency at 1khz, And need to modulate a square wave at 110 hz, The problem im facing is that the led fluctualte with a 1 second pulse. A little bit up and down. Its not stable, before i get up to 2khz(Testing with a regular Led). When using a arduino uno where i use interrupt to set hardware interrupt i get stable, and wonder if its possible to achiive the same kind of thing, and also if somebody can explain why this is happening. I will share the code below. Its a test program that you can set vakues in the serial monitor live.
Hope somebody has a good answer to this. I've also tried using the micros function and that works even worse.
Thanks
Best regards
Finn Andre Hotvedt
#include <Arduino.h>
#include <IntervalTimer.h>
// ----- Global Parameters (set via Serial commands) -----
const int pwmPin = 2; // PWM output pin (adjust as needed)
float pwmFreq = 1000; // Default PWM frequency in Hz
int dutyPercent = 50; // Default duty cycle (0–100%)
float modFreq = 111; // Default modulation frequency in Hz (0 = no modulation)
int amplitude = 100; // Default amplitude (0–100%)
// Derived variables (calculated from duty and amplitude)
// These are used by the timer interrupt to update the output.
volatile int pwmValue = 0;
volatile bool modState = false; // toggles in the interrupt
IntervalTimer modTimer; // Timer used for modulation updates
// This function is called by the timer interrupt.
void updatePwmOutput() {
// Toggle the modulation state
modState = !modState;
if (modFreq > 0) {
// During the "high" phase output the PWM value; else, off.
analogWrite(pwmPin, modState ? pwmValue : 0);
} else {
// No modulation: always output the PWM value.
analogWrite(pwmPin, pwmValue);
}
}
// (Re)start the modulation timer with a period based on modFreq.
// If modFreq == 0, the timer is stopped.
void updateModTimer() {
// Stop any running timer
modTimer.end();
if (modFreq > 0) {
// Calculate half period (in microseconds) for a square wave modulation.
unsigned long halfPeriod = 1000000UL / (2 * modFreq);
modTimer.begin(updatePwmOutput, halfPeriod);
} else {
// With no modulation, ensure the output remains constant.
analogWrite(pwmPin, pwmValue);
}
}
// Recalculate the PWM output value from dutyPercent and amplitude.
void updatePwmValue() {
int raw = (dutyPercent * 4095) / 100; // 12-bit PWM value based on duty
pwmValue = (raw * amplitude) / 100; // Scale by amplitude percentage
}
void setup() {
Serial.begin(115200);
while (!Serial) ; // Wait for serial monitor
pinMode(pwmPin, OUTPUT);
// Set the PWM frequency on pwmPin.
analogWriteFrequency(pwmPin, pwmFreq);
updatePwmValue();
// Start the modulation timer if modulation is enabled.
updateModTimer();
Serial.println(F("Enter commands in one of these formats:"));
Serial.println(F(" PWM <pwmFreq> <dutyPercent> <modFreq> <amp>"));
Serial.println(F(" e.g., PWM 900 50 111 80"));
Serial.println(F(" DUTY <dutyPercent>"));
Serial.println(F(" e.g., DUTY 80"));
Serial.println(F(" AMP <amp>"));
Serial.println(F(" e.g., AMP 90"));
Serial.println();
}
void loop() {
// Check for Serial input and parse commands.
if (Serial.available()) {
String command = Serial.readStringUntil('\n');
command.trim();
if (command.startsWith("PWM")) {
int firstSpace = command.indexOf(' ');
if (firstSpace > 0) {
String params = command.substring(firstSpace + 1);
params.trim();
int space1 = params.indexOf(' ');
int space2 = params.indexOf(' ', space1 + 1);
int space3 = params.indexOf(' ', space2 + 1);
if (space1 > 0 && space2 > 0 && space3 > 0) {
String freqStr = params.substring(0, space1);
String dutyStr = params.substring(space1 + 1, space2);
String modStr = params.substring(space2 + 1, space3);
String ampStr = params.substring(space3 + 1);
float newPwmFreq = freqStr.toFloat();
int newDuty = dutyStr.toInt();
float newModFreq = modStr.toFloat();
int newAmp = ampStr.toInt();
if (newPwmFreq > 0 && newDuty >= 0 && newDuty <= 100 &&
newModFreq >= 0 && newAmp >= 0 && newAmp <= 100) {
pwmFreq = newPwmFreq;
dutyPercent = newDuty;
modFreq = newModFreq;
amplitude = newAmp;
// Update PWM frequency.
analogWriteFrequency(pwmPin, pwmFreq);
updatePwmValue();
updateModTimer();
Serial.print(F("Updated: PWM Freq = "));
Serial.print(pwmFreq);
Serial.print(F(" Hz, Duty = "));
Serial.print(dutyPercent);
Serial.print(F("%, Mod Freq = "));
Serial.print(modFreq);
Serial.print(F(" Hz, Amp = "));
Serial.print(amplitude);
Serial.println(F("%"));
} else {
Serial.println(F("Invalid parameters. Check: pwmFreq > 0, duty & amp 0-100, modFreq >= 0."));
}
} else {
Serial.println(F("Invalid format. Use: PWM <pwmFreq> <dutyPercent> <modFreq> <amp>"));
}
}
}
else if (command.startsWith("DUTY")) {
int firstSpace = command.indexOf(' ');
if (firstSpace > 0) {
String dutyStr = command.substring(firstSpace + 1);
dutyStr.trim();
int newDuty = dutyStr.toInt();
if (newDuty >= 0 && newDuty <= 100) {
dutyPercent = newDuty;
updatePwmValue();
Serial.print(F("Updated Duty: "));
Serial.print(dutyPercent);
Serial.println(F("%"));
} else {
Serial.println(F("Invalid duty cycle. Must be 0-100."));
}
}
}
else if (command.startsWith("AMP")) {
int firstSpace = command.indexOf(' ');
if (firstSpace > 0) {
String ampStr = command.substring(firstSpace + 1);
ampStr.trim();
int newAmp = ampStr.toInt();
if (newAmp >= 0 && newAmp <= 100) {
amplitude = newAmp;
updatePwmValue();
Serial.print(F("Updated Amplitude: "));
Serial.print(amplitude);
Serial.println(F("%"));
} else {
Serial.println(F("Invalid amplitude. Must be 0-100."));
}
}
}
}
}
Hope somebody has a good answer to this. I've also tried using the micros function and that works even worse.
Thanks
Best regards
Finn Andre Hotvedt
#include <Arduino.h>
#include <IntervalTimer.h>
// ----- Global Parameters (set via Serial commands) -----
const int pwmPin = 2; // PWM output pin (adjust as needed)
float pwmFreq = 1000; // Default PWM frequency in Hz
int dutyPercent = 50; // Default duty cycle (0–100%)
float modFreq = 111; // Default modulation frequency in Hz (0 = no modulation)
int amplitude = 100; // Default amplitude (0–100%)
// Derived variables (calculated from duty and amplitude)
// These are used by the timer interrupt to update the output.
volatile int pwmValue = 0;
volatile bool modState = false; // toggles in the interrupt
IntervalTimer modTimer; // Timer used for modulation updates
// This function is called by the timer interrupt.
void updatePwmOutput() {
// Toggle the modulation state
modState = !modState;
if (modFreq > 0) {
// During the "high" phase output the PWM value; else, off.
analogWrite(pwmPin, modState ? pwmValue : 0);
} else {
// No modulation: always output the PWM value.
analogWrite(pwmPin, pwmValue);
}
}
// (Re)start the modulation timer with a period based on modFreq.
// If modFreq == 0, the timer is stopped.
void updateModTimer() {
// Stop any running timer
modTimer.end();
if (modFreq > 0) {
// Calculate half period (in microseconds) for a square wave modulation.
unsigned long halfPeriod = 1000000UL / (2 * modFreq);
modTimer.begin(updatePwmOutput, halfPeriod);
} else {
// With no modulation, ensure the output remains constant.
analogWrite(pwmPin, pwmValue);
}
}
// Recalculate the PWM output value from dutyPercent and amplitude.
void updatePwmValue() {
int raw = (dutyPercent * 4095) / 100; // 12-bit PWM value based on duty
pwmValue = (raw * amplitude) / 100; // Scale by amplitude percentage
}
void setup() {
Serial.begin(115200);
while (!Serial) ; // Wait for serial monitor
pinMode(pwmPin, OUTPUT);
// Set the PWM frequency on pwmPin.
analogWriteFrequency(pwmPin, pwmFreq);
updatePwmValue();
// Start the modulation timer if modulation is enabled.
updateModTimer();
Serial.println(F("Enter commands in one of these formats:"));
Serial.println(F(" PWM <pwmFreq> <dutyPercent> <modFreq> <amp>"));
Serial.println(F(" e.g., PWM 900 50 111 80"));
Serial.println(F(" DUTY <dutyPercent>"));
Serial.println(F(" e.g., DUTY 80"));
Serial.println(F(" AMP <amp>"));
Serial.println(F(" e.g., AMP 90"));
Serial.println();
}
void loop() {
// Check for Serial input and parse commands.
if (Serial.available()) {
String command = Serial.readStringUntil('\n');
command.trim();
if (command.startsWith("PWM")) {
int firstSpace = command.indexOf(' ');
if (firstSpace > 0) {
String params = command.substring(firstSpace + 1);
params.trim();
int space1 = params.indexOf(' ');
int space2 = params.indexOf(' ', space1 + 1);
int space3 = params.indexOf(' ', space2 + 1);
if (space1 > 0 && space2 > 0 && space3 > 0) {
String freqStr = params.substring(0, space1);
String dutyStr = params.substring(space1 + 1, space2);
String modStr = params.substring(space2 + 1, space3);
String ampStr = params.substring(space3 + 1);
float newPwmFreq = freqStr.toFloat();
int newDuty = dutyStr.toInt();
float newModFreq = modStr.toFloat();
int newAmp = ampStr.toInt();
if (newPwmFreq > 0 && newDuty >= 0 && newDuty <= 100 &&
newModFreq >= 0 && newAmp >= 0 && newAmp <= 100) {
pwmFreq = newPwmFreq;
dutyPercent = newDuty;
modFreq = newModFreq;
amplitude = newAmp;
// Update PWM frequency.
analogWriteFrequency(pwmPin, pwmFreq);
updatePwmValue();
updateModTimer();
Serial.print(F("Updated: PWM Freq = "));
Serial.print(pwmFreq);
Serial.print(F(" Hz, Duty = "));
Serial.print(dutyPercent);
Serial.print(F("%, Mod Freq = "));
Serial.print(modFreq);
Serial.print(F(" Hz, Amp = "));
Serial.print(amplitude);
Serial.println(F("%"));
} else {
Serial.println(F("Invalid parameters. Check: pwmFreq > 0, duty & amp 0-100, modFreq >= 0."));
}
} else {
Serial.println(F("Invalid format. Use: PWM <pwmFreq> <dutyPercent> <modFreq> <amp>"));
}
}
}
else if (command.startsWith("DUTY")) {
int firstSpace = command.indexOf(' ');
if (firstSpace > 0) {
String dutyStr = command.substring(firstSpace + 1);
dutyStr.trim();
int newDuty = dutyStr.toInt();
if (newDuty >= 0 && newDuty <= 100) {
dutyPercent = newDuty;
updatePwmValue();
Serial.print(F("Updated Duty: "));
Serial.print(dutyPercent);
Serial.println(F("%"));
} else {
Serial.println(F("Invalid duty cycle. Must be 0-100."));
}
}
}
else if (command.startsWith("AMP")) {
int firstSpace = command.indexOf(' ');
if (firstSpace > 0) {
String ampStr = command.substring(firstSpace + 1);
ampStr.trim();
int newAmp = ampStr.toInt();
if (newAmp >= 0 && newAmp <= 100) {
amplitude = newAmp;
updatePwmValue();
Serial.print(F("Updated Amplitude: "));
Serial.print(amplitude);
Serial.println(F("%"));
} else {
Serial.println(F("Invalid amplitude. Must be 0-100."));
}
}
}
}
}