热度 16
2015-10-21 21:21
1460 次阅读|
0 个评论
在 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 # ls rtc_s35390a.ko # 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 # 加入内核后,应该能够读写,但数值错误。调试接口使用 __DebugFormat 函数。函数使用格式如: _DebugFormat(__ERRORMESSAGE_LEVEL, "0x%x", aucBuffer ); 此接口比 printk 等要方便很多,在 SylixOS 中的驱动也是使用这种方法调试。 注:很奇怪SylixOS开发团队为什么没有特别声明这一点。 通过仔细阅读 s35390a 数据手册,注意到在“寄存器构成 / 实时数据寄存器”中提到“在通过实时数据存取指令进行读出 / 写入的情况下,从年 1 位栏的 LSB 开始传送接受”。读到这里隐隐感觉到了问题所在,在仔细核对时序图时看到,当传输指令时是 MSB ~ LSB ,在传输数据时确变成了 LSB ~ MSB 。不仅仅是实时数据寄存器,状态寄存器、 INT 寄存器均是如此。 在对实时数据读取写入的地方进行加入 bit 反转函数,重新编译下载后,可以正常读取 RTC 数据并设置。但仍存在一定问题: 上电后 RTC 时钟芯片为 0 且不正常行走,在设置后才正常行走, 当设置年月日数据时重复上述现象。 测试记录: 所有月份设置均无问题,日期设置也无问题,当年的时间超过 2000 年,时间归零。对年的问题主要是因为 s35390a 芯片内没有年的溢出标志,在 Linux 下的驱动中默认不支持 2000 以内的时间设置,将其更改与 Linux 下的一致即可。