Forum: Mikrocontroller und Digitale Elektronik Betrieb eines LCD (HD44780) an Port2


von Mikrocontroller Rookie (Gast)


Lesenswert?

Hallo zusammen,

ich würde gerne einen LCD (HD44780) an Port2 des MSP430G2553 betreiben.

Die Beschaltung soll folgendermaßen sein:

DB7 -> P2.5
DB6 -> P2.4
DB5 -> P2.3
DB4 -> P2.2

RS -> P2.0
RW -> GND
E -> P2.1


Beim Googlen fand ich einen Lösungsvorschlag (allerdings für Port 1 mit
P1.7 bis P1.4 für die Datenleitungen DB7 bis DB4) der auch funktioniert.

Allerdings zeigt mir das Display bei Portierung von Port1 auf Port2
wieder nur die Balken an.

Angepasst wurden nur die Defines (siehe Code)

1
#include  "msp430.h"
2
3
#define     LCM_DIR               P2DIR
4
#define     LCM_OUT               P2OUT
5
6
//
7
8
#define     LCM_PIN_RS            BIT0          // P2.0
9
#define     LCM_PIN_EN            BIT1          // P2.1
10
#define     LCM_PIN_D7            BIT5          // P2.5
11
#define     LCM_PIN_D6            BIT4          // P2.4
12
#define     LCM_PIN_D5            BIT3          // P2.3
13
#define     LCM_PIN_D4            BIT2          // P2.2
14
15
16
17
#define     LCM_PIN_MASK  ((LCM_PIN_RS | LCM_PIN_EN | LCM_PIN_D7 | 
18
LCM_PIN_D6 | LCM_PIN_D5 | LCM_PIN_D4))
19
20
#define     FALSE                 0
21
#define     TRUE                  1
22
23
//
24
// Routine Desc:
25
//
26
// This is the function that must be called
27
// whenever the LCM needs to be told to
28
// scan it's data bus.
29
//
30
// Parameters:
31
//
32
//     void.
33
//
34
// Return
35
//
36
//     void.
37
//
38
void PulseLcm()
39
{
40
    //
41
    // pull EN bit low
42
    //
43
    LCM_OUT &= ~LCM_PIN_EN;
44
    __delay_cycles(200);
45
46
    //
47
    // pull EN bit high
48
    //
49
    LCM_OUT |= LCM_PIN_EN;
50
    __delay_cycles(200);
51
52
    //
53
    // pull EN bit low again
54
    //
55
    LCM_OUT &= (~LCM_PIN_EN);
56
    __delay_cycles(200);
57
}
58
59
60
61
//
62
// Routine Desc:
63
//
64
// Send a byte on the data bus in the 4 bit mode
65
// This requires sending the data in two chunks.
66
// The high nibble first and then the low nible
67
//
68
// Parameters:
69
//
70
//    ByteToSend - the single byte to send
71
//
72
//    IsData - set to TRUE if the byte is character data
73
//                  FALSE if its a command
74
//
75
// Return
76
//
77
//     void.
78
//
79
void SendByte(char ByteToSend, int IsData)
80
{
81
    //
82
    // clear out all pins
83
    //
84
    LCM_OUT &= (~LCM_PIN_MASK);
85
    //
86
    // set High Nibble (HN) -
87
    // usefulness of the identity mapping
88
    // apparent here. We can set the
89
    // DB7 - DB4 just by setting P1.7 - P1.4
90
    // using a simple assignment
91
    //
92
    LCM_OUT |= (ByteToSend & 0x3C);
93
94
    if (IsData == TRUE)
95
    {
96
        LCM_OUT |= LCM_PIN_RS;
97
    }
98
    else
99
    {
100
        LCM_OUT &= ~LCM_PIN_RS;
101
    }
102
103
    //
104
    // we've set up the input voltages to the LCM.
105
    // Now tell it to read them.
106
    //
107
    PulseLcm();
108
     //
109
    // set Low Nibble (LN) -
110
    // usefulness of the identity mapping
111
    // apparent here. We can set the
112
    // DB7 - DB4 just by setting P1.7 - P1.4
113
    // using a simple assignment
114
    //
115
    LCM_OUT &= (~LCM_PIN_MASK);
116
    LCM_OUT |= ((ByteToSend & 0x0F) << 4);
117
118
    if (IsData == TRUE)
119
    {
120
        LCM_OUT |= LCM_PIN_RS;
121
    }
122
    else
123
    {
124
        LCM_OUT &= ~LCM_PIN_RS;
125
    }
126
127
    //
128
    // we've set up the input voltages to the LCM.
129
    // Now tell it to read them.
130
    //
131
    PulseLcm();
132
}
133
134
135
//
136
// Routine Desc:
137
//
138
// Set the position of the cursor on the screen
139
//
140
// Parameters:
141
//
142
//     Row - zero based row number
143
//
144
//     Col - zero based col number
145
//
146
// Return
147
//
148
//     void.
149
//
150
void LcmSetCursorPosition(char Row, char Col)
151
{
152
    char address;
153
154
    //
155
    // construct address from (Row, Col) pair
156
    //
157
    if (Row == 0)
158
    {
159
        address = 0;
160
    }
161
    else
162
    {
163
        address = 0x40;
164
    }
165
166
    address |= Col;
167
168
    SendByte(0x80 | address, FALSE);
169
}
170
171
172
//
173
// Routine Desc:
174
//
175
// Clear the screen data and return the
176
// cursor to home position
177
//
178
// Parameters:
179
//
180
//    void.
181
//
182
// Return
183
//
184
//     void.
185
//
186
void ClearLcmScreen()
187
{
188
    //
189
    // Clear display, return home
190
    //
191
    SendByte(0x01, FALSE);
192
    SendByte(0x02, FALSE);
193
}
194
195
196
//
197
// Routine Desc:
198
//
199
// Initialize the LCM after power-up.
200
//
201
// Note: This routine must not be called twice on the
202
//           LCM. This is not so uncommon when the power
203
//           for the MCU and LCM are separate.
204
//
205
// Parameters:
206
//
207
//    void.
208
//
209
// Return
210
//
211
//     void.
212
//
213
void InitializeLcm(void)
214
{
215
    //
216
    // set the MSP pin configurations
217
    // and bring them to low
218
    //
219
    LCM_DIR |= LCM_PIN_MASK;
220
    LCM_OUT &= ~(LCM_PIN_MASK);
221
222
223
    //
224
    // wait for the LCM to warm up and reach
225
    // active regions. Remember MSPs can power
226
    // up much faster than the LCM.
227
    //
228
    __delay_cycles(100000);
229
230
231
    //
232
    // initialize the LCM module
233
    //
234
    // 1. Set 4-bit input
235
    //
236
    LCM_OUT &= ~LCM_PIN_RS;
237
    LCM_OUT &= ~LCM_PIN_EN;
238
239
    LCM_OUT = 0x20;
240
    PulseLcm();
241
242
    //
243
    // set 4-bit input - second time.
244
    // (as reqd by the spec.)
245
    //
246
    SendByte(0x28, FALSE);
247
248
    //
249
    // 2. Display on, cursor on, blink cursor
250
    //
251
    SendByte(0x0E, FALSE);
252
253
    //
254
    // 3. Cursor move auto-increment
255
    //
256
    SendByte(0x06, FALSE);
257
}
258
259
260
//
261
// Routine Desc
262
//
263
// Print a string of characters to the screen
264
//
265
// Parameters:
266
//
267
//    Text - null terminated string of chars
268
//
269
// Returns
270
//
271
//     void.
272
//
273
void PrintStr(char *Text)
274
{
275
    char *c;
276
277
    c = Text;
278
279
    while ((c != 0) && (*c != 0))
280
    {
281
        SendByte(*c, TRUE);
282
        c++;
283
    }
284
}
285
286
287
//
288
// Routine Desc
289
//
290
// main entry point to the sketch
291
//
292
// Parameters
293
//
294
//     void.
295
//
296
// Returns
297
//
298
//     void.
299
//
300
void main(void)
301
{
302
    WDTCTL = WDTPW + WDTHOLD;             // Stop watchdog timer
303
304
    InitializeLcm();
305
306
    ClearLcmScreen();
307
308
    PrintStr("Hello World!");
309
310
    while (1)
311
    {
312
        __delay_cycles(1000);
313
    }
314
315
}

Was muss eurer Meinung nach am Code noch geändert werden?

Ich würde mich für jede Hilfe sehr freuen.

Gruß

von Karl H. (kbuchegg)


Lesenswert?

> (allerdings für Port 1 mit P1.7 bis P1.4 für die Datenleitungen
> DB7 bis DB4

Das stinkt verdächtig danach, dass die Nibble Aufteilung sich im 
originalen Code widerspiegeln wird.


Und tatsächlich kann man die Überbleibsel davon noch erkennen. Ähm
1
    LCM_OUT |= (ByteToSend & 0x3C);

nein.
Du willst nach wie vor das High-Nibble der Daten, also die oberen 4 Bit. 
Allerdings musst du die dann entsprechend deiner Pin-Zuordung 
zurechtschieben.

1
    LCM_OUT |= ((ByteToSend & 0x0F) << 4);
und auch hier muss überarbeitet werden.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:


> Allerdings musst du die dann entsprechend deiner Pin-Zuordung
> zurechtschieben.


Deine 8 Bit aus den Daten
1
     7   6   5   4   3   2   1   0
2
   +---+---+---+---+---+---+---+---+
3
   |   |   |   |   |   |   |   |   |
4
   +---+---+---+---+---+---+---+---+

müssen sich für das High_Nibble und deiner Datenbit-Portbelegung, so in 
die Portbits schieben
1
     7   6   5   4   3   2   1   0
2
   +---+---+---+---+---+---+---+---+
3
   |   |   |   |   |   |   |   |   |
4
   +---+---+---+---+---+---+---+---+
5
6
     |   |   |   |
7
     |   |   |   +-------+
8
     |   |   +-------+   |
9
     |   +-------+   |   |
10
     +-------+   |   |   |
11
             |   |   |   |
12
             v   v   v   v
13
   +---+---+---+---+---+---+---+---+
14
   |   |   |   |   |   |   |   |   |
15
   +---+---+---+---+---+---+---+---+
16
    2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0

Das musst du jetzt eben ausprogrammieren.


Und für die unteren 4 Bits des Low-Nibbles dann eben dementsprechend 
sinngemäss mit den restlichen 4 Bits vom Datenbyte. Die Bits 4 bis 0 
müssen an den Portbits 2.5 bis 2.2 landen.

von Mikrocontroller Rookie (Gast)


Lesenswert?

Vielen Dank für die Mühe und die schnelle Antwort!

Da primär ein Datenlogger entwickelt werden soll, der die Daten an den 
Rechner sendet, geht es mir im Prinzip nur darum, dass das Display 
zunächst funktioniert (ich habe mich leider kaum mit dem HD44780 
beschäftigen können, was hoffentlich bald folgt). Folglich bitte ich um 
Verzeihung, falls die Fragen zu absurd klingen sollten :)

Der MSP430G2553 hat nur die Portbits 2.0 bis 2.5, von denen Bit 5 bis 
Bit 2 für die Daten gedacht sind und Bit0 und Bit1 für RS und E. Somit 
bleiben keine Portbits mehr für das Low Nibble.

Wäre dies überhaupt möglich?

Beste Grüße

von Zuschauer (Gast)


Lesenswert?


von Karl H. (kbuchegg)


Lesenswert?

Mikrocontroller Rookie schrieb:

> Der MSP430G2553 hat nur die Portbits 2.0 bis 2.5, von denen Bit 5 bis
> Bit 2 für die Daten gedacht sind und Bit0 und Bit1 für RS und E. Somit
> bleiben keine Portbits mehr für das Low Nibble.

4-Bit Modus.

Erst wird mit dem Datenbyte das hier gemacht
1
     7   6   5   4   3   2   1   0
2
   +---+---+---+---+---+---+---+---+
3
   |   |   |   |   |   |   |   |   |
4
   +---+---+---+---+---+---+---+---+
5
6
     |   |   |   |
7
     |   |   |   +-------+
8
     |   |   +-------+   |
9
     |   +-------+   |   |
10
     +-------+   |   |   |
11
             |   |   |   |
12
             v   v   v   v
13
   +---+---+---+---+---+---+---+---+
14
   |   |   |   |   |   |   |   |   |
15
   +---+---+---+---+---+---+---+---+
16
    2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0
(also das High-Nibble an die Datenleitungen gelegt)
und diese 4 Bit rausgetaktet.
Danach wird das Low-Nibble an die 4 Datenleitungen gelegt
1
     7   6   5   4   3   2   1   0
2
   +---+---+---+---+---+---+---+---+
3
   |   |   |   |   |   |   |   |   |
4
   +---+---+---+---+---+---+---+---+
5
6
                     |   |   |   |
7
             +-------+   |   |   |
8
             |   +-------+   |   |
9
             |   |   +-------+   |
10
             |   |   |   +-------+
11
             |   |   |   |
12
             v   v   v   v
13
   +---+---+---+---+---+---+---+---+
14
   |   |   |   |   |   |   |   |   |
15
   +---+---+---+---+---+---+---+---+
16
    2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0

und rausgetaktet.

Also: 8 Bit werden übertragen, in dem sie in 2 Stück 4-Bit Happen 
aufgeteilt werden, die nacheinander rausgetaktet werden.
Nur müssen die 4 Bit Happen auch richtig aufgeteilt werden und sie 
müssen an die jeweils richtigen Portbits verschoben werden, so dass sie 
letztendlich dann auch am LCD auf dessen Pins DB4 bis DB7 landen.

Das hier
1
    LCM_OUT |= (ByteToSend & 0x3C);
bzw. das hier
1
    LCM_OUT |= ((ByteToSend & 0x0F) << 4);
machen die notwendigen Operation jedenfalls nicht. Die machen was 
anderes, was im zweiten Fall sinnvoll war, als noch die Portbits P7 bis 
P4 benutzt wurden. Die erste Zeile allerdings ist kompletter Nonsense im 
jetzigen Zusammenhang. Aber die war ursprünglich wohl  ByteToSend & 
0xF0, denn dann macht sie wieder Sinn mit der originalen 
Portpinzuordnung.

Die jeweils notwendigen Operationen an sich herzuleiten, ist allerdings 
nicht sehr schwer (überhaupt mit den Zeichnungen) und deshalb schreibe 
ich sie dir auch nicht hin. Denn ich bin der Meinung, wer ernsthaft 
Programme für µC schreiben will, kommt ohne Beherrschung von 
Bitoperationen (Und, Oder, Nicht, Verschieben) nicht weit. ISt sozusagen 
das kleine Einmal-Eins der µC-Programmiererei.

von Mikrocontroller Rookie (Gast)


Lesenswert?

...was ja immerhin besser für den Lerneffekt ist :)
Besten Dank nochmals, dann versuche ich das doch gleich mal so 
umzusetzen!

Gruß

von Mikrocontroller Rookie (Gast)


Lesenswert?

Soo, ich habe mir dann mal eine Lösung ausgedacht die vielleicht nicht 
die eleganteste ist. Leider funktioniert es hardwaretechnisch noch 
nicht.


Setze High-Nibble auf P1.5 bis P1.2, lösche restliche Pins.

    LCM_OUT |= (ByteToSend>>2);
    LCM_OUT &= ~0x80;
    LCM_OUT &= ~0x40;
    LCM_OUT &= ~0x02;
    LCM_OUT &= ~0x01;

Setze Low-Nibble auf P1.5 bis P1.2, lösche restliche Pins.

    LCM_OUT |= (ByteToSend<<2);
    LCM_OUT &= ~0x80;
    LCM_OUT &= ~0x40;
    LCM_OUT &= ~0x02;
    LCM_OUT &= ~0x01;


Ist der Code so in Ordnung?

von Peter D. (peda)


Lesenswert?

1
static void lcd_nibble( uint8_t d )
2
{
3
  LCD_D4 = 0; if( d & 1<<4 ) LCD_D4 = 1;
4
  LCD_D5 = 0; if( d & 1<<5 ) LCD_D5 = 1;
5
  LCD_D6 = 0; if( d & 1<<6 ) LCD_D6 = 1;
6
  LCD_D7 = 0; if( d & 1<<7 ) LCD_D7 = 1;
7
8
  LCD_E0 = 1;
9
  _delay_us( 1 );                       // 1us
10
  LCD_E0 = 0;

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=102296

von Karl H. (kbuchegg)


Lesenswert?

Mikrocontroller Rookie schrieb:
> Soo, ich habe mir dann mal eine Lösung ausgedacht die vielleicht nicht
> die eleganteste ist. Leider funktioniert es hardwaretechnisch noch
> nicht.
>
>
> Setze High-Nibble auf P1.5 bis P1.2, lösche restliche Pins.
>
>     LCM_OUT |= (ByteToSend>>2);
>     LCM_OUT &= ~0x80;
>     LCM_OUT &= ~0x40;
>     LCM_OUT &= ~0x02;
>     LCM_OUT &= ~0x01;


Wenn du hier einfach nur aus ByteToSend gleich mal nur die oberen 4 Bits 
nehmen würdest (alle anderen auf 0) und die um 2 Stellen nach rechts 
verschiebst, brauchst du nicht nachher alle anderen BIts auf 0 setzen.
1
   LCM_OUT |= ( ByteToSend & 0xF0 ) >> 2;
Denn LCM_OUT wurde ja vorher im Code sowieso schon mal auf komplett 0 
gesetzt.

>     LCM_OUT |= (ByteToSend<<2);
1
   LCM_OUT |= ( ByteToSend & 0x0F ) << 2;



Interessanterweise enthielt dein Originaler Code schon so was ähnliches:
1
    LCM_OUT |= ((ByteToSend & 0x0F) << 4);
Hat dir denn das, im Zusammenhang mit dem Wissen, dass im Original die 
Bits nich 2-5 sondern 4-7 waren, gar nichts gesagt? Warum da um 4 Bits 
verschoben wurde. Bzw. warum bei
1
    LCM_OUT |= ByteToSend & 0xF0;
überhaupt nicht geschoben wurde? Denn im Grunde kann man das auch lesen 
als
1
    LCM_OUT |= (ByteToSend & 0xF0) << 0;
nur, dass natürlich das Schieben um 0 Bits selbstverständlich nichts 
bewirkt.

Aber das allgemeine Schema ist doch

  Ziel |=  ( Quelle & Maske )  Verschiebeoperation;

Das ist in allen Fällen immer dasselbe
1
im Original
2
    LCM_OUT |= (ByteToSend & 0x0F) << 4;
3
    LCM_OUT |= (ByteToSend & 0xF0) << 0;
4
5
Bei dir jetzt
6
   LCM_OUT |= (ByteToSend & 0xF0) >> 2;
7
   LCM_OUT |= (ByteToSend & 0x0F) << 2;

Aus ByteToSend holst du dir mit der Verundung den Teil raus, den du 
brauchst, und danach schiebst du den Teil zurecht.
Da vorher im Code
1
void SendByte(char ByteToSend, int IsData)
2
{
3
    //
4
    // clear out all pins
5
    //
6
    LCM_OUT &= (~LCM_PIN_MASK);
7
....
alle Output Pins gezielt auf 0 gesetzt werden, reicht es daher aus, wenn 
du nur veroderst um dann aus den schon auf 0 gesetzten Pins wieder die 
1-er zu machen, die du brauchst und die eben aus der Kombination 
Maskieren und Verschieben übrig geblieben sind.

von Mikrocontroller Rookie (Gast)


Lesenswert?

Jetzt funktioniert es endlich, danke nochmal für die Hilfe, jetzt 
versteh ich auch den HD44780 etwas besser!

Beste Grüße

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.