linux下的i2c与时钟芯片pcf8563通信

		


2012/7/19
					
linux下的i2c与时钟芯片pcf8563通信  			

by: 韩大卫 @吉林师范大学

1,本程序增加了对星期寄存器(wday)的写操作。
2,  本程序将i2c-test 改为rtdate 命令,并将rtdate作为最终命令发布。
3,在linux下,成功地在用户层操作了时钟芯片pcf8563的寄存器。
4,本程序考虑到了不同的系统中的rtc时钟芯片挂在不同的i2c bus的位置。、
      比如:如果挂在 /dev/i2c-0 下,那么就只能打开/dev/i2c-0 进行操作,否则的话read/write 均会失败,
那么这样就需要我们手动的传入参数,这需在程序中加入相应的判断条件。同时为了最大程度上做到便捷,
如果不使用路径参数的话,那么程序中给出了默认路径/dev/i2c-1, 我们这样就可以直接 

#rtdate -s 2012.7.19-19.36.00  修改硬件时间。
#rtdate -s 19.36.00 修改硬件时间
#rtdate -r  显示硬件时间
#rtdate -l  显示系统时间

需要手动加路径的话:
#rtdate -d /dev/i2c-10 -s 18:20:30
这样可以做到在不同的嵌入式系统中,不修改代码的情况下操作时钟芯片读写硬件时间,强化了软件的健壮性。


我们嵌入式系统中的cpu是Cavium Networks OCTEON CN5220,基于64bit的mips处理器。										
 
********************************************* *******************************
                                             转载请务必表明出处。 韩大卫@吉林师范大学@08信息。

********************************************** *******************************
main.c
**************** ******************************************
#include "i2c.h"

#define TIMEOUT	3
#define RETRY	3

static int fd;

static inline int is_leap_year(unsigned int year){

        return (!(year % 4) && (year % 100)) || !(year % 400);
}   

int rtc_month_days(unsigned int month, unsigned int year){

        return rtc_days_in_month[month - 1] + (is_leap_year(year) && month == 1); 
}

/*
*	test year.mon.mday.wday.hour.min.sec
*/
int rtc_valid_tm(struct rtc_time *tm)
{
        if(	tm->tm_year < 1970
		|| tm->tm_year >2069
                || ((unsigned)tm->tm_mon)  > 12
                || tm->tm_mday < 0
                || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year)
                || ((unsigned)tm->tm_hour) >= 24
                || ((unsigned)tm->tm_min)  >= 60
                || ((unsigned)tm->tm_sec)  >= 60)                  
                return -EINVAL;

        return 0;
}

/*
*	test hour:min:sec
*/
int rtc_valid_half_tm(struct rtc_time *tm)
{
       if((unsigned)tm->tm_hour >= 24  ||
       	  (unsigned)tm->tm_min  >= 60  ||
       	  (unsigned)tm->tm_sec  >= 60)                     
                return -EINVAL;

        return 0;
}

int i_open(unsigned char* dev, unsigned int timeout, unsigned int retry){

        return i2c_open(dev,timeout,retry); 
}

int read_data(u16 addr, u8 offset, u8 *val){
	int ret;
	
	ret = i2c_read_data(addr,offset,val);
	if(ret < 0){
		printf("%s error!\n",__FUNCTION__);
		exit(-1);
	}
	return 0;
}

int write_data(u16 addr, u8 offset, u8 val){
	int ret;
	
	ret = i2c_write_data(addr,offset,val);
	if(ret < 0){
		printf("%s error!\n",__FUNCTION__);
		exit(-1);
	}
	return 0;
}

int get_week_data(int y,int m,int d){
	unsigned int w, c;

	if (m <= 2){ 
		m |= 4;
		y--;
	}
	c =	y/100;
	c &=	0x03;
	y %=	100;
	w =	((c | (c << 2)) + (y + (y >> 2)) + (13 * m + 8)/ 5 + d) % 7;

	return w;
}

/*
*	-s YYYY.MM.DD-hh.mm.ss
*/
int time_year_begin(char* ptr,struct rtc_time* tm){
    
	if( !(*(ptr+4) == '.' || *(ptr+4) == ':' )){
		printf("TIME Format error !\n");
		exit(-1);
	}

	u8 offset;
	u16 addr 		= 	0x51;
	int i			= 	0;
	int set_hour_flags	=	0;
	unsigned char buf[8]	=	{0};
	int data[10]		=	{0};

	/* get year */
        data[i++] = atoi(ptr);

        while( *ptr ){
                if( *ptr == '.' || *ptr == ':'){
                        ptr++;
			/* get mon and mday */
                        data[i++]  = atoi(ptr);
		}
                ptr++;
                if( *ptr == '-'){
			set_hour_flags = 1;
                        ptr++;
			/* get hour */
                        data[i++] = atoi(ptr);
                        while( *ptr ){
                                if( *ptr == '.' || *ptr == ':'  ){
                                        ptr++;
					/* get min and sec */
                                        data[i++]  = atoi(ptr);
                                }
                                else ptr++;
                        }
                        break;
                }
        }
        tm->tm_year     =       data[0];
        tm->tm_mon      =       data[1];
        tm->tm_mday     =       data[2];
        tm->tm_hour     =       data[3];
        tm->tm_min      =       data[4];
        tm->tm_sec      =       data[5];
	tm->tm_wday	=	get_week_data(tm->tm_year,tm->tm_mon,tm->tm_mday);

	if(rtc_valid_tm(tm) < 0){
		printf("date/time is not valid.set time error!\n");
		exit(-1);
	}

	buf[PCF8563_REG_SC]	=	bin2bcd(tm->tm_sec);
        buf[PCF8563_REG_MN]	=	bin2bcd(tm->tm_min);
        buf[PCF8563_REG_HR] 	=	bin2bcd(tm->tm_hour);
	buf[PCF8563_REG_DM]	=	bin2bcd(tm->tm_mday);
        buf[PCF8563_REG_DW]	=	tm->tm_wday & 0x07;
	buf[PCF8563_REG_MO]	=	bin2bcd(tm->tm_mon);
        buf[PCF8563_REG_YR]	=	bin2bcd(tm->tm_year % 100);

	if ( tm->tm_year/2000  )
		buf[PCF8563_REG_MO] |= 0x00;
	else
		buf[PCF8563_REG_MO] |= PCF8563_MO_C;
	
	/* write year-wday or year-sec	*/
	for(offset = set_hour_flags? PCF8563_REG_SC : PCF8563_REG_DM; offset <= PCF8563_REG_YR ; offset++ ){
		write_data(addr,offset,buf[offset]);
	}	
	rtc_time_list(); 
        return 0;
}

/*
*	-s hh.mm.ss
*/
int time_hour_begin(char *ptr,struct rtc_time* tm){

	if( !(*(ptr+2) == '.' || *(ptr+1) == '.' || *(ptr+2) == ':' || *(ptr+1) == ':') ){
                printf("TIME Format error !\n");
                exit(-1);
        }

	u8 offset;
	u16 addr 		=	0x51;
        int i 			=	0;
	unsigned char buf[4]	=	{0};	
	int data[10]		=	{0};

	/* get hour */
        data[i++] = atoi(ptr);
        while( *ptr ){
                if( *ptr == '.' || *ptr == ':'){
                        ptr++;
			/* get min and sec */
                        data[i++] = atoi(ptr);
                }
                else
                        ptr++;
        }

        tm->tm_hour     =       data[0];
        tm->tm_min      =       data[1];
        tm->tm_sec      =       data[2];

	if(rtc_valid_half_tm(tm) < 0){
		printf("date/time is not valid.set time error!\n");
		exit(-1);
	}
	
	buf[PCF8563_REG_SC]	=	bin2bcd(tm->tm_sec);
        buf[PCF8563_REG_MN]	=	bin2bcd(tm->tm_min);
        buf[PCF8563_REG_HR] 	=	bin2bcd(tm->tm_hour);
	
	for(offset = PCF8563_REG_SC; offset <= PCF8563_REG_HR ; offset++ ){
		write_data(addr,offset,buf[offset]);
	}

	rtc_time_list(); 
        return 0;
}

int rtc_time_list(void){

	u16 addr	=	0x51;
	u8 offset	=	0x00;
	u8 buf[13]	=	{0};
	int data[10]	=	{0};
	struct rtc_time *tm = (struct rtc_time*)malloc(sizeof(struct rtc_time));
	
	read_data(addr, offset, buf);
	
	tm->tm_sec	=	buf[PCF8563_REG_SC] & 0x7F;
        tm->tm_min	=	buf[PCF8563_REG_MN] & 0x7F;
        tm->tm_hour	=	buf[PCF8563_REG_HR] & 0x3F;	// rtc hr 0-23 
        tm->tm_mday	=	buf[PCF8563_REG_DM] & 0x3F;
        tm->tm_wday	=	buf[PCF8563_REG_DW] & 0x07;
        tm->tm_mon	=	buf[PCF8563_REG_MO] & 0x1F;	// rtc mn 1-12  -1 :tm_mon:[0-11]
        tm->tm_year	=	buf[PCF8563_REG_YR];

	if(buf[PCF8563_REG_MO] & PCF8563_MO_C)
		tm->tm_year	+=	0x1900;			//MO:8bit = 1	:1900
	else
		tm->tm_year	+=	0x2000;			//MO:8bit = 0	:2000

	printf("%4x:%02x:%02x  ",
                 tm->tm_year, tm->tm_mon, tm->tm_mday);

	switch(tm->tm_wday){
		case 1 :
			printf("Mon ");break;
		case 2 :
			printf("Tus ");break;
		case 3 :
			printf("Wed ");break;
		case 4 :
			printf("Thu ");break;
		case 5 :
			printf("Fir ");break;
		case 6 :
			printf("Sat ");break;
		case 7 :
			printf("Sun ");break;
	}
	printf("%02x:%02x:%02x\n",
                 tm->tm_hour, tm->tm_min, tm->tm_sec);
	return 0;
}

int sys_time_list(){

	struct tm* ptr;
	time_t lt;
	
	lt  = time(NULL);
	ptr = localtime(&lt);

	printf("%4d:",ptr->tm_year + 1900);	
	printf("%02d:",ptr->tm_mon + 1);	
	printf("%02d  ",ptr->tm_mday);	

	switch(ptr->tm_wday){
		case 1 :
			printf("Mon ");break;
		case 2 :
			printf("Tus ");break;
		case 3 :
			printf("Wed ");break;
		case 4 :
			printf("Thu ");break;
		case 5 :
			printf("Fir ");break;
		case 6 :
			printf("Sat ");break;
		case 7 :
			printf("Sun ");break;
	}
	printf("%02d:",ptr->tm_hour);	
	printf("%02d:",ptr->tm_min);
	printf("%02d\n",ptr->tm_sec);

	return 0;
}

int i2c_rtc_data(char* ptr){

	struct rtc_time* tm	=	(struct rtc_time*)malloc(sizeof(struct rtc_time));
	int c 			=	atoi(ptr);

        if( c /1000 )
                time_year_begin(ptr,tm);
        else
                time_hour_begin(ptr,tm);

	return 0;
}

int help_info(void){
	printf("\nUsage: rtdate  [-d PATH] [OPTIONS]\n");
	printf("\nOr:    rtdate  [-d PATH] -s TIME \n");
        printf("\nDisplay rtc-time or sys-time, or set time.\n");
        printf("\nOptions:\n");
        printf("\n\t-r\t\tshow rtc-time\n");
        printf("\n\t-l\t\tshow sys-time\n");
        printf("\n\t-s TIME\ttset rtc-time\n");
        printf("\n\t-h or --help\tshow help information\n");
        printf("\nRecognized formats for TIME:\n");
        printf("\n\tYYYY.MM.DD-hh.mm[.ss]\n");
        printf("\n\tYYYY.MM.DD\n");
        printf("\n\thh.mm[.ss]\n");
        printf("\n\thh:mm[:ss]\n");
        printf("\n\t'.' can be replaced by ':'\n");
	printf("\nFor example\n\trtdate -s YYYY.MM.DD \n");
	printf("\n\trtdate  -s hh.mm:ss \n");
	printf("\n\trtdate -d /dev/i2c-1 -s YYYY:MM:DD-hh.mm:ss \n\n");

	return 0;
}

void path(char* argv){

	fd = i_open(argv,TIMEOUT,RETRY);
	if( fd < 0 ){	
		printf("i2c_open error!\n");
		exit(-1);
	}	

}

int main(int argc,char* argv[]){

	int sw = argc;

	if( argc >= 3){
		if(!strcmp(argv[1],	"-d")){
			path(argv[2]);
			sw -= 2;
		}else{
			path("/dev/i2c-1");
		}
	}else{	
		path("/dev/i2c-1");
	}
	
	switch(sw){
	case 1: {
		if( rtc_time_list() ){
			printf("rtctime_show error!\n");
			exit(-1);			
		}
		break;
	}	
	case 2: {
		if(!strcmp(argv[argc-1],"-l")){ 
			if( sys_time_list() ){
				printf("systime_show error!\n");
				exit(-1);
			}
		}else if(!strcmp(argv[argc-1],"-r")){
			if( rtc_time_list() ){
				printf("rtctime_show error!\n");
				exit(-1);
			}
		}else if(!strcmp(argv[argc-1],"-h") || !strcmp(argv[argc-1],"--help" ) ) {
				help_info();
		}else{
			printf("useless operation  %s ...\n Please try -h or --help for more information\n",argv[argc-1]);  
			exit(-1);	
		}
		break;
	}
	case 3: {
		if(!strcmp(argv[argc-2], "-s")){
			i2c_rtc_data(argv[argc-1]);	
		}
		break;
	}
	default:	
		printf("command error!\nPlease input -h or--help for more information"),exit(-1);	
	}
	
	close(fd);

	return 0;
}

************************** *********************************************

i2c.c :

******************* *****************************************
#include "i2c.h"

static int fd;

int
i2c_read_data(u16 addr, u8 offset, u8 *val)
{
	int i,ret = 0;

	struct i2c_rdwr_ioctl_data *data;

	if((data =(struct i2c_rdwr_ioctl_data *)malloc(sizeof(struct i2c_rdwr_ioctl_data)))==NULL)
	return -1;

	data->nmsgs = 2;
	if ((data->msgs = (struct i2c_msg *)malloc(data->nmsgs * sizeof(struct i2c_msg))) == NULL) {
		ret = -1;
		goto errexit3;
	}
	if ((data->msgs[0].buf = (unsigned char *)malloc(sizeof(unsigned char))) == NULL) {
		ret = -1;
		goto errexit2;
	}
	if ((data->msgs[1].buf = (unsigned char *)malloc(sizeof(unsigned char))) == NULL) {
		ret = -1;
		goto errexit1;
	}

	data->msgs[0].addr = addr;
	data->msgs[0].flags = 0;
	data->msgs[0].len = 1;
	data->msgs[0].buf[0] = offset;

	data->msgs[1].addr = addr;
	data->msgs[1].flags = I2C_M_RD;
	data->msgs[1].len = 13;			//original data is 1
	data->msgs[1].buf[0] = 0;

	if ((ret = __i2c_send(fd, data)) < 0)
		goto errexit0;

	for(i = 0 ;i < data->msgs[1].len; i++)
		val[i] = data->msgs[1].buf[i];

errexit0:
	free(data->msgs[1].buf);
errexit1:
	free(data->msgs[0].buf);
errexit2:
	free(data->msgs);
errexit3:
	free(data);

	return ret;
}

int
i2c_write_data(u16 addr, u8 offset, u8 val)
{
	int ret = 0;

	struct i2c_rdwr_ioctl_data *data;

	if ((data = (struct i2c_rdwr_ioctl_data *)malloc(sizeof(struct i2c_rdwr_ioctl_data))) == NULL)
		return -1;

	data->nmsgs = 1;
	if ((data->msgs = (struct i2c_msg *)malloc(data->nmsgs * sizeof(struct i2c_msg))) == NULL) {
		ret = -1;
		goto errexit2;
	}
	if ((data->msgs[0].buf = (unsigned char *)malloc(2 * sizeof(unsigned char))) == NULL) {
		ret = -1;
		goto errexit1;
	}

	data->msgs[0].addr = addr;
	data->msgs[0].flags = 0;
	data->msgs[0].len = 2;
	data->msgs[0].buf[0] = offset;
	data->msgs[0].buf[1] = val;

	if ((ret = __i2c_send(fd, data)) < 0)
		goto errexit0;

errexit0:
	free(data->msgs[0].buf);
errexit1:
	free(data->msgs);
errexit2:
	free(data);

	return ret;
}

int
i2c_open(unsigned char* dev, unsigned int timeout, unsigned int retry)
{
	if ((fd = open(dev, O_RDWR)) < 0)
		return fd;
	
	__i2c_set(fd, timeout, retry);

	return fd;
}

static int
__i2c_send(int fd, struct i2c_rdwr_ioctl_data *data)
{
	if (fd < 0)
		return -1;

	if (data == NULL)
		return -1;

	if (data->msgs == NULL || data->nmsgs == 0)
		return -1;
	
	return ioctl(fd, I2C_RDWR, (unsigned long)data) ;
}

static int
__i2c_set(int fd, unsigned int timeout, unsigned int retry)
{
	if (fd == 0 )
		return -1;

	ioctl(fd, I2C_TIMEOUT, timeout ? timeout : I2C_DEFAULT_TIMEOUT);

	ioctl(fd, I2C_RETRIES, retry ? retry : I2C_DEFAULT_RETRY);
	
	return 0;
}

void
i2c_close(int fd)
{
	if (fd < 0)
		return;

	close(fd);
}

unsigned bcd2bin(unsigned char val)
{       
        return (val & 0x0f) + (val >> 4) * 10;  
}

unsigned char bin2bcd(unsigned val)
{       
        return ((val / 10) << 4) + val % 10;
}    


********* *******************************************************

i2c.h :

******************* *******************************************

#ifndef I2C_H
#define I2C_H

#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <linux/rtc.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>

#define I2C_DEFAULT_TIMEOUT		1
#define I2C_DEFAULT_RETRY		3

#define PCF8563_REG_SC          0x02 
#define PCF8563_REG_MN          0x03
#define PCF8563_REG_HR          0x04
#define PCF8563_REG_DM          0x05
#define PCF8563_REG_DW          0x06
#define PCF8563_REG_MO          0x07
#define PCF8563_REG_YR          0x08

#define PCF8563_MO_C            0x80

typedef unsigned char           u8;
typedef unsigned short          u16;
typedef unsigned int            u32;
typedef unsigned long long      u64;
typedef signed char             s8;
typedef short                   s16;            
typedef int                     s32;
typedef long long               s64;

static const unsigned char rtc_days_in_month[] = {
        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

unsigned bcd2bin(unsigned char val);

unsigned bcd2bin(unsigned char val);

static int
__i2c_send(int fd, struct i2c_rdwr_ioctl_data *data);

static int
__i2c_set(int fd, unsigned int timeout, unsigned int retry);

int
i2c_read_data(u16 addr, u8 offset, u8 *val);

int
i2c_write_data(u16 addr, u8 offset, u8 val);

int
i2c_open(unsigned char* dev, unsigned int timeout, unsigned int retry);

#endif


*************** *********************************************************

你可能感兴趣的:(linux,通信,驱动,i2c,时钟芯片)