2

I am trying to program my Arduino Mega 2560 to effectively create a PWM signal on any digital pin using timer interrupts and timer1. The pins will pulse a finite amount of times (not indefinitely). I am first trying this with a 256-prescaler value, which I believe means that each increment of my TCNT1 is 16.0 microseconds, and since it is a 16-bit register, it can reach a max. value of 1048567 microseconds.

I am designing the program so that the period and duty cycle are changed by Serial commands that the user can send (I already have that part figured out). I am using clear timer on compare mode and OCR1A and OCR1B to toggle the digital pin between HIGH and LOW.

If the desired period is less than 1048567 microseconds, I can divide the period by the modified clock (modified by the prescaler) to figure out the OCR1A value, and then based on the desired duty cycle, calculate OCR1B. Then I set the ISR vectors for A and B to toggle the pin and count down until the desired number of cycles is completed.

What I would like to also allow the user to do is set periods/duty cycles that would be longer than 1048567 microseconds, but I'm not sure how to accomplish that. For example, if I wanted to have the pin be HIGH for 2 seconds with a total period of 10 seconds, I would choose values for OCR1B and OCR1A that, through the ISR(TIMER1_COMPA_vect) and ISR(TIMER1_COMPB_vect), I could keep track (through a counter variable) of how many interrupts had occurred. If enough interrupts had occurred to equal the time of that the pin had to be HIGH and/or LOW, the ISRs would actually trigger the toggling of the pin (instead of just counting the number of interrupts).

How could I choose the OCR1B and OCR1A values that would accomplish this optimally? Is it reasonable to keep track of time this way as a workaround to the limitation of the timer counter max value? I am open to any suggestion and I appreciate any input. Thank you.

1
  • What sort of precision do you want? For something like 2 seconds on and 5 seconds off, I would have thought that simply toggling a pin by checking if time is up in the main loop would do it.
    – Nick Gammon
    Commented Jun 27, 2016 at 5:44

1 Answer 1

2

By maintaining your own second-level prescaler you can quite easily keep track of longer periods.

The most efficient method is to use a "divide and multiply by two" scheme.


  1. Start with a custom pre-scaler of 1.
  2. While the count period is > 65535:
    1. Multiply the pre-scaler by 2.
    2. Divide the count period by 2

uint16_t ps = 1;
uint32_t period = 429864;

while (period > 65535) {
    ps <<= 1;
    period >>= 1;
}

Then you can load the count period register with the now divided count period and use a variable to count the number of times the interrupt has been triggered. When it equals your custom pre-scaler value you reset the count to 0 and run your actual interrupt routine.

The left and right shifts multiply by two and divide by two respectively in a very efficient manner. Far more than multiplying by 10, or dividing the period by 65536 and using that as a prescaler.

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.