FastArduino v1.10
C++ library to build fast but small Arduino/AVR projects
Loading...
Searching...
No Matches
ds1307.h
Go to the documentation of this file.
1// Copyright 2016-2023 Jean-Francois Poilpret
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
16
22#ifndef DS1307_H
23#define DS1307_H
24
25#include "../array.h"
26#include "../bits.h"
27#include "../i2c.h"
28#include "../functors.h"
29#include "../future.h"
30#include "../utilities.h"
31#include "../i2c_device.h"
32#include "../i2c_device_utilities.h"
33
34// Device driver guidelines:
35// - Template on MANAGER, subclass of I2CDevice
36// - define types aliases PARENT and FUTURE
37// - constructor shall pass one of i2c::Mode i2c::I2C_STANDARD or i2c::I2C_FAST
38// to inherited constructor, in order tos pecify the BEST mode supported by this driver
39// - Define FUTURE subclass (inside device class) for every future requiring input (constant, or user-provided)
40// naming convention: MethodNameFuture
41// - FUTURE subclass shall have explicit constructor with mandatory input arguments (no default)
42// - each async API method returns int (error code) and takes a reference to specific future as unique argument
43// - each async API shall have a matching sync API with same name but different prototype:
44// direct input arguments, reference to output arguments, bool return (true if OK)
45// each sync API calls the matching async API after creating the proper Future on the stack
46// then it calls await() or get() on the future before returning.
47
48namespace devices
49{
53 namespace rtc
54 {
55 }
56}
57
58namespace devices::rtc
59{
61 enum class WeekDay : uint8_t
62 {
63 SUNDAY = 1,
64 MONDAY,
65 TUESDAY,
66 WEDNESDAY,
67 THURSDAY,
68 FRIDAY,
69 SATURDAY
70 };
71
79 struct tm
80 {
82 uint8_t tm_sec = 0;
84 uint8_t tm_min = 0;
86 uint8_t tm_hour = 0;
90 uint8_t tm_mday = 0;
92 uint8_t tm_mon = 0;
94 uint8_t tm_year = 0;
95 };
96
101 enum class SquareWaveFrequency : uint8_t
102 {
103 FREQ_1HZ = 0x00,
104 FREQ_4096HZ = 0x01,
105 FREQ_8192HZ = 0x02,
106 FREQ_32768HZ = 0x03
107 };
108
115 template<typename MANAGER>
116 class DS1307 : public i2c::I2CDevice<MANAGER>
117 {
118 private:
120 template<typename OUT, typename IN> using FUTURE = typename PARENT::template FUTURE<OUT, IN>;
121
122 template<uint8_t REGISTER, typename T = uint8_t, typename FUNCTOR = functor::Identity<T>>
124 template<uint8_t REGISTER, typename T = uint8_t, typename FUNCTOR = functor::Identity<T>>
126
127 static constexpr const uint8_t DEVICE_ADDRESS = 0x68 << 1;
128 static constexpr const uint8_t RAM_START = 0x08;
129 static constexpr const uint8_t RAM_END = 0x40;
130 static constexpr const uint8_t RAM_SIZE = RAM_END - RAM_START;
131 static constexpr const uint8_t TIME_ADDRESS = 0x00;
132 static constexpr const uint8_t CLOCK_HALT = 0x80;
133 static constexpr const uint8_t CONTROL_ADDRESS = 0x07;
134
135 class ControlRegister
136 {
137 public:
138 static constexpr ControlRegister create_output_level(bool level)
139 {
140 return ControlRegister(level ? OUT_MASK : 0);
141 }
142 static constexpr ControlRegister create_square_wave_enable(SquareWaveFrequency frequency)
143 {
144 return ControlRegister(SQWE_MASK | uint8_t(frequency));
145 }
146
147 private:
148 explicit ControlRegister(uint8_t data = 0) : data_{data} {}
149
150 static constexpr uint8_t FREQUENCY_MASK = bits::BV8(0, 1);
151 static constexpr uint8_t SQWE_MASK = bits::BV8(4);
152 static constexpr uint8_t OUT_MASK = bits::BV8(7);
153
154 uint8_t data_ = 0;
155 };
156
157 // Conversion functors for datetime (BCD Vs binary)
158 class DatetimeConverterToDevice
159 {
160 public:
161 using ARG_TYPE = tm;
162 using RES_TYPE = tm;
163 RES_TYPE operator()(const ARG_TYPE& datetime) const
164 {
165 tm dt;
166 dt.tm_sec = utils::binary_to_bcd(datetime.tm_sec);
167 dt.tm_min = utils::binary_to_bcd(datetime.tm_min);
168 dt.tm_hour = utils::binary_to_bcd(datetime.tm_hour);
169 dt.tm_mday = utils::binary_to_bcd(datetime.tm_mday);
170 dt.tm_mon = utils::binary_to_bcd(datetime.tm_mon);
171 dt.tm_year = utils::binary_to_bcd(datetime.tm_year);
172 return dt;
173 }
174 };
175
176 class DatetimeConverterFromDevice
177 {
178 public:
179 using ARG_TYPE = tm;
180 using RES_TYPE = tm;
181 RES_TYPE operator()(const ARG_TYPE& datetime) const
182 {
183 tm dt;
184 // convert DS1307 output (BCD) to integer type
185 dt.tm_sec = utils::bcd_to_binary(datetime.tm_sec);
186 dt.tm_min = utils::bcd_to_binary(datetime.tm_min);
187 dt.tm_hour = utils::bcd_to_binary(datetime.tm_hour);
188 dt.tm_mday = utils::bcd_to_binary(datetime.tm_mday);
189 dt.tm_mon = utils::bcd_to_binary(datetime.tm_mon);
190 dt.tm_year = utils::bcd_to_binary(datetime.tm_year);
191 return dt;
192 }
193 };
194
195 public:
201 explicit DS1307(MANAGER& manager) : PARENT{manager, DEVICE_ADDRESS, i2c::I2C_STANDARD, true} {}
202
206 static constexpr uint8_t ram_size()
207 {
208 return RAM_SIZE;
209 }
210
211 // Asynchronous API
212 //==================
226
245 {
246 return this->async_write(future);
247 }
248
257
276 {
277 return this->async_read(future);
278 }
279
292 template<uint8_t SIZE_>
293 class SetRamFuture : public FUTURE<void, containers::array<uint8_t, SIZE_ + 1>>
294 {
295 using PARENT = FUTURE<void, containers::array<uint8_t, SIZE_ + 1>>;
296 using IN = typename PARENT::IN;
297
298 static IN build_array(uint8_t address, const uint8_t (&data)[SIZE_])
299 {
300 IN input;
301 input[0] = static_cast<uint8_t>(address + RAM_START);
302 input.set(1, data);
303 return input;
304 }
305
306 public:
308 explicit SetRamFuture(uint8_t address, const uint8_t (&data)[SIZE_])
309 : PARENT{build_array(address, data)}
310 {
311 static_assert(SIZE_ <= RAM_SIZE, "SIZE_ template paraneter must be less than RAM_SIZE!");
312 }
313
314 bool is_input_valid() const
315 {
316 return ((this->get_input()[0] + SIZE_) <= RAM_END);
317 }
319 };
320
341 template<uint8_t SIZE> int set_ram(SetRamFuture<SIZE>& future)
342 {
343 if (!future.is_input_valid())
344 return errors::EINVAL;
345 return this->async_write(future);
346 }
347
358 class SetRam1Future : public FUTURE<void, containers::array<uint8_t, 2>>
359 {
360 using PARENT = FUTURE<void, containers::array<uint8_t, 2>>;
361 public:
363 explicit SetRam1Future(uint8_t address, uint8_t data)
364 : PARENT{{static_cast<uint8_t>(address + RAM_START), data}} {}
365
366 bool is_input_valid() const
367 {
368 return (this->get_input()[0] < RAM_END);
369 }
371 };
372
392 {
393 if (!future.is_input_valid())
394 return errors::EINVAL;
395 return this->async_write(future);
396 }
397
408 template<uint8_t SIZE_>
409 class GetRamFuture : public FUTURE<containers::array<uint8_t, SIZE_>, uint8_t>
410 {
411 using PARENT = FUTURE<containers::array<uint8_t, SIZE_>, uint8_t>;
412 public:
414 explicit GetRamFuture(uint8_t address) : PARENT{static_cast<uint8_t>(address + RAM_START)}
415 {
416 static_assert(SIZE_ <= RAM_SIZE, "SIZE_ template paraneter must be less than RAM_SIZE!");
417 }
418
419 bool is_input_valid() const
420 {
421 return ((this->get_input() + SIZE_) <= RAM_END);
422 }
424 };
425
446 template<uint8_t SIZE> int get_ram(GetRamFuture<SIZE>& future)
447 {
448 if (!future.is_input_valid())
449 return errors::EINVAL;
450 return this->async_read(future);
451 }
452
462 class GetRam1Future : public FUTURE<uint8_t, uint8_t>
463 {
464 using PARENT = FUTURE<uint8_t, uint8_t>;
465 public:
467 explicit GetRam1Future(uint8_t address) : PARENT{static_cast<uint8_t>(address + RAM_START)} {}
468
469 bool is_input_valid() const
470 {
471 return this->get_input() < RAM_END;
472 }
474 };
475
495 {
496 if (!future.is_input_valid())
497 return errors::EINVAL;
498 return this->async_read(future);
499 }
500
508 class HaltClockFuture : public TWriteRegisterFuture<TIME_ADDRESS>
509 {
510 public:
512 // just write 0x80 at address 0
515 };
516
538 {
539 return this->async_write(future);
540 }
541
551 class EnableOutputFuture : public TWriteRegisterFuture<CONTROL_ADDRESS, ControlRegister>
552 {
554
555 public:
557 explicit EnableOutputFuture(SquareWaveFrequency frequency)
558 : PARENT{ControlRegister::create_square_wave_enable(frequency)} {}
560 };
561
581 {
582 return this->async_write(future);
583 }
584
594 class DisableOutputFuture : public TWriteRegisterFuture<CONTROL_ADDRESS, ControlRegister>
595 {
597
598 public:
600 explicit DisableOutputFuture(bool output_value)
601 : PARENT{ControlRegister::create_output_level(output_value)} {}
603 };
604
624 {
625 return this->async_write(future);
626 }
627
628 // Synchronous API
629 //=================
643 bool set_datetime(const tm& datetime)
644 {
645 return this->template sync_write<SetDatetimeFuture>(datetime);
646 }
647
659 bool get_datetime(tm& datetime)
660 {
661 return this->template sync_read<GetDatetimeFuture>(datetime);
662 }
663
677 {
678 return this->template sync_write<HaltClockFuture>();
679 }
680
693 bool enable_output(SquareWaveFrequency frequency = SquareWaveFrequency::FREQ_1HZ)
694 {
695 return this->template sync_write<EnableOutputFuture>(frequency);
696 }
697
709 bool disable_output(bool output_value = false)
710 {
711 return this->template sync_write<DisableOutputFuture>(output_value);
712 }
713
727 bool set_ram(uint8_t address, uint8_t data)
728 {
729 SetRam1Future future{address, data};
730 if (set_ram(future) != 0) return false;
731 return (future.await() == future::FutureStatus::READY);
732 }
733
745 uint8_t get_ram(uint8_t address)
746 {
747 GetRam1Future future{address};
748 if (get_ram(future) != 0) return false;
749 uint8_t data = 0;
750 future.get(data);
751 return data;
752 }
753
769 template<uint8_t SIZE> bool set_ram(uint8_t address, const uint8_t (&data)[SIZE])
770 {
771 SetRamFuture<SIZE> future{address, data};
772 if (set_ram(future) != 0) return false;
773 return (future.await() == future::FutureStatus::READY);
774 }
775
791 template<uint8_t SIZE> bool get_ram(uint8_t address, uint8_t (&data)[SIZE])
792 {
793 GetRamFuture<SIZE> future{address};
794 if (get_ram(future) != 0) return false;
795 typename GetRamFuture<SIZE>::OUT temp;
796 if (!future.get(temp)) return false;
797 memcpy(data, temp.data(), SIZE);
798 return true;
799 }
800
815 template<typename T> bool set_ram(uint8_t address, const T& data)
816 {
817 uint8_t temp[sizeof(T)];
818 utils::as_array<T>(data, temp);
819 SetRamFuture<sizeof(T)> future{address, temp};
820 if (set_ram(future) != 0) return false;
821 return (future.await() == future::FutureStatus::READY);
822 }
823
841 template<typename T> bool get_ram(uint8_t address, T& data)
842 {
843 GetRamFuture<sizeof(T)> future{address};
844 if (get_ram(future) != 0) return false;
845 return future.get(reinterpret_cast<uint8_t&>(data));
846 }
847 };
848}
849
850#endif /* DS1307_H */
Create a future to be used by asynchronous method disable_output(DisableOutputFuture&).
Definition: ds1307.h:595
Create a future to be used by asynchronous method enable_output(EnableOutputFuture&).
Definition: ds1307.h:552
Create a future to be used by asynchronous method get_ram(GetRam1Future&).
Definition: ds1307.h:463
Create a future to be used by asynchronous method get_ram(GetRamFuture<SIZE>&).
Definition: ds1307.h:410
Create a future to be used by asynchronous method halt_clock(HaltClockFuture&).
Definition: ds1307.h:509
Create a future to be used by asynchronous method set_ram(SetRam1Future&).
Definition: ds1307.h:359
Create a future to be used by asynchronous method set_ram(SetRamFuture<SIZE>&).
Definition: ds1307.h:294
I2C device driver for the DS1307 RTC chip.
Definition: ds1307.h:117
uint8_t get_ram(uint8_t address)
Get the value of one cell of the chip internal RAM.
Definition: ds1307.h:745
bool get_ram(uint8_t address, T &data)
Read any type of data from the chip internal RAM.
Definition: ds1307.h:841
int get_datetime(GetDatetimeFuture &future)
Get the current date and time from the RTC chip.
Definition: ds1307.h:275
int set_ram(SetRamFuture< SIZE > &future)
Set several cells of the chip internal RAM to specified values.
Definition: ds1307.h:341
bool get_datetime(tm &datetime)
Get the current date and time from the RTC chip.
Definition: ds1307.h:659
int set_datetime(SetDatetimeFuture &future)
Change date and time of the RTC chip connected to this driver.
Definition: ds1307.h:244
static constexpr uint8_t ram_size()
Get the size of the additional RAM size of the chip.
Definition: ds1307.h:206
bool set_ram(uint8_t address, const uint8_t(&data)[SIZE])
Set several cells of the chip internal RAM to specified values.
Definition: ds1307.h:769
bool halt_clock()
Disable the RTC oscillator, hence the time will not be updated anymore.
Definition: ds1307.h:676
bool set_ram(uint8_t address, const T &data)
Write any type of data to the chip internal RAM.
Definition: ds1307.h:815
bool disable_output(bool output_value=false)
Disable square wave output to the SQW/OUT pin of the RTC chip.
Definition: ds1307.h:709
int get_ram(GetRamFuture< SIZE > &future)
Get values of several cells from the chip internal RAM.
Definition: ds1307.h:446
DS1307(MANAGER &manager)
Create a new device driver for a DS1307 chip.
Definition: ds1307.h:201
bool enable_output(SquareWaveFrequency frequency=SquareWaveFrequency::FREQ_1HZ)
Enable square wave output to the SQW/OUT pin of the RTC chip.
Definition: ds1307.h:693
bool set_datetime(const tm &datetime)
Change date and time of the RTC chip connected to this driver.
Definition: ds1307.h:643
int get_ram(GetRam1Future &future)
Get the value of one cell of the chip internal RAM.
Definition: ds1307.h:494
bool set_ram(uint8_t address, uint8_t data)
Set one cell of the chip internal RAM to the specified value.
Definition: ds1307.h:727
int enable_output(EnableOutputFuture &future)
Enable square wave output to the SQW/OUT pin of the RTC chip.
Definition: ds1307.h:580
int set_ram(SetRam1Future &future)
Set one cell of the chip internal RAM to the specified value.
Definition: ds1307.h:391
bool get_ram(uint8_t address, uint8_t(&data)[SIZE])
Get values of several cells from the chip internal RAM.
Definition: ds1307.h:791
int halt_clock(HaltClockFuture &future)
Disable the RTC oscillator, hence the time will not be updated anymore.
Definition: ds1307.h:537
int disable_output(DisableOutputFuture &future)
Enable square wave output to the SQW/OUT pin of the RTC chip.
Definition: ds1307.h:623
Base class for all I2C devices.
Definition: i2c_device.h:84
int async_write(F &future, bool stop=true)
Helper method that asynchronously launches I2C commands for a simple Future performing only one write...
Definition: i2c_device.h:357
MANAGER MANAGER
the type of I2C Manager that can handle this device.
Definition: i2c_device.h:87
int async_read(F &future, bool stop=true)
Helper method that asynchronously launches I2C commands for a simple Future performing one write foll...
Definition: i2c_device.h:309
Generic Future that can be used to read an I2C device register.
Generic Future that can be used to write to an I2C device register.
static constexpr uint8_t BV8(uint8_t bit)
Create a uint8_t bitmask for the given bit number.
Definition: bits.h:41
Defines API for Real-Time Clock chips usage.
Definition: ds1307.h:54
SquareWaveFrequency
The possible frequencies that can be generated by DS1307 RTC SQW/OUT pin.
Definition: ds1307.h:102
WeekDay
Days of the week.
Definition: ds1307.h:62
Defines all API for all external devices supported by FastArduino.
constexpr const int EINVAL
Invalid argument or invalid Future.
Definition: errors.h:49
Contains the API around Future implementation.
Definition: future.h:312
@ READY
The status of a Future once its output value has been fully set by a provider.
Define API to define and manage I2C devices.
Definition: i2c.h:51
uint8_t binary_to_bcd(uint8_t binary)
Convert a natural integers to a BCD byte (2 digits).
Definition: utilities.h:389
uint8_t bcd_to_binary(uint8_t bcd)
Convert Binary-coded decimal byte (each nibble is a digit from 0 to 9) into a natural byte.
Definition: utilities.h:375
The datetime structure used by the RTC API.
Definition: ds1307.h:80
uint8_t tm_mon
months since January - [ 1 to 12 ]
Definition: ds1307.h:92
WeekDay tm_wday
days since Sunday - [ 1 to 7 ]
Definition: ds1307.h:88
uint8_t tm_year
years since 2000
Definition: ds1307.h:94
uint8_t tm_mday
day of the month - [ 1 to 31 ]
Definition: ds1307.h:90
uint8_t tm_min
minutes after the hour - [ 0 to 59 ]
Definition: ds1307.h:84
uint8_t tm_hour
hours since midnight - [ 0 to 23 ]
Definition: ds1307.h:86
uint8_t tm_sec
seconds after the minute - [ 0 to 59 ]
Definition: ds1307.h:82