/* * i2c.c * * routines more or less copied from WCH example code, then fucked around with to work. * * these routines have serious issues. * - any i2c issue may lock up the machine. * - timeout handlers are hastily added and may have problems. * - there's no error handling of any kind * - the library code makes some serious assumptions re: flags * - it's a shitpile of polling */ #include #define I2C_TIMEOUT 0xefff #define I2C_TIMEOUT_ACK_POLL 0x180 static uint16_t timeout; void i2c_init() { I2C_InitTypeDef i2c = {0}; // ensure GPIO pins are configured before initializing i2c.I2C_ClockSpeed = 666666; i2c.I2C_Mode = I2C_Mode_I2C; i2c.I2C_DutyCycle = I2C_DutyCycle_16_9; i2c.I2C_OwnAddress1 = 0x7f; i2c.I2C_Ack = I2C_Ack_Enable; i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_Init(I2C1, &i2c); I2C_Cmd(I2C1, ENABLE); } /* * reads data from devices which use a single-byte address register */ int8_t i2c_read_addr1b(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len) { timeout = I2C_TIMEOUT; while((I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET) && timeout--); if (!timeout) return -1; I2C_GenerateSTART(I2C1, ENABLE); timeout = I2C_TIMEOUT; while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--); if (!timeout) return -2; I2C_Send7bitAddress(I2C1, addr, I2C_Direction_Transmitter); timeout = I2C_TIMEOUT; while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) && timeout--); if (!timeout) return -3; I2C_SendData(I2C1, reg); timeout = I2C_TIMEOUT; while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) && timeout--); if (!timeout) return -4; I2C_GenerateSTART(I2C1, ENABLE); timeout = I2C_TIMEOUT; while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--); if (!timeout) return -5; I2C_Send7bitAddress(I2C1, addr, I2C_Direction_Receiver); timeout = I2C_TIMEOUT; while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) && timeout--); if (!timeout) return -6; while (len) { while(I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) == RESET) { I2C_AcknowledgeConfig(I2C1, len); } *data++ = I2C_ReceiveData(I2C1); len--; if (!len) { I2C_GenerateSTOP(I2C1, ENABLE); } } return 0; } uint8_t i2c_read_reg_8b(uint8_t addr, uint8_t reg) { uint8_t dat; i2c_read_addr1b(addr, reg, &dat, 1); return dat; } int8_t i2c_write_addr1b(uint8_t addr, uint8_t reg, const uint8_t *data, uint8_t len) { timeout = I2C_TIMEOUT; while((I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET) && timeout--); if (!timeout) return -1; I2C_GenerateSTART(I2C1, ENABLE); timeout = I2C_TIMEOUT; while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--); if (!timeout) return -2; I2C_Send7bitAddress(I2C1, addr, I2C_Direction_Transmitter); timeout = I2C_TIMEOUT; while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) && timeout--); if (!timeout) return -3; I2C_SendData(I2C1, reg); timeout = I2C_TIMEOUT; while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) && timeout--); if (!timeout) return -4; while (len) { // fixme: can get stuck here if address isn't found. // somehow all the above passes but this will fail if (I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) != RESET) { I2C_SendData(I2C1, *data++); len--; while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); } // failed to acknowledge... reset bus if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF)) { I2C1->STAR1 = 0; I2C_GenerateSTOP(I2C1, ENABLE); break; } } I2C_GenerateSTOP(I2C1, ENABLE); return 0; } void i2c_write_reg_8b(uint8_t addr, uint8_t reg, uint8_t dat) { i2c_write_addr1b(addr, reg, &dat, 1); } int8_t i2c_ack_poll(uint8_t addr) { int8_t addr_match = 0; timeout = I2C_TIMEOUT; while((I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET) && timeout--); if (!timeout) return -1; I2C_GenerateSTART(I2C1, ENABLE); timeout = I2C_TIMEOUT; while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--); if (!timeout) return -2; I2C_Send7bitAddress(I2C1, addr, I2C_Direction_Receiver); timeout = I2C_TIMEOUT_ACK_POLL; while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) && timeout--); if (!timeout) { addr_match = -128; } I2C_GenerateSTOP(I2C1, ENABLE); return addr_match; }