diff --git a/pic/MDR/I2C.c b/pic/MDR/I2C.c new file mode 100644 index 0000000..491f552 --- /dev/null +++ b/pic/MDR/I2C.c @@ -0,0 +1,161 @@ +/* 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; +} + diff --git a/pic/MDR/I2C.h b/pic/MDR/I2C.h new file mode 100644 index 0000000..2ad222a --- /dev/null +++ b/pic/MDR/I2C.h @@ -0,0 +1,16 @@ +#define __16f1847 +#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); diff --git a/pic/MDR/MDR.c b/pic/MDR/MDR.c index 9c42e4d..d00308a 100644 --- a/pic/MDR/MDR.c +++ b/pic/MDR/MDR.c @@ -19,6 +19,7 @@ #define __16f1847 #include #include +#include "I2C.h" /* the peripherals connected to the pins */ #define RELAY1 _RA2 /* pin 1 */ @@ -32,8 +33,8 @@ /* pin 9 is for a secret function, set by jumper WJ1, Vdd per default */ /* pin 10 is not connected */ /* pin 11 in not connected */ -#define MEMORY_CLK _RB6 /* pin 12, external 24LC256 EEPROM */ -#define MEMORY_DATA _RB7 /* pin 13, external 24LC256 EEPROM */ +/* pin 12, external 24LC256 EEPROM */ +/* pin 13, external 24LC256 EEPROM */ /* pin 14 is Vdd (5V) */ /* pin 15 is clonnected to external 4MHz ceramic resonator */ /* pin 16 is clonnected to external 4MHz ceramic resonator */ @@ -72,12 +73,13 @@ uint16_t __at(_CONFIG2) __CONFIG2 = _LVP_ON & /* enable low voltage programming /* initialize micro-conroller */ void init (void) { /* configure IO */ + NOT_WPUEN = 0; /* enable individual pull-ups */ ANSELA = 0; /* all pins are digital */ ANSELB = 0; /* all pins are digital */ TRISA |= RADIO; /* radio signal is an input */ TRISA &= ~(RELAY1|RELAY2); /* relays are outputs */ TRISB |= SWITCH1|SWITCH2; /* switches are inputs (with external pull-ups) */ - TRISB &= ~(LED|MEMORY_CLK|MEMORY_DATA); /* LED is I2C memory are outputs */ + TRISB &= ~(LED); /* LED is an output (used a sink) */ led_off(); /* starting state */ /* configure switches input */ @@ -109,6 +111,14 @@ void init (void) { TMR2IE = 1; /* enable timer 2 interrupt */ TMR2IF = 0; /* clear timer 2 interrupt */ + /* the MDR originally uses a PIC16C54A + * this does does not provide hardware I2C function + * they implement I2C in software (it's even a bit buggy) + * it even does not used open collectors (with pull-ups), but drives the signals + * this PIC16F1847 offers hardware I2C, but not on those pins + * so we also have to use software bit banging I2C + */ + PEIE = 1; /* enable peripheral interrupt (for timer 2) */ GIE = 1; /* golablly enable interrupts */ }