Mikroprocesorski sistemi/Januar 2022

Izvor: SI Wiki
< Микропроцесорски системи
Datum izmene: 25. januar 2023. u 21:11; autor: KockaAdmiralac (razgovor | doprinosi) (Moje rešenje januara 2022)
(razl) ← Starija izmena | Trenutna verzija (razl) | Novija izmena → (razl)
Pređi na navigaciju Pređi na pretragu

Januarski 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]
  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:
    • 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 povezan na 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 mikrokontrolera preko pinova PB6 i PB7,
    • LCD displej LM041L (DISPLAY) sa četiri linije, kojima redom odgovaraju početne adrese 0x00, 0x40, 0x90 i 0xD0 u DDRAM memoriji kontrolera HD44780, povezan na pinove PC0-PC6 mikrokontrolera i
    • crvena LED-RED (ACTIVE) povezana na PA13.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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 IN1 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šeno 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

  1. 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.
  2. Na referentnoj šemi sa vikija povezan je samo na jedan kanal, jer je samo jedan kanal i potreban.
  3. Ovaj prilog ne postoji na vikiju, ali možete iskopirati funkciju adcToWindvane iz rešenja.