programación periférica iic de zynq 7000

Este artículo presenta principalmente la programación de periféricos i2c ps bajo zynq 7000. El experimento se completó el vivado 2018.3.

El requisito previo de este experimento es que haya realizado el experimento helloworld sdk de zynq 7000. Generalmente, los fabricantes de la placa de desarrollo los han proporcionado. Algunas configuraciones están relacionadas con el hardware utilizado. También puede consultar el experimento helloworld en mi blog  petalinux 2018.2.

Introducción al bus I2C

El bus I2C es un bus serie síncrono de dos hilos, bidireccional y sencillo desarrollado por Philips . Solo necesita dos cables para transferir información entre dispositivos conectados al bus. El bus I2C se divide en dos tipos de dispositivos: maestro y esclavo.

     El host se utiliza para iniciar el bus para transmitir datos y generar un reloj para sincronizar los esclavos En este momento, cualquier dispositivo direccionado se considera esclavo. En el bus, la relación entre el maestro y el esclavo, el envío y la recepción no es constante, sino que depende de la dirección de la transferencia de datos en este momento. Si el host desea enviar datos al dispositivo esclavo, el host primero se dirige al dispositivo esclavo, luego envía activamente los datos al dispositivo esclavo y, finalmente, el host finaliza la transferencia de datos; si el host desea recibir datos del dispositivo esclavo , el dispositivo maestro se dirige primero al dispositivo esclavo. Luego, el host recibe los datos enviados desde el dispositivo y, finalmente, el host finaliza el proceso de recepción. Bajo estas circunstancias. El anfitrión es responsable de generar el reloj de tiempo y terminar la transmisión de datos.

Tanto SDA (línea de datos en serie) como SCL (línea de reloj en serie) son líneas de E / S bidireccionales, y el circuito de interfaz es una salida de drenaje abierto , que debe conectarse a la fuente de alimentación VCC a través de una resistencia pull-up . Cuando el bus está libre. Ambas líneas son de alto nivel , los dispositivos externos conectados al bus son todos dispositivos CMOS y la etapa de salida también es un circuito de drenaje abierto. La corriente consumida en el bus es muy pequeña, por lo que el número de dispositivos expandidos en el bus está determinado principalmente por la carga capacitiva, ya que la interfaz de bus de cada dispositivo tiene una cierta capacitancia equivalente. La capacitancia en la línea afectará la velocidad de transmisión del bus. Cuando la capacitancia es demasiado grande, puede causar errores de transmisión. Por lo tanto, su capacidad de carga es de 400pF, por lo que se puede estimar la longitud permitida del bus y la cantidad de dispositivos conectados.

I2C para zynq 7000

Hay dos formas de controlar los periféricos iic (I2C) en zynq 7000, una utiliza el periférico PS i2c de zynq 7000 y la otra es axi4-i2c IP. Creo que el frente es un poco más simple, así que usé el método anterior.

Abra el proyecto helloworld previamente diseñado u otros proyectos en vivao. Si no tiene uno, cree uno primero, abra el diseño esquemático (diseño de bloque abierto), haga doble clic en zynq para abrir la configuración de zynq.

En la interfaz de configuración, seleccione I / OPins periféricos, en la fila I2C0, y EMIO en la columna. La razón por la que elegí esta forma es: no hay una opción MIO directa en mi placa de desarrollo, solo se pueden seleccionar los pines del puerto de salida de expansión. Tal vez su situación sea diferente, por lo que tiene una opción diferente. 

De hecho, esta elección está bien, pero sigo yendo a Configuración MIO para verificar, hay I2C0 correspondiente a EMIO

 

Después de modificar la configuración de esta manera, vuelva al diagrama esquemático. En este momento, puede ver que zynq tiene más IIC0 y luego mover la flecha del mouse a la interfaz. Cuando la flecha se convierta en una forma de lápiz, haga clic en el botón derecho del mouse y seleccione Hacer externo en el menú emergente para modificar el interfaz a una interfaz externa, puede ver A la interfaz externa denominada IIC_0

Guarde el esquema y realice las operaciones Generar productos de salida y Crear HDL Warpper en secuencia. Simplemente seleccione el archivo .bd en la fuente, haga clic con el botón derecho en el menú y selecciónelo.

Agregue el siguiente contenido en el archivo de restricción, el nombre del pin se puede ver en el archivo *** 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]

Los archivos de restricción también se pueden agregar de forma interactiva. Haga clic en ANÁLISIS RTL-> Abrir diseño elaborado en la barra de navegación izquierda y haga clic en "Aceptar" en el cuadro de diálogo emergente para abrir la ventana de diseño eléctrico. Haga clic en los puertos de E / S señalados por la flecha en la ventana abierta, y la ventana para configurar las restricciones de los pines se abrirá a continuación. Busque los pines relacionados con la IIC en la ventana y configúrelos. Así es como obtuve el script anterior.

Aquí C20 G17 es el número de pin, solo elijo 2 pines en el puerto de expansión de salida y luego conecto los pines correspondientes de los periféricos.

El diseño está completo, lo siguiente es generar flujo de bits, hardware de salida (debe incluir flujo de bits)

Diseño de software SDK

En Vivado, Archivo-> Iniciar SDK. Entramos en el diseño del software SDK.

En el SDK, cree un nuevo proyecto, seleccione la plantilla como hello world y ejecute helloworld para ver si todo es normal.

Elimine todo el contenido de helloworld.c y luego copie el siguiente código:

#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;
}

Mi código proviene de  periféricos IIC de manejo básico de ZYNQ  y luego lo modifiqué.
De hecho, el sdk también tiene código e instrucciones de programación. Abra system.mss en bsp, puede ver que hay un documento en ps7_i2c_0 y también puede importar ejemplos.

El directorio donde se almacenan los ejemplos se puede encontrar en Importar ejemplos. Simplemente haga clic en Directorio de ejemplos.

Comencé a probar xiicps_polled_master_example.c, pero no fue exitoso, por lo que mi ejemplo anterior es mejor.

 Prueba de ejecución de software

Para descartar errores de software, la copia debería compilar problemas.

Xilinx-> Programa FPGA

Luego, puede usar Depurar como o Ejecutar como para probar el software.

Para probar el software, hay algunas cosas a las que debe prestar atención.

1: Conecte el dispositivo i2c correctamente. El pin definido como SCL está conectado al pin SCL del dispositivo. SDA también está conectado correspondientemente, así como GND VCC.

2: Determine la dirección del dispositivo I2C, el mío es 0x40 

#define SLAVE_ADDR 0x40

Si es diferente, debe modificarlo aquí, si la dirección es incorrecta, no habrá respuesta.

3: Comprenda el significado de las direcciones de registro I2C. No todos los registros son iguales a la memoria, y el significado de lectura y escritura puede ser diferente. Probé el servocontrolador PCA9685.

// Prueba la lista de configuración
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} };











El contenido en configList es (dirección de registro en el frente, byte escrito en la parte posterior) tal tabla. Puede cambiar la longitud y el contenido de la mesa para que se adapte a su equipo.

Resultados de ejecución del programa:

Hola mundo1
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
Hola mundo2

Por supuesto, la pantalla es solo un aspecto, también depende del estado de la función de ejecución I2C. El código anterior puede controlar el servo para que funcione normalmente.

¡Eso es!

Supongo que te gusta

Origin blog.csdn.net/leon_zeng0/article/details/112307762
Recomendado
Clasificación