alternative needed to pics with multiple interrupts

Thread Starter

hunterage2000

Joined May 2, 2010
487
Hi, If you had an application that needed multiple interrupts (5+). Is there a microcontroller that has this? The pic18 devices have 2 but I was wondering if there is an alternative.
 

jpanhalt

Joined Jan 18, 2008
11,088
You need to be more specific as to what interrupts you want. More than 5 interrupts is true of many non-enhanced midrange devices (16Fxxx). The lowly 16F1829 has 10 interrupts on change plus others, such as timer interrupts. Of course, you don't have interrupt priority. Do you need that?

John
 

Thread Starter

hunterage2000

Joined May 2, 2010
487
yeah 5+ interrupts and priority. I thought the pic16 series has only 1 interrupt. I tried to setup 2 timer0 interrupts with the 16f785 but it wouldn't let me. What did you mean by "the 16F1829 has 10 interrupts on change"?
 

jpanhalt

Joined Jan 18, 2008
11,088
yeah 5+ interrupts and priority. I thought the pic16 series has only 1 interrupt. I tried to setup 2 timer0 interrupts with the 16f785 but it wouldn't let me. What did you mean by "the 16F1829 has 10 interrupts on change"?
"Interrupt on Change" is just one type of interrupt. That means that when the signal changes (high to low, low to high, or either) you can create an interrupt. That function is available on 10 pins (Ports A and B). There are other sources of interrupt too.

Why do you think you need priority? Have you ever written for a device that allows priority ? Did you know that just because you have a interrupt enabled at certain times, it doesn't have to be enabled all of the time?

John
 

Thread Starter

hunterage2000

Joined May 2, 2010
487
so your saying I can use multiple interrupts if I disable 1 then enable another? and can I use multiple interrupts if they come from different sources i.e 1 timer0, 1 adc, 1 external etc?
 

jpanhalt

Joined Jan 18, 2008
11,088
As for the various interrupt sources, and there are others than have not been mentioned, you can have none enabled, some enabled, and all enabled. With priorities, one interrupt can interrupt another. That is more complex, which I why I wonder whether you are sure that you need priorities.

John
 

Thread Starter

hunterage2000

Joined May 2, 2010
487
The priority thing was an option to look at but for now I need some clarity on running multiple interrupts. I have only recently started looking at interrupts and wondered why I couldn't run 2 timer0 interrupts. I wanted to start basic with an led flash every 500ms on 1 and 1s on another.
 

nsaspook

Joined Aug 27, 2009
10,426
You can easily have many sources on one interrupt level or use HI/LOW priority to separate critical service routines from ones whose timing can vary a bit.
Here is an example with both for PIC18 (C18 code). Having many flag checks on one interrupt does slow the response speed for any one event but it's rarely a problem in most small 8 bit controller projects. The multi priority/level interrupts on the PIC 24/32 series do allow for much finer and usually faster event handling with just the isolated interrupt event code run in response to a service request.

PIC32 interrupt vector code:
C:
// Configure UART1 RX Interrupt
INTEnable(INT_SOURCE_UART_RX(UART1), INT_ENABLED);
INTSetVectorPriority(INT_VECTOR_UART(UART1), INT_PRIORITY_LEVEL_2);
INTSetVectorSubPriority(INT_VECTOR_UART(UART1), INT_SUB_PRIORITY_LEVEL_0);

....

// UART 1 interrupt handler
// it is set at priority level 2

void __ISR(_UART_1_VECTOR, ipl2) IntUart1Handler(void)
{
static WORD rx9data;
// Is this an RX interrupt?
if (INTGetFlag(INT_SOURCE_UART_RX(UART1))) {
hoststat.usart1_rxint++;
rx9data = ReadUART1();
put_mbmc_serial_buf((char) rx9data); // put received data in buffer

// Clear the RX interrupt Flag
INTClearFlag(INT_SOURCE_UART_RX(UART1));

}

// We don't care about TX1 interrupt
if (INTGetFlag(INT_SOURCE_UART_TX(UART1))) {
hoststat.usart1_txint++;
INTClearFlag(INT_SOURCE_UART_TX(UART1));
}

}
ADC interrupt timing with TIMER4 interrupt example from the below code:
Top trace: HIGH processor in high ISR routine/LOW in main or low pri ISR, triggered from timer 4 interrupt.
Bottom trace: HIGH ADC conversion time triggered from the timer 4 interrupt and back to LOW at the ADC conversion complete interrupt.

Repeated interrupt sequences with a timed (timer4) ADC sample every ~400 usecs into a data buffer.


https://github.com/nsaspook/pyro/blob/master/p18pyro.X/pyro_vector.c

All of this is happening in background within a program with lots of other processing from main.
MPLAB X code call graph for routine ringBufs_put.
 
Last edited:

John P

Joined Oct 14, 2008
1,986
The priority thing was an option to look at but for now I need some clarity on running multiple interrupts. I have only recently started looking at interrupts and wondered why I couldn't run 2 timer0 interrupts. I wanted to start basic with an led flash every 500ms on 1 and 1s on another.
This is a serious misunderstanding. Timer0 (or timer1, or timer-anything) can only do one thing, and anyway it can't measure a time as long as a second. You'd be better off setting up a repetitive timer interrupt at something like 1000 per second, and then counting occurrences to get the time intervals you want. So you'd have one counter going up to 500, and one going to 1000, in order to give you 500msec and 1sec. Or do you actually want flash rates at those frequencies, so you'd want 250msec on, then 250msec off for one LED, and 500msec on and 500msec off for the other?
 

jpanhalt

Joined Jan 18, 2008
11,088
Another way to get longer times is to use a dedicated clock crystal for the timer that is different from the MCU clock. For example, TMR1 (16-bit) allows that, and you can use a 32.768 kHz watch crystal to clock it. You can even add a post scale of 1:8. At a maximum, that will give you 16 seconds. All the while, the MCU can be running from its internal oscillator at whatever clock you want that is allowed. You can do similar things with the other timers

Now, of course, nothing substitutes for going back to the datasheet and reviewing the sections on timers. So, if you are using a crystal for the MCU, then you will have used those pins that can be used to clock TMR1. You need to study TMR0 and TMR1 in particular, but TMR2 (aka TMRx) is also very useful. Some chips have multiple, identical TMR2's (numbered TMR2, TMR4, etc.). Rather than searching for the "best" chip, I recommend defining what you need to do and then finding a chip that does it.*

John

*Comments directed at mid-range and enhanced mid-range chips only.
 

Thread Starter

hunterage2000

Joined May 2, 2010
487
ok so I have tried this code but I get an error saying "more than one interrupt function defined"

Code:
#pragma config FOSC = INTOSCIO
#include <stdio.h>
#include <pic16f785.h>
#include <xc.h>
#include <stdlib.h>
int count=0;

void delay_1000(void);
void delay_100(void);
void interrupt delay1(void);
void interrupt delay2(void);
int main()
{
       OSCCON = 0b00110000; //500kHz osc
       T1CON = 0b00000101;
       INTCON = 0b11100000;
       TMR0 = 0x00;
       ei();
       TRISC = 0;
       ANSEL0 = 0;
       ANSEL1 = 0;
       while(1);
     
}
   
void interrupt delay1()
{
 
     if (INTCONbits.TMR0IF) {
       
        PORTCbits.RC0 = !PORTCbits.RC0;
        delay_1000();
        INTCONbits.TMR0IF = 0;
        return;
    }
} 

void interrupt delay2()
{
     if (INTCONbits.TMR0IF) {
       
        PORTCbits.RC1 = !PORTCbits.RC1;
        delay_100();
        INTCONbits.TMR0IF = 0;
        return;
    }
} 

void delay_1000()
{
    for(count=0; count<1000; count++)
    {
        if(INTCONbits.T0IF)
        {
            count++;
            INTCONbits.T0IF = 0;
        }   
    }   
       
       
}
void delay_100()
{
    for(count=0; count<100; count++)
    {
        if(INTCONbits.T0IF)
        {
            count++;
            INTCONbits.T0IF = 0;
        }   
    }   
       
       
}
 

John P

Joined Oct 14, 2008
1,986
In most compilers (CCS is an exception, I believe) you can only define one interrupt function because in PIC processors, all interrupts vector to the same location, 0x0004. Unless you tell it not to, the compiler will do some saving of vital registers in the interrupt before executing any of your code, but if you have multiple interrupt sources, you have to start with a test for which interrupt has actually occurred. Avoiding the need for testing and branching is a good reason for using only one interrupt source.

CCS fakes it by letting you define a function for every interrupt, but of course the hardware doesn't work that way. The compiler is inserting code to do the testing and branching for you.
 

NorthGuy

Joined Jun 28, 2014
611
ok so I have tried this code but I get an error saying "more than one interrupt function defined"
First, you need to understand what you're doing.

Timer is like a clock. Interrupt is like an alarm. If you want to toggle gadget A every week and gadget B every month, how would you do it? You run your clock, you wind up the alarm. It wakes you up every morning. You see if it's Sunday, and if it is you toggle gadget A. Then you see if this is the first of the month, and if it is you toggle gadget B. Then you forget about the alarm and go on with your life until it wakes you up next day.

Note that you don't need two alarm-clocks to perform these tasks.

Now you need to program this behaviour into the PIC. You set up a timer (clock). You set up an interrupt (alarm). Within the ISR (when alarm rings), you determine whether it's Sunday (countA == 1), and if it is then you toggle LED1. Then you determine whether it's the first of the month (countB == 1) and if it is then you toggle LED2. Then you leave ISR and let the processor go on with its life:

C:
int countA = 1;
int countB = 1;

void interrupt isr() {

  if (countA == 1) {
    // time to toggle gadget A
    PORTCbits.RC0 = !PORTCbits.RC0; // BTW: not the best way to do this
  }

  if (countB == 1) {
    // time to toggle gadget B
    PORTCbits.RC1 = !PORTCbits.RC1;
  }

  // step up the calendar
  if (countA++ == 7) { // 7 is used here because we want 7-day period (every week)
    // If it's Saturday, roll back to Sunday
    countA = 1;
  }
  if (countB++ == 30) { // 30 is used here because we want 30-day period (every month)
   // If it's the end of the month, roll back to the first
    countB = 1;
  }

  INTCONbits.TMR0IF = 0; // won't need the alarm until the next morning
}
Same with milliseconds. If you have an interrupt every 10 ms, and you want to toggle your LED every 500 ms, you roll back the counter for this LED when it reaches 50 (500/10), or if you want another LED which toggles every 730 ms, you roll back the counter for this LED when it reaches 73 (730/10).
 

dannyf

Joined Sep 13, 2015
2,197
so I have tried this code
It depends on what you meant by "interrupt". Low-end PICs don't have support for vectored interrupts. As such, you really have one isr.

However, you can code to give the appearance of multiple "ISRs", in ways like this:

Code:
//real isr function
void interrupt myISR(void) {
  //tmr0 isr
  if (T0IE && T0IF) {
    T0IF = 0;  //clear the flag
    _isrptr_t0isr();  //run tmr0isr
  }

  //tmr1 isr
  if (TMR1IE && TMR1IF) {
    TMR1IF = 0; //clear the flag
    _isrptr_t1isr(); //run tmr1isr
  }

  //add more isr support here
}
the isrptr_xxxx() are function pointers and can be user installed. Otherwise, they default to an empty handler.

Again, this is not a real vectored interrupt structure but just a way to mimic its functionality, at some expenses that may or may not be important to your given application.
 
Top