1. 程式人生 > >SylixOS中RTC設備驅動

SylixOS中RTC設備驅動

sylixos rtc

1、概述

本文檔基於SylixOS-EVB-i.MX6Q驗證平臺,介紹SylixOSRTC設備驅動實現過程,可作為在SylixOS集成開發環境下進行字符設備驅動開發的參考。

2RTC設備驅動

2.1硬件原理

實時時鐘(RTC)的主要功能是在系統掉電的情況下,利用備用電源使時鐘繼續運行,保證不會丟失時間信息。

i.MX6Q驗證平臺上使用的是外置實時時鐘集成電路ISL1208。硬件接線如圖 2.1所示。

技術分享

2.1 RTC硬件接線

圖中,X1X2為內部反向放大器的輸入和輸出引腳,要求外置一個

32.768kHz的晶體振蕩器以提供振蕩源;VBAT為備用電源提供端,當VDD電源失效時,VBAT端的電源立即工作,保證在外部供電中斷的情況下,內部的時鐘信號產生電路依舊正常工作;SDASCL為連接到I2C總線的兩個引腳,用於傳輸串行數據信號和時鐘信號,最高傳輸速率達到400kHznRQ/FOUT是一個多功能引腳,可以將其配置為中斷輸出或固定頻率輸出端;VDDGND分別為電源端和地,ISL1208的工作電壓為2.0~5.5V

2.2驅動實現

2.2.1安裝RTC設備驅動程序

參照上一章節字符設備驅動模型,調用API_RtcDrvInstall內核函數,註冊RTC設備驅動程序,包括RTC設備的創建、刪除、打開、關閉、讀寫及

IO控制等驅動函數,註冊的過稱即是在內核維護的驅動程序表中,找到空閑位置,然後將驅動函數填入該空閑驅動程序表。註冊RTC設備驅動流程如程序清單 2.1所示,函數返回驅動函數索引號_G_iRtcDrvNum——驅動函數在驅動程序表中的位置。

程序清單 2.1安裝RTC設備驅動程序

LW_API  INT  API_RtcDrvInstall (VOID)
{
    if (_G_iRtcDrvNum > 0) {
        return  (ERROR_NONE);
    }
    
    _G_iRtcDrvNum = iosDrvInstall(__rtcOpen, 
                                  (FUNCPTR)LW_NULL, 
                                  __rtcOpen,
                                  __rtcClose,
                                  LW_NULL,
                                  LW_NULL,
                                  __rtcIoctl);
    DRIVER_LICENSE(_G_iRtcDrvNum,     "GPL->Ver 2.0");
    DRIVER_AUTHOR(_G_iRtcDrvNum,      "Han.hui");
    DRIVER_DESCRIPTION(_G_iRtcDrvNum, "hardware rtc.");

    return  ((_G_iRtcDrvNum > 0) ? (ERROR_NONE) : (PX_ERROR));
}



2.2.2創建設備

1. ISL1208 RTC驅動程序

ISL1208驅動程序主要包括RTC初始化、時間設置、時間獲取。如程序清單 2.2所示。

程序清單 2.2 ISL1208 RTC驅動程序集

static  LW_RTC_FUNCS     _G_isl1208RtcFuncs = {
        isl1208RtcInit,
        isl1208RtcSetTime,
        isl1208RtcGetTime,
        LW_NULL
};


1isl1208RtcInit——RTC初始化

由於ISL1208 RTC通過I2C總線進行數據傳輸,所以需要在RTC初始化函數中調用API_I2cDeviceCreate內核函數創建RTC設備,將其作為從設備掛接在I2C總線上(前提是已實現驗證平臺上的I2C驅動並創建I2C適配器),這樣,RTC進行數據傳輸時即可根據適配器名查找到對應的I2C適配器,並調用I2C適配器的操作函數。具體實現如程序清單 2.3所示。

程序清單 2.3 RTC初始化

static VOID  isl1208RtcInit (VOID)
{
    __PISL1208_RTC_CONTROLER  pRtcControler = &_G_isl1208RtcControlers[0];

    if (!pRtcControler->RTCC_pI2cDevice) {
        pRtcControler->RTCC_pI2cDevice = API_I2cDeviceCreate(pRtcControler->RTCC_pcI2cBusName,
                                                             "/dev/rtc",
                                                             0x6F,      /* ISL 芯片的設備地址          */
                                                             0);
        if (pRtcControler->RTCC_pI2cDevice) {
        	isl1208RtcHwInit();
        }
    }
}


2isl1208RtcSetTime——RTC時間設置

RTC的時間設置過程:

  1. 將待設置時間進行轉碼並填入數據buf

  2. 封裝I2C總線傳輸控制消息,消息中包含RTC從設備地址、消息長度、傳輸控制標誌(讀/寫)、數據buf

  3. 發送I2C控制消息,控制RTC設備寫使能;

  4. 發送I2C控制消息,填充RTC對應時間設置寄存器;

  5. 發送I2C控制消息,控制RTC設備寫失能。

RTC時間設置過程如程序清單 2.4所示,其中isl1208ReadRegisl1208SetRegs即為封裝I2C總線傳輸控制消息並調用總線傳輸控制函數進行消息傳輸的過程,這裏不做過多描述。

程序清單 2.4 RTC時間設置

static INT  isl1208RtcSetTime (PLW_RTC_FUNCS  pRtcFuncs, time_t  *pTimeNow)
{
    __PISL1208_RTC_CONTROLER  pRtcControler = &_G_isl1208RtcControlers[0];
    struct tm                 tmNow;
    UINT8                     aucBuffer[ISL1208_RTC_SECTION_LEN] = { 0, };
    UINT8                     ucCtlDat[1], ucRegDat[1];

    gmtime_r(pTimeNow, &tmNow);                                         /*  轉換成 tm 時間格式          */

    /*
     * 將待設置時間進行轉碼並填入數據buf
     */
    aucBuffer[ISL1208_REG_YR] = rtcBinToBcd(tmNow.tm_year % 100);
    aucBuffer[ISL1208_REG_MO] = rtcBinToBcd(tmNow.tm_mon + 1);
    aucBuffer[ISL1208_REG_DT] = rtcBinToBcd(tmNow.tm_mday);
    aucBuffer[ISL1208_REG_DW] = rtcBinToBcd(tmNow.tm_wday & 7);
    aucBuffer[ISL1208_REG_HR] = rtcBinToBcd(tmNow.tm_hour);
    aucBuffer[ISL1208_REG_MN] = rtcBinToBcd(tmNow.tm_min);
    aucBuffer[ISL1208_REG_SC] = rtcBinToBcd(tmNow.tm_sec);

    /*
     * 讀取 RTC 的狀態值並設置 WRTC, 控制 RTC 寫使能
     */
    isl1208ReadReg(pRtcControler->RTCC_pI2cDevice, ISL1208_REG_SR, ucRegDat, 1);
    ucCtlDat[0] = ucRegDat[0] | ISL1208_REG_SR_WRTC;
    isl1208SetRegs(pRtcControler->RTCC_pI2cDevice, ISL1208_REG_SR, ucCtlDat, 1);

    /*
     * 寫入待設置時間
     */
    isl1208SetRegs(pRtcControler->RTCC_pI2cDevice, 0, aucBuffer, ISL1208_RTC_SECTION_LEN);

    /*
     * 設置 WRTC, 控制 RTC 寫失能
     */
    ucCtlDat[0] = ucRegDat[0] & ~ISL1208_REG_SR_WRTC;
    isl1208SetRegs(pRtcControler->RTCC_pI2cDevice, ISL1208_REG_SR, ucCtlDat, 1);

    return  (ERROR_NONE);
}


3isl1208RtcGetTime——RTC時間獲取

RTC時間獲取就是調用isl1208ReadReg函數,讀取RTC設備存放時間信息的寄存器,然後將獲取的時間進行轉碼,獲得當前年、月、日、時、分、秒等信息,填入相應結構體。具體實現過程如程序清單 2.5所示。

程序清單 2.5 RTC時間獲取

static INT  isl1208RtcGetTime (PLW_RTC_FUNCS  pRtcFuncs, time_t  *pTimeNow)
{
    __PISL1208_RTC_CONTROLER  pRtcControler = &_G_isl1208RtcControlers[0];
    struct tm                 tmNow;
    UINT8                     ucValue;
    UINT8                     aucBuffer[ISL1208_RTC_SECTION_LEN] = { 0, };

    /*
     * 讀取RTC設備存放時間信息的寄存器
     */
    isl1208ReadReg(pRtcControler->RTCC_pI2cDevice, 0, aucBuffer, ISL1208_RTC_SECTION_LEN);

    /*
     * 對獲取的時間信息進行轉碼並填入相應的結構體
     */
    tmNow.tm_sec  = rtcBcdToBin(aucBuffer[ISL1208_REG_SC]);
    tmNow.tm_min  = rtcBcdToBin(aucBuffer[ISL1208_REG_MN]);

    ucValue = aucBuffer[ISL1208_REG_HR];
    if (ucValue & ISL1208_REG_HR_MIL) {                                 /*  24h 格式                    */
        tmNow.tm_hour = rtcBcdToBin(ucValue & 0x3F);
    } else {                                                            /*  12h 格式                    */
        if (ucValue & ISL1208_REG_HR_PM) {                              /*  PM 標誌設置                 */
            tmNow.tm_hour = 12 + rtcBcdToBin(ucValue & 0x1F);
        } else {
            tmNow.tm_hour = rtcBcdToBin(ucValue & 0x1F);
        }
    }

    tmNow.tm_wday  = rtcBcdToBin(aucBuffer[ISL1208_REG_DW]);
    tmNow.tm_mday  = rtcBcdToBin(aucBuffer[ISL1208_REG_DT]);
    tmNow.tm_mon   = rtcBcdToBin(aucBuffer[ISL1208_REG_MO]) - 1;
    tmNow.tm_year  = rtcBcdToBin(aucBuffer[ISL1208_REG_YR]) + 100;
    tmNow.tm_yday  = 0;
    tmNow.tm_isdst = 0;

    if (pTimeNow) {
        *pTimeNow = timegm(&tmNow);
    }

    return  (ERROR_NONE);
}


2. 創建RTC設備

調用API_RtcDevCreate創建RTC設備,設備結構體prtcdev中保存的操作函數集就是函數入參——ISL1208 RTC驅動程序_G_isl1208RtcFuncs。然後再調用API_IosDevAddEx內核函數,向系統中添加一個設備,該函數中主要就是填充設備結構體中的另一個成員——設備頭,設備頭中保存著設備名稱、設備驅動索引號、設備類型、打開次數等。最後,將該設備頭添加入設備頭管理鏈表進行管理。RTC設備的創建過程如程序清單 2.6所示。

程序清單 2.6 RTC設備創建

LW_API  INT  API_RtcDevCreate (PLW_RTC_FUNCS    prtcfuncs)
{
    PLW_RTC_DEV     prtcdev;
    
    if (prtcfuncs == LW_NULL) {
        _ErrorHandle(EINVAL);
        return  (PX_ERROR);
    }
    
    if (_G_iRtcDrvNum <= 0) {
        _DebugHandle(__ERRORMESSAGE_LEVEL, "no driver.\r\n");
        _ErrorHandle(ERROR_IO_NO_DRIVER);
        return  (PX_ERROR);
    }

    /*
     * 為設備結構體分配空間
     */
    prtcdev = (PLW_RTC_DEV)__SHEAP_ALLOC(sizeof(LW_RTC_DEV));
    if (prtcdev == LW_NULL) {
        _DebugHandle(__ERRORMESSAGE_LEVEL, "system low memory.\r\n");
        _ErrorHandle(ERROR_SYSTEM_LOW_MEMORY);
        return  (PX_ERROR);
    }
    lib_bzero(prtcdev, sizeof(LW_RTC_DEV));
    
    /*
     * 保存RTC設備驅動程序
     */
    prtcdev->RTCDEV_prtcfuncs = prtcfuncs;

    /*
     * 向系統中添加一個設備,將設備頭鏈入鏈表進行管理
     */
    if (iosDevAddEx(&prtcdev->RTCDEV_devhdr, __LW_RTC_DEV_NAME, _G_iRtcDrvNum, DT_CHR) != ERROR_NONE) {
        __SHEAP_FREE((PVOID)prtcdev);
        return  (PX_ERROR);
    }
    
    /*
     * 初始化硬件
     */
    if (prtcfuncs->RTC_pfuncInit) {
        prtcfuncs->RTC_pfuncInit();
    }

    return  (ERROR_NONE);
}


3RTC時間同步

RTC設備創建完成並安裝完設備驅動程序後,即可調用API_RtcToSys內核函數進行時間同步。該函數調用API_RtcGet函數,打開已安裝的RTC設備,通過ioctl命令控制,獲取當前RTC時間,最終調用lib_clock_settime,將獲取的時間設置為系統時間。具體實現過程如程序清單 3.1所示。

程序清單 3.1系統時間同步

LW_API  INT  API_RtcToSys (VOID)
{
    struct timespec   tv;
    
    /*
     * 獲取當前RTC時間
     */
    if (API_RtcGet(&tv.tv_sec) < 0) {
        return  (PX_ERROR);
    }
    tv.tv_nsec = 0;

    /*
     * 將獲取的時間設置為系統時間
     */
    return  (lib_clock_settime(CLOCK_REALTIME, &tv));
}



SylixOS中RTC設備驅動