diff options
author | Vegard Storheil Eriksen <zyp@jvnv.net> | 2012-08-07 16:50:46 +0200 |
---|---|---|
committer | Vegard Storheil Eriksen <zyp@jvnv.net> | 2012-08-07 16:50:46 +0200 |
commit | e586c178073b9a0fee90d5fc8e795d266ebd7b7d (patch) | |
tree | ca9a9c40bab37d5af440c80833755223e2bdd946 /i2c/i2c.cpp |
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/i2c.cpp')
-rw-r--r-- | i2c/i2c.cpp | 145 |
1 files changed, 145 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 = ®_; + read_p = buf; + busy = 1; + + reg.CR1 |= 0x100; + + while(busy) { + Thread::yield(); + } +} |