/* * 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 uint32_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_2; // 16_9; 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_AcknowledgeConfig(I2C1, DISABLE); I2C_SendData(I2C1, reg); timeout = I2C_TIMEOUT; while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) && timeout--); if (!timeout) return -4; I2C_AcknowledgeConfig(I2C1, ENABLE); 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--; } 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_AcknowledgeConfig(I2C1, ENABLE); 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)); continue; } // failed to acknowledge... if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF)) { break; } } I2C_GenerateSTOP(I2C1, ENABLE); return 0; } void i2c_write_reg_8b(uint8_t addr, uint8_t reg, const uint8_t dat) { i2c_write_addr1b(addr, reg, &dat, 1); } int8_t i2c_addr_scan(uint8_t addr) { uint8_t found = 1; uint32_t event; // no low addresses if ((addr >> 4) == 0) return 0; // no high addresses if ((addr & 0xf8) == 0xf8) return 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 & 0xfe), (addr & 0x01)); timeout = I2C_TIMEOUT_ACK_POLL; if (addr & 1) event = I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED; else event = I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED; while ((I2C_CheckEvent(I2C1, event) == NoREADY) && timeout--) { if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF)) { found = 0; break; } } if (!timeout) { found = 0; } // send a stop to make sure anything listening knows to stfu I2C_GenerateSTOP(I2C1, ENABLE); // reset flags; it might be in a fucked state if (!found) { I2C1->STAR1 = 0; return 0; } return addr; }