// TODO: this driver relies heavily on polling,
// if we want it to be more async, we should use interrupts
# define I2C_TIMEOUT_US 100000U
// cppcheck-suppress misra-c2012-2.7; not sure why it triggers here?
bool i2c_status_wait ( const volatile uint32_t * reg , uint32_t mask , uint32_t val ) {
uint32_t start_time = microsecond_timer_get ( ) ;
while ( ( ( * reg & mask ) ! = val ) & & ( get_ts_elapsed ( microsecond_timer_get ( ) , start_time ) < I2C_TIMEOUT_US ) ) ;
return ( ( * reg & mask ) = = val ) ;
}
bool i2c_write_reg ( I2C_TypeDef * I2C , uint8_t addr , uint8_t reg , uint8_t value ) {
// Setup transfer and send START + addr
bool ret = false ;
for ( uint32_t i = 0U ; i < 10U ; i + + ) {
register_clear_bits ( & I2C - > CR2 , I2C_CR2_ADD10 ) ;
I2C - > CR2 = ( ( uint32_t ) addr < < 1U ) & I2C_CR2_SADD_Msk ;
register_clear_bits ( & I2C - > CR2 , I2C_CR2_RD_WRN ) ;
register_set_bits ( & I2C - > CR2 , I2C_CR2_AUTOEND ) ;
I2C - > CR2 | = 2UL < < I2C_CR2_NBYTES_Pos ;
I2C - > CR2 | = I2C_CR2_START ;
if ( ! i2c_status_wait ( & I2C - > CR2 , I2C_CR2_START , 0U ) ) {
continue ;
}
// check if we lost arbitration
if ( ( I2C - > ISR & I2C_ISR_ARLO ) ! = 0U ) {
register_set_bits ( & I2C - > ICR , I2C_ICR_ARLOCF ) ;
} else {
ret = true ;
break ;
}
}
if ( ! ret ) {
goto end ;
}
// Send data
ret = i2c_status_wait ( & I2C - > ISR , I2C_ISR_TXIS , I2C_ISR_TXIS ) ;
if ( ! ret ) {
goto end ;
}
I2C - > TXDR = reg ;
ret = i2c_status_wait ( & I2C - > ISR , I2C_ISR_TXIS , I2C_ISR_TXIS ) ;
if ( ! ret ) {
goto end ;
}
I2C - > TXDR = value ;
end :
return ret ;
}
bool i2c_read_reg ( I2C_TypeDef * I2C , uint8_t addr , uint8_t reg , uint8_t * value ) {
// Setup transfer and send START + addr
bool ret = false ;
for ( uint32_t i = 0U ; i < 10U ; i + + ) {
register_clear_bits ( & I2C - > CR2 , I2C_CR2_ADD10 ) ;
I2C - > CR2 = ( ( uint32_t ) addr < < 1U ) & I2C_CR2_SADD_Msk ;
register_clear_bits ( & I2C - > CR2 , I2C_CR2_RD_WRN ) ;
register_clear_bits ( & I2C - > CR2 , I2C_CR2_AUTOEND ) ;
I2C - > CR2 | = 1UL < < I2C_CR2_NBYTES_Pos ;
I2C - > CR2 | = I2C_CR2_START ;
if ( ! i2c_status_wait ( & I2C - > CR2 , I2C_CR2_START , 0U ) ) {
continue ;
}
// check if we lost arbitration
if ( ( I2C - > ISR & I2C_ISR_ARLO ) ! = 0U ) {
register_set_bits ( & I2C - > ICR , I2C_ICR_ARLOCF ) ;
} else {
ret = true ;
break ;
}
}
if ( ! ret ) {
goto end ;
}
// Send data
ret = i2c_status_wait ( & I2C - > ISR , I2C_ISR_TXIS , I2C_ISR_TXIS ) ;
if ( ! ret ) {
goto end ;
}
I2C - > TXDR = reg ;
// Restart
I2C - > CR2 = ( ( ( addr < < 1 ) | 0x1U ) & I2C_CR2_SADD_Msk ) | ( 1UL < < I2C_CR2_NBYTES_Pos ) | I2C_CR2_RD_WRN | I2C_CR2_START ;
ret = i2c_status_wait ( & I2C - > CR2 , I2C_CR2_START , 0U ) ;
if ( ! ret ) {
goto end ;
}
// check if we lost arbitration
if ( ( I2C - > ISR & I2C_ISR_ARLO ) ! = 0U ) {
register_set_bits ( & I2C - > ICR , I2C_ICR_ARLOCF ) ;
ret = false ;
goto end ;
}
// Read data
ret = i2c_status_wait ( & I2C - > ISR , I2C_ISR_RXNE , I2C_ISR_RXNE ) ;
if ( ! ret ) {
goto end ;
}
* value = I2C - > RXDR ;
// Stop
I2C - > CR2 | = I2C_CR2_STOP ;
end :
return ret ;
}
bool i2c_set_reg_bits ( I2C_TypeDef * I2C , uint8_t address , uint8_t regis , uint8_t bits ) {
uint8_t value ;
bool ret = i2c_read_reg ( I2C , address , regis , & value ) ;
if ( ret ) {
ret = i2c_write_reg ( I2C , address , regis , value | bits ) ;
}
return ret ;
}
bool i2c_clear_reg_bits ( I2C_TypeDef * I2C , uint8_t address , uint8_t regis , uint8_t bits ) {
uint8_t value ;
bool ret = i2c_read_reg ( I2C , address , regis , & value ) ;
if ( ret ) {
ret = i2c_write_reg ( I2C , address , regis , value & ( uint8_t ) ( ~ bits ) ) ;
}
return ret ;
}
bool i2c_set_reg_mask ( I2C_TypeDef * I2C , uint8_t address , uint8_t regis , uint8_t value , uint8_t mask ) {
uint8_t old_value ;
bool ret = i2c_read_reg ( I2C , address , regis , & old_value ) ;
if ( ret ) {
ret = i2c_write_reg ( I2C , address , regis , ( old_value & ( uint8_t ) ( ~ mask ) ) | ( value & mask ) ) ;
}
return ret ;
}
void i2c_init ( I2C_TypeDef * I2C ) {
// 100kHz clock speed
I2C - > TIMINGR = 0x107075B0 ;
I2C - > CR1 = I2C_CR1_PE ;
}