This is an old revision of the document!
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.
Motivul pentru care depanarea embedded este mai dificilă decât depanarea obișnuită a software-ului provine din mai multe probleme:
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.
Vizibilitatea la nivel hardware se realizează printr-o formă de InputOutput (dacă este disponibilă):
Instrumente de măsurare:
Un exemplu de flux de depanare ar putea fi următorul:
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:
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 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:
RX
și TX
.
Se gaseste în Datasheet Atmega 328p în capitolul 19. Registrele au un 'n' la sfârșit care distinge între mai multe periferice USART ce pot exista pe un microcontroller (pe ATmega328P 'n' va lua doar valoarea 0 corespunzatoare USART0).
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.
UCSRnA
este registrul de stare al controller-ului de comunicație. Biții cei mai importanți sunt:
UCSRnB
este un registru de control. Biții importanți:
UCSZ1
și UCSZ0
din portul UCSRC
, selectează dimensiunea unui cuvânt de date
UCSRnC
este tot un registru de control. Biții importanți:
UCSZn2
din portul UCSRnB
, selectează dimensiunea cuvântului de date
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 cele mai uzuale viteze de transmisie pentru o frecvență de ceas de 16 MHz.
void USART0_init() { /* seteaza baud rate la 9600 */ UBRR0 = 103; /* porneste transmitatorul */ UCSR0B = (1 << TXEN0) | (1 << RXEN0); /* seteaza formatul frame-ului: 8 biti de date, 1 bit de stop, fara paritate */ UCSR0C &= ~(1 << USBS0); UCSR0C |= (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; }
(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!
Arduino UNO se conecteaza la PC prin intermediul interfetei seriale, dar utilizeaza un convertor USB-UART integrat pe placa. Prin intermediul acestei interfete si utilizand IDE-ul dedicat Arduino se poate programa microcontrollerul, 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
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); } }
Task 1 (3p)
Folosind interfața serială și biblioteca Arduino, trimiteți următoarele comenzi dintr-un terminal serial:
Fiecare comandă va fi urmată de caracterul de control: “\n” (newline). Configurați terminalul serial să trimită (doar) acest caracter după fiecare mesaj. Implementați un program în Arduino care să recunoască aceste comenzi și să realizeze acțiunile corespunzătoare pentru fiecare dintre ele. Folosiți pin-ul 13 (PB5) pentru LED și pin-ul 2 (PD2) pentru buton.
long ts = 0; // global variable is your friend void loop() { // my other code here if ((millis() - ts) >= 100) { ts = millis(); // my non-blocking timed loop here } // my other code here }
Task 2 (3p)
Rulați exemplul pentru USART pe bază de registre. Pentru configurările serialei, urmăriți fisierul usart.c din schelet.
Configurați USART0 cu următorii parametri: baud rate 19200, 8 biți de date, 1 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ă PD2, se transmite “PD2 apăsat”, se lasă PD2, se transmite “PD2 lăsat”, câte o singură dată pe apăsare).
Task 3 (4p)
Colaborați cu colegii de lângă voi pentru a conecta două plăci Arduino între ele și trimiteți mesaje, dinspre primul Arduino către al doilea, și invers.
În cazul în care nu avem nevoie de conexiunea la PC, putem conecta un al doilea dispozitiv pe liniile RX și TX. Dacă însă am vrea să facem debug sau să trimitem mesaje de la PC, conexiunea nu mai funcționează corect, deoarece interfața serială nu permite conectarea a mai mult de 2 dispozitive (cel puțin pe linia TX).
O altă constrângere este că avem o singură interfață USART pe ATmega328p, fapt pentru care vom avea nevoie de simularea unei a doua interfețe seriale în software.
Găsiți un exemplu în Arduino IDE: Files > Examples > SoftwareSerial > SoftwareSerialExample
Bonus (2p) Realizați implementarea Task 1 pe bază de registre.