Skocz do zawartości

Problem z wysterowaniem LCD 2x16 - co zle ?


razors

Pomocna odpowiedź

Witam

Niestety mimo wielu prob nie udaje mi sie wyswietlic nic na ww. wyswietlaczu.

Zachowuje sie on tak jakby byl tylko podlaczony pod zasilanie czy pali sie pierwszy wiersz z zapalonymi wszystkimi pikselami.

Zamieszczam ponizej kod programu i mialbym wielko prosbe o przeanalizowanie go w celu znalezienie bledow.

LCD podlaczony do Atmega16 ustawionej na 4Mhz (wszystko pod PORTC - RS 0, E 2, D4:D7 odpowiednio 4:7, R/W do masy). Program ma sterowac 4bitowo bez sprawdzania zajetosci sterownika wyswietlacza.

kod:

// PROGRAM OBSLUGUJACY WYSWIETLACZ LCD (2X16 ZNAKOW) //
// Atmega16, 4Mhz, //
/////////////////////////////////////////////////////

//--------------------------------------------------
// Biblioteki:

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

//--------------------------------------------------

// Makra upraszczajace dostep do portow:

#define PORT(X) XPORT(X) //podanie w programie: PORT(D) zinterpretuje jako "PORTD"
#define XPORT(X) (PORT##X)

#define PIN(X) XPIN(X)
#define XPIN(X) (PIN##X)

#define DDR(X) XDDR(X)
#define XDDR(X) (DDR##X)

//--------------------------------------------------
// Definicje wyprowadzen:

#define LCD_RS 0
#define LCD_RSPORT C
#define LCD_E 2
#define LCD_EPORT C
#define LCD_DPORT C
#define LCD_D4 4

//--------------------------------------------------
// Komendy sterujace wyswietlaczem:

#define LCDC_CLS 0x01
#define LCDC_HOME 0x02
#define LCDC_MODE 0x04
#define LCDC_MODER 0x02
#define LCDC_MODEL 0
#define LCDC_MODEMOVE 0x01
#define LCDC_ON 0x08
#define LCDC_ONDISPLAY 0x04
#define LCDC_ONCURSOR 0x02
#define LCDC_ONBLINK 0x01
#define LCDC_SHIFT 0x10
#define LCDC_SHIFTDISPLAY 0x08
#define LCDC_SHIFTR 0x04
#define LCDC_SHIFTL 0
#define LCDC_FUNC 0x20
#define LCDC_FUNC8b 0x10
#define LCDC_FUNC4b 0
#define LCDC_FUNC2L 0x08
#define LCDC_FUNC1L 0
#define LCDC_FUNC5x10 0x4
#define LCDC_FUNC5x7 0
#define LCDC_CGRAM 0x40
#define LCDC_DDRAM 0x80

//---------------------------------------------------
// Definicje opoznien:

#define delay250ns() {asm volatile("nop":Smile;}

#define delayus8(t)\
{asm volatile( \
"delayus8_loop%=: \n\t"\
"nop \n\t"\
"dec %[ticks] \n\t"\
"brne delayus8_loop%= \n\t"\
: :[ticks]"r"(t) );}
//DEC - 1 cykl; BRNE - 2 cykle; +1 x nop; Zegar 4Mhz

void delay100us8(uint8_t t)
{
while(t>0)
{
delayus8(100)
--t;
}
}


//-------------------------------------------------
// Funkcje niskiego poziomu:


#define LCD_EPULSE() \
{PORT(LCD_EPORT) |= 1<<LCD_E; \
delay250ns(); \
PORT(LCD_EPORT) &= ~(1<<LCD_E);}
// LCD_EPULSE - strobuje dostep do wyswietlacza (zalaczamy czas na pobranie danych lub rozkazow)


void LCD_sendHalf(uint8_t data)
{
data = (data & 0x0F) << LCD_D4;
PORT(LCD_DPORT) = (PORT(LCD_DPORT) & ~(0x0F<<LCD_D4)) | data;
LCD_EPULSE();
}

void LCD_send(uint8_t data)
{
LCD_sendHalf(data>>4); //starsza czesc
LCD_sendHalf(data); //mlodsza czesc
delayus8(120);//120 us
}

//-------------------------------------------------
// Funkcje wysylajace komendy i dane:

void LCD_command(uint8_t command)
{
PORT(LCD_RSPORT) &= ~(1<<LCD_RS); //przeslanie instrykcji; RS = 0
LCD_send(command);
}

void LCD_data(uint8_t data) //przeslanie danych do DD RAM; RS = 1
{
PORT(LCD_RSPORT) |= 1<<LCD_RS;
LCD_send(data);
}

void LCD_cls(void) //czyszczenie wyswietlacza
{
LCD_command(LCDC_CLS);
delay100us8(48);
}

void LCD_home(void) //ustawianie wyswietlacza w pozycji home
{
LCD_command(LCDC_HOME);
delay100us8(50);
}

//-------------------------------------------------
// Inicjacja Wyswietlacza:

void LCD_init(void)
{
delay100us8(150);
PORT(LCD_RSPORT) &= ~(1<<LCD_RS);
LCD_sendHalf((LCDC_FUNC | LCDC_FUNC8b)<<LCD_D4);
delay100us8(41);
LCD_sendHalf((LCDC_FUNC | LCDC_FUNC8b)<<LCD_D4);
delay100us8(2);
LCD_sendHalf((LCDC_FUNC | LCDC_FUNC4b)<<LCD_D4);
//teraz jest juz 4b, koniec korzystania z sendHalf
LCD_command(LCDC_FUNC | LCDC_FUNC4b | LCDC_FUNC2L | LCDC_FUNC5x7);
LCD_command(LCDC_ON);
LCD_cls();
LCD_command(LCDC_MODE | LCDC_MODEL);
LCD_command(LCDC_ON | LCDC_ONDISPLAY);
}

//-------------------------------------------------
//PETLA GLOWNA:

int main(void)
{
//inicjalizacja portow
DDR(LCD_DPORT) |= 1<<LCD_E | 1<<LCD_RS | 0x0F<<LCD_D4;

DDRA |= 1<<0;//podciagniecie diodki sprawdzajacej czy program przejdzie

//przygotowanie wyswietlacza
LCD_init();

//podanie danych na wyswietlacz
LCD_data('H');
LCD_data('E');
LCD_data('L');
LCD_data('L');
LCD_data('O');

PORTA |= 1<<0;//wl. diodki sprawdzajacej
return 0;

}
Link do komentarza
Share on other sites

Kod proszę umieszczać zawsze w tagach [code ].

W 5 części kursu C jest omówiona obsługa wyświetlaczy:

www.kursC.forbot.pl

Może tam znajdziesz coś co rozwiąże Twój problem.

Link do komentarza
Share on other sites

niestety przeanalizowanie kursu nie pomogło mi za bardzo; kod do wyświetlacza jest tam budowany w trochę inny sposób; prosiłbym jednak o przeanalizowanie mojego kodu

Link do komentarza
Share on other sites

LCD_sendHalf((LCDC_FUNC | LCDC_FUNC8b)<<LCD_D4);
LCD_sendHalf((LCDC_FUNC | LCDC_FUNC8b)<<LCD_D4);
LCD_sendHalf((LCDC_FUNC | LCDC_FUNC4b)<<LCD_D4); 

Niepotrzebne są <

#define LCDC_FUNC 0x20
#define LCDC_FUNC8b 0x10
#define LCDC_FUNC4b 0 
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

niestety usuniecie tych przesunięć nie pomaga ... zastanawia mnie bardzo czy ten fragment kodu jest aby prawidłowy :

#define LCD_EPULSE() \
{PORT(LCD_EPORT) |= 1<<LCD_E; \
delay250ns(); \
PORT(LCD_EPORT) &= ~(1<<LCD_E);}
// LCD_EPULSE - strobuje dostep do wyswietlacza (zalaczamy czas na pobranie danych lub rozkazow)

zauważyłem mianowicie, że po przejściu programu zapala mi się dioda testowa i napięcie na E jest stanu wysokiego, zastanawiam się więc czy w ogóle jest ono zerowane po załączaniu w tym makrze ?

Link do komentarza
Share on other sites

Kod wygląda na ok. Spróbuj dodać trochę dłuższego delaya, tzn. delay250ns(); zamienić np. na 1us. Mój lcd ponizej pewnej granicznej wartosci (nie pamietam juz jakiej) nie działał prawidłowo, tzn. nie wyświetlał nic albo krzaczki.

Btw. masz gotową bibliotekę w AVR studio, lcd_lib - zobacz jak tam jest to napisane, porównaj ze swoim kodem.

Link do komentarza
Share on other sites

Uruchomiłem program na płytce olimex-a z atmega128 (http://olimex.com/dev/pdf/AVR/AVR-MT-128-SCH-REV-A.pdf).

Konieczne były drobne zmiany, ale działa:

    // PROGRAM OBSLUGUJACY WYSWIETLACZ LCD (2X16 ZNAKOW) //
   // Atmega16, 4Mhz, //
   /////////////////////////////////////////////////////

   //--------------------------------------------------
   // Biblioteki:

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

	#define F_CPU 4000000
	#include <util/delay.h>

   //--------------------------------------------------

   // Makra upraszczajace dostep do portow:

   #define PORT(X) XPORT(X) //podanie w programie: PORT(D) zinterpretuje jako "PORTD"
   #define XPORT(X) (PORT##X)

   #define PIN(X) XPIN(X)
   #define XPIN(X) (PIN##X)

   #define DDR(X) XDDR(X)
   #define XDDR(X) (DDR##X)

   //--------------------------------------------------
   // Definicje wyprowadzen:

   #define LCD_RS 0
   #define LCD_RSPORT C
	#define LCD_RW 1
	#define LCD_RWPORT C
   #define LCD_E 2
   #define LCD_EPORT C
   #define LCD_DPORT C
   #define LCD_D4 4

   //--------------------------------------------------
   // Komendy sterujace wyswietlaczem:

   #define LCDC_CLS 0x01
   #define LCDC_HOME 0x02
   #define LCDC_MODE 0x04
   #define LCDC_MODER 0x02
   #define LCDC_MODEL 0
   #define LCDC_MODEMOVE 0x01
   #define LCDC_ON 0x08
   #define LCDC_ONDISPLAY 0x04
   #define LCDC_ONCURSOR 0x02
   #define LCDC_ONBLINK 0x01
   #define LCDC_SHIFT 0x10
   #define LCDC_SHIFTDISPLAY 0x08
   #define LCDC_SHIFTR 0x04
   #define LCDC_SHIFTL 0
   #define LCDC_FUNC 0x20
   #define LCDC_FUNC8b 0x10
   #define LCDC_FUNC4b 0
   #define LCDC_FUNC2L 0x08
   #define LCDC_FUNC1L 0
   #define LCDC_FUNC5x10 0x4
   #define LCDC_FUNC5x7 0
   #define LCDC_CGRAM 0x40
   #define LCDC_DDRAM 0x80

   //---------------------------------------------------
   // Definicje opoznien:

   void delay250ns() 
	{
		volatile int i;
		for (i=0;i<2;i++)
		  i=i;
	}


   #define delayus8(t)\
   {asm volatile( \
   "delayus8_loop%=: \n\t"\
   "nop \n\t"\
   "dec %[ticks] \n\t"\
   "brne delayus8_loop%= \n\t"\
   : :[ticks]"r"(t) );}
   //DEC - 1 cykl; BRNE - 2 cykle; +1 x nop; Zegar 4Mhz

   void delay100us8(uint8_t t)
   {
   while(t>0)
   {
   delayus8(100)
   --t;
   }
   }


   //-------------------------------------------------
   // Funkcje niskiego poziomu:


   #define LCD_EPULSE() \
   {PORT(LCD_EPORT) |= 1<<LCD_E; \
   delay250ns(); \
   PORT(LCD_EPORT) &= ~(1<<LCD_E);}
   // LCD_EPULSE - strobuje dostep do wyswietlacza (zalaczamy czas na pobranie danych lub rozkazow)


   void LCD_sendHalf(uint8_t data)
   {
   data = (data & 0x0F) << LCD_D4;
   PORT(LCD_DPORT) = (PORT(LCD_DPORT) & ~(0x0F<<LCD_D4)) | data;
   LCD_EPULSE();
   }

   void LCD_send(uint8_t data)
   {
   LCD_sendHalf(data>>4); //starsza czesc
   LCD_sendHalf(data); //mlodsza czesc
   delayus8(120);//120 us
   }

   //-------------------------------------------------
   // Funkcje wysylajace komendy i dane:

   void LCD_command(uint8_t command)
   {
   PORT(LCD_RSPORT) &= ~(1<<LCD_RS); //przeslanie instrykcji; RS = 0
   LCD_send(command);
   }

   void LCD_data(uint8_t data) //przeslanie danych do DD RAM; RS = 1
   {
   PORT(LCD_RSPORT) |= 1<<LCD_RS;
   LCD_send(data);
   }

   void LCD_cls(void) //czyszczenie wyswietlacza
   {
   LCD_command(LCDC_CLS);
   delay100us8(48);
   }

   void LCD_home(void) //ustawianie wyswietlacza w pozycji home
   {
   LCD_command(LCDC_HOME);
   delay100us8(50);
   }

   //-------------------------------------------------
   // Inicjacja Wyswietlacza:

   void LCD_init(void)
   {
   delay100us8(150);
   PORT(LCD_RSPORT) &= ~(1<<LCD_RS);
   LCD_sendHalf((LCDC_FUNC | LCDC_FUNC8b));
   delay100us8(41);
   LCD_sendHalf((LCDC_FUNC | LCDC_FUNC8b));
   delay100us8(2);
   LCD_sendHalf((LCDC_FUNC | LCDC_FUNC4b));
   //teraz jest juz 4b, koniec korzystania z sendHalf
   LCD_command(LCDC_FUNC | LCDC_FUNC4b | LCDC_FUNC2L | LCDC_FUNC5x7);
   LCD_command(LCDC_ON);
   LCD_cls();
   LCD_command(LCDC_MODE | LCDC_MODEL);
   LCD_command(LCDC_ON | LCDC_ONDISPLAY);
   }

   //-------------------------------------------------
   //PETLA GLOWNA:

   int main(void)
   {
   //inicjalizacja portow
   DDR(LCD_DPORT) |= 1<<LCD_E | 1<<LCD_RW | 1<<LCD_RS | 0x0F<<LCD_D4;
	PORT(LCD_RWPORT) &= ~(1<<LCD_RW);

   DDRA |= 1<<0;//podciagniecie diodki sprawdzajacej czy program przejdzie

	_delay_ms(100);
   //przygotowanie wyswietlacza
   LCD_init();

   //podanie danych na wyswietlacz
	LCD_cls();
   LCD_data('H');
   LCD_data('E');
   LCD_data('L');
   LCD_data('L');
   LCD_data('O');

   PORTA |= 1<<0;//wl. diodki sprawdzajacej

	while (1) ;

   return 0;

   }

[ Dodano: 31 Mar 10 10:20 ]

Definicję F_CPU należy oczywiście przenieść do pliku make, ale na szybko jest w kodzie.

Musiałem dodać opóźnienie przed inicjalizacją lcd - 100ms, inaczej czasem pojawiały się krzaki.

Zmieniłem procedurę opóźniającą. Lepiej użyć _delay_us zamiast niej. To co było nie chciało mi się kompilować.

Dodałem jeszcze obsługę pinu RW - płytka olimex ma wyprowadzony, więc musiałem go wyzerować.

Jeszcze jedna zmiana to dodanie LCD_cls() przed wyświetlaniem napisu. Inaczej wyświetlał tylko 'H'.

Kod powinien działać, jeśli nie, to sprawdź czy hardware jest na pewno sprawny.

  • Lubię! 1
Link do komentarza
Share on other sites

[ Dodano: 31 Mar 10 02:36 ]

działa już mój wyświetlacz 🙂

... a więc problem tkwił w tym, że w Atmega16 standardowo jest ustawiony FUSE bit JTAG, który blokuje niektóre wyjścia PORTC, do którego pechowo podłączyłem sobie wyświetlacz ;/ .... mały niuans a zabrał mi ze 2 dni rozważań ... dziękuje wszystkim

Link do komentarza
Share on other sites

Tez miałem problem z moim LCD 2x16 na ATmega16 na PORTC. Odpowiednie ustawienie fusów (wył. JTAG) nie pomagało, natomiast zadziałało wtedy, gdy wpisałem odpowiednią wartość do pewnego rejestru:

MCUCSR |= 0x80 

Znalazłem to na jakimś zagranicznym forum

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.