Vlastna knižnica pre HD44780 - 4 bit mód a špecifické úpravy

Zdravim, skusam si vytvorit vlastne prikazy pre ovladanie znakoveho lcd 2x16 s radicom hd44780. Spravil som inicializaciu lcd a funkciu pre posielanie prikazu a dat. Vsetko ide OK, Problem je ze mi napis beha po celom lcd ked to dam do nekonecnej slucky. Skusal som aj s delay bolo to lepsie len to preblikovalo. A nerad by som brzdil cely program s nejakym dlhym delay. Je na to nejake riesenie? Kedze by som potom chcel pridavat dalsie veci. Jedna sa o atmegu8 bezi na f cpu 1MHz skusal som aj na 8 MHz to iste.

Tu je kod

[code]#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>

#define LCD_E 5 //vyvody na PORTE C
#define LCD_RS 4
#define LCD_D7 3
#define LCD_D6 2
#define LCD_D5 1
#define LCD_D4 0
#define LCD PORTC //definovany PORT
#define LCDR DDRC //definovanie smer vyvodou na porte C

	void E_prepni(void){
		LCD|=1<<LCD_E;		//zapni E 
		_delay_ms(1);		//cakaj
		LCD&=~(1<<LCD_E);	//vypni E
						}

void LCD_Prikaz(unsigned char cmd){ //odosielanie prikazu do lcd

  LCD=((cmd&0b11110000))>>4; //vymaskuj  spodne 4 bity + sprav bitovy posun
     E_prepni();			//prepni E signal
  LCD=(cmd&0b00001111);		//vymaskuj horne 4 bity
 	E_prepni();				//prepni E signal
   _delay_ms(1);			//cakaj
				}

void LCD_Data(unsigned char data){ //odosielanie znakov do lcd

        	LCD=((data&0b11110000))>>4;  //vymaskuj spodne bity + sprav bitovy posun
		LCD|=1<<LCD_RS;				//zapis data RS signal na 1
		E_prepni();					//prepni E signal
		LCD&=~(1<<LCD_RS);			//RS signal na nulu
		LCD=(data&0b00001111);		//vymaskuj horne 4 bity
		LCD|=1<<LCD_RS;				//zapis data
		E_prepni();					//prepni E signal
		LCD&=~(1<<LCD_RS);			//RS signal na nulu
						}	

}

	void InitLCD(void){

		_delay_ms(15);  //po zapnuti cakame 15ms
		LCDR = 0b11111111;  //vsetky vyvody ako vystup
		LCD = 0b00000000;  //vsetky vyvody lcd na nulu

		LCD= 0b00000011; //Function Set 8bit
		E_prepni();			//prepni E signal
		_delay_ms(1);
		LCD= 0b00000011; //Function Set 8bit
		E_prepni();			//prepni E signal
		_delay_ms(1);
		LCD= 0b00000010; //Function Set 4bit
		E_prepni();			//prepni E signal
		_delay_ms(1);

		LCD_Prikaz(0b00101000); //Funkcion Set 2line mode
		LCD_Prikaz(0b00001100); //Display On

							}

	void VymazLCD(void){
		LCD_Prikaz(0b00000001);
							}


void lcd_puts(const char *q)  
	{
		 while(*q){
   			 LCD_Data(*q++);
							 }
						}


int main(){

char text[32];

InitLCD();
VymazLCD();


	while(1){

	sprintf(text,"Hello world");	//zapis stringu do pola znakov "text"
	  
   	lcd_puts(text);					//vypis stringu na lcd
	
	_delay_ms(80);					

	VymazLCD();


	}

}
[/code]

:arrow_right: administrator: přejmenováno z "Vlastna kniznica pre HD44780"

cau . to bude asi tim ze to povypsani znaku mazes… to chvily trva

Skusal som to aj bez toho mazania, lenze vtedy mi zaplnilo cely lcd oba riadky hlaskou, ktoru som si zvolil.

tet si zrovna uz moc nepamatuju jak se presne komunikuje ale nenastavuje se tam nahodou zacatek kurzoru ? pokud ho nenastavis tak se posouva ,tim se ti to zaplni cely…, prikaz pro smazani to nastavi na zacatek,
nekde na netu je perfektni simulator bohuzel… nenasel sem ho…

EDIT: tak nasel dinceraydin.com/djlcdsim/djlcdsim.html
EDIT2: skus tohle dat misto mazanivoid kurzor_na_0(void){ LCD_Prikaz(0b00000010); }

Skusil som to dat super pomohlo to ale vsimol som si jednu vec. Po resete mi nie vzdy korektne nabehne lcd. Horny riadok ma silnejsi kontrast ako spodny (text je stale citatelny). Ale trosku vidno vsetky stvorce v hornom riadku. Po preruseni napajania mi korektne nabehne vzdy.

Doporučuji si přečíst datasheet k tomu LCD. Uprav to takto:

 void InitLCD(void){ 

         _delay_ms(15);  //po zapnuti cakame 15ms 
         LCDR = 0b11111111;  //vsetky vyvody ako vystup 
         LCD = 0b00000000;  //vsetky vyvody lcd na nulu 

         LCD= 0b00000011; //Function Set 8bit 
         E_prepni();         //prepni E signal 

         _delay_ms(5);         //čekej > 4,1ms(úprava časování)

         LCD= 0b00000011; //Function Set 8bit 
         E_prepni();         //prepni E signal 
         _delay_ms(1); 
         LCD= 0b00000010; //Function Set 4bit 
         E_prepni();         //prepni E signal 
         _delay_ms(1); 

         LCD_Prikaz(0b00101000); //Funkcion Set 2line mode
         VymazLCD();
         LCD_Prikaz(0b00001100); //Display On
         LCD_Prikaz(0b00000110); //po zapsání znaku posun kurzoru vpravo

                        } 

      void VymazLCD(void){ 
         LCD_Prikaz(0b00000001); 
         _delay_ms(2);                  //čekej > 1,64ms
                        }

      void HomeLCD(void){ 
         LCD_Prikaz(0b00000010); 
         _delay_ms(2);                  //čekej > 1,64ms
                        }

Příkazy Vymaž display a Návrat na začátek mají čas provedení 1,64ms. Také můžeš zkusit dát na začátku inicializace delší čas >15ms(40ms).

Pracujem aj s datasheetom k tomu lcd, ale neviem odkial si zobral hodnotu 1,64ms.
Ja pouzivam tento: sparkfun.com/datasheets/LCD/HD44780.pdf
na strane 24 je uvedeny execution time. Napr pri clear display nie je ziadny cas…

:arrow_right: administrator: přiloženy externí soubory
HD44780.pdf (322 KB)

Zde je popis v češtině a v datasheetu najdeš i postup přepnutí do 4bit modu:
m.pandatron.cz/?685ovladani_znak … %96_1._dil

Dik za link, skusil som doplnit delay do tych funkcii vyzera ze to funguje dobre.

Budem trochu vrtat ale uz davno som nevidel displej s tymto chipom vecsinou je to kompaktibilne s tymto cipom, vecsinou ± nemusi sediet casovanie. Tak ze stale opisat kompletny kod z displeja a pokusit s anajst datashit od vyrobcu :slight_smile:

Tiez si myslim ze to nie je priamo HD44780 kupoval som ho na inzerat a dotycny mi tvrdil ze je to ten cip. Vzadu su len dve zaliate chipy priamo na plosaku ale ziadny kod podla ktoreho by som zistil co je to v skutocnosti. Su tam nejake cisla ale na googli som nic konkretne nenasiel. Pripadne mozem uploadnut foto lcd, snad by niekto vedel…

Vysvětlení je jednoduché - Po resetu MCU je LCD ve 4-bitovém režimu, ale ne vždycky má LCD přijatou celou instrukci/data. Stává se (obzvlášť, pokud zapisuješ do LCD hodně často), že vyresetuješ MCU v okamžiku, kdy do máš do LCD odeslanou první polovinu bytu. V tuhle chvíli nastává problém, že Ty začneš odesílat 4-bitově instrukce znova, ale LCD přijme 1. polovinu instrukce jako 2. polovinu té, kterou měl rozodeslanou. Tím pádem se Ti rozhodí synchronizace instrukcí až do nějakého náhodného sesynchronizování. Podívej se na následující kód :

void LCD_init()
{
   LCD_CNTRL_DDR = 0xFF;
   LCD_CNTRL_PORT = 0x00;
   LCD_DATA_DDR = 0xFF;
   LCD_DATA_PORT = 0x00;
 
   _delay_ms(100);    // Start LCD vyžaduje alespoň 30ms. (dávám s rezervou 100 ms)

// ---------------------------------------------------------------------------------------------------------
// Následující sekvenci je nutné použít pouze v případě 4-bitové komunikace.

   LCD_CNTRL_PORT &= ~(1<<LCD_RS_PIN);
   LCD_CNTRL_PORT &= ~(1<<LCD_RW_PIN);
   LCD_DATA_PORT = 0x30; // pro LCD na bitech 7-4 - přepnutí do 8-bitové komunikace
 
   LCD_CNTRL_PORT |= (1<<LCD_ENABLE_PIN);
   _delay_us(1);
   LCD_CNTRL_PORT &= ~(1<<LCD_ENABLE_PIN);
   _delay_us(40);
// 1. - buď se a) dokončila rozpracovaná 4-bit komunikace nebo se b) zahájilo přepnutí do 8-bit komunikace.

   LCD_CNTRL_PORT |= (1<<LCD_ENABLE_PIN);
   _delay_us(1);
   LCD_CNTRL_PORT &= ~(1<<LCD_ENABLE_PIN);
   _delay_us(40);
// 2. - buď se b) dokončilo přepnutí do 8-bit komunikace nebo se c) zahájilo přepnutí do 8-bit komunikace

   LCD_CNTRL_PORT |= (1<<LCD_ENABLE_PIN);
   _delay_us(1);
   LCD_CNTRL_PORT &= ~(1<<LCD_ENABLE_PIN);
   _delay_us(40);
// 3. - buď se c) dokončilo přepnutí do 8-bit komunikace nebo se d) znovu přepnulo do 8-bit komunikace

// Teď je LCD zaručeně v 8-bitovém režimu

   LCD_DATA_PORT = 0x20; // pro LCD na bitech 7-4 - přepnutí do 4-bitové komunikace
 
   LCD_CNTRL_PORT |= (1<<LCD_ENABLE_PIN);
   _delay_us(1);
   LCD_CNTRL_PORT &= ~(1<<LCD_ENABLE_PIN);
   _delay_us(40);
// Konec sekvence.
// ---------------------------------------------------------------------------------------------------------


// Teď je komunikace 4-bitová a můžou se využít funkce pro 4-bitový zápis.
// Ještě však není nastaven počet řádků ani typ fontu znaků (5x7 nebo 5x10)


// Příklad inicializace LCD :
   LCD_Instrukce(0x28); // nastavit 4-bitovou komunikaci, 2(4) řádky, font 5x7
   _delay_us(40);
    LCD_Instrukce(0x06);   // Posouvat kurzor doprava, neposouvat displej
   _delay_us(40);
    LCD_Instrukce(0x01);   // Smazat LCD
   _delay_ms(2);            // Smazání LCD trvá 1,53 ms
    LCD_Instrukce(0x0C);   // Zapnout zobrazení na LCD, bez blikání, bez kurzoru
   _delay_us(40);

}

Po zapnutí, kdy je LCD v 8-bitovém režimu se prostě 3x znova přepne do 8-bitového režimu, pak teprve do 4-bitového a pak se posílají data a instrukce 4-bitove.

Tak teraz je to uz super. Doplnil som kod inicializacie lcd podla tvojho kodu a nabieha bezchybne ci po resete alebo po zapnuti. Dakujem za pomoc.
Este mam otazku pri prepinani E signalu staci cakanie 1us?

Mam este jednu otazku pracujem teraz na funkcii vytvorenie vlastneho znaku. Neviem ci som dobre pochopil datasheet ale podla vsetkeho prikazy lcd alebo data odosielam do RAM (bud DDRAM alebo CGRAM) ale znaky su ulozene uz v pamati ROM.
Napriklad pri vytvoreni vlastneho znaku posielam seriu prikazov do cgram ale samotny znak sa ulozi do ROM. Dobre som to pochopil?

Podle datasheetu je šířka pulzu minimálně 220 ns. Při kmitočtu mcu 8 MHz je doba trvání jedné jednocyklové instrukce (což SBI a CBI jsou - v assembleru) 125 ns, takže mezi nahození a shození signálu stačí 1x NOP (pro assembler). Při kmitočtu 4 MHz a méně už nic vkládat nemusíš, protože jedna jednocyklová instrukce trvá 250 ns (2 MHz = 500ns, 1MHz = 1us). Vložení čekání 1 us je tudíž nekolikanásobně delší, než je potřeba, nicméně nic tím nezkazíš.

LCD má vlastní znakovou sadu v ROM. Ale znaky 0-7 jsou uloženy v CGRAM, kde si můžeš definovat vlastní znaky. Na LCD se mění znaky okamžitě při zápisu do CGRAM, takže předefinováním znaku se změní i znak, který je již na LCD zobrazený.

Takze som skusal si vytvorit funkciu na vytvorenie vlastneho znaku podla datasheetu. Zatial je jednoducha na vypisanie znaku na jednu adresu… Ale nie som moc spokojny s vysledkom. Nakolko znak nie vzdy sa korektne zobrazil. Skusal som rozne moznosti a az ked som dal delay za kazdou funkciou vtedy uz zacalo pravidelne korektne zobrazovat.
Este jednej veci nerozumiem ked dam do while len tuto funkciu znak mi zobrazi ale kurzor ktory je hned za znakom blika tak rychlo ze ho takmer nie je vidiet. Inak kurzor je pekne vidiet na lcd bez pouzitia tejto funkcie. Tu je kod:

[code]void vytvor_znak(void){

		  LCD_Prikaz(0b01000000); //nastavenie cgram
			_delay_us(1);	

			LCD_Data(0b00011011); //posielanie bajtov do cgram
			_delay_us(1);
			LCD_Data(0b00010001);
			_delay_us(1);
			LCD_Data(0b00010001);
			_delay_us(1);
			LCD_Data(0b00010001);
			_delay_us(1);
			LCD_Data(0b00010001);
			_delay_us(1);
			LCD_Data(0b00010001);
			_delay_us(1);
			LCD_Data(0b00011011);
			_delay_us(1);	

	     LCD_Prikaz(0b10000000);  //nastavenie ddram
			_delay_ms(2);
		  LCD_Data(0b00000000);  //vypisanie znaku z cgram
									}[/code]

ked posles ten znak do gcram, a nepremazes ho nejakym inym znakom, alebo neresetnes disp., tak on tam uz ostane. Potom uz staci len vypisovat z GCram. Znova ho tam posielat netreba.

Nemělo tam být _delay_us(2); místo _delay_ms(2); ? (moc to nesleduji tak možná že jsem mimo)

Ak som dobre pochopil tak funkciu staci dat do main a nie do slucky a potom uz nechat v slucke pouzit funkciu na vypisanie znaku?

Panda38: skusal som aj casy menit od 1us do 2ms ale rozdiel som nevidel.

Správně. Znak stačí nadefinovat jednou na začátku, tak jako inicializuješ LCD jenom při startu. Pak už znak jenom používáš úplně stejně jako všechny ostatní z ROM.

Smazání a Cursor Home trvá řadiči 1,53 ms, zápis a čtení do/z DDRAM/CGRAM trvá 43us, všechny ostatní zápisy trvají 39us. Pokud tedy nepoužíváš čtení BUSY flagu z LCD, tak po zápisu do LCD bys měl dodržet výše zmíněné časy. Já osobně mám v knihovně po zápisu 50us čekání, takže v hlavním programu už čekání neřeším. Pro smazání a cursor home mám extra rutiny, protože tam je navíc další 1,5ms čekání. Prostě jenom zavolám rutinu pro zápis do LCD a rutina zapíše a 50us počká. Pochopitelně, pokud máš aplikaci, která nemůže čekat, pak je výhodnější zapisovat do LCD v přerušení a jenom vždy rutině předat data pro zápis na LCD. Rutina pak zapíše byte a vrátí kontrolu hlavnímu programu a přerušení se postará o to, aby další byte neodešel dříve, než za těch 50us.

Ahojte, trapim sa s jednym problemom pomaly 3 dni neviem prist na riesenie. Jedna sa vytvorenie noveho znaku ktory si vytvorim aj zobrazim ale na poslednom riadku znaku kde by mal byt kurzor mi zasvietia vzdy tie iste dva “pixeli”
Ak dam do funkcie vytvorenia znaku aby posledny 8 riadok bol cisty tak to ide pekne to zobrazi. Ked zrusim ten prikaz tak chvilu to zobrazuje korektne a potom zas zasvietia tie dva pixeli. Pozeral som nejake example kody na vytvorenie znaku a nikde sa nedaval prikaz na osmy riadok je to vlastne pozicia kurzora.
Moj kod na vytvorenie znaku:

[code]void vytvor_znak(void){

		  LCD_Prikaz(0b01000000); //nastavenie cgram
			
			LCD_Data(0b00011111); //posielanie bajtov do cgram
			LCD_Data(0b00001110);
			LCD_Data(0b00001110);
			LCD_Data(0b00001110);
			LCD_Data(0b00001110);
			LCD_Data(0b00001110);
			LCD_Data(0b00011111);
		  //LCD_Data(0b00000000);  //po zadani toho prikazu spodny riadok je cisty
										
	     LCD_Prikaz(0b10000000);  //nastavenie ddram
		 _delay_us(100);
		
		
								}[/code]

Este jedna vec mi nie je jasna. Ked som pozeral nejake kody tak autori davali delay do programu ale ten isty dva krat za sebou. Je to kvoli nejakemu zameru? Napr. [code]_delay_us(100);
_delay_us(100);

[/code]