This is an old revision of the document!
Laboratorul 1: USART. Debugging
Ca în toate lucrările de inginerie, bug-uri pot și vor apărea si în sisteme incorporate. În condiții de funcționare, este important să avem o modalitate de comunicare cu dispozitivul integrat. Pentru o imagine de ansamblu asupra metodelor de depanare posibile, vom face o scurtă introducere, apoi vom studia în detaliu interfața serială USART, folosită în mod uzual pentru comunicația serială dintre două dispozitive.
1. Ce este diferit fata de depanarea software uzuala?
Motivul pentru care depanarea embedded este mai dificilă decât depanarea obișnuită a software-ului provine din mai multe probleme:
Sistemele embedded sunt mult mai complicat de depanat deoarece produsele dedicate debugging-ului hardware (Software, IDE, Lauterbach, etc) sunt mult mai scumpe si greu de folosit decat depanarea software.
Folosirea unui debugger generic (ex. Remote GDB) vine la pachet cu o serie de probleme ce pot aparea. Debuggerele generice necesita o stiva de retea conforma cu hardware-ul folosit. Chiar daca am implementa o astfel de stiva , nu putem fii siguri ca aceasta functioneaza perfect si ar necesita foarte mult timp.
Chiar dacă ați avut un debugger specializat (de exemplu, o sondă Lauterbach), cel mai probabil veți avea nevoie de configurații specializate pentru debugger, pentru a lucra cu hardware-ul vostru (de exemplu, Practice Scripting Language for Lauterbach T32)
Depanarea invazivă poate afecta comportamentul codului vostru - gândiți-vă la RTOS (sisteme de operare în timp real), SMP (sisteme multiprocesor) sau la circuitul dvs. (de exemplu, modificați circuitul pentru a măsura intensitatea curentului).
Hardware-ul in fazele incipiente pot avea erori de proiectare ceea ce influenteaza procesul de debug.
Chiar și tipărirea mesajelor de eroare ar putea să nu funcționeze, deoarece uneori trebuie să implementați o astfel de funcție și ar putea avea bug-uri în sine.
Cu toate acestea, principiile de debugging sunt aceleasi ca si în cazul software-ul de nivel înalt: Trebuie sa comparați ce se dorește de la sistem (cod / circuit) cu ceea ce, de fapt, face si, pentru asta, ai nevoie de vizibilitate.
2. Instrumente necesare
Vizibilitatea la nivel hardware se realizează printr-o formă de InputOutput (dacă este disponibilă):
Depanare LED - verificarea stării true/false
mesaje pe interfața serială / USART - depanare prin USART, bluetooth, etc
Debuggere avansate pentru afisarea si modificarea memoriei / registrelor (a se vedea JTAG de mai jos)
Loopbacking (conectarea ieșirilor la intrări) poate oferi informații despre modul în care comenzile sunt trimise dispozitivelor externe.
Instrumente de măsurare:
Osciloscop
Analizoare logice (pentru semnale digitale)
Analizoare de protocol (pentru protocoale încorporate, ar fi I2C, SPI, etc)
Debuggere bazate pe JTAG - pot avea funcții foarte avansate: citire/modificare memorie, interoperarea cu kernelul, oprire ceas sistem, etc
Exemplu de flux de depanare
Un exemplu de flux de depanare ar putea fi următorul:
Verificați de două ori datasheet-ul și schema. Verificati daca accesati registrele potrivite. O alta probleme poate fii conectarea gresita dintre periferice si pini.
In prima faza verificam daca este posibila depanarea folosind mesaje de debug.
Daca ai o stiva ethernet poti lua in considerare folosirea SSH, NFS, etc pentru a folosii depanarea prin mesaje.
In cazul in care avem o stiva de retea , verificam daca avem acces la protocoale mai simple precum UART. Testam conectand prin UART un periferic de exemplu un PC, modul Bluetooth, LCD , etc.
De asemenea in anumite cazuri de probleme hardware , putem folosii si depanarea prin LED-uri.
Izolați problema folosind instrumente de măsura: multimetre, osciloscoape, analizoare logice.
Daca dorim sa izolam o problema hardware , folosim instrumente de masura: multimetrul, osciloscopul, analizor logic.
In cazul in care stim ca dispozitivul nostru este compatibil cu debuggere hardware, de exemplu JTAG , Lauterbach, etc, este de multe ori cea mai eficienta metoda de debug.
3. Interfața serială USART
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ă.
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.
Microcontroller-ul ATmega324p include doua periferice USART (Universal Synchronous-Asynchronous Receiver/Transmitter) pentru interfața serială. În partea de inițializare a acestui periferic trebuie efectuați următorii pași:
alegerea vitezei pentru transmisia de date - baud rate-ul (valori uzuale: 9600, 19200, 38400, 57600, 115200)
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
.
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 configurat în mod asincron 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 '&')
Pentru ca două dispozitive, în cazul nostru PC-ul și placa de laborator, să poată comunica între ele prin USART în mod asincron, 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.1 Registre
Descrierea completă pentru:
cele trei registre de control
registrul pentru baud rate
buffer-ele de transmisie/recepție
Se gaseste în Datasheet Atmega 324p în capitolul 21.12.
Registrele au un 'n' la sfârșit care distinge între mai multe periferice USART ce pot exista pe un microcontroller (pe ATmega324P 'n' va lua valoarea 0 sau 1 corespunzatoare USART0, respectiv USART1).
USART Data Register n (UDRn)
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)
UCSRnA
este registrul de stare al controller-ului 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 buffer-ul de transmisie devine gol
UDREn – Data Register Empty – devine 1 când buffer-ul de transmisie poate accepta noi date
USART Control and Status Register n B (UCSRnB)
UCSRnB
este un registru de control. Biții importanți:
RXCIEn – Receive Complete Interrupt Enable – când este 1, controller-ul de comunicație va genera o întrerupere când au fost primite date
TXCIEn – Transmit Complete Interrupt Enable – când este 1, controller-ul de comunicatie va genera o întrerupere când buffer-ul de transmisie devine gol
UDRIEn – Data 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
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
USART Control and Status Register n C (UCSRnC)
UCSRnC
este tot un registru de control. Biții importanți:
UMSELn – Mode Select – 0 pentru funcționare asincronă, 1 pentru funcționare sincronă
UPMn1, UPMn0 – Parity Mode - Fiind vorba de doi biți, împreună pot avea 4 valori posibile, detaliate în tabelul ce urmează:
USART Baud Rate Registers (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. Tabelul de mai jos este folosit pentru clock-uri de 8Mhz, 11.0592MHz si 14.7456MHz, in cazul placii noastre, clock-ul este de 12MHz , putem cauta valoarea pentru baud rate aici, sau o putem calcula folosind formula: (F_CPU/(UART_BAUD_RATE*16)-1)
Unde F_Cpu este frecventa clock-ului, UART_BAUD_RATE este ales de noi (ex. 4800, 9600, 14400).
Este de dorit alegerea unui baud rate care să poată fi obținut exact din frecvența de ceas. În caz contrar se definește o toleranță (eroarea maximă a baud rate-ului) pentru care comunicația se poate realiza în condiții acceptabile. Dacă doriți să aprofundați subiectul, găsiți multe informații
aici
Pentru a seta baud rate-ul exista si libraria util/setbaud.h care este menita sa simplifice procesul. Este nevoie sa setati frecventa ceasului procesorului. Gasiti
aici mai multe detaii despre aceasta si exemple de utilizare.
3.2 Exemplu de utilizare
void USART0_init(unsigned int baud_rate)
{
/* seteaza baud rate */
UBRR0H = (unsigned char)(baud_rate>>8);
UBRR0L = (unsigned char)baud_rate;
/* porneste transmitatorul */
UCSR0B = (1 << TXEN0) | (1 << RXEN0);
/* seteaza formatul frame-ului: 8 biti de date, 2 bit de stop, fara 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;
}
char USART0_receive()
{
/* asteapta cat timp bufferul e gol */
while (!(UCSR0A & (1 << RXC0)));
/* returneaza datele din buffer */
return UDR0;
}
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!
4. Exerciții
Schelet
Task 1
Folosind scheletul de laborator configureaza USART0 cu urmatorii parametrii : baud rate 28800, 8 biti de date, 2 bit de stop, fara paritate. Transmiteti catre PC mesajul “Butonul 1 a fost apasat” daca butonul 1 (PB2) este apasat.
Definițiile din AVR Libc care au nevoie de calculul frecvenței se bazează pe un parametru F_CPU furnizat de către compilator.
Cum noi folosim PlatformIO, nu uitați să setați ceasul de 12MHz din
platformio.ini
(
vedeți și documentația oficială):
board_build.f_cpu = 12000000L
Pentru a preciza baud rate-ul consolei seriale din VSCode cu PlatformIO, folosiți următoarea variabilă din
platformio.ini
:
monitor_speed = 28800
Task 2
Folosind scheletul de laborator, implementeaza o noua functie USART_exec(unsigned char command) care sa accepte urmaorele comenzi venite pe USART:
“on” – aprinde led-ul rgb in culoarea alba
“off” – stinge legul rgb
“red”, “green”, “blue” – seteaza culoarea led-ului indicata de text
Pentru a controla ledul RGB aveti urmatorii pini:
Red - PD5
Green – PD7
Blue – PB3
Task 3
Trimiteti numele vostru prin interfata seriala si folosind “morse_alphabet” din scheletul de laborator si buzzerul , generate codul morse aferent acestuia.
Bonus
Implementeaza un semafor controlat pe USART. Foloseste ledul RGB de la Task-ul 1. Culoarea led-ului va fii in permanenta rosie , pana mesajul “pieton” va fii primit pe USART. Cand mesajul este primit, se va seta culoarea galbena pentru 2 secunde , dupa aceasta , culoarea verde pentru 5 secunde , revenindu-se la culoarea rosie pana la primirea unui mesaj nou. In cazul in care se va primii un alt mesaj inafara de “pieton”, se va trimite pe USART mesajul “request incorect”. In timpul executiei comenzii de schimbare a culorilor , mesajele pe USART vor fii ignorate.