Микропроцесорски системи/К2 2021
Први колоквијум 2021. године нема поставку доступну са странице предмета. Није много познато о овом року, осим задатка наведеног испод. Исти задатак појавио се и на другом колоквијуму 2022. године.
Задатак
Поставка
Користећи алате arm-none-eabi-*, build-tools и Eclipse CDT, Proteus симулатор и CubeMX алат потребно је испунити ставке које се налазе у наставку.
- [6 поена] Направити CubeMX и Proteus пројекте за микроконтролер STM32F103R6. Додати два дугмета и повезати их на пинове
PB0
иPB2
(погледати референтну шему) тако да се могу користити за генерисање прекида. Извршити неопходну конфигурацију тако да се генеришу захтеви за обраду прекида уколико се на улазном сигналу ових пинова препозна узлазна ивица. Додати две LED (light emitting diode) и повезати их на пиновеPB1
иPB3
(погледати референтну шему). Извршити неопходну конфигурацију и затим у оквиру прекидне рутине за дугме које је везано на пинPB0
написати програмски код који врши промену стања на пинуPB1
, а у оквиру прекидне рутине за дугме које је везано на пинPB2
написати програмски код који врши промену стању[sic] на пинуPB3
(погледати референтни снимак).[1] - [6 поена] Додати седмосегментни екран ширине четири цифара, повезати га на одговарајуће пинове према референтној шеми, извршити неопходну конфигурацију и написати програмски код за приказ цифара 1234 на екрану. Освежавање екрана вршити у update event прекидној рутини тајмера TIM1. Уколико се ставка (4) уради у потпуности исправно приказ на екрану ускладити са захтевима из ставке (4) а поени за ставку (2) биће признати у целости.
- [6 поена] Додати електромотор и осцилоскоп у складу са референтном шемом. Потребно је проширити постојеће прекидне рутине из ставке (1) на начин да се контролише брзина рада електромотра.[sic] Улога дугмета повезаног на пин
PB0
јесте да повећа брзину, а улога дугмета повезаног на пинPB2
јесте да смањи брзину (погледати референтни снимак).[1] За генерисање сигнала којим се контролише електромотор користити тајмер TIM1 и његов канал CH1. Појединачни инкремент односно декремент за који се мења брзина представља 10% од максималне брзине електромотора. - [6 поена] Потребно је додати генератор сигнала према референтној шеми. Имплементирати израчунавање тренутне фреквенције сигнала са генератора и приказ израчунате фреквенције на седмосегментном екрану из ставке (2) уместо цифара 1234 (погледати референтни снимак).[1] Фреквенција приказана на екрану треба да буде изражена у Hz. За имплементацију ове ставке користити тајмер TIM3 а могуће је користити његове канале CH1 и CH2. Уколико се ставка (4) уради у потпуности исправно приказ на екрану ускладити са захтевима из ставке (4) а поени за ставку (2) биће признати у целости.
Решење
Цело решење може се преузети одавде.
Сви релевантни измењени фајлови са кодом налазе се у cubemx/code/Core/Src
директоријуму.
Опште напомене
- Потребно је урадити Refresh пројекта у Eclipse окружењу након поновног генерисања кода у CubeMX-у како би све радило исправно.
- За пинове се оставља подразумевана опција no pull-up, no pull-down за разлику од вежби.
- У случају проблема рада седмосегментог дисплеја додати
GPIOC->ODR &= ~0xFFF
пре постављања вредности на дисплеј.
CubeMX
Прво је под Project Manager у Project подешена путања пројекта и Makefile за Toolchain, а затим у Code Generator штиклирана опција за генерисање одвојених изворних фајлова и заглавља за сваку периферију, а затим у Pinout & Configuration подешено:
PC11..0
су подешени каоGPIO_Output
пинови.PB0
иPB2
су подешени каоGPIO_EXTI0
иGPIO_EXTI2
пинови а у NVIC су укључени EXTI line0 interrupt и EXTI line2 interrupt.PB1
иPB3
су подешени каоGPIO_Output
пинови.- У
TIM1
је као Clock Source подешен Internal Clock, на Channel1 је подешено PWM Generation CH1, као prescaler вредност је подешено 7999 (како би један откуцај била једна милисекунда ради лакшег рачунања) а као период је подешено 9 (тако да се прекорачење бројача, односно update event, дешава на сваких десет милисекунди). У PWM подешавањима пулс је подешен на 0 а поларитет на низак (тако да се мотор на почетку неће окретати). У NVIC је изабран TIM1 update interrupt. - У
TIM3
је као Clock Source подешен Internal Clock, на Channel1 је подешен Input Capture direct mode, као prescaler вредност је подешено 7999 (како би један откуцај била једна милисекунда ради лакшег рачунања) а као период је подешено 65535. Као поларитет input capture канала изабрана је узлазна ивица. У NVIC је изабран TIM3 global interrupt.
Makefile
У Makefile је померен tim.c
на дно C_SOURCES
променљиве, следећи блок:
ifeq ($(DEBUG), 1)
CFLAGS += -g -gdwarf-2
endif
# Generate dependency information
CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)"
је промењен на:
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
и startup_stm32f103x6.s
је промењено на Core/Src/startup_stm32f103x6.s
унутар ASM_SOURCES
.
main.c
Додато је:
HAL_TIM_Base_Start_IT(&htim1);
HAL_TIM_Base_Start_IT(&htim3);
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
испод иницијализације периферија.
stm32f1xx_it.c
У одељку за кориснички код на дну фајла је додато:
uint8_t currentMotorSpeed = 0;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == GPIO_PIN_0) {
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1);
if (currentMotorSpeed < 9) {
++currentMotorSpeed;
}
TIM1->CCR1 = currentMotorSpeed;
} else if (GPIO_Pin == GPIO_PIN_2) {
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
if (currentMotorSpeed > 0) {
--currentMotorSpeed;
}
TIM1->CCR1 = currentMotorSpeed;
}
}
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;
}
}