软件开发平台:android P 源码。
硬件开发平台:nxp imx8m mini开发板,RTC 型号8025T。
本文记录在nxp 8m mini 硬件平台, android P 源码的软件平台上调试RTC8025T驱动,RTC的framework 层android 已经写好了即 AlarmManagerService , APP 应用层通过((AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE)) 提供的接口来读取和设置 RTC 。
一.RTC相关驱动调试
先来了解下 RTC 8025T 操作模式:
操作模式 :
1 ) 实时时钟模式
实时时 钟模式
该功能被用来设定和读取年,月,日,星期,时,分,秒 时间信息。年份为后两位数字表示,
任何可以被 4 整除的年份被当成闰年处理。(2000 年到 2099 年)
2 ) 固定周期的中断发生功能:
固定周期的中断发生功能
固定周期定时中断发生功能可以产生一个固定周期的中断事件,固定周期可在 244.14uS 到
4095 分钟之间的 任意时间设定。
3)定时更新中断功能:
该功能可以根据内部时钟的定时设定,每秒或每分钟产生一个中断事件。
当中断事件产生,UF 标志位的值变成 1 同时/INT 引脚变成低电平表示一个中断事件的产生。
4 ) 闹钟中断功能
闹钟 中断功能:
中断功能
该功能可以根据报警设定来产生一个中断。
硬件电路设计如下:
Nxp 平台有有完整的RTC驱动框架,所以只需要完善8025T相关的读写功能就可以了。
初始化8025T
static int rx8025_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct rx8025_data *rx8025;
int err = 0;
//可以看到RTC 驱动初始化的上下文
dump_stack();
rx8025 = devm_kzalloc(&client->dev, sizeof(*rx8025), GFP_KERNEL);
rx8025->client = client;
i2c_set_clientdata(client, rx8025);
err = rx8025_init_client(client);
// 将设备操作函数rx8025_rtc_ops 和设备关联起来
rx8025->rtc = devm_rtc_device_register(&client->dev, client->name,
&rx8025_rtc_ops, THIS_MODULE);
//irq?
if (client->irq > 0) {
//注册中断回调函数 rx8025_handle_irq
err = devm_request_threaded_irq(&client->dev, client->irq, NULL,
rx8025_handle_irq,
IRQF_ONESHOT,
"rx8025", client);
}
rx8025->rtc->max_user_freq = 1;
/* the rx8025 alarm only supports a minute accuracy */
rx8025->rtc->uie_unsupported = 1;
err = rx8025_sysfs_register(&client->dev);
printk("%s end\r\n", __func__);/*Herber*/
return err;
}
这里重点关注 rx8025_rtc_ops,实现这里的读写函数就可以了。
static const struct rtc_class_ops rx8025_rtc_ops = {
.read_time = rx8025_get_time,
.set_time = rx8025_set_time,
.read_alarm = rx8025_read_alarm,
.set_alarm = rx8025_set_alarm,
.alarm_irq_enable = rx8025_alarm_irq_enable,
};
Struct rtc_class_ops 定义在rtc.h
struct rtc_class_ops {
int (*ioctl)(struct device *, unsigned int, unsigned long);
int (*read_time)(struct device *, struct rtc_time *);
int (*set_time)(struct device *, struct rtc_time *);
int (*read_alarm)(struct device *, struct rtc_wkalrm *);
int (*set_alarm)(struct device *, struct rtc_wkalrm *);
int (*proc)(struct device *, struct seq_file *);
int (*set_mmss64)(struct device *, time64_t secs);
int (*set_mmss)(struct device *, unsigned long secs);
int (*read_callback)(struct device *, int data);
int (*alarm_irq_enable)(struct device *, unsigned int enabled);
int (*read_offset)(struct device *, long *offset);
int (*set_offset)(struct device *, long offset);
};
这里分析下写入rtc寄存器
将dt的 tm_sec等,转成bcd码 写入RX8025_REG_SEC寄存器。
static int rx8025_set_time(struct device *dev, struct rtc_time *dt)
{
struct rx8025_data *rx8025 = dev_get_drvdata(dev);
u8 date[7];
int ret;
dump_stack();
printk("%s( %d %d %d %d %d %d %d %d %d)\n",__func__,dt->tm_sec,dt->tm_min,dt->tm_hour ,dt->tm_mday, dt->tm_mon,dt->tm_year, dt->tm_wday,dt->tm_yday, dt->tm_isdst );
if ((dt->tm_year < 100) || (dt->tm_year > 199))
{
printk("%s set Year Err %d === \r\n",__func__,dt->tm_year);
return -EINVAL;
}
/*
* Here the read-only bits are written as "0". I'm not sure if that
* is sound.
*/
date[RX8025_REG_SEC] = bin2bcd(dt->tm_sec);
date[RX8025_REG_MIN] = bin2bcd(dt->tm_min);
if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224)
date[RX8025_REG_HOUR] = bin2bcd(dt->tm_hour);
else
date[RX8025_REG_HOUR] = (dt->tm_hour >= 12 ? 0x20 : 0)
| bin2bcd((dt->tm_hour + 11) % 12 + 1);
date[RX8025_REG_WDAY] = bin2bcd(dt->tm_wday);
date[RX8025_REG_MDAY] = bin2bcd(dt->tm_mday);
date[RX8025_REG_MONTH] = bin2bcd(dt->tm_mon + 1);
date[RX8025_REG_YEAR] = bin2bcd(dt->tm_year - 100);
ret = rx8025_write_regs(rx8025->client, RX8025_REG_SEC, 7, date);
if (ret < 0)
return ret;
return rx8025_reset_validity(rx8025->client);
}
下面是 读取rtc的寄存器
static int rx8025_get_time(struct device *dev, struct rtc_time *dt)
{
struct rx8025_data *rx8025 = dev_get_drvdata(dev);
u8 date[7];
int err;
dump_stack();
//从RX8025_REG_SEC 读7位到 date
err = rx8025_read_regs(rx8025->client, RX8025_REG_SEC, 7, date);
printk("%s para 0x%02x,0x%02x,0x%02x=== \r\n",__func__,date[0],date[1],date[2]);/*Herber*/
if (err){
printk("%s S3,Err +++++\r\n",__func__);
return err;
}
dt->tm_sec = bcd2bin(date[RX8025_REG_SEC] & 0x7f);
dt->tm_min = bcd2bin(date[RX8025_REG_MIN] & 0x7f);
if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224)
dt->tm_hour = bcd2bin(date[RX8025_REG_HOUR] & 0x3f);
else
dt->tm_hour = bcd2bin(date[RX8025_REG_HOUR] & 0x1f) % 12
+ (date[RX8025_REG_HOUR] & 0x20 ? 12 : 0);
dt->tm_mday = bcd2bin(date[RX8025_REG_MDAY] & 0x3f);
dt->tm_mon = bcd2bin(date[RX8025_REG_MONTH] & 0x1f) - 1;
dt->tm_year = bcd2bin(date[RX8025_REG_YEAR]) + 100;
dev_dbg(dev, "%s: date %ds %dm %dh %dmd %dm %dy\n", __func__,
dt->tm_sec, dt->tm_min, dt->tm_hour,
dt->tm_mday, dt->tm_mon, dt->tm_year);
return rtc_valid_tm(dt);
}
可以看到rtc 驱动只提供了简单的设定和读取年,月,日,星期,时,分,秒 时间信息的功能。
#define CFG_RTC_I2C_DEV "/dev/rtc0"
#include
int main(void)
{
int fd_i2c,retval;
struct rtc_time new, current;
printf("==== RTC TIME operates ====\n");
fd_i2c = open(CFG_RTC_I2C_DEV, O_RDWR);
if (fd_i2c < 0) {
printf("Open i2c device %s failed!\n", CFG_RTC_I2C_DEV);
return -1;
}
scanf("%d-%d-%d", &new.tm_mday, &new.tm_mon, &new.tm_year);
printf("\r\n");
scanf("%d:%d:%d", &new.tm_hour, &new.tm_min, &new.tm_sec);
printf("\r\n");
printf("Test will set RTC date/time to %d-%d-%d, %02d:%02d:%02d.\n",
new.tm_mday, new.tm_mon + 1, new.tm_year + 1900,
new.tm_hour, new.tm_min, new.tm_sec);
/* Write the new date in RTC */
printf("renew data in RTC Module \r\n");
retval = ioctl(fd_i2c, RTC_SET_TIME, &new);
if (retval == -1) {
printf("--RTC_SET_TIME ioctl Err--\r\n");
//close(fd);
goto err;//exit(errno);
}
/* Read back */
retval = ioctl(fd_i2c, RTC_RD_TIME, ¤t);
if (retval == -1) {
printf("---RTC_RD_TIME ioctl Err ---\r\n");
goto err;exit(errno);
}
printf("\n\nCurrent RTC date/time is %d-%d-%d, %02d:%02d:%02d.\n",
current.tm_mday, current.tm_mon + 1, current.tm_year + 1900,
current.tm_hour, current.tm_min, current.tm_sec);
close(fd_i2c);
return 0;
err:
if (fd_i2c)
close(fd_i2c);
return -1;
}
以上代码可以测试 rtc 的读写功能.
再梳理下rtc驱动的流程,在内核启动时 初始化rtc即rx8025_probe, 再将rtc 的时间给了系统。
static int __init rtc_hctosys(void)
{
int err = -ENODEV;
struct rtc_time tm;
struct timespec64 tv64 = {
.tv_nsec = NSEC_PER_SEC >> 1,
};
struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
err = rtc_read_time(rtc, &tm);
tv64.tv_sec = rtc_tm_to_time64(&tm);
err = do_settimeofday64(&tv64);
dev_info(rtc->dev.parent,
"setting system clock to "
"%d-%02d-%02d %02d:%02d:%02d UTC (%lld)\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
(long long) tv64.tv_sec);
return err;
}
late_initcall(rtc_hctosys);
这样系统时间就和rtc 的时间对齐了。