Buscar este blog

miércoles, 12 de noviembre de 2014

PRACTICA # 17 Protocolo de comunicación I2C en PS


INTRODUCCION

I²C es un bus de comunicaciones en serie. Su nombre viene de Inter-Integrated Circuit (Inter-Circuitos Integrados). La versión 1.0 data del año 1992 y la versión 2.1 del año 2000, su diseñador es Philips. La velocidad es de 100 kbit/s en el modo estándar, aunque también permite velocidades de 3.4 Mbit/s. Es un bus muy usado en la industria, principalmente para comunicar microcontroladores y sus periféricos en sistemas integrados (Embedded Systems) y generalizando más para comunicar circuitos integrados entre sí que normalmente residen en un mismo circuito impreso.

La principal característica de I²C es que utiliza dos líneas para transmitir la información: una para los datos y otra para la señal de reloj. También es necesaria una tercera línea, pero esta sólo es la referencia (masa). Como suelen comunicarse circuitos en una misma placa que comparten una misma masa esta tercera línea no suele ser necesaria.

Las líneas se llaman:
·         SDA: datos
·         SCL: reloj
·         GND: tierra

Las dos primeras líneas son drenador abierto, por lo que necesitan resistencias de pull-up.

Los dispositivos conectados al bus I²C tienen una dirección única para cada uno. También pueden ser maestros o esclavos. El dispositivo maestro inicia la transferencia de datos y además genera la señal de reloj, pero no es necesario que el maestro sea siempre el mismo dispositivo, esta característica se la pueden ir pasando los dispositivos que tengan esa capacidad. Esta característica hace que al bus I²C se le denomine bus multimaestro.

Las transacciones en el bus I2C tienen este formato:
| start | A7 A6 A5 A4 A3 A2 A1 R/W | ACK | ... DATA ... | ACK | stop | idle |

·         El bus esta libre cuando SDA y SCL están en estado lógico alto.
·         En estado bus libre, cualquier dispositivo puede ocupar el bus I²C como maestro.
·         El maestro comienza la comunicación enviando un patrón llamado "start condition". Esto alerta a los dispositivos esclavos, poniéndolos a la espera de una transacción.
·         El maestro se dirige al dispositivo con el que quiere hablar, enviando un byte que contiene los siete bits (A7-A1) que componen la dirección del dispositivo esclavo con el que se quiere comunicar, y el octavo bit (A0) de menor peso se corresponde con la operación deseada (L/E), lectura=1 (recibir del esclavo) y escritura=0 (enviar al esclavo).
·         La dirección enviada es comparada por cada esclavo del bus con su propia dirección, si ambas coinciden, el esclavo se considera direccionado como esclavo-transmisor o esclavo-receptor dependiendo del bit R/W.
·         El esclavo responde enviando un bit de ACK que le indica al dispositivo maestro que el esclavo reconoce la solicitud y está en condiciones de comunicarse.
·         Seguidamente comienza el intercambio de información entre los dispositivos.
·         El maestro envía la dirección del registro interno del dispositivo que se desea leer o escribir.
·         El esclavo responde con otro bit de ACK
·         Ahora el maestro puede empezar a leer o escribir bytes de datos. Todos los bytes de datos deben constar de 8 bits, el número máximo de bytes que pueden ser enviados en una transmisión no está restringido, siendo el esclavo quien fija esta cantidad de acuerdo a sus características.
·         Cada byte leído/escrito por el maestro debe ser obligatoriamente reconocido por un bit de ACK por el dispositivo maestro/esclavo.
·         Se repiten los 2 pasos anteriores hasta finalizar la comunicación entre maestro y esclavo.
·         Aun cuando el maestro siempre controla el estado de la línea del reloj, un esclavo de baja velocidad o que deba detener la transferencia de datos mientras efectúa otra función, puede forzar la línea SCL a nivel bajo. Esto hace que el maestro entre en un estado de espera, durante el cual, no transmite información esperando a que el esclavo esté listo para continuar la transferencia en el punto donde había sido detenida.
·         Cuando la comunicación finaliza, el maestro transmite una "stop condition" para dejar libre el bus.
·         Después de la "stop condition", es obligatorio para el bus estar idle durante unos microsegundos.


  • Ejemplo de frame de datos:


  • OBJETIVO.


Para iniciar la comunicación I2C con la tarjeta Zedboard debemos tener previamente un esclavo y un maestro que valide el sistema de comunicación. Para ello se propone un esquema de microcontroladores como el de la siguiente imagen:



El esquemático está compuesto por dos microcontroladores PIC18F2550 configurados con oscilador interno a 8 Mhz, velocidad de 100 Khz en I2C y un led de estatus en el PIN_B7.

La dirección del esclavo es 0x38.

En la siguiente figura se muestran armados en un protoboard:



La idea es transferir 64 bytes del maestro al esclavo y este a su vez los retransmite nuevamente al maestro.

Maestro [0, 1, 2, … 63]  à Esclavo [0, 1, 2, … 63]  à Maestro [0, 1, 2, … 63] 

En la siguiente figura se muestra las transmisiones recibidas entre ambos:



  • Los códigos hexadecimales los puedes bajar de aquí:

ESCLAVO:
MAESTRO:

  • Ahora desarrollaremos la etapa del I2C Maestro con la Zedboard. Iniciando con la construcción de bloques en vivado de la siguiente manera:



  • La configuración en Zynq del I2C se muestra a continuación:


  • El código en SDK de eclipse es:


/***************************** Include Files **********************************/
#include "xparameters.h"
#include "xiicps.h"
#include "xil_printf.h"
#include "xgpio.h"


/************************** Constant Definitions ******************************/

/*
 * The following constants map to the XPAR parameters created in the
 * xparameters.h file. They are defined here such that a user can easily
 * change all the needed parameters in one place.
 */
#define IIC_DEVICE_ID               XPAR_XIICPS_0_DEVICE_ID
#define GPIO_EXAMPLE_DEVICE_ID  XPAR_AXI_GPIO_0_DEVICE_ID
#define LED_CHANNEL 1

/*
 * The slave address to send to and receive from.
 */
#define IIC_SLAVE_ADDR              0x38

#define IIC_SCLK_RATE               100000

/*
 * The following constant controls the length of the buffers to be sent
 * and received with the IIC.
 */
#define TEST_BUFFER_SIZE            64

/**************************** Type Definitions ********************************/


/************************** Function Prototypes *******************************/

int IicPsMasterPolledExample(u16 DeviceId);
/************************** Variable Definitions ******************************/

XIicPs Iic;             /**< Instance of the IIC Device */
XGpio Gpio;        // The Instance of the GPIO Driver

/*
 * The following buffers are used in this example to send and receive data
 * with the IIC.
 */
u8 SendBuffer[TEST_BUFFER_SIZE];    /**< Buffer for Transmitting Data */
u8 RecvBuffer[TEST_BUFFER_SIZE];    /**< Buffer for Receiving Data */


/******************************************************************************/
/**
*
* Main function to call the polled master example.
*
* @param    None.
*
* @return   XST_SUCCESS if successful, XST_FAILURE if unsuccessful.
*
* @note                 None.
*
*******************************************************************************/

int main(void)
{
            int Status;

            // Initialize the GPIO driver

            Status = XGpio_Initialize(&Gpio, GPIO_EXAMPLE_DEVICE_ID);
            if (Status != XST_SUCCESS) {
                 return XST_FAILURE;
            }

            // Clear the LEDs
            XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, 0);

            xil_printf(Microcarsil, 2014\n");

            xil_printf("IIC Master Polled Example Test \r\n");

            /*
             * Run the Iic polled example in master mode, specify the Device
             * ID that is specified in xparameters.h.
             */
            Status = IicPsMasterPolledExample(IIC_DEVICE_ID);
            if (Status != XST_SUCCESS) {
                        xil_printf("IIC Master Polled Example Test Failed\r\n");
                        return XST_FAILURE;
            }

            xil_printf("Successfully ran IIC Master Polled Example Test\r\n");
            return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* This function sends data and expects to receive data from slave as modular
* of 64.
*
* This function uses interrupt-driven mode of the device.
*
* @param    DeviceId is the Device ID of the IicPs Device and is the
*                       XPAR_<IICPS_instance>_DEVICE_ID value from xparameters.h
*
* @return   XST_SUCCESS if successful, otherwise XST_FAILURE.
*
* @note                 None.
*
*******************************************************************************/
int IicPsMasterPolledExample(u16 DeviceId)
{
            int Status;
            XIicPs_Config *Config;
            int Index;
            unsigned char i=1;

            /*
             * 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(&Iic, 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(&Iic);
            if (Status != XST_SUCCESS) {
                        return XST_FAILURE;
            }

            /*
             * Set the IIC serial clock rate.
             */
            XIicPs_SetSClk(&Iic, IIC_SCLK_RATE);


            /*
             * Initialize the send buffer bytes with a pattern to send and the
             * the receive buffer bytes to zero to allow the receive data to be
             * verified.
             */
            for (Index = 0; Index < TEST_BUFFER_SIZE; Index++)
            {
                        SendBuffer[Index] = Index;
                        RecvBuffer[Index] = 0;
            }

            while(1)
            {
                                    /*
                                     * Send the buffer using the IIC and ignore the number of bytes sent
                                     * as the return value since we are using it in interrupt mode.
                                     */
                            XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, i++);

                                    /*
                                    * Wait until bus is idle to start another transfer.
                                    */
                                    while (XIicPs_BusIsBusy(&Iic)) {                                        /* NOP */                           }

                                    Status = XIicPs_MasterSendPolled(&Iic, SendBuffer, TEST_BUFFER_SIZE, IIC_SLAVE_ADDR);
                                    if (Status != XST_SUCCESS)
                                    {
                                                xil_printf(" FALLA WRITE!\n");
                                    }
                                    else
                                    {
                                                xil_printf(" OK!\n");
                                    }

            usleep(10000);

                                    Status = XIicPs_MasterRecvPolled(&Iic, RecvBuffer,  TEST_BUFFER_SIZE, IIC_SLAVE_ADDR );
                                    if (Status != XST_SUCCESS)
                                    {
                                                            xil_printf(" FALLA READ!\n");
                                    }
                                    else
                                    {
                                                for(Index = 0; Index < TEST_BUFFER_SIZE; Index ++)
                                                {
                                                            xil_printf(" MASTER[%d] = %d\n",Index,RecvBuffer[Index]);
                                                 }
                                    }
                                    usleep(2000000);
            }

            return XST_SUCCESS;
}



  • Sustituimos el microcontrolador maestro pic18f2550 por la zedboard. Y verificamos la respuesta por medio de debug.



  • Funcionando correctamente en debug SDK:










2 comentarios:

  1. Hola, que buen contenido, ando en un proyecto similar para ingeniería electrónica, me preguntaba si aún tienes los códigos ya que los links están caídos ( ESCLAVO: https://mega.co.nz/#!0ptmDToT!W5s7HUwqK4eiqv8_0RzpiRxfdP1fWzDxiTboM9ItA9E
    MAESTRO:
    https://mega.co.nz/#!M0Ug2ahD!Z8VNIItNXI1AhZfonenHZ06DqamTvY5tp8gxfXsp2jA) Muchas gracias, saludos!

    ResponderBorrar
  2. Hola gracias por revisar el blog. Ya actualice los links caídos. Saludos

    ResponderBorrar