Compare commits

..

18 Commits

Author SHA1 Message Date
a946746960 Remove legacy rtc lib 2020-05-16 17:59:38 +02:00
16249914c2 Adapt to moved type submodule 2020-05-16 17:43:55 +02:00
11211be9b9 Implement setting date-time 2020-05-16 17:25:00 +02:00
8c50aa4688 Implement setting time 2020-05-16 17:19:54 +02:00
a65b30f9df Fix partial writing to only write to a range instead of until the end 2020-05-16 17:19:25 +02:00
cd5317db5b Implement setting RTC date 2020-05-16 17:03:30 +02:00
92096b6101 Add helper to allow writing partial register data 2020-05-16 17:02:54 +02:00
80cce4671f Implement optional automatic setting of day of week 2020-05-16 17:01:19 +02:00
c728d99f97 Fix default value for day field in time register 2020-05-16 16:59:54 +02:00
8e653ebd44 Add default init for registers 2020-05-16 16:00:27 +02:00
54b8917705 Implement modern C++ driver base 2020-05-15 19:47:44 +02:00
2768009720 Remove twi library and switch to i2c library submodule 2020-05-15 11:50:11 +02:00
04af54e7c8 Remove C time API glue from driver 2020-05-15 10:20:56 +02:00
9303fbf5b5 Fix clock path 2020-05-15 09:40:08 +02:00
727a974504 Fix warnings 2020-05-15 09:25:48 +02:00
b3364f0b88 Add gitignore, clang-format and license file 2020-05-15 09:20:23 +02:00
22a74d0b77 Imported changes from example branch 2017-12-17 10:55:25 +01:00
8e2e18128b Exported code as submodule 2017-12-17 10:48:59 +01:00
15 changed files with 725 additions and 523 deletions

18
.gitmodules vendored
View File

@@ -1,18 +0,0 @@
[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

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
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,22 +0,0 @@
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

214
ds3231.hpp Normal file
View File

@@ -0,0 +1,214 @@
#pragma once
#include "../clock.hpp"
#include <stddef.h>
#include "../i2c/i2c.hpp"
#include "../util/type.hpp"
#include "registers.hpp"
namespace rtc {
struct Date {
uint16_t year;
uint8_t month;
uint8_t day;
};
struct Time {
uint8_t hour;
uint8_t minute;
uint8_t second;
};
struct DateTime : Date, Time {
};
template <typename I2cDriver, bool SetDayOfWeek = true>
class DS3231 {
public:
static constexpr auto I2C_ADDRESS = 0x68;
static constexpr auto TIME_REG_ADDR = 0x00;
static constexpr auto ALARM1_REG_ADDR = 0x07;
static constexpr auto ALARM2_REG_ADDR = 0x0B;
static constexpr auto CONTROL_REG_ADDR = 0x0E;
static constexpr auto CONTROL_STATUS_REG_ADDR = 0x0F;
static constexpr auto AGING_OFFSET_REG_ADDR = 0x10;
static constexpr auto TEMP_REG_ADDR = 0x11;
// Construction does not call init and is only available for convenience
DS3231() = default;
// Moving and copying ds3231 objects is not supported
DS3231(const DS3231 &) = delete;
DS3231(DS3231 &&) = delete;
DS3231 &operator=(const DS3231 &) = delete;
DS3231 &operator=(DS3231 &&) = delete;
static inline void init()
{
I2cDriver::init();
}
static auto getDate()
{
const auto timeReg = readRegister<TIME_REG_ADDR>();
Date date;
date.year = timeReg.getYear();
date.month = timeReg.getMonth();
date.day = timeReg.getDate();
return date;
}
static auto getTime()
{
const auto timeReg = readRegister<TIME_REG_ADDR>();
Time time;
time.hour = timeReg.getHours();
time.minute = timeReg.getMinutes();
time.second = timeReg.getSeconds();
return time;
}
static auto getDateTime()
{
const auto timeReg = readRegister<TIME_REG_ADDR>();
DateTime dateTime;
dateTime.year = timeReg.getYear();
dateTime.month = timeReg.getMonth();
dateTime.day = timeReg.getDate();
dateTime.hour = timeReg.getHours();
dateTime.minute = timeReg.getMinutes();
dateTime.second = timeReg.getSeconds();
return dateTime;
}
static void setDate(const Date &date)
{
detail::TimeReg timeReg;
timeReg.setYear(date.year);
timeReg.setMonth(date.month);
timeReg.setDate(date.day);
if constexpr (SetDayOfWeek)
timeReg.setDay(calcDayOfWeek(date.year, date.month, date.day) + 1);
constexpr auto DATE_START_OFFSET = offsetof(detail::TimeReg, day);
constexpr auto DATE_END_OFFSET = offsetof(detail::TimeReg, year);
writePartialRegister<TIME_REG_ADDR, DATE_START_OFFSET, DATE_END_OFFSET>(timeReg);
}
static void setTime(const Time &time)
{
detail::TimeReg timeReg;
timeReg.setHours(time.hour);
timeReg.setMinutes(time.minute);
timeReg.setSeconds(time.second);
constexpr auto TIME_START_OFFSET = offsetof(detail::TimeReg, seconds);
constexpr auto TIME_END_OFFSET = offsetof(detail::TimeReg, hours);
writePartialRegister<TIME_REG_ADDR, TIME_START_OFFSET, TIME_END_OFFSET>(timeReg);
}
static void setDateTime(const DateTime &dateTime)
{
detail::TimeReg timeReg;
timeReg.setYear(dateTime.year);
timeReg.setMonth(dateTime.month);
timeReg.setDate(dateTime.day);
if constexpr (SetDayOfWeek)
timeReg.setDay(calcDayOfWeek(dateTime.year, dateTime.month, dateTime.day) + 1);
timeReg.setHours(dateTime.hour);
timeReg.setMinutes(dateTime.minute);
timeReg.setSeconds(dateTime.second);
constexpr auto START_OFFSET = offsetof(detail::TimeReg, seconds);
constexpr auto END_OFFSET = offsetof(detail::TimeReg, year);
writePartialRegister<TIME_REG_ADDR, START_OFFSET, END_OFFSET>(timeReg);
}
private:
template <uint8_t Address, typename Register>
static Register readRegisterHelper()
{
I2cDriver::template start<I2C_ADDRESS>(false);
I2cDriver::write(Address);
I2cDriver::stop();
Register reg;
I2cDriver::template start<I2C_ADDRESS>(true);
I2cDriver::template readBytes<sizeof(Register)>(reinterpret_cast<uint8_t *>(&reg));
I2cDriver::stop();
return reg;
}
template <uint8_t Address>
static auto readRegister()
{
if constexpr (Address == TIME_REG_ADDR) {
return readRegisterHelper<Address, detail::TimeReg>();
} else if constexpr (Address == ALARM1_REG_ADDR) {
return readRegisterHelper<Address, detail::Alarm1Reg>();
} else if constexpr (Address == ALARM2_REG_ADDR) {
return readRegisterHelper<Address, detail::Alarm2Reg>();
} else if constexpr (Address == CONTROL_REG_ADDR) {
return readRegisterHelper<Address, detail::ControlReg>();
} else if constexpr (Address == CONTROL_STATUS_REG_ADDR) {
return readRegisterHelper<Address, detail::ControlStatusReg>();
} else if constexpr (Address == AGING_OFFSET_REG_ADDR) {
return readRegisterHelper<Address, detail::AgingOffsetReg>();
} else if constexpr (Address == TEMP_REG_ADDR) {
return readRegisterHelper<Address, detail::TempReg>();
} else {
static_assert(util::always_false_v<decltype(Address)>, "Invalid register address");
}
}
template <uint8_t Address, uint8_t StartOffset, uint8_t EndOffset, typename Register>
static void writePartialRegister(const Register &reg)
{
static_assert(StartOffset <= EndOffset, "Invalid offset range");
static_assert(StartOffset < sizeof(Register), "Start offset out of bounds");
static_assert(EndOffset < sizeof(Register), "End offset out of bounds");
constexpr auto WRITE_SIZE = EndOffset + 1 - StartOffset;
static_assert(StartOffset + WRITE_SIZE <= sizeof(Register), "Writing out of bounds");
if constexpr (Address == TIME_REG_ADDR) {
static_assert(util::is_same_v<Register, detail::TimeReg>, "Invalid register type");
} else if constexpr (Address == ALARM1_REG_ADDR) {
static_assert(util::is_same_v<Register, detail::Alarm1Reg>, "Invalid register type");
} else if constexpr (Address == ALARM2_REG_ADDR) {
static_assert(util::is_same_v<Register, detail::Alarm2Reg>, "Invalid register type");
} else if constexpr (Address == CONTROL_REG_ADDR) {
static_assert(util::is_same_v<Register, detail::ControlReg>, "Invalid register type");
} else if constexpr (Address == CONTROL_STATUS_REG_ADDR) {
static_assert(util::is_same_v<Register, detail::ControlStatusReg>, "Invalid register type");
} else if constexpr (Address == AGING_OFFSET_REG_ADDR) {
static_assert(util::is_same_v<Register, detail::AgingOffsetReg>, "Invalid register type");
} else if constexpr (Address == TEMP_REG_ADDR) {
static_assert(util::is_same_v<Register, detail::TempReg>, "Invalid register type");
} else {
static_assert(util::always_false_v<Register>, "Invalid register address");
}
I2cDriver::template start<I2C_ADDRESS>(false);
I2cDriver::write(Address + StartOffset);
I2cDriver::template writeBytes<WRITE_SIZE>(reinterpret_cast<const uint8_t *>(&reg) + StartOffset);
I2cDriver::stop();
}
static uint8_t calcDayOfWeek(uint16_t year, uint8_t month, uint16_t day)
{
day += month < 3 ? year-- : year - 2;
const auto dayOfWeek = (23 * month / 9 + day + 4 + year / 4 - year / 100 + year / 400);
return dayOfWeek % 7;
}
};
} // namespace rtc

View File

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

Submodule ds3231/ds3231 deleted from 8a6170cb10

View File

@@ -1,285 +0,0 @@
<?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>

Submodule ds3231/flash deleted from 6edb2e5a21

Submodule ds3231/i2c deleted from 8af5afd00d

Submodule ds3231/io deleted from 80de36ee7e

View File

@@ -1,188 +0,0 @@
#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;
}

Submodule ds3231/uart deleted from 119de32445

Submodule ds3231/util deleted from 81b3ae244c

71
flags.hpp Normal file
View File

@@ -0,0 +1,71 @@
#pragma once
namespace rtc {
namespace detail {
template <typename FlagsT>
struct [[gnu::packed]] FlagsImpl
{
static_assert(sizeof(FlagsT) == sizeof(uint8_t), "Must use uint8_t enum class flags");
uint8_t data = 0;
using Flags = FlagsT;
FlagsImpl() : data(0) {}
FlagsImpl(const Flags &flag) : data(static_cast<uint8_t>(flag)) {}
FlagsImpl(const FlagsImpl &other) : data(other.data) {}
FlagsImpl(const FlagsImpl &&) = delete;
FlagsImpl &operator=(const FlagsImpl &rhs)
{
data = rhs.data;
return *this;
}
FlagsImpl &operator=(const FlagsImpl &&) = delete;
FlagsImpl &operator=(const FlagsT &rhs)
{
data = static_cast<uint8_t>(rhs);
return *this;
}
FlagsImpl &operator|=(const FlagsT &flag)
{
data |= static_cast<uint8_t>(flag);
return *this;
}
FlagsImpl &operator&=(const FlagsT &flag)
{
data &= static_cast<uint8_t>(flag);
return *this;
}
FlagsImpl &operator~()
{
data = ~data;
return *this;
}
};
template <typename FlagsT>
FlagsT operator|(const FlagsT &lhs, const FlagsT &rhs)
{
const auto lhsInt = static_cast<uint8_t>(lhs);
const auto rhsInt = static_cast<uint8_t>(rhs);
return static_cast<FlagsT>(lhsInt | rhsInt);
}
template <typename FlagsT>
FlagsT operator&(const FlagsT &lhs, const FlagsT &rhs)
{
const auto lhsInt = static_cast<uint8_t>(lhs);
const auto rhsInt = static_cast<uint8_t>(rhs);
return static_cast<FlagsT>(lhsInt & rhsInt);
}
} // namespace detail
} // namespace rtc

419
registers.hpp Normal file
View File

@@ -0,0 +1,419 @@
#pragma once
#include <stdint.h>
#include "flags.hpp"
namespace rtc {
namespace detail {
//////////////////////////////////////////////////////////////////////////
template <uint8_t Mask = 0xFF>
static inline uint8_t toBcd(const uint8_t &data)
{
return ((data / 10 * 16) + (data % 10)) & Mask;
}
template <uint8_t Mask = 0xFF>
static inline uint8_t fromBcd(const uint8_t &data)
{
const auto maskedData = data & Mask;
return ((maskedData / 16 * 10) + (maskedData % 16));
}
static inline uint8_t convertTo24Hour(const uint8_t &hoursReg)
{
constexpr auto FLAG_12_HOUR = 6;
constexpr auto FLAG_PM = 5;
const bool time12HourFormat = (hoursReg >> FLAG_12_HOUR) & 1;
if (time12HourFormat) {
const auto pmFlag = (hoursReg >> FLAG_PM) & 1;
constexpr auto HOUR_12_MASK = 0b00011111;
const auto hour12 = fromBcd<HOUR_12_MASK>(hoursReg);
if (hour12 == 12 && !pmFlag)
return 0;
return hour12 + pmFlag ? 12 : 0;
} else // 24 hour format
{
constexpr auto HOUR_MASK = 0b00111111;
return fromBcd<HOUR_MASK>(hoursReg);
}
}
template <uint8_t Mask = 0b01111111>
static inline uint8_t getMaskedBcd(const uint8_t &reg)
{
return fromBcd<Mask>(reg);
}
template <uint8_t Mask = 0b01111111>
static inline void setMaskedBcd(uint8_t &reg, const uint8_t &value)
{
reg &= ~Mask;
reg |= toBcd<Mask>(value);
}
static inline bool getEncodedDay(uint8_t &value, const uint8_t &reg)
{
constexpr auto DAY_FLAG = 6;
if ((reg >> DAY_FLAG) & 1) {
constexpr auto DAY_MASK = 0b00001111;
value = reg & DAY_MASK;
return true;
}
return false;
}
static inline bool getEncodedDate(uint8_t &value, const uint8_t &reg)
{
constexpr auto DAY_FLAG = 6;
if (!((reg >> DAY_FLAG) & 1)) {
value = getMaskedBcd<0b00111111>(reg);
return true;
}
return false;
}
static inline void setEncodedDay(uint8_t &reg, const uint8_t &value)
{
constexpr auto DAY_MASK = 0b11110000;
reg &= DAY_MASK;
reg |= value & DAY_MASK;
constexpr auto DAY_FLAG = 6;
reg |= (1 << DAY_FLAG);
}
static inline void setEncodedDate(uint8_t &reg, const uint8_t &value)
{
setMaskedBcd<0b00111111>(reg, value);
constexpr auto DAY_FLAG = 6;
reg &= ~(1 << DAY_FLAG);
}
//////////////////////////////////////////////////////////////////////////
struct [[gnu::packed]] TimeReg
{
uint8_t seconds = 0;
uint8_t minutes = 0;
uint8_t hours = 0;
uint8_t day = 1; // Range 1-7 according to datasheet
uint8_t date = 0;
uint8_t month_century = 0;
uint8_t year = 0;
//////////////////////////////////////////////////////////////////////////
inline uint8_t getSeconds() const
{
return getMaskedBcd(seconds);
}
inline uint8_t getMinutes() const
{
return getMaskedBcd(minutes);
}
inline uint8_t getHours() const
{
return convertTo24Hour(hours);
}
inline uint8_t getDay() const
{
return getMaskedBcd<0b00000111>(day);
}
inline uint8_t getDate() const
{
return getMaskedBcd<0b00111111>(date);
}
inline uint8_t getMonth() const
{
return getMaskedBcd<0b00011111>(month_century);
}
inline bool getCentury() const
{
constexpr auto CENTURY_FLAG = 7;
return (month_century >> CENTURY_FLAG) & 1;
}
inline uint16_t getYear() const
{
return 2000 + fromBcd(year);
}
//////////////////////////////////////////////////////////////////////////
inline void setSeconds(uint8_t seconds)
{
setMaskedBcd(this->seconds, seconds);
}
inline void setMinutes(uint8_t minutes)
{
setMaskedBcd(this->minutes, minutes);
}
inline void setHours(uint8_t hours)
{
setMaskedBcd(this->hours, hours);
}
inline void setDay(uint8_t day)
{
this->day = day & 0b111;
}
inline void setDate(uint8_t date)
{
setMaskedBcd<0b00111111>(this->date, date);
}
inline void setMonth(uint8_t month)
{
setMaskedBcd<0b00011111>(month_century, month);
}
inline void setCentury(bool century)
{
constexpr auto CENTURY_POS = 7;
month_century &= ~(1 << CENTURY_POS);
month_century |= (century << CENTURY_POS);
}
inline void setYear(uint16_t year)
{
year = year % 100;
this->year = toBcd(year);
}
};
static_assert(sizeof(TimeReg) == 7, "Invalid time register size");
//////////////////////////////////////////////////////////////////////////
struct [[gnu::packed]] Alarm1Reg
{
uint8_t seconds = 0;
uint8_t minutes = 0;
uint8_t hours = 0;
uint8_t day_date = 0;
enum class AlarmRate {
ONCE_PER_S = 0b1111,
WHEN_S_MATCH = 0b1110,
WHEN_M_S_MATCH = 0b1100,
WHEN_H_M_S_MATCH = 0b1000,
WHEN_DATE_H_M_S_MATCH = 0b00000,
WHEN_DAY_H_N_S_MATCH = 0b10000,
};
//////////////////////////////////////////////////////////////////////////
inline uint8_t getSeconds() const
{
return getMaskedBcd(seconds);
}
inline uint8_t getMinutes() const
{
return getMaskedBcd(minutes);
}
inline uint8_t getHours() const
{
return convertTo24Hour(hours);
}
inline bool getDay(uint8_t & day) const
{
return getEncodedDay(day, day_date);
}
inline bool getDate(uint8_t & date) const
{
return getEncodedDate(date, day_date);
}
inline AlarmRate getAlarmRate() const
{
constexpr auto M_FLAG = 7;
const auto m1 = (seconds >> M_FLAG) & 1;
const auto m2 = (minutes >> M_FLAG) & 1;
const auto m3 = (hours >> M_FLAG) & 1;
const auto m4 = (day_date >> M_FLAG) & 1;
const auto m = (m4 << 3) | (m3 << 2) | (m2 << 1) | (m1 << 0);
if (m == 0) {
constexpr auto DAY_FLAG = 6;
const auto dayFormat = ((day_date >> DAY_FLAG) & 1) << 4;
return static_cast<AlarmRate>(dayFormat);
}
return static_cast<AlarmRate>(m);
}
//////////////////////////////////////////////////////////////////////////
inline void setSeconds(uint8_t seconds)
{
setMaskedBcd(this->seconds, seconds);
}
inline void setMinutes(uint8_t minutes)
{
setMaskedBcd(this->minutes, minutes);
}
inline void setHours(uint8_t hours)
{
setMaskedBcd(this->hours, hours);
}
inline void setDay(uint8_t day)
{
setEncodedDay(day_date, day);
}
inline void setDate(uint8_t date)
{
setEncodedDate(day_date, date);
}
inline void setAlarmRate(const AlarmRate &alarmRate)
{
const auto alarmRateFlags = static_cast<uint8_t>(alarmRate);
constexpr auto M_FLAG = 7;
seconds &= ~(1 << M_FLAG);
seconds |= (alarmRateFlags & 1) << M_FLAG;
minutes &= ~(1 << M_FLAG);
minutes |= ((alarmRateFlags >> 1) & 1) << M_FLAG;
hours &= ~(1 << M_FLAG);
hours |= ((alarmRateFlags >> 2) & 1) << M_FLAG;
day_date &= ~(1 << M_FLAG);
day_date |= ((alarmRateFlags >> 3) & 1) << M_FLAG;
}
};
static_assert(sizeof(Alarm1Reg) == 4, "Invalid alarm1 register size");
//////////////////////////////////////////////////////////////////////////
struct [[gnu::packed]] Alarm2Reg
{
uint8_t minutes = 0;
uint8_t hours = 0;
uint8_t day_date = 0;
enum class AlarmRate {
ONCE_PER_M = 0b111,
WHEN_M_MATCH = 0b110,
WHEN_H_M_MATCH = 0b100,
WHEN_DATE_H_M_MATCH = 0b0000,
WHEN_DAY_H_N_MATCH = 0b1000,
};
//////////////////////////////////////////////////////////////////////////
inline uint8_t getMinutes() const
{
return getMaskedBcd(minutes);
}
inline uint8_t getHours() const
{
return convertTo24Hour(hours);
}
inline bool getDay(uint8_t & day) const
{
return getEncodedDay(day, day_date);
}
inline bool getDate(uint8_t & date) const
{
return getEncodedDate(date, day_date);
}
inline AlarmRate getAlarmRate() const
{
constexpr auto M_FLAG = 7;
const auto m2 = (minutes >> M_FLAG) & 1;
const auto m3 = (hours >> M_FLAG) & 1;
const auto m4 = (day_date >> M_FLAG) & 1;
const auto m = (m4 << 2) | (m3 << 1) | (m2 << 0);
if (m == 0) {
constexpr auto DAY_FLAG = 6;
const auto dayFormat = ((day_date >> DAY_FLAG) & 1) << 3;
return static_cast<AlarmRate>(dayFormat);
}
return static_cast<AlarmRate>(m);
}
//////////////////////////////////////////////////////////////////////////
inline void setMinutes(uint8_t minutes)
{
setMaskedBcd(this->minutes, minutes);
}
inline void setHours(uint8_t hours)
{
setMaskedBcd(this->hours, hours);
}
inline void setDay(uint8_t day)
{
setEncodedDay(day_date, day);
}
inline void setDate(uint8_t date)
{
setEncodedDate(day_date, date);
}
inline void setAlarmRate(const AlarmRate &alarmRate)
{
const auto alarmRateFlags = static_cast<uint8_t>(alarmRate);
constexpr auto M_FLAG = 7;
minutes &= ~(1 << M_FLAG);
minutes |= (alarmRateFlags & 1) << M_FLAG;
hours &= ~(1 << M_FLAG);
hours |= ((alarmRateFlags >> 1) & 1) << M_FLAG;
day_date &= ~(1 << M_FLAG);
day_date |= ((alarmRateFlags >> 2) & 1) << M_FLAG;
}
};
static_assert(sizeof(Alarm2Reg) == 3, "Invalid alarm2 register size");
//////////////////////////////////////////////////////////////////////////
enum class ControlRegFlags : uint8_t {
N_EOSC = 1 << 7,
BBSQW = 1 << 6,
CONV = 1 << 5,
RS2 = 1 << 4,
RS1 = 1 << 3,
INTCN = 1 << 2,
A2IE = 1 << 1,
A1IE = 1 << 0,
};
struct [[gnu::packed]] ControlReg : FlagsImpl<ControlRegFlags>{};
static_assert(sizeof(ControlReg) == 1, "Invalid control register size");
//////////////////////////////////////////////////////////////////////////
enum class ControlStatusRegFlags : uint8_t {
OSF = 1 << 7,
EN32KHZ = 1 << 3,
BSY = 1 << 2,
A2F = 1 << 1,
A1F = 1 << 0,
};
struct [[gnu::packed]] ControlStatusReg : FlagsImpl<ControlStatusRegFlags>{};
static_assert(sizeof(ControlStatusReg) == 1, "Invalid control/status register size");
//////////////////////////////////////////////////////////////////////////
struct [[gnu::packed]] AgingOffsetReg
{
uint8_t data = 0;
};
static_assert(sizeof(AgingOffsetReg) == 1, "Invalid aging offset register size");
//////////////////////////////////////////////////////////////////////////
struct [[gnu::packed]] TempReg
{
uint8_t msb_temp = 0;
uint8_t lsb_temp = 0;
};
static_assert(sizeof(TempReg) == 2, "Invalid temperature register size");
//////////////////////////////////////////////////////////////////////////
} // namespace detail
} // namespace rtc