diff options
Diffstat (limited to 'FanController/fan_controller/hw')
| -rw-r--r-- | FanController/fan_controller/hw/ad_converter.c | 130 | ||||
| -rw-r--r-- | FanController/fan_controller/hw/ad_converter.h | 23 | ||||
| -rw-r--r-- | FanController/fan_controller/hw/button.c | 48 | ||||
| -rw-r--r-- | FanController/fan_controller/hw/button.h | 23 | ||||
| -rw-r--r-- | FanController/fan_controller/hw/eeprom.c | 120 | ||||
| -rw-r--r-- | FanController/fan_controller/hw/eeprom.h | 21 | ||||
| -rw-r--r-- | FanController/fan_controller/hw/fan.c | 338 | ||||
| -rw-r--r-- | FanController/fan_controller/hw/fan.h | 22 | ||||
| -rw-r--r-- | FanController/fan_controller/hw/io.c | 428 | ||||
| -rw-r--r-- | FanController/fan_controller/hw/io.h | 46 | ||||
| -rw-r--r-- | FanController/fan_controller/hw/led.c | 184 | ||||
| -rw-r--r-- | FanController/fan_controller/hw/led.h | 32 |
12 files changed, 1415 insertions, 0 deletions
diff --git a/FanController/fan_controller/hw/ad_converter.c b/FanController/fan_controller/hw/ad_converter.c new file mode 100644 index 0000000..1285826 --- /dev/null +++ b/FanController/fan_controller/hw/ad_converter.c @@ -0,0 +1,130 @@ +/* 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. + */ + +#include <avr/io.h> +#include <avr/interrupt.h> +#include <stdbool.h> + +#include <fan_controller/hw/ad_converter.h> +#include <fan_controller/median.h> +#include <fan_controller/hw/fan.h> + +/** Pocet naposledy nameranych vzoriek z ktorych sa bude brat median. */ +#define ADC_SAMPLE_COUNT 5U + +/** Buffer pre median nameranych vzoriek. */ +static median_buffer_t adc_median_buffer; + +/** Naposledy ziskany median z nameranych vzoriek. */ +static uint8_t adc_converted_value; + +/** Ci sa ma nastavit konstantna hodnota pre otacky, namiesto nastavenia podla hodnoty z potenciometra. */ +static bool adc_is_value_overriden = false; + +/** Konstanta hodnota na ktoru sa nastavia otacky ventilatora pri aktivovani pouzitia pevnej hodnoty pre otacky. */ +static uint8_t adc_override_value; + +/** + * @brief Obsluha prerusenia od AD prevodnika pri dokonceni prevodu. + * + * Namerana hodnota sa ulozi medzi vzorky pre median, ziska sa median + * a bud sa otacky nastavia podla ziskaneho medianu, alebo ked je + * aktivovane pouzitie konstantnej hodnoty, bude sa napatie z potenciometra + * ignorovat a hodnota sa bude nastavovat na zvolenu konstantu. + */ +ISR(ADC_vect) +{ + // Povolenie preruseni s vyssou prioritou (pretecenie PWM casovaca pre ovladanie otacok ventilatora) + sei(); + + median_save_sample(&adc_median_buffer, ADCH); + adc_converted_value = median_get(&adc_median_buffer); + + if (adc_is_value_overriden) { + fan_rpm_set(adc_override_value); + } else { + fan_rpm_set(adc_converted_value); + } +} + +/** + * @brief Inicializacia AD prevodnika. + * + * Spusti sa AD prevodnik, bude stale konvertovat napatie + * z potenciometra (free-running mod) a povoli sa prerusenie + * pri dokonceni prevodu. + */ +void +adc_init(void) +{ + // Kanal ADC0 + // Napatova referencia na pine AREF MCU + // Zarovnanie vysledku dolava + ADMUX = (1 << ADLAR); + + // ADC enable + // Najnizsia ADC frekvencia, preddelicka 128 + // Free-runnung mod predvolene v SFIOR + // Povolenie prerusenia + ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0) | (1 << ADIE) | (1 << ADATE); + + median_init_buffer(&adc_median_buffer, ADC_SAMPLE_COUNT); + for (uint8_t i = 0; i < ADC_SAMPLE_COUNT; ++i) { + median_save_sample(&adc_median_buffer, 0); + } + + // Spustenie A/D prevodu + ADCSRA |= 1 << ADSC; +} + +/** + * @brief Ziskanie hodnoty z AD prevodnika spocitanej ako median niekolkych predchadzajucich merani. + * + * @param Hodnota namerana AD prevodnikom. + */ +uint8_t +adc_get_converted_value(void) +{ + uint8_t v; + + // Zakaze sa prerusenie aby k premennej nepristupovala zaroven aj obsluha prerusenia + ADCSRA &= ~(1 << ADIE); + v = adc_converted_value; + // Znovu sa prerusenie povoli + ADCSRA |= (1 << ADIE); + return v; +} + +/** + * @brief Nastavenie konstantej hodnoty pre otacky ventilatora. + * + * Pri nastaveni konstantnej hodnoty sa bude ignorovat hodnota z potenciometra. + * + * @param val Konstantna hodnota otacok ventilatora. + */ +void +adc_override_value_set(uint8_t val) +{ + adc_override_value = val; + adc_is_value_overriden = true; +} + +/** + * @brief Deaktivacia konstantnej hodnoty pre nastavenie otacok ventilatora. + * + * Otacky budu zase nastavovane podla napatia z potenciometra. + */ +void +adc_override_value_delete(void) +{ + adc_is_value_overriden = false; +} diff --git a/FanController/fan_controller/hw/ad_converter.h b/FanController/fan_controller/hw/ad_converter.h new file mode 100644 index 0000000..11b04c9 --- /dev/null +++ b/FanController/fan_controller/hw/ad_converter.h @@ -0,0 +1,23 @@ +/* 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. + */ + +#ifndef AD_CONVERTER_H_ +#define AD_CONVERTER_H_ + +#include <stdint.h> + +void adc_init(void); +uint8_t adc_get_converted_value(void); +void adc_override_value_set(uint8_t val); +void adc_override_value_delete(void); + +#endif /* AD_CONVERTER_H_ */
\ No newline at end of file diff --git a/FanController/fan_controller/hw/button.c b/FanController/fan_controller/hw/button.c new file mode 100644 index 0000000..90edf03 --- /dev/null +++ b/FanController/fan_controller/hw/button.c @@ -0,0 +1,48 @@ +/* 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. + */ + +#include <fan_controller/hw/button.h> +#include <fan_controller/hw/io.h> +#include <fan_controller/delay.h> + +/** + * @brief Zisti, ci je tlacitko stlacene. + * + * @retval true Tlacitko je stlacene. + * @retval false Tlacitko nie je stlacene. + */ +bool +button_is_pressed(void) +{ + bool a, b; + + a = io_is_button_pressed(); + // Minimalna hodnota pre cakanie funkciou delay_ms() je 16 ms. + delay_ms(20); + b = io_is_button_pressed(); + // Stavy oboch vzoriek sa musia rovnat + // a musia byt rovne stlacenemu tlacitku. + // Ak by sa nerovnali, islo by o zakmit. + return (!a == !b) && !a && !b; +} + +/** + * @brief Zisti, ci je tlacitko pustene (nestlacene). + * + * @retval true Tlacitko je pustene. + * @retval false Tlacitko nie je pustene. + */ +bool +button_is_released(void) +{ + return !button_is_pressed(); +} diff --git a/FanController/fan_controller/hw/button.h b/FanController/fan_controller/hw/button.h new file mode 100644 index 0000000..7e7bdef --- /dev/null +++ b/FanController/fan_controller/hw/button.h @@ -0,0 +1,23 @@ +/* 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. + */ + +#ifndef BUTTON_H_ +#define BUTTON_H_ + +#include <stdbool.h> + +bool button_is_pressed(void); +bool button_is_released(void); +void button_wait_for_press(void); +void button_wait_for_release(void); + +#endif /* BUTTON_H_ */
\ No newline at end of file diff --git a/FanController/fan_controller/hw/eeprom.c b/FanController/fan_controller/hw/eeprom.c new file mode 100644 index 0000000..dcad85c --- /dev/null +++ b/FanController/fan_controller/hw/eeprom.c @@ -0,0 +1,120 @@ +/* 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. + */ + +#include <avr/io.h> + +#include <fan_controller/hw/eeprom.h> +#include <fan_controller/error.h> + +/** Velkost pamate EEPROM v bajtoch */ +#define EEPROM_SIZE_IN_BYTES 512U // bajtov + +static void eeprom_write_byte(uint16_t address, uint8_t byte); +static void eeprom_read_byte(uint16_t address, uint8_t *const byte); +static void eeprom_wait_until_ready(void); +static void eeprom_check_address_and_size(uint16_t address, uint16_t op_size); + +/** + * @brief Zapis dat z EEPROM. + * + * @param address Adresa kam sa bude zapisovat. + * @param data Ukazovatel na buffer, odkial sa budu brat bajty pre zapis do EEPROM. + * @param data_size Pocet bajtov co sa budu zapisovat. + */ +void +eeprom_write(uint16_t address, const uint8_t * const data, uint16_t data_size) +{ + eeprom_check_address_and_size(address, data_size); + + for (uint16_t i = 0; i < data_size; i++) { + eeprom_write_byte(address, data[i]); + ++address; + } +} + +/** + * @brief Precitanie dat z EEPROM. + * + * @param address Adresa odkial sa bude citat. + * @param data Ukazovatel na buffer, kde sa precitane bajty ulozia. + * @param data_size Pocet bajtov co sa budu citat. + */ +void +eeprom_read(uint16_t address, uint8_t * const data, uint16_t data_size) +{ + eeprom_check_address_and_size(address, data_size); + + for (uint16_t i = 0; i < data_size; i++) { + eeprom_read_byte(address, &(data[i])); + ++address; + } +} + +/** + * @brief Zapis bajtu z EEPROM. + * + * @param address Adresa kam sa bude zapisovat. + * @param byte Bajt pre zapis na adresu @p address. + */ +void +eeprom_write_byte(uint16_t address, uint8_t byte) +{ + eeprom_wait_until_ready(); + EEAR = address; + EEDR = byte; + EECR |= (1 << EEMWE); + EECR |= (1 << EEWE); +} + +/** + * @brief Precitanie bajtu z EEPROM. + * + * @param address Adresa odkial sa bude citat. + * @param byte Ukazovatel na bajt, kde bude ulozena precitana hodnota. + */ +void +eeprom_read_byte(uint16_t address, uint8_t *const byte) +{ + eeprom_wait_until_ready(); + EEAR = address; + EECR |= (1<<EERE); + *byte = EEDR; +} + +/** + * @brief Pocka, az bude pamat EEPROM pripravena na dalsiu operaciu. + * + * Blokujuca funkcia. + */ +void +eeprom_wait_until_ready(void) +{ + while ((EECR & (1 << EEWE)) != 0) { + ; + } +} + +/** + * @brief Skontroluje ci sa velkost dat @p op_size vojde do EEPROM od adresy @p address. + * + * Ak sa data nezmestia, vyvola chybu. + * + * @param address Adresa v EEPROM. + * @param op_size Pocet bajtov dat. + */ +void +eeprom_check_address_and_size(uint16_t address, uint16_t op_size) +{ + if ((address >= EEPROM_SIZE_IN_BYTES) || ((address + op_size) > EEPROM_SIZE_IN_BYTES)) { + error(); + } +}
\ No newline at end of file diff --git a/FanController/fan_controller/hw/eeprom.h b/FanController/fan_controller/hw/eeprom.h new file mode 100644 index 0000000..c6eb825 --- /dev/null +++ b/FanController/fan_controller/hw/eeprom.h @@ -0,0 +1,21 @@ +/* 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. + */ + +#ifndef EEPROM_H_ +#define EEPROM_H_ + +#include <stdint.h> + +void eeprom_write(uint16_t address, const uint8_t * const data, uint16_t data_size); +void eeprom_read(uint16_t address, uint8_t * const data, uint16_t data_size); + +#endif /* EEPROM_H_ */
\ No newline at end of file diff --git a/FanController/fan_controller/hw/fan.c b/FanController/fan_controller/hw/fan.c new file mode 100644 index 0000000..e2ac6d8 --- /dev/null +++ b/FanController/fan_controller/hw/fan.c @@ -0,0 +1,338 @@ +/* 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. + */ + +#include <avr/io.h> +#include <avr/interrupt.h> + +#include <math.h> + +#include <fan_controller/hw/fan.h> +#include <fan_controller/error.h> +#include <fan_controller/median.h> +#include <fan_controller/hw/led.h> +#include <fan_controller/hw/io.h> +#include <fan_controller/config.h> +#include <fan_controller/delay.h> +#include <fan_controller/hw/button.h> + +/** Pocet vzoriek z ktorych sa bude pocitat median pri merani sirky pulzu z tachometra ventilatora. */ +#define FAN_PULSE_SAMPLE_COUNT 7 + +/** Pocet milisekund kolko sa bude cakat na ustalenie otacok pri novom nastaveni pri kalibracii merania otacok. */ +#define FAN_RPM_STABILIZATION_MS 10000U // = 10 s + +/** Pocet milisekund do kedy sa musi zachytit pulz tachometra pri merani. Inak sa bude povazovat ventilator za zastaveny a bude ohlasena chyba. */ +#define FAN_RPM_TIMER_MAX_VALUE 16000U // = 1,024 s + +static void fan_pwm_enable(void); +static void fan_pwm_disable(void); +static void fan_error(void); +static void fan_rpm_set_raw(uint8_t rpm); +static void fan_rpm_sense_calibrate(void); +static uint16_t fan_get_pulse_time(void); +static uint16_t fan_get_pulse_time_median(void); + +uint16_t fan_min_rpm_pulse_time; +uint16_t fan_max_rpm_pulse_time; + +/** Minimalna nastavitelna hodnota otacok pre PWM. */ +#define FAN_PWM_MIN 1U + +/** Maximalna nastavitelna hodnota otacok pre PWM. */ +#define FAN_PWM_MAX 79U + +/** Pocet urovni otacok pre PWM. */ +#define FAN_PWM_LEVEL_COUNT (FAN_PWM_MAX - FAN_PWM_MIN + 1U) + +/** Reload hodnota casovaca pre generovanie PWM o frekvencii priblizne 25 kHz. */ +#define FAN_PWM_TIMER_RELOAD_VALUE 176U + + +/** + * @brief Nastavenie otacok ventilatora cez PWM. + * + * @param rpm Hodnota otacok z rozsahu 0 az FAN_PWM_LEVEL_COUNT (vratane). + */ +void +fan_rpm_set_raw(uint8_t rpm) +{ + if (rpm >= FAN_PWM_LEVEL_COUNT) { + // Hodnota PWM je mimo pripustneho rozsahu + error(); + } + // Nastavenie output-compare pre generovanie PWM signalu + OCR0 = FAN_PWM_TIMER_RELOAD_VALUE + FAN_PWM_MIN + rpm; +} + +/** + * @brief Nastavenie otacok ventilatora. + * + * @param rpm Hodnota otacok z rozsahu 0 az 255. + */ +void +fan_rpm_set(uint8_t rpm) +{ + // Hodnota sa prepocita z rozsahu [0, 255] na [0, FAN_PWM_LEVEL_COUNT] + double ratio = ((double) (FAN_PWM_LEVEL_COUNT - 1)) / UINT8_MAX; + + uint8_t v = ratio * rpm; + + if (v >= FAN_PWM_LEVEL_COUNT) { + // Pretecenie pri prevode + v = FAN_PWM_LEVEL_COUNT - 1; + } + // Nastavenie otacok ventilatora + fan_rpm_set_raw(v); +} + +/** + * @brief Obsluha prerusenia od casovaca pre generovanie PWM signalu pre ovladanie otacok. + * + * Naplni casovac hodnotou tak, aby sa generovala PWM o frekvencii priblizne 25 kHz. + */ +ISR(TIMER0_OVF_vect) +{ + TCNT0 = FAN_PWM_TIMER_RELOAD_VALUE; +} + +/** + * @brief Inicializacia ventilatora. + * + * Zapne sa PWM, precita sa konfiguracia pre vypocet realnych otacok z EEPROM. + * Ak este nebola ziadna konfiguracia vytvorena, spusti sa kalibracia merania otacok. + * Kalibraciu meranie je mozne vyziadat vzdy stlacenim tlacitka pri resete zariadenia. + */ +void +fan_init(void) +{ + fan_pwm_enable(); + + if (!config_is_fan_max_rpm_pulse_time_init() || !config_is_fan_min_rpm_pulse_time_init() || button_is_pressed()) { + // Kalibruje sa ked je hodnota neinicializovana alebo ked je kalibracia vyziadana uzivatelom + fan_rpm_sense_calibrate(); + } + // Nacitaju sa casy pulzu na minimalnych a maximalnych otackach ventilatora + fan_min_rpm_pulse_time = config_fan_min_rpm_pulse_time_load(); + fan_max_rpm_pulse_time = config_fan_max_rpm_pulse_time_load(); +} + +/** + * @brief Chyba ventilatora. + * + * Odpoji sa PWM vstup ventilatora a vyvola sa chyba. + */ +void +fan_error(void) +{ + fan_pwm_disable(); + io_disable_fan_pwm(); + error(); +} + +/** + * @brief Kalibracia merania otacok ventilatora. + * + * Zmeria sa cas pulzu signalu tachometra pri minimalnych a pri maximalnych otackach. + * Postup merania sa indikuje stlpcom zelenych LED. + */ +void +fan_rpm_sense_calibrate(void) +{ + // Cervenou LED indikovat kalibracny mod + led_red_on(); + led_green_bar_show_by_mask(0); + + // Cas, ktoreho uplunutie bude indikovat jedna zelena LED + const uint16_t time_unit = (2 * FAN_RPM_STABILIZATION_MS) / LED_GREEN_BAR_LED_COUNT; + // Pocet rozsvietenych LED pre cakanie na ustalenie minimalnych otacok + const uint8_t leds_for_one_wait = LED_GREEN_BAR_LED_COUNT / 2; + // Maska pre postupne rozsvecovanie zelenych LED pre indikaciu priebehu + uint8_t mask = 0x00; + + // Ziska cas cas pulzu na minimalnych otackach + fan_rpm_set(0); + for (uint8_t i = 0; i < leds_for_one_wait; ++i) { + // Indikuje sa cas cakania + delay_ms(time_unit); + mask = ((mask << 1) | 0x01); + led_green_bar_show_by_mask(mask); + } + fan_min_rpm_pulse_time = fan_get_pulse_time_median(); + config_fan_min_rpm_pulse_time_save(fan_min_rpm_pulse_time); + + // Ziska sa cas pulzu na maximalnych otackach + fan_rpm_set(UINT8_MAX); + for (uint8_t i = 0; i < leds_for_one_wait; ++i) { + // Indikuje sa cas cakania + delay_ms(time_unit); + mask = ((mask << 1) | 0x01); + led_green_bar_show_by_mask(mask); + } + fan_max_rpm_pulse_time = fan_get_pulse_time_median(); + // Namerane vysledky sa zapisu do EEPROM, aby sa nemusela kalibracia spustat pri dalsom zapnuti zariadenia + config_fan_max_rpm_pulse_time_save(fan_max_rpm_pulse_time); + + // Nastavi sa kludova hodnota otacok = najnizsie otacky + fan_rpm_set(0); + + // Indikacia ukoncenia kalibracneho rezimu + led_red_off(); + led_green_bar_show_by_mask(0); +} + +/** + * @brief Ziskanie relativnej realnej hodnoty otacok. + * + * Zmeria sa sirka pulzu z tachometra a z rozsahu [sirka_pulzu_pri_max_otackach, sirka_pulzu_pri_min_otackach] + * sa prepocita do rozsahu 0 az 255. + * + * @return Relativna hodnota otacok z rozsahu 0 az 255. + */ +uint8_t +fan_rpm_get(void) +{ + const float pulse_time_range = (float) (fan_min_rpm_pulse_time - fan_max_rpm_pulse_time); + + uint16_t pt = fan_get_pulse_time(); + // Korekcia nameraneho casu pulzu do platneho rozsahu (rozsahu zmeranom pri kalibracii) + if (pt < fan_max_rpm_pulse_time) { + pt = fan_max_rpm_pulse_time; + } else if (pt > fan_min_rpm_pulse_time) { + pt = fan_min_rpm_pulse_time; + } + + const float pt_relative = pt - fan_max_rpm_pulse_time; + // Pomer nameranej hodnoty sirky pulzu k celemu rozsahu sirky pulzu z tachometra + float ratio = pt_relative / pulse_time_range; + // Osetrenie pretecenia a podtecenie pri aritm. operaciach + if (ratio < 0.0) { + ratio = 0.0; + } else if (ratio > 1.0) { + ratio = 1.0; + } + + // Aplikuje sa korekcia zobrazenia, aby merane otacky mali viac linearny priebeh + ratio = (ratio * 9.0) + 1.0; + ratio = log10(ratio); + ratio = (ratio * 9.0) + 1.0; + ratio = log10(ratio); + // Osetrenie pretecenia a podtecenie pri aritm. operaciach + if (ratio < 0.0) { + ratio = 0.0; + } else if (ratio > 1.0) { + ratio = 1.0; + } + + return (UINT8_MAX - ((uint8_t) (((float) UINT8_MAX) * ratio))); +} + +/** + * @brief Zmeranie sirky pulzu z tachometra ventilatora. + * + * Pri meranie sa detekuje zastavenie ventilatora a ak nastalo, vyvola sa chyba. + * + * @return Sirka pulzu z tachometra ventilatora. + */ +uint16_t +fan_get_pulse_time(void) +{ + // Zacina sa s meranim sirky pulzu, casovac je zastaveny + TCNT1 = 0x0000; + + // Pocka sa na nabeznu hranu + // Pocka sa na log. 0 RPM signalu + while (io_is_fan_tachometer_set()) { + // Mozne chyby pristup po 8-bitovych polovickach sa ignoruju + if (TCNT1 > FAN_RPM_TIMER_MAX_VALUE) { + // Detekovane zastavenie ventilatora + fan_error(); + } + } + + // Pocka sa na log. 1 RPM signalu + while (!io_is_fan_tachometer_set()) { + if (TCNT1 > FAN_RPM_TIMER_MAX_VALUE) { + // Detekovane zastavenie ventilatora + fan_error(); + } + } + + // Zapnutie casovaca 1, preddelicka 1024 + TCCR1B |= ((1 << CS12) | (1 << CS10)); + // Pocka sa na log. 0 RPM signalu + while (io_is_fan_tachometer_set()) { + if (TCNT1 > FAN_RPM_TIMER_MAX_VALUE) { + // Detekovane zastavenie ventilatora + fan_error(); + } + } + // Konci s s meranim sirky pulzu, vypnutie casovaca 1 + TCCR1B &= ~((1 << CS12) | (1 << CS10)); + + // Ziskanie nameranej hodnoty + return TCNT1; +} + +/** + * @brief Zmeranie sirky pulzu z tachometra ventilatora vypocitaneho ako median z viacerych nameranych hodnot. + * + * Pri meranie sa detekuje zastavenie ventilatora a ak nastalo, vyvola sa chyba. + * + * @return Sirka pulzu z tachometra ventilatora vypocitana ako median z viacerych nameranych hodnot. + */ +uint16_t +fan_get_pulse_time_median(void) +{ + median_buffer_t m; + + median_init_buffer(&m, FAN_PULSE_SAMPLE_COUNT); + for (uint8_t i = 0; i < FAN_PULSE_SAMPLE_COUNT; i++) { + median_save_sample(&m, fan_get_pulse_time()); + } + return median_get(&m); +} + +/** + * @brief Aktivovanie generovania PWM signalu pre ovladanie otacok ventilatora. + * + * Nastavi sa Casovac 0 a jemu prislusny Output Compare register. + */ +void +fan_pwm_enable(void) +{ + // 25 KHz perioda + TCNT0 = FAN_PWM_TIMER_RELOAD_VALUE; + // Povoli sa prerusenie od casovaca pre generovanie PWM + TIMSK |= 1 << TOIE0; + // Nastavi sa najnizsia kludova hodnota otacok + fan_rpm_set(0); + // Fast PWM mode + TCCR0 |= (1 << WGM01) | (1 << WGM00); + // Non-inverted PWM + TCCR0 |= (1 << COM01); + // Prescale by 8 = Zapnutie casovaca + TCCR0 |= (1 << CS01); +} + +/** + * @brief Deaktivovanie generovania PWM signalu pre ovladanie otacok ventilatora. + * + * Zastavi sa Casovac 0 a odpoji sa vystup z Output Compare od GPIO pinu. + */ +void +fan_pwm_disable(void) +{ + // Vypnutie casovaca + TCCR0 &= ~((1 << CS02) | (1 << CS01) | (1 << CS00)); + // Odpojenie OC0 + TCCR0 &= ~((1 << COM01) | (1 << COM00)); +}
\ No newline at end of file diff --git a/FanController/fan_controller/hw/fan.h b/FanController/fan_controller/hw/fan.h new file mode 100644 index 0000000..ae26d16 --- /dev/null +++ b/FanController/fan_controller/hw/fan.h @@ -0,0 +1,22 @@ +/* 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. + */ + +#ifndef FAN_H_ +#define FAN_H_ + +#include <stdint.h> + +void fan_init(void); +void fan_rpm_set(uint8_t rpm); +uint8_t fan_rpm_get(void); + +#endif /* FAN_H_ */
\ No newline at end of file diff --git a/FanController/fan_controller/hw/io.c b/FanController/fan_controller/hw/io.c new file mode 100644 index 0000000..3246f78 --- /dev/null +++ b/FanController/fan_controller/hw/io.c @@ -0,0 +1,428 @@ +/* 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. + */ + +#include <fan_controller/hw/io.h> +#include <fan_controller/utils.h> + +#include <avr/io.h> + +static void io_init_button(void); +static void io_init_fan_pwm(void); +static void io_init_fan_tachometer(void); +static void io_init_red_led(void); +static void io_init_green_led_0(void); +static void io_init_green_led_1(void); +static void io_init_green_led_2(void); +static void io_init_green_led_3(void); +static void io_init_green_led_4(void); +static void io_init_green_led_5(void); +static void io_init_green_led_6(void); +static void io_init_green_led_7(void); +static void io_init_green_led_8(void); + +/** + * @brief Inicializacia vstupov a vystupov mikrokontroleru. + */ +void +io_init(void) +{ + io_init_button(); + io_init_fan_pwm(); + io_init_fan_tachometer(); + io_init_red_led(); + io_init_green_led_0(); + io_init_green_led_1(); + io_init_green_led_2(); + io_init_green_led_3(); + io_init_green_led_4(); + io_init_green_led_5(); + io_init_green_led_6(); + io_init_green_led_7(); + io_init_green_led_8(); +} + +/** + * @brief Nastavenie vstupu pre tlacitko. + */ +void +io_init_button(void) +{ + // Aktivacia pull-up rezistoru, to je treba, aby vstup "neplaval" + UTILS_SET_BIT(PORTA, PORTA2); +} + +/** + * @brief Zistenie, ci je tlacitko stlacene, alebo nie. + * + * @retval true Tlacitko je stlacene. + * @retval false Tlacitko nie je stlacene. + */ +bool +io_is_button_pressed(void) +{ + return UTILS_IS_BIT_SET(PINA, PINA2); +} + +/** + * @brief Nastavenie vystupu pre PWM signal pre ovladanie otacok ventilatora. + */ +void +io_init_fan_pwm(void) +{ + // Nastavenie vystupu pre PWM (Timer0 a OutputCompare0) + UTILS_SET_BIT(DDRB, PORTB3); // OC0 ako vystup + UTILS_CLR_BIT(PORTB, PORTB3); +} + +/** + * @brief Odpojenie vystupu pre PWM signal pre ovladanie otacok ventilatora. + */ +void +io_disable_fan_pwm(void) +{ + // Deaktivacia vystupu pre PWM (Timer0 a OutputCompare0) + UTILS_CLR_BIT(PORTB, PORTB3); // Vypnutie pull-up rezistoru + UTILS_CLR_BIT(DDRB, PORTB3); // OC0 ako vstup v tretom stave + +} + +/** + * @brief Nastavenie vstupu pre signal od tachometra ventilatora. + */ +void +io_init_fan_tachometer(void) +{ + // Aktivacia pull-up rezistoru, to je treba, inak je na vstupe "rusenie" + UTILS_SET_BIT(PORTB, PORTB2); +} + +/** + * @brief Zistenie, ci je signal z tachometra v log. 1, alebo nie. + * + * @retval true Signal je v log. 1. + * @retval false Signal nie je v log. 1. + */ +bool +io_is_fan_tachometer_set(void) +{ + return UTILS_IS_BIT_SET(PINB, PINB2); +} + +/** + * @brief Inicializacia cervenej LED. + */ +void +io_init_red_led(void) +{ + UTILS_SET_BIT(DDRA, PORTA3); + io_off_red_led(); +} + +/** + * @brief Zasvietenie cervenej LED. + */ +void +io_on_red_led(void) +{ + UTILS_CLR_BIT(PORTA, PORTA3); +} + +/** + * @brief Zhasnutie cervenej LED. + */ +void +io_off_red_led(void) +{ + UTILS_SET_BIT(PORTA, PORTA3); +} + +/** + * @brief Inicializacia zelenej LED najviac vlavo. + * + * Ta bude ovladana PWM od Casovaca 2, aby sa dal regulovat jej jas. + * Casovac 2 je zarovej pouziti implementaciu cakania v delay.c. + */ +void +io_init_green_led_0(void) +{ + UTILS_SET_BIT(DDRD, PORTD7); + + // LED 0 bude ovladana PWM casovaca 2 + // 62,5 Hz perioda + TCNT2 = 0; + // Fast PWM mode + TCCR2 |= (1 << WGM21) | (1 << WGM20); + // Inverted PWM + TCCR2 |= ((1 << COM21) | (1 << COM20)); + // Prescale by 1024 = Zapnutie casovaca + TCCR2 |= ((1 << CS22) | (1 << CS21) | (1 << CS20)); + + io_off_green_led_0(); +} + +/** + * @brief Nastavenie maximalneho jasu zelenej LED najviac vlavo. + * + * Zasvietenie LED. + */ +void +io_on_green_led_0(void) +{ + // LED 0 je ovladana PWM casovaca 2 + io_set_green_led_0(UINT8_MAX); +} + +/** + * @brief Nastavenie jasu zelenej LED najviac vlavo. + * + * @param light Jas zelenej LED z rozsahu 0 az 255. + */ +void +io_set_green_led_0(uint8_t light) +{ + OCR2 = light; +} + +/** + * @brief Nastavenie minimalneho jasu zelenej LED najviac vlavo. + * + * Zhasnutie LED. + */ +void +io_off_green_led_0(void) +{ + io_set_green_led_0(0); +} + +/** + * @brief Inizializacia zelej LED 1. + */ +void +io_init_green_led_1(void) +{ + UTILS_SET_BIT(DDRC, PORTC0); + io_off_green_led_1(); +} + +/** + * @brief Zasvietenie zelej LED 1. + */ +void +io_on_green_led_1(void) +{ + UTILS_CLR_BIT(PORTC, PORTC0); +} + +/** + * @brief Zhasnutie zelej LED 1. + */ +void +io_off_green_led_1(void) +{ + UTILS_SET_BIT(PORTC, PORTC0); +} + +/** + * @brief Inizializacia zelej LED 2. + */ +void +io_init_green_led_2(void) +{ + UTILS_SET_BIT(DDRC, PORTC1); + io_off_green_led_2(); +} + +/** + * @brief Zasvietenie zelej LED 2. + */ +void +io_on_green_led_2(void) +{ + UTILS_CLR_BIT(PORTC, PORTC1); +} + +/** + * @brief Zhasnutie zelej LED 2. + */ +void +io_off_green_led_2(void) +{ + UTILS_SET_BIT(PORTC, PORTC1); +} + +/** + * @brief Inizializacia zelej LED 3. + */ +void +io_init_green_led_3(void) +{ + UTILS_SET_BIT(DDRC, PORTC3); + io_off_green_led_3(); +} + +/** + * @brief Zasvietenie zelej LED 3. + */ +void +io_on_green_led_3(void) +{ + UTILS_CLR_BIT(PORTC, PORTC3); +} + +/** + * @brief Zhasnutie zelej LED 3. + */ +void +io_off_green_led_3(void) +{ + UTILS_SET_BIT(PORTC, PORTC3); +} + +/** + * @brief Inizializacia zelej LED 4. + */ +void +io_init_green_led_4(void) +{ + UTILS_SET_BIT(DDRC, PORTC5); + io_off_green_led_4(); +} + +/** + * @brief Zasvietenie zelej LED 4. + */ +void +io_on_green_led_4(void) +{ + UTILS_CLR_BIT(PORTC, PORTC5); +} + +/** + * @brief Zhasnutie zelej LED 4. + */ +void +io_off_green_led_4(void) +{ + UTILS_SET_BIT(PORTC, PORTC5); +} + +/** + * @brief Inizializacia zelej LED 5. + */ +void +io_init_green_led_5(void) +{ + UTILS_SET_BIT(DDRC, PORTC6); + io_off_green_led_5(); +} + +/** + * @brief Zasvietenie zelej LED 5. + */ +void +io_on_green_led_5(void) +{ + UTILS_CLR_BIT(PORTC, PORTC6); +} + +/** + * @brief Zhasnutie zelej LED 5. + */ +void +io_off_green_led_5(void) +{ + UTILS_SET_BIT(PORTC, PORTC6); +} + +/** + * @brief Inizializacia zelej LED 6. + */ +void +io_init_green_led_6(void) +{ + UTILS_SET_BIT(DDRC, PORTC7); + io_off_green_led_6(); +} + +/** + * @brief Zasvietenie zelej LED 6. + */ +void +io_on_green_led_6(void) +{ + UTILS_CLR_BIT(PORTC, PORTC7); +} + +/** + * @brief Zhasnutie zelej LED 6. + */ +void +io_off_green_led_6(void) +{ + UTILS_SET_BIT(PORTC, PORTC7); +} + +/** + * @brief Inizializacia zelej LED 7. + */ +void +io_init_green_led_7(void) +{ + UTILS_SET_BIT(DDRA, PORTA7); + io_off_green_led_7(); +} + +/** + * @brief Zasvietenie zelej LED 7. + */ +void +io_on_green_led_7(void) +{ + UTILS_CLR_BIT(PORTA, PORTA7); +} + +/** + * @brief Zhasnutie zelej LED 8. + */ +void +io_off_green_led_7(void) +{ + UTILS_SET_BIT(PORTA, PORTA7); +} + +/** + * @brief Inizializacia zelej LED 8. + */ +void +io_init_green_led_8(void) +{ + UTILS_SET_BIT(DDRA, PORTA5); + io_off_green_led_8(); +} + +/** + * @brief Zasvietenie zelej LED 8. + */ +void +io_on_green_led_8(void) +{ + UTILS_CLR_BIT(PORTA, PORTA5); +} + +/** + * @brief Zhasnutie zelej LED 8. + */ +void +io_off_green_led_8(void) +{ + UTILS_SET_BIT(PORTA, PORTA5); +}
\ No newline at end of file diff --git a/FanController/fan_controller/hw/io.h b/FanController/fan_controller/hw/io.h new file mode 100644 index 0000000..783df00 --- /dev/null +++ b/FanController/fan_controller/hw/io.h @@ -0,0 +1,46 @@ +/* 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. + */ + +#ifndef IO_H_ +#define IO_H_ + +#include <stdint.h> +#include <stdbool.h> + +void io_init(void); + +bool io_is_button_pressed(void); +void io_disable_fan_pwm(void); +bool io_is_fan_tachometer_set(void); +void io_on_red_led(void); +void io_off_red_led(void); +void io_on_green_led_0(void); +void io_set_green_led_0(uint8_t light); +void io_off_green_led_0(void); +void io_on_green_led_1(void); +void io_off_green_led_1(void); +void io_on_green_led_2(void); +void io_off_green_led_2(void); +void io_on_green_led_3(void); +void io_off_green_led_3(void); +void io_on_green_led_4(void); +void io_off_green_led_4(void); +void io_on_green_led_5(void); +void io_off_green_led_5(void); +void io_on_green_led_6(void); +void io_off_green_led_6(void); +void io_on_green_led_7(void); +void io_off_green_led_7(void); +void io_on_green_led_8(void); +void io_off_green_led_8(void); + +#endif /* IO_H_ */
\ No newline at end of file diff --git a/FanController/fan_controller/hw/led.c b/FanController/fan_controller/hw/led.c new file mode 100644 index 0000000..221ac09 --- /dev/null +++ b/FanController/fan_controller/hw/led.c @@ -0,0 +1,184 @@ +/* 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. + */ + +#include <avr/io.h> + +#include <fan_controller/hw/io.h> +#include <fan_controller/hw/led.h> +#include <fan_controller/utils.h> + +static uint8_t led_green_bar_mask_from_value(uint8_t value); + +/** Naposledy pouzita maska pre nastavenie stlpca LED. Je pouziva pre detekciu zmeny zeleneho stlpca LED pre automaticke vypinanie LED. */ +static uint8_t led_green_bar_value_mask_last_set; + +/** + * @brief Inicializacia LED. + * + * Zelena LED nalavo bude svietit (s roznym jasom) vzdy, aby indikovala, ze je zariadenie zapnute. + */ +void +led_init(void) +{ + io_on_green_led_0(); +} + +/** + * @brief Zasvietenie cervenej LED. + */ +void +led_red_off(void) +{ + io_off_red_led(); +} + +/** + * @brief Zhasnutie cervenej LED. + */ +void +led_red_on(void) +{ + io_on_red_led(); +} + +/** + * @brief Zhasnutie zelenej LED najviac nalavo. + */ +void +led_min_green_off(void) +{ + io_off_green_led_0(); +} + +/** + * @brief Nastavenie svitu zelenej LED najviac nalavo. + * + * @param light Svit LED z rozsahu 0 az 255. + */ +void +led_min_green_set(uint8_t light) +{ + io_set_green_led_0(light); +} + +/** + * @brief Zasvietenie zelenej LED najviac nalavo. + */ +void +led_min_green_on(void) +{ + io_on_green_led_0(); +} + +/** + * @brief Nastavenie zobrazenie jednotlivych LED zelenho stlpca (druhej zlava az poslednej zelenej LED) podla bitovej masky. + * + * 0. bit bitovej masky zodpoveda zelej LED druhej zlava a ostatne bity nasledujucim LED. + * + * @param mask Bitova maska rozsvietenia LED. Log. 0 je zhasnutie a log. 1 je zasvietenie prislusnej LED. + */ +void +led_green_bar_show_by_mask(uint8_t mask) +{ + void (*led_on[LED_GREEN_BAR_LED_COUNT])(void) = { + io_on_green_led_1, + io_on_green_led_2, + io_on_green_led_3, + io_on_green_led_4, + io_on_green_led_5, + io_on_green_led_6, + io_on_green_led_7, + io_on_green_led_8 + }; + + void (*led_off[LED_GREEN_BAR_LED_COUNT])(void) = { + io_off_green_led_1, + io_off_green_led_2, + io_off_green_led_3, + io_off_green_led_4, + io_off_green_led_5, + io_off_green_led_6, + io_off_green_led_7, + io_off_green_led_8 + }; + + for (uint8_t i = 0; i < LED_GREEN_BAR_LED_COUNT; ++i) { + // Podla prislusneho bitu masky sa bud zavola funkcia pre zhasnutie alebo pre zasvietenie prislusnej LED + if (UTILS_IS_BIT_SET(mask, i)) { + (led_on[i])(); + } else { + (led_off[i])(); + } + } +} + +/** + * @brief Prevod hodnoty z rozsahu 0 az 255 na masku pre rozsvietenie zeleneho stlpca LED. + * + * Cim vyssia hodnota tym viac stlpec zelenych LED narastie. + * + * @param value Hodnota z rozsahu 0 az 255. + * @return Maska pre rozsvietenie stlpca zelenych LED. + */ +uint8_t +led_green_bar_mask_from_value(uint8_t value) +{ + // Hranice oblasti na ktore je rozdeleny rozsah 0 az 255 + const uint8_t led_thresholds[] = { + 0+16, 32+16, 64+16, 96+16, 128+16, 160+16, 192+16, 224+16 + }; + // Pocet oblasti na ktore je rozdeleny rozsah 0 az 255 + // Kazdej oblasi zodpoveda jedna zelena LED od druhej zlava az po poslednu zelenu. Spolu 8 zelenych LED. + const uint8_t threshold_count = sizeof(led_thresholds) / sizeof(led_thresholds[0]); + + uint8_t mask = 0x00; + + for (uint8_t i = 0; i < threshold_count; ++i) { + if (value > led_thresholds[i]) { + // Hodnota je vyssia ako prislusna hranica, prislusna LED sa nastavi v maske + UTILS_SET_BIT(mask, i); + } else { + // Hodnota nie je vyssia ako hranicna hodnota, nema zmysel dalej porovnavat + break; + } + } + + return mask; +} + +/** + * @brief Zisti, ci by hodnota pre nastavenie LED zmenila pocet zasvietenych LED zeleneho stlpca. + * + * @param value Hodnota z rozsahu 0 az 255. + * @retval true Hodnota by sposobila zmenu v rozsvieteni zelenych LED. + * @retval false Hodnota by nesposobila zmenu v rozsvieteni zelenych LED. + */ +bool +led_green_bar_will_be_changed_by_value(uint8_t value) +{ + return (led_green_bar_value_mask_last_set != led_green_bar_mask_from_value(value)); +} + +/** + * @brief Rozsvietenie stlpca zelenych LED podla velkosti hodnoty od 0 po 255. + * + * Cim vyssia hodnota tym viac stlpec zelenych LED narastie. + * + * @param value Hodnota z rozsahu 0 az 255. + */ +void +led_green_bar_show_by_value(uint8_t value) +{ + const uint8_t mask = led_green_bar_mask_from_value(value); + led_green_bar_show_by_mask(mask); + led_green_bar_value_mask_last_set = mask; +} diff --git a/FanController/fan_controller/hw/led.h b/FanController/fan_controller/hw/led.h new file mode 100644 index 0000000..3698243 --- /dev/null +++ b/FanController/fan_controller/hw/led.h @@ -0,0 +1,32 @@ +/* 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. + */ + +#ifndef LED_H_ +#define LED_H_ + +#include <stdint.h> +#include <stdbool.h> + +/** Pocet LED v stlpci zelenych LED pre indikaciu otacok. LED najviac nalavo nie je zapocitana. */ +#define LED_GREEN_BAR_LED_COUNT 8U + +void led_init(void); +void led_red_off(void); +void led_red_on(void); +void led_min_green_off(void); +void led_min_green_set(uint8_t light); +void led_min_green_on(void); +void led_green_bar_show_by_mask(uint8_t mask); +bool led_green_bar_will_be_changed_by_value(uint8_t value); +void led_green_bar_show_by_value(uint8_t value); + +#endif /* LED_H_ */
\ No newline at end of file |
