664 lines
17 KiB
C
664 lines
17 KiB
C
/* USER CODE BEGIN Header */
|
|
/**
|
|
******************************************************************************
|
|
* File Name : app_freertos.c
|
|
* Description : Code for freertos applications
|
|
******************************************************************************
|
|
* @attention
|
|
*
|
|
* Copyright (c) 2021 STMicroelectronics.
|
|
* All rights reserved.
|
|
*
|
|
* This software is licensed under terms that can be found in the LICENSE file
|
|
* in the root directory of this software component.
|
|
* If no LICENSE file comes with this software, it is provided AS-IS.
|
|
*
|
|
******************************************************************************
|
|
*/
|
|
/* USER CODE END Header */
|
|
|
|
/* Includes ------------------------------------------------------------------*/
|
|
#include "FreeRTOS.h"
|
|
#include "task.h"
|
|
#include "main.h"
|
|
#include "cmsis_os.h"
|
|
|
|
/* Private includes ----------------------------------------------------------*/
|
|
/* USER CODE BEGIN Includes */
|
|
#include "BME280_STM32.h"
|
|
#include "pid.h"
|
|
#include "icons.h"
|
|
#include "ssd1306.h"
|
|
#include "string.h"
|
|
#include "stdio.h"
|
|
/* USER CODE END Includes */
|
|
|
|
/* Private typedef -----------------------------------------------------------*/
|
|
/* USER CODE BEGIN PTD */
|
|
|
|
typedef struct {
|
|
char text[6];
|
|
uint8_t drytemp;
|
|
uint32_t drytime;
|
|
} MenuItem;
|
|
|
|
/* USER CODE END PTD */
|
|
|
|
/* Private define ------------------------------------------------------------*/
|
|
/* USER CODE BEGIN PD */
|
|
|
|
/* USER CODE END PD */
|
|
|
|
/* Private macro -------------------------------------------------------------*/
|
|
/* USER CODE BEGIN PM */
|
|
|
|
/* USER CODE END PM */
|
|
|
|
/* Private variables ---------------------------------------------------------*/
|
|
/* USER CODE BEGIN Variables */
|
|
float aimTemperature, Temperature, Pressure, aimHumidity, Humidity; //BME280 results and aim values
|
|
uint8_t powerFan = 0, powerHeater = 0; //Set fan/heater power 0 - 100
|
|
|
|
//PID settings
|
|
PIDController pid = {
|
|
.Kp = 5.0f,
|
|
.Ki = 2.0f,
|
|
.Kd = 1.0f,
|
|
.T = 0.25f, //4 times per second
|
|
.tau = 0.2f, //Low-pass filter (0 - no filter)
|
|
.limMin = 0.0f,
|
|
.limMax = 100.0f
|
|
};
|
|
|
|
MenuItem menuProg[] = {
|
|
// text temp time in seconds
|
|
{"PLA", 45, 4*3600},
|
|
{"ABS", 60, 2*3600},
|
|
{"PETG", 65, 2*3600},
|
|
{"TPU", 50, 4*3600},
|
|
{"NYLON", 70, 8*3600},
|
|
{"PVA", 45, 4*3600},
|
|
{"ASA", 60, 4*3600},
|
|
{"PP", 55, 6*3600},
|
|
{"SILIC", 65, 3*3600},
|
|
{"TEST", 27, 30}
|
|
};
|
|
|
|
uint8_t menuMax = 9; // Max menu index
|
|
uint8_t prog = 0; // Selected program
|
|
uint8_t menuFrame = 0; // Menu list frame index
|
|
uint8_t mode = 0; // Current mode: 0 - Idle menu; 1 - Drying; 2 - Drying stop question; 3 - Keeping;
|
|
// 4 - Keeping stop question; 98 - Thermal runout; 99 - sensor error;
|
|
uint8_t qConfirm = 0; // Question menu (Yes/No) selected answer
|
|
uint32_t time_cnt = 0; // Time counter for Drying / Keeping action
|
|
|
|
/* USER CODE END Variables */
|
|
/* Definitions for defaultTask */
|
|
osThreadId_t defaultTaskHandle;
|
|
const osThreadAttr_t defaultTask_attributes = {
|
|
.name = "defaultTask",
|
|
.priority = (osPriority_t) osPriorityNormal,
|
|
.stack_size = 128 * 4
|
|
};
|
|
/* Definitions for svcDisplay */
|
|
osThreadId_t svcDisplayHandle;
|
|
const osThreadAttr_t svcDisplay_attributes = {
|
|
.name = "svcDisplay",
|
|
.priority = (osPriority_t) osPriorityLow,
|
|
.stack_size = 128 * 4
|
|
};
|
|
/* Definitions for svcSensors */
|
|
osThreadId_t svcSensorsHandle;
|
|
const osThreadAttr_t svcSensors_attributes = {
|
|
.name = "svcSensors",
|
|
.priority = (osPriority_t) osPriorityLow,
|
|
.stack_size = 128 * 4
|
|
};
|
|
/* Definitions for svcKeys */
|
|
osThreadId_t svcKeysHandle;
|
|
const osThreadAttr_t svcKeys_attributes = {
|
|
.name = "svcKeys",
|
|
.priority = (osPriority_t) osPriorityLow,
|
|
.stack_size = 128 * 4
|
|
};
|
|
/* Definitions for qKeysPressed */
|
|
osMessageQueueId_t qKeysPressedHandle;
|
|
const osMessageQueueAttr_t qKeysPressed_attributes = {
|
|
.name = "qKeysPressed"
|
|
};
|
|
/* Definitions for qDisplay */
|
|
osMessageQueueId_t qDisplayHandle;
|
|
const osMessageQueueAttr_t qDisplay_attributes = {
|
|
.name = "qDisplay"
|
|
};
|
|
/* Definitions for timeCounter */
|
|
osTimerId_t timeCounterHandle;
|
|
const osTimerAttr_t timeCounter_attributes = {
|
|
.name = "timeCounter"
|
|
};
|
|
|
|
/* Private function prototypes -----------------------------------------------*/
|
|
/* USER CODE BEGIN FunctionPrototypes */
|
|
void update_ui(void); //update display main function
|
|
void draw_info_frame(void); //draw frame and pics for info frame
|
|
void draw_info(void); //update drying/keeping info
|
|
void draw_time_info(void); //update drying/keeping timers
|
|
void draw_header(void); //update display header lines
|
|
void draw_menu(void); //Draw program select menu
|
|
void draw_question(void); //Draw STOP? question
|
|
void draw_error(void); //Draw sensor error message
|
|
|
|
void setPWMs(void); //set Timer values according to requested settings
|
|
/* USER CODE END FunctionPrototypes */
|
|
|
|
void StartDefaultTask(void *argument);
|
|
void svc_display(void *argument);
|
|
void svc_sensors(void *argument);
|
|
void svc_keys(void *argument);
|
|
void clock_tick(void *argument);
|
|
|
|
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
|
|
|
|
/**
|
|
* @brief FreeRTOS initialization
|
|
* @param None
|
|
* @retval None
|
|
*/
|
|
void MX_FREERTOS_Init(void) {
|
|
/* USER CODE BEGIN Init */
|
|
|
|
/* USER CODE END Init */
|
|
|
|
/* USER CODE BEGIN RTOS_MUTEX */
|
|
/* add mutexes, ... */
|
|
/* USER CODE END RTOS_MUTEX */
|
|
|
|
/* USER CODE BEGIN RTOS_SEMAPHORES */
|
|
/* add semaphores, ... */
|
|
/* USER CODE END RTOS_SEMAPHORES */
|
|
|
|
/* Create the timer(s) */
|
|
/* creation of timeCounter */
|
|
timeCounterHandle = osTimerNew(clock_tick, osTimerPeriodic, NULL, &timeCounter_attributes);
|
|
|
|
/* USER CODE BEGIN RTOS_TIMERS */
|
|
/* start timers, add new ones, ... */
|
|
/* USER CODE END RTOS_TIMERS */
|
|
|
|
/* Create the queue(s) */
|
|
/* creation of qKeysPressed */
|
|
qKeysPressedHandle = osMessageQueueNew (16, sizeof(uint8_t), &qKeysPressed_attributes);
|
|
|
|
/* creation of qDisplay */
|
|
qDisplayHandle = osMessageQueueNew (16, sizeof(uint8_t), &qDisplay_attributes);
|
|
|
|
/* USER CODE BEGIN RTOS_QUEUES */
|
|
/* add queues, ... */
|
|
/* USER CODE END RTOS_QUEUES */
|
|
|
|
/* Create the thread(s) */
|
|
/* creation of defaultTask */
|
|
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
|
|
|
|
/* creation of svcDisplay */
|
|
svcDisplayHandle = osThreadNew(svc_display, NULL, &svcDisplay_attributes);
|
|
|
|
/* creation of svcSensors */
|
|
svcSensorsHandle = osThreadNew(svc_sensors, NULL, &svcSensors_attributes);
|
|
|
|
/* creation of svcKeys */
|
|
svcKeysHandle = osThreadNew(svc_keys, NULL, &svcKeys_attributes);
|
|
|
|
/* USER CODE BEGIN RTOS_THREADS */
|
|
/* add threads, ... */
|
|
/* USER CODE END RTOS_THREADS */
|
|
|
|
/* USER CODE BEGIN RTOS_EVENTS */
|
|
/* add events, ... */
|
|
/* USER CODE END RTOS_EVENTS */
|
|
|
|
}
|
|
|
|
/* USER CODE BEGIN Header_StartDefaultTask */
|
|
/**
|
|
* @brief Function implementing the defaultTask thread.
|
|
* @param argument: Not used
|
|
* @retval None
|
|
*/
|
|
/* USER CODE END Header_StartDefaultTask */
|
|
void StartDefaultTask(void *argument)
|
|
{
|
|
/* USER CODE BEGIN StartDefaultTask */
|
|
|
|
osTimerStart(timeCounterHandle, 1000);
|
|
|
|
/* Infinite loop */
|
|
uint8_t key; //pressed key id
|
|
for(;;)
|
|
{
|
|
//Check if there is a key pressed
|
|
if (osMessageQueueGetCount(qKeysPressedHandle) > 0) {
|
|
osMessageQueueGet(qKeysPressedHandle, &key, NULL, 50);
|
|
|
|
switch (key) {
|
|
case 0: // Down button
|
|
switch (mode) {
|
|
case 0:
|
|
if (prog < menuMax) prog++;
|
|
if (prog > menuFrame + 2) menuFrame = prog - 2;
|
|
break;
|
|
case 2:
|
|
case 4:
|
|
qConfirm = 0;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 1: // OK button
|
|
switch (mode) {
|
|
case 0: //Program has been chosen, lets start heating
|
|
time_cnt = menuProg[prog].drytime;
|
|
aimTemperature = menuProg[prog].drytemp;
|
|
pid.integrator = 0;
|
|
powerFan = 100;
|
|
mode = 1;
|
|
break;
|
|
case 1: //Ask for confirmation to stop drying
|
|
mode = 2;
|
|
qConfirm = 0;
|
|
break;
|
|
case 3: //Ask for confirmation to stop keeping
|
|
mode = 4;
|
|
qConfirm = 0;
|
|
break;
|
|
case 2: //resume heating or stop and go to idle mode
|
|
if (qConfirm) {
|
|
aimTemperature = 0;
|
|
mode = 0;
|
|
} else {
|
|
mode = 1;
|
|
}
|
|
break;
|
|
case 4: //resume keeping or stop and go to idle mode
|
|
if (qConfirm) {
|
|
aimTemperature = 0;
|
|
aimHumidity = 0;
|
|
mode = 0;
|
|
} else {
|
|
mode = 3;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 2: // Up button
|
|
switch (mode) {
|
|
case 0:
|
|
if (prog > 0) prog--;
|
|
if (prog < menuFrame) menuFrame = prog;
|
|
break;
|
|
case 2:
|
|
case 4:
|
|
qConfirm = 1;
|
|
break;
|
|
}
|
|
break;
|
|
} //switch key
|
|
update_ui();
|
|
} //key queue check
|
|
|
|
osDelay(1);
|
|
}
|
|
/* USER CODE END StartDefaultTask */
|
|
}
|
|
|
|
/* USER CODE BEGIN Header_svc_display */
|
|
/**
|
|
* @brief Function implementing the svcDisplay thread.
|
|
* @param argument: Not used
|
|
* @retval None
|
|
*/
|
|
/* USER CODE END Header_svc_display */
|
|
void svc_display(void *argument)
|
|
{
|
|
/* USER CODE BEGIN svc_display */
|
|
|
|
/* Infinite loop */
|
|
for(;;)
|
|
{
|
|
|
|
if (mode == 1 || mode == 3) {
|
|
draw_info();
|
|
}
|
|
|
|
osDelay(500);
|
|
}
|
|
/* USER CODE END svc_display */
|
|
}
|
|
|
|
/* USER CODE BEGIN Header_svc_sensors */
|
|
/**
|
|
* @brief Function implementing the svcSensors thread.
|
|
* @param argument: Not used
|
|
* @retval None
|
|
*/
|
|
/* USER CODE END Header_svc_sensors */
|
|
void svc_sensors(void *argument)
|
|
{
|
|
/* USER CODE BEGIN svc_sensors */
|
|
|
|
uint16_t tro = 0; //counter for thermal runout protection
|
|
|
|
aimTemperature = Temperature = Pressure = aimHumidity = Humidity = 0.0f;
|
|
PID_Init(&pid);
|
|
|
|
/* Infinite loop */
|
|
for(;;)
|
|
{
|
|
if (mode < 98) {
|
|
BME280_Measure();
|
|
if (Temperature == 0.0f && Humidity == 0.0f) {
|
|
//Some sensor malfunction
|
|
mode = 99;
|
|
aimTemperature = 0;
|
|
aimHumidity = 0;
|
|
powerFan = 100;
|
|
powerHeater = 0;
|
|
update_ui();
|
|
} else {
|
|
switch (mode) { //update fan/heater settings here.
|
|
case 0:
|
|
if (powerHeater > 0) powerHeater = 0;
|
|
if (Temperature >= 35) powerFan = 100;
|
|
else powerFan = 0;
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
powerHeater = PID_Update(&pid, aimTemperature, Temperature);
|
|
if (powerHeater > 0) powerFan = 100;
|
|
else powerFan = 85;
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
if (Humidity > 12) powerHeater = PID_Update(&pid, aimTemperature, Temperature);
|
|
else powerHeater = 0;
|
|
break;
|
|
} //switch (mode)
|
|
} //else
|
|
|
|
//Simple thermal runout protection
|
|
if (Temperature > aimTemperature && Temperature > 45) tro++;
|
|
else if (tro != 0) tro = 0;
|
|
if (tro > 480) { //480 - 2 minutes (120 sec * 4 as this task runs 4 times per second)
|
|
mode = 98;
|
|
aimTemperature = 0;
|
|
aimHumidity = 0;
|
|
powerFan = 100;
|
|
powerHeater = 0;
|
|
update_ui();
|
|
}
|
|
} //if (mode < 98)
|
|
|
|
setPWMs();
|
|
|
|
osDelay(250);
|
|
}
|
|
/* USER CODE END svc_sensors */
|
|
}
|
|
|
|
/* USER CODE BEGIN Header_svc_keys */
|
|
/**
|
|
* @brief Function implementing the svcKeys thread.
|
|
* @param argument: Not used
|
|
* @retval None
|
|
*/
|
|
/* USER CODE END Header_svc_keys */
|
|
void svc_keys(void *argument)
|
|
{
|
|
/* USER CODE BEGIN svc_keys */
|
|
uint8_t bBuff[3] = {0xFF}; // buttons are grounded when pressed
|
|
uint8_t bState[3] = {0xFF}; //last reported state. We need this to prevent repeated reports
|
|
uint8_t read, i;
|
|
|
|
/* Infinite loop */
|
|
for(;;)
|
|
{
|
|
read = KEY_DN_GPIO_Port->IDR & 0x00000007; //Read 3 bits of buttons port (PB0, PB1, PB2)
|
|
// check every button and shift its values to the array
|
|
for (i=0; i<3; i++) {
|
|
bBuff[i] = (bBuff[i]<<1) | ((read >> i) & 1); //Shift button buffer left and assign a button bit from the read data
|
|
if ( ((bBuff[i] & 0x0F) == 0b00000000) && (bState[i] == 1) ) { //if we read 0 last 4 times and last report was 1 then the button has been pressed
|
|
bState[i] = 0;
|
|
osMessageQueuePut(qKeysPressedHandle, &i, 0, 50); //Put the pressed button id to the key queue
|
|
}
|
|
if ( ((bBuff[i] & 0x0F) == 0b00001111) && (bState[i] == 0) ) { //if we read 1 last 4 times and last report was 0 then the button has been released
|
|
bState[i] = 1;
|
|
}
|
|
}
|
|
|
|
osDelay(4);
|
|
}
|
|
/* USER CODE END svc_keys */
|
|
}
|
|
|
|
/* clock_tick function */
|
|
void clock_tick(void *argument)
|
|
{
|
|
/* USER CODE BEGIN clock_tick */
|
|
|
|
//time counters here. Happens every second
|
|
switch (mode) {
|
|
case 1:
|
|
case 2:
|
|
time_cnt--;
|
|
if (time_cnt == 0) {
|
|
aimTemperature = 35;
|
|
pid.integrator = 0;
|
|
powerFan = 85;
|
|
mode = 3;
|
|
update_ui();
|
|
}
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
time_cnt++;
|
|
break;
|
|
}
|
|
draw_time_info();
|
|
|
|
|
|
/* USER CODE END clock_tick */
|
|
}
|
|
|
|
/* Private application code --------------------------------------------------*/
|
|
/* USER CODE BEGIN Application */
|
|
|
|
//update display main function
|
|
void update_ui(void) {
|
|
ssd1306_Fill(Black);
|
|
draw_header();
|
|
switch (mode) {
|
|
case 0:
|
|
draw_menu();
|
|
break;
|
|
case 1:
|
|
case 3:
|
|
draw_info_frame();
|
|
break;
|
|
case 2:
|
|
case 4:
|
|
draw_question();
|
|
break;
|
|
case 98:
|
|
case 99:
|
|
draw_error();
|
|
break;
|
|
} //switch (mode)
|
|
ssd1306_UpdateScreen();
|
|
}
|
|
|
|
//draw frame and pics for info frame
|
|
void draw_info_frame(void) {
|
|
ssd1306_DrawBitmap(4, 16, humidity16, 16, 16, White);
|
|
ssd1306_DrawBitmap(0, 32, temperature24, 24, 24, White);
|
|
ssd1306_DrawBitmap(26, 54, heat_icon, 18, 8, White);
|
|
ssd1306_DrawBitmap(80, 54, fan_icon, 16, 8, White);
|
|
}
|
|
|
|
//update drying/keeping info
|
|
void draw_info(void) {
|
|
char num[8];
|
|
|
|
//Humidity
|
|
ssd1306_DrawRectangle(26, 16, 61, 26, Black, 1);
|
|
sprintf(num, "%.1f%%", Humidity);
|
|
ssd1306_SetCursor(26, 16);
|
|
ssd1306_WriteString(num, Font_7x10, White);
|
|
|
|
//Temperature
|
|
ssd1306_DrawRectangle(26, 32, 122, 58, Black, 1);
|
|
sprintf(num, "%.1f*C", Temperature);
|
|
ssd1306_SetCursor(26, 32);
|
|
ssd1306_WriteString(num, Font_16x26, White);
|
|
|
|
//PWM Heater
|
|
ssd1306_DrawRectangle(47, 55, 65, 63, Black, 1);
|
|
sprintf(num, "%u", powerHeater);
|
|
ssd1306_SetCursor(47, 55);
|
|
ssd1306_WriteString(num, Font_6x8, White);
|
|
//PWM Fan
|
|
ssd1306_DrawRectangle(98, 55, 116, 63, Black, 1);
|
|
sprintf(num, "%u", powerFan);
|
|
ssd1306_SetCursor(98, 55);
|
|
ssd1306_WriteString(num, Font_6x8, White);
|
|
|
|
ssd1306_UpdateScreen();
|
|
}
|
|
|
|
//update drying/keeping timers
|
|
void draw_time_info(void) {
|
|
if (mode < 98) {
|
|
ssd1306_SetCursor(78, 8);
|
|
if (mode > 0) {
|
|
uint8_t hrs, mins, secs;
|
|
uint32_t rem;
|
|
hrs = time_cnt / 3600;
|
|
rem = time_cnt % 3600;
|
|
mins = rem / 60;
|
|
secs = rem % 60;
|
|
char ts[9];
|
|
sprintf(ts, "%02d:%02d:%02d", hrs, mins, secs);
|
|
ssd1306_DrawRectangle(78, 8, 128, 16, Black, 1);
|
|
ssd1306_WriteString(ts, Font_6x8, White);
|
|
} else {
|
|
ssd1306_WriteString("00:00:00", Font_6x8, White);
|
|
}
|
|
ssd1306_UpdateScreen();
|
|
}// if mode < 98
|
|
}
|
|
|
|
//update display header lines
|
|
void draw_header(void) {
|
|
char header[6], action[8];
|
|
|
|
switch (mode) {
|
|
case 0:
|
|
strcpy(header, "MENU");
|
|
strcpy(action, "Idle");
|
|
break;
|
|
case 1:
|
|
strcpy(header, menuProg[prog].text);
|
|
strcpy(action, "Drying");
|
|
break;
|
|
case 2:
|
|
strcpy(header, "STOP?");
|
|
strcpy(action, "Drying");
|
|
break;
|
|
case 3:
|
|
strcpy(header, "DONE!");
|
|
strcpy(action, "Keeping");
|
|
break;
|
|
case 4:
|
|
strcpy(header, "STOP?");
|
|
strcpy(action, "Keeping");
|
|
break;
|
|
case 98:
|
|
case 99:
|
|
strcpy(header, "ERROR");
|
|
break;
|
|
} //Switch (mode)
|
|
ssd1306_SetCursor(0, 0);
|
|
ssd1306_WriteString(header, Font_7x10, White);
|
|
|
|
if (mode < 98) {
|
|
ssd1306_SetCursor(78, 0);
|
|
ssd1306_WriteString(action, Font_6x8, White);
|
|
}// if mode < 98
|
|
|
|
}
|
|
|
|
//Draw program select menu
|
|
void draw_menu(void) {
|
|
char str[7];
|
|
for (uint8_t i=0; i<3; i++) {
|
|
if (prog == i + menuFrame) {
|
|
ssd1306_Line(0, i*15 + 16, 127, i*15 + 16, White);
|
|
ssd1306_Line(0, i*15 + 31, 127, i*15 + 31, White);
|
|
}
|
|
ssd1306_SetCursor(0, i*15 + 17);
|
|
ssd1306_WriteString(menuProg[i + menuFrame].text, Font_7x10, White);
|
|
ssd1306_SetCursor(78, i*15 + 16);
|
|
sprintf(str, "%u *c", menuProg[i + menuFrame].drytemp);
|
|
ssd1306_WriteString(str, Font_6x8, White);
|
|
ssd1306_SetCursor(78, i*15 + 24);
|
|
sprintf(str, "%luh%lum", menuProg[i + menuFrame].drytime/3600, menuProg[i + menuFrame].drytime%3600/60);
|
|
ssd1306_WriteString(str, Font_6x8, White);
|
|
} //for
|
|
}
|
|
|
|
//Draw STOP? question
|
|
void draw_question(void) {
|
|
ssd1306_DrawRoundRectangle(4, 20, 123, 60, White);
|
|
ssd1306_SetCursor(20, 33);
|
|
ssd1306_WriteString("YES NO", Font_7x10, White);
|
|
if (qConfirm) {
|
|
ssd1306_DrawRoundRectangle(17, 30, 56, 49, White);
|
|
} else {
|
|
ssd1306_DrawRoundRectangle(77, 30, 104, 49, White);
|
|
}
|
|
}
|
|
|
|
//Draw sensor error message
|
|
void draw_error(void) {
|
|
char str1[8], str2[7];
|
|
|
|
if (mode == 99) {
|
|
strcpy(str1, "SENSOR");
|
|
strcpy(str2, "FAIL");
|
|
}
|
|
if (mode == 98) {
|
|
strcpy(str1, "THERMAL");
|
|
strcpy(str2, "RUNOUT");
|
|
}
|
|
ssd1306_DrawBitmap(111, 0, error_icon, 14, 16, White);
|
|
ssd1306_SetCursor(26, 24);
|
|
ssd1306_WriteString(str1, Font_7x10, White);
|
|
ssd1306_SetCursor(38, 40);
|
|
ssd1306_WriteString(str2, Font_7x10, White);
|
|
}
|
|
|
|
//set Timer values according to requested settings
|
|
void setPWMs(void) {
|
|
TIM3->CCR1 = powerHeater;
|
|
TIM3->CCR2 = powerFan;
|
|
if (powerHeater < powerFan) {
|
|
TIM3->ARR = powerHeater - 10;
|
|
} else {
|
|
TIM3->ARR = powerFan - 10;
|
|
}
|
|
|
|
}
|
|
|
|
/* USER CODE END Application */
|
|
|