Mikroprocesorski sistemi/Januar 2022
Januarski rok 2022. godine nema postavku dostupnu sa stranice predmeta. Nije mnogo poznato o ovom roku, osim zadatka navedenog ispod.
Zadatak
Postavka
U sistemu postoji jedan mikrokontroler koji kontroliše ventilator u skladu sa trenutnom vrednošću temperature. U simulatoru Proteus napraviti projekat i instancirati mikrokontroler STM32F103R6 (CM3_STM32). Napraviti projekat u alatu CubeMX koji će služiti za konfigurisanje mikrokontrolera.U simulatoru Proteus dodati sledeće komponentestrogo prema datoj referentnoj šemi:- analogni multiplekser 74HC4051 (74HC) čiji je izlazni pin povezan na kanal 7 analogno-digitalnog konvertora mikrokontrolera; selekcija izlaza analognog multipleksera se vrši preko njegovih pinova CBA gde pin A ima najmanju težinu,
temperaturni senzor LM35 (NATDAC) čiji je izlazni pin povezanna nulti ulaz (X0) multileksera(analogna vrednost napona na izlaznom pinu senzora je linearno proporcionalna vrednosti temperature odnosno radi dobijanja vrednost temperature napon treba pomnožiti sa vrednošću 100),- anemometar ANEMOMETER (ACTIVE) povezan u skladu sa referentnom šemom na kanale 1 i 2 tajmera 1;[2] izlazni signal anemometra je periodična pravougaona povorka čija se frekvencija linearno povećava sa povećanjem brzine vetra (brzina vetra jednaka je proizvodu frekvencije izlaznog signala anemometra i realne konstante 2.4),
- vetrokaz WINDVANE (ACTIVE) čiji je izlazni pin povezan u skladu sa referentnom šemom (obratiti pažnju na postojanje otpornika) na prvi ulaz (X1) multipleksera; analogna vrednost napona na izlaznom pinu vetrokaza predstavlja azimut odnosno pravac vetra (funkcija za preslikavanje vrednosti napona u azimut data je u prilogu),[3]
virtuelni terminal povezan na periferiju USART1 mikrokontrolerapreko pinova PB6 i PB7,- LCD displej LM041L (DISPLAY) sa četiri linije, kojima redom odgovaraju početne adrese
0x00,0x40,0x90i0xD0u DDRAM memoriji kontrolera HD44780,povezan na pinove PC0-PC6 mikrokontrolera i - crvena LED-RED (ACTIVE) povezana na PA13.
- Obezbediti prikaz poruke Temper: <t> u trećem redu LCD displeja, gde <t> predstavlja trenutnu vrednost temperature izraženo u stepenima Celzijusa u opsegu [0, 60] očitanu sa temperaturnog senzora. Osvežavanje poruke vršiti periodično na svakih 200 milisekundi ukoliko je došlo do promene vrednosti.
- Obezbediti prikaz poruke Azimut: <a> u prvom redu LCD displeja, gde <a> predstavlja trenutnu vrednost azimuta vetra očitanu sa vetrokaza. Osvežavanje poruke vršiti periodično na svakih 200 milisekundi ukoliko je došlo do promene vrednosti.
- Obezbediti prikaz poruke Brzina: <b> u drugom redu LCD displeja, gde <b> predstavlja trenutnu vrednost brzine vetra očitanu sa anemometra. Osvežavanje poruke vršiti periodično na svakih 200 milisekundi ukoliko je došlo do promene vrednosti.
- Obezbediti treperenje crvene LED sa periodom od jedne sekunde (pola sekunde svetli, pola sekunde ne svetli) ukoliko je brzina vetra jednaka ili veća od 50 kilometara na sat. Ukoliko je brzina vetra manja od 50 kilometara na sat crvena LED ne svetli.
- Obezbediti prikaz poruka <a>/<b>/<t> na virtuelnom terminalu, gde je <a> trenutna vrednost azimuta vetra, <b> trenutna vrednost brzine vetra i <t> trenutna vrednost temperature izraženo u stepenima Celzijusa u opsegu [0, 60]. Za veličine čije vrednosti nisu uspešno izračunate ispisati ? (znak pitanja). Ukoliko je ipak za neku od stavki (3), (4) ili (5) vrednost uspešno izračunata onda ta vrednost mora biti prikazana na virtuelnom terminalu da bi za stavku (7) bili priznati poeni. Osvežavanje opisane poruke vršiti periodično na svakih 200 milisekundi ukoliko je došlo do promene vrednosti. Prilikom osvežavanja poruke promeniti trenutne vrednosti u postojećoj poruci umesto dodavanja nove poruke na virtuelnom terminalu.
Rešenje
Celo rešenje može se preuzeti odavde. Razlika rešenja u odnosu na postavku jeste što je redosled ispisa na LCD displeju temperatura, azimut, brzina umesto azimut, brzina, temperatura.
Svi relevantni izmenjeni fajlovi sa kodom nalaze se u cubemx/code/Core/Src i cubemx/code/Core/Inc direktorijumima.
CubeMX
U alatu CubeMX napravljene su sledeće izmene:
- Omogućeni su prekidi od ADC, TIM1 i USART1.
- Kao Timebase Source podešen je TIM2.
- Omogućen je IN7 ulaz ADC1.
- U TIM1 je kanal 1 podešen za Input Capture.
- U USART1 je režim podešen na Asynchronous.
- U FreeRTOS je interfejs podešen na CMSIS_V2.
- U Code Generator tabu je podešeno da se generišu odvojeni fajlovi za svaku periferiju.
- Pinovi PC6..0, PA13 i PB0 su podešeni kao GPIO_Output.
Nakon generisanja, linija 310 u Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c je zakomentarisana.
Makefile
U Makefile su dodati fajlovi uart.c, lcd.c, sensors.c i exam.c u C_SOURCES, 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.
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"
#define LCD_ENABLE_BIT 0x04
QueueHandle_t LCD_QueueHandle;
LCD_Command cmd;
void LCD_Write(LCD_Register reg, uint8_t data) {
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
writeLCD(0x00, "Temper: ");
writeLCD(0x40, "Azimut: ");
writeLCD(0x10, "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);
writeLCD(0x08, temper);
writeLCD(0x48, azimut);
writeLCD(0x18, 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
Unutar HAL_TIM_PeriodElapsedCallback, dodat je blok koda:
/* USER CODE BEGIN Callback 1 */
if (htim->Instance == TIM1) {
++overflowCounter;
}
/* USER CODE END Callback 1 */
Napomene
- ↑ Na datoj šemi LCD displej nije povezan na isti način kao na vežbama - kod je odgovarajuće modifikovan da se ovome prilagodi. Na pravom ispitu je šema najverovatnije bila kao što je rađeno na vežbama.
- ↑ Na referentnoj šemi sa vikija povezan je samo na jedan kanal, jer je samo jedan kanal i potreban.
- ↑ Ovaj prilog ne postoji na vikiju, ali možete iskopirati funkciju
adcToWindvaneiz rešenja.