Микропроцесорски системи/Јануар 2022
Јануарски рок 2022. године нема поставку доступну са странице предмета. Није много познато о овом року, осим задатка наведеног испод.
Задатак
Поставка
У систему постоји један микроконтролер који контролише вентилатор у складу са тренутном вредношћу температуре. У симулатору Proteus направити пројекат и инстанцирати микроконтролер STM32F103R6 (CM3_STM32). Направити пројекат у алату CubeMX који ће служити за конфигурисање микроконтролера.У симулатору Proteus додати следеће компонентестрого према датој референтној шеми:- аналогни мултиплексер 74HC4051 (74HC) чији је излазни пин повезан на канал 7 аналогно-дигиталног конвертора микроконтролера; селекција излаза аналогног мултиплексера се врши преко његових пинова CBA где пин A има најмању тежину,
температурни сензор LM35 (NATDAC) чији је излазни пин повезанна нулти улаз (X0) мултилексера(аналогна вредност напона на излазном пину сензора је линеарно пропорционална вредности температуре односно ради добијања вредност температуре напон треба помножити са вредношћу 100),- анемометар ANEMOMETER (ACTIVE) повезан у складу са референтном шемом на канале 1 и 2 тајмера 1;[2] излазни сигнал анемометра је периодична правоугаона поворка чија се фреквенција линеарно повећава са повећањем брзине ветра (брзина ветра једнака је производу фреквенције излазног сигнала анемометра и реалне константе 2.4),
- ветроказ WINDVANE (ACTIVE) чији је излазни пин повезан у складу са референтном шемом (обратити пажњу на постојање отпорника) на први улаз (X1) мултиплексера; аналогна вредност напона на излазном пину ветроказа представља азимут односно правац ветра (функција за пресликавање вредности напона у азимут дата је у прилогу),[3]
виртуелни терминал повезан на периферију USART1 микроконтролерапреко пинова PB6 и PB7,- LCD дисплеј LM041L (DISPLAY) са четири линије, којима редом одговарају почетне адресе
0x00,0x40,0x90и0xD0у DDRAM меморији контролера HD44780,повезан на пинове PC0-PC6 микроконтролера и - црвена LED-RED (ACTIVE) повезана на PA13.
- Обезбедити приказ поруке Temper: <t> у трећем реду LCD дисплеја, где <t> представља тренутну вредност температуре изражено у степенима Целзијуса у опсегу [0, 60] очитану са температурног сензора. Освежавање поруке вршити периодично на сваких 200 милисекунди уколико је дошло до промене вредности.
- Обезбедити приказ поруке Azimut: <a> у првом реду LCD дисплеја, где <a> представља тренутну вредност азимута ветра очитану са ветроказа. Освежавање поруке вршити периодично на сваких 200 милисекунди уколико је дошло до промене вредности.
- Обезбедити приказ поруке Brzina: <b> у другом реду LCD дисплеја, где <b> представља тренутну вредност брзине ветра очитану са анемометра. Освежавање поруке вршити периодично на сваких 200 милисекунди уколико је дошло до промене вредности.
- Обезбедити треперење црвене LED са периодом од једне секунде (пола секунде светли, пола секунде не светли) уколико је брзина ветра једнака или већа од 50 километара на сат. Уколико је брзина ветра мања од 50 километара на сат црвена LED не светли.
- Обезбедити приказ порука <a>/<b>/<t> на виртуелном терминалу, где је <a> тренутна вредност азимута ветра, <b> тренутна вредност брзине ветра и <t> тренутна вредност температуре изражено у степенима Целзијуса у опсегу [0, 60]. За величине чије вредности нису успешно израчунате исписати ? (знак питања). Уколико је ипак за неку од ставки (3), (4) или (5) вредност успешно израчуната онда та вредност мора бити приказана на виртуелном терминалу да би за ставку (7) били признати поени. Освежавање описане поруке вршити периодично на сваких 200 милисекунди уколико је дошло до промене вредности. Приликом освежавања поруке променити тренутне вредности у постојећој поруци уместо додавања нове поруке на виртуелном терминалу.
Решење
Цело решење може се преузети одавде. Разлика решења у односу на поставку јесте што је редослед исписа на LCD дисплеју температура, азимут, брзина уместо азимут, брзина, температура.
Сви релевантни измењени фајлови са кодом налазе се у cubemx/code/Core/Src и cubemx/code/Core/Inc директоријумима.
CubeMX
У алату CubeMX направљене су следеће измене:
- Омогућени су прекиди од ADC, TIM1 и USART1.
- Као Timebase Source подешен је TIM2.
- Омогућен је IN7 улаз ADC1.
- У TIM1 је канал 1 подешен за Input Capture.
- У USART1 је режим подешен на Asynchronous, Baud Rate је 9600Bits/s.
- У FreeRTOS је интерфејс подешен на CMSIS_V2.
- У Code Generator табу је подешено да се генеришу одвојени фајлови за сваку периферију.
- Пинови PC6..0, PA13 и PB0 су подешени као GPIO_Output.
Након генерисања, линија 310 у Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c је закоментарисана.
Makefile
У Makefile су додати фајлови uart.c, lcd.c, sensors.c и exam.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.
lcd.h
#ifndef CORE_INC_LCD_H_
#define CORE_INC_LCD_H_
#include <stdint.h>
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#define CLEAR_DISPLAY_INSTRUCTION 0x01
#define RETURN_HOME_INSTRUCTION 0x02
#define ENTRY_MODE_INSTRUCTION 0x04
#define ENTRY_MODE_INC_CURSOR 0x02
#define DISPLAY_INSTRUCTION 0x08
#define DISPLAY_ON 0x04
#define DISPLAY_CURSOR_ON 0x02
#define DISPLAY_BLINK_ON 0x01
#define SHIFT_INSTRUCTION 0x10
#define SHIFT_DISPLAY 0x08
#define SHIFT_RIGHT 0x04
#define FUNCTION_INSTRUCTION 0x20
#define FUNCTION_2_LINES 0x08
#define FUNCTION_5x10_DOTS 0x04
#define SET_CGRAM_ADDRESS_INSTRUCTION 0x40
#define SET_DDRAM_ADDRESS_INSTRUCTION 0x80
typedef enum {
LCD_INSTRUCTION, LCD_DATA
} LCD_Register;
typedef struct {
LCD_Register reg;
uint8_t data;
} LCD_Command;
void LCD_Init();
void LCD_Enqueue(LCD_Register reg, uint8_t data);
#endif
uart.h
#ifndef CORE_INC_UART_H_
#define CORE_INC_UART_H_
#include <stdint.h>
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
void UART_Transmit(uint8_t byte);
void UART_Init();
#endif
sensors.h
#ifndef CORE_INC_SENSORS_H_
#define CORE_INC_SENSORS_H_
extern float temperatureSensor;
extern float anemometerSensor;
extern float windvaneSensor;
void Sensors_Init();
#endif
exam.h
#ifndef CORE_INC_EXAM_H_
#define CORE_INC_EXAM_H_
extern unsigned overflowCounter;
void examInit();
#endif
lcd.c
#include "lcd.h"
#include "gpio.h"
// Napomena: referentna šema se razlikuje od one date na vežbama.
#define LCD_ENABLE_BIT 0x04
QueueHandle_t LCD_QueueHandle;
LCD_Command cmd;
void LCD_Write(LCD_Register reg, uint8_t data) {
// Napomena: referentna šema se razlikuje od one date na vežbama.
GPIOC->ODR = (data << 3) | reg;
GPIOC->ODR |= LCD_ENABLE_BIT;
GPIOC->ODR &= ~LCD_ENABLE_BIT;
}
void LCD_WriteCommand(LCD_Register reg, uint8_t data) {
LCD_Write(reg, data >> 4);
LCD_Write(reg, data);
vTaskDelay(pdMS_TO_TICKS(2));
}
void LCD_Task(void *parameters) {
UNUSED(parameters);
LCD_Write(LCD_INSTRUCTION, (FUNCTION_INSTRUCTION) >> 4);
vTaskDelay(pdMS_TO_TICKS(2));
LCD_WriteCommand(LCD_INSTRUCTION, FUNCTION_INSTRUCTION | FUNCTION_2_LINES);
LCD_WriteCommand(LCD_INSTRUCTION, DISPLAY_INSTRUCTION | DISPLAY_ON);
LCD_WriteCommand(LCD_INSTRUCTION,
ENTRY_MODE_INSTRUCTION | ENTRY_MODE_INC_CURSOR);
LCD_WriteCommand(LCD_INSTRUCTION, RETURN_HOME_INSTRUCTION);
while (1) {
xQueueReceive(LCD_QueueHandle, &cmd, portMAX_DELAY);
LCD_WriteCommand(cmd.reg, cmd.data);
}
}
void LCD_Enqueue(LCD_Register reg, uint8_t data) {
cmd.reg = reg;
cmd.data = data;
xQueueSendToBack(LCD_QueueHandle, &cmd, portMAX_DELAY);
}
void LCD_Init() {
xTaskCreate(LCD_Task, "LCD_Task", 128, NULL, 5, NULL);
LCD_QueueHandle = xQueueCreate(64, sizeof(LCD_Command));
}
uart.c
#include "uart.h"
#include "usart.h"
QueueHandle_t UART_QueueHandle;
TaskHandle_t UART_TaskHandle;
void UART_Task(void *parameters) {
UNUSED(parameters);
uint8_t byte;
while (1) {
xQueueReceive(UART_QueueHandle, &byte, portMAX_DELAY);
HAL_UART_Transmit_IT(&huart1, &byte, sizeof(uint8_t));
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
}
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance != huart1.Instance) {
return;
}
BaseType_t wokenTask = pdFALSE;
vTaskNotifyGiveFromISR(UART_TaskHandle, &wokenTask);
portYIELD_FROM_ISR(wokenTask);
}
void UART_Transmit(uint8_t byte) {
xQueueSendToBack(UART_QueueHandle, &byte, portMAX_DELAY);
}
void UART_Init() {
xTaskCreate(UART_Task, "UART_Task", 128, NULL, 5, &UART_TaskHandle);
UART_QueueHandle = xQueueCreate(64, sizeof(uint8_t));
}
sensors.c
#include "sensors.h"
#include "FreeRTOS.h"
#include "task.h"
#include "adc.h"
#include "gpio.h"
#include "tim.h"
#define MAX_VOLTAGE 5.0
#define MAX_RESOLUTION 4096
float temperatureSensor = 0.0;
float anemometerSensor = 0.0;
float windvaneSensor = 0.0;
int fetchingTemperature;
unsigned overflowCounter = 0;
int previousTicks = 0;
TaskHandle_t Sensors_TaskHandle;
void Sensors_Task(void *parameters) {
UNUSED(parameters);
while (1) {
fetchingTemperature = 1;
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
HAL_ADC_Start_IT(&hadc1);
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
fetchingTemperature = 0;
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
HAL_ADC_Start_IT(&hadc1);
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
vTaskDelay(pdMS_TO_TICKS(100));
}
}
float adcToWindvane(int adcValue) {
switch (adcValue) {
case 952:
return 0.0;
case 2471:
return 22.5;
case 2250:
return 45.0;
case 3760:
return 67.5;
case 3723:
return 90.0;
case 3831:
return 112.5;
case 3356:
return 135.0;
case 3589:
return 157.5;
case 2946:
return 180.0;
case 3116:
return 202.5;
case 1575:
return 225.0;
case 1698:
return 247.5;
case 315:
return 270.0;
case 786:
return 292.5;
case 547:
return 315.0;
case 1285:
return 337.5;
default:
return 0.0;
}
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) {
if (hadc->Instance != hadc1.Instance) {
return;
}
uint32_t value = HAL_ADC_GetValue(hadc);
if (fetchingTemperature) {
temperatureSensor =
((float) value) * MAX_VOLTAGE * 100 / MAX_RESOLUTION;
} else {
windvaneSensor = adcToWindvane(value);
}
xTaskNotifyGive(Sensors_TaskHandle);
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance != htim1.Instance) {
return;
}
float ticks = htim->Instance->CCR1 + overflowCounter * 65536
- previousTicks;
previousTicks = htim->Instance->CCR1;
overflowCounter = 0;
anemometerSensor = 8000000.0 / ticks * 2.4;
}
void Sensors_Init() {
HAL_TIM_Base_Start_IT(&htim1);
HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_1);
xTaskCreate(Sensors_Task, "Sensors_Task", 128, NULL, 5,
&Sensors_TaskHandle);
}
exam.c
#include "exam.h"
#include "lcd.h"
#include "uart.h"
#include "sensors.h"
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "gpio.h"
#include <string.h>
void writeLCD(unsigned addr, const char *str) {
LCD_Enqueue(LCD_INSTRUCTION, SET_DDRAM_ADDRESS_INSTRUCTION | addr);
for (size_t i = 0; i < strlen(str); ++i) {
LCD_Enqueue(LCD_DATA, str[i]);
}
}
void writeUART(char *str) {
for (size_t i = 0; i < strlen(str); ++i) {
UART_Transmit(str[i]);
}
}
void intToStr(int num, char *str, int maxLen) {
for (int i = 0; i < maxLen; ++i) {
str[i] = ' ';
}
int index = maxLen - 1;
if (num == 0) {
str[maxLen - 1] = '0';
}
while (num > 0 && index >= 0) {
str[index--] = '0' + (num % 10);
num /= 10;
}
str[maxLen] = '\0';
}
float prevTemperatureSensor;
float prevWindvaneSensor;
float prevAnemometerSensor;
void examTask(void *parameters) {
UNUSED(parameters);
// Ekvivalentne adrese: 0x80, 0xC0, 0x90
// Kontroleru se ne mogu poslati adrese sa preko 7 bita
// Napomena: redosled ispisa je ispravljen ovde, ali nije u projektu.
writeLCD(0x10, "Temper: ");
writeLCD(0x00, "Azimut: ");
writeLCD(0x40, "Brzina: ");
while (1) {
if (((int) prevTemperatureSensor) != ((int) temperatureSensor)
|| ((int) prevWindvaneSensor) != ((int) windvaneSensor)
|| ((int) prevAnemometerSensor) != ((int) anemometerSensor)) {
prevTemperatureSensor = temperatureSensor;
prevWindvaneSensor = windvaneSensor;
prevAnemometerSensor = anemometerSensor;
char temper[4];
char azimut[4];
char brzina[4];
intToStr((int) temperatureSensor, temper, 3);
intToStr((int) windvaneSensor, azimut, 3);
intToStr((int) anemometerSensor, brzina, 3);
// Napomena: redosled ispisa je ispravljen ovde, ali nije u projektu.
writeLCD(0x18, temper);
writeLCD(0x08, azimut);
writeLCD(0x48, brzina);
writeUART("\b\b\b\b\b\b\b\b\b\b\b");
writeUART(azimut);
writeUART("/");
writeUART(brzina);
writeUART("/");
writeUART(temper);
}
vTaskDelay(pdMS_TO_TICKS(200));
}
}
void ledTimer(TimerHandle_t xTimer) {
UNUSED(xTimer);
if (anemometerSensor < 50) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_13, GPIO_PIN_RESET);
} else {
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_13);
}
}
void examInit() {
LCD_Init();
UART_Init();
Sensors_Init();
xTaskCreate(examTask, "examTask", 128, NULL, 2, NULL);
TimerHandle_t timer = xTimerCreate("ledTimer", pdMS_TO_TICKS(500), pdTRUE,
NULL, ledTimer);
xTimerStart(timer, portMAX_DELAY);
}
main.c
Унутар HAL_TIM_PeriodElapsedCallback, додат је блок кода:
/* USER CODE BEGIN Callback 1 */
if (htim->Instance == TIM1) {
++overflowCounter;
}
/* USER CODE END Callback 1 */
Такође додати:
/* USER CODE BEGIN Includes */
#include "exam.h"
/* USER CODE END Includes */
/* USER CODE BEGIN 2 */
examInit();
/* USER CODE END 2 */
Напомене
- ↑ На датој шеми LCD дисплеј није повезан на исти начин као на вежбама - код је одговарајуће модификован да се овоме прилагоди. На правом испиту је шема највероватније била као што је рађено на вежбама.
- ↑ На референтној шеми са викија повезан је само на један канал, јер је само један канал и потребан.
- ↑ Овај прилог не постоји на викију, али можете ископирати функцију
adcToWindvaneиз решења.