Compare commits

..

29 Commits

Author SHA1 Message Date
8a6170cb10 Add get alarm helper to reduce code duplication 2020-05-17 20:11:35 +02:00
21f9215bba Implement getting alarm times 2020-05-17 20:06:13 +02:00
dfd2289aef Reduce code duplication 2020-05-17 19:54:46 +02:00
14e608d397 Implement setting alarms 2020-05-17 19:41:53 +02:00
1bc7e66389 Move alarm rate from inside details to visible namespace 2020-05-17 19:19:28 +02:00
ff52f4f152 Implement checking and clearing alarm 2020-05-17 19:10:16 +02:00
3029c3cfe0 Add wrapper to write complete register 2020-05-17 18:59:38 +02:00
c3f9aa6a13 Automatically deduce register address by type 2020-05-17 18:59:19 +02:00
db5197b3b1 Add more flag operator overloads 2020-05-17 18:57:26 +02:00
2a90cdee18 Add time equality check operators 2020-05-17 11:49:30 +02:00
1388412d70 Change interface to only pass i2c backend driver into class 2020-05-16 19:52:22 +02:00
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
16 changed files with 938 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.

24
alarms.hpp Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include <stdint.h>
namespace rtc {
enum class Alarm1Rate : uint8_t {
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,
};
enum class Alarm2Rate : uint8_t {
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,
};
} // namespace rtc

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

392
ds3231.hpp Normal file
View File

@ -0,0 +1,392 @@
#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;
inline bool operator==(const Date &rhs) const
{
if (day != rhs.day)
return false;
if (month != rhs.month)
return false;
if (year != rhs.year)
return false;
return true;
}
inline bool operator!=(const Date &rhs)
{
return !(*this == rhs);
}
};
struct Time {
uint8_t hour;
uint8_t minute;
uint8_t second;
inline bool operator==(const Time &rhs) const
{
if (second != rhs.second)
return false;
if (minute != rhs.minute)
return false;
if (hour != rhs.hour)
return false;
return true;
}
inline bool operator!=(const Time &rhs)
{
return !(*this == rhs);
}
};
struct DateTime : Date, Time {
inline bool operator==(const DateTime &rhs) const
{
if (second != rhs.second)
return false;
if (minute != rhs.minute)
return false;
if (hour != rhs.hour)
return false;
if (day != rhs.day)
return false;
if (month != rhs.month)
return false;
if (year != rhs.year)
return false;
return true;
}
inline bool operator!=(const DateTime &rhs)
{
return !(*this == rhs);
}
};
template <typename I2cDriver, bool SetDayOfWeek = true>
class DS3231 {
using i2c_t = i2c::I2c<I2cDriver>;
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()
{
i2c_t::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 DateTime getAlarm1()
{
return getAlarmHelper<ALARM1_REG_ADDR>();
}
static DateTime getAlarm2()
{
return getAlarmHelper<ALARM2_REG_ADDR>();
}
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<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_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<START_OFFSET, END_OFFSET>(timeReg);
}
static void setAlarm1(const DateTime &alarmTime, const Alarm1Rate &alarmRate, bool enableInterrupt = true)
{
setAlarmHelper(alarmTime, alarmRate);
if (enableInterrupt)
enableInterruptHelper<detail::ControlRegFlags::A1IE>();
}
static void setAlarm2(const DateTime &alarmTime, const Alarm2Rate &alarmRate, bool enableInterrupt = true)
{
setAlarmHelper(alarmTime, alarmRate);
if (enableInterrupt)
enableInterruptHelper<detail::ControlRegFlags::A2IE>();
}
static bool checkAlarm1()
{
return checkAlarmHelper<detail::ControlStatusRegFlags::A1F>();
}
static bool checkAlarm2()
{
return checkAlarmHelper<detail::ControlStatusRegFlags::A2F>();
}
static void clearAlarm1()
{
clearAlarmHelper<detail::ControlStatusRegFlags::A1F>();
}
static void clearAlarm2()
{
clearAlarmHelper<detail::ControlStatusRegFlags::A2F>();
}
private:
template <uint8_t Address, typename Register>
static Register readRegisterHelper()
{
i2c_t::template start<I2C_ADDRESS>(false);
i2c_t::write(Address);
i2c_t::stop();
Register reg;
i2c_t::template start<I2C_ADDRESS>(true);
i2c_t::template readBytes<sizeof(Register)>(reinterpret_cast<uint8_t *>(&reg));
i2c_t::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 StartOffset, uint8_t EndOffset, typename Register>
static void writePartialRegister(const Register &reg)
{
constexpr auto getRegisterAddress = []() {
if constexpr (util::is_same_v<Register, detail::TimeReg>) {
return TIME_REG_ADDR;
} else if constexpr (util::is_same_v<Register, detail::Alarm1Reg>) {
return ALARM1_REG_ADDR;
} else if constexpr (util::is_same_v<Register, detail::Alarm2Reg>) {
return ALARM2_REG_ADDR;
} else if constexpr (util::is_same_v<Register, detail::ControlReg>) {
return CONTROL_REG_ADDR;
} else if constexpr (util::is_same_v<Register, detail::ControlStatusReg>) {
return CONTROL_STATUS_REG_ADDR;
} else if constexpr (util::is_same_v<Register, detail::AgingOffsetReg>) {
return AGING_OFFSET_REG_ADDR;
} else if constexpr (util::is_same_v<Register, detail::TempReg>) {
return TEMP_REG_ADDR;
} else {
static_assert(util::always_false_v<Register>, "Invalid register type");
}
};
constexpr auto ADDRESS = getRegisterAddress();
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");
i2c_t::template start<I2C_ADDRESS>(false);
i2c_t::write(ADDRESS + StartOffset);
i2c_t::template writeBytes<WRITE_SIZE>(reinterpret_cast<const uint8_t *>(&reg) + StartOffset);
i2c_t::stop();
}
template <typename Register>
static void writeRegister(const Register &reg)
{
writePartialRegister<0, sizeof(Register) - 1>(reg);
}
template <uint8_t Address>
static inline auto getAlarmHelper()
{
constexpr auto IsAlarm1 = Address == ALARM1_REG_ADDR;
static_assert(IsAlarm1 || Address == ALARM2_REG_ADDR, "Must use valid alarm address");
const auto alarmReg = readRegister<Address>();
DateTime alarmTime = {};
alarmReg.getDate(alarmTime.day);
alarmTime.hour = alarmReg.getHours();
alarmTime.minute = alarmReg.getMinutes();
if constexpr (IsAlarm1)
alarmTime.second = alarmReg.getSeconds();
return alarmTime;
}
template <typename AlarmRate>
static inline void setAlarmHelper(const DateTime &alarmTime, const AlarmRate &alarmRate)
{
constexpr auto IsAlarm1 = util::is_same_v<AlarmRate, Alarm1Rate>;
static_assert(IsAlarm1 || util::is_same_v<AlarmRate, Alarm2Rate>, "Must use valid alarm rate");
using alarm_reg_t = util::conditional_t<IsAlarm1, detail::Alarm1Reg, detail::Alarm2Reg>;
alarm_reg_t alarmReg;
alarmReg.setAlarmRate(alarmRate);
alarmReg.setDate(alarmTime.day);
alarmReg.setHours(alarmTime.hour);
alarmReg.setMinutes(alarmTime.minute);
if constexpr (IsAlarm1)
alarmReg.setSeconds(alarmTime.second);
writeRegister(alarmReg);
}
template <detail::ControlRegFlags AlarmFlag>
static inline void enableInterruptHelper()
{
constexpr auto IsAlarm1Flag = AlarmFlag == detail::ControlRegFlags::A1IE;
constexpr auto IsAlarm2Flag = AlarmFlag == detail::ControlRegFlags::A2IE;
static_assert(IsAlarm1Flag || IsAlarm2Flag, "Must use valid alarm flag");
auto controlReg = readRegister<CONTROL_REG_ADDR>();
using Flags = typename decltype(controlReg)::Flags;
controlReg &= ~Flags::BBSQW;
controlReg |= Flags::INTCN | AlarmFlag;
writeRegister(controlReg);
}
template <detail::ControlStatusRegFlags AlarmFlag>
static inline bool checkAlarmHelper()
{
constexpr auto IsAlarm1Flag = AlarmFlag == detail::ControlStatusRegFlags::A1F;
constexpr auto IsAlarm2Flag = AlarmFlag == detail::ControlStatusRegFlags::A2F;
static_assert(IsAlarm1Flag || IsAlarm2Flag, "Must use valid alarm flag");
const auto alarmStatus = readRegister<CONTROL_STATUS_REG_ADDR>();
return alarmStatus == AlarmFlag;
}
template <detail::ControlStatusRegFlags AlarmFlag>
static inline void clearAlarmHelper()
{
constexpr auto IsAlarm1Flag = AlarmFlag == detail::ControlStatusRegFlags::A1F;
constexpr auto IsAlarm2Flag = AlarmFlag == detail::ControlStatusRegFlags::A2F;
static_assert(IsAlarm1Flag || IsAlarm2Flag, "Must use valid alarm flag");
auto controlStatusReg = readRegister<CONTROL_STATUS_REG_ADDR>();
controlStatusReg &= ~AlarmFlag;
writeRegister(controlStatusReg);
}
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>

@ -1 +0,0 @@
Subproject commit 8a6170cb109f79acae6b2fd6f802f75b27967cd3

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>

@ -1 +0,0 @@
Subproject commit 6edb2e5a21e0ce58ff2df936caee8b84e240a46b

@ -1 +0,0 @@
Subproject commit 8af5afd00d1cba9211e4fe9be1df44a14b7973a1

@ -1 +0,0 @@
Subproject commit 80de36ee7ee3e6b0842d5eaee81d54062cb496b2

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;
}

@ -1 +0,0 @@
Subproject commit 119de3244588b19b4afb06f33f66f22bb80a89b5

@ -1 +0,0 @@
Subproject commit 81b3ae244c9773e7ea8ee08af43193275db48514

88
flags.hpp Normal file
View File

@ -0,0 +1,88 @@
#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 uint8_t &flag)
{
data |= flag;
return *this;
}
FlagsImpl &operator&=(const FlagsT &flag)
{
data &= static_cast<uint8_t>(flag);
return *this;
}
FlagsImpl &operator&=(const uint8_t &flag)
{
data &= flag;
return *this;
}
FlagsImpl &operator~()
{
data = ~data;
return *this;
}
bool operator==(const FlagsT &flag) const
{
return data & static_cast<uint8_t>(flag);
}
};
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

413
registers.hpp Normal file
View File

@ -0,0 +1,413 @@
#pragma once
#include <stdint.h>
#include "alarms.hpp"
#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;
//////////////////////////////////////////////////////////////////////////
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 Alarm1Rate 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<Alarm1Rate>(dayFormat);
}
return static_cast<Alarm1Rate>(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 Alarm1Rate &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;
//////////////////////////////////////////////////////////////////////////
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 Alarm2Rate 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<Alarm2Rate>(dayFormat);
}
return static_cast<Alarm2Rate>(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 Alarm2Rate &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,
};
static inline uint8_t operator~(const ControlRegFlags &flag)
{
return ~static_cast<uint8_t>(flag);
}
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,
};
static inline uint8_t operator~(const ControlStatusRegFlags &flag)
{
return ~static_cast<uint8_t>(flag);
}
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