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(<);
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
*************** *********************************************************