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
}
}
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
}
}
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.
justin
Woodrow Barlow
rdtsc
counter
que incrementa +1 en cadawhile
iteración. Encienda (y reiniciecounter
) el LED cada vez que se reciban datos, pero solo apáguelo después de que hayan comenzado 10,000 bucles while.