#include "i2c.h" #include "rcc.h" #include "stm32.h" #include "thread.h" I2C* I2C::self; template <> void interrupt() { I2C::self->irq_ev(); } template <> void interrupt() { I2C::self->irq_er(); } void I2C::irq_ev() { uint32_t sr1 = I2C1.SR1; 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"); I2C1.CR1 = 0x8000; I2C1.CR1 = 0; 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) { addr = addr_; writing = 2; reading = 0; volatile uint8_t buf[] = {reg, data}; write_p = buf; busy = 1; I2C1.CR1 |= 0x100; while(busy) { Thread::yield(); } /* while(!(I2C1.SR1 & 0x01)); // Wait for SB. I2C1.DR = (addr << 1) | 0; while (!(I2C1.SR1 & 0x02)); // Wait for ADDR. I2C1.SR2; I2C1.DR = reg; while (!(I2C1.SR1 & 0x80)); // Wait for TxE. I2C1.DR = data; while (!(I2C1.SR1 & 0x04)); // Wait for BTF. I2C1.CR1 |= 0x200;*/ } 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) { Thread::yield(); } /* I2C1.CR1 |= 0x100; while(!(I2C1.SR1 & 0x01)); // Wait for SB. I2C1.DR = (addr << 1) | 0; while (!(I2C1.SR1 & 0x02)); // Wait for ADDR. I2C1.SR2; I2C1.DR = reg; while (!(I2C1.SR1 & 0x80)); // Wait for TxE. I2C1.CR1 |= 0x100; while(!(I2C1.SR1 & 0x01)); // Wait for SB. I2C1.DR = (addr << 1) | 1; 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. I2C1.CR1 &= ~0x400; // Clear ACK. *buf++ = I2C1.DR; len--; I2C1.CR1 |= 0x200; // Set STOP. *buf++ = I2C1.DR; len--; } else { while (!(I2C1.SR1 & 0x40)); // Wait for RxNE. *buf++ = I2C1.DR; len--; } } */ }