Compare commits

..

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
16 changed files with 523 additions and 938 deletions

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

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,24 +0,0 @@
#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

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

View File

@ -1,392 +0,0 @@
#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

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

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

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

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

1
ds3231/i2c Submodule

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

1
ds3231/io Submodule

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

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

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

1
ds3231/util Submodule

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

View File

@ -1,88 +0,0 @@
#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

View File

@ -1,413 +0,0 @@
#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