在bspam335x工程中有RTC芯片DS1337的驱动,因此这里选择在此驱动上修改,为了更好的理解S35390A的使用,这里还参考了其Linux下的驱动,在../driver/rtc里面有文件rtc-ds1307.c、rtc-s35390a.c分别对应DS1337的驱动和S35390A的驱动。
DS1337和S35390A的异同。
相同点:
均使用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+指令,实际只需要用到0x30、0x31、0x32。
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开始传送接受”。读到这里隐隐感觉到了问题所在,在仔细核对时序图时看到,当传输指令时是MSB~LSB,在传输数据时确变成了LSB~MSB。不仅仅是实时数据寄存器,状态寄存器、INT寄存器均是如此。
在对实时数据读取写入的地方进行加入bit反转函数,重新编译下载后,可以正常读取RTC数据并设置。但仍存在一定问题:
上电后RTC时钟芯片为0且不正常行走,在设置后才正常行走,
当设置年月日数据时重复上述现象。
测试记录:
所有月份设置均无问题,日期设置也无问题,当年的时间超过2000年,时间归零。对年的问题主要是因为s35390a芯片内没有年的溢出标志,在Linux下的驱动中默认不支持2000以内的时间设置,将其更改与Linux下的一致即可。
文章评论(0条评论)
登录后参与讨论