1. 获取DS3234 SPI RTC驱动源码
1.1 源码位置
DS3234是SPI接口的高精度实时时钟芯片,其驱动在Linux内核中的位置:
drivers/rtc/rtc-ds3234.c
1.2 获取方法
方法1:从内核源码树获取
# 克隆Linux内核源码
git clone https://github.com/torvalds/linux.git
cd linux
# 查看DS1307驱动
cat drivers/rtc/rtc-ds1307.c
方法2:在线查看
-
https://elixir.bootlin.com/linux/latest/source/drivers/rtc/rtc-ds3234.c
2. 驱动源码详细分析
2.1 模块头文件和宏定义
#include <linux/bcd.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/rtc.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
MODULE_DESCRIPTION("DS3234 SPI RTC driver");
MODULE_AUTHOR("Dennis Aberilla <denzzzhome@yahoo.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:ds3234");
2.2 寄存器定义
/* DS3234寄存器定义 */
#define DS3234_REG_SECONDS 0x00
#define DS3234_REG_MINUTES 0x01
#define DS3234_REG_HOURS 0x02
#define DS3234_REG_DAY 0x03
#define DS3234_REG_DATE 0x04
#define DS3234_REG_MONTH 0x05
#define DS3234_REG_YEAR 0x06
#define DS3234_REG_ALM1_SECONDS 0x07
#define DS3234_REG_ALM1_MINUTES 0x08
#define DS3234_REG_ALM1_HOURS 0x09
#define DS3234_REG_ALM1_DAYDATE 0x0A
#define DS3234_REG_ALM2_MINUTES 0x0B
#define DS3234_REG_ALM2_HOURS 0x0C
#define DS3234_REG_ALM2_DAYDATE 0x0D
#define DS3234_REG_CONTROL 0x0E
#define DS3234_REG_STATUS 0x0F
#define DS3234_REG_AGING_OFFSET 0x10
#define DS3234_REG_TEMP_MSB 0x11
#define DS3234_REG_TEMP_LSB 0x12
/* 控制寄存器位定义 */
#define DS3234_CTRL_EOSC 0x80 /* 振荡器使能/禁用 */
#define DS3234_CTRL_BBSQW 0x40 /* 电池备份方波使能 */
#define DS3234_CTRL_CONV 0x20 /* 转换温度 */
#define DS3234_CTRL_RS2 0x10 /* 频率选择位2 */
#define DS3234_CTRL_RS1 0x08 /* 频率选择位1 */
#define DS3234_CTRL_INTCN 0x04 /* 中断控制 */
#define DS3234_CTRL_A2IE 0x02 /* 闹钟2中断使能 */
#define DS3234_CTRL_A1IE 0x01 /* 闹钟1中断使能 */
/* 状态寄存器位定义 */
#define DS3234_STAT_OSF 0x80 /* 振荡器停止标志 */
#define DS3234_STAT_EN32KHZ 0x08 /* 32kHz输出使能 */
#define DS3234_STAT_BSY 0x04 /* 繁忙标志 */
#define DS3234_STAT_A2F 0x02 /* 闹钟2标志 */
#define DS3234_STAT_A1F 0x01 /* 闹钟1标志 */
/* 小时寄存器位定义 */
#define DS3234_HR_MIL 0x40 /* 24小时模式 */
#define DS3234_HR_12HR 0x20 /* 12小时模式 */
#define DS3234_HR_PM 0x20 /* PM标志(12小时模式) */
2.3 设备私有数据结构
struct ds3234 {
struct rtc_device *rtc; /* RTC设备指针 */
struct spi_device *spi; /* SPI设备指针 */
u8 *tx_buf; /* SPI发送缓冲区 */
u8 *rx_buf; /* SPI接收缓冲区 */
int secs; /* 秒缓存 */
int irq; /* 中断号 */
unsigned long flags; /* 状态标志 */
/* 温度相关 */
struct delayed_work work; /* 延迟工作队列 */
int temperature; /* 温度值 */
};
2.4 SPI读写操作函数
/* SPI读寄存器 */
static int ds3234_read_reg(struct ds3234 *ds3234, u8 addr, u8 *value)
{
int ret;
struct spi_message msg;
struct spi_transfer xfer[2];
u8 tx_buf[2];
u8 rx_buf[2];
/* 准备SPI传输 */
memset(xfer, 0, sizeof(xfer));
spi_message_init(&msg);
/* 写寄存器地址 */
tx_buf[0] = addr | 0x80; /* 设置读位 */
xfer[0].tx_buf = tx_buf;
xfer[0].len = 1;
xfer[0].cs_change = 0; /* 保持片选有效 */
spi_message_add_tail(&xfer[0], &msg);
/* 读取数据 */
xfer[1].rx_buf = rx_buf;
xfer[1].len = 1;
spi_message_add_tail(&xfer[1], &msg);
ret = spi_sync(ds3234->spi, &msg);
if (ret >= 0)
*value = rx_buf[1];
return ret;
}
/* SPI写寄存器 */
static int ds3234_write_reg(struct ds3234 *ds3234, u8 addr, u8 value)
{
int ret;
u8 tx_buf[2];
tx_buf[0] = addr & 0x7F; /* 清除读位 */
tx_buf[1] = value;
ret = spi_write(ds3234->spi, tx_buf, sizeof(tx_buf));
return ret;
}
/* 批量读取多个寄存器 */
static int ds3234_read_regs(struct ds3234 *ds3234, u8 addr,
u8 *buf, u16 len)
{
int ret;
struct spi_message msg;
struct spi_transfer xfer[2];
u8 tx_buf[1];
memset(xfer, 0, sizeof(xfer));
spi_message_init(&msg);
/* 发送读取命令 */
tx_buf[0] = addr | 0x80; /* 设置读位 */
xfer[0].tx_buf = tx_buf;
xfer[0].len = 1;
xfer[0].cs_change = 0; /* 保持片选有效 */
spi_message_add_tail(&xfer[0], &msg);
/* 读取数据 */
xfer[1].rx_buf = buf;
xfer[1].len = len;
spi_message_add_tail(&xfer[1], &msg);
ret = spi_sync(ds3234->spi, &msg);
return ret;
}
2.5 时间读取函数
static int ds3234_read_time(struct device *dev, struct rtc_time *dt)
{
struct ds3234 *ds3234 = dev_get_drvdata(dev);
u8 buf[7];
u8 val;
int err;
/* 读取时间寄存器(秒、分、时、星期、日、月、年) */
err = ds3234_read_regs(ds3234, DS3234_REG_SECONDS, buf, sizeof(buf));
if (err)
return err;
/* 解码秒 */
dt->tm_sec = bcd2bin(buf[0] & 0x7F);
/* 解码分 */
dt->tm_min = bcd2bin(buf[1] & 0x7F);
/* 解码时,处理12/24小时制 */
if (buf[2] & DS3234_HR_12HR) {
/* 12小时制 */
dt->tm_hour = bcd2bin(buf[2] & 0x1F);
if (buf[2] & DS3234_HR_PM)
dt->tm_hour += 12;
} else {
/* 24小时制 */
dt->tm_hour = bcd2bin(buf[2] & 0x3F);
}
/* 解码星期(1-7,1=星期日) */
dt->tm_wday = bcd2bin(buf[3] & 0x07) - 1;
/* 解码日 */
dt->tm_mday = bcd2bin(buf[4] & 0x3F);
/* 解码月(包括世纪位) */
dt->tm_mon = bcd2bin(buf[5] & 0x1F) - 1;
/* 解码年(假设21世纪) */
dt->tm_year = bcd2bin(buf[6]) + 100;
/* 检查振荡器停止标志 */
err = ds3234_read_reg(ds3234, DS3234_REG_STATUS, &val);
if (err)
return err;
if (val & DS3234_STAT_OSF) {
dev_warn(dev, "oscillator stopped, time is unreliable\n");
return -EINVAL;
}
return 0;
}
2.6 时间设置函数
static int ds3234_set_time(struct device *dev, struct rtc_time *dt)
{
struct ds3234 *ds3234 = dev_get_drvdata(dev);
u8 buf[8]; /* 寄存器地址 + 7个时间寄存器 */
u8 val;
int err;
/* 构建写缓冲区 */
buf[0] = DS3234_REG_SECONDS & 0x7F; /* 写命令 */
/* 编码秒 */
buf[1] = bin2bcd(dt->tm_sec);
/* 编码分 */
buf[2] = bin2bcd(dt->tm_min);
/* 编码时(24小时制) */
buf[3] = bin2bcd(dt->tm_hour) & 0x3F; /* 24小时模式 */
/* 编码星期 */
buf[4] = bin2bcd(dt->tm_wday + 1);
/* 编码日 */
buf[5] = bin2bcd(dt->tm_mday);
/* 编码月 */
buf[6] = bin2bcd(dt->tm_mon + 1);
/* 编码年 */
if (dt->tm_year >= 100)
buf[7] = bin2bcd(dt->tm_year - 100);
else
buf[7] = bin2bcd(dt->tm_year);
/* 写入时间寄存器 */
err = spi_write(ds3234->spi, buf, sizeof(buf));
if (err)
return err;
/* 清除振荡器停止标志 */
err = ds3234_read_reg(ds3234, DS3234_REG_STATUS, &val);
if (err)
return err;
val &= ~DS3234_STAT_OSF; /* 清除OSF位 */
err = ds3234_write_reg(ds3234, DS3234_REG_STATUS, val);
return err;
}
2.7 温度读取功能
/* 温度转换函数 */
static int ds3234_read_temp(struct ds3234 *ds3234, int *temp)
{
u8 buf[2];
s16 raw_temp;
int err;
/* 启动温度转换 */
err = ds3234_write_reg(ds3234, DS3234_REG_CONTROL,
DS3234_CTRL_CONV);
if (err)
return err;
/* 等待转换完成(最大10ms) */
msleep(10);
/* 读取温度寄存器 */
err = ds3234_read_regs(ds3234, DS3234_REG_TEMP_MSB, buf, 2);
if (err)
return err;
/* 转换温度值 */
raw_temp = (buf[0] << 8) | buf[1];
raw_temp >>= 6; /* 温度值在14位中,右移6位 */
*temp = (raw_temp * 250) / 1000; /* 转换为摄氏度 */
return 0;
}
/* 温度读取工作队列 */
static void ds3234_temp_work(struct work_struct *work)
{
struct ds3234 *ds3234 = container_of(work, struct ds3234,
work.work);
int temp;
int err;
err = ds3234_read_temp(ds3234, &temp);
if (!err) {
ds3234->temperature = temp;
dev_dbg(&ds3234->spi->dev, "temperature: %d°C\n", temp);
}
/* 10秒后再次读取 */
schedule_delayed_work(&ds3234->work, 10 * HZ);
}
2.8 闹钟功能
/* 设置闹钟 */
static int ds3234_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct ds3234 *ds3234 = dev_get_drvdata(dev);
u8 buf[4];
u8 ctrl, stat;
int err;
/* 读取控制寄存器 */
err = ds3234_read_reg(ds3234, DS3234_REG_CONTROL, &ctrl);
if (err)
return err;
/* 读取状态寄存器 */
err = ds3234_read_reg(ds3234, DS3234_REG_STATUS, &stat);
if (err)
return err;
/* 设置闹钟1时间(秒、分、时、日/星期) */
buf[0] = DS3234_REG_ALM1_SECONDS;
buf[1] = bin2bcd(alarm->time.tm_sec) | 0x80; /* 秒 + A1M1 */
buf[2] = bin2bcd(alarm->time.tm_min) | 0x80; /* 分 + A1M2 */
buf[3] = bin2bcd(alarm->time.tm_hour) | 0x80; /* 时 + A1M3 */
buf[4] = bin2bcd(alarm->time.tm_mday) | 0x80; /* 日 + A1M4 */
err = spi_write(ds3234->spi, buf, 5);
if (err)
return err;
/* 设置闹钟使能 */
if (alarm->enabled) {
ctrl |= DS3234_CTRL_A1IE; /* 使能闹钟1中断 */
ctrl |= DS3234_CTRL_INTCN; /* 设置中断模式 */
} else {
ctrl &= ~DS3234_CTRL_A1IE; /* 禁用闹钟1中断 */
}
err = ds3234_write_reg(ds3234, DS3234_REG_CONTROL, ctrl);
if (err)
return err;
/* 清除闹钟标志 */
stat &= ~DS3234_STAT_A1F;
err = ds3234_write_reg(ds3234, DS3234_REG_STATUS, stat);
return err;
}
/* 读取闹钟 */
static int ds3234_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct ds3234 *ds3234 = dev_get_drvdata(dev);
u8 buf[4];
u8 ctrl, stat;
int err;
/* 读取闹钟寄存器 */
err = ds3234_read_regs(ds3234, DS3234_REG_ALM1_SECONDS, buf, 4);
if (err)
return err;
/* 解码闹钟时间 */
alarm->time.tm_sec = bcd2bin(buf[0] & 0x7F);
alarm->time.tm_min = bcd2bin(buf[1] & 0x7F);
alarm->time.tm_hour = bcd2bin(buf[2] & 0x3F);
alarm->time.tm_mday = bcd2bin(buf[3] & 0x3F);
/* 检查闹钟使能状态 */
err = ds3234_read_reg(ds3234, DS3234_REG_CONTROL, &ctrl);
if (err)
return err;
alarm->enabled = !!(ctrl & DS3234_CTRL_A1IE);
/* 检查闹钟触发标志 */
err = ds3234_read_reg(ds3234, DS3234_REG_STATUS, &stat);
if (err)
return err;
alarm->pending = !!(stat & DS3234_STAT_A1F);
return 0;
}
2.9 RTC操作集
static const struct rtc_class_ops ds3234_rtc_ops = {
.read_time = ds3234_read_time,
.set_time = ds3234_set_time,
.read_alarm = ds3234_read_alarm,
.set_alarm = ds3234_set_alarm,
.alarm_irq_enable = ds3234_alarm_irq_enable,
.proc = ds3234_proc, /* 可选的proc文件系统接口 */
};
2.10 探测函数(probe)
static int ds3234_probe(struct spi_device *spi)
{
struct ds3234 *ds3234;
struct rtc_device *rtc;
u8 buf[2];
int err;
/* 分配设备私有数据结构 */
ds3234 = devm_kzalloc(&spi->dev, sizeof(*ds3234), GFP_KERNEL);
if (!ds3234)
return -ENOMEM;
/* 初始化数据结构 */
ds3234->spi = spi;
spi_set_drvdata(spi, ds3234);
/* 初始化缓冲区 */
ds3234->tx_buf = devm_kzalloc(&spi->dev, 8, GFP_KERNEL);
ds3234->rx_buf = devm_kzalloc(&spi->dev, 8, GFP_KERNEL);
if (!ds3234->tx_buf || !ds3234->rx_buf)
return -ENOMEM;
/* 验证设备ID */
err = ds3234_read_reg(ds3234, DS3234_REG_SECONDS, buf);
if (err) {
dev_err(&spi->dev, "unable to read RTC register\n");
return err;
}
/* 检查振荡器是否启用 */
err = ds3234_read_reg(ds3234, DS3234_REG_CONTROL, buf);
if (err)
return err;
if (buf[0] & DS3234_CTRL_EOSC) {
/* 振荡器被禁用,启用它 */
buf[0] &= ~DS3234_CTRL_EOSC;
err = ds3234_write_reg(ds3234, DS3234_REG_CONTROL, buf[0]);
if (err)
return err;
}
/* 清除振荡器停止标志 */
err = ds3234_read_reg(ds3234, DS3234_REG_STATUS, buf);
if (err)
return err;
buf[0] &= ~DS3234_STAT_OSF;
err = ds3234_write_reg(ds3234, DS3234_REG_STATUS, buf[0]);
if (err)
return err;
/* 注册RTC设备 */
rtc = devm_rtc_device_register(&spi->dev, "ds3234",
&ds3234_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
ds3234->rtc = rtc;
/* 设置SPI模式 */
spi->mode = SPI_MODE_3; /* CPOL=1, CPHA=1 */
spi->bits_per_word = 8;
err = spi_setup(spi);
if (err)
return err;
/* 初始化温度监控工作队列 */
INIT_DELAYED_WORK(&ds3234->work, ds3234_temp_work);
schedule_delayed_work(&ds3234->work, 0);
/* 如果支持中断,初始化中断 */
if (spi->irq > 0) {
err = devm_request_threaded_irq(&spi->dev, spi->irq, NULL,
ds3234_irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"ds3234", ds3234);
if (err) {
dev_warn(&spi->dev, "unable to request IRQ\n");
}
}
dev_info(&spi->dev, "DS3234 SPI RTC registered\n");
return 0;
}
2.11 移除函数(remove)
static int ds3234_remove(struct spi_device *spi)
{
struct ds3234 *ds3234 = spi_get_drvdata(spi);
/* 取消温度监控工作队列 */
cancel_delayed_work_sync(&ds3234->work);
/* 禁用闹钟中断 */
ds3234_write_reg(ds3234, DS3234_REG_CONTROL, 0);
return 0;
}
2.12 设备ID表
static const struct spi_device_id ds3234_id[] = {
{ "ds3234", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, ds3234_id);
2.13 设备树兼容性
#ifdef CONFIG_OF
static const struct of_device_id ds3234_dt_ids[] = {
{ .compatible = "maxim,ds3234" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ds3234_dt_ids);
#endif
2.14 SPI驱动结构
static struct spi_driver ds3234_driver = {
.driver = {
.name = "rtc-ds3234",
.of_match_table = of_match_ptr(ds3234_dt_ids),
},
.probe = ds3234_probe,
.remove = ds3234_remove,
.id_table = ds3234_id,
};
module_spi_driver(ds3234_driver);
3. 设备树绑定示例
/* DS3234设备树节点示例 */
&spi0 {
status = "okay";
rtc@0 {
compatible = "maxim,ds3234";
reg = <0>; /* CS0 */
spi-max-frequency = <5000000>; /* 最大SPI频率5MHz */
spi-cpol; /* 时钟极性1 */
spi-cpha; /* 时钟相位1 */
/* 中断引脚(如果连接) */
interrupts = <&gpio 17 IRQ_TYPE_EDGE_FALLING>;
interrupt-parent = <&gpio>;
};
};
4. 驱动使用示例
4.1 加载驱动模块
# 加载SPI核心模块
modprobe spi_bcm2835 # Raspberry Pi SPI控制器
# 加载DS3234驱动
insmod rtc-ds3234.ko
# 查看注册的RTC设备
ls /sys/class/rtc/
cat /proc/driver/rtc
4.2 用户空间访问
# 设置系统时间到RTC
hwclock -w
# 从RTC读取时间到系统
hwclock -s
# 查看RTC时间
hwclock -r
# 通过sysfs查看信息
cat /sys/class/rtc/rtc0/date
cat /sys/class/rtc/rtc0/time
cat /sys/class/rtc/rtc0/since_epoch
5. 驱动关键设计分析
5.1 SPI通信设计
-
使用Linux SPI子系统API
-
支持SPI模式3(CPOL=1, CPHA=1)
-
实现寄存器读写函数
-
支持批量读写操作
5.2 时间处理
-
BCD格式和二进制格式转换
-
12/24小时制处理
-
世纪位处理
-
振荡器状态监控
5.3 中断处理
-
闹钟中断支持
-
工作队列处理温度读取
-
异步事件处理
5.4 电源管理
-
振荡器使能/禁用控制
-
电池备份支持
-
低功耗模式
6. 与其他SPI RTC驱动的比较
|
特性 |
DS3234 |
DS1305 |
MCP7951X |
|---|---|---|---|
|
温度传感器 |
有 |
无 |
无 |
|
精度 |
±2ppm |
±2ppm |
±2ppm |
|
闹钟 |
2个 |
2个 |
2个 |
|
电池备份 |
支持 |
支持 |
支持 |
|
方波输出 |
支持 |
支持 |
支持 |
|
SPI模式 |
模式3 |
模式1,3 |
模式0,3 |
7. 总结
DS3234驱动的主要特点:
-
完整的SPI驱动实现:使用标准的SPI框架
-
高精度时间管理:支持温度补偿
-
完整的功能集:时间、闹钟、温度监控
-
中断支持:支持闹钟中断
-
设备树支持:完整的设备树绑定
-
电源管理:支持振荡器控制和低功耗模式
这个驱动展示了典型的SPI设备驱动开发模式:
-
实现probe/remove函数
-
定义设备私有数据结构
-
实现设备特定操作(RTC操作集)
-
处理中断和工作队列
-
支持sysfs/proc接口
-
完整的错误处理
2086

被折叠的 条评论
为什么被折叠?



