Forum: Mikrocontroller und Digitale Elektronik LCD 1x16 HD44870 kompatibel will einfach nicht mit der library von Peter Fleury funktionieren


von Neb N. (bluemorph)


Lesenswert?

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.

von Michael U. (amiga)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Dieter E. (netdieter) Benutzerseite


Lesenswert?

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.

von Neb N. (bluemorph)


Lesenswert?

@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

von Neb N. (bluemorph)


Lesenswert?

@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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Neb N. (bluemorph)


Lesenswert?

@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.

von Neb N. (bluemorph)


Lesenswert?

@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)
1
#endif

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Neb N. (bluemorph)


Lesenswert?

@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

von Neb N. (bluemorph)


Lesenswert?

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

von Neb N. (bluemorph)


Lesenswert?

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???

von Peter D. (peda)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Neb N. (bluemorph)


Lesenswert?

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.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.