Sunteți pe pagina 1din 7

Laborator 3.

Intrari digitale pe pinii microcontrolerului STM32F407VG


1. Identificarea intrarii butonului User si urmarirea actiunii sale in spatiul de
memorie

In continuare se va lucra cu butonul User si cele 4 LED-uri ale placii STM32F407


Discovery, cu care se pot executa diverse programe exemplificatoare.
Din fisierul de documentatie a placii, “STM32F407 Discovery Schematics.pdf”, se
arata (pagina 16) ca LED-urile user se afla pe pinii PD12, PD13, PD14 si PD15 (care fac parte
din portul GPIOD), iar butonul User se afla pe PA0 al portului GPIOA.
Prin urmare, pentru a se primi semnal digital de la acest buton, trebuie activat ceasul
pentru portul GPIOA si trebuie stabilit sensul intrare pentru pinul PA0.
Din schema bloc a microcontrolerului (pagina 12) rezulta ca si acest port este cuplat la
bus-ul AHB1.
In continuare se cauta in documentatia microcontrolerului “stm32f407vg ref
manual.pdf” registrele implicate. Astfel, in capitolul “Reset and clock control…” se gaseste la
Registers ->”RCC AHB1 peripheral clock enable register” ca trebuie setat in 1 bitul 0
pentru a genera clock pentru GPIOA (paginile 238-239) si de asemena setat in 1 bitul 3 pentru
a genera clock pentru GPIOD.
Prin urmare, la adresa corespunzatoare acestui registru 0x40023830 trebuie setati bitii
0 si 3, adica octetul 0x09.
De asemenea, pentru a putea citi starea logica a pinului PA0, trebuie ca modul (sensul)
acestuia sa fie de intrare. Adresa de baza a GPIOA se gaseste in 2.3 Memory map (pagina 65)
ca fiind 0x4002.0000
Pentru stabilirea unui pin ca intrare se cauta in registrele asociate GPIO pe cel care
comanda modul de lucru al pinilor. La capitolul “8 General-purpose I/Os”-> “8.4.1 GPIO port
mode register” la pagina 278 se observa ca adresa de offset este 0x00 iar fiecare pin are
asociati 2 biti din cei 32 ai registrului (care ocupa deci 4 octeti in spatiul de memorie). Tot aici
se poate observa ca pentru intrare cei doi biti trebuie sa aiba combinatia 00, si aceasta este de
altfel chiar starea implicita la reset, deci nu este nevoie deocamdata sa se scrie acesti biti.
Daca insa pe parcursul programului ar fi nevoie de alta stare, pentru revenirea la sensul de
intrare ar trebui sa se introduca explicit aceasta combinatie.
De asemenea, daca nu se cunoaste configuratia exacta a sursei semnalului de intrare si
eventual firele de pe intrare sunt lungi, ar fi necesar sa se activeze rezistenta interna de tragere
la Vdd, Pull-Up. Aceasta se realizeaza din registrul asociat, GPIO port pull-up/pull down
register de la pagina 280, GPIOA_PUPD. Se observa ca fiecare pin are alocati 2 biti, iar
activarea rezistentei pull-up se face scriind combinatia 01. In particular, la placa folosita, acest
lucru nu este necesar pentru butonul User, deoarece el este prevazut cu rezistenta externa pull-
down, iar prin apasare cupleaza intrarea direct la Vdd (pagina 40 din STM32f407 Discovery
schematics.pdf). Nu trebuie insa uitat sa se faca acest lucru in cazul folosirii unor alti pini de
intrare.
In fine, pentru citirea starii logice a acestei intrari se va apela la registrul GPIO port
input data register, GPIOA_IDR, care este descris la pagina 280 si are adresa de offset 0x10,
deci adresa absoluta 0x4002.0010.
Pentru testarea acestor operatii se va crea un nou program, fara nici o instructiune
dupa procedeul deja stabilit:
a. Se lanseaza IAR
b. Se da Project->Create new project
c. Se alege C->main.
d. Se alege directoarea de lucru si un nume pentru proiect
e. Se da Project->Options
f. In General se alege tipul de procesor (ST->STM32F407->STM32F407VG)
g. In Debugger se selecteaza in Setup->Driver-> ST-Link si la Download Verify+Use
Flash loader
h. Tot in Debugger, la ST-LINK selecteaza SWD.
i. Se da compilare (F7)
j. Se stabileste un nume pentru fisierul obiect
k. Pentru incarcarea programului in memoria microcontrolerului se da
Project->Download and Debug (CTRL+D) sau se da click pe penultinul buton din
bara de sus.
O cale alternativa (recomandata in acest caz) este sa se copieze subdirectoarea de la
laboratorul precedent in directoarea principala si sa i se dea alt nume. Dupa aceea se deschide
acest proiect si se sterg toate instructiunile si directivele, ramanand numai programul main:

void main()
{
}

Dupa comanda Debug, se deschide o fereastra View->Memory si se introduce adresa


RCC_AHB1ENR, adica 0x40023830. La aceasta adresa se introduce, cum s-a aratat, octetul
09, ceea ce deschide ceasul pentru GPIOA si GPIOD. Se introduce apoi adresa 0x4002.0010
si se deschide o fereastra View->Register din care se selecteaza GPIOA.
Ruland acum programul pas cu pas si actionand asupra butonului User (albastru) se
poate observa schimbarea starii octetului din memorie de la adresa 0x4002.0010 si in acelasi
timp a registrului IDR al GPIOA.
De mentionat ca anumiti biti ai portului GPIOA sunt utilizati pe placa STM32f407
Discovery si pentru comunicatia seriala prin mini-USB, deci trebuie sa separam doar bitul 0.
Pentru aceasta vom introduce in program o variabila int b, care va fi citita intr-o bucla
infinita de la adresa GPIOA_IDR, si vom comanda si deblocarea ceasului pentru porturile
dorite. Programul va arata acum astfel:

int b;
void main()
{
*(int *)0x40023830=0x09;
while(1)
{
b=*(int *)0x40020010;
}
}
Putem urmari evolutia variabilei b care arata starea registrului de date de intrare, dupa
lansarea Debug, cu o comanda View->Live Watch in care se introduce expresia b. Prin
rularea pas cu pas si actionarea asupra butonului albastru se va observa si actiunea asupra
variabilei b.

2. Varianta de program cu dereferentiere

Asa cum s-a mai aratat, pentru evitarea cautarii si scrierii adreselor diverselor registre
implicate intr-un program, se poate folosi headerul "stm32f4xx.h" furnizat de
producatorul ST Microelectronics, precum si alte headere si programe sursa din directoarea
STM32F4-Discovery_FW_Vx.x.
In acest caz, programele sursa respective (cu extensia .c) se vor adauga in fereastra de
proiect iar in Project->Optoins->C/C++ compiler->Preprocessor se adauga caile necesare, ca
in exemplul din laboratorul precedent
Acestea exista deja daca s-a creat programul prin copierea subdirectoarei respective,
cu un alt nume si sunt:

C:\STM32F4-Discovery_FW_V1.1.0\Libraries\CMSIS\ST\STM32F4xx\Include
C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\inc
C:\STM32F4-Discovery_FW_V1.1.0\Libraries\CMSIS\Include

In aceste fisiere, blocurile microcontrolerului sunt definite cu directive „typedef struct”,


deci pot fi selectati membri ai obiectului respectiv folosind operatorul „dereference” -> .
Intr-adevar, scriind in main RCC-> va apare o fereastra cu membrii obiectului RCC si se
selecteaza AHB1ENR (daca aceasta nu apare, se poate incerca salvarea programului si
reincarcarea lui). La fel se procedeaza si cu GPIOD, iar programul devine:

#include "stm32f4xx.h"
int b;
void main()
{
RCC->AHB1ENR |=0x09;
while(1)
{
b=0x01&GPIOA->IDR;
}
}
Se observa ca pentru a elimina efectul bitilor portului A implicate in alte procese (ST-
Link), s-a facut un AND al registrului GPIOA->IDR cu cuvantul 0x01.
Prin rularea pas cu pas, se poate observa ca variabila b ia acum doar valorile 0 si 1.
De asemenea, variabila poate fi urmarita si dupa comanda Go, de rulare continua a
programului, daca este introdusa intr-o fereastra View-> Live Watch.
Aceasta variabila poate fi folosita de exemplu pentru comanda unui LED, cum ar fi cel
albastru, ceea ce necesita introducerea instructiunii GPIOD->MODER|=(1<<2*15);. O
varianta de implementare ar fi urmatoarea:
GPIOD->ODR=(b<<15);
Introducand aceste doua instructiuni in locurile potrivite se va observa prin rulare ca
LED-ul albastru copiaza starea butonului albastru. Atentie insa, ultima instructiune modifica
toti bitii portului GPIOD. Daca acest lucru nu trebuie sa se intample, este preferabila o
constructie ca in programul:

#include "stm32f4xx.h"
int b;
void main()
{
RCC->AHB1ENR |=0x09;
GPIOD->MODER|=1<<2*15;
GPIOD->OSPEEDR=(unsigned)(2<<2*15);
GPIOD->OTYPER=0<<2*15;
GPIOD->PUPDR=1<<2*15;

while(1)
{
b=0x01&GPIOA->IDR;
if (b==1)
GPIOD->ODR|=(1<<15);
else GPIOD->ODR&=(0<<15);
}
}

Se observa ca pentru stabilirea frecventei de clock pe GPIOD, care ar necesita scrierea


unui 1 in pozitia 31 conform documentatiei la pagina 279, astfel ca ar da un numar negativ in
complement fata de 2 (se poate verifica pe Calculator in Decimal, Dword), s-a facut un type
cast ca un intreg fara semn.
Se pot elimina aproape complet constantele numerice si operatiile de plasare a lor prin
operatori << folosind definitiile din fisierul inclus deja, "stm32f4xx.h".
Pentru aceasta se expandeaza in fereasta de proiect linia Output, si din lista care apare
se da dublu click pe stm32f4xx.h. Acesta apare in fereastra principala intr-un nou tab
(eventual se poate cere din Window un New Vertical Editor Window in care sa se dragheze
acest tab). Cu acest tab activ se cauta structurile care ne intereseaza, introducand denumirile
in fereastra de cautare din bara de sus, imediat sub Help.
Pentru inceput se cauta AHB1ENR si se gaseste la linia 5011 ca este definit
RCC_AHB1ENR_GPIOAEN iar la linia 5014 RCC_AHB1ENR_GPIODEN. Aceasta permite
deblocarea clock-ului pentru porturile A si D cu instructiunea:

RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN|RCC_AHB1ENR_GPIOAEN;

Cautand acum MODER, se gaseste la linia 4247 inceputul definitiilor GPIO_MODER


iar la linia 4309 definitia GPIO_MODER_MODER15_0 cu valoarea 0x40000000, care
conform paginii 278 din manualul microcontrolerului inseamna mod de iesire.

GPIOD->MODER|= GPIO_MODER_MODER15_0;

Procedand similar, si cu celelalte linii, se poate inlocui programul anterior cu forma:

#include "stm32f4xx.h"
int b;
int main()
{
//Validare clock GPIOA si GPIOD
RCC->AHB1ENR|=RCC_AHB1ENR_GPIOAEN|RCC_AHB1ENR_GPIODEN;
//Parametrii PD15 (PA0 este corect la Reset)
GPIOD->MODER|=GPIO_MODER_MODER15_0;//Iesire
GPIOD->OSPEEDR|=GPIO_OSPEEDER_OSPEEDR15;//High speed
GPIOD->OTYPER|=0<<15;
GPIOD->PUPDR|=GPIO_PUPDR_PUPDR15_0;//Pull-up

while(1)
{
b=GPIOA->IDR&GPIO_IDR_IDR_0;
GPIOD->ODR=~b<<15;//Stinge la apasare
}
}
Se observa ca nu s-au facut modificari in linia OTYPER deoarece nu exista o definitie
explicita pentru PushPull, aceasta fiind implicita la reset (ceea ce permite de fapt omiterea in
intregime a acestei linii in program). De asemenea, registrele sunt modificate prin operatori
compusi cu SAU |= pentru a nu se influenta decat bitii vizati, lasand neschimbati ceilalti biti.
Pentru extragerea lui PA0, s-a folosit definitia pinului respectiv cu care se face un SI. De
asemenea, pentru exemplificare, s-a facut inversarea lui b inainte de afisare, dar este de
remarcat ca modul in care se face scrierea in ODR aduce la 0 toti bitii acestuia cu exceptia
eventual a bitului 15.
Utilizarea extinsa a acestor definitii poate parea mai simpla sau nu decat calculul
constantelor hexazecimale, in functie de stilul programatorului, acesta urmand sa aleaga.

2.1. Program de citire buton si aprindere LED API

Ca urmare a celor aratate mai sus, programul care citeste starea butonului albastru
(PA0) si il copiaza in LED-ul albastru (PD15) realizat in mare masura cu functii API va arata
astfel:

#include "stm32f4xx.h"

int dt=1500000;
int b;
void wait(uint32_t nr)
{
uint32_t index=0;
while(index++<nr);
}
int main()
{
//Validare ceas periferice (se pot specifica mai multe cu SAU)
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD|RCC_AHB1Periph_GPIOA,ENABLE);
//Configurare PD15 ca iesire Push-Pull, cu rezistenta la Vdd
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin=(GPIO_Pin_15);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;
GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_UP;
GPIO_Init(GPIOD, &GPIO_InitStruct);

//Configurare PA0 ca intrare, fara rezistente


GPIO_InitStruct.GPIO_Pin=(GPIO_Pin_0);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN;
GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStruct);

//Butonul albastru (PA0) este copiat in PD15;


while(1)
{
b=0x01&GPIOA->IDR;
GPIOD->ODR|=(b<<15);
GPIOD->ODR&=(b<<15);
}
}
Structura proiectului si caile din Preprocessor sunt urmatoarele prezentate in figura de mai jos.
Se observa ca pe langa fisierele sursa adaugate anterior (lucrarea 2), s-a mai adaugat fisierul sursa
“stm32f4xx_exti.c”. De asemenea nu trebuie uitat simbolul USE_STDPERIPH_DRIVER, ca in lucrarea
API anterioara.

In program s-a folosit o cale mixta de programare, atat cu functii API (initializari) cat si cu
registre (bucla while).
ATENTIE ! Daca se scrie ceva gresit in portul A, este posibil sa se blocheze ST-Link,
care utilizeaza acest port, astfel ca nu se mai poate scrie nimic in memoria Flash. In acest caz
se procedeaza in felul urmator:
1. Se deconecteaza cablul mini USB;
2. Se conecteaza un fir intre pinul BOOT0 (la mijlocul conectorului de pe dreapta) si
pinul 3V (al treilea de sus pe acelasi conector);
3. Se reconecteaza cablul mini USB;
4. Se deconecteaza firul dintre BOOT0 si 3V.
Unele dintre programele si headerele necesare ar putea fi copiate in directoarea de
lucru iar caile catre locatiile lor originale se pot inlocui in Preprocessor cu calea:
$PROJ_DIR$
Bineinteles, caile catre programele si headerele respective vor fi mai intai sterse din
proiect si apoi readaugate din directoarea curenta.
Desi procesul de constructie a structurilor API si a cailor incluse poate parea cronofag,
in realitate acest lucru trebuie facut pentru un periferic o singura data. La urmatoarele
programe se pot copia structurile de initializare si caile din preprocesor, sau se poate copia cu
totul directoarea unui proiect si apoi se poate modifica in aceasta copie programul pentru
obtinerea unuia nou. Avantajul utilizarii API este ca denumirile si parametrii functiilor sunt
sugestive, usor de citit, si pot fi selectate din meniul drop-down f(), cu posibitatea vizualizarii
imediate a prototipului.

S-ar putea să vă placă și