Módulo I2C no responde

Estoy escribiendo un esclavo I2C básico como parte de mi código en una MCU PIC16f de 20 pines y 8 bits . Lo mejor que puedo decir es que este microcontrolador tiene un módulo que observa el bus I2C y solo actualiza mis registros si hay un mensaje entrante dirigido al PIC; no puedo interactuar con ningún otro mensaje. El maestro es una CPU que ejecuta un kernel de Linux y algún otro software. Desde la CPU, puedo ejecutar un script de sondeo I2C que solo intenta leer desde todas las direcciones de dispositivos posibles en la cadena I2C (bus).

Tengo problemas para que el PIC responda a cualquier sondeo. He escrito el código I2C más simple que puedo administrar: los LED deberían parpadear cada vez que el dispositivo recibe algún tipo de mensaje I2C dirigido a él.

Conecté las líneas al osciloscopio y pude confirmar que el tráfico llega a través del reloj y las líneas de datos cuando pruebo la cadena.

Mi código está abajo. Mi compilador es la versión gratuita (sin optimizaciones) del XC8 de Microchip . Estoy usando la versión más nueva (v1.34) para Windows.

¿Me estoy perdiendo algún paso de inicialización o configuración necesario para que mi PIC vea los mensajes I2C entrantes?

#include <xc.h>                 /* XC8 General Include File */
#include <pic16lf1709.h>        /* Definitions of I/O pins */

#pragma config WDTE = OFF       // disable watchdog timer, for simplicity

// I2C address is 7 bits: 1111110
#define I2C_ADDRESS 0x7E

typedef unsigned char byte;

void main(void) {

    /* configure MSSP module */
    TRISBbits.TRISB4    = 1;    // set SDA to input
    TRISBbits.TRISB6    = 1;    // set SCL to input
    SSPCON1bits.SSPEN   = 1;    // enable SSP module
    SSPCON1bits.SSPM    = 0x6;  // SSP is in I2C slave mode, 7-bit addressing
    SSP1ADD             = I2C_ADDRESS<<1;  // set the device address (left-aligned)
    SSPCON1bits.CKP     = 1;    // release clock
    TRISC               = 0x00; // set LEDs to output
    PORTC               = 0xFF; // initialize LEDs to OFF

    while(1) {

        byte ssp_buf;           // for the data we read from the bus

        if(SSPSTATbits.BF) {    // if the I2C buffer is not empty
            PORTC = 0x00;       // turn on LEDs for a moment
            for(int i=0; i<100; i++) _delay(250);
            PORTC = 0xFF;       // turn them back off
            ssp_buf = SSPBUF;   // read the buffer
                                // BF flag is cleared by hardware
        }

        SSPCON1bits.CKP = 1;    // finally, release clock

    }

}
¿Notó que está cambiando la dirección I2C dos veces? (Una vez en la definición y una vez en la configuración del módulo). No tengo idea si eso es correcto o incorrecto, solo parece sospechoso.
@Justin ¡Uy! Lo arreglé y todavía no puedo parpadear.
100 ciclos de reloj? Si el PIC se ejecuta a 4MHz FOSC, la velocidad de instrucción es de 1M/seg. Un bucle de 100 * 2 (totalmente optimizado) será un mínimo de 200uS. El ojo humano tiene dificultades para ver las cosas a más de 50 mS. Podría estar parpadeando, pero demasiado rápido para verlo. Cambiaría el código para implementar una variable INT counterque incrementa +1 en cada whileiteración. Encienda (y reinicie counter) el LED cada vez que se reciban datos, pero solo apáguelo después de que hayan comenzado 10,000 bucles while.

Respuestas (2)

Existen varias funciones de conexión de E/S que utilizan diferentes PIC. El PIC que está utilizando tiene las siguientes características (como se copia del encabezado de los capítulos relevantes de la hoja de datos):

11.0 PUERTOS DE E/S

Cada puerto cuenta con seis registros estándar para su funcionamiento. Estos registros son:

  • Registros TRISx (dirección de datos)

  • Registros PORTx (lee los niveles en los pines del dispositivo)

  • Registros LATx (latch de salida)

  • INLVLx (control de nivel de entrada)

  • Registros ODCONx (drenaje abierto)

  • Registros SLRCONx (velocidad de respuesta

Algunos puertos pueden tener uno o más de los siguientes registros adicionales. Estos registros son:

  • ANSELx (selección analógica)

  • WPUx (tracción débil)

Los puertos que admiten entradas analógicas tienen un registro ANSELx asociado. Cuando se establece un bit ANSEL, el búfer de entrada digital asociado con ese bit se desactiva. La desactivación del búfer de entrada evita que los niveles de señal analógica en el pin entre una lógica alta y baja causen una corriente excesiva en el circuito de entrada lógica.

11.3.5 CONTROL ANALÓGICO

El registro ANSELB (Registro 11-12) se utiliza para configurar el modo de entrada de un pin de E/S a analógico. Establecer el bit ANSELB apropiado en alto hará que todas las lecturas digitales en el pin se lean como '0' y permitirá que las funciones analógicas en el pin funcionen correctamente. El estado de los bits ANSELB no tiene efecto sobre las funciones de salida digital. Un pin con TRIS claro y ANSELB configurado seguirá funcionando como una salida digital, pero el modo de entrada será analógico. Esto puede provocar un comportamiento inesperado al ejecutar instrucciones de lectura, modificación y escritura en el puerto afectado.

Nota: Los bits de ANSELB pasan por defecto al modo analógico después del reinicio. Para utilizar cualquiera de los pines como entradas digitales de uso general o periféricos, el software del usuario debe inicializar los bits ANSEL correspondientes en '0'.

12.0 MÓDULO DE SELECCIÓN DE PIN PERIFÉRICO (PPS)

El módulo Peripheral Pin Select (PPS) conecta entradas y salidas periféricas a los pines de E/S del dispositivo. Solo las señales digitales se incluyen en las selecciones. Todas las entradas y salidas analógicas permanecen fijas en sus pines asignados. Las selecciones de entrada y salida son independientes, como se muestra en el diagrama de bloques simplificado de la Figura 12-1.

12.2 Salidas PPS

Cada pin de E/S tiene un registro PPS con el que se selecciona la fuente de salida del pin. Con pocas excepciones, el control TRIS del puerto asociado con ese pin retiene el control sobre el controlador de salida del pin. Los periféricos que controlan el controlador de salida del pin como parte de la operación periférica anularán el control TRIS según sea necesario. Estos periféricos incluyen:

  • EUSART (funcionamiento síncrono)

  • MSSP (I2C)

  • COG (apagado automático)

12.3 Pines bidireccionales

Las selecciones de PPS para periféricos con señales bidireccionales en un solo pin deben realizarse de modo que la entrada PPS y la salida PPS seleccionen el mismo pin. Los periféricos que tienen señales bidireccionales incluyen:

  • EUSART (funcionamiento síncrono)

  • MSSP (I2C)

Debe leer las partes relevantes de la hoja de datos y configurar todos los registros de configuración de E/S necesarios para su configuración. En este caso, falta configurar ANSELB para habilitar las entradas digitales y configurar los registros PPS para enrutar las funciones de entrada y salida del módulo I2C a los pines deseados. Además, los registros PPS están protegidos contra cambios no deseados, por lo que hay un mecanismo de bloqueo que debe usar antes de poder acceder a los registros.

Después de realizar los cambios anteriores, el código debería verse así. Tenga en cuenta que dado que los mensajes I2C aún no se reconocen correctamente, es posible que el dispositivo no sea "visible" para el maestro, pero al menos debería ver la luz parpadear.

#include <xc.h>                 /* XC8 General Include File */
#include <pic16lf1709.h>        /* Definitions of I/O pins */

#pragma config WDTE = OFF

// I2C address is 7 bits: 1111110
#define I2C_ADDRESS 0x6E

typedef unsigned char byte;

void main(void) {

    /* configure ports */
    TRISBbits.TRISB4    = 1;    // set SDA to input
    TRISBbits.TRISB6    = 1;    // set SCL to input

    ANSELBbits.ANSB4    = 0;    // make sure SDA is set to digital

    TRISC               = 0x00; // set LEDs to output
    PORTC               = 0xFF; // initialize LEDs to OFF

    /* configure peripheral pin select */
    PPSLOCK             = 0x55;
    PPSLOCK             = 0xAA;
    PPSLOCK             = 0x00; // PPS is now unlocked
    SSPDATPPS           = 0x0C; // RB4 input is SDA (pg 140)
    SSPCLKPPS           = 0x0E; // RB6 input is SCL (pg 140)
    RB4PPS              = 0x11; // RB4 output is SDA (pg 141)
    RB6PPS              = 0x10; // RB6 output is SCL (pg 141)
    PPSLOCK             = 0x55;
    PPSLOCK             = 0xAA;
    PPSLOCK             = 0x01; // PPS is now locked

    /* configure MSSP module */
    SSPCON1bits.SSPEN   = 1;    // enable SSP module
    SSPCON1bits.SSPM    = 0x6;  // SSP is in I2C slave mode, 7-bit addressing
    SSP1ADD             = I2C_ADDRESS<<1;  // set the device address (left-aligned)
    SSPCON1bits.CKP     = 1;    // release clock

    while(1) {

        byte ssp_buf;

        if(SSPSTATbits.BF) {    // if the I2C buffer is not empty
            PORTC = 0x00;       // turn on LEDs for a moment
            for(int i=0; i<100; i++) _delay(250);
            PORTC = 0xFF;       // turn them back off
            ssp_buf = SSPBUF;   // read the buffer
                                // BF flag is cleared by hardware
        }

        SSPCON1bits.CKP = 1;    // finally, release clock

    }

}
¡gracias! configuraré los registros de pps por la mañana y le daré esa marca de verificación verde si funciona. en cuanto a los registros ansel, creo que digital es el valor predeterminado, pero seguiré adelante y seré explícito.
hmmmm. También me acabo de dar cuenta de que la dirección que estoy usando está reservada. arreglaré eso también.
¡Ajá! Tengo la luz para parpadear con sus sugerencias.

La capacitancia del bus afecta el tiempo, por lo que debe configurar correctamente los tiempos de retención de inicio/parada del SDA maestro, de modo que el esclavo pueda reconocer las condiciones de inicio y parada.

¿Puedo configurar el esclavo para que coincida con el maestro?
Agregaría que un osciloscopio generalmente no es el mejor instrumento para "ver datos I2C". Económicamente, pruebe con un Saleae o Rigol DS1054Z (con la opción de decodificación de datos) en su lugar.