Compare commits

...

16 Commits

21 changed files with 906 additions and 1610 deletions

13
.clang-format Normal file
View File

@@ -0,0 +1,13 @@
---
BasedOnStyle: LLVM
ColumnLimit: 120
IndentWidth: 4
TabWidth: 4
UseTab: ForIndentation
AlignEscapedNewlines: DontAlign
AllowShortFunctionsOnASingleLine: Empty
AlwaysBreakTemplateDeclarations: true
BreakBeforeBraces: Custom
BraceWrapping:
AfterFunction: true
...

8
.gitignore vendored
View File

@@ -2,4 +2,10 @@
Release
Debug
*.componentinfo.xml
avrdude.bat
*.elf
*.o
*.hex
*.srec
*.eeprom
*.lss
*.map

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "ds3231/usart"]
path = ds3231/usart
url = git@blackmark.me:usart.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 "../type/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(type::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(type::is_same_v<Register, detail::TimeReg>, "Invalid register type");
} else if constexpr (Address == ALARM1_REG_ADDR) {
static_assert(type::is_same_v<Register, detail::Alarm1Reg>, "Invalid register type");
} else if constexpr (Address == ALARM2_REG_ADDR) {
static_assert(type::is_same_v<Register, detail::Alarm2Reg>, "Invalid register type");
} else if constexpr (Address == CONTROL_REG_ADDR) {
static_assert(type::is_same_v<Register, detail::ControlReg>, "Invalid register type");
} else if constexpr (Address == CONTROL_STATUS_REG_ADDR) {
static_assert(type::is_same_v<Register, detail::ControlStatusReg>, "Invalid register type");
} else if constexpr (Address == AGING_OFFSET_REG_ADDR) {
static_assert(type::is_same_v<Register, detail::AgingOffsetReg>, "Invalid register type");
} else if constexpr (Address == TEMP_REG_ADDR) {
static_assert(type::is_same_v<Register, detail::TempReg>, "Invalid register type");
} else {
static_assert(type::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,56 +0,0 @@
/*
* Copyright (c) by BlackMark 2016
* Date 09/09/2016
* Version 1.4
*/
#ifndef BOOTLOADER_H
#define BOOTLOADER_H
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
typedef void (*flash)() __attribute__ ((noreturn));
flash boot = reinterpret_cast<flash>( 0x0000 );
flash bootloader = reinterpret_cast<flash>( 0x7E00 / 2 );
inline bool checkBootloader()
{
if( pgm_read_byte( reinterpret_cast<uint16_t>( bootloader ) * 2 ) == 0xF8 )
{
return true;
}
return false;
}
inline void callBootloader()
{
if( checkBootloader() )
{
bootloader();
}
else
{
boot();
}
}
inline uint8_t handleReset()
{
wdt_reset();
uint8_t ui8MCUSR = MCUSR;
MCUSR &= ~( 1 << WDRF );
wdt_disable();
return ui8MCUSR;
}
inline void reset()
{
wdt_enable( WDTO_15MS );
while( true );
}
#endif

View File

@@ -1,14 +0,0 @@
/*
* Copyright (c) by BlackMark 2015
* Date 24/11/2015
* Version 1.1
*/
#ifndef CLOCK_H
#define CLOCK_H
#define F_CPU 8000000
#include <util/delay.h>
#endif

View File

@@ -1,247 +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>ds3231</AssemblyName>
<Name>ds3231</Name>
<RootNamespace>ds3231</RootNamespace>
<ToolchainFlavour>Native</ToolchainFlavour>
<KeepTimersRunning>true</KeepTimersRunning>
<OverrideVtor>false</OverrideVtor>
<CacheFlash>true</CacheFlash>
<ProgFlashFromRam>true</ProgFlashFromRam>
<RamSnippetAddress>0x20000000</RamSnippetAddress>
<UncachedRange />
<preserveEEPROM>true</preserveEEPROM>
<OverrideVtorValue>exception_table</OverrideVtorValue>
<BootSegment>2</BootSegment>
<eraseonlaunchrule>0</eraseonlaunchrule>
<AsfFrameworkConfig>
<framework-data xmlns="">
<options />
<configurations />
<files />
<documentation help="" />
<offline-documentation help="" />
<dependencies>
<content-extension eid="atmel.asf" uuidref="Atmel.ASF" version="3.34.2" />
</dependencies>
</framework-data>
</AsfFrameworkConfig>
<avrtool>com.atmel.avrdbg.tool.stk500</avrtool>
<avrtoolserialnumber />
<avrdeviceexpectedsignature>0x1E950F</avrdeviceexpectedsignature>
<custom>
<ToolOptions>
<InterfaceProperties>
</InterfaceProperties>
<InterfaceName>
</InterfaceName>
</ToolOptions>
<ToolType>custom</ToolType>
<ToolNumber>
</ToolNumber>
<ToolName>Custom Programming Tool</ToolName>
</custom>
<avrtoolinterface>ISP</avrtoolinterface>
<com_atmel_avrdbg_tool_simulator>
<ToolOptions xmlns="">
<InterfaceProperties>
</InterfaceProperties>
<InterfaceName>
</InterfaceName>
</ToolOptions>
<ToolType xmlns="">com.atmel.avrdbg.tool.simulator</ToolType>
<ToolNumber xmlns="">
</ToolNumber>
<ToolName xmlns="">Simulator</ToolName>
</com_atmel_avrdbg_tool_simulator>
<ResetRule>0</ResetRule>
<EraseKey />
<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>
<avrtoolinterfaceclock>125000</avrtoolinterfaceclock>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<ToolchainSettings>
<AvrGccCpp>
<avrgcc.common.Device>-mmcu=atmega328p -B "%24(PackRepoDir)\Atmel\ATmega_DFP\1.2.193\gcc\dev\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.2.193\include</Value>
</ListValues>
</avrgcc.compiler.directories.IncludePaths>
<avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level>
<avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
<avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
<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.2.193\include</Value>
</ListValues>
</avrgcccpp.compiler.directories.IncludePaths>
<avrgcccpp.compiler.optimization.level>Optimize for size (-Os)</avrgcccpp.compiler.optimization.level>
<avrgcccpp.compiler.optimization.PackStructureMembers>True</avrgcccpp.compiler.optimization.PackStructureMembers>
<avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcccpp.compiler.warnings.AllWarnings>True</avrgcccpp.compiler.warnings.AllWarnings>
<avrgcccpp.compiler.warnings.Pedantic>True</avrgcccpp.compiler.warnings.Pedantic>
<avrgcccpp.compiler.miscellaneous.OtherFlags>-Wextra -std=c++14</avrgcccpp.compiler.miscellaneous.OtherFlags>
<avrgcccpp.linker.libraries.Libraries>
<ListValues>
<Value>libm</Value>
</ListValues>
</avrgcccpp.linker.libraries.Libraries>
<avrgcccpp.assembler.general.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\Atmel\ATmega_DFP\1.2.193\include</Value>
</ListValues>
</avrgcccpp.assembler.general.IncludePaths>
</AvrGccCpp>
</ToolchainSettings>
<PreBuildEvent>echo "C:\bin\avrdude-6.3\avrdude.exe" -v -p$(avrdevice) %%* -Uflash:w:"$(OutputDirectory)\$(Name).hex":i &gt; "$(MSBuildProjectDirectory)\avrdude.bat"</PreBuildEvent>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<ToolchainSettings>
<AvrGccCpp>
<avrgcc.common.Device>-mmcu=atmega328p -B "%24(PackRepoDir)\Atmel\ATmega_DFP\1.2.193\gcc\dev\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.2.193\include</Value>
</ListValues>
</avrgcc.compiler.directories.IncludePaths>
<avrgcc.compiler.optimization.level>Optimize (-O1)</avrgcc.compiler.optimization.level>
<avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
<avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcc.compiler.optimization.DebugLevel>Default (-g2)</avrgcc.compiler.optimization.DebugLevel>
<avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
<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.2.193\include</Value>
</ListValues>
</avrgcccpp.compiler.directories.IncludePaths>
<avrgcccpp.compiler.optimization.level>Optimize (-O1)</avrgcccpp.compiler.optimization.level>
<avrgcccpp.compiler.optimization.PackStructureMembers>True</avrgcccpp.compiler.optimization.PackStructureMembers>
<avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcccpp.compiler.optimization.DebugLevel>Default (-g2)</avrgcccpp.compiler.optimization.DebugLevel>
<avrgcccpp.compiler.warnings.AllWarnings>True</avrgcccpp.compiler.warnings.AllWarnings>
<avrgcccpp.compiler.warnings.Pedantic>True</avrgcccpp.compiler.warnings.Pedantic>
<avrgcccpp.compiler.miscellaneous.OtherFlags>-Wextra -std=c++14</avrgcccpp.compiler.miscellaneous.OtherFlags>
<avrgcccpp.linker.libraries.Libraries>
<ListValues>
<Value>libm</Value>
</ListValues>
</avrgcccpp.linker.libraries.Libraries>
<avrgcccpp.assembler.general.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\Atmel\ATmega_DFP\1.2.193\include</Value>
</ListValues>
</avrgcccpp.assembler.general.IncludePaths>
<avrgcccpp.assembler.debugging.DebugLevel>Default (-Wa,-g)</avrgcccpp.assembler.debugging.DebugLevel>
</AvrGccCpp>
</ToolchainSettings>
<PreBuildEvent>echo "C:\bin\avrdude-6.3\avrdude.exe" -v -p$(avrdevice) %%* -Uflash:w:"$(OutputDirectory)\$(Name).hex":i &gt; "$(MSBuildProjectDirectory)\avrdude.bat"</PreBuildEvent>
</PropertyGroup>
<ItemGroup>
<Compile Include="bootloader.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="clock.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="main.cpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="rtc.cpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="rtc.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="systime.cpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="systime.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="twi-lowlevel.cpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="twi-lowlevel.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="twi.cpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="twi.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="usart\usart.cpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="usart\usart.h">
<SubType>compile</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Folder Include="usart" />
</ItemGroup>
<Import Project="$(AVRSTUDIO_EXE_PATH)\\Vs\\Compiler.targets" />
</Project>

View File

@@ -1,178 +0,0 @@
/*
* Copyright (c) by BlackMark 2017
* Date 17/12/2017
* Version 1.2
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <avr/interrupt.h>
#include "usart/usart.h"
#include "clock.h"
#include "bootloader.h"
#include "systime.h"
void setup()
{
USART0 &cSerial = USART0::inst();
if( SysTime::init() )
{
cSerial << "DS3231 detected!" << "\r\n";
}
else
{
cSerial << "ERROR - No RTC detected!" << "\r\n";
}
}
void setTime()
{
USART0 &cSerial = USART0::inst();
tm sTime;
constexpr auto BUFFER_SIZE = 32;
char szBuffer[BUFFER_SIZE];
cSerial << "Set time:" << "\r\n" << "Year: ";
cSerial.receiveLine( szBuffer, BUFFER_SIZE, "\r" );
cSerial << szBuffer << "\r\n";
sTime.tm_year = atoi( szBuffer ) - 1900;
cSerial << "Set time:" << "\r\n" << "Month: ";
cSerial.receiveLine( szBuffer, BUFFER_SIZE, "\r" );
cSerial << szBuffer << "\r\n";
sTime.tm_mon = atoi( szBuffer ) - 1;
cSerial << "Set time:" << "\r\n" << "Day: ";
cSerial.receiveLine( szBuffer, BUFFER_SIZE, "\r" );
cSerial << szBuffer << "\r\n";
sTime.tm_mday = atoi( szBuffer );
cSerial << "Set time:" << "\r\n" << "Hour: ";
cSerial.receiveLine( szBuffer, BUFFER_SIZE, "\r" );
cSerial << szBuffer << "\r\n";
sTime.tm_hour = atoi( szBuffer );
cSerial << "Minute: ";
cSerial.receiveLine( szBuffer, BUFFER_SIZE, "\r" );
cSerial << szBuffer << "\r\n";
sTime.tm_min = atoi( szBuffer );
cSerial << "Second: ";
cSerial.receiveLine( szBuffer, BUFFER_SIZE, "\r" );
cSerial << szBuffer << "\r\n";
sTime.tm_sec = atoi( szBuffer );
mktime( &sTime );
SysTime::setTime( sTime );
}
void setAlarm()
{
USART0 &cSerial = USART0::inst();
rtc_tm* tmAlarm = rtc_get_alarm();
constexpr auto BUFFER_SIZE = 32;
char szBuffer[BUFFER_SIZE];
cSerial << "Set alarm:" << "\r\n" << "Hour: ";
cSerial.receiveLine( szBuffer, BUFFER_SIZE, "\r" );
cSerial << szBuffer << "\r\n";
tmAlarm->hour = atoi( szBuffer );
cSerial << "Minute: ";
cSerial.receiveLine( szBuffer, BUFFER_SIZE, "\r" );
cSerial << szBuffer << "\r\n";
tmAlarm->min = atoi( szBuffer );
cSerial << "Second: ";
cSerial.receiveLine( szBuffer, BUFFER_SIZE, "\r" );
cSerial << szBuffer << "\r\n";
tmAlarm->sec = atoi( szBuffer );
rtc_set_alarm( tmAlarm );
}
int main()
{
uint8_t ui8MCUSR = handleReset();
if( ui8MCUSR & ( 1 << WDRF ) )
{
callBootloader();
}
USART0 &cSerial = USART0::inst();
cSerial.init( 9600 );
sei();
setup();
constexpr auto BUFFER_SIZE = 32;
char szBuffer[BUFFER_SIZE];
while( true )
{
if( !SysTime::checkSync() )
{
SysTime::syncSysTime();
time_t timeNow = time( nullptr );
tm *psLocalTime = localtime( &timeNow );
strftime( szBuffer, BUFFER_SIZE, "%F %T %z", psLocalTime );
cSerial << "Time: " << szBuffer << "\r\n";
if( rtc_check_alarm() )
{
cSerial << "ALARM!" << "\r\n";
}
}
uint8_t ui8Cmd;
if( cSerial.receiveByte( ui8Cmd ) )
{
if( ui8Cmd == 'b' )
{
cSerial << "Bootloader . . . " << "\r\n";
_delay_ms( 1000 );
reset();
}
else if( ui8Cmd == 's' )
{
setTime();
}
else if( ui8Cmd == 'a' )
{
setAlarm();
}
else if( ui8Cmd == 'p' )
{
uint8_t ui8Hour;
uint8_t ui8Minute;
uint8_t ui8Second;
rtc_get_alarm_s( &ui8Hour, &ui8Minute, &ui8Second );
sprintf( szBuffer, "Alarm set to: %02d:%02d:%02d", ui8Hour, ui8Minute, ui8Second );
cSerial << szBuffer << "\r\n";
}
else
{
cSerial << "Invalid char: '";
cSerial.transmitByte( ui8Cmd );
cSerial << "'" << "\r\n";
}
}
}
return 0;
}

View File

@@ -1,107 +0,0 @@
#include "systime.h"
//////////////////////////////////////////////////////////////////////////
// DST magic by Edgar Bonet
int SysTime::euDST( const time_t *pTime, int32_t *pZ )
{
static_cast<void>( pZ );
uint32_t t = *pTime;
if( static_cast<uint8_t>( t >> 24 ) >= 194 )
t -= 3029443200U;
t = ( t + 655513200 ) / 604800 * 28;
if( static_cast<uint16_t>( t % 1461 ) < 856 )
return ONE_HOUR;
return 0;
}
//////////////////////////////////////////////////////////////////////////
bool SysTime::init()
{
twi_init_master();
rtc_init();
if( !rtc_is_ds3231() )
return false;
set_zone( 1 * ONE_HOUR );
set_dst( euDST );
syncSysTime();
return true;
}
//////////////////////////////////////////////////////////////////////////
void SysTime::syncSysTime()
{
tm sTime = getTime();
set_system_time( mk_gmtime( &sTime ) );
}
//////////////////////////////////////////////////////////////////////////
bool SysTime::checkSync()
{
time_t timeNow = time( nullptr );
tm *ptmUTC = gmtime( &timeNow );
rtc_tm *ptmRtcTime = rtc_get_time();
if( ptmUTC->tm_sec != ptmRtcTime->sec || ptmUTC->tm_min != ptmRtcTime->min || ptmUTC->tm_hour != ptmRtcTime->hour )
return false;
if( ptmUTC->tm_mday != ptmRtcTime->mday || ( ptmUTC->tm_mon + 1 ) != ptmRtcTime->mon || ( ptmUTC->tm_year + 1900 ) != ptmRtcTime->year )
return false;
return true;
}
//////////////////////////////////////////////////////////////////////////
void SysTime::tick()
{
system_tick();
}
//////////////////////////////////////////////////////////////////////////
tm SysTime::getTime()
{
tm sTime;
rtc_tm *ptmTime = rtc_get_time();
sTime.tm_sec = ptmTime->sec;
sTime.tm_min = ptmTime->min;
sTime.tm_hour = ptmTime->hour;
sTime.tm_mday = ptmTime->mday;
sTime.tm_mon = ptmTime->mon - 1;
sTime.tm_year = ptmTime->year - 1900;
sTime.tm_isdst = 0;
time_t timeUTC = mk_gmtime( &sTime );
sTime = *( gmtime( &timeUTC ) );
return sTime;
}
//////////////////////////////////////////////////////////////////////////
void SysTime::setTime( const tm &sTime )
{
rtc_tm *pRtcTime = rtc_get_time();
pRtcTime->sec = sTime.tm_sec;
pRtcTime->min = sTime.tm_min;
pRtcTime->hour = sTime.tm_hour;
pRtcTime->mday = sTime.tm_mday;
pRtcTime->mon = sTime.tm_mon + 1;
pRtcTime->year = sTime.tm_year + 1900;
pRtcTime->wday = sTime.tm_wday + 1;
pRtcTime->am = ( sTime.tm_hour < 12 ) ? true : false;
pRtcTime->twelveHour = ( sTime.tm_hour == 0 ) ? 12 : ( ( sTime.tm_hour > 12 ) ? ( sTime.tm_hour - 12 ) : sTime.tm_hour );
rtc_set_time( pRtcTime );
syncSysTime();
}

View File

@@ -1,29 +0,0 @@
/*
* Copyright (c) by BlackMark 2017
* Date 17/12/2016
* Version 1.0
*/
#ifndef SYSTIME_H
#define SYSTIME_H
#include <time.h>
#include "twi.h"
#include "rtc.h"
class SysTime
{
private:
static int euDST( const time_t *pTime, int32_t *pZ );
public:
static bool init();
static void syncSysTime();
static bool checkSync();
static void tick();
static tm getTime();
static void setTime( const tm &sTime );
};
#endif

View File

@@ -1,476 +0,0 @@
/*
twi.c - TWI/I2C library for Wiring & Arduino
Copyright (c) 2006 Nicholas Zambetti. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <math.h>
#include <stdlib.h>
#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <compat/twi.h>
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
#include "twi-lowlevel.h"
static volatile uint8_t twi_state;
static uint8_t twi_slarw;
static void (*twi_onSlaveTransmit)(void);
static void (*twi_onSlaveReceive)(uint8_t*, int);
static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH];
static volatile uint8_t twi_masterBufferIndex;
static uint8_t twi_masterBufferLength;
static uint8_t twi_txBuffer[TWI_BUFFER_LENGTH];
static volatile uint8_t twi_txBufferIndex;
static volatile uint8_t twi_txBufferLength;
static uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH];
static volatile uint8_t twi_rxBufferIndex;
static volatile uint8_t twi_error;
/*
* Function twi_init
* Desc readys twi pins and sets twi bitrate
* Input none
* Output none
*/
void twi_init(void)
{
// initialize state
twi_state = TWI_READY;
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega328P__)
// activate internal pull-ups for twi
// as per note from atmega8 manual pg167
sbi(PORTC, 4);
sbi(PORTC, 5);
#else
// activate internal pull-ups for twi
// as per note from atmega128 manual pg204
sbi(PORTD, 0);
sbi(PORTD, 1);
#endif
// initialize twi prescaler and bit rate
cbi(TWSR, TWPS0);
cbi(TWSR, TWPS1);
TWBR = ((CPU_FREQ / TWI_FREQ) - 16) / 2;
/* twi bit rate formula from atmega128 manual pg 204
SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
note: TWBR should be 10 or higher for master mode
It is 72 for a 16mhz Wiring board with 100kHz TWI */
// enable twi module, acks, and twi interrupt
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
}
/*
* Function twi_slaveInit
* Desc sets slave address and enables interrupt
* Input none
* Output none
*/
void twi_setAddress(uint8_t address)
{
// set twi slave address (skip over TWGCE bit)
TWAR = address << 1;
}
/*
* Function twi_readFrom
* Desc attempts to become twi bus master and read a
* series of bytes from a device on the bus
* Input address: 7bit i2c device address
* data: pointer to byte array
* length: number of bytes to read into array
* Output number of bytes read
*/
uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length)
{
uint8_t i;
// ensure data will fit into buffer
if(TWI_BUFFER_LENGTH < length){
return 0;
}
// wait until twi is ready, become master receiver
while(TWI_READY != twi_state){
continue;
}
twi_state = TWI_MRX;
// reset error state (0xFF.. no error occured)
twi_error = 0xFF;
// initialize buffer iteration vars
twi_masterBufferIndex = 0;
twi_masterBufferLength = length-1; // This is not intuitive, read on...
// On receive, the previously configured ACK/NACK setting is transmitted in
// response to the received byte before the interrupt is signalled.
// Therefor we must actually set NACK when the _next_ to last byte is
// received, causing that NACK to be sent in response to receiving the last
// expected byte of data.
// build sla+w, slave device address + w bit
twi_slarw = TW_READ;
twi_slarw |= address << 1;
// send start condition
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);
// wait for read operation to complete
while(TWI_MRX == twi_state){
continue;
}
if (twi_masterBufferIndex < length)
length = twi_masterBufferIndex;
// copy twi buffer to data
for(i = 0; i < length; ++i){
data[i] = twi_masterBuffer[i];
}
return length;
}
/*
* Function twi_writeTo
* Desc attempts to become twi bus master and write a
* series of bytes to a device on the bus
* Input address: 7bit i2c device address
* data: pointer to byte array
* length: number of bytes in array
* wait: boolean indicating to wait for write or not
* Output 0 .. success
* 1 .. length to long for buffer
* 2 .. address send, NACK received
* 3 .. data send, NACK received
* 4 .. other twi error (lost bus arbitration, bus error, ..)
*/
uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait)
{
uint8_t i;
// ensure data will fit into buffer
if(TWI_BUFFER_LENGTH < length){
return 1;
}
// wait until twi is ready, become master transmitter
while(TWI_READY != twi_state){
continue;
}
twi_state = TWI_MTX;
// reset error state (0xFF.. no error occured)
twi_error = 0xFF;
// initialize buffer iteration vars
twi_masterBufferIndex = 0;
twi_masterBufferLength = length;
// copy data to twi buffer
for(i = 0; i < length; ++i){
twi_masterBuffer[i] = data[i];
}
// build sla+w, slave device address + w bit
twi_slarw = TW_WRITE;
twi_slarw |= address << 1;
// send start condition
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);
// wait for write operation to complete
while(wait && (TWI_MTX == twi_state)){
continue;
}
if (twi_error == 0xFF)
return 0; // success
else if (twi_error == TW_MT_SLA_NACK)
return 2; // error: address send, nack received
else if (twi_error == TW_MT_DATA_NACK)
return 3; // error: data send, nack received
else
return 4; // other twi error
}
/*
* Function twi_transmit
* Desc fills slave tx buffer with data
* must be called in slave tx event callback
* Input data: pointer to byte array
* length: number of bytes in array
* Output 1 length too long for buffer
* 2 not slave transmitter
* 0 ok
*/
uint8_t twi_transmit(uint8_t* data, uint8_t length)
{
uint8_t i;
// ensure data will fit into buffer
if(TWI_BUFFER_LENGTH < length){
return 1;
}
// ensure we are currently a slave transmitter
if(TWI_STX != twi_state){
return 2;
}
// set length and copy data into tx buffer
twi_txBufferLength = length;
for(i = 0; i < length; ++i){
twi_txBuffer[i] = data[i];
}
return 0;
}
/*
* Function twi_attachSlaveRxEvent
* Desc sets function called before a slave read operation
* Input function: callback function to use
* Output none
*/
void twi_attachSlaveRxEvent( void (*function)(uint8_t*, int) )
{
twi_onSlaveReceive = function;
}
/*
* Function twi_attachSlaveTxEvent
* Desc sets function called before a slave write operation
* Input function: callback function to use
* Output none
*/
void twi_attachSlaveTxEvent( void (*function)(void) )
{
twi_onSlaveTransmit = function;
}
/*
* Function twi_reply
* Desc sends byte or readys receive line
* Input ack: byte indicating to ack or to nack
* Output none
*/
void twi_reply(uint8_t ack)
{
// transmit master read ready signal, with or without ack
if(ack){
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);
}else{
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);
}
}
/*
* Function twi_stop
* Desc relinquishes bus master status
* Input none
* Output none
*/
void twi_stop(void)
{
// send stop condition
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO);
// wait for stop condition to be exectued on bus
// TWINT is not set after a stop condition!
while(TWCR & _BV(TWSTO)){
continue;
}
// update twi state
twi_state = TWI_READY;
}
/*
* Function twi_releaseBus
* Desc releases bus control
* Input none
* Output none
*/
void twi_releaseBus(void)
{
// release bus
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT);
// update twi state
twi_state = TWI_READY;
}
SIGNAL(TWI_vect)
{
switch(TW_STATUS){
// All Master
case TW_START: // sent start condition
case TW_REP_START: // sent repeated start condition
// copy device address and r/w bit to output register and ack
TWDR = twi_slarw;
twi_reply(1);
break;
// Master Transmitter
case TW_MT_SLA_ACK: // slave receiver acked address
case TW_MT_DATA_ACK: // slave receiver acked data
// if there is data to send, send it, otherwise stop
if(twi_masterBufferIndex < twi_masterBufferLength){
// copy data to output register and ack
TWDR = twi_masterBuffer[twi_masterBufferIndex++];
twi_reply(1);
}else{
twi_stop();
}
break;
case TW_MT_SLA_NACK: // address sent, nack received
twi_error = TW_MT_SLA_NACK;
twi_stop();
break;
case TW_MT_DATA_NACK: // data sent, nack received
twi_error = TW_MT_DATA_NACK;
twi_stop();
break;
case TW_MT_ARB_LOST: // lost bus arbitration
twi_error = TW_MT_ARB_LOST;
twi_releaseBus();
break;
// Master Receiver
case TW_MR_DATA_ACK: // data received, ack sent
// put byte into buffer
twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
case TW_MR_SLA_ACK: // address sent, ack received
// ack if more bytes are expected, otherwise nack
if(twi_masterBufferIndex < twi_masterBufferLength){
twi_reply(1);
}else{
twi_reply(0);
}
break;
case TW_MR_DATA_NACK: // data received, nack sent
// put final byte into buffer
twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
case TW_MR_SLA_NACK: // address sent, nack received
twi_stop();
break;
// TW_MR_ARB_LOST handled by TW_MT_ARB_LOST case
// Slave Receiver
case TW_SR_SLA_ACK: // addressed, returned ack
case TW_SR_GCALL_ACK: // addressed generally, returned ack
case TW_SR_ARB_LOST_SLA_ACK: // lost arbitration, returned ack
case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack
// enter slave receiver mode
twi_state = TWI_SRX;
// indicate that rx buffer can be overwritten and ack
twi_rxBufferIndex = 0;
twi_reply(1);
break;
case TW_SR_DATA_ACK: // data received, returned ack
case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack
// if there is still room in the rx buffer
if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){
// put byte in buffer and ack
twi_rxBuffer[twi_rxBufferIndex++] = TWDR;
twi_reply(1);
}else{
// otherwise nack
twi_reply(0);
}
break;
case TW_SR_STOP: // stop or repeated start condition received
// put a null char after data if there's room
if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){
twi_rxBuffer[twi_rxBufferIndex] = '\0';
}
// sends ack and stops interface for clock stretching
twi_stop();
// callback to user defined callback
twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex);
// since we submit rx buffer to "wire" library, we can reset it
twi_rxBufferIndex = 0;
// ack future responses and leave slave receiver state
twi_releaseBus();
break;
case TW_SR_DATA_NACK: // data received, returned nack
case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack
// nack back at master
twi_reply(0);
break;
// Slave Transmitter
case TW_ST_SLA_ACK: // addressed, returned ack
case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack
// enter slave transmitter mode
twi_state = TWI_STX;
// ready the tx buffer index for iteration
twi_txBufferIndex = 0;
// set tx buffer length to be zero, to verify if user changes it
twi_txBufferLength = 0;
// request for txBuffer to be filled and length to be set
// note: user must call twi_transmit(bytes, length) to do this
twi_onSlaveTransmit();
// if they didn't change buffer & length, initialize it
if(0 == twi_txBufferLength){
twi_txBufferLength = 1;
twi_txBuffer[0] = 0x00;
}
// transmit first byte from buffer, fall
case TW_ST_DATA_ACK: // byte sent, ack returned
// copy data to output register
TWDR = twi_txBuffer[twi_txBufferIndex++];
// if there is more to send, ack, otherwise nack
if(twi_txBufferIndex < twi_txBufferLength){
twi_reply(1);
}else{
twi_reply(0);
}
break;
case TW_ST_DATA_NACK: // received nack, we are done
case TW_ST_LAST_DATA: // received ack, but we are done already!
// ack future responses
twi_reply(1);
// leave slave receiver state
twi_state = TWI_READY;
break;
// All
case TW_NO_INFO: // no state information
break;
case TW_BUS_ERROR: // bus error, illegal stop/start
twi_error = TW_BUS_ERROR;
twi_stop();
break;
}
}

View File

@@ -1,57 +0,0 @@
/*
twi.h - TWI/I2C library for Wiring & Arduino
Copyright (c) 2006 Nicholas Zambetti. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef twi_h
#define twi_h
#include <inttypes.h>
//#define ATMEGA8
#ifndef CPU_FREQ
#define CPU_FREQ 16000000L
#endif
#ifndef TWI_FREQ
#define TWI_FREQ 100000L
#endif
#ifndef TWI_BUFFER_LENGTH
#define TWI_BUFFER_LENGTH 32
#endif
#define TWI_READY 0
#define TWI_MRX 1
#define TWI_MTX 2
#define TWI_SRX 3
#define TWI_STX 4
void twi_init(void);
void twi_setAddress(uint8_t);
uint8_t twi_readFrom(uint8_t, uint8_t*, uint8_t);
uint8_t twi_writeTo(uint8_t, uint8_t*, uint8_t, uint8_t);
uint8_t twi_transmit(uint8_t*, uint8_t);
void twi_attachSlaveRxEvent( void (*)(uint8_t*, int) );
void twi_attachSlaveTxEvent( void (*)(void) );
void twi_reply(uint8_t);
void twi_stop(void);
void twi_releaseBus(void);
#endif

View File

@@ -1,226 +0,0 @@
/*
TwoWire.cpp - TWI/I2C library for Wiring & Arduino
Copyright (c) 2006 Nicholas Zambetti. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "twi-lowlevel.h"
#include "twi.h"
// local variables
uint8_t rxBuffer[BUFFER_LENGTH];
uint8_t rxBufferIndex = 0;
uint8_t rxBufferLength = 0;
uint8_t txAddress = 0;
uint8_t txBuffer[BUFFER_LENGTH];
uint8_t txBufferIndex = 0;
uint8_t txBufferLength = 0;
uint8_t transmitting = 0;
void (*user_onRequest)(void);
void (*user_onReceive)(int);
void onRequestService(void);
void onReceiveService(uint8_t*, int);
void twi_init_master(void)
{
rxBufferIndex = 0;
rxBufferLength = 0;
txBufferIndex = 0;
txBufferLength = 0;
twi_init();
}
void twi_init_slave(uint8_t address)
{
twi_setAddress(address);
twi_attachSlaveTxEvent(onRequestService);
twi_attachSlaveRxEvent(onReceiveService);
twi_init_master();
}
uint8_t twi_request_from(uint8_t address, uint8_t quantity)
{
// clamp to buffer length
if(quantity > BUFFER_LENGTH){
quantity = BUFFER_LENGTH;
}
// perform blocking read into buffer
uint8_t read = twi_readFrom(address, rxBuffer, quantity);
// set rx buffer iterator vars
rxBufferIndex = 0;
rxBufferLength = read;
return read;
}
void twi_begin_transmission(uint8_t address)
{
// indicate that we are transmitting
transmitting = 1;
// set address of targeted slave
txAddress = address;
// reset tx buffer iterator vars
txBufferIndex = 0;
txBufferLength = 0;
}
uint8_t twi_end_transmission(void)
{
// transmit buffer (blocking)
int8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, 1);
// reset tx buffer iterator vars
txBufferIndex = 0;
txBufferLength = 0;
// indicate that we are done transmitting
transmitting = 0;
return ret;
}
// must be called in:
// slave tx event callback
// or after beginTransmission(address)
void twi_send_byte(uint8_t data)
{
if(transmitting){
// in master transmitter mode
// don't bother if buffer is full
if(txBufferLength >= BUFFER_LENGTH){
return;
}
// put byte in tx buffer
txBuffer[txBufferIndex] = data;
++txBufferIndex;
// update amount in buffer
txBufferLength = txBufferIndex;
}else{
// in slave send mode
// reply to master
twi_transmit(&data, 1);
}
}
// must be called in:
// slave tx event callback
// or after beginTransmission(address)
void twi_send(uint8_t* data, uint8_t quantity)
{
if(transmitting){
// in master transmitter mode
for(uint8_t i = 0; i < quantity; ++i){
twi_send_byte(data[i]);
}
}else{
// in slave send mode
// reply to master
twi_transmit(data, quantity);
}
}
// must be called in:
// slave tx event callback
// or after beginTransmission(address)
void twi_send_char(char* data)
{
twi_send((uint8_t*)data, strlen(data));
}
// must be called in:
// slave rx event callback
// or after requestFrom(address, numBytes)
uint8_t twi_available(void)
{
return rxBufferLength - rxBufferIndex;
}
// must be called in:
// slave rx event callback
// or after requestFrom(address, numBytes)
uint8_t twi_receive(void)
{
// default to returning null char
// for people using with char strings
uint8_t value = '\0';
// get each successive byte on each call
if(rxBufferIndex < rxBufferLength){
value = rxBuffer[rxBufferIndex];
++rxBufferIndex;
}
return value;
}
// behind the scenes function that is called when data is received
void onReceiveService(uint8_t* inBytes, int numBytes)
{
// don't bother if user hasn't registered a callback
if(!user_onReceive){
return;
}
// don't bother if rx buffer is in use by a master requestFrom() op
// i know this drops data, but it allows for slight stupidity
// meaning, they may not have read all the master requestFrom() data yet
if(rxBufferIndex < rxBufferLength){
return;
}
// copy twi rx buffer into local read buffer
// this enables new reads to happen in parallel
for(uint8_t i = 0; i < numBytes; ++i){
rxBuffer[i] = inBytes[i];
}
// set rx iterator vars
rxBufferIndex = 0;
rxBufferLength = numBytes;
// alert user program
user_onReceive(numBytes);
}
// behind the scenes function that is called when data is requested
void onRequestService(void)
{
// don't bother if user hasn't registered a callback
if(!user_onRequest){
return;
}
// reset tx buffer iterator vars
// !!! this will kill any pending pre-master sendTo() activity
txBufferIndex = 0;
txBufferLength = 0;
// alert user program
user_onRequest();
}
// sets function called on slave write
void twi_set_on_receive( void (*function)(int) )
{
user_onReceive = function;
}
// sets function called on slave read
void twi_set_on_request( void (*function)(void) )
{
user_onRequest = function;
}

View File

@@ -1,40 +0,0 @@
/*
TwoWire.h - TWI/I2C library for Arduino & Wiring
Copyright (c) 2006 Nicholas Zambetti. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef TwoWire_h
#define TwoWire_h
#include <inttypes.h>
#define BUFFER_LENGTH 32
void twi_init_master(void);
void twi_init_slave(uint8_t);
void twi_begin_transmission(uint8_t);
uint8_t twi_end_transmission(void);
uint8_t twi_request_from(uint8_t, uint8_t);
void twi_send_byte(uint8_t);
void twi_send(uint8_t*, uint8_t);
void twi_send_char(char*);
uint8_t twi_available(void);
uint8_t twi_receive(void);
void twi_set_on_receive( void (*)(int) );
void twi_set_on_request( void (*)(void) );
#endif

Submodule ds3231/usart deleted from b2a3b03867

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

View File

@@ -73,6 +73,11 @@
#define TRUE 1
#define FALSE 0
#include "../clock.hpp"
#include "../i2c/i2c.hpp"
using i2c_t = i2c::I2c<i2c::Hardware<100'000>>;
#include "rtc.h"
#define RTC_ADDR 0x68 // I2C address
@@ -93,20 +98,23 @@ uint8_t bcd2dec(uint8_t b)
uint8_t rtc_read_byte(uint8_t offset)
{
twi_begin_transmission(RTC_ADDR);
twi_send_byte(offset);
twi_end_transmission();
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(offset);
i2c_t::stop();
twi_request_from(RTC_ADDR, 1);
return twi_receive();
i2c_t::start<RTC_ADDR>(true);
const auto received = i2c_t::read<true>();
i2c_t::stop();
return received;
}
void rtc_write_byte(uint8_t b, uint8_t offset)
{
twi_begin_transmission(RTC_ADDR);
twi_send_byte(offset);
twi_send_byte(b);
twi_end_transmission();
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(offset);
i2c_t::write(b);
i2c_t::stop();
}
static bool s_is_ds1307 = false;
@@ -114,6 +122,8 @@ static bool s_is_ds3231 = false;
void rtc_init(void)
{
i2c_t::init();
// Attempt autodetection:
// 1) Read and save temperature register
// 2) Write a value to temperature register
@@ -153,17 +163,13 @@ struct rtc_tm* rtc_get_time(void)
// read 7 bytes starting from register 0
// sec, min, hour, day-of-week, date, month, year
twi_begin_transmission(RTC_ADDR);
twi_send_byte(0x0);
twi_end_transmission();
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(0x0);
i2c_t::stop();
twi_request_from(RTC_ADDR, 7);
for (uint8_t i = 0; i < 7; i++) {
rtc[i] = twi_receive();
}
twi_end_transmission();
i2c_t::start<RTC_ADDR>(true);
i2c_t::readBytes<7>(rtc);
i2c_t::stop();
// Clear clock halt bit from read data
// This starts the clock for a DS1307, and has no effect for a DS3231
@@ -198,17 +204,13 @@ void rtc_get_time_s(uint8_t* hour, uint8_t* min, uint8_t* sec)
// read 7 bytes starting from register 0
// sec, min, hour, day-of-week, date, month, year
twi_begin_transmission(RTC_ADDR);
twi_send_byte(0x0);
twi_end_transmission();
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(0x0);
i2c_t::stop();
twi_request_from(RTC_ADDR, 7);
for(uint8_t i=0; i<7; i++) {
rtc[i] = twi_receive();
}
twi_end_transmission();
i2c_t::start<RTC_ADDR>(true);
i2c_t::readBytes<7>(rtc);
i2c_t::stop();
if (sec) *sec = bcd2dec(rtc[0]);
if (min) *min = bcd2dec(rtc[1]);
@@ -218,8 +220,8 @@ void rtc_get_time_s(uint8_t* hour, uint8_t* min, uint8_t* sec)
// fixme: support 12-hour mode for setting time
void rtc_set_time(struct rtc_tm* tm_)
{
twi_begin_transmission(RTC_ADDR);
twi_send_byte(0x0);
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(0x0);
uint8_t century;
if (tm_->year > 2000) {
@@ -231,28 +233,28 @@ void rtc_set_time(struct rtc_tm* tm_)
}
// clock halt bit is 7th bit of seconds: this is always cleared to start the clock
twi_send_byte(dec2bcd(tm_->sec)); // seconds
twi_send_byte(dec2bcd(tm_->min)); // minutes
twi_send_byte(dec2bcd(tm_->hour)); // hours
twi_send_byte(dec2bcd(tm_->wday)); // day of week
twi_send_byte(dec2bcd(tm_->mday)); // day
twi_send_byte(dec2bcd(tm_->mon) + century); // month
twi_send_byte(dec2bcd(tm_->year)); // year
i2c_t::write(dec2bcd(tm_->sec)); // seconds
i2c_t::write(dec2bcd(tm_->min)); // minutes
i2c_t::write(dec2bcd(tm_->hour)); // hours
i2c_t::write(dec2bcd(tm_->wday)); // day of week
i2c_t::write(dec2bcd(tm_->mday)); // day
i2c_t::write(dec2bcd(tm_->mon) + century); // month
i2c_t::write(dec2bcd(tm_->year)); // year
twi_end_transmission();
i2c_t::stop();
}
void rtc_set_time_s(uint8_t hour, uint8_t min, uint8_t sec)
{
twi_begin_transmission(RTC_ADDR);
twi_send_byte(0x0);
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(0x0);
// clock halt bit is 7th bit of seconds: this is always cleared to start the clock
twi_send_byte(dec2bcd(sec)); // seconds
twi_send_byte(dec2bcd(min)); // minutes
twi_send_byte(dec2bcd(hour)); // hours
i2c_t::write(dec2bcd(sec)); // seconds
i2c_t::write(dec2bcd(min)); // minutes
i2c_t::write(dec2bcd(hour)); // hours
twi_end_transmission();
i2c_t::stop();
}
// DS1307 only (has no effect when run on DS3231)
@@ -296,16 +298,14 @@ void ds3231_get_temp_int(int8_t* i, uint8_t* f)
if (s_is_ds1307) return; // only valid on DS3231
twi_begin_transmission(RTC_ADDR);
i2c_t::start<RTC_ADDR>(false);
// temp registers 0x11 and 0x12
twi_send_byte(0x11);
twi_end_transmission();
i2c_t::write(0x11);
i2c_t::stop();
twi_request_from(RTC_ADDR, 2);
if (twi_available()) {
msb = twi_receive(); // integer part (in twos complement)
lsb = twi_receive(); // fraction part
i2c_t::start<RTC_ADDR>(true);
msb = i2c_t::read(); // integer part (in twos complement)
lsb = i2c_t::read<true>(); // fraction part
// integer part in entire byte
*i = msb;
@@ -315,38 +315,40 @@ void ds3231_get_temp_int(int8_t* i, uint8_t* f)
// float value can be read like so:
// float temp = ((((short)msb << 8) | (short)lsb) >> 6) / 4.0f;
}
}
void rtc_force_temp_conversion(uint8_t block)
{
if (s_is_ds1307) return; // only valid on DS3231
// read control register (0x0E)
twi_begin_transmission(RTC_ADDR);
twi_send_byte(0x0E);
twi_end_transmission();
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(0x0E);
i2c_t::stop();
twi_request_from(RTC_ADDR, 1);
uint8_t ctrl = twi_receive();
i2c_t::start<RTC_ADDR>(true);
uint8_t ctrl = i2c_t::read<true>();
i2c_t::stop();
ctrl |= 0b00100000; // Set CONV bit
// write new control register value
twi_begin_transmission(RTC_ADDR);
twi_send_byte(0x0E);
twi_send_byte(ctrl);
twi_end_transmission();
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(0x0E);
i2c_t::write(ctrl);
i2c_t::stop();
if (!block) return;
// Temp conversion is ready when control register becomes 0
do {
// Block until CONV is 0
twi_begin_transmission(RTC_ADDR);
twi_send_byte(0x0E);
twi_end_transmission();
twi_request_from(RTC_ADDR, 1);
} while ((twi_receive() & 0b00100000) != 0);
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(0x0E);
i2c_t::stop();
i2c_t::start<RTC_ADDR>(true);
// HACK: Missing stop after read, might still work though
} while ((i2c_t::read<true>() & 0b00100000) != 0);
}
@@ -371,32 +373,35 @@ void rtc_set_sram(uint8_t *data)
uint8_t rtc_get_sram_byte(uint8_t offset)
{
twi_begin_transmission(RTC_ADDR);
twi_send_byte(DS1307_SRAM_ADDR + offset);
twi_end_transmission();
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(DS1307_SRAM_ADDR + offset);
i2c_t::stop();
twi_request_from(RTC_ADDR, 1);
return twi_receive();
i2c_t::start<RTC_ADDR>(true);
const auto received = i2c_t::read<true>();
i2c_t::stop();
return received;
}
void rtc_set_sram_byte(uint8_t b, uint8_t offset)
{
twi_begin_transmission(RTC_ADDR);
twi_send_byte(DS1307_SRAM_ADDR + offset);
twi_send_byte(b);
twi_end_transmission();
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(DS1307_SRAM_ADDR + offset);
i2c_t::write(b);
i2c_t::stop();
}
void rtc_SQW_enable(bool enable)
{
if (s_is_ds1307) {
twi_begin_transmission(RTC_ADDR);
twi_send_byte(0x07);
twi_end_transmission();
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(0x07);
i2c_t::stop();
// read control
twi_request_from(RTC_ADDR, 1);
uint8_t control = twi_receive();
i2c_t::start<RTC_ADDR>(true);
uint8_t control = i2c_t::read<true>();
i2c_t::stop();
if (enable)
control |= 0b00010000; // set SQWE to 1
@@ -404,20 +409,21 @@ void rtc_SQW_enable(bool enable)
control &= ~0b00010000; // set SQWE to 0
// write control back
twi_begin_transmission(RTC_ADDR);
twi_send_byte(0x07);
twi_send_byte(control);
twi_end_transmission();
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(0x07);
i2c_t::write(control);
i2c_t::stop();
}
else { // DS3231
twi_begin_transmission(RTC_ADDR);
twi_send_byte(0x0E);
twi_end_transmission();
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(0x0E);
i2c_t::stop();
// read control
twi_request_from(RTC_ADDR, 1);
uint8_t control = twi_receive();
i2c_t::start<RTC_ADDR>(true);
uint8_t control = i2c_t::read<true>();
i2c_t::stop();
if (enable) {
control |= 0b01000000; // set BBSQW to 1
@@ -428,51 +434,53 @@ void rtc_SQW_enable(bool enable)
}
// write control back
twi_begin_transmission(RTC_ADDR);
twi_send_byte(0x0E);
twi_send_byte(control);
twi_end_transmission();
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(0x0E);
i2c_t::write(control);
i2c_t::stop();
}
}
void rtc_SQW_set_freq(enum RTC_SQW_FREQ freq)
{
if (s_is_ds1307) {
twi_begin_transmission(RTC_ADDR);
twi_send_byte(0x07);
twi_end_transmission();
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(0x07);
i2c_t::stop();
// read control (uses bits 0 and 1)
twi_request_from(RTC_ADDR, 1);
uint8_t control = twi_receive();
i2c_t::start<RTC_ADDR>(true);
uint8_t control = i2c_t::read<true>();
i2c_t::stop();
control &= ~0b00000011; // Set to 0
control |= freq; // Set freq bitmask
// write control back
twi_begin_transmission(RTC_ADDR);
twi_send_byte(0x07);
twi_send_byte(control);
twi_end_transmission();
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(0x07);
i2c_t::write(control);
i2c_t::stop();
}
else { // DS3231
twi_begin_transmission(RTC_ADDR);
twi_send_byte(0x0E);
twi_end_transmission();
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(0x0E);
i2c_t::stop();
// read control (uses bits 3 and 4)
twi_request_from(RTC_ADDR, 1);
uint8_t control = twi_receive();
i2c_t::start<RTC_ADDR>(true);
uint8_t control = i2c_t::read<true>();
i2c_t::stop();
control &= ~0b00011000; // Set to 0
control |= (freq << 4); // Set freq bitmask
// write control back
twi_begin_transmission(RTC_ADDR);
twi_send_byte(0x0E);
twi_send_byte(control);
twi_end_transmission();
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(0x0E);
i2c_t::write(control);
i2c_t::stop();
}
}
@@ -480,13 +488,14 @@ void rtc_osc32kHz_enable(bool enable)
{
if (!s_is_ds3231) return;
twi_begin_transmission(RTC_ADDR);
twi_send_byte(0x0F);
twi_end_transmission();
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(0x0F);
i2c_t::stop();
// read status
twi_request_from(RTC_ADDR, 1);
uint8_t status = twi_receive();
i2c_t::start<RTC_ADDR>(true);
uint8_t status = i2c_t::read<true>();
i2c_t::stop();
if (enable)
status |= 0b00001000; // set to 1
@@ -494,10 +503,10 @@ void rtc_osc32kHz_enable(bool enable)
status &= ~0b00001000; // Set to 0
// write status back
twi_begin_transmission(RTC_ADDR);
twi_send_byte(0x0F);
twi_send_byte(status);
twi_end_transmission();
i2c_t::start<RTC_ADDR>(false);
i2c_t::write(0x0F);
i2c_t::write(status);
i2c_t::stop();
}
// Alarm functionality

View File

@@ -18,7 +18,6 @@
#include <stdbool.h>
#include <avr/io.h>
#include "twi.h"
#define DS1307_SLAVE_ADDR 0b11010000