软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!
1概述趁热打铁,我们刚刚在上一节课掌握了I2C利用ZYNQ I2C总线控制器读写EEPROM,本节课继续利用I2C总线控制器实现对RTC时钟芯片,DS1307的读写访问。有了前面的基础,这节课内容学习起来很轻松。 本文实验目的: 1:了解DS1307/1337的寄存器,以及通过I2C读写DS1307/1337的时序 2:掌握vitis-sdk下对DS1307/1337的编程,设置时间和读取时间 2系统框图
3RTC时钟DS1307/1337介绍DS1307/1337是低功耗、两线制串行读写接口、日历和时钟数据按BCD码存取的时钟/日历芯片。它提供秒、分、小时、星期、日期、月和年等时钟日历数据。另外它还集成了如下几点功能: (1)56 字节掉电时电池保持的NV SRAM 数据存储器 (2)可编程的方波信号输出 (3)掉电检测和自动切换电池供电模式
S1307/1337的寄存器地址空间如下,我们的代码也就是读写一下地址空间。
地址空间中详细的参数定义如下表
写时序如下:
写时序很容易理解,和我们前面写EEPROM一样,先发送器件地址为1101000,再发送寄存器的地址,之后是连续写数据。
读时序如下:
这里读的时序有点没描述清楚,读时序前首先还要进行一次写寄存器起始地址的设置。比如代码,首先是写器件地址,并且指定标记读寄存器的其实寄存器地址为0x00,然后从标记的0x00读7个字节的数据。后面时序分析的时候再配合以上读写时序图介绍。
4硬件原理图
如上图所示,I2C总线上挂了2个外设,分别是EEPROM 24LC02以及RTC S1337
5搭建SOC系统工程详细的搭建过程这里不再重复,对于初学读者如果还不清楚如何创建SOC工程的,请学习“01Vitis Soc开发入门”这篇文章。 5.1SOC系统工程
ZYNQ IP中设置I2C0
5.2编译并导出平台文件以下步骤简写,有不清楚的看第一篇文章。 1:单击Block文件à右键àGenerate the Output ProductsàGlobalàGenerate。 2:单击Block文件à右键à Create a HDL wrapper(生成HDL顶层文件)àLet vivado manager wrapper and auto-update(自动更新)。 3:添加配套工程路径下uisrc/04_pin/fpga_pin.xdc约束文件 4:生成Bit文件。 5:导出到硬件: FileàExport HardwareàInclude bitstream 6:导出完成后,对应工程路径的soc_hw路径下有硬件平台文件:system_wrapper.xsa的文件。根据硬件平台文件system_wrapper.xsa来创建需要Platform平台。
6搭建Vitis-sdk工程创建soc_base sdk platform和APP工程的过程不再重复,如果不清楚请参考本章节第一个demo。 6.1创建SDK Platform工程
右击soc_base编译,编译的时间可能会有点长 6.2创建APP工程
7程序分析上一实验中中我们详细介绍过I2C的读写以及I2C的中断,本文中不再重复描述。以下分析只针对DS1337本身。 7.1ds1337.c程序- #include "ds1337.h"
- u8 DS1337_decToBcd(u8 val)
- {
- return ( (val/10*16) + (val%10) );
- }
- //Convert binary coded decimal to normal decimal numbers
- u8 DS1337_bcdToDec(u8 val)
- {
- return ( (val/16*10) + (val%16) );
- }
- void DS1337_startClock(void) // set the ClockHalt bit low to start the rtc
- {
- //point to register
- DS1337_RBUF.reg_addr[0]=0x00;// Register 0x00 holds the oscillator start/stop bit
- i2cps_rd8(&I2cInst0,&DS1337_RBUF,1,DS1337_I2C_ADDRESS);
- rtc.second =DS1337_RBUF.reg_buf[0] & 0x7f;// save actual seconds and AND sec with bit 7 (sart/stop bit) = clock started
- //write register
- DS1337_WBUF.reg_addr[0]=0x00;
- DS1337_WBUF.reg_buf[0]=rtc.second;
- i2cps_wr8(&I2cInst0,&DS1337_WBUF,1,DS1337_I2C_ADDRESS);// write seconds back and start the clock
- }
- void DS1337_stopClock(void) // set the ClockHalt bit high to stop the rtc
- {
- //read register
- DS1337_RBUF.reg_addr[0]=0x00;// Register 0x00 holds the oscillator start/stop bit
- i2cps_rd8(&I2cInst0,&DS1337_RBUF,1,DS1337_I2C_ADDRESS);
- rtc.second =DS1337_RBUF.reg_buf[0] | 0x80;// save actual seconds and AND sec with bit 7 (sart/stop bit) = clock started
- //write register
- DS1337_WBUF.reg_addr[0]=0x00;
- DS1337_WBUF.reg_buf[0]=rtc.second;
- i2cps_wr8(&I2cInst0,&DS1337_WBUF,1,DS1337_I2C_ADDRESS);// write seconds back and stop the clock
- }
- /****************************************************************/
- /*Function: Read time and date from RTC */
- void DS1337_getTime()
- {
- //point to register
- DS1337_RBUF.reg_addr[0]=0x00;// Register 0x00 holds the oscillator start/stop bit
- i2cps_rd8(&I2cInst0,&DS1337_RBUF,7,DS1337_I2C_ADDRESS);
- // A few of these need masks because certain bits are control bits
- rtc.second = DS1337_bcdToDec(DS1337_RBUF.reg_buf[0] & 0x7f);
- rtc.minute = DS1337_bcdToDec(DS1337_RBUF.reg_buf[1]);
- rtc.hour = DS1337_bcdToDec(DS1337_RBUF.reg_buf[2] & 0x3f);// Need to change this if 12 hour am/pm
- rtc.dayOfWeek = DS1337_bcdToDec(DS1337_RBUF.reg_buf[3]);
- rtc.dayOfMonth = DS1337_bcdToDec(DS1337_RBUF.reg_buf[4]);
- rtc.month = DS1337_bcdToDec(DS1337_RBUF.reg_buf[5]);
- rtc.year = DS1337_bcdToDec(DS1337_RBUF.reg_buf[6]);
- }
- /*******************************************************************/
- /*Function: Write the time that includes the date to the RTC chip */
- void DS1337_setTime()
- {
- DS1337_WBUF.reg_addr[0]=0x00;
- DS1337_WBUF.reg_buf[0]=DS1337_decToBcd(rtc.second);// 0 to bit 7 starts the clock
- DS1337_WBUF.reg_buf[1]=DS1337_decToBcd(rtc.minute);
- DS1337_WBUF.reg_buf[2]=DS1337_decToBcd(rtc.hour);// If you want 12 hour am/pm you need to set bit 6
- DS1337_WBUF.reg_buf[3]=DS1337_decToBcd(rtc.dayOfWeek);
- DS1337_WBUF.reg_buf[4]=DS1337_decToBcd(rtc.dayOfMonth);
- DS1337_WBUF.reg_buf[5]=DS1337_decToBcd(rtc.month);
- DS1337_WBUF.reg_buf[6]=DS1337_decToBcd(rtc.year);
- i2cps_wr8(&I2cInst0,&DS1337_WBUF,7,DS1337_I2C_ADDRESS);
- }
- void DS1337_fillByHMS(u8 _hour, u8 _minute, u8 _second)
- {
- // assign variables
- rtc.hour = _hour;
- rtc.minute = _minute;
- rtc.second = _second;
- }
- void DS1337_fillByYMD(u16 _year, u8 _month, u8 _day)
- {
- rtc.year = _year-2000;
- rtc.month = _month;
- rtc.dayOfMonth = _day;
- }
- void DS1337_fillDayOfWeek(u8 _dow)
- {
- rtc.dayOfWeek = _dow;
- }
复制代码
DS1307_startClock 函数启动RTC时钟芯片运行 DS1307_stopClock 函数启动RTC停止芯片运行 DS1307_getTime 函数读取RTC时钟芯片时间 DS1307_setTime 函数设置RTC时钟芯片时间 DS1307_fillByHMS 初始化年时分秒时间变量 DS1307_fillByYMD 初始化年月日时间变量 DS1307_fillDayOfWeek 初始化周几时间变量 DS1307_decToBcd 十进制转BCD码 DS1307_bcdToDec BCD码转10进制 写时序很容易理解,和我们前面写EEPROM一样,先发送器件地址为1101000,再发送寄存器的地址,之后是连续写数据。
写时序如下:
设置时间的函数如下:
- /*******************************************************************/
- /*Function: Write the time that includes the date to the RTC chip */
- void DS1337_setTime()
- {
- DS1337_WBUF.reg_addr[0]=0x00;
- DS1337_WBUF.reg_buf[0]=DS1337_decToBcd(rtc.second);// 0 to bit 7 starts the clock
- DS1337_WBUF.reg_buf[1]=DS1337_decToBcd(rtc.minute);
- DS1337_WBUF.reg_buf[2]=DS1337_decToBcd(rtc.hour);// If you want 12 hour am/pm you need to set bit 6
- DS1337_WBUF.reg_buf[3]=DS1337_decToBcd(rtc.dayOfWeek);
- DS1337_WBUF.reg_buf[4]=DS1337_decToBcd(rtc.dayOfMonth);
- DS1337_WBUF.reg_buf[5]=DS1337_decToBcd(rtc.month);
- DS1337_WBUF.reg_buf[6]=DS1337_decToBcd(rtc.year);
- i2cps_wr8(&I2cInst0,&DS1337_WBUF,7,DS1337_I2C_ADDRESS);
- }<img width="15" _height="15" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" border="0" alt="">
复制代码
这里读的时序有点没描述清楚,读时序前首先还要进行一次写寄存器起始地址的设置。比如代码,首先是写器件地址,并且指定标记读寄存器的其实寄存器地址为0x00,然后从标记的0x00读7个字节的数据。
读时序如下:
获取实现的函数,如下:
- /*Function: Read time and date from RTC */
- void DS1337_getTime()
- {
- //point to register
- DS1337_RBUF.reg_addr[0]=0x00;// Register 0x00 holds the oscillator start/stop bit
- i2cps_rd8(&I2cInst0,&DS1337_RBUF,7,DS1337_I2C_ADDRESS);
- // A few of these need masks because certain bits are control bits
- rtc.second = DS1337_bcdToDec(DS1337_RBUF.reg_buf[0] & 0x7f);
- rtc.minute = DS1337_bcdToDec(DS1337_RBUF.reg_buf[1]);
- rtc.hour = DS1337_bcdToDec(DS1337_RBUF.reg_buf[2] & 0x3f);// Need to change this if 12 hour am/pm
- rtc.dayOfWeek = DS1337_bcdToDec(DS1337_RBUF.reg_buf[3]);
- rtc.dayOfMonth = DS1337_bcdToDec(DS1337_RBUF.reg_buf[4]);
- rtc.month = DS1337_bcdToDec(DS1337_RBUF.reg_buf[5]);
- rtc.year = DS1337_bcdToDec(DS1337_RBUF.reg_buf[6]);
- }
复制代码
7.2ds1337_test.c- /********************MILIANKE**************************
- *Company:Liyang Milian Electronic Technology Co., Ltd
- *Technical forum:www.uisrc.com
- *Taobao: https://milianke.taobao.com
- *Create Date: 2020/12/01
- *Module Name:iic_rtc_test
- *Copyright: Copyright (c) milianke
- *Revision: 1.1
- *Description:
- ****************************************************/
- #include "I2cPs_Polled.h"
- #include "ds1337.h"
- void setup()
- {
- DS1337_startClock();
- DS1337_fillByYMD(2019,5,10);//Jan 19,2013
- DS1337_fillByHMS(11,20,30);//15:28 30"
- DS1337_fillDayOfWeek(FRI);//Saturday
- DS1337_setTime();//write time to the RTC chip
- }
- int main(void)
- {
- I2cPs_init(&Iic,IIC_DEVICE_ID);
- setup();
- while(1)
- {
- DS1337_getTime();
- xil_printf("%d:%d:%d-%d-%d-%d-",rtc.hour,rtc.minute,rtc.second,rtc.month,rtc.dayOfMonth,rtc.year+2000);
- switch (rtc.dayOfWeek)// Friendly printout the weekday
- {
- case MON:
- xil_printf("MON");
- break;
- case TUE:
- xil_printf("TUE");
- break;
- case WED:
- xil_printf("WED");
- break;
- case THU:
- xil_printf("THU");
- break;
- case FRI:
- xil_printf("FRI");
- break;
- case SAT:
- xil_printf("SAT");
- break;
- case SUN:
- xil_printf("SUN");
- break;
- }
- xil_printf("\r\n");
- sleep(1);
- }
- return 0;
- }
复制代码
在ds1337_test.c文件中,初始化I2C控制器,设置RTC时钟芯片的时间,之后每间隔1S读取一次时间值,并且通过串口打印。 8方案演示
8.1硬件准备实验需要用到 JTAG 下载器、USB 转串口外设,另外需要把核心板上的 2P 模式开关设置到 JTAG 模式,即ON ON(注意新版本的 MLK_H3_CZ08-7100-MZ7100FC),支持 JTAG 模式,对于老版本的核心板,JTAG 调试的时候 一定要拔掉 TF 卡,并且设置模式开关为 OFF OFF)
8.2实验结果对于DS1337的测试采用了I2C Polled模式
|