diff options
Diffstat (limited to 'FanController/fan_controller/main.c')
| -rw-r--r-- | FanController/fan_controller/main.c | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/FanController/fan_controller/main.c b/FanController/fan_controller/main.c new file mode 100644 index 0000000..1d19237 --- /dev/null +++ b/FanController/fan_controller/main.c @@ -0,0 +1,381 @@ +/* Autor: Ján Sučan <jan@jansucan.sk> + * + * Zdrojove kody, ich casti a subory z nich vzniknute priamo alebo nepriamo + * (objektove subory, Intel Hex, ...) prosim nepouzivajte komercne, ani ako + * sucast komercnych diel. Vsetky ostatne pripady pouzitia su dovolene. + * + * Please don't use the source codes, their parts and files created from them + * directly or indirectly, (object files, Intel Hex files, ...) for commercial + * purposes, not even as a part of commercial products. All other use cases + * are allowed. + */ + +/* Verzia 2: Aktualna verzia programu. + * - Opravene nastavovanie otacok ventilatora. + * Povolene nested prerusenia. V obsluhe prerusenia od AD prevodnika + * povolene prerusenia globalne, aby sa obsluha prerusenia AD prevodnika + * mohla prerusit obsluhou casovaca pre generovanie PWM pre ovladanie + * otacok ventilatora. + * + * Verzia 1: + * - Uvodne vydanie programu. + */ + +#include <avr/io.h> +#include <avr/interrupt.h> +#include <stdint.h> +#include <stdbool.h> + +#include <fan_controller/delay.h> +#include <fan_controller/hw/led.h> +#include <fan_controller/hw/ad_converter.h> +#include <fan_controller/hw/fan.h> +#include <fan_controller/hw/io.h> +#include <fan_controller/hw/button.h> +#include <fan_controller/config.h> +#include <fan_controller/error.h> + +/** Maska pre zasvietenie zelenych LED, pri mode konstantnych otacok (Override mode). */ +#define RPM_OVERRIDE_LED_MASK 0xAA + +/** Otacky pri mode konstantnych otacok (Override mode). */ +#define RPM_OVERRIDE_VALUE UINT8_MAX + +/** Pocet milisekund po ktore sa bude indikovat predchadzajuci nastaveny typ zobrazenia pri indikacii prepnutia zobrazenia. */ +#define SHOW_SELECTED_DISP_TYPE_PREV_DELAY 400U // ms + +/** Pocet milisekund po ktore sa bude indikovat aktualne nastaveny typ zobrazenia pri indikacii prepnutia zobrazenia. */ +#define SHOW_SELECTED_DISP_TYPE_CURRENT_DELAY 1200U // ms + +static void display_set(bool display_type_changed); +static void display_real(bool display_type_changed, uint8_t rpm); +static void display_with_off(bool display_type_changed, uint8_t rpm); +static void rpm_override(bool display_type_changed); +static uint8_t show_get_display_type_mask(int display_type); +static void show_selected_display_type(int prev_display_type, int display_type); + +/** Stavy stavoveho automatu pre obsluhu tlacitka. */ +enum button_states { + BUTTON_WAITING_FOR_RELEASE, /**< Cakanie na pustenie tlacitka. */ + BUTTON_WAITING_FOR_PRESS, /**< Cakanie na stlacenie tlacitka. */ + BUTTON_PRESSED, /**< Tlacitko stlacene. */ + BUTTON_SHORT_PRESS, /**< Detekovane kratke stlacenie tlacitka. */ + BUTTON_LONG_PRESS /**< Detekovane dhle stlacenie tlacitka. */ +}; + +/** + * @brief Hlavna funkcia programu s hlavnou sluckou. + */ +int +main(void) +{ + // Globalne povolit prerusenia + sei(); + + io_init(); + delay_init(); + led_init(); + fan_init(); + // Ovladanie potenciometrom musi fungovat az po pripadnej kalibracii, + // aby neboli narusene otacky ventilatora + adc_init(); + + // Ziska sa ulozeny typ zobrazenia, ak existuje, inak sa vyberie predvoleny + int prev_display_type = DISPLAY_TYPE_NONE; + int display_type = config_disp_type_load(); + // Zobrazi sa prepnutie na zvoleny typ zobrazenia + show_selected_display_type(prev_display_type, display_type); + + // Zo zaciatku sa bude cakat na pustenie tlacitka + int button_state = BUTTON_WAITING_FOR_RELEASE; + // Pre ulozenie deadline pre meranie dlzky stlacenia tlacitka + uint32_t button_long_press_time = 0; + // Pre ulozenie zvoleneho casu ktory bol predbehnuty nastavenim konstantnej hodnoty otacok dlhym stlacenim tlacitka + int display_type_overriden = DISPLAY_TYPE_NONE; + + // Hlavna slucka + while (1) { + // Ziskat otacky pre detekciu zastavenia ventilatora (chyby) a pre pripadne zobrazenie otacok na LED + const uint8_t rpm = fan_rpm_get(); + + // Zistit, ci doslo k zmene typu zobrazenia + const bool display_type_changed = (display_type != prev_display_type); + + // Nastavit a zobrazit prislusne otacky + switch(display_type) { + case DISPLAY_TYPE_SET: + // Zobrazenie hodnoty nastavenej na potenciometri + display_set(display_type_changed); + break; + + case DISPLAY_TYPE_REAL: + // Zobrazenie realnych otacok podla RPM signalu z ventilatora + display_real(display_type_changed, rpm); + break; + + case DISPLAY_TYPE_SET_WITH_OFF: + // Zobrazenie hodnoty nastavenej na potenciometri, s automatickym zhasnutim LED + display_with_off(display_type_changed, adc_get_converted_value()); + break; + + case DISPLAY_TYPE_REAL_WITH_OFF: + // Zobrazenie realnych otacok podla RPM signalu z ventilatora, s automatickym zhasnutim LED + display_with_off(display_type_changed, rpm); + break; + + case DISPLAY_TYPE_OVERRIDE: + rpm_override(display_type_changed); + break; + + default: + // Neznama hodnota typu zobrazenia + error(); + break; + } + + // Predchadzajuci typ zobrazenie pre pouzitie v dalsej iteracii hlavnej slucky + prev_display_type = display_type; + + // Obsluzi sa tlacitko + switch(button_state) { + case BUTTON_WAITING_FOR_RELEASE: + if (button_is_released()) { + // Tlaticko bolo pustene + button_state = BUTTON_WAITING_FOR_PRESS; + } + break; + + case BUTTON_WAITING_FOR_PRESS: + if (button_is_pressed()) { + // Tlacitko bolo stlacene + button_state = BUTTON_PRESSED; + // A bude sa odmeriavat cas pre rozlisenie dlheho stlacenia + button_long_press_time = delay_get_deadline_ms(1500); + } + break; + + case BUTTON_PRESSED: + if (delay_has_deadline_expired(button_long_press_time)) { + // Bol prekroceny deadline pre dlhe stlacenie + button_state = BUTTON_LONG_PRESS; + } else if (button_is_released()) { + // Tlacitko bolo pustene pred uplynutim deadlinu pre dlhe stlacenie, je to kratke stlacenie + button_state = BUTTON_SHORT_PRESS; + } + break; + + case BUTTON_SHORT_PRESS: + if (display_type == DISPLAY_TYPE_OVERRIDE) { + // Kratke stlacenie tlacitka, prepne sa naspat na normalny mod aktivny pred nastavenim konstantnej hodnoty otacok (Override mod) + // Vynuti sa inicializacia noveho zobrazenia tym, ze predchadzjuci typ bude odlisny od vsetkych moznych typov zobrazenia + prev_display_type = DISPLAY_TYPE_NONE; + // Obnovi sa normalny mod zobrazenia aktivny pred Override modom + display_type = display_type_overriden; + // Indikuje sa novo zvoleny mod + show_selected_display_type(prev_display_type, display_type); + } else { + // Kratke stlacenie, prepne sa na dalsi typ + prev_display_type = display_type; + if (++display_type >= DISPLAY_TYPE_COUNT) { + display_type = DISPLAY_TYPE_FIRST; + } + // Ulozit nastaveny typ zobrazenia + config_disp_type_save(display_type); + // Indikacia zvoleneho typu zobrazenia + show_selected_display_type(prev_display_type, display_type); + } + // Bude sa cakat na dalsie stlacenie + button_state = BUTTON_WAITING_FOR_PRESS; + break; + + case BUTTON_LONG_PRESS: + // Dlhe stlacenie, override otacok + // Zalohuje sa aktualne zvoleny typ zobrazenia pre obnovu pri ukonceni nastavenia konstantnej hodnoty otacok + display_type_overriden = display_type; + display_type = DISPLAY_TYPE_OVERRIDE; + // Pre dalsiu reakciu na tlacitko sa musi najprv pustit + button_state = BUTTON_WAITING_FOR_RELEASE; + break; + + default: + // Neznamy stav tlacitka + error(); + break; + } + + } +} + +/** + * @brief Zobrazenie otacok nastavenych na potenciometri. + * + * @param display_type_changed Ci bolo prepnute na novy typ zobrazenia (na tento typ). + */ +void +display_set(bool display_type_changed) +{ + if (display_type_changed) { + // Zmeneny typ zobrazenia, inicializuje sa nove zobrazenie + adc_override_value_delete(); + led_min_green_on(); + } + led_green_bar_show_by_value(adc_get_converted_value()); +} + +/** Stavy statoveho automatu pre automaticke vypinanie LED. */ +enum off_states { + OFF_SHOW, /**< Zapnute zobrazenie. */ + OFF_SLEEP, /**< Vypnutie zobrazenie, stlmenie jasu LED. */ + OFF_WAITING_FOR_CHANGE_OR_DEADLINE, /**< Cakanie na vyprsanie casu pre vypnutie alebo na vyznamnu zmenu otacok. */ + OFF_WAITING_FOR_CHANGE /**< Cakanie na vyznamnu zmenu otacok. */ +}; + +/** + * @brief Zobrazenie otacok nastavenych na potenciometri, alebo realnych otacok, s automatickym vypinanim LED. + * + * @param display_type_changed Ci bolo prepnute na novy typ zobrazenia (na tento typ). + * @param rpm Otacky nastavene na potenciometri, alebo realne otacky ventilatora. + */ +void +display_with_off(bool display_type_changed, uint8_t rpm) +{ + // Pre ulozenie deadlinu pre vypnutie/stlmenie zobrazenia + static uint32_t off_time; + // Po prepnuti na typ zobrazenia s automatickym vypinanim bude zobrazenie zapnute + static int off_state = OFF_SHOW; + + if (display_type_changed) { + // Zmeneny typ zobrazenia, inicializuje sa nove zobrazenie + adc_override_value_delete(); + off_state = OFF_SHOW; + } + + while (1) { + switch (off_state) { + case OFF_SHOW: + led_min_green_on(); + led_green_bar_show_by_value(rpm); + off_time = delay_get_deadline_ms(4000); + off_state = OFF_WAITING_FOR_CHANGE_OR_DEADLINE; + break; + + case OFF_SLEEP: + led_min_green_set(90); + led_green_bar_show_by_mask(0); + off_state = OFF_WAITING_FOR_CHANGE; + break; + + case OFF_WAITING_FOR_CHANGE_OR_DEADLINE: + if (delay_has_deadline_expired(off_time)) { + off_state = OFF_SLEEP; + } else if (led_green_bar_will_be_changed_by_value(rpm)) { + off_state = OFF_SHOW; + } + break; + + case OFF_WAITING_FOR_CHANGE: + if (led_green_bar_will_be_changed_by_value(rpm)) { + off_state = OFF_SHOW; + } + break; + + default: + // Neznamy stav + error(); + break; + } + + // Koncove stavy, aby sa spracovavanie programu vratilo do hlavnej slucky, su stavy cakania + if ((off_state == OFF_WAITING_FOR_CHANGE_OR_DEADLINE) || (off_state == OFF_WAITING_FOR_CHANGE)) { + break; + } + } +} + +/** + * @brief Zobrazenie realnych otacok. + * + * @param display_type_changed Ci bolo prepnute na novy typ zobrazenia (na tento typ). + * @param rpm Realne otacky ventilatora. + */ +void +display_real(bool display_type_changed, uint8_t rpm) +{ + if (display_type_changed) { + // Zmeneny typ zobrazenia, inicializuje sa nove zobrazenie + adc_override_value_delete(); + led_min_green_on(); + } + led_green_bar_show_by_value(rpm); +} + +/** + * @brief Zobrazenie a nastavenie konstantnej hodnoty otacok ventilatora (Override mod). + * + * @param display_type_changed Ci bolo prepnute na novy typ zobrazenia (na tento typ). + */ +void +rpm_override(bool display_type_changed) +{ + if (display_type_changed) { + // Zmeneny typ zobrazenia, inicializuje sa nove zobrazenie + adc_override_value_set(RPM_OVERRIDE_VALUE); + led_min_green_on(); + led_green_bar_show_by_mask(RPM_OVERRIDE_LED_MASK); + } +} + +/** + * @brief Ziskanie masky pre zasvietenie LEDm pre indikaciu typu/modu zobrazenia. + * + * Tato funkcia z cisla tyu zobrazenia vyrobi masku urcujucu, ako sa bude mod zobrazovat + * na zelenych LED pouzivatelovi. + * + * @param display_type Typ zobrazenia. + * @return Maska pre rozsvietenie LED pre indikaciu typu zobrazenia @p display_type. + */ +uint8_t +show_get_display_type_mask(int display_type) +{ + if (display_type >= DISPLAY_TYPE_COUNT) { + // Neznamy typ zobrazenia + error(); + } + + // Cislo modu sa prevedie na masku pre jednu rozsvietenu LED sprava + // Mod cislo 0 rozsvieti zelenu LED najviac napravo + // Mod cislo 1 rozsvieti druhu LED sprava, atd. + uint8_t mask = 0x80; + while (display_type-- > 0) { + mask >>= 1; + } + + return mask; +} + +/** + * @brief Zobrazenie prepnutia typu zobrazenia otacok. + * + * Najprv na zobrazi aktualne nastaveny typ a potom sa zobrazi novo nastaveny typ. + * + * @param prev_display_type Predchadzajuci typ zobrazenia. + * @param display_type Aktualny typ zobrazenia. + */ +void +show_selected_display_type(int prev_display_type, int display_type) +{ + led_min_green_off(); + led_red_on(); + + // Zobrazit predchadzajuce zvolene zobrazenie, ak nejake bolo + if (prev_display_type < DISPLAY_TYPE_COUNT) { + led_green_bar_show_by_mask(show_get_display_type_mask(prev_display_type)); + delay_ms(SHOW_SELECTED_DISP_TYPE_PREV_DELAY); + } + // Zobrazit novo zvolene zobrazenie + led_green_bar_show_by_mask(show_get_display_type_mask(display_type)); + delay_ms(SHOW_SELECTED_DISP_TYPE_CURRENT_DELAY); + + led_green_bar_show_by_mask(0x00); + led_red_off(); +}
\ No newline at end of file |
