PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] HD44780 kompatibles LCD im 4-bit-mode, Busy Flag abfragen



crowdy
11.07.2013, 17:38
Hi Leute,
derzeit hänge ich mal wieder an einem nervigen kleinen Problemchen... Ich versuche mir ne Lib für mein LCD-Board (LCD 204B LED) aufzubauen, vermutlich für den Kontroller KS0073 (Das LCD war Teil eines Lern-Experimentier-Boards und ich habe keine gute Dokumentation, bei der ich mir 100% sicher bin, dass sie passt). Eigentlich funktioniert auch alles gut, bis auf das Auslesen des Speichers. Dies brauche ich eigentlich nur um das Busy Flag auszulesen. Wenn ich auf die BF Abfrage verzichte und stattdessen eine kleine Wartezeit einbauen funktioniert auch alles einwandfrei. Aber ich möchte umbedingt den Fehler finden, alles andere befriedigt mich nicht.

Jaaa, betrieben wird das LCD im 4-Bit Modus, die unteren Datenleitungen (DB0-DB3) habe ich Offen gelassen, da das LCD wohl interne Pull-Up's verwendet. Ansonsten ist alles direkt mit möglichst kurzen Leitungen auf meinem Experimentierboard mit einem ATmega 8 angeschlossen.

Also das Problem vermute ich in dieser Funktion

void lcd_busy(void)
{ uint8_t rep;

P_DD_DDR &= (~(0x0F << DD)); //Data-Port as Input
SET_DD(0x00); //No internal Pull-Up's!

RS_0; //Instruction Register
RW_1; //Read-Mode
_delay_us( t_W_RS_RW );

do
{
#ifdef _4_bit_mode_

EN_1;
_delay_us( t_H_EN ); //Wait for LCD: 5us

rep = ( (P_DD_IN >> DD) << 4 ); //Read high nibble
EN_0;
_delay_us( t_L_EN ); //Wait for LCD: 5us

EN_1;
_delay_us( t_H_EN );

rep |= ( (P_DD_IN >> DD) & 0x0F); //Read low nibble
EN_0;
_delay_us( t_L_EN );

#else
EN_1;
_delay_us( t_H_EN);

rep = P_DD_IN;
EN_0;
_delay_us( t_L_EN );

#endif

}while( 0x80 & rep ); //Check Busy Flag

RW_0;
}
Die Konstanten habe ich hier nochmal einzeln

#define F_CPU 8000000L
#include <avr/io.h>
#include <util/delay.h>
#include "uart.h"

/////////////////////////////////////////////////////////////////Mode////////////////////////////////////////////
#define _4_bit_mode_

/////////////////////////////////////////////////////////////////Port Constants//////////////////////////////////
#define RS 7
#define P_RS PORTD
#define P_RS_DDR DDRD

#define RW 0
#define P_RW PORTB
#define P_RW_DDR DDRB

#define EN 6
#define P_EN PORTD
#define P_EN_DDR DDRD

#define DD 2
#define P_DD PORTD
#define P_DD_IN PIND
#define P_DD_DDR DDRD

/////////////////////////////////////////////////////////////////Time delays/////////////////////////////////////////////

///us
#define t_H_EN 5
#define t_L_EN 5
#define t_W_RS_RW 1
///ms
#define t_Init_1 100
#define t_Init_2 5

/////////////////////////////////////////////////////////////////Commands for Bus/////////////////////////////////////////
#define RS_1 P_RS |= _BV(RS)
#define RS_0 P_RS &= ~_BV(RS)

#define RW_1 P_RW |= _BV(RW)
#define RW_0 P_RW &= ~_BV(RW)

#define EN_1 P_EN |= _BV(EN)
#define EN_0 P_EN &= ~_BV(EN)

/////////////////////////////////////////////////////////////////Commands and Addresses///////////////////////////////////
#define CLEAR_SCREEN 0x01
#define RETURN_HOME 0x02
#define ENTRY_MODE 0x04
#define ID 0x02
#define S 0x01
#define DISPLAY_CON 0x08
#define D 0x04
#define C 0x02
#define B 0x01
#define SHIFTING 0x10
#define SC 0x08
#define RL 0x04
#define FUNCTION_SET 0x20
#define DL 0x10
#define N 0x08
#define F 0x04
#define CG_ADD_SET 0x40
#define DD_ADD_SET 0x80
#define Line_1 0x00
#define Line_2 0x40
#define Line_3 0x14
#define Line_4 0x54

#ifdef _4_bit_mode_
#define SET_DD(x) P_DD &= ~(0x0F << DD);\
P_DD |= ((x) << DD)
#else
#define SET_DD(x) P_DD = (x)
#endif

Und Sicherheitshalber hier nochmal die ganze Lib, an der ich jetzt arbeite

/////////////////////////////////////////////////////////////////Display Functions////////////////////////////////////////
void delay_ms(uint16_t ms)
{
for(uint16_t t = 0; t <= ms; t++) _delay_ms(1);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Set_P_DD( uint8_t c)
{
EN_1;

#ifdef _4_bit_mode_
SET_DD( c >> 4 ); //Put high Nibble on Pin's
_delay_us( t_H_EN );

EN_0; //LCD reads
_delay_us(t_L_EN);

EN_1;
SET_DD( 0x0F & c ); //Put low nibble on Pin's
#else
P_DD = c;
#endif

_delay_us( t_H_EN );

EN_0;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void lcd_busy(void)
{ uint8_t rep;

P_DD_DDR &= (~(0x0F << DD)); //Data-Port as Input
SET_DD(0x00); //No internal Pull-Up's!

RS_0; //Instruction Register
RW_1; //Read-Mode
_delay_us( t_W_RS_RW );

do
{
#ifdef _4_bit_mode_

EN_1;
_delay_us( t_H_EN ); //Wait for LCD: 5us

rep = ( (P_DD_IN >> DD) << 4 ); //Read high nibble
EN_0;
_delay_us( t_L_EN ); //Wait for LCD: 5us

EN_1;
_delay_us( t_H_EN );

rep |= ( (P_DD_IN >> DD) & 0x0F); //Read low nibble
EN_0;
_delay_us( t_L_EN );

#else
EN_1;
_delay_us( t_H_EN);

rep = P_DD_IN;
EN_0;
_delay_us( t_L_EN );

#endif

}while( 0x80 & rep ); //Check Busy Flag

RW_0;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void lcd_cmd( uint8_t cmd )
{
delay_ms(1); //eigentlich lcd_busy();

RS_0;
RW_0;
_delay_us(t_W_RS_RW);

Set_P_DD( cmd );

RS_1;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void lcd_put( uint8_t dd )
{
delay_ms(1); //eigentlich lcd_busy();

Set_P_DD( dd );
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void lcd_init( void )
{ uint8_t i;

P_RS_DDR |= (1 << RS); //as Output
P_RW_DDR |= (1 << RW);
P_EN_DDR |= (1 << EN);

#ifdef _4_bit_mode_
P_DD_DDR |= (0x0F << DD); //as Output
#else
P_DD_DDR = 0xFF;
#endif

RS_0;
RW_0;

delay_ms(t_Init_1);

#ifdef _4_bit_mode_
SET_DD( (FUNCTION_SET + DL) >> 4 );
#else
SET_DD( FUNCTION_SET + DL );
#endif

for( i=0; i<=2; i++) //3x Function setting
{
EN_1;
_delay_us( t_H_EN );
EN_0;
_delay_us( t_L_EN );
delay_ms(t_Init_2);
}

lcd_cmd( FUNCTION_SET + N ); //2/4 line display
lcd_cmd( DISPLAY_CON + D + C + B ); //Display:ON, Cursor:ON, Blink: ON
lcd_cmd( ENTRY_MODE + ID ); //Increment Cursor
lcd_cmd( CLEAR_SCREEN );
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void lcd_write( const char *dd )
{
while( *dd != '\0' ) lcd_put( *dd++ );
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void lcd_pos( uint8_t x, uint8_t y )
{
uint8_t add;

switch (y)
{ case 1: add = DD_ADD_SET + Line_1 + x;
break;
case 2: add = DD_ADD_SET + Line_2 + x;
break;
case 3: add = DD_ADD_SET + Line_3 + x;
break;
case 4: add = DD_ADD_SET + Line_4 + x;
break;
default: return;
}

lcd_cmd( add );
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void lcd_put_bin( uint8_t c )
{
for(uint8_t j=0x80; j>= 0x01; j>>=1 )
{
if( j == 0x08 ) lcd_put( 0x7C );
if( c & j ) lcd_put( 0xFF );
else lcd_put( 0x20 );
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void lcd_CG ( uint8_t add, uint8_t *c)
{
add |= CG_ADD_SET;

lcd_cmd( add );

for (uint8_t i = 0; i<=7; i++) lcd_cmd( c[i] );

}

So ich bin für jeden Tipp und jede Anregung sehr dankbar. Außerdem würde mich es mal interessieren wie Ihr die Lesbarkeit des Codes findet.

Mit freundlichen Grüßen Crowdy

crowdy
18.07.2013, 13:37
Hey,

hat ne ganze Weile gedauert, aber jetzt ist mir der Fehler aufgefallen. Eigentlich ne ganz einfache Sache und zwar habe ich vergessen das DDR für den Port nach dem Lesemodus wieder auf Output zu stellen. Also die busys Funktion sieht nun so aus:

void lcd_busy(void)
{ uint8_t rep;

#ifdef _4_bit_mode_
P_DD_DDR &= (~(0x0F << DD)); //Data-Port as Input
#else
P_DD_DDR = 0x00;
#endif

SET_DD(0x0F); //internal Pull-Up's!

RS_0; //Instruction Register
RW_1; //Read-Mode

_delay_us( t_W_RS_RW );

do
{
#ifdef _4_bit_mode_

EN_1;
_delay_us( t_H_EN ); //Wait for LCD: 5us

rep = ( (P_DD_IN >> DD) << 4 ); //Read high nibble
EN_0;
_delay_us( t_L_EN ); //Wait for LCD: 5us

EN_1;
_delay_us( t_H_EN );

rep |= ( (P_DD_IN >> DD) & 0x0F); //Read low nibble
EN_0;
_delay_us( t_L_EN );

#else
EN_1;
_delay_us( t_H_EN);

rep = P_DD_IN;
EN_0;
_delay_us( t_L_EN );

#endif

}while( 0x80 & rep ); //Check Busy Flag

#ifdef _4_bit_mode_
P_DD_DDR |= (0x0F << DD); //Data-Port as Input
#else
P_DD_DDR = 0xFF;
#endif

RW_0;
RS_1;
}

Leider habe ich seit dem das Problem gelöst ist, wohl ein Neues dazubekommen. Irgendwie führt der µC nicht mehr so zuverlässig das Programm aus, manchamal schreibt er gar nichts aufs Display ( der Cursor fehlt dann auch), manchmal funktioniert alles einwandfrei und manchmal vertut er sich mit den Adressen des DDRAM und schreibt die Buchstaben an falsche Stellen. Ich habe im Moment keine Ahnung woran das liegen könnte. Hat jemand ein Idee? Könnte ich einen Fehler im C-Programm ausschließen, denn manchmal funktioniert der Mist ja??

Hier nochmal der Testcode, der sich auf die schon gepostete Lib bezieht:



#include <avr/io.h>
#include "HD44780.h"
#include "uart.h"

int main(void)
{

DDRC |= 0x3F;
PORTC &= ~0x3F;

lcd_init();

lcd_write("LCD-Testing");

lcd_pos(3,2);
lcd_write("Welche Zeile?");

lcd_pos(0,4);
lcd_write("Zeile 4?");

delay_ms(1000);
PORTC |= 0x3F;
while(1)
{
}
}


Also ich bin schon wieder ratlos und deshalb um jede Hilfe oder Denkanstoß dankbar!

RoboHolIC
18.07.2013, 22:25
Fremden Source verstehend zu lesen macht mir offengestanden zu viel Mühe (andere hier sind diesbezüglich begabter). Daher die stumpfe Frage:
Hast du eventuell die busy-Flag-Abfrage auch schon bei den Schreibvorgängen zur Initialisierung verwendet?
Dort sind i.d.R. Mindestwartezeiten einzuhalten.

crowdy
21.07.2013, 15:23
Hi RoboHolIC,
danke für deine offene Antwort, hab ne weile gebraucht um das zu testen. Tatsächlich habe ich das BF zu früh abgefragt, aber da war das LCD noch recht tolerant. Es lag dann am Timing für das Enable-Signal, es war viel zu konservativ 15µs (ich wollte auf der sicheren Seite sein) nun habe ich es auf 3µs begrenzt und es funktioniert so weit einwandfrei.

RoboHolIC
21.07.2013, 21:36
. . . Timing für das Enable-Signal . . viel zu konservativ 15µs . . auf 3µs begrenzt und es funktioniert so weit einwandfrei.

Hmm, hmm .... komisch!
Ich habe das Datenblatt zum HD44780 studiert: Dort ist nur eine minimale High-Zeit für das Enable angegeben. D.h., es kann eigentlich gar nicht zu lang sein. Die Bus-Kompatibilität der sog. kompatiblen Matrixcontrollerchips ("KS<irgendwas>") setze ich jetzt einfach mal mutig voraus.