{"id":88,"date":"2015-02-15T17:13:35","date_gmt":"2015-02-15T17:13:35","guid":{"rendered":"http:\/\/techblog.auchmonoabspielbar.de\/?p=88"},"modified":"2016-06-06T19:29:26","modified_gmt":"2016-06-06T19:29:26","slug":"frequenzzaehler-periodenmesser-mit-einem-pic32-teil-3","status":"publish","type":"post","link":"http:\/\/techblog.auchmonoabspielbar.de\/?p=88","title":{"rendered":"Frequenzz\u00e4hler\/ Periodenmesser mit einem PIC32 \u2013 Teil 3"},"content":{"rendered":"<p>An diesem Wochenende hatte ich ein paar Stunden Zeit um mich um die Anzeigeroutine zu k\u00fcmmern. Ziel war es, dass die Anzeige Interruptgesteuert aus einem Bildspeicher heraus erfolgt.<\/p>\n<p>Da die Anzeige 9-stellig ist und ich \u00fcber eine 10. Stelle noch ein paar externe LEDs ansteuern m\u00f6chte, brauche ich f\u00fcr eine Wiederholrate von 100 Hertz eine Interruptrate von 1 kHz. Diese Frequenz wird intern \u00fcber den Timer1 erzeugt.<\/p>\n<p><strong>OpenTimer1(T1_ON | T1_SOURCE_INT | T1_PS_1_256, 4);<\/strong><\/p>\n<p>Die Teilerfaktoren habe ich experimentell ermittelt, sie unterscheiden sich fast um den Faktor 10 von den theoretisch notwendigen Werten. Ich habe den Verdacht, dass mein PIC im Augenblick nicht mit der vollen Taktrate l\u00e4uft. Das muss ich bei Gelegenheit mal genauer untersuchen, im Augenblick st\u00f6rt es mich aber nicht.<\/p>\n<p>Als n\u00e4chstes ben\u00f6tige ich eine Interrupt Service Routine (ISR), welche bei jedem Timer-\u00dcberlauf aufgerufen wird. Dazu muss die Routine definiert werden und der Timer-Interrupt aktiviert werden.<\/p>\n<p><strong>void __ISR(_TIMER_1_VECTOR, ipl2) Timer1Handler(void) {<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0 displayTick();<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0 mT1ClearIntFlag();<\/strong><br \/>\n<strong>}<\/strong><\/p>\n<p>Die Funktion meldet sich als ISR f\u00fcr den Timer1 an. Bei jedem Aufruf wird die n\u00e4chste Stelle angezeigt, das ganze immer im Kreis. Am Ende der Routine wird das Interrupt Flag zur\u00fcckgesetzt damit sie im n\u00e4chsten Intervall wieder aufgerufen wird. Sp\u00e4ter wird diese Routine auch noch die Abfrage der Eingabetaster durchf\u00fchren.<\/p>\n<p><strong>void startHeartbeat() {<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0 OpenTimer1(T1_ON | T1_SOURCE_INT | T1_PS_1_256, 4);<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0 INTEnableSystemMultiVectoredInt();<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0 ConfigIntTimer1(T1_INT_ON | T1_INT_PRIOR_2);<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0 mT1ClearIntFlag();<\/strong><br \/>\n<strong>}<\/strong><\/p>\n<p>Hier nun die komplette Timer-Initialisierung. Nach der Programmierung der Teilerfaktoren wird der Interrupt eingeschaltet und konfiguriert. Das war schon alles. Jetzt wird die Funktion Timer1Handler rund 1000 mal pro Sekunde aufgerufen.<\/p>\n<p>Als n\u00e4chstes kommt dann die eigentliche Anzeige in der Funktion displayTick(). Sie m\u00fcsste eigentlich die aktuelle Ziffer aus dem Bildspeicher auslesen, das daf\u00fcr ben\u00f6tigte Bitmuster f\u00fcr den Ausgabeport ermitteln (jeweils 4 Bit f\u00fcr die Siebensegmentanzeige und den Multiplexer f\u00fcr die Stellenauswahl) und dieses Muster auf den Port B ausgeben. Es ist aber verschenkte Zeit, das Bitmuster 1000 mal pro Sekunde st\u00e4ndig neu zu errechnen, deshalb wird es beim F\u00fcllen des Bildspeichers errechnet und statt einfach nur der Ziffer wird das Bitmuster dort abgelegt. Die Ausgaberoutine muss also immer nur die n\u00e4chste Stelle aus dem Bildspeicher auslesen und auf den Port ausgeben. Die Position des Ausgabecursors wird in der lokalen Variablen pos gespeichert.<\/p>\n<p><strong>static int digits[9];<\/strong><\/p>\n<p><strong>void displayTick() {<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0 static int pos = 0;<\/strong><\/p>\n<p><strong>\u00a0\u00a0\u00a0 mPORTBWrite(digits[pos]);<\/strong><\/p>\n<p><strong>\u00a0\u00a0\u00a0 pos++;<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0 if (pos &gt; 8) {<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pos = 0;<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0 }<\/strong><br \/>\n<strong>}<\/strong><\/p>\n<p>Nun fehlt noch eine M\u00f6glichkeit, den Bildspeicher zu f\u00fcllen. Als low level Funktion gibt es einen Aufruf, der die Position und einen Wert enth\u00e4lt. Diese Funktion errechnet daraus das Bitmuster f\u00fcr den Port B.<\/p>\n<p><strong>void setDigit(int pos, int value) {<\/strong><br \/>\n<strong>\u00a0 int hexDigit = (value &amp; 0xf) &lt;&lt; 7;<\/strong><br \/>\n<strong>\u00a0 int position = ((pos &amp; 1) &lt;&lt; 11) | ((pos &amp; 0xe) &lt;&lt; 12);<\/strong><br \/>\n<strong>\u00a0 int pattern = hexDigit | position;<\/strong><br \/>\n<strong>\u00a0 digits[pos] = pattern;<\/strong><br \/>\n<strong>}<\/strong><\/p>\n<p>Darauf aufbauend gibt es nun eine Funktion, die einen 32 Bit Integer Wert in den Bildspeicher eintr\u00e4gt.<\/p>\n<p><strong>void setInt(int value) {<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0 int i, j;<\/strong><\/p>\n<p><strong>\u00a0\u00a0\u00a0 for (i = 8; i &gt;= 0; i&#8211;) {<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 int part = value % 10;<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 setDigit(i, part);<\/strong><\/p>\n<p><strong>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 value = value \/ 10;<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (value == 0) {<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ clear leading digits<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 for (j = i &#8211; 1; j &gt;= 0; j&#8211;) {<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 setDigit(j, 0xf);<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 break;<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }<\/strong><br \/>\n<strong>\u00a0\u00a0\u00a0 }<\/strong><br \/>\n<strong>}<\/strong><\/p>\n<p><a href=\"http:\/\/techblog.auchmonoabspielbar.de\/wp-content\/uploads\/2015\/02\/7-Segment-Anzeige.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignfull size-full wp-image-90\" src=\"http:\/\/techblog.auchmonoabspielbar.de\/wp-content\/uploads\/2015\/02\/7-Segment-Anzeige.jpg\" alt=\"7-Segment Anzeige\" width=\"800\" height=\"533\" srcset=\"http:\/\/techblog.auchmonoabspielbar.de\/wp-content\/uploads\/2015\/02\/7-Segment-Anzeige.jpg 800w, http:\/\/techblog.auchmonoabspielbar.de\/wp-content\/uploads\/2015\/02\/7-Segment-Anzeige-300x200.jpg 300w, http:\/\/techblog.auchmonoabspielbar.de\/wp-content\/uploads\/2015\/02\/7-Segment-Anzeige-624x416.jpg 624w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/a>Beim Betrachten der Bilder ist mir aufgefallen, dass es ein sichtbares \u00dcbersprechen zwischen den einzelnen Stellen gibt. Ich vermute, dass die Abschaltung der Stelle nicht schnell genug ist und jede Stelle deshalb ein paar Mikrosekunden lang noch aktiv ist, w\u00e4hrend bereits die n\u00e4chste Stelle angezeigt wird.<\/p>\n<p>[Edit] Das habe ich mir mittlerweile mal genauer angesehen. Man bekommt den Effekt leicht per Software weg, indem man vor dem Wechsel der Stelle den Wert 0xf (alles dunkel) ausgibt und ein paar Mikrosekunden wartet. Ich vermute, dass der PNP Darlington Transistor nicht besonders schnell schaltet. Da er einen gro\u00dfen Vorwiderstand hat, dauert es vermutlich mehrere Mikrosekunden bis er abschaltet.<\/p>\n<p>&nbsp;<br \/>\n<strong><a href=\"http:\/\/techblog.auchmonoabspielbar.de\/?p=92\">Weiter zum Teil 4<\/a><\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>An diesem Wochenende hatte ich ein paar Stunden Zeit um mich um die Anzeigeroutine zu k\u00fcmmern. Ziel war es, dass die Anzeige Interruptgesteuert aus einem Bildspeicher heraus erfolgt. Da die Anzeige 9-stellig ist und ich \u00fcber eine 10. Stelle noch ein paar externe LEDs ansteuern m\u00f6chte, brauche ich f\u00fcr eine Wiederholrate von 100 Hertz eine [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-88","post","type-post","status-publish","format-standard","hentry","category-embedded"],"_links":{"self":[{"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=\/wp\/v2\/posts\/88","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=88"}],"version-history":[{"count":7,"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=\/wp\/v2\/posts\/88\/revisions"}],"predecessor-version":[{"id":180,"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=\/wp\/v2\/posts\/88\/revisions\/180"}],"wp:attachment":[{"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=88"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=88"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/techblog.auchmonoabspielbar.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=88"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}