/** ****************************************************************************** * @file : i2c.c * @brief : I2C base functions ****************************************************************************** */ #include "main.h" /* Private macros */ #define ENABLE_TIM TIM17->CNT=1; TIM17->CR1 |= TIM_CR1_CEN #define TIM_CNT TIM17->CNT /* Private variables */ static i2c_status_t st_Read = I2C_Ret_OK; /* Private function prototypes */ static i2c_status_t i2c_check_err(void); static inline void i2c_start(void) { // Send 'Start' condition, and wait for acknowledge. I2C1->CR2 |= (I2C_CR2_START); while ((I2C1->CR2 & I2C_CR2_START)) {} } static inline void i2c_stop(void) { // Send 'Stop' condition, and wait for acknowledge. I2C1->CR2 |= (I2C_CR2_STOP); while ((I2C1->CR2 & I2C_CR2_STOP)) {} // Reset the ICR ('Interrupt Clear Register') event flag. I2C1->ICR |= (I2C_ICR_STOPCF); while ((I2C1->ICR & I2C_ICR_STOPCF)) {} } static void i2c_write_byte(uint8_t dat); static uint8_t i2c_read_byte(void); static uint8_t i2c_read_register(uint8_t reg_addr); /** * @brief Check I2C bus for errors. * @retval I2C return code */ static i2c_status_t i2c_check_err(void) { i2c_status_t r = I2C_Ret_OK; if ((I2C1->ISR & I2C_ISR_NACKF) != 0) { /* device not present */ r = I2C_Ret_NACK; } else if ((I2C1->ISR & I2C_ISR_TIMEOUT) != 0) { /* timeout error */ r = I2C_Ret_Tout; } else if ((I2C1->ISR & (I2C_ISR_ARLO | I2C_ISR_BERR)) != 0) { /* other error */ r = I2C_Ret_Err; } if (r != I2C_Ret_OK) { /* restart I2C and clear flags */ I2C1->CR1 &= ~I2C_CR1_PE; while ((I2C1->CR1 & I2C_CR1_PE) != 0); I2C1->CR1 |= I2C_CR1_PE; } return r; } /** * @brief Read len bytes from I2C bus to data by reg_addr. * @retval I2C return code */ i2c_status_t user_i2c_read(const uint8_t id, const uint8_t reg_addr, uint8_t *data, uint16_t len) { /* wait for i2c */ while ( I2C1->ISR & I2C_ISR_BUSY ) { __NOP(); }; st_Read = I2C_Ret_OK; Flag.I2C_RX_End = 0; Flag.I2C_RX_Err = 0; Flag.I2C_TX_Err = 0; /* prepare i2c for sending reg addr */ I2C1->CR2 &= ~(I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RD_WRN); I2C1->CR2 |= (id | (1 << I2C_CR2_NBYTES_Pos)); i2c_start(); /* wait for byte request or any error */ while ((I2C1->ISR & (I2C_ISR_TIMEOUT | I2C_ISR_ARLO | I2C_ISR_BERR | I2C_ISR_NACKF | I2C_ISR_TXE)) == 0) { __NOP(); } if ((I2C1->ISR & I2C_ISR_TXE) != 0) { /* device ok, send reg addr */ I2C1->TXDR = reg_addr; } else { st_Read = i2c_check_err(); Flag.I2C_TX_Err = 1; Flag.I2C_RX_End = 1; return st_Read; } /* wait for i2c or any error */ while (((I2C1->ISR & I2C_ISR_BUSY) != 0) && ((I2C1->ISR & (I2C_ISR_ARLO | I2C_ISR_BERR | I2C_ISR_NACKF)) == 0)) { __NOP(); }; st_Read = i2c_check_err(); if (st_Read != I2C_Ret_OK) { Flag.I2C_TX_Err = 1; Flag.I2C_RX_End = 1; return st_Read; } /* prepare i2c for receiving data */ I2C1->CR2 &= ~(I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RD_WRN); I2C1->CR2 |= (id | (len << I2C_CR2_NBYTES_Pos) | I2C_CR2_RD_WRN); /* launch receiving */ i2c_start(); /* receiving data */ while (len) { while ((I2C1->ISR & (I2C_ISR_NACKF | I2C_ISR_RXNE))==0) { __NOP(); } if (I2C1->ISR & I2C_ISR_RXNE) { // Reading data *data = I2C1->RXDR; data ++; len --; } else { st_Read = i2c_check_err(); Flag.I2C_RX_Err = 1; Flag.I2C_RX_End = 1; return st_Read; } } Flag.I2C_RX_End = 1; return st_Read; } /** * @brief Write len bytes to I2C bus from data by reg_addr. * @retval I2C return code */ i2c_status_t user_i2c_write(const uint8_t id, const uint8_t reg_addr, uint8_t *data, uint16_t len) { i2c_status_t r = I2C_Ret_OK; while ( I2C1->ISR & I2C_ISR_BUSY ); Flag.I2C_TX_End = 0; Flag.I2C_TX_Err = 0; I2C1->CR2 &= ~(I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RD_WRN); I2C1->CR2 |= (id | ((len + 1) << I2C_CR2_NBYTES_Pos )); i2c_start(); while ((I2C1->ISR & (I2C_ISR_ARLO | I2C_ISR_BERR | I2C_ISR_NACKF | I2C_ISR_TXE)) == 0) { __NOP(); } if ((I2C1->ISR & I2C_ISR_TXE) != 0) { I2C1->TXDR = reg_addr; } else { r = i2c_check_err(); Flag.I2C_TX_Err = 1; Flag.I2C_TX_End = 1; return r; } while (len) { while ((I2C1->ISR & (I2C_ISR_NACKF | I2C_ISR_TXE)) == 0) { __NOP(); } if ((I2C1->ISR & I2C_ISR_TXE) != 0) { I2C1->TXDR = *data; data ++; len --; } else { r = i2c_check_err(); Flag.I2C_TX_Err = 1; Flag.I2C_TX_End = 1; return r; } } Flag.I2C_TX_End = 1; return r; } static void i2c_write_byte(uint8_t dat) { I2C1->TXDR = (I2C1->TXDR & 0xFFFFFF00) | dat; // Wait for one of these ISR bits: // 'TXIS' ("ready for next byte") // 'TC' ("transfer complete") while (!(I2C1->ISR & (I2C_ISR_TXIS | I2C_ISR_TC))) {} // (Also of interest: 'TXE' ("TXDR register is empty") and // 'TCR' ("transfer complete, and 'RELOAD' is set.")) } static uint8_t i2c_read_byte(void) { // Wait for a byte of data to be available, then read it. while (!(I2C1->ISR & I2C_ISR_RXNE)) {} return (I2C1->RXDR & 0xFF); } static uint8_t i2c_read_register(uint8_t reg_addr) { // Set '1 byte to send.' I2C1->CR2 &= ~(I2C_CR2_NBYTES); I2C1->CR2 |= (0x01 << I2C_CR2_NBYTES_Pos); // Start the I2C write transmission. i2c_start(); // Send the register address. i2c_write_byte(reg_addr); // Stop the I2C write transmission. i2c_stop(); // Set '1 byte to receive.' I2C1->CR2 &= ~(I2C_CR2_NBYTES); I2C1->CR2 |= (0x01 << I2C_CR2_NBYTES_Pos); // Set 'read' I2C direction. I2C1->CR2 |= (I2C_CR2_RD_WRN); // Start the I2C read transmission. i2c_start(); // Read the transmitted data. uint8_t read_result = i2c_read_byte(); // Stop the I2C read transmission. i2c_stop(); // Set 'write' I2C direction again. I2C1->CR2 &= ~(I2C_CR2_RD_WRN); // Return the read value. return read_result; }