Estoy intentando diseñar un programa que sondea un reloj en tiempo real (DS1307 +) cada segundo a través de I2C. El microcontrolador utilizado es dsPIC33EP64GS506. Utilizo un indicador de interrupción maestra para esperar hasta que finalice una tarea (condición de inicio o detención, envío de bytes, etc.). Sin embargo, parece que el indicador de interrupción maestra nunca se establece. Aquí hay un ejemplo mínimo (que no funciona) (al final de la publicación). Pasé por varias notas de aplicación, hojas de datos, etc., desafortunadamente, no logré resolver el problema.
El programa está bloqueado en la primera llamada de la función I2C1_Wait_While_Busy()
, que utiliza un indicador de interrupción maestra. Esta función se usa primero después de enviar una condición de ARRANQUE. Sin embargo, hay otra forma de verificar si la condición de INICIO finalizó:
while (I2C1CON1bits.SEN==1);
ya que el hardware borra el bit SEN después de que finaliza la condición de INICIO Si uso esto, entonces el programa salta a la siguiente línea, lo que indica que la condición de inicio finalizó independientemente del indicador de interrupción principal. En otras palabras, el programa está bloqueado en la próxima llamada de la función I2C1_Wait_While_Busy()
.
#include <xc.h>
// CPU operates at 120 MHz (60 MIPS)
#define FCY 60000000UL
#include <libpic30.h>
/* INITIALIZATION ROUTINES */
// Primary oscillator configuration
_FOSCSEL(FNOSC_FRC & IESO_OFF);
_FOSC(PLLKEN_ON & FCKSM_CSECMD & OSCIOFNC_OFF & POSCMD_XT);
// ALTI2C1_ON - I2C1 mapped to ASDA1/ASCL1 instead of SDA1/SCL1
// ALTI2C2_ON - I2C2 mapped to ASDA2/ASCL2 instead of SDA2/SCL2
// DBCC (not used)
_FDEVOPT(ALTI2C1_ON & ALTI2C2_ON);
void Init_Oscillator(void) {
/* Configure primary oscillator */
// FOSC = FIN/N1*M/N2
// FPLLI = FIN/N1
// N1 = PLLPRE+2, N1 in [2,33]
short N1 = 2;
CLKDIVbits.PLLPRE = N1-2; // N1=PLLPRE+2=2
// FVCO = FPLLI*M
// M = PLLDIV+2, M in [2,513]
short M = 48;
PLLFBD = M-2;
// FPLLO = FVCO/N2 (FPLLO=FOSC, FCY=FOSC/2)
// N2 = 2*(PLLPOST+1), N2 is {2,4,8}
short N2 = 2; // 2, 4 or 8
CLKDIVbits.PLLPOST = N2/2-1;
/* Initiate Clock Switch */
// Primary Oscillator with PLL (XTPLL, HSPLL, ECPLL)
__builtin_write_OSCCONH(0x03); // 0b011 -> NOSC<2:0> (OSCCON<10:8>)
// Request oscillator switch to selection specified by the NOSC<2:0> bits
__builtin_write_OSCCONL(OSCCON | 0x01); // 0b1 -> OSWEN<0> (OSCCON<0>)
// Wait for Clock switch to occur
// Current Oscillator Selection bits (read-only): COSC<2:0> (OSCCON<15:13>)
while (OSCCONbits.COSC!=0b011);
// Wait for PLL to lock
// PLL Lock Status bit (read-only): LOCK<0> (OSCCON<5>)
while (OSCCONbits.LOCK!=1);
/* Configure auxiliary oscillator */
// 120 MHz for proper PWM and ADC operation
// 7.37 MHz * 16 = 117.92 MHz
// Configure source to fast RC with APLL
ACLKCONbits.ASRCSEL = 0; // Don't use primary oscillator
ACLKCONbits.FRCSEL = 1; // Use fast RC oscillator as source
ACLKCONbits.SELACLK = 1; // Don't use primary PLL (FVCO)
ACLKCONbits.APSTSCLR = 0b111; // Divide-by-1 for PWM
ACLKCONbits.ENAPLL = 1; // Enable 16x APLL
// Wait for auxiliary PLL to lock
while (ACLKCONbits.APLLCK!=1);
}
void Init_Ports(void) {
// Set all ports to digital
ANSELA = 0x00;
ANSELB = 0x00;
ANSELC = 0x00;
ANSELD = 0x00;
// Set all ports to low
LATA = 0x00;
LATB = 0x00;
LATC = 0x00;
LATD = 0x00;
}
void Init_I2C(void) {
// Set baud rate (100 kHz)
I2C1BRG = 291; // FSCL=100kHz, Tdelay=250ns, FP/2=30MHz
// // Enable master interrupts
// IPC4bits.MI2C1IP = 1; // interrupt priority
// IEC1bits.MI2C1IE = 1; // enable interrupt
// IFS1bits.MI2C1IF = 0; // clear interrupt flag
// Enable I2C1
I2C1CON1bits.I2CEN = 1;
}
/* I2C ROUTINES */
void I2C1_Send_Byte(char byte) {
// Load I2C1 transmit register
I2C1TRN = byte;
}
void I2C1_Wait_While_Busy() {
// Check if I2C1 operation is done
while(IFS1bits.MI2C1IF==0);
// I2C1 is ready, clear interrupt flag
IFS1bits.MI2C1IF = 0;
}
void I2C1_Check_Ack_Status() {
if (I2C1STATbits.ACKSTAT==1) {
// NACK, do nothing
} else {
// ACK, do nothing
}
}
void I2C_RTC_Get_Date_and_Time() {
int b;
char data[7];
// Reset master interrupt flag
IFS1bits.MI2C1IF = 0;
// Wait for idle state
while(I2C1STATbits.TRSTAT);
/* MASTER TRANSMITTER / SLAVE RECEIVER */
// Send START condition
I2C1CON1bits.SEN = 1;
// I2C1_Wait_While_Busy();
while (I2C1CON1bits.SEN==1);
// Call the RTC in receive mode
I2C1_Send_Byte(0xD0); // 1101000|0
I2C1_Wait_While_Busy();
I2C1_Check_Ack_Status();
// Send the first RTC register address
I2C1_Send_Byte(0x00);
I2C1_Wait_While_Busy();
I2C1_Check_Ack_Status();
/* MASTER RECEIVER / SLAVE TRANSMITTER */
do {
// Send RESTART condition
I2C1CON1bits.RSEN = 1;
I2C1_Wait_While_Busy();
// Reverse direction (RTC is transmitter)
I2C1_Send_Byte(0xD1); // 1101000|1
I2C1_Wait_While_Busy();
} while (I2C1STATbits.ACKSTAT==1);
// Set to ACK bit
I2C1CON1bits.ACKDT = 0;
// Receive 7 bytes of data from RTC
for (b=0; b<7; b++) {
// Enable receive mode
I2C1CON1bits.RCEN = 1;
I2C1_Wait_While_Busy();
// Get byte from buffer
data[b] = I2C1RCV;
// Set to NACK bit after last received byte
if (b==6)
I2C1CON1bits.ACKDT = 1;
// Master acknowledge
I2C1CON1bits.ACKEN = 1;
}
// Send STOP condition
I2C1CON1bits.PEN = 1;
I2C1_Wait_While_Busy();
}
/* MAIN ROUTINE */
int main(void) {
Init_Oscillator();
Init_Ports();
Init_I2C();
while(1) {
// Poll RTC every second
I2C_RTC_Get_Date_and_Time();
__delay_ms(1000);
}
}