Documente Academic
Documente Profesional
Documente Cultură
Pasii descrisi pana in acest moment sunt valabili practic pentru toate proiectele si vor fi
parcursi la fiecare nou proiect astfel ca nu vor mai fi mentionati in continuare, considerandu-se
ca fiind impliciti.
2. Timer in modul bazic
Evident, se va asigura ceasul pentru sistem asa cum s-a prezentat in capitolul anterior
(RCC, HSE, HCLK).
Se trece acum la tab-ul Configuration, unde se observa ca a aparut modulul TIM4. Se
da click pe acesta si se stabilesc Parameter Settings, ca in figura 13.5
Figura 13.5. Setarea parametrilor TIM4
Atunci cand se lanseaza generarea primului cod de proiect apare o fereastra in care se
semnaleaza absenta pachetului firmware, ca in figura 13.7. Se accepta download si instalare a
acestuia, care se va face implicit in directoarea: C:/Users/Nume_User/STM32Cube/Repository/
Dupa generare, se deschide proiectul, care va avea toate optiunile, structura necesara si
codurile de initializare pentru toate blocurile implicate.
Se observa ca structura proiectului contine de fisierele necesare ordonate in mai multe
grupuri care initial nu sunt deschise (pentru a nu complica inutil aceasta zona).
In grupul Application exista subgrupul EWARM care contine fisierul
startup_stm32f407.s, iar in grupul Drivers->CMSIS se afla fisierul system_stm32f407.c,
ambele pregatite corespunzator.
Pentru inceput, din grupul Application -> User se da dublu click pe main.c. In fereastra
aparuta este afisat continutul acestui fisier. Se observa ca exista deja toate liniile de cod necesare
initializarii blocurilor implicate. Alte setari s-ar putea observa si in fisierele insotitoare din acest
grup: stm32f4xx_hal_msp.c, care contine parametrii si functiile MCU Specific Package si
stm32f4xx_it.c care contine prototipurile unor handlere de intreruperi, dintre care intotdeauna
este operant HAL_SYSTICK_IRQHandler(); Acesta asigura intreruperea de 1 ms generata
hardware de catre blocul SysTick folosita pentru intarzieri prin functia generala HAL_Delay().
In toate fisierele generate, inclusiv in main.c exista de asemenea mai multe portiuni
marcate cu comentarii /* User CODE BEGIN... */ respectiv /* User CODE END... */. Atentie,
toate liniile de cod care vor fi scrise de catre programator vor trebui sa fie plasate intre doi astfel
de delimitatori (ca in figura 13.9)! In caz contrar, daca se face din nou o generare de cod pentru
acelasi proiect, tot ceea ce este adaugat de programator in afara acestor portiuni va fi sters
automat!
La folosirea generatorului de cod Cube MX functiile API din SPL sunt inlocuite de
functii in mare masura omoloage HAL_API disponibile in fisierele sursa corespunzatoare
perifericelor respective stm32f4xx_hal_....c care se pot gasi in grupul Drivers
STM32F4xxHAL_DRIVER. Pentru aceasta se deschide cu dublu click fisierul respectiv si se
da click pe simbolul f() din dreapta sus. Din meniul drop-down care apare se cauta si se da click
pe functia necesara.
Dupa cum arata schema bloc a timer-ului, intrarea sa poate fi cuplata sau la generatorul
de ceas intern sau la un pin extern pe care se aplica impulsuri care vor fi numarate.Timerele
dispun de intrari de numarare TIx, iar unele dintre ele (TIM2, TIM3 si TIM4) dispun si de
intrare de trigerare externa, ETR.
Dupa cum s-a aratat, pinul PA0, la care este cuplat butonul User, poate fi folosit ca
intrare cu trigerare externa pentru timerul TIM2. Fiind un contact mecanic, acesta este afectat
de fenomenul de multicontact, dar poate ilustra functionalitatea numaratorului.
Exemplificam in continuare aceasta functie cu un program care sa numere pe led-uri
impulsurile aplicate pe PA0. Avand in vedere ca led-urile sunt plasate pe pinii PD12-PD15,
variabila in care se citeste continutul timerului trebuie deplasata cu 12 biti spre stanga pentru a
putea fi afisati si bitii inferiori.
Folosind generatorul de cod Cube MX, programatorul este scutit de procesul complex
de initializare astfel ca se poate concentra asupra algoritmului propriu-zis si poate dezvolta
aplicatia intr-un timp mult mai scurt.
Asa cum s-a aratat insa, in buclele repetitive si critice din punct de vedere al vitezei nu
se recomanda folosirea functiilor HAL si in general a limbajului de nivel inalt, ci a programarii
directe in registre, care necesita cunostinte hardware. Folosirea definitiilor standard ale
registrelor si bitilor asigura si in acest caz o buna portabilitate si usureaza mult efortul de
migrare la un nou tip de microcontroler. Chiar si functiile macro, care „infasoara” intr-un mod
simplu instructiuni care folosesc registrele, sunt destul de performante si suficient de abstracte
pentru a facilita o programare mai formala.
O modalitate importanta pentru marirea vitezei o constituie utilizarea intreruperilor.
Acestea reprezinta un mecanism prin care nucleul procesor este degrevat de sarcini de polling,
ocupandu-se preponderent de un program principal, intrerupt doar cand se produc evenimente.
Modul de lucru.
a) Pentru inceput se va crea un nou proiect in Cube MX, selectand placa STM32F4
Discovery.
b) Se va da Clear Pinout, se va activa RCC ->HSE->Quartz Resonator si se va
stabili frecventa nominala de 168 MHz ca in lucrarea precedenta.
c) Se stabilesc pinii PD12, PD13, PD14 si PD15 ca GPIO_Output pentru a se putea
folosi ledurile user.
d) Se activeaza TIM4 cu Clock Source -> Internal Clock.
e) In tabul de configuratie se va da click pe TIM4 si se va deschide fereastra de
configurare a acestuia. La Parameter Settings se va stabili Precaller =1000 si ARR=0xFFFF, iar
la NVIC Settings se va bifa TIM4 Global Interrupt.
f) Se va da generare cod, se va stabili o directoare si un nume de proiect (de
exemplu TIM4 intrerupere depasire), se va genera codul si se va deschide proiectul in IAR.
g) Se vor deschide in fereastra principala fisierele main.c, stm32f4xx_it.c,
stm32f4xx_hal_gpio.c si stm32f4xx_hal_tim.c.
h) Pentru pornirea timerului se va copia din stm32f4xx_hal_tim.c prototipul
functiei HAL_TIM_Base_Start_IT in main.c in interiorul blocului „USER CODE 2”. Se va
scrie parametrul actual sub forma &htim4.
i) In fisierul stm32f4xx_it.c se cauta ISR pentru TIM4, si anume
TIM4_IRQHandler. Aici, intre /* USER CODE BEGIN TIM4_IRQn 0 */ si /* USER CODE
END TIM4_IRQn 0 */, se introduce instructiunea de toggle pentru pinul PD15, cu prototipul
copiat adin fisierul stm32f4xx_hal_gpio.c: HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_15);
ca in figura 14.1, linia 103.
j) Se compileaza si se testeaza programul. De remarcat ca nucleul proceor este
aproape mereu liber, deoarece in bucla infinita nu s-a folosit nici o instructiune.
Timerele, ca si alte blocuri periferice, pot genera mai multe surse de intrerupere. Pe
langa intreruperea de update (reinitializare a continutului) folosita anterior, se mai pot genera
intreruperi de egalitate de comparatie si de intrerupere de captura pe fiecare dintre cele 4 canale
de comparatie/captura asociate.
Pentru a se realiza actiuni specifice pentru fiecare tip de intrerupere, avand in vedere ca
se genereaza o singura subrutina de intrerupere pentru un timer, in interiorul acesteia va trebui
sa se testeze fiecare dintre flagurile intreruperilor utilizate, pentru a se decide care este
evenimentul care a generat intreruperea de la momentul respectiv.
In continuare, se va realiza un program care sa genereze, pe langa intreruperea de
depasire, inca doua intreruperi de comparatie pentru doua dintre canalele de comparatie/captura
ale TIM4. Astfel, canalul CC1 se va incarca cu o anumita valoare apropiata de limita maxima,
de exemplu 0xE000, iar canalul CC2 cu o valoare apropiata de limita minima, de exemplu
0x1000. Se va numara direct si apoi invers (mod Center-aligned), egalitatea cu primul canal va
bascula ledul PD14, iar egalitatea cu al doilea canal va bascula ledul PD13. Se va mentine
bascularea de update pe PD15.
Modul de lucru.
Asa cum s-a aratat anterior, TIM4 poate functiona ca generator de unda PWM pe patru
canale cu posibilitatea ca iesirile respective sa fis cuplate ca functii alternate la pinii dotati cu
leduri user, PD12-PD15. Realizarea unui astfel de program este deosebit de simpla folosind
mediul Cube MX.
Modul de lucru.
f) Se poate verifica faptul ca atat TIM4 cat si GPIOD sunt initializati conform
proiectului si se poate compila si testa programul. Se va observa ca ledul clipeste cu un factor
de unda 0x2000/0xFFFF adica 1/8.
Factorul de umplere este dat de raportul intre durata pulsului si valoarea maxima, ARR
care au fost setate initial prin Cube MX la 0x2000 si respectiv 0xFFFF. Programul poate sa
necesite in anumite situatii modificarea acestui factor de umplere, de regula prin schimbarea
duratei pulsului.
Din pacate, prin functii HAL acest lucru nu se poate face direct ci printr-o structura de
tip sConfig, dupa cum se poate observa prin reverse engeneering. Ce de obicei, exista doua
solutii: prin programarea in registre cu instructiunea TIMx->CCR4 = PulseVal (pagina 629 din
Reference Manual) sau cu o functie exported macro __HAL_TIM_SET_COMPARE (pagina
913 din HAL Manual).
Ne propunem sa modificam programul anterior pentru ca factorul de umplere sa creasca
automat in bucla infinita pana la o valoare maxima si apoi sa reia procesul de la 0. Pentru a se
putea observa usor factorul de umplere pe led se va mari de 100 de ori frecventa de clipire,
astfel incat ochiul va integra luminozitatea, facand-o proportionala cu durata pulsului.
Modul de lucru:
a) Se modifica in Cube MX->Configuration->TIM4->Parameter Settings
-> Prescaller=10.
b) Se genereaza codul si se reintra in IAR
c) Se declara variabila PulseVal ca unsigned integer pe 16 biti pentru Pulse cu
instructiunea uint16_t PulseVal;
d) In bucla principala se introduc instructiunile de modificare a registrului de
comparare pentru TIM4_CH4 si de incrementare la fiecare parcurgere a buclei a acestei valori,
cu o mica intarziere pentru reducerea vitezei de schimbare, ca in figura 14.5.
Pentru achizitia de date prin esantionarea si converisa numerica a unui semnal analogic,
cea mai eficienta metoda de initializare a sistemului o constituie utilizarea mediului grafic Cube
MX. Rezultatele conversiei rezultatele se pot transmite serial prin USART (eventual se pot
stoca initial local in memorie prin DMA circular), I2C, SPI sau CAN-bus. Pentru obtinerea
vitezei maxime, se poate lucra in intreruperi atat la conversie cat si la transmisie.
Vom realiza in continuare un program care sa faca conversia anaolg numerica a unui
semnal aplicat pe intrarea 10 a ADC1 (pinul PC0) si sa transmita prin USART rezultatul cu 4
cifre zecimale si terminator (CR+LF). ADC va fi initial pornit in finalul secventa de initializare
si functiona in regim de conversie continua cu generare de intrerupere EOC, care va determina
citirea rezultatului intr-o variabila globala. La terminarea unei transmisii, subrutina de
intrerupere USART va converti in zecimal valoarea actuala a rezultatului conversiei si il
transmite serial. Activarea primei transmisii se va face la finalul secventei de initializare.
Acest mod de lucru presupune si conversie si transmisie continua. In cazul in conversia
este mai rapida, unele dintre rezultatele acesteia nu mai apuca sa fie transmise, de aceea este
recomandat ca viteza ADC sa se aleaga cat mai redusa (suficienta insa pentru aplicatia concreta
si pentru a nu depasi timpul de transmisie), deoarece aceasta imbunatateste precizia conversiei.
De exemplu, daca rata de baud este 115200 bps, se vor transmite aproximativ 10000
caractere pe secunda, deci sub 1700 de rezultate ale conversiei pe secunda. Prin urmare, se pot
folosi chiar si cele mai mici viteze pentru ADC integrat in microcontrolerul STM32F407VG.
Programul ar putea fi modificat astfel ca sfarsitul unei transmisii sa declanseze o noua
conversie analog-numerica, avand in vedere ca durata acesteia poate fi considerata neglijabila
(0.4 microsecunde, tipic de 1000 de ori mai mica decat a transmisiei). Totusi, trebuie tinut cont
si de durata conversiei din binar in zecimal care poate fi destul de mare daca se foloseste o
functie C universala (de tip „sprintf”).
Modul de lucru
Interfata seriala cu periferice (SPI) este o modalitate simpla si foarte raspandita pentru
cuplarea unor dispozitive care detin un astfel de modul, cu viteze si distante nu foarte mari.
Comunicatia este seriala sincrona, suporta modul master-slave sau multimaster si se realizeaza
pe 4 fire:
• Master In / Slave Out (MISO). Acest pin receptioneaza datele in modul master si
transmite date in modul Slave;
• Master Out Slave In (MOSI). Acest pin transmite date in modul Master si
receptioneaza datele in modul Slave;
• Serial Clock (SCK).Acest pin este iesire de ceas in modul Master si intrare de ceas in
modul Slave.
• Slave Select (NSS). Acest pin se poate folosi pentru a selecta un dispozitiv Slave. El
actioneaza ca un „chip select” pentru a permite dispozitivului Master sa comunice individual
cu un Slave cu evitarea coliziunilor pe liniile de date. El poate fi util de asemenea in sistemele
multimaster, putand fi configurat si ca intrare pentru a determina trecerea unui Master in starea
Slave.
Interconectarea între un dispozitiv master şi unul slave este prezentată în figura 16.1. Pinul
SCK este ieşire de ceas generata de dispzitivul master şi intrare de ceas pentru dispozitivul slave.
Scrierea în registrul de date al SPI din master porneşte generatorul de ceas al SPI şi apoi datele
scrise sunt scoase prin deplasare de pe pinul MOSI, întrând pe pinul MOSI al dispozitivului slave.
După transmiterea unui octet, ceasul SPI se opreşte şi setează flagul de sfârşit de transmisie TXE.
Dacă este activat bitul de validare a întreruperilor de la SPI (SPIE), apare o cerere de întrerupere.
Cele două registre de deplasare din master şi slave pot fi considerate ca un singur
registru de deplasare circular de 16 biţi distribuit, după cum se arată în figura 3.30. Când
datele sunt deplasate din registrul master către slave, datele sunt, de asemenea, deplasate
simultan în direcţie opusă. Aceasta înseamnă că într-un ciclu de deplasare, datele din
master şi slave sunt interschimbate.
In general exista unul sau mai multe registre tampon la transmisie şi la recepţie.
Totusi, nu se recomanda sa se scrie un octet nou în registrul de date al SPI înainte de a se
termina un ciclu complet de deplasare. La recepţie, se recomanda ca un octet care a fost
deja recepţionat trebuie citit din registrul de date al SPI înainte ca următorul octet să fie
complet recepţionat.
Moduri de transfer de date SPI la microcontrolere STM32F4
Semnalul de ceas SCK poate sa fie initial in 0 (CPOL=0) sau in 1 (CPOL=1), iar
esantionarea se poate face pe primul front (CPHA=0) sau pe al doilea front (CPHA=1).
Există deci patru combinaţii de fază şi polarităţi ale SCK în raport cu datele seriale,
determinate acesti biţi de control CPHA şi CPOL astfel ca formatul transferurilor de date
este ca în figura 16.2. şi 16.3.
In modul Master cesul este generat pe pinul SCK, datele sunt emise pe pinul MOSI
si sunt receptionate pe pinul MISO.
Initializarea modulului presupune urmatorii pasi:
• Se selecteaza viteza (rata de baud) prin bitii BR[2:0] din registrul de control CR1
• Se alege configuratia CPOL si CPHA daca se foloseste modul implicit (Motorola).
Acest pas nu este necesar daca se alege standardul Texas Instruments (TI).
• Se alege formatul 8 sau 16 biti prin bitul DFF
• Se alege formatul cadrului prin bitul LSBFIRST din CR1
• Pinul NSS va fi conectat la 1 logic in modul hardware pe toata durata transmisiei.
In modul software se seteaza bitii SSM si SSI in registrul CR1.
Accelerometrul LIS3DH
Figura 16.3. Diagramele de timp ale interfetei SPI din accelerometrul LIS3DH
Registrele cele mai importante ale circuitului sunt descrise in continuare.
Vom realiza in continuare un program care sa citeasca prin SPI datele de la doua axe ale
accelerometrului (X si Y), si sa se afiseze pe ledurile user depasirea unei anumite valori. Astfel, daca se
inclina placa intr-o directie, depasind pragul de 10/256 se va aprinde ledul de pe directia respectiva.
Modul de lucru
Se deschide generatorul de cod Cube MX, se cere un proiect nou si se alege placa STM32F4
Discovery. In tab-ul Pinout se da Clear Pinout si se alege in RCC->HSE->Crystal/Ceramic Resonator.
Se intra in tab-ul Clock Configuration, se bifeaza HSE si se cere frecventa 168 MHz.
Se revine la tab-ul Pinout si se activeaza SPI1 in modul Full-Duplex Master, observandu-se ca
sunt automat activati pinii PA5,PA6 si PA7 in modul SPI1_SCK, SPI1_MISO si respectiv SPI1_MOSI.
Pentru activarea circuitului LIS3D trebuie setat de asemenea, asa cum s-a aratat, pinul PE3 in modul
GPIO_Output.
Tot in modul GPIO_Output trebuie setati si pinii PD12, PD13, PD14 si PD15, pentru vizualizare
pe ledurile user.
Se trece in continuare in tab-ul Configuration si se da click pe SPI1. Setarile pot sa ramana in
starea implicita: format Motorola, Data Size 8 bit, First Bit MSB, Prescaler 2, CPOL Low, CPHA 1
Edge, CRC Disabled, NSS Software. Se poate da acum generare cod, specificand numele si calea.
Observatie: CPOL Low pare in contradictie cu diagramele de timp, dar aceasta este totusi
setarea corecta. Aceasta setare trebuie interpretata in sensul ca tranzitia intre biti are loc la trecerea spre
0 logic a liniei SCK, dupa cum se specifica in documentatie.
Se intra acum in mediul IAR si se deschide fisierul main.c.
Dupa initializarea microcontrolerului va trebui facuta initializarea accelerometrului, prin
scrierea unor anumiti octeti in unele registre al acestuia. Deoarece accelerometrul are nevoie de un timp
de reset hardware, este recomandabil sa se lase o intarziere de 1 secunda inainte de prima scriere in
registrele sale. In continuare trebuie scris cel putin registrul CR1 al accelerometrului pentru stabilirea
vitezei de citire a senzorilor si validarea directiilor necesare. Alegand o viteza medie, de 100 de citiri pe
secunda, si validand toate directiile, rezulta ca in acest registri, avand adresa 0x20 trebuie scris octetul
0x57.
Pentru cazul in care se doresc si alte scrieri pentru modificari ulterioare ale setarilor, se va realiza
o rutina de transmisie de la microcontroler, care va fi plasata in zona USER CODE 0 a fisierului main.c.
De asemenea, tot aici se va plasa o rutina de receptie de la accelerometru, care de regula va fi folosita in
mod repetat.
Dupa cum arata diagrama de timp din figura 16.3, la transmisie trebuie trimisa mai intai adresa,
cu primul bit in 0 (Write) si al doilea tot in 0 (Slave). In continuare se trimite un octet de date. Evident,
atunci cand este folosit SPI, pinul CS trebuie sa fie, conform documentatiei (pagina 24), in 0 logic, deci
la inceputul acestor rutine trebuie pus in 0 pinul PE3 iar la sfarsit acesta trebuie pus in 1.
Mai intai se va scrie o rutina de transmisie SPI_Tx avand doua argumente de tip intreg pe 8 biti
fara semn: adresa si datele.
Deschizand fisierul „stm32f4xx_hal_gpio.c” se alege pentru scrierea pinului PE3 functia
HAL_GPIO_WritePin. Aceasta se va copia la inceputul si sfarsitul rutinei, prima oara cu pozitionare in
RESET iar a doua oara in SET.
Din fisierul „stm32f4xx_hal_spi.c” se va copia prototipul functiei HAL_SPI_Transmit, si
conform prezentarii acesteia, se va lua ca prim parametru un pointer catre handlerul spi1, al doilea
parametru va fi un pointer catre octetul transmis, al treilea numarul de octeti transmisi (in cazul de fata
1) si ultimul va fi o valoare pentru time-out. Conform descrierii formelor de unda, mai intai se va
transmite octetul de adresa si apoi cel de date, astfel ca rutina SPI_Tx va fi ca in figura 16.4.
Pentru receptie se va scrie acum o subrutina care va returna un intreg pe 8 biti, avand ca
argument adresa registrului vizat din dispozitivul slave. Conform diagramei de timp a circuitului
LIS3DH, octetul de adresa, care va fi si aici transmis mai intai, va trebuie sa aiba primul bit in 1. De
aceea, se face un SAU intre octetul de adresa si 0x80 si apoi se transmite, tot cu functia
HAL_SPI_Transmit. Apoi se va apela functia HAL-API de receptie, al carei prototip se ia din fisierul
sursa „stm32f4xx_hal_spi.c”, si care va avea ca prim parametru un pointer catre handlerul spi1, iar al
doilea parametru va fi un pointer catre o variabila in care se va stoca octetul receptionat –rezultatul
returnat de subrutina. Prin urmare, aceasta va arata ca in figura 16.4
Interfata si protocolul Inter-Integrated Circuit – I2C, este folosita destul de des pentru
cuplarea unei mari varietati de dispozitive la distante reduse (pe acceasi placa) si cu viteze medii
(pana la 3,4Mbps): convertoare analog-numerice si numeric analogice integrate, memorii CD-
card, sisteme de afisaj, diversi senzori etc. Dezvoltata initial de Philips (in prezent NXP), ea a
fost adoptata de multi producatori de dispozitive destinate sa comunice prin putini pini, si sa
permita lucrul in retele multi-master. Se folosesc numai doua fire de semnal si un protocol
destul de sofisticat de arbitrare a drepturilor dispozitivelor, dar care s-a dovedit foarte fiabil in
ultimele doua decenii. Totusi, deoarece nu sunt prevazute masuri deosebite de imunitate la
zgomot, ea tinde sa fie inlocuita in aplicatii pentru medii puternic perturbate de alte interfete,
cum ar fi CAN-bus (Computer Array Network).
Microcontrolerele din seria STM32F4 sunt dotate cu trei interfete de uz general I2C care
pot functiona atat ca Master si ca Slave, cu adrese pe 7 sau 10 biti (in modul Slave pot fi doua
adrese), viteze de 100/400kHz si compatibilitate PMBus/SMBus.
Functionarea si caracteristicile complete sunt prezentate in manualul de referinta al
microcontrolerului, iar conectarea la pini este prezentata in Datasheet (DocID022152, pag. 61-
69).
Magistrala este construită din două fire, SDA şi SCL, fiecare conectat la Vcc prin câte
o rezistenţă. Dispozitivele se cuplează la acestea cu pinii omologi SDA şi SCL ca în figura 17.1,
masa lor fiind comună.
Fiecare dispozitiv are ieşirile către aceşti pini de tip drenă în gol. Aceasta conduce la o
schema de tip SI-cablat, care permite ca starea unei linii să fie în 1 logic numai dacă toate
dispozitivele au ieşirea respectivă în 1 logic. Dacă cel puţin un dispozitiv are ieşirea în 0 logic,
atunci toată linia va fi în starea 0 logic, fiind trasă catre masă de dispozitivul care absoarbe
curent prin rezistenta cuplata la Vcc. După cum rezulta din descrierea protocolului I2C, o astfel
de structură permite rezolvarea simplă a arbitrarii în sistemele cu mai multe dispozitive master.
Informaţia se transferă prin linia SDA, în pachete formate din cuvinte de 8 biţi.
Sincronizarea şi semnalizările suplimentare se efectuază prin intermediul liniei SCL. Fiecare
bit util de informaţie de pe lina SDA trebuie să fie stabil în perioada în care linia SCL este în 1
logic, ca în figura 17.2, pentru a se asigura o citire corectă a acesteia.
Dacă apare o tranziţie din 1 în 0 logic pe linia SDA in timp ce SCL este în 1
logic, aceasta semnalează începutul transmisiei unui bloc, situaţie care va fi numită în
continuare condiţie de start. Dacă apare o tranziţie din 0 în 1 logic pe linia SDA în timp ce
SCL este in 1 logic, aceasta semnalează sfârşitul transmisiei unui bloc, situaţie care va fi numită
în continuare condiţie de stop. După o condiţie de start începe transmisia unui pachet, iar dupa
transmiterea întregului pachet poate să apară o condiţie de stop sau o altă condiţie de start. Acest
ultim caz, cănd o condiţie de start urmează unei condiţii de start anterioare fără ca între ele să
fi apărut o condiţie de stop, va fi denumit în continuare condiţie de start repetat. Ea apare
atunci când se doreşte transmiterea unui nou pachet fără a se ceda controlul magistarelei. De
exemplu, atunci când se citeşte dintr-o memorie prin intermediul magistralei pe două fire, se va
transmite mai intâi adresa locaţiei de la care se va face citirea şi apoi se va receptiona continutul
locaţiei respective. Dacă între timp ar intra un alt dispozitiv transmiţător pe magistrală, care se
adresează aceleiaşi memorii dar la altă locaţie, recepţia de către primul dispozitiv s-ar face
ulterior de la noua locaţie a memoriei. Acesta impune ca dispozitivul iniţial să nu cedeze
magistrala între faza de transmisie şi cea de recepţie, deci în loc de Stop după transmiterea
adresei locaţiei, va da un Start repetat pentru a continua imediat cu recepţia.
Astfel de situaţii sunt prezentate în figura 17.3, exemplificate pentru transmisii de la
master cu adresare pe 7 si 10 biti.
Se va realiza o comunicatie prin interfete I2C intre doua placi STM32F4 Discovery
dintre care una este master iar cealalta este slave. Pe fiecare placa se va putea modifica valoarea
unei variabile, unit8_t c, prin incrementare la apasarea butonului user. Placa master transmite
periodic valoarea acestei variabile catre slave, care o afiseaza pe user led si transmite catre
master propria valoare a variabilei. Masterul afiseaza de asemenea aceasta valoare pe ledurile
sale user si reia procesul. In acest fel, o modificare pe placa master este afisata de slave si
viceversa.
Deoarece placile au functii diferite, va trebui ca fiecare sa aiba un program propriu, deci
se vor realiza doua proiecte.
Folsind Cube MX, se realizeaza mai intai proiectul master. Se selecteaza tipul de placa
si se da Clear Pinout, dupa care se activeaza in RCC HSE->Crystal/Ceramic Resonator. Se
stabileste ceasul HSE de 168 MHz, dupa care se activeaza pinii PD12, PD13, PD14 si PD15 ca
GPIO_Output.
Se da click pe PB6 si se alege aici I2C1_SCL, apoi PB7 cu selectie I2C1_SDA. Se
activeaza apoi in fereastra din stanga I2C1->I2C.
Pentru modificarea variabilei prin butonul user se va lucra cu intreruperi externe, deci
se alege pentru PA0 functia GPIO_EXTI0. Tab-ul Pinout va arata acum ca in figura 17.5.
Se intra acum in tab-ul Configuration si se selecteaza I2C. Aici va exista singura
diferenta intre proiectele celor doua placi, si anume adresa proprie. Dupa cum se observa in
explicatia din partea de jos a figurii 17.6, adresa declarata aici va fi deplasata cu un bit spre
stanga in program, deci inmultita cu 2. Se poate alege aici pentru master de exemplu valoarea
1 iar pentru slave se va alege 2. Aceasta inseamna ca placa slave va avea in program adresa 4,
in timp ce placa master va avea adresa 2 (desi aceasta nu va fi necesara decat in cazul in care
intervine alt master pe magistrala).
Pentru validarea si arbitrarea prioritatilor intreruperilor, se intra in NVIC si se bifeaza
EXTI line 0, cu Sub Priority 1.
Se genereaza acum proiectul cu denumirea I2C02ma in IAR.
Se inchide priectul Cube MX si se deschide unul nou repetandu-se pasii anteriori, cu
singura deosebire ca se va alege adresa proprie 2, care se va transforma in program, cum s-a
aratat adresa 4. Se va genera acum in IAR codul proiectului slave cu denumirea I2C04sl.
Figura 17.6. Configuratia I2C pentru proiectul I2C02ma. Pentru I2C04sl la Primare
slave address se va scrie 2.
In fisierul main.c pentru master, acesta va transmite mai intai un octet catre slave si apoi
va astepta ca raspuns de la acesta un octet pe care il va afisa pe leduri.
Pentru modificarea valorii variabilei transmise se va folosi, asa cum s-a specificat,
intreruperea externa pe intrarea PA0 (butonul user). Pentru aceasta se va intra in fisierul
stm32f4xx_it.c si se va insera, in zona USER CODE EXTI0_IRQn 0, secventa uzuala folosita
in capitolul 4: asteptare pentru stabilizarea contactului, o citire a butonului pentru reconfirmare
si apoi incrementarea variabilei proprii, a. Acest fisier va arata deci ca in figura 17.8, unde se
va declara bineinteles variabila a ca extern uint8_t, pentru a fi vizibila si in fisierul main.c.
Pentru placa slave, acest fisier va fi identic, iar fisierul main va avea forma din figura
17.9, in care ordinea este inversa fata de master. Astfel, mai intai se receptioneaza un octet de
la master folosind instructiunea HAL_I2C_Slave_Receive al carei prototip se copiaza din
fisierul sursa stm32f4xx_hal_i2c.c. Dupa aceea se face transmisia variabilei proprii prin
instructiunea HAL_I2C_Slave_Transmit, se afiseaza variabila receptionata deplasata spre
stanga cu 12 biti si se insereaza o intarziere. Desigur, se vor declara corespunzator si cele doua
variabile implicate.
Functiile API, desi comode si destul de sugestive, iau in consideratie foarte multe
posibilitati ale contextului hardware/software, pe care le testeaza in timp real, tinzand sa
micsoreze viteza de executie. In plus, ele sunt susceptibile de modificari, fiind in continua
evolutie, ceea ce micsoreaza portabilitatea si maintenanta programelor. De aceea, ele se
recomanda doar in portiunile initiale ale curbei de invatare, astfel ca in aplicatii operationale
profesionistii prefera programarea buclelor repetitive direct prin registre.
Ca exemplu, vom reproduce programul precedent folosind varianta cu registre in bucla
infinita. Desi aparent vor fi necesare mai multe instructiuni, se va putea observa ca la incarcarea
in flash aceasta bucla va adauga circa 120 de octeti la restul programului in varianta registre, in
timp ce varianta HAL-API va adauga peste 1300 de octeti, ceea ce indica o viteza de cel putin
10 ori mai mica. In plus, scrierea in registre ramane neschimbata si in conditiile unor frecvente
schimbari ale setului de functii API, a caror portabilitate este deci doar aparenta. Chiar si din
punct de vedere al dificultatii programarii varianta cu registre poate fi avantajoasa, tinand cont
ca documentatia pentru functii API are deja peste 2000 de pagini, destul de interdependente.
Din documentatia microcontrolerelor STM32F4xx se poate afla modul de functionare
pentru cele patru situatii care ne intereseaza: transmisie master, receptie master, receptie slave
si transmisie slave. In cele ce urmeaza, vom considera ca generatorul de cod Cube MX a facut
initializarea microcontrolerului Master si a celui Slave cu parametrii specificati in paragraful
17.3, astfel ca vom rescrie doar instructiunile din bucla infinita.
a) Transmisia master se face conform diagramei din figura 17.3.
In mod normal interfata este in starea Slave. Pentru trecerea in starea Master, trebuie
generata o conditie de Start. Aceasta se face prin scrierea in 1 a bitului START din registrul
I2C_CR1 (pag . 838 reference manual), folosind instructiunea:
Ca urmare interfata va seta prin hardware bitul SB (Start Bit) din registrul de stare
I2C_SR1 (pag. 843 reference manual). Programul va trebui sa astepte acest eveniment prin
testarea in bucla a acestui bit, cu o instructiune de tipul:
Conform documentatiei, pentru a se continua trebuie sters acest bit, printr aceasta citire
din I2C_SR1 urmate de o citire a SR2. Vom folosi in acest scop o variabila temporara, t, declara
in prealabil ca uint16_t:
Deoarece bitul 0 al adresei a fost 0, in continuare se face o transmisie, cea de date, prin
scrierea octetului respectiv in DR:
Daca este de transmis un singur octet de date, se poate genera acum o conditie de Stop
prrin scrierea in 1 a bitului STOP din I2C_CR1, astfel incat interfata revine in starea se Slave:
(1)
Ca si la transmisie, primul pas este trecerea in starea Master cu generarea unei conditii
de Start, aceasta se face prin aceeasi secventa: scriere bit START in I2C_CR1 si steptarea
confirmarii acestuia in I2C_SR1, prin instructiunile:
Figura 17.10. Programul pentru placa master, zona buclei infinite, varianta registre
Dupa cum rezulta din documentatie, pentru ca dispozitivul master sa fie anunt ca oricare
slave adresat sa semnalizeze recunoasterea adresei (Acknowledge), la fiecare slave trebuie setat
bitul ACK din CR1. Aceasta nu se realizeaza de catre Cube MX in secventa de initializare si
trebuie scris inainte de intrarea in bucla infinita, printr-o instructiune de tipul:
I2C1->CR1 |= I2C_CR1_ACK;//Valideaza semnalizarea recunoasterii
while (!(I2C1->SR1&I2C_SR1_ADDR));
In caz contrar, va avea loc o receptie a slave de la master. Se va sta deci intr-o bucla de
interogare a sfarsitului receptiei, semnalizat de bitul RXNE din SR1, dupa care datele se pot
citi din DR si utiliza:
else
{
while(!(I2C1->SR1&I2C_SR1_RXNE));
r=I2C1->DR;//Receptie slave
}
GPIOD->ODR=(r<<12);// Utilizare date
Portiunea buclei while din programul Slave va arata deci ca in figura 17.12.
Figura 17.12. Programul pentru placa slave, zona buclei infinite, varianta registre