原创 SylixOS下RTC芯片驱动编写

2015-10-21 21:21 1460 16 16 分类: MCU/ 嵌入式 文集: SylixOS BSP开发

bspam335x工程中有RTC芯片DS1337的驱动,因此这里选择在此驱动上修改,为了更好的理解S35390A的使用,这里还参考了其Linux下的驱动,在../driver/rtc里面有文件rtc-ds1307.crtc-s35390a.c分别对应DS1337的驱动和S35390A的驱动。

DS1337S35390A的异同。

相同点:

均使用I2C接口通信;

芯片均使用BCD码表示。

不同点:

I2C通信时序不同;

DS1337的通信时序为

器件地址字节

寄存器地址

数据

数据

S35290A的通信时序为

地址字节

数据

数据

数据

S35290A的器件地址和寄存器地址均在启示标志后一个字节里完成。

数据数据顺序不同;

对时钟数据进行读取设置时,均是7bytes数据连续读写,但数据流的函数正好相反,通过宏定义可以看出:

 

DS1337                                                                         S35290A
#define RTC_SEC_REG_ADDR  0x0    #define S35390A_BYTE_YEAR  0
#define RTC_MIN_REG_ADDR  0x1    #define S35390A_BYTE_MONTH  1
#define RTC_HR_REG_ADDR   0x2    #define S35390A_BYTE_DAY   2
#define RTC_DAY_REG_ADDR  0x3    #define S35390A_BYTE_WDAY  3
#define RTC_DATE_REG_ADDR  0x4    #define S35390A_BYTE_HOURS 4
#define RTC_MON_REG_ADDR  0x5    #define S35390A_BYTE_MINS  5
#define RTC_YR_REG_ADDR   0x6    #define S35390A_BYTE_SECS  6

分析DS1337驱动中I2C的通信特点

首先驱动中实现了三个基本DS1337寄存器读写函数,分别是__ds1337RegWrite__ds1337TimeGet__ds1337TimeSet。在实现上述函数使用了不同的I2C总线函数,根据驱动中的代码可以了解两者区别:

 

API_I2cDeviceTransfer  /*使用方式复杂,但功能灵活,可以传输多个消息  */
API_I2cDeviceMasterSend /*使用方式简单,但功能固定,一次只能传输一个消息*/

因此理论上__ds1337RegWrite__ds1337TimeGet__ds1337TimeSet都可以使用一个I2C总线操作函数实现,这里更倾向于使用一个而不是两个不同的函数实现S35390A的驱动,参考其Linux驱动代码可以了解Linux下仅仅使用了一个函数。这里仅仅使用API_I2cDeviceTransfer函数的另外一个原因是因为S35390A的通信时序决定的。

RTC驱动中主要是实现三个函数,分别是__s35390aRtcInit__s35390aRtcSetTime__s35390aRtcGetTime三个函数,分别实现DS1337芯片的初始化、时间设置和时间读取。三个函数分别调用了三个基本的DS1337寄存器读写函即__ds1337RegWrite__ds1337TimeSet__ds1337TimeGet实现。至此基本明确了DS1337驱动的实现方式。

在编写S35390A驱动前,我们还需要对I2C通信方式和设置进行深入的了解,需要了解的内容如下:

I2C地址的设置

DS1337中的地址分为设备地址和寄存器地址,其中设备地址是固定的。在*RtcInit函数中使用API_I2cDeviceCreate函数创建设备时已经指定I2C的设备地址,但S35390A中由于设备地址与寄存器地址组合成一个字节,不能够使用这种方式。

I2C收发函数设置

使用API_I2cDeviceTransfer函数进行I2C通信需要先设置I2C消息,由于DS1337芯片在读取之前需要设置读寄存器指针,因此对DS1337的操作需要两个消息,在函数

__ds1337TimeGet设置如下:

 

UINT8             ucRegAddr = 0;
LW_I2C_MESSAGE  i2cMsgs[] = {
        {     /*  向DS1337写入读取数据的 reg 指针  */
            .I2CMSG_usAddr   = pI2cDev->I2CDEV_usAddr,
            .I2CMSG_usFlag   = 0,
            .I2CMSG_usLen   = 1,
            .I2CMSG_pucBuffer = &ucRegAddr,
        },
        {     /*  读取DS1337的数据            */
            .I2CMSG_usAddr   = pI2cDev->I2CDEV_usAddr,
            .I2CMSG_usFlag   = LW_I2C_M_RD,
            .I2CMSG_usLen    = 7,
            .I2CMSG_pucBuffer = aucValue,
        },
    };

S35390A的通信格式如下:

0

1

1

0

X

X

X

R/W

ACK

装置码

指令

读写

确认

在驱动编写中,对S35390A的地址填写为0x30+指令,实际只需要用到0x300x310x32

DS1337驱动函数编写

根据Linux下的驱动代码,实现reg读写函数,

随后实现函数__s35390aRtcInit功能,参考Linux下驱动函数s35390a_probe,根据函数功能主要是实现S35390A的复位和测试禁止。

完成编译后,注册内核模块,提示如下错误,使用默认内核模块工程代码同样提示此错误,应该是与工程的文件系统有关。

 

[root@sylixos_station:/lib/modules]# ls
rtc_s35390a.ko
[root@sylixos_station:/lib/modules]# modulereg rtc_s35390a.ko
elfModuleMemoryInit() error: alloc vm-memory error!
in thread "t_tshell" context.
load sub library error Not enough core!
can not register module, error : Can not find module
[root@sylixos_station:/lib/modules]#

加入内核后,应该能够读写,但数值错误。调试接口使用__DebugFormat函数。函数使用格式如:_DebugFormat(__ERRORMESSAGE_LEVEL, "0x%x", aucBuffer[6]);此接口比printk等要方便很多,在SylixOS中的驱动也是使用这种方法调试。

 

注:很奇怪SylixOS开发团队为什么没有特别声明这一点。

 

通过仔细阅读s35390a数据手册,注意到在“寄存器构成/实时数据寄存器”中提到“在通过实时数据存取指令进行读出/写入的情况下,从年1位栏的LSB开始传送接受”。读到这里隐隐感觉到了问题所在,在仔细核对时序图时看到,当传输指令时是MSBLSB,在传输数据时确变成了LSBMSB。不仅仅是实时数据寄存器,状态寄存器、INT寄存器均是如此。

在对实时数据读取写入的地方进行加入bit反转函数,重新编译下载后,可以正常读取RTC数据并设置。但仍存在一定问题:

上电后RTC时钟芯片为0且不正常行走,在设置后才正常行走,

当设置年月日数据时重复上述现象。

测试记录:

所有月份设置均无问题,日期设置也无问题,当年的时间超过2000年,时间归零。对年的问题主要是因为s35390a芯片内没有年的溢出标志,在Linux下的驱动中默认不支持2000以内的时间设置,将其更改与Linux下的一致即可。

文章评论0条评论)

登录后参与讨论
我要评论
0
16
关闭 站长推荐上一条 /2 下一条