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 față de depanarea software uzuală?

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 și greu de folosit decât depanarea software.
  • Folosirea unui debugger generic (ex. Remote GDB) vine la pachet cu o serie de probleme ce pot apărea. Debuggerele generice necesită o stivă de rețea sau protocoale conformă cu hardware-ul folosit. Chiar dacă am implementa o astfel de stivă, nu putem fi siguri că aceasta funcționează perfect.
  • 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 în fazele incipiente poate avea erori de proiectare, ceea ce influențează 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 aceleași ca și în cazul software-ul de nivel înalt: trebuie să comparați ce se dorește de la sistem (cod / circuit) cu ceea ce sistemul face de fapt.

2. Instrumente necesare

Vizibilitatea la nivel hardware se realizează printr-o formă de Input/Output (dacă este disponibilă):

  • Depanare cu LED-uri - verificarea stării true/false
  • mesaje pe interfața serială / USART - depanare prin USART, Bluetooth, etc.
  • Debuggere avansate pentru afișarea și modificarea memoriei/registrelor (a se vedea JTAG de mai jos)
  • Loopback-ing (conectarea ieșirilor la intrări) poate oferi informații despre modul în care comenzile sunt trimise dispozitivelor externe.

Instrumente de măsură:

  • Multimetre (pentru valori statice)
    • Rezistență: Doar conectați o componentă de circuit între sonde.
    • Tensiune: conectați în paralel - sonda pozitivă (roșie) la punctul potențial mai mare, sonda negativă (neagră) la punctul potențial mai mic. Pentru potențialul într-un singur punct: sonda negativă la GND, sonda pozitivă în punctul dorit.
    • Curent: Conectați multimetrul în serie cu circuitul pe care doriți să-l verificați.. Trebuie să plasați sonda roșie pe cablul unei componente și sonda neagră pe următorul conductor. Multimetrul acționează ca și cum ar fi fost un fir în circuitul tău. Dacă deconectați multimetrul, circuitul nu va funcționa.
    • Începeți întotdeauna prin a seta scara multimetrului la maxim, apoi corectați progresiv scara până când aveți cea mai precisă măsurare.
    • Pentru a verifica polaritatea diodelor: Selectați verificare diodă. Daca plasați sonda negativă pe anod și sonda pozitivă pe catod, multimetrul va face un sunet sau va arăta o cădere de tensiune de pe joncțiunea p-n.
    • Pentru a verifica scurtcircuite/conectivitate: utilizați modul buzzer dacă acesta este prezent ca setare în modul măsurare rezistență – dacă aparatul emite un sunet atunci înseamnă că există un scurtcircuit/conexiune între acele două puncte.

  • 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 accesați registrele potrivite. O altă problemă poate fi conectarea greșită dintre periferice și pini.
  • În primă fază verificăm dacă este posibilă depanarea folosind mesaje de debug și/sau mesaje pe interfața serială.
  • Dacă există o stivă ethernet funcțională pe dispozitiv, poți lua în considerare folosirea SSH, NFS, etc pentru a folosi depanarea prin mesaje.
  • În cazul în care nu avem o stivă de rețea, verificăm dacă avem acces la protocoale mai simple precum UART. Testăm conectând prin UART un periferic, de exemplu un PC, modul Bluetooth, LCD, etc.
  • În cazuri extreme sau pentru programe foarte simple, atunci când nu avem la dispoziție nimic din ce este enumerat mai sus, putem folosi și depanarea prin LED-uri.
  • Izolați problema folosind instrumente de măsura: multimetre, osciloscoape, analizoare logice.
  • În cazul în care știm că dispozitivul nostru este compatibil cu debuggere hardware (JTAG, Lauterbach, etc.) folosirea lor este de multe ori cea mai eficientă metodă 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ă.

 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.

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)

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 și 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 ceasuri de 8Mhz, 11.0592MHz și 14.7456MHz. În cazul plăcii noastre, ceasul este de 12MHz și putem căuta valoarea pentru baud rate aici, sau o putem calcula folosind formula: (F_CPU/(UART_BAUD_RATE*16)-1), unde F_CPU este frecvența de ceas iar UART_BAUD_RATE este ales de noi (ex. 4800, 9600, 14400).

Tabel baud rate

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 există și biblioteca util/setbaud.h care este menită să simplifice procesul. Este nevoie să setați frecvența ceasului procesorului. Găsiți aici mai multe detaii despre aceasta și 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 configurează USART0 cu următorii parametri : baud rate 28800, 8 biți de date, 2 bit de stop, fără paritate. Transmiteți către PC mesajul “Butonul 1 a fost apasat” dacă butonul 1 (PB2) este apăsat.

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

Pentru a vedea mesajul scris de la tastatura in serial monitor puteti adauga:

monitor_echo = true

Task 2

Folosind scheletul de laborator, implementează o nouă funcție USART_exec(unsigned char command) care să accepte următoarele comenzi venite pe USART:

  • “on” – aprinde led-ul rgb în culoarea albă
  • “off” – stinge legul rgb
  • “red”, “green” sau “blue” – setează culoarea led-ului indicată de text

Pentru a controla ledul RGB aveți următorii pini:

  • Red - PD5
  • Green – PD7
  • Blue – PB3

Task 3

Trimiteți numele vostru prin interfața serială. Folosind “morse_alphabet” din scheletul de laborator și buzzerul, generati codul Morse aferent acestuia.

Bonus

Implementează un semafor controlat pe USART. Folosește ledul RGB de la Task-ul 1. Culoarea led-ului va fi în permanență roșie, până când mesajul “pieton” va fi primit pe USART. Când mesajul este primit, se va seta culoarea galbenă pentru 2 secunde, după aceasta culoarea verde pentru 5 secunde, revenindu-se la culoarea roșie până la primirea unui mesaj nou. În cazul în care se va primi un mesaj diferit de “pieton”, se va trimite pe USART mesajul “cerere incorecta”. În timpul execuției comenzii de schimbare a culorilor, mesajele pe USART vor fi ignorate.

pm/lab/lab1-2023.txt · Last modified: 2024/03/15 11:45 by alexandru.predescu
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