https://rn-wissen.de/wiki/api.php?action=feedcontributions&user=Besserwessi&feedformat=atomRN-Wissen.de - Benutzerbeiträge [de]2024-03-29T07:28:58ZBenutzerbeiträgeMediaWiki 1.25.1https://rn-wissen.de/wiki/index.php?title=Operationsverst%C3%A4rker&diff=27243Operationsverstärker2015-11-09T17:48:20Z<p>Besserwessi: /* Stabilität in OP Schaltungen */ Umordung</p>
<hr />
<div>== Operationsverstärker Grundschaltungen ==<br />
<br />
== Verstärker ==<br />
<br />
Häufig müssen Sensorsignale in der ersten Stufe der Verarbeitung verstärkt werden und bei Spannungen von Meßbrücken wird die verstärkte Differenzspannung als Spannung gegen Masse benötigt. Schaltungen mit Operationsvertärkern die diese Aufgabe erfüllen werden hier dargestellt. <br />
<br />
Operationsverstärker werden zunächst als ideale Operationsverstärker betrachtet, das heißt sie haben eine unendlich hohe Verstärkung. Die Ausgangsspannung ist damit um einen sehr großen Faktor größer als die Differenz der Eingangsspannungen. In Wirklichkeit liegt der Faktor immerhin bei 10<sup>5</sup> bis 10<sup>6</sup>. <br />
<br />
Wird der Ausgang über einen Widerstand auf den negativen Eingang zurückgekoppelt, dann bewirkt diese Gegenkopplung, dass die Differenzspannung an den Eingängen (Ue+ - Ue-) zu null wird und die Verstärkung der Schaltung aus Operationsverstärker und Gegenkopplung endlich wird. Solche Schaltungen haben dann eine sehr präzise Verstärkung deren Wert nur durch den Wert der Widerstände bestimmt ist. Für die Betrachtung von idealen Operationsverstärkern gilt außerdem, dass in die Eingänge des Operationsverstäkers kein Strom fließt und dass der Ausgang den Innenwiderstand null hat. <br />
<br />
<br />
[[Bild:OperationsverstaerkerBild1.gif.gif]] <br />
<br />
Bild 1 zeigt die Schaltung für positive Verstärkung Bild 2 die Schaltung für negative Verstärkung. <br />
<br />
Die Beiden Schaltungen haben die gleiche Konfiguration, es wird nur jeweils der andere Eingang an Masse geschaltet. Mit U1 am positiven Eingang und U2 am negativen Eingang wird in beiden Fällen für die Ausgangsspannung Ua, der in Gleichung 2 angegebene Wert, erreicht. <br />
Setzt man U1 oder U2 gleich 0, dann erhält man die Ausgangsspannung für den positiven und den negativen Verstärker.<br />
<br />
Allgemein:<br />
<br />
Ua = U1 ( 1 + R2 / R1 ) - U2 R2 / R1 <br />
<br />
<!-- Das funktioniert nicht mit allen Browsern, auskommentiert (SprinterSB) --><br />
<!--/div--><br />
<br />
<br />
<!-- Das funktioniert nicht mit allen Browsern, auskommentiert (SprinterSB) --><br />
<!--div style="border:2px solid #ffd700; margin-left:auto; margin-right:auto; padding:0.3em; text-align:left; max-width:20em;"--><br />
<br />
== Differenzverstärker ==<br />
<br />
Um die Differenz zwischen zwei Spannungen am Ausgang gegen Masse zu erhalten wird die Schaltung in Bild 2 um einen Spannungsteiler am + Eingang erweitert siehe Bild 3. Die Eingangsspannung am Spannungsteiler heißt nun U1 und die Spannung am +Eingang (wie auch am -Eingang) ist Ue.<br />
<br />
[[Bild:Operationsverst%C3%A4rkerBild3.gif]] <br />
<br />
<br />
'''Damit gilt für die Schaltung in Bild3:''' <br />
<br />
Ua = Ue + (Ue - U2) * R2 / R1<br />
<br />
Ua = Ue * (R1 + R2) / R2 - U2 * R2 / R1 <br />
<br />
mit Ue = U1 * R2 / (R1 + R2) vereinfacht sich der Ausdruck zu: <br />
<br />
'''Ua = (U1 - U2) * R2 / R1'''<br />
<br />
<br />
Das heißt, dass die Ausgangsspannung gerade die Differenz der Eingangsspannungen mal dem Widerstandsverhältnis R2/R1 ist. Für große Widerstandswerte ist die Schaltung in Bild 3 schon einsetzbar, bei hoher Verstärkung und kleinen Werten für R1 ist es besser, die Eingänge hochohmig zu machen. <br />
<br />
<br />
Es wäre vorteilhaft die Messspannungen direkt an die hochohmigen Operationsverstärker-Eingänge zu legen. Beim + Eingang ist es ja die geteilte Spannung U1 die am + Eingang anliegt. Legt man sie direkt, ohne Teiler an den + Eingang, und verstärkt die Spannung U2 um den gleichen Faktor durch einen Verstärker nach Bild 2, dann ergibt sich am Ausgang wieder die Differenz von U1-U2 verstärkt um den Faktor (R1+R2)/R1. <br />
<br />
'''Für R1=R2 ergibt sich damit für die Schaltung in Bild 4''' <br />
<br />
'''Ua = 2 * (U1 - U2)''' <br />
<br />
<!-- Das funktioniert nicht mit allen Browsern, auskommentiert (SprinterSB) --><br />
<!-- </div> --><br />
<br />
<br />
<!-- Das funktioniert nicht mit allen Browsern, auskommentiert (SprinterSB) --><br />
<!--div style="border:2px solid #ffd700; margin-left:auto; margin-right:auto; padding:0.3em; text-align:left; max-width:20em;"--><br />
<br />
== Differenzverstärker mit einstellbarer Verstärkung ==<br />
<br />
Die Differenzverstärkerschaltung mit vier gleichen Widerständen R2 ist sehr gut für die Realisierung einer präzisen Verstärkung geeignet. Zur Erhöhung der Verstärkung ist es von Vorteil, wenn der Wert der Verstärkung mit nur einem Widerstand eingestellt werden kann. Hierfür wird ein Widerstand mit dem Wert R1 zwischen den Minus-Eingängen der beiden Verstärker eingefügt. Die Schaltung entspricht dann der Anordnung in Bild 5, machmal wird sie auch in der Form von Bild 6 dargestellt. <br />
<br />
[[Bild:Operationsverst%C3%A4rkerBild5.gif]] <br />
<br />
Hier überbrückt der Widerstand R1 die beiden Widerständ am Ausgang des linken Operationsverstärkers. Diese Kombination aus drei Widerstanden kann man zur Berechnung der Verstärkung von einem Stern in ein Dreick umwandeln dann hat jeder der beiden Widertände die nicht mit dem Ausgang verbunden sind den Wert R1*R2/(R1+2R2). Daraus errechnet sich die Verstärkung zu:<br />
<br />
'''Ua = 2*(U1 -U2) * (R1 + R2) / R1''' <br />
<br />
Ein einfacher Ausdruck der nur von der Differenz der Eingangsspannugen abhängt und der mit Änderung von R1 in der Amplitude einstellbar ist.<br />
<br />
<!-- Das funktioniert nicht mit allen Browsern, auskommentiert (SprinterSB) --><br />
<!--/div--><br />
<br />
<!-- Das funktioniert nicht mit allen Browsern, auskommentiert (SprinterSB) --><br />
<!--div style="border:2px solid #ffd700; margin-left:auto; margin-right:auto; padding:0.3em; text-align:left; max-width:20em;"--><br />
<br />
== Symmetrische Differenzverstärker ==<br />
<br />
Nun ist die Schaltung fast perfekt, in einigen Fällen ist jedoch auch die Laufzeit der Signale wichtig und es fällt auf, dass die beiden Eingangsgrößen U1 und U2 unterschiedlich lange Wege durch die Schaltung nehmen. <br />
<br />
Um dies auszugeichen geht man auf die Schaltung in Bild 3 zurück und versieht beide Eingänge in gleicher Weise mit Verstärkern nach Bild 1 und erhält die Konfiguration in Bild 7. Um die vielen Widerständ nicht einzeln zu benennen wird hier eine aus dem Farbcode abgeleitete Bezeichnung verwendet R1 = braun und R2 = rot. <br />
<br />
[[Bild:Operationsverst%C3%A4rkerBild7.gif]] <br />
<br />
In der ersten Stufe darf man bei dieser Anordnung die Verstärkung nicht zu groß wählen damit sie nicht intern übersteuert wird. Soll beispielsweise die Spannug 5V mit 5.01V verglichen und das Ergebnis 100fach verstärkt werden, dann kann man schlecht in der ersten Stufe die Spannungen auf 500V und 501V verstärken. Man kann dann in der der ersten Stufe die Verstärkung auf 1 oder wie hier auf 2 beschränken und die Verstärkung in der zweiten Stufe realisieren. <br />
<br />
Besser ist, wenigsten einen Teil der benötigten Verstärkung schon in der ersten Stufe einzubringen. Das gelingt weitgehend ohne interne Übersteuerung, wenn man die Bezugspegel der ersten Stufe nicht auf Masse setzt, sondern wie in der Schaltung nach Bild 8 mit der Kopplung der beiden Bezugspegel über den Widerstand R1 miteinander verbindet. Der Bezugspegel ist dann der Mittelwert der beiden Engangsspannungen und der wirksame Widerstand bei jedem Verstärker ist 0,5 * R1. <br />
<br />
Im oben angeführten Beispiel mit 5V und 5,01V ist der Mittelwert gerade 5,005V. <br />
Damit erhält man mit (0,5 * R1 + R2) / (0,5 * R1) = 100 die Ausgangsspannungen 5,505V und 4,505V aus denen in der letzten Stufe die gewünschte Differenz von 1V gewonnen wird. <br />
Für die Schaltung in Bild 8 gilt:<br />
<br />
'''Ua = (U1 - U2) * (R1 + 2*R2) / R1'''<br />
<br />
Die Schaltung nach Bild 8 hat zusätzlich den Vorteil, einer verbesserten Gleichtaktunterdrückung, auch ohne die Verwendung extra präziser Widerstände. Die erste Verstärkungstufe verstärkt nämlich nur das Differenzsignal und nicht den Mittelwert (Gleichtaktsignal). Die Schaltung nach Bild 8 heißt auch Instrumentenverstärker und ist auch fertig (ggf. R1 extern) als IC zu bekommen.<br />
<br />
So gibt es beispielsweise für die Auswertung von Messbrückenschaltungen immerhin schon einmal 5 Differenzverstärker, die mit ihren unterschiedlichen Schaltungen und Darstellungsweisen immer wieder für Verblüffung sorgen können.<br />
<br />
== Weitere Anwendungen für Operationsverstärker ==<br />
<br />
Operationsverstärker lassen sich nicht nur als normale Verstärker nutzen. Weitere Anwendungen sind:<br />
* Aktive [[Filter_(Elektronik)|Filter]]schaltungen<br />
* Regler-Schaltungen (z.B. PID), an sich auch nur eine Art Filter<br />
** als Spezialfall Spannungsregler<br />
* Aktive Gleichrichter / Präzisionsgleichrichter für Messzwecke<br />
* [[Analog-Komparator|Komparator]], allerdings meist nicht so gut wie ein echter Komparator<br />
* Frequenzgenerator / Oszillator (Sinus / Rechteck / Dreieck)<br />
* Transimpedanzverstärker (Strom Spannungswandler) (z.B. für Fotodioden)<br />
<br />
== Stabilität in OP Schaltungen ==<br />
<br />
Eine der Schwierigkeiten bei Schaltungen mit Operationsverstärkern ist es sicherzustellen, dass der <br />
Verstärker nicht schwingt. Hier soll keine ausführliche Darstellung der Stabilitätsanalyse folgen, sondern nur eine kurze, vereinfachte und eher praxisorientierte Form.<br />
<br />
Der Operationsverstärker kann zu schwingen anfangen, wenn aus der gewollten Gegenkopplung eine Mitkopplung wird. 180 Grad Phasenverschiebung entsprechen einer Invertierung und machen gerade aus der Gegenkopplung eine Mitkopplung. Durch RC Glieder (oder mit Induktivitäten) können Phasenverschiebungen erzeugt werden. Daher muß auf die Phasenverschiebung in der Rückkopplung (in der Regel vom Ausgang zum invertierenden Eingang) geachtet werden. Um die Bandbreite zu begrenzen haben die Operationsverstärker schon von sich aus etwa 90 Grad Phasenverschiebung über einen großen Frequenzbereich. Problematisch ist vor allem, wenn die Rückkopplung zu spät kommt. Die Bandbreite (für Schleifenverstärkung von eins) des Operationsverstärkers gibt vor, bis zu welcher Frequenz keine größeren Phasenverschiebungen (in Richtung Verzögerung) auftreten dürfen. Daran sieht man schon, dass es leichter ist einen langsamen Operationsverstärker stabil zu kriegen, als einen schnellen. <br />
<br />
Schlecht für die Stabilität sind:<br />
<br />
- Tiefpass-charakter in der Rückkopplung: dies führt leicht zum Schwingen.<br />
<br />
- Kapazität gegen Masse am Ausgang des OPs: dies sorgt für eine Verzögerung des Ausgangssignals.<br />
<br />
- Kapazität gegen Masse am invertierenden Eingang: dies ergibt zusammen mit einem Rückkopplungswiderstand einen Tiefpass.<br />
<br />
- hochohmige Rückkopplung ohne parallelen Kondensator: gibt zusammen mit parasitären Kapazitäten einen Tiefpass<br />
<br />
- OP mit hoher Bandbreite: parasitäre Kapazitäten und Induktivitäten werden wichtiger.<br />
<br />
- Verstärkung in der Rückkopplung: Gefahr von Verzögerungen und schon an sich schlecht, weil sich das Verstärkungs-Bandbreitenprodukt erhöht.<br />
<br />
- lange Leitungen: geben zusätzliche Kapazitäten und Induktivitäten (je schneller desto kleiner)<br />
<br />
- fehlender Entkoppelkondensator an der Versorgungsspannung, besonders bei schnellen OPs<br />
<br />
- niedrige Versorgungsspannung bei einigen OPs mit JFets (z.B. TL072)<br />
<br />
Von den Standardschaltungen mit OPs sind die folgenden etwas problematisch: Differenzierer, Hochpass, Transimpedanzverstärker. <br />
<br />
Um die die Stabilität zu verbessern, kann man gezielt für einen Hochpass-Character in der Rückkopplung sorgen, z.B. durch einen kleinen Kondensator vom Ausgang zum inv. Eingang. Dadurch verringert sich aber auch die Bandbreite der Schaltung. Wenn der Ausgang kapazitive Lasten treiben soll (z.B. lange Kabel) sollte ein Widerstand (z.B. 100 Ohm) vor die Last geschaltet werden. Je nach OP liegt die Grenze bei etwa 20pF (z.B. TLV271) bis 5 nF (z.B. LF356). Vor allem schnelle sparsame Operationsverstärker sind empfindlich. Einige moderne OPs sind auch mit relativ großer Kapazität am Ausgang stabil - es bleibt aber bei einer Verschlechterung der Phasenreserve wenn der Ausgang Kapazitiv belastet wird. <br />
<br />
Etwas gegen die Intuition sind Verstärkerschaltungen mit einer hohen Verstärkung für das Signal weniger schwingungsanfällig als solche mit einer kleinen Verstärkung (z.B. 1 beim Impedanzwandler). Dies liegt daran, dass die hohe äußere Verstärkung die Schleifenverstärkung reduziert. Einige OPs (z.B. OP37, LF357): sind speziell für Schaltungen mit einer Verstärkung von mindestens z.B. 5 gedacht - bei weniger Verstärkung muss man mit Schwingungen rechnen. <br />
<br />
Bei etwas komplizierteren Schaltungen kann eine Simulation (z.B. mit [[SwitcherCAD-Tutorial|LTSpice]]) sinnvoll sein. Dabei sollten auch parasitäre Kapazitäten mit berücksichtigt werden. Die Neigung zu Schwingungen kann im Zeitbereich als Überschwinger bzw. Nachschwingen beim Einem Rechtecksignal oder im Frequenzbereich als Resonanz (Maximum im Frequenzgang) erkannt werden.<br />
<br />
==Liste gängiger Typen von Operationsverstärkern==<br />
Die folgende Liste gibt eine Auswahl der wichtigsten Daten für einige Operationsverstärker. Das sind zum einen einige ältere und in Schaltplänen häufiger zu findende Typen (MC1458 , µA741, TL072, LM324), dann einige für Bots interessante Typen und auch ein paar eher exotische Typen für ungewöhnliche Anforderungen (z.B. OP177, AD8551, TCA0372, OPA355).<br />
<br />
----<br />
{| {{Blauetabelle}} style="text-align:center;"<br />
|+ im Aufbau http://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=257490&highlight=#257490<br />
|-<br />
|Typ||Vmin||Vmax||Voff||Voff||Vn||i in||di in||Gain||SR||BW||i out||Is||Rail in||Rail out||single||double||quad<br />
|-<br />
|.||V||V||mV||µV/°C||nV/Hz^,5||nA||nA||V/mV||V/µs||MHz||mA||mA||lo / hi||lo / hi||€||€||€<br />
|-<br />
|MC1458||10||36||2||.||30||80||20||15||0,5||1||20||1,5||2 / -2||1 / -1||.||0,18||.<br />
|-<br />
|µA741||?10||36||2||15||23||80||20||200||0,5||1,5||25||1,7||2 / -2||1 / -1||0,19||.||.<br />
|-<br />
|LM358||3||32||5||7||40||45||5||15||0,5||1||20/8||0,2||0 / -1,5||0 / -2||.||0,18||LM324<br />
|-<br />
|LM324||3||32||5||7||40||45||5||15||0,5||1||20/8||0,2||0 / -1,5||0 / -2||.||.||0,19<br />
|-<br />
|TL072||7||36||3||18||18||65p||5p||200||13||3||20||0,7||3 / 0||1,5/-1,5||0,27||0,28||0,29<br />
|-<br />
|TLC 272||3||16||1,1||1,8||25||0,6p||0,1p||27||4||2||30||0,7||-0,3/-0,8||(0.3)/-1,2||0,38||0,35||0,45<br />
|-<br />
|MC33078||10||36||0,15||2||4,5||300||25||300||7||9||30||2||2 / -2||1 / -1||.||0,35||0,99<br />
|-<br />
|OP 07||6||36||0,03||1,3||10||1,2||0,5||400||0,3||0,6||.||1||1 / -1||2 / -2||0,29||.||.<br />
|-<br />
|MCP6042||1,4||6||3||2||170||1p||1p||115dB||0,003||0,014||2-20||0,6µ||-0,3/0,3||0 / 0||x||0,71||1,35<br />
|-<br />
|MCP6002||1,8||5,5||7||2||28||1p||1p||400||0,6||1||6..20||0,1||-0,3/0,3||0 / 0||x||0,31||0,40<br />
|-<br />
|ICL7612||2||16||5||15||100||1p||0,5p||10||1,6||1,4||.||..1||-0,3/0,3||0 / 0||1,25||1,50||x<br />
|-<br />
|TS912||2,7||16||5||5||30||1p||1p||40||0,4||0,8||65||0,25||-0,2/0,2||0 / 0||.||0,93||1,20<br />
|-<br />
|OP177||5||44||4µ||0,03||10||1,5||0,3||12000||0,3||0,6||12||1,6||1 / -1||1 / -1||1,15||.||.<br />
|-<br />
|LTC1050||4,75||16||0,5µ||0,05||90||0,01||0,02||160dB||4||2,5||20/4||1||0/-1,7||0 / 0||2,95||.||.<br />
|-<br />
|AD8552||2,7||5,5||1µ||0,005||42||0,01||0,02||145dB||0,4||1,5||30/30||0,7||0 / 0||0 /0||1,90||3,20||x<br />
|-<br />
|TCA0372||5?||40||1||20|| 22 || 100|| 10||1 || 1,4||1,4 ||1000||2,5||0/-1||1 /-1||.||1,15||.<br />
|-<br />
|LMC6482||3||16||3,8||1,0||37||0,02p||0.01p||130dB||0,9||1,5||30||0,75||-0,3/0,3||0/0||.||0,99||1,95<br />
|-<br />
|OPA355||2,5||5,5||2||7,0||5,8||3-50p||?||>80dB||300||200||60||8,3||-0,1/-1,5||0,1/-0,2||€?||€?||€?(triple)<br />
|-<br />
|TS922||2,7||12||0,9||2||9||15||1||200||1,3||4||80||4,5||-0,2/0,2||0,1/-0,2||€?||€?||€?<br />
|-<br />
|}<br />
----<br />
<br />
;Erklärung der Spalten:<br />
Typ gibt die Bezeichnung für die 2-fach Version an, sofern verfügbar. Die 1-fach/4-fach Versionen unterscheiden sich meist in der letzten Ziffer.<br />
<br />
Vmin / Vmax sind die minimale und maximale Versorgungsspannung. Bei symmetrischer Versorgung die Differenz (V+) - (V-).<br />
<br />
Voff ist die Offsetspannung oder der Gleichspannungsfehler. Das ist die Gleichspannung die am Eingang anliegen muss, um den Ausgang auf eine mittlere Spannung zu bringen. Der Wert ist als typische obere Grenze zu verstehen, wobei positive oder negative Werte möglich sind. Dazu wird noch die typische Grenze der Temperaturabhängigkeit von Voff angegeben.<br />
<br />
Vn ist die Rauschspannungsdichte. Für Frequenzen unter etwa 1 kHz kann das Rauschen deutlich höher werden.<br />
<br />
i in ist der Bias Strom. Das ist der mittlere Eingangsstrom der beiden Eingänge. Die Werte geben nur die Größenordnung an und können stark von Exemplar zu Exemplar streuen. Außerdem ist der Bias Strom zu Teil (FET Eingänge) stark Temperaturabhängig.<br />
<br />
di in ist der Offsetstrom oder die Differenz der Eingangsströme der beiden Eingänge. Der Wert ist als typische obere Grenze zu verstehen. <br />
<br />
Gain ist die Verstärkung für niedrige Frequenzen (z.B. 1 Hz).<br />
<br />
SR ist die maximale Geschwindigkeit für Änderungen der Ausgangsspannung, engl. Slewrate. Bei FET Eingängen ist dafür ein relativ großes Eingangssignal nötig.<br />
<br />
BW ist die Bandbreite. Angeben ist die Frequenz bei der die Verstärkung bis auf 1 abfällt oder das Produkt aus Frequenz und Verstärkung bei mittleren Frequenzen. Die Frequenz des Nutzsignals sollte normalerweise mindestens um den Faktor 10 mal der Verstärkung der Schaltung niedriger liegen.<br />
<br />
i out ist der maximale Ausgangsstrom. Die meisten OPs sind zumindest kurzzeitig kurzschlussfest. Teils ist der Strom getrennt für das negative und positive Vorzeichen angegeben. <br />
<br />
Is ist der typische Stromverbauch pro Verstärker. Zum Teil ist der Stromverbrauch deutlich von der Spannung abhängig.<br />
<br />
Rail in ist der Eingangspannungsbereich oder Gleichtaktbereich. Angegeben ist für die untere und obere Grenze jeweils die Differenz zur negativen bzw. positiven Versorgungsspannung.<br />
<br />
Rail out ist der Ausgangspannungsbereich. Angegeben ist der Mindestabstand zur negativen und positiven Versorgungsspannung. Der Wert 0 kann natürlich nicht wirklich erreicht werden, aber die Spannung kann bei kleinem Strom (z.B. 10 µA) bis auf ein paar mV an die Versorgung heran.<br />
<br />
single/double/quad geben circa Preise für einfach / doppel / 4-fach Ausführungen an, soweit sie verfügbar sind.<br />
<br />
==Autor/en==<br />
* Manf<br />
<br />
==Weblinks==<br />
* [http://www.mikrocontroller.net/articles/Operationsverst%C3%A4rker-Grundschaltungen Ähnliche Liste von gebräuchlichen OPVs wie hier - mikrocontroller.net]<br />
* [http://www.eetkorea.com/ARTICLES/2003SEP/A/2003SEP19_AMD_AN07.PDF Op Amp Circuit Collection] - National Semiconductor Application Note 31 mit weiteren OP-Schaltungen<br />
* [http://www.elektronik-kompendium.de/sites/bau/0209092.htm OP in DAS ELKO]<br />
* [http://www.mikrocontroller.net/articles/Operationsverst%C3%A4rker-Grundschaltungen Operationsverstärker-Grundschaltungen - mikrocontroller.net]<br />
* http://www2.fh-fulda.de/~pfisterer/mt/mt8.pdf<br />
* [http://www.elektronikwissen.net/opamp/9-opamp-wissen.html Praxishinweise und schwingende Operationsverstärker] <br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Grundlagen]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Filter_(Elektronik)&diff=26259Filter (Elektronik)2015-03-13T21:17:00Z<p>Besserwessi: /* Multiple-Feedback Filter */</p>
<hr />
<div>= Wozu benutzt man Filter? =<br />
<br />
Für Filter gibt es eine ganze Menge Anwendungen.<br />
<br />
Als erstes Beispiel nehmen wir uns mal einen Verstärker her, einen einfachen. Einen Transistor in Emitterschaltung zum Beispiel. Am Eingang dieser Schaltung liegt die Basis des Transistors, beschaltet mit einem Vorwiderstand von Vcc aus. Dieser dient dazu, den Arbeitspunkt des Transistors festzulegen und "zieht" die Basis des Transistors zum Beispiel auf 0,70 Volt.<br />
Was passiert wenn man nun ein Signal anlegt, beispielsweise ein Audiosignal, welches zwischen 0,1 Volt und -0,1 Volt schwingt? Die Basis wird auf eben diese (im Mittel) 0 Volt heruntergezogen, und der Strom, der eigentlich für die Basis vorgesehen war, fließt nun einfach in die Signalquelle ab.<br><br />
Hier schafft ein Entkopplungskondensator, den man zwischen Signalquelle und Basis des Transistors schaltet, Abhilfe: Die Spannung zwischen dessen Anschlüssen steigt durch den abfließenden Strom langsam an. Die Spannung an der zum Transistor gewandten Seite steigt also an, und irgendwann ist die Spannung soweit gestiegen, dass durch die Basis des Transistors wieder der entsprechende Strom fließen kann. Durch den Kondensator fließt dann kein Gleichstrom mehr.<br />
Die Wechselspannung, in diesem Fall das Audiosignal, wird dagegen durchgelassen. Dabei fließt nämlich während der positiven Halbwelle Strom in die Schaltung herein, bei der negativen Halbwelle des Signals dagegen fließt der Strom wieder heraus. Diese Strompulse sind so kurz, dass der Kondensator seine Spannung dabei kaum ändert, sie werden also fast ungehindert durchgelassen.<br />
<br />
Als zweites Beispiel nehmen wir uns nochmal Audio-Kram her: Wir haben nen Tieftöner, den wir an unsere Stereoanlage anschließen wollen. Die tiefen Frequenzen soll er ruhig wiedergeben - die hohen Frequenzen jedoch nicht, denn für die sind die Hochtöner zuständig.<br><br />
Eine Spule, die hier in Reihe zum Lautsprecher geschaltet wird, erfüllt diese Bedingungen. Wenn ein Signal an die Kombination aus Lautsprecher und Spule angelegt wird, steigt der Strom, bedingt durch die Induktivität der Spule, nur langsam an. Bei tiefen Frequenzen fällt das kaum auf, denn eine Halbwelle ist lang genug, um den Strom durch den Lautsprecher und die Spule zu erhöhen. Bei hohen Frequenzen dagegen sind die Halbwellen so kurz, dass sich kein nennenswerter Stromfluss einstellen kann - die Signale werden von der Spule blockiert.<br />
<br />
Zur Glättung von Spannungen und / oder Strömen werden auch Filter eingesetzt. Eine Kombination aus Spule und Kondensator (LC-Filter) setzt man beispielsweise häufig vor der ADC-Versorgung von Mikrocontrollern ein. Diese Filter filtern doppelt: Wenn die Versorgungsspannung plötzlich leicht abfällt, ändert sich zunächst der Strom in der Spule, aber wie gesagt wurde, durch die Induktivität verlangsamt. Wenn der Strom etwas abgefallen ist, wird das zweite Filterelement - der Kondensator - aktiv, denn durch ihn muss zuerst ein Strom fließen, damit sich die Spannung am Ausgang der Schaltung ändern kann.<br />
<br />
----<br />
<br />
*Phantomspeisung (gecancelt: Gebräuchliche Systeme wie PoE brauchen dafür afaik keine echten Filter.)<br />
<br />
= Arten von Filtern =<br />
<br />
Nun gibt es aber zig verschiedene Möglichkeiten, Filter aufzubauen, noch dazu ist jede nur für spezielle Anwendungen geeignet.<br><br />
Man unterteilt Filter zunächst in aktive Filter und passive Filter: Passive Filter sind solche, die nur aus passiven Bauteilen bestehen. Dazu gehören die oben angesprochenen Kondensatoren, Spulen und Widerstände, aber auch Quarze.<br />
Aktive Filter enthalten als aktive Komponente meist einen Operationsverstärker. Der Vorteil liegt auf der Hand: Der Ausgang des OpAmps ist auch der Ausgang des Filters, der damit auch ruhig bis einige Milliampere belastet werden kann - bei passiven Filtern ist dies meistens nicht ohne weiteres möglich, die Ausgangsspannung würde sich dabei ändern. Der Nachteil sollte auch klar sein: Ein OpAmp, und jede andere aktive Komponente auch, braucht eine Versorgungsspannung. Für eine Frequenzweiche in einer passiven Standbox (d.h. der Verstärker ist nicht integriert) sind aktive Filter dadurch ungeeignet.<br />
<br />
[[Bild:Filterordnungen.gif|thumb|Beispiele für verschiedene Filterordnungen]]<br />
Weiterhin unterscheidet man Filter nach verschiedenen Ordnungen. Die Ordnung spiegelt dabei die Anzahl der frequenzabhängigen Bauteile wieder. Ein Filter höherer Ordnung, beispielsweise die oben beschriebene Kombination aus Spule und Kondensator, hat eine bessere Trennschärfe. Das bedeutet, dass der Abstand zwischen Frequenzen, die durchgelassen werden, und Frequenzen, die gesperrt werden, kleiner ist.<br><br />
Nun gut, im Bild rechts sieht man schon dass das so nicht ganz stimmt: Jede Frequenz wird noch irgendwie durchgelassen, aber die Dämpfung, die in dB gemessen wird, wird immer stärker. Man müsste also eigentlich sagen, dass der Abstand der Frequenzen, die als durchgelassen '''bezeichnet''' werden können und der Frequenzen, die als gesperrt '''bezeichnet''' werden können, kleiner ist. Ab welcher Dämpfung eine Frequenz als gesperrt oder durchgelassen bezeichnet werden kann, das hängt wiederum von der Anwendung ab. In Audioanwendungen reicht ein Filter 1. Ordnung meist völlig aus, aber um beispielsweise ein PWM-Signal in eine Gleichspannung zu wandelt, muss eine bestimmte Frequenz unbedingt möglichst schwach gedämpft durchgelassen werden, während die PWM-Frequenz möglichst stark gedämpft werden soll. Deshalb nutzt man hierfür vorwiegend Filter höherer Ordnungen.<br />
<br />
[[Bild:Filtervergleich.gif|thumb|Beispiele für verschiedene Filtercharakteristiken]]<br />
Zuletzt unterteilt man Filter noch in verschiedene Charakteristiken, namentlich Tiefpässe, Hochpässe, Bandpässe und Bandsperren.<br />
*Tiefpässe lassen tiefe Frequenzen durch und sperren hohe Frequenzen. (siehe blaue Kurve im Bild.) Eine typische Anwendung wäre die Glättung eines Signals mithilfe einer Kombination aus Spule und Kondensator. <br />
*Hochpässe machen das Gegenteil: Sie sperren tiefe Frequenzen und lassen hohe Frequenzen durch. (siehe grüne Kurve im Bild.) Beispiele wären eine Gleichspannungsentkopplung oder eine Frequenzweiche für einen Hochtonlautsprecher.<br />
*Bandpässe sperren hohe und tiefe Frequenzen - Ein bestimmter Frequenzbereich wird jedoch durchgelassen. (siehe rote Kurve im Bild.) Eine Anwendung hierfür wäre eine Frequenzweiche vor einem Mitteltöner '''''(Sorry für die ganzen Beispiele aus dem Audiobereich, fällt vielleicht wem was besseres ein? Passiv-PFC würde mir einfallen, würde aber ne längere Erläuterung notwendig machen.)'''''<br />
*Bandsperren sind das Gegenstück zu den Bandpässen: Hohe und tiefe Frequenzen werden durchgelassen, ein bestimmter Frequenzbereich wird gesperrt. (siehe türkise Kurve im Bild.) Eine solche Schaltung kann beispielsweise verwendet werden, um die möglicherweise bei Datenübertragungen über Modem störenden Gebührenimpulse aus der Telefonleitung herauszufiltern.<br />
<br />
Wie man sieht, sind die Bezeichnungen so gut wie selbsterklärend.<br />
<br />
= Realisierung =<br />
<br />
Wenn man einen Filter für eine bestimmte Anwendung berechnen will, braucht man folgende Informationen:<br />
* Die Grenzfrequenz. Das ist die Frequenz, ab der die Ausgangsspannung gegenüber der Eingangsspannung um 3dB abgeschwächt wird - das entspricht in etwa einer Abschwächum um den Faktor 1,414, genauer um <math>\sqrt{2}</math><br />
* Die Ordnung des Filters. Diese ergibt sich aus den Anforderungen in der Anwendung.<br />
* Ob der Filter aktiv oder passiv ausgeführt werden soll<br />
<br />
Alle Arten von Filtern, also solche 1. Ordnung, 2. Ordnung, Tiefpässe, Hochpässe etc. lassen sich sowohl Passiv als auch Aktiv aufbauen. Filter höherer als 2. Ordnung lassen sich immer als Verknüpfung von Filtern 1. und 2. Ordnung realisieren (bei passiven Filtern in der Regel mit einem Verstärker dazwischen). <br />
Beginnen wir zunächst mit den passiven Filtern:<br />
<br />
== Passiv ==<br />
<br />
=== 1. Ordnung, Tiefpass und Hochpass ===<br />
<br />
Für Tief- und Hochpässe 1. Ordnung gibt es zwei Möglichkeiten: Entweder man schaltet das frequenzabhängige Bauteil vor einen Lastwiderstand (beispielsweise einen Lautsprecher), oder man schaltet den Widerstand vor das frequenzabhängige Bauteil. Wie das dann aussieht, zeigt die folgende Abbildung:<br />
<br />
[[Bild:Filter 1.Ordnung.gif]]<br />
<br />
Ue ist die Eingangsspannung, vor dem Filter, und Ua ist die Spannung, die am Ausgang der Schaltung anliegt um weiterverarbeitet zu werden. Die obigen Aufbauten, mit dem frequenzabhängigen Bauteil vor dem Lastwiderstand, bieten sich an, wenn man bereits einen Lastwiderstand - beispielsweise einen Lautsprecher - gegeben hat. Den Widerstand R ersetzt man dann durch diese Last - und hat ein Bauteil weniger zu verbasteln.<br />
Das RC-Glied (links unten) bietet sich an, wenn man einen Tiefpassfilter mit hoher Eingangsimpedanz benötigt. Das ist beispielsweise der Fall, wenn man eine Referenzspannung glätten möchte.<br />
Das RL-Glied bietet sich meines Wissens überhaupt nicht an :D, da für die im Hobbybereich gängigen Frequenzen entweder ein sehr kleiner Widerstand R oder eine sehr große Spule nötig würde. Lösung 1 hat dann einen extrem kleinen Eingangswiderstand, Lösung 2 verbraucht massig Platz und ist teuer.<br><br />
Die Berechnungen sind, egal ob Hochpass oder Tiefpass, jeweils identisch. Bei einer Spule als frequenzabhängiges Bauteil gilt:<br />
<br />
<math>L = \frac{R}{2\pi*f_g}</math><br />
<br />
Wird dagegen ein Kondensator benutzt, gilt:<br />
<br />
<math>C = \frac{1}{2\pi*f_g*R}</math><br />
<br />
<br />
=== Passiv, 2. Ordnung ===<br />
<br />
Bei Filtern zweiter Ordnung gibt es neben der charakteristischen Frequenz noch einen 2. Parameter, die Güte. Diese beschreibt die Schwingfähigkeit des Filters, also etwa wie lange der Filter nach einem kurzen Impuls nachschwingt. Filter hoher Güte (nicht als Qualitätsmassstab) geben einen schärferen Übergang von Durchlass zum Sperren. Als Nachteil braucht es aber etwas Zeit bis der Filter eingeschwungen ist. Ob eine hohe oder niedriger Güte gewünscht ist, hängt von der Anwendung ab:<br />
Im Audiobereich nutzt man Filter mit eher geringer Güte und dadurch schwacher Schwingneigung. Wenn aber eine hohe Trennschärfe (etwa Radioempfang) gefordert ist, kann man diese durch Erhöhung der Güte verbessern.<br />
Bei Filtern mit hoher Güte sinkt die Dämpfung beim Übergang zum Sperrbereich des Filters erst ab, in einem bestimmten Bereich wird sie sogar kleiner als 1. Das heißt das Signal wird sogar noch verstärkt. Danach steigt die Dämpfung relativ schnell an.<br />
Bei einer Güte, die kleiner ist als die Wurzel aus 1/2 (ca. 0,707) tritt dieses sogenannte Überschwingen des Frequenzgangs nicht auf. Diese Güte ist auch die gebräuchlichste, sie ist meistens ein guter Kompromiss. Filter mit dieser Güte haben eine Butterworth-Charakteristik.<br />
Daneben gibt es noch andere: Ein Filter mit Tschebyshev-Charakteristik ist jeder Filter mit einer Güte, die größer ist als die Wurzel aus 1/2. Bei diesen Filtern tritt besagtes Überschwingen auf, je höher die Güte, desto stärker ist das Überschwingen.<br />
Ein Filter mit Bessel-Charakteristik hat eine Güte von Wurzel aus 1/3. Die Trennschärfe bei diesen Filtern ist schlechter als bei Filtern mit Butterworth-Charakteristik, aber Signale, deren Frequenzanteile im Durchlassbereich liegen, werden nicht so stark verzerrt. <br />
<br />
==== Bandpass ====<br />
<br />
[[Bild:Bandpass-Schaltung.gif|thumb|Die Schaltung für einen Bandpassfilter]]<br />
Bei diesen Arten von Filtern ist die Berechnung einen Tacken komplizierter. Bandpass und Bandsperre sind mindestens Filter 2. Ordnung. Im Grunde haben diese Filter eine obere und eine untere Grenzfrequenz, so dass verschieden große Frequenzbereiche beeinflusst werden können.<br />
Am einfachsten ist es dann, zwei einzelne Filter zu dimensionieren, nämlich einen Hochpass und einen Tiefpass und diese dann zu einem Filter zusammenzufassen. Aufgrund von Resonanz ü.ä. entspricht der entstehende Frequenzgang nicht dem, der entstehen würde, wenn man einfach die Dämpfungen der beiden Filter zusammenrechnet. Solange der Filter ein ganzes Frequenzband durchlassen soll, macht dies aber in den seltensten Fällen einen Unterschied. Wenn aber nur eine Frequenz durchgelassen werden soll, unterscheidet sich der Frequenzgang stark von dem der Einzelnen Filter. Es gibt dann eine Resonanzfrequenz, bei der die Dämpfung extrem gering wird. Wenn man sich von der Resonanzfrequenz wegbewegt, steigt die Dämpfung erst sehr schnell an, und geht schließlich in einen Abfall um 20dB / Dekade (Spannung proportional oder antiproportional zur Frequenz) über.<br><br />
Ein Problem gibt es aber dabei: Wenn die Abweichungen der Bauteile zu groß sind (und Kondensatoren sowie Spulen haben nunmal meistens große Toleranzen), wird die Frequenz möglicherweise nicht getroffen, und der Filter macht nicht das was er soll. Abhilfe schafft entweder die Verwendung von Bauteilen mit geringen Toleranzen (teuer!) oder eine Abgleich durch eine variable Kapazität oder Induktivität. Alternativ kann teilweise ein Filter höherer Ordnung mit einem etwas größerem des Frequenzband genutzt werden. Das gleiche gilt übrigens bei Bandsperren!<br />
<br />
[[Bild:bandpass-Ergebnis.gif|thumb|Das Ergebnis der Berechnung: Die Grenzfrequenzen stimmen mit den erwarteten mit ausreichender Genauigkeit überein.]]<br />
Ein Beispiel zur Berechnung: Nehmen wir an, wir brauchen einen Bandpass, der das hörbare Frequenzband von 20 Hertz bis 20 Kilohertz durchlässt. Diese Frequenzen sollten dann zugleich die Grenzfrequenzen des Filters sein. Wir berechnen dann einen Hochpassfilter, um Frequenzen unter 20 Hertz zu sperren, und einen Tiefpass, der Frequenzen über 20 Kilohertz sperren würde. Da ein Lastwiderstand, ein Breitbandlautsprecher mit einer Impedanz von 8 Ohm, gegeben ist, benutzen wir die oberen Aufbauten aus der obigen Grafik. Wir erhalten für die Induktivität <math>L \approx 63\mu H</math> und für den Kondensator <math>C \approx 1000\mu F</math>.<br />
<br />
Die Schaltung wird in dem Bild oben rechts gezeigt, die beiden Filter werden sozusagen hintereinandergeschaltet.<br><br />
Das Ergebnis ist in dem Bild darunter dargestellt: Die Grenzfrequenzen liegen mit ausreichender Genauigkeit bei den erwarteten Frequenzen von 20 Hertz und 20 Kilohertz.<br />
<br />
<br />
==== Bandsperre ====<br />
<br />
[[Bild:Bandsperre-Schaltung.gif|thumb|Die Schaltung für eine Bandsperre]]<br />
Bei einer Bandsperre verfährt man fast genauso. Man berechnet einen Tiefpassfilter für die untere Grenzfrequenz und einen Hochpassfilter für die obere Grenzfrequenz. Die Frequenzanteile, die dann zwischen den Grenzfrequenzen liegen, werden mehr oder weniger stark gesperrt, je nachdem, wie weit sie von den Grenzfrequenzen entfernt sind. In der geometrischen Mitte der beiden Frequenzen tritt eine Resonanz auf, die Sperrwirkung des Filters ist dann fast unendlich groß, sofern man ideale Bauteile hat. <br />
<br />
Bei einer Bandsperre werden die beiden frequenzabhängigen Bauteile parallel geschaltet. Die Schaltung sieht dann so aus wie in der nebenstehenden Abbildung.<br />
<br />
[[Bild:2T-notch.png|thumb| 2 T Bandsperre]]<br />
Für die Bandsperre gibt es noch eine zweite Möglichkeit, ohne Induktivität. Damit ist diese Schaltung gut als Filter für niedrige Frequenzen wie z.B. 50 Hz oder 100 Hz geeignet. Die erreichbare Sperrwirkung des Filters hängt davon ab, wie gut das Verhältnis der Werte stimmt. Die Güte liegt bei dieser Schaltung fest bei 1/2 - mit aktiven Variationen der Schaltung sind höhere Güten möglich.<br />
<br />
== Aktiv ==<br />
Bei relativ niedriger Frequenz, wie im Audiobereich sind Induktivitäten ziemlich unhandliche Bauteile und alles andere als ideal. Mit Hilfe von Verstärkern lassen sich auch Filter mit höherer Güte (Q > 0.7) ohne Induktivitäten, nur mit Widerständen, Kondensatoren und halt dem Verstärker (heute in der Regel ein [[Operationsverstärker]]) aufbauen.<br />
<br />
Die Filter 1. Ordnung sind oft auch weiter passive Filter mit einem nachgeschalteten Verstärker. Eine Ausnahme sind hier die Extremfälle Integrator und Differentiator, und ein Allpassfilter (Phasenschieber).<br />
<br />
Die typischen aktiven Filterschaltungen nutzen einen Operationsverstärker für einen Filter 2. Ordnung. Dabei gibt es mehrere Möglichkeiten der Realisierung. Zur Berechnung der passenden Widerstandswerte und Kapazitäten gibt es spezielle Programme zum Filter-Design (siehe Weblinks). Damit kann dann auch gleich der Frequenzgang im voraus berechnet werden. <br />
<br />
=== Multiple-Feedback Filter ===<br />
[[Bild:MultiFB_LP.png|thumb| Multi-Feedback Tiefpass]]<br />
<br />
[[Bild:MultiFB_BP.png|thumb| Multi-Feedback Bandpass]]<br />
<br />
Diese Schaltungsform ist vor allem angebracht für Filter mit hohem Q. Im Durchlassbereich wird das Signal invertiert. Der Hochpassfilter entsteht aus dem Tiefpass indem man die Widerstände und Kondensatoren vertauscht. Allerdings ist der Hochpass von der Stabilität problematisch. <br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
.<br />
<br />
=== Sallen-Key Filter ===<br />
[[Bild:Sallenkey_LP.png|thumb| Sallen-key Tiefpass]]<br />
<br />
<br />
[[Bild:Sallenkey_HP.png|thumb| Sallen-key Hochpass]]<br />
<br />
<br />
[[Bild:Sallenkey_BP.png|thumb| Sallen-key Bandpass]]<br />
<br />
Diese Schaltung ist vor allem angebracht für Filter mit niedrigem Q ( < 2). Im Durchlassbereich wird das Signal nicht invertiert und in der Regel auch nicht verstärkt. Bei der Wahl der Kapazitäten und Widerstände gibt es mehrere Möglichkeiten. Der einfache Fall mit 2 gleichen Widerständen und 2 gleiche Kondensatoren gibt für den Hochpass und Tiefpass eine Grenzfrequenz von 0,16 / (RC) und eine Güte von 0,5.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
.<br />
<br />
=== weitere Filterschaltungen ===<br />
Auch bei normalen Verstärkerschaltungen wird oft die Bandbreite nach oben und unten auf den nötigen Bereich begrenzt. Die Filterfunktion ist dabei eher Nebensache und eine Kombination aus einem Hochpass und Tiefpass jeweils 1. Ordnung genügt.<br />
<br />
<br />
=== Filter mit geschalteten Kondensatoren (switched capacitor filter) ===<br />
Um die Frequenz eines aktiven Filters höherer Ordnung zu verstellen muss man oft mehrere Widerstände synchron verstellen. Neben der relativ neuen Möglichkeit über elektronische Potis gibt es die alternative Widerstände durch kleine Ladungspumpen zu ersetzen. Statt eines kontinuierlichen Stromes wird die Ladung in einem Kondensator und elektronischen Schaltern in kleinen Portionen transportiert. Für niedrige Frequenzen verhält sich das dann wie ein Widerstand, umgekehrt proportional zur relativ hohe Umschaltfrequenz. Man kann so über die Umschaltfrequenz (z.B. 1 MHz) die Grenzfrequenz (z.B. 1 kHz) eines aktiven Filters, auch höherer Ordnung verstellen. Für diese Anwendung gibt es spezielle ICs.<br />
<br />
= Weblinks =<br />
*[http://focus.ti.com/docs/toolsw/folders/print/filterpro.html Ti Filter Pro Software für Aktive Filter]<br />
*[http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en010007 Microchip Software für Aktive Filter]<br />
*[http://www.linear.com/designtools/software/#Filter LT Software für Aktive Filter]<br />
*[http://elektronikbasteln.pl7.de/rfsim99-filter-berechnung.html RFSim99 Programm für Berechnung von HF- und NF-Filtern]<br />
[[Category:Elektronik]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Transistor&diff=25623Transistor2014-12-05T19:34:15Z<p>Besserwessi: /* Funktionsprinzip */ Teil mit Temperaturabhängigkeit gekürzt - lenkt zu sehr von der eigentlichen Funktion ab.</p>
<hr />
<div>Ein Transistor ist ein elektronisches Halbleiterbauelement. Dieser wird zum Schalten und Verstärken benutzt. Die Bezeichnung ''Transistor'' ist ein Kurzwort, das sich vom englischen ''Transfer Varistor'' ableitet und den Transistor als bei bipolaren durch Strom bzw. bei FETs durch Spannung steuerbaren Widerstand umschreibt. <br />
<br />
Transistoren werden in zwei Gruppen unterteilt:<br />
* Bipolare Transitoren<br />
* [[Feldeffekttransistor]]en (FETs)<br />
<br />
Bipolare Transistoren werden durch Stromfluss angesteuert. Die Anschlüsse des bipolaren Transistors sind ''Kollektor'', ''Basis'' und ''Emitter''. Ein kleiner Strom auf der Basis-Emitter-Strecke kann dabei einen großen Strom auf der Kollektor-Emitter-Strecke steuern. Es wird unter anderem auch zwischen NPN- und PNP-Transistoren unterschieden.<br />
<br />
Bei FETs ("Field Effect Transistor")werden die Anschlüsse als Gate (engl. Tor, Gatter), Drain (engl. Abfluss), Source (engl. Quelle) bezeichnet. Der Strom auf der Drain-Source-Strecke wird hier durch die Spannung zwischen Gate und Source gesteuert. Die Steuerung mit Gleichspannung erfolgt stromlos. Bei Steuerung mit Wechselspannung wird ein Strom fürs Umladen der Gate-Kapazität benötigt.<br />
<br />
Die FET's kann man auf j-FET's ("junction FET") und MOSFET's ("Metall Oxide Semiconductor FET") unterteilen, wobei Gate (G) vom Kanal (D-S Strecke) isolliert ist.<br />
<br />
== Funktionsprinzip ==<br />
<br />
[[Bild:Ib-Kennlinie, BC547.gif|thumb|Spannungs-Strom-Kennlinie der Basis-Emitter-Strecke]]<br />
Gehen wir zunächst von einem NPN-Transistor aus, dessen Emitter auf Masse liegt. Durch ihn können zwei Ströme fließen: Der Basis-Emitter-Strom (Kurz: I<sub>b</sub>) und der Collector-Emitter-Strom (Kurz: I<sub>c</sub>). Der Basisstrom I<sub>b</sub> ist der Steuerstrom. Die Spannungs-Strom-Kennlinie der Basis-Emitter-Strecke ähnelt einer Diodenkennlinie: Bis ca. 0,6V ( für Silizium; bei Germanium ist die Schwellenspannung schon bei etwa 0,35V) fließt kaum Strom, danach steigt die Stromstärke schnell an (siehe Grafik). Wie bei der Diode ist diese Kennlinie temperaturabhängig, bei höheren Temperaturen kann ein höherer Strom fließen. Je nach Schaltung werden zwischen die Ansteuerung und die Basis des Transistors Widerstände geschaltet um den Basis-Strom zu begrenzen.<br />
<br />
[[Bild:Ic-Kennlinie, BC547.gif|thumb|Spannungs-Strom-Kennlinine der Collector-Emitter-Strecke]]<br />
Die Collector-Emitter-Strecke des Transistors hat eine Kennlinie, die zuerst in etwa linear ansteigt und dann bei einer bestimmten Stromstärke in eine fast waagrechte Gerade übergeht (siehe Grafik).<br />
Bei welcher Stromstärke die Gerade abknickt, hängt von dem Strom ab, der durch die Basis-Emitter-Strecke fließt. Je höher dieser ist, desto später knickt die Gerade ab, und desto größere Lasten kann man schalten. In einem begrenzten Bereich ändert sich der Basisstrom linear zum Collectorstrom. Die Basis-Emitter-Stromstärken betragen in der Grafik von unten nach oben 0mA, 5mA, 10mA, 15mA und 20mA.<br />
Das Verhältnis aus dem Strom, der auf der Collector-Emitter-Strecke fließen kann, und dem Strom, der dazu als Steuerstrom benötigt wird, nennt man Verstärkungsfaktor. Der Verstärkungsfaktor, der bei einem Transistor angegeben ist, ist jedoch eine rein theoretische Größe. Die Werte, die im Datenblatt angegeben sind, beziehen sich meistens auf eine Collector-Emitter-Spannung von 5V, bei höheren Lastströmen sinkt der Verstärkungsfaktor weiter. Für Schaltanwendungen sollte man daher von etwa der halben Stromverstärkung ausgehen, damit die Emitter-Collector-Spannung sicher klein bleibt. <br />
Wie groß man I<sub>b</sub> wählen muss, probiert man in einer Schaltungssimulation aus, oder berechnet es näherungsweise.<br><br><br />
Bei einem PNP-Transistor sind im Grunde alle Spannungen umgedreht. Der Emitter zeigt nicht zur Masse, sondern zur positiven Versorgung (Vcc, z.B. 5 V). Die Basis muss auf einer niedrigeren Spannung liegen, beispielsweise 4,3V, damit der Transistor durchschaltet. Der Strom fließt aber weiterhin von + nach - (technische Stromrichtung angenommen)<br />
<br />
== Schaltsymbol und Anschlussbelegung ==<br />
[[Bild:Transistor.JPG|thumb|300px|{{FarbigerRahmen|<br />
'''Warnung:'''<br />
Es gibt ebenso Transistoren im TO-92 Gehäuse, deren Basis an der Seite liegt. <br />
Bevor du einen Transistor verwendest, solltest du auf jeden Falls sein Datenblatt gelesen haben<br />
bzw. dir sicher sein, wo sich welcher Anschluss befindet!<br />
}}]]<br />
<br />
[[Bild:Schaltsymbol NPN.png|thumb|250px|left|Schaltsymbol npn-Transistor. Der Pfeil zeigt von der Basis (p) zum Emitter (n)]] <br />
<br style="clear:left"/><br />
[[Bild:Schaltsymbol PNP.png|thumb|250px|left|Schaltsymbol pnp-Transistor. Der Pfeil zeigt vom Emitter (p) zur Basis (n)]] <br />
<br />
<br />
<br style="clear:both"/><br />
<br />
== Grundschaltungen (Als Beispiel: NPN-Transitor) ==<br />
Die Grundschaltungen sind nach dem Anschlusspin des Transistors benannt, der an eine feste Spannung angeschlossen ist, also gerade nicht als Eingang oder Ausgang dient.<br />
<br />
=== Emitterschaltung ===<br />
[[Bild:Emitterschaltung.gif|thumb|Grundschaltung der Emitterschaltung]]<br />
(1) Die Emitterschaltung besteht hauptsächlich aus dem Transistor, dem Kollektorwiderstand und dem Basis-Vorwiderstand. Ist an der Basis eine Spannung von unter 0,6V anlegt, ist der Transistor nicht leitend, also hochohmig. Weil der hochohmige Transistor einen höheren Widerstand als der Kollektorwiderstand aufweist fällt bei ihm die größte Spannung ab. Der Ausgang der Schaltung wird durch den Widerstand auf die positive Versorgungsspannung (Vcc) "gezogen". <br><br />
<br><br />
(2) Wenn man nun eine Spannung am Eingang anlegt, die größer als ca. 0,6V ist, fließt ein Strom durch die Basis des Transistors. Der Transistor wird leitend und zieht den Ausgang der Schaltung auf 0V (GND).<br><br />
Durch die Offsetspannung von 0,6V ist diese Schaltung nicht zum linearen Verstärken von Signalen geeignet. Die Schaltung kann nur als Schalter genutzt werden, sei es um die Flanken auf einem Signal zu verbessern (steiler zu machen) oder um Lasten zu schalten.<br><br />
<br><br />
In ersterem Fall muss man beachten, dass die Emitterschaltung das Signal invertiert, d.h. bei 0V am Eingang liegen 5V am Ausgang an und umgekehrt.<br />
In letzterem Fall wird die Last (beispielsweise eine Glühbirne) statt Rc angeschlossen. Sobald man nun eine Spannung an die Basis anlegt, wird der Transistor leitend und es kann ein Strom durch die Last fließen, die Lampe leuchtet.<br />
Bei großen Lasten benötigt der Transistor einen entsprechend hohen Basisstrom, der durch den Basis-Vorwiderstand eingestellt wird.<br />
<br />
==== Erweiterte Emitterschaltung ====<br />
[[Bild:Emitterschaltung, erweitert.gif|thumb|Emitterschaltung zum linearen Verstärken von Wechselspannungen]]<br />
Wenn man die Emitterschaltung zum linearen Verstärken eines Signals nutzen will, muss man sie noch ein wenig erweitern. An die Basis wird zusätzlich ein Pullup-Widerstand angeschlossen. Dieser sorgt dafür, dass der Transistor "vorgespannt" wird. Der Widerstand wird dabei gerade so groß gewählt, dass der Ausgang möglichst genau zwischen GND und der Versorgungsspannung liegt.<br><br />
Hinter den Ausgang und vor den Eingang müssen zusätzlich so genannte Koppelkondensatoren geschaltet werden. Diese sorgen am Eingang dafür, dass die angelegte Eingangsspannung die Vorspannung nicht zerstört und am Ausgang dafür, dass kein Gleichstrom durch die Last fließen kann. Dies ist vor allem bei Lautsprechern wichtig.<br><br />
Die Emitterschaltung kann in dieser Form Wechselspannungen verstärken. Für Gleichspannungen ist sie nicht mehr geeignet, da die Koppelkondensatoren diesen sperren.<br><br />
Wenn man eine Wechselspannung anlegt, fließt zusätzlich zu dem Vorspannstrom ein Eingangsstrom, der den Transistor entweder leitender (positive Halbwelle) oder weniger leitend (negative Halbwelle) macht.<br><br />
In ersterem Fall wird der Kollektor des Transistors durch selbigen auf eine negativere Spannung gezogen. Durch den Koppelkondensator am Ausgang fließt dann ein Strom in Richtung des Kollektors, bis der Kondensator durch diesen weit genug aufgeladen ist.<br><br />
im anderen Fall wird der Transistor weniger leitend, der Kollektor des Transistors wird dann durch den Widerstand Rc auf eine positivere Spannung gezogen. Folglich fließt durch den Koppelkondensator am Ausgang ein Strom in Richtung der Last, allerdings auch hier nur solange der Kondensator nicht vollständig geladen ist.<br />
<br />
=== Kollektorschaltung ===<br />
[[Bild:Kollektorschaltung.gif|thumb|Kollektorschaltung mit NPN-Transistor]]<br />
Für eine Kollektorschaltung werden zunächst nur ein Transistor und ein Widerstand benötigt.<br><br />
Ohne Eingangsspannung leitet der Transistor nicht, der Ausgang liegt also auf 0V.<br><br />
Sobald man am Eingang eine Spannung anlegt, wird der Transistor jedoch leitend, die Ausgangsspannung wird größer. Gleichzeitig wird dadurch die Basis-Emitterspannung kleiner. Der Ausgang erreicht also maximal etwa die Eingangsspannung abzüglich der 0,7 Volt, die benötigt werden, damit auf der Basis-Emitterstrecke ein Strom fließen kann. Die Spannungsverstärkung ist folglich etwas kleiner als 1. Im Gegenzug besitzt die Kollektorschaltung eine sehr hohe Stromverstärkung und dadurch auch einen sehr hohen Eingangswiderstand. Die Kollektorschaltung wird auch als Emitterfolger bezeichnet.<br><br />
Bei dieser Schaltung kann auf den Basis-Vorwiderstand verzichtet werden, solange man am Eingang keine Spannung anlegt, die größer ist als die Versorgungsspannung. Der Strom durch die Basis reguliert sich durch die variable Emitterspannung selbst. Allerdings kann ein Basis-Vorwiderstand auch als Kurzschlussschutz dienen, da bei einem begrenzten Basisstrom auch der Kollektor-Emitterstrom begrenzt ist.<br />
<br />
=== Basisschaltung ===<br />
[[Bild:Pegelwandler.png|thumb|Pegelwandler für 3 V auf 5 V Logicpegel, mit NPN Transistor in Basisschaltung.]]<br />
In der Basisschaltung wird die Basis fest auf eine mittlere Spannung gelegt. Der Strom am Emitter dient als Eingang, der Strom am Kollektor als Ausgang. Wenn man den kleinen Basisstrom vernachlässigt, sind der Emitter- und Kollektorstrom gleich groß. Wenn der Widerstand (bzw. die Impedanz) an der Emitterseite niedriger als an der Kollektorseite ist, ergibt sich eine Spannungsverstärkung. Die Basisschaltung wird hauptsächlich in HF-Schaltungen benutzt. <br />
<br />
Eine weitere Anwendung sind Pegelwandler. Die Schaltung im Bild rechts dient dazu, ein Digitalsignal von einem IC mit 2-3 V Versorgung auf 5 V zu verstärken. Wenn am Eingang mehr als etwa 1 V anliegen, sperrt der Transistor, und am Ausgang liegt die Spannung über den 1 K Widerstand. Wenn der Eingang auf GND Potential ist, ist der Transistor durchgeschaltet und der Ausgang liegt fast auf GND Potential. Der Basisstrom wird durch den 22 K Widerstand auf rund 0,2 mA begrenzt.<br />
<br />
=== Darlingtonschaltung ===<br />
[[Bild:Darlington.png|thumb|Darlingtonschaltung (NPN)]]<br />
Wenn die Stromverstärkung eines einzelnen Transistors nicht ausreicht, können zwei Transistoren so zusammengeschaltet werden, dass der Emitter des ersten Transistors an die Basis des zweiten Transistors geht. Die Kollectoren sind miteinander verbunden. Diese Schaltung verhält sich dann ganz ähnlich wie ein Transistor mit einer Stromverstärkung, die dem Produkt der beiden Stromverstärkungen entspricht. In einem Gehäuse zusammengefasst bezeichnet man diese Schaltung als Darlingtontransistor. Der im Bild gezeigte Widerstand kann auch weggelassen werden. Er macht die Schaltung schneller und reduziert den Leckstrom.<br />
<br />
=== Verstärkung mit Gegenkopplung ===<br />
==== Spannungsgegenkopplung ====<br />
<br />
[[Bild:Spannungsgegenkopplung.png|thumb|Verstärker mit Spannungsgegenkopplung]]<br />
<br />
Die Verstärkung mit der Emitterschaltung ist von den Eigenschaften (besonders Verstärkungsfaktor) des verwendeten Transistors abhängig und damit auch temperaturabhängig. Außerdem ist die Verstärkung nicht besonders linear.<br />
Die Schaltung im Bild zeigt einen Weg eine stabilere Verstärkung zu erzeugen. Über den Widerstand R2 wirkt die Ausgangsspannung der Eingangsspannung entgegen. <br />
Dieses Schaltungsprinzip wird daher Spannungsgegenkopplung genannt.<br />
Die Verstärkung ist in diesem Fall auf -R2/R3 = -5 fach festgelegt. Damit die Gegenkopplung wirken kann, muss die Verstärkung ohne die Gegenkopplung wesentlich höher sein, als sie durch die Widerstände eingestellt wird. Durch die Gegenkopplung reduziert sich die Verstärkung zugunsten einer besseren Linearität und thermischen Stabilität. <br />
<br />
<br />
==== Stromgegenkopplung ====<br />
<br />
[[Bild:Stromgegenkopplung.png|thumb|Verstärker mit Stromgegenkopplung]]<br />
<br />
Eine andere Form der Gegenkopplung zeigt das Bild rechts. Hier ist die Rückkopplung nicht so klar zu sehen, denn die Rückkopplung wirkt nicht auf die Basis, sondern auf den Emitter. Der Kollektorstrom hängt vom Basistrom ab und damit von der Spannung zwischen Basis und Emitter. Eine höhere Spannung am Emitter ist daher gleichbedeutend mit einer niedrigeren Spannung an der Basis und weniger Strom. Der Strom durch dem Kollektor fließt auch durch den Widerstand am Emitter, erhöht dort die Spannung und wirkt so einem höheren Strom entgegen. Man nennt diese Schaltung daher auch Stromgegenkopplung. Auch hier reduziert sich die Verstärkung auf etwa -R2/R3.<br />
<br />
=== Wie erkenne ich, um welche Grundschaltung es sich handelt? ===<br />
Einfache Vorgehensweise: Der Anschluss des Transistors, der weder als Eingang noch als Ausgang dient, gibt der Schaltung ihren Namen.<br />
Bsp: Eingang Basis, Ausgang Kollektor. Dann handelt es sich um eine Emitterschaltung, weil der Emitter weder Eingang noch Ausgang ist.<br />
<br />
=== Logische Interpretation der Schaltungen ===<br />
====Die NOT-Verknüpfung====<br />
<br />
[[Bild:NOT Gatter.JPG]]<br />
<br />
Diese einfache Schaltung, bestehend aus einem NPN-Transistor und zwei Widerständen, invertiert das Eingangssignal, sodass aus beispielsweise +5V (oder logisch 1) 0V (oder logisch 0) erzeugt werden.<br />
Die daraus resultierende Wertetabelle sieht folgendermaßen aus:<br />
<br />
<div align = "center"><br />
{| {{Blauetabelle}}<br />
| '''Eingang'''<br />
| '''Ausgang'''<br />
|-<br />
| 0V<br />
| +5V<br />
|-<br />
| +5V<br />
| 0V<br />
|}<br />
</div><br />
Wenn also an der Transistorbasis +5V angelegt werden (+0,7V reichen meistens auch schon), dann schaltet der Transistor durch und am Ausgang liegen 0V an. Der Strom, der nun durch den Transistor fließt, wird durch den Widerstand R<sub>2</sub> begrenzt. Wird dieser Widerstand weggelassen, dann wird durch den entstehenden Kurzschluss der Transistor unweigerlich zerstört.<br />
Legt man nun am Eingang 0V an, so sperrt der Transistor und am Ausgang liegen +5V an. <br />
<br />
<br />
Der Basisstrom wird durch den Widerstand R<sub>1</sub> bestimmt. Ein kleiner Widerstand beschleunigt die Schaltgeschwindigkeit des Transistors, ein großer ermöglicht die Ansteuerung auch mit kleinen Strömen.<br />
<br />
Baut man die obere Schaltung doppelt auf und verbindet beide Ausgänge miteinander, erhält man ein NOR-Gatter.<br />
<br />
====Die NAND Verknüpfung====<br />
<br />
[[Bild:NAND_Gatter.jpg]]<br />
<br />
Die NAND ('''N'''ot'''AND''', d.h. die invertierte Form einer AND Verknüpfung) Verknüpfung besteht aus zwei Transistoren und damit auch zwei Eingängen. Es gibt auch NAND Verknüpfungen mit mehr Transistoren und folglich auch mehr Eingängen, diese sind im Aufbau aber sehr ähnlich zu der vorgestellten Grund-NAND Verknüpfung.<br />
Schauen wir uns zunächst die Wertetabelle an:<br />
<div align = "center"><br />
{| {{Blauetabelle}}<br />
| '''Eingang 1 (E1)'''<br />
| '''Eingang 2 (E2)'''<br />
| '''Ausgang (A1)'''<br />
|-<br />
| 0V<br />
| 0V<br />
| +5V<br />
|-<br />
| +5V<br />
| 0V<br />
| +5V<br />
|-<br />
| 0V<br />
| +5V<br />
| +5V<br />
|-<br />
| +5V<br />
| +5V<br />
| 0V<br />
|}<br />
</div><br />
<br />
Wie kommt es nun zu dieser Werte- oder auch Wahrheitstabelle?<br />
<br />
Wenn an den beiden Eingängen 0V anliegen, dann schaltet keiner der beiden Transistoren durch und der Ausgang ist über den Widerstand R<sub>3</sub> mit +5V verbunden.<br />
Wechselt nun einer der beiden Eingänge auf 1, dann schaltet auch nur einer der beiden Transistoren durch und am Ausgang liegen immer noch +5V an. Werden nun aber beide Eingänge mit +5V verbunden, dann schalten beide Transistoren durch und der Ausgang ist leitend mit Masse verbunden.<br />
<br />
Die Widerstände (R<sub>1</sub>, R<sub>2</sub>, R<sub>3</sub>) haben die gleiche Funktion wie auch in der NOT Verknüpfung.<br />
<br />
== Transistor-Kennwerte ==<br />
Die Transistorkennwerte sind grundsätzlich in Grenzdaten und Kenndaten unterteilt. Grenzwerte dürfen auf keinen Fall überschritten werden, da eine Zerstörung des Transistors möglich ist. Eigenschaften eines Transistors werden als Kenndaten angegeben, die das Verhalten in bestimmten Arbeitspunkten kennzeichnen.<br />
<br />
===Grenzwerte für Sperrschichttemperatur===<br />
Durch die Verlustleistung bei Dauerbetrieb entsteht in der Sperrschicht Wärme, durch die sich die Sperrschichttemperatur erhöht. Die Sperrschichttemperatur '''T<sub>J</sub> ''' , darf bestimmte Werte nicht überschreiten, da sich sonst die Eigenschaften des Transistors stark verändern würden (z.B. sehr hoher Leckstrom), was oft eine Zerstörung zur Folge hat. Die maximale Temperatur hängt vom Halbleitermaterial ab.<br />
<br />
'''T<sub>J</sub> ''' : 90°C Germaniumtransistoren<br />
<br />
'''T<sub>J</sub> ''' : 150 - 200°C Siliziumtransistoren<br />
<br />
===Zulässiger Arbeitsbereich===<br />
In Transistorschaltungen dürfen bestimmte Grenzwerte nicht überschritten werden. Der zulässige Arbeitsbereich einer Transistorschaltung wird somit durch den Kollektorstrom '''I<sub>c</sub> ''', durch die Kollektor - Emitterspannung '''U<sub>CE</sub> ''' und durch die Verlustleistung '''P<sub>tot</sub> ''' begrenzt. Besonders bei höheren Spannungen (ab etwa 30 V) gibt noch eine zusätzliche Begrenzung des Stroms bzw. der Verlustleistung auf ggf. kleinere Werte, die im SOA (Safe operation Area) angegeben wird. Wird der Transistor außerhalb des erlaubten Arbeitsbereiches betrieben, wird er zerstört. <br />
<br />
Die zulässige Verlustleistung wird bei kleinen Transistoren oft für 25°C Umgebungstemperatur angegeben. Bei Leistungstransistoren wird oft 25°C Gehäusetemperatur vorausgesetzt. Bei höheren Temperaturen oder schlechterer Kühlung, was fast immer der Fall ist, reduziert sich die zulässige Verlustleistung - praktisch kann man etwa die Hälfte der angegebenen maximalen Verlustleistung nutzen. In den Datenblättern findet man dazu Zahlenwerte und teilweise Diagramme zur zulässigen Verlustleistung als Funktion der Umgebungstemperatur. Je nach Wärmewiderstand '''R<sub>Th</sub> ''' eines ggf. vorhandenen Kühlkörpers ergeben sich unterschiedliche Kurven.<br />
<br />
===Kenndaten===<br />
Von den Kenndaten ist im wesentlichen der Stromverstärkungsfaktor ('''h<sub>FE</sub> ''') wichtig. Für Schaltanwendungen interessiert noch die Kollektor-Emitter-Sättigungsspannung. Die anderen Kenndaten sind im wesentlichen für fortgeschrittene Schaltungen wichtig.<br />
<br />
<br />
''Autor: ZwieBack - Wiki Konvertierung Frank''<br />
<br />
==Siehe auch==<br />
* [[Feldeffekttransistor|FET]]<br />
* [[Kleinsignalverstärker]]<br />
<br />
==Weblinks==<br />
* [http://www.nano.physik.uni-muenchen.de/elektronik/nav/k5t2.html Grundlagen Transistorschaltungen] Anm.: Seite existiert nicht mehr<br />
* [http://www.mikrocontroller.net/articles/Transistor Mikrocontroller.net/Transistor]<br />
* [http://de.wikipedia.org/wiki/Transistor Wikipedia/Transistor]<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Grundlagen]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Timer/Counter_(Avr)&diff=25407Timer/Counter (Avr)2014-11-08T11:35:48Z<p>Besserwessi: /* Normaler Modus (Normal Mode) */</p>
<hr />
<div>Die Mikrocontroller der AVR-Familie besitzen je nach Typ eine unterschiedliche Anzahl an programmierbaren [[Timer|Timern]]. Bei den aktuellen ATmegas sind das mindestens ein 8-Bit Timer und ein 16-Bit Timer. Die Timer werden immer Timerx benannt, wobei x für die Timernummer steht (also 0, 1, 2, usw.). <br />
Die Konfigurationsmöglichkeiten sind von Timer zu Timer unterschiedlich.<br />
<br />
'''Hinweis:''' Die folgenden Code-Beispiele sind in C programmiert und wurden für einen [[ATMega32|ATmega32]] entwickelt. Sie lassen sich also ohne große Änderungen auch auf anderen Mikrocontrollern der AVR-Familie einsetzen. Allerdings hat Atmel bei den neueren µCs (etwa Mega88, Mega324 und fast alle der aktuellen Tiny) die Namen für die Register vielfach geändert. <br />
<br />
== Allgemeine Funktionsweise ==<br />
Timer funktionieren nach dem allgemeinen Prinzip, dass sie eine Ganzzahl (im weiteren als Zähler bezeichnet) je nach Betriebsmodus auf- oder abwärtszählen, d.h. inkrementieren bzw. dekrementieren.<br />
<br />
Angenommen, der Timer arbeitet im einfachsten Betriebsmodus, dem [[Timer/Counter (Avr)#Normaler Modus (Normal Mode)|Normalen Modus]]. Die Zählrichtung des Timers ist aufsteigend gerichtet. Je nach Auflösung, also 8-Bit oder 16-Bit, folgt auf den maximalen Zählerstand wieder die Null. Wenn z.B. bei einem 8-Bit Timer der Wert 255 inkrementiert wird folgt die Null (siehe Grafik).<br />
<br />
[[Bild:AbstrakterZaehlvorgang.png]]<br />
<br />
== Der Prescaler ==<br />
Der Prescaler (eng. = Vorteiler) kann der Takt für den Timer herunter geteilt werden. Oft hat man Faktoren von 1, 8, 64 ,256 oder 1024 zur Auswahl. Über das selbe Register kann der Timer auch ganz angehalten werden oder ein externer Takt ausgewählt werden. Ein externer Takt darf dabei höchstens halb so hoch wie der Prozezessortakt sein.<br />
Hier eine Grafik die den Prescaler veranschaulicht:<br />
<br />
[[Bild:Prescaler.png]]<br />
<br />
Das obere Diagramm zeigt den Betrieb ohne Prescaler, das untere mit Prescaler (:2). Die gestrichelte Linie zeigt, wann der Timer weiterzählt.<br />
<br />
Im Teil [[Timer/Counter (Avr)#Die Betriebsmodi|Die Betriebsmodi]] wird weiter auf die praktische Verwendung des Prescalers eingegangen.<br />
<br />
== Die Betriebsmodi ==<br />
Die AVR-Timer können in unterschiedlichen Betriebsmodi betrieben werden. Diese sind:<br />
* Normaler Modus<br />
* CTC Modus<br />
* PWM<br />
<br />
=== Normaler Modus (Normal Mode) ===<br />
Der einfachste Betriebsmodus ist der normale Modus. Er funktioniert wie im Abschnitt "[[Timer/Counter (Avr)#Allgemeine Funktionsweise|Allgemeine Funktionsweise]]" beschrieben. Die Zählrichtung des Timers ist immer aufsteigend, bis zum Überlauf - da fängt der Zähler wieder bei 0 an. Der Überlauf kann einen Interrupt (Timer-Overflow) auslösen. Im einfachsten Fall kann dieser Modus im folgendem Diagramm dargestellt werden:<br />
<br />
[[Bild:NormalerModus_1.png]]<br />
<br />
Der Zähler des Timers (im Diagramm oben, die aufsteigende und dann wieder zurückgesetzte Linie) ist in dem Register TCNTx gespeichert, wobei x für eine Zahl steht. Soll z.B. auf den Timer0 (siehe Datenblatt des jeweiligen Controllers) des Controllers zugegriffen werden, so ist an TCNT eine 0 anzuhängen, also TCNT0.<br />
Wie lange es braucht, bis der Zähler einen Overflow auslöst, ist von der Taktfrequenz des Controllers, dem eingestellten Prescaler-Wert und von der Timerauflösung abhängig. Nun wäre es ja sehr unpraktisch, wenn wir den Zähler nicht anpassen könnten. Denn sonst müssten wir unsere Software, die den Timer benutzt, evtl. anpassen und viel rechnen, um z.B. für 1000 ms zu schlafen. Deswegen kann auf den Zähler zugegriffen werden und ihn vorladen, bevor dieser wieder vom eigentlichen Timer hochgezählt wird. Dies veranschaulicht folgendes Diagramm:<br />
<br />
[[Bild:NormalerModus_1_Vorladen.png]]<br />
<br />
Dadurch kann eingestellt werden, wie lange es dauert, bis ein Overflow auftritt. Um zu berechnen, welchen Wert wir vorladen müssen, kann auch ein Java-Applet genutzt werden, siehe unter [[Timer/Counter (Avr)#Weblinks|Weblinks Java Applet]].<br />
<br />
Natürlich kann das auch "von Hand" berechnet werden. Die Berechnung des Preloader- sowie Prescalerwerts bei Verwendung der Overflow-Interrupts, eines Prescalers von 64 (nicht alle Prescaler können verwendet werden) und eines Quarzes mit der Frequenz von 8 MHz sieht folgendermaßen aus (gesuchte Frequenz beträgt 1000 Hz unter der Verwendung des Timer0 eines ATmega32):<br />
# <math>Prescale = Frequenz * 1000000 [Hz] = 8000000</math><br />
# Wir definieren den maximalen Zählerwert. Dieser ist bei einem 8-Bit Timer 256, bei einem 16-Bit Timer 65536. In unserem Fall ist der maximale Zählerwert 256, weil Timer0 verwendet wird.<br />
# Nun wird die Variable ''Prescale'' (s.o.) durch den verwendeten Prescaler (64) geteilt (<math>8000000 Hz / 64 = 125000</math>).<br />
# Als nächstes wird der im dritten Punkt errechnete Wert durch die gesuchte Frequenz geteilt <math>=125000 / 1000Hz = 125</math>.<br />
# Nun wird mathematisch überprüft, ob der errechnete Wert aus dem vierten Punkt kleiner als der maximale Zählerwert ist. Trifft dies zu, so wird der errechneten Wert vom maximalen Zählerwert subtrahiert(<math>= 256 - 125 = 131</math>).<br />
<br />
Damit haben wir den Wert errechnet, der bei jedem Interrupt, den der Timer0 auslöst, in TCNTx (in diesem Fall TCNT0) nachgeladen werden muss, damit die Interrupts in dem gewünschten Zeitabstand von einer Millisekunde ausgelöst werden.<br />
<br />
Zwischen dem Timer Overflow und dem tatsächlichen Aufrufen der ISR mit dem Nachladen des Timers ergibt sich eine kleine Verzögerung, die nicht einmal immer gleich ist. Bei einem genügend großen Prescaler (z.B. 64) kommt durch die Verzögerung kein zusätzlicher Timerschritt zustande, und auch die Methode mit dem Nachladen liefert exakte Ergebnisse. Bei kleinen Prescalern kommt es durch die Verzögerung zu längeren und nicht immer gleichen Zeitabständen. Wenn möglich wird für die Erzeugung einer konstanten Interruptrate deshalb besser der CTC Moduls benutzt.<br />
<br />
Zusammenfassend ein Code-Beispiel (kein vollständiges Programm):<br />
<br />
<pre><br />
/* Es wird der Timer2 (8-Bit) eines ATmega32 verwendet, der mit einem Quarz mit 7,3728 MHz<br />
betrieben wird. Im Abstand von etwa 0,1 ms erzeugt der Timer einen Interrupt, also eine<br />
Frequenz von 10000 Hz. Der Timer wird auf einen Prescaler von 64 und<br />
einem Preloader von 244 konfiguriert.*/<br />
<br />
volatile uint8_t countTimer2; // Speichert den aktuellen Zählerwert<br />
<br />
// ISR zum auffangen der Interrupts:<br />
SIGNAL(SIG_SIG_OVERFLOW2) // alter Form, für neuere GCC Versionen: ISR(TIMER2_OVF_vect)<br />
{<br />
TCNT2 = 244; // Nachladen<br />
countTimer2++;<br />
}<br />
<br />
// Initialisierung:<br />
TCCR2 = (1<<CS22); // Prescaler von 64 und damit Timer starten<br />
TCNT2 = 244; // Vorladen<br />
TIMSK |= (1<<TOIE2); // Interrupts aktivieren <br />
sei();<br />
<br />
// Funktionen zum benutzen der Timer:<br />
/** Diese Funktion nicht aufrufen. Wird von sleep_millisec aufgerufen.<br />
Bei t=10 schläft die Funktion 1 ms. */<br />
inline void sleep (uint8_t t)<br />
{<br />
// countTimer2 wird in der ISR oben inkrementiert<br />
countTimer2 = 0; // 1 Byte Typ, daher kein cli()... sei() nötig<br />
while (countTimer2 < t);<br />
}<br />
<br />
/** Schläft x-Millisekunden. */<br />
inline void sleep_millisec(uint16_t msec)<br />
{<br />
uint16_t i;<br />
for(i=0; i<msec; i++) {<br />
sleep(10);<br />
}<br />
}<br />
</pre><br />
<br />
Dieses Beispiel zeigt nicht unbedingt eine vorbildliche Nutzung des Timers. Eine ISR einfach nur zum schnellen Hochzählen der Zeit verbraucht recht viel Rechenzeit und sollte sonst eher vermieden werden. Das Hochzählen der "Zeit" ist eigentlich genau das, was der Timer in Hardware macht - nur halt nicht immer in geraden Zeitschritte, wie 0,1 ms, sondern halt in den Schritten, die der Prescaler vorgibt (z.B. 256 Takte). Die Umrechnung kann man aber gut auch bei der Wartezeit vorher, oder bei einer gemessenen Zeit nachher machen. <br />
<br />
==== Input Capture ====<br />
<br />
Die 16 Bit Timer haben eine "Input Capture" Funktion. Dieser Hardwareteil dient zur genauen Zeitmessung. Die typische Anwendung ist die Messung von kurze Zeiten, wie z.B. die Zeit für eine Motorumdrehung. Außer in einigen PWM Betriebsarten, wo das ICP Register als TOP-wert für den Timer benutzt wird, ist die ICP-Funktion immer aktiv. Wenn am ICP Pin die über das Bit "ICESx" eingestellte Flanke auftritt, wird der aktuelle Zählerstand in das ICP Register kopiert. Außerdem kann ein Interrupt ausgelöst werden. Der Interrupt wird, wie die anderen Timer Interrupts, in den Registern TIMSK und TIFR an- oder abgestellt. Man kann zwar die ICP-Funktion selber nicht ohne weiteres abschalten, aber natürlich den dazugehörigen Interrupt. Als eine spezielle Funktion ("Noise Cancler") gibt es die Möglichkeit sehr kurze Pulse (unter 4 Zyklen) zu unterdrücken. In der Regel kann man diese Funktion angestellt lassen, denn so schnell kann man die Daten ohnehin nicht verarbeiten.<br />
<br />
Solange die 16 Bit des Timers ausreichen ist die Benutzung ganz einfach: Der Timer wird mit dem gewünschten Vorteiler im normalen Modus gestartet. Im ICP-Interrupt wird die Differenz aus zwei aufeinanderfolgenden Zeiten (Werte in ICP-Register) berechnet. Dazu wird jeweils die vorherige Zeit im RAM zwischengespeichert. Wenn man bei der Rechnung (vorzeichenlose 16 Bit Zahlen) eventuelle Überläufe ignoriert, bekommt man die richtige Zeitdifferenz, auch wenn der Timer während der Messzeit einen Überlauf hatte. Das funktioniert so einfach, denn wenn noch weitere (höherwertige) Bytes vorhanden wären, damit es keinen Überlauf gibt, würde man genau so die unteren Bits berechnen.<br />
<br />
Etwas komplizierter wird es, wenn die 16 Bit Auflösung nicht mehr ausreicht. Dann kann der Timer-Überlauf benutzt werden, um auch längere Zeiten mit voller Auflösung zu messen. Die wesentliche Schwierigkeit ist es, den Fall zu berücksichtigen, dass ein Überlauf Interrupt und der ICP Interrupt fast gleichzeitig ausgelöst werden. Es kann passieren, dass der ICP-Interrupt aufgerufen wird, obwohl eigentlich erst der Overflow Interrupt dran gewesen wäre. Dieser seltene Fall lässt sich daran erkennen, dass das Overflow-Interrupt Flag gesetzt ist und der Wert im ICP Register klein ist (high Byte < 128, meistens 0).<br />
<br />
Beispielpropgramm (für GCC):<br />
(Bisher nur im Simulator getestet)<br />
<pre><br />
// Beispielprogramm für Zeitmessung mit ICP-Funktion<br />
// Erweiterung des Timers auf 32 Bit durch Software<br />
// Es wird die Periodendauer am ICP-Eingang gemessen und als ASCII via UART ausgegeben<br />
// Code für Mega48 / Mega88 / Mega 168 / ...<br />
// mit leichten Anpassungen auch für Tiny2313, Mega16, Mega32,...<br />
<br />
#include <stdlib.h> // für utoa<br />
#include <avr/io.h><br />
#include <avr/interrupt.h><br />
#include <avr/sleep.h> // Unterstützung für sleep mode<br />
<br />
#define F_CPU 1000000UL // Definition der Frequenz, ist ggf. im makefile<br />
#define BAUD 19200UL<br />
#define UBRR_BAUD ((F_CPU/(16UL*BAUD))-1)<br />
<br />
typedef union { // union erlaubt einen effektiven, separaten Zugriff auf Teile der Variable<br />
unsigned long i32;<br />
struct {uint8_t i8l; // low<br />
uint8_t i8m; // mid<br />
unsigned int high; // high, soft timer <br />
};<br />
} convert32to8;<br />
<br />
volatile unsigned long timestamp; // volatile wegen Zugriff im Interrupt<br />
volatile unsigned int softtimer;<br />
volatile unsigned long zeitdifferenz;<br />
unsigned long zeit;<br />
char puffer[12]; // Puffer für Ausgabe als Ascii<br />
<br />
ISR(TIMER1_OVF_vect) // Timer1 Überlauf<br />
{ <br />
++softtimer; // zählen der Überläufe<br />
} <br />
<br />
ISR(TIMER1_CAPT_vect) // Flanke an ICP pin<br />
{ <br />
convert32to8 cap; // Variablendeklaration<br />
<br />
cap.i8l = ICR1L; // low Byte zuerst, high Byte wird gepuffert<br />
cap.i8m = ICR1H; <br />
// overflow verpasst, wenn ICR1H klein und wartender Overflow Interrupt<br />
if ((cap.i8m < 128) && (TIFR1 & (1<<TOV1)))<br />
{ // wartenden timer overflow Interrupt vorziehen<br />
++softtimer; <br />
TIFR1 = (1<<TOV1); // timer overflow int. löschen, da schon hier ausgeführt<br />
}<br />
cap.high = softtimer; // obere 16 Bit aus Software Zähler<br />
zeitdifferenz = cap.i32 - timestamp;<br />
timestamp = cap.i32; // Zeit merken<br />
}<br />
<br />
void uart_init(void) // USART initialisieren (Mega48 etc.)<br />
{<br />
// Baudrate einstellen (Normaler Modus)<br />
// kann bei älteren AVR Typen etwas anders sein (kein UBRR0H, dafür prescaler)<br />
UBRR0H = (uint8_t) (UBRR_BAUD>>8); // bei Mega32 anders !<br />
UBRR0L = (uint8_t) (UBRR_BAUD & 0x0ff); // bei Mega32 UBRRL<br />
UCSR0B = (1<<TXEN0); // Aktivieren des Senders, bei Mega32 UCSRB<br />
UCSR0C = (1<<UCSZ01)|(1<<UCSZ00)|(1<<USBS0); // bei Mega32 UCSRC<br />
// Einstellen des Datenformats: 8 Datenbits, 2 Stopbit:<br />
}<br />
<br />
void putser(char c) // sende ein Byte via UART<br />
{<br />
while ( !( UCSR0A & (1<<UDRE0)) ) ; // Warten bis der Sendepuffer frei ist<br />
UDR0 = c;<br />
}<br />
<br />
void main(void)<br />
{<br />
unsigned char i;<br />
// Datenrichtungen:<br />
DDRB = 0; // Alles Eingänge, PB0 ist ICP<br />
PORTB = 0xFF - (1<<PB0); // Pullups an Eingängen außer ICP<br />
DDRC = 0; // Eingänge<br />
PORTC = 0xFF; // Pullups an Eingängen <br />
DDRD = (1<<PD1); // Eingänge, außer PD1 = Tx (UART)<br />
PORTD = 0xFF- (1<<PD1); // Pullups an alle Eingängen (außer TX)<br />
// Timer1 initialisieren:<br />
TCCR1A = 0; // normal mode, keine PWM Ausgänge<br />
TCCR1B = (1<< ICNC1) + (1<<CS10) // start Timer mit Systemtakt<br />
+ (1 << ICES1); // steigende Flanke auswählen<br />
TIMSK1 = (1<<TOIE1) + (1<<ICIE1); // overflow und Input-capture aktivieren, Mega32: TIMSK<br />
TIFR1 = (1<<TOIE1) + (1<<ICIE1); // Schon aktive Interrupts löschen, Mega32: TIFR<br />
// UART initialisieren:<br />
uart_init(); <br />
<br />
zeitdifferenz = 0;<br />
softtimer = 0; // wird für Zeitdifferenzmessung nicht mal gebraucht,<br />
// denn Differenz geht auch über Überlauf bei Softtimer<br />
set_sleep_mode (SLEEP_MODE_IDLE); // idle Mode: timer läuft weiter, int zum aufwachen<br />
<br />
sei(); // Interrupts erlauben: Messung startet<br />
while (1)<br />
{<br />
sleep_enable (); // Sleep Befehl freigeben <br />
sei();<br />
sleep_cpu(); // wartet auf irgendeinen Interrupt, z.B. ICP, timer_ovr,...<br />
sleep_disable(); // Sleep Befehl sperren<br />
cli(); // Interrupt sperren wegen Zugriff auf volatile Variable<br />
zeit = zeitdifferenz;<br />
zeitdifferenz = 0; // als Markierung für ungültigen Wert<br />
sei();<br />
if (zeit > 0)<br />
{ <br />
ultoa(zeit,puffer,10); // nach ASCII umwandeln<br />
i = 0;<br />
while (puffer[i])<br />
{<br />
putser(puffer[i++]); // Ausgabe<br />
}<br />
putser(13); putser(10); // Zeilenumbruch senden<br />
}<br />
} // Ende von While-schleife<br />
}<br />
</pre><br />
<br />
=== CTC Modus (Clear Timer on Compare Match mode) ===<br />
Viele Timer haben "Output-Compare" Register: OCRx oder OCRAx,OCRBx. Wenn der Zähler den darin eingestellten Wert erreicht hat, kann ein Interrupts ausgelöst werden. Der CTC Modus ist eine Erweiterung des "Output-Compare"-Funktion. Der CTC Modus eignet sich besonders, um einen mit konstanter Frequenz wiederkehrenden Interrupt zu erzeugen. Wie im normalen Modus zählt der Timer hoch. Wenn der Wert im OCRx Register erreicht wird, wird zusätzlich zum möglichen Interrupt der Zähler wieder auf 0 gesetzt. Es kann also die maximalen Zählergrenze selber definiert werden.<br />
Dieses Diagramm veranschaulicht den CTC Modus.<br />
<br />
[[Bild:NormalerModus_CompareMatch.png]]<br />
<br />
Beispielprogramm:<br />
<br />
<pre><br />
/* Es wird der Timer2 (8-Bit) eines ATmega32 verwendet, der mit einem Quarz <br />
mit 7,3728 MHz betrieben wird. Im Abstand von 0,01 ms erzeugt der Timer <br />
einen Interrupt, also eine Frequenz von 100000 Hz (oder 100 kHz). <br />
Der Timer wird auf einen Prescaler von 1 und einem OCR2-Wert von 73 konfiguriert. */<br />
<br />
volatile uint8_t countTimer2; // Speichert den aktuellen Zählerwert<br />
<br />
// ISR zum auffangen der Interrupts:<br />
SIGNAL(SIG_OUTPUT_COMPARE2)<br />
{<br />
countTimer2++;<br />
}<br />
<br />
// Initialisierung:<br />
TCCR2 = (1<<CS20) | (1<<WGM21); // Prescaler von 1 | CTC-Modus (siehe unten für Beschreibung)<br />
OCR2 = 73; // Vergleichswert<br />
TIMSK |= (1<<OCIE2); // Interrupts aktivieren und damit Timer starten<br />
sei();<br />
<br />
// Funktionen zum benutzen der Timer:<br />
/** Diese Funktion nicht aufrufen. Wird von sleep_millisec aufgerufen.<br />
Bei t=100 schläft die Funktion 1 ms. */<br />
inline void sleep(uint8_t t)<br />
{<br />
// countTimer2 wird in der ISR oben inkrementiert<br />
countTimer2 = 0;<br />
while (countTimer2 < t);<br />
}<br />
<br />
/** Schläft x-Millisekunden. */<br />
inline void sleep_millisec(uint16_t msec)<br />
{<br />
uint16_t i;<br />
for(i=0; i<msec; i++) {<br />
sleep(100);<br />
}<br />
}<br />
</pre><br />
<br />
----<br />
<br />
=== PWM ===<br />
<br />
Eine häufige Aufgabe für Mikrocontroller ist die Erzeugung von [[PWM]]-Signalen, zum Beispiel für Motorsteuerungen. Daher sind in den meisten [[AVR|AVRs]] PWM-Einheiten als Hardware vorhanden. Sie sind direkt mit den Timern verbunden und nutzen diese als Taktquelle. Die Hardware-PWM-Einheiten haben den Vorteil, sehr wenig Rechenzeit in Anspruch zu nehmen. Es muss nur die PWM aktiviert werden und bei Änderungen den gewünschten Wert in ein Register schreiben. Der Rest läuft automatisch und unabhängig vom restlichen Programm, ohne den AVR ständig zu beschäftigen wie bei einer PWM-Lösung in Software. Allerdings stehen meist nur zwei bis drei solcher PWM-Kanäle zur Verfügung, die außerdem an bestimmte Pins gebunden sind. Für die meisten Roboter mit zwei Antriebsmotoren reicht dies aber für gewöhnlich aus. <br />
<br />
==== nutzbare Pins am AVR ====<br />
Die Hardware-PWM-Funktion steht nur an bestimmten Pins zur Verfügung. In der Pinbelegungsübersicht im Datenblatt ist erkenntbar, dass als Sonderfunktion in Klammern "OC..." angegeben ist. Beim Mega32 sind dies zb. OC0 an PB3, OC1A an PD5, OC1B an PD4 und OC2 an PD7. Der Mega32 hat also insgesamt vier Hardware-PWM-Kanäle. Die Zahl hinter dem "OC" gibt an, zu welchem der Timer dieser PWM-Kanal gehört. Wenn noch ein Buchstabe dahinter kommt, dann gehören mehrere PWMs zu diesem Timer. Beim Mega32 sind also OC1A und OC1B demselben Timer, nämlich Timer1, zugeordnet.<br />
<br />
'''Zu beachten ist, dass die für die PWM benutzten Pins zuvor explizit als Ausgang konfiguriert werden müssen! Ansonsten gelangt das PWM-Signal nicht nach draußen!'''<br />
<br />
==== Funktionsprinzip ====<br />
Das "OC" in den Pinbezeichnungen steht für "Output Compare Unit", also frei übersetzt Ausgangs-Vergleicher-Einheit. Dies beschreibt die Funktionsweise der PWM-Kanäle: der Zählerstand des Timers wird fortlaufend mit einen einstellbaren Referenzwert verglichen, und wenn beide Werte übereinstimmen, kann ein Ausgangspin des AVRs automatisch geschaltet werden (und ein Interrupt ausgelöst werden, was allerdings für die PWM-Funktion nicht relevant ist). Dies entspricht dem Verfahren im [[PWM#PWM_per_Software|Beispiel zur Software-PWM]]. Es läuft nun allerdings vollautomatisch im Hintergrund, sodass der Controller nicht damit belastet wird.<br />
<br />
==== Die verschiedenen PWM-Modi ====<br />
Es gibt -je nach AVR und Timer- etliche Betriebsarten, in denen die PWM-Einheit betrieben werden kann. Sie unterscheiden sich vor allem darin, wie schnell und mit welchen Nebeneffekten sich Änderungen des Sollwertes auf das Ausgangssignal auswirken. Für den Anfang sind diese Unterschiede erst einmal nebensächlich, und für eine einfache Motorsteuerung meist auch irrelevant. Daher wird hier zunächst der "Fast PWM Mode" ("Schneller PWM Modus", weil hier die größte Ausgangsfrequenz möglich ist) beschreiben, welcher der einfachste von allen ist.<br />
<br />
Hierbei zählt der Timer immer von Null an aufwärts, bis er den Maximalwert (teilweise einstellbar) erreicht hat. Dann läuft er über und fängt von vorne an. Wie schnell dies geschieht, wird, wie im normalen Modus, über den Prescaler eingestellt.<br />
Der gewünschte PWM-Ausgangswert wird im "OCRn"-Register abgelegt. Er darf zwischen Null und dem Maximalwert des Timers liegen. Er wird nun mit dem Timer-Wert verglichen. Was dann passiert, regeln die "COM..."-Bits. Sie bestimmen, wie der Ausgang geschaltet wird. Die übliche Konfiguration ist, dass bei Erreichen des Sollwertes die Ausgänge auf high geschaltet werden, und beim Überlauf auf low. Damit ergibt sich ein nichtinvertiertes PWM-Signal. Schließt man (über einem passenden Motortreiber!) einen Motor an, dreht er sich bei einem Sollwert von 0 gar nicht und beim Maximalwert mit voller Geschwindigkeit.<br />
<br />
Beispielcode für den Timer1 des Mega16/32 (und vieler anderer AVRs): <br />
<pre><br />
#include <avr/io.h><br />
#include <util/delay.h> // Warteschleife für die Demo. Für die eigentliche PWM nicht benötigt! <br />
<br />
/* PWM-Beispiel für Mega16/32 (beiden haben den gleichen Timer)<br />
Benutzt wird Timer1 im Fast PWM Mode, 8 Bit Auflösung<br />
Die PWM-Signale liegen auf PD5/OC1A und PD4/OC1B<br />
*/ <br />
<br />
// 1. Den Prescaler einstellen, der die Frequenz festlegt<br />
TCCR1B |= (1<<CS12); //Prescaler 256<br />
<br />
// 2. Den Timer in den Fast PWM Mode, 8 Bit schalten<br />
// ACHTUNG: Die WGM-Bits sind auf beide Konfigurationsregister verteilt!<br />
TCCR1A |= (1<<WGM10);<br />
TCCR1B |= (1<<WGM12);<br />
<br />
// 3. Compare Output mode einstellen: Pin geht auf high bei Compare match, auf low bei Überlauf. <br />
// Ergibt nichtinvertierte PWM. <br />
TCCR1A |= (1<<COM1A1) | (1<<COM1B1) ; <br />
<br />
// In diesen Registern wird der gwünschte PWM-Wert abgelegt. Erlaubter Bereich: 0 bis 255.<br />
OCR1A = 0;<br />
OCR1B = 0; <br />
<br />
// 4. Die Pins als Ausgänge konfigurieren. Erst jetzt liegt das PWM-Signal an den Pins an! <br />
DDRD |= (1<<PD4) | (1<< PD5);<br />
<br />
/*Nun ist der PWM-Modus aktiv! Der Ausgangswert kann nun über die Register OCR1A und OCR1B<br />
vorgegeben werden. Man könnte ihnen per define noch einen Zweitnamen verpassen, zb */<br />
#define MotorLinks OCR1A<br />
#define MotorRechts OCR1B<br />
//Und nun kann man per <br />
MotorLinks = 127;<br />
MotorRechts = 127;<br />
//seinen Roboter mit halber Kraft vorwärts fahren lassen.<br />
<br />
/*PWM-Demo: Die PWM-Werte werden erst bis zum Maximalwert erhöht und dann wieder verringert. <br />
Ein angeschlossener Motor wird beschleunigen und dann wieder abbremsen. */<br />
uint8_t wert;<br />
while(1)<br />
{<br />
for (wert=0; wert<255; wert++)<br />
{<br />
OCR1A = wert;<br />
OCR1B = wert;<br />
_delay_ms(10);<br />
}<br />
<br />
for (wert=255; wert>0; wert--)<br />
{<br />
OCR1A = wert;<br />
OCR1B = wert;<br />
_delay_ms(10);<br />
} <br />
}<br />
</pre><br />
<br />
Es sind also vier Einstellungen zu treffen:<br />
# Takt anlegen per Prescaler<br />
# PWM-Modus wählen<br />
# Ausgangs-Aktion festlegen<br />
# Pins als Ausgänge schalten<br />
<br />
Diese Schritte müssen bei jedem AVR-PWM-Kanal ausgeführt werden. Die genauen Registernamen und Werte können sich jedoch je nach Timer-Ausführung etwas unterscheiden.<br />
<br />
== Registerübersicht ==<br />
''Hinweis: Diese Registertabellen wurden für den aktuellen [[Atmel Controller Mega16 und Mega32]] erstellt. Wenn Sie ein anderes Modell verwenden kann es sein, dass ein oder mehrere Register nicht existieren, oder sie eine andere Bezeichnung haben.''<br />
<br />
{| {{Blaueschmaltabelle}} width=100%<br />
|'''TIMSK'''<br />
|-<br />
|Mit diesem Register, der von allen Timern verwendet wird, lässt sich die Interruptausführung und Art des jeweiligen Timers bestimmen.<br/><br/><br />
<br />
{{Registertabelle8Bit|OCIE2|TOIE2|TICIE1|OCIE1A|OCIE1B|TOIE1|OCIE0|TOIE0}}<br />
<br />
|-<br />
|<br />
*'''OCIE2 (Timer/Counter2 Output Compare Match Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, wird der Timer/Counter2 Compare Match Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''TOIE2 (Timer/Counter2 Overflow Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, wird der Timer/Counter2 Overflow Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''TICIE1 (Timer/Counter1, Input Capture Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, wird der Timer/Counter1 Input Capture Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''OCIE1A (Timer/Counter1 Output Compare A Match Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, wird der Timer/Counter1 Output Compare A Match Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''OCIE1B (Timer/Counter1 Output Compare B Match Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, wird der Timer/Counter1 Output Compare B Match Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''TOIE1 (Timer/Counter1 Overflow Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, wird der Timer/Counter1 Overflow Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''OCIE0 (Timer/Counter0 Output Compare Match Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, so wird der Timer/Counter0 Compare Match Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''TOIE0 (Timer/Counter0 Overflow Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, so wird der Timer/Counter0 Overflow Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
|}<br />
<br />
<br />
== Siehe auch ==<br />
<br />
* [[Atmel]]<br />
* [[HEX Beispiel-Dateien für AVR]]<br />
* [[Bascom_und_Timer]]<br />
<br />
== Weblinks ==<br />
<br />
* [http://www.atmel.com/dyn/products/devices.asp?family_id=607 Die Datenblätter zu Atmel Controllern]<br />
* [http://frank.circleofcurrent.com/cache/avrtimercalc.htm Javascript-Toll zur Timerberechnung]<br />
* [http://www.roboternetz.de/phpBB2/dload.php?action=file&file_id=169 AvrTimer Windows Berechnungstool (für Bascom, nur nach Anmeldung)]<br />
<br />
<br />
[[Kategorie:Microcontroller]]<br />
[[Kategorie:Grundlagen]]<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Abkürzung|AVR]]<br />
[[Kategorie:Quellcode C]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Operationsverst%C3%A4rker&diff=25358Operationsverstärker2014-10-24T16:51:20Z<p>Besserwessi: /* Stabilität in OP Schaltungen */</p>
<hr />
<div>== Operationsverstärker Grundschaltungen ==<br />
<br />
== Verstärker ==<br />
<br />
Häufig müssen Sensorsignale in der ersten Stufe der Verarbeitung verstärkt werden und bei Spannungen von Meßbrücken wird die verstärkte Differenzspannung als Spannung gegen Masse benötigt. Schaltungen mit Operationsvertärkern die diese Aufgabe erfüllen werden hier dargestellt. <br />
<br />
Operationsverstärker werden zunächst als ideale Operationsverstärker betrachtet, das heißt sie haben eine unendlich hohe Verstärkung. Die Ausgangsspannung ist damit um einen sehr großen Faktor größer als die Differenz der Eingangsspannungen. In Wirklichkeit liegt der Faktor immerhin bei 10<sup>5</sup> bis 10<sup>6</sup>. <br />
<br />
Wird der Ausgang über einen Widerstand auf den negativen Eingang zurückgekoppelt, dann bewirkt diese Gegenkopplung, dass die Differenzspannung an den Eingängen (Ue+ - Ue-) zu null wird und die Verstärkung der Schaltung aus Operationsverstärker und Gegenkopplung endlich wird. Solche Schaltungen haben dann eine sehr präzise Verstärkung deren Wert nur durch den Wert der Widerstände bestimmt ist. Für die Betrachtung von idealen Operationsverstärkern gilt außerdem, dass in die Eingänge des Operationsverstäkers kein Strom fließt und dass der Ausgang den Innenwiderstand null hat. <br />
<br />
<br />
[[Bild:OperationsverstaerkerBild1.gif.gif]] <br />
<br />
Bild 1 zeigt die Schaltung für positive Verstärkung Bild 2 die Schaltung für negative Verstärkung. <br />
<br />
Die Beiden Schaltungen haben die gleiche Konfiguration, es wird nur jeweils der andere Eingang an Masse geschaltet. Mit U1 am positiven Eingang und U2 am negativen Eingang wird in beiden Fällen für die Ausgangsspannung Ua, der in Gleichung 2 angegebene Wert, erreicht. <br />
Setzt man U1 oder U2 gleich 0, dann erhält man die Ausgangsspannung für den positiven und den negativen Verstärker.<br />
<br />
Allgemein:<br />
<br />
Ua = U1 ( 1 + R2 / R1 ) - U2 R2 / R1 <br />
<br />
<!-- Das funktioniert nicht mit allen Browsern, auskommentiert (SprinterSB) --><br />
<!--/div--><br />
<br />
<br />
<!-- Das funktioniert nicht mit allen Browsern, auskommentiert (SprinterSB) --><br />
<!--div style="border:2px solid #ffd700; margin-left:auto; margin-right:auto; padding:0.3em; text-align:left; max-width:20em;"--><br />
<br />
== Differenzverstärker ==<br />
<br />
Um die Differenz zwischen zwei Spannungen am Ausgang gegen Masse zu erhalten wird die Schaltung in Bild 2 um einen Spannungsteiler am + Eingang erweitert siehe Bild 3. Die Eingangsspannung am Spannungsteiler heißt nun U1 und die Spannung am +Eingang (wie auch am -Eingang) ist Ue.<br />
<br />
[[Bild:Operationsverst%C3%A4rkerBild3.gif]] <br />
<br />
<br />
'''Damit gilt für die Schaltung in Bild3:''' <br />
<br />
Ua = Ue + (Ue - U2) * R2 / R1<br />
<br />
Ua = Ue * (R1 + R2) / R2 - U2 * R2 / R1 <br />
<br />
mit Ue = U1 * R2 / (R1 + R2) vereinfacht sich der Ausdruck zu: <br />
<br />
'''Ua = (U1 - U2) * R2 / R1'''<br />
<br />
<br />
Das heißt, dass die Ausgangsspannung gerade die Differenz der Eingangsspannungen mal dem Widerstandsverhältnis R2/R1 ist. Für große Widerstandswerte ist die Schaltung in Bild 3 schon einsetzbar, bei hoher Verstärkung und kleinen Werten für R1 ist es besser, die Eingänge hochohmig zu machen. <br />
<br />
<br />
Es wäre vorteilhaft die Messspannungen direkt an die hochohmigen Operationsverstärker-Eingänge zu legen. Beim + Eingang ist es ja die geteilte Spannung U1 die am + Eingang anliegt. Legt man sie direkt, ohne Teiler an den + Eingang, und verstärkt die Spannung U2 um den gleichen Faktor durch einen Verstärker nach Bild 2, dann ergibt sich am Ausgang wieder die Differenz von U1-U2 verstärkt um den Faktor (R1+R2)/R1. <br />
<br />
'''Für R1=R2 ergibt sich damit für die Schaltung in Bild 4''' <br />
<br />
'''Ua = 2 * (U1 - U2)''' <br />
<br />
<!-- Das funktioniert nicht mit allen Browsern, auskommentiert (SprinterSB) --><br />
<!-- </div> --><br />
<br />
<br />
<!-- Das funktioniert nicht mit allen Browsern, auskommentiert (SprinterSB) --><br />
<!--div style="border:2px solid #ffd700; margin-left:auto; margin-right:auto; padding:0.3em; text-align:left; max-width:20em;"--><br />
<br />
== Differenzverstärker mit einstellbarer Verstärkung ==<br />
<br />
Die Differenzverstärkerschaltung mit vier gleichen Widerständen R2 ist sehr gut für die Realisierung einer präzisen Verstärkung geeignet. Zur Erhöhung der Verstärkung ist es von Vorteil, wenn der Wert der Verstärkung mit nur einem Widerstand eingestellt werden kann. Hierfür wird ein Widerstand mit dem Wert R1 zwischen den Minus-Eingängen der beiden Verstärker eingefügt. Die Schaltung entspricht dann der Anordnung in Bild 5, machmal wird sie auch in der Form von Bild 6 dargestellt. <br />
<br />
[[Bild:Operationsverst%C3%A4rkerBild5.gif]] <br />
<br />
Hier überbrückt der Widerstand R1 die beiden Widerständ am Ausgang des linken Operationsverstärkers. Diese Kombination aus drei Widerstanden kann man zur Berechnung der Verstärkung von einem Stern in ein Dreick umwandeln dann hat jeder der beiden Widertände die nicht mit dem Ausgang verbunden sind den Wert R1*R2/(R1+2R2). Daraus errechnet sich die Verstärkung zu:<br />
<br />
'''Ua = 2*(U1 -U2) * (R1 + R2) / R1''' <br />
<br />
Ein einfacher Ausdruck der nur von der Differenz der Eingangsspannugen abhängt und der mit Änderung von R1 in der Amplitude einstellbar ist.<br />
<br />
<!-- Das funktioniert nicht mit allen Browsern, auskommentiert (SprinterSB) --><br />
<!--/div--><br />
<br />
<!-- Das funktioniert nicht mit allen Browsern, auskommentiert (SprinterSB) --><br />
<!--div style="border:2px solid #ffd700; margin-left:auto; margin-right:auto; padding:0.3em; text-align:left; max-width:20em;"--><br />
<br />
== Symmetrische Differenzverstärker ==<br />
<br />
Nun ist die Schaltung fast perfekt, in einigen Fällen ist jedoch auch die Laufzeit der Signale wichtig und es fällt auf, dass die beiden Eingangsgrößen U1 und U2 unterschiedlich lange Wege durch die Schaltung nehmen. <br />
<br />
Um dies auszugeichen geht man auf die Schaltung in Bild 3 zurück und versieht beide Eingänge in gleicher Weise mit Verstärkern nach Bild 1 und erhält die Konfiguration in Bild 7. Um die vielen Widerständ nicht einzeln zu benennen wird hier eine aus dem Farbcode abgeleitete Bezeichnung verwendet R1 = braun und R2 = rot. <br />
<br />
[[Bild:Operationsverst%C3%A4rkerBild7.gif]] <br />
<br />
In der ersten Stufe darf man bei dieser Anordnung die Verstärkung nicht zu groß wählen damit sie nicht intern übersteuert wird. Soll beispielsweise die Spannug 5V mit 5.01V verglichen und das Ergebnis 100fach verstärkt werden, dann kann man schlecht in der ersten Stufe die Spannungen auf 500V und 501V verstärken. Man kann dann in der der ersten Stufe die Verstärkung auf 1 oder wie hier auf 2 beschränken und die Verstärkung in der zweiten Stufe realisieren. <br />
<br />
Besser ist, wenigsten einen Teil der benötigten Verstärkung schon in der ersten Stufe einzubringen. Das gelingt weitgehend ohne interne Übersteuerung, wenn man die Bezugspegel der ersten Stufe nicht auf Masse setzt, sondern wie in der Schaltung nach Bild 8 mit der Kopplung der beiden Bezugspegel über den Widerstand R1 miteinander verbindet. Der Bezugspegel ist dann der Mittelwert der beiden Engangsspannungen und der wirksame Widerstand bei jedem Verstärker ist 0,5 * R1. <br />
<br />
Im oben angeführten Beispiel mit 5V und 5,01V ist der Mittelwert gerade 5,005V. <br />
Damit erhält man mit (0,5 * R1 + R2) / (0,5 * R1) = 100 die Ausgangsspannungen 5,505V und 4,505V aus denen in der letzten Stufe die gewünschte Differenz von 1V gewonnen wird. <br />
Für die Schaltung in Bild 8 gilt:<br />
<br />
'''Ua = (U1 - U2) * (R1 + 2*R2) / R1'''<br />
<br />
Die Schaltung nach Bild 8 hat zusätzlich den Vorteil, einer verbesserten Gleichtaktunterdrückung, auch ohne die Verwendung extra präziser Widerstände. Die erste Verstärkungstufe verstärkt nämlich nur das Differenzsignal und nicht den Mittelwert (Gleichtaktsignal). Die Schaltung nach Bild 8 heißt auch Instrumentenverstärker und ist auch fertig (ggf. R1 extern) als IC zu bekommen.<br />
<br />
So gibt es beispielsweise für die Auswertung von Messbrückenschaltungen immerhin schon einmal 5 Differenzverstärker, die mit ihren unterschiedlichen Schaltungen und Darstellungsweisen immer wieder für Verblüffung sorgen können.<br />
<br />
== Weitere Anwendungen für Operationsverstärker ==<br />
<br />
Operationsverstärker lassen sich nicht nur als normale Verstärker nutzen. Weitere Anwendungen sind:<br />
* Aktive [[Filter_(Elektronik)|Filter]]schaltungen<br />
* Regler-Schaltungen (z.B. PID), an sich auch nur eine Art Filter<br />
** als Spezialfall Spannungsregler<br />
* Aktive Gleichrichter / Präzisionsgleichrichter für Messzwecke<br />
* [[Analog-Komparator|Komparator]], allerdings meist nicht so gut wie ein echter Komparator<br />
* Frequenzgenerator / Oszillator (Sinus / Rechteck / Dreieck)<br />
* Transimpedanzverstärker (Strom Spannungswandler) (z.B. für Fotodioden)<br />
<br />
== Stabilität in OP Schaltungen ==<br />
<br />
Eine der Schwierigkeiten bei Schaltungen mit Operationsverstärkern ist es sicherzustellen, dass der <br />
Verstärker nicht schwingt. Hier soll keine ausführliche Darstellung der Stabilitätsanalyse folgen, sondern nur eine kurze, vereinfachte und eher praxisorientierte Form.<br />
<br />
Der Operationsverstärker kann zu schwingen anfangen, wenn aus der gewollten Gegenkopplung eine Mitkopplung wird. 180 Grad Phasenverschiebung entsprechen einer Invertierung und machen gerade aus der Gegenkopplung eine Mitkopplung. Durch RC Glieder (oder mit Induktivitäten) können Phasenverschiebungen erzeugt werden. Daher muß auf die Phasenverschiebung in der Rückkopplung (in der Regel vom Ausgang zum invertierenden Eingang) geachtet werden. Um die Bandbreite zu begrenzen haben die Operationsverstärker schon von sich aus etwa 90 Grad Phasenverschiebung über einen großen Frequenzbereich. Problematisch ist vor allem, wenn die Rückkopplung zu spät kommt. Die Bandbreite (für Schleifenverstärkung von eins) des Operationsverstärkers gibt vor, bis zu welcher Frequenz keine größeren Phasenverschiebungen (in Richtung Verzögerung) auftreten dürfen. Daran sieht man schon, dass es leichter ist einen langsamen Operationsverstärker stabil zu kriegen, als einen schnellen. <br />
<br />
Schlecht für die Stabilität sind:<br />
<br />
- Tiefpass-charakter in der Rückkopplung: dies führt leicht zum Schwingen.<br />
<br />
- Kapazität gegen Masse am Ausgang des OPs: dies sorgt für eine Verzögerung des Ausgangssignals.<br />
<br />
- Kapazität gegen Masse am invertierenden Eingang: dies ergibt zusammen mit einem Rückkopplungswiderstand einen Tiefpass.<br />
<br />
- OP mit hoher Bandbreite: parasitäre Kapazitäten und Induktivitäten werden wichtiger.<br />
<br />
- hochohmige Rückkopplung ohne parallelen Kondensator<br />
<br />
- Verstärkung in der Rückkopplung: Gefahr von Verzögerungen und schon an sich schlecht, weil sich das Verstärkungs-Bandbreitenprodukt erhöht.<br />
<br />
- lange Leitungen: geben zusätzliche Kapazitäten und Induktivitäten (je schneller desto kleiner)<br />
<br />
- fehlender Entkoppelkondensator an der Versorgungsspannung, besonders bei schnellen OPs<br />
<br />
- niedrige Versorgungsspannung bei einigen OPs mit JFets (z.B. TL072)<br />
<br />
Von den Standardschaltungen mit OPs sind die folgenden etwas problematisch: Differenzierer, Hochpass, Transimpedanzverstärker. <br />
<br />
Um die die Stabilität zu verbessern, kann man gezielt für einen Hochpass-Character in der Rückkopplung sorgen, z.B. durch einen kleinen Kondensator vom Ausgang zum inv. Eingang. Dadurch verringert sich aber auch die Bandbreite der Schaltung. Wenn der Ausgang kapazitive Lasten treiben soll (z.B. lange Kabel) sollte ein Widerstand (z.B. 100 Ohm) vor die Last geschaltet werden. Je nach OP liegt die Grenze bei etwa 20pF (z.B. TLV271) bis 5 nF (z.B. LF356). Vor allem schnelle sparsame Operationsverstärker sind empfindlich. <br />
<br />
Etwas gegen die Intuition sind Verstärkerschaltungen mit einer hohen Verstärkung für das Signal weniger schwingungsanfällig als solche mit einer kleinen Verstärkung (z.B. 1 beim Impedanzwandler). Dies liegt daran, dass die hohe äußere Verstärkung die Schleifenverstärkung reduziert. Einige OPs (z.B. OP37, LF357): sind speziell für Schaltungen mit einer Verstärkung von mindestens z.B. 5 gedacht - bei weniger Verstärkung muss man mit Schwingungen rechnen. Einige moderne OPs sind auch mit relativ großer Kapazität am Ausgang stabil - es bleibt aber bei einer Verschlechterung der Phasenreserve wenn der Ausgang Kapazitiv belastet wird. <br />
<br />
Bei etwas komplizierteren Schaltungen kann eine Simulation (z.B. mit [[SwitcherCAD-Tutorial|LTSpice]]) sinnvoll sein. Dabei sollten auch parasitäre Kapazitäten mit berücksichtigt werden. Die Neigung zu Schwingungen kann im Zeitbereich als Überschwinger bzw. Nachschwingen beim Einem Rechtecksignal oder im Frequenzbereich als Resonanz (Maximum im Frequenzgang) erkannt werden.<br />
<br />
==Liste gängiger Typen von Operationsverstärkern==<br />
Die folgende Liste gibt eine Auswahl der wichtigsten Daten für einige Operationsverstärker. Das sind zum einen einige ältere und in Schaltplänen häufiger zu findende Typen (MC1458 , µA741, TL072, LM324), dann einige für Bots interessante Typen und auch ein paar eher exotische Typen für ungewöhnliche Anforderungen (z.B. OP177, AD8551, TCA0372, OPA355).<br />
<br />
----<br />
{| {{Blauetabelle}} style="text-align:center;"<br />
|+ im Aufbau http://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=257490&highlight=#257490<br />
|-<br />
|Typ||Vmin||Vmax||Voff||Voff||Vn||i in||di in||Gain||SR||BW||i out||Is||Rail in||Rail out||single||double||quad<br />
|-<br />
|.||V||V||mV||µV/°C||nV/Hz^,5||nA||nA||V/mV||V/µs||MHz||mA||mA||lo / hi||lo / hi||€||€||€<br />
|-<br />
|MC1458||10||36||2||.||30||80||20||15||0,5||1||20||1,5||2 / -2||1 / -1||.||0,18||.<br />
|-<br />
|µA741||?10||36||2||15||23||80||20||200||0,5||1,5||25||1,7||2 / -2||1 / -1||0,19||.||.<br />
|-<br />
|LM358||3||32||5||7||40||45||5||15||0,5||1||20/8||0,2||0 / -1,5||0 / -2||.||0,18||LM324<br />
|-<br />
|LM324||3||32||5||7||40||45||5||15||0,5||1||20/8||0,2||0 / -1,5||0 / -2||.||.||0,19<br />
|-<br />
|TL072||7||36||3||18||18||65p||5p||200||13||3||20||0,7||3 / 0||1,5/-1,5||0,27||0,28||0,29<br />
|-<br />
|TLC 272||3||16||1,1||1,8||25||0,6p||0,1p||27||4||2||30||0,7||-0,3/-0,8||(0.3)/-1,2||0,38||0,35||0,45<br />
|-<br />
|MC33078||10||36||0,15||2||4,5||300||25||300||7||9||30||2||2 / -2||1 / -1||.||0,35||0,99<br />
|-<br />
|OP 07||6||36||0,03||1,3||10||1,2||0,5||400||0,3||0,6||.||1||1 / -1||2 / -2||0,29||.||.<br />
|-<br />
|MCP6042||1,4||6||3||2||170||1p||1p||115dB||0,003||0,014||2-20||0,6µ||-0,3/0,3||0 / 0||x||0,71||1,35<br />
|-<br />
|MCP6002||1,8||5,5||7||2||28||1p||1p||400||0,6||1||6..20||0,1||-0,3/0,3||0 / 0||x||0,31||0,40<br />
|-<br />
|ICL7612||2||16||5||15||100||1p||0,5p||10||1,6||1,4||.||..1||-0,3/0,3||0 / 0||1,25||1,50||x<br />
|-<br />
|TS912||2,7||16||5||5||30||1p||1p||40||0,4||0,8||65||0,25||-0,2/0,2||0 / 0||.||0,93||1,20<br />
|-<br />
|OP177||5||44||4µ||0,03||10||1,5||0,3||12000||0,3||0,6||12||1,6||1 / -1||1 / -1||1,15||.||.<br />
|-<br />
|LTC1050||4,75||16||0,5µ||0,05||90||0,01||0,02||160dB||4||2,5||20/4||1||0/-1,7||0 / 0||2,95||.||.<br />
|-<br />
|AD8552||2,7||5,5||1µ||0,005||42||0,01||0,02||145dB||0,4||1,5||30/30||0,7||0 / 0||0 /0||1,90||3,20||x<br />
|-<br />
|TCA0372||5?||40||1||20|| 22 || 100|| 10||1 || 1,4||1,4 ||1000||2,5||0/-1||1 /-1||.||1,15||.<br />
|-<br />
|LMC6482||3||16||3,8||1,0||37||0,02p||0.01p||130dB||0,9||1,5||30||0,75||-0,3/0,3||0/0||.||0,99||1,95<br />
|-<br />
|OPA355||2,5||5,5||2||7,0||5,8||3-50p||?||>80dB||300||200||60||8,3||-0,1/-1,5||0,1/-0,2||€?||€?||€?(triple)<br />
|-<br />
|TS922||2,7||12||0,9||2||9||15||1||200||1,3||4||80||4,5||-0,2/0,2||0,1/-0,2||€?||€?||€?<br />
|-<br />
|}<br />
----<br />
<br />
;Erklärung der Spalten:<br />
Typ gibt die Bezeichnung für die 2-fach Version an, sofern verfügbar. Die 1-fach/4-fach Versionen unterscheiden sich meist in der letzten Ziffer.<br />
<br />
Vmin / Vmax sind die minimale und maximale Versorgungsspannung. Bei symmetrischer Versorgung die Differenz (V+) - (V-).<br />
<br />
Voff ist die Offsetspannung oder der Gleichspannungsfehler. Das ist die Gleichspannung die am Eingang anliegen muss, um den Ausgang auf eine mittlere Spannung zu bringen. Der Wert ist als typische obere Grenze zu verstehen, wobei positive oder negative Werte möglich sind. Dazu wird noch die typische Grenze der Temperaturabhängigkeit von Voff angegeben.<br />
<br />
Vn ist die Rauschspannungsdichte. Für Frequenzen unter etwa 1 kHz kann das Rauschen deutlich höher werden.<br />
<br />
i in ist der Bias Strom. Das ist der mittlere Eingangsstrom der beiden Eingänge. Die Werte geben nur die Größenordnung an und können stark von Exemplar zu Exemplar streuen. Außerdem ist der Bias Strom zu Teil (FET Eingänge) stark Temperaturabhängig.<br />
<br />
di in ist der Offsetstrom oder die Differenz der Eingangsströme der beiden Eingänge. Der Wert ist als typische obere Grenze zu verstehen. <br />
<br />
Gain ist die Verstärkung für niedrige Frequenzen (z.B. 1 Hz).<br />
<br />
SR ist die maximale Geschwindigkeit für Änderungen der Ausgangsspannung, engl. Slewrate. Bei FET Eingängen ist dafür ein relativ großes Eingangssignal nötig.<br />
<br />
BW ist die Bandbreite. Angeben ist die Frequenz bei der die Verstärkung bis auf 1 abfällt oder das Produkt aus Frequenz und Verstärkung bei mittleren Frequenzen. Die Frequenz des Nutzsignals sollte normalerweise mindestens um den Faktor 10 mal der Verstärkung der Schaltung niedriger liegen.<br />
<br />
i out ist der maximale Ausgangsstrom. Die meisten OPs sind zumindest kurzzeitig kurzschlussfest. Teils ist der Strom getrennt für das negative und positive Vorzeichen angegeben. <br />
<br />
Is ist der typische Stromverbauch pro Verstärker. Zum Teil ist der Stromverbrauch deutlich von der Spannung abhängig.<br />
<br />
Rail in ist der Eingangspannungsbereich oder Gleichtaktbereich. Angegeben ist für die untere und obere Grenze jeweils die Differenz zur negativen bzw. positiven Versorgungsspannung.<br />
<br />
Rail out ist der Ausgangspannungsbereich. Angegeben ist der Mindestabstand zur negativen und positiven Versorgungsspannung. Der Wert 0 kann natürlich nicht wirklich erreicht werden, aber die Spannung kann bei kleinem Strom (z.B. 10 µA) bis auf ein paar mV an die Versorgung heran.<br />
<br />
single/double/quad geben circa Preise für einfach / doppel / 4-fach Ausführungen an, soweit sie verfügbar sind.<br />
<br />
==Autor/en==<br />
* Manf<br />
<br />
==Weblinks==<br />
* [http://www.mikrocontroller.net/articles/Operationsverst%C3%A4rker-Grundschaltungen Ähnliche Liste von gebräuchlichen OPVs wie hier - mikrocontroller.net]<br />
* [http://www.eetkorea.com/ARTICLES/2003SEP/A/2003SEP19_AMD_AN07.PDF Op Amp Circuit Collection] - National Semiconductor Application Note 31 mit weiteren OP-Schaltungen<br />
* [http://www.elektronik-kompendium.de/sites/bau/0209092.htm OP in DAS ELKO]<br />
* [http://www.mikrocontroller.net/articles/Operationsverst%C3%A4rker-Grundschaltungen Operationsverstärker-Grundschaltungen - mikrocontroller.net]<br />
* http://www2.fh-fulda.de/~pfisterer/mt/mt8.pdf<br />
* [http://www.elektronikwissen.net/opamp/9-opamp-wissen.html Praxishinweise und schwingende Operationsverstärker] <br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Grundlagen]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Motorkraft_berechnen&diff=25351Motorkraft berechnen2014-10-09T19:35:19Z<p>Besserwessi: /* Haftreibung */</p>
<hr />
<div>== Vorwort ==<br />
=== Allgemein ===<br />
Wenn man einen Roboter plant, stellt man sich häufig die Frage, wie stark die Motoren sein müssen. Sind sie zu schwach, dann kommt der Roboter nicht von der Stelle. Sind sie sie jedoch zu stark, dann verpulvert man unnötig Energie und verringert dadurch die Fahrzeit. Es gibt zwar etliche Überschlagsrechnungen, doch leider blieb dem Roboterbauer bis jetzt eine genaue Berechnung vorenthalten. '''Dieser Artikel versucht das Problem möglichst genau zu behandeln.'''<br />
<br />
=== Technische Informationen ===<br />
Zum Berechnen der Motorkraft muss man erst einige Reibungen und Widerstände kennenlernen. Diese werden nun im Folgenden beschrieben. Man muss nicht jede Kraft berechnen, einige Kräfte kann man auch mit einem Pauschalzuschlag unter den Tisch fallen lassen. Ein Beispiel: ''Luftwiderstand von einer Schnecke.'' Sicherlich darf man aber den Luftwiderstand bei einem Rennwagen nicht unterschlagen. Ich hoffe, ihr versteht, was ich sagen will.<br />
== Bewegungsreibungen ==<br />
Darunter fallen Haft-, Gleit- und Rollreibung. Diese sind von der Geschwindigkeit unabhängig. '''Wichtig:''' Sollte sich der Roboter an einer Schräge befinden, berechnet sich die Reibung noch mit dem Koeffizienten cos(alpha). Alpha ist hierbei der Steigungswinkel. Mehr siehe dazu bitte Steigung/Gefälle.<br />
=== Haftreibung ===<br />
Die Reibung, die auftritt, wenn ein Körper ohne Räder steht. Sie berechnet sich aus:<br />
<math>F=fh*m*g</math><br />
*F: Kraft in N<br />
*fh: Reibungskoeffizient (Haftreibung), typisch 0,2 bis 0,6<br />
*m: Masse des Roboters in kg<br />
*g: Ortsfaktor (ca. 10N/kg bzw. 10m/(sec^2))<br />
<br />
Ortsfaktor g entspricht der Erdanziehungskraft bzw. Fallbeschleunigung, etwa 9,81m/s². Es reicht aber für gewöhnlich mit 10m/s² zu rechnen. Der Reibungskoeffizient ist von beiden beteiligten Materialien abhängig [http://de.wikipedia.org/wiki/Haftreibungskoeffizient].<br />
<br />
Die Haftreibung wird etwa gebraucht, um zu berechnen, wie viel Kraft nötig ist um ein Hindernis weg zu schieben. Der andere Punkt wo die Haftreibung wichtig ist, das sie bestimmt wann die Räder beginnen durch zu drehen, bzw. welche Kraft maximal über die Räder übertragen werden kann. Hier muss dann ggf. dafür gesorgt werden das die Reibungskoeffizienten genügend groß werden (z.B. Gummiräder) und genügend Gewicht auf den Antriebsrädern ist.<br />
<br />
Mehr Kraft als nötig ist, um die Räder durchdrehen zu lassen braucht man in der Regel nicht. Damit erhält man eine relativ einfach zu berechnende obere Grenze für die sinnvolle Motorkraft. Wenn es um einen möglichst schnellen Bot geht, kann man hier schon fast aufhören und den Motor nach der Reibkraft der Räder auslegen.<br />
<br />
=== Gleitreibung ===<br />
Wird zum Beispiel bei Robotern mit 2 Rädern (z.B. Asuro) benötigt, wo statt eines dritten Rades ein Gleiter benutzt wird. Wie die Haftreibung oben, muß hier nur der über den Gleiter übertragene Teil des Gewichts berüchsichtigt werden. Der Gleitreibungskoeffizient ist normalerweise etwas kleiner als der Haftreibungskoeffizient.<br />
<br />
<math>F=fg*m*g</math><br />
<br />
*fg: Reibungskoeffizient (Gleitreibung)<br />
<br />
=== Rollreibung ===<br />
Wie der Name schon sagt, tritt diese Kraft auf, wenn Räder oder Rollen benutzt werden. Streng genommen müßte auch hier zwischen Stillstand und Fahren unterschieden werden. Diese errechnet sich folgendermaßen.<br />
<math>F=fr*m*g</math><br />
*fr: Reibungskoeffizient (Rollreibung)<br />
<br />
== Beschleunigung ==<br />
Für das Anfahren muss man die benötigte Beschleunigung und die dazu nötige Kraft wissen.<br />
<br />
<br />
=== Benötigte Beschleunigung berechnen ===<br />
Die Beschleunigung wird mit a (engl.: ''acceleration'') bezeichnet. Sie hat die Einheit m/(sec^2). Diese muss man zuerst ermitteln. <br />
<br />
Man kann diese anhand der Geschwindigkeit erechnen:<br />
<br />
Dabei gilt: <math>v=a*t</math>.<br />
*v: Geschwindigkeit in m/sec (nach t Sekunden)<br />
*t: die Zeit in sec.<br />
<br />
''Beispiel: Der Roboter soll in 10 Sekunden aus dem Stand (= 0 m/s) auf 25km/h (entspricht ca: 6,94 m/s) beschleunigen können. Wenn wir die Formel umstellen erhalten wir: a = v / t also ist a = 6,94m/s / 10s = 0,694m/s². Soll der Roboter bereits nach 1 Sekunde auf 25km/h beschleunigen, wäre entsprechend eine nötige Beschleunig a = 6,94m/s / 1s = 6,94m/s².''<br />
<br />
<br />
Auch kann man diese anhand der zurückgelegten Strecke ermitteln:<br />
<br />
Dabei gilt: s=0.5*t^2*a.<br />
*s ist die Strecke in m (nach der Zeit t)<br />
*t die Zeit in Sekunden.<br />
<br />
''Wichtig: Geschwindigkeiten für die Berechnungen immer in m/s (Meter pro Sekunde) umrechnen, da man sonst schnell Probleme mit den Formeln bekommt. Die Umrechung ist einfach: 3,6 km/h = 1 m/s (d.h. von km/h in m/s einfach durch 3,6 dividieren, in die Rückrichtung mit 3,6 multiplizieren; diese Berechung ist exakt, also keine Schätzung).''<br />
<br />
<br />
=== Kraftberechnung ===<br />
Hat man nun die Beschleunigung a ermittelt, kann man die benötigte Kraft über F=m*a errechnen. Vorausgesetzt wird, dass die Masse konstant bleibt. Das würde also für Roboter mit Raketenantrieb z.B nicht funktionieren. Genauso wäre die Kraft unterschiedlich, wenn der Roboter gerade etwas transportiert.<br />
<br />
''Merke: "Kraft ist Masse mal Beschleunigung"''<br />
<br />
<br />
Die Einheit der Kraft ist Newton und wird mit "N" abgekürzt: 1 N = 1 kg · m / s². Oft werden auch Werte in mN (milli-Newton = 0.001 Newton) angegeben.<br />
<br />
''Achtung: mN (milli-Newton) bitte nicht mit Nm (Newton-Meter) oder mNm (milli-Newton-Meter) verwechseln. Das sind andere Werte und beziehen sich auf das Drehmoment!!!''<br />
<br />
=== Bremskraft ===<br />
Das Gleiche gilt auch für das Bremsen, nur dass hier Energie freigesetzt wird. Denn die Reifen darf man nicht zu sehr blockieren, sonst überschreitet man die [#Haftreibung] und der Roboter rutscht. <br />
Die Kraft F kann man in diesem Fall durch <math>F=p/t</math> nehmen. P ist der Impuls und t die Zeit.<br />
Ein Impuls ist das Produkt aus Masse mal Geschwindigkeit. Also: <math>P=m*v</math>. Das setzt man nun in die Gleichung ein und erhält: <math>F=(m*v)/t</math>. Alternativ kann man die Bremskraft aus <math>F=m*a</math> errechnen. <br />
Überschreitet nun die errechnete Kraft F die Haftreibung, rutscht euer Roboter garantiert.<br />
<br />
== Steigung/Gefälle ==<br />
=== Steigung ===<br />
Wenn man einen Berg hochfährt muss man neben der Reibung auch noch die Erdanziehungskraft überwinden. Bei Steigungen gibt es grundlegend 2 Kräfte. <br />
* Normalkraft: Kraft, die auf den Boden wirkt und aus der die (Haft-/Gleit-/Roll-)Reibung berechnet wird. Diese berechnet sich aus <math>Fn=m*g*cos(alpha)</math>. Alpha ist hier die Steigung. Sollte man mit dem Roboter also eine Steigung überwinden, hat man eine geringere (Haft-/Gleit-/Roll-)Reibung zu überwinden.<br />
* Hang(auf|ab)triebskraft: Kraft, die der Roboter überwinden muss, um den Berg hinaufzufahren. Diese Kraft kann man aus <math>Fh=m*g*sin(alpha)</math> errechnen. <br />
=== Gefälle ===<br />
Bei Gefällen gilt das Gleiche wie bei Steigungen, nur dass hier die Hangabtriebskraft wirkt. Diese berechnet sich wie die Hangauftriebskraft. Die Berechnung der Normalkraft ist identisch wie die Berechnung der benötigten Kraft für die Steigung.<br />
<br />
=== Hindernisse ===<br />
<br />
Hindernisse, wie Türschwellen oder kleine Stufen kann man ähnlich wie Steigungen betrachten. Anhand der Radduchmesser kann man sich überlegen was für einer Steigung das Hindernis entspricht. Man betrachtet dazu die Verlagerung des Schwerpunktes des Robots, wenn sich die Räder ein wenig drehen, um über das Hindernis zu kommen. Vereinfacht kann man das Hindernis durch eine Rampe ersetzen, die die Räder an der selben Stelle wie das reale Hindernis berührt.<br />
<br />
==Luftwiderstand ==<br />
Der Luftwiderstand betrifft eigentlich nur schnelle Roboter. Hierzu braucht man eine Latte von Koeffizienten: Luftdichte ld, Reibungskoeffizient cw und die "Luftaufprallfläche" A.<br />
<math>F=0.5v^2*cw*ld*A</math>. Die Luftdichte ist standardgemäß 1.1. Für cw muss man nach Koeffizienten suchen. A ist die Aufprallfäche, das ist die Seite, die mit dem Fahrtwind konfrontiert wird. Mehr dieser Koeffizienten findet man unter Weblinks.<br />
== Errechnung der Motorkraft ==<br />
Man berechnet nun die Einzelkräfte für die verschiedenen Szenarios (z.B. Fahren auf Kies, Schotter, den Hang hinauf, über die Türschwelle etc).<br />
Die benötigten Einzelkräfte (Rollreibung, Luftwiderstand, Hang(auf|ab)triebskraft etc.) der Szenarios werden jeweils addiert:<br />
<br />
'''Fges=F1+F2+...+Fn'''<br />
<br />
Diese Gesamtkraft ist die Kraft, die -umgesetzt- werden muss, d.h. wirklich am Rad (oder was auch immer verwendet wird) wirken muss. Hat man die benötigte Kraft errechnet gilt:<br />
<br />
'''Mrad=Fges*r'''<br />
<br />
* Fges ... nötige Kraft [in Newton]<br />
* r ... Radius des Rades<br />
* Mrad ... nötiges Drehmoment am Rad [in Newton-Meter]<br />
<br />
Es geht jedoch zwischen Motor und Rad noch Kraft verloren - vor allem durch das Getriebe.<br />
Die Gesamtkraft muss also noch durch die Effektivität (z.B. des Getriebes) dividiert werden.<br />
<br />
'''Mmotor = Mrad / Eff%'''<br />
<br />
* Mmotor ... nötiges Drehmoment am Motor [in Newton-Meter]<br />
* Eff% ... Effektivität der Kraftübertragung (Motor zu Rad) [in Prozent, d.h. Wert 0-1, 1 = 100%]<br />
<br />
Die obige Rechnung geht davon aus, dass die Kraft 1:1 vom Motor auf das Rad übertragen wird. Wird ein Getriebe verwendet, muss die Gleichung etwas angepasst werden.<br />
<br />
<br />
<br />
=== Getriebe ===<br />
<br />
Ein Getriebe hat die Aufgabe Geschwindigkeit in Kraft oder Kraft in Geschwindigkeit umzuwandeln. Setzt man nun ein Getriebe mit 1:200 ein, wird die Kraft 200fach stärker, die Geschwindigkeit 200fach langsamer (wenn man die Reibung vernachlässigt). <br />
Ein Getriebe ist auf jeden Fall sinnvoll und zu empfehlen.<br />
<br />
Für die nötige Motorkraft gilt dann:<br />
<br />
'''Mmotor = Mrad / X / Eff%'''<br />
<br />
* X ... Übersetzungsverhältnis (Verringerungsfaktor der Drehzahl) des Getriebes<br />
<br />
Die Effektivität eines Getriebes liegt meist irgendwo bei 95% bis 47%. Man schätzt bei "normalen" Getrieben (das gilt also nicht für Planetengetriebe) mit 10% Verlust an Kraft pro Übersetzungsstufe (Anzahl Zahnräder - 1). Je größer die Übersetzung X und je kleiner die Bauform des Getriebes relativ zur Dicke der Achse ist, desto mehr Übersetzungsstufen werden notwendig. Die Effektivität ist auch vom Material, Schmiere und Raumtemperatur abhängig. Wenn man sein Getriebe nicht gerade selbst baut, schaut man diesen Wert besser im Datenblatt nach.<br />
<br />
Zur Schätzung kann man rechnen: Eff% = (100% - Verlust%) ^ N<br />
* Verlust% ... Verlust pro Übersetzungsstufe<br />
* N ... Übersetzungsstufen<br />
<br />
Für 10% Leistungsverlust pro Stufe ergeben sich folgende Werte:<br />
1 Stufe ... 90% ... 3:1<br />
2 Stufen ... 81% ... 9:1<br />
3 Stufen ... 73% ... 27:1<br />
4 Stufen ... 66% ... 81:1<br />
5 Stufen ... 59% ... 243:1 <br />
6 Stufen ... 53% ... 729:1<br />
7 Stufen ... 48% ... 2187:1<br />
<br />
In der letzten Spalte ist angegeben, wie z.B. das Übersetzungsverhältnis aussehen könnte, wenn pro Stufe ein Verhältnis von 3:1 erreicht wird. Dies ist allerdings nur als Anhaltspunkt zu sehen, man kann dadurch nur ansatzweise die Anzahl Übersetzungsstufen schätzen. Letztlich sollte man sich an die Werte im Datenblatt halten, wenn es um ein bestimmtes Getriebe geht.<br />
<br />
=== Geschwindigkeit ===<br />
Die Geschwindkeit des Roboters ist das Produkt aus 2*&pi;, dem Reifenradius (r) und der Drehzahl (N).<br />
Ein eventuell vorhandenes Getriebe (Übersetzungsverhältnis X:1) ist natürlich miteinzubeziehen.<br />
<br />
Als Formel (ohne Getriebe): '''v = 2&pi; * r * N'''<br />
<br />
Als Formel (mit Getriebe): '''v = 2&pi; * r * N / X'''<br />
<br />
== Leistung ==<br />
Das optimale Drehmoment Mopt (nicht zu verwechseln mit dem maximalen Drehmoment!) und die dabei erreichte Winkelgeschwindigkeit ergeben als Produkt die maximale Leistung Pmax:<br />
<br />
'''Pmax = Mopt * &omega;'''<br />
<br />
Die gesuchte Winkelgeschwindigkeit lässt sich aus der Drehzahl Nopt (nicht zu verwechseln mit der Leerlauf-Drehzahl), die bei dem optimalen Drehmoment erreicht wird, berechnen:<br />
<br />
&omega; = 2 * &pi; / T = 2 * &pi; * f<br />
<br />
f = Nopt [in "pro Sekunde"]<br />
<br />
Es gilt daher:<br />
<br />
'''Pmax = Mopt * 2&pi; * Nopt'''<br />
<br />
Zum Beispiel:<br />
Mopt = 2,5 mNm = 0,0025 Nm<br />
Nopt = 7200/min = 120/s (7200 Umdrehungen pro Minute)<br />
Pmax = 1,885 Nm/s = 1,885 Watt<br />
<br />
Setzt man für Mopt das notwendige Motordrehmoment Mmotor (= Mrad / X / Eff%) ein, so erhält man:<br />
<br />
Pmin = Mrad / X / Eff% * Nopt * 2 * &pi;<br />
Mrad = Fges * r<br />
Pmin = Fges * r / X / Eff% * Nopt * 2 * &pi;<br />
v = 2*&pi;*r*N/X <br />
X = 2*&pi;*r*N/v<br />
N = Nopt<br />
Pmin = Fges * r / (2*&pi;*r*Nopt) * v / Eff% * Nopt * 2 * &pi;<br />
Pmin = Fges * v / Eff%<br />
<br />
Wir können also die minimal nötige Motorleistung zurückführen auf:<br />
'''Pmin = Fges * v / Eff%'''<br />
<br />
== Notwendige Motorleistung ==<br />
<br />
Die nötige Geschwindigkeit ist je nach Szenario variabel. Der Roboter muss z.B. über eine Türschwelle vielleicht nicht unbedingt so schnell fahren wie auf ebenem Untergrund. Deshalb sind alle Szenarien getrennt durchzurechnen. Als notwendige Motorleistung ist dann die höchste errechnete Leistung aller Szenarien zu werten.<br />
<br />
Wenn man kein umschaltbares Getriebe hat, sollte man die Berechnung besser anhand der Kraft oder des Drehmomentes machen. Die nötige Kraft ergibt sich aus dem ungünstigsten Szenario, z.B. das Anfahren an einer Steigung.<br />
<br />
== Rechenbeispiel für die benötigte Leistung ==<br />
<br />
Annahmen: Das Getriebe ist variabel und hat eine fixe Effizienz von 50%.<br />
<br />
'''Unveränderliche Werte:'''<br />
* Gewicht (m) = 3kg<br />
* Fallbeschleunigung (g) = 9,81m/s² [Erdanziehung in Mitteleuropa]<br />
* Rollreibungskoeffizient (fr) = 0,05 [Autoreifen auf Erdweg]<br />
* Radius der Räder (r) = 0,0125m [ = 2.5cm Durchmesser]<br />
* Getriebe-Effektivität: 0,5 (= 50%; Schätzwert)<br />
<br />
<br />
'''Pmin = Fges * v / 0.5'''<br />
<br />
<br />
'''Szenario 1 (Ebene):'''<br />
* Geschwindigkeit (v) = 0,1 m/s<br />
<br />
Fn = fr * m * g = 1,4715 N<br />
Fges = Fn = 1,4715 N<br />
Pmin = Fges * v / 0.5<br />
Pmin = 0,2943 W<br />
<br />
<br />
'''Szenario 2 (langsameres Überfahren der Türschwelle):'''<br />
* Geschwindigkeit (v) = 0,03 m/s (Achtung! Weniger Geschwindigkeit als bei Szenario 1)<br />
* Höhe der Türschwelle: 0,01m [ = 1cm ]<br />
* Steigungswinkel (alpha): 39,23° [ alpha = arccos((r - h) / r)/2 ]<br />
<br />
Fn = fr * m * g * cos(alpha) = 1,140 N<br />
Fh = m * g * sin(alpha) = 18,613 N<br />
Fges = Fn + Fh = 19.753 N<br />
Pmin = Fges * v / 0.5 (gilt nur für Eff% = 0,5!)<br />
Pmin = 0,39506 W<br />
<br />
Das Maximum für Pmin aller Szenarien ist damit 0,39506 Watt.<br />
<br />
== Was bei der Komponentenwahl zu beachten ist ==<br />
<br />
Die minimal notwendige Leistung haben wir gerade berechnet oder besser abgeschätzt. Dabei wurde aber vernachlässigt, dass das Getriebe nicht von Szenario zu Szenario wechselt, sondern gleich bleibt. Zudem sind wir davon ausgegangen, dass ein Getriebe für jedes beliebige Übersetzungsverhältnis erhältlich (und bezahlbar) ist.<br />
<br />
Das bedeutet, man kann mit diesem Pmin nur ersteinmal eine Art Vorauswahl treffen. Für die in Frage kommenden Motoren und Getriebe ist dann noch einmal alles genau durchzurechnen. Vor allem gibt es für die Getriebe maximale Belastungen für die Eingangsdrehzahl und das maximale Last-Drehmoment. Das Lastdrehmoment entspricht an sich Mrad. Es kann aber auch weniger sein, d.h. Mrad/Y, wenn der Motor nicht direkt an das Getriebe angeschlossen wird, sondern dabei nochmal durch ein kleines "Getriebe" Y:1 übersetzt wird. Dann ist allerdings das Übersetzungsverhältnis des "Fertiggetriebes" X:1 noch durch Y zu teilen, d.h. mit X/Y statt mit X zu rechnen, da X in der Rechnung das vollständige Übersetzungsverhältnis vom Motor bis zur Kraftübertragung auf den Fahruntergrund entspricht.<br />
<br />
Folgende Herangehensweise ist empfehlenswert:<br />
# Motoren heraussuchen, deren Leistung (= Nenndrehzahl (U/min)/60 * Nenndrehmoment (mNm)) größer als die berechnete Mindestleistung ist<br />
# Mit dem preiswertesten Motor (Pmotor, Nopt = Nenndrehzahl, Mopt = Nenndrehmoment, Mmax = max. Drehmoment) beginnend:<br />
# Geschwindigkeit v des Szenarios mit der höchsten notwendigen Leistung Pmin verwenden, um das optimale Übersetzungsverhältnis zu errechnen: X = 2*&pi;*r*Nopt/v<br />
# Getriebe auswählen (Nmax = max. Drehzahl, Mmax = Max. Drehmoment (Dauerlast), Übersetzungsverhältnis X:1), das X:1 am besten entspricht. Dabei darf es höchstens das Übersetzungsverhältnis X:1 aufweisen (sonst ist die Geschwindigkeit später geringer als gewünscht). Bei verschiedenen Fabrikaten das Preiswerteste zuerst.<br />
# Prüfen, ob das Getriebe mit dem maximal möglichen Drehmoment klar kommt (Dauerlast-Angabe nehmen! nicht max. Spitzendrehmoment!)<br />
# Rückrechnen des nötigen Drehmoments Mmotor = Fges*r / X / Eff% mit X und Eff% des realen Getriebes für alle Szenarien und prüfen, ob Mmotor < Mmax des Motors.<br />
# Sämtliche Getriebe durchrechnen, bis eines paßt oder keines mehr übrig ist...<br />
# Falls kein Getriebe mit diesem Motor die Erfordernisse erfüllt, den nächst teureren Motor durchprobieren...<br />
<br />
<br />
Hinweis: Schließt man die Räder direkt an eine Achse eines Getriebes oder des Motors an, muss man darauf achten, dass die zulässige Achslast nicht überschritten wird, sonst werden Motor oder Getriebe geschädigt. Koppelt man das Rad über Zahnräder, Riemen oder Zahnriemenräder und verpasst der Radachse ein eigenes Lager, das entsprechend robuster ist, kann man das Übersetzungsverhältnis möglicherweise noch etwas anpassen, sodass es dem optimalen, berechneten Übersetzungsverhältnis noch näher kommt. Dadurch hat man allerdings zusätzliche Reibung (in den Lagern und bei der Kopplung), die in die Effizienzberechnungen (Effizienzen aller Kraftübertragungen immer aufmultiplizieren) eingeht. Evtl. kann man dadurch aber auch gleich noch Probleme beseitigen, die durch die Beschränkungen der "Fertiggetriebe" daherkommen (z.B. könnte man das nötige Drehmoment am "Fertiggetriebe"-Ausgang damit reduzieren, sodass es geringer als das zulässige Dauerdrehmoment des Getriebes ist).<br />
<br />
== Autoren ==<br />
*--[[Benutzer:Overthere|Overthere]] 22:24, 1. Feb 2007 (CET)<br />
*--[[Benutzer:SDwarfs|SDwarfs]] 15. May 2007<br />
<br />
=== Bitte noch ergänzen ===<br />
Jeder ist herzlich willkommen, diesen Artikel zu erweitern oder zu verbessern.<br />
<br />
== Weblinks ==<br />
*[http://de.wikipedia.org/wiki/Haftreibung Haftreibung]<br />
*[http://de.wikipedia.org/wiki/Haftreibungskoeffizient| Koeffizienten für Haft- und Gleitreibung ] <br />
*[http://de.wikipedia.org/wiki/Rollreibung Rollreibung]<br />
*[http://de.wikipedia.org/wiki/Luftwiderstand Luftriebungskoeffizenten]<br />
*[http://www.roboternetz.de/phpBB2/motordrehmoment.php Stärke des Motors online berechnen lassen]<br />
<br />
<br />
<br />
[[Kategorie:Motoren]]<br />
[[Kategorie:Grundlagen]]<br />
[[Kategorie:Mechanik]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=ADC&diff=25299ADC2014-09-25T18:22:10Z<p>Besserwessi: /* Allgemeines */</p>
<hr />
<div>Abkürzung des englischen Begriffs '''Analog to Digital Converter''', im deutschen Sprachgebrauch '''A/D-Wandler''' (Analog nach Digital Wandler) oder '''A/D-Umsetzer''' genannt.<br />
__NOTOC__<br />
== Allgemeines ==<br />
<br />
Da es in der digitalen Welt nur zwei Zustände gibt, um Informationen darzustellen, wird ein A/D-Wandler benötigt, um analoge Größen in digitale Werte umzuwandeln. Er ist somit das Gegenteil zum [[DAC]] (Digital-Analog-Wandler), der digitale Werte in analoge Größen wandelt.<br />
<br />
;Merkmale eines A/D-Wandlers<br />
* Auflösung in Bit<br />
* Geschwindigkeit der Umwandlung<br />
* Spannungsbereich bzw. Referenzspannung<br />
* Genauigkeit (Drift, Wiederhohlgenauigkeit, Linearität)<br />
<br />
<br />
In der Regel werden verschieden hohe Spannungen analysiert und je nach Auflösung des Wandlers ein entsprechender Wert als Zahl zurückgegeben.<br />
<br />
<br />
;Beispiel der Auflösung eines Wandlers<br />
Liegt der Messbereich zwischen 0V und 5V, bei einer Auflösung von 8 Bit, so ergibt der analoge Wert von 0V am Eingang den digitalen Wert 0. Entsprechend 5V am Eingang den Wert 255. Der kleinste messbare Spannungsunterschied liegt so bei ca. 0,02 Volt. Bei einer Auflösung von 10 Bit verringert sich dieser Wert schon auf etwa 0,005 Volt, also 4 mal kleiner weil 2 Bit mehr Auflösung 4 mal so viele Stufen ergeben.<br />
<br />
;Beispielgeräte mit integriertem A/D-Wandler<br />
* Soundkarten<br />
* ISDN-Telefone<br />
* Digital Multimeter<br />
* Scanner<br />
* Fax-Geräte<br />
<br />
In vielen [[Microcontroller|Microcontrollern]] sind A/D-Wandler enthalten, meistens mit mehreren Eingängen und einer Auflösung von 8 bis 10 Bit. Es gibt aber auch eigenständige Bausteine (ICs), die es mit einer Vielzahl von verschiedenen Schnittstellen gibt, um an das digitale Ergebnis zu kommen, wie u.a. [[I2C]].<br />
<br />
== ADC im µC ==<br />
Viele µC enthalten bereits einen AD-Wandler. Meist ist dies ein Wandler nach dem Funktionisprinzip der ''Sukzessiven Approximation'' [http://de.wikipedia.org/wiki/Analog-digital-Umsetzer#Sukzessive_Approximation] für etwa 8 - 12 Bit. <br />
In den klassischen [[AVR]]s und der PIC16/PIC18 Serie sind es 10 Bit Wandler bis etwa 15 kHz Samplingrate.<br />
Für weitere Details und Programmbeispiele über den ADC der AVRs, siehe [[ADC (Avr)|ADC der AVRs]].<br />
<br />
Für höhere Anforderungen werden externe ADCs etwa über [[SPI]] oder [[I2C]] angeschlossen.<br />
<br />
== Weblinks ==<br />
*[http://de.wikipedia.org/wiki/Analog-digital-Umsetzer Analog-digital-Umsetzer] - hier wird genauer erklärt wie ein AD-Wandler funktioniert, und welche Funktionsprinzipen es gibt.<br />
*[http://www.sprut.de/electronic/referenz/index.htm] - Informationen über Referenzspannungsquellen<br />
<br />
[[Kategorie:Abkürzung]]<br />
[[Kategorie:Grundlagen]]<br />
[[Kategorie:Microcontroller]]<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Sensoren]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&diff=25298Überlegungen zur Drehgeber-Auswertung2014-09-21T17:03:41Z<p>Besserwessi: /* Drehgeber-Auswertung */ Link angepasst</p>
<hr />
<div>=Drehgeber-Auswertung=<br />
Es gibt in RN-Wissen schon einige (sehr gute) Artikel über Drehgeber und Auswertungsvarianten. Denen will ich hier keine Konkurrenz machen.<br/> <br />
[[Sensorarten#Drehgeber Sharp GP1A30 und GP1A38|Technisches]] findet man hier, [[Beispiel_Drehzahlmessung_mit_Drehgeber#Richtung.2C_Geschwindigkeit_und_Position_mit_Doppellichtschranke_ermitteln|Grundsätzliches]] über das Quadratur-Signal da, und [[Drehencoder|Programm-Beispiele]] für Drehencoder gibt es auch<br/><br />
Ich möchte dem einige grundsätzliche Überlegungen zur Drehgeber-Auswertung hinzufügen, damit es einem Einsteiger leichter fällt, eine Auswahl für seine konkreten Anforderungen zu treffen. <br />
<br />
==Signalfolge==<br />
Zur Erinnerung:<br />
[[Bild:quad_0.png|center]]<br />
===Auswertung der Flanken===<br />
Gerade bei den AVR's bietet es sich an, das A-Signal an einen "Externen Interrupt" anzuschließen. <br />
Da man meist auch zwei Drehgeber hat (für links u. rechts), kommt man da wunderbar mit INT0 u. INT1 aus. Allerdings zeigt sich, dass dieser Weg nicht ideal ist.<br />
====Vorwärts====<br />
Bei einer Konfiguration "steigende Flanke" wird bei den dicken Pfeilen der Interrupt ausgelöst. Liest man in der ISR den B-Kanal, kann man daraus direkt die Richtung entnehmen (im Beispiel ist das ebenfalls "1") und auf den Schrittzähler 1 addieren. <br />
[[Bild:quad_isr1.png|center]]<br />
====Richtungsumkehr====<br />
Wird nun die Drehrichtung geändert, wird die bis dahin fallende Flanke von A zur steigenden Flanke, und B ist zu diesem Zeitpunkt dann "0". Die Sache ist eindeutig, man kann nun von Schrittzähler eins abziehen. <br />
[[Bild:quad_isr2.png|center]]<br />
Dabei tritt aber natürlich eine Verschiebung auf. Wenn die Impulsfolge im Verhältnis zum zurückgelegten Weg gross ist, kann man das ignorieren, es geht ja letztlich meistens nur um ein paar Millimeter, und wo genau sich ein Drehgeber zwischen zwei Impulsen befindet, kann man ohnehin nie sagen. <br />
====Möglicher Fehler====<br />
Es gibt aber auch Situationen, wo es zu Fehlern kommt. Hier wird der zweite Interrupt noch ausgelöst und (richtig) als Schritt vorwärts gezählt. Dann bewegt sich der Geber etwas zurück, kommt aber nicht bis zu nächsten steigenden Flanke, sondern bewegt sich wieder nach vorne. <br />
[[Bild:quad_err.png|center]]<br />
Unsere Logik zählt nun einen Impuls zweimal, obwohl der drehgeber sich immer noch am selben Platz befindet.<br />
<br />
=== Auswertung der Übergänge===<br />
(Genaugenommen der Zustände unmittelbar danach)<br/><br />
Die korrekte Auswertung ist möglich, wenn man alle Übergänge steigend/fallend von beiden Kanälen (A u. B) bewertet. Das kann man ebenfalls mit ISR's machen, wenn man den µC so konfigurieren kann, dass er bei beiden Flankenarten auslöst. Da die Auswertung nicht nach der Art des Interrupts unterscheidet, kann dies auch eine gemeinsame ISR sein, etwa der Pin-Change Interrupt beim AVR. <br />
Um auch auf sehr kurze Pulse, wie sie etwa durch Störungen oder eine schnelle hin und her Bewegung entstehen können, darf man sich nicht auf die Auslösung des Interrupts stützen (da kann ggf. eine verloren gehen), sondern muss den Zustand (A und B) jeweils einmal auslesen und mit den alten (aus dem letzten Interrupt) vergleichen. <br />
Im Grunde ist das dann dasselbe, als wenn man ohne ISR periodisch die Eingänge abtastet, was natürlich oft genug geschehen muss, damit nichts verlorengeht. Die Flanken selbst erkennt man an dem Unterschied AB / AB-alt.<br/> <br />
In beiden Fällen erfolgt dann die Auswertung an den strichlinierten senkrechten Linien. <br />
[[Bild:quad_states.png|center]] <br />
====Vorwärts====<br />
Von dem Mitte nach rechts gelesen bekommt also an den bezeichneten Stellen die Werte<br />
* 00 vorher: 01<br />
* 10 vorher: 00<br />
* 11 vorher: 10<br />
* 01 vorher: 11<br />
* usw<br />
====Rückwärts====<br />
Von dem Mitte nach links gelesen bekommt dann an den bezeichneten Stellen die Werte<br />
* 00 vorher: 10<br />
* 01 vorher: 00<br />
* 11 vorher: 01<br />
* 10 vorher: 11<br />
* usw<br />
====Störpulse====<br />
Bei Störpulsen kommt es in kurzer Zeit zu einem Schritt vor und zurück (oder andersherum). Wenn die beiden Schritte zu schnell kommen, wird ggf. nur einmal die Auswertung aufgerufen. Dabei ist dann der alte und neue Wert gleich - so dass kein Schritt erfolgt.<br />
<br />
===Programmtechnische Umsetzung===<br />
====Externer Interrupt oder Polling ?====<br />
Bei der Auswertung hat man die Wahl zwischen einem externen Interrupt, der auf jede Änderung reagiert (z.B. der Pin-Change Interrupt beim neueren AVR wie Mega88) oder dem regelmäßigen Polling des Zustandes, etwa in einer Timer ISR, oder weniger gut ggf. auch in der Schleife im Hauptprogramm. Der eigentliche Code zur Auswertung ist der gleiche - die Frage ist also nur noch wann er aufgerufen wird. <br />
<br />
Beim Polling wird eine häufige Abfrage benötigt, um keine Schritte zu verlieren. Auch wenn sich in den meisten Fällen nichts getan hat, wird dafür Rechenzeit benötigt. Die Belastung für den µC ist damit praktisch unabhängig vom Signal, abhängig von der maximalen erlaubten Geschwindigkeit.<br />
<br />
Bei der Interrupt-Lösung, erfolgt die Auswertung nur wenn wirklich ein Schritt erfolgt oder halt gerade eine Störung kommt. Hat man nur selten Störungen ist dies ein echter Vorteil, weil das Hauptprogramm nur selten unterbrochen wird. Bei dem seltenen Extremfall von starken Störungen, kann es aber passieren das die ISR sehr häufig aufgerufen wird und kaum noch Rechenzeit für da Hauptprogramm übrig bleibt. Wenn solche extremen Störungen möglich sind (etwa optische Geber ohne Hysterese), sind externe Interrupts generell nicht zu empfehlen. Bei schneller Bewegung und entsprechend vielen ISR-Aufrufen kann die Geschwindigkeit des Hauptprogramms geringer werden. Wartezeiten durch Warteschleifen sind dann nicht mehr zuverlässig. Oft wird aber nur ein sehr kleiner Anteil der Rechenleistung benötigt.<br />
<br />
Sofern man kleine Fehler tolerieren kann, etwa für eine Menüsteuerung oder Geschwindigkeitsmessung ohne Richtungswechsel, kann man natürlich auch die Auswertung über einen externen Interrupt wählen, der nur eine Flanke auswertet. Damit reduziert sich ggf. die Rechenzeit auf Kosten geringerer Auflösung und möglicher Schrittfehler.<br />
<br />
====Auswertung der Signale====<br />
Auch wenn bei der Benutzung von Interrupts schon eine Information da ist, welcher Kanal sich geändert hat (und ggf. auch in welche Richtung), sollte die Auswertung trotzdem wie beim Polling erfolgen: Die beiden Kanäle einmal Auslesen, und dann zusammen mit dem vorherigen Wert bestimmen ob es einen Schritt gab und in welche Richtung. So kann verhindert werden, dass durch zu schnelle Interrupts, z.B. durch kurzzeitige Störungen Schritte verloren gehen und Fehler entstehen. <br />
Sofern von Hand nötig, muss das Interruptflag vor dem Auslesen des Ports zurückgesetzt werden, so dass alle Änderungen nach dem Auslesen erfasst werden.<br />
<br />
* Bei der Auswertung nur einer Flanke (also z.B. steigende Flanke von Kanal A) ist die Sache einfach: Es erfolgt ein Schritt, die Richtung zeigt der andere Kanal. Allerdings lässt sich mit nur einer Flanke der oben beschrieben Fehler beim Pendeln um den Übergang nicht vermeiden, weil ein Schritt vor bzw. zurück nicht an der gleichen Stelle erkannt werden. <br />
* mit beiden Flanken eines Kanals ist die Sache auch nicht viel komplizierter und man kann Schrittfehler vermeiden: <br />
** A = B --> vorwärts<br />
** A <> B --> rückwärts<br />
* Bei den Übergängen des anderen Kanals dreht sich die Richtung um und die Auflösung wird verdoppelt. <br />
<br />
Die Auswertung der Übergänge kann man natürlich auf viele Arten machen. Je nach Sprache bietet sich <br />
* "Select/Case" (Bascom) oder "switch/case" (GCC) an. Als Beispiel das [[Bascom_Inside-Code#.28Quadratur-.29_ENCODER|Assembler-listing]] für die Bascom-ENCODER()-Funktion<br/><br />
* IF THEN ELSE oder in C auch die bedingte Berechnung mit dem ? Operator an.<br />
* Eine andere Möglichkeit ist es, die neuen und die vorhergegangenen AB Werte zusammenzufassen und als Index für eine Werte- oder Sprungtabelle mit 16 Werten zu verwenden. In einer solche Tabelle muss man allerdings auch die ungültigen AB Kombinationen belegen, damit das funktioniert.<br />
<br />
==Autoren==<br />
* [[Benutzer:PicNick|PicNick]]<br />
<br />
==Siehe auch==<br />
* [[Sensoren]]<br />
* [[Sensorarten]]<br />
<br />
[[Category:Robotikeinstieg]]<br />
[[Category:Grundlagen]]<br />
[[Category:Sensoren]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Interrupt&diff=25297Interrupt2014-09-21T16:08:28Z<p>Besserwessi: /* Anwendungen für Interrupts */</p>
<hr />
<div>===Interrupt Request===<br />
Normalerweise arbeitet ein Prozessor (µC oder CPU) ein Programm der Reihe nach ab. Praktisch alle CPUs und µCs haben aber die Möglichkeit unabhängig vom normalen Programmfluss auf einige Ereignisse zu reagieren, die Interrupts. Typische Interruptquellen sind der Überlauf eines Timers, das Ende einer AD Wandlung und ein empfangenes Zeichen an einer Schnittstelle.<br />
<br />
Wenn ein Interrupt Signal vorliegt, pausiert das normale Programm, und die ISR ([[Interrupt Service Routine]]) wird statt dessen aufgerufen und erst danach wieder das normale Programm fortgesetzt. Mit den Interrupts kann die CPU mehrere Dinge quasi gleichzeitig erledigen. In der ISR (je nach CPU auch direkt durch die Hardware), wird der Zustand der Register gesichert und am Ende der ISR wieder hergestellt. Das Hauptprogramm bemerkt so wenig von der ISR, außer der unvermeidlichen Verzögerung und den gewünschten Effekten in der ISR. <br />
<br />
Weil die ISR eben doch manchmal stört, erlauben es fast alle CPUs, dass das Programm den Aufruf der ISR durch ein Interruptflag unterbinden kann. Der Interrupt wird dann "maskiert". In der Regel wird das Signal, das den Interrupt ausgelöst hat, gespeichert und die ISR wird nachgeholt werden, wenn das Programm es wieder zulässt. Für wichtige Signale (z.B. Übertemperatur, Unterspannung), die nicht unterdrückt werden sollen gibt es teilweise nicht maskierbare Interrupts, die sich nicht so einfach abschalten lassen.<br />
<br />
Bei Mikrocontrollern sind viele der Interruptquellen interne Module wie [[Timer]], [[UART]], [[I2C]] oder A/D. Je nach Prozessor gibt es je nach Interruptquelle eine eigene ISR (z.B. AVR) oder nur eine (z.B. PIC16) bzw. einige wenige. <br />
<br />
Da die Anzahl der Interrupt-Leitungen bei Mikroprozessoren begrenzt ist, werden teils Interrupt-Controller vorgeschaltet (z.B. im PC). Die eigentliche Interruptquelle wird dann später in der ISR ermittelt.<br />
<br />
=== Anwendungen für Interrupts ===<br />
<br />
Die klassische Anwendung für Interrupts ist die Reaktion auf IO-Ereignisse. Die ISR zur UART kann z.B. die Daten von der RS232 Schnittstelle in einen Puffer speichern, oder aus einem Puffer senden. <br />
<br />
Für Vorgänge die in regelmäßigen Zeitabständen erledigt werden sollen, eignen sich Timer Interrupts. Der Timer kann z.B. alle 10 ms einen Interrupt auslösen in dem dann z.B. die interne Uhrzeit aktualisiert wird oder Tasten abgefragt werden.<br />
<br />
Preemptives Multitasking wird auch per Interrupt realisiert: der Wechsel zu einem anderen Thread / Task beginnt mit einem Interrupt. Nach der ISR wird dann aber nicht wieder an die gleiche Stelle zurückgesprungen, sondern auf einen anderen Task gewechselt. Gerade beim µC ist die Benutzung von Interrupts eine Alternative zum echte Multitasking mit gleichberechtigten Tasks - es können mehrere Aufgaben quasi parallel erledigt werden, wobei das Hauptprogramm allerdings eine Sonderrolle hat.<br />
<br />
Wenn die CPU in einen [[Stromspar-Modi(AVR)|Stromsparmodus]] versetzt wird, dient zu Aufwachen in der Regel auch ein Interrupt. Dadurch kann man z.B. in der Zeit, die man auf eine AD Wandlung wartet, Strom sparen.<br />
<br />
===Unterschiede zum Polling===<br />
<br />
Alternativ zur Verwendung von Interrupts lässt sich das Auftreten eines externes Ereignisses auch durch das regelmäßige Abfragen des externen Gerätes erkennen. Diese Technik nennt sich Polling (der Prozessor ''frägt'' das externe Gerät).<br />
<br />
'''Vorteile des Pollings gegenüber der Verwendung von Interrupts:'''<br />
* Es ist einfacher zu implementieren, vor allem für Anfänger, da die Abfrage des Geräts im Hauptprogramm erfolgen kann.<br />
* Es reagiert meist schneller auf das externe Ereignis, sofern nur auf ein Ereigniss gewartet wird. Bei Interrupts wird der aktuelle Befehl des Hauptprogramms noch fertig ausgeführt, dann wird die Rücksprungadresse gesichert und in die [[ISR]] verzweigt. Diese wiederrum muss die zu benutzenden Register auf dem Stack sichern, bevor sie mit der eigentlichen Reaktion auf das externe Ereignis beginnen kann. Beim Polling kann nach Erkennung des Ereignisses sofort reagiert werden.<br />
* Es ist weniger Hardwareaufwand bei den Geräten notwendig, da diese nicht in der Lage sein müssen, auf das Auftreten eines Ereignisses mit einer Interrupt-Anfrage zu reagieren. So braucht z.B. ein Temperatursensor für Polling im einfachsten Fall nur einen [[ADC|A/D-Wandler]]. Bei der Verwendung von Interrupts muss er jedoch neben der Möglichkeit, die Temperatur zu messen, zusätzlich auf die Änderung der Temperatur mit einer Interruptanfrage reagieren können.<br />
* Auch Eingänge ohne Interruptfunktion können genutzt werden.<br />
<br />
'''Vorteile von Interrupts gegenüber Polling:'''<br />
* Das Hauptprogramm wird u.U deutlich einfacher und besser verständlich, weil es sich auf das Wesentliche konzentriert, ohne ständig oder von unterschiedlichen Codestellen aus Ereignisse abzufragen zu müssen.<br />
* Es können leicht mehrere mögliche Signale Überwacht werden.<br />
* Das Auftreten des externen Ereignisses wird immer überwacht. Beim Polling geschieht dies nur zu den Zeiten, zu denen das Hauptprogramm danach frägt.<br />
* Das Hauptprogramm kann andere Aufgaben übernehmen als das externe Gerät zu überwachen. Beim Polling wird häufig sämtliche Rechenleistung für die Abfrage der Geräte aufgewendet.<br />
* Die Kommunikationsleitungen, Datenbusse und externen Geräte werden entlastet. Im Gegensatz zum Polling wird nur mit dem Gerät kommuniziert, wenn tatsächlich ein externes Ereigniss stattfindet.<br />
* Es ist oft stromsparender. Hat das Hauptprogramm keine Aufgaben mehr zu erledigen, so kann es Teile der CPU bis zum nächsten Auftreten eines Interrupts in einen stromsparenden Sleep-Mode versetzen. In diesem Modus wird die CPU angehalten und beim nächsten Interrupt wieder aktiviert. Fast alle modernen, interruptfähigen Prozessoren unterstützen einen solchen Modus.<br />
<br />
<div style="border:2px solid #ffd700; margin-left:auto; margin-right:auto; padding:0.3em; text-align:left; "><br />
In den allermeisten Fällen überwiegen die Vorteile der Verwendung von Interrupts deutlich!<br />
</div><br />
<br />
===Interrupt-Sharing===<br />
Stehen auf dem Controller weniger Interruptleitungen zur Verfügung, als interruptfähige Geräte angeschlossen werden sollen, so ist es notwendig, dass sich mehrere Geräte eine Interruptleitung teilen (Interrupt-Sharing). Bei einfachen Controllern kann es sogar vorkommen, dass nur eine einzige [[ISR]] definiert werden kann, die bei '''jeder Art''' von Ereignis aufgerufen wird.<br />
In diesem Fall muss die durch den Interrupt aufgerufene [[ISR|Interrupt Service Routine]] prüfen, bei welchem Gerät das Ereignis aufgetreten ist. Das geschieht wiederrum durch Abfrage jedes in Frage kommenden Geräts (also Polling).<br />
<br />
===Interrupt-Prioritäten===<br />
Im einfachsten Fall sind während der Ausführung der ISR weitere Interrupts unterdrückt.<br />
Bei einigen Prozessoren gibt es Interrupts mit verschiedener Priorität. Dann kann ein Interrupt eine ISR niedriegerer Priorität unterbrechen. <br />
Davon zu unterscheiden sind Prioritäten bei der Signalisierung der Interrupts: Wenn mehrere Interrupts auf die Ausführung warten (z.B. weil Interrupts vorher gesperrt waren), wird darüber festgelegt welche ISR zuerst ausgeführt wird, sofern es mehr als eine ISR gibt.<br />
<br />
==Siehe auch==<br />
* [[ISR]] <br />
<br />
[[Kategorie:Microcontroller]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Avr&diff=25296Avr2014-09-21T15:43:49Z<p>Besserwessi: /* Hardware */</p>
<hr />
<div>[[Bild:AtmelController.jpg|thumb|Beispiel eines AVR Controllers]]<br />
'''AVR''' ist eine 8-Bit [[Microcontroller]]-Familie mit RISC-Architektur.<br />
Im Gegensatz zu vielen anderen Microcontroller-Architekturen hat die AVR-Architektur keine Vorgänger. Sie ist ein komplettes Neudesign, das Anfang der 90-Jahre an der Universität von Trondheim/Norwegen entwickelt und vom (bis heute einzigen) Hersteller [[Atmel]] aufgekauft wurde. Es gibt eine ganze Serie von AVR-Controllern. Sie alle werden ähnlich programmiert, haben vergleichbaren Befehlssatz und physikalische Eigenschaften, bieten jedoch unterschiedliche Features und Peripherie. <br />
<br />
Es gibt zahlreiche und kostenlose Entwicklungssysteme in den Sprachen Basic, C/C++, Pascal und Assembler für diese Controller-Familie. <br />
<br />
== Wofür steht AVR? ==<br />
"AVR" steht angeblich für ''Advanced Virtual RISC'' (in einem Paper der Entwickler des AVR-Kerns Alf Egin Bogen und Vegard Wollan). Laut [[Atmel]] bedeutet es nichts.<br />
<br />
== Hardware ==<br />
AVR-Controller besitzen eine zweistufige Pipeline (fetch and execute), die es ermöglicht, die meisten Befehle innerhalb eines einzigen Prozessortaktes auszuführen. Dadurch ist ein AVR wesentlich schneller als etwa 8051-Controller, bei denen der Prozessortakt intern noch durch 12 geteilt wird.<br />
<br />
*AVR-Kern<br />
** Harvard-Architektur (getrennter Befehls- und Datenspeicher) <br />
** 8-Bit Architektur ist für Hochsprachen (C) optimiert <br />
** 32 Register, davon 6 als 3 Pointerregister, kein Akkumulator<br />
** Lineares Speichermodell (keine Segmentierung bis 128 kBytes Programmspeicher)<br />
* In-System programmierbar: die Controller können sehr einfach über ein Programmierkabel (oft ISP-Kabel genannt), das mit dem PC verbunden wird, programmiert werden &ndash; auch dann, wenn sie sich nicht in einer Schaltung befindet.<br />
* integrierter Flash-Speicher für Programm <br />
* umfangreiche Peripherie<br />
** [[Watchdog]], [[Bootloader]]-Support, verschiedene [[Stromspar-Modi(AVR)|Stromspar-Modi]], Brownout-Erkennung, Interner Oszillator<br />
** EEPROM-Datenspeicher<br />
** 8- und 16-Bit-Timer/Counter mit [[PWM]], Capture/Compare, externe Betaktung, asynchrone Operation<br />
** Kommunikation: [[UART|USART]], [[SPI]], [[I2C]] ([[TWI]])<br />
** Analog-Comparator, Analog-Digital-Wandler <br />
** unterschiedlichste externe und interne Interrupt-Quellen (UART, SPI, Timer, A/D-Wandler, Analog-Comparator, ...)<br />
** JTAG (Debugerinterface) (Teilweise)<br />
* AVR Typen (AT90 "Classic AVR", ATtiny, ATmega), trotzdem sehr ähnlich, die neue XMega Serie ist vor allem bei der Peripherie etwas anders<br />
* erhältlich in unterschiedlichen Gehäusen, idR Durchsteck und als [[SMD]]<br />
* Viele Entwicklungsboards erhältlich, z.B. das Roboternetzboard [[RN-Control]]<br />
<br />
==Einige Pinbelegungen der populärsten AVR-Controller==<br />
(in etwa nach Leistungsfähigkeit sortiert)<br />
<br />
* [[AT90S2313]]<br />
<br />
[[Bild:at90s2313tiny.png|center]]<br />
<br />
<br />
* [[Atmel Controller Mega8]]<br />
* [[Atmel Controller Mega48 Mega88 Mega168]]<br />
<br />
[[Bild:mega8kompatibel.png|center]]<br />
<br />
* [[Atmel Controller Mega16 und Mega32]]<br />
<br />
[[Bild:Mega1632.gif|center]]<br />
<br />
<br />
* [[Atmel Controller Mega128]] ([[SMD]]-Chip)<br />
<br />
[[Bild:mega128pin.gif|center]]<br />
<br />
<br />
----<br />
<br />
=== Die AVR-Pin-Bezeichnungen und deren Funktion ===<br />
Die meisten Ports sind doppelt belegt und besitzen neben der normalen Port-Funktion noch eine Sonderfunktion. Die verschiedenen Pinbezeichnungen und Sonderfunktionen werden hier beschrieben:<br />
<br />
{| {{Blauetabelle}}<br />
|+ '''Tabelle: Die AVR-Pin-Bezeichnungen und deren Funktion'''<br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| Versorgungs- und Referenzpins, Reset<br />
|-<br />
|'''[[VCC]]''' <br />
| Versorgungsspannung von 2,7 V bis 5,5 V bei den L-Varianten (low power), ansonsten 4,5V bis 5,5 V. Neuere AVR ab 2,7 V und ab 1,8 V in V-Variante.<br />
|-<br />
|'''[[GND]]''' <br />
|Masse <br />
|-<br />
|'''AREF''' <br />
|Referenzspannung für den Analog-Digital-Wandler. Auch die interne Bandgap-Referenzspannung kann über diesen Pin entstört werden (dann KEINE externe Spannung an diesen Pin geben (Kurzschluss)!). <br />
|-<br />
|'''AGND''' <br />
|Analoge Masse für AD Wandler und dazugehörige Ports. Sollte in aller Regel mit GND verbunden werden.<br />
|-<br />
|'''AVCC''' <br />
| <br />
Die Betriebsspannung für den Analog-Digital-Wandler (und einiges mehr) (siehe Beschaltungsskizze). Die Pins AVCC und AGND müssen immer beschaltet werden, selbst wenn man den AD-Wandler und Port A nicht benutzt. <br />
|-<br />
|'''RESET''' <br />
|Rücksetz-Eingang, intern über einen [[Pullup]] mit VCC verbunden. Ein LOW–Pegel an diesem Pin für die Dauer von mindestens zwei Zyklen des Systemtaktes bei aktivem Oszillator setzt den Controller zurück. Rücksetzen der Ports erfolgt unabhängig von einem evtl. anliegenden Systemtakt.<br />
|-<br />
|'''PEN'''<br />
|Programming Enable - Diesen Pin gibt es nur beim Mega128/64 u.ä. Wird dieser Pin beim Power-On Reset nach Masse gezogen, geht der Controller in den [[ISP]] Programmiermodus. Man kann ihn also alternativ zu Reset verwenden. In der Regel verwendet man aber die Reset-Leitung und PEN sollte man direkt mit VCC verbinden.<br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| System-Takt<br />
|-<br />
|'''XTAL1''' <br />
|Eingang des internen Oszillators zur Erzeugung des Systemtaktes bzw. Eingang für ein externes Taktsignal, wenn der interne Oszillator nicht verwendet werden soll bzw. Anschluss von Quarz/Keramik-Resonator/RC-Glied.<br />
|-<br />
|'''XTAL2''' <br />
|Anschluss von Quarz oder Keramik-Resonator oder Ausgang des integrierten Oszillators zur Nutzung als Systemtakt (Je nach Fuse-Einstellungen). <br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| Digitale bidirektionale I/O-Ports<br />
|- <br />
| colspan="2"| Jeder Pin der Ports kann individuell als Eingang oder Ausgang konfiguriert werden. Die I/O-Ports sind maximal 8 Bit breit und verfügen ja nach AVR-Typ über eine unterschiedliche Anzahl von Pins. An jedem als Eingang (Input) geschalteten Pin gibt es zuschaltbare [[Pullup]]-Widerstände, die teilweise auch bei aktivierter Sonderfunkton verfügbar sind.<br />
<br />
Bei eingeschalteten Sonderfunktionen wie UART, SPI, ADC, etc. sind die entsprechenden Pins nicht als "normale" digitale I/O verwendbar, sondern dienen der Sonderfunktion. Die Anzahl der als I/O verwendbaren Pins ist auch abhängig von den Fuse-Einstellungen (Vorsicht beim Umstellen, Handbuch GENAU lesen!).<br />
|-<br />
|'''PA 0 – 7''' || Port A <br />
|-<br />
|'''PB 0 – 7''' || Port B <br />
|-<br />
|'''PC 0 – 7''' || Port C <br />
|-<br />
|'''PD 0 – 7''' || Port D <br />
|-<br />
|'''PE 0 – 7''' || Port E <br />
|-<br />
|'''PF 0 – 7''' || Port F <br />
|-<br />
|'''PG 0 – 7''' || Port G <br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| Externe Interrupts<br />
|- <br />
| colspan="2"| Die PCINT-Interrupts gibt es nur für neuere AVRs wie den [[ATmega88]]. Falls die Anzahl an externen Interrupts nicht ausreicht, kann evtl. auch andere Hardware dafür eingesetzt werden, etwa der Analog-Comparator mit interner Bandgap-Referenz, falls er anderwärtig nicht gebraucht wird.<br />
|-<br />
|'''INT0''' ||Externer Interrupt 0<br />
|-<br />
|'''INT1''' ||Externer Interrupt 1<br />
|-<br />
|'''INT2''' ||Externer Interrupt 2 <br />
|-<br />
|'''PCINTx''' ||Pin-Change Interrupt<br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| [[Timer]] und [[PWM]]<br />
|-<br />
|'''T0''' <br />
|Timer 0: externer Takteingang.<br />
|-<br />
|'''T1''' <br />
|Timer 1: externer Takteingang.<br />
|-<br />
|'''OC0''' <br />
|PWM bzw. Output Compare Ausgang des Timers 0 <br />
|-<br />
|'''OC1A''' <br />
|Ausgang für die Compare-Funktion des integrierten Zeitgeber- / Zählerbausteines <br />
Der erste PWM-Ausgang des Timers1. Er kann zum Regeln der Bot-Motorgeschwindigkeit benutzt werden. <br />
|-<br />
|'''OC1B''' <br />
|Ausgang für die Compare-Funktion des integrierten Zeitgeber- / Zählerbausteines <br />
Der zweite PWM-Ausgang des Timers1. Er kann zum Regeln der Bot-Motorgeschwindigkeit benutzt werden. <br />
|-<br />
|'''ICP1''' <br />
|Eingang für die [[Timer/Counter_(Avr)#Input_Capture|Capture-Funktion]] des integrierten Zeitgebers / Zählerbausteines <br />
<br />
|-<br />
|'''OC2''' <br />
|[[Pwm]] bzw. Output Compare Ausgang des Timers2. Er kann zum Regeln der Bot-Motorgeschwindigkeit benutzt werden. <br />
|-<br />
|'''TOSC1, TOSC2''' <br />
|TOSC1 und TOSC2 sind Eingänge für den asynchronen Modus von Timer2. Sie sind vorgesehen für den Anschluss eines externen Uhrenquarzes ( 32.768 kHz ). Damit lassen sich zum Beispiel genaue Ein-Sekunden-Impulse für eine Uhr generien, sogar wenn der normale Takt im Power-save Modus aus ist.<br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| Analog-Digital-Wandler<br />
|-<br />
|'''ADC0''' bis '''ADC7''' <br />
|Eingänge des AD-Wandlers. Spannungen können hier gemessen werden oder an den Analog-Komparator weiter geleitet werden. <br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| Analog-Komparator<br />
|-<br />
|'''AIN0, AIN1''' <br />
|Die beiden externen Eingänge des Analog-Komparators. <br />
Mit AIN0(+) und AIN1(-) kann man zwei Spannungen miteinander vergleichen. Wenn die Spannung an AIN0 höher als bei AIN1 ist, liefert der Komparator "High", ansonsten ein "Low". Als interne Eingänge des Komparators können die Interne Bandgap-Referenzspannung oder Ausgänge des ADC-Multiplexers dienen.<br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| Serielle Schnittstelle ([[UART|USART]])<br />
|-<br />
|'''RXD''' <br />
|Eingang der Seriellen Schnittstelle (Receive Data), TTL-Pegel <br />
|-<br />
|'''TXD''' <br />
|Ausgang Serielle Schnittstelle (Transmit Data), TTL-Pegel <br />
|-<br />
|'''XCK''' <br />
|Taktsignal der USART im synchronen Mode (z.B. als SPI Master). <br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| [[SPI]]-Schnittstelle<br />
|-<br />
|'''SS''' <br />
|SPI-Interface – wird benötigt, um den µC als aktiven Slave auszuwählen<br />
|-<br />
|'''MOSI''' <br />
|SPI-Interface – Datenausgang (als Master) oder Dateneingang (als Slave), verwendet bei ISP (In-System-Programmierung)<br />
|-<br />
|'''MISO''' <br />
|SPI-Interface – Dateneingang (als Master) oder Datenausgang (als Slave), verwendet bei ISP (In-System-Programmierung)<br />
|-<br />
|'''SCK''' <br />
|SPI-Interface – Bustakt vom Master, verwendet bei ISP (In-System-Programmierung)<br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| [[I2C|I<sup>2</sup>C]]-Schnittstelle ([[TWI]])<br />
|-<br />
|'''SDA''' <br />
|I2C-Schnittstelle (Bus aus 2 Leitungen) Datenleitung <br />
|-<br />
|'''SCL''' <br />
|I2C-Schnittstelle (Bus aus 2 Leitungen) Clockleitung <br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| [[JTAG]]-Interface<br />
|-<br />
|'''TDI''' <br />
|JTAG-Debug Interface - Über dieses Interface kann man den AVR programmieren und debuggen. Die Schnittstelle ist ähnlich wie die SPI Schnittstelle und hat getrennte Dateneingangs- und Datenausgangsleitungen sowie eine Taktleitung. TDI ist die Dateneingangsleitung<br />
|-<br />
|'''TDO''' <br />
|JTAG-Debug Interface - TDO ist die Datenausgangsleitung des JTAG Interface<br />
|-<br />
|'''TMS''' <br />
|JTAG-Debug Interface<br />
|-<br />
|'''TCK''' <br />
|JTAG-Debug Interface <br />
|}<br />
<br />
== Timer/Counter ==<br />
Für Infos zu Timer und Counter siehe Artikel [[Timer/Counter (Avr)]].<br />
<br />
== Analog-Digital-Wandler ==<br />
Für Infos zu Analog-Digital-Wandler siehe Artikel [[ADC (Avr)]].<br />
<br />
== Analog-Komparator ==<br />
Für Infos zu Analog-Komparator siehe Artikel [[Analog Komparator (Avr)]].<br />
<br />
== TWI/I2C ==<br />
Für Details über das Two-wire Serial Interface (kurz [[TWI]]) siehe Artikel [[TWI]].<br />
<br />
== UART/USART ==<br />
Für Details über den UART/USART siehe Artikel [[UART]].<br />
<br />
== SPI - Serial Peripheral Interface ==<br />
Für Details über SPI siehe Artikel [[SPI]].<br />
<br />
Näheres zu SPI beim AVR siehe [[SPI (AVR)]].<br />
<br />
== USI - Universal Serial Interface ==<br />
Für Infos zu USI (Universal Serial Interface) siehe Artikel [[USI (Avr)]].<br />
<br />
== IO-PORTs ==<br />
Die IO-Port dienen dazu direkt digitale Werte auszugeben oder einzulesen. Zu jedem Port (im folgenden X für A,B,C,D,...) gehören 3 Register. Zum einem Port gehören bis zu 8 Pins (PX0,...,PX7).<br />
* DDRX : Datenrichtungsregister (1 = Ausgang, 0 = Eingang)<br />
* PORTX bzw. Portx bei BASCOM : Ausgaberegister. Für Ausgänge wird hier der Ausgabewert bestimmt. Für Eingänge wird hier der Pullup-Widerstand eingeschaltet (1) oder ausgeschaltet (0).<br />
* PINX bzw. Pinx bei BASCOM : Eingangsregister. Lesen gibt den Zustand am Pin (1 = high, 0 = low). Das gilt auch wenn der IO Pin als Ausgang funktioniert. Die Wirkung beim Schreiben in dieses Register hängt vom Typ ab (siehe Datenblatt). Bei älteren Typen passiert beim schreiben nichts. Bei einigen neueren Typen wird beim schreiben einer 1 das entsprechende Bit im Register PORTX umgedreht.<br />
<br />
Von außen gesehen kann der IO Pin also 4 Zustände haben: niederohmig an VCC (high), niederohmig an GND (low), Pullup an VCC und hochohmiger Eingang. Für die Ausgabe nutzt man in der Regel PORTX, für Eingänge immer PINX.<br />
<br />
== Die Fusebits ==<br />
Zur Konfigurierung eines AVR-Controllers werden Fusebits benutzt. Bei der Auslieferung neuer AVR Controller sind die Fusebits bereits vorkonfiguriert, in der Regel auf den internen RC Oszillator und etwa 1 MHz Frequenz. In vielen Fällen kann die Konfiguration unverändert bleiben. Bei den Typen Mega xxx bestimmen einige Fusebits beispielsweise, dass der interne Taktgeber aktiviert ist. Soll z.B. dagegen ein externer Quarz anschlossen oder die Taktfrequenz geändert werden, so müssen auch die Fusebits geändert werden. Auch das Deaktivieren des "[[On Chip Debugging]]" Modus ist oft notwendig, wenn alle Ports genutzt werden sollen. <br />
<br />
Die Fusebits werden in der Regel über die Software eingestellt, welche auch für das Übertragen des Programmcodes zuständig ist. Besonders einfach geht dies beispielsweise mit der Entwicklungsumgebung [[Bascom]]. Aber auch andere Programme wie [[PonyProg]] können für die Umstellung der Fusebits genutzt werden. Einmal eingestellte Fusebits bleiben bis zur erneuten Fusebit-Änderung erhalten. Der normale Programmiermodus verändert die Fusebits nicht. <br />
<br />
Je nach AVR Controllertyp sind unterschiedliche Fusebits (Einstellungen) vorhanden. Die genaue Beschreibung findet man im jeweiligen Datenblatt. Da aber falsch gesetzte Fusebit-Einstellungen zu den häufigsten Problemen gehören (siehe auch unter dieser Tabelle), liste ich hier die Funktion der üblichen Fusebits nochmals genauer auf:<br />
<br />
{| {{Blauetabelle}}<br />
|'''CKSEL0, CKSEL1, CKSEL2, CKSEL3'''<br />
|Die Kombination dieser 4 Fusebits bestimmt die Taktquelle des Controllers. Das kann eine interner Taktgenerator, ein Quarz, Quarzoszillator, RC-Glied und ähnliches sein.<br />
|-<br />
|'''JTAGEN'''<br />
|Hiermit wird die "[[On Chip Debugging]]" Schnittstelle aktiviert bzw. deaktiviert. Das sind die Bits mit den Bezeichnungen TDI, TDO, TMS und TCK. Möchte man diese Pins als normalen Port nutzen, so muss diese Schnittstelle immer deaktiviert werden. Alternativ kann man das JTAG aber auch per Software deaktivieren.<br />
|-<br />
|'''SUT0, SUT1'''<br />
|Die sogenannte StartUp-Zeit (PowerOn delay). Diese Einstellung muss abhängig von der Art des Taktgenerators eingestellt werden, genaueres im jeweiligen Datenblatt. <br />
|-<br />
|'''SPIEN'''<br />
|Hiermit kann die serielle [[AVR-ISP Programmierkabel|ISP-Programmierung]], welche die meisten Programmierkabel nutzen, deaktiviert werden. Dies sollte man lieber vermeiden, denn wenn dieser Programmiermodus deaktiviert wurde, kann nur noch der Parallel-Programmiermodus genutzt werden. Der Parallel-Programmiermodus benötigt jedoch ein spezielles Programmiergerät, das die wenigsten Bastler besitzen. ''Also Vorsicht!''<br />
|-<br />
|'''BODEN'''<br />
|Über dieses Bit wird der '''Brown-out Detector''' aktiviert bzw. deaktiviert. Dies ist eine Überwachung der Betriebsspannung, die dafür sorgt, dass bei zu geringer Spannung der Controller angehalten wird und dann ein ordentlicher RESET durchgeführt wird, wenn die Spannung wieder ausreicht. Dadurch wird verhindert, dass der Controller in einen undefinierten Zustand gerät (hängen bleibt), sich verrechnet oder versehentlich das EEPROM / Flash verändert. In der Regel sollte man daher den Brown-out Detector aktivieren.<br />
|-<br />
|'''BODLEVEL'''<br />
|Über dieses Bit (ggf. auch mehrere) wird festgelegt, ab welcher Spannung der Brown-out Detector anspricht. <br />
|-<br />
|'''BOOTRST'''<br />
|Gewöhnlich startet ein Programm im Controller nach einem RESET ab Adresse 0. Durch dieses Fusebit kann der Controller jedoch veranlasst werden, nach einem Reset einen sogenannten Bootloader-Bereich auszuführen. Ein [[Bootloader]] kann genutzt werden, um Controller über andere Schnittstellen (z.B. RS232) zu programmieren.<br />
|-<br />
|'''BOOTSZ0, BOOTSZ1'''<br />
|Der zuvor genannte Bootloaderbereich kann bei AVR-Controllern verschieden groß sein. Über diese beiden Bits können vier verschiedene Größen eingestellt werden. Siehe unter [[Bootloader]].<br />
|-<br />
|'''EESAVE'''<br />
|Dieses Bit legt fest, ob beim Programmieren des Controllers (man nennt es auch brennen) immer das EEPROM gelöscht werden soll.<br />
|-<br />
|'''CKOPT'''<br />
|Abhängig von den Einstellungen von CKSEL kann hier dir Oszillator-Verstärkung eingestellt werden. Genaueres im Datenblatt des jeweiligen Controllers.<br />
|-<br />
|'''CKDIV8'''<br />
|Bei neueren µCs (aber etwa Mega88) stellt dieses Bit den Teiler für den Takt ein. Default ist dieses Bit aktiv, so dass der Takt des internen Oszillators von 8 MHz auf 1 MHz geteilt wird.<br />
|-<br />
|'''WDTON'''<br />
|Schaltet den WatchDog-Timer beim Booten ein/aus. Dies ist auch per Software möglich<br />
|-<br />
|'''RSTDISBL'''<br />
|Durch dieses Bit kann man den RESET-Pin deaktivieren und dann als normalen I/O-Port nutzen. Aber Vorsicht! Da die RESET-Leitung beim Programmieren (Brennen) des Chips genutzt wird, kann man nach dessen Deaktivierung den Controller mit den üblichen [[AVR-ISP Programmierkabel|ISP-Adaptern]] nicht mehr programmieren. In diesem Fall könnte man zwar den Controlle noch mit speziellen Programmiergeräten im Parallelmodus programmieren, aber in der Praxis verfügen nur wenige Bastler über ein Programmiergerät, das dies leistet.<br />
|-<br />
|'''LB1, LB2'''<br />
|Das sind die sogenannten Lockbits, mit denen sich das Auslesen des Flash- als auch EEPROM-Speichers verhindern läßt. Zwar können andere Anwender immer noch Daten lesen, allerdings handelt es sich dabei nicht mehr um den wirklichen Inhalt sondern lediglich um wirre Datenbytefolgen. Programmierer, die den erarbeiteten Code vor Raubkopierern schützen wollen, nutzen diese Lockbits. Das Programmieren ist auch bei gesetzen Lockbits noch möglich. Der Bootloader-Bereich wird nicht durch die Lockbits geschützt.<br />
|-<br />
|'''BLB01, BLB02'''<br />
|Durch diese Bits kann der Code sogar vor dem Zugriff durch den Bootloader geschützt werden<br />
|-<br />
|'''BLB11, BLB12'''<br />
|Diese Bits schützen den Bootloaderbereich selbst<br />
|}<br />
<br />
Wie man die Fusebits mit [[Bascom]] einstellt, wird im Beitrag [[Bascom - Erstes Programm in den AVR Controller übertragen]] erläutert.<br />
<br />
''Autoren des Artikels: Frank, Luma''<br />
<br />
Fusebits verstellt auf Externer Oszillator gehört zu den sehr häufigen Fehlern insbesondere bei Anfängern. Eine Möglichkeit, diesen Fehler mit einem Minimum an Hardware (minimalistisch gehts mit nur 1 Widerstand an einer EIA232-Schnittstelle) zu reparieren, ist hier vorgestellt: [http://www.roboternetz.de/phpBB2/viewtopic.php?t=51685 Fuse irrtümlich auf extern Takt?].<br />
<br />
== Siehe auch ==<br />
* [[AVR-Einstieg leicht gemacht]]<br />
===Entwicklungsumgebungen===<br />
* [[Microsoft_Visual_Studio_2008_als_AVR_Entwicklungsumgebung| Microsoft Visual Studio]] - Die kostenlose "Express Edition" setzt auf WinAVR und auf den GCC auf, compiliert über custom-build und generiert ein Script für Ponyprog<br />
<br />
* [[Bascom]] - Basic-Entwicklungssystem<br />
* [[Bascom - Erstes Programm in den AVR Controller übertragen]]<br />
* [[Avr-gcc|avr-gcc]] - Leistungsfähiger AVR-Port des freien Compilers GCC<br />
* [[WinAVR]] - Freies, kostenloses Werkzeugpaket mit avr-gcc, binutils, tools ([[make]], [[Programmer's Notepad]], [[avrdude]], etc.) für MS-Windows.<br />
* [[Linuxdistribution_Avr-live-cd]]<br />
* [[AVR_Assembler_Einf%C3%BChrung|AVR Assembler Einführung (AvrStudio)]]<br />
* [http://www.mikroe.com/en/compilers/mikropascal/avr/ MikroPascal for AVR] Sehr gute kommerzielle Pascal Entwicklungsumgebung. Der Compiler ist auch für PIC und andere Controller verfügbar.<br />
<br />
=== Hardware ===<br />
* [[AVR-ISP Programmierkabel]] - Bauanleitung für die AVR Controller Programmierkabel<br />
* [[RN-Control]] - Eines der beliebtestet AVR-Boards im Roboternetz<br />
* [[RNBFRA-Board]] - Größeres Board mit zwei Atmel Controllern<br />
<br />
===Sonstiges===<br />
* [[Atmel]]<br />
* [[HEX Beispiel-Dateien für AVR]]<br />
* [[Bootloader]]<br />
* [[On Chip Debugging]]<br />
<br />
== Weblinks ==<br />
* [http://www.atmel.com/dyn/products/param_table.asp?family_id=607&OrderBy=part_no&Direction=ASC Aktuelle AVR Vergleichstabelle]<br />
* [http://www.atmel.com/dyn/products/devices.asp?family_id=607 Die Datenblätter zu Atmel Controllern]<br />
* [https://mpg.dnsalias.com/~magerlu/rn-wiki/avrtimer_applet Java Applet Timer Berechnung] <br />
* [http://www.roboternetz.de/phpBB2/dload.php?action=file&file_id=169 AvrTimer Windows Berechnungstool (für Bascom, nur nach Anmeldung)]<br />
* [http://people.freenet.de/gjl/helferlein/avr-uart-rechner.html AVR-Baudraten-Rechner (JavaScript)]<br />
* [http://www.engbedded.com/fusecalc/ Berechnung der Fusebits (englisch)]<br />
<br />
[[Kategorie:Microcontroller]]<br />
[[Kategorie:Grundlagen]]<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Abkürzung|AVR]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=AT90S2313&diff=25295AT90S23132014-09-21T15:39:43Z<p>Besserwessi: /* Packages */</p>
<hr />
<div>Der AT90S2313 ist ein alter Atmel µC, der früher gerne zum Einstieg genutzt wurde. Als neueren und etwas verbesserten Ersatztyp gibt es den ATtiny2313 und mit mehr Speicher den ATtiny4313. Die Änderungen sind so gering, dass einige Programme sogar unverändert (d.h. mit dem alten .hex File) laufen. Atmel hat allerdings die Namen einiger Bits und Register geändert, so dass im Sourcecodes ggf. einige Änderungen nötig sind.<br />
<br />
'''Versorgungspannung:''' <br />
::AT90S2313-4: 2.7V - 6V<br />
::AT90S2313-10: 4V - 6V<br />
::ATTiny2313-20: 1.8V - 5.5V<br />
'''Flash-Programm-Speicher:''' 2kByte <br><br />
'''EEPROM-Datenspeicher:''' 128Byte <br><br />
'''SRAM-Datenspeicher:''' 128 Byte <br><br />
'''Taktfrequenz:'''<br />
::AT90S2313-4: bis 4MHz<br />
::AT90S2313-10: bis 10MHz<br />
::ATTiny2313-20: bis 20MHz<br />
'''Features:'''<br />
* Full Duplex UART<br />
* ein 8-bit Timer<br />
* ein 16-bit Timer mit PWM, InputCapture, external Clock<br />
* Analog-Comparator<br />
* 15 digitale I/O-Ports<br />
* programmierbarer Watchdog-Timer<br />
'''weitere Features beim Tiny2313:'''<br />
* 4 PWM Kanäle ( 2 zusätzliche Kanäle bei Timer0)<br />
* 18 digitale I/O-Ports, wenn kein externer Quarz und kein Reset benutzt werden<br />
* [[USI (Avr)|Universelle serielle Schnittstelle (USI)]], damit kann [[TWI]] oder [[SPI]] nachgebildet werden<br />
* Pin Change Interrupts an PortB<br />
* Debug-wire<br />
* Interner RC-Oszillator bis 8 MHz<br />
* erweiterte UART (USART), mit Parity-Bit usw.<br />
<br />
<br />
[[Bild:at90s2313tiny.png|center]]<br />
<br />
<br />
==Siehe auch==<br />
* [[Atmel]]<br />
* [[AVR]]<br />
* [[HEX Beispiel-Dateien für AVR]]<br />
<br />
== Packages==<br />
* PDIP 20<br />
* SOIC 20 (0.3" breit)<br />
* MLF 20<br />
<br />
==Weblinks==<br />
* [http://www.atmel.com/dyn/resources/prod_documents/DOC0839.PDF Datenblatt des AT90S2313 (englisches PDF, 92 Seiten, 1.6 MByte)]<br />
* [http://www.atmel.com/dyn/resources/prod_documents/doc4298.pdf AVR091: Replacing AT90S2313 by ATtiny2313 (englisches PDF, 11 Seiten, 118 kByte)]<br />
* [http://www.atmel.com/dyn/resources/prod_documents/doc2543.pdf Datenblatt des ATTiny2313 (englisches PDF, 231 Seiten, 2MB)]<br />
<br />
<br />
[[Kategorie:Microcontroller]]<br />
[[Kategorie:Grundlagen]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=AT90S2313&diff=25294AT90S23132014-09-21T15:35:47Z<p>Besserwessi: Link auf sich selbst entfernt</p>
<hr />
<div>Der AT90S2313 ist ein alter Atmel µC, der früher gerne zum Einstieg genutzt wurde. Als neueren und etwas verbesserten Ersatztyp gibt es den ATtiny2313 und mit mehr Speicher den ATtiny4313. Die Änderungen sind so gering, dass einige Programme sogar unverändert (d.h. mit dem alten .hex File) laufen. Atmel hat allerdings die Namen einiger Bits und Register geändert, so dass im Sourcecodes ggf. einige Änderungen nötig sind.<br />
<br />
'''Versorgungspannung:''' <br />
::AT90S2313-4: 2.7V - 6V<br />
::AT90S2313-10: 4V - 6V<br />
::ATTiny2313-20: 1.8V - 5.5V<br />
'''Flash-Programm-Speicher:''' 2kByte <br><br />
'''EEPROM-Datenspeicher:''' 128Byte <br><br />
'''SRAM-Datenspeicher:''' 128 Byte <br><br />
'''Taktfrequenz:'''<br />
::AT90S2313-4: bis 4MHz<br />
::AT90S2313-10: bis 10MHz<br />
::ATTiny2313-20: bis 20MHz<br />
'''Features:'''<br />
* Full Duplex UART<br />
* ein 8-bit Timer<br />
* ein 16-bit Timer mit PWM, InputCapture, external Clock<br />
* Analog-Comparator<br />
* 15 digitale I/O-Ports<br />
* programmierbarer Watchdog-Timer<br />
'''weitere Features beim Tiny2313:'''<br />
* 4 PWM Kanäle ( 2 zusätzliche Kanäle bei Timer0)<br />
* 18 digitale I/O-Ports, wenn kein externer Quarz und kein Reset benutzt werden<br />
* [[USI (Avr)|Universelle serielle Schnittstelle (USI)]], damit kann [[TWI]] oder [[SPI]] nachgebildet werden<br />
* Pin Change Interrupts an PortB<br />
* Debug-wire<br />
* Interner RC-Oszillator bis 8 MHz<br />
* erweiterte UART (USART), mit Parity-Bit usw.<br />
<br />
<br />
[[Bild:at90s2313tiny.png|center]]<br />
<br />
<br />
==Siehe auch==<br />
* [[Atmel]]<br />
* [[AVR]]<br />
* [[HEX Beispiel-Dateien für AVR]]<br />
<br />
== Packages==<br />
* PDIP 20<br />
* SOIC 20 <br />
<br />
==Weblinks==<br />
* [http://www.atmel.com/dyn/resources/prod_documents/DOC0839.PDF Datenblatt des AT90S2313 (englisches PDF, 92 Seiten, 1.6 MByte)]<br />
* [http://www.atmel.com/dyn/resources/prod_documents/doc4298.pdf AVR091: Replacing AT90S2313 by ATtiny2313 (englisches PDF, 11 Seiten, 118 kByte)]<br />
* [http://www.atmel.com/dyn/resources/prod_documents/doc2543.pdf Datenblatt des ATTiny2313 (englisches PDF, 231 Seiten, 2MB)]<br />
<br />
<br />
[[Kategorie:Microcontroller]]<br />
[[Kategorie:Grundlagen]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=ADC&diff=25293ADC2014-09-20T17:02:38Z<p>Besserwessi: /* ADC des AVR */</p>
<hr />
<div>Abkürzung des englischen Begriffs '''Analog to Digital Converter''', im deutschen Sprachgebrauch '''A/D-Wandler''' (Analog nach Digital Wandler) oder '''A/D-Umsetzer''' genannt.<br />
__NOTOC__<br />
== Allgemeines ==<br />
<br />
Da es in der digitalen Welt nur zwei Zustände gibt, um Informationen darzustellen, wird ein A/D-Wandler benötigt, um analoge Größen in digitale Werte umzuwandeln. Er ist somit das Gegenteil zum [[DAC]] (Digital-Analog-Wandler), der digitale Werte in analoge Größen wandelt.<br />
<br />
;Merkmale eines A/D-Wandlers<br />
* Auflösung in Bit<br />
* Geschwindigkeit der Umwandlung<br />
* Spannungsbereich bzw. Referenzspannung<br />
* Genauigkeit (Drift, Wiederhohlgenauigkeit, Linearität)<br />
<br />
<br />
In der Regel werden verschieden hohe Spannungen analysiert und je nach Auflösung des Wandlers ein entsprechender Wert als Zahl zurückgegeben.<br />
<br />
<br />
;Beispiel der Auflösung eines Wandlers<br />
Liegt der Messbereich zwischen 0V und 5V, bei einer Auflösung von 8 Bit und einer Referenzspannung von ebenfalls 5V, so ergibt der analoge Wert von 0V am Eingang den digitalen Wert 0. Entsprechend 5V am Eingang den Wert 255. Der kleinste messbare Spannungsunterschied liegt so bei ca. 0,02 Volt. Bei einer Auflösung von 10 Bit verringert sich dieser Wert schon auf etwa 0,005 Volt, also 4 mal kleiner weil 2 Bit mehr Auflösung.<br />
<br />
<br />
;Beispielgeräte mit integriertem A/D-Wandler<br />
* Soundkarten<br />
* ISDN-Telefone<br />
* Digital Multimeter<br />
* Scanner<br />
* Fax-Geräte<br />
* TV- und Videokarten<br />
<br />
In vielen [[Microcontroller|Microcontrollern]] sind A/D-Wandler enthalten, meistens mit mehreren Eingängen und einer Auflösung von 8 bis 10 Bit. Es gibt aber auch eigenständige Bausteine (ICs), die es mit einer Vielzahl von verschiedenen Schnittstellen gibt, um an das digitale Ergebnis zu kommen, wie u.a. [[I2C]].<br />
<br />
== ADC im µC ==<br />
Viele µC enthalten bereits einen AD-Wandler. Meist ist dies ein Wandler nach dem Funktionisprinzip der ''Sukzessiven Approximation'' [http://de.wikipedia.org/wiki/Analog-digital-Umsetzer#Sukzessive_Approximation] für etwa 8 - 12 Bit. <br />
In den klassischen [[AVR]]s und der PIC16/PIC18 Serie sind es 10 Bit Wandler bis etwa 15 kHz Samplingrate.<br />
Für weitere Details und Programmbeispiele über den ADC der AVRs, siehe [[ADC (Avr)|ADC der AVRs]].<br />
<br />
Für höhere Anforderungen werden externe ADCs etwa über [[SPI]] oder [[I2C]] angeschlossen.<br />
<br />
== Weblinks ==<br />
*[http://de.wikipedia.org/wiki/Analog-digital-Umsetzer Analog-digital-Umsetzer] - hier wird genauer erklärt wie ein AD-Wandler funktioniert, und welche Funktionsprinzipen es gibt.<br />
*[http://www.sprut.de/electronic/referenz/index.htm] - Informationen über Referenzspannungsquellen<br />
<br />
[[Kategorie:Abkürzung]]<br />
[[Kategorie:Grundlagen]]<br />
[[Kategorie:Microcontroller]]<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Sensoren]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=PTC/NTC&diff=25292PTC/NTC2014-09-20T16:51:27Z<p>Besserwessi: /* Mehr Auflösung */</p>
<hr />
<div>[[Bild:Schaltzeichen_ntcptc.PNG|thumb|Schaltzeichen eines NTC (oben) und eines PTC (unten)]]<br />
Heißleiter (engl. '''NTC''', ''negative temperature coefficient'') und Kaltleiter (engl. '''PTC''', ''positive temperature coefficient'') sind elektrische Widerstände aus bestimmten Materialien, deren Leitfähigkeit (und damit der Widerstand) sich mit der Temperatur deutlich ändert. <br />
<br />
<br />
==NTCs==<br />
[[Bild:ntc.gif|thumb|Typische Bauform]]Heißleiter sind stromleitende Materialien, die bei hohen Temperaturen eine höhere Leitfähigkeit besitzen als bei tiefen Temperaturen. Sie haben einen negativen Temperaturkoeffizienten; das heißt, mit steigender Temperatur sinkt ihr elektrischer Widerstand.<br />
<br />
Ein großer Vorteil ist die starke Temperaturabhängigkeit und der geringe Preis. <br />
Das nichtlineare Verhalten des Widerstandswertes bei sich verändernder Temperatur ist jedoch zugleich auch der größte Nachteil.<br />
<br />
Ein einzelner, nicht kompensierter NTC ändert seinen Widerstand nicht linear. Hier dazu ein Beispieldiagramm:<br />
<br />
<br />
[[Bild:ntcdiagramm.gif]]<br />
<br />
<br />
<br />
===Temperaturabhängigkeit===<br />
<br />
Mithilfe eines NTCs lässt sich ein einfacher Temperatursensor bauen.<br />
Bildet der Heißleiter einen Spannungsteiler mit einem konstanten Widerstand, so lässt sich die geteilte Spannung mithilfe eines Microcontrollers messen, wodurch auf den Widerstand des NTCs geschlossen werden kann.<br /><br />
Der Heißleiter ändert seinen elektrischen Widerstand nicht linear. Jedoch kann die Temperatur auch rechnerisch ermittelt werden. Es gilt:<br/><br />
<pre><br />
T ~ 1/ln(R)<br />
</pre><br />
Bei Einbeziehung der Aktivierungsenergie, der Nenntemperatur (meist 25°C), und des Nennwiderstandes bei Nenntemperatur<br />
gilt folgende Funktion:<br /><br />
<pre><br />
T(R) = T_N * B / (B + T_N * ln(R / R_N))<br />
</pre><br />
Wobei '''T''' die momentane Temperatur, '''T_N''' die Nenntemperatur (in Kelvin), '''R''' der Widerstand des Heißleiters und '''R_N''' den Nennwiderstand darstellt. Bei '''B''' handelt es sich um eine Materialkonstante, die vom Herrsteller im Datenblatt angegeben wird. Bei Implementierung der Funktion in ein Programm für den Microcontroller müssen umbedingt die Datentypen berücksichtigt werden!<br />
<br />
{{Ausbauwunsch|Erbitte Implementierung der Gleichungen (Unbekannter Parserfehler)}}<br />
<br />
==PTCs==<br />
Kaltleiter sind stromleitende Materialien, die bei hohen Temperaturen eine niedrigere Leitfähigkeit besitzen als bei tiefen Temperaturen. Sie haben einen positiven Temperaturkoeffizienten; das heißt, mit steigender Temperatur steigt auch ihr elektrischer Widerstand.<br />
<br />
Bei den PTCs gibt es zwei Sorten: Einmal welche mit näherungsweise linearer Kennlinie und dann welche mit stark nichtlinearer Kennlinie. Die linearen Typen (z.B. KTYxx Reihe) oder Platinwiderstände (z.B. PT100, PT1000 ) sind mehr für die Temperaturmessung gedacht. <br />
Recht beliebt sind die PTCs der KTYxx-Reihe z.B. der KTY81-110. Sie sind sehr günstig (ca. 0,50€) und bieten für die meisten Anwendungen eine ausreichende Genauigkeit. <br />
<br />
Für höhere Anforderungen an den Messbereich (je nach Typ von -260 ... 850 °C) oder die Genauigkeit gibt es Platin-Widerstände zur Temperaturmessung. Auch hier ist der Temperaturkoeffizient positiv (0,38 %/K bei Raumtemperatur). Am häufigsten findet man hier Widerstände von 100 Ohm (PT100) und 1000 Ohm (PT1000). Diese sind jedoch relativ teurer (je nach Typ und Bezugsquelle und Qualität ab etwa 3 €).<br />
<br />
Die stark nichtlinearen Typen sind mehr als Übertemperaturschutz oder als selbst rückstellende Sicherung geeignet.<br />
Auch dazu ein Beispieldiagramm für einen nicht linearen PTC:<br />
<br />
<br />
[[Bild:Ptcdiagramm.GIF]]<br />
<br />
==Einatzmöglichkeiten==<br />
Mit NTCs und PTCs lassen sich in "normalen" Bereichen (das heißt hier kleiner etwa 200 °C) günstig Temperaturen messen. Dieses Gebiet umfasst alles vom kleinen digitalen Fieberthermometer über die verschiedenen Anzeigeinstrumente (wie Wetterstationen und KFZ-Elektronik) bis hin zu kompletten Heizungs- und Klimasteuerungen in Gebäuden.<br />
<br />
Daneben gibt es auch Anwendungen bei denen es nicht um eine eigentliche Messung geht: spezielle NTCs werden z.B. bei Netzteilen als Einschaltstrombegrenzer genutzt. Hier wird die Eigenerwärmung als gewollter Effekt mit genutzt.<br />
<br />
==Temperaturmessung mit PTC==<br />
Um aus der Widerstandsänderung am Sensor eine passende Spannung zu erzeugen gibt es verschieden Möglichkeiten. Ein naheliegender Weg ist eine Konstantstromquelle, so dass die Spannung am Sensor direkt proportional zum Widerstand ist. Wenn man das Signal danach digitalisieren will, hat dieser Weg aber zwei wesentliche Nachteile: Eine gute Konstantstromquelle ist aufwendig, und man hat dann 2 Referenzen im System und damit unnötige Fehlerquellen. Für eine Widerstandsmessung ist ein Widerstand als Vergleichswert besser und einfacher als das Verhältnis von einer Spannungsquelle und einer Stromquelle. Dafür gibt es im wesentlichen 3 Möglichkeiten:<br />
<br />
1) Der selbe Strom fließt durch den Sensor und den Vergleichswiderstand. Die Spannung an Vergleichswiderstand wird als Referenz für den AD Wandler benutzt, die Spannung am Sensor wird gemessen. Dieses Verfahren ist vor allem mit hochauflösenden AD Wandlern mit Differenzeingängen (z.B. LTC2440) sinnvoll. <br />
<br />
2) Der selbe AD Wandler misst nacheinander die Spannung am Sensor und Vergleichswiderstand. Dafür braucht man 2 Eingänge mit Differenzeingang. <br />
<br />
3) Der Sensor und der Vergleichswiderstand bilden einen Spannungsteiler. Die Spannung über den Spannungsteiler dient als Referenz für den AD-wandler. Anders als bei den beiden vorherigen Möglichkeiten ist das Ergebnis der AD Wandlung hier nicht mehr linear vom Widerstand abhängig.<br />
Bei einem kleinen Messbereich und geringer Auflösung des AD's lohnt es sich ggf. den konstanten Teil der Spannung abzuziehen, damit der Wertebereich des AD Wandler weitgehend ausgenutzt werden kann. Die Schaltung ist dann eine Brückenschaltung. <br />
<br />
In allen 3 Fällen muss der Strom nicht besonders stabilisiert sein und es wird keine stabile Referenzspannung für den AD benötigt. Es kommt nur auf das Spannungsverhältnis an. <br />
<br />
Beim Strom durch den Sensor muss ein Kompromiss zwischen genügend Spannung und der Eigenerwärmung des Sensors gefunden werden. Bei mehr als etwa 1 mW Verlustleistung am Sensor wird eine genaue Temperaturmessung schwierig. Für genaue Messungen mit dem PT100 werden oft sogar 0,1 mW als Grenze angegeben, d.h der Strom liegt bei nur 1 mA.<br />
<br />
==Linearisierung==<br />
[[Bild:LinearPTC.GIF]]<br />
KTY81 mit konstanten Strom (1,4 mA), und mit Widerstand in Reihe.<br />
<br />
Um trotz der nicht linearen Kennlinie des Sensors ein linear von der Messgröße (Temperatur) abhängiges Ergebnis (Spannung oder Digitaler Wert) zu erhalten, bieten sich verschiedene Methoden an:<br />
<br />
* Man nutzt die Nichtlineare Kennlinie von Dioden. Wegen der Temperaturabhängigkeit der Diodenkennlinie ist das aber schwierig und nur für sehr starke Nichtlinearitäten gerechtfertigt. Für die schwache Nichtlinearität der PTCs ist das Verfahren eher ungeeignet.<br />
<br />
* Man nutzt den nichtlinearen Zusammenhang zwischen Spannung und Widerstand beim Spannungsteiler, bzw. in einer Brückenschaltung. Bei den Sensoren des Typs KTY81 wird mit einem 2,7kOhm-Widerstand in Reihe gerade eine relativ gute Kompensation der Nichtlinearitäten erreicht. Im Bereich von -40 °C ... +140 °C erscheint der PTC nun nahezu linear, der verbleibende Linearitätsfehler liegt bei etwa ±10 mV (für 5 V am Spannungsteiler). So einfach geht die Linearisierung allerdings nicht bei allen Sensoren.<br />
<br />
* Man überlässt die Linearisierung den mathematischen Fähigkeiten eines Prozessors, z.B. einem ATMega-x, an dessen ADC der PTC angeschlossen ist. Das Verfahren ist sehr universell, benötigt aber einiges an Programmcode.<br />
<br />
==Schaltungsbeispiele==<br />
<br />
=== Brückenschaltung ===<br />
[[Bild:PT1000-Brücke.png]]<br />
<br />
Eine einfache, aber dennoch gute Auswerteschaltung ist eine Brückenschaltung. Wegen der relativ kleinen Spannungsänderungen wird oft eine Verstärkung benötigt, vor allem beim <b>Pt100</b> oder — Strom sparender — <b>Pt1000</b>.<br />
Bei den gezeigten Widerstandswerten reicht der Messbereich von etwa -25 °C bis +250 °C für 0 bis V+ (z.B. 5 V) am Ausgang. Über die Widerstände R2 und R3 kann der Bereich angepasst werden. Mit R3 = 20 kΩ hätte man z.B. einen Messbereich von etwa -10 °C bis + 130 °C. Die Linearisierung erfolgt in der Regel digital hinter dem A/D-Wandler. Die Referenzspannung des A/D-Wandlers sollte V+ sein, für eine ratiometrische Messung. Wenn nur eine Versorgung (z.B. 5 V) zur Verfügung stehen, sollte V+ an den Ausgangsbereich des OPs angepasst sein, also etwa 3 V für den LM358 (oder den besseren LT1013), oder der Operationsverstärker eine Rail-to-Rail-Typ (z.B. MCP6001) sein, wenn der AD mit der Versorgungsspannung als Ref. Arbeitet.<br />
<br />
{{Ausbauwunsch|... auf jeden Fall Anwendungs- und Schaltungsbeispiele mit Heiß- oder Kaltleiter!'''}}<br />
<br />
=== Spannungsteiler ===<br />
<br />
Für <b>NTC</b> (Heißleiter) bietet sich ein einfacher Spannungsteiler an. Den Vorwiderstand dimensioniert man so, dass er etwa so groß ist wie der Widerstandswert <i>in der Mitte des gewünschten Temperatur-Messbereiches</i>. Die A/D-Wandlung erfolgt „ratiometrisch“ mit dem vollen Speisespannungsbereich. Dann hat man — nach der Linearisierung in Software — etwas oberhalb der Mitte (zu niedrigeren Temperaturen hin) die höchste und am den beiden Rändern gleichmäßig abnehmende Auflösung. Von Vorteil bei dieser Lösung ist, dass man sich über den Messbereich keine Gedanken machen muss, er ist prinzipiell 0 K .. ∞ K, und die Auflösung nimmt zu den beiden Extrema kontinuierlich ab.<br />
<br />
==== Rechenbeispiel (NTC mit 220 kΩ bei 25 °C) ====<br />
<table border cellspacing=0 cellpadding=4><br />
<tr bgcolor=#FFFFE0><th>Temperatur<th>NTC-Widerstandswert<th>Vorwiderstand<th>Ausgangsspannung<th>Auflösung<br />
<tr><td>0 °C<td>844 kΩ<td rowspan=6>100 kΩ<td>U<sub>B</sub> * 0,894<td rowspan=2>10 bit: 0,4 K<br>12 bit: 0,1 K<br />
<tr><td>10 °C<td>500 kΩ<td>U<sub>B</sub> * 0,667<br />
<tr bgcolor=#E0FFE0><td>40 °C<td>120 kΩ<td>U<sub>B</sub> * 0,545<td rowspan=2>10 bit: 0,1 K<br>12 bit: 0,025 K<br />
<tr bgcolor=#E0FFE0><td>50 °C<td>80 kΩ<td>U<sub>B</sub> * 0,444<br />
<tr><td>130 °C<td>5,63 kΩ<td>U<sub>B</sub> * 0,053<td rowspan=2>10 bit: 0,75 K<br>12 bit: 0,2 K<br />
<tr><td>140 °C<td>4,21 kΩ<td>U<sub>B</sub> * 0,040<br />
</table><br />
<br />
==== Grafisches Rechenbeispiel mittels WolframAlpha ====<br />
* [http://www.wolframalpha.com/input/?i=log+plot+y=220000*exp(4000*(1/(275%2Bx)-1/300)),+x=-50+to+150 Kennlinie (R über T)], die Exponentialkonstante '''B''' ist hier gleich 4000 angesetzt.<br />
* [http://www.wolframalpha.com/input/?i=plot+y=1/(1%2B1/exp(4000*(1/(275%2Bx)-1/300))),+x=-50+to+150 Ausgangsspannung (U/U<sub>B</sub> über T)], der Heißleiter befindet sich einpolig auf Masse, daher nimmt die Spannung mit steigender Temperatur ab. Der Vorwiderstand ist hier genauso groß wie der Heißleiter-Widerstand bei 25 °C (Nennwiderstand). Am Wendepunkt der s-förmigen Kurve ist die Steilheit und damit die Auflösung am größten.<br />
* [http://www.wolframalpha.com/input/?i=plot+derive+y=1/(1%2B1/exp(4000*(1/(275%2Bx)-1/300))),+x=-50+to+150 Steilheit (<i>d</i>U/U<sub>B</sub> über T) = Ableitung], der Wert -0,01 bedeutet ≈ 0,1 K Auflösung bei 1024 A/D-Werten (eines 10-bit-A/D-Wandlers).<br />
<br />
==== Tipp zur Spreizung des Messbereichs ====<br />
<br />
Möchte man den ''genau arbeitenden'' Messbereich vergrößern, ist es am einfachsten, den Vorwiderstand ''umschaltbar'' zu machen!<br />
<br />
Ein per Portpin schaltbarer Widerstand ist ohnehin nötig, wenn die Widerstandsmessung bei einem batteriebetriebenen Aufnehmer nur kurzzeitig erfolgen soll. Um Querstrom zu vermeiden, ist für die jeweiligen niederohmigen Widerstände (sofern umschaltbar mehrere vorhanden) an einem Pin anzuschließen, dessen digitaler Einang abtrennbar ist. Bei manchen Controllern, etwa MSP430x2xx, ist man da eingeschränkt.<br />
<br />
Da beide A/D-Wandlungen ratiometrisch arbeiten, sind keinerlei Referenzspannungen erforderlich. Die Maßverkörperung (Vergleichsnormal) erfolgt mit dem Festwiderstand bzw. den Festwiderständen.<br />
<br />
=== Mehr Auflösung ===<br />
<br />
<i>… bedeutet nicht unbedingt mehr Genauigkeit!</i><br />
<br />
Hier gibt es prinzipiell drei Wege:<br />
* Einschränken des Wandlungsbereiches durch Brückenschaltung und Verstärkung : Geringerer Messumfang<br><i>Typische Anwendung: Fieberthermometer</i><br />
* Verwenden eines besseren (externen) A/D-Wandlers - kann sich angesichts der gesunkenen Preise mittlerweile lohnen.<br />
* Verwenden eines anderen Wandlungsprinzips in Mikrocontroller-Software. In der einfachsten Form Oversampling (d.h. häufiges abtasten und Mitteln) mit dem vorhandenen ADC im µC. Möglich, wenn auch nicht besonders genau, ist etwa ein Einflanken-Umsetzer mit dem µC internen Analog-Komparator,der wenig externe Beschaltung erfordert.<br />
<br />
==Siehe auch==<br />
*[[Sensorarten]]<br />
<br />
<br />
==Weblinks==<br />
[http://de.wikipedia.org/wiki/Kaltleiter Wikipedia: Kaltleiter]<br />
<br />
[http://www.maxim-ic.com/app-notes/index.mvp/id/3450 Schaltung für PT100 mit Linearisierung]<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Sensoren]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=PTC/NTC&diff=25291PTC/NTC2014-09-20T16:32:19Z<p>Besserwessi: /* Brückenschaltung */</p>
<hr />
<div>[[Bild:Schaltzeichen_ntcptc.PNG|thumb|Schaltzeichen eines NTC (oben) und eines PTC (unten)]]<br />
Heißleiter (engl. '''NTC''', ''negative temperature coefficient'') und Kaltleiter (engl. '''PTC''', ''positive temperature coefficient'') sind elektrische Widerstände aus bestimmten Materialien, deren Leitfähigkeit (und damit der Widerstand) sich mit der Temperatur deutlich ändert. <br />
<br />
<br />
==NTCs==<br />
[[Bild:ntc.gif|thumb|Typische Bauform]]Heißleiter sind stromleitende Materialien, die bei hohen Temperaturen eine höhere Leitfähigkeit besitzen als bei tiefen Temperaturen. Sie haben einen negativen Temperaturkoeffizienten; das heißt, mit steigender Temperatur sinkt ihr elektrischer Widerstand.<br />
<br />
Ein großer Vorteil ist die starke Temperaturabhängigkeit und der geringe Preis. <br />
Das nichtlineare Verhalten des Widerstandswertes bei sich verändernder Temperatur ist jedoch zugleich auch der größte Nachteil.<br />
<br />
Ein einzelner, nicht kompensierter NTC ändert seinen Widerstand nicht linear. Hier dazu ein Beispieldiagramm:<br />
<br />
<br />
[[Bild:ntcdiagramm.gif]]<br />
<br />
<br />
<br />
===Temperaturabhängigkeit===<br />
<br />
Mithilfe eines NTCs lässt sich ein einfacher Temperatursensor bauen.<br />
Bildet der Heißleiter einen Spannungsteiler mit einem konstanten Widerstand, so lässt sich die geteilte Spannung mithilfe eines Microcontrollers messen, wodurch auf den Widerstand des NTCs geschlossen werden kann.<br /><br />
Der Heißleiter ändert seinen elektrischen Widerstand nicht linear. Jedoch kann die Temperatur auch rechnerisch ermittelt werden. Es gilt:<br/><br />
<pre><br />
T ~ 1/ln(R)<br />
</pre><br />
Bei Einbeziehung der Aktivierungsenergie, der Nenntemperatur (meist 25°C), und des Nennwiderstandes bei Nenntemperatur<br />
gilt folgende Funktion:<br /><br />
<pre><br />
T(R) = T_N * B / (B + T_N * ln(R / R_N))<br />
</pre><br />
Wobei '''T''' die momentane Temperatur, '''T_N''' die Nenntemperatur (in Kelvin), '''R''' der Widerstand des Heißleiters und '''R_N''' den Nennwiderstand darstellt. Bei '''B''' handelt es sich um eine Materialkonstante, die vom Herrsteller im Datenblatt angegeben wird. Bei Implementierung der Funktion in ein Programm für den Microcontroller müssen umbedingt die Datentypen berücksichtigt werden!<br />
<br />
{{Ausbauwunsch|Erbitte Implementierung der Gleichungen (Unbekannter Parserfehler)}}<br />
<br />
==PTCs==<br />
Kaltleiter sind stromleitende Materialien, die bei hohen Temperaturen eine niedrigere Leitfähigkeit besitzen als bei tiefen Temperaturen. Sie haben einen positiven Temperaturkoeffizienten; das heißt, mit steigender Temperatur steigt auch ihr elektrischer Widerstand.<br />
<br />
Bei den PTCs gibt es zwei Sorten: Einmal welche mit näherungsweise linearer Kennlinie und dann welche mit stark nichtlinearer Kennlinie. Die linearen Typen (z.B. KTYxx Reihe) oder Platinwiderstände (z.B. PT100, PT1000 ) sind mehr für die Temperaturmessung gedacht. <br />
Recht beliebt sind die PTCs der KTYxx-Reihe z.B. der KTY81-110. Sie sind sehr günstig (ca. 0,50€) und bieten für die meisten Anwendungen eine ausreichende Genauigkeit. <br />
<br />
Für höhere Anforderungen an den Messbereich (je nach Typ von -260 ... 850 °C) oder die Genauigkeit gibt es Platin-Widerstände zur Temperaturmessung. Auch hier ist der Temperaturkoeffizient positiv (0,38 %/K bei Raumtemperatur). Am häufigsten findet man hier Widerstände von 100 Ohm (PT100) und 1000 Ohm (PT1000). Diese sind jedoch relativ teurer (je nach Typ und Bezugsquelle und Qualität ab etwa 3 €).<br />
<br />
Die stark nichtlinearen Typen sind mehr als Übertemperaturschutz oder als selbst rückstellende Sicherung geeignet.<br />
Auch dazu ein Beispieldiagramm für einen nicht linearen PTC:<br />
<br />
<br />
[[Bild:Ptcdiagramm.GIF]]<br />
<br />
==Einatzmöglichkeiten==<br />
Mit NTCs und PTCs lassen sich in "normalen" Bereichen (das heißt hier kleiner etwa 200 °C) günstig Temperaturen messen. Dieses Gebiet umfasst alles vom kleinen digitalen Fieberthermometer über die verschiedenen Anzeigeinstrumente (wie Wetterstationen und KFZ-Elektronik) bis hin zu kompletten Heizungs- und Klimasteuerungen in Gebäuden.<br />
<br />
Daneben gibt es auch Anwendungen bei denen es nicht um eine eigentliche Messung geht: spezielle NTCs werden z.B. bei Netzteilen als Einschaltstrombegrenzer genutzt. Hier wird die Eigenerwärmung als gewollter Effekt mit genutzt.<br />
<br />
==Temperaturmessung mit PTC==<br />
Um aus der Widerstandsänderung am Sensor eine passende Spannung zu erzeugen gibt es verschieden Möglichkeiten. Ein naheliegender Weg ist eine Konstantstromquelle, so dass die Spannung am Sensor direkt proportional zum Widerstand ist. Wenn man das Signal danach digitalisieren will, hat dieser Weg aber zwei wesentliche Nachteile: Eine gute Konstantstromquelle ist aufwendig, und man hat dann 2 Referenzen im System und damit unnötige Fehlerquellen. Für eine Widerstandsmessung ist ein Widerstand als Vergleichswert besser und einfacher als das Verhältnis von einer Spannungsquelle und einer Stromquelle. Dafür gibt es im wesentlichen 3 Möglichkeiten:<br />
<br />
1) Der selbe Strom fließt durch den Sensor und den Vergleichswiderstand. Die Spannung an Vergleichswiderstand wird als Referenz für den AD Wandler benutzt, die Spannung am Sensor wird gemessen. Dieses Verfahren ist vor allem mit hochauflösenden AD Wandlern mit Differenzeingängen (z.B. LTC2440) sinnvoll. <br />
<br />
2) Der selbe AD Wandler misst nacheinander die Spannung am Sensor und Vergleichswiderstand. Dafür braucht man 2 Eingänge mit Differenzeingang. <br />
<br />
3) Der Sensor und der Vergleichswiderstand bilden einen Spannungsteiler. Die Spannung über den Spannungsteiler dient als Referenz für den AD-wandler. Anders als bei den beiden vorherigen Möglichkeiten ist das Ergebnis der AD Wandlung hier nicht mehr linear vom Widerstand abhängig.<br />
Bei einem kleinen Messbereich und geringer Auflösung des AD's lohnt es sich ggf. den konstanten Teil der Spannung abzuziehen, damit der Wertebereich des AD Wandler weitgehend ausgenutzt werden kann. Die Schaltung ist dann eine Brückenschaltung. <br />
<br />
In allen 3 Fällen muss der Strom nicht besonders stabilisiert sein und es wird keine stabile Referenzspannung für den AD benötigt. Es kommt nur auf das Spannungsverhältnis an. <br />
<br />
Beim Strom durch den Sensor muss ein Kompromiss zwischen genügend Spannung und der Eigenerwärmung des Sensors gefunden werden. Bei mehr als etwa 1 mW Verlustleistung am Sensor wird eine genaue Temperaturmessung schwierig. Für genaue Messungen mit dem PT100 werden oft sogar 0,1 mW als Grenze angegeben, d.h der Strom liegt bei nur 1 mA.<br />
<br />
==Linearisierung==<br />
[[Bild:LinearPTC.GIF]]<br />
KTY81 mit konstanten Strom (1,4 mA), und mit Widerstand in Reihe.<br />
<br />
Um trotz der nicht linearen Kennlinie des Sensors ein linear von der Messgröße (Temperatur) abhängiges Ergebnis (Spannung oder Digitaler Wert) zu erhalten, bieten sich verschiedene Methoden an:<br />
<br />
* Man nutzt die Nichtlineare Kennlinie von Dioden. Wegen der Temperaturabhängigkeit der Diodenkennlinie ist das aber schwierig und nur für sehr starke Nichtlinearitäten gerechtfertigt. Für die schwache Nichtlinearität der PTCs ist das Verfahren eher ungeeignet.<br />
<br />
* Man nutzt den nichtlinearen Zusammenhang zwischen Spannung und Widerstand beim Spannungsteiler, bzw. in einer Brückenschaltung. Bei den Sensoren des Typs KTY81 wird mit einem 2,7kOhm-Widerstand in Reihe gerade eine relativ gute Kompensation der Nichtlinearitäten erreicht. Im Bereich von -40 °C ... +140 °C erscheint der PTC nun nahezu linear, der verbleibende Linearitätsfehler liegt bei etwa ±10 mV (für 5 V am Spannungsteiler). So einfach geht die Linearisierung allerdings nicht bei allen Sensoren.<br />
<br />
* Man überlässt die Linearisierung den mathematischen Fähigkeiten eines Prozessors, z.B. einem ATMega-x, an dessen ADC der PTC angeschlossen ist. Das Verfahren ist sehr universell, benötigt aber einiges an Programmcode.<br />
<br />
==Schaltungsbeispiele==<br />
<br />
=== Brückenschaltung ===<br />
[[Bild:PT1000-Brücke.png]]<br />
<br />
Eine einfache, aber dennoch gute Auswerteschaltung ist eine Brückenschaltung. Wegen der relativ kleinen Spannungsänderungen wird oft eine Verstärkung benötigt, vor allem beim <b>Pt100</b> oder — Strom sparender — <b>Pt1000</b>.<br />
Bei den gezeigten Widerstandswerten reicht der Messbereich von etwa -25 °C bis +250 °C für 0 bis V+ (z.B. 5 V) am Ausgang. Über die Widerstände R2 und R3 kann der Bereich angepasst werden. Mit R3 = 20 kΩ hätte man z.B. einen Messbereich von etwa -10 °C bis + 130 °C. Die Linearisierung erfolgt in der Regel digital hinter dem A/D-Wandler. Die Referenzspannung des A/D-Wandlers sollte V+ sein, für eine ratiometrische Messung. Wenn nur eine Versorgung (z.B. 5 V) zur Verfügung stehen, sollte V+ an den Ausgangsbereich des OPs angepasst sein, also etwa 3 V für den LM358 (oder den besseren LT1013), oder der Operationsverstärker eine Rail-to-Rail-Typ (z.B. MCP6001) sein, wenn der AD mit der Versorgungsspannung als Ref. Arbeitet.<br />
<br />
{{Ausbauwunsch|... auf jeden Fall Anwendungs- und Schaltungsbeispiele mit Heiß- oder Kaltleiter!'''}}<br />
<br />
=== Spannungsteiler ===<br />
<br />
Für <b>NTC</b> (Heißleiter) bietet sich ein einfacher Spannungsteiler an. Den Vorwiderstand dimensioniert man so, dass er etwa so groß ist wie der Widerstandswert <i>in der Mitte des gewünschten Temperatur-Messbereiches</i>. Die A/D-Wandlung erfolgt „ratiometrisch“ mit dem vollen Speisespannungsbereich. Dann hat man — nach der Linearisierung in Software — etwas oberhalb der Mitte (zu niedrigeren Temperaturen hin) die höchste und am den beiden Rändern gleichmäßig abnehmende Auflösung. Von Vorteil bei dieser Lösung ist, dass man sich über den Messbereich keine Gedanken machen muss, er ist prinzipiell 0 K .. ∞ K, und die Auflösung nimmt zu den beiden Extrema kontinuierlich ab.<br />
<br />
==== Rechenbeispiel (NTC mit 220 kΩ bei 25 °C) ====<br />
<table border cellspacing=0 cellpadding=4><br />
<tr bgcolor=#FFFFE0><th>Temperatur<th>NTC-Widerstandswert<th>Vorwiderstand<th>Ausgangsspannung<th>Auflösung<br />
<tr><td>0 °C<td>844 kΩ<td rowspan=6>100 kΩ<td>U<sub>B</sub> * 0,894<td rowspan=2>10 bit: 0,4 K<br>12 bit: 0,1 K<br />
<tr><td>10 °C<td>500 kΩ<td>U<sub>B</sub> * 0,667<br />
<tr bgcolor=#E0FFE0><td>40 °C<td>120 kΩ<td>U<sub>B</sub> * 0,545<td rowspan=2>10 bit: 0,1 K<br>12 bit: 0,025 K<br />
<tr bgcolor=#E0FFE0><td>50 °C<td>80 kΩ<td>U<sub>B</sub> * 0,444<br />
<tr><td>130 °C<td>5,63 kΩ<td>U<sub>B</sub> * 0,053<td rowspan=2>10 bit: 0,75 K<br>12 bit: 0,2 K<br />
<tr><td>140 °C<td>4,21 kΩ<td>U<sub>B</sub> * 0,040<br />
</table><br />
<br />
==== Grafisches Rechenbeispiel mittels WolframAlpha ====<br />
* [http://www.wolframalpha.com/input/?i=log+plot+y=220000*exp(4000*(1/(275%2Bx)-1/300)),+x=-50+to+150 Kennlinie (R über T)], die Exponentialkonstante '''B''' ist hier gleich 4000 angesetzt.<br />
* [http://www.wolframalpha.com/input/?i=plot+y=1/(1%2B1/exp(4000*(1/(275%2Bx)-1/300))),+x=-50+to+150 Ausgangsspannung (U/U<sub>B</sub> über T)], der Heißleiter befindet sich einpolig auf Masse, daher nimmt die Spannung mit steigender Temperatur ab. Der Vorwiderstand ist hier genauso groß wie der Heißleiter-Widerstand bei 25 °C (Nennwiderstand). Am Wendepunkt der s-förmigen Kurve ist die Steilheit und damit die Auflösung am größten.<br />
* [http://www.wolframalpha.com/input/?i=plot+derive+y=1/(1%2B1/exp(4000*(1/(275%2Bx)-1/300))),+x=-50+to+150 Steilheit (<i>d</i>U/U<sub>B</sub> über T) = Ableitung], der Wert -0,01 bedeutet ≈ 0,1 K Auflösung bei 1024 A/D-Werten (eines 10-bit-A/D-Wandlers).<br />
<br />
==== Tipp zur Spreizung des Messbereichs ====<br />
<br />
Möchte man den ''genau arbeitenden'' Messbereich vergrößern, ist es am einfachsten, den Vorwiderstand ''umschaltbar'' zu machen!<br />
<br />
Ein per Portpin schaltbarer Widerstand ist ohnehin nötig, wenn die Widerstandsmessung bei einem batteriebetriebenen Aufnehmer nur kurzzeitig erfolgen soll. Um Querstrom zu vermeiden, ist für die jeweiligen niederohmigen Widerstände (sofern umschaltbar mehrere vorhanden) an einem Pin anzuschließen, dessen digitaler Einang abtrennbar ist. Bei manchen Controllern, etwa MSP430x2xx, ist man da eingeschränkt.<br />
<br />
Da beide A/D-Wandlungen ratiometrisch arbeiten, sind keinerlei Referenzspannungen erforderlich. Die Maßverkörperung (Vergleichsnormal) erfolgt mit dem Festwiderstand bzw. den Festwiderständen.<br />
<br />
=== Mehr Auflösung ===<br />
<br />
<i>… bedeutet nicht unbedingt mehr Genauigkeit!</i><br />
<br />
Hier gibt es prinzipiell drei Wege:<br />
* Einschränken des Wandlungsbereiches durch Vorverstärkung oder mittels Referenzspannungen: Geringerer Messumfang<br><i>Typische Anwendung: Fieberthermometer</i><br />
* Verwenden eines besseren (externen) A/D-Wandlers — wäre nur für Temperaturmessung Overkill<br />
* Verwenden eines anderen Wandlungsprinzips in Mikrocontroller-Software<br />
<br />
Recht gute Ergebnisse liefern Zweiflanken-A/D-Umsetzer. Diese erfordern jedoch einen OPV sowie einen Analogsignal-Umschalter. Diese sind selten in Mikrocontrollern bereits integriert.<br />
<br />
Die übliche Vorgehensweise ist ein Einflanken-Umsetzer, der mit dem zumeist vorhandenen Analog-Komparator arbeitet und wenig externe Beschaltung erfordert.<br />
<br />
==Siehe auch==<br />
*[[Sensorarten]]<br />
<br />
<br />
==Weblinks==<br />
[http://de.wikipedia.org/wiki/Kaltleiter Wikipedia: Kaltleiter]<br />
<br />
[http://www.maxim-ic.com/app-notes/index.mvp/id/3450 Schaltung für PT100 mit Linearisierung]<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Sensoren]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Arm&diff=25107Arm2014-08-23T16:47:27Z<p>Besserwessi: Werbung für sinnlosen Webshop entfernt</p>
<hr />
<div>{{Ausbauwunsch|Vorteile/Nachteile gegenüber AVRs, Hersteller, Bezugsquellen...}}<br />
ARM ist die Bezeichnung für eine schnelle 32-bit-Mikrocontroller-RISC-Architektur. <br />
Namensgebend ist die englische Firma ARM Limited. Diese Firma produziert selbst keine Mikrocontroller, vertreibt aber Lizenzen an Lizenznehmer, die ARM-basierte Mikrocontroller entwickeln, fertigen und vermarkten. Die Controller werden von sehr vielen Herstellern produziert, auch von dem Hersteller [[Atmel]], der im Roboternetz durch die AVR Controller recht bekannt ist. Verbreiteter sind jedoch Philips (NXP) ARM7 Controller als LPC2xxx in verschiedenen Ausstattungen sowie STM32Fxxx Controller von STMicroelectronics. <br />
<br />
Als verschiedene Leistungsklassen gibt es am unteren Ende Cortex M0, M3 und M4 und die älteren ARM7. ARM7 und Cortex M0 und M3 Mikrocontroller bieten beachtliche Leistungsmerkmale zu einen günstigen Preis, weshalb sich diese Leistungsklasse sehr rasant z.B. in MP3-Playern verbreitet hat. ARM9 Mikrocontroller bilden das Herz vieler eingebetteter Systeme (Embedded Systems) und bieten meist komplexe Funktionen und werden in WLAN-Router oder PDAs eingesetzt, sind aber für den Bastler auf Grund der hohen Packungsdichte (PQFP,BGA) kaum selbst zu verarbeiten. Die kleinen Cortex M0 sind immer noch leistungsfähiger als z.B. ein 8-Bit AVR.<br />
<br />
Kleine Schwierigkeiten für den Bastler entstehen auch bei den kleineren ARM's: Die µC sind nur als SMD zu bekommen und viele (vor allem ARM7) benötigen 2 Versorgungsspannungen (z.B. 1,8 V und 3,3 V). Entsprechend, sind diese bislang bei Bastlern bisher weniger verbreitet. Es gibt jedoch auch diverse Ausführungen als "Stamp" oder komplette Experimentalboards incl. SD/MMC-Kartenleser, Bluetooth und USB, Ethernet und was das Herz sonst noch erfreut.<br />
<br />
Dank des Bootloaders im Controller sind die Prozessoren mit normalen RS232 Schnittstellen programmierbar. Als Toolchain kann man einen dem WinAVR ähnlichen WinARM mit GNU gcc Compiler und Betriebssystemen von z.B. von FreeRTOS, UTOS, tn-Kernel oder bei ARM9-Systemen ucLinux bis hin zu Windows CE nutzen. Als einfache Plattform für erste Experimente kann z.B. ein ausrangierter WLAN-Access point oder PDA dienen, zu dem es Docu und ein System wie DD-WRT im Netz gibt. Wie man die diversen Geräte hackt, ist im Web einschlägig beschrieben. Für einfache Aufgaben wie Linien folgen/Schalter abfragen ist so ein ARM9 System etwas "oversized" aber wenn man dem Bot etwas mehr Intelligenz gönnen möchte (USBCam, WLAN, Ethernet, GPS, oder Dienste wie Webserver/SSH, eigenständige räumliche Orientierung), kommt man an diesen komplexeren Systemen kaum vorbei. Ein denkbares Szenario wäre z.B. die Kopplung eines oder mehrerer ARM7 (als ausführende, zeitkritische Einheiten) und einem ARM 9 Prozessors (als "denkende" Einheit) mittels I2C, CAN, RS423 oder RS485. (Was natürlich statt des ARM7 auch mit kleinen AVR CPUs geht). Aber auch ein ARM7 System mit 512KB Flash und 32KB Ram wie bei diversen LPC2138 Stamps (LPC2148 mit USB+8KRam DMA) kann schon einiges.<br />
<br />
Der Einstieg in die ARM-Architektur ist mit C Kenntnissen nicht schwieriger als bei jeder anderen CPU und die Leistungsmerkmale von 30 (ARM7) bis weit über 200 (ARM9) MIPS oder Speicherverwaltung bis in den Megabyte Bereich (ARM9) sprechen für sich.<br />
<br />
== Vergleich mit 8 /16 Bit µCs ==<br />
=== Vorteile gegenüber 8 Bit µCs ===<br />
* schnell, vor allem wenn mit 16 Bit oder 32 Bit Zahlen gerechnet wird<br />
* sehr leistungsfähige Peripherie (AD, Timer, UART) teils auch USB, CAN oder Ethernet<br />
* oft recht viele IO-Pins<br />
* Modelle mit großem Flash-speicher verfügbar (z.B. 1 MBytes) und ohne Einschränkung nutzbar<br />
* fertige Platinen ("Stamp", "Evalution-module", Breakout-boards, BeagleBone, Raspberry Pi, FOX Board G20, Aria G25) sind teils sehr günstig<br />
* JTAG-Interface ist günstig zu haben<br />
* geringer Stromverbrauch im Vergleich zur Leistung<br />
* Betrieb ab ca. 3 V mit voller Geschwindigkeit<br />
* leistungsfähige Betriebssysteme (z.B. Linux) einsetzbar<br />
<br />
=== Nachteile gegenüber 8 Bit µCs ===<br />
* reine µC nur als SMD Version. Durch den geringen Pin Abstand (meist 0,5 mm) ist es sehr schwer hier selbst geätzte Platinen zu nutzen.<br />
* mit extra Platine oft teurer als 8 Bit µC. Auch reduziert sich damit die Auswahl der µCs deutlich und die mechanische Größe nimmt zu.<br />
* Timing durch Waitstates, und ggf. Puffer nicht so leicht berechenbar<br />
* teils relativ langsamer Zugriff auf IO Register<br />
* nur 3.3 V als Ausgangsspannung - ggf. Pegelwandler nötig<br />
<br />
==Siehe auch==<br />
*[[Atmel]] <br />
*[[Avr|AVR]]<br />
<br />
==Weblinks==<br />
<br />
* http://arm.com/products/CPUs/architecture.html<br />
* http://www.mikrocontroller.net/articles/ARM<br />
* http://www.freertos.org/<br />
* http://www.uclinux.org/<br />
* http://raspberrypi.org<br />
<br />
[[Kategorie:Microcontroller]]<br />
[[Kategorie:Grundlagen]]<br />
[[Kategorie:Abkürzung]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Optokoppler&diff=25102Optokoppler2014-08-15T17:42:33Z<p>Besserwessi: /* Lichtsender und -empfänger */</p>
<hr />
<div>[[Bild:optokoppler.jpg|thumb|Beispiel eines Optokopplers]]<br />
Sein Name lässt es schon erahnen: Wie die [[Leuchtdiode]] gehört auch der Optokoppler zu den optoelektronischen Bauelementen. Genauer betrachtet sind bei einem Optokoppler zwei "optisch ge-koppelte" Halbleiterbauelemente in einem Gehäuse untergebracht. <br />
Der Optokoppler ist, salopp formuliert, Optoelektronik im Doppelpack - die Wechselwirkung zwischen Photonen ("Licht") und elektrischen Ladungsträgern bei Halbleitern wird gleich zweifach genutzt, um die gewünschte Funktion zu erreichen. <br />
<br />
==Lichtsender und -empfänger== <br />
Im Optokoppler finden wir eine Leuchtdiode als Lichtsender und eine Fotodiode (oder Fototransistor/-thyristor/-triac) als Lichtempfänger. Dabei sind Sender und Empfänger gegenüber und vor äußerer Lichtstrahlung geschützt in einem gemeinsamen Gehäuse angeordnet, oft kleiner als ein herkömmliches 8 Pin Dil IC. Versorgt man die Leuchtdiode mit Strom, leuchtet sie auf und das Licht gelangt über einen lichtdurchlässigen Isolator, z. B. Glas, zur Fotodiode, die unter dieser Lichteinwirkung leitend wird. Durch ihre Lichtstrahlung steuert die [[Leuchtdiode]] die Fotodiode (Leuchtdiode ein = Fotodiode leitet, Leuchtdiode aus = Fotodiode sperrt). Und das, obwohl beide nicht elektrisch verbunden, sondern nur über einen Lichtpfad gekoppelt sind.<br />
<br />
==Wichtige Kenngrößen ==<br />
Die meisten Optokoppler bestehen aus einer IR-LED und einem Fototransistor. Auf diese Typen beziehen sich die folgenden Eigenschaften der Optokoppler:<br />
Der Strom durch die LED auf der Sendeseite darf einen Maximalwert von z.B. 50 mA nicht überschreiten, einfach um die LED nicht zu überhitzen. Der Strom auf der Ausgangsseite ist etwa proportional zum Strom durch die LED. Das Verhältnis der Ströme (Ausgangsseite durch Eingangsseite) wird meist als CTR (current transfer ratio) genannt. Wenn z.B. ein CTR Wert von mindestens 50% angegeben ist, heißt das, dass der Ausgang bis mindestens 50% des LED Stromes leiten kann.<br />
<br />
In manchen Anwendungen (etwa UART über Optokoppler) ist die Geschwindigkeit des Optokopplers wichtig. Hier muss man etwas aufpassen, denn die Schaltzeiten hängen vom Widerstand auf der Ausgangsseite und dem LED Strom ab. Die Angaben im Datenblatt beziehen sich teils auf sehr kleine Widerstände. Mit den üblichen Widerständen um am Ausgang einen gültigen Logikpegel zu erreichen ist der Optokoppler dann einiges langsamer, insbesondere wenn er bis in die Sättigung (Kollektor- Emitter-Spannung nahe 0) getrieben wird. Gelegentlich findet man auch ein Kurve für verschieden Lastwiderstände.<br />
<br />
Ein weiterer Punkt ist die Spannungsfestigkeit der Isolation. Nicht alle Typen sind ausreichen für die sichere Trennung von Netzspannung.<br />
<br />
==Wozu braucht man Optokoppler?==<br />
In der Elektronik gibt es viele Anwendungen, bei denen Signale übertragen und die zugehörigen Ein- und Ausgangsschaltungen galvanisch getrennt (elektrisch isoliert) werden müssen. Die galvanische Trennung von Schaltungsteilen kann technische Gründe haben und/oder als Sicherheitsmaßnahme dienen. Zum Beispiel, wenn nachfolgende Schaltungen keinen elektrischen Einfluss auf vorhergehende Schaltungen haben dürfen. Im Fachjargon heißt das dann "rückwirkungsfreie Signalübertragung". <br />
<br />
Mit seiner Fähigkeit, über kleine Eingangssignale einen galvanisch getrennten Ausgangskreis zu steuern, ist der Optokoppler einem [[Relais]] sehr ähnlich. Im Unterschied zu [[Relais]] weisen Optokoppler jedoch keine verschleißanfälligen mechanischen Teile auf und zeichnen sich darüber hinaus durch kürzere Schaltzeiten und kleinere Abmessungen aus. Die Isolationsspannung (Durchschlagsfestigkeit) von Optokopplern beträgt mehrere Kilovolt, je nach Abstand und Anordnung von Lichtsender und -empfänger und Isolationsmaterial. Für medizinische Anwendungen sind bspw. mindestens 5.3kV vorgeschrieben. Mit Optokopplern, die übrigens schon seit 1972 angeboten werden, können sowohl binäre als auch analoge Signale übertragen werden. Ohne besondere Vorkehrungen und Typen ist die analoge Übertragung allerdings recht ungenau.<br />
<br />
==Autoren/Quellen==<br />
* Live News Spoerle<br />
* Übernommen Frank<br />
<br />
<br />
==Siehe auch==<br />
* [[Leuchtdiode]]<br />
* [[Relais]]<br />
* [[Fotodiode]]<br />
* [[Fototransistor]]<br />
<br />
[[Kategorie:Grundlagen]]<br />
[[Kategorie:Elektronik]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Optokoppler&diff=25101Optokoppler2014-08-15T17:08:00Z<p>Besserwessi: /* Wozu braucht man Optokoppler? */</p>
<hr />
<div>[[Bild:optokoppler.jpg|thumb|Beispiel eines Optokopplers]]<br />
Sein Name lässt es schon erahnen: Wie die [[Leuchtdiode]] gehört auch der Optokoppler zu den optoelektronischen Bauelementen. Genauer betrachtet sind bei einem Optokoppler zwei "optisch ge-koppelte" Halbleiterbauelemente in einem Gehäuse untergebracht. <br />
Der Optokoppler ist, salopp formuliert, Optoelektronik im Doppelpack - die Wechselwirkung zwischen Photonen ("Licht") und elektrischen Ladungsträgern bei Halbleitern wird gleich zweifach genutzt, um die gewünschte Funktion zu erreichen. <br />
<br />
==Lichtsender und -empfänger== <br />
Im Optokoppler finden wir eine Leuchtdiode als Lichtsender und eine Fotodiode (oder Fototransistor/-thyristor/-triac) als Lichtempfänger. Dabei sind Sender und Empfänger gegenüber und vor äußerer Lichtstrahlung geschützt in einem gemeinsamen Gehäuse angeordnet, oft kleiner als ein herkömmliches 8 Pin Dil IC. Versorgt man die Leuchtdiode mit Strom, leuchtet sie auf und das Licht gelangt über einen lichtdurchlässigen Isolator, z. B. Glas, zur Fotodiode, die unter dieser Lichteinwirkung leitend wird. Durch ihre Lichtstrahlung steuert die [[Leuchtdiode]] die Fotodiode (Leuchtdiode ein = Fotodiode leitet, Leuchtdiode aus = Fotodiode sperrt). Und das, obwohl beide nicht elektrisch verbunden, sondern nur über einen Lichtpfad gekoppelt sind.<br />
<br />
==Wozu braucht man Optokoppler?==<br />
In der Elektronik gibt es viele Anwendungen, bei denen Signale übertragen und die zugehörigen Ein- und Ausgangsschaltungen galvanisch getrennt (elektrisch isoliert) werden müssen. Die galvanische Trennung von Schaltungsteilen kann technische Gründe haben und/oder als Sicherheitsmaßnahme dienen. Zum Beispiel, wenn nachfolgende Schaltungen keinen elektrischen Einfluss auf vorhergehende Schaltungen haben dürfen. Im Fachjargon heißt das dann "rückwirkungsfreie Signalübertragung". <br />
<br />
Mit seiner Fähigkeit, über kleine Eingangssignale einen galvanisch getrennten Ausgangskreis zu steuern, ist der Optokoppler einem [[Relais]] sehr ähnlich. Im Unterschied zu [[Relais]] weisen Optokoppler jedoch keine verschleißanfälligen mechanischen Teile auf und zeichnen sich darüber hinaus durch kürzere Schaltzeiten und kleinere Abmessungen aus. Die Isolationsspannung (Durchschlagsfestigkeit) von Optokopplern beträgt mehrere Kilovolt, je nach Abstand und Anordnung von Lichtsender und -empfänger und Isolationsmaterial. Für medizinische Anwendungen sind bspw. mindestens 5.3kV vorgeschrieben. Mit Optokopplern, die übrigens schon seit 1972 angeboten werden, können sowohl binäre als auch analoge Signale übertragen werden. Ohne besondere Vorkehrungen und Typen ist die analoge Übertragung allerdings recht ungenau.<br />
<br />
==Autoren/Quellen==<br />
* Live News Spoerle<br />
* Übernommen Frank<br />
<br />
<br />
==Siehe auch==<br />
* [[Leuchtdiode]]<br />
* [[Relais]]<br />
* [[Fotodiode]]<br />
* [[Fototransistor]]<br />
<br />
[[Kategorie:Grundlagen]]<br />
[[Kategorie:Elektronik]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=PTC/NTC&diff=24309PTC/NTC2014-05-04T18:39:44Z<p>Besserwessi: /* Brückenschaltung */ Anpassung U+ -> V+ passend zum Bild</p>
<hr />
<div>[[Bild:Schaltzeichen_ntcptc.PNG|thumb|Schaltzeichen eines NTC (oben) und eines PTC (unten)]]<br />
Heißleiter (engl. '''NTC''', ''negative temperature coefficient'') und Kaltleiter (engl. '''PTC''', ''positive temperature coefficient'') sind elektrische Widerstände aus bestimmten Materialien, deren Leitfähigkeit (und damit der Widerstand) sich mit der Temperatur deutlich ändert. <br />
<br />
<br />
==NTCs==<br />
[[Bild:ntc.gif|thumb|Typische Bauform]]Heißleiter sind stromleitende Materialien, die bei hohen Temperaturen eine höhere Leitfähigkeit besitzen als bei tiefen Temperaturen. Sie haben einen negativen Temperaturkoeffizienten; das heißt, mit steigender Temperatur sinkt ihr elektrischer Widerstand.<br />
<br />
Ein großer Vorteil ist die starke Temperaturabhängigkeit und der geringe Preis. <br />
Das nichtlineare Verhalten des Widerstandswertes bei sich verändernder Temperatur ist jedoch zugleich auch der größte Nachteil.<br />
<br />
Ein einzelner, nicht kompensierter NTC ändert seinen Widerstand nicht linear. Hier dazu ein Beispieldiagramm:<br />
<br />
<br />
[[Bild:ntcdiagramm.gif]]<br />
<br />
<br />
<br />
===Temperaturabhängigkeit===<br />
<br />
Mithilfe eines NTCs lässt sich ein einfacher Temperatursensor bauen.<br />
Bildet der Heißleiter einen Spannungsteiler mit einem konstanten Widerstand, so lässt sich die geteilte Spannung mithilfe eines Microcontrollers messen, wodurch auf den Widerstand des NTCs geschlossen werden kann.<br /><br />
Der Heißleiter ändert seinen elektrischen Widerstand nicht linear. Jedoch kann die Temperatur auch rechnerisch ermittelt werden. Es gilt:<br/><br />
<pre><br />
T ~ 1/ln(R)<br />
</pre><br />
Bei Einbeziehung der Aktivierungsenergie, der Nenntemperatur (meist 25°C), und des Nennwiderstandes bei Nenntemperatur<br />
gilt folgende Funktion:<br /><br />
<pre><br />
T(R) = T_N * B / (B + T_N * ln(R / R_N))<br />
</pre><br />
Wobei '''T''' die momentane Temperatur, '''T_N''' die Nenntemperatur (in Kelvin), '''R''' der Widerstand des Heißleiters und '''R_N''' den Nennwiderstand darstellt. Bei '''B''' handelt es sich um eine Materialkonstante, die vom Herrsteller im Datenblatt angegeben wird. Bei Implementierung der Funktion in ein Programm für den Microcontroller müssen umbedingt die Datentypen berücksichtigt werden!<br />
<br />
{{Ausbauwunsch|Erbitte Implementierung der Gleichungen (Unbekannter Parserfehler)}}<br />
<br />
==PTCs==<br />
Kaltleiter sind stromleitende Materialien, die bei hohen Temperaturen eine niedrigere Leitfähigkeit besitzen als bei tiefen Temperaturen. Sie haben einen positiven Temperaturkoeffizienten; das heißt, mit steigender Temperatur steigt auch ihr elektrischer Widerstand.<br />
<br />
Bei den PTCs gibt es zwei Sorten: Einmal welche mit näherungsweise linearer Kennlinie und dann welche mit stark nichtlinearer Kennlinie. Die linearen Typen (z.B. KTYxx Reihe) oder Platinwiderstände (z.B. PT100, PT1000 ) sind mehr für die Temperaturmessung gedacht. <br />
Recht beliebt sind die PTCs der KTYxx-Reihe z.B. der KTY81-110. Sie sind sehr günstig (ca. 0,50€) und bieten für die meisten Anwendungen eine ausreichende Genauigkeit. <br />
<br />
Für höhere Anforderungen an den Messbereich (je nach Typ von -260 ... 850 °C) oder die Genauigkeit gibt es Platin-Widerstände zur Temperaturmessung. Auch hier ist der Temperaturkoeffizient positiv (0,38 %/K bei Raumtemperatur). Am häufigsten findet man hier Widerstände von 100 Ohm (PT100) und 1000 Ohm (PT1000). Diese sind jedoch relativ teurer (je nach Typ und Bezugsquelle und Qualität ab etwa 3 €).<br />
<br />
Die stark nichtlinearen Typen sind mehr als Übertemperaturschutz oder als selbst rückstellende Sicherung geeignet.<br />
Auch dazu ein Beispieldiagramm für einen nicht linearen PTC:<br />
<br />
<br />
[[Bild:Ptcdiagramm.GIF]]<br />
<br />
==Einatzmöglichkeiten==<br />
Mit NTCs und PTCs lassen sich in "normalen" Bereichen (das heißt hier kleiner etwa 200 °C) günstig Temperaturen messen. Dieses Gebiet umfasst alles vom kleinen digitalen Fieberthermometer über die verschiedenen Anzeigeinstrumente (wie Wetterstationen und KFZ-Elektronik) bis hin zu kompletten Heizungs- und Klimasteuerungen in Gebäuden.<br />
<br />
Daneben gibt es auch Anwendungen bei denen es nicht um eine eigentliche Messung geht: spezielle NTCs werden z.B. bei Netzteilen als Einschaltstrombegrenzer genutzt. Hier wird die Eigenerwärmung als gewollter Effekt mit genutzt.<br />
<br />
==Temperaturmessung mit PTC==<br />
Um aus der Widerstandsänderung am Sensor eine passende Spannung zu erzeugen gibt es verschieden Möglichkeiten. Ein naheliegender Weg ist eine Konstantstromquelle, so dass die Spannung am Sensor direkt proportional zum Widerstand ist. Wenn man das Signal danach digitalisieren will, hat dieser Weg aber zwei wesentliche Nachteile: Eine gute Konstantstromquelle ist aufwendig, und man hat dann 2 Referenzen im System und damit unnötige Fehlerquellen. Für eine Widerstandsmessung ist ein Widerstand als Vergleichswert besser und einfacher als das Verhältnis von einer Spannungsquelle und einer Stromquelle. Dafür gibt es im wesentlichen 3 Möglichkeiten:<br />
<br />
1) Der selbe Strom fließt durch den Sensor und den Vergleichswiderstand. Die Spannung an Vergleichswiderstand wird als Referenz für den AD Wandler benutzt, die Spannung am Sensor wird gemessen. Dieses Verfahren ist vor allem mit hochauflösenden AD Wandlern mit Differenzeingängen (z.B. LTC2440) sinnvoll. <br />
<br />
2) Der selbe AD Wandler misst nacheinander die Spannung am Sensor und Vergleichswiderstand. Dafür braucht man 2 Eingänge mit Differenzeingang. <br />
<br />
3) Der Sensor und der Vergleichswiderstand bilden einen Spannungsteiler. Die Spannung über den Spannungsteiler dient als Referenz für den AD-wandler. Anders als bei den beiden vorherigen Möglichkeiten ist das Ergebnis der AD Wandlung hier nicht mehr linear vom Widerstand abhängig.<br />
Bei einem kleinen Messbereich und geringer Auflösung des AD's lohnt es sich ggf. den konstanten Teil der Spannung abzuziehen, damit der Wertebereich des AD Wandler weitgehend ausgenutzt werden kann. Die Schaltung ist dann eine Brückenschaltung. <br />
<br />
In allen 3 Fällen muss der Strom nicht besonders stabilisiert sein und es wird keine stabile Referenzspannung für den AD benötigt. Es kommt nur auf das Spannungsverhältnis an. <br />
<br />
Beim Strom durch den Sensor muss ein Kompromiss zwischen genügend Spannung und der Eigenerwärmung des Sensors gefunden werden. Bei mehr als etwa 1 mW Verlustleistung am Sensor wird eine genaue Temperaturmessung schwierig. Für genaue Messungen mit dem PT100 werden oft sogar 0,1 mW als Grenze angegeben, d.h der Strom liegt bei nur 1 mA.<br />
<br />
==Linearisierung==<br />
[[Bild:LinearPTC.GIF]]<br />
KTY81 mit konstanten Strom (1,4 mA), und mit Widerstand in Reihe.<br />
<br />
Um trotz der nicht linearen Kennlinie des Sensors ein linear von der Messgröße (Temperatur) abhängiges Ergebnis (Spannung oder Digitaler Wert) zu erhalten, bieten sich verschiedene Methoden an:<br />
<br />
* Man nutzt die Nichtlineare Kennlinie von Dioden. Wegen der Temperaturabhängigkeit der Diodenkennlinie ist das aber schwierig und nur für sehr starke Nichtlinearitäten gerechtfertigt. Für die schwache Nichtlinearität der PTCs ist das Verfahren eher ungeeignet.<br />
<br />
* Man nutzt den nichtlinearen Zusammenhang zwischen Spannung und Widerstand beim Spannungsteiler, bzw. in einer Brückenschaltung. Bei den Sensoren des Typs KTY81 wird mit einem 2,7kOhm-Widerstand in Reihe gerade eine relativ gute Kompensation der Nichtlinearitäten erreicht. Im Bereich von -40 °C ... +140 °C erscheint der PTC nun nahezu linear, der verbleibende Linearitätsfehler liegt bei etwa ±10 mV (für 5 V am Spannungsteiler). So einfach geht die Linearisierung allerdings nicht bei allen Sensoren.<br />
<br />
* Man überlässt die Linearisierung den mathematischen Fähigkeiten eines Prozessors, z.B. einem ATMega-x, an dessen ADC der PTC angeschlossen ist. Das Verfahren ist sehr universell, benötigt aber einiges an Programmcode.<br />
<br />
==Schaltungsbeispiele==<br />
<br />
=== Brückenschaltung ===<br />
[[Bild:PT1000-Brücke.png]]<br />
<br />
Ein einfache, aber dennoch gute Auswerteschaltung ist eine Brückenschaltung. Wegen der relativ kleinen Spannungsänderungen wird oft eine Verstärkung benötigt, vor allem beim <b>Pt100</b> oder — Strom sparender — <b>Pt1000</b>.<br />
Bei den gezeigten Widerstandswerten reicht der Messbereich von etwa -25 °C bis +250 °C für 0 bis V+ (z.B. 5 V) am Ausgang. Über die Widerstände R2 und R3 kann der Bereich angepasst werden. Mit R3 = 20 kΩ hätte man z.B. einen Messbereich von etwa -10 °C bis + 130 °C. Die Linearisierung erfolgt in der Regel digital hinter dem A/D-Wandler. Die Referenzspannung des A/D-Wandlers sollte V+ sein, für eine ratiometrische Messung. Wenn nur eine Versorgung (z.B. 5 V) zur Verfügung stehen, sollte V+ an den Ausgangsbereich des OPs angepasst sein, also etwa 3 V für den LM358 (oder den besseren LT1013), oder der Operationsverstärker eine Rail-to-Rail-Typ (z.B. MCP6001) sein, wenn der AD mit der Versorgungsspannung als Ref. Arbeitet.<br />
<br />
{{Ausbauwunsch|... auf jeden Fall Anwendungs- und Schaltungsbeispiele mit Heiß- oder Kaltleiter!'''}}<br />
<br />
=== Spannungsteiler ===<br />
<br />
Für <b>NTC</b> (Heißleiter) bietet sich ein einfacher Spannungsteiler an. Den Vorwiderstand dimensioniert man so, dass er etwa so groß ist wie der Widerstandswert <i>in der Mitte des gewünschten Temperatur-Messbereiches</i>. Die A/D-Wandlung erfolgt „ratiometrisch“ mit dem vollen Speisespannungsbereich. Dann hat man — nach der Linearisierung in Software — etwas oberhalb der Mitte (zu niedrigeren Temperaturen hin) die höchste und am den beiden Rändern gleichmäßig abnehmende Auflösung. Von Vorteil bei dieser Lösung ist, dass man sich über den Messbereich keine Gedanken machen muss, er ist prinzipiell 0 K .. ∞ K, und die Auflösung nimmt zu den beiden Extrema kontinuierlich ab.<br />
<br />
==== Rechenbeispiel (NTC mit 220 kΩ bei 25 °C) ====<br />
<table border cellspacing=0 cellpadding=4><br />
<tr bgcolor=#FFFFE0><th>Temperatur<th>NTC-Widerstandswert<th>Vorwiderstand<th>Ausgangsspannung<th>Auflösung<br />
<tr><td>0 °C<td>844 kΩ<td rowspan=6>100 kΩ<td>U<sub>B</sub> * 0,894<td rowspan=2>10 bit: 0,4 K<br>12 bit: 0,1 K<br />
<tr><td>10 °C<td>500 kΩ<td>U<sub>B</sub> * 0,667<br />
<tr bgcolor=#E0FFE0><td>40 °C<td>120 kΩ<td>U<sub>B</sub> * 0,545<td rowspan=2>10 bit: 0,1 K<br>12 bit: 0,025 K<br />
<tr bgcolor=#E0FFE0><td>50 °C<td>80 kΩ<td>U<sub>B</sub> * 0,444<br />
<tr><td>130 °C<td>5,63 kΩ<td>U<sub>B</sub> * 0,053<td rowspan=2>10 bit: 0,75 K<br>12 bit: 0,2 K<br />
<tr><td>140 °C<td>4,21 kΩ<td>U<sub>B</sub> * 0,040<br />
</table><br />
<br />
==== Grafisches Rechenbeispiel mittels WolframAlpha ====<br />
* [http://www.wolframalpha.com/input/?i=log+plot+y=220000*exp(4000*(1/(275%2Bx)-1/300)),+x=-50+to+150 Kennlinie (R über T)], die Exponentialkonstante '''B''' ist hier gleich 4000 angesetzt.<br />
* [http://www.wolframalpha.com/input/?i=plot+y=1/(1%2B1/exp(4000*(1/(275%2Bx)-1/300))),+x=-50+to+150 Ausgangsspannung (U/U<sub>B</sub> über T)], der Heißleiter befindet sich einpolig auf Masse, daher nimmt die Spannung mit steigender Temperatur ab. Der Vorwiderstand ist hier genauso groß wie der Heißleiter-Widerstand bei 25 °C (Nennwiderstand). Am Wendepunkt der s-förmigen Kurve ist die Steilheit und damit die Auflösung am größten.<br />
* [http://www.wolframalpha.com/input/?i=plot+derive+y=1/(1%2B1/exp(4000*(1/(275%2Bx)-1/300))),+x=-50+to+150 Steilheit (<i>d</i>U/U<sub>B</sub> über T) = Ableitung], der Wert -0,01 bedeutet ≈ 0,1 K Auflösung bei 1024 A/D-Werten (eines 10-bit-A/D-Wandlers).<br />
<br />
==== Tipp zur Spreizung des Messbereichs ====<br />
<br />
Möchte man den ''genau arbeitenden'' Messbereich vergrößern, ist es am einfachsten, den Vorwiderstand ''umschaltbar'' zu machen!<br />
<br />
Ein per Portpin schaltbarer Widerstand ist ohnehin nötig, wenn die Widerstandsmessung bei einem batteriebetriebenen Aufnehmer nur kurzzeitig erfolgen soll. Um Querstrom zu vermeiden, ist für die jeweiligen niederohmigen Widerstände (sofern umschaltbar mehrere vorhanden) an einem Pin anzuschließen, dessen digitaler Einang abtrennbar ist. Bei manchen Controllern, etwa MSP430x2xx, ist man da eingeschränkt.<br />
<br />
Da beide A/D-Wandlungen ratiometrisch arbeiten, sind keinerlei Referenzspannungen erforderlich. Die Maßverkörperung (Vergleichsnormal) erfolgt mit dem Festwiderstand bzw. den Festwiderständen.<br />
<br />
=== Mehr Auflösung ===<br />
<br />
<i>… bedeutet nicht unbedingt mehr Genauigkeit!</i><br />
<br />
Hier gibt es prinzipiell drei Wege:<br />
* Einschränken des Wandlungsbereiches durch Vorverstärkung oder mittels Referenzspannungen: Geringerer Messumfang<br><i>Typische Anwendung: Fieberthermometer</i><br />
* Verwenden eines besseren (externen) A/D-Wandlers — wäre nur für Temperaturmessung Overkill<br />
* Verwenden eines anderen Wandlungsprinzips in Mikrocontroller-Software<br />
<br />
Recht gute Ergebnisse liefern Zweiflanken-A/D-Umsetzer. Diese erfordern jedoch einen OPV sowie einen Analogsignal-Umschalter. Diese sind selten in Mikrocontrollern bereits integriert.<br />
<br />
Die übliche Vorgehensweise ist ein Einflanken-Umsetzer, der mit dem zumeist vorhandenen Analog-Komparator arbeitet und wenig externe Beschaltung erfordert.<br />
<br />
==Siehe auch==<br />
*[[Sensorarten]]<br />
<br />
<br />
==Weblinks==<br />
[http://de.wikipedia.org/wiki/Kaltleiter Wikipedia: Kaltleiter]<br />
<br />
[http://www.maxim-ic.com/app-notes/index.mvp/id/3450 Schaltung für PT100 mit Linearisierung]<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Sensoren]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&diff=23746Überlegungen zur Drehgeber-Auswertung2014-03-26T21:06:38Z<p>Besserwessi: /* INT0/1 oder Polling ? */</p>
<hr />
<div>=Drehgeber-Auswertung=<br />
Es gibt in RN-Wissen schon einige (sehr gute) Artikel über Drehgeber und Auswertungsvarianten. Denen will ich hier keine Konkurrenz machen.<br/> <br />
[[Sensorarten#Incremental-Geber|Technisches]] findet man hier, [[Beispiel_Drehzahlmessung_mit_Drehgeber#Richtung.2C_Geschwindigkeit_und_Position_mit_Doppellichtschranke_ermitteln|Grundsätzliches]] über das Quadratur-Signal da, und [[Drehencoder|Programm-Beispiele]] für Drehencoder gibt es auch<br/><br />
Ich möchte dem einige grundsätzliche Überlegungen zur Drehgeber-Auswertung hinzufügen, damit es einem Einsteiger leichter fällt, eine Auswahl für seine konkreten Anforderungen zu treffen. <br />
<br />
==Signalfolge==<br />
Zur Erinnerung:<br />
[[Bild:quad_0.png|center]]<br />
===Auswertung der Flanken===<br />
Gerade bei den AVR's bietet es sich an, das A-Signal an einen "Externen Interrupt" anzuschließen. <br />
Da man meist auch zwei Drehgeber hat (für links u. rechts), kommt man da wunderbar mit INT0 u. INT1 aus. Allerdings zeigt sich, dass dieser Weg nicht ideal ist.<br />
====Vorwärts====<br />
Bei einer Konfiguration "steigende Flanke" wird bei den dicken Pfeilen der Interrupt ausgelöst. Liest man in der ISR den B-Kanal, kann man daraus direkt die Richtung entnehmen (im Beispiel ist das ebenfalls "1") und auf den Schrittzähler 1 addieren. <br />
[[Bild:quad_isr1.png|center]]<br />
====Richtungsumkehr====<br />
Wird nun die Drehrichtung geändert, wird die bis dahin fallende Flanke von A zur steigenden Flanke, und B ist zu diesem Zeitpunkt dann "0". Die Sache ist eindeutig, man kann nun von Schrittzähler eins abziehen. <br />
[[Bild:quad_isr2.png|center]]<br />
Dabei tritt aber natürlich eine Verschiebung auf. Wenn die Impulsfolge im Verhältnis zum zurückgelegten Weg gross ist, kann man das ignorieren, es geht ja letztlich meistens nur um ein paar Millimeter, und wo genau sich ein Drehgeber zwischen zwei Impulsen befindet, kann man ohnehin nie sagen. <br />
====Möglicher Fehler====<br />
Es gibt aber auch Situationen, wo es zu Fehlern kommt. Hier wird der zweite Interrupt noch ausgelöst und (richtig) als Schritt vorwärts gezählt. Dann bewegt sich der Geber etwas zurück, kommt aber nicht bis zu nächsten steigenden Flanke, sondern bewegt sich wieder nach vorne. <br />
[[Bild:quad_err.png|center]]<br />
Unsere Logik zählt nun einen Impuls zweimal, obwohl der drehgeber sich immer noch am selben Platz befindet.<br />
<br />
=== Auswertung der Übergänge===<br />
(Genaugenommen der Zustände unmittelbar danach)<br/><br />
Die korrekte Auswertung ist möglich, wenn man alle Übergänge steigend/fallend von beiden Kanälen (A u. B) bewertet. Das kann man ebenfalls mit ISR's machen, wenn man den µC so konfigurieren kann, dass er bei beiden Flankenarten auslöst. Da die Auswertung nicht nach der Art des Interrupts unterscheidet, kann dies auch eine gemeinsame ISR sein, etwa der Pin-Change Interrupt beim AVR. <br />
Um auch auf sehr kurze Pulse, wie sie etwa durch Störungen oder eine schnelle hin und her Bewegung entstehen können, darf man sich nicht auf die Auslösung des Interrupts stützen (da kann ggf. eine verloren gehen), sondern muss den Zustand (A und B) jeweils einmal auslesen und mit den alten (aus dem letzten Interrupt) vergleichen. <br />
Im Grunde ist das dann dasselbe, als wenn man ohne ISR periodisch die Eingänge abtastet, was natürlich oft genug geschehen muss, damit nichts verlorengeht. Die Flanken selbst erkennt man an dem Unterschied AB / AB-alt.<br/> <br />
In beiden Fällen erfolgt dann die Auswertung an den strichlinierten senkrechten Linien. <br />
[[Bild:quad_states.png|center]] <br />
====Vorwärts====<br />
Von dem Mitte nach rechts gelesen bekommt also an den bezeichneten Stellen die Werte<br />
* 00 vorher: 01<br />
* 10 vorher: 00<br />
* 11 vorher: 10<br />
* 01 vorher: 11<br />
* usw<br />
====Rückwärts====<br />
Von dem Mitte nach links gelesen bekommt dann an den bezeichneten Stellen die Werte<br />
* 00 vorher: 10<br />
* 01 vorher: 00<br />
* 11 vorher: 01<br />
* 10 vorher: 11<br />
* usw<br />
====Störpulse====<br />
Bei Störpulsen kommt es in kurzer Zeit zu einem Schritt vor und zurück (oder andersherum). Wenn die beiden Schritte zu schnell kommen, wird ggf. nur einmal die Auswertung aufgerufen. Dabei ist dann der alte und neue Wert gleich - so dass kein Schritt erfolgt.<br />
<br />
===Programmtechnische Umsetzung===<br />
====Externer Interrupt oder Polling ?====<br />
Bei der Auswertung hat man die Wahl zwischen einem externen Interrupt, der auf jede Änderung reagiert (z.B. der Pin-Change Interrupt beim neueren AVR wie Mega88) oder dem regelmäßigen Polling des Zustandes, etwa in einer Timer ISR, oder weniger gut ggf. auch in der Schleife im Hauptprogramm. Der eigentliche Code zur Auswertung ist der gleiche - die Frage ist also nur noch wann er aufgerufen wird. <br />
<br />
Beim Polling wird eine häufige Abfrage benötigt, um keine Schritte zu verlieren. Auch wenn sich in den meisten Fällen nichts getan hat, wird dafür Rechenzeit benötigt. Die Belastung für den µC ist damit praktisch unabhängig vom Signal, abhängig von der maximalen erlaubten Geschwindigkeit.<br />
<br />
Bei der Interrupt-Lösung, erfolgt die Auswertung nur wenn wirklich ein Schritt erfolgt oder halt gerade eine Störung kommt. Hat man nur selten Störungen ist dies ein echter Vorteil, weil das Hauptprogramm nur selten unterbrochen wird. Bei dem seltenen Extremfall von starken Störungen, kann es aber passieren das die ISR sehr häufig aufgerufen wird und kaum noch Rechenzeit für da Hauptprogramm übrig bleibt. Wenn solche extremen Störungen möglich sind (etwa optische Geber ohne Hysterese), sind externe Interrupts generell nicht zu empfehlen. Bei schneller Bewegung und entsprechend vielen ISR-Aufrufen kann die Geschwindigkeit des Hauptprogramms geringer werden. Wartezeiten durch Warteschleifen sind dann nicht mehr zuverlässig. Oft wird aber nur ein sehr kleiner Anteil der Rechenleistung benötigt.<br />
<br />
Sofern man kleine Fehler tolerieren kann, etwa für eine Menüsteuerung oder Geschwindigkeitsmessung ohne Richtungswechsel, kann man natürlich auch die Auswertung über einen externen Interrupt wählen, der nur eine Flanke auswertet. Damit reduziert sich ggf. die Rechenzeit auf Kosten geringerer Auflösung und möglicher Schrittfehler.<br />
<br />
====Auswertung der Signale====<br />
Auch wenn bei der Benutzung von Interrupts schon eine Information da ist, welcher Kanal sich geändert hat (und ggf. auch in welche Richtung), sollte die Auswertung trotzdem wie beim Polling erfolgen: Die beiden Kanäle einmal Auslesen, und dann zusammen mit dem vorherigen Wert bestimmen ob es einen Schritt gab und in welche Richtung. So kann verhindert werden, dass durch zu schnelle Interrupts, z.B. durch kurzzeitige Störungen Schritte verloren gehen und Fehler entstehen. <br />
Sofern von Hand nötig, muss das Interruptflag vor dem Auslesen des Ports zurückgesetzt werden, so dass alle Änderungen nach dem Auslesen erfasst werden.<br />
<br />
* Bei der Auswertung nur einer Flanke (also z.B. steigende Flanke von Kanal A) ist die Sache einfach: Es erfolgt ein Schritt, die Richtung zeigt der andere Kanal. Allerdings lässt sich mit nur einer Flanke der oben beschrieben Fehler beim Pendeln um den Übergang nicht vermeiden, weil ein Schritt vor bzw. zurück nicht an der gleichen Stelle erkannt werden. <br />
* mit beiden Flanken eines Kanals ist die Sache auch nicht viel komplizierter und man kann Schrittfehler vermeiden: <br />
** A = B --> vorwärts<br />
** A <> B --> rückwärts<br />
* Bei den Übergängen des anderen Kanals dreht sich die Richtung um und die Auflösung wird verdoppelt. <br />
<br />
Die Auswertung der Übergänge kann man natürlich auf viele Arten machen. Je nach Sprache bietet sich <br />
* "Select/Case" (Bascom) oder "switch/case" (GCC) an. Als Beispiel das [[Bascom_Inside-Code#.28Quadratur-.29_ENCODER|Assembler-listing]] für die Bascom-ENCODER()-Funktion<br/><br />
* IF THEN ELSE oder in C auch die bedingte Berechnung mit dem ? Operator an.<br />
* Eine andere Möglichkeit ist es, die neuen und die vorhergegangenen AB Werte zusammenzufassen und als Index für eine Werte- oder Sprungtabelle mit 16 Werten zu verwenden. In einer solche Tabelle muss man allerdings auch die ungültigen AB Kombinationen belegen, damit das funktioniert.<br />
<br />
==Autoren==<br />
* [[Benutzer:PicNick|PicNick]]<br />
<br />
==Siehe auch==<br />
* [[Sensoren]]<br />
* [[Sensorarten]]<br />
<br />
[[Category:Robotikeinstieg]]<br />
[[Category:Grundlagen]]<br />
[[Category:Sensoren]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&diff=23745Überlegungen zur Drehgeber-Auswertung2014-03-26T20:22:07Z<p>Besserwessi: /* Auswertung der Flanken */</p>
<hr />
<div>=Drehgeber-Auswertung=<br />
Es gibt in RN-Wissen schon einige (sehr gute) Artikel über Drehgeber und Auswertungsvarianten. Denen will ich hier keine Konkurrenz machen.<br/> <br />
[[Sensorarten#Incremental-Geber|Technisches]] findet man hier, [[Beispiel_Drehzahlmessung_mit_Drehgeber#Richtung.2C_Geschwindigkeit_und_Position_mit_Doppellichtschranke_ermitteln|Grundsätzliches]] über das Quadratur-Signal da, und [[Drehencoder|Programm-Beispiele]] für Drehencoder gibt es auch<br/><br />
Ich möchte dem einige grundsätzliche Überlegungen zur Drehgeber-Auswertung hinzufügen, damit es einem Einsteiger leichter fällt, eine Auswahl für seine konkreten Anforderungen zu treffen. <br />
<br />
==Signalfolge==<br />
Zur Erinnerung:<br />
[[Bild:quad_0.png|center]]<br />
===Auswertung der Flanken===<br />
Gerade bei den AVR's bietet es sich an, das A-Signal an einen "Externen Interrupt" anzuschließen. <br />
Da man meist auch zwei Drehgeber hat (für links u. rechts), kommt man da wunderbar mit INT0 u. INT1 aus. Allerdings zeigt sich, dass dieser Weg nicht ideal ist.<br />
====Vorwärts====<br />
Bei einer Konfiguration "steigende Flanke" wird bei den dicken Pfeilen der Interrupt ausgelöst. Liest man in der ISR den B-Kanal, kann man daraus direkt die Richtung entnehmen (im Beispiel ist das ebenfalls "1") und auf den Schrittzähler 1 addieren. <br />
[[Bild:quad_isr1.png|center]]<br />
====Richtungsumkehr====<br />
Wird nun die Drehrichtung geändert, wird die bis dahin fallende Flanke von A zur steigenden Flanke, und B ist zu diesem Zeitpunkt dann "0". Die Sache ist eindeutig, man kann nun von Schrittzähler eins abziehen. <br />
[[Bild:quad_isr2.png|center]]<br />
Dabei tritt aber natürlich eine Verschiebung auf. Wenn die Impulsfolge im Verhältnis zum zurückgelegten Weg gross ist, kann man das ignorieren, es geht ja letztlich meistens nur um ein paar Millimeter, und wo genau sich ein Drehgeber zwischen zwei Impulsen befindet, kann man ohnehin nie sagen. <br />
====Möglicher Fehler====<br />
Es gibt aber auch Situationen, wo es zu Fehlern kommt. Hier wird der zweite Interrupt noch ausgelöst und (richtig) als Schritt vorwärts gezählt. Dann bewegt sich der Geber etwas zurück, kommt aber nicht bis zu nächsten steigenden Flanke, sondern bewegt sich wieder nach vorne. <br />
[[Bild:quad_err.png|center]]<br />
Unsere Logik zählt nun einen Impuls zweimal, obwohl der drehgeber sich immer noch am selben Platz befindet.<br />
<br />
=== Auswertung der Übergänge===<br />
(Genaugenommen der Zustände unmittelbar danach)<br/><br />
Die korrekte Auswertung ist möglich, wenn man alle Übergänge steigend/fallend von beiden Kanälen (A u. B) bewertet. Das kann man ebenfalls mit ISR's machen, wenn man den µC so konfigurieren kann, dass er bei beiden Flankenarten auslöst. Da die Auswertung nicht nach der Art des Interrupts unterscheidet, kann dies auch eine gemeinsame ISR sein, etwa der Pin-Change Interrupt beim AVR. <br />
Um auch auf sehr kurze Pulse, wie sie etwa durch Störungen oder eine schnelle hin und her Bewegung entstehen können, darf man sich nicht auf die Auslösung des Interrupts stützen (da kann ggf. eine verloren gehen), sondern muss den Zustand (A und B) jeweils einmal auslesen und mit den alten (aus dem letzten Interrupt) vergleichen. <br />
Im Grunde ist das dann dasselbe, als wenn man ohne ISR periodisch die Eingänge abtastet, was natürlich oft genug geschehen muss, damit nichts verlorengeht. Die Flanken selbst erkennt man an dem Unterschied AB / AB-alt.<br/> <br />
In beiden Fällen erfolgt dann die Auswertung an den strichlinierten senkrechten Linien. <br />
[[Bild:quad_states.png|center]] <br />
====Vorwärts====<br />
Von dem Mitte nach rechts gelesen bekommt also an den bezeichneten Stellen die Werte<br />
* 00 vorher: 01<br />
* 10 vorher: 00<br />
* 11 vorher: 10<br />
* 01 vorher: 11<br />
* usw<br />
====Rückwärts====<br />
Von dem Mitte nach links gelesen bekommt dann an den bezeichneten Stellen die Werte<br />
* 00 vorher: 10<br />
* 01 vorher: 00<br />
* 11 vorher: 01<br />
* 10 vorher: 11<br />
* usw<br />
====Störpulse====<br />
Bei Störpulsen kommt es in kurzer Zeit zu einem Schritt vor und zurück (oder andersherum). Wenn die beiden Schritte zu schnell kommen, wird ggf. nur einmal die Auswertung aufgerufen. Dabei ist dann der alte und neue Wert gleich - so dass kein Schritt erfolgt.<br />
<br />
===Programmtechnische Umsetzung===<br />
====INT0/1 oder Polling ?====<br />
* Wie schon erwähnt, bei AVR's bieten sich die beiden "external interrrupts" geradezu an. Bei vielen fertigen Boards werden die notwendigen Leitungen schon für einen Anschluss herausgeführt. Bei neuere AVR µCs (z.B. Mega88) bietet sich der Pin-Change Interrupt an, der an allen Ports verfügbar ist. Die Hauptschleife (DO...LOOP) bzw. (while(true)) braucht sich dadurch nicht unbedingt um die Sache zu kümmern. Unpraktisch ist ein externer Interrupt, wenn es durch eine kleine Hysterese zu vielen Störungen kommen kann, wenn eine der Lichtschranken gerade an der Grenze ist. Man hat dann im ungünstigen Fall ein sehr hohe Interruptfrequenz und ggf. auftretende Schrittfehler werden zum Problem. <br/><br />
Bei Odometrie-Anwendungen gehen die oben erwähnten möglichen Schrittfehler meistens völlig in der Gesamt-Ungenauigkeit unter (schlupfende Räder etc.).<br/><br />
Hat man als Drehgeber ein Dateneingaberad ( z.B. für Menüs u. Parametereingaben) erfolgt ohnehin eine Sichtkontrolle der Position, da kommt es schon garnicht darauf an.<br />
<br />
* Die Sache liegt anders, wenn wirklich kein Tick falsch interpretiert werden soll, also z.B. bei Positionier-Anwendungen. Da ist es wohl besser, die Eingänge zu pollen und alle Übergänge auszuwerten.<br/><br />
Das kann man durchaus in der Hauptschleife machen, wenn dann die Abtastfrequenz ausreichend ist, man kann aber auch durch einen TIMER-Interrupt das Polling auslösen (mit dem gleichen Kriterium). <br />
<br />
* Ganz elegant ist natürlich eine Mischform: wenn man auch die Geschwindigkeit misst, kann man abhängig davon die Methode ändern, denn die genannten Schrittfehler werden bei höherem Tempo kaum auftreten, eher bei Stillstand, genauso, wie auch die Geschwindigkeitsmessung selbst wechseln kann zwischen "schritte in der Zeit" und "Zeit je Schritt".<br />
<br />
====Auswertung der Signale====<br />
Auch wenn bei der Benutzung von Interrupts schon eine Information da ist, welcher Kanal sich geändert hat (und ggf. auch in welche Richtung), sollte die Auswertung trotzdem wie beim Polling erfolgen: Die beiden Kanäle einmal Auslesen, und dann zusammen mit dem vorherigen Wert bestimmen ob es einen Schritt gab und in welche Richtung. So kann verhindert werden, dass durch zu schnelle Interrupts, z.B. durch kurzzeitige Störungen Schritte verloren gehen und Fehler entstehen. <br />
Sofern von Hand nötig, muss das Interruptflag vor dem Auslesen des Ports zurückgesetzt werden, so dass alle Änderungen nach dem Auslesen erfasst werden.<br />
<br />
* Bei der Auswertung nur einer Flanke (also z.B. steigende Flanke von Kanal A) ist die Sache einfach: Es erfolgt ein Schritt, die Richtung zeigt der andere Kanal. Allerdings lässt sich mit nur einer Flanke der oben beschrieben Fehler beim Pendeln um den Übergang nicht vermeiden, weil ein Schritt vor bzw. zurück nicht an der gleichen Stelle erkannt werden. <br />
* mit beiden Flanken eines Kanals ist die Sache auch nicht viel komplizierter und man kann Schrittfehler vermeiden: <br />
** A = B --> vorwärts<br />
** A <> B --> rückwärts<br />
* Bei den Übergängen des anderen Kanals dreht sich die Richtung um und die Auflösung wird verdoppelt. <br />
<br />
Die Auswertung der Übergänge kann man natürlich auf viele Arten machen. Je nach Sprache bietet sich <br />
* "Select/Case" (Bascom) oder "switch/case" (GCC) an. Als Beispiel das [[Bascom_Inside-Code#.28Quadratur-.29_ENCODER|Assembler-listing]] für die Bascom-ENCODER()-Funktion<br/><br />
* IF THEN ELSE oder in C auch die bedingte Berechnung mit dem ? Operator an.<br />
* Eine andere Möglichkeit ist es, die neuen und die vorhergegangenen AB Werte zusammenzufassen und als Index für eine Werte- oder Sprungtabelle mit 16 Werten zu verwenden. In einer solche Tabelle muss man allerdings auch die ungültigen AB Kombinationen belegen, damit das funktioniert.<br />
<br />
==Autoren==<br />
* [[Benutzer:PicNick|PicNick]]<br />
<br />
==Siehe auch==<br />
* [[Sensoren]]<br />
* [[Sensorarten]]<br />
<br />
[[Category:Robotikeinstieg]]<br />
[[Category:Grundlagen]]<br />
[[Category:Sensoren]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&diff=23744Überlegungen zur Drehgeber-Auswertung2014-03-26T20:18:14Z<p>Besserwessi: /* Auswertung der Übergänge */</p>
<hr />
<div>=Drehgeber-Auswertung=<br />
Es gibt in RN-Wissen schon einige (sehr gute) Artikel über Drehgeber und Auswertungsvarianten. Denen will ich hier keine Konkurrenz machen.<br/> <br />
[[Sensorarten#Incremental-Geber|Technisches]] findet man hier, [[Beispiel_Drehzahlmessung_mit_Drehgeber#Richtung.2C_Geschwindigkeit_und_Position_mit_Doppellichtschranke_ermitteln|Grundsätzliches]] über das Quadratur-Signal da, und [[Drehencoder|Programm-Beispiele]] für Drehencoder gibt es auch<br/><br />
Ich möchte dem einige grundsätzliche Überlegungen zur Drehgeber-Auswertung hinzufügen, damit es einem Einsteiger leichter fällt, eine Auswahl für seine konkreten Anforderungen zu treffen. <br />
<br />
==Signalfolge==<br />
Zur Erinnerung:<br />
[[Bild:quad_0.png|center]]<br />
===Auswertung der Flanken===<br />
Gerade bei den AVR's bietet es sich an, das A-Signal an einen "Externen Interrupt" anzuschliessen. <br />
Da man meist auch zwei Drehgeber hat (für links u. rechts), kommt man da wunderbar mit INT0 u. INT1 aus. <br />
====Vorwärts====<br />
Bei einer Konfiguration "steigende Flanke" wird bei den dicken Pfeilen der Interrupt ausgelöst. Liest man in der ISR den B-Kanal, kann man daraus direkt die Richtung entnehmen (im Beispiel ist das ebenfalls "1") und auf den Schrittzähler 1 addieren. <br />
[[Bild:quad_isr1.png|center]]<br />
====Richtungsumkehr====<br />
Wird nun die Drehrichtung geändert, wird die bis dahin fallende Flanke von A zur steigenden Flanke, und B ist zu diesem Zeitpunkt dann "0". Die Sache ist eindeutig, man kann nun von Schrittzähler eins abziehen. <br />
[[Bild:quad_isr2.png|center]]<br />
Dabei tritt aber natürlich eine Verschiebung auf. Wenn die Impulsfolge im Verhältnis zum zurückgelegten Weg gross ist, kann man das ignorieren, es geht ja letztlich meistens nur um ein paar Millimeter, und wo genau sich ein Drehgeber zwischen zwei Impulsen befindet, kann man ohnehin nie sagen. <br />
====Möglicher Fehler====<br />
Es gibt aber auch Situationen, wo es zu Fehlern kommt. Hier wird der zweite Interrupt noch ausgelöst und (richtig) als Schritt vorwärts gezählt. Dann bewegt sich der Geber etwas zurück, kommt aber nicht bis zu nächsten steigenden Flanke, sondern bewegt sich wieder nach vorne. <br />
[[Bild:quad_err.png|center]]<br />
Unsere Logik zählt nun einen Impuls zweimal, obwohl der drehgeber sich immer noch am selben Platz befindet.<br />
<br />
=== Auswertung der Übergänge===<br />
(Genaugenommen der Zustände unmittelbar danach)<br/><br />
Die korrekte Auswertung ist möglich, wenn man alle Übergänge steigend/fallend von beiden Kanälen (A u. B) bewertet. Das kann man ebenfalls mit ISR's machen, wenn man den µC so konfigurieren kann, dass er bei beiden Flankenarten auslöst. Da die Auswertung nicht nach der Art des Interrupts unterscheidet, kann dies auch eine gemeinsame ISR sein, etwa der Pin-Change Interrupt beim AVR. <br />
Um auch auf sehr kurze Pulse, wie sie etwa durch Störungen oder eine schnelle hin und her Bewegung entstehen können, darf man sich nicht auf die Auslösung des Interrupts stützen (da kann ggf. eine verloren gehen), sondern muss den Zustand (A und B) jeweils einmal auslesen und mit den alten (aus dem letzten Interrupt) vergleichen. <br />
Im Grunde ist das dann dasselbe, als wenn man ohne ISR periodisch die Eingänge abtastet, was natürlich oft genug geschehen muss, damit nichts verlorengeht. Die Flanken selbst erkennt man an dem Unterschied AB / AB-alt.<br/> <br />
In beiden Fällen erfolgt dann die Auswertung an den strichlinierten senkrechten Linien. <br />
[[Bild:quad_states.png|center]] <br />
====Vorwärts====<br />
Von dem Mitte nach rechts gelesen bekommt also an den bezeichneten Stellen die Werte<br />
* 00 vorher: 01<br />
* 10 vorher: 00<br />
* 11 vorher: 10<br />
* 01 vorher: 11<br />
* usw<br />
====Rückwärts====<br />
Von dem Mitte nach links gelesen bekommt dann an den bezeichneten Stellen die Werte<br />
* 00 vorher: 10<br />
* 01 vorher: 00<br />
* 11 vorher: 01<br />
* 10 vorher: 11<br />
* usw<br />
====Störpulse====<br />
Bei Störpulsen kommt es in kurzer Zeit zu einem Schritt vor und zurück (oder andersherum). Wenn die beiden Schritte zu schnell kommen, wird ggf. nur einmal die Auswertung aufgerufen. Dabei ist dann der alte und neue Wert gleich - so dass kein Schritt erfolgt.<br />
<br />
===Programmtechnische Umsetzung===<br />
====INT0/1 oder Polling ?====<br />
* Wie schon erwähnt, bei AVR's bieten sich die beiden "external interrrupts" geradezu an. Bei vielen fertigen Boards werden die notwendigen Leitungen schon für einen Anschluss herausgeführt. Bei neuere AVR µCs (z.B. Mega88) bietet sich der Pin-Change Interrupt an, der an allen Ports verfügbar ist. Die Hauptschleife (DO...LOOP) bzw. (while(true)) braucht sich dadurch nicht unbedingt um die Sache zu kümmern. Unpraktisch ist ein externer Interrupt, wenn es durch eine kleine Hysterese zu vielen Störungen kommen kann, wenn eine der Lichtschranken gerade an der Grenze ist. Man hat dann im ungünstigen Fall ein sehr hohe Interruptfrequenz und ggf. auftretende Schrittfehler werden zum Problem. <br/><br />
Bei Odometrie-Anwendungen gehen die oben erwähnten möglichen Schrittfehler meistens völlig in der Gesamt-Ungenauigkeit unter (schlupfende Räder etc.).<br/><br />
Hat man als Drehgeber ein Dateneingaberad ( z.B. für Menüs u. Parametereingaben) erfolgt ohnehin eine Sichtkontrolle der Position, da kommt es schon garnicht darauf an.<br />
<br />
* Die Sache liegt anders, wenn wirklich kein Tick falsch interpretiert werden soll, also z.B. bei Positionier-Anwendungen. Da ist es wohl besser, die Eingänge zu pollen und alle Übergänge auszuwerten.<br/><br />
Das kann man durchaus in der Hauptschleife machen, wenn dann die Abtastfrequenz ausreichend ist, man kann aber auch durch einen TIMER-Interrupt das Polling auslösen (mit dem gleichen Kriterium). <br />
<br />
* Ganz elegant ist natürlich eine Mischform: wenn man auch die Geschwindigkeit misst, kann man abhängig davon die Methode ändern, denn die genannten Schrittfehler werden bei höherem Tempo kaum auftreten, eher bei Stillstand, genauso, wie auch die Geschwindigkeitsmessung selbst wechseln kann zwischen "schritte in der Zeit" und "Zeit je Schritt".<br />
<br />
====Auswertung der Signale====<br />
Auch wenn bei der Benutzung von Interrupts schon eine Information da ist, welcher Kanal sich geändert hat (und ggf. auch in welche Richtung), sollte die Auswertung trotzdem wie beim Polling erfolgen: Die beiden Kanäle einmal Auslesen, und dann zusammen mit dem vorherigen Wert bestimmen ob es einen Schritt gab und in welche Richtung. So kann verhindert werden, dass durch zu schnelle Interrupts, z.B. durch kurzzeitige Störungen Schritte verloren gehen und Fehler entstehen. <br />
Sofern von Hand nötig, muss das Interruptflag vor dem Auslesen des Ports zurückgesetzt werden, so dass alle Änderungen nach dem Auslesen erfasst werden.<br />
<br />
* Bei der Auswertung nur einer Flanke (also z.B. steigende Flanke von Kanal A) ist die Sache einfach: Es erfolgt ein Schritt, die Richtung zeigt der andere Kanal. Allerdings lässt sich mit nur einer Flanke der oben beschrieben Fehler beim Pendeln um den Übergang nicht vermeiden, weil ein Schritt vor bzw. zurück nicht an der gleichen Stelle erkannt werden. <br />
* mit beiden Flanken eines Kanals ist die Sache auch nicht viel komplizierter und man kann Schrittfehler vermeiden: <br />
** A = B --> vorwärts<br />
** A <> B --> rückwärts<br />
* Bei den Übergängen des anderen Kanals dreht sich die Richtung um und die Auflösung wird verdoppelt. <br />
<br />
Die Auswertung der Übergänge kann man natürlich auf viele Arten machen. Je nach Sprache bietet sich <br />
* "Select/Case" (Bascom) oder "switch/case" (GCC) an. Als Beispiel das [[Bascom_Inside-Code#.28Quadratur-.29_ENCODER|Assembler-listing]] für die Bascom-ENCODER()-Funktion<br/><br />
* IF THEN ELSE oder in C auch die bedingte Berechnung mit dem ? Operator an.<br />
* Eine andere Möglichkeit ist es, die neuen und die vorhergegangenen AB Werte zusammenzufassen und als Index für eine Werte- oder Sprungtabelle mit 16 Werten zu verwenden. In einer solche Tabelle muss man allerdings auch die ungültigen AB Kombinationen belegen, damit das funktioniert.<br />
<br />
==Autoren==<br />
* [[Benutzer:PicNick|PicNick]]<br />
<br />
==Siehe auch==<br />
* [[Sensoren]]<br />
* [[Sensorarten]]<br />
<br />
[[Category:Robotikeinstieg]]<br />
[[Category:Grundlagen]]<br />
[[Category:Sensoren]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Timer/Counter_(Avr)&diff=23606Timer/Counter (Avr)2014-02-28T13:03:20Z<p>Besserwessi: /* Normaler Modus (Normal Mode) */ Hinweis auf ISR() statt Signal()</p>
<hr />
<div>Die Mikrocontroller der AVR-Familie besitzen je nach Typ eine unterschiedliche Anzahl an programmierbaren [[Timer|Timern]]. Bei den aktuellen ATmegas sind das mindestens ein 8-Bit Timer und ein 16-Bit Timer. Die Timer werden immer Timerx benannt, wobei x für die Timernummer steht (also 0, 1, 2, usw.). <br />
Die Konfigurationsmöglichkeiten sind von Timer zu Timer unterschiedlich.<br />
<br />
'''Hinweis:''' Die folgenden Code-Beispiele sind in C programmiert und wurden für einen [[ATMega32|ATmega32]] entwickelt. Sie lassen sich also ohne große Änderungen auch auf anderen Mikrocontrollern der AVR-Familie einsetzen. Allerdings hat Atmel bei den neueren µCs (etwa Mega88, Mega324 und fast alle der aktuellen Tiny) die Namen für die Register vielfach geändert. <br />
<br />
== Allgemeine Funktionsweise ==<br />
Timer funktionieren nach dem allgemeinen Prinzip, dass sie eine Ganzzahl (im weiteren als Zähler bezeichnet) je nach Betriebsmodus auf- oder abwärtszählen, d.h. inkrementieren bzw. dekrementieren.<br />
<br />
Angenommen, der Timer arbeitet im einfachsten Betriebsmodus, dem [[Timer/Counter (Avr)#Normaler Modus (Normal Mode)|Normalen Modus]]. Die Zählrichtung des Timers ist aufsteigend gerichtet. Je nach Auflösung, also 8-Bit oder 16-Bit, folgt auf den maximalen Zählerstand wieder die Null. Wenn z.B. bei einem 8-Bit Timer der Wert 255 inkrementiert wird folgt die Null (siehe Grafik).<br />
<br />
[[Bild:AbstrakterZaehlvorgang.png]]<br />
<br />
== Der Prescaler ==<br />
Der Prescaler (eng. = Vorteiler) kann der Takt für den Timer herunter geteilt werden. Oft hat man Faktoren von 1, 8, 64 ,256 oder 1024 zur Auswahl. Über das selbe Register kann der Timer auch ganz angehalten werden oder ein externer Takt ausgewählt werden. Ein externer Takt darf dabei höchstens halb so hoch wie der Prozezessortakt sein.<br />
Hier eine Grafik die den Prescaler veranschaulicht:<br />
<br />
[[Bild:Prescaler.png]]<br />
<br />
Das obere Diagramm zeigt den Betrieb ohne Prescaler, das untere mit Prescaler (:2). Die gestrichelte Linie zeigt, wann der Timer weiterzählt.<br />
<br />
Im Teil [[Timer/Counter (Avr)#Die Betriebsmodi|Die Betriebsmodi]] wird weiter auf die praktische Verwendung des Prescalers eingegangen.<br />
<br />
== Die Betriebsmodi ==<br />
Die AVR-Timer können in unterschiedlichen Betriebsmodi betrieben werden. Diese sind:<br />
* Normaler Modus<br />
* CTC Modus<br />
* PWM<br />
<br />
=== Normaler Modus (Normal Mode) ===<br />
Der einfachste Betriebsmodus ist der normale Modus. Er funktioniert wie im Abschnitt "[[Timer/Counter (Avr)#Allgemeine Funktionsweise|Allgemeine Funktionsweise]]" beschrieben. Die Zählrichtung des Timers ist immer aufsteigend, bis zum Überlauf - da fängt der Zähler wieder bei 0 an. Der Überlauf kann einen Interrupt (Timer-Overflow) auslösen. Im einfachsten Fall kann dieser Modus im folgendem Diagramm dargestellt werden:<br />
<br />
[[Bild:NormalerModus_1.png]]<br />
<br />
Der Zähler des Timers (im Diagramm oben, die aufsteigende und dann wieder zurückgesetzte Linie) ist in dem Register TCNTx gespeichert, wobei x für eine Zahl steht. Soll z.B. auf den Timer0 (siehe Datenblatt des jeweiligen Controllers) des Controllers zugegriffen werden, so ist an TCNT eine 0 anzuhängen, also TCNT0.<br />
Wie lange es braucht, bis der Zähler einen Overflow auslöst, ist von der Taktfrequenz des Controllers, dem eingestellten Prescaler-Wert und von der Timerauflösung abhängig. Nun wäre es ja sehr unpraktisch, wenn wir den Zähler nicht anpassen könnten. Denn sonst müssten wir unsere Software, die den Timer benutzt, evtl. anpassen und viel rechnen, um z.B. für 1000 ms zu schlafen. Deswegen kann auf den Zähler zugegriffen werden und ihn vorladen, bevor dieser wieder vom eigentlichen Timer hochgezählt wird. Dies veranschaulicht folgendes Diagramm:<br />
<br />
[[Bild:NormalerModus_1_Vorladen.png]]<br />
<br />
Dadurch kann eingestellt werden, wie lange es dauert, bis ein Overflow auftritt. Um zu berechnen, welchen Wert wir vorladen müssen, kann auch ein Java-Applet genutzt werden, siehe unter [[Timer/Counter (Avr)#Weblinks|Weblinks Java Applet]].<br />
<br />
Natürlich kann das auch "von Hand" berechnet werden. Die Berechnung des Preloader- sowie Prescalerwerts bei Verwendung der Overflow-Interrupts, eines Prescalers von 64 (nicht alle Prescaler können verwendet werden) und eines Quarzes mit der Frequenz von 8 MHz sieht folgendermaßen aus (gesuchte Frequenz beträgt 1000 Hz unter der Verwendung des Timer0 eines ATmega32):<br />
# <math>Prescale = Frequenz * 1000000 [Hz] = 8000000</math><br />
# Wir definieren den maximalen Zählerwert. Dieser ist bei einem 8-Bit Timer 256, bei einem 16-Bit Timer 65536. In unserem Fall ist der maximale Zählerwert 256, weil Timer0 verwendet wird.<br />
# Nun wird die Variable ''Prescale'' (s.o.) durch den verwendeten Prescaler (64) geteilt (<math>8000000 Hz / 64 = 125000</math>).<br />
# Als nächstes wird der im dritten Punkt errechnete Wert durch die gesuchte Frequenz geteilt <math>=125000 / 1000Hz = 125</math>.<br />
# Nun wird mathematisch überprüft, ob der errechnete Wert aus dem vierten Punkt kleiner als der maximale Zählerwert ist. Trifft dies zu, so wird der errechneten Wert vom maximalen Zählerwert subtrahiert(<math>= 256 - 125 = 131</math>).<br />
<br />
Damit haben wir den Wert errechnet, der bei jedem Interrupt, den der Timer0 auslöst, in TCNTx (in diesem Fall TCNT0) nachgeladen werden muss, damit die Interrupts in dem gewünschten Zeitabstand von einer Millisekunde ausgelöst werden.<br />
<br />
Zwischen dem Timer Overflow und dem tatsächlichen Aufrufen der ISR mit dem Nachladen des Timers ergibt sich eine kleine Verzögerung, die nicht einmal immer gleich ist. Bei einem genügend großen Prescaler (z.B. 64) kommt durch die Verzögerung kein zusätzlicher Timerschritt zustande, und auch die Methode mit dem Nachladen liefert exacte Ergebnisse. Bei kleinen Prescalern kommt es durch die Verzögerung zu längeren und nicht immer gleichen Zeitabständen. Wenn möglich wird für die Erzeugung einer konstanten Interruptrate deshalb besser der CTC Moduls benutzt.<br />
<br />
Zusammenfassend ein Beispielprogramm:<br />
<br />
<pre><br />
/* Es wird der Timer2 (8-Bit) eines ATmega32 verwendet, der mit einem Quarz mit 7,3728 MHz<br />
betrieben wird. Im Abstand von etwa 0,1 ms erzeugt der Timer einen Interrupt, also eine<br />
Frequenz von 10000 Hz. Der Timer wird auf einen Prescaler von 64 und<br />
einem Preloader von 244 konfiguriert.*/<br />
<br />
volatile uint8_t countTimer2; // Speichert den aktuellen Zählerwert<br />
<br />
// ISR zum auffangen der Interrupts:<br />
SIGNAL(SIG_SIG_OVERFLOW2) // alter Form, für neuere GCC Versionen: ISR(TIMER2_OVF_vect)<br />
{<br />
TCNT2 = 244; // Nachladen<br />
countTimer2++;<br />
}<br />
<br />
// Initialisierung:<br />
TCCR2 = (1<<CS22); // Prescaler von 64 und damit Timer starten<br />
TCNT2 = 244; // Vorladen<br />
TIMSK |= (1<<TOIE2); // Interrupts aktivieren <br />
sei();<br />
<br />
// Funktionen zum benutzen der Timer:<br />
/** Diese Funktion nicht aufrufen. Wird von sleep_millisec aufgerufen.<br />
Bei t=10 schläft die Funktion 1 ms. */<br />
inline void sleep (uint8_t t)<br />
{<br />
// countTimer2 wird in der ISR oben inkrementiert<br />
countTimer2 = 0; // 1 Byte Typ, daher kein cli()... sei() nötig<br />
while (countTimer2 < t);<br />
}<br />
<br />
/** Schläft x-Millisekunden. */<br />
inline void sleep_millisec(uint16_t msec)<br />
{<br />
uint16_t i;<br />
for(i=0; i<msec; i++) {<br />
sleep(10);<br />
}<br />
}<br />
</pre><br />
<br />
Dieses Beispiel zeigt nicht unbedingt eine vorbildliche Nutzung des Timers. Eine ISR einfach nur zum schnellen Hochzählen der Zeit verbraucht recht viel Rechenzeit und sollte sonst eher vermieden werden. Das Hochzählen der "Zeit" ist eigentlich genau das, was der Timer in Hardware macht - nur halt nicht immer in geraden Zeitschritte, wie 0,1 ms, sondern halt in den Schritten, die der Prescaler vorgibt (z.B. 256 Takte). Die Umrechnung kann man aber gut auch bei der Wartezeit vorher, oder bei einer gemessenen Zeit nachher machen. <br />
<br />
==== Input Capture ====<br />
<br />
Die 16 Bit Timer haben eine "Input Capture" Funktion. Dieser Hardwareteil dient zur genauen Zeitmessung. Die typische Anwendung ist die Messung von kurze Zeiten, wie z.B. die Zeit für eine Motorumdrehung. Außer in einigen PWM Betriebsarten, wo das ICP Register als TOP-wert für den Timer benutzt wird, ist die ICP-Funktion immer aktiv. Wenn am ICP Pin die über das Bit "ICESx" eingestellte Flanke auftritt, wird der aktuelle Zählerstand in das ICP Register kopiert. Außerdem kann ein Interrupt ausgelöst werden. Der Interrupt wird, wie die anderen Timer Interrupts, in den Registern TIMSK und TIFR an- oder abgestellt. Man kann zwar die ICP-Funktion selber nicht ohne weiteres abschalten, aber natürlich den dazugehörigen Interrupt. Als eine spezielle Funktion ("Noise Cancler") gibt es die Möglichkeit sehr kurze Pulse (unter 4 Zyklen) zu unterdrücken. In der Regel kann man diese Funktion angestellt lassen, denn so schnell kann man die Daten ohnehin nicht verarbeiten.<br />
<br />
Solange die 16 Bit des Timers ausreichen ist die Benutzung ganz einfach: Der Timer wird mit dem gewünschten Vorteiler im normalen Modus gestartet. Im ICP-Interrupt wird die Differenz aus zwei aufeinanderfolgenden Zeiten (Werte in ICP-Register) berechnet. Dazu wird jeweils die vorherige Zeit im RAM zwischengespeichert. Wenn man bei der Rechnung (vorzeichenlose 16 Bit Zahlen) eventuelle Überläufe ignoriert, bekommt man die richtige Zeitdifferenz, auch wenn der Timer während der Messzeit einen Überlauf hatte. Das funktioniert so einfach, denn wenn noch weitere (höherwertige) Bytes vorhanden wären, damit es keinen Überlauf gibt, würde man genau so die unteren Bits berechnen.<br />
<br />
Etwas komplizierter wird es, wenn die 16 Bit Auflösung nicht mehr ausreicht. Dann kann der Timer-Überlauf benutzt werden, um auch längere Zeiten mit voller Auflösung zu messen. Die wesentliche Schwierigkeit ist es, den Fall zu berücksichtigen, dass ein Überlauf Interrupt und der ICP Interrupt fast gleichzeitig ausgelöst werden. Es kann passieren, dass der ICP-Interrupt aufgerufen wird, obwohl eigentlich erst der Overflow Interrupt dran gewesen wäre. Dieser seltene Fall lässt sich daran erkennen, dass das Overflow-Interrupt Flag gesetzt ist und der Wert im ICP Register klein ist (high Byte < 128, meistens 0).<br />
<br />
Beispielpropgramm (für GCC):<br />
(Bisher nur im Simulator getestet)<br />
<pre><br />
// Beispielprogramm für Zeitmessung mit ICP-Funktion<br />
// Erweiterung des Timers auf 32 Bit durch Software<br />
// Es wird die Periodendauer am ICP-Eingang gemessen und als ASCII via UART ausgegeben<br />
// Code für Mega48 / Mega88 / Mega 168 / ...<br />
// mit leichten Anpassungen auch für Tiny2313, Mega16, Mega32,...<br />
<br />
#include <stdlib.h> // für utoa<br />
#include <avr/io.h><br />
#include <avr/interrupt.h><br />
#include <avr/sleep.h> // Unterstützung für sleep mode<br />
<br />
#define F_CPU 1000000UL // Definition der Frequenz, ist ggf. im makefile<br />
#define BAUD 19200UL<br />
#define UBRR_BAUD ((F_CPU/(16UL*BAUD))-1)<br />
<br />
typedef union { // union erlaubt einen effektiven, separaten Zugriff auf Teile der Variable<br />
unsigned long i32;<br />
struct {uint8_t i8l; // low<br />
uint8_t i8m; // mid<br />
unsigned int high; // high, soft timer <br />
};<br />
} convert32to8;<br />
<br />
volatile unsigned long timestamp; // volatile wegen Zugriff im Interrupt<br />
volatile unsigned int softtimer;<br />
volatile unsigned long zeitdifferenz;<br />
unsigned long zeit;<br />
char puffer[12]; // Puffer für Ausgabe als Ascii<br />
<br />
ISR(TIMER1_OVF_vect) // Timer1 Überlauf<br />
{ <br />
++softtimer; // zählen der Überläufe<br />
} <br />
<br />
ISR(TIMER1_CAPT_vect) // Flanke an ICP pin<br />
{ <br />
convert32to8 cap; // Variablendeklaration<br />
<br />
cap.i8l = ICR1L; // low Byte zuerst, high Byte wird gepuffert<br />
cap.i8m = ICR1H; <br />
// overflow verpasst, wenn ICR1H klein und wartender Overflow Interrupt<br />
if ((cap.i8m < 128) && (TIFR1 & (1<<TOV1)))<br />
{ // wartenden timer overflow Interrupt vorziehen<br />
++softtimer; <br />
TIFR1 = (1<<TOV1); // timer overflow int. löschen, da schon hier ausgeführt<br />
}<br />
cap.high = softtimer; // obere 16 Bit aus Software Zähler<br />
zeitdifferenz = cap.i32 - timestamp;<br />
timestamp = cap.i32; // Zeit merken<br />
}<br />
<br />
void uart_init(void) // USART initialisieren (Mega48 etc.)<br />
{<br />
// Baudrate einstellen (Normaler Modus)<br />
// kann bei älteren AVR Typen etwas anders sein (kein UBRR0H, dafür prescaler)<br />
UBRR0H = (uint8_t) (UBRR_BAUD>>8); // bei Mega32 anders !<br />
UBRR0L = (uint8_t) (UBRR_BAUD & 0x0ff); // bei Mega32 UBRRL<br />
UCSR0B = (1<<TXEN0); // Aktivieren des Senders, bei Mega32 UCSRB<br />
UCSR0C = (1<<UCSZ01)|(1<<UCSZ00)|(1<<USBS0); // bei Mega32 UCSRC<br />
// Einstellen des Datenformats: 8 Datenbits, 2 Stopbit:<br />
}<br />
<br />
void putser(char c) // sende ein Byte via UART<br />
{<br />
while ( !( UCSR0A & (1<<UDRE0)) ) ; // Warten bis der Sendepuffer frei ist<br />
UDR0 = c;<br />
}<br />
<br />
void main(void)<br />
{<br />
unsigned char i;<br />
// Datenrichtungen:<br />
DDRB = 0; // Alles Eingänge, PB0 ist ICP<br />
PORTB = 0xFF - (1<<PB0); // Pullups an Eingängen außer ICP<br />
DDRC = 0; // Eingänge<br />
PORTC = 0xFF; // Pullups an Eingängen <br />
DDRD = (1<<PD1); // Eingänge, außer PD1 = Tx (UART)<br />
PORTD = 0xFF- (1<<PD1); // Pullups an alle Eingängen (außer TX)<br />
// Timer1 initialisieren:<br />
TCCR1A = 0; // normal mode, keine PWM Ausgänge<br />
TCCR1B = (1<< ICNC1) + (1<<CS10) // start Timer mit Systemtakt<br />
+ (1 << ICES1); // steigende Flanke auswählen<br />
TIMSK1 = (1<<TOIE1) + (1<<ICIE1); // overflow und Input-capture aktivieren, Mega32: TIMSK<br />
TIFR1 = (1<<TOIE1) + (1<<ICIE1); // Schon aktive Interrupts löschen, Mega32: TIFR<br />
// UART initialisieren:<br />
uart_init(); <br />
<br />
zeitdifferenz = 0;<br />
softtimer = 0; // wird für Zeitdifferenzmessung nicht mal gebraucht,<br />
// denn Differenz geht auch über Überlauf bei Softtimer<br />
set_sleep_mode (SLEEP_MODE_IDLE); // idle Mode: timer läuft weiter, int zum aufwachen<br />
<br />
sei(); // Interrupts erlauben: Messung startet<br />
while (1)<br />
{<br />
sleep_enable (); // Sleep Befehl freigeben <br />
sei();<br />
sleep_cpu(); // wartet auf irgendeinen Interrupt, z.B. ICP, timer_ovr,...<br />
sleep_disable(); // Sleep Befehl sperren<br />
cli(); // Interrupt sperren wegen Zugriff auf volatile Variable<br />
zeit = zeitdifferenz;<br />
zeitdifferenz = 0; // als Markierung für ungültigen Wert<br />
sei();<br />
if (zeit > 0)<br />
{ <br />
ultoa(zeit,puffer,10); // nach ASCII umwandeln<br />
i = 0;<br />
while (puffer[i])<br />
{<br />
putser(puffer[i++]); // Ausgabe<br />
}<br />
putser(13); putser(10); // Zeilenumbruch senden<br />
}<br />
} // Ende von While-schleife<br />
}<br />
</pre><br />
<br />
=== CTC Modus (Clear Timer on Compare Match mode) ===<br />
Viele Timer haben "Output-Compare" Register: OCRx oder OCRAx,OCRBx. Wenn der Zähler den darin eingestellten Wert erreicht hat, kann ein Interrupts ausgelöst werden. Der CTC Modus ist eine Erweiterung des "Output-Compare"-Funktion. Der CTC Modus eignet sich besonders, um einen mit konstanter Frequenz wiederkehrenden Interrupt zu erzeugen. Wie im normalen Modus zählt der Timer hoch. Wenn der Wert im OCRx Register erreicht wird, wird zusätzlich zum möglichen Interrupt der Zähler wieder auf 0 gesetzt. Es kann also die maximalen Zählergrenze selber definiert werden.<br />
Dieses Diagramm veranschaulicht den CTC Modus.<br />
<br />
[[Bild:NormalerModus_CompareMatch.png]]<br />
<br />
Beispielprogramm:<br />
<br />
<pre><br />
/* Es wird der Timer2 (8-Bit) eines ATmega32 verwendet, der mit einem Quarz <br />
mit 7,3728 MHz betrieben wird. Im Abstand von 0,01 ms erzeugt der Timer <br />
einen Interrupt, also eine Frequenz von 100000 Hz (oder 100 kHz). <br />
Der Timer wird auf einen Prescaler von 1 und einem OCR2-Wert von 73 konfiguriert. */<br />
<br />
volatile uint8_t countTimer2; // Speichert den aktuellen Zählerwert<br />
<br />
// ISR zum auffangen der Interrupts:<br />
SIGNAL(SIG_OUTPUT_COMPARE2)<br />
{<br />
countTimer2++;<br />
}<br />
<br />
// Initialisierung:<br />
TCCR2 = (1<<CS20) | (1<<WGM21); // Prescaler von 1 | CTC-Modus (siehe unten für Beschreibung)<br />
OCR2 = 73; // Vergleichswert<br />
TIMSK |= (1<<OCIE2); // Interrupts aktivieren und damit Timer starten<br />
sei();<br />
<br />
// Funktionen zum benutzen der Timer:<br />
/** Diese Funktion nicht aufrufen. Wird von sleep_millisec aufgerufen.<br />
Bei t=100 schläft die Funktion 1 ms. */<br />
inline void sleep(uint8_t t)<br />
{<br />
// countTimer2 wird in der ISR oben inkrementiert<br />
countTimer2 = 0;<br />
while (countTimer2 < t);<br />
}<br />
<br />
/** Schläft x-Millisekunden. */<br />
inline void sleep_millisec(uint16_t msec)<br />
{<br />
uint16_t i;<br />
for(i=0; i<msec; i++) {<br />
sleep(100);<br />
}<br />
}<br />
</pre><br />
<br />
----<br />
<br />
=== PWM ===<br />
<br />
Eine häufige Aufgabe für Mikrocontroller ist die Erzeugung von [[PWM]]-Signalen, zum Beispiel für Motorsteuerungen. Daher sind in den meisten [[AVR|AVRs]] PWM-Einheiten als Hardware vorhanden. Sie sind direkt mit den Timern verbunden und nutzen diese als Taktquelle. Die Hardware-PWM-Einheiten haben den Vorteil, sehr wenig Rechenzeit in Anspruch zu nehmen. Es muss nur die PWM aktiviert werden und bei Änderungen den gewünschten Wert in ein Register schreiben. Der Rest läuft automatisch und unabhängig vom restlichen Programm, ohne den AVR ständig zu beschäftigen wie bei einer PWM-Lösung in Software. Allerdings stehen meist nur zwei bis drei solcher PWM-Kanäle zur Verfügung, die außerdem an bestimmte Pins gebunden sind. Für die meisten Roboter mit zwei Antriebsmotoren reicht dies aber für gewöhnlich aus. <br />
<br />
==== nutzbare Pins am AVR ====<br />
Die Hardware-PWM-Funktion steht nur an bestimmten Pins zur Verfügung. In der Pinbelegungsübersicht im Datenblatt ist erkenntbar, dass als Sonderfunktion in Klammern "OC..." angegeben ist. Beim Mega32 sind dies zb. OC0 an PB3, OC1A an PD5, OC1B an PD4 und OC2 an PD7. Der Mega32 hat also insgesamt vier Hardware-PWM-Kanäle. Die Zahl hinter dem "OC" gibt an, zu welchem der Timer dieser PWM-Kanal gehört. Wenn noch ein Buchstabe dahinter kommt, dann gehören mehrere PWMs zu diesem Timer. Beim Mega32 sind also OC1A und OC1B demselben Timer, nämlich Timer1, zugeordnet.<br />
<br />
'''Zu beachten ist, dass die für die PWM benutzten Pins zuvor explizit als Ausgang konfiguriert werden müssen! Ansonsten gelangt das PWM-Signal nicht nach draußen!'''<br />
<br />
==== Funktionsprinzip ====<br />
Das "OC" in den Pinbezeichnungen steht für "Output Compare Unit", also frei übersetzt Ausgangs-Vergleicher-Einheit. Dies beschreibt die Funktionsweise der PWM-Kanäle: der Zählerstand des Timers wird fortlaufend mit einen einstellbaren Referenzwert verglichen, und wenn beide Werte übereinstimmen, kann ein Ausgangspin des AVRs automatisch geschaltet werden (und ein Interrupt ausgelöst werden, was allerdings für die PWM-Funktion nicht relevant ist). Dies entspricht dem Verfahren im [[PWM#PWM_per_Software|Beispiel zur Software-PWM]]. Es läuft nun allerdings vollautomatisch im Hintergrund, sodass der Controller nicht damit belastet wird.<br />
<br />
==== Die verschiedenen PWM-Modi ====<br />
Es gibt -je nach AVR und Timer- etliche Betriebsarten, in denen die PWM-Einheit betrieben werden kann. Sie unterscheiden sich vor allem darin, wie schnell und mit welchen Nebeneffekten sich Änderungen des Sollwertes auf das Ausgangssignal auswirken. Für den Anfang sind diese Unterschiede erst einmal nebensächlich, und für eine einfache Motorsteuerung meist auch irrelevant. Daher wird hier zunächst der "Fast PWM Mode" ("Schneller PWM Modus", weil hier die größte Ausgangsfrequenz möglich ist) beschreiben, welcher der einfachste von allen ist.<br />
<br />
Hierbei zählt der Timer immer von Null an aufwärts, bis er den Maximalwert (teilweise einstellbar) erreicht hat. Dann läuft er über und fängt von vorne an. Wie schnell dies geschieht, wird, wie im normalen Modus, über den Prescaler eingestellt.<br />
Der gewünschte PWM-Ausgangswert wird im "OCRn"-Register abgelegt. Er darf zwischen Null und dem Maximalwert des Timers liegen. Er wird nun mit dem Timer-Wert verglichen. Was dann passiert, regeln die "COM..."-Bits. Sie bestimmen, wie der Ausgang geschaltet wird. Die übliche Konfiguration ist, dass bei Erreichen des Sollwertes die Ausgänge auf high geschaltet werden, und beim Überlauf auf low. Damit ergibt sich ein nichtinvertiertes PWM-Signal. Schließt man (über einem passenden Motortreiber!) einen Motor an, dreht er sich bei einem Sollwert von 0 gar nicht und beim Maximalwert mit voller Geschwindigkeit.<br />
<br />
Beispielcode für den Timer1 des Mega16/32 (und vieler anderer AVRs): <br />
<pre><br />
#include <avr/io.h><br />
#include <util/delay.h> // Warteschleife für die Demo. Für die eigentliche PWM nicht benötigt! <br />
<br />
/* PWM-Beispiel für Mega16/32 (beiden haben den gleichen Timer)<br />
Benutzt wird Timer1 im Fast PWM Mode, 8 Bit Auflösung<br />
Die PWM-Signale liegen auf PD5/OC1A und PD4/OC1B<br />
*/ <br />
<br />
// 1. Den Prescaler einstellen, der die Frequenz festlegt<br />
TCCR1B |= (1<<CS12); //Prescaler 256<br />
<br />
// 2. Den Timer in den Fast PWM Mode, 8 Bit schalten<br />
// ACHTUNG: Die WGM-Bits sind auf beide Konfigurationsregister verteilt!<br />
TCCR1A |= (1<<WGM10);<br />
TCCR1B |= (1<<WGM12);<br />
<br />
// 3. Compare Output mode einstellen: Pin geht auf high bei Compare match, auf low bei Überlauf. <br />
// Ergibt nichtinvertierte PWM. <br />
TCCR1A |= (1<<COM1A1) | (1<<COM1B1) ; <br />
<br />
// In diesen Registern wird der gwünschte PWM-Wert abgelegt. Erlaubter Bereich: 0 bis 255.<br />
OCR1A = 0;<br />
OCR1B = 0; <br />
<br />
// 4. Die Pins als Ausgänge konfigurieren. Erst jetzt liegt das PWM-Signal an den Pins an! <br />
DDRD |= (1<<PD4) | (1<< PD5);<br />
<br />
/*Nun ist der PWM-Modus aktiv! Der Ausgangswert kann nun über die Register OCR1A und OCR1B<br />
vorgegeben werden. Man könnte ihnen per define noch einen Zweitnamen verpassen, zb */<br />
#define MotorLinks OCR1A<br />
#define MotorRechts OCR1B<br />
//Und nun kann man per <br />
MotorLinks = 127;<br />
MotorRechts = 127;<br />
//seinen Roboter mit halber Kraft vorwärts fahren lassen.<br />
<br />
/*PWM-Demo: Die PWM-Werte werden erst bis zum Maximalwert erhöht und dann wieder verringert. <br />
Ein angeschlossener Motor wird beschleunigen und dann wieder abbremsen. */<br />
uint8_t wert;<br />
while(1)<br />
{<br />
for (wert=0; wert<255; wert++)<br />
{<br />
OCR1A = wert;<br />
OCR1B = wert;<br />
_delay_ms(10);<br />
}<br />
<br />
for (wert=255; wert>0; wert--)<br />
{<br />
OCR1A = wert;<br />
OCR1B = wert;<br />
_delay_ms(10);<br />
} <br />
}<br />
</pre><br />
<br />
Es sind also vier Einstellungen zu treffen:<br />
# Takt anlegen per Prescaler<br />
# PWM-Modus wählen<br />
# Ausgangs-Aktion festlegen<br />
# Pins als Ausgänge schalten<br />
<br />
Diese Schritte müssen bei jedem AVR-PWM-Kanal ausgeführt werden. Die genauen Registernamen und Werte können sich jedoch je nach Timer-Ausführung etwas unterscheiden.<br />
<br />
== Registerübersicht ==<br />
''Hinweis: Diese Registertabellen wurden für den aktuellen [[Atmel Controller Mega16 und Mega32]] erstellt. Wenn Sie ein anderes Modell verwenden kann es sein, dass ein oder mehrere Register nicht existieren, oder sie eine andere Bezeichnung haben.''<br />
<br />
{| {{Blaueschmaltabelle}} width=100%<br />
|'''TIMSK'''<br />
|-<br />
|Mit diesem Register, der von allen Timern verwendet wird, lässt sich die Interruptausführung und Art des jeweiligen Timers bestimmen.<br/><br/><br />
<br />
{{Registertabelle8Bit|OCIE2|TOIE2|TICIE1|OCIE1A|OCIE1B|TOIE1|OCIE0|TOIE0}}<br />
<br />
|-<br />
|<br />
*'''OCIE2 (Timer/Counter2 Output Compare Match Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, wird der Timer/Counter2 Compare Match Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''TOIE2 (Timer/Counter2 Overflow Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, wird der Timer/Counter2 Overflow Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''TICIE1 (Timer/Counter1, Input Capture Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, wird der Timer/Counter1 Input Capture Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''OCIE1A (Timer/Counter1 Output Compare A Match Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, wird der Timer/Counter1 Output Compare A Match Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''OCIE1B (Timer/Counter1 Output Compare B Match Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, wird der Timer/Counter1 Output Compare B Match Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''TOIE1 (Timer/Counter1 Overflow Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, wird der Timer/Counter1 Overflow Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''OCIE0 (Timer/Counter0 Output Compare Match Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, so wird der Timer/Counter0 Compare Match Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''TOIE0 (Timer/Counter0 Overflow Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, so wird der Timer/Counter0 Overflow Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
|}<br />
<br />
<br />
== Siehe auch ==<br />
<br />
* [[Atmel]]<br />
* [[HEX Beispiel-Dateien für AVR]]<br />
* [[Bascom_und_Timer]]<br />
<br />
== Weblinks ==<br />
<br />
* [http://www.atmel.com/dyn/products/devices.asp?family_id=607 Die Datenblätter zu Atmel Controllern]<br />
* [http://frank.circleofcurrent.com/cache/avrtimercalc.htm Javascript-Toll zur Timerberechnung]<br />
* [http://www.roboternetz.de/phpBB2/dload.php?action=file&file_id=169 AvrTimer Windows Berechnungstool (für Bascom, nur nach Anmeldung)]<br />
<br />
<br />
[[Kategorie:Microcontroller]]<br />
[[Kategorie:Grundlagen]]<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Abkürzung|AVR]]<br />
[[Kategorie:Quellcode C]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Timer/Counter_(Avr)&diff=23605Timer/Counter (Avr)2014-02-28T11:10:41Z<p>Besserwessi: </p>
<hr />
<div>Die Mikrocontroller der AVR-Familie besitzen je nach Typ eine unterschiedliche Anzahl an programmierbaren [[Timer|Timern]]. Bei den aktuellen ATmegas sind das mindestens ein 8-Bit Timer und ein 16-Bit Timer. Die Timer werden immer Timerx benannt, wobei x für die Timernummer steht (also 0, 1, 2, usw.). <br />
Die Konfigurationsmöglichkeiten sind von Timer zu Timer unterschiedlich.<br />
<br />
'''Hinweis:''' Die folgenden Code-Beispiele sind in C programmiert und wurden für einen [[ATMega32|ATmega32]] entwickelt. Sie lassen sich also ohne große Änderungen auch auf anderen Mikrocontrollern der AVR-Familie einsetzen. Allerdings hat Atmel bei den neueren µCs (etwa Mega88, Mega324 und fast alle der aktuellen Tiny) die Namen für die Register vielfach geändert. <br />
<br />
== Allgemeine Funktionsweise ==<br />
Timer funktionieren nach dem allgemeinen Prinzip, dass sie eine Ganzzahl (im weiteren als Zähler bezeichnet) je nach Betriebsmodus auf- oder abwärtszählen, d.h. inkrementieren bzw. dekrementieren.<br />
<br />
Angenommen, der Timer arbeitet im einfachsten Betriebsmodus, dem [[Timer/Counter (Avr)#Normaler Modus (Normal Mode)|Normalen Modus]]. Die Zählrichtung des Timers ist aufsteigend gerichtet. Je nach Auflösung, also 8-Bit oder 16-Bit, folgt auf den maximalen Zählerstand wieder die Null. Wenn z.B. bei einem 8-Bit Timer der Wert 255 inkrementiert wird folgt die Null (siehe Grafik).<br />
<br />
[[Bild:AbstrakterZaehlvorgang.png]]<br />
<br />
== Der Prescaler ==<br />
Der Prescaler (eng. = Vorteiler) kann der Takt für den Timer herunter geteilt werden. Oft hat man Faktoren von 1, 8, 64 ,256 oder 1024 zur Auswahl. Über das selbe Register kann der Timer auch ganz angehalten werden oder ein externer Takt ausgewählt werden. Ein externer Takt darf dabei höchstens halb so hoch wie der Prozezessortakt sein.<br />
Hier eine Grafik die den Prescaler veranschaulicht:<br />
<br />
[[Bild:Prescaler.png]]<br />
<br />
Das obere Diagramm zeigt den Betrieb ohne Prescaler, das untere mit Prescaler (:2). Die gestrichelte Linie zeigt, wann der Timer weiterzählt.<br />
<br />
Im Teil [[Timer/Counter (Avr)#Die Betriebsmodi|Die Betriebsmodi]] wird weiter auf die praktische Verwendung des Prescalers eingegangen.<br />
<br />
== Die Betriebsmodi ==<br />
Die AVR-Timer können in unterschiedlichen Betriebsmodi betrieben werden. Diese sind:<br />
* Normaler Modus<br />
* CTC Modus<br />
* PWM<br />
<br />
=== Normaler Modus (Normal Mode) ===<br />
Der einfachste Betriebsmodus ist der normale Modus. Er funktioniert wie im Abschnitt "[[Timer/Counter (Avr)#Allgemeine Funktionsweise|Allgemeine Funktionsweise]]" beschrieben. Die Zählrichtung des Timers ist immer aufsteigend, bis zum Überlauf - da fängt der Zähler wieder bei 0 an. Der Überlauf kann einen Interrupt (Timer-Overflow) auslösen. Im einfachsten Fall kann dieser Modus im folgendem Diagramm dargestellt werden:<br />
<br />
[[Bild:NormalerModus_1.png]]<br />
<br />
Der Zähler des Timers (im Diagramm oben, die aufsteigende und dann wieder zurückgesetzte Linie) ist in dem Register TCNTx gespeichert, wobei x für eine Zahl steht. Soll z.B. auf den Timer0 (siehe Datenblatt des jeweiligen Controllers) des Controllers zugegriffen werden, so ist an TCNT eine 0 anzuhängen, also TCNT0.<br />
Wie lange es braucht, bis der Zähler einen Overflow auslöst, ist von der Taktfrequenz des Controllers, dem eingestellten Prescaler-Wert und von der Timerauflösung abhängig. Nun wäre es ja sehr unpraktisch, wenn wir den Zähler nicht anpassen könnten. Denn sonst müssten wir unsere Software, die den Timer benutzt, evtl. anpassen und viel rechnen, um z.B. für 1000 ms zu schlafen. Deswegen kann auf den Zähler zugegriffen werden und ihn vorladen, bevor dieser wieder vom eigentlichen Timer hochgezählt wird. Dies veranschaulicht folgendes Diagramm:<br />
<br />
[[Bild:NormalerModus_1_Vorladen.png]]<br />
<br />
Dadurch kann eingestellt werden, wie lange es dauert, bis ein Overflow auftritt. Um zu berechnen, welchen Wert wir vorladen müssen, kann auch ein Java-Applet genutzt werden, siehe unter [[Timer/Counter (Avr)#Weblinks|Weblinks Java Applet]].<br />
<br />
Natürlich kann das auch "von Hand" berechnet werden. Die Berechnung des Preloader- sowie Prescalerwerts bei Verwendung der Overflow-Interrupts, eines Prescalers von 64 (nicht alle Prescaler können verwendet werden) und eines Quarzes mit der Frequenz von 8 MHz sieht folgendermaßen aus (gesuchte Frequenz beträgt 1000 Hz unter der Verwendung des Timer0 eines ATmega32):<br />
# <math>Prescale = Frequenz * 1000000 [Hz] = 8000000</math><br />
# Wir definieren den maximalen Zählerwert. Dieser ist bei einem 8-Bit Timer 256, bei einem 16-Bit Timer 65536. In unserem Fall ist der maximale Zählerwert 256, weil Timer0 verwendet wird.<br />
# Nun wird die Variable ''Prescale'' (s.o.) durch den verwendeten Prescaler (64) geteilt (<math>8000000 Hz / 64 = 125000</math>).<br />
# Als nächstes wird der im dritten Punkt errechnete Wert durch die gesuchte Frequenz geteilt <math>=125000 / 1000Hz = 125</math>.<br />
# Nun wird mathematisch überprüft, ob der errechnete Wert aus dem vierten Punkt kleiner als der maximale Zählerwert ist. Trifft dies zu, so wird der errechneten Wert vom maximalen Zählerwert subtrahiert(<math>= 256 - 125 = 131</math>).<br />
<br />
Damit haben wir den Wert errechnet, der bei jedem Interrupt, den der Timer0 auslöst, in TCNTx (in diesem Fall TCNT0) nachgeladen werden muss, damit die Interrupts in dem gewünschten Zeitabstand von einer Millisekunde ausgelöst werden.<br />
<br />
Zwischen dem Timer Overflow und dem tatsächlichen Aufrufen der ISR mit dem Nachladen des Timers ergibt sich eine kleine Verzögerung, die nicht einmal immer gleich ist. Bei einem genügend großen Prescaler (z.B. 64) kommt durch die Verzögerung kein zusätzlicher Timerschritt zustande, und auch die Methode mit dem Nachladen liefert exacte Ergebnisse. Bei kleinen Prescalern kommt es durch die Verzögerung zu längeren und nicht immer gleichen Zeitabständen. Wenn möglich wird für die Erzeugung einer konstanten Interruptrate deshalb besser der CTC Moduls benutzt.<br />
<br />
Zusammenfassend ein Beispielprogramm:<br />
<br />
<pre><br />
/* Es wird der Timer2 (8-Bit) eines ATmega32 verwendet, der mit einem Quarz mit 7,3728 MHz<br />
betrieben wird. Im Abstand von etwa 0,1 ms erzeugt der Timer einen Interrupt, also eine<br />
Frequenz von 10000 Hz. Der Timer wird auf einen Prescaler von 64 und<br />
einem Preloader von 244 konfiguriert.*/<br />
<br />
volatile uint8_t countTimer2; // Speichert den aktuellen Zählerwert<br />
<br />
// ISR zum auffangen der Interrupts:<br />
SIGNAL(SIG_SIG_OVERFLOW2)<br />
{<br />
TCNT2 = 244; // Nachladen<br />
countTimer2++;<br />
}<br />
<br />
// Initialisierung:<br />
TCCR2 = (1<<CS22); // Prescaler von 64<br />
TCNT2 = 244; // Vorladen<br />
TIMSK |= (1<<TOIE2); // Interrupts aktivieren und damit Timer starten<br />
sei();<br />
<br />
// Funktionen zum benutzen der Timer:<br />
/** Diese Funktion nicht aufrufen. Wird von sleep_millisec aufgerufen.<br />
Bei t=10 schläft die Funktion 1 ms. */<br />
inline void sleep (uint8_t t)<br />
{<br />
// countTimer2 wird in der ISR oben inkrementiert<br />
countTimer2 = 0; // 1 Byte Typ, daher kein cli()... sei() nötig<br />
while (countTimer2 < t);<br />
}<br />
<br />
/** Schläft x-Millisekunden. */<br />
inline void sleep_millisec(uint16_t msec)<br />
{<br />
uint16_t i;<br />
for(i=0; i<msec; i++) {<br />
sleep(10);<br />
}<br />
}<br />
</pre><br />
<br />
Dieses Beispiel zeigt nicht unbedingt eine vorbildliche Nutzung des Timers. Eine ISR einfach nur zum schnellen Hochzählen der Zeit verbraucht recht viel Rechenzeit und sollte sonst eher vermieden werden. Das Hochzählen der "Zeit" ist eigentlich genau das, was der Timer in Hardware macht - nur halt nicht immer in geraden Zeitschritte, wie 0,1 ms, sondern halt in den Schritten, die der Prescaler vorgibt (z.B. 256 Takte). Die Umrechnung kann man aber gut auch bei der Wartezeit vorher, oder bei einer gemessenen Zeit nachher machen. <br />
<br />
==== Input Capture ====<br />
<br />
Die 16 Bit Timer haben eine "Input Capture" Funktion. Dieser Hardwareteil dient zur genauen Zeitmessung. Die typische Anwendung ist die Messung von kurze Zeiten, wie z.B. die Zeit für eine Motorumdrehung. Außer in einigen PWM Betriebsarten, wo das ICP Register als TOP-wert für den Timer benutzt wird, ist die ICP-Funktion immer aktiv. Wenn am ICP Pin die über das Bit "ICESx" eingestellte Flanke auftritt, wird der aktuelle Zählerstand in das ICP Register kopiert. Außerdem kann ein Interrupt ausgelöst werden. Der Interrupt wird, wie die anderen Timer Interrupts, in den Registern TIMSK und TIFR an- oder abgestellt. Man kann zwar die ICP-Funktion selber nicht ohne weiteres abschalten, aber natürlich den dazugehörigen Interrupt. Als eine spezielle Funktion ("Noise Cancler") gibt es die Möglichkeit sehr kurze Pulse (unter 4 Zyklen) zu unterdrücken. In der Regel kann man diese Funktion angestellt lassen, denn so schnell kann man die Daten ohnehin nicht verarbeiten.<br />
<br />
Solange die 16 Bit des Timers ausreichen ist die Benutzung ganz einfach: Der Timer wird mit dem gewünschten Vorteiler im normalen Modus gestartet. Im ICP-Interrupt wird die Differenz aus zwei aufeinanderfolgenden Zeiten (Werte in ICP-Register) berechnet. Dazu wird jeweils die vorherige Zeit im RAM zwischengespeichert. Wenn man bei der Rechnung (vorzeichenlose 16 Bit Zahlen) eventuelle Überläufe ignoriert, bekommt man die richtige Zeitdifferenz, auch wenn der Timer während der Messzeit einen Überlauf hatte. Das funktioniert so einfach, denn wenn noch weitere (höherwertige) Bytes vorhanden wären, damit es keinen Überlauf gibt, würde man genau so die unteren Bits berechnen.<br />
<br />
Etwas komplizierter wird es, wenn die 16 Bit Auflösung nicht mehr ausreicht. Dann kann der Timer-Überlauf benutzt werden, um auch längere Zeiten mit voller Auflösung zu messen. Die wesentliche Schwierigkeit ist es, den Fall zu berücksichtigen, dass ein Überlauf Interrupt und der ICP Interrupt fast gleichzeitig ausgelöst werden. Es kann passieren, dass der ICP-Interrupt aufgerufen wird, obwohl eigentlich erst der Overflow Interrupt dran gewesen wäre. Dieser seltene Fall lässt sich daran erkennen, dass das Overflow-Interrupt Flag gesetzt ist und der Wert im ICP Register klein ist (high Byte < 128, meistens 0).<br />
<br />
Beispielpropgramm (für GCC):<br />
(Bisher nur im Simulator getestet)<br />
<pre><br />
// Beispielprogramm für Zeitmessung mit ICP-Funktion<br />
// Erweiterung des Timers auf 32 Bit durch Software<br />
// Es wird die Periodendauer am ICP-Eingang gemessen und als ASCII via UART ausgegeben<br />
// Code für Mega48 / Mega88 / Mega 168 / ...<br />
// mit leichten Anpassungen auch für Tiny2313, Mega16, Mega32,...<br />
<br />
#include <stdlib.h> // für utoa<br />
#include <avr/io.h><br />
#include <avr/interrupt.h><br />
#include <avr/sleep.h> // Unterstützung für sleep mode<br />
<br />
#define F_CPU 1000000UL // Definition der Frequenz, ist ggf. im makefile<br />
#define BAUD 19200UL<br />
#define UBRR_BAUD ((F_CPU/(16UL*BAUD))-1)<br />
<br />
typedef union { // union erlaubt einen effektiven, separaten Zugriff auf Teile der Variable<br />
unsigned long i32;<br />
struct {uint8_t i8l; // low<br />
uint8_t i8m; // mid<br />
unsigned int high; // high, soft timer <br />
};<br />
} convert32to8;<br />
<br />
volatile unsigned long timestamp; // volatile wegen Zugriff im Interrupt<br />
volatile unsigned int softtimer;<br />
volatile unsigned long zeitdifferenz;<br />
unsigned long zeit;<br />
char puffer[12]; // Puffer für Ausgabe als Ascii<br />
<br />
ISR(TIMER1_OVF_vect) // Timer1 Überlauf<br />
{ <br />
++softtimer; // zählen der Überläufe<br />
} <br />
<br />
ISR(TIMER1_CAPT_vect) // Flanke an ICP pin<br />
{ <br />
convert32to8 cap; // Variablendeklaration<br />
<br />
cap.i8l = ICR1L; // low Byte zuerst, high Byte wird gepuffert<br />
cap.i8m = ICR1H; <br />
// overflow verpasst, wenn ICR1H klein und wartender Overflow Interrupt<br />
if ((cap.i8m < 128) && (TIFR1 & (1<<TOV1)))<br />
{ // wartenden timer overflow Interrupt vorziehen<br />
++softtimer; <br />
TIFR1 = (1<<TOV1); // timer overflow int. löschen, da schon hier ausgeführt<br />
}<br />
cap.high = softtimer; // obere 16 Bit aus Software Zähler<br />
zeitdifferenz = cap.i32 - timestamp;<br />
timestamp = cap.i32; // Zeit merken<br />
}<br />
<br />
void uart_init(void) // USART initialisieren (Mega48 etc.)<br />
{<br />
// Baudrate einstellen (Normaler Modus)<br />
// kann bei älteren AVR Typen etwas anders sein (kein UBRR0H, dafür prescaler)<br />
UBRR0H = (uint8_t) (UBRR_BAUD>>8); // bei Mega32 anders !<br />
UBRR0L = (uint8_t) (UBRR_BAUD & 0x0ff); // bei Mega32 UBRRL<br />
UCSR0B = (1<<TXEN0); // Aktivieren des Senders, bei Mega32 UCSRB<br />
UCSR0C = (1<<UCSZ01)|(1<<UCSZ00)|(1<<USBS0); // bei Mega32 UCSRC<br />
// Einstellen des Datenformats: 8 Datenbits, 2 Stopbit:<br />
}<br />
<br />
void putser(char c) // sende ein Byte via UART<br />
{<br />
while ( !( UCSR0A & (1<<UDRE0)) ) ; // Warten bis der Sendepuffer frei ist<br />
UDR0 = c;<br />
}<br />
<br />
void main(void)<br />
{<br />
unsigned char i;<br />
// Datenrichtungen:<br />
DDRB = 0; // Alles Eingänge, PB0 ist ICP<br />
PORTB = 0xFF - (1<<PB0); // Pullups an Eingängen außer ICP<br />
DDRC = 0; // Eingänge<br />
PORTC = 0xFF; // Pullups an Eingängen <br />
DDRD = (1<<PD1); // Eingänge, außer PD1 = Tx (UART)<br />
PORTD = 0xFF- (1<<PD1); // Pullups an alle Eingängen (außer TX)<br />
// Timer1 initialisieren:<br />
TCCR1A = 0; // normal mode, keine PWM Ausgänge<br />
TCCR1B = (1<< ICNC1) + (1<<CS10) // start Timer mit Systemtakt<br />
+ (1 << ICES1); // steigende Flanke auswählen<br />
TIMSK1 = (1<<TOIE1) + (1<<ICIE1); // overflow und Input-capture aktivieren, Mega32: TIMSK<br />
TIFR1 = (1<<TOIE1) + (1<<ICIE1); // Schon aktive Interrupts löschen, Mega32: TIFR<br />
// UART initialisieren:<br />
uart_init(); <br />
<br />
zeitdifferenz = 0;<br />
softtimer = 0; // wird für Zeitdifferenzmessung nicht mal gebraucht,<br />
// denn Differenz geht auch über Überlauf bei Softtimer<br />
set_sleep_mode (SLEEP_MODE_IDLE); // idle Mode: timer läuft weiter, int zum aufwachen<br />
<br />
sei(); // Interrupts erlauben: Messung startet<br />
while (1)<br />
{<br />
sleep_enable (); // Sleep Befehl freigeben <br />
sei();<br />
sleep_cpu(); // wartet auf irgendeinen Interrupt, z.B. ICP, timer_ovr,...<br />
sleep_disable(); // Sleep Befehl sperren<br />
cli(); // Interrupt sperren wegen Zugriff auf volatile Variable<br />
zeit = zeitdifferenz;<br />
zeitdifferenz = 0; // als Markierung für ungültigen Wert<br />
sei();<br />
if (zeit > 0)<br />
{ <br />
ultoa(zeit,puffer,10); // nach ASCII umwandeln<br />
i = 0;<br />
while (puffer[i])<br />
{<br />
putser(puffer[i++]); // Ausgabe<br />
}<br />
putser(13); putser(10); // Zeilenumbruch senden<br />
}<br />
} // Ende von While-schleife<br />
}<br />
</pre><br />
<br />
=== CTC Modus (Clear Timer on Compare Match mode) ===<br />
Viele Timer haben "Output-Compare" Register: OCRx oder OCRAx,OCRBx. Wenn der Zähler den darin eingestellten Wert erreicht hat, kann ein Interrupts ausgelöst werden. Der CTC Modus ist eine Erweiterung des "Output-Compare"-Funktion. Der CTC Modus eignet sich besonders, um einen mit konstanter Frequenz wiederkehrenden Interrupt zu erzeugen. Wie im normalen Modus zählt der Timer hoch. Wenn der Wert im OCRx Register erreicht wird, wird zusätzlich zum möglichen Interrupt der Zähler wieder auf 0 gesetzt. Es kann also die maximalen Zählergrenze selber definiert werden.<br />
Dieses Diagramm veranschaulicht den CTC Modus.<br />
<br />
[[Bild:NormalerModus_CompareMatch.png]]<br />
<br />
Beispielprogramm:<br />
<br />
<pre><br />
/* Es wird der Timer2 (8-Bit) eines ATmega32 verwendet, der mit einem Quarz <br />
mit 7,3728 MHz betrieben wird. Im Abstand von 0,01 ms erzeugt der Timer <br />
einen Interrupt, also eine Frequenz von 100000 Hz (oder 100 kHz). <br />
Der Timer wird auf einen Prescaler von 1 und einem OCR2-Wert von 73 konfiguriert. */<br />
<br />
volatile uint8_t countTimer2; // Speichert den aktuellen Zählerwert<br />
<br />
// ISR zum auffangen der Interrupts:<br />
SIGNAL(SIG_OUTPUT_COMPARE2)<br />
{<br />
countTimer2++;<br />
}<br />
<br />
// Initialisierung:<br />
TCCR2 = (1<<CS20) | (1<<WGM21); // Prescaler von 1 | CTC-Modus (siehe unten für Beschreibung)<br />
OCR2 = 73; // Vergleichswert<br />
TIMSK |= (1<<OCIE2); // Interrupts aktivieren und damit Timer starten<br />
sei();<br />
<br />
// Funktionen zum benutzen der Timer:<br />
/** Diese Funktion nicht aufrufen. Wird von sleep_millisec aufgerufen.<br />
Bei t=100 schläft die Funktion 1 ms. */<br />
inline void sleep(uint8_t t)<br />
{<br />
// countTimer2 wird in der ISR oben inkrementiert<br />
countTimer2 = 0;<br />
while (countTimer2 < t);<br />
}<br />
<br />
/** Schläft x-Millisekunden. */<br />
inline void sleep_millisec(uint16_t msec)<br />
{<br />
uint16_t i;<br />
for(i=0; i<msec; i++) {<br />
sleep(100);<br />
}<br />
}<br />
</pre><br />
<br />
----<br />
<br />
=== PWM ===<br />
<br />
Eine häufige Aufgabe für Mikrocontroller ist die Erzeugung von [[PWM]]-Signalen, zum Beispiel für Motorsteuerungen. Daher sind in den meisten [[AVR|AVRs]] PWM-Einheiten als Hardware vorhanden. Sie sind direkt mit den Timern verbunden und nutzen diese als Taktquelle. Die Hardware-PWM-Einheiten haben den Vorteil, sehr wenig Rechenzeit in Anspruch zu nehmen. Es muss nur die PWM aktiviert werden und bei Änderungen den gewünschten Wert in ein Register schreiben. Der Rest läuft automatisch und unabhängig vom restlichen Programm, ohne den AVR ständig zu beschäftigen wie bei einer PWM-Lösung in Software. Allerdings stehen meist nur zwei bis drei solcher PWM-Kanäle zur Verfügung, die außerdem an bestimmte Pins gebunden sind. Für die meisten Roboter mit zwei Antriebsmotoren reicht dies aber für gewöhnlich aus. <br />
<br />
==== nutzbare Pins am AVR ====<br />
Die Hardware-PWM-Funktion steht nur an bestimmten Pins zur Verfügung. In der Pinbelegungsübersicht im Datenblatt ist erkenntbar, dass als Sonderfunktion in Klammern "OC..." angegeben ist. Beim Mega32 sind dies zb. OC0 an PB3, OC1A an PD5, OC1B an PD4 und OC2 an PD7. Der Mega32 hat also insgesamt vier Hardware-PWM-Kanäle. Die Zahl hinter dem "OC" gibt an, zu welchem der Timer dieser PWM-Kanal gehört. Wenn noch ein Buchstabe dahinter kommt, dann gehören mehrere PWMs zu diesem Timer. Beim Mega32 sind also OC1A und OC1B demselben Timer, nämlich Timer1, zugeordnet.<br />
<br />
'''Zu beachten ist, dass die für die PWM benutzten Pins zuvor explizit als Ausgang konfiguriert werden müssen! Ansonsten gelangt das PWM-Signal nicht nach draußen!'''<br />
<br />
==== Funktionsprinzip ====<br />
Das "OC" in den Pinbezeichnungen steht für "Output Compare Unit", also frei übersetzt Ausgangs-Vergleicher-Einheit. Dies beschreibt die Funktionsweise der PWM-Kanäle: der Zählerstand des Timers wird fortlaufend mit einen einstellbaren Referenzwert verglichen, und wenn beide Werte übereinstimmen, kann ein Ausgangspin des AVRs automatisch geschaltet werden (und ein Interrupt ausgelöst werden, was allerdings für die PWM-Funktion nicht relevant ist). Dies entspricht dem Verfahren im [[PWM#PWM_per_Software|Beispiel zur Software-PWM]]. Es läuft nun allerdings vollautomatisch im Hintergrund, sodass der Controller nicht damit belastet wird.<br />
<br />
==== Die verschiedenen PWM-Modi ====<br />
Es gibt -je nach AVR und Timer- etliche Betriebsarten, in denen die PWM-Einheit betrieben werden kann. Sie unterscheiden sich vor allem darin, wie schnell und mit welchen Nebeneffekten sich Änderungen des Sollwertes auf das Ausgangssignal auswirken. Für den Anfang sind diese Unterschiede erst einmal nebensächlich, und für eine einfache Motorsteuerung meist auch irrelevant. Daher wird hier zunächst der "Fast PWM Mode" ("Schneller PWM Modus", weil hier die größte Ausgangsfrequenz möglich ist) beschreiben, welcher der einfachste von allen ist.<br />
<br />
Hierbei zählt der Timer immer von Null an aufwärts, bis er den Maximalwert (teilweise einstellbar) erreicht hat. Dann läuft er über und fängt von vorne an. Wie schnell dies geschieht, wird, wie im normalen Modus, über den Prescaler eingestellt.<br />
Der gewünschte PWM-Ausgangswert wird im "OCRn"-Register abgelegt. Er darf zwischen Null und dem Maximalwert des Timers liegen. Er wird nun mit dem Timer-Wert verglichen. Was dann passiert, regeln die "COM..."-Bits. Sie bestimmen, wie der Ausgang geschaltet wird. Die übliche Konfiguration ist, dass bei Erreichen des Sollwertes die Ausgänge auf high geschaltet werden, und beim Überlauf auf low. Damit ergibt sich ein nichtinvertiertes PWM-Signal. Schließt man (über einem passenden Motortreiber!) einen Motor an, dreht er sich bei einem Sollwert von 0 gar nicht und beim Maximalwert mit voller Geschwindigkeit.<br />
<br />
Beispielcode für den Timer1 des Mega16/32 (und vieler anderer AVRs): <br />
<pre><br />
#include <avr/io.h><br />
#include <util/delay.h> // Warteschleife für die Demo. Für die eigentliche PWM nicht benötigt! <br />
<br />
/* PWM-Beispiel für Mega16/32 (beiden haben den gleichen Timer)<br />
Benutzt wird Timer1 im Fast PWM Mode, 8 Bit Auflösung<br />
Die PWM-Signale liegen auf PD5/OC1A und PD4/OC1B<br />
*/ <br />
<br />
// 1. Den Prescaler einstellen, der die Frequenz festlegt<br />
TCCR1B |= (1<<CS12); //Prescaler 256<br />
<br />
// 2. Den Timer in den Fast PWM Mode, 8 Bit schalten<br />
// ACHTUNG: Die WGM-Bits sind auf beide Konfigurationsregister verteilt!<br />
TCCR1A |= (1<<WGM10);<br />
TCCR1B |= (1<<WGM12);<br />
<br />
// 3. Compare Output mode einstellen: Pin geht auf high bei Compare match, auf low bei Überlauf. <br />
// Ergibt nichtinvertierte PWM. <br />
TCCR1A |= (1<<COM1A1) | (1<<COM1B1) ; <br />
<br />
// In diesen Registern wird der gwünschte PWM-Wert abgelegt. Erlaubter Bereich: 0 bis 255.<br />
OCR1A = 0;<br />
OCR1B = 0; <br />
<br />
// 4. Die Pins als Ausgänge konfigurieren. Erst jetzt liegt das PWM-Signal an den Pins an! <br />
DDRD |= (1<<PD4) | (1<< PD5);<br />
<br />
/*Nun ist der PWM-Modus aktiv! Der Ausgangswert kann nun über die Register OCR1A und OCR1B<br />
vorgegeben werden. Man könnte ihnen per define noch einen Zweitnamen verpassen, zb */<br />
#define MotorLinks OCR1A<br />
#define MotorRechts OCR1B<br />
//Und nun kann man per <br />
MotorLinks = 127;<br />
MotorRechts = 127;<br />
//seinen Roboter mit halber Kraft vorwärts fahren lassen.<br />
<br />
/*PWM-Demo: Die PWM-Werte werden erst bis zum Maximalwert erhöht und dann wieder verringert. <br />
Ein angeschlossener Motor wird beschleunigen und dann wieder abbremsen. */<br />
uint8_t wert;<br />
while(1)<br />
{<br />
for (wert=0; wert<255; wert++)<br />
{<br />
OCR1A = wert;<br />
OCR1B = wert;<br />
_delay_ms(10);<br />
}<br />
<br />
for (wert=255; wert>0; wert--)<br />
{<br />
OCR1A = wert;<br />
OCR1B = wert;<br />
_delay_ms(10);<br />
} <br />
}<br />
</pre><br />
<br />
Es sind also vier Einstellungen zu treffen:<br />
# Takt anlegen per Prescaler<br />
# PWM-Modus wählen<br />
# Ausgangs-Aktion festlegen<br />
# Pins als Ausgänge schalten<br />
<br />
Diese Schritte müssen bei jedem AVR-PWM-Kanal ausgeführt werden. Die genauen Registernamen und Werte können sich jedoch je nach Timer-Ausführung etwas unterscheiden.<br />
<br />
== Registerübersicht ==<br />
''Hinweis: Diese Registertabellen wurden für den aktuellen [[Atmel Controller Mega16 und Mega32]] erstellt. Wenn Sie ein anderes Modell verwenden kann es sein, dass ein oder mehrere Register nicht existieren, oder sie eine andere Bezeichnung haben.''<br />
<br />
{| {{Blaueschmaltabelle}} width=100%<br />
|'''TIMSK'''<br />
|-<br />
|Mit diesem Register, der von allen Timern verwendet wird, lässt sich die Interruptausführung und Art des jeweiligen Timers bestimmen.<br/><br/><br />
<br />
{{Registertabelle8Bit|OCIE2|TOIE2|TICIE1|OCIE1A|OCIE1B|TOIE1|OCIE0|TOIE0}}<br />
<br />
|-<br />
|<br />
*'''OCIE2 (Timer/Counter2 Output Compare Match Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, wird der Timer/Counter2 Compare Match Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''TOIE2 (Timer/Counter2 Overflow Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, wird der Timer/Counter2 Overflow Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''TICIE1 (Timer/Counter1, Input Capture Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, wird der Timer/Counter1 Input Capture Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''OCIE1A (Timer/Counter1 Output Compare A Match Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, wird der Timer/Counter1 Output Compare A Match Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''OCIE1B (Timer/Counter1 Output Compare B Match Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, wird der Timer/Counter1 Output Compare B Match Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''TOIE1 (Timer/Counter1 Overflow Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, wird der Timer/Counter1 Overflow Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''OCIE0 (Timer/Counter0 Output Compare Match Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, so wird der Timer/Counter0 Compare Match Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
*'''TOIE0 (Timer/Counter0 Overflow Interrupt Enable)'''<br/>Wenn dieses Bit gesetzt wird, so wird der Timer/Counter0 Overflow Interrupt aktiviert (vorrausgesetzt die Interrupts sind global aktiviert).<br />
|}<br />
<br />
<br />
== Siehe auch ==<br />
<br />
* [[Atmel]]<br />
* [[HEX Beispiel-Dateien für AVR]]<br />
* [[Bascom_und_Timer]]<br />
<br />
== Weblinks ==<br />
<br />
* [http://www.atmel.com/dyn/products/devices.asp?family_id=607 Die Datenblätter zu Atmel Controllern]<br />
* [http://frank.circleofcurrent.com/cache/avrtimercalc.htm Javascript-Toll zur Timerberechnung]<br />
* [http://www.roboternetz.de/phpBB2/dload.php?action=file&file_id=169 AvrTimer Windows Berechnungstool (für Bascom, nur nach Anmeldung)]<br />
<br />
<br />
[[Kategorie:Microcontroller]]<br />
[[Kategorie:Grundlagen]]<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Abkürzung|AVR]]<br />
[[Kategorie:Quellcode C]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Feldeffekttransistor&diff=23496Feldeffekttransistor2014-01-08T19:54:56Z<p>Besserwessi: /* Auswahl des passenden MOSFET */</p>
<hr />
<div>Der Feldeffekttransistor, meist als FET (Field Effect Transistor) bezeichnet, ist ein unipolarer [[Transistor]]. Unipolar deshalb, weil im Gegensatz des bipolaren Transistors, je nach Typ, entweder nur Löcher oder Elektronen am Stromtransport beteiligt sind. <br />
<br />
Der FET hat 3 Anschlüsse, Source (Zufluss, Quelle), Gate (Tor) und Drain (Abfluss). Ein vierter Anschluss Bulk (Substrat) ist bei Einzeltransistoren i.d.R. mit Source verbunden und nicht extra herausgeführt.<br />
<br />
Die Steuerspannung zwischen Gate und Source erzeugt ein elektrisches Feld. Die dazugehörigen Ladungen beeinflussen die Leitfähigkeit des Source-Drain-Kanals des Feldeffekt-Transistors. Je nach benutzter Isolierung wird zwischen MOSFET und JFET (Junction- oder Sperrschicht-FET) unterschieden. <br />
<br />
Es gibt folgende Formen von FETs:<br />
* Sperrschicht-Feldeffekt-Transistor (JFET)<br />
* Metalloxidhalbleiter-Feldeffekt-Transistor (MOSFET)<br />
* dual Gate MOSFET : Spezialltype, hauptsächlich für HF<br />
* Schottky-Feldeffekt-Transistor (MESFET) - Spezialtype für HF<br />
* High Electron Mobility Transistor (HEMT) - Spezialtype für HF, > 1 GHz<br />
* Ionen-Sensitiver Feldeffekt-Transistor (ISFET) - als Sensor<br />
<br />
==Schaltsymbole==<br />
;N-MOSFET (Anreicherungstyp, selbstsperrend): [[Bild:Schaltsymbol NFET.png]]<br />
<br />
;P-MOSFET (Anreicherungstyp, selbstsperrend): [[Bild:Schaltsymbol PFET.png]]<br />
<br />
;N-Kanal JFET (Verarmungstyp, selbstleitend): [[Bild:Schaltsymbol JFETN.png]] [[Bild:Schaltsymbol JFETN2.png]]<br />
<br />
;P-Kanal JFET (Verarmungstyp, selbstleitend): [[Bild:Schaltsymbol JFETP.png]] [[Bild:Schaltsymbol JFETP2.png]]<br />
<br />
==JFET==<br />
JFETs nutzen einen in Sperrrichtung betriebenen p-n-Übergang, um das elektrische Feld zu bilden. Theoretisch kann er auch in Flussrichtung betrieben werden, was allerdings den Vorteil der leistungslosen Ansteuerung zunichte macht. Bei vielen JFETs gibt es keinen, oder wenig, Unterschied zwischen Drain und Source. Sie können also für beide Stromrichtungen benutzt werden.<br />
Ohne Gate-Source Spannung ist ein JFET leitend. Durch Spannung in Sperrichtung wird der Strom reduziert und erreicht bei der Abschnürrspannung (sog. pinch-off voltage) schließlich 0A. Bei kleinen Drain-Source Spannung verhält sich der JFET wie ein Spannungsgesteuerter Widerstand. Bei Drain-Source Spannung über etwa 1-2 V verhält sich ein JFET wie eine Spannungsgesteuerte Strombegrenzung. JFETs werden hauptsächlich als Schalter für Signalspannungen und als schnelle hochohmige Verstärker eingesetzt. Es gibt viele Operationsverstärker mit JFET Eingängen.<br />
Gebräuchliche N-Kanal JFETs sind: BF245, BF256, 2N4416. P-Kanal JFETs sind selten, es gibt sie aber.<br />
<br />
==MOSFET==<br />
MOSFETs haben eine dünne Oxidschicht als Isolierung zwischen Gate und dem Kanal. MOSFETs können so hergestellt werden, dass sie ohne Gate-Source Spannung sperren (Anreicherungs- bzw. Enhancement-Typ) oder leiten (Verarmungs- bzw. Depletion-Typ). Die einzeln erhältlichen MOSFETs sind aber ohne Gate-Source-Spannung fast alle sperrend (Enhancement-Typ), P-Channel gibt es wohl nur als Enhancement-Typ. Ab einer Gate-Source-Spannung von z.B. +2 V (N-Channel FET) steigt die Leitfähigkeit bzw. der Drain-Source-Strom an. Für den maximalen Strom sind typisch 10 V oder bei sogenannten Logic-Level Mosfets ca. 4 V nötig. Die maximal zulässige Gate-Source-Spannung liegt je nach Typ bei etwa 20-30 V, bei Logic-Level FETs zum Teil darunter.<br />
Bei einigen wenigen MOSFETs ist das Substrat als vierter Anschluss separat herausgeführt. Die Steuerspannung ist dann die Spannung zwischen Gate und Substrat. Sonst ist das Substrat intern mit Source verbunden und der MOSFETs enthält damit eine Diode zwischen Drain und Source. Zum Teil wird die Diode im Schaltungssymbol mit eingezeichnet. Es kann also Drain und Source hier nicht vertauscht werden, wenn mehr als 0,5 V Spannung anliegen. Die integrierte Diode kann einen ähnlich hohen Strom wie der FET vertragen und kann bei Brückenschaltungen als Freilaufdiode genutzt werden. Besonders kleine MOSFETs sind empfindlich gegen elektrostatische Aufladungen.<br />
<br />
== Verwendung für Schaltanwendungen ==<br />
MOSFETs werden oft für Schaltanwendungen verwendet, da sie größere Leistungen als bipolare Transistoren schalten können und geringere Verluste haben. Anders als bipolare Transistoren können MOSFETs als Schalter (d.h. bei kleiner Drain-Source Spannung, oder ausgeschaltet) problemlos parallelgeschaltet werden.<br />
<br />
Ein weiterer, wesentlicher Vorteil ist, dass sie leistungslos geschaltet werden. Wenn das Gate einmal auf eine bestimmte Spannung aufgeladen wurde, dann ist keine weitere Steuerleistung nötig. Bei bipolaren Transistoren muss die ganze Zeit ein relativ hoher Basisstrom fliessen. Allerdings hat das Gate eine relativ hohe Kapazität (ca. 1 nF für einen 10 A MOSFET). Um die Verluste beim Schalten zu minimieren, muss das Gate schnell geladen und wieder entladen werden. Dafür ist kurzzeitig ein relativ hoher Strom nötig. Aus diesem Grund gibt es fertige Treiberbausteine, wie den ICL7667, die das Gate sehr schnell umladen können. Ein schnelles Schalten des Mosfets ist insbesondere dann wichtig, wenn der Mosfet mit einer hohen Frequenz an- und ausgeschaltet wird (z.B. [[PWM]], Schaltnetzteile). <br />
<br />
Andererseits darf das Schalten auch nicht schneller erfolgen als der Rest der Schaltung es verträgt. Begrenzend sind hier z.B. ungewollte Induktivitäten, die Erholzeiten von Dioden und die Gefahr von Funkstörungen. Für eine definierte Schaltzeit wird oft ein kleiner Widerstand (ca. 100 Ohm) vor das Gate geschaltet. Wichtig ist der Widerstand besonders dann, wenn der Aufbau nicht für hohe Frequenzen ausgelegt ist, denn sonst besteht die Gefahr, dass es zwischenzeitlich zu HF-Schwingungen und damit Funkstörungen kommt. Wenn MOSFETs parallel geschaltet werden braucht jeder MOSFET getrennt einen Widerstand am Gate. <br />
<br />
In der Praxis werden wesentlich häufiger N-Kanal Mosfets als P-Kanal Mosfets verwendet. Das liegt daran, dass sich die Elektronen im Halbleiter leichter bewegen können als Löcher. Aus diesem Grund haben N-Kanal Mosfets bei gleicher Chipfläche einen geringeren RDS_ON und damit geringere Verluste. <br />
<br />
Die folgende Schaltung zeigt die Verwendung eines MOSFETs zum Regeln eines Motors über [[PWM]]. Der eingezeichnete BS170 ist allerdings nur für kleine Lasten geeignet.<br />
<br />
[[Bild:fetschaltstufe.jpg|center]]<br />
<br />
===Auswahl des passenden MOSFET===<br />
Es gibt eine verwirrend große Auswahl an MOSFETs. Die im Datenblatt genannte Strombelastbarkeit ist eher theoretischer Natur: bei dem Strom wird der sehr gut gekühlte MOSFET bereits grenzwertig heiß. Genutzt wird entsprechen nur einer kleiner Teil davon - wichtiger ist meist der On-Widerstand: darüber berechnen sich die Verluste im eingeschalteten Zustand. Hierbei ist allerdings meist der Widerstand bei erhöhter Temperatur von Bedeutung, und der kann etwa um den Faktor 1,5-2 höher liegen als der oben im Datenblatt genannte. Größere MOSFETs mit kleinerem Widerstand haben allerdings auch eine größere Gate-kapazität und damit mehr Verluste beim Umschalten. Je höher die Schaltfrequenz, desto wichtiger ist es hier einen guten Kompromiss zu finden. Eine unnötig hohe Spannungsfestigkeit führt zu hohem On-Widerstand bzw. unnötig großer Gate-Kapazität und meist hohem Preis. Die Spannungsfestigkeit muss hoch genug sein, mehr als etwa 50% Reserve über der höchsten möglichen Spannung sind aber eher nicht nötig. Lediglich bei Linearbetrieb eine eine deutlich höhere Spannungsfestigkeit oft sinnvoll, da Typen für geringe Spannung oft schlecht (oft nicht einmal spezifiziert) dafür geeignet sind. Hier muss man auf die SOA Kurve (meist relativ weit hinten im Datenblatt) achten: Sie gibt an bei welcher Spannung (Drain-Source Spannung am MOSFET - nicht nicht die Versorgungsspannung) wie viel Strom fließen darf.<br />
<br />
Ein weiterer wichtiger Parameter ist die Schwellspannung, bzw. welche Spannung für die Ansteuerung am Gate nötig ist. Zum Schalten wird eine Spannung deutlich (z.B. 2-3 V) über der Schwellspannung benötigt. MOSFETs, die zur Steuerung mit 5 V geeignet sind, sogenannte Logic level MOSFETs, erkennt man meist daran, dass im Datenblatt der On-Widerstand bei 4-5 V angeben ist. Der dazu angegebene Strom ist auch eine Hinweis auf den Strom der maximal realistisch nutzbar ist. Für den Betrieb mit nur 3,3 V Gate-Spannung gibt es spezielle Low-Level MOSFETs. <br />
<br />
Als Hilfe bieten viel Händler und Hersteller eine parametrische Suche, um die Auswahl z.B. auf die passende Spannungsfestigkeit, Gehäuseform und den Preisrahmen zu begrenzen.<br />
<br />
Sucht man etwa einen MOSFET um einen MOTOR mit maximal 3 A bei 12 V zu steuern, so genügt ein MOSFET für 20 V oder 30 V Spannungsfestigkeit. Bei einer Verlustleistung von etwa 0,3 W ginge es ggf. noch ohne extra Kühlkörper - dies entspricht einem Spannungsabfall von 100 mV (ein realistischer Wert) oder etwa 33 mOhm als Widerstand. Passend wäre damit ein MOSFET mit weniger als rund 20-25 mOhm (im kalten Zustand) - etwa ein IRF7401 (20 V, 22 mOhm im SO8 SMD Gehäuse, oder etwa ein IRLZ44 (60 V, 28 mOhm TO220).<br />
<br />
== Bauform Beispiel ==<br />
[[Bild:fetbs170.gif|center]]<br />
<br />
<br />
= Weblinks =<br />
[http://www.mikrocontroller.net/articles/MOSFET-Übersicht |Liste von MOSFET Typen (Mikrocontroller.net)]<br />
<br />
[[Kategorie:Elektronik]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Feldeffekttransistor&diff=23495Feldeffekttransistor2014-01-08T18:15:57Z<p>Besserwessi: /* Verwendung für Schaltanwendungen */</p>
<hr />
<div>Der Feldeffekttransistor, meist als FET (Field Effect Transistor) bezeichnet, ist ein unipolarer [[Transistor]]. Unipolar deshalb, weil im Gegensatz des bipolaren Transistors, je nach Typ, entweder nur Löcher oder Elektronen am Stromtransport beteiligt sind. <br />
<br />
Der FET hat 3 Anschlüsse, Source (Zufluss, Quelle), Gate (Tor) und Drain (Abfluss). Ein vierter Anschluss Bulk (Substrat) ist bei Einzeltransistoren i.d.R. mit Source verbunden und nicht extra herausgeführt.<br />
<br />
Die Steuerspannung zwischen Gate und Source erzeugt ein elektrisches Feld. Die dazugehörigen Ladungen beeinflussen die Leitfähigkeit des Source-Drain-Kanals des Feldeffekt-Transistors. Je nach benutzter Isolierung wird zwischen MOSFET und JFET (Junction- oder Sperrschicht-FET) unterschieden. <br />
<br />
Es gibt folgende Formen von FETs:<br />
* Sperrschicht-Feldeffekt-Transistor (JFET)<br />
* Metalloxidhalbleiter-Feldeffekt-Transistor (MOSFET)<br />
* dual Gate MOSFET : Spezialltype, hauptsächlich für HF<br />
* Schottky-Feldeffekt-Transistor (MESFET) - Spezialtype für HF<br />
* High Electron Mobility Transistor (HEMT) - Spezialtype für HF, > 1 GHz<br />
* Ionen-Sensitiver Feldeffekt-Transistor (ISFET) - als Sensor<br />
<br />
==Schaltsymbole==<br />
;N-MOSFET (Anreicherungstyp, selbstsperrend): [[Bild:Schaltsymbol NFET.png]]<br />
<br />
;P-MOSFET (Anreicherungstyp, selbstsperrend): [[Bild:Schaltsymbol PFET.png]]<br />
<br />
;N-Kanal JFET (Verarmungstyp, selbstleitend): [[Bild:Schaltsymbol JFETN.png]] [[Bild:Schaltsymbol JFETN2.png]]<br />
<br />
;P-Kanal JFET (Verarmungstyp, selbstleitend): [[Bild:Schaltsymbol JFETP.png]] [[Bild:Schaltsymbol JFETP2.png]]<br />
<br />
==JFET==<br />
JFETs nutzen einen in Sperrrichtung betriebenen p-n-Übergang, um das elektrische Feld zu bilden. Theoretisch kann er auch in Flussrichtung betrieben werden, was allerdings den Vorteil der leistungslosen Ansteuerung zunichte macht. Bei vielen JFETs gibt es keinen, oder wenig, Unterschied zwischen Drain und Source. Sie können also für beide Stromrichtungen benutzt werden.<br />
Ohne Gate-Source Spannung ist ein JFET leitend. Durch Spannung in Sperrichtung wird der Strom reduziert und erreicht bei der Abschnürrspannung (sog. pinch-off voltage) schließlich 0A. Bei kleinen Drain-Source Spannung verhält sich der JFET wie ein Spannungsgesteuerter Widerstand. Bei Drain-Source Spannung über etwa 1-2 V verhält sich ein JFET wie eine Spannungsgesteuerte Strombegrenzung. JFETs werden hauptsächlich als Schalter für Signalspannungen und als schnelle hochohmige Verstärker eingesetzt. Es gibt viele Operationsverstärker mit JFET Eingängen.<br />
Gebräuchliche N-Kanal JFETs sind: BF245, BF256, 2N4416. P-Kanal JFETs sind selten, es gibt sie aber.<br />
<br />
==MOSFET==<br />
MOSFETs haben eine dünne Oxidschicht als Isolierung zwischen Gate und dem Kanal. MOSFETs können so hergestellt werden, dass sie ohne Gate-Source Spannung sperren (Anreicherungs- bzw. Enhancement-Typ) oder leiten (Verarmungs- bzw. Depletion-Typ). Die einzeln erhältlichen MOSFETs sind aber ohne Gate-Source-Spannung fast alle sperrend (Enhancement-Typ), P-Channel gibt es wohl nur als Enhancement-Typ. Ab einer Gate-Source-Spannung von z.B. +2 V (N-Channel FET) steigt die Leitfähigkeit bzw. der Drain-Source-Strom an. Für den maximalen Strom sind typisch 10 V oder bei sogenannten Logic-Level Mosfets ca. 4 V nötig. Die maximal zulässige Gate-Source-Spannung liegt je nach Typ bei etwa 20-30 V, bei Logic-Level FETs zum Teil darunter.<br />
Bei einigen wenigen MOSFETs ist das Substrat als vierter Anschluss separat herausgeführt. Die Steuerspannung ist dann die Spannung zwischen Gate und Substrat. Sonst ist das Substrat intern mit Source verbunden und der MOSFETs enthält damit eine Diode zwischen Drain und Source. Zum Teil wird die Diode im Schaltungssymbol mit eingezeichnet. Es kann also Drain und Source hier nicht vertauscht werden, wenn mehr als 0,5 V Spannung anliegen. Die integrierte Diode kann einen ähnlich hohen Strom wie der FET vertragen und kann bei Brückenschaltungen als Freilaufdiode genutzt werden. Besonders kleine MOSFETs sind empfindlich gegen elektrostatische Aufladungen.<br />
<br />
== Verwendung für Schaltanwendungen ==<br />
MOSFETs werden oft für Schaltanwendungen verwendet, da sie größere Leistungen als bipolare Transistoren schalten können und geringere Verluste haben. Anders als bipolare Transistoren können MOSFETs als Schalter (d.h. bei kleiner Drain-Source Spannung, oder ausgeschaltet) problemlos parallelgeschaltet werden.<br />
<br />
Ein weiterer, wesentlicher Vorteil ist, dass sie leistungslos geschaltet werden. Wenn das Gate einmal auf eine bestimmte Spannung aufgeladen wurde, dann ist keine weitere Steuerleistung nötig. Bei bipolaren Transistoren muss die ganze Zeit ein relativ hoher Basisstrom fliessen. Allerdings hat das Gate eine relativ hohe Kapazität (ca. 1 nF für einen 10 A MOSFET). Um die Verluste beim Schalten zu minimieren, muss das Gate schnell geladen und wieder entladen werden. Dafür ist kurzzeitig ein relativ hoher Strom nötig. Aus diesem Grund gibt es fertige Treiberbausteine, wie den ICL7667, die das Gate sehr schnell umladen können. Ein schnelles Schalten des Mosfets ist insbesondere dann wichtig, wenn der Mosfet mit einer hohen Frequenz an- und ausgeschaltet wird (z.B. [[PWM]], Schaltnetzteile). <br />
<br />
Andererseits darf das Schalten auch nicht schneller erfolgen als der Rest der Schaltung es verträgt. Begrenzend sind hier z.B. ungewollte Induktivitäten, die Erholzeiten von Dioden und die Gefahr von Funkstörungen. Für eine definierte Schaltzeit wird oft ein kleiner Widerstand (ca. 100 Ohm) vor das Gate geschaltet. Wichtig ist der Widerstand besonders dann, wenn der Aufbau nicht für hohe Frequenzen ausgelegt ist, denn sonst besteht die Gefahr, dass es zwischenzeitlich zu HF-Schwingungen und damit Funkstörungen kommt. Wenn MOSFETs parallel geschaltet werden braucht jeder MOSFET getrennt einen Widerstand am Gate. <br />
<br />
In der Praxis werden wesentlich häufiger N-Kanal Mosfets als P-Kanal Mosfets verwendet. Das liegt daran, dass sich die Elektronen im Halbleiter leichter bewegen können als Löcher. Aus diesem Grund haben N-Kanal Mosfets bei gleicher Chipfläche einen geringeren RDS_ON und damit geringere Verluste. <br />
<br />
Die folgende Schaltung zeigt die Verwendung eines MOSFETs zum Regeln eines Motors über [[PWM]]. Der eingezeichnete BS170 ist allerdings nur für kleine Lasten geeignet.<br />
<br />
[[Bild:fetschaltstufe.jpg|center]]<br />
<br />
===Auswahl des passenden MOSFET===<br />
Es gibt eine verwirrend große Auswahl an MOSFETs. Die im Datenblatt genannte Strombelastbarkeit ist eher theoretischer Natur: bei dem Strom wird der sehr gut gekühlte MOSFET bereits grenzwertig heiß. Genutzt wird entsprechen nur einer kleiner Teil davon - wichtiger ist meist der On-Widerstand: darüber berechnen sich die Verluste im eingeschalteten Zustand. Hierbei ist allerdings meist der Widerstand bei erhöhter Temperatur von Bedeutung, und der kann etwa um den Faktor 1,5-2 höher liegen als der oben im Datenblatt genannte. Größere MOSFETs mit kleinerem Widerstand haben allerdings auch eine größere Gate-kapazität und damit mehr Verluste beim Umschalten. Je höher die Schaltfrequenz, desto wichtiger ist es hier einen guten Kompromiss zu finden. Eine unnötig hohe Spannungsfestigkeit führt zu hohem On-Widerstand bzw. unnötig großer Gate-Kapazität und meist hohem Preis. Die Spannungsfestigkeit muss hoch genug sein, mehr als etwa 50% Reserve über der höchsten möglichen Spannung sind aber eher nicht nötig. Lediglich bei Linearbetrieb eine eine deutlich höhere Spannungsfestigkeit oft sinnvoll, da Typen für geringe Spannung oft schlecht (oft nicht einmal spezifiziert) dafür geeignet sind. Hier muss man auf die SOA Kurve (meist relativ weit hinten im Datenblatt) achten: Sie gibt an bei welcher Spannung (Drain-Source Spannung am MOSFET - nicht nicht die Versorgungsspannung) wie viel Strom fließen darf.<br />
<br />
Ein weiterer wichtiger Parameter ist die Schwellspannung, bzw. welche Spannung für die Ansteuerung am Gate nötig ist. Zum Schalten wird eine Spannung deutlich (z.B. 2-3 V) über der Schwellspannung benötigt. MOSFETs, die zur Steuerung mit 5 V geeignet sind, sogenannte Logic level MOSFETs, erkennt man meist daran, dass im Datenblatt der On-Widerstand bei 4-5 V angeben ist. Der dazu angegebene Strom ist auch eine Hinweis auf den Strom der maximal realistisch nutzbar ist. Für den Betrieb mit nur 3,3 V Gate-Spannung gibt es spezielle Low-Level MOSFETs. <br />
<br />
Als Hilfe bieten viel Händler und Hersteller eine parametrische Suche, um die Auswahl z.B. auf die passende Spannungsfestigkeit, Gehäuseform und den Preisrahmen zu begrenzen.<br />
<br />
Sucht man etwa einen MOSFET um einen MOTOR mit maximal 3 A bei 12 V zu steuern, so genügt ein MOSFET für 20 V oder 30 V Spannungsfestigkeit.<br />
<br />
== Bauform Beispiel ==<br />
[[Bild:fetbs170.gif|center]]<br />
<br />
<br />
= Weblinks =<br />
[http://www.mikrocontroller.net/articles/MOSFET-Übersicht |Liste von MOSFET Typen (Mikrocontroller.net)]<br />
<br />
[[Kategorie:Elektronik]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Pulsweitenmodulation&diff=23494Pulsweitenmodulation2014-01-08T14:18:31Z<p>Besserwessi: /* PWM als D/A Wandler */</p>
<hr />
<div>Die Pulsweitenmodulation (oft mit PWM abgekürzt) wird vornehmlich zum Ansteuern größerer Lasten wie z.B. Motoren verwendet. [[Microcontroller]] haben daher oft bereits spezielle PWM-Ausgänge integriert. Bei der Pulsweitenmodulation werden Impulse mit voller Spannung aber variabler Breite an die Last gesendet. Ein Rechtecksignal mit konstanter Frequenz wird also mit einem bestimmten Tastverhältnis moduliert. Eine PWM ist also charakterisiert durch ihre '''Frequenz''' und ihr '''Tastverhältnis''' (duty cycle).<br />
<br />
Vorteil dieser Ansteuerung ist, dass weniger Leistung verbraucht wird, da nicht permanent eine Eingangsspannung anliegt, die von einer Elektronik auf die gewünschte Motorspannung heruntergeregelt wird, sondern der Motor durch die Breite der Schaltimpulse gesteuert wird.<br />
<br />
http://www.roboternetz.de/wiki/uploads/Main/pwm.gif<br />
<br />
Diese drei Skizzen demonstrieren, wie ein Motor mit unterschiedlicher Pulsweite in drei verschiedenen Geschwindigkeit geregelt wird. In der Praxis ist die Pulsweite oft in 255 Schritten (8 Bit) und mehr regelbar. Gut zu erkennen ist in der Skizze, dass die eigentliche Grundfrequenz bei der Pulsweitenmodulation nicht verändert wird, sondern lediglich das Verhältnis der Ein- und Ausschaltzeit pro Welle (<math>T_\mathrm{on}</math> und <math>T_\mathrm{off}</math>). Die Modulationsfrequenz kann dabei variieren. Oft findet man Anwendungen im Frequenzbereich zwischen mehreren hundert Hertz (z.B. Bremslichter bei Autos) bis in den Bereich von 100 kHz (Schaltregler).<br />
<br />
Durch die Pulsweitenmodulation ist es möglich, dass auch Verbraucher mit niedrigeren Nennspannungen an weit höheren Spannungen betrieben werden können. Dadurch, dass man die Pulsweite reduziert, reduziert man die im Mittel am Verbraucher anliegende Spannung bzw. den durch den Verbraucher fliessenden Strom bzw. die vom Verbraucher aufgenommene Leistung. Jedoch kann nicht bei allen Verbrauchern PWM angewandt werden. Dazu muss der Verbraucher eine gewisse Trägheit besitzen und die PWM eine hinreichend hohe Frequenz haben. Eine [[Diode#Leuchtdiode|Leuchtdiode]] kann durchaus durch Spannungsspitzen oberhalb ihrer Nennspannung zerstört werden.<br />
<br />
Für die Durchschnittsspannung gilt<br />
:<math><br />
\overline{U_\mathrm{pwm}} <br />
= U_\mathrm{in} \cdot \frac{t_\mathrm{on}}{t_\mathrm{on}+t_\mathrm{off}} <br />
= U_\mathrm{in}\cdot t_\mathrm{on} \cdot f_\mathrm{pwm}<br />
</math><br />
<br />
Zu beachten ist, dass es sich hierbei um die mittlere Spannung am Verbraucher handelt. Diese ist nicht unbedingt ausschlaggebend für dessen Leistungsaufnahmen!<br />
<br />
Der große Vorteil einer PWM ist, dass die "herabgesetzte" Spannung nicht wie bei herkömmlichen Regelungen am Transistor oder einem Widerstand in Wärme umgesetzt wird. Aber auch in einer PWM-geregelten Schaltung entstehen Verlustleistungen durch die endlichen Widerstände der Schaltelemente wie ([[Feldeffekttransistor|Feldeffekt]]-)[[Transistor|Transistoren]]. Diese Verlustleistung setzt sich aus den Verlusten am on-Widerstand des Schalters und den Umschaltverlusten zusammen. Die Umschaltverluste sind in erster Näherung proportional zur Schaltfrequenz und der Zeit <math>t_\mathrm{switch}</math>, die benötigt wird, um zwischen An- und Auszustand umzuschalten. Als Näherung:<br />
:<math><br />
P_\mathrm{Verl} \approx t_\mathrm{switch} \cdot f_\mathrm{pwm} \cdot U \cdot I + \overline{I^2} \cdot R_\mathrm{on}<br />
</math><br />
Darüber hinaus trägt eine evtl. benötigte [[Diode#Freilaufdiode|Freilaufdiode]] ebenfalls zu den Verlusten bei.<br />
<br />
== PWM und induktive Last ==<br />
<br />
Beispielschaltungen, die PWM nutzen, findet man unter [[Getriebemotoren Ansteuerung]].<br />
Auch bei [[Schrittmotoren ]] macht man sich diese Technik bei modernen Schaltkreisen und Steuerungen zunutze. Hier bezeichnet man sie oft auch als Chopper-Regelung, was letztlich aber sehr ähnlich ist. Induktive Lasten sind vor allem Motoren und Elektromagneten. Teilweise werden auch zusätzliche Induktivitäten vor eine Last (z.B. LED, Lüfter) geschaltet. Die Schaltung ist dann ähnlich einem vereinfachten [[Spannungsregler#Schaltregler|Schaltnetzteil]].<br />
<br />
{{FarbigerRahmen|<br />
Bei induktiven Lasten wie einem Motor ist zusätzlich darauf zu achten, dass während der off-Zeit der Strom durch die Last weiterfließen kann. Dazu wird der Last eine so genannte [[Diode#Freilaufdiode|Freilaufdiode]] parallel geschaltet. Ohne eine Freilaufdiode würden beim Abschalten Spannungsspitzen durch die Induktionsspannung entstehen, die zusätzlich Probleme bereiten und die Schaltung sogar schädigen können.<br />
}}<br />
<br />
Durch die Induktivität kann sich der Strom nicht sofort ändern, sondern steigt in der Anphase an und fällt in der Ausphase wieder ab. Der Strom in der Ausphase fließt über die Freilaufdiode. Dadurch wird der mittlere Strom durch die Last größer als der mittlere Strom der aus der Spannungsquelle entnommen wird. Wenn die PWM-Frequenz genügend hoch ist, fällt der Strom dabei nicht wieder auf Null. In diesem Fall muß man auf die Geschwindigkeit der Freilaufdiode achten. Oft benutzt man daher Schottkydioden, die auch eine niedrige Flussspannung haben. <br />
<br />
Wenn die Frequenz hinreichend hoch ist, schwankt der Strom nur gering um den mittleren Strom. Der mittlere Strom wird dabei näherungsweise von der mittleren Spannung bestimmt.<br />
<br />
;Beispiel: Wenn die Spannung <math>U_\mathrm{in}</math> = 12V und die Pulsweite genau 50% beträgt, so bedeutet das, dass der Motor nur für die halbe Zeit mit der Spannung versorgt wird. Dies wäre vergleichbar mit dem Fall, dass der Motor für die volle Zeit mit einer kontinuierlichen Gleichspannung von 6V betrieben würde.<br />
<br />
== PWM und ohmsche Last ==<br />
Möchte man einen ohmschen Verbraucher an einer höheren Spannung als seiner Nennspannung betreiben, dann ist das über eine PWM möglich, wenn der Verbraucher hinreichend träge ist im Vergleich zur PWM-Periode. Die ist etwa für Glüh- oder Heizfäden der Fall.<br />
<br />
Bei ohmscher Last wird man also wie bei induktiver Last (Motor) die Leistung so einstellen wollen, wie sie bei Nennspannung auftritt. Für die PWM-Spannung bedeutet dies, dass ihr Effektivwert gerade der Nennspannung des Verbrauchers entsprechen soll. Daher muss gelten:<br />
<br />
<math><br />
t_\mathrm{on} \cdot (U_\mathrm{in}-U_\mathrm{Tr})^2 <br />
\,=\, (t_\mathrm{on}+t_\mathrm{off}) \cdot U_\mathrm{eff}^2<br />
</math><br />
<br />
Dabei bezeichnet ''U''<sub>Tr</sub> die Spannung, die am Transistor abfällt. Damit gilt für das Tastverhältnis (duty cycle) der PWM:<br />
<br />
:<math><br />
\mathrm{duty} <br />
\,=\, \frac{t_\mathrm{on}}{t_\mathrm{on}+t_\mathrm{off}} <br />
\,=\, \left(\frac{U_\mathrm{eff}}{U_\mathrm{in}-U_\mathrm{Tr}}\right)^2 <br />
</math><br />
<br />
Die mittlere Spannung an der Last, wie man sie zum Beispiel mit einem Voltmeter angezeigt bekommt, ist<br />
<br />
:<math><br />
\overline{U} = \frac{U_\mathrm{eff}^2}{U_\mathrm{in}-U_\mathrm{Tr}}<br />
</math><br />
<br />
Je höher die Eingangsspannung, desto kleiner ist also die Spannung, die das Voltmeter (auf "Gleichspannung "bzw. "DC" eingestellt) anzeigt!<br />
<br />
;Beispiel: Ein Glühbirnchen mit einer Nennspannung von 6.3V soll an einer Spannung von 10V betrieben werden. Als Schalter dient ein npn-Transistor wie BC517. Es ergibt sich duty = 0.46 = 46% und ein DC-Voltmeter zeigt eine Spannung von ca. 4.25 Volt an. Bei der Rechnung wurde ein Spannungsabfall von 0.7 Volt über der Collector-Emitter-Strecke des Transistors angenommen.<br />
<br />
== PWM bei LEDs ==<br />
LEDs haben eine stark nichtlineare Kennline. Sie vertragen zwar kurzzeitig Ströme bis zum etwa 10 fachen des Nennstromes, aber dennoch darf die Spannung auch kurzzeitig nicht wesentlich höher sein als im Normalbetrieb. Man darf daher auch bei der PWM Steuerung nicht auf einen Vorwiderstand (oder Ersatzweise eine Induktivität und Freilaufdiode) verzichten. Die Helligkeit der LEDs hängt im wesenlichen vom mittleren Strom ab. Man hat also nur dann weniger Verlustleistung als bei einer linearen Ansteuerung, wenn man eine Induktivität und Freilaufdiode nutzt. Wegen des einfachen Aufbaus ist die PWM Regelung von LEDs mit Vorwiderstand dennoch sinnvoll.<br />
<br />
== PWM als D/A Wandler ==<br />
<br />
Mit einem [[Filter_(Elektronik)|Tiefpassfilter]] (z.B. RC Glied) kann ein PWM-Ausgang auch als einfacher, langsamer Digital-Analog-Wandler benutzt werden. Meistens sollte die PWM-Frequenz hier möglichst hoch sein. <br />
Die Auslegung des Tiefpaßfilters ist meistens so, dass die restliche Wechselspannung kleiner ist als ein LSB-Schritt wird. Für die Anwendung als 8 Bit D/A sollte also die Amplitude weniger als 1/256 sein. Für ein einfaches RC Glied muss die Grenzfrequenz dazu unter 1/128 der PWM Frequenz liegen. Besonders für höhere Auflösung werden oft Filter höherer Ordnung gebraucht, z.B. als aktiven Tiefpass 2.Ordnung mit einem Operationsverstärker. Damit hat man auch gleich einen belastbaren Ausgang. <br />
Die Genauigkeit als D/A Wandler ist begrenzt durch die Stabilität der Spannung mit der das PWM Signal erzeugt wird und Nichtlinearitäten der Ausgänge. Vom Prinzip her ist die Wandlung monoton - d.h. ein höherer PWM Wert erzeugt auch immer ein höhere Spannung am Ausgang. <br />
<br />
{{Ausbauwunsch|Kombination zweier gewichteter PWM-Kanäle, z.B. 2 mal 8-Bit PWM mit Widerständen 1:256 gewichtet = 16Bit-PWM-DAC; Vorteil der hohen Abtastfrequenz; funktioniert theoretisch => wo liegen die Probleme in der Praxis? Ein- und Ausschwingvorgänge der Porttreiber? Controllerabhängigkeiten? ==> wäre super, wenn jemand hier noch etwas beitragen könnte, im Internet gibt es zwar ein bißchen was dazu, aber keiner verrät warum es meist nicht oder nur mit Tricks klappt}}<br />
<br />
== PWM per Software ==<br />
<br />
Um eine PWM in Software nachzubilden, braucht man ein paar ganzzahlige Variablen sowie eine Zeitbasis, die es erlaubt, die PWM-Routine in regelmäßigen Zeitabständen (Ticks) aufzurufen:<br />
;max: Gibt die Auflösung der PWM an. Für max=100 hat die PWM eine Auflösung von 1% und es können 101 verschiedene duty cycle eingestellt werden (von 0 bis 100)<br />
;duty: Codiert den duty cycle; kann Werte von 0..max annehmen. Ein Wert von 0 entspricht 0%, ein Wert von max entspricht 100%.<br />
;tick: Eine Variable, die zu jedem Tick enhöht wird. Sie entspricht dem Timer-Wert bei einer Hardware-PWM.<br />
;pwm: Der Ausgabewert. Ist entweder 0 oder 1.<br />
<br />
Der Code ist als Pseudocode angegeben und sollte problemlos zu verstehen sein<br />
<pre><br />
# tick um 1 erhöhen und Überlauf beim Erreichen von max (modulo max)<br />
<br />
tick := tick + 1<br />
<br />
IF tick >= max<br />
THEN<br />
tick := 0<br />
END IF<br />
<br />
# tick gegen duty vergleichen und pwm entsprechend setzen<br />
<br />
IF tick < duty<br />
THEN<br />
pwm := 1<br />
ELSE<br />
pwm := 0<br />
END IF<br />
</pre><br />
<br />
und als C-Code<br />
<pre><br />
unsigned int tick,duty=0;<br />
tick++;<br />
if (tick > max)<br />
{<br />
tick=0;<br />
}<br />
//PWM mit duty vergleichen und ggf. PWM an/aus schalten<br />
if(tick < duty)<br />
{<br />
PORTB |= _BV(PB1); <br />
}<br />
else<br />
{<br />
PORTB &= ~ _BV(PB1); <br />
}<br />
</pre><br />
<br />
==Siehe auch==<br />
* [[Chopper Betrieb]]<br />
* [[Bascom und PWM]]<br />
* [[Getriebemotoren Ansteuerung]]<br />
<br />
[[Kategorie:Grundlagen]]<br />
[[Kategorie:Elektronik]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Avr&diff=23481Avr2013-12-31T16:19:59Z<p>Besserwessi: /* Hardware */</p>
<hr />
<div>[[Bild:AtmelController.jpg|thumb|Beispiel eines AVR Controllers]]<br />
'''AVR''' ist eine 8-Bit [[Microcontroller]]-Familie mit RISC-Architektur.<br />
Im Gegensatz zu vielen anderen Microcontroller-Architekturen hat die AVR-Architektur keine Vorgänger. Sie ist ein komplettes Neudesign, das Anfang der 90-Jahre an der Universität von Trondheim/Norwegen entwickelt und vom (bis heute einzigen) Hersteller [[Atmel]] aufgekauft wurde. Es gibt eine ganze Serie von AVR-Controllern. Sie alle werden ähnlich programmiert, haben vergleichbaren Befehlssatz und physikalische Eigenschaften, bieten jedoch unterschiedliche Features und Peripherie. <br />
<br />
Es gibt zahlreiche und kostenlose Entwicklungssysteme in den Sprachen Basic, C/C++, Pascal und Assembler für diese Controller-Familie. <br />
<br />
== Wofür steht AVR? ==<br />
"AVR" steht angeblich für ''Advanced Virtual RISC'' (in einem Paper der Entwickler des AVR-Kerns Alf Egin Bogen und Vegard Wollan). Laut [[Atmel]] bedeutet es nichts.<br />
<br />
== Hardware ==<br />
AVR-Controller besitzen eine zweistufige Pipeline (fetch and execute), die es ermöglicht, die meisten Befehle innerhalb eines einzigen Prozessortaktes auszuführen. Dadurch ist ein AVR wesentlich schneller als etwa 8051-Controller, bei denen der Prozessortakt intern noch durch 12 geteilt wird.<br />
<br />
*AVR-Kern<br />
** Harvard-Architektur (getrennter Befehls- und Datenspeicher) <br />
** 8-Bit Architektur ist für Hochsprachen (C) optimiert <br />
** 32 Register, davon 6 als 3 Pointerregister, kein Akkumulator<br />
** Lineares Speichermodell (keine Segmentierung bis 128 kBytes Programmspeicher)<br />
* In-System programmierbar: die Controller können sehr einfach über ein Programmierkabel (oft ISP-Kabel genannt), das mit dem PC verbunden wird, programmiert werden &ndash; auch dann, wenn sie sich nicht in einer Schaltung befindet.<br />
* integrierter Flash-Speicher für Programm <br />
* umfangreiche Peripherie<br />
** [[Watchdog]], [[Bootloader]]-Support, verschiedene [[Stromspar-Modi(AVR)|Stromspar-Modi]], Brownout-Erkennung, Interner Oszillator<br />
** EEPROM-Datenspeicher<br />
** 8- und 16-Bit-Timer/Counter mit [[PWM]], Capture/Compare, externe Betaktung, asynchrone Operation<br />
** Kommunikation: [[UART|USART]], [[SPI]], [[I2C]] ([[TWI]])<br />
** Analog-Comparator, Analog-Digital-Wandler <br />
** unterschiedlichste externe und interne Interrupt-Quellen (UART, SPI, Timer, A/D-Wandler, Analog-Comparator, ...)<br />
** JTAG (Debugerinterface) (Teilweise)<br />
* AVR Typen (AT90 "Classic AVR", ATtiny, ATmega), trotzdem sehr ähnlich<br />
* erhältlich in unterschiedlichen Gehäusen, idR Durchsteck und als [[SMD]]<br />
* Viele Entwicklungsboards erhältlich, z.B. das Roboternetzboard [[RN-Control]]<br />
<br />
==Einige Pinbelegungen der populärsten AVR-Controller==<br />
(in etwa nach Leistungsfähigkeit sortiert)<br />
<br />
* [[AT90S2313]]<br />
<br />
[[Bild:at90s2313tiny.png|center]]<br />
<br />
<br />
* [[Atmel Controller Mega8]]<br />
* [[Atmel Controller Mega48 Mega88 Mega168]]<br />
<br />
[[Bild:mega8kompatibel.png|center]]<br />
<br />
* [[Atmel Controller Mega16 und Mega32]]<br />
<br />
[[Bild:Mega1632.gif|center]]<br />
<br />
<br />
* [[Atmel Controller Mega128]] ([[SMD]]-Chip)<br />
<br />
[[Bild:mega128pin.gif|center]]<br />
<br />
<br />
----<br />
<br />
=== Die AVR-Pin-Bezeichnungen und deren Funktion ===<br />
Die meisten Ports sind doppelt belegt und besitzen neben der normalen Port-Funktion noch eine Sonderfunktion. Die verschiedenen Pinbezeichnungen und Sonderfunktionen werden hier beschrieben:<br />
<br />
{| {{Blauetabelle}}<br />
|+ '''Tabelle: Die AVR-Pin-Bezeichnungen und deren Funktion'''<br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| Versorgungs- und Referenzpins, Reset<br />
|-<br />
|'''[[VCC]]''' <br />
| Versorgungsspannung von 2,7 V bis 5,5 V bei den L-Varianten (low power), ansonsten 4,5V bis 5,5 V. Neuere AVR ab 2,7 V und ab 1,8 V in V-Variante.<br />
|-<br />
|'''[[GND]]''' <br />
|Masse <br />
|-<br />
|'''AREF''' <br />
|Referenzspannung für den Analog-Digital-Wandler. Auch die interne Bandgap-Referenzspannung kann über diesen Pin entstört werden (dann KEINE externe Spannung an diesen Pin geben (Kurzschluss)!). <br />
|-<br />
|'''AGND''' <br />
|Analoge Masse für AD Wandler und dazugehörige Ports. Sollte in aller Regel mit GND verbunden werden.<br />
|-<br />
|'''AVCC''' <br />
| <br />
Die Betriebsspannung für den Analog-Digital-Wandler (und einiges mehr) (siehe Beschaltungsskizze). Die Pins AVCC und AGND müssen immer beschaltet werden, selbst wenn man den AD-Wandler und Port A nicht benutzt. <br />
|-<br />
|'''RESET''' <br />
|Rücksetz-Eingang, intern über einen [[Pullup]] mit VCC verbunden. Ein LOW–Pegel an diesem Pin für die Dauer von mindestens zwei Zyklen des Systemtaktes bei aktivem Oszillator setzt den Controller zurück. Rücksetzen der Ports erfolgt unabhängig von einem evtl. anliegenden Systemtakt.<br />
|-<br />
|'''PEN'''<br />
|Programming Enable - Diesen Pin gibt es nur beim Mega128/64 u.ä. Wird dieser Pin beim Power-On Reset nach Masse gezogen, geht der Controller in den [[ISP]] Programmiermodus. Man kann ihn also alternativ zu Reset verwenden. In der Regel verwendet man aber die Reset-Leitung und PEN sollte man direkt mit VCC verbinden.<br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| System-Takt<br />
|-<br />
|'''XTAL1''' <br />
|Eingang des internen Oszillators zur Erzeugung des Systemtaktes bzw. Eingang für ein externes Taktsignal, wenn der interne Oszillator nicht verwendet werden soll bzw. Anschluss von Quarz/Keramik-Resonator/RC-Glied.<br />
|-<br />
|'''XTAL2''' <br />
|Anschluss von Quarz oder Keramik-Resonator oder Ausgang des integrierten Oszillators zur Nutzung als Systemtakt (Je nach Fuse-Einstellungen). <br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| Digitale bidirektionale I/O-Ports<br />
|- <br />
| colspan="2"| Jeder Pin der Ports kann individuell als Eingang oder Ausgang konfiguriert werden. Die I/O-Ports sind maximal 8 Bit breit und verfügen ja nach AVR-Typ über eine unterschiedliche Anzahl von Pins. An jedem als Eingang (Input) geschalteten Pin gibt es zuschaltbare [[Pullup]]-Widerstände, die teilweise auch bei aktivierter Sonderfunkton verfügbar sind.<br />
<br />
Bei eingeschalteten Sonderfunktionen wie UART, SPI, ADC, etc. sind die entsprechenden Pins nicht als "normale" digitale I/O verwendbar, sondern dienen der Sonderfunktion. Die Anzahl der als I/O verwendbaren Pins ist auch abhängig von den Fuse-Einstellungen (Vorsicht beim Umstellen, Handbuch GENAU lesen!).<br />
|-<br />
|'''PA 0 – 7''' || Port A <br />
|-<br />
|'''PB 0 – 7''' || Port B <br />
|-<br />
|'''PC 0 – 7''' || Port C <br />
|-<br />
|'''PD 0 – 7''' || Port D <br />
|-<br />
|'''PE 0 – 7''' || Port E <br />
|-<br />
|'''PF 0 – 7''' || Port F <br />
|-<br />
|'''PG 0 – 7''' || Port G <br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| Externe Interrupts<br />
|- <br />
| colspan="2"| Die PCINT-Interrupts gibt es nur für neuere AVRs wie den [[ATmega88]]. Falls die Anzahl an externen Interrupts nicht ausreicht, kann evtl. auch andere Hardware dafür eingesetzt werden, etwa der Analog-Comparator mit interner Bandgap-Referenz, falls er anderwärtig nicht gebraucht wird.<br />
|-<br />
|'''INT0''' ||Externer Interrupt 0<br />
|-<br />
|'''INT1''' ||Externer Interrupt 1<br />
|-<br />
|'''INT2''' ||Externer Interrupt 2 <br />
|-<br />
|'''PCINTx''' ||Pin-Change Interrupt<br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| [[Timer]] und [[PWM]]<br />
|-<br />
|'''T0''' <br />
|Timer 0: externer Takteingang.<br />
|-<br />
|'''T1''' <br />
|Timer 1: externer Takteingang.<br />
|-<br />
|'''OC0''' <br />
|PWM bzw. Output Compare Ausgang des Timers 0 <br />
|-<br />
|'''OC1A''' <br />
|Ausgang für die Compare-Funktion des integrierten Zeitgeber- / Zählerbausteines <br />
Der erste PWM-Ausgang des Timers1. Er kann zum Regeln der Bot-Motorgeschwindigkeit benutzt werden. <br />
|-<br />
|'''OC1B''' <br />
|Ausgang für die Compare-Funktion des integrierten Zeitgeber- / Zählerbausteines <br />
Der zweite PWM-Ausgang des Timers1. Er kann zum Regeln der Bot-Motorgeschwindigkeit benutzt werden. <br />
|-<br />
|'''ICP1''' <br />
|Eingang für die [[Timer/Counter_(Avr)#Input_Capture|Capture-Funktion]] des integrierten Zeitgebers / Zählerbausteines <br />
<br />
|-<br />
|'''OC2''' <br />
|[[Pwm]] bzw. Output Compare Ausgang des Timers2. Er kann zum Regeln der Bot-Motorgeschwindigkeit benutzt werden. <br />
|-<br />
|'''TOSC1, TOSC2''' <br />
|TOSC1 und TOSC2 sind Eingänge für den asynchronen Modus von Timer2. Sie sind vorgesehen für den Anschluss eines externen Uhrenquarzes ( 32.768 kHz ). Damit lassen sich zum Beispiel genaue Ein-Sekunden-Impulse für eine Uhr generien, sogar wenn der normale Takt im Power-save Modus aus ist.<br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| Analog-Digital-Wandler<br />
|-<br />
|'''ADC0''' bis '''ADC7''' <br />
|Eingänge des AD-Wandlers. Spannungen können hier gemessen werden oder an den Analog-Komparator weiter geleitet werden. <br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| Analog-Komparator<br />
|-<br />
|'''AIN0, AIN1''' <br />
|Die beiden externen Eingänge des Analog-Komparators. <br />
Mit AIN0(+) und AIN1(-) kann man zwei Spannungen miteinander vergleichen. Wenn die Spannung an AIN0 höher als bei AIN1 ist, liefert der Komparator "High", ansonsten ein "Low". Als interne Eingänge des Komparators können die Interne Bandgap-Referenzspannung oder Ausgänge des ADC-Multiplexers dienen.<br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| Serielle Schnittstelle ([[UART|USART]])<br />
|-<br />
|'''RXD''' <br />
|Eingang der Seriellen Schnittstelle (Receive Data), TTL-Pegel <br />
|-<br />
|'''TXD''' <br />
|Ausgang Serielle Schnittstelle (Transmit Data), TTL-Pegel <br />
|-<br />
|'''XCK''' <br />
|Taktsignal der USART im synchronen Mode (z.B. als SPI Master). <br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| [[SPI]]-Schnittstelle<br />
|-<br />
|'''SS''' <br />
|SPI-Interface – wird benötigt, um den µC als aktiven Slave auszuwählen<br />
|-<br />
|'''MOSI''' <br />
|SPI-Interface – Datenausgang (als Master) oder Dateneingang (als Slave), verwendet bei ISP (In-System-Programmierung)<br />
|-<br />
|'''MISO''' <br />
|SPI-Interface – Dateneingang (als Master) oder Datenausgang (als Slave), verwendet bei ISP (In-System-Programmierung)<br />
|-<br />
|'''SCK''' <br />
|SPI-Interface – Bustakt vom Master, verwendet bei ISP (In-System-Programmierung)<br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| [[I2C|I<sup>2</sup>C]]-Schnittstelle ([[TWI]])<br />
|-<br />
|'''SDA''' <br />
|I2C-Schnittstelle (Bus aus 2 Leitungen) Datenleitung <br />
|-<br />
|'''SCL''' <br />
|I2C-Schnittstelle (Bus aus 2 Leitungen) Clockleitung <br />
<!-------------------------------------------------------------------------------------><br />
|- {{Hintergrund1}}<br />
! colspan="2"| [[JTAG]]-Interface<br />
|-<br />
|'''TDI''' <br />
|JTAG-Debug Interface - Über dieses Interface kann man den AVR programmieren und debuggen. Die Schnittstelle ist ähnlich wie die SPI Schnittstelle und hat getrennte Dateneingangs- und Datenausgangsleitungen sowie eine Taktleitung. TDI ist die Dateneingangsleitung<br />
|-<br />
|'''TDO''' <br />
|JTAG-Debug Interface - TDO ist die Datenausgangsleitung des JTAG Interface<br />
|-<br />
|'''TMS''' <br />
|JTAG-Debug Interface<br />
|-<br />
|'''TCK''' <br />
|JTAG-Debug Interface <br />
|}<br />
<br />
== Timer/Counter ==<br />
Für Infos zu Timer und Counter siehe Artikel [[Timer/Counter (Avr)]].<br />
<br />
== Analog-Digital-Wandler ==<br />
Für Infos zu Analog-Digital-Wandler siehe Artikel [[ADC (Avr)]].<br />
<br />
== Analog-Komparator ==<br />
Für Infos zu Analog-Komparator siehe Artikel [[Analog Komparator (Avr)]].<br />
<br />
== TWI/I2C ==<br />
Für Details über das Two-wire Serial Interface (kurz [[TWI]]) siehe Artikel [[TWI]].<br />
<br />
== UART/USART ==<br />
Für Details über den UART/USART siehe Artikel [[UART]].<br />
<br />
== SPI - Serial Peripheral Interface ==<br />
Für Details über SPI siehe Artikel [[SPI]].<br />
<br />
Näheres zu SPI beim AVR siehe [[SPI (AVR)]].<br />
<br />
== USI - Universal Serial Interface ==<br />
Für Infos zu USI (Universal Serial Interface) siehe Artikel [[USI (Avr)]].<br />
<br />
== IO-PORTs ==<br />
Die IO-Port dienen dazu direkt digitale Werte auszugeben oder einzulesen. Zu jedem Port (im folgenden X für A,B,C,D,...) gehören 3 Register. Zum einem Port gehören bis zu 8 Pins (PX0,...,PX7).<br />
* DDRX : Datenrichtungsregister (1 = Ausgang, 0 = Eingang)<br />
* PORTX bzw. Portx bei BASCOM : Ausgaberegister. Für Ausgänge wird hier der Ausgabewert bestimmt. Für Eingänge wird hier der Pullup-Widerstand eingeschaltet (1) oder ausgeschaltet (0).<br />
* PINX bzw. Pinx bei BASCOM : Eingangsregister. Lesen gibt den Zustand am Pin (1 = high, 0 = low). Das gilt auch wenn der IO Pin als Ausgang funktioniert. Die Wirkung beim Schreiben in dieses Register hängt vom Typ ab (siehe Datenblatt). Bei älteren Typen passiert beim schreiben nichts. Bei einigen neueren Typen wird beim schreiben einer 1 das entsprechende Bit im Register PORTX umgedreht.<br />
<br />
Von außen gesehen kann der IO Pin also 4 Zustände haben: niederohmig an VCC (high), niederohmig an GND (low), Pullup an VCC und hochohmiger Eingang. Für die Ausgabe nutzt man in der Regel PORTX, für Eingänge immer PINX.<br />
<br />
== Die Fusebits ==<br />
Zur Konfigurierung eines AVR-Controllers werden Fusebits benutzt. Bei der Auslieferung neuer AVR Controller sind die Fusebits bereits vorkonfiguriert, in der Regel auf den internen RC Oszillator und etwa 1 MHz Frequenz. In vielen Fällen kann die Konfiguration unverändert bleiben. Bei den Typen Mega xxx bestimmen einige Fusebits beispielsweise, dass der interne Taktgeber aktiviert ist. Soll z.B. dagegen ein externer Quarz anschlossen oder die Taktfrequenz geändert werden, so müssen auch die Fusebits geändert werden. Auch das Deaktivieren des "[[On Chip Debugging]]" Modus ist oft notwendig, wenn alle Ports genutzt werden sollen. <br />
<br />
Die Fusebits werden in der Regel über die Software eingestellt, welche auch für das Übertragen des Programmcodes zuständig ist. Besonders einfach geht dies beispielsweise mit der Entwicklungsumgebung [[Bascom]]. Aber auch andere Programme wie [[PonyProg]] können für die Umstellung der Fusebits genutzt werden. Einmal eingestellte Fusebits bleiben bis zur erneuten Fusebit-Änderung erhalten. Der normale Programmiermodus verändert die Fusebits nicht. <br />
<br />
Je nach AVR Controllertyp sind unterschiedliche Fusebits (Einstellungen) vorhanden. Die genaue Beschreibung findet man im jeweiligen Datenblatt. Da aber falsch gesetzte Fusebit-Einstellungen zu den häufigsten Problemen gehören (siehe auch unter dieser Tabelle), liste ich hier die Funktion der üblichen Fusebits nochmals genauer auf:<br />
<br />
{| {{Blauetabelle}}<br />
|'''CKSEL0, CKSEL1, CKSEL2, CKSEL3'''<br />
|Die Kombination dieser 4 Fusebits bestimmt die Taktquelle des Controllers. Das kann eine interner Taktgenerator, ein Quarz, Quarzoszillator, RC-Glied und ähnliches sein.<br />
|-<br />
|'''JTAGEN'''<br />
|Hiermit wird die "[[On Chip Debugging]]" Schnittstelle aktiviert bzw. deaktiviert. Das sind die Bits mit den Bezeichnungen TDI, TDO, TMS und TCK. Möchte man diese Pins als normalen Port nutzen, so muss diese Schnittstelle immer deaktiviert werden. Alternativ kann man das JTAG aber auch per Software deaktivieren.<br />
|-<br />
|'''SUT0, SUT1'''<br />
|Die sogenannte StartUp-Zeit (PowerOn delay). Diese Einstellung muss abhängig von der Art des Taktgenerators eingestellt werden, genaueres im jeweiligen Datenblatt. <br />
|-<br />
|'''SPIEN'''<br />
|Hiermit kann die serielle [[AVR-ISP Programmierkabel|ISP-Programmierung]], welche die meisten Programmierkabel nutzen, deaktiviert werden. Dies sollte man lieber vermeiden, denn wenn dieser Programmiermodus deaktiviert wurde, kann nur noch der Parallel-Programmiermodus genutzt werden. Der Parallel-Programmiermodus benötigt jedoch ein spezielles Programmiergerät, das die wenigsten Bastler besitzen. ''Also Vorsicht!''<br />
|-<br />
|'''BODEN'''<br />
|Über dieses Bit wird der '''Brown-out Detector''' aktiviert bzw. deaktiviert. Dies ist eine Überwachung der Betriebsspannung, die dafür sorgt, dass bei zu geringer Spannung der Controller angehalten wird und dann ein ordentlicher RESET durchgeführt wird, wenn die Spannung wieder ausreicht. Dadurch wird verhindert, dass der Controller in einen undefinierten Zustand gerät (hängen bleibt), sich verrechnet oder versehentlich das EEPROM / Flash verändert. In der Regel sollte man daher den Brown-out Detector aktivieren.<br />
|-<br />
|'''BODLEVEL'''<br />
|Über dieses Bit (ggf. auch mehrere) wird festgelegt, ab welcher Spannung der Brown-out Detector anspricht. <br />
|-<br />
|'''BOOTRST'''<br />
|Gewöhnlich startet ein Programm im Controller nach einem RESET ab Adresse 0. Durch dieses Fusebit kann der Controller jedoch veranlasst werden, nach einem Reset einen sogenannten Bootloader-Bereich auszuführen. Ein [[Bootloader]] kann genutzt werden, um Controller über andere Schnittstellen (z.B. RS232) zu programmieren.<br />
|-<br />
|'''BOOTSZ0, BOOTSZ1'''<br />
|Der zuvor genannte Bootloaderbereich kann bei AVR-Controllern verschieden groß sein. Über diese beiden Bits können vier verschiedene Größen eingestellt werden. Siehe unter [[Bootloader]].<br />
|-<br />
|'''EESAVE'''<br />
|Dieses Bit legt fest, ob beim Programmieren des Controllers (man nennt es auch brennen) immer das EEPROM gelöscht werden soll.<br />
|-<br />
|'''CKOPT'''<br />
|Abhängig von den Einstellungen von CKSEL kann hier dir Oszillator-Verstärkung eingestellt werden. Genaueres im Datenblatt des jeweiligen Controllers.<br />
|-<br />
|'''CKDIV8'''<br />
|Bei neueren µCs (aber etwa Mega88) stellt dieses Bit den Teiler für den Takt ein. Default ist dieses Bit aktiv, so dass der Takt des internen Oszillators von 8 MHz auf 1 MHz geteilt wird.<br />
|-<br />
|'''WDTON'''<br />
|Schaltet den WatchDog-Timer beim Booten ein/aus. Dies ist auch per Software möglich<br />
|-<br />
|'''RSTDISBL'''<br />
|Durch dieses Bit kann man den RESET-Pin deaktivieren und dann als normalen I/O-Port nutzen. Aber Vorsicht! Da die RESET-Leitung beim Programmieren (Brennen) des Chips genutzt wird, kann man nach dessen Deaktivierung den Controller mit den üblichen [[AVR-ISP Programmierkabel|ISP-Adaptern]] nicht mehr programmieren. In diesem Fall könnte man zwar den Controlle noch mit speziellen Programmiergeräten im Parallelmodus programmieren, aber in der Praxis verfügen nur wenige Bastler über ein Programmiergerät, das dies leistet.<br />
|-<br />
|'''LB1, LB2'''<br />
|Das sind die sogenannten Lockbits, mit denen sich das Auslesen des Flash- als auch EEPROM-Speichers verhindern läßt. Zwar können andere Anwender immer noch Daten lesen, allerdings handelt es sich dabei nicht mehr um den wirklichen Inhalt sondern lediglich um wirre Datenbytefolgen. Programmierer, die den erarbeiteten Code vor Raubkopierern schützen wollen, nutzen diese Lockbits. Das Programmieren ist auch bei gesetzen Lockbits noch möglich. Der Bootloader-Bereich wird nicht durch die Lockbits geschützt.<br />
|-<br />
|'''BLB01, BLB02'''<br />
|Durch diese Bits kann der Code sogar vor dem Zugriff durch den Bootloader geschützt werden<br />
|-<br />
|'''BLB11, BLB12'''<br />
|Diese Bits schützen den Bootloaderbereich selbst<br />
|}<br />
<br />
Wie man die Fusebits mit [[Bascom]] einstellt, wird im Beitrag [[Bascom - Erstes Programm in den AVR Controller übertragen]] erläutert.<br />
<br />
''Autoren des Artikels: Frank, Luma''<br />
<br />
Fusebits verstellt auf Externer Oszillator gehört zu den sehr häufigen Fehlern insbesondere bei Anfängern. Eine Möglichkeit, diesen Fehler mit einem Minimum an Hardware (minimalistisch gehts mit nur 1 Widerstand an einer EIA232-Schnittstelle) zu reparieren, ist hier vorgestellt: [http://www.roboternetz.de/phpBB2/viewtopic.php?t=51685 Fuse irrtümlich auf extern Takt?].<br />
<br />
== Siehe auch ==<br />
* [[AVR-Einstieg leicht gemacht]]<br />
===Entwicklungsumgebungen===<br />
* [[Microsoft_Visual_Studio_2008_als_AVR_Entwicklungsumgebung| Microsoft Visual Studio]] - Die kostenlose "Express Edition" setzt auf WinAVR und auf den GCC auf, compiliert über custom-build und generiert ein Script für Ponyprog<br />
<br />
* [[Bascom]] - Basic-Entwicklungssystem<br />
* [[Bascom - Erstes Programm in den AVR Controller übertragen]]<br />
* [[Avr-gcc|avr-gcc]] - Leistungsfähiger AVR-Port des freien Compilers GCC<br />
* [[WinAVR]] - Freies, kostenloses Werkzeugpaket mit avr-gcc, binutils, tools ([[make]], [[Programmer's Notepad]], [[avrdude]], etc.) für MS-Windows.<br />
* [[Linuxdistribution_Avr-live-cd]]<br />
* [[AVR_Assembler_Einf%C3%BChrung|AVR Assembler Einführung (AvrStudio)]]<br />
* [http://www.mikroe.com/en/compilers/mikropascal/avr/ MikroPascal for AVR] Sehr gute kommerzielle Pascal Entwicklungsumgebung. Der Compiler ist auch für PIC und andere Controller verfügbar.<br />
<br />
=== Hardware ===<br />
* [[AVR-ISP Programmierkabel]] - Bauanleitung für die AVR Controller Programmierkabel<br />
* [[RN-Control]] - Eines der beliebtestet AVR-Boards im Roboternetz<br />
* [[RNBFRA-Board]] - Größeres Board mit zwei Atmel Controllern<br />
<br />
===Sonstiges===<br />
* [[Atmel]]<br />
* [[HEX Beispiel-Dateien für AVR]]<br />
* [[Bootloader]]<br />
* [[On Chip Debugging]]<br />
<br />
== Weblinks ==<br />
* [http://www.atmel.com/dyn/products/param_table.asp?family_id=607&OrderBy=part_no&Direction=ASC Aktuelle AVR Vergleichstabelle]<br />
* [http://www.atmel.com/dyn/products/devices.asp?family_id=607 Die Datenblätter zu Atmel Controllern]<br />
* [https://mpg.dnsalias.com/~magerlu/rn-wiki/avrtimer_applet Java Applet Timer Berechnung] <br />
* [http://www.roboternetz.de/phpBB2/dload.php?action=file&file_id=169 AvrTimer Windows Berechnungstool (für Bascom, nur nach Anmeldung)]<br />
* [http://people.freenet.de/gjl/helferlein/avr-uart-rechner.html AVR-Baudraten-Rechner (JavaScript)]<br />
* [http://www.engbedded.com/fusecalc/ Berechnung der Fusebits (englisch)]<br />
<br />
[[Kategorie:Microcontroller]]<br />
[[Kategorie:Grundlagen]]<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Abkürzung|AVR]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=PTC/NTC&diff=23474PTC/NTC2013-12-28T17:14:04Z<p>Besserwessi: /* Brückenschaltung */</p>
<hr />
<div>[[Bild:Schaltzeichen_ntcptc.PNG|thumb|Schaltzeichen eines NTC (oben) und eines PTC (unten)]]<br />
Heißleiter (engl. '''NTC''', ''negative temperature coefficient'') und Kaltleiter (engl. '''PTC''', ''positive temperature coefficient'') sind elektrische Widerstände aus bestimmten Materialien, deren Leitfähigkeit (und damit der Widerstand) sich mit der Temperatur deutlich ändert. <br />
<br />
<br />
==NTCs==<br />
[[Bild:ntc.gif|thumb|Typische Bauform]]Heißleiter sind stromleitende Materialien, die bei hohen Temperaturen eine höhere Leitfähigkeit besitzen als bei tiefen Temperaturen. Sie haben einen negativen Temperaturkoeffizienten; das heißt, mit steigender Temperatur sinkt ihr elektrischer Widerstand.<br />
<br />
Ein großer Vorteil ist die starke Temperaturabhängigkeit und der geringe Preis. <br />
Das nichtlineare Verhalten des Widerstandswertes bei sich verändernder Temperatur ist jedoch zugleich auch der größte Nachteil.<br />
<br />
Ein einzelner, nicht kompensierter NTC ändert seinen Widerstand nicht linear. Hier dazu ein Beispieldiagramm:<br />
<br />
<br />
[[Bild:ntcdiagramm.gif]]<br />
<br />
<br />
<br />
===Temperaturabhängigkeit===<br />
<br />
Mithilfe eines NTCs lässt sich ein einfacher Temperatursensor bauen.<br />
Bildet der Heißleiter einen Spannungsteiler mit einem konstanten Widerstand, so lässt sich die geteilte Spannung mithilfe eines Microcontrollers messen, wodurch auf den Widerstand des NTCs geschlossen werden kann.<br /><br />
Der Heißleiter ändert seinen elektrischen Widerstand nicht linear. Jedoch kann die Temperatur auch rechnerisch ermittelt werden. Es gilt:<br/><br />
<pre><br />
T ~ 1/ln(R)<br />
</pre><br />
Bei Einbeziehung der Aktivierungsenergie, der Nenntemperatur (meist 25°C), und des Nennwiderstandes bei Nenntemperatur<br />
gilt folgende Funktion:<br /><br />
<pre><br />
T(R) = T_N * B / (B + T_N * ln(R / R_N))<br />
</pre><br />
Wobei '''T''' die momentane Temperatur, '''T_N''' die Nenntemperatur (in Kelvin), '''R''' der Widerstand des Heißleiters und '''R_N''' den Nennwiderstand darstellt. Bei '''B''' handelt es sich um eine Materialkonstante, die vom Herrsteller im Datenblatt angegeben wird. Bei Implementierung der Funktion in ein Programm für den Microcontroller müssen umbedingt die Datentypen berücksichtigt werden!<br />
<br />
{{Ausbauwunsch|Erbitte Implementierung der Gleichungen (Unbekannter Parserfehler)}}<br />
<br />
==PTCs==<br />
Kaltleiter sind stromleitende Materialien, die bei hohen Temperaturen eine niedrigere Leitfähigkeit besitzen als bei tiefen Temperaturen. Sie haben einen positiven Temperaturkoeffizienten; das heißt, mit steigender Temperatur steigt auch ihr elektrischer Widerstand.<br />
<br />
Bei den PTCs gibt es zwei Sorten: Einmal welche mit näherungsweise linearer Kennlinie und dann welche mit stark nichtlinearer Kennlinie. Die linearen Typen (z.B. KTYxx Reihe) oder Platinwiderstände (z.B. PT100, PT1000 ) sind mehr für die Temperaturmessung gedacht. <br />
Recht beliebt sind die PTCs der KTYxx-Reihe z.B. der KTY81-110. Sie sind sehr günstig (ca. 0,50€) und bieten für die meisten Anwendungen eine ausreichende Genauigkeit. <br />
<br />
Für höhere Anforderungen an den Messbereich (je nach Typ von -260 ... 850 °C) oder die Genauigkeit gibt es Platin-Widerstände zur Temperaturmessung. Auch hier ist der Temperaturkoeffizient positiv (0,38 %/K bei Raumtemperatur). Am häufigsten findet man hier Widerstände von 100 Ohm (PT100) und 1000 Ohm (PT1000). Diese sind jedoch relativ teurer (je nach Typ und Bezugsquelle und Qualität ab etwa 3 €).<br />
<br />
Die stark nichtlinearen Typen sind mehr als Übertemperaturschutz oder als selbst rückstellende Sicherung geeignet.<br />
Auch dazu ein Beispieldiagramm für einen nicht linearen PTC:<br />
<br />
<br />
[[Bild:Ptcdiagramm.GIF]]<br />
<br />
==Einatzmöglichkeiten==<br />
Mit NTCs und PTCs lassen sich in "normalen" Bereichen (das heißt hier kleiner etwa 200 °C) günstig Temperaturen messen. Dieses Gebiet umfasst alles vom kleinen digitalen Fieberthermometer über die verschiedenen Anzeigeinstrumente (wie Wetterstationen und KFZ-Elektronik) bis hin zu kompletten Heizungs- und Klimasteuerungen in Gebäuden.<br />
<br />
Daneben gibt es auch Anwendungen bei denen es nicht um eine eigentliche Messung geht: spezielle NTCs werden z.B. bei Netzteilen als Einschaltstrombegrenzer genutzt. Hier wird die Eigenerwärmung als gewollter Effekt mit genutzt.<br />
<br />
==Temperaturmessung mit PTC==<br />
Um aus der Widerstandsänderung am Sensor eine passende Spannung zu erzeugen gibt es verschieden Möglichkeiten. Ein naheliegender Weg ist eine Konstantstromquelle, so dass die Spannung am Sensor direkt proportional zum Widerstand ist. Wenn man das Signal danach digitalisieren will, hat dieser Weg aber zwei wesentliche Nachteile: Eine gute Konstantstromquelle ist aufwendig, und man hat dann 2 Referenzen im System und damit unnötige Fehlerquellen. Für eine Widerstandsmessung ist ein Widerstand als Vergleichswert besser und einfacher als das Verhältnis von einer Spannungsquelle und einer Stromquelle. Dafür gibt es im wesentlichen 3 Möglichkeiten:<br />
<br />
1) Der selbe Strom fließt durch den Sensor und den Vergleichswiderstand. Die Spannung an Vergleichswiderstand wird als Referenz für den AD Wandler benutzt, die Spannung am Sensor wird gemessen. Dieses Verfahren ist vor allem mit hochauflösenden AD Wandlern mit Differenzeingängen (z.B. LTC2440) sinnvoll. <br />
<br />
2) Der selbe AD Wandler misst nacheinander die Spannung am Sensor und Vergleichswiderstand. Dafür braucht man 2 Eingänge mit Differenzeingang. <br />
<br />
3) Der Sensor und der Vergleichswiderstand bilden einen Spannungsteiler. Die Spannung über den Spannungsteiler dient als Referenz für den AD-wandler. Anders als bei den beiden vorherigen Möglichkeiten ist das Ergebnis der AD Wandlung hier nicht mehr linear vom Widerstand abhängig.<br />
Bei einem kleinen Messbereich und geringer Auflösung des AD's lohnt es sich ggf. den konstanten Teil der Spannung abzuziehen, damit der Wertebereich des AD Wandler weitgehend ausgenutzt werden kann. Die Schaltung ist dann eine Brückenschaltung. <br />
<br />
In allen 3 Fällen muss der Strom nicht besonders stabilisiert sein und es wird keine stabile Referenzspannung für den AD benötigt. Es kommt nur auf das Spannungsverhältnis an. <br />
<br />
Beim Strom durch den Sensor muss ein Kompromiss zwischen genügend Spannung und der Eigenerwärmung des Sensors gefunden werden. Bei mehr als etwa 1 mW Verlustleistung am Sensor wird eine genaue Temperaturmessung schwierig. Für genaue Messungen mit dem PT100 werden oft sogar 0,1 mW als Grenze angegeben, d.h der Strom liegt bei nur 1 mA.<br />
<br />
==Linearisierung==<br />
[[Bild:LinearPTC.GIF]]<br />
KTY81 mit konstanten Strom (1,4 mA), und mit Widerstand in Reihe.<br />
<br />
Um trotz der nicht linearen Kennlinie des Sensors ein linear von der Messgröße (Temperatur) abhängiges Ergebnis (Spannung oder Digitaler Wert) zu erhalten, bieten sich verschiedene Methoden an:<br />
<br />
* Man nutzt die Nichtlineare Kennlinie von Dioden. Wegen der Temperaturabhängigkeit der Diodenkennlinie ist das aber schwierig und nur für sehr starke Nichtlinearitäten gerechtfertigt. Für die schwache Nichtlinearität der PTCs ist das Verfahren eher ungeeignet.<br />
<br />
* Man nutzt den nichtlinearen Zusammenhang zwischen Spannung und Widerstand beim Spannungsteiler, bzw. in einer Brückenschaltung. Bei den Sensoren des Typs KTY81 wird mit einem 2,7kOhm-Widerstand in Reihe gerade eine relativ gute Kompensation der Nichtlinearitäten erreicht. Im Bereich von -40 °C ... +140 °C erscheint der PTC nun nahezu linear, der verbleibende Linearitätsfehler liegt bei etwa ±10 mV (für 5 V am Spannungsteiler). So einfach geht die Linearisierung allerdings nicht bei allen Sensoren.<br />
<br />
* Man überlässt die Linearisierung den mathematischen Fähigkeiten eines Prozessors, z.B. einem ATMega-x, an dessen ADC der PTC angeschlossen ist. Das Verfahren ist sehr universell, benötigt aber einiges an Programmcode.<br />
<br />
==Schaltungsbeispiele==<br />
<br />
=== Brückenschaltung ===<br />
[[Bild:PT1000-Brücke.png]]<br />
<br />
Ein einfache, aber dennoch gute Auswerteschaltung ist eine Brückenschaltung. Wegen der relativ kleinen Spannungsänderungen wird oft eine Verstärkung benötigt, vor allem beim <b>Pt100</b> oder — Strom sparender — <b>Pt1000</b>.<br />
Bei den gezeigten Widerstandswerten reicht der Messbereich von etwa -25 °C bis +250 °C für 0 bis U+ (z.B. 5 V) am Ausgang. Über die Widerstände R2 und R3 kann der Bereich angepasst werden. Mit R3 = 20 kΩ hätte man z.B. einen Messbereich von etwa -10 °C bis + 130 °C. Die Linearisierung erfolgt in der Regel digital hinter dem A/D-Wandler. Die Referenzspannung des A/D-Wandlers sollte U+ sein, für eine ratiometrische Messung. Wenn nur eine Versorgung (z.B. 5 V) zur Verfügung stehen, sollte U+ an den Ausgangsbereich des OPs angepasst sein, also etwa 3 V für den LM358 (oder den besseren LT1013), oder der Operationsverstärker eine Rail-to-Rail-Typ (z.B. MCP6001) sein, wenn der AD mit der Versorgungsspannung als Ref. Arbeitet.<br />
<br />
{{Ausbauwunsch|... auf jeden Fall Anwendungs- und Schaltungsbeispiele mit Heiß- oder Kaltleiter!'''}}<br />
<br />
=== Spannungsteiler ===<br />
<br />
Für <b>NTC</b> (Heißleiter) bietet sich ein einfacher Spannungsteiler an. Den Vorwiderstand dimensioniert man so, dass er etwa so groß ist wie der Widerstandswert <i>in der Mitte des gewünschten Temperatur-Messbereiches</i>. Die A/D-Wandlung erfolgt „ratiometrisch“ mit dem vollen Speisespannungsbereich. Dann hat man — nach der Linearisierung in Software — etwas oberhalb der Mitte (zu niedrigeren Temperaturen hin) die höchste und am den beiden Rändern gleichmäßig abnehmende Auflösung. Von Vorteil bei dieser Lösung ist, dass man sich über den Messbereich keine Gedanken machen muss, er ist prinzipiell 0 K .. ∞ K, und die Auflösung nimmt zu den beiden Extrema kontinuierlich ab.<br />
<br />
==== Rechenbeispiel (NTC mit 220 kΩ bei 25 °C) ====<br />
<table border cellspacing=0 cellpadding=4><br />
<tr bgcolor=#FFFFE0><th>Temperatur<th>NTC-Widerstandswert<th>Vorwiderstand<th>Ausgangsspannung<th>Auflösung<br />
<tr><td>0 °C<td>844 kΩ<td rowspan=6>100 kΩ<td>U<sub>B</sub> * 0,894<td rowspan=2>10 bit: 0,4 K<br>12 bit: 0,1 K<br />
<tr><td>10 °C<td>500 kΩ<td>U<sub>B</sub> * 0,667<br />
<tr bgcolor=#E0FFE0><td>40 °C<td>120 kΩ<td>U<sub>B</sub> * 0,545<td rowspan=2>10 bit: 0,1 K<br>12 bit: 0,025 K<br />
<tr bgcolor=#E0FFE0><td>50 °C<td>80 kΩ<td>U<sub>B</sub> * 0,444<br />
<tr><td>130 °C<td>5,63 kΩ<td>U<sub>B</sub> * 0,053<td rowspan=2>10 bit: 0,75 K<br>12 bit: 0,2 K<br />
<tr><td>140 °C<td>4,21 kΩ<td>U<sub>B</sub> * 0,040<br />
</table><br />
<br />
==== Grafisches Rechenbeispiel mittels WolframAlpha ====<br />
* [http://www.wolframalpha.com/input/?i=log+plot+y=220000*exp(4000*(1/(275%2Bx)-1/300)),+x=-50+to+150 Kennlinie (R über T)], die Exponentialkonstante '''B''' ist hier gleich 4000 angesetzt.<br />
* [http://www.wolframalpha.com/input/?i=plot+y=1/(1%2B1/exp(4000*(1/(275%2Bx)-1/300))),+x=-50+to+150 Ausgangsspannung (U/U<sub>B</sub> über T)], der Heißleiter befindet sich einpolig auf Masse, daher nimmt die Spannung mit steigender Temperatur ab. Der Vorwiderstand ist hier genauso groß wie der Heißleiter-Widerstand bei 25 °C (Nennwiderstand). Am Wendepunkt der s-förmigen Kurve ist die Steilheit und damit die Auflösung am größten.<br />
* [http://www.wolframalpha.com/input/?i=plot+derive+y=1/(1%2B1/exp(4000*(1/(275%2Bx)-1/300))),+x=-50+to+150 Steilheit (<i>d</i>U/U<sub>B</sub> über T) = Ableitung], der Wert -0,01 bedeutet ≈ 0,1 K Auflösung bei 1024 A/D-Werten (eines 10-bit-A/D-Wandlers).<br />
<br />
==== Tipp zur Spreizung des Messbereichs ====<br />
<br />
Möchte man den ''genau arbeitenden'' Messbereich vergrößern, ist es am einfachsten, den Vorwiderstand ''umschaltbar'' zu machen!<br />
<br />
Ein per Portpin schaltbarer Widerstand ist ohnehin nötig, wenn die Widerstandsmessung bei einem batteriebetriebenen Aufnehmer nur kurzzeitig erfolgen soll. Um Querstrom zu vermeiden, ist für die jeweiligen niederohmigen Widerstände (sofern umschaltbar mehrere vorhanden) an einem Pin anzuschließen, dessen digitaler Einang abtrennbar ist. Bei manchen Controllern, etwa MSP430x2xx, ist man da eingeschränkt.<br />
<br />
Da beide A/D-Wandlungen ratiometrisch arbeiten, sind keinerlei Referenzspannungen erforderlich. Die Maßverkörperung (Vergleichsnormal) erfolgt mit dem Festwiderstand bzw. den Festwiderständen.<br />
<br />
=== Mehr Auflösung ===<br />
<br />
<i>… bedeutet nicht unbedingt mehr Genauigkeit!</i><br />
<br />
Hier gibt es prinzipiell drei Wege:<br />
* Einschränken des Wandlungsbereiches durch Vorverstärkung oder mittels Referenzspannungen: Geringerer Messumfang<br><i>Typische Anwendung: Fieberthermometer</i><br />
* Verwenden eines besseren (externen) A/D-Wandlers — wäre nur für Temperaturmessung Overkill<br />
* Verwenden eines anderen Wandlungsprinzips in Mikrocontroller-Software<br />
<br />
Recht gute Ergebnisse liefern Zweiflanken-A/D-Umsetzer. Diese erfordern jedoch einen OPV sowie einen Analogsignal-Umschalter. Diese sind selten in Mikrocontrollern bereits integriert.<br />
<br />
Die übliche Vorgehensweise ist ein Einflanken-Umsetzer, der mit dem zumeist vorhandenen Analog-Komparator arbeitet und wenig externe Beschaltung erfordert.<br />
<br />
==Siehe auch==<br />
*[[Sensorarten]]<br />
<br />
<br />
==Weblinks==<br />
[http://de.wikipedia.org/wiki/Kaltleiter Wikipedia: Kaltleiter]<br />
<br />
[http://www.maxim-ic.com/app-notes/index.mvp/id/3450 Schaltung für PT100 mit Linearisierung]<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Sensoren]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=PTC/NTC&diff=23473PTC/NTC2013-12-28T17:02:37Z<p>Besserwessi: /* Temperaturmessung mit PTC */</p>
<hr />
<div>[[Bild:Schaltzeichen_ntcptc.PNG|thumb|Schaltzeichen eines NTC (oben) und eines PTC (unten)]]<br />
Heißleiter (engl. '''NTC''', ''negative temperature coefficient'') und Kaltleiter (engl. '''PTC''', ''positive temperature coefficient'') sind elektrische Widerstände aus bestimmten Materialien, deren Leitfähigkeit (und damit der Widerstand) sich mit der Temperatur deutlich ändert. <br />
<br />
<br />
==NTCs==<br />
[[Bild:ntc.gif|thumb|Typische Bauform]]Heißleiter sind stromleitende Materialien, die bei hohen Temperaturen eine höhere Leitfähigkeit besitzen als bei tiefen Temperaturen. Sie haben einen negativen Temperaturkoeffizienten; das heißt, mit steigender Temperatur sinkt ihr elektrischer Widerstand.<br />
<br />
Ein großer Vorteil ist die starke Temperaturabhängigkeit und der geringe Preis. <br />
Das nichtlineare Verhalten des Widerstandswertes bei sich verändernder Temperatur ist jedoch zugleich auch der größte Nachteil.<br />
<br />
Ein einzelner, nicht kompensierter NTC ändert seinen Widerstand nicht linear. Hier dazu ein Beispieldiagramm:<br />
<br />
<br />
[[Bild:ntcdiagramm.gif]]<br />
<br />
<br />
<br />
===Temperaturabhängigkeit===<br />
<br />
Mithilfe eines NTCs lässt sich ein einfacher Temperatursensor bauen.<br />
Bildet der Heißleiter einen Spannungsteiler mit einem konstanten Widerstand, so lässt sich die geteilte Spannung mithilfe eines Microcontrollers messen, wodurch auf den Widerstand des NTCs geschlossen werden kann.<br /><br />
Der Heißleiter ändert seinen elektrischen Widerstand nicht linear. Jedoch kann die Temperatur auch rechnerisch ermittelt werden. Es gilt:<br/><br />
<pre><br />
T ~ 1/ln(R)<br />
</pre><br />
Bei Einbeziehung der Aktivierungsenergie, der Nenntemperatur (meist 25°C), und des Nennwiderstandes bei Nenntemperatur<br />
gilt folgende Funktion:<br /><br />
<pre><br />
T(R) = T_N * B / (B + T_N * ln(R / R_N))<br />
</pre><br />
Wobei '''T''' die momentane Temperatur, '''T_N''' die Nenntemperatur (in Kelvin), '''R''' der Widerstand des Heißleiters und '''R_N''' den Nennwiderstand darstellt. Bei '''B''' handelt es sich um eine Materialkonstante, die vom Herrsteller im Datenblatt angegeben wird. Bei Implementierung der Funktion in ein Programm für den Microcontroller müssen umbedingt die Datentypen berücksichtigt werden!<br />
<br />
{{Ausbauwunsch|Erbitte Implementierung der Gleichungen (Unbekannter Parserfehler)}}<br />
<br />
==PTCs==<br />
Kaltleiter sind stromleitende Materialien, die bei hohen Temperaturen eine niedrigere Leitfähigkeit besitzen als bei tiefen Temperaturen. Sie haben einen positiven Temperaturkoeffizienten; das heißt, mit steigender Temperatur steigt auch ihr elektrischer Widerstand.<br />
<br />
Bei den PTCs gibt es zwei Sorten: Einmal welche mit näherungsweise linearer Kennlinie und dann welche mit stark nichtlinearer Kennlinie. Die linearen Typen (z.B. KTYxx Reihe) oder Platinwiderstände (z.B. PT100, PT1000 ) sind mehr für die Temperaturmessung gedacht. <br />
Recht beliebt sind die PTCs der KTYxx-Reihe z.B. der KTY81-110. Sie sind sehr günstig (ca. 0,50€) und bieten für die meisten Anwendungen eine ausreichende Genauigkeit. <br />
<br />
Für höhere Anforderungen an den Messbereich (je nach Typ von -260 ... 850 °C) oder die Genauigkeit gibt es Platin-Widerstände zur Temperaturmessung. Auch hier ist der Temperaturkoeffizient positiv (0,38 %/K bei Raumtemperatur). Am häufigsten findet man hier Widerstände von 100 Ohm (PT100) und 1000 Ohm (PT1000). Diese sind jedoch relativ teurer (je nach Typ und Bezugsquelle und Qualität ab etwa 3 €).<br />
<br />
Die stark nichtlinearen Typen sind mehr als Übertemperaturschutz oder als selbst rückstellende Sicherung geeignet.<br />
Auch dazu ein Beispieldiagramm für einen nicht linearen PTC:<br />
<br />
<br />
[[Bild:Ptcdiagramm.GIF]]<br />
<br />
==Einatzmöglichkeiten==<br />
Mit NTCs und PTCs lassen sich in "normalen" Bereichen (das heißt hier kleiner etwa 200 °C) günstig Temperaturen messen. Dieses Gebiet umfasst alles vom kleinen digitalen Fieberthermometer über die verschiedenen Anzeigeinstrumente (wie Wetterstationen und KFZ-Elektronik) bis hin zu kompletten Heizungs- und Klimasteuerungen in Gebäuden.<br />
<br />
Daneben gibt es auch Anwendungen bei denen es nicht um eine eigentliche Messung geht: spezielle NTCs werden z.B. bei Netzteilen als Einschaltstrombegrenzer genutzt. Hier wird die Eigenerwärmung als gewollter Effekt mit genutzt.<br />
<br />
==Temperaturmessung mit PTC==<br />
Um aus der Widerstandsänderung am Sensor eine passende Spannung zu erzeugen gibt es verschieden Möglichkeiten. Ein naheliegender Weg ist eine Konstantstromquelle, so dass die Spannung am Sensor direkt proportional zum Widerstand ist. Wenn man das Signal danach digitalisieren will, hat dieser Weg aber zwei wesentliche Nachteile: Eine gute Konstantstromquelle ist aufwendig, und man hat dann 2 Referenzen im System und damit unnötige Fehlerquellen. Für eine Widerstandsmessung ist ein Widerstand als Vergleichswert besser und einfacher als das Verhältnis von einer Spannungsquelle und einer Stromquelle. Dafür gibt es im wesentlichen 3 Möglichkeiten:<br />
<br />
1) Der selbe Strom fließt durch den Sensor und den Vergleichswiderstand. Die Spannung an Vergleichswiderstand wird als Referenz für den AD Wandler benutzt, die Spannung am Sensor wird gemessen. Dieses Verfahren ist vor allem mit hochauflösenden AD Wandlern mit Differenzeingängen (z.B. LTC2440) sinnvoll. <br />
<br />
2) Der selbe AD Wandler misst nacheinander die Spannung am Sensor und Vergleichswiderstand. Dafür braucht man 2 Eingänge mit Differenzeingang. <br />
<br />
3) Der Sensor und der Vergleichswiderstand bilden einen Spannungsteiler. Die Spannung über den Spannungsteiler dient als Referenz für den AD-wandler. Anders als bei den beiden vorherigen Möglichkeiten ist das Ergebnis der AD Wandlung hier nicht mehr linear vom Widerstand abhängig.<br />
Bei einem kleinen Messbereich und geringer Auflösung des AD's lohnt es sich ggf. den konstanten Teil der Spannung abzuziehen, damit der Wertebereich des AD Wandler weitgehend ausgenutzt werden kann. Die Schaltung ist dann eine Brückenschaltung. <br />
<br />
In allen 3 Fällen muss der Strom nicht besonders stabilisiert sein und es wird keine stabile Referenzspannung für den AD benötigt. Es kommt nur auf das Spannungsverhältnis an. <br />
<br />
Beim Strom durch den Sensor muss ein Kompromiss zwischen genügend Spannung und der Eigenerwärmung des Sensors gefunden werden. Bei mehr als etwa 1 mW Verlustleistung am Sensor wird eine genaue Temperaturmessung schwierig. Für genaue Messungen mit dem PT100 werden oft sogar 0,1 mW als Grenze angegeben, d.h der Strom liegt bei nur 1 mA.<br />
<br />
==Linearisierung==<br />
[[Bild:LinearPTC.GIF]]<br />
KTY81 mit konstanten Strom (1,4 mA), und mit Widerstand in Reihe.<br />
<br />
Um trotz der nicht linearen Kennlinie des Sensors ein linear von der Messgröße (Temperatur) abhängiges Ergebnis (Spannung oder Digitaler Wert) zu erhalten, bieten sich verschiedene Methoden an:<br />
<br />
* Man nutzt die Nichtlineare Kennlinie von Dioden. Wegen der Temperaturabhängigkeit der Diodenkennlinie ist das aber schwierig und nur für sehr starke Nichtlinearitäten gerechtfertigt. Für die schwache Nichtlinearität der PTCs ist das Verfahren eher ungeeignet.<br />
<br />
* Man nutzt den nichtlinearen Zusammenhang zwischen Spannung und Widerstand beim Spannungsteiler, bzw. in einer Brückenschaltung. Bei den Sensoren des Typs KTY81 wird mit einem 2,7kOhm-Widerstand in Reihe gerade eine relativ gute Kompensation der Nichtlinearitäten erreicht. Im Bereich von -40 °C ... +140 °C erscheint der PTC nun nahezu linear, der verbleibende Linearitätsfehler liegt bei etwa ±10 mV (für 5 V am Spannungsteiler). So einfach geht die Linearisierung allerdings nicht bei allen Sensoren.<br />
<br />
* Man überlässt die Linearisierung den mathematischen Fähigkeiten eines Prozessors, z.B. einem ATMega-x, an dessen ADC der PTC angeschlossen ist. Das Verfahren ist sehr universell, benötigt aber einiges an Programmcode.<br />
<br />
==Schaltungsbeispiele==<br />
<br />
=== Brückenschaltung ===<br />
[[Bild:PT1000-Brücke.png]]<br />
<br />
Ein einfache, aber dennoch gute Auswerteschaltung ist eine Brückenschaltung. Wegen der relativ kleinen Spannungsänderungen wird oft eine Verstärkung benötigt, vor allem beim <b>Pt100</b> oder — Strom sparender — <b>Pt1000</b>.<br />
Bei den gezeigten Widerstandswerten reicht der Messbereich von etwa -25 °C bis +250 °C für 0 bis U+ (z.B. 5 V) am Ausgang. Über die Widerstände R2 und R3 kann der Bereich angepasst werden. Mit R3 = 20 kΩ hätte man z.B. einen Messbereich von etwa -10 °C bis + 130 °C. Die Linearisierung erfolgt in der Regel digital hinter dem A/D-Wandler. Die Referenzspannung des A/D-Wandlers sollte U+ sein, für eine ratiometrische Messung. Wenn nur eine Versorgung (z.B. 5 V) zur Verfügung stehen, sollte der Operationsverstärker eine Rail-to-Rail-Typ (z.B. MCP6001) sein. Je nach Eingangsspannung genügt auch ein Rail-to-Rail-Ausgang.<br />
<br />
{{Ausbauwunsch|... auf jeden Fall Anwendungs- und Schaltungsbeispiele mit Heiß- oder Kaltleiter!'''}}<br />
<br />
=== Spannungsteiler ===<br />
<br />
Für <b>NTC</b> (Heißleiter) bietet sich ein einfacher Spannungsteiler an. Den Vorwiderstand dimensioniert man so, dass er etwa so groß ist wie der Widerstandswert <i>in der Mitte des gewünschten Temperatur-Messbereiches</i>. Die A/D-Wandlung erfolgt „ratiometrisch“ mit dem vollen Speisespannungsbereich. Dann hat man — nach der Linearisierung in Software — etwas oberhalb der Mitte (zu niedrigeren Temperaturen hin) die höchste und am den beiden Rändern gleichmäßig abnehmende Auflösung. Von Vorteil bei dieser Lösung ist, dass man sich über den Messbereich keine Gedanken machen muss, er ist prinzipiell 0 K .. ∞ K, und die Auflösung nimmt zu den beiden Extrema kontinuierlich ab.<br />
<br />
==== Rechenbeispiel (NTC mit 220 kΩ bei 25 °C) ====<br />
<table border cellspacing=0 cellpadding=4><br />
<tr bgcolor=#FFFFE0><th>Temperatur<th>NTC-Widerstandswert<th>Vorwiderstand<th>Ausgangsspannung<th>Auflösung<br />
<tr><td>0 °C<td>844 kΩ<td rowspan=6>100 kΩ<td>U<sub>B</sub> * 0,894<td rowspan=2>10 bit: 0,4 K<br>12 bit: 0,1 K<br />
<tr><td>10 °C<td>500 kΩ<td>U<sub>B</sub> * 0,667<br />
<tr bgcolor=#E0FFE0><td>40 °C<td>120 kΩ<td>U<sub>B</sub> * 0,545<td rowspan=2>10 bit: 0,1 K<br>12 bit: 0,025 K<br />
<tr bgcolor=#E0FFE0><td>50 °C<td>80 kΩ<td>U<sub>B</sub> * 0,444<br />
<tr><td>130 °C<td>5,63 kΩ<td>U<sub>B</sub> * 0,053<td rowspan=2>10 bit: 0,75 K<br>12 bit: 0,2 K<br />
<tr><td>140 °C<td>4,21 kΩ<td>U<sub>B</sub> * 0,040<br />
</table><br />
<br />
==== Grafisches Rechenbeispiel mittels WolframAlpha ====<br />
* [http://www.wolframalpha.com/input/?i=log+plot+y=220000*exp(4000*(1/(275%2Bx)-1/300)),+x=-50+to+150 Kennlinie (R über T)], die Exponentialkonstante '''B''' ist hier gleich 4000 angesetzt.<br />
* [http://www.wolframalpha.com/input/?i=plot+y=1/(1%2B1/exp(4000*(1/(275%2Bx)-1/300))),+x=-50+to+150 Ausgangsspannung (U/U<sub>B</sub> über T)], der Heißleiter befindet sich einpolig auf Masse, daher nimmt die Spannung mit steigender Temperatur ab. Der Vorwiderstand ist hier genauso groß wie der Heißleiter-Widerstand bei 25 °C (Nennwiderstand). Am Wendepunkt der s-förmigen Kurve ist die Steilheit und damit die Auflösung am größten.<br />
* [http://www.wolframalpha.com/input/?i=plot+derive+y=1/(1%2B1/exp(4000*(1/(275%2Bx)-1/300))),+x=-50+to+150 Steilheit (<i>d</i>U/U<sub>B</sub> über T) = Ableitung], der Wert -0,01 bedeutet ≈ 0,1 K Auflösung bei 1024 A/D-Werten (eines 10-bit-A/D-Wandlers).<br />
<br />
==== Tipp zur Spreizung des Messbereichs ====<br />
<br />
Möchte man den ''genau arbeitenden'' Messbereich vergrößern, ist es am einfachsten, den Vorwiderstand ''umschaltbar'' zu machen!<br />
<br />
Ein per Portpin schaltbarer Widerstand ist ohnehin nötig, wenn die Widerstandsmessung bei einem batteriebetriebenen Aufnehmer nur kurzzeitig erfolgen soll. Um Querstrom zu vermeiden, ist für die jeweiligen niederohmigen Widerstände (sofern umschaltbar mehrere vorhanden) an einem Pin anzuschließen, dessen digitaler Einang abtrennbar ist. Bei manchen Controllern, etwa MSP430x2xx, ist man da eingeschränkt.<br />
<br />
Da beide A/D-Wandlungen ratiometrisch arbeiten, sind keinerlei Referenzspannungen erforderlich. Die Maßverkörperung (Vergleichsnormal) erfolgt mit dem Festwiderstand bzw. den Festwiderständen.<br />
<br />
=== Mehr Auflösung ===<br />
<br />
<i>… bedeutet nicht unbedingt mehr Genauigkeit!</i><br />
<br />
Hier gibt es prinzipiell drei Wege:<br />
* Einschränken des Wandlungsbereiches durch Vorverstärkung oder mittels Referenzspannungen: Geringerer Messumfang<br><i>Typische Anwendung: Fieberthermometer</i><br />
* Verwenden eines besseren (externen) A/D-Wandlers — wäre nur für Temperaturmessung Overkill<br />
* Verwenden eines anderen Wandlungsprinzips in Mikrocontroller-Software<br />
<br />
Recht gute Ergebnisse liefern Zweiflanken-A/D-Umsetzer. Diese erfordern jedoch einen OPV sowie einen Analogsignal-Umschalter. Diese sind selten in Mikrocontrollern bereits integriert.<br />
<br />
Die übliche Vorgehensweise ist ein Einflanken-Umsetzer, der mit dem zumeist vorhandenen Analog-Komparator arbeitet und wenig externe Beschaltung erfordert.<br />
<br />
==Siehe auch==<br />
*[[Sensorarten]]<br />
<br />
<br />
==Weblinks==<br />
[http://de.wikipedia.org/wiki/Kaltleiter Wikipedia: Kaltleiter]<br />
<br />
[http://www.maxim-ic.com/app-notes/index.mvp/id/3450 Schaltung für PT100 mit Linearisierung]<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Sensoren]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=PIC_Assembler&diff=23464PIC Assembler2013-12-21T19:12:49Z<p>Besserwessi: /* Assembler */</p>
<hr />
<div>= Einführung =<br />
<br />
== Bit, Byte, Nibble, Bin und Hex ==<br />
<br />
Ein Mikrocontroller (kurz: µC) kann eigentlich nur durch ein Portpin eine Spannung einlesen bzw. ausgeben. Er kann aber nur erkennen, ob eine Spannung vorhanden ist oder nicht. Wenn fast keine Spannung vorhanden ist erkennt er das als 0 und wenn eine Spannung fast so gross, wie seine Versorgungsspannung anliegt, als 1.<br />
<br />
Genauso bei der Ausgabe, wenn er 0 ausgibt ist auf dem Portpin fast keine Spannung, wenn 1, eine Spannung fast gleich gross seiner Versorgungsspannung. Und das ist ein Bit, die kleinste Menge einer Information. Das Bit ist binär, d.h. es kann nur zwei unterschiedliche Werte haben: 0 oder 1.<br />
<br />
Wenn wir gleichzeitig (parallel) 8 Bits haben, dann ist es ein Byte, das 256 Bitkombinationen von 00000000b bis 11111111b enthält, weil ein Bit (X) auf jeder Stelle 0 bzw. 1 sein kann.<br />
<br />
<table border=0 cellpadding=3 cellspacing=2><br />
<br />
<tr><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
</tr><br />
<br />
<tr><br />
<td colspan=4 align=middle bgcolor=#007fff>High Nibble </td><br />
<td colspan=4 align=middle bgcolor=#ff8305>Low Nibble </td><br />
</tr><br />
<tr><br />
<td colspan=8 align=middle bgcolor=#810f40> <font color=#ffffff>Byte</font> </td><br />
</tr><br />
</table><br />
<br />
Das "b" bedeutet, dass es sich um binäre (kurz: bin) Darstellung (auch Zahl genannt) handelt. Binäre Zahlen sind aber lang, weil jedes Bit eine Stelle benötigt.<br />
<br />
Um die Schreibweise zu verkürzen, wurden hexadezimale (kurz: hex) Zahlen eingeführt. Zuerst wurde ein Byte auf zwei 4-Bit Halbbytes (Nibbles) verteilt und danach ein Nibble als Ziffer genommen. Weil 4 Bits 16 Kombinationen ergeben, haben die Ziffer 0 bis 9 aus dem Dezimalsystem (d) nicht ausgereicht und wurden um Buchstaben A bis F erweitert. Die hexadezimalen Zahlen haben ein "h" Zeichen am Ende. Für die Zahlen 0 bis 9 sind die (h) und<br />
(d) Zeichen nicht nötig, da sie beide gleich den entsprechenden bin Zahlen sind.<br />
<br />
Die Umwandlung zwischen bin, hex und dec Zahlen für ein Nibble zeigt folgende Tabelle:<br />
<br />
0b = 0h = 0d 100b = 4h = 4d 1000b = 8h = 8d 1100b = Ch = 12d<br />
1b = 1h = 1d 101b = 5h = 5d 1001b = 9h = 9d 1101b = Dh = 13d<br />
10b = 2h = 2d 110b = 6h = 6d 1010b = Ah = 10d 1110b = Eh = 14d<br />
11b = 3h = 3d 111b = 7h = 7d 1011b = Bh = 11d 1111b = Fh = 15d<br />
<br />
Damit kann ein Byte mit zwei hex Ziffern definiert werden z.B. 1100 0011b = C3h. Für zwei Bytes braucht man 4 hex Ziffern z.B.<br />
<br />
101 0111 1010 1001b = 57A9h, usw. Für MPASM wird sehr oft die Schreibweise für hex Zahlen 0x57A9 oder 0xC3 benutzt.<br />
<br />
So wie im Dezimalsystem werden führende Nullen nicht geschrieben. Bei einer Wandlung bin->hex fängt man immer von der rechten Seite der bin Zahl an, da die Anzahl führender Nullen unbekannt ist.<br />
<br />
== Speicher und Register ==<br />
<br />
Als Speicher bezeichnet man das Teil der Hardware, in das eine Information geschrieben, gespeichert und von dort wieder ausgelesen werden kann.<br />
<br />
Es gibt eigentlich nur zwei Arten von elektronischen Speichern: flüchtige und nichtflüchtige. Die Information die sich im flüchtigen Speicher befindet, geht verloren, wenn die Versorgungsspannung des Speichers unterbrochen oder abgeschaltet wird. Bei PICs ist es Datenspeicher (RAM).<br />
<br />
Wenn die Versorgungsspannung vom nichtflüchtigen Speicher abgeschaltet wird, ist die gespeicherte Information zwar momentan nicht lesbar, bleibt aber erhalten und sobald der Speicher wieder mit Spannung versorgt wird, kann sie ausgelesen werden. Ein PIC hat zwei solche Speicher: Programmspeicher (Flash) und EEPROM.<br />
<br />
Der wichtigste Unterschied zwischen den Speicherarten ist, dass die flüchtigen direkt (sehr schnell) beschreibbar sind, dagegen das Beschreiben des nichtflüchtigen Speichers spezielle Algorithmen benötigt, die im Vergleich zu direkten Zugriffen langsamer sind. Beim Auslesen gibt es praktisch keinen Unterschied.<br />
<br />
Speicher besitzen eine bestimmte Menge von s.g. Speicherstellen. Jede Speicherstelle hat eine individuelle Adresse und kann eine binäre Information mit bestimmter Anzahl von Bits abspeichern. <br />
<br />
PIC Prozessoren haben drei Arten von Speicher, wegen verschiedener Anwendung, auch unterschiedliche Struktur. Die beiden Speicher für Daten (RAM und EEPROM) haben jeweils 8 Bit Breite und Programmspeicher (Flasch) bei Basic-Line hat 12-bittige, Mid-Range 14-bittige und High-End 8-bittige Speicherstellen. Die Anzahl den Speicherstellen im bestimmten Speicher ist vom PIC-Typ abhängig.<br />
<br />
Eine 8-bittige Speicherstelle im RAM wird bei PICs Register genannt und kann so skizziert werden:<br />
<br />
<table border=0 cellpadding=3 cellspacing=2><br />
<br />
<tr><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
</tr><br />
<br />
<tr><br />
<td> MSB </td><br />
<td colspan=6></td><br />
<td> LSB </td><br />
</tr><br />
<br />
<tr><br />
<td bgcolor="#007fff">bit 7</td><br />
<td bgcolor="#007fff">bit 6</td><br />
<td bgcolor="#007fff">bit 5</td><br />
<td bgcolor="#007fff">bit 4</td><br />
<td bgcolor="#007fff">bit 3</td><br />
<td bgcolor="#007fff">bit 2</td><br />
<td bgcolor="#007fff">bit 1</td><br />
<td bgcolor="#007fff">bit 0</td><br />
</tr><br />
<br />
<tr><br />
<td colspan=4 align=middle bgcolor=#ff8305>High Nibble </td><br />
<td colspan=4 align=middle bgcolor=#ff8305>Low Nibble </td><br />
</tr><br />
<tr><br />
<td colspan=8 align=middle bgcolor=#810f40> <font color=#ffffff>Byte</font> </td><br />
</tr><br />
</table><br />
<br />
<br />
Bit 7 wird als hochwertigstes (MSB = Most Significant Bit) und bit 0 als niederwertigstes (LSB = Least Significant Bit) bezeichnet. Jedes Bit im Register (X) kann gleich 0 bzw. 1 sein. In einem Register existieren immer 8 Bits also auch führende Nullen. Zum Beispiel die hex Zahl 3h sieht im PIC Register so aus: 00000011.<br />
<br />
Um ein Datenbyte in ein Register zu schreiben oder aus einem Register zu lesen, muss zuerst das Register durch seine Adresse gewählt werden. Dafür gibt es beim PIC folgende Möglichkeiten:<br />
<br />
Direkte Adressierung per absolute Adresse: movwf 0x20<br />
<br />
Direkte Adressierung per vorher definiertem Namen des Registers (z.B. Temp equ 0x20): movwf Temp<br />
<br />
Indirekte Adressierung durch "FSR" Register, in das die absolute Adresse des Registers "Temp" eingeschrieben wird. Durch Zugriff mittels "INDF" Register wird auf die Speicherstelle zugegriffen, die durch das Register "FSR" adressiert wird. ("INDF" ist dabei kein real in Hardware implementiertes Register). Wie vorher wurde "Temp equ 0x20" definiert und weiter:<br />
<br />
movlw Temp ;ins W-Register wird die absolute Adresse des Registers "Temp" geladen<br />
movwf FSR ;diese Adresse wird ins "FSR" Register kopiert<br />
movf INDF,0 ;der Wert aus dem indirekt adressierten Register "Temp"<br />
;wird aus dem "INDF" Register ins W-Register geladen.<br />
<br />
Weil in jedem 12 bzw. 14-bittigem Befehl, der mit Datenspeicher verbunden ist, für die Adresse des ansprechenden Registers nur 7 Bits existieren, die bis zur Adresse 7Fh (128d) Register direkt ansprechen können, ist bei Basic-Line und Mid-Range PICs der Datenspeicher (RAM) in s.g. Bänke verteilt.<br />
<br />
Für Auswahl einer Bank sind zwei Bits "RP0" und "RP1" im "STATUS" Register zuständig. Die Anzahl der Bänke und ihre Verwendung ist von der gesamten Größe des RAMs abhängig und kann dem Datenblatt des PICs entnommen werden.<br />
<br />
Die Beschreibung allen SFRs (Special Funktion Register), in den sämtliche Funktionen des PICs festgelegt werden, befinden sich im Datenblatt unter "Memory Organisation".<br />
<br />
Siehe auch: [[#Speicherbankorganisation|Speicherbankorganisation]]<br />
<br />
== Prozessor ==<br />
Die Prozessoren der PIC's von Microchip sind alle in der "Harvard"-Architektur gefertigt. Das bedeutet, dass Datenspeicher und Programmspeicher einen eigenen Bus zur CPU (Central Processing Unit) besitzen. Der Vorteil zur "von Neumann"-Architektur ist, dass sich die Busgrößen damit unterscheiden können. <br />
<br />
Der Prozessor von PICs gehört zu den RISC (Reduced Instruction Set Computer) Prozessoren und man hat nur 35 bzw. 75 Befehle zu erlernen, was seine Programmierung im Vergleich zu anderen µCs deutlich vereinfacht. Jeder Befehl benötigt im Programmspeicher nur eine Speicherstelle und im Quellcode nur eine Zeile. Die Ausführung des Befehls dauert, abhängig vom Befehl, 1 bis 2 Prozessortakte.<br />
<br />
Ein sogenannter Prozessortakt besteht aus 4 Oszillator-Takten, die jeweils einen Teil eines Befehls abarbeiten. Deswegen entspricht die Taktfrequenz der CPU der durch 4 geteilten Frequenz des Oszillators.<br />
<br />
CPU Vorgang Richtung Speicher<br />
------------------------------------------------- -<br />
1.Befehl lesen (fetch) <------- Flash |<br />
2.Daten lesen (read) <------- RAM | 1 Prozessortakt =<br />
3.Daten verarbeiten (execute) | 4 Oszillatortakte<br />
4.Daten schreiben (write) -------> RAM | <br />
-<br />
<br />
Nur o.g. CPU Vorgänge sind direkt möglich. Es können deswegen keine Befehle aus dem RAM oder EEPROM ausgeführt werden. Um ein Databyte aus einem RAM Register in ein anderes zu kopieren, muss er zuerst aus dem ersten RAM Register in das W-Register (eigenen s.g. Arbeitsregister des CPU) und erst davon in das zweite RAM Register kopiert werden.<br />
<br />
Der Prozessor hat einen kleinen eigenen Speicher, der weder zum Programmspeicher noch zum Datenspeicher gehört. Er besteht aus dem Programmzähler PC (program counter) und dem Stapel (stack).<br />
<br />
In dem PC befindet sich die Adresse des momentan ausführbaren Befehls. Nach der Ausführung jedes Befehls wird der PC um 1 erhöht und "zeigt" somit auf den nächsten Befehl im Programmspeicher, der zum Ausführen wäre. Wenn der nächste Befehl aber z.B. ein Sprung ("call") ist, wird die Adresse aus dem Befehl genommen. Nach dem Sprung wird das Programm bis zum Befehl "return" ausgeführt und danach muss der Prozessor zurückspringen, und zwar, genau zur nächsten Adresse im Programmspeicher nach dem "call" Befehl.<br />
<br />
Um dies zu ermöglichen, wird vor dem Sprung die Adresse aus dem PC "auf dem Stapel gelegt" (gespeichert). Für den Rücksprung wird die abgelegte Adresse "aus dem Stapel genommen" (vom Stapel in den PC geladen). Beim Auftreten eines Interrupts wird auf dem Stapel die Adresse, der zuletzt ausgeführten Zeile abgelegt, damit der Prozessor, wenn er nach der Ausführung der "Interruppt Service Routine" (ISR) an "retfie" kommt, das unterbrochene Programm an der richtigen Stelle wieder startet. <br />
<br />
Der Stapel kann aber nur 8 bzw. 31 Adressen speichern, deswegen darf nur entsprechende Anzahl von nacheinander folgenden "call" Befehlen benutzt werden. Wenn ein Interrupt benutzt wird, reduziert sich es um 1, da eine Speicherstelle immer für die Adresse, an der das Programm unterbrochen wurde, reserviert werden muss. Sonst findet der Prozessor nicht mehr zurück und springt in die "Nirvana" , was einen Absturz des Programms bedeutet. Bei dem "goto" Befehl wird die nächste Adresse nicht gespeichert, da der Prozessor nicht zurückkehren braucht.<br />
<br />
Das Lesen/Schreiben aus/in den EEPROM Speicher ist mit Hilfe speziellen Register und Unterprogrammen bei allen PICs möglich. Der Lese und Schreibzugriff auf den Programmspeicher ist aber nur bei wenigen PIC-Typen (z.B. PIC16F87X) möglich. Dies ermöglicht ein "sich selbst Programmieren", was bei Bootloadern genutzt wird.<br />
<br />
== Assembler ==<br />
<br />
Die Maschinensprache, ist eine Sprache die nur eine bestimmte CPU versteht. Für einen Menschen ist sie unverständlich, da sie nur aus Zahlen besteht.<br />
<br />
Um sich die Sprache verständlicher zu machen wurden den Zahlen s.g. Mnemonics aus Buchstaben zugewiesen. Jeder Befehl für einen CPU hat somit ein "Namen", der aus der englischen Sprache stammt. In dieser Form wird auch von Assembler oder kurz ASM gesprochen. Siehe: [[#Kurzübersicht Assembler Befehle|Kurzübersicht Assembler Befehle]]<br />
<br />
Obwohl sie bis zu 1000 mal schneller als die meisten Hochsprachen ist, wird sie, wegen des großen Aufwands bei der Erstellung von umfangreichen Programmen, selten benutzt. Man findet sie aber oft in fast allen Hochsprachen, in eingebundenen Funktionen, überall dort wo die Hochsprachen zu langsam sind oder die nötigen Aufgaben nicht unterstützen (z.B. Maus in Q-Basic).<br />
<br />
ASM eignet sich sehr gut für kleine Anwendungen (meistens Steuerungen) mit µC, weil nur bei dieser Programmiersprache ein direkter Zusammenhang zwischen einem Bit im Programm und einer Spannung am I/O Pin besteht. Aus dem Grund sind Hardware-Kenntnisse sehr vorteilhaft. <br />
<br />
Die Aufgabe eines ASM-Programmierers ist, ein Programm zu schreiben, das das Assemblerprogramm (z.B. MPASM) fehlerfrei in die Machinensprache assembliert ("übersetzt"), die dann eine bestimmte CPU "versteht". Anders als bei einer Hochsprache ist die "Übersetzung" genau vorhersehbar und festgelegt. Sie endet eigentlich erst dann, wenn das geschriebene Programm so wie geplant funktioniert. <br />
<br />
Die beste Methode um ASM Programmierung zu lernen ist einfach mit den Befehlen, wie mit "LEGO" Bausteinen, zu spielen. Dafür wurde ein Programm "PIC Trainer" entwickelt. Der PIC kann durch ein Programmfehler nicht kaputtgehen. Vorsicht ist nur bei der angeschlossener Hardware nötig, da ein Fehler den PIC oder die Hardware sogar "töten" kann. <br />
<br />
Weil ASM Programme nicht besonders durchschaubar sind, wurde als Hilfsmittel ein Programmablaufdiagramm (kurz: PAD) erfunden. Bei der Programmerstellung fängt man damit an, ein PAD zu erstellen, das alle Programmschritte enthält.<br />
<br />
Weiter werden alle Befehle nach dem PAD mit einem üblichen Texteditor in eine Textdatei mit Erweiterung ".asm" (Quellcode) geschrieben, durch ein Assemblerprogramm (für PICs: MPASM oder [http://gputils.sourceforge.net/ GPASM]) von dem für Menschen noch verständlichen Code in die Maschinensprache assembliert und als Textdatei mit Erweiterung ".hex" gespeichert. Diese Datei wird danach in den Programmspeicher des µC übertragen ("gebrannt").<br />
<br />
Das Assemblerprogramm MPASM kann kostenlos von der Homepage des Herstellers von PICs [http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en019469&part=SW007002] runtergeladen werden. Es muss zuerst vom Downloads die "MPLAB IDE v7.50 Full Zipped Installation" runtergeladen und erst danach können gewählte Programme (z.B. nur MPASM) installiert werden. Für MPASM Benutzer werden auch folgende ".pdf" Dateien empfohlen:<br />
<br />
MPASM/MPLINK User's Guide (2628 KB) [Benutzerhandbuch] <br />
<br />
MPASM™/MPLINK™ PICmicro® Quick Chart (81 KB) [Kurzübersicht] <br />
<br />
Nach dem Einschalten der Betriebsspannung des µC, fängt der CPU an, sich im Programmspeicher befindliches Programm mit dem Befehl, der an der Adresse 0 steht, auszuführen.<br />
<br />
Aber wann das Programm endet? Natürlich wenn die Versorgungsspannung abgeschaltet wird. Nein! Das ist die einfachste Lösung um ein laufendes Programm an zufälliger Stelle zu unterbrechen,<br />
aber keine, um es an einer definierten Stelle zu beenden.<br />
<br />
Wenn an den µC angeschlossene externe Hardware (z.B. Grafikdisplay), eine bestimmte Befehlsfolge vor dem Abschalten benötigt oder wichtige Daten (in EEPROM oder Flash) abgespeichert werden sollen, darf die Spannung erst dann abgeschaltet werden, wenn die CPU eine Meldung ausgibt, dass sie sich schon auf der "STOP" Stelle des Programms befindet. Es muss auch<br />
definiert werden (z.B. durch eine Tastenkombination), wann die CPU zum letzten Fragment des ASM Programms vor dem "STOP" gehen soll.<br />
<br />
== Grundbeschaltung ==<br />
<br />
Der Prozessor von einem PIC kann sofort nach dem Einschalten der Versorgungsspannung (z.B. + 5V DC) arbeiten. Allerdings nur, wenn er den Takt, in dem er die Befehle ausführen soll, vorgegeben hat. Manche PICs besitzen einen internen RC-Oszillator, (z.B. PIC12F629, PIC16F630, PIC16F628, usw.). Bei diesen reicht es die Versorgungsspannung anzulegen und sie laufen bereits.<br />
<br />
Die meisten haben ihn aber nicht (z.B. PIC16F84, PIC16F870, usw.) und brauchen fürs Funktionieren zusätzliche Bauteile. Grundsätzlich gibt es mehrere Möglichkeiten:<br />
<br />
* Quarz oder Keramik-Resonator + 2 Kondensatoren (LP,HS oder XT) <br />
* Keramik-Resonator mit integrierten Kondensatoren (HS oder XT)<br />
* Quarzoszillator (EC); genau und stabil<br />
* Widerstand + Kondensator (RC); keine hohe Frequenzstabilität<br />
<br />
Die entsprechenden Bauteile werden an die Pins OSC1/OSC2 angeschlossen, um den notwendigen Prozessortakt zu erzeugen. Im Konfiguration-Word "__config" muss noch angegeben werden, welcher Oszillator (LP, HS, XT bzw. RC) verwendet wird.<br />
<br />
Desweiteren existiert ein MCLR-Pin, der beim PIC einen Neustart (=Reset) auslösen kann (Low-Pegel). Diesen Pin sollte man, wenn er in "__config" aktiviert ist, über einen Widerstand (pull-up) an Versorgungsspannung legen, damit der PIC anfängt, sein Programm abzuarbeiten. Der Anschluss wird auch für die Programmierung benötigt. Beim sog. High-Voltage-Programming wird MCLR auf ca. 12-14 Volt gelegt, um den PIC in den Programmiermodus zu schalten. Bei manchen PICs kann dieser Anschluss auch als normalen I/O Pin eingestellt werden. In dem Fall, bei ICSP Benutzung, soll noch eine Diode zwischen den pull-up und Versorgungsspannung angeschlossen werden, um die an PIC angeschlossene Hardware während der Programmierung vom Auftreten der Vpp an Vcc zu schützen und die Vpp nicht zu belasten.<br />
<br />
VCC<br />
+<br />
|<br />
V Diode<br />
-<br />
|<br />
.-.<br />
| | Pull-up<br />
| | (10k)<br />
'-'<br />
MCLR/Vpp | .-------.<br />
Pin +-| |<br />
| | PIC |<br />
| o | |<br />
Reset |=|> | |<br />
Taster | o '-------'<br />
|<br />
===<br />
GND <br />
<br />
Bei externen Oszillatoren bleibt der Pin OSC2 nicht angeschlossen und kann als I/O benutzt werden. Falls ein interner Oszillator benutzt wird, können beide OSC Pins als I/O dienen.<br />
<br />
Damit ein Programm zuverlässig ausgeführt werden kann, muss die Versorgungsspannung störungsfrei sein. Dafür wird ein Keramik-Vielschicht-Kondensator 100 nF (0,1 µF) möglichst am kürzesten direkt zwischen VDD und VSS Pins geschaltet.<br />
<br />
Folgende Skizzen zeigen die Grundbeschaltung eines PICs:<br />
<br />
{|<br />
|-<br />
|<br />
[[Bild:Pic-entstoer.png|thumb|160px|Entstörkondensator beim PIC]]<br />
|<br />
[[Bild:Qz-os.png|thumb|160px|Quarz ]]<br />
|<br />
[[Bild:Qos-os.png|thumb|160px|externer Quarzoszillator]]<br />
|<br />
[[Bild:Rc-os.png|thumb|160px|externer RC-Oszillator]]<br />
|}<br />
<br />
== Konfiguration ==<br />
<br />
Die Konfiguration eines PICs wird beim "brennen" fest programmiert. Sie ist eigentlich für fast jeden PIC-Typ anders. Um Probleme zu vermeiden, muss sie für bestimmten PIC aus einer im MPASM Verzeichnis enthaltenen Datei "PXXFXX.INC" entnommen werden. Die Erklärung der Optionen befindet sich im entsprechenden Datenblatt unter "Special Features of the CPU"<br />
<br />
Als Beispiel für PIC16F84 haben wir in der Datei "P16F84.INC":<br />
<br />
;==========================================================================<br />
;<br />
; Configuration Bits<br />
;<br />
;==========================================================================<br />
<br />
_CP_ON EQU H'000F'<br />
_CP_OFF EQU H'3FFF'<br />
_PWRTE_ON EQU H'3FF7'<br />
_PWRTE_OFF EQU H'3FFF'<br />
_WDT_ON EQU H'3FFF'<br />
_WDT_OFF EQU H'3FFB'<br />
_LP_OSC EQU H'3FFC'<br />
_XT_OSC EQU H'3FFD'<br />
_HS_OSC EQU H'3FFE'<br />
_RC_OSC EQU H'3FFF'<br />
<br />
Wobei:<br />
<br />
_CP_ON - Programmspeicher ist vorm Auslesen geschützt<br />
_CP_OFF - Programmspeicher kann ausgelesen werden (ist nicht geschützt)<br />
_PWRTE_ON - Das Programm wird mit Verzögerung von ca. 72 ms nach dem Einschalten gestartet<br />
_PWRTE_OFF - Das Programm wird sofort nach dem Einschalten gestartet<br />
_WDT_ON - Watchdog Timer ist aktiv<br />
_WDT_OFF - Watchdog Timer ist unaktiv<br />
<br />
Die alle folgende Optionen beziehen sich an den Oszillatortyp:<br />
<br />
_LP_OSC - Oszillator bis ca. 200 kHz (z.B. Uhrenquarz 32768 Hz)<br />
_XT_OSC - Oszillator bis ca. 3,5 MHz<br />
_HS_OSC - Oszillator über 3,5 MHz (z.B. 4 MHz)<br />
_RC_OSC - Externer RC Oszillator<br />
<br />
Die Konfiguration wird im Quellcode (*.asm Datei) mit Direktive "__config" definiert, z.B. so:<br />
<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC<br />
<br />
Alle Optionen müssen groß geschrieben sein, genauso wie in der "*.inc" Datei.<br />
<br />
Warnung !<br />
<br />
Anhand praktischer Erfahrung kann man feststellen, dass bei den kleinsten PICs der Familie 12FXXX, bei dennen ein I/O Pin mit VPP gemultiplext wird (z.B. PIC12F510, PIC12F629, PIC12F635 und 12F675), das Wählen des VPP Pins als I/O verwandelt ihn ins One Time Programming (OTP) Chip, der nur einmal programmiert werden kann. Die gewählte Konfuguration wird entgültig fest "gebrannt" und wegen seitdem fehlender Verbindung des I/O Pins mit VPP keine Umprogrammierung mehr möglich ist. Wer solchen PIC verwenden möchte, sollte aus dem Grund für Entwicklung anderen PIC-Typ nehmen und erst fertiges ausprobiertes Programm einmalig in den für konkrete Anwendung vorgesehenen PIC brennen.<br />
<br />
== Wahl des PICs ==<br />
<br />
Es gibt PIC µC die in der Typenbezeichnung den Buchstaben "C" oder "F" haben.<br />
<br />
Die älteren mit "C" haben EPROM Programmspeicher und die gibt es in zwei Versionen: ohne und mit Fenster (aus Quarz-Glass) fürs Löschen des EPROMs mit UV Strahlung. Bei denen ohne Fenster kann der Programmspeicher nur einmal beschrieben und nicht mehr gelöscht werden.<br />
<br />
Die neuen mit "F" besitzen einen Flash-Programmspeicher, der bis zu 100 000 mal mit angelegter Spannung gelöscht und danach neu beschrieben werden kann.<br />
<br />
Für die Wahl eines PICs für bestimmte Anwendung wichtig sind:<br />
<br />
- Max. Taktfrequenz des Prozessors.<br />
<br />
- Größe des Datenspeichers (für Variablen).<br />
<br />
- Größe des Programmspeichers (für Programm).<br />
<br />
- Integrierte Hardware (Komparatoren, A/D Wandler, Timer, USART, I²C, SPI, PWM, usw.).<br />
<br />
- Freie I/O Pins für externe Hardware (Display, Tasten, usw.).<br />
<br />
- Vorhandene Betriebspannung (Netzteil, Akku, Batterie).<br />
<br />
In der Praxis wird meistens für die Programmerstellung ein grösserer PIC genommen (wenn möglich pinkompatibler z.B. PIC16F628 für PIC16F84 oder PIC16F630 für PIC12F629) und erst nach der Optimierung des lauffähiges Programms, der tatsächlich nötige, da seine Parameter am Anfang nur geschätzt werden können. Wenn man viele Programme für verschiedene PICs entwickelt, wäre der größte PIC16F877 mit 20 MHz max. Taktfrequenz optimal.<br />
<br />
Diese Lösung hat auch den Vorteil, dass während der Programmerstellung kurze Hilfsprogramme (z.B. "PIC Trainer") in den Programmspeicher kopiert und benutzt werden können, da sie sowohl ein bißchen Programmspeicher und RAM als auch 2 freie I/O Pins fürs PIC Miniterminal brauchen.<br />
<br />
= Programm =<br />
<br />
== Allgemeines ==<br />
<br />
Jedes Programm kann man in kleinere Fragmente unterteilen, die auf bestimmte Weise miteinander verknüpft sind und gemeinsam die Aufgabe des Programms erfüllen. Das wichtigste Teil eines Programms ist das s.g. Hautprogramm (kurz:HP), das eine führende Rolle spielt. Dem HP sind fast alle andere Programmteile untergeordnet (weiter als Unterprogramm (kurz:UP) genannt) und werden nach Bedarf von ihm aufgerufen, um eine bestimmte Aufgabe zu erledigen.<br />
<br />
Die Struktur eines Programms ist aber komplizierter, da ein UP auch ein oder mehrere UPs nacheinander aufrufen kann. Ganz unten sind die UP1s, die ganz einfache Sachen erledigen. Höher ist das nächste Ebene mit UP2s die schon mehr komplizierten Aufgaben durch ein Aufruf der UP1s erledigen können, usw. Bei Mid-Range PICs (12FXXX und 16FXXX) können ohne Tricks maximal bis zu 8 Ebenen benutzt werden, da auf dem Stapel (stack) nur max. 8 Rücksprungadressen abgespeichert werden können. Siehe hierzu: [[#Prozessor|Prozessor]] und [[#Programmspeicher|Programmspeicher]]<br />
<br />
<center><br />
[[Bild:HP-UP.png|Hauptprogramm - Unterprogramm]]<br />
</center><br />
<br />
Jedes UP kann jederzeit aufgerufen werden, je nach dem was gerade erledigt werden muss. Weil das nicht egal ist, welches UP aufgerufen wird, da jedes nur eine bestimmte Funktion im Programm hat, muss der Programmierer dafür sorgen, dass alles richtig nach Programablaufdiagramm, und nicht chaotisch, abläuft.<br />
<br />
Die Programmierung in ASM ist ähnlich wie bei Hochsprachen, wenn man sich Bibliotheken mit geprüften prozessorspezifischen UPs erstellt. Um ein lauffähiges Programm zu erstellen, braucht man nur benötigte UPs ins Programm kopieren oder einbinden und ein geeignetes HP, das sie aufruft, schreiben.<br />
<br />
Ein ASM Programm (Quellcode) muss in einer Textdatei mit der Endung ".asm" in der vom Assemblerprogramm erwarteten Form verfasst werden, um die fehlerfreie Konvertierung in die Maschinensprache (Assemblierung) zu gewährleisten. Dieser Prozess verläuft in der Form eines Dialoges.<br />
<br />
Der Programmierer schreibt und gibt es dem Assemblerprogramm zum Übersetzen. Alles was der Assemblerprogramm nicht versteht oder nicht richtig ist, erscheint als Fehlermeldungen, die der Programmierer kennen muss, um die Fehler korrigieren zu können. Eine ".hex" Datei wird erst dann erstellt, wenn das Assemblerprogramm keine Fehler mehr im Quellcode findet. Deswegen ist es sehr wichtig, sich mit dem Assemblerprogramm vertraut zu machen, um die Dialogzeit zu minimieren.<br />
<br />
== Programmablaufdiagramm (PAD)==<br />
<br />
Der Programablaufdiagram (kurz: PAD) ist eine vorläufige und laufend änderbare Stufe zwischen einer Idee und ihrer Verwirklichung. Die Kreativität des Programmierers ist nur für die Erstellung des PADs nötig. Jedes sein Symbol (außer "Start/Stop") muss als Befehlsreihenfolge für einen bestimmten Prozessor in den Quellcode übertragen werden. Das ist aber nur reine "Übersetzung". Der Quellcode ist nur eine andere Form des PADs und hoffentlich wird es zukünftig Computerprogramme geben, die ein PAD direkt in eine "*.hex" Datei für bestimmten Prozessor wandeln werden. Zur Zeit muss die "Übersetzung" leider vom Programmierer gemacht werden. Der PAD wird erst dann fertig, wenn nach ihm erstelltes ASM Programm auf einem µC so wie gewünscht funktioniert.<br />
<br />
Die Anschriften "Ein" und "Aus" gehören nicht zu Symbolen des PADs und wurden nur zur Erklärung benutzt.<br />
<br />
[[Bild:PAD_beispiel.png|thumb|80px|Beispiel für ein PAD]]<br />
<br />
Der PAD ist sehr einfach zu erstellen, weil dafür nur drei Symbole benötigt sind:<br />
<center><br />
[[Bild:PAD_kurz.png|Symbole des PAD]]<br />
</center><br />
<br />
Bei PAD Erstellung darf man selbstverständlich beliebige Symbole verwenden, die man selber am besten versteht. <br />
<br />
Das "Start/Stopp" Symbol bedeutet, dass das gesamte Programm sich im stabilen Zustand befindet und nicht "läuft". Anstatt "Stopp" kann auch "Schlaf" ("sleep") angewendet werden, da das Programm in dem Fall auch nicht aktiv ist. Das "Tun" Symbol stellt meistens ein UP mit Reihenfolge von Befehlen dar. Das "Prüfen" bedeutet eine Prüfung bestimmter Bedingung und abhängig davon einen weiteren Lauf eines Programms, entweder in der "ja" (J) oder "nein" (N) Richtung.<br />
<br />
Als allgemeinnütziges Standard für µCs kann man folgender PAD bezeichnen:<br />
<br />
PAD _____<br />
/ \<br />
Spannung ein (Ein) ----->( Start )<br />
\_____/<br />
| -<br />
V |<br />
.---------------. |<br />
|Initialisierung| |<br />
'---------------' |<br />
| |<br />
.--------->V |<br />
| .---------------. |<br />
| | Hauptprogramm | |<br />
| '---------------' |<br />
| | |<br />
| V |<br />
| A |<br />
| / \ > Gesamtes Programm <br />
| / \ |<br />
| /Ende \____ |<br />
| \ ? / J | |<br />
| \ / | |<br />
| \ / | |<br />
| V | |<br />
| N| | |<br />
`----------´ | |<br />
V |<br />
.---------------. |<br />
| Beenden | |<br />
'---------------' |<br />
| |<br />
V -<br />
_____<br />
/ \<br />
Spannung aus (Aus) <-------------( Stopp )<br />
\_____/<br />
<br />
Das Hauptprogramm wird in einer endlosen Schleife ausgeführt, die durch die Prüfung "Ende?" unterbrochen werden kann. In dem Fall wird vor dem Beenden des gesamten Programms noch ein UP "Beenden" ausgeführt, das z.B. Daten im EEPROM speichert.<br />
<br />
Es ist nicht nötig, immer die Symbole zu zeichnen, man kann sie sich vorstellen und nur den Text schreiben. Die Prüfungen werden mit "?" gekennzeichnet und die Zeichen "V", "A", "<" und ">" zeigen die Richtung des weiteren Verlaufs. Dann sieht der PAD so aus:<br />
<br />
Ein > Start<br />
V - <br />
Initialisierung |<br />
.------->V |<br />
| Hauptprogramm > Gesamtes Programm<br />
| V | <br />
| Ende? J > Beenden |<br />
| N V -<br />
| V Stopp > Aus<br />
`--------´<br />
<br />
Man kann auch die unbedeutenden Richtungspfeilen weg lassen:<br />
<br />
PAD1 Ein > Start _<br />
Initialisierung |<br />
.------->V | Gesamtes<br />
| Hauptprogramm | Programm<br />
| Ende? J > Beenden _|<br />
| N Stopp <br />
| V V<br />
`--------´ Aus<br />
<br />
In der Praxis werden aus Platzgründen meistens die vereinfachten PADs benutzt. Als "movxx" wird oft ein Zeichen "->" benutzt. Zum Beispiel "A->W" bedeutet, dass der Inhalt des A Registers ins W-Register geladen (kopiert) wird. Außerdem werden Zeichen ("+", "-", "*", "/", "^", usw.) für arithmetische und ("&", "or", "xor", usw.) für logische Operationen angewendet. In den Quellcode werden für diese Zeichen entsprechende Befehle geschrieben. Beispiel:<br />
<br />
V<br />
10h->W<br />
W+B->B<br />
V<br />
<br />
wird im Quellcode so aussehen:<br />
<br />
...........<br />
movlw 10h<br />
addwf B,1<br />
...........<br />
<br />
Siehe auch: [[#Das erste Programm|Das erste Programm]] und [[#Mausrad bzw. Drehencoder|Mausrad bzw. Drehencoder]]<br />
<br />
Der PAD1 kann aber für Hauptprogramme, die in beliebigem Moment unterbrochen werden dürfen, deutlich vereinfacht werden, da die Prüfung "Ende?" ob das Hauptprogramm beendet werden soll, und das UP "Beenden", entfallen.<br />
<br />
Die meisten ASM Programme für µC sind deswegen nach solchem PAD erstellt:<br />
<br />
PAD2 Ein > Start _<br />
Initialisierung |<br />
.------->V |<br />
| Hauptprogramm > Gesamtes Programm<br />
| V |<br />
`--------´ _|<br />
<br />
Für Testprogramme wird meistens folgender PAD angewendet, weil es ziemlich einfach festzustellen<br />
ist (z.B. durch Stromverbrauchmessung des µCs), wann sich die CPU schon im Schlaf befindet. Erst dann, darf die Betriebspannung des µCs ausgeschaltet werden.<br />
<br />
PAD3 Ein > Start _<br />
Initialisierung | Gesamtes<br />
Hauptprogramm _| Programm<br />
Schlaf > Aus<br />
<br />
Und eine batteriebetriebene Uhr wird überwiegend so gestaltet:<br />
<br />
PAD4 Ein > Start _<br />
Interrupt Initialisierung |<br />
Timer------------------------->V > Gesamtes Programm<br />
Hauptprogramm _|<br />
Schlaf<br />
<br />
In dem Fall reicht es aus, wenn die CPU jede Minute vom Timer aufgeweckt wird, um die Zeit zu aktualisieren. Eine Uhr ist immer (außer Batteriewechsel) ununterbrochen mit Spannung versorgt.<br />
<br />
Für komplizierte Programme ist es praktisch unmöglich ein PAD zu erstellen, in dem jeder CPU Befehl sein eigenes Symbol hat. Man beschränkt sich nur auf alle Prüfungen, die über den Lauf des Programms entscheiden, und ganze UPs (z.B. "Initialisierung") nur als ein Symbol verwendet. Für jedes UP wird dann ein eigener PAD erstellt.<br />
<br />
Das Erstellen von PAD bei ASM Programmen ist sehr wichtig und darf nicht unterschätzt werden. Je stärker ein Programmierer glaubt, dass er das ohne PAD schafft, um so mehr Zeit wird er danach bei Fehlersuche oder Änderungen im ASM Programm verlieren.<br />
<br />
Beispiel aus dem Alltag:<br />
<br />
"Das Programm hat anfangs auf Anhieb funktioniert. Doch leider habe ich dann was geändert was ich nicht mehr weiß. Das Programm macht nun komische Sachen...... Ich kann mir nicht erklären warum, kann mir jemand helfen???" <br />
<br />
Für einfache ASM Programme, die gut kommentiert sind, reicht es meistens aus, ein PAD nur "im Kopf" zu erstellen, aber ganz ohne PAD geht es sicher nicht.<br />
<br />
Der PAD kann auch benutzt werden, um einen "fremden" Code verständlich zu machen. In dem Fall werden alle Befehle (Zeilen) aus dem Quellcode nacheinander in PAD Symbole umgewandelt und daraus ein PAD erstellt.<br />
<br />
== Hauptprogramm ==<br />
<br />
Wie sein Namen schon vermuten lässt, ist das Hauptprogramm das wichtigste Teil des gesamten Programms. Meistens ist es auch das kleinste Teil, vor allem, wenn die UPs sehr komplex sind. Seine Aufgabe ist die benötigte UPs in bestimmter Reihenfolge nacheinander aufzurufen, um die alle Funktionen des gesamten Programms zu realisieren. <br />
<br />
Das HP ist meistens als endlose Schleife, wie im PAD2, aufgebaut. Weil die endlose Schleife sehr schnell läuft, werden alle durch die UPS realisierten Aufgaben quasi gleichzeitig ausgeführt. Wenn es unerwünscht ist, müssen einige UPs als Verzögerungen realisiert werden. Dafür können auch UPs mit fester Ausführungszeit (z.B. Displayausgabe) angewendet werden.<br />
<br />
Typischer PAD für ein HP sieht so aus:<br />
<br />
Haupt .--->V<br />
| UP1<br />
| UP2<br />
| ...<br />
| UPn<br />
| V<br />
`----´<br />
<br />
In den Quellcode wird es so eingeschrieben:<br />
<br />
Haupt call UP1 <br />
call UP2<br />
...........<br />
call UPn<br />
goto Haupt<br />
<br />
In der Praxis wird das HP schrittweise erstellt. Am Anfang wird sich nur ein Aufruf vom UP im HP befinden und die folgenden kommen nach Erstellung und Prüfen weiteren UPs dazu, bis das HP fertig wird.<br />
<br />
=== Multitasking ===<br />
<br />
Echtes Multitasking ist nur mit mehr (Core)Prozessoren möglich. Bei PIC's ist echtes Multitasking (Parallellaufen) nur mit Timer und ADC Wandler möglich (siehe dazu: http://www.roboternetz.de/community/threads/42200-Frequenzz%C3%A4hler-mit-LPH2673-1-%28zum-Nachbauen%29?highlight=LPH2673-1 ) <br />
<br />
Sonst gibt es nur Quasi-Multitasking, das kann auf zwei Weisen realisiert werden:<br />
<br />
1. Alle Tasks werden in fester bzw. per Interrupts bestimmter Reihenfolge auf Bedarf geprüft und nur die mit gesetztem Flag werden vollständig bis zum Ende realisiert. Als Beispiel sehe: [[http://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=47685]]. Es kann natürlich mit Proritäten für Interrupts versehen werden.<br />
<br />
2. Der s.g. Taskmanager (meistens HP) gibt jedem Task (meistens UP) feste Zeit und wenn der Task aktiv ist, wird er nach dieser Zeit z.B. per Timer unterbrochen und nächster Tast gestartet. Dafür muss immer nach Beenden der ISR unbedingt ins HP gesprungen werden und die Adresse vom PC, wo der Task unterbrochen wurde auf dem Stapel gespeichert werden. Wenn die Zeit für den unterbrochenen Task wieder kommt, wird er ab der unterbrochenen Stelle wieder in der ihn zustehenden Zeit ausgeführt, wieder unterbrochen u.s.w.<br />
<br />
Dafür muss der Stapel mit Rücksprungadressen laufend mit "pop" and "push" Befehlen bearbeitet werden, was erst ab PIC18... möglich ist Bei dieser Methode bei kurzen Laufzeiten für jeden Task, sieht der Beobachter praktisch keine Unterbrechungen von Tasks. Auch hier können Prioritäten benutzt werden, aber z.B. Zeiten für Warteschleifen können nicht genau berechnet werden. Das Unterbrechen von Tasks ist auch per Software-Interrupts möglich.<br />
<br />
== Unterprogramm ==<br />
<br />
Unterprogramm wird durch übergeordnetes Programmteil (Aufrufer) mit "call" aufgerufen und nach seinem Ausführen, wird zurück zum Aufrufer in die Zeile nach dem "call" gesprungen. Der Rückkehr zum Aufrufer wird durch "return" bzw. "retlw" Befehl, der sich am Ende jedes UPs befinden muss, erreicht. Und das ist der einzige Unterschied zwischen einem HP und einem UP.<br />
<br />
Jedes UP hat folgender PAD:<br />
<br />
vom Aufrufer ------------>V<br />
Tun<br />
V<br />
zurück zum Aufrufer <----------return bzw. retlw <br />
<br />
Bei dem Rückkehr aus dem UP mit "retlw" befindet sich im W-Register der Wert aus dem Befehl "retlw". Dies wird benutzt um zu erkennen aus welchem UP das verzweigte Programm zurückkommt und ermöglicht eventuelle Fehler beim Ausführung des Programmteils zu erkennen.<br />
<br />
Ein HP von einem ASM Programm kann in anderem, mehr umfangreichem ASM Programm als UP benutzt werden, wenn der sich am Ende des HPs befindlicher Befehl "goto" durch "return" ersetzt wird. Ein Beispiel dazu:<br />
<br />
Haupt1 call UP11 Haupt1 call UP11<br />
call UP21 call UP21<br />
........... -------> ...........<br />
call UPn1 call UPn1 <br />
goto Haupt1 return <br />
<br />
Jetzt können wir im mehr komplexen HP (Haupt) das Haupt1 als Unterprogramm aufrufen:<br />
<br />
Haupt call UP1 <br />
call Haupt1<br />
...........<br />
call UPn<br />
goto Haupt<br />
<br />
Jedes UP kann auch von einem anderen übergeordneten UP aufgerufen werden, wenn das was es realisiert, benötigt wird.<br />
<br />
In der Praxis wird oft ein UP von mehreren anderen UPs benutzt. Zum Beispiel um LCD Display zu steuern, brauchen wir entweder ein Befehl (Cmd) oder ein Zeichen (Data) an Display zu schicken. In beiden Fällen wird ein Byte geschickt, einmal mit RS=0 (Befehl) und einmal mit RS=1 (Zeichen) laut folgendem PAD:<br />
<br />
"Cmd" "Data" <br />
RS=0 RS=1<br />
V V <br />
`-->V<--´<br />
"Send" Byte schicken<br />
V<br />
return<br />
<br />
Das wird in den Quellcode z.B. so eingeschrieben:<br />
<br />
Cmd bcf RS<br />
goto Send<br />
Data bsf RS<br />
Send ............<br />
return<br />
<br />
Das UP "Send" ist den UPs "Cmd" und "Data" untergeordnet, da es von beiden benutzt wird, kann aber weder "Cmd" noch "Data" benutzen.<br />
<br />
=== Initialisierung ===<br />
<br />
Damit der PIC ein Programm ausführen kann, muss er vollständig und richtig konfiguriert und initialisiert werden. Deswegen als erstes UP, das von dem gesamten Programm noch vor dem HP aufgerufen wird , ist "Initialisierung" (kurz: Init)<br />
<br />
==== Variablen ====<br />
<br />
Weil sich nach dem Einschalten der Spannung im RAM zufällige Werte befinden, wird oft als erstes der benutzte Bereich des RAMs (z.B. 20h bis 7Fh) gelöscht. Es wird einfach und sparsam mit einer Schleife erledigt, die indirekte Adressierung verwendet:<br />
<br />
V<br />
Adresse des ersten Registers in FSR laden (20h)<br />
.-------------------->V<br />
RAMClr |Indirekt adressierter Register löschen (INDF)<br />
| Adresse erhöhen<br />
| Letzte Adresse + 1 = 80h J > Return<br />
| N<br />
| V<br />
`---------------------´<br />
<br />
Es wird wie folgt in Quellcode eingeschrieben:<br />
<br />
movlw 0x20<br />
movwf FSR<br />
RAMClr ,->clrf INDF<br />
| incf FSR,1<br />
| btfss FSR,7<br />
`-<goto RAMClr<br />
return<br />
<br />
Um Anzahl den Marken (label) zu verringern, wird oft ein Symbol "$" benutzt. Es bedeutet die Adresse der aktuellen Befehlszeile. Es kann also auch so geschrieben werden:<br />
<br />
movlw 0x20<br />
movwf FSR<br />
,->clrf INDF<br />
| incf FSR,1<br />
| btfss FSR,7<br />
`-<goto $-3 ; springe zu aktueller Adresse -3<br />
return<br />
<br />
Danach können den benötigten Variablen die gewünschten Werte zugewiesen werden:<br />
<br />
movlw 0x3C<br />
movwf LimH<br />
movlw 0x5A<br />
movwf LimL<br />
u.s.w.<br />
<br />
Somit sind die Variablen initialisiert.<br />
<br />
==== I/O Ports ====<br />
<br />
Nach dem Einschalten der Spannung sind die für Komparatoren oder A/D Wandler benutzte Pins als analoge Eingänge initialisiert. Wenn sie alle als digitale I/Os verwendet werden sollen, müssen sie als solche definiert werden. Das geschieht durch Schreiben des Wertes 7 in das entsprechende Register (CMCON bzw. ADCON1):<br />
<br />
movlw 7 bzw. movlw 7 <br />
movwf CMCON movwf ADCON1<br />
<br />
Wenn einige als analoge Eingänge benutzt werden sollen, müssen die entsprechende Werte dem Datenblatt des jeweiligen PICs entnommen werden. <br />
<br />
Danach werden in alle Ports nacheinander die gewünschte Werte die an den Pins vor dem Start des Hauptprogramms ausgegeben werden sollen, geschrieben:<br />
<br />
clrf PORTA<br />
movlw 0x37<br />
movwf PORTB <br />
usw.<br />
<br />
Anschließend werden für jeden Port die Werte in TRISx Register eingeschrieben, wobei ein Bit einem Pin entspricht. Ein Pin wird in TRISx Register durch 1 als Eingang und durch 0 als Ausgang definiert. Beispielweise beim PORTB sollen B7,B5 und B3 als Eingänge und restliche Pins als Ausgänge definiert werden. Das ergibt den Wert 10101000b = A8h, der in den TRISB Register geschrieben werden muss. Weil die alle TRISx Register sich in der höheren Bank befinden, muss im STATUS-Register auf entsprechende Bank und danach zurück auf Bank 0 umgeschaltet werden. Zum Beispiel für die TRISB in der Bank1:<br />
<br />
bsf STATUS,RP0<br />
movlw 0xA8<br />
movwf TRISB<br />
bcf STATUS,RP0<br />
<br />
Bei einem Umschalten der Bank können selbstverständlich alle TRISx Register, die in der gleichen Bank liegen, nacheinander beschrieben werden. Siehe : [[#PORTx|PORTx]] und [[#TRISx|TRISx]]<br />
<br />
Bei dem PORTA gibt es Pins, die nur "open drain" Ausgang haben (können nur auf GND schalten) bzw. nur als Eingang benutzt werden können. Bei Planung der Verwendung von PORTA muss immer im Datenblatt geprüft werden, ob sich ein bestimmter Pin für die geplante Anwendung eignet.<br />
<br />
==== Hardware ====<br />
<br />
Die für ASM Programm benutzte Hardware kann auf integrierte und externe geteilt werden. Für eine Initialisierung der integrierten Hardware (Komparatoren, A/D Wandler, Timer, USART, I²C, SPI, PWM, usw.), müssen entsprechende SFRs (Spezial Function Registers) laut Datenblatt des PICs definiert werden.<br />
<br />
Die externe Hardware muss nach Datenblättern der Hersteller initialisiert werden.<br />
<br />
=== Einlesen ===<br />
<br />
Um ein Bit von einem Portpin einzulesen und in ein bestimmtes Register zu Kopieren wird folgender PAD benutzt, weil ein PIC kein Befehl dafür hat:<br />
<br />
V<br />
Quellbit = 0 ? N >------.<br />
J |<br />
V |<br />
Bit im Zielregister löschen |<br />
V<-------------´<br />
Quellbit = 1 ? N >------.<br />
J |<br />
V |<br />
Bit im Zielregister setzen |<br />
V<-------------´<br />
<br />
Wenn wir z.B. ein bit3 von PortA als bit1 in den Register Tasten kopieren wollen, dann wird es in Quellcode so geschrieben:<br />
<br />
btfss PORTA,3<br />
bcf Tasten,1<br />
btfsc PORTA,3<br />
bsf Tasten,1<br />
<br />
Wenn es zulässig ist, dass sich das Bit im Zielregister kurzzeitig ändert, kann man sich die erste Zeile im Quellcode ersparen:<br />
V<br />
Bit im Zielregister löschen<br />
Quellbit = 0 ? J >------.<br />
N |<br />
V |<br />
Bit im Zielregister setzen |<br />
V<-------------´<br />
<br />
bcf Tasten,1<br />
btfsc PORTA,3<br />
bsf Tasten,1<br />
<br />
Wenn ein ganzes Byte vom Port in das W-Register eingelesen wird, kann man den Wert gleich komplett in das Zielregister schreiben:<br />
<br />
movf PORTA,0<br />
movwf Tasten<br />
<br />
=== Ausgeben ===<br />
<br />
Um ein Bit an einem Portpin auszugeben wird ein bestimmter Bit mit "bcf" gelöscht oder mit "bsf" gesetzt. Zum Beispiel bit4 im PORTA:<br />
<br />
bcf PORTA,4.<br />
<br />
Um ein Byte auszugeben wird es zuerst in das W-Register geladen und danach an den Port übergeben, z.B.:<br />
<br />
movlw 0x12<br />
movwf PORTA<br />
<br />
=== Schleifen ===<br />
<br />
Ein in ASM Programmen meist verbreitetes Codefragment ist eine Schleife.<br />
<br />
Es kann eine endlose Schleife, die durch Prüfung einer bestimmten Bedingung (z.B. Taste) unterbrochen wird, sein:<br />
<br />
V<-----.<br />
Tun |<br />
? N >--´<br />
J<br />
V<br />
weiter<br />
<br />
Es kann eine Schleife mit Schleifenzähler (z.B. Temp), die bestimmte Anzahl Durchläufe hat, sein:<br />
<br />
V<br />
Anzahl ins Temp laden <br />
V<---------. <br />
Tun |<br />
Temp decrementieren | <br />
Temp = 0 ? N >--´<br />
J<br />
V<br />
weiter <br />
<br />
Solche Schleifen können als UPs verwendet werden, wenn "weiter" mit "return" ersetzt wird.<br />
<br />
Das waren Beispiele für Schleifen, die bewusst programmiert sind. Es gibt leider auch endlose Schleifen, die durch einen Fehler im Programm enstanden sind und zum "hängen" des Programms führen. Solche Schleifen sind im Programm nicht einfach zu finden, da ihre Parameter unbekannt sind. In dem Fall sehr behilflich ist der [[#PIC RAM Monitor|PIC RAM Monitor]] bzw. [[#PIC Trainer|PIC Trainer]].<br />
<br />
=== Pause ===<br />
<br />
Um eine Pause (Warten, Verzögerung) im Programm anzulegen wird der "nop" Befehl benutzt, während dessen Ausführung der CPU nichts macht. Mit einem "nop" kann eine Zeit gleich 4 Takten (Perioden) des Oszillators realisiert werden.<br />
<br />
Für sehr kurze Wartezeiten benutzt man eine Reihe von nachfolgenden "nop"s. Beim einem Oszillator 4 MHz, die Ausführungszeit des Prozessors beträgt 1 µs pro "nop". Wenn z.B. 4 µs benötigt werden, werden dafür 4 "nop"s gebraucht:<br />
<br />
...<br />
nop<br />
nop<br />
nop<br />
nop<br />
...<br />
<br />
Das gleiche bewirken 2 "goto"s, brauchen aber im Vergleich zu "nop"s nur die Hälfte der Bytes im Programmspeicher:<br />
<br />
<br />
...<br />
goto $+1<br />
goto $+1<br />
...<br />
<br />
Der Befehl goto $+1 bedeutet "springe zur aktuellen Adresse + 1" (also nächster Adresse) und seine Ausführungszeit ist gleich 2 Prozessortakten.<br />
<br />
Für kurze Zeiten werden s.g. Warteschleifen, wie im folgendem PAD benutzt:<br />
<br />
V<br />
n * nop<br />
P0 laden<br />
V<---------.<br />
P0 decrementieren |<br />
P0 = 0 ? N >--´<br />
J<br />
V<br />
<br />
In Quellcode wird es als UP so eigeschrieben: <br />
<br />
Warte nop Warte nop<br />
... ...<br />
nop nop <br />
movlw 0xXX movlw 0xXX<br />
movwf P0 movwf P0 <br />
Warte0 decfsz P0,1 decfsz P0,1<br />
goto Warte0 goto $-1<br />
return return<br />
<br />
Der Sprung zur Marke "Warte0" ("goto Warte0") wurde in rechtem Beispiel durch "goto $-1" ersetzt.<br />
<br />
Die gesammte Anzahl den CPU Takten (N) lässt sich aus folgender Formel berechnen:<br />
<br />
N = 3 * P0 + 6 + n <br />
<br />
und die Wartezeit (T) in Sekunden:<br />
<br />
T = N * ( 4 / Fosc ), für Schätzungen T ~ 3 * P0 * ( 4 / Fosc ) <br />
<br />
Wobei:<br />
<br />
P0 = Zahl im Register P0, 6 = Ausführungszeit von "call" (2) + "movlw" (1) + "movwf" (1) + "return" (2), n = Anzahl "nop"s, Fosc = Frequenz des Oszillators (z.B. Quartz)<br />
<br />
Wenn in ein Register PX eine 0 eingeschrieben wird, bedeutet es, dass die decrementierung des Registers 100h = 256d Takten dauern wird, da dekrementierte 0 eine FFh = 255d ergibt. Deshalb in Warteschleifen ist die Zahl 0 die grösste (256 Dürchläufe) und 1 die kleinste. Für solche einfache Schleifen (als UP) die Wartezeit beträgt min. 9 und max. ca. 800 Prozessortakten (ohne "nop"s). <br />
<br />
Für mittlere Wartezeiten z.B. 0,1 Sekunde werden doppelte Warteschleifen verwendet:<br />
<br />
Warte V<br />
n * nop<br />
P1 laden<br />
Warte1 V<-------------.<br />
P0 laden |<br />
Warte0 V<---------. |<br />
P0 decrementieren | |<br />
P0 = 0 ? N >--´ |<br />
J |<br />
V |<br />
P1 dekrementieren |<br />
P1 = 0 ? N >------´<br />
J<br />
V<br />
<br />
Das wird in Quellcode als UP so aussehen:<br />
<br />
Warte nop Warte nop<br />
... ...<br />
nop nop<br />
movlw 0xXX movlw 0xXX<br />
movwf P1 movwf P1 <br />
Warte1 movlw 0xXX movlw 0xXX<br />
movwf P0 movwf P0<br />
Warte0 decfsz P0,1 decfsz P0,1<br />
goto Warte0 goto $-1<br />
decfsz P1,1 decfsz P1,1<br />
goto Warte1 goto $-5<br />
return return<br />
<br />
Wie vorher wurden rechts die Marken durch "$-n" ersetzt. <br />
<br />
Die gesammte Anzahl den CPU Takten (N) lässt sich aus folgender Formel berechnen:<br />
<br />
N = P1 * (3 * P0 + 5) + 8 + n<br />
<br />
und die Wartezeit (T) in Sekunden:<br />
<br />
T = N * ( 4 / Fosc ), für Schätzungen T ~ 3 * P1 * P0 * ( 4 / Fosc )<br />
<br />
Wobei:<br />
<br />
P0 = Zahl im Register P0, P1 = Zahl im Register P1, 8 = Ausführungszeit von "call" + "return" + 2 * ("movlw" + "movwf"), n = Anzahl "nop"s, Fosc = Frequenz des Oszillators (z.B. Quartz)<br />
<br />
Für solche doppelte Schleifen (als UP) die Wartezeit beträgt min. 16 und max. ca. 200 000 Prozessortakten (ohne "nop"s). <br />
<br />
Für lange Wartezeiten z.B. 1 Sekunde werden 3-fache Warteschleifen angewendet.<br />
<br />
Solche Warteschleife ist im folgenden PAD abgebildet:<br />
<br />
Warte V<br />
n * nop<br />
P2 laden<br />
Warte2 V<-----------------.<br />
P1 laden |<br />
Warte1 V<-------------. |<br />
P0 laden | |<br />
Warte0 V<---------. | |<br />
P0 decrementieren | | |<br />
P0 = 0 ? N >--´ | |<br />
J | |<br />
V | |<br />
P1 decrementieren | |<br />
P1 = 0 ? N >------´ |<br />
J |<br />
V |<br />
P2 dekrementieren |<br />
P2 = 0 ? N >----------´<br />
J<br />
V<br />
<br />
Das wird in Quellcode als UP so aussehen:<br />
<br />
Warte nop Warte nop<br />
... ...<br />
nop nop<br />
movlw 0xXX movlw 0xXX<br />
movwf P2 movwf P2<br />
Warte2 movlw 0xXX movlw 0xXX<br />
movwf P1 movwf P1 <br />
Warte1 movlw 0xXX movlw 0xXX<br />
movwf P0 movwf P0<br />
Warte0 decfsz P0,1 decfsz P0,1<br />
goto Warte0 goto $-1<br />
decfsz P1,1 decfsz P1,1<br />
goto Warte1 goto $-5<br />
decfsz P2,1 decfsz P2,1 <br />
goto Warte2 goto $-9<br />
return return<br />
<br />
Auch hier wurden rechts die Marken durch "$-n" ersetzt. <br />
<br />
Die gesammte Anzahl den CPU Takten (N) lässt sich aus folgender Formel berechnen:<br />
<br />
N = P2 * [ P1 * (3 * P0 + 5) + 9 ] + 10 + n<br />
<br />
und die Wartezeit (T) in Sekunden:<br />
<br />
T = N * ( 4 / Fosc ), für Schätzungen T ~ 3 * P2 * P1 * P0 * ( 4 / Fosc )<br />
<br />
Wobei:<br />
<br />
P0 = Zahl im Register P0, P1 = Zahl im Register P1, P2 = Zahl im Register P2, 10 = Ausführungszeit von "call" + "return" + 3 * ("movlw" + "movwf"), n = Anzahl "nop"s, Fosc = Frequenz des Oszillators (z.B. Quartz)<br />
<br />
Für solche 3-fache Schleifen (als UP) die Wartezeit beträgt min. 27 und max. ca. 50 000 000 Prozessortakten (ohne "nop"s). <br />
<br />
Die "nop"s vor allen Warteschleifen sind nur notwendig um genaue Wartezeit einzustellen zu können. Sie können auch mit "goto $+1"s ersetzt, oder weg gelassen werden. Sie können auch innerhalb jeder Schleife eingesetzt werden, um sie deutlich zu verlängern.<br />
<br />
Ein Beispiel:<br />
<br />
V<br />
n * nop<br />
P0 laden<br />
V<---------.<br />
n0 * nop <br />
P0 decrementieren |<br />
P0 = 0 ? N >--´<br />
J<br />
V<br />
<br />
Bei nur einem "nop" (n0=1) ist die max. Wartezeit ca. T ~ 4 * P0 * ( 4 / Fosc ), bei zwei (n0=2) ca. T ~ 5 * P0 * ( 4 / Fosc ) usw. <br />
<br />
Anstatt "movlw 0xXX" kann auch "movf PauseX,0" angewendet werden, wenn die Schleife mehrmals mit verschiedenen Werten P0, P1 und P2 aus den Register Pause0, Pause1 und Pause2 benutzt wird.<br />
<br />
Für sehr lange Wartezeiten können, nach gleichem Prinzip, Warteschleifen erstellt werden, die mehr als 3 Schleifen enthalten.<br />
<br />
In zeitkritischen ASM Programmen werden anstatt Warteschleifen (z.B. für Tastenenentprellung) zusammengesetzte UPs mit benötigter Ausführungszeit (z.B. Frequenzmessung + Displayausgabe + ...) angewendet.<br />
<br />
=== Tabellen ===<br />
<br />
Es gibt zwei Arten von Tabellen: Sprungtabellen (computed goto) die "goto" Befehle enthalten und Wertetabellen (lookup table) in denen feste Werte in "retlw" gespeichert sind. Der wichtigste Unterschied zwischen denen ist, dass die Sprungtabellen steuern den Programmlauf abhängig vom Inhalt des W-Registers und die Wertetabellen liefern abhängig von Inhalt des W-Registers ein Wert an den Aufrufer zurück. <br />
<br />
Beide werden in Programmspeicher erstellt. Sie können nur bis zu 256 Speicherstellen belegen, da in den W-Register auch nur so viel verschiedenen Zahlen "passen". Sie Fangen also (fast) immer bei einer Adresse XX00h an und enden bei XXFFh. Der Hochwertige Byte "XX" der Adresse an der sich der Anfang einer Tabelle befindet, muss vor dem Einsprung in die Tabelle ins PCLATH Register eingeschrieben werden, wenn die Tabelle weit vom Aufrufer liegt. In der Praxis werden solche Tabellen am oberen Ende des Programmspeichers angelegt, damit sie den ASM Code nicht unterbrechen.<br />
<br />
Eine Sprungtabelle wird so aufgebaut:<br />
<br />
org (XX-1)FF <--- eine Direktive für Assemblerprogramm, wo es <br />
die Tabelle im Programmspeicher plazieren soll<br />
Adresse Inhalt<br />
------------------------- <br />
Tab1 (XX-1)FF addwf PCL,1<br />
XX00 goto Marke0<br />
XX01 goto Marke1<br />
.......................<br />
XXFE goto Marke254<br />
XXFF goto Marke255<br />
<br />
Und so aufgerufen:<br />
<br />
movlw 0xXX<br />
movwf PCLATH<br />
movf TWert,0<br />
call Tab1<br />
<br />
Wobei:<br />
<br />
0xXX = Hochwertiger Byte der Adresse von Tab1, TWert = ein Wert, der die Wahl wohin gesprungen wird bestimmt.<br />
<br />
Nach ausführen der obiger Befehlsfolge, wird das ASM Programm z.B. für Twert=0x01 weiter ab Marke1 "laufen" bis es an "return" kommt. Dann springt es zurück zum Aufrufer der Tabelle.<br />
<br />
Eine Sprungtabelle kann auch mit "goto" eingesprungen werden, dann wird aber beim Erreichen des "return"s bis zum letzten "call" zurückgesprungen. Siehe hierzu auch [[#PIC Trainer|PIC Trainer]].<br />
<br />
<br />
Eine Wertetabelle wird so aufgebaut:<br />
<br />
org (XX-1)FF <--- eine Direktive für Assemblerprogramm, wo es <br />
die Tabelle im Programmspeicher plazieren soll<br />
Adresse Inhalt<br />
------------------------- <br />
Tab1 (XX-1)FF addwf PCL,1<br />
XX00 retlw Wert0<br />
XX01 retlw Wert1<br />
.......................<br />
XXFE retlw Wert254<br />
XXFF retlw Wert255<br />
<br />
Und so aufgerufen:<br />
<br />
movlw 0xXX<br />
movwf PCLATH<br />
movf TWert,0<br />
call Tab1<br />
<br />
wobei:<br />
<br />
0xXX = Hochwertiger Byte der Adresse von Tab1, TWert = ein Wert im W-Register, für welchen, an den Aufrufer bestimmter Wert aus der Tabelle im W-Register zurückgeliefert wird.<br />
<br />
Wertetabelle kann auch mit Hilfe der MPASM Direktive "dt" erstellt werden. So kann man sich mehrfaches Schreiben von "retlw" Befehlen ersparen:<br />
<br />
org 0x(XX-1)FF<br />
Tabelle addwf PCL,1 <br />
; nachfolgend der benötigte Wert im W- Register<br />
; vor dem Aufruf der Tabelle<br />
dt 3, 0xE8 ; 0x00<br />
dt 5, 0xB7 ; 0x02<br />
dt 'M','H','z',0 ; 0x04<br />
dt ' ',' ','W','A','I','T',0 ; 0x08<br />
dt 'C','o','n','s','t',' ','X',0 ; 0x0F<br />
dt 'C','=',0 ; 0x17<br />
dt 'L','=',0 ; 0x1A<br />
dt 'F','=',0 ; 0x1D<br />
dt 'O','K',0 ; 0x20<br />
usw.<br />
<br />
Das Lesen eines Strings muss ab entsprechendem Wert im W-Register (z.B. für "MHz" -> 0x04) anfangen und auf dem Wert 0 (Null) enden, der hier als Stringende dient. Einfacher ist, wenn alle Strings gleich lang sind (wie z.B. in einem Zeichengenerator für Grafikdisplay).<br />
<br />
Bei Tabellen, die länger als 256 Byte sind, muss immer PCLATH an den Seitengrenzen korriegiert werden, wie auf der 3. Seite der Applikation Note AN556 vom Microchip vorgestellt: http://ww1.microchip.com/downloads/en/AppNotes/00556e.pdf .<br />
<br />
=== Interrupt ===<br />
<br />
==== Prinzip ====<br />
<br />
Ein Interrupt (Unterbrechung) unterbricht ein laufendes Programm, wenn ein bestimmtes Ereigniss, auf das reagiert werden soll, statt gefunden hat. Die Reaktion wird in der sogenannten Interrupt Service Routine (kurz: ISR) definiert.<br />
<br />
Einfach gesagt, ein Interrupt stoppt ein laufendes Programm nach dem Ausführen der aktuellen Zeile, ruft die ISR auf und nach deren Ausführung startet das Programm wieder, ab der Zeile, vor der es durch Interrupt angehalten wurde. <br />
<br />
.--->V<br />
| Tun<br />
| V<br />
Interrupt ------>Tun-------->V<br />
| V ISR<br />
| Tun<--------´<br />
| V<br />
| Tun<br />
| V <br />
`----´<br />
<br />
Damit das unterbrochene Programm nach dem Rückkehr aus der ISR weiter fehlerfrei ausgeführt werden kann, darf die ISR keine Änderungen in vom Programm benutzten Register verursachen.<br />
Deswegen wenn UPs aus dem Programm durch ISR benutzt werden, müssen alle Register, dessen Inhalt durch ISR geändert wird, vor dem Ausführen der ISR (als ersten Befehle der ISR nach dem "bcf INTCON,GIE") im RAM gespeichert und nach der Ausführung der ISR (vorm "retfie") wiederhergestellt werden.<br />
<br />
Um einen Interrupt auslösen zu können, muss er vorher, durch beschreiben nötigen Register (INTCON, PIR, usw.) vorbereitet werden. Siehe hierzu: [[#INTCON|INTCON]] und [[#Interrupts|Interrupts]]<br />
<br />
==== Quellen ====<br />
<br />
Es gibt folgende Interrupt-Quellen:<br />
<br />
- interne Hardware (z.B. ein Timer)<br />
<br />
- externe Hardware (z.B. eine Taste)<br />
<br />
Die PICs der Mid-Range Familie haben im Programmspeicher nur eine Adresse (s.g. Interrupt Vektor 0x0004), wohin im Falle eines Interrupts, gesprungen wird. Um den Verursacher des Interrupts zu finden, hat jede Quelle ein eigenes Interrupt Flag (IF), das gesetzt wird, wenn der Interrupt ausgelöst wird. Somit kann in der ISR nach der Prüfung der IFs auf jeden Interrupt gezielt reagiert werden. Siehe hierzu: [[#Interrupts|Interrupts]].<br />
<br />
==== Interrupt Service Routine ====<br />
<br />
Die Interrupt Service Routine ist ein besonderes UP das für Basic-Line und Mid-Range immer an der Adresse 0x0004 beginnt und immer mit dem Befehl "retfie" endet. Ihr Umfang ist von der Menge der Quellen und der Reaktionen auf ihre Interrupts abhängig. Folgender PAD zeigt ein allgemeiner Aufbau der ISR:<br />
<br />
org 0x0004<br />
Interrupts sperren ; bcf INTCON,GIE<br />
Beeinflüsste Register sichern<br />
Interrupt-Quelle ermitteln<br />
Interrupt-Flag löschen<br />
und entsprechend reagieren<br />
Beeinflüsste Register restaurieren<br />
zurück ins Programm springen ; retfie<br />
<br />
Je nach Bedürfnissen, wird eine ISR nur nötige Elemente davon enthalten. Um auf alle Interrupts reagieren zu können muss die ISR vor dem nächsten Interrupt beendet werden. Da das Programm nur in den Pausen zwischen Ausführungen der ISR laufen kann, sollte die Ausführungszeit der ISR möglichst kurz sein. <br />
<br />
Die kürzeste ISR setzt nur ein Flag ("_Fint"), das dem Programm zeigt, dass ein Interrupt statt gefunden hat. Sie eignet sich für den Fall, wenn nur eine Interrupt-Quelle erlaubt ist. Das Programm wird für nur 5 Prozessortakten unterbrochen.<br />
<br />
org 0x0004<br />
bcf INTCON,GIE ; Interrupts sperren<br />
bcf IF ; Interrupt Flag löschen<br />
bsf _Fint ; Flag setzen<br />
retfie ; zurück ins Programm<br />
<br />
Das Programm prüft das Flag "_Fint", löscht es und reagiert entsprechend. Somit kann ein Interrupt im Hauptprogramm bearbeitet werden. <br />
<br />
Die ISR kann aber auch ein HP darstellen. Siehe hierzu: [[#Interrupts|Interrupts]]<br />
<br />
=== Schnittstellen und Treiber ===<br />
<br />
Als Schnittstelle wird externe Hadware, die zum Steuern eines an sie angeschlossenes "Gerätes" (z.B. eines Displays) dient, genannt. Das ASM Programmteil, das die Steuerung ermöglicht ist ein Treiber. Als Beispiele siehe: [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=13685]] und [http://www.roboternetz.de/phpBB2/viewtopic.php?t=22749]<br />
<br />
== Vorlage für MPASM ==<br />
<br />
Diese Vorlage ist nur für ASM Programme ohne Interrupts geeignet:<br />
<br />
list P=12F629 ; Prozessor definieren<br />
include "P12F629.inc" ; entsprechende .inc Datei für MPASM<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _MCLRE_OFF & _INTRC_OSC_NOCLKOUT ; Konfiguration<br />
#define _DTT1 GPIO,0 ; Portpins benennen<br />
#define _CKT2 GPIO,1<br />
#define _T3 GPIO,2<br />
#define _RNG GPIO,3<br />
#define _INT GPIO,4<br />
#define _RL GPIO,5<br />
usw.<br />
SecondL equ 0x20 ; Variablen definieren (Register benennen)<br />
SecondH equ 0x21<br />
MinuteL equ 0x22<br />
MinuteH equ 0x23<br />
StundeL equ 0x24<br />
StundeH equ 0x25<br />
usw.<br />
org 0x0000 ; bis da sind MPASM Direktiven<br />
; hier fängt das gesamte ASM Programm an<br />
call Init ; rufe UP "Init" (Initialisierung) auf<br />
Haupt ............ ; hier fängt das Hauptprogramm als endlose Schleife an<br />
Eigener Code<br />
............<br />
goto Haupt ; hier endet das HP, gehe zum Anfang des HPs (zurück)<br />
UP1 ............ ; Unterprogramme<br />
Eigener Code<br />
............<br />
return<br />
############<br />
UPn ............<br />
Eigener Code<br />
............<br />
return<br />
Init clrf GPIO ; lösche Port<br />
bsf STATUS,RP0 ; auf Bank1 umschalten<br />
call 0x3FF ; hole Kalibrationswert<br />
movwf OSCCAL ; kalibriere internen RC oscillator<br />
bcf OPTION_REG,7 ; aktiviere pull-ups<br />
movlw 0x38 ; definiere Portpins GPIO, (z.B. 0-2 Aus- und 3-5 Eingänge)<br />
movwf TRISIO ; schreibe in TRIS Register<br />
bcf STATUS,RP0 ; auf Bank0 umschalten<br />
movlw 7 ; schalte Komparator aus<br />
movwf CMCON ; und definiere GPIO 0-2 als digitale I/Os<br />
............<br />
eigener Code<br />
............<br />
return ; springe zurück (zum Haupt)<br />
; hier endet das gesamte ASM Programm <br />
end ; MPASM Direktive<br />
<br />
Die Variablen können auch kürzer mit s.g. cblock definiert werden:<br />
<br />
cblock 0x20 <br />
SecondL<br />
SecondH<br />
MinuteL<br />
MinuteH<br />
StundeL<br />
StundeH<br />
endc<br />
<br />
Bei sehr vielen Variablen sind aber die Registeradressen nicht so übersichtlich.<br />
<br />
Für Programme mit Interrupts ist diese Vorlage geeignet:<br />
<br />
list P=12F629 ; Prozessor definieren<br />
include "P12F629.inc" ; entsprechende .inc Datei für MPASM<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _MCLRE_OFF & _INTRC_OSC_NOCLKOUT ; Konfiguration<br />
#define _DTT1 GPIO,0 ; Portpins benennen<br />
#define _CKT2 GPIO,1<br />
#define _T3 GPIO,2<br />
#define _RNG GPIO,3<br />
#define _INT GPIO,4<br />
#define _RL GPIO,5<br />
usw. <br />
SecondL equ 0x20 ; Variablen definieren (Register benennen)<br />
SecondH equ 0x21<br />
MinuteL equ 0x22<br />
MinuteH equ 0x23<br />
StundeL equ 0x24<br />
StundeH equ 0x25<br />
usw.<br />
org 0x0000 ; bis da sind MPASM Direktiven<br />
; hier fängt das gesamte ASM Programm an<br />
call Init ; rufe UP "Init" (Initialisierung) auf<br />
goto Haupt ; gehe zum Hauptprogramm "Haupt" <br />
org 0x004 ; hier fängt die ISR (interrupt service routine) an<br />
bcf INTCON,GIE ; interrupts sperren<br />
.............<br />
Eigener Code<br />
.............<br />
retfie ; hier endet die ISR, interrupts wieder erlauben<br />
Haupt ............ ; hier fängt das Hauptprogramm als endlose Schleife an<br />
Eigener Code<br />
............<br />
goto Haupt ; hier endet das HP, gehe zum Anfang des HPs (zurück)<br />
UP1 ............ ; Unterprogramme<br />
Eigener Code<br />
............<br />
return<br />
############<br />
UPn ............<br />
Eigener Code<br />
............<br />
return<br />
Init clrf GPIO ; lösche Port<br />
bsf STATUS,RP0 ; auf Bank1 umschalten<br />
call 0x3FF ; hole Kalibrationswert<br />
movwf OSCCAL ; kalibriere internen RC oscillator<br />
bcf OPTION_REG,7 ; aktiviere pull-ups<br />
movlw 0x38 ; definiere Portpins GPIO, (z.B. 0-2 Aus- und 3-5 Eingänge)<br />
movwf TRISIO ; schreibe in TRIS Register<br />
bcf STATUS,RP0 ; auf Bank0 umschalten<br />
movlw 7 ; schalte Komparator aus<br />
movwf CMCON ; und definiere GPIO 0-2 als digitale I/Os<br />
............<br />
eigener Code<br />
............<br />
return ; springe zurück (zum Haupt)<br />
; hier endet das gesamte ASM Programm <br />
end ; MPASM Direktive<br />
<br />
== Das erste Programm ==<br />
<br />
Hier wird das ganze Prozess der Erstellung eines ASM Programms detailiert beschrieben. Die Idee:<br />
<br />
Es gibt 4 LEDs (_L1, _L2, _L3 und _L4), die mit 2 Tastern gesteuert werden sollen. Nach dem Einschalten soll keine LED leuchten. Solange der linke Taster (_T1) gedrückt ist, sollte eine leuchtende LED von links nach rechts "wandern" und von der letzten rechten Position wieder nach ganz linke "springen". Solange der rechte Taster (_T2) gedrückt ist, sollte eine leuchtende LED von rechts nach links "wandern" und von der letzten linken Position wieder nach ganz rechte "springen". Solange beide Taster gedrückt sind soll die leuchtende LED von links nach rechts und zurück "wandern".<br />
<br />
Dafür nötige Hardware zeigt folgende Skizze:<br />
<br />
.-----------------------------------------------.<br />
| |<br />
| PIC12F629 |<br />
| |<br />
| GPIO,3 GPIO,4 GPIO,5 GPIO,2 GPIO,1 GPIO,0|<br />
'-----------------------------------------------'<br />
4| 3| 2| 5| 6| 7|<br />
| | .-. .-. .-. .-.<br />
| | R | | R | | R | | R | |<br />
| | 470| | 470| | 470| | 470| |<br />
| | '-' '-' '-' '-'<br />
\ o \ o | | | |<br />
\ \ V -> V -> V -> V -><br />
\. \. - - - -<br />
_T1 o _T2 o _L1 | _L2 | _L3 | _L4 |<br />
| | | | | |<br />
+-------+-------+---+---+-------+-------+<br />
|<br />
===<br />
GND<br />
<br />
Weil der Pin 4 (GPIO,3) nur einen "open drain" Ausgang hat (kann nur an GND schalten), wird er hier als Eingang benutzt.<br />
<br />
Jetzt muss die Idee vom Programmierer in ein PAD verfasst werden, z.B. solcher:<br />
<br />
Start<br />
Initialisierung<br />
.-------------->V<br />
| _T1=0 gedrückt ? N >----.<br />
| J |<br />
| V |<br />
| links->rechts "wandern" |<br />
| V<------------´<br />
| _T2=0 gedrückt ? N >----.<br />
| J |<br />
| V |<br />
| rechts->links "wandern" |<br />
| V<------------´<br />
`---------------´<br />
<br />
danach detailierter:<br />
<br />
Start<br />
Initialisierung<br />
V<-------------------------------------------.<br />
_T1=0 gedrückt ? N >---+--->_T2=0 gedrückt ? N >---+---´<br />
J A J A<br />
V | V |<br />
_L1 an | _L4 an |<br />
Warten | Warten |<br />
_L1 aus | _L4 aus |<br />
_L2 an | _L3 an |<br />
Warten | Warten |<br />
_L2 aus | _L3 aus |<br />
_L3 an | _L2 an |<br />
Warten | Warten |<br />
_L3 aus | _L2 aus |<br />
_L4 an | _L1 an |<br />
Warten | Warten |<br />
_L4 aus | _L1 aus |<br />
V | V |<br />
`------------´ `------------´<br />
<br />
Weil das Assemblerprogram (z.B. MPASM) und der Prozessor (CPU) die Befehle nacheinander liest, ist jeder Quellcode einspaltig. Um sich die "Übersetzung" des PADs in den Quellcode zu erleichtern wird er noch z.B. so umgestaltet:<br />
<br />
Start<br />
Initialisierung<br />
V<-----------------+<----.<br />
Haupt _T1=0 gedrückt ? N >---. A | <br />
J | | |<br />
V | | |<br />
_L1 an | | |<br />
Warten | | |<br />
_L1 aus | | |<br />
_L2 an | | |<br />
Warten | | |<br />
_L2 aus | | |<br />
_L3 an | | |<br />
Warten | | |<br />
_L3 aus | | |<br />
_L4 an | | |<br />
Warten | | |<br />
_L4 aus | | |<br />
V<-----------´ | |<br />
T2Test _T2=0 gedrückt ? N >---------´ |<br />
J |<br />
V |<br />
_L4 an |<br />
Warten |<br />
_L4 aus |<br />
_L3 an |<br />
Warten |<br />
_L3 aus |<br />
_L2 an |<br />
Warten |<br />
_L2 aus |<br />
_L1 an |<br />
Warten |<br />
_L1 aus |<br />
V |<br />
`------------------------´<br />
<br />
Alle Pfeilen aus diesem PAD werden als "goto" in den Quellcode eingetragen.<br />
<br />
Zuerst wird im MPASM Verzeichnis ein neuer Verzeichniss (z,B. "PIC Programme") angelegt und in dem Verzeichniss neue Textdatei (z.B. "Erstes.txt") erstellt. Als nächstes wird die "Vorlage für MPASM" dorthin kopiert.<br />
<br />
Danach müssen, die im Programm benutzte Portpins und Register mit "#define" und "equ" definiert werden.<br />
<br />
Als Initialisierung wird das UP "Init" aus der Vorlage für MPASM mit geändertem "TRISIO" Wert (0x18) übernommen. Siehe: [[#Vorlage für MPASM|Vorlage für MPASM]]<br />
<br />
Fürs "Warten" wird eine dreifache Warteschleife verwendet. Siehe: [[#Pause|Pause]]<br />
<br />
Die Taster werden mit "btfsc" Befehl geprüft und zum LEDs An- und Ausschalten werden Befehle "bsf" und "bcf" für ensprechenden Portpin von GPIO angewendet. Siehe: [[#Einlesen|Einlesen]] und [[#Ausgeben|Ausgeben]] <br />
<br />
Anschlessend wird alles laut PAD in die Vorlage für MPASM eingeschrieben und der Quellcode ist fertig:<br />
<br />
; Erstes.asm<br />
list P=12F629 ; Prozessor definieren<br />
include "P12F629.inc" ; entsprechende .inc Datei für MPASM<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _MCLRE_OFF & _INTRC_OSC_NOCLKOUT ; Konfiguration <br />
#define _T1 GPIO,3 ; Portpins benennen<br />
#define _T2 GPIO,4<br />
#define _L1 GPIO,5<br />
#define _L2 GPIO,2<br />
#define _L3 GPIO,1<br />
#define _L4 GPIO,0<br />
P0 equ 0x20 ; Variablen definieren (Register benennen)<br />
P1 equ 0x21<br />
P2 equ 0x22<br />
org 0x0000 ; bis da sind MPASM Direktiven<br />
; hier fängt das gesamte ASM Programm an<br />
call Init ; rufe UP "Init" (Initialisierung) auf<br />
Haupt btfsc _T1 ; Taster _T1=0 gedrückt ?, wenn ja, überspringe "goto T2Test"<br />
goto T2Test ; wenn nicht, springe zu "T2Test"<br />
bsf _L1 ; schalte _L1 an (setze den Pin 2 auf "1")<br />
call Warten ; warte ca. 0,4 s (ca. 400 000 Prozessortakten)<br />
bcf _L1 ; schalte _L1 aus (setze den Pin 2 auf "0")<br />
bsf _L2 ; schalte _L2 an (setze den Pin 5 auf "1")<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus (setze den Pin 5 auf "0")<br />
bsf _L3 ; schalte _L3 an (setze den Pin 6 auf "1")<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus (setze den Pin 6 auf "0")<br />
bsf _L4 ; schalte _L4 an (setze den Pin 7 auf "1")<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus (setze den Pin 7 auf "0")<br />
T2Test btfsc _T2 ; Taster _T2=0 gedrückt ?, wenn ja, überspringe "goto Haupt"<br />
goto Haupt ; wenn nicht, springe zu "Haupt"<br />
bsf _L4 ; schalte _L4 an (setze den Pin 7 auf "1")<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus (setze den Pin 7 auf "0")<br />
bsf _L3 ; schalte _L3 an (setze den Pin 6 auf "1")<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus (setze den Pin 6 auf "0")<br />
bsf _L2 ; schalte _L2 an (setze den Pin 5 auf "1")<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus (setze den Pin 5 auf "0")<br />
bsf _L1 ; schalte _L1 an (setze den Pin 2 auf "1")<br />
call Warten ; warte<br />
bcf _L1 ; schalte _L1 aus (setze den Pin 2 auf "0")<br />
goto Haupt ; springe zu "Haupt"<br />
Warten movlw 2 ; schreibe "2" für ca. 0,4 s<br />
movwf P2 ; ins Register P2<br />
clrf P1 ; lösche Register P1 ("0" für 256 Durchläufe) <br />
clrf P0 ; lösche Register P0 ("0" für 256 Durchläufe)<br />
decfsz P0,1 ; dekrementiere P0 und überspringe "goto $-1" bei P0 = 0<br />
goto $-1 ; sonst gehe zur voherigen Adresse (Befehl "decfsz P0") <br />
decfsz P1,1 ; dekrementiere P1 und überspringe "goto $-4" bei P1 = 0 <br />
goto $-4 ; sonst gehe zur aktueller Adresse - 4 (Befehl "clrf P0")<br />
decfsz P2,1 ; dekrementiere P2 und überspringe "goto $-7" bei P2 = 0<br />
goto $-7 ; sonst gehe zur aktueller Adresse - 7 (Befehl "clrf P1")<br />
return ; springe zurück zum Aufrufer ("Haupt"),<br />
Init clrf GPIO ; lösche Port (setze alle werdende Ausgänge auf "0")<br />
bsf STATUS,RP0 ; auf Bank1 umschalten<br />
call 0x3FF ; hole Kalibrationswert (als "retlw" unter Adresse 0x3FF)<br />
movwf OSCCAL ; und kalibriere internen RC oscillator (4 MHz)<br />
bcf OPTION_REG,7 ; aktiviere pull-ups für die Portpins<br />
movlw 0x18 ; definiere Portpins GPIO,3 und 4 als Eingänge<br />
; und restlichen als Ausgänge (00011000b)<br />
movwf TRISIO ; schreibe in TRIS Register<br />
bcf STATUS,RP0 ; auf Bank0 umschalten<br />
movlw 7 ; schalte Komparator aus<br />
movwf CMCON ; und definiere GPIO 0-2 als digitale I/Os<br />
return ; springe zurück (zum Haupt) <br />
; hier endet das gesamte ASM Programm <br />
end ; MPASM Direktive<br />
<br />
Jetzt wird der Quellcode "Erstes.txt" in "Erstes.asm" umbenannt. Diese "*.asm" Datei können wir dem MPASM zum "Übersetzten" geben. Nach der Assemblierung sehen wir 3 Meldungen (Messages) vom MPASM. Diese befinden sich in der vom MPASM erstellter Datei "Erstes.lst" und lauten:<br />
<br />
Message[302]: Register in operand not in bank 0. Ensure that bank bits are correct.<br />
<br />
Diese Meldung kann man übersetzen als:<br />
<br />
Meldung[302]: Register im Befehl nicht in Bank 0. Vergewisse Dich, dass die Bank-Bits korrekt sind.<br />
<br />
Wir sind sicher, dass die Register "OSCCAL", "OPTION_REG" und "TRISIO" in der Bank 1 sich befinden. Wenn man aber nicht sicher ist, soll man im Datenblatt nachschauen. Die Meldungen kommen immer für alle SFRs (Special Function Register) die sich nicht in der Bank 0 befinden und können ignoriert werden.<br />
<br />
Am Ende der Datei "Erstes.lst" befinden sich noch einige Informationen über Programmspeicher:<br />
<br />
Program Memory Words Used: 52 ; benutzte Speicherstellen<br />
Program Memory Words Free: 972 ; freie Speicherstellen<br />
<br />
und die Anzahl den eventuellen Fehlern (Errors), Warnungen (Warnings) und Meldungen (Messages):<br />
<br />
Errors : 0<br />
Warnings : 0 reported, 0 suppressed<br />
Messages : 3 reported, 0 suppressed<br />
<br />
Insgesamt werden durch MPASM 4 Dateien erstellt: "*.cod", "*.err", "*.hex" und "*.lst".<br />
<br />
Wichtigste davon, falls wegen Fehler keine "*.hex" Datei erstellt wird, ist die "*.err" Datei, wo alles was dem MPASM nicht "passt" aufgelistet ist. In der "*.lst" Datei kann man sich ein Programm mit genauen Adressen für jeden Befehl anschauen.<br />
<br />
Bei Erstellung von ASM Programmen, um sich das Kompilieren zu vereinfachen, kann folgende Prozedur verwendet werden:<br />
<br />
Zuerst wird in gleichem Verzeichnis, wo sich die Sammlung Unter/Programme befindet, eine Textdatei (z.B. "Test.txt") erstellt, wo ein einspaltiges PAD mit Pfeilen nach oben und unten geschrieben/skizziert wird. In der Datei wird auch ganz oben ständig aktualisierte Liste aller Register erstellt, die in diesem Unter/Programm verwendet sind.<br />
<br />
Wenn das PAD fertig ist, wird diese Datei mit gleichem Namen als "*.asm" Datei (z.B. "Test.asm")gespeichert und alle PAD "Symbole" mit Befehlen für bestimmten PIC/Assemblerprogramm ersetzt (z.B. für MPASM werden alle Register benannt).<br />
<br />
Bei jeder Änderung wird sie gleich in beiden Dateien gemacht, damit sie am Ende wirklich aktuell sind. Letztendlich haben wir dann beide, wenn wir später etwas ändern müssen. Dank dessen braucht man ausser eventuellen Namen der UPs keine Kommentare im Programm schreiben, was seine Erstellung deutlich beschleinigt.<br />
<br />
Die während der Assemblierung vom MPASM generierte Datei "Erstes.hex" kann jetzt durch ein Brenner mit geeignetem Programm in den PIC Programmspeicher eingeschrieben werden.<br />
<br />
== Für anderen PIC umschreiben ==<br />
<br />
Die wichtigste Vorraussetzung ist, das der andere PIC2, auf dem das vorhandene ASM Programm für PIC1 laufen soll, zumindest für das ASM Programm nötige interne Hardware hat. Die Frequenz des Oszillators sollte gleich sein. Der Code benötigt ausser UP "Init" keine Änderungen.<br />
<br />
Wenn der benutzte Port vom PIC2 anderen Namen hat, muss man das im Quellcode umdefinieren, z.B.:<br />
<br />
#define PORTC PORTB<br />
#define TRISC TRISB<br />
<br />
Dann wird das Assemblerprogramm, wenn es PORTC findet, immer PORTB nehmen. Das gleiche Betrifft die "__config" Ausdrücke, die entsprechend der "*.ini" Datei für den PIC2, geändert werden müssen. <br />
<br />
Das Assemblerprogramm findet sicher alles, was ihm nicht "passt" und bringt Fehlermeldungen, auf die man entsprechend reagieren muss.<br />
<br />
Als Beispiel, schreiben wir "Erstes.asm" für den PIC16F84A um.<br />
<br />
Zum Ändern sind die ersten 3 Zeilen:<br />
<br />
list P=12F629 ; Prozessor definieren<br />
include "P12F629.inc" ; entsprechende *.inc Datei für MPASM<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _MCLRE_OFF & _INTRC_OSC_NOCLKOUT ; Konfiguration<br />
<br />
Sie werden für den PIC16F84A so aussehen:<br />
<br />
list P=16F84A ; Prozessor definieren<br />
include "P16F84A.inc" ; entsprechende *.inc Datei für MPASM <br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC ; Konfiguration<br />
<br />
Dazu muss noch "GPIO" Port umdefiniert werden, da der PIC16F84A solchen nicht hat, z.B. wegen internen pull-ups auf "B":<br />
<br />
#define GPIO PORTB<br />
#define TRISIO TRISB<br />
<br />
Die Hardware muss natürlich an entsprechende Pins angeschlossen werden:<br />
<br />
_T1 auf 12 (B3), _T2 auf 13 (B4), _L1 auf 14 (B5), _L2 auf 11 (B2), _L3 auf 10 (B1) und _L4 auf 9 (B0) <br />
<br />
Jetzt assemblieren wir die Qelldatei und der MPASM bringt in der Datei "Erstes.err" folgende Fehler:<br />
<br />
Error[113] C:\MPASM\PICPROG\PIC16F84\ERSTES.ASM 61 : Symbol not previously defined (OSCCAL)<br />
Error[113] C:\MPASM\PICPROG\PIC16F84\ERSTES.ASM 67 : Symbol not previously defined (CMCON)<br />
<br />
In dem UP "Init" müssen also noch die Befehle mit "OSCCAL" und "CMCON" Register entfernt werden, da der PIC16F84A sie nicht hat:<br />
<br />
call 0x3FF ; hole Kalibrationswert<br />
movwf OSCCAL ; kalibriere internen RC oscillator (4 MHz)<br />
<br />
und:<br />
<br />
movlw 7 ; schalte Komparator aus<br />
movwf CMCON ; und mache GPIO 0-2 als digital I/O<br />
<br />
Weiter kann schon alles übernommen werden und die Datei "Erstes.asm" für den PIC16F84A ist fertig:<br />
<br />
; Erstes.asm <br />
list P=16F84A ; Prozessor definieren<br />
include "P16F84A.inc" ; 4.000 MHz<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC<br />
#define GPIO PORTB ; Ports umbenennen<br />
#define TRISIO TRISB<br />
#define _T1 GPIO,3 ; Portpins benennen<br />
#define _T2 GPIO,4<br />
#define _L1 GPIO,5<br />
#define _L2 GPIO,2<br />
#define _L3 GPIO,1<br />
#define _L4 GPIO,0<br />
P0 equ 0x20 ; Variablen definieren (Register benennen)<br />
P1 equ 0x21<br />
P2 equ 0x22<br />
org 0x0000 ; bis da sind MPASM Direktiven<br />
; hier fängt das gesamte ASM Programm an<br />
call Init ; rufe UP "Init" (Initialisierung) auf<br />
Haupt btfsc _T1 ; Taster _T1=0 gedrückt ?, wenn ja, überspringe "goto T2Test"<br />
goto T2Test ; wenn nicht, springe zu "T2Test"<br />
bsf _L1 ; schalte _L1 an<br />
call Warten ; warte ca. 0,4 s (ca. 400 000 Prozessortakten)<br />
bcf _L1 ; schalte _L1 aus <br />
bsf _L2 ; schalte _L2 an<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus<br />
bsf _L3 ; schalte _L3 an<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus<br />
bsf _L4 ; schalte _L4 an<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus<br />
T2Test btfsc _T2 ; Taster _T2=0 gedrückt ?, wenn ja, überspringe "goto Haupt"<br />
goto Haupt ; wenn nicht, springe zu "Haupt"<br />
bsf _L4 ; schalte _L4 an<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus<br />
bsf _L3 ; schalte _L3 an<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus<br />
bsf _L2 ; schalte _L2 an<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus<br />
bsf _L1 ; schalte _L1 an<br />
call Warten ; warte<br />
bcf _L1 ; schalte _L1 aus<br />
goto Haupt ; springe zu "Haupt"<br />
Warten movlw 2 ; schreibe "2" für ca. 0,4 s<br />
movwf P2 ; ins Register P2<br />
clrf P1 ; lösche Register P1 ("0" für 256 Durchläufe) <br />
clrf P0 ; lösche Register P0 ("0" für 256 Durchläufe)<br />
decfsz P0,1 ; dekrementiere P0 und überspringe "goto $-1" bei P0 = 0<br />
goto $-1 ; sonst gehe zur voherigen Adresse (Befehl "decfsz P0") <br />
decfsz P1,1 ; dekrementiere P1 und überspringe "goto $-4" bei P1 = 0 <br />
goto $-4 ; sonst gehe zur aktueller Adresse - 4 (Befehl "clrf P0")<br />
decfsz P2,1 ; dekrementiere P2 und überspringe "goto $-7" bei P2 = 0<br />
goto $-7 ; sonst gehe zur aktueller Adresse - 7 (Befehl "clrf P1")<br />
return ; springe zurück zum Aufrufer ("Haupt"),<br />
Init clrf GPIO ; lösche Port (setze alle werdende Ausgänge auf "0")<br />
bsf STATUS,RP0 ; auf Bank1 umschalten<br />
bcf OPTION_REG,7 ; aktiviere pull-ups<br />
movlw 0x18 ; definiere Portpins GPIO,3 und 4 als Eingänge<br />
; und die restlichen als Ausgänge (00011000b) <br />
movwf TRISIO ; schreibe in TRIS Register<br />
bcf STATUS,RP0 ; auf Bank0 umschalten<br />
return ; springe zurück (zum Haupt), nach der Zeile<br />
; aus der du aufgerufen wurdest <br />
; hier endet das gesamte ASM Programm <br />
end ; MPASM Direktive<br />
<br />
Man kann auch die Portpins neu definieren und das UP "Warten" für z.B. 20 MHz Quarz anpassen:<br />
<br />
; Erstes.asm<br />
list P=16F84A ; Prozessor definieren<br />
include "P16F84A.inc" ; 20.000 MHz<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC<br />
#define _T1 PORTB,5 ; Portpins benennen<br />
#define _T2 PORTB,4<br />
#define _L1 PORTB,3<br />
#define _L2 PORTB,2<br />
#define _L3 PORTB,1<br />
#define _L4 PORTB,0<br />
P0 equ 0x20 ; Variablen definieren (Register benennen)<br />
P1 equ 0x21<br />
P2 equ 0x22<br />
org 0x0000 ; bis da sind MPASM Direktiven<br />
; hier fängt das gesamte ASM Programm an<br />
call Init ; rufe UP Init (Initialisierung) auf<br />
Haupt btfsc _T1 ; Taster T1 gedrückt ist, wenn ja, überspringe "goto T2Test"<br />
goto T2Test ; wenn nicht, springe zu T2Test<br />
bsf _L1 ; schalte _L1 an<br />
call Warten ; warte ca. 0,4 s (2 000 000 Prozessortakten)<br />
bcf _L1 ; schalte _L1 aus<br />
bsf _L2 ; schalte _L2 an<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus<br />
bsf _L3 ; schalte _L3 an<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus<br />
bsf _L4 ; schalte _L4 an<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus<br />
T2Test btfsc _T2 ; Taster T2 gedrückt ist, wenn ja, überspringe "goto Haupt"<br />
goto Haupt ; Wenn nicht, springe zu Haupt<br />
bsf _L4 ; schalte _L4 an<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus<br />
bsf _L3 ; schalte _L3 an<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus<br />
bsf _L2 ; schalte _L2 an<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus<br />
bsf _L1 ; schalte _L1 an<br />
call Warten ; warte<br />
bcf _L1 ; schalte _L1 aus<br />
goto Haupt ; springe zu Haupt<br />
Warten movlw 0x0A ; schreibe "0x0A" für ca. 0,4 s<br />
movwf P2 ; ins Register P2<br />
clrf P1 ; lösche Register P1 (schreibe "0" für 256 Durchläufe) <br />
clrf P0 ; lösche Register P0 (schreibe "0" für 256 Durchläufe)<br />
decfsz P0,1 ; dekrementiere P0 und überspringe "goto $-1" bei P0 = 0<br />
goto $-1 ; sonst gehe zur voherigen Adresse (Befehl "decfsz P0") <br />
decfsz P1,1 ; dekrementiere P1 und überspringe "goto $-4" bei P1 = 0 <br />
goto $-4 ; sonst gehe zur aktueller Adresse - 4 (Befehl "clrf P0")<br />
decfsz P2,1 ; dekrementiere P2 und überspringe "goto $-7" bei P2 = 0<br />
goto $-7 ; sonst gehe zur aktueller Adresse - 7 (Befehl "clrf P1")<br />
return ; springe zurück zum Aufrufer ("Haupt"),<br />
Init clrf PORTB ; lösche Port (setze alle werdende Ausgänge auf "0")<br />
bsf STATUS,RP0 ; auf Bank1 umschalten<br />
bcf OPTION_REG,7 ; aktiviere pull-ups<br />
movlw 0x30 ; definiere PortB: Pins 5 und 4 als Eingänge<br />
; und die restlichen als Ausgänge (00011000b)<br />
movwf TRISB ; schreibe in TRIS Register<br />
bcf STATUS,RP0 ; auf Bank0 umschalten<br />
return ; springe zurück (zum Haupt), nach der Zeile<br />
; aus der du aufgerufen wurdest <br />
; hier endet das gesamte ASM Programm <br />
end ; MPASM Direktive<br />
<br />
In dem Fall müssen natürlich die Taster und LEDs an für sie definierte Portpins angeschlossen werden.<br />
<br />
== Einbinden ==<br />
<br />
Die einfachste Möglichkeit die gewünschten Programme in ein neues Programm einzubinden ist, alle Programme in das neue Programm zu kopieren. Dafür müssen die ersten 3 Zeilen, die den Prozessor und die Konfiguration des PICs festlegen aus den eigebundenen Programmen entfernt werden. Danach müssen alle "#define", "equ" und "org" Direktiven verglichen und wenn nötig geändert werden. Dabei hilft der MPASM mit seinen Fehlermeldungen in der Datei "*.err".<br />
<br />
Die zweite Möglichkeit ist, die gewünschten Programme als "*.asm" Dateien mit der Direktive "include *.asm" am Ende des neuen Programms vor der Direktive "end" einzubinden. Die einbindende sowie alle einzubindenden Dateien müssen sich in einem Verzeichniss befinden.<br />
<br />
include "1.asm" <br />
include "2.asm"<br />
.<br />
.<br />
.<br />
include "n.asm" <br />
end <br />
<br />
In dem Fall gelten die gleichen o.g. Regeln wie beim direkten Kopieren. Wenn es in den eingebundenen Programmen keine "org" Direktiven gibt, werden die weiteren Programme im Programmspeicher ab Ende des ersten Programms nacheinander plaziert.<br />
<br />
== Fehlersuche ==<br />
<br />
Als erstes wird an den PIC angeschlossene externe Hardware geprüft. Das sollte noch vor dem Einstecken oder Einlöten des PICs gemacht werden, da die gravierende Fehler (z.B. Kurzschlüsse, Umpolung von VCC und GND, usw.) für den PIC schädlich werden können. Für einige Teile der Hardware kann entsprechende Spannung (VCC oder GND) an bestimmten Pin gelegt werden und die richtige Reaktion der Hardware optisch (z.B. für LEDs) oder akustisch (z.B. für Relais) festgestellt werden. Sonst müssen die elektrischen Verbindungen mit einem Messgerät überprüft werden. Danach kann man den PIC einstecken bzw. einlöten.<br />
<br />
Die Fehlersuche in der Software fängt mit der ersten und endet mit der letzten Zeile des ASM Programms. Sie läuft die ganze Zeit parallel zum Programmschreiben. Jedes UP sollte vor dem Übertragen ins Programm geprüft und eventuelle Fehler beseitigt werden. Dazu arbeitet man gleichzeitig mit zwei "*.asm" Dateien, einer für die UPs und einer für das gesamte Programm. Es wird empfohlen, nach der "Initialisierung", als erstes UP, das für Ausgabegerät (z.B. Display) zu erstellen, um das funktionieren des Programms, vom Anfang an beobachten zu können.<br />
<br />
Für die Fehlersuche in "hängenden" Programmen ist der [[#PIC RAM Monitor|PIC RAM Monitor]] bzw. [[#PIC Trainer|PIC Trainer]] unersetzlich. Weil sie durch Interrupt das "hängende" Programm unterbrechen und auf dem "PIC Miniterminal" Registerinhalte während der Unterbrechung zeigen, können alle in der endlosen Schleife sich befindliche Register schnell festgestellt werden, da nur dessen Inhalte sich ständig ändern. <br />
<br />
Wenn aus geprüften Unterprogrammen erstelltes Programm nicht richtig funktioniert, müsste ein Fehler im PAD des Hauptprogramms sein.<br />
<br />
== Optimierung ==<br />
Work in Progress...<br />
Wer sonst noch Beispiele hat, bitte hier vervollständigen...<br />
BMS ...<br />
=== Speicherbedarf ===<br />
==== Programmspeicher ====<br />
Die ersten Programme für den PIC sind natürlich einfach und kurz. Doch nach und nach werden die Codes länger und unübersichtlicher, das Brennen dauert auch umso länger. Das Programm ist zu umfangreich. Doch wo soll man etwas kürzen?<br />
<br />
Es gibt unzählige Möglichkeiten - hier ein paar Beispiele:<br />
<br />
===== Unterprogramme =====<br />
<br />
Bestimmt wird man im Code immer die selben Abschnitte brauchen, zum Beispiel Warteschleifen oder ADC-Routinen, etc. Wieso sollte man diese also mehrfach (doppelt, dreifach, u.s.w.) schreiben?<br />
<br />
Die Lösung: Der Programmteil wird als Unterprogramm benutzt. Man erstellt eine Sprungmarke (ab hier beginnt das Unterprogramm) und endet wieder mit einem "return"- oder "retlw"-Befehl.<br />
Aufgerufen werden Unterprogramme meistens mit dem "call"-Befehl. Es "lohnt sich" erst ein Codefragment als UP zu definieren wenn es min. 2 mal aufgerufen wird. <br />
<br />
Ein "goto"-Befehl ist zu diesem Zweck nur geeignet, wenn der Prozessor zum letzten Aufruf von "call" zurück springen soll, da die Rücksprungadresse vom letzten "call" auf dem Stapel (stack) abgelegt wurde. Somit kann man mehr als 8 Ebenen von UPs nutzen.<br />
<br />
Den Unterprogrammen kann man natürlich auch Werte übergeben. Möchte man z.B. mehrere unterschiedliche Zeiten für Warteschleifen benutzen, so kann man die Werte vor dem Aufruf des UPs in die benötigte Anzahl von Register einschreben und dann das Unterprogramm mit "call" aufrufen. Das Unterprogramm verwendet dann die Werte aus dem Register. Siehe: [[#Pause|Pause]]<br />
<br />
Um gleiche Vorgänge in mehreren Registern durchzuführen (z.B. löschen) ist die Anwendung von Schleifen geeignet (mit bestimmter Anzahl der Abläufe und indirekter Adressierung). Siehe: [[#Variablen|Variablen]]<br />
<br />
===== bsf und bcf =====<br />
<br />
Bestimmt gibt es Situationen, in denen der PIC mehrere Bits an einem Port/Register setzen oder löschen muss. Z.B. wenn mehrere LEDs an einem Port ein- oder ausgeschaltet werden sollen.<br />
Werden mehr als 2 Bits verändert, so kann man hier die "bsf"- und "bcf"-Befehle umgehen und einen kürzeren Code erstellen.<br />
<br />
bsf PORTB,0 ;An PORTB sind 8 LEDs angeschlossen.<br />
bsf PORTB,1 ;Es sollen die ersten 4 LEDs angeschaltet werden,<br />
bsf PORTB,2 ;die anderen sollen ausgeschaltet werden.<br />
bsf PORTB,3 ;links steht es mit bcf/bsf<br />
bcf PORTB,4 ;ziemlich lang<br />
bcf PORTB,5<br />
bcf PORTB,6<br />
bcf PORTB,7<br />
<br />
movlw b'00001111' ;Das geht auch kürzer und übersichtlicher:<br />
movwf PORTB<br />
<br />
Man sieht - der Codeabschnitt umfasst nur noch 2 Zeilen anstatt 8. Somit braucht es weniger Speicher und wird von PIC schneller verarbeitet. Allerdings muss man aufpassen, da durch diese Routine der Inhalt des W-Registers und der Inhalt des <i>gesamten</i> Zielregisters verändert wird. Sollen die anderen Bits unangetastet bleiben, muss man es entweder bei den "bcf"/"bsf"-Befehlen belassen oder logische Funktionen (and bzw. or) durchführen.<br />
<br />
===== Bit kopieren =====<br />
<br />
;An den PIC ist ein Taster(RB0) und eine LED(RB1) angeschlossen.<br />
;Taster: High=gedrückt Low=nicht gedrückt<br />
;LED: High=Ein Low=Aus<br />
;Wenn der Taster gedrückt ist, soll die LED leuchten<br />
;wenn er nicht gedrückt ist, soll die LED nicht leuchten.<br />
;1.Vorschlag:<br />
btfss PORTB,0 ;ist der taster gedrückt?<br />
goto no ;nein<br />
bsf PORTB,1 ;ja - LED ein<br />
goto endif ;fertig abgefragt - unten weitermachen...<br />
no bcf PORTB,1 ;LED aus<br />
endif<br />
<br />
Der oben aufgeführte Code hat viele Nachteile: er ist relativ lang, braucht zwei Gotos und entspr. Sprungmarken. Ok, das ist vielleicht Erbsenzählerei, aber aus diesen 5 Zeilen kann man 3 machen.<br />
<br />
bcf PORTB,1 ;Das Bit wird erst gelöscht<br />
btfsc PORTB,0 ;Taster gedrückt?<br />
bsf PORTB,1 ;JA-LED an<br />
<br />
Man geht davon aus, dass der Taster nicht gedrückt ist. Somit wird die LED erst einmal ausgeschaltet. Ist der Taster aber doch gedrückt, dann wird die LED angeschaltet.<br />
<br />
Achtung möglicher Nebeneffekt: Wird dies in einer Schleife sehr schnell und oft ausgeführt, kann die LED flimmern oder nicht in ihrer vollen Helligkeit leuchten. Das passiert, weil ja angenommen wird, dass der Taster nicht gedrückt wird und die LED somit aus sein muss. Dann wird sie aber bei Tastendruck eingeschaltet. Somit entsteht ein schnelles ein-/ausschalten (PWM) und die LED leuchtet nicht mehr so hell.<br />
Meist ist das aber nicht tragisch oder wird nicht unbedingt in einer Schleife sehr schnell abgefragt. Siehe hierzu: [[#Einlesen|Einlesen]]<br />
<br />
==== RAM ====<br />
<br />
Um Anzahl benötigten Register zu sparen werden vorläufige Register (temporary) definiert, die von mehreren UPs benutzt werden. Als Beispiel, das Register "Tmp" in [[#Matrix Display|Matrix Display]] oder "ATmp" in [[#Hex Dec Wandlung|Hex Dec Wandlung]]. Die Benutzung muss aber sehr genau nach PAD zugeteilt werden. Bei gleichzeitiger Benutzung des gleichen Registers durch mehrere UPS werden falsche Daten verarbeitet und im schlimmsten Fall können endlose Schleifen entstehen, die in Folge haben, dass das Programm nicht weiter ausgeführt wird ("hängt").<br />
<br />
Viele im ASM Programm ungebrauchte SFRs können als "normale" Register (GPR) verwendet werden. Wenn zum Beispiel Timer1 nicht benutzt wird, können seine Register TMR1H und TMR1L für Speicherung von Daten benutzt werden. Es könnte aber gefährlich werden, wenn mehrere Programme gebindet sind. Deswegen muss es immer am Ende, beim volständigen Programm, geprüft werden, welche SFRs tatsächlich benutzt werden dürfen. <br />
<br />
Für Konstanten (z.B. pi, ln2, Meldungen, usw.), die sich im Laufe des Programms nicht ändern, kann EEPROM (mit MPASM Direktive "de") oder Programmspeicher (mit MPASM Direktive "dt") als Ablage benutzt werden. Siehe auch: [[#EEPROM|EEPROM]] und [[#Tabellen|Tabellen]]<br />
<br />
=== Ausführungszeit ===<br />
<br />
Alles was schnellmöglichst ablaufen soll, widersetzt sich der Optimierung des Programmspeichers.<br />
<br />
Es können keine Schleifen und keine indirekte Adressierung benutzt werden, alles muss direkt programmiert werden. <br />
<br />
Als Beispiel zur Verdeutlichung: Es sollen 16 Werte vom PORTA ins RAM ab Adresse 0x20 eingelesen werden.<br />
<br />
Als Schleife mit indirekter Adressierung:<br />
<br />
................<br />
movlw 0x10 ;Anzahl Durchläufe<br />
movwf Temp ;in den Schleifenzähler<br />
movlw 0x20 ;erste Adresse<br />
movwf FSR ;ins FSR Register<br />
Einlesen movf PORTA,0 ;PortA ins W-Register<br />
movwf INDF ;und ins aktuellen RAM Register<br />
incf FSR,1 ;nächste Adresse<br />
decfsz Temp,1 ;Temp = 0 ?<br />
goto Einlesen ;nein, zum Einlesen<br />
................ ;ja, weiter <br />
Gesamtdauer: 100 Takten<br />
Pause zwischen Einlesungen: 6 Takten<br />
Programmspeicher Bedarf: 9 Speicherstellen <br />
Direkt:<br />
<br />
...............<br />
movf PORTA,0<br />
movwf 0x20<br />
movf PORTA,0<br />
movwf 0x21<br />
movf PORTA,0<br />
movwf 0x22<br />
movf PORTA,0<br />
movwf 0x23<br />
movf PORTA,0<br />
movwf 0x24<br />
movf PORTA,0<br />
movwf 0x25<br />
movf PORTA,0<br />
movwf 0x26<br />
movf PORTA,0<br />
movwf 0x27<br />
movf PORTA,0<br />
movwf 0x28<br />
movf PORTA,0<br />
movwf 0x29<br />
movf PORTA,0<br />
movwf 0x2A<br />
movf PORTA,0<br />
movwf 0x2B<br />
movf PORTA,0<br />
movwf 0x2C<br />
movf PORTA,0<br />
movwf 0x2D<br />
movf PORTA,0<br />
movwf 0x2E<br />
movf PORTA,0<br />
movwf 0x2F<br />
...............<br />
Gesamtdauer: 32 Takten<br />
Pause zwischen Einlesungen: 2 Takten<br />
Programmspeicher Bedarf: 32 Speicherstellen<br />
<br />
Anstatt Warteschleifen (z.B. für Tastenentprellung) sollten Programmfragmente mit entsprechender Ausführungszeit benutzt werden (z.B. Ausgabe auf einem Display). Die nötige Zeit lässt sich aus mehreren UPs zusammensetzen.<br />
<br />
Es sollte kein Prozessortakt ungenutzt bleiben.<br />
<br />
Man kann auch eine Bearbeitung eines UPs um 4 Takte beschleunigen ("call" + "return"), indem man bei dem letzten Aufruf eines UPs (direkt vorm "return") anstatt "call UP", "goto UP" anwendet, da das UP sowieso mit "return" bzw. "retlw" endet.<br />
<br />
Ursprünglich:<br />
<br />
movlw 0x28<br />
call Cmd<br />
movlw 0x0C<br />
call Cmd<br />
movlw 6<br />
call Cmd <--<br />
return<br />
<br />
Optimiert:<br />
<br />
movlw 0x28<br />
call Cmd<br />
movlw 0x0C<br />
call Cmd<br />
movlw 6<br />
goto Cmd <--<br />
<br />
Nebenbei sparrt man sich eine Zeile im Quellcode und eine Spaicherstelle im Programmspeicher.<br />
<br />
= Basic-Line (PIC12...) & Mid-Range (PIC16...)=<br />
<br />
Zu Basic-Line gehören alle PIC12... mit 12-bit und zu Mid-Range alle PIC16... mit 14-bit langen Befehlen. Weil beide Familien gleichen Befehlsatz haben, werden sie hier gemeinsam behandelt. Sie haben insgesamt 35 Befehle.<br />
<br />
Alle Befehle aus der Tabelle, ausser mit "*" gekenzeichneten, gehören zum Befehlsatz den High-End PIC18... dazu.<br />
<br />
== Kurzübersicht Assembler Befehle ==<br />
<font style="font-size:10px;"><br />
{| <br />
|-<br />
| valign=top |<br />
<br />
{| {{Blauetabelle}}<br />
|ADDLW||Add literal and W <br />
|-<br />
|ADDWF||Add W and f <br />
|-<br />
|ANDLW||AND literal with W <br />
|-<br />
|ANDWF||AND W with f<br />
|-<br />
|BCF||Bit Clear f <br />
|-<br />
|BSF||Bit Set f <br />
|-<br />
|BTFSC||Bit Test f, Skip if Clear <br />
|-<br />
|BTFSS||Bit Test f, Skip if Set <br />
|-<br />
|CALL||Call subroutine <br />
|-<br />
|CLRF||Clear f<br />
|-<br />
|*CLRW||Clear W<br />
|-<br />
|CLRWDT||Clear Watchdog Timer <br />
|}<br />
<br />
| valign=top |<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
|COMF||Complement f<br />
|-<br />
|DECF||Decrement f<br />
|-<br />
|DECFSZ||Decrement f, Skip if 0<br />
|-<br />
|GOTO||Go to address or label<br />
|-<br />
|INCF||Increment f<br />
|-<br />
|INCFSZ||Increment f, Skip if 0<br />
|-<br />
|IORLW||Inclusive OR literal with W <br />
|-<br />
|IORWF||Inclusive OR W with f<br />
|-<br />
|MOVF||Move f<br />
|-<br />
|MOVLW||Move literal to W <br />
|-<br />
|MOVWF||Move W to f<br />
|-<br />
|NOP||No Operation<br />
|}<br />
<br />
| valign=top |<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
|RETFIE||Return from interrupt <br />
|-<br />
|RETLW||Return with literal in W <br />
|-<br />
|RETURN||Return from Subroutine <br />
|-<br />
|*RLF||Rotate Left f through Carry<br />
|-<br />
|*RRF||Rotate Right f through Carry<br />
|-<br />
|SLEEP||Go into standby mode <br />
|-<br />
|SUBLW||Subtract W from literal <br />
|-<br />
|SUBWF||Subtract W from f<br />
|-<br />
|SWAPF||Swap nibbles in f<br />
|-<br />
|XORLW||Exclusive OR literal with W <br />
|-<br />
|XORWF||Exclusive OR W with f<br />
|}<br />
[[:bild:pic_asm_short.jpg|Kurzübersicht zum Ausdrucken]]<br />
|}<br />
</font><br />
<br />
==Ausführliche Beschreibung zu den Befehlen==<br />
<br />
Fast alle Befehle werden in einem Takt = 4 Oszillatortakten ausgeführt.<br />
<br />
Aussnahmen:<br />
<br />
"goto", "call", "return" und "retfie" benötigen wegen Zugriff auf den Programmzähler (PC) immer 2 Takten.<br />
<br />
"btfsc", "btfss", "decfsz" und "incfsz" brauchen 1 Takt wenn der nächste Befehl ausgeführt wird bzw. 2 Takten wenn er überspringen wird, weil es immer anstatt des überspringenden Befehls ein "nop" ausgeführt wird.<br />
<br />
Eine ausgegliederte Beschreibung der Befehle findet sich unter [[PIC Assemblerbefehle]]<br />
<br />
Erklärungen zu den Verwendeten Platzhaltern:<br />
*'''k''' stellt einen fest definierten Wert da. z.B. hexadezimal <tt>0x20</tt> bzw. <tt>20</tt>, dezimal <tt>d'42'</tt> bzw. <tt>.42</tt> oder binär <tt>b'00101010'</tt><br />
*'''W''' steht für das W-Register.<br />
*'''d''' steht für ''destination'' (Ziel). Im code wird d durch ein <tt>w</tt> bzw. <tt>0</tt> (der Wert wird in das W-Register gespeichert ) oder <tt>f</tt> bzw. <tt>1</tt> (der Wert wird in das davor definierte Register gespeichert)<br />
*'''b''' steht für Bitnummer im Register (eine Zahl zwischen 0 und 7)<br />
*'''R''' steht für ein Register<br />
*'''fett''' geschrieben Bedeutet, dass es ein Platzhalter ist und im Quellcode durch eine Registeradresse oder einen Wert ersetzt werden muss<br />
*<tt>Schreibmaschinenstil</tt> bedeutet, dass es so im Quellcode geschrieben werden kann.<br />
<br />
<b> ADDLW k </b> <i style="color:grey;">ADD Literal and W - Addiere Zahl (k) und W</i><hr><br />
:Es wird die Rechenoperation <math>W+k</math> ausgeführt und das Ergebniss in das W-Register gespeichert. Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu [[#Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register|Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register]]<br />
<br />
<b> ADDWF R,d </b> <i style="color:grey;">ADD W and F - Addiere W und f </i><hr><br />
:Es wird die Rechenoperation <math>W+R</math> ausgeführt und das Ergebniss entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu [[#Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register|Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register]]<br />
<br />
<b> ANDLW k</b> <i style="color:grey;">AND Literal with W</i><hr><br />
:Es wird bitweise die logische Funktion <math>k\ and\ W</math> ausgeführt und das Ergebniss in das W-Register gespeichert. Dieser Befehl setzt das Z bit des STATUS-Register, falls W=k und das Ergebnis 0 ist.<br />
:Zur Verdeutlichung der Operation:<br />
<dl><dd><!-- zum einrücken da--><br />
11001010 ;k<br />
10101100 ;W-Register<br />
-------- and<br />
10001000 ;W-Register<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
:Dieser Befehl wird zum Löschen bestimmten Bits im Register benutzt, ohne restlichen Bits zu beeinflussen. Die bits, die gelöscht werden sollen, werden in W-Register mit 0 und die unbeeinflußte mit 1 angegeben.<br />
<br />
<b> ANDWF R,d </b> <i style="color:grey;">AND W with F</i><hr><br />
:Es wird bitweise die logische Funktion <math>W\ and\ R</math> ausgeführt und das Ergebniss entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Vergleiche mit ANDLW.<br />
<br />
<b>BCF R,b</b> <i style="color:grey;">Bit Clear F - Bit b im R wird gelöscht</i><hr><br />
:Mit dem Befehl <tt>BCF</tt> wird das Bit '''b''' im Register '''R''' gelöscht. Ein Beispiel:<br />
<dl><dd><!-- zum einrücken da--><br />
movlw b'11111111' ;es wird b'11111111' in das W-Register geschrieben<br />
BCF W,2 ;es wird bit 2 im W-Register gelöscht.<br />
;das Ergebnis ist: b'11111011'<br />
</dl></dd><!-- zum einrücken da--><br />
<br />
<b>BSF R,b</b> <i style="color:grey;">Bit Set F - Bit b im R wird gesetzt</i><hr><br />
:Mit dem Befehl <tt>BSF</tt> wird das Bit '''b''' im Register '''R''' gesetzt. Ein Beispiel:<br />
<dl><dd><!-- zum einrücken da--><br />
clrw ;es wird b'00000000' in das W-Register geschrieben<br />
BSF W,2 ;es wird bit 2 im W-Register gesetzt.<br />
;das Ergebnis ist: b'00000100'<br />
</dl></dd><!-- zum einrücken da--><br />
<br />
<b>BTFSC R,b</b> <i style="color:grey;">Bit Test F, Skip if Clear - Wenn das Bit b im Register R 0 ist, überspringe den nächsten Befehl</i><hr><br />
:Mit dem Befehl <tt>BTFSC</tt> kann eine Verzweigung im Programmablauf bewirkt werden. Wenn das Bit '''b''' im Register '''R''' 0 ist, wird der nächste Befehl übersprungen. Ein Beispiel:<br />
<dl><dd><!-- zum einrücken da--><br />
movlw b'00000001' ;es wird die Zahl 1 in das W-Register kopiert.<br />
BTFSC W,0 ;es wird bit 0 geprüft.<br />
;wenn es 0 ist, wird der nächste Befehl übersprungen<br />
goto IST_EINS ;springt zur Marke "IST_EINS" <- in diesem Fall wird dieser<br />
;Sprungbefehl ausgeführt.<br />
goto IST_NULL ;springt zur Marke "IST_NULL"<br />
</dl></dd><!-- zum einrücken da--><br />
<br />
<b>BTFSS R,b</b> <i style="color:grey;">Bit Test F, Skip if Set - Wenn das Bit b im Register R 1 ist, überspringe den nächsten Befehl</i><hr><br />
:Mit dem Befehl <tt>BTFSS</tt> kann eine Verzweigung im Programmablauf bewirkt werden. Wenn das Bit '''b''' im Register '''R''' 1 ist, wird der nächste Befehl übersprungen. Ein Beispiel:<br />
<dl><dd><!-- zum einrücken da--><br />
movlw b'00000001' ;es wird die Zahl 1 in das W-Register kopiert.<br />
BTFSS W,0 ;es wird bit 0 geprüft.<br />
;wenn es 1 ist, wird der nächste Befehl übersprungen<br />
goto IST_NULL ;springt zur Marke "IST_NULL"<br />
goto IST_EINS ;springt zur Marke "IST_EINS" <- in diesem Fall wird dieser<br />
;Sprungbefehl ausgeführt, da der Befehl<br />
;darüber übersprungen wurde.<br />
</dl></dd><!-- zum einrücken da--><br />
<br />
<br />
<b>CALL</b> <i style="color:grey;">CALL Subroutine - Rufe Unterprogramm auf</i><hr><br />
:Mit dem <tt>CALL</tt> Befehl wird ein Unterprogramm aufgerufen. Mit <tt>RETURN</tt> oder <tt>RETLW</tt>-Befehl wird das Unterprogramm beendet und man kehrt zum Befehl nach dem <tt>CALL</tt>-Befehl zurück. Das Unterprogramm wird so definiert, dass im Quellcode der Name des Unterprogramms nicht eingerückt steht. Ein Beispiel:<br />
<br />
<dl><dd><!-- zum einrücken da--><br />
movlw d'13' ;in das W-Register wird 13d geladen<br />
CALL UP1 ;es wird das Unterprogramm "UP1" aufgerufen<br />
movwf ergebnis ;das W-Register wird in das Register "ergebnis" kopiert.<br />
;im Register "ergebnis" steht nun 23d<br />
<br />
UP1 addlw d'10' ;es wird 10d zum W-Register addiert<br />
return ;kehre zurück zum Aufrufer<br />
</dl></dd><!-- zum einrücken da--><br />
<br />
<b>CLRF R</b> <i style="color:grey;">CLeaR F- Schreibe 0 in das Register R</i><hr><br />
:Das Register '''R''' wird mit Nullen gefüllt (gelöscht).<br />
<br />
<b>CLRW</b> <i style="color:grey;">CLeaR W - Schreibe 0 in W</i><hr><br />
:Das W-Register wird mit Nullen gefüllt (gelöscht).<br />
<br />
<b>CLRWDT</b> <i style="color:grey;">CLeaR WatchDog Timer - Setzt den Watchdog-Timer zurück</i><hr><br />
:Es wird der WDT (Watchdog-Timer) zurückgesetzt und der Zähler des WDT auf 0 gesetzt, zusätzlich werden die STATUS-bits TO und PD gesetzt.<br />
<br />
<b>COMF R,d</b> <i style="color:grey;">COMplement F - negiere alle bits im Register R</i><hr><br />
:Von der Binärzahl im Register '''R''' werden alle 0 mit 1 und alle 1 mit 0 ersetzt. Das Ergebnis wird entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Ein kleines Beispiel: aus <tt>AAh</tt> (<tt>10101010b</tt>) wird <tt>55h</tt> (<tt>01010101b</tt>).<br />
<br />
<b>DECF R,d</b> <i style="color:grey;">DECrement F - Subtrahiert 1 vom Regiser f</i><hr><br />
:Vom Wert des Registers '''R''' wird 1 subtrahiert und das Ergebnis entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>).<br />
:Weil es keine "echte" Substraktion ist, beeinflusst dieser Befehl das C-Flag im STATUS-Register nicht.<br />
<br />
<b>DECFSZ R,d</b> <i style="color:grey;">DECrement F, Skip if Zero - Subtrahiert 1 vom Regiser f, überspringe wenn 0</i><hr><br />
:Vom Wert des Registers '''R''' wird 1 subtrahiert und das Ergebnis entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Der Zusatz SZ steht für ''skip if zero'', d.h. wenn das Ergebnis der Rechnung Null ist, wird der nächste Befehl übersprungen. Dieser Befehl wird für Schleifen mit bestimmter Anzahl der Durchläufe benutzt.<br />
<br />
<b>GOTO</b> <i style="color:grey;">GO TO address - Gehe zu Adresse/Sprungmarke</i><hr><br />
:Nach dem GOTO Befehl wird das Programm ab der Adresse weiter ausgeführt, die nach dem GOTO-Befehl steht. Diese Adresse wird durch so genannte Sprungmarke definiert, welche, im Gegensatz zu den Befehlen nicht eingerückt im Quellcode stehen.<br />
<br />
:Um die Anzahl den Sprungmarken im Programm zu verringern, kann auch indirekte Adressierung von Sprungzielen mit "GOTO $+n" bzw. "GOTO $-n" benutzt werden. Das Symbol "$" bedeutet immer die aktuelle Adresse vom Programmzähler (PC). Das Assemblerprogramm, das sowieso jede Sprungmarke in entsprechende absolute Adresse wandelt, erzeugt in diesem Fall die Zieladresse als "PC+n" bzw. "PC-n", wobei "n" immer als hexadezimale Zahl genommen wird.<br />
<br />
:Der Befehl "goto $+1" verbraucht nur Zeit von zwei "nop". <br />
<br />
:Beispiele dazu sind in [[#Pause|Pause]].<br />
<br />
<b>INCF R,d</b> <i style="color:grey;">INCrement F - Addiere 1 zum Register f</i><hr><br />
:Zum Wert des Registers '''R''' wird 1 addiert und das Ergebniss entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>).<br />
:Weil es keine "echte" Addition ist, beeinflusst dieser Befehl das C-Flag im STATUS-Register nicht.<br />
<br />
<b>INCFSZ R,d</b> <i style="color:grey;">INCrement F, Skip if Zero - Addiere 1 zum Regiser f, überspringe wenn 0</i><hr><br />
:Zum Wert des Registers '''R''' wird 1 addiert und das Ergebniss entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Der Zusatz SZ steht für ''skip if zero'', d.h. wenn das Ergebnis der Rechnung Null ist, wird der nächste Befehl übersprungen. Dieser Befehl wird für Schleifen mit bestimmter Anzahl der Durchläufe benutzt.<br />
<br />
<b> IORLW k</b> <i style="color:grey;">Inclusive OR Literal with W</i><hr><br />
:Es wird bitweise die logische Funktion <math>k\ or\ W</math> ausgeführt und das Ergebniss in das W-Register gespeichert. Dieser Befehl setzt das Z bit des STATUS-Register, falls W=k und das Ergebnis 0 ist.<br />
:Zur Verdeutlichung der Operation:<br />
<dl><dd><!-- zum einrücken da--><br />
11001010 ;k<br />
10101100 ;W-Register<br />
-------- or<br />
11101110 ;W-Register<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
:Dieser Befehl wird zum Setzen bestimmten Bits im Register benutzt, ohne restlichen Bits zu beeinflussen. Die bits, die gesetzt werden sollen, werden in W-Register mit 1 und die unbeeinflußte mit 0 angegeben.<br />
<br />
<b> IORWF R,d </b> <i style="color:grey;">Inclusive OR W with F</i><hr><br />
:Es wird bitweise die logische Funktion <math>W\ or\ R</math> ausgeführt und das Ergebniss entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Vergleiche mit IORLW.<br />
<br />
<b>MOVF R,d</b> <i style="color:grey;">MOVe F - Bewege f</i><hr><br />
:Das Register R wird in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder wieder in R kopiert ('''d'''=<tt>F</tt>=<tt>1</tt>). Letzteres mag sinnlos scheinen, ist aber nützlich, da durch den Befehl das Z-Bit im STATUS-Register gesetzt wird, falls R Null ist.<br />
<br />
<b>MOVLW k</b> <i style="color:grey;">MOVe Literal to W - Bewege Zahl (k) in W-Register</i><hr><br />
:Der festgelegte Wert k wird in das W-Register geladen.<br />
<dl><dd><!-- zum einrücken da--><br />
MOVLW 0xA3 ;ins W-Register wird eine Zahl A3h geladen<br />
</dd></dl><!-- zum einrücken da--><br />
:Dieser Befehl wird auch bei indirekter Adressierung zum laden der absoluter Adresse ins "FSR" Register benutzt. Beispiel:<br />
:Der Register "A3" wurde mit "A3 equ 23" definiert.<br />
<dl><dd><!-- zum einrücken da--><br />
MOVLW A3 ;ins W-Register wird die absolute Adresse 23h vom "A3" geladen<br />
movwf FSR ;diese Adresse wird jetzt ins Register "FSR" kopiert <br />
</dd></dl><!-- zum einrücken da--><br />
<b>MOVWF R</b> <i style="color:grey;">MOVe W to F- Bewege W-Register in das Register F</i><hr><br />
:Das W-Register wird in das Register '''R''' kopiert.<br />
<br />
<b>NOP</b> <i style="color:grey;">No OPeration - Kein Befehl zum Ausführen (warte)</i><hr><br />
:Dieser Befehl macht nichts. Er verbraucht nur Zeit, welche sich einfach mit folgender Formel berechnen lässt. <math>t=\frac{4}{f}</math>,wobei <math>f</math> für die Frequenz des Oszillators steht. Siehe hierzu: [[#Pause|Pause]]<br />
<br />
<b>RETFIE</b> <i style="color:grey;">RETurn From Interrupt Enable - Kehre zurück aus der Unterbrechung, erlaube Unterbrechungen</i><hr><br />
:Mit diesem Befehl wird die Interrupt Service Routine (ISR) beendet und das Programm wird an der Zeile weiter ausgeführt, vor der es durch den Interrupt angehalten wurde. Es werden auch alle Interrupts wieder erlaubt (das GIE bit wird gesetzt). Siehe hierzu auch [[#Interrupt | Interrupt]]<br />
<br />
<b>RETLW k</b> <i style="color:grey;">RETurn with Literal in W - Kehre zurück mit Zahl k im W-Register</i><hr><br />
:Wurde ein Programmteil mit dem Befehl <tt>CALL</tt> aufgerufen, dann springt man mit dem Befehl <tt>RETLW</tt> zurück in die nächste Zeile nach der Zeile aus der das <tt>CALL</tt> Befehl ausgeführt wurde. Der in k angegebene Wert wird dabei in das W-Register geschrieben. Dies wird benutzt um zu erkennen aus welchem UP das verzweigte Programm zurückkommt. Dieser Befehl wird aber vor allem für s.g. Wertetabellen (eng: lookup tables) verwendet.<br />
<br />
<b>RETURN</b> <i style="color:grey;">RETURN from Subroutine - Kehre zurück zum Aufrufer</i><hr><br />
:Wurde ein Programmteil mit dem Befehl <tt>CALL</tt> aufgerufen, dann springt man mit dem Befehl <tt>RETURN</tt> zurück zu der nächsten Zeile nach der Zeile aus der das <tt>CALL</tt> Befehl ausgeführt wurde.<br />
<br />
<b>RLF R,d</b> <i style="color:grey;">Rotate Left F through carry - Rotiere das Register f mithilfe des Carry-bits nach links</i><hr><br />
:Alle Bits im Register '''R''' werden um eine Position nach links verschoben. Dabei wird das Carry bit (<tt>STATUS,C</tt>) in das Bit 0 des Registers R geschoben. Bit 7 aus dem Register '''R''' wird in das Carry bit "geschoben". Das Ergebnis wird entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). <br />
<dl><dd><!-- zum einrücken da--><br />
|<--- Register R --->|<br />
|c|<-7<-6<-5<-4<-3<-2<-1<-0<br />
V A<br />
|________________________|<br />
</dd></dl><!-- zum einrücken da--><br />
:Zur Verdeutlichung:<br />
<dl><dd><!-- zum einrücken da--><br />
|C| |-Register R-| ;C steht für das Carry-bit, STATUS,C ; 0-7 stehen für Bitnummer im Register.<br />
c 7 6 5 4 3 2 1 0 ;vor der Rotation<br />
7 6 5 4 3 2 1 0 c ;nach der Rotation<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
<b>RRF R,d</b> <i style="color:grey;">Rotate Right F through carry - Rotiere das Register f mithilfe des Carry-bits nach rechts</i><hr><br />
:Alle Bits im Register '''R''' werden um eine Position nach rechts verschoben. Dabei wird das Carry bit (<tt>STATUS,C</tt>) in das 7.Bit des Registers R geschoben. Bit 0 aus dem Register '''R''' wird in das Carry bit "geschoben". Das Ergebnis wird entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). <br />
<dl><dd><!-- zum einrücken da--><br />
|<--- Register R --->|<br />
|c|->7->6->5->4->3->2->1->0<br />
A V<br />
|________________________|<br />
</dd></dl><!-- zum einrücken da--><br />
:Zur Verdeutlichung:<br />
<dl><dd><!-- zum einrücken da--><br />
|C| |-Register R-| ;C steht für das Carry-bit, STATUS,C ; 0-7 stehen für Bitnummer im Register.<br />
C 7 6 5 4 3 2 1 0 ;vor der Rotation<br />
0 C 7 6 5 4 3 2 1 ;nach der Rotation <br />
</dd></dl><!-- zum einrücken da--><br />
<br />
<b>SLEEP </b> <i style="color:grey;">Go into standby mode - Versetze den Mirokontroller in Bereitschaftsmodus (Schlaf)</i><hr><br />
:Der µC wird in den Sleep-Mode versetzt, in dem er weniger Strom verbraucht. Er kann durch einen Reset, einem Watchdog-Timer-Reset oder durch einen Interrupt wieder aufgeweckt werden.<br />
<br />
<b> SUBLW k </b> <i style="color:grey;">SUBtract W from Literal - Ziehe W von Zahl (k) ab</i><hr><br />
:Es wird die Rechenoperation <math>k-W</math> ausgeführt und das Ergebniss in das W-Register gespeichert. Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu [[#Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register|Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register]]<br />
<br />
<b> SUBWF R,d </b> <i style="color:grey;">SUBtract W from F - Ziehe W von f ab</i><hr><br />
:Es wird die Rechenoperation <math>R-W</math> ausgeführt und das Ergebniss entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu [[#Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register|Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register]]<br />
<br />
:Beispiel:<br />
<dl><dd><!-- zum einrücken da--><br />
movlw d'20' ;schreibe 20 in das W-Register<br />
movwf Register1 ;bewegt das W-Register in das Register1<br />
movlw d'10' ;schreibt 10 in das W-Register<br />
SUBWF Register1,F ;schreibt Register1(20)-W(10) in Register1<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
<b>SWAPF R,d </b> <i style="color:grey;">SWAP nibbles in F - Vertausche die Halbbytes (Nibbles)</i><hr><br />
:Es werden die Nibbles, also die höheren 4 bits (bit7-bit4) mit den niedrigeren 4 bits (bit3-bit0) eines Registers vertauscht und entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>).<br />
:Beispiel:<br />
<dl><dd><!-- zum einrücken da--><br />
movlw b'00001111' ;schreibe b'00001111' in das W-Register<br />
movwf Register1 ;kopiert das W-Register in das Register1<br />
SWAPF Register1,W ;vertauscht die ersten 4 bit mit den letzen<br />
;4 bit in Register 1 und schreibt es in das W-Register<br />
;im W-Register steht nun b'11110000'<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
:Der Befehl wird immer zum Sichern und Wiederherstellen des STATUS-Registers am Anfang und Ende einer ISR (interrupt service routine) verwendet, da er das STATUS-Register nicht beeinflußt (verändert keine STATUS bits).<br />
<br />
<b> XORLW k</b> <i style="color:grey;">EXclusive OR Literal with W</i><hr><br />
:Es wird bitweise die logische Funktion <math>k\ xor\ W</math> ausgeführt und das Ergebniss in das W-Register gespeichert. Dieser Befehl setzt das Z bit des STATUS-Registers, falls W=k und das Ergebnis 0 ist.<br />
<br />
:Zur Verdeutlichung der Operation:<br />
<dl><dd><!-- zum einrücken da--><br />
11001010 ;k<br />
10101100 ;W-Register<br />
-------- xor<br />
01100110 ;W-Register<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
:Dieser Befehl wird vor allem zum Vergleichen eines Registers mit ins W-Register geladenem Wert benutzt. Wenn die Werte in beiden Register gleich sind, wird ein Z bit im STATUS-Register gesetzt.<br />
<br />
<b> XORWF R,d </b> <i style="color:grey;">EXclusive OR W with F</i><hr><br />
:Es wird bitweise die logische Funktion <math>W\ xor\ R</math> ausgeführt und das Ergebniss entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Vergleiche XORLW. Dieser Befehl setzt das Z bit des STATUS-Registers, falls W=k und das Ergebnis 0 ist. Er wird zum Vergleichen zwei Register benutzt, wobei ein Register vorher ins W-Register geladen wird..<br />
<br />
==Besondere, oft gebrauchte Register==<br />
<br />
=== STATUS === <br />
Der Statusregister beinhaltet den Status der Recheneinheit ALU (Arithmetic-Logic Unit), Resetinformationen und die beiden Bits zur Wahl der Speicherbank<br />
<br />
<table style="text-align: center;" cellspacing="0"><br />
<tr><br />
<td colspan="8" style>'''STATUS''' (ADDRESSE 03h, 83h, 103h, 183h)</td><br />
</tr><br />
<tr style="border:0px;"><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R-1</td><br />
<td style="width:60px;">R-1</td><br />
<td style="width:60px;">R/W-x</td><br />
<td style="width:60px;">R/W-x</td><br />
<td style="width:60px;">R/W-x</td><br />
</tr><br />
<tr><br />
<td style="background:#f5f7ff;">'''IRP'''</td><br />
<td style="background:#f5f7ff;">'''RP1'''</td><br />
<td style="background:#f5f7ff;">'''RP0'''</td><br />
<td style="background:#f5f7ff;">'''TO'''</td><br />
<td style="background:#f5f7ff;">'''PD'''</td><br />
<td style="background:#f5f7ff;">'''Z'''</td><br />
<td style="background:#f5f7ff;">'''DC'''</td><br />
<td style="background:#f5f7ff;">'''C'''</td><br />
</tr><br />
<tr><br />
<td>Bit7</td><br />
<td colspan="6"></td><br />
<td>Bit0</td><br />
</tr><br />
</table><br />
<br />
<br />
<br />
*Bit 7 '''IRP''': Register Bank Select Bit (für indirekte Adressierung)<br />
:: 1 = Bank 2, 3 (100h-1FFh)<br />
:: 0 = Bank 0, 1 (00h-FFh)<br />
*Bit 6-5 '''RP<1:0>''': Register Bank Select Bits (für direkte Adressierung)<br />
:: 11 = Bank 3 (180h-1FFh)<br />
:: 10 = Bank 2 (100h-17Fh)<br />
:: 01 = Bank 1 (80h-FFh)<br />
:: 00 = Bank 0 (00h-7Fh) <br />
*Bit 4 '''TO''': Time-out Bit<br />
:: 1 = Nach Power-up, CLRWDT Befehl oder SLEEP Befehl<br />
:: 0 = A Watchdogtimer time-out ist eingetreten<br />
*Bit 3 '''PD''': Power-Down Bit<br />
:: 1 = Nach Power-up oder durch den CLRWDT<br />
:: 0 = Nach einem SLEEP befehl<br />
*Bit 2 '''Z''': Zero bit<br />
:: 1 = Das Ergebnis einer arithmetischen oder logischen Operation ist 0<br />
:: 0 = Das Ergebnis einer arithmetischen oder logischen Operation ist NICHT 0<br />
*Bit 1 '''DC''': Digit carry/borrow bit (ADDWF, ADDLW, SUBLW und SUBWF Befehle)<br />
:: 1 = Ein Carry-out des 4.Niedrigsten Bits (Low Nibble) eines Rechenergebnisses existiert<br />
:: 0 = Kein Carry-out des 4.Niedrigsten Bits eines Rechenergebnisses existiert<br />
*Bit 0 '''C''': Carry/borrow Bit (ADDWF, ADDLW, SUBLW und SUBWF Befehle)<br />
:: 1 = Ein Carry-out des MSB eines Rechenergebnisses existiert<br />
:: 0 = Kein Carry-out des MSB eines Rechenergebnisses existiert<br />
<br />
Das "Carry/Borrow Bit" (to carry = etwas übertragen, to borrow = etwas borgen) dient zum erkennen, wenn ein Übertrag einer Rechenoperation exisitiert. 250d+10d ergibt zum Beispiel 4, und setzt dabei das "Carry/Borrow Bit" auf 1. Damit kann das Programm erkennen, wenn wieder einmal ein Ergebnis größer als 255d herauskam.<br />
Bei Subtraktionen (SUBLW und SUBWF) verhält sich das "Carry/Borrow Bit" umgekehrt als bei Additionen (ADDWF und ADDLW)!! Zum Beispiel 55d-6=49d setzt "Carry/Borrow Bit" auf 1 aber 10d-25d=241d löscht das "Carry/Borrow Bit".<br />
<br />
====Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Registers====<br />
<br />
{|<br />
|-<br />
|<br />
{| {{Blauetabelle}}<br />
|+ Auswirkungen auf das STATUS-Register bei Subtraktionen<br />
|-<br />
| Ergebnis<br />
|| STATUS,C<br />
|| STATUS,Z<br />
|-<br />
| positiv<br />
|align="center"|1<br />
|align="center"|0<br />
|-<br />
| negativ<br />
|align="center"|0<br />
|align="center"|0<br />
|-<br />
| Null<br />
|align="center"|1<br />
|align="center"|1<br />
|}<br />
<br />
||<br />
<br />
{| {{Blauetabelle}}<br />
|+ Auswirkungen auf das STATUS-Register bei Additionen<br />
|-<br />
| Ergebnis<br />
|| STATUS,C<br />
|| STATUS,Z<br />
|-<br />
| positiv<br />
|align="center"|0<br />
|align="center"|0<br />
|-<br />
| Überlauf<br />
|align="center"|1<br />
|align="center"|0<br />
|-<br />
| Null<br />
|align="center"|1<br />
|align="center"|1<br />
|}<br />
|}<br />
<br />
=== OPTION_REG ===<br />
<br />
Durch OPTION_REG werden PORTB pull-ups, aktive Flanke des externen Interrupts, der Prescaler und der Timer0 konfiguriert.<br />
<br />
<br />
<table style="text-align: center;" cellspacing="0"><br />
<tr><br />
<td colspan="8" style>'''OPTION_REG''' (ADDRESSE 81h, 181h)</td><br />
</tr><br />
<tr style="border:0px;"><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-x</td><br />
</tr><br />
<tr><br />
<td style="background:#f5f7ff;">'''/RBPU'''</td><br />
<td style="background:#f5f7ff;">'''INTEDG'''</td><br />
<td style="background:#f5f7ff;">'''T0CS'''</td><br />
<td style="background:#f5f7ff;">'''T0SE'''</td><br />
<td style="background:#f5f7ff;">'''PSA'''</td><br />
<td style="background:#f5f7ff;">'''PS2'''</td><br />
<td style="background:#f5f7ff;">'''PS1'''</td><br />
<td style="background:#f5f7ff;">'''PS0'''</td><br />
</tr><br />
<tr><br />
<td>Bit7</td><br />
<td colspan="6"></td><br />
<td>Bit0</td><br />
</tr><br />
</table><br />
<br />
<br />
*Bit 7 '''/RBPU''': PORTB Pull-up Enable bit ("/" bedeutet Negation)<br />
::1 = Pull-ups sind deaktiviert <br />
::0 = Pull-ups sind aktiviert für die Pins, die im Register TRISB als Eingänge definiert sind<br />
*Bit 6 '''INTEDG''': Interrupt Edge Select bit<br />
::1 = Interrupt auf steigende Flanke am RB0/INT pin<br />
::0 = Interrupt auf fallende Flanke am RB0/INT pin<br />
*Bit 5 '''T0CS''': Timer0 Clock Source Select bit<br />
::1 = Wechsel am RA4/T0CKI pin<br />
::0 = Internes Taktsignal (Fosc/4), wo Fosc ist gleich der Frequenz des Oszillators (z.B. Quarz)<br />
*Bit 4 '''T0SE''': TMR0 Source Edge Select bit<br />
::1 = Inkrementiere bei fallender Flanke am RA4/T0CKI pin<br />
::0 = Inkrementiere bei steigender Flanke am RA4/T0CKI pin<br />
*Bit 3 '''PSA''': Prescaler Assignment bit<br />
::1 = Prescaler ist dem WDT zugewiesen<br />
::0 = Prescaler ist dem Timer0 zugewiesen<br />
*Bit 2-0 '''PS2:PS0''': Prescaler Rate Select bit<br />
<br />
Je nach Zuweisung des Prescalers (durch PSA bit) wird die interne Taktfrequenz des Prozessors (Fosc/4) wie folgt geteilt:<br />
<br />
{|<br />
|-<br />
|<br />
{| {{Blauetabelle}}<br />
<br />
| PS2:PS0<br />
|| TMR0<br />
|| WDT<br />
|-<br />
|align="center"|000<br />
|align="center"|1:2<br />
|align="center"|1:1<br />
|-<br />
|align="center"|001<br />
|align="center"|1:4<br />
|align="center"|1:2<br />
|-<br />
|align="center"|010<br />
|align="center"|1:8<br />
|align="center"|1:4<br />
|-<br />
|align="center"|011<br />
|align="center"|1:16<br />
|align="center"|1:8<br />
|-<br />
|align="center"|100<br />
|align="center"|1:32<br />
|align="center"|1:16<br />
|-<br />
|align="center"|101<br />
|align="center"|1:64<br />
|align="center"|1:32<br />
|-<br />
|align="center"|110<br />
|align="center"|1:128<br />
|align="center"|1:64<br />
|-<br />
|align="center"|111<br />
|align="center"|1:256<br />
|align="center"|1:128<br />
|}<br />
|}<br />
<br />
=== PORTx ===<br />
<br />
<table style="text-align: center;" cellspacing="0"><br />
<tr><br />
<td colspan="8" style>'''PORTx'''</td><br />
</tr><br />
<tr style="border:0px;"><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-x</td><br />
</tr><br />
<tr><br />
<td style="background:#f5f7ff;">'''Rx7'''</td><br />
<td style="background:#f5f7ff;">'''Rx6'''</td><br />
<td style="background:#f5f7ff;">'''Rx5'''</td><br />
<td style="background:#f5f7ff;">'''Rx4'''</td><br />
<td style="background:#f5f7ff;">'''Rx3'''</td><br />
<td style="background:#f5f7ff;">'''Rx2'''</td><br />
<td style="background:#f5f7ff;">'''Rx1'''</td><br />
<td style="background:#f5f7ff;">'''Rx0'''</td><br />
</tr><br />
<tr><br />
<td>Bit7</td><br />
<td colspan="6"></td><br />
<td>Bit0</td><br />
</tr><br />
</table><br />
<br />
!Das "x" steht in allen Fällen für den Buchstaben des Ports!<br />
BSP: "PORTB" oder "RA1"<br />
<br />
Diese Special Funktion Register liegen ALLE in Bank0 und Bank2 (Falls vorhanden).<br />
<br />
Jedes Bit eines PORT-Registers steht für einen Pin (Ein/Ausgang) des jeweiligen Ports. Je nachdem Ob ein Pin als Ausgang oder als Eingang definiert ist, kann man dort entweder den Wert schreiben oder auslesen. Es hat nicht Jeder PIC gleich viele Ports und es sind auch nicht immer 8 Pins pro Port, es können auch weniger sein. Alle PICs der Midrange-Serie besitzen nur bidirektionale Ports, d.h. sie können sowohl Eingang als auch Ausgang sein. Bei Port B kann man bei allen Pins einen internen PULL-UP Widerstand mit dem Bit OPTION_REG<RBPU> hinzuschalten (Standardmäßig auf nicht aktiviert, Bit muss gelöscht werden um die Funktion zu aktivieren). Weiters ist zu beachten, dass einzelne Pins nach dem Reset mit anderen Funktionen belegt sein können. Das gilt im Speziellen für PORTA, wo sich die Komperatoren, AD's (falls vorhanden) und die Oszillatoreingänge befinden. Siehe [[PIC Assembler#I/O Ports|I/O Ports]]. Bei den "kleinsten" PICs der 12F Serie die nur einen I/O Port (6 Pins) haben, heisst der PORT-Register GPIO. <br />
{|{{Blauetabelle}}<br />
|+ Beispiel PICs und ihre Ports (Zahlen in der Klammer sind die Anzahl der Pins des Ports)<br />
|<br />
|'''PIC16F628A'''<br />
|'''PIC16F876'''<br />
|'''PIC16F877'''<br />
|-<br />
|'''PORTA'''<br />
|Ja<br />
|Ja(5)<br />
|Ja(6)<br />
|-<br />
|'''PORTB'''<br />
|ja<br />
|ja<br />
|ja<br />
|-<br />
|'''PORTC'''<br />
|/<br />
|ja<br />
|ja<br />
|-<br />
|'''PORTD'''<br />
|/<br />
|/<br />
|ja<br />
|-<br />
|'''PORTE'''<br />
|/<br />
|/<br />
|ja (3)<br />
|-<br />
|}<br />
<br />
=== TRISx ===<br />
<br />
<table style="text-align: center;" cellspacing="0"><br />
<tr><br />
<td colspan="8" style>'''TRISx'''</td><br />
</tr><br />
<tr style="border:0px;"><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-x</td><br />
</tr><br />
<tr><br />
<td style="background:#f5f7ff;">'''TRISx7'''</td><br />
<td style="background:#f5f7ff;">'''TRISx6'''</td><br />
<td style="background:#f5f7ff;">'''TRISx5'''</td><br />
<td style="background:#f5f7ff;">'''TRISx4'''</td><br />
<td style="background:#f5f7ff;">'''TRISx3'''</td><br />
<td style="background:#f5f7ff;">'''TRISx2'''</td><br />
<td style="background:#f5f7ff;">'''TRISx1'''</td><br />
<td style="background:#f5f7ff;">'''TRISx0'''</td><br />
</tr><br />
<tr><br />
<td>Bit7</td><br />
<td colspan="6"></td><br />
<td>Bit0</td><br />
</tr><br />
</table><br />
<br />
!Das "x" steht in allen Fällen für den Buchstaben des Ports!<br />
BSP: "TRISB" oder "TRISA"<br />
<br />
Bei den "kleinsten" PICs der 12F Serie die nur einen I/O Port (6 Pins) haben, heisst der TRIS-Register TRISIO.<br />
<br />
Die TRIS-Bytes dienen dazu, einzelne I/O Pins als Eingang oder Ausgang zu definieren. Sie liegen immer genau eine Bank über den jeweiligen PORT-Bytes, d.h. sie liegen alle in Bank1 und Bank3 (falls vorhanden.) Besonders zu beachten ist, dass manche Eingebaute Features wie die Serielle Schnittstelle, I2C, SPI usw nicht funktionieren, wenn die Jeweiligen Sende und Empfangspins nicht manuell umgestellt werden, ja es ''muss'' sogar manchmal umgestellt werden (bei I2C z.B.) Egal ist dies jedoch für Komperatoren und AD-Wandler.<br />
<br />
Merke:<br />
* Eine 1 (als "I" für "Input" zu lesen) eines TRISxx-Bits definiert den zugehörigen PIN am PIC als Eingang.<br />
* Eine 0 (als "O" für "Output" zu lesen) eines TRISxx-Bits definiert den zugehörigen PIN am PIC als Ausgang.<br />
<br />
Siehe [[PIC Assembler#I/O Ports|I/O Ports]].<br />
<br />
=== INTCON === <br />
<br />
<table style="text-align: center;" cellspacing="0"><br />
<tr><br />
<td colspan="8" style>'''INTCON''' (ADDRESSE 0Bh, 8Bh, 10Bh, 18Bh)</td><br />
</tr><br />
<tr style="border:0px;"><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-x</td><br />
</tr><br />
<tr><br />
<td style="background:#f5f7ff;">'''GIE'''</td><br />
<td style="background:#f5f7ff;">'''PEIE'''</td><br />
<td style="background:#f5f7ff;">'''TMR0IE'''</td><br />
<td style="background:#f5f7ff;">'''INT0IE'''</td><br />
<td style="background:#f5f7ff;">'''RBIE'''</td><br />
<td style="background:#f5f7ff;">'''TMR0IF'''</td><br />
<td style="background:#f5f7ff;">'''INT0IF'''</td><br />
<td style="background:#f5f7ff;">'''RBIF'''</td><br />
</tr><br />
<tr><br />
<td>Bit7</td><br />
<td colspan="6"></td><br />
<td>Bit0</td><br />
</tr><br />
</table><br />
<br />
<br />
*Bit 7 '''GIE''': Global Interrupt Enable Bit<br />
::1 = Aktiviert alle Interrupts<br />
::0 = Deaktiviert alle Interrupts<br />
*Bit 6 '''PEIE''': Peripheral Interrupt Enable Bit<br />
::1 = Aktiviert alle peripheren Interrupts<br />
::0 = Deaktiviert alle peripheren Interrupts<br />
*Bit 5 '''TMR0IE''': TMR0 Overflow Interrupt Enable Bit (Overflow von Timer0 löst Interrupt aus)<br />
::1 = Aktiviert den TMR0 Interrupt<br />
::0 = Deaktiviert den TMR0 Interrupt<br />
*Bit 4 '''INT0IE''': RB0/INT External Interrupt Enable Bit (Änderung an RB0 Pin löst Interrupt aus) *)<br />
::1 = Aktiviert den RB0/INT Interrupt (Extern ausgelöst)<br />
::0 = Deaktiviert den RB0/INT Interrupt (Extern ausgelöst)<br />
*Bit 3 '''RBIE''': RB Port On-Change-Interrupt Enable Bit (Änderung an PortB löst Interrupt aus) **)<br />
::1 = Aktiviert den RB port on-change-interrupt<br />
::0 = Deaktiviert den RB port on-change-interrupt<br />
*Bit 2 '''TMR0IF''': TMR0 Overflow Interrupt Flag Bit<br />
::1 = TMR0 Register ist Übergelaufen (muss per Software gelöscht werden)<br />
::0 = TMR0 register ist nicht Übergelaufen<br />
*Bit 1 '''INT0IF''': RB0/INT External Interrupt Flag Bit<br />
::1 = Eine Änderung an RB0/INT Pin hat stattgefunden (muss per Software gelöscht werden)<br />
::0 = Eine Änderung an RB0/INT Pin hat nicht stattgefunden <br />
*Bit 0 '''RBIF''': RB Port On-Change-Interrupt Flag bit<br />
::1 = Mind. einer der Pins von RB7:RB4 hat sich geändert (muss per Software gelöscht werden)<br />
::0 = Keiner der Pins von RB7:RB4 hat sich geändert<br />
<br />
<br />
* *) Die Flanke auf die reagiert werden soll, wird mit dem Bit OPTION_REG,INTEDG definiert. (steigende = 1 oder fallende = 0)<br />
* **) Der Interrupt klingt verführerischer Weise so, als ob er den gesamten Port überwacht. Dabei reagiert er nur auf RB7:RB4!!!! Er kann aber den Prozessor vom "Schlaf" aufwecken (z.B. durch einen Tastendruck).<br />
<br />
==Speicherbankorganisation==<br />
===Programmspeicher===<br />
Die Mid-Range MCUs haben einen 2-8k großen Programmspeicher. Dieser hat aber in jeder Speicherzelle nicht 8, sondern 14 Bit - also genau die Länge eines Befehls. Die aktuelle Stelle im Programm wird im PC (Program Counter) verwaltet. Er speichert immer die aktuelle Adresse im Programmspeicher (wird im Code oft als "$" bezeichnet). Bei einem PIC mit 8k Adressen muss er also die Adressen 0000-1FFF speichern können. Daraus folgt die Größe von 13 Bit für den PC. Der Programmspeicher ist in mehrere Bänke geteilt, die alle 2k groß sind. Das Programm springt ohne zutun des Benutzers von einer in die Nächste. Wenn man aber selber springen will, muss man die Register PCLATH (Program Counter Latch High) und PCL (Program Counter Least Significant Byte) mit der Sprungadresse beschreiben.<br />
<br />
===Datenspeicher===<br />
Der Datenspeicher besteht aus den Special Function Registern (SFR) und den General Purpose<br />
Registern (GPR). Die SFRs sind für die Funktionen des PICs zuständig (Interrupts, Timer, ADCs, CCPM...) und die GPRs für die Speicherung von Variablen und Daten.<br />
<br />
Da immer nur 7 Bit der (Ziel)Adresse in einem Befehl gespeichert werden können, sind nur 7Fh (128d) Adressen im Datenbereich möglich. Deswegen wurde das "Banking" eingeführt. 2 Bit im Statusregister (welcher in allen Bänken der selbe ist und auch an der gleichen Stelle sitzt) geben die akutelle "Bank" an und sind nichts anderes als die 2 höchstwertigsten (MSB) Bits der Adresse. Damit lassen sich max. 4 Bänke ansprechen. Je nach PIC gibt es 2-4 Bänke. Die beiden Bits im Register STATUS heißen RP0 (STATUS<5>) und RP1 (STATUS<6>).<br />
<br />
{| {{Blauetabelle}}<br />
|+ Wechseln der Bänke mit RP0 und RP1<br />
|-<br />
| <br />
|| RP1<br />
|| RP0<br />
|-<br />
| Bank0<br />
|align="center"|0<br />
|align="center"|0<br />
|-<br />
| Bank1<br />
|align="center"|0<br />
|align="center"|1<br />
|-<br />
| Bank2<br />
|align="center"|1<br />
|align="center"|0<br />
|-<br />
| Bank3<br />
|align="center"|1<br />
|align="center"|1<br />
|}<br />
<br />
[[Bild:PIC midrange register.JPG]]<br />
# '''FETTE Register''' sind in allen PICs vorhanden<br />
# können je nach PIC unimplementierte Bereiche beinhalten - diese werden immer als 0 gelesen. (DATENBLATT!!)<br />
# siehe 2<br />
# Könnten je nach PIC auch nicht in Bank0 gemapped werden, sind dann eigenständige Register.<br />
# je nach PIC kann es diese Bänke geben oder nicht geben.<br />
<br />
<br />
<br />
== Codeschnipsel ==<br />
<br />
Alle hier sich befindliche Programmbeispiele sind nur auf den PIC12... und PIC16... sicher lauffähig und müssen für PIC18... bei Problemen umgeschrieben werden. Weitere Beispiele befinden sich in http://www.rn-wissen.de/index.php/PIC_ASM_Beispiele<br />
<br />
=== Analog-Digital-Wandler (ADC) ===<br />
<br />
Viele PICs besitzen einen Analog-Digital-Wandler, der eine angelegte Spannung messen kann und diese als Zahl speichert.<br />
<br />
Es gibt AD-Wandler mit 8Bit bzw. 10Bit Auflösung, d.h. die Spannung an einem analogen Eingang wird linear 2^8=256 bzw. 2^10=1024 Werten zugeordnet.<br />
Der höchste Wert, auch Referenzspannung genannt, (255 bzw. 1023; der Controller rechnet die Null auch mit) entspricht der Betriebsspannung des PICs oder wahlweise einer wählbaren Spannung, die an RA3 angelegt werden muss. Der niedrigste Wert entspricht 0 Volt.<br />
<br />
Mit dem Analog-Digital-Wandler kann man somit z.B. Sensoren auswerten, die mehr als zwei Zustände ausgeben (Beispiele: Temperatursensor, Helligkeitssensor, Entfernungssensoren usw.) oder Akkuspannungen messen.<br />
<br />
<br />
Bevor überhaupt gemessen werden kann, muss einiges eingestellt werden:<br />
<br />
* Eingangsverteilung Analog/Digital bzw.Referenzspannung (Betriebsspannung oder RA3)<br />
die genauen Einstellungsmöglichkeiten entnehmen sie bitte dem Datenblatt ihres Controllers (Register ADCON1 Bank 1).<br />
<br />
* Wählen des Taktes für den ADC und Einschalten des ADCs<br />
<br />
Das Register ADCON0 besitzt 8 Bits, die folgendes bestimmen:<br />
Bit0 ADON - schaltet AD-Wandler ein oder aus (Strom sparen)<br />
Bit1 - nicht vorhanden<br />
Bit2 GO/Done - startet Wandlung / signalisiert, wann Umwandlung beendet ist.<br />
Bit3 CHS0 - Channel Select 0 wählt Eingang aus<br />
Bit4 CHS1 - Channel Select 1 wählt Eingang aus<br />
Bit5 CHS2 - Channel Select 2 wählt Eingang aus<br />
Bit6 ADCS0 - AD-Clock-Select wählt Takt<br />
Bit7 ADCS1 - AD-Clock-Select wählt Takt<br />
<br />
Kümmern wir uns zunächst um den Takt. Mit den zwei Bits ADCS0 und ADCS1 bestimmt man den Takt - hierbei gibt es vier Möglichkeiten:<br />
<br />
ADCS1=0, ADCS0=0 -> Taktfrequenz des PICs :2<br />
ADCS1=0, ADCS0=1 -> Taktfrequenz des PICs :8<br />
ADCS1=1, ADCS0=0 -> Taktfrequenz des PICs :32<br />
ADCS1=1, ADCS0=1 -> interner RC-Oszillator des ADCs verwenden<br />
<br />
Die Taktfrequenz des ADCs darf 625kHz nicht überschreiten, dementsprechend ist ADCS0 und ADCS1 zu wählen.<br />
<br />
Ist dies vollbracht, so kann man den AD-Wandler einschalten und das Go/Done-Bit löschen.<br />
Codebeispiel:<br />
<br />
bcf ADCON0,6 ;Takt für<br />
bsf ADCON0,7 ;ADC (OSC/32)<br />
bsf ADCON0,0 ;ADC einschalten, ADON=1<br />
bcf ADCON0,2 ;Go/Done-Bit zurücksetzen<br />
<br />
Nun ist der AD-Wandler bereit, Spannungen in Zahlen umzuwandeln.<br />
Für eine Wandlung muss erst der entsprechende Eingang gewählt werden. Dafür sind die CHS0..CHS2-Bits zuständig:<br />
<br />
CHS2 CHS1 CHS0 gewählter Eingang(falls vorhanden)<br />
0 0 0 RA0<br />
0 0 1 RA1<br />
0 1 0 RA2<br />
0 1 1 RA3<br />
1 0 0 RA5<br />
1 0 1 RE0<br />
1 1 0 RE1<br />
1 1 1 RE2<br />
<br />
Achtung! RA4 ist kein analoger Eingang, folglich ist er oben nicht aufgelistet !!<br />
<br />
Codebeispiel:<br />
<br />
bcf ADCON0,3 ;RA2 auswählen<br />
bsf ADCON0,4<br />
bcf ADCON0,5<br />
<br />
Als letztes muss die AD-Routine nur noch gestartet werden. Ist der AD-Wandler fertig, so löscht er das Bit wieder. Das Bit wird nun solange abgefragt, bis der AD-Wandler fertig ist. Den zugeordneten Wert findet man im Register 'ADRES'. <br />
Code:<br />
<br />
bsf ADCON0,2 ;start<br />
wa btfsz ADCON0,2 ;warten,bis es wieder low ist<br />
goto wa ;noch nicht fertig<br />
;jetzt ist er fertig<br />
movf ADRES,W ;ADRES in W verschieben, um damit gleich weiter zu arbeiten<br />
<br />
Die AD-Wandlung ist somit fertig. Liest man mehrere Werte von unterschiedlichen Eingängen ein und möchte diese miteinander vergleichen o.ä., sollte man die Zahlen von ADRES in anderen Registern zwischenspeichern.<br />
<br />
Desweiteren ist zu beachten, dass der Analog-Digital-Wandler eine gewisse Pause zwischen den Wandlungen benötigt, die sog. 'Aquisition Time'. Diese Zeitangabe entnehmen sie bitte ihrem Datenblatt.<br />
<br />
mögliche Probleme:<br />
<br />
<br />
;Der PIC liefert stark schwankende Werte<br />
:-> Mögliche Ursachen dafür sind z.B. nicht stabile Betriebsspannung, falsche Zeiteinstellungen. Abhilfe schafft auch das mehrmalige Einlesen eines Eingangs. Auch können z.B. 8 Werte eingelesen werden und dann der Durchschnitt gebildet werden.<br />
Evtl. kann ein Kondensator gegen Masse (z.B. 1nF) helfen; Wichtig ist auch eine kleine Verzögerung zwischen Kanalauswahl und Start der Wandlung. In dieser Zeit kann sich der interne Kondensator des ADC's aufladen.<br />
<br />
<br />
;Die Spannung an einem Eingang wird erhöht, die Werte der anderen Eingänge werden aber auch beeinflusst.<br />
:-> Dann sind Sie Opfer des sog. 'Ghostings' geworden (Werte 'scheinen durch', verschleißen). Die Werte der anderen Eingänge gehen mit, weil der interne Kondensator noch auf die Spannung des Vorgängers aufgeladen ist. Maßnahme: Aquisition Time erhöhen, Werte öfters abfragen, Pause zwischen Kanalauswahl und Start<br />
<br />
<br />
;Der PIC liefert immer die gleichen Werte an einem Eingang, obwohl sich die angelegte Spannung ändert<br />
:-> möglicherweise falsche Eingangsverteilung (ADCON1) eingestellt oder falschen Pin gewählt<br />
<br />
=== Hex Dec Wandlung ===<br />
<br />
Das ist ein Unterprogramm das mit "call Hex_Dec" aufgerufen wird. Es gibt 4 Register (A, B, C und D) mit jeweils 32 Bits, die aus jeweils 4 Register mit 8 Bits (z.B. fur A: A3, A2, A1 und A0) bestehen.<br />
<br />
A3 A2 A1 A0<br />
.--------.--------.--------.--------.<br />
|76543210|76543210|76543210|76543210|<br />
'--------'--------'--------'--------'<br />
| |<br />
|<----------- A Register ---------->|<br />
<br />
Sie müssen (wegen FSR) so wie im Code nacheinander liegen (die absoluten Adressen sind nicht wichtig). In das Register "A" wird die hex Zahl eingeschrieben und aus dem "D" die umgewandelte dec Zahl ausgelesen. A3,7 und D3,7 sind MSB und A0,0 und D0,0 sind LSB. Die "B" und "C" sind Hilfsregister. Das UP kann für kleinere als 32-bittige hex Zahlen modifiziert werden. Zum Beispiel für 16-bittige hex Zahlen bleiben nur Register A1,A0,B1,B0,C1,C0,D1,D0 und in den UPs werden in die Register und als X, folgende Zahlen eingeschrieben: HTmp -> 0x10 (Anzahl Bits der hex Zahl), ATmp -> 2 = X (Anzahl Bytes der hex Zahl). Es müssen auch die UPs "CClr", "DClr" und "CopyCB" entsprechend der Registerlänge angepasst werden. Genauso kann natürlich das UP auch für längere hex Zahlen modifiziert werden.<br />
<br />
Das UP "AddCB" addiert dezimal die 32- bittige Register C und B, das Ergebniss befindet sich in C.<br />
Das UP "AddDC" addiert dezimal die 32-bittige Register D und C, das Ergebniss befindet sich in D.<br />
<br />
Die 32-bit Additionen werden in 4 Schritten wie folgt realisiert:<br />
<br />
C0+B0->C0, C1+B1->C1, C2+B2->C2 und C3+B3->C3 <br />
<br />
D0+C0->D0, D1+C1->D1, D2+C2->D2 und D3+C3->D3<br />
<br />
Die Flags "_Fcra", "_Fcrp" und "_Fdca" steuern die alle nötigen Korrekturen.<br />
<br />
Die Wandlung wird in 32 Schritten laut folgender Formel durchgeführt:<br />
<br />
Zahl(d)=B0*2^0+B1*2^1+B2*2^2+B3*2^3+B4*2^4+B5*2^5+B6*2^6+B7*2^7+B8*2^8+B9*2^9+B10*2^10+B11*2^11+<br />
<br />
B12*2^12+B13*2^13+B14*2^14+B15*2^15+B16*2^16+B17*2^17+B18*2^18+B19*2^19+B20*2^20+B21*2^21+<br />
<br />
B22*2^22+B23*2^23+B24*2^24+B25*2^25+B26*2^26+B27*2^27+B28*2^28+B29*2^29+B30*2^30+B31*2^31<br />
<br />
Wobei B0 bedeutet den LSB und B31 den MSB der hex Zahl.<br />
<br />
Die dec Zahl wird im D Register durch "AddDC" gebildet. In dem C Register befindet sich immer die laufende 2^X. Am Anfang wird dort 1=2^0 geladen. Da die nächste zu addierende dec Zahl immer gleich 2* vorherige ist, wird sie einfach so berechnet, dass die aktuelle aus C Register ins B Register kopiert ("CopyCB") und zu C addiert ("AddCB") wird. Diese dec Zahl wird zum D Register addiert nur wenn das entsprechende Bit der hex Zahl gleich 1 ist. Weil im UP "Hex_Dec" immer das bit A0,0 dafür geprüft wird, wird das gesamte 32-bittige A Register nach jeder Addition "AddDC" um ein Bit mit "ARotRb" nach rechts rotiert. Die Flags "_Fcra" und "_Fcrp" übertragen die Bits zwischen den 8 Bit Registern A0, A1, A2 und A3. Es würde zwar reichen, wenn das A Register nur verschoben würde, aber hier wurde Rotation gewählt, damit nach der Wandlung die hex Zahl im A Register unverändert steht. Weil die dec Zahl nur 8-stellig ist, darf die max. hex Zahl 99999999d = 5F5E0FFh sein.<br />
<br />
#define _C STATUS,C<br />
#define _Z STATUS,Z<br />
#define _DC STATUS,DC<br />
#define _Fcra Flags,0<br />
#define _Fcrp Flags,1<br />
#define _Fdca Flags,2<br />
#define _Ferr Flags,3 ;Überlauf (Fehler)<br />
;.................................................................................. <br />
A3 EQU 0x20 ;Register fürs Rechnen<br />
A2 EQU 0x21<br />
A1 EQU 0x22<br />
A0 EQU 0x23<br />
B3 EQU 0x24<br />
B2 EQU 0x25<br />
B1 EQU 0x26<br />
B0 EQU 0x27<br />
C3 EQU 0x28<br />
C2 EQU 0x29<br />
C1 EQU 0x2A<br />
C0 EQU 0x2B<br />
D3 EQU 0x2C<br />
D2 EQU 0x2D<br />
D1 EQU 0x2E<br />
D0 EQU 0x2F<br />
;..................................................................................<br />
Flags EQU 0x30<br />
ATmp EQU 0x31 ;Schleifenzähler<br />
HTmp EQU 0x32 ;Schleifenzähler<br />
RTmp EQU 0x33 ;Zwischenspeicher<br />
;..................................................................................<br />
Hex_Dec call DClr ;Hex>A, D>Dec<br />
movlw 1<br />
movwf C0<br />
movlw 0x20 ;32 bit Hex > 32 bit Dec (8 Stellen)<br />
movwf HTmp<br />
HexDecL btfsc A0,0<br />
call AddDC<br />
call CopyCB<br />
call AddCB<br />
call ARotRb<br />
decfsz HTmp,1<br />
goto HexDecL<br />
return<br />
DClr clrf D0<br />
clrf D1<br />
clrf D2<br />
clrf D3<br />
clrf C0<br />
clrf C1<br />
clrf C2<br />
clrf C3<br />
return<br />
CopyCB movf C0,0<br />
movwf B0<br />
movf C1,0<br />
movwf B1<br />
movf C2,0<br />
movwf B2<br />
movf C3,0<br />
movwf B3<br />
return<br />
AddCB movlw B0 ;C+B>C<br />
movwf FSR<br />
goto AddR<br />
AddDC movlw C0 ;D+C>D<br />
movwf FSR<br />
AddR bcf _Ferr ;Addiere zwei Register<br />
bcf _Fcrp<br />
movlw 4 ;X=4 Bytes lang<br />
movwf ATmp<br />
AddRL bcf _Fdca<br />
bcf _Fcra<br />
movf INDF,0<br />
movwf RTmp<br />
movlw 4 ;X=4 <br />
addwf FSR,1<br />
btfss _Fcrp<br />
goto AddRN<br />
movlw 1<br />
addwf INDF,1<br />
call DecCor<br />
AddRN movf RTmp,0<br />
addwf INDF,1<br />
call DecCor<br />
btfss _Fdca<br />
goto AddCor1<br />
movlw 6<br />
addwf INDF,1<br />
AddCor1 btfss _Fcra<br />
goto AddCor2<br />
movlw 0x60<br />
addwf INDF,1<br />
btfsc _C<br />
bsf _Fcra<br />
AddCor2 movf INDF,0<br />
andlw 0xF0<br />
sublw 0xA0<br />
btfss _Z<br />
goto AddCor3<br />
movf INDF,0<br />
andlw 0x0F<br />
clrf INDF<br />
addwf INDF,1<br />
bsf _Fcra<br />
AddCor3 bcf _Fcrp<br />
btfsc _Fcra<br />
bsf _Fcrp<br />
movlw 5 ;X+1=5<br />
subwf FSR,1<br />
decfsz ATmp,1<br />
goto AddRL<br />
btfsc _Fcra<br />
bsf _Ferr<br />
return<br />
DecCor btfsc _DC ;dezimale Korrektur (equ "DAW" bei PIC18FXXX)<br />
bsf _Fdca<br />
btfsc _C<br />
bsf _Fcra<br />
movf INDF,0<br />
andlw 0x0F<br />
sublw 9<br />
btfss _C<br />
bsf _Fdca<br />
movf INDF,0<br />
andlw 0xF0<br />
sublw 0x90<br />
btfss _C<br />
bsf _Fcra<br />
return<br />
ARotRb movlw A3 ;rotiere A Register 1 Bit rechts<br />
movwf FSR <br />
RRotRb movlw 4 ;rotiere X=4 Bytes<br />
movwf ATmp<br />
bcf _Fcrp<br />
btfsc A0,0<br />
bsf _Fcrp<br />
RRotRbL bcf _Fcra<br />
btfsc INDF,0<br />
bsf _Fcra<br />
bcf _C<br />
btfsc _Fcrp<br />
bsf _C<br />
rrf INDF,1<br />
bcf _Fcrp<br />
btfsc _Fcra<br />
bsf _Fcrp<br />
incf FSR,1<br />
decfsz ATmp,1<br />
goto RRotRbL<br />
return<br />
<br />
=== EEPROM ===<br />
<br />
Alle PICs besitzen EEPROM in dem je nach Typ können 64 bis 256 Databytes abgespeichert werden. Die Adressierung des EEPROMs fängt immer mit 0x00 an. Hier werden nur geprüfte UPs kurz erklärt. Die ersten zwei brauchen nur ein Register "Temp" für Schleifenzähler, dessen Name, falls im Programm schon existiert, geändert werden muss. Die Adresse im EEPROM ist gleich der Adresse im RAM.<br />
<br />
EEPROM beschreiben: <br />
<br />
EEWrite movlw 0x20 ; ab der RAM Adresse wird in EEPROM abgespeichert<br />
movwf FSR<br />
movlw 4 ; soviel Bytes<br />
movwf Temp ; Schleifenzähler<br />
EEWLoop call EEWrite1<br />
incf FSR,1 ; nächste Adresse<br />
decfsz Temp,1<br />
goto EEWLoop<br />
return <br />
<br />
EEWrite1 bcf INTCON,GIE ; Interrupts sperren<br />
movf FSR,0<br />
bsf STATUS,RP0 ; auf Bank1 umschalten<br />
movwf EEADR ; EEPROM Adresse<br />
movf INDF,0<br />
movwf EEDATA ; EEPROM Data<br />
bsf EECON1,WREN<br />
movlw 0x55<br />
movwf EECON2<br />
movlw 0xAA<br />
movwf EECON2<br />
bsf EECON1,WR<br />
bcf EECON1,WREN<br />
btfsc EECON1,WR<br />
goto $-1 ; warten bis WR=0<br />
bcf STATUS,RP0 ; zurück auf Bank 0 umschalten<br />
bsf INTCON,GIE ; Interrupts erlauben<br />
return<br />
<br />
EEPROM lesen und zurück in RAM schreiben: <br />
<br />
EERead movlw 0x20 ; ab der Adresse wird aus EEPROM in RAM abgelegt <br />
movwf FSR<br />
movlw 4 ; soviel Bytes<br />
movwf Temp ; Schleifenzähler<br />
EERLoop call EERead1<br />
incf FSR,1 ; nächste Adresse<br />
decfsz Temp,1<br />
goto EERLoop<br />
return<br />
<br />
EERead1 movf FSR,0<br />
bsf STATUS,RP0 ; auf Bank1 umschalten <br />
movwf EEADR ; EEPROM Adresse<br />
bsf EECON1,RD<br />
movf EEDATA,0 ; EEPROM Data<br />
bcf STATUS,RP0 ; zurück auf Bank 0 umschalten<br />
movwf INDF<br />
return<br />
<br />
Tabelle im EEPROM mit MPASM Direktive "de" erstellen:<br />
<br />
org 0x2100 ; EEPROM initialisieren<br />
; nachfolgend als Kommentar die EEPROM Adressen<br />
de 0x03, 0xE8 ; 0x00<br />
de 0x03, 0xE8 ; 0x02<br />
de 'M','H','z',0x00 ; 0x04<br />
de ' ',' ','W','A','I','T',0x00 ; 0x08<br />
de 'C','o','n','s','t',' ','X',0x00 ; 0x0F<br />
de 'C','=',0x00 ; 0x17<br />
de 'L','=',0x00 ; 0x1A<br />
de 'F','=',0x00 ; 0x1D<br />
de 'O','K',0x00 ; 0x20<br />
usw.<br />
<br />
Wenn die Tabelle sich im Quellcode befindet, wird sie beim brennen des PICs in EEPROM eigeschrieben.<br />
<br />
Das Lesen eines Strings muss ab entsprechender EEPROM Adresse (z.B. für "MHz" -> 0x04) anfangen und auf dem Wert 0x00, der hier als Stringende dient, enden. Einfacher ist, wenn alle Strings gleich lang sind (wie z.B. in einem Zeichengenerator für Grafikdisplay).<br />
<br />
Die Adresse "0x2100" in der "org" Direktive hat mit der Adresse im Programmspeicher nichts zu tun.<br />
<br />
=== Interrupts ===<br />
<br />
Das Programm misst eine Frequenz an T0CKI Pin, wurde für den PIC16F628 geschrieben und in einem genauen Frequenzzähler angewendet. Es ist nur auf PICs lauffähig, die mindestens zwei Timer besitzen.<br />
<br />
Es gibt nur ein Messbereich von 1 Hz bis 99999999 Hz mit gleicher Auflösung 1 Hz, deswegen ist kein Dezimalpunkt benutzt. Für einen kompletten Frequenzzähler ist nur noch ein Eingangsverstärker nötig.<br />
<br />
Benötigte Hardware: Widerstand 470 Ohm zwischen der Frequenzquelle und TOCKI Pin (A4). <br />
<br />
Die Ausgabe erfolgt auf ein 2x8 standard Zeichendisplay. Das HP ("Main") als leere endlose Schleife macht nichts außer warten auf ein Interrupt von Timer0 bzw. Timer1. Die ISR benutzt keine Register vom UPs und deshalb müssen W- und STATUS Register nicht gesichert und wiederhergestellt werden.<br />
<br />
Da der Timer0 nicht, wie der Timer1 durch ein Flag gestartet und gestoppt werden kann, wird es durch setzen und löschen des Bits für TOCKI (A4) Pin im TRISA Register, genauso wie in der AN592 vom Microchip, gemacht.<br />
<br />
Der Timer 0 zählt die Impulse am TOCKI Pin (A4) in der Zeit, die vom Timer 1 bestimmt wird.<br />
<br />
Der Zähler der Frequenz wurde um zwei zusätzliche Register A2 und A3 erweitert und bei jedem Timer0 Überlauf erhöht. Der Prescaler wird ins Register A0 und der TMR0 ins A1 kopiert. Somit ergibt sich 32-bittiges Ergebnis, der als hex Zahl in den Register A3 (MSB),A2, A1, und A0 (LSB) steht. Nach der "Hex_Dec" Wandlung kann die Frequenz aus den Register D3 (MSB), D2, D1 und D0 (LSB) an einem beliebigen Display angezeigt werden.<br />
<br />
Die Quarzfrequenz 7,3728 MHz wurde gewählt, weil sie am nächsten der temperaturstabiltesten Frequenz für AT Quarzen 7,2 MHz ist. Die Quarzfrequenz muß nicht genau sein, da der Frequenzzähler lässt sich sehr genau mit den Werten von TMR1H und TMR1L "trimmen", die vor dem Start des Timers 1 geladen werden.<br />
<br />
Für sehr genaue Messungen wird empfohlen als Taktfrequenz für den Timer1, die Trägerfrequenz eines nächsten LW Senders (z.B. DLF 153 bzw. 207 kHz) zu nutzen. Die Genauigkeit der Frequenz ist ca. 1E-11 und geprüfter Empfänger kann laut der Skizze in [[http://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=13706&highlight=frequenzz%E4hler]] nachgebaut werden. In dem Fall müssen die Register vom Timer1 (TMR1H und TMR1L) vor seinem Start mit entsprechenden Werten geladen werden. <br />
<br />
Hier wird die Messzeit 1 s genutzt und die Frequenz wird mit einer Auflösung von 1 Hz gemessen. Die Messzeit beträgt 3*10000h+(FFFFh-7BFFh) Timertakten. Für den Quarz 7,372800 MHz und Prescaler=8, wird der Takt von Timer1 gleich 7372800/4*8=230400 Hz. Deswegen wird der Timer1 beim Starten mit ca. 7BFFh geladen und nach dem 4. Überlauf gestoppt. Die in TMR1H und TMR1L praktisch geladene Werte unterscheiden sich ein bißchen von berechneten, weil die Quarzfrequenz fast nie genau bis auf 1 Hz stimmt.<br />
<br />
Für z.B. 20 MHz Quarz werden 10 Überläufe des Timers benötigt und er wird beim Start mit 7697h geladen, da 1s gleich 9x10000h+(FFFFh-7697h) Timertakten ist.<br />
<br />
In dem UP "Init" muss eventuell für ein anderes Display die Initialisierung des Displaykontrollers geändert werden.<br />
<br />
In dem Program wurden unveränderte UPs verwendet, die näher in [[#Hex Dec Wandlung|Hex Dec Wandlung]] und [[#Matrix|Matrix]] erklärt sind.<br />
<br />
; Programm zum Messen der Frequenz an T0CKI Pin mit 7,3728 MHz Quarz <br />
LIST P=16F628<br />
include "P16F628.inc"<br />
__CONFIG _WDT_OFF & _PWRTE_ON & _MCLRE_OFF & _HS_OSC & _BODEN_OFF & _LVP_OFF<br />
<br />
; TOCKI PORTA,4 ;I Frequenzeingang<br />
; DB4 PORTB,0 ;O Display Data Bit 4<br />
; DB5 PORTB,1 ;O Display Data Bit 5<br />
; DB6 PORTB,2 ;O Display Data Bit 6<br />
; DB7 PORTB,3 ;O Display Data Bit 7<br />
#define _RS PORTB,4 ;O Display RS<br />
#define _E PORTB,5 ;O Display Enable<br />
#define _C STATUS,C<br />
#define _Z STATUS,Z<br />
#define _DC STATUS,DC<br />
#define _RP0 STATUS,RP0<br />
#define _Fcra Flags,0<br />
#define _Fcrp Flags,1<br />
#define _Fdca Flags,2<br />
#define _Ferr Flags,3 ;Überlauf (Fehler)<br />
#define _Frs Flags,4<br />
#define _Fnz Flags,5<br />
A3 equ 0x20 ;Register fürs Rechnen Hex_Dec<br />
A2 equ 0x21<br />
A1 equ 0x22<br />
A0 equ 0x23<br />
B3 equ 0x24<br />
B2 equ 0x25<br />
B1 equ 0x26<br />
B0 equ 0x27<br />
C3 equ 0x28<br />
C2 equ 0x29<br />
C1 equ 0x2A<br />
C0 equ 0x2B<br />
D3 equ 0x2C<br />
D2 equ 0x2D<br />
D1 equ 0x2E<br />
D0 equ 0x2F<br />
Tmp equ 0x30 ;Register, die für Displayausgabe<br />
Tmp1 equ 0x31 ;vorläufig benutzt werden<br />
Flags equ 0x32<br />
ATmp equ 0x33 ;vorläufige Schleifenzähler<br />
HTmp equ 0x34<br />
RTmp equ 0x35 ;Zwischenspeicher<br />
ORG 0x0000<br />
call Init ;alles initialisieren<br />
Main goto Main ;und in die leere endlose Schleife springen <br />
ORG 0x0004 ;hier fängt ISR (interrupt service routine) an<br />
bcf INTCON,GIE ;alle interrupts sperren<br />
btfss INTCON,T0IF ;ist Timer0 überlaufen ?<br />
goto CheckTMR1 ;nein, dann prüfe Timer1<br />
bcf INTCON,T0IF ;ja, Timer0 interrupt flag löschen und bearbeiten<br />
movlw 1<br />
addwf A2,1 ;erhöhe A2 durch Addition<br />
btfsc _C ;um Überlauf von A2 zu erkennen und<br />
incf A3,1 ;increment A3, wenn A2 überlaufen ist<br />
CheckTMR1 btfss PIR1,TMR1IF ;ist Timer1 überlaufen ?<br />
retfie ;nein, dann ISR beenden und interrupts erlauben<br />
bcf PIR1,TMR1IF ;Timer1 interrupt flag löschen<br />
call Multip ;Interrupts vom Timer1 im UP "Multip" zählen<br />
retfie ;ISR beenden und interrupts erlauben<br />
Multip incf Tmp,1 ;Interruptszähler von Timer1 erhöhen<br />
btfss Tmp,2 ;schon 4.interrupt?<br />
return ;nein, dann zurück<br />
bsf _RP0 ;ja, weiter<br />
movf TRISA,0 ;stopp Timer0 usw.<br />
andlw 0xEF<br />
movwf TRISA ;TOCKI Pin (A4) als Ausgang<br />
bcf _RP0<br />
bcf T1CON,TMR1ON ;stopp Timer1<br />
GetFreq movf TMR0,0 ;Prescaler ins Register A0 kopieren<br />
movwf A1<br />
movwf RTmp<br />
clrf ATmp<br />
FToggle incf ATmp,1<br />
bsf _RP0<br />
bsf OPTION_REG,T0SE<br />
bcf OPTION_REG,T0SE<br />
bcf _RP0<br />
movf TMR0,0<br />
subwf RTmp,0<br />
btfsc _Z<br />
goto FToggle<br />
comf ATmp,1<br />
incf ATmp,0<br />
movwf A0<br />
call Hex_Dec ;Messergebnis auf Decimal wandeln<br />
call AClr <br />
call DispFrq ;eventuell eigenes UP für benutztes Display<br />
clrf Tmp<br />
clrf TMR0<br />
call TMRStart ;beide Timer starten<br />
return<br />
AClr clrf A0 ;Register A löschen<br />
clrf A1<br />
clrf A2<br />
clrf A3<br />
return<br />
TMRStart movlw 0x34 ;Timer1 konfigurieren<br />
movwf T1CON ;und laden<br />
movlw 0x7B ;berechneter Wert: TMR1H=7Bh<br />
movwf TMR1H<br />
movlw 0xFB ;berechneter Wert: TMR1L=FFh<br />
movwf TMR1L<br />
bsf _RP0 ;start Timer0<br />
movf TRISA,0<br />
iorlw 0x10<br />
movwf TRISA ;TOCKI Pin (A4)als Eingang<br />
bcf _RP0<br />
bsf T1CON,TMR1ON ;start Timer1<br />
return<br />
Hex_Dec call DClr ;Hex>A, D>Dec<br />
movlw 1<br />
movwf C0<br />
movlw 0x20 ;32 bit Hex > 32 bit Dec (8 Stellen)<br />
movwf HTmp<br />
HexDecL btfsc A0,0<br />
call AddDC<br />
call CopyCB<br />
call AddCB<br />
call ARotRb<br />
decfsz HTmp,1<br />
goto HexDecL<br />
return<br />
DClr clrf D0<br />
clrf D1<br />
clrf D2<br />
clrf D3<br />
clrf C0<br />
clrf C1<br />
clrf C2<br />
clrf C3<br />
return<br />
CopyCB movf C0,0<br />
movwf B0<br />
movf C1,0<br />
movwf B1<br />
movf C2,0<br />
movwf B2<br />
movf C3,0<br />
movwf B3<br />
return<br />
AddCB movlw B0 ;C+B>C<br />
movwf FSR<br />
goto AddR<br />
AddDC movlw C0 ;D+C>D<br />
movwf FSR<br />
AddR bcf _Ferr ;Addiere zwei Register<br />
bcf _Fcrp<br />
movlw 4 ;X=4 Bytes lang<br />
movwf ATmp<br />
AddRL bcf _Fdca<br />
bcf _Fcra<br />
movf INDF,0<br />
movwf RTmp<br />
movlw 4 ;X=4 <br />
addwf FSR,1<br />
btfss _Fcrp<br />
goto AddRN<br />
movlw 1<br />
addwf INDF,1<br />
call DecCor<br />
AddRN movf RTmp,0<br />
addwf INDF,1<br />
call DecCor<br />
btfss _Fdca<br />
goto AddCor1<br />
movlw 6<br />
addwf INDF,1<br />
AddCor1 btfss _Fcra<br />
goto AddCor2<br />
movlw 0x60<br />
addwf INDF,1<br />
btfsc _C<br />
bsf _Fcra<br />
AddCor2 movf INDF,0<br />
andlw 0xF0<br />
sublw 0xA0<br />
btfss _Z<br />
goto AddCor3<br />
movf INDF,0<br />
andlw 0x0F<br />
clrf INDF<br />
addwf INDF,1<br />
bsf _Fcra<br />
AddCor3 bcf _Fcrp<br />
btfsc _Fcra<br />
bsf _Fcrp<br />
movlw 5 ;X+1=5<br />
subwf FSR,1<br />
decfsz ATmp,1<br />
goto AddRL<br />
btfsc _Fcra<br />
bsf _Ferr<br />
return<br />
DecCor btfsc _DC ;dezimale Korrektur (equ "DAW" bei PIC18FXXX)<br />
bsf _Fdca<br />
btfsc _C<br />
bsf _Fcra<br />
movf INDF,0<br />
andlw 0x0F<br />
sublw 9<br />
btfss _C<br />
bsf _Fdca<br />
movf INDF,0<br />
andlw 0xF0<br />
sublw 0x90<br />
btfss _C<br />
bsf _Fcra<br />
return<br />
ARotRb movlw A3 ;rotiere A Register 1 Bit rechts<br />
movwf FSR <br />
RRotRb movlw 4 ;rotiere X=4 Bytes<br />
movwf ATmp<br />
bcf _Fcrp<br />
btfsc A0,0<br />
bsf _Fcrp<br />
RRotRbL bcf _Fcra<br />
btfsc INDF,0<br />
bsf _Fcra<br />
bcf _C<br />
btfsc _Fcrp<br />
bsf _C<br />
rrf INDF,1<br />
bcf _Fcrp<br />
btfsc _Fcra<br />
bsf _Fcrp<br />
incf FSR,1<br />
decfsz ATmp,1<br />
goto RRotRbL<br />
return<br />
DispFrq bcf _Fnz ;Frequenz anzeigen (8 Ziffern)<br />
call Fst ;mit Unterdrückung der führenden Nullen <br />
movlw 3<br />
movwf ATmp<br />
movlw D3<br />
movwf FSR<br />
swapf INDF,0<br />
andlw 0x0F<br />
btfsc _Z<br />
goto $+2<br />
bsf _Fnz<br />
btfss _Fnz<br />
goto $+3<br />
call Num<br />
goto $+3<br />
movlw " "<br />
call Char<br />
movf INDF,0<br />
andlw 0x0F<br />
btfsc _Z<br />
goto $+2<br />
bsf _Fnz<br />
btfss _Fnz<br />
goto $+3<br />
call Num<br />
goto $+3<br />
movlw " "<br />
call Char<br />
incf FSR,1<br />
decfsz ATmp,1<br />
goto $-18<br />
swapf D0,0<br />
andlw 0x0F<br />
btfsc _Z<br />
goto $+2<br />
bsf _Fnz<br />
btfss _Fnz<br />
goto $+3<br />
call Num<br />
goto $+3<br />
movlw " "<br />
call Char<br />
movf D0,0<br />
andlw 0x0F<br />
call Num<br />
call Snd ;zweite Zeile<br />
movlw "M"<br />
call Char<br />
movlw "^"<br />
call Char<br />
movlw " "<br />
call Char<br />
movlw "k"<br />
call Char<br />
movlw " "<br />
call Char<br />
movlw "^"<br />
call Char<br />
movlw "H"<br />
call Char<br />
movlw "z"<br />
call Char<br />
return<br />
Fst movlw 0x80 ;Adresse der ersten Zeile<br />
goto Cmd<br />
Snd movlw 0xC0 ;Adresse der zweiten Zeile<br />
Cmd movwf Tmp ;Befehl ins Tmp laden<br />
bcf _Frs ;RS=0<br />
goto Send<br />
Val movwf Tmp1<br />
swapf Tmp1,0<br />
call Num<br />
movf Tmp1,0<br />
Num andlw 0x0F<br />
movwf Tmp<br />
movlw 0x0A<br />
subwf Tmp,0<br />
btfsc _C<br />
addlw 7<br />
addlw 0x3A ;ASCII 0-F<br />
Char movwf Tmp ;Zeichen ins Tmp laden<br />
bsf _Frs ;RS=1<br />
Send swapf Tmp,0 ;zuerst High Nibble<br />
andlw 0x0F<br />
movwf PORTB ;an Port (Display) schicken<br />
btfsc _Frs ;RS Flag an Port kopieren<br />
bsf _RS<br />
bsf _E ;Enable erzeugen<br />
bcf _E<br />
movf Tmp,0 ;Low Nibble<br />
andlw 0x0F<br />
movwf PORTB ;an Port (Display) schicken<br />
Enab btfsc _Frs ;RS Flag in Port kopieren<br />
bsf _RS<br />
bsf _E ;Enable erzeugen<br />
bcf _E<br />
Del movlw 0x30 ;Verzögerung min. ca. 50µs<br />
movwf Tmp<br />
decfsz Tmp,1<br />
goto $-1<br />
return<br />
Init clrf PORTA ;Register vorm Start löschen <br />
clrf PORTB<br />
clrf Tmp<br />
clrf TMR0<br />
call AClr <br />
bsf _RP0 ;Bank 1<br />
movlw 0xFF<br />
movwf TRISA ;alle PORTA Pins als Eingänge<br />
clrf TRISB ;und alle PORTB Pins als Ausgänge<br />
movlw 0xE7 ;Takt für Timer0 vom T0CKI Pin usw.<br />
movwf OPTION_REG ;Timer0 konfigurieren<br />
bsf PIE1,TMR1IE ;TMR1 interrupt erlauben<br />
bcf _RP0 ;Bank 0<br />
movlw 0xE0 ;GIE, PEIE & TMR0IE erlauben<br />
movwf INTCON<br />
call TMRStart ;beide Timer starten<br />
bcf _Frs<br />
movlw 2 ;Display auf 4-bit umschalten und initialisieren<br />
movwf PORTB<br />
call Enab<br />
movlw 0x28 ;4 bit, 2 Zeilen, 5x7 Punkten<br />
call Cmd<br />
movlw 0x0C ;display an, cursor aus, nicht blinken<br />
call Cmd<br />
movlw 6 ;incrementieren, nicht schieben<br />
goto Cmd<br />
end<br />
<br />
=== Mausrad bzw. Drehencoder ===<br />
<br />
Das UP wertet ein Mausrad bzw. Drehencoder aus und setzt, je nach Drehrichtung und sein Anschluss an PORT (PORTB,0 und PORTB,1), ein Flag "_Finc" bzw. "_Fdec", das vor der nächsten Abfrage, gelöscht werden muss. Diese Flags können z.B. für Inkrementierung und Dekrementierung von Zählern dienen. <br />
<br />
Die Hardware:<br />
<br />
Mausrad bzw. Drehencoder<br />
.-------.<br />
| o---> PORTB,0<br />
+----o---- |<br />
| | o---> PORTB,1<br />
=== '-------'<br />
GND<br />
<br />
Es müssen die internen pull-ups vom PORTB aktiviert werden. Wenn das Mausrad bzw. Drehencoder an anderen PORT angeschlossen wird, werden externe pull-ups (z.B. 10 kOhm) benötigt. Als "_Z" wurde "STATUS,Z" definiert. Zum Auswerten werden 4 Register "MausA", "MausB", "MausC" und "Flags" gebraucht, die im gesamten Programm definiert werden müssen. Das UP "Delay" soll ca. 10ms dauern. Dafür kann eine Warteschleife oder zusammengesetzte UPs (z.B. Displayausgabe) mit solcher Ausführungszeit benutzt werden. <br />
<br />
In dem UP "Mouse" wird PORTB eingelesen, die alle Bits außer 0 und 1 durch "and" Funktion gelöscht und in den Register "MausB" und "MausC" gespeichert. Nach ca. 10ms wird das gleiche gemacht und im Register "MausA" gespeichert. Der Wert aus "MausA" wird mit dem vorherigen aus "MouseC" durch "xor" verglichen. Sind sie nicht identisch, wird je nach dem Wert "X" (0, 1, 2, bzw. 3) im "MausB" in das entsprechende UP "MouseX" gesprungen. Sonst, wenn "MausA"="MausC", wird zum Aufrufer zurückgekehrt.<br />
<br />
In einem UP "MouseX" wird der letzte Wert im "MausA" ermittelt und nach der Kombination der Werte im "MausB" und "MausA" (01 bzw. 02, 10 bzw. 13, 20 bzw. 23 oder 31 bzw. 32) entsprechendes der Drehrichtung Flag "_Finc" bzw. "_Fdec" gesetzt. Anschließend wird zum Aufrufer zurückgesprungen.<br />
<br />
Detaillierter PAD:<br />
<br />
Mouse V<br />
PORTB->W<br />
3 and W->W<br />
W->MausB<br />
W->MausC<br />
Warten 10ms<br />
PORTB->W <br />
3 and W->W<br />
W->MausA<br />
W xor MausC->MausC<br />
_Z=1 ? J > return<br />
N<br />
V<br />
MausB->MausB<br />
.--------------------------< J _Z=1 ?<br />
| N <br />
| V<br />
| MausB->W<br />
| 1-W->W<br />
| .----< J _Z=1 ?<br />
| | N<br />
| | V<br />
| | MausB->W<br />
| | 2-W->W<br />
| | _Z=1 ? J >---------------------------.<br />
| | N |<br />
| | V |<br />
| | MausB->W |<br />
| | 3-W->W |<br />
| | _Z=1 ? J >-----. |<br />
| | N | |<br />
| | V | |<br />
| | return | |<br />
| | | |<br />
| | | |<br />
Mouse0 V Mouse1 V Mouse3 V Mouse2 V<br />
MausA->W MausA->MausA MausA->W MausA->MausA<br />
1-W->W V 1-W->W V<br />
_Z=1 ? N >--. _Z=1 ? N >--. _Z=1 ? N >--. _Z=1 ? N >--.<br />
J | J | J | J |<br />
V | V | V | V |<br />
setze _Fdec | setze _Finc | setze _Finc | setze _Fdec |<br />
V<-----´ V<-----´ V<-----´ V<-----´<br />
MausA->W MausA->W MausA->W MausA->W <br />
2-W->W 3-W->W 2-W->W 3-W->W<br />
_Z=1 ? N >--. _Z=1 ? N >--. _Z=1 ? N >--. _Z=1 ? N >--.<br />
J | J | J | J |<br />
V | V | V | V |<br />
setze _Finc | setze _Fdec | setze _Fdec | setze _Finc |<br />
V<-----´ V<-----´ V<-----´ V<-----´<br />
return return return return<br />
<br />
und Quellcode:<br />
<br />
Mouse movf PORTB,0<br />
andlw 3<br />
movwf MausB<br />
movwf MausC<br />
call Delay ; ca. 10 ms<br />
movf PORTB,0<br />
andlw 3<br />
movwf MausA<br />
xorwf MausC,1<br />
btfsc _Z<br />
return<br />
movf MausB,1<br />
btfsc _Z<br />
goto Mouse0<br />
movf MausB,0<br />
sublw 1<br />
btfsc _Z<br />
goto Mouse1<br />
movf MausB,0<br />
sublw 2<br />
btfsc _Z<br />
goto Mouse2<br />
movf MausB,0<br />
sublw 3<br />
btfsc _Z<br />
goto Mouse3<br />
return<br />
Mouse0 movf MausA,0<br />
sublw 1<br />
btfsc _Z<br />
bsf _Fdec<br />
movf MausA,0<br />
sublw 2<br />
btfsc _Z<br />
bsf _Finc<br />
return<br />
Mouse1 movf MausA,1<br />
btfsc _Z<br />
bsf _Finc<br />
movf MausA,0<br />
sublw 3<br />
btfsc _Z<br />
bsf _Fdec<br />
return<br />
Mouse2 movf MausA,1<br />
btfsc _Z<br />
bsf _Fdec<br />
movf MausA,0<br />
sublw 3<br />
btfsc _Z<br />
bsf _Finc<br />
return<br />
Mouse3 movf MausA,1<br />
sublw 1<br />
btfsc _Z<br />
bsf _Finc<br />
movf MausA,0<br />
sublw 2<br />
btfsc _Z<br />
bsf _Fdec<br />
return<br />
<br />
=== LCD Displays ===<br />
<br />
==== Matrix ====<br />
<br />
Ein Programm zum Ansteuern von Matrixdisplays im 4-bit Modus. Das Display ist an 6 Pins eines Ports (hier: B) angeschlossen. In diesem Beispielprogramm wurde 2x16 Zeichen Display verwendet, das Programm kann aber für andere Displays modifiziert werden. Für andere Taktfrequenzen muss lediglich nur die Verzögerung vom "Del" geändert werden. Zum Beispiel für 20 MHz Quarz, anstatt 0x10 auf 0x50. Die im "Main" ans Display geschickte Zeichen können beliebig geändert werden.<br />
<br />
Das Display wird wie folgt an den PIC16F84A angeschlossen:<br />
<br />
VCC<br />
+<br />
| .-------.<br />
+----|2 |<br />
+--|1,3,5 |<br />
| | |<br />
=== | |<br />
.-------. GND | |<br />
| B4 10|---------|4 RS |<br />
| B5 11|---------|6 E |<br />
| | | |<br />
| | |7 DB0 |<br />
| | |8 DB1 |<br />
| | |9 DB2 |<br />
| | |10 DB3 |<br />
| B0 6|---------|11 DB4 |<br />
| B1 7|---------|12 DB5 |<br />
| B2 8|---------|13 DB6 |<br />
| B3 9|---------|14 DB7 |<br />
'-------' '-------'<br />
PIC16F84A DISPLAY<br />
<br />
; Display Test im 4-bit Modus<br />
LIST P=16F84a<br />
include "P16F84a.inc" ; 4.000 MHz<br />
__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC<br />
; DB4 PORTB,0 ; Display Data-Bits<br />
; DB5 PORTB,1<br />
; DB6 PORTB,2<br />
; DB7 PORTB,3<br />
#define _RS PORTB,4 ; Display RS<br />
#define _E PORTB,5 ; Display Enable<br />
#define _Frs Flags,0 ; RS Flag<br />
Tmp equ 0x20 <br />
Flags equ 0x21<br />
org 0x0000<br />
call Init<br />
Main call Fst<br />
movlw " "<br />
call Char<br />
movlw " "<br />
call Char<br />
movlw "D"<br />
call Char<br />
movlw "i"<br />
call Char<br />
movlw "s"<br />
call Char<br />
movlw "p"<br />
call Char<br />
movlw "l"<br />
call Char<br />
movlw "a"<br />
call Char<br />
movlw "y"<br />
call Char<br />
movlw " "<br />
call Char<br />
movlw "T"<br />
call Char<br />
movlw "e"<br />
call Char<br />
movlw "s"<br />
call Char<br />
movlw "t"<br />
call Char<br />
call Snd<br />
movlw " "<br />
call Char<br />
movlw "i"<br />
call Char<br />
movlw "m"<br />
call Char<br />
movlw " "<br />
call Char<br />
movlw "4"<br />
call Char<br />
movlw "-"<br />
call Char<br />
movlw "b"<br />
call Char<br />
movlw "i"<br />
call Char<br />
movlw "t"<br />
call Char<br />
movlw " "<br />
call Char<br />
movlw "M"<br />
call Char<br />
movlw "o"<br />
call Char<br />
movlw "d"<br />
call Char<br />
movlw "u"<br />
call Char<br />
movlw "s"<br />
call Char<br />
sleep<br />
Fst movlw 0x80 ; Anfangsadresse der ersten Zeile<br />
goto Cmd <br />
Snd movlw 0xC0 ; Anfangsadresse der zweiten Zeile<br />
Cmd movwf Tmp ; Befehl ins Tmp laden<br />
bcf _Frs ; RS=0<br />
goto Send<br />
Char movwf Tmp ; Zeichen ins Tmp laden<br />
bsf _Frs ; RS=1<br />
Send swapf Tmp,0 ; zuerst High Nibble (ab jetzt Low Nibble)<br />
andlw 0x0F ; (aktuelles Low Nibble ausblenden)<br />
movwf PORTB ; an Port (Display) schicken<br />
btfsc _Frs ; RS Flag ans Port kopieren<br />
bsf _RS<br />
bsf _E ; Enable erzeugen<br />
bcf _E<br />
movf Tmp,0 ; Low Nibble<br />
andlw 0x0F ; (High Nibble ausblenden) <br />
movwf PORTB ; an Port (Display) schicken<br />
Enab btfsc _Frs ; RS Flag ans Port kopieren<br />
bsf _RS<br />
bsf _E ; Enable erzeugen<br />
bcf _E<br />
Del movlw 0x10 ; Verzögerung ca. 50µs<br />
movwf Tmp<br />
decfsz Tmp,1<br />
goto $-1<br />
return<br />
Init clrf PORTB ; PortB initialisieren<br />
bsf STATUS,RP0 ; Bank 1 <br />
clrf TRISB ; alle Pins als Ausgänge<br />
bcf STATUS,RP0 ; Bank 0<br />
bcf _Frs<br />
movlw 2 ; Display auf 4-bit umschalten und initialisieren<br />
movwf PORTB<br />
call Enab<br />
movlw 0x28 ; 4 bit, 2 Zeilen, 5x7 Punkten<br />
call Cmd<br />
movlw 0x0C ; display an, cursor aus, nicht blinken<br />
call Cmd<br />
movlw 6 ; incrementieren, nicht schieben<br />
goto Cmd<br />
end<br />
<br />
==== Grafik ====<br />
<br />
2-pin Schnittstelle und ein Testprogramm für Grafikdisplay HYUNDAI HP12542R_DYO [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=20277]]<br />
<br />
==== Handy ====<br />
<br />
Als Beispiel die Hardware und ein Testprogramm für Nokia 3310/3330 Display: [[http://www.roboternetz.de/phpBB2/viewtopic.php?p=124669#124669]]<br />
<br />
==== 7-Segment mit 3 Backplanes ohne Kontroller ====<br />
<br />
Eine Schnittstelle und ein Testprogramm für LPH2673-1 von Pollin:<br />
[[http://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=26005&highlight=lph26731]]<br />
<br />
= High-End (PIC18...)=<br />
<br />
Als High-End werden alle 8-bit PIC18F... klasiffieziert, die Befehlslänge 16-Bit, also 2 Bytes haben. Ihr Befehlsatz enthält insgesamt 75 Befehle.<br />
<br />
Es werden nur nötige fürs Erstellen von ASM Programmen spezifische Unterschiede behandelt, die in der Praxis Probleme für Umsteiger von Basic-Line und Mid-Range bereiten können. Wenn sich jemand ausschliesslich mit PIC18F... beschäftigen will, wird sich keine Unterschiede merken müssen, da ausser erweitertem Befehlsatz die Programme von ihrer Struktur identisch sind. Genaue Parameter für bestimmten PIC befinden sich im entsprechendem Datenblatt.<br />
<br />
== Hardware ==<br />
<br />
Weil die PIC18F... für einige Anwendungen schneller als Mid-Range laufen müssen, wurde bei Takterzeugung eine PLL-Option beim Oszillator zugefügt, die aus standard Quarzen (z.B. 12 MHz) durch Multiplikation mal 4 eine Taktfrequenz für CPU 12 MHz (z.B. für USB) erzeugt. Weil die Frequenz des Oszilators für interne Taktung der CPU durch 4 geteilt ist, ermöglichst diese Option, dass die CPU mit Oscillatorfrequenz, also bei z.B. 10 MHz Quarz mit echten 10 MHz (10 MIPs) arbeitet (intern mit 40 MHz).<br />
<br />
Der Programmspeicher ist als 8-Bit breit organisiert. Aus dem Grund jeder 16 Bit langer Befehl belegt im Speicher 2 Bytes. Wenn im Datenblatt z.B. 16384 Bytes angegeben sind, bedeutet das, dass dort sich nur die Hälfte davon, also 8192 Befehle abspeichern lassen. Als Folge sind für Befehle nur gerade Adressen zulässig.<br />
<br />
== Software ==<br />
<br />
Alle Befehle sind um 2 Bit länger, als bei Mid-Range, und es gibt keine Speicherbänke, was Erstellung von Programmen sehr vereinfacht.<br />
<br />
Bisher für Mid-Range ausführlich beschriebene Befehle, ausser "CLRW", "RLF" und "RRF" und Speicherbankumschaltungen (z.B. "BSF RP0" und "BCF RP0") sind für PIC18F... "verständlich" und werden ausgeführt. Die PIC18F... haben zusätzlich 43 Befehle.<br />
<br />
Wenn es um ASM Programm geht, ist er grundsätzlich, ausser zusätzlichen Befehlen, identisch wie bei Mid-Range. Die zusätzliche Befehle ermöglichen Erstellen von mehr kompakten Programmen.<br />
<br />
Die PIC18... haben die Möglichkeit für Interuppts individuell Prioritäten definieren, was die Bearbeitung in ISR vereinfacht. Aus dem Grund besitzen sie zwei Interrupt-Vektoren (Anfangsadressen für ISR): 8h für höhere und 18h für niedrigere Prioritäten, die anders als bei übrigen PIC's sind (4h).<br />
<br />
Ausser erweiterten Befehlsatz haben die PIC18F... drei Register für indirekte Adressierung FSR0, FSR1 und FSR2, die unabhängig voneinender und gleichzeitig benutzt werden können.<br />
<br />
Die CPU's von PIC18F... haben tieferen Stapel für Rücksprungadressen mit 31 Ebenen, der zusätzlich mit "PUSH" und "POP" Befehlen während Ausführung eines Unterprogramms (UP) modifiziert werden kann. Das ermöglichst Änderungen von Rücksprungadressen, so dass nicht unbedingt nach der Ausführung des UP zurück, sondern fast beliebig gesprungen werden kann. Ausführlich ist es in AN818 vom Microchip beschrieben. Siehe dazu: http://ww1.microchip.com/downloads/en/AppNotes/00818a.pdf<br />
<br />
Sehr nutzlich sind auch alle neue Sprungmöglichkeiten z.B. "BRA" der "GOTO" entspricht. Da die bedingte Sprünge von Bits des "STATUS" Register abhängen, muß davor kein Befehl aüsgeführt werden der benötigten Bit (z.B. "Z") prüft.<br />
<br />
Wegen Struktur des Programspeichers ein relativer Sprung zur nächster Adresse muss um 2 Byte erfolgen. Deswegen ist für PIC18F... also "GOTO $+2" der kürzeste mögliche Sprung. Bei "GOTO $+1" springt die CPU "zwischen" gültige Befehle ins "Nirvana", was Absturz des Programms bedeutet. Bei bedingten Sprungen und "BRA" wird der angebebener Wert "n" für die Anzahl den übersprüngten Zeilen automatisch durch 2 multipliziert. <br />
<br />
Alle PIC18F... können den Programspeicher beschreiben und sich selbst programmieren. Dafür gibt es die "TBLWT.." Befehle. Das ist aber nur für mindestens 8 Bytes am Stück möglich. Vor dem Beschreiben müssen die dafür vorgesehene Speicherplätze zuerst gelöscht werden, wobei das für minimum 64 Bytes möglich ist.<br />
<br />
Als Beispiel ein geprüftes Fragment von Bearbeitung des Programmspeichers (Flash) in http://www.rn-wissen.de/index.php/PIC_ASM_Beispiele.<br />
<br />
== Kurzübersicht zusätzliche Befehle ==<br />
<font style="font-size:10px;"><br />
{| <br />
|-<br />
| valign=top |<br />
<br />
{| {{Blauetabelle}}<br />
|ADDWFC||Add WREG and Carry bit to f <br />
|-<br />
|BC||Branch if Carry<br />
|-<br />
|BN||Branch if Negative<br />
|-<br />
|BNC||Branch if Not Carry<br />
|-<br />
|BNN||Branch if Not Negative<br />
|-<br />
|BNOV||Branch if Not Overflow<br />
|-<br />
|BNZ||Branch if Not Zero<br />
|-<br />
|BOV||Branch if Overflow<br />
|-<br />
|BRA||Branch unconditionally<br />
|-<br />
|BZ||Branch if Zero<br />
|-<br />
|BTG||Bit toggle in f<br />
|-<br />
|CPFSEQ||Compare f with W, skip if f=W <br />
|-<br />
|CPFSGT||Compare f with W, skip if f>W<br />
|-<br />
|CPFSLT||Compare f with W, skip if f<W<br />
|}<br />
<br />
| valign=top |<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
|DAW||Decimal Adjust W<br />
|-<br />
|DCFSNZ||Decrement f, skip if not 0<br />
|-<br />
|INFSNZ||Increment f, skip if not 0<br />
|-<br />
|LFSR||Move literal to FSRx<br />
|-<br />
|MOVFF||Move f1 to f2<br />
|-<br />
|MOVLB||Move literal to BSR < 3 : 0 ><br />
|-<br />
|MULLW||Multiply literal with W<br />
|-<br />
|MULWF||Multiply W with f<br />
|-<br />
|NEGF||Negate f<br />
|-<br />
|POP||Pop top of return stack (TOS)<br />
|-<br />
|PUSH||Push top of return stack (TOS)<br />
|-<br />
|RCALL||Relative call<br />
|-<br />
|RESET||Software device RESET<br />
|-<br />
|RLCF||Rotate left f through Carry<br />
|-<br />
|RLNCF||Rotate left f no Carry<br />
|}<br />
<br />
| valign=top |<br />
<br />
{| {{Blauetabelle}}<br />
<br />
|-<br />
|RRCF||Rotate right f trough Carry<br />
|-<br />
|RRNCF||Rotate right f no Carry<br />
|-<br />
|SETF||Set f<br />
|-<br />
|SUBFWB||Subtract f from W with borrow<br />
|-<br />
|SUBWFB||Subtract W from f with borrow<br />
|-<br />
|TBLRD*||Table Read<br />
|-<br />
|TBLRD*+||Table Read with post-increment<br />
|-<br />
|TBLRD*-||Table Read with post-decrement<br />
|-<br />
|TBLRD+*||Table Read with pre-increment<br />
|-<br />
|TBLWT*||Table write<br />
|-<br />
|TBLWT*+||Table write with post-increment<br />
|-<br />
|TBLWT*-||Table write with post-decrement<br />
|-<br />
|TBLWT+*||Table write with pre-increment<br />
|-<br />
|TSTFSZ||Test f, skip if 0<br />
|}<br />
<br />
|}<br />
</font><br />
<br />
== Ausführliche Beschreibung zu den Befehlen ==<br />
<br />
Alle Sprunge ausser "BRA" können nur im Bereich eines Bytes, d.h. von -128 bis +127 erfolgen. Nur der Sprung "BRA" kann zwischen -1024 bis +1023 lang sein.<br />
<br />
Wenn die Bedingung für ein bedingten Sprung nicht erfüllt ist, wird er in einem Takt übersprungen und nächster Befehl ausgeführt.<br />
<br />
Erklärungen zu den Verwendeten Platzhaltern:<br />
*'''k''' stellt einen fest definierten Wert da. z.B. hexadezimal <tt>0x20</tt> bzw. <tt>20</tt>, dezimal <tt>d'42'</tt> bzw. <tt>.42</tt> oder binär <tt>b'00101010'</tt><br />
*'''n''' stellt einen fest definierten Wert wie oben für Sprünge<br />
*'''W''' steht für das W-Register.<br />
*'''d''' steht für ''destination'' (Ziel). Im code wird d durch ein <tt>w</tt> bzw. <tt>0</tt> (der Wert wird in das W-Register gespeichert ) oder <tt>f</tt> bzw. <tt>1</tt> (der Wert wird in das davor definierte Register gespeichert)<br />
*'''b''' steht für Bitnummer im Register (eine Zahl zwischen 0 und 7)<br />
*'''R''' steht für ein Register<br />
*'''fett''' geschrieben Bedeutet, dass es ein Platzhalter ist und im Quellcode durch eine Registeradresse oder einen Wert ersetzt werden muss<br />
*<tt>Schreibmaschinenstil</tt> bedeutet, dass es so im Quellcode geschrieben werden kann.<br />
<br />
<b>ADDWFC R,d</b> <i style="color:grey;"><b>ADD W</b> and <b>F</b> with <b>C</b>arry - Addiere W und f mit Übertrag </i><hr><br />
:Es wird die Rechenoperation <math>W+R</math> mit Berücksichtigung des schon vorhandenen Übertrags ausgeführt und das Ergebniss entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu [[#Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register|Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register]]<br />
<br />
<b>BC n</b> <i style="color:grey;"><b>B</b>ranch if <b>C</b>arry - Springe wenn Übertrag </i><hr> <br />
:Es wird das "C" Bit im "STATUS"-Register überprüft und beim gesetzten gesprungen.<br />
<br />
<b>BN n</b> <i style="color:grey;"><b>B</b>ranch if <b>N</b>egative - Springe wenn negative </i><hr> <br />
:Es wird das "N" Bit im "STATUS"-Register überprüft und beim gesetzten gesprungen.<br />
<br />
<b>BNC n</b> <i style="color:grey;"><b>B</b>ranch if <b>N</b>ot <b>C</b>arry - Springe wenn kein Übertrag </i><hr> <br />
:Es wird das "C" Bit im "STATUS"-Register überprüft und beim gelöschten gesprungen.<br />
<br />
<b>BNN n</b> <i style="color:grey;"><b>B</b>ranch if <b>N</b>ot <b>N</b>egative - Springe wenn nicht negative </i><hr> <br />
:Es wird das "N" Bit im "STATUS"-Register überprüft und beim gelöschten gesprungen.<br />
<br />
<b>BNOV n</b> <i style="color:grey;"><b>B</b>ranch if <b>N</b>ot <b>OV</b>erflow - Springe wenn kein Überlauf </i><hr> <br />
:Es wird das "OV" Bit im "STATUS"-Register überprüft und beim gelöschten gesprungen.<br />
<br />
<b>BNZ n</b> <i style="color:grey;"><b>B</b>ranch if <b>N</b>ot <b>Z</b>ero - Springe wenn ungleich Null </i><hr> <br />
:Es wird das "Z" Bit im "STATUS"-Register überprüft und beim gelöschten gesprungen.<br />
<br />
<b>BOV n</b> <i style="color:grey;"><b>B</b>ranch if <b>OV</b>erflow - Springe wenn Überlauf </i><hr> <br />
:Es wird das "OV" Bit im "STATUS"-Register überprüft und beim gesetzten gesprungen.<br />
<br />
<b>BRA n</b> <i style="color:grey;"><b>BR</b>anch <b>A</b>bsolutly - Springe unbedingt </i><hr> <br />
:Es wird immer unbedingt gesprungen.<br />
<br />
<b>BZ n</b> <i style="color:grey;"><b>B</b>ranch if <b>Z</b>ero - Springe bei Null </i><hr> <br />
:Es wird das "Z" Bit im "STATUS"-Register überprüft und beim gesetzten gesprungen.<br />
<br />
<b>BTG R,b</b> <i style="color:grey;"><b>B</b>it <b>T</b>o<b>G</b>gle F - Kehre bestimmten Bitwert in f um </i><hr> <br />
:Der bestimmte Bit im F-Register wird umgekehrt. Also wenn er vorm Ausführen des Befehls z.B. gleich 1 war, wird er danach gleich 0 sein und umgekehrt.<br />
<br />
<b>CPFSEQ R,d</b> <i style="color:grey;"><b>C</b>om<b>P</b>are <b>F</b> with W, <b>S</b>kip if <b>EQ</b>ual - Vergleiche Register f mit W und überspringe, wenn f=W</i><hr><br />
:Es wird der Registers f mit dem W verglichen und bei f=W, wird der nächste Befehl übersprungen. Der Zusatz SEQ steht für ''skip if equal'', d.h. wenn die Werte in beiden Register gleich sind, wird der nächste Befehl übersprungen.<br />
<br />
<b>CPFSGT R,d</b> <i style="color:grey;"><b>C</b>om<b>P</b>are <b>F</b> with W, <b>S</b>kip if <b>G</b>rea<b>T</b>er - Vergleiche Register f mit W und überspringe, wenn f>W</i><hr><br />
:Es wird der Registers f mit dem W verglichen und bei f>W der nächste Befehl übersprungen. Der Zusatz SGT steht für ''skip if greater'', d.h. wenn der Wert im f grösser als im W-Register ist, wird der nächste Befehl übersprungen.<br />
<br />
<b>CPFSLT R,d</b> <i style="color:grey;"><b>C</b>om<b>P</b>are <b>F</b> with W, <b>S</b>kip if <b>L</b>i<b>T</b>tler - Vergleiche Register f mit W und überspringe, wenn f<W</i><hr><br />
:Es wird der Registers f mit dem W verglichen und bei f<W der nächste Befehl übersprungen. Der Zusatz SLT steht für ''skip if littler (lower)'', d.h. wenn der Wert im f kleiner als im W-Register ist, wird der nächste Befehl übersprungen.<br />
<br />
<b>DAW</b> <i style="color:grey;"><b>D</b>ecimal <b>A</b>djust <b>W</b>REG - Justiere W-Register nach dezimaler Addition </i><hr><br />
:Es weden alle nötige Korrekturen für richtigen Ergebnis im W-Register nach dezimaler Addition durchgeführt.<br />
<br />
<b>DCFSNZ R,d</b> <i style="color:grey;"><b>D</b>e<b>C</b>rement <b>F</b>, <b>S</b>kip if <b>N</b>ot <b>Z</b>ero - Subtrahiere 1 vom Regiser f und überspringe den nächsten Befehl, wenn f ungleich Null ist.</i><hr><br />
:Vom Wert des Registers '''R''' wird 1 subtrahiert und das Ergebnis entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Weil es keine "echte" Substraktion ist, beeinflusst dieser Befehl das C-Flag im STATUS-Register nicht.<br />
<br />
<b>INFSNZ R,d</b> <i style="color:grey;"><b>IN</b>crement <b>F</b>, <b>S</b>kip if <b>N</b>ot <b>Z</b>ero - Addiere 1 zum Register f und überspringe den nächsten Befehl, wenn f ungleich Null ist</i><hr><br />
:Zum Wert des Registers '''R''' wird 1 addiert und das Ergebniss entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Der Zusatz SNZ steht für ''skip if not zero'', d.h. wenn das Ergebnis der Rechnung ungleich Null ist, wird der nächste Befehl übersprungen. Weil es keine "echte" Addition ist, beeinflusst dieser Befehl das C-Flag im STATUS-Register nicht. <br />
<br />
<b>LFSR R,k</b> <i style="color:grey;"><b>L</b>oad <b>FSR</b> - Lade FSR-Register mit einem Wert k </i><hr><br />
:Es wird gewähles FSR-Register (FSR0, FSR1, bzw. FSR2) mit dem Wert k geladen.<br />
<br />
<b>MOVLB k</b> <i style="color:grey;"><b>MOV</b> <b>L</b>iteral to <b>B</b>SR <3:0> - Bewege k ins BSR-Register </i><hr> <br />
:Es wird der Bank Select Register (BSR) mit dem Wert k geladen.<br />
<br />
<b>MOVFF R1,R2</b> <i style="color:grey;"><b>MOV</b>e <b>F</b>1 to <b>F</b>2 - Bewege f1 in f2</i><hr><br />
:Das Register R1 wird in das Register R2 kopiert.<br />
<br />
<b>MULLW k</b> <i style="color:grey;"><b>MUL</b>tiply <b>L</b>iteral with <b>W</b>REG - Multipliziere k mit W-Register </i><hr><br />
:Es wird W-Register mit k multiplieziert und das Ergebnis in Registern PRODH und PRODL gespeichert. Dieser Befehl beeinflusst das STATUS-Register nicht.<br />
<br />
<b>MULWF R</b> <i style="color:grey;"><b>MUL</b>tiply <b>W</b> with <b>F</b> - Multipliziere W-Register mit F</i><hr><br />
:Es wird F-Register mit W-Register multiplieziert und das Ergebnis in F gespeichert. Dieser Befehl beeinflusst das STATUS-Register nicht.<br />
<br />
<b>NEGF R</b> <i style="color:grey;"><b>NEG</b>ate <b>F</b> - Negiere F</i><hr><br />
:Es werden alle Bits von F-Regiester negiert. Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu [[#Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register|Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register]]<br />
<br />
<b>POP</b> <i style="color:grey;"><b>POP</b> top of return stack (TOS) - Nehme die oberste Rücksprungsadresse vom Stapel weg</i><hr><br />
:Es wird die oberste Rücksprungadresse vom Stapel entfernt und alle übrigen Adressen um eine Stelle nach oben geschoben.<br />
<dl><dd><!-- zum einrücken da--><br />
alle <--- Stapel --->|<br />
übrige A Adresse 1 -----> entfernt<br />
Adressen | Adresse 2<br />
um 1 Stelle | Adresse 3<br />
nach oben | ..........<br />
verschieben | Adresse 31<br />
</dd></dl><!-- zum einrücken da--><br />
:Zur Verdeutlichung:<br />
<dl><dd><!-- zum einrücken da--><br />
<br />
<--- Stapel --->| ;vor dem "POP"<br />
Adresse 1 ---> verworfen<br />
Adresse 2<br />
Adresse 3<br />
..........<br />
Adresse 31<br />
<br />
<--- Stapel --->| ;nach dem "POP"<br />
Adresse 2<br />
Adresse 3<br />
Adresse 4<br />
..........<br />
?????????<br />
<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
<b>PUSH</b> <i style="color:grey;"><b>PUSH</b> top of return stack (TOS) - Lege neue oberste Rücksprungsadresse am Stapel </i><hr><br />
:Es wird eine neue oberste Rücksprungadresse am Stapel abgelegt und alle übrigen Adressen um eine Stelle nach unten geschoben.<br />
<dl><dd><!-- zum einrücken da--><br />
alle <--- Stapel --->|<br />
übrige | Adresse 1 <--- neue wird abgelegt<br />
Adressen | Adresse 2<br />
um 1 Stelle | Adresse 3<br />
nach unten | ..........<br />
verschieben V Adresse 31<br />
</dd></dl><!-- zum einrücken da--><br />
:Zur Verdeutlichung:<br />
<dl><dd><!-- zum einrücken da--><br />
<br />
<--- Stapel --->| ;vor dem "POP"<br />
Adresse 1<br />
Adresse 2<br />
Adresse 3<br />
..........<br />
Adresse 31<br />
<br />
<--- Stapel --->| ;nach dem "POP"<br />
PC + 2<br />
Adresse 1<br />
Adresse 2<br />
..........<br />
Adresse 30<br />
<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
<b>RCALL n</b> <i style="color:grey;"><b>R</b>elative <b>CALL</b> - Relativer Aufruf eines Unterprogramms </i><hr><br />
:Es wird ein Unterprogramm reletiv aufgerufen, der innerhalb 1 kB von aktueller Adresse liegen darf. Vergleiche mit "CALL".<br />
<br />
<b>RESET</b> <i style="color:grey;"> < Software device <b>RESET</b> - Software Reset </i><hr><br />
:Es wird Reset des PIC's durchgeführt, genauso wie hardwaremässig mit /MCLR-Pin.<br />
<br />
<b>RLCF R,d</b> <i style="color:grey;"><b>R</b>otate <b>L</b>eft through <b>C</b>arry <b>F</b> - Rotiere das Register f mithilfe des Carry-bits nach links</i><hr><br />
:Alle Bits im Register '''R''' werden um eine Position nach links verschoben. Dabei wird das Carry bit (<tt>STATUS,C</tt>) in das Bit 0 des Registers R geschoben. Bit 7 aus dem Register '''R''' wird in das Carry bit "geschoben". Das Ergebnis wird entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). <br />
<dl><dd><!-- zum einrücken da--><br />
|<--- Register R --->|<br />
|c|<-7<-6<-5<-4<-3<-2<-1<-0<br />
V A<br />
|________________________|<br />
</dd></dl><!-- zum einrücken da--><br />
:Zur Verdeutlichung:<br />
<dl><dd><!-- zum einrücken da--><br />
|C| |-Register R-| ;C steht für das Carry-bit, STATUS,C ; 0-7 stehen für Bitnummer im Register.<br />
c 7 6 5 4 3 2 1 0 ;vor der Rotation<br />
7 6 5 4 3 2 1 0 c ;nach der Rotation<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
<b>RLNCF R,d</b> <i style="color:grey;"><b>R</b>otate <b>L</b>eft <b>N</b>o <b>C</b>arry <b>F</b> - Rotiere das Register f ohne des Carry-bits nach links</i><hr><br />
:Alle Bits im Register '''R''' werden um eine Position nach links verschoben. Das Ergebnis wird entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). <br />
<dl><dd><!-- zum einrücken da--><br />
|<--- Register R --->|<br />
7<-6<-5<-4<-3<-2<-1<-0<br />
V A<br />
|____________________|<br />
</dd></dl><!-- zum einrücken da--><br />
:Zur Verdeutlichung:<br />
<dl><dd><!-- zum einrücken da--><br />
|-Register R-| ;0-7 stehen für Bitnummer im Register.<br />
7 6 5 4 3 2 1 0 ;vor der Rotation<br />
6 5 4 3 2 1 0 7 ;nach der Rotation<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
<b>RRCF R,d</b> <i style="color:grey;"><b>R</b>otate <b>R</b>ight through <b>C</b>arry <b>F</b> - Rotiere das Register f mithilfe des Carry-bits nach rechts</i><hr><br />
:Alle Bits im Register '''R''' werden um eine Position nach rechts verschoben. Dabei wird das Carry bit (<tt>STATUS,C</tt>) in das 7.Bit des Registers R geschoben. Bit 0 aus dem Register '''R''' wird in das Carry bit "geschoben". Das Ergebnis wird entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). <br />
<dl><dd><!-- zum einrücken da--><br />
|<--- Register R --->|<br />
|c|->7->6->5->4->3->2->1->0<br />
A V<br />
|________________________|<br />
</dd></dl><!-- zum einrücken da--><br />
:Zur Verdeutlichung:<br />
<dl><dd><!-- zum einrücken da--><br />
|C| |-Register R-| ;C steht für das Carry-bit, STATUS,C ; 0-7 stehen für Bitnummer im Register.<br />
C 7 6 5 4 3 2 1 0 ;vor der Rotation<br />
0 C 7 6 5 4 3 2 1 ;nach der Rotation <br />
</dd></dl><!-- zum einrücken da--><br />
<br />
<b>RRNCF R,d</b> <i style="color:grey;"><b>R</b>otate <b>R</b>ight <b>N</b>o <b>C</b>arry <b>F</b> - Rotiere das Register f ohne des Carry-bits nach rechts</i><hr><br />
:Alle Bits im Register '''R''' werden um eine Position nach rechts verschoben. Das Ergebnis wird entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). <br />
<dl><dd><!-- zum einrücken da--><br />
|<--- Register R --->|<br />
7->6->5->4->3->2->1->0<br />
A V<br />
|____________________|<br />
</dd></dl><!-- zum einrücken da--><br />
:Zur Verdeutlichung:<br />
<dl><dd><!-- zum einrücken da--><br />
|-Register R-| ; 0-7 stehen für Bitnummer im Register.<br />
7 6 5 4 3 2 1 0 ;vor der Rotation<br />
0 7 6 5 4 3 2 1 ;nach der Rotation <br />
</dd></dl><!-- zum einrücken da--><br />
<br />
<b>SETF R</b> <i style="color:grey;"><b>SETF</b> - Setze das Register f</i><hr><br />
:Alle Bits im Register ''R'' werden gesetzt, also nach dem Ausführen des Befehls wird im Register "FFh" stehen.<br />
<br />
<b>SUBFWB R,d</b> <i style="color:grey;"><b>SUB</b>stract <b>F</b> from <b>W</b> with <b>B</b>orrow - Substrahiere das F-Register von W-Register mit Übertrag</i><hr><br />
:Der inhalt des W-Registers wird vom F-Register mit Berücksichtigung des vorhandenes Übertrags abgezogen. Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu [[#Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register|Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register]]<br />
<br />
<b>SUBWFB R,d</b> <i style="color:grey;"><b>SUB</b>stract <b>W</b> from <b>F</b> with <b>B</b>orrow - Substrahiere das W-Register von F-Register mit Übertrag</i><hr><br />
:Der inhalt des F-Registers wird vom W-Register mit Berücksichtigung des vorhandenes Übertrags abgezogen. Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu [[#Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register|Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register]]<br />
<br />
<b>TBLRD*</b> <i style="color:grey;"><b>T</b>a<b>BL</b>e <b>R</b>ea<b>D*</b> - Lese Tabelle ohne Änderung des Zeigers </i><hr><br />
:Dieser Befehl wird fürs Lesen des Programmspeichers (Flash) angewendet. Nach der Ausführung bleibt der Zeiger unverändert.<br />
<br />
<b>TBLRD*+</b> <i style="color:grey;"><b>T</b>a<b>BL</b>e <b>R</b>ea<b>D*+</b> with post-increment - Lese Labelle mit Nacherhöhung des Zeigers </i><hr><br />
:Dieser Befehl wird fürs Lesen des Programmspeichers (Flash) angewendet. Nach der Ausführung wird der Zeiger um 1 erhöht.<br />
<br />
<b>TBLRD*-</b> <i style="color:grey;"><b>T</b>a<b>BL</b>e <b>R</b>ea<b>D*-</b> with post-decrement - Lese Tabelle mit Nacherniedrigung des Zeigers </i><hr><br />
:Dieser Befehl wird fürs Lesen des Programmspeichers (Flash) angewendet. Nach der Ausführung wird der Zeiger um 1 erniedrigt.<br />
<br />
<b>TBLRD+*</b> <i style="color:grey;"><b>T</b>a<b>BL</b>e <b>R</b>ea<b>D+*</b> with pre-increment - Lese Tabelle mit Vorerhöhung des Zeigers </i><hr><br />
:Dieser Befehl wird fürs Lesen des Programmspeichers (Flash) angewendet. Vor der Ausführung wird der Zeiger um 1 erhöht.<br />
<br />
<b>TBLWT*</b> <i style="color:grey;"><b>T</b>a<b>BL</b>e <b>W</b>ri<b>T*</b>e - Schreibe Tabelle ohne Änderung des Zeigers </i><hr><br />
:Dieser Befehl wird fürs Beschreiben des Programmspeichers (Flash) angewendet. Nach der Ausführung bleibt der Zeiger unverändert.<br />
<br />
<b>TBLWT*+</b> <i style="color:grey;"><b>T</b>a<b>BL</b>e <b>W</b>ri<b>T*+</b>e with post-increment - Schreibe Tabelle mit Nacherhöhung des Zeigers </i><hr><br />
:Diesers Befehl wird fürs Beschreiben des Programmspeichers (Flash) angewendet. Nach der Ausführung wird der Zeiger um 1 erhöht.<br />
<br />
<b>TBLWT*-</b> <i style="color:grey;"><b>T</b>a<b>BL</b>e <b>W</b>ri<b>T*-</b>e with post-decrement - Schreibe Tabelle mit Nacherniedrigung des Zeigers </i><hr><br />
:Dieser Befehl wird fürs Beschreiben des Programmspeichers (Flash) angewendet. Nach der Ausführung wird der Zeiger um 1 erniedrigt.<br />
<br />
<b>TBLWT+*</b> <i style="color:grey;"><b>T</b>a<b>BL</b>e <b>W</b>ri<b>T+*</b>e with pre-increment - Schreibe Tabelle mit Vorerhöhung des Zeigers </i><hr><br />
:Dieser Befehl wird fürs Beschreiben des Programmspeichers (Flash) angewendet. Vor der Ausführung wird der Zeiger um 1 erhöht.<br />
<br />
<b>TSTFSZ R</b> <i style="color:grey;"><b>T</b>e<b>ST</b>t <b>F</b> <b>S</b>kip if <b>Z</b>ero - Prüfe f, überspringe wenn gleich Null ist </i><hr><br />
:Es wird der nächste Befehl öbersprungen, wenn der Register f gleich Null ist.<br />
<br />
== Umschreiben von Programmen ==<br />
<br />
Es werden alle nötige Änderungen beschrieben, die vorhandenes Programm lauffähig machen.<br />
Ausserdem muß für gewünschten PIC nötige Konfiguration im Quellcode ersetzt werden. Weil einige Befehle von PIC18F... müssen für Mid-Range in UPs umgeschrieben werden, die Auführung Zeit vom umgeschriebenen Program kann sich erheblich ändern. Bei zeitkritischen Abläufen ist deswegen Umschreibung High-End -> Mid-Range nicht immer möglich.<br />
<br />
=== Basic-Line & Mid-Range -> High-End ===<br />
<br />
Alle Speicherbankumschaltungen müssen mit ";" ausgeblendet bzw. gelöscht werden.<br />
<br />
Da die PIC18... drei "FSR" Register besitzen muss überall "FSR" konsequent anstatt "FSR" nach Wunsch "FSR0", "FSR1", bzw. "FSR2" eingeschrieben werden.<br />
<br />
Die für PIC18... unverständliche Befehle von Mid-Range müssen, wie folgt, ersetzt werden<br />
<br />
"CLRW" -> "MOVLW 0"<br />
<br />
"RLF" -> "RLCF"<br />
<br />
"RRF" -> "RRCF"<br />
<br />
Alle Relative Sprunge mussen verdoppelt werden. Beispielweise jeder "GOTO $+4" Befehl muss durch "GOTO $+8" bzw. "BRA $+8" ersetzt werden (Asführungszeit bei Schleifen beachten).<br />
<br />
Beim Umschreiben vorhandenen Programen von PIC12... und PIC16... ist für Interrupts ein kompatibilität Modus vorgesehen, die keine definierte Prioritäten für Interrupts benötigt, was folgendemassen in Initialisierung (Init) festgelegt wird:<br />
<br />
bcf RCON,IPEN ; disable priority levels on interrupts<br />
(compatibility modus)<br />
<br />
=== High-End -> Basic Line & Mid-Range ===<br />
<br />
Alle zusätzliche Befehle sind für Mid-Range PIC's unverständlich und müssen als kleine Unterprogramme erstellt werden. Für einige Befehle ist es enfach:<br />
<br />
"MOVFF R1, R2" -> "MOVF R1,0"<br />
"MOVWF R2"<br />
<br />
Es kann auch komplizierter werden, z.B. für "DAW":<br />
<br />
DecCor btfsc _DC ;dezimale Korrektur (ersetzt "DAW" von PIC18FXXX)<br />
bsf _Fdca<br />
btfsc _C<br />
bsf _Fcra<br />
movf INDF,0<br />
andlw 0x0F<br />
sublw 9<br />
btfss _C<br />
bsf _Fdca<br />
movf INDF,0<br />
andlw 0xF0<br />
sublw 90<br />
btfss _C<br />
bsf _Fcra<br />
return<br />
<br />
In dem UP sind "_Fcra" und "_Fdca" im Program definierte Flags. Siehe dazu [[#Hex Dec Wandlung|Hex Dec Wandlung]]<br />
<br />
= Hilfsmittel =<br />
<br />
Hier werden einige Programme presentiert, die das ASM Programmieren erleichtern. Sie sind nur auf PIC12... und PIC16... lauffähig und für PIC18... müssen umgeschrieben werden.<br />
<br />
Damit sie möglichst wenig Data- und Programmspeicher benötigen, sind alle Zahlen auf dem Display in der hex Darstellung.<br />
<br />
Für alle, die es in Dezimalsystem haben wollen, ist ein Taschenrechner mit hex<->dec Wandlung nötig.<br />
<br />
Hoffentlich hilfreiche Beiträge sind auch dort zu finden: http://www.roboternetz.de/community/threads/26098-Tips-Tricks .<br />
<br />
== PIC Miniterminal ==<br />
<br />
Das ist eine Schnittstelle, die nur 2 Leitungen zum Anschluss an PIC braucht. Sie ermöglicht das Steuern von diversen LCD Displays mit üblichem Anschluss ("RS", "Enable" und 8 Databits) und Abfragen von 3 Tasten.<br />
<br />
Sie wird mit einem 2x16 Zeichen Matrixdisplay und 3 Tasten für weiter beschriebene Hilfsprogramme verwendet.<br />
<br />
Hardware und Testprogramm: [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=13685]]<br />
<br />
== PIC RAM Monitor ==<br />
<br />
Er wurde entwickelt um 16 Register (0x20 bis 0x2F) im RAM eines PICs auf dem Display von "PIC Miniterminal", wie skizziert, beobachten zu können:<br />
<br />
Register Adressen (hex) 20 21 22 23 24 25 26 27<br />
.-----------------------.<br />
Zweistelligen Werte (hex) |XX XX XX XX XX XX XX XX|<br />
| |<br />
|XX XX XX XX XX XX XX XX|<br />
'-----------------------'<br />
28 29 2A 2B 2C 2D 2E 2F<br />
<br />
Es können natürlich auch z.B. PORTs beobachtet werden, wenn sie im HP in ausgewähle Register (0x20 bis 0x2F) kopiert werden. Beispielweise so:<br />
<br />
..............<br />
movf PORTA,0 ; PORTA ins W-Register laden<br />
movwf 0x20 ; und ins Register 0x20 kopieren<br />
movf PORTB,0 ; PORTB ins W-Register laden<br />
movwf 0x21 ; und ins Register 0x21 kopieren<br />
u.s.w.<br />
..............<br />
<br />
Um das Beobachten zu ermöglichen, wenn wegen Fehler das ASM Programm in einer endloser Schleife "hängt", wurde Interrupt vom Timer0 angewendet. Dieser Timer wurde gewählt, weil er in allen PICs vorhanden ist. Das Programm zeigt die Registerinhalte auf dem Display ca. 1 mal pro Sekunde, wenn der PIC mit einem 4 MHz Quarz oder internem Oszillator arbeitet.<br />
<br />
Der "PIC RAM Monitor" ist kein selbständiges Programm und wird so konzipiert, dass er in jedes ASM Programm, das den Timer0 nicht benutzt, eingebunden werden kann. Er braucht insgesamt 103 Speicherstellen im Programmspeicher, 10 Register im RAM (0x46 bis 0x4F) und 2 I/O Portpins des PICs für den er benutzt wird. Das beobachtete ASM Programm darf, wegen ISR vom "PIC RAM Monitor", erst ab der Adresse 0x0023 beginnen. Das UP "@" kann für PICs, die mehr Programmspeicher besitzen, mit entsprechender "org" Direktive höher verschoben werden. Entsprechend können auch alle im Programm benutzte Register höchstmögliche Adressen im RAM haben (z.B. @Tmp equ 0x7F anstatt 0x4F). <br />
<br />
Um "Kollisionen" mit dem Programm, in das er eingebunden wird, zu vermeiden, fangen alle seine Marken mit "@" an.<br />
<br />
In dem beobachteten Programm müssen lediglich zwei freie Portpins, an die PIC Miniterminal angeschlossen wird, als Ausgang definiert und initialisiert werden. Außerdem muss in die Initialisierung "goto @Init" eingefügt werden.<br />
<br />
Hier der Quellcode:<br />
<br />
; PIC RAM Monitor.asm<br />
@Tmp equ 0x4F<br />
@Tmp1 equ 0x4E<br />
@Tmp2 equ 0x4D<br />
@Tmp3 equ 0x4C<br />
@Tmp4 equ 0x4B<br />
@Int equ 0x4A<br />
@FSR equ 0x49<br />
@SSR equ 0x48<br />
@SWR equ 0x47<br />
@PORT equ 0x46<br />
ORG 0x0004 ; ISR<br />
bcf INTCON,GIE ; interrupts sperren<br />
movwf @SWR ; W-Register sichern<br />
swapf STATUS,0 ; STATUS Register<br />
movwf @SSR ; sichern<br />
movf FSR,0 ; FSR Register<br />
movwf @FSR ; sichern<br />
clrf @PORT ; Portpins Zustände sichern<br />
btfsc @DT<br />
bsf @PORT,0<br />
btfsc @CK<br />
bsf @PORT,1<br />
btfss INTCON,T0IF ; Timer0 interrupt Flag prüfen<br />
goto @IntTest ; wenn nicht gesetzt, eventuell andere prüfen<br />
bcf INTCON,T0IF ; Timer0 interrupt Flag löschen<br />
incf @Int,1 ; Zähler erhöhen<br />
btfss @Int,4 ; prüfen, ob schon 16d. Timer0 interrupt<br />
goto $+3 ; wenn nicht, Anzeigen der Registerinhalte überspringen<br />
clrf @Int ; Zähler löschen<br />
call @ ; Anzeigen der Registerinhalte aufrufen<br />
@IntTest ;**************;<br />
; eigener Code ;<br />
;**************;<br />
bcf @CK ; Portpins Zustände wiederherstellen<br />
btfsc @PORT,1<br />
bsf @CK<br />
bcf @DT<br />
btfsc @PORT,0<br />
bsf @DT<br />
movf @FSR,0 ; FSR Register<br />
movwf FSR ; wiederherstellen<br />
swapf @SSR,0 ; STATUS Register<br />
movwf STATUS ; wiederherstellen<br />
movf @SWR,0 ; W-Register wiederherstellen<br />
retfie ; zurück ins Programm springen, interrupts erlauben <br />
org 0x03B8 ; diese Adresse kann gleich max.Adresse - 47h sein<br />
@ movlw 0x20 ; Anzeigen der Registerinhalte<br />
movwf FSR <br />
call @1st<br />
call @Line<br />
call @2nd<br />
call @Line<br />
return<br />
@Line movlw 8<br />
movwf @Tmp<br />
movf INDF,0<br />
call @Val<br />
incf FSR,1<br />
decfsz @Tmp,1<br />
goto $-4<br />
return<br />
@1st movlw 0x80 ; Adresse der 1. Zeile an Display schicken<br />
goto @Cmd<br />
@2nd movlw 0xC0 ; Adresse der 2. Zeile an Display schicken<br />
@Cmd bcf STATUS,C ; Befehl an Display schicken<br />
goto @Send<br />
@Val movwf @Tmp1 ; Zahl (00-FF) an Display schicken<br />
swapf @Tmp1,0<br />
call @Num<br />
movf @Tmp1,0<br />
@Num andlw 0x0F ; Ziffer (0-F) an Display schicken<br />
movwf @Tmp2 <br />
movlw 0x0A<br />
subwf @Tmp2,0<br />
btfsc STATUS,C<br />
addlw 7<br />
addlw 0x3A<br />
bsf STATUS,C<br />
@Send movwf @Tmp2<br />
movlw 9 ; ein Byte + RS aus @Tmp2 (9 Bits) an Display schicken<br />
movwf @Tmp3<br />
@Ser bcf @CK<br />
bcf @DT<br />
btfsc @Tmp2,7<br />
bsf @DT<br />
bsf @CK<br />
rlf @Tmp2,1<br />
decfsz @Tmp3,1<br />
goto @Ser<br />
bcf @DT ; setze Display<br />
bsf @DT ; Enable<br />
bcf @CK ; lösche<br />
bcf @DT ; Display<br />
bsf @DT ; Enable<br />
bcf @DT<br />
return<br />
@Init bsf INTCON,GIE ; alle interrupts erlauben<br />
bsf INTCON,T0IE ; Timer0 interrupt erlauben<br />
bcf INTCON,T0IF ; Timer0 interrupt Flag löschen<br />
bsf STATUS,RP0 ; auf Bank 1 umschalten<br />
movf OPTION_REG,0 ; OPTION_REG in W-Register laden<br />
andlw 0xC0 ; Timer0 konfigurieren<br />
iorlw 7 ; Prescaler 256:1 <br />
movwf OPTION_REG ; ins OPTION_REG schreiben<br />
bcf STATUS,RP0 ; zurück auf Bank 0 umschalten<br />
movlw 0x38 ; Display vom PIC Miniterminal initialisieren<br />
call @Cmd<br />
movlw 0x0C<br />
call @Cmd<br />
movlw 6<br />
call @Cmd<br />
return<br />
end<br />
<br />
Und hier ein Beispiel für eine Anwendung mit dem Programm "Erstes.asm" für PIC16F84A:<br />
<br />
; Erstes.asm<br />
list P=16F84A ; Prozessor definieren<br />
include "P16F84A.inc" ; 4.000 MHz<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC<br />
#define @DT PORTB,7 ; * definiere Anschlüsse @DT und @CK<br />
#define @CK PORTB,6 ; * für "PIC RAM Monitor"<br />
#define _T1 PORTB,5 ; Portpins benennen<br />
#define _T2 PORTB,4<br />
#define _L1 PORTB,3<br />
#define _L2 PORTB,2<br />
#define _L3 PORTB,1<br />
#define _L4 PORTB,0<br />
P0 equ 0x20 ; Variablen definieren (Register benennen)<br />
P1 equ 0x21<br />
P2 equ 0x22<br />
org 0x0000 ; hier fängt das gesamte ASM Programm an <br />
call Init ; rufe UP Init (Initialisierung) auf<br />
goto Haupt <br />
org 0x0023 ; hier fängt das "Erstes" an<br />
Haupt btfsc _T1 ; prüfe, ob Taster T1 gedrückt ist, wenn ja,<br />
; überspringe "goto T2Test"<br />
goto T2Test ; wenn nicht, springe zu T2Test<br />
bsf _L1 ; schalte _L1 an (setze den Pin 2 auf "1")<br />
call Warten ; warte ca. 0,4 s (400 000 Prozessortakten)<br />
bcf _L1 ; schalte _L1 aus (setze den Pin2 auf "0")<br />
bsf _L2 ; schalte _L2 an (setze den Pin 5 auf "1")<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus (setze den Pin 5 auf "0")<br />
bsf _L3 ; schalte _L3 an (setze den Pin 6 auf "1")<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus (setze den Pin 6 auf "0")<br />
bsf _L4 ; schalte _L4 an (setze den Pin 7 auf "1")<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus (setze den Pin 7 auf "0")<br />
T2Test btfsc _T2 ; prüfe, ob Taster T2 gedrückt ist, wenn ja,<br />
; überspringe "goto Haupt"<br />
goto Haupt ; Wenn nicht, springe zu Haupt<br />
bsf _L4 ; schalte _L4 an (setze den Pin 7 auf "1")<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus (setze den Pin 7 auf "0")<br />
bsf _L3 ; schalte _L3 an (setze den Pin 6 auf "1")<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus (setze den Pin 6 auf "0")<br />
bsf _L2 ; schalte _L2 an (setze den Pin 5 auf "1")<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus (setze den Pin 5 auf "0")<br />
bsf _L1 ; schalte _L1 an (setze den Pin 2 auf "1")<br />
call Warten ; warte<br />
bcf _L1 ; schalte _L1 aus (setze den Pin2 auf "0")<br />
goto Haupt ; springe zu Haupt<br />
Warten movlw 2 ; schreibe "2" für ca. 0,4 s<br />
movwf P2 ; ins Register P2<br />
clrf P1 ; lösche Register P1 (schreibe "0" für 256 Durchläufe) <br />
clrf P0 ; lösche Register P0 (schreibe "0" für 256 Durchläufe)<br />
decfsz P0,1 ; dekrementiere P0, überspringe nächsten Befehl bei P0 = 0<br />
goto $-1 ; gehe zur vorherigen Adresse (Befehl "decfsz P0") <br />
decfsz P1,1 ; dekrementiere P1, überspringe nächsten Befehl bei P1 = 0 <br />
goto $-4 ; gehe zur aktueller Adresse - 4 (Befehl "clrf P0")<br />
decfsz P2,1 ; dekrementiere P2, überspringe nächsten Befehl bei P2 = 0<br />
goto $-7 ; gehe zur aktueller Adresse - 7 (Befehl "clrf P1")<br />
return ; springe zurück zum Aufrufer ("Haupt"),<br />
Init clrf PORTB ; lösche Port (setze alle werdende Ausgänge auf "0")<br />
bsf STATUS,RP0 ; auf Bank1 umschalten<br />
bcf OPTION_REG,7 ; aktiviere pull-ups<br />
movlw 0x30 ; definiere PortB: Pins 5 und 4 als Eingänge<br />
; und die restlichen als Ausgänge (00110000b) <br />
movwf TRISB ; schreibe in TRIS Register<br />
bcf STATUS,RP0 ; auf Bank0 umschalten<br />
goto @Init ; * initialisiere "PIC RAM Monitor" <br />
; hier endet das gesamte ASM Programm<br />
include "PIC RAM Monitor.asm" <br />
end<br />
<br />
Mit dem "*" wurden 3 Zeilen gekenzeichnet, die, in das mit PIC RAM Monitor beobachtetes ASM Programm, eingefügt werden müssen.<br />
<br />
Falls im beobachteten ASM Programm Interrupts benutzt werden, muss die ISR um die Prüfungen anderen Interrupt Flags ergänzt werden, was in der ISR als "eigener Code" eingetragen ist. Der PIC RAM Monitor reagiert nur auf Timer0 Interrupt Flag.<br />
<br />
Wenn keine I/O Pins mehr frei sind, kann der PIC Miniterminal parallel zur schon vorhandenen Hardware an die als Ausgänge definierte Portpins angeschlossen werden. In dem Fall werden aber die Ausgänge durch Ausgabe auf das Display gestört, was in Kauf genommen werden muss. Die Anschlüsse "@DT" und "@CK" müssen entsprechend definiert werden, z.B beim "Erstes.asm" für den PIC12F629:<br />
<br />
#define @DT _L3 ; * definiere Anschlüsse @DT und @CK<br />
#define @CK _L4 ; * für "PIC RAM Monitor"<br />
#define _T1 GPIO,3 ; Portpins benennen<br />
#define _T2 GPIO,4<br />
#define _L1 GPIO,5<br />
#define _L2 GPIO,2<br />
#define _L3 GPIO,1<br />
#define _L4 GPIO,0<br />
<br />
Demensprechend wird die Leitung "@DT" an GPIO,1 (Pin 6) und "@CK" an GPIO,0 (Pin 7) angeschlossen.<br />
<br />
== PIC Trainer ==<br />
<br />
Das ist im Prinzip ein "PIC RAM Monitor", das um ein paar Funktionen erweitert wurde. Mit diesem Programm wurden schon mehrere ASM Programme erstellt, z.B. [[#Hex Dec Wandlung|Hex Dec Wandlung]].<br />
<br />
Auch hier wurde Timer0 Interrupt verwendet, aber die Frequenz beträgt 2 Interrupts pro Sekunde. Das Programm benötigt ein "PIC Miniterminal", das an 2 freigewählte Portpins des PICs, die sowohl als Ein- als auch Ausgänge funktionieren, angeschlossen wird. Es ist kein selbständiges Programm, das in jedes Programm, das den Timer0 nicht benutzt, eingebunden werden kann. Es belegt 187 Speicherstellen im Programmspeicher und 11 Register im RAM (0x45 bis 0x4F). Die alle mit "*" gekennzeichnete Zeilen (alle außer "goto @Init" können geändert werden), müssen im Programm, in das "PIC Trainer" eingebunden ist, enthalten sein. Wegen ISR darf das Programm, in das "PIC Trainer" eingebunden ist, erst ab der Adresse 0x001E anfangen. Das HP "@Trainer", alle UPs und die Sprungtabelle können für PICs, die mehr Programmspeicher besitzen, mit entsprechender "org" Direktive höher verschoben werden. Entsprechend können auch alle im Programm benutzte Register höchstmögliche Adressen im RAM haben (z.B. @SWR equ 0x7F anstatt 0x4F).Dabei muss auch im UP @Test der Wert im PCLATH geändert werden. Siehe dazu [[#Tabellen|Tabellen]]. <br />
<br />
Um "Kollisionen" mit dem Programm, in das er eingebunden wird, zu vermeiden, fangen alle seine Marken mit "@" an. <br />
<br />
Der Zusammenhang zwischen der Register-Adresse und der Nummer des aufgerufenen Programm-Fragments zeigt folgende Skizze:<br />
<br />
<br />
----->0 1 2 3 4 5 6 7<-----TestX Nummer, für beiden Nibbles gleich<br />
/ -----------------------<br />
Register 2 |XX XX XX XX XX XX XX XX|<br />
Adresse \ |XX XX XX XX XX XX XX XX|<br />
\ -----------------------<br />
----->8 9 A B C D E F<br />
<br />
Es wird empfohlen, für schnelle Orientierung, sich die Nummer auf das Display anzubringen. <br />
<br />
Funktionen den Tasten:<br />
<br />
T1 - bewegt den Cursor auf dem Display um eine Position nach rechts. Am rechten Ende der unteren Zeile springt der Cursor wieder nach ganz links in die obere Zeile.<br />
<br />
T2 - erhöht (incrementiert) das Nibble an der Cursor-Position. Nach dem Fh kommt 0, 1, 2, usw.<br />
<br />
T3 - startet ein TestX mit der Nummer 0 bis Fh, die gleich dem rechten Nibble der Adresse des Registers an der Cursor-Position ist.<br />
<br />
Die Tasten werden 2 mal pro Sekunde während des Interrupts eigelesen und man braucht sie zum gewünschten Ergebniss gedrückt halten und dann los lassen. Gleichzeitiges Drücken mehr als einer Taste ist nicht vorgesehen.<br />
<br />
Um "Üben" zu ermöglichen und das Funktionieren des Programms zu Verstehen, wurde ein kurzes Testprogramm geschrieben.<br />
<br />
Der "PIC Trainer" eignet sich besonders gut zum erstellen von ASM Programmen, da beim einmaligen "brennen" des PICs bis zum 16 Programmfragmente die mit "return" enden (z.B. ein HP als UP) nacheinander aufgerufen und mit bilibigen, in Register eingestellten Werten , getestet werden können. <br />
<br />
Der Quellcode:<br />
<br />
; PIC Trainer.asm<br />
#define @Ftst @Int,7 ; Variablen definieren (Register benennen)<br />
@SWR equ 0x4F<br />
@SSR equ 0x4E<br />
@FSR equ 0x4D<br />
@Tmp equ 0x4C<br />
@Tmp1 equ 0x4B<br />
@Tmp2 equ 0x4A<br />
@Tmp3 equ 0x49<br />
@Int equ 0x48<br />
@LPC equ 0x47<br />
@LPC1 equ 0x46<br />
@Tasten equ 0x45<br />
ORG 0x0004 ; ISR<br />
bcf INTCON,GIE ; interrupts sperren<br />
movwf @SWR ; W-Register sichern<br />
swapf STATUS,0 ; STATUS Register<br />
movwf @SSR ; sichern<br />
movf FSR,0 ; FSR Register<br />
movwf @FSR ; sichern<br />
btfss INTCON,T0IF ; Timer0 interrupt Flag prüfen<br />
goto @IntTest ; wenn nicht gesetzt, eventuell andere prüfen<br />
bcf INTCON,T0IF ; Timer0 interrupt Flag löschen<br />
incf @Int,1 ; Zähler erhöhen<br />
btfss @Int,3 ; prüfen, ob schon 8. Timer0 interrupt<br />
goto $+3 ; wenn nicht, zum "@IntTest" springen<br />
clrf @Int ; Zähler löschen<br />
call @ ; Tasten auswerten und Registerinhalte anzeigen<br />
@IntTest ;**************;<br />
; eigener Code ;<br />
;**************;<br />
movf @FSR,0 ; FSR Register<br />
movwf FSR ; wiederherstellen<br />
swapf @SSR,0 ; STATUS Register<br />
movwf STATUS ; wiederherstellen<br />
movf @SWR,0 ; W-Register wiederherstellen<br />
retfie ; zurück ins Programm springen, interrupts erlauben <br />
ORG 0x0358 ; diese Adresse kann gleich max.Adresse - A7h sein<br />
@Trainer btfss @Ftst<br />
goto @Trainer<br />
bcf @Ftst<br />
call @Test<br />
call @Zeigen<br />
goto @Trainer<br />
@ bsf STATUS,RP0 ; @DT und @CK als Eingänge<br />
bsf @TDT<br />
bsf @TCK<br />
bcf STATUS,RP0<br />
clrf @Tasten ; Zustände von @DT und @CK ins @Tasten kopieren<br />
btfsc @CK<br />
bsf @Tasten,0<br />
btfsc @DT<br />
bsf @Tasten,1<br />
movf @Tasten,1 ; Tasten auswerten <br />
btfsc STATUS,Z<br />
bsf @Ftst ; setze Flag, wenn Taste3 gedrückt<br />
movf @Tasten,0<br />
sublw 1<br />
btfsc STATUS,Z<br />
call @IncLPC ; nächstes Nibble, wenn T2 gedrückt<br />
movf @Tasten,0<br />
sublw 2<br />
btfsc STATUS,Z<br />
call @Inc ; nibble erhöhen, wenn T1 gedrückt<br />
@Zeigen call @Out<br />
movlw 0x20 ; Anzeigen der Registerinhalte<br />
movwf FSR <br />
movlw 0x0C ; Cursor aus<br />
call @Cmd<br />
call @1st<br />
call @Line<br />
call @2nd<br />
call @Line<br />
movlw 0x0E ; Cursor an<br />
call @Cmd<br />
movlw 0x80 ; Display Adresse berechnen und Cursor plazieren<br />
btfsc @LPC,4<br />
addlw 0x30<br />
addwf @LPC,0<br />
call @Cmd<br />
return<br />
@Out bsf STATUS,RP0 ; @DT und @CK als Ausgänge<br />
bcf @TDT<br />
bcf @TCK<br />
bcf STATUS,RP0<br />
return<br />
@Test movlw 3 ; Test0-F starten<br />
movwf PCLATH<br />
movf @LPC1,0<br />
andlw 0x0F<br />
goto @TestTab<br />
@Inc movlw 0x20 ; Register Wert erhöhen<br />
movwf FSR<br />
movf @LPC1,0<br />
addwf FSR,1<br />
btfsc @LPC,0 <br />
goto @IncLN<br />
@IncHN movlw 0x10 ; High Nibble erhöhen<br />
addwf INDF,1<br />
return<br />
@IncLN incf INDF,1 ; Low Nibble erhöhen<br />
movf INDF,0<br />
andlw 0x0F<br />
btfss STATUS,Z<br />
return<br />
movlw 0x10<br />
subwf INDF,1<br />
return<br />
@IncLPC incf @LPC,1 ; Zähler der Position in der Zeile erhöhen<br />
movf @LPC,0<br />
sublw 0x20<br />
btfsc STATUS,Z<br />
clrf @LPC<br />
movf @LPC,0<br />
movwf @LPC1<br />
rrf @LPC1,1 ; @LPC1=@LPC/2<br />
return<br />
@Line movlw 8 ; Zeile an Display schicken<br />
movwf @Tmp<br />
movf INDF,0<br />
call @Val<br />
incf FSR,1<br />
decfsz @Tmp,1<br />
goto $-4<br />
return<br />
@1st movlw 0x80 ; Adresse der 1. Zeile an Display schicken<br />
goto @Cmd<br />
@2nd movlw 0xC0 ; Adresse der 2. Zeile an Display schicken<br />
@Cmd bcf STATUS,C ; Befehl an Display schicken<br />
goto @Send<br />
@Val movwf @Tmp1 ; Zahl (00-FF) an Display schicken<br />
swapf @Tmp1,0<br />
call @Num<br />
movf @Tmp1,0<br />
@Num andlw 0x0F ; Ziffer (0-F) an Display schicken<br />
movwf @Tmp2 <br />
movlw 0x0A<br />
subwf @Tmp2,0<br />
btfsc STATUS,C<br />
addlw 7<br />
addlw 0x3A<br />
bsf STATUS,C<br />
@Send movwf @Tmp2<br />
movlw 9 ; Byte + RS aus @Tmp2 (9 Bits) an Display schicken<br />
movwf @Tmp3<br />
@Ser bcf @CK<br />
bcf @DT<br />
btfsc @Tmp2,7<br />
bsf @DT<br />
bsf @CK<br />
rlf @Tmp2,1<br />
decfsz @Tmp3,1<br />
goto @Ser<br />
bcf @DT ; setze Display<br />
bsf @DT ; Enable<br />
bcf @CK ; lösche<br />
bcf @DT ; Display<br />
bsf @DT ; Enable<br />
bcf @DT<br />
return<br />
@Init bsf INTCON,GIE ; alle interrupts erlauben<br />
bsf INTCON,T0IE ; Timer0 interrupt erlauben<br />
bcf INTCON,T0IF ; Timer0 interrupt Flag löschen<br />
bsf STATUS,RP0 ; auf Bank 1 umschalten<br />
movf OPTION_REG,0 ; OPTION_REG in W-Register laden<br />
andlw 0xC0 ; Timer0 konfigurieren<br />
iorlw 7 ; Prescaler 256:1 <br />
movwf OPTION_REG ; ins OPTION_REG schreiben<br />
bcf STATUS,RP0 ; zurück auf Bank 0 umschalten<br />
clrf @Int ; Variablen initialisieren (löschen)<br />
clrf @LPC<br />
clrf @LPC1<br />
call @Out ; @DT und @CK als Ausgänge<br />
movlw 0x38 ; Display vom PIC Miniterminal initialisieren<br />
call @Cmd<br />
movlw 0x0C<br />
call @Cmd<br />
movlw 6<br />
call @Cmd<br />
return<br />
org 0x3EF ; diese Adresse kann gleich max.Adresse - 10h sein<br />
@TestTab addwf PCL,1 ; Sprungtabelle<br />
goto Test0<br />
goto Test1<br />
goto Test2<br />
goto Test3<br />
goto Test4<br />
goto Test5<br />
goto Test6<br />
goto Test7<br />
goto Test8<br />
goto Test9<br />
goto TestA<br />
goto TestB<br />
goto TestC<br />
goto TestD<br />
goto TestE<br />
goto TestF<br />
end<br />
<br />
Das Testprogramm:<br />
<br />
; Testprogramm.asm<br />
list P=16F84A ; Prozessor definieren<br />
include "P16F84A.inc" ; 4.000 MHz<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC<br />
#define @DT PORTB,7 ; * definiere Anschlüsse @DT und @CK<br />
#define @CK PORTB,6 ; * für "PIC Trainer"<br />
#define @TDT TRISB,7 ; * definiere ensprechende Bits im TRISx Register <br />
#define @TCK TRISB,6 ; * für o.g. Anschlüsse<br />
org 0x0000 ; hier fängt das gesamte ASM Programm an <br />
call Init ; rufe UP Init (Initialisierung) auf<br />
goto @Trainer <br />
org 0x001E ; ab da kann eigener Code anfangen<br />
Test0 incf 0x2F,1<br />
return<br />
Test1 incf 0x2E,1<br />
return<br />
Test2 incf 0x2D,1<br />
return<br />
Test3 incf 0x2C,1<br />
return<br />
Test4 incf 0x2B,1<br />
return<br />
Test5 incf 0x2A,1<br />
return<br />
Test6 incf 0x29,1<br />
return<br />
Test7 incf 0x28,1<br />
return<br />
Test8 incf 0x27,1<br />
return<br />
Test9 incf 0x26,1<br />
return<br />
TestA incf 0x25,1<br />
return<br />
TestB incf 0x24,1<br />
return<br />
TestC incf 0x23,1<br />
return<br />
TestD incf 0x22,1<br />
return<br />
TestE incf 0x21,1<br />
return<br />
TestF incf 0x20,1<br />
goto TestF<br />
Init ;--------------;<br />
; eigener Code ;<br />
;--------------;<br />
goto @Init ; * initialisiere "PIC Trainer" <br />
; hier endet das gesamte ASM Programm<br />
include "PIC Trainer.asm" <br />
end<br />
<br />
Das Programm "PIC Trainer" kann auch für grösseres Display (mehr Register und Testx) modifiziert werden. Als Beispiel ein Quellcode für 4x16 Display (0 bis 1Fh Register/Test):<br />
<br />
; PIC Trainer32.asm<br />
#define @Ftst @Int,7 ; Variablen definieren (Register benennen)<br />
@SWR equ 0x4F<br />
@SSR equ 0x4E<br />
@FSR equ 0x4D<br />
@Tmp equ 0x4C<br />
@Tmp1 equ 0x4B<br />
@Tmp2 equ 0x4A<br />
@Tmp3 equ 0x49<br />
@Int equ 0x48<br />
@LPC equ 0x47<br />
@LPC1 equ 0x46<br />
@LPC2 equ 0x45<br />
@Tasten equ 0x44<br />
ORG 0x0004 ; ISR<br />
bcf INTCON,GIE ; interrupts sperren<br />
movwf @SWR ; W-Register sichern<br />
swapf STATUS,0 ; STATUS Register<br />
movwf @SSR ; sichern<br />
movf FSR,0 ; FSR Register<br />
movwf @FSR ; sichern<br />
btfss INTCON,T0IF ; Timer0 interrupt Flag prüfen<br />
goto @IntTest ; wenn nicht gesetzt, eventuell andere prüfen<br />
bcf INTCON,T0IF ; Timer0 interrupt Flag löschen<br />
incf @Int,1 ; Zähler erhöhen<br />
btfss @Int,2 ; prüfen, ob schon 4. Timer0 interrupt<br />
goto $+3 ; wenn nicht, zum "@IntTest" springen<br />
clrf @Int ; Zähler löschen<br />
call @ ; Anzeigen der Registerinhalte aufrufen<br />
@IntTest ;**************;<br />
; eigener Code ;<br />
;**************;<br />
movf @FSR,0 ; FSR Register<br />
movwf FSR ; wiederherstellen<br />
swapf @SSR,0 ; STATUS Register<br />
movwf STATUS ; wiederherstellen<br />
movf @SWR,0 ; W-Register wiederherstellen<br />
retfie ; zurück ins Programm springen, interrupts erlauben <br />
ORG 0x0358 ; diese Adresse kann gleich max.Adresse - A7h sein<br />
@Trainer btfss @Ftst<br />
goto @Trainer<br />
bcf @Ftst<br />
call @Test<br />
call @Zeigen<br />
goto @Trainer<br />
ORG 0x0338<br />
@ bsf STATUS,RP0 ; @DT und @CK als Eingänge<br />
bsf @TDT<br />
bsf @TCK<br />
bcf STATUS,RP0<br />
clrf @Tasten ; Zustände von @DT und @CK ins @Tasten kopieren<br />
btfsc @CK<br />
bsf @Tasten,0<br />
btfsc @DT<br />
bsf @Tasten,1<br />
movf @Tasten,1 ; Tasten auswerten <br />
btfsc STATUS,Z<br />
bsf @Ftst ; setze Flag, wenn Taste3 gedrückt<br />
movf @Tasten,0<br />
sublw 1<br />
btfsc STATUS,Z<br />
call @IncLPC ; nächstes Nibble, wenn T2 gedrückt<br />
movf @Tasten,0<br />
sublw 2<br />
btfsc STATUS,Z<br />
call @Inc ; nibble erhöhen, wenn T1 gedrückt<br />
@Zeigen call @Out<br />
movlw 0x20 ; Anzeigen der Registerinhalte<br />
movwf FSR <br />
movlw 0x0C ; Cursor aus<br />
call @Cmd<br />
call @1st<br />
call @Line<br />
call @2nd<br />
call @Line<br />
call @3rd<br />
call @Line<br />
call @4th<br />
call @Line<br />
movlw 0x0E ; Cursor an<br />
call @Cmd<br />
swapf @LPC,0 ; Display Adresse berechnen und Cursor plazieren<br />
andlw 0x0F<br />
movwf @LPC2<br />
btfss STATUS,Z<br />
goto $+3<br />
movlw 0x80<br />
goto @AddLPC<br />
movf @LPC2,0<br />
sublw 1<br />
btfss STATUS,Z<br />
goto $+3<br />
movlw 0x30<br />
goto @AddLPC<br />
movf @LPC2,0<br />
sublw 2<br />
btfss STATUS,Z<br />
goto $+3<br />
movlw 0x70<br />
goto @AddLPC<br />
movf @LPC2,0<br />
sublw 3<br />
btfsc STATUS,Z<br />
movlw 0xA0<br />
@AddLPC addwf @LPC,0 <br />
call @Cmd<br />
return<br />
@Out bsf STATUS,RP0 ; @DT und @CK als Ausgänge<br />
bcf @TDT<br />
bcf @TCK<br />
bcf STATUS,RP0<br />
return<br />
@Test movlw 3 ; Test0-1F starten<br />
movwf PCLATH<br />
movf @LPC1,0<br />
andlw 0x1F<br />
goto @TestTab<br />
@Inc movlw 0x20 ; Register Wert erhöhen<br />
movwf FSR<br />
movf @LPC1,0<br />
addwf FSR,1<br />
btfsc @LPC,0 <br />
goto @IncLN<br />
@IncHN movlw 0x10 ; High Nibble erhöhen<br />
addwf INDF,1<br />
return<br />
@IncLN incf INDF,1 ; Low Nibble erhöhen<br />
movf INDF,0<br />
andlw 0x0F<br />
btfss STATUS,Z<br />
return<br />
movlw 0x10<br />
subwf INDF,1<br />
return<br />
@IncLPC incf @LPC,1 ; Zähler der Position in der Zeile erhöhen<br />
movf @LPC,0<br />
sublw 0x40<br />
btfsc STATUS,Z<br />
clrf @LPC<br />
movf @LPC,0<br />
movwf @LPC1<br />
rrf @LPC1,1<br />
return<br />
@Line movlw 8 ; Zeile an Display schicken<br />
movwf @Tmp<br />
movf INDF,0<br />
call @Val<br />
incf FSR,1<br />
decfsz @Tmp,1<br />
goto $-4<br />
return<br />
@1st movlw 0x80 ; Adresse der 1. Zeile an Display schicken<br />
goto @Cmd<br />
@2nd movlw 0xC0 ; Adresse der 2. Zeile an Display schicken<br />
goto @Cmd<br />
@3rd movlw 0x90 ; Adresse der 3. Zeile an Display schicken<br />
goto @Cmd<br />
@4th movlw 0xD0 ; Adresse der 4. Zeile an Display schicken<br />
@Cmd bcf STATUS,C ; Befehl an Display schicken<br />
goto @Send<br />
@Val movwf @Tmp1 ; Zahl (00-FF) an Display schicken<br />
swapf @Tmp1,0<br />
call @Num<br />
movf @Tmp1,0<br />
@Num andlw 0x0F ; Ziffer (0-F) an Display schicken<br />
movwf @Tmp2 <br />
movlw 0x0A<br />
subwf @Tmp2,0<br />
btfsc STATUS,C<br />
addlw 7<br />
addlw 0x3A<br />
bsf STATUS,C<br />
@Send movwf @Tmp2<br />
movlw 9 ; Byte + RS aus @Tmp2 (9 Bits) an Display schicken<br />
movwf @Tmp3<br />
@Ser bcf @CK<br />
bcf @DT<br />
btfsc @Tmp2,7<br />
bsf @DT<br />
bsf @CK<br />
rlf @Tmp2,1<br />
decfsz @Tmp3,1<br />
goto @Ser<br />
bcf @DT ; setze Display<br />
bsf @DT ; Enable<br />
bcf @CK ; lösche<br />
bcf @DT ; Display<br />
bsf @DT ; Enable<br />
bcf @DT<br />
return<br />
@Init bsf INTCON,GIE ; alle interrupts erlauben<br />
bsf INTCON,T0IE ; Timer0 interrupt erlauben<br />
bcf INTCON,T0IF ; Timer0 interrupt Flag löschen<br />
bsf STATUS,RP0 ; auf Bank 1 umschalten<br />
movf OPTION_REG,0 ; OPTION_REG in W-Register laden<br />
andlw 0xC0 ; Timer0 konfigurieren<br />
iorlw 7 ; Prescaler 256:1 <br />
movwf OPTION_REG ; ins OPTION_REG schreiben<br />
bcf STATUS,RP0 ; zurück auf Bank 0 umschalten<br />
clrf @Int<br />
clrf @LPC<br />
clrf @LPC1<br />
call @Out<br />
movlw 0x38 ; Display vom PIC Miniterminal initialisieren<br />
call @Cmd<br />
movlw 0x0C<br />
call @Cmd<br />
movlw 6<br />
call @Cmd<br />
return<br />
org 0x3DF ; diese Adresse kann gleich max.Adresse - 20h sein<br />
@TestTab addwf PCL,1 ; Sprungtabelle<br />
goto Test0<br />
goto Test1<br />
goto Test2<br />
goto Test3<br />
goto Test4<br />
goto Test5<br />
goto Test6<br />
goto Test7<br />
goto Test8<br />
goto Test9<br />
goto TestA<br />
goto TestB<br />
goto TestC<br />
goto TestD<br />
goto TestE<br />
goto TestF<br />
goto Test10<br />
goto Test11<br />
goto Test12<br />
goto Test13<br />
goto Test14<br />
goto Test15<br />
goto Test16<br />
goto Test17<br />
goto Test18<br />
goto Test19<br />
goto Test1A<br />
goto Test1B<br />
goto Test1C<br />
goto Test1D<br />
goto Test1E<br />
goto Test1F<br />
end<br />
<br />
Es können eigene Namen für mit "PIC Trainer" erstellten und geprüften UPs vewendet werden. Sie müssen nur im eigenem Programm mit "#define" den entsprechenden TestX zugewiesen werden. Aus unerklärlichen Gründen funktioniert es aber nur, wenn die Definitionen als erste im Quellcode sind (MPASM Falle?).<br />
<br />
#define Test0 Init<br />
#define Test1 Lesen<br />
#define Test2 Schreiben<br />
usw.<br />
<br />
Somit wird z.B. das UP "Lesen" aufgerufen wenn die Taste T3 gedrückt wird und der Cursor auf der Register-Adresse "21" steht. Es wird empfohlen, eine Tabelle mit UPs-Namen und den enstprechenden Nummern (0 bis Fh) zum dessen Aufrufen, sich zu erstellen.<br />
<br />
Für alle definierte, aber noch nicht existierende UPs, sollten "return" Befehle angewendet werden, um einen Programmabsturtz durch versehentliches Aufrufen von dennen, zu vermeinden. Zum Beispiel:<br />
<br />
#define TestD RAMClr<br />
#define TestE RAMSet<br />
#define TestF KeyTst<br />
<br />
RAMClr return<br />
RAMSet return<br />
KeyTst return<br />
<br />
Wenn ein UP (z.B. eine Warteschleife) läuft, kann ein nächstes erst nach seiner Beendigung gestartet werden.<br />
<br />
Wenn ein UP in einer endloser Schleife "hängen" bleibt, dann kann nur anderes UP nicht gestartet werden. Die alle andere Funktionen des Programms werden nicht gestört. In dem Testprogramm wurde absichtlich TestF als endlose Schleife erstellt, um das Funktionieren des "PIC Trainer"s in diesem Fall zu zeigen.<br />
<br />
== PIC Profiler ==<br />
<br />
Der "PIC Profiler" ermöglicht das Messen von Ausführungszeit eines ASM UPs, also Programm-Fragments das mit "return" endet. Es wird die gesamte Ausführungszeit gemessen mit "call" (2 Takten) und "return" (2 Takten) inklusive. Um ein HP messen zu können, muss es ins UP umgewandelt werden, wie im [[#Unterprogramm|Unterprogramm]] erklärt. <br />
<br />
Zum Messen wurde Timer0 benutzt, der um 2 Byte Zähler erweitert wurde. Somit beträgt die maximale messbare Ausführungszeit FFFFFFFFh = 4 294 967 295 Prozessortakten, was mehr als einer Stunde beim 4 MHz Quarz entspricht. Bis FFFFh ist das Messergebniss genau, weiter wurde es nicht getestet. <br />
<br />
Um die Durchführung der Messung zu ermöglichen, wird das "goto Haupt" für den MPASM mit einem Semikolon augeblendet und "goto @Profiler" eingeschrieben. Nach dem Einschalten wird anstatt ins "Haupt" zum "@Profiler" gesprungen. Der "@Profiler" misst die Ausführungszeit nur einmal, da er mit "sleep" endet. Wenn endlose Messung gewünscht wird, muss der Befehl "sleep" mit "goto @Profiler" ersetzt werden. Wegen ISR vom "PIC Profiler" darf das Programm, in dem gemessen wird, erst ab der Adresse 0x0013 anfangen.<br />
<br />
Zum Darstellen des Messergebnisses kann entweder schon im zu messenden Programm vorhandenes Display bzw. "PIC Miniterminal" benutzt werden. Das hex Ergebniss befindet sich in den Register @A3 (MSB), @A2, @A1 und @A0 (LSB).<br />
<br />
Die Version, die das im Programm vorhandenes Display benutzt, braucht 46 Speicherstellen im Programmspeicher und 8 Register im RAM.<br />
<br />
; PIC Profiler1.asm<br />
@SWR equ 0x4F ; Variablen deklarieren (Register benennen)<br />
@SSR equ 0x4E<br />
@A3 equ 0x4D<br />
@A2 equ 0x4C<br />
@A1 equ 0x4B<br />
@A0 equ 0x4A<br />
@ATmp equ 0x49<br />
@RTmp equ 0x48<br />
ORG 0x0004 ; ISR (interrupt service routine)<br />
bcf INTCON,GIE ; interrupts sperren<br />
bcf INTCON,T0IF ; Timer0 interrupt flag löschen<br />
movwf @SWR ; W-Register sichern<br />
swapf STATUS,0 ; STATUS Register<br />
movwf @SSR ; sichern<br />
movlw 1<br />
addwf @A2,1 ; erhöhe @A2, wenn Timer0 überlaufen ist<br />
btfsc STATUS,C<br />
incf @A3,1 ; erhöhe @A3, wenn @A2 überlaufen ist<br />
swapf @SSR,0 ; STATUS Register<br />
movwf STATUS ; wiederherstellen<br />
movf @SWR,0 ; W-Register wiederherstellen<br />
clrf TMR0 ; Timer0 und Prescaler löschen<br />
retfie ; zurück ins gemessene UP, Interrupts erlauben<br />
org 0x03E0 ; diese Adresse kann gleich max.Adresse - 1Fh sein<br />
@Profiler clrf @A3 ; Messregister löschen<br />
clrf @A2<br />
bsf STATUS,RP0 ; Bank1<br />
movlw 0xC7 ; interner Takt<br />
movwf OPTION_REG ; Timer0 konfigurieren<br />
bcf STATUS,RP0 ; Bank 0<br />
movlw 0xE0 ; nur Timer0 Interrupt erlaubt<br />
movwf INTCON ; Interrupts konfigurieren<br />
clrf TMR0 ; Timer0 und Prescaler löschen<br />
;............................................................................<br />
call Messen ; dieses UP wird gemessen<br />
;............................................................................<br />
bsf STATUS,RP0<br />
bsf OPTION_REG,T0CS ; "stopp" Timer0<br />
bcf STATUS,RP0 <br />
movf TMR0,0 ; TMR0 ins Register<br />
movwf @A1 ; @A1 kopieren<br />
movwf @RTmp<br />
clrf @ATmp ; Prescaler ins Register @A0 kopieren<br />
incf @ATmp,1<br />
bsf STATUS,RP0<br />
bsf OPTION_REG,T0SE<br />
bcf OPTION_REG,T0SE<br />
bcf STATUS,RP0<br />
movf TMR0,0<br />
subwf @RTmp,0<br />
btfsc STATUS,Z<br />
goto $-8<br />
comf @ATmp,1<br />
incf @ATmp,0<br />
movwf @A0<br />
decf @A0,1<br />
call Ausgeben ; Messergebniss aus @A3, @A2, @A1 und @A0<br />
; auf einem Display anzeigen (eigener Code)<br />
sleep<br />
end<br />
<br />
Die Version, die zur Ausgabe den "PIC Miniterminal" benutzt, belegt 94 Speicherstellen im Programmspeicher und 12 Register im RAM.<br />
<br />
; PIC Profiler2.asm<br />
@SWR equ 0x4F ; Variablen deklarieren (Register benennen)<br />
@SSR equ 0x4E<br />
@A3 equ 0x4D<br />
@A2 equ 0x4C<br />
@A1 equ 0x4B<br />
@A0 equ 0x4A<br />
@ATmp equ 0x49<br />
@RTmp equ 0x48<br />
@Tmp equ 0x47 ; * Variablen für "PIC Miniterminal" definieren<br />
@Tmp1 equ 0x46<br />
@Tmp2 equ 0x45<br />
@Tmp3 equ 0x44 <br />
ORG 0x0004 ; ISR (interrupt service routine)<br />
bcf INTCON,GIE ; interrupts sperren<br />
bcf INTCON,T0IF ; Timer0 interrupt flag löschen<br />
movwf @SWR ; W-Register sichern<br />
swapf STATUS,0 ; STATUS Register<br />
movwf @SSR ; sichern<br />
movlw 1<br />
addwf @A2,1 ; erhöhe @A2, wenn Timer0 überlaufen ist<br />
btfsc STATUS,C<br />
incf @A3,1 ; erhöhe @A3, wenn @A2 überlaufen ist<br />
swapf @SSR,0 ; STATUS Register<br />
movwf STATUS ; wiederherstellen<br />
movf @SWR,0 ; W-Register wiederherstellen<br />
clrf TMR0 ; Timer0 und Prescaler löschen<br />
retfie ; zurück ins gemessene UP, Interrupts erlauben<br />
org 0x03B0 ; diese Adresse kann gleich max.Adresse - 4Fh sein<br />
@Profiler movlw 0x38 ; Display vom "PIC Miniterminal" initialisieren<br />
call @Cmd<br />
movlw 0x0C<br />
call @Cmd<br />
movlw 6<br />
call @Cmd <br />
clrf @A3 ; Messregister löschen<br />
clrf @A2<br />
bsf STATUS,RP0 ; Bank1<br />
movlw 0xC7 ; interner Takt<br />
movwf OPTION_REG ; Timer0 konfigurieren<br />
bcf STATUS,RP0 ; Bank 0<br />
movlw 0xE0 ; nur Timer0 Interrupt erlaubt<br />
movwf INTCON ; Interrupts konfigurieren<br />
clrf TMR0 ; Timer0 löschen<br />
;............................................................................<br />
call Messen ; dieses UP wird gemessen<br />
;............................................................................<br />
bsf STATUS,RP0<br />
bsf OPTION_REG,T0CS ; "stopp" Timer0<br />
bcf STATUS,RP0 <br />
movf TMR0,0 ; TMR0 ins Register<br />
movwf @A1 ; @A1 kopieren<br />
movwf @RTmp<br />
clrf @ATmp ; Prescaler ins Register @A0 kopieren<br />
incf @ATmp,1<br />
bsf STATUS,RP0<br />
bsf OPTION_REG,T0SE<br />
bcf OPTION_REG,T0SE<br />
bcf STATUS,RP0<br />
movf TMR0,0<br />
subwf @RTmp,0<br />
btfsc STATUS,Z<br />
goto $-8<br />
comf @ATmp,1<br />
incf @ATmp,0<br />
movwf @A0<br />
decf @A0,1<br />
call @1st ; Messergebniss aus @A3, @A2, @A1 und @A0<br />
; auf "PIC Miniterminal" ausgeben<br />
movf @A3,0<br />
call @Val<br />
movf @A2,0<br />
call @Val<br />
movf @A1,0<br />
call @Val<br />
movf @A0,0<br />
call @Val<br />
sleep<br />
@1st movlw 0x80 ; Adresse der 1. Zeile an Display schicken<br />
@Cmd bcf STATUS,C ; Befehl an Display schicken <br />
goto @Send<br />
@Val movwf @Tmp1 ; Zahl (00-FF) an Display schicken<br />
swapf @Tmp1,0<br />
call @Num<br />
movf @Tmp1,0<br />
@Num andlw 0x0F ; Ziffer (0-F) an Display schicken<br />
movwf @Tmp2 <br />
movlw 0x0A <br />
subwf @Tmp2,0<br />
btfsc STATUS,C<br />
addlw 7<br />
addlw 0x3A<br />
bsf STATUS,C<br />
@Send movwf @Tmp2<br />
movlw 9 ; ein Byte + RS aus @Tmp2 (9 Bits) an Display schicken<br />
movwf @Tmp3<br />
@Ser bcf @CK<br />
bcf @DT<br />
btfsc @Tmp2,7<br />
bsf @DT<br />
bsf @CK<br />
rlf @Tmp2,1<br />
decfsz @Tmp3,1<br />
goto @Ser<br />
bcf @DT ; setze Display<br />
bsf @DT ; Enable<br />
bcf @CK ; lösche<br />
bcf @DT ; Display<br />
bsf @DT ; Enable<br />
bcf @DT<br />
return <br />
end <br />
<br />
Das Programm "@Profiler" kann für PICs, die mehr Programmspeicher besitzen, mit entsprechender "org" Direktive höher verschoben werden. Entsprechend können auch alle im Programm benutzte Register höchstmögliche Adressen im RAM haben (z.B. @SWR equ 0x7F anstatt 0x4F).<br />
<br />
Als praktisches Beispiel wurde das UP "Warten" vom "Erstes.asm" für den PIC16F84A gemessen und das Ergebniss auf dem "PIC Miniterminal" dargestellt.<br />
<br />
; Erstesp.asm<br />
list P=16F84A ; Prozessor definieren<br />
include "P16F84A.inc" ; 4.000 MHz<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC<br />
#define Messen Warten ; definiere das zu messende UP<br />
#define @DT PORTB,7 ; * definiere Anschlüsse @DT und @CK<br />
#define @CK PORTB,6 ; * für "PIC Miniterminal"<br />
#define _T1 PORTB,5 ; Portpins benennen<br />
#define _T2 PORTB,4<br />
#define _L1 PORTB,3<br />
#define _L2 PORTB,2<br />
#define _L3 PORTB,1<br />
#define _L4 PORTB,0<br />
P0 equ 0x20 ; Variablen definieren (Register benennen)<br />
P1 equ 0x21<br />
P2 equ 0x22<br />
org 0x0000 ; hier fängt das ASM Programm in dem gemessen wird an <br />
call Init ; rufe UP Init (Initialisierung) auf<br />
;goto Haupt <br />
goto @Profiler <br />
org 0x0013 ; hier fängt das Program, in dem gemessen wird, an<br />
Haupt btfsc _T1 ; prüfe, ob Taster T1 gedrückt ist, wenn ja,<br />
; überspringe "goto T2Test"<br />
goto T2Test ; wenn nicht, springe zu T2Test<br />
bsf _L1 ; schalte _L1 an<br />
call Warten ; warte ca. 0,4 s (400 000 Prozessortakten)<br />
bcf _L1 ; schalte _L1 aus<br />
bsf _L2 ; schalte _L2 an<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus<br />
bsf _L3 ; schalte _L3 an<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus<br />
bsf _L4 ; schalte _L4 an<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus<br />
T2Test btfsc _T2 ; prüfe, ob Taster T2 gedrückt ist, wenn ja,<br />
; überspringe "goto Haupt"<br />
goto Haupt ; Wenn nicht, springe zu Haupt<br />
bsf _L4 ; schalte _L4 an<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus<br />
bsf _L3 ; schalte _L3 an<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus<br />
bsf _L2 ; schalte _L2 an<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus<br />
bsf _L1 ; schalte _L1 an<br />
call Warten ; warte<br />
bcf _L1 ; schalte _L1 aus<br />
goto Haupt ; springe zu Haupt<br />
Warten movlw 2 ; schreibe "2" für ca. 0,4 s<br />
movwf P2 ; ins Register P2<br />
clrf P1 ; lösche Register P1 (schreibe "0" für 256 Durchläufe) <br />
clrf P0 ; lösche Register P0 (schreibe "0" für 256 Durchläufe)<br />
decfsz P0,1 ; dekrementiere P0, überspringe nächsten Befehl bei P0=0<br />
goto $-1 ; gehe zur voherigen Adresse (Befehl "decfsz P0") <br />
decfsz P1,1 ; dekrementiere P1, überspringe nächsten Befehl bei P1=0 <br />
goto $-4 ; gehe zur aktueller Adresse - 4 (Befehl "clrf P0")<br />
decfsz P2,1 ; dekrementiere P2, überspringe nächsten Befehl bei P2=0<br />
goto $-7 ; gehe zur aktueller Adresse - 7 (Befehl "clrf P1")<br />
return ; springe zurück zum Aufrufer ("Haupt"),<br />
Init clrf PORTB ; lösche Port (setze alle werdende Ausgänge auf "0")<br />
bsf STATUS,RP0 ; auf Bank1 umschalten<br />
bcf OPTION_REG,7 ; aktiviere pull-ups<br />
movlw 0x30 ; definiere PortB: Pins B5 und B4 als Eingänge<br />
; und die restlichen als Ausgänge (00110000b) <br />
movwf TRISB ; schreibe in TRIS Register<br />
bcf STATUS,RP0 ; auf Bank0 umschalten<br />
return<br />
; hier endet das ASM Programm in dem gemessen wird<br />
include "PIC Profiler2.asm" <br />
end<br />
<br />
[[Kategorie:Microcontroller]]<br />
[[Kategorie:Software]]<br />
[[Category:PIC]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=PIC16F87x_Einf%C3%BChrung&diff=23463PIC16F87x Einführung2013-12-21T17:16:27Z<p>Besserwessi: removed missleading resolution (1/16 - prescaler does not increase resolution)</p>
<hr />
<div>==PIC16F87x Einführung==<br />
<br />
Der bekannteste und noch immer weit verbreitete PIC ist immer noch der PIC16F84. Es gibt, besonders im englischsprachigen Raum, zahllose Anwendungs-Beispiele in vielen Foren und Homepages.<br />
<br />
Hier soll eine Gruppe der (grösseren) Nachfolge-Modelle vorgestellt werden, der<br />
*PIC16F873 <br />
*PIC16F876 <br />
*PIC16F874<br />
*PIC16F877<br />
Sie sind in den üblichen Bauformen PDIP, SOIC, PLCC und QFP im Handel und unterscheiden sich in der Ausstattung, haben aber im Wesentlichen die gleichen Eigenschaften. (Es gibt nur ein Datasheet für alle 4). Es können alle Programm-Sourcen, die für den PIC16F84 geschrieben wurden, mit einer einfachen Compilierung sofort lauffähig gemacht werden (es muss natürlich der Prozessortyp geändert werden).<br />
<br />
[[Bild:picfeat.jpg|center|framed|quelle:Microchip]]<br />
<br />
===Peripheral Features===<br />
* Timer0: 8-bit timer/counter with 8-bit prescaler<br />
* Timer1: 16-bit timer/counter with prescaler, can be incremented during SLEEP vie external crystal/clock<br />
* Timer2: 8-bit timer/counter with 8-bit period register, prescaler and postscaler<br />
* Two Capture, Compare, PWM Modules<br />
** Capture/Compare is 16-bit, max. resolution is 200 nS<br />
** PWM max resolution is 10-bit<br />
* 10-bit multi-channel Analog-to-Digital converter<br />
* Synchronous Serial Port (SSP) with SPI tm (Master mode) and I2Ctm (Master/Slave)<br />
* Universale Synchronous Asynchronous Receiver Transmitter (USART/SCI) with 9-bit address detection<br />
* Parallel Slave Port (PSP) 8 -bits wide, with external /RD, /WR and /CS controls (40/44-pin only)<br />
* Brown-out detection circuitry for Brown-out Reset (BOR)<br />
<br />
===PC16F876===<br />
[[Bild:876pins.jpg|center|framed|quelle:Microchip]]<br />
<br />
===Pinout PC16F877===<br />
[[Bild:877pins.jpg|center|framed|quelle:Microchip]]<br />
<br />
<br />
{| {{Blauetabelle}}<br />
|'''OSC1/CLKIN''' <br />
|Quartz Input oder Input für externe Clock-Source. <br />
|-<br />
|'''OSC2/CLKOUT''' <br />
|Quartz Output. Im RC-Mode kann hier 1/4 Frequenz von OSC1 entnommen werden. <br />
|-<br />
|'''/MCLR /Vpp''' <br />
|Reset oder Programmierspannung (+12 V oder +5V). <br />
|-<br />
|'''RA0 / AN0''' <br />
|PORTA Bit 0 (TTL) oder Analog Input 0. <br />
|-<br />
|'''RA1 / AN1''' <br />
|PORTA Bit 1 (TTL) oder Analog Input 1. <br />
|-<br />
|'''RA2 / AN2 / Vref-''' <br />
|PORTA Bit 2 (TTL), Analog Input 2 oder Referenzspannung - <br />
|-<br />
|'''RA3 / AN3 / Vref+''' <br />
|PORTA Bit 3 (TTL), Analog Input 3 oder Referenzspannung +<br />
|-<br />
|'''RA4 / T0CKI / Vref-''' <br />
|PORTA Bit 4 (TTL mit ST), oder Clock-In für Timer0 timer/counter. '''Wenn Output, dann Open drain'''<br />
|-<br />
|'''RA5 / /SS / AN4''' <br />
|PORTA Bit 5 (TTL), Analog Input 4 oder Slave Select für SSP<br />
|-<br />
|'''RB0 / INT''' <br />
|PORTB Bit 0 (TTL) mit Schmitt-Trigger, wenn INT<br />
|-<br />
|'''RB1''' <br />
|PORTB Bit 1 (TTL)<br />
|-<br />
|'''RB2''' <br />
|PORTB Bit 2 (TTL)<br />
|-<br />
|'''RB3 / PGM ''' <br />
|PORTB Bit 3 (TTL) Bei Low-Voltage-Programming ist dieser Pin besetzt<br />
|-<br />
|'''RB4''' <br />
|PORTB Bit 4 (TTL) Interrupt-on-change möglich<br />
|-<br />
|'''RB5''' <br />
|PORTB Bit 5 (TTL) Interrupt-on-change möglich <br />
|-<br />
|'''RB6 / PGC ''' <br />
|PORTB Bit 6 (TTL) Interrupt-on-change möglich. CLOCK beim Programmieren u. Debug<br />
|-<br />
|'''RB7 / PGD ''' <br />
|PORTB Bit 7 (TTL) Interrupt-on-change möglich. DATA beim Programmieren u. Debug<br />
|-<br />
|'''RC0 / T1OSO/ T1CKI''' <br />
|PORTC Bit 0 (TTL/ST) Timer1 oscillator output oder clock input<br />
|-<br />
|'''RC1 / T1OSI/ CCP2''' <br />
|PORTC Bit 1 (TTL/ST) Timer1 oscillator input oder CAP2 input od. COMP2/PWM2 output<br />
|-<br />
|'''RC2 / CCP1''' <br />
|PORTC Bit 2 (TTL/ST) CAP1 input od. COMP1/PWM1 output<br />
|-<br />
|'''RC3 / SCK / SCL''' <br />
|PORTC Bit 3 (TTL/ST) Clock in / Out für SSP oder I2C<br />
|-<br />
|'''RC4 / SDI / SDA''' <br />
|PORTC Bit 4 (TTL/ST) Data in (SSP) oder DATA für I2C<br />
|-<br />
|'''RC5 / SDO''' <br />
|PORTC Bit 5 (TTL/ST) Data out (SSP)<br />
|-<br />
|'''RC6 / TX / CK''' <br />
|PORTC Bit 6 (TTL/ST) USART-Async TX oder USART-Sync Clock<br />
|-<br />
|'''RC7 / RX / DT''' <br />
|PORTC Bit 7 (TTL/ST) USART-Async RX oder USART-Sync Data<br />
|-<br />
|'''RD0 - 7 / PSP0 - 7''' <br />
|PORTD Bit 0 - 7 (TTL/ST) Parallel Slave Port<br />
|-<br />
|'''RE0 / /RD / AN5''' <br />
|PORTE Bit 0 (TTL/ST) Parallel Slave Port READ oder analog 5<br />
|-<br />
|'''RE1 / /WR / AN6''' <br />
|PORTE Bit 1 (TTL/ST) Parallel Slave Port WRITE oder analog 6<br />
|-<br />
|'''RE2 / /CS / AN7''' <br />
|PORTE Bit 2 (TTL/ST) Parallel Slave Port SELECT oder analog 7<br />
|-<br />
|}<br />
----<br />
<br />
===Besonderheiten Peripherie===<br />
Bei den Geräten wie ADC, TIMER, PWM, USART, INT0 unterscheiden sich diese PIC kaum von anderen Controllern. Vieleicht ein paar Bemerkungen<br />
====CPU / Clock====<br />
Man darf sich von den angegebenen 20 MHZ nicht täuschen lassen. Da diese PIC nicht mit ähnlichen Tricks wir die AVRs arbeiten, brauchen die 4 RISC-Cycles auch wirklich 4 Takte. Daher muß man beim Konzipieren von 5 MHZ ausgegehen. <br />
[[Bild:pic_qcyc.jpg|center|framed|quelle:Microchip]]<br />
<br />
====Programmierung====<br />
Es gibt zwei Arten<br />
*HVP "High Voltage Programming". Anders als bei den AVRs muß die 12 Volt Programmierspannung extern erzeugt werden <br />
*LVP "Low Voltage Programming". Hier braucht man keine 12 Volt, allerdings geht dann ein Pin verloren (RB3 /PGM).<br />
====USART====<br />
Diese PIC können die RS232 auch im Synchronmode (fremdgetaktet) betreiben, allerdings nur als Receiver. <br />
====I2C / SPI====<br />
Hier gibt es nur entweder-oder. Für den I2C-Bus wird die gleiche Hardware benutzt wie für das SPI<br />
====PSP / Parallel Slave Port====<br />
Sowas gibt's bei den AVRs nicht. Hier kann ein anderer (Slave) Chip 8-Bit parallel in den Controller reinstellen oder abholen<br />
====Interrupt on change====<br />
Bei den oben bezeichneten Pins kann ein gemeinsamer Interrupt bei jeder Pegel-Änderung ausgelöst werden.<br />
<br />
===Besonderheiten CPU und Memory===<br />
====Memory====<br />
Es gibt keine getrennten Bereiche für GPR, SFR und SRAM. Alles zusammen befindet sich in der "Register File", die einzelnen Register werden individuell gemappt. Dabei heißt es vorsichtig zu sein, denn dieses Bereich ist in 4 Banks (PIC16F877) unterteilt, und je nach effektiver Zieladresse muss man die Bank-Switch-Bits richtig setzen. Einen geschlossenen Speicherbereich, wie man es vielleicht gewohnt ist, gibt es hier nicht. <br />
Da es also eigentlich nur Register und keine Memory gibt, entfallen das Load & Store, wie es z.B. bei den AVRs erfordelich ist, Alle Befehle gehen an jeder möglichen Adresse<br />
[[Bild:877regis.jpg|center|framed|quelle:Microchip]]<br />
====Flash====<br />
Auch hier ist das Bereich in Pages geteilt, die man zur Adressierung ebenfalls umschalten muß. <br />
<br />
[[Bild:877mem.jpg|center|framed|quelle:Microchip<br>Auch hier ist das Bereich in Pages geteilt, die man zur Adressierung ebenfalls umschalten muß. ]]<br />
<br />
====Stack====<br />
Der Stack + Stackpointer sind isoliert gehalten, es gibt keinen von aussen zugänglichen Stack. Der Stack Pointer ist festgelegt mit maximal 8 Leveln, dann erfolgt ein Wraparound mit schwer beherrschbaren Effekten. <br />
====Pointer Register====<br />
Es gibt nur EIN Register, mit dem man indirekt adressieren kann, wiederum mit eigenen Bank-Select-Bits. <br />
====CPU / Instruction Set====<br />
Der Hersteller hat nur 35 RISC Befehle bereitgestellt (AVR: 96). Es ist nun nicht so, daß der PIC irgendwas von den üblichen Befehlen nicht könnte. Aber im Gegensatz zu den AVRs, wo viele Befehle verschieden heissen, aber das gleiche tun, verfolgt Microchip die Strategie des Minimalismus. Es gibt zum Beispiel keinen eigenen Vergleichsbefehl.<br />
Man muß das tun, was bei einem AVR im Grunde auch geschieht: Man subtrahiert, ohne das Ergebnis zu speichern. Übrig bleiben nur die Statusbits. <br />
[[Bild:877cpu.jpg|center|framed|quelle:Microchip]]<br />
====Interrupts====<br />
Der PIC hat nur einen Interrupt Vector. Welches von den Geräten tatsächlich ausgelöst hat, muß der User den einzelnen Geräte-Flags entnehmen. <br />
<br />
<br />
<br />
<br />
====Autor/en====<br />
* PicNick<br />
<br />
<br />
[[Kategorie:Microcontroller]]<br />
[[Kategorie:PIC]]<br />
<br />
===Weblinks===<br />
* [http://pic-projekte.de/pic_tutorial.html Tutorial für PIC(18) Microcontroller]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Filter_(Elektronik)&diff=23387Filter (Elektronik)2013-12-12T13:56:53Z<p>Besserwessi: /* Passiv */</p>
<hr />
<div>= Wozu benutzt man Filter? =<br />
<br />
Für Filter gibt es eine ganze Menge Anwendungen.<br />
<br />
Als erstes Beispiel nehmen wir uns mal einen Verstärker her, einen einfachen. Einen Transistor in Emitterschaltung zum Beispiel. Am Eingang dieser Schaltung liegt die Basis des Transistors, beschaltet mit einem Vorwiderstand von Vcc aus. Dieser dient dazu, den Arbeitspunkt des Transistors festzulegen und "zieht" die Basis des Transistors zum Beispiel auf 0,70 Volt.<br />
Was passiert wenn man nun ein Signal anlegt, beispielsweise ein Audiosignal, welches zwischen 0,1 Volt und -0,1 Volt schwingt? Die Basis wird auf eben diese (im Mittel) 0 Volt heruntergezogen, und der Strom, der eigentlich für die Basis vorgesehen war, fließt nun einfach in die Signalquelle ab.<br><br />
Hier schafft ein Entkopplungskondensator, den man zwischen Signalquelle und Basis des Transistors schaltet, Abhilfe: Die Spannung zwischen dessen Anschlüssen steigt durch den abfließenden Strom langsam an. Die Spannung an der zum Transistor gewandten Seite steigt also an, und irgendwann ist die Spannung soweit gestiegen, dass durch die Basis des Transistors wieder der entsprechende Strom fließen kann. Durch den Kondensator fließt dann kein Gleichstrom mehr.<br />
Die Wechselspannung, in diesem Fall das Audiosignal, wird dagegen durchgelassen. Dabei fließt nämlich während der positiven Halbwelle Strom in die Schaltung herein, bei der negativen Halbwelle des Signals dagegen fließt der Strom wieder heraus. Diese Strompulse sind so kurz, dass der Kondensator seine Spannung dabei kaum ändert, sie werden also fast ungehindert durchgelassen.<br />
<br />
Als zweites Beispiel nehmen wir uns nochmal Audio-Kram her: Wir haben nen Tieftöner, den wir an unsere Stereoanlage anschließen wollen. Die tiefen Frequenzen soll er ruhig wiedergeben - die hohen Frequenzen jedoch nicht, denn für die sind die Hochtöner zuständig.<br><br />
Eine Spule, die hier in Reihe zum Lautsprecher geschaltet wird, erfüllt diese Bedingungen. Wenn ein Signal an die Kombination aus Lautsprecher und Spule angelegt wird, steigt der Strom, bedingt durch die Induktivität der Spule, nur langsam an. Bei tiefen Frequenzen fällt das kaum auf, denn eine Halbwelle ist lang genug, um den Strom durch den Lautsprecher und die Spule zu erhöhen. Bei hohen Frequenzen dagegen sind die Halbwellen so kurz, dass sich kein nennenswerter Stromfluss einstellen kann - die Signale werden von der Spule blockiert.<br />
<br />
Zur Glättung von Spannungen und / oder Strömen werden auch Filter eingesetzt. Eine Kombination aus Spule und Kondensator (LC-Filter) setzt man beispielsweise häufig vor der ADC-Versorgung von Mikrocontrollern ein. Diese Filter filtern doppelt: Wenn die Versorgungsspannung plötzlich leicht abfällt, ändert sich zunächst der Strom in der Spule, aber wie gesagt wurde, durch die Induktivität verlangsamt. Wenn der Strom etwas abgefallen ist, wird das zweite Filterelement - der Kondensator - aktiv, denn durch ihn muss zuerst ein Strom fließen, damit sich die Spannung am Ausgang der Schaltung ändern kann.<br />
<br />
----<br />
<br />
*Phantomspeisung (gecancelt: Gebräuchliche Systeme wie PoE brauchen dafür afaik keine echten Filter.)<br />
<br />
= Arten von Filtern =<br />
<br />
Nun gibt es aber zig verschiedene Möglichkeiten, Filter aufzubauen, noch dazu ist jede nur für spezielle Anwendungen geeignet.<br><br />
Man unterteilt Filter zunächst in aktive Filter und passive Filter: Passive Filter sind solche, die nur aus passiven Bauteilen bestehen. Dazu gehören die oben angesprochenen Kondensatoren, Spulen und Widerstände, aber auch Quarze.<br />
Aktive Filter enthalten als aktive Komponente meist einen Operationsverstärker. Der Vorteil liegt auf der Hand: Der Ausgang des OpAmps ist auch der Ausgang des Filters, der damit auch ruhig bis einige Milliampere belastet werden kann - bei passiven Filtern ist dies meistens nicht ohne weiteres möglich, die Ausgangsspannung würde sich dabei ändern. Der Nachteil sollte auch klar sein: Ein OpAmp, und jede andere aktive Komponente auch, braucht eine Versorgungsspannung. Für eine Frequenzweiche in einer passiven Standbox (d.h. der Verstärker ist nicht integriert) sind aktive Filter dadurch ungeeignet.<br />
<br />
[[Bild:Filterordnungen.gif|thumb|Beispiele für verschiedene Filterordnungen]]<br />
Weiterhin unterscheidet man Filter nach verschiedenen Ordnungen. Die Ordnung spiegelt dabei die Anzahl der frequenzabhängigen Bauteile wieder. Ein Filter höherer Ordnung, beispielsweise die oben beschriebene Kombination aus Spule und Kondensator, hat eine bessere Trennschärfe. Das bedeutet, dass der Abstand zwischen Frequenzen, die durchgelassen werden, und Frequenzen, die gesperrt werden, kleiner ist.<br><br />
Nun gut, im Bild rechts sieht man schon dass das so nicht ganz stimmt: Jede Frequenz wird noch irgendwie durchgelassen, aber die Dämpfung, die in dB gemessen wird, wird immer stärker. Man müsste also eigentlich sagen, dass der Abstand der Frequenzen, die als durchgelassen '''bezeichnet''' werden können und der Frequenzen, die als gesperrt '''bezeichnet''' werden können, kleiner ist. Ab welcher Dämpfung eine Frequenz als gesperrt oder durchgelassen bezeichnet werden kann, das hängt wiederum von der Anwendung ab. In Audioanwendungen reicht ein Filter 1. Ordnung meist völlig aus, aber um beispielsweise ein PWM-Signal in eine Gleichspannung zu wandelt, muss eine bestimmte Frequenz unbedingt möglichst schwach gedämpft durchgelassen werden, während die PWM-Frequenz möglichst stark gedämpft werden soll. Deshalb nutzt man hierfür vorwiegend Filter höherer Ordnungen.<br />
<br />
[[Bild:Filtervergleich.gif|thumb|Beispiele für verschiedene Filtercharakteristiken]]<br />
Zuletzt unterteilt man Filter noch in verschiedene Charakteristiken, namentlich Tiefpässe, Hochpässe, Bandpässe und Bandsperren.<br />
*Tiefpässe lassen tiefe Frequenzen durch und sperren hohe Frequenzen. (siehe blaue Kurve im Bild.) Eine typische Anwendung wäre die Glättung eines Signals mithilfe einer Kombination aus Spule und Kondensator. <br />
*Hochpässe machen das Gegenteil: Sie sperren tiefe Frequenzen und lassen hohe Frequenzen durch. (siehe grüne Kurve im Bild.) Beispiele wären eine Gleichspannungsentkopplung oder eine Frequenzweiche für einen Hochtonlautsprecher.<br />
*Bandpässe sperren hohe und tiefe Frequenzen - Ein bestimmter Frequenzbereich wird jedoch durchgelassen. (siehe rote Kurve im Bild.) Eine Anwendung hierfür wäre eine Frequenzweiche vor einem Mitteltöner '''''(Sorry für die ganzen Beispiele aus dem Audiobereich, fällt vielleicht wem was besseres ein? Passiv-PFC würde mir einfallen, würde aber ne längere Erläuterung notwendig machen.)'''''<br />
*Bandsperren sind das Gegenstück zu den Bandpässen: Hohe und tiefe Frequenzen werden durchgelassen, ein bestimmter Frequenzbereich wird gesperrt. (siehe türkise Kurve im Bild.) Eine solche Schaltung kann beispielsweise verwendet werden, um die möglicherweise bei Datenübertragungen über Modem störenden Gebührenimpulse aus der Telefonleitung herauszufiltern.<br />
<br />
Wie man sieht, sind die Bezeichnungen so gut wie selbsterklärend.<br />
<br />
= Realisierung =<br />
<br />
Wenn man einen Filter für eine bestimmte Anwendung berechnen will, braucht man folgende Informationen:<br />
* Die Grenzfrequenz. Das ist die Frequenz, ab der die Ausgangsspannung gegenüber der Eingangsspannung um 3dB abgeschwächt wird - das entspricht in etwa einer Abschwächum um den Faktor 1,414, genauer um <math>\sqrt{2}</math><br />
* Die Ordnung des Filters. Diese ergibt sich aus den Anforderungen in der Anwendung.<br />
* Ob der Filter aktiv oder passiv ausgeführt werden soll<br />
<br />
Alle Arten von Filtern, also solche 1. Ordnung, 2. Ordnung, Tiefpässe, Hochpässe etc. lassen sich sowohl Passiv als auch Aktiv aufbauen. Filter höherer als 2. Ordnung lassen sich immer als Verknüpfung von Filtern 1. und 2. Ordnung realisieren (bei passiven Filtern in der Regel mit einem Verstärker dazwischen). <br />
Beginnen wir zunächst mit den passiven Filtern:<br />
<br />
== Passiv ==<br />
<br />
=== 1. Ordnung, Tiefpass und Hochpass ===<br />
<br />
Für Tief- und Hochpässe 1. Ordnung gibt es zwei Möglichkeiten: Entweder man schaltet das frequenzabhängige Bauteil vor einen Lastwiderstand (beispielsweise einen Lautsprecher), oder man schaltet den Widerstand vor das frequenzabhängige Bauteil. Wie das dann aussieht, zeigt die folgende Abbildung:<br />
<br />
[[Bild:Filter 1.Ordnung.gif]]<br />
<br />
Ue ist die Eingangsspannung, vor dem Filter, und Ua ist die Spannung, die am Ausgang der Schaltung anliegt um weiterverarbeitet zu werden. Die obigen Aufbauten, mit dem frequenzabhängigen Bauteil vor dem Lastwiderstand, bieten sich an, wenn man bereits einen Lastwiderstand - beispielsweise einen Lautsprecher - gegeben hat. Den Widerstand R ersetzt man dann durch diese Last - und hat ein Bauteil weniger zu verbasteln.<br />
Das RC-Glied (links unten) bietet sich an, wenn man einen Tiefpassfilter mit hoher Eingangsimpedanz benötigt. Das ist beispielsweise der Fall, wenn man eine Referenzspannung glätten möchte.<br />
Das RL-Glied bietet sich meines Wissens überhaupt nicht an :D, da für die im Hobbybereich gängigen Frequenzen entweder ein sehr kleiner Widerstand R oder eine sehr große Spule nötig würde. Lösung 1 hat dann einen extrem kleinen Eingangswiderstand, Lösung 2 verbraucht massig Platz und ist teuer.<br><br />
Die Berechnungen sind, egal ob Hochpass oder Tiefpass, jeweils identisch. Bei einer Spule als frequenzabhängiges Bauteil gilt:<br />
<br />
<math>L = \frac{R}{2\pi*f_g}</math><br />
<br />
Wird dagegen ein Kondensator benutzt, gilt:<br />
<br />
<math>C = \frac{1}{2\pi*f_g*R}</math><br />
<br />
<br />
=== Passiv, 2. Ordnung ===<br />
<br />
Bei Filtern zweiter Ordnung gibt es neben der charakteristischen Frequenz noch einen 2. Parameter, die Güte. Diese beschreibt die Schwingfähigkeit des Filters, also etwa wie lange der Filter nach einem kurzen Impuls nachschwingt. Filter hoher Güte (nicht als Qualitätsmassstab) geben einen schärferen Übergang von Durchlass zum Sperren. Als Nachteil braucht es aber etwas Zeit bis der Filter eingeschwungen ist. Ob eine hohe oder niedriger Güte gewünscht ist, hängt von der Anwendung ab:<br />
Im Audiobereich nutzt man Filter mit eher geringer Güte und dadurch schwacher Schwingneigung. Wenn aber eine hohe Trennschärfe (etwa Radioempfang) gefordert ist, kann man diese durch Erhöhung der Güte verbessern.<br />
Bei Filtern mit hoher Güte sinkt die Dämpfung beim Übergang zum Sperrbereich des Filters erst ab, in einem bestimmten Bereich wird sie sogar kleiner als 1. Das heißt das Signal wird sogar noch verstärkt. Danach steigt die Dämpfung relativ schnell an.<br />
Bei einer Güte, die kleiner ist als die Wurzel aus 1/2 (ca. 0,707) tritt dieses sogenannte Überschwingen des Frequenzgangs nicht auf. Diese Güte ist auch die gebräuchlichste, sie ist meistens ein guter Kompromiss. Filter mit dieser Güte haben eine Butterworth-Charakteristik.<br />
Daneben gibt es noch andere: Ein Filter mit Tschebyshev-Charakteristik ist jeder Filter mit einer Güte, die größer ist als die Wurzel aus 1/2. Bei diesen Filtern tritt besagtes Überschwingen auf, je höher die Güte, desto stärker ist das Überschwingen.<br />
Ein Filter mit Bessel-Charakteristik hat eine Güte von Wurzel aus 1/3. Die Trennschärfe bei diesen Filtern ist schlechter als bei Filtern mit Butterworth-Charakteristik, aber Signale, deren Frequenzanteile im Durchlassbereich liegen, werden nicht so stark verzerrt. <br />
<br />
==== Bandpass ====<br />
<br />
[[Bild:Bandpass-Schaltung.gif|thumb|Die Schaltung für einen Bandpassfilter]]<br />
Bei diesen Arten von Filtern ist die Berechnung einen Tacken komplizierter. Bandpass und Bandsperre sind mindestens Filter 2. Ordnung. Im Grunde haben diese Filter eine obere und eine untere Grenzfrequenz, so dass verschieden große Frequenzbereiche beeinflusst werden können.<br />
Am einfachsten ist es dann, zwei einzelne Filter zu dimensionieren, nämlich einen Hochpass und einen Tiefpass und diese dann zu einem Filter zusammenzufassen. Aufgrund von Resonanz ü.ä. entspricht der entstehende Frequenzgang nicht dem, der entstehen würde, wenn man einfach die Dämpfungen der beiden Filter zusammenrechnet. Solange der Filter ein ganzes Frequenzband durchlassen soll, macht dies aber in den seltensten Fällen einen Unterschied. Wenn aber nur eine Frequenz durchgelassen werden soll, unterscheidet sich der Frequenzgang stark von dem der Einzelnen Filter. Es gibt dann eine Resonanzfrequenz, bei der die Dämpfung extrem gering wird. Wenn man sich von der Resonanzfrequenz wegbewegt, steigt die Dämpfung erst sehr schnell an, und geht schließlich in einen Abfall um 20dB / Dekade (Spannung proportional oder antiproportional zur Frequenz) über.<br><br />
Ein Problem gibt es aber dabei: Wenn die Abweichungen der Bauteile zu groß sind (und Kondensatoren sowie Spulen haben nunmal meistens große Toleranzen), wird die Frequenz möglicherweise nicht getroffen, und der Filter macht nicht das was er soll. Abhilfe schafft entweder die Verwendung von Bauteilen mit geringen Toleranzen (teuer!) oder eine Abgleich durch eine variable Kapazität oder Induktivität. Alternativ kann teilweise ein Filter höherer Ordnung mit einem etwas größerem des Frequenzband genutzt werden. Das gleiche gilt übrigens bei Bandsperren!<br />
<br />
[[Bild:bandpass-Ergebnis.gif|thumb|Das Ergebnis der Berechnung: Die Grenzfrequenzen stimmen mit den erwarteten mit ausreichender Genauigkeit überein.]]<br />
Ein Beispiel zur Berechnung: Nehmen wir an, wir brauchen einen Bandpass, der das hörbare Frequenzband von 20 Hertz bis 20 Kilohertz durchlässt. Diese Frequenzen sollten dann zugleich die Grenzfrequenzen des Filters sein. Wir berechnen dann einen Hochpassfilter, um Frequenzen unter 20 Hertz zu sperren, und einen Tiefpass, der Frequenzen über 20 Kilohertz sperren würde. Da ein Lastwiderstand, ein Breitbandlautsprecher mit einer Impedanz von 8 Ohm, gegeben ist, benutzen wir die oberen Aufbauten aus der obigen Grafik. Wir erhalten für die Induktivität <math>L \approx 63\mu H</math> und für den Kondensator <math>C \approx 1000\mu F</math>.<br />
<br />
Die Schaltung wird in dem Bild oben rechts gezeigt, die beiden Filter werden sozusagen hintereinandergeschaltet.<br><br />
Das Ergebnis ist in dem Bild darunter dargestellt: Die Grenzfrequenzen liegen mit ausreichender Genauigkeit bei den erwarteten Frequenzen von 20 Hertz und 20 Kilohertz.<br />
<br />
<br />
==== Bandsperre ====<br />
<br />
[[Bild:Bandsperre-Schaltung.gif|thumb|Die Schaltung für eine Bandsperre]]<br />
Bei einer Bandsperre verfährt man fast genauso. Man berechnet einen Tiefpassfilter für die untere Grenzfrequenz und einen Hochpassfilter für die obere Grenzfrequenz. Die Frequenzanteile, die dann zwischen den Grenzfrequenzen liegen, werden mehr oder weniger stark gesperrt, je nachdem, wie weit sie von den Grenzfrequenzen entfernt sind. In der geometrischen Mitte der beiden Frequenzen tritt eine Resonanz auf, die Sperrwirkung des Filters ist dann fast unendlich groß, sofern man ideale Bauteile hat. <br />
<br />
Bei einer Bandsperre werden die beiden frequenzabhängigen Bauteile parallel geschaltet. Die Schaltung sieht dann so aus wie in der nebenstehenden Abbildung.<br />
<br />
[[Bild:2T-notch.png|thumb| 2 T Bandsperre]]<br />
Für die Bandsperre gibt es noch eine zweite Möglichkeit, ohne Induktivität. Damit ist diese Schaltung gut als Filter für niedrige Frequenzen wie z.B. 50 Hz oder 100 Hz geeignet. Die erreichbare Sperrwirkung des Filters hängt davon ab, wie gut das Verhältnis der Werte stimmt. Die Güte liegt bei dieser Schaltung fest bei 1/2 - mit aktiven Variationen der Schaltung sind höhere Güten möglich.<br />
<br />
== Aktiv ==<br />
Bei relativ niedriger Frequenz, wie im Audiobereich sind Induktivitäten ziemlich unhandliche Bauteile und alles andere als ideal. Mit Hilfe von Verstärkern lassen sich auch Filter mit höherer Güte (Q > 0.7) ohne Induktivitäten, nur mit Widerständen, Kondensatoren und halt dem Verstärker (heute in der Regel ein [[Operationsverstärker]]) aufbauen.<br />
<br />
Die Filter 1. Ordnung sind oft auch weiter passive Filter mit einem nachgeschalteten Verstärker. Eine Ausnahme sind hier die Extremfälle Integrator und Differentiator, und ein Allpassfilter (Phasenschieber).<br />
<br />
Die typischen aktiven Filterschaltungen nutzen einen Operationsverstärker für einen Filter 2. Ordnung. Dabei gibt es mehrere Möglichkeiten der Realisierung. Zur Berechnung der passenden Widerstandswerte und Kapazitäten gibt es spezielle Programme zum Filter-Design (siehe Weblinks). Damit kann dann auch gleich der Frequenzgang im voraus berechnet werden. <br />
<br />
=== Multiple-Feedback Filter ===<br />
[[Bild:MultiFB_LP.png|thumb| Multi-Feedback Tiefpass]]<br />
<br />
[[Bild:MultiFB_BP.png|thumb| Multi-Feedback Bandpass]]<br />
<br />
Diese Schaltungsform ist vor allem angebracht für Filter mit hohem Q. Im Durchlassbereich wird das Signal invertiert. Der Hochpassfilter entsteht einfach aus dem Tiefpass indem man die Widerstände und Kondensatoren vertauscht. <br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
.<br />
<br />
=== Sallen-Key Filter ===<br />
[[Bild:Sallenkey_LP.png|thumb| Sallen-key Tiefpass]]<br />
<br />
<br />
[[Bild:Sallenkey_HP.png|thumb| Sallen-key Hochpass]]<br />
<br />
<br />
[[Bild:Sallenkey_BP.png|thumb| Sallen-key Bandpass]]<br />
<br />
Diese Schaltung ist vor allem angebracht für Filter mit niedrigem Q ( < 2). Im Durchlassbereich wird das Signal nicht invertiert und in der Regel auch nicht verstärkt. Bei der Wahl der Kapazitäten und Widerstände gibt es mehrere Möglichkeiten. Der einfache Fall mit 2 gleichen Widerständen und 2 gleiche Kondensatoren gibt für den Hochpass und Tiefpass eine Grenzfrequenz von 0,16 / (RC) und eine Güte von 0,5.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
.<br />
<br />
=== weitere Filterschaltungen ===<br />
Auch bei normalen Verstärkerschaltungen wird oft die Bandbreite nach oben und unten auf den nötigen Bereich begrenzt. Die Filterfunktion ist dabei eher Nebensache und eine Kombination aus einem Hochpass und Tiefpass jeweils 1. Ordnung genügt.<br />
<br />
<br />
=== Filter mit geschalteten Kondensatoren (switched capacitor filter) ===<br />
Um die Frequenz eines aktiven Filters höherer Ordnung zu verstellen muss man oft mehrere Widerstände synchron verstellen. Neben der relativ neuen Möglichkeit über elektronische Potis gibt es die alternative Widerstände durch kleine Ladungspumpen zu ersetzen. Statt eines kontinuierlichen Stromes wird die Ladung in einem Kondensator und elektronischen Schaltern in kleinen Portionen transportiert. Für niedrige Frequenzen verhält sich das dann wie ein Widerstand, umgekehrt proportional zur relativ hohe Umschaltfrequenz. Man kann so über die Umschaltfrequenz (z.B. 1 MHz) die Grenzfrequenz (z.B. 1 kHz) eines aktiven Filters, auch höherer Ordnung verstellen. Für diese Anwendung gibt es spezielle ICs.<br />
<br />
= Weblinks =<br />
*[http://focus.ti.com/docs/toolsw/folders/print/filterpro.html Ti Filter Pro Software für Aktive Filter]<br />
*[http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en010007 Microchip Software für Aktive Filter]<br />
*[http://www.linear.com/designtools/software/#Filter LT Software für Aktive Filter]<br />
*[http://elektronikbasteln.pl7.de/rfsim99-filter-berechnung.html RFSim99 Programm für Berechnung von HF- und NF-Filtern]<br />
[[Category:Elektronik]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Filter_(Elektronik)&diff=23386Filter (Elektronik)2013-12-12T13:40:22Z<p>Besserwessi: /* Arten von Filtern */ Teil wieder entfernt - weiter unten schon so ähnlich</p>
<hr />
<div>= Wozu benutzt man Filter? =<br />
<br />
Für Filter gibt es eine ganze Menge Anwendungen.<br />
<br />
Als erstes Beispiel nehmen wir uns mal einen Verstärker her, einen einfachen. Einen Transistor in Emitterschaltung zum Beispiel. Am Eingang dieser Schaltung liegt die Basis des Transistors, beschaltet mit einem Vorwiderstand von Vcc aus. Dieser dient dazu, den Arbeitspunkt des Transistors festzulegen und "zieht" die Basis des Transistors zum Beispiel auf 0,70 Volt.<br />
Was passiert wenn man nun ein Signal anlegt, beispielsweise ein Audiosignal, welches zwischen 0,1 Volt und -0,1 Volt schwingt? Die Basis wird auf eben diese (im Mittel) 0 Volt heruntergezogen, und der Strom, der eigentlich für die Basis vorgesehen war, fließt nun einfach in die Signalquelle ab.<br><br />
Hier schafft ein Entkopplungskondensator, den man zwischen Signalquelle und Basis des Transistors schaltet, Abhilfe: Die Spannung zwischen dessen Anschlüssen steigt durch den abfließenden Strom langsam an. Die Spannung an der zum Transistor gewandten Seite steigt also an, und irgendwann ist die Spannung soweit gestiegen, dass durch die Basis des Transistors wieder der entsprechende Strom fließen kann. Durch den Kondensator fließt dann kein Gleichstrom mehr.<br />
Die Wechselspannung, in diesem Fall das Audiosignal, wird dagegen durchgelassen. Dabei fließt nämlich während der positiven Halbwelle Strom in die Schaltung herein, bei der negativen Halbwelle des Signals dagegen fließt der Strom wieder heraus. Diese Strompulse sind so kurz, dass der Kondensator seine Spannung dabei kaum ändert, sie werden also fast ungehindert durchgelassen.<br />
<br />
Als zweites Beispiel nehmen wir uns nochmal Audio-Kram her: Wir haben nen Tieftöner, den wir an unsere Stereoanlage anschließen wollen. Die tiefen Frequenzen soll er ruhig wiedergeben - die hohen Frequenzen jedoch nicht, denn für die sind die Hochtöner zuständig.<br><br />
Eine Spule, die hier in Reihe zum Lautsprecher geschaltet wird, erfüllt diese Bedingungen. Wenn ein Signal an die Kombination aus Lautsprecher und Spule angelegt wird, steigt der Strom, bedingt durch die Induktivität der Spule, nur langsam an. Bei tiefen Frequenzen fällt das kaum auf, denn eine Halbwelle ist lang genug, um den Strom durch den Lautsprecher und die Spule zu erhöhen. Bei hohen Frequenzen dagegen sind die Halbwellen so kurz, dass sich kein nennenswerter Stromfluss einstellen kann - die Signale werden von der Spule blockiert.<br />
<br />
Zur Glättung von Spannungen und / oder Strömen werden auch Filter eingesetzt. Eine Kombination aus Spule und Kondensator (LC-Filter) setzt man beispielsweise häufig vor der ADC-Versorgung von Mikrocontrollern ein. Diese Filter filtern doppelt: Wenn die Versorgungsspannung plötzlich leicht abfällt, ändert sich zunächst der Strom in der Spule, aber wie gesagt wurde, durch die Induktivität verlangsamt. Wenn der Strom etwas abgefallen ist, wird das zweite Filterelement - der Kondensator - aktiv, denn durch ihn muss zuerst ein Strom fließen, damit sich die Spannung am Ausgang der Schaltung ändern kann.<br />
<br />
----<br />
<br />
*Phantomspeisung (gecancelt: Gebräuchliche Systeme wie PoE brauchen dafür afaik keine echten Filter.)<br />
<br />
= Arten von Filtern =<br />
<br />
Nun gibt es aber zig verschiedene Möglichkeiten, Filter aufzubauen, noch dazu ist jede nur für spezielle Anwendungen geeignet.<br><br />
Man unterteilt Filter zunächst in aktive Filter und passive Filter: Passive Filter sind solche, die nur aus passiven Bauteilen bestehen. Dazu gehören die oben angesprochenen Kondensatoren, Spulen und Widerstände, aber auch Quarze.<br />
Aktive Filter enthalten als aktive Komponente meist einen Operationsverstärker. Der Vorteil liegt auf der Hand: Der Ausgang des OpAmps ist auch der Ausgang des Filters, der damit auch ruhig bis einige Milliampere belastet werden kann - bei passiven Filtern ist dies meistens nicht ohne weiteres möglich, die Ausgangsspannung würde sich dabei ändern. Der Nachteil sollte auch klar sein: Ein OpAmp, und jede andere aktive Komponente auch, braucht eine Versorgungsspannung. Für eine Frequenzweiche in einer passiven Standbox (d.h. der Verstärker ist nicht integriert) sind aktive Filter dadurch ungeeignet.<br />
<br />
[[Bild:Filterordnungen.gif|thumb|Beispiele für verschiedene Filterordnungen]]<br />
Weiterhin unterscheidet man Filter nach verschiedenen Ordnungen. Die Ordnung spiegelt dabei die Anzahl der frequenzabhängigen Bauteile wieder. Ein Filter höherer Ordnung, beispielsweise die oben beschriebene Kombination aus Spule und Kondensator, hat eine bessere Trennschärfe. Das bedeutet, dass der Abstand zwischen Frequenzen, die durchgelassen werden, und Frequenzen, die gesperrt werden, kleiner ist.<br><br />
Nun gut, im Bild rechts sieht man schon dass das so nicht ganz stimmt: Jede Frequenz wird noch irgendwie durchgelassen, aber die Dämpfung, die in dB gemessen wird, wird immer stärker. Man müsste also eigentlich sagen, dass der Abstand der Frequenzen, die als durchgelassen '''bezeichnet''' werden können und der Frequenzen, die als gesperrt '''bezeichnet''' werden können, kleiner ist. Ab welcher Dämpfung eine Frequenz als gesperrt oder durchgelassen bezeichnet werden kann, das hängt wiederum von der Anwendung ab. In Audioanwendungen reicht ein Filter 1. Ordnung meist völlig aus, aber um beispielsweise ein PWM-Signal in eine Gleichspannung zu wandelt, muss eine bestimmte Frequenz unbedingt möglichst schwach gedämpft durchgelassen werden, während die PWM-Frequenz möglichst stark gedämpft werden soll. Deshalb nutzt man hierfür vorwiegend Filter höherer Ordnungen.<br />
<br />
[[Bild:Filtervergleich.gif|thumb|Beispiele für verschiedene Filtercharakteristiken]]<br />
Zuletzt unterteilt man Filter noch in verschiedene Charakteristiken, namentlich Tiefpässe, Hochpässe, Bandpässe und Bandsperren.<br />
*Tiefpässe lassen tiefe Frequenzen durch und sperren hohe Frequenzen. (siehe blaue Kurve im Bild.) Eine typische Anwendung wäre die Glättung eines Signals mithilfe einer Kombination aus Spule und Kondensator. <br />
*Hochpässe machen das Gegenteil: Sie sperren tiefe Frequenzen und lassen hohe Frequenzen durch. (siehe grüne Kurve im Bild.) Beispiele wären eine Gleichspannungsentkopplung oder eine Frequenzweiche für einen Hochtonlautsprecher.<br />
*Bandpässe sperren hohe und tiefe Frequenzen - Ein bestimmter Frequenzbereich wird jedoch durchgelassen. (siehe rote Kurve im Bild.) Eine Anwendung hierfür wäre eine Frequenzweiche vor einem Mitteltöner '''''(Sorry für die ganzen Beispiele aus dem Audiobereich, fällt vielleicht wem was besseres ein? Passiv-PFC würde mir einfallen, würde aber ne längere Erläuterung notwendig machen.)'''''<br />
*Bandsperren sind das Gegenstück zu den Bandpässen: Hohe und tiefe Frequenzen werden durchgelassen, ein bestimmter Frequenzbereich wird gesperrt. (siehe türkise Kurve im Bild.) Eine solche Schaltung kann beispielsweise verwendet werden, um die möglicherweise bei Datenübertragungen über Modem störenden Gebührenimpulse aus der Telefonleitung herauszufiltern.<br />
<br />
Wie man sieht, sind die Bezeichnungen so gut wie selbsterklärend.<br />
<br />
= Realisierung =<br />
<br />
Wenn man einen Filter für eine bestimmte Anwendung berechnen will, braucht man folgende Informationen:<br />
* Die Grenzfrequenz. Das ist die Frequenz, ab der die Ausgangsspannung gegenüber der Eingangsspannung um 3dB abgeschwächt wird - das entspricht in etwa einer Abschwächum um den Faktor 1,414, genauer um <math>\sqrt{2}</math><br />
* Die Ordnung des Filters. Diese ergibt sich aus den Anforderungen in der Anwendung.<br />
* Ob der Filter aktiv oder passiv ausgeführt werden soll<br />
<br />
Alle Arten von Filtern, also solche 1. Ordnung, 2. Ordnung, Tiefpässe, Hochpässe etc. lassen sich sowohl Passiv als auch Aktiv aufbauen. Filter höherer als 2. Ordnung lassen sich immer als Verknüpfung von Filtern 1. und 2. Ordnung realisieren (bei passiven Filtern in der Regel mit einem Verstärker dazwischen). <br />
Beginnen wir zunächst mit den passiven Filtern:<br />
<br />
== Passiv ==<br />
<br />
=== 1. Ordnung, Tiefpass und Hochpass ===<br />
<br />
Für Tief- und Hochpässe 1. Ordnung gibt es zwei Möglichkeiten: Entweder man schaltet das frequenzabhängige Bauteil vor einen Lastwiderstand (beispielsweise einen Lautsprecher), oder man schaltet den Widerstand vor das frequenzabhängige Bauteil. Wie das dann aussieht, zeigt die folgende Abbildung:<br />
<br />
[[Bild:Filter 1.Ordnung.gif]]<br />
<br />
Ue ist die Eingangsspannung, vor dem Filter, und Ua ist die Spannung, die am Ausgang der Schaltung anliegt um weiterverarbeitet zu werden. Die obigen Aufbauten, mit dem frequenzabhängigen Bauteil vor dem Lastwiderstand, bieten sich an, wenn man bereits einen Lastwiderstand - beispielsweise einen Lautsprecher - gegeben hat. Den Widerstand R ersetzt man dann durch diese Last - und hat ein Bauteil weniger zu verbasteln.<br />
Das RC-Glied (links unten) bietet sich an, wenn man einen Tiefpassfilter mit hoher Eingangsimpedanz benötigt. Das ist beispielsweise der Fall, wenn man eine Referenzspannung glätten möchte.<br />
Das RL-Glied bietet sich meines Wissens überhaupt nicht an :D, da für die im Hobbybereich gängigen Frequenzen entweder ein sehr kleiner Widerstand R oder eine sehr große Spule nötig würde. Lösung 1 hat dann einen extrem kleinen Eingangswiderstand, Lösung 2 verbraucht massig Platz und ist teuer.<br><br />
Die Berechnungen sind, egal ob Hochpass oder Tiefpass, jeweils identisch. Bei einer Spule als frequenzabhängiges Bauteil gilt:<br />
<br />
<math>L = \frac{R}{2\pi*f_g}</math><br />
<br />
Wird dagegen ein Kondensator benutzt, gilt:<br />
<br />
<math>C = \frac{1}{2\pi*f_g*R}</math><br />
<br />
=== Bandpass ===<br />
<br />
[[Bild:Bandpass-Schaltung.gif|thumb|Die Schaltung für einen Bandpassfilter]]<br />
Bei diesen Arten von Filtern ist die Berechnung einen Tacken komplizierter. Bandpass und Bandsperre sind mindestens Filter 2. Ordnung. Im Grunde haben diese Filter eine obere und eine untere Grenzfrequenz, so dass verschieden große Frequenzbereiche beeinflusst werden können.<br />
Am einfachsten ist es dann, zwei einzelne Filter zu dimensionieren, nämlich einen Hochpass und einen Tiefpass und diese dann zu einem Filter zusammenzufassen. Aufgrund von Resonanz ü.ä. entspricht der entstehende Frequenzgang nicht dem, der entstehen würde, wenn man einfach die Dämpfungen der beiden Filter zusammenrechnet. Solange der Filter ein ganzes Frequenzband durchlassen soll, macht dies aber in den seltensten Fällen einen Unterschied. Wenn aber nur eine Frequenz durchgelassen werden soll, unterscheidet sich der Frequenzgang stark von dem der Einzelnen Filter. Es gibt dann eine Resonanzfrequenz, bei der die Dämpfung extrem gering wird. Wenn man sich von der Resonanzfrequenz wegbewegt, steigt die Dämpfung erst sehr schnell an, und geht schließlich in einen Abfall um 20dB / Dekade (Spannung proportional oder antiproportional zur Frequenz) über.<br><br />
Ein Problem gibt es aber dabei: Wenn die Abweichungen der Bauteile zu groß sind (und Kondensatoren sowie Spulen haben nunmal meistens große Toleranzen), wird die Frequenz möglicherweise nicht getroffen, und der Filter macht nicht das was er soll. Abhilfe schafft entweder die Verwendung von Bauteilen mit geringen Toleranzen (teuer!) oder eine Abgleich durch eine variable Kapazität oder Induktivität. Alternativ kann teilweise ein Filter höherer Ordnung mit einem etwas größerem des Frequenzband genutzt werden. Das gleiche gilt übrigens bei Bandsperren!<br />
<br />
[[Bild:bandpass-Ergebnis.gif|thumb|Das Ergebnis der Berechnung: Die Grenzfrequenzen stimmen mit den erwarteten mit ausreichender Genauigkeit überein.]]<br />
Ein Beispiel zur Berechnung: Nehmen wir an, wir brauchen einen Bandpass, der das hörbare Frequenzband von 20 Hertz bis 20 Kilohertz durchlässt. Diese Frequenzen sollten dann zugleich die Grenzfrequenzen des Filters sein. Wir berechnen dann einen Hochpassfilter, um Frequenzen unter 20 Hertz zu sperren, und einen Tiefpass, der Frequenzen über 20 Kilohertz sperren würde. Da ein Lastwiderstand, ein Breitbandlautsprecher mit einer Impedanz von 8 Ohm, gegeben ist, benutzen wir die oberen Aufbauten aus der obigen Grafik. Wir erhalten für die Induktivität <math>L \approx 63\mu H</math> und für den Kondensator <math>C \approx 1000\mu F</math>.<br />
<br />
Die Schaltung wird in dem Bild oben rechts gezeigt, die beiden Filter werden sozusagen hintereinandergeschaltet.<br><br />
Das Ergebnis ist in dem Bild darunter dargestellt: Die Grenzfrequenzen liegen mit ausreichender Genauigkeit bei den erwarteten Frequenzen von 20 Hertz und 20 Kilohertz.<br />
<br />
<br />
<br />
=== Passiv, 2. Ordnung ===<br />
<br />
Bei passiven Filtern zweiter Ordnung sind ein paar Schritte mehr notwendig. Zuerst einmal muss man wissen, welche Güte der Filter haben soll. Diese beschreibt die Schwingfähigkeit des Filters. Im Audiobereich nutzt man Filter mit möglichst geringen Güten und dadurch schwacher Schwingneigung. Wenn aber eine hohe Trennschärfe gefordert ist, kann man diese durch Erhöhung der Güte verbessern.<br />
Bei Filtern mit hoher Güte sinkt die Dämpfung beim Übergang zum Sperrbereich des Filters erst ab, in einem bestimmten Bereich wird sie sogar kleiner als 1. Das heißt das Signal wird sogar noch verstärkt. Danach steigt die Dämpfung relativ schnell an.<br />
Bei einer Güte, die kleiner ist als die Wurzel aus 1/2 (ca. 0,707) tritt dieses sogenannte Überschwingen des Frequenzgangs nicht auf. Diese Güte ist auch die gebräuchlichste, sie ist meistens ein guter Kompromiss. Filter mit dieser Güte haben eine Butterworth-Charakteristik.<br />
Daneben gibt es noch andere: Ein Filter mit Tschebyshev-Charakteristik ist jeder Filter mit einer Güte, die größer ist als die Wurzel aus 1/2. Bei diesen Filtern tritt besagtes Überschwingen auf, je höher die Güte, desto stärker ist das Überschwingen.<br />
Ein Filter mit Bessel-Charakteristik hat eine Güte von Wurzel aus 1/3. Die Trennschärfe bei diesen Filtern ist schlechter als bei Filtern mit Butterworth-Charakteristik, aber Signale, deren Frequenzanteile im Durchlassbereich liegen, werden nicht so stark verzerrt. Das ist auch der Grund, warum dieser Filter in Audioanwendungen die beste Qualität liefert.<br />
<br />
==== Bandsperre ====<br />
<br />
[[Bild:Bandsperre-Schaltung.gif|thumb|Die Schaltung für eine Bandsperre]]<br />
Bei einer Bandsperre verfährt man fast genauso. Man berechnet einen Tiefpassfilter für die untere Grenzfrequenz und einen Hochpassfilter für die obere Grenzfrequenz. Die Frequenzanteile, die dann zwischen den Grenzfrequenzen liegen, werden mehr oder weniger stark gesperrt, je nachdem, wie weit sie von den Grenzfrequenzen entfernt sind. In der geometrischen Mitte der beiden Frequenzen tritt eine Resonanz auf, die Sperrwirkung des Filters ist dann fast unendlich groß, sofern man ideale Bauteile hat. <br />
<br />
Bei einer Bandsperre werden die beiden frequenzabhängigen Bauteile parallel geschaltet. Die Schaltung sieht dann so aus wie in der nebenstehenden Abbildung.<br />
<br />
[[Bild:2T-notch.png|thumb| 2 T Bandsperre]]<br />
Für die Bandsperre gibt es noch eine zweite Möglichkeit, ohne Induktivität. Damit ist diese Schaltung gut als Filter für niedrige Frequenzen wie z.B. 50 Hz oder 100 Hz geeignet. Die erreichbare Sperrwirkung des Filters hängt davon ab, wie gut das Verhältnis der Werte stimmt.<br />
<br />
== Aktiv ==<br />
Bei relativ niedriger Frequenz, wie im Audiobereich sind Induktivitäten ziemlich unhandliche Bauteile und alles andere als ideal. Mit Hilfe von Verstärkern lassen sich auch Filter mit höherer Güte (Q > 0.7) ohne Induktivitäten, nur mit Widerständen, Kondensatoren und halt dem Verstärker (heute in der Regel ein [[Operationsverstärker]]) aufbauen.<br />
<br />
Die Filter 1. Ordnung sind oft auch weiter passive Filter mit einem nachgeschalteten Verstärker. Eine Ausnahme sind hier die Extremfälle Integrator und Differentiator, und ein Allpassfilter (Phasenschieber).<br />
<br />
Die typischen aktiven Filterschaltungen nutzen einen Operationsverstärker für einen Filter 2. Ordnung. Dabei gibt es mehrere Möglichkeiten der Realisierung. Zur Berechnung der passenden Widerstandswerte und Kapazitäten gibt es spezielle Programme zum Filter-Design (siehe Weblinks). Damit kann dann auch gleich der Frequenzgang im voraus berechnet werden. <br />
<br />
=== Multiple-Feedback Filter ===<br />
[[Bild:MultiFB_LP.png|thumb| Multi-Feedback Tiefpass]]<br />
<br />
[[Bild:MultiFB_BP.png|thumb| Multi-Feedback Bandpass]]<br />
<br />
Diese Schaltungsform ist vor allem angebracht für Filter mit hohem Q. Im Durchlassbereich wird das Signal invertiert. Der Hochpassfilter entsteht einfach aus dem Tiefpass indem man die Widerstände und Kondensatoren vertauscht. <br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
.<br />
<br />
=== Sallen-Key Filter ===<br />
[[Bild:Sallenkey_LP.png|thumb| Sallen-key Tiefpass]]<br />
<br />
<br />
[[Bild:Sallenkey_HP.png|thumb| Sallen-key Hochpass]]<br />
<br />
<br />
[[Bild:Sallenkey_BP.png|thumb| Sallen-key Bandpass]]<br />
<br />
Diese Schaltung ist vor allem angebracht für Filter mit niedrigem Q ( < 2). Im Durchlassbereich wird das Signal nicht invertiert und in der Regel auch nicht verstärkt. Bei der Wahl der Kapazitäten und Widerstände gibt es mehrere Möglichkeiten. Der einfache Fall mit 2 gleichen Widerständen und 2 gleiche Kondensatoren gibt für den Hochpass und Tiefpass eine Grenzfrequenz von 0,16 / (RC) und eine Güte von 0,5.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
.<br />
<br />
=== weitere Filterschaltungen ===<br />
Auch bei normalen Verstärkerschaltungen wird oft die Bandbreite nach oben und unten auf den nötigen Bereich begrenzt. Die Filterfunktion ist dabei eher Nebensache und eine Kombination aus einem Hochpass und Tiefpass jeweils 1. Ordnung genügt.<br />
<br />
<br />
=== Filter mit geschalteten Kondensatoren (switched capacitor filter) ===<br />
Um die Frequenz eines aktiven Filters höherer Ordnung zu verstellen muss man oft mehrere Widerstände synchron verstellen. Neben der relativ neuen Möglichkeit über elektronische Potis gibt es die alternative Widerstände durch kleine Ladungspumpen zu ersetzen. Statt eines kontinuierlichen Stromes wird die Ladung in einem Kondensator und elektronischen Schaltern in kleinen Portionen transportiert. Für niedrige Frequenzen verhält sich das dann wie ein Widerstand, umgekehrt proportional zur relativ hohe Umschaltfrequenz. Man kann so über die Umschaltfrequenz (z.B. 1 MHz) die Grenzfrequenz (z.B. 1 kHz) eines aktiven Filters, auch höherer Ordnung verstellen. Für diese Anwendung gibt es spezielle ICs.<br />
<br />
= Weblinks =<br />
*[http://focus.ti.com/docs/toolsw/folders/print/filterpro.html Ti Filter Pro Software für Aktive Filter]<br />
*[http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en010007 Microchip Software für Aktive Filter]<br />
*[http://www.linear.com/designtools/software/#Filter LT Software für Aktive Filter]<br />
*[http://elektronikbasteln.pl7.de/rfsim99-filter-berechnung.html RFSim99 Programm für Berechnung von HF- und NF-Filtern]<br />
[[Category:Elektronik]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=PIC_Assembler&diff=23385PIC Assembler2013-12-12T13:36:45Z<p>Besserwessi: /* Prozessor */ Vereinfacht und korrigiert</p>
<hr />
<div>= Einführung =<br />
<br />
== Bit, Byte, Nibble, Bin und Hex ==<br />
<br />
Ein Mikrocontroller (kurz: µC) kann eigentlich nur durch ein Portpin eine Spannung einlesen bzw. ausgeben. Er kann aber nur erkennen, ob eine Spannung vorhanden ist oder nicht. Wenn fast keine Spannung vorhanden ist erkennt er das als 0 und wenn eine Spannung fast so gross, wie seine Versorgungsspannung anliegt, als 1.<br />
<br />
Genauso bei der Ausgabe, wenn er 0 ausgibt ist auf dem Portpin fast keine Spannung, wenn 1, eine Spannung fast gleich gross seiner Versorgungsspannung. Und das ist ein Bit, die kleinste Menge einer Information. Das Bit ist binär, d.h. es kann nur zwei unterschiedliche Werte haben: 0 oder 1.<br />
<br />
Wenn wir gleichzeitig (parallel) 8 Bits haben, dann ist es ein Byte, das 256 Bitkombinationen von 00000000b bis 11111111b enthält, weil ein Bit (X) auf jeder Stelle 0 bzw. 1 sein kann.<br />
<br />
<table border=0 cellpadding=3 cellspacing=2><br />
<br />
<tr><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
</tr><br />
<br />
<tr><br />
<td colspan=4 align=middle bgcolor=#007fff>High Nibble </td><br />
<td colspan=4 align=middle bgcolor=#ff8305>Low Nibble </td><br />
</tr><br />
<tr><br />
<td colspan=8 align=middle bgcolor=#810f40> <font color=#ffffff>Byte</font> </td><br />
</tr><br />
</table><br />
<br />
Das "b" bedeutet, dass es sich um binäre (kurz: bin) Darstellung (auch Zahl genannt) handelt. Binäre Zahlen sind aber lang, weil jedes Bit eine Stelle benötigt.<br />
<br />
Um die Schreibweise zu verkürzen, wurden hexadezimale (kurz: hex) Zahlen eingeführt. Zuerst wurde ein Byte auf zwei 4-Bit Halbbytes (Nibbles) verteilt und danach ein Nibble als Ziffer genommen. Weil 4 Bits 16 Kombinationen ergeben, haben die Ziffer 0 bis 9 aus dem Dezimalsystem (d) nicht ausgereicht und wurden um Buchstaben A bis F erweitert. Die hexadezimalen Zahlen haben ein "h" Zeichen am Ende. Für die Zahlen 0 bis 9 sind die (h) und<br />
(d) Zeichen nicht nötig, da sie beide gleich den entsprechenden bin Zahlen sind.<br />
<br />
Die Umwandlung zwischen bin, hex und dec Zahlen für ein Nibble zeigt folgende Tabelle:<br />
<br />
0b = 0h = 0d 100b = 4h = 4d 1000b = 8h = 8d 1100b = Ch = 12d<br />
1b = 1h = 1d 101b = 5h = 5d 1001b = 9h = 9d 1101b = Dh = 13d<br />
10b = 2h = 2d 110b = 6h = 6d 1010b = Ah = 10d 1110b = Eh = 14d<br />
11b = 3h = 3d 111b = 7h = 7d 1011b = Bh = 11d 1111b = Fh = 15d<br />
<br />
Damit kann ein Byte mit zwei hex Ziffern definiert werden z.B. 1100 0011b = C3h. Für zwei Bytes braucht man 4 hex Ziffern z.B.<br />
<br />
101 0111 1010 1001b = 57A9h, usw. Für MPASM wird sehr oft die Schreibweise für hex Zahlen 0x57A9 oder 0xC3 benutzt.<br />
<br />
So wie im Dezimalsystem werden führende Nullen nicht geschrieben. Bei einer Wandlung bin->hex fängt man immer von der rechten Seite der bin Zahl an, da die Anzahl führender Nullen unbekannt ist.<br />
<br />
== Speicher und Register ==<br />
<br />
Als Speicher bezeichnet man das Teil der Hardware, in das eine Information geschrieben, gespeichert und von dort wieder ausgelesen werden kann.<br />
<br />
Es gibt eigentlich nur zwei Arten von elektronischen Speichern: flüchtige und nichtflüchtige. Die Information die sich im flüchtigen Speicher befindet, geht verloren, wenn die Versorgungsspannung des Speichers unterbrochen oder abgeschaltet wird. Bei PICs ist es Datenspeicher (RAM).<br />
<br />
Wenn die Versorgungsspannung vom nichtflüchtigen Speicher abgeschaltet wird, ist die gespeicherte Information zwar momentan nicht lesbar, bleibt aber erhalten und sobald der Speicher wieder mit Spannung versorgt wird, kann sie ausgelesen werden. Ein PIC hat zwei solche Speicher: Programmspeicher (Flash) und EEPROM.<br />
<br />
Der wichtigste Unterschied zwischen den Speicherarten ist, dass die flüchtigen direkt (sehr schnell) beschreibbar sind, dagegen das Beschreiben des nichtflüchtigen Speichers spezielle Algorithmen benötigt, die im Vergleich zu direkten Zugriffen langsamer sind. Beim Auslesen gibt es praktisch keinen Unterschied.<br />
<br />
Speicher besitzen eine bestimmte Menge von s.g. Speicherstellen. Jede Speicherstelle hat eine individuelle Adresse und kann eine binäre Information mit bestimmter Anzahl von Bits abspeichern. <br />
<br />
PIC Prozessoren haben drei Arten von Speicher, wegen verschiedener Anwendung, auch unterschiedliche Struktur. Die beiden Speicher für Daten (RAM und EEPROM) haben jeweils 8 Bit Breite und Programmspeicher (Flasch) bei Basic-Line hat 12-bittige, Mid-Range 14-bittige und High-End 8-bittige Speicherstellen. Die Anzahl den Speicherstellen im bestimmten Speicher ist vom PIC-Typ abhängig.<br />
<br />
Eine 8-bittige Speicherstelle im RAM wird bei PICs Register genannt und kann so skizziert werden:<br />
<br />
<table border=0 cellpadding=3 cellspacing=2><br />
<br />
<tr><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
<td align=middle bgcolor="#CCCCCC" style="border:1px solid #333333">X</td><br />
</tr><br />
<br />
<tr><br />
<td> MSB </td><br />
<td colspan=6></td><br />
<td> LSB </td><br />
</tr><br />
<br />
<tr><br />
<td bgcolor="#007fff">bit 7</td><br />
<td bgcolor="#007fff">bit 6</td><br />
<td bgcolor="#007fff">bit 5</td><br />
<td bgcolor="#007fff">bit 4</td><br />
<td bgcolor="#007fff">bit 3</td><br />
<td bgcolor="#007fff">bit 2</td><br />
<td bgcolor="#007fff">bit 1</td><br />
<td bgcolor="#007fff">bit 0</td><br />
</tr><br />
<br />
<tr><br />
<td colspan=4 align=middle bgcolor=#ff8305>High Nibble </td><br />
<td colspan=4 align=middle bgcolor=#ff8305>Low Nibble </td><br />
</tr><br />
<tr><br />
<td colspan=8 align=middle bgcolor=#810f40> <font color=#ffffff>Byte</font> </td><br />
</tr><br />
</table><br />
<br />
<br />
Bit 7 wird als hochwertigstes (MSB = Most Significant Bit) und bit 0 als niederwertigstes (LSB = Least Significant Bit) bezeichnet. Jedes Bit im Register (X) kann gleich 0 bzw. 1 sein. In einem Register existieren immer 8 Bits also auch führende Nullen. Zum Beispiel die hex Zahl 3h sieht im PIC Register so aus: 00000011.<br />
<br />
Um ein Datenbyte in ein Register zu schreiben oder aus einem Register zu lesen, muss zuerst das Register durch seine Adresse gewählt werden. Dafür gibt es beim PIC folgende Möglichkeiten:<br />
<br />
Direkte Adressierung per absolute Adresse: movwf 0x20<br />
<br />
Direkte Adressierung per vorher definiertem Namen des Registers (z.B. Temp equ 0x20): movwf Temp<br />
<br />
Indirekte Adressierung durch "FSR" Register, in das die absolute Adresse des Registers "Temp" eingeschrieben wird. Durch Zugriff mittels "INDF" Register wird auf die Speicherstelle zugegriffen, die durch das Register "FSR" adressiert wird. ("INDF" ist dabei kein real in Hardware implementiertes Register). Wie vorher wurde "Temp equ 0x20" definiert und weiter:<br />
<br />
movlw Temp ;ins W-Register wird die absolute Adresse des Registers "Temp" geladen<br />
movwf FSR ;diese Adresse wird ins "FSR" Register kopiert<br />
movf INDF,0 ;der Wert aus dem indirekt adressierten Register "Temp"<br />
;wird aus dem "INDF" Register ins W-Register geladen.<br />
<br />
Weil in jedem 12 bzw. 14-bittigem Befehl, der mit Datenspeicher verbunden ist, für die Adresse des ansprechenden Registers nur 7 Bits existieren, die bis zur Adresse 7Fh (128d) Register direkt ansprechen können, ist bei Basic-Line und Mid-Range PICs der Datenspeicher (RAM) in s.g. Bänke verteilt.<br />
<br />
Für Auswahl einer Bank sind zwei Bits "RP0" und "RP1" im "STATUS" Register zuständig. Die Anzahl der Bänke und ihre Verwendung ist von der gesamten Größe des RAMs abhängig und kann dem Datenblatt des PICs entnommen werden.<br />
<br />
Die Beschreibung allen SFRs (Special Funktion Register), in den sämtliche Funktionen des PICs festgelegt werden, befinden sich im Datenblatt unter "Memory Organisation".<br />
<br />
Siehe auch: [[#Speicherbankorganisation|Speicherbankorganisation]]<br />
<br />
== Prozessor ==<br />
Die Prozessoren der PIC's von Microchip sind alle in der "Harvard"-Architektur gefertigt. Das bedeutet, dass Datenspeicher und Programmspeicher einen eigenen Bus zur CPU (Central Processing Unit) besitzen. Der Vorteil zur "von Neumann"-Architektur ist, dass sich die Busgrößen damit unterscheiden können. <br />
<br />
Der Prozessor von PICs gehört zu den RISC (Reduced Instruction Set Computer) Prozessoren und man hat nur 35 bzw. 75 Befehle zu erlernen, was seine Programmierung im Vergleich zu anderen µCs deutlich vereinfacht. Jeder Befehl benötigt im Programmspeicher nur eine Speicherstelle und im Quellcode nur eine Zeile. Die Ausführung des Befehls dauert, abhängig vom Befehl, 1 bis 2 Prozessortakte.<br />
<br />
Ein sogenannter Prozessortakt besteht aus 4 Oszillator-Takten, die jeweils einen Teil eines Befehls abarbeiten. Deswegen entspricht die Taktfrequenz der CPU der durch 4 geteilten Frequenz des Oszillators.<br />
<br />
CPU Vorgang Richtung Speicher<br />
------------------------------------------------- -<br />
1.Befehl lesen (fetch) <------- Flash |<br />
2.Daten lesen (read) <------- RAM | 1 Prozessortakt =<br />
3.Daten verarbeiten (execute) | 4 Oszillatortakte<br />
4.Daten schreiben (write) -------> RAM | <br />
-<br />
<br />
Nur o.g. CPU Vorgänge sind direkt möglich. Es können deswegen keine Befehle aus dem RAM oder EEPROM ausgeführt werden. Um ein Databyte aus einem RAM Register in ein anderes zu kopieren, muss er zuerst aus dem ersten RAM Register in das W-Register (eigenen s.g. Arbeitsregister des CPU) und erst davon in das zweite RAM Register kopiert werden.<br />
<br />
Der Prozessor hat einen kleinen eigenen Speicher, der weder zum Programmspeicher noch zum Datenspeicher gehört. Er besteht aus dem Programmzähler PC (program counter) und dem Stapel (stack).<br />
<br />
In dem PC befindet sich die Adresse des momentan ausführbaren Befehls. Nach der Ausführung jedes Befehls wird der PC um 1 erhöht und "zeigt" somit auf den nächsten Befehl im Programmspeicher, der zum Ausführen wäre. Wenn der nächste Befehl aber z.B. ein Sprung ("call") ist, wird die Adresse aus dem Befehl genommen. Nach dem Sprung wird das Programm bis zum Befehl "return" ausgeführt und danach muss der Prozessor zurückspringen, und zwar, genau zur nächsten Adresse im Programmspeicher nach dem "call" Befehl.<br />
<br />
Um dies zu ermöglichen, wird vor dem Sprung die Adresse aus dem PC "auf dem Stapel gelegt" (gespeichert). Für den Rücksprung wird die abgelegte Adresse "aus dem Stapel genommen" (vom Stapel in den PC geladen). Beim Auftreten eines Interrupts wird auf dem Stapel die Adresse, der zuletzt ausgeführten Zeile abgelegt, damit der Prozessor, wenn er nach der Ausführung der "Interruppt Service Routine" (ISR) an "retfie" kommt, das unterbrochene Programm an der richtigen Stelle wieder startet. <br />
<br />
Der Stapel kann aber nur 8 bzw. 31 Adressen speichern, deswegen darf nur entsprechende Anzahl von nacheinander folgenden "call" Befehlen benutzt werden. Wenn ein Interrupt benutzt wird, reduziert sich es um 1, da eine Speicherstelle immer für die Adresse, an der das Programm unterbrochen wurde, reserviert werden muss. Sonst findet der Prozessor nicht mehr zurück und springt in die "Nirvana" , was einen Absturz des Programms bedeutet. Bei dem "goto" Befehl wird die nächste Adresse nicht gespeichert, da der Prozessor nicht zurückkehren braucht.<br />
<br />
Das Lesen/Schreiben aus/in den EEPROM Speicher ist mit Hilfe speziellen Register und Unterprogrammen bei allen PICs möglich. Der Lese und Schreibzugriff auf den Programmspeicher ist aber nur bei wenigen PIC-Typen (z.B. PIC16F87X) möglich. Dies ermöglicht ein "sich selbst Programmieren", was bei Bootloadern genutzt wird.<br />
<br />
== Assembler ==<br />
<br />
Die Maschinensprache, auch Assembler oder kurz ASM genannt, ist eine Sprache die nur eine bestimmte CPU versteht. Für einen Menschen ist sie unverständlich, da sie nur aus hex Zahlen besteht.<br />
<br />
Um sich die Sprache verständlicher zu machen wurden den hex Zahlen s.g. Mnemonics aus Buchstaben zugewiesen. Jeder Befehl für einen CPU hat somit ein "Namen", der aus der englischen Sprache stammt. Siehe: [[#Kurzübersicht Assembler Befehle|Kurzübersicht Assembler Befehle]]<br />
<br />
Obwohl sie 2 bis 1000 mal schneller als die meisten Hochsprachen ist, wird sie, wegen des grossen Aufwands bei der Erstellung von umfangreichen Programmen, selten benutzt. Man findet sie aber oft in fast allen Hochsprachen, in eingebundenen Funktionen, überall dort wo die Hochsprachen zu langsam sind oder die nötigen Aufgaben nicht unterstützen (z.B. Maus in Q-Basic).<br />
<br />
ASM eignet sich sehr gut für kleine Anwendungen (meistens Steuerungen) mit µC, weil nur bei dieser Programmiersprache ein direkter Zusammenhang zwischen einem Bit im Programm und einer Spannung am I/O Pin besteht. Aus dem Grund sind Hardware-Kenntnisse sehr vorteilhaft. Man kann sagen, dass je mehr externer Hardware, um so weniger Software, und umgekehrt. In der Praxis wird immer ein Kompromiss gesucht.<br />
<br />
Dank der integrierten oder an Portpins angeschlossenen Hardware und dem entsprechenden Programm kann ein µC umfangreiche Aufgaben realisieren, die fast unbegrenzt und schwer vorstellbar sind.<br />
<br />
Die Aufgabe eines ASM-Programmierers ist, ein Programm zu schreiben, das das Assemblerprogramm (z.B. MPASM) fehlerfrei in die Machinensprache assembliert ("übersetzt"), die dann eine bestimmte CPU "versteht". Sie endet eigentlich erst dann, wenn das geschriebene Programm so wie geplant funktioniert. <br />
<br />
Die beste Methode um ASM Programmierung zu lernen ist einfach mit den Befehlen, wie mit "LEGO" Bausteinen, zu spielen. Dafür wurde ein Programm "PIC Trainer" entwickelt. Der PIC kann durch ein Programmfehler nicht kaputtgehen. Vorsicht ist nur bei der angeschlossener Hardware nötig, da ein Fehler den PIC oder die Hardware sogar "töten" kann. <br />
<br />
Weil ASM Programme nicht besonders durchschaubar sind, wurde als Hilfsmittel ein Programmablaufdiagramm (kurz: PAD) erfunden. Bei der Programmerstellung fängt man damit an, ein PAD zu erstellen, das alle Programmschritte enthält.<br />
<br />
Weiter werden alle Befehle nach dem PAD mit einem üblichen Texteditor in eine Textdatei mit Erweiterung ".asm" (Quellcode) geschrieben, durch ein Assemblerprogramm (für PICs: MPASM oder [http://gputils.sourceforge.net/ GPASM]) von dem für Menschen noch verständlichen Code in die Maschinensprache assembliert und als Textdatei mit Erweiterung ".hex" gespeichert. Diese Datei wird danach in den Programmspeicher des µC übertragen ("gebrannt").<br />
<br />
Das Assemblerprogramm MPASM kann kostenlos von der Homepage des Herstellers von PICs [http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en019469&part=SW007002] runtergeladen werden. Es muss zuerst vom Downloads die "MPLAB IDE v7.50 Full Zipped Installation" runtergeladen und erst danach können gewählte Programme (z.B. nur MPASM) installiert werden. Für MPASM Benutzer werden auch folgende ".pdf" Dateien empfohlen:<br />
<br />
MPASM/MPLINK User's Guide (2628 KB) [Benutzerhandbuch] <br />
<br />
MPASM™/MPLINK™ PICmicro® Quick Chart (81 KB) [Kurzübersicht] <br />
<br />
Nach dem Einschalten der Betriebsspannung des µC, fängt der CPU an, sich im Programmspeicher befindliches Programm mit dem Befehl, der an der Adresse 0 steht, auszuführen.<br />
<br />
Aber wann das Programm endet? Natürlich wenn die Versorgungsspannung abgeschaltet wird. Nein! Das ist die einfachste Lösung um ein laufendes Programm an zufälliger Stelle zu unterbrechen,<br />
aber keine, um es an einer definierten Stelle zu beenden.<br />
<br />
Wenn an den µC angeschlossene externe Hardware (z.B. Grafikdisplay), eine bestimmte Befehlsfolge vor dem Abschalten benötigt oder wichtige Daten (in EEPROM oder Flash) abgespeichert werden sollen, darf die Spannung erst dann abgeschaltet werden, wenn die CPU eine Meldung ausgibt, dass sie sich schon auf der "STOP" Stelle des Programms befindet. Es muss auch<br />
definiert werden (z.B. durch eine Tastenkombination), wann die CPU zum letzten Fragment des ASM Programms vor dem "STOP" gehen soll.<br />
<br />
== Grundbeschaltung ==<br />
<br />
Der Prozessor von einem PIC kann sofort nach dem Einschalten der Versorgungsspannung (z.B. + 5V DC) arbeiten. Allerdings nur, wenn er den Takt, in dem er die Befehle ausführen soll, vorgegeben hat. Manche PICs besitzen einen internen RC-Oszillator, (z.B. PIC12F629, PIC16F630, PIC16F628, usw.). Bei diesen reicht es die Versorgungsspannung anzulegen und sie laufen bereits.<br />
<br />
Die meisten haben ihn aber nicht (z.B. PIC16F84, PIC16F870, usw.) und brauchen fürs Funktionieren zusätzliche Bauteile. Grundsätzlich gibt es mehrere Möglichkeiten:<br />
<br />
* Quarz oder Keramik-Resonator + 2 Kondensatoren (LP,HS oder XT) <br />
* Keramik-Resonator mit integrierten Kondensatoren (HS oder XT)<br />
* Quarzoszillator (EC); genau und stabil<br />
* Widerstand + Kondensator (RC); keine hohe Frequenzstabilität<br />
<br />
Die entsprechenden Bauteile werden an die Pins OSC1/OSC2 angeschlossen, um den notwendigen Prozessortakt zu erzeugen. Im Konfiguration-Word "__config" muss noch angegeben werden, welcher Oszillator (LP, HS, XT bzw. RC) verwendet wird.<br />
<br />
Desweiteren existiert ein MCLR-Pin, der beim PIC einen Neustart (=Reset) auslösen kann (Low-Pegel). Diesen Pin sollte man, wenn er in "__config" aktiviert ist, über einen Widerstand (pull-up) an Versorgungsspannung legen, damit der PIC anfängt, sein Programm abzuarbeiten. Der Anschluss wird auch für die Programmierung benötigt. Beim sog. High-Voltage-Programming wird MCLR auf ca. 12-14 Volt gelegt, um den PIC in den Programmiermodus zu schalten. Bei manchen PICs kann dieser Anschluss auch als normalen I/O Pin eingestellt werden. In dem Fall, bei ICSP Benutzung, soll noch eine Diode zwischen den pull-up und Versorgungsspannung angeschlossen werden, um die an PIC angeschlossene Hardware während der Programmierung vom Auftreten der Vpp an Vcc zu schützen und die Vpp nicht zu belasten.<br />
<br />
VCC<br />
+<br />
|<br />
V Diode<br />
-<br />
|<br />
.-.<br />
| | Pull-up<br />
| | (10k)<br />
'-'<br />
MCLR/Vpp | .-------.<br />
Pin +-| |<br />
| | PIC |<br />
| o | |<br />
Reset |=|> | |<br />
Taster | o '-------'<br />
|<br />
===<br />
GND <br />
<br />
Bei externen Oszillatoren bleibt der Pin OSC2 nicht angeschlossen und kann als I/O benutzt werden. Falls ein interner Oszillator benutzt wird, können beide OSC Pins als I/O dienen.<br />
<br />
Damit ein Programm zuverlässig ausgeführt werden kann, muss die Versorgungsspannung störungsfrei sein. Dafür wird ein Keramik-Vielschicht-Kondensator 100 nF (0,1 µF) möglichst am kürzesten direkt zwischen VDD und VSS Pins geschaltet.<br />
<br />
Folgende Skizzen zeigen die Grundbeschaltung eines PICs:<br />
<br />
{|<br />
|-<br />
|<br />
[[Bild:Pic-entstoer.png|thumb|160px|Entstörkondensator beim PIC]]<br />
|<br />
[[Bild:Qz-os.png|thumb|160px|Quarz ]]<br />
|<br />
[[Bild:Qos-os.png|thumb|160px|externer Quarzoszillator]]<br />
|<br />
[[Bild:Rc-os.png|thumb|160px|externer RC-Oszillator]]<br />
|}<br />
<br />
== Konfiguration ==<br />
<br />
Die Konfiguration eines PICs wird beim "brennen" fest programmiert. Sie ist eigentlich für fast jeden PIC-Typ anders. Um Probleme zu vermeiden, muss sie für bestimmten PIC aus einer im MPASM Verzeichnis enthaltenen Datei "PXXFXX.INC" entnommen werden. Die Erklärung der Optionen befindet sich im entsprechenden Datenblatt unter "Special Features of the CPU"<br />
<br />
Als Beispiel für PIC16F84 haben wir in der Datei "P16F84.INC":<br />
<br />
;==========================================================================<br />
;<br />
; Configuration Bits<br />
;<br />
;==========================================================================<br />
<br />
_CP_ON EQU H'000F'<br />
_CP_OFF EQU H'3FFF'<br />
_PWRTE_ON EQU H'3FF7'<br />
_PWRTE_OFF EQU H'3FFF'<br />
_WDT_ON EQU H'3FFF'<br />
_WDT_OFF EQU H'3FFB'<br />
_LP_OSC EQU H'3FFC'<br />
_XT_OSC EQU H'3FFD'<br />
_HS_OSC EQU H'3FFE'<br />
_RC_OSC EQU H'3FFF'<br />
<br />
Wobei:<br />
<br />
_CP_ON - Programmspeicher ist vorm Auslesen geschützt<br />
_CP_OFF - Programmspeicher kann ausgelesen werden (ist nicht geschützt)<br />
_PWRTE_ON - Das Programm wird mit Verzögerung von ca. 72 ms nach dem Einschalten gestartet<br />
_PWRTE_OFF - Das Programm wird sofort nach dem Einschalten gestartet<br />
_WDT_ON - Watchdog Timer ist aktiv<br />
_WDT_OFF - Watchdog Timer ist unaktiv<br />
<br />
Die alle folgende Optionen beziehen sich an den Oszillatortyp:<br />
<br />
_LP_OSC - Oszillator bis ca. 200 kHz (z.B. Uhrenquarz 32768 Hz)<br />
_XT_OSC - Oszillator bis ca. 3,5 MHz<br />
_HS_OSC - Oszillator über 3,5 MHz (z.B. 4 MHz)<br />
_RC_OSC - Externer RC Oszillator<br />
<br />
Die Konfiguration wird im Quellcode (*.asm Datei) mit Direktive "__config" definiert, z.B. so:<br />
<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC<br />
<br />
Alle Optionen müssen groß geschrieben sein, genauso wie in der "*.inc" Datei.<br />
<br />
Warnung !<br />
<br />
Anhand praktischer Erfahrung kann man feststellen, dass bei den kleinsten PICs der Familie 12FXXX, bei dennen ein I/O Pin mit VPP gemultiplext wird (z.B. PIC12F510, PIC12F629, PIC12F635 und 12F675), das Wählen des VPP Pins als I/O verwandelt ihn ins One Time Programming (OTP) Chip, der nur einmal programmiert werden kann. Die gewählte Konfuguration wird entgültig fest "gebrannt" und wegen seitdem fehlender Verbindung des I/O Pins mit VPP keine Umprogrammierung mehr möglich ist. Wer solchen PIC verwenden möchte, sollte aus dem Grund für Entwicklung anderen PIC-Typ nehmen und erst fertiges ausprobiertes Programm einmalig in den für konkrete Anwendung vorgesehenen PIC brennen.<br />
<br />
== Wahl des PICs ==<br />
<br />
Es gibt PIC µC die in der Typenbezeichnung den Buchstaben "C" oder "F" haben.<br />
<br />
Die älteren mit "C" haben EPROM Programmspeicher und die gibt es in zwei Versionen: ohne und mit Fenster (aus Quarz-Glass) fürs Löschen des EPROMs mit UV Strahlung. Bei denen ohne Fenster kann der Programmspeicher nur einmal beschrieben und nicht mehr gelöscht werden.<br />
<br />
Die neuen mit "F" besitzen einen Flash-Programmspeicher, der bis zu 100 000 mal mit angelegter Spannung gelöscht und danach neu beschrieben werden kann.<br />
<br />
Für die Wahl eines PICs für bestimmte Anwendung wichtig sind:<br />
<br />
- Max. Taktfrequenz des Prozessors.<br />
<br />
- Größe des Datenspeichers (für Variablen).<br />
<br />
- Größe des Programmspeichers (für Programm).<br />
<br />
- Integrierte Hardware (Komparatoren, A/D Wandler, Timer, USART, I²C, SPI, PWM, usw.).<br />
<br />
- Freie I/O Pins für externe Hardware (Display, Tasten, usw.).<br />
<br />
- Vorhandene Betriebspannung (Netzteil, Akku, Batterie).<br />
<br />
In der Praxis wird meistens für die Programmerstellung ein grösserer PIC genommen (wenn möglich pinkompatibler z.B. PIC16F628 für PIC16F84 oder PIC16F630 für PIC12F629) und erst nach der Optimierung des lauffähiges Programms, der tatsächlich nötige, da seine Parameter am Anfang nur geschätzt werden können. Wenn man viele Programme für verschiedene PICs entwickelt, wäre der größte PIC16F877 mit 20 MHz max. Taktfrequenz optimal.<br />
<br />
Diese Lösung hat auch den Vorteil, dass während der Programmerstellung kurze Hilfsprogramme (z.B. "PIC Trainer") in den Programmspeicher kopiert und benutzt werden können, da sie sowohl ein bißchen Programmspeicher und RAM als auch 2 freie I/O Pins fürs PIC Miniterminal brauchen.<br />
<br />
= Programm =<br />
<br />
== Allgemeines ==<br />
<br />
Jedes Programm kann man in kleinere Fragmente unterteilen, die auf bestimmte Weise miteinander verknüpft sind und gemeinsam die Aufgabe des Programms erfüllen. Das wichtigste Teil eines Programms ist das s.g. Hautprogramm (kurz:HP), das eine führende Rolle spielt. Dem HP sind fast alle andere Programmteile untergeordnet (weiter als Unterprogramm (kurz:UP) genannt) und werden nach Bedarf von ihm aufgerufen, um eine bestimmte Aufgabe zu erledigen.<br />
<br />
Die Struktur eines Programms ist aber komplizierter, da ein UP auch ein oder mehrere UPs nacheinander aufrufen kann. Ganz unten sind die UP1s, die ganz einfache Sachen erledigen. Höher ist das nächste Ebene mit UP2s die schon mehr komplizierten Aufgaben durch ein Aufruf der UP1s erledigen können, usw. Bei Mid-Range PICs (12FXXX und 16FXXX) können ohne Tricks maximal bis zu 8 Ebenen benutzt werden, da auf dem Stapel (stack) nur max. 8 Rücksprungadressen abgespeichert werden können. Siehe hierzu: [[#Prozessor|Prozessor]] und [[#Programmspeicher|Programmspeicher]]<br />
<br />
<center><br />
[[Bild:HP-UP.png|Hauptprogramm - Unterprogramm]]<br />
</center><br />
<br />
Jedes UP kann jederzeit aufgerufen werden, je nach dem was gerade erledigt werden muss. Weil das nicht egal ist, welches UP aufgerufen wird, da jedes nur eine bestimmte Funktion im Programm hat, muss der Programmierer dafür sorgen, dass alles richtig nach Programablaufdiagramm, und nicht chaotisch, abläuft.<br />
<br />
Die Programmierung in ASM ist ähnlich wie bei Hochsprachen, wenn man sich Bibliotheken mit geprüften prozessorspezifischen UPs erstellt. Um ein lauffähiges Programm zu erstellen, braucht man nur benötigte UPs ins Programm kopieren oder einbinden und ein geeignetes HP, das sie aufruft, schreiben.<br />
<br />
Ein ASM Programm (Quellcode) muss in einer Textdatei mit der Endung ".asm" in der vom Assemblerprogramm erwarteten Form verfasst werden, um die fehlerfreie Konvertierung in die Maschinensprache (Assemblierung) zu gewährleisten. Dieser Prozess verläuft in der Form eines Dialoges.<br />
<br />
Der Programmierer schreibt und gibt es dem Assemblerprogramm zum Übersetzen. Alles was der Assemblerprogramm nicht versteht oder nicht richtig ist, erscheint als Fehlermeldungen, die der Programmierer kennen muss, um die Fehler korrigieren zu können. Eine ".hex" Datei wird erst dann erstellt, wenn das Assemblerprogramm keine Fehler mehr im Quellcode findet. Deswegen ist es sehr wichtig, sich mit dem Assemblerprogramm vertraut zu machen, um die Dialogzeit zu minimieren.<br />
<br />
== Programmablaufdiagramm (PAD)==<br />
<br />
Der Programablaufdiagram (kurz: PAD) ist eine vorläufige und laufend änderbare Stufe zwischen einer Idee und ihrer Verwirklichung. Die Kreativität des Programmierers ist nur für die Erstellung des PADs nötig. Jedes sein Symbol (außer "Start/Stop") muss als Befehlsreihenfolge für einen bestimmten Prozessor in den Quellcode übertragen werden. Das ist aber nur reine "Übersetzung". Der Quellcode ist nur eine andere Form des PADs und hoffentlich wird es zukünftig Computerprogramme geben, die ein PAD direkt in eine "*.hex" Datei für bestimmten Prozessor wandeln werden. Zur Zeit muss die "Übersetzung" leider vom Programmierer gemacht werden. Der PAD wird erst dann fertig, wenn nach ihm erstelltes ASM Programm auf einem µC so wie gewünscht funktioniert.<br />
<br />
Die Anschriften "Ein" und "Aus" gehören nicht zu Symbolen des PADs und wurden nur zur Erklärung benutzt.<br />
<br />
[[Bild:PAD_beispiel.png|thumb|80px|Beispiel für ein PAD]]<br />
<br />
Der PAD ist sehr einfach zu erstellen, weil dafür nur drei Symbole benötigt sind:<br />
<center><br />
[[Bild:PAD_kurz.png|Symbole des PAD]]<br />
</center><br />
<br />
Bei PAD Erstellung darf man selbstverständlich beliebige Symbole verwenden, die man selber am besten versteht. <br />
<br />
Das "Start/Stopp" Symbol bedeutet, dass das gesamte Programm sich im stabilen Zustand befindet und nicht "läuft". Anstatt "Stopp" kann auch "Schlaf" ("sleep") angewendet werden, da das Programm in dem Fall auch nicht aktiv ist. Das "Tun" Symbol stellt meistens ein UP mit Reihenfolge von Befehlen dar. Das "Prüfen" bedeutet eine Prüfung bestimmter Bedingung und abhängig davon einen weiteren Lauf eines Programms, entweder in der "ja" (J) oder "nein" (N) Richtung.<br />
<br />
Als allgemeinnütziges Standard für µCs kann man folgender PAD bezeichnen:<br />
<br />
PAD _____<br />
/ \<br />
Spannung ein (Ein) ----->( Start )<br />
\_____/<br />
| -<br />
V |<br />
.---------------. |<br />
|Initialisierung| |<br />
'---------------' |<br />
| |<br />
.--------->V |<br />
| .---------------. |<br />
| | Hauptprogramm | |<br />
| '---------------' |<br />
| | |<br />
| V |<br />
| A |<br />
| / \ > Gesamtes Programm <br />
| / \ |<br />
| /Ende \____ |<br />
| \ ? / J | |<br />
| \ / | |<br />
| \ / | |<br />
| V | |<br />
| N| | |<br />
`----------´ | |<br />
V |<br />
.---------------. |<br />
| Beenden | |<br />
'---------------' |<br />
| |<br />
V -<br />
_____<br />
/ \<br />
Spannung aus (Aus) <-------------( Stopp )<br />
\_____/<br />
<br />
Das Hauptprogramm wird in einer endlosen Schleife ausgeführt, die durch die Prüfung "Ende?" unterbrochen werden kann. In dem Fall wird vor dem Beenden des gesamten Programms noch ein UP "Beenden" ausgeführt, das z.B. Daten im EEPROM speichert.<br />
<br />
Es ist nicht nötig, immer die Symbole zu zeichnen, man kann sie sich vorstellen und nur den Text schreiben. Die Prüfungen werden mit "?" gekennzeichnet und die Zeichen "V", "A", "<" und ">" zeigen die Richtung des weiteren Verlaufs. Dann sieht der PAD so aus:<br />
<br />
Ein > Start<br />
V - <br />
Initialisierung |<br />
.------->V |<br />
| Hauptprogramm > Gesamtes Programm<br />
| V | <br />
| Ende? J > Beenden |<br />
| N V -<br />
| V Stopp > Aus<br />
`--------´<br />
<br />
Man kann auch die unbedeutenden Richtungspfeilen weg lassen:<br />
<br />
PAD1 Ein > Start _<br />
Initialisierung |<br />
.------->V | Gesamtes<br />
| Hauptprogramm | Programm<br />
| Ende? J > Beenden _|<br />
| N Stopp <br />
| V V<br />
`--------´ Aus<br />
<br />
In der Praxis werden aus Platzgründen meistens die vereinfachten PADs benutzt. Als "movxx" wird oft ein Zeichen "->" benutzt. Zum Beispiel "A->W" bedeutet, dass der Inhalt des A Registers ins W-Register geladen (kopiert) wird. Außerdem werden Zeichen ("+", "-", "*", "/", "^", usw.) für arithmetische und ("&", "or", "xor", usw.) für logische Operationen angewendet. In den Quellcode werden für diese Zeichen entsprechende Befehle geschrieben. Beispiel:<br />
<br />
V<br />
10h->W<br />
W+B->B<br />
V<br />
<br />
wird im Quellcode so aussehen:<br />
<br />
...........<br />
movlw 10h<br />
addwf B,1<br />
...........<br />
<br />
Siehe auch: [[#Das erste Programm|Das erste Programm]] und [[#Mausrad bzw. Drehencoder|Mausrad bzw. Drehencoder]]<br />
<br />
Der PAD1 kann aber für Hauptprogramme, die in beliebigem Moment unterbrochen werden dürfen, deutlich vereinfacht werden, da die Prüfung "Ende?" ob das Hauptprogramm beendet werden soll, und das UP "Beenden", entfallen.<br />
<br />
Die meisten ASM Programme für µC sind deswegen nach solchem PAD erstellt:<br />
<br />
PAD2 Ein > Start _<br />
Initialisierung |<br />
.------->V |<br />
| Hauptprogramm > Gesamtes Programm<br />
| V |<br />
`--------´ _|<br />
<br />
Für Testprogramme wird meistens folgender PAD angewendet, weil es ziemlich einfach festzustellen<br />
ist (z.B. durch Stromverbrauchmessung des µCs), wann sich die CPU schon im Schlaf befindet. Erst dann, darf die Betriebspannung des µCs ausgeschaltet werden.<br />
<br />
PAD3 Ein > Start _<br />
Initialisierung | Gesamtes<br />
Hauptprogramm _| Programm<br />
Schlaf > Aus<br />
<br />
Und eine batteriebetriebene Uhr wird überwiegend so gestaltet:<br />
<br />
PAD4 Ein > Start _<br />
Interrupt Initialisierung |<br />
Timer------------------------->V > Gesamtes Programm<br />
Hauptprogramm _|<br />
Schlaf<br />
<br />
In dem Fall reicht es aus, wenn die CPU jede Minute vom Timer aufgeweckt wird, um die Zeit zu aktualisieren. Eine Uhr ist immer (außer Batteriewechsel) ununterbrochen mit Spannung versorgt.<br />
<br />
Für komplizierte Programme ist es praktisch unmöglich ein PAD zu erstellen, in dem jeder CPU Befehl sein eigenes Symbol hat. Man beschränkt sich nur auf alle Prüfungen, die über den Lauf des Programms entscheiden, und ganze UPs (z.B. "Initialisierung") nur als ein Symbol verwendet. Für jedes UP wird dann ein eigener PAD erstellt.<br />
<br />
Das Erstellen von PAD bei ASM Programmen ist sehr wichtig und darf nicht unterschätzt werden. Je stärker ein Programmierer glaubt, dass er das ohne PAD schafft, um so mehr Zeit wird er danach bei Fehlersuche oder Änderungen im ASM Programm verlieren.<br />
<br />
Beispiel aus dem Alltag:<br />
<br />
"Das Programm hat anfangs auf Anhieb funktioniert. Doch leider habe ich dann was geändert was ich nicht mehr weiß. Das Programm macht nun komische Sachen...... Ich kann mir nicht erklären warum, kann mir jemand helfen???" <br />
<br />
Für einfache ASM Programme, die gut kommentiert sind, reicht es meistens aus, ein PAD nur "im Kopf" zu erstellen, aber ganz ohne PAD geht es sicher nicht.<br />
<br />
Der PAD kann auch benutzt werden, um einen "fremden" Code verständlich zu machen. In dem Fall werden alle Befehle (Zeilen) aus dem Quellcode nacheinander in PAD Symbole umgewandelt und daraus ein PAD erstellt.<br />
<br />
== Hauptprogramm ==<br />
<br />
Wie sein Namen schon vermuten lässt, ist das Hauptprogramm das wichtigste Teil des gesamten Programms. Meistens ist es auch das kleinste Teil, vor allem, wenn die UPs sehr komplex sind. Seine Aufgabe ist die benötigte UPs in bestimmter Reihenfolge nacheinander aufzurufen, um die alle Funktionen des gesamten Programms zu realisieren. <br />
<br />
Das HP ist meistens als endlose Schleife, wie im PAD2, aufgebaut. Weil die endlose Schleife sehr schnell läuft, werden alle durch die UPS realisierten Aufgaben quasi gleichzeitig ausgeführt. Wenn es unerwünscht ist, müssen einige UPs als Verzögerungen realisiert werden. Dafür können auch UPs mit fester Ausführungszeit (z.B. Displayausgabe) angewendet werden.<br />
<br />
Typischer PAD für ein HP sieht so aus:<br />
<br />
Haupt .--->V<br />
| UP1<br />
| UP2<br />
| ...<br />
| UPn<br />
| V<br />
`----´<br />
<br />
In den Quellcode wird es so eingeschrieben:<br />
<br />
Haupt call UP1 <br />
call UP2<br />
...........<br />
call UPn<br />
goto Haupt<br />
<br />
In der Praxis wird das HP schrittweise erstellt. Am Anfang wird sich nur ein Aufruf vom UP im HP befinden und die folgenden kommen nach Erstellung und Prüfen weiteren UPs dazu, bis das HP fertig wird.<br />
<br />
=== Multitasking ===<br />
<br />
Echtes Multitasking ist nur mit mehr (Core)Prozessoren möglich. Bei PIC's ist echtes Multitasking (Parallellaufen) nur mit Timer und ADC Wandler möglich (siehe dazu: http://www.roboternetz.de/community/threads/42200-Frequenzz%C3%A4hler-mit-LPH2673-1-%28zum-Nachbauen%29?highlight=LPH2673-1 ) <br />
<br />
Sonst gibt es nur Quasi-Multitasking, das kann auf zwei Weisen realisiert werden:<br />
<br />
1. Alle Tasks werden in fester bzw. per Interrupts bestimmter Reihenfolge auf Bedarf geprüft und nur die mit gesetztem Flag werden vollständig bis zum Ende realisiert. Als Beispiel sehe: [[http://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=47685]]. Es kann natürlich mit Proritäten für Interrupts versehen werden.<br />
<br />
2. Der s.g. Taskmanager (meistens HP) gibt jedem Task (meistens UP) feste Zeit und wenn der Task aktiv ist, wird er nach dieser Zeit z.B. per Timer unterbrochen und nächster Tast gestartet. Dafür muss immer nach Beenden der ISR unbedingt ins HP gesprungen werden und die Adresse vom PC, wo der Task unterbrochen wurde auf dem Stapel gespeichert werden. Wenn die Zeit für den unterbrochenen Task wieder kommt, wird er ab der unterbrochenen Stelle wieder in der ihn zustehenden Zeit ausgeführt, wieder unterbrochen u.s.w.<br />
<br />
Dafür muss der Stapel mit Rücksprungadressen laufend mit "pop" and "push" Befehlen bearbeitet werden, was erst ab PIC18... möglich ist Bei dieser Methode bei kurzen Laufzeiten für jeden Task, sieht der Beobachter praktisch keine Unterbrechungen von Tasks. Auch hier können Prioritäten benutzt werden, aber z.B. Zeiten für Warteschleifen können nicht genau berechnet werden. Das Unterbrechen von Tasks ist auch per Software-Interrupts möglich.<br />
<br />
== Unterprogramm ==<br />
<br />
Unterprogramm wird durch übergeordnetes Programmteil (Aufrufer) mit "call" aufgerufen und nach seinem Ausführen, wird zurück zum Aufrufer in die Zeile nach dem "call" gesprungen. Der Rückkehr zum Aufrufer wird durch "return" bzw. "retlw" Befehl, der sich am Ende jedes UPs befinden muss, erreicht. Und das ist der einzige Unterschied zwischen einem HP und einem UP.<br />
<br />
Jedes UP hat folgender PAD:<br />
<br />
vom Aufrufer ------------>V<br />
Tun<br />
V<br />
zurück zum Aufrufer <----------return bzw. retlw <br />
<br />
Bei dem Rückkehr aus dem UP mit "retlw" befindet sich im W-Register der Wert aus dem Befehl "retlw". Dies wird benutzt um zu erkennen aus welchem UP das verzweigte Programm zurückkommt und ermöglicht eventuelle Fehler beim Ausführung des Programmteils zu erkennen.<br />
<br />
Ein HP von einem ASM Programm kann in anderem, mehr umfangreichem ASM Programm als UP benutzt werden, wenn der sich am Ende des HPs befindlicher Befehl "goto" durch "return" ersetzt wird. Ein Beispiel dazu:<br />
<br />
Haupt1 call UP11 Haupt1 call UP11<br />
call UP21 call UP21<br />
........... -------> ...........<br />
call UPn1 call UPn1 <br />
goto Haupt1 return <br />
<br />
Jetzt können wir im mehr komplexen HP (Haupt) das Haupt1 als Unterprogramm aufrufen:<br />
<br />
Haupt call UP1 <br />
call Haupt1<br />
...........<br />
call UPn<br />
goto Haupt<br />
<br />
Jedes UP kann auch von einem anderen übergeordneten UP aufgerufen werden, wenn das was es realisiert, benötigt wird.<br />
<br />
In der Praxis wird oft ein UP von mehreren anderen UPs benutzt. Zum Beispiel um LCD Display zu steuern, brauchen wir entweder ein Befehl (Cmd) oder ein Zeichen (Data) an Display zu schicken. In beiden Fällen wird ein Byte geschickt, einmal mit RS=0 (Befehl) und einmal mit RS=1 (Zeichen) laut folgendem PAD:<br />
<br />
"Cmd" "Data" <br />
RS=0 RS=1<br />
V V <br />
`-->V<--´<br />
"Send" Byte schicken<br />
V<br />
return<br />
<br />
Das wird in den Quellcode z.B. so eingeschrieben:<br />
<br />
Cmd bcf RS<br />
goto Send<br />
Data bsf RS<br />
Send ............<br />
return<br />
<br />
Das UP "Send" ist den UPs "Cmd" und "Data" untergeordnet, da es von beiden benutzt wird, kann aber weder "Cmd" noch "Data" benutzen.<br />
<br />
=== Initialisierung ===<br />
<br />
Damit der PIC ein Programm ausführen kann, muss er vollständig und richtig konfiguriert und initialisiert werden. Deswegen als erstes UP, das von dem gesamten Programm noch vor dem HP aufgerufen wird , ist "Initialisierung" (kurz: Init)<br />
<br />
==== Variablen ====<br />
<br />
Weil sich nach dem Einschalten der Spannung im RAM zufällige Werte befinden, wird oft als erstes der benutzte Bereich des RAMs (z.B. 20h bis 7Fh) gelöscht. Es wird einfach und sparsam mit einer Schleife erledigt, die indirekte Adressierung verwendet:<br />
<br />
V<br />
Adresse des ersten Registers in FSR laden (20h)<br />
.-------------------->V<br />
RAMClr |Indirekt adressierter Register löschen (INDF)<br />
| Adresse erhöhen<br />
| Letzte Adresse + 1 = 80h J > Return<br />
| N<br />
| V<br />
`---------------------´<br />
<br />
Es wird wie folgt in Quellcode eingeschrieben:<br />
<br />
movlw 0x20<br />
movwf FSR<br />
RAMClr ,->clrf INDF<br />
| incf FSR,1<br />
| btfss FSR,7<br />
`-<goto RAMClr<br />
return<br />
<br />
Um Anzahl den Marken (label) zu verringern, wird oft ein Symbol "$" benutzt. Es bedeutet die Adresse der aktuellen Befehlszeile. Es kann also auch so geschrieben werden:<br />
<br />
movlw 0x20<br />
movwf FSR<br />
,->clrf INDF<br />
| incf FSR,1<br />
| btfss FSR,7<br />
`-<goto $-3 ; springe zu aktueller Adresse -3<br />
return<br />
<br />
Danach können den benötigten Variablen die gewünschten Werte zugewiesen werden:<br />
<br />
movlw 0x3C<br />
movwf LimH<br />
movlw 0x5A<br />
movwf LimL<br />
u.s.w.<br />
<br />
Somit sind die Variablen initialisiert.<br />
<br />
==== I/O Ports ====<br />
<br />
Nach dem Einschalten der Spannung sind die für Komparatoren oder A/D Wandler benutzte Pins als analoge Eingänge initialisiert. Wenn sie alle als digitale I/Os verwendet werden sollen, müssen sie als solche definiert werden. Das geschieht durch Schreiben des Wertes 7 in das entsprechende Register (CMCON bzw. ADCON1):<br />
<br />
movlw 7 bzw. movlw 7 <br />
movwf CMCON movwf ADCON1<br />
<br />
Wenn einige als analoge Eingänge benutzt werden sollen, müssen die entsprechende Werte dem Datenblatt des jeweiligen PICs entnommen werden. <br />
<br />
Danach werden in alle Ports nacheinander die gewünschte Werte die an den Pins vor dem Start des Hauptprogramms ausgegeben werden sollen, geschrieben:<br />
<br />
clrf PORTA<br />
movlw 0x37<br />
movwf PORTB <br />
usw.<br />
<br />
Anschließend werden für jeden Port die Werte in TRISx Register eingeschrieben, wobei ein Bit einem Pin entspricht. Ein Pin wird in TRISx Register durch 1 als Eingang und durch 0 als Ausgang definiert. Beispielweise beim PORTB sollen B7,B5 und B3 als Eingänge und restliche Pins als Ausgänge definiert werden. Das ergibt den Wert 10101000b = A8h, der in den TRISB Register geschrieben werden muss. Weil die alle TRISx Register sich in der höheren Bank befinden, muss im STATUS-Register auf entsprechende Bank und danach zurück auf Bank 0 umgeschaltet werden. Zum Beispiel für die TRISB in der Bank1:<br />
<br />
bsf STATUS,RP0<br />
movlw 0xA8<br />
movwf TRISB<br />
bcf STATUS,RP0<br />
<br />
Bei einem Umschalten der Bank können selbstverständlich alle TRISx Register, die in der gleichen Bank liegen, nacheinander beschrieben werden. Siehe : [[#PORTx|PORTx]] und [[#TRISx|TRISx]]<br />
<br />
Bei dem PORTA gibt es Pins, die nur "open drain" Ausgang haben (können nur auf GND schalten) bzw. nur als Eingang benutzt werden können. Bei Planung der Verwendung von PORTA muss immer im Datenblatt geprüft werden, ob sich ein bestimmter Pin für die geplante Anwendung eignet.<br />
<br />
==== Hardware ====<br />
<br />
Die für ASM Programm benutzte Hardware kann auf integrierte und externe geteilt werden. Für eine Initialisierung der integrierten Hardware (Komparatoren, A/D Wandler, Timer, USART, I²C, SPI, PWM, usw.), müssen entsprechende SFRs (Spezial Function Registers) laut Datenblatt des PICs definiert werden.<br />
<br />
Die externe Hardware muss nach Datenblättern der Hersteller initialisiert werden.<br />
<br />
=== Einlesen ===<br />
<br />
Um ein Bit von einem Portpin einzulesen und in ein bestimmtes Register zu Kopieren wird folgender PAD benutzt, weil ein PIC kein Befehl dafür hat:<br />
<br />
V<br />
Quellbit = 0 ? N >------.<br />
J |<br />
V |<br />
Bit im Zielregister löschen |<br />
V<-------------´<br />
Quellbit = 1 ? N >------.<br />
J |<br />
V |<br />
Bit im Zielregister setzen |<br />
V<-------------´<br />
<br />
Wenn wir z.B. ein bit3 von PortA als bit1 in den Register Tasten kopieren wollen, dann wird es in Quellcode so geschrieben:<br />
<br />
btfss PORTA,3<br />
bcf Tasten,1<br />
btfsc PORTA,3<br />
bsf Tasten,1<br />
<br />
Wenn es zulässig ist, dass sich das Bit im Zielregister kurzzeitig ändert, kann man sich die erste Zeile im Quellcode ersparen:<br />
V<br />
Bit im Zielregister löschen<br />
Quellbit = 0 ? J >------.<br />
N |<br />
V |<br />
Bit im Zielregister setzen |<br />
V<-------------´<br />
<br />
bcf Tasten,1<br />
btfsc PORTA,3<br />
bsf Tasten,1<br />
<br />
Wenn ein ganzes Byte vom Port in das W-Register eingelesen wird, kann man den Wert gleich komplett in das Zielregister schreiben:<br />
<br />
movf PORTA,0<br />
movwf Tasten<br />
<br />
=== Ausgeben ===<br />
<br />
Um ein Bit an einem Portpin auszugeben wird ein bestimmter Bit mit "bcf" gelöscht oder mit "bsf" gesetzt. Zum Beispiel bit4 im PORTA:<br />
<br />
bcf PORTA,4.<br />
<br />
Um ein Byte auszugeben wird es zuerst in das W-Register geladen und danach an den Port übergeben, z.B.:<br />
<br />
movlw 0x12<br />
movwf PORTA<br />
<br />
=== Schleifen ===<br />
<br />
Ein in ASM Programmen meist verbreitetes Codefragment ist eine Schleife.<br />
<br />
Es kann eine endlose Schleife, die durch Prüfung einer bestimmten Bedingung (z.B. Taste) unterbrochen wird, sein:<br />
<br />
V<-----.<br />
Tun |<br />
? N >--´<br />
J<br />
V<br />
weiter<br />
<br />
Es kann eine Schleife mit Schleifenzähler (z.B. Temp), die bestimmte Anzahl Durchläufe hat, sein:<br />
<br />
V<br />
Anzahl ins Temp laden <br />
V<---------. <br />
Tun |<br />
Temp decrementieren | <br />
Temp = 0 ? N >--´<br />
J<br />
V<br />
weiter <br />
<br />
Solche Schleifen können als UPs verwendet werden, wenn "weiter" mit "return" ersetzt wird.<br />
<br />
Das waren Beispiele für Schleifen, die bewusst programmiert sind. Es gibt leider auch endlose Schleifen, die durch einen Fehler im Programm enstanden sind und zum "hängen" des Programms führen. Solche Schleifen sind im Programm nicht einfach zu finden, da ihre Parameter unbekannt sind. In dem Fall sehr behilflich ist der [[#PIC RAM Monitor|PIC RAM Monitor]] bzw. [[#PIC Trainer|PIC Trainer]].<br />
<br />
=== Pause ===<br />
<br />
Um eine Pause (Warten, Verzögerung) im Programm anzulegen wird der "nop" Befehl benutzt, während dessen Ausführung der CPU nichts macht. Mit einem "nop" kann eine Zeit gleich 4 Takten (Perioden) des Oszillators realisiert werden.<br />
<br />
Für sehr kurze Wartezeiten benutzt man eine Reihe von nachfolgenden "nop"s. Beim einem Oszillator 4 MHz, die Ausführungszeit des Prozessors beträgt 1 µs pro "nop". Wenn z.B. 4 µs benötigt werden, werden dafür 4 "nop"s gebraucht:<br />
<br />
...<br />
nop<br />
nop<br />
nop<br />
nop<br />
...<br />
<br />
Das gleiche bewirken 2 "goto"s, brauchen aber im Vergleich zu "nop"s nur die Hälfte der Bytes im Programmspeicher:<br />
<br />
<br />
...<br />
goto $+1<br />
goto $+1<br />
...<br />
<br />
Der Befehl goto $+1 bedeutet "springe zur aktuellen Adresse + 1" (also nächster Adresse) und seine Ausführungszeit ist gleich 2 Prozessortakten.<br />
<br />
Für kurze Zeiten werden s.g. Warteschleifen, wie im folgendem PAD benutzt:<br />
<br />
V<br />
n * nop<br />
P0 laden<br />
V<---------.<br />
P0 decrementieren |<br />
P0 = 0 ? N >--´<br />
J<br />
V<br />
<br />
In Quellcode wird es als UP so eigeschrieben: <br />
<br />
Warte nop Warte nop<br />
... ...<br />
nop nop <br />
movlw 0xXX movlw 0xXX<br />
movwf P0 movwf P0 <br />
Warte0 decfsz P0,1 decfsz P0,1<br />
goto Warte0 goto $-1<br />
return return<br />
<br />
Der Sprung zur Marke "Warte0" ("goto Warte0") wurde in rechtem Beispiel durch "goto $-1" ersetzt.<br />
<br />
Die gesammte Anzahl den CPU Takten (N) lässt sich aus folgender Formel berechnen:<br />
<br />
N = 3 * P0 + 6 + n <br />
<br />
und die Wartezeit (T) in Sekunden:<br />
<br />
T = N * ( 4 / Fosc ), für Schätzungen T ~ 3 * P0 * ( 4 / Fosc ) <br />
<br />
Wobei:<br />
<br />
P0 = Zahl im Register P0, 6 = Ausführungszeit von "call" (2) + "movlw" (1) + "movwf" (1) + "return" (2), n = Anzahl "nop"s, Fosc = Frequenz des Oszillators (z.B. Quartz)<br />
<br />
Wenn in ein Register PX eine 0 eingeschrieben wird, bedeutet es, dass die decrementierung des Registers 100h = 256d Takten dauern wird, da dekrementierte 0 eine FFh = 255d ergibt. Deshalb in Warteschleifen ist die Zahl 0 die grösste (256 Dürchläufe) und 1 die kleinste. Für solche einfache Schleifen (als UP) die Wartezeit beträgt min. 9 und max. ca. 800 Prozessortakten (ohne "nop"s). <br />
<br />
Für mittlere Wartezeiten z.B. 0,1 Sekunde werden doppelte Warteschleifen verwendet:<br />
<br />
Warte V<br />
n * nop<br />
P1 laden<br />
Warte1 V<-------------.<br />
P0 laden |<br />
Warte0 V<---------. |<br />
P0 decrementieren | |<br />
P0 = 0 ? N >--´ |<br />
J |<br />
V |<br />
P1 dekrementieren |<br />
P1 = 0 ? N >------´<br />
J<br />
V<br />
<br />
Das wird in Quellcode als UP so aussehen:<br />
<br />
Warte nop Warte nop<br />
... ...<br />
nop nop<br />
movlw 0xXX movlw 0xXX<br />
movwf P1 movwf P1 <br />
Warte1 movlw 0xXX movlw 0xXX<br />
movwf P0 movwf P0<br />
Warte0 decfsz P0,1 decfsz P0,1<br />
goto Warte0 goto $-1<br />
decfsz P1,1 decfsz P1,1<br />
goto Warte1 goto $-5<br />
return return<br />
<br />
Wie vorher wurden rechts die Marken durch "$-n" ersetzt. <br />
<br />
Die gesammte Anzahl den CPU Takten (N) lässt sich aus folgender Formel berechnen:<br />
<br />
N = P1 * (3 * P0 + 5) + 8 + n<br />
<br />
und die Wartezeit (T) in Sekunden:<br />
<br />
T = N * ( 4 / Fosc ), für Schätzungen T ~ 3 * P1 * P0 * ( 4 / Fosc )<br />
<br />
Wobei:<br />
<br />
P0 = Zahl im Register P0, P1 = Zahl im Register P1, 8 = Ausführungszeit von "call" + "return" + 2 * ("movlw" + "movwf"), n = Anzahl "nop"s, Fosc = Frequenz des Oszillators (z.B. Quartz)<br />
<br />
Für solche doppelte Schleifen (als UP) die Wartezeit beträgt min. 16 und max. ca. 200 000 Prozessortakten (ohne "nop"s). <br />
<br />
Für lange Wartezeiten z.B. 1 Sekunde werden 3-fache Warteschleifen angewendet.<br />
<br />
Solche Warteschleife ist im folgenden PAD abgebildet:<br />
<br />
Warte V<br />
n * nop<br />
P2 laden<br />
Warte2 V<-----------------.<br />
P1 laden |<br />
Warte1 V<-------------. |<br />
P0 laden | |<br />
Warte0 V<---------. | |<br />
P0 decrementieren | | |<br />
P0 = 0 ? N >--´ | |<br />
J | |<br />
V | |<br />
P1 decrementieren | |<br />
P1 = 0 ? N >------´ |<br />
J |<br />
V |<br />
P2 dekrementieren |<br />
P2 = 0 ? N >----------´<br />
J<br />
V<br />
<br />
Das wird in Quellcode als UP so aussehen:<br />
<br />
Warte nop Warte nop<br />
... ...<br />
nop nop<br />
movlw 0xXX movlw 0xXX<br />
movwf P2 movwf P2<br />
Warte2 movlw 0xXX movlw 0xXX<br />
movwf P1 movwf P1 <br />
Warte1 movlw 0xXX movlw 0xXX<br />
movwf P0 movwf P0<br />
Warte0 decfsz P0,1 decfsz P0,1<br />
goto Warte0 goto $-1<br />
decfsz P1,1 decfsz P1,1<br />
goto Warte1 goto $-5<br />
decfsz P2,1 decfsz P2,1 <br />
goto Warte2 goto $-9<br />
return return<br />
<br />
Auch hier wurden rechts die Marken durch "$-n" ersetzt. <br />
<br />
Die gesammte Anzahl den CPU Takten (N) lässt sich aus folgender Formel berechnen:<br />
<br />
N = P2 * [ P1 * (3 * P0 + 5) + 9 ] + 10 + n<br />
<br />
und die Wartezeit (T) in Sekunden:<br />
<br />
T = N * ( 4 / Fosc ), für Schätzungen T ~ 3 * P2 * P1 * P0 * ( 4 / Fosc )<br />
<br />
Wobei:<br />
<br />
P0 = Zahl im Register P0, P1 = Zahl im Register P1, P2 = Zahl im Register P2, 10 = Ausführungszeit von "call" + "return" + 3 * ("movlw" + "movwf"), n = Anzahl "nop"s, Fosc = Frequenz des Oszillators (z.B. Quartz)<br />
<br />
Für solche 3-fache Schleifen (als UP) die Wartezeit beträgt min. 27 und max. ca. 50 000 000 Prozessortakten (ohne "nop"s). <br />
<br />
Die "nop"s vor allen Warteschleifen sind nur notwendig um genaue Wartezeit einzustellen zu können. Sie können auch mit "goto $+1"s ersetzt, oder weg gelassen werden. Sie können auch innerhalb jeder Schleife eingesetzt werden, um sie deutlich zu verlängern.<br />
<br />
Ein Beispiel:<br />
<br />
V<br />
n * nop<br />
P0 laden<br />
V<---------.<br />
n0 * nop <br />
P0 decrementieren |<br />
P0 = 0 ? N >--´<br />
J<br />
V<br />
<br />
Bei nur einem "nop" (n0=1) ist die max. Wartezeit ca. T ~ 4 * P0 * ( 4 / Fosc ), bei zwei (n0=2) ca. T ~ 5 * P0 * ( 4 / Fosc ) usw. <br />
<br />
Anstatt "movlw 0xXX" kann auch "movf PauseX,0" angewendet werden, wenn die Schleife mehrmals mit verschiedenen Werten P0, P1 und P2 aus den Register Pause0, Pause1 und Pause2 benutzt wird.<br />
<br />
Für sehr lange Wartezeiten können, nach gleichem Prinzip, Warteschleifen erstellt werden, die mehr als 3 Schleifen enthalten.<br />
<br />
In zeitkritischen ASM Programmen werden anstatt Warteschleifen (z.B. für Tastenenentprellung) zusammengesetzte UPs mit benötigter Ausführungszeit (z.B. Frequenzmessung + Displayausgabe + ...) angewendet.<br />
<br />
=== Tabellen ===<br />
<br />
Es gibt zwei Arten von Tabellen: Sprungtabellen (computed goto) die "goto" Befehle enthalten und Wertetabellen (lookup table) in denen feste Werte in "retlw" gespeichert sind. Der wichtigste Unterschied zwischen denen ist, dass die Sprungtabellen steuern den Programmlauf abhängig vom Inhalt des W-Registers und die Wertetabellen liefern abhängig von Inhalt des W-Registers ein Wert an den Aufrufer zurück. <br />
<br />
Beide werden in Programmspeicher erstellt. Sie können nur bis zu 256 Speicherstellen belegen, da in den W-Register auch nur so viel verschiedenen Zahlen "passen". Sie Fangen also (fast) immer bei einer Adresse XX00h an und enden bei XXFFh. Der Hochwertige Byte "XX" der Adresse an der sich der Anfang einer Tabelle befindet, muss vor dem Einsprung in die Tabelle ins PCLATH Register eingeschrieben werden, wenn die Tabelle weit vom Aufrufer liegt. In der Praxis werden solche Tabellen am oberen Ende des Programmspeichers angelegt, damit sie den ASM Code nicht unterbrechen.<br />
<br />
Eine Sprungtabelle wird so aufgebaut:<br />
<br />
org (XX-1)FF <--- eine Direktive für Assemblerprogramm, wo es <br />
die Tabelle im Programmspeicher plazieren soll<br />
Adresse Inhalt<br />
------------------------- <br />
Tab1 (XX-1)FF addwf PCL,1<br />
XX00 goto Marke0<br />
XX01 goto Marke1<br />
.......................<br />
XXFE goto Marke254<br />
XXFF goto Marke255<br />
<br />
Und so aufgerufen:<br />
<br />
movlw 0xXX<br />
movwf PCLATH<br />
movf TWert,0<br />
call Tab1<br />
<br />
Wobei:<br />
<br />
0xXX = Hochwertiger Byte der Adresse von Tab1, TWert = ein Wert, der die Wahl wohin gesprungen wird bestimmt.<br />
<br />
Nach ausführen der obiger Befehlsfolge, wird das ASM Programm z.B. für Twert=0x01 weiter ab Marke1 "laufen" bis es an "return" kommt. Dann springt es zurück zum Aufrufer der Tabelle.<br />
<br />
Eine Sprungtabelle kann auch mit "goto" eingesprungen werden, dann wird aber beim Erreichen des "return"s bis zum letzten "call" zurückgesprungen. Siehe hierzu auch [[#PIC Trainer|PIC Trainer]].<br />
<br />
<br />
Eine Wertetabelle wird so aufgebaut:<br />
<br />
org (XX-1)FF <--- eine Direktive für Assemblerprogramm, wo es <br />
die Tabelle im Programmspeicher plazieren soll<br />
Adresse Inhalt<br />
------------------------- <br />
Tab1 (XX-1)FF addwf PCL,1<br />
XX00 retlw Wert0<br />
XX01 retlw Wert1<br />
.......................<br />
XXFE retlw Wert254<br />
XXFF retlw Wert255<br />
<br />
Und so aufgerufen:<br />
<br />
movlw 0xXX<br />
movwf PCLATH<br />
movf TWert,0<br />
call Tab1<br />
<br />
wobei:<br />
<br />
0xXX = Hochwertiger Byte der Adresse von Tab1, TWert = ein Wert im W-Register, für welchen, an den Aufrufer bestimmter Wert aus der Tabelle im W-Register zurückgeliefert wird.<br />
<br />
Wertetabelle kann auch mit Hilfe der MPASM Direktive "dt" erstellt werden. So kann man sich mehrfaches Schreiben von "retlw" Befehlen ersparen:<br />
<br />
org 0x(XX-1)FF<br />
Tabelle addwf PCL,1 <br />
; nachfolgend der benötigte Wert im W- Register<br />
; vor dem Aufruf der Tabelle<br />
dt 3, 0xE8 ; 0x00<br />
dt 5, 0xB7 ; 0x02<br />
dt 'M','H','z',0 ; 0x04<br />
dt ' ',' ','W','A','I','T',0 ; 0x08<br />
dt 'C','o','n','s','t',' ','X',0 ; 0x0F<br />
dt 'C','=',0 ; 0x17<br />
dt 'L','=',0 ; 0x1A<br />
dt 'F','=',0 ; 0x1D<br />
dt 'O','K',0 ; 0x20<br />
usw.<br />
<br />
Das Lesen eines Strings muss ab entsprechendem Wert im W-Register (z.B. für "MHz" -> 0x04) anfangen und auf dem Wert 0 (Null) enden, der hier als Stringende dient. Einfacher ist, wenn alle Strings gleich lang sind (wie z.B. in einem Zeichengenerator für Grafikdisplay).<br />
<br />
Bei Tabellen, die länger als 256 Byte sind, muss immer PCLATH an den Seitengrenzen korriegiert werden, wie auf der 3. Seite der Applikation Note AN556 vom Microchip vorgestellt: http://ww1.microchip.com/downloads/en/AppNotes/00556e.pdf .<br />
<br />
=== Interrupt ===<br />
<br />
==== Prinzip ====<br />
<br />
Ein Interrupt (Unterbrechung) unterbricht ein laufendes Programm, wenn ein bestimmtes Ereigniss, auf das reagiert werden soll, statt gefunden hat. Die Reaktion wird in der sogenannten Interrupt Service Routine (kurz: ISR) definiert.<br />
<br />
Einfach gesagt, ein Interrupt stoppt ein laufendes Programm nach dem Ausführen der aktuellen Zeile, ruft die ISR auf und nach deren Ausführung startet das Programm wieder, ab der Zeile, vor der es durch Interrupt angehalten wurde. <br />
<br />
.--->V<br />
| Tun<br />
| V<br />
Interrupt ------>Tun-------->V<br />
| V ISR<br />
| Tun<--------´<br />
| V<br />
| Tun<br />
| V <br />
`----´<br />
<br />
Damit das unterbrochene Programm nach dem Rückkehr aus der ISR weiter fehlerfrei ausgeführt werden kann, darf die ISR keine Änderungen in vom Programm benutzten Register verursachen.<br />
Deswegen wenn UPs aus dem Programm durch ISR benutzt werden, müssen alle Register, dessen Inhalt durch ISR geändert wird, vor dem Ausführen der ISR (als ersten Befehle der ISR nach dem "bcf INTCON,GIE") im RAM gespeichert und nach der Ausführung der ISR (vorm "retfie") wiederhergestellt werden.<br />
<br />
Um einen Interrupt auslösen zu können, muss er vorher, durch beschreiben nötigen Register (INTCON, PIR, usw.) vorbereitet werden. Siehe hierzu: [[#INTCON|INTCON]] und [[#Interrupts|Interrupts]]<br />
<br />
==== Quellen ====<br />
<br />
Es gibt folgende Interrupt-Quellen:<br />
<br />
- interne Hardware (z.B. ein Timer)<br />
<br />
- externe Hardware (z.B. eine Taste)<br />
<br />
Die PICs der Mid-Range Familie haben im Programmspeicher nur eine Adresse (s.g. Interrupt Vektor 0x0004), wohin im Falle eines Interrupts, gesprungen wird. Um den Verursacher des Interrupts zu finden, hat jede Quelle ein eigenes Interrupt Flag (IF), das gesetzt wird, wenn der Interrupt ausgelöst wird. Somit kann in der ISR nach der Prüfung der IFs auf jeden Interrupt gezielt reagiert werden. Siehe hierzu: [[#Interrupts|Interrupts]].<br />
<br />
==== Interrupt Service Routine ====<br />
<br />
Die Interrupt Service Routine ist ein besonderes UP das für Basic-Line und Mid-Range immer an der Adresse 0x0004 beginnt und immer mit dem Befehl "retfie" endet. Ihr Umfang ist von der Menge der Quellen und der Reaktionen auf ihre Interrupts abhängig. Folgender PAD zeigt ein allgemeiner Aufbau der ISR:<br />
<br />
org 0x0004<br />
Interrupts sperren ; bcf INTCON,GIE<br />
Beeinflüsste Register sichern<br />
Interrupt-Quelle ermitteln<br />
Interrupt-Flag löschen<br />
und entsprechend reagieren<br />
Beeinflüsste Register restaurieren<br />
zurück ins Programm springen ; retfie<br />
<br />
Je nach Bedürfnissen, wird eine ISR nur nötige Elemente davon enthalten. Um auf alle Interrupts reagieren zu können muss die ISR vor dem nächsten Interrupt beendet werden. Da das Programm nur in den Pausen zwischen Ausführungen der ISR laufen kann, sollte die Ausführungszeit der ISR möglichst kurz sein. <br />
<br />
Die kürzeste ISR setzt nur ein Flag ("_Fint"), das dem Programm zeigt, dass ein Interrupt statt gefunden hat. Sie eignet sich für den Fall, wenn nur eine Interrupt-Quelle erlaubt ist. Das Programm wird für nur 5 Prozessortakten unterbrochen.<br />
<br />
org 0x0004<br />
bcf INTCON,GIE ; Interrupts sperren<br />
bcf IF ; Interrupt Flag löschen<br />
bsf _Fint ; Flag setzen<br />
retfie ; zurück ins Programm<br />
<br />
Das Programm prüft das Flag "_Fint", löscht es und reagiert entsprechend. Somit kann ein Interrupt im Hauptprogramm bearbeitet werden. <br />
<br />
Die ISR kann aber auch ein HP darstellen. Siehe hierzu: [[#Interrupts|Interrupts]]<br />
<br />
=== Schnittstellen und Treiber ===<br />
<br />
Als Schnittstelle wird externe Hadware, die zum Steuern eines an sie angeschlossenes "Gerätes" (z.B. eines Displays) dient, genannt. Das ASM Programmteil, das die Steuerung ermöglicht ist ein Treiber. Als Beispiele siehe: [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=13685]] und [http://www.roboternetz.de/phpBB2/viewtopic.php?t=22749]<br />
<br />
== Vorlage für MPASM ==<br />
<br />
Diese Vorlage ist nur für ASM Programme ohne Interrupts geeignet:<br />
<br />
list P=12F629 ; Prozessor definieren<br />
include "P12F629.inc" ; entsprechende .inc Datei für MPASM<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _MCLRE_OFF & _INTRC_OSC_NOCLKOUT ; Konfiguration<br />
#define _DTT1 GPIO,0 ; Portpins benennen<br />
#define _CKT2 GPIO,1<br />
#define _T3 GPIO,2<br />
#define _RNG GPIO,3<br />
#define _INT GPIO,4<br />
#define _RL GPIO,5<br />
usw.<br />
SecondL equ 0x20 ; Variablen definieren (Register benennen)<br />
SecondH equ 0x21<br />
MinuteL equ 0x22<br />
MinuteH equ 0x23<br />
StundeL equ 0x24<br />
StundeH equ 0x25<br />
usw.<br />
org 0x0000 ; bis da sind MPASM Direktiven<br />
; hier fängt das gesamte ASM Programm an<br />
call Init ; rufe UP "Init" (Initialisierung) auf<br />
Haupt ............ ; hier fängt das Hauptprogramm als endlose Schleife an<br />
Eigener Code<br />
............<br />
goto Haupt ; hier endet das HP, gehe zum Anfang des HPs (zurück)<br />
UP1 ............ ; Unterprogramme<br />
Eigener Code<br />
............<br />
return<br />
############<br />
UPn ............<br />
Eigener Code<br />
............<br />
return<br />
Init clrf GPIO ; lösche Port<br />
bsf STATUS,RP0 ; auf Bank1 umschalten<br />
call 0x3FF ; hole Kalibrationswert<br />
movwf OSCCAL ; kalibriere internen RC oscillator<br />
bcf OPTION_REG,7 ; aktiviere pull-ups<br />
movlw 0x38 ; definiere Portpins GPIO, (z.B. 0-2 Aus- und 3-5 Eingänge)<br />
movwf TRISIO ; schreibe in TRIS Register<br />
bcf STATUS,RP0 ; auf Bank0 umschalten<br />
movlw 7 ; schalte Komparator aus<br />
movwf CMCON ; und definiere GPIO 0-2 als digitale I/Os<br />
............<br />
eigener Code<br />
............<br />
return ; springe zurück (zum Haupt)<br />
; hier endet das gesamte ASM Programm <br />
end ; MPASM Direktive<br />
<br />
Die Variablen können auch kürzer mit s.g. cblock definiert werden:<br />
<br />
cblock 0x20 <br />
SecondL<br />
SecondH<br />
MinuteL<br />
MinuteH<br />
StundeL<br />
StundeH<br />
endc<br />
<br />
Bei sehr vielen Variablen sind aber die Registeradressen nicht so übersichtlich.<br />
<br />
Für Programme mit Interrupts ist diese Vorlage geeignet:<br />
<br />
list P=12F629 ; Prozessor definieren<br />
include "P12F629.inc" ; entsprechende .inc Datei für MPASM<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _MCLRE_OFF & _INTRC_OSC_NOCLKOUT ; Konfiguration<br />
#define _DTT1 GPIO,0 ; Portpins benennen<br />
#define _CKT2 GPIO,1<br />
#define _T3 GPIO,2<br />
#define _RNG GPIO,3<br />
#define _INT GPIO,4<br />
#define _RL GPIO,5<br />
usw. <br />
SecondL equ 0x20 ; Variablen definieren (Register benennen)<br />
SecondH equ 0x21<br />
MinuteL equ 0x22<br />
MinuteH equ 0x23<br />
StundeL equ 0x24<br />
StundeH equ 0x25<br />
usw.<br />
org 0x0000 ; bis da sind MPASM Direktiven<br />
; hier fängt das gesamte ASM Programm an<br />
call Init ; rufe UP "Init" (Initialisierung) auf<br />
goto Haupt ; gehe zum Hauptprogramm "Haupt" <br />
org 0x004 ; hier fängt die ISR (interrupt service routine) an<br />
bcf INTCON,GIE ; interrupts sperren<br />
.............<br />
Eigener Code<br />
.............<br />
retfie ; hier endet die ISR, interrupts wieder erlauben<br />
Haupt ............ ; hier fängt das Hauptprogramm als endlose Schleife an<br />
Eigener Code<br />
............<br />
goto Haupt ; hier endet das HP, gehe zum Anfang des HPs (zurück)<br />
UP1 ............ ; Unterprogramme<br />
Eigener Code<br />
............<br />
return<br />
############<br />
UPn ............<br />
Eigener Code<br />
............<br />
return<br />
Init clrf GPIO ; lösche Port<br />
bsf STATUS,RP0 ; auf Bank1 umschalten<br />
call 0x3FF ; hole Kalibrationswert<br />
movwf OSCCAL ; kalibriere internen RC oscillator<br />
bcf OPTION_REG,7 ; aktiviere pull-ups<br />
movlw 0x38 ; definiere Portpins GPIO, (z.B. 0-2 Aus- und 3-5 Eingänge)<br />
movwf TRISIO ; schreibe in TRIS Register<br />
bcf STATUS,RP0 ; auf Bank0 umschalten<br />
movlw 7 ; schalte Komparator aus<br />
movwf CMCON ; und definiere GPIO 0-2 als digitale I/Os<br />
............<br />
eigener Code<br />
............<br />
return ; springe zurück (zum Haupt)<br />
; hier endet das gesamte ASM Programm <br />
end ; MPASM Direktive<br />
<br />
== Das erste Programm ==<br />
<br />
Hier wird das ganze Prozess der Erstellung eines ASM Programms detailiert beschrieben. Die Idee:<br />
<br />
Es gibt 4 LEDs (_L1, _L2, _L3 und _L4), die mit 2 Tastern gesteuert werden sollen. Nach dem Einschalten soll keine LED leuchten. Solange der linke Taster (_T1) gedrückt ist, sollte eine leuchtende LED von links nach rechts "wandern" und von der letzten rechten Position wieder nach ganz linke "springen". Solange der rechte Taster (_T2) gedrückt ist, sollte eine leuchtende LED von rechts nach links "wandern" und von der letzten linken Position wieder nach ganz rechte "springen". Solange beide Taster gedrückt sind soll die leuchtende LED von links nach rechts und zurück "wandern".<br />
<br />
Dafür nötige Hardware zeigt folgende Skizze:<br />
<br />
.-----------------------------------------------.<br />
| |<br />
| PIC12F629 |<br />
| |<br />
| GPIO,3 GPIO,4 GPIO,5 GPIO,2 GPIO,1 GPIO,0|<br />
'-----------------------------------------------'<br />
4| 3| 2| 5| 6| 7|<br />
| | .-. .-. .-. .-.<br />
| | R | | R | | R | | R | |<br />
| | 470| | 470| | 470| | 470| |<br />
| | '-' '-' '-' '-'<br />
\ o \ o | | | |<br />
\ \ V -> V -> V -> V -><br />
\. \. - - - -<br />
_T1 o _T2 o _L1 | _L2 | _L3 | _L4 |<br />
| | | | | |<br />
+-------+-------+---+---+-------+-------+<br />
|<br />
===<br />
GND<br />
<br />
Weil der Pin 4 (GPIO,3) nur einen "open drain" Ausgang hat (kann nur an GND schalten), wird er hier als Eingang benutzt.<br />
<br />
Jetzt muss die Idee vom Programmierer in ein PAD verfasst werden, z.B. solcher:<br />
<br />
Start<br />
Initialisierung<br />
.-------------->V<br />
| _T1=0 gedrückt ? N >----.<br />
| J |<br />
| V |<br />
| links->rechts "wandern" |<br />
| V<------------´<br />
| _T2=0 gedrückt ? N >----.<br />
| J |<br />
| V |<br />
| rechts->links "wandern" |<br />
| V<------------´<br />
`---------------´<br />
<br />
danach detailierter:<br />
<br />
Start<br />
Initialisierung<br />
V<-------------------------------------------.<br />
_T1=0 gedrückt ? N >---+--->_T2=0 gedrückt ? N >---+---´<br />
J A J A<br />
V | V |<br />
_L1 an | _L4 an |<br />
Warten | Warten |<br />
_L1 aus | _L4 aus |<br />
_L2 an | _L3 an |<br />
Warten | Warten |<br />
_L2 aus | _L3 aus |<br />
_L3 an | _L2 an |<br />
Warten | Warten |<br />
_L3 aus | _L2 aus |<br />
_L4 an | _L1 an |<br />
Warten | Warten |<br />
_L4 aus | _L1 aus |<br />
V | V |<br />
`------------´ `------------´<br />
<br />
Weil das Assemblerprogram (z.B. MPASM) und der Prozessor (CPU) die Befehle nacheinander liest, ist jeder Quellcode einspaltig. Um sich die "Übersetzung" des PADs in den Quellcode zu erleichtern wird er noch z.B. so umgestaltet:<br />
<br />
Start<br />
Initialisierung<br />
V<-----------------+<----.<br />
Haupt _T1=0 gedrückt ? N >---. A | <br />
J | | |<br />
V | | |<br />
_L1 an | | |<br />
Warten | | |<br />
_L1 aus | | |<br />
_L2 an | | |<br />
Warten | | |<br />
_L2 aus | | |<br />
_L3 an | | |<br />
Warten | | |<br />
_L3 aus | | |<br />
_L4 an | | |<br />
Warten | | |<br />
_L4 aus | | |<br />
V<-----------´ | |<br />
T2Test _T2=0 gedrückt ? N >---------´ |<br />
J |<br />
V |<br />
_L4 an |<br />
Warten |<br />
_L4 aus |<br />
_L3 an |<br />
Warten |<br />
_L3 aus |<br />
_L2 an |<br />
Warten |<br />
_L2 aus |<br />
_L1 an |<br />
Warten |<br />
_L1 aus |<br />
V |<br />
`------------------------´<br />
<br />
Alle Pfeilen aus diesem PAD werden als "goto" in den Quellcode eingetragen.<br />
<br />
Zuerst wird im MPASM Verzeichnis ein neuer Verzeichniss (z,B. "PIC Programme") angelegt und in dem Verzeichniss neue Textdatei (z.B. "Erstes.txt") erstellt. Als nächstes wird die "Vorlage für MPASM" dorthin kopiert.<br />
<br />
Danach müssen, die im Programm benutzte Portpins und Register mit "#define" und "equ" definiert werden.<br />
<br />
Als Initialisierung wird das UP "Init" aus der Vorlage für MPASM mit geändertem "TRISIO" Wert (0x18) übernommen. Siehe: [[#Vorlage für MPASM|Vorlage für MPASM]]<br />
<br />
Fürs "Warten" wird eine dreifache Warteschleife verwendet. Siehe: [[#Pause|Pause]]<br />
<br />
Die Taster werden mit "btfsc" Befehl geprüft und zum LEDs An- und Ausschalten werden Befehle "bsf" und "bcf" für ensprechenden Portpin von GPIO angewendet. Siehe: [[#Einlesen|Einlesen]] und [[#Ausgeben|Ausgeben]] <br />
<br />
Anschlessend wird alles laut PAD in die Vorlage für MPASM eingeschrieben und der Quellcode ist fertig:<br />
<br />
; Erstes.asm<br />
list P=12F629 ; Prozessor definieren<br />
include "P12F629.inc" ; entsprechende .inc Datei für MPASM<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _MCLRE_OFF & _INTRC_OSC_NOCLKOUT ; Konfiguration <br />
#define _T1 GPIO,3 ; Portpins benennen<br />
#define _T2 GPIO,4<br />
#define _L1 GPIO,5<br />
#define _L2 GPIO,2<br />
#define _L3 GPIO,1<br />
#define _L4 GPIO,0<br />
P0 equ 0x20 ; Variablen definieren (Register benennen)<br />
P1 equ 0x21<br />
P2 equ 0x22<br />
org 0x0000 ; bis da sind MPASM Direktiven<br />
; hier fängt das gesamte ASM Programm an<br />
call Init ; rufe UP "Init" (Initialisierung) auf<br />
Haupt btfsc _T1 ; Taster _T1=0 gedrückt ?, wenn ja, überspringe "goto T2Test"<br />
goto T2Test ; wenn nicht, springe zu "T2Test"<br />
bsf _L1 ; schalte _L1 an (setze den Pin 2 auf "1")<br />
call Warten ; warte ca. 0,4 s (ca. 400 000 Prozessortakten)<br />
bcf _L1 ; schalte _L1 aus (setze den Pin 2 auf "0")<br />
bsf _L2 ; schalte _L2 an (setze den Pin 5 auf "1")<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus (setze den Pin 5 auf "0")<br />
bsf _L3 ; schalte _L3 an (setze den Pin 6 auf "1")<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus (setze den Pin 6 auf "0")<br />
bsf _L4 ; schalte _L4 an (setze den Pin 7 auf "1")<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus (setze den Pin 7 auf "0")<br />
T2Test btfsc _T2 ; Taster _T2=0 gedrückt ?, wenn ja, überspringe "goto Haupt"<br />
goto Haupt ; wenn nicht, springe zu "Haupt"<br />
bsf _L4 ; schalte _L4 an (setze den Pin 7 auf "1")<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus (setze den Pin 7 auf "0")<br />
bsf _L3 ; schalte _L3 an (setze den Pin 6 auf "1")<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus (setze den Pin 6 auf "0")<br />
bsf _L2 ; schalte _L2 an (setze den Pin 5 auf "1")<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus (setze den Pin 5 auf "0")<br />
bsf _L1 ; schalte _L1 an (setze den Pin 2 auf "1")<br />
call Warten ; warte<br />
bcf _L1 ; schalte _L1 aus (setze den Pin 2 auf "0")<br />
goto Haupt ; springe zu "Haupt"<br />
Warten movlw 2 ; schreibe "2" für ca. 0,4 s<br />
movwf P2 ; ins Register P2<br />
clrf P1 ; lösche Register P1 ("0" für 256 Durchläufe) <br />
clrf P0 ; lösche Register P0 ("0" für 256 Durchläufe)<br />
decfsz P0,1 ; dekrementiere P0 und überspringe "goto $-1" bei P0 = 0<br />
goto $-1 ; sonst gehe zur voherigen Adresse (Befehl "decfsz P0") <br />
decfsz P1,1 ; dekrementiere P1 und überspringe "goto $-4" bei P1 = 0 <br />
goto $-4 ; sonst gehe zur aktueller Adresse - 4 (Befehl "clrf P0")<br />
decfsz P2,1 ; dekrementiere P2 und überspringe "goto $-7" bei P2 = 0<br />
goto $-7 ; sonst gehe zur aktueller Adresse - 7 (Befehl "clrf P1")<br />
return ; springe zurück zum Aufrufer ("Haupt"),<br />
Init clrf GPIO ; lösche Port (setze alle werdende Ausgänge auf "0")<br />
bsf STATUS,RP0 ; auf Bank1 umschalten<br />
call 0x3FF ; hole Kalibrationswert (als "retlw" unter Adresse 0x3FF)<br />
movwf OSCCAL ; und kalibriere internen RC oscillator (4 MHz)<br />
bcf OPTION_REG,7 ; aktiviere pull-ups für die Portpins<br />
movlw 0x18 ; definiere Portpins GPIO,3 und 4 als Eingänge<br />
; und restlichen als Ausgänge (00011000b)<br />
movwf TRISIO ; schreibe in TRIS Register<br />
bcf STATUS,RP0 ; auf Bank0 umschalten<br />
movlw 7 ; schalte Komparator aus<br />
movwf CMCON ; und definiere GPIO 0-2 als digitale I/Os<br />
return ; springe zurück (zum Haupt) <br />
; hier endet das gesamte ASM Programm <br />
end ; MPASM Direktive<br />
<br />
Jetzt wird der Quellcode "Erstes.txt" in "Erstes.asm" umbenannt. Diese "*.asm" Datei können wir dem MPASM zum "Übersetzten" geben. Nach der Assemblierung sehen wir 3 Meldungen (Messages) vom MPASM. Diese befinden sich in der vom MPASM erstellter Datei "Erstes.lst" und lauten:<br />
<br />
Message[302]: Register in operand not in bank 0. Ensure that bank bits are correct.<br />
<br />
Diese Meldung kann man übersetzen als:<br />
<br />
Meldung[302]: Register im Befehl nicht in Bank 0. Vergewisse Dich, dass die Bank-Bits korrekt sind.<br />
<br />
Wir sind sicher, dass die Register "OSCCAL", "OPTION_REG" und "TRISIO" in der Bank 1 sich befinden. Wenn man aber nicht sicher ist, soll man im Datenblatt nachschauen. Die Meldungen kommen immer für alle SFRs (Special Function Register) die sich nicht in der Bank 0 befinden und können ignoriert werden.<br />
<br />
Am Ende der Datei "Erstes.lst" befinden sich noch einige Informationen über Programmspeicher:<br />
<br />
Program Memory Words Used: 52 ; benutzte Speicherstellen<br />
Program Memory Words Free: 972 ; freie Speicherstellen<br />
<br />
und die Anzahl den eventuellen Fehlern (Errors), Warnungen (Warnings) und Meldungen (Messages):<br />
<br />
Errors : 0<br />
Warnings : 0 reported, 0 suppressed<br />
Messages : 3 reported, 0 suppressed<br />
<br />
Insgesamt werden durch MPASM 4 Dateien erstellt: "*.cod", "*.err", "*.hex" und "*.lst".<br />
<br />
Wichtigste davon, falls wegen Fehler keine "*.hex" Datei erstellt wird, ist die "*.err" Datei, wo alles was dem MPASM nicht "passt" aufgelistet ist. In der "*.lst" Datei kann man sich ein Programm mit genauen Adressen für jeden Befehl anschauen.<br />
<br />
Bei Erstellung von ASM Programmen, um sich das Kompilieren zu vereinfachen, kann folgende Prozedur verwendet werden:<br />
<br />
Zuerst wird in gleichem Verzeichnis, wo sich die Sammlung Unter/Programme befindet, eine Textdatei (z.B. "Test.txt") erstellt, wo ein einspaltiges PAD mit Pfeilen nach oben und unten geschrieben/skizziert wird. In der Datei wird auch ganz oben ständig aktualisierte Liste aller Register erstellt, die in diesem Unter/Programm verwendet sind.<br />
<br />
Wenn das PAD fertig ist, wird diese Datei mit gleichem Namen als "*.asm" Datei (z.B. "Test.asm")gespeichert und alle PAD "Symbole" mit Befehlen für bestimmten PIC/Assemblerprogramm ersetzt (z.B. für MPASM werden alle Register benannt).<br />
<br />
Bei jeder Änderung wird sie gleich in beiden Dateien gemacht, damit sie am Ende wirklich aktuell sind. Letztendlich haben wir dann beide, wenn wir später etwas ändern müssen. Dank dessen braucht man ausser eventuellen Namen der UPs keine Kommentare im Programm schreiben, was seine Erstellung deutlich beschleinigt.<br />
<br />
Die während der Assemblierung vom MPASM generierte Datei "Erstes.hex" kann jetzt durch ein Brenner mit geeignetem Programm in den PIC Programmspeicher eingeschrieben werden.<br />
<br />
== Für anderen PIC umschreiben ==<br />
<br />
Die wichtigste Vorraussetzung ist, das der andere PIC2, auf dem das vorhandene ASM Programm für PIC1 laufen soll, zumindest für das ASM Programm nötige interne Hardware hat. Die Frequenz des Oszillators sollte gleich sein. Der Code benötigt ausser UP "Init" keine Änderungen.<br />
<br />
Wenn der benutzte Port vom PIC2 anderen Namen hat, muss man das im Quellcode umdefinieren, z.B.:<br />
<br />
#define PORTC PORTB<br />
#define TRISC TRISB<br />
<br />
Dann wird das Assemblerprogramm, wenn es PORTC findet, immer PORTB nehmen. Das gleiche Betrifft die "__config" Ausdrücke, die entsprechend der "*.ini" Datei für den PIC2, geändert werden müssen. <br />
<br />
Das Assemblerprogramm findet sicher alles, was ihm nicht "passt" und bringt Fehlermeldungen, auf die man entsprechend reagieren muss.<br />
<br />
Als Beispiel, schreiben wir "Erstes.asm" für den PIC16F84A um.<br />
<br />
Zum Ändern sind die ersten 3 Zeilen:<br />
<br />
list P=12F629 ; Prozessor definieren<br />
include "P12F629.inc" ; entsprechende *.inc Datei für MPASM<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _MCLRE_OFF & _INTRC_OSC_NOCLKOUT ; Konfiguration<br />
<br />
Sie werden für den PIC16F84A so aussehen:<br />
<br />
list P=16F84A ; Prozessor definieren<br />
include "P16F84A.inc" ; entsprechende *.inc Datei für MPASM <br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC ; Konfiguration<br />
<br />
Dazu muss noch "GPIO" Port umdefiniert werden, da der PIC16F84A solchen nicht hat, z.B. wegen internen pull-ups auf "B":<br />
<br />
#define GPIO PORTB<br />
#define TRISIO TRISB<br />
<br />
Die Hardware muss natürlich an entsprechende Pins angeschlossen werden:<br />
<br />
_T1 auf 12 (B3), _T2 auf 13 (B4), _L1 auf 14 (B5), _L2 auf 11 (B2), _L3 auf 10 (B1) und _L4 auf 9 (B0) <br />
<br />
Jetzt assemblieren wir die Qelldatei und der MPASM bringt in der Datei "Erstes.err" folgende Fehler:<br />
<br />
Error[113] C:\MPASM\PICPROG\PIC16F84\ERSTES.ASM 61 : Symbol not previously defined (OSCCAL)<br />
Error[113] C:\MPASM\PICPROG\PIC16F84\ERSTES.ASM 67 : Symbol not previously defined (CMCON)<br />
<br />
In dem UP "Init" müssen also noch die Befehle mit "OSCCAL" und "CMCON" Register entfernt werden, da der PIC16F84A sie nicht hat:<br />
<br />
call 0x3FF ; hole Kalibrationswert<br />
movwf OSCCAL ; kalibriere internen RC oscillator (4 MHz)<br />
<br />
und:<br />
<br />
movlw 7 ; schalte Komparator aus<br />
movwf CMCON ; und mache GPIO 0-2 als digital I/O<br />
<br />
Weiter kann schon alles übernommen werden und die Datei "Erstes.asm" für den PIC16F84A ist fertig:<br />
<br />
; Erstes.asm <br />
list P=16F84A ; Prozessor definieren<br />
include "P16F84A.inc" ; 4.000 MHz<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC<br />
#define GPIO PORTB ; Ports umbenennen<br />
#define TRISIO TRISB<br />
#define _T1 GPIO,3 ; Portpins benennen<br />
#define _T2 GPIO,4<br />
#define _L1 GPIO,5<br />
#define _L2 GPIO,2<br />
#define _L3 GPIO,1<br />
#define _L4 GPIO,0<br />
P0 equ 0x20 ; Variablen definieren (Register benennen)<br />
P1 equ 0x21<br />
P2 equ 0x22<br />
org 0x0000 ; bis da sind MPASM Direktiven<br />
; hier fängt das gesamte ASM Programm an<br />
call Init ; rufe UP "Init" (Initialisierung) auf<br />
Haupt btfsc _T1 ; Taster _T1=0 gedrückt ?, wenn ja, überspringe "goto T2Test"<br />
goto T2Test ; wenn nicht, springe zu "T2Test"<br />
bsf _L1 ; schalte _L1 an<br />
call Warten ; warte ca. 0,4 s (ca. 400 000 Prozessortakten)<br />
bcf _L1 ; schalte _L1 aus <br />
bsf _L2 ; schalte _L2 an<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus<br />
bsf _L3 ; schalte _L3 an<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus<br />
bsf _L4 ; schalte _L4 an<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus<br />
T2Test btfsc _T2 ; Taster _T2=0 gedrückt ?, wenn ja, überspringe "goto Haupt"<br />
goto Haupt ; wenn nicht, springe zu "Haupt"<br />
bsf _L4 ; schalte _L4 an<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus<br />
bsf _L3 ; schalte _L3 an<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus<br />
bsf _L2 ; schalte _L2 an<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus<br />
bsf _L1 ; schalte _L1 an<br />
call Warten ; warte<br />
bcf _L1 ; schalte _L1 aus<br />
goto Haupt ; springe zu "Haupt"<br />
Warten movlw 2 ; schreibe "2" für ca. 0,4 s<br />
movwf P2 ; ins Register P2<br />
clrf P1 ; lösche Register P1 ("0" für 256 Durchläufe) <br />
clrf P0 ; lösche Register P0 ("0" für 256 Durchläufe)<br />
decfsz P0,1 ; dekrementiere P0 und überspringe "goto $-1" bei P0 = 0<br />
goto $-1 ; sonst gehe zur voherigen Adresse (Befehl "decfsz P0") <br />
decfsz P1,1 ; dekrementiere P1 und überspringe "goto $-4" bei P1 = 0 <br />
goto $-4 ; sonst gehe zur aktueller Adresse - 4 (Befehl "clrf P0")<br />
decfsz P2,1 ; dekrementiere P2 und überspringe "goto $-7" bei P2 = 0<br />
goto $-7 ; sonst gehe zur aktueller Adresse - 7 (Befehl "clrf P1")<br />
return ; springe zurück zum Aufrufer ("Haupt"),<br />
Init clrf GPIO ; lösche Port (setze alle werdende Ausgänge auf "0")<br />
bsf STATUS,RP0 ; auf Bank1 umschalten<br />
bcf OPTION_REG,7 ; aktiviere pull-ups<br />
movlw 0x18 ; definiere Portpins GPIO,3 und 4 als Eingänge<br />
; und die restlichen als Ausgänge (00011000b) <br />
movwf TRISIO ; schreibe in TRIS Register<br />
bcf STATUS,RP0 ; auf Bank0 umschalten<br />
return ; springe zurück (zum Haupt), nach der Zeile<br />
; aus der du aufgerufen wurdest <br />
; hier endet das gesamte ASM Programm <br />
end ; MPASM Direktive<br />
<br />
Man kann auch die Portpins neu definieren und das UP "Warten" für z.B. 20 MHz Quarz anpassen:<br />
<br />
; Erstes.asm<br />
list P=16F84A ; Prozessor definieren<br />
include "P16F84A.inc" ; 20.000 MHz<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC<br />
#define _T1 PORTB,5 ; Portpins benennen<br />
#define _T2 PORTB,4<br />
#define _L1 PORTB,3<br />
#define _L2 PORTB,2<br />
#define _L3 PORTB,1<br />
#define _L4 PORTB,0<br />
P0 equ 0x20 ; Variablen definieren (Register benennen)<br />
P1 equ 0x21<br />
P2 equ 0x22<br />
org 0x0000 ; bis da sind MPASM Direktiven<br />
; hier fängt das gesamte ASM Programm an<br />
call Init ; rufe UP Init (Initialisierung) auf<br />
Haupt btfsc _T1 ; Taster T1 gedrückt ist, wenn ja, überspringe "goto T2Test"<br />
goto T2Test ; wenn nicht, springe zu T2Test<br />
bsf _L1 ; schalte _L1 an<br />
call Warten ; warte ca. 0,4 s (2 000 000 Prozessortakten)<br />
bcf _L1 ; schalte _L1 aus<br />
bsf _L2 ; schalte _L2 an<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus<br />
bsf _L3 ; schalte _L3 an<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus<br />
bsf _L4 ; schalte _L4 an<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus<br />
T2Test btfsc _T2 ; Taster T2 gedrückt ist, wenn ja, überspringe "goto Haupt"<br />
goto Haupt ; Wenn nicht, springe zu Haupt<br />
bsf _L4 ; schalte _L4 an<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus<br />
bsf _L3 ; schalte _L3 an<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus<br />
bsf _L2 ; schalte _L2 an<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus<br />
bsf _L1 ; schalte _L1 an<br />
call Warten ; warte<br />
bcf _L1 ; schalte _L1 aus<br />
goto Haupt ; springe zu Haupt<br />
Warten movlw 0x0A ; schreibe "0x0A" für ca. 0,4 s<br />
movwf P2 ; ins Register P2<br />
clrf P1 ; lösche Register P1 (schreibe "0" für 256 Durchläufe) <br />
clrf P0 ; lösche Register P0 (schreibe "0" für 256 Durchläufe)<br />
decfsz P0,1 ; dekrementiere P0 und überspringe "goto $-1" bei P0 = 0<br />
goto $-1 ; sonst gehe zur voherigen Adresse (Befehl "decfsz P0") <br />
decfsz P1,1 ; dekrementiere P1 und überspringe "goto $-4" bei P1 = 0 <br />
goto $-4 ; sonst gehe zur aktueller Adresse - 4 (Befehl "clrf P0")<br />
decfsz P2,1 ; dekrementiere P2 und überspringe "goto $-7" bei P2 = 0<br />
goto $-7 ; sonst gehe zur aktueller Adresse - 7 (Befehl "clrf P1")<br />
return ; springe zurück zum Aufrufer ("Haupt"),<br />
Init clrf PORTB ; lösche Port (setze alle werdende Ausgänge auf "0")<br />
bsf STATUS,RP0 ; auf Bank1 umschalten<br />
bcf OPTION_REG,7 ; aktiviere pull-ups<br />
movlw 0x30 ; definiere PortB: Pins 5 und 4 als Eingänge<br />
; und die restlichen als Ausgänge (00011000b)<br />
movwf TRISB ; schreibe in TRIS Register<br />
bcf STATUS,RP0 ; auf Bank0 umschalten<br />
return ; springe zurück (zum Haupt), nach der Zeile<br />
; aus der du aufgerufen wurdest <br />
; hier endet das gesamte ASM Programm <br />
end ; MPASM Direktive<br />
<br />
In dem Fall müssen natürlich die Taster und LEDs an für sie definierte Portpins angeschlossen werden.<br />
<br />
== Einbinden ==<br />
<br />
Die einfachste Möglichkeit die gewünschten Programme in ein neues Programm einzubinden ist, alle Programme in das neue Programm zu kopieren. Dafür müssen die ersten 3 Zeilen, die den Prozessor und die Konfiguration des PICs festlegen aus den eigebundenen Programmen entfernt werden. Danach müssen alle "#define", "equ" und "org" Direktiven verglichen und wenn nötig geändert werden. Dabei hilft der MPASM mit seinen Fehlermeldungen in der Datei "*.err".<br />
<br />
Die zweite Möglichkeit ist, die gewünschten Programme als "*.asm" Dateien mit der Direktive "include *.asm" am Ende des neuen Programms vor der Direktive "end" einzubinden. Die einbindende sowie alle einzubindenden Dateien müssen sich in einem Verzeichniss befinden.<br />
<br />
include "1.asm" <br />
include "2.asm"<br />
.<br />
.<br />
.<br />
include "n.asm" <br />
end <br />
<br />
In dem Fall gelten die gleichen o.g. Regeln wie beim direkten Kopieren. Wenn es in den eingebundenen Programmen keine "org" Direktiven gibt, werden die weiteren Programme im Programmspeicher ab Ende des ersten Programms nacheinander plaziert.<br />
<br />
== Fehlersuche ==<br />
<br />
Als erstes wird an den PIC angeschlossene externe Hardware geprüft. Das sollte noch vor dem Einstecken oder Einlöten des PICs gemacht werden, da die gravierende Fehler (z.B. Kurzschlüsse, Umpolung von VCC und GND, usw.) für den PIC schädlich werden können. Für einige Teile der Hardware kann entsprechende Spannung (VCC oder GND) an bestimmten Pin gelegt werden und die richtige Reaktion der Hardware optisch (z.B. für LEDs) oder akustisch (z.B. für Relais) festgestellt werden. Sonst müssen die elektrischen Verbindungen mit einem Messgerät überprüft werden. Danach kann man den PIC einstecken bzw. einlöten.<br />
<br />
Die Fehlersuche in der Software fängt mit der ersten und endet mit der letzten Zeile des ASM Programms. Sie läuft die ganze Zeit parallel zum Programmschreiben. Jedes UP sollte vor dem Übertragen ins Programm geprüft und eventuelle Fehler beseitigt werden. Dazu arbeitet man gleichzeitig mit zwei "*.asm" Dateien, einer für die UPs und einer für das gesamte Programm. Es wird empfohlen, nach der "Initialisierung", als erstes UP, das für Ausgabegerät (z.B. Display) zu erstellen, um das funktionieren des Programms, vom Anfang an beobachten zu können.<br />
<br />
Für die Fehlersuche in "hängenden" Programmen ist der [[#PIC RAM Monitor|PIC RAM Monitor]] bzw. [[#PIC Trainer|PIC Trainer]] unersetzlich. Weil sie durch Interrupt das "hängende" Programm unterbrechen und auf dem "PIC Miniterminal" Registerinhalte während der Unterbrechung zeigen, können alle in der endlosen Schleife sich befindliche Register schnell festgestellt werden, da nur dessen Inhalte sich ständig ändern. <br />
<br />
Wenn aus geprüften Unterprogrammen erstelltes Programm nicht richtig funktioniert, müsste ein Fehler im PAD des Hauptprogramms sein.<br />
<br />
== Optimierung ==<br />
Work in Progress...<br />
Wer sonst noch Beispiele hat, bitte hier vervollständigen...<br />
BMS ...<br />
=== Speicherbedarf ===<br />
==== Programmspeicher ====<br />
Die ersten Programme für den PIC sind natürlich einfach und kurz. Doch nach und nach werden die Codes länger und unübersichtlicher, das Brennen dauert auch umso länger. Das Programm ist zu umfangreich. Doch wo soll man etwas kürzen?<br />
<br />
Es gibt unzählige Möglichkeiten - hier ein paar Beispiele:<br />
<br />
===== Unterprogramme =====<br />
<br />
Bestimmt wird man im Code immer die selben Abschnitte brauchen, zum Beispiel Warteschleifen oder ADC-Routinen, etc. Wieso sollte man diese also mehrfach (doppelt, dreifach, u.s.w.) schreiben?<br />
<br />
Die Lösung: Der Programmteil wird als Unterprogramm benutzt. Man erstellt eine Sprungmarke (ab hier beginnt das Unterprogramm) und endet wieder mit einem "return"- oder "retlw"-Befehl.<br />
Aufgerufen werden Unterprogramme meistens mit dem "call"-Befehl. Es "lohnt sich" erst ein Codefragment als UP zu definieren wenn es min. 2 mal aufgerufen wird. <br />
<br />
Ein "goto"-Befehl ist zu diesem Zweck nur geeignet, wenn der Prozessor zum letzten Aufruf von "call" zurück springen soll, da die Rücksprungadresse vom letzten "call" auf dem Stapel (stack) abgelegt wurde. Somit kann man mehr als 8 Ebenen von UPs nutzen.<br />
<br />
Den Unterprogrammen kann man natürlich auch Werte übergeben. Möchte man z.B. mehrere unterschiedliche Zeiten für Warteschleifen benutzen, so kann man die Werte vor dem Aufruf des UPs in die benötigte Anzahl von Register einschreben und dann das Unterprogramm mit "call" aufrufen. Das Unterprogramm verwendet dann die Werte aus dem Register. Siehe: [[#Pause|Pause]]<br />
<br />
Um gleiche Vorgänge in mehreren Registern durchzuführen (z.B. löschen) ist die Anwendung von Schleifen geeignet (mit bestimmter Anzahl der Abläufe und indirekter Adressierung). Siehe: [[#Variablen|Variablen]]<br />
<br />
===== bsf und bcf =====<br />
<br />
Bestimmt gibt es Situationen, in denen der PIC mehrere Bits an einem Port/Register setzen oder löschen muss. Z.B. wenn mehrere LEDs an einem Port ein- oder ausgeschaltet werden sollen.<br />
Werden mehr als 2 Bits verändert, so kann man hier die "bsf"- und "bcf"-Befehle umgehen und einen kürzeren Code erstellen.<br />
<br />
bsf PORTB,0 ;An PORTB sind 8 LEDs angeschlossen.<br />
bsf PORTB,1 ;Es sollen die ersten 4 LEDs angeschaltet werden,<br />
bsf PORTB,2 ;die anderen sollen ausgeschaltet werden.<br />
bsf PORTB,3 ;links steht es mit bcf/bsf<br />
bcf PORTB,4 ;ziemlich lang<br />
bcf PORTB,5<br />
bcf PORTB,6<br />
bcf PORTB,7<br />
<br />
movlw b'00001111' ;Das geht auch kürzer und übersichtlicher:<br />
movwf PORTB<br />
<br />
Man sieht - der Codeabschnitt umfasst nur noch 2 Zeilen anstatt 8. Somit braucht es weniger Speicher und wird von PIC schneller verarbeitet. Allerdings muss man aufpassen, da durch diese Routine der Inhalt des W-Registers und der Inhalt des <i>gesamten</i> Zielregisters verändert wird. Sollen die anderen Bits unangetastet bleiben, muss man es entweder bei den "bcf"/"bsf"-Befehlen belassen oder logische Funktionen (and bzw. or) durchführen.<br />
<br />
===== Bit kopieren =====<br />
<br />
;An den PIC ist ein Taster(RB0) und eine LED(RB1) angeschlossen.<br />
;Taster: High=gedrückt Low=nicht gedrückt<br />
;LED: High=Ein Low=Aus<br />
;Wenn der Taster gedrückt ist, soll die LED leuchten<br />
;wenn er nicht gedrückt ist, soll die LED nicht leuchten.<br />
;1.Vorschlag:<br />
btfss PORTB,0 ;ist der taster gedrückt?<br />
goto no ;nein<br />
bsf PORTB,1 ;ja - LED ein<br />
goto endif ;fertig abgefragt - unten weitermachen...<br />
no bcf PORTB,1 ;LED aus<br />
endif<br />
<br />
Der oben aufgeführte Code hat viele Nachteile: er ist relativ lang, braucht zwei Gotos und entspr. Sprungmarken. Ok, das ist vielleicht Erbsenzählerei, aber aus diesen 5 Zeilen kann man 3 machen.<br />
<br />
bcf PORTB,1 ;Das Bit wird erst gelöscht<br />
btfsc PORTB,0 ;Taster gedrückt?<br />
bsf PORTB,1 ;JA-LED an<br />
<br />
Man geht davon aus, dass der Taster nicht gedrückt ist. Somit wird die LED erst einmal ausgeschaltet. Ist der Taster aber doch gedrückt, dann wird die LED angeschaltet.<br />
<br />
Achtung möglicher Nebeneffekt: Wird dies in einer Schleife sehr schnell und oft ausgeführt, kann die LED flimmern oder nicht in ihrer vollen Helligkeit leuchten. Das passiert, weil ja angenommen wird, dass der Taster nicht gedrückt wird und die LED somit aus sein muss. Dann wird sie aber bei Tastendruck eingeschaltet. Somit entsteht ein schnelles ein-/ausschalten (PWM) und die LED leuchtet nicht mehr so hell.<br />
Meist ist das aber nicht tragisch oder wird nicht unbedingt in einer Schleife sehr schnell abgefragt. Siehe hierzu: [[#Einlesen|Einlesen]]<br />
<br />
==== RAM ====<br />
<br />
Um Anzahl benötigten Register zu sparen werden vorläufige Register (temporary) definiert, die von mehreren UPs benutzt werden. Als Beispiel, das Register "Tmp" in [[#Matrix Display|Matrix Display]] oder "ATmp" in [[#Hex Dec Wandlung|Hex Dec Wandlung]]. Die Benutzung muss aber sehr genau nach PAD zugeteilt werden. Bei gleichzeitiger Benutzung des gleichen Registers durch mehrere UPS werden falsche Daten verarbeitet und im schlimmsten Fall können endlose Schleifen entstehen, die in Folge haben, dass das Programm nicht weiter ausgeführt wird ("hängt").<br />
<br />
Viele im ASM Programm ungebrauchte SFRs können als "normale" Register (GPR) verwendet werden. Wenn zum Beispiel Timer1 nicht benutzt wird, können seine Register TMR1H und TMR1L für Speicherung von Daten benutzt werden. Es könnte aber gefährlich werden, wenn mehrere Programme gebindet sind. Deswegen muss es immer am Ende, beim volständigen Programm, geprüft werden, welche SFRs tatsächlich benutzt werden dürfen. <br />
<br />
Für Konstanten (z.B. pi, ln2, Meldungen, usw.), die sich im Laufe des Programms nicht ändern, kann EEPROM (mit MPASM Direktive "de") oder Programmspeicher (mit MPASM Direktive "dt") als Ablage benutzt werden. Siehe auch: [[#EEPROM|EEPROM]] und [[#Tabellen|Tabellen]]<br />
<br />
=== Ausführungszeit ===<br />
<br />
Alles was schnellmöglichst ablaufen soll, widersetzt sich der Optimierung des Programmspeichers.<br />
<br />
Es können keine Schleifen und keine indirekte Adressierung benutzt werden, alles muss direkt programmiert werden. <br />
<br />
Als Beispiel zur Verdeutlichung: Es sollen 16 Werte vom PORTA ins RAM ab Adresse 0x20 eingelesen werden.<br />
<br />
Als Schleife mit indirekter Adressierung:<br />
<br />
................<br />
movlw 0x10 ;Anzahl Durchläufe<br />
movwf Temp ;in den Schleifenzähler<br />
movlw 0x20 ;erste Adresse<br />
movwf FSR ;ins FSR Register<br />
Einlesen movf PORTA,0 ;PortA ins W-Register<br />
movwf INDF ;und ins aktuellen RAM Register<br />
incf FSR,1 ;nächste Adresse<br />
decfsz Temp,1 ;Temp = 0 ?<br />
goto Einlesen ;nein, zum Einlesen<br />
................ ;ja, weiter <br />
Gesamtdauer: 100 Takten<br />
Pause zwischen Einlesungen: 6 Takten<br />
Programmspeicher Bedarf: 9 Speicherstellen <br />
Direkt:<br />
<br />
...............<br />
movf PORTA,0<br />
movwf 0x20<br />
movf PORTA,0<br />
movwf 0x21<br />
movf PORTA,0<br />
movwf 0x22<br />
movf PORTA,0<br />
movwf 0x23<br />
movf PORTA,0<br />
movwf 0x24<br />
movf PORTA,0<br />
movwf 0x25<br />
movf PORTA,0<br />
movwf 0x26<br />
movf PORTA,0<br />
movwf 0x27<br />
movf PORTA,0<br />
movwf 0x28<br />
movf PORTA,0<br />
movwf 0x29<br />
movf PORTA,0<br />
movwf 0x2A<br />
movf PORTA,0<br />
movwf 0x2B<br />
movf PORTA,0<br />
movwf 0x2C<br />
movf PORTA,0<br />
movwf 0x2D<br />
movf PORTA,0<br />
movwf 0x2E<br />
movf PORTA,0<br />
movwf 0x2F<br />
...............<br />
Gesamtdauer: 32 Takten<br />
Pause zwischen Einlesungen: 2 Takten<br />
Programmspeicher Bedarf: 32 Speicherstellen<br />
<br />
Anstatt Warteschleifen (z.B. für Tastenentprellung) sollten Programmfragmente mit entsprechender Ausführungszeit benutzt werden (z.B. Ausgabe auf einem Display). Die nötige Zeit lässt sich aus mehreren UPs zusammensetzen.<br />
<br />
Es sollte kein Prozessortakt ungenutzt bleiben.<br />
<br />
Man kann auch eine Bearbeitung eines UPs um 4 Takte beschleunigen ("call" + "return"), indem man bei dem letzten Aufruf eines UPs (direkt vorm "return") anstatt "call UP", "goto UP" anwendet, da das UP sowieso mit "return" bzw. "retlw" endet.<br />
<br />
Ursprünglich:<br />
<br />
movlw 0x28<br />
call Cmd<br />
movlw 0x0C<br />
call Cmd<br />
movlw 6<br />
call Cmd <--<br />
return<br />
<br />
Optimiert:<br />
<br />
movlw 0x28<br />
call Cmd<br />
movlw 0x0C<br />
call Cmd<br />
movlw 6<br />
goto Cmd <--<br />
<br />
Nebenbei sparrt man sich eine Zeile im Quellcode und eine Spaicherstelle im Programmspeicher.<br />
<br />
= Basic-Line (PIC12...) & Mid-Range (PIC16...)=<br />
<br />
Zu Basic-Line gehören alle PIC12... mit 12-bit und zu Mid-Range alle PIC16... mit 14-bit langen Befehlen. Weil beide Familien gleichen Befehlsatz haben, werden sie hier gemeinsam behandelt. Sie haben insgesamt 35 Befehle.<br />
<br />
Alle Befehle aus der Tabelle, ausser mit "*" gekenzeichneten, gehören zum Befehlsatz den High-End PIC18... dazu.<br />
<br />
== Kurzübersicht Assembler Befehle ==<br />
<font style="font-size:10px;"><br />
{| <br />
|-<br />
| valign=top |<br />
<br />
{| {{Blauetabelle}}<br />
|ADDLW||Add literal and W <br />
|-<br />
|ADDWF||Add W and f <br />
|-<br />
|ANDLW||AND literal with W <br />
|-<br />
|ANDWF||AND W with f<br />
|-<br />
|BCF||Bit Clear f <br />
|-<br />
|BSF||Bit Set f <br />
|-<br />
|BTFSC||Bit Test f, Skip if Clear <br />
|-<br />
|BTFSS||Bit Test f, Skip if Set <br />
|-<br />
|CALL||Call subroutine <br />
|-<br />
|CLRF||Clear f<br />
|-<br />
|*CLRW||Clear W<br />
|-<br />
|CLRWDT||Clear Watchdog Timer <br />
|}<br />
<br />
| valign=top |<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
|COMF||Complement f<br />
|-<br />
|DECF||Decrement f<br />
|-<br />
|DECFSZ||Decrement f, Skip if 0<br />
|-<br />
|GOTO||Go to address or label<br />
|-<br />
|INCF||Increment f<br />
|-<br />
|INCFSZ||Increment f, Skip if 0<br />
|-<br />
|IORLW||Inclusive OR literal with W <br />
|-<br />
|IORWF||Inclusive OR W with f<br />
|-<br />
|MOVF||Move f<br />
|-<br />
|MOVLW||Move literal to W <br />
|-<br />
|MOVWF||Move W to f<br />
|-<br />
|NOP||No Operation<br />
|}<br />
<br />
| valign=top |<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
|RETFIE||Return from interrupt <br />
|-<br />
|RETLW||Return with literal in W <br />
|-<br />
|RETURN||Return from Subroutine <br />
|-<br />
|*RLF||Rotate Left f through Carry<br />
|-<br />
|*RRF||Rotate Right f through Carry<br />
|-<br />
|SLEEP||Go into standby mode <br />
|-<br />
|SUBLW||Subtract W from literal <br />
|-<br />
|SUBWF||Subtract W from f<br />
|-<br />
|SWAPF||Swap nibbles in f<br />
|-<br />
|XORLW||Exclusive OR literal with W <br />
|-<br />
|XORWF||Exclusive OR W with f<br />
|}<br />
[[:bild:pic_asm_short.jpg|Kurzübersicht zum Ausdrucken]]<br />
|}<br />
</font><br />
<br />
==Ausführliche Beschreibung zu den Befehlen==<br />
<br />
Fast alle Befehle werden in einem Takt = 4 Oszillatortakten ausgeführt.<br />
<br />
Aussnahmen:<br />
<br />
"goto", "call", "return" und "retfie" benötigen wegen Zugriff auf den Programmzähler (PC) immer 2 Takten.<br />
<br />
"btfsc", "btfss", "decfsz" und "incfsz" brauchen 1 Takt wenn der nächste Befehl ausgeführt wird bzw. 2 Takten wenn er überspringen wird, weil es immer anstatt des überspringenden Befehls ein "nop" ausgeführt wird.<br />
<br />
Eine ausgegliederte Beschreibung der Befehle findet sich unter [[PIC Assemblerbefehle]]<br />
<br />
Erklärungen zu den Verwendeten Platzhaltern:<br />
*'''k''' stellt einen fest definierten Wert da. z.B. hexadezimal <tt>0x20</tt> bzw. <tt>20</tt>, dezimal <tt>d'42'</tt> bzw. <tt>.42</tt> oder binär <tt>b'00101010'</tt><br />
*'''W''' steht für das W-Register.<br />
*'''d''' steht für ''destination'' (Ziel). Im code wird d durch ein <tt>w</tt> bzw. <tt>0</tt> (der Wert wird in das W-Register gespeichert ) oder <tt>f</tt> bzw. <tt>1</tt> (der Wert wird in das davor definierte Register gespeichert)<br />
*'''b''' steht für Bitnummer im Register (eine Zahl zwischen 0 und 7)<br />
*'''R''' steht für ein Register<br />
*'''fett''' geschrieben Bedeutet, dass es ein Platzhalter ist und im Quellcode durch eine Registeradresse oder einen Wert ersetzt werden muss<br />
*<tt>Schreibmaschinenstil</tt> bedeutet, dass es so im Quellcode geschrieben werden kann.<br />
<br />
<b> ADDLW k </b> <i style="color:grey;">ADD Literal and W - Addiere Zahl (k) und W</i><hr><br />
:Es wird die Rechenoperation <math>W+k</math> ausgeführt und das Ergebniss in das W-Register gespeichert. Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu [[#Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register|Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register]]<br />
<br />
<b> ADDWF R,d </b> <i style="color:grey;">ADD W and F - Addiere W und f </i><hr><br />
:Es wird die Rechenoperation <math>W+R</math> ausgeführt und das Ergebniss entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu [[#Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register|Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register]]<br />
<br />
<b> ANDLW k</b> <i style="color:grey;">AND Literal with W</i><hr><br />
:Es wird bitweise die logische Funktion <math>k\ and\ W</math> ausgeführt und das Ergebniss in das W-Register gespeichert. Dieser Befehl setzt das Z bit des STATUS-Register, falls W=k und das Ergebnis 0 ist.<br />
:Zur Verdeutlichung der Operation:<br />
<dl><dd><!-- zum einrücken da--><br />
11001010 ;k<br />
10101100 ;W-Register<br />
-------- and<br />
10001000 ;W-Register<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
:Dieser Befehl wird zum Löschen bestimmten Bits im Register benutzt, ohne restlichen Bits zu beeinflussen. Die bits, die gelöscht werden sollen, werden in W-Register mit 0 und die unbeeinflußte mit 1 angegeben.<br />
<br />
<b> ANDWF R,d </b> <i style="color:grey;">AND W with F</i><hr><br />
:Es wird bitweise die logische Funktion <math>W\ and\ R</math> ausgeführt und das Ergebniss entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Vergleiche mit ANDLW.<br />
<br />
<b>BCF R,b</b> <i style="color:grey;">Bit Clear F - Bit b im R wird gelöscht</i><hr><br />
:Mit dem Befehl <tt>BCF</tt> wird das Bit '''b''' im Register '''R''' gelöscht. Ein Beispiel:<br />
<dl><dd><!-- zum einrücken da--><br />
movlw b'11111111' ;es wird b'11111111' in das W-Register geschrieben<br />
BCF W,2 ;es wird bit 2 im W-Register gelöscht.<br />
;das Ergebnis ist: b'11111011'<br />
</dl></dd><!-- zum einrücken da--><br />
<br />
<b>BSF R,b</b> <i style="color:grey;">Bit Set F - Bit b im R wird gesetzt</i><hr><br />
:Mit dem Befehl <tt>BSF</tt> wird das Bit '''b''' im Register '''R''' gesetzt. Ein Beispiel:<br />
<dl><dd><!-- zum einrücken da--><br />
clrw ;es wird b'00000000' in das W-Register geschrieben<br />
BSF W,2 ;es wird bit 2 im W-Register gesetzt.<br />
;das Ergebnis ist: b'00000100'<br />
</dl></dd><!-- zum einrücken da--><br />
<br />
<b>BTFSC R,b</b> <i style="color:grey;">Bit Test F, Skip if Clear - Wenn das Bit b im Register R 0 ist, überspringe den nächsten Befehl</i><hr><br />
:Mit dem Befehl <tt>BTFSC</tt> kann eine Verzweigung im Programmablauf bewirkt werden. Wenn das Bit '''b''' im Register '''R''' 0 ist, wird der nächste Befehl übersprungen. Ein Beispiel:<br />
<dl><dd><!-- zum einrücken da--><br />
movlw b'00000001' ;es wird die Zahl 1 in das W-Register kopiert.<br />
BTFSC W,0 ;es wird bit 0 geprüft.<br />
;wenn es 0 ist, wird der nächste Befehl übersprungen<br />
goto IST_EINS ;springt zur Marke "IST_EINS" <- in diesem Fall wird dieser<br />
;Sprungbefehl ausgeführt.<br />
goto IST_NULL ;springt zur Marke "IST_NULL"<br />
</dl></dd><!-- zum einrücken da--><br />
<br />
<b>BTFSS R,b</b> <i style="color:grey;">Bit Test F, Skip if Set - Wenn das Bit b im Register R 1 ist, überspringe den nächsten Befehl</i><hr><br />
:Mit dem Befehl <tt>BTFSS</tt> kann eine Verzweigung im Programmablauf bewirkt werden. Wenn das Bit '''b''' im Register '''R''' 1 ist, wird der nächste Befehl übersprungen. Ein Beispiel:<br />
<dl><dd><!-- zum einrücken da--><br />
movlw b'00000001' ;es wird die Zahl 1 in das W-Register kopiert.<br />
BTFSS W,0 ;es wird bit 0 geprüft.<br />
;wenn es 1 ist, wird der nächste Befehl übersprungen<br />
goto IST_NULL ;springt zur Marke "IST_NULL"<br />
goto IST_EINS ;springt zur Marke "IST_EINS" <- in diesem Fall wird dieser<br />
;Sprungbefehl ausgeführt, da der Befehl<br />
;darüber übersprungen wurde.<br />
</dl></dd><!-- zum einrücken da--><br />
<br />
<br />
<b>CALL</b> <i style="color:grey;">CALL Subroutine - Rufe Unterprogramm auf</i><hr><br />
:Mit dem <tt>CALL</tt> Befehl wird ein Unterprogramm aufgerufen. Mit <tt>RETURN</tt> oder <tt>RETLW</tt>-Befehl wird das Unterprogramm beendet und man kehrt zum Befehl nach dem <tt>CALL</tt>-Befehl zurück. Das Unterprogramm wird so definiert, dass im Quellcode der Name des Unterprogramms nicht eingerückt steht. Ein Beispiel:<br />
<br />
<dl><dd><!-- zum einrücken da--><br />
movlw d'13' ;in das W-Register wird 13d geladen<br />
CALL UP1 ;es wird das Unterprogramm "UP1" aufgerufen<br />
movwf ergebnis ;das W-Register wird in das Register "ergebnis" kopiert.<br />
;im Register "ergebnis" steht nun 23d<br />
<br />
UP1 addlw d'10' ;es wird 10d zum W-Register addiert<br />
return ;kehre zurück zum Aufrufer<br />
</dl></dd><!-- zum einrücken da--><br />
<br />
<b>CLRF R</b> <i style="color:grey;">CLeaR F- Schreibe 0 in das Register R</i><hr><br />
:Das Register '''R''' wird mit Nullen gefüllt (gelöscht).<br />
<br />
<b>CLRW</b> <i style="color:grey;">CLeaR W - Schreibe 0 in W</i><hr><br />
:Das W-Register wird mit Nullen gefüllt (gelöscht).<br />
<br />
<b>CLRWDT</b> <i style="color:grey;">CLeaR WatchDog Timer - Setzt den Watchdog-Timer zurück</i><hr><br />
:Es wird der WDT (Watchdog-Timer) zurückgesetzt und der Zähler des WDT auf 0 gesetzt, zusätzlich werden die STATUS-bits TO und PD gesetzt.<br />
<br />
<b>COMF R,d</b> <i style="color:grey;">COMplement F - negiere alle bits im Register R</i><hr><br />
:Von der Binärzahl im Register '''R''' werden alle 0 mit 1 und alle 1 mit 0 ersetzt. Das Ergebnis wird entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Ein kleines Beispiel: aus <tt>AAh</tt> (<tt>10101010b</tt>) wird <tt>55h</tt> (<tt>01010101b</tt>).<br />
<br />
<b>DECF R,d</b> <i style="color:grey;">DECrement F - Subtrahiert 1 vom Regiser f</i><hr><br />
:Vom Wert des Registers '''R''' wird 1 subtrahiert und das Ergebnis entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>).<br />
:Weil es keine "echte" Substraktion ist, beeinflusst dieser Befehl das C-Flag im STATUS-Register nicht.<br />
<br />
<b>DECFSZ R,d</b> <i style="color:grey;">DECrement F, Skip if Zero - Subtrahiert 1 vom Regiser f, überspringe wenn 0</i><hr><br />
:Vom Wert des Registers '''R''' wird 1 subtrahiert und das Ergebnis entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Der Zusatz SZ steht für ''skip if zero'', d.h. wenn das Ergebnis der Rechnung Null ist, wird der nächste Befehl übersprungen. Dieser Befehl wird für Schleifen mit bestimmter Anzahl der Durchläufe benutzt.<br />
<br />
<b>GOTO</b> <i style="color:grey;">GO TO address - Gehe zu Adresse/Sprungmarke</i><hr><br />
:Nach dem GOTO Befehl wird das Programm ab der Adresse weiter ausgeführt, die nach dem GOTO-Befehl steht. Diese Adresse wird durch so genannte Sprungmarke definiert, welche, im Gegensatz zu den Befehlen nicht eingerückt im Quellcode stehen.<br />
<br />
:Um die Anzahl den Sprungmarken im Programm zu verringern, kann auch indirekte Adressierung von Sprungzielen mit "GOTO $+n" bzw. "GOTO $-n" benutzt werden. Das Symbol "$" bedeutet immer die aktuelle Adresse vom Programmzähler (PC). Das Assemblerprogramm, das sowieso jede Sprungmarke in entsprechende absolute Adresse wandelt, erzeugt in diesem Fall die Zieladresse als "PC+n" bzw. "PC-n", wobei "n" immer als hexadezimale Zahl genommen wird.<br />
<br />
:Der Befehl "goto $+1" verbraucht nur Zeit von zwei "nop". <br />
<br />
:Beispiele dazu sind in [[#Pause|Pause]].<br />
<br />
<b>INCF R,d</b> <i style="color:grey;">INCrement F - Addiere 1 zum Register f</i><hr><br />
:Zum Wert des Registers '''R''' wird 1 addiert und das Ergebniss entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>).<br />
:Weil es keine "echte" Addition ist, beeinflusst dieser Befehl das C-Flag im STATUS-Register nicht.<br />
<br />
<b>INCFSZ R,d</b> <i style="color:grey;">INCrement F, Skip if Zero - Addiere 1 zum Regiser f, überspringe wenn 0</i><hr><br />
:Zum Wert des Registers '''R''' wird 1 addiert und das Ergebniss entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Der Zusatz SZ steht für ''skip if zero'', d.h. wenn das Ergebnis der Rechnung Null ist, wird der nächste Befehl übersprungen. Dieser Befehl wird für Schleifen mit bestimmter Anzahl der Durchläufe benutzt.<br />
<br />
<b> IORLW k</b> <i style="color:grey;">Inclusive OR Literal with W</i><hr><br />
:Es wird bitweise die logische Funktion <math>k\ or\ W</math> ausgeführt und das Ergebniss in das W-Register gespeichert. Dieser Befehl setzt das Z bit des STATUS-Register, falls W=k und das Ergebnis 0 ist.<br />
:Zur Verdeutlichung der Operation:<br />
<dl><dd><!-- zum einrücken da--><br />
11001010 ;k<br />
10101100 ;W-Register<br />
-------- or<br />
11101110 ;W-Register<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
:Dieser Befehl wird zum Setzen bestimmten Bits im Register benutzt, ohne restlichen Bits zu beeinflussen. Die bits, die gesetzt werden sollen, werden in W-Register mit 1 und die unbeeinflußte mit 0 angegeben.<br />
<br />
<b> IORWF R,d </b> <i style="color:grey;">Inclusive OR W with F</i><hr><br />
:Es wird bitweise die logische Funktion <math>W\ or\ R</math> ausgeführt und das Ergebniss entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Vergleiche mit IORLW.<br />
<br />
<b>MOVF R,d</b> <i style="color:grey;">MOVe F - Bewege f</i><hr><br />
:Das Register R wird in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder wieder in R kopiert ('''d'''=<tt>F</tt>=<tt>1</tt>). Letzteres mag sinnlos scheinen, ist aber nützlich, da durch den Befehl das Z-Bit im STATUS-Register gesetzt wird, falls R Null ist.<br />
<br />
<b>MOVLW k</b> <i style="color:grey;">MOVe Literal to W - Bewege Zahl (k) in W-Register</i><hr><br />
:Der festgelegte Wert k wird in das W-Register geladen.<br />
<dl><dd><!-- zum einrücken da--><br />
MOVLW 0xA3 ;ins W-Register wird eine Zahl A3h geladen<br />
</dd></dl><!-- zum einrücken da--><br />
:Dieser Befehl wird auch bei indirekter Adressierung zum laden der absoluter Adresse ins "FSR" Register benutzt. Beispiel:<br />
:Der Register "A3" wurde mit "A3 equ 23" definiert.<br />
<dl><dd><!-- zum einrücken da--><br />
MOVLW A3 ;ins W-Register wird die absolute Adresse 23h vom "A3" geladen<br />
movwf FSR ;diese Adresse wird jetzt ins Register "FSR" kopiert <br />
</dd></dl><!-- zum einrücken da--><br />
<b>MOVWF R</b> <i style="color:grey;">MOVe W to F- Bewege W-Register in das Register F</i><hr><br />
:Das W-Register wird in das Register '''R''' kopiert.<br />
<br />
<b>NOP</b> <i style="color:grey;">No OPeration - Kein Befehl zum Ausführen (warte)</i><hr><br />
:Dieser Befehl macht nichts. Er verbraucht nur Zeit, welche sich einfach mit folgender Formel berechnen lässt. <math>t=\frac{4}{f}</math>,wobei <math>f</math> für die Frequenz des Oszillators steht. Siehe hierzu: [[#Pause|Pause]]<br />
<br />
<b>RETFIE</b> <i style="color:grey;">RETurn From Interrupt Enable - Kehre zurück aus der Unterbrechung, erlaube Unterbrechungen</i><hr><br />
:Mit diesem Befehl wird die Interrupt Service Routine (ISR) beendet und das Programm wird an der Zeile weiter ausgeführt, vor der es durch den Interrupt angehalten wurde. Es werden auch alle Interrupts wieder erlaubt (das GIE bit wird gesetzt). Siehe hierzu auch [[#Interrupt | Interrupt]]<br />
<br />
<b>RETLW k</b> <i style="color:grey;">RETurn with Literal in W - Kehre zurück mit Zahl k im W-Register</i><hr><br />
:Wurde ein Programmteil mit dem Befehl <tt>CALL</tt> aufgerufen, dann springt man mit dem Befehl <tt>RETLW</tt> zurück in die nächste Zeile nach der Zeile aus der das <tt>CALL</tt> Befehl ausgeführt wurde. Der in k angegebene Wert wird dabei in das W-Register geschrieben. Dies wird benutzt um zu erkennen aus welchem UP das verzweigte Programm zurückkommt. Dieser Befehl wird aber vor allem für s.g. Wertetabellen (eng: lookup tables) verwendet.<br />
<br />
<b>RETURN</b> <i style="color:grey;">RETURN from Subroutine - Kehre zurück zum Aufrufer</i><hr><br />
:Wurde ein Programmteil mit dem Befehl <tt>CALL</tt> aufgerufen, dann springt man mit dem Befehl <tt>RETURN</tt> zurück zu der nächsten Zeile nach der Zeile aus der das <tt>CALL</tt> Befehl ausgeführt wurde.<br />
<br />
<b>RLF R,d</b> <i style="color:grey;">Rotate Left F through carry - Rotiere das Register f mithilfe des Carry-bits nach links</i><hr><br />
:Alle Bits im Register '''R''' werden um eine Position nach links verschoben. Dabei wird das Carry bit (<tt>STATUS,C</tt>) in das Bit 0 des Registers R geschoben. Bit 7 aus dem Register '''R''' wird in das Carry bit "geschoben". Das Ergebnis wird entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). <br />
<dl><dd><!-- zum einrücken da--><br />
|<--- Register R --->|<br />
|c|<-7<-6<-5<-4<-3<-2<-1<-0<br />
V A<br />
|________________________|<br />
</dd></dl><!-- zum einrücken da--><br />
:Zur Verdeutlichung:<br />
<dl><dd><!-- zum einrücken da--><br />
|C| |-Register R-| ;C steht für das Carry-bit, STATUS,C ; 0-7 stehen für Bitnummer im Register.<br />
c 7 6 5 4 3 2 1 0 ;vor der Rotation<br />
7 6 5 4 3 2 1 0 c ;nach der Rotation<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
<b>RRF R,d</b> <i style="color:grey;">Rotate Right F through carry - Rotiere das Register f mithilfe des Carry-bits nach rechts</i><hr><br />
:Alle Bits im Register '''R''' werden um eine Position nach rechts verschoben. Dabei wird das Carry bit (<tt>STATUS,C</tt>) in das 7.Bit des Registers R geschoben. Bit 0 aus dem Register '''R''' wird in das Carry bit "geschoben". Das Ergebnis wird entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). <br />
<dl><dd><!-- zum einrücken da--><br />
|<--- Register R --->|<br />
|c|->7->6->5->4->3->2->1->0<br />
A V<br />
|________________________|<br />
</dd></dl><!-- zum einrücken da--><br />
:Zur Verdeutlichung:<br />
<dl><dd><!-- zum einrücken da--><br />
|C| |-Register R-| ;C steht für das Carry-bit, STATUS,C ; 0-7 stehen für Bitnummer im Register.<br />
C 7 6 5 4 3 2 1 0 ;vor der Rotation<br />
0 C 7 6 5 4 3 2 1 ;nach der Rotation <br />
</dd></dl><!-- zum einrücken da--><br />
<br />
<b>SLEEP </b> <i style="color:grey;">Go into standby mode - Versetze den Mirokontroller in Bereitschaftsmodus (Schlaf)</i><hr><br />
:Der µC wird in den Sleep-Mode versetzt, in dem er weniger Strom verbraucht. Er kann durch einen Reset, einem Watchdog-Timer-Reset oder durch einen Interrupt wieder aufgeweckt werden.<br />
<br />
<b> SUBLW k </b> <i style="color:grey;">SUBtract W from Literal - Ziehe W von Zahl (k) ab</i><hr><br />
:Es wird die Rechenoperation <math>k-W</math> ausgeführt und das Ergebniss in das W-Register gespeichert. Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu [[#Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register|Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register]]<br />
<br />
<b> SUBWF R,d </b> <i style="color:grey;">SUBtract W from F - Ziehe W von f ab</i><hr><br />
:Es wird die Rechenoperation <math>R-W</math> ausgeführt und das Ergebniss entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu [[#Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register|Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register]]<br />
<br />
:Beispiel:<br />
<dl><dd><!-- zum einrücken da--><br />
movlw d'20' ;schreibe 20 in das W-Register<br />
movwf Register1 ;bewegt das W-Register in das Register1<br />
movlw d'10' ;schreibt 10 in das W-Register<br />
SUBWF Register1,F ;schreibt Register1(20)-W(10) in Register1<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
<b>SWAPF R,d </b> <i style="color:grey;">SWAP nibbles in F - Vertausche die Halbbytes (Nibbles)</i><hr><br />
:Es werden die Nibbles, also die höheren 4 bits (bit7-bit4) mit den niedrigeren 4 bits (bit3-bit0) eines Registers vertauscht und entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>).<br />
:Beispiel:<br />
<dl><dd><!-- zum einrücken da--><br />
movlw b'00001111' ;schreibe b'00001111' in das W-Register<br />
movwf Register1 ;kopiert das W-Register in das Register1<br />
SWAPF Register1,W ;vertauscht die ersten 4 bit mit den letzen<br />
;4 bit in Register 1 und schreibt es in das W-Register<br />
;im W-Register steht nun b'11110000'<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
:Der Befehl wird immer zum Sichern und Wiederherstellen des STATUS-Registers am Anfang und Ende einer ISR (interrupt service routine) verwendet, da er das STATUS-Register nicht beeinflußt (verändert keine STATUS bits).<br />
<br />
<b> XORLW k</b> <i style="color:grey;">EXclusive OR Literal with W</i><hr><br />
:Es wird bitweise die logische Funktion <math>k\ xor\ W</math> ausgeführt und das Ergebniss in das W-Register gespeichert. Dieser Befehl setzt das Z bit des STATUS-Registers, falls W=k und das Ergebnis 0 ist.<br />
<br />
:Zur Verdeutlichung der Operation:<br />
<dl><dd><!-- zum einrücken da--><br />
11001010 ;k<br />
10101100 ;W-Register<br />
-------- xor<br />
01100110 ;W-Register<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
:Dieser Befehl wird vor allem zum Vergleichen eines Registers mit ins W-Register geladenem Wert benutzt. Wenn die Werte in beiden Register gleich sind, wird ein Z bit im STATUS-Register gesetzt.<br />
<br />
<b> XORWF R,d </b> <i style="color:grey;">EXclusive OR W with F</i><hr><br />
:Es wird bitweise die logische Funktion <math>W\ xor\ R</math> ausgeführt und das Ergebniss entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Vergleiche XORLW. Dieser Befehl setzt das Z bit des STATUS-Registers, falls W=k und das Ergebnis 0 ist. Er wird zum Vergleichen zwei Register benutzt, wobei ein Register vorher ins W-Register geladen wird..<br />
<br />
==Besondere, oft gebrauchte Register==<br />
<br />
=== STATUS === <br />
Der Statusregister beinhaltet den Status der Recheneinheit ALU (Arithmetic-Logic Unit), Resetinformationen und die beiden Bits zur Wahl der Speicherbank<br />
<br />
<table style="text-align: center;" cellspacing="0"><br />
<tr><br />
<td colspan="8" style>'''STATUS''' (ADDRESSE 03h, 83h, 103h, 183h)</td><br />
</tr><br />
<tr style="border:0px;"><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R-1</td><br />
<td style="width:60px;">R-1</td><br />
<td style="width:60px;">R/W-x</td><br />
<td style="width:60px;">R/W-x</td><br />
<td style="width:60px;">R/W-x</td><br />
</tr><br />
<tr><br />
<td style="background:#f5f7ff;">'''IRP'''</td><br />
<td style="background:#f5f7ff;">'''RP1'''</td><br />
<td style="background:#f5f7ff;">'''RP0'''</td><br />
<td style="background:#f5f7ff;">'''TO'''</td><br />
<td style="background:#f5f7ff;">'''PD'''</td><br />
<td style="background:#f5f7ff;">'''Z'''</td><br />
<td style="background:#f5f7ff;">'''DC'''</td><br />
<td style="background:#f5f7ff;">'''C'''</td><br />
</tr><br />
<tr><br />
<td>Bit7</td><br />
<td colspan="6"></td><br />
<td>Bit0</td><br />
</tr><br />
</table><br />
<br />
<br />
<br />
*Bit 7 '''IRP''': Register Bank Select Bit (für indirekte Adressierung)<br />
:: 1 = Bank 2, 3 (100h-1FFh)<br />
:: 0 = Bank 0, 1 (00h-FFh)<br />
*Bit 6-5 '''RP<1:0>''': Register Bank Select Bits (für direkte Adressierung)<br />
:: 11 = Bank 3 (180h-1FFh)<br />
:: 10 = Bank 2 (100h-17Fh)<br />
:: 01 = Bank 1 (80h-FFh)<br />
:: 00 = Bank 0 (00h-7Fh) <br />
*Bit 4 '''TO''': Time-out Bit<br />
:: 1 = Nach Power-up, CLRWDT Befehl oder SLEEP Befehl<br />
:: 0 = A Watchdogtimer time-out ist eingetreten<br />
*Bit 3 '''PD''': Power-Down Bit<br />
:: 1 = Nach Power-up oder durch den CLRWDT<br />
:: 0 = Nach einem SLEEP befehl<br />
*Bit 2 '''Z''': Zero bit<br />
:: 1 = Das Ergebnis einer arithmetischen oder logischen Operation ist 0<br />
:: 0 = Das Ergebnis einer arithmetischen oder logischen Operation ist NICHT 0<br />
*Bit 1 '''DC''': Digit carry/borrow bit (ADDWF, ADDLW, SUBLW und SUBWF Befehle)<br />
:: 1 = Ein Carry-out des 4.Niedrigsten Bits (Low Nibble) eines Rechenergebnisses existiert<br />
:: 0 = Kein Carry-out des 4.Niedrigsten Bits eines Rechenergebnisses existiert<br />
*Bit 0 '''C''': Carry/borrow Bit (ADDWF, ADDLW, SUBLW und SUBWF Befehle)<br />
:: 1 = Ein Carry-out des MSB eines Rechenergebnisses existiert<br />
:: 0 = Kein Carry-out des MSB eines Rechenergebnisses existiert<br />
<br />
Das "Carry/Borrow Bit" (to carry = etwas übertragen, to borrow = etwas borgen) dient zum erkennen, wenn ein Übertrag einer Rechenoperation exisitiert. 250d+10d ergibt zum Beispiel 4, und setzt dabei das "Carry/Borrow Bit" auf 1. Damit kann das Programm erkennen, wenn wieder einmal ein Ergebnis größer als 255d herauskam.<br />
Bei Subtraktionen (SUBLW und SUBWF) verhält sich das "Carry/Borrow Bit" umgekehrt als bei Additionen (ADDWF und ADDLW)!! Zum Beispiel 55d-6=49d setzt "Carry/Borrow Bit" auf 1 aber 10d-25d=241d löscht das "Carry/Borrow Bit".<br />
<br />
====Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Registers====<br />
<br />
{|<br />
|-<br />
|<br />
{| {{Blauetabelle}}<br />
|+ Auswirkungen auf das STATUS-Register bei Subtraktionen<br />
|-<br />
| Ergebnis<br />
|| STATUS,C<br />
|| STATUS,Z<br />
|-<br />
| positiv<br />
|align="center"|1<br />
|align="center"|0<br />
|-<br />
| negativ<br />
|align="center"|0<br />
|align="center"|0<br />
|-<br />
| Null<br />
|align="center"|1<br />
|align="center"|1<br />
|}<br />
<br />
||<br />
<br />
{| {{Blauetabelle}}<br />
|+ Auswirkungen auf das STATUS-Register bei Additionen<br />
|-<br />
| Ergebnis<br />
|| STATUS,C<br />
|| STATUS,Z<br />
|-<br />
| positiv<br />
|align="center"|0<br />
|align="center"|0<br />
|-<br />
| Überlauf<br />
|align="center"|1<br />
|align="center"|0<br />
|-<br />
| Null<br />
|align="center"|1<br />
|align="center"|1<br />
|}<br />
|}<br />
<br />
=== OPTION_REG ===<br />
<br />
Durch OPTION_REG werden PORTB pull-ups, aktive Flanke des externen Interrupts, der Prescaler und der Timer0 konfiguriert.<br />
<br />
<br />
<table style="text-align: center;" cellspacing="0"><br />
<tr><br />
<td colspan="8" style>'''OPTION_REG''' (ADDRESSE 81h, 181h)</td><br />
</tr><br />
<tr style="border:0px;"><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-x</td><br />
</tr><br />
<tr><br />
<td style="background:#f5f7ff;">'''/RBPU'''</td><br />
<td style="background:#f5f7ff;">'''INTEDG'''</td><br />
<td style="background:#f5f7ff;">'''T0CS'''</td><br />
<td style="background:#f5f7ff;">'''T0SE'''</td><br />
<td style="background:#f5f7ff;">'''PSA'''</td><br />
<td style="background:#f5f7ff;">'''PS2'''</td><br />
<td style="background:#f5f7ff;">'''PS1'''</td><br />
<td style="background:#f5f7ff;">'''PS0'''</td><br />
</tr><br />
<tr><br />
<td>Bit7</td><br />
<td colspan="6"></td><br />
<td>Bit0</td><br />
</tr><br />
</table><br />
<br />
<br />
*Bit 7 '''/RBPU''': PORTB Pull-up Enable bit ("/" bedeutet Negation)<br />
::1 = Pull-ups sind deaktiviert <br />
::0 = Pull-ups sind aktiviert für die Pins, die im Register TRISB als Eingänge definiert sind<br />
*Bit 6 '''INTEDG''': Interrupt Edge Select bit<br />
::1 = Interrupt auf steigende Flanke am RB0/INT pin<br />
::0 = Interrupt auf fallende Flanke am RB0/INT pin<br />
*Bit 5 '''T0CS''': Timer0 Clock Source Select bit<br />
::1 = Wechsel am RA4/T0CKI pin<br />
::0 = Internes Taktsignal (Fosc/4), wo Fosc ist gleich der Frequenz des Oszillators (z.B. Quarz)<br />
*Bit 4 '''T0SE''': TMR0 Source Edge Select bit<br />
::1 = Inkrementiere bei fallender Flanke am RA4/T0CKI pin<br />
::0 = Inkrementiere bei steigender Flanke am RA4/T0CKI pin<br />
*Bit 3 '''PSA''': Prescaler Assignment bit<br />
::1 = Prescaler ist dem WDT zugewiesen<br />
::0 = Prescaler ist dem Timer0 zugewiesen<br />
*Bit 2-0 '''PS2:PS0''': Prescaler Rate Select bit<br />
<br />
Je nach Zuweisung des Prescalers (durch PSA bit) wird die interne Taktfrequenz des Prozessors (Fosc/4) wie folgt geteilt:<br />
<br />
{|<br />
|-<br />
|<br />
{| {{Blauetabelle}}<br />
<br />
| PS2:PS0<br />
|| TMR0<br />
|| WDT<br />
|-<br />
|align="center"|000<br />
|align="center"|1:2<br />
|align="center"|1:1<br />
|-<br />
|align="center"|001<br />
|align="center"|1:4<br />
|align="center"|1:2<br />
|-<br />
|align="center"|010<br />
|align="center"|1:8<br />
|align="center"|1:4<br />
|-<br />
|align="center"|011<br />
|align="center"|1:16<br />
|align="center"|1:8<br />
|-<br />
|align="center"|100<br />
|align="center"|1:32<br />
|align="center"|1:16<br />
|-<br />
|align="center"|101<br />
|align="center"|1:64<br />
|align="center"|1:32<br />
|-<br />
|align="center"|110<br />
|align="center"|1:128<br />
|align="center"|1:64<br />
|-<br />
|align="center"|111<br />
|align="center"|1:256<br />
|align="center"|1:128<br />
|}<br />
|}<br />
<br />
=== PORTx ===<br />
<br />
<table style="text-align: center;" cellspacing="0"><br />
<tr><br />
<td colspan="8" style>'''PORTx'''</td><br />
</tr><br />
<tr style="border:0px;"><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-x</td><br />
</tr><br />
<tr><br />
<td style="background:#f5f7ff;">'''Rx7'''</td><br />
<td style="background:#f5f7ff;">'''Rx6'''</td><br />
<td style="background:#f5f7ff;">'''Rx5'''</td><br />
<td style="background:#f5f7ff;">'''Rx4'''</td><br />
<td style="background:#f5f7ff;">'''Rx3'''</td><br />
<td style="background:#f5f7ff;">'''Rx2'''</td><br />
<td style="background:#f5f7ff;">'''Rx1'''</td><br />
<td style="background:#f5f7ff;">'''Rx0'''</td><br />
</tr><br />
<tr><br />
<td>Bit7</td><br />
<td colspan="6"></td><br />
<td>Bit0</td><br />
</tr><br />
</table><br />
<br />
!Das "x" steht in allen Fällen für den Buchstaben des Ports!<br />
BSP: "PORTB" oder "RA1"<br />
<br />
Diese Special Funktion Register liegen ALLE in Bank0 und Bank2 (Falls vorhanden).<br />
<br />
Jedes Bit eines PORT-Registers steht für einen Pin (Ein/Ausgang) des jeweiligen Ports. Je nachdem Ob ein Pin als Ausgang oder als Eingang definiert ist, kann man dort entweder den Wert schreiben oder auslesen. Es hat nicht Jeder PIC gleich viele Ports und es sind auch nicht immer 8 Pins pro Port, es können auch weniger sein. Alle PICs der Midrange-Serie besitzen nur bidirektionale Ports, d.h. sie können sowohl Eingang als auch Ausgang sein. Bei Port B kann man bei allen Pins einen internen PULL-UP Widerstand mit dem Bit OPTION_REG<RBPU> hinzuschalten (Standardmäßig auf nicht aktiviert, Bit muss gelöscht werden um die Funktion zu aktivieren). Weiters ist zu beachten, dass einzelne Pins nach dem Reset mit anderen Funktionen belegt sein können. Das gilt im Speziellen für PORTA, wo sich die Komperatoren, AD's (falls vorhanden) und die Oszillatoreingänge befinden. Siehe [[PIC Assembler#I/O Ports|I/O Ports]]. Bei den "kleinsten" PICs der 12F Serie die nur einen I/O Port (6 Pins) haben, heisst der PORT-Register GPIO. <br />
{|{{Blauetabelle}}<br />
|+ Beispiel PICs und ihre Ports (Zahlen in der Klammer sind die Anzahl der Pins des Ports)<br />
|<br />
|'''PIC16F628A'''<br />
|'''PIC16F876'''<br />
|'''PIC16F877'''<br />
|-<br />
|'''PORTA'''<br />
|Ja<br />
|Ja(5)<br />
|Ja(6)<br />
|-<br />
|'''PORTB'''<br />
|ja<br />
|ja<br />
|ja<br />
|-<br />
|'''PORTC'''<br />
|/<br />
|ja<br />
|ja<br />
|-<br />
|'''PORTD'''<br />
|/<br />
|/<br />
|ja<br />
|-<br />
|'''PORTE'''<br />
|/<br />
|/<br />
|ja (3)<br />
|-<br />
|}<br />
<br />
=== TRISx ===<br />
<br />
<table style="text-align: center;" cellspacing="0"><br />
<tr><br />
<td colspan="8" style>'''TRISx'''</td><br />
</tr><br />
<tr style="border:0px;"><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-x</td><br />
</tr><br />
<tr><br />
<td style="background:#f5f7ff;">'''TRISx7'''</td><br />
<td style="background:#f5f7ff;">'''TRISx6'''</td><br />
<td style="background:#f5f7ff;">'''TRISx5'''</td><br />
<td style="background:#f5f7ff;">'''TRISx4'''</td><br />
<td style="background:#f5f7ff;">'''TRISx3'''</td><br />
<td style="background:#f5f7ff;">'''TRISx2'''</td><br />
<td style="background:#f5f7ff;">'''TRISx1'''</td><br />
<td style="background:#f5f7ff;">'''TRISx0'''</td><br />
</tr><br />
<tr><br />
<td>Bit7</td><br />
<td colspan="6"></td><br />
<td>Bit0</td><br />
</tr><br />
</table><br />
<br />
!Das "x" steht in allen Fällen für den Buchstaben des Ports!<br />
BSP: "TRISB" oder "TRISA"<br />
<br />
Bei den "kleinsten" PICs der 12F Serie die nur einen I/O Port (6 Pins) haben, heisst der TRIS-Register TRISIO.<br />
<br />
Die TRIS-Bytes dienen dazu, einzelne I/O Pins als Eingang oder Ausgang zu definieren. Sie liegen immer genau eine Bank über den jeweiligen PORT-Bytes, d.h. sie liegen alle in Bank1 und Bank3 (falls vorhanden.) Besonders zu beachten ist, dass manche Eingebaute Features wie die Serielle Schnittstelle, I2C, SPI usw nicht funktionieren, wenn die Jeweiligen Sende und Empfangspins nicht manuell umgestellt werden, ja es ''muss'' sogar manchmal umgestellt werden (bei I2C z.B.) Egal ist dies jedoch für Komperatoren und AD-Wandler.<br />
<br />
Merke:<br />
* Eine 1 (als "I" für "Input" zu lesen) eines TRISxx-Bits definiert den zugehörigen PIN am PIC als Eingang.<br />
* Eine 0 (als "O" für "Output" zu lesen) eines TRISxx-Bits definiert den zugehörigen PIN am PIC als Ausgang.<br />
<br />
Siehe [[PIC Assembler#I/O Ports|I/O Ports]].<br />
<br />
=== INTCON === <br />
<br />
<table style="text-align: center;" cellspacing="0"><br />
<tr><br />
<td colspan="8" style>'''INTCON''' (ADDRESSE 0Bh, 8Bh, 10Bh, 18Bh)</td><br />
</tr><br />
<tr style="border:0px;"><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-0</td><br />
<td style="width:60px;">R/W-x</td><br />
</tr><br />
<tr><br />
<td style="background:#f5f7ff;">'''GIE'''</td><br />
<td style="background:#f5f7ff;">'''PEIE'''</td><br />
<td style="background:#f5f7ff;">'''TMR0IE'''</td><br />
<td style="background:#f5f7ff;">'''INT0IE'''</td><br />
<td style="background:#f5f7ff;">'''RBIE'''</td><br />
<td style="background:#f5f7ff;">'''TMR0IF'''</td><br />
<td style="background:#f5f7ff;">'''INT0IF'''</td><br />
<td style="background:#f5f7ff;">'''RBIF'''</td><br />
</tr><br />
<tr><br />
<td>Bit7</td><br />
<td colspan="6"></td><br />
<td>Bit0</td><br />
</tr><br />
</table><br />
<br />
<br />
*Bit 7 '''GIE''': Global Interrupt Enable Bit<br />
::1 = Aktiviert alle Interrupts<br />
::0 = Deaktiviert alle Interrupts<br />
*Bit 6 '''PEIE''': Peripheral Interrupt Enable Bit<br />
::1 = Aktiviert alle peripheren Interrupts<br />
::0 = Deaktiviert alle peripheren Interrupts<br />
*Bit 5 '''TMR0IE''': TMR0 Overflow Interrupt Enable Bit (Overflow von Timer0 löst Interrupt aus)<br />
::1 = Aktiviert den TMR0 Interrupt<br />
::0 = Deaktiviert den TMR0 Interrupt<br />
*Bit 4 '''INT0IE''': RB0/INT External Interrupt Enable Bit (Änderung an RB0 Pin löst Interrupt aus) *)<br />
::1 = Aktiviert den RB0/INT Interrupt (Extern ausgelöst)<br />
::0 = Deaktiviert den RB0/INT Interrupt (Extern ausgelöst)<br />
*Bit 3 '''RBIE''': RB Port On-Change-Interrupt Enable Bit (Änderung an PortB löst Interrupt aus) **)<br />
::1 = Aktiviert den RB port on-change-interrupt<br />
::0 = Deaktiviert den RB port on-change-interrupt<br />
*Bit 2 '''TMR0IF''': TMR0 Overflow Interrupt Flag Bit<br />
::1 = TMR0 Register ist Übergelaufen (muss per Software gelöscht werden)<br />
::0 = TMR0 register ist nicht Übergelaufen<br />
*Bit 1 '''INT0IF''': RB0/INT External Interrupt Flag Bit<br />
::1 = Eine Änderung an RB0/INT Pin hat stattgefunden (muss per Software gelöscht werden)<br />
::0 = Eine Änderung an RB0/INT Pin hat nicht stattgefunden <br />
*Bit 0 '''RBIF''': RB Port On-Change-Interrupt Flag bit<br />
::1 = Mind. einer der Pins von RB7:RB4 hat sich geändert (muss per Software gelöscht werden)<br />
::0 = Keiner der Pins von RB7:RB4 hat sich geändert<br />
<br />
<br />
* *) Die Flanke auf die reagiert werden soll, wird mit dem Bit OPTION_REG,INTEDG definiert. (steigende = 1 oder fallende = 0)<br />
* **) Der Interrupt klingt verführerischer Weise so, als ob er den gesamten Port überwacht. Dabei reagiert er nur auf RB7:RB4!!!! Er kann aber den Prozessor vom "Schlaf" aufwecken (z.B. durch einen Tastendruck).<br />
<br />
==Speicherbankorganisation==<br />
===Programmspeicher===<br />
Die Mid-Range MCUs haben einen 2-8k großen Programmspeicher. Dieser hat aber in jeder Speicherzelle nicht 8, sondern 14 Bit - also genau die Länge eines Befehls. Die aktuelle Stelle im Programm wird im PC (Program Counter) verwaltet. Er speichert immer die aktuelle Adresse im Programmspeicher (wird im Code oft als "$" bezeichnet). Bei einem PIC mit 8k Adressen muss er also die Adressen 0000-1FFF speichern können. Daraus folgt die Größe von 13 Bit für den PC. Der Programmspeicher ist in mehrere Bänke geteilt, die alle 2k groß sind. Das Programm springt ohne zutun des Benutzers von einer in die Nächste. Wenn man aber selber springen will, muss man die Register PCLATH (Program Counter Latch High) und PCL (Program Counter Least Significant Byte) mit der Sprungadresse beschreiben.<br />
<br />
===Datenspeicher===<br />
Der Datenspeicher besteht aus den Special Function Registern (SFR) und den General Purpose<br />
Registern (GPR). Die SFRs sind für die Funktionen des PICs zuständig (Interrupts, Timer, ADCs, CCPM...) und die GPRs für die Speicherung von Variablen und Daten.<br />
<br />
Da immer nur 7 Bit der (Ziel)Adresse in einem Befehl gespeichert werden können, sind nur 7Fh (128d) Adressen im Datenbereich möglich. Deswegen wurde das "Banking" eingeführt. 2 Bit im Statusregister (welcher in allen Bänken der selbe ist und auch an der gleichen Stelle sitzt) geben die akutelle "Bank" an und sind nichts anderes als die 2 höchstwertigsten (MSB) Bits der Adresse. Damit lassen sich max. 4 Bänke ansprechen. Je nach PIC gibt es 2-4 Bänke. Die beiden Bits im Register STATUS heißen RP0 (STATUS<5>) und RP1 (STATUS<6>).<br />
<br />
{| {{Blauetabelle}}<br />
|+ Wechseln der Bänke mit RP0 und RP1<br />
|-<br />
| <br />
|| RP1<br />
|| RP0<br />
|-<br />
| Bank0<br />
|align="center"|0<br />
|align="center"|0<br />
|-<br />
| Bank1<br />
|align="center"|0<br />
|align="center"|1<br />
|-<br />
| Bank2<br />
|align="center"|1<br />
|align="center"|0<br />
|-<br />
| Bank3<br />
|align="center"|1<br />
|align="center"|1<br />
|}<br />
<br />
[[Bild:PIC midrange register.JPG]]<br />
# '''FETTE Register''' sind in allen PICs vorhanden<br />
# können je nach PIC unimplementierte Bereiche beinhalten - diese werden immer als 0 gelesen. (DATENBLATT!!)<br />
# siehe 2<br />
# Könnten je nach PIC auch nicht in Bank0 gemapped werden, sind dann eigenständige Register.<br />
# je nach PIC kann es diese Bänke geben oder nicht geben.<br />
<br />
<br />
<br />
== Codeschnipsel ==<br />
<br />
Alle hier sich befindliche Programmbeispiele sind nur auf den PIC12... und PIC16... sicher lauffähig und müssen für PIC18... bei Problemen umgeschrieben werden. Weitere Beispiele befinden sich in http://www.rn-wissen.de/index.php/PIC_ASM_Beispiele<br />
<br />
=== Analog-Digital-Wandler (ADC) ===<br />
<br />
Viele PICs besitzen einen Analog-Digital-Wandler, der eine angelegte Spannung messen kann und diese als Zahl speichert.<br />
<br />
Es gibt AD-Wandler mit 8Bit bzw. 10Bit Auflösung, d.h. die Spannung an einem analogen Eingang wird linear 2^8=256 bzw. 2^10=1024 Werten zugeordnet.<br />
Der höchste Wert, auch Referenzspannung genannt, (255 bzw. 1023; der Controller rechnet die Null auch mit) entspricht der Betriebsspannung des PICs oder wahlweise einer wählbaren Spannung, die an RA3 angelegt werden muss. Der niedrigste Wert entspricht 0 Volt.<br />
<br />
Mit dem Analog-Digital-Wandler kann man somit z.B. Sensoren auswerten, die mehr als zwei Zustände ausgeben (Beispiele: Temperatursensor, Helligkeitssensor, Entfernungssensoren usw.) oder Akkuspannungen messen.<br />
<br />
<br />
Bevor überhaupt gemessen werden kann, muss einiges eingestellt werden:<br />
<br />
* Eingangsverteilung Analog/Digital bzw.Referenzspannung (Betriebsspannung oder RA3)<br />
die genauen Einstellungsmöglichkeiten entnehmen sie bitte dem Datenblatt ihres Controllers (Register ADCON1 Bank 1).<br />
<br />
* Wählen des Taktes für den ADC und Einschalten des ADCs<br />
<br />
Das Register ADCON0 besitzt 8 Bits, die folgendes bestimmen:<br />
Bit0 ADON - schaltet AD-Wandler ein oder aus (Strom sparen)<br />
Bit1 - nicht vorhanden<br />
Bit2 GO/Done - startet Wandlung / signalisiert, wann Umwandlung beendet ist.<br />
Bit3 CHS0 - Channel Select 0 wählt Eingang aus<br />
Bit4 CHS1 - Channel Select 1 wählt Eingang aus<br />
Bit5 CHS2 - Channel Select 2 wählt Eingang aus<br />
Bit6 ADCS0 - AD-Clock-Select wählt Takt<br />
Bit7 ADCS1 - AD-Clock-Select wählt Takt<br />
<br />
Kümmern wir uns zunächst um den Takt. Mit den zwei Bits ADCS0 und ADCS1 bestimmt man den Takt - hierbei gibt es vier Möglichkeiten:<br />
<br />
ADCS1=0, ADCS0=0 -> Taktfrequenz des PICs :2<br />
ADCS1=0, ADCS0=1 -> Taktfrequenz des PICs :8<br />
ADCS1=1, ADCS0=0 -> Taktfrequenz des PICs :32<br />
ADCS1=1, ADCS0=1 -> interner RC-Oszillator des ADCs verwenden<br />
<br />
Die Taktfrequenz des ADCs darf 625kHz nicht überschreiten, dementsprechend ist ADCS0 und ADCS1 zu wählen.<br />
<br />
Ist dies vollbracht, so kann man den AD-Wandler einschalten und das Go/Done-Bit löschen.<br />
Codebeispiel:<br />
<br />
bcf ADCON0,6 ;Takt für<br />
bsf ADCON0,7 ;ADC (OSC/32)<br />
bsf ADCON0,0 ;ADC einschalten, ADON=1<br />
bcf ADCON0,2 ;Go/Done-Bit zurücksetzen<br />
<br />
Nun ist der AD-Wandler bereit, Spannungen in Zahlen umzuwandeln.<br />
Für eine Wandlung muss erst der entsprechende Eingang gewählt werden. Dafür sind die CHS0..CHS2-Bits zuständig:<br />
<br />
CHS2 CHS1 CHS0 gewählter Eingang(falls vorhanden)<br />
0 0 0 RA0<br />
0 0 1 RA1<br />
0 1 0 RA2<br />
0 1 1 RA3<br />
1 0 0 RA5<br />
1 0 1 RE0<br />
1 1 0 RE1<br />
1 1 1 RE2<br />
<br />
Achtung! RA4 ist kein analoger Eingang, folglich ist er oben nicht aufgelistet !!<br />
<br />
Codebeispiel:<br />
<br />
bcf ADCON0,3 ;RA2 auswählen<br />
bsf ADCON0,4<br />
bcf ADCON0,5<br />
<br />
Als letztes muss die AD-Routine nur noch gestartet werden. Ist der AD-Wandler fertig, so löscht er das Bit wieder. Das Bit wird nun solange abgefragt, bis der AD-Wandler fertig ist. Den zugeordneten Wert findet man im Register 'ADRES'. <br />
Code:<br />
<br />
bsf ADCON0,2 ;start<br />
wa btfsz ADCON0,2 ;warten,bis es wieder low ist<br />
goto wa ;noch nicht fertig<br />
;jetzt ist er fertig<br />
movf ADRES,W ;ADRES in W verschieben, um damit gleich weiter zu arbeiten<br />
<br />
Die AD-Wandlung ist somit fertig. Liest man mehrere Werte von unterschiedlichen Eingängen ein und möchte diese miteinander vergleichen o.ä., sollte man die Zahlen von ADRES in anderen Registern zwischenspeichern.<br />
<br />
Desweiteren ist zu beachten, dass der Analog-Digital-Wandler eine gewisse Pause zwischen den Wandlungen benötigt, die sog. 'Aquisition Time'. Diese Zeitangabe entnehmen sie bitte ihrem Datenblatt.<br />
<br />
mögliche Probleme:<br />
<br />
<br />
;Der PIC liefert stark schwankende Werte<br />
:-> Mögliche Ursachen dafür sind z.B. nicht stabile Betriebsspannung, falsche Zeiteinstellungen. Abhilfe schafft auch das mehrmalige Einlesen eines Eingangs. Auch können z.B. 8 Werte eingelesen werden und dann der Durchschnitt gebildet werden.<br />
Evtl. kann ein Kondensator gegen Masse (z.B. 1nF) helfen; Wichtig ist auch eine kleine Verzögerung zwischen Kanalauswahl und Start der Wandlung. In dieser Zeit kann sich der interne Kondensator des ADC's aufladen.<br />
<br />
<br />
;Die Spannung an einem Eingang wird erhöht, die Werte der anderen Eingänge werden aber auch beeinflusst.<br />
:-> Dann sind Sie Opfer des sog. 'Ghostings' geworden (Werte 'scheinen durch', verschleißen). Die Werte der anderen Eingänge gehen mit, weil der interne Kondensator noch auf die Spannung des Vorgängers aufgeladen ist. Maßnahme: Aquisition Time erhöhen, Werte öfters abfragen, Pause zwischen Kanalauswahl und Start<br />
<br />
<br />
;Der PIC liefert immer die gleichen Werte an einem Eingang, obwohl sich die angelegte Spannung ändert<br />
:-> möglicherweise falsche Eingangsverteilung (ADCON1) eingestellt oder falschen Pin gewählt<br />
<br />
=== Hex Dec Wandlung ===<br />
<br />
Das ist ein Unterprogramm das mit "call Hex_Dec" aufgerufen wird. Es gibt 4 Register (A, B, C und D) mit jeweils 32 Bits, die aus jeweils 4 Register mit 8 Bits (z.B. fur A: A3, A2, A1 und A0) bestehen.<br />
<br />
A3 A2 A1 A0<br />
.--------.--------.--------.--------.<br />
|76543210|76543210|76543210|76543210|<br />
'--------'--------'--------'--------'<br />
| |<br />
|<----------- A Register ---------->|<br />
<br />
Sie müssen (wegen FSR) so wie im Code nacheinander liegen (die absoluten Adressen sind nicht wichtig). In das Register "A" wird die hex Zahl eingeschrieben und aus dem "D" die umgewandelte dec Zahl ausgelesen. A3,7 und D3,7 sind MSB und A0,0 und D0,0 sind LSB. Die "B" und "C" sind Hilfsregister. Das UP kann für kleinere als 32-bittige hex Zahlen modifiziert werden. Zum Beispiel für 16-bittige hex Zahlen bleiben nur Register A1,A0,B1,B0,C1,C0,D1,D0 und in den UPs werden in die Register und als X, folgende Zahlen eingeschrieben: HTmp -> 0x10 (Anzahl Bits der hex Zahl), ATmp -> 2 = X (Anzahl Bytes der hex Zahl). Es müssen auch die UPs "CClr", "DClr" und "CopyCB" entsprechend der Registerlänge angepasst werden. Genauso kann natürlich das UP auch für längere hex Zahlen modifiziert werden.<br />
<br />
Das UP "AddCB" addiert dezimal die 32- bittige Register C und B, das Ergebniss befindet sich in C.<br />
Das UP "AddDC" addiert dezimal die 32-bittige Register D und C, das Ergebniss befindet sich in D.<br />
<br />
Die 32-bit Additionen werden in 4 Schritten wie folgt realisiert:<br />
<br />
C0+B0->C0, C1+B1->C1, C2+B2->C2 und C3+B3->C3 <br />
<br />
D0+C0->D0, D1+C1->D1, D2+C2->D2 und D3+C3->D3<br />
<br />
Die Flags "_Fcra", "_Fcrp" und "_Fdca" steuern die alle nötigen Korrekturen.<br />
<br />
Die Wandlung wird in 32 Schritten laut folgender Formel durchgeführt:<br />
<br />
Zahl(d)=B0*2^0+B1*2^1+B2*2^2+B3*2^3+B4*2^4+B5*2^5+B6*2^6+B7*2^7+B8*2^8+B9*2^9+B10*2^10+B11*2^11+<br />
<br />
B12*2^12+B13*2^13+B14*2^14+B15*2^15+B16*2^16+B17*2^17+B18*2^18+B19*2^19+B20*2^20+B21*2^21+<br />
<br />
B22*2^22+B23*2^23+B24*2^24+B25*2^25+B26*2^26+B27*2^27+B28*2^28+B29*2^29+B30*2^30+B31*2^31<br />
<br />
Wobei B0 bedeutet den LSB und B31 den MSB der hex Zahl.<br />
<br />
Die dec Zahl wird im D Register durch "AddDC" gebildet. In dem C Register befindet sich immer die laufende 2^X. Am Anfang wird dort 1=2^0 geladen. Da die nächste zu addierende dec Zahl immer gleich 2* vorherige ist, wird sie einfach so berechnet, dass die aktuelle aus C Register ins B Register kopiert ("CopyCB") und zu C addiert ("AddCB") wird. Diese dec Zahl wird zum D Register addiert nur wenn das entsprechende Bit der hex Zahl gleich 1 ist. Weil im UP "Hex_Dec" immer das bit A0,0 dafür geprüft wird, wird das gesamte 32-bittige A Register nach jeder Addition "AddDC" um ein Bit mit "ARotRb" nach rechts rotiert. Die Flags "_Fcra" und "_Fcrp" übertragen die Bits zwischen den 8 Bit Registern A0, A1, A2 und A3. Es würde zwar reichen, wenn das A Register nur verschoben würde, aber hier wurde Rotation gewählt, damit nach der Wandlung die hex Zahl im A Register unverändert steht. Weil die dec Zahl nur 8-stellig ist, darf die max. hex Zahl 99999999d = 5F5E0FFh sein.<br />
<br />
#define _C STATUS,C<br />
#define _Z STATUS,Z<br />
#define _DC STATUS,DC<br />
#define _Fcra Flags,0<br />
#define _Fcrp Flags,1<br />
#define _Fdca Flags,2<br />
#define _Ferr Flags,3 ;Überlauf (Fehler)<br />
;.................................................................................. <br />
A3 EQU 0x20 ;Register fürs Rechnen<br />
A2 EQU 0x21<br />
A1 EQU 0x22<br />
A0 EQU 0x23<br />
B3 EQU 0x24<br />
B2 EQU 0x25<br />
B1 EQU 0x26<br />
B0 EQU 0x27<br />
C3 EQU 0x28<br />
C2 EQU 0x29<br />
C1 EQU 0x2A<br />
C0 EQU 0x2B<br />
D3 EQU 0x2C<br />
D2 EQU 0x2D<br />
D1 EQU 0x2E<br />
D0 EQU 0x2F<br />
;..................................................................................<br />
Flags EQU 0x30<br />
ATmp EQU 0x31 ;Schleifenzähler<br />
HTmp EQU 0x32 ;Schleifenzähler<br />
RTmp EQU 0x33 ;Zwischenspeicher<br />
;..................................................................................<br />
Hex_Dec call DClr ;Hex>A, D>Dec<br />
movlw 1<br />
movwf C0<br />
movlw 0x20 ;32 bit Hex > 32 bit Dec (8 Stellen)<br />
movwf HTmp<br />
HexDecL btfsc A0,0<br />
call AddDC<br />
call CopyCB<br />
call AddCB<br />
call ARotRb<br />
decfsz HTmp,1<br />
goto HexDecL<br />
return<br />
DClr clrf D0<br />
clrf D1<br />
clrf D2<br />
clrf D3<br />
clrf C0<br />
clrf C1<br />
clrf C2<br />
clrf C3<br />
return<br />
CopyCB movf C0,0<br />
movwf B0<br />
movf C1,0<br />
movwf B1<br />
movf C2,0<br />
movwf B2<br />
movf C3,0<br />
movwf B3<br />
return<br />
AddCB movlw B0 ;C+B>C<br />
movwf FSR<br />
goto AddR<br />
AddDC movlw C0 ;D+C>D<br />
movwf FSR<br />
AddR bcf _Ferr ;Addiere zwei Register<br />
bcf _Fcrp<br />
movlw 4 ;X=4 Bytes lang<br />
movwf ATmp<br />
AddRL bcf _Fdca<br />
bcf _Fcra<br />
movf INDF,0<br />
movwf RTmp<br />
movlw 4 ;X=4 <br />
addwf FSR,1<br />
btfss _Fcrp<br />
goto AddRN<br />
movlw 1<br />
addwf INDF,1<br />
call DecCor<br />
AddRN movf RTmp,0<br />
addwf INDF,1<br />
call DecCor<br />
btfss _Fdca<br />
goto AddCor1<br />
movlw 6<br />
addwf INDF,1<br />
AddCor1 btfss _Fcra<br />
goto AddCor2<br />
movlw 0x60<br />
addwf INDF,1<br />
btfsc _C<br />
bsf _Fcra<br />
AddCor2 movf INDF,0<br />
andlw 0xF0<br />
sublw 0xA0<br />
btfss _Z<br />
goto AddCor3<br />
movf INDF,0<br />
andlw 0x0F<br />
clrf INDF<br />
addwf INDF,1<br />
bsf _Fcra<br />
AddCor3 bcf _Fcrp<br />
btfsc _Fcra<br />
bsf _Fcrp<br />
movlw 5 ;X+1=5<br />
subwf FSR,1<br />
decfsz ATmp,1<br />
goto AddRL<br />
btfsc _Fcra<br />
bsf _Ferr<br />
return<br />
DecCor btfsc _DC ;dezimale Korrektur (equ "DAW" bei PIC18FXXX)<br />
bsf _Fdca<br />
btfsc _C<br />
bsf _Fcra<br />
movf INDF,0<br />
andlw 0x0F<br />
sublw 9<br />
btfss _C<br />
bsf _Fdca<br />
movf INDF,0<br />
andlw 0xF0<br />
sublw 0x90<br />
btfss _C<br />
bsf _Fcra<br />
return<br />
ARotRb movlw A3 ;rotiere A Register 1 Bit rechts<br />
movwf FSR <br />
RRotRb movlw 4 ;rotiere X=4 Bytes<br />
movwf ATmp<br />
bcf _Fcrp<br />
btfsc A0,0<br />
bsf _Fcrp<br />
RRotRbL bcf _Fcra<br />
btfsc INDF,0<br />
bsf _Fcra<br />
bcf _C<br />
btfsc _Fcrp<br />
bsf _C<br />
rrf INDF,1<br />
bcf _Fcrp<br />
btfsc _Fcra<br />
bsf _Fcrp<br />
incf FSR,1<br />
decfsz ATmp,1<br />
goto RRotRbL<br />
return<br />
<br />
=== EEPROM ===<br />
<br />
Alle PICs besitzen EEPROM in dem je nach Typ können 64 bis 256 Databytes abgespeichert werden. Die Adressierung des EEPROMs fängt immer mit 0x00 an. Hier werden nur geprüfte UPs kurz erklärt. Die ersten zwei brauchen nur ein Register "Temp" für Schleifenzähler, dessen Name, falls im Programm schon existiert, geändert werden muss. Die Adresse im EEPROM ist gleich der Adresse im RAM.<br />
<br />
EEPROM beschreiben: <br />
<br />
EEWrite movlw 0x20 ; ab der RAM Adresse wird in EEPROM abgespeichert<br />
movwf FSR<br />
movlw 4 ; soviel Bytes<br />
movwf Temp ; Schleifenzähler<br />
EEWLoop call EEWrite1<br />
incf FSR,1 ; nächste Adresse<br />
decfsz Temp,1<br />
goto EEWLoop<br />
return <br />
<br />
EEWrite1 bcf INTCON,GIE ; Interrupts sperren<br />
movf FSR,0<br />
bsf STATUS,RP0 ; auf Bank1 umschalten<br />
movwf EEADR ; EEPROM Adresse<br />
movf INDF,0<br />
movwf EEDATA ; EEPROM Data<br />
bsf EECON1,WREN<br />
movlw 0x55<br />
movwf EECON2<br />
movlw 0xAA<br />
movwf EECON2<br />
bsf EECON1,WR<br />
bcf EECON1,WREN<br />
btfsc EECON1,WR<br />
goto $-1 ; warten bis WR=0<br />
bcf STATUS,RP0 ; zurück auf Bank 0 umschalten<br />
bsf INTCON,GIE ; Interrupts erlauben<br />
return<br />
<br />
EEPROM lesen und zurück in RAM schreiben: <br />
<br />
EERead movlw 0x20 ; ab der Adresse wird aus EEPROM in RAM abgelegt <br />
movwf FSR<br />
movlw 4 ; soviel Bytes<br />
movwf Temp ; Schleifenzähler<br />
EERLoop call EERead1<br />
incf FSR,1 ; nächste Adresse<br />
decfsz Temp,1<br />
goto EERLoop<br />
return<br />
<br />
EERead1 movf FSR,0<br />
bsf STATUS,RP0 ; auf Bank1 umschalten <br />
movwf EEADR ; EEPROM Adresse<br />
bsf EECON1,RD<br />
movf EEDATA,0 ; EEPROM Data<br />
bcf STATUS,RP0 ; zurück auf Bank 0 umschalten<br />
movwf INDF<br />
return<br />
<br />
Tabelle im EEPROM mit MPASM Direktive "de" erstellen:<br />
<br />
org 0x2100 ; EEPROM initialisieren<br />
; nachfolgend als Kommentar die EEPROM Adressen<br />
de 0x03, 0xE8 ; 0x00<br />
de 0x03, 0xE8 ; 0x02<br />
de 'M','H','z',0x00 ; 0x04<br />
de ' ',' ','W','A','I','T',0x00 ; 0x08<br />
de 'C','o','n','s','t',' ','X',0x00 ; 0x0F<br />
de 'C','=',0x00 ; 0x17<br />
de 'L','=',0x00 ; 0x1A<br />
de 'F','=',0x00 ; 0x1D<br />
de 'O','K',0x00 ; 0x20<br />
usw.<br />
<br />
Wenn die Tabelle sich im Quellcode befindet, wird sie beim brennen des PICs in EEPROM eigeschrieben.<br />
<br />
Das Lesen eines Strings muss ab entsprechender EEPROM Adresse (z.B. für "MHz" -> 0x04) anfangen und auf dem Wert 0x00, der hier als Stringende dient, enden. Einfacher ist, wenn alle Strings gleich lang sind (wie z.B. in einem Zeichengenerator für Grafikdisplay).<br />
<br />
Die Adresse "0x2100" in der "org" Direktive hat mit der Adresse im Programmspeicher nichts zu tun.<br />
<br />
=== Interrupts ===<br />
<br />
Das Programm misst eine Frequenz an T0CKI Pin, wurde für den PIC16F628 geschrieben und in einem genauen Frequenzzähler angewendet. Es ist nur auf PICs lauffähig, die mindestens zwei Timer besitzen.<br />
<br />
Es gibt nur ein Messbereich von 1 Hz bis 99999999 Hz mit gleicher Auflösung 1 Hz, deswegen ist kein Dezimalpunkt benutzt. Für einen kompletten Frequenzzähler ist nur noch ein Eingangsverstärker nötig.<br />
<br />
Benötigte Hardware: Widerstand 470 Ohm zwischen der Frequenzquelle und TOCKI Pin (A4). <br />
<br />
Die Ausgabe erfolgt auf ein 2x8 standard Zeichendisplay. Das HP ("Main") als leere endlose Schleife macht nichts außer warten auf ein Interrupt von Timer0 bzw. Timer1. Die ISR benutzt keine Register vom UPs und deshalb müssen W- und STATUS Register nicht gesichert und wiederhergestellt werden.<br />
<br />
Da der Timer0 nicht, wie der Timer1 durch ein Flag gestartet und gestoppt werden kann, wird es durch setzen und löschen des Bits für TOCKI (A4) Pin im TRISA Register, genauso wie in der AN592 vom Microchip, gemacht.<br />
<br />
Der Timer 0 zählt die Impulse am TOCKI Pin (A4) in der Zeit, die vom Timer 1 bestimmt wird.<br />
<br />
Der Zähler der Frequenz wurde um zwei zusätzliche Register A2 und A3 erweitert und bei jedem Timer0 Überlauf erhöht. Der Prescaler wird ins Register A0 und der TMR0 ins A1 kopiert. Somit ergibt sich 32-bittiges Ergebnis, der als hex Zahl in den Register A3 (MSB),A2, A1, und A0 (LSB) steht. Nach der "Hex_Dec" Wandlung kann die Frequenz aus den Register D3 (MSB), D2, D1 und D0 (LSB) an einem beliebigen Display angezeigt werden.<br />
<br />
Die Quarzfrequenz 7,3728 MHz wurde gewählt, weil sie am nächsten der temperaturstabiltesten Frequenz für AT Quarzen 7,2 MHz ist. Die Quarzfrequenz muß nicht genau sein, da der Frequenzzähler lässt sich sehr genau mit den Werten von TMR1H und TMR1L "trimmen", die vor dem Start des Timers 1 geladen werden.<br />
<br />
Für sehr genaue Messungen wird empfohlen als Taktfrequenz für den Timer1, die Trägerfrequenz eines nächsten LW Senders (z.B. DLF 153 bzw. 207 kHz) zu nutzen. Die Genauigkeit der Frequenz ist ca. 1E-11 und geprüfter Empfänger kann laut der Skizze in [[http://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=13706&highlight=frequenzz%E4hler]] nachgebaut werden. In dem Fall müssen die Register vom Timer1 (TMR1H und TMR1L) vor seinem Start mit entsprechenden Werten geladen werden. <br />
<br />
Hier wird die Messzeit 1 s genutzt und die Frequenz wird mit einer Auflösung von 1 Hz gemessen. Die Messzeit beträgt 3*10000h+(FFFFh-7BFFh) Timertakten. Für den Quarz 7,372800 MHz und Prescaler=8, wird der Takt von Timer1 gleich 7372800/4*8=230400 Hz. Deswegen wird der Timer1 beim Starten mit ca. 7BFFh geladen und nach dem 4. Überlauf gestoppt. Die in TMR1H und TMR1L praktisch geladene Werte unterscheiden sich ein bißchen von berechneten, weil die Quarzfrequenz fast nie genau bis auf 1 Hz stimmt.<br />
<br />
Für z.B. 20 MHz Quarz werden 10 Überläufe des Timers benötigt und er wird beim Start mit 7697h geladen, da 1s gleich 9x10000h+(FFFFh-7697h) Timertakten ist.<br />
<br />
In dem UP "Init" muss eventuell für ein anderes Display die Initialisierung des Displaykontrollers geändert werden.<br />
<br />
In dem Program wurden unveränderte UPs verwendet, die näher in [[#Hex Dec Wandlung|Hex Dec Wandlung]] und [[#Matrix|Matrix]] erklärt sind.<br />
<br />
; Programm zum Messen der Frequenz an T0CKI Pin mit 7,3728 MHz Quarz <br />
LIST P=16F628<br />
include "P16F628.inc"<br />
__CONFIG _WDT_OFF & _PWRTE_ON & _MCLRE_OFF & _HS_OSC & _BODEN_OFF & _LVP_OFF<br />
<br />
; TOCKI PORTA,4 ;I Frequenzeingang<br />
; DB4 PORTB,0 ;O Display Data Bit 4<br />
; DB5 PORTB,1 ;O Display Data Bit 5<br />
; DB6 PORTB,2 ;O Display Data Bit 6<br />
; DB7 PORTB,3 ;O Display Data Bit 7<br />
#define _RS PORTB,4 ;O Display RS<br />
#define _E PORTB,5 ;O Display Enable<br />
#define _C STATUS,C<br />
#define _Z STATUS,Z<br />
#define _DC STATUS,DC<br />
#define _RP0 STATUS,RP0<br />
#define _Fcra Flags,0<br />
#define _Fcrp Flags,1<br />
#define _Fdca Flags,2<br />
#define _Ferr Flags,3 ;Überlauf (Fehler)<br />
#define _Frs Flags,4<br />
#define _Fnz Flags,5<br />
A3 equ 0x20 ;Register fürs Rechnen Hex_Dec<br />
A2 equ 0x21<br />
A1 equ 0x22<br />
A0 equ 0x23<br />
B3 equ 0x24<br />
B2 equ 0x25<br />
B1 equ 0x26<br />
B0 equ 0x27<br />
C3 equ 0x28<br />
C2 equ 0x29<br />
C1 equ 0x2A<br />
C0 equ 0x2B<br />
D3 equ 0x2C<br />
D2 equ 0x2D<br />
D1 equ 0x2E<br />
D0 equ 0x2F<br />
Tmp equ 0x30 ;Register, die für Displayausgabe<br />
Tmp1 equ 0x31 ;vorläufig benutzt werden<br />
Flags equ 0x32<br />
ATmp equ 0x33 ;vorläufige Schleifenzähler<br />
HTmp equ 0x34<br />
RTmp equ 0x35 ;Zwischenspeicher<br />
ORG 0x0000<br />
call Init ;alles initialisieren<br />
Main goto Main ;und in die leere endlose Schleife springen <br />
ORG 0x0004 ;hier fängt ISR (interrupt service routine) an<br />
bcf INTCON,GIE ;alle interrupts sperren<br />
btfss INTCON,T0IF ;ist Timer0 überlaufen ?<br />
goto CheckTMR1 ;nein, dann prüfe Timer1<br />
bcf INTCON,T0IF ;ja, Timer0 interrupt flag löschen und bearbeiten<br />
movlw 1<br />
addwf A2,1 ;erhöhe A2 durch Addition<br />
btfsc _C ;um Überlauf von A2 zu erkennen und<br />
incf A3,1 ;increment A3, wenn A2 überlaufen ist<br />
CheckTMR1 btfss PIR1,TMR1IF ;ist Timer1 überlaufen ?<br />
retfie ;nein, dann ISR beenden und interrupts erlauben<br />
bcf PIR1,TMR1IF ;Timer1 interrupt flag löschen<br />
call Multip ;Interrupts vom Timer1 im UP "Multip" zählen<br />
retfie ;ISR beenden und interrupts erlauben<br />
Multip incf Tmp,1 ;Interruptszähler von Timer1 erhöhen<br />
btfss Tmp,2 ;schon 4.interrupt?<br />
return ;nein, dann zurück<br />
bsf _RP0 ;ja, weiter<br />
movf TRISA,0 ;stopp Timer0 usw.<br />
andlw 0xEF<br />
movwf TRISA ;TOCKI Pin (A4) als Ausgang<br />
bcf _RP0<br />
bcf T1CON,TMR1ON ;stopp Timer1<br />
GetFreq movf TMR0,0 ;Prescaler ins Register A0 kopieren<br />
movwf A1<br />
movwf RTmp<br />
clrf ATmp<br />
FToggle incf ATmp,1<br />
bsf _RP0<br />
bsf OPTION_REG,T0SE<br />
bcf OPTION_REG,T0SE<br />
bcf _RP0<br />
movf TMR0,0<br />
subwf RTmp,0<br />
btfsc _Z<br />
goto FToggle<br />
comf ATmp,1<br />
incf ATmp,0<br />
movwf A0<br />
call Hex_Dec ;Messergebnis auf Decimal wandeln<br />
call AClr <br />
call DispFrq ;eventuell eigenes UP für benutztes Display<br />
clrf Tmp<br />
clrf TMR0<br />
call TMRStart ;beide Timer starten<br />
return<br />
AClr clrf A0 ;Register A löschen<br />
clrf A1<br />
clrf A2<br />
clrf A3<br />
return<br />
TMRStart movlw 0x34 ;Timer1 konfigurieren<br />
movwf T1CON ;und laden<br />
movlw 0x7B ;berechneter Wert: TMR1H=7Bh<br />
movwf TMR1H<br />
movlw 0xFB ;berechneter Wert: TMR1L=FFh<br />
movwf TMR1L<br />
bsf _RP0 ;start Timer0<br />
movf TRISA,0<br />
iorlw 0x10<br />
movwf TRISA ;TOCKI Pin (A4)als Eingang<br />
bcf _RP0<br />
bsf T1CON,TMR1ON ;start Timer1<br />
return<br />
Hex_Dec call DClr ;Hex>A, D>Dec<br />
movlw 1<br />
movwf C0<br />
movlw 0x20 ;32 bit Hex > 32 bit Dec (8 Stellen)<br />
movwf HTmp<br />
HexDecL btfsc A0,0<br />
call AddDC<br />
call CopyCB<br />
call AddCB<br />
call ARotRb<br />
decfsz HTmp,1<br />
goto HexDecL<br />
return<br />
DClr clrf D0<br />
clrf D1<br />
clrf D2<br />
clrf D3<br />
clrf C0<br />
clrf C1<br />
clrf C2<br />
clrf C3<br />
return<br />
CopyCB movf C0,0<br />
movwf B0<br />
movf C1,0<br />
movwf B1<br />
movf C2,0<br />
movwf B2<br />
movf C3,0<br />
movwf B3<br />
return<br />
AddCB movlw B0 ;C+B>C<br />
movwf FSR<br />
goto AddR<br />
AddDC movlw C0 ;D+C>D<br />
movwf FSR<br />
AddR bcf _Ferr ;Addiere zwei Register<br />
bcf _Fcrp<br />
movlw 4 ;X=4 Bytes lang<br />
movwf ATmp<br />
AddRL bcf _Fdca<br />
bcf _Fcra<br />
movf INDF,0<br />
movwf RTmp<br />
movlw 4 ;X=4 <br />
addwf FSR,1<br />
btfss _Fcrp<br />
goto AddRN<br />
movlw 1<br />
addwf INDF,1<br />
call DecCor<br />
AddRN movf RTmp,0<br />
addwf INDF,1<br />
call DecCor<br />
btfss _Fdca<br />
goto AddCor1<br />
movlw 6<br />
addwf INDF,1<br />
AddCor1 btfss _Fcra<br />
goto AddCor2<br />
movlw 0x60<br />
addwf INDF,1<br />
btfsc _C<br />
bsf _Fcra<br />
AddCor2 movf INDF,0<br />
andlw 0xF0<br />
sublw 0xA0<br />
btfss _Z<br />
goto AddCor3<br />
movf INDF,0<br />
andlw 0x0F<br />
clrf INDF<br />
addwf INDF,1<br />
bsf _Fcra<br />
AddCor3 bcf _Fcrp<br />
btfsc _Fcra<br />
bsf _Fcrp<br />
movlw 5 ;X+1=5<br />
subwf FSR,1<br />
decfsz ATmp,1<br />
goto AddRL<br />
btfsc _Fcra<br />
bsf _Ferr<br />
return<br />
DecCor btfsc _DC ;dezimale Korrektur (equ "DAW" bei PIC18FXXX)<br />
bsf _Fdca<br />
btfsc _C<br />
bsf _Fcra<br />
movf INDF,0<br />
andlw 0x0F<br />
sublw 9<br />
btfss _C<br />
bsf _Fdca<br />
movf INDF,0<br />
andlw 0xF0<br />
sublw 0x90<br />
btfss _C<br />
bsf _Fcra<br />
return<br />
ARotRb movlw A3 ;rotiere A Register 1 Bit rechts<br />
movwf FSR <br />
RRotRb movlw 4 ;rotiere X=4 Bytes<br />
movwf ATmp<br />
bcf _Fcrp<br />
btfsc A0,0<br />
bsf _Fcrp<br />
RRotRbL bcf _Fcra<br />
btfsc INDF,0<br />
bsf _Fcra<br />
bcf _C<br />
btfsc _Fcrp<br />
bsf _C<br />
rrf INDF,1<br />
bcf _Fcrp<br />
btfsc _Fcra<br />
bsf _Fcrp<br />
incf FSR,1<br />
decfsz ATmp,1<br />
goto RRotRbL<br />
return<br />
DispFrq bcf _Fnz ;Frequenz anzeigen (8 Ziffern)<br />
call Fst ;mit Unterdrückung der führenden Nullen <br />
movlw 3<br />
movwf ATmp<br />
movlw D3<br />
movwf FSR<br />
swapf INDF,0<br />
andlw 0x0F<br />
btfsc _Z<br />
goto $+2<br />
bsf _Fnz<br />
btfss _Fnz<br />
goto $+3<br />
call Num<br />
goto $+3<br />
movlw " "<br />
call Char<br />
movf INDF,0<br />
andlw 0x0F<br />
btfsc _Z<br />
goto $+2<br />
bsf _Fnz<br />
btfss _Fnz<br />
goto $+3<br />
call Num<br />
goto $+3<br />
movlw " "<br />
call Char<br />
incf FSR,1<br />
decfsz ATmp,1<br />
goto $-18<br />
swapf D0,0<br />
andlw 0x0F<br />
btfsc _Z<br />
goto $+2<br />
bsf _Fnz<br />
btfss _Fnz<br />
goto $+3<br />
call Num<br />
goto $+3<br />
movlw " "<br />
call Char<br />
movf D0,0<br />
andlw 0x0F<br />
call Num<br />
call Snd ;zweite Zeile<br />
movlw "M"<br />
call Char<br />
movlw "^"<br />
call Char<br />
movlw " "<br />
call Char<br />
movlw "k"<br />
call Char<br />
movlw " "<br />
call Char<br />
movlw "^"<br />
call Char<br />
movlw "H"<br />
call Char<br />
movlw "z"<br />
call Char<br />
return<br />
Fst movlw 0x80 ;Adresse der ersten Zeile<br />
goto Cmd<br />
Snd movlw 0xC0 ;Adresse der zweiten Zeile<br />
Cmd movwf Tmp ;Befehl ins Tmp laden<br />
bcf _Frs ;RS=0<br />
goto Send<br />
Val movwf Tmp1<br />
swapf Tmp1,0<br />
call Num<br />
movf Tmp1,0<br />
Num andlw 0x0F<br />
movwf Tmp<br />
movlw 0x0A<br />
subwf Tmp,0<br />
btfsc _C<br />
addlw 7<br />
addlw 0x3A ;ASCII 0-F<br />
Char movwf Tmp ;Zeichen ins Tmp laden<br />
bsf _Frs ;RS=1<br />
Send swapf Tmp,0 ;zuerst High Nibble<br />
andlw 0x0F<br />
movwf PORTB ;an Port (Display) schicken<br />
btfsc _Frs ;RS Flag an Port kopieren<br />
bsf _RS<br />
bsf _E ;Enable erzeugen<br />
bcf _E<br />
movf Tmp,0 ;Low Nibble<br />
andlw 0x0F<br />
movwf PORTB ;an Port (Display) schicken<br />
Enab btfsc _Frs ;RS Flag in Port kopieren<br />
bsf _RS<br />
bsf _E ;Enable erzeugen<br />
bcf _E<br />
Del movlw 0x30 ;Verzögerung min. ca. 50µs<br />
movwf Tmp<br />
decfsz Tmp,1<br />
goto $-1<br />
return<br />
Init clrf PORTA ;Register vorm Start löschen <br />
clrf PORTB<br />
clrf Tmp<br />
clrf TMR0<br />
call AClr <br />
bsf _RP0 ;Bank 1<br />
movlw 0xFF<br />
movwf TRISA ;alle PORTA Pins als Eingänge<br />
clrf TRISB ;und alle PORTB Pins als Ausgänge<br />
movlw 0xE7 ;Takt für Timer0 vom T0CKI Pin usw.<br />
movwf OPTION_REG ;Timer0 konfigurieren<br />
bsf PIE1,TMR1IE ;TMR1 interrupt erlauben<br />
bcf _RP0 ;Bank 0<br />
movlw 0xE0 ;GIE, PEIE & TMR0IE erlauben<br />
movwf INTCON<br />
call TMRStart ;beide Timer starten<br />
bcf _Frs<br />
movlw 2 ;Display auf 4-bit umschalten und initialisieren<br />
movwf PORTB<br />
call Enab<br />
movlw 0x28 ;4 bit, 2 Zeilen, 5x7 Punkten<br />
call Cmd<br />
movlw 0x0C ;display an, cursor aus, nicht blinken<br />
call Cmd<br />
movlw 6 ;incrementieren, nicht schieben<br />
goto Cmd<br />
end<br />
<br />
=== Mausrad bzw. Drehencoder ===<br />
<br />
Das UP wertet ein Mausrad bzw. Drehencoder aus und setzt, je nach Drehrichtung und sein Anschluss an PORT (PORTB,0 und PORTB,1), ein Flag "_Finc" bzw. "_Fdec", das vor der nächsten Abfrage, gelöscht werden muss. Diese Flags können z.B. für Inkrementierung und Dekrementierung von Zählern dienen. <br />
<br />
Die Hardware:<br />
<br />
Mausrad bzw. Drehencoder<br />
.-------.<br />
| o---> PORTB,0<br />
+----o---- |<br />
| | o---> PORTB,1<br />
=== '-------'<br />
GND<br />
<br />
Es müssen die internen pull-ups vom PORTB aktiviert werden. Wenn das Mausrad bzw. Drehencoder an anderen PORT angeschlossen wird, werden externe pull-ups (z.B. 10 kOhm) benötigt. Als "_Z" wurde "STATUS,Z" definiert. Zum Auswerten werden 4 Register "MausA", "MausB", "MausC" und "Flags" gebraucht, die im gesamten Programm definiert werden müssen. Das UP "Delay" soll ca. 10ms dauern. Dafür kann eine Warteschleife oder zusammengesetzte UPs (z.B. Displayausgabe) mit solcher Ausführungszeit benutzt werden. <br />
<br />
In dem UP "Mouse" wird PORTB eingelesen, die alle Bits außer 0 und 1 durch "and" Funktion gelöscht und in den Register "MausB" und "MausC" gespeichert. Nach ca. 10ms wird das gleiche gemacht und im Register "MausA" gespeichert. Der Wert aus "MausA" wird mit dem vorherigen aus "MouseC" durch "xor" verglichen. Sind sie nicht identisch, wird je nach dem Wert "X" (0, 1, 2, bzw. 3) im "MausB" in das entsprechende UP "MouseX" gesprungen. Sonst, wenn "MausA"="MausC", wird zum Aufrufer zurückgekehrt.<br />
<br />
In einem UP "MouseX" wird der letzte Wert im "MausA" ermittelt und nach der Kombination der Werte im "MausB" und "MausA" (01 bzw. 02, 10 bzw. 13, 20 bzw. 23 oder 31 bzw. 32) entsprechendes der Drehrichtung Flag "_Finc" bzw. "_Fdec" gesetzt. Anschließend wird zum Aufrufer zurückgesprungen.<br />
<br />
Detaillierter PAD:<br />
<br />
Mouse V<br />
PORTB->W<br />
3 and W->W<br />
W->MausB<br />
W->MausC<br />
Warten 10ms<br />
PORTB->W <br />
3 and W->W<br />
W->MausA<br />
W xor MausC->MausC<br />
_Z=1 ? J > return<br />
N<br />
V<br />
MausB->MausB<br />
.--------------------------< J _Z=1 ?<br />
| N <br />
| V<br />
| MausB->W<br />
| 1-W->W<br />
| .----< J _Z=1 ?<br />
| | N<br />
| | V<br />
| | MausB->W<br />
| | 2-W->W<br />
| | _Z=1 ? J >---------------------------.<br />
| | N |<br />
| | V |<br />
| | MausB->W |<br />
| | 3-W->W |<br />
| | _Z=1 ? J >-----. |<br />
| | N | |<br />
| | V | |<br />
| | return | |<br />
| | | |<br />
| | | |<br />
Mouse0 V Mouse1 V Mouse3 V Mouse2 V<br />
MausA->W MausA->MausA MausA->W MausA->MausA<br />
1-W->W V 1-W->W V<br />
_Z=1 ? N >--. _Z=1 ? N >--. _Z=1 ? N >--. _Z=1 ? N >--.<br />
J | J | J | J |<br />
V | V | V | V |<br />
setze _Fdec | setze _Finc | setze _Finc | setze _Fdec |<br />
V<-----´ V<-----´ V<-----´ V<-----´<br />
MausA->W MausA->W MausA->W MausA->W <br />
2-W->W 3-W->W 2-W->W 3-W->W<br />
_Z=1 ? N >--. _Z=1 ? N >--. _Z=1 ? N >--. _Z=1 ? N >--.<br />
J | J | J | J |<br />
V | V | V | V |<br />
setze _Finc | setze _Fdec | setze _Fdec | setze _Finc |<br />
V<-----´ V<-----´ V<-----´ V<-----´<br />
return return return return<br />
<br />
und Quellcode:<br />
<br />
Mouse movf PORTB,0<br />
andlw 3<br />
movwf MausB<br />
movwf MausC<br />
call Delay ; ca. 10 ms<br />
movf PORTB,0<br />
andlw 3<br />
movwf MausA<br />
xorwf MausC,1<br />
btfsc _Z<br />
return<br />
movf MausB,1<br />
btfsc _Z<br />
goto Mouse0<br />
movf MausB,0<br />
sublw 1<br />
btfsc _Z<br />
goto Mouse1<br />
movf MausB,0<br />
sublw 2<br />
btfsc _Z<br />
goto Mouse2<br />
movf MausB,0<br />
sublw 3<br />
btfsc _Z<br />
goto Mouse3<br />
return<br />
Mouse0 movf MausA,0<br />
sublw 1<br />
btfsc _Z<br />
bsf _Fdec<br />
movf MausA,0<br />
sublw 2<br />
btfsc _Z<br />
bsf _Finc<br />
return<br />
Mouse1 movf MausA,1<br />
btfsc _Z<br />
bsf _Finc<br />
movf MausA,0<br />
sublw 3<br />
btfsc _Z<br />
bsf _Fdec<br />
return<br />
Mouse2 movf MausA,1<br />
btfsc _Z<br />
bsf _Fdec<br />
movf MausA,0<br />
sublw 3<br />
btfsc _Z<br />
bsf _Finc<br />
return<br />
Mouse3 movf MausA,1<br />
sublw 1<br />
btfsc _Z<br />
bsf _Finc<br />
movf MausA,0<br />
sublw 2<br />
btfsc _Z<br />
bsf _Fdec<br />
return<br />
<br />
=== LCD Displays ===<br />
<br />
==== Matrix ====<br />
<br />
Ein Programm zum Ansteuern von Matrixdisplays im 4-bit Modus. Das Display ist an 6 Pins eines Ports (hier: B) angeschlossen. In diesem Beispielprogramm wurde 2x16 Zeichen Display verwendet, das Programm kann aber für andere Displays modifiziert werden. Für andere Taktfrequenzen muss lediglich nur die Verzögerung vom "Del" geändert werden. Zum Beispiel für 20 MHz Quarz, anstatt 0x10 auf 0x50. Die im "Main" ans Display geschickte Zeichen können beliebig geändert werden.<br />
<br />
Das Display wird wie folgt an den PIC16F84A angeschlossen:<br />
<br />
VCC<br />
+<br />
| .-------.<br />
+----|2 |<br />
+--|1,3,5 |<br />
| | |<br />
=== | |<br />
.-------. GND | |<br />
| B4 10|---------|4 RS |<br />
| B5 11|---------|6 E |<br />
| | | |<br />
| | |7 DB0 |<br />
| | |8 DB1 |<br />
| | |9 DB2 |<br />
| | |10 DB3 |<br />
| B0 6|---------|11 DB4 |<br />
| B1 7|---------|12 DB5 |<br />
| B2 8|---------|13 DB6 |<br />
| B3 9|---------|14 DB7 |<br />
'-------' '-------'<br />
PIC16F84A DISPLAY<br />
<br />
; Display Test im 4-bit Modus<br />
LIST P=16F84a<br />
include "P16F84a.inc" ; 4.000 MHz<br />
__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC<br />
; DB4 PORTB,0 ; Display Data-Bits<br />
; DB5 PORTB,1<br />
; DB6 PORTB,2<br />
; DB7 PORTB,3<br />
#define _RS PORTB,4 ; Display RS<br />
#define _E PORTB,5 ; Display Enable<br />
#define _Frs Flags,0 ; RS Flag<br />
Tmp equ 0x20 <br />
Flags equ 0x21<br />
org 0x0000<br />
call Init<br />
Main call Fst<br />
movlw " "<br />
call Char<br />
movlw " "<br />
call Char<br />
movlw "D"<br />
call Char<br />
movlw "i"<br />
call Char<br />
movlw "s"<br />
call Char<br />
movlw "p"<br />
call Char<br />
movlw "l"<br />
call Char<br />
movlw "a"<br />
call Char<br />
movlw "y"<br />
call Char<br />
movlw " "<br />
call Char<br />
movlw "T"<br />
call Char<br />
movlw "e"<br />
call Char<br />
movlw "s"<br />
call Char<br />
movlw "t"<br />
call Char<br />
call Snd<br />
movlw " "<br />
call Char<br />
movlw "i"<br />
call Char<br />
movlw "m"<br />
call Char<br />
movlw " "<br />
call Char<br />
movlw "4"<br />
call Char<br />
movlw "-"<br />
call Char<br />
movlw "b"<br />
call Char<br />
movlw "i"<br />
call Char<br />
movlw "t"<br />
call Char<br />
movlw " "<br />
call Char<br />
movlw "M"<br />
call Char<br />
movlw "o"<br />
call Char<br />
movlw "d"<br />
call Char<br />
movlw "u"<br />
call Char<br />
movlw "s"<br />
call Char<br />
sleep<br />
Fst movlw 0x80 ; Anfangsadresse der ersten Zeile<br />
goto Cmd <br />
Snd movlw 0xC0 ; Anfangsadresse der zweiten Zeile<br />
Cmd movwf Tmp ; Befehl ins Tmp laden<br />
bcf _Frs ; RS=0<br />
goto Send<br />
Char movwf Tmp ; Zeichen ins Tmp laden<br />
bsf _Frs ; RS=1<br />
Send swapf Tmp,0 ; zuerst High Nibble (ab jetzt Low Nibble)<br />
andlw 0x0F ; (aktuelles Low Nibble ausblenden)<br />
movwf PORTB ; an Port (Display) schicken<br />
btfsc _Frs ; RS Flag ans Port kopieren<br />
bsf _RS<br />
bsf _E ; Enable erzeugen<br />
bcf _E<br />
movf Tmp,0 ; Low Nibble<br />
andlw 0x0F ; (High Nibble ausblenden) <br />
movwf PORTB ; an Port (Display) schicken<br />
Enab btfsc _Frs ; RS Flag ans Port kopieren<br />
bsf _RS<br />
bsf _E ; Enable erzeugen<br />
bcf _E<br />
Del movlw 0x10 ; Verzögerung ca. 50µs<br />
movwf Tmp<br />
decfsz Tmp,1<br />
goto $-1<br />
return<br />
Init clrf PORTB ; PortB initialisieren<br />
bsf STATUS,RP0 ; Bank 1 <br />
clrf TRISB ; alle Pins als Ausgänge<br />
bcf STATUS,RP0 ; Bank 0<br />
bcf _Frs<br />
movlw 2 ; Display auf 4-bit umschalten und initialisieren<br />
movwf PORTB<br />
call Enab<br />
movlw 0x28 ; 4 bit, 2 Zeilen, 5x7 Punkten<br />
call Cmd<br />
movlw 0x0C ; display an, cursor aus, nicht blinken<br />
call Cmd<br />
movlw 6 ; incrementieren, nicht schieben<br />
goto Cmd<br />
end<br />
<br />
==== Grafik ====<br />
<br />
2-pin Schnittstelle und ein Testprogramm für Grafikdisplay HYUNDAI HP12542R_DYO [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=20277]]<br />
<br />
==== Handy ====<br />
<br />
Als Beispiel die Hardware und ein Testprogramm für Nokia 3310/3330 Display: [[http://www.roboternetz.de/phpBB2/viewtopic.php?p=124669#124669]]<br />
<br />
==== 7-Segment mit 3 Backplanes ohne Kontroller ====<br />
<br />
Eine Schnittstelle und ein Testprogramm für LPH2673-1 von Pollin:<br />
[[http://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=26005&highlight=lph26731]]<br />
<br />
= High-End (PIC18...)=<br />
<br />
Als High-End werden alle 8-bit PIC18F... klasiffieziert, die Befehlslänge 16-Bit, also 2 Bytes haben. Ihr Befehlsatz enthält insgesamt 75 Befehle.<br />
<br />
Es werden nur nötige fürs Erstellen von ASM Programmen spezifische Unterschiede behandelt, die in der Praxis Probleme für Umsteiger von Basic-Line und Mid-Range bereiten können. Wenn sich jemand ausschliesslich mit PIC18F... beschäftigen will, wird sich keine Unterschiede merken müssen, da ausser erweitertem Befehlsatz die Programme von ihrer Struktur identisch sind. Genaue Parameter für bestimmten PIC befinden sich im entsprechendem Datenblatt.<br />
<br />
== Hardware ==<br />
<br />
Weil die PIC18F... für einige Anwendungen schneller als Mid-Range laufen müssen, wurde bei Takterzeugung eine PLL-Option beim Oszillator zugefügt, die aus standard Quarzen (z.B. 12 MHz) durch Multiplikation mal 4 eine Taktfrequenz für CPU 12 MHz (z.B. für USB) erzeugt. Weil die Frequenz des Oszilators für interne Taktung der CPU durch 4 geteilt ist, ermöglichst diese Option, dass die CPU mit Oscillatorfrequenz, also bei z.B. 10 MHz Quarz mit echten 10 MHz (10 MIPs) arbeitet (intern mit 40 MHz).<br />
<br />
Der Programmspeicher ist als 8-Bit breit organisiert. Aus dem Grund jeder 16 Bit langer Befehl belegt im Speicher 2 Bytes. Wenn im Datenblatt z.B. 16384 Bytes angegeben sind, bedeutet das, dass dort sich nur die Hälfte davon, also 8192 Befehle abspeichern lassen. Als Folge sind für Befehle nur gerade Adressen zulässig.<br />
<br />
== Software ==<br />
<br />
Alle Befehle sind um 2 Bit länger, als bei Mid-Range, und es gibt keine Speicherbänke, was Erstellung von Programmen sehr vereinfacht.<br />
<br />
Bisher für Mid-Range ausführlich beschriebene Befehle, ausser "CLRW", "RLF" und "RRF" und Speicherbankumschaltungen (z.B. "BSF RP0" und "BCF RP0") sind für PIC18F... "verständlich" und werden ausgeführt. Die PIC18F... haben zusätzlich 43 Befehle.<br />
<br />
Wenn es um ASM Programm geht, ist er grundsätzlich, ausser zusätzlichen Befehlen, identisch wie bei Mid-Range. Die zusätzliche Befehle ermöglichen Erstellen von mehr kompakten Programmen.<br />
<br />
Die PIC18... haben die Möglichkeit für Interuppts individuell Prioritäten definieren, was die Bearbeitung in ISR vereinfacht. Aus dem Grund besitzen sie zwei Interrupt-Vektoren (Anfangsadressen für ISR): 8h für höhere und 18h für niedrigere Prioritäten, die anders als bei übrigen PIC's sind (4h).<br />
<br />
Ausser erweiterten Befehlsatz haben die PIC18F... drei Register für indirekte Adressierung FSR0, FSR1 und FSR2, die unabhängig voneinender und gleichzeitig benutzt werden können.<br />
<br />
Die CPU's von PIC18F... haben tieferen Stapel für Rücksprungadressen mit 31 Ebenen, der zusätzlich mit "PUSH" und "POP" Befehlen während Ausführung eines Unterprogramms (UP) modifiziert werden kann. Das ermöglichst Änderungen von Rücksprungadressen, so dass nicht unbedingt nach der Ausführung des UP zurück, sondern fast beliebig gesprungen werden kann. Ausführlich ist es in AN818 vom Microchip beschrieben. Siehe dazu: http://ww1.microchip.com/downloads/en/AppNotes/00818a.pdf<br />
<br />
Sehr nutzlich sind auch alle neue Sprungmöglichkeiten z.B. "BRA" der "GOTO" entspricht. Da die bedingte Sprünge von Bits des "STATUS" Register abhängen, muß davor kein Befehl aüsgeführt werden der benötigten Bit (z.B. "Z") prüft.<br />
<br />
Wegen Struktur des Programspeichers ein relativer Sprung zur nächster Adresse muss um 2 Byte erfolgen. Deswegen ist für PIC18F... also "GOTO $+2" der kürzeste mögliche Sprung. Bei "GOTO $+1" springt die CPU "zwischen" gültige Befehle ins "Nirvana", was Absturz des Programms bedeutet. Bei bedingten Sprungen und "BRA" wird der angebebener Wert "n" für die Anzahl den übersprüngten Zeilen automatisch durch 2 multipliziert. <br />
<br />
Alle PIC18F... können den Programspeicher beschreiben und sich selbst programmieren. Dafür gibt es die "TBLWT.." Befehle. Das ist aber nur für mindestens 8 Bytes am Stück möglich. Vor dem Beschreiben müssen die dafür vorgesehene Speicherplätze zuerst gelöscht werden, wobei das für minimum 64 Bytes möglich ist.<br />
<br />
Als Beispiel ein geprüftes Fragment von Bearbeitung des Programmspeichers (Flash) in http://www.rn-wissen.de/index.php/PIC_ASM_Beispiele.<br />
<br />
== Kurzübersicht zusätzliche Befehle ==<br />
<font style="font-size:10px;"><br />
{| <br />
|-<br />
| valign=top |<br />
<br />
{| {{Blauetabelle}}<br />
|ADDWFC||Add WREG and Carry bit to f <br />
|-<br />
|BC||Branch if Carry<br />
|-<br />
|BN||Branch if Negative<br />
|-<br />
|BNC||Branch if Not Carry<br />
|-<br />
|BNN||Branch if Not Negative<br />
|-<br />
|BNOV||Branch if Not Overflow<br />
|-<br />
|BNZ||Branch if Not Zero<br />
|-<br />
|BOV||Branch if Overflow<br />
|-<br />
|BRA||Branch unconditionally<br />
|-<br />
|BZ||Branch if Zero<br />
|-<br />
|BTG||Bit toggle in f<br />
|-<br />
|CPFSEQ||Compare f with W, skip if f=W <br />
|-<br />
|CPFSGT||Compare f with W, skip if f>W<br />
|-<br />
|CPFSLT||Compare f with W, skip if f<W<br />
|}<br />
<br />
| valign=top |<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
|DAW||Decimal Adjust W<br />
|-<br />
|DCFSNZ||Decrement f, skip if not 0<br />
|-<br />
|INFSNZ||Increment f, skip if not 0<br />
|-<br />
|LFSR||Move literal to FSRx<br />
|-<br />
|MOVFF||Move f1 to f2<br />
|-<br />
|MOVLB||Move literal to BSR < 3 : 0 ><br />
|-<br />
|MULLW||Multiply literal with W<br />
|-<br />
|MULWF||Multiply W with f<br />
|-<br />
|NEGF||Negate f<br />
|-<br />
|POP||Pop top of return stack (TOS)<br />
|-<br />
|PUSH||Push top of return stack (TOS)<br />
|-<br />
|RCALL||Relative call<br />
|-<br />
|RESET||Software device RESET<br />
|-<br />
|RLCF||Rotate left f through Carry<br />
|-<br />
|RLNCF||Rotate left f no Carry<br />
|}<br />
<br />
| valign=top |<br />
<br />
{| {{Blauetabelle}}<br />
<br />
|-<br />
|RRCF||Rotate right f trough Carry<br />
|-<br />
|RRNCF||Rotate right f no Carry<br />
|-<br />
|SETF||Set f<br />
|-<br />
|SUBFWB||Subtract f from W with borrow<br />
|-<br />
|SUBWFB||Subtract W from f with borrow<br />
|-<br />
|TBLRD*||Table Read<br />
|-<br />
|TBLRD*+||Table Read with post-increment<br />
|-<br />
|TBLRD*-||Table Read with post-decrement<br />
|-<br />
|TBLRD+*||Table Read with pre-increment<br />
|-<br />
|TBLWT*||Table write<br />
|-<br />
|TBLWT*+||Table write with post-increment<br />
|-<br />
|TBLWT*-||Table write with post-decrement<br />
|-<br />
|TBLWT+*||Table write with pre-increment<br />
|-<br />
|TSTFSZ||Test f, skip if 0<br />
|}<br />
<br />
|}<br />
</font><br />
<br />
== Ausführliche Beschreibung zu den Befehlen ==<br />
<br />
Alle Sprunge ausser "BRA" können nur im Bereich eines Bytes, d.h. von -128 bis +127 erfolgen. Nur der Sprung "BRA" kann zwischen -1024 bis +1023 lang sein.<br />
<br />
Wenn die Bedingung für ein bedingten Sprung nicht erfüllt ist, wird er in einem Takt übersprungen und nächster Befehl ausgeführt.<br />
<br />
Erklärungen zu den Verwendeten Platzhaltern:<br />
*'''k''' stellt einen fest definierten Wert da. z.B. hexadezimal <tt>0x20</tt> bzw. <tt>20</tt>, dezimal <tt>d'42'</tt> bzw. <tt>.42</tt> oder binär <tt>b'00101010'</tt><br />
*'''n''' stellt einen fest definierten Wert wie oben für Sprünge<br />
*'''W''' steht für das W-Register.<br />
*'''d''' steht für ''destination'' (Ziel). Im code wird d durch ein <tt>w</tt> bzw. <tt>0</tt> (der Wert wird in das W-Register gespeichert ) oder <tt>f</tt> bzw. <tt>1</tt> (der Wert wird in das davor definierte Register gespeichert)<br />
*'''b''' steht für Bitnummer im Register (eine Zahl zwischen 0 und 7)<br />
*'''R''' steht für ein Register<br />
*'''fett''' geschrieben Bedeutet, dass es ein Platzhalter ist und im Quellcode durch eine Registeradresse oder einen Wert ersetzt werden muss<br />
*<tt>Schreibmaschinenstil</tt> bedeutet, dass es so im Quellcode geschrieben werden kann.<br />
<br />
<b>ADDWFC R,d</b> <i style="color:grey;"><b>ADD W</b> and <b>F</b> with <b>C</b>arry - Addiere W und f mit Übertrag </i><hr><br />
:Es wird die Rechenoperation <math>W+R</math> mit Berücksichtigung des schon vorhandenen Übertrags ausgeführt und das Ergebniss entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu [[#Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register|Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register]]<br />
<br />
<b>BC n</b> <i style="color:grey;"><b>B</b>ranch if <b>C</b>arry - Springe wenn Übertrag </i><hr> <br />
:Es wird das "C" Bit im "STATUS"-Register überprüft und beim gesetzten gesprungen.<br />
<br />
<b>BN n</b> <i style="color:grey;"><b>B</b>ranch if <b>N</b>egative - Springe wenn negative </i><hr> <br />
:Es wird das "N" Bit im "STATUS"-Register überprüft und beim gesetzten gesprungen.<br />
<br />
<b>BNC n</b> <i style="color:grey;"><b>B</b>ranch if <b>N</b>ot <b>C</b>arry - Springe wenn kein Übertrag </i><hr> <br />
:Es wird das "C" Bit im "STATUS"-Register überprüft und beim gelöschten gesprungen.<br />
<br />
<b>BNN n</b> <i style="color:grey;"><b>B</b>ranch if <b>N</b>ot <b>N</b>egative - Springe wenn nicht negative </i><hr> <br />
:Es wird das "N" Bit im "STATUS"-Register überprüft und beim gelöschten gesprungen.<br />
<br />
<b>BNOV n</b> <i style="color:grey;"><b>B</b>ranch if <b>N</b>ot <b>OV</b>erflow - Springe wenn kein Überlauf </i><hr> <br />
:Es wird das "OV" Bit im "STATUS"-Register überprüft und beim gelöschten gesprungen.<br />
<br />
<b>BNZ n</b> <i style="color:grey;"><b>B</b>ranch if <b>N</b>ot <b>Z</b>ero - Springe wenn ungleich Null </i><hr> <br />
:Es wird das "Z" Bit im "STATUS"-Register überprüft und beim gelöschten gesprungen.<br />
<br />
<b>BOV n</b> <i style="color:grey;"><b>B</b>ranch if <b>OV</b>erflow - Springe wenn Überlauf </i><hr> <br />
:Es wird das "OV" Bit im "STATUS"-Register überprüft und beim gesetzten gesprungen.<br />
<br />
<b>BRA n</b> <i style="color:grey;"><b>BR</b>anch <b>A</b>bsolutly - Springe unbedingt </i><hr> <br />
:Es wird immer unbedingt gesprungen.<br />
<br />
<b>BZ n</b> <i style="color:grey;"><b>B</b>ranch if <b>Z</b>ero - Springe bei Null </i><hr> <br />
:Es wird das "Z" Bit im "STATUS"-Register überprüft und beim gesetzten gesprungen.<br />
<br />
<b>BTG R,b</b> <i style="color:grey;"><b>B</b>it <b>T</b>o<b>G</b>gle F - Kehre bestimmten Bitwert in f um </i><hr> <br />
:Der bestimmte Bit im F-Register wird umgekehrt. Also wenn er vorm Ausführen des Befehls z.B. gleich 1 war, wird er danach gleich 0 sein und umgekehrt.<br />
<br />
<b>CPFSEQ R,d</b> <i style="color:grey;"><b>C</b>om<b>P</b>are <b>F</b> with W, <b>S</b>kip if <b>EQ</b>ual - Vergleiche Register f mit W und überspringe, wenn f=W</i><hr><br />
:Es wird der Registers f mit dem W verglichen und bei f=W, wird der nächste Befehl übersprungen. Der Zusatz SEQ steht für ''skip if equal'', d.h. wenn die Werte in beiden Register gleich sind, wird der nächste Befehl übersprungen.<br />
<br />
<b>CPFSGT R,d</b> <i style="color:grey;"><b>C</b>om<b>P</b>are <b>F</b> with W, <b>S</b>kip if <b>G</b>rea<b>T</b>er - Vergleiche Register f mit W und überspringe, wenn f>W</i><hr><br />
:Es wird der Registers f mit dem W verglichen und bei f>W der nächste Befehl übersprungen. Der Zusatz SGT steht für ''skip if greater'', d.h. wenn der Wert im f grösser als im W-Register ist, wird der nächste Befehl übersprungen.<br />
<br />
<b>CPFSLT R,d</b> <i style="color:grey;"><b>C</b>om<b>P</b>are <b>F</b> with W, <b>S</b>kip if <b>L</b>i<b>T</b>tler - Vergleiche Register f mit W und überspringe, wenn f<W</i><hr><br />
:Es wird der Registers f mit dem W verglichen und bei f<W der nächste Befehl übersprungen. Der Zusatz SLT steht für ''skip if littler (lower)'', d.h. wenn der Wert im f kleiner als im W-Register ist, wird der nächste Befehl übersprungen.<br />
<br />
<b>DAW</b> <i style="color:grey;"><b>D</b>ecimal <b>A</b>djust <b>W</b>REG - Justiere W-Register nach dezimaler Addition </i><hr><br />
:Es weden alle nötige Korrekturen für richtigen Ergebnis im W-Register nach dezimaler Addition durchgeführt.<br />
<br />
<b>DCFSNZ R,d</b> <i style="color:grey;"><b>D</b>e<b>C</b>rement <b>F</b>, <b>S</b>kip if <b>N</b>ot <b>Z</b>ero - Subtrahiere 1 vom Regiser f und überspringe den nächsten Befehl, wenn f ungleich Null ist.</i><hr><br />
:Vom Wert des Registers '''R''' wird 1 subtrahiert und das Ergebnis entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Weil es keine "echte" Substraktion ist, beeinflusst dieser Befehl das C-Flag im STATUS-Register nicht.<br />
<br />
<b>INFSNZ R,d</b> <i style="color:grey;"><b>IN</b>crement <b>F</b>, <b>S</b>kip if <b>N</b>ot <b>Z</b>ero - Addiere 1 zum Register f und überspringe den nächsten Befehl, wenn f ungleich Null ist</i><hr><br />
:Zum Wert des Registers '''R''' wird 1 addiert und das Ergebniss entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). Der Zusatz SNZ steht für ''skip if not zero'', d.h. wenn das Ergebnis der Rechnung ungleich Null ist, wird der nächste Befehl übersprungen. Weil es keine "echte" Addition ist, beeinflusst dieser Befehl das C-Flag im STATUS-Register nicht. <br />
<br />
<b>LFSR R,k</b> <i style="color:grey;"><b>L</b>oad <b>FSR</b> - Lade FSR-Register mit einem Wert k </i><hr><br />
:Es wird gewähles FSR-Register (FSR0, FSR1, bzw. FSR2) mit dem Wert k geladen.<br />
<br />
<b>MOVLB k</b> <i style="color:grey;"><b>MOV</b> <b>L</b>iteral to <b>B</b>SR <3:0> - Bewege k ins BSR-Register </i><hr> <br />
:Es wird der Bank Select Register (BSR) mit dem Wert k geladen.<br />
<br />
<b>MOVFF R1,R2</b> <i style="color:grey;"><b>MOV</b>e <b>F</b>1 to <b>F</b>2 - Bewege f1 in f2</i><hr><br />
:Das Register R1 wird in das Register R2 kopiert.<br />
<br />
<b>MULLW k</b> <i style="color:grey;"><b>MUL</b>tiply <b>L</b>iteral with <b>W</b>REG - Multipliziere k mit W-Register </i><hr><br />
:Es wird W-Register mit k multiplieziert und das Ergebnis in Registern PRODH und PRODL gespeichert. Dieser Befehl beeinflusst das STATUS-Register nicht.<br />
<br />
<b>MULWF R</b> <i style="color:grey;"><b>MUL</b>tiply <b>W</b> with <b>F</b> - Multipliziere W-Register mit F</i><hr><br />
:Es wird F-Register mit W-Register multiplieziert und das Ergebnis in F gespeichert. Dieser Befehl beeinflusst das STATUS-Register nicht.<br />
<br />
<b>NEGF R</b> <i style="color:grey;"><b>NEG</b>ate <b>F</b> - Negiere F</i><hr><br />
:Es werden alle Bits von F-Regiester negiert. Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu [[#Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register|Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register]]<br />
<br />
<b>POP</b> <i style="color:grey;"><b>POP</b> top of return stack (TOS) - Nehme die oberste Rücksprungsadresse vom Stapel weg</i><hr><br />
:Es wird die oberste Rücksprungadresse vom Stapel entfernt und alle übrigen Adressen um eine Stelle nach oben geschoben.<br />
<dl><dd><!-- zum einrücken da--><br />
alle <--- Stapel --->|<br />
übrige A Adresse 1 -----> entfernt<br />
Adressen | Adresse 2<br />
um 1 Stelle | Adresse 3<br />
nach oben | ..........<br />
verschieben | Adresse 31<br />
</dd></dl><!-- zum einrücken da--><br />
:Zur Verdeutlichung:<br />
<dl><dd><!-- zum einrücken da--><br />
<br />
<--- Stapel --->| ;vor dem "POP"<br />
Adresse 1 ---> verworfen<br />
Adresse 2<br />
Adresse 3<br />
..........<br />
Adresse 31<br />
<br />
<--- Stapel --->| ;nach dem "POP"<br />
Adresse 2<br />
Adresse 3<br />
Adresse 4<br />
..........<br />
?????????<br />
<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
<b>PUSH</b> <i style="color:grey;"><b>PUSH</b> top of return stack (TOS) - Lege neue oberste Rücksprungsadresse am Stapel </i><hr><br />
:Es wird eine neue oberste Rücksprungadresse am Stapel abgelegt und alle übrigen Adressen um eine Stelle nach unten geschoben.<br />
<dl><dd><!-- zum einrücken da--><br />
alle <--- Stapel --->|<br />
übrige | Adresse 1 <--- neue wird abgelegt<br />
Adressen | Adresse 2<br />
um 1 Stelle | Adresse 3<br />
nach unten | ..........<br />
verschieben V Adresse 31<br />
</dd></dl><!-- zum einrücken da--><br />
:Zur Verdeutlichung:<br />
<dl><dd><!-- zum einrücken da--><br />
<br />
<--- Stapel --->| ;vor dem "POP"<br />
Adresse 1<br />
Adresse 2<br />
Adresse 3<br />
..........<br />
Adresse 31<br />
<br />
<--- Stapel --->| ;nach dem "POP"<br />
PC + 2<br />
Adresse 1<br />
Adresse 2<br />
..........<br />
Adresse 30<br />
<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
<b>RCALL n</b> <i style="color:grey;"><b>R</b>elative <b>CALL</b> - Relativer Aufruf eines Unterprogramms </i><hr><br />
:Es wird ein Unterprogramm reletiv aufgerufen, der innerhalb 1 kB von aktueller Adresse liegen darf. Vergleiche mit "CALL".<br />
<br />
<b>RESET</b> <i style="color:grey;"> < Software device <b>RESET</b> - Software Reset </i><hr><br />
:Es wird Reset des PIC's durchgeführt, genauso wie hardwaremässig mit /MCLR-Pin.<br />
<br />
<b>RLCF R,d</b> <i style="color:grey;"><b>R</b>otate <b>L</b>eft through <b>C</b>arry <b>F</b> - Rotiere das Register f mithilfe des Carry-bits nach links</i><hr><br />
:Alle Bits im Register '''R''' werden um eine Position nach links verschoben. Dabei wird das Carry bit (<tt>STATUS,C</tt>) in das Bit 0 des Registers R geschoben. Bit 7 aus dem Register '''R''' wird in das Carry bit "geschoben". Das Ergebnis wird entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). <br />
<dl><dd><!-- zum einrücken da--><br />
|<--- Register R --->|<br />
|c|<-7<-6<-5<-4<-3<-2<-1<-0<br />
V A<br />
|________________________|<br />
</dd></dl><!-- zum einrücken da--><br />
:Zur Verdeutlichung:<br />
<dl><dd><!-- zum einrücken da--><br />
|C| |-Register R-| ;C steht für das Carry-bit, STATUS,C ; 0-7 stehen für Bitnummer im Register.<br />
c 7 6 5 4 3 2 1 0 ;vor der Rotation<br />
7 6 5 4 3 2 1 0 c ;nach der Rotation<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
<b>RLNCF R,d</b> <i style="color:grey;"><b>R</b>otate <b>L</b>eft <b>N</b>o <b>C</b>arry <b>F</b> - Rotiere das Register f ohne des Carry-bits nach links</i><hr><br />
:Alle Bits im Register '''R''' werden um eine Position nach links verschoben. Das Ergebnis wird entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). <br />
<dl><dd><!-- zum einrücken da--><br />
|<--- Register R --->|<br />
7<-6<-5<-4<-3<-2<-1<-0<br />
V A<br />
|____________________|<br />
</dd></dl><!-- zum einrücken da--><br />
:Zur Verdeutlichung:<br />
<dl><dd><!-- zum einrücken da--><br />
|-Register R-| ;0-7 stehen für Bitnummer im Register.<br />
7 6 5 4 3 2 1 0 ;vor der Rotation<br />
6 5 4 3 2 1 0 7 ;nach der Rotation<br />
</dd></dl><!-- zum einrücken da--><br />
<br />
<b>RRCF R,d</b> <i style="color:grey;"><b>R</b>otate <b>R</b>ight through <b>C</b>arry <b>F</b> - Rotiere das Register f mithilfe des Carry-bits nach rechts</i><hr><br />
:Alle Bits im Register '''R''' werden um eine Position nach rechts verschoben. Dabei wird das Carry bit (<tt>STATUS,C</tt>) in das 7.Bit des Registers R geschoben. Bit 0 aus dem Register '''R''' wird in das Carry bit "geschoben". Das Ergebnis wird entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). <br />
<dl><dd><!-- zum einrücken da--><br />
|<--- Register R --->|<br />
|c|->7->6->5->4->3->2->1->0<br />
A V<br />
|________________________|<br />
</dd></dl><!-- zum einrücken da--><br />
:Zur Verdeutlichung:<br />
<dl><dd><!-- zum einrücken da--><br />
|C| |-Register R-| ;C steht für das Carry-bit, STATUS,C ; 0-7 stehen für Bitnummer im Register.<br />
C 7 6 5 4 3 2 1 0 ;vor der Rotation<br />
0 C 7 6 5 4 3 2 1 ;nach der Rotation <br />
</dd></dl><!-- zum einrücken da--><br />
<br />
<b>RRNCF R,d</b> <i style="color:grey;"><b>R</b>otate <b>R</b>ight <b>N</b>o <b>C</b>arry <b>F</b> - Rotiere das Register f ohne des Carry-bits nach rechts</i><hr><br />
:Alle Bits im Register '''R''' werden um eine Position nach rechts verschoben. Das Ergebnis wird entweder in das W-Register ('''d'''=<tt>W</tt>=<tt>0</tt>) oder in R gespeichert ('''d'''=<tt>F</tt>=<tt>1</tt>). <br />
<dl><dd><!-- zum einrücken da--><br />
|<--- Register R --->|<br />
7->6->5->4->3->2->1->0<br />
A V<br />
|____________________|<br />
</dd></dl><!-- zum einrücken da--><br />
:Zur Verdeutlichung:<br />
<dl><dd><!-- zum einrücken da--><br />
|-Register R-| ; 0-7 stehen für Bitnummer im Register.<br />
7 6 5 4 3 2 1 0 ;vor der Rotation<br />
0 7 6 5 4 3 2 1 ;nach der Rotation <br />
</dd></dl><!-- zum einrücken da--><br />
<br />
<b>SETF R</b> <i style="color:grey;"><b>SETF</b> - Setze das Register f</i><hr><br />
:Alle Bits im Register ''R'' werden gesetzt, also nach dem Ausführen des Befehls wird im Register "FFh" stehen.<br />
<br />
<b>SUBFWB R,d</b> <i style="color:grey;"><b>SUB</b>stract <b>F</b> from <b>W</b> with <b>B</b>orrow - Substrahiere das F-Register von W-Register mit Übertrag</i><hr><br />
:Der inhalt des W-Registers wird vom F-Register mit Berücksichtigung des vorhandenes Übertrags abgezogen. Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu [[#Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register|Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register]]<br />
<br />
<b>SUBWFB R,d</b> <i style="color:grey;"><b>SUB</b>stract <b>W</b> from <b>F</b> with <b>B</b>orrow - Substrahiere das W-Register von F-Register mit Übertrag</i><hr><br />
:Der inhalt des F-Registers wird vom W-Register mit Berücksichtigung des vorhandenes Übertrags abgezogen. Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu [[#Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register|Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register]]<br />
<br />
<b>TBLRD*</b> <i style="color:grey;"><b>T</b>a<b>BL</b>e <b>R</b>ea<b>D*</b> - Lese Tabelle ohne Änderung des Zeigers </i><hr><br />
:Dieser Befehl wird fürs Lesen des Programmspeichers (Flash) angewendet. Nach der Ausführung bleibt der Zeiger unverändert.<br />
<br />
<b>TBLRD*+</b> <i style="color:grey;"><b>T</b>a<b>BL</b>e <b>R</b>ea<b>D*+</b> with post-increment - Lese Labelle mit Nacherhöhung des Zeigers </i><hr><br />
:Dieser Befehl wird fürs Lesen des Programmspeichers (Flash) angewendet. Nach der Ausführung wird der Zeiger um 1 erhöht.<br />
<br />
<b>TBLRD*-</b> <i style="color:grey;"><b>T</b>a<b>BL</b>e <b>R</b>ea<b>D*-</b> with post-decrement - Lese Tabelle mit Nacherniedrigung des Zeigers </i><hr><br />
:Dieser Befehl wird fürs Lesen des Programmspeichers (Flash) angewendet. Nach der Ausführung wird der Zeiger um 1 erniedrigt.<br />
<br />
<b>TBLRD+*</b> <i style="color:grey;"><b>T</b>a<b>BL</b>e <b>R</b>ea<b>D+*</b> with pre-increment - Lese Tabelle mit Vorerhöhung des Zeigers </i><hr><br />
:Dieser Befehl wird fürs Lesen des Programmspeichers (Flash) angewendet. Vor der Ausführung wird der Zeiger um 1 erhöht.<br />
<br />
<b>TBLWT*</b> <i style="color:grey;"><b>T</b>a<b>BL</b>e <b>W</b>ri<b>T*</b>e - Schreibe Tabelle ohne Änderung des Zeigers </i><hr><br />
:Dieser Befehl wird fürs Beschreiben des Programmspeichers (Flash) angewendet. Nach der Ausführung bleibt der Zeiger unverändert.<br />
<br />
<b>TBLWT*+</b> <i style="color:grey;"><b>T</b>a<b>BL</b>e <b>W</b>ri<b>T*+</b>e with post-increment - Schreibe Tabelle mit Nacherhöhung des Zeigers </i><hr><br />
:Diesers Befehl wird fürs Beschreiben des Programmspeichers (Flash) angewendet. Nach der Ausführung wird der Zeiger um 1 erhöht.<br />
<br />
<b>TBLWT*-</b> <i style="color:grey;"><b>T</b>a<b>BL</b>e <b>W</b>ri<b>T*-</b>e with post-decrement - Schreibe Tabelle mit Nacherniedrigung des Zeigers </i><hr><br />
:Dieser Befehl wird fürs Beschreiben des Programmspeichers (Flash) angewendet. Nach der Ausführung wird der Zeiger um 1 erniedrigt.<br />
<br />
<b>TBLWT+*</b> <i style="color:grey;"><b>T</b>a<b>BL</b>e <b>W</b>ri<b>T+*</b>e with pre-increment - Schreibe Tabelle mit Vorerhöhung des Zeigers </i><hr><br />
:Dieser Befehl wird fürs Beschreiben des Programmspeichers (Flash) angewendet. Vor der Ausführung wird der Zeiger um 1 erhöht.<br />
<br />
<b>TSTFSZ R</b> <i style="color:grey;"><b>T</b>e<b>ST</b>t <b>F</b> <b>S</b>kip if <b>Z</b>ero - Prüfe f, überspringe wenn gleich Null ist </i><hr><br />
:Es wird der nächste Befehl öbersprungen, wenn der Register f gleich Null ist.<br />
<br />
== Umschreiben von Programmen ==<br />
<br />
Es werden alle nötige Änderungen beschrieben, die vorhandenes Programm lauffähig machen.<br />
Ausserdem muß für gewünschten PIC nötige Konfiguration im Quellcode ersetzt werden. Weil einige Befehle von PIC18F... müssen für Mid-Range in UPs umgeschrieben werden, die Auführung Zeit vom umgeschriebenen Program kann sich erheblich ändern. Bei zeitkritischen Abläufen ist deswegen Umschreibung High-End -> Mid-Range nicht immer möglich.<br />
<br />
=== Basic-Line & Mid-Range -> High-End ===<br />
<br />
Alle Speicherbankumschaltungen müssen mit ";" ausgeblendet bzw. gelöscht werden.<br />
<br />
Da die PIC18... drei "FSR" Register besitzen muss überall "FSR" konsequent anstatt "FSR" nach Wunsch "FSR0", "FSR1", bzw. "FSR2" eingeschrieben werden.<br />
<br />
Die für PIC18... unverständliche Befehle von Mid-Range müssen, wie folgt, ersetzt werden<br />
<br />
"CLRW" -> "MOVLW 0"<br />
<br />
"RLF" -> "RLCF"<br />
<br />
"RRF" -> "RRCF"<br />
<br />
Alle Relative Sprunge mussen verdoppelt werden. Beispielweise jeder "GOTO $+4" Befehl muss durch "GOTO $+8" bzw. "BRA $+8" ersetzt werden (Asführungszeit bei Schleifen beachten).<br />
<br />
Beim Umschreiben vorhandenen Programen von PIC12... und PIC16... ist für Interrupts ein kompatibilität Modus vorgesehen, die keine definierte Prioritäten für Interrupts benötigt, was folgendemassen in Initialisierung (Init) festgelegt wird:<br />
<br />
bcf RCON,IPEN ; disable priority levels on interrupts<br />
(compatibility modus)<br />
<br />
=== High-End -> Basic Line & Mid-Range ===<br />
<br />
Alle zusätzliche Befehle sind für Mid-Range PIC's unverständlich und müssen als kleine Unterprogramme erstellt werden. Für einige Befehle ist es enfach:<br />
<br />
"MOVFF R1, R2" -> "MOVF R1,0"<br />
"MOVWF R2"<br />
<br />
Es kann auch komplizierter werden, z.B. für "DAW":<br />
<br />
DecCor btfsc _DC ;dezimale Korrektur (ersetzt "DAW" von PIC18FXXX)<br />
bsf _Fdca<br />
btfsc _C<br />
bsf _Fcra<br />
movf INDF,0<br />
andlw 0x0F<br />
sublw 9<br />
btfss _C<br />
bsf _Fdca<br />
movf INDF,0<br />
andlw 0xF0<br />
sublw 90<br />
btfss _C<br />
bsf _Fcra<br />
return<br />
<br />
In dem UP sind "_Fcra" und "_Fdca" im Program definierte Flags. Siehe dazu [[#Hex Dec Wandlung|Hex Dec Wandlung]]<br />
<br />
= Hilfsmittel =<br />
<br />
Hier werden einige Programme presentiert, die das ASM Programmieren erleichtern. Sie sind nur auf PIC12... und PIC16... lauffähig und für PIC18... müssen umgeschrieben werden.<br />
<br />
Damit sie möglichst wenig Data- und Programmspeicher benötigen, sind alle Zahlen auf dem Display in der hex Darstellung.<br />
<br />
Für alle, die es in Dezimalsystem haben wollen, ist ein Taschenrechner mit hex<->dec Wandlung nötig.<br />
<br />
Hoffentlich hilfreiche Beiträge sind auch dort zu finden: http://www.roboternetz.de/community/threads/26098-Tips-Tricks .<br />
<br />
== PIC Miniterminal ==<br />
<br />
Das ist eine Schnittstelle, die nur 2 Leitungen zum Anschluss an PIC braucht. Sie ermöglicht das Steuern von diversen LCD Displays mit üblichem Anschluss ("RS", "Enable" und 8 Databits) und Abfragen von 3 Tasten.<br />
<br />
Sie wird mit einem 2x16 Zeichen Matrixdisplay und 3 Tasten für weiter beschriebene Hilfsprogramme verwendet.<br />
<br />
Hardware und Testprogramm: [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=13685]]<br />
<br />
== PIC RAM Monitor ==<br />
<br />
Er wurde entwickelt um 16 Register (0x20 bis 0x2F) im RAM eines PICs auf dem Display von "PIC Miniterminal", wie skizziert, beobachten zu können:<br />
<br />
Register Adressen (hex) 20 21 22 23 24 25 26 27<br />
.-----------------------.<br />
Zweistelligen Werte (hex) |XX XX XX XX XX XX XX XX|<br />
| |<br />
|XX XX XX XX XX XX XX XX|<br />
'-----------------------'<br />
28 29 2A 2B 2C 2D 2E 2F<br />
<br />
Es können natürlich auch z.B. PORTs beobachtet werden, wenn sie im HP in ausgewähle Register (0x20 bis 0x2F) kopiert werden. Beispielweise so:<br />
<br />
..............<br />
movf PORTA,0 ; PORTA ins W-Register laden<br />
movwf 0x20 ; und ins Register 0x20 kopieren<br />
movf PORTB,0 ; PORTB ins W-Register laden<br />
movwf 0x21 ; und ins Register 0x21 kopieren<br />
u.s.w.<br />
..............<br />
<br />
Um das Beobachten zu ermöglichen, wenn wegen Fehler das ASM Programm in einer endloser Schleife "hängt", wurde Interrupt vom Timer0 angewendet. Dieser Timer wurde gewählt, weil er in allen PICs vorhanden ist. Das Programm zeigt die Registerinhalte auf dem Display ca. 1 mal pro Sekunde, wenn der PIC mit einem 4 MHz Quarz oder internem Oszillator arbeitet.<br />
<br />
Der "PIC RAM Monitor" ist kein selbständiges Programm und wird so konzipiert, dass er in jedes ASM Programm, das den Timer0 nicht benutzt, eingebunden werden kann. Er braucht insgesamt 103 Speicherstellen im Programmspeicher, 10 Register im RAM (0x46 bis 0x4F) und 2 I/O Portpins des PICs für den er benutzt wird. Das beobachtete ASM Programm darf, wegen ISR vom "PIC RAM Monitor", erst ab der Adresse 0x0023 beginnen. Das UP "@" kann für PICs, die mehr Programmspeicher besitzen, mit entsprechender "org" Direktive höher verschoben werden. Entsprechend können auch alle im Programm benutzte Register höchstmögliche Adressen im RAM haben (z.B. @Tmp equ 0x7F anstatt 0x4F). <br />
<br />
Um "Kollisionen" mit dem Programm, in das er eingebunden wird, zu vermeiden, fangen alle seine Marken mit "@" an.<br />
<br />
In dem beobachteten Programm müssen lediglich zwei freie Portpins, an die PIC Miniterminal angeschlossen wird, als Ausgang definiert und initialisiert werden. Außerdem muss in die Initialisierung "goto @Init" eingefügt werden.<br />
<br />
Hier der Quellcode:<br />
<br />
; PIC RAM Monitor.asm<br />
@Tmp equ 0x4F<br />
@Tmp1 equ 0x4E<br />
@Tmp2 equ 0x4D<br />
@Tmp3 equ 0x4C<br />
@Tmp4 equ 0x4B<br />
@Int equ 0x4A<br />
@FSR equ 0x49<br />
@SSR equ 0x48<br />
@SWR equ 0x47<br />
@PORT equ 0x46<br />
ORG 0x0004 ; ISR<br />
bcf INTCON,GIE ; interrupts sperren<br />
movwf @SWR ; W-Register sichern<br />
swapf STATUS,0 ; STATUS Register<br />
movwf @SSR ; sichern<br />
movf FSR,0 ; FSR Register<br />
movwf @FSR ; sichern<br />
clrf @PORT ; Portpins Zustände sichern<br />
btfsc @DT<br />
bsf @PORT,0<br />
btfsc @CK<br />
bsf @PORT,1<br />
btfss INTCON,T0IF ; Timer0 interrupt Flag prüfen<br />
goto @IntTest ; wenn nicht gesetzt, eventuell andere prüfen<br />
bcf INTCON,T0IF ; Timer0 interrupt Flag löschen<br />
incf @Int,1 ; Zähler erhöhen<br />
btfss @Int,4 ; prüfen, ob schon 16d. Timer0 interrupt<br />
goto $+3 ; wenn nicht, Anzeigen der Registerinhalte überspringen<br />
clrf @Int ; Zähler löschen<br />
call @ ; Anzeigen der Registerinhalte aufrufen<br />
@IntTest ;**************;<br />
; eigener Code ;<br />
;**************;<br />
bcf @CK ; Portpins Zustände wiederherstellen<br />
btfsc @PORT,1<br />
bsf @CK<br />
bcf @DT<br />
btfsc @PORT,0<br />
bsf @DT<br />
movf @FSR,0 ; FSR Register<br />
movwf FSR ; wiederherstellen<br />
swapf @SSR,0 ; STATUS Register<br />
movwf STATUS ; wiederherstellen<br />
movf @SWR,0 ; W-Register wiederherstellen<br />
retfie ; zurück ins Programm springen, interrupts erlauben <br />
org 0x03B8 ; diese Adresse kann gleich max.Adresse - 47h sein<br />
@ movlw 0x20 ; Anzeigen der Registerinhalte<br />
movwf FSR <br />
call @1st<br />
call @Line<br />
call @2nd<br />
call @Line<br />
return<br />
@Line movlw 8<br />
movwf @Tmp<br />
movf INDF,0<br />
call @Val<br />
incf FSR,1<br />
decfsz @Tmp,1<br />
goto $-4<br />
return<br />
@1st movlw 0x80 ; Adresse der 1. Zeile an Display schicken<br />
goto @Cmd<br />
@2nd movlw 0xC0 ; Adresse der 2. Zeile an Display schicken<br />
@Cmd bcf STATUS,C ; Befehl an Display schicken<br />
goto @Send<br />
@Val movwf @Tmp1 ; Zahl (00-FF) an Display schicken<br />
swapf @Tmp1,0<br />
call @Num<br />
movf @Tmp1,0<br />
@Num andlw 0x0F ; Ziffer (0-F) an Display schicken<br />
movwf @Tmp2 <br />
movlw 0x0A<br />
subwf @Tmp2,0<br />
btfsc STATUS,C<br />
addlw 7<br />
addlw 0x3A<br />
bsf STATUS,C<br />
@Send movwf @Tmp2<br />
movlw 9 ; ein Byte + RS aus @Tmp2 (9 Bits) an Display schicken<br />
movwf @Tmp3<br />
@Ser bcf @CK<br />
bcf @DT<br />
btfsc @Tmp2,7<br />
bsf @DT<br />
bsf @CK<br />
rlf @Tmp2,1<br />
decfsz @Tmp3,1<br />
goto @Ser<br />
bcf @DT ; setze Display<br />
bsf @DT ; Enable<br />
bcf @CK ; lösche<br />
bcf @DT ; Display<br />
bsf @DT ; Enable<br />
bcf @DT<br />
return<br />
@Init bsf INTCON,GIE ; alle interrupts erlauben<br />
bsf INTCON,T0IE ; Timer0 interrupt erlauben<br />
bcf INTCON,T0IF ; Timer0 interrupt Flag löschen<br />
bsf STATUS,RP0 ; auf Bank 1 umschalten<br />
movf OPTION_REG,0 ; OPTION_REG in W-Register laden<br />
andlw 0xC0 ; Timer0 konfigurieren<br />
iorlw 7 ; Prescaler 256:1 <br />
movwf OPTION_REG ; ins OPTION_REG schreiben<br />
bcf STATUS,RP0 ; zurück auf Bank 0 umschalten<br />
movlw 0x38 ; Display vom PIC Miniterminal initialisieren<br />
call @Cmd<br />
movlw 0x0C<br />
call @Cmd<br />
movlw 6<br />
call @Cmd<br />
return<br />
end<br />
<br />
Und hier ein Beispiel für eine Anwendung mit dem Programm "Erstes.asm" für PIC16F84A:<br />
<br />
; Erstes.asm<br />
list P=16F84A ; Prozessor definieren<br />
include "P16F84A.inc" ; 4.000 MHz<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC<br />
#define @DT PORTB,7 ; * definiere Anschlüsse @DT und @CK<br />
#define @CK PORTB,6 ; * für "PIC RAM Monitor"<br />
#define _T1 PORTB,5 ; Portpins benennen<br />
#define _T2 PORTB,4<br />
#define _L1 PORTB,3<br />
#define _L2 PORTB,2<br />
#define _L3 PORTB,1<br />
#define _L4 PORTB,0<br />
P0 equ 0x20 ; Variablen definieren (Register benennen)<br />
P1 equ 0x21<br />
P2 equ 0x22<br />
org 0x0000 ; hier fängt das gesamte ASM Programm an <br />
call Init ; rufe UP Init (Initialisierung) auf<br />
goto Haupt <br />
org 0x0023 ; hier fängt das "Erstes" an<br />
Haupt btfsc _T1 ; prüfe, ob Taster T1 gedrückt ist, wenn ja,<br />
; überspringe "goto T2Test"<br />
goto T2Test ; wenn nicht, springe zu T2Test<br />
bsf _L1 ; schalte _L1 an (setze den Pin 2 auf "1")<br />
call Warten ; warte ca. 0,4 s (400 000 Prozessortakten)<br />
bcf _L1 ; schalte _L1 aus (setze den Pin2 auf "0")<br />
bsf _L2 ; schalte _L2 an (setze den Pin 5 auf "1")<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus (setze den Pin 5 auf "0")<br />
bsf _L3 ; schalte _L3 an (setze den Pin 6 auf "1")<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus (setze den Pin 6 auf "0")<br />
bsf _L4 ; schalte _L4 an (setze den Pin 7 auf "1")<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus (setze den Pin 7 auf "0")<br />
T2Test btfsc _T2 ; prüfe, ob Taster T2 gedrückt ist, wenn ja,<br />
; überspringe "goto Haupt"<br />
goto Haupt ; Wenn nicht, springe zu Haupt<br />
bsf _L4 ; schalte _L4 an (setze den Pin 7 auf "1")<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus (setze den Pin 7 auf "0")<br />
bsf _L3 ; schalte _L3 an (setze den Pin 6 auf "1")<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus (setze den Pin 6 auf "0")<br />
bsf _L2 ; schalte _L2 an (setze den Pin 5 auf "1")<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus (setze den Pin 5 auf "0")<br />
bsf _L1 ; schalte _L1 an (setze den Pin 2 auf "1")<br />
call Warten ; warte<br />
bcf _L1 ; schalte _L1 aus (setze den Pin2 auf "0")<br />
goto Haupt ; springe zu Haupt<br />
Warten movlw 2 ; schreibe "2" für ca. 0,4 s<br />
movwf P2 ; ins Register P2<br />
clrf P1 ; lösche Register P1 (schreibe "0" für 256 Durchläufe) <br />
clrf P0 ; lösche Register P0 (schreibe "0" für 256 Durchläufe)<br />
decfsz P0,1 ; dekrementiere P0, überspringe nächsten Befehl bei P0 = 0<br />
goto $-1 ; gehe zur vorherigen Adresse (Befehl "decfsz P0") <br />
decfsz P1,1 ; dekrementiere P1, überspringe nächsten Befehl bei P1 = 0 <br />
goto $-4 ; gehe zur aktueller Adresse - 4 (Befehl "clrf P0")<br />
decfsz P2,1 ; dekrementiere P2, überspringe nächsten Befehl bei P2 = 0<br />
goto $-7 ; gehe zur aktueller Adresse - 7 (Befehl "clrf P1")<br />
return ; springe zurück zum Aufrufer ("Haupt"),<br />
Init clrf PORTB ; lösche Port (setze alle werdende Ausgänge auf "0")<br />
bsf STATUS,RP0 ; auf Bank1 umschalten<br />
bcf OPTION_REG,7 ; aktiviere pull-ups<br />
movlw 0x30 ; definiere PortB: Pins 5 und 4 als Eingänge<br />
; und die restlichen als Ausgänge (00110000b) <br />
movwf TRISB ; schreibe in TRIS Register<br />
bcf STATUS,RP0 ; auf Bank0 umschalten<br />
goto @Init ; * initialisiere "PIC RAM Monitor" <br />
; hier endet das gesamte ASM Programm<br />
include "PIC RAM Monitor.asm" <br />
end<br />
<br />
Mit dem "*" wurden 3 Zeilen gekenzeichnet, die, in das mit PIC RAM Monitor beobachtetes ASM Programm, eingefügt werden müssen.<br />
<br />
Falls im beobachteten ASM Programm Interrupts benutzt werden, muss die ISR um die Prüfungen anderen Interrupt Flags ergänzt werden, was in der ISR als "eigener Code" eingetragen ist. Der PIC RAM Monitor reagiert nur auf Timer0 Interrupt Flag.<br />
<br />
Wenn keine I/O Pins mehr frei sind, kann der PIC Miniterminal parallel zur schon vorhandenen Hardware an die als Ausgänge definierte Portpins angeschlossen werden. In dem Fall werden aber die Ausgänge durch Ausgabe auf das Display gestört, was in Kauf genommen werden muss. Die Anschlüsse "@DT" und "@CK" müssen entsprechend definiert werden, z.B beim "Erstes.asm" für den PIC12F629:<br />
<br />
#define @DT _L3 ; * definiere Anschlüsse @DT und @CK<br />
#define @CK _L4 ; * für "PIC RAM Monitor"<br />
#define _T1 GPIO,3 ; Portpins benennen<br />
#define _T2 GPIO,4<br />
#define _L1 GPIO,5<br />
#define _L2 GPIO,2<br />
#define _L3 GPIO,1<br />
#define _L4 GPIO,0<br />
<br />
Demensprechend wird die Leitung "@DT" an GPIO,1 (Pin 6) und "@CK" an GPIO,0 (Pin 7) angeschlossen.<br />
<br />
== PIC Trainer ==<br />
<br />
Das ist im Prinzip ein "PIC RAM Monitor", das um ein paar Funktionen erweitert wurde. Mit diesem Programm wurden schon mehrere ASM Programme erstellt, z.B. [[#Hex Dec Wandlung|Hex Dec Wandlung]].<br />
<br />
Auch hier wurde Timer0 Interrupt verwendet, aber die Frequenz beträgt 2 Interrupts pro Sekunde. Das Programm benötigt ein "PIC Miniterminal", das an 2 freigewählte Portpins des PICs, die sowohl als Ein- als auch Ausgänge funktionieren, angeschlossen wird. Es ist kein selbständiges Programm, das in jedes Programm, das den Timer0 nicht benutzt, eingebunden werden kann. Es belegt 187 Speicherstellen im Programmspeicher und 11 Register im RAM (0x45 bis 0x4F). Die alle mit "*" gekennzeichnete Zeilen (alle außer "goto @Init" können geändert werden), müssen im Programm, in das "PIC Trainer" eingebunden ist, enthalten sein. Wegen ISR darf das Programm, in das "PIC Trainer" eingebunden ist, erst ab der Adresse 0x001E anfangen. Das HP "@Trainer", alle UPs und die Sprungtabelle können für PICs, die mehr Programmspeicher besitzen, mit entsprechender "org" Direktive höher verschoben werden. Entsprechend können auch alle im Programm benutzte Register höchstmögliche Adressen im RAM haben (z.B. @SWR equ 0x7F anstatt 0x4F).Dabei muss auch im UP @Test der Wert im PCLATH geändert werden. Siehe dazu [[#Tabellen|Tabellen]]. <br />
<br />
Um "Kollisionen" mit dem Programm, in das er eingebunden wird, zu vermeiden, fangen alle seine Marken mit "@" an. <br />
<br />
Der Zusammenhang zwischen der Register-Adresse und der Nummer des aufgerufenen Programm-Fragments zeigt folgende Skizze:<br />
<br />
<br />
----->0 1 2 3 4 5 6 7<-----TestX Nummer, für beiden Nibbles gleich<br />
/ -----------------------<br />
Register 2 |XX XX XX XX XX XX XX XX|<br />
Adresse \ |XX XX XX XX XX XX XX XX|<br />
\ -----------------------<br />
----->8 9 A B C D E F<br />
<br />
Es wird empfohlen, für schnelle Orientierung, sich die Nummer auf das Display anzubringen. <br />
<br />
Funktionen den Tasten:<br />
<br />
T1 - bewegt den Cursor auf dem Display um eine Position nach rechts. Am rechten Ende der unteren Zeile springt der Cursor wieder nach ganz links in die obere Zeile.<br />
<br />
T2 - erhöht (incrementiert) das Nibble an der Cursor-Position. Nach dem Fh kommt 0, 1, 2, usw.<br />
<br />
T3 - startet ein TestX mit der Nummer 0 bis Fh, die gleich dem rechten Nibble der Adresse des Registers an der Cursor-Position ist.<br />
<br />
Die Tasten werden 2 mal pro Sekunde während des Interrupts eigelesen und man braucht sie zum gewünschten Ergebniss gedrückt halten und dann los lassen. Gleichzeitiges Drücken mehr als einer Taste ist nicht vorgesehen.<br />
<br />
Um "Üben" zu ermöglichen und das Funktionieren des Programms zu Verstehen, wurde ein kurzes Testprogramm geschrieben.<br />
<br />
Der "PIC Trainer" eignet sich besonders gut zum erstellen von ASM Programmen, da beim einmaligen "brennen" des PICs bis zum 16 Programmfragmente die mit "return" enden (z.B. ein HP als UP) nacheinander aufgerufen und mit bilibigen, in Register eingestellten Werten , getestet werden können. <br />
<br />
Der Quellcode:<br />
<br />
; PIC Trainer.asm<br />
#define @Ftst @Int,7 ; Variablen definieren (Register benennen)<br />
@SWR equ 0x4F<br />
@SSR equ 0x4E<br />
@FSR equ 0x4D<br />
@Tmp equ 0x4C<br />
@Tmp1 equ 0x4B<br />
@Tmp2 equ 0x4A<br />
@Tmp3 equ 0x49<br />
@Int equ 0x48<br />
@LPC equ 0x47<br />
@LPC1 equ 0x46<br />
@Tasten equ 0x45<br />
ORG 0x0004 ; ISR<br />
bcf INTCON,GIE ; interrupts sperren<br />
movwf @SWR ; W-Register sichern<br />
swapf STATUS,0 ; STATUS Register<br />
movwf @SSR ; sichern<br />
movf FSR,0 ; FSR Register<br />
movwf @FSR ; sichern<br />
btfss INTCON,T0IF ; Timer0 interrupt Flag prüfen<br />
goto @IntTest ; wenn nicht gesetzt, eventuell andere prüfen<br />
bcf INTCON,T0IF ; Timer0 interrupt Flag löschen<br />
incf @Int,1 ; Zähler erhöhen<br />
btfss @Int,3 ; prüfen, ob schon 8. Timer0 interrupt<br />
goto $+3 ; wenn nicht, zum "@IntTest" springen<br />
clrf @Int ; Zähler löschen<br />
call @ ; Tasten auswerten und Registerinhalte anzeigen<br />
@IntTest ;**************;<br />
; eigener Code ;<br />
;**************;<br />
movf @FSR,0 ; FSR Register<br />
movwf FSR ; wiederherstellen<br />
swapf @SSR,0 ; STATUS Register<br />
movwf STATUS ; wiederherstellen<br />
movf @SWR,0 ; W-Register wiederherstellen<br />
retfie ; zurück ins Programm springen, interrupts erlauben <br />
ORG 0x0358 ; diese Adresse kann gleich max.Adresse - A7h sein<br />
@Trainer btfss @Ftst<br />
goto @Trainer<br />
bcf @Ftst<br />
call @Test<br />
call @Zeigen<br />
goto @Trainer<br />
@ bsf STATUS,RP0 ; @DT und @CK als Eingänge<br />
bsf @TDT<br />
bsf @TCK<br />
bcf STATUS,RP0<br />
clrf @Tasten ; Zustände von @DT und @CK ins @Tasten kopieren<br />
btfsc @CK<br />
bsf @Tasten,0<br />
btfsc @DT<br />
bsf @Tasten,1<br />
movf @Tasten,1 ; Tasten auswerten <br />
btfsc STATUS,Z<br />
bsf @Ftst ; setze Flag, wenn Taste3 gedrückt<br />
movf @Tasten,0<br />
sublw 1<br />
btfsc STATUS,Z<br />
call @IncLPC ; nächstes Nibble, wenn T2 gedrückt<br />
movf @Tasten,0<br />
sublw 2<br />
btfsc STATUS,Z<br />
call @Inc ; nibble erhöhen, wenn T1 gedrückt<br />
@Zeigen call @Out<br />
movlw 0x20 ; Anzeigen der Registerinhalte<br />
movwf FSR <br />
movlw 0x0C ; Cursor aus<br />
call @Cmd<br />
call @1st<br />
call @Line<br />
call @2nd<br />
call @Line<br />
movlw 0x0E ; Cursor an<br />
call @Cmd<br />
movlw 0x80 ; Display Adresse berechnen und Cursor plazieren<br />
btfsc @LPC,4<br />
addlw 0x30<br />
addwf @LPC,0<br />
call @Cmd<br />
return<br />
@Out bsf STATUS,RP0 ; @DT und @CK als Ausgänge<br />
bcf @TDT<br />
bcf @TCK<br />
bcf STATUS,RP0<br />
return<br />
@Test movlw 3 ; Test0-F starten<br />
movwf PCLATH<br />
movf @LPC1,0<br />
andlw 0x0F<br />
goto @TestTab<br />
@Inc movlw 0x20 ; Register Wert erhöhen<br />
movwf FSR<br />
movf @LPC1,0<br />
addwf FSR,1<br />
btfsc @LPC,0 <br />
goto @IncLN<br />
@IncHN movlw 0x10 ; High Nibble erhöhen<br />
addwf INDF,1<br />
return<br />
@IncLN incf INDF,1 ; Low Nibble erhöhen<br />
movf INDF,0<br />
andlw 0x0F<br />
btfss STATUS,Z<br />
return<br />
movlw 0x10<br />
subwf INDF,1<br />
return<br />
@IncLPC incf @LPC,1 ; Zähler der Position in der Zeile erhöhen<br />
movf @LPC,0<br />
sublw 0x20<br />
btfsc STATUS,Z<br />
clrf @LPC<br />
movf @LPC,0<br />
movwf @LPC1<br />
rrf @LPC1,1 ; @LPC1=@LPC/2<br />
return<br />
@Line movlw 8 ; Zeile an Display schicken<br />
movwf @Tmp<br />
movf INDF,0<br />
call @Val<br />
incf FSR,1<br />
decfsz @Tmp,1<br />
goto $-4<br />
return<br />
@1st movlw 0x80 ; Adresse der 1. Zeile an Display schicken<br />
goto @Cmd<br />
@2nd movlw 0xC0 ; Adresse der 2. Zeile an Display schicken<br />
@Cmd bcf STATUS,C ; Befehl an Display schicken<br />
goto @Send<br />
@Val movwf @Tmp1 ; Zahl (00-FF) an Display schicken<br />
swapf @Tmp1,0<br />
call @Num<br />
movf @Tmp1,0<br />
@Num andlw 0x0F ; Ziffer (0-F) an Display schicken<br />
movwf @Tmp2 <br />
movlw 0x0A<br />
subwf @Tmp2,0<br />
btfsc STATUS,C<br />
addlw 7<br />
addlw 0x3A<br />
bsf STATUS,C<br />
@Send movwf @Tmp2<br />
movlw 9 ; Byte + RS aus @Tmp2 (9 Bits) an Display schicken<br />
movwf @Tmp3<br />
@Ser bcf @CK<br />
bcf @DT<br />
btfsc @Tmp2,7<br />
bsf @DT<br />
bsf @CK<br />
rlf @Tmp2,1<br />
decfsz @Tmp3,1<br />
goto @Ser<br />
bcf @DT ; setze Display<br />
bsf @DT ; Enable<br />
bcf @CK ; lösche<br />
bcf @DT ; Display<br />
bsf @DT ; Enable<br />
bcf @DT<br />
return<br />
@Init bsf INTCON,GIE ; alle interrupts erlauben<br />
bsf INTCON,T0IE ; Timer0 interrupt erlauben<br />
bcf INTCON,T0IF ; Timer0 interrupt Flag löschen<br />
bsf STATUS,RP0 ; auf Bank 1 umschalten<br />
movf OPTION_REG,0 ; OPTION_REG in W-Register laden<br />
andlw 0xC0 ; Timer0 konfigurieren<br />
iorlw 7 ; Prescaler 256:1 <br />
movwf OPTION_REG ; ins OPTION_REG schreiben<br />
bcf STATUS,RP0 ; zurück auf Bank 0 umschalten<br />
clrf @Int ; Variablen initialisieren (löschen)<br />
clrf @LPC<br />
clrf @LPC1<br />
call @Out ; @DT und @CK als Ausgänge<br />
movlw 0x38 ; Display vom PIC Miniterminal initialisieren<br />
call @Cmd<br />
movlw 0x0C<br />
call @Cmd<br />
movlw 6<br />
call @Cmd<br />
return<br />
org 0x3EF ; diese Adresse kann gleich max.Adresse - 10h sein<br />
@TestTab addwf PCL,1 ; Sprungtabelle<br />
goto Test0<br />
goto Test1<br />
goto Test2<br />
goto Test3<br />
goto Test4<br />
goto Test5<br />
goto Test6<br />
goto Test7<br />
goto Test8<br />
goto Test9<br />
goto TestA<br />
goto TestB<br />
goto TestC<br />
goto TestD<br />
goto TestE<br />
goto TestF<br />
end<br />
<br />
Das Testprogramm:<br />
<br />
; Testprogramm.asm<br />
list P=16F84A ; Prozessor definieren<br />
include "P16F84A.inc" ; 4.000 MHz<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC<br />
#define @DT PORTB,7 ; * definiere Anschlüsse @DT und @CK<br />
#define @CK PORTB,6 ; * für "PIC Trainer"<br />
#define @TDT TRISB,7 ; * definiere ensprechende Bits im TRISx Register <br />
#define @TCK TRISB,6 ; * für o.g. Anschlüsse<br />
org 0x0000 ; hier fängt das gesamte ASM Programm an <br />
call Init ; rufe UP Init (Initialisierung) auf<br />
goto @Trainer <br />
org 0x001E ; ab da kann eigener Code anfangen<br />
Test0 incf 0x2F,1<br />
return<br />
Test1 incf 0x2E,1<br />
return<br />
Test2 incf 0x2D,1<br />
return<br />
Test3 incf 0x2C,1<br />
return<br />
Test4 incf 0x2B,1<br />
return<br />
Test5 incf 0x2A,1<br />
return<br />
Test6 incf 0x29,1<br />
return<br />
Test7 incf 0x28,1<br />
return<br />
Test8 incf 0x27,1<br />
return<br />
Test9 incf 0x26,1<br />
return<br />
TestA incf 0x25,1<br />
return<br />
TestB incf 0x24,1<br />
return<br />
TestC incf 0x23,1<br />
return<br />
TestD incf 0x22,1<br />
return<br />
TestE incf 0x21,1<br />
return<br />
TestF incf 0x20,1<br />
goto TestF<br />
Init ;--------------;<br />
; eigener Code ;<br />
;--------------;<br />
goto @Init ; * initialisiere "PIC Trainer" <br />
; hier endet das gesamte ASM Programm<br />
include "PIC Trainer.asm" <br />
end<br />
<br />
Das Programm "PIC Trainer" kann auch für grösseres Display (mehr Register und Testx) modifiziert werden. Als Beispiel ein Quellcode für 4x16 Display (0 bis 1Fh Register/Test):<br />
<br />
; PIC Trainer32.asm<br />
#define @Ftst @Int,7 ; Variablen definieren (Register benennen)<br />
@SWR equ 0x4F<br />
@SSR equ 0x4E<br />
@FSR equ 0x4D<br />
@Tmp equ 0x4C<br />
@Tmp1 equ 0x4B<br />
@Tmp2 equ 0x4A<br />
@Tmp3 equ 0x49<br />
@Int equ 0x48<br />
@LPC equ 0x47<br />
@LPC1 equ 0x46<br />
@LPC2 equ 0x45<br />
@Tasten equ 0x44<br />
ORG 0x0004 ; ISR<br />
bcf INTCON,GIE ; interrupts sperren<br />
movwf @SWR ; W-Register sichern<br />
swapf STATUS,0 ; STATUS Register<br />
movwf @SSR ; sichern<br />
movf FSR,0 ; FSR Register<br />
movwf @FSR ; sichern<br />
btfss INTCON,T0IF ; Timer0 interrupt Flag prüfen<br />
goto @IntTest ; wenn nicht gesetzt, eventuell andere prüfen<br />
bcf INTCON,T0IF ; Timer0 interrupt Flag löschen<br />
incf @Int,1 ; Zähler erhöhen<br />
btfss @Int,2 ; prüfen, ob schon 4. Timer0 interrupt<br />
goto $+3 ; wenn nicht, zum "@IntTest" springen<br />
clrf @Int ; Zähler löschen<br />
call @ ; Anzeigen der Registerinhalte aufrufen<br />
@IntTest ;**************;<br />
; eigener Code ;<br />
;**************;<br />
movf @FSR,0 ; FSR Register<br />
movwf FSR ; wiederherstellen<br />
swapf @SSR,0 ; STATUS Register<br />
movwf STATUS ; wiederherstellen<br />
movf @SWR,0 ; W-Register wiederherstellen<br />
retfie ; zurück ins Programm springen, interrupts erlauben <br />
ORG 0x0358 ; diese Adresse kann gleich max.Adresse - A7h sein<br />
@Trainer btfss @Ftst<br />
goto @Trainer<br />
bcf @Ftst<br />
call @Test<br />
call @Zeigen<br />
goto @Trainer<br />
ORG 0x0338<br />
@ bsf STATUS,RP0 ; @DT und @CK als Eingänge<br />
bsf @TDT<br />
bsf @TCK<br />
bcf STATUS,RP0<br />
clrf @Tasten ; Zustände von @DT und @CK ins @Tasten kopieren<br />
btfsc @CK<br />
bsf @Tasten,0<br />
btfsc @DT<br />
bsf @Tasten,1<br />
movf @Tasten,1 ; Tasten auswerten <br />
btfsc STATUS,Z<br />
bsf @Ftst ; setze Flag, wenn Taste3 gedrückt<br />
movf @Tasten,0<br />
sublw 1<br />
btfsc STATUS,Z<br />
call @IncLPC ; nächstes Nibble, wenn T2 gedrückt<br />
movf @Tasten,0<br />
sublw 2<br />
btfsc STATUS,Z<br />
call @Inc ; nibble erhöhen, wenn T1 gedrückt<br />
@Zeigen call @Out<br />
movlw 0x20 ; Anzeigen der Registerinhalte<br />
movwf FSR <br />
movlw 0x0C ; Cursor aus<br />
call @Cmd<br />
call @1st<br />
call @Line<br />
call @2nd<br />
call @Line<br />
call @3rd<br />
call @Line<br />
call @4th<br />
call @Line<br />
movlw 0x0E ; Cursor an<br />
call @Cmd<br />
swapf @LPC,0 ; Display Adresse berechnen und Cursor plazieren<br />
andlw 0x0F<br />
movwf @LPC2<br />
btfss STATUS,Z<br />
goto $+3<br />
movlw 0x80<br />
goto @AddLPC<br />
movf @LPC2,0<br />
sublw 1<br />
btfss STATUS,Z<br />
goto $+3<br />
movlw 0x30<br />
goto @AddLPC<br />
movf @LPC2,0<br />
sublw 2<br />
btfss STATUS,Z<br />
goto $+3<br />
movlw 0x70<br />
goto @AddLPC<br />
movf @LPC2,0<br />
sublw 3<br />
btfsc STATUS,Z<br />
movlw 0xA0<br />
@AddLPC addwf @LPC,0 <br />
call @Cmd<br />
return<br />
@Out bsf STATUS,RP0 ; @DT und @CK als Ausgänge<br />
bcf @TDT<br />
bcf @TCK<br />
bcf STATUS,RP0<br />
return<br />
@Test movlw 3 ; Test0-1F starten<br />
movwf PCLATH<br />
movf @LPC1,0<br />
andlw 0x1F<br />
goto @TestTab<br />
@Inc movlw 0x20 ; Register Wert erhöhen<br />
movwf FSR<br />
movf @LPC1,0<br />
addwf FSR,1<br />
btfsc @LPC,0 <br />
goto @IncLN<br />
@IncHN movlw 0x10 ; High Nibble erhöhen<br />
addwf INDF,1<br />
return<br />
@IncLN incf INDF,1 ; Low Nibble erhöhen<br />
movf INDF,0<br />
andlw 0x0F<br />
btfss STATUS,Z<br />
return<br />
movlw 0x10<br />
subwf INDF,1<br />
return<br />
@IncLPC incf @LPC,1 ; Zähler der Position in der Zeile erhöhen<br />
movf @LPC,0<br />
sublw 0x40<br />
btfsc STATUS,Z<br />
clrf @LPC<br />
movf @LPC,0<br />
movwf @LPC1<br />
rrf @LPC1,1<br />
return<br />
@Line movlw 8 ; Zeile an Display schicken<br />
movwf @Tmp<br />
movf INDF,0<br />
call @Val<br />
incf FSR,1<br />
decfsz @Tmp,1<br />
goto $-4<br />
return<br />
@1st movlw 0x80 ; Adresse der 1. Zeile an Display schicken<br />
goto @Cmd<br />
@2nd movlw 0xC0 ; Adresse der 2. Zeile an Display schicken<br />
goto @Cmd<br />
@3rd movlw 0x90 ; Adresse der 3. Zeile an Display schicken<br />
goto @Cmd<br />
@4th movlw 0xD0 ; Adresse der 4. Zeile an Display schicken<br />
@Cmd bcf STATUS,C ; Befehl an Display schicken<br />
goto @Send<br />
@Val movwf @Tmp1 ; Zahl (00-FF) an Display schicken<br />
swapf @Tmp1,0<br />
call @Num<br />
movf @Tmp1,0<br />
@Num andlw 0x0F ; Ziffer (0-F) an Display schicken<br />
movwf @Tmp2 <br />
movlw 0x0A<br />
subwf @Tmp2,0<br />
btfsc STATUS,C<br />
addlw 7<br />
addlw 0x3A<br />
bsf STATUS,C<br />
@Send movwf @Tmp2<br />
movlw 9 ; Byte + RS aus @Tmp2 (9 Bits) an Display schicken<br />
movwf @Tmp3<br />
@Ser bcf @CK<br />
bcf @DT<br />
btfsc @Tmp2,7<br />
bsf @DT<br />
bsf @CK<br />
rlf @Tmp2,1<br />
decfsz @Tmp3,1<br />
goto @Ser<br />
bcf @DT ; setze Display<br />
bsf @DT ; Enable<br />
bcf @CK ; lösche<br />
bcf @DT ; Display<br />
bsf @DT ; Enable<br />
bcf @DT<br />
return<br />
@Init bsf INTCON,GIE ; alle interrupts erlauben<br />
bsf INTCON,T0IE ; Timer0 interrupt erlauben<br />
bcf INTCON,T0IF ; Timer0 interrupt Flag löschen<br />
bsf STATUS,RP0 ; auf Bank 1 umschalten<br />
movf OPTION_REG,0 ; OPTION_REG in W-Register laden<br />
andlw 0xC0 ; Timer0 konfigurieren<br />
iorlw 7 ; Prescaler 256:1 <br />
movwf OPTION_REG ; ins OPTION_REG schreiben<br />
bcf STATUS,RP0 ; zurück auf Bank 0 umschalten<br />
clrf @Int<br />
clrf @LPC<br />
clrf @LPC1<br />
call @Out<br />
movlw 0x38 ; Display vom PIC Miniterminal initialisieren<br />
call @Cmd<br />
movlw 0x0C<br />
call @Cmd<br />
movlw 6<br />
call @Cmd<br />
return<br />
org 0x3DF ; diese Adresse kann gleich max.Adresse - 20h sein<br />
@TestTab addwf PCL,1 ; Sprungtabelle<br />
goto Test0<br />
goto Test1<br />
goto Test2<br />
goto Test3<br />
goto Test4<br />
goto Test5<br />
goto Test6<br />
goto Test7<br />
goto Test8<br />
goto Test9<br />
goto TestA<br />
goto TestB<br />
goto TestC<br />
goto TestD<br />
goto TestE<br />
goto TestF<br />
goto Test10<br />
goto Test11<br />
goto Test12<br />
goto Test13<br />
goto Test14<br />
goto Test15<br />
goto Test16<br />
goto Test17<br />
goto Test18<br />
goto Test19<br />
goto Test1A<br />
goto Test1B<br />
goto Test1C<br />
goto Test1D<br />
goto Test1E<br />
goto Test1F<br />
end<br />
<br />
Es können eigene Namen für mit "PIC Trainer" erstellten und geprüften UPs vewendet werden. Sie müssen nur im eigenem Programm mit "#define" den entsprechenden TestX zugewiesen werden. Aus unerklärlichen Gründen funktioniert es aber nur, wenn die Definitionen als erste im Quellcode sind (MPASM Falle?).<br />
<br />
#define Test0 Init<br />
#define Test1 Lesen<br />
#define Test2 Schreiben<br />
usw.<br />
<br />
Somit wird z.B. das UP "Lesen" aufgerufen wenn die Taste T3 gedrückt wird und der Cursor auf der Register-Adresse "21" steht. Es wird empfohlen, eine Tabelle mit UPs-Namen und den enstprechenden Nummern (0 bis Fh) zum dessen Aufrufen, sich zu erstellen.<br />
<br />
Für alle definierte, aber noch nicht existierende UPs, sollten "return" Befehle angewendet werden, um einen Programmabsturtz durch versehentliches Aufrufen von dennen, zu vermeinden. Zum Beispiel:<br />
<br />
#define TestD RAMClr<br />
#define TestE RAMSet<br />
#define TestF KeyTst<br />
<br />
RAMClr return<br />
RAMSet return<br />
KeyTst return<br />
<br />
Wenn ein UP (z.B. eine Warteschleife) läuft, kann ein nächstes erst nach seiner Beendigung gestartet werden.<br />
<br />
Wenn ein UP in einer endloser Schleife "hängen" bleibt, dann kann nur anderes UP nicht gestartet werden. Die alle andere Funktionen des Programms werden nicht gestört. In dem Testprogramm wurde absichtlich TestF als endlose Schleife erstellt, um das Funktionieren des "PIC Trainer"s in diesem Fall zu zeigen.<br />
<br />
== PIC Profiler ==<br />
<br />
Der "PIC Profiler" ermöglicht das Messen von Ausführungszeit eines ASM UPs, also Programm-Fragments das mit "return" endet. Es wird die gesamte Ausführungszeit gemessen mit "call" (2 Takten) und "return" (2 Takten) inklusive. Um ein HP messen zu können, muss es ins UP umgewandelt werden, wie im [[#Unterprogramm|Unterprogramm]] erklärt. <br />
<br />
Zum Messen wurde Timer0 benutzt, der um 2 Byte Zähler erweitert wurde. Somit beträgt die maximale messbare Ausführungszeit FFFFFFFFh = 4 294 967 295 Prozessortakten, was mehr als einer Stunde beim 4 MHz Quarz entspricht. Bis FFFFh ist das Messergebniss genau, weiter wurde es nicht getestet. <br />
<br />
Um die Durchführung der Messung zu ermöglichen, wird das "goto Haupt" für den MPASM mit einem Semikolon augeblendet und "goto @Profiler" eingeschrieben. Nach dem Einschalten wird anstatt ins "Haupt" zum "@Profiler" gesprungen. Der "@Profiler" misst die Ausführungszeit nur einmal, da er mit "sleep" endet. Wenn endlose Messung gewünscht wird, muss der Befehl "sleep" mit "goto @Profiler" ersetzt werden. Wegen ISR vom "PIC Profiler" darf das Programm, in dem gemessen wird, erst ab der Adresse 0x0013 anfangen.<br />
<br />
Zum Darstellen des Messergebnisses kann entweder schon im zu messenden Programm vorhandenes Display bzw. "PIC Miniterminal" benutzt werden. Das hex Ergebniss befindet sich in den Register @A3 (MSB), @A2, @A1 und @A0 (LSB).<br />
<br />
Die Version, die das im Programm vorhandenes Display benutzt, braucht 46 Speicherstellen im Programmspeicher und 8 Register im RAM.<br />
<br />
; PIC Profiler1.asm<br />
@SWR equ 0x4F ; Variablen deklarieren (Register benennen)<br />
@SSR equ 0x4E<br />
@A3 equ 0x4D<br />
@A2 equ 0x4C<br />
@A1 equ 0x4B<br />
@A0 equ 0x4A<br />
@ATmp equ 0x49<br />
@RTmp equ 0x48<br />
ORG 0x0004 ; ISR (interrupt service routine)<br />
bcf INTCON,GIE ; interrupts sperren<br />
bcf INTCON,T0IF ; Timer0 interrupt flag löschen<br />
movwf @SWR ; W-Register sichern<br />
swapf STATUS,0 ; STATUS Register<br />
movwf @SSR ; sichern<br />
movlw 1<br />
addwf @A2,1 ; erhöhe @A2, wenn Timer0 überlaufen ist<br />
btfsc STATUS,C<br />
incf @A3,1 ; erhöhe @A3, wenn @A2 überlaufen ist<br />
swapf @SSR,0 ; STATUS Register<br />
movwf STATUS ; wiederherstellen<br />
movf @SWR,0 ; W-Register wiederherstellen<br />
clrf TMR0 ; Timer0 und Prescaler löschen<br />
retfie ; zurück ins gemessene UP, Interrupts erlauben<br />
org 0x03E0 ; diese Adresse kann gleich max.Adresse - 1Fh sein<br />
@Profiler clrf @A3 ; Messregister löschen<br />
clrf @A2<br />
bsf STATUS,RP0 ; Bank1<br />
movlw 0xC7 ; interner Takt<br />
movwf OPTION_REG ; Timer0 konfigurieren<br />
bcf STATUS,RP0 ; Bank 0<br />
movlw 0xE0 ; nur Timer0 Interrupt erlaubt<br />
movwf INTCON ; Interrupts konfigurieren<br />
clrf TMR0 ; Timer0 und Prescaler löschen<br />
;............................................................................<br />
call Messen ; dieses UP wird gemessen<br />
;............................................................................<br />
bsf STATUS,RP0<br />
bsf OPTION_REG,T0CS ; "stopp" Timer0<br />
bcf STATUS,RP0 <br />
movf TMR0,0 ; TMR0 ins Register<br />
movwf @A1 ; @A1 kopieren<br />
movwf @RTmp<br />
clrf @ATmp ; Prescaler ins Register @A0 kopieren<br />
incf @ATmp,1<br />
bsf STATUS,RP0<br />
bsf OPTION_REG,T0SE<br />
bcf OPTION_REG,T0SE<br />
bcf STATUS,RP0<br />
movf TMR0,0<br />
subwf @RTmp,0<br />
btfsc STATUS,Z<br />
goto $-8<br />
comf @ATmp,1<br />
incf @ATmp,0<br />
movwf @A0<br />
decf @A0,1<br />
call Ausgeben ; Messergebniss aus @A3, @A2, @A1 und @A0<br />
; auf einem Display anzeigen (eigener Code)<br />
sleep<br />
end<br />
<br />
Die Version, die zur Ausgabe den "PIC Miniterminal" benutzt, belegt 94 Speicherstellen im Programmspeicher und 12 Register im RAM.<br />
<br />
; PIC Profiler2.asm<br />
@SWR equ 0x4F ; Variablen deklarieren (Register benennen)<br />
@SSR equ 0x4E<br />
@A3 equ 0x4D<br />
@A2 equ 0x4C<br />
@A1 equ 0x4B<br />
@A0 equ 0x4A<br />
@ATmp equ 0x49<br />
@RTmp equ 0x48<br />
@Tmp equ 0x47 ; * Variablen für "PIC Miniterminal" definieren<br />
@Tmp1 equ 0x46<br />
@Tmp2 equ 0x45<br />
@Tmp3 equ 0x44 <br />
ORG 0x0004 ; ISR (interrupt service routine)<br />
bcf INTCON,GIE ; interrupts sperren<br />
bcf INTCON,T0IF ; Timer0 interrupt flag löschen<br />
movwf @SWR ; W-Register sichern<br />
swapf STATUS,0 ; STATUS Register<br />
movwf @SSR ; sichern<br />
movlw 1<br />
addwf @A2,1 ; erhöhe @A2, wenn Timer0 überlaufen ist<br />
btfsc STATUS,C<br />
incf @A3,1 ; erhöhe @A3, wenn @A2 überlaufen ist<br />
swapf @SSR,0 ; STATUS Register<br />
movwf STATUS ; wiederherstellen<br />
movf @SWR,0 ; W-Register wiederherstellen<br />
clrf TMR0 ; Timer0 und Prescaler löschen<br />
retfie ; zurück ins gemessene UP, Interrupts erlauben<br />
org 0x03B0 ; diese Adresse kann gleich max.Adresse - 4Fh sein<br />
@Profiler movlw 0x38 ; Display vom "PIC Miniterminal" initialisieren<br />
call @Cmd<br />
movlw 0x0C<br />
call @Cmd<br />
movlw 6<br />
call @Cmd <br />
clrf @A3 ; Messregister löschen<br />
clrf @A2<br />
bsf STATUS,RP0 ; Bank1<br />
movlw 0xC7 ; interner Takt<br />
movwf OPTION_REG ; Timer0 konfigurieren<br />
bcf STATUS,RP0 ; Bank 0<br />
movlw 0xE0 ; nur Timer0 Interrupt erlaubt<br />
movwf INTCON ; Interrupts konfigurieren<br />
clrf TMR0 ; Timer0 löschen<br />
;............................................................................<br />
call Messen ; dieses UP wird gemessen<br />
;............................................................................<br />
bsf STATUS,RP0<br />
bsf OPTION_REG,T0CS ; "stopp" Timer0<br />
bcf STATUS,RP0 <br />
movf TMR0,0 ; TMR0 ins Register<br />
movwf @A1 ; @A1 kopieren<br />
movwf @RTmp<br />
clrf @ATmp ; Prescaler ins Register @A0 kopieren<br />
incf @ATmp,1<br />
bsf STATUS,RP0<br />
bsf OPTION_REG,T0SE<br />
bcf OPTION_REG,T0SE<br />
bcf STATUS,RP0<br />
movf TMR0,0<br />
subwf @RTmp,0<br />
btfsc STATUS,Z<br />
goto $-8<br />
comf @ATmp,1<br />
incf @ATmp,0<br />
movwf @A0<br />
decf @A0,1<br />
call @1st ; Messergebniss aus @A3, @A2, @A1 und @A0<br />
; auf "PIC Miniterminal" ausgeben<br />
movf @A3,0<br />
call @Val<br />
movf @A2,0<br />
call @Val<br />
movf @A1,0<br />
call @Val<br />
movf @A0,0<br />
call @Val<br />
sleep<br />
@1st movlw 0x80 ; Adresse der 1. Zeile an Display schicken<br />
@Cmd bcf STATUS,C ; Befehl an Display schicken <br />
goto @Send<br />
@Val movwf @Tmp1 ; Zahl (00-FF) an Display schicken<br />
swapf @Tmp1,0<br />
call @Num<br />
movf @Tmp1,0<br />
@Num andlw 0x0F ; Ziffer (0-F) an Display schicken<br />
movwf @Tmp2 <br />
movlw 0x0A <br />
subwf @Tmp2,0<br />
btfsc STATUS,C<br />
addlw 7<br />
addlw 0x3A<br />
bsf STATUS,C<br />
@Send movwf @Tmp2<br />
movlw 9 ; ein Byte + RS aus @Tmp2 (9 Bits) an Display schicken<br />
movwf @Tmp3<br />
@Ser bcf @CK<br />
bcf @DT<br />
btfsc @Tmp2,7<br />
bsf @DT<br />
bsf @CK<br />
rlf @Tmp2,1<br />
decfsz @Tmp3,1<br />
goto @Ser<br />
bcf @DT ; setze Display<br />
bsf @DT ; Enable<br />
bcf @CK ; lösche<br />
bcf @DT ; Display<br />
bsf @DT ; Enable<br />
bcf @DT<br />
return <br />
end <br />
<br />
Das Programm "@Profiler" kann für PICs, die mehr Programmspeicher besitzen, mit entsprechender "org" Direktive höher verschoben werden. Entsprechend können auch alle im Programm benutzte Register höchstmögliche Adressen im RAM haben (z.B. @SWR equ 0x7F anstatt 0x4F).<br />
<br />
Als praktisches Beispiel wurde das UP "Warten" vom "Erstes.asm" für den PIC16F84A gemessen und das Ergebniss auf dem "PIC Miniterminal" dargestellt.<br />
<br />
; Erstesp.asm<br />
list P=16F84A ; Prozessor definieren<br />
include "P16F84A.inc" ; 4.000 MHz<br />
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC<br />
#define Messen Warten ; definiere das zu messende UP<br />
#define @DT PORTB,7 ; * definiere Anschlüsse @DT und @CK<br />
#define @CK PORTB,6 ; * für "PIC Miniterminal"<br />
#define _T1 PORTB,5 ; Portpins benennen<br />
#define _T2 PORTB,4<br />
#define _L1 PORTB,3<br />
#define _L2 PORTB,2<br />
#define _L3 PORTB,1<br />
#define _L4 PORTB,0<br />
P0 equ 0x20 ; Variablen definieren (Register benennen)<br />
P1 equ 0x21<br />
P2 equ 0x22<br />
org 0x0000 ; hier fängt das ASM Programm in dem gemessen wird an <br />
call Init ; rufe UP Init (Initialisierung) auf<br />
;goto Haupt <br />
goto @Profiler <br />
org 0x0013 ; hier fängt das Program, in dem gemessen wird, an<br />
Haupt btfsc _T1 ; prüfe, ob Taster T1 gedrückt ist, wenn ja,<br />
; überspringe "goto T2Test"<br />
goto T2Test ; wenn nicht, springe zu T2Test<br />
bsf _L1 ; schalte _L1 an<br />
call Warten ; warte ca. 0,4 s (400 000 Prozessortakten)<br />
bcf _L1 ; schalte _L1 aus<br />
bsf _L2 ; schalte _L2 an<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus<br />
bsf _L3 ; schalte _L3 an<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus<br />
bsf _L4 ; schalte _L4 an<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus<br />
T2Test btfsc _T2 ; prüfe, ob Taster T2 gedrückt ist, wenn ja,<br />
; überspringe "goto Haupt"<br />
goto Haupt ; Wenn nicht, springe zu Haupt<br />
bsf _L4 ; schalte _L4 an<br />
call Warten ; warte<br />
bcf _L4 ; schalte _L4 aus<br />
bsf _L3 ; schalte _L3 an<br />
call Warten ; warte<br />
bcf _L3 ; schalte _L3 aus<br />
bsf _L2 ; schalte _L2 an<br />
call Warten ; warte<br />
bcf _L2 ; schalte _L2 aus<br />
bsf _L1 ; schalte _L1 an<br />
call Warten ; warte<br />
bcf _L1 ; schalte _L1 aus<br />
goto Haupt ; springe zu Haupt<br />
Warten movlw 2 ; schreibe "2" für ca. 0,4 s<br />
movwf P2 ; ins Register P2<br />
clrf P1 ; lösche Register P1 (schreibe "0" für 256 Durchläufe) <br />
clrf P0 ; lösche Register P0 (schreibe "0" für 256 Durchläufe)<br />
decfsz P0,1 ; dekrementiere P0, überspringe nächsten Befehl bei P0=0<br />
goto $-1 ; gehe zur voherigen Adresse (Befehl "decfsz P0") <br />
decfsz P1,1 ; dekrementiere P1, überspringe nächsten Befehl bei P1=0 <br />
goto $-4 ; gehe zur aktueller Adresse - 4 (Befehl "clrf P0")<br />
decfsz P2,1 ; dekrementiere P2, überspringe nächsten Befehl bei P2=0<br />
goto $-7 ; gehe zur aktueller Adresse - 7 (Befehl "clrf P1")<br />
return ; springe zurück zum Aufrufer ("Haupt"),<br />
Init clrf PORTB ; lösche Port (setze alle werdende Ausgänge auf "0")<br />
bsf STATUS,RP0 ; auf Bank1 umschalten<br />
bcf OPTION_REG,7 ; aktiviere pull-ups<br />
movlw 0x30 ; definiere PortB: Pins B5 und B4 als Eingänge<br />
; und die restlichen als Ausgänge (00110000b) <br />
movwf TRISB ; schreibe in TRIS Register<br />
bcf STATUS,RP0 ; auf Bank0 umschalten<br />
return<br />
; hier endet das ASM Programm in dem gemessen wird<br />
include "PIC Profiler2.asm" <br />
end<br />
<br />
[[Kategorie:Microcontroller]]<br />
[[Kategorie:Software]]<br />
[[Category:PIC]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Diskussion:PIC_Assembler&diff=23379Diskussion:PIC Assembler2013-12-10T12:17:51Z<p>Besserwessi: Befehlsausführung</p>
<hr />
<div><br />
<br />
== Befehlsausführung ==<br />
<br />
Ich kann mir nicht vorstellen, dass der PIC (also PIC10...18) pipelined arbeitet, also wirklich bis zu 4 Befehle quasi gleichzeitig. So gut kenne ich die PICs nicht, aber soweit ich weiss wirklich 4 Oszillator Takte = 1 Rechentakt für einen Befehl, und erst danach kommt der nächste Befehl.--Besserwessi 13:17, 10. Dez 2013 (CET)</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Filter_(Elektronik)&diff=23376Filter (Elektronik)2013-12-09T10:46:27Z<p>Besserwessi: /* Arten von Filtern */</p>
<hr />
<div>= Wozu benutzt man Filter? =<br />
<br />
Für Filter gibt es eine ganze Menge Anwendungen.<br />
<br />
Als erstes Beispiel nehmen wir uns mal einen Verstärker her, einen einfachen. Einen Transistor in Emitterschaltung zum Beispiel. Am Eingang dieser Schaltung liegt die Basis des Transistors, beschaltet mit einem Vorwiderstand von Vcc aus. Dieser dient dazu, den Arbeitspunkt des Transistors festzulegen und "zieht" die Basis des Transistors zum Beispiel auf 0,70 Volt.<br />
Was passiert wenn man nun ein Signal anlegt, beispielsweise ein Audiosignal, welches zwischen 0,1 Volt und -0,1 Volt schwingt? Die Basis wird auf eben diese (im Mittel) 0 Volt heruntergezogen, und der Strom, der eigentlich für die Basis vorgesehen war, fließt nun einfach in die Signalquelle ab.<br><br />
Hier schafft ein Entkopplungskondensator, den man zwischen Signalquelle und Basis des Transistors schaltet, Abhilfe: Die Spannung zwischen dessen Anschlüssen steigt durch den abfließenden Strom langsam an. Die Spannung an der zum Transistor gewandten Seite steigt also an, und irgendwann ist die Spannung soweit gestiegen, dass durch die Basis des Transistors wieder der entsprechende Strom fließen kann. Durch den Kondensator fließt dann kein Gleichstrom mehr.<br />
Die Wechselspannung, in diesem Fall das Audiosignal, wird dagegen durchgelassen. Dabei fließt nämlich während der positiven Halbwelle Strom in die Schaltung herein, bei der negativen Halbwelle des Signals dagegen fließt der Strom wieder heraus. Diese Strompulse sind so kurz, dass der Kondensator seine Spannung dabei kaum ändert, sie werden also fast ungehindert durchgelassen.<br />
<br />
Als zweites Beispiel nehmen wir uns nochmal Audio-Kram her: Wir haben nen Tieftöner, den wir an unsere Stereoanlage anschließen wollen. Die tiefen Frequenzen soll er ruhig wiedergeben - die hohen Frequenzen jedoch nicht, denn für die sind die Hochtöner zuständig.<br><br />
Eine Spule, die hier in Reihe zum Lautsprecher geschaltet wird, erfüllt diese Bedingungen. Wenn ein Signal an die Kombination aus Lautsprecher und Spule angelegt wird, steigt der Strom, bedingt durch die Induktivität der Spule, nur langsam an. Bei tiefen Frequenzen fällt das kaum auf, denn eine Halbwelle ist lang genug, um den Strom durch den Lautsprecher und die Spule zu erhöhen. Bei hohen Frequenzen dagegen sind die Halbwellen so kurz, dass sich kein nennenswerter Stromfluss einstellen kann - die Signale werden von der Spule blockiert.<br />
<br />
Zur Glättung von Spannungen und / oder Strömen werden auch Filter eingesetzt. Eine Kombination aus Spule und Kondensator (LC-Filter) setzt man beispielsweise häufig vor der ADC-Versorgung von Mikrocontrollern ein. Diese Filter filtern doppelt: Wenn die Versorgungsspannung plötzlich leicht abfällt, ändert sich zunächst der Strom in der Spule, aber wie gesagt wurde, durch die Induktivität verlangsamt. Wenn der Strom etwas abgefallen ist, wird das zweite Filterelement - der Kondensator - aktiv, denn durch ihn muss zuerst ein Strom fließen, damit sich die Spannung am Ausgang der Schaltung ändern kann.<br />
<br />
----<br />
<br />
*Phantomspeisung (gecancelt: Gebräuchliche Systeme wie PoE brauchen dafür afaik keine echten Filter.)<br />
<br />
= Arten von Filtern =<br />
<br />
Nun gibt es aber zig verschiedene Möglichkeiten, Filter aufzubauen, noch dazu ist jede nur für spezielle Anwendungen geeignet.<br><br />
Man unterteilt Filter zunächst in aktive Filter und passive Filter: Passive Filter sind solche, die nur aus passiven Bauteilen bestehen. Dazu gehören die oben angesprochenen Kondensatoren, Spulen und Widerstände, aber auch Quarze.<br />
Aktive Filter enthalten als aktive Komponente meist einen Operationsverstärker. Der Vorteil liegt auf der Hand: Der Ausgang des OpAmps ist auch der Ausgang des Filters, der damit auch ruhig bis einige Milliampere belastet werden kann - bei passiven Filtern ist dies meistens nicht ohne weiteres möglich, die Ausgangsspannung würde sich dabei ändern. Der Nachteil sollte auch klar sein: Ein OpAmp, und jede andere aktive Komponente auch, braucht eine Versorgungsspannung. Für eine Frequenzweiche in einer passiven Standbox (d.h. der Verstärker ist nicht integriert) sind aktive Filter dadurch ungeeignet.<br />
<br />
[[Bild:Filterordnungen.gif|thumb|Beispiele für verschiedene Filterordnungen]]<br />
Weiterhin unterscheidet man Filter nach verschiedenen Ordnungen. Die Ordnung spiegelt dabei die Anzahl der frequenzabhängigen Bauteile wieder. Ein Filter höherer Ordnung, beispielsweise die oben beschriebene Kombination aus Spule und Kondensator, hat eine bessere Trennschärfe. Das bedeutet, dass der Abstand zwischen Frequenzen, die durchgelassen werden, und Frequenzen, die gesperrt werden, kleiner ist.<br><br />
Nun gut, im Bild rechts sieht man schon dass das so nicht ganz stimmt: Jede Frequenz wird noch irgendwie durchgelassen, aber die Dämpfung, die in dB gemessen wird, wird immer stärker. Man müsste also eigentlich sagen, dass der Abstand der Frequenzen, die als durchgelassen '''bezeichnet''' werden können und der Frequenzen, die als gesperrt '''bezeichnet''' werden können, kleiner ist. Ab welcher Dämpfung eine Frequenz als gesperrt oder durchgelassen bezeichnet werden kann, das hängt wiederum von der Anwendung ab. In Audioanwendungen reicht ein Filter 1. Ordnung meist völlig aus, aber um beispielsweise ein PWM-Signal in eine Gleichspannung zu wandelt, muss eine bestimmte Frequenz unbedingt möglichst schwach gedämpft durchgelassen werden, während die PWM-Frequenz möglichst stark gedämpft werden soll. Deshalb nutzt man hierfür vorwiegend Filter höherer Ordnungen.<br />
Filter von mehr als 2. Ordnung sind equivalent zu einer Kombination von Filtern 1. und 2. Ordnung. Beispielsweise werden für einen Filter 5. Ordnung ein Filter 1. Ordnung und 2 von 2. Ordnung hintereinander geschaltet.<br />
<br />
[[Bild:Filtervergleich.gif|thumb|Beispiele für verschiedene Filtercharakteristiken]]<br />
Zuletzt unterteilt man Filter noch in verschiedene Charakteristiken, namentlich Tiefpässe, Hochpässe, Bandpässe und Bandsperren.<br />
*Tiefpässe lassen tiefe Frequenzen durch und sperren hohe Frequenzen. (siehe blaue Kurve im Bild.) Eine typische Anwendung wäre die Glättung eines Signals mithilfe einer Kombination aus Spule und Kondensator. <br />
*Hochpässe machen das Gegenteil: Sie sperren tiefe Frequenzen und lassen hohe Frequenzen durch. (siehe grüne Kurve im Bild.) Beispiele wären eine Gleichspannungsentkopplung oder eine Frequenzweiche für einen Hochtonlautsprecher.<br />
*Bandpässe sperren hohe und tiefe Frequenzen - Ein bestimmter Frequenzbereich wird jedoch durchgelassen. (siehe rote Kurve im Bild.) Eine Anwendung hierfür wäre eine Frequenzweiche vor einem Mitteltöner '''''(Sorry für die ganzen Beispiele aus dem Audiobereich, fällt vielleicht wem was besseres ein? Passiv-PFC würde mir einfallen, würde aber ne längere Erläuterung notwendig machen.)'''''<br />
*Bandsperren sind das Gegenstück zu den Bandpässen: Hohe und tiefe Frequenzen werden durchgelassen, ein bestimmter Frequenzbereich wird gesperrt. (siehe türkise Kurve im Bild.) Eine solche Schaltung kann beispielsweise verwendet werden, um die möglicherweise bei Datenübertragungen über Modem störenden Gebührenimpulse aus der Telefonleitung herauszufiltern.<br />
<br />
Wie man sieht, sind die Bezeichnungen so gut wie selbsterklärend.<br />
<br />
= Realisierung =<br />
<br />
Wenn man einen Filter für eine bestimmte Anwendung berechnen will, braucht man folgende Informationen:<br />
* Die Grenzfrequenz. Das ist die Frequenz, ab der die Ausgangsspannung gegenüber der Eingangsspannung um 3dB abgeschwächt wird - das entspricht in etwa einer Abschwächum um den Faktor 1,414, genauer um <math>\sqrt{2}</math><br />
* Die Ordnung des Filters. Diese ergibt sich aus den Anforderungen in der Anwendung.<br />
* Ob der Filter aktiv oder passiv ausgeführt werden soll<br />
<br />
Alle Arten von Filtern, also solche 1. Ordnung, 2. Ordnung, Tiefpässe, Hochpässe etc. lassen sich sowohl Passiv als auch Aktiv aufbauen. Filter höherer als 2. Ordnung lassen sich immer als Verknüpfung von Filtern 1. und 2. Ordnung realisieren (bei passiven Filtern in der Regel mit einem Verstärker dazwischen). <br />
Beginnen wir zunächst mit den passiven Filtern:<br />
<br />
== Passiv ==<br />
<br />
=== 1. Ordnung, Tiefpass und Hochpass ===<br />
<br />
Für Tief- und Hochpässe 1. Ordnung gibt es zwei Möglichkeiten: Entweder man schaltet das frequenzabhängige Bauteil vor einen Lastwiderstand (beispielsweise einen Lautsprecher), oder man schaltet den Widerstand vor das frequenzabhängige Bauteil. Wie das dann aussieht, zeigt die folgende Abbildung:<br />
<br />
[[Bild:Filter 1.Ordnung.gif]]<br />
<br />
Ue ist die Eingangsspannung, vor dem Filter, und Ua ist die Spannung, die am Ausgang der Schaltung anliegt um weiterverarbeitet zu werden. Die obigen Aufbauten, mit dem frequenzabhängigen Bauteil vor dem Lastwiderstand, bieten sich an, wenn man bereits einen Lastwiderstand - beispielsweise einen Lautsprecher - gegeben hat. Den Widerstand R ersetzt man dann durch diese Last - und hat ein Bauteil weniger zu verbasteln.<br />
Das RC-Glied (links unten) bietet sich an, wenn man einen Tiefpassfilter mit hoher Eingangsimpedanz benötigt. Das ist beispielsweise der Fall, wenn man eine Referenzspannung glätten möchte.<br />
Das RL-Glied bietet sich meines Wissens überhaupt nicht an :D, da für die im Hobbybereich gängigen Frequenzen entweder ein sehr kleiner Widerstand R oder eine sehr große Spule nötig würde. Lösung 1 hat dann einen extrem kleinen Eingangswiderstand, Lösung 2 verbraucht massig Platz und ist teuer.<br><br />
Die Berechnungen sind, egal ob Hochpass oder Tiefpass, jeweils identisch. Bei einer Spule als frequenzabhängiges Bauteil gilt:<br />
<br />
<math>L = \frac{R}{2\pi*f_g}</math><br />
<br />
Wird dagegen ein Kondensator benutzt, gilt:<br />
<br />
<math>C = \frac{1}{2\pi*f_g*R}</math><br />
<br />
=== Bandpass ===<br />
<br />
[[Bild:Bandpass-Schaltung.gif|thumb|Die Schaltung für einen Bandpassfilter]]<br />
Bei diesen Arten von Filtern ist die Berechnung einen Tacken komplizierter. Bandpass und Bandsperre sind mindestens Filter 2. Ordnung. Im Grunde haben diese Filter eine obere und eine untere Grenzfrequenz, so dass verschieden große Frequenzbereiche beeinflusst werden können.<br />
Am einfachsten ist es dann, zwei einzelne Filter zu dimensionieren, nämlich einen Hochpass und einen Tiefpass und diese dann zu einem Filter zusammenzufassen. Aufgrund von Resonanz ü.ä. entspricht der entstehende Frequenzgang nicht dem, der entstehen würde, wenn man einfach die Dämpfungen der beiden Filter zusammenrechnet. Solange der Filter ein ganzes Frequenzband durchlassen soll, macht dies aber in den seltensten Fällen einen Unterschied. Wenn aber nur eine Frequenz durchgelassen werden soll, unterscheidet sich der Frequenzgang stark von dem der Einzelnen Filter. Es gibt dann eine Resonanzfrequenz, bei der die Dämpfung extrem gering wird. Wenn man sich von der Resonanzfrequenz wegbewegt, steigt die Dämpfung erst sehr schnell an, und geht schließlich in einen Abfall um 20dB / Dekade (Spannung proportional oder antiproportional zur Frequenz) über.<br><br />
Ein Problem gibt es aber dabei: Wenn die Abweichungen der Bauteile zu groß sind (und Kondensatoren sowie Spulen haben nunmal meistens große Toleranzen), wird die Frequenz möglicherweise nicht getroffen, und der Filter macht nicht das was er soll. Abhilfe schafft entweder die Verwendung von Bauteilen mit geringen Toleranzen (teuer!) oder eine Abgleich durch eine variable Kapazität oder Induktivität. Alternativ kann teilweise ein Filter höherer Ordnung mit einem etwas größerem des Frequenzband genutzt werden. Das gleiche gilt übrigens bei Bandsperren!<br />
<br />
[[Bild:bandpass-Ergebnis.gif|thumb|Das Ergebnis der Berechnung: Die Grenzfrequenzen stimmen mit den erwarteten mit ausreichender Genauigkeit überein.]]<br />
Ein Beispiel zur Berechnung: Nehmen wir an, wir brauchen einen Bandpass, der das hörbare Frequenzband von 20 Hertz bis 20 Kilohertz durchlässt. Diese Frequenzen sollten dann zugleich die Grenzfrequenzen des Filters sein. Wir berechnen dann einen Hochpassfilter, um Frequenzen unter 20 Hertz zu sperren, und einen Tiefpass, der Frequenzen über 20 Kilohertz sperren würde. Da ein Lastwiderstand, ein Breitbandlautsprecher mit einer Impedanz von 8 Ohm, gegeben ist, benutzen wir die oberen Aufbauten aus der obigen Grafik. Wir erhalten für die Induktivität <math>L \approx 63\mu H</math> und für den Kondensator <math>C \approx 1000\mu F</math>.<br />
<br />
Die Schaltung wird in dem Bild oben rechts gezeigt, die beiden Filter werden sozusagen hintereinandergeschaltet.<br><br />
Das Ergebnis ist in dem Bild darunter dargestellt: Die Grenzfrequenzen liegen mit ausreichender Genauigkeit bei den erwarteten Frequenzen von 20 Hertz und 20 Kilohertz.<br />
<br />
<br />
<br />
=== Passiv, 2. Ordnung ===<br />
<br />
Bei passiven Filtern zweiter Ordnung sind ein paar Schritte mehr notwendig. Zuerst einmal muss man wissen, welche Güte der Filter haben soll. Diese beschreibt die Schwingfähigkeit des Filters. Im Audiobereich nutzt man Filter mit möglichst geringen Güten und dadurch schwacher Schwingneigung. Wenn aber eine hohe Trennschärfe gefordert ist, kann man diese durch Erhöhung der Güte verbessern.<br />
Bei Filtern mit hoher Güte sinkt die Dämpfung beim Übergang zum Sperrbereich des Filters erst ab, in einem bestimmten Bereich wird sie sogar kleiner als 1. Das heißt das Signal wird sogar noch verstärkt. Danach steigt die Dämpfung relativ schnell an.<br />
Bei einer Güte, die kleiner ist als die Wurzel aus 1/2 (ca. 0,707) tritt dieses sogenannte Überschwingen des Frequenzgangs nicht auf. Diese Güte ist auch die gebräuchlichste, sie ist meistens ein guter Kompromiss. Filter mit dieser Güte haben eine Butterworth-Charakteristik.<br />
Daneben gibt es noch andere: Ein Filter mit Tschebyshev-Charakteristik ist jeder Filter mit einer Güte, die größer ist als die Wurzel aus 1/2. Bei diesen Filtern tritt besagtes Überschwingen auf, je höher die Güte, desto stärker ist das Überschwingen.<br />
Ein Filter mit Bessel-Charakteristik hat eine Güte von Wurzel aus 1/3. Die Trennschärfe bei diesen Filtern ist schlechter als bei Filtern mit Butterworth-Charakteristik, aber Signale, deren Frequenzanteile im Durchlassbereich liegen, werden nicht so stark verzerrt. Das ist auch der Grund, warum dieser Filter in Audioanwendungen die beste Qualität liefert.<br />
<br />
==== Bandsperre ====<br />
<br />
[[Bild:Bandsperre-Schaltung.gif|thumb|Die Schaltung für eine Bandsperre]]<br />
Bei einer Bandsperre verfährt man fast genauso. Man berechnet einen Tiefpassfilter für die untere Grenzfrequenz und einen Hochpassfilter für die obere Grenzfrequenz. Die Frequenzanteile, die dann zwischen den Grenzfrequenzen liegen, werden mehr oder weniger stark gesperrt, je nachdem, wie weit sie von den Grenzfrequenzen entfernt sind. In der geometrischen Mitte der beiden Frequenzen tritt eine Resonanz auf, die Sperrwirkung des Filters ist dann fast unendlich groß, sofern man ideale Bauteile hat. <br />
<br />
Bei einer Bandsperre werden die beiden frequenzabhängigen Bauteile parallel geschaltet. Die Schaltung sieht dann so aus wie in der nebenstehenden Abbildung.<br />
<br />
[[Bild:2T-notch.png|thumb| 2 T Bandsperre]]<br />
Für die Bandsperre gibt es noch eine zweite Möglichkeit, ohne Induktivität. Damit ist diese Schaltung gut als Filter für niedrige Frequenzen wie z.B. 50 Hz oder 100 Hz geeignet. Die erreichbare Sperrwirkung des Filters hängt davon ab, wie gut das Verhältnis der Werte stimmt.<br />
<br />
== Aktiv ==<br />
Bei relativ niedriger Frequenz, wie im Audiobereich sind Induktivitäten ziemlich unhandliche Bauteile und alles andere als ideal. Mit Hilfe von Verstärkern lassen sich auch Filter mit höherer Güte (Q > 0.7) ohne Induktivitäten, nur mit Widerständen, Kondensatoren und halt dem Verstärker (heute in der Regel ein [[Operationsverstärker]]) aufbauen.<br />
<br />
Die Filter 1. Ordnung sind oft auch weiter passive Filter mit einem nachgeschalteten Verstärker. Eine Ausnahme sind hier die Extremfälle Integrator und Differentiator, und ein Allpassfilter (Phasenschieber).<br />
<br />
Die typischen aktiven Filterschaltungen nutzen einen Operationsverstärker für einen Filter 2. Ordnung. Dabei gibt es mehrere Möglichkeiten der Realisierung. Zur Berechnung der passenden Widerstandswerte und Kapazitäten gibt es spezielle Programme zum Filter-Design (siehe Weblinks). Damit kann dann auch gleich der Frequenzgang im voraus berechnet werden. <br />
<br />
=== Multiple-Feedback Filter ===<br />
[[Bild:MultiFB_LP.png|thumb| Multi-Feedback Tiefpass]]<br />
<br />
[[Bild:MultiFB_BP.png|thumb| Multi-Feedback Bandpass]]<br />
<br />
Diese Schaltungsform ist vor allem angebracht für Filter mit hohem Q. Im Durchlassbereich wird das Signal invertiert. Der Hochpassfilter entsteht einfach aus dem Tiefpass indem man die Widerstände und Kondensatoren vertauscht. <br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
.<br />
<br />
=== Sallen-Key Filter ===<br />
[[Bild:Sallenkey_LP.png|thumb| Sallen-key Tiefpass]]<br />
<br />
<br />
[[Bild:Sallenkey_HP.png|thumb| Sallen-key Hochpass]]<br />
<br />
<br />
[[Bild:Sallenkey_BP.png|thumb| Sallen-key Bandpass]]<br />
<br />
Diese Schaltung ist vor allem angebracht für Filter mit niedrigem Q ( < 2). Im Durchlassbereich wird das Signal nicht invertiert und in der Regel auch nicht verstärkt. Bei der Wahl der Kapazitäten und Widerstände gibt es mehrere Möglichkeiten. Der einfache Fall mit 2 gleichen Widerständen und 2 gleiche Kondensatoren gibt für den Hochpass und Tiefpass eine Grenzfrequenz von 0,16 / (RC) und eine Güte von 0,5.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
.<br />
<br />
=== weitere Filterschaltungen ===<br />
Auch bei normalen Verstärkerschaltungen wird oft die Bandbreite nach oben und unten auf den nötigen Bereich begrenzt. Die Filterfunktion ist dabei eher Nebensache und eine Kombination aus einem Hochpass und Tiefpass jeweils 1. Ordnung genügt.<br />
<br />
<br />
=== Filter mit geschalteten Kondensatoren (switched capacitor filter) ===<br />
Um die Frequenz eines aktiven Filters höherer Ordnung zu verstellen muss man oft mehrere Widerstände synchron verstellen. Neben der relativ neuen Möglichkeit über elektronische Potis gibt es die alternative Widerstände durch kleine Ladungspumpen zu ersetzen. Statt eines kontinuierlichen Stromes wird die Ladung in einem Kondensator und elektronischen Schaltern in kleinen Portionen transportiert. Für niedrige Frequenzen verhält sich das dann wie ein Widerstand, umgekehrt proportional zur relativ hohe Umschaltfrequenz. Man kann so über die Umschaltfrequenz (z.B. 1 MHz) die Grenzfrequenz (z.B. 1 kHz) eines aktiven Filters, auch höherer Ordnung verstellen. Für diese Anwendung gibt es spezielle ICs.<br />
<br />
= Weblinks =<br />
*[http://focus.ti.com/docs/toolsw/folders/print/filterpro.html Ti Filter Pro Software für Aktive Filter]<br />
*[http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en010007 Microchip Software für Aktive Filter]<br />
*[http://www.linear.com/designtools/software/#Filter LT Software für Aktive Filter]<br />
*[http://elektronikbasteln.pl7.de/rfsim99-filter-berechnung.html RFSim99 Programm für Berechnung von HF- und NF-Filtern]<br />
[[Category:Elektronik]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Filter_(Elektronik)&diff=23375Filter (Elektronik)2013-12-09T10:32:40Z<p>Besserwessi: Verschoben Bandsperre ist imme wenigstens 2. Ordnung</p>
<hr />
<div>= Wozu benutzt man Filter? =<br />
<br />
Für Filter gibt es eine ganze Menge Anwendungen.<br />
<br />
Als erstes Beispiel nehmen wir uns mal einen Verstärker her, einen einfachen. Einen Transistor in Emitterschaltung zum Beispiel. Am Eingang dieser Schaltung liegt die Basis des Transistors, beschaltet mit einem Vorwiderstand von Vcc aus. Dieser dient dazu, den Arbeitspunkt des Transistors festzulegen und "zieht" die Basis des Transistors zum Beispiel auf 0,70 Volt.<br />
Was passiert wenn man nun ein Signal anlegt, beispielsweise ein Audiosignal, welches zwischen 0,1 Volt und -0,1 Volt schwingt? Die Basis wird auf eben diese (im Mittel) 0 Volt heruntergezogen, und der Strom, der eigentlich für die Basis vorgesehen war, fließt nun einfach in die Signalquelle ab.<br><br />
Hier schafft ein Entkopplungskondensator, den man zwischen Signalquelle und Basis des Transistors schaltet, Abhilfe: Die Spannung zwischen dessen Anschlüssen steigt durch den abfließenden Strom langsam an. Die Spannung an der zum Transistor gewandten Seite steigt also an, und irgendwann ist die Spannung soweit gestiegen, dass durch die Basis des Transistors wieder der entsprechende Strom fließen kann. Durch den Kondensator fließt dann kein Gleichstrom mehr.<br />
Die Wechselspannung, in diesem Fall das Audiosignal, wird dagegen durchgelassen. Dabei fließt nämlich während der positiven Halbwelle Strom in die Schaltung herein, bei der negativen Halbwelle des Signals dagegen fließt der Strom wieder heraus. Diese Strompulse sind so kurz, dass der Kondensator seine Spannung dabei kaum ändert, sie werden also fast ungehindert durchgelassen.<br />
<br />
Als zweites Beispiel nehmen wir uns nochmal Audio-Kram her: Wir haben nen Tieftöner, den wir an unsere Stereoanlage anschließen wollen. Die tiefen Frequenzen soll er ruhig wiedergeben - die hohen Frequenzen jedoch nicht, denn für die sind die Hochtöner zuständig.<br><br />
Eine Spule, die hier in Reihe zum Lautsprecher geschaltet wird, erfüllt diese Bedingungen. Wenn ein Signal an die Kombination aus Lautsprecher und Spule angelegt wird, steigt der Strom, bedingt durch die Induktivität der Spule, nur langsam an. Bei tiefen Frequenzen fällt das kaum auf, denn eine Halbwelle ist lang genug, um den Strom durch den Lautsprecher und die Spule zu erhöhen. Bei hohen Frequenzen dagegen sind die Halbwellen so kurz, dass sich kein nennenswerter Stromfluss einstellen kann - die Signale werden von der Spule blockiert.<br />
<br />
Zur Glättung von Spannungen und / oder Strömen werden auch Filter eingesetzt. Eine Kombination aus Spule und Kondensator (LC-Filter) setzt man beispielsweise häufig vor der ADC-Versorgung von Mikrocontrollern ein. Diese Filter filtern doppelt: Wenn die Versorgungsspannung plötzlich leicht abfällt, ändert sich zunächst der Strom in der Spule, aber wie gesagt wurde, durch die Induktivität verlangsamt. Wenn der Strom etwas abgefallen ist, wird das zweite Filterelement - der Kondensator - aktiv, denn durch ihn muss zuerst ein Strom fließen, damit sich die Spannung am Ausgang der Schaltung ändern kann.<br />
<br />
----<br />
<br />
*Phantomspeisung (gecancelt: Gebräuchliche Systeme wie PoE brauchen dafür afaik keine echten Filter.)<br />
<br />
= Arten von Filtern =<br />
<br />
Nun gibt es aber zig verschiedene Möglichkeiten, Filter aufzubauen, noch dazu ist jede nur für spezielle Anwendungen geeignet.<br><br />
Man unterteilt Filter zunächst in aktive Filter und passive Filter: Passive Filter sind solche, die nur aus passiven Bauteilen bestehen. Dazu gehören die oben angesprochenen Kondensatoren, Spulen und Widerstände, aber auch Quarze.<br />
Aktive Filter enthalten als aktive Komponente meist einen Operationsverstärker. Der Vorteil liegt auf der Hand: Der Ausgang des OpAmps ist auch der Ausgang des Filters, der damit auch ruhig bis einige Milliampere belastet werden kann - bei passiven Filtern ist dies meistens nicht ohne weiteres möglich, die Ausgangsspannung würde sich dabei ändern. Der Nachteil sollte auch klar sein: Ein OpAmp, und jede andere aktive Komponente auch, braucht ne Versorgungsspannung. Für eine Frequenzweiche in einer passiven Standbox (d.h. der Verstärker ist nicht integriert) sind aktive Filter dadurch ungeeignet.<br />
<br />
[[Bild:Filterordnungen.gif|thumb|Beispiele für verschiedene Filterordnungen]]<br />
Weiterhin unterscheidet man Filter nach verschiedenen Ordnungen. Die Ordnung spiegelt dabei die Anzahl der frequenzabhängigen Bauteile wieder. Ein Filter höherer Ordnung, beispielsweise die oben beschriebene Kombination aus Spule und Kondensator, hat eine bessere Trennschärfe. Das bedeutet, dass der Abstand zwischen Frequenzen, die durchgelassen werden, und Frequenzen, die gesperrt werden, kleiner ist.<br><br />
Nun gut, im Bild rechts sieht man schon dass das so nicht ganz stimmt: Jede Frequenz wird noch irgendwie durchgelassen, aber die Dämpfung, die in dB gemessen wird, wird immer stärker. Man müsste also eigentlich sagen, dass der Abstand der Frequenzen, die als durchgelassen '''bezeichnet''' werden können und der Frequenzen, die als gesperrt '''bezeichnet''' werden können, kleiner ist. Ab welcher Dämpfung eine Frequenz als gesperrt oder durchgelassen bezeichnet werden kann, das hängt wiederrum von der Anwendung ab. In Audioanwendungen reicht ein Filter 1. Ordnung meist völlig aus, aber um beispielsweise ein PWM-Signal in eine Gleichspannung zu wandelt, muss eine bestimmte Frequenz unbedingt möglichst schwach gedämpft durchgelassen werden, während die PWM-Frequenz möglichst stark gedämpft werden soll. Deshalb nutzt man hierfür vorwiegend Filter höherer Ordnungen.<br />
<br />
[[Bild:Filtervergleich.gif|thumb|Beispiele für verschiedene Filtercharakteristiken]]<br />
Zuletzt unterteilt man Filter noch in verschiedene Charakteristiken, namentlich Tiefpässe, Hochpässe, Bandpässe und Bandsperren.<br />
*Tiefpässe lassen tiefe Frequenzen durch und sperren hohe Frequenzen. (siehe blaue Kurve im Bild.) Eine typische Anwendung wäre die Glättung eines Signals mithilfe einer Kombination aus Spule und Kondensator. <br />
*Hochpässe machen das Gegenteil: Sie sperren tiefe Frequenzen und lassen hohe Frequenzen durch. (siehe grüne Kurve im Bild.) Beispiele wären eine Gleichspannungsentkopplung oder eine Frequenzweiche für einen Hochtonlautsprecher.<br />
*Bandpässe sperren hohe und tiefe Frequenzen - Ein bestimmter Frequenzbereich wird jedoch durchgelassen. (siehe rote Kurve im Bild.) Eine Anwendung hierfür wäre eine Frequenzweiche vor einem Mitteltöner '''''(Sorry für die ganzen Beispiele aus dem Audiobereich, fällt vielleicht wem was besseres ein? Passiv-PFC würde mir einfallen, würde aber ne längere Erläuterung notwendig machen.)'''''<br />
*Bandsperren sind das Gegenstück zu den Bandpässen: Hohe und tiefe Frequenzen werden durchgelassen, ein bestimmter Frequenzbereich wird gesperrt. (siehe türkise Kurve im Bild.) Eine solche Schaltung kann beispielsweise verwendet werden, um die möglicherweise bei Datenübertragungen über Modem störenden Gebührenimpulse aus der Telefonleitung herauszufiltern.<br />
<br />
Wie man sieht, sind die Bezeichnungen so gut wie selbsterklärend.<br />
<br />
= Realisierung =<br />
<br />
Wenn man einen Filter für eine bestimmte Anwendung berechnen will, braucht man folgende Informationen:<br />
* Die Grenzfrequenz. Das ist die Frequenz, ab der die Ausgangsspannung gegenüber der Eingangsspannung um 3dB abgeschwächt wird - das entspricht in etwa einer Abschwächum um den Faktor 1,414, genauer um <math>\sqrt{2}</math><br />
* Die Ordnung des Filters. Diese ergibt sich aus den Anforderungen in der Anwendung.<br />
* Ob der Filter aktiv oder passiv ausgeführt werden soll<br />
<br />
Alle Arten von Filtern, also solche 1. Ordnung, 2. Ordnung, Tiefpässe, Hochpässe etc. lassen sich sowohl Passiv als auch Aktiv aufbauen. Filter höherer als 2. Ordnung lassen sich immer als Verknüpfung von Filtern 1. und 2. Ordnung realisieren (bei passiven Filtern in der Regel mit einem Verstärker dazwischen). <br />
Beginnen wir zunächst mit den passiven Filtern:<br />
<br />
== Passiv ==<br />
<br />
=== 1. Ordnung, Tiefpass und Hochpass ===<br />
<br />
Für Tief- und Hochpässe 1. Ordnung gibt es zwei Möglichkeiten: Entweder man schaltet das frequenzabhängige Bauteil vor einen Lastwiderstand (beispielsweise einen Lautsprecher), oder man schaltet den Widerstand vor das frequenzabhängige Bauteil. Wie das dann aussieht, zeigt die folgende Abbildung:<br />
<br />
[[Bild:Filter 1.Ordnung.gif]]<br />
<br />
Ue ist die Eingangsspannung, vor dem Filter, und Ua ist die Spannung, die am Ausgang der Schaltung anliegt um weiterverarbeitet zu werden. Die obigen Aufbauten, mit dem frequenzabhängigen Bauteil vor dem Lastwiderstand, bieten sich an, wenn man bereits einen Lastwiderstand - beispielsweise einen Lautsprecher - gegeben hat. Den Widerstand R ersetzt man dann durch diese Last - und hat ein Bauteil weniger zu verbasteln.<br />
Das RC-Glied (links unten) bietet sich an, wenn man einen Tiefpassfilter mit hoher Eingangsimpedanz benötigt. Das ist beispielsweise der Fall, wenn man eine Referenzspannung glätten möchte.<br />
Das RL-Glied bietet sich meines Wissens überhaupt nicht an :D, da für die im Hobbybereich gängigen Frequenzen entweder ein sehr kleiner Widerstand R oder eine sehr große Spule nötig würde. Lösung 1 hat dann einen extrem kleinen Eingangswiderstand, Lösung 2 verbraucht massig Platz und ist teuer.<br><br />
Die Berechnungen sind, egal ob Hochpass oder Tiefpass, jeweils identisch. Bei einer Spule als frequenzabhängiges Bauteil gilt:<br />
<br />
<math>L = \frac{R}{2\pi*f_g}</math><br />
<br />
Wird dagegen ein Kondensator benutzt, gilt:<br />
<br />
<math>C = \frac{1}{2\pi*f_g*R}</math><br />
<br />
=== Bandpass ===<br />
<br />
[[Bild:Bandpass-Schaltung.gif|thumb|Die Schaltung für einen Bandpassfilter]]<br />
Bei diesen Arten von Filtern ist die Berechnung einen Tacken komplizierter. Bandpass und Bandsperre sind mindestens Filter 2. Ordnung. Im Grunde haben diese Filter eine obere und eine untere Grenzfrequenz, so dass verschieden große Frequenzbereiche beeinflusst werden können.<br />
Am einfachsten ist es dann, zwei einzelne Filter zu dimensionieren, nämlich einen Hochpass und einen Tiefpass und diese dann zu einem Filter zusammenzufassen. Aufgrund von Resonanz ü.ä. entspricht der entstehende Frequenzgang nicht dem, der entstehen würde, wenn man einfach die Dämpfungen der beiden Filter zusammenrechnet. Solange der Filter ein ganzes Frequenzband durchlassen soll, macht dies aber in den seltensten Fällen einen Unterschied. Wenn aber nur eine Frequenz durchgelassen werden soll, unterscheidet sich der Frequenzgang stark von dem der Einzelnen Filter. Es gibt dann eine Resonanzfrequenz, bei der die Dämpfung extrem gering wird. Wenn man sich von der Resonanzfrequenz wegbewegt, steigt die Dämpfung erst sehr schnell an, und geht schließlich in einen Abfall um 20dB / Dekade (Spannung proportional oder antiproportional zur Frequenz) über.<br><br />
Ein Problem gibt es aber dabei: Wenn die Abweichungen der Bauteile zu groß sind (und Kondensatoren sowie Spulen haben nunmal meistens große Toleranzen), wird die Frequenz möglicherweise nicht getroffen, und der Filter macht nicht das was er soll. Abhilfe schafft entweder die Verwendung von Bauteilen mit geringen Toleranzen (teuer!) oder eine Abgleich durch eine variable Kapazität oder Induktivität. Alternativ kann teilweise ein Filter höherer Ordnung mit einem etwas größerem des Frequenzband genutzt werden. Das gleiche gilt übrigens bei Bandsperren!<br />
<br />
[[Bild:bandpass-Ergebnis.gif|thumb|Das Ergebnis der Berechnung: Die Grenzfrequenzen stimmen mit den erwarteten mit ausreichender Genauigkeit überein.]]<br />
Ein Beispiel zur Berechnung: Nehmen wir an, wir brauchen einen Bandpass, der das hörbare Frequenzband von 20 Hertz bis 20 Kilohertz durchlässt. Diese Frequenzen sollten dann zugleich die Grenzfrequenzen des Filters sein. Wir berechnen dann einen Hochpassfilter, um Frequenzen unter 20 Hertz zu sperren, und einen Tiefpass, der Frequenzen über 20 Kilohertz sperren würde. Da ein Lastwiderstand, ein Breitbandlautsprecher mit einer Impedanz von 8 Ohm, gegeben ist, benutzen wir die oberen Aufbauten aus der obigen Grafik. Wir erhalten für die Induktivität <math>L \approx 63\mu H</math> und für den Kondensator <math>C \approx 1000\mu F</math>.<br />
<br />
Die Schaltung wird in dem Bild oben rechts gezeigt, die beiden Filter werden sozusagen hintereinandergeschaltet.<br><br />
Das Ergebnis ist in dem Bild darunter dargestellt: Die Grenzfrequenzen liegen mit ausreichender Genauigkeit bei den erwarteten Frequenzen von 20 Hertz und 20 Kilohertz.<br />
<br />
<br />
<br />
=== Passiv, 2. Ordnung ===<br />
<br />
Bei passiven Filtern zweiter Ordnung sind ein paar Schritte mehr notwendig. Zuerst einmal muss man wissen, welche Güte der Filter haben soll. Diese beschreibt die Schwingfähigkeit des Filters. Im Audiobereich nutzt man Filter mit möglichst geringen Güten und dadurch schwacher Schwingneigung. Wenn aber eine hohe Trennschärfe gefordert ist, kann man diese durch Erhöhung der Güte verbessern.<br />
Bei Filtern mit hoher Güte sinkt die Dämpfung beim Übergang zum Sperrbereich des Filters erst ab, in einem bestimmten Bereich wird sie sogar kleiner als 1. Das heißt das Signal wird sogar noch verstärkt. Danach steigt die Dämpfung relativ schnell an.<br />
Bei einer Güte, die kleiner ist als die Wurzel aus 1/2 (ca. 0,707) tritt dieses sogenannte Überschwingen des Frequenzgangs nicht auf. Diese Güte ist auch die gebräuchlichste, sie ist meistens ein guter Kompromiss. Filter mit dieser Güte haben eine Butterworth-Charakteristik.<br />
Daneben gibt es noch andere: Ein Filter mit Tschebyshev-Charakteristik ist jeder Filter mit einer Güte, die größer ist als die Wurzel aus 1/2. Bei diesen Filtern tritt besagtes Überschwingen auf, je höher die Güte, desto stärker ist das Überschwingen.<br />
Ein Filter mit Bessel-Charakteristik hat eine Güte von Wurzel aus 1/3. Die Trennschärfe bei diesen Filtern ist schlechter als bei Filtern mit Butterworth-Charakteristik, aber Signale, deren Frequenzanteile im Durchlassbereich liegen, werden nicht so stark verzerrt. Das ist auch der Grund, warum dieser Filter in Audioanwendungen die beste Qualität liefert.<br />
<br />
==== Bandsperre ====<br />
<br />
[[Bild:Bandsperre-Schaltung.gif|thumb|Die Schaltung für eine Bandsperre]]<br />
Bei einer Bandsperre verfährt man fast genauso. Man berechnet einen Tiefpassfilter für die untere Grenzfrequenz und einen Hochpassfilter für die obere Grenzfrequenz. Die Frequenzanteile, die dann zwischen den Grenzfrequenzen liegen, werden mehr oder weniger stark gesperrt, je nachdem, wie weit sie von den Grenzfrequenzen entfernt sind. In der geometrischen Mitte der beiden Frequenzen tritt eine Resonanz auf, die Sperrwirkung des Filters ist dann fast unendlich groß, sofern man ideale Bauteile hat. <br />
<br />
Bei einer Bandsperre werden die beiden frequenzabhängigen Bauteile parallel geschaltet. Die Schaltung sieht dann so aus wie in der nebenstehenden Abbildung.<br />
<br />
[[Bild:2T-notch.png|thumb| 2 T Bandsperre]]<br />
Für die Bandsperre gibt es noch eine zweite Möglichkeit, ohne Induktivität. Damit ist diese Schaltung gut als Filter für niedrige Frequenzen wie z.B. 50 Hz oder 100 Hz geeignet. Die erreichbare Sperrwirkung des Filters hängt davon ab, wie gut das Verhältnis der Werte stimmt.<br />
<br />
== Aktiv ==<br />
Bei relativ niedriger Frequenz, wie im Audiobereich sind Induktivitäten ziemlich unhandliche Bauteile und alles andere als ideal. Mit Hilfe von Verstärkern lassen sich auch Filter mit höherer Güte (Q > 0.7) ohne Induktivitäten, nur mit Widerständen, Kondensatoren und halt dem Verstärker (heute in der Regel ein [[Operationsverstärker]]) aufbauen.<br />
<br />
Die Filter 1. Ordnung sind oft auch weiter passive Filter mit einem nachgeschalteten Verstärker. Eine Ausnahme sind hier die Extremfälle Integrator und Differentiator, und ein Allpassfilter (Phasenschieber).<br />
<br />
Die typischen aktiven Filterschaltungen nutzen einen Operationsverstärker für einen Filter 2. Ordnung. Dabei gibt es mehrere Möglichkeiten der Realisierung. Zur Berechnung der passenden Widerstandswerte und Kapazitäten gibt es spezielle Programme zum Filter-Design (siehe Weblinks). Damit kann dann auch gleich der Frequenzgang im voraus berechnet werden. <br />
<br />
=== Multiple-Feedback Filter ===<br />
[[Bild:MultiFB_LP.png|thumb| Multi-Feedback Tiefpass]]<br />
<br />
[[Bild:MultiFB_BP.png|thumb| Multi-Feedback Bandpass]]<br />
<br />
Diese Schaltungsform ist vor allem angebracht für Filter mit hohem Q. Im Durchlassbereich wird das Signal invertiert. Der Hochpassfilter entsteht einfach aus dem Tiefpass indem man die Widerstände und Kondensatoren vertauscht. <br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
.<br />
<br />
=== Sallen-Key Filter ===<br />
[[Bild:Sallenkey_LP.png|thumb| Sallen-key Tiefpass]]<br />
<br />
<br />
[[Bild:Sallenkey_HP.png|thumb| Sallen-key Hochpass]]<br />
<br />
<br />
[[Bild:Sallenkey_BP.png|thumb| Sallen-key Bandpass]]<br />
<br />
Diese Schaltung ist vor allem angebracht für Filter mit niedrigem Q ( < 2). Im Durchlassbereich wird das Signal nicht invertiert und in der Regel auch nicht verstärkt. Bei der Wahl der Kapazitäten und Widerstände gibt es mehrere Möglichkeiten. Der einfache Fall mit 2 gleichen Widerständen und 2 gleiche Kondensatoren gibt für den Hochpass und Tiefpass eine Grenzfrequenz von 0,16 / (RC) und eine Güte von 0,5.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
.<br />
<br />
=== weitere Filterschaltungen ===<br />
Auch bei normalen Verstärkerschaltungen wird oft die Bandbreite nach oben und unten auf den nötigen Bereich begrenzt. Die Filterfunktion ist dabei eher Nebensache und eine Kombination aus einem Hochpass und Tiefpass jeweils 1. Ordnung genügt.<br />
<br />
<br />
=== Filter mit geschalteten Kondensatoren (switched capacitor filter) ===<br />
Um die Frequenz eines aktiven Filters höherer Ordnung zu verstellen muss man oft mehrere Widerstände synchron verstellen. Neben der relativ neuen Möglichkeit über elektronische Potis gibt es die alternative Widerstände durch kleine Ladungspumpen zu ersetzen. Statt eines kontinuierlichen Stromes wird die Ladung in einem Kondensator und elektronischen Schaltern in kleinen Portionen transportiert. Für niedrige Frequenzen verhält sich das dann wie ein Widerstand, umgekehrt proportional zur relativ hohe Umschaltfrequenz. Man kann so über die Umschaltfrequenz (z.B. 1 MHz) die Grenzfrequenz (z.B. 1 kHz) eines aktiven Filters, auch höherer Ordnung verstellen. Für diese Anwendung gibt es spezielle ICs.<br />
<br />
= Weblinks =<br />
*[http://focus.ti.com/docs/toolsw/folders/print/filterpro.html Ti Filter Pro Software für Aktive Filter]<br />
*[http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en010007 Microchip Software für Aktive Filter]<br />
*[http://www.linear.com/designtools/software/#Filter LT Software für Aktive Filter]<br />
*[http://elektronikbasteln.pl7.de/rfsim99-filter-berechnung.html RFSim99 Programm für Berechnung von HF- und NF-Filtern]<br />
[[Category:Elektronik]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Diode&diff=23157Diode2013-11-02T17:32:58Z<p>Besserwessi: /* Selengleichrichter */</p>
<hr />
<div>[[Bild:Div_Dioden.JPG|thumb|verschiedene Dioden]]<br />
Eine Diode hat die Eigenschaft, Strom nur in einer Richtung durchzulassen.<br />
Sehr häufig werden Dioden als sogenannte Gleichrichterdioden in einem Wechselstromkreis verwendet. Dadurch, dass Dioden Strom nur in eine Richtung durchlassen, wird am Ausgang ein pulsierenden Gleichstrom erzeugt, der dann mit Hilfe eines Elkos geglättet wird. Es gibt jedoch noch viele weitere Anwendungen in der Elektronik, bei denen es darauf ankommt, daß sehr kleine Steuerströme nur in eine Richtung fließen können. Dioden werden auch als Schutzdioden genutzt, um eine teure Schaltung (Controllerboards) vor der Zerstörung durch eine Verpolung zu schützen. <br />
<br />
Eine Diode besteht aus zwei Schichten, einem n-dotierten und einem p-dotierten Halbleitermaterial, die sich berühren. Aufgrund von Abstoßung bzw. Anziehung von gleichen Ladungen findet, abhängig von der Polung einer externen Spannungsquelle, entweder ein Stromfluß statt oder wird komplett unterbunden. Somit leitet eine Diode nur in eine Richtung. <br />
<br />
==Schaltzeichen==<br />
<br />
[[Bild:schaltzeichendiode.jpg]] [[Bild:Diodebeispiel.gif]]<br />
<br />
<br />
[[Bild:kennliniediode.jpg|Kennlinienverlauf von Germanium- und Silizium-Dioden]]<br />
<br />
<br />
<br />
== Diodentypen ==<br />
<br />
===Gleichrichterdiode===<br />
Gleichrichterdioden sind die "normale" Form der Dioden. Neben den langsamen Ausführungen für Netzfrequenz gibt es auch schnelle (fast, ultra fast) Ausführungen für Schaltnetzteile und ähnliche Anwendungen. Das schnell bei den Dioden bezieht sich dabei auf den Übergang vom leitenden in den sperrenden Zustand. Wenn die Spannung schnell von Vorwärts- in Sperrrichtung wechselt, kann bei langsamen Dioden noch einige Mikrosekunden (z.B. 5 µs) lang Strom in Sperrrichtung fließen. Beim Wechsel von sperrend nach leitend sind alle Dioden schnell.<br />
<br />
===Schottky Diode===<br />
[[Bild:Schaltsymbol_Schottkydiode.png|left|Schaltsymbol Schottkydiode]]<br />
Hier erfolgt die Sperrschichtbildung zwischen einem N-dotierten Siliziumkristall und einer Metallelektrode (Randschichttheorie nach W. Schottky, 1938). Kennzeichen des nach dem Planarverfahren hergestellten Metall-Halbleiterübergangs sind eine gegenüber Silizium niedrige Kniespannung (0,3V...0,4V), ein sehr scharfer Kennlinienknick in Durchlaß- und Sperrichtung, niedrige Sperrspannung, hohe Sperrströme, geringes Rauschen und extrem schnelle Schaltzeiten. Wegen der schnellen Schaltzeiten und geringen Durchlassspannung werden Schottkydioden häufig in Schaltnetzteilen eingesetzt.<br />
<br />
===Lawinen-Gleichrichterdiode===<br />
Im Gegensatz zu normalen Dioden darf die Durchbruchspannung U(BR) mit nichtperiodischen Verlustleistungsimpulsen überschritten werden, ohne daß damit die Lawinen-Gleichrichterdiode (Si-Diode mit kontrolliertem Durchbruchverhalten) zerstört wird.<br />
<br />
===Selengleichrichter===<br />
Die veralteten Selengleichrichter (polykristallin) haben im Vergleich zu Siliziumgleichrichtern größere Abmessungen und hohe Durchlaß- und Sperrverluste. Vorteilhaft ist eine höhere Überlastbarkeit und der Überlastschutz mit normalen flinken Sicherungen. Je Gleichrichterplatte werden bis zu 45 Volt Sperrspannung und Stromdichten bis zu 150mA/cm2 erreicht.<br />
<br />
===Zenerdiode===<br />
[[Bild:Schaltsymbol_Zenerdiode.png|left|Schaltsymbol Zenerdiode]]<br />
<br />
Wird die Sperrspannung einer Diode Überschritten, dann leitet die auch in Sperrrichtung; sie ''bricht durch''.<br />
<br />
Zener-Dioden sind Dioden, die für den Betrieb in Sperrichtung ausgelegt sind. Sie haben bei der sogenannten Zenerspannung eine sehr steile Kennlinie, die relativ temperaturstabil ist.<br />
Daher werden Zenerdioden meist zur Spannungsstabilisierung eingesetzt. Den niedrigsten Temperatukoeffizienten und den schärfsten<br />
Kennlinienknick haben Zenerdioden mit einer Sperrspannung zwischen 5V und ca 6V. Zenerdioden werden gefertigt für Spannungen von wenigen Volt bis ca. 200V. Für kleine Durchbruchsspannungen von z.B. 1.4V kann man zwei Si-Dioden in Reihe oder eine LED in Vorwärtsrichtung betreiben.<br />
<br />
===Suppressordiode===<br />
<br />
Suppressordioden sind spezielle Zenerdioden zum Schutz einer Schaltung vor Überspannung. Im Vergleich zu einer normalen Zenerdiode können sie kurzzeitig sehr hohe Ströme vertragen und haben dafür aber größere Tolleranzen, Temperaturabhängikteit und Kapazität und keinen so scharfen Knick in der Kennlinie. Es gibt auch Bipolare Ausführungen.<br />
<br />
===Lumineszenzdiode===<br />
[[Bild:Schaltsymbol_Lunineszenzdiode.png|left|Schaltsymbol Lunineszenzdiode (LED)]]<br />
Besser bekannt als "[[Leuchtdioden|Leuchtdiode]]" oder kurz LED. In Durchlassrichtung wird ein Teil der beim Passieren des pn-Übergangs freiwerdenden Energie in Form von Licht charakteristischer Wellenlänge frei.<br />
<br />
Die Vorwärtsspannung der Leuchtdiode ist um so größer, je höher die Energie der ausgesendeten Photonen ist. Sie ist am kleinsten für Infrarotdioden (IR-Diode, ca. 1.2V) und steigt von Rot (ca. 1.5-1.8V) über Gelb, Grün bis zu Blau/Weiss und Ultraviolett (UV-Diode, ca. 3-3.5V). Sie sind sensibel gegen Überstrom und Falschpolung, denn sie haben sehr geringe Sperrspannungen!<br />
<br />
Eine Leuchtdiode kann in gewissem Umfang also auch als Photodiode eingesetzt werden. Sie ist aber unempfindlich auf Licht längerer Wellenlängen.<br />
<br />
===Photodiode===<br />
[[Bild:Schaltsymbol_Photodiode.png|left|Schaltsymbol Photodiode (LED)]]<br />
Auf die pn-Schicht auftreffende Photonen oberhalb einer bestimmten Frequenz trennen dort Ladungsträger, was zu einer intensitätsabhängigen Spannung führt, bzw. die Photodiode wird zu einer Stromquelle. Auch Leuchtdioden sind in gewissem Umfang als Photodioden einsetzbar. Siehe auch [[Fotodiode]].<br />
<br />
<br style="clear:left"/><br />
<br />
===PIN-Diode===<br />
PIN - Dioden haben zwischen der P und N Schicht eine nicht oder sehr schwach dotierte (intrinsische) Schicht. Dadurch ergibt sich eine relativ kleine Kapazität, hohe Spannungsfestigkeit und lange Ladungsträgerlebensdauer. PIN Dioden eignen sich als Schalter oder variable Impedanz im Hochfrequenz (ab etwa 1 MHz) Bereich. Ein relativ kleiner Gleichstrom in Durchflußrichtung macht die Diode für das HF-signal niederohmig. Photodioden und langsame Gleichrichterdioden für Hochspannung sind ebenfalls oft PIN Dioden.<br />
<br />
===Tunneldiode===<br />
[[Bild:Schaltsymbol_Tunneldiode.png|left|Schaltsymbol Tunneldiode]]<br />
<br />
Bei einer Tunneldiode ist die Dotierung der Halbleiterschichten so hoch, dass bereits bei kleiner Flusspannung ein Stromfluss stattfindet (Esaki-Strom). Mit wachsender Flussspannung verschwindet dieser Effekt, und die Kennlinie beginnt zu fallen. Für größere Spannungen nimmt die Kennlinie die gewohnte Form an. Eine Tunneldiode hat also in Durchlassrichtung ein lokales Leitfähigkeits-Maximum. Sie zeichnen sich also dadurch aus, daß sie für bestimmte Spannungen einen negativen differenziellen Widerstand haben, also gilt<br />
:<math><br />
\frac{\mathrm{d}I(U)}{\mathrm{d}U} < 0<br />
</math><br />
In dem Bereich ist die Kennlinie fallend, und nicht wie sonst üblich überall mit der Spannung steigend.<br />
<br />
Bei Sperrpolung tritt ein kräftiger Stromfluss auf, d.h. Tunneldioden haben keine Sperrfähigkeit<br />
<br />
===Backward-Diode===<br />
[[Bild:Schaltsymbol_Backwarddiode.png|left|Schaltsymbol Backwars-Diode]]<br />
<br />
Spezialfall der Tunneldiode, die kein Leitfähigkeitsmaximum in Flussrichtung aufweist und in Sperrichtung leitet. Backward-Dioden sind geeignet zum Gleichrichten von Spannungen im Millivolt-Bereich, wobei die dann in Flussreichtung sperren und in "Sperrichtung" leiten.<br />
<br />
<hr style="visibility:hidden; clear:both"/><br />
<br />
===Kapazitätsdiode===<br />
[[Bild:Schaltsymbol_Kapazitätsdiode.png|Schaltsymbol Kapazitätsdiode]]<br />
Bei allen Dioden ist die Kapazität in Sperrichtung abhängig von der Spannung. Durch Spannung in Sperrichtung verbreitert sich die Verarmungsschicht am PN Übergang und die Kapazität wird kleiner. Bei Kapazitätsdioden wird auf eine definierte Kapazität und Spannungsabhängigkeit geachtet. Die typische Anwendung sind Radioempfänger und Spannungsgesteuerte Oszillatoren.<br />
<br />
===Laser-Diode===<br />
Laser-Dioden sind im Prinzip spezielle,hochwertige LEDs. Bei niedrigen Strömen verhalten sich Laserdioden auch wie normale LEDs. Ab einer Schwellstromstärke erhöht sich Effizienz und es wird kohärentes, fast ideal einfarbiges (monochromatisches) Laser-Licht erzeugt. Bei den gewöhnlichen Laserdioden tritt an 2 gegenüberliegenden Seiten aus einem kleinen Bereich ein stark divergentes Lichtbündel aus. Das Licht der einen Seite wird oft von einer Photodiode im gleichen Gehäuse aufgefangen. Laser-dioden sind sehr empfindlich gegen elektrostatische Ladungen und zu hohe Ströme.<br />
Wegen der hohen Intensität und Leuchtdichte sind Laserdioden gefährlich für das Auge und entsprechende Sicherheitsvorschriften müssen eingehalten werden.<br />
<br />
===Röhrendiode===<br />
[[Bild:Schaltsymbol_Röhrendiode.png|left|Schaltsymbol Diode (Röhre)]]<br />
Die älteste Form der Diode. Eine Röhrendiode ist eine Elektronenröhre mit zwei Elektroden: Kathode und Anode. An der Kathode treten durch Glüh-, Photo oder Feldemmission Elektronen aus. Ist die Anode positiv gegenübder der Kathode, werden die Elektronen von der Kathode "abgesaugt" und es fliesst ein Strom. Ist die Anode negativ, fliesst kein Strom, da die Anode keine Elektroden emittieren kann.<br />
Die Kathode ist oft mit einem speziellen Material überzogen, das eine niedrige Austrittsenergie für Elektronen aufweist und zudem als Heizwendel ausgebildet. <br />
<br />
Bei Beleuchtung der Kathode können Ladungsträger durch den äusseren Photoeffekt gebildet werden und die Diode wird zu einer Stromquelle (Photodiode).<br />
<br />
== Schaltbeispiele ==<br />
<br />
===Freilaufdiode===<br />
[[Bild:Freilaufdiode.png|left]]<br />
Bei einer Freilaufdiode handelt es sich nicht um einen bestimmten Diodentyp, der Begriff bezeichnet vielmehr eine Diode, die wie gezeigt verschaltet ist. Damit der Strom durch die induktive Last (Motor, Relaisspule, etc) nach Abschalten der Spannung (Öffnen des Schalters) weiter fliessen kann bzw. Spannungsspitzen durch Induktion vermieden werden, wird antiparallel zur Stromflussrichtung durch die Last eine Diode angeschlossen. Die Freilaufdiode muß also wenigstens kurzzeitig den Laststrom verkraften. Wenn nicht nur gelegentlich, sondern schnell (z.B. für PWM)geschaltet wird, ist die Geschwindigkeit der Diode wichtig. Bei PWM-Schaltungen sind in der Regel nur schnelle Diode oder Shottkydioden als Freilaufdioden geeignet.<br />
<br />
Ist der Schalter geschlossen und wird die Last von aussen mit Energie versorgt, dann arbeitet die Diode in Sperrrichtung; sie muss also eine Sperrspannung haben, die mindestens so groß ist wie die maximale Spannung an der Last. Ist der Schalter offen, dann arbeitet die Diode in Vorwärtsrichtung und hält den Stromfluß durch die Last aufrecht und vermeidet dadurch auch induktive Spannungsspitzen, die ansonsten zur Zerstörung anderer Bauteile oder zu Problemen in der Schaltung führen könnten.<br />
<br />
<br style="clear:both"/><br />
<br />
==Siehe auch==<br />
* [[Leuchtdioden]]<br />
* [[Gleichrichter]]<br />
<br />
==Weblinks==<br />
* [http://geoastro.de/Quiz/Schaltung/Schaltung.html Dioden Lerntest]<br />
* [http://de.wikipedia.org/wiki/Diode Wikipedia]<br />
<br />
<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Grundlagen]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Operationsverst%C3%A4rker&diff=23156Operationsverstärker2013-11-02T17:21:19Z<p>Besserwessi: /* Symmetrische Differenzverstärker */</p>
<hr />
<div>== Operationsverstärker Grundschaltungen ==<br />
<br />
== Verstärker ==<br />
<br />
Häufig müssen Sensorsignale in der ersten Stufe der Verarbeitung verstärkt werden und bei Spannungen von Meßbrücken wird die verstärkte Differenzspannung als Spannung gegen Masse benötigt. Schaltungen mit Operationsvertärkern die diese Aufgabe erfüllen werden hier dargestellt. <br />
<br />
Operationsverstärker werden zunächst als ideale Operationsverstärker betrachtet, das heißt sie haben eine unendlich hohe Verstärkung. Die Ausgangsspannung ist damit um einen sehr großen Faktor größer als die Differenz der Eingangsspannungen. In Wirklichkeit liegt der Faktor immerhin bei 10<sup>5</sup> bis 10<sup>6</sup>. <br />
<br />
Wird der Ausgang über einen Widerstand auf den negativen Eingang zurückgekoppelt, dann bewirkt diese Gegenkopplung, dass die Differenzspannung an den Eingängen (Ue+ - Ue-) zu null wird und die Verstärkung der Schaltung aus Operationsverstärker und Gegenkopplung endlich wird. Solche Schaltungen haben dann eine sehr präzise Verstärkung deren Wert nur durch den Wert der Widerstände bestimmt ist. Für die Betrachtung von idealen Operationsverstärkern gilt außerdem, dass in die Eingänge des Operationsverstäkers kein Strom fließt und dass der Ausgang den Innenwiderstand null hat. <br />
<br />
<br />
[[Bild:OperationsverstaerkerBild1.gif.gif]] <br />
<br />
Bild 1 zeigt die Schaltung für positive Verstärkung Bild 2 die Schaltung für negative Verstärkung. <br />
<br />
Die Beiden Schaltungen haben die gleiche Konfiguration, es wird nur jeweils der andere Eingang an Masse geschaltet. Mit U1 am positiven Eingang und U2 am negativen Eingang wird in beiden Fällen für die Ausgangsspannung Ua, der in Gleichung 2 angegebene Wert, erreicht. <br />
Setzt man U1 oder U2 gleich 0, dann erhält man die Ausgangsspannung für den positiven und den negativen Verstärker.<br />
<br />
Allgemein:<br />
<br />
Ua = U1 ( 1 + R2 / R1 ) - U2 R2 / R1 <br />
<br />
<!-- Das funktioniert nicht mit allen Browsern, auskommentiert (SprinterSB) --><br />
<!--/div--><br />
<br />
<br />
<!-- Das funktioniert nicht mit allen Browsern, auskommentiert (SprinterSB) --><br />
<!--div style="border:2px solid #ffd700; margin-left:auto; margin-right:auto; padding:0.3em; text-align:left; max-width:20em;"--><br />
<br />
== Differenzverstärker ==<br />
<br />
Um die Differenz zwischen zwei Spannungen am Ausgang gegen Masse zu erhalten wird die Schaltung in Bild 2 um einen Spannungsteiler am + Eingang erweitert siehe Bild 3. Die Eingangsspannung am Spannungsteiler heißt nun U1 und die Spannung am +Eingang (wie auch am -Eingang) ist Ue.<br />
<br />
[[Bild:Operationsverst%C3%A4rkerBild3.gif]] <br />
<br />
<br />
'''Damit gilt für die Schaltung in Bild3:''' <br />
<br />
Ua = Ue + (Ue - U2) * R2 / R1<br />
<br />
Ua = Ue * (R1 + R2) / R2 - U2 * R2 / R1 <br />
<br />
mit Ue = U1 * R2 / (R1 + R2) vereinfacht sich der Ausdruck zu: <br />
<br />
'''Ua = (U1 - U2) * R2 / R1'''<br />
<br />
<br />
Das heißt, dass die Ausgangsspannung gerade die Differenz der Eingangsspannungen mal dem Widerstandsverhältnis R2/R1 ist. Für große Widerstandswerte ist die Schaltung in Bild 3 schon einsetzbar, bei hoher Verstärkung und kleinen Werten für R1 ist es besser, die Eingänge hochohmig zu machen. <br />
<br />
<br />
Es wäre vorteilhaft die Messspannungen direkt an die hochohmigen Operationsverstärker-Eingänge zu legen. Beim + Eingang ist es ja die geteilte Spannung U1 die am + Eingang anliegt. Legt man sie direkt, ohne Teiler an den + Eingang, und verstärkt die Spannung U2 um den gleichen Faktor durch einen Verstärker nach Bild 2, dann ergibt sich am Ausgang wieder die Differenz von U1-U2 verstärkt um den Faktor (R1+R2)/R1. <br />
<br />
'''Für R1=R2 ergibt sich damit für die Schaltung in Bild 4''' <br />
<br />
'''Ua = 2 * (U1 - U2)''' <br />
<br />
<!-- Das funktioniert nicht mit allen Browsern, auskommentiert (SprinterSB) --><br />
<!-- </div> --><br />
<br />
<br />
<!-- Das funktioniert nicht mit allen Browsern, auskommentiert (SprinterSB) --><br />
<!--div style="border:2px solid #ffd700; margin-left:auto; margin-right:auto; padding:0.3em; text-align:left; max-width:20em;"--><br />
<br />
== Differenzverstärker mit einstellbarer Verstärkung ==<br />
<br />
Die Differenzverstärkerschaltung mit vier gleichen Widerständen R2 ist sehr gut für die Realisierung einer präzisen Verstärkung geeignet. Zur Erhöhung der Verstärkung ist es von Vorteil, wenn der Wert der Verstärkung mit nur einem Widerstand eingestellt werden kann. Hierfür wird ein Widerstand mit dem Wert R1 zwischen den Minus-Eingängen der beiden Verstärker eingefügt. Die Schaltung entspricht dann der Anordnung in Bild 5, machmal wird sie auch in der Form von Bild 6 dargestellt. <br />
<br />
[[Bild:Operationsverst%C3%A4rkerBild5.gif]] <br />
<br />
Hier überbrückt der Widerstand R1 die beiden Widerständ am Ausgang des linken Operationsverstärkers. Diese Kombination aus drei Widerstanden kann man zur Berechnung der Verstärkung von einem Stern in ein Dreick umwandeln dann hat jeder der beiden Widertände die nicht mit dem Ausgang verbunden sind den Wert R1*R2/(R1+2R2). Daraus errechnet sich die Verstärkung zu:<br />
<br />
'''Ua = 2*(U1 -U2) * (R1 + R2) / R1''' <br />
<br />
Ein einfacher Ausdruck der nur von der Differenz der Eingangsspannugen abhängt und der mit Änderung von R1 in der Amplitude einstellbar ist.<br />
<br />
<!-- Das funktioniert nicht mit allen Browsern, auskommentiert (SprinterSB) --><br />
<!--/div--><br />
<br />
<!-- Das funktioniert nicht mit allen Browsern, auskommentiert (SprinterSB) --><br />
<!--div style="border:2px solid #ffd700; margin-left:auto; margin-right:auto; padding:0.3em; text-align:left; max-width:20em;"--><br />
<br />
== Symmetrische Differenzverstärker ==<br />
<br />
Nun ist die Schaltung fast perfekt, in einigen Fällen ist jedoch auch die Laufzeit der Signale wichtig und es fällt auf, dass die beiden Eingangsgrößen U1 und U2 unterschiedlich lange Wege durch die Schaltung nehmen. <br />
<br />
Um dies auszugeichen geht man auf die Schaltung in Bild 3 zurück und versieht beide Eingänge in gleicher Weise mit Verstärkern nach Bild 1 und erhält die Konfiguration in Bild 7. Um die vielen Widerständ nicht einzeln zu benennen wird hier eine aus dem Farbcode abgeleitete Bezeichnung verwendet R1 = braun und R2 = rot. <br />
<br />
[[Bild:Operationsverst%C3%A4rkerBild7.gif]] <br />
<br />
In der ersten Stufe darf man bei dieser Anordnung die Verstärkung nicht zu groß wählen damit sie nicht intern übersteuert wird. Soll beispielsweise die Spannug 5V mit 5.01V verglichen und das Ergebnis 100fach verstärkt werden, dann kann man schlecht in der ersten Stufe die Spannungen auf 500V und 501V verstärken. Man kann dann in der der ersten Stufe die Verstärkung auf 1 oder wie hier auf 2 beschränken und die Verstärkung in der zweiten Stufe realisieren. <br />
<br />
Besser ist, wenigsten einen Teil der benötigten Verstärkung schon in der ersten Stufe einzubringen. Das gelingt weitgehend ohne interne Übersteuerung, wenn man die Bezugspegel der ersten Stufe nicht auf Masse setzt, sondern wie in der Schaltung nach Bild 8 mit der Kopplung der beiden Bezugspegel über den Widerstand R1 miteinander verbindet. Der Bezugspegel ist dann der Mittelwert der beiden Engangsspannungen und der wirksame Widerstand bei jedem Verstärker ist 0,5 * R1. <br />
<br />
Im oben angeführten Beispiel mit 5V und 5,01V ist der Mittelwert gerade 5,005V. <br />
Damit erhält man mit (0,5 * R1 + R2) / (0,5 * R1) = 100 die Ausgangsspannungen 5,505V und 4,505V aus denen in der letzten Stufe die gewünschte Differenz von 1V gewonnen wird. <br />
Für die Schaltung in Bild 8 gilt:<br />
<br />
'''Ua = (U1 - U2) * (R1 + 2*R2) / R1'''<br />
<br />
Die Schaltung nach Bild 8 hat zusätzlich den Vorteil, einer verbesserten Gleichtaktunterdrückung, auch ohne die Verwendung extra präziser Widerstände. Die erste Verstärkungstufe verstärkt nämlich nur das Differenzsignal und nicht den Mittelwert (Gleichtaktsignal). Die Schaltung nach Bild 8 heißt auch Instrumentenverstärker und ist auch fertig (ggf. R1 extern) als IC zu bekommen.<br />
<br />
So gibt es beispielsweise für die Auswertung von Messbrückenschaltungen immerhin schon einmal 5 Differenzverstärker, die mit ihren unterschiedlichen Schaltungen und Darstellungsweisen immer wieder für Verblüffung sorgen können.<br />
<br />
== Weitere Anwendungen für Operationsverstärker ==<br />
<br />
Operationsverstärker lassen sich nicht nur als normale Verstärker nutzen. Weitere Anwendungen sind:<br />
* Aktive [[Filter_(Elektronik)|Filter]]schaltungen<br />
* Regler-Schaltungen (z.B. PID), an sich auch nur eine Art Filter<br />
** als Spezialfall Spannungsregler<br />
* Aktive Gleichrichter / Präzisionsgleichrichter für Messzwecke<br />
* [[Analog-Komparator|Komparator]], allerdings meist nicht so gut wie ein echter Komparator<br />
* Frequenzgenerator / Oszillator (Sinus / Rechteck / Dreieck)<br />
* Transimpedanzverstärker (Strom Spannungswandler) (z.B. für Fotodioden)<br />
<br />
== Stabilität in OP Schaltungen ==<br />
<br />
Eine der Schwierigkeiten bei Schaltungen mit Operationsverstärkern ist es sicherzustellen, dass der <br />
Verstärker nicht schwingt. Hier soll keine ausführliche Darstellung der Stabilitätsanalyse folgen, sondern nur eine kurze, vereinfachte und eher praxisorientierte Form.<br />
<br />
Der Operationsverstärker kann zu schwingen anfangen, wenn aus der gewollten Gegenkopplung eine Mitkopplung wird. 180 Grad Phasenverschiebung entsprechen einer Invertierung und machen gerade aus der Gegenkopplung eine Mitkopplung. Durch RC Glieder (oder mit Induktivitäten) können Phasenverschiebungen erzeugt werden. Daher muß auf die Phasenverschiebung in der Rückkopplung (in der Regel vom Ausgang zum invertierenden Eingang) geachtet werden. Um die Bandbreite zu begrenzen haben die Operationsverstärker schon von sich aus etwa 90 Grad Phasenverschiebung über einen großen Frequenzbereich. Problematisch ist vor allem, wenn die Rückkopplung zu spät kommt. Die Bandbreite (für Schleifenverstärkung von eins) des Operationsverstärkers gibt vor, bis zu welcher Frequenz keine größeren Phasenverschiebungen (in Richtung Verzögerung) auftreten dürfen. Daran sieht man schon, dass es leichter ist einen langsamen Operationsverstärker stabil zu kriegen, als einen schnellen. <br />
<br />
Schlecht für die Stabilität sind:<br />
<br />
- Tiefpass-charakter in der Rückkopplung: dies führt leicht zum Schwingen.<br />
<br />
- Kapazität gegen Masse am Ausgang des OPs: dies sorgt für eine Verzögerung des Ausgangssignals.<br />
<br />
- Kapazität gegen Masse am invertierenden Eingang: dies ergibt zusammen mit einem Rückkopplungswiderstand einen Tiefpass.<br />
<br />
- OP mit hoher Bandbreite: parasitäre Kapazitäten und Induktivitäten werden wichtiger.<br />
<br />
- hochohmige Rückkopplung ohne parallelen Kondensator<br />
<br />
- Verstärkung in der Rückkopplung: Gefahr von Verzögerungen und schon an sich schlecht, weil sich das Verstärkungs-Bandbreitenprodukt erhöht.<br />
<br />
- lange Leitungen: geben zusätzliche Kapazitäten und Induktivitäten (je schneller desto kleiner)<br />
<br />
- fehlender Entkoppelkondensator an der Versorgungsspannung, besonders bei schnellen OPs<br />
<br />
- niedrige Versorgungsspannung bei einigen OPs mit JFets (z.B. TL072)<br />
<br />
Von den Standardschaltungen mit OPs sind die folgenden etwas problematisch: Differenzierer, Hochpass, Transimpedanzverstärker.<br />
<br />
Um die die Stabilität zu verbessern, kann man gezielt für einen Hochpass-Character in der Rückkopplung sorgen, z.B. durch einen kleinen Kondensator vom Ausgang zum inv. Eingang. Dadurch verringert sich aber auch die Bandbreite der Schaltung. Wenn der Ausgang kapazitive Lasten treiben soll (z.B. lange Kabel) sollte ein Widerstand (z.B. 47 Ohm) vor die Last geschaltet werden. Je nach OP liegt die Grenze bei etwa 20pF (z.B. TLV271) bis 5 nF (z.B. LF356).<br />
<br />
Etwas gegen die Intuition sind Verstärkerschaltungen mit einer hohen Verstärkung für das Signal weniger schwingungsanfällig als solche mit einer kleinen Verstärkung (z.B. 1 beim Impedanzwandler). Einige OPs (z.B. OP37, LF357): sind speziell für Schaltungen mit einer Verstärkung von mindestens z.B. 5 gedacht - bei weniger Verstärkung muss man mit Schwingungen rechnen.<br />
<br />
Bei etwas komplizierteren Schaltungen kann eine Simulation (z.B. mit [[SwitcherCAD-Tutorial|LTSpice]]) sinnvoll sein. Dabei sollten auch parasitäre Kapazitäten mit berücksichtigt werden. Die Neigung zu Schwingungen kann im Zeitbereich als Überschwinger oder im Frequenzbereich als Resonanz (Maximum im Frequenzgang) erkannt werden.<br />
<br />
==Liste gängiger Typen von Operationsverstärkern==<br />
Die folgende Liste gibt eine Auswahl der wichtigsten Daten für einige Operationsverstärker. Das sind zum einen einige ältere und in Schaltplänen häufiger zu findende Typen (MC1458 , µA741, TL072, LM324), dann einige für Bots interessante Typen und auch ein paar eher exotische Typen für ungewöhnliche Anforderungen (z.B. OP177, AD8551, TCA0372, OPA355).<br />
<br />
----<br />
{| {{Blauetabelle}} style="text-align:center;"<br />
|+ im Aufbau http://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=257490&highlight=#257490<br />
|-<br />
|Typ||Vmin||Vmax||Voff||Voff||Vn||i in||di in||Gain||SR||BW||i out||Is||Rail in||Rail out||single||double||quad<br />
|-<br />
|.||V||V||mV||µV/°C||nV/Hz^,5||nA||nA||V/mV||V/µs||MHz||mA||mA||lo / hi||lo / hi||€||€||€<br />
|-<br />
|MC1458||10||36||2||.||30||80||20||15||0,5||1||20||1,5||2 / -2||1 / -1||.||0,18||.<br />
|-<br />
|µA741||?10||36||2||15||23||80||20||200||0,5||1,5||25||1,7||2 / -2||1 / -1||0,19||.||.<br />
|-<br />
|LM358||3||32||5||7||40||45||5||15||0,5||1||20/8||0,2||0 / -1,5||0 / -2||.||0,18||LM324<br />
|-<br />
|LM324||3||32||5||7||40||45||5||15||0,5||1||20/8||0,2||0 / -1,5||0 / -2||.||.||0,19<br />
|-<br />
|TL072||7||36||3||18||18||65p||5p||200||13||3||20||0,7||3 / 0||1,5/-1,5||0,27||0,28||0,29<br />
|-<br />
|TLC 272||3||16||1,1||1,8||25||0,6p||0,1p||27||4||2||30||0,7||-0,3/-0,8||(0.3)/-1,2||0,38||0,35||0,45<br />
|-<br />
|MC33078||10||36||0,15||2||4,5||300||25||300||7||9||30||2||2 / -2||1 / -1||.||0,35||0,99<br />
|-<br />
|OP 07||6||36||0,03||1,3||10||1,2||0,5||400||0,3||0,6||.||1||1 / -1||2 / -2||0,29||.||.<br />
|-<br />
|MCP6042||1,4||6||3||2||170||1p||1p||115dB||0,003||0,014||2-20||0,6µ||-0,3/0,3||0 / 0||x||0,71||1,35<br />
|-<br />
|MCP6002||1,8||5,5||7||2||28||1p||1p||400||0,6||1||6..20||0,1||-0,3/0,3||0 / 0||x||0,31||0,40<br />
|-<br />
|ICL7612||2||16||5||15||100||1p||0,5p||10||1,6||1,4||.||..1||-0,3/0,3||0 / 0||1,25||1,50||x<br />
|-<br />
|TS912||2,7||16||5||5||30||1p||1p||40||0,4||0,8||65||0,25||-0,2/0,2||0 / 0||.||0,93||1,20<br />
|-<br />
|OP177||5||44||4µ||0,03||10||1,5||0,3||12000||0,3||0,6||12||1,6||1 / -1||1 / -1||1,15||.||.<br />
|-<br />
|LTC1050||4,75||16||0,5µ||0,05||90||0,01||0,02||160dB||4||2,5||20/4||1||0/-1,7||0 / 0||2,95||.||.<br />
|-<br />
|AD8552||2,7||5,5||1µ||0,005||42||0,01||0,02||145dB||0,4||1,5||30/30||0,7||0 / 0||0 /0||1,90||3,20||x<br />
|-<br />
|TCA0372||5?||40||1||20|| 22 || 100|| 10||1 || 1,4||1,4 ||1000||2,5||0/-1||1 /-1||.||1,15||.<br />
|-<br />
|LMC6482||3||16||3,8||1,0||37||0,02p||0.01p||130dB||0,9||1,5||30||0,75||-0,3/0,3||0/0||.||0,99||1,95<br />
|-<br />
|OPA355||2,5||5,5||2||7,0||5,8||3-50p||?||>80dB||300||200||60||8,3||-0,1/-1,5||0,1/-0,2||€?||€?||€?(triple)<br />
|-<br />
|TS922||2,7||12||0,9||2||9||15||1||200||1,3||4||80||4,5||-0,2/0,2||0,1/-0,2||€?||€?||€?<br />
|-<br />
|}<br />
----<br />
<br />
;Erklärung der Spalten:<br />
Typ gibt die Bezeichnung für die 2-fach Version an, sofern verfügbar. Die 1-fach/4-fach Versionen unterscheiden sich meist in der letzten Ziffer.<br />
<br />
Vmin / Vmax sind die minimale und maximale Versorgungsspannung. Bei symmetrischer Versorgung die Differenz (V+) - (V-).<br />
<br />
Voff ist die Offsetspannung oder der Gleichspannungsfehler. Das ist die Gleichspannung die am Eingang anliegen muss, um den Ausgang auf eine mittlere Spannung zu bringen. Der Wert ist als typische obere Grenze zu verstehen, wobei positive oder negative Werte möglich sind. Dazu wird noch die typische Grenze der Temperaturabhängigkeit von Voff angegeben.<br />
<br />
Vn ist die Rauschspannungsdichte. Für Frequenzen unter etwa 1 kHz kann das Rauschen deutlich höher werden.<br />
<br />
i in ist der Bias Strom. Das ist der mittlere Eingangsstrom der beiden Eingänge. Die Werte geben nur die Größenordnung an und können stark von Exemplar zu Exemplar streuen. Außerdem ist der Bias Strom zu Teil (FET Eingänge) stark Temperaturabhängig.<br />
<br />
di in ist der Offsetstrom oder die Differenz der Eingangsströme der beiden Eingänge. Der Wert ist als typische obere Grenze zu verstehen. <br />
<br />
Gain ist die Verstärkung für niedrige Frequenzen (z.B. 1 Hz).<br />
<br />
SR ist die maximale Geschwindigkeit für Änderungen der Ausgangsspannung, engl. Slewrate. Bei FET Eingängen ist dafür ein relativ großes Eingangssignal nötig.<br />
<br />
BW ist die Bandbreite. Angeben ist die Frequenz bei der die Verstärkung bis auf 1 abfällt oder das Produkt aus Frequenz und Verstärkung bei mittleren Frequenzen. Die Frequenz des Nutzsignals sollte normalerweise mindestens um den Faktor 10 mal der Verstärkung der Schaltung niedriger liegen.<br />
<br />
i out ist der maximale Ausgangsstrom. Die meisten OPs sind zumindest kurzzeitig kurzschlussfest. Teils ist der Strom getrennt für das negative und positive Vorzeichen angegeben. <br />
<br />
Is ist der typische Stromverbauch pro Verstärker. Zum Teil ist der Stromverbrauch deutlich von der Spannung abhängig.<br />
<br />
Rail in ist der Eingangspannungsbereich oder Gleichtaktbereich. Angegeben ist für die untere und obere Grenze jeweils die Differenz zur negativen bzw. positiven Versorgungsspannung.<br />
<br />
Rail out ist der Ausgangspannungsbereich. Angegeben ist der Mindestabstand zur negativen und positiven Versorgungsspannung. Der Wert 0 kann natürlich nicht wirklich erreicht werden, aber die Spannung kann bei kleinem Strom (z.B. 10 µA) bis auf ein paar mV an die Versorgung heran.<br />
<br />
single/double/quad geben circa Preise für einfach / doppel / 4-fach Ausführungen an, soweit sie verfügbar sind.<br />
<br />
==Autor/en==<br />
* Manf<br />
<br />
==Weblinks==<br />
* [http://www.eetkorea.com/ARTICLES/2003SEP/A/2003SEP19_AMD_AN07.PDF Op Amp Circuit Collection] - National Semiconductor Application Note 31 mit weiteren OP-Schaltungen<br />
* [http://www.elektronik-kompendium.de/sites/bau/0209092.htm OP in DAS ELKO]<br />
* [http://www.mikrocontroller.net/articles/Operationsverst%C3%A4rker-Grundschaltungen mikrocontroller.net]<br />
* http://www2.fh-fulda.de/~pfisterer/mt/mt8.pdf<br />
* [http://www.elektronikwissen.net/opamp/9-opamp-wissen.html Praxishinweise und schwingende Operationsverstärker] <br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Grundlagen]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Leuchtdiode&diff=22798Leuchtdiode2013-07-06T15:29:33Z<p>Besserwessi: /* Leuchtdiode */</p>
<hr />
<div>= Leuchtdiode =<br />
[[Bild:SFH300.jpg|right|thumb|Standard Bauform]]Leuchtdioden (Lumineszenzdiode) oder auch LED abgekürzt (light emitting diodes) basieren auf Halbleiterverbindungen, die den Strom direkt in Licht umwandeln. Bezogen auf Größe, Effektivität, Haltbarkeit und Lebensdauer verhalten sich die Leuchtdioden zu konventionellen Glühlampen, wie Halbleiterdioden zu Röhrendioden. Sie werden die Beleuchtungstechnik in ähnlicher Weise verändern, wie die Halbleitertechnologie schon die Elektronik verändert hat.<br />
[[Bild:led.gif|right]]<br />
[[Bild:Schaltsymbol_Lunineszenzdiode.png|left|Schaltsymbol Lunineszenzdiode (LED)]]Ein großer Vorteil ist die hohe Leuchtkraft bei geringer Stromstärke. Daher werden LEDs fast in allen elektronischen Geräten zur Anzeige von Signalen/Zuständen genutzt. Gerade ist auch die neue weisse LED dabei sogar der Glühlampe etwas Konkurrenz zu machen.<br />
Gewöhnlich werden LED's mit maximal 20 mA betrieben. Bei der „SuperFlux LED“ und der „Luxeon“ von Lumileds ist der maximale Betriebsstrom höher, nämlich 70 mA und 350 mA. Dagegen gibt es auch Low-current Typen, die für 2mA ausgelegt sind. Weniger Strom ist kein Problem: die Helligkeit nimmt ungefähr proportional zum Strom ab. <br />
<br />
Der Wirkungsgrad und damit die Helligkeit der LEDs bei einem gegebenen Strom hängt stark vom Typ ab. Zwischen den einfachen Low-Cost Typen und extra hellen (effizienten) Typen kann ein Faktor 100 sein, selbst wenn man berücksichtigt, dass viele der hellen Typen das Licht stark bündeln und so nur nach vorne besonders hell erscheinen.<br />
<br />
Die Durchlassspannung hängt von der Bandlücke ab und damit von der Lichtfarbe. Die Betriebsspannungen betragen 1,6V bis 4V. Entsprechend der steilen Diodenkennline ist die Spannung nur wenig vom Strom abhängig.<br />
Wegen des hohen Dotierungsunterschieds an der Sperrschicht vertragen Leuchtdioden nur geringe Sperrspannung von ca. 5 V. Wenn eine LED also an der Kathode positive Spannung abbekommt muss eine Diode antiparallel eingesetzt werden.<br />
<br />
Es ist eine große Vielfalt von Bauformen lieferbar. Neben diversen Metall-/Glas-Gehäusen werden hauptsächlich Plastikbauformen eingesetzt. Hier setzt der Kunststoffkörper zum einen den Grenzwinkel der Totalreflexion an der Chipoberfläche herab und erhöht damit die aus dem Kristall austretende Strahlungsleistung, zum anderen wirkt die gekrümmte Oberfläche als Linse und bündelt die Strahlung in Achsrichtung. Sie sind problemlos in großen Stückzahlen zu fertigen.<br />
<br />
Je nach Verlötungsverfahren wird bei elektronischen Bauteilen zwischen den auf der Rückseite der Platine verlöteten und den SMD (Surface Mounted Device) Bauformen unterschieden. Auch LED werden in beiden Bauformen angeboten.<br />
<br />
Die meisten LEDs kann man nicht direkt an eine Spannungsquelle anschließen. Man stellt den Strom über eine Konstantstromquellen oder einen Vorwiderstand ein. <br />
<br />
'''Praxistip:''' An einer Spannung von 5V (auch Digitalports) werden Leuchtdioden gewöhnlich mit einem Widerstand von min. 330 Ohm bis 1000 Ohm (je nach gewünschter Leuchtstärke) betrieben.<br />
<br />
'''Berechnung des Vorwiderstandes:''' Zur Berechnung des Vorwiderstandes benutzt man das Ohmsche Gesetz. Von der Versorgungsspannung wird die Spannung der LED abgezogen und dann durch den gwünschten Strom geteilt.<br />
Beispiel: Eine LED soll 20mA bekommen und braucht 2V. Unsere Versorgungsspannung hat 9V. Setzt man das in die Formel ein bekommt man 350 Ohm herraus.<br />
Da es die meisten errechneten Widerstände nicht gibt nimmt man sicherheitshalber den nächst höheren.<br />
Beim Rechnen sollte man immer in den Grundeinheiten Ampere, Volt und Ohm bleiben.<br />
Man rechnet also nicht mit 20mA, sondern mit 0,02A. <br />
<br />
<br />
[[Bild:LED.jpg|thumb|center|Weiße LED]] <br />
<br />
<br />
==IR-LEDs==<br />
Neben den LEDs für sichbares Licht gibt es auch LEDs für Infrarotlicht. Die üblichen Wellenlängen sind 880 nm und 950 nm. Diese LEDs haben einen recht hohen Wirkungsgrad und Silizium Fotodioden sind hier besonders empfindlich. Entsprechend eignen sich die IR-LEDs gut für Lichtschranken und die Datenübertragung, z.B. für Fernsteuerungen. Die typischen IR LEDs vertragen bis 100 mA, wenn für eine einigermaßene Wärmeableitung gesorgt wird.<br />
<br />
<br />
'''Funktionstest von IR-LEDs:''' Infrarotlicht ist für Menschen nicht sichtbar. Daher ist der Funktionstest einer IR-LED ohne Hilfsmittel nicht möglich. Neben der Messung der Stromaufnahme ist eine praxiserprobte Möglichkeit das Messgerät Digitalkamera. Warum? Die meisten Digitalkameras "sehen" Infrarotlicht. Eine Ausnahme bilden z.T. hochpreisige Systemkameras, aber die übliche Digicams oder Handikameras sind geeignet. Es muss einfach bei - vermutetem - Leuchten der IR-LED auf das Kameradisplay geschaut werden.<br />
<br />
'''ACHTUNG, extremes Verletzungsrisiko = Erblindungsgefahr :''' Bitte keinesfalls direkt in die leuchtende Infrarotdiode sehen - egal ob man denkt, dass die leuchtet oder nicht. Das Licht kann nicht erkannt werden; wenn man etwas merkt, ist der Augenschaden schon da.<br />
<br />
<br />
[[Bild:Gp2d-060608_1297.jpg]]<br />
<br />
Die Infrarot-LED, links im Bauteil, leuchtet nicht.<br />
<br />
[[Bild:Gpd2d-060608_1503.jpg]]<br />
<br />
Die Infrarot-LED, links im Bauteil, leuchtet.<br />
<br />
<br />
Wie bei jedem indirektem Test bietet sich eine Gegenprobe an. Ist ein Leuchten nicht zu erkennen, kann es sein, dass Spannung gar nicht anliegt: Spannung prüfen oder Schalter in andere Position (oder Code ändern). Wenn beide Tests kein positives Ergebnis zeigen, macht ein Diodentest Sinn: wenn man den Minuspol (-) an die Kathode hält, siehe oben, kürzeres Bein, Marke "K" und den Pluspol (+) an die Anode, dann sollte das Instrument einen Wert um etwa 1 V anzeigen. Nach Wechsel der Anschlüsse ist der Durchgang gesperrt.<br />
<br />
= Parallelschalten von LEDs =<br />
Es kommt immer wieder die Frage auf, ob man LEDs parallel schalten kann: Die kurze Antwort ist besser nicht. <br />
<br />
Dafür gibt es 2 Gründe: Zum einen ist die Spannung der LEDs nicht immer hundertprozentig gleich, und schon etwa 20 mV Unterschied machen einen Faktor 2 beim Strom aus. <br />
Selbst wenn man gleiche Dioden ausgesucht hat, gibt es noch ein Temperaturproblem: Wie bei anderen Dioden auch, nimmt mit steigender Temperatur und konstanter Spannung der Strom relativ schnell zu. Schon bei etwa 5-10 Grad mehr kann sich der Strom verdoppeln. Wenn man jetzt LEDs parallel hat, steigt der Strom der wärmeren LED an und macht diese LED noch wärmer, bis praktisch der ganze Strom durch diese eine LEDs fließt und die LED dann eventuell schädigt.<br />
Ein ähnliches Problem hat man auch beim Parallelschalten von Gleichrichterdioden oder bipolaren Transistoren.<br />
<br />
Wenn man LEDs parallel betreiben will, muß man durch jeweils einen Serienwiderstand dafür sorgen, das der Strom durch den Widerstand, und weniger durch die recht steile Diodenkennlinie bestimmt wird. Der Spannungsabfall am Widerstand sollte dazu wenigstens 100-200 mV betragen.<br />
<br />
Bei den blauen und weißen LEDs gibt es einige Typen, die intern schon einen relativ hohen Serienwiderstand haben und deshalb unter günstigen Umständen direkt parallel geschaltet werden können. Ohne genaue Informationen aus dem Datenblatt sollte man das aber nicht machen und besser für jede LED eine Vorwiderstand spendieren.<br />
<br />
=Siehe auch=<br />
* [[Diode]]<br />
* [[Optokoppler]]<br />
* [[RN-Digi|7 Segment Anzeigen Bauanleitung]]<br />
<br />
<br />
=Weblinks=<br />
* [http://www.roboternetz.de/phpBB2/konstantstrom.php Konstantstromquelle mit LM317]<br />
* [http://www.mikrocontroller.net/articles/LED-Matrix Ansteuerung vieler LED als Matrix]<br />
* [http://www.ledshift.com/Lichtstaerke%20German.html Erklärung zur Lichtstärke ]<br />
* [http://de.wikipedia.org/wiki/Leuchtdiode LED bei Wikipedia]<br />
* [http://www.stromflo.de/dokuwiki/doku.php?id=led_tutorial LED-Tutorial]<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Grundlagen]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Beispiel_Drehzahlmessung_mit_Drehgeber&diff=22756Beispiel Drehzahlmessung mit Drehgeber2013-06-29T21:51:27Z<p>Besserwessi: /* Messen der Drehzahl durch zählen der Impulse */</p>
<hr />
<div>[[Kategorie:Sensoren]] <br />
[[Kategorie:Praxis]]<br />
[[Kategorie:Motoren]] <br />
<br />
<br />
Ermitteln der Drehzahl und Wegstrecke in Abhängigkeit einer Winkeländerung oder Impulsfolge in einer Abgelaufenen Zeit<br />
<br />
* Messen der Drehzahl nach jedem Impuls, also in Abhängigkeit einer Winkeländerung.<br />
* Messen der Drehzahl durch Zählen der Impulse nach Ablauf einer bestimmten Zeit. <br />
* Richtung, Geschwindigkeit und Position mit Doppellichtschranke ermitteln<br />
{{Ausbauwunsch|Mehr Grundlagen und vor allem mal praktische Programmbeispiele / Algorithmen etc.}}<br />
= Messen der Drehzahl nach jedem Impuls =<br />
<br />
Dieser Teil beschäftigt sich vor allem mit der Optischen Drehzahlmessung mit Hilfe einer Lochscheibe und [[Gabellichtschranke]] berechnet über die Periodendauer ohne Drehrichtungserkennung.<br />
[[Bild:GP1S23 Testaufbau.JPG|thumb| Testaufbau mit einer 32er Lochscheibe ]]<br />
[[Bild:183015_LB_00_FB.EPS_250.jpg|thumb|GP1S23 Gabellichtschranke Bild: Conrad Electronic ]]<br />
<br />
<br />
== Einführung ==<br />
<br />
Am einfachsten ist Impulse direkt an einer Bürste eines Motors zu zählen. Siehe dazu: http://www.roboternetz.de/community/...einem-DC-Motor .<br />
<br />
Die [http://de.wikipedia.org/wiki/Winkelgeschwindigkeit Winkelgeschwindigkeit] ist die Winkeländerung pro Zeiteinheit. Mit der Lichtschranke und den geometrischen Daten der Lochscheibe kann man nun relativ einfach die Zeit für eine bestimmte Winkeländerung messen. <br />
Die Lochscheibe gibt uns die Winkeländerung bei einer Periode vor, bei einer 32er Lochscheibe wären das 11,25° pro Periode , wie in den Bildern vom Oszilloskop zu sehen ändert sich natürlich bei Änderung der Drehzahl die Periodendauer, die Winkeländerung pro Periode ist aber konstant da diese durch die Lochscheibe vor gegeben ist. <br />
<br />
<gallery><br />
Bild:GP1S23_signal_langsam.png|Signal der Lichtschranke bei langsamer Drehzahl <br />
Bild:GP1S23_signal_schnell.PNG |Signal der Lichtschranke bei schneller Drehzahl <br />
</gallery><br />
<br />
Um die Periodendauer zu messen verwende ich den ICP1 (Input Capture Pin) eines Atmega8 der bei steigender Flanke den Timer1 ausliest und in das ICR1 Register schreibt. Läuft der Mikrocontroller auf 8Mhz und wir stellen den Vorteiler des Timers auf 8 so bekommen wir die Periodendauer in µs als Wert.<br />
<br />
== Auslegen der Lochscheibe ==<br />
<br />
In den meisten Fällen definieren der mechanische Aufbau den Durchmesser der Lochscheibe und die Drehzahl die Teilung (Anzahl der Löcher). Der µContoller und die Gabellichtschranke müssen die Impulse auch erfassen und verarbeiten können. Wenn man das Drehzahlband abschätzen kann ist es hilfreich, sich schon Gedanken um die Programmierung zu machen, denn bei hohen Drehzahlen springt der µC jedes mal in die Interrupt Service Routine rein, sofern man es so programmiert wie im Beispiel. Bei hohen Drehzahlen sollte die Teilung somit gröber, bei niedriger Drehzahl die Teilung feiner sein. <br />
<br />
[[Bild:Drehscheibe20mm.png|400px|thumb|left|Lochscheibe abgerollt mit idealisiertem Signal ]]<br />
<br />
== Berechnungen ==<br />
<br />
Hier folgt nun die Berechnung über die Periodendauer, beachten sollte man das im [http://de.wikipedia.org/wiki/Bogenma%C3%9F Bogenmaß] gerechnet wird, ein Winkel mit dem Bogenmaß 1 rad hat ein Gradmaß von ca. 57,3°. 11.25° sind also ca. 0,196 rad, das führt später natürlich zu unschönen Rechenoperationen im µC, Stichwort [http://www.mikrocontroller.net/articles/Festkommaarithmetik Festkommaarithmetik]. <br />
<br />
Nach dem Umstellen kommt man aber auf eine recht handliche Formel <math>n = \frac{\varphi }{dt \cdot 360}</math><br />
<br />
Es ist auch eine Überlegung wert, ob man die Drehzahl nicht umrechnet sondern mit der Periodendauer arbeitet. Das ist zwar nicht so geläufig aber das stört den µController nicht. Mit einem Kalkulationsprogramm kann man die Umrechnung von Periodendauer in Drehzahl auch extern vornehmen. <br />
<br />
Man sollte sich auch klar machen, das zumindest ein 8-Bit Prozessor am besten mit '''Ganzzahlen''' zwischen 0 und 255 oder 0 und 65535 zurechtkommt. Für die Teilung der Lochscheibe bieten sich deshalb Werte wie 90, 72, 45, 36, 30, 24, 20, 18, 15, 12, 10 und 8 an. Verwendet man an Stelle der dargestellten 32er Lochscheibe eine 30er oder 36er Scheibe, kann man zumindest mit ganzzahligen Gradzahlen rechnen. "Krumme Werte" wie [http://de.wikipedia.org/wiki/Kreiszahl Pi = 3,1415...], [http://de.wikipedia.org/wiki/Radiant_%28Einheit%29 Radiant = 57,29577...°] usw. sollten eine seltene Ausnahme in der Programmierung sein.<br />
<br />
= Messen der Drehzahl durch zählen der Impulse =<br />
<br />
Dieser Teil beschäftigt sich mit der Optischen Drehzahlmessung mit Hilfe einer Lochscheibe und [[Gabellichtschranke]] berechnet über die die Anzahl der Impulse in einer gewissen Zeit.<br />
<br />
Die meisten µC bieten die Möglichkeit über einen Eingang direkt mit der Hardware externe Pulse zu zählen (z.B. beim AVR der Eingang T1). Über eine vorgegeben Zeit von z.B. 1 Sekunde wird über den externen Eingang die Zahl der Pulse gezählt. Die Drehzahl ergibt sich dann einfach auch der Zahl der Impulse geteilt durch die Zahl der Löcher in der Scheibe und die Zeit für die Messung. Da die Zahl der Löcher und die Zeit konstant sind, erhält man direkt einen Wert proportional zur Drehzahl. Auch ist bei geeigneter Hardware die Belastung für die CPU gering, es werden aber i.A. 2 Timer benötigt: der eine für das Zählen der Impulse und der andere für die Festlegung des Zeitintervalls. Das Zeitintervall muss als Kompromiss zwischen Aktualisierungsfrequenz und Auflösung gewählt werden: mit einer Scheibe mit 32 Löchern erhält man in einer Sekunde ein Auflösung von 1/32 Umdrehungen pro Sekunde oder bei 1/32 Sekunde nur noch 1 Umdrehung pro Sekunde als Auflösung. Geeignet ist diese Methode vor allem für schnell kommende Pulse, also hohe Drehzahlen bzw. Lochscheiben mit vielen Löchern, da so in der Messzeit genügend Pulse für eine ausreichende Auflösung kommen.<br />
<br />
= Richtung, Geschwindigkeit und Position mit Doppellichtschranke ermitteln =<br />
Dieser Teil beschäftigt sich mit der Optischen Drehzahl- und Geschwindigkeitsmessung sowie der Erfassung von Drehrichtung und zurückgelegter Position mit Hilfe einer Lochscheibe und 2 Gabellichtschranken. Hierfür werden Quadratursignale in definierten Zeitabständen ausgewertet.<br />
<br />
== Einführung ==<br />
<br />
Für kontrollierte Bewegungen ist es mitunter wichtig, auch die Drehrichtung/Richtungswechsel und Position auszuwerten. Eine Lochscheibe mit einer Gabellichtschranke liefert lediglich Rechteckimpulse, egal wie rum sich die Lochscheibe bewegt. Bringt man eine zweite Gabellichtschranke an der gleichen Lochscheibe leicht versetzt an, bekommt man auch die Drehrichtung mit. Diese Methode ist altbekannt, simpel und leicht nachzubauen. Wenn man die Drehrichtung kennt, ist auch die Position kein Problem mehr. Ausgehend von einer Kalibrierposition können bei Vorwärtsbewegung Inkremente hochgezählt oder bei Rückwärtsbewegung runtergezählt werden. <br />
<br />
Interessierte nehmen dazu mal eine PC-Maus mit Kugelantrieb zur Hand. Darin findet man zwei kleine Lochscheiben, die für die X- und Y-Richtung die Impulse erzeugen. Durch spezielle Gabellichtschranken mit 2 phasenversetzten Ausgängen wird auch die Drehrichtung erkannt. <br />
<br />
Das selbe Prinzip ist in linearer Form auch in PC-Druckern zu finden: Entlang des Wagenrücklaufes ist ein transparentes Kunsstoffband gespannt, auf dem winzig kleine Streifenmuster aufgebracht sind. Diese werden von einer Gabellichtschranke mit 2 Ausgängen erkannt und so die Wagenposition gesteuert.<br />
<br />
Aber es geht natürlich noch einfacher...<br />
<br />
== Zwei Lichtschranken liefern vier Zustände ==<br />
<br />
[[Bild:vorwaerts_.jpg|thumb|Zustandsfolge bei Vorwärtsbewegung]]<br />
[[Bild:rueckwaerts_.jpg|thumb|Zustandsfolge bei Rückwärtsbewegung]]<br />
[[Bild:2LSZustand.jpg|thumb|2 Lichtschranken (LSA und LSB) liefern phasenversetzte Rechtecksignale. In Vorwärts- und Rückwärtsrichtung ergeben sich unterschiedliche Zustandsfolgen]]<br />
<br />
Man nimmt 2 normale Gabellichtschranken und bringt sie so an, das sie im 1,5 fachen Lochabstand die Lochscheibe durchleuchten. Wenn sich Lichtschranke A in der Mitte des Lichtstreifens befindet, ist Lichtschranke B an der Grenze des Schattenstreifens. Nebenstehende Skizzen zeigen die möglichen Zustände beider Lichtschranken bei Vorwärts- und Rückwärtsbewegung der Lochscheibe. Wichtig ist hierbei, das die Lochscheibe in etwa gleich breite Licht- und Schattensegmente eingeteilt ist und die Lichtschranke(n) einen möglichst haarfeinen Lichtstrahl besitzt. Die Signale der Fototransistoren sollten verstärkt und gegebenenfalls mit TTL-Bausteinen zu korrekten High- und Low-Pegeln aufbereitet werden.<br />
<br />
<br />
Es ergeben sich nun folgende Kombinationen:<br />
<br />
* Zustand 1: LSA = hell und LSB = hell<br />
* Zustand 2: LSA = hell und LSB = dunkel<br />
* Zustand 3: LSA = dunkel und LSB = dunkel<br />
* Zustand 4: LSA = dunkel und LSB = hell<br />
<br />
Dreht man die Lochscheibe weiter, wiederholt sich der Vorgang. Die Zustandsfolge ist 1 > 2 > 3 > 4 > 1 > 2 > 3 > 4 > usw.<br />
<br />
<br />
<br />
Bei entgegengesetzter Drehrichtung ändert sich diese Reihenfolge. Betrachtet man wieder vom Zustand 1 ausgehend die Gegenrichtung, ergeben sich nun diese Kombinationen:<br />
<br />
* Zustand 1: LSA = hell und LSB = hell<br />
* Zustand 4: LSA = dunkel und LSB = hell<br />
* Zustand 3: LSA = dunkel und LSB = dunkel<br />
* Zustand 2: LSA = hell und LSB = dunkel<br />
<br />
Die Zustandsfolge in Gegenrichtung lautet dann: 1 < 4 < 3 < 2 < 1 < 4 < 3 < 2 < usw.<br />
<br />
<br />
Mit diesem Wissen ist es nun ein Kinderspiel, auch die Änderung der Drehrichtung festzustellen. Dazu muss der letzte Zustand in einer Variable gespeichert und mit dem neu erkannten Zustand verglichen werden. Folgt nach Zustand 1 der Zustand 2, so ist es eine Vorwärtsbewegung. Folgt dem Zustand 1 der Zustand 4, so ist es eine Rückwärtsbewegung. Gleiches Prinzip gilt auch für die restlichen Zustände 2, 3 und 4.<br />
<br />
== Positionsüberwachung ==<br />
<br />
Von jedem der 4 Zustände gibt es also zwei zu unterscheidende Fälle: Vorwärts und Rückwärts. Insgesamt müssen demzufolge 8 Bedingungen zyklisch abgetastet und ein Positionszähler entsprechend inkrementiert oder dekrementiert werden und schon hat man eine exakte Streckeninformation. In Worte gefasst ergibt sich folgende Logik:<br />
<br />
Ausgangszustand: Beide Lichtschranken sind aus (A=0 UND B=0) und Zustand =1<br />
<br />
* WENN (Zustand=1 UND A=0 UND B=1) DANN: Setze Zustand=2, Position inkrementieren, Vor<br />
* WENN (Zustand=1 UND A=1 UND B=0) DANN: Setze Zustand=4, Position dekrementieren, Rück<br />
* WENN (Zustand=2 UND A=1 UND B=1) DANN: Setze Zustand=3, Position inkrementieren, Vor<br />
* WENN (Zustand=2 UND A=0 UND B=0) DANN: Setze Zustand=1, Position dekrementieren, Rück<br />
* WENN (Zustand=3 UND A=1 UND B=0) DANN: Setze Zustand=4, Position inkrementieren, Vor<br />
* WENN (Zustand=3 UND A=0 UND B=1) DANN: Setze Zustand=2, Position dekrementieren, Rück<br />
* WENN (Zustand=4 UND A=0 UND B=0) DANN: Setze Zustand=1, Position inkrementieren, Vor<br />
* WENN (Zustand=4 UND A=1 UND B=1) DANN: Setze Zustand=3, Position dekrementieren, Rück<br />
[[Bild:Lochscheibe8.jpg|thumb|Lochscheibe 8 ]]<br />
[[Bild:Lochscheibe10.jpg|thumb|Lochscheibe 10 ]]<br />
[[Bild:Lochscheibe12.jpg|thumb|Lochscheibe 12 ]]<br />
[[Bild:Lochscheibe15.jpg|thumb|Lochscheibe 15 ]]<br />
[[Bild:Lochscheibe18.jpg|thumb|Lochscheibe 18 ]]<br />
[[Bild:Lochscheibe20.jpg|thumb|Lochscheibe 20 ]]<br />
<br />
<br />
Diese Prüfung muss ständig wiederholt werden, damit alle Pegelwechsel der beiden Lichtschranken erfasst werden. Nur so ist es möglich, die exakte Position zu verfolgen. Dafür bieten sich 2 Möglichkeiten: Entweder die Prüfung wird im Hauptprogramm ständig durchlaufen bzw. ein Timer-Interrupt tastet regelmäßig die Lichtschranken ab oder ein Interrupt reagiert auf eine Änderung der Leichtschranken und die Auswertung erfolgt nach jeder Änderung. Die Art der Auswertung kann in allen Fällen gleich sein - lediglich wann und wie oft sie aufgerufen wird ist verschieden. Hier soll nur die Interruptvariante betrachtet werden, weil sie mehr Vorteile bringt. Weiter unten im Artikel ist dazu ein Beispiel in Sprache C zu finden. <br />
<br />
Zuvor muss aber eine sinnvolle Auslegung von Lochscheibe, Drehzahl und Positionsbereich gefunden und in Einklang mit den Ressourcen des verwendeten µControlles gebracht werden. Dazu folgende Betrachtungen:<br />
<br />
Wenn 2 phasenversetzte Lichtschranken je Hell-/ Dunkelzyklus bereits 4 Positionen liefern, benötigt die Lochscheibe nur halb so viel "Löcher" wie eine Lochscheibe bei Verwendung nur einer Lichtschranke. Eine 15er Lochscheibe liefert demzufolge 15x4 = 60 Positionen auf 360°, das sind 6° je erfasster Position. Entscheidend ist, an welchem Antriebsteil die Lochscheibe montiert ist und mit welcher Untersetzung zwischen Motor und Abtriebswelle(Rad) gearbeitet wird. Ist die Lochscheibe an der Motorwelle angebracht, würde bei einer angenommenen Untersetzung von 10:1 eine erfasste Position nur 0,6° an der Abtriebswelle ausmachen. <br />
<br />
Man muss sich klar machen, welche '''Positioniergenauigkeit''' nötig ist. Dabei gilt: Auslegung so genau wie nötig, nicht wie möglich. Weiter muss geklärt werden, wo man die Lochscheibe sinnvoll anbringt. Sie darf im Betrieb weder verschmutzt oder beschädigt werden. Fahrmodelle ziehen Staub, Haare etc. gern an, gut beraten ist man mit einer '''verkapselten Optomechanik'''. Ein weiterer Faktor ist die '''Drehzahl''' (im Leerlauf am höchsten) des Motors, weil diese die Frequenz der abzutastenden Signale beeinflusst. Zwischen Drehzahl n [U/min], Frequenz f [Hz] und Teilung T besteht folgender Zusammenhang:<br />
<br />
'''f = n * T / 60 s'''<br />
<br />
Beispiel: Ein Motor dreht im Leerlauf mit 2370 U/min und hat eine Lochscheibe mit Teilung = 12 an der Motorwelle. Die Frequenz der abzutastenden Signale einer Lichtschranke soll ermittelt werden. (oben im Osszillogramm dargestellt)<br />
<br />
'''f = 2370 * 12 / 60 s'''<br />
<br />
'''f = 474 Hz'''<br />
<br />
Eine Lichtschranke liefert also ein Rechtecksignal mit f = 474 Hz. Jetzt muss noch das Signal der zweiten Lichtschranke um 90° versetzt darübergelegt werden. Bei idealen Pegeln sind in einer Periode 4 Schaltzustände abzutasten, also muss die Abtastfrequenz mindestens 4-fach über der Grundfrequenz liegen. Da es in der Praxis aber keine idealen Pegel gibt, muss die Abtastfrequenz noch höher ausgelegt werden. Betrachten wir noch mal das Oszillogrammm weiter oben:<br />
<br />
Es fällt auf, das die Zeiten nicht exakt gleich sind, die Zustandszeiten 1 und 3 sind etwas länger als 2 und 4. Das liegt an der Einstellung des Abstandes beider Lichtschranken zueinander, die Phasenlage ist hier nicht genau 90°. Außerdem sind Lochscheibe und Lichtschranken mit kleinen Unregelmäßikeiten, Streuungen, Anstiegs- und Abfallzeiten behaftet, so dass es in einer Periode auch Zeiten gibt, die nicht eindeutig einem der 4 genannten Zustände entsprechen. Für eine sichere Abtastung ist deshalb von der kürzesten (= ungünstigsten) Schaltzeit auszugehen, hier sind es ca. 0,4 Millisekunden, das entspricht 2,5 kHz. Jeder Zustand sollte vom µController sicherheitshalber schon 3 bis 4 mal abgetastet werden, so das wir für das Beispiel mindestens ca. 10 kHz Abtastfrequenz einplanen müssen. Als Orientierungshilfe gilt:<br />
<br />
'''Abtastfrequenz >= 20 * Frequenz Lichtschranke'''<br />
<br />
Entsprechend der gewünschten Positioniergenauigkeit und Drehzahl kann nun die Teilung der Lochscheibe so gewählt werden, das die Lichtschrankensignale vom µController abgetastet werden können. Da Kaufteile aus dem Industriebereich oft eine recht hohe Teilung vorgeben, ist es manchmal besser, mit kleineren Teilungen anzufangen. Rechts sind Vorlagen, die man sich auf Folien ausdrucken kann.(Nähere Beschreibung im Praxis-Teil)<br />
<br />
Die Abtastung muss sicher sein, auch wenn das Hauptprogramm gerade mit anderen Dingen als der Lochscheibenauswertung beschäftigt ist. Man kann zwar die Lochscheibe vom Hauptprogramm durchaus mit abfragen lassen, aber dann sollten keine anderen zeitraubenden Prozeduren darin enthalten sein. Ist nur ein Zustandswechsel übersehen und ausgelassen wurden, geht es erst an der nachfolgenden gleichen Hell-/Dunkel-Kombination weiter und der Contoller verzählt sich um 4 Inkremente! Deshalb sollte die TimerInterrupt-Variante vorgezogen oder die Abtastung in einen separaten µController ausgelagert werden.<br />
<br />
Der Positionsbereich muss schließlich auch in einer Variablen dargestellt werden. Im Beispiel mit Teilung =12 wird der Positionzähler bei einer Motorumdrehung um 48 Inkremente hochgezählt. Bei 2370 U/min wäre eine 16Bit-Variable nach ca. 34 Sekunden übergelaufen. Demzufolge ist auch das Speichervolumen der Positionsvariablen dem tatsächlichen Fahrbereich anzupassen. Man kann auch Ober-/ Untergrenzen definieren, die bei Erreichen den Antrieb anhalten. Somit wird ein mechanisches Überfahren von Endlagen oder ein logisches Überlaufen von Variablen verhindert.<br />
<br />
<br />
'''Zusammenfassung:'''<br />
{| {{Blauetabelle}}<br />
|<br />
# Welche Positionsgenauigkeit brauch ich?<br />
# Sind Lochscheibe und Optik vor Störeinflüssen geschützt?<br />
# Welche maximale Drehzahl muss noch sicher abgetastet werden?<br />
# Frequenz Lichtschranke anpassen: Hohe Drehzahl mit kleiner Teilung / geringe Drehzahl mit großer Teilung<br />
# Phasenlage richtig eingestellt? Optimal sind 90°<br />
# Positionsvariable mit genügend Speichervolumen?<br />
|}<br />
<br />
== Kalibrierung ==<br />
<br />
Im einfachsten Fall fährt man seinem Antrieb auf eine Referenzmarke (Nonius, Paßstift o.ä.) und setzt dort den Positionszähler zurück. <br />
<br />
Wer es noch genauer will, muß auch noch die Lochscheibe in eine definierte Ausgangsstellung bringen. Dazu kann man die Lichtschrankensignale mit 2 LEDs optisch anzeigen. In der Kalibrierstellung müssen dann der Antrieb auf der Refernzmarke stehen und beide LEDs leuchten. Als Referenzmarke kann natürlich auch eine zusätzliche Lichtschranke dienen, die eine kleine Bohrung am Antrieb erkennt. Wichtig ist, dass die Referenzmarke nur einmal und eindeutig am Antrieb angebracht ist.<br />
<br />
== Geschwindigkeit und Beschleunigung ==<br />
<br />
Geschwindigkeit wird meist in [m/s], [km/h] oder [mph] ausgedrückt, also der Wegstrecke je Zeiteinheit. Im µController interessieren uns diese Einheiten erst mal nicht, hier werden Inkremente je Zeiteinheit gezählt. Die Zeitintervalle, in denen die Inkremente ausgewertet werden, liefert wieder ein TimerInterrupt. Dieser ist mit deutlich längeren Zyklen auszulegen, damit auch genügend Inkremente je Zyklus gezählt werden können. Im Code-Beispiel unten werden bei 16Mhz CPU-Takt die Vorteiler 1 und 1024 für die TimerInterrupt´s verwendet: Timer 0 mit Prescaler =1024 für die Geschwindigkeitsberechnung mit ca. 61 Hz und Timer 2 mit Prescaler =1 für die Abtastung der Lichtschranken mit 62,5 kHz. Die Geschwindigkeitsmessung wurde mit 12er, 15er und 18er Teilung getestet. In der ISR des Timer_0 Overflow wird die Differenz aus aktueller Position und der Position des vergangenen Zyklus gebildet. Da diese Wegdifferenz in definierten Zeitabständen ermittelt wird, hat man hier bereits die Geschwindigkeit in n Inkremente je 0,01638 Sekunden. Mit dieser etwas abstrakten Geschwindigkeitseinheit wird im Hauptprogramm der Motor gesteuert.<br />
<br />
Das gleiche Verfahren ist auch für die Beschleunigung anwendbar. Die Beschleunigung ist die Geschwindigkeitsänderung je Zeiteinheit. In der ISR des Timer_0 Overflow kann die Differenz aus aktueller Geschwindigkeit und der Geschwindigkeit des vergangenen Zyklus gebildet werden, um so die Beschleunigung zu erhalten. Die Einheit ist wieder etwas ungewohnt: Inkremente je 0,01638 Sekunden in 0,01638 Sekunden, also Inkremente/Quadratsekunden. Voraussetzung für eine vernünftige Beschleunigungsermittlung ist, das eine genügend hohe Auflösung der Geschwindigkeit vorliegt(=hohe Teilung der Lochscheibe). Ansonsten sind die ermittelten Werte zu klein und liegen, je nach Trägheit des Motors, fast immer bei 0, 1, 2 oder maximal 3.<br />
<br />
Vorteil der Differenzmethode ist, man kann die Geschwindigkeit in einer 8Bit-Variable mit Vorzeichen ausdrücken. Die Werte des Beispieles lagen je nach Teilung zwischen -50 und +50 und sind für die Motorsteuerung ausreichend. Bessere Auflösung erfordert höhere Teilung, höhere Abtastrate, höhere CPU-Frequenz.<br />
<br />
== Praxis==<br />
<br />
=== Herstellung der Lochscheibe ===<br />
Für eigene Projekte ist es mitunter schwer, Lochscheiben in passender Größe und Teilung zu finden. Oft müssen dann Kompromisse in der Drehzahlsteuerung oder Positionierung gemacht werden, weil die vielleicht günstigere Teilung nicht zu beschaffen ist. <br />
<br />
Hier wird deshalb ein Selbstbau von preiswerten Lochscheiben vorgestellt: Man trägt in einer Excel-Tabelle z.B. 18 mal die Zahl 20 ein und markiert diesen Zellbereich. Anschließend erstellt man mit dem Diagramm-Assistent ein Ring-Diagramm. Darin muss man nun jeden Datenpunkt einzeln formatieren und abwechslend die Farbe schwarz und weiß zuordnen. Markiert man die komplette Datenreihe, kann man unter 'Formatieren'>'Optionen' auch die Innenringgröße einstellen. Dieses Ring-Diagramm kann nun beliebig kopiert und vergößert oder verkleinert werden, auch die Datenreihe kann auf z.B. 24x15 oder 30x12 angepasst werden. So lassen sich auf einer A4-Seite einige Dutzend Lochscheiben in allen Größen und Teilungen herstellen. Das fertige Arbeitsblatt muss nun noch mit geeigneter Tinte auf Folie ausgedruckt werden. Den Drucker muss man möglichst auf satte Farbe und kräftgen Kontrast einstellen. Zum Schluß wird die ausgedruckte Folie mit A4- Laminierfolie verstärkt, wer kein Laminiergerät hat, geht in einen Copy-Shop o.ä. <br />
<br />
Die so gewonnenen Lochscheiben werden nun ausgeschnitten und bei Bedarf mit einem Heißluftgerät noch etwas "nachgebacken", bevor sie im eigenen Projekt mit Kraftkleber auf ein kleines Rad aufgeklebt werden. Hierbei auf gute Zentrierung achten, damit die Lochscheibe möglichst rund läuft. Sekundenkleber ist deshalb ungeeignet, weil kaum Zeit für Korrektur und Einstellung bleibt.<br />
<br />
=== Anpassen der Lichtschranke ===<br />
<br />
Baustelle:<br />
<br />
Fotos, Anregungen zum Nachbau, Beschaffung, Erfahrungen<br />
<br />
weiterführende Informationen:<br />
[http://de.wikipedia.org/wiki/Inkrementalgeber Inkrementalgeber]<br />
<br />
=== Quellcode ===<br />
<br />
<pre><br />
<br />
//Deklaration global<br />
volatile uint8_t Zust_Li; //Zustandsmerker Position linker Motor<br />
volatile uint16_t Li_Inkr; //aktuelle Position linker Motor<br />
volatile uint16_t old_Li_Inkr; //Position im vorangegangenen Takt<br />
volatile int8_t Speed_Li_Ist; //Wegänderung je Takt, in ISR Timer 0<br />
<br />
...<br />
<br />
// Interrupt-Service-Routine Timer_0 Overflow<br />
SIGNAL (SIG_OVERFLOW0)<br />
{<br />
// ISR-Code 61,035 Pro Sekunde<br />
Timeout++;<br />
Speed_Li_Ist = Li_Inkr - old_Li_Inkr;<br />
old_Li_Inkr = Li_Inkr;<br />
}<br />
<br />
<br />
// Interrupt-Service-Routine Timer_2 Overflow<br />
SIGNAL (SIG_OVERFLOW2)<br />
{<br />
// ISR-Code 62.5 kHz<br />
// 4 Zustaende durch jeweils 1 Bit repräsentiert (1,2,4,8)<br />
// Lichtschranke A liegt an Port A.0<br />
// Lichtschranke B liegt an Port A.1<br />
<br />
if ( (Zust_Li == 1) && ( !(PINA & (1<<PINA0)) )&& ( PINA & (1<<PINA1) ) ){<br />
Li_Inkr = Li_Inkr + 1;<br />
Zust_Li = 2;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 1) && (PINA & (1<<PINA0) )&& ( !( PINA & (1<<PINA1)) ) ){<br />
Li_Inkr = Li_Inkr - 1;<br />
Zust_Li = 8;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 2) && (PINA & (1<<PINA0) )&& ( PINA & (1<<PINA1) ) ){<br />
Li_Inkr = Li_Inkr + 1;<br />
Zust_Li = 4;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 2) && ( !(PINA & (1<<PINA0)) )&& ( !( PINA & (1<<PINA1)) ) ){<br />
Li_Inkr = Li_Inkr - 1;<br />
Zust_Li = 1;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 4) && (PINA & (1<<PINA0) )&& ( !( PINA & (1<<PINA1)) ) ){<br />
Li_Inkr = Li_Inkr + 1;<br />
Zust_Li = 8;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 4) && ( !(PINA & (1<<PINA0)) )&& ( PINA & (1<<PINA1) ) ){<br />
Li_Inkr = Li_Inkr - 1;<br />
Zust_Li = 2;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 8) && ( !(PINA & (1<<PINA0)) )&& ( !( PINA & (1<<PINA1)) ) ){<br />
Li_Inkr = Li_Inkr + 1;<br />
Zust_Li = 1;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 8) && (PINA & (1<<PINA0) )&& ( PINA & (1<<PINA1) ) ){<br />
Li_Inkr = Li_Inkr - 1;<br />
Zust_Li = 4;<br />
} /* end if */<br />
<br />
}<br />
<br />
...<br />
<br />
int main (void)<br />
{<br />
<br />
//Init<br />
TCCR2 |= (1<<CS20); //Prescaler = 1 (16MHz/1 = 16000000) -><br />
//16000000/256 -> 62500 Interrupts je Sekunde<br />
<br />
TCCR0 |= (1<<CS02)|(1<<CS00); //Prescaler = 1024 (16000000 / 1024 = 15625) -><br />
//15625/256 = 61,035 -> 61 Interrupts je Sekunde<br />
<br />
TIMSK |= (1<<TOIE0)|(1<<TOIE2); //Timer Overflow Interrupt Enablae für Timer 0 und 2<br />
<br />
<br />
Li_Inkr = 32000;Zust_Li = 1; // Kalibrierstellung bei 32000<br />
sei(); // Interrupts aktivieren<br />
<br />
<br />
while(1)<br />
{<br />
<br />
// hier kann Position und Geschwindigkeit ausgewertet werden<br />
<br />
}<br />
}<br />
<br />
</pre><br />
<br />
= Vor und Nachteile der Messmethoden=</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Beispiel_Drehzahlmessung_mit_Drehgeber&diff=22755Beispiel Drehzahlmessung mit Drehgeber2013-06-29T16:56:46Z<p>Besserwessi: /* Positionsüberwachung */</p>
<hr />
<div>[[Kategorie:Sensoren]] <br />
[[Kategorie:Praxis]]<br />
[[Kategorie:Motoren]] <br />
<br />
<br />
Ermitteln der Drehzahl und Wegstrecke in Abhängigkeit einer Winkeländerung oder Impulsfolge in einer Abgelaufenen Zeit<br />
<br />
* Messen der Drehzahl nach jedem Impuls, also in Abhängigkeit einer Winkeländerung.<br />
* Messen der Drehzahl durch Zählen der Impulse nach Ablauf einer bestimmten Zeit. <br />
* Richtung, Geschwindigkeit und Position mit Doppellichtschranke ermitteln<br />
{{Ausbauwunsch|Mehr Grundlagen und vor allem mal praktische Programmbeispiele / Algorithmen etc.}}<br />
= Messen der Drehzahl nach jedem Impuls =<br />
<br />
Dieser Teil beschäftigt sich vor allem mit der Optischen Drehzahlmessung mit Hilfe einer Lochscheibe und [[Gabellichtschranke]] berechnet über die Periodendauer ohne Drehrichtungserkennung.<br />
[[Bild:GP1S23 Testaufbau.JPG|thumb| Testaufbau mit einer 32er Lochscheibe ]]<br />
[[Bild:183015_LB_00_FB.EPS_250.jpg|thumb|GP1S23 Gabellichtschranke Bild: Conrad Electronic ]]<br />
<br />
<br />
== Einführung ==<br />
<br />
Am einfachsten ist Impulse direkt an einer Bürste eines Motors zu zählen. Siehe dazu: http://www.roboternetz.de/community/...einem-DC-Motor .<br />
<br />
Die [http://de.wikipedia.org/wiki/Winkelgeschwindigkeit Winkelgeschwindigkeit] ist die Winkeländerung pro Zeiteinheit. Mit der Lichtschranke und den geometrischen Daten der Lochscheibe kann man nun relativ einfach die Zeit für eine bestimmte Winkeländerung messen. <br />
Die Lochscheibe gibt uns die Winkeländerung bei einer Periode vor, bei einer 32er Lochscheibe wären das 11,25° pro Periode , wie in den Bildern vom Oszilloskop zu sehen ändert sich natürlich bei Änderung der Drehzahl die Periodendauer, die Winkeländerung pro Periode ist aber konstant da diese durch die Lochscheibe vor gegeben ist. <br />
<br />
<gallery><br />
Bild:GP1S23_signal_langsam.png|Signal der Lichtschranke bei langsamer Drehzahl <br />
Bild:GP1S23_signal_schnell.PNG |Signal der Lichtschranke bei schneller Drehzahl <br />
</gallery><br />
<br />
Um die Periodendauer zu messen verwende ich den ICP1 (Input Capture Pin) eines Atmega8 der bei steigender Flanke den Timer1 ausliest und in das ICR1 Register schreibt. Läuft der Mikrocontroller auf 8Mhz und wir stellen den Vorteiler des Timers auf 8 so bekommen wir die Periodendauer in µs als Wert.<br />
<br />
== Auslegen der Lochscheibe ==<br />
<br />
In den meisten Fällen definieren der mechanische Aufbau den Durchmesser der Lochscheibe und die Drehzahl die Teilung (Anzahl der Löcher). Der µContoller und die Gabellichtschranke müssen die Impulse auch erfassen und verarbeiten können. Wenn man das Drehzahlband abschätzen kann ist es hilfreich, sich schon Gedanken um die Programmierung zu machen, denn bei hohen Drehzahlen springt der µC jedes mal in die Interrupt Service Routine rein, sofern man es so programmiert wie im Beispiel. Bei hohen Drehzahlen sollte die Teilung somit gröber, bei niedriger Drehzahl die Teilung feiner sein. <br />
<br />
[[Bild:Drehscheibe20mm.png|400px|thumb|left|Lochscheibe abgerollt mit idealisiertem Signal ]]<br />
<br />
== Berechnungen ==<br />
<br />
Hier folgt nun die Berechnung über die Periodendauer, beachten sollte man das im [http://de.wikipedia.org/wiki/Bogenma%C3%9F Bogenmaß] gerechnet wird, ein Winkel mit dem Bogenmaß 1 rad hat ein Gradmaß von ca. 57,3°. 11.25° sind also ca. 0,196 rad, das führt später natürlich zu unschönen Rechenoperationen im µC, Stichwort [http://www.mikrocontroller.net/articles/Festkommaarithmetik Festkommaarithmetik]. <br />
<br />
Nach dem Umstellen kommt man aber auf eine recht handliche Formel <math>n = \frac{\varphi }{dt \cdot 360}</math><br />
<br />
Es ist auch eine Überlegung wert, ob man die Drehzahl nicht umrechnet sondern mit der Periodendauer arbeitet. Das ist zwar nicht so geläufig aber das stört den µController nicht. Mit einem Kalkulationsprogramm kann man die Umrechnung von Periodendauer in Drehzahl auch extern vornehmen. <br />
<br />
Man sollte sich auch klar machen, das zumindest ein 8-Bit Prozessor am besten mit '''Ganzzahlen''' zwischen 0 und 255 oder 0 und 65535 zurechtkommt. Für die Teilung der Lochscheibe bieten sich deshalb Werte wie 90, 72, 45, 36, 30, 24, 20, 18, 15, 12, 10 und 8 an. Verwendet man an Stelle der dargestellten 32er Lochscheibe eine 30er oder 36er Scheibe, kann man zumindest mit ganzzahligen Gradzahlen rechnen. "Krumme Werte" wie [http://de.wikipedia.org/wiki/Kreiszahl Pi = 3,1415...], [http://de.wikipedia.org/wiki/Radiant_%28Einheit%29 Radiant = 57,29577...°] usw. sollten eine seltene Ausnahme in der Programmierung sein.<br />
<br />
= Messen der Drehzahl durch zählen der Impulse =<br />
<br />
Dieser Teil beschäftigt sich mit der Optischen Drehzahlmessung mit Hilfe einer Lochscheibe und [[Gabellichtschranke]] berechnet über die die Anzahl der Impulse in einer gewissen Zeit. Geeignet ist diese Methode vor allem für schnell kommende Pulse, also hohe Drehzahlen bzw. Lochscheiben mit vielen Löchern. Die meisten µC bieten die Möglichkeit über einen Eingang direkt mit der Hardware externe Pulse zu zählen (z.B. beim AVR der Eingang T1). Über eine Vorgegeben Zeit von z.B. 1 Sekunde wird über den externen Eingang die Zahl der Pulse gezählt. Die Drehzahl ergibt sich dann einfach auch der Zahl der Impulse geteilt durch die Zahl der Löcher in der Scheibe und die Zeit für die Messung. Diese Methode liefert keine so gute Auflösung, liefert dafür aber direkt einen Wert Proportional zur Drehzahl. Auch ist bei geeigneter Hardware die Belastung für die CPU gering, es werden aber i.A. 2 Timer benötigt: der eine für das Zählen der Impulse und der andere für die Festlegung der des Zeitintervalls. Das Zeitintervall muss als Kompromiss zwischen Aktualisierungsfrequenz und Auflösung gewählt werden: mit einer Scheibe mit 32 Löchern erhält man in einer Sekunde ein Auflösung von 1/32 Umdrehungen pro Sekunde oder bei 1/32 Sekunde nur noch 1 Umdrehung pro Sekunde als Auflösung.<br />
<br />
= Richtung, Geschwindigkeit und Position mit Doppellichtschranke ermitteln =<br />
Dieser Teil beschäftigt sich mit der Optischen Drehzahl- und Geschwindigkeitsmessung sowie der Erfassung von Drehrichtung und zurückgelegter Position mit Hilfe einer Lochscheibe und 2 Gabellichtschranken. Hierfür werden Quadratursignale in definierten Zeitabständen ausgewertet.<br />
<br />
== Einführung ==<br />
<br />
Für kontrollierte Bewegungen ist es mitunter wichtig, auch die Drehrichtung/Richtungswechsel und Position auszuwerten. Eine Lochscheibe mit einer Gabellichtschranke liefert lediglich Rechteckimpulse, egal wie rum sich die Lochscheibe bewegt. Bringt man eine zweite Gabellichtschranke an der gleichen Lochscheibe leicht versetzt an, bekommt man auch die Drehrichtung mit. Diese Methode ist altbekannt, simpel und leicht nachzubauen. Wenn man die Drehrichtung kennt, ist auch die Position kein Problem mehr. Ausgehend von einer Kalibrierposition können bei Vorwärtsbewegung Inkremente hochgezählt oder bei Rückwärtsbewegung runtergezählt werden. <br />
<br />
Interessierte nehmen dazu mal eine PC-Maus mit Kugelantrieb zur Hand. Darin findet man zwei kleine Lochscheiben, die für die X- und Y-Richtung die Impulse erzeugen. Durch spezielle Gabellichtschranken mit 2 phasenversetzten Ausgängen wird auch die Drehrichtung erkannt. <br />
<br />
Das selbe Prinzip ist in linearer Form auch in PC-Druckern zu finden: Entlang des Wagenrücklaufes ist ein transparentes Kunsstoffband gespannt, auf dem winzig kleine Streifenmuster aufgebracht sind. Diese werden von einer Gabellichtschranke mit 2 Ausgängen erkannt und so die Wagenposition gesteuert.<br />
<br />
Aber es geht natürlich noch einfacher...<br />
<br />
== Zwei Lichtschranken liefern vier Zustände ==<br />
<br />
[[Bild:vorwaerts_.jpg|thumb|Zustandsfolge bei Vorwärtsbewegung]]<br />
[[Bild:rueckwaerts_.jpg|thumb|Zustandsfolge bei Rückwärtsbewegung]]<br />
[[Bild:2LSZustand.jpg|thumb|2 Lichtschranken (LSA und LSB) liefern phasenversetzte Rechtecksignale. In Vorwärts- und Rückwärtsrichtung ergeben sich unterschiedliche Zustandsfolgen]]<br />
<br />
Man nimmt 2 normale Gabellichtschranken und bringt sie so an, das sie im 1,5 fachen Lochabstand die Lochscheibe durchleuchten. Wenn sich Lichtschranke A in der Mitte des Lichtstreifens befindet, ist Lichtschranke B an der Grenze des Schattenstreifens. Nebenstehende Skizzen zeigen die möglichen Zustände beider Lichtschranken bei Vorwärts- und Rückwärtsbewegung der Lochscheibe. Wichtig ist hierbei, das die Lochscheibe in etwa gleich breite Licht- und Schattensegmente eingeteilt ist und die Lichtschranke(n) einen möglichst haarfeinen Lichtstrahl besitzt. Die Signale der Fototransistoren sollten verstärkt und gegebenenfalls mit TTL-Bausteinen zu korrekten High- und Low-Pegeln aufbereitet werden.<br />
<br />
<br />
Es ergeben sich nun folgende Kombinationen:<br />
<br />
* Zustand 1: LSA = hell und LSB = hell<br />
* Zustand 2: LSA = hell und LSB = dunkel<br />
* Zustand 3: LSA = dunkel und LSB = dunkel<br />
* Zustand 4: LSA = dunkel und LSB = hell<br />
<br />
Dreht man die Lochscheibe weiter, wiederholt sich der Vorgang. Die Zustandsfolge ist 1 > 2 > 3 > 4 > 1 > 2 > 3 > 4 > usw.<br />
<br />
<br />
<br />
Bei entgegengesetzter Drehrichtung ändert sich diese Reihenfolge. Betrachtet man wieder vom Zustand 1 ausgehend die Gegenrichtung, ergeben sich nun diese Kombinationen:<br />
<br />
* Zustand 1: LSA = hell und LSB = hell<br />
* Zustand 4: LSA = dunkel und LSB = hell<br />
* Zustand 3: LSA = dunkel und LSB = dunkel<br />
* Zustand 2: LSA = hell und LSB = dunkel<br />
<br />
Die Zustandsfolge in Gegenrichtung lautet dann: 1 < 4 < 3 < 2 < 1 < 4 < 3 < 2 < usw.<br />
<br />
<br />
Mit diesem Wissen ist es nun ein Kinderspiel, auch die Änderung der Drehrichtung festzustellen. Dazu muss der letzte Zustand in einer Variable gespeichert und mit dem neu erkannten Zustand verglichen werden. Folgt nach Zustand 1 der Zustand 2, so ist es eine Vorwärtsbewegung. Folgt dem Zustand 1 der Zustand 4, so ist es eine Rückwärtsbewegung. Gleiches Prinzip gilt auch für die restlichen Zustände 2, 3 und 4.<br />
<br />
== Positionsüberwachung ==<br />
<br />
Von jedem der 4 Zustände gibt es also zwei zu unterscheidende Fälle: Vorwärts und Rückwärts. Insgesamt müssen demzufolge 8 Bedingungen zyklisch abgetastet und ein Positionszähler entsprechend inkrementiert oder dekrementiert werden und schon hat man eine exakte Streckeninformation. In Worte gefasst ergibt sich folgende Logik:<br />
<br />
Ausgangszustand: Beide Lichtschranken sind aus (A=0 UND B=0) und Zustand =1<br />
<br />
* WENN (Zustand=1 UND A=0 UND B=1) DANN: Setze Zustand=2, Position inkrementieren, Vor<br />
* WENN (Zustand=1 UND A=1 UND B=0) DANN: Setze Zustand=4, Position dekrementieren, Rück<br />
* WENN (Zustand=2 UND A=1 UND B=1) DANN: Setze Zustand=3, Position inkrementieren, Vor<br />
* WENN (Zustand=2 UND A=0 UND B=0) DANN: Setze Zustand=1, Position dekrementieren, Rück<br />
* WENN (Zustand=3 UND A=1 UND B=0) DANN: Setze Zustand=4, Position inkrementieren, Vor<br />
* WENN (Zustand=3 UND A=0 UND B=1) DANN: Setze Zustand=2, Position dekrementieren, Rück<br />
* WENN (Zustand=4 UND A=0 UND B=0) DANN: Setze Zustand=1, Position inkrementieren, Vor<br />
* WENN (Zustand=4 UND A=1 UND B=1) DANN: Setze Zustand=3, Position dekrementieren, Rück<br />
[[Bild:Lochscheibe8.jpg|thumb|Lochscheibe 8 ]]<br />
[[Bild:Lochscheibe10.jpg|thumb|Lochscheibe 10 ]]<br />
[[Bild:Lochscheibe12.jpg|thumb|Lochscheibe 12 ]]<br />
[[Bild:Lochscheibe15.jpg|thumb|Lochscheibe 15 ]]<br />
[[Bild:Lochscheibe18.jpg|thumb|Lochscheibe 18 ]]<br />
[[Bild:Lochscheibe20.jpg|thumb|Lochscheibe 20 ]]<br />
<br />
<br />
Diese Prüfung muss ständig wiederholt werden, damit alle Pegelwechsel der beiden Lichtschranken erfasst werden. Nur so ist es möglich, die exakte Position zu verfolgen. Dafür bieten sich 2 Möglichkeiten: Entweder die Prüfung wird im Hauptprogramm ständig durchlaufen bzw. ein Timer-Interrupt tastet regelmäßig die Lichtschranken ab oder ein Interrupt reagiert auf eine Änderung der Leichtschranken und die Auswertung erfolgt nach jeder Änderung. Die Art der Auswertung kann in allen Fällen gleich sein - lediglich wann und wie oft sie aufgerufen wird ist verschieden. Hier soll nur die Interruptvariante betrachtet werden, weil sie mehr Vorteile bringt. Weiter unten im Artikel ist dazu ein Beispiel in Sprache C zu finden. <br />
<br />
Zuvor muss aber eine sinnvolle Auslegung von Lochscheibe, Drehzahl und Positionsbereich gefunden und in Einklang mit den Ressourcen des verwendeten µControlles gebracht werden. Dazu folgende Betrachtungen:<br />
<br />
Wenn 2 phasenversetzte Lichtschranken je Hell-/ Dunkelzyklus bereits 4 Positionen liefern, benötigt die Lochscheibe nur halb so viel "Löcher" wie eine Lochscheibe bei Verwendung nur einer Lichtschranke. Eine 15er Lochscheibe liefert demzufolge 15x4 = 60 Positionen auf 360°, das sind 6° je erfasster Position. Entscheidend ist, an welchem Antriebsteil die Lochscheibe montiert ist und mit welcher Untersetzung zwischen Motor und Abtriebswelle(Rad) gearbeitet wird. Ist die Lochscheibe an der Motorwelle angebracht, würde bei einer angenommenen Untersetzung von 10:1 eine erfasste Position nur 0,6° an der Abtriebswelle ausmachen. <br />
<br />
Man muss sich klar machen, welche '''Positioniergenauigkeit''' nötig ist. Dabei gilt: Auslegung so genau wie nötig, nicht wie möglich. Weiter muss geklärt werden, wo man die Lochscheibe sinnvoll anbringt. Sie darf im Betrieb weder verschmutzt oder beschädigt werden. Fahrmodelle ziehen Staub, Haare etc. gern an, gut beraten ist man mit einer '''verkapselten Optomechanik'''. Ein weiterer Faktor ist die '''Drehzahl''' (im Leerlauf am höchsten) des Motors, weil diese die Frequenz der abzutastenden Signale beeinflusst. Zwischen Drehzahl n [U/min], Frequenz f [Hz] und Teilung T besteht folgender Zusammenhang:<br />
<br />
'''f = n * T / 60 s'''<br />
<br />
Beispiel: Ein Motor dreht im Leerlauf mit 2370 U/min und hat eine Lochscheibe mit Teilung = 12 an der Motorwelle. Die Frequenz der abzutastenden Signale einer Lichtschranke soll ermittelt werden. (oben im Osszillogramm dargestellt)<br />
<br />
'''f = 2370 * 12 / 60 s'''<br />
<br />
'''f = 474 Hz'''<br />
<br />
Eine Lichtschranke liefert also ein Rechtecksignal mit f = 474 Hz. Jetzt muss noch das Signal der zweiten Lichtschranke um 90° versetzt darübergelegt werden. Bei idealen Pegeln sind in einer Periode 4 Schaltzustände abzutasten, also muss die Abtastfrequenz mindestens 4-fach über der Grundfrequenz liegen. Da es in der Praxis aber keine idealen Pegel gibt, muss die Abtastfrequenz noch höher ausgelegt werden. Betrachten wir noch mal das Oszillogrammm weiter oben:<br />
<br />
Es fällt auf, das die Zeiten nicht exakt gleich sind, die Zustandszeiten 1 und 3 sind etwas länger als 2 und 4. Das liegt an der Einstellung des Abstandes beider Lichtschranken zueinander, die Phasenlage ist hier nicht genau 90°. Außerdem sind Lochscheibe und Lichtschranken mit kleinen Unregelmäßikeiten, Streuungen, Anstiegs- und Abfallzeiten behaftet, so dass es in einer Periode auch Zeiten gibt, die nicht eindeutig einem der 4 genannten Zustände entsprechen. Für eine sichere Abtastung ist deshalb von der kürzesten (= ungünstigsten) Schaltzeit auszugehen, hier sind es ca. 0,4 Millisekunden, das entspricht 2,5 kHz. Jeder Zustand sollte vom µController sicherheitshalber schon 3 bis 4 mal abgetastet werden, so das wir für das Beispiel mindestens ca. 10 kHz Abtastfrequenz einplanen müssen. Als Orientierungshilfe gilt:<br />
<br />
'''Abtastfrequenz >= 20 * Frequenz Lichtschranke'''<br />
<br />
Entsprechend der gewünschten Positioniergenauigkeit und Drehzahl kann nun die Teilung der Lochscheibe so gewählt werden, das die Lichtschrankensignale vom µController abgetastet werden können. Da Kaufteile aus dem Industriebereich oft eine recht hohe Teilung vorgeben, ist es manchmal besser, mit kleineren Teilungen anzufangen. Rechts sind Vorlagen, die man sich auf Folien ausdrucken kann.(Nähere Beschreibung im Praxis-Teil)<br />
<br />
Die Abtastung muss sicher sein, auch wenn das Hauptprogramm gerade mit anderen Dingen als der Lochscheibenauswertung beschäftigt ist. Man kann zwar die Lochscheibe vom Hauptprogramm durchaus mit abfragen lassen, aber dann sollten keine anderen zeitraubenden Prozeduren darin enthalten sein. Ist nur ein Zustandswechsel übersehen und ausgelassen wurden, geht es erst an der nachfolgenden gleichen Hell-/Dunkel-Kombination weiter und der Contoller verzählt sich um 4 Inkremente! Deshalb sollte die TimerInterrupt-Variante vorgezogen oder die Abtastung in einen separaten µController ausgelagert werden.<br />
<br />
Der Positionsbereich muss schließlich auch in einer Variablen dargestellt werden. Im Beispiel mit Teilung =12 wird der Positionzähler bei einer Motorumdrehung um 48 Inkremente hochgezählt. Bei 2370 U/min wäre eine 16Bit-Variable nach ca. 34 Sekunden übergelaufen. Demzufolge ist auch das Speichervolumen der Positionsvariablen dem tatsächlichen Fahrbereich anzupassen. Man kann auch Ober-/ Untergrenzen definieren, die bei Erreichen den Antrieb anhalten. Somit wird ein mechanisches Überfahren von Endlagen oder ein logisches Überlaufen von Variablen verhindert.<br />
<br />
<br />
'''Zusammenfassung:'''<br />
{| {{Blauetabelle}}<br />
|<br />
# Welche Positionsgenauigkeit brauch ich?<br />
# Sind Lochscheibe und Optik vor Störeinflüssen geschützt?<br />
# Welche maximale Drehzahl muss noch sicher abgetastet werden?<br />
# Frequenz Lichtschranke anpassen: Hohe Drehzahl mit kleiner Teilung / geringe Drehzahl mit großer Teilung<br />
# Phasenlage richtig eingestellt? Optimal sind 90°<br />
# Positionsvariable mit genügend Speichervolumen?<br />
|}<br />
<br />
== Kalibrierung ==<br />
<br />
Im einfachsten Fall fährt man seinem Antrieb auf eine Referenzmarke (Nonius, Paßstift o.ä.) und setzt dort den Positionszähler zurück. <br />
<br />
Wer es noch genauer will, muß auch noch die Lochscheibe in eine definierte Ausgangsstellung bringen. Dazu kann man die Lichtschrankensignale mit 2 LEDs optisch anzeigen. In der Kalibrierstellung müssen dann der Antrieb auf der Refernzmarke stehen und beide LEDs leuchten. Als Referenzmarke kann natürlich auch eine zusätzliche Lichtschranke dienen, die eine kleine Bohrung am Antrieb erkennt. Wichtig ist, dass die Referenzmarke nur einmal und eindeutig am Antrieb angebracht ist.<br />
<br />
== Geschwindigkeit und Beschleunigung ==<br />
<br />
Geschwindigkeit wird meist in [m/s], [km/h] oder [mph] ausgedrückt, also der Wegstrecke je Zeiteinheit. Im µController interessieren uns diese Einheiten erst mal nicht, hier werden Inkremente je Zeiteinheit gezählt. Die Zeitintervalle, in denen die Inkremente ausgewertet werden, liefert wieder ein TimerInterrupt. Dieser ist mit deutlich längeren Zyklen auszulegen, damit auch genügend Inkremente je Zyklus gezählt werden können. Im Code-Beispiel unten werden bei 16Mhz CPU-Takt die Vorteiler 1 und 1024 für die TimerInterrupt´s verwendet: Timer 0 mit Prescaler =1024 für die Geschwindigkeitsberechnung mit ca. 61 Hz und Timer 2 mit Prescaler =1 für die Abtastung der Lichtschranken mit 62,5 kHz. Die Geschwindigkeitsmessung wurde mit 12er, 15er und 18er Teilung getestet. In der ISR des Timer_0 Overflow wird die Differenz aus aktueller Position und der Position des vergangenen Zyklus gebildet. Da diese Wegdifferenz in definierten Zeitabständen ermittelt wird, hat man hier bereits die Geschwindigkeit in n Inkremente je 0,01638 Sekunden. Mit dieser etwas abstrakten Geschwindigkeitseinheit wird im Hauptprogramm der Motor gesteuert.<br />
<br />
Das gleiche Verfahren ist auch für die Beschleunigung anwendbar. Die Beschleunigung ist die Geschwindigkeitsänderung je Zeiteinheit. In der ISR des Timer_0 Overflow kann die Differenz aus aktueller Geschwindigkeit und der Geschwindigkeit des vergangenen Zyklus gebildet werden, um so die Beschleunigung zu erhalten. Die Einheit ist wieder etwas ungewohnt: Inkremente je 0,01638 Sekunden in 0,01638 Sekunden, also Inkremente/Quadratsekunden. Voraussetzung für eine vernünftige Beschleunigungsermittlung ist, das eine genügend hohe Auflösung der Geschwindigkeit vorliegt(=hohe Teilung der Lochscheibe). Ansonsten sind die ermittelten Werte zu klein und liegen, je nach Trägheit des Motors, fast immer bei 0, 1, 2 oder maximal 3.<br />
<br />
Vorteil der Differenzmethode ist, man kann die Geschwindigkeit in einer 8Bit-Variable mit Vorzeichen ausdrücken. Die Werte des Beispieles lagen je nach Teilung zwischen -50 und +50 und sind für die Motorsteuerung ausreichend. Bessere Auflösung erfordert höhere Teilung, höhere Abtastrate, höhere CPU-Frequenz.<br />
<br />
== Praxis==<br />
<br />
=== Herstellung der Lochscheibe ===<br />
Für eigene Projekte ist es mitunter schwer, Lochscheiben in passender Größe und Teilung zu finden. Oft müssen dann Kompromisse in der Drehzahlsteuerung oder Positionierung gemacht werden, weil die vielleicht günstigere Teilung nicht zu beschaffen ist. <br />
<br />
Hier wird deshalb ein Selbstbau von preiswerten Lochscheiben vorgestellt: Man trägt in einer Excel-Tabelle z.B. 18 mal die Zahl 20 ein und markiert diesen Zellbereich. Anschließend erstellt man mit dem Diagramm-Assistent ein Ring-Diagramm. Darin muss man nun jeden Datenpunkt einzeln formatieren und abwechslend die Farbe schwarz und weiß zuordnen. Markiert man die komplette Datenreihe, kann man unter 'Formatieren'>'Optionen' auch die Innenringgröße einstellen. Dieses Ring-Diagramm kann nun beliebig kopiert und vergößert oder verkleinert werden, auch die Datenreihe kann auf z.B. 24x15 oder 30x12 angepasst werden. So lassen sich auf einer A4-Seite einige Dutzend Lochscheiben in allen Größen und Teilungen herstellen. Das fertige Arbeitsblatt muss nun noch mit geeigneter Tinte auf Folie ausgedruckt werden. Den Drucker muss man möglichst auf satte Farbe und kräftgen Kontrast einstellen. Zum Schluß wird die ausgedruckte Folie mit A4- Laminierfolie verstärkt, wer kein Laminiergerät hat, geht in einen Copy-Shop o.ä. <br />
<br />
Die so gewonnenen Lochscheiben werden nun ausgeschnitten und bei Bedarf mit einem Heißluftgerät noch etwas "nachgebacken", bevor sie im eigenen Projekt mit Kraftkleber auf ein kleines Rad aufgeklebt werden. Hierbei auf gute Zentrierung achten, damit die Lochscheibe möglichst rund läuft. Sekundenkleber ist deshalb ungeeignet, weil kaum Zeit für Korrektur und Einstellung bleibt.<br />
<br />
=== Anpassen der Lichtschranke ===<br />
<br />
Baustelle:<br />
<br />
Fotos, Anregungen zum Nachbau, Beschaffung, Erfahrungen<br />
<br />
weiterführende Informationen:<br />
[http://de.wikipedia.org/wiki/Inkrementalgeber Inkrementalgeber]<br />
<br />
=== Quellcode ===<br />
<br />
<pre><br />
<br />
//Deklaration global<br />
volatile uint8_t Zust_Li; //Zustandsmerker Position linker Motor<br />
volatile uint16_t Li_Inkr; //aktuelle Position linker Motor<br />
volatile uint16_t old_Li_Inkr; //Position im vorangegangenen Takt<br />
volatile int8_t Speed_Li_Ist; //Wegänderung je Takt, in ISR Timer 0<br />
<br />
...<br />
<br />
// Interrupt-Service-Routine Timer_0 Overflow<br />
SIGNAL (SIG_OVERFLOW0)<br />
{<br />
// ISR-Code 61,035 Pro Sekunde<br />
Timeout++;<br />
Speed_Li_Ist = Li_Inkr - old_Li_Inkr;<br />
old_Li_Inkr = Li_Inkr;<br />
}<br />
<br />
<br />
// Interrupt-Service-Routine Timer_2 Overflow<br />
SIGNAL (SIG_OVERFLOW2)<br />
{<br />
// ISR-Code 62.5 kHz<br />
// 4 Zustaende durch jeweils 1 Bit repräsentiert (1,2,4,8)<br />
// Lichtschranke A liegt an Port A.0<br />
// Lichtschranke B liegt an Port A.1<br />
<br />
if ( (Zust_Li == 1) && ( !(PINA & (1<<PINA0)) )&& ( PINA & (1<<PINA1) ) ){<br />
Li_Inkr = Li_Inkr + 1;<br />
Zust_Li = 2;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 1) && (PINA & (1<<PINA0) )&& ( !( PINA & (1<<PINA1)) ) ){<br />
Li_Inkr = Li_Inkr - 1;<br />
Zust_Li = 8;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 2) && (PINA & (1<<PINA0) )&& ( PINA & (1<<PINA1) ) ){<br />
Li_Inkr = Li_Inkr + 1;<br />
Zust_Li = 4;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 2) && ( !(PINA & (1<<PINA0)) )&& ( !( PINA & (1<<PINA1)) ) ){<br />
Li_Inkr = Li_Inkr - 1;<br />
Zust_Li = 1;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 4) && (PINA & (1<<PINA0) )&& ( !( PINA & (1<<PINA1)) ) ){<br />
Li_Inkr = Li_Inkr + 1;<br />
Zust_Li = 8;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 4) && ( !(PINA & (1<<PINA0)) )&& ( PINA & (1<<PINA1) ) ){<br />
Li_Inkr = Li_Inkr - 1;<br />
Zust_Li = 2;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 8) && ( !(PINA & (1<<PINA0)) )&& ( !( PINA & (1<<PINA1)) ) ){<br />
Li_Inkr = Li_Inkr + 1;<br />
Zust_Li = 1;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 8) && (PINA & (1<<PINA0) )&& ( PINA & (1<<PINA1) ) ){<br />
Li_Inkr = Li_Inkr - 1;<br />
Zust_Li = 4;<br />
} /* end if */<br />
<br />
}<br />
<br />
...<br />
<br />
int main (void)<br />
{<br />
<br />
//Init<br />
TCCR2 |= (1<<CS20); //Prescaler = 1 (16MHz/1 = 16000000) -><br />
//16000000/256 -> 62500 Interrupts je Sekunde<br />
<br />
TCCR0 |= (1<<CS02)|(1<<CS00); //Prescaler = 1024 (16000000 / 1024 = 15625) -><br />
//15625/256 = 61,035 -> 61 Interrupts je Sekunde<br />
<br />
TIMSK |= (1<<TOIE0)|(1<<TOIE2); //Timer Overflow Interrupt Enablae für Timer 0 und 2<br />
<br />
<br />
Li_Inkr = 32000;Zust_Li = 1; // Kalibrierstellung bei 32000<br />
sei(); // Interrupts aktivieren<br />
<br />
<br />
while(1)<br />
{<br />
<br />
// hier kann Position und Geschwindigkeit ausgewertet werden<br />
<br />
}<br />
}<br />
<br />
</pre><br />
<br />
= Vor und Nachteile der Messmethoden=</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Beispiel_Drehzahlmessung_mit_Drehgeber&diff=22754Beispiel Drehzahlmessung mit Drehgeber2013-06-29T16:49:35Z<p>Besserwessi: /* Zwei Lichtschranken liefern vier Informationen */</p>
<hr />
<div>[[Kategorie:Sensoren]] <br />
[[Kategorie:Praxis]]<br />
[[Kategorie:Motoren]] <br />
<br />
<br />
Ermitteln der Drehzahl und Wegstrecke in Abhängigkeit einer Winkeländerung oder Impulsfolge in einer Abgelaufenen Zeit<br />
<br />
* Messen der Drehzahl nach jedem Impuls, also in Abhängigkeit einer Winkeländerung.<br />
* Messen der Drehzahl durch Zählen der Impulse nach Ablauf einer bestimmten Zeit. <br />
* Richtung, Geschwindigkeit und Position mit Doppellichtschranke ermitteln<br />
{{Ausbauwunsch|Mehr Grundlagen und vor allem mal praktische Programmbeispiele / Algorithmen etc.}}<br />
= Messen der Drehzahl nach jedem Impuls =<br />
<br />
Dieser Teil beschäftigt sich vor allem mit der Optischen Drehzahlmessung mit Hilfe einer Lochscheibe und [[Gabellichtschranke]] berechnet über die Periodendauer ohne Drehrichtungserkennung.<br />
[[Bild:GP1S23 Testaufbau.JPG|thumb| Testaufbau mit einer 32er Lochscheibe ]]<br />
[[Bild:183015_LB_00_FB.EPS_250.jpg|thumb|GP1S23 Gabellichtschranke Bild: Conrad Electronic ]]<br />
<br />
<br />
== Einführung ==<br />
<br />
Am einfachsten ist Impulse direkt an einer Bürste eines Motors zu zählen. Siehe dazu: http://www.roboternetz.de/community/...einem-DC-Motor .<br />
<br />
Die [http://de.wikipedia.org/wiki/Winkelgeschwindigkeit Winkelgeschwindigkeit] ist die Winkeländerung pro Zeiteinheit. Mit der Lichtschranke und den geometrischen Daten der Lochscheibe kann man nun relativ einfach die Zeit für eine bestimmte Winkeländerung messen. <br />
Die Lochscheibe gibt uns die Winkeländerung bei einer Periode vor, bei einer 32er Lochscheibe wären das 11,25° pro Periode , wie in den Bildern vom Oszilloskop zu sehen ändert sich natürlich bei Änderung der Drehzahl die Periodendauer, die Winkeländerung pro Periode ist aber konstant da diese durch die Lochscheibe vor gegeben ist. <br />
<br />
<gallery><br />
Bild:GP1S23_signal_langsam.png|Signal der Lichtschranke bei langsamer Drehzahl <br />
Bild:GP1S23_signal_schnell.PNG |Signal der Lichtschranke bei schneller Drehzahl <br />
</gallery><br />
<br />
Um die Periodendauer zu messen verwende ich den ICP1 (Input Capture Pin) eines Atmega8 der bei steigender Flanke den Timer1 ausliest und in das ICR1 Register schreibt. Läuft der Mikrocontroller auf 8Mhz und wir stellen den Vorteiler des Timers auf 8 so bekommen wir die Periodendauer in µs als Wert.<br />
<br />
== Auslegen der Lochscheibe ==<br />
<br />
In den meisten Fällen definieren der mechanische Aufbau den Durchmesser der Lochscheibe und die Drehzahl die Teilung (Anzahl der Löcher). Der µContoller und die Gabellichtschranke müssen die Impulse auch erfassen und verarbeiten können. Wenn man das Drehzahlband abschätzen kann ist es hilfreich, sich schon Gedanken um die Programmierung zu machen, denn bei hohen Drehzahlen springt der µC jedes mal in die Interrupt Service Routine rein, sofern man es so programmiert wie im Beispiel. Bei hohen Drehzahlen sollte die Teilung somit gröber, bei niedriger Drehzahl die Teilung feiner sein. <br />
<br />
[[Bild:Drehscheibe20mm.png|400px|thumb|left|Lochscheibe abgerollt mit idealisiertem Signal ]]<br />
<br />
== Berechnungen ==<br />
<br />
Hier folgt nun die Berechnung über die Periodendauer, beachten sollte man das im [http://de.wikipedia.org/wiki/Bogenma%C3%9F Bogenmaß] gerechnet wird, ein Winkel mit dem Bogenmaß 1 rad hat ein Gradmaß von ca. 57,3°. 11.25° sind also ca. 0,196 rad, das führt später natürlich zu unschönen Rechenoperationen im µC, Stichwort [http://www.mikrocontroller.net/articles/Festkommaarithmetik Festkommaarithmetik]. <br />
<br />
Nach dem Umstellen kommt man aber auf eine recht handliche Formel <math>n = \frac{\varphi }{dt \cdot 360}</math><br />
<br />
Es ist auch eine Überlegung wert, ob man die Drehzahl nicht umrechnet sondern mit der Periodendauer arbeitet. Das ist zwar nicht so geläufig aber das stört den µController nicht. Mit einem Kalkulationsprogramm kann man die Umrechnung von Periodendauer in Drehzahl auch extern vornehmen. <br />
<br />
Man sollte sich auch klar machen, das zumindest ein 8-Bit Prozessor am besten mit '''Ganzzahlen''' zwischen 0 und 255 oder 0 und 65535 zurechtkommt. Für die Teilung der Lochscheibe bieten sich deshalb Werte wie 90, 72, 45, 36, 30, 24, 20, 18, 15, 12, 10 und 8 an. Verwendet man an Stelle der dargestellten 32er Lochscheibe eine 30er oder 36er Scheibe, kann man zumindest mit ganzzahligen Gradzahlen rechnen. "Krumme Werte" wie [http://de.wikipedia.org/wiki/Kreiszahl Pi = 3,1415...], [http://de.wikipedia.org/wiki/Radiant_%28Einheit%29 Radiant = 57,29577...°] usw. sollten eine seltene Ausnahme in der Programmierung sein.<br />
<br />
= Messen der Drehzahl durch zählen der Impulse =<br />
<br />
Dieser Teil beschäftigt sich mit der Optischen Drehzahlmessung mit Hilfe einer Lochscheibe und [[Gabellichtschranke]] berechnet über die die Anzahl der Impulse in einer gewissen Zeit. Geeignet ist diese Methode vor allem für schnell kommende Pulse, also hohe Drehzahlen bzw. Lochscheiben mit vielen Löchern. Die meisten µC bieten die Möglichkeit über einen Eingang direkt mit der Hardware externe Pulse zu zählen (z.B. beim AVR der Eingang T1). Über eine Vorgegeben Zeit von z.B. 1 Sekunde wird über den externen Eingang die Zahl der Pulse gezählt. Die Drehzahl ergibt sich dann einfach auch der Zahl der Impulse geteilt durch die Zahl der Löcher in der Scheibe und die Zeit für die Messung. Diese Methode liefert keine so gute Auflösung, liefert dafür aber direkt einen Wert Proportional zur Drehzahl. Auch ist bei geeigneter Hardware die Belastung für die CPU gering, es werden aber i.A. 2 Timer benötigt: der eine für das Zählen der Impulse und der andere für die Festlegung der des Zeitintervalls. Das Zeitintervall muss als Kompromiss zwischen Aktualisierungsfrequenz und Auflösung gewählt werden: mit einer Scheibe mit 32 Löchern erhält man in einer Sekunde ein Auflösung von 1/32 Umdrehungen pro Sekunde oder bei 1/32 Sekunde nur noch 1 Umdrehung pro Sekunde als Auflösung.<br />
<br />
= Richtung, Geschwindigkeit und Position mit Doppellichtschranke ermitteln =<br />
Dieser Teil beschäftigt sich mit der Optischen Drehzahl- und Geschwindigkeitsmessung sowie der Erfassung von Drehrichtung und zurückgelegter Position mit Hilfe einer Lochscheibe und 2 Gabellichtschranken. Hierfür werden Quadratursignale in definierten Zeitabständen ausgewertet.<br />
<br />
== Einführung ==<br />
<br />
Für kontrollierte Bewegungen ist es mitunter wichtig, auch die Drehrichtung/Richtungswechsel und Position auszuwerten. Eine Lochscheibe mit einer Gabellichtschranke liefert lediglich Rechteckimpulse, egal wie rum sich die Lochscheibe bewegt. Bringt man eine zweite Gabellichtschranke an der gleichen Lochscheibe leicht versetzt an, bekommt man auch die Drehrichtung mit. Diese Methode ist altbekannt, simpel und leicht nachzubauen. Wenn man die Drehrichtung kennt, ist auch die Position kein Problem mehr. Ausgehend von einer Kalibrierposition können bei Vorwärtsbewegung Inkremente hochgezählt oder bei Rückwärtsbewegung runtergezählt werden. <br />
<br />
Interessierte nehmen dazu mal eine PC-Maus mit Kugelantrieb zur Hand. Darin findet man zwei kleine Lochscheiben, die für die X- und Y-Richtung die Impulse erzeugen. Durch spezielle Gabellichtschranken mit 2 phasenversetzten Ausgängen wird auch die Drehrichtung erkannt. <br />
<br />
Das selbe Prinzip ist in linearer Form auch in PC-Druckern zu finden: Entlang des Wagenrücklaufes ist ein transparentes Kunsstoffband gespannt, auf dem winzig kleine Streifenmuster aufgebracht sind. Diese werden von einer Gabellichtschranke mit 2 Ausgängen erkannt und so die Wagenposition gesteuert.<br />
<br />
Aber es geht natürlich noch einfacher...<br />
<br />
== Zwei Lichtschranken liefern vier Zustände ==<br />
<br />
[[Bild:vorwaerts_.jpg|thumb|Zustandsfolge bei Vorwärtsbewegung]]<br />
[[Bild:rueckwaerts_.jpg|thumb|Zustandsfolge bei Rückwärtsbewegung]]<br />
[[Bild:2LSZustand.jpg|thumb|2 Lichtschranken (LSA und LSB) liefern phasenversetzte Rechtecksignale. In Vorwärts- und Rückwärtsrichtung ergeben sich unterschiedliche Zustandsfolgen]]<br />
<br />
Man nimmt 2 normale Gabellichtschranken und bringt sie so an, das sie im 1,5 fachen Lochabstand die Lochscheibe durchleuchten. Wenn sich Lichtschranke A in der Mitte des Lichtstreifens befindet, ist Lichtschranke B an der Grenze des Schattenstreifens. Nebenstehende Skizzen zeigen die möglichen Zustände beider Lichtschranken bei Vorwärts- und Rückwärtsbewegung der Lochscheibe. Wichtig ist hierbei, das die Lochscheibe in etwa gleich breite Licht- und Schattensegmente eingeteilt ist und die Lichtschranke(n) einen möglichst haarfeinen Lichtstrahl besitzt. Die Signale der Fototransistoren sollten verstärkt und gegebenenfalls mit TTL-Bausteinen zu korrekten High- und Low-Pegeln aufbereitet werden.<br />
<br />
<br />
Es ergeben sich nun folgende Kombinationen:<br />
<br />
* Zustand 1: LSA = hell und LSB = hell<br />
* Zustand 2: LSA = hell und LSB = dunkel<br />
* Zustand 3: LSA = dunkel und LSB = dunkel<br />
* Zustand 4: LSA = dunkel und LSB = hell<br />
<br />
Dreht man die Lochscheibe weiter, wiederholt sich der Vorgang. Die Zustandsfolge ist 1 > 2 > 3 > 4 > 1 > 2 > 3 > 4 > usw.<br />
<br />
<br />
<br />
Bei entgegengesetzter Drehrichtung ändert sich diese Reihenfolge. Betrachtet man wieder vom Zustand 1 ausgehend die Gegenrichtung, ergeben sich nun diese Kombinationen:<br />
<br />
* Zustand 1: LSA = hell und LSB = hell<br />
* Zustand 4: LSA = dunkel und LSB = hell<br />
* Zustand 3: LSA = dunkel und LSB = dunkel<br />
* Zustand 2: LSA = hell und LSB = dunkel<br />
<br />
Die Zustandsfolge in Gegenrichtung lautet dann: 1 < 4 < 3 < 2 < 1 < 4 < 3 < 2 < usw.<br />
<br />
<br />
Mit diesem Wissen ist es nun ein Kinderspiel, auch die Änderung der Drehrichtung festzustellen. Dazu muss der letzte Zustand in einer Variable gespeichert und mit dem neu erkannten Zustand verglichen werden. Folgt nach Zustand 1 der Zustand 2, so ist es eine Vorwärtsbewegung. Folgt dem Zustand 1 der Zustand 4, so ist es eine Rückwärtsbewegung. Gleiches Prinzip gilt auch für die restlichen Zustände 2, 3 und 4.<br />
<br />
== Positionsüberwachung ==<br />
<br />
Von jedem der 4 Zustände gibt es also zwei zu unterscheidende Fälle: Vorwärts und Rückwärts. Insgesamt müssen demzufolge 8 Bedingungen zyklisch abgetastet und ein Positionszähler entsprechend inkrementiert oder dekrementiert werden und schon hat man eine exakte Streckeninformation. In Worte gefasst ergibt sich folgende Logik:<br />
<br />
Ausgangszustand: Beide Lichtschranken sind aus (A=0 UND B=0) und Zustand =1<br />
<br />
* WENN (Zustand=1 UND A=0 UND B=1) DANN: Setze Zustand=2, Position inkrementieren, Vor<br />
* WENN (Zustand=1 UND A=1 UND B=0) DANN: Setze Zustand=4, Position dekrementieren, Rück<br />
* WENN (Zustand=2 UND A=1 UND B=1) DANN: Setze Zustand=3, Position inkrementieren, Vor<br />
* WENN (Zustand=2 UND A=0 UND B=0) DANN: Setze Zustand=1, Position dekrementieren, Rück<br />
* WENN (Zustand=3 UND A=1 UND B=0) DANN: Setze Zustand=4, Position inkrementieren, Vor<br />
* WENN (Zustand=3 UND A=0 UND B=1) DANN: Setze Zustand=2, Position dekrementieren, Rück<br />
* WENN (Zustand=4 UND A=0 UND B=0) DANN: Setze Zustand=1, Position inkrementieren, Vor<br />
* WENN (Zustand=4 UND A=1 UND B=1) DANN: Setze Zustand=3, Position dekrementieren, Rück<br />
[[Bild:Lochscheibe8.jpg|thumb|Lochscheibe 8 ]]<br />
[[Bild:Lochscheibe10.jpg|thumb|Lochscheibe 10 ]]<br />
[[Bild:Lochscheibe12.jpg|thumb|Lochscheibe 12 ]]<br />
[[Bild:Lochscheibe15.jpg|thumb|Lochscheibe 15 ]]<br />
[[Bild:Lochscheibe18.jpg|thumb|Lochscheibe 18 ]]<br />
[[Bild:Lochscheibe20.jpg|thumb|Lochscheibe 20 ]]<br />
<br />
<br />
Diese Prüfung muss ständig wiederholt werden, damit alle Pegelwechsel der beiden Lichtschranken erfasst werden. Nur so ist es möglich, die exakte Position zu verfolgen. Dafür bieten sich 2 Möglichkeiten: Entweder die Prüfung wird im Hauptprogramm ständig durchlaufen (Polling) oder ein Timer-Interrupt tastet regelmäßig die Lichtschranken ab. Hier soll nur die zweite, also Interruptvariante betrachtet werden, weil sie mehr Vorteile bringt. Weiter unten im Artikel ist dazu ein Beispiel in Sprache C zu finden. <br />
<br />
Zuvor muss aber eine sinnvolle Auslegung von Lochscheibe, Drehzahl und Positionsbereich gefunden und in Einklang mit den Ressourcen des verwendeten µControlles gebracht werden. Dazu folgende Betrachtungen:<br />
<br />
Wenn 2 phasenversetzte Lichtschranken je Hell-/ Dunkelzyklus bereits 4 Positionen liefern, benötigt die Lochscheibe nur halb so viel "Löcher" wie eine Lochscheibe bei Verwendung nur einer Lichtschranke. Eine 15er Lochscheibe liefert demzufolge 15x4 = 60 Positionen auf 360°, das sind 6° je erfasster Position. Entscheidend ist, an welchem Antriebsteil die Lochscheibe montiert ist und mit welcher Untersetzung zwischen Motor und Abtriebswelle(Rad) gearbeitet wird. Ist die Lochscheibe an der Motorwelle angebracht, würde bei einer angenommenen Untersetzung von 10:1 eine erfasste Position nur 0,6° an der Abtriebswelle ausmachen. <br />
<br />
Man muss sich klar machen, welche '''Positioniergenauigkeit''' nötig ist. Dabei gilt: Auslegung so genau wie nötig, nicht wie möglich. Weiter muss geklärt werden, wo man die Lochscheibe sinnvoll anbringt. Sie darf im Betrieb weder verschmutzt oder beschädigt werden. Fahrmodelle ziehen Staub, Haare etc. gern an, gut beraten ist man mit einer '''verkapselten Optomechanik'''. Ein weiterer Faktor ist die '''Drehzahl''' (im Leerlauf am höchsten) des Motors, weil diese die Frequenz der abzutastenden Signale beeinflusst. Zwischen Drehzahl n [U/min], Frequenz f [Hz] und Teilung T besteht folgender Zusammenhang:<br />
<br />
'''f = n * T / 60 s'''<br />
<br />
Beispiel: Ein Motor dreht im Leerlauf mit 2370 U/min und hat eine Lochscheibe mit Teilung = 12 an der Motorwelle. Die Frequenz der abzutastenden Signale einer Lichtschranke soll ermittelt werden. (oben im Osszillogramm dargestellt)<br />
<br />
'''f = 2370 * 12 / 60 s'''<br />
<br />
'''f = 474 Hz'''<br />
<br />
Eine Lichtschranke liefert also ein Rechtecksignal mit f = 474 Hz. Jetzt muss noch das Signal der zweiten Lichtschranke um 90° versetzt darübergelegt werden. Bei idealen Pegeln sind in einer Periode 4 Schaltzustände abzutasten, also muss die Abtastfrequenz mindestens 4-fach über der Grundfrequenz liegen. Da es in der Praxis aber keine idealen Pegel gibt, muss die Abtastfrequenz noch höher ausgelegt werden. Betrachten wir noch mal das Osszillogrammm weiter oben:<br />
<br />
Es fällt auf, das die Zeiten nicht exakt gleich sind, die Zustandszeiten 1 und 3 sind etwas länger als 2 und 4. Das liegt an der Einstellung des Abstandes beider Lichtschranken zueinander, die Phasenlage ist hier nicht genau 90°. Außerdem sind Lochscheibe und Lichtschranken mit kleinen Unregelmäßikeiten, Streuungen, Anstiegs- und Abfallzeiten behaftet, so dass es in einer Periode auch Zeiten gibt, die nicht eindeutig einem der 4 genannten Zustände entsprechen. Für eine sichere Abtastung ist deshalb von der kürzesten (= ungünstigsten) Schaltzeit auszugehen, hier sind es ca. 0,4 Millisekunden, das entspricht 2,5 kHz. Jeder Zustand sollte vom µController sicherheitshalber schon 3 bis 4 mal abgetastet werden, so das wir für das Beispiel mindestens ca. 10 kHz Abtastfrequenz einplanen müssen. Als Orientierungshilfe gilt:<br />
<br />
'''Abtastfrequenz >= 20 * Frequenz Lichtschranke'''<br />
<br />
Entsprechend der gewünschten Positioniergenauigkeit und Drehzahl kann nun die Teilung der Lochscheibe so gewählt werden, das die Lichtschrankensignale vom µController abgetastet werden können. Da Kaufteile aus dem Industriebereich oft eine recht hohe Teilung vorgeben, ist es manchmal besser, mit kleineren Teilungen anzufangen. Rechts sind Vorlagen, die man sich auf Folien ausdrucken kann.(Nähere Beschreibung im Praxis-Teil)<br />
<br />
Die Abtastung muss sicher sein, auch wenn das Hauptprogramm gerade mit anderen Dingen als der Lochscheibenauswertung beschäftigt ist. Man kann zwar die Lochscheibe vom Hauptprogramm durchaus mit abfragen lassen, aber dann sollten keine anderen zeitraubenden Prozeduren darin enthalten sein. Ist nur ein Zustandswechsel übersehen und ausgelassen wurden, geht es erst an der nachfolgenden gleichen Hell-/Dunkel-Kombination weiter und der Contoller verzählt sich um 4 Inkremente! Deshalb sollte die TimerInterrupt-Variante vorgezogen oder die Abtastung in einen separaten µController ausgelagert werden.<br />
<br />
Der Positionsbereich muss schließlich auch in einer Variablen dargestellt werden. Im Beispiel mit Teilung =12 wird der Positionzähler bei einer Motorumdrehung um 48 Inkremente hochgezählt. Bei 2370 U/min wäre eine 16Bit-Variable nach ca. 34 Sekunden übergelaufen. Demzufolge ist auch das Speichervolumen der Positionsvariablen dem tatsächlichen Fahrbereich anzupassen. Man kann auch Ober-/ Untergrenzen definieren, die bei Erreichen den Antrieb anhalten. Somit wird ein mechanisches Überfahren von Endlagen oder ein logisches Überlaufen von Variablen verhindert.<br />
<br />
<br />
'''Zusammenfassung:'''<br />
{| {{Blauetabelle}}<br />
|<br />
# Welche Positionsgenauigkeit brauch ich?<br />
# Sind Lochscheibe und Optik vor Störeinflüssen geschützt?<br />
# Welche maximale Drehzahl muss noch sicher abgetastet werden?<br />
# Frequenz Lichtschranke anpassen: Hohe Drehzahl mit kleiner Teilung / geringe Drehzahl mit großer Teilung<br />
# Phasenlage richtig eingestellt? Optimal sind 90°<br />
# Positionsvariable mit genügend Speichervolumen?<br />
|}<br />
<br />
== Kalibrierung ==<br />
<br />
Im einfachsten Fall fährt man seinem Antrieb auf eine Referenzmarke (Nonius, Paßstift o.ä.) und setzt dort den Positionszähler zurück. <br />
<br />
Wer es noch genauer will, muß auch noch die Lochscheibe in eine definierte Ausgangsstellung bringen. Dazu kann man die Lichtschrankensignale mit 2 LEDs optisch anzeigen. In der Kalibrierstellung müssen dann der Antrieb auf der Refernzmarke stehen und beide LEDs leuchten. Als Referenzmarke kann natürlich auch eine zusätzliche Lichtschranke dienen, die eine kleine Bohrung am Antrieb erkennt. Wichtig ist, dass die Referenzmarke nur einmal und eindeutig am Antrieb angebracht ist.<br />
<br />
== Geschwindigkeit und Beschleunigung ==<br />
<br />
Geschwindigkeit wird meist in [m/s], [km/h] oder [mph] ausgedrückt, also der Wegstrecke je Zeiteinheit. Im µController interessieren uns diese Einheiten erst mal nicht, hier werden Inkremente je Zeiteinheit gezählt. Die Zeitintervalle, in denen die Inkremente ausgewertet werden, liefert wieder ein TimerInterrupt. Dieser ist mit deutlich längeren Zyklen auszulegen, damit auch genügend Inkremente je Zyklus gezählt werden können. Im Code-Beispiel unten werden bei 16Mhz CPU-Takt die Vorteiler 1 und 1024 für die TimerInterrupt´s verwendet: Timer 0 mit Prescaler =1024 für die Geschwindigkeitsberechnung mit ca. 61 Hz und Timer 2 mit Prescaler =1 für die Abtastung der Lichtschranken mit 62,5 kHz. Die Geschwindigkeitsmessung wurde mit 12er, 15er und 18er Teilung getestet. In der ISR des Timer_0 Overflow wird die Differenz aus aktueller Position und der Position des vergangenen Zyklus gebildet. Da diese Wegdifferenz in definierten Zeitabständen ermittelt wird, hat man hier bereits die Geschwindigkeit in n Inkremente je 0,01638 Sekunden. Mit dieser etwas abstrakten Geschwindigkeitseinheit wird im Hauptprogramm der Motor gesteuert.<br />
<br />
Das gleiche Verfahren ist auch für die Beschleunigung anwendbar. Die Beschleunigung ist die Geschwindigkeitsänderung je Zeiteinheit. In der ISR des Timer_0 Overflow kann die Differenz aus aktueller Geschwindigkeit und der Geschwindigkeit des vergangenen Zyklus gebildet werden, um so die Beschleunigung zu erhalten. Die Einheit ist wieder etwas ungewohnt: Inkremente je 0,01638 Sekunden in 0,01638 Sekunden, also Inkremente/Quadratsekunden. Voraussetzung für eine vernünftige Beschleunigungsermittlung ist, das eine genügend hohe Auflösung der Geschwindigkeit vorliegt(=hohe Teilung der Lochscheibe). Ansonsten sind die ermittelten Werte zu klein und liegen, je nach Trägheit des Motors, fast immer bei 0, 1, 2 oder maximal 3.<br />
<br />
Vorteil der Differenzmethode ist, man kann die Geschwindigkeit in einer 8Bit-Variable mit Vorzeichen ausdrücken. Die Werte des Beispieles lagen je nach Teilung zwischen -50 und +50 und sind für die Motorsteuerung ausreichend. Bessere Auflösung erfordert höhere Teilung, höhere Abtastrate, höhere CPU-Frequenz.<br />
<br />
== Praxis==<br />
<br />
=== Herstellung der Lochscheibe ===<br />
Für eigene Projekte ist es mitunter schwer, Lochscheiben in passender Größe und Teilung zu finden. Oft müssen dann Kompromisse in der Drehzahlsteuerung oder Positionierung gemacht werden, weil die vielleicht günstigere Teilung nicht zu beschaffen ist. <br />
<br />
Hier wird deshalb ein Selbstbau von preiswerten Lochscheiben vorgestellt: Man trägt in einer Excel-Tabelle z.B. 18 mal die Zahl 20 ein und markiert diesen Zellbereich. Anschließend erstellt man mit dem Diagramm-Assistent ein Ring-Diagramm. Darin muss man nun jeden Datenpunkt einzeln formatieren und abwechslend die Farbe schwarz und weiß zuordnen. Markiert man die komplette Datenreihe, kann man unter 'Formatieren'>'Optionen' auch die Innenringgröße einstellen. Dieses Ring-Diagramm kann nun beliebig kopiert und vergößert oder verkleinert werden, auch die Datenreihe kann auf z.B. 24x15 oder 30x12 angepasst werden. So lassen sich auf einer A4-Seite einige Dutzend Lochscheiben in allen Größen und Teilungen herstellen. Das fertige Arbeitsblatt muss nun noch mit geeigneter Tinte auf Folie ausgedruckt werden. Den Drucker muss man möglichst auf satte Farbe und kräftgen Kontrast einstellen. Zum Schluß wird die ausgedruckte Folie mit A4- Laminierfolie verstärkt, wer kein Laminiergerät hat, geht in einen Copy-Shop o.ä. <br />
<br />
Die so gewonnenen Lochscheiben werden nun ausgeschnitten und bei Bedarf mit einem Heißluftgerät noch etwas "nachgebacken", bevor sie im eigenen Projekt mit Kraftkleber auf ein kleines Rad aufgeklebt werden. Hierbei auf gute Zentrierung achten, damit die Lochscheibe möglichst rund läuft. Sekundenkleber ist deshalb ungeeignet, weil kaum Zeit für Korrektur und Einstellung bleibt.<br />
<br />
=== Anpassen der Lichtschranke ===<br />
<br />
Baustelle:<br />
<br />
Fotos, Anregungen zum Nachbau, Beschaffung, Erfahrungen<br />
<br />
weiterführende Informationen:<br />
[http://de.wikipedia.org/wiki/Inkrementalgeber Inkrementalgeber]<br />
<br />
=== Quellcode ===<br />
<br />
<pre><br />
<br />
//Deklaration global<br />
volatile uint8_t Zust_Li; //Zustandsmerker Position linker Motor<br />
volatile uint16_t Li_Inkr; //aktuelle Position linker Motor<br />
volatile uint16_t old_Li_Inkr; //Position im vorangegangenen Takt<br />
volatile int8_t Speed_Li_Ist; //Wegänderung je Takt, in ISR Timer 0<br />
<br />
...<br />
<br />
// Interrupt-Service-Routine Timer_0 Overflow<br />
SIGNAL (SIG_OVERFLOW0)<br />
{<br />
// ISR-Code 61,035 Pro Sekunde<br />
Timeout++;<br />
Speed_Li_Ist = Li_Inkr - old_Li_Inkr;<br />
old_Li_Inkr = Li_Inkr;<br />
}<br />
<br />
<br />
// Interrupt-Service-Routine Timer_2 Overflow<br />
SIGNAL (SIG_OVERFLOW2)<br />
{<br />
// ISR-Code 62.5 kHz<br />
// 4 Zustaende durch jeweils 1 Bit repräsentiert (1,2,4,8)<br />
// Lichtschranke A liegt an Port A.0<br />
// Lichtschranke B liegt an Port A.1<br />
<br />
if ( (Zust_Li == 1) && ( !(PINA & (1<<PINA0)) )&& ( PINA & (1<<PINA1) ) ){<br />
Li_Inkr = Li_Inkr + 1;<br />
Zust_Li = 2;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 1) && (PINA & (1<<PINA0) )&& ( !( PINA & (1<<PINA1)) ) ){<br />
Li_Inkr = Li_Inkr - 1;<br />
Zust_Li = 8;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 2) && (PINA & (1<<PINA0) )&& ( PINA & (1<<PINA1) ) ){<br />
Li_Inkr = Li_Inkr + 1;<br />
Zust_Li = 4;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 2) && ( !(PINA & (1<<PINA0)) )&& ( !( PINA & (1<<PINA1)) ) ){<br />
Li_Inkr = Li_Inkr - 1;<br />
Zust_Li = 1;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 4) && (PINA & (1<<PINA0) )&& ( !( PINA & (1<<PINA1)) ) ){<br />
Li_Inkr = Li_Inkr + 1;<br />
Zust_Li = 8;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 4) && ( !(PINA & (1<<PINA0)) )&& ( PINA & (1<<PINA1) ) ){<br />
Li_Inkr = Li_Inkr - 1;<br />
Zust_Li = 2;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 8) && ( !(PINA & (1<<PINA0)) )&& ( !( PINA & (1<<PINA1)) ) ){<br />
Li_Inkr = Li_Inkr + 1;<br />
Zust_Li = 1;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 8) && (PINA & (1<<PINA0) )&& ( PINA & (1<<PINA1) ) ){<br />
Li_Inkr = Li_Inkr - 1;<br />
Zust_Li = 4;<br />
} /* end if */<br />
<br />
}<br />
<br />
...<br />
<br />
int main (void)<br />
{<br />
<br />
//Init<br />
TCCR2 |= (1<<CS20); //Prescaler = 1 (16MHz/1 = 16000000) -><br />
//16000000/256 -> 62500 Interrupts je Sekunde<br />
<br />
TCCR0 |= (1<<CS02)|(1<<CS00); //Prescaler = 1024 (16000000 / 1024 = 15625) -><br />
//15625/256 = 61,035 -> 61 Interrupts je Sekunde<br />
<br />
TIMSK |= (1<<TOIE0)|(1<<TOIE2); //Timer Overflow Interrupt Enablae für Timer 0 und 2<br />
<br />
<br />
Li_Inkr = 32000;Zust_Li = 1; // Kalibrierstellung bei 32000<br />
sei(); // Interrupts aktivieren<br />
<br />
<br />
while(1)<br />
{<br />
<br />
// hier kann Position und Geschwindigkeit ausgewertet werden<br />
<br />
}<br />
}<br />
<br />
</pre><br />
<br />
= Vor und Nachteile der Messmethoden=</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Beispiel_Drehzahlmessung_mit_Drehgeber&diff=22753Beispiel Drehzahlmessung mit Drehgeber2013-06-29T16:44:53Z<p>Besserwessi: /* Messen der Drehzahl durch zählen der Impulse */</p>
<hr />
<div>[[Kategorie:Sensoren]] <br />
[[Kategorie:Praxis]]<br />
[[Kategorie:Motoren]] <br />
<br />
<br />
Ermitteln der Drehzahl und Wegstrecke in Abhängigkeit einer Winkeländerung oder Impulsfolge in einer Abgelaufenen Zeit<br />
<br />
* Messen der Drehzahl nach jedem Impuls, also in Abhängigkeit einer Winkeländerung.<br />
* Messen der Drehzahl durch Zählen der Impulse nach Ablauf einer bestimmten Zeit. <br />
* Richtung, Geschwindigkeit und Position mit Doppellichtschranke ermitteln<br />
{{Ausbauwunsch|Mehr Grundlagen und vor allem mal praktische Programmbeispiele / Algorithmen etc.}}<br />
= Messen der Drehzahl nach jedem Impuls =<br />
<br />
Dieser Teil beschäftigt sich vor allem mit der Optischen Drehzahlmessung mit Hilfe einer Lochscheibe und [[Gabellichtschranke]] berechnet über die Periodendauer ohne Drehrichtungserkennung.<br />
[[Bild:GP1S23 Testaufbau.JPG|thumb| Testaufbau mit einer 32er Lochscheibe ]]<br />
[[Bild:183015_LB_00_FB.EPS_250.jpg|thumb|GP1S23 Gabellichtschranke Bild: Conrad Electronic ]]<br />
<br />
<br />
== Einführung ==<br />
<br />
Am einfachsten ist Impulse direkt an einer Bürste eines Motors zu zählen. Siehe dazu: http://www.roboternetz.de/community/...einem-DC-Motor .<br />
<br />
Die [http://de.wikipedia.org/wiki/Winkelgeschwindigkeit Winkelgeschwindigkeit] ist die Winkeländerung pro Zeiteinheit. Mit der Lichtschranke und den geometrischen Daten der Lochscheibe kann man nun relativ einfach die Zeit für eine bestimmte Winkeländerung messen. <br />
Die Lochscheibe gibt uns die Winkeländerung bei einer Periode vor, bei einer 32er Lochscheibe wären das 11,25° pro Periode , wie in den Bildern vom Oszilloskop zu sehen ändert sich natürlich bei Änderung der Drehzahl die Periodendauer, die Winkeländerung pro Periode ist aber konstant da diese durch die Lochscheibe vor gegeben ist. <br />
<br />
<gallery><br />
Bild:GP1S23_signal_langsam.png|Signal der Lichtschranke bei langsamer Drehzahl <br />
Bild:GP1S23_signal_schnell.PNG |Signal der Lichtschranke bei schneller Drehzahl <br />
</gallery><br />
<br />
Um die Periodendauer zu messen verwende ich den ICP1 (Input Capture Pin) eines Atmega8 der bei steigender Flanke den Timer1 ausliest und in das ICR1 Register schreibt. Läuft der Mikrocontroller auf 8Mhz und wir stellen den Vorteiler des Timers auf 8 so bekommen wir die Periodendauer in µs als Wert.<br />
<br />
== Auslegen der Lochscheibe ==<br />
<br />
In den meisten Fällen definieren der mechanische Aufbau den Durchmesser der Lochscheibe und die Drehzahl die Teilung (Anzahl der Löcher). Der µContoller und die Gabellichtschranke müssen die Impulse auch erfassen und verarbeiten können. Wenn man das Drehzahlband abschätzen kann ist es hilfreich, sich schon Gedanken um die Programmierung zu machen, denn bei hohen Drehzahlen springt der µC jedes mal in die Interrupt Service Routine rein, sofern man es so programmiert wie im Beispiel. Bei hohen Drehzahlen sollte die Teilung somit gröber, bei niedriger Drehzahl die Teilung feiner sein. <br />
<br />
[[Bild:Drehscheibe20mm.png|400px|thumb|left|Lochscheibe abgerollt mit idealisiertem Signal ]]<br />
<br />
== Berechnungen ==<br />
<br />
Hier folgt nun die Berechnung über die Periodendauer, beachten sollte man das im [http://de.wikipedia.org/wiki/Bogenma%C3%9F Bogenmaß] gerechnet wird, ein Winkel mit dem Bogenmaß 1 rad hat ein Gradmaß von ca. 57,3°. 11.25° sind also ca. 0,196 rad, das führt später natürlich zu unschönen Rechenoperationen im µC, Stichwort [http://www.mikrocontroller.net/articles/Festkommaarithmetik Festkommaarithmetik]. <br />
<br />
Nach dem Umstellen kommt man aber auf eine recht handliche Formel <math>n = \frac{\varphi }{dt \cdot 360}</math><br />
<br />
Es ist auch eine Überlegung wert, ob man die Drehzahl nicht umrechnet sondern mit der Periodendauer arbeitet. Das ist zwar nicht so geläufig aber das stört den µController nicht. Mit einem Kalkulationsprogramm kann man die Umrechnung von Periodendauer in Drehzahl auch extern vornehmen. <br />
<br />
Man sollte sich auch klar machen, das zumindest ein 8-Bit Prozessor am besten mit '''Ganzzahlen''' zwischen 0 und 255 oder 0 und 65535 zurechtkommt. Für die Teilung der Lochscheibe bieten sich deshalb Werte wie 90, 72, 45, 36, 30, 24, 20, 18, 15, 12, 10 und 8 an. Verwendet man an Stelle der dargestellten 32er Lochscheibe eine 30er oder 36er Scheibe, kann man zumindest mit ganzzahligen Gradzahlen rechnen. "Krumme Werte" wie [http://de.wikipedia.org/wiki/Kreiszahl Pi = 3,1415...], [http://de.wikipedia.org/wiki/Radiant_%28Einheit%29 Radiant = 57,29577...°] usw. sollten eine seltene Ausnahme in der Programmierung sein.<br />
<br />
= Messen der Drehzahl durch zählen der Impulse =<br />
<br />
Dieser Teil beschäftigt sich mit der Optischen Drehzahlmessung mit Hilfe einer Lochscheibe und [[Gabellichtschranke]] berechnet über die die Anzahl der Impulse in einer gewissen Zeit. Geeignet ist diese Methode vor allem für schnell kommende Pulse, also hohe Drehzahlen bzw. Lochscheiben mit vielen Löchern. Die meisten µC bieten die Möglichkeit über einen Eingang direkt mit der Hardware externe Pulse zu zählen (z.B. beim AVR der Eingang T1). Über eine Vorgegeben Zeit von z.B. 1 Sekunde wird über den externen Eingang die Zahl der Pulse gezählt. Die Drehzahl ergibt sich dann einfach auch der Zahl der Impulse geteilt durch die Zahl der Löcher in der Scheibe und die Zeit für die Messung. Diese Methode liefert keine so gute Auflösung, liefert dafür aber direkt einen Wert Proportional zur Drehzahl. Auch ist bei geeigneter Hardware die Belastung für die CPU gering, es werden aber i.A. 2 Timer benötigt: der eine für das Zählen der Impulse und der andere für die Festlegung der des Zeitintervalls. Das Zeitintervall muss als Kompromiss zwischen Aktualisierungsfrequenz und Auflösung gewählt werden: mit einer Scheibe mit 32 Löchern erhält man in einer Sekunde ein Auflösung von 1/32 Umdrehungen pro Sekunde oder bei 1/32 Sekunde nur noch 1 Umdrehung pro Sekunde als Auflösung.<br />
<br />
= Richtung, Geschwindigkeit und Position mit Doppellichtschranke ermitteln =<br />
Dieser Teil beschäftigt sich mit der Optischen Drehzahl- und Geschwindigkeitsmessung sowie der Erfassung von Drehrichtung und zurückgelegter Position mit Hilfe einer Lochscheibe und 2 Gabellichtschranken. Hierfür werden Quadratursignale in definierten Zeitabständen ausgewertet.<br />
<br />
== Einführung ==<br />
<br />
Für kontrollierte Bewegungen ist es mitunter wichtig, auch die Drehrichtung/Richtungswechsel und Position auszuwerten. Eine Lochscheibe mit einer Gabellichtschranke liefert lediglich Rechteckimpulse, egal wie rum sich die Lochscheibe bewegt. Bringt man eine zweite Gabellichtschranke an der gleichen Lochscheibe leicht versetzt an, bekommt man auch die Drehrichtung mit. Diese Methode ist altbekannt, simpel und leicht nachzubauen. Wenn man die Drehrichtung kennt, ist auch die Position kein Problem mehr. Ausgehend von einer Kalibrierposition können bei Vorwärtsbewegung Inkremente hochgezählt oder bei Rückwärtsbewegung runtergezählt werden. <br />
<br />
Interessierte nehmen dazu mal eine PC-Maus mit Kugelantrieb zur Hand. Darin findet man zwei kleine Lochscheiben, die für die X- und Y-Richtung die Impulse erzeugen. Durch spezielle Gabellichtschranken mit 2 phasenversetzten Ausgängen wird auch die Drehrichtung erkannt. <br />
<br />
Das selbe Prinzip ist in linearer Form auch in PC-Druckern zu finden: Entlang des Wagenrücklaufes ist ein transparentes Kunsstoffband gespannt, auf dem winzig kleine Streifenmuster aufgebracht sind. Diese werden von einer Gabellichtschranke mit 2 Ausgängen erkannt und so die Wagenposition gesteuert.<br />
<br />
Aber es geht natürlich noch einfacher...<br />
<br />
== Zwei Lichtschranken liefern vier Informationen ==<br />
<br />
[[Bild:vorwaerts_.jpg|thumb|Zustandsfolge bei Vorwärtsbewegung]]<br />
[[Bild:rueckwaerts_.jpg|thumb|Zustandsfolge bei Rückwärtsbewegung]]<br />
[[Bild:2LSZustand.jpg|thumb|2 Lichtschranken (LSA und LSB) liefern phasenversetzte Rechtecksignale. In Vorwärts- und Rückwärtsrichtung ergeben sich unterschiedliche Zustandsfolgen]]<br />
<br />
Man nimmt 2 normale Gabellichtschranken und bringt sie so an, das sie im 1,5 fachen Lochabstand die Lochscheibe durchleuchten. Wenn sich Lichtschranke A in der Mitte des Lichtstreifens befindet, ist Lichtschranke B an der Grenze des Schattenstreifens. Nebenstehende Skizzen zeigen die möglichen Zustände beider Lichtschranken bei Vorwärts- und Rückwärtsbewgung der Lochscheibe. Wichtig ist hierbei, das die Lochscheibe in gleich breite Licht- und Schattensegmente eingeteilt ist und die Lichtschranke(n) einen möglichst haarfeinen Lichtstrahl besitzt. Die Signale der Fototransistoren sollten verstärkt und gegebenenfalls mit TTL-Bausteinen zu korrekten High- und Low-Pegeln aufbereitet werden.<br />
<br />
<br />
<br />
Es ergeben sich nun folgende Kombinationen:<br />
<br />
* Zustand 1: LSA = hell und LSB = hell<br />
* Zustand 2: LSA = hell und LSB = dunkel<br />
* Zustand 3: LSA = dunkel und LSB = dunkel<br />
* Zustand 4: LSA = dunkel und LSB = hell<br />
<br />
Dreht man die Lochscheibe weiter, wiederholt sich der Vorgang. Die Zustandsfolge ist 1 > 2 > 3 > 4 > 1 > 2 > 3 > 4 > usw.<br />
<br />
<br />
<br />
Bei entgegengesetzter Drehrichtung ändert sich diese Reihenfolge. Betrachtet man wieder vom Zustand 1 ausgehend die Gegenrichtung, ergeben sich nun diese Kombinationen:<br />
<br />
* Zustand 1: LSA = hell und LSB = hell<br />
* Zustand 4: LSA = dunkel und LSB = hell<br />
* Zustand 3: LSA = dunkel und LSB = dunkel<br />
* Zustand 2: LSA = hell und LSB = dunkel<br />
<br />
Die Zustandsfolge in Gegenrichtung lautet dann: 1 < 4 < 3 < 2 < 1 < 4 < 3 < 2 < usw.<br />
<br />
<br />
Mit diesem Wissen ist es nun ein Kinderspiel, auch die Änderung der Drehrichtung festzustellen. Dazu muss der letzte Zustand in einer Variable gespeichert und mit dem neu erkannten Zustand verglichen werden. Folgt nach Zustand 1 der Zustand 2, so ist es eine Vorwärtsbewegung. Folgt dem Zustand 1 der Zustand 4, so ist es eine Rückwärtsbewegung. Gleiches Prinzip gilt auch für die restlichen Zustände 2, 3 und 4.<br />
<br />
<br />
<br />
<br />
== Positionsüberwachung ==<br />
<br />
Von jedem der 4 Zustände gibt es also zwei zu unterscheidende Fälle: Vorwärts und Rückwärts. Insgesamt müssen demzufolge 8 Bedingungen zyklisch abgetastet und ein Positionszähler entsprechend inkrementiert oder dekrementiert werden und schon hat man eine exakte Streckeninformation. In Worte gefasst ergibt sich folgende Logik:<br />
<br />
Ausgangszustand: Beide Lichtschranken sind aus (A=0 UND B=0) und Zustand =1<br />
<br />
* WENN (Zustand=1 UND A=0 UND B=1) DANN: Setze Zustand=2, Position inkrementieren, Vor<br />
* WENN (Zustand=1 UND A=1 UND B=0) DANN: Setze Zustand=4, Position dekrementieren, Rück<br />
* WENN (Zustand=2 UND A=1 UND B=1) DANN: Setze Zustand=3, Position inkrementieren, Vor<br />
* WENN (Zustand=2 UND A=0 UND B=0) DANN: Setze Zustand=1, Position dekrementieren, Rück<br />
* WENN (Zustand=3 UND A=1 UND B=0) DANN: Setze Zustand=4, Position inkrementieren, Vor<br />
* WENN (Zustand=3 UND A=0 UND B=1) DANN: Setze Zustand=2, Position dekrementieren, Rück<br />
* WENN (Zustand=4 UND A=0 UND B=0) DANN: Setze Zustand=1, Position inkrementieren, Vor<br />
* WENN (Zustand=4 UND A=1 UND B=1) DANN: Setze Zustand=3, Position dekrementieren, Rück<br />
[[Bild:Lochscheibe8.jpg|thumb|Lochscheibe 8 ]]<br />
[[Bild:Lochscheibe10.jpg|thumb|Lochscheibe 10 ]]<br />
[[Bild:Lochscheibe12.jpg|thumb|Lochscheibe 12 ]]<br />
[[Bild:Lochscheibe15.jpg|thumb|Lochscheibe 15 ]]<br />
[[Bild:Lochscheibe18.jpg|thumb|Lochscheibe 18 ]]<br />
[[Bild:Lochscheibe20.jpg|thumb|Lochscheibe 20 ]]<br />
<br />
<br />
Diese Prüfung muss ständig wiederholt werden, damit alle Pegelwechsel der beiden Lichtschranken erfasst werden. Nur so ist es möglich, die exakte Position zu verfolgen. Dafür bieten sich 2 Möglichkeiten: Entweder die Prüfung wird im Hauptprogramm ständig durchlaufen (Polling) oder ein Timer-Interrupt tastet regelmäßig die Lichtschranken ab. Hier soll nur die zweite, also Interruptvariante betrachtet werden, weil sie mehr Vorteile bringt. Weiter unten im Artikel ist dazu ein Beispiel in Sprache C zu finden. <br />
<br />
Zuvor muss aber eine sinnvolle Auslegung von Lochscheibe, Drehzahl und Positionsbereich gefunden und in Einklang mit den Ressourcen des verwendeten µControlles gebracht werden. Dazu folgende Betrachtungen:<br />
<br />
Wenn 2 phasenversetzte Lichtschranken je Hell-/ Dunkelzyklus bereits 4 Positionen liefern, benötigt die Lochscheibe nur halb so viel "Löcher" wie eine Lochscheibe bei Verwendung nur einer Lichtschranke. Eine 15er Lochscheibe liefert demzufolge 15x4 = 60 Positionen auf 360°, das sind 6° je erfasster Position. Entscheidend ist, an welchem Antriebsteil die Lochscheibe montiert ist und mit welcher Untersetzung zwischen Motor und Abtriebswelle(Rad) gearbeitet wird. Ist die Lochscheibe an der Motorwelle angebracht, würde bei einer angenommenen Untersetzung von 10:1 eine erfasste Position nur 0,6° an der Abtriebswelle ausmachen. <br />
<br />
Man muss sich klar machen, welche '''Positioniergenauigkeit''' nötig ist. Dabei gilt: Auslegung so genau wie nötig, nicht wie möglich. Weiter muss geklärt werden, wo man die Lochscheibe sinnvoll anbringt. Sie darf im Betrieb weder verschmutzt oder beschädigt werden. Fahrmodelle ziehen Staub, Haare etc. gern an, gut beraten ist man mit einer '''verkapselten Optomechanik'''. Ein weiterer Faktor ist die '''Drehzahl''' (im Leerlauf am höchsten) des Motors, weil diese die Frequenz der abzutastenden Signale beeinflusst. Zwischen Drehzahl n [U/min], Frequenz f [Hz] und Teilung T besteht folgender Zusammenhang:<br />
<br />
'''f = n * T / 60 s'''<br />
<br />
Beispiel: Ein Motor dreht im Leerlauf mit 2370 U/min und hat eine Lochscheibe mit Teilung = 12 an der Motorwelle. Die Frequenz der abzutastenden Signale einer Lichtschranke soll ermittelt werden. (oben im Osszillogramm dargestellt)<br />
<br />
'''f = 2370 * 12 / 60 s'''<br />
<br />
'''f = 474 Hz'''<br />
<br />
Eine Lichtschranke liefert also ein Rechtecksignal mit f = 474 Hz. Jetzt muss noch das Signal der zweiten Lichtschranke um 90° versetzt darübergelegt werden. Bei idealen Pegeln sind in einer Periode 4 Schaltzustände abzutasten, also muss die Abtastfrequenz mindestens 4-fach über der Grundfrequenz liegen. Da es in der Praxis aber keine idealen Pegel gibt, muss die Abtastfrequenz noch höher ausgelegt werden. Betrachten wir noch mal das Osszillogrammm weiter oben:<br />
<br />
Es fällt auf, das die Zeiten nicht exakt gleich sind, die Zustandszeiten 1 und 3 sind etwas länger als 2 und 4. Das liegt an der Einstellung des Abstandes beider Lichtschranken zueinander, die Phasenlage ist hier nicht genau 90°. Außerdem sind Lochscheibe und Lichtschranken mit kleinen Unregelmäßikeiten, Streuungen, Anstiegs- und Abfallzeiten behaftet, so dass es in einer Periode auch Zeiten gibt, die nicht eindeutig einem der 4 genannten Zustände entsprechen. Für eine sichere Abtastung ist deshalb von der kürzesten (= ungünstigsten) Schaltzeit auszugehen, hier sind es ca. 0,4 Millisekunden, das entspricht 2,5 kHz. Jeder Zustand sollte vom µController sicherheitshalber schon 3 bis 4 mal abgetastet werden, so das wir für das Beispiel mindestens ca. 10 kHz Abtastfrequenz einplanen müssen. Als Orientierungshilfe gilt:<br />
<br />
'''Abtastfrequenz >= 20 * Frequenz Lichtschranke'''<br />
<br />
Entsprechend der gewünschten Positioniergenauigkeit und Drehzahl kann nun die Teilung der Lochscheibe so gewählt werden, das die Lichtschrankensignale vom µController abgetastet werden können. Da Kaufteile aus dem Industriebereich oft eine recht hohe Teilung vorgeben, ist es manchmal besser, mit kleineren Teilungen anzufangen. Rechts sind Vorlagen, die man sich auf Folien ausdrucken kann.(Nähere Beschreibung im Praxis-Teil)<br />
<br />
Die Abtastung muss sicher sein, auch wenn das Hauptprogramm gerade mit anderen Dingen als der Lochscheibenauswertung beschäftigt ist. Man kann zwar die Lochscheibe vom Hauptprogramm durchaus mit abfragen lassen, aber dann sollten keine anderen zeitraubenden Prozeduren darin enthalten sein. Ist nur ein Zustandswechsel übersehen und ausgelassen wurden, geht es erst an der nachfolgenden gleichen Hell-/Dunkel-Kombination weiter und der Contoller verzählt sich um 4 Inkremente! Deshalb sollte die TimerInterrupt-Variante vorgezogen oder die Abtastung in einen separaten µController ausgelagert werden.<br />
<br />
Der Positionsbereich muss schließlich auch in einer Variablen dargestellt werden. Im Beispiel mit Teilung =12 wird der Positionzähler bei einer Motorumdrehung um 48 Inkremente hochgezählt. Bei 2370 U/min wäre eine 16Bit-Variable nach ca. 34 Sekunden übergelaufen. Demzufolge ist auch das Speichervolumen der Positionsvariablen dem tatsächlichen Fahrbereich anzupassen. Man kann auch Ober-/ Untergrenzen definieren, die bei Erreichen den Antrieb anhalten. Somit wird ein mechanisches Überfahren von Endlagen oder ein logisches Überlaufen von Variablen verhindert.<br />
<br />
<br />
'''Zusammenfassung:'''<br />
{| {{Blauetabelle}}<br />
|<br />
# Welche Positionsgenauigkeit brauch ich?<br />
# Sind Lochscheibe und Optik vor Störeinflüssen geschützt?<br />
# Welche maximale Drehzahl muss noch sicher abgetastet werden?<br />
# Frequenz Lichtschranke anpassen: Hohe Drehzahl mit kleiner Teilung / geringe Drehzahl mit großer Teilung<br />
# Phasenlage richtig eingestellt? Optimal sind 90°<br />
# Positionsvariable mit genügend Speichervolumen?<br />
|}<br />
<br />
== Kalibrierung ==<br />
<br />
Im einfachsten Fall fährt man seinem Antrieb auf eine Referenzmarke (Nonius, Paßstift o.ä.) und setzt dort den Positionszähler zurück. <br />
<br />
Wer es noch genauer will, muß auch noch die Lochscheibe in eine definierte Ausgangsstellung bringen. Dazu kann man die Lichtschrankensignale mit 2 LEDs optisch anzeigen. In der Kalibrierstellung müssen dann der Antrieb auf der Refernzmarke stehen und beide LEDs leuchten. Als Referenzmarke kann natürlich auch eine zusätzliche Lichtschranke dienen, die eine kleine Bohrung am Antrieb erkennt. Wichtig ist, dass die Referenzmarke nur einmal und eindeutig am Antrieb angebracht ist.<br />
<br />
== Geschwindigkeit und Beschleunigung ==<br />
<br />
Geschwindigkeit wird meist in [m/s], [km/h] oder [mph] ausgedrückt, also der Wegstrecke je Zeiteinheit. Im µController interessieren uns diese Einheiten erst mal nicht, hier werden Inkremente je Zeiteinheit gezählt. Die Zeitintervalle, in denen die Inkremente ausgewertet werden, liefert wieder ein TimerInterrupt. Dieser ist mit deutlich längeren Zyklen auszulegen, damit auch genügend Inkremente je Zyklus gezählt werden können. Im Code-Beispiel unten werden bei 16Mhz CPU-Takt die Vorteiler 1 und 1024 für die TimerInterrupt´s verwendet: Timer 0 mit Prescaler =1024 für die Geschwindigkeitsberechnung mit ca. 61 Hz und Timer 2 mit Prescaler =1 für die Abtastung der Lichtschranken mit 62,5 kHz. Die Geschwindigkeitsmessung wurde mit 12er, 15er und 18er Teilung getestet. In der ISR des Timer_0 Overflow wird die Differenz aus aktueller Position und der Position des vergangenen Zyklus gebildet. Da diese Wegdifferenz in definierten Zeitabständen ermittelt wird, hat man hier bereits die Geschwindigkeit in n Inkremente je 0,01638 Sekunden. Mit dieser etwas abstrakten Geschwindigkeitseinheit wird im Hauptprogramm der Motor gesteuert.<br />
<br />
Das gleiche Verfahren ist auch für die Beschleunigung anwendbar. Die Beschleunigung ist die Geschwindigkeitsänderung je Zeiteinheit. In der ISR des Timer_0 Overflow kann die Differenz aus aktueller Geschwindigkeit und der Geschwindigkeit des vergangenen Zyklus gebildet werden, um so die Beschleunigung zu erhalten. Die Einheit ist wieder etwas ungewohnt: Inkremente je 0,01638 Sekunden in 0,01638 Sekunden, also Inkremente/Quadratsekunden. Voraussetzung für eine vernünftige Beschleunigungsermittlung ist, das eine genügend hohe Auflösung der Geschwindigkeit vorliegt(=hohe Teilung der Lochscheibe). Ansonsten sind die ermittelten Werte zu klein und liegen, je nach Trägheit des Motors, fast immer bei 0, 1, 2 oder maximal 3.<br />
<br />
Vorteil der Differenzmethode ist, man kann die Geschwindigkeit in einer 8Bit-Variable mit Vorzeichen ausdrücken. Die Werte des Beispieles lagen je nach Teilung zwischen -50 und +50 und sind für die Motorsteuerung ausreichend. Bessere Auflösung erfordert höhere Teilung, höhere Abtastrate, höhere CPU-Frequenz.<br />
<br />
== Praxis==<br />
<br />
=== Herstellung der Lochscheibe ===<br />
Für eigene Projekte ist es mitunter schwer, Lochscheiben in passender Größe und Teilung zu finden. Oft müssen dann Kompromisse in der Drehzahlsteuerung oder Positionierung gemacht werden, weil die vielleicht günstigere Teilung nicht zu beschaffen ist. <br />
<br />
Hier wird deshalb ein Selbstbau von preiswerten Lochscheiben vorgestellt: Man trägt in einer Excel-Tabelle z.B. 18 mal die Zahl 20 ein und markiert diesen Zellbereich. Anschließend erstellt man mit dem Diagramm-Assistent ein Ring-Diagramm. Darin muss man nun jeden Datenpunkt einzeln formatieren und abwechslend die Farbe schwarz und weiß zuordnen. Markiert man die komplette Datenreihe, kann man unter 'Formatieren'>'Optionen' auch die Innenringgröße einstellen. Dieses Ring-Diagramm kann nun beliebig kopiert und vergößert oder verkleinert werden, auch die Datenreihe kann auf z.B. 24x15 oder 30x12 angepasst werden. So lassen sich auf einer A4-Seite einige Dutzend Lochscheiben in allen Größen und Teilungen herstellen. Das fertige Arbeitsblatt muss nun noch mit geeigneter Tinte auf Folie ausgedruckt werden. Den Drucker muss man möglichst auf satte Farbe und kräftgen Kontrast einstellen. Zum Schluß wird die ausgedruckte Folie mit A4- Laminierfolie verstärkt, wer kein Laminiergerät hat, geht in einen Copy-Shop o.ä. <br />
<br />
Die so gewonnenen Lochscheiben werden nun ausgeschnitten und bei Bedarf mit einem Heißluftgerät noch etwas "nachgebacken", bevor sie im eigenen Projekt mit Kraftkleber auf ein kleines Rad aufgeklebt werden. Hierbei auf gute Zentrierung achten, damit die Lochscheibe möglichst rund läuft. Sekundenkleber ist deshalb ungeeignet, weil kaum Zeit für Korrektur und Einstellung bleibt.<br />
<br />
=== Anpassen der Lichtschranke ===<br />
<br />
Baustelle:<br />
<br />
Fotos, Anregungen zum Nachbau, Beschaffung, Erfahrungen<br />
<br />
weiterführende Informationen:<br />
[http://de.wikipedia.org/wiki/Inkrementalgeber Inkrementalgeber]<br />
<br />
=== Quellcode ===<br />
<br />
<pre><br />
<br />
//Deklaration global<br />
volatile uint8_t Zust_Li; //Zustandsmerker Position linker Motor<br />
volatile uint16_t Li_Inkr; //aktuelle Position linker Motor<br />
volatile uint16_t old_Li_Inkr; //Position im vorangegangenen Takt<br />
volatile int8_t Speed_Li_Ist; //Wegänderung je Takt, in ISR Timer 0<br />
<br />
...<br />
<br />
// Interrupt-Service-Routine Timer_0 Overflow<br />
SIGNAL (SIG_OVERFLOW0)<br />
{<br />
// ISR-Code 61,035 Pro Sekunde<br />
Timeout++;<br />
Speed_Li_Ist = Li_Inkr - old_Li_Inkr;<br />
old_Li_Inkr = Li_Inkr;<br />
}<br />
<br />
<br />
// Interrupt-Service-Routine Timer_2 Overflow<br />
SIGNAL (SIG_OVERFLOW2)<br />
{<br />
// ISR-Code 62.5 kHz<br />
// 4 Zustaende durch jeweils 1 Bit repräsentiert (1,2,4,8)<br />
// Lichtschranke A liegt an Port A.0<br />
// Lichtschranke B liegt an Port A.1<br />
<br />
if ( (Zust_Li == 1) && ( !(PINA & (1<<PINA0)) )&& ( PINA & (1<<PINA1) ) ){<br />
Li_Inkr = Li_Inkr + 1;<br />
Zust_Li = 2;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 1) && (PINA & (1<<PINA0) )&& ( !( PINA & (1<<PINA1)) ) ){<br />
Li_Inkr = Li_Inkr - 1;<br />
Zust_Li = 8;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 2) && (PINA & (1<<PINA0) )&& ( PINA & (1<<PINA1) ) ){<br />
Li_Inkr = Li_Inkr + 1;<br />
Zust_Li = 4;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 2) && ( !(PINA & (1<<PINA0)) )&& ( !( PINA & (1<<PINA1)) ) ){<br />
Li_Inkr = Li_Inkr - 1;<br />
Zust_Li = 1;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 4) && (PINA & (1<<PINA0) )&& ( !( PINA & (1<<PINA1)) ) ){<br />
Li_Inkr = Li_Inkr + 1;<br />
Zust_Li = 8;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 4) && ( !(PINA & (1<<PINA0)) )&& ( PINA & (1<<PINA1) ) ){<br />
Li_Inkr = Li_Inkr - 1;<br />
Zust_Li = 2;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 8) && ( !(PINA & (1<<PINA0)) )&& ( !( PINA & (1<<PINA1)) ) ){<br />
Li_Inkr = Li_Inkr + 1;<br />
Zust_Li = 1;<br />
} /* end if */<br />
<br />
if ( (Zust_Li == 8) && (PINA & (1<<PINA0) )&& ( PINA & (1<<PINA1) ) ){<br />
Li_Inkr = Li_Inkr - 1;<br />
Zust_Li = 4;<br />
} /* end if */<br />
<br />
}<br />
<br />
...<br />
<br />
int main (void)<br />
{<br />
<br />
//Init<br />
TCCR2 |= (1<<CS20); //Prescaler = 1 (16MHz/1 = 16000000) -><br />
//16000000/256 -> 62500 Interrupts je Sekunde<br />
<br />
TCCR0 |= (1<<CS02)|(1<<CS00); //Prescaler = 1024 (16000000 / 1024 = 15625) -><br />
//15625/256 = 61,035 -> 61 Interrupts je Sekunde<br />
<br />
TIMSK |= (1<<TOIE0)|(1<<TOIE2); //Timer Overflow Interrupt Enablae für Timer 0 und 2<br />
<br />
<br />
Li_Inkr = 32000;Zust_Li = 1; // Kalibrierstellung bei 32000<br />
sei(); // Interrupts aktivieren<br />
<br />
<br />
while(1)<br />
{<br />
<br />
// hier kann Position und Geschwindigkeit ausgewertet werden<br />
<br />
}<br />
}<br />
<br />
</pre><br />
<br />
= Vor und Nachteile der Messmethoden=</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&diff=22716Überlegungen zur Drehgeber-Auswertung2013-06-15T16:45:40Z<p>Besserwessi: /* Auswertung der Signale */</p>
<hr />
<div>=Drehgeber-Auswertung=<br />
Es gibt in RN-Wissen schon einige (sehr gute) Artikel über Drehgeber und Auswertungsvarianten. Denen will ich hier keine Konkurrenz machen.<br/> <br />
[[Sensorarten#Incremental-Geber|Technisches]] findet man hier, [[Beispiel_Drehzahlmessung_mit_Drehgeber#Richtung.2C_Geschwindigkeit_und_Position_mit_Doppellichtschranke_ermitteln|Grundsätzliches]] über das Quadratur-Signal da, und [[Drehencoder|Programm-Beispiele]] für Drehencoder gibt es auch<br/><br />
Ich möchte dem einige grundsätzliche Überlegungen zur Drehgeber-Auswertung hinzufügen, damit es einem Einsteiger leichter fällt, eine Auswahl für seine konkreten Anforderungen zu treffen. <br />
<br />
==Signalfolge==<br />
Zur Erinnerung:<br />
[[Bild:quad_0.png|center]]<br />
===Auswertung der Flanken===<br />
Gerade bei den AVR's bietet es sich an, das A-Signal an einen "Externen Interrupt" anzuschliessen. <br />
Da man meist auch zwei Drehgeber hat (für links u. rechts), kommt man da wunderbar mit INT0 u. INT1 aus. <br />
====Vorwärts====<br />
Bei einer Konfiguration "steigende Flanke" wird bei den dicken Pfeilen der Interrupt ausgelöst. Liest man in der ISR den B-Kanal, kann man daraus direkt die Richtung entnehmen (im Beispiel ist das ebenfalls "1") und auf den Schrittzähler 1 addieren. <br />
[[Bild:quad_isr1.png|center]]<br />
====Richtungsumkehr====<br />
Wird nun die Drehrichtung geändert, wird die bis dahin fallende Flanke von A zur steigenden Flanke, und B ist zu diesem Zeitpunkt dann "0". Die Sache ist eindeutig, man kann nun von Schrittzähler eins abziehen. <br />
[[Bild:quad_isr2.png|center]]<br />
Dabei tritt aber natürlich eine Verschiebung auf. Wenn die Impulsfolge im Verhältnis zum zurückgelegten Weg gross ist, kann man das ignorieren, es geht ja letztlich meistens nur um ein paar Millimeter, und wo genau sich ein Drehgeber zwischen zwei Impulsen befindet, kann man ohnehin nie sagen. <br />
====Möglicher Fehler====<br />
Es gibt aber auch Situationen, wo es zu Fehlern kommt. Hier wird der zweite Interrupt noch ausgelöst und (richtig) als Schritt vorwärts gezählt. Dann bewegt sich der Geber etwas zurück, kommt aber nicht bis zu nächsten steigenden Flanke, sondern bewegt sich wieder nach vorne. <br />
[[Bild:quad_err.png|center]]<br />
Unsere Logik zählt nun einen Impuls zweimal, obwohl der drehgeber sich immer noch am selben Platz befindet.<br />
<br />
=== Auswertung der Übergänge===<br />
(Genaugenommen der Zustände unmittelbar danach)<br/><br />
Die genaueste Auswertung ist möglich, wenn man alle Übergänge steigend/fallend von beiden Kanälen (A u. B) bewertet. Das kann man ebenfalls mit ISR's machen, wenn man den µC so konfigurieren kann, dass er beide Flankenarten auslöst. Da dafür aber nur ein ISR-Vektor zur Verfügung steht, muss man in der Routine sowohl Kanal A als auch B abfragen und auch, ob fallend oder steigend.<br/><br />
Am einfachsten merkt man sich immer den Zustand von A u. B und vergleicht mit dem neuen Abtastergebnis.<br/><br />
Im Grunde ist das dann dasselbe, als wenn man ohne ISR periodisch die Eingänge abtastet, was natürlich oft genug geschehen muss, damit nichts verlorengeht. Die Flanken selbst erkennt man an dem Unterschien AB / AB-alt.<br/> <br />
In beiden Fällen erfolgt dann die Auswertung an den strichlierten senkrechten Linien. <br />
[[Bild:quad_states.png|center]]<br />
====Vorwärts====<br />
Von dem Mitte nach rechts gelesen bekommt also an den bezeichneten Stellen die Werte<br />
* 00 vorher: 01<br />
* 10 vorher: 00<br />
* 11 vorher: 10<br />
* 01 vorher: 11<br />
* usw<br />
====Rückwärts====<br />
Von dem Mitte nach links gelesen bekommt dann an den bezeichneten Stellen die Werte<br />
* 00 vorher: 10<br />
* 01 vorher: 00<br />
* 11 vorher: 01<br />
* 10 vorher: 11<br />
* usw<br />
===Programmtechnische Umsetzung===<br />
====INT0/1 oder Polling ?====<br />
* Wie schon erwähnt, bei AVR's bieten sich die beiden "external interrrupts" geradezu an. Bei vielen fertigen Boards werden die notwendigen Leitungen schon für einen Anschluss herausgeführt. Bei neuere AVR µCs (z.B. Mega88) bietet sich der Pin-Change Interrupt an, der an allen Ports verfügbar ist. Die Hauptschleife (DO...LOOP) bzw. (while(true)) braucht sich dadurch nicht unbedingt um die Sache zu kümmern. Unpraktisch ist ein externer Interrupt, wenn es durch eine kleine Hysterese zu vielen Störungen kommen kann, wenn eine der Lichtschranken gerade an der Grenze ist. Man hat dann im ungünstigen Fall ein sehr hohe Interruptfrequenz und ggf. auftretende Schrittfehler werden zum Problem. <br/><br />
Bei Odometrie-Anwendungen gehen die oben erwähnten möglichen Schrittfehler meistens völlig in der Gesamt-Ungenauigkeit unter (schlupfende Räder etc.).<br/><br />
Hat man als Drehgeber ein Dateneingaberad ( z.B. für Menüs u. Parametereingaben) erfolgt ohnehin eine Sichtkontrolle der Position, da kommt es schon garnicht darauf an.<br />
<br />
* Die Sache liegt anders, wenn wirklich kein Tick falsch interpretiert werden soll, also z.B. bei Positionier-Anwendungen. Da ist es wohl besser, die Eingänge zu pollen und alle Übergänge auszuwerten.<br/><br />
Das kann man durchaus in der Hauptschleife machen, wenn dann die Abtastfrequenz ausreichend ist, man kann aber auch durch einen TIMER-Interrupt das Polling auslösen (mit dem gleichen Kriterium). <br />
<br />
* Ganz elegant ist natürlich eine Mischform: wenn man auch die Geschwindigkeit misst, kann man abhängig davon die Methode ändern, denn die genannten Schrittfehler werden bei höherem Tempo kaum auftreten, eher bei Stillstand, genauso, wie auch die Geschwindigkeitsmessung selbst wechseln kann zwischen "schritte in der Zeit" und "Zeit je Schritt".<br />
<br />
====Auswertung der Signale====<br />
Auch wenn bei der Benutzung von Interrupts schon eine Information da ist, welcher Kanal sich geändert hat (und ggf. auch in welche Richtung), sollte die Auswertung trotzdem wie beim Polling erfolgen: Die beiden Kanäle einmal Auslesen, und dann zusammen mit dem vorherigen Wert bestimmen ob es einen Schritt gab und in welche Richtung. So kann verhindert werden, dass durch zu schnelle Interrupts, z.B. durch kurzzeitige Störungen Schritte verloren gehen und Fehler entstehen. <br />
Sofern von Hand nötig, muss das Interruptflag vor dem Auslesen des Ports zurückgesetzt werden, so dass alle Änderungen nach dem Auslesen erfasst werden.<br />
<br />
* Bei der Auswertung nur einer Flanke (also z.B. steigende Flanke von Kanal A) ist die Sache einfach: Es erfolgt ein Schritt, die Richtung zeigt der andere Kanal. Allerdings lässt sich mit nur einer Flanke der oben beschrieben Fehler beim Pendeln um den Übergang nicht vermeiden, weil ein Schritt vor bzw. zurück nicht an der gleichen Stelle erkannt werden. <br />
* mit beiden Flanken eines Kanals ist die Sache auch nicht viel komplizierter und man kann Schrittfehler vermeiden: <br />
** A = B --> vorwärts<br />
** A <> B --> rückwärts<br />
* Bei den Übergängen des anderen Kanals dreht sich die Richtung um und die Auflösung wird verdoppelt. <br />
<br />
Die Auswertung der Übergänge kann man natürlich auf viele Arten machen. Je nach Sprache bietet sich <br />
* "Select/Case" (Bascom) oder "switch/case" (GCC) an. Als Beispiel das [[Bascom_Inside-Code#.28Quadratur-.29_ENCODER|Assembler-listing]] für die Bascom-ENCODER()-Funktion<br/><br />
* IF THEN ELSE oder in C auch die bedingte Berechnung mit dem ? Operator an.<br />
* Eine andere Möglichkeit ist es, die neuen und die vorhergegangenen AB Werte zusammenzufassen und als Index für eine Werte- oder Sprungtabelle mit 16 Werten zu verwenden. In einer solche Tabelle muss man allerdings auch die ungültigen AB Kombinationen belegen, damit das funktioniert.<br />
<br />
==Autoren==<br />
* [[Benutzer:PicNick|PicNick]]<br />
<br />
==Siehe auch==<br />
* [[Sensoren]]<br />
* [[Sensorarten]]<br />
<br />
[[Category:Robotikeinstieg]]<br />
[[Category:Grundlagen]]<br />
[[Category:Sensoren]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&diff=22705Überlegungen zur Drehgeber-Auswertung2013-06-09T20:39:50Z<p>Besserwessi: /* Auswertung der Signale */</p>
<hr />
<div>=Drehgeber-Auswertung=<br />
Es gibt in RN-Wissen schon einige (sehr gute) Artikel über Drehgeber und Auswertungsvarianten. Denen will ich hier keine Konkurrenz machen.<br/> <br />
[[Sensorarten#Incremental-Geber|Technisches]] findet man hier, [[Beispiel_Drehzahlmessung_mit_Drehgeber#Richtung.2C_Geschwindigkeit_und_Position_mit_Doppellichtschranke_ermitteln|Grundsätzliches]] über das Quadratur-Signal da, und [[Drehencoder|Programm-Beispiele]] für Drehencoder gibt es auch<br/><br />
Ich möchte dem einige grundsätzliche Überlegungen zur Drehgeber-Auswertung hinzufügen, damit es einem Einsteiger leichter fällt, eine Auswahl für seine konkreten Anforderungen zu treffen. <br />
<br />
==Signalfolge==<br />
Zur Erinnerung:<br />
[[Bild:quad_0.png|center]]<br />
===Auswertung der Flanken===<br />
Gerade bei den AVR's bietet es sich an, das A-Signal an einen "Externen Interrupt" anzuschliessen. <br />
Da man meist auch zwei Drehgeber hat (für links u. rechts), kommt man da wunderbar mit INT0 u. INT1 aus. <br />
====Vorwärts====<br />
Bei einer Konfiguration "steigende Flanke" wird bei den dicken Pfeilen der Interrupt ausgelöst. Liest man in der ISR den B-Kanal, kann man daraus direkt die Richtung entnehmen (im Beispiel ist das ebenfalls "1") und auf den Schrittzähler 1 addieren. <br />
[[Bild:quad_isr1.png|center]]<br />
====Richtungsumkehr====<br />
Wird nun die Drehrichtung geändert, wird die bis dahin fallende Flanke von A zur steigenden Flanke, und B ist zu diesem Zeitpunkt dann "0". Die Sache ist eindeutig, man kann nun von Schrittzähler eins abziehen. <br />
[[Bild:quad_isr2.png|center]]<br />
Dabei tritt aber natürlich eine Verschiebung auf. Wenn die Impulsfolge im Verhältnis zum zurückgelegten Weg gross ist, kann man das ignorieren, es geht ja letztlich meistens nur um ein paar Millimeter, und wo genau sich ein Drehgeber zwischen zwei Impulsen befindet, kann man ohnehin nie sagen. <br />
====Möglicher Fehler====<br />
Es gibt aber auch Situationen, wo es zu Fehlern kommt. Hier wird der zweite Interrupt noch ausgelöst und (richtig) als Schritt vorwärts gezählt. Dann bewegt sich der Geber etwas zurück, kommt aber nicht bis zu nächsten steigenden Flanke, sondern bewegt sich wieder nach vorne. <br />
[[Bild:quad_err.png|center]]<br />
Unsere Logik zählt nun einen Impuls zweimal, obwohl der drehgeber sich immer noch am selben Platz befindet.<br />
<br />
=== Auswertung der Übergänge===<br />
(Genaugenommen der Zustände unmittelbar danach)<br/><br />
Die genaueste Auswertung ist möglich, wenn man alle Übergänge steigend/fallend von beiden Kanälen (A u. B) bewertet. Das kann man ebenfalls mit ISR's machen, wenn man den µC so konfigurieren kann, dass er beide Flankenarten auslöst. Da dafür aber nur ein ISR-Vektor zur Verfügung steht, muss man in der Routine sowohl Kanal A als auch B abfragen und auch, ob fallend oder steigend.<br/><br />
Am einfachsten merkt man sich immer den Zustand von A u. B und vergleicht mit dem neuen Abtastergebnis.<br/><br />
Im Grunde ist das dann dasselbe, als wenn man ohne ISR periodisch die Eingänge abtastet, was natürlich oft genug geschehen muss, damit nichts verlorengeht. Die Flanken selbst erkennt man an dem Unterschien AB / AB-alt.<br/> <br />
In beiden Fällen erfolgt dann die Auswertung an den strichlierten senkrechten Linien. <br />
[[Bild:quad_states.png|center]]<br />
====Vorwärts====<br />
Von dem Mitte nach rechts gelesen bekommt also an den bezeichneten Stellen die Werte<br />
* 00 vorher: 01<br />
* 10 vorher: 00<br />
* 11 vorher: 10<br />
* 01 vorher: 11<br />
* usw<br />
====Rückwärts====<br />
Von dem Mitte nach links gelesen bekommt dann an den bezeichneten Stellen die Werte<br />
* 00 vorher: 10<br />
* 01 vorher: 00<br />
* 11 vorher: 01<br />
* 10 vorher: 11<br />
* usw<br />
===Programmtechnische Umsetzung===<br />
====INT0/1 oder Polling ?====<br />
* Wie schon erwähnt, bei AVR's bieten sich die beiden "external interrrupts" geradezu an. Bei vielen fertigen Boards werden die notwendigen Leitungen schon für einen Anschluss herausgeführt. Bei neuere AVR µCs (z.B. Mega88) bietet sich der Pin-Change Interrupt an, der an allen Ports verfügbar ist. Die Hauptschleife (DO...LOOP) bzw. (while(true)) braucht sich dadurch nicht unbedingt um die Sache zu kümmern. Unpraktisch ist ein externer Interrupt, wenn es durch eine kleine Hysterese zu vielen Störungen kommen kann, wenn eine der Lichtschranken gerade an der Grenze ist. Man hat dann im ungünstigen Fall ein sehr hohe Interruptfrequenz und ggf. auftretende Schrittfehler werden zum Problem. <br/><br />
Bei Odometrie-Anwendungen gehen die oben erwähnten möglichen Schrittfehler meistens völlig in der Gesamt-Ungenauigkeit unter (schlupfende Räder etc.).<br/><br />
Hat man als Drehgeber ein Dateneingaberad ( z.B. für Menüs u. Parametereingaben) erfolgt ohnehin eine Sichtkontrolle der Position, da kommt es schon garnicht darauf an.<br />
<br />
* Die Sache liegt anders, wenn wirklich kein Tick falsch interpretiert werden soll, also z.B. bei Positionier-Anwendungen. Da ist es wohl besser, die Eingänge zu pollen und alle Übergänge auszuwerten.<br/><br />
Das kann man durchaus in der Hauptschleife machen, wenn dann die Abtastfrequenz ausreichend ist, man kann aber auch durch einen TIMER-Interrupt das Polling auslösen (mit dem gleichen Kriterium). <br />
<br />
* Ganz elegant ist natürlich eine Mischform: wenn man auch die Geschwindigkeit misst, kann man abhängig davon die Methode ändern, denn die genannten Schrittfehler werden bei höherem Tempo kaum auftreten, eher bei Stillstand, genauso, wie auch die Geschwindigkeitsmessung selbst wechseln kann zwischen "schritte in der Zeit" und "Zeit je Schritt".<br />
<br />
====Auswertung der Signale====<br />
Auch wenn bei der Benutzung von Interrupts schon eine Information da ist, welcher Kanal sich geändert hat (und ggf. auch in welche Richtung), sollte die Auswertung trotzdem wie beim Polling erfolgen: Die beiden Kanäle einmal Auslesen, und dann zusammen mit dem vorherigen Wert bestimmen ob es einen Schritt gab und in welche Richtung. So kann verhindert werden, dass durch zu schnelle Interrupts, z.B. durch kurzzeitige Störungen Schritte verloren gehen und Fehler entstehen. <br />
Sofern von Hand nötig, muss das Interruptflag vor dem Auslesen des Ports zurückgesetzt werden, so dass alle Änderungen nach dem Auslesen erfasst werden. <br />
<br />
* Bei der Auswertung nur einer Flanke (also z.B. steigende Flanke von Kanal A) ist die Sache einfach: Es erfolgt ein Schritt, die Richtung zeigt der andere Kanal. <br />
* mit beiden Flanken eines Kanals ist die Sache auch nicht viel komplizierter: <br />
** A = B --> vorwärts<br />
** A <> B --> rückwärts<br />
* Bei den Übergängen des anderen Kanals dreht sich die Richtung um. <br />
<br />
Die Auswertung der Übergänge kann man natürlich auf viele Arten machen. Je nach Sprache bietet sich <br />
* "Select/Case" (Bascom) oder "switch/case" (GCC) an. Als Beispiel das [[Bascom_Inside-Code#.28Quadratur-.29_ENCODER|Assembler-listing]] für die Bascom-ENCODER()-Funktion<br/><br />
* IF THEN ELSE oder in C auch die bedingte Berechnung mit dem ? Operator an.<br />
* Eine andere Möglichkeit ist es, die neuen und die vorhergegangenen AB Werte zusammenzufassen und als Index für eine Werte- oder Sprungtabelle mit 16 Werten zu verwenden. In einer solche Tabelle muss man allerdings auch die ungültigen AB Kombinationen belegen, damit das funktioniert.<br />
<br />
==Autoren==<br />
* [[Benutzer:PicNick|PicNick]]<br />
<br />
==Siehe auch==<br />
* [[Sensoren]]<br />
* [[Sensorarten]]<br />
<br />
[[Category:Robotikeinstieg]]<br />
[[Category:Grundlagen]]<br />
[[Category:Sensoren]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Spannungsregler&diff=22582Spannungsregler2013-05-04T15:35:27Z<p>Besserwessi: /* Lineare Spannungsregler */</p>
<hr />
<div>Die meisten Elektronikschaltungen brauchen eine bestimmte, stets gleichbleibende Betriebsspannung. Üblich sind hier 5V, bei einigen Bauteilen auch 3,3V. Oft stellt sich daher das Problem, dass eine schwankende Spannung auf einen festen Wert geregelt werden muss, um die Schaltung zu betreiben. Bei Batterien und Akkus sinkt die Spannung ab, wenn sie entladen werden, und bei einfachen Netzteilen und Trafos schwankt die Spannung je nach Belastung. <br />
Um dieses Problem zu lösen, gibt es die sogenannten Spannungsregler: Bauteile, die mit der ungeregelten, schwankenden Spannung versorgt werden und daraus die gewünschte, konstante Spannung erzeugen.<br />
<br />
<br />
=Lineare Spannungsregler=<br />
<br />
[[Bild:78s05.jpg|thumb|Spannungsregler 78S05]] Die wohl bekanntesten und sehr häufig eingesetzten Spannungsregler sind die der 78xx-Serie. Das xx steht hierbei für die Spannung, die die Regler erzeugen: 7805 für 5V, 7812 für 12V, und so weiter. Der große Vorteil dieser Regler ist ihr geringer Preis (ab etwa 20 Cent) und die einfache Verwendung: in der Minimalvariante sind keine externen, zusätzlichen Bauteile nötig. Es wird aber trotzdem dringend empfohlen, zwei Kondensatoren anzuschließen, damit der Regler stabil arbeitet. <br />
<br />
Die Regler vergleichen die Ausgangsspannung mit einer intern erzeugen Referenzspannung. Wenn die Ausgangsspannung zu niedrig ist, wird ein [[Transistor]], durch den der Ausgangsstrom fließt, stärker angesteuert, sodass ein größerer Strom fließen kann, bis die gewünschte Spannung erreicht ist. <br />
[[Bild:Linearregler-Prinzip.png|thumb|Prinzipelle Arbeitsweise eines Linearreglers]]<br />
Steigt die Ausgangsspannung zu sehr an, wird über den Transistor der Strom reduziert, bis die Spannung sich wieder stabilisiert hat. Der Transistor wird also benutzt wie ein variabler Widerstand, der so eingeregelt wird, daß die "überflüssige" Spannungsdifferenz zwischen Eingangsspannung und gewünschter Ausgangsspannung an ihm abfällt, und zwar unabhängig vom fließenden Strom. <br />
<br />
Der Nachteil dieser linearen Spannungsregler liegt in ihrem schlechten Wirkungsgrad und ihrer großen Verlustleistung. Der Teil der Spannung, die am Eingang zugeführt wird und gerade nicht am Ausgang benötigt wird, fällt am Transistor ab und wird dort in Wärme umgewandelt.<br />
<br />
Der Wirkungsgrad eines Linearreglers (ohne Berücksichtigung des eigenen Strombedarfs) berechnet sich mit:<br />
<br />
[[Bild:spannungsregler_wirkungsgrad.png]]<br />
<br />
Beispiel: Ein Linearregler mit 5V Ausgangsspannung wird mit 12V am Eingang versorgt. Unabhängig vom entnommenen Strom beträgt der Wirkungsgrad dann nur (5V/12V)*100%=41,7%. Bei höheren Eingangsspannungen wird er sogar noch schlechter.<br />
Angenommen, am Ausgang werden 500mA benötigt. Der Regler muss dann 12V-5V=7V bei 500mA in Wärme umwandeln. Das entspricht einer Verlustleistung von 7V*0,5A=3,5Watt.<br />
<br />
Je nach Stromfluss führt das zu einer starken Erwärmung des Reglers, so dass in den meisten Fällen ein Kühlkörper nötig wird, wodurch weitere Kosten entstehen und viel Platz in Anspruch genommen wird. Außerdem reduziert sich durch die großen Verluste die Betriebsdauer, wenn man den Regler mit Akkus oder Batterien versorgt.<br />
<br />
Linearregler gibt es als Festspannungsregler und Regler mit einstellbarer Ausgangsspannung. Viele der Regler sind kurzschlussfest.<br />
<br />
[[Bild:7805.png|thumb|Übliche Anwendungsschaltung für den 7805. D1 dient als Verpolschutz, D2 schützt den Regler bei Spannungsspitzen am Ausgang (D2 wird meistens weggelassen), Kondensatoren und Elkos zum Abblocken. Die Bauteilwerte sind nur Beispiele, es können auch andere Werte verwendet werden.]]<br />
<br />
Bei den normalen Spannungsreglern muss die Eingangsspannung mindestens 2-3 Volt höher als die Ausgangsspannung sein. Bei "low-drop" Reglern ist nur eine extra Spannung ("dropout") von etwa 0,1-0,5 V nötig. Dafür fließt aber in der Regel mehr Strom über den Masseanschluss. Außerdem muss bei Low-drop Reglern genauer auf den Kondensator hinter dem Regler geachtet werden. Meist wird ein Mindestwert für die Kapazität (teils >20 µF) verlangt. Dazu kommt ein erlaubter Bereich für den scheinbaren Serienwiderstand (ESR) des Kondensators, der ebenfalls eingehalten werden muss, damit der Regler zuverlässig nicht schwingt. Neben der Kapazität muss dazu der Kondensator Typ (i.A. Keramikkondensator, Tantal Elko oder low ESR Elko) passen. Je nach Regler sind die Anforderungen unterschiedlich - '''um einen Blick ins Datenblatt kommt man entsprechend bei einem low Drop Regler kaum herum'''.<br />
<br />
;Festspannungsregler:<br />
* 7805 (5V)<br />
* 78xx (xxV) , 1 A positiv, Gehäuse TO220<br />
* 78Sxx (xxV), 2 A positiv, Gehäuse TO220<br />
* 79xx (-xxV), 1 A negativ, Gehäuse TO220<br />
* LP2950-xx (xxV) Low drop, Dropout typ. 300mV, max. 100mA, Gehäuse TO92 /SO8 <br />
* LM2940-xx (xxV) Low drop, Dropout typ. 500mV, max. 1 A, Gehäuse TO220, Ausgangskondensator >22 µF mit ESR 0,1 - 1 Ohm (z.B. Tantal, ggf. low ESR Elko)<br />
* LT1761ES5-xx (xxV) Low drop,Dropout typ. 300mV, max. 100mA, Verpolungsschutz, Gehäuse SOT23-5<br />
<br />
;Linerregler, einstellbar:<br />
* LM317: 1 A positiv<br />
* LP2951: low drop 100 mA<br />
<br />
Für die Widerstandsberechnung an einem LM317 siehe [http://people.freenet.de/gjl/pub/lm317/index.html].<br />
<br />
=Schaltregler=<br />
<br />
Schaltregler arbeiten nach einem komplett anderen Prinzip. Während bei Linearreglern die Spannungsdifferenz in Wärme umgewandelt wird und damit verloren ist, wird diese Energie bei Schaltreglern in einem Magnetfeld gespeichert und der Schaltung zu einem späteren Zeitpunkt wieder zugeführt.<br />
<br />
Dies erfordert einen komplizierteren Aufbau als beim Linearregler. Ein Schaltregler hat zwei Arbeitsphasen:<br />
;On-Phase: Eingangsenergie wird im Magnetfeld gespeichert<br />
;Off-Phase: Die im Magnetfeld gespeicherte Energie wird an den Ausgang abgegeben<br />
Je nach Konstruktionsprinzip kann in beiden Phasen auch Energie direkt vom Eingang zum Ausgang fliessen.<br />
<br />
Der wesentliche Vorteil eines Schaltreglers besteht in seinem hohen Wirkungsgrad, je nach Typ lassen sich etwa 70% bis über 90% erzielen. Zudem kann die Ausgangsspannung über der Eingangsspannung liegen und ein anderes Vorzeichen haben. Dadurch ergeben sich längere Laufzeiten im Akku- und Batteriebetrieb und eine gegenüber linearen Reglern wesentlich geringere Erwärmung. Daher kommt man ohne oder mit einem vergleichsweise kleinen Kühlkörper aus.<br />
<br />
Der Nachteil liegt in einem höheren Preis. Übliche Schaltungen, die im Leistungsbereich eines 78xx liegen, kosten etwa 2 bis 5€. Außerdem brauchen diese Schaltungen teilweise mehr Platz, weil zum Regler-IC noch die Spule, Kondensatoren und oft eine Diode kommen. Auch das Platinenlayout erfordert etwas Sorgfalt, denn gewisse Leitungen sollten möglichst kurz und breit ausgeführt werden, um die einwandfreie Funktion sicherzustellen. Die Qualität der Spannung ist wegen der hohen Schaltfrequenzen in der Regel etwas schlechter als bei einem Linearregler (kann aber noch gefiltert werden) und kann zu Problemen in der Schaltung führen wie z.B. die Störung vom Funkempfängern.<br />
<br />
==Abwärtswandler==<br />
[[Bild:smps-down.png|thumb|263px|Prinzipieller Aufbau eines Abwärtsreglers]]<br />
Am einfachsten zu verstehen ist der Abwärtswandler (Step-Down):<br />
<br />
;On-Phase (Schalter S geschlossen): Über S und die Drossel L fliesst Strom an der Diode D vorbei zum Ausgang. Über der Drossel fällt Spannung ab, daher ist V<sub>OUT</sub> kleiner als V<sub>IN</sub>. Die Energiedifferenz wird in der Drossel als Magnetfeld gespeichert. Mit der Zeit setzt die Drossel dem Strom einen immer geringer werdenden Widerstand entgegen: Die V<sub>OUT</sub> steigt immer weiter. Hat V<sub>OUT</sub> den gewünschten Wert erreicht, dann wird S geöffnet und die On-Phase beendet.<br />
<br />
;Off-Phase (Schalter S geöffnet): Die Drossel ist ein induktives Bauelement. Daher kann der Strom durch sie nicht sofort stoppen; er muss weiterfliessen: Die Drossel ist jetzt eine aus der Energie ihres Magnetfelds gespeiste Strompumpe, die den Kondensator C weiter mit Energie versorgt, indem sie Strom durch die Diode D saugt. In der Off-Phase zieht der Verbraucher seine Energie aus dem elektrischen Feld des Kondensators und dem Magnetfeld der Drossel. Sinkt der Strom durch die Drossel unter einen Schwellwert, dann folgt die nächste On-Phase.<br />
<br />
==Aufwärtswandler==<br />
[[Bild:smps-up.png|thumb|267px|Prinzipieller Aufbau eines Aufwärtswandlers]]<br />
;On-Phase (Schalter S geschlossen): Über S und die Drossel L fliesst Strom an der Diode D vorbei nach GND. Der Stromfluss baut ein Magnetfeld in der Drossel auf. In der On-Phase versorgt sich der Verbraucher aus dem Kondensator C. Erreicht der Strom durch die Drossel einen oberen Schwellwert, wird S geöffnet und die on-Phase endet.<br />
<br />
;Off-Phase (Schalter S geöffnet): Die Drossel ist ein induktives Bauelement. Daher kann der Strom durch sie nicht sofort stoppen; er muss weiterfliessen: Die Drossel pumpt Ladungsträger über die Diode D auf den Kondensator C. Die Energie dazu nimmt sie aus ihrem Magnetfeld. V<sub>OUT</sub> kann weit über V<sub>IN</sub> steigen. Sinkt der Strom durch die Drossel unter einen unteren Schwellwert, dann folgt die nächste On-Phase.<br />
<br />
==Invertierender Wandler==<br />
[[Bild:smps-neg.png|thumb|265px|Prinzipieller Aufbau eines invertierenden Wandlers]]<br />
Funktioniert vom Prinzip wie der Aufwärtswandler.<br />
<br />
;On-Phase (Schalter S geschlossen): Über S und die Drossel L fliesst Strom an der Diode D vorbei nach GND. Der Stromfluss baut ein Magnetfeld in der Drossel auf. In der On-Phase versorgt sich der Verbraucher aus dem Kondensator C. Erreicht der Strom durch die Drossel einen oberen Schwellwert, wird S geöffnet und die on-Phase endet.<br />
<br />
;Off-Phase (Schalter S geöffnet): Die Drossel ist ein induktives Bauelement. Daher kann der Strom durch sie nicht sofort stoppen; er muss weiterfliessen: Die Drossel saugt über die Diode D Ladungsträger vom Kondensator C. Die Energie dazu nimmt sie aus ihrem Magnetfeld. V<sub>OUT</sub> sinkt unter GND. Sinkt der Strom durch die Drossel unter einen unteren Schwellwert, dann folgt die nächste On-Phase.<br />
<br />
==praktische Umsetzung==<br />
<br />
Für Schaltregler gibt es spezielle ICs, die den größten Teil der Schaltung enthalten.<br />
Neben der Frequenz und dem Tastverhältnis sind auch die beteiligten Komponenten (Schalttransistor, Drossel, Diode, Kondensatoren) entscheidend für die Eigenschaften des Reglers (Effizienz, Störungen, Leistungsbereich, Ausgangsspannung- und Strom, Rippel, Baugröße, etc.). Einige der Regler arbeiten nur ab einer Mindestlast - bei weniger Belastung besteht die Gefahr von Überspannung am Ausgang. <br />
<br />
[[Bild:lm2575.png|thumb|480px|Beispielschaltung für Schaltregler mit LM2575-5: 5 V bis ca. 1 A. Der 100µF Elko vor dem Regler sollte ein low ESR Typ sein und ggf. auch größer, um die nötige Strombelastbarkeit zu erreichen.]]<br />
<br />
'''Beispiele für Schaltregler ICs:'''<br />
* LM2574 N5 : 5 V, 0,5 A , Abwärtswandler, DIP8<br />
* LM2576 T5 : 5 V, 3 A , Abwärtswandler, TO220-5<br />
* LM2576 T12: 12 V, 3 A , Abwärtswandler, TO220-5<br />
* LM2576 ADJ: einstellbar, 3A, TO220-5<br />
* LM2673 T5 : 5 V , 3 A , Abwärtswandler mit einstellbarer Strombegrenzung, TO220-5<br />
* LM2679 T5 : 5 V , 5 A , Abwärtswandler mit einstellbarer Strombegrenzung, TO220-5<br />
* MC33063/MC34063 : variabel ca. 0,2-1 A, DIP8, SO8, Online-Berechnung dazu unter [[#Weblinks|Weblinks]]<br />
* LT1072 , variabel, ca. 1 A<br />
* MAX856, aufwärts, z.B. 3 V -> 5 V, ca. 100 mA, SO8 (SMD)<br />
* MCP1640 aufwärts, z.B. 1,2 V -> 5 V, ca. 100 mA, SOT23-6 (SMD)<br />
* PR4401, aufwärts, LED-teiber ca. 20 mA, SOT23 (SMD)<br />
* L4970A : einstellbar 5,1-40V Output, 10A, Abwärtswandler, Multiwatt-15-Gehäuse<br />
<br />
'''Hinweise zu den beteiligten Komponenten:'''<br />
* Als Diode sollte eine Schottkydiode verwendet werden, die eine ausreichende Strombelastbarkeit aufweist, z.B. 1N5818 (1Ampere) oder 1N5821 (3Ampere). Sie sollte eine möglichst geringe Vorwärtsspannung (engl. "forward voltage") bei dem gewünschten Ausgangsstrom haben. Das lässt sich normalerweise im Datenblatt der Diode aus einem Diagramm entnehmen. Ein langsame Diode wie die 1N4003 ist hier nicht geeignet. <br />
<br />
[[Bild:Ringkernspule.jpg|thumb|selber gewickelte Ringkernspule. 18 Windungen 1mm² Kupferlackdraht, Induktivität 250µH, (Kern: Pollin Best.-Nr. 250242), ca 20mm Durchmesser. Die Windungen wurden auseinander gedrückt, um die parasitäre Kapazität zwischen den Windungen zu reduzieren.]]<br />
<br />
[[Bild:Bauteile_schaltregler.JPG|thumb|Bauteile für einen Schaltregler: von links nach rechts: Eingangs-Ker.ko., Eingangselko, Schaltregler-IC LM2576, Spule, Ausgangselko, unten: Schottkydiode]]<br />
<br />
* Die [[Spule]] sollte einen möglichst geringen ohmschen Widerstand haben. Man kann sie ganz einfach selbst anfertigen. Zur Reduktion von Funkstörungen ist eine Ringkernspule sinnvoll, da sie ein geschlossenes Magnetfeld bildet. Auf den Ringkern wird dann möglichst dicker Kupferlackdraht gewickelt (z.B. 0,5...1mm Durchmesser). Die Anzahl der erforderlichen Windungszahl n kann mit Hilfe der Formel L=Al*n² berechnet werden (L=benötigte Induktivität, Al=Konstante des Kerns in nH). Ein zweiter wichtiger Punkt ist, das der Kern nicht in die Sättigung kommt. Dazu sollte ein geeignetes Kernmaterial (in der Regel Ferrit) gewählt werden (keine zu hohe Permeabilität, Al nicht zu groß für die Bauform). [Bitte ergänzen]. Festinduktivitäten in Widerstandsbauform sind aufgrund ihres hohen Innenwiderstandes (schlechte Güte) dafür nicht geeignet. Langgestreckte Spulen (axiale Bauform) können aufgrund ihres erzeugten Magnetfeldes mehr Störungen verursachen. Die erforderliche Induktivität kann je nach Eingangsspannung und Ausgangsstrom variieren. Im Datenblatt des jeweiligen Schaltreglers gibt es hierfür Diagramme, in denen man die notwendige Induktivität abhängig von den genannten Größen ablesen kann.<br />
* Der Ausgangselko sollte einen geringen ESR ("equivalent series resistance") haben. Es gibt spezielle Low-ESR Typen, die für solche Anwendungen verwendet werden können. Alternativ kann durch eine Parallelschaltung von mehreren Elkos ebenfalls ein geringer ESR erreicht werden.<br />
<br />
'''Ausgangsfilter:'''<br />
<br />
Zur Reduktion der Welligkeit ("ripple") der Ausgangsspannung sollte ein zusätzliches LC-Tiefpassfilter hinter den Regler hinzugefügt werden. Man sollte darauf achten, dass die Filterspule wieder einen geringen ohmschen Widerstand besitzt, da ansonsten bei größerer Strombelastung die Ausgangsspannung aufgrund dieses Widerstandes sinkt. Festinduktivitäten in Widerstandsbauform sind aufgrund ihres hohen Innenwiderstandes (zu viel Spannungsabfall) dafür nicht geeignet. Parallel zum Elko kann auch noch z.B. ein 100nF Keramikkondensator geschaltet werden.<br />
<br />
Beispiel für eine Dimensionierung des Filters (lt.Datenblatt des LM2576-5): 20µH, 100µF<br />
<br />
'''Eingangsfilter:'''<br />
<br />
Falls der Regler Rückwirkungen auf die Eingangsspannung verursacht, kann auch vor dem Regler ein LC-Tiefpassfilter eingesetzt werden. Die Kapazität des Eingangselkos darf in diesem Fall ruhig größer gewählt werden.<br />
<br />
Im jeweiligen Datenblatt findet man in der Regel weiterführende Informationen zur Schaltung und den benötigten Teilen, insbesondere zur Dimensionierung sowie Anhaltspunkte zum Design eines Platinenlayouts.<br />
<br />
<br />
'''Noch zu ergänzen:'''<br />
<br />
* Fotos<br />
* Schaltpläne<br />
<br />
= Spannungsregler als Konstantstromquelle=<br />
<br />
Für viele Aufgaben sind konstante Ströme ebenso wichtig wie konstante Spannungen. Beispielsweise bei Leuchtdioden. Leuchtdioden benötigen in der Regel ca. 20 mA Strom, je nach Ausführung. Vorwiderstände sind oft nicht die beste Lösung, zumal diese bei fallender Spannung den Strom nicht mehr konstant halten und somit die Leuchtkraft unnötig reduzieren. Eine einfache und zugleich günstige Lösung ist die hier abgebildete Schaltung mit dem zweckentfremdeten Spannungsregler LM317K. <br />
Der LM317K ist normalerweise ein einstellbarer Spannungsregler. Über die untere Schaltung wird er auch zur Konstantstromquelle: Der Spannungsregler stellt sich so ein, daß zwischen "Vout" und "Adj." 1,25 V liegen. Dank R = U / I läßt sich leicht errechnen, daß bei einem betimmten Widerstandswert ein bestimmter Strom fließt. Und diesen lassen wir dann einfach durch unsere Verbraucher weiterfließen. <br />
Der LM 317K kann mit einer maximalen Eingangsspannung von ca. 4 bis 35 Volt betrieben werden. Die Strombegrenzung ist in einem Bereich von 1,25 - 0,01 Ampere einstellbar, R1 liegt somit zwischen 1 Ohm bis 120 Ohm. Siehe unten unter [[#Weblinks|Weblinks]], dort findet man ein Online-Formular im Roboternetz, mit der sich die Schaltung genau dimensionieren läßt.<br />
<br />
Es sollte darauf geachtet werden, dass der LM317 ausreichend gekühlt wird (bei größeren Strömen/Verlustleistungen). Üblicherweise wird der Regler mit etwas Wärmeleitpaste (evtl. auch Glimmerscheibe als Isolator) auf einen Kühlkörper geklebt und mit Hilfe einer M3 Schraube am Kühlkörper festgeschraubt.<br />
<br />
[[Bild:Konstantstromquellelm317.gif]] <br />
[[Bild:lm317.gif]]<br />
<br />
Ist die Spannungsdifferenz zwischen Betriebsspannung und Last (z.B. High-Power-LEDs) sehr groß, bietet es sich an, nicht den LM317, sondern einen "normalen" Spannungsregler zu verwenden (z.B. 7805, 7809 o.ä.). Der Regler wird genau wie im abgebildeten Schaltplan angeschlossen. Vorteil ist dann, dass '''verhältnismäßig''' mehr Leistung im Widerstand als im Regler selbst umgesetzt werden muss. Ein Hochleistungswiderstand lässt sich nämlich in der Regel besser kühlen als der Regler im TO-220-Gehäuse.<br />
<br />
Allerdings sollte man sich bei einer großen Spannungsdifferenz Gedanken über den Wirkungsgrad machen.<br />
<br />
=Autoren=<br />
* [[Benutzer:Uwegw|Uwegw]] 15:09, 10. Sep 2006 (CEST)<br />
* [[Benutzer:SprinterSB|SprinterSB]]<br />
* [[Benutzer:Frank|Frank]]<br />
* [[Benutzer:BMS|BMS]]<br />
<br />
==Weblinks==<br />
* [http://www.roboternetz.de/phpBB2/konstantstrom.php Konstantstromregler mit Spannungsregler berechnen]<br />
* [http://schmidt-walter.eit.h-da.de/smps/smps.html Schaltnetzteile: Erklärung der verschiedenen SPMS-Topologien, Dimensionierung, Rechenhilfe, ...] <br />
* [http://www.mikrocontroller.net/articles/MC34063 Hinweise zum Schaltregler IC MC34063]<br />
* [http://www.nomad.ee/micros/mc34063a/index.shtml Nützliches Design-Tool für den MC34063, Pinbelegung beachten(gespiegelt)]<br />
* [http://www.cl-projects.de/projects/tools/resmatch-lm317.phtml Berechnung des Spannungsteilers beim LM317 mit E12 Widerständen]<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Grundlagen]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Avr-gcc/Optimierungen&diff=22559Avr-gcc/Optimierungen2013-04-26T16:32:55Z<p>Besserwessi: /* Optimierungsgrad */</p>
<hr />
<div>Beim Programmieren in C möchte man sich möglichst wenig mit der Codeerzeugung selbst auseinandersetzen. Man verwendet ja gerade deshalb einen Compiler und programmiert nicht in Assembler, weil man sich nicht um Register-Belegungen o.ä. kümmern will, sondern nur um die zu lösende Aufgabe.<br />
<br />
GCC erzeugt zwar recht guten Code, aber er ist nicht perfekt. Gerade auf Systemen wie AVR mit nur sehr begrenzten Resourcen muss man daher dem Compiler hilfreich zur Seite stehen, wenn man noch dichteren/schnelleren Code erhalten möchte.<br />
<br />
:''"Unlike most other C compilers, GCC allows you to use -g with -O. The shortcuts taken by optimized code may occasionally produce surprising results: some variables you declared may not exist at all; flow of control may briefly move where you did not expect it; some statements may not be executed because they compute constant results or their values were already at hand; some statements may execute in different places because they were moved out of loops.''<br />
<br />
:''Nevertheless it proves possible to debug optimized output. This makes it reasonable to use the optimizer for programs that might have bugs."''<br />
<br />
Um das Ergebnis zu beurteilen, hilft ein Blick ins Listfile. <br />
Siehe dazu auch die Abschnitte <br />
"[[Hallo Welt für AVR (LED blinken)#Listfile erstellen|Listfile erstellen]]" <br />
und<br />
"[[Hallo Welt für AVR (LED blinken)#Die Größe ermitteln|Die Größe ermitteln]]" <br />
im [[Hallo Welt für AVR (LED blinken)|Hallo Welt für AVR]].<br />
<br />
==Optimierungsgrad==<br />
Als Optimierungsgrad erweist sich <tt>-Os</tt> (Optimize for Size) als der beste, evtl. noch <tt>-O2</tt>. Abzuraten ist von der maximalen Optimierung <tt>-O3</tt>, die wegen function inlining und loop unrolling zu sehr breitem Code führt und für AVR absolut nicht angesagt ist.<br />
Ohne Angabe eines Optimierungsgrades wird nicht optimiert, was gleichbedeutend mit der Option <tt>-O0</tt> ist. Das Übersetzen ohne Optimierung ist vor allem für Debug-Zwecke sinnvoll, da so der erzeugte ASM Code leichter zu verfolgen ist.<br />
<br />
==Vermeide printf, scanf, malloc==<br />
Funktionen von diesem Kaliber sind die absoluten Platz- und Zeitfresser. <br />
<br />
Alternativen findet man reichlich in der <tt>avr-libc</tt> wie <tt>itoa</tt> und <tt>atoi</tt>.<br />
Und für <tt>malloc</tt> und Konsorten sind dynamische Arrays und das Compiler-Builtin <tt>__builtin_alloca</tt> effizientere Alternativen, siehe auch im Abschnitt "[[avr-gcc#Dynamische Speicherallokierung|Dynamische Speicherallokierung]]".<br />
<br />
==Konstante Strings ins Flash== <br />
Konstante Strings, wie sie zu Ausgabezwecken Verwendung finden, werden im Programm oft nicht verändert und brauchen nicht SRAM zu belegen (und damit auch Flash, von wo aus sie vom Startup-Code ins SRAM kopiert werden), sondern gehören ins Flash! <br />
<br />
Entsprechende Routinen, um auf Strings im Flash zuzugreifen, tragen die Suffix <tt>_P</tt>, wie z.B. <tt>strcmp_P</tt> mit dem Prototyp<br />
extern int *strcmp_P (char *, const prog_char *)<br />
Die Implementierungen befinden sich in der <tt>avr-libc</tt>.<br />
<br />
'''Anwendung:'''<br />
#include <avr/pgmspace.h><br />
<br />
const prog_char str_p[] = "Ein String im Flash";<br />
const char str2_p[] PROGMEM = "Noch ein String im Flash";<br />
...<br />
{{ccomment|String im SRAM mit String im Flash vergleichen}}<br />
if (!strcmp_P (str_sram, str_p))<br />
{<br />
{{ccomment|mach was bei Gleichheit}}<br />
}<br />
<br />
{{ccomment|"foo" wird im RAM angelegt. Ineffizient für konstante Strings!}} <br />
{{ccomment|Beachte, daß damit strcmp (nicht strcmp_P) benutzt werden muss.}} <br />
if (!strcmp (str_sram, "foo"))<br />
{<br />
{{ccomment|mach was bei Gleichheit}}<br />
}<br />
<br />
{{ccomment|PSTR bewirkt, daß die String-Konstante "foo"}}<br />
{{ccomment|im Flash angelegt wird}}<br />
if (!strcmp_P (str_sram, PSTR ("foo"))<br />
{<br />
{{ccomment|mach was bei Gleichheit}}<br />
}<br />
...<br />
}<br />
<br />
===Sprungtabelle===<br />
Genauso macht man auch eine Sprungtabelle, um anhand von Kommando-Strings dazugehörige Funktionen ausführen zu lassen:<br />
#include <avr/pgmspace.h><br />
<br />
int func1 (int arg)<br />
{<br />
...<br />
}<br />
<br />
#define TEXT_LEN 15<br />
<br />
{{ccomment|Die Kommandostruktur}}<br />
typedef struct <br />
{<br />
int (*func)(int); {{ccomment|Zeiger auf die auszuführende Funktion}}<br />
int arg; {{ccomment|das Argument, das mitübergeben wird}}<br />
char text[1+TEXT_LEN]; {{ccomment|Text, maximal TEXT_LEN Zeichen lang}}<br />
} command_t;<br />
<br />
{{ccomment|Das Array mit den Kommandos.}}<br />
{{ccomment|Die funcx sind vom Prototyp (z.B. func1 oben)}}<br />
{{ccomment|int funcx (int arg);}}<br />
const command_t commands[] PROGMEM =<br />
{<br />
{ func1, 0, "Befehl 1" },<br />
{ func2, 3, "Befehl für func2" }<br />
};<br />
<br />
{{ccomment|Sucht in commands[] nach text und führt gegebenenfalls}}<br />
{{ccomment|die dazugehörige Funktion funcx mit Argument arg aus.}}<br />
{{ccomment|Liefert den Rückgabewert von funcx}}<br />
{{ccomment|oder -1, falls text nicht gefunden wurde.}}<br />
int execute (const char *text)<br />
{<br />
{{ccomment|Schleifenvariable}}<br />
unsigned char i;<br />
<br />
{{ccomment|Wandert durch das Array mit Kommando-Strukturen}}<br />
const command_t * cmd = commands;<br />
<br />
{{ccomment|sizeof wird von gcc ausgewertet und ist wie eine Konstante,}}<br />
{{ccomment|denn beide sizeofs sind zur Compilezeit bekannt}}<br />
for (i=0; i < sizeof(commands) / sizeof(command_t); i++)<br />
{<br />
{{ccomment|Ist das der gesuchte String?}} <br />
if (strcmp_P (text, cmd->text))<br />
{<br />
{{ccomment|Nein, dann weitersuchen}}<br />
cmd++;<br />
continue;<br />
}<br />
<br />
{{ccomment|Ja}}<br />
int (*func)(int), arg;<br />
<br />
{{ccomment|Dann Funktionszeiger und Argument besorgen,}}<br />
func = (int(*)(int)) pgm_read_word (& cmd->func);<br />
arg = (int) pgm_read_word (& cmd->arg);<br />
<br />
{{ccomment|Funktion ausführen und deren Wert zurückliefern}} <br />
return func (arg);<br />
}<br />
<br />
{{ccomment|text ist nicht in commands}}<br />
return -1;<br />
}<br />
<br />
Nachteil dabei ist, daß jeder String den maximalen Platz von <tt>TEXT_LEN+1</tt> Zeichen belegt.<br />
Falls man da noch weiter sparen will, dann kann man die Strings wieder ins Flash legen und ihre Adresse in der Struktur merken. Dadurch belegt ein String nur noch Länge+3 Zeichen (+3 wegen 1 Endezeichen und 2 Bytes für seine in der Struktur gemerkte Adresse). Die Definition der Tabelle wird aber umständlicher, weil jeder String einzeln angegeben werden muss:<br />
#include <avr/pgmspace.h><br />
<br />
{{ccomment|Die Kommandostruktur}}<br />
typedef struct <br />
{<br />
...<br />
char * text; {{ccomment|Zeiger auf Text}}<br />
} command_t;<br />
<br />
const prog_char str_1[] = "Befehl 1";<br />
const prog_char str_2[] = "Befehl für func2";<br />
<br />
const command_t commands[] PROGMEM =<br />
{<br />
{ func1, 0, str_1 },<br />
{ func2, 3, str_2 }<br />
};<br />
<br />
{{ccomment|Sucht in commands[] nach text und führt gegebenenfalls}}<br />
{{ccomment|die dazugehörige Funktion funcx mit Argument arg aus.}}<br />
{{ccomment|Liefert den Rückgabewert von funcx}}<br />
{{ccomment|oder -1, falls text nicht gefunden wurde.}}<br />
int execute (const char *text)<br />
{<br />
{{ccomment|Schleifenvariable}}<br />
unsigned char i;<br />
<br />
{{ccomment|Wandert durch das Array mit Kommando-Strukturen}}<br />
const command_t * cmd = commands;<br />
<br />
{{ccomment|sizeof wird von gcc ausgewertet und ist wie eine Konstante,}}<br />
{{ccomment|denn beide sizeofs sind zur Compilezeit bekannt}}<br />
for (i=0; i < sizeof(commands) / sizeof (command_t); i++)<br />
{<br />
const prog_char * text_P;<br />
<br />
{{ccomment|Liest die Startadresse von str_x}}<br />
text_P = (const prog_char *) pgm_read_word (& cmd->text);<br />
<br />
{{ccomment|Ist das der gesuchte String?}} <br />
if (strcmp_P (text, text_P))<br />
{<br />
...<br />
<br />
==Lokale Variablen verwenden==<br />
<br />
Beim Manipulieren globaler Variablen kann es günstig sein, diese in eine lokale Variable zu kopieren, dort zu verändern, und sie danach wieder zu schreiben <br />
<pre><br />
char var;<br />
<br />
void foo1()<br />
{<br />
var++;<br />
if (var > 10)<br />
var = 1;<br />
} <br />
</pre><br />
Dadurch wird einmal unnötig gespeichert (der dritte Befehl kann vermieden werden).<br />
<pre><br />
foo1:<br />
lds r24,var ; *movqi/4 [length = 2]<br />
subi r24,lo8(-(1)) ; addqi3/2 [length = 1]<br />
sts var,r24 ; *movqi/3 [length = 2]<br />
cpi r24,lo8(11) ; cmpqi/2 [length = 1]<br />
brlt .L3 ; branch [length = 1]<br />
ldi r24,lo8(1) ; *movqi/2 [length = 1]<br />
sts var,r24 ; *movqi/3 [length = 2]<br />
.L3:<br />
ret <br />
</pre><br />
Indem man eine lokale Variable (<tt>var2</tt>) verwendet für die Änderung von <tt>var</tt> vermeidet man dies:<br />
<pre><br />
char var;<br />
<br />
void foo2()<br />
{<br />
char var2 = var;<br />
<br />
var2++;<br />
if (var2 > 10)<br />
var2 = 1;<br />
<br />
var = var2;<br />
} <br />
</pre><br />
Dadurch wird erst am Ende gespeichert. <tt>var2</tt> lebt in Register <tt>r24</tt>.<br />
<pre><br />
foo2:<br />
lds r24, var ; *movqi/4 [length = 2]<br />
subi r24,lo8(-(1)) ; addqi3/2 [length = 1]<br />
cpi r24,lo8(11) ; cmpqi/2 [length = 1]<br />
brlt .L2 ; branch [length = 1]<br />
ldi r24,lo8(1) ; *movqi/2 [length = 1]<br />
.L2:<br />
sts var, r24 ; *movqi/3 [length = 2]<br />
ret<br />
</pre><br />
<br />
Bei diesem einfachen Beispiel spart man lediglich eine Instruktion. Bei komplexeren Rechnungen oder längeren Datentypen kann es aber durchaus lohnender sein, in lokale Register zu kopieren.<br />
<br />
==Arithmetik==<br />
<br />
=== Daten zerlegen/zusammensetzen ===<br />
<br />
In systemnahen Programmen hat man oft was Problem, auf die einzelnen Bytes oder Bitfelder einer grösseren Datenstruktur zuzugreifen. Indem man sich ein Komposit baut, das die gewünschten Strukturen überlagert, kann man effizient z.B. auf Bytes zugreifen. Ausnahme sind Bitfelder, deren Verwendung etwas breiten Code ergibt. Bitfelder "von Hand" zu manipulieren, ist da manchmal effizienter, führt jedoch zu schlecht lesbarem Code.<br />
<br />
Oft benötigt wird der Zugriff auf die einzelnen Bytes eines <tt>int</tt>, also der Zugriff auf die Bytes eines 16-Bit-Wertes:<br />
<br />
typedef union<br />
{<br />
unsigned char asByte[2];<br />
unsigned short asWord;<br />
int asInt;<br />
} data16_t;<br />
<br />
data16_t data;<br />
...<br />
int foo;<br />
uint8_t wert;<br />
<br />
data.asInt = foo;<br />
wert = data.asByte[1]; {{ccomment|die oberen 8 Bits von foo}}<br />
<br />
Ein komplexeres Beispiel, das noch mehr Datentypen überlagert:<br />
typedef ... foo_t;<br />
<br />
typedef union<br />
{<br />
unsigned char byte[4]; {{ccomment| Zugriff als Bytes (8 Bit) }}<br />
unsigned short word[2]; {{ccomment| Zugriff als Words (16 Bit) }}<br />
signed long slong; {{ccomment| Zugriff als signed long (32 Bit) }}<br />
<br />
struct {{ccomment| Zugriff auf einzelne Bitgruppen }}<br />
{<br />
unsigned bit_0_3 : 4; {{ccomment| 4 Bits (0..3) }}<br />
unsigned bit_4_8 : 5; {{ccomment| 5 Bits (4..8) }}<br />
unsigned bit_9_21 : 13; {{ccomment| 13 Bits (9..21) }}<br />
unsigned bit_22_31: 10; {{ccomment| 10 Bits (22..31) }}<br />
};<br />
<br />
foo_t foo; {{ccomment| Zugriff als foo-Struktur }}<br />
} data_t;<br />
<br />
...<br />
{<br />
data_t data;<br />
<br />
data.byte[2] = 12; {{ccomment| setzt byte 2 auf 12 }}<br />
data.bit_4_8 = 0x1f; {{ccomment| setzt bits 4..8 (5 Stück) alle auf 1 }}<br />
<br />
int anInt = data.foo.anInt; {{ccomment| liest ein Feld von foo (hier ein int) }}<br />
...<br />
}<br />
<br />
===libgcc2 verwenden===<br />
<br />
In der libgcc2 sind einige Arithmetik-Routinen in Assembler implementiert. Dazu gehören ein paar Algorithmen zu Division (mit Rest) und Multiplikation. <br />
<br />
Von diesen Algorithmen werden durch die avr-libc jedoch nur zwei Strukturen und Funktionen veröffentlicht: <tt>div_t</tt> und <tt>ldiv_t</tt> resp. die Funktionen <tt>div()</tt> und <tt>ldiv()</tt>. Siehe dazu deine Dokumentation zur avr-libc. Damit kann man Quotient und zusätzlich den Rest bei einer Division 16/16 bzw. 32/32 berechnen lassen; den Rest bekommt man quasi kostenlos als Nebenprodukt. Das ist praktisch, wenn man z.b. eine Zahl in Dezimaldarstellung umwandeln möchte oder von/nach [[BCD]].<br />
<br />
Zusätzlich zu den via avr-libc veröffentlichten Funktionen gibt es aber noch Routinen, die z.B. auf 8-Bit-Werten operieren oder mit <tt>unsigned</tt> Typen und dementsprechend effizienter sind.<br />
<br />
'''Beispiel: Umwandeln nach Dezimalstring'''<br />
<br />
Hier ein Beispiel, das Division mit Rest für <tt>unsigned short</tt> verwendet, um eine 16-Bit-Zahl in Dezimaldarstellung zu wandeln:<br />
<br />
{{ccomment| Struktur definieren und Funktion bekannt machen }}<br />
typedef struct<br />
{<br />
unsigned short quot;<br />
unsigned short rem;<br />
} udiv_t;<br />
<br />
extern udiv_t udiv (unsigned short, unsigned short) __asm__("__udivmodhi4");<br />
<br />
{{ccomment| 5 Ziffern (0...65535) und evtl. noch eine führende 0 }}<br />
#define DIGITS 6<br />
<br />
{{ccomment| +1 wegen String-Ende (wird im Startup auf 0 gesetzt) }}<br />
char string[DIGITS+1];<br />
<br />
{{ccomment| Wandelt zahl in Dezimaldarstellung um. }}<br />
{{ccomment| Der return-Wert zeigt irgendwo ins string[]-Array. }}<br />
{{ccomment| string[] wird verändert. }}<br />
char* toString (unsigned short zahl)<br />
{<br />
{{ccomment| s zeigt auf das Ende von string }}<br />
{{ccomment| string wird von hinten nach vorne gefüllt }}<br />
char *s = string + DIGITS;<br />
<br />
{{ccomment| qrem enthält Quotient (quot) und Rest (rem) der Divisionen }}<br />
udiv_t qrem = {.quot = zahl}; <br />
<br />
do<br />
{<br />
{{ccomment| Division mit Rest durch 10 }}<br />
{{ccomment| quot: Ergebnis für den nächsten Durchlauf }}<br />
{{ccomment| rem: Rest ist die Ziffer im 10er-System }}<br />
qrem = udiv (qrem.quot, 10);<br />
<br />
{{ccomment| Ziffer in Zeichen wandeln und speichern }}<br />
*(--s) = '0' + qrem.rem;<br />
} <br />
while (0 != qrem.quot);<br />
<br />
{{ccomment| Falls eine führende '0' gespeichert wurde: weg damit }}<br />
{{ccomment| ausser zahl war selbst schon 0 }}<br />
if (*s == '0' && *(s+1) != '\0')<br />
s++;<br />
<br />
return s;<br />
}<br />
<br />
Falls man eine Division und/oder Rest für 8-Bit braucht, dann geht für <tt>unsigned</tt> analog. <br />
<br />
'''Beispiel: BCD-Umrechnung'''<br />
<br />
Wandeln einer 8-Bit-Zahl <tt>0 &lt;= num &lt; 100</tt> nach [[BCD]]<br />
<br />
typedef struct<br />
{<br />
unsigned char quot; {{ccomment| Quotient }}<br />
unsigned char rem; {{ccomment| Rest (remainder) }}<br />
} udiv8_t;<br />
<br />
extern udiv8_t udiv8 (unsigned char, unsigned char) __asm__ ("__udivmodqi4");<br />
<br />
{{ccomment| Wandelt num nach BCD um, 0 <&#61; num <&#61; 99 }}<br />
{{ccomment| return-Wert ist dann 0x0 <&#61; return <&#61; 0x99 }}<br />
unsigned char to_bcd (unsigned char num)<br />
{<br />
udiv8_t qrem = udiv8 (num, 10);<br />
<br />
return (unsigned char) (qrem.quot << 4) | qrem.rem;<br />
}<br />
<br />
===Division durch Multiplikation===<br />
Bei den AVRs (Mega...) sind Multiplikationen deulich schneller als Divisionen. Besonders bei Fließkommazahlen lohnt es daher eine Division durch die Multiplikation mit dem Kehrwert zu ersetzen. Bei Integer Zahlen sind dem durch die Rundung Grenzen gesetzt.<br />
<br />
===Vermeiden von float und double===<br />
GCC kennt für die AVRs zur Zeit nur einen Fließkommatyp. Da keine Hardwareunterstützung dafür vorhanden ist, dauern Fließkommarechnungen relativ lange. Gerade Additionen sind deutlich langsamer als bei Integer. Auch die Codelänge nimmt erheblich zu, selbst wenn nur wenige Fließkommazahlen genutzt werden.<br />
<br />
Ein Alternative zu Fließkommazahlen ist die Benutzung von Festkommazahlen, auch wenn die nicht direkt von GCC unterstützt werden. Die Werte werden einfach alle mit einem konstanten Faktor (z.B. 256,1024 oder 1000) skaliert.<br />
Es wird dann mit Integer oder Long Integer gerechnet und bei Multiplikationen / Divisionen der zusätzliche Skalenfaktor berücksichtig.<br />
<br />
[[Kategorie:Microcontroller]]<br />
[[Kategorie:Quellcode C]]</div>Besserwessihttps://rn-wissen.de/wiki/index.php?title=Feldeffekttransistor&diff=22544Feldeffekttransistor2013-04-20T17:17:28Z<p>Besserwessi: neuer Abschnitt: MOSFET Auswahl</p>
<hr />
<div>Der Feldeffekttransistor, meist als FET (Field Effect Transistor) bezeichnet, ist ein unipolarer [[Transistor]]. Unipolar deshalb, weil im Gegensatz des bipolaren Transistors, je nach Typ, entweder nur Löcher oder Elektronen am Stromtransport beteiligt sind. <br />
<br />
Der FET hat 3 Anschlüsse, Source (Zufluss, Quelle), Gate (Tor) und Drain (Abfluss). Ein vierter Anschluss Bulk (Substrat) ist bei Einzeltransistoren i.d.R. mit Source verbunden und nicht extra herausgeführt.<br />
<br />
Die Steuerspannung zwischen Gate und Source erzeugt ein elektrisches Feld. Die dazugehörigen Ladungen beeinflussen die Leitfähigkeit des Source-Drain-Kanals des Feldeffekt-Transistors. Je nach benutzter Isolierung wird zwischen MOSFET und JFET (Junction- oder Sperrschicht-FET) unterschieden. <br />
<br />
Es gibt folgende Formen von FETs:<br />
* Sperrschicht-Feldeffekt-Transistor (JFET)<br />
* Metalloxidhalbleiter-Feldeffekt-Transistor (MOSFET)<br />
* dual Gate MOSFET : Spezialltype, hauptsächlich für HF<br />
* Schottky-Feldeffekt-Transistor (MESFET) - Spezialtype für HF<br />
* High Electron Mobility Transistor (HEMT) - Spezialtype für HF, > 1 GHz<br />
* Ionen-Sensitiver Feldeffekt-Transistor (ISFET) - als Sensor<br />
<br />
==Schaltsymbole==<br />
;N-MOSFET (Anreicherungstyp, selbstsperrend): [[Bild:Schaltsymbol NFET.png]]<br />
<br />
;P-MOSFET (Anreicherungstyp, selbstsperrend): [[Bild:Schaltsymbol PFET.png]]<br />
<br />
;N-Kanal JFET (Verarmungstyp, selbstleitend): [[Bild:Schaltsymbol JFETN.png]] [[Bild:Schaltsymbol JFETN2.png]]<br />
<br />
;P-Kanal JFET (Verarmungstyp, selbstleitend): [[Bild:Schaltsymbol JFETP.png]] [[Bild:Schaltsymbol JFETP2.png]]<br />
<br />
==JFET==<br />
JFETs nutzen einen in Sperrrichtung betriebenen p-n-Übergang, um das elektrische Feld zu bilden. Theoretisch kann er auch in Flussrichtung betrieben werden, was allerdings den Vorteil der leistungslosen Ansteuerung zunichte macht. Bei vielen JFETs gibt es keinen, oder wenig, Unterschied zwischen Drain und Source. Sie können also für beide Stromrichtungen benutzt werden.<br />
Ohne Gate-Source Spannung ist ein JFET leitend. Durch Spannung in Sperrichtung wird der Strom reduziert und erreicht bei der Abschnürrspannung (sog. pinch-off voltage) schließlich 0A. Bei kleinen Drain-Source Spannung verhält sich der JFET wie ein Spannungsgesteuerter Widerstand. Bei Drain-Source Spannung über etwa 1-2 V verhält sich ein JFET wie eine Spannungsgesteuerte Strombegrenzung. JFETs werden hauptsächlich als Schalter für Signalspannungen und als schnelle hochohmige Verstärker eingesetzt. Es gibt viele Operationsverstärker mit JFET Eingängen.<br />
Gebräuchliche N-Kanal JFETs sind: BF245, BF256, 2N4416. P-Kanal JFETs sind selten, es gibt sie aber.<br />
<br />
==MOSFET==<br />
MOSFETs haben eine dünne Oxidschicht als Isolierung zwischen Gate und dem Kanal. MOSFETs können so hergestellt werden, dass sie ohne Gate-Source Spannung sperren (Anreicherungs- bzw. Enhancement-Typ) oder leiten (Verarmungs- bzw. Depletion-Typ). Die einzeln erhältlichen MOSFETs sind aber ohne Gate-Source-Spannung fast alle sperrend (Enhancement-Typ), P-Channel gibt es wohl nur als Enhancement-Typ. Ab einer Gate-Source-Spannung von z.B. +2 V (N-Channel FET) steigt die Leitfähigkeit bzw. der Drain-Source-Strom an. Für den maximalen Strom sind typisch 10 V oder bei sogenannten Logic-Level Mosfets ca. 4 V nötig. Die maximal zulässige Gate-Source-Spannung liegt je nach Typ bei etwa 20-30 V, bei Logic-Level FETs zum Teil darunter.<br />
Bei einigen wenigen MOSFETs ist das Substrat als vierter Anschluss separat herausgeführt. Die Steuerspannung ist dann die Spannung zwischen Gate und Substrat. Sonst ist das Substrat intern mit Source verbunden und der MOSFETs enthält damit eine Diode zwischen Drain und Source. Zum Teil wird die Diode im Schaltungssymbol mit eingezeichnet. Es kann also Drain und Source hier nicht vertauscht werden, wenn mehr als 0,5 V Spannung anliegen. Die integrierte Diode kann einen ähnlich hohen Strom wie der FET vertragen und kann bei Brückenschaltungen als Freilaufdiode genutzt werden. Besonders kleine MOSFETs sind empfindlich gegen elektrostatische Aufladungen.<br />
<br />
== Verwendung für Schaltanwendungen ==<br />
MOSFETs werden oft für Schaltanwendungen verwendet, da sie größere Leistungen als bipolare Transistoren schalten können und geringere Verluste haben. Anders als bipolare Transistoren können MOSFETs als Schalter (d.h. bei kleiner Drain-Source Spannung, oder ausgeschaltet) problemlos parallelgeschaltet werden. Ein weiterer, wesentlicher Vorteil ist, dass sie leistungslos geschaltet werden. Wenn das Gate einmal auf eine bestimmte Spannung aufgeladen wurde, dann ist keine weitere Steuerleistung nötig. Bei bipolaren Transistoren muss die ganze Zeit ein relativ hoher Basisstrom fliessen. Allerdings hat das Gate eine relativ hohe Kapazität (ca. 1 nF für einen 10 A MOSFET). Um die Verluste beim Schalten zu minimieren, muss das Gate schnell geladen und wieder entladen werden. Dafür ist kurzzeitig ein relativ hoher Strom nötig. Aus diesem Grund gibt es fertige Treiberbausteine, wie den ICL7667, die das Gate sehr schnell umladen können. Ein schnelles Schalten des Mosfets ist insbesondere dann wichtig, wenn der Mosfet mit einer hohen Frequenz an- und ausgeschaltet wird (z.B. [[PWM]], Schaltnetzteile). <br />
<br />
Andererseits darf das Schalten auch nicht schneller erfolgen als der Rest der Schaltung es verträgt. Begrenzend sind hier z.B. ungewollte Induktivitäten, die Erhohlzeiten von Dioden und die Gefahr von Funkstörungen. Für eine definierte Schaltzeit wird oft ein kleiner Widerstand (ca. 100 Ohm) vor das Gate geschaltet. Wichtig ist der Widerstand besonders dann, wenn der Aufbau nicht für hohe Frequenzen ausgelegt ist, denn sonst besteht die Gefahr, dass es zwischenzeitlich zu HF-Schwingungen und damit Funkstörungen kommt. <br />
<br />
In der Praxis werden wesentlich häufiger N-Kanal Mosfets als P-Kanal Mosfets verwendet. Das liegt daran, dass sich die Elektronen im Halbleiter leichter bewegen können als Löcher. Aus diesem Grund haben N-Kanal Mosfets bei gleicher Chipfläche einen geringeren RDS_ON und damit geringere Verluste. <br />
<br />
Die folgende Schaltung zeigt die Verwendung eines MOSFETs zum Regeln eines Motors über [[PWM]]. Der eingezeichnete BS170 ist allerdings nur für kleine Lasten geeignet.<br />
<br />
[[Bild:fetschaltstufe.jpg|center]]<br />
<br />
===Auswahl des passenden MOSFET===<br />
Es gibt eine verwirrend große Auswahl an MOSFETs. Die im Datenblatt genannte Strombelastbarkeit ist eher theoretischer Natur, genutzt wird meist nur einer kleiner Teil davon - wichtiger ist meist der On-Widerstand: darüber berechnen sich die Verluste im eingeschalteten Zustand. Hierbei ist allerdings meist der Widerstand bei erhöhter Temperatur von Bedeutung, und der kann etwa um den Faktor 1,5-2 höher liegen als der oben im Datenblatt genannte. Größere MOSFETs mit kleinerem Widerstand haben allerdings auch eine größere Gate-kapazität und damit mehr Verluste beim Umschalten. Je höher die Schaltfrequenz, desto wichtiger ist es hier einen guten Kompromiss zu finden und die Spannungsfestigkeit nicht zu hoch zu wählen. Eine unnötig hohe Spannungsfestigkeit führt zu hohem On-Widerstand bzw. unnötig großer Gate-Kapazität und hohem Preis. Die Spannungsfestigkeit muss hoch genug sein, mehr als etwa 50% Reserve über der höchsten möglichen Spannung sind aber eher nicht nötig. Lediglich bei Linearbetrieb eine eine höhere Spannungsfestigkeit oft sinnvoll, da Typen für geringe Spannung oft schleicht dafür geeignet sind. <br />
<br />
Ein weiterer wichtiger Parameter ist die Schwellspannung, bzw. welche Spannung für die Ansteuerung am Gate nötig ist. Zum Schalten wird eine Spannung deutlich (z.B. 2-3 V) über der Schwellspannung benötigt. MOSFETs, die zur Steuerung mit 5 V geeignet sind, sogenannte Logic level MOSFETs, erkennt man meist daran, dass im Datenblatt der On-Widerstand bei 4-5 V angeben ist. Der dazu angebene Strom ist auch eine Hinweis auf den Strom der maximal realistisch nutzbar ist.<br />
<br />
Als Hilfe bieten viel Händler und Hersteller eine parametrische Suche, um die Auswahl z.B. auf die passende Spannungsfestigkeit, Gehäuseform und den Preisrahmen zu begrenzen.<br />
<br />
== Bauform Beispiel ==<br />
[[Bild:fetbs170.gif|center]]<br />
<br />
<br />
= Weblinks =<br />
[http://www.mikrocontroller.net/articles/MOSFET-Übersicht |Liste von MOSFET Typen (Mikrocontroller.net)]<br />
<br />
[[Kategorie:Elektronik]]</div>Besserwessi