您的位置:首页 > 路由器知识路由器知识

OK210试用体验第十一篇:实时时钟(RTC)驱动开发实践与问题解析

2025-06-18人已围观

【OK210试用体验】第十一篇:实时时钟(RTC)驱动开发实践与问题解析

杨永胜

当前离线

经验2386

窥视卡

雷达卡

杨永胜

(楼主)

201594 23:00:59??只看该作者

3397|2|倒序浏览

本帖最后由iysheng于201595 00:00编辑

前几天因未携带家用路由器,一直使用NFS挂载根文件系统的我,今日终于收到新淘的路由器(图1)。迫不及待想测试前几天编写的程序,却碰上数据乱码问题。经反复排查,最终定位到RTC驱动源码的rtc_read函数——内核与用户空间数据传递处理不当是主因。通过针对性调整,问题得以解决,现将完整开发过程与解决思路整理如下。

![图1 新购路由器实物](IMG20150904203014.jpg)(312.46KB,下载6次)

驱动开发关键问题与解决

首版驱动的乱码问题

首次运行程序时,读取的时钟数据出现乱码。经分析,问题出在rtc_read函数设计:内核空间与用户空间数据传递需通过特定接口完成,而我早期代码未正确处理这一过程。

首版rtc_read函数(存在缺陷):

```c

ssize_t rtc_read (struct file *filp, char __user *buff, size_t size, loff_t *ppos)

{

int i;

unsigned int ad_base=BCDSEC;

for(i=0; i<7; i++){//依次读取秒、分、时、日、星期、月、年(共7组数据)

unsigned int *adv=ioremap(ad_base, 4);//物理地址映射为虚拟地址

copy_to_user(buff, adv, 4);//尝试拷贝32位数据到用户空间

ad_base +=4;

buff +=4;

}

return 0;

}

```

此版本虽意识到需使用copy_to_user完成跨空间数据传递,但对32位时间数据的处理逻辑仍不完善。经多次调整,最终优化为:

优化后rtc_read函数(初步解决乱码):

```c

ssize_t rtc_read (struct file *filp, char __user *buff, size_t size, loff_t *ppos)

{

int i;

unsigned int ad_base=BCDSEC;

for(i=0; i<7; i++){

unsigned int *adv=ioremap(ad_base, 4);

copy_to_user(buff, adv, 4);

ad_base +=4;

buff +=4;

}

return 0;

}

```

此次调整重点解决了32位数据的完整拷贝问题,确保时间信息从内核到用户空间的准确传输。

完整驱动与用户程序实现

驱动核心代码

结合硬件特性,驱动代码需完成设备初始化、时间配置及数据读取功能。以下为核心代码片段(关键参数与寄存器地址详见表1):

```c

include

include

include

include"rtc.h"

unsigned int *rtccon;

unsigned int *rtcalm;

unsigned int *bcdyear,*bcdmon,*bcdday,*bcdweek,*bcdhour,*bcdmin,*bcdsec;

typedef struct INIT_TIME {

unsigned long year;

unsigned long mon;

unsigned long day;

unsigned long week;

unsigned long hour;

unsigned long min;

unsigned long sec;

}ITIME;

struct cdev rtcdev;

dev_t devnum;

int rtc_open (struct inode *inode, struct file *filp)

{

ITIME itime={0x15,0x8,0x30,0x7,0x21,0x57,0x27};//初始化时间参数

rtccon=ioremap(RTCCON,4);

readl(rtccon)|(1<<0); // 使能RTCCON

bcdyear=ioremap(BCDYEAR,4);writel(itime.year,bcdyear);

bcdmon=ioremap(BCDMON,4);writel(itime.mon,bcdmon);

bcdday=ioremap(BCDDAY,4);writel(itime.day,bcdday);

bcdweek=ioremap(BCDWEEK,4);writel(itime.week,bcdweek);

bcdhour=ioremap(BCDHOUR,4);writel(itime.hour,bcdhour);

bcdmin=ioremap(BCDMIN,4);writel(itime.min,bcdmin);

bcdsec=ioremap(BCDSEC,4);writel(itime.sec,bcdsec);

printk(KERN_INFO"RTC驱动初始化完成

");

return 0;

}

ssize_t rtc_read (struct file *filp, char __user *buff, size_t size, loff_t *ppos)

{

int i;

for(i=0; i<7; i++){

switch(i){

case 0: copy_to_user(buff, bcdsec,4); break;

case 1: copy_to_user(buff, bcdmin,4); break;

case 2: copy_to_user(buff, bcdhour,4); break;

case 3: copy_to_user(buff, bcdday,4); break;

case 4: copy_to_user(buff, bcdweek,4); break;

case 5: copy_to_user(buff, bcdmon,4); break;

case 6: copy_to_user(buff, bcdyear,4); break;

}

buff +=4;

}

return 0;

}

static struct file_operations rtcfops={

.open=rtc_open,

.owner=THIS_MODULE,

.read=rtc_read,

};

static int rtc_init(void)

{

cdev_init(&rtcdev,&rtcfops);

alloc_chrdev_region(&devnum,0,1,"ysrtc");

cdev_add(&rtcdev,devnum,1);

return 0;

}

static void rtc_exit(void)

{

cdev_del(&rtcdev);

unregister_chrdev_region(devnum,1);

}

MODULE_LICENSE("GPL");

MODULE_AUTHOR("SIMON YANG");

module_init(rtc_init);

module_exit(rtc_exit);

```

用户程序实现

用户程序需通过设备文件与驱动交互,实现时间数据读取功能。以下为测试代码:

```c

include

include

include

include

include"rtc.h"

void mdelay(unsigned long a)

{

unsigned long b,c;

for(b=0; b

for(c=0; c<0xffff; c++);

}

int main(void)

{

int fd=open("/dev/ysrtc",O_RDWR);

if(fd==1){

printf("设备打开失败,错误码:%d

",errno);

return errno;

}

unsigned int buff[7];

while(1){

read(fd,buff,sizeof(unsigned int)*7);

printf("年:0x%x 月:0x%x 星期:0x%x 日:0x%x 时:0x%x 分:0x%x 秒:0x%x

",

buff[6],buff[5],buff[4],buff[3],buff[2],buff[1],buff[0]);

mdelay(1000);

}

return 0;

}

```

测试过程与优化

初始测试问题

首次运行用户程序时,发现输出数据存在重复现象(图2)。经分析,问题源于延时函数精度不足——自定义mdelay(1000)实际延时小于1秒,导致短时间内多次读取未更新的数据。

![图2 初始测试重复数据截图](rtc.PNG)(128.19KB,下载5次)

优化方案: 增加秒级数据比对逻辑,仅当秒数变化时输出新数据。修改后用户程序如下:

```c

int main(void)

{

int fd=open("/dev/ysrtc",O_RDWR);

if(fd==1){

printf("设备打开失败,错误码:%d

",errno);

return errno;

}

int i,temp;

unsigned int buff[7];

fd=open("/dev/ysrtc", O_RDWR);

if (fd==1){

printf("设备打开失败,错误码:%d

",errno);

return errno;}

while(1){

temp=buff[0];

for(i=0; i<7; i++)

read(fd, buff, sizeof(unsigned int)*7);

if(temp !=buff[0]){

printf("年:0x%x 月:0x%x 星期:0x%x 日:0x%x 时:0x%x 分:0x%x 秒:0x%x

",

buff[6],buff[5],buff[4],buff[3],buff[2],buff[1],buff[0]);}

mdelay(1000);

}

return 0;

}

```

稳定性优化

优化后程序运行稳定,时间数据正常更新(图3)。但持续运行约1小时后,系统提示“空间分配不足”错误。经排查,问题仍与驱动代码相关——早期版本频繁调用ioremap函数动态映射地址,虽能完成功能,但会导致内存资源持续占用,长期运行易引发资源耗尽。

![图3 优化后稳定输出截图](Fedora20150904213604.PNG)(124.79KB,下载8次)

最终优化方案: 复用rtc_open函数中已映射的虚拟地址,避免重复调用ioremap。调整后驱动代码如下:

```c

ssize_t rtc_read (struct file *filp, char __user *buff, size_t size, loff_t *ppos)

{

int i;

for(i=0; i<7; i++){

switch(i){

case 0: copy_to_user(buff, bcdsec,4); break;

case 1: copy_to_user(buff, bcdmin,4); break;

case 2: copy_to_user(buff, bcdhour,4); break;

case 3: copy_to_user(buff, bcdday,4); break;

case 4: copy_to_user(buff, bcdweek,4); break;

case 5: copy_to_user(buff, bcdmon,4); break;

case 6: copy_to_user(buff, bcdyear,4); break;

}

buff +=4;

}

return 0;

}

```

此版本直接使用全局变量保存的虚拟地址(如bcdsec、bcdmin等),避免了动态内存分配,显著降低了资源消耗。调整后程序运行稳定,时间数据持续正确输出(图4)。

![图4 最终稳定运行截图](ok.PNG)(131.91KB,下载9次)

寄存器地址定义

开发中涉及的关键寄存器物理地址如下(基于OK210硬件平台):

```c

define RTC_BASE 0Xe2800000

define INTP RTC_BASE+0X30

define RTCCON RTC_BASE+0X40

define TICCNT RTC_BASE+0X44

define RTCALM RTC_BASE+0X50

define ALMSEC RTC_BASE+0X54

define ALMMIN RTC_BASE+0X58

define ALMHOUR RTC_BASE+0X5C

define ALMDATE RTC_BASE+0X60

define ALMMON RTC_BASE+0X64

define ALMYEAR RTC_BASE+0X68

define RTCRST RTC_BASE+0X6C

define BCDSEC RTC_BASE+0X70

define BCDMIN RTC_BASE+0X74

define BCDHOUR RTC_BASE+0X78

define BCDDAY RTC_BASE+0X7C

define BCDWEEK RTC_BASE+0X80

define BCDMON RTC_BASE+0X84

define BCDYEAR RTC_BASE+0X88

define CURTICCNT RTC_BASE+0X90

define RTCLVD RTC_BASE+0X94

```

开发总结

本次RTC驱动开发历时数日,核心挑战集中在内核与用户空间数据传递、内存资源管理两方面。通过系统性分析问题根源,针对性调整驱动逻辑(如优化数据拷贝方式、复用虚拟地址),最终实现了稳定可靠的实时时钟功能。开发过程中,对内核空间与用户空间交互机制、内存映射资源的合理利用有了更深刻的理解,为后续驱动开发积累了宝贵经验。

![图5 开发思维导图](rtc1.png)(23.21KB,下载7次)