SylixOS中RTC设备驱动

来源:互联网 发布:爱奇艺网络剧排行榜 编辑:程序博客网 时间:2024/06/07 15:15

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));}

原创粉丝点击