{"id":701,"date":"2012-09-10T01:00:00","date_gmt":"2012-09-09T23:00:00","guid":{"rendered":"https:\/\/www.fussylogic.co.uk\/blog\/?p=701"},"modified":"2012-10-08T17:53:01","modified_gmt":"2012-10-08T16:53:01","slug":"cpu-interrupted-timers-ii","status":"publish","type":"post","link":"https:\/\/www.fussylogic.co.uk\/blog\/?p=701","title":{"rendered":"CPU, Interrupted &#8212; Timers II"},"content":{"rendered":"<p>When <a href=\"?p=694\">last we spoke on this subject<\/a> I had left you with a time-accurate but impractical timer interrupt handler.<\/p>\n<pre class=\"sourceCode C\"><code class=\"sourceCode c\"><span class=\"ot\">#define F_CPU 16000000<\/span>\n\n<span class=\"dt\">uint8_t<\/span> Timer_ms = <span class=\"dv\">0<\/span>;\n\n<span class=\"dt\">static<\/span> <span class=\"dt\">void<\/span> initTIMER1( <span class=\"dt\">void<\/span> )\n{\n    <span class=\"co\">\/\/ CTC mode (WGM1[3:0] = 0x04)<\/span>\n    TCCR1A = <span class=\"dv\">0<\/span>;\n    TCCR1B = _BV(WGM12);\n    <span class=\"co\">\/\/ CLK\/8 prescaler (see datasheet for table)<\/span>\n    TCCR1B |= _BV(CS11);\n\n    <span class=\"co\">\/\/ Automatic comparison register (16Mhz\/8) is a 2 MHz tick, i.e. two<\/span>\n    <span class=\"co\">\/\/ million ticks per second, or two thousand ticks per millisecond.<\/span>\n    <span class=\"co\">\/\/ Note that on AVR we get one more tick than the value we put in<\/span>\n    <span class=\"co\">\/\/ this register<\/span>\n    OCR1A = (F_CPU\/<span class=\"dv\">8<\/span>)\/<span class=\"dv\">1000<\/span> - <span class=\"dv\">1<\/span>;\n\n    <span class=\"co\">\/\/ Enable timer1 match-A interrupt<\/span>\n    TIMSK = _BV(OCIE1A);\n}\n\n<span class=\"co\">\/\/ Note the change of vector here from &quot;overflow&quot; to &quot;match&quot;<\/span>\nISR(TIMER1_COMPA_vect)\n{\n    <span class=\"co\">\/\/ 1 ms has passed<\/span>\n    Timer_ms++;\n\n    <span class=\"co\">\/\/ TIFR.OCF1A is automatically cleared when the ISR is called, so we<\/span>\n    <span class=\"co\">\/\/ needn&#39;t worry about the ISR being incorrectly retriggered<\/span>\n}<\/code><\/pre>\n<p>Why is this impractical though? Well let\u00e2\u20ac\u2122s look a little wider, what might we want to do with a timer in a main loop? Let\u00e2\u20ac\u2122s say something simple: flash an LED every quarter of a second. We want to know when the timer has changed, but we\u00e2\u20ac\u2122re only updating a counter in the ISR. We also have to be aware that <code>Timer_ms<\/code> is altered by an interrupt context, so to prevent races we shouldn\u00e2\u20ac\u2122t carelessly access it in the main loop context.<\/p>\n<p>Let\u00e2\u20ac\u2122s look at the solution.<\/p>\n<pre class=\"sourceCode C\"><code class=\"sourceCode c\"><span class=\"dt\">static<\/span> <span class=\"dt\">uint8_t<\/span> interruptTicks = <span class=\"dv\">0<\/span>;\n\nISR(TIMER1_COMPA_vect)\n{\n    interruptTicks++; \n}\n\n<span class=\"dt\">int<\/span> main( <span class=\"dt\">void<\/span> )\n{\n    <span class=\"dt\">uint8_t<\/span> localTicks = <span class=\"dv\">0<\/span>;\n    unit16_t Timer_ms = <span class=\"dv\">0<\/span>;\n    <span class=\"dt\">uint16_t<\/span> Timer_1s = <span class=\"dv\">0<\/span>;\n    <span class=\"dt\">uint8_t<\/span> Alarm_ms = <span class=\"dv\">0<\/span>;\n\n    initTimer1();\n\n    <span class=\"kw\">while<\/span>(<span class=\"dv\">1<\/span>) {\n        <span class=\"co\">\/\/ Disable interrupts as a poor-man&#39;s atomic operation<\/span>\n        cli();\n        localTicks += interruptTicks;\n        interruptTicks = <span class=\"dv\">0<\/span>;\n        <span class=\"co\">\/\/ Disabling interrupts still leaves them pending<\/span>\n        sei();\n\n        <span class=\"co\">\/\/ We could put the processor to low-power sleep here and wait<\/span>\n        <span class=\"co\">\/\/ to be woken by the timer interrupt rather than busy-waiting<\/span>\n\n        <span class=\"co\">\/\/ ----- Timer interrupt bottom half<\/span>\n\n        <span class=\"co\">\/\/ We&#39;ve now got a tick count we can use without worrying about<\/span>\n        <span class=\"co\">\/\/ race conditions, localTicks is never accessed outside this<\/span>\n        <span class=\"co\">\/\/ context<\/span>\n\n        <span class=\"kw\">if<\/span>( localTicks == <span class=\"dv\">0<\/span> )\n            <span class=\"kw\">continue<\/span>;\n\n        <span class=\"co\">\/\/ --- Timers tick upwards<\/span>\n        Timer_ms += localTicks;\n        <span class=\"kw\">if<\/span>( Timer_ms &gt; <span class=\"dv\">1000<\/span> ) {\n            Timer_ms -= <span class=\"dv\">1000<\/span>;\n            <span class=\"kw\">if<\/span>( Timer_1s &lt; <span class=\"bn\">0xffff<\/span> )\n                Timer_1s++;\n        }\n\n        <span class=\"co\">\/\/ --- Alarms tick downward<\/span>\n        <span class=\"kw\">if<\/span>( Alarm_ms &lt; localTicks ) {\n            Alarm_ms = <span class=\"dv\">0<\/span>;\n            <span class=\"co\">\/\/ Call whatever alarm handler you want here<\/span>\n        } <span class=\"kw\">else<\/span> {\n            Alarm_ms -= localTicks;\n        }\n\n        <span class=\"co\">\/\/ Everyone who needed to count ticks has counted them<\/span>\n        localTicks = <span class=\"dv\">0<\/span>;\n    }\n\n    <span class=\"kw\">return<\/span>;\n}<\/code><\/pre>\n<p>You can see that you could add any number of timers, any number of alarms, and any cascade conditions you want between them. None of this is done in the ISR and can take up to 255 ms (an age in microcontroller land), without ever missing a single tick or creating a race condition. By using interrupts for our timer we\u00e2\u20ac\u2122ve also gained the ability to go to sleep and thus use much less power than we would if we were simply polling for an overflow.<\/p>\n<p>I haven\u00e2\u20ac\u2122t done it here as it would only obfuscate matters, but there is nothing to stop you writing a generic alarm structure that contains both the alarm time and a pointer to the function to call on timeout. Oh what the heck\u00e2\u20ac\u00a6<\/p>\n<pre class=\"sourceCode C\"><code class=\"sourceCode c\"><span class=\"kw\">struct<\/span> sAlarmStructure\n{\n    <span class=\"dt\">uint16_t<\/span> AlarmTime_ms;\n    <span class=\"dt\">void<\/span> (*timeoutCallback)(<span class=\"dt\">uint8_t<\/span>);\n    <span class=\"dt\">void<\/span> *opaque:\n} AlarmList[MAX_ALARMS];\n\n<span class=\"dt\">uint8_t<\/span> registerTimer( <span class=\"dt\">uint16_t<\/span> timeout, <span class=\"dt\">void<\/span> (*callback)(<span class=\"dt\">uint8_t<\/span>), <span class=\"dt\">void<\/span> *opaque )\n{\n    <span class=\"dt\">uint8_t<\/span> i;\n    <span class=\"kw\">for<\/span>( i = <span class=\"dv\">0<\/span>; i &lt; MAX_ALARMS; i++ ) {\n        <span class=\"co\">\/\/ Timers <\/span>\n        <span class=\"kw\">if<\/span>( AlarmList[i].AlarmTime_ms == <span class=\"dv\">0<\/span> ) {\n            AlarmList[i].AlarmTime_ms = timeout;\n            AlarmList[i].timeoutCallback = callback;\n            AlarmList[i].opaque = opaque;\n            <span class=\"kw\">return<\/span> i;\n        }\n    }\n    <span class=\"co\">\/\/ No spare timer slots left<\/span>\n    <span class=\"kw\">return<\/span> -<span class=\"dv\">1<\/span>;\n}\n\n<span class=\"dt\">int<\/span> main( <span class=\"dt\">void<\/span> )\n{\n    <span class=\"dt\">uint8_t<\/span> localTicks = <span class=\"dv\">0<\/span>;\n    <span class=\"dt\">uint8_t<\/span> i;\n\n    initTimer1();\n\n    <span class=\"kw\">while<\/span>(<span class=\"dv\">1<\/span>) {\n        <span class=\"co\">\/\/ Disable interrupts as a poor-man&#39;s atomic operation<\/span>\n        cli();\n        localTicks += interruptTicks;\n        interruptTicks = <span class=\"dv\">0<\/span>;\n        <span class=\"co\">\/\/ Disabling interrupts still leaves them pending<\/span>\n        sei();\n\n        <span class=\"kw\">for<\/span>( i = <span class=\"dv\">0<\/span>; i &lt; MAX_ALARMS]; i++ ) {\n            <span class=\"kw\">if<\/span>( AlarmList[i].AlarmTime_ms &gt; localTicks ) {\n                AlarmList[i].AlarmTime_ms -= localTicks;\n            } <span class=\"kw\">else<\/span> {\n                AlarmList[i].AlarmTime_ms = <span class=\"dv\">0<\/span>;\n                <span class=\"co\">\/\/ The callback function can restart itself if it wants<\/span>\n                <span class=\"co\">\/\/ because we&#39;re handing it its own AlarmList index<\/span>\n                AlarmList[i].timeoutCallback(i);\n            }\n        }\n\n        <span class=\"co\">\/\/ Rest of main loop can call registerTimer() when it needs to<\/span>\n        <span class=\"co\">\/\/ defer a task<\/span>\n    }\n\n    <span class=\"kw\">return<\/span>;\n}<\/code><\/pre>\n<p>So we\u00e2\u20ac\u2122ve gone from not being able to rely on the exact number of counts between events to a generic, run-time configurable alarm system. We\u00e2\u20ac\u2122ve got a fast ISR and no race condition.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When last we spoke on this subject I had left you with a time-accurate but impractical timer interrupt handler. #define F_CPU 16000000 uint8_t Timer_ms = 0; static void initTIMER1( void ) { \/\/ CTC mode (WGM1[3:0] = 0x04) TCCR1A = 0; TCCR1B = _BV(WGM12); \/\/ CLK\/8 prescaler (see datasheet for table) TCCR1B |= _BV(CS11); \/\/\u2026 <span class=\"read-more\"><a href=\"https:\/\/www.fussylogic.co.uk\/blog\/?p=701\">Read More &raquo;<\/a><\/span><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[39,37,41,38,42,6,40],"_links":{"self":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/701"}],"collection":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=701"}],"version-history":[{"count":8,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/701\/revisions"}],"predecessor-version":[{"id":813,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/701\/revisions\/813"}],"wp:attachment":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=701"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=701"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=701"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}