This is an old revision of the document!


Laboratorul 2: Digital Debugging, USART

Ca în toate lucrările de inginerie, bug-uri pot și vor apărea si în sisteme încorporate.

1. Ce este diferit fata de depanarea uzuala?

Motivul pentru care depanarea embedded este mai dificilă decât depanarea obișnuită a software-ului provine din mai multe probleme:

  • Un începător este obișnuit cu instrumente de nivel înalt generice, de cele mai multe ori gratuite (de ex. IDE-uri, debuggere etc). Pentru sistemele embedded, este posibil ca user-ul sa nu aibă acces la instrumente hardware si software dedicate.
  • Dacă nu aveți un debugger specializat și încercați să utilizați unul generic (să spunem Remote GDB), ce vă face să credeți că aveți stiva de rețea necesară pentru acel debugger? Dacă ați implementat o astfel de stivă, cât de sigur sunteți că funcționează corect?
  • 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 dvs. ar putea avea erori.
  • 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:

  • 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-vă în serie.
    • Î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.
    • Pentru a verifica scurtcircuite/conectivitate: Utilizați verificare diodă – daca aparatul emite un sunet atunci înseamnă că există un scurtcircuit/conexiune între acele două puncte.

  • Osciloscoape (pentru valori dinamice - Electroboom explică mai bine decât noi)

  • 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, inter operarea 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 foaia de catalog și schema. Accesați registrele potrivite? Sunt perifericele conectate la pinii la care am crezut că sunt?
  • Este posibilă depanarea prin mesaje de debug (echivalent printf)?:
  • Ai o stivă ethernet? Dacă da, ia în considerare SSH, NFS, etc pentru a realiza debugging printf.
  • În cazul în care aveti o stiva de rețea, poate sunt disponibile protocoale mai simple: UART? Putem conecta ceva prin UART? Poate un PC, modul HC-05 Bluetooth, un LCD?
  • Se poate lua în considerare depanarea cu LED-uri.
  • Izolați problema folosind instrumente de măsura: multimetre, osciloscoape, analizoare logice.
  • Încercați un debugger JTAG sau orice alt depanator despre care știți că funcționează cu dispozitivul dvs.

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 Atmega328p include un periferic 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.

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 '&')

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.1 Registre

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!

Utilizarea interfetei seriale de pe Arduino UNO

Arduino UNO se conecteaza la PC prin intermediul interfetei seriale, dar utilizeaza un convertor serial-to-USB integrat pe placa. Prin intermediul acestei interfete si utilizand IDE-ul dedicat Arduino se poate programa microprocesorul, dar se poate asigura si un canal de debug. Astfel, prin mesaje simple, se poate afla starea sistemului, se pot afisa valorile variabilelor, sau chiar se pot trimite comenzi, interfata functionand bidirectional. Mai multe detalii se pot gasi aici

Configurația implicită pentru interfața serială USART folosește 8 biți de date, un bit de stop, fără paritate (8N1).

Următorul program poate trimite mesaje de la Arduino către PC, prin USB (sau folosind emulatorul din Tinkercad)

void setup()
{
  Serial.begin(9600);
  Serial.println("in function setup");
}
 
void loop()
{
  Serial.println("in function loop");
  delay(1000);
}

Următorul program poate primi mesaje trimise de PC, prin USB (sau folosind emulatorul din Tinkercad)

void setup()
{
  Serial.begin(9600);
  Serial.println("astept comenzi");
}
 
void loop()
{
  if (Serial.available()){
    char a = Serial.read();
    char buf[20];
    sprintf(buf, "%s: %c", "primit caracter", a);
    Serial.println(buf);
  }
}

4. Exerciții

Task 0 Incercati voltmetrul

Task 1 Faceti un Arduino Uno sa seteze 1 si 0 pe un pin (echivalent cu blink la un led) si conectati voltmetrul.

Task 2 Studiati functia analogWrite. Determinati de ce nu functioneaza schema de mai sus cu codul urmator:

void setup() {
  pinMode(12, OUTPUT);
}

void loop() {
  analogWrite(12, 200);
}

Modificați codul astfel încât să obtineti o valoare de 2V la bornele voltmetrului.

Task 3 Folosind interfața serială, trimiteti următoarele comenzi catre placuta

  • “on” - aprinde un led
  • “off” - stinge un led
  • “blink” – blink un led
  • “get” - afiseaza prin interfata seriala starea unui buton apasat
  • “analog” urmat de o valoare – seteaza o tensiune pe un pin

4. Resurse

5. Linkuri utile

pm/lab/lab2-2021.1646394412.txt.gz · Last modified: 2022/03/04 13:46 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