[米联客-XILINX-H3_CZ08_7100] FPGA程序设计基础实验连载-22读写I2C接口的RTC时钟芯片

本文主要是介绍[米联客-XILINX-H3_CZ08_7100] FPGA程序设计基础实验连载-22读写I2C接口的RTC时钟芯片,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

软件版本: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概述

2 RTC时钟DS1337介绍

3用户程序设计

3.1用户接口时序

3.2 RTC读写程序设计

3.2.1状态机介绍

3.2.2 RTC读写用户程序源码

4 FPGA工程

5下载演示

5.1硬件连接

5.2运行结果


1概述

本节课继续利用I2C总线控制器实现对RTC时钟芯片,DS1337的读写访问,进一步验证我们设计的i2c控制器的可靠性。有了前面的基础,这节课内容学习起来很轻松。本节课主要是基于我们编写的I2C控制器的应用,侧重点是应用,所以不再给出RTL级别的仿真结果,直接通过控制器访问DS1337芯片。

2 RTC时钟DS1337介绍

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

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

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

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

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

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

写时序如下:

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

读时序如下:

读时序我们采用上图的方式,这种方式比较方便,大家学习I2C的课程,如果和ZYNQ 部分SDK对比会发现SDK里面操作比较麻烦,而且采取的不是这种方式,这是因为SDK里面的I2C控制不能发送Repeated Start位的原因。其实作为FPGA开发者,感觉FPGA编程熟练后,写这种接口代码还是非常方便。

我们这里只针对时、分、秒的读写,而且上电后默认的控制寄存器不需要设置,采用默认参数就可以。

3用户程序设计

3.1用户接口时序

先温习下前面课程内容中关于I2C控制器的功能模块可以接口信号:

IO_sda为I2C双向数据总线

O_scl为I2C时钟

I_wr_cnt写数据字节长度,包含了器件地址,发送I_iic_req前,预设该值

I_rd_cnt读数据字节长度,仅包含读回有效部分,发送I_iic_req前,预设该值

I_wr_data写入的数据

O_rd_data读出的数据,如果是读请求,当O_iic_busy从高变低代表数据读回有效

I_iic_req I2C操作请求,根据I_rd_cnt是否大于0决定是否有读请求

I_iic_mode是否支持随机读写,发送I_iic_req前,预设该值

O_iic_busy总线忙

请求一次I2C传输的控制时序如下:

首先在O_iic_busy=0即I2C总线空闲情况下,设置I_wr_cnt,I_rd_cnt,I_wr_data,并且设置I_iic_req=1,启动I2C传输。当O_iic_busy=1说明I2C控制器开始传输,这时候可以设置I_iic_req=0,结束本次请求,并且等待O_iic_busy=0,当O_iic_busy=0代表本次传

传输结束.如果发送的是读请求(当I_rd_cnt>0),则此时O_rd_data有效可以读走数据。

3.2 RTC读写程序设计

3.2.1状态机介绍

3.2.2 RTC读写用户程序源码
/*******************************rtc_clock_ds1337*********************
--1.本实验目基于米联客I2C控制器,实现对RTC时钟芯片的读写访问
--2.本实验通过串口打印读取的RTC时钟时间值
*********************************************************************/
`timescale 1ns / 1ns//仿真时间刻度/精度module rtc_clock_ds1337(
input  wire I_sysclk_p,
input  wire I_sysclk_n,//系统时钟输入
output wire O_iic_scl, //I2C总线,SCL时钟
inout  wire IO_iic_sda, //I2C总线,SDA数据
output wire O_uart_tx, //UART串行发送总线
output wire O_card_power_en
);
localparam SYSCLKHZ     =  100_000_000; //定义系统时钟
localparam T1000MS_CNT   =  (SYSCLKHZ-1); //定义访问RTC的时间间隔为1000MS
localparam [7:0] RTC_DEV_ADDR =  8'b1101_0000;wire I_clk;
IBUFGDS CLK_U(
.I(I_sysclk_p),
.IB(I_sysclk_n),
.O(I_clk)
);
reg [8 :0]  rst_cnt       = 9'd0;//上电延迟复位
reg [29:0]  t_cnt = 30'd0;//定时计数器
wire t_en = (t_cnt==T1000MS_CNT);//定时使能  wire [23:0] wr_data;//写数据信号
wire [23:0] rd_data;//读数据信号
wire        iic_busy;//I2C总线忙
reg  [7 :0] wr_cnt = 8'd0;//写数据计数器
reg  [7 :0] rd_cnt = 8'd0;//读数据计数器
reg         iic_req = 1'b0;//i2c 控制器请求信号
reg  [2 :0] TS_S   = 3'd0;//状态机
reg  [7 :0] rtc_addr;//RTC的寄存器地址
reg         wr_done = 1'b0; //写RTC初值完成信号//初始化时间的BDC码,12:00:00
wire [7 :0] WSecond = {4'd0,4'd0};//妙
wire [7 :0] WMinute = {4'd0,4'd0};//分
wire [7 :0] WHour   = {4'd1,4'd2};//时
reg  [23:0] rtime   = 24'd0; //用于保存读取的时间,格式为BCD码assign wr_data   = {WHour,WMinute,WSecond};//写数据初值
assign O_card_power_en = 1'b1; //ascii
//**********上电延迟复位***************************/
always@(posedge I_clk) beginif(!rst_cnt[8]) rst_cnt <= rst_cnt + 1'b1;
end//**********500ms定时计数器**********************/
always@(posedge I_clk) beginif(t_cnt == T1000MS_CNT) t_cnt <= 0;else t_cnt <= t_cnt + 1'b1;
end//读写RTC时钟芯片状态机
always@(posedge I_clk) beginif(!rst_cnt[8])begin//复位初始化寄存器rtc_addr <= 8'd0;iic_req  <= 1'b0;wr_done  <= 1'b0;rd_cnt   <= 8'd0; wr_cnt   <= 8'd0;TS_S     <= 2'd0;    endelse begincase(TS_S)0:if(wr_done == 1'b0)begin//上电后,wr_done=0,对RTC时间寄存器初始化,给定初始时间wr_done  <= 1'b1;//设置wr_done=1rtc_addr <= 8'd0;//设置需要访问的寄存器起始地址TS_S     <= 3'd1;//下一个状态endelse begin //已经对RTC芯片初始化完成iic_req  <= 1'b0; //重置 iic_req =0if(t_en)//每间隔1000ms进行一次读操作TS_S     <= 3'd3;//下一个状态,进入读寄时间寄存器状态机end1:if(!iic_busy)begin//当总线非忙,才可以操作I2C控制器iic_req  <= 1'b1;//请求操作I2C控制器rd_cnt   <= 8'd0;//由于本操作是写数据,不需要读数据,读数据寄存器设置0 wr_cnt   <= 8'd5;//需要写入5 BYTES,包括1字节的器件地址,1字节的寄存器起始地址,3字节的BCD时间参数TS_S     <= 3'd2;//下一个状态机end2:if(iic_busy)begin//等待总线忙iic_req  <= 1'b0;//重置 iic_req =0TS_S     <= 3'd3;//下一个状态机   end3:if(!iic_busy)begin//该状态读RTC时间寄存器iic_req  <= 1'b1;//请求操作I2C控制器rtc_addr <= 8'd0;//读RTC寄存器的起始地址wr_cnt   <= 8'd2;//读操作需要些1BYTE器件地址,1BYTE 寄存器起始地址rd_cnt   <= 8'd3;//读取3个时间寄存器 TS_S     <= 3'd4;//下一个状态    end 4:if(iic_busy)begin//等待总线空闲iic_req  <= 1'b0;//重置 iic_req =0TS_S     <= 3'd0;//下一个状态    end   default: TS_S    <= 3'd0;//default状态回到0endcaseend
end
//***********保存从RTC读取到的时间寄存器,时间为BCD格式***********//
always@(posedge I_clk) beginif(!rst_cnt[8])rtime <=0;else if(TS_S == 3)rtime[23: 0] <= rd_data;//读取的时间包括 时:分:秒,BCD格式
end//例化I2C控制模块
uii2c#
(
.WMEN_LEN(5),//最大支持一次写入4BYTE(包含器件地址)
.RMEN_LEN(3),//最大支持一次读出3BYTE
.CLK_DIV(SYSCLKHZ/100000)//100KHZ I2C总线时钟
)
uii2c_inst
(
.I_clk(I_clk),//系统时钟
.I_rstn(rst_cnt[8]),//系统复位
.O_iic_scl(O_iic_scl),//I2C SCL总线时钟
.IO_iic_sda(IO_iic_sda),//I2C SDA数据总线
.I_wr_data({wr_data,rtc_addr,RTC_DEV_ADDR}),//写数据寄存器
.I_wr_cnt(wr_cnt),//需要写的数据BYTES
.O_rd_data(rd_data), //读数据寄存器
.I_rd_cnt(rd_cnt),//需要读的数据BYTES
.I_iic_req(iic_req),//I2C控制器请求
.I_iic_mode(1'b1),//读模式
.O_iic_busy(iic_busy)//I2C控制器忙
//.O_iic_bus_error(iic_bus_error),//总线错误信号标志
//.IO_iic_sda_dg(IO_iic_sda_dg)//debug IO_iic_sda
); //以下完成BCD码赚ASCII码,这样通过串口打印可以方便观察
function signed[7:0] ascii ;   //定义ascii码转换函数,只需要转换BCD数据 input[7:0] bcd; //输入参数  begin                                                    case(bcd)0 :     ascii   =   {8'h30};//ascii 码0  1 :     ascii   =   {8'h31};//ascii 码1      2 :     ascii   =   {8'h32};//ascii 码2  3 :     ascii   =   {8'h33};//ascii 码34 :     ascii   =   {8'h34};//ascii 码4  5 :     ascii   =   {8'h35};//ascii 码5          6 :     ascii   =   {8'h36};//ascii 码6  7 :     ascii   =   {8'h37};//ascii 码7  8 :     ascii   =   {8'h38};//ascii 码8  9 :     ascii   =   {8'h39};//ascii 码9default:ascii   =   {8'h00};    endcase                                
end  endfunction   //例化UART发送模块
uart_tx_block #
(
.TX_BYTES(10),//设置需要发送的字节数
.BAUD_DIV(SYSCLKHZ/115200 -1) //设置串口波特率
)
u_uart_tx_block
(
.I_sysclk(I_clk),//系统时钟输入
.O_uart_tx(O_uart_tx),//UART 串行总线数据发送
//高位,8'h0a,8'h0d,为回车+换行控制字符
.I_uart_tx_buf({8'h0a,8'h0d,ascii(rtime[3:0]),ascii(rtime[7:4]),8'h2d,ascii(rtime[11:8]),ascii(rtime[15:12]),8'h2d,ascii(rtime[19:16]),ascii(rtime[23:20])}),
.I_uart_tx_buf_en(t_en)//t_en也是发送使能
);
endmodule

4 FPGA工程

fpga工程的创建过程不再重复,如有不清楚的请看前面实验

米联客的代码管理规范,在对应的FPGA工程路径下创建uisrc路径,并且创建以下文件夹

01_rtl:放用户编写的rtl代码

02_sim:仿真文件或者工程

03_ip:放使用到的ip文件

04_pin:放fpga的pin脚约束文件或者时序约束文件

05_boot:放编译好的bit或者bin文件(一般为空)

06_doc:放本一些相关文档(一般为空)

5下载演示

5.1硬件连接

(该教程为通用型教程,教程中仅展示一款示例开发板的连接方式,具体连接方式以所购买的开发板型号以及结合配套代码管脚约束为准。)

请确保下载器和开发板已经正确连接,另外需要把核心板上的2P模式开关设置到JTAG模式,即ON ON,并且开发板已经上电。(注意JTAG端子不支持热插拔,而USB接口支持,所以在不通电的情况下接通好JTAG后,再插入USB到电脑,之后再上电,以免造成JTAG IO损坏)

5.2运行结果

使用串口打印软件进行打印

这篇关于[米联客-XILINX-H3_CZ08_7100] FPGA程序设计基础实验连载-22读写I2C接口的RTC时钟芯片的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1122569

相关文章

10. 文件的读写

10.1 文本文件 操作文件三大类: ofstream:写操作ifstream:读操作fstream:读写操作 打开方式解释ios::in为了读文件而打开文件ios::out为了写文件而打开文件,如果当前文件存在则清空当前文件在写入ios::app追加方式写文件ios::trunc如果文件存在先删除,在创建ios::ate打开文件之后令读写位置移至文件尾端ios::binary二进制方式

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close

Java 后端接口入参 - 联合前端VUE 使用AES完成入参出参加密解密

加密效果: 解密后的数据就是正常数据: 后端:使用的是spring-cloud框架,在gateway模块进行操作 <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30.0-jre</version></dependency> 编写一个AES加密

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

C 语言基础之数组

文章目录 什么是数组数组变量的声明多维数组 什么是数组 数组,顾名思义,就是一组数。 假如班上有 30 个同学,让你编程统计每个人的分数,求最高分、最低分、平均分等。如果不知道数组,你只能这样写代码: int ZhangSan_score = 95;int LiSi_score = 90;......int LiuDong_score = 100;int Zhou

STM32(十一):ADC数模转换器实验

AD单通道: 1.RCC开启GPIO和ADC时钟。配置ADCCLK分频器。 2.配置GPIO,把GPIO配置成模拟输入的模式。 3.配置多路开关,把左面通道接入到右面规则组列表里。 4.配置ADC转换器, 包括AD转换器和AD数据寄存器。单次转换,连续转换;扫描、非扫描;有几个通道,触发源是什么,数据对齐是左对齐还是右对齐。 5.ADC_CMD 开启ADC。 void RCC_AD