Polling and interrupts

Discussion in 'Embedded Systems and Microcontrollers' started by hunterage2000, Apr 23, 2016.

  1. hunterage2000

    Thread Starter Senior Member

    May 2, 2010
    I have been reading the advantages of using interrupts over polling. A couple of questions:

    1. Is the number of interrupts used in a program limited to use of specific pins?
    2. Seen as interrupts have many advantages over polling, are interrupts solely used in applications or it a mix of both?
  2. OBW0549

    Distinguished Member

    Mar 2, 2015
    That depends on the particular microcontroller chip. I think in most cases the answer is "yes" although there are micros, such as Microchip's dsPIC33EPxxx series, that feature reconfigurable pin assignments via a crossbar switch.

    I imagine that in most cases it's a mix.
    hunterage2000 likes this.
  3. dannyf

    Well-Known Member

    Sep 13, 2015
    generally unrelated. Many interrupts are triggered within the mcu, no pins involved at all.

    It depends and in many cases that statement isn't true.

    Not sure what you meant by that.
  4. Papabravo


    Feb 24, 2006
    Take the standard UART. It is quite common to use interrupts on the receiver because you can't predict when the characters will arrive, or how short the inter-character arrival time will be. On the transmitter side it may be preferable to poll the the transmitter data register empty bit and just send the characters at the synchronous limit. Using an interrupt on the transmitter side is a bit tricky and one of the first of many epic fails a first time programmer will make. the results can be surprising and hilarious.
  5. nsaspook

    AAC Fanatic!

    Aug 27, 2009
    Interrupts on a typical controller can also be internal from specific modules that run independently from main CPU execution and software driven (traps) in some architectures.

    Instead of directly polling the done flag for a ADC conversion value the program can be designed to use the ADC interrupt flag to transfer the result to a memory location while the main program execution does other things. One of the problems with interrupts vs polling is the synchronization of processes. How does the main program know there is new data? This problem can be solved with memory buffers, the main process could 'poll' a data buffer to see if one or more values are stored inside from the ADC interrupt routine. While there are advantages to interrupts it can be a minefield where you have to plan each step carefully in advance.
  6. shteii01

    AAC Fanatic!

    Feb 19, 2010
    Ok. Back to basics. Like others said, there are actually two TYPES of interrupts. If you have a decent textbook, you would have known this, but you, obviously, don't have one.

    There are HARDWARE interrupts, they use pins.
    There are SOFTWARE interrupts, they use flag bits that are built into registers of the uC, meaning that you can not physically access them from outside, you can not hook a wire to them like you can to a pin.

    Now. The number of software and hardware interrupts is limited. If I remember right, Intel 8051 uC has 4 hardware interrupts and 4 software interrupts. Why? Because interrupts is a feature, it uses resources and there is a limit to how many resources a chip has, so the designers of the chip, Intel in this case, decided that they will have 4 hardware interrupts and 4 software interrupts. Just from hardware interrupt view, 8051 has something like 40 or 44 pins. You use some for Vcc and GND, so now you removed 2 pins from the pool of resources, you use 2 for external oscillator, remove 2 more pins from the pool of resources, other things will use other pins, so the pool of available pins to be used for hardware interrupts keeps on shrinking. And that brings us to the interrupts vs polling.

    Like any good textbook will point out, if you have relatively simple system, that does not have a lot of sudden changes or have just a set of tasks that run very regularly, then polling is actually better/simpler system to have/to implement. The other aspect is availability of hardware, if all your pins are occupied, then you have no choice, you can not use hardware interrupts and you are forced to use polling instead, but that also depends on your planning, if you planned to use hardware interrupts and set those pins aside, then found workarounds for other things, then you use interrupts.
  7. MaxHeadRoom


    Jul 18, 2013
    To add all the Microchip modules have their own settable interrupt, and the type 18F and on have hi & lo priority interrupt capability.
  8. shteii01

    AAC Fanatic!

    Feb 19, 2010
    Right. That is additional aspect of interrupts, they have priority. If the uC receives two interrupts, it will execute the high-er priority interrupt first, always. And that is the other part of the design process, you the designer, need to be careful which interrupt is used for which signal. If you have some life and death situation, you want that signal to go to high priority interrupt vs low priority one.

    I guess maybe temperature might be good example. Let say you are taking daily temperature reading, say every 1 minute, you know, one of those environmental studies that some scientists then use a supercomputer to crunch all the data a 100 years later. Do you really need interrupt for this? You can use it, but do you actually need it? No, you don't need it, polling will do this just fine. On the other hand, if you have oven for baking bread or steel tempering process or a chemical reaction that is temperature specific, here your polling might miss the critical event, here high sensitivity and/or quick reaction to change is important, interrupt will provide you with the ability to act on whatever sudden change takes place.
  9. MaxHeadRoom


    Jul 18, 2013
    And not only, but also Interrupt the low priority if it is in process, with the low continuing after the high has done.
  10. Picbuster

    Well-Known Member

    Dec 2, 2013
    To poll or not to poll is embedded in your design and based on that you select the mpu to use.
    example: measure temperature changes of a huge mass (100Kg++) once in 5 or 10 minutes. no int needed this will free one.
    but an alarm or other signals whom require direct action thus high priority.
    At the other hand handling many interrupts could put you in trouble when processor is to slow and or too little memory for stack.
    It's balancing and assuming worst case situations.
    (electronic engineers life is like a toddler shirt short and full of sh......)
  11. John P

    AAC Fanatic!

    Oct 14, 2008
    Ah, but there is one important thing you can predict: the characters can't arrive at an interval of less than one character time. If you poll faster than that, you'll never have any problems.

    Personally I try to write code which uses one interrupt only, and that one is a counter/timer, which on a mid-range PIC usually ends up being TMR2. I'll interrupt the program fast enough to catch incoming data on the UART as I explained above, and then any other inputs are polled and any other time intervals are generated by dividing down the basic timer. The way I feel is that I want to have an exact measure of time, and if I allowed events like characters coming in on the UART to happen at random, there would be a chance that the basic clock could be delayed. If you say "some things are too important to be delayed" then I have to ask what happens if there's already an interrupt being serviced when that "high priority" event occurs? Obviously it gets delayed then, and maybe it gets delayed some more if there are multiple interrupts pending. What makes it worse in my mind is that it's difficult to know for sure how long an interrupt will be delayed before service occurs.

    And beyond that, typically when an interrupt occurs it gets processed completely, and control returns to the main() routine, and then another interrupt gets a chance to be serviced. It costs you time for every instance of entry or exit from an interrupt, whereas in my timed program, you pay for those entries and exits at a constant fixed rate, and all the tasks are performed in a known order. If some things have to be done at an exact time, I'll put them first, so that they get done as soon as the interrupt begins. Other things (scan the keypad, for instance) won't be affected by timing jitter. Yes, you will point out that with my approach I absolutely must get everything done in the timer interrupt before the next interrupt occurs, and get some work done in main() in between. I'm always aware of that! And in fact it's a check I often make, to set and clear a port pin at the start and end of the interrupt, and I'll watch it on a scope. Look for the "jackpot" when the interrupt has to perform all the most time-consuming jobs in one cycle!

    I'm not consistent about performing tasks directly in the interrupt versus setting a flag and letting main() do them when it finds the flag set. It depends on whether I'm comfortable with the amount of work that takes place in the interrupt, versus accepting that the timing of the delayed tasks will be somewhat random, maybe even waiting for multiple interrupts if main() is doing something slow.

    There is a way that software is an art and not a science, where human personality affects the finished product. Maybe a real connoisseur could look over people's code and identify the artist by their personal style.
    OBW0549 likes this.
  12. hunterage2000

    Thread Starter Senior Member

    May 2, 2010
    This is all very helpful info, thank you all. :)
  13. cmartinez

    AAC Fanatic!

    Jan 17, 2007
    That's why I seldom use interrupts at all (the love of my life is the 8051 architecture, btw) I normally prefer polling, since it's perfectly predictable. And use interrupts only when the application is simple to code (so that it can be easy to read and interpret later on), and not time-critical, or if the process being controlled is quite slow compared to the chip's cycle.
  14. nsaspook

    AAC Fanatic!

    Aug 27, 2009
    I'll jump back in to show a C example of PIC18 interrupt based programming driving a simple HD44780 LCD display. The old C18 xlcd lib used bit-banging bus signals and delays in the main program execution flow to sequence commands to the chip for display. This has the effect of blocking other main tasks for a fairly long period of time.

    With this code one small splice of time in the main (1) triggers a much longer sequence of interrupt driven (2) I/O sequences giving quick panel updates while other processes are executed.

    With slow command delay set on the first item from the ring-buffer.
    Detail of one char being sent to LCD. DLED_4 (1) from main and DLED_5 (2) pulses from the state machine for debug

    Its more complex but if you have several I/O streams to manage the advantages grow and it's really only complicated the first time you try it. After a while it becomes natural to think of programming projects this way if the system has the resources to handle the extra cpu time and memory requirements.

    You need a simple circular buffer for commands and data to be sent to LCD. (and other I/O sources) This allows you to access the LCD asynchronously, you dump the data into the buffer then move on to other tasks. The buffer has extra bits for each buf variable so you can insert control data to the state machine.

    fragment of the ring-buffer C header
    Code (C):
    2. #define RBUF_SIZE 32
    4. typedef struct ringBufS_t {
    5. uint16_t buf[RBUF_SIZE];
    6. int8_t head;
    7. int8_t tail;
    8. int8_t count;
    9. } ringBufS_t;
    11. void ringBufS_init(ringBufS_t *_this);
    12. int8_t ringBufS_empty(ringBufS_t *_this);
    13. int8_t ringBufS_full(ringBufS_t *_this);
    14. uint16_t ringBufS_get(ringBufS_t *_this);
    15. void ringBufS_put(ringBufS_t *_this, const uint16_t c);
    16. void ringBufS_flush(ringBufS_t *_this, const int8_t clearBuffer);
    Next is a timer based ISR state machine to handle the bit-banging and delays. Using the timer allows the main application to keep executing during periods it would be otherwise be waiting. Learning how to manage memory queues with a simple FSM should be a fundamental step in learning embedded programming without an operating system .

    Standard method of sending LCD commands with xlcd
    Code (C):
    2. while (BusyXLCD());
    3. WriteCmdXLCD(0xc); // blink, cursor off
    4. while (BusyXLCD());
    5. WriteCmdXLCD(0x1); // clear screen
    Testing code for the ISR based method. S_WriteCmdXLCD is a new buffer based function and putsXLCD was modified to the S_WriteDataXLCD routine.
    Code (C):
    3. #include <p18cxxx.h>
    4. #include "xlcd.h"
    6. /********************************************************************
    7. *       Function Name:  putsXLCD
    8. *       Return Value:   void
    9. *       Parameters:     buffer: pointer to string
    10. *       Description:    This routine writes a string of bytes to the
    11. *                       Hitachi HD44780 LCD controller. The user
    12. *                       must check to see if the LCD controller is
    13. *                       busy before calling this routine. The data
    14. *                       is written to the character generator RAM or
    15. *                       the display data RAM depending on what the
    16. *                       previous SetxxRamAddr routine was called.
    17. ********************************************************************/
    18. void putsXLCD(char *buffer)
    19. {
    20.     while (*buffer) // Write data to LCD up to null
    21.     {
    22.         S_WriteDataXLCD(*buffer); // Write character to LCD buffer
    23.         buffer++; // Increment buffer
    24.     }
    25.     return;
    26. }
    28. void putrsXLCD(const rom char *buffer)
    29. {
    30.     while (*buffer) // Write data to LCD up to null
    31.     {
    32.         S_WriteDataXLCD(*buffer); // Write character to LCD buffer
    33.         buffer++; // Increment buffer
    34.     }
    35.     return;
    36. }
    38. ...
    39. #include <p18cxxx.h>
    40. #include "xlcd.h"
    42. void S_WriteDataXLCD(char data)
    43. {
    44.     while (ringBufS_full(L.tx1b)); // normally there is plenty of space
    45.     ringBufS_put(L.tx1b, data);
    46.     return;
    47. }
    48. ...
    50. /* Loop forever */
    51. while (TRUE) {
    52. ClrWdt(); // reset the WDT timer
    53. if (ringBufS_empty(L.rx1b)) {
    54. if (!z++) {
    55. voltfp(L.adc_val[adc_buf.map.index], f1); // convert to string
    56. sprintf(bootstr2, "S %sV,R %uC %d ", f1, L.adc_raw[adc_buf.map.index], adc_buf.map.index); // display  info
    57. bootstr2[20] = NULL0; // limit the string to 20 chars
    58. DLED_4 = HIGH;
    59. S_WriteCmdXLCD(0b10000000 | LL2); // SetDDRamAddr to line #2 to the buffer
    60. putsXLCD(bootstr2); // sends chars to the buffer
    61. DLED_4 = LOW;
    62. }
    63. } else {
    64. DLED_2 = HIGH;
    65. adc_buf.buf = ringBufS_get(L.rx1b); // get the analog voltages
    66. ADC_Update(adc_buf.buf & ADC_MASK, adc_buf.map.index);
    67. // do something
    68. ringBufS_put(spi_link.tx1b, adc_buf.map.index); // send control data to SPI devices (DAC)
    69. DLED_2 = LOW;
    70. }
    72. }
    ISR timer3 FSM, reading and writing ring-buffers
    Code (C):
    2. // state machine variables
    3. static union adc_buf_type adc_buf;
    4. static union lcd_buf_type lcd_buf;
    5. static int8_t data, slow = 0;
    6. ....
    7. // ADC interrupts
    8. if (PIE1bits.ADIE && PIR1bits.ADIF) { // ADC conversion complete flag
    9. V.adc_count++; // just keep count
    10. PIR1bits.ADIF = LOW;
    11. adc_buf.buf = ADRES;
    12. adc_buf.map.index = L.adc_chan; // add channel data to the 16 bit variable
    13. ringBufS_put(L.rx1b, adc_buf.buf);
    14. if (!(L.adc_chan & ADC_CHAN_MASK)) {
    15. DLED_3 = HIGH; // pulse high on ADC channel zero
    16. } else {
    17. DLED_3 = LOW;
    18. }
    19. L.adc_chan++; // next ADC channel
    20. adc_trigger = FALSE; // reset the skip flag
    21. }
    23. // timer 3 interrupts
    24. /*
    25. * several khz state machine sequencer timer
    26. */
    27. if (PIE3bits.TMR4IE && PIR3bits.TMR4IF) {
    28. PIR3bits.TMR4IF = LOW;
    29. PR4 = TIMER4_NORM;
    30. V.pwm4int_count++;
    32. /*
    33. * scan ADC channels
    34. */
    35. if (!ADCON0bits.GO) {
    36. ADCON0bits.CHS = L.adc_chan & ADC_CHAN_MASK; // set the current channel
    37. if (adc_trigger++) // trigger the conversion on the next timer int so the channel mux can settle
    38. ADCON0bits.GO = HIGH; // and begin A/D conv, will set adc int flag when done.
    39. }
    41. /*
    42. * send SPI data
    43. */
    44. if (!ringBufS_empty(spi_link.tx1b)) { // SPI send
    45. SSP1BUF = ringBufS_get(spi_link.tx1b); // transfer the 8 bit data buffer
    46. }
    48. if (!ringBufS_empty(L.tx1b) || lcd_buf.map.state) { // LCD send, 4bit , upper nibble
    49. switch (lcd_buf.map.state) {
    50. case 0:
    51. DLED_5 = HIGH;
    52. lcd_buf.buf = ringBufS_get(L.tx1b);
    53. data = lcd_buf.buf;
    54. lcd_buf.map.state = 0;
    55. lcd_buf.map.skip = 0;
    56. TRIS_DATA_PORT &= 0x0f;
    57. DATA_PORT &= 0x0f;
    58. DATA_PORT |= data & 0xf0;
    59. if (lcd_buf.map.cmd) {
    60. RS_PIN = 0; // Set control signals for command
    61. } else {
    62. RS_PIN = 1; // Set control bits for data
    63. }
    64. RW_PIN = 0;
    65. lcd_buf.map.state++;
    66. break;
    67. case 1:
    68. DLED_5 = !DLED_5;
    69. E_PIN = 1; // Clock nibble into LCD
    70. lcd_buf.map.state++;
    71. break;
    72. case 2:
    73. DLED_5 = !DLED_5;
    74. E_PIN = 0;
    75. DATA_PORT &= 0x0f;
    76. DATA_PORT |= ((data << 4)&0xf0);
    77. lcd_buf.map.state++;
    78. break;
    79. case 3:
    80. DLED_5 = !DLED_5;
    81. E_PIN = 1; // Clock nibble into LCD
    82. lcd_buf.map.state++;
    83. break;
    84. case 4:
    85. if (!lcd_buf.map.skip) { // don't repeat if we're in slow time
    86. DLED_5 = !DLED_5;
    87. E_PIN = 0;
    88. TRIS_DATA_PORT |= 0xf0;
    89. }
    90. /* fall-through to default people */
    91. default:
    92. if (lcd_buf.map.slow) { // stay in this state until the slow flag clears below
    93. lcd_buf.map.skip = 1;
    94. if (slow++ >= LCD_SLOW) { // for home and clear commands >3ms delay
    95. lcd_buf.map.slow = 0;
    96. slow = 0;
    97. }
    98. } else {
    99. lcd_buf.map.state = 0;
    100. DLED_5 = LOW;
    101. }
    102. break;
    103. }
    104. if (!lcd_buf.map.slow)
    105. PR4 = TIMER4_FAST; // pump the LCD faster than normal
    106. } else {
    107. lcd_buf.map.state = 0;
    108. }
    109. }
    If you program in C learning how to use unions and structures to access memory blocks with bitmaps in a human readable way makes it easier to debug code. Here the 8 bit data and state machine control bits are embedded into one 16 bit variable.

    Code (C):
    2. // LCD structs
    4. typedef struct D_data { // bitmap
    5. uint16_t buf : 8; // data/cmd
    6. uint16_t slow : 1; // long delay for clear and home
    7. uint16_t cmd : 1; // LCD control bit
    8. uint16_t state : 3; // state machine sequence
    9. uint16_t skip : 1; // slow cmd repeating flag
    10. } D_data;
    12. /* used to hold 16-bit LCD buffer and control bits*/
    13. union lcd_buf_type {
    14. uint16_t buf;
    15. struct D_data map;
    16. };
    18. // ADC structs
    20. typedef struct A_data {
    21. uint16_t dummy8 :8; // dummy space for adc data
    22. uint16_t dummy4 :4; // C18 limits bitmaps to 8
    23. uint16_t config : 1; // adc control bit
    24. uint16_t index : 3; //adc channel select
    25. } A_data;
    27. /* used to hold 16-bit adc buffer, index and control bits*/
    28. union adc_buf_type {
    29. uint16_t buf;
    30. struct A_data map;
    31. };
    Last edited: May 6, 2016
    JohnInTX likes this.