Mikroprocesorski sistemi/Februar 2022

Izvor: SI Wiki
Pređi na navigaciju Pređi na pretragu

Februarski rok 2022. godine nema postavku dostupnu sa stranice predmeta. Nije mnogo poznato o ovom roku, osim zadatka navedenog ispod.

Zadatak

Postavka

Referentna šema u zadatku.
  1. 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.
  2. U simulatoru Proteus dodati sledeće komponente strogo prema datoj referentnoj šemi:
    • elektromotor MOTOR (MOTORS) koji predstavlja ventilator i čija će brzina rada biti kontrolisana PWM signalom kanala 1 tajmera 3 mikrokontrolera,
    • tastaturu KEYPAD-PHONE (ACTIVE) povezanu u skladu sa referentnom šemom,
    • temperaturni senzor LM35 (NATDAC) čiji je izlazni pin povezan na kanal 7 analogno-digitalnog konvertora mikrokontrolera (analogna vrednost napona na izlaznom pinu senzora je linearno proporcionalna vrednosti temperature),
    • kišomer RAINGAUGE (ACTIVE) povezan u skladu sa referentnom šemom na monostabilni multivibrator 74HC221 (74HC); kišomer radi po principu kofe sa prevrtanjem (kofa se puni kišnicom sve dok se ne prepuni, zatim dolazi do njenog prevrtanja i potpunog pražnjenja uz generisanje električnog impulsa); monostabilni multivibrator obezbeđuje uniformno trajanje generisanog električnog impulsa; podesiti na kišomeru nivo prevrtanja kofe (Trigger Level) na vrednost 0.01 milimetara usled čega će kofa slati električni impuls na svakih 1200 milisekundi pri konstantnom intenzitetu padavine od 30.0 milimetara po sat (za veći intenzitet padavine impuls će stizati brže i obrnuto); podesiti na monostabilnom multivibratoru vremensku konstantu (Monostable Time Constant) na jednu milisekundu (1mS) usled čega će signal na njegovom izlazu uvek trajati tačno jednu milisekundu,
    • virtuelni terminal povezan na periferiju USART1 mikrokontrolera,
    • LCD displej LM041L (DISPLAY) sa četiri linije, kojima redom odgovaraju početne adrese 0x80, 0xC0, 0x90 i 0xD0 u DDRAM memoriji kontrolera HD44780, povezan na pinove PC0-PC6 mikrokontrolera i
    • dvobojnu LED-BIRG (ACTIVE), povezanu u skladu sa referentnom šemom, kojoj treba podesiti jačinu struje punog provoda (Full drive current) na jedan mikro amper (1uA); ova dioda dozvoljava protok struje u oba smera pri čemu svetli različitom bojom u zavisnosti od smera protoka struje.
  3. Obezbediti prikaz poruke Temp: <t> na virtuelnom terminalu, gde je <t> trenutna vrednost temperature izraženo u stepenima Celzijusa u opsegu [0, 60] očitana sa temperaturnog senzora. 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.
  4. Obezbediti prikaz poruke Kisa: <k> u prvom redu LCD displeja, gde <k> predstavlja trenutnu vrednost intenziteta padavine izraženo u milimetrima po satu očitanu sa kišomera. Osvežavanje poruke vršiti periodično na svakih 200 milisekundi ukoliko je došlo do promene vrednosti.
  5. Obezbediti mogućnost unosa vrednosti temp_granica, koja predstavlja granicu temperature za uključivanje ventilatora, preko tastature i prikaz poruke Thld:(??)=><thld> u četvrtom redu LCD displeja u skladu sa referentnim snimkom, gde <thld> predstavlja trenutno važeću vrednost granice (inicijalna vrednost granice je 30) a na mestu znakova pitanja se prikazuju pritisnuti tasteri do trenutka kada se pritisne drugi po redu taster usled čega se postavlja nova vrednost granice i vraćaju znakovi pitanja. Osvežavanje poruke vršiti periodično na svakih 200 milisekundi ukoliko je došlo do promene vrednosti.
  6. Obezbediti ponašanje dvobojne LED opisano u nastavku. Dvobojna LED konstantno svetli zelenom bojom kada je trenutna temperatura niža od temp_granica, dok ukoliko je trenutna temperatura jednaka ili veća od temp_granica dvobojna LED treperi crvenom bojom sa periodom od jedne sekunde (pola sekunde svetli, pola sekunde ne svetli).
  7. Obezbediti kontrolu ventilatora na način opisan u nastavku. Zavisno od trenutne vrednosti temperature ventilator se vrti odgovarajućom brzinom. Ukoliko trenutna vrednost temperature pripada opsegu:
    • [0, temp_granica) ventilator se ne vrti i
    • [temp_granica, 60] ventilator se vrti brzinom jednakom 50% maksimalne brzine
    Ažuriranje brzine ventilatora vršiti periodično na 200 milisekundi.

Rešenje

Celo rešenje može se preuzeti odavde. Rešenje je zasnovano na rešenju domaćeg zadatka iz 2023. godine, pa će ispod biti navedeni fajlovi izmenjeni u odnosu na to rešenje.

Svi relevantni izmenjeni fajlovi sa kodom nalaze se u cubemx/code/Core/Src i cubemx/code/Core/Inc direktorijumima.

NA SAMOM ROKU NISU BILE DATE IMPLEMENTACIJE METODA U DVA FAJLA ISPOD, MORAJU DA SE DOPUNE NA LICU MESTA.

driver_motor.c

#include "tim.h"

#define ARR 9
#define STEP 2
#define INCREMENT ((ARR+1)/STEP)

static uint32_t volatile compReg = 0;

void MOTOR_SpeedIncrease()
{
	if(compReg < (ARR+1)){
		compReg += INCREMENT;
	}

	htim3.Instance->CCR1 = compReg;
}

void MOTOR_SpeedDecrease()
{
	if(compReg > 0){
		compReg -= INCREMENT;
	}

	htim3.Instance->CCR1 = compReg;
}

void MOTOR_Init()
{
	HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
}

driver_uart.c

#include "driver_uart.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"

#include <string.h>

#include "usart.h"

// TRANSMIT
// -----------------------------------------------------------------------------

static TaskHandle_t UART_TransmitTaskHandle;
static QueueHandle_t UART_TransmitQueueHandle;
static SemaphoreHandle_t UART_TransmitMutexHandle;

static void UART_TransmitTask(void *parameters)
{
	uint8_t buf;
	while(1){
		xQueueReceive(UART_TransmitQueueHandle, &buf,portMAX_DELAY);
		HAL_UART_Transmit_IT(&huart1, &buf, sizeof(uint8_t));
		ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
	}
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == huart1.Instance){
		BaseType_t woken = pdFALSE;
		vTaskNotifyGiveFromISR(UART_TransmitTaskHandle, &woken);
		portYIELD_FROM_ISR(woken);
	}
}

// RECEIVE
// -----------------------------------------------------------------------------

static TaskHandle_t UART_ReceiveTaskHandle;
static QueueHandle_t UART_ReceiveQueueHandle;
static SemaphoreHandle_t UART_ReceiveMutexHandle;

static void UART_ReceiveTask(void *parameters)
{
	uint8_t buf;
	while(1){
		HAL_UART_Receive_IT(&huart1, &buf, sizeof(uint8_t));
		ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
		xQueueSendToBack(UART_ReceiveQueueHandle,&buf,portMAX_DELAY);
	}
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == huart1.Instance){
		BaseType_t woken = pdFALSE;
		vTaskNotifyGiveFromISR(UART_ReceiveTaskHandle, &woken);
		portYIELD_FROM_ISR(woken);
	}
}

// GENERAL
// -----------------------------------------------------------------------------

void UART_Init()
{
	UART_TransmitMutexHandle = xSemaphoreCreateMutex();
	UART_TransmitQueueHandle = xQueueCreate(64, sizeof(uint8_t));
	xTaskCreate(UART_TransmitTask, "transmitTask", 64, NULL,4, &UART_TransmitTaskHandle);

	UART_ReceiveMutexHandle = xSemaphoreCreateMutex();
	UART_ReceiveQueueHandle = xQueueCreate(64, sizeof(uint8_t));
	xTaskCreate(UART_ReceiveTask, "receiveTask", 64, NULL, 10, &UART_ReceiveTaskHandle);
}

// TRANSMIT UTIL
// -----------------------------------------------------------------------------

void UART_AsyncTransmitCharacter(char character)
{
	xSemaphoreTake(UART_TransmitMutexHandle, portMAX_DELAY);
	xQueueSendToBack(UART_TransmitQueueHandle, &character, portMAX_DELAY);
	xSemaphoreGive(UART_TransmitMutexHandle);
}


// RECEIVE UTIL
// -----------------------------------------------------------------------------

char UART_BlockReceiveCharacter()
{
	char c;
	xSemaphoreTake(UART_ReceiveMutexHandle, portMAX_DELAY);
	xQueueReceive(UART_ReceiveQueueHandle, &c, portMAX_DELAY);
	xSemaphoreGive(UART_ReceiveMutexHandle);
	return c;
}

CubeMX

U alatu CubeMX napravljene su sledeće izmene na projektu iz domaćeg zadatka:

  • Omogućeni su prekidi od EXTI.
  • Pinovi PB3..0 i PB9..8 su podešeni kao GPIO_Output, pinovi PB4..6 kao GPIO_Input, dok su pinovi PB7 i PB10 podešeni kao EXTI.

Nakon generisanja, linija 310 u Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c je zakomentarisana.

Makefile

U odnosu na projekat iz domaćeg zadatka, u Makefile je dodat keypad.c fajl.

keypad.h

#ifndef CORE_INC_KEYPAD_H_
#define CORE_INC_KEYPAD_H_

extern char keys[2];
extern unsigned temp_granica;
extern int keysChanged;

void KEY_Init();

#endif

keypad.c

#include "keypad.h"

#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"

#include "gpio.h"

char keys[2];
unsigned temp_granica = 30;
int keysChanged = 1;

static int keyCount = 0;
static int keyReleased = 1;

TaskHandle_t KEY_TaskHandle;
TimerHandle_t KEY_TimerHandle;

const char KEY_MATRIX[4][3] = {
	{ '1', '2', '3' },
	{ '4', '5', '6' },
	{ '7', '8', '9' },
	{ '*', '0', '#' }
};

extern void homeworkOverflow();

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
	if (GPIO_Pin == GPIO_PIN_7) {
		BaseType_t wokenTask = pdFALSE;
		vTaskNotifyGiveFromISR(KEY_TaskHandle, &wokenTask);
		portYIELD_FROM_ISR(wokenTask);
	} else if (GPIO_Pin == GPIO_PIN_10) {
		homeworkOverflow();
	}
}

static void KEY_Task(void *parameters) {
	UNUSED(parameters);
	keys[0] = '?';
	keys[1] = '?';
	while (1) {
		for (int row = 0; row < 4; ++row) {
			HAL_GPIO_WritePin(GPIOB, 1u << row, GPIO_PIN_SET);
		}
		vTaskDelay(pdMS_TO_TICKS(10));
		if (!keyReleased) {
			continue;
		}
		for (int row = 0; row < 4; ++row) {
			HAL_GPIO_WritePin(GPIOB, 1u << row, GPIO_PIN_RESET);
		}
		for (int row = 0; row < 4; ++row) {
			HAL_GPIO_WritePin(GPIOB, 1u << row, GPIO_PIN_SET);
			for (int column = 0; column < 3; ++column) {
				if (HAL_GPIO_ReadPin(GPIOB, 1u << (4 + column))
						== GPIO_PIN_SET) {
					keys[keyCount] = KEY_MATRIX[row][column];
					if (keyCount == 1) {
						temp_granica = (keys[0] - '0') * 10 + keys[1] - '0';
						keys[0] = '?';
						keys[1] = '?';
					}
					keyCount = 1 - keyCount;
					keysChanged = 1;
					keyReleased = 0;
					xTimerStart(KEY_TimerHandle, portMAX_DELAY);
				}
			}
			HAL_GPIO_WritePin(GPIOB, 1u << row, GPIO_PIN_RESET);
		}
	}
}

void KEY_Timer(TimerHandle_t xTimer) {
	UNUSED(xTimer);
	if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_SET) {
		xTimerStart(KEY_TimerHandle, portMAX_DELAY);
	} else {
		keyReleased = 1;
	}
}

void KEY_Init() {
	xTaskCreate(KEY_Task, "KEY_Task", 128, NULL, 5, &KEY_TaskHandle);
	KEY_TimerHandle = xTimerCreate("KEY_Timer", pdMS_TO_TICKS(10), pdFALSE,
	NULL, KEY_Timer);
}

homework.h

#ifndef CORE_INC_HOMEWORK_H_
#define CORE_INC_HOMEWORK_H_

typedef enum
{
	TURNED_OFF, SLOW, FAST
} FanState;

extern void homeworkInit();
extern void homeworkOverflow();

#endif

homework.c

#include "homework.h"

#include "FreeRTOS.h"
#include "task.h"

#include <stdlib.h>
#include <string.h>

#include "FreeRTOS.h"
#include "timers.h"

#include "driver_lcd.h"
#include "driver_uart.h"
#include "driver_motor.h"
#include "driver_temp.h"
#include "keypad.h"

#include "gpio.h"

FanState fanState = TURNED_OFF;
TimerHandle_t homeworkTimer;
TimerHandle_t ledTimer;

unsigned passedMs = 0;
unsigned rainfall = 0;

static uint32_t tempValue;
static char tempText[4];
static void homeworkTask(void *parameters) {
	UNUSED(parameters);
	char messageTemp[9] = "Temp:   ";
	char messageKisa[7] = "Kisa: ";
#pragma GCC diagnostic ignored "-Wtrigraphs"
	char messageThld[14] = "Thld:(??)=>30";
	// Ekvivalentna adresa: 0x80
	LCD_CommandEnqueue(LCD_INSTRUCTION,
	LCD_SET_DD_RAM_ADDRESS_INSTRUCTION | 0x00);
	for (uint32_t i = 0; i < 8; i++) {
		UART_AsyncTransmitCharacter(messageTemp[i]);
	}
	for (uint32_t i = 0; i < 6; i++) {
		LCD_CommandEnqueue(LCD_DATA, messageKisa[i]);
	}
	// Ekvivalentna adresa: 0xD0
	LCD_CommandEnqueue(LCD_INSTRUCTION,
	LCD_SET_DD_RAM_ADDRESS_INSTRUCTION | 0x50);
	for (uint32_t i = 0; i < 13; i++) {
		LCD_CommandEnqueue(LCD_DATA, messageThld[i]);
	}

	while (1) {
		tempValue = TEMP_GetCurrentValue();
		itoa(rainfall, tempText, 10);

		FanState fanStateTarget;
		if (tempValue < temp_granica) {
			fanStateTarget = TURNED_OFF;
		} else {
			fanStateTarget = SLOW;
		}
		for (uint32_t i = 0; i < abs(fanStateTarget - fanState); i++) {
			if (fanStateTarget > fanState) {
				MOTOR_SpeedIncrease();
			} else {
				MOTOR_SpeedDecrease();
			}
		}
		fanState = fanStateTarget;

		LCD_CommandEnqueue(LCD_INSTRUCTION,
		LCD_SET_DD_RAM_ADDRESS_INSTRUCTION | 0x06);
		for (uint32_t i = 0; i < strlen(tempText); i++) {
			LCD_CommandEnqueue(LCD_DATA, tempText[i]);
		}
		UART_AsyncTransmitCharacter('\b');
		UART_AsyncTransmitCharacter('\b');
		if (tempValue < 10) {
			UART_AsyncTransmitCharacter(' ');
		} else {
			UART_AsyncTransmitCharacter(tempValue / 10 + '0');
		}
		UART_AsyncTransmitCharacter(tempValue % 10 + '0');

		if (keysChanged) {
			keysChanged = 0;
			LCD_CommandEnqueue(LCD_INSTRUCTION,
			LCD_SET_DD_RAM_ADDRESS_INSTRUCTION | 0x56);
			LCD_CommandEnqueue(LCD_DATA, keys[0]);
			LCD_CommandEnqueue(LCD_DATA, keys[1]);
			LCD_CommandEnqueue(LCD_INSTRUCTION,
			LCD_SET_DD_RAM_ADDRESS_INSTRUCTION | 0x5B);
			LCD_CommandEnqueue(LCD_DATA,
					(temp_granica < 10) ? ' ' : (temp_granica / 10) + '0');
			LCD_CommandEnqueue(LCD_DATA, (temp_granica % 10) + '0');
		}

		vTaskDelay(pdMS_TO_TICKS(200));
	}
}

void homeworkOverflow() {
	rainfall = 36000 / passedMs;
	passedMs = 0;
}

void homeworkCounter(TimerHandle_t xTimer) {
	UNUSED(xTimer);
	++passedMs;
}

void ledCounter(TimerHandle_t xTimer) {
	UNUSED(xTimer);
	if (TEMP_GetCurrentValue() < temp_granica) {
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
	} else {
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
		HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_9);
	}
}

void homeworkInit() {
	LCD_Init();
	UART_Init();
	MOTOR_Init();
	TEMP_Init();
	KEY_Init();
	xTaskCreate(homeworkTask, "homeworkTask", 64, NULL, 5, NULL);
	homeworkTimer = xTimerCreate("homeworkTimer", pdMS_TO_TICKS(1), pdTRUE,
	NULL, homeworkCounter);
	xTimerStart(homeworkTimer, portMAX_DELAY);
	ledTimer = xTimerCreate("ledTimer", pdMS_TO_TICKS(500), pdTRUE,
	NULL, ledCounter);
	xTimerStart(ledTimer, portMAX_DELAY);
}