From 791a7820c3b0e495a0828e2ada5a8b6619220912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Wed, 30 Jul 2014 09:15:34 -0700 Subject: [PATCH] implemented own I2C protocol --- pic/MDR/I2C.c | 235 ++++++++++++++++++++++---------------------------- pic/MDR/I2C.h | 14 ++- pic/MDR/MDR.c | 17 ++-- 3 files changed, 116 insertions(+), 150 deletions(-) diff --git a/pic/MDR/I2C.c b/pic/MDR/I2C.c index 17a7dfe..c644dc9 100644 --- a/pic/MDR/I2C.c +++ b/pic/MDR/I2C.c @@ -1,159 +1,126 @@ -/* software bit banging implementation - * based on https://en.wikipedia.org/wiki/I2c example code - * - * there is no external pull-up on SCL, and the internal one is too weak - * SCL must be driven - * but that is ok as we are the only master - * there is an external pull-up on SDA, and the internal one is too weak - * SDA goes to 3V instead of 5V due to the external pull-up - * but the 24LC256 still seems to work - */ +#define __16f1847 +#include +#include #include "I2C.h" -// Hardware-specific support functions that MUST be customized: -/* even when doing nothing, the max speed will be 100kHz, while the chip supports 400kHz */ -#define I2C_delay() +/* set clock high (default state) */ +#define release_SCL() LATB |= SCL +/* set clock low */ +#define hold_SCL() LATB &= ~SCL +/* set data high (default state) */ +#define release_SDA() LATB |= SDA +/* set data low */ +#define hold_SDA() LATB &= ~SDA -/* Set SCL as input and return current level of line, 0 or 1 */ -bool read_SCL(void) +/* a small delay for better clock */ +#define WAIT 1 +/* +void delay(void) { - LATB |= SCL; /* set high (must be driven because no pull-up */ - return 1; + uint8_t wait=WAIT; + while (wait--); +} +*/ +#define delay() __asm\ + nop\ + nop\ + nop\ + nop\ +__endasm + +/* read SDA + * don't forget to set SDA as input in the begining + * are set SDA as output in the end + */ +uint8_t read_SDA(void) +{ + hold_SCL(); /* set clock to low for SDA to change */ + delay(); /* wait for SDA to change */ + delay(); + delay(); + release_SCL(); /* SDA should be valid when clock is high */ + delay(); + /* we don't verify the SCL state as only we can drive it */ + if (PORTB&SDA) { /* read bit */ + return 1; + } else { + return 0; + } } -/* Set SDA as input and return current level of line, 0 or 1 */ -bool read_SDA(void) +uint8_t read_ack(void) { + uint8_t ack = 0; TRISB |= SDA; /* set as input */ - return (PORTB&SDA)!=0; + ack = read_SDA(); + TRISB &= ~SDA; /* set back to output */ + hold_SCL(); + return ack; } -/* Actively drive SCL signal low */ -void clear_SCL(void) +/* start bit is when SDA goes low while SCL is high */ +void send_start(void) { - LATB &= ~SCL; /* set low */ + release_SCL(); + delay(); + release_SDA(); /* ensure SDA is high on the beginning */ + delay(); + hold_SDA(); } -/* Actively drive SDA signal low */ -void clear_SDA(void) +void send_stop(void) { - TRISB &= ~SDA; /* set as output */ - LATB &= ~SDA; /* set low */ + hold_SDA(); /* be sure SDA is low */ + release_SCL(); + delay(); + release_SDA(); /* set SDA high while SCL is high */ } -void arbitration_lost(void) +void send_1(void) { - /* do nothing, since we are the only master */ + hold_SCL(); /* value can change when SCL is low */ + delay(); + release_SDA(); /* set the right state */ + delay(); + release_SCL(); /* value will be read when SCL goes high */ } - -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) +void send_0(void) { - unsigned bit; - bool nack; - if (send_start) { - i2c_start_cond(); - } + hold_SCL(); /* value can change when SCL is low */ + delay(); + hold_SDA(); /* set the right state */ + delay(); + release_SCL(); /* value will be read when SCL goes high */ +} + +/* send byte and get ack */ +uint8_t send_byte(uint8_t byte) +{ + uint8_t bit; + /* send every bit, MSb first */ for (bit = 0; bit < 8; bit++) { - i2c_write_bit((byte & 0x80) != 0); + if (byte&0x80) { + send_1(); + } else { + send_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; + return read_ack(); } +uint8_t read_byte(void) +{ + uint8_t byte = 0; + uint8_t bit; + TRISB |= SDA; /* set as input */ + /* read 8 bits */ + for (bit=0; bit<8; bit++) { + byte += read_SDA(); + byte <<= 1; + } + TRISB &= ~SDA; /* set back to output */ + send_0(); /* send ack */ + return byte; +} diff --git a/pic/MDR/I2C.h b/pic/MDR/I2C.h index 2ad222a..19998ad 100644 --- a/pic/MDR/I2C.h +++ b/pic/MDR/I2C.h @@ -2,15 +2,11 @@ #include #include -#define bool uint8_t -#define true 1 -#define false 0 - #define SCL _RB6 /* pin 12, external 24LC256 I2C EEPROM memory */ #define SDA _RB7 /* pin 13, external 24LC256 I2C EEPROM memory */ -// 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); - -// Read a byte from I2C bus -unsigned char i2c_read_byte(bool nack, bool send_stop); +void send_start(void); +void send_stop(void); +uint8_t send_byte(uint8_t byte); +uint8_t read_byte(void); + diff --git a/pic/MDR/MDR.c b/pic/MDR/MDR.c index 4685a51..49d50f3 100644 --- a/pic/MDR/MDR.c +++ b/pic/MDR/MDR.c @@ -116,8 +116,8 @@ void init (void) { * this PIC16F1847 offers hardware I2C, but not on those pins * so we also have to use software bit banging I2C */ - TRISB &= ~SCL; /* set as output (it must be driven because there is no pull-up */ - LATB |= SCL; /* set clock as high (default pull-up state) */ + TRISB &= ~(SCL|SDA); /* set as output (it must be driven because there is no pull-up */ + LATB |= SCL|SDA; /* set as high (default pull-up state) */ PEIE = 1; /* enable peripheral interrupt (for timer 2) */ GIE = 1; /* golablly enable interrupts */ @@ -161,11 +161,14 @@ static void interrupt(void) __interrupt 0 if (switches&SWITCH1) { /* switch 1 changed */ if (PORTB&SWITCH1) { /* switch 1 released */ led_off(); /* reset LED */ - i2c_write_byte(1,0,0xa0); - i2c_write_byte(0,1,0xa0); -// i2c_write_byte(0,1,0x17); -// i2c_write_byte(1,0,0xa1); -// i2c_read_byte(0,0); + send_start(); + send_byte(0xa0); + send_byte(0x1c); + send_byte(0x17); + send_start(); + send_byte(0xa1); + read_byte(); + send_stop(); } else { /* switch 1 pressed */ led_on(); /* test LED */ }