2019-12-05 13:26:49 +01:00
/** library to communicate using I²C as master (code)
* @ file
2017-04-03 13:07:03 +02:00
* @ author King Kévin < kingkevin @ cuvoodoo . info >
2020-06-06 14:35:55 +02:00
* @ copyright SPDX - License - Identifier : GPL - 3.0 - or - later
2020-02-11 12:20:13 +01:00
* @ date 2017 - 2020
2018-04-03 16:59:02 +02:00
* @ note peripherals used : I2C
2017-04-03 13:07:03 +02:00
*/
/* standard libraries */
# include <stdint.h> // standard integer types
# include <stdlib.h> // general utilities
/* STM32 (including CM3) libraries */
2019-03-26 18:01:13 +01:00
# include <libopencm3/cm3/systick.h> // SysTick library
2019-03-26 18:35:46 +01:00
# include <libopencm3/cm3/assert.h> // assert utilities
2017-04-03 13:07:03 +02:00
# include <libopencm3/stm32/rcc.h> // real-time control clock library
# include <libopencm3/stm32/gpio.h> // general purpose input output library
2019-12-05 13:26:49 +01:00
# include <libopencm3/stm32/i2c.h> // I²C library
2017-04-03 13:07:03 +02:00
2018-02-18 15:21:18 +01:00
/* own libraries */
2017-04-03 13:07:03 +02:00
# include "global.h" // global utilities
2019-12-05 13:26:49 +01:00
# include "i2c_master.h" // I²C header and definitions
2017-04-03 13:07:03 +02:00
2019-12-05 13:26:49 +01:00
/** get RCC for I²C based on I²C identifier
* @ param [ in ] i2c I ² C base address
* @ return RCC address for I ² C peripheral
2017-04-03 13:07:03 +02:00
*/
2018-04-03 16:59:02 +02:00
static uint32_t RCC_I2C ( uint32_t i2c )
{
2019-03-26 18:35:46 +01:00
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
2018-04-03 16:59:02 +02:00
switch ( i2c ) {
2019-03-26 18:35:46 +01:00
case I2C1 :
return RCC_I2C1 ;
break ;
case I2C2 :
return RCC_I2C2 ;
break ;
default :
return 0 ;
2018-04-03 16:59:02 +02:00
}
}
2017-04-03 13:07:03 +02:00
2019-12-05 13:26:49 +01:00
/** get RCC for GPIO port for SCL pin based on I²C identifier
* @ param [ in ] i2c I ² C base address
2018-04-03 16:59:02 +02:00
* @ return RCC GPIO address
2017-04-03 13:07:03 +02:00
*/
2018-04-03 16:59:02 +02:00
static uint32_t RCC_GPIO_PORT_SCL ( uint32_t i2c )
{
2019-03-26 18:35:46 +01:00
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
2018-04-03 16:59:02 +02:00
switch ( i2c ) {
2019-03-26 18:35:46 +01:00
case I2C1 :
case I2C2 :
return RCC_GPIOB ;
break ;
default :
return 0 ;
2018-04-03 16:59:02 +02:00
}
}
2017-04-03 13:07:03 +02:00
2019-12-05 13:26:49 +01:00
/** get RCC for GPIO port for SDA pin based on I²C identifier
* @ param [ in ] i2c I ² C base address
2018-04-03 16:59:02 +02:00
* @ return RCC GPIO address
*/
static uint32_t RCC_GPIO_PORT_SDA ( uint32_t i2c )
2017-04-03 13:07:03 +02:00
{
2019-03-26 18:35:46 +01:00
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
2018-04-03 16:59:02 +02:00
switch ( i2c ) {
2019-03-26 18:35:46 +01:00
case I2C1 :
case I2C2 :
return RCC_GPIOB ;
break ;
default :
return 0 ;
2018-04-03 16:59:02 +02:00
}
}
2019-12-05 13:26:49 +01:00
/** get GPIO port for SCL pin based on I²C identifier
* @ param [ in ] i2c I ² C base address
2018-04-03 16:59:02 +02:00
* @ return GPIO address
*/
static uint32_t GPIO_PORT_SCL ( uint32_t i2c )
{
2019-03-26 18:35:46 +01:00
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
2018-04-03 16:59:02 +02:00
switch ( i2c ) {
2019-03-26 18:35:46 +01:00
case I2C1 :
if ( AFIO_MAPR & AFIO_MAPR_I2C1_REMAP ) {
return GPIO_BANK_I2C1_RE_SCL ;
} else {
return GPIO_BANK_I2C1_SCL ;
}
break ;
case I2C2 :
return GPIO_BANK_I2C2_SCL ;
break ;
default :
return 0 ;
2018-04-03 16:59:02 +02:00
}
}
2019-12-05 13:26:49 +01:00
/** get GPIO port for SDA pin based on I²C identifier
* @ param [ in ] i2c I ² C base address
2018-04-03 16:59:02 +02:00
* @ return GPIO address
*/
static uint32_t GPIO_PORT_SDA ( uint32_t i2c )
{
2019-03-26 18:35:46 +01:00
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
2018-04-03 16:59:02 +02:00
switch ( i2c ) {
2019-03-26 18:35:46 +01:00
case I2C1 :
if ( AFIO_MAPR & AFIO_MAPR_I2C1_REMAP ) {
return GPIO_BANK_I2C1_RE_SDA ;
} else {
return GPIO_BANK_I2C1_SDA ;
}
break ;
case I2C2 :
return GPIO_BANK_I2C2_SDA ;
break ;
default :
return 0 ;
2018-04-03 16:59:02 +02:00
}
}
2019-12-05 13:26:49 +01:00
/** get GPIO pin for SCL pin based on I²C identifier
* @ param [ in ] i2c I ² C base address
2018-04-03 16:59:02 +02:00
* @ return GPIO address
*/
static uint32_t GPIO_PIN_SCL ( uint32_t i2c )
{
2019-03-26 18:35:46 +01:00
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
2018-04-03 16:59:02 +02:00
switch ( i2c ) {
2019-03-26 18:35:46 +01:00
case I2C1 :
if ( AFIO_MAPR & AFIO_MAPR_I2C1_REMAP ) {
return GPIO_I2C1_RE_SCL ;
} else {
return GPIO_I2C1_SCL ;
}
break ;
case I2C2 :
return GPIO_I2C2_SCL ;
break ;
default :
return 0 ;
2018-04-03 16:59:02 +02:00
}
}
2019-12-05 13:26:49 +01:00
/** get GPIO pin for SDA pin based on I²C identifier
* @ param [ in ] i2c I ² C base address
2018-04-03 16:59:02 +02:00
* @ return GPIO address
*/
static uint32_t GPIO_PIN_SDA ( uint32_t i2c )
{
2019-03-26 18:35:46 +01:00
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
2018-04-03 16:59:02 +02:00
switch ( i2c ) {
2019-03-26 18:35:46 +01:00
case I2C1 :
if ( AFIO_MAPR & AFIO_MAPR_I2C1_REMAP ) {
return GPIO_I2C1_RE_SDA ;
} else {
return GPIO_I2C1_SDA ;
}
break ;
case I2C2 :
return GPIO_I2C2_SDA ;
break ;
default :
return 0 ;
2018-04-03 16:59:02 +02:00
}
}
void i2c_master_setup ( uint32_t i2c , uint16_t frequency )
{
2019-03-26 18:35:46 +01:00
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
2018-04-03 16:59:02 +02:00
2019-12-05 13:26:49 +01:00
// configure I²C peripheral
rcc_periph_clock_enable ( RCC_GPIO_PORT_SCL ( i2c ) ) ; // enable clock for I²C I/O peripheral
2018-04-03 16:59:02 +02:00
gpio_set ( GPIO_PORT_SCL ( i2c ) , GPIO_PIN_SCL ( i2c ) ) ; // already put signal high to avoid small pulse
2019-12-05 13:26:49 +01:00
gpio_set_mode ( GPIO_PORT_SCL ( i2c ) , GPIO_MODE_OUTPUT_10_MHZ , GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN , GPIO_PIN_SCL ( i2c ) ) ; // setup I²C I/O pins
rcc_periph_clock_enable ( RCC_GPIO_PORT_SDA ( i2c ) ) ; // enable clock for I²C I/O peripheral
2018-04-03 16:59:02 +02:00
gpio_set ( GPIO_PORT_SDA ( i2c ) , GPIO_PIN_SDA ( i2c ) ) ; // already put signal high to avoid small pulse
2019-12-05 13:26:49 +01:00
gpio_set_mode ( GPIO_PORT_SDA ( i2c ) , GPIO_MODE_OUTPUT_10_MHZ , GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN , GPIO_PIN_SDA ( i2c ) ) ; // setup I²C I/O pins
2017-04-03 13:07:03 +02:00
rcc_periph_clock_enable ( RCC_AFIO ) ; // enable clock for alternate function
2019-12-05 13:26:49 +01:00
rcc_periph_clock_enable ( RCC_I2C ( i2c ) ) ; // enable clock for I²C peripheral
2018-04-03 16:59:02 +02:00
i2c_reset ( i2c ) ; // reset peripheral domain
2019-12-05 13:26:49 +01:00
i2c_peripheral_disable ( i2c ) ; // I²C needs to be disable to be configured
2018-04-03 16:59:02 +02:00
I2C_CR1 ( i2c ) | = I2C_CR1_SWRST ; // reset peripheral
I2C_CR1 ( i2c ) & = ~ I2C_CR1_SWRST ; // clear peripheral reset
if ( 0 = = frequency ) { // don't allow null frequency
frequency = 1 ;
2019-03-26 18:25:23 +01:00
} else if ( frequency > 400 ) { // limit frequency to 400 kHz
2018-04-03 16:59:02 +02:00
frequency = 400 ;
}
2019-03-26 18:25:23 +01:00
i2c_set_clock_frequency ( i2c , rcc_apb1_frequency / 1000000 ) ; // configure the peripheral clock to the APB1 freq (where it is connected to)
2018-04-03 16:59:02 +02:00
if ( frequency > 100 ) { // use fast mode for frequencies over 100 kHz
i2c_set_fast_mode ( i2c ) ; // set fast mode (Fm)
2019-03-26 18:25:23 +01:00
i2c_set_ccr ( i2c , rcc_apb1_frequency / ( frequency * 1000 * 2 ) ) ; // set Thigh/Tlow to generate frequency (fast duty not used)
i2c_set_trise ( i2c , ( 300 / ( 1000 / ( rcc_apb1_frequency / 1000000 ) ) ) + 1 ) ; // max rise time for Fm mode (< 400) kHz is 300 ns
2018-04-03 16:59:02 +02:00
} else { // use fast mode for frequencies below 100 kHz
i2c_set_standard_mode ( i2c ) ; // set standard mode (Sm)
2019-03-26 18:25:23 +01:00
i2c_set_ccr ( i2c , rcc_apb1_frequency / ( frequency * 1000 * 2 ) ) ; // set Thigh/Tlow to generate frequency of 100 kHz
i2c_set_trise ( i2c , ( 1000 / ( 1000 / ( rcc_apb1_frequency / 1000000 ) ) ) + 1 ) ; // max rise time for Sm mode (< 100 kHz) is 1000 ns (~1 MHz)
2018-04-03 16:59:02 +02:00
}
2019-12-05 13:26:49 +01:00
i2c_peripheral_enable ( i2c ) ; // enable I²C after configuration completed
2018-04-03 16:59:02 +02:00
}
void i2c_master_release ( uint32_t i2c )
{
2019-03-26 18:35:46 +01:00
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
2018-04-03 16:59:02 +02:00
2019-12-05 13:26:49 +01:00
i2c_reset ( i2c ) ; // reset I²C peripheral configuration
i2c_peripheral_disable ( i2c ) ; // disable I²C peripheral
rcc_periph_clock_disable ( RCC_I2C ( i2c ) ) ; // disable clock for I²C peripheral
gpio_set_mode ( GPIO_PORT_SCL ( i2c ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , GPIO_PIN_SCL ( i2c ) ) ; // put I²C I/O pins back to floating
gpio_set_mode ( GPIO_PORT_SDA ( i2c ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , GPIO_PIN_SDA ( i2c ) ) ; // put I²C I/O pins back to floating
2018-04-03 16:59:02 +02:00
}
bool i2c_master_check_signals ( uint32_t i2c )
{
2019-03-26 18:35:46 +01:00
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
2018-04-03 16:59:02 +02:00
2019-12-12 18:53:23 +01:00
// enable GPIOs to read SDA and SCL
rcc_periph_clock_enable ( RCC_GPIO_PORT_SDA ( i2c ) ) ; // enable clock for I²C I/O peripheral
rcc_periph_clock_enable ( RCC_GPIO_PORT_SCL ( i2c ) ) ; // enable clock for I²C I/O peripheral
2018-04-03 16:59:02 +02:00
// pull SDA and SDC low to check if there are pull-up resistors
uint32_t sda_crl = GPIO_CRL ( GPIO_PORT_SDA ( i2c ) ) ; // backup port configuration
uint32_t sda_crh = GPIO_CRH ( GPIO_PORT_SDA ( i2c ) ) ; // backup port configuration
uint32_t sda_bsrr = GPIO_BSRR ( GPIO_PORT_SDA ( i2c ) ) ; // backup port configuration
uint32_t scl_crl = GPIO_CRL ( GPIO_PORT_SCL ( i2c ) ) ; // backup port configuration
uint32_t scl_crh = GPIO_CRH ( GPIO_PORT_SCL ( i2c ) ) ; // backup port configuration
uint32_t scl_bsrr = GPIO_BSRR ( GPIO_PORT_SCL ( i2c ) ) ; // backup port configuration
gpio_set_mode ( GPIO_PORT_SDA ( i2c ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_PULL_UPDOWN , GPIO_PIN_SDA ( i2c ) ) ; // configure signal as pull down
gpio_set_mode ( GPIO_PORT_SCL ( i2c ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_PULL_UPDOWN , GPIO_PIN_SCL ( i2c ) ) ; // configure signal as pull down
gpio_clear ( GPIO_PORT_SDA ( i2c ) , GPIO_PIN_SDA ( i2c ) ) ; // pull down
gpio_clear ( GPIO_PORT_SCL ( i2c ) , GPIO_PIN_SCL ( i2c ) ) ; // pull down
2019-03-26 18:25:23 +01:00
bool to_return = ( 0 ! = gpio_get ( GPIO_PORT_SCL ( i2c ) , GPIO_PIN_SCL ( i2c ) ) & & 0 ! = gpio_get ( GPIO_PORT_SDA ( i2c ) , GPIO_PIN_SDA ( i2c ) ) ) ; // check if the signals are still pulled high by external stronger pull-up resistors
2018-04-03 16:59:02 +02:00
GPIO_CRL ( GPIO_PORT_SDA ( i2c ) ) = sda_crl ; // restore port configuration
GPIO_CRH ( GPIO_PORT_SDA ( i2c ) ) = sda_crh ; // restore port configuration
GPIO_BSRR ( GPIO_PORT_SDA ( i2c ) ) = sda_bsrr ; // restore port configuration
GPIO_CRL ( GPIO_PORT_SCL ( i2c ) ) = scl_crl ; // restore port configuration
GPIO_CRH ( GPIO_PORT_SCL ( i2c ) ) = scl_crh ; // restore port configuration
GPIO_BSRR ( GPIO_PORT_SCL ( i2c ) ) = scl_bsrr ; // restore port configuration
return to_return ;
}
2019-03-26 18:05:15 +01:00
bool i2c_master_reset ( uint32_t i2c )
2018-04-03 16:59:02 +02:00
{
2019-03-26 18:35:46 +01:00
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
2018-04-03 16:59:02 +02:00
2019-03-26 18:05:15 +01:00
bool to_return = true ;
2018-04-03 16:59:02 +02:00
// follow procedure described in STM32F10xxC/D/E Errata sheet, Section 2.14.7
2019-12-05 13:26:49 +01:00
i2c_peripheral_disable ( i2c ) ; // disable I²C peripheral
gpio_set_mode ( GPIO_PORT_SCL ( i2c ) , GPIO_MODE_OUTPUT_10_MHZ , GPIO_CNF_OUTPUT_OPENDRAIN , GPIO_PIN_SCL ( i2c ) ) ; // put I²C I/O pins to general output
2018-04-03 16:59:02 +02:00
gpio_set ( GPIO_PORT_SCL ( i2c ) , GPIO_PIN_SCL ( i2c ) ) ; // set high
2019-03-26 18:05:15 +01:00
to_return & = ! gpio_get ( GPIO_PORT_SCL ( i2c ) , GPIO_PIN_SCL ( i2c ) ) ; // ensure it is high
2019-12-05 13:26:49 +01:00
gpio_set_mode ( GPIO_PORT_SDA ( i2c ) , GPIO_MODE_OUTPUT_10_MHZ , GPIO_CNF_OUTPUT_OPENDRAIN , GPIO_PIN_SDA ( i2c ) ) ; // put I²C I/O pins to general output
2018-04-03 16:59:02 +02:00
gpio_set ( GPIO_PORT_SDA ( i2c ) , GPIO_PIN_SDA ( i2c ) ) ; // set high
2019-03-26 18:05:15 +01:00
to_return & = ! gpio_get ( GPIO_PORT_SDA ( i2c ) , GPIO_PIN_SDA ( i2c ) ) ; // ensure it is high
2018-04-03 16:59:02 +02:00
gpio_clear ( GPIO_PORT_SDA ( i2c ) , GPIO_PIN_SDA ( i2c ) ) ; // set low (try first transition)
2019-03-26 18:05:15 +01:00
to_return & = gpio_get ( GPIO_PORT_SDA ( i2c ) , GPIO_PIN_SDA ( i2c ) ) ; // ensure it is low
2018-04-03 16:59:02 +02:00
gpio_clear ( GPIO_PORT_SCL ( i2c ) , GPIO_PIN_SCL ( i2c ) ) ; // set low (try first transition)
2019-03-26 18:05:15 +01:00
to_return & = gpio_get ( GPIO_PORT_SCL ( i2c ) , GPIO_PIN_SCL ( i2c ) ) ; // ensure it is low
2018-04-03 16:59:02 +02:00
gpio_set ( GPIO_PORT_SCL ( i2c ) , GPIO_PIN_SCL ( i2c ) ) ; // set high (try second transition)
2019-03-26 18:05:15 +01:00
to_return & = ! gpio_get ( GPIO_PORT_SCL ( i2c ) , GPIO_PIN_SCL ( i2c ) ) ; // ensure it is high
2018-04-03 16:59:02 +02:00
gpio_set ( GPIO_PORT_SDA ( i2c ) , GPIO_PIN_SDA ( i2c ) ) ; // set high (try second transition)
2019-03-26 18:05:15 +01:00
to_return & = ! gpio_get ( GPIO_PORT_SDA ( i2c ) , GPIO_PIN_SDA ( i2c ) ) ; // ensure it is high
2019-12-05 13:26:49 +01:00
gpio_set_mode ( GPIO_PORT_SCL ( i2c ) , GPIO_MODE_OUTPUT_10_MHZ , GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN , GPIO_PIN_SCL ( i2c ) ) ; // set I²C I/O pins back
gpio_set_mode ( GPIO_PORT_SDA ( i2c ) , GPIO_MODE_OUTPUT_10_MHZ , GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN , GPIO_PIN_SDA ( i2c ) ) ; // set I²C I/O pins back
2018-04-03 16:59:02 +02:00
I2C_CR1 ( i2c ) | = I2C_CR1_SWRST ; // reset device
I2C_CR1 ( i2c ) & = ~ I2C_CR1_SWRST ; // reset device
i2c_peripheral_enable ( i2c ) ; // re-enable device
2019-03-26 18:05:15 +01:00
return to_return ;
2017-04-03 13:07:03 +02:00
}
2018-04-03 16:59:02 +02:00
enum i2c_master_rc i2c_master_start ( uint32_t i2c )
2017-04-03 13:07:03 +02:00
{
2019-03-26 18:35:46 +01:00
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
2018-04-03 16:59:02 +02:00
2019-03-26 18:01:13 +01:00
bool retry = true ; // retry after reset if first try failed
enum i2c_master_rc to_return ; // return code
2020-02-11 12:20:13 +01:00
uint16_t sr1 ; // read register once, since reading/writing other registers or other events clears some flags
2019-03-26 18:01:13 +01:00
try :
to_return = I2C_MASTER_RC_NONE ; // return code
2018-02-18 15:21:18 +01:00
// send (re-)start condition
2019-03-26 18:25:23 +01:00
if ( I2C_CR1 ( i2c ) & ( I2C_CR1_START | I2C_CR1_STOP ) ) { // ensure start or stop operations are not in progress
2018-04-03 16:59:02 +02:00
return I2C_MASTER_RC_START_STOP_IN_PROGESS ;
2017-04-03 13:07:03 +02:00
}
2019-12-05 13:26:49 +01:00
// prepare timer in case the peripheral hangs on sending stop condition (see errata 2.14.4 Wrong behavior of I²C peripheral in master mode after a misplaced Stop)
2019-03-26 18:01:13 +01:00
systick_counter_disable ( ) ; // disable SysTick to reconfigure it
systick_set_frequency ( 500 , rcc_ahb_frequency ) ; // set timer to 2 ms (that should be long enough to send a start condition)
systick_clear ( ) ; // reset SysTick (set to 0)
systick_interrupt_disable ( ) ; // disable interrupt to prevent ISR to read the flag
systick_get_countflag ( ) ; // reset flag (set when counter is going for 1 to 0)
2018-04-03 16:59:02 +02:00
i2c_send_start ( i2c ) ; // send start condition to start transaction
2019-03-26 18:01:13 +01:00
bool timeout = false ; // remember if the timeout has been reached
systick_counter_enable ( ) ; // start timer
2020-02-11 12:20:13 +01:00
while ( ( I2C_CR1 ( i2c ) & I2C_CR1_START ) & & ! ( ( sr1 = I2C_SR1 ( i2c ) ) & ( I2C_SR1_BERR | I2C_SR1_ARLO ) ) & & ! timeout ) { // wait until start condition has been accepted and cleared
2019-03-26 18:01:13 +01:00
timeout | = systick_get_countflag ( ) ; // verify if timeout has been reached
2018-04-03 16:59:02 +02:00
}
2020-02-11 12:20:13 +01:00
sr1 = I2C_SR1 ( i2c ) ; // be sure to get the current value
if ( sr1 & ( I2C_SR1_BERR | I2C_SR1_ARLO ) ) {
2019-03-26 18:01:13 +01:00
to_return = I2C_MASTER_RC_BUS_ERROR ;
}
2020-02-11 12:20:13 +01:00
while ( ! ( ( sr1 = I2C_SR1 ( i2c ) ) & ( I2C_SR1_SB | I2C_SR1_BERR | I2C_SR1_ARLO ) ) & & ! timeout & & I2C_MASTER_RC_NONE = = to_return ) { // wait until start condition is transmitted
2019-03-26 18:01:13 +01:00
timeout | = systick_get_countflag ( ) ; // verify if timeout has been reached
2018-04-03 16:59:02 +02:00
}
2020-02-11 12:20:13 +01:00
sr1 = I2C_SR1 ( i2c ) ; // be sure to get the current value
if ( sr1 & ( I2C_SR1_BERR | I2C_SR1_ARLO ) ) {
2019-03-26 18:01:13 +01:00
to_return = I2C_MASTER_RC_BUS_ERROR ;
2020-02-11 12:20:13 +01:00
} else if ( ! ( sr1 & I2C_SR1_SB ) ) { // the start bit has not been set although we the peripheral is not busy anymore
2019-03-26 18:01:13 +01:00
to_return = I2C_MASTER_RC_BUS_ERROR ;
2020-02-11 12:20:13 +01:00
} else if ( ! ( sr1 & I2C_SR2_MSL ) ) { // verify if in master mode
2019-03-26 18:01:13 +01:00
to_return = I2C_MASTER_RC_NOT_MASTER ;
} else if ( timeout ) { // timeout has been reached, i.e. the peripheral hangs
to_return = I2C_MASTER_RC_NOT_MASTER ;
2018-02-18 15:21:18 +01:00
}
2019-03-26 18:01:13 +01:00
if ( I2C_MASTER_RC_NOT_MASTER = = to_return & & retry ) { // error happened
retry = false ; // don't retry a second time
I2C_CR1 ( i2c ) | = I2C_CR1_SWRST ; // assert peripheral reset
I2C_CR1 ( i2c ) & = ~ I2C_CR1_SWRST ; // release peripheral reset
goto try ;
}
systick_counter_disable ( ) ; // we don't need to timer anymore
return to_return ;
2018-02-18 15:21:18 +01:00
}
2020-02-27 13:05:35 +01:00
/** wait until stop is sent and bus is released
* @ param [ in ] i2c I ² C base address
* @ return I ² C return code
*/
static enum i2c_master_rc i2c_master_wait_stop ( uint32_t i2c )
{
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
enum i2c_master_rc to_return = I2C_MASTER_RC_NONE ; // return code
// prepare timer in case the peripheral hangs on sending stop condition (see errata 2.14.4 Wrong behavior of I²C peripheral in master mode after a misplaced Stop)
systick_counter_disable ( ) ; // disable SysTick to reconfigure it
systick_set_frequency ( 500 , rcc_ahb_frequency ) ; // set timer to 2 ms (that should be long enough to send a stop condition)
systick_clear ( ) ; // reset SysTick (set to 0)
systick_interrupt_disable ( ) ; // disable interrupt to prevent ISR to read the flag
systick_get_countflag ( ) ; // reset flag (set when counter is going for 1 to 0)
bool timeout = false ; // remember if the timeout has been reached
systick_counter_enable ( ) ; // start timer
while ( ( I2C_CR1 ( i2c ) & I2C_CR1_STOP ) & & ! ( I2C_SR1 ( i2c ) & ( I2C_SR1_BERR | I2C_SR1_ARLO ) ) & & ! timeout ) { // wait until stop condition is accepted and cleared
timeout | = systick_get_countflag ( ) ; // verify if timeout has been reached
}
if ( I2C_SR1 ( i2c ) & ( I2C_SR1_BERR | I2C_SR1_ARLO ) ) {
to_return = I2C_MASTER_RC_BUS_ERROR ;
}
while ( ( I2C_SR2 ( i2c ) & I2C_SR2_MSL ) & & ! ( I2C_SR1 ( i2c ) & ( I2C_SR1_BERR | I2C_SR1_ARLO ) ) & & ! timeout ) { // wait until bus released (non master mode)
timeout | = systick_get_countflag ( ) ; // verify if timeout has been reached
}
if ( I2C_SR1 ( i2c ) & ( I2C_SR1_BERR | I2C_SR1_ARLO ) ) {
to_return = I2C_MASTER_RC_BUS_ERROR ;
}
while ( ( I2C_SR2 ( i2c ) & I2C_SR2_BUSY ) & & ! ( I2C_SR1 ( i2c ) & ( I2C_SR1_BERR ) ) & & ! timeout ) { // wait until peripheral is not busy anymore
timeout | = systick_get_countflag ( ) ; // verify if timeout has been reached
}
if ( I2C_SR1 ( i2c ) & ( I2C_SR1_BERR | I2C_SR1_ARLO ) ) {
to_return = I2C_MASTER_RC_BUS_ERROR ;
}
while ( ( 0 = = gpio_get ( GPIO_PORT_SCL ( i2c ) , GPIO_PIN_SCL ( i2c ) ) | | 0 = = gpio_get ( GPIO_PORT_SDA ( i2c ) , GPIO_PIN_SDA ( i2c ) ) ) & & ! timeout ) { // wait until lines are really high again
timeout | = systick_get_countflag ( ) ; // verify if timeout has been reached
}
if ( timeout ) { // I2C_CR1_STOP could also be used to detect a timeout, but I'm not sure when
if ( I2C_MASTER_RC_NONE = = to_return ) {
to_return = I2C_MASTER_RC_TIMEOUT ; // indicate timeout only when no more specific error has occurred
}
I2C_CR1 ( i2c ) | = I2C_CR1_SWRST ; // assert peripheral reset
I2C_CR1 ( i2c ) & = ~ I2C_CR1_SWRST ; // release peripheral reset
}
systick_counter_disable ( ) ; // we don't need to timer anymore
return to_return ;
}
enum i2c_master_rc i2c_master_stop ( uint32_t i2c )
{
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
// sanity check
if ( ! ( I2C_SR2 ( i2c ) & I2C_SR2_BUSY ) ) { // release is not busy
return I2C_MASTER_RC_NONE ; // bus has probably already been released
}
if ( I2C_CR1 ( i2c ) & ( I2C_CR1_START | I2C_CR1_STOP ) ) { // ensure start or stop operations are not in progress
return I2C_MASTER_RC_START_STOP_IN_PROGESS ;
}
if ( ! ( ( I2C_SR2 ( i2c ) & I2C_SR2_TRA ) ) ) { // if we are in receiver mode
i2c_disable_ack ( i2c ) ; // disable ACK to be able to close the communication
}
i2c_send_stop ( i2c ) ; // send stop to release bus
return i2c_master_wait_stop ( i2c ) ;
}
2018-04-03 16:59:02 +02:00
enum i2c_master_rc i2c_master_select_slave ( uint32_t i2c , uint16_t slave , bool address_10bit , bool write )
2018-02-18 15:21:18 +01:00
{
2019-03-26 18:35:46 +01:00
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
2018-04-03 16:59:02 +02:00
2019-12-05 13:26:49 +01:00
enum i2c_master_rc rc = I2C_MASTER_RC_NONE ; // to store I²C return codes
2020-02-11 12:20:13 +01:00
uint16_t sr1 , sr2 ; // read register once, since reading/writing other registers or other events clears some flags
if ( ! ( ( sr1 = I2C_SR1 ( i2c ) ) & I2C_SR1_SB ) ) { // start condition has not been sent
2018-04-03 16:59:02 +02:00
rc = i2c_master_start ( i2c ) ; // send start condition
2019-03-26 18:25:23 +01:00
if ( I2C_MASTER_RC_NONE ! = rc ) {
2018-04-03 16:59:02 +02:00
return rc ;
2018-02-18 15:21:18 +01:00
}
2018-04-03 16:59:02 +02:00
}
2020-02-11 12:20:13 +01:00
if ( ! ( ( sr2 = I2C_SR2 ( i2c ) ) & I2C_SR2_MSL ) ) { // I²C device is not in master mode
2018-04-03 16:59:02 +02:00
return I2C_MASTER_RC_NOT_MASTER ;
2017-04-03 13:07:03 +02:00
}
// select slave
2020-02-11 12:20:13 +01:00
I2C_SR1 ( i2c ) & = ~ ( I2C_SR1_AF ) ; // clear acknowledgement failure
2018-04-03 16:59:02 +02:00
if ( ! address_10bit ) { // 7-bit address
i2c_send_7bit_address ( i2c , slave , write ? I2C_WRITE : I2C_READ ) ; // select slave, with read/write flag
2020-02-11 12:20:13 +01:00
while ( ! ( ( sr1 = I2C_SR1 ( i2c ) ) & ( I2C_SR1_ADDR | I2C_SR1_AF | I2C_SR1_BERR | I2C_SR1_ARLO ) ) ) ; // wait until address is transmitted
if ( sr1 & ( I2C_SR1_BERR | I2C_SR1_ARLO ) ) {
2018-04-03 16:59:02 +02:00
return I2C_MASTER_RC_BUS_ERROR ;
}
2020-02-11 12:20:13 +01:00
if ( sr1 & I2C_SR1_AF ) { // address has not been acknowledged
2018-04-03 16:59:02 +02:00
return I2C_MASTER_RC_NAK ;
}
} else { // 10-bit address
// send first part of address
2019-03-26 18:25:23 +01:00
I2C_DR ( i2c ) = 11110000 | ( ( ( slave > > 8 ) & 0x3 ) < < 1 ) ; // send first header (11110xx0, where xx are 2 MSb of slave address)
2020-02-11 12:20:13 +01:00
while ( ! ( ( sr1 = I2C_SR1 ( i2c ) ) & ( I2C_SR1_ADD10 | I2C_SR1_AF | I2C_SR1_BERR | I2C_SR1_ARLO ) ) ) ; // wait until first part of address is transmitted
if ( sr1 & ( I2C_SR1_BERR | I2C_SR1_ARLO ) ) {
return I2C_MASTER_RC_BUS_ERROR ;
}
if ( sr1 & I2C_SR1_AF ) { // address has not been acknowledged
2018-04-03 16:59:02 +02:00
return I2C_MASTER_RC_NAK ;
}
// send second part of address
I2C_SR1 ( i2c ) & = ~ ( I2C_SR1_AF ) ; // clear acknowledgement failure
2019-03-26 18:25:23 +01:00
I2C_DR ( i2c ) = ( slave & 0xff ) ; // send remaining of address
2020-02-11 12:20:13 +01:00
while ( ! ( ( sr1 = I2C_SR1 ( i2c ) ) & ( I2C_SR1_ADDR | I2C_SR1_AF | I2C_SR1_BERR | I2C_SR1_ARLO ) ) ) ; // wait until remaining part of address is transmitted
if ( sr1 & ( I2C_SR1_BERR | I2C_SR1_ARLO ) ) {
2018-04-03 16:59:02 +02:00
return I2C_MASTER_RC_BUS_ERROR ;
}
2020-02-11 12:20:13 +01:00
if ( sr1 & I2C_SR1_AF ) { // address has not been acknowledged
2018-04-03 16:59:02 +02:00
return I2C_MASTER_RC_NAK ;
}
// go into receive mode if necessary
if ( ! write ) {
rc = i2c_master_start ( i2c ) ; // send start condition
2019-03-26 18:25:23 +01:00
if ( I2C_MASTER_RC_NONE ! = rc ) {
2018-04-03 16:59:02 +02:00
return rc ;
}
// send first part of address with receive flag
I2C_SR1 ( i2c ) & = ~ ( I2C_SR1_AF ) ; // clear acknowledgement failure
2019-03-26 18:25:23 +01:00
I2C_DR ( i2c ) = 11110001 | ( ( ( slave > > 8 ) & 0x3 ) < < 1 ) ; // send header (11110xx1, where xx are 2 MSb of slave address)
2020-02-11 12:20:13 +01:00
while ( ! ( ( sr1 = I2C_SR1 ( i2c ) ) & ( I2C_SR1_ADDR | I2C_SR1_AF | I2C_SR1_BERR | I2C_SR1_ARLO ) ) ) ; // wait until remaining part of address is transmitted
if ( sr1 & ( I2C_SR1_BERR | I2C_SR1_ARLO ) ) {
2018-04-03 16:59:02 +02:00
return I2C_MASTER_RC_BUS_ERROR ;
}
2020-02-11 12:20:13 +01:00
if ( sr1 & I2C_SR1_AF ) { // address has not been acknowledged
2018-04-03 16:59:02 +02:00
return I2C_MASTER_RC_NAK ;
}
}
2017-04-03 13:07:03 +02:00
}
2019-03-26 18:07:22 +01:00
// do not check I2C_SR2_TRA to verify if we really are in transmit or receive mode since reading SR2 also clears ADDR and starting the read/write transaction
2018-04-03 16:59:02 +02:00
return I2C_MASTER_RC_NONE ;
2018-02-18 15:21:18 +01:00
}
2017-04-03 13:07:03 +02:00
2018-04-03 16:59:02 +02:00
enum i2c_master_rc i2c_master_read ( uint32_t i2c , uint8_t * data , size_t data_size )
2018-02-18 15:21:18 +01:00
{
2019-03-26 18:35:46 +01:00
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
2018-04-03 16:59:02 +02:00
2018-02-18 15:21:18 +01:00
// sanity check
2019-03-26 18:25:23 +01:00
if ( NULL = = data | | 0 = = data_size ) { // no data to read
2018-04-03 16:59:02 +02:00
return I2C_MASTER_RC_NONE ;
2019-03-26 18:12:21 +01:00
}
2020-02-11 12:20:13 +01:00
// I²C start condition check
uint16_t sr1 = I2C_SR1 ( i2c ) ; // read once
if ( ! ( sr1 & I2C_SR1_ADDR ) ) { // no slave have been selected
return I2C_MASTER_RC_NOT_READY ;
}
if ( sr1 & I2C_SR1_AF ) { // check if the previous transaction went well
return I2C_MASTER_RC_NOT_READY ;
}
2020-02-11 12:21:35 +01:00
// prepare (N)ACK (EV6_3 in RM0008)
2019-03-26 18:12:21 +01:00
if ( 1 = = data_size ) {
i2c_disable_ack ( i2c ) ; // NACK after first byte
} else {
i2c_enable_ack ( i2c ) ; // NAK after next byte
}
2020-02-11 12:20:13 +01:00
uint16_t sr2 = I2C_SR2 ( i2c ) ; // reading SR2 will also also clear ADDR in SR1 and start the transaction
if ( ! ( sr2 & I2C_SR2_MSL ) ) { // I²C device is not master
2018-04-03 16:59:02 +02:00
return I2C_MASTER_RC_NOT_MASTER ;
}
2020-02-11 12:20:13 +01:00
if ( ( sr2 & I2C_SR2_TRA ) ) { // I²C device not in receiver mode
2018-04-03 16:59:02 +02:00
return I2C_MASTER_RC_NOT_RECEIVE ;
}
2017-04-03 13:07:03 +02:00
// read data
2019-03-26 18:25:23 +01:00
for ( size_t i = 0 ; i < data_size ; i + + ) { // read bytes
2020-02-11 12:21:35 +01:00
// set (N)ACK (EV6_3, EV6_1)
if ( 1 = = data_size - i ) { // prepare to sent NACK for last byte
i2c_send_stop ( i2c ) ; // already indicate we will send a stop (required to not send an ACK, and this must happen before the byte is transferred, see errata)
i2c_nack_current ( i2c ) ; // (N)ACK current byte
2019-03-26 18:12:21 +01:00
i2c_disable_ack ( i2c ) ; // NACK received to stop slave transmission
2020-02-11 12:21:35 +01:00
} else if ( 2 = = data_size - i ) { // prepare to sent NACK for second last byte
i2c_nack_next ( i2c ) ; // NACK next byte
2018-04-03 16:59:02 +02:00
i2c_disable_ack ( i2c ) ; // NACK received to stop slave transmission
2017-04-03 13:07:03 +02:00
} else {
2018-04-03 16:59:02 +02:00
i2c_enable_ack ( i2c ) ; // ACK received byte to continue slave transmission
2017-04-03 13:07:03 +02:00
}
2020-02-11 12:20:13 +01:00
while ( ! ( ( sr1 = I2C_SR1 ( i2c ) ) & ( I2C_SR1_RxNE | I2C_SR1_BERR | I2C_SR1_ARLO ) ) ) ; // wait until byte has been received
if ( sr1 & ( I2C_SR1_BERR | I2C_SR1_ARLO ) ) {
2018-04-03 16:59:02 +02:00
return I2C_MASTER_RC_BUS_ERROR ;
2017-04-03 13:07:03 +02:00
}
2018-04-03 16:59:02 +02:00
data [ i ] = i2c_get_data ( i2c ) ; // read received byte
2017-04-03 13:07:03 +02:00
}
2020-02-27 13:05:35 +01:00
return i2c_master_wait_stop ( i2c ) ;
2018-02-18 15:21:18 +01:00
}
2018-04-03 16:59:02 +02:00
enum i2c_master_rc i2c_master_write ( uint32_t i2c , const uint8_t * data , size_t data_size )
2018-02-18 15:21:18 +01:00
{
2019-03-26 18:35:46 +01:00
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
2018-04-03 16:59:02 +02:00
2018-02-18 15:21:18 +01:00
// sanity check
2019-03-26 18:25:23 +01:00
if ( NULL = = data | | 0 = = data_size ) { // no data to write
2018-04-03 16:59:02 +02:00
return I2C_MASTER_RC_NONE ;
2019-03-26 19:27:40 +01:00
}
2020-02-11 12:20:13 +01:00
// I²C start condition check
uint16_t sr1 = I2C_SR1 ( i2c ) ; // read once
if ( ! ( sr1 & I2C_SR1_ADDR ) ) { // no slave have been selected
return I2C_MASTER_RC_NOT_READY ;
}
if ( sr1 & I2C_SR1_AF ) { // check if the previous transaction went well
return I2C_MASTER_RC_NOT_READY ;
}
// master check
uint16_t sr2 = I2C_SR2 ( i2c ) ; // reading SR2 will also also clear ADDR in SR1 and start the transaction
if ( ! ( sr2 & I2C_SR2_MSL ) ) { // I²C device is not master
2018-04-03 16:59:02 +02:00
return I2C_MASTER_RC_NOT_MASTER ;
}
2020-02-11 12:20:13 +01:00
if ( ! ( sr2 & I2C_SR2_TRA ) ) { // I²C device not in transmitter mode
2018-04-03 16:59:02 +02:00
return I2C_MASTER_RC_NOT_TRANSMIT ;
}
2018-02-18 15:21:18 +01:00
// write data
2019-03-26 18:25:23 +01:00
for ( size_t i = 0 ; i < data_size ; i + + ) { // write bytes
2018-04-03 16:59:02 +02:00
I2C_SR1 ( i2c ) & = ~ ( I2C_SR1_AF ) ; // clear acknowledgement failure
i2c_send_data ( i2c , data [ i ] ) ; // send byte to be written in memory
2019-03-26 18:25:23 +01:00
while ( ! ( I2C_SR1 ( i2c ) & ( I2C_SR1_TxE | I2C_SR1_AF ) ) & & ! ( I2C_SR1 ( i2c ) & ( I2C_SR1_BERR | I2C_SR1_ARLO ) ) ) ; // wait until byte has been transmitted
if ( I2C_SR1 ( i2c ) & ( I2C_SR1_BERR | I2C_SR1_ARLO ) ) {
2018-04-03 16:59:02 +02:00
return I2C_MASTER_RC_BUS_ERROR ;
}
if ( I2C_SR1 ( i2c ) & I2C_SR1_AF ) { // data has not been acknowledged
return I2C_MASTER_RC_NAK ;
2018-02-18 15:21:18 +01:00
}
}
2018-04-03 16:59:02 +02:00
return I2C_MASTER_RC_NONE ;
2018-02-18 15:21:18 +01:00
}
2018-04-03 16:59:02 +02:00
enum i2c_master_rc i2c_master_slave_read ( uint32_t i2c , uint16_t slave , bool address_10bit , uint8_t * data , size_t data_size )
2017-04-03 13:07:03 +02:00
{
2019-03-26 18:35:46 +01:00
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
2017-04-03 13:07:03 +02:00
2019-12-05 13:26:49 +01:00
enum i2c_master_rc rc = I2C_MASTER_RC_NONE ; // to store I²C return codes
2018-04-03 16:59:02 +02:00
rc = i2c_master_start ( i2c ) ; // send (re-)start condition
2019-12-05 13:26:49 +01:00
if ( I2C_MASTER_RC_NONE ! = rc ) {
2018-04-03 16:59:02 +02:00
return rc ;
2017-04-03 13:07:03 +02:00
}
2018-04-03 16:59:02 +02:00
rc = i2c_master_select_slave ( i2c , slave , address_10bit , false ) ; // select slave to read
2019-03-26 18:25:23 +01:00
if ( I2C_MASTER_RC_NONE ! = rc ) {
2017-04-03 13:07:03 +02:00
goto error ;
}
2019-03-26 18:25:23 +01:00
if ( NULL ! = data & & data_size > 0 ) { // only read data if needed
2020-02-27 13:05:35 +01:00
rc = i2c_master_read ( i2c , data , data_size ) ; // read data (includes stop)
2019-03-26 18:25:23 +01:00
if ( I2C_MASTER_RC_NONE ! = rc ) {
2018-02-18 15:21:18 +01:00
goto error ;
}
2020-02-27 13:05:35 +01:00
} else {
i2c_master_stop ( i2c ) ; // sent stop condition
2018-02-18 15:21:18 +01:00
}
2017-04-03 13:07:03 +02:00
2018-04-03 16:59:02 +02:00
rc = I2C_MASTER_RC_NONE ; // all went well
2018-02-18 15:21:18 +01:00
error :
2020-02-27 13:05:35 +01:00
if ( I2C_MASTER_RC_NONE ! = rc ) {
i2c_master_stop ( i2c ) ; // sent stop condition
}
2018-04-03 16:59:02 +02:00
return rc ;
2018-02-18 15:21:18 +01:00
}
2018-04-03 16:59:02 +02:00
enum i2c_master_rc i2c_master_slave_write ( uint32_t i2c , uint16_t slave , bool address_10bit , const uint8_t * data , size_t data_size )
2018-02-18 15:21:18 +01:00
{
2019-03-26 18:35:46 +01:00
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
2018-02-18 15:21:18 +01:00
2019-12-05 13:26:49 +01:00
enum i2c_master_rc rc = I2C_MASTER_RC_NONE ; // to store I²C return codes
2018-04-03 16:59:02 +02:00
rc = i2c_master_start ( i2c ) ; // send (re-)start condition
2019-03-26 18:25:23 +01:00
if ( I2C_MASTER_RC_NONE ! = rc ) {
2018-04-03 16:59:02 +02:00
return rc ;
2017-04-03 13:07:03 +02:00
}
2018-04-03 16:59:02 +02:00
rc = i2c_master_select_slave ( i2c , slave , address_10bit , true ) ; // select slave to write
2019-03-26 18:25:23 +01:00
if ( I2C_MASTER_RC_NONE ! = rc ) {
2017-04-03 13:07:03 +02:00
goto error ;
}
2019-03-26 18:25:23 +01:00
if ( NULL ! = data & & data_size > 0 ) { // write data only is some is available
2018-04-03 16:59:02 +02:00
rc = i2c_master_write ( i2c , data , data_size ) ; // write data
2019-03-26 18:25:23 +01:00
if ( I2C_MASTER_RC_NONE ! = rc ) {
2017-04-03 13:07:03 +02:00
goto error ;
}
}
2018-04-03 16:59:02 +02:00
rc = I2C_MASTER_RC_NONE ; // all went well
2018-02-18 15:21:18 +01:00
error :
2018-04-03 16:59:02 +02:00
i2c_master_stop ( i2c ) ; // sent stop condition
return rc ;
2018-02-18 15:21:18 +01:00
}
2018-04-03 16:59:02 +02:00
enum i2c_master_rc i2c_master_address_read ( uint32_t i2c , uint16_t slave , bool address_10bit , const uint8_t * address , size_t address_size , uint8_t * data , size_t data_size )
2018-02-18 15:21:18 +01:00
{
2019-03-26 18:35:46 +01:00
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
2018-02-18 15:21:18 +01:00
2019-12-05 13:26:49 +01:00
enum i2c_master_rc rc = I2C_MASTER_RC_NONE ; // to store I²C return codes
2018-04-03 16:59:02 +02:00
rc = i2c_master_start ( i2c ) ; // send (re-)start condition
2019-03-26 18:25:23 +01:00
if ( I2C_MASTER_RC_NONE ! = rc ) {
2018-04-03 16:59:02 +02:00
return rc ;
}
rc = i2c_master_select_slave ( i2c , slave , address_10bit , true ) ; // select slave to write
2019-03-26 18:25:23 +01:00
if ( I2C_MASTER_RC_NONE ! = rc ) {
2018-04-03 16:59:02 +02:00
goto error ;
}
2018-02-18 15:21:18 +01:00
// write address
2019-03-26 18:25:23 +01:00
if ( NULL ! = address & & address_size > 0 ) {
2018-04-03 16:59:02 +02:00
rc = i2c_master_write ( i2c , address , address_size ) ; // send memory address
2019-03-26 18:25:23 +01:00
if ( I2C_MASTER_RC_NONE ! = rc ) {
2018-02-18 15:21:18 +01:00
goto error ;
}
}
// read data
2019-03-26 18:25:23 +01:00
if ( NULL ! = data & & data_size > 0 ) {
2018-04-03 16:59:02 +02:00
rc = i2c_master_start ( i2c ) ; // send re-start condition
2019-03-26 18:25:23 +01:00
if ( I2C_MASTER_RC_NONE ! = rc ) {
2018-04-03 16:59:02 +02:00
return rc ;
2018-02-18 15:21:18 +01:00
}
2018-04-03 16:59:02 +02:00
rc = i2c_master_select_slave ( i2c , slave , address_10bit , false ) ; // select slave to read
2019-03-26 18:25:23 +01:00
if ( I2C_MASTER_RC_NONE ! = rc ) {
2018-02-18 15:21:18 +01:00
goto error ;
}
2020-02-27 13:06:29 +01:00
rc = i2c_master_read ( i2c , data , data_size ) ; // read memory (includes stop)
2019-03-26 18:25:23 +01:00
if ( I2C_MASTER_RC_NONE ! = rc ) {
2017-04-03 13:07:03 +02:00
goto error ;
}
2020-02-27 13:06:29 +01:00
} else {
i2c_master_stop ( i2c ) ; // sent stop condition
2017-04-03 13:07:03 +02:00
}
2020-02-27 13:06:29 +01:00
rc = I2C_MASTER_RC_NONE ;
2017-04-03 13:07:03 +02:00
error :
2020-02-27 13:06:29 +01:00
if ( I2C_MASTER_RC_NONE ! = rc ) { // only send stop on error
i2c_master_stop ( i2c ) ; // sent stop condition
}
2018-04-03 16:59:02 +02:00
return rc ;
2018-02-18 15:21:18 +01:00
}
2018-04-03 16:59:02 +02:00
enum i2c_master_rc i2c_master_address_write ( uint32_t i2c , uint16_t slave , bool address_10bit , const uint8_t * address , size_t address_size , const uint8_t * data , size_t data_size )
2018-02-18 15:21:18 +01:00
{
2019-03-26 18:35:46 +01:00
cm3_assert ( I2C1 = = i2c | | I2C2 = = i2c ) ;
2020-02-27 13:06:29 +01:00
if ( SIZE_MAX - address_size < data_size ) { // prevent integer overflow
return I2C_MASTER_RC_OTHER ;
}
if ( address_size + data_size > 10 * 1024 ) { // we won't enough RAM
return I2C_MASTER_RC_OTHER ;
}
if ( address_size > 0 & & NULL = = address ) {
return I2C_MASTER_RC_OTHER ;
}
if ( data_size > 0 & & NULL = = data ) {
return I2C_MASTER_RC_OTHER ;
}
2018-02-18 15:21:18 +01:00
2020-02-27 13:06:29 +01:00
uint8_t buffer [ address_size + data_size ] ;
2019-12-05 13:26:49 +01:00
enum i2c_master_rc rc = I2C_MASTER_RC_NONE ; // to store I²C return codes
2018-04-03 16:59:02 +02:00
rc = i2c_master_start ( i2c ) ; // send (re-)start condition
2019-03-26 18:25:23 +01:00
if ( I2C_MASTER_RC_NONE ! = rc ) {
2018-04-03 16:59:02 +02:00
return rc ;
2018-02-18 15:21:18 +01:00
}
2018-04-03 16:59:02 +02:00
rc = i2c_master_select_slave ( i2c , slave , address_10bit , true ) ; // select slave to write
2019-03-26 18:25:23 +01:00
if ( I2C_MASTER_RC_NONE ! = rc ) {
2018-02-18 15:21:18 +01:00
goto error ;
}
2018-04-03 16:59:02 +02:00
2020-02-27 13:06:29 +01:00
// we can't send the address then the data size short address will cause a stop (because of how crappy the STM32F10x I²C peripheral is)
if ( address ) {
for ( size_t i = 0 ; i < address_size ; i + + ) {
buffer [ i ] = address [ i ] ;
2018-02-18 15:21:18 +01:00
}
2017-04-03 13:07:03 +02:00
}
2020-02-27 13:06:29 +01:00
if ( data ) {
for ( size_t i = 0 ; i < data_size ; i + + ) {
buffer [ address_size + i ] = data [ i ] ;
2018-02-18 15:21:18 +01:00
}
}
2020-02-27 13:06:29 +01:00
rc = i2c_master_write ( i2c , buffer , address_size + data_size ) ; // send memory address
if ( I2C_MASTER_RC_NONE ! = rc ) {
goto error ;
}
2018-02-18 15:21:18 +01:00
error :
2020-02-27 13:05:35 +01:00
rc = i2c_master_stop ( i2c ) ; // sent stop condition
2018-04-03 16:59:02 +02:00
return rc ;
2017-04-03 13:07:03 +02:00
}