{"id":204,"date":"2015-12-29T14:04:50","date_gmt":"2015-12-29T14:04:50","guid":{"rendered":"http:\/\/ceptimus.co.uk\/?p=204"},"modified":"2022-06-03T10:58:38","modified_gmt":"2022-06-03T09:58:38","slug":"pov-source-code-part-2","status":"publish","type":"post","link":"https:\/\/ceptimus.co.uk\/index.php\/2015\/12\/29\/pov-source-code-part-2\/","title":{"rendered":"POV source code &#8211; part 2"},"content":{"rendered":"<h2 style=\"text-align: center;\">Clock interrupt<\/h2>\n<p>The STC89C52 has three timer\/counters (most of the 8051 chips have the first two, and the third one is pretty common).&nbsp; One of the modes for timer 1 is to count clock pulses in an 8-bit register and when FF is reached, reload the count register from a separate 8-bit register.&nbsp; Each time this overflow is reached an interrupt is generated, and we can use this to provide a regular clock &#8216;tick&#8217;.<\/p>\n<p>The crystal frequency is divided by 6 when the chip is set to fast mode and by 12 when it&#8217;s set to standard (slow) mode.&nbsp; The crystal oscillates at 11.0592 MHz so assuming we&#8217;re using fast mode the slowest rate we can generate interrupts this way would be to set the timer reload value to zero and then we&#8217;d get:<\/p>\n<p>(11059200 \/ 6) \/ 256 interrupts per second = 7200 Hz.<\/p>\n<p>I wanted the interrupts to happen a little faster than that as I was also intending to piggy-back the LED switching from the interrupt routine, so I chose a reload value of 0x40 (64) which results in 9600 interrupts per second.<\/p>\n<p>The set-up code to initialize the timer is as follows:<\/p>\n<p>\/\/ set up timer 1 interrupt for 9.6 kHz (assuming 11.0592 MHz xtal)<br \/>\nTMOD = 0x20; \/\/ timer 1 in 8-bit auto-reload mode<br \/>\nTH1 = 0x40; \/\/ reload value to give 9.6 kHz in &#8216;fast&#8217; (6T) mode<br \/>\nET1 = 1; \/\/ enable timer 1 interrupt<br \/>\nEA = 1; \/\/ enable interrupts<br \/>\nTR1 = 1; \/\/ run timer 1<\/p>\n<p>The first part of the interrupt handler just counts the interrupts and uses them to advance the clock: (seconds, minutes, hours)<\/p>\n<pre>unsigned int fracSecs; \/\/ fractions of second in ticks\n\nvoid T1INT(void) interrupt 3 using 1 { \/\/ configured to fire @ 9.6 kHz\n&nbsp;&nbsp; &nbsp;if (++fracSecs == 9600) { \/\/ one second elapsed\n&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;fracSecs = 0;\n&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if (++topRow[7] == 26) { \/\/ units seconds overflow\n&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;topRow[7] = 16;\n&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if (++topRow[6] == 22) { \/\/ tens seconds overflow\n&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;topRow[6] = 16;\n&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if (++topRow[4] == 26) { \/\/ units minutes overflow\n&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;topRow[4] = 16;\n&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if (++topRow[3] == 22) { \/\/ tens minutes overflow \n&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;topRow[3] = 16;\n                        \/\/ check for midnight\n&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if (++topRow[1] == 20 &amp;&amp; topRow[0] == 18) {\n&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;topRow[0] = topRow[1] = 16;\n&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;} else if (topRow[1] == 26) { \/\/ 10am or 8pm\n&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;topRow[1] = 16;\n&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;++topRow[0];\n&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}\n&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}\n&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}\n&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}\n&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}\n&nbsp;&nbsp; &nbsp;}\n}<\/pre>\n<p>It&#8217;s a little confusing as the routine directly manipulates &#8216;topRow&#8217; which is a 16-character buffer holding the data to display on the top row of the display in the format: <span style=\"font-family: 'Courier New';\">&#8220;HH:MM:SS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8220;<\/span> except that the character values are not ASCII but (ASCII &#8211; 32) to suit the way the font is defined.<\/p>\n<p>So the first check (topRow[7] == 26) checks if the units seconds character has reached ASCII 58 (a colon &#8211; which is the character that comes after &#8216;9&#8217;) and if so it resets it back to 16 (ASCII 48 to display a &#8216;0&#8217;) and increments topRow[6] which is the tens of seconds character, and so on&#8230;<\/p>\n<p>So why did I choose 9.6kHz?&nbsp; Well it was a compromise to do with the rate that the LEDs spin round at, and wanting to get a reasonably well-defined display only using a single interrupt source and without overloading the microcontroller with too many interrupts.&nbsp; Actually when I moved onto the bottom row display, I used a second interrupt source auto-adjusted to give exactly 256 interrupts per rotor revolution &#8211; but more on that later&#8230;<\/p>\n<p>When powered with a nominal five volts, the board spins about 900 rpm (revolutions per minute).&nbsp; The lowest voltage my kit would operate at without the microcontroller &#8216;browning out&#8217; and resetting was about 3.3V which gave 700 rpm.&nbsp; It&#8217;s unreliable at this low speed &#8211; if you try to switch too many LEDs on at the same time the extra current drags down the voltage on the board to the point where the microcontroller resets itself.&nbsp; The &#8216;regulator&#8217; on the board is a little sketchy &#8211; just a zener diode to bleed away current when the voltage begins to exceed 5.1V &#8211; so I&#8217;ve not risked going above 6V on the power supply at which voltage the motor still only spins about 950 rpm.<\/p>\n<p>My program displays 32 characters around the circumference of the cylinder drawn out by the LEDs and with 8 pixel width characters at 900 rpm that gives a pixel rate of 900\/60 x 32 x 8 = 3840 pixels per second.&nbsp; As the voltage varies or the rotor is affected by draughts or similar, the rate can vary in the range roughly 3000 to 4000 pixels per second.<\/p>\n<p>So at an interrupt rate of 9.6kHz we need to output a fresh set of pixels roughly every two-and-a-half interrupts (9600 \/ 3840).&nbsp; We can&#8217;t do half-interrupts, of course, so the program needs to use a mixture of two, three, maybe even four, interrupts between each update &#8211; and spread them out in such a way as to provide a fairly regular and stable display.<\/p>\n<p>As the rotor passes a reference position each revolution, it picks up an &#8216;index pulse&#8217; from a photodiode illuminated by a stationary infra red LED &#8211; so it can use that information to recalculate the correct ratio of two interrupt\/three interrupt cycles to keep the display fairly steady.<\/p>\n<p>It sounds like a lot of calculation, maybe involving division (which is very slow on this chip as it doesn&#8217;t have multiply or divide instructions so it has to &#8216;long division&#8217; by repeated subtraction\/addition and bit-shifts).&nbsp; Luckily there is a faster way just involving addition and bit shifts, although it&#8217;s a little confusing until you&#8217;ve studied it for a while.<\/p>\n<p>The basic idea is to have a 32-bit counter which we try to get to count up to 0x01000000 (16777216) for each rev of the rotor.&nbsp; We do this by adding an amount to the counter each time there is an interrupt.&nbsp; If the rotor speed were exactly 900 rpm (fifteen revs per second) then we&#8217;re going to get 9600 \/ 15 interrupts&nbsp; (640) for each revolution of the rotor so to count up to our target number we add 0x01000000 \/ 640 (which is about 26214)&nbsp; every interrupt.<\/p>\n<p>Now when the index pulse comes around we can see if we&#8217;ve counted past our target number or not reached it &#8211; and the error between the actual count and the target can be used to adjust our 26214 value up or down for the next revolution.&nbsp; If you just divide the error by 1024 and add that value to the &#8216;26214&#8217; then the counter quickly locks into counting to the correct target value after a few revolutions &#8211; and then it automatically adjusts the rate to compensate for any changes in the rotor speed.&nbsp; Dividing by 1024 is something the microcontroller can do pretty quickly &#8211; just by bit shifts.&nbsp; In fact because the 32-bit count is held in four 8-bit registers, the correction calculation can be done with just&nbsp; a 2-bit shift on a 16-bit quantity and a 16-bit subtraction.<\/p>\n<p>Looking at the 32-bit count as four bytes, we see that the most significant byte is targeted to count up to exactly &#8216;1&#8217; per revolution, which means that the next most significant byte counts from 0 to 255 per revolution with the 0 aligned with the index pulse &#8211; so we can just look at this byte to give us the current position around the circle in &#8220;<a href=\"https:\/\/en.wikipedia.org\/wiki\/Binary_scaling#Binary_angles\" target=\"_blank\" rel=\"noopener\">brads<\/a>&#8221; (one brad = 1\/256 of a circle or 1.40625 degrees).<\/p>\n<p>That should be enough explanation to allow you to understand the source code which you can see\/download: <a href=\"https:\/\/ceptimus.co.uk\/POVclockMain.c\" target=\"_blank\" rel=\"noopener\">here<\/a>.<\/p>\n<p>I&#8217;ve not covered how the second interrupt source for the lower row of text works yet though &#8211; that&#8217;s for another post&#8230;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Clock interrupt The STC89C52 has three timer\/counters (most of the 8051 chips have the first two, and the third one is pretty common).&nbsp; One of the modes for timer 1 is to count clock pulses in an 8-bit register and when FF is reached, reload the count register from a separate 8-bit register.&nbsp; Each time [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,9],"tags":[],"class_list":["post-204","post","type-post","status-publish","format-standard","hentry","category-programming","category-stc-micro"],"_links":{"self":[{"href":"https:\/\/ceptimus.co.uk\/index.php\/wp-json\/wp\/v2\/posts\/204","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ceptimus.co.uk\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ceptimus.co.uk\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ceptimus.co.uk\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ceptimus.co.uk\/index.php\/wp-json\/wp\/v2\/comments?post=204"}],"version-history":[{"count":1,"href":"https:\/\/ceptimus.co.uk\/index.php\/wp-json\/wp\/v2\/posts\/204\/revisions"}],"predecessor-version":[{"id":597,"href":"https:\/\/ceptimus.co.uk\/index.php\/wp-json\/wp\/v2\/posts\/204\/revisions\/597"}],"wp:attachment":[{"href":"https:\/\/ceptimus.co.uk\/index.php\/wp-json\/wp\/v2\/media?parent=204"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ceptimus.co.uk\/index.php\/wp-json\/wp\/v2\/categories?post=204"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ceptimus.co.uk\/index.php\/wp-json\/wp\/v2\/tags?post=204"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}