Forum: Mikrocontroller und Digitale Elektronik PIC16F1509/Sansmart LCD2004/I2C , benötige etwas Hilfe


von Harry R. (harryr)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

als PIC-Anfänger bin ich am WE (mal wieder) an meine Grenzen gestoßen 
:-(

Worum gehts es ?

Ich möchte via I2C ein 4-zeiliges LCD-Display ansteuern.
Als PIC nehme ich den PIC16F1509

Ich verwende MPLAB X 6.10 mit MCC im Modus "Melody".

Melody implementiert I2C via Interrupt, was meiner Meinung nach
eine gute Lösung darstellt.

Nachdem ich ein neues Projekt erstellt habe und via MCC die Devices/etc 
konfiguriert habe, existiert folgende Konfiguration (hier: Dateien, 
Verzeichnisse): LCDI2C1.png

Der Zweig i2c_host stellt dabei die Funktionen für eine Kommunikation 
via I2C zur Verfügung.

Nun muss ich also (nur noch) eine LCD-Schnittstelle implementieren, die
die generierte  I2C-Schnittstelle verwendet.

Ich habe mich dazu in diversen Seiten im Internet schlau gemacht und
versucht, die grundlegenden Funktionen zu implementieren.

Die erste ist natürlich eine Initialisierungsroutine:
1
bool LCD_Initialize(unsigned char lAddress)
2
{
3
  if (lAddress == 0)
4
    return(false);
5
  else
6
    lcdAddress = lAddress;
7
  
8
  unsigned char _displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS;
9
    
10
  IO_Expander_Write(0x00);
11
  __delay_ms(30);
12
  LCD_CMD(0x03);
13
  __delay_ms(5);
14
  LCD_CMD(0x03);
15
  __delay_ms(5);
16
  LCD_CMD(0x03);
17
  __delay_ms(5);
18
  LCD_CMD(LCD_RETURN_HOME);
19
  __delay_ms(5);
20
  LCD_CMD(0x20 | (LCD_TYPE << 2|_displayfunction));
21
  __delay_ms(50);
22
  LCD_CMD(LCD_TURN_ON);
23
  __delay_ms(50);
24
  LCD_CMD(LCD_CLEAR);
25
  __delay_ms(50);
26
  LCD_CMD(LCD_ENTRY_MODE_SET | LCD_RETURN_HOME);
27
  __delay_ms(50);
28
    
29
  initialized = true;
30
  return(true);
31
};

Man sieht am Code schon, dass dort "Ideen" aus anderen Implementierungen 
eingeflossen sind. Ich zeige gerne den ganzen Code, vorerst möchte ich 
aber schlicht und ergreifend wissen, ob diese Init überhaupt 
funktioniert. Auch hier schon mal die Information, immerhin kann ich die 
Hintergrundbeleuchtung damit an/ausschalten, so verkehrt kann es also 
nicht sein. Dito stimmt die I2C-Adresse des LCD-Moduls schonmal. 
Allerdings ist das Togglen des Backlights sehr lowlevel, also unabhängig 
von der Auflösung des Dislays. Und da scheint der Hund begraben zu sein, 
denn bisher hat sich noch kein Zeichen auf dem Display  gezeigt. Das 
Display sieht auch so aus, als ob es mit meinem Init gar nicht 
einverstanden ist. Grundlegende Schreibroutinen funktionieren, denn 
sonst könnte ich das LCD nicht togglen.

Das LCD sieht so aus wie in Bild LCDI2C2.JPG.
Das ist merkwürdig zweizeilig. Für das Verständnis der Funktion zeige 
ich jetzt mal alle verwendeten #defines
1
#define LCDDEFAULTI2CAdress2 0x27
2
#define LCDI2CAdress LCDDEFAULTI2CAdress2  // = lAddress
3
4
#define LCD_BACKLIGHT          0x08
5
#define LCD_NOBACKLIGHT        0x00
6
#define LCD_FIRST_ROW          0x80
7
#define LCD_SECOND_ROW         0xC0
8
#define LCD_THIRD_ROW          0x94
9
#define LCD_FOURTH_ROW         0xD4
10
#define LCD_CLEAR              0x01
11
#define LCD_RETURN_HOME        0x02
12
#define LCD_ENTRY_MODE_SET     0x04
13
#define LCD_CURSOR_OFF         0x0C
14
#define LCD_UNDERLINE_ON       0x0E
15
#define LCD_BLINK_CURSOR_ON    0x0F
16
#define LCD_MOVE_CURSOR_LEFT   0x10
17
#define LCD_MOVE_CURSOR_RIGHT  0x14
18
#define LCD_TURN_ON            0x0C
19
#define LCD_TURN_OFF           0x08
20
#define LCD_SHIFT_LEFT         0x18
21
#define LCD_SHIFT_RIGHT        0x1E
22
#define LCD_TYPE               2       // 0 -> 5x7 | 1 -> 5x10 | 2 -> 2 lines
23
#define LCD_8BITMODE 0x10
24
#define LCD_4BITMODE 0x00
25
#define LCD_2LINE 0x08
26
#define LCD_1LINE 0x00
27
#define LCD_5x10DOTS 0x04
28
#define LCD_5x8DOTS 0x00

Wenn ich Glück habe und ihr mir helft, ist "der Hund" bereits hier 
begraben. Ansonsten gebe ich gerne mehr Information raus, ich will im 
Moment meinen Beitrag hier nur nicht ausufernd lang machen.

Danke vorab für jeden Beitrag
Harry

von Frank K. (fchk)


Lesenswert?

Hast Du einen Logic Analyzer? Wenn nicht, dann kauf Dir einen. Die 
billigen 24MHz/8Ch Teile von Amazon reichen.

Dann verifizierst Du, ob das was Du in Deinem Programmcode senden 
willst, auch tatsächlich auf dem I2C gesendet wird und dass Du für jedes 
geschriebene Byte ein ACK bekommst.

Dann nimmst Du Dir das Datenblatt zum Display und schaust, welche 
Befehle das kann. Dann hängst Du Dich mit Deinem Logicanalyzer direkt an 
das Display (d.h. an die Datenleitungen D4...D7 (das Display wird 
wahrscheinlich im 4-Bit Modus betrieben) und E, RS und W) und schaust, 
ob die Signale, die da im Datenblatt stehen, genauso richtig ankommen.

Wenn Du damit durch bist, sollte das gehen.

Du musst lernen zu lernen anstelle für jeden Mist hier zu fragen.
Du musst lernen, systematisch Fehlersuche zu betreiben.
Du musst lernen, welche Tools es gibt, wann Du sie einsetzt und wie sie 
funktionieren.

Auf!

fchk

: Bearbeitet durch User
von Harry R. (harryr)


Lesenswert?

Frank K. schrieb:
> Hast Du einen Logic Analyzer? Wenn nicht, dann kauf Dir einen. Die
> [...]
> Auf!
>
> fchk
Dein Beitrag hilft mir nicht weiter und dein Ton gefällt mir nicht.
Würde es dir etwas ausmachen, diesen Thread einfach zu ignorieren ?
Wenn ja: gut, wenn nicht auch gut, aber bitte nicht antworten, ich habe 
echt keine Lust das auszudiskutieren.

von Stephan S. (uxdx)


Lesenswert?

Harry R. schrieb:
> Dein Beitrag hilft mir nicht weiter und dein Ton gefällt mir nicht.

Frank hat Dir eine absolut korrekte Antwort gegeben, so sucht man den 
Fehler.

DEIN TON ist hier unangebracht.

von Harry R. (harryr)


Lesenswert?

Stephan S. schrieb:
> Harry R. schrieb:
>> Dein Beitrag hilft mir nicht weiter und dein Ton gefällt mir nicht.
>
> Frank hat Dir eine absolut korrekte Antwort gegeben, so sucht man den
> Fehler.
>
> DEIN TON ist hier unangebracht.
Für dich gilt das gleiche wie für Frank. Vielen Dank

von ElC (elc)


Lesenswert?

Hi
Meine Erfahrung mit PIC16F1509 und MLAB ist gleich null.
Aus Zeitvertreib habe ich mal gegoogelt.
Möglicherweise hilft dir dieser Link weiter:

https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/

Aufgefallen ist mir auf den ersten Blick die dortige 
Initialisierungs-Sequenz, die I2C Adresse und auch der von dir 
abweichende Wert bei der Definition für den 4-Bit Mode. Nur (?) unter 
der Arduino IDE ist die I2C Adresse 0x27 oder 0x3F.

MfG

: Bearbeitet durch User
von Harry R. (harryr)


Lesenswert?

ElC schrieb:
> Hi
> Meine Erfahrung mit PIC16F1509 und MLAB ist gleich null.
> Aus Zeitvertreib habe ich mal gegoogelt.
> Möglicherweise hilft dir dieser Link weiter:
>
> https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/
>
> Aufgefallen ist mir auf den ersten Blick die dortige
> Initialisierungs-Sequenz, die I2C Adresse und auch der von dir
> abweichende Wert für den 4-Bit Mode. Nur (?) unter der Arduino IDE ist
> die I2C Adresse 0x27 oder 0x3F.
>
> MfG
Die Adresse muss stimmen, sonst könne ich das BackLight nicht 
An/Aussschalten (mit bewusst falscher Adresse funktioniert das eben 
nicht,
habe ich eben nochmal mal mit der FALSCHEN Adresse 0x28 getestet).

Ändern des 4Bit-Modes im Quelltext ändert nichts am Problem.

Also:
1
  LCD_CMD(0x20 );   //| (LCD_TYPE << 2|_displayfunction));

ändert nichts.

Dito funktioniert die Sequenz
1
  LCD_CMD(0x20 );
2
  __delay_ms(50);
3
  LCD_CMD(0x08 );
4
__delay_ms(50);

nicht :-(

Danke und Grüße
Harry

von Εrnst B. (ernst)


Lesenswert?

Dann zeig doch mal, wie du "LCD_CMD" implementiert hast.
Wenn dort einfach garnix oder was falsches passiert, wird das nix mit 
dem Display.
nach allem was wir bislang wissen, könntest du ja auch ein
1
#define LCD_CMD(x) IO_Expander_Write(rand())
irgendwo versteckt haben, um uns zu trollen.

von Sigi S. (sermon)


Lesenswert?

Nur Codeschnipsel, kein Displaytyp, keine Hilfe.....

von Ottmar K. (wil1)


Lesenswert?

Hallo Harry,

Tut mir leid für Dich, daß Dich. Den gelegentlich herablassenden Ton 
mußt Du hier einfach hin und wieder erdulden (rühmliche Ausnahmen gibt 
es natürlich).

Jetzt aber zu Deinem Problem, vielleicht hilft es Dir, wenn Du im 
Datenblatt des verbauten LCD-Controllers HD44780 nachliest - vermutlich 
handelt es sich bei Deinem LCD um einen solchen - wie die 
Initialisierung im 4- Bit Modus abzulaufen hat. Ich versuche es mal so:

Das LCD startet im 8Bit-Modus. Warum? Vom Hersteller so festgelegt. Zum 
Start der Initialisierung muß die Betriebsspannung einen Mindestwert 
erreicht haben, daher das Delay.

Nachfolgend die Initialisierung per Assembler (Für ASM unvollständig, da 
hier nur die Zahlenwerte und der systematische Ablauf gezeigt werden 
sollen). Die Zahlenwerte nach "movlw" mußt Du eben ins Hex-Format 
umwandeln und mit LCD_CMD absenden. Die Sequenz ist zwar binär codiert, 
aber einfach in das von Dir verwendete Hex-Format umzuwandeln.

Jeder Befehl muß per Toggeln des "Enable-Pins LCD_E" in den 
Arbeitsspeicher des LCD geschoben werden. Ich zeige das nur wegen dem 
Prinzip, dürfte bei Dir wegen des verwendeten Librarys auch so ablaufen.

mfG Ottmar

Hier die Sequenz
----------------
;Wait for more than 40 ms after Vcc rises to 2.7 V
   movlw    b'00110000'    ;1. Function set
   movwf    LCD_LAT        ;   (Interface is 8 bits long  .)
   bsf      LCD_E          ;   Toggle LCD_LAT,E
   nop
   bcf      LCD_E
   ;
   movlw    b'00110000'    ;1. Function set
   movwf    LCD_LAT        ;   (Interface is 8 bits long.)
   bsf      LCD_E          ;   Toggle LCD_LAT,E
   nop
   bcf      LCD_E
   ;
   ;wait for more than 4.1ms
   ;
   movlw    b'00110000'    ;2. Function set
   CALL     Control8Bit    ;   (Interface is 8 bits long.)
   ;
   movlw    b'00110000'    ;3. Function set
   CALL     Control8Bit    ;   (Interface is 8 bits long.)
   ;
   movlw    b'00100000'    ;4. Function set
   CALL     Control8Bit    ;   INTERFACE IS NOW 4 BITS LONG.
   ;
   movlw    b'00101000'    ;5. Function set 2 AND MORE LINES 5x8 Dot
   ;   movlw   b'00100000'    ;   Function set 1 SINGLE LINE    5x8 Dot
   CALL     OutLcd_Ctrl    ;   send function to LCD
   ;
   movlw    b'00001000'    ;6. display off
   CALL     OutLcd_Ctrl
   ;
   movlw    b'00000110'    ;7. entry mode, increment
   CALL     OutLcd_Ctrl  ;   disable display-shift
   ;
   movlw    b'00000011'    ;8. cursor home, cursor home
   CALL     OutLcd_Ctrl
   ;
   movlw    b'00001100'    ;9. Set   Display ON, CRSR_ON OFF Blink OFF
   CALL     OutLcd_Ctrl    ;   b3      b2         b1          b0
   ;
   movlw    b'00000001'    ;   Display löschen und cusor home
   CALL     OutLcd_Ctrl
   RETURN

von Ottmar K. (wil1)


Lesenswert?

Hi,ich glaub es nicht!

Gerade schau ich auf den Tisch,, in der Schaltung vor mir liegt ja das 
LCD2004/I2C, gekauft bei Amazon und ist HD44780-kompatibel. Läuft 
prächtig mit meinem 2Wire-Adapter.
Ottmar

von Εrnst B. (ernst)


Lesenswert?

Ottmar K. schrieb:
> Läuft
> prächtig mit meinem 2Wire-Adapter.

Aber du versuchst auch nicht, den 4-Bit-Modus im 4-Bit-Modus zu 
aktivieren.

Deshalb meine Frage nach der LCD_CMD... wenn da keine automatische 
Umschaltung für 8bit/4bit-Modus drinnen versteckt ist, ist Harry's 
Initialisierungssequenz falsch.

von Sigi S. (sermon)


Lesenswert?

Εrnst B. schrieb:
> Ottmar K. schrieb:
>> Läuft
>> prächtig mit meinem 2Wire-Adapter.
>
> Aber du versuchst auch nicht, den 4-Bit-Modus im 4-Bit-Modus zu
> aktivieren.
>
> Deshalb meine Frage nach der LCD_CMD... wenn da keine automatische
> Umschaltung für 8bit/4bit-Modus drinnen versteckt ist, ist Harry's
> Initialisierungssequenz falsch.

Das sollte doch alles der Controlle auf dem I2C Adapter übernehmen?!

Er erklärt die Lösung in C für einen PI.
Da sollte locker zum Ziel führen
https://youtu.be/1p1rgkslO0U

von H. H. (Gast)


Lesenswert?

Sigi S. schrieb:
> Das sollte doch alles der Controlle auf dem I2C Adapter übernehmen?!

Auf den üblichen Adaptern werkelt kein Controller, da gibts nur einen 
PCF8574T.

von Sigi S. (sermon)


Lesenswert?

Ok,

das ist richtig.
Dann sollte er das Datenblatt studieren.
Anfang der 90 er kannte ich das auswendig.
Mit den 805xx dann in Assembler programmiert.

von Harry R. (harryr)


Lesenswert?

Εrnst B. schrieb:
> Dann zeig doch mal, wie du "LCD_CMD" implementiert hast.
> Wenn dort einfach garnix oder was falsches passiert, wird das nix mit
> dem Display.
> nach allem was wir bislang wissen, könntest du ja auch ein
>
1
> #define LCD_CMD(x) IO_Expander_Write(rand())
2
>
> irgendwo versteckt haben, um uns zu trollen.
Trollen mache ich nur im AfD-Forum :-)
Spaß beiseite, hier mal die Funktionen, die im Endeffekt das machen, was
nötig ist, um via I2C zu kommunizieren.
1
unsigned char RS;
2
3
void I2C__Write(unsigned char Data)
4
{
5
   unsigned char tmpData = Data;
6
   I2C1_Host.Write(lcdAddress, &tmpData, sizeof(tmpData) );
7
}
8
9
void IO_Expander_Write(unsigned char Data)
10
{
11
  I2C__Write(Data | BackLight_State);
12
}
13
14
void LCD_Write_4Bit(unsigned char Nibble)
15
{
16
  // Get The RS Value To LSB OF Data..
17
  Nibble |= RS;
18
  IO_Expander_Write(Nibble | 0x04);
19
  IO_Expander_Write(Nibble & 0xFB);
20
}
21
22
void LCD_CMD(unsigned char CMD)
23
{
24
  RS = 0; // Command Register Select
25
  LCD_Write_4Bit(CMD & 0xF0);
26
  LCD_Write_4Bit((CMD << 4) & 0xF0);
27
}
28
29
void LCD_Write_Char(char Data)
30
{
31
  RS = 1;  // Data Register Select
32
  LCD_Write_4Bit(Data & 0xF0);
33
  LCD_Write_4Bit((Data << 4) & 0xF0);
34
}
35
36
/*
37
void LiquidCrystal_I2C::send(uint8_t value, uint8_t mode) {
38
  uint8_t highnib=value&0xf0;
39
  uint8_t lownib=(value<<4)&0xf0;
40
       write4bits((highnib)|mode);
41
  write4bits((lownib)|mode); 
42
}
43
 * */
44
45
void LCD_Write_String(char* Str)
46
{
47
    for(int i=0; Str[i]!='\0'; i++)
48
       LCD_Write_Char(Str[i]);
49
}

von Sigi S. (sermon)


Lesenswert?

Sigi S. schrieb:
> Dann sollte er das Datenblatt studieren.

Des uns nicht bekannten LCD Controllers.................

von Εrnst B. (ernst)


Lesenswert?

Harry R. schrieb:
1
 void LCD_CMD(unsigned char CMD)
2
 {
3
   RS = 0; // Command Register Select
4
   LCD_Write_4Bit(CMD & 0xF0);
5
   LCD_Write_4Bit((CMD << 4) & 0xF0);
6
 }

Genau da liegt das Problem. Beim Initialisieren ist das Display im 8 
Bit-Modus. da kannst du nicht zwei Nibble hintereinander raussenden und 
einzeln per E-Toggle übernehmen.
Du brauchst da nur das High-Nibble vom "Set 4 Bit Mode"-Commando setzen, 
einmal E Togglen.
d.H.: erste Initialisierung per "LCD_Write_4Bit", nicht per "LCD_CMD".
TLDR:
1
RS=0;
2
LCD_Write_4Bit(0x20);

von Harry R. (harryr)


Lesenswert?

Εrnst B. schrieb:
> Genau da liegt das Problem. Beim Initialisieren ist das Display im 8
> Bit-Modus. da kannst du nicht zwei Nibble hintereinander raussenden und
> einzeln per E-Toggle übernehmen.
Klingt logisch :-)
> Du brauchst da nur das High-Nibble vom "Set 4 Bit Mode"-Commando setzen,
> einmal E Togglen.
E togglen ?
Aber LCD_Write_4Bit(unsigned char Nibble)

sendet doch via IO_Expander_Write zweimal hintereinander 8 Bits.

Ich glaube ich/wir sind kurz vor der Lösung, aber mein Groschen hängt 
noch fest .. Kannst du bitte das  LCD_Initialize(unsigned char lAddress)
mal so übermitteln, wie es richtig wäre ?

von Εrnst B. (ernst)


Lesenswert?

Harry R. schrieb:
> Kannst du bitte das  LCD_Initialize(unsigned char lAddress)
> mal so übermitteln, wie es richtig wäre

Gucks dir hier ab:
https://github.com/lucasmaziero/LiquidCrystal_I2C/blob/master/LiquidCrystal_I2C.cpp#L69

"write4bits" entspricht deinem "LCD_Write_4Bit"
"command" entspricht deinem "LCD_CMD"
usw.

Harry R. schrieb:
> sendet doch via IO_Expander_Write zweimal hintereinander 8 Bits.

Von den 8 Bit, die der Expander ausgibt, sind nur 4 (die 4 oberen, das 
High-Nibble) mit den Datenleitungen des Displays verbunden.
Deshalb: Jeder 8-Bit-Schreibvorgang am Expander schreibt 4 Bits zum 
Display und die anderen 4 Bits bedienen Steuerleitungen (E, RS, RW, 
Backlight)

: Bearbeitet durch User
von Harry R. (harryr)


Lesenswert?

Εrnst B. schrieb:
> Harry R. schrieb:
>> Kannst du bitte das  LCD_Initialize(unsigned char lAddress)
>> mal so übermitteln, wie es richtig wäre
>
> Gucks dir hier ab:
> 
https://github.com/lucasmaziero/LiquidCrystal_I2C/blob/master/LiquidCrystal_I2C.cpp#L69
Tatsächlich ist das der Ursprung meines Versuchs, ich gehe das mit neuem 
Wissen nochmal an.
> "write4bits" entspricht deinem "LCD_Write_4Bit"
> "command" entspricht deinem "LCD_CMD"
> usw.
>
> Harry R. schrieb:
>> sendet doch via IO_Expander_Write zweimal hintereinander 8 Bits.
>
> Von den 8 Bit, die der Expander ausgibt, sind nur 4 (die 4 oberen, das
> High-Nibble) mit den Datenleitungen des Displays verbunden.
> Deshalb: Jeder 8-Bit-Schreibvorgang am Expander schreibt 4 Bits zum
> Display und die anderen 4 Bits bedienen Steuerleitungen (E, RS, RW,
> Backlight)
Ah, verstehe, jetzt ist mir das schon sehr viel klarer, ich bin kurz vor 
dem Verständnis :-)

VG

von Harry R. (harryr)


Lesenswert?

So, da wäre ich wieder mit neuen Erkenntnissen, alten Problemen und 
neuen Fragen. Der Tipp von Εrnst B. schrieb:

> Gucks dir hier ab:
> 
https://github.com/lucasmaziero/LiquidCrystal_I2C/blob/master/LiquidCrystal_I2C.cpp#L69

Das habe ich schon von Anfang an versucht, der Hinweis hat mich aber 
bestärkt es nochmal auf diesem Weg zu versuchen.

Die Portierung vom Arduino in die PIC-Welt sollte zu bewältigen sein.

Ich gehe mal ins Detail, bzgl. der Arduino-Bibliothek gibt ist noch auf 
die Header-Datei zu achten 
(https://github.com/lucasmaziero/LiquidCrystal_I2C/blob/master/LiquidCrystal_I2C.h).

Die erste zu überwindende Hürde ist eigentlich nicht schwer, portiert 
wird von C++ nach C. Sollte man meinen, denn die C++-Klase enthält kein 
print, das nützlich sein könnte :
1
void LiquidCrystal_I2C::printstr(const char c[]){
2
  //This function is not identical to the function used for "real" I2C displays
3
  //it's here so the user sketch doesn't have to be changed 
4
  print(c);
5
}

In den Examples findet man ein HelloWorld (leicht gekürzt):
1
#include <LiquidCrystal_I2C.h>
2
// Set the LCD address to 0x27 for a 16 chars and 2 line display
3
LiquidCrystal_I2C lcd(0x27, 16, 2);
4
5
void setup()
6
{
7
  // initialize the LCD
8
  lcd.begin(); //Init with pin default ESP8266 or ARDUINO
9
  //lcd.begin(0, 2); //ESP8266-01 I2C with pin 0-SDA 2-SCL
10
11
  // Turn on the blacklight and print a message.
12
  lcd.backlight();
13
  
14
  lcd.setCursor(0, 0);
15
#if defined(ESP8266)
16
  lcd.print("ESP8266");
17
#else
18
  lcd.print("ARDUINO");
19
#endif
20
  lcd.setCursor(0, 1);
21
  lcd.print("Hello, world!");
22
}

Das init des LCD besteht aus dem Constructor und dann dem begin(),
diese beiden Methoden weren in meiner Portierung dann zusammengefasst. 
Aber zurück zum print-Problem, im HW wird einfach lcd.print() 
aufgerufen. Das existiert im Quelltext der Klasse nicht, sondern wird 
irgendwie geerbt
1
class LiquidCrystal_I2C : public Print 
2
{

Ich habe mich auf die Suche begeben um herauszufinden welche Klaaase das 
vererbt, bin aber nicht fündig geworden, kann mir da jemand auf die 
Sprünge helfen ?

So, das dazu, das größte Problem ist bisher, dass ich es immer noch 
nicht geschafft habe, das LCD dazu zu bewegen 4 Zeilen anzuzeigen.

Ich zeige hier mal den kompletten Code (ohne Header-Datei).
1
#include <xc.h>
2
#include <stdio.h>
3
#include <stdint.h>
4
#include <stdbool.h>
5
#include "mcc_generated_files/system/clock.h" 
6
#include "mcc_generated_files/i2c_host/mssp.h"
7
#include "custom.h"
8
#include "lcdi2c.h"
9
10
// commands
11
#define LCD_CLEARDISPLAY   0x01
12
#define LCD_RETURNHOME     0x02
13
#define LCD_ENTRYMODESET   0x04
14
#define LCD_DISPLAYCONTROL 0x08
15
#define LCD_CURSORSHIFT    0x10
16
#define LCD_FUNCTIONSET    0x20
17
#define LCD_SETCGRAMADDR   0x40
18
#define LCD_SETDDRAMADDR   0x80
19
20
// flags for display entry mode
21
#define LCD_ENTRYRIGHT          0x00
22
#define LCD_ENTRYLEFT           0x02
23
#define LCD_ENTRYSHIFTINCREMENT 0x01
24
#define LCD_ENTRYSHIFTDECREMENT 0x00
25
26
// flags for display on/off control
27
#define LCD_DISPLAYON  0x04
28
#define LCD_DISPLAYOFF 0x00
29
#define LCD_CURSORON   0x02
30
#define LCD_CURSOROFF  0x00
31
#define LCD_BLINKON    0x01
32
#define LCD_BLINKOFF   0x00
33
34
// flags for display/cursor shift
35
#define LCD_DISPLAYMOVE 0x08
36
#define LCD_CURSORMOVE  0x00
37
#define LCD_MOVERIGHT   0x04
38
#define LCD_MOVELEFT    0x00
39
40
// flags for function set
41
#define LCD_8BITMODE 0x10
42
#define LCD_4BITMODE 0x00
43
#define LCD_2LINE    0x08
44
#define LCD_1LINE    0x00
45
#define LCD_5x10DOTS 0x04
46
#define LCD_5x8DOTS  0x00
47
48
// flags for backlight control
49
#define LCD_BACKLIGHT   0x08
50
#define LCD_NOBACKLIGHT 0x00
51
52
#define En 0b00000100  // Enable bit
53
#define Rw 0b00000010  // Read/Write bit
54
#define Rs 0b00000001  // Register select bit  : Data
55
#define Co 0b00000000  // Register select bit : command
56
57
void expanderWrite(uint8_t data);
58
void pulseEnable(uint8_t _data);
59
void write4bits(uint8_t value) ;
60
void send(uint8_t value, uint8_t mode) ;
61
inline void command(uint8_t value); 
62
void writeChar(uint8_t value); 
63
void writeStr(char* str);
64
void writeStrXY(uint8_t col, uint8_t row,char* str);
65
inline void writeData(char value) ;
66
bool lcdinit(uint8_t lcd_addr, uint8_t lcd_cols, uint8_t lcd_rows);
67
bool lcdclose(void);
68
inline void clear(void);
69
inline void home(void);
70
void setCursor(uint8_t col, uint8_t row);
71
void noDisplay(void); 
72
inline void display(void) ;
73
void noCursor(void); 
74
void cursor(void) ;
75
void noBlink(void); 
76
void blink(void) ;
77
void scrollDisplayLeft(void) ;
78
void scrollDisplayRight(void) ;
79
void leftToRight(void) ;
80
void rightToLeft(void) ;
81
void autoscroll(void) ;
82
void noAutoscroll(void) ;
83
void createChar(uint8_t location, uint8_t charmap[]) ;
84
void noBacklight(void); 
85
void backlight(void); 
86
bool getBacklight(void);
87
88
uint8_t _displayfunction  = 0;
89
uint8_t _displaycontrol   = 0;
90
uint8_t _displaymode      = 0;
91
uint8_t _cols             = 0;
92
uint8_t _rows             = 0;
93
uint8_t _charsize         = 0;
94
uint8_t _backlightval     = 0;
95
96
unsigned char lcdAddress  = 0;
97
bool     _initialized      = false;
98
99
#define TRY4BITMODE      0x03
100
#define SET4BITMODE      0x02
101
102
inline void expanderWrite(uint8_t data)
103
{                                        
104
  data |= _backlightval;
105
    //bool I2C1_Write(uint16_t address, uint8_t *data, size_t dataLength)
106
    // & ?
107
  I2C1_Host.Write((uint16_t)lcdAddress, &data, sizeof(data) );
108
//  Wire.endTransmission();   
109
}
110
111
inline void pulseEnable(uint8_t _data)
112
{
113
  expanderWrite(_data | En);  // En high
114
  //delayMicroseconds(1);    // enable pulse must be >450ns
115
  delayUs(1);
116
    
117
  expanderWrite(_data & ~En);  // En low
118
  delayUs(50);
119
    //delayMicroseconds(50);    // commands need > 37us to settle
120
}
121
122
inline void write4bits(uint8_t value) 
123
{
124
  expanderWrite(value);
125
  pulseEnable(value);
126
}
127
128
// write either command or data
129
inline void send(uint8_t value, uint8_t mode) 
130
{
131
  uint8_t highnib=value&0xf0;
132
  uint8_t lownib=(value<<4)&0xf0;
133
  write4bits((highnib)|mode);
134
  write4bits((lownib)|mode); 
135
}
136
137
inline void command(uint8_t value) 
138
{
139
  uint8_t highnib=value&0xf0;
140
  uint8_t lownib=(value<<4)&0xf0;
141
  write4bits((highnib));
142
  write4bits((lownib)); 
143
}
144
145
/*********** mid level commands, for sending data/cmds */
146
147
/*
148
inline void command(uint8_t value) 
149
{
150
  send(value, Co);
151
} */
152
153
//inline 
154
inline void writeChar(uint8_t value) 
155
{
156
 // send((uint8_t)value, Rs);
157
    uint8_t highnib=value&0xf0;
158
  uint8_t lownib=(value<<4)&0xf0;
159
  write4bits((highnib)|Rs);
160
  write4bits((lownib)|Rs); 
161
}
162
163
inline void writeStr(char* str)
164
{
165
   for(int i=0; str[i]!='\0'; i++)
166
       writeChar(str[i]);   
167
};
168
169
inline void writeStrXY(uint8_t col, uint8_t row,char* str)
170
{
171
   setCursor(col, row);
172
   writeStr(str);
173
};
174
175
inline void writeData(char value) 
176
{
177
   send((uint8_t)value, Rs);
178
}
179
180
uint8_t setlcdmode = 0b00010000;
181
182
inline bool lcdinit(uint8_t lcd_addr, uint8_t lcd_cols, uint8_t lcd_rows)
183
{
184
    
185
  if (lcd_addr == 0)
186
    return(false);
187
  else
188
    lcdAddress = lcd_addr;
189
  
190
  _cols = lcd_cols;
191
  _rows = lcd_rows;
192
  _charsize = LCD_5x8DOTS;
193
  _backlightval = LCD_BACKLIGHT;
194
  
195
  if (lcd_rows > 1)
196
      setlcdmode |= 0x08;
197
    
198
  _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
199
  
200
  if (_rows > 1) 
201
  {
202
  _displayfunction |= LCD_2LINE;
203
  }
204
  // for some 1 line displays you can select a 10 pixel high font
205
  if ((_charsize != 0) && (_rows == 1)) 
206
  {
207
  _displayfunction |= LCD_5x10DOTS;
208
  }
209
210
  // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
211
  // according to datasheet, we need at least 40ms after power rises above 2.7V
212
  // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
213
  delayMs(50); 
214
215
  // Now we pull both RS and R/W low to begin commands
216
  expanderWrite(_backlightval);  // reset expanderand turn backlight off (Bit 8 =1)
217
  delayMs(1000);
218
219
  //put the LCD into 4 bit mode
220
  // this is according to the hitachi HD44780 datasheet
221
  // figure 24, pg 46
222
223
  // we start in 8bit mode, try to set 4 bit mode
224
  // oder
225
  // write4bits(TRY4BITMODE << 4);
226
227
  write4bits(TRY4BITMODE << 4);
228
  delayMs(5);
229
  //delayMicroseconds(4500); // wait min 4.1ms
230
231
  // second try
232
  write4bits(TRY4BITMODE << 4);
233
  //delayMicroseconds(4500); // wait min 4.1ms
234
  delayMs(5);
235
    
236
  // third go!
237
  write4bits(TRY4BITMODE << 4); 
238
  delayMs(150);
239
240
  // finally, set to 4-bit interface
241
//  write4bits(0b00101000);
242
  write4bits(SET4BITMODE << 4); 
243
244
  // set # lines, font size, etc.
245
  command(LCD_FUNCTIONSET | _displayfunction);  
246
  
247
  // turn the display on with no cursor or blinking default
248
  _displaycontrol = LCD_DISPLAYON | LCD_CURSORON | LCD_BLINKOFF;
249
    
250
  display();
251
  
252
  // clear it off
253
  clear();
254
  
255
  // Initialize to default text direction (for roman languages)
256
  _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
257
  
258
  // set the entry mode
259
////  command(LCD_ENTRYMODESET | _displaymode);
260
  
261
  home();
262
  _initialized = true;
263
  return(true);
264
}
265
266
bool lcdclose(void)
267
{
268
  lcdAddress = 0;
269
  _displayfunction  = 0;
270
  _displaycontrol   = 0;
271
  _displaymode      = 0;
272
  _cols             = 0;
273
  _rows             = 0;
274
  _charsize         = 0;
275
  _backlightval     = 0;
276
  I2C1_Host.Deinitialize(); 
277
  _initialized = false;
278
  return(true);
279
};
280
281
/********** high level commands, for the user! */
282
inline void clear()
283
{
284
  command(LCD_CLEARDISPLAY);// clear display, set cursor position to zero
285
  delayMs(2);
286
  //delayMicroseconds(2000);  // this command takes a long time!
287
}
288
289
inline void home()
290
{
291
  command(LCD_RETURNHOME);  // set cursor position to zero
292
  //delayMicroseconds(2000);  // this command takes a long time!
293
  delayMs(2);
294
}
295
296
void setCursor(uint8_t col, uint8_t row)
297
{
298
  char row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
299
  
300
  if (row > _rows) 
301
    row = _rows;
302
  row = _rows-1;    // we count rows starting w/0
303
  
304
  command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
305
}
306
307
// Turn the display on/off (quickly)
308
void noDisplay() 
309
{
310
  _displaycontrol &= ~LCD_DISPLAYON;
311
  command(LCD_DISPLAYCONTROL | _displaycontrol);
312
}
313
314
inline void display() 
315
{
316
  _displaycontrol |= LCD_DISPLAYON;
317
  command(LCD_DISPLAYCONTROL | _displaycontrol);
318
}
319
320
// Turns the underline cursor on/off
321
void noCursor() 
322
{
323
  _displaycontrol &= ~LCD_CURSORON;
324
  command(LCD_DISPLAYCONTROL | _displaycontrol);
325
}
326
327
void cursor() 
328
{
329
  _displaycontrol |= LCD_CURSORON;
330
  command(LCD_DISPLAYCONTROL | _displaycontrol);
331
}
332
333
// Turn on and off the blinking cursor
334
void noBlink() 
335
{
336
  _displaycontrol &= ~LCD_BLINKON;
337
  command(LCD_DISPLAYCONTROL | _displaycontrol);
338
}
339
340
void blink() 
341
{
342
  _displaycontrol |= LCD_BLINKON;
343
  command(LCD_DISPLAYCONTROL | _displaycontrol);
344
}
345
346
// These commands scroll the display without changing the RAM
347
void scrollDisplayLeft(void) 
348
{
349
  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
350
}
351
352
void scrollDisplayRight(void) 
353
{
354
  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
355
}
356
357
// This is for text that flows Left to Right
358
void leftToRight(void) 
359
{
360
  _displaymode |= LCD_ENTRYLEFT;
361
  command(LCD_ENTRYMODESET | _displaymode);
362
}
363
364
// This is for text that flows Right to Left
365
void rightToLeft(void) 
366
{
367
  _displaymode &= ~LCD_ENTRYLEFT;
368
  command(LCD_ENTRYMODESET | _displaymode);
369
}
370
371
// This will 'right justify' text from the cursor
372
void autoscroll(void) 
373
{
374
  _displaymode |= LCD_ENTRYSHIFTINCREMENT;
375
  command(LCD_ENTRYMODESET | _displaymode);
376
}
377
378
// This will 'left justify' text from the cursor
379
void noAutoscroll(void) 
380
{
381
  _displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
382
  command(LCD_ENTRYMODESET | _displaymode);
383
}
384
385
// Allows us to fill the first 8 CGRAM locations
386
// with custom characters
387
void createChar(uint8_t location, uint8_t charmap[]) 
388
{
389
  location &= 0x7; // we only have 8 locations 0-7
390
  command(LCD_SETCGRAMADDR | (uint8_t)(location << 3));
391
  for (int i=0; i<8; i++) 
392
  writeData(charmap[i]);
393
}
394
395
// Turn the (optional) backlight off/on
396
void noBacklight(void) 
397
{
398
  _backlightval=LCD_NOBACKLIGHT;
399
  expanderWrite(0);
400
}
401
402
void backlight(void) 
403
{
404
  _backlightval=LCD_BACKLIGHT;
405
  expanderWrite(0);
406
}
407
408
// Get status of blacklight
409
bool getBacklight() 
410
{
411
  return(_backlightval == LCD_BACKLIGHT);
412
}
413
414
void load_custom_character(uint8_t char_num, uint8_t *rows)
415
{
416
  createChar(char_num, rows);
417
}
418
419
void setBacklight(uint8_t new_val)
420
{
421
  if (new_val) 
422
  {
423
  backlight();    // turn backlight on
424
  } 
425
  else 
426
  {
427
  noBacklight();    // turn backlight off
428
  }
429
}
430
431
bool isinitialized(void)
432
{
433
   return(_initialized);
434
};
435
436
const lcd_host_t lcd =
437
{
438
  .init = lcdinit,
439
  .close = lcdclose,
440
  .printC = writeChar,
441
  .printStr = writeStr,
442
  .printStrXY = writeStrXY,   
443
  .cls = clear,
444
  .home = home,
445
  .hideDisplay = noDisplay,
446
  .showDisplay = display,
447
  .blinkOff = noBlink,
448
  .blinkOn = blink,
449
  .hideCursor = noCursor,
450
  .showcursor = cursor,
451
  .scrollLeft = scrollDisplayLeft,
452
  .scrollRight = scrollDisplayRight,
453
  .setLeftToRight = leftToRight,
454
  .setRightToLeft = rightToLeft,
455
  .backlightOff = noBacklight,
456
  .backlightOn = backlight,
457
  .getBacklight = getBacklight,
458
  .setAutoscrollMode = autoscroll,
459
  .setNoAutoscrollModel = noAutoscroll,
460
  .createChar = createChar,
461
  .setCursor = setCursor,
462
  .initialized =  isinitialized
463
} ;

Da sind noch Code-Reste drinnen, die viel. merkwürdig sind, einfach 
nicht beachten. Mittels einer LED konnte ich sehen, dass immerhin schon 
etwas auf den I2C-Bus geschickt wird.

Kommen wir zum Init,  muss vorher noch über die einzelnen Routinen 
gesprochen werden, die sich dann zum "verwenden" des I2C führen.
Aus
1
void LiquidCrystal_I2C::expanderWrite(uint8_t _data){                                        
2
  Wire.beginTransmission(_addr);
3
  Wire.write((int)(_data) | _backlightval);
4
  Wire.endTransmission();   
5
}
Wird bei mir:
1
inline void expanderWrite(uint8_t data)
2
{                                        
3
  data |= _backlightval;
4
    //bool I2C1_Write(uint16_t address, uint8_t *data, size_t dataLength)
5
    // & ?
6
  I2C1_Host.Write((uint16_t)lcdAddress, &data, sizeof(data) );
7
//  Wire.endTransmission();   
8
}

Da in
1
  I2C1_Host.Write((uint16_t)lcdAddress, &data, sizeof(data) );
alles "versteckt ist" was die I2C-Schnittstelle betrifft
wird kein Wire.begin mehr benötigt (oder etwa doch ?).
Unsicher bin ich beim Parameter data.

In der Headerdatei (von mcc generiert) ist das so
definiert
1
bool I2C1_Write(uint16_t address, uint8_t *data, size_t dataLength);
data ist ein Zeiger auf einen uint8_t-Wert. Ich denke mal mein &data 
sollte das richtig implementieren. Die Frage ist, wo befindet sich data 
denn ?
Wenn es auf dem Stack ist wird es nach dem Ende von expanderWrite 
irgendwann überschrieben. Da die I2C-Kommunikation aysynchron über einen 
Interrupt erfogt ist es möglich, dass an dieser Speicherstelle bereits 
etwas anderes steht. Um da auf der sichern Seite zu sein würde ich das 
dannn so implementieren :
1
inline void expanderWrite(uint8_t data)
2
{       
3
  static  uint8_t staticdata = data;
4
  staticdata |= _backlightval;
5
  I2C1_Host.Write((uint16_t)lcdAddress, &staticdata, sizeof(data) );
6
}

Die Routine
1
inline void pulseEnable(uint8_t _data)
2
{
3
  expanderWrite(_data | En);  // En high
4
      // enable pulse must be >450ns
5
  delayUs(1);
6
    
7
  expanderWrite(_data & ~En);  // En low
8
  delayUs(50);
9
       // commands need > 37us to settle
10
}
ist mir völlig unklar.Wenn ich davon ausgehe, dass I2C1_Host.Write
die einzige Schnittstelle zur I2C-Kommunikation drinnen ist
brauche ich pulseEnable doch gar nicht, oder ??

So, erst mal bis hierhin. Wenn diese Dinge geklärt/erklärt sind kann ich 
mich um die weiterer Implementierung kümmern, sonst wird das hier ein 
sinnloses tryAnderror.

Viele Grüße und danke für jede Hilfe

PS.: Da es vom Compiler Mecker wg. Stackgröße gabe habe ich viel mit 
inline gearbeitet und teilweise routinen auseinandergezogen, damit nicht 
so viele Calls den Stack zumüllen.

von Michael W. (miks)


Lesenswert?

Ey Du Stoffel! Diesen Satz gelesen? Und auch verstanden?

● Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Meine Fresse...

: Bearbeitet durch User
von Harry R. (harryr)


Lesenswert?

Michael W. schrieb:
> Ey Du Stoffel! Diesen Satz gelesen? Und auch verstanden?
Wenn's für dich zu lang ist, gilt das nicht für alle.
Mach' doch mal einen Längenvorschlag.
> ● Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang
>
> Meine Fresse...
Schmerzen von der letzten Prügelei ?
Gute Besserung :-)

: Bearbeitet durch User
von Christoph S. (155christo)


Lesenswert?

Guten Abend:

Diese Seite kennst Du?

https://iradan.com/?p=420

"I used a standard 44780 16×2 LCD, a Tautic development board, and a PIC 
16F1509 (happens to come with the dev board). For programming I used 
MPLAB X IDE v1.95 and XC8 v1.21 (free version) and the PICkit 3 
programmer. The code is commented enough to figure out the hardware 
setup."

PIC1509 und LCD, ok ohne i2c, aber vlt hift es weiter.

von Stephan S. (uxdx)


Lesenswert?

Harry R. schrieb:
> Michael W. schrieb:
>> Ey Du Stoffel! Diesen Satz gelesen? Und auch verstanden?
> Wenn's für dich zu lang ist, gilt das nicht für alle.
> Mach' doch mal einen Längenvorschlag.
>> ● Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang
>>
>> Meine Fresse...
> Schmerzen von der letzten Prügelei ?
> Gute Besserung :-)

Es ist schon eine Frechheit einen fast 500 Zeilen langen Quellcode 
einzufügen, dazu noch 'zig Leerzeilen und dann noch rumstänkern, wenn es 
den Mitlesern nicht passt.

Zu hast ganz oben schon mal einen Mitleser der einen korrekten Beitrag 
brachte angemacht, obwohl Du froh sein solltest, hier kostenlose Hilfe 
zu bekommen. Das Leben ist keine Einbahnstrasse: wenn Du etwas von der 
Community erwartest, kann die Community erwarten, dass Du Dich ihr 
anpasst und nicht umgekehrt.

: Bearbeitet durch User
von Harry R. (harryr)


Lesenswert?

Christoph S. schrieb:
> Guten Abend:
>
> Diese Seite kennst Du?
>
> https://iradan.com/?p=420
>
> PIC1509 und LCD, ok ohne i2c, aber vlt hift es weiter.
Danke. Nein diese Seite kannte ich bisher nicht, aber das wird sich 
ändern.

von Harry R. (harryr)


Lesenswert?

Stephan S. schrieb:

> Es ist schon eine Frechheit einen fast 500 Zeilen langen Quellcode
> einzufügen, dazu noch 'zig Leerzeilen und dann noch rumstänkern, wenn es
> den Mitlesern nicht passt.
- Die Leerzeilen fügt die Forumssoftware größtenteils selbst ein.
- Ich habe nicht gestänkert, sondern den Kommentar kommentiert.
> Zu hast ganz oben schon mal einen Mitleser der einen korrekten Beitrag
> brachte angemacht, obwohl Du froh sein solltest, hier kostenlose Hilfe
> zu bekommen.
Ich habe meine Meinung zu seinem Kommentar abgegeben, ist das hier 
verboten ?
> Das Leben ist keine Einbahnstrasse: wenn Du etwas von der
> Community erwartest, kann die Community erwarten, dass Du Dich ihr
> anpasst und nicht umgekehrt.
Hast du auch was zum Thema beizutragen oder suchst du einfach nur nach 
Dingen, die dir nicht gefallen ?

Einen schönen Abend noch :-)

von Stephan S. (uxdx)


Lesenswert?

Harry R. schrieb:
> Hast du auch was zum Thema beizutragen oder suchst du einfach nur nach
> Dingen, die dir nicht gefallen ?

Ich hab etwas beizutragen:
Etwas mehr Dankbarkeit deinerseits den Leuten die Dir hier helfen wäre 
angebracht.

: Bearbeitet durch User
von Harry R. (harryr)


Lesenswert?

Stephan S. schrieb:
> Harry R. schrieb:
>> Hast du auch was zum Thema beizutragen oder suchst du einfach nur nach
>> Dingen, die dir nicht gefallen ?
>
> Ich hab etwas beizutragen:
> Etwas mehr Dankbarkeit deinerseits den Leuten die Dir hier helfen wäre
> angebracht.
Ich bedanke mich für jede Hilfe, da kannst du gerne alle meine Beiträge 
durchlesen. Damit würde ich die Diskussion mit dir gerne beenden und das 
Ganze hier wieder auch fachlich/sachlicher Ebene weiter führen.

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.