Software Wir haben die Sprache C benutzt, da es für fast alles einen C-Compiler gibt, ich nur diese Sprache kann und Sie einfach die schnellste Hochsprache ist die es gibt. Compiler: Unser Compiler war der GCC-Compiler (GCC = GNU C Compiler; neuerdings steht das für GNU C Collection), der unter dem GNU-Projekt steht. Dieses Projekt ist gegründet worden um ein vollständiges freies Betriebssystem mit dem Namen GNU zu entwickeln. Wichtig ist zusagen, das frei nicht kostenlos heißt, sondern viel das der Quellcode jedem frei zur Verfügung steht. Etwas was viele bei dem Wort Open-Source falsch verstehen, denn im englischen heißt „free“ frei und kostenlos. Um aber zum Thema zurück zukommen, ein Grund war es den Compiler zu nehmen, da er hier tatsächlich auch frei im Internet zu bekommen ist und einer der besten C-Compilern ist aufgrund der Jahrelangen arbeit an diesem Compiler-Projekt „GCC“. Entwicklungsumgebung: Das Programm oder IDE (Integrated Development Environment; auf deutsch = Integrierte Entwicklungsumgebung) für das wir uns entschieden haben oder mussten ist AVRStudio 4, da es das einzige war womit wir simulieren konnten. Das Programm Deklaration #include : Hiermit wird die Header-Datei „avr/io.h“ eingebunden. #include : Hiermit wird die Header-Datei „stdint.h“ eingebunden. int v=0; : Dies ist eine Initialliesierung. Erstmal wird dem Compiler ein Speicherplatz zur Verfügung gestellt „v“, dieser Speicherplatz wird als Ganzzahl (integer=int) Definiert und zum Schluss wird diesem Speicherplatz noch der Wert 0 zugewiesen. int v1=0; : Das gleiche passiert hier auch nur das es ein weiteren Speicherplatz mit einem anderen Namen gibt und zwar „v1“. int v2=0; : alles gleich nur anderer Name „v2“. int v3=0; : „v3“ int impuls_diverenz; : Hier ist jetzt ein Unterschied zusehen, z.B: ist dem Speicherplatz keinen Wert zugewiesen worden. Warum nicht? In diesem Fall ist es nicht so wichtig, da diese Variable sowieso nur ein Speicher für ein Ergebnis ist. unsigned int impulse_1; : Jetzt wird’s noch komplizierter, den was ist „unsigned“ ? Damit sagen wir dem Compiler, das int nicht kleiner als 0 sein kann. Da wir mit diesem ein 8 Bit Code speichern wollen. Und er bis 255 Zählen soll. Würden wir das nicht machen würde er nicht richtig zählen, da er dann auch negative werte anzeigen könnte. Da die nächsten nur andere Namen tragen werde ich die restlichen hier nicht mehr aufführen. Das Hauptprogramm („Main“-Funktion) int main(void) { … } Dieser Teil ist schon ein ganzes Programm, nämlich das Hauptprogramm. Alles was in diesem Programm steht wird ausgeführt. Alles was außerhalb steht wird nicht ausgeführt oder der Compiler kompiliert nicht da er eine Fehlermeldung ausgibt. Das „int“ haben wir schon kennengelernt. Hier heißt es das Ganzzahlen überall stehen können innerhalb der Funktion und außerhalb. „main“ ist ein reservierter Name, jedes C-Programm muss genau eine „main“-Funktion enthalten. In dem „(void)“ wird normalerweise eine Deklaration vorgenommen, da wir das aber schon am Anfang des Programms gemacht haben ist es hier nicht mehr von nöten. „{„: dieses Klammerzeichen leitet den Beginn des Programms oder einer Funktion ein, ich sage speziell Funktion, da wir später noch andere Funktionen kennen lernen, die dazu da sind das Programm übersichtlicher zu machen. „}„ : das Zeichen leitet logischerweise den Programmschluss ein. Jetzt beschäftigen wir uns mit dem was in dem Hauptprogramm drinnen steht, also die Aktionen die das Programm ausführen soll. DDRA = 0xff; DDRB = 0xff; DDRC = 0x00; DDRD = 0xff; „DDR“ heißt in diesem Fall Datenrichtungsregister (data direction register). Die Buchstaben hinter den DDR sind die Ports. Weiter sollte man wissen das bei Mikrocontrollern (vielleicht auch wo anders) „0“ (Low) für Eingang steht und „1“ (High) für Ausgang. Und diese „0xff“ oder „0x00“ stehen für Hexadezimalschreibweise (erkennt C in unserem Programm automatisch und wandelt das um). In Dualschreibweise würde „0xff“ so aussehen „0b11111111“, das heißt also das alle Pins an den jeweiligen Ports auf Ausgang stehen. Im Fall „0x00“ würde in Dual so aussehen „0b00000000“, das heißt das alle Pins an den jeweiligen Ports stehen auf Eingang. Also kommen wir zudem Schluss das die Ports A, B und D auf Ausgang stehen und Port C als Eingang. PORTA = 0xf6; PORTB = 0xf6; PORTD = 0xf6; Also jetzt wird’s etwas komplizierter. Wir wissen jetzt das „0xff = 0b11111111“ heißt. Was ist aber mit „0xf6“? Ich komme wohl nicht drum rum etwas mehr über dual und hexadezimal zu erklären. Also das Dualsystem sollten wir alle kennen, denn in dem gibt es nur „high“ oder „low“. Also 1 oder 0. In unserem Fall geht es immer um 1 Byte oder eben 8 Bit, in dem es möglich ist 256 verschiedene Bitkombinationen darzustellen. Da an unserem Controller an jedem Port genau 8 Pins sind können wir getrost mit Ports arbeiten und das Hexadezimalsystem benutzen. Jede der 8 Bit Kombination kann eine Zahl zwischen 0 und 255 dezimal darstellen. Genau das gleiche gilt für Hexadezimal. Der unterschied zwischen dem Hexadezimal- und dem Dualsystem ist das im Hexadezimalsystem viel weniger zuschreiben ist. Außerdem ist das Umrechnen ins Dualsystem oder Dezimalsystem leichter als vom Dualsystem ins Dezimalsystem. Das Hexadezimalsystem stellt die Bitkombinationen in einer Basis von 16. Das heißt das Hexadezimalsystem benutzt 16 Zahlen. Normalerweise würde das aber so aussehen: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15. Aber ab der 10 sind dann wieder 2 Ziffern. Was tun? Um das zu vereinfachen hat man sich darauf geeinigt für die Zahlen 10, 11, 12, 13, 14, 15, die Buchstaben nach Reihenfolge des Alphabets zunehmen. Also sieht das dann so aus: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f. Jetzt hatte ich ja gesagt das die Umrechnung von Hexadezimal in Dezimal einfach sei. Aber wie funktioniert das? Man hat also eine Bitfolge von 8 Bits. Wenn man diese genau in der Mitte teilt hat man zweimal 4 Bitfolgen. Jetzt raten Sie mal wie viele Bitkombinationen mit 4 Bits möglich sind. Genau, 16. Man darf nur nicht vergessen das die letzten 4 Bits trotzdem von Anfang zählt. Hier ein Beispiel: dual 00011100 = dez 28 Hier wird die Dualzahl geteilt in : 0001 was Hexadezimal 1 und 1100 was Hexadezimal C ergibt. Also würde es Hexadezimal so aussehen 1C. Jetzt müssen wir uns noch fragen warum diese Bitfolge „0xf6“ und was „PORT“ ist. Das „PORT“ ist leicht erklärt, denn damit wird einfach nur der Port angesprochen. Für die Bitfolge „0xf6“ müssen wir in unser Datenblatt für den selbstgebauten Tacho schauen. Dann sehen wir, dass wir diese Pins ansprechen um eine „0“ auf unserer 7-Segment-Anzeige anzuzeigen. int zaehler(void); : Ich hatte im teil über main schon gesagt das man mehrere Funktion haben kann. Hier ist eine weitere Funktion. Das Deklarieren von Funktionen ist das gleiche wie bei Variablen. Weitere Funktionen stehen nicht in der mein. Jetzt fragt man sich wenn diese Funktion nicht in der main steht, wie kommt das dann das diese doch ausgeführt wird. Das ist ganz einfach, denn in der main wird dem Compiler gesagt das es eine Funktion mit dem Namen zaehler irgendwo gibt. Dem Compiler ist auch egal wo die Funktion steht, wichtig ist nur das die Deklaration in der main ist. Suchen tut er von selbst. do { } while(PINC2==0); : Dies ist mal was ganz neues, den das ist eine Bedingungsschleife. Dem Compiler wird gesagt „tuhe …, solange (…);“ auf englische „do …, while(…)“. Kurz gesagt er führt das was in der so genannten do-while-Schleife solange aus bis die Bedingung in while nicht mehr erfüllt ist. Also hier bis PINC2 ein Signal bekommt, z.B.: durch einen Schalter oder so. PINC2 heißt einfach das Pin2 an Port C ein Signal bekommen muss um die Schleife zu verlassen. Da ich mit der Funktion zaehler das wichtigste erklärt habe überspringen wir die anderen, die in der do-while-Schleife stehen. Die Funktionen: Jetzt kommt der wichtigste Teil, nämlich die eigentlichen Aktionen die der Controller ausführen soll. Diese haben wir in verschiedene Funktionen getan damit es übersichtlich bleibt. Die Funktion zaehler: Wie der Name schon sagt handelt es sich hier um einen Zähler. Für uns ein Impuls Zähler. Dieser Zähler soll Impulse zählen innerhalb von 1km/h. Dies muss man leider vorerst per Hand machen. Mir ist leider nicht eingefallen, wie der Controller lernen kann. Also muss man diesen Teil ein Mal mit unserem Rad auf 1km/h durchlaufen lassen und sich den Impulswert merken. Nun zur Erklärung zur Programmierung. Also da ich auf Signale warten muss habe ich zwei if-Anweisung benutzt. Da wir das Programm so leider nicht Test oder Simulieren konnten fehlen natürlich Feinabstimmungen. Also weiter im Text. Eine if-Anweisung hat eine Bedingung, diese steht in Klammern und wenn diese Bedingung zutrifft wird das in den geschweiften Klammern stehende ausgeführt. Es könnte ein Problem geben, denn was ist wenn die Bedingung nicht erfüllt wird. Das zu den gemeinten Feinabstimmungen. In der ersten if-Anweisung steht als Bedingung „PINC1 ==1“, das heißt wenn PINC1 ein Signal bekommt führt er die if-Anweisung aus. Da ich möglichst viel zählen möchte habe ich mich für den 16Bit Counter entschieden. In der if-Anweisung steht TCCR1B, dies ist das Time/Counter Control Register B Timer 1. Dieses ist dazu da um den Timer/Counter so einzustellen wie wir ihn verwenden möchten. In diesem Register stellen wir zwei Bits ein und zwar das CS12 und CS10 und beide auf High. Das CS heißt Clock Select was dazu dient den Prescaler einzustellen. Dieser wiederum verändert die Frequenz mit der der Timer/Counter betrieben wird. In unserem Fall mit einer Frequenz von CPU-Takt/1024. Damit verhindere ich das der Timer/Counter eine Overflow hat bevor das Rad einmal rum ist. Was natürlich nicht sein darf. Außerdem fängt der Timer/Counter in diesem Moment an zu zählen. Jetzt gucken wir uns die zweite if-Anweisung an. Die Bedingung ist die gleiche. Logisch weil er ja nur für eine Raddrehung zählen soll. Diese Anweisung ist eigentlich leichter zu verstehen, den TCNT1H und TCNT1l sind die Datenregister in dem der Zählwert also die Impuls Anzahl steht. Da sich das TCNT1 Register wieder ändert speichere ich den Zählwert in den Variablen impuls_1 und impuls_2. Jetzt sind die Impulse vorerst gespeichert. Und die gespeicherten Impulse werden zusammen gerechnet. Die Funktion variabler_zaehler: Diese Funktion ist wiederum leicht erklärt. Denn die macht genau das gleiche wie die Funktion zaehler. Mit einem unterschied im Gebrauch, den jetzt sind wir in der do-while-Schleife, das heißt das ist der richtige Zähler der nach jeder neuen 360° Rad Drehung, neu anfängt zu zählen. Das heißt also das die 7-Segment-Anzeige nach jeder Drehung die Geschwindigkeit neu zählt und ausgibt. Die Funktion berechnung: Hier findet also die Berechnung statt. Wichtig ist zu wissen wie ich mir das gedacht habe. Also erstmal da wir Ganzzahlen darstellen sind zwischenwerte nicht so wichtig bei den Impulsen. Wir haben ja am Anfang die Impulse für 1km/h gezählt. Und dann die Variablen Impulse. Wenn wir jetzt die Impulse, die wir bei 1km/h gezählt haben, nehmen und durch die variablen Impulse nehmen, bekommen wir einen Richtwert heraus. Stellt euch vor wir stellen den Motor auf 50km/h, dann muss sich das Rad natürlich 50 mal schneller drehen, also werden 50 mal weniger Impulse gezählt als bei 1km/h. Jetzt gehen wir zur eigentlichen Berechnung über. Also als erstes haben wir „28*2,54“, dies ist der Raddurchmesser. Die 28 sind Zoll, denn unser Testrad ist ein 28 Zoll Rad. Um den Durchmesser aber in cm zubekommen haben wir den Umrechnungsfaktor 2,54 genommen, denn 1Zoll = 2,54cm. Da wir aber eine Strecke haben wollen nehmen wir den Radumfang, außerdem müssen wir den nehmen da wir nach 360° ein Signal haben, wie oben schon besprochen. Für den Umfang gilt die Formel U = Pi * 2 * r, da wir aber den Durchmesser haben setzen wir (28*2,54) für 2 * r ein. Deswegen (3,14 * (28 * 2,54)). Wir nehmen für die Geschwindigkeitsberechnung v = s / t, s haben wir wie eben erklärt. Jetzt fehlt noch t. Für t habe ich mir überlegt erstmal einen Richtwert zunehmen um zu wissen mit was für einer Zeit ich rechnen muss. Dazu habe ich wieder die Methode wie oben benutzt, nämlich habe ich einfach gesagt v = 1km/h. Jetzt habe ich wieder die Formel v = s / t genommen, da wir ja ne Zeit als Richtwert nehmen wollten. Jetzt stelle ich diese Formel nach t um und setze die 1km/h und unser Radumfang ein, und schon haben wir die Zeit, die wir haben müssen um 1km/h darzustellen. Diese habe ich als Richtwert genommen und wie man oben im Programm sieht ist es die 8,037, mit der ist es wenn es so läuft wie gewollt möglich von 0 bis 999 alles anzuzeigen. Hinzu kommt jetzt der Richtwert von den Impulsen, die wir gezählt haben mit 1km/h und den variablen Impulsen. Diese nehmen wir um das Verhältnis darzustellen. Z.B.: bei 1km/h und einem Umfang von (3,14 * (28 * 2,54))/100 ist die Zeit wie wir wissen 8,037s. Und jetzt überlegen wir was für eine Zeit wir haben müssen bei 50km/h. Wie wir wissen muss die Zeit 50-mal kleiner sein. Und der Richtwert der Impulse sind diese 50-mal kleiner oder eben eine andere Zahl. Stellen sie sich vor wir haben bei dem festwert der Impulse 40 und der Wert bei den variablen Impulsen von 20, dann müsste doch bei v = 2km/h rauskommen oder? Das tut er auch! Denn 40/20 ergibt 2 und diese setzen wir beim Umfang oder eben bei der Zeit ein. Entweder muss der Umfang verdoppelt werden oder die Zeit halbiert um 2km/h auszugeben und das war eben die Aufgabe. Übrigens hat unsere Rechnung eine Toleranz von 0,0003s, was reicht um alle Zahlen anzugeben. Jetzt kommt nur noch die Darstellung. Wir haben im Programm mit Ganzzahlen gearbeitet und C hat eine Eigenart mit Kommazahlen umzugehen, den bei C fallen die Zahlen hinterm Komma weg und werden nicht auf oder abgerundet. Also müssen wir dafür sorgen, die Geschwindigkeit in 3 teile zu teilen. Worauf man aber noch achten muss ist, dass es ja auch sein kann das unsere Geschwindigkeit unter 100 oder unter 10 km/h ist. Dazu dienen die if-Anweisungen. Also als erstes wird geprüft wie groß die Zahl ist, wenn die Zahl größer als 100 oder gleich 100 ist geht er in die erste if-Anweisung. Dann fängt er an die Zahl in 3 Teile zuteilen. v1 = v / 100: v1 ist die erste ausgegebene Zahl also der Hunderter. z.B.: würde man, wenn v = 378 ist, als v1 ne 3 bekommen. Dann kommt die nächste Formel. v2 = (v-(v1*100))/10: für v würde er jetzt wieder die 378 haben, von der zieht er aber dann 300 ab (3*100=300). Das würde 78 ergeben(378-300=78). Und zum Schluss teilt er diese 78 noch durch 10 und man hat seine zweite Zahl die 7. Bei v3 = v- ((v1*100)+(v2*10)) zieht er alles ab. Also erstmal haben wir v1=100, also 3*100=300, dann haben wir noch v2*10, also(7*10=70). Wenn man diese jetzt zusammen rechnet bekommt man 370 und diese wir von den 378 abgezogen und man hat seine 3 Zahl. Das gleiche wird bei den beiden anderen if-Anweisungen auch gemacht nur das man da jeweils eine Zahl weniger hat. Zum Schluss wollte ich nur noch sagen, die 100 die abgezogen wird und die 3,6 die mal genommen wird in der ersten Formel sind nur zum umrechnen, die /100 von Zentimeter in Meter und die 3,6 von m/s in km/h. Die Funktionen segment_1, segment_2, segment_3 : Nicht wundern das hier was abgeschnitten ist, im Grunde ist alles was noch kommt das gleiche, deshalb werde ich nur das Prinzip erklären. Also diese drei Funktionen sind nur da um die errechneten Zahlen auf den 7-Segment-Anzeigen auszugeben. Auch hier werden wieder if-Anweisungen verwendet. Im ersten Segment wird v1 verwendet, also die erste Zahl von den teil Zahlen. Es wird also geprüft, mit der if-Anweisung, welche Zahl in v1 steht und je nachdem was da steht wird diese ausgegeben. Um diese auszugeben müssen natürlich wieder die Ports angesprochen werden. Die Bitkombinationen für die jeweiligen Zahlen haben wir etwas weiter vorne ja schon kennen gelernt. Das wird natürlich auch bei segment_2 und segment_3 benutzt, dort mit den jeweiligen Zahlen. Also bei segment_1 = v1, segment_2 = v2 und segment_3 = v3. Schluss: Das war es eigentlich, ich hoffe ich habe alles Angesprochen und Sie haben alles verstanden. Da wir die Platine für unseren Tacho so spät bekommen hatten, konnten wir dies leider nicht testen. Aber von der Software her hat der Compiler keine Probleme gemacht und vom Verständnis sollte es auch funktionieren. So ich habe schon viel zuviel geschrieben und es waren wahrscheinlich auch ein bissel zu viel Informationen. Aber wenn man das einmal durchliest sollte man es auch verstehen. Danke fürs zuhören. Dennis Wolkenhauer FOH5b Aus der Gruppe : Digitaltacho mit Thomas Owusu und