由于工作原因,最近开始做Linux下的驱动程序,刚接触驱动,确实有很多东西都是新鲜名词,以前在学校的时候,基本没有摸过linux,更别说写驱动了,废话不扯了,直接讲讲我最近做RTC驱动的过程,由于是新手,上手比较慢,搞了两个礼拜左右吧。
我用的操作系统是Linux 2.4.19 ,开发板是自己单位的交换机,用的是AT91RM9200,RTC芯片是PCF8563,之前在网上有许多大侠已经写了好多这方面的文章,作为新手的我,还是愿把自己的经历分享一把。
刚接到老大的通知,要干这个活的时候,一头雾水,linux也没有玩过,驱动也没有写过,带操作系统的驱动,也基本扯淡,还好,这边的开发环境都是现成的,花了两天的时间,把如何烧flash,如何建立NFS文件,还有一些Linux的简单命令熟悉了一下,当然uboot的一些命令也顺带看了一下,我想对新人最大的困难就是不知道,如何运用一些命令,和这些命令的含义。同时向同事借了本宋宝华老师的《设备驱动开发详解》,2.6内核的,开始看的云里雾里的,不知所云。
开始看书,都是想编个小程序,自己试验一下,想在开发板上看看效果,那hello world的程序,所处可见。光这个程序我开始都调了半天,由于是闭门造车,自己摸索,说实话,当时跑通了,别提多高兴了,之间还闹出,用gcc编辑的程序下载到开发板上,半天不认,找不出原因的笑话,现在回想,确实对新人来说还可以接受。
跑通了第一个程序,信心倍增,接着我就抱着书看关于RTC的程序,由于一开始之间的逻辑关系不是很清楚,关于操作系统也不是很了解,只以为要自己动手写个完整的驱动,看的都是内核的一些封装好的模块。实在理解不了,去网上搜了许多关于RTC方面的资料,2.6的内核确实简单,里面直接由PCF8563的驱动,只需要,写个应用程序,测试一下。这就给了我启发,看了一下我的这个内核,惊奇的发现。里面的各种驱动也是很齐全的,I2C-dev,I2C-CORE,at91-i2c都有,那我做的就是如何控制I2c去读写了。关于I2C和at91rm2000中TWI之间的控制,说实话,看的一只半解,写好应用程序,开始漫长的调程序过程。
看程序间的调用关系,在调的过程中,惊奇的发现,写不进去一个字节,在I2C和TWI之间的驱动有问题,没有办法,在调用的程序里面,一直打着printk,通过一遍遍地努力,终于给调通了。当时真的是欣喜若狂啊,不容易啊,后来就是调应用程序,发现调起来也不容易,开始时,时钟设置和输出的时钟不对,改了一下读写的寄存器地址,就更正了过来,这时候,能get到时间而且秒能跑了,心情大好,知道成功了大半,就是日历星期的时间有问题,这些参考了下网上的程序,很容易的搞定了,终于告一段落,回首过来的路,确实不容易。。
好了直接贴我的应用程序 吧:希望对大家有用:
#include
#include
#include
#include
#include
#include
#include
#define I2C_DEV "/dev/i2c"
#define START_ADDR 0x0
#define END_ADDR 0x9
#define IIC_SIZE (END_ADDR-START_ADDR+1)
#define CHIP_ADDR 0x51//slave address
//#define CHIP_ADDR_W 0xA2 //写地址
//#define CHIP_ADDR_R 0xA3//读地址
//#define CHIP_ADDR 0x68
#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10)
#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10)
#define TWELVE_HOUR_MODE(n) (((n)>>6)&1)
#define HOURS_AP(n) (((n)>>5)&1)
#define HOURS_12(n) BCD_TO_BIN((n)&0x1F)
#define HOURS_24(n) BCD_TO_BIN((n)&0x3F)
#define date_time(n) BCD_TO_BIN((n)&0x3f)
#define day_time(n) BCD_TO_BIN((n)&0x7)
struct rtc8563_time
{
unsigned char Sec;
unsigned char Min;
unsigned char Hrs;
unsigned char Day;
unsigned char Date;
unsigned char Mon;
unsigned char Year;
unsigned char alarm1_Sec;
unsigned char alarm1_Min;
unsigned char alarm1_Hrs;
unsigned char alarm1_Date;
unsigned char alarm2_Min;
unsigned char alarm2_Hrs;
unsigned char alarm2_Date;
unsigned char ctrl;
unsigned char status;
unsigned char trik_charger;
};
struct rtc_time {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mdate;
int tm_mon;
int tm_year;
};
static int read_rtc(int fd, unsigned char buff[], int addr, int count)
{
int res;
///*
// res=write(fd, &addr, 1);
// printf("return=%d \n", res);
if(write(fd, &addr, 1)!=1) //write address error
{
printf("write address error \n");
return -1;
}
//*/
printf("write over!\n");
res = read(fd, buff, count);
// res=i2cdev_read(fd,buff,count);
printf("read %d byte at 0x%x \n", res, addr);
return res;
}
static int write_rtc(int fd,unsigned char buff[], int addr, int count)
{
//int i;
int res;
static char sendbuffer[9];
printf("count %d\n\n",count);
memcpy(sendbuffer+1, buff, count);
sendbuffer[0]=addr;
res=write(fd, sendbuffer, count+1);
// res=i2cdev_write(fs,sendbuffer,count+1);
printf("write %d byte at 0x%x\n", res, addr);
return res;
}
static void
ds8563_convert_to_time( struct rtc_time *dt, char *buf)
{
dt->tm_sec = BCD_TO_BIN(buf[0]);
dt->tm_min = BCD_TO_BIN(buf[1]);
if ( TWELVE_HOUR_MODE(buf[2]) )
{
dt->tm_hour = HOURS_12(buf[2]);
if (HOURS_AP(buf[2])) /* PM */
{
dt->tm_hour += 12;
}
}
else /* 24-hour-mode */
{
dt->tm_hour = HOURS_24(buf[2]);
}
// dt->tm_mday=buf[4];
dt->tm_mdate= date_time(buf[3]);
dt->tm_mday=day_time(buf[4]);
/* dt->tm_mon is zero-based */
dt->tm_mon = (buf[5]&0x0f)+((buf[5]&0x10)>>4)*10;
/* year is 1900 + dt->tm_year */
dt->tm_year = BCD_TO_BIN(buf[6]) + 100*((buf[5]&0x80)?1:0);
if(1)
{
printf("ds8563_get_datetime: year = %d\n", dt->tm_year);//year
printf("ds8563_get_datetime: mon = %d\n", dt->tm_mon);//months
printf("ds8563_get_datetime:mdate=%d\n", dt->tm_mdate);//date
printf("ds8563_get_datetime: mday = %d\n", dt->tm_mday);//day
printf("ds8563_get_datetime: hour = %d\n", dt->tm_hour);//hour
printf("ds8563_get_datetime: min = %d\n", dt->tm_min);//min
printf("ds8563_get_datetime: sec = %d\n", dt->tm_sec);//sec
}
}
int ds8563_get_datetime(int fd, unsigned char *dt_ptr)
{
int res;
struct rtc8563_time recvbuff;
struct rtc_time *m_dt;
m_dt= (struct rtc_time *)dt_ptr;
res=read_rtc(fd, (unsigned char *)&recvbuff,01, sizeof(struct rtc8563_time));
ds8563_convert_to_time(m_dt, (unsigned char *)&recvbuff);
return res;
}
int ds8563_set_datetime(int fd, unsigned char *dt_ptr,int datetoo)
{
int res;
struct rtc8563_time *sendbuff;
struct rtc_time *m_dt;
unsigned char buf[8];//buf[8]
int len=4;
m_dt=(struct rtc_time *)dt_ptr;
sendbuff=(struct rtc8563_time*)&buf[1];
{
printf("ds8563_set_datetime: tm_year = %d\n", m_dt->tm_year);
printf("ds8563_set_datetime: tm_mon = %d\n", m_dt->tm_mon);
printf("ds8563_set_datetime:tm_date=%d\n",m_dt->tm_mdate);
printf("ds8563_set_datetime: tm_mday = %d\n", m_dt->tm_mday);
printf("ds8563_set_datetime: tm_hour = %d\n", m_dt->tm_hour);
printf("ds8563_set_datetime: tm_min = %d\n", m_dt->tm_min);
printf("ds8563_set_datetime: tm_sec = %d\n", m_dt->tm_sec);
}
buf[0] = 02; /* register address on DS8563 */
buf[1] = (BIN_TO_BCD(m_dt->tm_sec));
buf[2] = (BIN_TO_BCD(m_dt->tm_min));
buf[3] = (BIN_TO_BCD(m_dt->tm_hour));
if (datetoo) {
len=8;
if(m_dt->tm_year>200)
{
printf("error! m_dt->tm_year=%d, >200!",m_dt->tm_year);
return -1;
}
/* we skip buf[5] as we don't use day-of-week. */
buf[4] = (BIN_TO_BCD(m_dt->tm_mdate));
buf[5]=(BIN_TO_BCD(m_dt->tm_mday));
buf[6] = (BIN_TO_BCD(m_dt->tm_mon));
/* The year only ranges from 0-99, we are being passed an offset from 1900,
* and the chip calulates leap years based on 2000, thus we adjust by 100.
*/
buf[7] = (BIN_TO_BCD((m_dt->tm_year)%100));
if(m_dt->tm_year/100) buf[6]|=0x80;
}
res=write_rtc(fd, (unsigned char *)sendbuff,02,7);
printf("write %d byte at 0x%x\n", res, buf[0]);
return res;
}
int main(int argc,char *argv[])
{
int fd,res;
struct rtc_time dt;
dt.tm_year = 10;
dt.tm_mon = 9;
dt.tm_mdate = 17;
dt.tm_mday = 5;
dt.tm_hour = 13;
dt.tm_min = 22;
dt.tm_sec = 13;
fd = open(I2C_DEV, O_RDWR);
if(fd<0){
printf("#####i2c test device open failed#### \n");
return(-1);
}
else{
printf("test device open\n");
}
res = ioctl(fd, I2C_TENBIT, 0);
// printf("return=%d\n",res);
res = ioctl(fd, I2C_SLAVE, CHIP_ADDR);
printf("I2C_SLAVE return=%d\n\n\n",res);
// ds8563_set_datetime(fd, (unsigned char *)&dt, 1);
ds8563_get_datetime(fd, (unsigned char *)&dt);
close(fd);
return(0);
}