平台 | 内核版本 | 安卓版本 |
---|---|---|
RK3399 | Linux4.4 | Android7.1 |
AM1805介绍
AM1805
是集RTC
、Watchdog
、Alarm
、Countdown
四大功能于一身,功能齐全的实时时钟芯片,包含片上振荡器以提供最低的功耗,完整的RTC
功能,包括电池备份,可编程计数器和定时器和看门狗功能的报警,以及用于与主机控制器通信的I2C
或SPI
串行接口。 集成电源开关和具有计数器,定时器,闹钟和中断功能的复杂系统睡眠管理器使AM18X5
可用作基于主机微控制器系统的监控组件。
SDK
中默认没有该IC的驱动支持,所以里面的驱动是根据规格书编写的。
原理图
硬件上的特性:
硬件原理图上的中断输入WDI
没有接,中断输出接的有nRST
、PSW/nIRQ2
,其他中断引脚nTIRQ
、CLKOUT/nIRQ3
、FOUT/nIRQ
。目前软件Watchdog
中断输出走的是nRST
,其他Alarm
、Countdown
走的是nIRQ2
中断脚。功能总述为下图中红色的走线,CDT
和Alarm
到或门控制输出到nIRQ2
,WDT
到或门输出到nRST
:
驱动步骤
- 调通
I2C
,能通过I2C
读写寄存器(或使用I2C
探针测试) - 做调试工具,驱动里面写一个
sys
控制寄存器,实现读写单个寄存器和打印全部寄存器。然后通过串口命令行用cat
和echo
直接去读写寄存器,并在调RTC
,ALARM
,COUNTDOWN
,WATCHDOG
之前,先用读写寄存器命令验证功能。 - 根据规格书提供的寄存器资料,开始通过直接读写寄存器,去先检验
RTC
、Alarm
、Watchdog
、Countdown
功能,确认用寄存器能调试没问题后,在驱动里面把相关代码写好。 - 调完后,写一个用户空间的程序,通过
IOCTL
来测试驱动。 - 校正
RTC
外部晶振
调试
前期调试:
由于硬件上VCC
直接上电,所以不用去控制IO
上电。
AM1805
规格书I2C
地址是0x8c/0x8d
,这个地址包含读写位,bit0
是读写位,所以bit1~7
是实际的I2C
地址,0x8c/0x8d>>1
可以算出I2C
地址是0x69
。I2C
实际挂载为i2c ao
,不是原理图的i2c b
。
在dts
中加上RTC
的配置编译烧录了dtb
后,在驱动的probe
函数中加读取chip id
并打印,看到I2C
通了。
由于验证功能需要频繁读写寄存器,所以注册了一个class
直接操作寄存器。目前调试或者查问题都可以通过命令行直接操作class
读写寄存器,方便快速定位问题。
驱动里面通过class_create
来创建一个设备的逻辑类:
class_register(&rtc_am1805_class)
然后创建三个功能的属性:读单个寄存器,写单个寄存器,读所有寄存器。这三个属性目前基本能满足这个芯片的调试。
使用示例:
- 这里举例在命令行直接设置一个闹钟
Alarm
关闭AIE
使能,设置IM
为电平模式
echo "0x12 0xe0">/sys/class/rtc_am1805/write_reg
- 清除
ALM Status
echo "0x0f 0x00">/sys/class/rtc_am1805/write_reg
- 设置为
IRQ2
中断
echo "0x11 0x0c">/sys/class/rtc_am1805/write_reg
- 设置时间
echo "0x08 0x01">/sys/class/rtc_am1805/write_reg
echo "0x09 0x01">/sys/class/rtc_am1805/write_reg
echo "0x0a 0x50">/sys/class/rtc_am1805/write_reg
echo "0x0b 0x00">/sys/class/rtc_am1805/write_reg
echo "0x0c 0x02">/sys/class/rtc_am1805/write_reg
echo "0x0e 0x01">/sys/class/rtc_am1805/write_reg
- 一年一次
echo "0x18 0xc6">/sys/class/rtc_am1805/write_reg
- 启动
echo "0x12 0xe4">/sys/class/rtc_am1805/write_reg
RTC驱动
RTC时间调试
RTC
可给系统提供时间,当系统关机后,RTC
芯片由于有电池供电,时间就不会停止,这样下次设备开机后就知道时间。RTC
时间由于只需要读写时间相关的寄存器,直接在命令行用echo
设置一下相关寄存器,可直接设置时间,然后在驱动里面实现RTC
驱动提供的接口。寄存器:
驱动主要工作是填充rtc_class_ops
结构体,结构体描述了RTC
芯片能够提供的所有操作接口函数:
struct rtc_class_ops {
int (*open)(struct device *);
void (*release)(struct device *);
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_mmss)(struct device *, unsigned long secs);
int (*read_callback)(struct device *, int data);
int (*alarm_irq_enable)(struct device *, unsigned int enabled);
};
具体实现:
static const struct rtc_class_ops rtc_am1805_ops = {
.open = rtc_am1805_open,
.read_time = rtc_am1805_read_time,
.set_time = rtc_am1805_write_time,
.read_alarm = rtc_am1805_read_alarm,
.set_alarm = rtc_am1805_set_alarm,
.set_cdt_time = rtc_am1805_set_timer,
.read_cdt_time = rtc_am1805_read_timer,
.ioctl = rtc_am1805_ioctl,
};
注册设备:
rtc_device_register(name, dev, &test_rtc_ops, THIS_MODULE);
watchdog调试
看门狗是用来定期监控系统的运行情况,一旦系统死机,看门狗就发出重启电路的信号。用途上比如广告机,我们需要它一直播放广告,如果突然死机了怎么办,所以我们就通过看门狗来解决,死机后看门狗自动重启设备,然后继续播放广告。
由于原理图上没有WDI
中断输入,所以需要驱动模拟实现。模拟喂狗驱动上用一个计时器和一个工作队列来实现。计时器每隔一定时间发出一个需要喂狗的工作,工作队列对看门狗倒计时的寄存器重新填值。
目前我在驱动上设计喂狗的时间为2
秒,超时的时间4
秒。也就是4
秒内没有喂狗,看门狗就产生一个中断到nRST
引脚复位设备。
目前驱动里面计时器所做的内容就是添加一个工作到队列里:
工作的内容就是喂狗:
工作流程:第一次add_timer
的时候会启动一次计时器,计时器会添加一个工作到工作队列,工作队列填完值后,mod_timer
会改动计时器的启动时间,也就是2
秒,2
秒后会再次进入计时器函数,然后计时器又添加一个工作到工作队列,如此循环。也就是不停的喂狗。
假如kernel
跑飞了,这时驱动不会去喂狗了,这时看门狗的计时器会自动减到0
,进而出发电平中断到nRST
引脚,设备就会复位。