Tenes 2 formas de realizar el codigo para el I2C, utilizando el modulo (hardware), o hacerlo por software.
Cual es la diferencia? Por software vos sos el encargado de manejar los pines, es como estas haciendo, poner el SCL a 1, o a 0, pasarlo a entrada, controlar los tiempos con NOPs o demoras. Etc. Esto es por software.
Por Hardware (usando el modulo) solo le indicas al modulo que de un Start, y el modulo se encarga de todo lo que hacias antes. es decir controlar SDA, SCL y ademas los tiempos. El unico "extra" que tenes usando el modulo es configurarlo. Y la unica limitante es que solo algunos pines pueden ser I2C, en cambio en software como se hace a mano y controlando el puerto, podes hacerlo con cualquier pin. Al codigo para usarlo por hardware ya lo tenias realizado en un codigo que me pasaste:
void Send_I2C_Data(unsigned int databyte)
{
PIR1bits.SSPIF=0; // clear SSP interrupt bit
SSPBUF = databyte; // send databyte
while(!PIR1bits.SSPIF); // Wait for interrupt flag to go high indicating transmission is complete
}
unsigned int Read_I2C_Data(void)
{
PIR1bits.SSPIF=0; // clear SSP interrupt bit
SSPCON2bits.RCEN=1; // set the receive enable bit to initiate a read of 8 bits from the serial eeprom
while(!PIR1bits.SSPIF); // Wait for interrupt flag to go high indicating transmission is complete
return (SSPBUF); // Data from eeprom is now in the SSPBUF so return that value
void Send_I2C_StartBit(void)
{
PIR1bits.SSP1IF=0; // clear SSP interrupt bit
SSPCON2bits.SEN=1; // send start bit
while(!PIR1bits.SSP1IF); // Wait for the SSPIF bit to go back high before we load the data buffer
}
void Send_I2C_StopBit(void)
{
PIR1bits.SSP1IF=0; // clear SSP interrupt bit
SSPCON2bits.PEN=1; // send stop bit
while(!PIR1bits.SSP1IF); // Wait for interrupt flag to go high indicating transmission is complete
}
void Send_I2C_ACK(void)
{
PIR1bits.SSP1IF=0; // clear SSP interrupt bit
SSPCON2bits.ACKDT=0; // clear the Acknowledge Data Bit - this means we are sending an Acknowledge or 'ACK'
SSPCON2bits.ACKEN=1; // set the ACK enable bit to initiate transmission of the ACK bit to the serial eeprom
while(!PIR1bits.SSP1IF); // Wait for interrupt flag to go high indicating transmission is complete
}
void Send_I2C_NAK(void)
{
PIR1bits.SSP1IF=0; // clear SSP interrupt bit
SSPCON2bits.ACKDT=1; // set the Acknowledge Data Bit- this means we are sending a No-Ack or 'NAK'
SSPCON2bits.ACKEN=1; // set the ACK enable bit to initiate transmission of the ACK bit to the serial eeprom
while(!PIR1bits.SSP1IF); // Wait for interrupt flag to go high indicating transmission is complete
}
El modulo del I2C suele esta con el de SPI juntos, y lo llaman MSSPx, Como el PIC posee 2 MSSP, voy a poner la x donde se necesite un 1, o un 2 para identificarlo, Asi que asi lo vas a encontrar en tu datasheet.
Segun el datasheet para configurar el modulo se necesita:
Poner los pines SDAx y SCLx como entradas.
Configurar los registros.
Antes de meternos en los registros, Como deberias saber en I2C hay un master y un slave. Tu micro va a ofciar de Master, por lo tanto vas a controlar el clock. Cuando estas como Maestro tenes que la bandera de interrupcion se activa (ojo la bandera se activa sin necesidad de que vos habilites la interrupcion) cuando ocurren las siguientes posibilidades:
The following events will cause the SSPx Interrupt Flag bit, SSPxIF, to be set (SSPx interrupt, if enabled):
• Start condition detected
• Stop condition detected
• Data transfer byte transmitted/received
• Acknowledge transmitted/received
• Repeated Start generated
Es decir cuando termina el Start que da el maestro, o el Stop, o termina de transmitir o recibir, cualquiera de esas termina con la bandera poniendose a 1 en maestro. Es por eso que tu codigo lee continuamente el SSPxIF para saber cuando termino.
Primero que nada tenemos que ajustar la velocidad de transmision. Que el I2C define 100Khz/400Khz y otro mas, pero esos son los mas comunes. Vamos a definirlo como 100KHz, segun uno de tus codigos, tenes un cristal de 16Mhz. Segun el datasheet necesitas cargar 0x27 en SSPxADD para tner 100Khz. Este registro tiene doble proposito, cuando es esclavo lo configuras para asignarle una direccion, cuando es maestro para controlar la frecuencia del clock
Ahora los demas registros:
SSPxSTAT = 0x80; // Desactivo el slew-rate para los 100Khz
SSPxCON1 = 0x38; // activo los pines para I2C, activo la salida de clock, lo configuro como I2C Master
SSPxMSK = 0x00 // Desactivo la comparacion con la direccion, ya que estamos como maestro.
SSPxCON2, tiene los bits que debes activar para inciar un START, para ver el ACK si llego o no, etc.
Respecto al codigo que pasaste antes, tendrias que hacerle unos cambios.. Por ejemplo:
SSPCON2bits.SEN=1; // send start bit
Ahora va a depender de que modulo uses, si el 1 o el 2.
SSP1CON2bits.SEN=1; // send start bit
SSP2CON2bits.SEN=1; // send start bit
Igual la bandera de interrupcion, la del SSP1 esta en PIR1 como tu codigo, y la del SSP2 esta en PIR4.
Hasta te da un "paso a paso" de como hacerlo, claramente vas a ver que cumple con todo lo que tenes que enviar en tu memoria:
24.6.6.4 Typical transmit sequence:
1. The user generates a Start condition by setting the SEN bit of the SSPxCON2 register.
2. SSPxIF is set by hardware on completion of the Start.
3. SSPxIF is cleared by software.
4. The MSSPx module will wait the required start time before any other operation takes place.
5. The user loads the SSPxBUF with the slave address to transmit.
6. Address is shifted out the SDAx pin until all eight bits are transmitted. Transmission begins as soon as SSPxBUF is written to.
7. The MSSPx module shifts in the ACK bit from the slave device and writes its value into the ACKSTAT bit of the SSPxCON2 register.
8. The MSSPx module generates an interrupt at the end of the ninth clock cycle by setting the SSPxIF bit.
9. The user loads the SSPxBUF with eight bits of data.
10. Data is shifted out the SDAx pin until all eight bits are transmitted.
11. The MSSPx module shifts in the ACK bit from the slave device and writes its value into the ACKSTAT bit of the SSPxCON2 register.
12. Steps 8-11 are repeated for all transmitted data bytes.
13. The user generates a Stop or Restart condition by setting the PEN or RSEN bits of the SSPxCON2 register. Interrupt is generated once the Stop/Restart condition is complete
Lo bueno es que tenes las funciones, tenes masomenos un paso a paso. Solo falta ver que es lo que pide la memoria.
Ejemplo para escribir Necesitas:
- Enviar el Start
- Enviar la direccion de la memoria + R/W , Devuelve un ACK
- Enviar la direccion del contenido, byte alto, Devuelve un ACK
- Enviar la direccion del contenido, byte bajo, Devuelve un ACK
- Enviar el/los datos, Devuelve un ACK
- Generar STOP.
Entonces usando las funciones de arriba, omitimos la lectura de los ACK recibidos de la memoria, si queres asegurarte, tenes que leer el bit ACKSTAT del registro SSPxCON2 para saber si envio un NACK o ACK:
Voy a suponer que, tenemos definidos la direccion de la eeprom y el WRITE/READ con defines, y que se quiere escrbir 0x50, en la direccion 0x123 (Supongamos que llegue ejje )
Send_I2C_StartBit();
Send_I2C_Data(DIRECCION_EEPROM || WRITE);
Send_I2C_Data(0x01); //Direccion alta
Send_I2C_Data(0x23); //Direccion baja
Send_I2C_Data(0x50); //Dato
Send_I2C_StopBit()
;
La lectura es un poco mas complicada, pero si ves el grafico de como leer la memoria ,te vas a dar cuenta que tenes que enviar paso a paso y que es lo que recibis. Y para recibir tenes que avisarle al modulo que vas a hacerlo, por eso pone a 1 un bit en la funcion de recepcion. Lo que hace el modulo es encargarse de generar el clk para recibir y una ves completa la recepcion poner a 1 la bandera de interrupcion.