aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FanController.atsln22
-rw-r--r--FanController/FanController.componentinfo.xml86
-rw-r--r--FanController/FanController.cproj218
-rw-r--r--FanController/fan_controller/config.c198
-rw-r--r--FanController/fan_controller/config.h42
-rw-r--r--FanController/fan_controller/delay.c113
-rw-r--r--FanController/fan_controller/delay.h24
-rw-r--r--FanController/fan_controller/error.c47
-rw-r--r--FanController/fan_controller/error.h20
-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
-rw-r--r--FanController/fan_controller/main.c381
-rw-r--r--FanController/fan_controller/median.c131
-rw-r--r--FanController/fan_controller/median.h42
-rw-r--r--FanController/fan_controller/utils.h23
25 files changed, 2762 insertions, 0 deletions
diff --git a/FanController.atsln b/FanController.atsln
new file mode 100644
index 0000000..7caf5e6
--- /dev/null
+++ b/FanController.atsln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Atmel Studio Solution File, Format Version 11.00
+VisualStudioVersion = 14.0.23107.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{54F91283-7BC4-4236-8FF9-10F437C3AD48}") = "FanController", "FanController\FanController.cproj", "{DCE6C7E3-EE26-4D79-826B-08594B9AD897}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|AVR = Debug|AVR
+ Release|AVR = Release|AVR
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Debug|AVR.ActiveCfg = Debug|AVR
+ {DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Debug|AVR.Build.0 = Debug|AVR
+ {DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Release|AVR.ActiveCfg = Release|AVR
+ {DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Release|AVR.Build.0 = Release|AVR
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/FanController/FanController.componentinfo.xml b/FanController/FanController.componentinfo.xml
new file mode 100644
index 0000000..e135a7f
--- /dev/null
+++ b/FanController/FanController.componentinfo.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Store xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="AtmelPackComponentManagement">
+ <ProjectComponents>
+ <ProjectComponent z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
+ <CApiVersion></CApiVersion>
+ <CBundle></CBundle>
+ <CClass>Device</CClass>
+ <CGroup>Startup</CGroup>
+ <CSub></CSub>
+ <CVariant></CVariant>
+ <CVendor>Atmel</CVendor>
+ <CVersion>1.2.0</CVersion>
+ <DefaultRepoPath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs</DefaultRepoPath>
+ <DependentComponents xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />
+ <Description></Description>
+ <Files xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
+ <d4p1:anyType i:type="FileInfo">
+ <AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.2.132\include</AbsolutePath>
+ <Attribute></Attribute>
+ <Category>include</Category>
+ <Condition>C</Condition>
+ <FileContentHash i:nil="true" />
+ <FileVersion></FileVersion>
+ <Name>include</Name>
+ <SelectString></SelectString>
+ <SourcePath></SourcePath>
+ </d4p1:anyType>
+ <d4p1:anyType i:type="FileInfo">
+ <AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.2.132\include\avr\iom16a.h</AbsolutePath>
+ <Attribute></Attribute>
+ <Category>header</Category>
+ <Condition>C</Condition>
+ <FileContentHash>9dUmVhk1YK5bL4h4q/lK7A==</FileContentHash>
+ <FileVersion></FileVersion>
+ <Name>include/avr/iom16a.h</Name>
+ <SelectString></SelectString>
+ <SourcePath></SourcePath>
+ </d4p1:anyType>
+ <d4p1:anyType i:type="FileInfo">
+ <AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.2.132\templates\main.c</AbsolutePath>
+ <Attribute>template</Attribute>
+ <Category>source</Category>
+ <Condition>C Exe</Condition>
+ <FileContentHash>iWaXYR+zdyVB4i5U15qkgA==</FileContentHash>
+ <FileVersion></FileVersion>
+ <Name>templates/main.c</Name>
+ <SelectString>Main file (.c)</SelectString>
+ <SourcePath></SourcePath>
+ </d4p1:anyType>
+ <d4p1:anyType i:type="FileInfo">
+ <AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.2.132\templates\main.cpp</AbsolutePath>
+ <Attribute>template</Attribute>
+ <Category>source</Category>
+ <Condition>C Exe</Condition>
+ <FileContentHash>YXFphlh0CtZJU+ebktABgQ==</FileContentHash>
+ <FileVersion></FileVersion>
+ <Name>templates/main.cpp</Name>
+ <SelectString>Main file (.cpp)</SelectString>
+ <SourcePath></SourcePath>
+ </d4p1:anyType>
+ <d4p1:anyType i:type="FileInfo">
+ <AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.2.132\gcc\dev\atmega16a</AbsolutePath>
+ <Attribute></Attribute>
+ <Category>libraryPrefix</Category>
+ <Condition>GCC</Condition>
+ <FileContentHash i:nil="true" />
+ <FileVersion></FileVersion>
+ <Name>gcc/dev/atmega16a</Name>
+ <SelectString></SelectString>
+ <SourcePath></SourcePath>
+ </d4p1:anyType>
+ </Files>
+ <PackName>ATmega_DFP</PackName>
+ <PackPath>C:/Program Files (x86)/Atmel/Studio/7.0/Packs/atmel/ATmega_DFP/1.2.132/Atmel.ATmega_DFP.pdsc</PackPath>
+ <PackVersion>1.2.132</PackVersion>
+ <PresentInProject>true</PresentInProject>
+ <ReferenceConditionId>ATmega16A</ReferenceConditionId>
+ <RteComponents xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
+ <d4p1:string></d4p1:string>
+ </RteComponents>
+ <Status>Resolved</Status>
+ <VersionMode>Fixed</VersionMode>
+ <IsComponentInAtProject>true</IsComponentInAtProject>
+ </ProjectComponent>
+ </ProjectComponents>
+</Store> \ No newline at end of file
diff --git a/FanController/FanController.cproj b/FanController/FanController.cproj
new file mode 100644
index 0000000..71c28cb
--- /dev/null
+++ b/FanController/FanController.cproj
@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="14.0">
+ <PropertyGroup>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectVersion>7.0</ProjectVersion>
+ <ToolchainName>com.Atmel.AVRGCC8.C</ToolchainName>
+ <ProjectGuid>dce6c7e3-ee26-4d79-826b-08594b9ad897</ProjectGuid>
+ <avrdevice>ATmega16A</avrdevice>
+ <avrdeviceseries>none</avrdeviceseries>
+ <OutputType>Executable</OutputType>
+ <Language>C</Language>
+ <OutputFileName>$(MSBuildProjectName)</OutputFileName>
+ <OutputFileExtension>.elf</OutputFileExtension>
+ <OutputDirectory>$(MSBuildProjectDirectory)\$(Configuration)</OutputDirectory>
+ <AssemblyName>FanController</AssemblyName>
+ <Name>FanController</Name>
+ <RootNamespace>FanController</RootNamespace>
+ <ToolchainFlavour>Native</ToolchainFlavour>
+ <KeepTimersRunning>true</KeepTimersRunning>
+ <OverrideVtor>false</OverrideVtor>
+ <CacheFlash>true</CacheFlash>
+ <ProgFlashFromRam>true</ProgFlashFromRam>
+ <RamSnippetAddress>0x20000000</RamSnippetAddress>
+ <UncachedRange />
+ <preserveEEPROM>true</preserveEEPROM>
+ <OverrideVtorValue>exception_table</OverrideVtorValue>
+ <BootSegment>2</BootSegment>
+ <eraseonlaunchrule>0</eraseonlaunchrule>
+ <AsfFrameworkConfig>
+ <framework-data xmlns="">
+ <options />
+ <configurations />
+ <files />
+ <documentation help="" />
+ <offline-documentation help="" />
+ <dependencies>
+ <content-extension eid="atmel.asf" uuidref="Atmel.ASF" version="3.34.1" />
+ </dependencies>
+ </framework-data>
+ </AsfFrameworkConfig>
+ <avrtool>com.atmel.avrdbg.tool.simulator</avrtool>
+ <avrtoolserialnumber />
+ <avrdeviceexpectedsignature>0x1E9403</avrdeviceexpectedsignature>
+ <com_atmel_avrdbg_tool_simulator>
+ <ToolOptions xmlns="">
+ <InterfaceProperties>
+ </InterfaceProperties>
+ </ToolOptions>
+ <ToolType xmlns="">com.atmel.avrdbg.tool.simulator</ToolType>
+ <ToolNumber xmlns="">
+ </ToolNumber>
+ <ToolName xmlns="">Simulator</ToolName>
+ </com_atmel_avrdbg_tool_simulator>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
+ <ToolchainSettings>
+ <AvrGcc>
+ <avrgcc.common.Device>-mmcu=atmega16a -B "%24(PackRepoDir)\atmel\ATmega_DFP\1.2.132\gcc\dev\atmega16a"</avrgcc.common.Device>
+ <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
+ <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
+ <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
+ <avrgcc.common.outputfiles.srec>True</avrgcc.common.outputfiles.srec>
+ <avrgcc.common.outputfiles.usersignatures>False</avrgcc.common.outputfiles.usersignatures>
+ <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
+ <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
+ <avrgcc.compiler.symbols.DefSymbols>
+ <ListValues>
+ <Value>NDEBUG</Value>
+ </ListValues>
+ </avrgcc.compiler.symbols.DefSymbols>
+ <avrgcc.compiler.directories.IncludePaths>
+ <ListValues>
+ <Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.2.132\include</Value>
+ <Value>..</Value>
+ </ListValues>
+ </avrgcc.compiler.directories.IncludePaths>
+ <avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level>
+ <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
+ <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
+ <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
+ <avrgcc.compiler.warnings.ExtraWarnings>True</avrgcc.compiler.warnings.ExtraWarnings>
+ <avrgcc.compiler.warnings.Undefined>True</avrgcc.compiler.warnings.Undefined>
+ <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors>
+ <avrgcc.compiler.warnings.Pedantic>True</avrgcc.compiler.warnings.Pedantic>
+ <avrgcc.compiler.warnings.PedanticWarningsAsErrors>True</avrgcc.compiler.warnings.PedanticWarningsAsErrors>
+ <avrgcc.linker.libraries.Libraries>
+ <ListValues>
+ <Value>libm</Value>
+ </ListValues>
+ </avrgcc.linker.libraries.Libraries>
+ <avrgcc.assembler.general.IncludePaths>
+ <ListValues>
+ <Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.2.132\include</Value>
+ </ListValues>
+ </avrgcc.assembler.general.IncludePaths>
+</AvrGcc>
+ </ToolchainSettings>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <ToolchainSettings>
+ <AvrGcc>
+ <avrgcc.common.Device>-mmcu=atmega16a -B "%24(PackRepoDir)\atmel\ATmega_DFP\1.2.132\gcc\dev\atmega16a"</avrgcc.common.Device>
+ <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
+ <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
+ <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
+ <avrgcc.common.outputfiles.srec>True</avrgcc.common.outputfiles.srec>
+ <avrgcc.common.outputfiles.usersignatures>False</avrgcc.common.outputfiles.usersignatures>
+ <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
+ <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
+ <avrgcc.compiler.symbols.DefSymbols>
+ <ListValues>
+ <Value>DEBUG</Value>
+ </ListValues>
+ </avrgcc.compiler.symbols.DefSymbols>
+ <avrgcc.compiler.directories.IncludePaths>
+ <ListValues>
+ <Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.2.132\include</Value>
+ <Value>..</Value>
+ </ListValues>
+ </avrgcc.compiler.directories.IncludePaths>
+ <avrgcc.compiler.optimization.level>Optimize (-O1)</avrgcc.compiler.optimization.level>
+ <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
+ <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
+ <avrgcc.compiler.optimization.DebugLevel>Default (-g2)</avrgcc.compiler.optimization.DebugLevel>
+ <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
+ <avrgcc.compiler.warnings.ExtraWarnings>True</avrgcc.compiler.warnings.ExtraWarnings>
+ <avrgcc.compiler.warnings.Undefined>True</avrgcc.compiler.warnings.Undefined>
+ <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors>
+ <avrgcc.compiler.warnings.Pedantic>True</avrgcc.compiler.warnings.Pedantic>
+ <avrgcc.compiler.warnings.PedanticWarningsAsErrors>True</avrgcc.compiler.warnings.PedanticWarningsAsErrors>
+ <avrgcc.linker.libraries.Libraries>
+ <ListValues>
+ <Value>libm</Value>
+ </ListValues>
+ </avrgcc.linker.libraries.Libraries>
+ <avrgcc.assembler.general.IncludePaths>
+ <ListValues>
+ <Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.2.132\include</Value>
+ </ListValues>
+ </avrgcc.assembler.general.IncludePaths>
+ <avrgcc.assembler.debugging.DebugLevel>Default (-Wa,-g)</avrgcc.assembler.debugging.DebugLevel>
+</AvrGcc>
+ </ToolchainSettings>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="fan_controller\config.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\config.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\delay.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\delay.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\error.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\error.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\hw\ad_converter.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\hw\ad_converter.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\hw\button.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\hw\button.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\hw\eeprom.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\hw\eeprom.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\hw\fan.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\hw\fan.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\hw\io.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\hw\io.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\hw\led.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\hw\led.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\main.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\median.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\median.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="fan_controller\utils.h">
+ <SubType>compile</SubType>
+ </Compile>
+ </ItemGroup>
+ <ItemGroup>
+ <Folder Include="fan_controller\hw\" />
+ <Folder Include="fan_controller" />
+ </ItemGroup>
+ <Import Project="$(AVRSTUDIO_EXE_PATH)\\Vs\\Compiler.targets" />
+</Project> \ No newline at end of file
diff --git a/FanController/fan_controller/config.c b/FanController/fan_controller/config.c
new file mode 100644
index 0000000..763ad88
--- /dev/null
+++ b/FanController/fan_controller/config.c
@@ -0,0 +1,198 @@
+/* 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/config.h>
+#include <fan_controller/hw/eeprom.h>
+
+/** Hodnota EEPROM ktora oznacuje neinizializovany bajt. */
+#define CONFIG_UNINITIALIZED_VALUE 0xff
+
+/** Hodnota EEPROM ktora oznacuje inizializovany bajt. */
+#define CONFIG_INITIALIZED_VALUE 0x00
+
+/** Adresa v EEPROM, kam sa ulozi priznak inicializacie hodnoty pre mod zobrazenia otacok. */
+#define CONFIG_ADDR_DISP_TYPE_INIT 0x00
+
+/** Adresa v EEPROM, kam sa ulozi nastaveny mod zobrazenia otacok. */
+#define CONFIG_ADDR_DISP_TYPE_VALUE 0x01
+
+/** Adresa v EEPROM, kam sa ulozi priznak inicializacie sirky pulzu ventilatora na max. otackach. */
+#define CONFIG_ADDR_FAN_MAX_INIT 0x10
+
+/** Adresa v EEPROM, kam sa ulozi sirka pulzu ventilatora na max. otackach. */
+#define CONFIG_ADDR_FAN_MAX_VALUE 0x11
+
+/** Adresa v EEPROM, kam sa ulozi priznak inicializacie sirky pulzu ventilatora na min. otackach. */
+#define CONFIG_ADDR_FAN_MIN_INIT 0x20
+
+/** Adresa v EEPROM, kam sa ulozi sirka pulzu ventilatora na min. otackach. */
+#define CONFIG_ADDR_FAN_MIN_VALUE 0x21
+
+/** Predvoleny mod zobrazenia otacok, ak este nebol ziadny zvoleny. */
+#define DISPLAY_TYPE_DEFAULT DISPLAY_TYPE_SET
+
+static bool config_is_initialized(uint16_t address);
+static void config_save(uint16_t address_init, uint16_t address_data, const uint8_t * const data, uint16_t data_size);
+
+/**
+ * @brief Zistenie, ci je hodnota v EEPROM inicializovana.
+ *
+ * @param address Adresa bajtu v EEPROM.
+ * @retval true Hodnota je inicializovana.
+ * @retval false Hodnota nie je inicializovana.
+ */
+bool
+config_is_initialized(uint16_t address)
+{
+ uint8_t b;
+
+ eeprom_read(address, &b, sizeof(b));
+
+ return (b != CONFIG_UNINITIALIZED_VALUE);
+}
+
+/**
+ * @brief Ulozenie dat do EEPROM a nastavenie priznakoveho bajtu inicializacie dat.
+ *
+ * @param address_init Adresa priznakoveho bajtu inicializacie dat.
+ * @param address_data Adresa kam budu zapisane data.
+ * @param data Ukazovatel na buffer dat pre zapis.
+ * @param data_size Pocet bajtov pre zapis.
+ */
+void
+config_save(uint16_t address_init, uint16_t address_data, const uint8_t * const data, uint16_t data_size)
+{
+ // Zapise sa hodnota
+ eeprom_write(address_data, data, data_size);
+ // Zapise sa priznak inicializacie hodnoty
+ uint8_t i = CONFIG_INITIALIZED_VALUE;
+ eeprom_write(address_init, &i, sizeof(i));
+}
+
+/**
+ * @brief Zistenie, ci je type zobrazenia ulozeny v EEPROM inicializovany.
+ *
+ * @retval true Typ je inicializovany.
+ * @retval false Typ nie je inicializovany.
+ */
+bool
+config_disp_type_is_initialized(void)
+{
+ return config_is_initialized(CONFIG_ADDR_DISP_TYPE_INIT);
+}
+
+/**
+ * @brief Ulozenie typu zobrazenie do EEPROM.
+ *
+ * @param disp_type Typ zobrazenia.
+ */
+void
+config_disp_type_save(uint8_t disp_type)
+{
+ config_save(CONFIG_ADDR_DISP_TYPE_INIT, CONFIG_ADDR_DISP_TYPE_VALUE, &disp_type, sizeof(disp_type));
+}
+
+/**
+ * @brief Nacitanie typu zobrazenia z EEPROM.
+ *
+ * Ak este nebolo ziadne zobrazenie ulozene, ulozi sa predvolena hodnota a ta sa aj vrati volajucemu.
+ *
+ * @return Typ zobrazenia.
+ */
+uint8_t
+config_disp_type_load(void)
+{
+ uint8_t display_type = DISPLAY_TYPE_NONE;
+
+ if (config_disp_type_is_initialized()) {
+ eeprom_read(CONFIG_ADDR_DISP_TYPE_VALUE, &display_type, sizeof(display_type));
+ }
+
+ if (display_type >= DISPLAY_TYPE_COUNT) {
+ display_type = DISPLAY_TYPE_DEFAULT;
+ config_disp_type_save(display_type);
+ }
+
+ return display_type;
+}
+
+/**
+ * @brief Zisti, ci je hodnota sirky pulzu na max. otackach inicializovana v EEPROM.
+ *
+ * @retval true Hodnota je inicializovana.
+ * @retval false Hodnota nie je inicializovana.
+ */
+bool
+config_is_fan_max_rpm_pulse_time_init(void)
+{
+ return config_is_initialized(CONFIG_ADDR_FAN_MAX_INIT);
+}
+
+/**
+ * @brief Ulozenie sirky pulzu na max. otackach do EEPROM.
+ *
+ * @param pulse_time Sirka pulzu.
+ */
+void
+config_fan_max_rpm_pulse_time_save(uint16_t pulse_time)
+{
+ config_save(CONFIG_ADDR_FAN_MAX_INIT, CONFIG_ADDR_FAN_MAX_VALUE, (uint8_t *) &pulse_time, sizeof(pulse_time));
+}
+
+/**
+ * @brief Nacitanie sirky pulzu na max. otackach z EEPROM.
+ *
+ * @return Sirka pulzu.
+ */
+uint16_t
+config_fan_max_rpm_pulse_time_load(void)
+{
+ uint16_t w;
+ eeprom_read(CONFIG_ADDR_FAN_MAX_VALUE, (uint8_t *) &w, sizeof(w));
+ return w;
+}
+
+/**
+ * @brief Zisti, ci je hodnota sirky pulzu na min. otackach inicializovana v EEPROM.
+ *
+ * @retval true Hodnota je inicializovana.
+ * @retval false Hodnota nie je inicializovana.
+ */
+bool
+config_is_fan_min_rpm_pulse_time_init(void)
+{
+ return config_is_initialized(CONFIG_ADDR_FAN_MIN_INIT);
+}
+
+/**
+ * @brief Ulozenie sirky pulzu na min. otackach do EEPROM.
+ *
+ * @param pulse_time Sirka pulzu.
+ */
+void
+config_fan_min_rpm_pulse_time_save(uint16_t pulse_time)
+{
+ config_save(CONFIG_ADDR_FAN_MIN_INIT, CONFIG_ADDR_FAN_MIN_VALUE, (uint8_t *) &pulse_time, sizeof(pulse_time));
+}
+
+/**
+ * @brief Nacitanie sirky pulzu na min. otackach z EEPROM.
+ *
+ * @return Sirka pulzu.
+ */
+uint16_t
+config_fan_min_rpm_pulse_time_load(void)
+{
+ uint16_t w;
+ eeprom_read(CONFIG_ADDR_FAN_MIN_VALUE, (uint8_t *) &w, sizeof(w));
+ return w;
+}
diff --git a/FanController/fan_controller/config.h b/FanController/fan_controller/config.h
new file mode 100644
index 0000000..33ae44f
--- /dev/null
+++ b/FanController/fan_controller/config.h
@@ -0,0 +1,42 @@
+/* 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 CONFIG_H_
+#define CONFIG_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/** Kody modov zobrazenia otacok a pomocne hodnoty. */
+enum display_type_codes {
+ DISPLAY_TYPE_SET = 0, /**< Otacky nastavene na potenciometri. */
+ DISPLAY_TYPE_REAL, /**< Realne otacky ventilatora. */
+ DISPLAY_TYPE_SET_WITH_OFF, /**< Otacky nastavene na potenciometri s automatickym vypinanim zobrazenia. */
+ DISPLAY_TYPE_REAL_WITH_OFF, /**< Realne otacky ventilatora s automatickym vypinanim zobrazenia. */
+ DISPLAY_TYPE_COUNT, /**< Pocet kodov modov zobrazeni. */
+ DISPLAY_TYPE_OVERRIDE, /**< Mod pre nastavenie otacok na konstantnu hodnotu sa nepocita medzi standardne mody. */
+ DISPLAY_TYPE_NONE /**< Neznamy mod zobrazenia. */
+};
+
+/** Mod zobrazenia na ktory sa prepina, ked uz neexistuje dalsi mod (s vyssim cislom). */
+#define DISPLAY_TYPE_FIRST DISPLAY_TYPE_SET
+
+void config_disp_type_save(uint8_t disp_type);
+uint8_t config_disp_type_load(void);
+bool config_is_fan_max_rpm_pulse_time_init(void);
+void config_fan_max_rpm_pulse_time_save(uint16_t pulse_time);
+uint16_t config_fan_max_rpm_pulse_time_load(void);
+bool config_is_fan_min_rpm_pulse_time_init(void);
+void config_fan_min_rpm_pulse_time_save(uint16_t pulse_time);
+uint16_t config_fan_min_rpm_pulse_time_load(void);
+
+#endif /* CONFIG_H_ */ \ No newline at end of file
diff --git a/FanController/fan_controller/delay.c b/FanController/fan_controller/delay.c
new file mode 100644
index 0000000..9f793b9
--- /dev/null
+++ b/FanController/fan_controller/delay.c
@@ -0,0 +1,113 @@
+/* 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 <fan_controller/delay.h>
+#include <fan_controller/error.h>
+
+/** Pocet milisekund pre jeden tik casovaca. */
+#define DELAY_MS_FOR_1_TIMER_TICK 16U // ms
+
+/** Pristup k hodnote systemoveho casu priamo bez zakazania presusenia (nie bezpecny). */
+#define DELAY_SYSTEM_TIME_UNSAFE (delay_system_time)
+
+static uint32_t delay_get_system_time(void);
+
+/** Systemovy cas. Inkrementuje sa vzdy s kazdym pretecenim Casovaca 2. */
+static volatile uint32_t delay_system_time = 0;
+
+/**
+ * @brief Obsluha pretecenie Casovaca 2. Inkrementuje sa systemovy cas.
+ */
+ISR(TIMER2_OVF_vect)
+{
+ delay_system_time++;
+}
+
+/**
+ * @brief Inicializacia cakania a systemoveho casu.
+ */
+void
+delay_init(void)
+{
+ delay_system_time = 0;
+ // Enable interrupt
+ TIMSK |= (1 << TOIE2);
+}
+
+/**
+ * @brief Blokujuce cakanie zadaneho poctu milisekund. Minimalna hodnota je 16.
+ *
+ * @param ms Pocet milisekund ktore sa bude cakat.
+ */
+void
+delay_ms(unsigned int ms)
+{
+ const uint32_t d = delay_get_deadline_ms(ms);
+ bool end_waiting = false;
+
+ while (!end_waiting) {
+ // Aby sa nemuselo stale zakazovat a povolovat prerusenie, dosiahnutie konca cakanie sa detekuje najprv len priblizne
+ if (DELAY_SYSTEM_TIME_UNSAFE > d) {
+ // Potom sa skontroluje aj presne, tzn. hodnota systemoveho casovaca sa ziska so zakazanim prerusenia.
+ if (delay_get_system_time() > d) {
+ end_waiting = true;
+ }
+ }
+ }
+}
+
+/**
+ * @brief Bezpecne ziskanie hodnoty systemoveho casu.
+ *
+ * Zakaze sa prerusenie od Casovaca 2 pri pristupe k premennej zdielanej s obsluhou prerusenia.
+ *
+ * @return Hodnota systemoveho casovaca.
+ */
+uint32_t
+delay_get_system_time(void)
+{
+ TIMSK &= ~(1 << TOIE2);
+ const uint32_t t = DELAY_SYSTEM_TIME_UNSAFE;
+ TIMSK |= (1 << TOIE2);
+ return t;
+}
+
+/**
+ * @brief Ziskanie buducej hodnoty systemoveho casu, ktora sa dosiahne za zadany pocet milisekund.
+ *
+ * @param ms Pocet milisekund pre cakanie. Minimalna hodnota je 16.
+ * @return Hodnota systemoveho casu, ktora sa dosiahne za zadany pocet milisekund.
+ */
+uint32_t
+delay_get_deadline_ms(uint16_t ms)
+{
+ if (ms < DELAY_MS_FOR_1_TIMER_TICK) {
+ error();
+ }
+ return (delay_get_system_time() + (ms / DELAY_MS_FOR_1_TIMER_TICK));
+}
+
+/**
+ * @brief Zistenie, ci bola prekrocena zadana hodnota systemoveho casovaca.
+ *
+ * @param deadline Hodnota systemoveho casu z funkcie delay_get_deadline_ms().
+ * @retval true Hodnota bola prekrocena.
+ * @retval false Hodnota nebola prekrocena.
+ */
+bool
+delay_has_deadline_expired(uint32_t deadline)
+{
+ return (delay_get_system_time() > deadline);
+} \ No newline at end of file
diff --git a/FanController/fan_controller/delay.h b/FanController/fan_controller/delay.h
new file mode 100644
index 0000000..e652048
--- /dev/null
+++ b/FanController/fan_controller/delay.h
@@ -0,0 +1,24 @@
+/* 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 DELAY_H_
+#define DELAY_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+
+void delay_init(void);
+void delay_ms(uint16_t ms);
+uint32_t delay_get_deadline_ms(uint16_t ms);
+bool delay_has_deadline_expired(uint32_t deadline);
+
+#endif /* DELAY_H_ */ \ No newline at end of file
diff --git a/FanController/fan_controller/error.c b/FanController/fan_controller/error.c
new file mode 100644
index 0000000..66ad2f9
--- /dev/null
+++ b/FanController/fan_controller/error.c
@@ -0,0 +1,47 @@
+/* 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/delay.h>
+#include <fan_controller/error.h>
+#include <fan_controller/hw/led.h>
+
+/** Pocet milisekund periody blikania LED pri chybe. Polovicu periody su LED zasvietene a polovicu zhasnute. */
+#define ERROR_BLINK_PERIOD_MS 1000U // ms
+
+/**
+ * @brief Funkcia pre indikaciu chyby blikanim LED.
+ *
+ * Blikaju vsetky zelene a cervena LED naraz. Tato funkcia
+ * sa vola pri detekcii zastavenia ventilatora ale aj pri
+ * lubovolnej dalsej chybe (napr. chyba argumentov nejakej
+ * funkcie). Spracovanie programu sa z tejto funkcie uz
+ * nedostane.
+ */
+void
+error(void)
+{
+ while (1)
+ {
+ // Zasvietit LED
+ led_min_green_on();
+ led_green_bar_show_by_mask(0xff);
+ led_red_on();
+ // Pockat
+ delay_ms(ERROR_BLINK_PERIOD_MS / 2);
+ // Zhasnut LED
+ led_min_green_off();
+ led_green_bar_show_by_mask(0x00);
+ led_red_off();
+ // Pockat
+ delay_ms(ERROR_BLINK_PERIOD_MS / 2);
+ }
+} \ No newline at end of file
diff --git a/FanController/fan_controller/error.h b/FanController/fan_controller/error.h
new file mode 100644
index 0000000..7493475
--- /dev/null
+++ b/FanController/fan_controller/error.h
@@ -0,0 +1,20 @@
+/* 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 ERROR_H_
+#define ERROR_H_
+
+#include <stdint.h>
+
+void error(void);
+
+#endif /* ERROR_H_ */ \ No newline at end of file
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
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
diff --git a/FanController/fan_controller/median.c b/FanController/fan_controller/median.c
new file mode 100644
index 0000000..03f16d4
--- /dev/null
+++ b/FanController/fan_controller/median.c
@@ -0,0 +1,131 @@
+/* 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 <string.h>
+
+#include <fan_controller/median.h>
+#include <fan_controller/error.h>
+
+static void median_delete_oldest_sample(median_buffer_t * const buf);
+
+/**
+ * @brief Vymazanie najstarsej vzorky z bufferu medianu.
+ *
+ * @param buf Ukazovatel na buffer medianu.
+ */
+void
+median_delete_oldest_sample(median_buffer_t * const buf)
+{
+ // Ziskat index najstarsieho prvku v poli buf->samples
+ const uint8_t oldest_index = buf->age[0];
+
+ if (oldest_index >= buf->number_of_elements) {
+ error();
+ }
+ // Pocet prvkov, ktore sa budu presuvat na prazdne miesto po zmazanom prvku
+ const uint8_t samples_to_move = buf->number_of_elements - oldest_index - 1;
+ // Ak je co presuvat, presunie sa
+ if (samples_to_move > 0) {
+ memmove(buf->samples + oldest_index,
+ buf->samples + oldest_index + 1,
+ samples_to_move * sizeof(buf->samples[0]));
+ }
+
+ // Zmensi sa pocet ulozenych prvkov
+ --(buf->number_of_elements);
+
+ // Aktualizovat pozicie presunutych prvkov vo FIFO bufferi
+ for (uint8_t i = oldest_index; i < (oldest_index + samples_to_move); ++i) {
+ // Prvky sa posunuli k nizsim indexom
+ --(buf->age[(buf->samples[i]).age_index]);
+ }
+
+ // Treba aktualizovat FIFO indexov do buf->samples. Vyhodi sa najstarsi prvok
+ memmove(buf->age,
+ buf->age + 1,
+ buf->number_of_elements * sizeof(buf->samples[0]));
+
+ // Aktualizovat pozicie presunutych prvkov v bufferi vzoriek
+ for (uint8_t i = 0; i < buf->number_of_elements; ++i) {
+ // Prvky sa posunuli k nizsim indexom
+ --(buf->samples[buf->age[i]].age_index);
+ }
+}
+
+/**
+ * @brief Inicializacia bufferu medianu.
+ *
+ * Buffer medianu sa musi vzdy inicializovat pred prvym pouzitim.
+ *
+ * @param buf Ukazovatel na buffer medianu.
+ * @param capacity Kapacita bufferu, tj. max. pocet vzoriek z ktorych sa bude pocitat median.
+ */
+void
+median_init_buffer(median_buffer_t * const buf, uint8_t capacity)
+{
+ if ((capacity < MEDIAN_MIN_SAMPLES) ||(capacity > MEDIAN_MAX_SAMPLES)) {
+ error();
+ }
+ buf->number_of_elements = 0;
+ buf->capacity = capacity;
+}
+
+/**
+ * @brief Vlozenie prvku do bufferu pre median.
+ *
+ * @param buf Ukazovatel na buffer medianu.
+ * @param sample Hodnota pre ulozenie do bufferu medianu.
+ */
+void
+median_save_sample(median_buffer_t * const buf, uint16_t sample)
+{
+ if (buf->number_of_elements >= buf->capacity) {
+ // Buffer je plny, vymaze sa najtarsi prvok
+ median_delete_oldest_sample(buf);
+ }
+
+ // Buffer nie je plny, vzorka sa ulozi na koniec bufferu
+ buf->samples[buf->number_of_elements].value = sample;
+ buf->samples[buf->number_of_elements].age_index = buf->number_of_elements;
+ buf->age[buf->number_of_elements] = buf->number_of_elements;
+
+ // Vzorka sa zaradi na spravne miesto
+ for (uint8_t j = buf->number_of_elements, i = j - 1; j > 0; --i, --j) {
+ if (buf->samples[j].value < buf->samples[i].value) {
+ // Aktualizovat casove znacky
+ // Vzorka na pozicii i pojde na vyssi index
+ ++(buf->age[buf->samples[i].age_index]);
+ // Vzorka na pozicii j pojde na nizsi index
+ --(buf->age[buf->samples[j].age_index]);
+ // Vymenit vzorky
+ median_sample_t t;
+ t = buf->samples[j];
+ buf->samples[j] = buf->samples[i];
+ buf->samples[i] = t;
+ }
+ }
+
+ // Aktualizovat pocet ulozenych prvkov
+ ++(buf->number_of_elements);
+}
+
+/**
+ * @brief Ziskanie medianu z bufferu.
+ *
+ * @param buf Ukazovatel na buffer medianu.
+ * @return Median z doteraz ulozenych hodnot.
+ */
+uint16_t
+median_get(const median_buffer_t * const buf)
+{
+ return (buf->samples[buf->number_of_elements / 2]).value;
+} \ No newline at end of file
diff --git a/FanController/fan_controller/median.h b/FanController/fan_controller/median.h
new file mode 100644
index 0000000..b24cec4
--- /dev/null
+++ b/FanController/fan_controller/median.h
@@ -0,0 +1,42 @@
+/* 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 MEDIAN_H_
+#define MEDIAN_H_
+
+#include <stdint.h>
+
+/** Minimalny pocet vzoriek pre vypocet medianu. */
+#define MEDIAN_MIN_SAMPLES 3U
+
+/** Maximalny pocet vzoriek pre vypocet medianu. */
+#define MEDIAN_MAX_SAMPLES 16U
+
+/** Vzorka pre median. */
+typedef struct {
+ uint16_t value; /**< Hodnota vzorky. */
+ uint8_t age_index; /**< Index do FIFO age v bufferi medianu. */
+} median_sample_t;
+
+/** Buffer pre vypocet medianu. */
+typedef struct {
+ uint8_t number_of_elements; /**< Pocet aktualne ulozenych vzoriek. */
+ uint8_t capacity; /**< Maximalny pocet ulozenych vzoriek. */
+ median_sample_t samples[MEDIAN_MAX_SAMPLES]; /**< Vzorky z ktorych sa pocita median. */
+ uint8_t age[MEDIAN_MAX_SAMPLES]; /**< FIFO. Na indexe 0 je index najstarsej vzorky v poli samples, na indexe 1 je index druhej najstarsej, atd. Pouziva sa pre rychle najdenie najstarsieho prvku. */
+} median_buffer_t;
+
+void median_init_buffer(median_buffer_t * const buf, uint8_t capacity);
+void median_save_sample(median_buffer_t * const buf, uint16_t sample);
+uint16_t median_get(const median_buffer_t * const buf);
+
+#endif /* MEDIAN_H_ */ \ No newline at end of file
diff --git a/FanController/fan_controller/utils.h b/FanController/fan_controller/utils.h
new file mode 100644
index 0000000..9880754
--- /dev/null
+++ b/FanController/fan_controller/utils.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 UTILS_H_
+#define UTILS_H_
+
+/** Nastavenie bitu b v registri r na 1. */
+#define UTILS_SET_BIT(r, b) (r |= (1 << b))
+/** Nastavenie bitu b v registri r na 0. */
+#define UTILS_CLR_BIT(r, b) (r &= ~(1 << b))
+/** Zistenie, ci je bit b v registri r nastaveny na 1. */
+#define UTILS_IS_BIT_SET(r, b) (r & (1 << b))
+
+#endif /* UTILS_H_ */ \ No newline at end of file