3
\$\begingroup\$

I'm new to PIC microcontrollers (I have some experience with Arduino but wanted some "real" experience =P ). I got myself a PIC16F88 and I intend to make a simple robot with two 2 DC motors. Each motor will be independently controlled via PWM.

I want to implement the PWM signals in software (ya I know, quite a good learning experience!). This microcontroller only has one PWM module, but I would want to do it in software even if it had more.

Without further ado:

What happens when two timer overflow interrupts occur at the same time? Does the controller ignore one? Will they both call the interrupt vector one after another?

\$\endgroup\$
4
  • \$\begingroup\$ PIC16F88's are so cheap, why don't you use one for each motor and skip having to write a firmware PWM routine? \$\endgroup\$
    – tcrosley
    Commented Apr 24, 2014 at 20:48
  • 2
    \$\begingroup\$ Well that would be a lazy approach. I like a challenge. And 2pics cost 2* and take up twice the same amount of space and energy so ya. \$\endgroup\$
    – Ethienne
    Commented Apr 24, 2014 at 21:06
  • \$\begingroup\$ Buy a PIC with two PWM modules instead... software PWM is just ugly. \$\endgroup\$
    – markt
    Commented Apr 25, 2014 at 2:06
  • \$\begingroup\$ Well hey. Ill finish my project, I'm doing this for fun anyway. I will gladly share my code here for feedback once I'm done. You guys can tell me if it's ugly then. But I'm starting to like the idea in fact. I can literally send individual PWM to a dozen of devices with a single super cheap microcontroller. But that's a whole new discussion and somewhat unrelated to the question of this thread. \$\endgroup\$
    – Ethienne
    Commented Apr 26, 2014 at 2:44

1 Answer 1

4
\$\begingroup\$

Say that you have enabled the TMR0 and TMR1 interrupts by setting TMR0IE and TMR1IE. When either of the interrupt events happen, their interrupt flags (TMR0IF or TMR1IF) get set. This doesn't cause the code to actually vector to the interrupt routine unless the GIE (global interrupt enable) bit is also set.

So, say that GIE has been set, and you get a TMR0 overflow. This sets TMR0IF, vectors to the interrupt location, and clears the GIE bit. Now that GIE is cleared, future interrupt triggers won't cause any code vectoring. However, the interrupt flags still get set.

Now, say that you are in your interrupt routine. The TMR1 interrupt gets triggered. This sets the TMR1IF bit, but doesn't do anything else. The bit simply stays set.

When your interrupt routine is finished, you use a RETFIE statement (in assembly) or a return(); (in c). This vectors back to your mainline code, and sets the GIE bit.

Now that both the GIE and TMR1IF bits are set, it causes an immediate vector back into your interrupt code.

So, you don't really lose interrupt data; it just gets delayed.

An common way to structure an ISR is like this:

if (TMR0IF && TMR0IE)
{
    (do something)
    TMR0IF = 0; // Clear the flag!
    return();
}
else if (TMR1IF && TMR1IE)
{
    (do something else)
    TMR1IF = 0;
    return();
}

But, if you expect to have concurrent interrupts, you might want to allow them both to be processed in just one pass through the ISR:

if (TMR0IF && TMR0IE)
{
    (do something)
    TMR0IF = 0;
}
if (TMR1IF && TMR1IE) // Notice this isn't an "else if"
{
    (do something else)
    TMR1IF = 0;
}
return();

There's one last issue to be aware of. Say that both interrupt flags get set at the same time (or very close to the same time). By the time the code vectors to the interrupt routine both flags will already be set. The ISR doesn't know which one got set first; it simply runs through your code. So in my earlier examples the TMR0 interrupt will get serviced first, even if the TMR1 flag was slightly earlier.

I hope this helps :)

\$\endgroup\$
6
  • 1
    \$\begingroup\$ @Ethienne You're welcome! I just edited my example a bit, if you want to look at it. \$\endgroup\$
    – bitsmack
    Commented Apr 24, 2014 at 21:26
  • 1
    \$\begingroup\$ You have confused RETURN and RETFIE. RETURN simply pops the call stack to return from a subroutine. RETFIE does that, but in additional also sets the GIE bit. You use RETFIE, not RETURN, to return from a typical interrupt routine. \$\endgroup\$ Commented Apr 24, 2014 at 21:27
  • \$\begingroup\$ @OlinLathrop I was indeed, thank you! I'll fix it... \$\endgroup\$
    – bitsmack
    Commented Apr 24, 2014 at 21:40
  • \$\begingroup\$ I wouldn't bother with the "&& TMRxIE" ... the fact that you are in the interrupt service routine implies that interrupts are enabled. \$\endgroup\$
    – aja
    Commented Apr 24, 2014 at 23:55
  • \$\begingroup\$ @aja The reason to do it that way is that it gives you the ability to disable a specific interrupt in the code. For example, if you wanted to temporarily ignore any TMR1 interrupts, you would clear the TMR1IE bit. The TMR1IF bit can get set anyway, even though it doesn't cause a jump to the ISR. Say that you've cleared TMR1IE, and TMR1IF gets set. Then, some other interrupt (like TMR0) makes the code jump into the ISR. The ISR would see that TMR1IF was set and behave accordingly... \$\endgroup\$
    – bitsmack
    Commented Apr 25, 2014 at 0:08

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.