PIC16F1509/Sansmart LCD2004/I2C , benötige etwas Hilfe

OP #7482851
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

Angehängte Dateien:
#7482857
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

#7482942
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

OP #7482951
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

#7482976
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.

#7483051
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

#7483171
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

OP #7483212
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
}
#7483225
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);
OP #7483253
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 ?

#7483612
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)

OP #7483769
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

OP #7485761
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.

#7486239
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.

#7486264
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.

OP #7486337
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 :-)

OP #7486547
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren