aboutsummaryrefslogtreecommitdiff
path: root/FanController/fan_controller/hw
diff options
context:
space:
mode:
Diffstat (limited to 'FanController/fan_controller/hw')
-rw-r--r--FanController/fan_controller/hw/ad_converter.c130
-rw-r--r--FanController/fan_controller/hw/ad_converter.h23
-rw-r--r--FanController/fan_controller/hw/button.c48
-rw-r--r--FanController/fan_controller/hw/button.h23
-rw-r--r--FanController/fan_controller/hw/eeprom.c120
-rw-r--r--FanController/fan_controller/hw/eeprom.h21
-rw-r--r--FanController/fan_controller/hw/fan.c338
-rw-r--r--FanController/fan_controller/hw/fan.h22
-rw-r--r--FanController/fan_controller/hw/io.c428
-rw-r--r--FanController/fan_controller/hw/io.h46
-rw-r--r--FanController/fan_controller/hw/led.c184
-rw-r--r--FanController/fan_controller/hw/led.h32
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