summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVegard Storheil Eriksen <zyp@jvnv.net>2011-10-08 22:18:28 +0200
committerVegard Storheil Eriksen <zyp@jvnv.net>2011-10-08 22:18:28 +0200
commit8c7d77006f9427ddb8b563e928cfab319157866c (patch)
treebfa810ff5b211de497fb2ff6f3e8f6439879d738
parentfa6d7a961afa0ceedc8b6ac39ccba2a07421acfd (diff)
Interrupt driven I2C.
-rw-r--r--i2c.cpp145
-rw-r--r--i2c.h28
2 files changed, 156 insertions, 17 deletions
diff --git a/i2c.cpp b/i2c.cpp
index 77eb67a..03dd6b6 100644
--- a/i2c.cpp
+++ b/i2c.cpp
@@ -2,6 +2,93 @@
#include "stm32.h"
+I2C* I2C::self;
+
+template <>
+void interrupt<Interrupt::I2C1_EV>() {
+ I2C::self->irq_ev();
+}
+
+template <>
+void interrupt<Interrupt::I2C1_ER>() {
+ I2C::self->irq_er();
+}
+
+void I2C::irq_ev() {
+ uint32_t sr1 = I2C1.SR1;
+ uint32_t sr2 = I2C1.SR2;
+
+ // EV5, SB = 1: Start condition sent.
+ if(sr1 & 0x01) {
+ // Send address.
+ I2C1.DR = (addr << 1) | (writing ? 0 : 1);
+ }
+
+ // EV6, ADDR = 1: Address sent.
+ if(sr1 & 0x02) {
+ if(writing) {
+ I2C1.DR = *write_p++;
+ writing--;
+ } else {
+ if(reading > 1) {
+ I2C1.CR1 |= 0x400; // Set ACK.
+ } else {
+ I2C1.CR1 |= 0x200; // Set STOP.
+ }
+ }
+ }
+
+ // EV7, RxNE = 1: Receive buffer not empty.
+ if(sr1 & 0x40) {
+ *read_p++ = I2C1.DR;
+ reading--;
+
+ if(reading == 1) {
+ // Unset ACK, set STOP.
+ I2C1.CR1 = (I2C1.CR1 & ~0x400) | 0x200;
+ }
+
+ if(reading == 0) {
+ busy = 0;
+ }
+ }
+
+ //I2C1.CR1 &= ~0x400;
+
+ // EV8, TxE = 1, BTF = 0: Transmit buffer empty, still writing.
+ if(sr1 & 0x80 && !(sr1 & 0x04)) {
+ if(writing) {
+ // Send data.
+ I2C1.DR = *write_p++;
+ writing--;
+ } else {
+ // All data sent.
+
+ if(reading) {
+ // Send repeat start.
+ I2C1.CR1 |= 0x100;
+ } else {
+ // Send stop.
+ I2C1.CR1 |= 0x200;
+ busy = 0;
+ }
+ }
+ }
+}
+
+void I2C::irq_er() {
+ handle_error();
+}
+
+void I2C::handle_error() {
+ I2C1.SR1;
+ I2C1.SR2;
+
+ //while(1);
+ I2C1.CR1 |= 0x200;
+ busy = 0;
+}
+
void I2C::enable() {
RCC.enable(RCC.I2C1);
asm volatile("nop");
@@ -9,52 +96,79 @@ void I2C::enable() {
I2C1.CR1 = 0x8000;
I2C1.CR1 = 0;
- I2C1.CR2 = 36;
+ I2C1.CR2 = 0x700 | 36;
I2C1.TRISE = 37;
I2C1.CCR = 180;
+
+ Interrupt::enable(Interrupt::I2C1_EV);
+ Interrupt::enable(Interrupt::I2C1_ER);
+
I2C1.CR1 = 1;
}
-void I2C::write_reg(uint8_t addr, uint8_t reg, uint8_t data) {
+void I2C::write_reg(uint8_t addr_, uint8_t reg, uint8_t data) {
+ addr = addr_;
+ writing = 2;
+ reading = 0;
+ volatile uint8_t buf[] = {reg, data};
+ write_p = buf;
+ busy = 1;
+
I2C1.CR1 |= 0x100;
- while(!(I2C1.SR1 & 0x01));
+
+ while(busy);
+
+ /*
+ while(!(I2C1.SR1 & 0x01)); // Wait for SB.
I2C1.DR = (addr << 1) | 0;
- while (!(I2C1.SR1 & 0x02));
+ while (!(I2C1.SR1 & 0x02)); // Wait for ADDR.
I2C1.SR2;
I2C1.DR = reg;
- while (!(I2C1.SR1 & 0x80));
+ while (!(I2C1.SR1 & 0x80)); // Wait for TxE.
I2C1.DR = data;
- while (!(I2C1.SR1 & 0x04));
+ while (!(I2C1.SR1 & 0x04)); // Wait for BTF.
- I2C1.CR1 |= 0x200;
+ I2C1.CR1 |= 0x200;*/
}
-void I2C::read_reg(uint8_t addr, uint8_t reg, uint8_t len, uint8_t* buf) {
+void I2C::read_reg(uint8_t addr_, uint8_t reg, uint8_t len, uint8_t* buf) {
+ addr = addr_;
+ writing = 1;
+ reading = len;
+ write_p = &reg;
+ read_p = buf;
+ busy = 1;
+
+ I2C1.CR1 |= 0x100;
+
+ while(busy);
+
+ /*
I2C1.CR1 |= 0x100;
- while(!(I2C1.SR1 & 0x01));
+ while(!(I2C1.SR1 & 0x01)); // Wait for SB.
I2C1.DR = (addr << 1) | 0;
- while (!(I2C1.SR1 & 0x02));
+ while (!(I2C1.SR1 & 0x02)); // Wait for ADDR.
I2C1.SR2;
I2C1.DR = reg;
- while (!(I2C1.SR1 & 0x80));
+ while (!(I2C1.SR1 & 0x80)); // Wait for TxE.
I2C1.CR1 |= 0x100;
- while(!(I2C1.SR1 & 0x01));
+ while(!(I2C1.SR1 & 0x01)); // Wait for SB.
I2C1.DR = (addr << 1) | 1;
- while (!(I2C1.SR1 & 0x02));
+ while (!(I2C1.SR1 & 0x02)); // Wait for ADDR.
I2C1.SR2;
I2C1.CR1 |= 0x400; // Set ACK.
while(len) {
if(len == 3) {
- while (!(I2C1.SR1 & 0x04)); // Wait for BTF
+ while (!(I2C1.SR1 & 0x04)); // Wait for BTF.
I2C1.CR1 &= ~0x400; // Clear ACK.
*buf++ = I2C1.DR;
@@ -65,10 +179,11 @@ void I2C::read_reg(uint8_t addr, uint8_t reg, uint8_t len, uint8_t* buf) {
len--;
} else {
- while (!(I2C1.SR1 & 0x40)); // Wait for RXNE
+ while (!(I2C1.SR1 & 0x40)); // Wait for RxNE.
*buf++ = I2C1.DR;
len--;
}
}
+ */
}
diff --git a/i2c.h b/i2c.h
index 0681160..3d58056 100644
--- a/i2c.h
+++ b/i2c.h
@@ -3,12 +3,36 @@
#include <stdint.h>
+#include "interrupt.h"
+
class I2C {
+ friend void interrupt<Interrupt::I2C1_EV>();
+ friend void interrupt<Interrupt::I2C1_ER>();
+
+ private:
+ static I2C* self;
+
+ volatile uint8_t addr;
+ volatile uint8_t writing;
+ volatile uint8_t reading;
+ volatile uint8_t* write_p;
+ volatile uint8_t* read_p;
+
+ volatile bool busy;
+
+ void irq_ev();
+ void irq_er();
+ void handle_error();
+
public:
+ I2C() {
+ self = this;
+ }
+
void enable();
- void write_reg(uint8_t addr, uint8_t reg, uint8_t data);
- void read_reg(uint8_t addr, uint8_t reg, uint8_t len, uint8_t* buf);
+ void write_reg(uint8_t addr_, uint8_t reg, uint8_t data);
+ void read_reg(uint8_t addr_, uint8_t reg, uint8_t len, uint8_t* buf);
};