megacode/pic/MDR/I2C.c

162 lines
3.2 KiB
C

/* software bit banging implementation
* based on https://en.wikipedia.org/wiki/I2c example code
*/
#include "I2C.h"
// Hardware-specific support functions that MUST be customized:
#define I2CSPEED 100
void I2C_delay()
{
volatile int v = 0;
int i;
for (i=0; i < I2CSPEED/2; i++) {
v;
}
}
/* Set SCL as input and return current level of line, 0 or 1 */
bool read_SCL(void)
{
PORTB |= SCL; /* set as input */
WPUB |= SCL; /* enable pull-up */
return (PORTB&SCL);
}
/* Set SDA as input and return current level of line, 0 or 1 */
bool read_SDA(void)
{
PORTB |= SDA; /* set as input */
WPUB |= SDA; /* enable pull-up */
return (PORTB&SDA);
}
/* Actively drive SCL signal low */
void clear_SCL(void)
{
PORTB &= ~SCL; /* set as output */
PORTB &= ~SDA; /* set low */
}
/* Actively drive SDA signal low */
void clear_SDA(void)
{
PORTB &= ~SCL; /* set as output */
PORTB &= ~SDA; /* set low */
}
void arbitration_lost(void)
{
/* do nothing, since we are the only master */
}
bool started = false; // global data
void i2c_start_cond(void) {
if (started) { // if started, do a restart cond
// set SDA to 1
read_SDA();
I2C_delay();
while (read_SCL() == 0) { // Clock stretching
// You should add timeout to this loop
}
// Repeated start setup time, minimum 4.7us
I2C_delay();
}
if (read_SDA() == 0) {
arbitration_lost();
}
// SCL is high, set SDA from 1 to 0.
clear_SDA();
I2C_delay();
clear_SCL();
started = true;
}
void i2c_stop_cond(void){
// set SDA to 0
clear_SDA();
I2C_delay();
// Clock stretching
while (read_SCL() == 0) {
// add timeout to this loop.
}
// Stop bit setup time, minimum 4us
I2C_delay();
// SCL is high, set SDA from 0 to 1
if (read_SDA() == 0) {
arbitration_lost();
}
I2C_delay();
started = false;
}
// Write a bit to I2C bus
void i2c_write_bit(bool bit) {
if (bit) {
read_SDA();
} else {
clear_SDA();
}
I2C_delay();
while (read_SCL() == 0) { // Clock stretching
// You should add timeout to this loop
}
// SCL is high, now data is valid
// If SDA is high, check that nobody else is driving SDA
if (bit && read_SDA() == 0) {
arbitration_lost();
}
I2C_delay();
clear_SCL();
}
// Read a bit from I2C bus
bool i2c_read_bit(void) {
bool bit;
// Let the slave drive data
read_SDA();
I2C_delay();
while (read_SCL() == 0) { // Clock stretching
// You should add timeout to this loop
}
// SCL is high, now data is valid
bit = read_SDA();
I2C_delay();
clear_SCL();
return bit;
}
// Write a byte to I2C bus. Return 0 if ack by the slave.
bool i2c_write_byte(bool send_start,
bool send_stop,
unsigned char byte) {
unsigned bit;
bool nack;
if (send_start) {
i2c_start_cond();
}
for (bit = 0; bit < 8; bit++) {
i2c_write_bit((byte & 0x80) != 0);
byte <<= 1;
}
nack = i2c_read_bit();
if (send_stop) {
i2c_stop_cond();
}
return nack;
}
// Read a byte from I2C bus
unsigned char i2c_read_byte(bool nack, bool send_stop) {
unsigned char byte = 0;
unsigned bit;
for (bit = 0; bit < 8; bit++) {
byte = (byte << 1) | i2c_read_bit();
}
i2c_write_bit(nack);
if (send_stop) {
i2c_stop_cond();
}
return byte;
}