firmware-nucleo/App/Src/task_system.c (17421B)
1 /* 2 * Copyright (c) 2023 Juan Manuel Cruz <jcruz@fi.uba.ar> <jcruz@frba.utn.edu.ar>. 3 * Copyright (c) 2026 Martin Javier Klöckner <mklockner@fi.uba.ar> 4 * 5 * See file `LICENSE` for full details 6 */ 7 8 #include "main.h" 9 10 #include "logger.h" 11 #include "dwt.h" 12 13 #include "app.h" 14 #include "task_system_attribute.h" 15 #include "task_system_interface.h" 16 #include "task_leds_attribute.h" 17 #include "task_leds_interface.h" 18 #include "task_menu_attribute.h" 19 #include "task_menu_interface.h" 20 #include "task_buzzer_attribute.h" 21 #include "task_buzzer_interface.h" 22 23 #define G_TASK_SYS_CNT_INI 0ul 24 #define G_TASK_SYS_TICK_CNT_INI 0ul 25 #define G_TASK_SYS_EEPROM_START_ADDR 0x0801FC00 26 #define G_TASK_SYS_EEPROM_END_ADDR 0x0801FFF0 27 #define G_TASK_SYS_TIMER_UPDATE_RATE_MS 1000ul 28 29 #define DEL_SYS_MIN 0ul 30 #define DEL_SYS_MED 50ul 31 #define DEL_SYS_MAX 500ul 32 33 // If persistent values are lost these are the timer default values 34 #define TIMER_DEFAULT_START_HOUR 00ul 35 #define TIMER_DEFAULT_START_MIN 00ul 36 #define TIMER_DEFAULT_END_HOUR 12ul 37 #define TIMER_DEFAULT_END_MIN 00ul 38 #define TIMER_DEFAULT_STATUS false 39 40 // Exported variables 41 volatile uint32_t g_task_system_tick_cnt; // Updated every ms by SysTick callback 42 43 // Private variables 44 static task_system_dta_t task_system_dta = { 45 DEL_SYS_MIN, ST_SYS_IDLE, EV_SYS_IDLE, false 46 }; 47 48 #define SYSTEM_DTA_QTY (sizeof(task_system_dta)/sizeof(task_system_dta_t)) 49 50 static uint32_t g_task_system_timer_tick; 51 static task_system_timer_t task_system_timer = { 52 TIMER_DEFAULT_START_HOUR, TIMER_DEFAULT_START_MIN, 53 TIMER_DEFAULT_END_HOUR, TIMER_DEFAULT_END_MIN, 54 TIMER_DEFAULT_STATUS 55 }; 56 57 // Private function declaration 58 static void task_system_statechart(void *parameters); 59 static void task_system_temp_ctrl_enable(void *parameters); 60 static void task_system_timer_update(void *parameters); 61 static void task_system_rtc_init(void); 62 static void task_system_rtc_increment_hour(void); 63 static void task_system_rtc_increment_min(void); 64 static void task_system_eeprom_read_param(void *parameters); 65 static void task_system_eeprom_write_param(void *parameters); 66 static uint32_t task_system_eeprom_get_current_addr(void); 67 68 // Public function definition 69 void task_system_init(void *parameters) 70 { 71 task_system_dta_t *p_task_system_dta; 72 task_system_st_t state; 73 74 LOGGER_INFO("Initializing `task_system`..."); 75 76 g_task_system_tick_cnt = G_TASK_SYS_TICK_CNT_INI; 77 g_task_system_timer_tick = G_TASK_SYS_CNT_INI; 78 79 task_system_interface_init_event_queue(); 80 task_system_rtc_init(); 81 task_system_eeprom_read_param(parameters); 82 83 p_task_system_dta = &task_system_dta; 84 85 state = ST_SYS_IDLE; 86 p_task_system_dta->state = state; 87 88 shared_data_type *p_shared_data = (shared_data_type *)parameters; 89 p_shared_data->timer = &task_system_timer; 90 91 LOGGER_INFO("Done initializing `task_system`"); 92 } 93 94 void task_system_update(void *parameters) 95 { 96 bool b_time_update_required = false; 97 98 __asm("CPSID i"); 99 if (G_TASK_SYS_TICK_CNT_INI < g_task_system_tick_cnt) 100 { 101 g_task_system_tick_cnt--; 102 b_time_update_required = true; 103 } 104 __asm("CPSIE i"); 105 106 while (b_time_update_required) 107 { 108 if (0 == g_task_system_timer_tick) 109 { 110 g_task_system_timer_tick = G_TASK_SYS_TIMER_UPDATE_RATE_MS; 111 task_system_timer_update(parameters); 112 } 113 else 114 { 115 g_task_system_timer_tick--; 116 } 117 task_system_statechart(parameters); 118 119 __asm("CPSID i"); 120 if (G_TASK_SYS_TICK_CNT_INI < g_task_system_tick_cnt) 121 { 122 g_task_system_tick_cnt--; 123 b_time_update_required = true; 124 } 125 else 126 { 127 b_time_update_required = false; 128 } 129 __asm("CPSIE i"); 130 } 131 } 132 133 // Private function definition 134 static void task_system_rtc_init(void) 135 { 136 HAL_PWR_EnableBkUpAccess(); 137 138 // Set RTC Time only if RTC has been reset 139 if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) != RTC_BACKUP_MAGIC_NUMBER) 140 { 141 s_time.Hours = 16; 142 s_time.Minutes = 41; 143 s_time.Seconds = 20; 144 HAL_RTC_SetTime(&hrtc, &s_time, RTC_FORMAT_BIN); 145 146 s_date.WeekDay = RTC_WEEKDAY_FRIDAY; 147 s_date.Month = RTC_MONTH_MARCH; 148 s_date.Date = 20; 149 s_date.Year = 26; // 2026 150 HAL_RTC_SetDate(&hrtc, &s_date, RTC_FORMAT_BIN); 151 HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, RTC_BACKUP_MAGIC_NUMBER); 152 } 153 } 154 155 static void task_system_rtc_increment_hour() 156 { 157 HAL_RTC_GetTime(&hrtc, &s_time, RTC_FORMAT_BIN); 158 s_time.Hours = (s_time.Hours + 1) % 24; 159 HAL_RTC_SetTime(&hrtc, &s_time, RTC_FORMAT_BIN); 160 } 161 162 static void task_system_rtc_increment_min() 163 { 164 HAL_RTC_GetTime(&hrtc, &s_time, RTC_FORMAT_BIN); 165 s_time.Minutes = (s_time.Minutes + 1) % 60; 166 HAL_RTC_SetTime(&hrtc, &s_time, RTC_FORMAT_BIN); 167 } 168 169 static uint32_t task_system_eeprom_get_current_addr(void) 170 { 171 uint32_t addr = G_TASK_SYS_EEPROM_START_ADDR; 172 173 while (G_TASK_SYS_EEPROM_END_ADDR > addr) 174 { 175 if (0xFFFF == *(uint16_t*)addr) 176 { 177 // Empty value found 178 return (addr - sizeof(uint16_t)); 179 } 180 addr += sizeof(uint16_t); 181 } 182 183 // Page is full 184 return 0; 185 } 186 187 static void task_system_eeprom_read_param(void *parameters) 188 { 189 shared_data_type *p_shared_data = (shared_data_type *)parameters; 190 191 uint32_t addr = task_system_eeprom_get_current_addr(); 192 if (0 == addr) 193 { 194 addr = G_TASK_SYS_EEPROM_END_ADDR; 195 } 196 197 p_shared_data->temp_set_point = *(uint16_t*)addr; 198 } 199 200 // To reduce the wear of flash its better to write the new variables on the same 201 // page but on contiguous addresses every time the variables are updated, this 202 // also has the benefit that there is no need to erase the full page all the 203 // time but only once the page fills which after 512 writes (2 bytes on a page 204 // of 1024 bytes). 205 static void task_system_eeprom_write_param(void *parameters) 206 { 207 uint32_t new_addr, curr_addr; 208 shared_data_type *p_shared_data = (shared_data_type *)parameters; 209 210 curr_addr = task_system_eeprom_get_current_addr(); 211 212 if(p_shared_data->temp_set_point == *(uint16_t *)curr_addr) 213 { 214 // No need to update if the value is the same 215 return; 216 } 217 218 uint16_t data[] = { 219 p_shared_data->temp_set_point 220 }; 221 222 HAL_FLASH_Unlock(); 223 224 if (0 == curr_addr) 225 { 226 // Memory page is full 227 LOGGER_INFO("Memory page is full. Erasing full page..."); 228 229 FLASH_EraseInitTypeDef erase = {0}; 230 uint32_t sector_error; 231 232 erase.TypeErase = FLASH_TYPEERASE_PAGES; 233 erase.PageAddress = G_TASK_SYS_EEPROM_START_ADDR; 234 erase.NbPages = 1; // 1024 bytes 235 236 if(HAL_FLASHEx_Erase(&erase, §or_error) != HAL_OK) 237 { 238 HAL_FLASH_Lock(); 239 LOGGER_ERROR("Could erase flash at address %d", 240 G_TASK_SYS_EEPROM_START_ADDR); 241 return; 242 } 243 244 new_addr = G_TASK_SYS_EEPROM_START_ADDR; 245 } 246 else 247 { 248 new_addr = (curr_addr + sizeof(uint16_t)); 249 } 250 251 HAL_FLASH_Program(TYPEPROGRAM_HALFWORD, new_addr, *(uint16_t *)data); 252 HAL_FLASH_Lock(); 253 return; 254 } 255 256 static void task_system_temp_ctrl_enable(void *parameters) 257 { 258 shared_data_type *p_shared_data = (shared_data_type *)parameters; 259 260 p_shared_data->temp_ctrl_enabled = !p_shared_data->temp_ctrl_enabled; 261 task_menu_interface_put_event(EV_MEN_DISPLAY_UPDATE); 262 task_buzzer_interface_put_event(EV_BUZ_XX_LONG_BEEP, ID_BUZ_A); 263 264 if (true == p_shared_data->temp_ctrl_enabled) 265 { 266 task_leds_interface_put_event(EV_LED_XX_BLINK, ID_LED_A); 267 task_leds_interface_put_event(EV_LED_XX_BLINK, ID_LED_B); 268 } 269 else 270 { 271 task_leds_interface_put_event(EV_LED_XX_OFF, ID_LED_A); 272 task_leds_interface_put_event(EV_LED_XX_OFF, ID_LED_B); 273 } 274 } 275 276 static void task_system_statechart(void *parameters) 277 { 278 task_system_dta_t *p_task_system_dta = &task_system_dta; 279 shared_data_type *p_shared_data = (shared_data_type *)parameters; 280 281 if (true == task_system_interface_any_event()) 282 { 283 p_task_system_dta->event_flag = true; 284 p_task_system_dta->event = task_system_interface_get_event(); 285 286 // Events which are common to every state 287 switch (p_task_system_dta->event) 288 { 289 290 case EV_SYS_REM_TIMER_TOGGLE: 291 p_task_system_dta->event_flag = false; 292 p_shared_data->timer->enabled = !p_shared_data->timer->enabled; 293 task_menu_interface_put_event(EV_MEN_DISPLAY_UPDATE); 294 break; 295 296 case EV_SYS_REM_TEMP_CTRL_ENABLED: 297 p_task_system_dta->event_flag = false; 298 task_system_temp_ctrl_enable(parameters); 299 task_menu_interface_put_event(EV_MEN_DISPLAY_UPDATE); 300 break; 301 302 case EV_SYS_REM_TEMP_SET: 303 p_task_system_dta->event_flag = false; 304 task_system_eeprom_write_param(parameters); 305 task_menu_interface_put_event(EV_MEN_DISPLAY_UPDATE); 306 break; 307 308 default: 309 break; 310 } 311 } 312 313 switch (p_task_system_dta->state) 314 { 315 case ST_SYS_IDLE: 316 if (true == p_task_system_dta->event_flag) 317 { 318 p_task_system_dta->event_flag = false; 319 switch (p_task_system_dta->event) 320 { 321 case EV_SYS_BTN_B_ACTIVE: 322 break; 323 324 case EV_SYS_BTN_C_ACTIVE: 325 task_system_temp_ctrl_enable(parameters); 326 break; 327 328 case EV_SYS_BTN_A_ACTIVE: 329 case EV_SYS_BTN_D_ACTIVE: 330 p_task_system_dta->state = ST_SYS_CLOCK_SET; 331 task_menu_interface_put_event(EV_MEN_ST_CLOCK_SET); 332 break; 333 334 default: break; 335 } 336 } 337 break; 338 339 case ST_SYS_CLOCK_SET: 340 if (true == p_task_system_dta->event_flag) 341 { 342 p_task_system_dta->event_flag = false; 343 switch (p_task_system_dta->event) 344 { 345 case EV_SYS_BTN_A_ACTIVE: 346 case EV_SYS_BTN_D_ACTIVE: 347 p_task_system_dta->state = ST_SYS_TEMP_SET; 348 task_menu_interface_put_event(EV_MEN_ST_TEMP_SET); 349 break; 350 case EV_SYS_BTN_B_ACTIVE: 351 task_system_rtc_increment_hour(); 352 task_menu_interface_put_event(EV_MEN_DISPLAY_UPDATE); 353 break; 354 case EV_SYS_BTN_C_ACTIVE: 355 task_system_rtc_increment_min(); 356 task_menu_interface_put_event(EV_MEN_DISPLAY_UPDATE); 357 break; 358 default: break; 359 } 360 } 361 break; 362 363 case ST_SYS_TEMP_SET: 364 if (true == p_task_system_dta->event_flag) 365 { 366 p_task_system_dta->event_flag = false; 367 switch (p_task_system_dta->event) 368 { 369 case EV_SYS_BTN_A_ACTIVE: 370 case EV_SYS_BTN_D_ACTIVE: 371 p_task_system_dta->state = ST_SYS_TIMER_SET_STATUS; 372 task_menu_interface_put_event(EV_MEN_ST_TIMER_SET_STATUS); 373 task_system_eeprom_write_param(parameters); 374 break; 375 376 case EV_SYS_BTN_B_ACTIVE: 377 p_shared_data->temp_set_point = 378 ((p_shared_data->temp_set_point - 5) % 85); 379 task_menu_interface_put_event(EV_MEN_DISPLAY_UPDATE); 380 break; 381 382 case EV_SYS_BTN_C_ACTIVE: 383 p_shared_data->temp_set_point = 384 ((p_shared_data->temp_set_point + 5) % 85); 385 386 task_menu_interface_put_event(EV_MEN_DISPLAY_UPDATE); 387 break; 388 389 default: break; 390 } 391 } 392 break; 393 394 case ST_SYS_TIMER_SET_STATUS: 395 if (true == p_task_system_dta->event_flag) 396 { 397 p_task_system_dta->event_flag = false; 398 switch (p_task_system_dta->event) 399 { 400 401 case EV_SYS_BTN_C_ACTIVE: 402 p_shared_data->timer->enabled = !p_shared_data->timer->enabled; 403 task_menu_interface_put_event(EV_MEN_DISPLAY_UPDATE); 404 break; 405 406 case EV_SYS_BTN_A_ACTIVE: 407 case EV_SYS_BTN_B_ACTIVE: 408 p_task_system_dta->state = ST_SYS_IDLE; 409 task_menu_interface_put_event(EV_MEN_ST_IDLE); 410 break; 411 412 case EV_SYS_BTN_D_ACTIVE: 413 p_task_system_dta->state = ST_SYS_TIMER_SET_START; 414 task_menu_interface_put_event(EV_MEN_ST_TIMER_SET_START); 415 break; 416 417 default: break; 418 } 419 } 420 break; 421 422 case ST_SYS_TIMER_SET_START: 423 if (true == p_task_system_dta->event_flag) 424 { 425 p_task_system_dta->event_flag = false; 426 switch (p_task_system_dta->event) 427 { 428 case EV_SYS_BTN_B_ACTIVE: 429 p_shared_data->timer->start_hour = 430 ((p_shared_data->timer->start_hour + 1) % 24); 431 task_menu_interface_put_event(EV_MEN_DISPLAY_UPDATE); 432 break; 433 434 case EV_SYS_BTN_C_ACTIVE: 435 p_shared_data->timer->start_min = 436 ((p_shared_data->timer->start_min + 1) % 60); 437 task_menu_interface_put_event(EV_MEN_DISPLAY_UPDATE); 438 break; 439 440 case EV_SYS_BTN_A_ACTIVE: 441 case EV_SYS_BTN_D_ACTIVE: 442 p_task_system_dta->state = ST_SYS_TIMER_SET_END; 443 task_menu_interface_put_event(EV_MEN_ST_TIMER_SET_END); 444 break; 445 446 default: break; 447 } 448 } 449 break; 450 451 case ST_SYS_TIMER_SET_END: 452 if (true == p_task_system_dta->event_flag) 453 { 454 p_task_system_dta->event_flag = false; 455 switch (p_task_system_dta->event) 456 { 457 case EV_SYS_BTN_B_ACTIVE: 458 p_shared_data->timer->end_hour = 459 ((p_shared_data->timer->end_hour + 1) % 24); 460 task_menu_interface_put_event(EV_MEN_DISPLAY_UPDATE); 461 break; 462 463 case EV_SYS_BTN_C_ACTIVE: 464 p_shared_data->timer->end_min = 465 ((p_shared_data->timer->end_min + 1) % 60); 466 task_menu_interface_put_event(EV_MEN_DISPLAY_UPDATE); 467 break; 468 469 case EV_SYS_BTN_A_ACTIVE: 470 case EV_SYS_BTN_D_ACTIVE: 471 p_task_system_dta->state = ST_SYS_IDLE; 472 task_menu_interface_put_event(EV_MEN_ST_IDLE); 473 break; 474 475 default: break; 476 } 477 } 478 break; 479 480 default: 481 p_task_system_dta->state = ST_SYS_IDLE; 482 p_task_system_dta->event_flag = false; 483 p_shared_data->temp_ctrl_enabled = false; 484 task_menu_interface_put_event(EV_MEN_ST_IDLE); 485 task_leds_interface_put_event(EV_LED_XX_OFF, ID_LED_A); 486 task_leds_interface_put_event(EV_LED_XX_OFF, ID_LED_B); 487 break; 488 } 489 } 490 491 // TODO: Only check timer conditions every second (not on every tick) 492 static void task_system_timer_update(void *parameters) 493 { 494 shared_data_type *p_shared_data = (shared_data_type *)parameters; 495 uint16_t start_24, end_24, clock_24; 496 bool new_temp_ctrl_state; 497 498 if (false == p_shared_data->timer->enabled) 499 { 500 return; 501 } 502 503 HAL_RTC_GetTime(&hrtc, &s_time, RTC_FORMAT_BIN); 504 505 clock_24 = (s_time.Hours*100) + s_time.Minutes; 506 start_24 = (p_shared_data->timer->start_hour*100) + 507 p_shared_data->timer->start_min; 508 end_24 = (p_shared_data->timer->end_hour*100) + 509 p_shared_data->timer->end_min; 510 511 new_temp_ctrl_state = ((end_24 > start_24) 512 ? ((clock_24 >= start_24) && (clock_24 < end_24)) 513 : ((clock_24 >= start_24) || (clock_24 < end_24))); 514 515 if (new_temp_ctrl_state != p_shared_data->temp_ctrl_enabled) 516 { 517 p_shared_data->temp_ctrl_enabled = new_temp_ctrl_state; 518 519 task_menu_interface_put_event(EV_MEN_DISPLAY_UPDATE); 520 task_buzzer_interface_put_event(EV_BUZ_XX_LONG_BEEP, ID_BUZ_A); 521 522 if (true == p_shared_data->temp_ctrl_enabled) 523 { 524 task_leds_interface_put_event(EV_LED_XX_BLINK, ID_LED_A); 525 task_leds_interface_put_event(EV_LED_XX_BLINK, ID_LED_B); 526 } 527 else 528 { 529 task_leds_interface_put_event(EV_LED_XX_OFF, ID_LED_A); 530 task_leds_interface_put_event(EV_LED_XX_OFF, ID_LED_B); 531 } 532 } 533 }
