Чуть правленные исходники v3.11 1.11: изменен делитель тактирования spi + частота кварца для мастер станции

This commit is contained in:
2025-10-03 09:48:30 +03:00
parent df18fedc7f
commit 3f886d0f98
94 changed files with 19758 additions and 0 deletions
@@ -0,0 +1,17 @@
DS3231 library for the Arduino.
This library implements the following features:
- read/write of current time, both of the alarms,
control/status registers, aging register
- read of the temperature register, and of any address from the chip.
Author: Petre Rodan <petre.rodan@simplex.ro>
Available from: https://github.com/rodan/ds3231
License: GNU GPLv3
The DS3231 is a low-cost, extremely accurate I2C real-time clock
(RTC) with an integrated temperature-compensated crystal oscillator
(TCXO) and crystal.
@@ -0,0 +1,8 @@
#ifndef __config_h_
#define __config_h_
// comment this out if you need unixtime support
// this will add about 324bytes to your firmware
//#define CONFIG_UNIXTIME
#endif
@@ -0,0 +1,418 @@
/*
DS3231 library for the Arduino.
This library implements the following features:
- read/write of current time, both of the alarms,
control/status registers, aging register
- read of the temperature register, and of any address from the chip.
Author: Petre Rodan <petre.rodan@simplex.ro>
Available from: https://github.com/rodan/ds3231
The DS3231 is a low-cost, extremely accurate I2C real-time clock
(RTC) with an integrated temperature-compensated crystal oscillator
(TCXO) and crystal.
GNU GPLv3 license:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wire.h>
#include <stdio.h>
#include "ds3231.h"
#ifdef __AVR__
#include <avr/pgmspace.h>
// Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734
#ifdef PROGMEM
#undef PROGMEM
#define PROGMEM __attribute__((section(".progmem.data")))
#endif
#else
#define PROGMEM
#define pgm_read_byte(addr) (*(const uint8_t *)(addr))
#endif
/* control register 0Eh/8Eh
bit7 EOSC Enable Oscillator (1 if oscillator must be stopped when on battery)
bit6 BBSQW Battery Backed Square Wave
bit5 CONV Convert temperature (1 forces a conversion NOW)
bit4 RS2 Rate select - frequency of square wave output
bit3 RS1 Rate select
bit2 INTCN Interrupt control (1 for use of the alarms and to disable square wave)
bit1 A2IE Alarm2 interrupt enable (1 to enable)
bit0 A1IE Alarm1 interrupt enable (1 to enable)
*/
void DS3231_init(const uint8_t ctrl_reg)
{
DS3231_set_creg(ctrl_reg);
}
void DS3231_set(struct ts t)
{
uint8_t i, century;
if (t.year > 2000) {
century = 0x80;
t.year_s = t.year - 2000;
} else {
century = 0;
t.year_s = t.year - 1900;
}
uint8_t TimeDate[7] = { t.sec, t.min, t.hour, t.wday, t.mday, t.mon, t.year_s };
Wire.beginTransmission(DS3231_I2C_ADDR);
Wire.write(DS3231_TIME_CAL_ADDR);
for (i = 0; i <= 6; i++) {
TimeDate[i] = dectobcd(TimeDate[i]);
if (i == 5)
TimeDate[5] += century;
Wire.write(TimeDate[i]);
}
Wire.endTransmission();
}
bool DS3231_get(struct ts *t)
{
memset(t, 0, sizeof(ts));
Wire.beginTransmission(DS3231_I2C_ADDR);
Wire.write(DS3231_TIME_CAL_ADDR);
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDR, 7);
uint8_t TimeDate[7]; // second,minute,hour,dow,day,month,year
uint8_t century = 0;
uint8_t b = 0xff;
for (uint8_t i = 0; i <= 6; i++) {
uint8_t n = Wire.read();
b &= n;
if (i == 5) {
TimeDate[5] = bcdtodec(n & 0x1F);
century = (n & 0x80) >> 7;
} else {
TimeDate[i] = bcdtodec(n);
}
}
if (b == 0xff) {
// DS3231 not connected
return false;
}
uint16_t year_full;
if (century == 1) {
year_full = 2000 + TimeDate[6];
} else {
year_full = 1900 + TimeDate[6];
}
t->sec = TimeDate[0];
t->min = TimeDate[1];
t->hour = TimeDate[2];
t->mday = TimeDate[4];
t->mon = TimeDate[5];
t->year = year_full;
t->wday = TimeDate[3];
t->year_s = TimeDate[6];
t->unixtime = get_unixtime(*t);
return true;
}
void DS3231_set_addr(const uint8_t addr, const uint8_t val)
{
Wire.beginTransmission(DS3231_I2C_ADDR);
Wire.write(addr);
Wire.write(val);
Wire.endTransmission();
}
uint8_t DS3231_get_addr(const uint8_t addr)
{
uint8_t rv;
Wire.beginTransmission(DS3231_I2C_ADDR);
Wire.write(addr);
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDR, 1);
rv = Wire.read();
return rv;
}
// control register
void DS3231_set_creg(const uint8_t val)
{
DS3231_set_addr(DS3231_CONTROL_ADDR, val);
}
// status register 0Fh/8Fh
/*
bit7 OSF Oscillator Stop Flag (if 1 then oscillator has stopped and date might be innacurate)
bit3 EN32kHz Enable 32kHz output (1 if needed)
bit2 BSY Busy with TCXO functions
bit1 A2F Alarm 2 Flag - (1 if alarm2 was triggered)
bit0 A1F Alarm 1 Flag - (1 if alarm1 was triggered)
*/
void DS3231_set_sreg(const uint8_t val)
{
DS3231_set_addr(DS3231_STATUS_ADDR, val);
}
uint8_t DS3231_get_sreg(void)
{
uint8_t rv;
rv = DS3231_get_addr(DS3231_STATUS_ADDR);
return rv;
}
// aging register
void DS3231_set_aging(const int8_t val)
{
uint8_t reg;
if (val >= 0)
reg = val;
else
reg = ~(-val) + 1; // 2C
DS3231_set_addr(DS3231_AGING_OFFSET_ADDR, reg);
}
int8_t DS3231_get_aging(void)
{
uint8_t reg;
int8_t rv;
reg = DS3231_get_addr(DS3231_AGING_OFFSET_ADDR);
if ((reg & 0x80) != 0)
rv = reg | ~((1 << 8) - 1); // if negative get two's complement
else
rv = reg;
return rv;
}
// temperature register
float DS3231_get_treg()
{
float rv;
uint8_t temp_msb, temp_lsb;
int8_t nint;
Wire.beginTransmission(DS3231_I2C_ADDR);
Wire.write(DS3231_TEMPERATURE_ADDR);
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDR, 2);
temp_msb = Wire.read();
temp_lsb = Wire.read() >> 6;
if ((temp_msb & 0x80) != 0)
nint = temp_msb | ~((1 << 8) - 1); // if negative get two's complement
else
nint = temp_msb;
rv = 0.25 * temp_lsb + nint;
return rv;
}
// alarms
// flags are: A1M1 (seconds), A1M2 (minutes), A1M3 (hour),
// A1M4 (day) 0 to enable, 1 to disable, DY/DT (dayofweek == 1/dayofmonth == 0)
void DS3231_set_a1(const uint8_t s, uint8_t mi, uint8_t h, uint8_t d, const uint8_t * flags)
{
uint8_t t[4] = { s, mi, h, d };
uint8_t i;
Wire.beginTransmission(DS3231_I2C_ADDR);
Wire.write(DS3231_ALARM1_ADDR);
for (i = 0; i <= 3; i++) {
if (i == 3) {
Wire.write(dectobcd(t[3]) | (flags[3] << 7) | (flags[4] << 6));
} else
Wire.write(dectobcd(t[i]) | (flags[i] << 7));
}
Wire.endTransmission();
}
void DS3231_get_a1(struct ts *td)
{
uint8_t n[4];
uint8_t t[4]; // second,minute,hour,day
//uint8_t f[5]; // flags
uint8_t i;
memset(td, 0, sizeof(ts));
Wire.beginTransmission(DS3231_I2C_ADDR);
Wire.write(DS3231_ALARM1_ADDR);
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDR, 4);
for (i = 0; i <= 3; i++) {
n[i] = Wire.read();
//f[i] = (n[i] & 0x80) >> 7;
t[i] = bcdtodec(n[i] & 0x7F);
}
//f[4] = (n[3] & 0x40) >> 6;
t[3] = bcdtodec(n[3] & 0x3F);
td->sec = t[0];
td->min = t[1];
td->hour = t[2];
td->mday = t[3];
}
// when the alarm flag is cleared the pulldown on INT is also released
void DS3231_clear_a1f(void)
{
uint8_t reg_val;
reg_val = DS3231_get_sreg() & ~DS3231_A1F;
DS3231_set_sreg(reg_val);
}
uint8_t DS3231_triggered_a1(void)
{
return DS3231_get_sreg() & DS3231_A1F;
}
// flags are: A2M2 (minutes), A2M3 (hour), A2M4 (day) 0 to enable, 1 to disable, DY/DT (dayofweek == 1/dayofmonth == 0) -
void DS3231_set_a2(uint8_t mi, const uint8_t h, const uint8_t d, const uint8_t * flags)
{
uint8_t t[3] = { mi, h, d };
uint8_t i;
Wire.beginTransmission(DS3231_I2C_ADDR);
Wire.write(DS3231_ALARM2_ADDR);
for (i = 0; i <= 2; i++) {
if (i == 2) {
Wire.write(dectobcd(t[2]) | (flags[2] << 7) | (flags[3] << 6));
} else
Wire.write(dectobcd(t[i]) | (flags[i] << 7));
}
Wire.endTransmission();
}
void DS3231_get_a2(char *buf, const uint8_t len)
{
uint8_t n[3];
uint8_t t[3]; //second,minute,hour,day
uint8_t f[4]; // flags
uint8_t i;
Wire.beginTransmission(DS3231_I2C_ADDR);
Wire.write(DS3231_ALARM2_ADDR);
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDR, 3);
for (i = 0; i <= 2; i++) {
n[i] = Wire.read();
f[i] = (n[i] & 0x80) >> 7;
t[i] = bcdtodec(n[i] & 0x7F);
}
f[3] = (n[2] & 0x40) >> 6;
t[2] = bcdtodec(n[2] & 0x3F);
snprintf(buf, len, "m%02d h%02d d%02d fm%d h%d d%d wm%d %d %d %d", t[0],
t[1], t[2], f[0], f[1], f[2], f[3], n[0], n[1], n[2]);
}
// when the alarm flag is cleared the pulldown on INT is also released
void DS3231_clear_a2f(void)
{
uint8_t reg_val;
reg_val = DS3231_get_sreg() & ~DS3231_A2F;
DS3231_set_sreg(reg_val);
}
uint8_t DS3231_triggered_a2(void)
{
return DS3231_get_sreg() & DS3231_A2F;
}
// helpers
const uint8_t days_in_month [12] PROGMEM = { 31,28,31,30,31,30,31,31,30,31,30,31 };
// returns the number of seconds since 01.01.1970 00:00:00 UTC, valid for 2000..FIXME
uint32_t get_unixtime(struct ts t)
{
uint8_t i;
uint16_t d;
int16_t y;
uint32_t rv;
if (t.year >= 2000) {
y = t.year - 2000;
} else {
return 0;
}
d = t.mday - 1;
for (i=1; i<t.mon; i++) {
d += pgm_read_byte(days_in_month + i - 1);
}
if (t.mon > 2 && y % 4 == 0) {
d++;
}
// count leap days
d += (365 * y + (y + 3) / 4);
rv = ((d * 24UL + t.hour) * 60 + t.min) * 60 + t.sec + SECONDS_FROM_1970_TO_2000;
return rv;
}
uint8_t dectobcd(const uint8_t val)
{
return ((val / 10 * 16) + (val % 10));
}
uint8_t bcdtodec(const uint8_t val)
{
return ((val / 16 * 10) + (val % 16));
}
uint8_t inp2toi(char *cmd, const uint16_t seek)
{
uint8_t rv;
rv = (cmd[seek] - 48) * 10 + cmd[seek + 1] - 48;
return rv;
}
@@ -0,0 +1,89 @@
#ifndef __ds3231_h_
#define __ds3231_h_
//#if ARDUINO >= 100
#include <Arduino.h>
//#else
//#include <WProgram.h>
//#endif
#include "config.h"
#define SECONDS_FROM_1970_TO_2000 946684800
// i2c slave address of the DS3231 chip
#define DS3231_I2C_ADDR 0x68
// timekeeping registers
#define DS3231_TIME_CAL_ADDR 0x00
#define DS3231_ALARM1_ADDR 0x07
#define DS3231_ALARM2_ADDR 0x0B
#define DS3231_CONTROL_ADDR 0x0E
#define DS3231_STATUS_ADDR 0x0F
#define DS3231_AGING_OFFSET_ADDR 0x10
#define DS3231_TEMPERATURE_ADDR 0x11
// control register bits
#define DS3231_A1IE 0x1
#define DS3231_A2IE 0x2
#define DS3231_INTCN 0x4
// status register bits
#define DS3231_A1F 0x1
#define DS3231_A2F 0x2
#define DS3231_OSF 0x80
struct ts {
uint8_t sec; /* seconds */
uint8_t min; /* minutes */
uint8_t hour; /* hours */
uint8_t mday; /* day of the month */
uint8_t mon; /* month */
int16_t year; /* year */
uint8_t wday; /* day of the week */
uint8_t yday; /* day in the year */
uint8_t isdst; /* daylight saving time */
uint8_t year_s; /* year in short notation*/
uint32_t unixtime; /* seconds since 01.01.1970 00:00:00 UTC*/
};
void DS3231_init(const uint8_t creg);
void DS3231_set(struct ts t);
bool DS3231_get(struct ts *t);
void DS3231_set_addr(const uint8_t addr, const uint8_t val);
uint8_t DS3231_get_addr(const uint8_t addr);
// control/status register
void DS3231_set_creg(const uint8_t val);
void DS3231_set_sreg(const uint8_t val);
uint8_t DS3231_get_sreg(void);
// aging offset register
void DS3231_set_aging(const int8_t val);
int8_t DS3231_get_aging(void);
// temperature register
float DS3231_get_treg(void);
// alarms
void DS3231_set_a1(const uint8_t s, const uint8_t mi, const uint8_t h, const uint8_t d,
const uint8_t * flags);
void DS3231_get_a1(struct ts *td);
void DS3231_clear_a1f(void);
uint8_t DS3231_triggered_a1(void);
void DS3231_set_a2(const uint8_t mi, const uint8_t h, const uint8_t d, const uint8_t * flags);
void DS3231_get_a2(char *buf, const uint8_t len);
void DS3231_clear_a2f(void);
uint8_t DS3231_triggered_a2(void);
// helpers
uint32_t get_unixtime(struct ts t);
uint8_t dectobcd(const uint8_t val);
uint8_t bcdtodec(const uint8_t val);
uint8_t inp2toi(char *cmd, const uint16_t seek);
#endif
@@ -0,0 +1,12 @@
# Board settings.
#BOARD = atmega328
#BOARD = pro5v328
MON_SPEED = 9600
# Where to find header files and libraries.
LIB_DIRS = ../../ /local/arduino-100/libraries/Wire/ /local/arduino-100/libraries/Wire/utility/
include ../../../Makefile.master
@@ -0,0 +1,142 @@
#include <Wire.h>
#include "ds3231.h"
#define BUFF_MAX 128
uint8_t time[8];
char recv[BUFF_MAX];
unsigned int recv_size = 0;
unsigned long prev, interval = 5000;
void parse_cmd(char *cmd, int cmdsize);
void setup()
{
Serial.begin(9600);
Wire.begin();
DS3231_init(DS3231_INTCN);
memset(recv, 0, BUFF_MAX);
Serial.println("GET time");
}
void loop()
{
char in;
char buff[BUFF_MAX];
unsigned long now = millis();
struct ts t;
// show time once in a while
if ((now - prev > interval) && (Serial.available() <= 0)) {
DS3231_get(&t);
// there is a compile time option in the library to include unixtime support
#ifdef CONFIG_UNIXTIME
snprintf(buff, BUFF_MAX, "%d.%02d.%02d %02d:%02d:%02d %ld", t.year,
t.mon, t.mday, t.hour, t.min, t.sec, t.unixtime);
#else
snprintf(buff, BUFF_MAX, "%d.%02d.%02d %02d:%02d:%02d", t.year,
t.mon, t.mday, t.hour, t.min, t.sec);
#endif
Serial.println(buff);
prev = now;
}
if (Serial.available() > 0) {
in = Serial.read();
if ((in == 10 || in == 13) && (recv_size > 0)) {
parse_cmd(recv, recv_size);
recv_size = 0;
recv[0] = 0;
} else if (in < 48 || in > 122) {; // ignore ~[0-9A-Za-z]
} else if (recv_size > BUFF_MAX - 2) { // drop lines that are too long
// drop
recv_size = 0;
recv[0] = 0;
} else if (recv_size < BUFF_MAX - 2) {
recv[recv_size] = in;
recv[recv_size + 1] = 0;
recv_size += 1;
}
}
}
void parse_cmd(char *cmd, int cmdsize)
{
uint8_t i;
uint8_t reg_val;
char buff[BUFF_MAX];
struct ts t;
//snprintf(buff, BUFF_MAX, "cmd was '%s' %d\n", cmd, cmdsize);
//Serial.print(buff);
// TssmmhhWDDMMYYYY aka set time
if (cmd[0] == 84 && cmdsize == 16) {
//T355720619112011
t.sec = inp2toi(cmd, 1);
t.min = inp2toi(cmd, 3);
t.hour = inp2toi(cmd, 5);
t.wday = cmd[7] - 48;
t.mday = inp2toi(cmd, 8);
t.mon = inp2toi(cmd, 10);
t.year = inp2toi(cmd, 12) * 100 + inp2toi(cmd, 14);
DS3231_set(t);
Serial.println("OK");
} else if (cmd[0] == 49 && cmdsize == 1) { // "1" get alarm 1
DS3231_get_a1(&buff[0], 59);
Serial.println(buff);
} else if (cmd[0] == 50 && cmdsize == 1) { // "2" get alarm 1
DS3231_get_a2(&buff[0], 59);
Serial.println(buff);
} else if (cmd[0] == 51 && cmdsize == 1) { // "3" get aging register
Serial.print("aging reg is ");
Serial.println(DS3231_get_aging(), DEC);
} else if (cmd[0] == 65 && cmdsize == 9) { // "A" set alarm 1
DS3231_set_creg(DS3231_INTCN | DS3231_A1IE);
//ASSMMHHDD
for (i = 0; i < 4; i++) {
time[i] = (cmd[2 * i + 1] - 48) * 10 + cmd[2 * i + 2] - 48; // ss, mm, hh, dd
}
uint8_t flags[5] = { 0, 0, 0, 0, 0 };
DS3231_set_a1(time[0], time[1], time[2], time[3], flags);
DS3231_get_a1(&buff[0], 59);
Serial.println(buff);
} else if (cmd[0] == 66 && cmdsize == 7) { // "B" Set Alarm 2
DS3231_set_creg(DS3231_INTCN | DS3231_A2IE);
//BMMHHDD
for (i = 0; i < 4; i++) {
time[i] = (cmd[2 * i + 1] - 48) * 10 + cmd[2 * i + 2] - 48; // mm, hh, dd
}
uint8_t flags[5] = { 0, 0, 0, 0 };
DS3231_set_a2(time[0], time[1], time[2], flags);
DS3231_get_a2(&buff[0], 59);
Serial.println(buff);
} else if (cmd[0] == 67 && cmdsize == 1) { // "C" - get temperature register
Serial.print("temperature reg is ");
Serial.println(DS3231_get_treg(), DEC);
} else if (cmd[0] == 68 && cmdsize == 1) { // "D" - reset status register alarm flags
reg_val = DS3231_get_sreg();
reg_val &= B11111100;
DS3231_set_sreg(reg_val);
} else if (cmd[0] == 70 && cmdsize == 1) { // "F" - custom fct
reg_val = DS3231_get_addr(0x5);
Serial.print("orig ");
Serial.print(reg_val,DEC);
Serial.print("month is ");
Serial.println(bcdtodec(reg_val & 0x1F),DEC);
} else if (cmd[0] == 71 && cmdsize == 1) { // "G" - set aging status register
DS3231_set_aging(0);
} else if (cmd[0] == 83 && cmdsize == 1) { // "S" - get status register
Serial.print("status reg is ");
Serial.println(DS3231_get_sreg(), DEC);
} else {
Serial.print("unknown command prefix ");
Serial.println(cmd[0]);
Serial.println(cmd[0], DEC);
}
}