Chinaunix首页 | 论坛 | 博客
  • 博客访问: 393163
  • 博文数量: 380
  • 博客积分: 75
  • 博客等级: 民兵
  • 技术积分: 1925
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-05 15:35
文章分类

全部博文(380)

文章存档

2014年(1)

2013年(2)

2012年(19)

2011年(358)

我的朋友

分类:

2012-05-29 17:15:02

原文地址:2440下Linux的RTC驱动程序 作者:yaoqigui

刚开始时运行测试程序系统就挂起,查明原因应该是头文件定义的寄存器端口地址可能是个错误的虚拟内存地址
于是在对RTC寄存器进行读写之前,我将需要用到的几个RTC寄存器自己用ioremap重新映射了一次
------------------------------------------------------------------------------------------------------------------
驱动程序代码如下:

#include
#include
#include
#include
#include
#include

static int major;

#define DEVICE_NAME "RTC"
#define DEVICE_MAJOR major
#define DEVICE_MINOR 0
#define DEVICE_IRQ IRQ_TICK

//寄存器的物理地址,查datasheet
#define RTCCON 0x57000040
#define TICNT 0x57000044
#define RTCSEC 0x57000070
#define RTCMIN 0x57000074
#define RTCHOUR 0x57000078

//用来存放映射到内存的寄存器虚拟地址
static void * rtccon;
static void * ticnt;
static void * rtcsec;
static void * rtcmin;
static void * rtchour;

static int flag=0; //中断事件发生标记
static char time[8]; //存放时间数据

static int num_to_BCD(int num) //将两位的十进制数转换为BCD码
{
    int i,j,r;
    i=num/10;
    j=num%10;
    r=(i<<4)|j;
    return r;
}

static int num_from_BCD(int num) //将BCD码转换为两位的十进制数
{
    int i,j,r;
    i=(num&0xf0)>>4;
    j=num&0x0f;
    r=i*10+j;
    return r;
}

static void set_time(int hour,int min,int sec) //将整型变量转为字符数据存放
{
    time[0]=hour/10+'0';
    time[1]=hour%10+'0';
    time[2]=':';
    time[3]=min/10+'0';
    time[4]=min%10+'0';
    time[5]=':';
    time[6]=sec/10+'0';
    time[7]=sec%10+'0';
}

static DECLARE_WAIT_QUEUE_HEAD(wq); //创建一个等待队列

irqreturn_t RTC_interrupt(void)
{
    flag++;
    wake_up_interruptible(&wq); //在中断服务程序内唤醒等待队列的进程,这里是唤醒read进程
    return IRQ_HANDLED;
}

static int RTC_read(struct file *filp,char __user *buff,size_t count,loff_t *offp)
{
    int hour,min,sec;
    wait_event_interruptible(wq,flag); //在此处进入休眠,等待中断唤醒
    hour=num_from_BCD(ioread32(rtchour)); //从BCD寄存器读取时间,并将BCD码转换为整型数据
    min=num_from_BCD(ioread32(rtcmin));
    sec=num_from_BCD(ioread32(rtcsec));
    set_time(hour,min,sec);
    copy_to_user(buff,time,sizeof(time)); //将时间数据复制到用户空间
    flag=0;
    return 0;
}

static int RTC_write(struct file *filp,const char __user *buff,size_t count,loff_t *offp)
{
    int hour,min,sec,tmp;
    copy_from_user(time,buff,sizeof(time)); //从用户空间复制时间数据到内核空间
    hour=time[1]-'0'+(time[0]-'0')*10;
    min=time[4]-'0'+(time[3]-'0')*10;
    sec=time[7]-'0'+(time[6]-'0')*10;
    tmp=ioread32(ticnt)&(~(1<<7));
    iowrite32(tmp,ticnt); //停止时钟节拍
    iowrite32(0x01,rtccon); //写BCD寄存器前RTCCON位0写1
    iowrite32(num_to_BCD(hour),rtchour);
    iowrite32(num_to_BCD(min),rtcmin);
    iowrite32(num_to_BCD(sec),rtcsec);
    iowrite32(0x00,rtccon); //写完BCD寄存器后RTCCON位0清0
    tmp=ioread32(ticnt)|(1<<7);
    iowrite32(tmp,ticnt); //启动时钟节拍
    return 0;
}

static int RTC_open(struct inode *inode,struct file *filp)
{
    int ret;
    ret=request_irq(DEVICE_IRQ,&RTC_interrupt,SA_INTERRUPT,DEVICE_NAME,NULL);
    if(ret<0){
        printk("Request irq failed!\n");
        return ret;
    }
/********************************************************************************
ioremap函数,将端口物理地址映射到虚拟内存,之后可以直接读写返回的虚拟内存地址,即寄存器,
多次映射的虚拟地址可能会不同,但都是对同一物理地址进行操作
**********************************************************************************/
    rtccon=ioremap(RTCCON,4);//RTCCON的寄存器端口地址(4个字节长度)映射到虚拟内存,返回值是虚拟地址
    ticnt=ioremap(TICNT,4);
    rtcsec=ioremap(RTCSEC,4);
    rtcmin=ioremap(RTCMIN,4);
    rtchour=ioremap(RTCHOUR,4);
    iowrite32(0x01,rtccon);  //写BCD寄存器前RTCCON置1
    iowrite32(0,rtcsec); //设置BCD寄存器内的实时时间
    iowrite32((3<<4)|0,rtcmin);
    iowrite32((1<<4)|2,rtchour);
    iowrite32(0x00,rtccon); //写完BCD寄存器RTCCON清0保护数据
    iowrite32(127|(1<<7),ticnt);  //时钟节拍中断为一秒
    return 0;
}

static int RTC_close(struct inode *inode,struct file *filp)
{
    free_irq(DEVICE_IRQ,NULL);
    return 0;
}

static struct file_operations RTC_fops={
    .owner=THIS_MODULE,
    .open=RTC_open,
    .release=RTC_close,
    .read=RTC_read,
    .write=RTC_write,
};

int RTC_init(void)
{
    int ret;
    ret=register_chrdev(0,DEVICE_NAME,&RTC_fops);
    if(ret<0){
        printk("Register device failed!\n");
        return ret;
    }
    else major=ret;
    printk("Register device successfully!\n");
    devfs_mk_cdev(MKDEV(DEVICE_MAJOR,DEVICE_MINOR),S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP,DEVICE_NAME);
    return 0;
}

void RTC_exit(void)
{
    unregister_chrdev(DEVICE_MAJOR,DEVICE_NAME);
    devfs_remove(DEVICE_NAME);
    printk("Device has been unregistered!\n");
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("HJW");
module_init(RTC_init);
module_exit(RTC_exit);






----------------------------------------------------------------------------------
当需要用到write函数时,应用程序的open函数注意不要以0方式打开,而要以O_RDWR方式打开,否则是进入不了内核的write函数的

测试程序代码如下:

#include
#include //open函数用到
#include //signal函数用到

static char time[8]; //输入输出都是字符格式
static int fd;   //打开设备文件的文件句柄

void int_to_char(int t[])
{
 time[0]=t[0]/10+'0';
 time[1]=t[0]%10+'0';
 time[3]=t[1]/10+'0';
 time[4]=t[1]%10+'0';
 time[6]=t[2]/10+'0';
 time[7]=t[2]%10+'0';
}

void sig_handle(void)
{
 int t[3];
 printf("Input new time below\n");
 printf("hour:");scanf("%d",&t[0]);
 printf("min:");scanf("%d",&t[1]);
 printf("sec:");scanf("%d",&t[2]);
 int_to_char(t);
 write(fd,time,sizeof(time)); //fd和time设为全局变量是因为这里要用到
}

int main(void)
{
 int i;
 signal(SIGTSTP,(void *)sig_handle); //捕捉信号,Ctrl+Z是一个SIGTSTP信号
 fd=open("/dev/RTC",O_RDWR);  //以可读可写方式打开,用0默认方式不可写
 if(fd<0){
  printf("Open device failed!\n");
  exit(1); //异常退出
 }
 printf("Open device successfully!\n");
 while(1){
  read(fd,time,sizeof(time));
  for(i=0;i<8;i++){
   printf("%c",time[i]);
  }
  printf(" (press Ctrl+Z to reset time)\n");
  printf("\n");
 }
 close(fd);
 return 0;
}
阅读(720) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~