[X]关闭

[米联客-XILINX-H3_CZ08_7100] FPGA_SDK入门篇连载-11 PS IIC-EEPROM实验

文档创建者:FPGA课程
浏览次数:292
最后更新:2024-09-25
文档课程分类-AMD-ZYNQ
AMD-ZYNQ: ZYNQ-SOC » 1_SDK应用方案(仅旗舰型号) » 1-SDK基础入门方案
本帖最后由 FPGA课程 于 2024-9-25 14:37 编辑

软件版本: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总线具备广泛的用途,比如寄存器的配置,EEPROM的使用,更重要的是I2C总线上可以挂多种外设。 对于一些低速器件的访问非常节省IO资源,由于是标准的总线接口,使用起来非常方便。I2C总线是OC开路,支持双向传输,所以总线上需要上拉电阻,如下图。
483f331e32cb4fbb8ac80b94ab71fc1a.jpg
实验目的:
1:学习I2C总线协议
2:熟悉ZYNQ I2C控制器的资源
3:掌握vitis-sdk下中断和polled两种模式的使用
4:掌握EEPROM的I2C访问时序
5:掌握vitis-sdk下对EEPROM的存储空间读写
2系统框图
1fc693aac567482496e0d352e572381b.jpg
3I2C串行总线及EEPROM介绍
3.1I2C串行总线协议
由于节课讲解的I2C是基于ZYNQ的I2C控制器,实际上可以不需要非常清楚I2C的详细时序,但是作为初学者,如果第一次学习I2C总线的,还是有必要学习下。

I2C协议把传输的消息分为两种类型的帧:
一个地址帧 :用于master指明消息发往哪个slave;
一个或多个数据帧: 由master发往slave的数据(或由slave发往master),每一帧是8-bit的数据。
注:协议要求每次放到SDA上的字节长度必须为8位,并且每个字节后须跟一个ACK位,在下面会讲到。
数据在SCL处于低电平时放到SDA上,并在SCL变为高电平后进行采样。读写数据和SCL上升沿之间的时间间隔是由总线上的设备自己定义的,不同芯片可能有差异。
I2C数据传输的时序图如下:
a8f3948486324552bebf433cc8dd0ca1.jpg
开始条件(start condition):
为了标识传输正式启动,master设备会将SCL置为高电平(当总线空闲时,SDA和SCL都处于高电平状态),然后将SDA拉低,这样,所有slave设备就会知道传输即将开始。如果两个master设备在同一时刻都希望获得总线的所有权,那么谁先将SDA拉低,谁就赢得了总线的控制权。在整个通信期间,可以存在多个start来开启每一次新的通信序列(communication sequence),而无需先放弃总线的控制权,后面会讲到这种机制。

地址帧(address frame):
地址帧总是在一次通信的最开始出现。一个7-bit的地址是从最高位(MSB)开始发送的,这个地址后面会紧跟1-bit的操作符,1表示读操作,0表示写操作。
接下来的一个bit是NACK/ACK,当这个帧中前面8bits发送完后,接收端的设备获得SDA控制权,此时接收设备应该在第9个时钟脉冲之前回复一个ACK(将SDA拉低)以表示接收正常,如果接收设备没有将SDA拉低,则说明接收设备可能没有收到数据(如寻址的设备不存在或设备忙)或无法解析收到的消息,如果是这样,则由master来决定如何处理(stop或repeated start condition)。

数据帧(data frames):
在地址帧发送之后,就可以开始传输数据了。Master继续产生时钟脉冲,而数据则由master(写操作)或slave(读操作)放到SDA上。每个数据帧8bits,数据帧的数量可以是任意的,直到产生停止条件。每一帧数据传输(即每8-bit)之后,接收方就需要回复一个ACK或NACK(写数据时由slave发送ACK,读数据时由master发送ACK。当master知道自己读完最后一个byte数据时,可发送NACK然后接stop condition)。

停止条件(stop condition):
当所有数据都发送完成时,master将产生一个停止条件。停止条件定义为:在SDA置于低电平时,将SCL拉高并保持高电平,然后将SDA拉高。
注意,在正常传输数据过程中,当SCL处于高电平时,SDA上的值不应该变化,防止意外产生一个停止条件。

重复开始条件(repeated start condition):
有时master需要在一次通信中进行多次消息交换(例如与不同的slave传输消息,或切换读写操作),并且期间不希望被其他master干扰,这时可以使用“重复开始条件” —— 在一次通信中,master可以产生多次start condition,来完成多次消息交换,最后再产生一个stop condition结束整个通信过程。由于期间没有stop condition,因此master一直占用总线,其他master无法切入。
为了产生一个重复的开始条件,SDA在SCL低电平时拉高,然后SCL拉高。接着master就可以产生一个开始条件继续新的消息传输(按照正常的7-bit/10-bit地址传输时序)。重复开始条件的传输时序如下图所示:
066e7562965f4b5b8a3146f97b0f67d9.jpg
时钟拉伸(clock stretching):
有时候,低速slave可能由于上一个请求还没处理完,尚无法继续接收master的后续请求,即master的数据传输速率超过了slave的处理能力。这种情况下,slave可以进行时钟拉伸来要求master暂停传输数据 —— 通常时钟都是由master提供的,slave只是在SDA上放数据或读数据。而时钟拉伸则是slave在master释放SCL后,将SCL主动拉低并保持,此时要求master停止在SCL上产生脉冲以及在SDA上发送数据,直到slave释放SCL(SCL为高电平)。之后,master便可以继续正常的数据传输了。可见时钟拉伸实际上是利用了时钟同步的机制(见下文),只是时钟由slave产生。
如果系统中存在这种低速slave并且slave实现了clock stretching,则master必须实现为能够处理这种情况,实际上大部分slave设备中不包含SCL驱动器的,因此无法拉伸时钟。
所以更完整的I2C数据传输时序图为:
f55f5b5ea47c417193766f4e2b13f13f.jpg

10-bit地址空间:
上面讲到I2C支持10-bit的设备地址,此时的时序如下图所示:
e2cdc32e3443410289020335a5714cf6.jpg
在10-bit地址的I2C系统中,需要两个帧来传输slave的地址。第一个帧的前5个bit固定为b11110,后接slave地址的高2位,第8位仍然是R/W位,接着是一个ACK位,由于系统中可能有多个10-bit slave设备地址的高2bit相同,因此这个ACK可能由多有slave设备设置。第二个帧紧接着第一帧发送,包含slave地址的低8位(7:0),接着该地址的slave回复一个ACK(或NACK)。
注意,10-bit地址的设备和7-bit地址的设备在一个系统中是可以并存的,因为7-bit地址的高5位不可能是b11110。实际上对于7-bit的从设备地址,合法范围为b0001XXX-b1110XXX,’X’表示任意值,因此该类型地址最多有112个(其他为保留地址[1])。
两个地址帧传输完成后,就开始数据帧的传输了,这和7-bit地址中的数据帧传输过程相同。

时钟同步和仲裁:
如果两个master都想在同一条空闲总线上传输,此时必须能够使用某种机制来选择将总线控制权交给哪个master,这是通过时钟同步和仲裁来完成的,而被迫让出控制权的master则需要等待总线空闲后再继续传输。在单一master的系统上无需实现时钟同步和仲裁。

时钟同步:
时钟同步是通过I2C接口和SCL之间的线“与”(wired-AND)来完成的,即如果有多个master同时产生时钟,那么只有所有master都发送高电平时,SCL上才表现为高电平,否则SCL都表现为低电平。

总线仲裁:
总线仲裁和时钟同步类似,当所有master在SDA上都写1时,SDA的数据才是1,只要有一个master写0,那此时SDA上的数据就是0。一个master每发送一个bit数据,在SCL处于高电平时,就检查看SDA的电平是否和发送的数据一致,如果不一致,这个master便知道自己输掉仲裁,然后停止向SDA写数据。也就是说,如果master一直检查到总线上数据和自己发送的数据一致,则继续传输,这样在仲裁过程中就保证了赢得仲裁的master不会丢失数据。
输掉仲裁的master在检测到自己输了之后也不再产生时钟脉冲,并且要在总线空闲时才能重新传输。
仲裁的过程可能要经过多个bit的发送和检查,实际上两个master如果发送的时序和数据完全一样,则两个master都能正常完成整个的数据传输。
3.2EEPROM 24C02芯片
经过上面对I2C总线的介绍,下面我们介绍EEPROM 24C02的I2C总线读写控制。
24C02的容量位2Kbit=256Bytes,每1页16Bytes因此又16页。对于写操作一次最多写16Bytes,对于读操作可以一次全部读完256Bytes.
如下图,A0-A2是EEPROM I2C器件地址,SDA和SCL是EEPROM I2C总线SLAVE接口,WP是保护脚,一般接VCC。
2fc27bfe5e454959b59f38c3bfef502e.jpg
24LXX 器件地址如下图
ce9fcd979ec34ae2bf9b946c7d9ad83c.jpg
我们看下24C02的写时序,可以看到,支持单个字节的写,以及多个字节的写。首先发送器件的地址,然后发送需要写EEPROM存储空间的地址,之后就是数据,对于读操作一次可以写1个字节或者多个字节。
22f592e6f09f4ce0b20bcd39ea71b9cb.jpg
003eddca5ee54582a5b5b3ed70e3f02d.jpg

我们看下24C02的读时序,可以看到,支持单个字节的读,以及多个字节的读。以下支持3种读的方式:
第一种:CURRENT ADDRESS READ 只要发送器件地址就能读数据
第二种:RANDOM READ 需要发送器件地址,然后发送内存地址,之后再发送器件地址并且读取到数据,支持连续读取。
第三种:SEQUENTIAL CURRENT READ 只要发送器件地址,就能连续读取当前地址的数据,支持连续读取。
教程代码中采用的是第二种方法。
940fc5e29be54e8e90b6d9a18c8516f7.jpg
ca42bf9099bb4e6598ca9ab89f53fb63.jpg
64780a48136f442680fc4b28c39fb7b3.jpg
4ZYNQ I2C控制器介绍
4.1ZYNQ I2C控制器框图
7d4dcaf6b4a2492cb2f21c7a1ac6b2b6.jpg
ZYNQ的PS自带2个I2C控制器,I2C的IO可以映射到MIO或者EMIO上。ZYNQ的I2C控制器支持Master模式或者Slave模式,一般都是使用master模块,本实验也是基于Master模式访问EEPROM.
4.2ZYNQ I2C控制器寄存器
XIICPS_CR寄存器XIICPS_CR_OFFSET (0x00U)
I2C控制寄存器
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
divisor_a
                        
                        
15:14
                        
                        
WR
                        
                        
0x0
                        
                        
预分频A:对应的值位N+1,N的大小为0~3。
                        
                        
divisor_b
                        
                        
13:8
                        
                        
WR
                        
                        
0x0
                        
                        
预分频B:对应的值为N+1,N的大小为0~63
                        
                        
保留
                        
                        
7
                        
                        
RO
                        
                        
0x0
                        
                        
保留
                        
                        
CLR_FIFO
                        
                        
6
                        
                        
WR
                        
                        
0x0
                        
                        
清FIFO和传输大小寄存器
                        
1-清除FIFO和传输大小寄存器,该位下一个APB 时钟自动归零
                        
                        
SLVMON
                        
                        
5
                        
                        
WR
                        
                        
0x0
                        
                        
从Monitor模式(本实验用不到)
                        
1-Monitor模式
                        
0-普通模式
                        
                        
HOLD
                        
                        
4
                        
                        
WR
                        
                        
0x0
                        
                        
总线保持
                        
1-保持scl时钟低电平,这样总线上不再继续传输数据
                        
0-当数据传输完毕可以立即停止传输
                        
                        
ACK_EN
                        
                        
3
                        
                        
WR
                        
                        
0x0
                        
                        
ACK/NACK
                        
1-发送ACK
                        
0-发送NACK
                        
                        
NEA
                        
                        
2
                        
                        
WR
                        
                        
0x0
                        
                        
地址模式
                        
1-7bit地址模式
                        
0-预留
                        
                        
MS
                        
                        
1
                        
                        
WR
                        
                        
0x0
                        
                        
主/从模式
                        
1-Master模式
                        
0-Slave模式
                        
                        
RW
                        
                        
0
                        
                        
WR
                        
                        
0x0
                        
                        
方向控制
                        
1-Master接收
                        
0- Master发送
                        

XIICPS_SR寄存器XIICPS_SR_OFFSET (0x04U)
I2C状态寄存器
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
保留
                        
                        
15:0
                        
                        
RO
                        
                        
0x0
                        
                        
保留
                        
                        
BA
                        
                        
8
                        
                        
RO
                        
                        
                        
                        
总线激活
                        
1-数据正在传输
                        
                        
RXOVF
                        
                        
7
                        
                        
RO
                        
                        
                        
                        
接收FIFO溢出
                        
1-接收FIFO已经满,继续有数据收到,FIFO的数据保持不变
                        
                        
TXDV
                        
                        
6
                        
                        
RO
                        
                        
                        
                        
数据有效
                        
1-有1Byte需要发送,不能用该位判断数据传输完毕。
                        
                        
RXDV
                        
                        
5
                        
                        
RO
                        
                        
                        
                        
接收数据有效
                        
1-接收端口数据需要被读走
                        
                        
保留
                        
                        
4
                        
                        
RO
                        
                        
                        
                        
保留
                        
                        
RXRW
                        
                        
3
                        
                        
RO
                        
                        
                        
                        
RX读/写(仅工作SLAVE模式有效,所以这里用不到)
                        
1-从主机接收到传输模式
                        
                        
保留
                        
                        
2:0
                        
                        
RO
                        
                        
                        
                        
2~0bit:保留
                        

XIICPS_ADDR寄存器XIICPS_ADDR_OFFSET (0x08U)
I2C地址寄存器
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
保留
                        
                        
15:10
                        
                        
RO
                        
                        
0x0
                        
                        
保留
                        
                        
ADD
                        
                        
9:0
                        
                        
RW
                        
                        
0x0
                        
                        
地址
                        
普通地址模式6:0-7bits地址
                        
扩展地址模式9:0-10bits地址
                        

XIICPS_DATA寄存器XIICPS_DATA_OFFSET (0x0CU)
I2C数据寄存器
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
预留
                        
                        
15:8
                        
                        
RW
                        
                        
0x0
                        
                        
预留
                        
                        
DATA
                        
                        
7:0
                        
                        
                        
                        
0x0
                        
                        
数据
                        
发送或者接收的数据
                        

XIICPS_ISR寄存器XIICPS_ISR_OFFSET (0x10U)
I2C中断状态寄存器
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
保留
                        
                        
15:10
                        
                        
WTC
                        
                        
0x0
                        
                        
保留
                        
                        
ARB_LOST
                        
                        
9
                        
                        
WTC
                        
                        
0x0
                        
                        
仲裁丢失
                        
1-当工作于多主机模式代表仲裁丢失
                        
                        
保留
                        
                        
8
                        
                        
WTC
                        
                        
0x0
                        
                        
保留
                        
                        
RX_UNF
                        
                        
7
                        
                        
WTC
                        
                        
0x0
                        
                        
FIFO下溢
                        
1-读FIFO已空,Master多读了接收FIFO导致了,FIFO下溢
                        
                        
TX_OVF
                        
                        
6
                        
                        
WTC
                        
                        
0x0
                        
                        
FIFO溢出
                        
1-写FIFO已满,Master多写了发送FIFO导致了,FIFO溢出
                        
                        
RX_OVF
                        
                        
5
                        
                        
WTC
                        
                        
0x0
                        
                        
接收溢出
                        
1-接收FIFO已满,但是继续接收到了数据,多出的数据不会被应答,FIFO数据保持不变
                        
                        
SLV_RDY
                        
                        
4
                        
                        
WTC
                        
                        
0x0
                        
                        
Monitored Slave 准备好
                        
1-寻址从机返回ACK
                        
                        
TO
                        
                        
3
                        
                        
WTC
                        
                        
0x0
                        
                        
传输超时
                        
1-SCLK保持低电平时间过长导致超时
                        
                        
NACK
                        
                        
2
                        
                        
WTC
                        
                        
0x0
                        
                        
传输NACK
                        
1-SLAVE相应NACK或者Master提前停止传输
                        
                        
DATA
                        
                        
1
                        
                        
WTC
                        
                        
0x0
                        
                        
数据是否传输完毕
                        
1-数据未传输完毕
                        
                        
COMP
                        
                        
                        
                        
WTC
                        
                        
0x0
                        
                        
传输完毕
                        
1-I2C传输完毕
                        

XIICPS_TRANS_SIZE寄存器XIICPS_TRANS_SIZE_OFFSET (0x14U)
I2C发送大小寄存器
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
TRANS_SIZE
                        
                        
7:0
                        
                        
WR
                        
                        
0x0
                        
                        
7~0bit:传输大小
                        
Master发送模式:剩余未传输的数据
                        
Master接收模式:剩余需要被读走的数据
                        
Slave发送模式,当主机结束传输后剩余的未传输的数据
                        
Slave接收模式,FIFO中有效的数据
                        

XIICPS_SLV_PAUSE寄存器XIICPS_SLV_PAUSE_OFFSET (0x18U)
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
XIICPS_SLV_PAUSE
                        
                        
7:0
                        
                        
WR
                        
                        
0x0
                        
                        
7~4bit:保留位
                        
3~0bit:暂停时间
                        
      暂停间隔0~7
                        

XIICPS_TIME_OUT寄存器XIICPS_TIME_OUT_OFFSET (0x1CU)
I2C超时寄存器
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
XIICPS_TIME_OUT
                        
                        
7:0
                        
                        
WR
                        
                        
0x1F
                        
                        
255~31:超时寄存器
                        

XIICPS_IMR寄存器XIICPS_IMR_OFFSET (0x20U)
该寄存器对应于XIICPS_ISR中断状态寄存器中的中断是否被屏蔽了,是只读寄存器,当相应位的状态是1代表该中断被屏蔽了。
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
保留
                        
                        
15:20
                        
                        
RO
                        
                        
0x0
                        
                        
保留
                        
                        
ARB_LOST
                        
                        
9
                        
                        
RO
                        
                        
0x0
                        
                        
仲裁丢失中断掩码状态
                        
                        
保留
                        
                        
8
                        
                        
RO
                        
                        
0x0
                        
                        
保留
                        
                        
RX_UNF
                        
                        
7
                        
                        
RO
                        
                        
0x0
                        
                        
FIFO下溢中断掩码状态
                        
                        
TX_OVF
                        
                        
6
                        
                        
RO
                        
                        
0x0
                        
                        
FIFO溢出中断掩码状态
                        
                        
RX_OVF
                        
                        
5
                        
                        
RO
                        
                        
0x0
                        
                        
接收溢出中断掩码状态
                        
                        
SLV_RDY
                        
                        
4
                        
                        
RO
                        
                        
0x0
                        
                        
Monitored Slave 准备好中断掩码状态
                        
                        
TO
                        
                        
3
                        
                        
RO
                        
                        
0x0
                        
                        
传输超时中断掩码状态
                        
                        
NACK
                        
                        
2
                        
                        
RO
                        
                        
0x0
                        
                        
传输NACK中断掩码状态
                        
                        
DATA
                        
                        
1
                        
                        
RO
                        
                        
0x0
                        
                        
数据是否传输完毕中断掩码状态
                        
                        
COMP
                        
                        
0
                        
                        
RO
                        
                        
0x0
                        
                        
传输完毕中断掩码状态
                        

XIICPS_IER寄存器XIICPS_IER_OFFSET (0x24U)
该寄存器对应于XIICPS_ISR中断状态寄存器中的中断是否被使能了,当相应位设置位1代表该中断被使能。
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
保留
                        
                        
15:20
                        
                        
RO
                        
                        
0x0
                        
                        
保留
                        
                        
ARB_LOST
                        
                        
9
                        
                        
WO
                        
                        
0x0
                        
                        
仲裁丢失中断使能
                        
                        
保留
                        
                        
8
                        
                        
RO
                        
                        
0x0
                        
                        
保留
                        
                        
RX_UNF
                        
                        
7
                        
                        
WO
                        
                        
0x0
                        
                        
FIFO下溢中断使能
                        
                        
TX_OVF
                        
                        
6
                        
                        
WO
                        
                        
0x0
                        
                        
FIFO溢出中断使能
                        
                        
RX_OVF
                        
                        
5
                        
                        
WO
                        
                        
0x0
                        
                        
接收溢出中断使能
                        
                        
SLV_RDY
                        
                        
4
                        
                        
WO
                        
                        
0x0
                        
                        
Monitored Slave 准备好中断使能
                        
                        
TO
                        
                        
3
                        
                        
WO
                        
                        
0x0
                        
                        
传输超时中断使能
                        
                        
NACK
                        
                        
2
                        
                        
WO
                        
                        
0x0
                        
                        
传输NACK中断使能
                        
                        
DATA
                        
                        
1
                        
                        
WO
                        
                        
0x0
                        
                        
数据是否传输完毕中断使能
                        
                        
COMP
                        
                        
0
                        
                        
WO
                        
                        
0x0
                        
                        
传输完毕中断使能
                        

XIICPS_IDR寄存器XIICPS_IDR_OFFSET (0x28U)
该寄存器对应于XIICPS_ISR中断状态寄存器中的中断是否被禁用了,当相应位设置位1代表该中断被禁用,同时会设置中断掩码寄存器XIICPS_IMR中相应的寄存器位位1。
                        
Field Name
                        
                        
Bits
                        
                        
Type
                        
                        
Reset Value
                        
                        
Description
                        
                        
保留
                        
                        
15:20
                        
                        
RO
                        
                        
0x0
                        
                        
保留
                        
                        
ARB_LOST
                        
                        
9
                        
                        
WO
                        
                        
0x0
                        
                        
仲裁丢失中断禁用
                        
                        
保留
                        
                        
8
                        
                        
RO
                        
                        
0x0
                        
                        
保留
                        
                        
RX_UNF
                        
                        
7
                        
                        
WO
                        
                        
0x0
                        
                        
FIFO下溢中断禁用
                        
                        
TX_OVF
                        
                        
6
                        
                        
WO
                        
                        
0x0
                        
                        
FIFO溢出中断禁用
                        
                        
RX_OVF
                        
                        
5
                        
                        
WO
                        
                        
0x0
                        
                        
接收溢出中断禁用
                        
                        
SLV_RDY
                        
                        
4
                        
                        
WO
                        
                        
0x0
                        
                        
Monitored Slave 准备好中断禁用
                        
                        
TO
                        
                        
3
                        
                        
WO
                        
                        
0x0
                        
                        
传输超时中断禁用
                        
                        
NACK
                        
                        
2
                        
                        
WO
                        
                        
0x0
                        
                        
传输NACK中断禁用
                        
                        
DATA
                        
                        
1
                        
                        
WO
                        
                        
0x0
                        
                        
数据是否传输完毕中断禁用
                        
                        
COMP
                        
                        
0
                        
                        
WO
                        
                        
0x0
                        
                        
传输完毕中断禁用
                        
5硬件原理图
b43a688198ef4a1b9356a346ce8374d5.jpg
如上图所示,I2C总线上挂了2个外设,分别是EEPROM 24LC02以及RTC S1337
6搭建SOC系统工程
详细的搭建过程这里不再重复,对于初学读者如果还不清楚如何创建SOC工程的,请学习“01Vitis Soc开发入门”这篇文章。
6.1SOC系统工程
e1bc8ee5029a400a8a262575cbea4107.jpg
ZYNQ IP中设置I2C0
2d268f71af0b46f6b90a4a9864ad28f7.jpg
6.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平台。
7bb3a0cb8bb34ca48296eca980219a4b.jpg
7搭建Vitis-sdk工程
创建soc_base sdk platform和APP工程的过程不再重复,如果不清楚请参考本章节第一个demo。
7.1创建SDK Platform工程
5ed50552579c4046b4304377453c0527.jpg
右击soc_base编译,编译的时间可能会有点长
7.2创建APP工程
以下应用程序中给出了polled方式,以及中断方式两种方案。
bef97160dce24110a3dc44e347ae8800.jpg
8Polled模式读写EEPROM
8.1eeprom_polled读写模式
  1. #include "I2cPs_Polled.h"
  2. #include "sleep.h"

  3. extern XIicPs I2cInst0;

  4. I2C_ADDR8 *I2C_WINST_8 = (void*)0x0800000; //定义写数据的地址
  5. I2C_ADDR8 *I2C_RINST_8 = (void*)0x0810000; //定义读数据的地址

  6. int main(void)
  7. {
  8.         u32 i=0;
  9.         u32 j=0;
  10.         i2cps_init(&I2cInst0,IIC_DEVICE_ID0);//initial ps i2c controller

  11.         while(1)
  12.         {
  13.                 //24c02 2kbits write
  14.                 for(i=0; i<16*2; i++)//24cl02 有16个页,每个页有16Bytes,这里每次写8bytes 共需要32次写完
  15.                 {
  16.                         I2C_WINST_8->reg_addr[0]=i*8; //EEPROM的内存地址,需要确保写的数据不能跨页
  17.                         for(j=0; j<8; j++)//每次写8bytes,单独的一次写数据必须确保在同一页内
  18.                                 I2C_WINST_8->reg_buf[j] = j + i*8;
  19.                         i2cps_wr8(&I2cInst0,I2C_WINST_8,8,0x50); //调用驱动程序写数据
  20.                         usleep(4000); //等待一段时间,确保EEPROM数据完成更新
  21.                 }

  22.                 //以下程序一次性读完256Bytes,读操作可以一次性全部读完
  23.                 I2C_RINST_8->reg_addr[0]=0; //设置读EEPROM的地址,从0开始
  24.                 i2cps_rd8(&I2cInst0,I2C_RINST_8,256,0x50);//一次性读完256bytes

  25.                 for(i=0;i<256;i++)//比较读写的数据是否正确
  26.                         if(i!=I2C_RINST_8->reg_buf[i])
  27.                                 xil_printf("error addr%d=%d\r\n",i,I2C_RINST_8->reg_buf[i]);

  28.                 xil_printf("i2c test successfully!\r\n");

  29.                 sleep(1);
  30.         }

  31.     return 0;
  32. }
复制代码

1:polled 模式eeprom写数据
首先我们定义数据结构:
  1. typedef struct
  2. {
  3.         u8   reg_addr[2];
  4.         u8   reg_buf[2048];
  5. }I2C_ADDR16;
  6. typedef struct
  7. {
  8.         u8   reg_addr[1];
  9.         u8   reg_buf [2048];
  10. }I2C_ADDR8;
复制代码

这两个数据结构分别支持8bit的内存寻址,和16bit的内存寻址。
对于EEPROM 只需要使用8bit的内存寻址方式。
我们在程序中定义了I2C_ADDR8 I2C_WINST_8,I2C_RINST_8两个变量用于分别读和写EEPROM.
比如当我们需要写入一个只位1的数据到EEPROM的第0页第一个地址,只需要设置:
I2C_WINST_8.reg_addr[0]=0;
I2C_WINST_8.reg_buf[0] = 1;
i2cps_wr8(&I2cInst0,&I2C_WINST_8,1,0x50);

再比如当我们需要写入8个递增的数据到EEPROM的第0页第一个地址,只需要设置:
I2C_WINST_8.reg_addr[0]=0*8;
for(j=0; j<8; j++)
  I2C_WINST_8.reg_buf[j] = j + i*8;
i2cps_wr8(&I2cInst0,&I2C_WINST_8,8,0x50);

这里读者需要特别注意,写EEPROM 一次最多写一页,对于24LC02就一页最多是16bytes所以最多写入16bytes,但是实际发现连续写入16个Bytes总是高8Bytes无法写入,改成每次写入8Bytes 正常。
for(i=0; i<16*2; i++)//24cl02 有16个页,每个页有16Bytes,这里每次写8bytes 共需要32次写完
{
I2C_WINST_8->reg_addr[0]=i*8; //EEPROM的内存地址,需要确保写的数据不能跨页
for(j=0; j<8; j++)//每次写8bytes,单独的一次写数据必须确保在同一页内
I2C_WINST_8->reg_buf[j] = j + i*8;
i2cps_wr8(&I2cInst0,I2C_WINST_8,8,0x50); //调用驱动程序写数据
usleep(4000); //等待一段时间,确保EEPROM数据完成更新
}
}
2:polled 模式eeprom读数据
写需要注意页写,跨页,而且ZYNQ一次只能写EEPROM 8Bytes,但是对于读操作,没有这个要求,所以我们可以一次读取所有的页
I2C_RINST_8.reg_addr[0]=0;
i2cps_rd8(&I2cInst0,&I2C_RINST_8,256,0x50);//read all 256 bytes
8.2.polled驱动程序分析

1:i2cps_polled.c驱动程序
为了让操作I2C控制器更加方便,我们编写了i2cps_polled.c驱动程序,使用该驱动程序调用SDK库函数。该驱动程序支持读写8bits的数据和16bits的数据,在SDK应用中,这种模式一般用于一些外设的寄存器初始化。
  1. #include "i2cps_polled.h"

  2. XIicPs I2cInst0;

  3. int i2cps_init(XIicPs *I2C_Ptr,u16 DeviceId) //初始化I2C外设
  4. {
  5.         int Status;
  6.         XIicPs_Config *Config;
  7.         /*
  8.          * Initialize the IIC driver so that it's ready to use
  9.          * Look up the configuration in the config table, then initialize it.
  10.          */
  11.         Config = XIicPs_LookupConfig(DeviceId);
  12.         if (NULL == Config) {
  13.                 return XST_FAILURE;
  14.         }

  15.         Status = XIicPs_CfgInitialize(I2C_Ptr, Config, Config->BaseAddress);
  16.         if (Status != XST_SUCCESS) {
  17.                 return XST_FAILURE;
  18.         }
  19.         /*
  20.          * Set the IIC serial clock rate.
  21.          */
  22.         XIicPs_SetSClk(I2C_Ptr, IIC_SCLK_RATE);

  23.         return XST_SUCCESS;
  24. }

  25. //8bit数据写数据函数
  26. void i2cps_wr8(XIicPs *I2C_Ptr, I2C_ADDR8 *MsgPtr , u16 byte_cnt ,u16 slave_addr)
  27. {
  28.         XIicPs_MasterSendPolled(I2C_Ptr, (u8 *)MsgPtr, byte_cnt + 1, slave_addr); //SDK 库函数polled 模式写数据
  29.         while (XIicPs_BusIsBusy(I2C_Ptr)) ; //SDK 库函数返回1代表总线忙
  30. }

  31. //8bit数据写数据函数
  32. void i2cps_rd8(XIicPs *I2C_Ptr, I2C_ADDR8 *MsgPtr , u16 byte_cnt ,u16 slave_addr)
  33. {
  34.         XIicPs_MasterSendPolled(I2C_Ptr, MsgPtr->reg_addr, 1, slave_addr); //SDK 库函数polled 模式写数据
  35.         while (XIicPs_BusIsBusy(I2C_Ptr)) ; //SDK 库函数返回1代表总线忙
  36.         XIicPs_MasterRecvPolled(I2C_Ptr, MsgPtr->reg_buf, byte_cnt, slave_addr); //SDK 库函数polled 模式读数据
  37.         while (XIicPs_BusIsBusy(I2C_Ptr)) ; //SDK 库函数返回1代表总线忙
  38. }

  39. //16bit数据写数据函数
  40. void i2cps_wr16(XIicPs *I2C_Ptr, I2C_ADDR16 *MsgPtr , u16 byte_cnt ,u16 slave_addr)
  41. {
  42.         XIicPs_MasterSendPolled(I2C_Ptr, (u8 *)MsgPtr, byte_cnt + 2, slave_addr); //SDK 库函数polled 模式写数据
  43.         while (XIicPs_BusIsBusy(I2C_Ptr)) ; //SDK 库函数返回1代表总线忙
  44. }

  45. //16bit数据读数据函数
  46. void i2cps_rd16(XIicPs *I2C_Ptr, I2C_ADDR16 *MsgPtr , u16 byte_cnt ,u16 slave_addr)
  47. {
  48.         XIicPs_MasterSendPolled(I2C_Ptr, MsgPtr->reg_addr, 2, slave_addr); //SDK 库函数polled 模式写数据
  49.         while (XIicPs_BusIsBusy(I2C_Ptr)) ; //SDK 库函数返回1代表总线忙
  50.         XIicPs_MasterRecvPolled(I2C_Ptr, MsgPtr->reg_buf, byte_cnt, slave_addr); //SDK 库函数polled 模式读数据
  51.         while (XIicPs_BusIsBusy(I2C_Ptr)) ; //SDK 库函数返回1代表总线忙
  52. }
复制代码

其中数据结构I2C_ADDR8用于读写8bits的数据,定于如下
  1. typedef struct
  2. {
  3.         u8   reg_addr[1];
  4.         u8   reg_buf [2048];
  5. }I2C_ADDR8;
复制代码


其中数据结构I2C_ADDR16用于读写16bit的数据,定于如下
  1. typedef struct
  2. {
  3.         u8   reg_addr[2];
  4.         u8   reg_buf[2048];
  5. }I2C_ADDR16;
复制代码



以下是针对驱动程序的分析,比较繁琐,只是关注应用的可以不看。
2:I2cPs_init函数

7fff720aacbb43b183a43efa2937686a.jpg
2.1:XIicPs_LookupConfig(DeviceId)函数
首先依然是通过查找配置程序来获取I2C的硬件配置。我们跟踪这个程序,看看他获取的配置是什么。
0f5637d1768c4ebca635a8ac9eb2cbd3.jpg
右击参数打开参数定义
c29c11ca22c84a6e941675bc9181381a.jpg
可以看到,这个数组里存放的是I2C设备ID、I2C控制器的基地址,I2C APB总线时钟频率。
7829d26c85b64ca392224f10fa5c470d.jpg
可以继续右击这些参数定位到这些参数的定义
9c9de0dfa62f4d4face6714b647f6abb.jpg
在xparameters.h中找到如下定义,可以看到I2C0的基地址和 APB总线的时钟
ff2f7067dcd84c8391174c88d0f470e1.jpg
2.2:XIicPs_CfgInitialize(I2C_Ptr, Config, Config->BaseAddress)函数
3c14267bd30740878a4300920864eb5f.jpg
2.2.1:XIicPs *InstancePtr结构体
XiicPs结构体定义了包括I2C数据收发缓存指针,收发数据的数量,I2C数据传输方向是发送还是接收,是7bit寻址还是10bit寻址,I2C中断的回调函数。
68c4fd1e3ad7400a86352363f0965f8d.jpg
2.2.2:XIicPs_Reset(InstancePtr)函数
e8a0d97b11354477b01550caf1a6020d.jpg
这个函数中对3个寄存器进行了操作,分别是I2C控制寄存器、I2C超时寄存器、I2C中断寄存器
各个寄存器的偏移地址如下:
dc004ee1673f4f5d965b8595d05375f2.jpg
2.2.3I2C控制寄存器地址偏移XIICPS_CR_OFFSET=0x00
24f8063b4cbf42b9be842ecd46214f13.jpg
写入XIICPS_CR_RESET_VALUE=0
2.2.4:I2C超时寄存器地址偏移XIICPS_TIME_OUT_OFFSET=0x 1C
0b8c03a1fde54b63b5712c7db0843584.jpg
写入XIICPS_TO_RESET_VALUE =0x000000FF
2.2.5I2C中断禁止寄存器地址偏移XIICPS_IDR_OFFSET= 0x28
0d93298c08814676bb63cd0a77467d2b.jpg
9b4f73333b7043d8b0957a1eb109b87a.jpg
写入之XIICPS_IXR_ALL_INTR_MASK = 0x 0x000002FF
2.3:XIicPs_SetSClk(I2C_Ptr, IIC_SCLK_RATE)
这个函数设置I2C的时钟参数相关的寄存器,计算出来的参数最终会写入I2C控制寄存器。这里需要注意一点,代码中有这么一段,I2C总线的速度实际要么工作在低速90Khz要么高速384.6Khz
cb0f9ddbfd144d9081a477ea3a7da7d1.jpg
3:i2cps_wr8函数

d999792fc1454ee19ff6baa463088903.jpg
3.1:XIicPs_MasterSendPolled(I2C_Ptr, MsgPtr, ByteCount, SlaveAddr)函数
3.1.1设置控制寄存器
这个函数比较长,我们只拿其中关键的代码分析,以下代码中设置首先判断是否有RepeatedStart或者需要发送的数据量是否大于I2C控制器FIFO的大小,如果是的,那么设置SCL为高电平(通过设置I2C控制寄存器的(XIICPS_CR_HOLD_MASK=0x00000010)。
dab29370101b42cf8a994420292942d7.jpg
之后执行XIicPs_SetupMaster函数,设置I2C控制器,使能TX发送ACK检测、清空FIFO、地址为7bit寻址、Master模式、发送模式。
这个控制寄存器每个位的功能已经在前面有介绍。
9b137a0a287f46cf9afe1701299dde6b.jpg
以下是相关的寄存器控制位操作
e176dad37bba40e8a3c357f1b689ce84.jpg
3.1.2设置中断寄存器
8084a379ec2a484791b176d64694603a.jpg
以上代码中清除的中断包括:仲裁丢失中断、发送溢出中断、NACK中断
c48286ace53a4fc5b2f760604ed5936e.jpg
3.1.3 TransmitFifoFill(InstancePtr)
此函数首先完成一次FIFO数据的填充,I2C控制器发送数据先要把数据填充到FIFO.
b5770d146cfe44aa8a90e32566d65ac3.jpg
读取发什么大小寄存器,判断本次传输能传输多少数据,这个寄存器的偏移地址XIICPS_TRANS_SIZE_OFFSET = 0x14,寄存器功能描述如下:

我们知道I2C的FIFO最大深度是16,如果I2C控制器的FIFO没有填满,则继续填写数据。
这里还有另外一个数据寄存器是我们第一次遇到,这个寄存器的偏移地址XIICPS_DATA_OFFSET= 0x0C
3.1.4:XIicPs_WriteReg(BaseAddr, XIICPS_ADDR_OFFSET, (u32)SlaveAddr)
往I2C控制器地址寄存器里面写入从机地址,这里我们又遇到一个新的寄存器,这个寄存器的偏移地址XIICPS_ADDR_OFFSET = 0x08
6a9a24def9f44961b0f4494479a148d6.jpg
3.1.5发送剩余的数据
41df26acf78c4f0094ed45e7c3f013fb.jpg
发送剩余数据需要先判断之前一次的发送是否有错误。通过读取中断状态寄存器的仲裁丢失位、发送溢出位、NACK位判断之前一次传输是否有错误。如果没有错误再进行下次一传输,直到数据发送完毕。
这里我们看下中断状态寄存器,这个寄存器的偏移地址XIICPS_ISR_OFFSET= 0x10,寄存器说明如下:
0470ff97f6d2416f9923792bfa8937db.jpg
接下来还要通过判断以上中断状态寄存器的FIFO数据位是否已经空,如果空了就可以继续准备发送数据。
3.1.6数据发送结束
通过判单中断状态寄存器判断数据是否发送完毕,如果发送完毕,需要设置控制寄存器的HOLD位为0结束本次传输。
6c4a54947ef84eb19cd0a03c40f66c48.jpg
3.2:XIicPs_BusIsBusy(I2C_Ptr)函数
通过判断I2C状态寄存器,读取忙标志位判断I2C是否还在传输中。
f2f621b16d014858be91b9547ced0e68.jpg
I2C状态寄存器偏移地址XIICPS_SR_OFFSET = 0x04寄存器描述如下:
d647843312ff4f499443c3565b1ab155.jpg
4:i2cps_rd8函数

c12404d33c42429dae7f4c2d2c59bee2.jpg
对于读数据先要发送器件地址和寄存器地址,之后再发起读操作。
4.1:XIicPs_MasterRecvPolled(I2C_Ptr, MsgPtr, ByteCount, SlaveAddr)函数
前面我们分析过I2C发送数据部分的函数,这部分接收的代码操作非常类似。以下代码中设置首先判断是否有RepeatedStart或者需要发送的数据量是否大于I2C控制器FIFO的大小,如果是的,那么设置SCL为高电平(通过设置I2C控制寄存器的(XIICPS_CR_HOLD_MASK=0x00000010)。
4.1.1 XIicPs_SetupMaster(InstancePtr, RECVING_ROLE)函数
fde3b889726442b19c47a21e494fabdc.jpg
之后执行XIicPs_SetupMaster函数,设置I2C控制器,使能TX发送ACK检测、清空FIFO、地址为7bit寻址、Master模式、接收模式。
0a957af9f48e4ca2b0418a2bb7d57749.jpg
这个控制寄存器每个位的功能已经在前面有介绍,以下是相关的寄存器控制位操作:
8bd5026e18d145339b765bffa6d25786.jpg
4.1.2清除I2C中断状态寄存器
8944a52ddb394a6c89053ded6a7192ba.jpg
4.1.3设置需要接收的数据量
以下代码设置I2C控制器需要接收的数据量,最大的数据传输量:
XIICPS_MAX_TRANSFER_SIZE=252,所以大于这个数据量
4e945816350f42bc8524e2d257b990f5.jpg
4.1.4接收剩余的数据
d6c077f9a6f34b249f7b1bfe66e90b63.jpg
这段代理理解起来需要把握以下关键几点:
1`I2C控制器的RX 接收FIFO最大深度16 bytes,所以以下代码判断接收的数据是否大于16字节,如果时,则设置IsHlod=1
f2a6ad5cb74f422e8dcc0bc6631d628d.jpg
2`I2C控制器的RX接收数据一次最大时252bytes,所以以下代码首先判断需要接收的数据是否大于XIICPS_MAX_TRANSFER_SIZE,如果时则设置第一次接收的最大数据为ByteCountVar = (s32)XIICPS_MAX_TRANSFER_SIZE,并且设置UpdateTxSize=1,用以标识当第一次数据传输完成后还要继续传输。
8c22316203d545a8aedc826e681c5d10.jpg
3`这个大while循环就是负责把所有需要接收的数据都接收了。 在这个大while循环中,代码分析需要分3个部分。分为:1个小while循环判断数据是否有效、1个if分支代码硬件是ZYNQ、1个else分支代码硬件是MSPSOC。之所以要这么设计,因为ZYNQ和MSPOC的I2C控制器还是有所差异。我们先分析if...else...这两种分支
4`这段代码中处理的机制是一样的,差异在于针对ZYNQ平台
1180538e5dce4c61bf7cd644697c5d49.jpg
if分支ZYNQ平台的处理:从代码可以猜出,每次最大接收数据的最后阶段如果满足以下条件,即:硬件平台是ZYNQ&UpdateTxSize=1&ByteCountVar == (XIICPS_FIFO_DEPTH + 1)则需要对ZYNQ的I2C接收部分重新设置。
0ca87a5986c44edda8df2ea0af6ac3b8.jpg
所以有了以下代码,这部分的代码中,首先会等待ZYNQ的I2C控制器等待接收FIFO满,之后再去判断下一次的传输。
c08e3567fa2f46819fb00143823e68c3.jpg
else分支MPSOC平台的处理:相比ZYNQ平台处理的复杂程度,对于MPSOC就简单多了,只要继续判断剩余传输的数据是否大于XIICPS_MAX_TRANSFER_SIZE,如果时则设置再一次接收的最大数据为ByteCountVar = (s32)XIICPS_MAX_TRANSFER_SIZE,并且设置UpdateTxSize=1,用以标识当本次数据传输完成后还要继续传输

1个小while循环接收有效数据:小while循环中接收数据到内存中,通过XIicPs_RecvByte(InstancePtr)读取数据。
2a7ee161b61846c3b150943bedb77d66.jpg
4.2:XIicPs_BusIsBusy(I2C_Ptr)函数
通过判断I2C状态寄存器,读取忙标志位判断I2C是否还在传输中。
0bb6c4ece18c482b9a82d9c0d43d379c.jpg
这个函数的寄存器介绍在I2cPs_wirte部分有介绍。
9中断模式读写EEPROM
9.1eeprom_intr读写模式
  1. #include "i2cps_intr.h"
  2. #include "sys_intr.h"
  3. #include "sleep.h"

  4. extern XIicPs  I2cInst0;
  5. extern XScuGic Intc;
  6. extern volatile u32 SendStatu;
  7. extern volatile u32 RevStatu;

  8. I2C_ADDR8 *I2C_WINST_8 = (void*)0x0800000;
  9. I2C_ADDR8 *I2C_RINST_8 = (void*)0x0810000;

  10. void init_intr_sys(void)
  11. {
  12.         Init_Intr_System(&Intc);//initial system interrupt

  13.         i2cps_init(&I2cInst0 ,IIC_DEVICE_ID0); //initial i2cps controller

  14.         i2cps_setup_IntrSystem(&Intc, &I2cInst0,IIC_INT_VEC_ID0); //setup i2c ps interrupt

  15.         Setup_Intr_Exception(&Intc);//setup system interrupt
  16. }

  17. int main(void)
  18. {
  19.         u32 i=0;
  20.         u32 j=0;
  21.         init_intr_sys();

  22.         while(1)
  23.         {
  24.                 //24c02 2kbits write(256 bytes)
  25.                 for(i=0; i<16*2; i++)//24cl02 has 16 pages as each page 16bytes
  26.                 {
  27.                         I2C_WINST_8->reg_addr[0]=i*8;
  28.                         for(j=0; j<8; j++)//every time write 8bytes data
  29.                                 I2C_WINST_8->reg_buf[j] = j + i*8;
  30.                         SendStatu = 1;
  31.                         while(SendStatu != S_IIC_TSUCCESS) //wait i2c controller not busy
  32.                         {
  33.                                 if(SendStatu == S_IIC_TERROR) break;
  34.                                 i2cps_wr8(&I2cInst0,I2C_WINST_8,8,0x50); //write 1byte address 8byes data
  35.                         }
  36.                         usleep(4000);
  37.                 }
  38.                 //24c02 2kbits read all
  39.                 I2C_RINST_8->reg_addr[0]=0;
  40.                 RevStatu = 1;
  41.                 while(RevStatu != S_IIC_RSUCCESS)//wait i2c controller not busy
  42.                 {
  43.                         if(RevStatu == S_IIC_RERROR) break;
  44.                         i2cps_rd8(&I2cInst0,I2C_RINST_8,256,0x50); //read all 256 bytes
  45.                 }
  46.                 for(i=0;i<256;i++)//compare write buffer and read buffer
  47.                         if(i!=I2C_RINST_8->reg_buf[i])
  48.                                 xil_printf("error addr%d=%d\r\n",i,I2C_RINST_8->reg_buf[i]);

  49.                 xil_printf("i2c test successfully!\r\n");
  50.                 sleep(1);
  51.         }
  52.     return 0;
  53. }
复制代码

9.2中断驱动程序分析
1:i2cps_intr.c驱动程序
Polled模式下,程序需要等待I2C总线操作完后才能进行其他的操作,这样会导致其他程序无法运行。相比polled模式,中断模式,可以几乎不影响其他程序的运行,所以需要多任务执行,并且需要尽量不影响其他程序的运行的方案中需要使用中断模式。
  1. #include "i2cps_intr.h"

  2. XIicPs  I2cInst0;

  3. volatile u32 SendStatu;
  4. volatile u32 RevStatu;
  5. volatile u32 SendComplete;
  6. volatile u32 RecvComplete;
  7. volatile u32 TotalErrorCount;

  8. int i2cps_init(XIicPs *I2C_Ptr,u16 DeviceId) //初始化I2C外设
  9. {
  10.         int Status;
  11.         XIicPs_Config *Config;

  12.         /*
  13.          * Initialize the IIC driver so that it's ready to use
  14.          * Look up the configuration in the config table, then initialize it.
  15.          */
  16.         Config = XIicPs_LookupConfig(DeviceId);
  17.         if (NULL == Config) {
  18.                 return XST_FAILURE;
  19.         }

  20.         Status = XIicPs_CfgInitialize(I2C_Ptr, Config, Config->BaseAddress);
  21.         if (Status != XST_SUCCESS) {
  22.                 return XST_FAILURE;
  23.         }
  24.         /*
  25.          * Set the IIC serial clock rate.
  26.          */
  27.         XIicPs_SetSClk(I2C_Ptr, IIC_SCLK_RATE); //设置I2C的总线时钟

  28.         return XST_SUCCESS;
  29. }

  30. void I2cPs_Handler(void *CallBackRef, u32 Event) //中断回调函数
  31. {
  32.         if (0 != (Event & XIICPS_EVENT_COMPLETE_RECV)){ //数据接收完毕
  33.                 RecvComplete = TRUE;
  34.         } else if (0 != (Event & XIICPS_EVENT_COMPLETE_SEND)) {//数据发送完毕
  35.                 SendComplete = TRUE;
  36.         } else if (0 == (Event & XIICPS_EVENT_SLAVE_RDY)){ //如果是其他中断,并且不是SLAVE准备好中断代表错误
  37.                 TotalErrorCount++;
  38.         }
  39. }

  40. int i2cps_setup_IntrSystem(XScuGic * GicInstancePtr , XIicPs *I2C_Ptr ,u16 I2cIntrId) //设置中断回调函数
  41. {
  42.         int Status;

  43.         Status = XScuGic_Connect(GicInstancePtr, I2cIntrId,
  44.                         (Xil_InterruptHandler)XIicPs_MasterInterruptHandler,
  45.                         (void *)I2C_Ptr);
  46.         if (Status != XST_SUCCESS) {
  47.                 return Status;
  48.         }

  49.         XIicPs_SetStatusHandler(I2C_Ptr, (void *)I2C_Ptr, I2cPs_Handler);

  50.         XScuGic_Enable(GicInstancePtr, I2cIntrId);
  51.         return XST_SUCCESS;
  52. }
  53. //中断方式写数据8bits数据
  54. void i2cps_wr8(XIicPs *I2C_Ptr, I2C_ADDR8 *MsgPtr , u16 byte_cnt ,u16 slave_addr)
  55. {
  56.         switch(SendStatu)
  57.         {
  58.         case S_IIC_TEN://write address
  59.                 if(XIicPs_BusIsBusy(I2C_Ptr))
  60.                 {

  61.                 }
  62.                 else
  63.                 {
  64.                         SendStatu = S_IIC_TBUSY;
  65.                         SendComplete = FALSE;
  66.                         XIicPs_MasterSend(I2C_Ptr, (u8 *)MsgPtr, byte_cnt + 1, slave_addr);
  67.                 }
  68.         break;

  69.         case S_IIC_TBUSY:
  70.                 if(SendComplete == FALSE)
  71.                 {
  72.                         if (0 != TotalErrorCount) {
  73.                                 xil_printf("I2C tx error! %d\r\n", TotalErrorCount);
  74.                                 SendStatu = S_IIC_TERROR ;
  75.                         }
  76.                 }
  77.                 else
  78.                         SendStatu = S_IIC_TSUCCESS;
  79.         break;

  80.         default:
  81.         break;
  82.         }

  83.         return ;
  84. }

  85. //中断方式读数据8bits数据
  86. void i2cps_rd8(XIicPs *I2C_Ptr, I2C_ADDR8 *MsgPtr , u16 byte_cnt ,u16 slave_addr)
  87. {
  88.         switch(RevStatu)
  89.         {
  90.         case S_IIC_TEN://write address
  91.                 if(XIicPs_BusIsBusy(I2C_Ptr))
  92.                 {

  93.                 }
  94.                 else
  95.                 {
  96.                         RevStatu = S_IIC_TBUSY;
  97.                         SendComplete = FALSE;
  98.                         XIicPs_MasterSend(I2C_Ptr, (u8 *)MsgPtr, 1, slave_addr);
  99.                 }
  100.         break;

  101.         case S_IIC_TBUSY:
  102.                 if(SendComplete == FALSE)
  103.                 {
  104.                         if (0 != TotalErrorCount) {
  105.                                 xil_printf("I2C rx error! %d\r\n", TotalErrorCount);
  106.                                 RevStatu = S_IIC_RERROR ;
  107.                         }
  108.                 }
  109.                 else
  110.                 {
  111.                         RevStatu = S_IIC_RCV;
  112.                 }
  113.         break;

  114.         case S_IIC_RCV:
  115.                 if (XIicPs_BusIsBusy(I2C_Ptr))// busy nop
  116.                 {

  117.                 }
  118.                 else
  119.                 {
  120.                         XIicPs_MasterRecv(I2C_Ptr, MsgPtr->reg_buf, byte_cnt, slave_addr);//recv data
  121.                         RevStatu  = S_IIC_RBUSY;// read busy
  122.                         RecvComplete = FALSE;
  123.                 }
  124.         break;

  125.         case S_IIC_RBUSY://wait
  126.                 if(RecvComplete == FALSE)
  127.                 {
  128.                         if (0 != TotalErrorCount) {
  129.                                 xil_printf("I2C rx error! %d\r\n", TotalErrorCount);
  130.                                 RevStatu = S_IIC_RERROR ;
  131.                         }
  132.                 }
  133.                 else
  134.                         RevStatu = S_IIC_RSUCCESS;
  135.                 break;

  136.         default:
  137.                 break;
  138.         }

  139.         return ;

  140. }
复制代码


其中数据结构I2C_ADDR8用于读写8bits的数据,定于如下
  1. typedef struct
  2. {
  3.         u8   reg_addr[1];
  4.         u8   reg_buf [2048];
  5. }I2C_ADDR8;
复制代码

以下内容是对I2C中断驱动程序底层部分的分析。

2:init_intr_sys函数
  1. void init_intr_sys(void)
  2. {
  3.         Init_Intr_System(&Intc);
  4.         I2cPs_init(&Iic ,IIC_DEVICE_ID);
  5.         I2cPs_Setup_IntrSystem(&Intc, &Iic,IIC_INT_VEC_ID);
  6.         Setup_Intr_Exception(&Intc);
  7. }
复制代码

此函数在程序文件eeprom_test.c中通过调用相关中断函数实现中断功能。为了实现多类型中断,用户必须根据以下方式设置中断。
3:Init_Intr_System(&Intc)函数
  1. int Init_Intr_System(XScuGic * IntcInstancePtr)
  2. {
  3.         int Status;

  4.         XScuGic_Config *IntcConfig;
  5.         /*
  6.          * Initialize the interrupt controller driver so that it is ready to
  7.          * use.
  8.          */
  9.         IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
  10.         if (NULL == IntcConfig) {
  11.                 return XST_FAILURE;
  12.         }

  13.         Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
  14.                                         IntcConfig->CpuBaseAddress);
  15.         if (Status != XST_SUCCESS) {
  16.                 return XST_FAILURE;
  17.         }
  18.         return XST_SUCCESS;
  19. }
复制代码

3.1:XScuGic_LookupConfig(INTC_DEVICE_ID)
a744ba2d6bf1410db4f9e94648fc6e3e.jpg
右击XScuGic_ConfigTable查看参数定义:
42ed5aaef17a4ec7a956be953388fd5a.jpg
可可以继续右击以下参数查看其定义
192196f34ea346289add02f314e137b0.jpg
可以看到这里定义了GIC中断控制器的基地址
a63a436584ae4847a8ec26d8632e4898.jpg
3.2:XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,IntcConfig->CpuBaseAddress)
中断部分主要内容是回调函数的设置,以下回调函数相关参数定义如下:
423bdc3528dd47a5a243d0ee648f2d3d.jpg
以下代码对所有为定义的全局中断进行定义回调函数和回调参数。
296377e681414a8a8279068422ceef24.jpg
StubHandler函数定义如下:
5cb8524108254576a803e2e8d9d77009.jpg
4:Setup_Intr_Exception函数
  1. void Setup_Intr_Exception(XScuGic * IntcInstancePtr)
  2. {
  3.         /* Enable interrupts from the hardware */
  4.         Xil_ExceptionInit();
  5.         Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
  6.                         (Xil_ExceptionHandler)XScuGic_InterruptHandler,
  7.                         (void *)IntcInstancePtr);
  8.         Xil_ExceptionEnable();
  9. }
复制代码

这个函数中对设置全局中断回调函数,以及使能全局中断
4.1:Xil_ExceptionInit()函数
这个函数目前说明都没做,只是未来保留兼容性,预留在这里。
939333d8eae8420bbcc3af0ce1a3544b.jpg
4.2:Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,(void *)IntcInstancePtr)函数
fda5a4f3e533464da42056f5dda7d51d.jpg
4.2.1XIL_EXCEPTION_ID_INT定义
933fa5b5604740b7a0798f37aac8d331.jpg
bacfe972f7a14430939cdb933dc2e758.jpg
4.2.2: XExc_VectorTableEntry定义
任何的中断都可以理解位异常处理,这里定义了MPSOC或者ZYNQ支持的异常处理类型。
4692464d5813425d88bb010ccbfb195c.jpg
4.2.3XScuGic_InterruptHandler函数
可以看到这个函数会根据读取到的中断号调用相应的回调函数,这个回调函数会在具体的外设中断初始化中设置。
e3b45efcc7974bae9d250f055437af47.jpg
4.2.4中断异常回调函数
47d4740b06b64af3ae695e23de18289c.jpg
由于XIL_EXCEPTION_ID_INT=2所以Xil_ExceptionNullHandler在中断产生的时候会被调用。
138a093be5f143f78866dce3eb9687bd.jpg
a04dd37338374d0cb5c466cd3ad73283.jpg
4.3:Xil_ExceptionEnable()函数
这条函数最终指向了汇编指令:
6ef5c12f01154c9e8eb913121fdca082.jpg

092aae7199c54866b96ebe94385711f5.jpg

587805aa45b2444ea4fd323bf130ca6e.jpg
5:I2cPs_init函数5.1:XIicPs_LookupConfig(DeviceId)函数
此函数在polled模式中已经详细介绍,这里不再重复。
5.2:XIicPs_CfgInitialize(I2C_Ptr, Config, Config->BaseAddress)函数
此函数在polled模式中已经详细介绍,这里不再重复。
5.3:XIicPs_SetSClk(I2C_Ptr, IIC_SCLK_RATE)
此函数在polled模式中已经详细介绍,这里不再重复。
5.4:XIicPs_SetStatusHandler(I2C_Ptr, (void *)I2C_Ptr, I2cPs_Handler)函数
此函数用于设置I2C的中断回调函数,通过以下定义可以确定回调函数是通过无符号的指针函数定义。
e86da09bb1c84722a49f936463bcd0e2.jpg

8600fdce1d9d4ef7901b6494320400d5.jpg

6:I2cPs_Setup_IntrSystem(&Intc, &Iic,IIC_INT_VEC_ID)6.1:XScuGic_Connect
(GicInstancePtr, I2cIntrId ,(Xil_InterruptHandler)XIicPs_MasterInterruptHandler, (void *)I2C_Ptr)
这里把GIC的49号中断回调函数XIicPs_MasterInterruptHandler和回调参数I2C_Ptr,这样当XIicPs_MasterInterruptHandler函数回调的时候,可以通过参数I2C_Ptr继续回调。
65b51151e51f444b85f62414d2dcc8b6.jpg
读者可以看前面Setup_Intr_Exception函数中关于Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,(void *)IntcInstancePtr)函数的说明再看这里就能明白了。
6.2: XIicPs_MasterInterruptHandler
这个函数初步一看有点复杂,当I2C中断产生后,这个函数被调用。虽然有点复杂,但是当拆开分析并且结合前面的polled模式的代码分析,反而变的简单了,我们把这个函数代码分3个部分分析:1)发生部分;2)接收部分;3)中断回调部分
1)发生部分:只要数据没有发送完就会继续发送,如果发送完,设置StatusEvent |= XIICPS_EVENT_COMPLETE_SEND
74a45f65f0b24caa9b6c10e6bcdf68f2.jpg
2)接收部分:接收部分的代码和I2C 采用polled模式类似,可以阅读polled模式部分分析
e5f5bb716491482c9c465ac099136a1c.jpg
3)中断回调部分:当所有数据发送或者接收完,回调函数I2cPs_Handler 被执行以及StatusEvent参数被传递。

73d90544396f459e91e3a9ea65f0e506.jpg
7:I2cPs_Handler中断回调函数
这个函数是最终的回调函数,主要判断数据是否发送完毕,以及统计错误次数
  1. void I2cPs_Handler(void *CallBackRef, u32 Event)
  2. {
  3.         /*
  4.          * All of the data transfer has been finished.
  5.          */
  6.         //xil_printf("Event %d \r\n", Event);

  7.         if (0 != (Event & XIICPS_EVENT_COMPLETE_RECV)){
  8.                 RecvComplete = TRUE;
  9.         } else if (0 != (Event & XIICPS_EVENT_COMPLETE_SEND)) {
  10.                 SendComplete = TRUE;
  11.         } else if (0 == (Event & XIICPS_EVENT_SLAVE_RDY)){
  12.                 /*
  13.                  * If it is other interrupt but not slave ready interrupt, it is
  14.                  * an error.
  15.                  * Data was received with an error.
  16.                  */
  17.                 TotalErrorCount++;
  18.         }
  19. }
复制代码



10方案演示
10.1硬件准备
实验需要用到 JTAG 下载器、USB 转串口外设,另外需要把核心板上的 2P 模式开关设置到 JTAG 模式,即ON ON(注意新版本的 MLK_H3_CZ08-7100-MZ7100FC),支持 JTAG 模式,对于老版本的核心板,JTAG 调试的时候 一定要拔掉 TF 卡,并且设置模式开关为 OFF OFF)
2598d66857a94b03a5bb4849a8c140f5.jpg
10.2实验结果
afd6cd8d5978485a8aaa3d7e3516b41f.jpg




您需要登录后才可以回帖 登录 | 立即注册

本版积分规则