diff options
-rw-r--r-- | i2c.cpp | 145 | ||||
-rw-r--r-- | i2c.h | 28 |
2 files changed, 156 insertions, 17 deletions
@@ -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 = ® + 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--; } } + */ } @@ -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); }; |