Interfata USART, LCD

1. Ce este un datasheet?

Orice componentă electronică, de la un senzor de temperatură la un microcontroller la un motor la… orice are un document în care este descrisă în detaliu. Acest document se numește datasheet. Pe parcursul laboratoarelor și al proiectului de PM aceste datasheet-uri vor fi cei mai buni prieteni ai voștri. În ele veți găsi toate detaliile necesare pentru înțelegerea funcționării și folosirea componentei respective.


2. Interfața serială

Interfața serială este cel mai facil mod de a comunica cu microcontrollerul vostru pentru citirea de date sau trimiterea de comenzi. Din perspectiva microcontrollerului, comunicația serială se bazeaza pe doar doua linii:
  • linie pentru transmisie, notata Tx,
  • linie pentru recepție, notata Rx.
Transmisia asincronă de date se face la nivel de cadre(frames), fiecare cadru fiind format din mai mulți biți, având formatul descris in figura 1.




Se transmite un bit de start, apoi un cuvânt de date. Urmează un bit de partitate, care este opțional, iar rolul lui este de a face o verificare simplă a corectitudinii datelor, și unul sau doi biți de stop.

2.1 Registre

Microcontrollerul Atmega324 include doua controllere USART (Universal Synchronous-Asynchronous Receiver/Transmitter) pentru interfața serială, care sunt controlate de registrele descrise în secțiunile următoare. În partea de inițializare a acestui controller trebuie efectuați următorii pași:
  • alegerea baud rate-ului de transmisie de date
  • alegerea formatul cadrului (câți biți de date, de stop, dacă va conține sau nu bit de partitate)
  • activarea transmisiei și recepției datelor pe liniile Rx si Tx.
Baud rate este numărul de simboluri/pulsuri pe secundă al semnalului. În esență, reprezintă viteza de transmisie și este foarte important ca și transmițătorul și receptorul sa foloseasca același baud rate pentru transmisia corecta a datelor. Una dintre cele mai comune probleme cu USART este setarea diferită a baud rate pe transmițător față de receptor și se manifestă prin recepția unor date greșite (transmițătorul trimite caracterul 'a', receptorul primește caracterul '&')

Descrierea completă a celor trei registre de control, a registrului pentru baud rate și a celui pentru bufferele de transmisie/recepție o puteți găsi în datasheet la capitolul 19, unde 'n' poate lua valoarea 0 pentru USART0, respectiv 1 pentru USART1.

UDRn - USART Data Register n



RXB și TXB sunt bufferele de recepție, respectiv transmisie. Ele folosesc aceeași adresă de I/O. Deci RXB este accesat citind din UDRn, TXB scriind în UDRn. Bufferul de transmisie poate fi scris numai atunci când bitul UDRE (USART Data Register Empty) din portul UCSRnA este 1. În caz contrar, scrierile vor fi ignorate.

UCSRnA (USART Control and Status Register n A)


UCSRnA este primul registru de stare al controllerului de comunicație. Biții cei mai importanți sunt:
  • RXCn – Receive Complete – devine 1 când există date primite și necitite. Când buffer-ul de recepție este gol, bitul este resetat automat
  • TXCn – Transmit Complete – devine 1 când bufferul de transmisie devine gol
  • UDREn – Data Register Empty – devine 1 când bufferul de transmisie poate accepta noi date

UCSRnB (USART Control and Status Register n B)


UCSRnB este al doilea registru de control. Biții importanți:
  • RXCIEn – Receive Complete Interrupt Enable – când este 1, controllerul de comunicație va genera o întrerupere când au fost primite date
  • TXCIEn – Transmit Complete Interrupt Enable – când este 1, controllerul de comunicatie va genera o întrerupere când bufferul de transmisie devine gol
  • UDRIEn – Data Register Empty Interrupt Enable – când este 1, controllerul de comunicație va genera o întrerupere când bufferul de transmisie mai poate accepta date
  • RXENn – Receiver Enable – dacă este 0, nu se pot recepta date
  • TXENn – Transmitter Enabler – dacă este 0, nu se pot transmite date
  • UCSZn2 – împreună cu UCSZ1 și UCSZ0 din portul UCSRC, selectează dimensiunea unui cuvânt de date

UCSRnC (USART Control and Status Register n C)


Al treilea registru de control. Biții importanti:
  • UMSELn – Mode Select – 0 pentru funcționare asincronă, 1 pentru funcționare sincronă
  • UPMn1, UPMn0 – Parity Mode

  • USBSn – Stop Bit Select – 0 pentru un bit de stop, 1 pentru doi biți de stop

UCSZn1, UCSZn0 – împreună cu UCSZn2 din portul UCSRnB, decid dimensiunea cuvântului de date

UBRRn (USART Baud Rate Registers)


UBRRn este registrul care decide baud rate-ul. Are 12 biți. Primii 4 se află în UBRRnH, ceilalți 8 în UBRRnL. Valoarea de scris în UBRRn depinde de frecvența procesorului și de baud rate-ul dorit. În tabelul următor găsiți valorile pentru frecvența de 16 Mhz.


2.2 Exemplu de utilizare

void USART0_init(unsigned int baud) {
    /* setează baud rate */
    UBRR0H = (unsigned char)(baud>>8);
    UBRR0L = (unsigned char)baud;
 
    /* pornește transmițătorul */
    UCSR0B = (1<<TXEN0);
 
    /* setează formatul frame-ului: 8 biți de date, 2 biți de stop, fără paritate */
    UCSR0C = (1<<USBS0)|(3<<UCSZ00);
}
 
void USART0_transmit(unsigned char data) {
    /* așteaptă până bufferul e gol */
    while(!(UCSR0A & (1<<UDRE0)));
 
    /* pune datele în buffer; transmisia va porni automat în urma scrierii */
    UDR0 = data;
}
Pentru a putea comunica între ele prin USART cele două dispozitive, în cazul nostru PC-ul si placa de laborator, trebuie configurate identic. Dacă placa este configurată cu baud rate 115200, 9 biți de date, 1 bit de stop si fără paritate atunci PC-ul trebuie configurat exact la fel pentru a merge comunicația.


3. Interfațarea unui LCD text


Display-urile, și în principal cele care folosesc cristale lichide (eng. LCD - liquid crystal display), reprezintă una din cele mai folosite moduri de a prezenta informații utilizatorului sau de a oferi un aspect profesional unui dispozitiv. O altă utilizare importantă a display-urilor este de a ușura procesul de debugging pe un sistem embedded.
De obicei, un LCD conține pe lângă afisaj și un controller integrat care simplifică folosirea display-ului. Un controller LCD text uzual este Hitachi 44780, ce oferă o modalitate simplă de interfațare între un microcontroller și afișajulul LCD-ului. Din punct de vedere al costului, display-urile care se bazează pe controllerul Hitachi 44780sunt de obicei relativ ieftine și răspândite, putând fi ușor recuperate din dispozitive mai vechi și refolosite.

3.1 Interfața de conectare


Conectorul folosit în laborator pentru LCD-ul text are 14 pini, dispuși pe o linie, cu distanță de 0.1” (inch) între pini, semnificația lor fiind cea descrisă în table 1.

Interfața de communicație este una paralelă, permițând astfel să se efectueze scrieri sau citiri de date într-un mod simplu și rapid. Controller-ul Hitachi 44780 suportă 2 moduri de comunicație: un mod extins, pe 8 biți, și un mod restrâns, pe 4 biți.
Plăcuța de laborator folosește o interfațare în modul restrâns, pe 4 biți de date. Se preferă această abordare deoarece folosește doar 7 pini ai microcontroller-ului pentru controlul complet al LCD-ului (chiar dacă la jumătate din viteză): 3 pini de control (RS, R/W și E) și 4 pini de date (D4-D7). Toți cei 7 pini pot fi mapati în același port de I/O al microcontrollerului simplificând astfel și software-ul de control al LCD-ului.



Din punct de vedere hardware interfațarea se efectuează pin la pin cu microcontroller-ul, precum în schema din firuta 11 Pinii Vss și Vdd se conectează la masă, respectiv la alimentare. Pinul 3 este pinul de contrast și se conectează direct la masă, pentru contrast maxim, sau printr-un potențiometru între Vss și Vdd (divizor de tensiune) dacă se dorește un reglaj al constrastului.
În cazul în care se dorește interfațarea în modul extins, cu 8 biți de date, este suficient să se conecteze toți pinii de date (D0-D7) la același port, iar pinii de control (RS, R/W și E) la un alt port. Software-ul de control va trebui scris ținând cont de această structură.

3.2 Modul de funcționare


Funcționarea unui LCD text bazat pe controller-ul Hitachi 44780 este bazată pe modificarea valorilor din memoriile interne ale controller-ului. Modificarea acestor valori se face prin transmiterea de instrucțiuni către controller folosind pinii de control și de date, conform protocolului înțeles de către controller.

3.2.1 Memorii

Memoriile controller-ului Hitachi 44780 sunt:
  • DDRAM - Display Data RAM
    • stochează caracterele afișate pe display
    • capacitate de 80 x 8 biți
    • DDRAM address corespunde poziției cursorului și reprezintă locația care va fi modificată de o instrucțiune de scriere
    • adresa 0x00 corespunde primului caracter de pe prima linie a display-ului (colț stânga-sus)
    • fiecare byte reprezintă codul caracterului afișat la poziția respectivă
  • CGRAM - Character Generator RAM
    • conține pattern-urile de pixeli (8 linii, 5 coloane) afișați pentru caracterele predefinite
    • anumite locații pot fi rescrise pentru a creea pattern-uri pentru caractere noi (custom)
    • organizată pe cuvinte de câte 8 biți
    • începând cu adresa 0x00, fiecare byte reprezintă o linie dintr-un caracter, 8 linii fiind grupate pentru a forma pattern-ul unui caracter (ex: primul caracter custom începe la adresa 0x00 și se sfârșește la 0x07)
    • sunt folosiți efectiv doar cei mai puțin semnificativi 5 biți ai unui byte deoarece pattern-ul unui caracter conține 5 coloane

3.2.2 Instrucțiuni

Comunicarea dintre microcontroller și controllerul LCD-ului se realizează printr-o serie de instrucțiuni, descrise în table 2.


Biții din table 2 au următoarea semnificație:
  • Setarea direcției de deplasare a cursorului:
    • I/D – incrementeaza (1) / decrementeaza (0) cursorul după fiecare byte scris
    • S – shiftează (1) display-ul atunci când este scris un caracter
  • Activarea display-ului / cursorului
    • D – Activează display-ul on (1) / off (0)
    • C – Activează cursorul on (1) / off (0)
    • B – Setează cursorul pe blink on (1) / off (0)
  • Mutarea cursorului / Shiftarea display-ului
    • S/C – Activează shiftarea display-ului on (1) / off (0)
    • R/L – Setează direcția de shiftare right (1) / left (0)
  • Setarea lățimii interfeței
    • DL – Lățimea interfeței de conectare este 8 biți (1) / 4 biți (0)
    • N – Numărul de linii afișate pe display 1 linie (0) / 2 linii (1)
    • F – Fontul caracterelor afișate 5×10 dots (1) / 5×8 dots (0)
  • Citirea stării controller-ului
    • BF – 1 - controller-ul procesează informațiile primite și nu poate primi alte instrucțiuni, 0 - controller-ul poate accepta instrucțiuni
Mai multe informații referitoare la structura controllerului (mod de funcționare, registre etc.) pot fi găsite în datasheet-ul său.

3.3 Protocolul de comunicație

Pentru a comunica cu controller-ul Hitachi 44780 un microcontroller trebuie să respecte protocolul impus de către controller. Acesta constă în anumite restricții asupra ordinii și duratelor minime ale semnalelor care sunt transmise pe pinii de control/date. Mai jos este descris pe scurt acest protocol, el fiind prezentat în întregime în datasheet.
  1. Se setează pinul RS pentru a indica dacă urmează o instrucțiune de control sau de date
  2. Se setează pinul R/W pentru a indica dacă urmează o instrucțiune de citire sau de scriere
  3. Se transferă codul instrucțiunii
    • Interfața pe 8 biți (care nu este cea din laborator):
      1. Se setează pinii D0-D7 la valoarea biților 0-7 ai instrucțiunii
      2. Se activează pinul E
      3. Se asteaptă cel puțin 230ns (PWeh: enable pulse width)
      4. Se dezactiveaza pinul E
    • Interfată pe 4 biți:
      1. Se setează pinii D4-D7 la valoarea biților 4-7 ai instrucțiunii
      2. Se activează pinul E
      3. Se asteaptă cel puțin 230ns (PWeh: enable pulse width)
      4. Se dezactiveaza pinul E
      5. Se asteaptă cel putin 270ns (Tcyce - PWeh: enable cycle time - enable pulse width)
      6. Se setează pinii D4-D7 la valoarea biților 0-3 ai instrucțiunii
      7. Se activează pinul E
      8. Se asteaptă cel puțin 230ns (PWeh: enable pulse width)
      9. Se dezactiveaza pinul E
  4. Se asteaptă cel puțin 10ns (Th: data hold time)
  5. Se pot dezactiva pinii RS, R/W și D0-D7
  6. Se asteaptă timpul de execuție al instrucțiunii sau se citește Busy Flag până când devine 0

3.4 Bibliotecă LCD

Pentru a facilita lucrul cu LCD-ul, vom defini (implementa) o bibliotecă de funcții pentru interacțiunea cu controller-ul Hitachi 44780. Un API minimal pentru această bibliotecă conține:
void LCD_init(void);                                // Initializare LCD considerand o interfatare cu 4 pini de date.
uint8_t LCD_read(void);                             // Executa secventa de citire a unui octet de date de la LCD.
uint8_t LCD_readStatus(void);                       // Citeste starea LCD-ului (contine busy flag).
uint8_t LCD_readData(void);                         // Citeste un octet din ultima memorie folosita (DDRAM sau CGRAM).
uint8_t LCD_isBusy(void);                           // Returneaza starea LCD-ului: 1 - busy, 0 - available
void LCD_waitNotBusy(void);                         // Asteapta pana cand LCD-ul devine disponibil pentru o noua comanda.
void LCD_write(uint8_t data);                       // Executa secventa de trimitere a unui octet de date catre LCD.
void LCD_writeInstr(uint8_t instr);                 // Trimite o instructiune de control catre LCD.
void LCD_writeData(uint8_t data);                   // Trimite o instructiune de scriere date catre LCD.
void LCD_putChar(char c);                           // Afiseaza caracterul pe LCD la adresa curenta.
void LCD_putCharAt(uint8_t addr, char c);           // Afiseaza caracterul pe LCD la adresa primita.
void LCD_print(const char* msg);                    // Afiseaza string-ul pe LCD incepand de la adresa curenta.
void LCD_printAt(uint8_t addr, const char* msg);    // Afiseaza string-ul pe LCD incepand de la adresa primita.

3.5 Exemplu de comunicație

Pentru folosirea controller-ului în modul de interfațare cu 4 pini de date el necesită o secvență de initializare care începe cu transferul valorii binare 0100 pe cei 4 pini, conform protocolului de comunicare, și este urmată de trimiterea comenzilor normale de configurare (pe 8 biți). Mai jos este prezentată implementarea funcției de inițializare din biblioteca LCD-ului care realizează această secvență.
Observați folosirea în implementare a macro-urilor definite în header-ul bibliotecii LCD, pentru a îmbunătății lizibilitatea codului și, de asemenea, pentru a menține genericitatea bibliotecii, fiind necesară doar definirea pinilor microcontroller-ului legați la LCD pentru a putea folosi biblioteca și în alte proiecte.
// Mask pentru bitii de date
#define LCD_DATA_MASK           (_BV(LcdD4)|_BV(LcdD5)|_BV(LcdD6)|_BV(LcdD7))
 
// Mask pentru bitii de control
#define LCD_CTRL_MASK           (_BV(LcdRS)|_BV(LcdRW)|_BV(LcdE))
 
// Controleaza pinul RS
#define LCD_RS_HIGH()           do { LcdCTRL_PORT |=  _BV(LcdRS); } while(0)
#define LCD_RS_LOW()            do { LcdCTRL_PORT &= ~_BV(LcdRS); } while(0)
 
// Controleaza pinul RW
#define LCD_RW_HIGH()           do { LcdCTRL_PORT |=  _BV(LcdRW); } while(0)
#define LCD_RW_LOW()            do { LcdCTRL_PORT &= ~_BV(LcdRW); } while(0)
 
// Controleaza pinul E
#define LCD_ENABLE()            do { LcdCTRL_PORT |=  _BV(LcdE); } while(0)
#define LCD_DISABLE()           do { LcdCTRL_PORT &= ~_BV(LcdE); } while(0)
 
// Delay-ul minim necesar pentru operatiile cu semnalul E -> 250ns
// Acopera toate celelalte delay-uri si este jumatate din Enable cycle time (T_cycE)
#define LCD_DELAY()             __builtin_avr_delay_cycles(250 / (1000000000/F_CPU))
Observați folosirea operațiilor pe biți pentru a modifica doar cei 4 biți corespunzători pinilor de comunicație cu LCD-ul, din registrul de I/O al portului digital.
void LCD_init(void)
{
    // setam pinii de date ca pini de iesire
    LcdDATA_DDR |= LCD_DATA_MASK;
    // setam pinii de comenzi ca pini de iesire
    LcdCTRL_DDR |= LCD_CTRL_MASK;
 
    // intram in modul comenzi: RS - low, RW - low
    LCD_RS_LOW();
    LCD_RW_LOW();
 
    // asteptam timpul de set-up pentru RS si RW
    LCD_DELAY();
 
    // initializarea pentru 4 fire de date necesita transferarea lui 0100 -> D4-D7
 
    // E pe high incepe transferul
    LCD_ENABLE();
 
    // scriem cei mai semnificativi 4 biti pe firele de date, fara a modifica alti biti
    LcdDATA_PORT = (LcdDATA_PORT & ~LCD_DATA_MASK) |
                   (0 << LcdD7) |
                   (0 << LcdD6) |
                   (1 << LcdD5) |
                   (0 << LcdD4);
 
    // asteptam timpul minim de set-up
    LCD_DELAY();
    // E pe low termina transferul
    LCD_DISABLE();
    // asteptam timpul minim pana la urmatorul E (contine timpul de hold)
    LCD_DELAY();
 
    // transmitem configuratia initiala
    // 4 biti de date, 2 linii, font 8x5
    LCD_writeInstr(LCD_INSTR_4wire);
    // display pornit, cursor afisat, blink activat
    LCD_writeInstr(LCD_INSTR_displayConfig);
    // auto-increment, fara shiftare
    LCD_writeInstr(LCD_INSTR_incNoShift);
    // sterge display
    LCD_writeInstr(LCD_INSTR_clearDisplay);
}