alternative needed to pics with multiple interrupts

Discussion in 'Embedded Systems and Microcontrollers' started by hunterage2000, May 21, 2016.

  1. hunterage2000

    Thread Starter Active Member

    May 2, 2010
    400
    0
    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.
     
  2. NorthGuy

    Active Member

    Jun 28, 2014
    603
    121
    PIC24
     
  3. jpanhalt

    AAC Fanatic!

    Jan 18, 2008
    5,685
    900
    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
     
  4. hunterage2000

    Thread Starter Active Member

    May 2, 2010
    400
    0
    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"?
     
  5. jpanhalt

    AAC Fanatic!

    Jan 18, 2008
    5,685
    900
    "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
     
  6. hunterage2000

    Thread Starter Active Member

    May 2, 2010
    400
    0
    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?
     
  7. jpanhalt

    AAC Fanatic!

    Jan 18, 2008
    5,685
    900
    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
     
  8. hunterage2000

    Thread Starter Active Member

    May 2, 2010
    400
    0
    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.
     
  9. nsaspook

    AAC Fanatic!

    Aug 27, 2009
    2,907
    2,168
    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:
    Code (C):
    1.  
    2. // Configure UART1 RX Interrupt
    3. INTEnable(INT_SOURCE_UART_RX(UART1), INT_ENABLED);
    4. INTSetVectorPriority(INT_VECTOR_UART(UART1), INT_PRIORITY_LEVEL_2);
    5. INTSetVectorSubPriority(INT_VECTOR_UART(UART1), INT_SUB_PRIORITY_LEVEL_0);
    6.  
    7. ....
    8.  
    9. // UART 1 interrupt handler
    10. // it is set at priority level 2
    11.  
    12. void __ISR(_UART_1_VECTOR, ipl2) IntUart1Handler(void)
    13. {
    14. static WORD rx9data;
    15. // Is this an RX interrupt?
    16. if (INTGetFlag(INT_SOURCE_UART_RX(UART1))) {
    17. hoststat.usart1_rxint++;
    18. rx9data = ReadUART1();
    19. put_mbmc_serial_buf((char) rx9data); // put received data in buffer
    20.  
    21. // Clear the RX interrupt Flag
    22. INTClearFlag(INT_SOURCE_UART_RX(UART1));
    23.  
    24. }
    25.  
    26. // We don't care about TX1 interrupt
    27. if (INTGetFlag(INT_SOURCE_UART_TX(UART1))) {
    28. hoststat.usart1_txint++;
    29. INTClearFlag(INT_SOURCE_UART_TX(UART1));
    30. }
    31.  
    32. }
    33.  
    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.
    [​IMG]
    Repeated interrupt sequences with a timed (timer4) ADC sample every ~400 usecs into a data buffer.
    [​IMG]

    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.
    [​IMG]
     
    Last edited: May 21, 2016
  10. John P

    AAC Fanatic!

    Oct 14, 2008
    1,634
    224
    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?
     
  11. jpanhalt

    AAC Fanatic!

    Jan 18, 2008
    5,685
    900
    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.
     
  12. hunterage2000

    Thread Starter Active Member

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

    Code (Text):
    1. #pragma config FOSC = INTOSCIO
    2. #include <stdio.h>
    3. #include <pic16f785.h>
    4. #include <xc.h>
    5. #include <stdlib.h>
    6. int count=0;
    7.  
    8. void delay_1000(void);
    9. void delay_100(void);
    10. void interrupt delay1(void);
    11. void interrupt delay2(void);
    12. int main()
    13. {
    14.        OSCCON = 0b00110000; //500kHz osc
    15.        T1CON = 0b00000101;
    16.        INTCON = 0b11100000;
    17.        TMR0 = 0x00;
    18.        ei();
    19.        TRISC = 0;
    20.        ANSEL0 = 0;
    21.        ANSEL1 = 0;
    22.        while(1);
    23.      
    24. }
    25.    
    26. void interrupt delay1()
    27. {
    28.  
    29.      if (INTCONbits.TMR0IF) {
    30.        
    31.         PORTCbits.RC0 = !PORTCbits.RC0;
    32.         delay_1000();
    33.         INTCONbits.TMR0IF = 0;
    34.         return;
    35.     }
    36. }
    37.  
    38. void interrupt delay2()
    39. {
    40.      if (INTCONbits.TMR0IF) {
    41.        
    42.         PORTCbits.RC1 = !PORTCbits.RC1;
    43.         delay_100();
    44.         INTCONbits.TMR0IF = 0;
    45.         return;
    46.     }
    47. }
    48.  
    49. void delay_1000()
    50. {
    51.     for(count=0; count<1000; count++)
    52.     {
    53.         if(INTCONbits.T0IF)
    54.         {
    55.             count++;
    56.             INTCONbits.T0IF = 0;
    57.         }  
    58.     }  
    59.        
    60.        
    61. }
    62. void delay_100()
    63. {
    64.     for(count=0; count<100; count++)
    65.     {
    66.         if(INTCONbits.T0IF)
    67.         {
    68.             count++;
    69.             INTCONbits.T0IF = 0;
    70.         }  
    71.     }  
    72.        
    73.        
    74. }
    75.  
     
  13. John P

    AAC Fanatic!

    Oct 14, 2008
    1,634
    224
    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.
     
  14. NorthGuy

    Active Member

    Jun 28, 2014
    603
    121
    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:

    Code (C):
    1. int countA = 1;
    2. int countB = 1;
    3.  
    4. void interrupt isr() {
    5.  
    6.   if (countA == 1) {
    7.     // time to toggle gadget A
    8.     PORTCbits.RC0 = !PORTCbits.RC0; // BTW: not the best way to do this
    9.   }
    10.  
    11.   if (countB == 1) {
    12.     // time to toggle gadget B
    13.     PORTCbits.RC1 = !PORTCbits.RC1;
    14.   }
    15.  
    16.   // step up the calendar
    17.   if (countA++ == 7) { // 7 is used here because we want 7-day period (every week)
    18.     // If it's Saturday, roll back to Sunday
    19.     countA = 1;
    20.   }
    21.   if (countB++ == 30) { // 30 is used here because we want 30-day period (every month)
    22.    // If it's the end of the month, roll back to the first
    23.     countB = 1;
    24.   }
    25.  
    26.   INTCONbits.TMR0IF = 0; // won't need the alarm until the next morning
    27. }
    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).
     
  15. nsaspook

    AAC Fanatic!

    Aug 27, 2009
    2,907
    2,168
    An ugly abstraction of 8 bit PIC hardware IMO.
     
  16. dannyf

    Well-Known Member

    Sep 13, 2015
    1,782
    360
    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 (Text):
    1.  
    2. //real isr function
    3. void interrupt myISR(void) {
    4.   //tmr0 isr
    5.   if (T0IE && T0IF) {
    6.     T0IF = 0;  //clear the flag
    7.     _isrptr_t0isr();  //run tmr0isr
    8.   }
    9.  
    10.   //tmr1 isr
    11.   if (TMR1IE && TMR1IF) {
    12.     TMR1IF = 0; //clear the flag
    13.     _isrptr_t1isr(); //run tmr1isr
    14.   }
    15.  
    16.   //add more isr support here
    17. }
    18.  
    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.
     
Loading...