Subversion Repositories group.electronics

Rev

Rev 139 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * i2cbb.c
 *
 *      Bit Banging I2C code for any Atmel device
 *              The I2CBB_DELAY delay (in header) may need to be tweaked
 *              for different clock speeds, 10 if a fast enough
 *              speed, 100 if your having problems and dont need
 *              the speed. 7 seems to work well with an Attiny85
 *              running the PLL 16.5Mhz clock.
 *
 *              Base code from Raul
 *              http://codinglab.blogspot.com.au/2008/10/i2c-on-avr-using-bit-banging.html
 */

#include <avr/io.h>
#include <util/delay.h>


#include "config.h"
#include "avrutil.h"
#include "i2cbb.h"

#define I2CBB_DATA_HI()\
I2CBB_DDR &= ~ (1 << I2CBB_DAT);\
I2CBB_PORT |= (1 << I2CBB_DAT);
#define I2CBB_DATA_LO()\
I2CBB_DDR |= (1 << I2CBB_DAT);\
I2CBB_PORT &= ~ (1 << I2CBB_DAT);

#define I2CBB_CLOCK_HI()\
I2CBB_DDR &= ~ (1 << I2CBB_CLK);\
I2CBB_PORT |= (1 << I2CBB_CLK);
#define I2CBB_CLOCK_LO()\
I2CBB_DDR |= (1 << I2CBB_CLK);\
I2CBB_PORT &= ~ (1 << I2CBB_CLK);

void i2cbb_WriteBit(unsigned char c) {
    if (c > 0) {
        I2CBB_DATA_HI();
    }
    else {
        I2CBB_DATA_LO();
    }

    I2CBB_CLOCK_HI();
    while ((I2CBB_PIN & (1 << I2CBB_CLK)) == 0);
    _delay_us(I2CBB_DELAY);

    I2CBB_CLOCK_LO();
    //_delay_us(I2CBB_DELAY);

    if (c > 0) {
        _delay_us(I2CBB_DELAY); // Added
        I2CBB_DATA_LO();
    }

//    _delay_us(I2CBB_DELAY);
}

unsigned char i2cbb_ReadBit() {
    I2CBB_DATA_HI();
    _delay_us(I2CBB_DELAY);

    I2CBB_CLOCK_HI();

    while ((I2CBB_PIN & (1 << I2CBB_CLK)) == 0);
    _delay_us(I2CBB_DELAY);

    unsigned char c = I2CBB_PIN;

    I2CBB_CLOCK_LO();
    //_delay_us(I2CBB_DELAY);

    return (c >> I2CBB_DAT) & 1;
}

// Inits bitbanging port, must be called before using the functions below
//
void i2cbb_Init() {
    I2CBB_PORT &= ~ ((1 << I2CBB_DAT) | (1 << I2CBB_CLK));

    I2CBB_CLOCK_HI();
    I2CBB_DATA_HI();

    _delay_us(I2CBB_DELAY);
}

// Send a START Condition
//
void i2cbb_Start() {
    // set both to high at the same time
    I2CBB_DDR &= ~ ((1 << I2CBB_DAT) | (1 << I2CBB_CLK));
    _delay_us(I2CBB_DELAY);

    I2CBB_DATA_LO();
    _delay_us(I2CBB_DELAY);

    I2CBB_CLOCK_LO();
    _delay_us(I2CBB_DELAY);
}

// Send a STOP Condition
//
void i2cbb_Stop() {
    I2CBB_DATA_LO();
    I2CBB_CLOCK_LO();
    _delay_us(I2CBB_DELAY);
    _delay_us(I2CBB_DELAY);

    I2CBB_CLOCK_HI();
    _delay_us(I2CBB_DELAY);
    _delay_us(I2CBB_DELAY);

    I2CBB_DATA_HI();
    //_delay_us(I2CBB_DELAY);
}

// write a byte to the I2C slave device

unsigned char i2cbb_Write(unsigned char c) {
        uint8_t i;
    for (i=0; i < 8; i++) {
        i2cbb_WriteBit(c & 128);
        c <<= 1;
    }
    return i2cbb_ReadBit();
    //return 0;
}


// read a byte from the I2C slave device
//
unsigned char i2cbb_Read(unsigned char ack) {
    unsigned char res = 0;
    uint8_t i;
    for (i = 0; i < 8; i++) {
        res <<= 1;
        res |= i2cbb_ReadBit();
    }

    if (ack > 0) {
        i2cbb_WriteBit(0);
    }
    else {
        i2cbb_WriteBit(1);
    }

    //_delay_us(I2CBB_DELAY);

    return res;
}