Initial commit
This commit is contained in:
commit
161de6dd12
|
@ -0,0 +1,29 @@
|
|||
# Created by https://www.gitignore.io/api/atmelstudio
|
||||
# Edit at https://www.gitignore.io/?templates=atmelstudio
|
||||
|
||||
### AtmelStudio ###
|
||||
## Ignore Atmel Studio temporary files and build results
|
||||
# http://www.atmel.com/microsite/atmel_studio6/
|
||||
|
||||
# Atmel Studio is powered by an older version of Visual Studio,
|
||||
# so most of the project and solution files are the same as VS files,
|
||||
# only prefixed by an `at`.
|
||||
|
||||
#Build Directories
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
.vs/
|
||||
|
||||
#Build Results
|
||||
*.o
|
||||
*.d
|
||||
*.eep
|
||||
*.elf
|
||||
*.hex
|
||||
*.map
|
||||
*.srec
|
||||
|
||||
#User Specific Files
|
||||
*.atsuo
|
||||
|
||||
# End of https://www.gitignore.io/api/atmelstudio
|
|
@ -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("{E66E83B9-2572-4076-B26E-6BE79FF3018A}") = "Uhrensteuerung", "Uhrensteuerung.cppproj", "{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
|
|
@ -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.209\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.209\include\avr\iom1284.h</AbsolutePath>
|
||||
<Attribute></Attribute>
|
||||
<Category>header</Category>
|
||||
<Condition>C</Condition>
|
||||
<FileContentHash>u35tFhJl9qSwWbpsy/24AA==</FileContentHash>
|
||||
<FileVersion></FileVersion>
|
||||
<Name>include/avr/iom1284.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.209\templates\main.c</AbsolutePath>
|
||||
<Attribute>template</Attribute>
|
||||
<Category>source</Category>
|
||||
<Condition>C Exe</Condition>
|
||||
<FileContentHash>GD1k8YYhulqRs6FD1B2Hog==</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.209\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.209\gcc\dev\atmega1284</AbsolutePath>
|
||||
<Attribute></Attribute>
|
||||
<Category>libraryPrefix</Category>
|
||||
<Condition>GCC</Condition>
|
||||
<FileContentHash i:nil="true" />
|
||||
<FileVersion></FileVersion>
|
||||
<Name>gcc/dev/atmega1284</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.209/Atmel.ATmega_DFP.pdsc</PackPath>
|
||||
<PackVersion>1.2.209</PackVersion>
|
||||
<PresentInProject>true</PresentInProject>
|
||||
<ReferenceConditionId>ATmega1284</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>
|
|
@ -0,0 +1,200 @@
|
|||
<?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.CPP</ToolchainName>
|
||||
<ProjectGuid>dce6c7e3-ee26-4d79-826b-08594b9ad897</ProjectGuid>
|
||||
<avrdevice>ATmega1284</avrdevice>
|
||||
<avrdeviceseries>none</avrdeviceseries>
|
||||
<OutputType>Executable</OutputType>
|
||||
<Language>CPP</Language>
|
||||
<OutputFileName>$(MSBuildProjectName)</OutputFileName>
|
||||
<OutputFileExtension>.elf</OutputFileExtension>
|
||||
<OutputDirectory>$(MSBuildProjectDirectory)\$(Configuration)</OutputDirectory>
|
||||
<AssemblyName>Uhrensteuerung</AssemblyName>
|
||||
<Name>Uhrensteuerung</Name>
|
||||
<RootNamespace>Uhrensteuerung</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.atmelice</avrtool>
|
||||
<avrtoolserialnumber>J42700051414</avrtoolserialnumber>
|
||||
<avrdeviceexpectedsignature>0x1E9706</avrdeviceexpectedsignature>
|
||||
<com_atmel_avrdbg_tool_atmelice>
|
||||
<ToolOptions>
|
||||
<InterfaceProperties>
|
||||
<JtagDbgClock>200000</JtagDbgClock>
|
||||
</InterfaceProperties>
|
||||
<InterfaceName>JTAG</InterfaceName>
|
||||
</ToolOptions>
|
||||
<ToolType>com.atmel.avrdbg.tool.atmelice</ToolType>
|
||||
<ToolNumber>J42700051414</ToolNumber>
|
||||
<ToolName>Atmel-ICE</ToolName>
|
||||
</com_atmel_avrdbg_tool_atmelice>
|
||||
<avrtoolinterface>JTAG</avrtoolinterface>
|
||||
<avrtoolinterfaceclock>200000</avrtoolinterfaceclock>
|
||||
<ResetRule>0</ResetRule>
|
||||
<EraseKey />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<ToolchainSettings>
|
||||
<AvrGccCpp>
|
||||
<avrgcc.common.Device>-mmcu=atmega1284 -B "%24(PackRepoDir)\atmel\ATmega_DFP\1.2.209\gcc\dev\atmega1284"</avrgcc.common.Device>
|
||||
<avrgcc.common.optimization.RelaxBranches>True</avrgcc.common.optimization.RelaxBranches>
|
||||
<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>
|
||||
<Value>F_CPU=16000000UL</Value>
|
||||
</ListValues>
|
||||
</avrgcc.compiler.symbols.DefSymbols>
|
||||
<avrgcc.compiler.directories.IncludePaths>
|
||||
<ListValues>
|
||||
<Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.2.209\include</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.miscellaneous.OtherFlags>-std=c++11</avrgcc.compiler.miscellaneous.OtherFlags>
|
||||
<avrgcccpp.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcccpp.compiler.general.ChangeDefaultCharTypeUnsigned>
|
||||
<avrgcccpp.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcccpp.compiler.general.ChangeDefaultBitFieldUnsigned>
|
||||
<avrgcccpp.compiler.symbols.DefSymbols>
|
||||
<ListValues>
|
||||
<Value>NDEBUG</Value>
|
||||
<Value>F_CPU=16000000UL</Value>
|
||||
</ListValues>
|
||||
</avrgcccpp.compiler.symbols.DefSymbols>
|
||||
<avrgcccpp.compiler.directories.IncludePaths>
|
||||
<ListValues>
|
||||
<Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.2.209\include</Value>
|
||||
</ListValues>
|
||||
</avrgcccpp.compiler.directories.IncludePaths>
|
||||
<avrgcccpp.compiler.optimization.level>Optimize for size (-Os)</avrgcccpp.compiler.optimization.level>
|
||||
<avrgcccpp.compiler.optimization.PackStructureMembers>True</avrgcccpp.compiler.optimization.PackStructureMembers>
|
||||
<avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>
|
||||
<avrgcccpp.compiler.warnings.AllWarnings>True</avrgcccpp.compiler.warnings.AllWarnings>
|
||||
<avrgcccpp.compiler.miscellaneous.OtherFlags>-std=c++11</avrgcccpp.compiler.miscellaneous.OtherFlags>
|
||||
<avrgcccpp.linker.libraries.Libraries>
|
||||
<ListValues>
|
||||
<Value>libm</Value>
|
||||
</ListValues>
|
||||
</avrgcccpp.linker.libraries.Libraries>
|
||||
<avrgcccpp.assembler.general.IncludePaths>
|
||||
<ListValues>
|
||||
<Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.2.209\include</Value>
|
||||
</ListValues>
|
||||
</avrgcccpp.assembler.general.IncludePaths>
|
||||
</AvrGccCpp>
|
||||
</ToolchainSettings>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<ToolchainSettings>
|
||||
<AvrGccCpp>
|
||||
<avrgcc.common.Device>-mmcu=atmega1284 -B "%24(PackRepoDir)\atmel\ATmega_DFP\1.2.209\gcc\dev\atmega1284"</avrgcc.common.Device>
|
||||
<avrgcc.common.optimization.RelaxBranches>True</avrgcc.common.optimization.RelaxBranches>
|
||||
<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>
|
||||
<Value>F_CPU=16000000UL</Value>
|
||||
</ListValues>
|
||||
</avrgcc.compiler.symbols.DefSymbols>
|
||||
<avrgcc.compiler.directories.IncludePaths>
|
||||
<ListValues>
|
||||
<Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.2.209\include</Value>
|
||||
</ListValues>
|
||||
</avrgcc.compiler.directories.IncludePaths>
|
||||
<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.miscellaneous.OtherFlags>-std=c++11</avrgcc.compiler.miscellaneous.OtherFlags>
|
||||
<avrgcccpp.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcccpp.compiler.general.ChangeDefaultCharTypeUnsigned>
|
||||
<avrgcccpp.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcccpp.compiler.general.ChangeDefaultBitFieldUnsigned>
|
||||
<avrgcccpp.compiler.symbols.DefSymbols>
|
||||
<ListValues>
|
||||
<Value>DEBUG</Value>
|
||||
<Value>F_CPU=16000000UL</Value>
|
||||
</ListValues>
|
||||
</avrgcccpp.compiler.symbols.DefSymbols>
|
||||
<avrgcccpp.compiler.directories.IncludePaths>
|
||||
<ListValues>
|
||||
<Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.2.209\include</Value>
|
||||
</ListValues>
|
||||
</avrgcccpp.compiler.directories.IncludePaths>
|
||||
<avrgcccpp.compiler.optimization.PackStructureMembers>True</avrgcccpp.compiler.optimization.PackStructureMembers>
|
||||
<avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>
|
||||
<avrgcccpp.compiler.optimization.DebugLevel>Default (-g2)</avrgcccpp.compiler.optimization.DebugLevel>
|
||||
<avrgcccpp.compiler.warnings.AllWarnings>True</avrgcccpp.compiler.warnings.AllWarnings>
|
||||
<avrgcccpp.compiler.miscellaneous.OtherFlags>-std=c++11</avrgcccpp.compiler.miscellaneous.OtherFlags>
|
||||
<avrgcccpp.linker.libraries.Libraries>
|
||||
<ListValues>
|
||||
<Value>libm</Value>
|
||||
</ListValues>
|
||||
</avrgcccpp.linker.libraries.Libraries>
|
||||
<avrgcccpp.assembler.general.IncludePaths>
|
||||
<ListValues>
|
||||
<Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.2.209\include</Value>
|
||||
</ListValues>
|
||||
</avrgcccpp.assembler.general.IncludePaths>
|
||||
<avrgcccpp.assembler.debugging.DebugLevel>Default (-Wa,-g)</avrgcccpp.assembler.debugging.DebugLevel>
|
||||
</AvrGccCpp>
|
||||
</ToolchainSettings>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="clock.cpp">
|
||||
<SubType>compile</SubType>
|
||||
</Compile>
|
||||
<Compile Include="clock.h">
|
||||
<SubType>compile</SubType>
|
||||
</Compile>
|
||||
<Compile Include="config.h">
|
||||
<SubType>compile</SubType>
|
||||
</Compile>
|
||||
<Compile Include="dcf77.cpp">
|
||||
<SubType>compile</SubType>
|
||||
</Compile>
|
||||
<Compile Include="dcf77.h">
|
||||
<SubType>compile</SubType>
|
||||
</Compile>
|
||||
<Compile Include="main.cpp">
|
||||
<SubType>compile</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(AVRSTUDIO_EXE_PATH)\\Vs\\Compiler.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* clock.cpp
|
||||
*
|
||||
* Created: 04.09.2022 17:34:31
|
||||
* Author: Julian Metzler
|
||||
*/
|
||||
|
||||
#include <avr/eeprom.h>
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
// Actual, currently displayed time
|
||||
uint8_t clock_curHour = 0;
|
||||
uint8_t clock_curMinute = 0;
|
||||
|
||||
// Desired time (might be different in case of catching up)
|
||||
uint8_t clock_setHour = 0;
|
||||
uint8_t clock_setMinute = 0;
|
||||
|
||||
// Status data in EEPROM
|
||||
uint32_t* clock_eepromStartAddr = 0;
|
||||
uint16_t clock_eepromWriteCounter = 0;
|
||||
|
||||
// Millisecond counter of last loop execution, used for non-blocking timing
|
||||
uint32_t clock_lastPulseStartTimestamp = 0;
|
||||
|
||||
// Stores if there is a pulse currently ongoing
|
||||
uint8_t clock_pulseOngoing = 0;
|
||||
|
||||
|
||||
void clock_init() {
|
||||
BRIDGE_PWM_DDR |= (1 << BRIDGE_PWM_PIN);
|
||||
BRIDGE_DIR_DDR |= (1 << BRIDGE_DIR_PIN);
|
||||
BRIDGE_DIS_DDR |= (1 << BRIDGE_DIS_PIN);
|
||||
clock_pulseEnd();
|
||||
//clock_initEeprom();
|
||||
clock_eepromStartAddr = clock_findEepromStartAddress();
|
||||
clock_updateDataFromEeprom();
|
||||
clock_setHour = clock_curHour;
|
||||
clock_setMinute = clock_curMinute;
|
||||
}
|
||||
|
||||
void clock_loop(uint32_t loopTimestamp) {
|
||||
if (clock_pulseOngoing) {
|
||||
if ((loopTimestamp - clock_lastPulseStartTimestamp) >= CLOCK_PULSE_DURATION) {
|
||||
clock_pulseEnd();
|
||||
clock_advanceCurrent();
|
||||
clock_writeDataToEeprom();
|
||||
}
|
||||
} else {
|
||||
uint16_t pulsesNeeded = clock_calcPulsesNeeded();
|
||||
if (pulsesNeeded > 0) {
|
||||
clock_pulseStart(clock_getPolarity());
|
||||
clock_lastPulseStartTimestamp = loopTimestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clock_pulseStart(uint8_t polarity) {
|
||||
clock_pulseOngoing = 1;
|
||||
if (polarity) BRIDGE_DIR_PORT |= (1 << BRIDGE_DIR_PIN);
|
||||
BRIDGE_PWM_PORT |= (1 << BRIDGE_PWM_PIN);
|
||||
BRIDGE_DIS_PORT &= ~(1 << BRIDGE_DIS_PIN);
|
||||
}
|
||||
|
||||
void clock_pulseEnd() {
|
||||
BRIDGE_DIS_PORT |= (1 << BRIDGE_DIS_PIN);
|
||||
BRIDGE_PWM_PORT &= ~(1 << BRIDGE_PWM_PIN);
|
||||
BRIDGE_DIR_PORT &= ~(1 << BRIDGE_DIR_PIN);
|
||||
clock_pulseOngoing = 0;
|
||||
}
|
||||
|
||||
void clock_advanceCurrent() {
|
||||
clock_curMinute++;
|
||||
if (clock_curMinute >= 60) {
|
||||
clock_curMinute = 0;
|
||||
clock_curHour++;
|
||||
if (clock_curHour >= 12) {
|
||||
clock_curHour = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clock_advanceSet() {
|
||||
clock_setMinute++;
|
||||
if (clock_setMinute >= 60) {
|
||||
clock_setMinute = 0;
|
||||
clock_setHour++;
|
||||
if (clock_setHour >= 12) {
|
||||
clock_setHour = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clock_setTime(uint8_t hour, uint8_t minute) {
|
||||
clock_setHour = hour % 12;
|
||||
clock_setMinute = minute % 60;
|
||||
}
|
||||
|
||||
uint16_t clock_calcPulsesNeeded() {
|
||||
// Calculate the number of pulses needed to get from the current time to the desired time
|
||||
int16_t numPulses = 0;
|
||||
if (clock_setHour < clock_curHour) {
|
||||
numPulses += (clock_setHour + 12 - clock_curHour) * 60;
|
||||
} else {
|
||||
numPulses += (clock_setHour - clock_curHour) * 60;
|
||||
}
|
||||
if (clock_setMinute < clock_curMinute) {
|
||||
// In this case, we need to subtract because this already involves increasing the hour by one
|
||||
numPulses -= (clock_curMinute - clock_setMinute);
|
||||
} else {
|
||||
numPulses += clock_setMinute - clock_curMinute;
|
||||
}
|
||||
if (numPulses < 0) numPulses += 720;
|
||||
return (uint16_t)numPulses;
|
||||
}
|
||||
|
||||
uint8_t clock_getPolarity() {
|
||||
// Get the polarity required for the UPCOMING minute transition
|
||||
return clock_curMinute & 1;
|
||||
}
|
||||
|
||||
void clock_initEeprom() {
|
||||
for (uint8_t* i = 0; i < (uint8_t*)EEPROM_SIZE; i++) {
|
||||
eeprom_write_byte(i, 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t* clock_findEepromStartAddress() {
|
||||
// Scan through EEPROM to find the first location with less than
|
||||
// the number of cycles specified for wear leveling
|
||||
// Entry format: <16 Bit Write Counter> <8 Bit Hour> <8 Bit Minute>
|
||||
|
||||
uint16_t* startAddr = 0;
|
||||
uint32_t counter = 0;
|
||||
do {
|
||||
counter = eeprom_read_word(startAddr);
|
||||
if (counter < EEPROM_MAX_WRITE_COUNTER) {
|
||||
break;
|
||||
}
|
||||
startAddr += 4;
|
||||
|
||||
// This should only happen after about 35555 years
|
||||
if ((uint32_t*)startAddr > (uint32_t*)(EEPROM_SIZE - 4)) {
|
||||
return 0;
|
||||
}
|
||||
} while(true);
|
||||
return (uint32_t*)startAddr;
|
||||
}
|
||||
|
||||
void clock_updateDataFromEeprom() {
|
||||
uint32_t data = eeprom_read_dword(clock_eepromStartAddr);
|
||||
clock_eepromWriteCounter = data >> 16;
|
||||
clock_curHour = (data >> 8) & 0xFF;
|
||||
clock_curMinute = data & 0xFF;
|
||||
}
|
||||
|
||||
void clock_writeDataToEeprom() {
|
||||
clock_eepromWriteCounter++;
|
||||
|
||||
uint32_t eepromValue = (((uint32_t)clock_eepromWriteCounter) << 16);
|
||||
eepromValue |= (((uint16_t)clock_curHour) << 8);
|
||||
eepromValue |= clock_curMinute;
|
||||
|
||||
eeprom_update_dword(clock_eepromStartAddr, eepromValue);
|
||||
|
||||
if (clock_eepromWriteCounter >= EEPROM_MAX_WRITE_COUNTER) {
|
||||
clock_eepromStartAddr += 4;
|
||||
clock_eepromWriteCounter = 0;
|
||||
|
||||
// This should only happen after about 35555 years
|
||||
if (clock_eepromStartAddr > (uint32_t*)(EEPROM_SIZE - 4)) {
|
||||
clock_eepromStartAddr = 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* clock.h
|
||||
*
|
||||
* Created: 04.09.2022 17:34:40
|
||||
* Author: Julian Metzler
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CLOCK_H_
|
||||
#define CLOCK_H_
|
||||
|
||||
#define BRIDGE_PWM_DDR DDRB
|
||||
#define BRIDGE_PWM_PORT PORTB
|
||||
#define BRIDGE_PWM_PIN 3
|
||||
|
||||
#define BRIDGE_DIR_DDR DDRB
|
||||
#define BRIDGE_DIR_PORT PORTB
|
||||
#define BRIDGE_DIR_PIN 4
|
||||
|
||||
#define BRIDGE_DIS_DDR DDRD
|
||||
#define BRIDGE_DIS_PORT PORTD
|
||||
#define BRIDGE_DIS_PIN 5
|
||||
|
||||
#define EEPROM_MAX_WRITE_COUNTER 50000
|
||||
#define EEPROM_SIZE 4096
|
||||
|
||||
#define CLOCK_PULSE_DURATION 250
|
||||
|
||||
|
||||
void clock_init();
|
||||
void clock_loop(uint32_t loopTimestamp);
|
||||
void clock_pulseStart(uint8_t polarity);
|
||||
void clock_pulseEnd();
|
||||
void clock_advanceCurrent();
|
||||
void clock_advanceSet();
|
||||
void clock_setTime(uint8_t hour, uint8_t minute);
|
||||
uint16_t clock_calcPulsesNeeded();
|
||||
uint8_t clock_getPolarity();
|
||||
void clock_initEeprom();
|
||||
uint32_t* clock_findEepromStartAddress();
|
||||
void clock_updateDataFromEeprom();
|
||||
void clock_writeDataToEeprom();
|
||||
|
||||
#endif /* CLOCK_H_ */
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* config.h
|
||||
*
|
||||
* Created: 03.09.2022 22:31:23
|
||||
* Author: Julian Metzler
|
||||
*/
|
||||
|
||||
#include <avr/io.h>
|
||||
|
||||
|
||||
#ifndef CONFIG_H_
|
||||
#define CONFIG_H_
|
||||
|
||||
#define DCF77_DDR DDRA
|
||||
#define DCF77_PINR PINA
|
||||
#define DCF77_PORT PORTA
|
||||
#define DCF77_PIN 4
|
||||
|
||||
#define DCF77_UPDATE_INTERVAL (60 * 60 * 1000UL) // 1 hour
|
||||
|
||||
#endif /* CONFIG_H_ */
|
|
@ -0,0 +1,196 @@
|
|||
/**
|
||||
* libdcf77 -- Cross Platform C++ DCF77 decoder
|
||||
* Copyright (C) 2016 Andreas Stöckel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dcf77.h"
|
||||
|
||||
namespace dcf77 {
|
||||
|
||||
/******************************************************************************
|
||||
* Class "debounce" *
|
||||
******************************************************************************/
|
||||
|
||||
static constexpr uint8_t FIXED_POINT_LOG2_BASE = 7;
|
||||
static constexpr uint8_t FIXED_POINT_BASE = (1 << FIXED_POINT_LOG2_BASE);
|
||||
static constexpr uint8_t FLT_F1 = FIXED_POINT_BASE * 0.97;
|
||||
static constexpr uint8_t FLT_F2 = FIXED_POINT_BASE - FLT_F1;
|
||||
|
||||
static constexpr uint8_t filter(bool ctrl, uint8_t x)
|
||||
{
|
||||
return (uint16_t(x) * uint16_t(FLT_F1) +
|
||||
(ctrl ? uint16_t(FLT_F2) << FIXED_POINT_LOG2_BASE : 0)) >>
|
||||
FIXED_POINT_LOG2_BASE;
|
||||
}
|
||||
|
||||
static constexpr uint8_t filter_convergence(bool ctrl,
|
||||
uint8_t x = FIXED_POINT_BASE / 2)
|
||||
{
|
||||
return filter(ctrl, x) == x ? x : filter_convergence(ctrl, filter(ctrl, x));
|
||||
}
|
||||
|
||||
static constexpr uint8_t FLT_MAX = filter_convergence(true);
|
||||
static constexpr uint8_t FLT_MIN = filter_convergence(false);
|
||||
|
||||
debounce::debounce(uint8_t hysteresis)
|
||||
: m_low_pass(FIXED_POINT_BASE / 2), m_last_t(0), m_last_state_change(0),
|
||||
m_hysteresis((uint16_t(hysteresis) * (FLT_MAX - FLT_MIN)) >> 8),
|
||||
m_last_input_value(false)
|
||||
{
|
||||
}
|
||||
|
||||
const debounce::result &debounce::sample(bool value, uint16_t t)
|
||||
{
|
||||
// Apply a low-pass filter to the input signal
|
||||
const uint16_t dt = t - m_last_t;
|
||||
uint8_t lv = m_low_pass;
|
||||
for (uint16_t i = 0; i < dt; i++) {
|
||||
m_low_pass = filter(value, m_low_pass);
|
||||
if (m_low_pass == lv) {
|
||||
break;
|
||||
}
|
||||
lv = m_low_pass;
|
||||
}
|
||||
|
||||
// Remember the time of the last state change
|
||||
if (value != m_last_input_value) {
|
||||
m_last_state_change = t;
|
||||
}
|
||||
|
||||
// Assemble the result structure, apply the hysteresis
|
||||
if (m_low_pass > FLT_MAX - m_hysteresis && m_result.value == false) {
|
||||
m_result.t = m_last_state_change;
|
||||
m_result.edge = true;
|
||||
m_result.value = true;
|
||||
} else if (m_low_pass < FLT_MIN + m_hysteresis && m_result.value == true) {
|
||||
m_result.t = m_last_state_change;
|
||||
m_result.edge = true;
|
||||
m_result.value = false;
|
||||
} else {
|
||||
m_result.edge = false;
|
||||
}
|
||||
|
||||
// Remember time and input/output values
|
||||
m_last_t = t;
|
||||
m_last_input_value = value;
|
||||
|
||||
return m_result;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Union "data" *
|
||||
******************************************************************************/
|
||||
|
||||
template <typename T>
|
||||
uint8_t parity(T x)
|
||||
{
|
||||
// __builtin_popcount is 16 bits only, so call it twice
|
||||
// return __builtin_popcount(x) & 1;
|
||||
uint8_t result = 0;
|
||||
result += __builtin_popcount(x >> 16);
|
||||
result += __builtin_popcount(x & 0xFFFF);
|
||||
return result & 1;
|
||||
}
|
||||
|
||||
template <uint8_t max_hi, uint8_t max_lo, typename T>
|
||||
static bool valid_bcd(T x)
|
||||
{
|
||||
const uint8_t hi = (x & 0xF0) >> 4;
|
||||
const uint8_t lo = (x & 0x0F) >> 0;
|
||||
|
||||
// The individual digits must not be larger than 9!
|
||||
if (hi > 9 || lo > 9) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The first digit must not be larger than the maximum first digit
|
||||
if (hi > max_hi) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the first digit is equal to the maximum first digit, the second digit
|
||||
// must not be larger than the maximum second digit
|
||||
if (hi == max_hi && lo > max_lo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
bool data::valid(bool time_and_date_only) const
|
||||
{
|
||||
return (time_and_date_only || raw.minute_start == 0) &&
|
||||
(raw.time_start == 1) // Constant flags
|
||||
&&
|
||||
(time_and_date_only || raw.cest != raw.cet) // There can be only one!
|
||||
&& (raw.parity_minute == parity(raw.minute)) && // Check parity
|
||||
(raw.parity_hour == parity(raw.hour)) &&
|
||||
(raw.parity_date ==
|
||||
parity(uint32_t((bitstream & 0x3FFFFF000000000LL) >> 32))) &&
|
||||
valid_bcd<5, 9>(raw.minute) && // Check BCD values
|
||||
valid_bcd<2, 3>(raw.hour) && valid_bcd<3, 1>(raw.day) &&
|
||||
(raw.day > 0) && (raw.day_of_week > 0) &&
|
||||
valid_bcd<1, 2>(raw.month) && (raw.month > 0) &&
|
||||
valid_bcd<9, 9>(raw.year);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Class "decoder" *
|
||||
******************************************************************************/
|
||||
|
||||
decoder::state decoder::sample(bool value, uint16_t t)
|
||||
{
|
||||
auto event = m_debouncer.sample(value, t);
|
||||
state res = state::no_result;
|
||||
if (event.edge) {
|
||||
uint16_t dt = event.t - m_last_t;
|
||||
if (!event.value) {
|
||||
// Falling edge
|
||||
if (dt > SYNC_HIGH_TIME - SLACK) {
|
||||
// Handle a sync event
|
||||
if (m_state < 59) {
|
||||
m_data_new.bitstream = m_data_new.bitstream
|
||||
<< (59 - m_state);
|
||||
if (m_data_new.valid(true)) {
|
||||
res = state::has_time_and_date;
|
||||
}
|
||||
} else if (m_data_new.valid(false)) {
|
||||
res = state::has_complete;
|
||||
}
|
||||
if (res >= state::has_time_and_date) {
|
||||
m_data_current = m_data_new;
|
||||
m_phase = event.t;
|
||||
}
|
||||
m_state = 0;
|
||||
m_data_new.bitstream = 0;
|
||||
}
|
||||
}
|
||||
if (event.value) {
|
||||
// Rising edge
|
||||
if (dt > LOW_ZERO_TIME - SLACK) {
|
||||
// We received a "one" or a "zero"
|
||||
if (dt > LOW_ONE_TIME - SLACK) {
|
||||
// It's a "one"
|
||||
m_data_new.bitstream |= uint64_t(1) << m_state;
|
||||
}
|
||||
m_state++;
|
||||
}
|
||||
}
|
||||
m_last_t = event.t;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,447 @@
|
|||
/**
|
||||
* libdcf77 -- Cross Platform C++ DCF77 decoder
|
||||
* Copyright (C) 2016 Andreas Stöckel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file dcf77.hpp
|
||||
*
|
||||
* Implementation of a decoder of the time signal sent by the DCF77 station in
|
||||
* Mainflingen, Germany near Frankfurt on 77.5kHz.
|
||||
*
|
||||
* @author Andreas Stöckel
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Namespace encompassing all types used in the DCF77 decoder.
|
||||
*/
|
||||
namespace dcf77 {
|
||||
|
||||
/**
|
||||
* A digital low-pass filter with Schmitt-Trigger and user-definable
|
||||
* hysteresis. The filter is a simple finite impulse response filter with a
|
||||
* single coefficient (also known as moving average or exponential filter). As
|
||||
* an important feature for time signal analysis, the debounce filter allows to
|
||||
* recover the current input signal phase, allowing for a reconstruction of the
|
||||
* current time.
|
||||
*/
|
||||
class debounce {
|
||||
public:
|
||||
/**
|
||||
* Structure describing the output of the debounce filter.
|
||||
*/
|
||||
struct result {
|
||||
/**
|
||||
* Timestamp at which the state transition occured.
|
||||
*/
|
||||
uint16_t t;
|
||||
|
||||
/**
|
||||
* Current output state.
|
||||
*/
|
||||
bool value : 1;
|
||||
|
||||
/**
|
||||
* True if a state transition occured just now.
|
||||
*/
|
||||
bool edge : 1;
|
||||
|
||||
result() : t(0), value(false), edge(false) {}
|
||||
};
|
||||
|
||||
private:
|
||||
/**
|
||||
* Low-pass filtered input value.
|
||||
*/
|
||||
uint8_t m_low_pass;
|
||||
|
||||
/**
|
||||
* Last timestamp passed to the sample() function.
|
||||
*/
|
||||
uint16_t m_last_t;
|
||||
|
||||
/**
|
||||
* Time of the last raw state change.
|
||||
*/
|
||||
uint16_t m_last_state_change;
|
||||
|
||||
/**
|
||||
* User-supplied hysteresis.
|
||||
*/
|
||||
uint8_t m_hysteresis;
|
||||
|
||||
/**
|
||||
* Last input value received by the sample function.
|
||||
*/
|
||||
bool m_last_input_value;
|
||||
|
||||
/**
|
||||
* Current/last result.
|
||||
*/
|
||||
result m_result;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor of the debounce class with user-definable hysteresis.
|
||||
*
|
||||
* @param hysteresis is a value between zero and 255, which is mapped to a
|
||||
* value between zero and one hundred percent (for example, the default
|
||||
* value of 64 corresponds to 25 percent). This percentage p is then used to
|
||||
* derive the values at which the output is switched: if the current output
|
||||
* of the filter is zero, and the low-pass filtered value reaches 1 - p, the
|
||||
* filter output is set to one, otherwise, if the current filter output is
|
||||
* one and the low-pass filtered values reaches p, the output is set to
|
||||
* zero.
|
||||
*/
|
||||
debounce(uint8_t hysteresis = 64);
|
||||
|
||||
/**
|
||||
* Processes a new sample.
|
||||
*
|
||||
* @param value is the input bit.
|
||||
* @param t is a monotonous timestamp in milliseconds. This value is used
|
||||
* to determine the* number of filter steps. The longer the time that has
|
||||
* passed since the last call to "sample", the more filter steps are
|
||||
* required.
|
||||
*/
|
||||
const result &sample(bool value, uint16_t t);
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
/**
|
||||
* The data union stores the data received from the DCF77 radio station. It
|
||||
* provides a view on both the incomming bitstream as a 64-bit integer, as well
|
||||
* as the corresponding raw fields. The access methods provided by this class
|
||||
* can be used to validate and read the decoded data in a convenient manner.
|
||||
* Data should only be used if the valid() method evaluates to true. When using
|
||||
* the decoder class, this check is already performed by the sample() method.
|
||||
* The return value of that method informs about the validity of the
|
||||
* information.
|
||||
*/
|
||||
union data {
|
||||
/**
|
||||
* Contains the raw fields as received from the DCF77 station. Except for
|
||||
* special applications, use the access methods below to read the data.
|
||||
*/
|
||||
struct {
|
||||
/**
|
||||
* First minute bit -- should always be set to zero.
|
||||
*/
|
||||
uint8_t minute_start : 1;
|
||||
|
||||
/**
|
||||
* Auxiliary data -- used to tranmit alarms and weather data. The latter
|
||||
* is encrypted.
|
||||
*/
|
||||
uint16_t aux_data : 14;
|
||||
|
||||
/**
|
||||
* Used to inform about an irregularity at the station.
|
||||
*/
|
||||
uint8_t call_bit : 1;
|
||||
|
||||
/**
|
||||
* If true, there's a swtich from CET to CEST or vice versa at the end
|
||||
* of this hour.
|
||||
*/
|
||||
uint8_t dst_leap_hour : 1;
|
||||
|
||||
/**
|
||||
* The time described by this transmission is central european summer
|
||||
* time (CEST).
|
||||
*/
|
||||
uint8_t cest : 1;
|
||||
|
||||
/**
|
||||
* The time described by this transmission is central european time
|
||||
* (CET).
|
||||
*/
|
||||
uint8_t cet : 1;
|
||||
|
||||
/**
|
||||
* This hour will end with a leap second.
|
||||
*/
|
||||
uint8_t leap_second : 1;
|
||||
|
||||
/**
|
||||
* Time start indicator. Always set to one.
|
||||
*/
|
||||
uint8_t time_start : 1;
|
||||
|
||||
/**
|
||||
* The minute as 2-digit binary coded decimal (BCD).
|
||||
*/
|
||||
uint8_t minute : 7;
|
||||
|
||||
/**
|
||||
* Even parity of the minute bits.
|
||||
*/
|
||||
uint8_t parity_minute : 1;
|
||||
|
||||
/**
|
||||
* The current hour as 2-digit binary coded decimal (BCD).
|
||||
*/
|
||||
uint8_t hour : 6;
|
||||
|
||||
/**
|
||||
* Even parity of the minute bits.
|
||||
*/
|
||||
uint8_t parity_hour : 1;
|
||||
|
||||
/**
|
||||
* The current day of the month as 2-digit binary coded decimal (BCD).
|
||||
*/
|
||||
uint8_t day : 6;
|
||||
|
||||
/**
|
||||
* The current day of the week from one to seven.
|
||||
*/
|
||||
uint8_t day_of_week : 3;
|
||||
|
||||
/**
|
||||
* The current month as a value from one to twelve.
|
||||
*/
|
||||
uint8_t month : 5;
|
||||
|
||||
/**
|
||||
* The current year as a two digit BCD.
|
||||
*/
|
||||
uint8_t year : 8;
|
||||
|
||||
/**
|
||||
* Even parity of the day, day_of_week, month and year bits.
|
||||
*/
|
||||
uint8_t parity_date : 1;
|
||||
|
||||
/**
|
||||
* Additional bit used for leap seconds. Is always set to zero.
|
||||
*/
|
||||
uint8_t leap_second_bit : 1;
|
||||
} raw;
|
||||
|
||||
/**
|
||||
* Integer representing the DCF77 data. The decoder directly sets the bits
|
||||
* in this integer. The data can then be read using the bitfields in the
|
||||
* "raw" structure.
|
||||
*/
|
||||
uint64_t bitstream;
|
||||
|
||||
/**
|
||||
* Initialises an empty DCF77 data object.
|
||||
*/
|
||||
data() : bitstream(0) {}
|
||||
|
||||
/**
|
||||
* Validates the data contained in this object. Checks the constant flags,
|
||||
* the parity and the numerical values for validity. If incomplete data
|
||||
* has been read, the "time_and_date_only" flag can be used to skip
|
||||
* validation of the parts which are not relevant for time and date.
|
||||
*
|
||||
* @param time_and_date_only if true, does not check the validity of the
|
||||
* first 19 bits of the data stream.
|
||||
*/
|
||||
bool valid(bool time_and_date_only = false) const;
|
||||
|
||||
/**
|
||||
* Used to decode two-digit bcd values to bits.
|
||||
*/
|
||||
static uint8_t decode_bcd(uint8_t v)
|
||||
{
|
||||
uint8_t res = v & 0x0F;
|
||||
if (v & 0x10) {
|
||||
res += 10;
|
||||
}
|
||||
if (v & 0x20) {
|
||||
res += 20;
|
||||
}
|
||||
if (v & 0x40) {
|
||||
res += 40;
|
||||
}
|
||||
if (v & 0x80) {
|
||||
res += 80;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, daylight_saving is currently active.
|
||||
*/
|
||||
bool daylight_saving() const { return raw.cest; }
|
||||
|
||||
/**
|
||||
* If true, the timestamps will swtich from CEST to CET or vice versa in the
|
||||
* next hour.
|
||||
*/
|
||||
bool daylight_saving_leap_hour() const { return raw.dst_leap_hour; }
|
||||
|
||||
/**
|
||||
* If true, the current hour ends with a leap second.
|
||||
*/
|
||||
bool leap_second() const { return raw.leap_second; }
|
||||
|
||||
/**
|
||||
* Minute encoded in the transmission (0 to 59).
|
||||
*/
|
||||
uint8_t minute() const { return decode_bcd(raw.minute); }
|
||||
|
||||
/**
|
||||
* Hour encoded in the transmission (0 to 23).
|
||||
*/
|
||||
uint8_t hour() const { return decode_bcd(raw.hour); }
|
||||
|
||||
/**
|
||||
* Day encoded in the transmission (1 to 31).
|
||||
*/
|
||||
uint8_t day() const { return decode_bcd(raw.day); }
|
||||
|
||||
/**
|
||||
* Day of the week (1 to 7), a value of one corresponds to Monday.
|
||||
*/
|
||||
uint8_t day_of_week() const { return raw.day_of_week; }
|
||||
|
||||
/**
|
||||
* Current month (1 to 12), one is January.
|
||||
*/
|
||||
uint8_t month() const { return decode_bcd(raw.month); }
|
||||
|
||||
/**
|
||||
* Current year. Assumes we are in the 21th century.
|
||||
*/
|
||||
uint16_t year() const { return decode_bcd(raw.year) + 2000; }
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
/**
|
||||
* The DCF77 decoder class allows to decode the DCF77 signal. It performs phase
|
||||
* recovery, input signal low-pass filtering with hysteresis and data
|
||||
* validation.
|
||||
*/
|
||||
class decoder {
|
||||
public:
|
||||
/**
|
||||
* Enum describing the state of the decoder.
|
||||
*/
|
||||
enum class state : int8_t {
|
||||
/**
|
||||
* Currently, there is no result available.
|
||||
*/
|
||||
no_result = 0,
|
||||
|
||||
/**
|
||||
* A synchronisation bit has been received, but the data could not
|
||||
* be validated.
|
||||
*/
|
||||
invalid_result = -1,
|
||||
|
||||
/**
|
||||
* Time and date have been received and are valid, but supplementary
|
||||
* information is missing.
|
||||
*/
|
||||
has_time_and_date = 1,
|
||||
|
||||
/**
|
||||
* An entire dataset is available.
|
||||
*/
|
||||
has_complete = 2
|
||||
};
|
||||
|
||||
private:
|
||||
/**
|
||||
* All measured time values may be smaller than the nominal value by this
|
||||
* value (in milliseconds).
|
||||
*/
|
||||
static constexpr uint16_t SLACK = 50;
|
||||
|
||||
/**
|
||||
* Synchronisation time in milliseconds. The carrier has a high amplitude
|
||||
* for this amount of time.
|
||||
*/
|
||||
static constexpr uint16_t SYNC_HIGH_TIME = 1800;
|
||||
|
||||
/**
|
||||
* Time for which the carrier has a low value when encoding a zero-bit.
|
||||
*/
|
||||
static constexpr uint16_t LOW_ZERO_TIME = 100;
|
||||
|
||||
/**
|
||||
* Time for which the carrier has a low value when encoding a one-bit.
|
||||
*/
|
||||
static constexpr uint16_t LOW_ONE_TIME = 200;
|
||||
|
||||
/**
|
||||
* Instance of the "debouncer" class used to software-filter the input
|
||||
* signal.
|
||||
*/
|
||||
debounce m_debouncer;
|
||||
|
||||
/**
|
||||
* Timestamp at which the falling edge corresponding to the start of the
|
||||
* first second in a minute was received. Only updated when valid data is
|
||||
* received.
|
||||
*/
|
||||
uint16_t m_phase = 0;
|
||||
|
||||
/**
|
||||
* Last timestamp when the "sample" method was called.
|
||||
*/
|
||||
uint16_t m_last_t = 0;
|
||||
|
||||
/**
|
||||
* Working data register. Incomming bits are written to this variable.
|
||||
*/
|
||||
data m_data_new;
|
||||
|
||||
/**
|
||||
* Valid data store. Valid received transmissions are stored in this
|
||||
* variable.
|
||||
*/
|
||||
data m_data_current;
|
||||
|
||||
/**
|
||||
* Current bit into which data is written.
|
||||
*/
|
||||
uint8_t m_state = 0;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Pushes a new input sample into the decoder.
|
||||
*
|
||||
* @param value is the current value of the DCF77 carrier amplitude. "True"
|
||||
* corresponds to a high amplitude, "False" to a low amplitude. Depending
|
||||
* on the receiving circuitry you may have to invert this signal.
|
||||
* @param t is a monotonously increasing timestamp in milliseconds. This
|
||||
* time stamp can for example be generated by a simple one-millisecond
|
||||
* timer.
|
||||
* @return the decoder state. If has_time_and_date or has_complete is
|
||||
* returned, the time data can be read via get_data() and the information
|
||||
* can be accessed using the get_phase() method.
|
||||
*/
|
||||
state sample(bool value, uint16_t t);
|
||||
|
||||
/**
|
||||
* Returns the timestamp at which the end of the last valid synchronisation
|
||||
* pulse was received.
|
||||
*/
|
||||
uint16_t get_phase() const { return m_phase; }
|
||||
|
||||
/**
|
||||
* Returns a reference at the last validated time data.
|
||||
*/
|
||||
const data &get_data() const { return m_data_current; }
|
||||
};
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Uhrensteuerung.cpp
|
||||
*
|
||||
* Created: 03.09.2022 22:29:19
|
||||
* Author : Julian Metzler
|
||||
*/
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/sleep.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "clock.h"
|
||||
#include "dcf77.h"
|
||||
|
||||
|
||||
dcf77::decoder dcf77_dec;
|
||||
static volatile uint32_t t = 0;
|
||||
uint32_t dcf77_lastUpdate = 0;
|
||||
uint32_t minute_lastUpdate = 0;
|
||||
uint8_t dcf77_initial = 1;
|
||||
ISR(TIMER1_COMPA_vect) { t++; }
|
||||
|
||||
|
||||
int main(void) {
|
||||
clock_init();
|
||||
|
||||
// Set up pin directions
|
||||
DCF77_DDR &= ~(1 << DCF77_PIN);
|
||||
DCF77_PORT |= (1 << DCF77_PIN);
|
||||
|
||||
// Configure a millisecond timer
|
||||
TCCR1B = (1 << WGM12) | (1 << CS10);
|
||||
OCR1A = F_CPU / 1000;
|
||||
TIMSK1 = (1 << OCIE1A);
|
||||
sei();
|
||||
|
||||
//clock_setTime(1, 23);
|
||||
//while(1) clock_loop(t);
|
||||
|
||||
while (1) {
|
||||
if (dcf77_initial || (t - dcf77_lastUpdate) >= DCF77_UPDATE_INTERVAL) {
|
||||
const bool dcf77_signal = !(DCF77_PINR & (1 << DCF77_PIN));
|
||||
if (dcf77_dec.sample(!dcf77_signal, t) >=
|
||||
dcf77::decoder::state::has_time_and_date) {
|
||||
auto &d = dcf77_dec.get_data();
|
||||
// Valid data has been received, do something with it
|
||||
clock_setTime(d.hour(), d.minute());
|
||||
dcf77_lastUpdate = t;
|
||||
minute_lastUpdate = t;
|
||||
dcf77_initial = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((t - minute_lastUpdate) >= 60000) {
|
||||
clock_advanceSet();
|
||||
minute_lastUpdate = t;
|
||||
}
|
||||
|
||||
clock_loop(t);
|
||||
|
||||
// Wait for the next millisecond
|
||||
set_sleep_mode(SLEEP_MODE_IDLE);
|
||||
sleep_mode();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* utils.cpp
|
||||
*
|
||||
* Created: 03.09.2022 22:50:15
|
||||
* Author: Julian
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
uint32_t min(uint32_t a, uint32_t b) {
|
||||
if (a < b) return a;
|
||||
return b;
|
||||
}
|
||||
|
||||
uint32_t max(uint32_t a, uint32_t b) {
|
||||
if (a > b) return a;
|
||||
return b;
|
||||
}
|
Loading…
Reference in New Issue