K (→GCC: überflüssige (verwirrende) includes entfernt) |
K (→GCC: überflüssige (verwirrende) includes entfernt) |
||
Zeile 31: | Zeile 31: | ||
<pre> | <pre> | ||
− | |||
− | |||
#include <avr/io.h> | #include <avr/io.h> | ||
Zeile 42: | Zeile 40: | ||
//----------------------------------------------------- | //----------------------------------------------------- | ||
− | void _writeChar(char c) | + | void _writeChar (char c) |
{ | { | ||
while (!(UCSRA & (1<<UDRE))) {} | while (!(UCSRA & (1<<UDRE))) {} | ||
Zeile 48: | Zeile 46: | ||
} | } | ||
//----------------------------------------------------- | //----------------------------------------------------- | ||
− | void _writeString(unsigned char *string) | + | void _writeString (unsigned char *string) |
{ | { | ||
− | while ( *string) | + | while (*string) |
− | _writeChar(*string++); | + | _writeChar (*string++); |
} | } | ||
//----------------------------------------------------- | //----------------------------------------------------- | ||
Zeile 60: | Zeile 58: | ||
UBRRL = (unsigned char) USART_BAUD_SELECT; | UBRRL = (unsigned char) USART_BAUD_SELECT; | ||
_writeString(cText); | _writeString(cText); | ||
+ | |||
+ | // Endlossschleife: nichts mehr tun | ||
+ | while (1) | ||
+ | {} | ||
} | } | ||
</pre> | </pre> |
Version vom 28. November 2005, 16:00 Uhr
Inhaltsverzeichnis
Source-Vergleich
Es sollen hier einige Beispiele gebracht werden, wie einige Dinge in verschiedenen Sprachen gelöst werden können. Es sollte nicht als Wettbewerb der Compiler gesehen werden, welcher denn nun der "Bessere" sei. Über Vorzüge und Nachteile der verschiedenen Sprachen gibt es durchaus kontroversielle Ansichten. Daran will ich hier gar nicht rütteln, über Geschmack kann man nicht streiten.
Hello, world
Ein ganz einfaches Programm, oft auch das erste, ist, den Text "Hello, world !" auf dem Terminal erscheinen zu lassen.
Was so trivial klingt, hat den Zweck, mehrere Dinge zu überprüfen:
- Komme ich mit der Entwicklungsoberfläche zurecht
- funktioniert das Kompilieren und das Übertragen eines Programms auf den Microcontroller
- stimmen die Einstellungen Quartz und Baudrate
- funktioniert die RS232-Verbindung mit dem PC
Natürlich ist der Text vollkommen egal, er hat sich ganz einfach eingebürgert und fast jeder weiß, was damit gemeint ist.
BasCom
Die folgenden vier Zeilen lassen ahnen, warum Bascom speziell für Einsteiger geradezu ein Segen ist:
$Crystal=8000000 $Baud=9600 Print "Hello, world !" End
GCC
#include <avr/io.h> #define F_CPU 8000000 #define USART_BAUD_RATE 9600 #define USART_BAUD_SELECT (F_CPU/(USART_BAUD_RATE*16L)-1) char cText[] = "Hello, world !\r\n"; //----------------------------------------------------- void _writeChar (char c) { while (!(UCSRA & (1<<UDRE))) {} UDR = c; } //----------------------------------------------------- void _writeString (unsigned char *string) { while (*string) _writeChar (*string++); } //----------------------------------------------------- void main() { UCSRB |= (1<<TXEN); UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); UBRRL = (unsigned char) USART_BAUD_SELECT; _writeString(cText); // Endlossschleife: nichts mehr tun while (1) {} }
Bemerkungen
Wenn jemanden der Aufwand bei GCC erschrecken sollte:
GCC hat keine Standard-Annahme darüber, wie und wo eigentlich der Output stattfinden sollte. Es ist für ihn nicht selbstverständlich, die UART zu verwenden. Dadurch muß natürlich mehr definiert werden. Dem BasCom kommt zugute, daß er die ganze Konfiguration mit AVR, RS232 und PC-Terminal erstmal als gegeben nimmt.
Tastatur-Echo
Wenn das vorhergegangene Beispiel funktioniert hat, wird man natürlich auch überprüfen wollen, ob auch die umgekehrte Richtung VOM Terminal klappt. Die einfachste Methode ist es, ganz einfach jedes Zeichen, das eingegeben wird, sofort zurückzusenden.
BasCom
$Crystal=8000000 $Baud=9600 DIM Zeichen as Byte Do inputbin Zeichen Printbin Zeichen Loop End
GCC
#include <avr/io.h> #define F_CPU 8000000 #define USART_BAUD_RATE 9600 #define USART_BAUD_SELECT (F_CPU/(USART_BAUD_RATE*16L)-1) char bZeichen; //----------------------------------------------------- void main() { UCSRB = (1<<RXEN)|(1<<TXEN); UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); UBRRL = (unsigned char) USART_BAUD_SELECT; while (1) { while ( !(UCSRA & (1<<RXC)) ) {} bZeichen = UDR; while (!(UCSRA & (1<<UDRE))) {} UDR = bZeichen; } }
Bemerkungen
Hier ist der Unterschied des Codes nicht mehr so groß. Ganz klar, hier konnte BasCom wegen der Aufgabenstellung nicht auf eine vorgefertigte komplexe Funktion zurückgreifen.
Signallänge messen
Sehr oft ist es bei Controllern nötig, die Länge eines kurzen Impulses zu messen. Zum Beispiel wenn man einen RC-Empfänger (Modellbauempfänger) an ein Controllerboard anschließt. Ein RC-Empfänger sendet alle 20 Millisekunden ein High-Impuls von 1 bis 2 Millisekunden Länge aus. Die Länge dieses Impules bestimmt die Position des Steuerknüppels. Man braucht also nur diese Impulsdauer zu messen, um ein Fernsteuersignal auszuwerten.
BasCom
Bascom vefügt für diesen Zweck über den PULSEIN-Befehl, daher wird ein Programm extrem kurz. Die Messung erfolgt in etwa in 100 Abstufungen, da Pulsein in 10 µSek Schritten die Zeit ermittelt (läßt sich in Libary ändern)
$regfile = "m32def.dat" $framesize = 32 $swstack = 32 $hwstack = 32 $crystal = 16000000 'Quarzfrequenz $baud = 9600 Dim Rckanal As Word Do Pulsein Rckanal , Pind , 2 , 1 'Messung Zeit zwischen 1 und 0 Pegel Print "RC Signal: " ; Rckanal ; "0 uS" Wait 2 Loop End
GCC
Das GCC-Beispiel das die gleiche Aufgabe erfüllt:
... Vielleicht kann hier ein GCC-Kollege das Beispiel einfügen ...
Externe Interrupts
Dieses kleine Beispiel demonstriert, wie man in Bascom und GCC Interrupt-Routinen anlegt. Also Programmzeilen, die nur dann ausgeführt werden, wenn ein Low/High Pegel an einem bestimmten PIN eines Controllers wechselt. Bei einem solchen Wechsel wird das Hauptprogramm unterbrochen und die Interrupt-Routine aufgerufen. Anschließend wird das Hauptprogramm wieder weiter ausgeführt als sei nix gewesen. In diesem Beispiel macht das Hauptprogramm garnichts, es ist nur eine Endlosschleife. Die Interruptroutine schalten bei jedem Aufruf den Zustand einer LED um.
BasCom
$regfile = "m32def.dat" 'z.B. rn-control $framesize = 32 $swstack = 32 $hwstack = 32 $crystal = 16000000 'Quarzfrequenz $baud = 9600 Config Pinc.2 = Output 'An dem PIN sollte LED sein Led3 Alias Portc.2 'Hier geben wir der Definition einen schöneren Namen Config Int0 = RISING 'Interrupt bei steigender Flanke On Int0 Irq0 'Festlegen wo bei externem Interrupt hin gesprungen wird Enable Int0 'Diesen Interrupt aktivieren Enable Interrupts 'Alle aktivierten Interrupts einschalten Do 'Endlosschleife Loop End 'Interrupt Routine wird immer ausgelöst wenn der Pegel von 0 auf 1 am 'INT0 (Pin 18 PD2) Eingang wechselt Irq0: Toggle Led3 Return
GCC
Das GCC-Beispiel, das die gleiche Aufgabe erfüllt:
#include <avr/io.h> #include <avr/interrupt.h> #include <avr/signal.h> #define LED3 (1 << PC2) /* Interrupt Routine wird immer ausgelöst wenn der Pegel von 0 auf 1 am INT0 (Pin 18 PD2) Eingang wechselt */ SIGNAL (SIG_INTERRUPT0) { PORTC ^= LED3; // Pin wechseln } //----------------------------------------------------- int main() { /* Ports und Interrupts initialisieren */ DDRC |= LED3; // PC2 als Ausgang DDRD &= ~(1 << DDD2); // PD2 als Eingang (ext. Interrupt 0) MCUCR |= ((1 << ISC01) | (1 <<ISC00)); // steigende Flanke an INT0 erzeugt einen Interrupt GICR |= (1 << INT0); // Diesen Interrupt aktivieren sei(); // Alle aktivierten Interrupts einschalten while(1); // Endlosschleife }