Forum: Mikrocontroller und Digitale Elektronik LCD 1x16 HD44870 kompatibel will einfach nicht mit der library von Peter Fleury funktionieren
Hallo Leute,
ich hab hier im Forum herausgefunden, dass die am meisten verbreitete
library für die Ansteuerung eines LC Displays mit der lcd Bibliothek von
Peter Fleury gemacht wird.
Ich sitze nun vor meinem Display und versuche seit zwei Tagen die
Bibliothek die aus den Dateien lcd.h und lcd.c besteht meinen
Anforderungen anzupassen. Ich habe alle Teile gelöscht, die ich nicht
brauche und Codefragmente angepasst, sodass diese auf mein Display
passen.
Ich benutze das STK500 Board und einen Atmega8515, außerdem geht es um
ein LC Display 1x16 HD44870 kompatibel.
Das Display möchte ich im 4 Bit Modus und an Port B betreiben.
Wie folgt ist das Display mit dem Port verbunden:
PB0 -> Enable
PB1 -> RS
PB2 -> frei
PB3 -> frei
PB4 -> DB4
PB5 -> DB5
PB6 -> DB6
PB7 -> DB7
RW am Display habe ich direkt auf Masse gelegt, da ich nicht vom Display
einlesen möchte.
Der uC läuft mit der Standardtaktfrequenz des STK500.
Ich habe nun in der Datei lcd.h so angepasst, dass die Pinbelegungen auf
mein LC Display stimmen. Mein veränderter Teil der lcd.h sieht dann so
aus:
1 | #define XTAL 4000000 /**< clock frequency in Hz, used to calculate delay timer */
| 2 |
| 3 | #define LCD_LINES 2 /**< number of visible lines of the display */
| 4 | #define LCD_DISP_LENGTH 8 /**< visibles characters per line of the display */
| 5 | #define LCD_LINE_LENGTH 0x40 /**< internal line length of the display */
| 6 | #define LCD_START_LINE1 0x00 /**< DDRAM address of first char of line 1 */
| 7 | #define LCD_START_LINE2 0x40 /**< DDRAM address of first char of line 2 */
| 8 | #define LCD_WRAP_LINES 0 /**< 0: no wrap, 1: wrap at end of visibile line */
| 9 |
| 10 | #define LCD_PORT PORTB /**< port for the LCD lines */
| 11 | #define LCD_DATA0_PORT LCD_PORT /**< port for 4bit data bit 0 */
| 12 | #define LCD_DATA1_PORT LCD_PORT /**< port for 4bit data bit 1 */
| 13 | #define LCD_DATA2_PORT LCD_PORT /**< port for 4bit data bit 2 */
| 14 | #define LCD_DATA3_PORT LCD_PORT /**< port for 4bit data bit 3 */
| 15 | #define LCD_RS_PORT LCD_PORT /**< port for RS line */
| 16 | #define LCD_E_PORT LCD_PORT /**< port for Enable line */
| 17 | #define LCD_DATA0_PIN 4 /**< pin for 4bit data bit 0 */
| 18 | #define LCD_DATA1_PIN 5 /**< pin for 4bit data bit 1 */
| 19 | #define LCD_DATA2_PIN 6 /**< pin for 4bit data bit 2 */
| 20 | #define LCD_DATA3_PIN 7 /**< pin for 4bit data bit 3 */
| 21 | #define LCD_RS_PIN 1 /**< pin for RS line */
| 22 | #define LCD_E_PIN 0 /**< pin for Enable line */
|
Die lcd.c Datei musste ich mehr anpassen. Hier nun also der komplette
Code:
1 | #include <inttypes.h>
| 2 | #include <avr/io.h>
| 3 | #include <avr/pgmspace.h>
| 4 | #include "lcd.h"
| 5 |
| 6 | // constants/macros
| 7 |
| 8 | #define DDR(x) (*(&x - 1)) /* address of data direction register of port x */
| 9 | #define PIN(x) (*(&x - 2)) /* address of input register of port x */
| 10 |
| 11 | #define lcd_e_delay() __asm__ __volatile__( "rjmp 1f\n 1:" );
| 12 | #define lcd_e_high() LCD_E_PORT |= _BV(LCD_E_PIN);
| 13 | #define lcd_e_low() LCD_E_PORT &= ~_BV(LCD_E_PIN);
| 14 | #define lcd_e_toggle() toggle_e()
| 15 | #define lcd_rs_high() LCD_RS_PORT |= _BV(LCD_RS_PIN)
| 16 | #define lcd_rs_low() LCD_RS_PORT &= ~_BV(LCD_RS_PIN)
| 17 |
| 18 | #define LCD_FUNCTION_DEFAULT LCD_FUNCTION_4BIT_1LINE
| 19 |
| 20 | // function prototypes
| 21 | static void toggle_e(void);
| 22 |
| 23 | static inline void _delayFourCycles(unsigned int __count)
| 24 | {
| 25 | if ( __count == 0 )
| 26 | __asm__ __volatile__( "rjmp 1f\n 1:" ); // 2 cycles
| 27 | else
| 28 | __asm__ __volatile__ (
| 29 | "1: sbiw %0,1" "\n\t"
| 30 | "brne 1b" // 4 cycles/loop
| 31 | : "=w" (__count)
| 32 | : "0" (__count)
| 33 | );
| 34 | }
| 35 |
| 36 | #define delay(us) _delayFourCycles( ( ( 1*(XTAL/4000) )*us)/1000 )
| 37 |
| 38 | /* toggle Enable Pin to initiate write */
| 39 | static void toggle_e(void)
| 40 | {
| 41 | lcd_e_high();
| 42 | lcd_e_delay();
| 43 | delay(1);
| 44 | lcd_e_low();
| 45 | }
| 46 |
| 47 | static void lcd_write(uint8_t data,uint8_t rs)
| 48 | {
| 49 | unsigned char dataBits ;
| 50 |
| 51 | if (rs) { /* write data (RS=1) */
| 52 | lcd_rs_high();
| 53 | }
| 54 | else { /* write instruction (RS=0) */
| 55 | lcd_rs_low();
| 56 | }
|
An dieser Stelle hab ich ein bisschen was verändert, da ja bei mir die
höherwertigen Bits an PortB auch den höherwertigen Bits des Displays
entsprechen. Das heißt also, das high nibble von data ist schon an der
richtigen Stelle und muss nicht irgendwie geshiftet werden. Im Gegensatz
zum Low nibbel, welches ich schließlich verschoben hab und dann erst ans
lcd sende.
1 | /* output high nibble first */
| 2 | dataBits = LCD_DATA0_PORT & 0x0F;
| 3 | LCD_DATA0_PORT = dataBits |(data & 0xF0);
| 4 | lcd_e_toggle();
| 5 |
| 6 | /* output low nibble */
| 7 | LCD_DATA0_PORT = dataBits | ((data << 4) & 0xF0);
| 8 | lcd_e_toggle();
| 9 |
| 10 | /* all data pins high (inactive) */
| 11 | LCD_DATA0_PORT = dataBits | 0xF0;
| 12 | }
| 13 |
| 14 | static inline void lcd_newline(uint8_t pos)
| 15 | {
| 16 | register uint8_t addressCounter;
| 17 |
| 18 | if ( pos < (LCD_START_LINE2) )
| 19 | addressCounter = LCD_START_LINE2;
| 20 | else
| 21 | addressCounter = LCD_START_LINE1;
| 22 |
| 23 | lcd_command((1<<LCD_DDRAM)+addressCounter);
| 24 |
| 25 | }/* lcd_newline */
| 26 |
| 27 | void lcd_command(uint8_t cmd)
| 28 | {
| 29 | lcd_write(cmd,0);
| 30 | }
| 31 |
| 32 | void lcd_data(uint8_t data)
| 33 | {
| 34 | lcd_write(data,1);
| 35 | }
| 36 |
| 37 | void lcd_gotoxy(uint8_t x, uint8_t y)
| 38 | {
| 39 | if ( y==0 )
| 40 | lcd_command((1<<LCD_DDRAM)+LCD_START_LINE1+x);
| 41 | else
| 42 | lcd_command((1<<LCD_DDRAM)+LCD_START_LINE2+x);
| 43 | }/* lcd_gotoxy */
| 44 |
| 45 | void lcd_clrscr(void)
| 46 | {
| 47 | lcd_command(1<<LCD_CLR);
| 48 | }
| 49 |
| 50 | void lcd_home(void)
| 51 | {
| 52 | lcd_command(1<<LCD_HOME);
| 53 | }
| 54 |
| 55 | void lcd_putc(char c)
| 56 | {
| 57 | lcd_write(c, 1);
| 58 | } /* lcd_putc */
| 59 |
| 60 | void lcd_puts(const char *s)
| 61 | /* print string on lcd (no auto linefeed) */
| 62 | {
| 63 | register char c;
| 64 |
| 65 | while ( (c = *s++) ) {
| 66 | lcd_putc(c);
| 67 | }
| 68 |
| 69 | }/* lcd_puts */
| 70 |
| 71 | void lcd_puts_p(const char *progmem_s)
| 72 | /* print string from program memory on lcd (no auto linefeed) */
| 73 | {
| 74 | register char c;
| 75 |
| 76 | while ( (c = pgm_read_byte(progmem_s++)) ) {
| 77 | lcd_putc(c);
| 78 | }
| 79 |
| 80 | }/* lcd_puts_p */
| 81 |
| 82 | void lcd_init(uint8_t dispAttr)
| 83 | {
| 84 | /* configure all port bits as output (all LCD lines on same port) */
| 85 | DDR(LCD_PORT) = 0xFF;
| 86 |
| 87 | delay(16000); /* wait 16ms or more after power-on */
| 88 |
| 89 | /* hier wird einfach ne 0x30 ans Display gesendet! */
| 90 | LCD_PORT = 0x30; // _BV heißt setze Bit an dieser angegebenen Stelle
| 91 | //Beispiel: x = 0000 0000 | _BV(5) = 0010 0000
| 92 | lcd_e_toggle();
| 93 | delay(4992); /* delay, busy flag can't be checked here */
| 94 |
| 95 | /* repeat last command */
| 96 | lcd_e_toggle();
| 97 | delay(64); /* delay, busy flag can't be checked here */
| 98 |
| 99 | /* repeat last command a third time */
| 100 | lcd_e_toggle();
| 101 | delay(64); /* delay, busy flag can't be checked here */
| 102 |
| 103 | /* now configure for 4bit mode */
| 104 | LCD_PORT = 0b00100000; // LCD_FUNCTION_4BIT_1LINE>>4
| 105 | lcd_e_toggle();
| 106 | delay(64); /* some displays need this additional delay */
| 107 |
| 108 | /* from now the LCD only accepts 4 bit I/O, we can use lcd_command() */
| 109 |
| 110 | lcd_command(LCD_FUNCTION_DEFAULT); /* function set: display lines -> sende 0x20 ans Display */
| 111 | delay(64);
| 112 | lcd_command(LCD_DISP_ON_CURSOR); /* display on, cursor on -> sende 0x0E ans Display */
| 113 | delay(64);
| 114 | lcd_command(0x06); /* set entry mode, inkrementieren, kein scrollen */
| 115 | delay(64);
| 116 | lcd_clrscr(); /* display clear */
| 117 | delay(200);
| 118 | lcd_command(dispAttr); /* display/cursor control */
| 119 |
| 120 | }/* lcd_init */
|
Die Funktion lcd_read und alles was damit in Verbindung steht, habe ich
aus dem Code entfernt, weil ich den RW Pin ja eh auf Masse gelegt habe
und somit aus dem Display kein Busyflag oder andere Daten auslesen kann.
AVR Studio übersetz alles ohne Fehler, das Display jedoch reagiert
nicht. Es zeigt 8 schwarze Balken an.
Das heißt ja wahrscheinlich, dass die Initialisierung nicht geklappt
hat.
Ich weis nur nicht an welcher Stelle es hakt. Das ausführende Programm
soll übrigens einfach nur lcd_init(LCD_DISP_ON_CURSOR); machen.
Also auszuschließen ist, dass das Display defekt ist, da ich es per
Assembler ansteuern kann.
Ich hoffe ich habe alle Informationen untergebracht und ihr könnt mir
irgendwie helfen. Vielleicht liegt es ja nur irgendwie an einer
Kleinigkeit. Zum Beispiel am Delay oder so.
Hallo,
ich hab mir das nicht weiter angeschaut, aber Du hast hoffentlich nach
den jeweiligen Display-Kommandos auch die Wartezeiten für die Ausführung
eingefügt?
Wenn Busy abgefragt wird, wird ja gewartet, bis das Display wieder
bereit ist und erst dann weitergemacht.
Wenn Du das rauswirfst mußt Du natürlich selbst für die passenden
Wartezeiten sorgen.
Gruß aus Berlin
Michael
Das mit der Rum-Nibbelei kann man machen, man kann sich dabei aber auch
leicht verheddern.
Leichter ist daher, die Pins als Bits zu definieren.
Dann ist man wirklich universell und die anderen Pins der Ports sind
uneingeschränkt nutzbar (sogar in Interrupts!).
Und viel langsamer ist das auch nicht:
http://www.mikrocontroller.net/attachment/30300/lcd_drv.zip
Peter
Benny Nestler wrote:
> Hallo Leute,
>
>>
> RW am Display habe ich direkt auf Masse gelegt, da ich nicht vom Display
> einlesen möchte.
Das könnte evl. problematisch sein, da Du ja zB. die Stati ausliest.
Ist schon ne Weile her, dass ich mich damit beschäftigt habe, aber ich
meine due Fleury-Lib macht das so.
@amiga
Eigentlich kann es nicht daran liegen. Ich warte zwar nicht mehr aufs
Busy Flag, allerdings habe ich zumindest bei der lcd_init routine nach
jedem lcd_command() aufruf ein delay eingefügt.
Aber ich glaube irgendwas stimmt mit meiner lcd_write funktion noch
nicht.
Ich habe gerade mal ausprobiert die letzten befehle aus der init routine
zu ersetzen. So wurde aus:
1 | lcd_command(LCD_FUNCTION_DEFAULT);
| 2 | delay(64);
| 3 | lcd_command(LCD_DISP_ON_CURSOR);
| 4 | delay(64);
| 5 | lcd_command(0x06);
| 6 | delay(64);
|
Jetzt dieser Code:
1 | LCD_PORT = 0x20;
| 2 | lcd_e_toggle();
| 3 | delay(64);
| 4 |
| 5 | LCD_PORT = 0x0E;
| 6 | lcd_e_toggle();
| 7 | delay(64);
| 8 |
| 9 | LCD_PORT = 0x06;
| 10 | lcd_e_toggle();
| 11 | delay(64);
|
Komischerweise wird jetzt das Display initialisiert. Allerdings versteh
ich nicht warum. Das kann eigentlich nur zufall sein. Denn eigentlich
ist das High Nibble zumindest bei 0x0E und 0x06 immer leer ... also
0000, aber nur diese Leitungen sind ja direkt an das LCD angeschlossen.
Das Muss also wirklich zufall sein, weil das Display ja schon im 4 Bit
Modus arbeitet.
MfG BlueMorph
@netdieter
Ja richtig, eigentlich wird mit der lcd.c / lcd.h das BusyFlag des
Displays ausgelesen. Aber man kann doch auch einfach diese Wartezeiten
durch dementsprechende Delays ersetzen. Oder??
@peda
Danke für deine bibliothek, ich werd sie mir gleich mal angucken! Ich
denke mal, dass ich mich da auch irgendwie "vernibbelt" hab, wie du so
schön gesagt hast.
Benny Nestler wrote:
> @netdieter
>
> Ja richtig, eigentlich wird mit der lcd.c / lcd.h das BusyFlag des
> Displays ausgelesen. Aber man kann doch auch einfach diese Wartezeiten
> durch dementsprechende Delays ersetzen. Oder??
Müsste gehen.
Die entsprechende Abfrage ist im PeFleury Code an einer Stelle zentral
zusammengefasst (wenn ich mich richtig erinnere).
Und genau das hätte ich an deiner Stelle auch getan:
Nicht damit anfangen, die Lib umzuschreiben, sondern erst mal alles so
lassen wies ist, und nur in dieser einen Funktion die Abfrage von Busy
durch einen delay von was_weiß_ich_wievielen ms ersetzen (von mir aus
100, damit ist man auf jeden Fall auf der sicheren Seite). Nachsehen obs
geht. Und erst dann anfangen aus den Funktionen die Teile
rauszuschmeissen die man nicht braucht, bzw. umzuschreiben.
@kbuchegg
Hab ich ja auch genauso zuerst gemacht.
Dann hats aber nicht funktioniert. Weil in der Origina lcd.c die Nibbel
anders übertragen wurden. Deshalb musste ich die dann auch umschreiben.
Aber ich werde jetzt erstmal die Bibliothek von peda benutzen. Die hat
nämlich fast auf anhieb funktioniert. Danke nochmal dafür @peda.
@peda
Also, deine Bibliothek funktioniert sehr gut. Mir ist nur an vielen
Stellen nicht klar, was überhaupt passiert. Es fängt an mit deiner Datei
mydefs.h
1 | #ifndef _mydefs_h_
| 2 | #define _mydefs_h_
| 3 |
| 4 |
| 5 | typedef unsigned char u8;
| 6 | typedef signed char s8;
| 7 | typedef unsigned short u16;
| 8 | typedef signed short s16;
| 9 | typedef unsigned long u32;
| 10 | typedef signed long s32;
|
Bis hierhin ist alles klar, aber was jetzt kommt versteh ich alles
nicht.
Ich hab gegoogelt, was volatile mach und herausgefunden, dass der
Prozessor bei einer volatile variable diese nicht in ein Register
ablegt, sondern direkt mit der Speicherstelle der Variable gearbeitet
wird. Aber eigentlich versteh ich's nicht.
1 | #define vu8(x) (*(volatile u8*)&(x))
| 2 | #define vs8(x) (*(volatile s8*)&(x))
| 3 | #define vu16(x) (*(volatile u16*)&(x))
| 4 | #define vs16(x) (*(volatile s16*)&(x))
| 5 | #define vu32(x) (*(volatile u32*)&(x))
| 6 | #define vs32(x) (*(volatile s32*)&(x))
|
Okay, hier wird jetzt anscheinend irgendeine Struktur erstellt. Aus
unsigned chars. Aber hat diese Structur 8 unsigned chars die b0 .. b7
heißen? und was heißt ":1"? Ist das eine Zuweisung?
Und was bedeutet __attribute__((_packed_));
1 | struct bits {
| 2 | u8 b0:1;
| 3 | u8 b1:1;
| 4 | u8 b2:1;
| 5 | u8 b3:1;
| 6 | u8 b4:1;
| 7 | u8 b5:1;
| 8 | u8 b6:1;
| 9 | u8 b7:1;
| 10 | } __attribute__((__packed__));
|
Das hier kommende Codefragment versteh ich auch leider gar nicht!
1 | #define SBIT(port,pin) ((*(volatile struct bits*)&port).b##pin)
|
Es wäre schön, wenn mir jemand erklären könnte, was eigentlich hier so
passiert. Es sind ja nur Definitionen und Variablendeklarationen.
MfG BlueMorph
volatile
ist der Hinweis an den Compiler, dass sich eine Varible auf Wegen ändern
kann, die der Compiler nicht herausfinden kann. Dieser Hinweis sagt dem
Compiler, alle Optimierungen auf dieser Variablen zu unterlassen.
Insbesondere muss er den Wert der Variablen jedes mal bei einem
Lesezugriff aus dem Speicher holen, selbst wenn er das unmittelbar davor
schon gemacht hat und der Wert noch in einem Register herumlungert.
Stell dir einfach mal vor, du hättest eine Hardware-Uhr, die so
geschaltet ist, dass sie als ganz normale Variable im Speicher
auftaucht. Und nun möchtest du den Start der nächsten Sekunde abwarten
now = uhren_Sekunde;
while( uhren_Sekunde == now )
;
Für den Compiler sieht es jetzt so aus als ob du völlig übergeschnappt
bist und eine Endlosschleife produziert hast. Er kann nicht erkennen,
dass sich uhren_sekunde völlig ohne Programmzutun verändert (weil das ja
die Hardware macht).
Lösung: Die 'Variable' uhren_Sekunde als volatile deklarieren und schon
hält sich der Compiler aus allem raus, was mit dieser Variablen
zusammenhängt. Du willst in der Schleife uhren_Sekunde ständig auslesen
- du kriegst das auch.
Struktur mit :
Das sind Bitvariablen. Ein unsigned char wird in lauter einzelne Bits
unterteilt, die Namen von b0 bis b8 bekommen.
> Das hier kommende Codefragment versteh ich auch leider gar nicht!
Ich geh mal davon aus, dass dich das ## verwirrt.
Das ist eine Concat Anweisung für den Preprozessor.
Wird für pin zb 5 an das Makro übergeben, so bildet b##pin daraus das
Token b5
Anders ist das nicht machbar, den bpin würde nicht das Makroargument
matchen (pin != bpin) und die Ersetzung für "b pin" ist "b 5" also mit
einem Leerzeichen dazwischen.
@kbuchegg
Danke dir erstmal für deine Erklärungen. das mit dem volatile hab ich
nun verstanden! War ja auch ein sehr anschaulisches Beispiel mit der
Uhr.
1 | #define vu8(x) (*(volatile u8*)&(x))
|
Okay, wie muss ich nun aber dieses Makro (!?) deuten?
Wenn ich nun irgendwo im Programmtext vu8(x) [und für x jetzt ja
wahrscheinlich "was ich will"] aufrufen, dann macht der Compiler was
draus??
Der erste Stern ist doch ein Zeichen für einen Zeiger auf den typ u8.
Wofür ist denn nun aber das zweite Sternchen? und macht das & dann eine
Bitverknüpfung mit dem x?? Also ich versteh einfach irgendwie nicht was
diese ganze Anweisung / Makro macht.
Okay, das mit der Struktur hab ich jetzt so aufgenommen. Das heißt also,
auf die Struktur "Bits", kann man Hilfe des Punktoperators auf die
einzelnen bits zugreifen? Ist die Struktur jetzt eigentlich nur ein
einzelnes unsigned char, bei dem ich allerdings jedes bit einzeln setzen
kann?? Vom Verständnis her, wäre sowas möglich??
1 | Bits.b0 = 0
| 2 | Bits.b1 = 1
| 3 | Bits.b2 = 0
| 4 | .
| 5 | .
| 6 | .
|
Und was macht dieser Zusatz?? 1 | __attribute__((__packed__));
|
-----------------------------------------------------------------
Und zu diesem hier: 1 | #define SBIT(port,pin) ((*(volatile struct bits*)&port).b##pin)
|
Kann man das irgendwie in Worte fassen?? Also von wegen, wenn man im
Programm SBIT(PORTB, 1) aufruft, dann macht der Compiler eigentlich
was??
Danke schonmal für eure Hilfe, ich hoffe ich nerve euch nicht mit diesen
Verständnissachen. Aber ich will nicht einfach nur Andwenden, sondern
lieber auch Verstehen!
MfG BlueMorph
Okay, ich hab nun ein bisschen gegoogelt und schonmal herausgefunden,
was man mit dem
1 | __attribute__((__packed__))
|
machen kann. Wen es interessiert, der sei auf diese Website verwiesen.
http://www.hs-augsburg.de/~sandman/c_von_a_bis_z/c_017_011.htm
Langsam wird mir vieles klarer.
MfG BlueMorph
1 | #define vu8(x) (*(volatile u8*)&(x))
|
Nochmal ein versuch, diese Zeile zu "verwörtlichen". Heißt das einfach
"nur", dass wenn man ein vu8(x) aufruft, der Compiler eine Zeiger des
Datentypes volatile u8, auf die Adresse von x macht??? Aber was heißt
dann das zweite Sternchen???
Eine Variable, die ein Interrupt an das Main übergibt, muß oft vom Main
her volatile sein.
Blöd ist nur, daß dann auch der Interrupt selber jeden Zugriff nicht
optimiert, obwohl er alleinig darüber herrscht.
Dann kann man die Variable ganz normal definieren und nur das Main macht
den Zugriff über das Makro. D.h. nur das Main denkt, sie sei volatile. 1 | u8 i;
| 2 |
| 3 | ISR( xxx )
| 4 | {
| 5 | i++;
| 6 | if( i == 4 )
| 7 | ...
| 8 | }
| 9 |
| 10 | main
| 11 | {
| 12 | while( vu8(i) != 3 );
| 13 | ...
| 14 | }
|
Peter
Ein Makro 'rufst' du gar nicht auf. Und auch der eigentlich Compiler hat
damit nichts zu tun. Makros werden vom Präprozessor abgearbeitet und
sind reine Textersetzung.
Du definierst eine Textersetzungsregel, und der Präprozessor ersetzt im
Quellext einen Text durch einen anderen Text. Erst das Ergebnis davon
geht dann durch den Compiler.
Du schreibst zb.
1 | #define SBIT(port,pin) ((*(volatile struct bits*)&port).b##pin)
| 2 | [/C
| 3 |
| 4 | und
| 5 |
| 6 | [C]
| 7 | int main()
| 8 | {
| 9 | ....
| 10 |
| 11 | SBIT( PORTA, 1 );
| 12 |
| 13 | ...
|
und der Präprozessor ersetzt erst mal das SBIT durch den Makrotext,
wobei er im Makrotext überall port durch PORTA und pin durch 1 ersetzt.
1 | int main()
| 2 | {
| 3 | ...
| 4 | ((*(volatile struct bits*)&PORTA).b##1)
| 5 | ...
|
Jetzt noch schnell den token-Concat gemacht
1 | int main()
| 2 | {
| 3 | ...
| 4 | ((*(volatile struct bits*)&PORTA).b1)
| 5 | ...
|
und der Präprozessor hat seine Schuldigkeit getan, der Text kann so
durch den Compiler laufen.
> Aber ich will nicht einfach nur Andwenden, sondern
> lieber auch Verstehen!
Du solltest dir Literatur zulegen. Es gibt noch millionen anderer Dinge
die du noch nicht weißt und die alle in einem vernünftigen Buch
beschrieben sind.
Danke für eure Antworten!!
Ich hab mich heut den ganzen Tag mit dem Display beschäftigt und noch
einige Funktionen hinzugefügt. Dank eurer Hilfe hab ich auch nun
einigermaßen verstanden, was es mit den Definitionen und Makros zu tun
hat.
Vielen Dank nochmal!
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|