simple timer0 interrupt to light up ledch is a

Discussion in 'Embedded Systems and Microcontrollers' started by kuannygohcheetatt, Feb 17, 2014.

  1. kuannygohcheetatt

    Thread Starter Member

    Oct 31, 2013
    61
    0
    Code ( (Unknown Language)):
    1.  
    2. //Student Name:Ooi Wei Sheng
    3.  
    4. #include <pic.h>
    5. /*Lab1.c - Turn ON PORT B 0 LED*/
    6.  
    7. __CONFIG(0x3f38);
    8.  
    9. void interrupt timer()
    10. {
    11.     PORTA=1;
    12.  
    13.     for(int i= 0; i<16;i++)
    14.     {
    15.         while(!T0IF);
    16.         T0IF=0;
    17.         TMR0=0;
    18.         }
    19.         PORTA=PORTA<<1;
    20. }
    21.  
    22.  
    23.  
    24. main()
    25. {
    26.  
    27.  
    28. TRISA=0;
    29. OPTION_REG =7;
    30. GIE=7;
    31. T0IE=1;
    32. while(1);
    33.  
    34.  
    35. }
    36.  

    this is the code given by my lecturer in order to blink the led for 1 second, but i was wandering, how the led blink ? since after the delay 1s loop which is a for loop looping for 15 times, the program requires the led to shift by 1 bit to the left, however, there is no more delay after shifting it to the left by one bit, doesnt it meant the LED in RA0 will light up forever since the bliking is too fast for our naked eye to detect? can someone explain it to me?
     
  2. takao21203

    Distinguished Member

    Apr 28, 2012
    3,577
    463
    Your code is a bit offtrack, there are some blatant errors as well.

    for instance, you use while(!T0IF), which would mean, T0IF is 0, then after it is supposed to have changed, you set it to 0 again.

    When a timer interrupt is raised, TMR0IF becomes 1.

    Then you need to check if(TMR0IF==1), because there are many interrupts. You also need to reset it with TMR0IF=0 again.

    I have put a complete code which blinks a LED slowly on my blog, try to understand how it works. I don't do things inside the interrupt, but I exit again immediately and set a flag.

    Also I extend the timer to a 16bit timer (in software). Result, the LED blinks slowly.

    http://aranna.altervista.org/dragonsnest/pic-microcontrollers/blinking-led-timer-interrupt-pic-16f/
     
  3. spinnaker

    AAC Fanatic!

    Oct 29, 2009
    4,866
    988
    please use a capital at the start of a sentence and a period at the end, see how hard this is to read, we want to understand you, the best way to do that is do what you can to make yourself understood.

    There is an excellent example of timer interrupts here:

    http://www.mikroe.com/products/view/285/book-pic-microcontrollers-programming-in-c/

    Along with a a number of other good examples,


    If you have a PIC18F45K20, Microchip has some excellent prebuilt lessins, if You can find the code for the Debug Express Lesson Files.
     
  4. Brownout

    Well-Known Member

    Jan 10, 2012
    2,375
    998
    Where is the interrupt vector written? I didn't see it in the OP post or the examples. Am I missing something?
     
  5. Ian Rogers

    Member

    Dec 12, 2012
    158
    29
    On the mid range pic, there is only one vector... So its assumed!!

    All you need to do is set the interrupt flag!!
     
  6. takao21203

    Distinguished Member

    Apr 28, 2012
    3,577
    463
    Maybe not so well known is this.

    you don't need to enable the interrupt. The flag will become set anyway.

    So, you can poll the serial interrupt flag in a while loop without dealing with the latency.

    I don't know if it works on all PICs and I don't recall if I ever tried that with the timer.

    It's bad practice but when you can improve performance for reading from a serial FLASH *fast*, it is good.

    Overclocking the PIC a little, I archieved high refresh rates on a serial TFT.

    If you do a lot of testing in the interrupt that adds to the latency, so the display refreshs slower.
     
  7. JohnInTX

    Moderator

    Jun 26, 2012
    2,339
    1,022
    Here's something I posted in another thread. It shows how to generate and use a system tik using TIMER1 interrupts on a 12F683 using XC8. It flashes a couple of LEDs at different rates.

    The key points include:

    A single timer interrupt is used to generate a system tik that drives many different timers. TIMER1 is used in this example leaving TIMER0 free for other uses and to free up the WDT prescaler. Different PIC families have other timers more suitable for this but not this one.

    It doesn't do IO processing, loops, delays etc. in a PIC interrupt routine, rather it maintains a chain of derived timers, one for each task e.g. flash an LED.

    In main, the code screams around the while loop running each task in round-robin sequence.

    Each task simply maintains its timer and its LED (or whatever) then return control to the scheduling loop.

    Task timing can be a single bit (one systik has elapsed) OR a register a task loads with a number of systiks and waits until it goes to 0 - meaning time has elapsed.

    A task is NEVER allowed to wait or delay. If its not time to do something, it returns immediately.

    This is a basic example. More complex constructs can be built but even with this simple one, a 4MHz PIC has plenty of time to keep up with everything and more.

    In main, the code screams around the while loop running each task in round-robin sequence.

    Each task simply maintains its timer and its LED (or whatever) then return control to the scheduling loop.

    Code ( (Unknown Language)):
    1. /*
    2.  * File:   Guichess.c
    3.  * Author: John
    4.  *
    5.  * Created on February 10, 2014, 11:49 PM
    6.  */
    7.  
    8. #pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA4/OSC2/CLKOUT pin, I/O function on RA5/OSC1/CLKIN)
    9. #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
    10. #pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)
    11. #pragma config MCLRE = OFF // MCLR Pin Function Select bit (MCLR pin function is digital input, MCLR internally tied to VDD)
    12. #pragma config CP = OFF // Code Protection bit (Program memory code protection is disabled)
    13. #pragma config CPD = OFF // Data Code Protection bit (Data memory code protection is disabled)
    14. #pragma config BOREN = OFF // Brown Out Detect (BOR disabled)
    15. #pragma config IESO = ON // Internal External Switchover bit (Internal External Switchover mode is enabled)
    16. #pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is enabled)
    17.  
    18.  
    19. #include <xc.h>
    20. #include <stdlib.h>
    21.  
    22.  
    23. /************************************************************/
    24. //              HARDWARE CONFIGURATION
    25. /************************************************************/
    26. #define _XTAL_FREQ 8000000
    27. #define usTcyc ((_XTAL_FREQ / 1000000) / 4) // how many uS 1 Tcyc is
    28.  
    29. //*****************  CODE CONFIGURATION  ********************
    30. #define usSysTik 10000      // 10msec systik
    31. #define ms100PSset 10       // 10 * systik = 100ms
    32.  
    33.  
    34. /************************************************************/
    35. //              TIMERS
    36. /************************************************************/
    37. #if 0
    38.     Timer 1 is used to generate a 10msec system tik. Its the
    39.     smallest derived time available and is used to drive the
    40.     debouncer and EE update timer.
    41.     Timer 0 is reserved to free up the prescaler for the WDT.
    42.  
    43.     The timer runs w/o prescaler so that we can load it with
    44.     an adjusted time without having to worry about clearing the PS
    45. #endif
    46.  
    47. #define T1CONinit 0b00000000    // No gate, 1:1ps, Internal, OFF
    48. #define TMR1adj 9               // Number of Tcyc it takes to stop-load-start TMR1
    49.                                 // determined by looking at disassembly.
    50.     //Calculate reload value for timer counting up to rollover. Result is
    51.     // number of counts based on Tcyc, adjustment, SysTik value etc.
    52. #define TMR1setTiks (2^16-(usSysTik / usTcyc) + TMR1adj)
    53.  
    54. unsigned char  ms100PreScaler;
    55. bit ms10_happened;      // bits get set by IRQ at the noted time interval
    56. bit ms100_happened;
    57.  
    58. //Derived timers
    59. // These get loaded with count by main program. They count to 00 and stay there
    60. // When main sees they are 00, it knows to do something
    61.  
    62. unsigned char Timer10ms_A;
    63. unsigned char Timer100ms_A;
    64.  
    65. void interrupt isr(void)
    66. {
    67.     //---------------- MAINTAIN SYSTIK  ---------------------
    68.     if(TMR1IF){
    69.         TMR1IF = 0;     // ack the IRQ
    70.  
    71.         TMR1ON = 0;     // stop the timer, reload it, restart it
    72.         TMR1 += TMR1setTiks;  // any accumulated time + the reload value
    73.         TMR1ON = 1;
    74.  
    75.         //------------- PROCESS SYSTIK  ------------------------
    76.         // do 10msec stuff here
    77.         // Flag that systik happened
    78.         ms10_happened = 1;                 // flag 10 ms happened
    79.         if (Timer10ms_A) Timer10ms_A--;   // count 10ms timer to 0
    80.  
    81.         //---------------- 100 ms TIMERS --------------------------
    82.         // 10ms systik is prescaled to 100ms..
    83.         ms100PreScaler--;
    84.         if(ms100PreScaler == 0){
    85.             ms100PreScaler = ms100PSset;        // reset prescaler
    86.             ms100_happened = 1;         // flag 100ms happened
    87.             //--------------- SERVICE 100ms TIMERS  ----------------
    88.             // These timers run at 100ms tiks.
    89.             if (Timer100ms_A) Timer100ms_A--; // maintain Timer100ms_A timer
    90.         }
    91.     } // TMR1IF
    92. }//END OF INTERRUPT SERVICE ROUTINE
    93.  
    94. void main(void) {
    95. TRISIO = 0b00001000;
    96. GPIO = 0b00000000;
    97. ANSEL = 0;
    98. CMCON0 = 0b00000111;
    99. OSCCON = 0b01110111;
    100.  
    101.     //--------------- INIT SYSTIK -------------------------------
    102.     // Timer 1 is the master timer.
    103.     // Timer 0 is not used so far.
    104.  
    105.     T1CON = T1CONinit;  // stop timer, configure it
    106.     TMR1 = TMR1setTiks; // load it
    107.                         // init timer chain
    108.     ms100PreScaler = ms100PSset;
    109.  
    110.     TMR1IF = 0;         // clear IRQ
    111.     TMR1IE = 1;         // then enable TMR1 interrupt
    112.     PEIE = 1;           // and peripherial IRQ
    113.     TMR1ON = 1;         // start the timer
    114.  
    115.     //--------------- FIRE UP THE SYSTEM -------------------------
    116.     GIE = 1;            //GLOBAL INTERRUPTS ENABLED
    117.  
    118.     while(1){
    119.         // Toggle GP1 at 500ms uning a derived timer
    120.         if(Timer10ms_A == 0){
    121.              GP1 = !GP1;
    122.              Timer10ms_A = 50;   // reload timer for next time
    123.         }
    124.  
    125.         // Toggle GP0 at 100ms using flag
    126.         if(ms100_happened){
    127.             ms100_happened = 0;  // reset flag for next time
    128.             GP0 = !GP0;
    129.         }
    130.     }// while
    131.  
    132.  
    133. }//main
    134.  
    135.  
    EDIT: An easy way to play with this is to compile it for the simulator and set breakpoints in the two 'tasks'. Click run and observe GPIO in the SFR window.
     
    Last edited: Feb 17, 2014
    takao21203 and Brownout like this.
  8. takao21203

    Distinguished Member

    Apr 28, 2012
    3,577
    463
    something I oserved for assembler programmers- comment each line even more comment that source.

    It's OK however I try to write it with variablesnames and indention so it does not need comments or just a few for the paragraphs.

    for instance v_display_refresh_phase should be clear (if you know multiplex).

    that said I also see sparely commented sources for Shaders (directx), and when you don't have the prerequisites, it can be baffling.

    Of course I write comments, but...

    Rather I tend to break up the source like in a tutorial, and write a few lines for points which are not so obvious.

    The key seems to be variable names that make sense, and to break up functions, so there is not too mmuch complexity.

    Even do a stupid or bad practice thing, if it is increasing readability.

    For instance goto in C has some use- where it can reduce line count, reduce distances, and make source easier to read.

    I say, use goto sparingly, and it helps- like MSG (monosodium glutamate).

    It is not so hard to think in C when the complexity is broken up into the right size pieces.
     
  9. Brownout

    Well-Known Member

    Jan 10, 2012
    2,375
    998
    Thanks for sharing. I assume using different methods to check for timeout between the two different derived timers is just to explore different coding schemes, or is there another reason?

    Great post. Though I use interrupts, I still struggle with how to use them most effectively.
     
    takao21203 likes this.
  10. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,386
    1,605
    Once established, the main loop has no code beyond an infinite loop, so let's ignore it. The interrupt routine does it all:
    Code ( (Unknown Language)):
    1.  
    2. void interrupt timer()                                             1
    3. {                                                                  2
    4.     PORTA=1;    // *will* turn LED on                              3
    5.     for(int i= 0; i<16;i++)                                        4
    6.     {                                                              5
    7.         while(!T0IF);    // wait here until TOIF is high           6
    8.         T0IF=0;        // clear the rollover flag                  7
    9.         TMR0=0;        // reset the timer count (redundant?)       8
    10.     }                                                              9
    11.     PORTA=PORTA<<1;    // *will* turn LED off till next rollover   10
    12. }                                                                  11
    13.  
    Yes, this is ugly code and demonstrates several bad practices. However, this may actually blink the LED as advertised. It is just "may" as the specific PIC is undisclosed so I cannot be sure if PORTA bit 0 is truly enabled for digital output, but let us assume it does and continue.

    The LED remains off until the first timer rollover and gets turned on by line 3. Then we enter the loop, which loops (0 to 15) for a total of 16 times. The first time thru TOIF is high (it's what triggered the ISR) so we drop thru line 6, reset TOIF (line 7) and reset the timer (line 8).

    Then the loop goes thru i=1, TOIF is off so we loop at line 6 till it turns back on one rollover interval later, and the same happens for 1 thru 15 (15 times) for a total delay of 15 rollovers.

    Then we drop out of the for loop, and PORTA bit zero gets reset by the shift instruction, and we drop out of the interrupt routine. We loop inside main() until the next rollover happens, when the interrupt happens again.

    SO... LED is on for 15 rollovers, off for 1. Very ugly code, two main objections:

    1: We spend lots and lots of time inside the interrupt routine. This should be a very fast small tight routine.

    2: We turn on PORTA bit 1 for no reason whatsoever.


    An improved routine would look like so:

    Code ( (Unknown Language)):
    1.  
    2. void interrupt timer()                                   1
    3. {                                                        2
    4.     static i = 0;                                        3
    5.     if (T0IF)    // test for rollover                    4
    6.     {                                                    5
    7.         T0IF = 0;     //reset rollover flag              6
    8.         if (i < 15)   // see which loop count we're at   7
    9.         {                                                8
    10.             PORTAbits.RA0 = 1;    // turn LED on         9
    11.         }                                               10
    12.         else                                            11
    13.         {                                               12
    14.             PORTAbits.RA0 = 0;    // turn LED off       13
    15.             i = 0;                // reset our count    14
    16.         }                                               15
    17.     }                                                   16
    18. }                                                       17
    19.  
    First, by directly setting PORTA bit 0 (PORTAbits.RA0) we don't change port pins we do not use.

    Then we keep the value of i each time we run the routine (that's what "static" does). Loops 0 to 14 we turn the LED on, and only loop 15 do we turn it off. On the outside the LED flashes the same rate, but we use a minimum of time to do this task. Most of the time we "waste" inside main(), which is fine because one day we'll want to do a whole lot more there.
     
  11. takao21203

    Distinguished Member

    Apr 28, 2012
    3,577
    463
    it is extremely bad because once any interrupt goes off, the app will hang until timer int is raised. It may work in a specific case, but it is considerably bad.

    The standard is to test if(TMR0IF),
    and otherwise skip.

    Eventually I did misread it so my answer was a wrong answer about wrong code...

    Years ago in assembler I did put all the code in the interrupt handler and later wondered about spurious behaviour when reusing the code.
     
  12. JohnInTX

    Moderator

    Jun 26, 2012
    2,339
    1,022
    Your assumption is correct. The code was originally written as an instructional example so different methods are explored. That's also why its heavily commented, much to the consternation of some.. In this case, the bit flags indicate that a tik has happened, sometimes that's enough - I think the original use for one was to drive a switch debouncer - tik tik tik... The countdown timers are for variable numbers of tiks - although only one value is used in the example.

    As the tasks get more complex, other constructs are used, state machines etc. but the method is the same, the tasks are 'scheduled' by the 'while' loop and are not allowed to wait or delay.

    FWIW, the reason to keep interrupt processing to a minimum in a PIC is that you only get 1 level (in midrange). While processing an interrupt, you can't be further interrupted (unless you are really careful how you do it) and so if you are number crunching or doing some time-intensive task or *horrors* a dumb delay, you can miss things like characters from the UART that can't wait.

    My reason for not doing outputs in interrupt routines with midrange/baseline is that if you ever have to shadow the ports to fix a r-m-w problem rising from flipping individual bits on a port, you have to disable interrupts in non-interrupt code before doing shadowed outputs on the same port. Bummer.

    A properly implemented interrupt-driven scheme can extend what you can do with a PICs limited resources by a great degree and as you can see from the code snippet, are not real hard to implement.

    A final bonus for a scheme like this is that the code gets back to the top of the loop very often. That is a good place to add clrwdt() so you can use the watchdog without having to sprinkle clrwdt() all over the code. Its also a good place to inspect the system settings to make sure an errant task hasn't clobbered something important (like INTCON,GIE maybe - ask me how I know these things..). Inspect and make sure that the interrupts are on, timers are running, ports are set the right way, whatever is important. Fix on the fly or flag it and reset the system as req'd.

    Have fun.
     
    Last edited: Feb 17, 2014
  13. takao21203

    Distinguished Member

    Apr 28, 2012
    3,577
    463
    put the I2C chips serial FLASH etc. on a plugboard and see what happens.

    A LEd should blink or a text be shown "error: I2C memory removed", with some fallback to a default.

    One consideration really is if some regular state of the software is essential for the comfort or even survival of animals, people or valueables.

    If not, like if you could simply observe the errand condition and press reset, you don't have to care that much, just test it properly.

    Normally you'd have more than one CPU, another for probing a regular signal or some conditions, if the frequency goes off or it disappears, you raise an alert, record it, and reset to a default.

    OP from a Chinese college. Maybe the exercize made some progress.

    2

    I have sometimes just disabled the interrupts if needed, just polled the INT flag, and I normally keep update values for ports in memory and then write to all ports at once from updated memory.

    Depends how many external chips you drive, how much MCU time you use up, and how much you multiplex the IO.

    The way of waiting for the flag in the int handler is something I'd never consider.

    Build the task scheduler, and only update the flag, and exit immediately from the int handler (keep it small).

    That way, when something goes wrong, you would always exit from it into the int handler.
     
  14. spinnaker

    AAC Fanatic!

    Oct 29, 2009
    4,866
    988

    Where was I2C chips mentioned in this post?

    And "the comfort or even survival of animals, people or valueables." What is that all about? And the rest of it for that matter? You are doing nothing but confusing the OP. All he want to do is get a simple timer 0 interrupt working.
     
  15. takao21203

    Distinguished Member

    Apr 28, 2012
    3,577
    463
    Sure you are right. How do you know it's confusing the Chinese guy?

    especially after original question was answered with enough detail for OP to go along with it, I sometimes enjoy to discuss additional, related aspects. I don't consider it hijacking of a thread.

    On the one hand people using microcontrollers are expected to get along with TFTs USB and whatnot, on the other hand supposed to be the individuals are totally easy confused with some lines of explanation they have not directly asked for in a very narrow meaning of sense.
     
Loading...