PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : LCD-Ansteuerung funktioniert nicht



Spurius
01.05.2006, 12:58
Hallo,
ich versuche gerade eine eigene LCD-Ansteuerung ohne Lib hinzukriegen, leider wird auf dem Display nicht angezeigt...



#include <inttypes.h>
#include <avr/io.h>
#include <util/delay.h>

#define LCD_RS_PORT PORTB
#define LCD_RS_PIN 3
#define LCD_DATA0_PORT PORTD /**< port for 4bit data bit 0 */
#define LCD_DATA1_PORT PORTD /**< port for 4bit data bit 1 */
#define LCD_DATA2_PORT PORTD /**< port for 4bit data bit 2 */
#define LCD_DATA3_PORT PORTB /**< port for 4bit data bit 3 */
#define LCD_DATA0_PIN 5 /**< pin for 4bit data bit 0 */
#define LCD_DATA1_PIN 6 /**< pin for 4bit data bit 1 */
#define LCD_DATA2_PIN 7 /**< pin for 4bit data bit 2 */
#define LCD_DATA3_PIN 0
#define LCD_RW_PORT PORTB /**< port for RW line */
#define LCD_RW_PIN 4 /**< pin for RW line */
#define LCD_E_PORT PORTB /**< port for Enable line */
#define LCD_E_PIN 2 /**< pin for Enable line */
#define LCD_E_PORT PORTB /**< port for Enable line */
#define LCD_E_PIN 1 /**< pin for Enable line */



void write_data(uint8_t wert)
{
if(wert & (1<<0))
{
LCD_DATA0_PORT &= ~(1 << LCD_DATA0_PIN);
}
else
{
LCD_DATA0_PORT |= (1 << LCD_DATA0_PIN);
}

if(wert & (1<<1))
{
LCD_DATA1_PORT &= ~(1 << LCD_DATA1_PIN);
}
else
{
LCD_DATA1_PORT |= (1 << LCD_DATA1_PIN);
}

if(wert & (1<<2))
{
LCD_DATA2_PORT &= ~(1 << LCD_DATA2_PIN);
}
else
{
LCD_DATA2_PORT |= (1 << LCD_DATA2_PIN);
}
if(wert & (1<<3))
{
LCD_DATA3_PORT &= ~(1 << LCD_DATA3_PIN);
}
else
{
LCD_DATA3_PORT |= (1 << LCD_DATA3_PIN);
}

}
void init_lcd(void)
{
_delay_ms(20);
LCD_RS_PORT &= ~(1<<LCD_RS_PIN); /* Steuerregister wird angesprochen */
_delay_ms(8);
write_data(0x3);
_delay_ms(1);
write_data(0x3);
LCD_RW_PORT |= (1<<LCD_RW_PIN); /*Lesezugriff, warten bis RW 0 */
DDRB = 0xfe; /*PB0 als Eingang, um Busyflag einzulesen*/
while(LCD_DATA3_PORT & (1<<LCD_DATA3_PIN));
LCD_RW_PORT &= ~(1<<LCD_RW_PIN);
DDRB = 0xff;
write_data(0x2);
LCD_RW_PORT |= ~(1<<LCD_RW_PIN);
DDRB = 0xfe;
while(LCD_DATA3_PORT & (1<<LCD_DATA3_PIN));
LCD_RW_PORT &= (1<<LCD_RW_PIN);
DDRB = 0xff;
write_data(0x2); /*System-Set oberes Nibble */
write_data(0x8); /*System-Set unteres Nibble */

write_data(0x0); /*Display on/off, Display on, Unterstrich Cursor on, blinkender Cursor on*/
write_data(0xF);

write_data(0x0); /* Cursor Home */
write_data(0x2);

LCD_RS_PORT |= (1<<LCD_RS_PIN); /*Datenregister auswählen*/
write_data(0x19);


}

void main(void)
{
DDRB = 0xFF;
DDRD = 0xFF;
init_lcd();
for(;;);
}

Ich habe versucht nach der Anleitung im PDF zu gehen, aber irgendwie habe ich nie Enable verwendet..

Gruß
Martin

izaseba
01.05.2006, 13:26
Ich habe versucht nach der Anleitung im PDF zu gehen, aber irgendwie habe ich nie Enable verwendet

O weh,

Eben, Enable ist dabei der "Auslöser"
Stell Dir mal vor Du liegst im Schützengraben, Deine Waffe ist geladen,gespannt und entsichert, Du Zielst auf denen Feind, und ....
...da fehlt noch was.
Du mußt den DataPort, RW und RS entsprechend belegen, und wenn das stimmt, den Enable Pin kurz auf High und dann wieder auf Low ziehen.
Bei der fallenden Flanke werden die Daten dann vom LCD übernommen.

Hoffe geholfen zu haben.

Gruß Sebastian

Spurius
01.05.2006, 13:52
Hallo,
ich hab jetzt versucht das umzusetzten, aber es geht immer noch nicht:


void init_lcd(void)
{

_delay_ms(20);
LCD_E_PORT |= (1<<LCD_E_PIN);
LCD_RS_PORT &= ~(1<<LCD_RS_PIN); /* Steuerregister wird angesprochen */
write_data(0x3); /* 1.0x3 */
LCD_E_PORT &= ~(1<<LCD_E_PIN);

_delay_ms(8);
LCD_E_PORT |= (1<<LCD_E_PIN);
write_data(0x3); /* 2.0x3 */
LCD_E_PORT &= ~(1<<LCD_E_PIN);

_delay_ms(1);
LCD_E_PORT |= (1<<LCD_E_PIN);
write_data(0x3); /* 3.0x3 */
LCD_E_PORT &= ~(1<<LCD_E_PIN);

LCD_RW_PORT |= (1<<LCD_RW_PIN); /*Lesezugriff, warten bis RW 0 */
DDRB = 0xfe; /*PB0 als Eingang, um Busyflag einzulesen*/
LCD_E_PORT |= (1<<LCD_E_PIN);
while(LCD_DATA3_PORT & (1<<LCD_DATA3_PIN)); /*Warten bis Busy-Flag = 0 */
LCD_RW_PORT &= ~(1<<LCD_RW_PIN); /*Schreibzugriff einstellen*/
DDRB = 0xff;
LCD_E_PORT &= ~(1<<LCD_E_PIN);

LCD_E_PORT |= (1<<LCD_E_PIN); /*System-Set*/
write_data(0x2);
LCD_E_PORT &= ~(1<<LCD_E_PIN);
LCD_E_PORT |= (1<<LCD_E_PIN);
write_data(0xC);
LCD_E_PORT &= ~(1<<LCD_E_PIN);


LCD_E_PORT |= (1<<LCD_E_PIN);
write_data(0x0); /*Display on/off, Display on, Unterstrich Cursor on, blinkender Cursor on*/
LCD_E_PORT &= ~(1<<LCD_E_PIN);
LCD_E_PORT |= (1<<LCD_E_PIN);
write_data(0xF);
LCD_E_PORT &= ~(1<<LCD_E_PIN);

LCD_E_PORT |= (1<<LCD_E_PIN);
write_data(0x0); /* Cursor Home */
LCD_E_PORT &= ~(1<<LCD_E_PIN);
LCD_E_PORT |= (1<<LCD_E_PIN);
write_data(0x2);
LCD_E_PORT &= ~(1<<LCD_E_PIN);

LCD_RS_PORT |= (1<<LCD_RS_PIN); /*Datenregister auswählen*/
write_data(0x8);
LCD_E_PORT &= ~(1<<LCD_E_PIN);

}

Das Display ist ein 4x27 mit 2 Controllern und es sind in der 1 und 3 Zeile die Balken zu sehen, was meiner Meinung nach darauf hindeutet, dass schon das System Set nicht funktioniert, da dort eigentlich mehr Zeilen eingestellt werden.

Spurius
01.05.2006, 16:35
Nochwas, kann es sein, dass das mit der Busyflag Abfrage nicht richtig hinhaut? Oder brauche ich die überhaupt? Bei anderen init-Routinen habe ich die nämlich nicht immer gesehen...

izaseba
01.05.2006, 16:54
Fang am besten ohne Busy an, nur delays, damit sparst Du Dir ein Pin UND kommst schneller ans Ziel.

und den Enable Puls erzeugst Du am besten in einer extra Funktion:


void enable(void){
LCD_E_PORT |= (1<<LCD_E_PIN);
__asm volatile ("nop");
__asm volatile ("nop");
__asm volatile ("nop");
__asm volatile ("nop");
__asm volatile ("nop");
LCD_E_PORT &= ~(1<<LCD_E_PIN);
}


Die Anzahl der nop's hängt vom Takt ab.

schau Dir diese (http://www.mikrocontroller.net/tutorial/lcd) Seite an.
Es ist zwar für Assembler, aber man kann sich schön angucken, wie die Ansteuerung funktioniert.

Gruß Sebastian

pebisoft
01.05.2006, 17:32
Stell Dir mal vor Du liegst im Schützengraben, Deine Waffe ist geladen,gespannt und entsichert, Du Zielst auf denen Feind, und ....
...da fehlt noch was.


..ja..ladehemmung...

Spurius
01.05.2006, 17:38
Hallo,
ich hab jetzt das enable in eine extra Routine verpackt und das Busy wegelassen, dafür mehr delays.


void init_lcd(void)
{
_delay_ms(20);

LCD_RS_PORT &= ~(1<<LCD_RS_PIN); /* Steuerregister wird angesprochen */
write_data(0x3); /* 1.0x3 */
lcd_enable;

_delay_ms(8);
write_data(0x3); /* 2.0x3 */
lcd_enable;

_delay_ms(1);
write_data(0x3); /* 3.0x3 */
lcd_enable;
_delay_ms(5);
/*System-Set 4bit...*/
write_data(0x2);
lcd_enable;
_delay_ms(5);
write_data(0x8); /* <-- Hier */
lcd_enable;
_delay_ms(5);

write_data(0x0); /*Display on/off, Display on, Unterstrich Cursor on, blinkender Cursor on*/
lcd_enable;
_delay_ms(5);
write_data(0xF);
lcd_enable;
_delay_ms(5);

write_data(0x0); /* Cursor Home */
lcd_enable;
_delay_ms(5);
write_data(0x2);
lcd_enable;
_delay_ms(5);

}

Eigentlich müsste laut dem geposteten DB der Cursor blinken, und zwar an der Home-Position.
Mache ich vielleicht mit der Übertragung von oberem und unterem Nibble was falsch?

An der Stelle wo im Code "Hier" steht übertrage ich z.B., nachdem 4-Bit eingestellt wird, das untere Nibble von SystemSet.
Wenn man einmal vergisst, zB. ein unteres Nibble zu übertragen, ist dann ab dem Zeitpunkt alles so verschoben, dass das nächste obere Nibble als das vergessene untere Nibble gezählt wird?

Gruß
Martin

izaseba
01.05.2006, 18:04
@pebi,
glaub mir, wenn ich Dich vor die Flinte kriegen würde, hätte ich keine Ladehemmungen...

@Martin,
Die Nibbles mußt Du schon komplett übertragen, allergings habe ich das noch nie so über die Pins verstreut gemacht...

So mach ich das z.B.


void lcd_data(uint8_t data) {
uint8_t temp = data;
warte(0x00C8);
RSPORT |=(1<<RS);
DATAPORT = (data>>4);
enable();
DATAPORT = temp;
enable();
RSPORT &= ~(1<<RS);

}
mit lcd_data werden beide Nibbles übertragen, und ich brauche mir keine Sorgen zu machen, daß ich die Hälfte vergesse.
Aber die Datapins liegen schön hintereinander auf einem Port .
Vielleicht kannst Du das irgendwie gebrauchen.

Gruß Sebastian

Spurius
01.05.2006, 19:04
Hallo Sebastian,
erstmal Danke dass du dich so bemühst!
Um meine Werte auf die verschiedenen Pins zu bekommen habe ich mir ja extra die Routine write_data geschrieben, mit der ich auch die gewünschten Portzustände bekomme.
Muss ihc nach den ertsen 3 0x3 jedesmal noch das untere Nibble übertragen, dass 0x0 ist?
Die 4-Bit stelle ich jetzt so ein:



write_data(0x2); /* Explizit 4-Bit einstellen */
lcd_enable;
_delay_ms(5);
write_data(0x0);
lcd_enable;
_delay_ms(5);
/*Nochmal System-Set 4bit...*/
write_data(0x2);
lcd_enable;
_delay_ms(5);
write_data(0x8); /* Anderes von System Set einstellen */
lcd_enable;
_delay_ms(5);

Die Verkabelung sollte auch stimmen, da es mit der Lib von Fleury geht, allerdings braucht der RW.

Spurius
03.05.2006, 17:26
Immer noch nicht gelöst...

VictimOfMyOwn
19.05.2006, 00:09
hi ho...

falls dein LCD immer noch net will ist hier mal ein ausschnitt aus meinem quelltext. das display wird über einen 4 bit bus betrieben.

die sendefunktionen:



void sendb(unsigned char datenh, unsigned char datenl) // Befehle senden
{
PORTC = datenh; // High - Nibble
PORTB |= 1 << E; // Enable 1
_delay_us(40);
PORTB &= ~1 << E; // Enable 0

PORTC = datenl; // Low - Nibble
PORTB |= 1 << E; // Enable 1
_delay_us(40);
PORTB &= ~1 << E; // Enable 0
}

void sendd(unsigned char datenh, unsigned char datenl) // Daten senden
{
PORTC = datenh + 16; // High - Nibble
PORTB |= 1 << E; // Enable 1
_delay_us(40);
PORTB &= ~1 << E; // Enable 0

PORTC = datenl + 16; // Low - Nibble
PORTB |= 1 << E; // Enable 1
_delay_us(40);
PORTB &= ~1 << E; // Enable 0
}


initialisierung des displays:



PORT C:

7 6 5 4 3 2 1 0
- - RW RS D7 D6 D5 D4 */

// 4 bit 2 zeilen 5x7 dots -> 00101000

sendb(0b0010, 0b1000);

// display on, cursor off, blink off -> 00001100

sendb(0b0000, 0b1100);

// Clear Display & Cursor Home

sendb(0b0000, 0b0001);


das "+ 16" beim daten senden bedeutet, daß die RS leitung auf "high" gezogen wird. (daten -> RS=1, befehle -> RS=0)

mfg

Spurius
19.05.2006, 19:02
Hi,
ich bin mittlerweile soweit, dass der Cursor blinkt, trotzdem danke!