Files
rt-thread/components/drivers/rtc/rtc-ds1302.c
GUI e465ec567d [dm][rtc] update rtc and new drivers (#11033)
* [dd][rtc] set the RTC alarm thread stack size default.

Signed-off-by: GuEe-GUI <2991707448@qq.com>

* [dm][rtc] make Kconfig import for DM

Signed-off-by: GuEe-GUI <2991707448@qq.com>

* [dm][rtc] support DM API for RTC

1. rtc_dev_set_name for RTC device init the name auto.
2. rtc_wkalarm_to_timestamp and rtc_timestamp_to_wkalarm for
   rt_rtc_wkalarm/time_t convert.

Signed-off-by: GuEe-GUI <2991707448@qq.com>

* [dm][rtc] add new drivers

1. Dallas/Maxim DS1302
2. Dallas/Maxim DS1307/37/38/39/40, ST M41T11
3. Goldfish Real Time Clock
4. Haoyu Microelectronics HYM8563
5. NXP PCF8523
6. Philips PCF8563/Epson RTC8564
7. ARM PL031
8. Epson RX8010SJ

Signed-off-by: GuEe-GUI <2991707448@qq.com>

---------

Signed-off-by: GuEe-GUI <2991707448@qq.com>
2025-12-10 17:03:20 +08:00

257 lines
6.7 KiB
C

/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-09-23 GuEe-GUI first version
*/
#include "rtc_dm.h"
#define DBG_TAG "rtc.ds1302"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#define RTC_CMD_READ 0x81 /* Read command */
#define RTC_CMD_WRITE 0x80 /* Write command */
#define RTC_CMD_WRITE_ENABLE 0x00 /* Write enable */
#define RTC_CMD_WRITE_DISABLE 0x80 /* Write disable */
#define RTC_ADDR_RAM0 0x20 /* Address of RAM0 */
#define RTC_ADDR_TCR 0x08 /* Address of trickle charge register */
#define RTC_CLCK_BURST 0x1F /* Address of clock burst */
#define RTC_CLCK_LEN 0x08 /* Size of clock burst */
#define RTC_ADDR_CTRL 0x07 /* Address of control register */
#define RTC_ADDR_YEAR 0x06 /* Address of year register */
#define RTC_ADDR_DAY 0x05 /* Address of day of week register */
#define RTC_ADDR_MON 0x04 /* Address of month register */
#define RTC_ADDR_DATE 0x03 /* Address of day of month register */
#define RTC_ADDR_HOUR 0x02 /* Address of hour register */
#define RTC_ADDR_MIN 0x01 /* Address of minute register */
#define RTC_ADDR_SEC 0x00 /* Address of second register */
static rt_err_t ds1302_rtc_get_time(struct rt_spi_device *spi_dev, time_t *sec)
{
struct tm tm;
rt_err_t err;
rt_uint8_t addr = RTC_CLCK_BURST << 1 | RTC_CMD_READ, buf[RTC_CLCK_LEN - 1];
err = rt_spi_send_then_recv(spi_dev, &addr, sizeof(addr), buf, sizeof(buf));
if (err)
{
return err;
}
/* Decode the registers */
tm.tm_sec = rt_bcd2bin(buf[RTC_ADDR_SEC]);
tm.tm_min = rt_bcd2bin(buf[RTC_ADDR_MIN]);
tm.tm_hour = rt_bcd2bin(buf[RTC_ADDR_HOUR]);
tm.tm_wday = buf[RTC_ADDR_DAY] - 1;
tm.tm_mday = rt_bcd2bin(buf[RTC_ADDR_DATE]);
tm.tm_mon = rt_bcd2bin(buf[RTC_ADDR_MON]) - 1;
tm.tm_year = rt_bcd2bin(buf[RTC_ADDR_YEAR]) + 100;
*sec = timegm(&tm);
return RT_EOK;
}
static rt_err_t ds1302_rtc_set_time(struct rt_spi_device *spi_dev, time_t *sec)
{
rt_err_t err;
struct tm *tm;
rt_uint8_t buf[1 + RTC_CLCK_LEN], *bp;
tm = localtime(sec);
/* Enable writing */
bp = buf;
*bp++ = RTC_ADDR_CTRL << 1 | RTC_CMD_WRITE;
*bp++ = RTC_CMD_WRITE_ENABLE;
err = rt_spi_send_then_recv(spi_dev, buf, 2, RT_NULL, 0);
if (err)
{
return err;
}
/* Write registers starting at the first time/date address. */
bp = buf;
*bp++ = RTC_CLCK_BURST << 1 | RTC_CMD_WRITE;
*bp++ = rt_bin2bcd(tm->tm_sec);
*bp++ = rt_bin2bcd(tm->tm_min);
*bp++ = rt_bin2bcd(tm->tm_hour);
*bp++ = rt_bin2bcd(tm->tm_mday);
*bp++ = rt_bin2bcd(tm->tm_mon + 1);
*bp++ = tm->tm_wday + 1;
*bp++ = rt_bin2bcd(tm->tm_year % 100);
*bp++ = RTC_CMD_WRITE_DISABLE;
return rt_spi_send_then_recv(spi_dev, buf, sizeof(buf), RT_NULL, 0);
}
static rt_err_t ds1302_rtc_control(rt_device_t dev, int cmd, void *args)
{
rt_err_t err = RT_EOK;
struct rt_spi_device *spi_dev = dev->user_data;
if (!args)
{
return -RT_EINVAL;
}
switch (cmd)
{
case RT_DEVICE_CTRL_RTC_GET_TIME:
err = ds1302_rtc_get_time(spi_dev, args);
break;
case RT_DEVICE_CTRL_RTC_SET_TIME:
err = ds1302_rtc_set_time(spi_dev, args);
break;
case RT_DEVICE_CTRL_RTC_GET_TIMEVAL:
err = ds1302_rtc_get_time(spi_dev, (time_t *)&((struct timeval *)args)->tv_sec);
break;
case RT_DEVICE_CTRL_RTC_SET_TIMEVAL:
err = ds1302_rtc_set_time(spi_dev, (time_t *)&((struct timeval *)args)->tv_sec);
break;
case RT_DEVICE_CTRL_RTC_GET_ALARM:
case RT_DEVICE_CTRL_RTC_SET_ALARM:
err = -RT_ENOSYS;
break;
default:
err = -RT_EINVAL;
break;
}
return err;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops ds1302_rtc_ops =
{
.control = ds1302_rtc_control,
};
#endif
static rt_err_t ds1302_rtc_probe(struct rt_spi_device *spi_dev)
{
rt_err_t err = RT_EOK;
const char *dev_name;
rt_uint8_t addr, buf[4];
if (spi_dev->config.max_hz > 2000000)
{
LOG_E("Speed is too high");
return -RT_EINVAL;
}
else if (spi_dev->config.mode & RT_SPI_CPHA)
{
LOG_E("Bad mode");
return -RT_EINVAL;
}
addr = RTC_ADDR_CTRL << 1 | RTC_CMD_READ;
if ((err = rt_spi_send_then_recv(spi_dev, &addr, sizeof(addr), buf, 1)))
{
LOG_E("Control register read error = %s", rt_strerror(err));
return err;
}
if ((buf[0] & ~RTC_CMD_WRITE_DISABLE) != 0)
{
if ((err = rt_spi_send_then_recv(spi_dev, &addr, sizeof(addr), buf, 1)))
{
LOG_E("Control register read error = %s", rt_strerror(err));
return err;
}
if ((buf[0] & ~RTC_CMD_WRITE_DISABLE) != 0)
{
LOG_E("Junk in control register");
return -RT_EIO;
}
}
if (buf[0] == 0)
{
buf[0] = RTC_ADDR_CTRL << 1 | RTC_CMD_WRITE;
buf[1] = RTC_CMD_WRITE_DISABLE;
if ((err = rt_spi_send_then_recv(spi_dev, buf, 2, RT_NULL, 0)))
{
LOG_E("Control register write error = %s", rt_strerror(err));
return err;
}
addr = RTC_ADDR_CTRL << 1 | RTC_CMD_READ;
if ((err = rt_spi_send_then_recv(spi_dev, &addr, sizeof(addr), buf, 1)))
{
LOG_E("Reading control register error = %s", rt_strerror(err));
return err;
}
if (buf[0] != RTC_CMD_WRITE_DISABLE)
{
LOG_E("Failed to detect chip");
return -RT_EIO;
}
}
spi_dev->parent.user_data = spi_dev;
spi_dev->parent.type = RT_Device_Class_RTC;
#ifdef RT_USING_DEVICE_OPS
spi_dev->parent.ops = &ds1302_rtc_ops;
#else
spi_dev->parent.control = ds1302_rtc_control;
#endif
rtc_dev_set_name(&spi_dev->parent);
dev_name = rt_dm_dev_get_name(&spi_dev->parent);
err = rt_device_register(&spi_dev->parent, dev_name, RT_DEVICE_FLAG_RDWR);
return err;
}
static rt_err_t ds1302_rtc_remove(struct rt_spi_device *spi_dev)
{
rt_device_unregister(&spi_dev->parent);
return RT_EOK;
}
static const struct rt_spi_device_id ds1302_rtc_ids[] =
{
{ .name = "ds1302" },
{ /* sentinel */ },
};
static const struct rt_ofw_node_id ds1302_rtc_ofw_ids[] =
{
{ .compatible = "maxim,ds1302" },
{ /* sentinel */ },
};
static struct rt_spi_driver ds1302_rtc_driver =
{
.ids = ds1302_rtc_ids,
.ofw_ids = ds1302_rtc_ofw_ids,
.probe = ds1302_rtc_probe,
.remove = ds1302_rtc_remove,
};
RT_SPI_DRIVER_EXPORT(ds1302_rtc_driver);