2

In this other stack QA I'm asking about generating a pulse signal with a period ranging from 63 ms to 6.5 ms. There is a popular PWM library however it doesn't compile and also since 63 ms is 15.8 Hz, that library's integer Hz setting isn't of help.

Is there a definitive process that isn't waving incense bundles in the corners of the room? There is an over abundance of "how tos" on the internet, however, none seemed to get me to a solution.

I've got a Mega and am using TIMER5. It's "just like" TIMER1 in that it is a 16 bit timer.

Edgar Bonet
45.1k4 gold badges42 silver badges81 bronze badges
asked Aug 22, 2019 at 6:02

1 Answer 1

2

I stumbled upon a solution via trial and error coupled with watching the results on an o'scope.

The best guidance came from a Stack Exchange QA. Setup isn't particularly special, however the combination of WGM bits here worked and I didn't investigate further. Of the biggest importance are the prescaler divider bits, the least three significant bits of TCCRxB. 001 means no dividing or prescaling of the clock.

It all boiled down to 4 registers and the pinMode() call. Ultimately, seven lines of code:

#define PULSE_PIN 45
void setup() {
 // IMPORTANT: Clear Timer/Counter Control Registers
 TCCR5A = 0;
 TCCR5B = 0;
 // Set Timer/Counter Control Registers
 TCCR5A = B00101001; // Phase and frequency correct PWM change at OCRA
 TCCR5B = B00010010; // least sig bits sets "carrier frequency" by dividing CPU clock
 // Observation:
 // TCCR5B = B00010010 and OCR5A = 16667 results in period of 16.70 mSec
 // meaning OCR5A is period in uSec when TCCR5B = B00010010
 // TCCR5B = B00010001 : OCR5A = 20000 results in period = 2.5 mSec 399.3Hz
 pinMode(PULSE_PIN, OUTPUT);
}

In the sketch, I watch an analog pin with a potentiometer on it and map its sweep to an effective RPM range of 950 to 9000 RPM. Hence the arbitrary range of periods I want to handle. When loop() sees the ADC change, the below code adjusts the PWM accordingly.

Note that no floating point data types are used. The magic numbers below were chosen by trial and error in a spreadsheet with values rounded to whole numbers.

void setRpm(){
 // Getting to period in uSec from rpm the long way to keep 
 // the answer in range of the data type:
 unsigned int rpm = map(pot_adc, 0, 1023, RPM_MIN, RPM_MAX);
 unsigned long period = rpm/50;
 period = 24000/period;
 period = period*50;
 // The trigger lasts 26°, so the PW is 26/360 * period.
 dwell = period/36;
 dwell = dwell*26;
 dwell = dwell/10;
 // OCR5A is a counter the carrier frequency counts up to,
 // which sets output period / frequency
 // meaning OCR5A is period in uSec when TCCR5B = B00010010
 OCR5A = period; // OCR5A is period in uSec when TCCR5B = B00010010
 // OCR5B is the width of the pulse. If we used floats,
 // we could use duty cycle * period.
 OCR5B = dwell;
}

Obviously, this code will have limitations to the range of frequencies it can handle, but the Internet has plenty of means for higher frequency PWM, especially when the desired PWM is fixed and doesn't need varying.

The most helpful way to see the control registers is as binary positions and not to rely upon macros, masks, and bit shifting. This comment block helped greatly:

 // WGM modes: 
 // TCCR1A
 // Bit 7 6 5 4 3 2 1 0
 // Bit Name COM1A1 COM1A0 COM1B1 COM1B0 ----- ----- WGM11 WGM10
 // Initial Value 0 0 0 0 0 0 0 0
 // changed to 1 1 1 1 0 0 0 1
 // TCCR1B
 // Bit 7 6 5 4 3 2 1 0
 // Bit Name ICNC1 ICES1 ----- WGM13 WGM12 CS12 CS11 CS10
 // Initial Value 0 0 0 0 0 0 0 0
 // changed to 0 0 0 0 1 0 0 1 
 // CS12,CS11,CS10 = (0,1,1) = prescaler divide by 64
VE7JRO
2,51519 gold badges27 silver badges29 bronze badges
answered Aug 22, 2019 at 6:02
1
  • I like bit-shifting in groups. E.g. TCCR1A = (0b11 <<COM1A0) | (0b11 <<COM1B0) | (0b01 << WGM10) ; and TCCR1B = (0b11 <<WGM12)| (0b001 <<CS10) -- then the trial-and-error of flipping bits is more clear and self-documenting -- If you want to try the /64 prescaler, it's a 1 bit edit to 0b011 Commented Jan 18, 2022 at 6:09

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.