Laboratorul 1: 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 (fișă tehnică). 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.

Pentru cei care nu se descurcă în cele câteva sute de pagini ale datasheet-ului unui microcontroller am pregătit un mic ghid bazat pe datasheet-ul ATmega324

Click to display ⇲

Click to hide ⇱

Capitole utile din Datasheet ATmega324

  • 1. Pin Configurations - pag. 2
  • 19. USART - pag. 174
    • secțiunile 19.1 - 19.3 pentru overview
    • secțiunea 19.4.1 pentru generarea ceasului
    • secțiunile 19.5 - 19.8 pentru formatul frame-ului, modul de a programa inițializarea și funcționarea
    • secțiunea 19.11 este referința pentru registrele I/O

2. Interfața serială

Interfața serială este cel mai facil mod de a comunica cu microcontroller-ul vostru pentru citirea de date sau trimiterea de comenzi. Din perspectiva microcontroller-ului, comunicația serială se bazează pe doar două linii de date:

  • linie pentru transmisie, notată Tx,
  • linie pentru recepție, notată Rx.

Comunicația este full-duplex, se poate transmite concomitent cu recepția.

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 în figură.

 Transmisia serială

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

2.1 Registre

Microcontroller-ul Atmega324 include două periferice 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 periferic trebuie efectuați următorii pași:

  • alegerea vitezei pentru transmisia de date - baud rate-ul
  • alegerea formatului 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 și TX.

Ambele părți implicate în comunicație trebuie să aibă aceeași configurație! De exemplu, în terminalul serial folosit pe calculator, trebuie configurat același baud rate și același format al cadrului ca cel din codul de pe microcontroller.

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 să folosească același baud rate pentru transmisia corectă a datelor. Una dintre cele mai comune probleme cu USART este setarea diferită a baud rate-ului pe transmițător și pe receptor. Această neconcordanță se manifestă prin recepția unor date greșite (transmițătorul trimite caracterul 'a', receptorul primește caracterul '&')

Descrierea completă pentru:

  • a celor trei registre de control
  • a registrului pentru baud rate
  • a celui pentru buffer-ele de transmisie/recepție

Se gaseste în datasheet la capitolul 19. Registrele au un 'n' la sfârșit care distinge între cele două periferice USART de pe microcontroller-ul nostru. 'n' va lua valoarea 0 pentru USART0, respectiv 1 pentru USART1.

USART Data Register n (UDRn)

Registrul UDR

RXB și TXB sunt buffer-ele de recepție, respectiv transmisie. Ele folosesc aceeași adresă de I/O. Deci RXB este accesat citind din UDRn, TXB scriind în UDRn. Buffer-ul 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.

USART Control and Status Register n A (UCSRnA)

Registrul de control UCSRnA

UCSRnA este registrul de stare al controller-ului de comunicație. Biții cei mai importanți sunt:

  • RXCnReceive Complete – devine 1 când există date primite și necitite. Când buffer-ul de recepție este gol, bitul este resetat automat
  • TXCnTransmit Complete – devine 1 când buffer-ul de transmisie devine gol
  • UDREnData Register Empty – devine 1 când buffer-ul de transmisie poate accepta noi date

USART Control and Status Register n B (UCSRnB)

Registrul de control UCSRnB

UCSRnB este un registru de control. Biții importanți:

  • RXCIEnReceive Complete Interrupt Enable – când este 1, controller-ul de comunicație va genera o întrerupere când au fost primite date
  • TXCIEnTransmit Complete Interrupt Enable – când este 1, controller-ul de comunicatie va genera o întrerupere când buffer-ul de transmisie devine gol
  • UDRIEnData Register Empty Interrupt Enable – când este 1, controller-ul de comunicație va genera o întrerupere când buffer-ul de transmisie mai poate accepta date
  • RXENnReceiver Enable – dacă este 0, nu se pot recepta date
  • TXENnTransmitter 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

USART Control and Status Register n C (UCSRnC)

Registrul de control UCSRnC

UCSRnC este tot un registru de control. Biții importanți:

  • UMSELnMode Select – 0 pentru funcționare asincronă, 1 pentru funcționare sincronă
  • UPMn1, UPMn0Parity Mode - Fiind vorba de doi biți, împreună pot avea 4 valori posibile, detaliate în tabelul ce urmează:

 Biții UPM

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

 Biții USBS

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

Biții UCSZ

USART Baud Rate Registers (UBRRn)

Registrul UBRRn

UBRRn este registrul care selectează baud rate-ul. Are 12 biți. Primii 4 se află în UBRRnH, ceilalți 8 în UBRRnL. Valoarea pe care o scriem î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.

Pentru valorile UBRR pentru 12MHz cautati valorile in acest link

2.2 Exemplu de utilizare

void USART0_init(unsigned int baud) {
    /* setează baud rate */
    UBRR0 = baud;
    /* UBRR0 este un registru pe 16 biți, la nivel de compilator se vor face doua scrieri de 8 biti */
 
    /* 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ă când buffer-ul e gol */
    while(!(UCSR0A & (1<<UDRE0)));
 
    /* pune datele în buffer; transmisia va porni automat în urma scrierii */
    UDR0 = data;
}

Scrieri pe 16 biți:

(3 << x)

Pentru biți de configurație care se găsesc întotdeauna unul după altul se folosește și o mască cu mai mulți biți shiftați cu index-ul celui mai din dreapta: (3 << UCSZ00) înlocuiește astfel (1 << UCSZ01) | (1 << UCSZ00)

(1 << x) | (1 << y)

De cele mai multe ori o să facem măști compuse, pe care le vom aplica unui registru I/O în același timp.

Atenție! Pot doar să compun măști pentru aceeași operație, nu pot aplica o mască SAU în același timp cu o mască ȘI pentru că rezultatul ar fi complet eronat!

Pentru ca cele două dispozitive, în cazul nostru PC-ul și placa de laborator, să poată comunica între ele prin USART, trebuie configurate identic. Dacă placa este configurată cu baud rate 115200, 9 biți de date, 1 bit de stop și fără paritate atunci PC-ul trebuie configurat exact la fel pentru a comunica.

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 controller-ul Hitachi 44780 sunt 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 tabelul de mai jos.

Pin number Symbol I/O Function
1 Vss - Power supply (GND)
2 Vdd - Power supply (+5V)
3 Vee - Contrast adjust
4 RS I Register select: 0 - Instruction, 1 - Data
5 R/W I 0 - Write to LCD module, 1 - Read from LCD module
6 E I Enable signal
7 D0-D7 I/O Data bus line 0 (LSB) - 7 (MSB)

Interfața de comunicaț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:

  • extins - pe 8 biți
  • restrâns - pe 4 biți.

Placa 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)
  • 4 pini de date (D4-D7).

Toți cei 7 pini pot fi mapați în același port de I/O al microcontroller-ului simplificând astfel și software-ul de control al LCD-ului.

Click to display ⇲

Click to hide ⇱

 Interfațarea unui LCD 2x16 cu microcontroller-ul ATmega16 (compatibil pin la pin cu ATmega324)

Din punct de vedere hardware interfațarea se efectuează pin la pin cu microcontroller-ul, precum în schema alaturata. 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

Nu vom folosi CGRAM la laboratorul de astazi, dar poate sa va fie util pentru proiect.

  • conține pattern-urile de pixeli (8 linii, 5 coloane) afișați pentru caracterele predefinite
  • anumite locații pot fi rescrise pentru a crea 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 controller-ul LCD-ului se realizează printr-o serie de instrucțiuni, descrise în tabelul de mai jos.

Instruction RS R/W D7 D6 D5 D4 D3 D2 D1 D0 Description Execution time
Clear display 0 0 0 0 0 0 0 0 0 1 Clears display and returns cursor to the home position (address 0x00) 1.64ms
Cursor home 0 0 0 0 0 0 0 0 1 * Returns cursor to home position (address 0x00) and resets display shift offset 1.64ms
Entry mode set 0 0 0 0 0 0 0 1 I/D S Sets cursor move direction (I/D), enables display shift (S) 40us
Display control 0 0 0 0 0 0 1 D C B Sets display on/off (D), cursor on/off (C) and cursor blink (B) 40us
Cursor/display shift 0 0 0 0 0 1 S/C R/L * * Sets cursor-move or display-shift (S/C), shift direction (R/L) 40us
Function set 0 0 0 0 1 DL N F * * Sets interface data length (DL), number of display lines (N) and character font (F) 40us
Set CGRAM address 0 0 0 1 CGRAM address Sets the CGRAM address. CGRAM data will be modified by read and write intructions 40us
Set DDRAM address 0 0 1 DDRAM address Sets the DDRAM address. DDRAM data will be modified by read and write intructions 40us
Read busy-flag and address counter 0 1 BF CGRAM/DDRAM address Reads Busy-flag (BF) and CGRAM or DDRAM address counter (depending on previous instruction) 0us
Write to CGRAM or DDRAM 1 0 Data Writes data to CGRAM or DDRAM 40us
Read from CGRAM or DDRAM 1 1 Data Reads data from CGRAM or DDRAM 40us

Biții din tabel au următoarea semnificație:

  • Setarea direcției de deplasare a cursorului:
    • I/D – Incrementează (1) / Decrementează (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 controller-ului (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. Mai multe informatii gasiti in link-ul urmator

Click to display ⇲

Click to hide ⇱

 Protocolul de comunicatie cu controller-ul Hitachi 44780

  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 așteaptă cel puțin 230ns (PWeh: enable pulse width)
      4. Se dezactivează pinul E
    • Interfața 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 așteaptă cel puțin 230ns (PWeh: enable pulse width)
      4. Se dezactivează pinul E
      5. Se asteaptă cel putin 270ns (Tcycle - 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 așteaptă cel puțin 230ns (PWeh: enable pulse width)
      9. Se dezactivează pinul E
  4. Se așteaptă cel puțin 10ns (Th: data hold time)
  5. Se pot dezactiva pinii RS, R/W și D0-D7
  6. Se așteaptă 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.

4. Exerciții

Capitole utile din Datasheet ATmega324

  • 1. Pin Configurations - pag. 2
  • 19. USART - pag. 174
    • secțiunile 19.1 - 19.3 pentru overview
    • secțiunea 19.4.1 pentru generarea ceasului
    • secțiunile 19.5 - 19.8 pentru formatul frame-ului, modul de a programa inițializarea și funcționarea
    • secțiunea 19.11 este referința pentru registrele I/O

Task 0 (1p) Rulați exemplul pentru USART. Pentru configurările serialei, vedeți fisierul usart.c din schelet.

Task 1 (3p) Configurați USART0 cu următorii parametri: baud rate 19200, 8 biți de date, 2 bit de stop, fara paritate. Transmiteți către PC câte un mesaj pentru fiecare eveniment de apăsare/lăsare a unui buton (ex: se apasă PD6, se transmite “PD6 apăsat”, se lasă PD6, se transmite “PD6 lăsat”, câte o singură dată pe apăsare).

Task 2 (2p) Comandați prin serială generarea in cod Morse a numelui vostru, folosind buzzer-ul.

În scheletul de laborator găsiți variabila morse_alphabet ce conține literele alfabetului în cod Morse. Pe serială veți trimite caractere ASCII litere mici (ex: 'a', 'b', 'c', etc.). Placa va citi caractere de pe serială și va reda semnalul Morse corespunzător de fiecare dată când citește un caracter valid.

Task 3 (1p) Rulați exemplul pentru LCD.

Task 4 (3p) Implementați funcțiile din bibliotecă:

  • LCD_putCharAt
  • LCD_printAt

Puteți folosi funcțiile LCD_writeInstr și LCD_writeData.

  1. Utilizați funcțiile implementate pentru a afișa un mesaj pe prima linie a LCD-ului.
  2. Folositi implementarea anterioara pentru a afisa un mesaj trimis pe USART.

Mesajul trebuie să poată fi deplasat la stânga cu butonul PB2 și la dreapta cu butonul PD6. Limitați deplasarea mesajului astfel încât acesta să nu iasă de pe ecran.

Trebuie să folosiți o instrucțiune a LCD-ului (apelată cu LCD_writeInstr) pentru a poziționa cursorul la poziția dorită, apoi să scrieți datele către LCD (cu LCD_writeData). Codul instrucțiunilor LCD le puteți vedea fie în <tabref instructiuni_lcd>, fie în fișierul lcd.h prezent în scheletul de laborator.

Task 5: Bonus USART (1p) Configurați printf astfel încât să printeze direct pe USART0. Refaceți exercițiul 1 folosind printf.

Tips pentru printf (click to show)

Click to display ⇲

Click to hide ⇱

După cum știți de la programare, printf este echivalent cu fprintf(stdout,… ), cu alte cuvinte, folosește stdout ca fișier în care să scrie. Pentru a putea utiliza printf corect pe AVR, trebuie ca stdout trebuie să pointeze către o structură validă FILE.

struct __file {
	char	*buf;		/* buffer pointer */
	unsigned char unget;	/* ungetc() buffer */
	uint8_t	flags;		/* flags, see below */
#define __SRD	 0x0001		/* OK to read */
#define __SWR	 0x0002		/* OK to write */
#define __SSTR 	0x0004		/* this is an sprintf/snprintf string */
#define __SPGM	 0x0008		/* fmt string is in progmem */
#define __SERR	 0x0010		/* found error */
#define __SEOF	 0x0020		/* found EOF */
#define __SUNGET 0x040		/* ungetc() happened */
#define __SMALLOC 0x80		/* handle is malloc()ed */
	int	size;		/* size of buffer */
	int	len;		/* characters read or written so far */
	int	(*put)(char, struct __file *);	/* function to write one char to device */
	int	(*get)(struct __file *);	/* function to read one char from device */
	void	*udata;		/* User defined and accessible data. */
};

#typedef struct __file FILE

Pentru un fișier în care trebuie doar să scriem, nu ne trebuie decât câmpul flags și câmpul put. put este un pointer către o funcție care scrie un caracter, iar flags este un câmp cu flag-uri, printre care există și un flag care spune că putem scrie în fișier, __SWR. Este suficient să faceți o structură cu put și flags corecte și să pointeze stdout către ea ca să vă meargă printf.

5. Resurse

pm/lab/lab1.txt · Last modified: 2020/03/03 14:31 by daniel.berbece
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0