第四章——整合打包代码
完整的 DS3231 I2C 通信代码如下:
#include <Wire.h>
#define DS3231_ADDRESS 0x68
#define SECOND_ADDRESS 0x00
#define ALARM1_SECOND_ADDRESS 0x07
#define ALARM2_MINUTE_ADDRESS 0x0B
#define STATUS_ADDRESS 0x0F
#define TEMPERATURE_ADDRESS 0x11
// 时间的结构体
struct Time {
uint16_t year; //年
uint8_t mon; //月
uint8_t date; //日
uint8_t hour; //时
uint8_t min; //分
uint8_t sec; //秒
uint8_t day; //星期
};
// 星期字符串
char weekofday[7][10] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
uint8_t bcd2dec(uint8_t);
uint8_t dec2bcd(uint8_t);
void write_data(uint8_t);
void write_data(uint8_t*, uint8_t);
uint8_t read_data();
void write_data(uint8_t*, uint8_t);
Time readTime();
void setTime(Time);
void setAlarm(uint8_t, uint8_t, bool choice = false);
bool readAlarmFlag(bool choice = false);
void turnAlarmOFF(bool choice = false);
int8_t readTemp();
void setup() {
Wire.begin();// 初始化I2C
Wire.setClock(100 * 1000);// 设置I2C频率为100KHz
Serial.begin(115200);
Time now = {2022, 2, 10, 12, 30, 0, 4}; // 2022-2-10 12:30:0 Thursday
setTime(now);
setAlarm(12, 31);
turnAlarmOFF();
}
void loop() {
char str[50];
Time now = readTime();// 更新时间
sprintf(// 格式化一段字符串数据
str,
"%d-%d-%d %d:%d:%d %s %d^C ",
now.year, now.mon, now.date, now.hour, now.min, now.sec, weekofday[now.day], readTemp()
);
Serial.print(str);
if (readAlarmFlag()) {
Serial.println("Alarm ON!");
turnAlarmOFF();
}
else Serial.println("Alarm OFF!");
delay(1000);
}
// 将BCD数据转换成DEC格式
uint8_t bcd2dec(uint8_t bcd) {
uint8_t ones = bcd % 0x10;
uint8_t tens = bcd / 0x10;
uint8_t dec = ones + tens * 10;
return dec;
}
// 将DEC数据转换成BCD格式
uint8_t dec2bcd(uint8_t dec) {
uint8_t ones = dec % 10;
uint8_t tens = dec / 10;
uint8_t bcd = ones + tens * 0x10;
return bcd;
}
// 写入一个字节
void write_data(uint8_t data) {
Wire.beginTransmission(DS3231_ADDRESS);
Wire.write(data);
Wire.endTransmission();
}
// 写入一个指定长度的数组
void write_data(uint8_t *data, uint8_t length) {
Wire.beginTransmission(DS3231_ADDRESS);
Wire.write(data, length);
Wire.endTransmission();
}
// 读取一个字节
uint8_t read_data() {
Wire.requestFrom(DS3231_ADDRESS, 1);
return Wire.read();
}
// 读取一个指定长度的数组
void read_data(uint8_t* data, uint8_t length) {
Wire.requestFrom(DS3231_ADDRESS, length);
for (uint8_t i = 0; i < length; i++) {
data[i] = Wire.read();
}
}
// 连续读取整个时间
Time readTime() {
Time now;
uint8_t data[7];
write_data(SECOND_ADDRESS);
read_data(data, 7);
// 读取后的数据需要转换格式
now.sec = bcd2dec(data[0]);
now.min = bcd2dec(data[1]);
now.hour = bcd2dec(data[2]);
now.day = bcd2dec(data[3]);
now.date = bcd2dec(data[4]);
now.mon = bcd2dec(data[5]);
now.year = bcd2dec(data[6]) + 2000;
return now;
}
// 连续设置整个时间
void setTime(Time now) {
// 记得写入数据前先将DEC数据转换成BCD格式
uint8_t data[] = {
SECOND_ADDRESS,
dec2bcd(now.sec),
dec2bcd(now.min),
dec2bcd(now.hour),
dec2bcd(now.day),
dec2bcd(now.date),
dec2bcd(now.mon),
dec2bcd(now.year % 100),
};
write_data(data, 8);
}
// 设置闹钟,hour:minute格式,每日闹铃,记得写入数据前先将DEC数据转换成BCD格式
void setAlarm(uint8_t hour, uint8_t minute, bool choice = false) {
// 闹钟1
if (!choice) {
uint8_t data[] = {
ALARM1_SECOND_ADDRESS,
0, //秒,A1M1=0
dec2bcd(minute), //分,A1M2=0
dec2bcd(hour), //时,A1M3=0
0x80, //天/星期,A1M4=1
};
write_data(data, 5);
}
// 闹钟2
else {
uint8_t data[] = {
ALARM2_MINUTE_ADDRESS,
dec2bcd(minute), //分,A2M2=0
dec2bcd(hour), //时,A2M3=0
0x80, //天/星期,A2M4=1
};
write_data(data, 4);
}
}
bool readAlarmFlag(bool choice = false) {
write_data(STATUS_ADDRESS);
// 闹钟1Flag
if (!choice) {
bool flag = read_data() & 0x01;
return flag;
}
// 闹钟2flag
else {
bool flag = (read_data() & 0x02) >> 1;
return flag;
}
}
// 关闭闹钟Flag
void turnAlarmOFF(bool choice = false) {
write_data(STATUS_ADDRESS);
uint8_t mask = 0xFE;
if (choice)mask = 0xFD;
uint8_t data[] = {STATUS_ADDRESS, read_data() & mask};
write_data(data, 2);
}
// 读取温度,忽略温度的小数位,只读取整数位,从而温度在DS3231中的数据类型int8_t
int8_t readTemp() {
write_data(TEMPERATURE_ADDRESS);
int8_t temperature = read_data();
return temperature;
}