diff options
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 |
