openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

153 lines
3.8 KiB

// 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;
}