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處理器。                                        
 
********************************************* *******************************
                                             轉載請務必表明出處。 韓大衛@吉林師範大學。

********************************************** *******************************
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


*************** *********************************************************
發佈了29 篇原創文章 · 獲贊 28 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章