Hi all, I am trying to generate two 180-degree phase-shifted PWM waves. However, my proposed code's wave time period is off by a factor of 8. Also, the code abruptly stops responding for frequencies greater than 200 kHz (Frequency index >7).
My code:
// Define pins for output
const int PIN_9 = 9;
const int PIN_10 = 10;
// Define arrays of frequency and duty cycle values
unsigned long FREQUENCY_VALUES[] = {40000, 50000, 60000, 70000, 80000, 90000, 100000, 200000, 300000, 400000, 500000}; // 100000, 200000, 300000, 400000, 500000, 600000, 700000, 800000, 900000};
unsigned long DUTY_CYCLE_VALUES[] = {10, 20, 30, 40, 50, 60, 70, 80, 90};
// Define the number of frequency and duty cycle values
const int NUM_FREQUENCY_VALUES = sizeof(FREQUENCY_VALUES) / sizeof(FREQUENCY_VALUES[0]);
const int NUM_DUTY_CYCLE_VALUES = sizeof(DUTY_CYCLE_VALUES) / sizeof(DUTY_CYCLE_VALUES[0]);
// Define variables for the frequency and duty cycle index
int frequencyIndex = 1;
int dutyCycleIndex = 4;
// Define variables for the high time and period time in timer cycles
unsigned long periodTime = 0; // duration of a full signal turn
unsigned long highTime = 0; // duration (pulse width) or hight time of the leading pin as fraction of the periodTime
unsigned long lowTime = 0; // duration or low time of the leading pin as fraction of the periodTime
// Define a lookup table for the high and period time in timer cycles
unsigned long timerValues[NUM_FREQUENCY_VALUES][NUM_DUTY_CYCLE_VALUES][2];
// Define a function to calculate the high and period time in timer cycles
void calculateTimerValues(unsigned long frequency, unsigned long dutyCycle, unsigned long& highTime, unsigned long& lowTime) {
// Calculate the period time in timer cycles
periodTime = (unsigned long)(16000000.0 / (unsigned long)(frequency));
// Calculate the high time in timer cycles
highTime = (unsigned long)(periodTime * (unsigned long)(dutyCycle) / 800.0);
// Calculate the low time in timer cylces
lowTime = (unsigned long)(periodTime * (unsigned long)(100 - dutyCycle) / 800);
}
// Set up timer1 to generate interrupt at desired frequency and duty cycle
void setupTimerInterrupt() {
noInterrupts();
// Timer/Counter Control Register TCCRnA
TCCR1A = 0;
// Timer/Counter Control Register TCCRnB
TCCR1B = 0;
TCNT1 = 0;
// Select initial low time value depending on index selected.
OCR1A = timerValues[frequencyIndex][dutyCycleIndex][0];
// Waveform Generation Mode bits (WGM)
TCCR1B |= (1 << WGM12);
// Clock Select bits (CS)
// This should set the prescaler of timer1 to 1, i.e., the highest possible frequency of 16 MHz
TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10);
// E.g. this code line sets the prescaler to 8, i.e., the frequency would be 2 MHz -> TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10);
TIMSK1 |= (1 << OCIE1A);
interrupts();
}
// ISR for timer1 compare match A interrupt
ISR(TIMER1_COMPA_vect) {
// pin_phase = false; // current phase (false = 0 degrees, true = 180 degrees)
static bool phase = false; // current phase (false = 0 degrees, true = 180 degrees)
uint16_t highTime = (uint16_t)(timerValues[frequencyIndex][dutyCycleIndex][0]); // get high time based on duty cycle
uint16_t lowTime = (uint16_t)timerValues[frequencyIndex][dutyCycleIndex][1]; // get low time based on high time and waveform period
// Serial.print("ISR highTime: ");
// Serial.println(highTime);
// Serial.print("ISR lowTime: ");
// Serial.println(lowTime);
// Toggle the output pins based on phase
digitalWrite(PIN_9, phase ? HIGH : LOW);
digitalWrite(PIN_10, !phase ? HIGH : LOW);
phase = !phase; // toggle pin phase
// Update the OCR1A register with the next high or low time value
OCR1A = phase ? highTime : lowTime;
}
// Setup function
void setup() {
// Initialize the output pins
pinMode(PIN_9, OUTPUT);
pinMode(PIN_10, OUTPUT);
// Calculate the timer values for each frequency and duty cycle value and store them in the lookup table
for (int i = 0; i < NUM_FREQUENCY_VALUES; i++) {
for (int j = 0; j < NUM_DUTY_CYCLE_VALUES; j++) {
calculateTimerValues(FREQUENCY_VALUES[i], DUTY_CYCLE_VALUES[j], timerValues[i][j][0], timerValues[i][j][1]);}
}
// Set up the timer interrupt with the initial frequency and duty cycle values
setupTimerInterrupt();
// Initialize the serial communication
Serial.begin(9600);
// Make sure the Serial monitor is up and running to avoid strange behavior later.
delay(10000);
}
// Loop function
void loop() {
// Prompt the user to enter a new frequency index
Serial.println("Enter a new frequency index (0-4): ");
// Read the input frequency index
while (!Serial.available()); // wait for user input
String input = Serial.readStringUntil('\n'); // read string value from serial monitor
int newFrequencyIndex = input.toInt();
Serial.println();
// Prompt the user to enter a new duty cycle high time value:
Serial.println("Enter a new duty cycle index (0-4): ");
// Read the input duty cycle index
while (!Serial.available()); // wait for user input
input = Serial.readStringUntil('\n'); // read string value from serial monitor
int newDutyCycleIndex = input.toInt();
Serial.println();
// Check if the input values are within the valid range
if (newFrequencyIndex >= 0 && newFrequencyIndex < NUM_FREQUENCY_VALUES && newDutyCycleIndex >= 0 && newDutyCycleIndex < NUM_DUTY_CYCLE_VALUES) {
// Update the frequency and duty cycle index
frequencyIndex = newFrequencyIndex;
dutyCycleIndex = newDutyCycleIndex;
.
// Print the new frequency, high time, low time, and total period time to the serial monitor
double highTimeInMs = (double)(timerValues[frequencyIndex][dutyCycleIndex][0]) / 16000000.0 * 1000.0;
double lowTimeInMs = (double)(timerValues[frequencyIndex][dutyCycleIndex][1]) / 16000000.0 * 1000.0;
double periodTimeInMs = highTimeInMs + lowTimeInMs;
Serial.print("Frequency: ");
Serial.print(FREQUENCY_VALUES[frequencyIndex]);
Serial.print(" Hz, High Time: ");
Serial.print(highTimeInMs, 5);
Serial.print(" ms, Low Time: ");
Serial.print(lowTimeInMs, 5);
Serial.print(" ms, Period Time: ");
Serial.print(periodTimeInMs, 5);
Serial.println(" ms");
}
} // END void loop()