summaryrefslogtreecommitdiff
path: root/i2c
diff options
context:
space:
mode:
authorVegard Storheil Eriksen <zyp@jvnv.net>2012-08-07 16:50:46 +0200
committerVegard Storheil Eriksen <zyp@jvnv.net>2012-08-07 16:50:46 +0200
commite586c178073b9a0fee90d5fc8e795d266ebd7b7d (patch)
treeca9a9c40bab37d5af440c80833755223e2bdd946 /i2c
Initial import.
Most sources are split off from suzumebachi project revision 2fc77d2 as is with some path changes. New build rules introduced.
Diffstat (limited to 'i2c')
-rw-r--r--i2c/i2c.cpp145
-rw-r--r--i2c/i2c.h62
2 files changed, 207 insertions, 0 deletions
diff --git a/i2c/i2c.cpp b/i2c/i2c.cpp
new file mode 100644
index 0000000..a395b13
--- /dev/null
+++ b/i2c/i2c.cpp
@@ -0,0 +1,145 @@
+#include "i2c.h"
+
+#include <rcc/rcc.h>
+#include <os/thread.h>
+
+#if defined(STM32F1)
+I2C_t I2C1(0x40005400, 36000000, Interrupt::I2C1_EV, Interrupt::I2C1_ER);
+I2C_t I2C2(0x40005800, 36000000, Interrupt::I2C2_EV, Interrupt::I2C2_ER);
+#elif defined(STM32F4)
+I2C_t I2C1(0x40005400, 42000000, Interrupt::I2C1_EV, Interrupt::I2C1_ER);
+I2C_t I2C2(0x40005800, 42000000, Interrupt::I2C2_EV, Interrupt::I2C2_ER);
+//I2C_t I2C3(0x40005c00, 42000000, Interrupt::I2C3_EV, Interrupt::I2C3_ER);
+#endif
+
+void I2C_t::irq_ev() {
+ uint32_t sr1 = reg.SR1;
+ reg.SR2;
+
+ // EV5, SB = 1: Start condition sent.
+ if(sr1 & 0x01) {
+ // Send address.
+ reg.DR = (addr << 1) | (writing ? 0 : 1);
+ }
+
+ // EV6, ADDR = 1: Address sent.
+ if(sr1 & 0x02) {
+ if(writing) {
+ reg.DR = *write_p++;
+ writing--;
+ } else {
+ if(reading > 1) {
+ reg.CR1 |= 0x400; // Set ACK.
+ } else {
+ reg.CR1 |= 0x200; // Set STOP.
+ }
+ }
+ }
+
+ // EV7, RxNE = 1: Receive buffer not empty.
+ if(sr1 & 0x40) {
+ *read_p++ = reg.DR;
+ reading--;
+
+ if(reading == 1) {
+ // Unset ACK, set STOP.
+ reg.CR1 = (reg.CR1 & ~0x400) | 0x200;
+ }
+
+ if(reading == 0) {
+ busy = 0;
+ }
+ }
+
+ //reg.CR1 &= ~0x400;
+
+ // EV8, TxE = 1, BTF = 0: Transmit buffer empty, still writing.
+ if(sr1 & 0x80 && !(sr1 & 0x04)) {
+ if(writing) {
+ // Send data.
+ reg.DR = *write_p++;
+ writing--;
+ } else {
+ // All data sent.
+
+ if(reading) {
+ // Send repeat start.
+ reg.CR1 |= 0x100;
+ } else {
+ // Send stop.
+ reg.CR1 |= 0x200;
+ busy = 0;
+ }
+ }
+ }
+}
+
+void I2C_t::irq_er() {
+ handle_error();
+}
+
+void I2C_t::handle_error() {
+ reg.SR1;
+ reg.SR2;
+
+ //while(1);
+
+ reg.SR1 = 0;
+
+ reg.CR1 |= 0x200;
+ busy = 0;
+}
+
+void I2C_t::enable(Pin& scl, Pin& sda) {
+ RCC.enable(RCC.I2C1);
+ asm volatile("nop");
+
+ scl.set_af(4);
+ sda.set_af(4);
+ scl.set_type(Pin::OpenDrain);
+ sda.set_type(Pin::OpenDrain);
+ scl.set_mode(Pin::AF);
+ sda.set_mode(Pin::AF);
+
+ reg.CR1 = 0x8000;
+ reg.CR1 = 0;
+
+ reg.CR2 = 0x700 | (clk / 1000000);
+ reg.TRISE = clk / 1000000 + 1;
+ reg.CCR = clk / 2 / 100000;
+
+ Interrupt::enable(irq_ev_n, &I2C_t::irq_ev, this);
+ Interrupt::enable(irq_er_n, &I2C_t::irq_er, this);
+
+ reg.CR1 = 1;
+}
+
+void I2C_t::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;
+
+ reg.CR1 |= 0x100;
+
+ while(busy) {
+ Thread::yield();
+ }
+}
+
+void I2C_t::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;
+
+ reg.CR1 |= 0x100;
+
+ while(busy) {
+ Thread::yield();
+ }
+}
diff --git a/i2c/i2c.h b/i2c/i2c.h
new file mode 100644
index 0000000..f4d6949
--- /dev/null
+++ b/i2c/i2c.h
@@ -0,0 +1,62 @@
+#ifndef I2C_H
+#define I2C_H
+
+#include <stdint.h>
+#include <interrupt/interrupt.h>
+#include <gpio/pin.h>
+
+struct I2C_reg_t {
+ volatile uint32_t CR1;
+ volatile uint32_t CR2;
+ volatile uint32_t OAR1;
+ volatile uint32_t OAR2;
+ volatile uint32_t DR;
+ volatile uint32_t SR1;
+ volatile uint32_t SR2;
+ volatile uint32_t CCR;
+ volatile uint32_t TRISE;
+};
+
+class I2C_t {
+ private:
+ 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;
+
+ public:
+ I2C_reg_t& reg;
+ const uint32_t clk;
+ Interrupt::IRQ irq_ev_n;
+ Interrupt::IRQ irq_er_n;
+
+ I2C_t(uint32_t reg_addr, uint32_t bus_clk, Interrupt::IRQ ev_n, Interrupt::IRQ er_n) : reg(*(I2C_reg_t*)reg_addr), clk(bus_clk), irq_ev_n(ev_n), irq_er_n(er_n) {
+ reading = writing = 0;
+ }
+
+ void irq_ev();
+ void irq_er();
+
+ void handle_error();
+
+ void enable(Pin& scl, Pin& sda);
+
+ 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);
+};
+
+#if defined(STM32F1)
+extern I2C_t I2C1;
+extern I2C_t I2C2;
+#elif defined(STM32F4)
+extern I2C_t I2C1;
+extern I2C_t I2C2;
+extern I2C_t I2C3;
+#endif
+
+typedef I2C_t I2C;
+
+#endif