PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : LCD an RP6-Base



radbruch
16.07.2008, 19:23
Hallo

Nach einer längeren Auszeit habe ich nun ein neues RP6-Projekt begonnen:

http://radbruch.roboterbastler.de/rp6/lcd/lcd4_klein.jpg (http://radbruch.roboterbastler.de/rp6/lcd/lcd4.jpg)
http://radbruch.roboterbastler.de/rp6/lcd/lcd1_klein.jpg (http://radbruch.roboterbastler.de/rp6/lcd/lcd1.jpg) http://radbruch.roboterbastler.de/rp6/lcd/lcd2_klein.jpg (http://radbruch.roboterbastler.de/rp6/lcd/lcd2.jpg) http://radbruch.roboterbastler.de/rp6/lcd/lcd3_klein.jpg (http://radbruch.roboterbastler.de/rp6/lcd/lcd3.jpg)

Ich betreibe das 20x4-LCD im 4-bit-Mode (ohne Busy) an den Erweiterungspins 101-104, BPL und BPR (das sind die LEDs 1-6). Die Adapterplatine wird direkt in die entsprechenden Stiftleisten auf der RP6-Platine eingesteckt . Zusätzlich sind auch die Anschlüsse ADC0 und ADC1 angeschlossen, sie liefern die Versorgungsspannung für das Display. XBUS und UBUS sind nicht angeschlossen, die Stecker dienen nur der Stabilität. Die elektrische Verbindung zum Display besteht aus zwei 6-poligen Stiftleisten in die der 16-polige Stecker des LCDs eingesteckt wird:

1 - GND
2 - Vcc (5V)
3 - Kontrast (0-0,5V)
4 - RS Steuer- oder Datenregister (LED3)
5 - R/W Datenrichtung (GND)
6 - E Datenübernahme (LED6)

11 - D4 Datenbit4 (LED1)
12 - D5 Datenbit5 (LED2)
13 - D6 Datenbit6 (LED4)
14 - D7 Datenbit7 (LED5)
15 - A Anode Hintergrundbeleuchtung (5V)
16 - K Kathode Hintergrundbeleuchtung (GND)

Die rausgenagten Löcher in der Adapterplatine schaffen Platz für die überstehenen Bauteile wie Kondensatoren und Quarz. btw sind diese Bauteile nun auch vor tieffliegenden Katzenpfoten geschützt ;)

Zur Ansteuerung habe ich mir ein kleines C-Programm gebastelt. Es verwendet zwar meine abgespeckte RP6-Lib, sollte aber auch mit den orginalen Libs zusammenarbeiten. Geplant ist eine Erweiterung als Task, aber das dauert noch etwas:


// LCD 20x4 (LMC-SSC4A20) an den LEDs 1-6 16.7.2008 mic

// 4-Bit-Ansteuerung ohne busy-Abfrage:

// 1 - GND - GND
// 2 - Vcc - Vcc
// 3 - Kontrast (0-0,5V)
// 4 - RS - LS3 (BPR)
// 5 - R/W - GND
// 6 - E - LS6 (BPL)
// 7
// 8
// 9
// 10
// 11 - Data4 - LS1 (101)
// 12 - Data5 - LS2 (102)
// 13 - Data6 - LS4 (103)
// 14 - Data7 - LS5 (104)
// 15 - A - Vcc
// 16 - K - GND

#include "rblib.h"
#include "rblib.c"

#define demospeed 50
#define d4h (PORTC|=SL1)
#define d4l (PORTC&=~SL1)
#define d5h (PORTC|=SL2)
#define d5l (PORTC&=~SL2)
#define d6h (PORTB|=SL4)
#define d6l (PORTB&=~SL4)
#define d7h (PORTB|=SL5)
#define d7l (PORTB&=~SL5)
#define rsh (PORTC|=SL3)
#define rsl (PORTC&=~SL3)
#define eh (PORTB|=SL6)
#define el (PORTB&=~SL6)
//#define vri_on (PORTA |= 1)
//#define vri_off (PORTA &= ~1)
//#define vra_on (PORTA |= 2)
//#define vra_off (PORTA &= ~2)

void delay_ms(uint16_t ms)
{
while(ms--) delay(5); // delay(5) ~ 1,1 ms
// hier müßte bei Verwendung der orginalen Library die Funktion mSleep() stehen:
// mSleep(ms);
}
void strobe(void)
{
eh;
delay_ms(1);
el;
}
void lcd_write8(uint8_t wert, uint8_t pause)
{
if(wert & 16) d4h; else d4l;
if(wert & 32) d5h; else d5l;
if(wert & 64) d6h; else d6l;
if(wert & 128) d7h; else d7l;
strobe();
delay_ms(pause);
}
void lcd_write4(uint8_t wert, uint8_t pause)
{
if(wert & 16) d4h; else d4l; // high nipple
if(wert & 32) d5h; else d5l;
if(wert & 64) d6h; else d6l;
if(wert & 128) d7h; else d7l;
strobe();
if(wert & 1) d4h; else d4l; // low nipple
if(wert & 2) d5h; else d5l;
if(wert & 4) d6h; else d6l;
if(wert & 8) d7h; else d7l;
strobe();
delay_ms(pause);
}
void lcd_cls(void)
{
rsl;
lcd_write4(1,2);
rsh;
}
void lcd_locate(uint8_t x, uint8_t y)
{
rsl;
switch (y) {
case 0: lcd_write4(0x80+x, 40); break; // 0. Zeile
case 1: lcd_write4(0xc0+x, 40); break; // 1. Zeile
case 2: lcd_write4(0x94+x, 40); break; // 2. Zeile
case 3: lcd_write4(0xd4+x, 40); break; // 3. Zeile
}
rsh;
}
void lcd_writeChar(uint8_t zeichen)
{
lcd_write4(zeichen,1);
}
void lcd_writeString(char *string)
{
while(*string)
lcd_writeChar(*string++);
}
void lcd_init(void)
{
DDRC |= 0x70; // LED1-3 auf Ausgang und low
PORTC &= ~0x70;
DDRB |= 0x83; // LED4-6 auf Ausgang und low
PORTB &= ~0x83;
//DDRA |= 3; // ADC0 und ADC1 auf Ausgang und low
//PORTA &= ~3;
//DDRC |= 0x3; // SCL und SDA auf Ausgang und low
//PORTC &= ~0x3;
rsl;
delay_ms(100);
lcd_write8(0b00100000,100); // Function Set: 4bit-Modus starten
lcd_write4(0b00101000,40); // Function Set: 2 Zeilen, Font 0 (0010NFxx)
lcd_write4(0b00000001,40); // Display Clear
lcd_write4(0b00000110,40); // Entry Mode Set: inc, no shift
lcd_write4(0b00001100,40); // Display On and Cursor
//lcd_write4(0b00001111,40); // Display On and Cursor
rsh;
delay_ms(1);
}
int main(void)
{
uint8_t i; // Hilfsvariable
rblib_init(); // rblib initialisieren
lcd_init(); // lcd initialisieren

lcd_cls(); // Display löschen
lcd_locate(0,0); // Schreibpositon festlegen
for(i=0; i<20; i++) {lcd_writeChar('*'); delay_ms(demospeed); }
lcd_locate(0,1);
for(i=0; i<20; i++) {lcd_writeChar('a'+i); delay_ms(demospeed/2); }
lcd_locate(0,2);
for(i=0; i<20; i++) {lcd_writeChar('A'+i); delay_ms(demospeed/3); }
lcd_locate(0,3);
for(i=0; i<10; i++) {lcd_writeChar('0'+i); delay_ms(demospeed/4); }
for(i=0; i<10; i++) {lcd_writeChar('0'+i); delay_ms(demospeed/4); }

delay_ms(2000);
lcd_cls();
lcd_locate(2,0);
lcd_writeString("RP6 ROBOT SYSTEM");
lcd_locate(0,2);
lcd_writeString("LC-Display an LED1-6");
lcd_locate(9,3);
lcd_writeString("16.7.08 mic");

while(1){
}
return 0;
}
Zusammengewurstelt nach den Infos aus:
https://www.roboternetz.de/wissen/index.php/LCD-Modul_am_AVR
http://www.mikrocontroller.net/articles/AVR-Tutorial:_LCD
und dem Datenblatt des Displays (http://www.eclife.com.tw/photo/A1260010.pdf)

Noch ein Filmchen dazu:
http://i.ytimg.com/vi/2UtIABxSUXs/2.jpg (http://www.youtube.com/watch?v=2UtIABxSUXs)
http://www.youtube.com/watch?v=2UtIABxSUXs

Gruß

mic

RP6conrad
18.07.2008, 21:27
Sieht gans nett aus. Braucht das Diplay auch eine Initialisierung ? Und welche lib ist rblib.c ?

radbruch
18.07.2008, 22:31
Hallo


Braucht das Display auch eine Initialisierung?
In der Funktion lcd_init() wird das Display initialisiert.


Und welche lib ist rblib.c?
rblib ist eine minimale RP6-Library mit einfachen Funktionen für UART, Motor, LEDs und ADCs. Es wird nur der Timer 1 für die Motor-PWM verwendet. (rb steht für radbruch ;):
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=329255#329255

Gruß

mic

radbruch
22.07.2008, 22:06
Hallo

Jetzt habe ich noch eine kleine Erweiterung angebaut:

http://radbruch.roboterbastler.de/rp6/lcd/lcd5_klein.jpg (http://radbruch.roboterbastler.de/rp6/lcd/lcd5.jpg)

Fünf Taster mit Vorwiderstand (820 Ohm, die hatte ich grad rumliegen) zwischen den Anschlüssen SL1-SL5 und GND. Blöderweise sind die Widerstände zu klein, so kann ich nicht senden wenn eine der Tasten gedrückt ist, aber trotzdem recht brauchbar:

http://i.ytimg.com/vi/ieltub967Jo/2.jpg (http://www.youtube.com/watch?v=ieltub967Jo)
http://www.youtube.com/watch?v=ieltub967Jo

Der Code (Tastenabfrage und Usergraphic):

// LCD 20x4 (LMC-SSC4A20) an den LEDs 1-6 16.7.2008 mic

// 4-Bit-Ansteuerung ohne busy-Abfrage:

// 1 - GND - GND
// 2 - Vcc - Vcc
// 3 - Kontrast (0-0,5V)
// 4 - RS - LS3 (BPR)
// 5 - R/W - GND
// 6 - E - LS6 (BPL)
// 7
// 8
// 9
// 10
// 11 - Data4 - LS1 (101)
// 12 - Data5 - LS2 (102)
// 13 - Data6 - LS4 (103)
// 14 - Data7 - LS5 (104)
// 15 - A - Vcc
// 16 - K - GND

#include "rblib.h"
#include "rblib.c"

#define demospeed 50
#define d4h (PORTC|=SL1)
#define d4l (PORTC&=~SL1)
#define d5h (PORTC|=SL2)
#define d5l (PORTC&=~SL2)
#define d6h (PORTB|=SL4)
#define d6l (PORTB&=~SL4)
#define d7h (PORTB|=SL5)
#define d7l (PORTB&=~SL5)
#define rsh (PORTC|=SL3)
#define rsl (PORTC&=~SL3)
#define eh (PORTB|=SL6)
#define el (PORTB&=~SL6)
//#define vri_on (PORTA |= 1)
//#define vri_off (PORTA &= ~1)
//#define vra_on (PORTA |= 2)
//#define vra_off (PORTA &= ~2)

void delay_ms(uint16_t ms)
{
while(ms--) delay(5); // delay(5) ~ 1,1 ms
// hier müßte bei Verwendung der orginalen Library die Funktion mSleep() stehen:
// mSleep(ms);
}
void strobe(void)
{
eh;
delay_ms(1);
el;
}
void lcd_write8(uint8_t wert, uint8_t pause)
{
if(wert & 16) d4h; else d4l;
if(wert & 32) d5h; else d5l;
if(wert & 64) d6h; else d6l;
if(wert & 128) d7h; else d7l;
strobe();
delay_ms(pause);
}
void lcd_write4(uint8_t wert, uint8_t pause)
{
if(wert & 16) d4h; else d4l; // high nipple
if(wert & 32) d5h; else d5l;
if(wert & 64) d6h; else d6l;
if(wert & 128) d7h; else d7l;
strobe();
if(wert & 1) d4h; else d4l; // low nipple
if(wert & 2) d5h; else d5l;
if(wert & 4) d6h; else d6l;
if(wert & 8) d7h; else d7l;
strobe();
delay_ms(pause);
}
void lcd_cls(void)
{
rsl;
lcd_write4(1,2);
rsh;
}
void lcd_locate(uint8_t x, uint8_t y)
{
rsl;
switch (y) {
case 0: lcd_write4(0x80+x, 40); break; // 0. Zeile
case 1: lcd_write4(0xc0+x, 40); break; // 1. Zeile
case 2: lcd_write4(0x94+x, 40); break; // 2. Zeile
case 3: lcd_write4(0xd4+x, 40); break; // 3. Zeile
}
rsh;
}
void lcd_writeChar(uint8_t zeichen)
{
lcd_write4(zeichen,1);
}
void lcd_writeString(char *string)
{
while(*string)
lcd_writeChar(*string++);
}
void lcd_writeInteger(int16_t number, uint8_t base)
{
char buffer[17];
itoa(number, &buffer[0], base);
lcd_writeString(&buffer[0]);
}
uint8_t lcd_getkeys(void)
{
uint8_t keys=0;
el;
DDRC &= ~SL1;
PORTC|=SL1;
DDRC &= ~SL2;
PORTC|=SL3;
DDRC &= ~SL3;
PORTC|=SL2;
DDRB &= ~SL4;
PORTB|=SL4;
DDRB &= ~SL5;
PORTB|=SL5;

if(PINC & SL1) keys |= 8;
if(PINC & SL2) keys |= 4;
if(PINC & SL3) keys |= 16;
if(PINB & SL4) keys |= 2;
if(PINB & SL5) keys |= 1;

DDRC |= 0x70; // LED1-3 auf Ausgang und low
PORTC &= ~0x70;
DDRB |= 0x83; // LED4-6 auf Ausgang und low
PORTB &= ~0x83;
return(~keys & 0b11111);
}
void lcd_init(void)
{
DDRC |= 0x70; // LED1-3 auf Ausgang und low
PORTC &= ~0x70;
DDRB |= 0x83; // LED4-6 auf Ausgang und low
PORTB &= ~0x83;
//DDRA |= 3; // ADC0 und ADC1 auf Ausgang und low
//PORTA &= ~3;
//DDRC |= 0x3; // SCL und SDA auf Ausgang und low
//PORTC &= ~0x3;
rsl;
delay_ms(100);
lcd_write8(0b00100000,100); // Function Set: 4bit-Modus starten
lcd_write4(0b00101000,40); // Function Set: 2 Zeilen, Font 0 (0010NFxx)
lcd_write4(0b00000001,40); // Display Clear
lcd_write4(0b00000110,40); // Entry Mode Set: inc, no shift
lcd_write4(0b00001100,40); // Display On and Cursor
//lcd_write4(0b00001111,40); // Display On and Cursor
rsh;
delay_ms(1);
}
int main(void)
{
uint8_t i=0; // Hilfsvariable
rblib_init(); // rblib initialisieren
lcd_init(); // lcd initialisieren
lcd_cls(); // lcd Inhalt löschen
rsl; // Startadresse Usergraphic setzen
lcd_write4(64,40);
rsh;
lcd_writeChar(0b00000); // eigene Zeichen definieren
lcd_writeChar(0b00000);
lcd_writeChar(0b00100);
lcd_writeChar(0b00100);
lcd_writeChar(0b00100);
lcd_writeChar(0b10101);
lcd_writeChar(0b01110);
lcd_writeChar(0b00100);

for(i=0; i<8;i++) // 5x8-Block
{
lcd_writeChar(31);
}

lcd_writeChar(0);
lcd_writeChar(31);
lcd_writeChar(15);
lcd_writeChar(7);
lcd_writeChar(3);
lcd_writeChar(1);
lcd_writeChar(0);
lcd_writeChar(0);

lcd_writeChar(0b00000);
lcd_writeChar(0b01010);
lcd_writeChar(0b00000);
lcd_writeChar(0b10001);
lcd_writeChar(0b01110);
lcd_writeChar(0b00000);
lcd_writeChar(0b00000);
lcd_writeChar(0b00000);

lcd_writeChar(0b00000);
lcd_writeChar(0b01010);
lcd_writeChar(0b00000);
lcd_writeChar(0b01110);
lcd_writeChar(0b10001);
lcd_writeChar(0b00000);
lcd_writeChar(0b00000);
lcd_writeChar(0b00000);

lcd_writeChar(0b11111);
lcd_writeChar(0b10001);
lcd_writeChar(0b10001);
lcd_writeChar(0b10001);
lcd_writeChar(0b10001);
lcd_writeChar(0b10001);
lcd_writeChar(0b11111);
lcd_writeChar(0b00000);

lcd_writeChar(0b00000);
lcd_writeChar(0b00100);
lcd_writeChar(0b01110);
lcd_writeChar(0b11111);
lcd_writeChar(0b01110);
lcd_writeChar(0b00100);
lcd_writeChar(0b00000);
lcd_writeChar(0b00000);

lcd_writeChar(0b10101);
lcd_writeChar(0b01010);
lcd_writeChar(0b10101);
lcd_writeChar(0b01010);
lcd_writeChar(0b10101);
lcd_writeChar(0b01010);
lcd_writeChar(0b10101);
lcd_writeChar(0b00000);

lcd_locate(2,0);
lcd_writeString("RP6 ROBOT SYSTEM");
lcd_locate(0,2);
lcd_writeString("Bitte Taste druecken");
/* lcd_locate(2,3);
for(i=0; i<8;i++)
{
lcd_writeChar('0'+i);
lcd_writeChar(i);
}
*/
delay_ms(2000);
while(1){
if(lcd_getkeys())
{
i=lcd_getkeys();
while(lcd_getkeys());
delay_ms(10);
while(lcd_getkeys());
lcd_locate(0,3);
lcd_writeString(" ");
if(i & 16) lcd_locate(4,3);
if(i & 8) lcd_locate(7,3);
if(i & 4) lcd_locate(11,3);
if(i & 2) lcd_locate(14,3);
if(i & 1) lcd_locate(18,3);
lcd_writeChar(0);
i=0;
}
}
return 0;
}

Viel Spaß mit wenig Aufwand :)

Gruß

mic

radbruch
23.07.2008, 14:25
Nochmal hallo

Nun bin ich wirklich begeistert, denn ich habe einen kleinen Bug beseitigt (RS war nach dem Einlesen der Tasten nicht richtig gesetzt) und kann nun jederzeit zwischen der Ausgabe auf das Display die Tasten drücken/abfragen:


...
while(1)
{
lcd_locate(7,3);
lcd_writeInteger(128+lcd_getkeys(), 2);
}
...
(Bit7 dient der Formatierung des Ausgabestrings)

Das sieht dann so aus:
http://i.ytimg.com/vi/Gz7zc7He9F8/2.jpg (http://www.youtube.com/watch?v=Gz7zc7He9F8)
http://www.youtube.com/watch?v=Gz7zc7He9F8

Es wundert mich etwas dass ich über diese zweifache Verwendung der LCD-Leitungen noch nichts gelesen habe...

Gruß

mic

radbruch
09.11.2010, 21:11
Hallo

Endlich habe ich mal Zeit um mich mit dem PCF8574 als I2C-Porterweiterung zu beschäftigen. Nach einem guten Start mit der 4x4-Tastatur (https://www.roboternetz.de/phpBB2/viewtopic.php?p=525175#525175) habe ich mir nun eine LCD-Ansteuerung vorgenommen. Das sieht dann so aus:

http://i1.ytimg.com/vi/tD82ZbdWSwo/2.jpg (http://www.youtube.com/watch?v=tD82ZbdWSwo) http://i4.ytimg.com/vi/gx4wFKOHa-g/1.jpg (http://www.youtube.com/watch?v=gx4wFKOHa-g) http://radbruch.bplaced.net/robot/arm/lcd-mit-PCF8574A_klein.jpg (http://radbruch.bplaced.net/robot/arm/lcd-mit-PCF8574A.jpg)
http://www.youtube.com/watch?v=tD82ZbdWSwo
http://www.youtube.com/watch?v=gx4wFKOHa-g

Die Hardware ist quasi Standard. Das LCD wird im 4-Bit-Modus nur schreibend angeschlossen. Die Datenbits 4 bis 7 gehen auf den Port 4 bis 7, Register select (RS), read/write (RW) und enable (E) auf Port 0 bis 2. Port 3 ist frei für zweites E oder Backlight-LED. (Das ist nicht die Belegung aus dem Wiki!)

Nun zur Software. Da der RP6 für seine Base keine LCD-Funktionen zur Verfügung stellt, habe ich mich bei der Library des m32 bedient. Das m32 verwendet allerdings eine parallele Schnittstelle zum LCD, deshalb habe ich die Ausgabefunktionen zum LCD durch die Funktionen der asuro-I 2 C-Erweiterung (http://www.asurowiki.de/pmwiki/pmwiki.php/Main/LCDErweiterung) ersetzt. Das alles habe ich letztlich in eine kleine Lib eingebaut:

RP6Base_I2C_PCF8574.h

// LCD und Keys für die RP6-Base über I2C. 8.11.2010 mic

// An die Base angepasste m32-Library

#ifndef RP6Base_I2C_PCF8574_H
#define RP6Base_I2C_PCF8574_H

#include "RP6Base_I2C_PCF8574.h"
#include "RP6I2CmasterTWI.h"

#include <avr/pgmspace.h> // Program memory (=Flash ROM) access routines.
#include <avr/io.h> // I/O Port definitions
#include <stdint.h> // Standard Integer definitions
#include <stdlib.h> // C standard functions (e.g. itoa...)
#include <string.h>
#include <avr/interrupt.h> // Signal and other Interrupt macros

// Assembly and system macros:
#define nop() asm volatile("nop\n\t")
#define sysSleep() asm volatile("sleep\n\t")

////// PCF8574p Addressen und Pins (Defines aus asurowiki und asuro-Buch Band 2))

#define pcf8574_address_KEYS 0b01110000 // I2C Adresse des PCF8574 KEYS
#define pcf8574_address_LCD 0b01110010 // I2C Adresse des PCF8574 LCD


#define RS_bit 0 //Register Select Bit ( high for data, low for command)
#define RW_bit 1 //H: Read / L: Write
#define E_bit 2 //Enable Bit
#define LED_bit 3 //H: LED Off / L: LED on

#define LD4 4 // Pin to Data Bus 4
#define LD5 5 // Pin to Data Bus 5
#define LD6 6 // Pin to Data Bus 6
#define LD7 7 // Pin to Data Bus 7

/************************************************** ***************************/
// LCD:

void setLCDD(uint8_t lcdd);
void write4BitLCDData(uint8_t data);
void writeLCDCommand(uint8_t cmd);
void initLCD(void);

void clearLCD(void);
void clearPosLCD(uint8_t line, uint8_t pos, uint8_t length);
void writeCharLCD(uint8_t ch);
#define writeStringLCD_P(__pstr) writeNStringLCD_P((PSTR(__pstr)))
void writeStringLengthLCD(char *string, uint8_t length, uint8_t offset);
void writeStringLCD(char *string);
void writeNStringLCD_P(const char *pstring);

void _showScreenLCD_P(const char *line1, const char *line2);

#define showScreenLCD(__line1,__line2); ({_showScreenLCD_P((PSTR(__line1)),(PSTR(__line2)) );})

#ifndef HEX
#define HEX 16
#endif
#ifndef DEC
#define DEC 10
#endif
#ifndef OCT
#define OCT 8
#endif
#ifndef BIN
#define BIN 2
#endif
void writeIntegerLCD(int16_t number, uint8_t base);
void writeIntegerLengthLCD(int16_t number, uint8_t base, uint8_t length);

void setCursorPosLCD(uint8_t line, uint8_t pos);


/************************************************** ***************************/
// Keys:

uint16_t PollSwitch16(void); // Liest 4x4-Tastenmatrix des RA2 per I2C
uint8_t getPressedKeyNumber(void);
uint8_t getKeyNumber(uint16_t mask); // prüft welche Taste gedrückt wurde
uint8_t checkPressedKeyEvent(void);
uint8_t checkReleasedKeyEvent(void);

#endif



RP6Base_I2C_PCF8574.c

#include "RP6RobotBaseLib.h"
#include "RP6Base_I2C_PCF8574.h"
#include "RP6I2CmasterTWI.h"

char lcd_tmp_buffer[17];

// ************************************************** ***************************
// Low Level Functions:
// Aus dem asuro-Buch Band 2
// ************************************************** ***************************

// Sendet ein Halbbyte (4 Bit) mit Enableflanke an das Display.
void setLCDD(uint8_t lcdd)
{
I2CTWI_transmitByte(pcf8574_address_LCD, lcdd); //Daten anlegen
I2CTWI_transmitByte(pcf8574_address_LCD, (lcdd|(1<<E_bit))); //Enable_bit setzen
delayCycles(500);
I2CTWI_transmitByte(pcf8574_address_LCD, lcdd); //Enable_bit löschen
delayCycles(500);
}

// Sendet Steuerbyte, getrennt in zwei Halbbytes
void writeLCDCommand(unsigned char byte)
{
setLCDD(byte&0xF0); // sende oberes Halbbyte
setLCDD(byte<<4); // sende unteres Halbbyte
}

// Sendet Datenbyte, getrennt in zwei Halbbytes
void write4BitLCDData(unsigned char byte)
{
setLCDD((byte&0xF0)|(1<<RS_bit)); // sende oberes Halbbyte, dabei RS_bit gesetzt
setLCDD((byte<<4) |(1<<RS_bit)); // sende unteres Halbbyte, dabei RS_bit gesetzt
}

// Initialize the LCD. Always call this before using the LCD!
void initLCD(void)
{
//delayCycles(34000); No need for Power ON delay as usually the
// Bootloader should have been executed before...
setLCDD(0b0011);
delayCycles(18000);
setLCDD(0b0011);
delayCycles(5500);
setLCDD(0b0011);
delayCycles(5500);
setLCDD(0b0010);
delayCycles(5500);
writeLCDCommand(0b00101000);
delayCycles(5500);
writeLCDCommand(0b00001000);
delayCycles(5500);
writeLCDCommand(0b00000001);
delayCycles(5500);
writeLCDCommand(0b00000010);
delayCycles(5500);
writeLCDCommand(0b00001100);
delayCycles(5500);
}

// ************************************************** ***************************

// Diese m32-Funktionen wurden durch die entsprechenden Funktionen
// aus der asuro LCD-Library ersetzt:
/*

void setLCDD(uint8_t lcdd)
{
externalPort.LCDD = lcdd;
outputExt();
PORTB |= LCD_EN;
delayCycles(50);
PORTB &= ~LCD_EN;
}

void write4BitLCDData(uint8_t data)
{
setLCDD(data >> 4);
setLCDD(data);
delayCycles(150);
}

void writeLCDCommand(uint8_t cmd)
{
PORTB &= ~LCD_RS;
write4BitLCDData(cmd);
delayCycles(150);
}
************************************************** *****************************/

// Clears the whole LCD!
void clearLCD(void)
{
writeLCDCommand(0b00000001);
delayCycles(5500);
}


/**
* Write a single character to the LCD.
*
* Example:
*
* writeCharLCD('R');
* writeCharLCD('P');
* writeCharLCD('6');
* writeCharLCD(' ');
* writeCharLCD('0');
* writeCharLCD(48); // 48 is ASCII code for '0'
* writeCharLCD(49); // '1'
* writeCharLCD(50); // '2'
* writeCharLCD(51); // '3'
* //...
*
* would output:
* RP6 00123
* at the current cursor position!
* use setCursorPos function to move the cursor to a
* different location!
*/
void writeCharLCD(uint8_t ch)
{
write4BitLCDData(ch);
delayCycles(50); // ??? 8.11.2010 mic
}

/**
* Writes a null terminated string from flash program memory to the LCD.
* You can use the macro writeStringLCD_P(STRING); instead, this macro
* ensures that the String is stored in program memory only!
*
* Example:
*
* writeNStringLCD_P(PSTR("RP6 Control"));
*
* // There is also a Macro that makes life easier and
* // you can simply write:
* writeStringLCD_P("RP6 Control");
*
*/
void writeNStringLCD_P(const char *pstring)
{
uint8_t c;
for (;(c = pgm_read_byte_near(pstring++));writeCharLCD(c));
}

/**
* Writes a String from SRAM to the LCD.
*/
void writeStringLCD(char *string)
{
while(*string)
writeCharLCD(*string++);
}

/**
* Writes a string with specified length and offset from SRAM to the LCD.
* If it is a null terminated string, output will be stopped at the
* end. It does not need to be null terminated, but it is recommended
* to use only null terminated strings/buffers, otherwise the function could
* output any SRAM memory data stored after the string until it reaches a 0
* or the specified length!
*
* Example:
*
* writeStringLength("RP6 Robot Sytem",16,0);
* // would output: "RP6 Robot Sytem\n"
* writeStringLength("RP6 Robot Sytem",11,4);
* // would output: "Robot System"
* writeStringLength("RP6 Robot Sytem",40,4);
* // would output: "Robot System"
* // No matter if the specified length is 40 characters!
*
*/
void writeStringLengthLCD(char *string, uint8_t length, uint8_t offset)
{
for(string = &string[offset]; *string && length; length--)
writeCharLCD(*string++);
}

/**
* Write a number (with specified base) to the LCD.
*
* Example:
*
* // Write a hexadecimal number to the LCD:
* writeInteger(0xAACC,16);
* // Instead of 16 you can also write "HEX" as this is defined in the
* // RP6RobotBaseLib.h :
* writeInteger(0xAACC, HEX);
* // Other Formats:
* writeInteger(1024,DEC); // Decimal
* writeInteger(511,OCT); // Ocal
* writeInteger(0b11010111,BIN); // Binary
*/
void writeIntegerLCD(int16_t number, uint8_t base)
{
itoa(number, &lcd_tmp_buffer[0], base);
writeStringLCD(&lcd_tmp_buffer[0]);
}

/**
* Same as writeInteger, but with defined length.
* This means this routine will add leading zeros to the number if length is
* larger than the actual value or cut the upper digits if length is smaller
* than the actual value.
*
* Example:
*
* // Write a hexadecimal number to the LCD:
* writeIntegerLength(0xAACC, 16, 8);
* // Instead of 16 you can also write "HEX" as this is defined in the
* // RP6ControlLib.h :
* writeIntegerLength(0xAACC, HEX, 8);
* // Other Formats:
* writeIntegerLength(1024,DEC,6); // Decimal
* writeIntegerLength(511,OCT,4); // Ocal
* writeIntegerLength(0b11010111,BIN,8); // Binary
*/

void writeIntegerLengthLCD(int16_t number, uint8_t base, uint8_t length)
{
char buffer[17];
itoa(number, &buffer[0], base);
int8_t cnt = length - strlen(buffer);
if(cnt > 0) {
for(; cnt > 0; cnt--, writeCharLCD('0'));
writeStringLCD(&buffer[0]);
}
else
writeStringLengthLCD(&buffer[0],length,-cnt);
}

/**
* This function is useful for displaying text screens on the LCD.
* It clears the whole LCD and writes the two Strings to line 1 and
* line 2.
*/
void _showScreenLCD_P(const char *line1, const char *line2)
{
clearLCD();
writeNStringLCD_P(line1);
setCursorPosLCD(1, 0);
writeNStringLCD_P(line2);
}

/**
* Sets the cursor position on LCD.
*/
void setCursorPosLCD(uint8_t line, uint8_t pos)
{
pos |= 128;
if(line==1) pos += 0x40;
writeLCDCommand(pos);
}

/**
* Clears some characters after the given position.
*/
void clearPosLCD(uint8_t line, uint8_t pos, uint8_t length)
{
setCursorPosLCD(line,pos);
while(length--)
writeCharLCD(' ');
}

/************************************************** ***************************/
// Keypad:

uint16_t PollSwitch16(void)
{
uint8_t i, j, x, temp;
uint16_t mask=0;

temp=I2CTWI_readByte(0b01110001); // aktuelles Bitmuster sichern
for(j=0; j<4; j++)
{
I2CTWI_transmitByte(0b01110000, ~(1<<j)); // Pin j (0 bis 3) auf Low
sleep(2); // Warten auf Pegel
x=(I2CTWI_readByte(0b01110001)>>4); // Eingang 7-4 wird zu Bit 3-0 in x
for(i=0; i<4; i++) // Bits 0-3 überprüfen
if(!(x & (1<<i))) // Wenn Eingang low ist wurde die Taste gedrückt
mask += (1 << (i*4)) * (1<<j); // entsprechendes Bit in Maske setzen
}
I2CTWI_transmitByte(0b01110000, temp); // gespeichertes Bitmuster ausgeben
return(mask);
}
/**
* Checks which key is pressed - returns the key number,
* or 0, if no key is pressed.
*
*/
uint8_t getPressedKeyNumber(void)
{
int i, mask = PollSwitch16();

for(i=0; i<16; i++) // wurde eine Taste gedrückt?
{
if(mask & (1<<i)) // Taste i gedrückt?
{
if(mask & ~(1<<i))
return(0); // mehrere Tasten gleichzeitig gedrückt!
else
return(i+1); // Tastennummer zurückgeben
}
}
return(0); // keine Taste gedrückt
}

uint8_t getKeyNumber(uint16_t mask)
{
int i;

for(i=0; i<16; i++) // wurde eine Taste gedrückt?
{
if(mask & (1<<i)) // Taste i gedrückt?
{
if(mask & ~(1<<i))
return(0); // mehrere Tasten gleichzeitig gedrückt!
else
return(i+1); // Tastennummer zurückgeben
}
}
return(0); // keine Taste gedrückt
}

/**
* This function has to be called frequently out of
* the main loop and checks if a button is pressed! It only returns
* the key number a single time, DIRECTLY when the button is pressed.
*
* This is useful for non-blocking keyboard check in the
* main loop. You don't need something like
* "while(getPressedKeyNumber());" to wait for the button
* to be released again!
*/
uint8_t checkPressedKeyEvent(void)
{
static uint8_t pressed_key = 0;
if(pressed_key) {
if(!getPressedKeyNumber())
pressed_key = 0;
}
else {
pressed_key = getPressedKeyNumber();
if(pressed_key)
return pressed_key;
}
return 0;
}

/**
* This function has to be called frequently out of
* the main loop and checks if a button is pressed AND
* released. It only returns the key number a single time,
* AFTER the button has been released.
*
* This is useful for non-blocking keyboard check in the
* main loop. You don't need something like
* "while(getPressedKeyNumber());" to wait for the button
* to be released again!
*/
uint8_t checkReleasedKeyEvent(void)
{
static uint8_t released_key = 0;
if(released_key) {
if(!getPressedKeyNumber()) {
uint8_t tmp = released_key;
released_key = 0;
return tmp;
}
}
else
released_key = getPressedKeyNumber();
return 0;
}


Diese Lib ist verträglich mit der Lib der Base und stellt zusätzlich die LCD- und Tastenfunktionen des m32 über I2C zur Verfügung. Die Videos oben wurde mit diesem Democode erzeugt:

// Base mit PCF8574 für 4x4-Tastenmatrix und LCD 8.11.2010 mic

// Aus den Libraries der Base, des m32 und der LCD-Library des asuro aus Band 2
// des asuro-Buches habe ich eine Tasten- und LCD-Library für die Base mit I2C
// und PCF8574 zusammengebastelt.

// Die auf das parallele m32-Keypad und das LCD zugreifenden Funktionen habe ich
// durch die entsprechenden PCF8574-Funktionen der asuro-Library ersetzt.

// PCF8574 allgemein:
// https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=56693
// http://www.asurowiki.de/pmwiki/pmwiki.php/Main/LCDErweiterung

// PCF8574 mit 4x4-Tastenmatrix:
// https://www.roboternetz.de/phpBB2/viewtopic.php?p=525175#525175

// PCF8574 mit LCD:
// http://www.rn-wissen.de/index.php/LCD-Modul_am_AVR#Ansteuerung_.C3.BCber_I.C2.B2C
// https://www.roboternetz.de/phpBB2/viewtopic.php?t=22643

// I2C-Bibliothek von Peter Fleury (die hier nicht verwendet wird!):
// http://jump.to/fleury

// Tasten und LCD mit Bascom:
// https://www.roboternetz.de/phpBB2/viewtopic.php?t=11188

// Mein RP6 mit LCD an den LEDs:
// https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=41805

#include "RP6RobotBaseLib.h" // Library der Base
#include "RP6Config.h" // Konfiguration der Base
#include "RP6I2CmasterTWI.h" // I2C-Funktionen der Base
#include "RP6Base_I2C_PCF8574.h" // meine neue PCF8574-Library

uint8_t c=0, leds=0;
uint16_t tasten;

int main(void)
{
initRobotBase();
I2CTWI_initMaster(100);
setLEDs(63);
initLCD();

showScreenLCD("################", "################");
mSleep(1500);
showScreenLCD("<<RP6 mit I2C> >", "<<LC - DISPLAY>>");
mSleep(2500);
showScreenLCD("Hello World", "Example Program");
mSleep(2500);
clearLCD(); // Clear the whole LCD Screen
mSleep(1500);
showScreenLCD("Bitte Taste", "druecken");

startStopwatch1();
while(1)
{
if(getStopwatch1() > 50) // Tastenabfrage
{
setStopwatch1(0);
tasten=PollSwitch16();
if(tasten)
{
writeInteger(tasten, 2); // Bitmuster des Keypads an Terminal ausgeben
writeChar('\n');

setCursorPosLCD(0,0);
writeIntegerLengthLCD(tasten, 2, 16);
setCursorPosLCD(1,0);
writeStringLCD_P("Taste Nr.: ");
writeIntegerLCD(getKeyNumber(tasten), 10);
writeCharLCD(' ');

leds=0; // Spielerei mit den Leds
if(tasten & (1<<0)) leds+=8; // Taste 1
if(tasten & (1<<1)) leds+=1;
if(tasten & (1<<2)) leds+=16;
if(tasten & (1<<3)) leds+=2;
if(tasten & (1<<4)) leds+=32;
if(tasten & (1<<5)) leds+=4; // Taste 6
setLEDs(leds);
}
else setLEDs(0); // Keine Taste gedrückt
}
}
return(0);
}


Das sieht zwar schon mal ganz gut aus, hat aber auch noch ein paar Macken. Das LCD wird nur bei jedem 3. bis 5. Versuch richtig initialisiert. Dieses Problem hatte ich auch, wenn das LCD direkt parallel angeschlossen ist. Ich vermute, dieser Effekt hängt mit der 4Bit-Ansteuerung zusammen.

Nach vielen Versuchen habe ich nun erkannt, dass die PullUps auf der Base wohl zu groß sind. Seit ich zusätzliche 1k-PullUps verwende klappt die Kommunikation relativ gut.

Gruß

mic