24 Commits

Author SHA1 Message Date
2147637385 Update submodule 2020-05-17 20:11:51 +02:00
966af1d5cf Add ability to read alarm times 2020-05-17 20:07:13 +02:00
a83671bda5 Add alarms to example 2020-05-17 19:56:11 +02:00
7e8a6b0dad Add power control for RTC 2020-05-17 15:40:29 +02:00
a897b7d9be Move comparison operator into library 2020-05-17 15:40:15 +02:00
f8ee536829 Only pass i2c backend to rtc class 2020-05-16 19:52:49 +02:00
2fd64c8611 Implement setting time through serial 2020-05-16 19:48:37 +02:00
97bb522189 Remove old rtc lib and systime 2020-05-16 19:20:32 +02:00
b8c7c9ec5c Adapt to moved type submodule 2020-05-16 17:44:16 +02:00
5d77cad8d7 Add setting date-time example with new rtc lib 2020-05-16 17:26:56 +02:00
4b3dd99ebc Add usage for new C++ driver 2020-05-15 19:48:22 +02:00
f00a154e59 Change i2c library 2020-05-15 11:51:51 +02:00
e9d3a95239 Move C time API glue from driver to user space 2020-05-15 10:21:34 +02:00
b2065b987a Switch from old usart to new uart library 2020-05-15 09:43:06 +02:00
85e37de950 Ran clang-format 2020-05-15 09:29:26 +02:00
7310eb2c9a Remove bootloader support 2020-05-15 09:28:05 +02:00
b02454c45b Update submodule 2020-05-15 09:26:08 +02:00
af4f2ed0e5 Update project file to C++17 config with gcc 9 2020-05-15 09:24:24 +02:00
7a8d31718f Add gitignore and clang-format file 2020-05-15 09:21:08 +02:00
05e8b1b53f Change to 16 MHz crystal 2020-05-15 09:14:31 +02:00
a340cb04d3 Actually use ds3231 submodule instead of duplicating code 2020-05-15 09:06:34 +02:00
4cffd8f56b Fix submodule path 2020-05-15 09:01:11 +02:00
6072c34eef Fixed setting time bug, because of uninitialized tm struct 2018-01-02 17:21:04 +01:00
4bc59d1a33 Changed wrong CPU FREQ define to central correct one 2017-12-17 10:53:38 +01:00
19 changed files with 534 additions and 459 deletions

13
.gitignore vendored
View File

@@ -1,2 +1,11 @@
build/
.cache/
.vs
Release
Debug
*.componentinfo.xml
*.elf
*.o
*.hex
*.srec
*.eeprom
*.lss
*.map

18
.gitmodules vendored Normal file
View File

@@ -0,0 +1,18 @@
[submodule "ds3231/ds3231"]
path = ds3231/ds3231
url = git@git.blackmark.me:avr/ds3231.git
[submodule "ds3231/uart"]
path = ds3231/uart
url = git@git.blackmark.me:avr/uart.git
[submodule "ds3231/io"]
path = ds3231/io
url = git@git.blackmark.me:avr/io.git
[submodule "ds3231/flash"]
path = ds3231/flash
url = git@git.blackmark.me:avr/flash.git
[submodule "ds3231/i2c"]
path = ds3231/i2c
url = git@git.blackmark.me:avr/i2c.git
[submodule "ds3231/util"]
path = ds3231/util
url = git@git.blackmark.me:avr/util.git

View File

@@ -1,25 +0,0 @@
cmake_minimum_required(VERSION 3.28)
project(ds3231 LANGUAGES CXX)
# libavr comes from a local checkout (LIBAVR_ROOT cache/env variable) or,
# failing that, straight from the forge. The toolchain file also lives in
# that checkout — see CMakePresets.json.
include(FetchContent)
if(NOT LIBAVR_ROOT AND DEFINED ENV{LIBAVR_ROOT})
set(LIBAVR_ROOT $ENV{LIBAVR_ROOT})
endif()
if(LIBAVR_ROOT)
FetchContent_Declare(libavr SOURCE_DIR ${LIBAVR_ROOT})
else()
FetchContent_Declare(libavr GIT_REPOSITORY git@git.blackmark.me:avr/libavr.git GIT_TAG main)
endif()
FetchContent_MakeAvailable(libavr)
add_library(ds3231 INTERFACE)
target_include_directories(ds3231 INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(ds3231 INTERFACE libavr)
if(PROJECT_IS_TOP_LEVEL)
add_subdirectory(example)
endif()

View File

@@ -1,43 +0,0 @@
{
"version": 8,
"configurePresets": [
{
"name": "base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"toolchainFile": "$env{LIBAVR_ROOT}/cmake/avr-toolchain.cmake",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
"CMAKE_COLOR_DIAGNOSTICS": "ON"
}
},
{
"name": "attiny85-generated",
"inherits": "base",
"cacheVariables": { "LIBAVR_MCU": "attiny85", "LIBAVR_REFLECT": "OFF" }
},
{
"name": "attiny85-reflect",
"inherits": "base",
"cacheVariables": { "LIBAVR_MCU": "attiny85", "LIBAVR_REFLECT": "ON" }
},
{
"name": "atmega328p-generated",
"inherits": "base",
"cacheVariables": { "LIBAVR_MCU": "atmega328p", "LIBAVR_REFLECT": "OFF" }
},
{
"name": "atmega328p-reflect",
"inherits": "base",
"cacheVariables": { "LIBAVR_MCU": "atmega328p", "LIBAVR_REFLECT": "ON" }
}
],
"buildPresets": [
{ "name": "attiny85-generated", "configurePreset": "attiny85-generated" },
{ "name": "attiny85-reflect", "configurePreset": "attiny85-reflect" },
{ "name": "atmega328p-generated", "configurePreset": "atmega328p-generated" },
{ "name": "atmega328p-reflect", "configurePreset": "atmega328p-reflect" }
]
}

21
LICENSE
View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020 BlackMark
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,29 +0,0 @@
# ds3231
Maxim DS3231 RTC driver on [libavr](https://git.blackmark.me/avr/libavr):
clock and alarm read/write, alarm interrupts, oscillator-stop detection,
die temperature. One source runs on every libavr chip — TWI hardware on
the mega328P, open-drain software I2C on the tinies. All bus errors
surface as `std::expected`.
```cpp
using bus = dev::i2c<{.frequency = 100_kHz}>;
using rtc = ds3231::device<bus>;
auto now = rtc::read_clock(); // result<date_time>
(void)rtc::set_alarm1({}, ds3231::alarm1_rate::once_per_second);
```
`example/main.cpp` is the full tour. Build with a libavr checkout:
```sh
LIBAVR_ROOT=/path/to/libavr cmake --preset attiny85-generated
cmake --build --preset attiny85-generated
```
Presets cover attiny85/atmega328p in both libavr modes (generated and
reflect). The legacy yazoalfa-based driver lives on the `master` branch.
Differences from legacy: weekday-rate alarms now actually set the DY bit
(the old `setAlarmHelper` always cleared it), reads/writes are single
coherent bus transactions, and errors are reported instead of ignored.

22
ds3231.atsln Normal file
View File

@@ -0,0 +1,22 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Atmel Studio Solution File, Format Version 11.00
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{E66E83B9-2572-4076-B26E-6BE79FF3018A}") = "ds3231", "ds3231\ds3231.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

4
ds3231/clock.hpp Normal file
View File

@@ -0,0 +1,4 @@
#pragma once
#define F_CPU 16'000'000
#include <util/delay.h>

1
ds3231/ds3231 Submodule

Submodule ds3231/ds3231 added at 8a6170cb10

285
ds3231/ds3231.cppproj Normal file
View File

@@ -0,0 +1,285 @@
<?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>ATmega328P</avrdevice>
<avrdeviceseries>none</avrdeviceseries>
<OutputType>Executable</OutputType>
<Language>CPP</Language>
<OutputFileName>$(MSBuildProjectName)</OutputFileName>
<OutputFileExtension>.elf</OutputFileExtension>
<OutputDirectory>$(MSBuildProjectDirectory)\$(Configuration)</OutputDirectory>
<AssemblyName>DailySafe</AssemblyName>
<Name>DailySafe</Name>
<RootNamespace>DailySafe</RootNamespace>
<ToolchainFlavour>avr-g++-9.1.0</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>
<ResetRule>0</ResetRule>
<eraseonlaunchrule>0</eraseonlaunchrule>
<EraseKey />
<avrtool>com.atmel.avrdbg.tool.atmelice</avrtool>
<avrtoolserialnumber>J41800099437</avrtoolserialnumber>
<avrdeviceexpectedsignature>0x1E950F</avrdeviceexpectedsignature>
<com_atmel_avrdbg_tool_stk500>
<ToolOptions>
<InterfaceProperties>
<IspClock>125000</IspClock>
</InterfaceProperties>
<InterfaceName>ISP</InterfaceName>
</ToolOptions>
<ToolType>com.atmel.avrdbg.tool.stk500</ToolType>
<ToolNumber>
</ToolNumber>
<ToolName>STK500</ToolName>
</com_atmel_avrdbg_tool_stk500>
<avrtoolinterface>ISP</avrtoolinterface>
<avrtoolinterfaceclock>125000</avrtoolinterfaceclock>
<AsfFrameworkConfig>
<framework-data xmlns="">
<options />
<configurations />
<files />
<documentation help="" />
<offline-documentation help="" />
<dependencies>
<content-extension eid="atmel.asf" uuidref="Atmel.ASF" version="3.47.0" />
</dependencies>
</framework-data>
</AsfFrameworkConfig>
<com_atmel_avrdbg_tool_atmelice>
<ToolOptions>
<InterfaceProperties>
<IspClock>125000</IspClock>
</InterfaceProperties>
<InterfaceName>ISP</InterfaceName>
</ToolOptions>
<ToolType>com.atmel.avrdbg.tool.atmelice</ToolType>
<ToolNumber>J41800099437</ToolNumber>
<ToolName>Atmel-ICE</ToolName>
</com_atmel_avrdbg_tool_atmelice>
<custom>
<ToolOptions>
<InterfaceProperties>
<IspClock>125000</IspClock>
</InterfaceProperties>
<InterfaceName>
</InterfaceName>
</ToolOptions>
<ToolType>custom</ToolType>
<ToolNumber>
</ToolNumber>
<ToolName>Custom Programming Tool</ToolName>
</custom>
<AAFDebugger>
<AAFDebugFiles>
<DebugFile>
<path>\Debug\ds3231.lss</path>
<AAFSetting>
<Label>Lss Files</Label>
<Extention>.lss</Extention>
<Regex>^\s*(?&lt;address&gt;[a-f0-9]*):\s*.*$</Regex>
<DebugEnabled>true</DebugEnabled>
<RegexGroups>address</RegexGroups>
<DebuggerExpression>$pc</DebuggerExpression>
</AAFSetting>
</DebugFile>
</AAFDebugFiles>
</AAFDebugger>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<ToolchainSettings>
<AvrGccCpp>
<avrgcc.common.Device>-mmcu=atmega328p</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.4.346\include</Value>
</ListValues>
</avrgcc.compiler.directories.IncludePaths>
<avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level>
<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.Pedantic>True</avrgcc.compiler.warnings.Pedantic>
<avrgcc.compiler.miscellaneous.OtherFlags>-fno-threadsafe-statics -std=c11</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>
</ListValues>
</avrgcccpp.compiler.symbols.DefSymbols>
<avrgcccpp.compiler.directories.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\Atmel\ATmega_DFP\1.4.346\include</Value>
</ListValues>
</avrgcccpp.compiler.directories.IncludePaths>
<avrgcccpp.compiler.optimization.level>Optimize for size (-Os)</avrgcccpp.compiler.optimization.level>
<avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcccpp.compiler.warnings.AllWarnings>True</avrgcccpp.compiler.warnings.AllWarnings>
<avrgcccpp.compiler.warnings.Pedantic>True</avrgcccpp.compiler.warnings.Pedantic>
<avrgcccpp.compiler.miscellaneous.OtherFlags>-fno-threadsafe-statics -Wextra -std=c++17</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.4.346\include</Value>
</ListValues>
</avrgcccpp.assembler.general.IncludePaths>
</AvrGccCpp>
</ToolchainSettings>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<ToolchainSettings>
<AvrGccCpp>
<avrgcc.common.Device>-mmcu=atmega328p</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.4.346\include</Value>
</ListValues>
</avrgcc.compiler.directories.IncludePaths>
<avrgcc.compiler.optimization.level>Optimize (-O1)</avrgcc.compiler.optimization.level>
<avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcc.compiler.optimization.DebugLevel>Maximum (-g3)</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.Pedantic>True</avrgcc.compiler.warnings.Pedantic>
<avrgcc.compiler.miscellaneous.OtherFlags>-fno-threadsafe-statics -std=c11</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>
</ListValues>
</avrgcccpp.compiler.symbols.DefSymbols>
<avrgcccpp.compiler.directories.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\Atmel\ATmega_DFP\1.4.346\include</Value>
</ListValues>
</avrgcccpp.compiler.directories.IncludePaths>
<avrgcccpp.compiler.optimization.level>Optimize debugging experience (-Og)</avrgcccpp.compiler.optimization.level>
<avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcccpp.compiler.optimization.DebugLevel>Maximum (-g3)</avrgcccpp.compiler.optimization.DebugLevel>
<avrgcccpp.compiler.warnings.AllWarnings>True</avrgcccpp.compiler.warnings.AllWarnings>
<avrgcccpp.compiler.warnings.Pedantic>True</avrgcccpp.compiler.warnings.Pedantic>
<avrgcccpp.compiler.miscellaneous.OtherFlags>-fno-threadsafe-statics -Wextra -std=c++17</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.4.346\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.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="ds3231\alarms.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="ds3231\ds3231.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="ds3231\flags.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="ds3231\registers.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="flash\flash.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="i2c\hardware.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="i2c\i2c.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="io\io.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="main.cpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="uart\config.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="uart\hardware.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="uart\hardware0.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="uart\hardware1.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="uart\software.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="uart\uart.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="util\func.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="util\type.hpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="util\util.hpp">
<SubType>compile</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Folder Include="ds3231" />
<Folder Include="io" />
<Folder Include="flash" />
<Folder Include="i2c" />
<Folder Include="util" />
<Folder Include="uart" />
</ItemGroup>
<Import Project="$(AVRSTUDIO_EXE_PATH)\\Vs\\Compiler.targets" />
</Project>

1
ds3231/flash Submodule

Submodule ds3231/flash added at 6edb2e5a21

1
ds3231/i2c Submodule

Submodule ds3231/i2c added at 8af5afd00d

1
ds3231/io Submodule

Submodule ds3231/io added at 80de36ee7e

188
ds3231/main.cpp Normal file
View File

@@ -0,0 +1,188 @@
#include "clock.hpp"
#include <stdlib.h>
#include "ds3231/ds3231.hpp"
#include "uart/uart.hpp"
using uart_t = uart::Uart0<>;
REGISTER_UART0_INT_VECTORS(uart_t);
static inline bool isEuDst(const rtc::DateTime &dateTime)
{
constexpr auto calcDstBegin = [](const auto &year) {
const auto beginDay = (31 - ((((5 * year) / 4) + 4) % 7));
return beginDay;
};
constexpr auto calcDstEnd = [](const auto &year) {
const auto endDay = (31 - ((((5 * year) / 4) + 1) % 7));
return endDay;
};
if (dateTime.month > 10 || dateTime.month < 3)
return false;
else if (dateTime.month > 3 && dateTime.month < 10)
return true;
else if (dateTime.month == 3) {
if (dateTime.day > calcDstBegin(dateTime.year)) {
return true;
} else if (dateTime.day == calcDstBegin(dateTime.year) && dateTime.hour >= 1)
return true;
return false;
}
// month == 10
if (dateTime.day < calcDstEnd(dateTime.year)) {
return true;
} else if (dateTime.day == calcDstEnd(dateTime.year) && dateTime.hour < 1)
return true;
return false;
}
inline uart_t &operator<<(uart_t &serial, const rtc::DateTime &dateTime)
{
serial.txNumber<uint16_t, 10, 4>(dateTime.year);
serial << '-';
serial.txNumber<uint8_t, 10, 2>(dateTime.month);
serial << '-';
serial.txNumber<uint8_t, 10, 2>(dateTime.day);
serial << ' ';
serial.txNumber<uint8_t, 10, 2>(dateTime.hour);
serial << ':';
serial.txNumber<uint8_t, 10, 2>(dateTime.minute);
serial << ':';
serial.txNumber<uint8_t, 10, 2>(dateTime.second);
return serial;
}
static inline void printLocalTime(const rtc::DateTime &dateTime)
{
const auto dst = isEuDst(dateTime);
uart_t serial;
serial << dateTime << (dst ? F(" +2") : F(" +1")) << F("\r\n");
}
static inline size_t receiveLine(char *buffer, const size_t maxLength)
{
uart_t serial;
size_t received = 0;
while (received < maxLength) {
if (serial.rxByte(*reinterpret_cast<uint8_t *>(&buffer[received]))) {
++received;
if (buffer[received - 1] == '\r' || buffer[received - 1] == '\n')
break;
}
}
return received;
}
static inline rtc::DateTime receiveTime()
{
uart_t serial;
rtc::DateTime dateTime;
constexpr auto BUF_LEN = 8;
char receiveBuffer[BUF_LEN];
serial << F("Enter year: ");
auto receivedLen = receiveLine(receiveBuffer, BUF_LEN);
receiveBuffer[receivedLen] = '\0';
dateTime.year = atoi(receiveBuffer);
serial << F("\r\nEnter month: ");
receivedLen = receiveLine(receiveBuffer, BUF_LEN);
receiveBuffer[receivedLen] = '\0';
dateTime.month = atoi(receiveBuffer);
serial << F("\r\nEnter date: ");
receivedLen = receiveLine(receiveBuffer, BUF_LEN);
receiveBuffer[receivedLen] = '\0';
dateTime.day = atoi(receiveBuffer);
serial << F("\r\nEnter hour: ");
receivedLen = receiveLine(receiveBuffer, BUF_LEN);
receiveBuffer[receivedLen] = '\0';
dateTime.hour = atoi(receiveBuffer);
serial << F("\r\nEnter minute: ");
receivedLen = receiveLine(receiveBuffer, BUF_LEN);
receiveBuffer[receivedLen] = '\0';
dateTime.minute = atoi(receiveBuffer);
serial << F("\r\nEnter second: ");
receivedLen = receiveLine(receiveBuffer, BUF_LEN);
receiveBuffer[receivedLen] = '\0';
dateTime.second = atoi(receiveBuffer);
serial << F("\r\n");
return dateTime;
}
int main()
{
uart_t serial;
rtc::DS3231<i2c::Hardware<100'000>, false> ds3231;
io::Pin<io::P::C3> rtcPwr;
rtcPwr = false;
rtcPwr.dir(io::Dir::OUT);
rtcPwr = true;
_delay_ms(1000);
ds3231.init();
serial.init();
ds3231.clearAlarm1();
ds3231.clearAlarm2();
rtc::DateTime alarmTime;
alarmTime.second = 17;
alarmTime.minute = 54;
ds3231.setAlarm1(alarmTime, rtc::Alarm1Rate::WHEN_S_MATCH);
ds3231.setAlarm2(alarmTime, rtc::Alarm2Rate::WHEN_M_MATCH);
auto oldDate = ds3231.getDateTime();
while (true) {
const auto date = ds3231.getDateTime();
if (oldDate != date) {
oldDate = date;
printLocalTime(date);
}
if (ds3231.checkAlarm1()) {
serial << F("Alarm1!\r\n");
ds3231.clearAlarm1();
}
if (ds3231.checkAlarm2()) {
serial << F("Alarm2!\r\n");
ds3231.clearAlarm2();
}
uint8_t receivedByte;
if (serial.rxByte(receivedByte)) {
if (receivedByte == 's') {
const auto newDate = receiveTime();
ds3231.setDateTime(newDate);
} else if (receivedByte == '1') {
const auto alarm = ds3231.getAlarm1();
serial << F("Alarm1: ") << alarm << F("\r\n");
} else if (receivedByte == '2') {
const auto alarm = ds3231.getAlarm2();
serial << F("Alarm2: ") << alarm << F("\r\n");
} else
serial << F("Invalid input: ") << static_cast<char>(receivedByte) << F("\r\n");
}
}
return 0;
}

1
ds3231/uart Submodule

Submodule ds3231/uart added at 119de32445

1
ds3231/util Submodule

Submodule ds3231/util added at 81b3ae244c

View File

@@ -1,3 +0,0 @@
add_executable(clock main.cpp)
target_link_libraries(clock PRIVATE ds3231)
add_custom_command(TARGET clock POST_BUILD COMMAND ${CMAKE_SIZE} $<TARGET_FILE:clock>)

View File

@@ -1,32 +0,0 @@
#include <ds3231/ds3231.hpp>
#include <libavr/libavr.hpp>
using namespace avr::literals;
// One source for the tiny85 (open-drain software I2C on the USI pins) and
// the mega328P (TWI hardware). On first power-up the clock is seeded and
// alarm 1 armed; then the LED mirrors the seconds parity — a stuck LED
// means bus errors.
using dev = avr::device<{.clock = 8_MHz}>;
using bus = dev::i2c<{.frequency = 100_kHz}>;
using rtc = ds3231::device<bus>;
using led = dev::output<avr::pb3>;
int main()
{
avr::init<bus, led>();
if (auto stopped = rtc::oscillator_stopped(); stopped.value_or(false)) {
(void)rtc::write_clock({{2026, 1, 1}, {12, 0, 0}});
(void)rtc::set_alarm1({}, ds3231::alarm1_rate::once_per_second);
(void)rtc::clear_oscillator_stopped();
}
while (true) {
if (auto now = rtc::read_clock(); now.has_value())
led::write(now->second % 2 == 0);
if (auto fired = rtc::alarm1_fired(); fired.value_or(false))
(void)rtc::clear_alarm1();
dev::delay<100_ms>();
}
}

View File

@@ -1,304 +0,0 @@
#pragma once
#include <array>
#include <cstdint>
#include <expected>
#include <libavr/i2c.hpp>
namespace ds3231 {
using avr::i2c::error;
using avr::i2c::status;
template <typename T>
using result = std::expected<T, error>;
struct date {
std::uint16_t year; // 2000..2099
std::uint8_t month; // 1..12
std::uint8_t day; // 1..31 (weekday 1..7 in weekday-alarm reads)
friend constexpr bool operator==(const date &, const date &) = default;
};
struct time_of_day {
std::uint8_t hour; // 0..23
std::uint8_t minute;
std::uint8_t second;
friend constexpr bool operator==(const time_of_day &, const time_of_day &) = default;
};
struct date_time : date, time_of_day {
friend constexpr bool operator==(const date_time &, const date_time &) = default;
};
// Alarm match rates (datasheet Table 2). Alarm 2 has no seconds register
// and fires at :00 of the matching minute.
enum class alarm1_rate : std::uint8_t {
once_per_second = 0b01111,
seconds_match = 0b01110,
minutes_seconds_match = 0b01100,
time_match = 0b01000,
date_time_match = 0b00000,
weekday_time_match = 0b10000,
};
enum class alarm2_rate : std::uint8_t {
once_per_minute = 0b0111,
minutes_match = 0b0110,
time_match = 0b0100,
date_time_match = 0b0000,
weekday_time_match = 0b1000,
};
namespace detail {
constexpr std::uint8_t to_bcd(std::uint8_t value)
{
return static_cast<std::uint8_t>(((value / 10) << 4) | (value % 10));
}
constexpr std::uint8_t from_bcd(std::uint8_t value)
{
return static_cast<std::uint8_t>((value >> 4) * 10 + (value & 0x0f));
}
// Hours register: bit 6 selects 12-hour mode, bit 5 is then PM. Writes
// always use 24-hour form; reads accept either.
constexpr std::uint8_t hours_from_reg(std::uint8_t reg)
{
if (reg & 0x40) {
auto hour = from_bcd(reg & 0x1f);
bool pm = reg & 0x20;
if (hour == 12)
return pm ? 12 : 0;
return static_cast<std::uint8_t>(pm ? hour + 12 : hour);
}
return from_bcd(reg & 0x3f);
}
// Sakamoto's method; the device counts weekdays 1..7 with a free epoch,
// this maps Sunday to 1.
constexpr std::uint8_t weekday(std::uint16_t year, std::uint8_t month, std::uint8_t day)
{
constexpr std::uint8_t offsets[]{0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
if (month < 3)
--year;
return static_cast<std::uint8_t>((year + year / 4 - year / 100 + year / 400 + offsets[month - 1] + day) % 7 + 1);
}
} // namespace detail
// Maxim DS3231 RTC on any libavr i2c master (register map: datasheet
// 19-5170 Table 1). Every call is one bus transaction; errors surface as
// std::expected. Weekday registers maintain themselves from the date when
// SetWeekday is on.
template <typename Bus, bool SetWeekday = true>
class device {
using dev = avr::i2c::device<Bus, 0x68>;
static constexpr std::uint8_t reg_clock = 0x00;
static constexpr std::uint8_t reg_alarm1 = 0x07;
static constexpr std::uint8_t reg_alarm2 = 0x0b;
static constexpr std::uint8_t reg_control = 0x0e;
static constexpr std::uint8_t reg_status = 0x0f;
static constexpr std::uint8_t reg_temp = 0x11;
static constexpr std::uint8_t a1ie = 0x01, a2ie = 0x02, intcn = 0x04, bbsqw = 0x40;
static constexpr std::uint8_t a1f = 0x01, a2f = 0x02, osf = 0x80;
public:
[[nodiscard]] static result<date_time> read_clock()
{
std::array<std::uint8_t, 7> raw;
if (auto s = dev::read_regs(reg_clock, raw); !s)
return std::unexpected(s.error());
date_time now;
now.second = detail::from_bcd(raw[0] & 0x7f);
now.minute = detail::from_bcd(raw[1] & 0x7f);
now.hour = detail::hours_from_reg(raw[2]);
now.day = detail::from_bcd(raw[4] & 0x3f);
now.month = detail::from_bcd(raw[5] & 0x1f);
now.year = static_cast<std::uint16_t>(2000 + detail::from_bcd(raw[6]));
return now;
}
[[nodiscard]] static result<date> read_date()
{
auto now = read_clock();
if (!now)
return std::unexpected(now.error());
return static_cast<date>(*now);
}
[[nodiscard]] static result<time_of_day> read_time()
{
std::array<std::uint8_t, 3> raw;
if (auto s = dev::read_regs(reg_clock, raw); !s)
return std::unexpected(s.error());
return time_of_day{detail::hours_from_reg(raw[2]), detail::from_bcd(raw[1] & 0x7f),
detail::from_bcd(raw[0] & 0x7f)};
}
[[nodiscard]] static status write_clock(const date_time &now)
{
std::array<std::uint8_t, 7> raw{detail::to_bcd(now.second),
detail::to_bcd(now.minute),
detail::to_bcd(now.hour),
SetWeekday ? detail::weekday(now.year, now.month, now.day) : std::uint8_t{1},
detail::to_bcd(now.day),
detail::to_bcd(now.month),
detail::to_bcd(static_cast<std::uint8_t>(now.year % 100))};
return dev::write_regs(reg_clock, raw);
}
[[nodiscard]] static status write_date(const date &value)
{
if constexpr (SetWeekday) {
std::array<std::uint8_t, 4> raw{detail::weekday(value.year, value.month, value.day),
detail::to_bcd(value.day), detail::to_bcd(value.month),
detail::to_bcd(static_cast<std::uint8_t>(value.year % 100))};
return dev::write_regs(reg_clock + 3, raw);
} else {
std::array<std::uint8_t, 3> raw{detail::to_bcd(value.day), detail::to_bcd(value.month),
detail::to_bcd(static_cast<std::uint8_t>(value.year % 100))};
return dev::write_regs(reg_clock + 4, raw);
}
}
[[nodiscard]] static status write_time(const time_of_day &value)
{
std::array<std::uint8_t, 3> raw{detail::to_bcd(value.second), detail::to_bcd(value.minute),
detail::to_bcd(value.hour)};
return dev::write_regs(reg_clock, raw);
}
// Alarm times use .day as the date of month, or as weekday 1..7 with
// the weekday_time_match rates.
[[nodiscard]] static status set_alarm1(const date_time &at, alarm1_rate rate, bool enable_interrupt = true)
{
auto m = static_cast<std::uint8_t>(rate);
std::array<std::uint8_t, 4> raw{
static_cast<std::uint8_t>(detail::to_bcd(at.second) | ((m & 1) << 7)),
static_cast<std::uint8_t>(detail::to_bcd(at.minute) | (((m >> 1) & 1) << 7)),
static_cast<std::uint8_t>(detail::to_bcd(at.hour) | (((m >> 2) & 1) << 7)),
static_cast<std::uint8_t>(day_date(at.day, rate == alarm1_rate::weekday_time_match) |
(((m >> 3) & 1) << 7))};
if (auto s = dev::write_regs(reg_alarm1, raw); !s)
return s;
return enable_interrupt ? enable_alarm_interrupt(a1ie) : status{};
}
[[nodiscard]] static status set_alarm2(const date_time &at, alarm2_rate rate, bool enable_interrupt = true)
{
auto m = static_cast<std::uint8_t>(rate);
std::array<std::uint8_t, 3> raw{
static_cast<std::uint8_t>(detail::to_bcd(at.minute) | ((m & 1) << 7)),
static_cast<std::uint8_t>(detail::to_bcd(at.hour) | (((m >> 1) & 1) << 7)),
static_cast<std::uint8_t>(day_date(at.day, rate == alarm2_rate::weekday_time_match) |
(((m >> 2) & 1) << 7))};
if (auto s = dev::write_regs(reg_alarm2, raw); !s)
return s;
return enable_interrupt ? enable_alarm_interrupt(a2ie) : status{};
}
[[nodiscard]] static result<date_time> read_alarm1()
{
std::array<std::uint8_t, 4> raw;
if (auto s = dev::read_regs(reg_alarm1, raw); !s)
return std::unexpected(s.error());
date_time at{};
at.second = detail::from_bcd(raw[0] & 0x7f);
at.minute = detail::from_bcd(raw[1] & 0x7f);
at.hour = detail::hours_from_reg(raw[2] & 0x7f);
at.day = (raw[3] & 0x40) ? (raw[3] & 0x0f) : detail::from_bcd(raw[3] & 0x3f);
return at;
}
[[nodiscard]] static result<date_time> read_alarm2()
{
std::array<std::uint8_t, 3> raw;
if (auto s = dev::read_regs(reg_alarm2, raw); !s)
return std::unexpected(s.error());
date_time at{};
at.minute = detail::from_bcd(raw[0] & 0x7f);
at.hour = detail::hours_from_reg(raw[1] & 0x7f);
at.day = (raw[2] & 0x40) ? (raw[2] & 0x0f) : detail::from_bcd(raw[2] & 0x3f);
return at;
}
[[nodiscard]] static result<bool> alarm1_fired()
{
return flag_set(a1f);
}
[[nodiscard]] static result<bool> alarm2_fired()
{
return flag_set(a2f);
}
[[nodiscard]] static status clear_alarm1()
{
return clear_flag(a1f);
}
[[nodiscard]] static status clear_alarm2()
{
return clear_flag(a2f);
}
// True after the oscillator was ever stopped (first power-up, battery
// ran out): the clock needs to be set.
[[nodiscard]] static result<bool> oscillator_stopped()
{
return flag_set(osf);
}
[[nodiscard]] static status clear_oscillator_stopped()
{
return clear_flag(osf);
}
// Die temperature in quarter °C (updated every 64 s by the device).
[[nodiscard]] static result<std::int16_t> temperature_quarters()
{
std::array<std::uint8_t, 2> raw;
if (auto s = dev::read_regs(reg_temp, raw); !s)
return std::unexpected(s.error());
return static_cast<std::int16_t>((static_cast<std::int16_t>(static_cast<std::int8_t>(raw[0])) << 2) |
(raw[1] >> 6));
}
private:
static constexpr std::uint8_t day_date(std::uint8_t day, bool weekday_mode)
{
if (weekday_mode)
return static_cast<std::uint8_t>(0x40 | (day & 0x0f));
return detail::to_bcd(day) & std::uint8_t{0x3f};
}
static status enable_alarm_interrupt(std::uint8_t enable_bit)
{
auto control = dev::read_reg(reg_control);
if (!control)
return std::unexpected(control.error());
return dev::write_reg(reg_control, static_cast<std::uint8_t>((*control & ~bbsqw) | intcn | enable_bit));
}
static result<bool> flag_set(std::uint8_t bit)
{
auto flags = dev::read_reg(reg_status);
if (!flags)
return std::unexpected(flags.error());
return (*flags & bit) != 0;
}
static status clear_flag(std::uint8_t bit)
{
auto flags = dev::read_reg(reg_status);
if (!flags)
return std::unexpected(flags.error());
return dev::write_reg(reg_status, static_cast<std::uint8_t>(*flags & ~bit));
}
};
} // namespace ds3231