DS3231是高精度I2C實時時鐘晶元, I2C匯流排地址為固定的 0xD0, 內置溫度補償晶體振蕩源(TCXO), 降低溫度變化造成的晶體頻率漂移, 在[-40°C, 85°C]範圍內誤差 ±0.432s/Day, 秒、分、時、星期、日期、月、年, 閏年補償, 計數年份區間為[1990, 2190]... ...
目錄
- STC8H開發(一): 在Keil5中配置和使用FwLib_STC8封裝庫(圖文詳解)
- STC8H開發(二): 在Linux VSCode中配置和使用FwLib_STC8封裝庫(圖文詳解)
- STC8H開發(三): 基於FwLib_STC8的模數轉換ADC介紹和演示用例說明
- STC8H開發(四): FwLib_STC8 封裝庫的介紹和使用註意事項
- STC8H開發(五): SPI驅動nRF24L01無線模塊
- STC8H開發(六): SPI驅動ADXL345三軸加速度檢測模塊
- STC8H開發(七): I2C驅動MPU6050三軸加速度+三軸角速度檢測模塊
- STC8H開發(八): NRF24L01無線傳輸音頻(對講機原型)
- STC8H開發(九): STC8H8K64U模擬USB HID外設
- STC8H開發(十): SPI驅動Nokia5110 LCD(PCD8544)
- STC8H開發(十一): GPIO單線驅動多個DS18B20數字溫度計
- STC8H開發(十二): I2C驅動AT24C08,AT24C32系列EEPROM存儲
- STC8H開發(十三): I2C驅動DS3231高精度實時時鐘晶元
DS3231簡介
DS3231是高精度I2C實時時鐘晶元, I2C匯流排地址為固定的0xD0
- 內置溫度補償晶體振蕩源(TCXO), 降低溫度變化造成的晶體頻率漂移, 在[-40°C, 85°C]範圍內誤差 ±0.432s/Day.
- 秒、分、時、星期、日期、月、年, 閏年補償, 計數年份區間為[1990, 2190]
- 兩個可編程鬧鐘, 可以按周或按日重覆
- 方波輸出
- 供電 2.3V – 5.5V (typical: 3.3V)
- 工作電流 200 – 300 μA
- 待機電流 110 – 170 μA
- 電池工作電流 70 – 150 μA
- 時間保持電池電流 0.84 – 3.5 μA
DS3231管腳和典型電路
- 32KHz - 32.768KHz輸出(50%占空比), 漏極開路輸出, 需要上拉電阻, 如不使用可保持開路.
- VCC
- INT/SQW - 低電平有效中斷或方波輸出(1Hz, 4kHz, 8kHz or 32kHz)
- RST - 低電平有效複位引腳
- GND
- VBAT - 備用電源
- SDA - I2C 數據
- SCL - I2C 時鐘
ZS-042模塊
在某寶上最常見的DS3231是 ZS-042 模塊, 模塊集成一個CR2032電池座和一個AT24C32的8K位元組EEPROM存儲, 後者可以通過同一個I2C匯流排訪問.
CR2032電池座
當電源中斷時為DS3231提供備用電源
板載 AT24C32 EEPROM
存儲晶元 AT24C32, 容量 32K Bit = 4K Byte, 地址可通過短路 A0/A1/A2修改, 根據 24C32 的數據手冊, 這三個bit對應的是7位I2C地址的第五到第七位
1 0 1 0 A2 A1 A0 R/W
A0至A2內部電阻上拉, 開路為1, 短路為0, 不同的組合可以產生8個不同的地址, 預設全開路對應的地址為0xAE
使用STC8H3K驅動DS3231
接線
AT24C32的3對觸點都保持開路
P32 -> SCL
P33 -> SDA
GND -> GND
3.3V -> VCC
示例代碼
代碼下載地址
- Gitee https://gitee.com/iosetting/fw-lib_-stc8/tree/master/demo/i2c/ds3231
- GitHub https://github.com/IOsetting/FwLib_STC8/tree/master/demo/i2c/ds3231
代碼會將DS3231時間設置為 2022-07-10 14:21:10, 然後每隔一秒顯示一次時間, 數值為十六進位
20-07-0A 0E:15:1E 00 00␍␊
20-07-0A 0E:15:1F 00 00␍␊
20-07-0A 0E:15:20 00 00␍␊
20-07-0A 0E:15:21 00 00␍␊
20-07-0A 0E:15:22 00 00␍␊
初始化I2C介面
使用P32和P33
void I2C_Init(void)
{
// Master mode
I2C_SetWorkMode(I2C_WorkMode_Master);
/**
* I2C clock = FOSC / 2 / (__prescaler__ * 2 + 4)
*/
I2C_SetClockPrescaler(0x1F);
// Switch alternative port
I2C_SetPort(I2C_AlterPort_P32_P33);
// Start I2C
I2C_SetEnabled(HAL_State_ON);
}
void GPIO_Init(void)
{
// SDA
GPIO_P3_SetMode(GPIO_Pin_3, GPIO_Mode_InOut_QBD);
// SCL
GPIO_P3_SetMode(GPIO_Pin_2, GPIO_Mode_Output_PP);
}
基礎I2C介面讀寫方法
#define DS3231_I2C_ADDR 0xD0
uint8_t DS3231_Write(uint8_t reg, uint8_t dat)
{
return I2C_Write(DS3231_I2C_ADDR, reg, &dat, 1);
}
uint8_t DS3231_MultipleRead(uint8_t reg, uint8_t *buf, uint8_t len)
{
return I2C_Read(DS3231_I2C_ADDR, reg, buf, len);
}
BCD碼與HEX的轉換
uint8_t DS3231_Hex2Bcd(uint8_t hex)
{
return (hex % 10) + ((hex / 10) << 4);
}
uint8_t DS3231_Bcd2Hex(uint8_t bcd)
{
return (bcd >> 4) * 10 + (bcd & 0x0F);
}
讀取時間
讀取時間並轉換為HEX, 使用一個uint8_t數組, 結構為
/**
uint8_t year;
uint8_t month;
uint8_t week;
uint8_t date;
uint8_t hour;
uint8_t minute;
uint8_t second;
DS3231_HourFormat_t format;
DS3231_AmPm_t am_pm;
*/
從DS3231中讀出時間
uint8_t DS3231_GetTime(uint8_t *t)
{
uint8_t res;
res = I2C_Read(DS3231_I2C_ADDR, DS3231_REG_SECOND, buff, 7);
if (res != HAL_OK)
{
return res;
}
t[0] = DS3231_Bcd2Hex(buff[6]) + ((buff[5] >> 7) & 0x01) * 100; // year
t[1] = DS3231_Bcd2Hex(buff[5] & 0x1F); // month
t[2] = DS3231_Bcd2Hex(buff[3]); // week
t[3] = DS3231_Bcd2Hex(buff[4]); // date
t[7] = (buff[2] >> 6) & 0x01; // 12h/24h
t[8] = (buff[2] >> 5) & 0x01; // am/pm
if (t[7] == DS3231_FORMAT_12H)
{
t[4] = DS3231_Bcd2Hex(buff[2] & 0x1F); // hour
}
else
{
t[4] = DS3231_Bcd2Hex(buff[2] & 0x3F); // hour
}
t[5] = DS3231_Bcd2Hex(buff[1]); // minute
t[6] = DS3231_Bcd2Hex(buff[0]); // second
return HAL_OK;
}
設置時間
先校驗各時間數值, 然後通過地址分別寫入
uint8_t DS3231_SetTime(uint8_t *t)
{
uint8_t res, reg;
// Time validation
if (t[0] > 200) t[0] = 200; // year
if (t[1] == 0) t[1] = 1; // month
else if (t[1] > 12) t[1] = 12;
if (t[2] == 0) t[2] = 1; // week
else if (t[2] > 7) t[2] = 7;
if (t[3] == 0) t[3] = 1; // date
else if (t[3] > 31) t[3] = 31;
if (t[7] == DS3231_FORMAT_12H)
{
if (t[4] > 12) t[4] = 12; // hour
}
else if (t[7] == DS3231_FORMAT_24H)
{
if (t[4] > 23) t[4] = 23; // hour
}
if (t[5] > 59) t[5] = 59; // minute
if (t[6] > 59) t[6] = 59; // second
res = DS3231_Write(DS3231_REG_SECOND, DS3231_Hex2Bcd(t[6]));
if (res != HAL_OK) return res;
res = DS3231_Write(DS3231_REG_MINUTE, DS3231_Hex2Bcd(t[5]));
if (res != HAL_OK) return res;
if (t[7] == DS3231_FORMAT_12H)
{
reg = (uint8_t)((1 << 6) | (t[8] << 5) | DS3231_Hex2Bcd(t[4]));
}
else
{
reg = (0 << 6) | DS3231_Hex2Bcd(t[4]);
}
res = DS3231_Write(DS3231_REG_HOUR, reg);
if (res != HAL_OK) return res;
res = DS3231_Write(DS3231_REG_WEEK, DS3231_Hex2Bcd(t[2]));
if (res != HAL_OK) return res;
res = DS3231_Write(DS3231_REG_DATE, DS3231_Hex2Bcd(t[3]));
if (res != HAL_OK) return res;
if (t[0] >= 100)
{
res = DS3231_Write(DS3231_REG_MONTH, DS3231_Hex2Bcd(t[1]) | (1 << 7));
if (res != HAL_OK) return res;
return DS3231_Write(DS3231_REG_YEAR, DS3231_Hex2Bcd(t[0] - 100));
}
else
{
res = DS3231_Write(DS3231_REG_MONTH, DS3231_Hex2Bcd(t[1]));
if (res != HAL_OK) return res;
return DS3231_Write(DS3231_REG_YEAR, DS3231_Hex2Bcd(t[0]));
}
}
讀寫ZS-042模塊中的AT24C32
參考前面一篇 STC8H開發(十二): I2C驅動AT24C08,AT24C32系列EEPROM存儲
參考
- DS3231的模塊ZS-042的討論, 分析了5V供電時的電池充電問題和改造 https://forum.arduino.cc/t/zs-042-ds3231-rtc-module/268862/24
- AT24C 的讀寫 https://www.likecs.com/show-204385163.html