07 ARM SAM4 – LCD 44780

Dzisiaj chciałem przedstawić obsługę popularnego wyświetlacza LCD znakowego na przykładzie rodziny wyświetlaczy 44780. Ponieważ pracujemy na poziomie napięcia 3,3V musimy zakupić wyświetlacz mogący współpracować z takim poziomem sygnału dla przykładu CBC016002BS00-YIY-R. W przeciwnym przypadku musimy zastosować konwersję sygnału sterującego z 3,3V na 5V.

Poniżej przedstawiłem podłączenie elektryczne wyświetlacza do układu SAM4S.

Na początku zaczynamy od zdefiniowania pinów wejściowych i przypisania ich memoników ( zrozumiałych nazw w kodzie programu).

#define LCD_RS_s       PIOC->PIO_SODR = (1<<4)
#define LCD_RS_c       PIOC->PIO_CODR = (1<<4)
#define LCD_E_s        PIOC->PIO_SODR = (1<<5)
#define LCD_E_c        PIOC->PIO_CODR = (1<<5)
#define LCD_D4_s       PIOA->PIO_SODR = (1<<16)
#define LCD_D4_c       PIOA->PIO_CODR = (1<<16)
#define LCD_D5_s       PIOC->PIO_SODR = (1<<7)
#define LCD_D5_c       PIOC->PIO_CODR = (1<<7)
#define LCD_D6_s       PIOA->PIO_SODR = (1<<15)
#define LCD_D6_c       PIOA->PIO_CODR = (1<<15)
#define LCD_D7_s       PIOC->PIO_SODR = (1<<6)
#define LCD_D7_c       PIOC->PIO_CODR = (1<<6)

Dzięki temu używamy bezpośrednio nazwy np. wejścia E wyświetlacza i jego stanu wysokiego ( LCD_RS_s) lun niskiego (LCD_RS_c).

Teraz potrzebujemy dodatkowe procedury umożliwiające odliczanie czasu, ponieważ wyświetlacz potrzebuje czas na odczytanie sygnału. Procedury są bardzo proste i testowane były tylko dla zegara 4Mhz. Odliczany przez nie czas nie jest dokładny. Procedura DelayUs – odlicza w przybliżeniu 1 ns, poprzez wykonanie dwóch instrukcji NOP – to jest pusta intrukcja + wymagany jest skok do samej instrukcji co zajmuje 2 cykle zegara. Wykonanie instrukcji DelayMs trwa w przybliżeniu 1 ms, poprzez wykonanie 800 razy instrukcji DelayUs, ale również wykonanie pętli zabiera dużą liczbę instrukcji.

void DelayUs(uint32_t us)
 {
  while(--us)
  {
    __NOP(); __NOP();
  }
}

void DelayMs(uint32_t ms)
 {
  uint32_t ims;
  for(ims=0; ims<ms; ims++) DelayUs(800);
 }

 

 

Strona w budowie poniżej cały kod:

 

#include "sam.h"

 

#define LEDA (1<<0)
#define LEDB (1<<17)

 

#define LCD_RS_s PIOC->PIO_SODR = (1<<4)
#define LCD_RS_c PIOC->PIO_CODR = (1<<4)
#define LCD_E_s PIOC->PIO_SODR = (1<<5)
#define LCD_E_c PIOC->PIO_CODR = (1<<5)
#define LCD_D4_s PIOA->PIO_SODR = (1<<16)
#define LCD_D4_c PIOA->PIO_CODR = (1<<16)
#define LCD_D5_s PIOC->PIO_SODR = (1<<7)
#define LCD_D5_c PIOC->PIO_CODR = (1<<7)
#define LCD_D6_s PIOA->PIO_SODR = (1<<15)
#define LCD_D6_c PIOA->PIO_CODR = (1<<15)
#define LCD_D7_s PIOC->PIO_SODR = (1<<6)
#define LCD_D7_c PIOC->PIO_CODR = (1<<6)

//#define INIT_DB4() GPIO_dir(PORT_DB4, DB4, 1);
//#define SET_DB4() GPIO_set(PORT_DB4, DB4, 1);
//#define CLR_DB4() GPIO_set(PORT_DB4, DB4, 0);

#define HD44780_CLEAR 0x01

#define HD44780_HOME 0x02

#define HD44780_ENTRY_MODE 0x04
#define HD44780_EM_SHIFT_CURSOR 0
#define HD44780_EM_SHIFT_DISPLAY 1
#define HD44780_EM_DECREMENT 0
#define HD44780_EM_INCREMENT 2

#define HD44780_DISPLAY_ONOFF 0x08
#define HD44780_DISPLAY_OFF 0
#define HD44780_DISPLAY_ON 4
#define HD44780_CURSOR_OFF 0
#define HD44780_CURSOR_ON 2
#define HD44780_CURSOR_NOBLINK 0
#define HD44780_CURSOR_BLINK 1

#define HD44780_DISPLAY_CURSOR_SHIFT 0x10
#define HD44780_SHIFT_CURSOR 0
#define HD44780_SHIFT_DISPLAY 8
#define HD44780_SHIFT_LEFT 0
#define HD44780_SHIFT_RIGHT 4

#define HD44780_FUNCTION_SET 0x20
#define HD44780_FONT5x7 0
#define HD44780_FONT5x10 4
#define HD44780_ONE_LINE 0
#define HD44780_TWO_LINE 8
#define HD44780_4_BIT 0
#define HD44780_8_BIT 16

#define HD44780_CGRAM_SET 0x40

#define HD44780_DDRAM_SET 0x80

//#define Lcd(tekst) LcdWriteProgmem(PSTR(tekst))
#define Lcd1 LcdGoto(0,0) // skok do pierwszej linii
#define Lcd2 LcdGoto(0,1) // skok do drugiej linii

void DelayUs(uint32_t us)
{
while(--us)
{
__NOP(); __NOP();
}
}

void DelayMs(uint32_t ms)
{
uint32_t ims;
for(ims=0; ims<ms; ims++) DelayUs(800);
}

 

 

void _lcd_Write(unsigned char dataToWrite)
{
if(dataToWrite & 0b00010000) LCD_D4_s; else LCD_D4_c;
if(dataToWrite & 0b00100000) LCD_D5_s; else LCD_D5_c;
if(dataToWrite & 0b01000000) LCD_D6_s; else LCD_D6_c;
if(dataToWrite & 0b10000000) LCD_D7_s; else LCD_D7_c;
LCD_E_s;
DelayUs(1);
LCD_E_c;
if(dataToWrite & 0b00000001) LCD_D4_s; else LCD_D4_c;
if(dataToWrite & 0b00000010) LCD_D5_s; else LCD_D5_c;
if(dataToWrite & 0b00000100) LCD_D6_s; else LCD_D6_c;
if(dataToWrite & 0b00001000) LCD_D7_s; else LCD_D7_c;
LCD_E_s;
DelayUs(1);
LCD_E_c;
DelayUs(40);

}

 

void LcdCommand(unsigned char commandToWrite) {
LCD_RS_c;
DelayUs(1);
_lcd_Write(commandToWrite);
}

void LcdData(unsigned char dataToWrite) {
LCD_RS_s;
DelayUs(1);
_lcd_Write(dataToWrite);
}

void LcdWrite(const char * text) {
while(*text) LcdData(*text++);
}

void LcdNumber(uint32_t licz)
{
char arr[10];
itoa(licz,arr,10) ;
LcdWrite(arr);
}

 

void LcdGoto(unsigned char x, unsigned char y) {
LcdCommand(HD44780_DDRAM_SET | (x + (0x40 * y)));
}

void LcdClear(void) {
LcdCommand(HD44780_CLEAR);
DelayMs(2);
}

void LcdInit(void) {
PIOC->PIO_PER = (1<<4);
PIOC->PIO_OER = (1<<4);
PIOC->PIO_PUDR = (1<<4);
PIOC->PIO_PER = (1<<5);
PIOC->PIO_OER = (1<<5);
PIOC->PIO_PUDR = (1<<5);
PIOA->PIO_PER = (1<<16);
PIOA->PIO_OER = (1<<16);
PIOA->PIO_PUDR = (1<<16);
PIOC->PIO_PER = (1<<7);
PIOC->PIO_OER = (1<<7);
PIOC->PIO_PUDR = (1<<7);
PIOA->PIO_PER = (1<<15);
PIOA->PIO_OER = (1<<15);
PIOA->PIO_PUDR = (1<<15);
PIOC->PIO_PER = (1<<6);
PIOC->PIO_OER = (1<<6);
PIOC->PIO_PUDR = (1<<6);

 

DelayMs(5); // oczekiwanie na ustalibizowanie się napiecia zasilajacego
LCD_RS_c;
LCD_E_c;
DelayMs(1);

for(uint8_t i = 0; i < 3; i++) { // trzykrotne powtórzenie bloku instrukcji
LCD_E_s;
_lcd_Write(0x03); // tryb 8-bitowy
DelayMs(1);
LCD_E_c;
DelayMs(5);
}

LCD_E_s;
_lcd_Write(0x02); // tryb 4-bitowy
DelayMs(1);
LCD_E_c;

DelayMs(1);

LcdCommand(HD44780_FUNCTION_SET | HD44780_FONT5x7 | HD44780_TWO_LINE | HD44780_4_BIT); // interfejs 4-bity, 2-linie, znak 5x7
LcdCommand(HD44780_DISPLAY_ONOFF | HD44780_DISPLAY_OFF); // wyłączenie wyswietlacza
LcdCommand(HD44780_CLEAR); // czyszczenie zawartosći pamieci DDRAM
DelayMs(2);
LcdCommand(HD44780_ENTRY_MODE | HD44780_EM_SHIFT_CURSOR | HD44780_EM_INCREMENT);// inkrementaja adresu i przesuwanie kursora
LcdCommand(HD44780_DISPLAY_ONOFF | HD44780_DISPLAY_ON | HD44780_CURSOR_OFF | HD44780_CURSOR_NOBLINK); // włącz lcd, bez kursora i mrugania
}

 

int main(void)

{

/* Initialize the SAM system */

SystemInit();
WDT->WDT_MR = WDT_MR_WDDIS;
PIOC->PIO_PER = LEDB;
PIOC->PIO_OER = LEDB;
PIOC->PIO_PUDR = LEDB;
PIOA->PIO_PER = LEDA;
PIOA->PIO_OER = LEDA;
PIOA->PIO_PUDR = LEDA;

LcdInit(); // inicjalizacja sterownika LCD
LcdClear();
LcdWrite("Test 1"); // wyświetlenie napisu

uint16_t liczbs, liczbm = 0;
while (1)
{
LcdGoto(0,1);
liczbs++;
if (liczbs >= 59)
{
liczbs = 0;
liczbm++;
}
LcdNumber(liczbs);
LcdWrite(":");
LcdNumber(liczbm);


//LcdData("A");
PIOA->PIO_SODR = LEDA;
PIOC->PIO_CODR = LEDB;
DelayMs(500);
//LcdData("0x00");

PIOC->PIO_SODR = LEDB;
PIOA->PIO_CODR = LEDA;
DelayMs(500);

}
}

kh