Mikroprocesorski sistemi/K2 2021
Prvi kolokvijum 2021. godine nema postavku dostupnu sa stranice predmeta. Nije mnogo poznato o ovom roku, osim zadatka navedenog ispod. Isti zadatak pojavio se i na drugom kolokvijumu 2022. godine.
Zadatak
Postavka
Koristeći alate arm-none-eabi-*, build-tools i Eclipse CDT, Proteus simulator i CubeMX alat potrebno je ispuniti stavke koje se nalaze u nastavku.
- [6 poena] Napraviti CubeMX i Proteus projekte za mikrokontroler STM32F103R6. Dodati dva dugmeta i povezati ih na pinove
PB0
iPB2
(pogledati referentnu šemu) tako da se mogu koristiti za generisanje prekida. Izvršiti neophodnu konfiguraciju tako da se generišu zahtevi za obradu prekida ukoliko se na ulaznom signalu ovih pinova prepozna uzlazna ivica. Dodati dve LED (light emitting diode) i povezati ih na pinovePB1
iPB3
(pogledati referentnu šemu). Izvršiti neophodnu konfiguraciju i zatim u okviru prekidne rutine za dugme koje je vezano na pinPB0
napisati programski kod koji vrši promenu stanja na pinuPB1
, a u okviru prekidne rutine za dugme koje je vezano na pinPB2
napisati programski kod koji vrši promenu stanju[sic] na pinuPB3
(pogledati referentni snimak).[1] - [6 poena] Dodati sedmosegmentni ekran širine četiri cifara, povezati ga na odgovarajuće pinove prema referentnoj šemi, izvršiti neophodnu konfiguraciju i napisati programski kod za prikaz cifara 1234 na ekranu. Osvežavanje ekrana vršiti u update event prekidnoj rutini tajmera TIM1. Ukoliko se stavka (4) uradi u potpunosti ispravno prikaz na ekranu uskladiti sa zahtevima iz stavke (4) a poeni za stavku (2) biće priznati u celosti.
- [6 poena] Dodati elektromotor i osciloskop u skladu sa referentnom šemom. Potrebno je proširiti postojeće prekidne rutine iz stavke (1) na način da se kontroliše brzina rada elektromotra.[sic] Uloga dugmeta povezanog na pin
PB0
jeste da poveća brzinu, a uloga dugmeta povezanog na pinPB2
jeste da smanji brzinu (pogledati referentni snimak).[1] Za generisanje signala kojim se kontroliše elektromotor koristiti tajmer TIM1 i njegov kanal CH1. Pojedinačni inkrement odnosno dekrement za koji se menja brzina predstavlja 10% od maksimalne brzine elektromotora. - [6 poena] Potrebno je dodati generator signala prema referentnoj šemi. Implementirati izračunavanje trenutne frekvencije signala sa generatora i prikaz izračunate frekvencije na sedmosegmentnom ekranu iz stavke (2) umesto cifara 1234 (pogledati referentni snimak).[1] Frekvencija prikazana na ekranu treba da bude izražena u Hz. Za implementaciju ove stavke koristiti tajmer TIM3 a moguće je koristiti njegove kanale CH1 i CH2. Ukoliko se stavka (4) uradi u potpunosti ispravno prikaz na ekranu uskladiti sa zahtevima iz stavke (4) a poeni za stavku (2) biće priznati u celosti.
Rešenje
Celo rešenje može se preuzeti odavde.
Svi relevantni izmenjeni fajlovi sa kodom nalaze se u cubemx/code/Core/Src
direktorijumu.
Rešenje ispod se može u manjim detaljima razlikovati od rešenja u arhivi iznad, kako je kod ispod dodatno pojednostavljen radi pojašnjenja nekih funckionalnosti.
Opšte napomene
- Potrebno je uraditi Refresh projekta u Eclipse okruženju nakon ponovnog generisanja koda u CubeMX-u kako bi sve radilo ispravno.
- Za pinove se ostavlja podrazumevana opcija no pull-up, no pull-down za razliku od vežbi.
- U slučaju problema rada sedmosegmentog displeja dodati
GPIOC->ODR &= ~0xFFF
pre postavljanja vrednosti na displej.
CubeMX opšte
Prvo je pod Project Manager u Project podešena putanja projekta i Makefile za Toolchain, a zatim u Code Generator štiklirana opcija za generisanje odvojenih izvornih fajlova i zaglavlja za svaku periferiju. Uraditi inicijalno generisanje koda.
Eclipse opšte
- Otvoriti Eclipse i postaviti radno okruženje na folder CubeMX unutar foldera projekta.
- Window->Preferences->MCU:
- Workspace Arm -> postaviti pitanju do
interna_putanja_na_vašem_računaru\GNU MCU\arm-none-eabi-gcc-10.2.1-1.1\bin
. - Workspace BuildTools -> postaviti pitanju do
interna_putanja_na_vašem_računaru\GNU MCU\build-tools-4.2.1-2\bin
.
- Workspace Arm -> postaviti pitanju do
- Putem opcije import uvesti generisan kod iz foldera Code, kao postojeći C/C++ projekat koji koristi makefile. Obavezno označiti Arm Cross GCC.
- Nakon učitavanja, desni klik na učitani projekat, properties->C/C++ Build->Setting, a zatim Apply and close.
- Opet desni klik na učitani projekat, properties->C/C++ General->Paths and symbols, i dodati C simbole STM32F103x6 i USE_HAL_DRIVER.
Makefile opšte
U Makefile je pomeren tim.c
na dno C_SOURCES
promenljive, sledeći blok:
ifeq ($(DEBUG), 1)
CFLAGS += -g -gdwarf-2
endif
# Generate dependency information
CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)"
je promenjen na:
ifeq ($(DEBUG), 1)
CFLAGS += -g -gdwarf-2 -fdebug-prefix-map==../
endif
# Generate dependency information
CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)"
CFLAGS += -mlong-calls
CFLAGS += -Wall -Wextra
i startup_stm32f103x6.s
je promenjeno na Core/Src/startup_stm32f103x6.s
unutar ASM_SOURCES
.
Stavka 1
CubeMX
- Pinove
PB0
iPB2
podesiti kaoGPIO_EXTI
. - Pinove
PB1
iPB3
podesiti kaoGPIO_Output
. - U
NVIC
omogućiti prekide poEXTI0
iEXTI2
. - Generisati kod.
Eclipse
stm32f1xx_it.c
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
switch (GPIO_Pin) {
case GPIO_PIN_0:
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1);
break;
case GPIO_PIN_2:
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
break;
}
}
Proteus
- Dodati mikotrokontroler i postaviti signale prema referentnoj šemi.
- Dioda je
LED-YELLOW
iz bibliotekeACTIVE
. Diode je potrebno postaviti da budu digitalne. - Za dugme se koristi
BUTTON
iz bibliotekeACTIVE
. Switching time postaviti na 1n. - Postaviti otpornike u skladu sa referentnom šemom.
- Postaviti .elf ili .hex fajl u mikrokontroleru i testirati ispravan rad.
Stavka 2
CubeMX
- Tajmer
TIM1
:- Clock Source->Internal clock,
- Prescaler (PSC)->999
- Counter Period (AutoReload Register)->79
- Postavke iznad obezbeđuju na svakih 10ms da se desi update interrupt, 8MHz/PSC je broj taktova u jednoj sekundi, a ARR na koliko taktova se generiše update interrupt.
- U
NVIC
omogućiti update interrupt zaTIM1
. - Podesiti pinove
PC0-PC11
kaoGPIO_Output
. - Generisati kod.
Eclipse
main.c
(&htim1)->Instance->SR = ~TIM_SR_UIF; // Чисти иницијалани update interrupt flag по покретању пројекта
HAL_TIM_Base_Start_IT(&htim1); // Стартује TIM1
tim.c
uint8_t digits[4] = { 1, 2, 3, 4 };
uint8_t sevenSeg[] = { 0x81, 0xCF, 0x92, 0x86, 0xCC, 0xA4, 0xA0, 0x8F, 0x80, 0x84 };
uint8_t currentDigit = 0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM1) {
GPIOC->ODR &= ~0xF00; // Чисти приказ свих цифара (чисти пинове за аноде 7seg дисплеја)
GPIOC->ODR &= ~0xFF; //Чисти стару вредност текуће цифре
GPIOC->ODR |= sevenSeg[digits[currentDigit]]; // Поставља нову вредност текуће цифре
GPIOC->ODR |= 0x1 << (currentDigit + 8); // Приказује текућу цифреу активирањем пина аноде на одговарајућој позицији
currentDigit = (currentDigit + 1) % 4;
}
}
Proteus
- Koristi se
7SEG-MPX4-CA
iz bibliotekeDISPLAY
. - Povezati sedmosegmenti displej u skladu sa referentnom šemom.
- Testirati ispravan rad.
Stavka 3
CubeMX
- Tajmer
TIM1
:- Channel1->IPWM Generation CH1,
- Counter Period (AutoReload Register)->9
- Pulse->0
- CH Polarity->Low
- U
NVIC
omogućiti capture compare interrupt. - Generisati kod.
Eclipse
main.c
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
stm32f1xx_it.c
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
switch (GPIO_Pin) {
case GPIO_PIN_0:
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1);
if (power < 9) {
power++;
}
htim1.Instance->CCR1 = power;
break;
case GPIO_PIN_2:
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
if (power > 0) {
power--;
}
htim1.Instance->CCR1 = power;
break;
}
}
Proteus
- Postaviti kolo sa motorom u skladu sa referentnom šemom.
- Za bateriju se koristi
CELL
iz bibliotekeDEVICE
, a za motorMOTOR
iz bibliotekeACTIVE
. - Dioda ovde ne treba da bude digitalna.
- Obratiti pažnju da je napon baterija 12V.
Stavka 4
CubeMX
- Tajmer
TIM3
:- Clock Source->Internal clock,
- Channel1->Input Capture direct mode,
- Prescaler (PSC)->7999
- Counter Period (AutoReload Register)->65565
- Polarity Selection kanala 1->Rising edge
- U
NVIC
omogućiti general interrupt odTIM3
. - Generisati kod.
Eclipse
main.c
(&htim3)->Instance->SR = ~TIM_SR_UIF;
HAL_TIM_Base_Start_IT(&htim3);
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);
tim.c
uint8_t digits[4] = { 1, 2, 3, 4 };
uint8_t sevenSeg[] = { 0x81, 0xCF, 0x92, 0x86, 0xCC, 0xA4, 0xA0, 0x8F, 0x80, 0x84 };
uint8_t currentDigit = 0;
uint32_t timestamp = 0;
uint32_t overflowCounter = 0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance != TIM3 && htim->Channel != HAL_TIM_ACTIVE_CHANNEL_1) {
return;
}
uint32_t currentTimestamp = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
// Прво мерење не треба да ажурира дисплеј, већ само измери време.
if (timestamp != 0) {
// Број откуцаја који су прошли од претходног мерења, а један откуцај је
// једна милисекунда.
uint32_t periodMs = currentTimestamp - timestamp + overflowCounter * 65536;
// фреквенција = 1 / периода у секундама = 1000 / периода у милисекундама
uint32_t frequency = 1000 / periodMs;
digits[0] = frequency / 1000;
digits[1] = frequency / 100 % 10;
digits[2] = frequency / 10 % 10;
digits[3] = frequency % 10;
}
timestamp = currentTimestamp;
overflowCounter = 0;
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM1) {
// У нижих осам битова (PC7..0) иду битови тренутне цифре, а у виших четири
// (PC11..8) иде један бит који каже на коју цифру тренутно исписујемо.
GPIOC->ODR = sevenSeg[digits[currentDigit]] | (1 << (8 + currentDigit));
currentDigit = (currentDigit + 1) % 4;
} else if (htim->Instance == TIM3) {
++overflowCounter;
}
}
Proteus
- Postaviti preostale komponente u skladu sa referentnom šemom.
- Na DCLOCK-u se isprobavaju frekvencije manje od 8000.