#include <xc.h>
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
// CONFIG
#pragma config FOSC = INTOSCCLK // Oscillator Selection bits (INTOSC oscillator: CLKOUT function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)
#pragma config BOREN = OFF // Brown-out Detect Enable bit (BOD disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB4/PGM pin has PGM function, low-voltage programming enabled)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
#define LED1def TRISB1
#define LED1 RB1
#define LED2def TRISB2
#define LED2 RB2
#define SDA RB4
#define SDA_IN TRISB4
#define SCL RB5
#define SCL_IN TRISB5
#define _XTAL_FREQ 4000000
void delay_ms(int milliseconds)
{
while(milliseconds > 0)
{
__delay_ms(1);
milliseconds--;
}
}
void Start(void)
{
SDA = 1;
delay_ms(1);
SCL = 1;
delay_ms(1);
SDA = 0;
delay_ms(1);
SCL = 0;
delay_ms(1);
}
void SendWriteAddress(void)
{
unsigned char writeAddress = 0xEE;
for (char i = 0; i < 8; i++)
{
if (writeAddress & 0x80)
SDA = 1;
else
SDA = 0;
delay_ms(1);
SCL = 1;
delay_ms(1);
SCL = 0;
delay_ms(1);
writeAddress <<= 1;
}
}
void SendRegisterAddress(void)
{
unsigned char registerAddress = 0xD0;
for (char i = 0; i < 8; i++)
{
if (registerAddress & 0x80)
SDA = 1;
else
SDA = 0;
delay_ms(1);
SCL = 1;
delay_ms(1);
SCL = 0;
delay_ms(1);
registerAddress <<= 1;
}
}
void SendReadAddress(void)
{
unsigned char readAddress = 0xD0;
for (char i = 0; i < 8; i++)
{
if (readAddress & 0x80)
SDA = 1;
else
SDA = 0;
delay_ms(1);
SCL = 1;
delay_ms(1);
SCL = 0;
delay_ms(1);
readAddress <<= 1;
}
}
unsigned char RecieveByte(void)
{
unsigned char recievedData = 0x00;
char binaryData[8] = {0};
SDA = 0;
SDA_IN = 1;
delay_ms(10); //Just in case.
for (char i = 0; i < 8; i++)
{
SCL = 1;
while (!SCL)
{
delay_ms(1);
}
binaryData[i] = SDA;
delay_ms(1);
SCL = 0;
}
delay_ms(10);
SDA_IN = 0;
//Convert binary to hex:
for (int i = 0; i < 8; i++)
{
recievedData <<= 1;
if (binaryData[i] == 1)
recievedData |= 1;
}
return recievedData;
}
void Stop(void)
{
SCL = 1;
delay_ms(1);
SDA = 1;
}
void main(void)
{
LED1def = 0;
LED1 = 0;
LED2def = 0;
LED2 = 0;
SDA_IN = 0;
SCL_IN = 0;
SDA = 1;
SCL = 1;
delay_ms(10);
unsigned char testVariable = 0xCC;
Start();
SendWriteAddress();
SendRegisterAddress();
Start();
SendReadAddress();
testVariable = RecieveByte();
Stop();
while(1)
{
for (char i = 0; i < 8; i++)
{
if (testVariable & 0x80)
{
LED1 = 1;
delay_ms(100);
LED1 = 0;
delay_ms(900);
}
else
{
LED2 = 1;
delay_ms(100);
LED2 = 0;
delay_ms(900);
}
testVariable <<= 1;
}
delay_ms(2000);
}
}
void i2c_init() {
// Configure RA1/SCL and RA2/SDA as digital inputs
TRISAbits.TRISA1 = 1;
TRISAbits.TRISA2 = 1;
ANSELAbits.ANSA1 = 0;
ANSELAbits.ANSA2 = 0;
// configure MSSP module for I2C
SSP1STAT = 0b10000000; // disable slew rate control and SMBus compatibility
SSP1CON1 = 0b00111000; // enable and configure module for I2C
SSP1ADD = 0; // should result in 100kHz I2C clock with 500kHz oscillator
}
uint8_t i2c_writeRead(uint8_t address, uint8_t *wData, uint8_t wLen, uint8_t *rData, uint8_t rLen) {
if ((wLen | rLen) == 0) { return 1; }
uint8_t success = 0;
address <<= 1;
if (wLen) {
SSP1CON2bits.SEN = 1; // send start
while (SSP1STATbits.S);
SSP1BUF = address; // address and write bit
while (SSP1STATbits.BF);
if (SSP1CON2bits.ACKSTAT) { goto end; }
do {
SSP1BUF = *wData;
++wData; --wLen;
while (SSP1STATbits.BF);
if (SSP1CON2bits.ACKSTAT) { goto end; }
} while (wLen);
}
if (rLen) {
if (wData) {
SSP1CON2bits.RSEN = 1; // send restart
while (SSP1CON2bits.RSEN);
} else {
SSP1CON2bits.SEN = 1; // send start
while (SSP1STATbits.S);
}
SSP1BUF = address | 1; // address and read bit
while (SSP1STATbits.BF);
if (SSP1CON2bits.ACKSTAT) { goto end; }
do {
SSP1CON2bits.RCEN = 1; // receive
while (SSP1CON2bits.RCEN);
*rData++ = SSP1BUF;
--rLen;
// send ACK (or NACK for last byte)
if (rLen) { SSP1CON2bits.ACKDT = 0; }
else { SSP1CON2bits.ACKDT = 1; }
SSP1CON2bits.ACKEN = 1;
while (SSP1CON2bits.ACKEN);
} while (rLen);
}
success = 1;
end:
SSP1CON2bits.PEN = 1;
while (SSP1CON2bits.PEN);
return success;
}
Or some reading material for this kind of situation?
The main reason im using bit-banging, my favorite phrase from now on , is my PIC does not support hardware I2C.
clock stretching can happen here, pretty common actually
clock stretching can happen here, pretty common actuallyDo you have an example which devices use clock stretching? I've used a number of different I2C chips, like clock expanders, accelerometers, and temperature sensors, and never saw it on the scope.
BTW: in normal mode for your bitbanging code, you shouldn't drive SCL or SDA to 1. Instead set the pin direction to input, if you want 1 and to output (with 0 for the output value), if you want 0.
May I ask why you want to do this is software? The PIC has hardware I2C and it would perform faster and better.