pic16 i2c interrupt

0

Estoy intentando usar el Configurador de Código de Microchip para establecer una comunicación I2C entre dos PIC16F1827. Mi aplicación es muy similar al ejemplo que dan con una EEPROM emulada. He usado Proteus para simular el código, pero el resultado no fue satisfactorio. Así que tengo una pregunta y un problema:

  • En el código maestro, la dirección EEPROM tiene dos bytes, pero en el código esclavo parece ser solo un byte. No entiendo por qué. He cambiado el código maestro y parece que está bien ahora, pero ¿puede alguien explicarme por qué el código maestro y el código esclavo no son compatibles?

  • Mi problema real es sobre las interrupciones I2C durante la lectura. En realidad, cuando intento hacer una lectura de un byte (supongo que la dirección EEPROM ya está configurada en el valor correcto), se producen dos interrupciones. El primero después del Ack, luego se establece el bit R_nW, los bits D_nA y ACKSTAT están claros. Luego, después de NAck (cuando el maestro recibió suficientes bytes, en este caso solo uno), se produce una segunda interrupción. Pero el bit R_nW es borrado por hardware. Por lo tanto, no puedo usar el caso "I2C1_SLAVE_READ_COMPLETED". Peor aún, se ejecuta el caso "I2C1_SLAVE_WRITE_COMPLETED". No entiendo por qué ocurre esto. Por supuesto, puedo cambiar el I2C1_ISR para evitar este problema, pero no creo que sea el mejor para resolver el problema.

Estaría muy agradecido si alguien me puede ayudar. :)

EDITAR: Siguiendo la sugerencia de Olin Lathrop, he escrito el código sin usar el MCC. Todavía tengo el mismo problema.

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <xc.h>

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection->INTOSC oscillator: I/O function on CLKIN pin
#pragma config WDTE = ON    // Watchdog Timer Enable->WDT disabled
#pragma config PWRTE = OFF    // Power-up Timer Enable->PWRT disabled
#pragma config MCLRE = OFF    // MCLR Pin Function Select->MCLR/VPP pin function is digital input
#pragma config CP = OFF    // Flash Program Memory Code Protection->Program memory code protection is disabled
#pragma config CPD = OFF    // Data Memory Code Protection->Data memory code protection is disabled
#pragma config BOREN = OFF    // Brown-out Reset Enable->Brown-out Reset disabled
#pragma config CLKOUTEN = OFF    // Clock Out Enable->CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin
#pragma config IESO = OFF    // Internal/External Switchover->Internal/External Switchover mode is disabled
#pragma config FCMEN = OFF    // Fail-Safe Clock Monitor Enable->Fail-Safe Clock Monitor is disabled

// CONFIG2
#pragma config WRT = OFF    // Flash Memory Self-Write Protection->Write protection off
#pragma config PLLEN = OFF    // PLL Enable->4x PLL disabled
#pragma config STVREN = OFF    // Stack Overflow/Underflow Reset Enable->Stack Overflow or Underflow will not cause a Reset
#pragma config BORV = LO    // Brown-out Reset Voltage Selection->Brown-out Reset Voltage (Vbor), low trip point selected.
#pragma config LVP = OFF    // Low-Voltage Programming Enable->High-voltage on MCLR/VPP must be used for programming

#define RX_ELMNTS   32
unsigned uint8_t I2C_slave_address = 0x03;         // slave address

volatile unsigned char I2C_Array[RX_ELMNTS] =   // array for master to write to
{0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88,
0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0xFA,
0xEA, 0xDA, 0xCA, 0xBA, 0xFB, 0xFC, 0xFD, 0xFE,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};

unsigned int index_i2c = 0;            // used as an index pointer in array
unsigned char junk = 0;                // used to place unnecessary data
unsigned char first = 1;               // used to determine whether data address 
                                       // location or actual data
unsigned char clear = 0x00;      // used to clear array location once
                                 // it has been read

void initialize(void);                 // Initialize routine

void main(void) {
    initialize();               // call initialize routine
    while (1)                   // main while() loop
    {
        asm("CLRWDT");          // while here just clear WDT and wait for
    }                           // the ISR to be called
} // end main

void initialize(void) {
    //uC SET UP
    OSCCON = 0b01111010;        // Internal OSC @ 16MHz
    OPTION_REG = 0b11010111;    // WPU disabled, INT on rising edge, FOSC/4
                                // Prescaler assigned to TMR0, rate @ 1:256
    WDTCON = 0b00010111;        // prescaler 1:65536 
                                // period is 2 sec(RESET value)
    PORTB = 0x00;               // Clear PORTC
    LATB = 0x00;                // Clear PORTC latches
    TRISB = 0b00010010;         // Set RC3, RC4 as inputs for I2C

    //I2C SLAVE MODULE SET UP
    SSP1STAT = 0b10000000;       // Slew rate control disabled for standard 
                                // speed mode (100 kHz and 1 MHz)
    SSP1CON1 = 0b00110110;       // Enable serial port, I2C slave mode, 
                                // 7-bit address
    SSP1CON2bits.SEN = 1;        // Clock stretching is enabled
    //SSP1CON3bits.BOEN = 1;       // SSPBUF is updated and NACK is generated 
                                // for a received address/data byte,
                                // ignoring the state of the SSPOV bit 
                                // only if the BF bit = 0
    //SSP1CON3bits.SDAHT = 1;      // Minimum of 300 ns hold time on SDA after 
                                // the falling edge of SCL
    //SSP1CON3bits.SBCDE = 1;      // Enable slave bus collision detect interrupts
    SSP1ADD = I2C_slave_address << 1; // Load the slave address
    SSP1IF = 0;                  // Clear the serial port interrupt flag
    BCL1IF = 0;                  // Clear the bus collision interrupt flag
    BCL1IE = 1;                  // Enable bus collision interrupts
    SSP1IE = 1;                  // Enable serial port interrupts
    PEIE = 1;                   // Enable peripheral interrupts
    GIE = 1;                    // Enable global interrupts
}//end initialize

void interrupt ISR() {
    if (SSP1IF) {
        if (SSP1STATbits.R_nW) {
            if (!SSP1STATbits.D_nA) {
                // read and adress
                SSP1BUF = I2C_Array[index_i2c++]; 
                SSP1CON1bits.CKP = 1; 
            }
            if (SSP1STATbits.D_nA) {
                //read and data
                SSP1BUF = I2C_Array[index_i2c++]; 
                SSP1CON1bits.CKP = 1; 
            }
        }
        if (!SSP1STATbits.R_nW) {
            if (!SSP1STATbits.D_nA) {
                //write and adress
                first = 1; 
                junk = SSP1BUF; 
                SSP1CON1bits.CKP = 1; 
            }
            if (SSP1STATbits.D_nA) {
                //write and data
                if (first) {
                    index_i2c = SSP1BUF; 
                    first = 0; 
                } else {
                    if (index_i2c < RX_ELMNTS) {
                        I2C_Array[index_i2c++] = SSP1BUF; 
                    } else {
                        junk = SSP1BUF; 
                    }
                }
                if (SSP1CON1bits.WCOL) {
                    SSP1CON1bits.WCOL = 0; 
                    junk = SSP1BUF; 
                }
                SSP1CON1bits.CKP = 1; 
            }
        }
    }
    if (BCL1IF) {
        junk = SSP1BUF; 
        BCL1IF = 0; 
        SSP1CON1bits.CKP = 1; 
    }
    SSP1IF = 0; 
}
    
pregunta ardayigit

1 respuesta

0

La hoja de datos es clara sobre cómo se comporta el bit R / nW.

  

Este bit contiene la información del bit R / nW después de la última coincidencia de dirección. Este bit solo es válido desde la coincidencia de dirección   al siguiente bit de inicio, bit de parada o no ACK bit .

Esto implica que tan pronto como el maestro envía el NACK para que se lean los datos, el bit R / nW ya no es válido.

También necesita un controlador para los casos ACK / NACK. Esto parece faltar en su código junto con cualquier colisión o comprobación de errores. Podría usar el registro ACKSTAT para averiguar en qué modo está.

Mi sugerencia final sería tener un conjunto global de variables de estado. Use estas variables desde dentro de la interrupción para saber en qué estado se encuentra y luego verifique los registros apropiados para averiguar a qué estado necesita ir.

Por ejemplo: estoy transmitiendo datos al modo maestro. Cuando se dispara la interrupción, verifique ACKSTAT, si se recibió la confirmación, prepárese para enviar otro byte. Si no se recibió el reconocimiento, se cambió el modo de transmisión.

    
respondido por el vini_i

Lea otras preguntas en las etiquetas