一、前言
在Linux系统上主要有两个时间基准,一个数是系统时间和,一个是RTC 时间。 其中系统时间是系统运行时由定时器(滴答定时器)维护的时间,掉电不保存数据。而RTC时间,是由RTC实时时钟芯片维护的时间,一般都接了后备电源(常见表现行为就是一颗纽扣电池供电),系统掉电后它不受影响,还是会运行保证时间准确。 每次系统开机时,系统会从RTC芯片里读取当前时间给系统时间赋值,保证系统开机之后时间也是准确的。
那么系统开机如何读取RTC时间的? 这个就需要用到1个命令:hwclock 。 这个命令是专门读写RTC驱动的。当然,通过打开设备文件,利用ioctl的命令也可以与RTC设备进行交互,使用hwclock命令更简单些。
而在linux应用层,可以通过date 和 time 命令来设置系统时间的,而刚才说到的 hwclock 命令是用来设置和读写 RTC 时间的。
下面是系统 RTC 实时时钟时间的获取与设置命令用法案例:
二、设计思路
为了驱动的编写规范,Linux下规定了很多标准的驱动框架结构,而RTC驱动也是有一套标准结构,只有按照标准写的RTC驱动,才可以对接上应用层的命令。这个结构称为:RTC子系统。 RTC是实时时钟,一般可以采用芯片内置的RTC实时时钟,也可以采用外置的RTC实时时钟芯片,比如:DS1320。 这篇文章接下来编写RTC驱动案例,硬件就采用外置的DS1302来作为RTC实时时钟芯片。
在第一章里介绍了hwclock命令的用法,我们发现这个命令主要功能就是从驱动里读写时间,那么对于驱动而言就是要响应这两个操作。根据之前编写这么多驱动的经验,大家应该也就想到了,应用层命令访问到驱动层肯定会有两个接口:这两个接口就是设置时间和获取时间。 驱动层只要把这两个接口实现了,应用层的命令就可以正确的读写RTC时间了。和之间讲到的块设备驱动(实现读写扇区接口),网络设备驱动(实现网络数据收发接口)的意思差不多。
标准的RTC驱动安装注册之后,在/dev目录下会生成:rtcX这样的名字。后面的X是数字,系统里可以存在多个RTC驱动。默认情况下hwclock命令是读写的/dev/rtc0作为选择的主RTC驱动。这个可以在内核源码的配置里去修改的。
下面是调用框图:



去掉内核自带的RTC驱动:
|
Device Drivers —>[*] Real Time Clock —><M> Samsung S3C series SoC RTC |
将内核自带的RTC驱动编译成模块,方便后面动态加载测试。
配置之后,重新内核,烧写内核。

三、DS1302
当前RTC驱动的时钟芯片采用的是DS1302,下面看看DSS1302的介绍(来至数据手册):


下面是实物图:



四、实现代码
4.1 内核提供的 rtc 底层注册与注销函数
4.2 驱动代码(平台设备层)
4.3 驱动代码(平台应用层)
4.4 驱动代码(平台驱动层)
#include <linux/module.h> /*驱动模块相关*/
#include <linux/init.h>
#include <linux/fs.h> /*文件操作集合*/
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h> /*中断相关头文件*/
#include <linux/irq.h> /*中断相关头文件*/
#include <linux/gpio.h> /*硬件相关->定义了寄存器名字与地址*/
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/timer.h> /*内核定时器*/
#include <asm-generic/poll.h>
#include <linux/poll.h> /* poll机制*/
#include <linux/platform_device.h> /* 平台设备驱动相关头文件*/
#include <linux/rtc.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/delay.h>
/*--------------------------------DS1302相关操作代码---------------------------------------------*/
static unsigned char RTC_bin2bcd(unsigned val)
{
return ((val/10)<<4)+val%10;
}
static unsigned RTC_bcd2bin(unsigned char val)
{
return (val&0x0f)+(val>>4)*10;
}
/*
函数功能:DS1302初始化
Tiny4412硬件连接:
CLK :GPB_4
DAT :GPB_5
RST :GPB_6
*/
void DS1302IO_Init(void)
{
/*1. 注册GPIO*/
gpio_request(EXYNOS4_GPB(4), "DS1302_CLK");
gpio_request(EXYNOS4_GPB(5), "DS1302_DAT");
gpio_request(EXYNOS4_GPB(6), "DS1302_RST");
/*2. 配置GPIO口模式*/
s3c_gpio_cfgpin(EXYNOS4_GPB(4), S3C_GPIO_OUTPUT); //时钟
s3c_gpio_cfgpin(EXYNOS4_GPB(5), S3C_GPIO_OUTPUT); //数据
// s3c_gpio_cfgpin(EXYNOS4_GPB(2), S3C_GPIO_INPUT); //输入模式
s3c_gpio_cfgpin(EXYNOS4_GPB(6), S3C_GPIO_OUTPUT); //复位
/*3. 上拉GPIO口*/
gpio_set_value(EXYNOS4_GPB(4), 1); //CLK
gpio_set_value(EXYNOS4_GPB(5), 1); //DAT
gpio_set_value(EXYNOS4_GPB(6), 1); //RST
gpio_set_value(EXYNOS4_GPB(6), 0); //RST脚置低
gpio_set_value(EXYNOS4_GPB(4), 0); //SCK脚置低
}
//#define RTC_CMD_READ 0x81 /* Read command */
//#define RTC_CMD_WRITE 0x80 /* Write command */
//#define RTC_ADDR_RAM0 0x20 /* Address of RAM0 */
//#define RTC_ADDR_TCR 0x08 /* Address of trickle charge 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 */
//DS1302地址定义
#define ds1302_sec_add 0x80 //秒数据地址
#define ds1302_min_add 0x82 //分数据地址
#define ds1302_hr_add 0x84 //时数据地址
#define ds1302_date_add 0x86 //日数据地址
#define ds1302_month_add 0x88 //月数据地址
#define ds1302_day_add 0x8a //星期数据地址
#define ds1302_year_add 0x8c //年数据地址
#define ds1302_control_add 0x8e //控制数据地址
#define ds1302_charger_add 0x90
#define ds1302_clkburst_add 0xbe
//初始时间定义
static unsigned char time_buf[8] = {0x20,0x10,0x06,0x01,0x23,0x59,0x55,0x02};//初始时间2010年6月1号23点59分55秒 星期二
static unsigned char readtime[14];//当前时间
static unsigned char sec_buf=0; //秒缓存
static unsigned char sec_flag=0; //秒标志位
//向DS1302写入一字节数据
static void ds1302_write_byte(unsigned char addr, unsigned char d)
{
unsigned char i;
gpio_set_value(EXYNOS4_GPB(6), 1); //启动DS1302总线
//写入目标地址:addr
addr = addr & 0xFE; //最低位置零,寄存器0位为0时写,为1时读
for(i=0;i<8;i++)
{
if(addr&0x01){gpio_set_value(EXYNOS4_GPB(5), 1);}
else{gpio_set_value(EXYNOS4_GPB(5), 0);}
gpio_set_value(EXYNOS4_GPB(4), 1); //产生时钟
gpio_set_value(EXYNOS4_GPB(4), 0);
addr=addr >> 1;
}
//写入数据:d
for(i=0;i<8;i++)
{
if(d & 0x01) {gpio_set_value(EXYNOS4_GPB(5), 1);}
else {gpio_set_value(EXYNOS4_GPB(5), 0);}
gpio_set_value(EXYNOS4_GPB(4), 1); //产生时钟
gpio_set_value(EXYNOS4_GPB(4), 0);
d = d >> 1;
}
gpio_set_value(EXYNOS4_GPB(6), 0); //停止DS1302总线
}
//从DS1302读出一字节数据
static unsigned char ds1302_read_byte(unsigned char addr)
{
unsigned char i,temp;
gpio_set_value(EXYNOS4_GPB(6), 1);//启动DS1302总线
//写入目标地址:addr
addr=addr | 0x01; //最低位置高,寄存器0位为0时写,为1时读
for(i=0; i<8; i++)
{
if(addr & 0x01){gpio_set_value(EXYNOS4_GPB(5), 1);}
else {gpio_set_value(EXYNOS4_GPB(5), 0);}
gpio_set_value(EXYNOS4_GPB(4), 1);
gpio_set_value(EXYNOS4_GPB(4), 0);
addr=addr >> 1;
}
s3c_gpio_cfgpin(EXYNOS4_GPB(5), S3C_GPIO_INPUT); //输入模式
//输出数据:temp
for(i=0; i<8; i++)
{
temp=temp>>1;
if(gpio_get_value(EXYNOS4_GPB(5))){temp |= 0x80;}
else{temp&=0x7F;}
gpio_set_value(EXYNOS4_GPB(4), 1);
gpio_set_value(EXYNOS4_GPB(4), 0);
}
s3c_gpio_cfgpin(EXYNOS4_GPB(5), S3C_GPIO_OUTPUT); //输出模式
gpio_set_value(EXYNOS4_GPB(6), 0); //停止DS1302总线
return temp;
}
//向DS302写入时钟数据
static void ds1302_write_time(struct rtc_time *time)
{
ds1302_write_byte(ds1302_control_add,0x00); //关闭写保护
ds1302_write_byte(ds1302_sec_add,0x80); //暂停时钟
//ds1302_write_byte(ds1302_charger_add,0xa9); //涓流充电
/*设置RTC时间*/
//因为DS1302的年份只能设置后两位,所有需要使用正常的年份减去2000,得到实际的后两位
ds1302_write_byte(ds1302_year_add,RTC_bin2bcd(time->tm_year-2000)); //年
ds1302_write_byte(ds1302_month_add,RTC_bin2bcd(time->tm_mon)); //月
ds1302_write_byte(ds1302_date_add,RTC_bin2bcd(time->tm_mday)); //日
ds1302_write_byte(ds1302_hr_add,RTC_bin2bcd(time->tm_hour)); //时
ds1302_write_byte(ds1302_min_add,RTC_bin2bcd(time->tm_min)); //分
ds1302_write_byte(ds1302_sec_add,RTC_bin2bcd(time->tm_sec)); //秒
//ds1302_write_byte(ds1302_day_add,RTC_bin2bcd(time->tm_wday)); //周 time->tm_wday一周中的某一天
ds1302_write_byte(ds1302_control_add,0x80); //打开写保护
}
static int DS1302_rtc_ioctl(struct device *dev, unsigned int cmd,unsigned long arg)
{
/*设置RTC时间*/
struct rtc_time time;
copy_from_user(&time,(const void __user *)arg,sizeof(struct rtc_time));
ds1302_write_time(&time);
return 0;
}
//此函数通过应用层的ioctl的RTC_RD_TIME命令进行调用
static int tiny4412_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
{
rtc_tm->tm_year=RTC_bcd2bin(ds1302_read_byte(ds1302_year_add))+2000; //年
rtc_tm->tm_mon=RTC_bcd2bin(ds1302_read_byte(ds1302_month_add)); //月
rtc_tm->tm_mday=RTC_bcd2bin(ds1302_read_byte(ds1302_date_add)); //日
rtc_tm->tm_hour=RTC_bcd2bin(ds1302_read_byte(ds1302_hr_add)); //时
rtc_tm->tm_min=RTC_bcd2bin(ds1302_read_byte(ds1302_min_add)); //分
rtc_tm->tm_sec=RTC_bcd2bin((ds1302_read_byte(ds1302_sec_add))&0x7f);//秒,屏蔽秒的第7位,避免超出59
//time_buf[7]=ds1302_read_byte(ds1302_day_add); //周
return 0;
}
//此函数通过应用层的ioctl的RTC_SET_TIME命令进行调用
static int tiny4412_rtc_settime(struct device *dev, struct rtc_time *tm)
{
ds1302_write_time(tm);
return 0;
}
/*RTC文件操作*/
static const struct rtc_class_ops DS1302_rtcops = {
.ioctl=DS1302_rtc_ioctl,
.read_time = tiny4412_rtc_gettime,
.set_time = tiny4412_rtc_settime
};
static struct rtc_device *rtc=NULL;
/*当设备匹配成功执行的函数-资源探查函数*/
static int drv_probe(struct platform_device *pdev)
{
rtc = rtc_device_register("DS1302RTC",&pdev->dev, &DS1302_rtcops,THIS_MODULE);
if(rtc==NULL)
printk("RTC驱动注册失败1\n");
else
{
printk("RTC驱动注册成功1\n");
}
/*1. 初始化GPIO口*/
DS1302IO_Init();
msleep(10);
return 0;
}
static int drv_remove(struct platform_device *dev)/*当设备卸载后调用这条函数*/
{
/*释放GPIO口*/
gpio_free(EXYNOS4_GPB(4));
gpio_free(EXYNOS4_GPB(5));
gpio_free(EXYNOS4_GPB(6));
rtc_device_unregister(rtc);
printk("RTC驱动卸载成功\n");
return 0;
}
/*平台设备驱动端结构体-包含和probe匹配的设备名字*/
struct platform_driver drv=
{
.probe = drv_probe, /*需要创建一个probe函数,这个函数是对设备进行操作*/
.remove = drv_remove, /*创建一个remove函数,用于设备退出*/
.driver =
{
.name = "DS1302rtc", /*设备名称,用来与设备端匹配(非常重要)*/
},
};
/*平台驱动端的入口函数*/
static int __init plat_drv_init(void)
{
platform_driver_register(&drv);/*注册平台驱动*/
return 0;
}
/*平台驱动端的出口函数*/
static void __exit plat_drv_exit(void)
{
platform_driver_unregister(&drv);/*释放平台驱动*/
}
module_init(plat_drv_init); /*驱动模块的入口*/
module_exit(plat_drv_exit); /*驱动模块的出口*/
MODULE_LICENSE("GPL"); /*驱动的许可证-声明*/
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.
- 205.
- 206.
- 207.
- 208.
- 209.
- 210.
- 211.
- 212.
- 213.
- 214.
- 215.
- 216.
- 217.
- 218.
- 219.
- 220.
- 221.
- 222.
- 223.
- 224.
- 225.
- 226.
- 227.
- 228.
- 229.
- 230.
- 231.
- 232.
- 233.
- 234.
- 235.
- 236.
- 237.
- 238.
- 239.
- 240.
- 241.
- 242.
- 243.
- 244.
- 245.
- 246.
- 247.
- 248.
- 249.
- 250.
- 251.
- 252.
- 253.
- 254.
- 255.
- 256.
- 257.
- 258.
- 259.
- 260.
- 261.
- 262.
- 263.
- 264.
- 265.
- 266.
- 267.
- 268.
- 269.
- 270.
- 271.
- 272.
- 273.
- 274.
- 275.
- 276.