Support for BM8563 RTC chip (I2C) found in M5Stack Core2 and M5StickC

This commit is contained in:
Stephan Hadinger 2021-05-26 21:47:07 +02:00
parent 90330a5000
commit c1f82141b9
7 changed files with 600 additions and 2 deletions

View File

@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
## [9.4.0.4]
### Added
- Version bump to signal new features to Hass
- Support for BM8563 RTC chip (I2C) found in M5Stack Core2 and M5StickC
## [9.4.0.3] 20210515
### Added

View File

@ -91,4 +91,5 @@ Index | Define | Driver | Device | Address(es) | Description
55 | USE_EZOPMP | xsns_78 | EZOPMP | 0x61 - 0x70 | Peristaltic Pump
56 | USE_SEESAW_SOIL | xsns_81 | SEESOIL | 0x36 - 0x39 | Adafruit seesaw soil moisture sensor
57 | USE_TOF10120 | xsns_84 | TOF10120 | 0x52 | Time-of-flight (ToF) distance sensor
58 | USE_MPU6886 | xsns_85 | MPU6886 | 0x68 | MPU6886 M5Stack
58 | USE_MPU6886 | xsns_85 | MPU6886 | 0x68 | MPU6886 M5Stack
59 | USE_BM8563 | xdrv_56 | BM8563 | 0x51 | BM8563 RTC from M5Stack

View File

@ -0,0 +1,9 @@
name=BME8563
version=
author=
maintainer=
sentence=Driver for BM8563 RTC
paragraph=Driver for BM8563 RTC
category=Driver
url=
architectures=esp8266,esp32

View File

@ -0,0 +1,352 @@
#include "BM8563.h"
BM8563::BM8563()
{
}
void BM8563::begin(void)
{
WriteReg(0x00,0x00);
WriteReg(0x01,0x00);
WriteReg(0x0D,0x00);
}
void BM8563::WriteReg(uint8_t reg, uint8_t data)
{
myWire->beginTransmission(BM8563_ADRESS);
myWire->write(reg);
myWire->write(data);
myWire->endTransmission();
}
uint8_t BM8563::ReadReg(uint8_t reg)
{
myWire->beginTransmission(BM8563_ADRESS);
myWire->write(reg);
myWire->endTransmission();
myWire->requestFrom(BM8563_ADRESS, 1);
return myWire->read();
}
void BM8563::GetBm8563Time(void)
{
myWire->beginTransmission(BM8563_ADRESS);
myWire->write(0x02);
myWire->endTransmission();
myWire->requestFrom(BM8563_ADRESS, 7);
while (myWire->available())
{
trdata[0] = myWire->read();
trdata[1] = myWire->read();
trdata[2] = myWire->read();
trdata[3] = myWire->read();
trdata[4] = myWire->read();
trdata[5] = myWire->read();
trdata[6] = myWire->read();
}
DataMask();
Bcd2asc();
Str2Time();
}
void BM8563::Str2Time(void)
{
Second = (asc[0] - 0x30) * 10 + asc[1] - 0x30;
Minute = (asc[2] - 0x30) * 10 + asc[3] - 0x30;
Hour = (asc[4] - 0x30) * 10 + asc[5] - 0x30;
/*
uint8_t Hour;
uint8_t Week;
uint8_t Day;
uint8_t Month;
uint8_t Year;
*/
}
void BM8563::DataMask()
{
trdata[0] = trdata[0] & 0x7f; //秒
trdata[1] = trdata[1] & 0x7f; //分
trdata[2] = trdata[2] & 0x3f; //时
trdata[3] = trdata[3] & 0x3f; //日
trdata[4] = trdata[4] & 0x07; //星期
trdata[5] = trdata[5] & 0x1f; //月
trdata[6] = trdata[6] & 0xff; //年
}
/********************************************************************
void Bcd2asc(void)
bcd asc Lcd显示用
***********************************************************************/
void BM8563::Bcd2asc(void)
{
uint8_t i, j;
for (j = 0, i = 0; i < 7; i++)
{
asc[j++] = (trdata[i] & 0xf0) >> 4 | 0x30; /*格式为: 秒 分 时 日 月 星期 年 */
asc[j++] = (trdata[i] & 0x0f) | 0x30;
}
}
uint8_t BM8563::Bcd2ToByte(uint8_t Value)
{
uint8_t tmp = 0;
tmp = ((uint8_t)(Value & (uint8_t)0xF0) >> (uint8_t)0x4) * 10;
return (tmp + (Value & (uint8_t)0x0F));
}
uint8_t BM8563::ByteToBcd2(uint8_t Value)
{
uint8_t bcdhigh = 0;
while (Value >= 10)
{
bcdhigh++;
Value -= 10;
}
return ((uint8_t)(bcdhigh << 4) | Value);
}
void BM8563::GetTime(RTC_TimeTypeDef *RTC_TimeStruct)
{
//if()
uint8_t buf[3] = {0};
myWire->beginTransmission(BM8563_ADRESS);
myWire->write(0x02);
myWire->endTransmission();
myWire->requestFrom(BM8563_ADRESS, 3);
while (myWire->available())
{
buf[0] = myWire->read();
buf[1] = myWire->read();
buf[2] = myWire->read();
}
RTC_TimeStruct->Seconds = Bcd2ToByte(buf[0] & 0x7f); //秒
RTC_TimeStruct->Minutes = Bcd2ToByte(buf[1] & 0x7f); //分
RTC_TimeStruct->Hours = Bcd2ToByte(buf[2] & 0x3f); //时
}
void BM8563::SetTime(RTC_TimeTypeDef *RTC_TimeStruct)
{
if (RTC_TimeStruct == NULL)
return;
myWire->beginTransmission(BM8563_ADRESS);
myWire->write(0x02);
myWire->write(ByteToBcd2(RTC_TimeStruct->Seconds));
myWire->write(ByteToBcd2(RTC_TimeStruct->Minutes));
myWire->write(ByteToBcd2(RTC_TimeStruct->Hours));
myWire->endTransmission();
}
void BM8563::GetDate(RTC_DateTypeDef *RTC_DateStruct)
{
uint8_t buf[4] = {0};
myWire->beginTransmission(BM8563_ADRESS);
myWire->write(0x05);
myWire->endTransmission();
myWire->requestFrom(BM8563_ADRESS, 4);
while (myWire->available())
{
buf[0] = myWire->read();
buf[1] = myWire->read();
buf[2] = myWire->read();
buf[3] = myWire->read();
}
RTC_DateStruct->Date = Bcd2ToByte(buf[0] & 0x3f);
RTC_DateStruct->WeekDay = Bcd2ToByte(buf[1] & 0x07);
RTC_DateStruct->Month = Bcd2ToByte(buf[2] & 0x1f);
if (buf[2] & 0x80)
{
RTC_DateStruct->Year = 1900 + Bcd2ToByte(buf[3] & 0xff);
}
else
{
RTC_DateStruct->Year = 2000 + Bcd2ToByte(buf[3] & 0xff);
}
}
void BM8563::SetDate(RTC_DateTypeDef *RTC_DateStruct)
{
if (RTC_DateStruct == NULL)
return;
myWire->beginTransmission(BM8563_ADRESS);
myWire->write(0x05);
myWire->write(ByteToBcd2(RTC_DateStruct->Date));
myWire->write(ByteToBcd2(RTC_DateStruct->WeekDay));
if (RTC_DateStruct->Year < 2000)
{
myWire->write(ByteToBcd2(RTC_DateStruct->Month) | 0x80);
myWire->write(ByteToBcd2((uint8_t)(RTC_DateStruct->Year % 100)));
}
else
{
/* code */
myWire->write(ByteToBcd2(RTC_DateStruct->Month) | 0x00);
myWire->write(ByteToBcd2((uint8_t)(RTC_DateStruct->Year % 100)));
}
myWire->endTransmission();
}
int BM8563::SetAlarmIRQ(int afterSeconds)
{
uint8_t reg_value = 0;
reg_value = ReadReg(0x01);
if (afterSeconds < 0)
{
reg_value &= ~(1 << 0);
WriteReg(0x01, reg_value);
reg_value = 0x03;
WriteReg(0x0E, reg_value);
return -1;
}
uint8_t type_value = 2;
uint8_t div = 1;
if (afterSeconds > 255)
{
div = 60;
type_value = 0x83;
}
else
{
type_value = 0x82;
}
afterSeconds = (afterSeconds / div) & 0xFF;
WriteReg(0x0F, afterSeconds);
WriteReg(0x0E, type_value);
reg_value |= (1 << 0);
reg_value &= ~(1 << 7);
WriteReg(0x01, reg_value);
return afterSeconds * div;
}
int BM8563::SetAlarmIRQ(const RTC_TimeTypeDef &RTC_TimeStruct)
{
uint8_t irq_enable = false;
uint8_t out_buf[4] = {0x80, 0x80, 0x80, 0x80};
if (RTC_TimeStruct.Minutes >= 0)
{
irq_enable = true;
out_buf[0] = ByteToBcd2(RTC_TimeStruct.Minutes) & 0x7f;
}
if (RTC_TimeStruct.Hours >= 0)
{
irq_enable = true;
out_buf[1] = ByteToBcd2(RTC_TimeStruct.Hours) & 0x3f;
}
//out_buf[2] = 0x00;
//out_buf[3] = 0x00;
uint8_t reg_value = ReadReg(0x01);
if (irq_enable)
{
reg_value |= (1 << 1);
}
else
{
reg_value &= ~(1 << 1);
}
for (int i = 0; i < 4; i++)
{
WriteReg(0x09 + i, out_buf[i]);
}
WriteReg(0x01, reg_value);
return irq_enable ? 1 : 0;
}
int BM8563::SetAlarmIRQ(const RTC_DateTypeDef &RTC_DateStruct, const RTC_TimeTypeDef &RTC_TimeStruct)
{
uint8_t irq_enable = false;
uint8_t out_buf[4] = {0x80, 0x80, 0x80, 0x80};
if (RTC_TimeStruct.Minutes >= 0)
{
irq_enable = true;
out_buf[0] = ByteToBcd2(RTC_TimeStruct.Minutes) & 0x7f;
}
if (RTC_TimeStruct.Hours >= 0)
{
irq_enable = true;
out_buf[1] = ByteToBcd2(RTC_TimeStruct.Hours) & 0x3f;
}
if (RTC_DateStruct.Date >= 0)
{
irq_enable = true;
out_buf[2] = ByteToBcd2(RTC_DateStruct.Date) & 0x3f;
}
if (RTC_DateStruct.WeekDay >= 0)
{
irq_enable = true;
out_buf[3] = ByteToBcd2(RTC_DateStruct.WeekDay) & 0x07;
}
uint8_t reg_value = ReadReg(0x01);
if (irq_enable)
{
reg_value |= (1 << 1);
}
else
{
reg_value &= ~(1 << 1);
}
for (int i = 0; i < 4; i++)
{
WriteReg(0x09 + i, out_buf[i]);
}
WriteReg(0x01, reg_value);
return irq_enable ? 1 : 0;
}
void BM8563::clearIRQ()
{
uint8_t data = ReadReg(0x01);
WriteReg(0x01, data & 0xf3);
}
void BM8563::disableIRQ()
{
clearIRQ();
uint8_t data = ReadReg(0x01);
WriteReg(0x01, data & 0xfC);
}

View File

@ -0,0 +1,82 @@
#ifndef __MB8563_H__
#define __MB8563_H__
#include <Wire.h>
#define BM8563_ADRESS 0x51
typedef struct
{
uint8_t Hours;
uint8_t Minutes;
uint8_t Seconds;
}RTC_TimeTypeDef;
typedef struct
{
uint8_t WeekDay;
uint8_t Month;
uint8_t Date;
uint16_t Year;
}RTC_DateTypeDef;
class BM8563 {
public:
BM8563();
#ifdef ESP32
void setBus(uint32_t _bus) { myWire = _bus ? &Wire1 : &Wire; };
#else
void setBus(uint32_t _bus) { myWire = &Wire; };
#endif
void begin(void);
void GetBm8563Time(void);
void SetTime(RTC_TimeTypeDef* RTC_TimeStruct);
void SetDate(RTC_DateTypeDef* RTC_DateStruct);
void GetTime(RTC_TimeTypeDef* RTC_TimeStruct);
void GetDate(RTC_DateTypeDef* RTC_DateStruct);
int SetAlarmIRQ(int afterSeconds);
int SetAlarmIRQ( const RTC_TimeTypeDef &RTC_TimeStruct);
int SetAlarmIRQ( const RTC_DateTypeDef &RTC_DateStruct, const RTC_TimeTypeDef &RTC_TimeStruct);
void clearIRQ();
void disableIRQ();
public:
uint8_t Second;
uint8_t Minute;
uint8_t Hour;
uint8_t Week;
uint8_t Day;
uint8_t Month;
uint8_t Year;
uint8_t DateString[9];
uint8_t TimeString[9];
uint8_t asc[14];
private:
TwoWire * myWire = &Wire; // default to Wire (bus 0)
void Bcd2asc(void);
void DataMask();
void Str2Time(void);
void WriteReg(uint8_t reg, uint8_t data);
uint8_t ReadReg(uint8_t reg);
uint8_t Bcd2ToByte(uint8_t Value);
uint8_t ByteToBcd2(uint8_t Value);
private:
/*Define an array to store the time data read */
uint8_t trdata[7];
/* Define an array to store the converted asc code time data */
//uint8_t asc[14];
};
#endif // __MB8563_H__

View File

@ -628,7 +628,8 @@
// #define USE_EZORGB // [I2cDriver55] Enable support for EZO's RGB sensor (+0k5 code) - Shared EZO code required for any EZO device (+1k2 code)
// #define USE_EZOPMP // [I2cDriver55] Enable support for EZO's PMP sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code)
// #define USE_SEESAW_SOIL // [I2cDriver56] Enable Capacitice Soil Moisture & Temperature Sensor (I2C addresses 0x36 - 0x39) (+1k3 code)
// #define USE_MPU6886 // [I2cDriver58] Enable MPU6886 - found in M5Stack - support 2 I2C buses on ESP32 (I2C address 0x68) (+2k code)
// #define USE_MPU6886 // [I2cDriver58] Enable MPU6886 - found in M5Stack - support both I2C buses on ESP32 (I2C address 0x68) (+2k code)
// #define USE_BM8563 // [I2cDriver58] Enable BM8563 RTC - found in M5Stack - support both I2C buses on ESP32 (I2C address 0x51) (+2.5k code)
// #define USE_DISPLAY // Add I2C Display Support (+2k code)
#define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0

View File

@ -0,0 +1,152 @@
/*
xdrv_52_9_berry.ino - Berry scripting language
Copyright (C) 2021 Stephan Hadinger, Berry language by Guan Wenliang https://github.com/Skiars/berry
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/>.
*/
#ifdef USE_I2C
#ifdef USE_BM8563
#define XDRV_56 56
#define XI2C_59 59 // See I2CDEVICES.md
#include "BM8563.h"
struct {
BM8563 Rtc;
bool rtc_ready = false;
bool ntp_time_ok = false;
} bm8563_driver;
/*********************************************************************************************\
*
*
\*********************************************************************************************/
void BM8563Detect(void) {
#ifdef ESP32
if (!I2cSetDevice(BM8563_ADRESS, 0)) {
if (!I2cSetDevice(BM8563_ADRESS, 1)) { return; } // check on bus 1
bm8563_driver.Rtc.setBus(1); // switch to bus 1
I2cSetActiveFound(BM8563_ADRESS, "BM8563", 1);
} else {
I2cSetActiveFound(BM8563_ADRESS, "BM8563", 0);
}
#else
if (!I2cSetDevice(BM8563_ADRESS)) { return; }
I2cSetActiveFound(BM8563_ADRESS, "BM8563");
#endif
bm8563_driver.Rtc.begin();
bm8563_driver.rtc_ready = true;
}
uint32_t BM8563GetUtc(void) {
if (!bm8563_driver.rtc_ready) return 0;
RTC_TimeTypeDef RTCtime;
// 1. read has errors ???
bm8563_driver.Rtc.GetTime(&RTCtime);
// core2_globs.Rtc.GetTime(&RTCtime);
RTC_DateTypeDef RTCdate;
bm8563_driver.Rtc.GetDate(&RTCdate);
TIME_T tm;
tm.second = RTCtime.Seconds;
tm.minute = RTCtime.Minutes;
tm.hour = RTCtime.Hours;
tm.day_of_week = RTCdate.WeekDay;
tm.day_of_month = RTCdate.Date;
tm.month = RTCdate.Month;
tm.year = RTCdate.Year - 1970;
return MakeTime(tm);
}
void BM8563SetUtc(uint32_t epoch_time) {
if (!bm8563_driver.rtc_ready) return;
TIME_T tm;
BreakTime(epoch_time, tm);
RTC_TimeTypeDef RTCtime;
RTCtime.Hours = tm.hour;
RTCtime.Minutes = tm.minute;
RTCtime.Seconds = tm.second;
bm8563_driver.Rtc.SetTime(&RTCtime);
RTC_DateTypeDef RTCdate;
RTCdate.WeekDay = tm.day_of_week;
RTCdate.Month = tm.month;
RTCdate.Date = tm.day_of_month;
RTCdate.Year = tm.year + 1970;
bm8563_driver.Rtc.SetDate(&RTCdate);
}
void InitTimeFromRTC(void) {
if (bm8563_driver.rtc_ready && Rtc.utc_time < START_VALID_TIME) {
// set rtc from chip
Rtc.utc_time = BM8563GetUtc();
TIME_T tmpTime;
TasmotaGlobal.ntp_force_sync = true; // Force to sync with ntp
BreakTime(Rtc.utc_time, tmpTime);
Rtc.daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year);
Rtc.standard_time = RuleToTime(Settings.tflag[0], RtcTime.year);
AddLog(LOG_LEVEL_INFO, PSTR("I2C: Set time from BM8563 to RTC (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"),
GetDateAndTime(DT_UTC).c_str(), GetDateAndTime(DT_DST).c_str(), GetDateAndTime(DT_STD).c_str());
if (Rtc.local_time < START_VALID_TIME) { // 2016-01-01
TasmotaGlobal.rules_flag.time_init = 1;
} else {
TasmotaGlobal.rules_flag.time_set = 1;
}
}
}
void BM8563EverySecond(void) {
if (bm8563_driver.rtc_ready) {
if (!bm8563_driver.ntp_time_ok && Rtc.utc_time > START_VALID_TIME && abs(Rtc.utc_time - BM8563GetUtc()) > 3) {
BM8563SetUtc(Rtc.utc_time);
AddLog(LOG_LEVEL_INFO, PSTR("I2C: Write Time TO BM8563 from NTP (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"),
GetDateAndTime(DT_UTC).c_str(), GetDateAndTime(DT_DST).c_str(), GetDateAndTime(DT_STD).c_str());
bm8563_driver.ntp_time_ok = true;
}
}
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xdrv56(uint8_t function)
{
bool result = false;
if (!I2cEnabled(XI2C_59)) { return false; }
switch (function) {
// case FUNC_PRE_INIT: // we start Berry in pre_init so that other modules can call Berry in their init methods
case FUNC_INIT:
BM8563Detect();
InitTimeFromRTC();
break;
case FUNC_EVERY_SECOND:
BM8563EverySecond();
break;
case FUNC_SAVE_BEFORE_RESTART:
break;
}
return result;
}
#endif // USE_BM8563
#endif // USE_I2C