Forum: Mikrocontroller und Digitale Elektronik Problem bei I2C Verbindung zu 1602 LCD


von Sven K. (dires)


Lesenswert?

Hallo zusammen,

ich bin noch recht neu im Umgang mit Mikrocontrollern und versuche 
folgendes Setup zum Laufen zu bringen:

Ich habe einen ATmega328P auf einem Breadboard mit einem 8MHz Quarz. An 
PIN 28 (SCL) und PIN 27 (SDA) ist das Display 1602 (HD44780) über einen 
I2C Controller (PCF8574AT) verbunden.
Die Adresse des I2C Controllers ist 0x27 oder 0x3F (keine Lötbrücken). 
Die I2C clock ist dabei 100kHz.
Die Verbindungslänge ist 30 cm.
SDA und SCL sind über 7,87kOhm Widerstände an 5V verbunden.

Ich programmiere in Atmel Studio 7 und verwende zur Ansteuerung den Code 
von Nico/Thomas Eichelmann (http://computerheld.de/i2clcd/) und das I2C 
MasterInferace von Peter Fleury 
(http://homepage.hispeed.ch/peterfleury/avr-software.html).

Ich habe die i2clcd.h in folgender Weise angepasst:
1
//--Display-Configuration-Settings--
2
3
/** \defgroup DISPLAY_CONFIGURATION DISPLAY CONFIGURATION
4
 Change this settings to your configuration.
5
*/
6
/*@{*/
7
#define LCD_I2C_DEVICE 0x27  /**< Change this to the address of your expander */
8
#define LCD_LINES      2  /**< Enter the number of lines of your display here */
9
#define LCD_ROWS       16  /**< Enter the number of rows of your display here */
10
11
#define LCD_LINE1      0x00  /**< This should be 0x00 on all displays */
12
#define LCD_LINE2      0x40  /**< Change this to the address for line 2 on your display */
13
#define LCD_LINE3      0x10  /**< Change this to the address for line 3 on your display */
14
#define LCD_LINE4      0x50  /**< Change this to the address for line 4 on your display */
15
/*@}*/
16
17
//---------------------------------
18
19
//--The-following-definitions-are-corresponding-to-the-PIN-Assignment-(see-above)--
20
21
/** \defgroup PIN_ASSIGNMENT PIN ASSIGNMENT
22
 This pin assignment shows how the display is connected to the PCF8574.
23
*/
24
/*@{*/
25
#define LCD_D4_PIN      4  /**< LCD-Pin D4 is connected to P0 on the PCF8574 */
26
#define LCD_D5_PIN      5  /**< LCD-Pin D5 is connected to P1 on the PCF8574 */
27
#define LCD_D6_PIN      6  /**< LCD-Pin D6 is connected to P2 on the PCF8574 */
28
#define LCD_D7_PIN      7  /**< LCD-Pin D7 is connected to P3 on the PCF8574 */
29
#define LCD_RS_PIN      0  /**< LCD-Pin RS is connected to P4 on the PCF8574 */
30
#define LCD_RW_PIN      1  /**< LCD-Pin RW is connected to P5 on the PCF8574 */
31
#define LCD_EMPTY_PIN   3  /**< this pin is not connected */
32
#define LCD_E_PIN       2  /**< LCD-Pin D4 is connected to P7 on the PCF8574 */

In meiner main habe ich folgendes stehen:
1
#define F_CPU 8000000UL
2
3
#include <avr/io.h>
4
#include <util/delay.h>
5
#include "lcd/i2clcd.h"
6
7
int main(void){
8
  i2c_init();
9
  lcd_init();
10
  unsigned char string[] = "Hi World";
11
  lcd_print(string);
12
  lcd_nextline();
13
14
  lcd_command(LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKINGON);
15
16
    while (1) {
17
    }
18
}

Das Problem ist, dass auf dem Display in der ersten Zeile nur weiße 
Blöcke angezeigt werden. Ich habe herausgefunden, dass das auf eine 
nicht ausgeführte oder fehlerhafte Initialisierung des Displays 
zurückzuführen ist.

Ich habe jetzt den Fehler so eingegrenzt, dass während der 
Initialisierung des Displays (lcd_init) die Funktion i2c_write 
aufgerufen wird.
1
unsigned char i2c_write( unsigned char data )
2
{  
3
    uint8_t   twst;
4
    
5
  // send data to the previously addressed device
6
  TWDR = data;
7
  TWCR = (1<<TWINT) | (1<<TWEN);
8
  
9
  // wait until transmission completed
10
  while(!(TWCR & (1<<TWINT)));
11
  
12
  // check value of TWI Status Register. Mask prescaler bits
13
  twst = TW_STATUS & 0xF8;
14
  if( twst != TW_MT_DATA_ACK) return 1;
15
  
16
  return 0;
17
18
}/* i2c_write */

Es bleibt dabei bei der Abfrage
1
while(!(TWCR & (1<<TWINT)));
hängen.

Mit dem Oszi habe ich herausgefunden, dass SDA währenddessen high ist, 
jedoch SCL low ist. SCL ist auch low, wenn ich das Display nicht am 
ATmega angeschlossen habe.

Vielen Dank schon einmal für eure Hilfe. :)

Viele Grüße

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Bist Du am richtigen SCL-Pin?
SCL und SDA müssen in Ruhe beide HIGH sein.
Wenn Du auf SCL einen Takt hast, passt ggf. die Adresse nicht.

Sonst: Was macht das Display, was macht Es nicht?
Was ist wo am µC angeschlossen?

MfG

von Arbeitnehmer (Gast)


Lesenswert?

Sven K. schrieb:
> I2C Controller (PCF8574AT) verbunden.
> Die Adresse des I2C Controllers ist 0x27 oder 0x3F (keine Lötbrücken).

Meiner Meinung nach: Der 8574 A hat eine andere Adresse, siehe 
Datenblatt.

von Dieter F. (Gast)


Lesenswert?

Arbeitnehmer schrieb:
> Meiner Meinung nach: Der 8574 A hat eine andere Adresse, siehe
> Datenblatt

Sehe ich auch so

0x70 schreiben
0x71 lesen

jeweils ohne gesetzte Adress-Brücken.

von Sven K. (dires)


Lesenswert?

@ Arbeitnehmer / Dieter:
Vielen Dank für den Hinweis. Ich hatte mich blind auf den Hinweis des 
Verkäufers des I2C Moduls verlassen.

Das Display scheint auf 0x7E zu reagieren (A0-2 = 1).
Werden A0-2 durch die Lötbrücken auf low gezogen?
Beim PCF857 (ohne A) sind bei 0x27 standardmäßig ja auch A0-2 auf 1 
gesetzt.

Allerdings schaltet sich das Display jetzt nach Start des µC aus.
Womöglich ist irgendwo ein DISPLAYOFF Befehl zu häufig?
Ich werde jetzt versuchen damit weiter zu arbeiten.

@ Patrick:
Ich glaube die Verkabelung ist richtig (PIN 28 (SCL) und PIN 27 (SDA)).
Ich gehe jetzt davon aus, dass es tatsächlich ein Addressfehler war. 
(s.o.)
Danke trotzdem für die Antwort.

Viele Grüße

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Aus gehen liest sich zumindest so, daß das Display Steuerbefehle 
bekommt.

Die Belegung der 8bit des Expander ist bei den verschiedenen Versionen 
nicht identisch!
Bei den 0x27 z.B. Bit 3 die Hintergrundbeleuchtung.
Dort sind verschiedene Versionen gezeigt:
http://arduino-info.wikispaces.com/LCD-Blue-I2C?responseToken=93c153e52892ea62109f6eaf90429aa5

MfG

von Wolfgang (Gast)


Lesenswert?

Dieter F. schrieb:
> Arbeitnehmer schrieb:
>> Meiner Meinung nach: Der 8574 A hat eine andere Adresse, siehe
>> Datenblatt
>
> Sehe ich auch so
>
> 0x70 schreiben
> 0x71 lesen

Klar, deshalb ist im Datenblatt (Table 5. PCA8574A address map) abhängig 
von A[0..2] als Adresse angegeben 38h .. 3Fh.
;-(

von Sven K. (dires)


Lesenswert?

Patrick J. schrieb:
> Hi
>
> Aus gehen liest sich zumindest so, daß das Display Steuerbefehle
> bekommt.
>
> Die Belegung der 8bit des Expander ist bei den verschiedenen Versionen
> nicht identisch!
> Bei den 0x27 z.B. Bit 3 die Hintergrundbeleuchtung.
> Dort sind verschiedene Versionen gezeigt:
> 
http://arduino-info.wikispaces.com/LCD-Blue-I2C?responseToken=93c153e52892ea62109f6eaf90429aa5
>
> MfG

Ich habe alle Verbindungen vom Display zum PCF857AT durchgepiepst und 
bin dann auf folgende Verbindungen gekommen:
1
#define LCD_D4_PIN      4  /**< LCD-Pin D4 is connected to P4 on the PCF8574 */
2
#define LCD_D5_PIN      5  /**< LCD-Pin D5 is connected to P5 on the PCF8574 */
3
#define LCD_D6_PIN      6  /**< LCD-Pin D6 is connected to P6 on the PCF8574 */
4
#define LCD_D7_PIN      7  /**< LCD-Pin D7 is connected to P7 on the PCF8574 */
5
#define LCD_RS_PIN      0  /**< LCD-Pin RS is connected to P0 on the PCF8574 */
6
#define LCD_RW_PIN      1  /**< LCD-Pin RW is connected to P1 on the PCF8574 */
7
#define LCD_EMPTY_PIN    3  /**< this pin is not connected */
8
#define LCD_E_PIN      2  /**< LCD-Pin D4 is connected to P2 on the PCF8574 */

Sofern die Beschriftungen der Pins auf dem Display nicht falsch sind, 
sollte das hoffentlich passen.

War das das, was du gemeint hast?

Viele Grüße

von Dieter F. (Gast)


Lesenswert?

Wolfgang schrieb:
> Klar, deshalb ist im Datenblatt (Table 5. PCA8574A address map) abhängig
> von A[0..2] als Adresse angegeben 38h .. 3Fh.
> ;-(

Scheinbar hest Du meinen Beitrag nicht richtig gelesen:

0x70 schreiben
0x71 lesen

incl. Schreib-/Lese-Bit. Ohne macht eine Adressierung keinen Sinn - 
oder?

von Sven K. (dires)


Angehängte Dateien:

Lesenswert?

Anbei habe ich den Abschnitt aus dem Datenblatt des HD44780 angehängt in 
dem der Display Mode definiert wird.

Und hier ist der Code der das definiert:
1
#define LCD_DISPLAYMODE      0x08      /**< Set displaymode */
2
  #define LCD_DISPLAYON    LCD_DISPLAYMODE | 0x04  /**<  Display on */
3
  #define LCD_DISPLAYOFF    LCD_DISPLAYMODE | 0x00  /**<  Display off */
4
  #define LCD_CURSORON    LCD_DISPLAYMODE | 0x02  /**<  Cursor on */
5
  #define LCD_CURSOROFF    LCD_DISPLAYMODE | 0x00  /**<  Cursor off */
6
  #define LCD_BLINKINGON    LCD_DISPLAYMODE | 0x01  /**<  Blinking on */
7
  #define LCD_BLINKINGOFF    LCD_DISPLAYMODE | 0x00  /**<  Blinking off */

Für Display_on also b1100.

Meiner Meinung nach ist das auch richtig.

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Hier ging es doch um die Ansteuerung per I2C-Portexpander, oder was habe 
ich verpasst?
Da der Expander nur 8bit hat und Du dort auch noch E,RS,RW mit drüber 
bringen musst, bleiben 4bit für's Display über.
Zum Umschalten auf 4-bit-Betrieb reicht Das, danach 16x2, 
Cursor,Blinken,Display-Shift, dann Display an, fertig.

Wobei im 4-Bit-Betrieb jedes Byte in zwei Nibble gesendet wird, wo die 
Steuer-Bits doppelt gesendet werden.


MfG

von Sven K. (dires)


Lesenswert?

Patrick J. schrieb:
> Hi
>
> Hier ging es doch um die Ansteuerung per I2C-Portexpander, oder was habe
> ich verpasst?
> Da der Expander nur 8bit hat und Du dort auch noch E,RS,RW mit drüber
> bringen musst, bleiben 4bit für's Display über.
> Zum Umschalten auf 4-bit-Betrieb reicht Das, danach 16x2,
> Cursor,Blinken,Display-Shift, dann Display an, fertig.
>
> Wobei im 4-Bit-Betrieb jedes Byte in zwei Nibble gesendet wird, wo die
> Steuer-Bits doppelt gesendet werden.
>
>
> MfG

Hi Patrick,

entschuldige, wenn ich noch nicht alles ganz durchschaue.
Ja es geht um einen I2C-Portexpander mit 8 bit.
Die 4 Datenbits werden kopiert und werden in D0-3 und D4-7 des Displays 
geschrieben:
1
#define LCD_D0        (1 << LCD_D4_PIN)  /**< bit 0 in 1st lower nibble */
2
#define LCD_D1        (1 << LCD_D5_PIN)  /**< bit 1 in 1st lower nibble */
3
#define LCD_D2        (1 << LCD_D6_PIN)  /**< bit 2 in 1st lower nibble */
4
#define LCD_D3        (1 << LCD_D7_PIN)  /**< bit 3 in 1st lower nibble */
5
6
#define LCD_D4        (1 << LCD_D4_PIN)  /**< bit 4 in 2nd lower nibble */
7
#define LCD_D5        (1 << LCD_D5_PIN)  /**< bit 5 in 2nd lower nibble */
8
#define LCD_D6        (1 << LCD_D6_PIN)  /**< bit 6 in 2nd lower nibble */
9
#define LCD_D7        (1 << LCD_D7_PIN)  /**< bit 7 in 2nd lower nibble */
10
11
#define LCD_RS        (1 << LCD_RS_PIN)  /**< RS-bit in 1st and 2nd higher nibble */
12
#define LCD_RW        (1 << LCD_RW_PIN)  /**< RW-bit in 1st and 2nd higher nibble */
13
#define LCD_EMPTY      (1 << LCD_EMPTY_PIN)  /**< empty-bit in 1st and 2nd higher nibble */
14
#define LCD_E        (1 << LCD_E_PIN)  /**< E-bit in 1st and 2nd higher nibble */

So ist es zumindest im Code, den ich habe, realisiert. In der Funktion 
lcd_command werden dann jeweils die oberen und unteren 4 bits 
nacheinander gesendet:
1
void lcd_command(unsigned char command)
2
{
3
  unsigned char lcddata;
4
  lcddata = command;
5
  lcddata >>=  4;
6
  lcd_write(lcddata);
7
  lcddata = command;
8
  lcddata &= 0x0F;
9
  lcd_write(lcddata);  
10
}

Ist das soweit richtig?

Danke für die Hilfe. :)

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Die LCD_write müsste dann mit den Steuerbits verodern und diese so 
generierten 8bit per I2C ans Display schicken.

Mit der realen Belegung des Display hast Du hier weniger zu tun, da der 
Expander hier vorgibt, was wie zu übertragen ist.

Wenn Du dieses Display bereits an einem anderen µC was anzeigt, bitten 
den Code davon sowie den Code dieses µC, denke, da haben wir Differenzen 
drin.

Komplette Code wäre hier sinnig, z.B. ist in Deiner Funktion lcd_command 
nicht ersichtlich, wo die Steuerbits her kommen und welche Pins wofür 
benutzt werden.

MfG

von Sven K. (dires)


Angehängte Dateien:

Lesenswert?

Patrick J. schrieb:
> Hi
>
> Die LCD_write müsste dann mit den Steuerbits verodern und diese so
> generierten 8bit per I2C ans Display schicken.
>
> Mit der realen Belegung des Display hast Du hier weniger zu tun, da der
> Expander hier vorgibt, was wie zu übertragen ist.
>
> Wenn Du dieses Display bereits an einem anderen µC was anzeigt, bitten
> den Code davon sowie den Code dieses µC, denke, da haben wir Differenzen
> drin.
>
> Komplette Code wäre hier sinnig, z.B. ist in Deiner Funktion lcd_command
> nicht ersichtlich, wo die Steuerbits her kommen und welche Pins wofür
> benutzt werden.
>
> MfG

Ich habe bis jetzt nur den einen µC, deshalb kann ich es gerade nicht an 
einem anderen µC testen.

Anbei der gesamt Code als zip. Ich hoffe das ist in Ordnung.

Vielen Dank noch einmal.

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Wie Du oben schreibst, siehst Du nur einen Balken.
Init nicht korrekt

Leider komme ich mit der Schreibweise des Code nicht sonderlich zurecht, 
meinem Verständnis nach, dürfte Da rein gar Nichts passieren :O
1
void lcd_init(void);          //-  Display initialization sequence
2
3
/**
4
 \brief Write data to i2c (for internal use)
5
 \param value byte to send over i2c
6
 \return none
7
 */
8
void lcd_write_i2c(unsigned char value);    //-  Write data to i2c
9
10
/**
11
 \brief Write byte to display with toggle of enable-bit
12
 \param value the upper nibble represents  E, RS, RW pins and the lower nibble contains data D0 to D3 pins or D4 to D7 pins
13
 \return none
14
 */
15
void lcd_write(unsigned char value);      //-  Write byte to display with toggle of enable-bit
16
17
/**
Geht dann noch weiter ...
Ist erkenne da nicht, was wirklich zum Init verschickt wird.

MfG

von Sven K. (dires)


Lesenswert?

Patrick J. schrieb:
> Hi
>
> Wie Du oben schreibst, siehst Du nur einen Balken.
> Init nicht korrekt
>
> Leider komme ich mit der Schreibweise des Code nicht sonderlich zurecht,
> meinem Verständnis nach, dürfte Da rein gar Nichts passieren :O
>
1
void lcd_init(void);          //-  Display initialization 
2
> sequence
3
> 
4
> /**
5
>  \brief Write data to i2c (for internal use)
6
>  \param value byte to send over i2c
7
>  \return none
8
>  */
9
> void lcd_write_i2c(unsigned char value);    //-  Write data to i2c
10
> 
11
> /**
12
>  \brief Write byte to display with toggle of enable-bit
13
>  \param value the upper nibble represents  E, RS, RW pins and the lower 
14
> nibble contains data D0 to D3 pins or D4 to D7 pins
15
>  \return none
16
>  */
17
> void lcd_write(unsigned char value);      //-  Write byte to display 
18
> with toggle of enable-bit
19
> 
20
> /**
> Geht dann noch weiter ...
> Ist erkenne da nicht, was wirklich zum Init verschickt wird.
>
> MfG

Hi Patrick,

die Implementierungen der I2C Funktionen stehen in der "twimaster.c".

Mittlerweile habe ich ja auch keine Balken mehr, sondern das Display 
schaltet sich aus.
Die I2C Verbindung steht also scheinbar, nur scheine ich falsche Befehle 
zu schicken, oder ähnliches.

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.