I have a set of duty cycles of a 400 hz pulse train in microseconds and I want to use internal hardware pwm instead of bit banging. How do I do that?
Most, if not all of what you need to know is in this excellent timer tutorial, with plenty of examples. PWM is toward the end.
Can you help me check the code? I am trying to do the following objective:
write a hardware pwm for a 400 hz pulse train in arduino uno, I have an array of duty cycles in microseconds which will be sent as a 400 hz pwm signal and the duty cycles will only be updated every 4 ms/250 hz
This is the code, I need this to be accurate that's why I am using two timers
// Array of duty cycle values in microseconds
const int numDutyCycles = 3;
uint16_t dutyCycles[numDutyCycles] = {1000, 1500, 2000}; // Example duty cycle values
int dutyCycleIndex = 0;
void setup() {
// Set pin 9 as output (Pin 9 is connected to Timer1)
pinMode(9, OUTPUT);
// Initialize Timer1 for 400 Hz PWM
initializeTimer1();
// Initialize Timer2 to update duty cycle every 4 ms
initializeTimer2();
}
void loop() {
// Nothing to do here, the timers handle everything
}
// Function to initialize Timer1 for 400 Hz PWM on pin 9
void initializeTimer1() {
// Stop the timer
TCCR1A = 0;
TCCR1B = 0;
// Set fast PWM mode using ICR1 as top
TCCR1A |= (1 << WGM11);
TCCR1B |= (1 << WGM13) | (1 << WGM12);
// Clear OC1A on compare match, set OC1A at BOTTOM (non-inverting mode)
TCCR1A |= (1 << COM1A1);
// Set prescaler to 8 (gives us a clock of 2 MHz)
TCCR1B |= (1 << CS11);
// Set the top value for a 400 Hz PWM
ICR1 = 5000; // 2 MHz / 400 Hz = 5000 counts
// Start with an initial duty cycle (e.g., 20%)
OCR1A = dutyCycles[dutyCycleIndex]*2;
}
// Function to initialize Timer2 to trigger every 4 ms
void initializeTimer2() {
// Stop the timer
TCCR2A = 0;
TCCR2B = 0;
// Set CTC mode (Clear Timer on Compare Match)
TCCR2A |= (1 << WGM21);
// Set prescaler to 64 (gives us a clock of 250 kHz)
TCCR2B |= (1 << CS22);
// Set compare match register for 4 ms interrupt
// 250 kHz / 1000 = 250 counts per ms -> 250 * 4 ms = 1000 counts
OCR2A = 249;
// Enable Compare Match A Interrupt
TIMSK2 |= (1 << OCIE2A);
}
// Interrupt Service Routine for Timer2 Compare Match A
ISR(TIMER2_COMPA_vect) {
// Increment duty cycle index
if (dutyCycleIndex == numDutyCycles) {
dutyCycleIndex = 0;
}
// Update Timer1 duty cycle (OCR1A)
OCR1A = dutyCycles[dutyCycleIndex]*2;
dutyCycleIndex = (dutyCycleIndex + 1);
}
That looks exactly like code spit out of ChatGPT.
When is your homework assignment due?
Yes it is from chatgpt and this isn't a homework fyi
Do you have anything to feedback or are you just passing by and commenting?
What methods have you tried for testing the code?
Are you using the pulse train in a project, and does the output do what you want?
Do you have an Oscilloscope to view the output?
Can you run the code on Wowoki and view the output?
Do you have another Arduino which you can use to read the pulses from pin 9?
I already checked the code and ran a scope through the pin 9, the expected output if the array of pwm commands are {900, 1100, 1300} than a 400 hz pulse train at an update rate of 200 hz (its actually 250 but for the example i am choosing even numbers) would look something like this 900, 900, 1100, 1100, 1300, 1300. But the scope shows absolutely random sequence
It doesn't if you get the oscilloscope to trigger correctly.
Shows a sequence of 2 x 2ms pulses, 2 x 1.5ms pulses, 2 x 1.0ms pulses, repeated.
I triggered the oscilloscope on a positive going pulse, >1.8ms wide and < 2.2ms wide.
It looks to me you are attempting to generate a 3-channel PPM stream with a higher frame rate than the 50Hz frame rate that we normally see when generating a 8 channel PPM stream. Am I correct in my understanding?
But you're triggering in this sequence from what I see in the screenshot
1000, 1000, 2000, 2000, 1500, 1500
But according to the stored array, shouldn't it be,
1000, 1000, 1500, 1500, 2000, 2000
Also I am using a logic analyzer from logic pro, Saleae Logic Pro 16 Logic Analyzer
I am not sure what PPM means, but I am trying to replicate a throttle input coming from a px4 flight controller for my motor drive for a bench setup
I agree that there is an inconsistency.
Also with the factor of *2 in the OCR1A values, I think the pulse length values should be twice the array values.
OCR1A = dutyCycles[dutyCycleIndex]*2;
Yes it is twice that in the code right now, do you mean it shouldn't be that?
You're the one who knows what the duty cycle needs to be for your application.
The time period of the pulse should be 2500 us, I just adhoc calculated the ratio of it by doing trial and error
What update rate from Timer2 are you trying to achieve?
// Function to initialize Timer2 to trigger every 4 ms
void initializeTimer2() {
// Stop the timer
TCCR2A = 0;
TCCR2B = 0;
// Set CTC mode (Clear Timer on Compare Match)
TCCR2A |= (1 << WGM21);
// Set prescaler to 64 (gives us a clock of 250 kHz)
TCCR2B |= (1 << CS22);
// Set compare match register for 4 ms interrupt
// 250 kHz / 1000 = 250 counts per ms -> 250 * 4 ms = 1000 counts
OCR2A = 249;
// Enable Compare Match A Interrupt
TIMSK2 |= (1 << OCIE2A);
}
This code is setting an update rate of 1ms, with a compare match interrupt at 250 counts of 4us ticks. You need to either count overflows, or else set the timer2 prescaler to 256
// Set prescaler to 256 (gives us a clock of 250 kHz)
TCCR2B |= (1 << CS22) | (1<<CS23);
With an update rate of 1ms which is shorter than the pulse lengths you want, there will be all kinds of unwanted behaviour.
I think that you do need the *2.
The following line of code sets the prescaler to 8.
// Set prescaler to 8 (gives us a clock of 2 MHz)
TCCR1B |= (1 << CS11);
That means that the units of time are 0.5µs.
I'm still trying to work out the issue with the sequence being reversed.
I think it is a bit like western films/movies where the wagon wheels appear to be going backwards.
So which should I set it to? I am trying to wait for 4 ms before I increment the dutycycle pointer and that is basically the timer 2's limit before it resets again and starts counting again
Read post #17 again more carefully.