-         

Ergebnis 1 bis 6 von 6

Thema: LCD an RP6-Base

  1. #1
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    54
    Beiträge
    5.783
    Blog-Einträge
    8

    LCD an RP6-Base

    Anzeige

    Hallo

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




    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:

    Code:
    // 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:
    http://www.roboternetz.de/wissen/ind...D-Modul_am_AVR
    http://www.mikrocontroller.net/artic...-Tutorial:_LCD
    und dem Datenblatt des Displays


    Noch ein Filmchen dazu:

    http://www.youtube.com/watch?v=2UtIABxSUXs

    Gruß

    mic

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  2. #2
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    09.04.2008
    Beiträge
    375
    Sieht gans nett aus. Braucht das Diplay auch eine Initialisierung ? Und welche lib ist rblib.c ?

  3. #3
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    54
    Beiträge
    5.783
    Blog-Einträge
    8
    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 ;):
    http://www.roboternetz.de/phpBB2/zei...=329255#329255

    Gruß

    mic

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  4. #4
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    54
    Beiträge
    5.783
    Blog-Einträge
    8
    Hallo

    Jetzt habe ich noch eine kleine Erweiterung angebaut:



    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://www.youtube.com/watch?v=ieltub967Jo

    Der Code (Tastenabfrage und Usergraphic):
    Code:
    // 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

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  5. #5
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    54
    Beiträge
    5.783
    Blog-Einträge
    8
    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:

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

    Das sieht dann so aus:

    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

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  6. #6
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    54
    Beiträge
    5.783
    Blog-Einträge
    8
    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 habe ich mir nun eine LCD-Ansteuerung vorgenommen. Das sieht dann so aus:


    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 ersetzt. Das alles habe ich letztlich in eine kleine Lib eingebaut:

    RP6Base_I2C_PCF8574.h
    Code:
    // 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
    Code:
    #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:
    Code:
    // 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:
    // http://www.roboternetz.de/phpBB2/zei...ag.php?t=56693
    // http://www.asurowiki.de/pmwiki/pmwik...LCDErweiterung
    
    // PCF8574 mit 4x4-Tastenmatrix:
    // http://www.roboternetz.de/phpBB2/vie...=525175#525175
    
    // PCF8574 mit LCD:
    // http://www.rn-wissen.de/index.php/LC...BCber_I.C2.B2C
    // http://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:
    // http://www.roboternetz.de/phpBB2/viewtopic.php?t=11188
    
    // Mein RP6 mit LCD an den LEDs:
    // http://www.roboternetz.de/phpBB2/zei...ag.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

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

Stichworte

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •