Arhitectura

CUPRINS

PREZENTAREA TEMEI

Lucrând în domeniul automotive, mai exact pe partea sistemului de frânare, în urma cunoștințelor acumulate și cercetării făcute, mi-am dat seama că se poate dezvolta un sistem prin intermediul căruia să se poată testa motorul DC din cadrul pompei hidraulice care acționează pistoanele ce împing lichidul de frână în sistem. Prin următoarea lucrare propun realizarea unui dispozitiv (ECU) care să comande motorul DC, un Tool de comandă și monitorizare, pentru a putea comanda dispozitivul de pe PC și în același timp și urmărirea comportamentului în funcție de aceste comenzi și comunicația între dispozitiv și PC. De asemenea, din motive de development, în cadrul dispozitivului s-au pus la dispoziție și controale hardware. Din cadrul Tool-ului se poate comunica cu dispozitivul, se pot trimite comenzi și monitoriza reacția la aceste comenzi, iar ECU-ul sau dispozitivul realizat este programat să reacționeze la aceste comenzi.

Lucrarea este structurată pe 5 capitole, fiecare urmărind subiectele prezentate mai jos:

– În capitolul 1 se face o introducere în domeniul microcontrolerelor, clasificare, arhitecturi, memorii folosite, porturi intrare/ieșire

– În capitolul 2 au fost descrise mediile de dezvoltare în care s-au realizat aplicațiile propuse, împreună cu limbajele de programare folosite și exemple din fiecare.

– În capitolul 3 se descrie Tool-ul realizat, aplicația integrată pe ECU și firul principal al programului

– În capitolul 4 se vor prezenta echipamentele și componentele care au dus la realizarea proiectului ( cele care nu au fost deja explicate pe parcurs)

– În capitolul 5 au fost prezentate o serie de concluzii

Introducere

Microcontrolere

Un microcontroler este un microcircuit care încorporează o unitate centrala (CPU) și o memorie împreună cu alte resurse care îi permit interacțiunea cu mediul exterior.

Un microcontroler diferă de un microprocesor prin funcționalitatea sa. Un microprocesor reprezintă numai unitatea de calcul dintr-un computer, pentru a putea funcționa trebuie să i se mai adauge și alte componente cum ar fi memorie, porturi seriale, porturi paralele sau alte periferice. Microcontrolerele au fost dezvoltate și produse pentru a putea fi folosite ca și un calculator complet, integrat într-un singur chip. Toate perifericele, memoria și celelalte cu fost toate integrate într-un singur chip, un lucru revoluționar în construirea produselor electronice din punct de vedere al spațiului, timpului, costurilor de producție.

.

Clasificare

Microcontrolere incorporate EM

Microcontrolere propriu-zise M

Procesoare de Semnal DSP

Prima categorie reprezintă microcontrolerele care înmagazinează toate resursele de care are nevoie pentru a putea funcționa că un singur computer. Programul este salvat într-o memorie nevolatilă integrată în același chip cu procesorul.

A doua categorie reperezinta microcontrolerele cu caracteristici de procesor cu uz general. Pot deține unul sau mai multe chip-uri, interfețe pentru accesul memoriei externe și interfețe interconectate cu diverse circuite electronice.

Un procesor de semnal digital este un dispozitiv asemănător cu un microprocesor exceptând faptul că CPU-ul intern a fost optimizat pentru utilizarea în aplicații care necesită prelucrarea semnalelor discrete în timp. Pot efectua operații matematice asupra datelor de intrare. Pentru aceste prelucrări, DSP lucrează împreună cu convertoare analog-digital și digital-analog.

Arhitecturi

Arhitectura unitatii centrale de calcul este primul element care trebuie luat in considerare la un sistem de calcul. Principalele arhitecturi intalnite sunt: “Von Neumann” sau “Princeton”, “Harvard” si arhitecturi CISC si RISC.

Arhitectura “Von Neumann”

Majoritatea microcontrolerelor întâlnite sunt realizate pe baza acestei arhitecturi. Unitatea centrală este caracterizată de faptul că există un singur spațiu de memorie atât pentru memorarea codului cât și pentru memorarea datelor. Există o singură magistrală (bus) care este folosită pentru preluarea instrucțiunilor și a datelor. Efectuarea acestor două operații separate, în mod secvențial, are ca efect încetinirea operațiilor. Este arhitectura cel mai des întâlnita și pentru microcontrolerele de uz general..

Această arhitectură descrie o componentă cu patru module:

– Unitatea aritmetico-logică

– Unitatea de control

– Memoria centrala

– Dispozitive de intrare/ieșire

Acestea sunt interconectate prin magistrală (bus) pe care se transferă datele de calcul și datele de program, conduse de tactul unui ceas.

Arhitectura Harvard

Această arhitectură prezintă spații de memorie separate pentru program și date, deci avem magistrale diferite de adrese și de date pentru codul instrucțiunilor și pentru date. Codul unei instrucțiuni poate fi preluat din memorie în timp ce se execută operațiile cu datele aferente instrucțiunii anterioare. Teoretic ar trebui să avem o execuție mai rapida pe baza unei complexități mărite a microcircuitului.

Făcând o comparație între arhitecturile von Neumann și Harvard , în cadrul arhitecturii von Neumann se poate citi o instrucțiune, se poate citi sau scrie date din sau în memorie. Nu se pot executa operațiunile simultan, folosindu-se aceeași magistrală. În cadrul arhitecturii Harvard având magistrale diferite, accesul la date nu se face pe aceeași cale de transfer, în concluzie ar trebui să avem o rapiditate sporită.

În principal arhitectura Harvard este folosită la două tipuri de dispozitive:

Procesoarele de semnale din cadrul dispozitivelor pentru prelucrarea de semnale audio-video.

Microcontrolerele PIC de la Microchip, AVR de la Atmel

Arhitectura RISC

RISC ( Reduced Instruction Set Computer )este un concept de realizare a CPU care a început să fie utilizat cu success la realizarea microcontrolerelor. Prin implementarea unui set redus de instrucțiuni care se pot executa foarte rapid și eficient, se obține o reducere a complexității, suprafața disponibilizată putând fi utilizată în alte scopuri.

Caracteristici:

Arhitectura Harvard modificată sau arhitectură von Neumann.

Viteză sporită de execuție prin implementarea unui pipeline pentru instrucțiuni.

Arhitectura CISC

Majoritatea microcontrolerelor au la baza realizării CPU conceptul CISC ( Complex Instruction Set Computer ). Aceasta prezintă un set de peste 80 de instrucțiuni, fiind diferite inrte ele. Unele operează numai cu anumite spații de adrese sau regiștrii, pe când altele permit numai anumite moduri de adresare.

Memorii folosite în cadrul microcontrolerelor

Memoria RAM ( Random Acces Memory)

Este volatila și poate fi citită și scrisă. Se utilizează pentru păstrarea datelor. Este relativ mică, până la 512 octeti și poate fi internă, împărțită pe mai multe zone, sau externă.

Memoria Program ( Read Only Memory )

Acest tip de memorie este nevolatilă, adică nu își pierde conținutul dup ce alimentarea este întreruptă, ceea ce o face potrivită pentru păstrarea programului unui microcontroler. Memoria ROM poate fi scrisă de ultilizator și nu în timpul execuției programului. Acest proces se numește “programare” și de obicei este făcut ofline când memoria nu este folosită.

Memoria EPROM

Acest tip de memorie folosește tranzistoare MOS ca element programabil. Aceste tranzistoare conțin câte o poartă flotantă ceea ce înseamnă că poarta nu este conectată. Programarea are loc prin injectarea de electroni în poarta flotantă folosind o tensiune mai mare decât cea obișnuită (uzual între 12V și 25V). Ștergerea se produce prin scoaterea electronilor din poarta flotantă, proces realizat prin expunerea la raze ultraviolete printr-o fereastră mică în exteriorul microcontrolerului. Expunerea durează în jur de 20 de minute. După efectuarea ștergerii fereastra trebuie acoperită pentru a proteja microcontrolerul de razele ultraviolete provenite de la soare. Există și variante de microcontrolere ce nu au fereastră, acestea fiind practic programabile o singură dată și poartă denumirea de OTPEPROM(one time programmable EPROM). Motivul pentru eliminarea ferestrei este de a reduce costurile de fabricație. Aceste memorii pot păstra datele programate un timp îndelungat, de la zece la chiar douăzeci de ani, în absența alimentării. Microcontrolerele cu astfel de memorii program au fost înlocuite de cele cu memorii EEPROM sau FLASH, deoarece costurile de fabricație sunt mai mici iar citirea și scrierea este mai lentă decât la cele din urmă.

Memoria EEPROM

EEPROM (electricaly erasable programmable read only memory) este un tip de memorie ce permite programarea și ștergerea octeților electric. Programarea se face similar cu cea de la memoriile EPROM iar ștergerea se face aplicând un curent invers porților flotante, astfel eliminând necesitatea ferestrelor iar ștergerea poate fi făcută în același circuit. Aceste memorii au însă un număr limitat de scrieri/ștergeri, însă în ultimul timp numărul acesta a crescut simțitor. Există o varietate de microcontrolere ce folosesc memorie EEPROM pentru program, acestea putând fi programate pentru diferite aplicații în funcție de necesități. Astfel de microcontrolere sunt de preferat când memoria program conține un firmware la care firma poate aduce îmbunătățiri sau să corecteze erori neidentificate. În cazul în care este nevoie de un firmware mai complex, de dimensiuni mai mari se preferă folosirea de microcontrollere cu memorie FLASH.

Memoria Flash

Memoria FLASH este un tip de memorie EEPROM la care ștergerea se efectuează la nivel de bloc de date, spre deosebire de EEPROM unde ștergerea este la nivel de octet. Ca urmare ștergerea la FLASH se poate face mult mai rapid, deși poate fi un inconvenient în momentul în care se dorește ștergerea a unui singur octet. Timpii de citire la memoria FLASH sunt mai mari decât la EEPROM însă structura memoriei FLASH este mai densă, oferind capacități de memorare mai mari pe aceeași suprafață de siliciu, lucru dorit în cazul microcontrollerelor unde dimensiunile sunt limitate. În plus, costurile de fabricație pentru memoriile FLASH sunt mai mici în comparație cu cele pentru memoriile EEPROM.

Ceasul Sistem

Unitatea de bază a timer-ului este un contor liber care este de fapt un registru a cărui valoare numerică crește cu intervale de timp egale, așa încât luându-i valoarea după intervalele T1 și T2 și pe baza diferenței lor să putem determina cât timp a trecut. Acesta este o parte foarte importantă a microcontrolerului a cărui control necesită cea mai mare parte a timpului nostru.

Utilizări ale timerului

Generarea unei întreruperi la intervale regulate de timp

Măsurarea precisă a momentului producerii unor evenimente externe; captură logică

Deoarece utilizarea unei linii de port care să genereze o întrerupere în momentul producerii evenimentului extern nu este o soluție acceptabilă (datorită timpului scurs între momentul producerii evenimentului și momentul în care se iau deciziile asociate) timerele conțin hardul necesar capturii logice.

Timerul este asociat cu un număr de regiștri de captură care copiază conținutul timerului în registru atunci când, producându-se evenimentul extern, se produce o tranziție pe un pin de intrare asociat registrului.

pinii de intrare asociați sunt linii de port I/O obișnuite având ca funcțiune alternativă captura logică.; copierea se face automat dacă timerul este programat în acest scop.

Generarea precisă a unor semnale spre proces; comparația logică

generarea acestora prin program poate fi imprecisa (existența întreruperilor în sistem, dificultatea de a genera unele semnale prin program)

Controlul funcționarii corecte a microcontrolerului (watchdog =ceas de gardă)

Să presupunem că urmare a unei anumite interferențe (ce adesea se întâmplă în industrie-situatie similara este căderea tensiunii de alimentare) microcontrolerul nostru se oprește dinexecutarea programului, sau și mai rău, începe să funcționeze incorect. Bineînțeles, când aceasta se întâmplă cu un computer, îl resetăm pur și simplu și vade siliciu, lucru dorit în cazul microcontrollerelor unde dimensiunile sunt limitate. În plus, costurile de fabricație pentru memoriile FLASH sunt mai mici în comparație cu cele pentru memoriile EEPROM.

Ceasul Sistem

Unitatea de bază a timer-ului este un contor liber care este de fapt un registru a cărui valoare numerică crește cu intervale de timp egale, așa încât luându-i valoarea după intervalele T1 și T2 și pe baza diferenței lor să putem determina cât timp a trecut. Acesta este o parte foarte importantă a microcontrolerului a cărui control necesită cea mai mare parte a timpului nostru.

Utilizări ale timerului

Generarea unei întreruperi la intervale regulate de timp

Măsurarea precisă a momentului producerii unor evenimente externe; captură logică

Deoarece utilizarea unei linii de port care să genereze o întrerupere în momentul producerii evenimentului extern nu este o soluție acceptabilă (datorită timpului scurs între momentul producerii evenimentului și momentul în care se iau deciziile asociate) timerele conțin hardul necesar capturii logice.

Timerul este asociat cu un număr de regiștri de captură care copiază conținutul timerului în registru atunci când, producându-se evenimentul extern, se produce o tranziție pe un pin de intrare asociat registrului.

pinii de intrare asociați sunt linii de port I/O obișnuite având ca funcțiune alternativă captura logică.; copierea se face automat dacă timerul este programat în acest scop.

Generarea precisă a unor semnale spre proces; comparația logică

generarea acestora prin program poate fi imprecisa (existența întreruperilor în sistem, dificultatea de a genera unele semnale prin program)

Controlul funcționarii corecte a microcontrolerului (watchdog =ceas de gardă)

Să presupunem că urmare a unei anumite interferențe (ce adesea se întâmplă în industrie-situatie similara este căderea tensiunii de alimentare) microcontrolerul nostru se oprește dinexecutarea programului, sau și mai rău, începe să funcționeze incorect. Bineînțeles, când aceasta se întâmplă cu un computer, îl resetăm pur și simplu și va continua să lucreze. Totuși, nu există buton de resetare pe care să apăsăm în cazul microcontrolerului care să rezolve astfel problema noastră.

Pentru a depăși acest obstacol, avem nevoie de a introduce încă un bloc numit watchdog- câinele de pază. Acest bloc este de fapt un alt contor liber unde programul nostru are nevoie să scrie un zero ori de câte ori se execută corect. În caz că programul se "înțepenește", nu se va mai scrie zero, iar contorul se va reseta singur până la obținerea valorii sale maxime. Aceasta va duce la rularea programului din nou, și corect de această dată pe toată durată. Acesta este un element important al fiecărui program ce trebuie să fie fiabil fără supravegherea omului.

Unitatea de intrare-iesire ( I/O )

Microcontrolerele trebuie să comunice cu lumea exterioară, nu doar să lucreze singure.

Pentru aceasta să adăugat un bloc ce conține câteva locații de memorie a căror singur capăt este conectat la busul de date, iar celălat are conexiune cu liniile de ieșire la microcontroler ce pot fi văzute cu ochiul liber ca pini la componenta electronică.

Aceste locații sunt numite "porți". Sunt diferite tipuri de porți: intrare, ieșire sau porți pe două-căi.

Când se lucrează cu porți, mai întâi de toate este necesar să se aleagă cu ce port urmează să se lucreze, și apoi să se trimită, sau să se ia date de la port.

În timpul accesării, portul se comportă ca o locație de memorie, unde “ceva” este pur și simplu scris în el sau citit din el, și este posibil de a remarcă ușor aceasta la pinii microcontrolerului.

Toate microcontrolerele au de un număr oarecare de intrări/ieșiri numerice Conexiunile exterioare sunt bidirecționale sau unidirecționale, unele sunt multifuncționale (se oferă funcții alternative pe același pin), altele pot avea o capacitate sporită de a absorbi curent (de ex. pentru comanda directă a unui LED).

În afară de acestea pentru interfața cu mediul exterior se oferă o serie de alte facilități cum ar fi :

UART (Universal Asynchronous Receiver Transmitter)

Este un port serial bidirecțional destinat implementării unui protocol clasic de comunicație asincron;

USART (Universal Synchronous Asynchronous Receiver Transmitter ) este un port similar cu cele cunoscute, dar care permite implementarea și a unui protocol sincron cu obținerea unor viteze mai mari de comunucație.

SCI (Serial Communications Interface) este un circuit de tip UART îmbunătățit, definit și utilizate de firma Freescale(Motorola). Ex: RS-232, RS-422, RS-485.

LIN (Local Interconnect Network) reprezintă o implementare particulară a unui protocol de comunicație asincron

Porturi seriale sincrone dedicate

Sunt destinate transferului serial de date de mare viteză cu unele periferice specializate (sisteme de afișare, convertoare analog numerice,etc) sau permit conectarea într-o rețea de comunicație simplă.

Implică și implementarea unor protocoale mai mult sau mai puțin complexe de transfer al informației. Există câteva implementări răspândite (prezentate în ordinea crescătoare a complexității):

SPI (Serial Peripheral Interface)

Este un port serial sincron și SCI (serial comunications interface) un circuit de tip UART imbunătățit, definite si utilizate de fima Motorola.

Microwire / Plus

Este o interfață serială bidirecțională sincronă utilizată de firma National Semiconductor.

I2C (Inter Integrated Circuits bus)

Este o interfață serială bidirecțională (pe doua fire) , dezvoltată de firma Philips,

destinată aplicațiilor de 8 biti . Există și multe circuite “periferice“ care sunt

prevăzute cu o astfel de interfață.

CAN (Controller Area Network)

Este un standard (o magistrală si un protocol) de comunicație serială utilizate in industria de automobile, permițând interconectarea într-o rețea a diverselor componente inteligente (senzori, elemente de execuție, etc.) folosite într-un automobil modern.

Conectivitate Ethernet/Web – implică existența unor resurse care să permită integrarea cu ușurință într-o reŃea de tip Ethernet, pentru a face posibilă, în final, implementarea unui protocol TCP/IP (a unei stive TCP/IP). Resursele respective pot fi de natură software (stivă soft) care presupun o viteză de prelucrare (putere de calcul) a CPU suficient de mare pentru a nu afecta vizibil operarea propriu-zisă a controlerului, sau hardware (stivă hardware). Pe baza acestei stive se poate realiza o conectivitate tip HTTP, FTP, STMP, POP3 etc.

Conectivitate USB – magistrala serială USB (Universal Serial Bus) a fost creată pentru conectarea cu ușurință a diverselor periferice la un calculator PC (cu rolul de gazdă -host). Conexiunea permite si furnizarea tensiunii de alimentare. Varianta USB 1.1 permite atingerea unei rate de transfer maxime a datelor de 12Mbytes/sec, iar varianta USB 2.0 a unei rate maxime de cca. 480MBytes/sec. La ora actuală există pe piață multe firme care oferă o gamă largă de microcontrolere cu conectivitate USB (majoritatea compatibile USB 1.1), cu un preț de cost minim pentru componentele hardware și software. Exemple în acest sens ar fi firmele: Atmel, Microchip, Intel, Cypress, ST, Infineon, s.a. Majoritatea sunt destinate realizării unor periferice USB si mai puține realizării unui USB host.

Conectivitate Wireless- se referă la existența unor resurse hardware si/sau software care să permită integrarea cu ușurință și la un preț avantajos într-o rețea de tip wireless, pentru a face posibilă, în final, implementarea unui protocol. Exemplele cele mai cunoscute de astfel de rețele, sunt Bluetooth (IEEE 802.15.1) și Zigbee (IEEE 802.15.4).

Microcontrolerul dsPIC30F prezentare generala

Acest circuit beneficiază de un set de instrucțiuni pe 24 de biți. Program counter-ul prezintă o capacitate de 23 de biți, cel mai putin semnificativ bit (este bitul 23) fiind pus mereu pe zero, iar cel mai semnificativ bit este ignorat in timpul de execuție al unui program obisnuit, excepție facand instrucțiunile specializate. Cu toate acestea program counter-ul are capacitatea de a adresa pană la 4M de instrucțiuni. Un mecanism de pre-fetch al instrucțiunilor este folosit pentru a menține acest flux de informații.

Regiștrii de lucru sunt regiștrii de 16 biți, fiecare dintre aceștia putand fi regiștrii de date, de adrese sau offset. în momentul apariției unei intreruperi un singur registru de lucru si anume (W15) operează ca și pointer al stivei.

Spațiul de memorie are capacitatea de 64 Kbytes și este imparțit in doua blocuri X si Y. fiecare bloc în parte are Unitate de Generare a Adreselor. Cele mai multe instrucțiuni folosesc numai blocul X de memorie, acest lucru dă impresia unui bloc unitar de memorie. Aceste blocuri de memorie X si Y sunt individualizate prin construcția circuitului si nu pot sa fie modificate de catre utilizator.

Programatorul este prezentat in figura de mai jos si conține 16 regiștrii de lucru pe 16 biți, (SR doi regiștrii acumulatori pe 40 de biți(ACCA si ACCB), registrul STATUS) registrul Data Table Page (TBLPAG), registrul Program Space Visibility Page (PSVPAG), regiștrii DO si REPEAT (DOEND, DOSTART, DCOUNT si RCOUNT) și Program Counter-ul.

Modelul Programatorului

Semnificatia pinilor

dsPIC30F2020 are un număr total de 28 pini. De obicei se găsește într-o capsulă de tip DIP28 este prescurtarea de la Dual In Package. Pinii microcontrolerului dsPIC30F2020 au următoarea semnificație:

Pin nr.1MCLR Acest pin este folosit pentru resetarea microcontrolerului.

Pin nr.2AN0 Canalele cu intrari analogice.

Pin nr.3AN1 Canalele cu intrari analogice.

Pin nr.4AN2 Canalele cu intrari analogice.

Pin nr.5AN3 Canalele cu intrari analogice.

Pin nr.6AN4 Canalele cu intrari analogice.

Pin nr.7AN5 Canalele cu intrari analogice.

Pin nr.8VSS Referință de masă pentru modulul analogic.

Pin nr.9AN6 Canalele cu intrari analogice.

Pin nr.10AN7 Canalele cu intrari analogice.

Pin nr.11PGD1 Pinul 1 cu date de intrare sau ieșire al ICSP.

Pin nr.12RE6 Este un port bidirecțional de I-O

Pin nr.13VDD Tensiunea de alimentare pentru logic.

Pin nr.14PGD2 Pinul 2 cu date de intrare sau ieșire al ICSP.

Pin nr.15PGC2 Pinul 2 cu intrare clock

Pin nr.16RA9 Este un port bidirecțional de I-O

Pin nr.17RF8 Este un port bidirecțional de I-O.

Pin nr.18RF7 Este un port bidirecțional de I-O

Pin nr.19VSS Referință de masă pentru modulul analogic.

.Pin nr.20VDD Referinta de masă pentru modulul analogic.

.Pin nr.21RE5 Este un port bidirecțional de I-O

Pin nr.22RE4 Este un port bidirecțional de I-O

Pin nr.23RE3 Este un port bidirecțional de I-O

Pin nr.24RE2 Este un port bidirecțional de I-O

Pin nr.25RE1 Este un port bidirecțional de I-O.

Pin nr.26RE0 Este un port bidirecțional de I-O.

Pin nr.27AVSS Referintă de masă pentru modulul analogic.

Pin nr.28AVDDTensiune pozitivă de alimentare pentru modulul analogic.

Ceasul Sistem

Oscilatorul ceasului intra in microcontroler prin intermediul pinului OSC1 unde cu ajutorul circuitului intern al microcontrolerului se divide semnalul de ceas in 4 tacturi egale ce nu se suprapun. Aceste tacturi constituie un ciclu de o instructiune in timpul careia instructiunea este executata. Din memoria program instructiunea este apelata la fiecare Q1 si este notata in registrul de instructiuni la Q4. Executarea instructiunilor se face intre Q1 si Q4.

In diagrama urmatoare se poate observa relatia dintre OSC1 si cilcul instructiunii cat si cea a tacturilor interne Q1-Q4.

Unitatea de intrare-iesire ( I/O)

Pinii dispozitivului în afară de MCLR, VDD,VSS și OSC1 sunt conectați între portul paralel de intrare-ieșire și periferice. Pentru a îmbunătății imunitatea la zgomot pinii de intrare au câte un Trigger Schmitt.

În momentul în care un periferic este activat și un pin îi este asociat, utilizarea pinului cu scop de ieșire este dezactivată. Pinul intrare/ieșire poate fi citit dar va fi dezactivat driverul de ieșire pentru portul paralel.

Registrul LATx distribuie datele la ieșire și poate fi atât scris cât și citit iar direcția pinului este controlată de registru TRISx. Starea pinului de intrare este reprezentată de citirea registrului PORTx iar scrierea acestuia modifică conținutul registrului LATx. Mai jos este prezentată schema bloc a unui pin.

Intreruperi

Acest tip de microcontrolere prezinta un set de peste 35 de surse de intrerupere si 4 exceptii procesor, care trebuiesc arbitrate pe baza unei scheme de prioritate. Unitatea de CPU este responsabila de citirea vectorului de intrerupere si transfera adresa din vectorul de intrerupere catre PC ( Program Counter ). Vectorul de intrerupere este transferat prin intermediul magistralei de date catre PC prin intermediul unui multiplexor de 24 de biti care serveste ca intrare in PC.

Tabela vectorului de intrerupere si tabela alternativa a vectorului de intrerupere sunt plasate la inceputul memoriei program. Controlerul de intrerupere este responsabil pentru procesarea intreruperilor si pentru prioritizarea lor.

Flagul unei intreruperi este setat atunci cand conditia de intrerupere este indeplinita si bineinteles este nevoie ca si bitul de activare sa fie setat.

Daca avem mai multe surse de intrerupere trebuie sa se faca o prioritizare intre aceste intreruperi, asignandu-i fiecareia un nivel de prioritate. Daca nivelul de prioritate este pus pe 0 atunci este echivalent cu dezactivarea intreruperii.

Modulul I2C

Acest modul oferă urmatoarele funcții:

interfată I2C suportă operați master si slave

modul slave suportă 7 sau 10 biți de adresă

modul master suportă 7 sau 10 biți de adresă

portul I2C permite transferul bidirecțional intre master si slave

modulul I2C suportă operațiuni multi master, detectează coliziuni de magistrale si va acționa in consecintă.

Modulul I2C poate opera fie ca slave fie ca master pe un bus (magistrala) I2C.

Configurarea pinilor in modul I2C

Interfața I2C are 2 pini, pinul SCL care este clock-ul si pinul SDA care reprezintă datele transferate.

I2CCON si I2CSTAT sunt regiștri de control respectiv status. Registrul I2CCON poate fi citit și scris. Cei mai nesimnificativi 6 biți ai I2CSTAT sunt doar pentru citire. Ceilalți biți pot fi si scriși si citiți. I2CRSR este registrul de schimbare folosit pentru shiftarea datelor iar I2CRCV este registrul buffer catre care octeți de date sunt scriși sau de unde octeți de date sunt citiți.

Mediu Dezvoltare

Visual Studio

Introducere în Visual Studio

Microsoft Visual Studio este un mediu de dezvoltare integrat ( integrated development environment – IDE) de la Microsoft. Acesta poate fi utilizat pentru a dezvoltă aplicații consola și aplicații cu interfață grafică pentru toate platformele suportate de Microsoft Windows (ex. .NET Framework etc).

Microsoft Visual Studio pune la dispoziție editor, compilator/debugger și mediu de proiectare (designer) pentru mai multe limbaje de programare suportate. Limbaje de programare: Microsoft Visual C++, Microsoft Visual C#, Microsoft Visual Basic, Microsoft Visual Web Developer.

.NET Framework este o platforma de dezvoltare software unitara, inzestrata cu un set mare de clase, structuri, enumerari etc., organizate intr-un set de spatii de nume bazate pe un limbaj comun.

Componente Visual Studio

Editor

Microsoft Visual Studio include un editor de cod, care suportă IntelliSense. Este o denumire populară de la Microsoft pentru Sens Inteligent. Este o trăsătură a mediului de dezvoltare care anticipează oarecum ce cod vrem să scriem, și ne sugerează de exemplu printr-o fereastră pop-up ce anume ar tebui să tastăm, astfel procesul de scriere de cod devine mai rapid, reducând cantitatea de cod tastat sau posibile greșeli de sciere.

Lucrează folosind o baza de date generată automat a claselor, numelor de variabile sau a altor construcții. IntelliSense lucrează prin detectarea caracterelor depinzând de limbaj. Când utilizatorul tastează cod, IntelliSense face o sugestie într-o fereastră pop-up. Acesta poate să accepte sugestia sau poate să continuie să tasteze.

Depanator

Microsoft Visual Studio ne pune la dispoziție un depanator care lucrează și pentru nivel-sursă și pentru nivel mașină. Lucrează și cu cod-sursă și cu cod-mașină și este folosit pentru aplicații scrise în orice limbaj de programare suportat de Visual Studio. Dacă este disponibil codul sursă al unui proces care rulează, acesta va fi afișat așa cum este rulat, iar dacă nu este disponibil va fi afișat codul asamblare. Depanatorul pus la dispoziție de Microsoft Visual Studio poate crea sau rezerva anumite zone de memorie pe care le va folosi ulterior pentru depanare. De asemenea suportă programele care operează pe mai multe fire de execuție (Mulți-threading) și poate fi configurat să fie lansat în execuție atunci când o aplicație „crapă“.

Ne permite să setăm anumite puncte de întrerupere a programului (breakpoint) sau monitorizează anumite valori ale variabilelor. Cu ajutorul Depanatorului codul poate fi rulat pas cu pas, linie cu linie, se poate face debug în interiorul unei funcții sau se poate sări direct după execuția acesteia.

Proiectant

Microsoft Visual Studio oferă o serie de modele vizuale menite să ajute la dezvoltarea de aplicații:

Windows Form Designer

Ne oferă posibilitatea de a crea aplicații de tip GUI ( Interfață Grafică cu Utilizatorul). Într-o fereastră principala putem plasa anumite controale ( butoane, cursoare, ferestre-text) și se crează automat codul sursă pentru fiecare, cod legat în principal de modelul vizual a acestora.

Windows Presentation Foundation

Este folosit pentru a crea formulare de introducere a datelor generând cod XAML care poate fi stocat în sistem sau în baza de date pentru a fi refolosit. Se pot plasa controale din fereastră de instrumente prin intermediul unei ferestre de proprietăți. Setează legături de date pentru fiecare tip de control prin intermediul unei ferestre de proprietăți.

Dezvoltare WEB

Suportă limbajele HTML, CSS, JavaScript pentru crearea și administrarea aplicațiilor WEB.

Proiectant Clase

Este folosit pentru a edita clase (inclusiv membrii) folosind modelul UML. Proiectantul de clasa poate genera cod pentru clase și metode. Poate deasemena genera diagrame de clase din clase scrise manual.

Limbajul C#

Introducere

Limbajul C# a fost dezvoltat de o echipa de ingineri de la Microsoft și este un limbaj simplu, circa 80 de cuvinte cheie și 12 tipuri de date predefinite. Permite programarea structurată și orientate obiectual, conform conceptelor programării profesioniste. Principiile de baza ale programării obiectuale(încapsulare, moștenire, polimorfism) sunt elemente fundamentale ale programării C#. La baza acestuia stă limbajul C sar sunt preluate și principiile de programare din C++. Sintaxa este apropiată și limbajului Java.

Au fost adăugate o serie de tipuri noi de date sau funcțiuni diferite ale datelor din C++, iar în spiritual realizării unor secvențe de cod sigure (safe) , unele funcțiuni au fost adăugate(interfețe), diversificate (tipul string) sau chiar eliminate(moștenirea multiplă și pointerii către funcții). Unele funcțiuni (cum ar fi accesul direct la memorie folosind pointeri) au fost păstrate, dar secvențele de cod corespunzătoare se consideră nesigure.

În momentul în care s-a decis să se dezvolte C# s-a hotărât că acesta să aibă o legătură deosebită cu mediul sau de rulare, arhitectură .NET. Pe de o parte, C# a fost dezvoltat pentru crearea codului pentru arhitectură .NET, iar pe de altă parte bibliotecile utilizate de C# sunt cele ale arhitecturii .NET.

Tipuri de date in C#

Limbajul C# include două categorii de tipuri de date: tipuri valorice și tipuri referință. Tipurile valoare se clasifică astfel:

Tipuri numerice

Intregi

În C# sunt definite 9 tipuri de întregi: char, byte, sbyte, short, ushort, int, uint, long, ulong. Cu excepția lui char, toate sunt folosite la calcule numerice.

Variabilele de tip int se utilizează în controlul buclelor, indexarea tablourilor, aritmetică normală cu numere întregi. Se recomandă că atunci când e nevoie de o valoare ( fără semn ) care depășește domeniul lui int, să se folosească uint. În cazul valorilor mari este recomandat long, iar la cele fără semn, ulong.

În virgulă mobilă

Tipurile în virgulă mobilă, se utilizează pentru reprezentarea numerelor care au parte fractionara. Există două tipuri: float și double.

Decimal

Tipul decimal este folosit în calcule. El are avantajul că elimina erorile de rotunjire atunci când se lucrează cu valori fracționare, pentru că poate reprezenta în mod precis până la 28 de poziții zecimale.

Tipul Bool

Acest tip reține valorile de adevăr ( true ) și ( fals ). Orice expresie de tipul bool va lua una din aceste valori.

Clase

O clasa este o structura care contine date constante si variabile, functii ( metode, proprietati, evenimente, operatori supraincarcati, operatori de indexare, constructori, destructori ) si tipuri imbricate.

Clasele de declara asemanator cu cele din C++, cu unele mici deosebiri de sintaxa (declaratiile de clase nu se termina cu „;“, modificatorii de acces public si privat se aplica pentru fiecare element in parte). Cuvantul cheie this este prezent in continuare, dar este folosit ca o referinta ( nu mai are sintaxa de pointer ).

Modificatorii de acces in C# sunt:

public: accesibil din interiorul și din exteriorul clasei

protected: accesibil numai din interiorul clasei și a claselor derivate

internal: accesibil din interiorul din exteriorul clasei dar numai în cadrul

assembly-ului (proiectului in VS)

protected internal: accesibil numai din interiorul clasei și a claselor derivate în

cadrul assembly-ului (proiectului in VS)

private: accesibil numai din interiorul clasei

Constructori si destructori

Constructorul este o metodă care are același nume cu clasa de care aparțin și se cheamă ori de câte ori este nevoie pentru a crea o instanța a clasei. Aceștia au o sintaxa asemănătoare cu cea din C++ ( au același nume cu clasa de care aparțin și nu au tip returnat ). Diferența apare la lista de initializare: în C# în lista de initializare nu pot apărea decât cuvintele cheie this ( care permite apelarea unui alt constructor din aceeași clasa).

Destructorii sunt metode care au același nume cu clasa din care fac parte, precedat de semnul ~. Nu au tipuri de acces, nu au argumente și nu permit niciun fel de specificatori ( static, virtual etc). Nu pot fi invocați explicit, ci numai de librăriile .NET specializate pe recuperarea memoriei. Ordinea și momentul în care sunt apelați sunt nedefinite, că și firul de execuție în care sunt executați. Este bine că în aceste metode să se dealoce numai obiectele care nu pot fi dealocate automat de .NET și să nu se facă niciun alt fel de alte operații.

Proprietăți

Proprietățile sunt membrii în clasa care facilitează accesul la diferite caracteristici ale clasei. Deși sunt utilzate la fel că atributele, proprietățile sunt de fapt metode și nu reprezintă locații de memorie.

Declararea proprietăților se face sub formă:

tip NumeProprietate

{

get { … }

set { …}

}

După cum se poate observă, o proprietate este alcătuită de fapt din două funcții; din declarația de mai sus compilatorul va genera automat două funcții: tip get_NumeProprietate() și void set_NumeProprietate(tip value). Metodele de tip set primesc un parametru implicit denumit value care conține valoarea atribuită proprietății.

Moștenire

Moștenirea ( numită și derivare ) permite crearea unei clase derivate care conține implicit toți membrii clasei de baza ( cu excepția constructorilor și destructorilor ) unei alte clase numite de baza. În C# o clasa poate avea numai o clasa de baza, nu există moștenire multiplă. În cazul în care nu se specifică nicio clasa de baza, compilatorul consideră că este derivată implicit din clasa System.Object. Sintaxa este asemănătoare cu cea din C++ cu excepția faptului că există un singur tip de moștenire echivalent derivării publice din C++.

Moștenirea este tranzitivă, în sensul că dacă A este derivată din B și B este derivată din C, implicit A va conține și membrii lui C, evident și pe ai lui B. Prin moștenire, o clasa derivată extinde clasa de baza. Clasa derivată poate adaugă noi membrii, dar nu îi poate elimina pe cei existenți.

Deși clasa derivată conține implicit toți membrii clasei de baza, asta nu înseamnă că îi și poate accesa. Membrii privați ai clasei de baza există și în clasa derivată, dar nu pot fi accesați. În acest fel, clasa de baza își poate schimbă la nevoie implementarea internă fără a distruge funcționalitatea claselor derivate existente.

Tratarea Excepțiilor

Tratarea excepțiilor permite interceptarea și tratarea erorilor care astfel ar conduce la terminarea programului și oferă un mecanism pentru semnalarea condițiilor excepționale care pot apărea în timpul execuției programului.

Excepțiile sunt de fapt obiecte derivate din System.Exception care conțin informații despre tipul erorii și locul unde au apărut. Se pot folosi excepțiile predefinite, dar se pot crea și excepții noi prin definirea unei clase derivate din System.Exception. Lansarea unei excepții se face folosind instructiunea throw. Această are că efect oprirea execuției funcției și transferul controlului către apelant.

Tratarea excepțiilor se face utilizând instrucțiunile try și catch și finally în forma:

try

{

// instrucțiuni

}

catch (Exceptie1 e1)

{

// tratare exceptie 1

}

catch (Exceptie1 e2)

{

// tratare exceptie 1

}

finally

{

// instructiuni

}

Succesiunea execuției este următoarea:

Se execută instrucțiunile din blocul try până la apariția unei excepții; în căzul în care nu se declanșează nicio excepție se execută întregul bloc;

Dacă a apărut o excepție se compară tipul exceptiei cu tipurile din lista de catch și se execută blocul de instrucțiuni corespunzător pentru tratarea excepției;

Comparația se face în ordinea în care apar blocurile catch;

După execuția unui bloc catch nu se continuă căutarea în celelalte blocuri catch, deci excepțiile mai generale trebuie puse după excepțiile particulare;

Se execută instrucțiunile din blocul finally, indiferent dacă a apărut sau nu o excepție și indiferent dacă această a fost tratată sau nu

Dacă nu a apărut nicio excepție sau dacă excepția a fost tratată printr-un bloc catch execuția continuă cu instrucțiunile de după blocul finally, altfel excepția este programată în apelator.

Blocurile catch si finally nu sunt obligatorii ( unul dintre ele poate lipsi ).

Proiect Nou in Microsoft Visual Studio

Pentru crearea unui proiect nou este necesar să urmăm câțiva pași:

Pentru a crea un proiect nou se selectează File New Project

Apare o nouă fereastră în care trebuie să selectăm Windows Forms Application

Crearea unui program C#

Microsoft Visual Studio și limbajul de programare C# ne oferă posibilitatea de a crea aplicații, de exemplu construirea unei aplicații de tip interfață, prin intermediul căreia să putem comunica cu un anumit dispozitiv.

Ca un exemplu pentru crearea unui astfel de program vom utiliza un buton care să verifice dacă portul serial este pregătit pentru transmisia de date, cu alte cuvinte dacă este initializata comunicația, și care să trimită un caracter pe portul serial.

După urmarea pașilor specificați anterior, vom avea disponibilă o fereastă de proiectant ( designer ). În partea stânga este disponibilă o fereastră de instrumente de unde vom selecta un buton și îl vom plasa în fereastră de proiectare.

Următorul pas este să dăm dublu-clic pe buton și va apărea codul generat automat așa cum am amintit anterior. Este realizată o legătură între controlul adăugat, în cazul de față un buton, și codul generat automat pentru acesta. Acest cod generat automat are legătură în mare parte cu modelul de vizualizare al acestuia.

Tot ce a mai rămas de făcut este să tastăm codul C# necesar pentru a realiza cele menționate anterior.

Am ales ca exemplu implementarea unui sistem cu tratare a excepțiilor. În cazul de față, dacă la apelul port.Write() aplicația ar fi eșuat și trasmiterea pe portul serial nu ar mai fi fost posibilă, s-ar executa codul din catch și ne va apărea un mesaj de informare. Alfel, în caz contrat, aplicația ar fi “crăpat”.

În acest moment putem spune că avem un mic program realizat în Microsoft Visual Studio cu ajutorul limbajului de programare C# în care se încearcă testarea portului serial și trimiterea de date înspre acesta.

MPLAB IDE

Introducere

MPLAB este un program software al sistemului de operare Windows care rulează pe un PC, cu ajutorul căruia putem dezvolta aplicații pentru microcontolerele de la Microchip. Este denumit Mediu de Dezvoltare Integrat ( IDE ) deoarece oferă un singur mediu de dezvoltare pentru a dezvolta cod pentru microcontrolerele integrate.

Componente

Managerul de Proiect

Managerul de Proiect oferă integrarea și comunicarea între mediul de dezvoltare și instrumentele de limbaj.

Editorul

Editorul este un editor de cod programare echipat complet și care servește de asemenea ca și o fereastră în cadrul depanatorului.

Asamblorul / Fișierul de Legătură

Asamblorul poate fi folosit pentru a asambla un singur fișier, sau poate fi folosit împreună cu Fișierul de Legătură pentru a construi un proiect compus din mai multe fișiere sursă, librării sau obiecte recompilate. Fișierul de Legătură este responsabil cu poziționarea codului compilat într-o zonă de memorie a microcontrolerului folosit.

Depanatorul

Depanatorul de la Microchip ne dă voie să poziționăm puncte de întrerupere a programului, rularea pas cu pas a programului, ferestre de monitorizare, unde putem monitoriza valorile anumitor variabile, diferiți regiștrii etc. Lucrează în strânsă legătură cu Editorul. Depanatorul folosit oferit din cadrul mediului de dezvoltare MPLAB este ICD 2.

Compilatorul

Compilatorul folosit este oferit de Microchip, Microchip C30 Compiler, și se instalează separat de mediul de dezvoltare MPLAB.

Limbajul ANSI C

Scurta Prezentare

Este un limbaj de programare standardizat, compilat, de nivel mediu. Este implementat pe majoritatea platformelor de calcul existente azi, și este cel mai popular limbaj de programare pentru scrierea de software de sistem. Este în strânsă legătură cu hardware-ul, fiind cel mai apropiat de limbajul de asamblare față de majoritatea celorlalte limbaje de programare.

Acest limbaj de programare a fost creat având ca scop important de a face ca programele mari să poată fi scrise mai ușor și cu mai puține erori. Are următoarele caracteristici importante:

Este un limbaj de bază simplu, cu importante funcționalități, cum ar fi funcțiile matematice

Utilizează un set simplu de tipuri de date ce împiedică multe operații neintenționate

Folosește un limbaj preprocesor, preprocesorul C, pentru sarcini cum ar fi definirea de macrouri și includerea mai multor fișiere sursă

Permite accesarea la nivel jos a memoriei calculatorului prin intermediul pointerilor

Permite folosirea parametrilor, care sunt comunicați funcțiilor prin valoare și nu prin referință.

Alegerea acestui limbaj de programare pentru dezvoltarea aplicațiilor integrate pe microcontrolere este una destul de simplă.

Limbaj de programare foarte eficient

Limbaj de programare foarte popular și foarte bine înțeles

Limbaj de programare de nivel mediu, cu caracteristicile unui limbaj de nivel înalt ( suport pentru funcții și module), dar și cu caracteristici de limbaj de nivel jos, oferă acces către hardware prin intermediul pointerilor.

Este foarte ușor de înțeles chiar și pentru programatorii care folosesc limbajele de programare Java, C#, C++

Se găsesc foarte ușor cărți de specialitate, cursuri sau exemple de cod.

Proiect nou in MPLAB IDE

Pentru crearea unui proiect nou este necesar să urmăm câțiva pași.

Din bara de meniu trebuie să selectăm Project Project Wizard

Următorul pas este să apăsăm Next și să selectăm tipul de microcontroler folosit pentru dezvoltarea aplicației, în cazul de față dsPIC30F2020.

În acest pas trebuie să selectăm tipul de compilator dorit, pentru compilarea codului, în funcție de codul scris, putem să selectăm un compilator pentru cod C sau un compilator pentru cod asamblare, în cazul de față se selectează compilatorul Microchip C30 Compiler.

Tot ce ne mai rămâne de făcut este să selectăm locația la care să se salveze proiectul nou creat. Aici avem opțiunea să creăm un proiect nou sau putem să reconfigurăm unul deja existent.

Crearea unui program C

Mediul de dezvoltare MPLAB împreună cu limbajul de programare C ne oferă posibilitatea de a crea programe care pot fi integrate pe un microcontroler, împreună formând un sistem dedicat. Ca și exemplu pentru un astfel de program, am putea alege ceva simplu, cum ar fi aprinderea și stingerea unui LED.

În cele menționate anterior, am văzut cum se poate crea un proiect nou în mediul de dezvoltare MPLAB, tot ce ne mai rămâne de făcut este să creăm programul în sine.

După cum se poate observa, am stabilit că portul trebuie să fie setat ca ieșire, prin intermediul registrului PORTB, am inițializat registrul de direcție TRISB. După aceea în bucla infinită while (1) aprindem led-ul punând valoarea maximă 0xFF în registrul PORTB, o întârziere, după care led-ul este stins, 0x00 în registrul PORTB. Avem nevoie de acea întârziere deoarece altfel nu am sesiza aprinderea și stingerea, acest proces se desfășoară foarte rapid.

În cele din urmă trebuie să vedem dacă programul nostru conține erori, de sintaxă, de logică etc, cu alte cuvinte dacă se construiește.

În acest moment putem spune că am realizat un program C cu ajutorul mediului de dezvoltare MPLAB care să facă ce ne-am propus.

PREZENTAREA APLICATIEI

Scurta Introducere

Folosind notiunile teoretice prezentate in capitolele anterioare am realizat un tool care permite comunicatia cu un dispozitiv ( ECU ), in mediul de dezvoltare Microsoft Visual Studio, si deasemenea o aplicatie integrata pe dispozitiv ( ECU ) in mediul de dezvoltare MPLAB IDE.

Tool-ul realizat ofera o interfata prietenoasa cu utilizatorul, nefiind necesare cunostinte avansate de operare a calculatorului pentru a-l putea utiliza.

Meniul permite deplasarea usoara intre comenzi, elementele de meniu avand denumiri explicite, astfel utilizatorul nu intalneste niciun fel de dificultati in utilizarea acestuia.

Tool-ul comunica cu dispozitivul si ii trimite comenzi. Reactia la aceste comenzi este programata in cadrul dispozitivului, prin intermediul aplicatiei integrate. Comenzile sunt trimise serial si sunt executate.

Functiile de baza pe care acest tool le poate face sunt acelea de a comanda dispozitivul electronic, dar si de a primi un raspuns in urma acestor comenzi, ca rezultat al executiei lor, functii de monitorizare a partii hardware. Cu ajutorul acestor functii se poate testa si valida proiectul.

Pe cealalta parte s-a realizat un sistem dedicat, menit sa simuleze actionarea motorului de curent continuu, componenta a pompei hidraulice din cadrul sistemului de franare al unui automobil.

Pompa hidraulica este alcatuita dintr-un motor de curent continuu si doua sau mai multe pistoane. Daca softul de control cere activarea pompei, atunci motorul incepe sa se roteasca si lichidul de frana va incepe sa curga. Pompa hidraulica are doua mari sarcini:

Impingerea lichidului de frana in sistem

Pedala de frana este apasata, supapa de admisie este inchisa, supapa de evacuare este deschisa. In tot acest timp motorul se roteste actionand pistoanele pentru a face posibil procesul.

Mentinerea presiunii fara ca pedala sa fie actionata

Motorul se roteste actionand pistoanele astfel incat presiunea necesara sa fie mentinuta.

Aplicatia realizata cu ajutorul mediului de dezvoltare MPLAB si a limbajului de programare C actioneaza motorul in ambele sensuri, bazat pe un sistem de intreruperi, la diferite viteze, simuland astfel necesitatea de a impinge lichidul de frana in sistem si de a mentine presiunea acestuia in diferite conditii. Aplicatia realizata pune la dispozitie un sistem de monitorizare pentru anumiti parametri cum ar fi numarul de rotatii ale motorului intr-o unitate de timp, sau stocarea valorii unui consum mare de curent in caz ca intervine o problema mecanica a pistoanelor si totodata cu asta blocarea motorului, valori care ulterior pot fi vizualizate in tool-ul pus la dispozitie, sau pe un LCD pus la dispozitie in cadrul dispozitivului.

Pentru a putea fi controlat cu usurinta si in timpul development-ului dispozitivului electronic i-au fost atasate controale hardware, doua butoane pentru sensul de rotatie si un potentiometru pentru marirea sau micsorarea numarului de rotatii, bazat pe un sistem cu PWM.

Proiectare

Schema Bloc Proiect

Schema Bloc Program Tool (Interfata Grafica)

Schema Bloc Program Microcontroler

Comunicatia intre Tool si ECU

Comunicatia intre Tool-ul realizat si ECU se face serial prin intermediul unui driver FTDI incorporat pe dispozitiv. S-a realizat o comunicatie prin intermediul unui astfel de driver pentru a facilita comunicatia intre dispozitiv si mai multe potentiale PC-uri care nu dispun de un conector RS232.

In prima faza se trimite un mesaj din interfata grafica catre dispozitiv, din acel moment controlul asupra ECU-ului fiind numai din interfata grafica.

Apelarea unei operatii duce la schimburi de date. Schimbul de date consta in trimiterea unui mesaj care contine o comanda dupa care urmeaza executia acesteia de catre ECU, sau primirea unui raspuns continand date in urma executarii unor comenzi. In cazul in care initializarea nu s-a desfasurat cu succes, vom avea un mesaj de eroare.

Interfata Grafica

Aplicatia este formata din doua panouri:

Panoul de Initializare Comunicatie in care:

Se poate initializa comunicatia ( se deschide portul COM, se seteaza Baudrate, bitii de date, bitii de stop si se trimite comanda de conectare catre ECU )

Se poate deinitializa comunicatia ( se trimite comanda de deconectare catre ECU si se inchide portul COM)

O fereastra de tip text in care ne va apararea un mesaj daca inititializarea a fost sau nu cu succes

Panoul de control in care:

Se poate comanda rotirea motorului in ambele sensuri

Se poate opri rotirea motorului

Se poate comanda numarul de rotatii ale motorului printr-un sistem PWM

Se poate vizualiza consumul de curent al motorului

Se poate vizualiza numarul de rotatii ale motorului in unitatea de timp

Se poate vizualiza un consum de curent peste limita daca este cazul

Firul principal al algoritmului ( exemple pe codul aplicatiei)

Initializarea comunicatiei

Este declansata prin apasarea butonului „Connect“ din cadrul panoului de initializare a comunicatiei.

private void btnConnect_Click(object sender, EventArgs e)

{

if (port.IsOpen)

MessageBox.Show("Communication is already initialized!");

else

{

try

{

port.Open();

port.Write("c");

btnInit.BackColor = Color.GreenYellow;

txtCommunication.Text = "COM6 Opened BaudRate 9600 Initialization OK…";

}

catch (Exception ex)

{

MessageBox.Show("Nu se poate conecta la portul 6");

btnInit.BackColor = Color.Red;

txtCommunication.Text = "COM6 Access denied…";

}

}

}

Explicatie:

Prima data se verifica starea portului COM, daca acesta este deja deschis, inseamna ca avem deja comunicatia initializata si ne va aparea un mesaj de informare.

Daca portul nu este deschis, se apeleaza metoda port.Open() care deschide portul COM.

Dupa ce portul este deschis se apeleaza metoda port.Write(”c”) si se trimite catre ECU caracterul „c”, care reprezinta comanda de conectare cu ECU-ul.

Pentru interactiunea prietenoasa dintre interfata si utilizator butonul de „Connect” o sa isi schimbe culoarea in verde, iar in fereastra de tip text vom avea mesajul de informare despre initializarea cu succes a comunicatiei.

Daca intervine o problema in procesul de initializare a comunicatiei, vom avea un mesaj de informare, culoarea butonului „Connect” va deveni rosu, iar in fereastra de tip text vom avea un mesaj de eroare.

Pentru ca accesul la portul COM sa fie posibil acesta trebuie mai intai configurat.

SerialPort port = new SerialPort("COM4", 9600, Parity.None, 8, StopBits.One);

Se face acces la COM4, se seteaza baudrate-ul la 9600, niciun bit de paritate, 8 biti de date si un bit de stop.

Comandarea ECU-ului

Dupa ce faza de initializare este terminata cu succes, se pot trimite alte comenzi, cum ar fi comenzile de comanda a ECU-ului.

Actionare Motor spre stanga

Daca testul presupune in prima faza rotirea motorului inspre stanga, atunci aceasta comanda este declansata de apasarea butonului „Move Left“ din cadrul panoului de comanda.

private void btnLeft_Click(object sender, EventArgs e)

{

if (!port.IsOpen)

{

MessageBox.Show("Communication is not initialized!", "Message", MessageBoxButtons.OK, MessageBoxIcon.Warning);

}

else

{

try

{

port.Write("l");

bool btnLeft_Clicked = true;

}

catch (Exception ex)

{

MessageBox.Show("Rotate to the left is not possible", "Message", MessageBoxButtons.OK, MessageBoxIcon.Warning);

}

}

}

Explicatie:

In prima faza se verifica starea portului COM, cu alte cuvinte daca avem comunicatia initializata.

Daca acest proces nu a fost cu succes, s-au nici macar nu s-a incercat initializarea comunicatiei, atunci aceasta comanda nu va putea fi executata si vom avea un mesaj de alerta.

Daca procesul de initializare a comunicatiei a fost realizat cu succes, atunci se apeleaza metoda port.Write(“l“), metoda prin intermediul careia se trimite comanda catre ECU de actionare a motorului inspre stanga, trimitand caracterul „l“ pe serial.

Dupa ce comanda a fost trimisa se seteaza o variabila de tip bool cum ca butonul de trimitere a comenzii de rotire a motorului inspre stanga a fost apasat.

Daca aceasta comanda nu a fost trimisa cu succes, atunci vom avea un mesaj de alerta.

Actionare Motor spre Dreapta

Daca testul presupune rotirea motorului inspre dreapta, atunci aceasta comanda este declansata prin apasarea butonului „Move Right“ din cadrul panoului de comanda.

private void btnRight_Click(object sender, EventArgs e)

{

if (!port.IsOpen)

{

MessageBox.Show("Communication is not initialized!", "Message", MessageBoxButtons.OK, MessageBoxIcon.Warning);

}

else

{

try

{

port.Write("r");

}

catch

{

MessageBox.Show("Message was not sent");

}

bool btnRight_Clicked = true;

}

}

Explicatie:

In prima faza se verifica starea portului COM, cu alte cuvinte daca avem comunicatia initializata.

Daca acest proces nu a fost cu succes, sau nici macar nu s-a incercat initializarea comunicatiei, atunci aceasta comanda nu va putea fi executata si vom avea un mesaj de alerta.

Daca procesul de initializare a comunicatiei a fost realizat cu succes, atunci se apeleaza metoda port.Write(“r“), metoda prin intermediul careia se trimite comanda catre ECU de actionare a motorului inspre dreapta, trimitand caracterul „r“ pe serial.

Dupa ce comanda a fost trimisa se seteaza o variabila de tip bool cum ca butonul de trimitere a comenzii de rotire a motorului inspre dreapta a fost apasat.

Daca aceasta comanda nu a fost trimisa cu succes, atunci vom avea un mesaj de alerta.

Comanda PWM

Daca testul presupune urmarirea comportamentului ECU-ului, sau mai exact a motorului la diferite rotatii ale acestuia, sau numarul rotatiilor mai exact, in unitatea de timp, indiferent de sensul de rotatie, atunci avem la dispozitie un cursor, care trimite comanda de marire sau micsorare a numarului rotatiilor in unitatea de timp. Cursorul trimite pe serial catre ECU valori de la 0 la 1023, valori ce sunt interpretate in cadrul ECU-ului, mai exact de blocul de PWM.

private void trackBar1_Scroll(object sender, EventArgs e)

{

if (port.IsOpen)

{

PWM();

}

else

{

trackBar1.Value = 0;

MessageBox.Show("Communication is not initialized!", "Message", MessageBoxButtons.OK, MessageBoxIcon.Warning);

}

}

Explicatie:

In prima faza se verifica daca este initializata comunicatia, daca este portul COM deschis

Daca avem comunicatia initializata se apeleaza functia PWM()

Daca procesul de initializare nu este efectuat cu succes, atunci vom avea un mesaj de alerta

Explicatie functie PWM():

private void PWM()

{

int stochez, temporary;

string a;

stochez = trackBar1.Value;

port.Write("p");

if (stochez < 1000)

port.Write("0");

if (stochez < 100)

port.Write("0");

temporary = stochez;

a = temporary.ToString();

port.Write(a);

}

Se declara doua variabile locale de tip intreg, stochez si temporary, si o variabila locala de tip sir de caractere.

In variabila de tip intreg stochez stocam valorile date de cursor, in momentul comandarii acestuia, valori de la 0 la 1023, asa cum am mentionat si anterior. Aceste valori trebuiesc trimise pe serial catre ECU pentru a putea fi interpretate de catre modulul de PWM.

Mediul de dezvoltare Microsoft Visual Studio prin intermediul metodei port.Write() ne permite sa stocam caractere in buffer-ul de serial, caractere ce ulterior vor fi trimise pe serial. Valorile care trebuiesc puse pe serial, date de cursor, sunt de tipul intreg. Acestea trebuiesc convertite la tipul caracter pentru a putea fi trimise pe serial. Trebuiesc trimise 4 caractere care, ulterior, interpretate si convertite in cadrul ECU-ului vor forma un intreg.

Se trimite pe serial caracterul „p“ care interpretat de catre ECU va face diferenta intre 2 mesaje succesive. In momentul in care va primi caracterul „p“ va sti ca a sosit urmatoarea valoare.

Se testeaza daca valoarea data la un moment dat de catre cursor este mai mica decat 1000 sau decat 100. Acest test se realizeaza pentru a ne ajuta la formarea mesajului de 4 caractere, daca valoarea este mai mica decat 1000 atunci primul caracter trimis va fi „0“. Daca valoarea este mai mica si decat 100, atunci se mai trimite inca un caracter „0“.

In ultima faza, valoarea data de catre cursor se converteste de la tipul intreg la tipul caracter si se trimite pe serial. Mesajul va fi interpretat ulterior de catre ECU si valoarea va fi trimisa catre modulul de PWM.

Comanda Citire Curent Consumat

Daca testul presupune monitorizarea consumului de curent al motorului, indiferent de sensul de rotatie sau numarul de rotatii ale acestuia in unitatea de timp, atunci aceasta comanda este declansata de apasarea butonului „View Curent“.

private void btnViewCurent_Click(object sender, EventArgs e)

{

if (!port.IsOpen)

{

MessageBox.Show("Communication is not initialized!", "Message", MessageBoxButtons.OK, MessageBoxIcon.Warning);

}

else

{

try

{

port.Write("w");

txtRead.Text = port.ReadExisting();

}

catch

{

MessageBox.Show("Message was not sent");

}

}

}

Explicatie:

In prima faza se verifica starea portului COM, daca portul este deschis sau inchis, cu alte cuvinte, daca a fost initializata comunicatia cu succes. Daca initializarea comunicatiei nu a fost cu succs, atunci vom avea un mesaj de alerta

Daca avem o comunicatie initializata cu succes, atunci prin intermediul metodei port.Write(“w“) se trimite pe serial caracterul „w“, care interpretat de catre ECU reprezinta comanda de citire a curentului consumat de motor.

In cadrul ECU-ul se interpreteaza comanda si se calculeaza valoarea curentului consumat de motor, vom vedea intr-un capitol urmator cum este posibil acest lucru, si se trimite catre interfata pentru a fi afisata.

Urmatorul pas este sa se ia valoarea trimisa de catre ECU, din buffer-ul de serial, prin intermediul metodei port.readExisting() si se va afisa in fereastra de tip text.

Comanda Citire Numar Rotatii Motor

Urmatorul lucru care trebuie monitorizat este numarul rotatiilor motorului in anumite conditii, in functie de cativa parametri cum ar fi sensul de rotatie, PWM sau daca ceva este in neregula cu motorul sau pistoanele actionate de acesta.

private void btnViewSpeed_Click(object sender, EventArgs e)

{

if (!port.IsOpen)

{

MessageBox.Show("Communication is not initialized!", "Message", MessageBoxButtons.OK, MessageBoxIcon.Warning);

}

else

{

try

{

port.Write("z");

txtSpeed.Text = port.ReadExisting();

}

catch

{

MessageBox.Show("Message was not sent");

}

}

}

Explicatie:

In prima faza se verifica starea portului COM, daca portul este deschis sau inchis, cu alte cuvinte, daca a fost initializata comunicatia cu succes. Daca initializarea comunicatiei nu a fost cu succs, atunci vom avea un mesaj de alerta

Daca avem o comunicatie initializata cu succes, atunci prin intermediul metodei port.Write(“z“) se trimite pe serial caracterul „z“, care interpretat de catre ECU reprezinta comanda de citire a numarului rotatiilor motorului pe secunda.

In cadrul ECU-ul se interpreteaza comanda si se calculeaza numarul rotatiilor motorului, vom vedea intr-un capitol urmator cum este posibil acest lucru, si se trimite catre interfata pentru a fi afisata.

Urmatorul pas este sa se ia valoarea trimisa de catre ECU, din buffer-ul de serial, prin intermediul metodei port.readExisting() si se va afisa in fereastra de tip text.

Comanda citire Valori Curent peste limita

Sa presupunem ca o problema mecanica a intervenit si unul dintre pistoane s-a blocat. Atunci motorul o sa incerce sa se roteasca in continuare, numai ca o sa fie un consum foarte mare de curent. Aceste valori trebuiesc stocate si afisate.

private void btnView_Consumptions_Click(object sender, EventArgs e)

{

if (!port.IsOpen)

{

MessageBox.Show("Communication is not initialized!", "Message", MessageBoxButtons.OK, MessageBoxIcon.Warning);

}

else

{

try

{

port.Write("a");

txtConsumptions.Text = port.ReadExisting();

if (txtConsumptions.Text == "000")

MessageBox.Show("Curent Consumption OK!");

}

catch

{

MessageBox.Show("Message was not sent");

}

}

}

Explicatie:

In prima faza se verifica starea portului COM, daca portul este deschis sau inchis, cu alte cuvinte, daca a fost initializata comunicatia cu succes. Daca initializarea comunicatiei nu a fost cu succs, atunci vom avea un mesaj de alerta

Daca avem o comunicatie initializata cu succes, atunci prin intermediul metodei port.Write(“a“) se trimite pe serial caracterul „a“, care interpretat de catre ECU reprezinta comanda de citire a valorii curentului peste limita.

In cadrul ECU-ul se interpreteaza comanda si se calculeaza valoarea curentului peste limita, vom vedea intr-un capitol urmator cum este posibil acest lucru, si se trimite catre interfata pentru a fi afisata.

Urmatorul pas este sa se ia valoarea trimisa de catre ECU, din buffer-ul de serial, prin intermediul metodei port.readExisting() si se va afisa in fereastra de tip text.

In ultima faza se verifica valoarea trimisa de catre ECU, daca aceasta este 0, atunci se trimite un mesaj de informare cum ca nu am avut niciun consum exagerat de curent.

Aplicatia Integrata pe ECU

Macrouri define

Pentru manipularea registrilor si a variabilelor s-au folosit cateva macrouri, care au fost declarate in cadrul fisierului main.h.

#define PWMH_Output IOCON2bits.PENH

#define PWML_Output IOCON2bits.PENL

#define TMR2_ON_Control_Bit T2CONbits.TON

#define TMR2_Interrupt_Flag_Status _T2IF

#define ADC_Conversion_Complete_Int_Flag_Status IFS0bits.ADIF

#define ADC_CHANNEL_4 ADCBUF4

#define ADC_CHANNEL_5 ADCBUF5

#define UART_Receive_Reg U1RXREG

#define UART_Transmit_Reg U1TXREG

#define PWM_Duty_Cycle_Reg PDC2

#define Curent_THRESHOLD 100

#define UART1_TX_Interrupt_Status_Flag IFS0bits.U1TXIF

#define UART1_RX_Interrupt_Status_Flag IFS0bits.U1RXIF

#define INTERRUPT_ENABLE_CONTROL IEC1

#define PWM_Control_Immediate_Update PWMCON2bits.IUE

#define U1_Baud_Rate_Generator U1BRG

#define UART_Module_Enable U1MODEbits.UARTEN

#define LoopBack_Disable U1MODEbits.LPBACK

#define High_Baudrate_Disabled U1MODEbits.BRGH

#define UART_Transmit_Enable U1STAbits.UTXEN

#define LED_Left _LATE5 /* Led Stanga */

#define LED_Right _LATE4 /* Led Dreapta */

#define INT0 IFS0bits.INT0IF

#define INT1 IFS1bits.INT1IF

#define INT2 IFS1bits.INT2IF

Module Microcontroler

Pentru realizarea aplicatiei a fost nevoie de configurarea catorva module ale microcontrolerului dsPIC30f2020.

Modul Comunicatie ( UART.c ):

Void UART_init ( void )

{

IPC2bits.U1RXIP = 7; /* prioritatea intreruperii, maxim */

IFS0bits.U1RXIF = 0; /* flag intrerupere U1RX */

IEC0bits.U1RXIE = 1; /* activeaza intreruperea de primire */

U1_Baud_Rate_Generator = 194; /* seteaza baudrate la 9600, U1BRG=((FCY/Baudrate Dorit)/16) – 1 unde FCY=30 000 000 */

UART_Module_Enable = 1; /* Activeaza modulul UART */

U1MODEbits.ALTIO = 1; /* Foloseste U1RX si U1TX alternat */

High_Baudrate_Disabled = 0; /* Dezactiveaza baudrate maxim */

U1MODEbits.PDSEL = 0; /* 8 biti de date, fara paritate */

U1MODEbits.STSEL = 0; /* 1 bit de stop */

UART_Transmit_Enable = 1; /* Activeaza modulul UART de Transmitere */

}

Modul PWM ( pwm.c):

void InitMCPWM(void)

{

/* PWM2 I/O Control Register register */

PWMH_Output = 0; /* PWM2H este controlat de modulul GPIO */

PWML_Output = 0; /* PWM2L este controlat de modulul GPIO */

PWM_Duty_Cycle_Reg = 0; /* Incarca registrul PDC2 cu valoarea initiala a Duty Cycle-ului */

/* PWM Module Common settings */

PTPER = 65000; /* PWM Period 2.15 usec @ 30 MIPS

PWM Period = PTPER*1.05nsec = 2.15 usec */

PWM_Control_Immediate_Update = 1; /* activeaza actualizarea imediata a PWM-ului */

PTCON = 0x8000; /* Activeaza modulul PWM */

}

Modul ADC ( adc.c ):

void ADC_init (void)

{

/* Set up the ADC Module */

ADCONbits.ADSIDL = 0; /* Opereaza in modul Idle*/

ADCONbits.FORM = 0; /* Rezultat in format intreg */

ADCONbits.SEQSAMP = 1; /* Activeaza prelevarea secventiala de date */

ADCONbits.ADCS = 5; /* Clock Divider setat pentru Fadc/14 */

ADPCFG = 0b1111111111001111; /* AN4 and AN5 sunt intrari analogice*/

ADSTAT = 0; /* Initializeaza registrul ADSTAT cu 0 (ADC status register) */

ADCPC1bits.TRGSRC2 = 0xC; /* Declanseaza conversia pe perioada TMR1 */

ADCPC1bits.IRQEN2 = 1; /* Activeaza intreruperea */

/* Set up Timer1 */

T1CON = 0; /* Timer cu 0 prescale */

TMR1 = 0; /* Reseteaza Timer-ul */

PR1 = TIMER_PERIOD; /* Incarca registrul de perioada */

/* Set up the Interrupts */

IFS0bits.ADIF = 0; /* reseteaza flag-ul de intrerupere ADC */

IFS0bits.T1IF = 0; /* reseteaza flag-ul de intrerupere pentru Timer1 */

IEC0bits.T1IE = 0; /* Intreruperea Timer este dezactivata */

IPC2bits.ADIP = 4; /* Seteaza prioritatea intreruperii de ADC */

IEC0bits.ADIE = 1; /* Activeaza intreruperea de ADC */

/* Enable ADC and Timer */

ADCONbits.ADON = 1; /* Porneste modulul ADC */

T1CONbits.TON = 1; /* Porneste Timer */

}

Blocul de Intreruperi ( interrupts.c ):

void Interrupt_init(void)

{

INTCON1 = 0x0000; /* Control intrerupere 1 */

INTCON2 = 0x0000; /* Control intrerupere 2 */

IFS0 = 0x0000; /* Flagul de status al intreruperii */

IFS1 = 0x0000; /* Flagul de status al intreruperii */

IEC0 = 0x0001; /* Se initilizeaza controlerul de activare a intreruperii */

IEC1 = 0x0003;

}

Modulul de Timer ( timer.c ):

void timer_init (void)

{

T2CON = 0x0030; /* Selecteaza bitii de prescale pt Timer2 */

PR2 = 0xFFFF; /* Registrul de perioada Timer2 */

TMR2_Interrupt_Flag_Status = 0; /* Flag-ul de intrerupere Timer2 */

_T2IE = 1; /* Activeaza intreruperea Timer2 */

TMR2_ON_Control_Bit = 0; /* Bitul de control Timer2 */

}

Modul Configurare LCD ( lcd.c ):

void lcd_goto(unsigned char pos)

{

LCD_RS = 0;

DelayUs(40);

lcd_write(0x80+pos);

}

void lcd_putch( const char c)

{

LCD_RS = 1; // write characters

lcd_write(c);//+0x51

}

void Lcd_Chr(unsigned char linie, unsigned char coloana, char cc)

{

if (linie==2) coloana+=0x40;

lcd_goto(coloana);

lcd_putch(cc);

}

void Lcd_Out(unsigned char linie,unsigned char coloana, const char * sir)

{

if (linie==2) coloana+=0x40;

lcd_goto(coloana);

lcd_puts(sir);

}

void lcd_puts(const char *s)

{

LCD_RS = 1; // write characters

while(*s)

lcd_write(*s++);

}

void

lcd_clear()

{

LCD_RS = 0;

lcd_write(0x1);

DelayNmSec(5);

}

void LCD_STROBE() {

LCD_EN = 1;

DelayUs(40);

LCD_EN = 0;

}

void lcd_init()

{

LCD_RS = 0;

LCD_EN = 0;

DelayNmSec(100); // wait 100mSec after power applied,

LCD_DATA &= 0xFFF0;

LCD_DATA |= 0x03;

LCD_STROBE();

DelayNmSec(5);

LCD_STROBE();

DelayUs(100);

LCD_STROBE();

DelayUs(100);

LCD_DATA &= 0xFFF0;

LCD_DATA |=0x02;

LCD_STROBE();

lcd_write((0x28));

lcd_write((0xC));

lcd_clear();

lcd_write((0x6));

lcd_write((0x80));

}

void lcd_write(unsigned char c)

{

DelayUs(40);

LCD_DATA &= 0xFFF0;

LCD_DATA |= ( ( c >> 4 ) & 0x0F );

LCD_STROBE();

LCD_DATA &= 0xFFF0;

LCD_DATA |= ( c & 0x0F );

LCD_STROBE();

Definire functie Delay ( delay_C30.c ):

void DelayNmSec(unsigned int N)

{

unsigned int j;

N=N*4;

while(N–)

for(j=0;j < MILLISEC;j++);

}

Prin aceasta functie se urmareste intarzierea softului in anumite locuri, in functie de necesitati.

Firul principal al algoritmului ( exemple pe codul aplicatiei)

In momentul in care ECU-ul este alimentat se va afisa pe LCD, Bigiu Gabriel pe prima linie si 2014, pe a doua linie.

Lcd_Out (1,0," BIGIU GABRIEL");

Lcd_Out (2,0," 2014 ");

DelayNmSec(500);

Dupa o mica intarziere, pe LCD se vor afisa, curentul cosumat si numarul rotatiilor pe secunda ale motorului, vom vedea intr-un capitol urmator.

Comandare ECU folosind HW

Comandare ECU pentru a roti motorul inspre stanga

Aceasta comanda se declanseaza cand se apasa butonul de actionare a motorului inspre partea stanga. In momentul in care butonul este apasat se apeleaza o intrerupere, _INT2, un flag al acestei intreruperi se seteaza, cum ca intreruperea a venit, si se trateaza rutina de tratare a intreruperii.

void _ISR _INT2Interrupt( void )

{

INT2 = 0;

if ( Right_Btn_Flag == 0 )

{

Left_Btn_Flag ^= 1;

LED_Left ^= 1;

PWML_Output ^= 1;

TMR2_ON_Control_Bit ^= 1;

}

}

Explicatie:

In prima faza trebuie sa resetam flag-ul de intrerupere, in caz contrar, nicio alta intrerupere nu ar mai putea fi declansata.

Se verifica starea butonului de actionare a motorului in partea dreapta. Daca acest buton este apasat, atunci nu se va intampla nimic.

Urmatorul pas, daca motorul nu este actionat deloc, se seteaza flag-ul de stare al acestui buton, prin intermediul operatiei logice XOR, Led-ul corespunzator acestui buton va fi aprins, tot prin intermediul operatiei XOR.

Se seteaza bitul PENL din cadrul registrului de control PWM care serveste ca intrare a driver-ul de control al motorului si motorul este actionat cu sensul de rotatie stanga, cu o valoare de Duty Cycle initiala.

In registrul de control al TIMER2, se seteaza bitul de start al timer-ului pentru a putea inregistra numarul rotatiilor motorului.

In continuare se va prezenta comportamentul daca motorul este deja actionat in sensul de rotatie stanga.

Se reseteaza flag-ul de stare al acestui buton, prin intermediul operatiei logice XOR, si Led-ul corespunzator acestui buton va fi stins, tot prin intermediul operatiei XOR

Se reseteaza bitul PENL din cadrul registrului de control PWM, care serveste ca intrare a driver-ului de control al motorului si motorul care este actionat cu sensul de rotatie stanga cu o anumita valoare de Duty Cycle, va fi oprit.

In registrul de control al TIMER2, se reseteaza bitul de start al timer-ului.

Pentru a putea numara rotatiile motorului, pe acesta s-a aplicat un senzor hall. Pe axul motorului este un mic magnet care, la o rotatie a axului, in timp ce magnetul trece pe langa senzor ii da acestuia un semnal. De fiecare data cand se intampla acest lucru se apeleaza o intrerupere, INT0, care in rutina de tratare a acesteia incrementeaza o variabila. De fiecare data cand intreruperea se apeleaza, variabila “rotations_cnt” este incrementata si astfel se retine numarul rotatiilor motorului.

void _ISR _INT0Interrupt(void)

{

DelayNmSec(10);

INT0 = 0;

rotations_cnt++;

}

Explicatie:

Flagul de stare al intreruperii este resetat

Variabila „rotations_cnt“ este incrementata

De fiecare data cand butonul este apasat si motorul actionat, asa cum am spus anterior, TIMER2 este pornit si acesta genereaza o intrerupere, in a carei rutine de tratare a intreruperii sunt prelucrate numarul de rotatii ale motorului.

void __attribute__((__interrupt__, __auto_psv__)) _T2Interrupt(void)

{

TMR2_Interrupt_Flag_Status = 0;

Rotations_per_Second= ( rotations_cnt * 1.7 );

rotations_cnt = 0;

}

Explicatie:

Flagul de intrerupere al TIMER-ului 2 este resetat

Se face acest calcul al numarului de rotatii pentru a putea monitoriza numarul de rotatii pe secunda

Se reseteaza variabila care stocheaza numarul de rotatii

Comanda PWM si verificare Curent Consumat

Dupa cum am mai precizat, unul dintre testele la care este supus motorul , este acela de a vedea si urmari comportamentul HW si SW al acestuia si la diferite viteze de rotatie, indiferent de sensul de rotatie. Pentru a realiza acest lucru trebuie manipulat modulul de PWM, mai exact valoarea de Duty Cycle, din cadrul modulului de PWM. Comandarea PWM-ului se face din controlul hardware, mai exact potentiometrul pus la dispozitie. Acest potentiometru este conectat la pinul care serveste drept intrare analogica in blocul de ADC. Potentiometrul da o valoare analogica, aceasta este prelucrata si convertita de catre modulul de ADC, valoare care serveste drept Duty Cycle pentru modulul de PWM.

Dupa ce motorul este actionat cu un anumit PWM trebuie sa verificam consumul de curent al acestuia, sa vedem daca este unul normal sau daca avem un consum exagerat de curent in anumite situatii. Pentru a realiza acest lucru, in serie cu pinul de GND al motorului s-a montat un rezistor de sunt, dupa care prin intermediul unui amplificator operational, serveste drept intrare in blocul de ADC. La fel ca si valoarea data de potentiometru si aceasta este tot o valoare analogica, care reprezinta tensiunea de pe motor. Aceasta este transmisa catre modulul de ADC, unde cu valoarea rezistentei din circuit se calculeaza folosind legea lui Ohm, curentul consumat.

In modulul de initializare al ADC-ului printre altele se activeaza si intreruperea de ADC, astfel incat in momentul in care modulul de ADC cere sa fie folosit se genereaza intreruperea acestuia. Calcularea curentului consumat, valoarea de Duty Cycle si celelalte calcule se fac in rutina de tratare a intreruperii.

void __attribute__ ((__interrupt__)) _ADCInterrupt(void)

{

ADC_Conversion_Complete_Int_Flag_Status = 0;

ADC_Channel_4_Result = ADC_CHANNEL_4;

ADC_Channel_5_Result = ADC_CHANNEL_5;

ADC_Channel_4_Result= ( ( ADC_Channel_4_Result * 5 ) / 10 );

if ( ADC_Channel_4_Result > Curent_THRESHOLD )

current_value = ADC_Channel_4_Result;

if( CONNECTED_TO_THE_INTERFACE == 0 )

PWM_Duty_Cycle_Reg = 60 * ADC_Channel_5_Result;

if ( ( TMR2_ON_Control_Bit == 0 ) && ( CONNECTED_TO_THE_INTERFACE == 0 ) )

Rotations_per_Second = 0;

}

Explicatie:

Se reseteaza flag-ul de intrerupere ADC.

Conversia valorii tensiunii citite pe motor, se face pe pinul corespunzator canalului 4 al blocului ADC. Se ia aceasta valoare a tensiunii si cu legea lui Ohm se calculeaza curentul consumat.

Urmatorul pas este sa se testeze daca valoarea curentului consumat este peste o anumita limita prestabilita. Daca valoarea este mai mare decat aceasta limita, atunci aceasta se memoreaza in variabila “current_value” si se va trimite ulterior pe serial catre PC pentru a fi monitorizata in interfata grafica.

Conversia valorii date de potentiometru pentru a comanda valoarea de Duty Cycle pentru PWM se face pe pinul corespunzator canalului 5 al blocului ADC.

Se efectueaza un test pentru a ne asigura ca ECU-ul nu este conectat la PC. In acest caz, se ia rezultatul conversiei valorii date de potentiometru, se inmulteste cu 60 si se salveaza in registrul de PWM Duty Cycle. Aceasta inmultire cu 60 se face din cauza ca registrul de PWM Duty Cycle este pe 16 biti iar registrul de ADC este pe 10 biti.

In continuare se testeaza daca TIMER2 nu este activat nici de vreun control hardware nici de PC prin intermediul tool-ului pus la dispozitie si in acest caz se reseteaza variabila care stocheaza numarul rotatiilor pe secunda ale motorului.

Comandare ECU pentru a roti motorul inspre dreapta

Aceasta comanda se declanseaza cand se apasa butonul de actionare a motorului inspre partea dreapta. In momentul in care butonul este apasat se apeleaza o intrerupere, _INT1, un flag al acestei intreruperi se seteaza, cum ca intreruperea a venit, si se trateaza rutina de tratare a intreruperii.

void _ISR _INT1Interrupt ( void )

{

DelayNmSec( 10 );

INT1 = 0;

if( Left_Btn_Flag == 0 )

{

Right_Btn_Flag ^= 1;

LED_Right ^= 1;

PWMH_Output ^= 1;

TMR2_ON_Control_Bit ^= 1;

}

}

Explicatie:

In prima faza trebuie sa resetam flag-ul de intrerupere, in caz contrar, nicio alta intrerupere nu ar mai putea fi declansata.

Se verifica starea butonului de actionare a motorului in partea stanga. Daca acest buton este apasat, atunci nu se va intampla nimic.

Urmatorul pas, daca motorul nu este actionat deloc, se seteaza flag-ul de stare al acestui buton, prin intermediul operatiei logice XOR, Led-ul corespunzator acestui buton va fi aprins, tot prin intermediul operatiei XOR.

Se seteaza bitul PENH din cadrul registrului de control PWM care serveste ca intrare a driver-ului de control al motorului si motorul este actionat cu sensul de rotatie dreapta, cu o valoare de Duty Cycle initiala.

In registrul de control al TIMER2, se seteaza bitul de start al timer-ului pentru a putea inregistra numarul rotatiilor motorului.

Afisare Valori pe LCD

Dupa ce am vazut cum se realizeaza conversia valorilor analogice, mai exact calculul valorii curentului cosumat sau valoarea pentru Duty Cycle, tot ce mai ramane de facut, este ca aceste valori sa fie afisat pe ecranul LCD pus la dispozitie in cadrul ECU-ului. LCD-ul, mai exact functiile de afisare pe acesta si celelalte configurari, au fost initializate, cum am specificat in capitolul anterior.

while ( CONNECTED_TO_THE_INTERFACE == 0 )

{

lcd_clear();

Lcd_Out (1,1,"SPEED= rps");

Lcd_Out (2,1,"CURRENT= mA");

while( CONNECTED_TO_THE_INTERFACE == 0 )

{

Curent_to_LCD = ADC_Channel_4_Result;

Lcd_Chr( 2, 12, 0x30 + Curent_to_LCD % 10 );

Curent_to_LCD = Curent_to_LCD / 10;

Lcd_Chr( 2, 11, 0x30 + Curent_to_LCD % 10 );

Curent_to_LCD = Curent_to_LCD / 10;

Lcd_Chr( 2, 10, 0x30 + Curent_to_LCD % 10 );

Curent_to_LCD = Curent_to_LCD / 10;

Lcd_Chr( 2, 9, 0x30 + Curent_to_LCD % 10 );

Rotations_to_LCD = Rotations_per_Second;

Lcd_Chr( 1, 10, 0x30 + Rotations_to_LCD % 10 );

Rotations_to_LCD = Rotations_to_LCD / 10;

Lcd_Chr( 1, 9, 0x30 + Rotations_to_LCD % 10 );

Rotations_to_LCD = Rotations_to_LCD / 10;

Lcd_Chr( 1, 8, 0x30 + Rotations_to_LCD % 10 );

Rotations_to_LCD = Rotations_to_LCD / 10;

Lcd_Chr( 1, 7, 0x30 + Rotations_to_LCD % 10 );

}

}

Explicatie:

In prima bucla “while”, pe prima linie se afiseaza “SPEED” si pe a doua linie a LCD-ului se afiseaza “CURRENT” si unitatile de masura pentru fiecare. Se pun separat in bucla pentru ca acestea se vor afisa in continuu, numai valorile fiecareia se vor schimba.

Prima valoare care este afisata este cea a curentului consumat. Aceasta valoare este un numar format din 4 cifre, iar pe LCD trebuie sa afisam caractere. Procedeul este urmatorul: Se ia prima cifra de la dreapta, se transforma in caracter si se afiseaza pe LCD cu ajutorul functiei Lcd_Chr(), dupa care cifra respective este eliminata. La fel se procedeaza in continuare pan cand toata valoarea este afisata.

Urmatoarea valoare care trebuie afisata este numarul rotatiilor motorului. Se foloseste aceeasi metoda ca la afisarea valorii curentului consumat.

Comandare ECU folosind PC ( Tool-ul creat )

In capitolul 2.2 am vazut cum a fost realizat Tool-ul care comunica cu ECU-ul, ce comenzi se pot trimite catre ECU, ce facilitati ne ofera si cum se poate folosi. In continuare se va prezenta cum reactioneaza ECU-ul la aceste comenzi, cum sunt ele interpretate si cum se trimit valorile inapoi catre Tool, pentru a putea fi urmarite.

Comunicatia dintre Tool si ECU

In capitolul de initializare a comunicatiei ( UART ), am vazut ca se activeaza modulul de UART, se activeaza intreruperea de primire pe UART si se seteaza prioritatea acesteia ca fiind maxima. Se face acest lucru pentru ca atunci cand Tool-ul doreste sa se conecteze cu ECU-ul, sa nu fie generata o alta intrerupere si aceasta conexiune sa nu se poata realiza.

In momentul in care se primeste comanda de initializare a comunicatiei, ECU-ul primeste caracterul “c” pe serial si intreruperea de UART este declansata. In rutina de tratare a acestei intreruperi vom vedea cum este interpretata comanda de conectare a Tool-ului cu ECU-ul.

if ( Data_from_UART_RX == 'c' )

{

CONNECTED_TO_THE_INTERFACE = 1;

LED_Left = 0;

LED_Right = 0;

PWML_Output = 0;

PWMH_Output = 0;

INTERRUPT_ENABLE_CONTROL = 0x0000;

TMR2_ON_Control_Bit = 1;

}

Explicatie:

In primul rand se seteaza flagul ca Tool-ul s-a conectat cu ECU-ul.

Ambele led-uri de pe ECU sunt stinse.

Iesirile pentru modulul de PWM pentru ambele sensuri de rotatie sunt resetate, cu alte cuvinte daca ECU-ul este comandat sa roteasca motorul, in momentul in care Tool-ul se conecteaza cu ECU-ul orice alta activitate se va opri.

Registrul de control al intreruperilor este setat pe 0, pentru ca, atata timp cat rutina de tratare a interuperii UART este active atunci celelalte intreruperi externe sa fie dezactivate.

Se seteaza bitul de start din registrul de control al Timer-ului 2.

Actionare Motor spre Dreapta

In momentul in care din Tool se trimite catre ECU comanda de actionare a motorului cu sensul de rotatie dreapta, atunci ECU-ul primeste pe serial caracterul „r“. Se va urmari cum este aceasta comanda interpretata.

if ( Data_from_UART_RX == 'r' && CONNECTED_TO_THE_INTERFACE == 1 )

{

if ( Interface_Left_Btn_Flag == 0 )

{

Interface_Right_Btn_Flag^=1;

LED_Right ^= 1;

PWMH_Output ^= 1;

}

}

Explicatie:

Se face primul test pentru a verifica daca ECU-ul este conectat la Tool si daca a primit caracterul “r” pe serial

Se verifica flag-ul butonului de actionare motor inspre stanga, daca acesta este setat inseamna ca motorul se roteste cu sensul de rotatie stanga si nimic nu se va intampla.

Urmatorul pas, daca flag-ul nu este setat, se seteaza flag-ul de stare al acestui buton, prin intermediul operatiei logice XOR, Led-ul corespunzator acestui sens de rotatie va fi aprins, tot prin intermediul operatiei XOR.

Se seteaza bitul PENH din cadrul registrului de control PWM care serveste ca intrare a driver-ului de control al motorului si motorul este actionat cu sensul de rotatie dreapta, cu o valoare de Duty Cycle initiala.

Actionare Motore spre Stanga

In momentul in care din Tool se trimite catre ECU comanda de actionare a motorului cu sensul de rotatie stanga, atunci ECU-ul primeste pe serial caracterul „l“. Se va urmari cum este aceasta comanda interpretata.

if ( Data_from_UART_RX=='l'&& CONNECTED_TO_THE_INTERFACE == 1 )

{

if (Interface_Right_Btn_Flag == 0 )

{

Interface_Left_Btn_Flag ^= 1;

LED_Left ^= 1;

PWML_Output ^= 1;

}

}

Explicatie:

Se face primul test pentru a verifica daca ECU-ul este conectat la Tool si daca a primit caracterul “l” pe serial

Se verifica flag-ul butonului de actionare motor inspre dreapta, daca acesta este setat inseamna ca motorul se roteste cu sensul de rotatie dreapta si nimic nu se va intampla.

Urmatorul pas, daca flag-ul nu este setat, se seteaza flag-ul de stare al acestui buton, prin intermediul operatiei logice XOR, Led-ul corespunzator acestui sens de rotatie va fi aprins, tot prin intermediul operatiei XOR.

Se seteaza bitul PENL din cadrul registrului de control PWM care serveste ca intrare a driver-ului de control al motorului si motorul este actionat cu sensul de rotatie stanga, cu o valoare de Duty Cycle initiala.

Comanda PWM

In momentul in care din Tool se trimite catre ECU comandarea PWM-ului, ECU-ul primeste pe serial caracterul „p“. Se va urmari cum este aceasta comanda interpretata.

if ( Data_from_UART_RX == 'p' && CONNECTED_TO_THE_INTERFACE == 1 )

{

RX_Data_Flag = 1;

Data_from_UART_RX = 0;

}

if ( RX_Data_Flag == 1 )

Data_from_UART_RX = Data_from_UART_RX – 48;

Explicatie:

Se seteaza acest flag pentru a stii ca pe serial se primesc caracterele care reprezinta valoarea ce trebuie memorata in registrul de PWM Duty Cycle si trebuiesc convertite din tipul caracter in tipul intreg.

Urmatorul pas este sa se reseteze aceasta valoare primita pentru a nu se amesteca cu urmatoarea si valoarea va fi una eronata.

Se verifica daca flagul este setat si in acest caz valorile ce vor fi preluate pe serial vor fi converite la tipul intreg

if ( RX_Data_Flag == 1 && CONNECTED_TO_THE_INTERFACE == 1 && i < 4 )

{

if ( i == 0 )

{

value = value + ( Data_from_UART_RX * 1000 );

}

if ( i == 1 )

{

value = value + ( Data_from_UART_RX * 100 );

}

if ( i == 2 )

{

value = value + ( Data_from_UART_RX * 10 );

}

if ( i == 3 )

{

value = value + ( Data_from_UART_RX * 1 );

}

i++;

}

if ( i == 4 )

{

PWM_Duty_Cycle_Reg = ( 60 * value );

i = 0;

RX_Data_Flag = 0;

value = 0;

}

Explicatie:

Dupa cum stim pe serial se primeste rand pe rand cate un caracter, acesta este convertit la tipul intreg si tot ce ne mai ramane de facut este sa formam un numar alcatuit din 4 cifre.

Daca variabila „i“ este 0 atunci inseamna ca o noua valoare trebuie formata.

Valoarea primita pe serial se inmulteste cu 1000 si este stocata in variabila „value“

Urmatoarea valoare primita se inmulteste cu 100 si este stocata in variabila „value“ plus valoarea anterioara a acesteia.

Urmatoarea valoare primita se inmulteste cu 10 si este stocata in variabila „value“ plus valoarea anterioara a acesteia.

Urmatoarea valoare primita se inmulteste cu 1 si este stocata in variabila „value“ plus valoarea anterioara a acesteia.

In tot acest timp variabila de test „i“ este incrementata. Cand aceasta este 4 atunci inseamna ca valoarea din 4 cifre este gata formata.

Urmatorul pas este ca aceasta valoare sa fie atribuita registrului de PWM Duty Cycle pentru a comanda astfel PWM-ul pentru actionarea motorului.

Variabilele „i“, „RX_Data_Flag“, si „value“ vor fi resetate pentru a putea ajuta la formarea urmatoarei valori.

Comanda Citire Curent Consumat

In momentul in care din Tool se trimite catre ECU comanda de citire a curentului consumat, ECU-ul primeste pe serial caracterul „w“. Se va urmari cum este aceasta comanda interpretata.

if ( Data_from_UART_RX == 'w' )

{

curent_transmit = ADC_Channel_4_Result / 1000;

curent_transmit = curent_transmit % 10;

curent_transmit = curent_transmit + 48;

UART_Transmit_Reg = curent_transmit;

curent_transmit = ADC_Channel_4_Result / 100;

curent_transmit = curent_transmit % 10;

curent_transmit = curent_transmit + 48;

UART_Transmit_Reg = curent_transmit;

curent_transmit = ADC_Channel_4_Result / 10;

curent_transmit = curent_transmit % 10;

curent_transmit = curent_transmit + 48;

UART_Transmit_Reg = curent_transmit;

curent_transmit = ADC_Channel_4_Result % 10;

curent_transmit = curent_transmit + 48;

UART_Transmit_Reg = curent_transmit;

}

Explicatie:

La fel cum am mai precizat si anterior pe serial se poate trimite numai un singur caracter o data. Valoarea care trebuie trimisa catre Tool pentru a putea fi afisata si urmarita este un numar intreg format din 4 cifre. Pentru ca acest proces de transmitere a valorii sa poata fi realizat, se ia pe rand fiecare cifra a numarului, se transforma in caracter si se trimite pe serial catre Tool.

Se ia valoarea curentului consumat pusa la dispozitie de catre ADC, se imparte la 1000 pentru a obtine prima cifra, se transforma in caracter dupa care este trimisa catre Tool prin intermediul registrului de transmitere UART. La fel se proedeaza si cu celelalte cifre pana cand tot numarul ce reprezinta valoarea curentului consumat este trimis catre Tool.

Comanda Citire Numar Rotatii Motor

In momentul in care din Tool se trimite catre ECU comanda de citire a numarului rotatiilor motorului, ECU-ul primeste pe serial caracterul „z“. Se va urmari cum este aceasta comanda interpretata.

if ( Data_from_UART_RX == 'z' )

{

Rotations_to_Interface = Rotations_per_Second / 1000;

Rotations_to_Interface = Rotations_to_Interface % 10;

Rotations_to_Interface = Rotations_to_Interface + 48;

UART_Transmit_Reg = Rotations_to_Interface;

Rotations_to_Interface = Rotations_per_Second / 100;

Rotations_to_Interface = Rotations_to_Interface % 10;

Rotations_to_Interface = Rotations_to_Interface + 48;

UART_Transmit_Reg = Rotations_to_Interface;

Rotations_to_Interface = Rotations_per_Second / 10;

Rotations_to_Interface = Rotations_to_Interface % 10;

Rotations_to_Interface = Rotations_to_Interface + 48;

UART_Transmit_Reg = Rotations_to_Interface;

Rotations_to_Interface = Rotations_per_Second % 10;

Rotations_to_Interface = Rotations_to_Interface + 48;

UART_Transmit_Reg = Rotations_to_Interface;

}

Explicatie:

Pentru a transmite valoarea numarului rotatiilor motorului in unitatea de timp se foloseste acelasi procedeu ca la transmiterea valorii curentului consumat.

Comanda citire Valori Curent peste limita

In momentul in care din Tool se trimite catre ECU comanda de citire a numarului rotatiilor motorului, ECU-ul primeste pe serial caracterul „z“. Se va urmari cum este aceasta comanda interpretata.

if ( Data_from_UART_RX == 'a' )

{

curent_value_to_interface = curent_value / 100;

curent_value_to_interface = curent_value_to_interface % 10;

curent_value_to_interface = curent_value_to_interface + 48;

UART_Transmit_Reg = curent_value_to_interface;

curent_value_to_interface = curent_value / 10;

curent_value_to_interface = curent_value_to_interface % 10;

curent_value_to_interface = curent_value_to_interface + 48;

UART_Transmit_Reg = curent_value_to_interface;

curent_value_to_interface = curent_value % 10;

curent_value_to_interface = curent_value_to_interface + 48;

UART_Transmit_Reg = curent_value_to_interface;

}

Explicatie:

Pentru a transmite valoarea numarului rotatiilor motorului in unitatea de timp se foloseste acelasi procedeu ca la transmiterea valorii curentului consumat.

Componente si Echipamente folosite

PICkit2

PICkit2 este un programator care permite programarea microcontrolerelor de tip PIC sau dsPIC . Acesta este conectat la calculator cu un conector de tip USB, poate fi conectat la ECU cu ajutorul unui conector care utilizeaza doi pini pentru intrarea respectiv ieșirea din dispozitiv iar linia de reset este folosită pentru a implementa circuitul de debugging și ICSP(In-Circuit Serial Programming).

Integrat FT232R

Este un circuit integrat care face conversia de la USB la RS232. S-a dorit fabricarea acestui integrat pentru ca aduce cateva facilitati utilizatorului, cel mai important fiind faptul ca utilizatorul se poate conecta si comunica cu un dispozitiv pe orice PC chiar daca ii lipseste conectorul RS232. Implementarea programelor este aceeasi, se foloseste acelasi protocol de comunicatie serial UART. Intregul protocol USB este manipulat in interiorul integratului.

Integrat L298

Circuitul integrat L298 este un circuit monolotic având 15 pini. Acest driver este folosit pentru tensiuni și curenți mari, el este constituit dintr-o punte dublă de tranzistori, ce acceptă nivele logice, standard TTL. Driverul L298, poate comanda: motoare de curent continuu sau motoare pas cu pas, bobine și relee.

Pentru activarea sau dezactivarea dispozitivului, driverul are 2 intrări de activare, independent de semnalele de intrare. În partea de jos a fiecărei punți se afla tranzistori, ai caror emitori sunt conectați împreună iar terminalul extern corespunzător este utilizat asemantor unui șanț extern.

Pentru ca diverul L298 să poată fi utilizat și la tensiuni mai mici, acesta este prevăzut cu o intrare suplimentară.

CONCLUZII SI DEZVOLTARI ULTERIOARE

Tool-ul are implementate comenzile de baza care pot fi aplicate motorului de curent continuu, de actionare in ambele sensuri, de comandare a modulului de PWM, de citire a curentului consumat, de citire a numarului rotatiilor acestuia. Cu ajutorul acestor functii se pot acoperi in mare parte functionalitatile principale si se poate testa si valida proiectul.

Implementari ulterioare:

Se pot realiza functii care sa poata citi si scrie in memoria EEPROM

Cu ajutorul functiilor de scriere si citire EEPROM se pot stoca valorile consumului mare de curent, in cazul anumitor erori care pot duce la asta, sau a altor tipuri de erori. Daca avem stocate in memoria EEPROM astfel de valori, vom putea face asa numitul „fault management”. Acest lucru trebuie implementat deoarece trebuie cunoscut cate erori pot fi maxim stocate si structura fiecareia pentru a putea fi interpretate.

Se pot simula anumite comportamente anormale sau anumite erori, pentru a monitoriza indeaproape sistemul si pentru a ne da seama ce dezvoltari ulterioare trebuie aduse.

BIBLIOGRAFIE

[1] H. Caprita – Sisteme Incorporate

[2] Brian W. Kernigham, Dennis M. Ritchie – THE C PROGRAMMING

[3] Addison Wesley – Embedded C

[5] Jack Ganssle si Michael Barr – Embedded Systems Dictionary

[6] John Crisp – Introduction to microprocessors and microcontrollers

[7] dsPIC30F2020 – Datasheet

[8] L298 – Stepper Driver Datasheet

BIBLIOGRAFIE

[1] H. Caprita – Sisteme Incorporate

[2] Brian W. Kernigham, Dennis M. Ritchie – THE C PROGRAMMING

[3] Addison Wesley – Embedded C

[5] Jack Ganssle si Michael Barr – Embedded Systems Dictionary

[6] John Crisp – Introduction to microprocessors and microcontrollers

[7] dsPIC30F2020 – Datasheet

[8] L298 – Stepper Driver Datasheet

Similar Posts