zynq 7000 的 iic 外设编程

本文主要介绍zynq 7000下的i2c ps 外设编程。实验是在vivado 2018.3 上完成的。

本实验的前提条件是你已经做过了zynq 7000的helloworld sdk 实验。一般开发板的厂家都提供了的,一些设置与所用硬件有关,也可参考我的博客 petalinux 2018.2 下的helloworld 实验

I2C总线简介

I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。I2C总线上分成主机和从机两种设备。

     主机用于启动总线传送数据并产生时钟以同步从机,此时任何被寻址的器件均被认为是从器件.在总线上主机和从机、发送和接收的关系不是恒定的,而取决于此时数据传送方向。如果主机要发送数据给从器件,则主机首先寻址从器件,然后主动发送数据至从器件,最后由主机终止数据传送;如果主机要接收从器件的数据,首先由主器件寻址从器件.然后主机接收从器件发送的数据,最后由主机终止接收过程。在这种情况下.主机负责产生定时时钟和终止数据传送。

SDA(串行数据线)和SCL(串行时钟线)都是双向I/O线,接口电路为开漏输出,需通过上拉电阻接电源VCC。当总线空闲时.两根线都是高电平,连接总线的外同器件都是CMOS器件,输出级也是开漏电路.在总线上消耗的电流很小,因此,总线上扩展的器件数量主要由电容负载来决定,因为每个器件的总线接口都有一定的等效电容.而线路中电容会影响总线传输速度.当电容过大时,有可能造成传输错误.所以,其负载能力为400pF,因此可以估算出总线允许长度和所接器件数量。

zynq 7000的I2C

在zynq 7000中有2种方式可以控制iic(I2C)外设,一种是利用zynq 7000的 PS 外设i2c ,还有一种是axi4-i2c IP。 我认为前面简单一点,所以采用的前面那种方式。

在vivao 里打开以前设计的helloworld 工程,或者其他工程,没有就先做一个,打开原理图设计(open block design),双击zynq 打开zynq设置。

在设置界面里,选择peripheral I/OPins,在I2C0 行里,列里 EMIO。我之所以这样选是:我的开发板里没有直接的MIO 可选,只能选扩展输出口的引脚。也许你的情况不一样,那就不一样的选择。 

其实这样选择就可以了,但我还是到MIO Configuration 查看下,这里有I2C0 对应EMIO

这样修改设置后回到原理图。这时可以看到zynq 多了IIC0,接下来将鼠标箭头移动到接口上,当看到箭头变为铅笔形状是点击鼠标右键,在弹出的菜单中选择 Make External 将接口修改为外部接口,可以看到外部接口名为IIC_0

保存原理图, 依次进行Generate Output Products和 Create HDL Warpper操作。就是在source 中选择.bd文件,右键出现菜单,选择他们。

在约束文件中添加如下内容,引脚的名字可以在***wrapper.v文件中看到。

set_property IOSTANDARD LVCMOS33 [get_ports IIC_0_scl_io]
set_property PACKAGE_PIN G17 [get_ports IIC_0_scl_io]
set_property IOSTANDARD LVCMOS33 [get_ports IIC_0_sda_io]
set_property PACKAGE_PIN C20 [get_ports IIC_0_sda_io]

set_property PULLUP true [get_ports IIC_0_scl_io]
set_property PULLUP true [get_ports IIC_0_sda_io]

约束文件其实也可交互式添加。点击左侧导航栏 RTL ANALYSIS->Open Elaborated Design,在弹出的对话框中点击“ OK” 打开电气设计窗口。在打开的窗口内点击箭头所指的 I/O Ports,会在下面打开配置引脚约束的窗口。在窗口里找到IIC 相关引脚并设置。我是这样得到上面脚本的。

在这里C20 G17 是引脚编号,我就是在输出扩展口中选2个脚,然后对应连接外设的相应引脚。

设计做好了,下面就是产生比特流,输出硬件(应该包含比特流)

SDK软件设计

在Vivado 里, File->Launch SDK。我们进入SDK 软件设计。

在SDK 里, 新建一个工程,选择模板为hello world , 可以运行下helloworld,看看显示等是否一切正常。

删除helloworld.c 里的所有内容, 然后复制如下代码:

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xiicps.h"
// IIC device ID
#define IIC_DEV_ID 			XPAR_PS7_I2C_0_DEVICE_ID
#define IIC_RATE 			200000
//7位设备地址
#define SLAVE_ADDR			0x40

#define MODE1	0
#define PRESCALE	254

static 	XIicPs IicPs;
static 	XIicPs_Config * IicPs_Cfg;

//测试配置列表
u8 configList[11][2]={
		{0x00,0x10},
		{0xfe,120},
		{0x00,0x00},
		{6,0x0},
		{7,0x0},
		{8,200},
		{9,0},
		{0x0A,0},
		{0x0B,0},
		{0x0c,150},
		{0x0d,1}
};

// 初始化IIC,并设置IIC速率
int initIic()
{
	int status;
	// 1.查找IIC设备
	IicPs_Cfg = XIicPs_LookupConfig(IIC_DEV_ID);
	// 2.初始化
	status = XIicPs_CfgInitialize(&IicPs, IicPs_Cfg, IicPs_Cfg->BaseAddress);
	if(status != XST_SUCCESS)
	{
		print("initial IIC failed \n");
		return XST_FAILURE;
	}
	//设置IIC速率
	status = XIicPs_SetSClk(&IicPs, IIC_RATE);
	if(status != XST_SUCCESS)
	{
		print("set IIC clock rate failed \n");
		return XST_FAILURE;
	}
	return XST_SUCCESS;
}

/******************************************************************
 * function IIC完成单个寄存器的配置
 *
 * @parameter : XIicPs * iicPs =====> IIC设备结构体
 * @parameter : u16 slaveAddr  =====> IIC从机设备地址
 * @parameter : u8 * Cfg_Ptr ====> 配置寄存器的指针
 *
 * @return s32 XST_SUCCESS or XST_FAILURE
 ******************************************************************/
s32 writeReg(XIicPs * iicPs, u16 slaveAddr, u8 * Cfg_Ptr)
{
	s32 status ;
	//IIC写入数据,从机地址,寄存器地址和写入的数据
	status = XIicPs_MasterSendPolled(iicPs, Cfg_Ptr, 2, SLAVE_ADDR );
	if (status != XST_SUCCESS)
	{
		printf("configure register failed! \n");
		return XST_FAILURE;
	}
	//两次IIC写入之间保持一定间隔
	usleep(8000);
	return status;
}
/******************************************************************
 * function IIC从寄存器中读出数据
 *
 * @parameter : XIicPs * iicPs =====> IIC设备结构体
 * @parameter : u16 slaveAddr  =====> IIC从机设备地址
 * @parameter : u8 * Cfg_Ptr ====> 配置寄存器的指针
 *
 * @return u8 registerData =====> 从寄存器中读出的数据
 ******************************************************************/
u8 readReg(XIicPs * iicPs, u16 slaveAddr, u8 * regAddr)
{
	s32 status ;
	u8 registerData;
	//发送设备地址,寄存器地址
	status = XIicPs_MasterSendPolled(iicPs, regAddr, 1, SLAVE_ADDR );
	if (status != XST_SUCCESS)
	{
		printf("configure register failed! \n");
		return XST_FAILURE;
	}
	//从寄存器中读出数据
	status = XIicPs_MasterRecvPolled(iicPs, &registerData, 1, SLAVE_ADDR);
	if (status != XST_SUCCESS)
	{
		printf("configure register failed! \n");
		return XST_FAILURE;
	}
	return registerData;
}

int main()
{
    init_platform();
    u8 dataBuf[31];
    u8 addr,obj;
    //初始化IIC
    int status = initIic();
    if(status != XST_SUCCESS)
    {
    	printf("initialize IIC failed \n");
    	return XST_FAILURE;
    }
    print("Hello World1\n\r");
    //依次写入配置列表
    for(int i=0; i < 11; i++ )
    {
    	writeReg(&IicPs, SLAVE_ADDR, configList[i]);
    	addr=configList[i][0];
    	obj=readReg(&IicPs, SLAVE_ADDR, &addr);
    	printf("i=%d, addr=%x, obj=%x\n",i,addr,obj);
    }
    //将值从寄存器中读出
    print("Hello World2\n\r");

    cleanup_platform();
    return 0;
}

我这代码来自 ZYNQ基础----驱动IIC外设 ,然后对他进行了修改。
其实sdk 本身也有代码,并且还有编程说明,打开bsp 下的 system.mss,可以看到ps7_i2c_0  这里有document,还可以Import Examples。

在Import Examples 里可以找到例子存放的目录。就是点击 Examples Directory。

我开始测试了xiicps_polled_master_example.c,但是不成功,还是我上面例子比较好。

 软件运行测试

排除软件错误,复制应该编译问题。

Xilinx->Program FPGA

然后就可以用 Debug As或者Run As 测试软件了。

要测试软件,有一些需要注意。

1:正确连接i2c 设备。定义为SCL 的脚连设备的SCL脚, SDA也是对应连接,还有GND VCC。

2:确定好I2C 设备的地址,我的是0x40 

#define SLAVE_ADDR            0x40

你的不同就要修改这里,如果地址不对,就不会有响应。

3:明白I2C 寄存器地址的含义,并不是所有寄存器都是内存一样,读写含义可能不同。我测试的是PCA9685 舵机控制器。

//测试配置列表
u8 configList[11][2]={
        {0x00,0x10},
        {0xfe,120},
        {0x00,0x00},
        {6,0x0},
        {7,0x0},
        {8,200},
        {9,0},
        {0x0A,0},
        {0x0B,0},
        {0x0c,150},
        {0x0d,1}
};

configList 里面的内容是(前面寄存器地址,后面写入字节) 这样一个表。你可以改变表的长度和内容,适应你设备的情况。

程序运行结果:

Hello World1
i=0, addr=0, obj=90
i=1, addr=fe, obj=78
i=2, addr=0, obj=80
i=3, addr=6, obj=0
i=4, addr=7, obj=0
i=5, addr=8, obj=c8
i=6, addr=9, obj=0
i=7, addr=a, obj=0
i=8, addr=b, obj=0
i=9, addr=c, obj=96
i=10, addr=d, obj=1
Hello World2

当然显示只是一方面,还要看I2C 运行功能状态。我上面代码可以控制舵机正常工作。

介绍到此!

猜你喜欢

转载自blog.csdn.net/leon_zeng0/article/details/112307762
今日推荐