aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKing Kévin <kingkevin@cuvoodoo.info>2014-07-28 17:13:27 -0700
committerKing Kévin <kingkevin@cuvoodoo.info>2014-07-28 17:13:27 -0700
commitdf5e3f7521937a3bb13d3fae35943edabba6c3d1 (patch)
treeac89d8ad18bf1da00ee14f4ae8d5d29b2f0ce201
parentd6cefb130823ab130f7e07dd0f80927509e06691 (diff)
add I2C library
-rw-r--r--pic/MDR/I2C.c161
-rw-r--r--pic/MDR/I2C.h16
-rw-r--r--pic/MDR/MDR.c16
3 files changed, 190 insertions, 3 deletions
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 <pic16f1847.h>
+#include <stdint.h>
+
+#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 <pic16f1847.h>
#include <stdint.h>
+#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 */
}