SylixOS中RTC設備驅動
1、概述
本文檔基於SylixOS-EVB-i.MX6Q驗證平臺,介紹SylixOS中RTC設備驅動實現過程,可作為在SylixOS集成開發環境下進行字符設備驅動開發的參考。
2、RTC設備驅動
2.1硬件原理
實時時鐘(RTC)的主要功能是在系統掉電的情況下,利用備用電源使時鐘繼續運行,保證不會丟失時間信息。
i.MX6Q驗證平臺上使用的是外置實時時鐘集成電路ISL1208。硬件接線如圖 2.1所示。
圖 2.1 RTC硬件接線
圖中,X1和X2為內部反向放大器的輸入和輸出引腳,要求外置一個
2.2驅動實現
2.2.1安裝RTC設備驅動程序
參照上一章節字符設備驅動模型,調用API_RtcDrvInstall內核函數,註冊RTC設備驅動程序,包括RTC設備的創建、刪除、打開、關閉、讀寫及
程序清單 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 };
1)isl1208RtcInit——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(); } } }
2)isl1208RtcSetTime——RTC時間設置
RTC的時間設置過程:
將待設置時間進行轉碼並填入數據buf;
封裝I2C總線傳輸控制消息,消息中包含RTC從設備地址、消息長度、傳輸控制標誌(讀/寫)、數據buf;
發送I2C控制消息,控制RTC設備寫使能;
發送I2C控制消息,填充RTC對應時間設置寄存器;
發送I2C控制消息,控制RTC設備寫失能。
RTC時間設置過程如程序清單 2.4所示,其中isl1208ReadReg和isl1208SetRegs即為封裝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); }
3)isl1208RtcGetTime——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); }
3、RTC時間同步
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設備驅動