[X]关闭
0

S02-CH12 I2C读写 RTC时钟实验

摘要: 软件版本:VIVADO2017.4操作系统:WIN10 64bit硬件平台:适用米联客 ZYNQ系列开发板米联客(MSXBO)论坛:www.osrc.cn答疑解惑专栏开通,欢迎大家给我提问!!12.1 概述 趁热打铁,我们刚刚在上一节课掌握了I2C利用ZY ...

软件版本:VIVADO2017.4

操作系统:WIN10 64bit

硬件平台:适用米联客 ZYNQ系列开发板

米联客(MSXBO)论坛:www.osrc.cn答疑解惑专栏开通,欢迎大家给我提问!!

12.1 概述

      趁热打铁,我们刚刚在上一节课掌握了I2C利用ZYNQ I2C总线控制器读写EEPROM,本节课继续利用I2C总线控制器实现对RTC时钟芯片,DS1307的读写访问。有了前面的基础,这节课内容学习起来很轻松。

12.2 RTC时钟DS1307介绍

      DS1307是低功耗、两线制串行读写接口、日历和时钟数据按BCD码存取的时钟/日历芯片。它提供秒、分、小时、星期、日期、月和年等时钟日历数据。另外它还集成了如下几点功能:

(1)56 字节掉电时电池保持的NV SRAM 数据存储器

(2)可编程的方波信号输出

(3)掉电检测和自动切换电池供电模式

S1307的寄存器地址空间如下,我们的代码也就是读写一下地址空间。

地址空间中详细的参数定义如下表

写时序如下:

写时序很容易理解,和我们前面写EEPROM一样,先发送器件地址为1101000,再发送寄存器的地址,之后是连续写数据。

读时序如下:

      这里读的时序有点没描述清楚,读时序前首先还要进行一次写寄存器起始地址的设置。比如代码,首先是写器件地址,并且指定标记读寄存器的其实寄存器地址为0x00,然后从标记的0x00读7个字节的数据。后面时序分析的时候再配合以上读写时序图介绍。

12.3 FPGA BD工程

      这节课的FPGA  BD工程和上一节课是一样的,我们依然把关键部分描述下。

      做这个实验必须勾选支持I2C控制器,通过EMIO的方式引出I2C总线。RTC模块连线就可以完成实验。对于初学者需要注意,EMIO是FPGA的PIN脚因此需要添加XDC文件约束FPGA PIN脚。

 

      另外为了完本课程实验,需要选择购买RTC模块。对于MZ7XA-7010(mini)/MZ7XA-7020/MZ7XB-7020开发板具有板载的IO扩展,只要正确和EEPROM模块对接就能完成此实验。11.3 I2C Polled方式读写EEPROM

12.4 I2C Polled方式读写RTC时钟芯片

12.4.1 I2c 控制器

PS支持两个具有以下主要功能的I2C设备:

I2C总线规范版本2

支持16字节FIFO

可编程的正常和快速总线数据速率

主模式

 -写转移

 -读取转移

 -扩展地址支持

-支持缓慢处理器服务的HOLD

-支持中断

从模式

12.4.2 I2cPs_Polled.c

我们米联客(MSXBO)编写了两种读写EEPROM的方式,并且封装成子函数方便用户调用。我们先看代码。

include "I2cPs_Polled.h"

#include "sleep.h"

int I2cPs_init(XIicPs *I2C_Ptr,u16 DeviceId)

{

int Status;

XIicPs_Config *Config;


/*

 * Initialize the IIC driver so that it's ready to use

 * Look up the configuration in the config table, then initialize it.

 */

Config = XIicPs_LookupConfig(DeviceId);

if (NULL == Config) {

return XST_FAILURE;

}


Status = XIicPs_CfgInitialize(I2C_Ptr, Config, Config->BaseAddress);

if (Status != XST_SUCCESS) {

return XST_FAILURE;

}


/*

 * Perform a self-test to ensure that the hardware was built correctly.

 */

Status = XIicPs_SelfTest(I2C_Ptr);

if (Status != XST_SUCCESS) {

return XST_FAILURE;

}


/*

 * Set the IIC serial clock rate.

 */

XIicPs_SetSClk(I2C_Ptr, IIC_SCLK_RATE);


return XST_SUCCESS;

}



void I2cPs_write(XIicPs *I2C_Ptr, u8 *MsgPtr, s32 ByteCount, u16 SlaveAddr)

{


XIicPs_MasterSendPolled(I2C_Ptr, MsgPtr, ByteCount, SlaveAddr);


while (XIicPs_BusIsBusy(I2C_Ptr)) {

}

usleep(2000);


}



void I2cPs_read(XIicPs *I2C_Ptr, u8 *MsgPtr, s32 ByteCount, u16 SlaveAddr)

{

XIicPs_MasterRecvPolled(I2C_Ptr, MsgPtr, ByteCount, SlaveAddr);

while (XIicPs_BusIsBusy(I2C_Ptr)) {

}

usleep(2000);


}


I2cPs_init

函数负责初始化I2C控制器,关键是初始化了I2C控制的速度XIicPs_SetSClk(I2C_Ptr, IIC_SCLK_RATE)。

I2cPs_write 

函数顾名思义是实现I2C的写数据

I2cPs_read  

函数顾名思义是实现I2C的读数据

12.4.3 RTC_DS1307.c

#include "I2cPs_Polled.h"

#include "RTC_DS1307.h"



u8 DS1307_decToBcd(u8 val)

{

return ( (val/10*16) + (val%10) );

}


//Convert binary coded decimal to normal decimal numbers

u8 DS1307_bcdToDec(u8 val)

{

return ( (val/16*10) + (val%16) );

}


void DS1307_startClock(void)        // set the ClockHalt bit low to start the rtc

{

  //point to register

ds1307_wbuf[0]=0x00;// Register 0x00 holds the oscillator start/stop bit

I2cPs_write(&Iic,ds1307_wbuf, 1, DS1307_I2C_ADDRESS);


  //read register

I2cPs_read (&Iic,ds1307_rbuf, 1, DS1307_I2C_ADDRESS);

rtc.second =ds1307_rbuf[0] & 0x7f;// save actual seconds and AND sec with bit 7 (sart/stop bit) = clock started


  //write register

ds1307_wbuf[0]=0x00;

ds1307_wbuf[1]=rtc.second;

I2cPs_write(&Iic,ds1307_wbuf, 2, DS1307_I2C_ADDRESS);// write seconds back and start the clock

}


void DS1307_stopClock(void)         // set the ClockHalt bit high to stop the rtc

{

//point to register

ds1307_wbuf[0]=0x00;// Register 0x00 holds the oscillator start/stop bit

I2cPs_write(&Iic,ds1307_wbuf, 1, DS1307_I2C_ADDRESS);


//read register

I2cPs_read (&Iic,ds1307_rbuf, 1, DS1307_I2C_ADDRESS);

rtc.second =ds1307_rbuf[0] | 0x80;// save actual seconds and AND sec with bit 7 (sart/stop bit) = clock started


//write register

ds1307_wbuf[0]=0x00;

ds1307_wbuf[1]=rtc.second;

I2cPs_write(&Iic,ds1307_wbuf, 2, DS1307_I2C_ADDRESS);// write seconds back and start the clock

}


/****************************************************************/

/*Function: Read time and date from RTC */

void DS1307_getTime()

{

//point to register

ds1307_wbuf[0]=0x00;// Register 0x00 holds the oscillator start/stop bit

I2cPs_write(&Iic,ds1307_wbuf, 1, DS1307_I2C_ADDRESS);

//read register

I2cPs_read (&Iic,ds1307_rbuf, 7, DS1307_I2C_ADDRESS);

// A few of these need masks because certain bits are control bits


rtc.second    = DS1307_bcdToDec(ds1307_rbuf[0] & 0x7f);

rtc.minute    = DS1307_bcdToDec(ds1307_rbuf[1]);

rtc.hour    = DS1307_bcdToDec(ds1307_rbuf[2] & 0x3f);// Need to change this if 12 hour am/pm

rtc.dayOfWeek  = DS1307_bcdToDec(ds1307_rbuf[3]);

rtc.dayOfMonth = DS1307_bcdToDec(ds1307_rbuf[4]);

rtc.month      = DS1307_bcdToDec(ds1307_rbuf[5]);

rtc.year    = DS1307_bcdToDec(ds1307_rbuf[6]);

}


/*******************************************************************/

/*Function: Write the time that includes the date to the RTC chip */

void DS1307_setTime()

{

ds1307_wbuf[0]=0x00;

ds1307_wbuf[1]=DS1307_decToBcd(rtc.second);// 0 to bit 7 starts the clock

ds1307_wbuf[2]=DS1307_decToBcd(rtc.minute);

ds1307_wbuf[3]=DS1307_decToBcd(rtc.hour);// If you want 12 hour am/pm you need to set bit 6

ds1307_wbuf[4]=DS1307_decToBcd(rtc.dayOfWeek);

ds1307_wbuf[5]=DS1307_decToBcd(rtc.dayOfMonth);

ds1307_wbuf[6]=DS1307_decToBcd(rtc.month);

ds1307_wbuf[7]=DS1307_decToBcd(rtc.year);


I2cPs_write(&Iic,ds1307_wbuf, 8, DS1307_I2C_ADDRESS);

}


void DS1307_fillByHMS(u8 _hour, u8 _minute, u8 _second)

{

// assign variables

rtc.hour = _hour;

rtc.minute = _minute;

rtc.second = _second;

}

void DS1307_fillByYMD(u16 _year, u8 _month, u8 _day)

{


rtc.year = _year-2000;

rtc.month = _month;

rtc.dayOfMonth = _day;

}

void DS1307_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,再发送寄存器的地址,之后是连续写数据。

     写时序如下:

设置时间的函数如下:

void DS1307_setTime()

{

ds1307_wbuf[0]=0x00;

ds1307_wbuf[1]=DS1307_decToBcd(rtc.second);// 0 to bit 7 starts the clock

ds1307_wbuf[2]=DS1307_decToBcd(rtc.minute);

ds1307_wbuf[3]=DS1307_decToBcd(rtc.hour);// If you want 12 hour am/pm you need to set bit 6

ds1307_wbuf[4]=DS1307_decToBcd(rtc.dayOfWeek);

ds1307_wbuf[5]=DS1307_decToBcd(rtc.dayOfMonth);

ds1307_wbuf[6]=DS1307_decToBcd(rtc.month);

ds1307_wbuf[7]=DS1307_decToBcd(rtc.year);


I2cPs_write(&Iic,ds1307_wbuf, 8, DS1307_I2C_ADDRESS);

}

     这里读的时序有点没描述清楚,读时序前首先还要进行一次写寄存器起始地址的设置。比如代码,首先是写器件地址,并且指定标记读寄存器的其实寄存器地址为0x00,然后从标记的0x00读7个字节的数据。

     读时序如下:


获取实现的函数,如下:

/****************************************************************/

/*Function: Read time and date from RTC */

void DS1307_getTime()

{

//point to register

ds1307_wbuf[0]=0x00;// Register 0x00 holds the oscillator start/stop bit

I2cPs_write(&Iic,ds1307_wbuf, 1, DS1307_I2C_ADDRESS);

//read register

I2cPs_read (&Iic,ds1307_rbuf, 7, DS1307_I2C_ADDRESS);

// A few of these need masks because certain bits are control bits


rtc.second    = DS1307_bcdToDec(ds1307_rbuf[0] & 0x7f);

rtc.minute    = DS1307_bcdToDec(ds1307_rbuf[1]);

rtc.hour    = DS1307_bcdToDec(ds1307_rbuf[2] & 0x3f);// Need to change this if 12 hour am/pm

rtc.dayOfWeek  = DS1307_bcdToDec(ds1307_rbuf[3]);

rtc.dayOfMonth = DS1307_bcdToDec(ds1307_rbuf[4]);

rtc.month      = DS1307_bcdToDec(ds1307_rbuf[5]);

rtc.year    = DS1307_bcdToDec(ds1307_rbuf[6]);

}

12.4.4 mian.c


#include "I2cPs_Polled.h"

#include "RTC_DS1307.h"


void setup()

{

DS1307_startClock();

DS1307_fillByYMD(2019,5,10);//Jan 19,2013

DS1307_fillByHMS(11,20,30);//15:28 30"

DS1307_fillDayOfWeek(FRI);//Saturday

DS1307_setTime();//write time to the RTC chip


}


int main(void)

{


I2cPs_init(&Iic,IIC_DEVICE_ID);

setup();

while(1)

{


DS1307_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;

}



       在mian.c文件中,初始化I2C控制器,设置RTC时钟芯片的时间,之后每间隔1S读取一次时间值,并且通过串口打印。

12.5 硬件连线

通过外扩的FPGA GPIO 连接 RTC模块,MZ7XA、MZ7XB自带此IO

对于没有这组IO的开发板通过FEP转NEP转接卡实现,此转接卡需要单独购买

12.6 测试结果


路过

雷人

握手

鲜花

鸡蛋

最新评论

本文作者
2019-9-6 19:23
  • 7
    粉丝
  • 2309
    阅读
  • 0
    回复

关注米联客

扫描关注,了解最新资讯

联系人:汤经理
电话:0519-80699907
EMAIL:270682667@qq.com
地址:常州溧阳市天目云谷3号楼北楼201B
热门评论
排行榜