Arduino timer interrupt

Discussion in 'Embedded Systems and Microcontrollers' started by gilley, Sep 6, 2016.

  1. gilley

    Thread Starter New Member

    Nov 23, 2014
    23
    0
    I have found the example for blinking led using timer interrupt.

    Code (Text):
    1. /*
    2. * Arduino 101: timer and interrupts
    3. * 2: Timer1 overflow interrupt example
    4. * more infos: http://www.letmakerobots.com/node/28278
    5. * created by RobotFreak
    6. */
    7.  
    8. #define ledPin 13
    9.  
    10. void setup()
    11. {
    12.   pinMode(ledPin, OUTPUT);
    13.  
    14.   // initialize timer1
    15.   noInterrupts();           // disable all interrupts
    16.   TCCR1A = 0;
    17.   TCCR1B = 0;
    18.  
    19.   TCNT1 = 34286;            // preload timer 65536-16MHz/256/2Hz
    20.   TCCR1B |= (1 << CS12);    // 256 prescaler
    21.   TIMSK1 |= (1 << TOIE1);   // enable timer overflow interrupt
    22.   interrupts();             // enable all interrupts
    23. }
    24.  
    25. ISR(TIMER1_OVF_vect)        // interrupt service routine that wraps a user defined function supplied by attachInterrupt
    26. {
    27.   TCNT1 = 34286;            // preload timer
    28.   digitalWrite(ledPin, digitalRead(ledPin) ^ 1);
    29. }
    30.  
    31. void loop()
    32. {
    33.   // your program here...
    34. }

    In the code we can see that TCNT1 is set to 34286, but I wanna know if there is any way to change that withing program. For example that after LED blinks 5 times, I wanna set TCNT1 to 0 so I have longer "blinking" period.

    Is this possible using hardware interrupt or somehow withing software ?

    Thank you
     
  2. JohnInTX

    Moderator

    Jun 26, 2012
    2,339
    1,022
    I don't see why you couldn't.
    The timer is initialized at line 19 and reloaded at line 27 after it rolls over by the interrupt routine.
    Load a counter to 5 when you init the timer. On every interrupt, check it for zero. If not zero, reload the timer to the short value (34286). When the counter runs to zero, stop decrementing and load the timer to the fast value:

    Code (C):
    1.  // in the interrupt routine..
    2.  
    3.   digitalWrite(ledPin, digitalRead(ledPin) ^ 1); // toggle LED on interrupt
    4.  
    5.   if (flash_counter == 0)  // reload timer
    6.     TCNT1 = 0;   // slow blink
    7.   else {
    8.     flash_counter--;  // decrement counter towards 0
    9.     TCNT1 = 34286; //fast blink
    10.    }
    11.  
    Good luck!
     
  3. dannyf

    Well-Known Member

    Sep 13, 2015
    1,768
    357
    Setting tcnt1 to 34286 is much harder to read than to set it to -30000 which is the period you are going after. You can think of it as an up counter that counts from a negative value and overflows to zero.

    No reason to set it to zero as the counter naturally rolls over.
     
  4. gilley

    Thread Starter New Member

    Nov 23, 2014
    23
    0

    Thank you, I understand it now.

    I also have one more question. I have noticed that I have two registers ( If I am using timer1 I have OCR1A and OCR1B ) that can be set to some values, and when timer1 counts up to that value it calls ISR(TIMER1_COMPx_vect). My question is what can I do If I need to interrupt it more than two times ?

    For example , I want to call ISR when timer1 is at value 100, 1000 and 10000 ( random numbers). What are my options then if I only have 2 compare registers ?

    Thank you.
     
  5. JohnInTX

    Moderator

    Jun 26, 2012
    2,339
    1,022
    One option would be to load one of the compare registers with the next value less any accumulated time. For example, at 100, add 900 to the compare value (1000-the 100 already counted) then at the next interrupt add 9000 and so on.

    What are you trying to do? Flash one LED in a sequence, multiple LEDs at different rates or ?? If we have a better idea of the final goal, we can come up with a good solution.
    Note that one timer / compare / interrupt setup can be used to generate a system TIK (a small, repeating time interval) . By running counters off the system TIK you can derive as many discrete timed events as you wish.

    Good luck!
     
  6. gilley

    Thread Starter New Member

    Nov 23, 2014
    23
    0
    Actually no, I just took LED diodes as basic example.


    [​IMG]


    What I want to do is in picture above. Each of this rectangles is one arduino pin turned on and off. Green one is the longest and last for T/2, and after that he is turned off and another green one (different arduino pin, but it will turn on electric current of opposite direction). Red one starts at T/8 and ends at 3T/8 and so on. So one arduino pin is for, let's call it "+voltage" and one is for "-voltage" ( I am aware that arduino digital pins cannot give negative voltage ). That sums up one phase, and I have to have 3 phases ( main goal is to control 3 phase asynchronous motor and I have hardware for it). All phases look the same and next one starts on T/3 ( 120 degrees moved), and the last phase starts at T/6 (240 degrees moved ). So basically I need to have 18 "points" in timer/counter where I will turn digitals pins on and off. Some of these points can be used to turn more of pins on and off, but 18 is the worst case.


    Also one more thing is that is has to be scalable, so I have to be able to make period narrower or wider without messing up the signals. I tough about using resistor pot on analog input and by changing his value also change the period of phases.

    For test I have used LED diode (and of course, much longer period, so that I am able to see LED blinking ) and by using your advice you gave me earlier I managed to change blinking time of LED diode.

    Now the only thing that bothers me is how to make these points where timer will call ISR to turn off or on digital pins. To be precise the problem is that I have to have 18 of them, while I only have 2 registers to compare values.

    If possible it would be good to have some mean to change time of period ( shorter or longer ) but main concern is to make it work on 100kHz.


    Thank you.
     
    Last edited: Sep 7, 2016
  7. JohnInTX

    Moderator

    Jun 26, 2012
    2,339
    1,022
    For something like this, I usually make an array of chars (1 byte each) that represent a pattern of LEDs for each time slice. On each interrupt, pull the next pattern and jam it onto the port pins.

    Suppose your LEDs were connected to the same port like this:
    Green to bit 01h
    Orange to bit 02h
    Blue to bit 04h
    The first few entries in the array would be (reading your timeline left to right)
    01h Green ON (port bit 01h on, others off)
    03h Green + Orange ON
    07h Green + Orange + Blue ON
    07h (4T/16 is the same)
    03h Green + Orange ON
    01h Green ON
    .. continue for the other half. If those are different LEDs they might be weighted 08h, 10h, 20h depending on the port pins they are connected to.

    The array would be declared like:
    unsigned char LEDpatterns[] = {0x01,0x03,0x07,0x03,0x01}; // for the first half, continue in the same declaration for the 2ed half.

    Then just pull the next pattern for each interrupt tik and write it to the port. Change the interrupt timer setting to change the speed without changing the pattern. Wrap the array index to 0 when you reach the end.

    Nice!
     
  8. gilley

    Thread Starter New Member

    Nov 23, 2014
    23
    0
    Thank you.

    Could you just elaborate this bit more ? Because I'm still not sure how to make more than 2 interrupts. Where could I find example how to use/generate system TIK for what I wanna do ?

    Thank you
     
  9. dannyf

    Well-Known Member

    Sep 13, 2015
    1,768
    357
    The interrupt names are ikb the header file. The actual mechanism in the data sheet.
     
  10. JohnInTX

    Moderator

    Jun 26, 2012
    2,339
    1,022
    You only need one periodic interrupt - the system TIK. The single service routine maintains the array index. On interrupt it reads array[index], writes the port and bumps/wraps the index for the next time. Changing the speed of the periodic interrupt changes how fast the LED pattern changes.
     
  11. gilley

    Thread Starter New Member

    Nov 23, 2014
    23
    0
    I have come up with this

    Code (Text):
    1.  
    2. unsigned int T=65535;
    3. unsigned char LEDpatterns[]={0x01,0x03,0x07,0x03,0x01,0x08,0x18,0x78,0x18,0x08};
    4. unsigned int TimerInterrupts[]={0,T/8,(3*T)/16,(5*T)/16,(3*T)/8,
    5. T/2,(5*T)/8,(11*T)/16,(13*T)/16,(7*T)/8,T};
    6. //period in counter
    7. int counter =0;
    8.  
    9.  
    10. void setup(){
    11.  
    12.   noInterrupts();           // disable all interrupts
    13.   TCCR1A = 0;
    14.   TCCR1B = 0;
    15.  
    16.   TCNT1 = 0;    // set timer/counter 1 to zero
    17.  
    18.   DDRC = 0xff;    //define portC
    19.   TCCR1B |= (1 << CS12);
    20.   TCCR1B |= (1 << CS10);  // 256 prescaler
    21.   OCR1B = 0;
    22.   TIMSK1 |= (1 << OCIE0B);  //register to compare to
    23.  
    24.   interrupts();             // enable all interrupts
    25.  
    26.  
    27. }
    28.  
    29.  
    30.   ISR(TIMER1_COMPB_vect){
    31.  
    32.    PORTC = LEDpatterns[counter];
    33.    OCR1B = TimerInterrupts[counter];
    34.    if(counter<9)
    35.       counter++;
    36.    else
    37.       counter=0;
    38.   }
    39.  
    40.  
    41.  
    42.  
    43. void loop(){
    44.  
    45.   }

    I have implemented your advice of LEDpatterns in array. It is actually very good idea. I have also used one array for setting OCR1B for next time at which interrupt should happen. Code is currently controling only one phase.

    I have tested it with led diodes , but it is kinda hard to tell does it work 100% ( I can definitely see pattern is right, but not sure about time periods ). I will try to get my hands on oscilloscope to test it properly.

    Just one last question. How would you implement control of period without messing up signals. I have tough about using linear pot and reading analog value, and according to it setting up T. Would that mess up signals if I put it in void loop?


    EDIT:

    I have noticed one more problem that is bugging me.

    TCNT1 is 16bit timer with max value 65535.
    When I declare T as 65535 ( period length of 65535 counts ) everything is working great. But when I set for example T=32000, then on last iteration I have delay before again reading array from 0 again.

    I think it has something to do with TCNT1 not overflowing to 0, as it happens when he reaches 65535, but when he reaches 32000 he simply keeps counting until overflowing (aka reaching 65535).

    I have tried adding TCNT1=0 in else condition but without luck.

    Any advices ?

    Thank you.


    EDIT2:

    OK I got it.

    Code (Text):
    1. //Digital pin Mapping
    2. /*
    3. 35 -C2
    4. 36 -C1
    5. 37 -C0
    6. 38 -D7
    7. 39 -G2
    8. 40 -G1
    9.  
    10. First phase is using portC
    11.  
    12. Port numbers go from 0 to 7
    13.  
    14. DDRx is data direction registr and is used to setup pins as input
    15. or output (1 for output, 0 for input).
    16.  
    17. PORTx is for accesing whole port, PINx is for acessing specific pin
    18.  
    19. */
    20.  
    21. unsigned int T=267;
    22. unsigned char LEDpatterns[]={0x01,0x03,0x07,0x03,0x01,0x08,0x18,0x78,0x18,0x08};
    23.   unsigned int TimerInterrupts[]={0,(T/8),((T/16)*3),((T/16)*5),((T/8)*3),
    24. (T/2),((T/8)*5),((T/16)*11),((T/16)*13),((T/8)*7),T};
    25. //period in counter
    26. int counter =0;
    27. void setup(){
    28.  
    29.   noInterrupts();           // disable all interrupts
    30.  
    31.  
    32.  
    33.   TCCR1A =0;
    34.   TCCR1B = 0;
    35.  
    36.  
    37.  
    38.   DDRC = 0xff;    //define portC
    39.   TCCR1B |= (1<<CS12) |(1<<CS10) | (1<<WGM12) ; // 256 prescaler
    40.   //WGM12 compares to 0CR1A !!!!!!!!!!!!!!!!!
    41.    OCR1B =0;
    42.    OCR1A = T+1;
    43.  
    44.  
    45.  
    46.   TIMSK1 |= (1 << OCIE0B);  //register to compare to
    47.   TIMSK1 |= (1 << OCIE0A);
    48.   TCNT1 = 0;    // set timer/counter 1 to zero
    49.    interrupts();       // enable all interrupts
    50.  
    51. }
    52.  
    53.  
    54. ISR(TIMER1_COMPB_vect){
    55.  
    56.    PORTC = LEDpatterns[counter];
    57.    OCR1B = TimerInterrupts[counter];
    58.    if(counter<9){
    59.       counter++;
    60.    }
    61.    else{
    62.       counter=0;
    63.    
    64.    
    65.    }
    66.  
    67.    
    68.   }
    69.  
    70.  
    71.  
    72.  
    73. void loop(){
    74.  
    75.  
    76.   }
    77.  
    78.  

    So the only problem I have now is how to scale T using some hardware(button, linear pot, etc ).

    Much appreciated :):):):):):):)
     
    Last edited: Sep 8, 2016
Loading...