2017-04-03 13:07:03 +02:00
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*
*/
/** library to communicate using I2C as master (code)
2017-08-02 13:57:36 +02:00
* @ file i2c_master . c
2017-04-03 13:07:03 +02:00
* @ author King Kévin < kingkevin @ cuvoodoo . info >
2018-02-06 11:53:50 +01:00
* @ date 2017 - 2018
2018-03-21 10:10:19 +01: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 */
# include <libopencm3/stm32/rcc.h> // real-time control clock library
# include <libopencm3/stm32/gpio.h> // general purpose input output library
# include <libopencm3/stm32/i2c.h> // I2C library
2018-02-06 11:53:50 +01:00
/* own libraries */
2017-04-03 13:07:03 +02:00
# include "global.h" // global utilities
# include "i2c_master.h" // I2C header and definitions
2018-03-20 09:37:31 +01:00
/** get RCC for I2C based on I2C identifier
* @ param [ in ] i2c I2C base address
* @ return RCC address for I2C peripheral
*/
static uint32_t RCC_I2C ( uint32_t i2c )
{
switch ( i2c ) {
case I2C1 :
return RCC_I2C1 ;
break ;
case I2C2 :
return RCC_I2C2 ;
break ;
default :
while ( true ) ;
}
}
2018-03-20 14:25:51 +01:00
2018-03-20 09:37:31 +01:00
/** get RCC for GPIO port for SCL pin based on I2C identifier
* @ param [ in ] i2c I2C base address
* @ return RCC GPIO address
*/
static uint32_t RCC_GPIO_PORT_SCL ( uint32_t i2c )
{
switch ( i2c ) {
case I2C1 :
case I2C2 :
return RCC_GPIOB ;
break ;
default :
while ( true ) ;
}
}
2018-03-20 14:25:51 +01:00
2018-03-20 09:37:31 +01:00
/** get RCC for GPIO port for SDA pin based on I2C identifier
* @ param [ in ] i2c I2C base address
* @ return RCC GPIO address
*/
static uint32_t RCC_GPIO_PORT_SDA ( uint32_t i2c )
{
switch ( i2c ) {
case I2C1 :
case I2C2 :
return RCC_GPIOB ;
break ;
default :
while ( true ) ;
}
}
2018-03-20 14:25:51 +01:00
2018-03-20 09:37:31 +01:00
/** get GPIO port for SCL pin based on I2C identifier
* @ param [ in ] i2c I2C base address
* @ return GPIO address
*/
static uint32_t GPIO_PORT_SCL ( uint32_t i2c )
{
switch ( i2c ) {
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 :
while ( true ) ;
}
}
2018-03-20 14:25:51 +01:00
2018-03-20 09:37:31 +01:00
/** get GPIO port for SDA pin based on I2C identifier
* @ param [ in ] i2c I2C base address
* @ return GPIO address
*/
static uint32_t GPIO_PORT_SDA ( uint32_t i2c )
{
switch ( i2c ) {
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 :
while ( true ) ;
}
}
2018-03-20 14:25:51 +01:00
2018-03-20 09:37:31 +01:00
/** get GPIO pin for SCL pin based on I2C identifier
* @ param [ in ] i2c I2C base address
* @ return GPIO address
*/
static uint32_t GPIO_PIN_SCL ( uint32_t i2c )
{
switch ( i2c ) {
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 :
while ( true ) ;
}
}
2018-03-20 14:25:51 +01:00
2018-03-20 09:37:31 +01:00
/** get GPIO pin for SDA pin based on I2C identifier
* @ param [ in ] i2c I2C base address
* @ return GPIO address
*/
static uint32_t GPIO_PIN_SDA ( uint32_t i2c )
{
switch ( i2c ) {
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 :
while ( true ) ;
}
}
2018-03-20 14:25:51 +01:00
void i2c_master_setup ( uint32_t i2c , uint16_t frequency )
2017-04-03 13:07:03 +02:00
{
2018-03-20 09:37:31 +01:00
// check I2C peripheral
if ( I2C1 ! = i2c & & I2C2 ! = i2c ) {
while ( true ) ;
}
2017-04-03 13:07:03 +02:00
// configure I2C peripheral
2018-03-20 09:37:31 +01:00
rcc_periph_clock_enable ( RCC_GPIO_PORT_SCL ( i2c ) ) ; // enable clock for I2C I/O peripheral
gpio_set ( GPIO_PORT_SCL ( i2c ) , GPIO_PIN_SCL ( i2c ) ) ; // already put signal high to avoid small pulse
2018-03-21 10:10:19 +01:00
gpio_set_mode ( GPIO_PORT_SCL ( i2c ) , GPIO_MODE_OUTPUT_10_MHZ , GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN , GPIO_PIN_SCL ( i2c ) ) ; // setup I2C I/O pins
2018-03-20 09:37:31 +01:00
rcc_periph_clock_enable ( RCC_GPIO_PORT_SDA ( i2c ) ) ; // enable clock for I2C I/O peripheral
gpio_set ( GPIO_PORT_SDA ( i2c ) , GPIO_PIN_SDA ( i2c ) ) ; // already put signal high to avoid small pulse
2018-03-21 10:10:19 +01:00
gpio_set_mode ( GPIO_PORT_SDA ( i2c ) , GPIO_MODE_OUTPUT_10_MHZ , GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN , GPIO_PIN_SDA ( i2c ) ) ; // setup I2C I/O pins
2017-04-03 13:07:03 +02:00
rcc_periph_clock_enable ( RCC_AFIO ) ; // enable clock for alternate function
2018-03-20 09:37:31 +01:00
rcc_periph_clock_enable ( RCC_I2C ( i2c ) ) ; // enable clock for I2C peripheral
2018-03-21 10:10:19 +01:00
i2c_reset ( i2c ) ; // reset peripheral domain
2018-03-20 09:37:31 +01:00
i2c_peripheral_disable ( i2c ) ; // I2C needs to be disable to be configured
2018-03-21 10:10:19 +01: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 ;
} else if ( frequency > 400 ) { // limit frequency to 400 kHz
2018-03-20 14:25:51 +01:00
frequency = 400 ;
}
2018-03-20 09:37:31 +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-03-21 10:10:19 +01:00
if ( frequency > 100 ) { // use fast mode for frequencies over 100 kHz
i2c_set_fast_mode ( i2c ) ; // set fast mode (Fm)
2018-03-20 14:25:51 +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-03-21 10:10:19 +01:00
} else { // use fast mode for frequencies below 100 kHz
i2c_set_standard_mode ( i2c ) ; // set standard mode (Sm)
2018-03-20 14:25:51 +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)
2017-04-03 13:07:03 +02:00
}
2018-03-20 09:37:31 +01:00
i2c_peripheral_enable ( i2c ) ; // enable I2C after configuration completed
}
void i2c_master_release ( uint32_t i2c )
{
2018-03-20 14:25:51 +01:00
// check I2C peripheral
if ( I2C1 ! = i2c & & I2C2 ! = i2c ) {
while ( true ) ;
}
2018-03-20 09:37:31 +01:00
i2c_reset ( i2c ) ; // reset I2C peripheral configuration
i2c_peripheral_disable ( i2c ) ; // disable I2C peripheral
rcc_periph_clock_disable ( RCC_I2C ( i2c ) ) ; // disable clock for I2C peripheral
gpio_set_mode ( GPIO_PORT_SCL ( i2c ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , GPIO_PIN_SCL ( i2c ) ) ; // put I2C 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 I2C I/O pins back to floating
}
bool i2c_master_check_signals ( uint32_t i2c )
{
2018-03-20 14:25:51 +01:00
// check I2C peripheral
if ( I2C1 ! = i2c & & I2C2 ! = i2c ) {
while ( true ) ;
}
2018-04-02 20:37:52 +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
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
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 ;
2017-04-03 13:07:03 +02:00
}
2018-03-21 10:10:19 +01:00
void i2c_master_reset ( uint32_t i2c )
{
// check I2C peripheral
if ( I2C1 ! = i2c & & I2C2 ! = i2c ) {
while ( true ) ;
}
// follow procedure described in STM32F10xxC/D/E Errata sheet, Section 2.14.7
i2c_peripheral_disable ( i2c ) ; // disable i2c peripheral
gpio_set_mode ( GPIO_PORT_SCL ( i2c ) , GPIO_MODE_OUTPUT_10_MHZ , GPIO_CNF_OUTPUT_OPENDRAIN , GPIO_PIN_SCL ( i2c ) ) ; // put I2C I/O pins to general output
gpio_set ( GPIO_PORT_SCL ( i2c ) , GPIO_PIN_SCL ( i2c ) ) ; // set high
while ( ! gpio_get ( GPIO_PORT_SCL ( i2c ) , GPIO_PIN_SCL ( i2c ) ) ) ; // ensure it is high
gpio_set_mode ( GPIO_PORT_SDA ( i2c ) , GPIO_MODE_OUTPUT_10_MHZ , GPIO_CNF_OUTPUT_OPENDRAIN , GPIO_PIN_SDA ( i2c ) ) ; // put I2C I/O pins to general output
gpio_set ( GPIO_PORT_SDA ( i2c ) , GPIO_PIN_SDA ( i2c ) ) ; // set high
while ( ! gpio_get ( GPIO_PORT_SDA ( i2c ) , GPIO_PIN_SDA ( i2c ) ) ) ; // ensure it is high
gpio_clear ( GPIO_PORT_SDA ( i2c ) , GPIO_PIN_SDA ( i2c ) ) ; // set low (try first transition)
while ( gpio_get ( GPIO_PORT_SDA ( i2c ) , GPIO_PIN_SDA ( i2c ) ) ) ; // ensure it is low
gpio_clear ( GPIO_PORT_SCL ( i2c ) , GPIO_PIN_SCL ( i2c ) ) ; // set low (try first transition)
while ( gpio_get ( GPIO_PORT_SCL ( i2c ) , GPIO_PIN_SCL ( i2c ) ) ) ; // ensure it is low
gpio_set ( GPIO_PORT_SCL ( i2c ) , GPIO_PIN_SCL ( i2c ) ) ; // set high (try second transition)
while ( ! gpio_get ( GPIO_PORT_SCL ( i2c ) , GPIO_PIN_SCL ( i2c ) ) ) ; // ensure it is high
gpio_set ( GPIO_PORT_SDA ( i2c ) , GPIO_PIN_SDA ( i2c ) ) ; // set high (try second transition)
while ( ! gpio_get ( GPIO_PORT_SDA ( i2c ) , GPIO_PIN_SDA ( i2c ) ) ) ; // ensure it is high
gpio_set_mode ( GPIO_PORT_SCL ( i2c ) , GPIO_MODE_OUTPUT_10_MHZ , GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN , GPIO_PIN_SCL ( i2c ) ) ; // set I2C 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 I2C I/O pins back
I2C_CR1 ( i2c ) | = I2C_CR1_SWRST ; // reset device
I2C_CR1 ( i2c ) & = ~ I2C_CR1_SWRST ; // reset device
i2c_peripheral_enable ( i2c ) ; // re-enable device
}
2018-03-20 09:37:31 +01:00
2018-03-21 14:13:55 +01:00
enum i2c_master_rc i2c_master_start ( uint32_t i2c )
2017-04-03 13:07:03 +02:00
{
2018-03-20 09:37:31 +01:00
// check I2C peripheral
if ( I2C1 ! = i2c & & I2C2 ! = i2c ) {
while ( true ) ;
}
2018-02-06 11:53:50 +01:00
// send (re-)start condition
2018-03-21 10:10:19 +01:00
if ( I2C_CR1 ( i2c ) & ( I2C_CR1_START | I2C_CR1_STOP ) ) { // ensure start or stop operations are not in progress
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_START_STOP_IN_PROGESS ;
2017-04-03 13:07:03 +02:00
}
2018-03-21 10:10:19 +01:00
i2c_send_start ( i2c ) ; // send start condition to start transaction
while ( I2C_CR1 ( i2c ) & I2C_CR1_START ) ; // wait until start condition has been accepted and cleared
while ( ( I2C_CR1 ( i2c ) & I2C_CR1_START ) ) ; // wait until start condition is accepted and cleared
while ( ! ( I2C_SR1 ( i2c ) & I2C_SR1_SB ) ) ; // wait until start condition is transmitted
2018-03-20 09:37:31 +01:00
if ( ! ( I2C_SR2 ( i2c ) & I2C_SR2_MSL ) ) { // verify if in master mode
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NOT_MASTER ;
2018-02-06 11:53:50 +01:00
}
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NONE ;
2018-02-06 11:53:50 +01:00
}
2018-03-21 14:13:55 +01:00
enum i2c_master_rc i2c_master_select_slave ( uint32_t i2c , uint16_t slave , bool address_10bit , bool write )
2018-02-06 11:53:50 +01:00
{
2018-03-20 09:37:31 +01:00
// check I2C peripheral
if ( I2C1 ! = i2c & & I2C2 ! = i2c ) {
while ( true ) ;
}
2018-03-21 14:13:55 +01:00
enum i2c_master_rc rc = I2C_MASTER_RC_NONE ; // to store I2C return codes
2018-03-21 10:10:19 +01:00
if ( ! ( I2C_SR1 ( i2c ) & I2C_SR1_SB ) ) { // start condition has not been sent
2018-03-21 14:13:55 +01:00
rc = i2c_master_start ( i2c ) ; // send start condition
if ( I2C_MASTER_RC_NONE ! = rc ) {
return rc ;
2018-02-06 11:53:50 +01:00
}
2018-03-21 10:10:19 +01:00
}
2018-03-20 14:25:51 +01:00
if ( ! ( I2C_SR2 ( i2c ) & I2C_SR2_MSL ) ) { // I2C device is not in master mode
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NOT_MASTER ;
2017-04-03 13:07:03 +02:00
}
// select slave
2018-03-21 10:10:19 +01:00
if ( ! address_10bit ) { // 7-bit address
I2C_SR1 ( i2c ) & = ~ ( I2C_SR1_AF ) ; // clear acknowledgement failure
i2c_send_7bit_address ( i2c , slave , write ? I2C_WRITE : I2C_READ ) ; // select slave, with read/write flag
while ( ! ( I2C_SR1 ( i2c ) & ( I2C_SR1_ADDR | I2C_SR1_AF ) ) ) ; // wait until address is transmitted
if ( I2C_SR1 ( i2c ) & I2C_SR1_AF ) { // address has not been acknowledged
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NAK ;
2018-03-21 10:10:19 +01:00
}
} else { // 10-bit address
// send first part of address
I2C_SR1 ( i2c ) & = ~ ( I2C_SR1_AF ) ; // clear acknowledgement failure
I2C_DR ( i2c ) = 11110000 | ( ( ( slave > > 8 ) & 0x3 ) < < 1 ) ; // send first header (11110xx0, where xx are 2 MSb of slave address)
while ( ! ( I2C_SR1 ( i2c ) & ( I2C_SR1_ADD10 | I2C_SR1_AF ) ) ) ; // wait until first part of address is transmitted
if ( I2C_SR1 ( i2c ) & I2C_SR1_AF ) { // address has not been acknowledged
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NAK ;
2018-03-21 10:10:19 +01:00
}
// send second part of address
I2C_SR1 ( i2c ) & = ~ ( I2C_SR1_AF ) ; // clear acknowledgement failure
I2C_DR ( i2c ) = ( slave & 0xff ) ; // send remaining of address
while ( ! ( I2C_SR1 ( i2c ) & ( I2C_SR1_ADDR | I2C_SR1_AF ) ) ) ; // wait until remaining part of address is transmitted
if ( I2C_SR1 ( i2c ) & I2C_SR1_AF ) { // address has not been acknowledged
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NAK ;
2018-03-21 10:10:19 +01:00
}
// go into receive mode if necessary
if ( ! write ) {
2018-03-21 14:13:55 +01:00
rc = i2c_master_start ( i2c ) ; // send start condition
if ( I2C_MASTER_RC_NONE ! = rc ) {
return rc ;
2018-03-21 10:10:19 +01:00
}
// send first part of address with receive flag
I2C_SR1 ( i2c ) & = ~ ( I2C_SR1_AF ) ; // clear acknowledgement failure
I2C_DR ( i2c ) = 11110001 | ( ( ( slave > > 8 ) & 0x3 ) < < 1 ) ; // send header (11110xx1, where xx are 2 MSb of slave address)
while ( ! ( I2C_SR1 ( i2c ) & ( I2C_SR1_ADDR | I2C_SR1_AF ) ) ) ; // wait until remaining part of address is transmitted
if ( I2C_SR1 ( i2c ) & I2C_SR1_AF ) { // address has not been acknowledged
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NAK ;
2018-03-21 10:10:19 +01:00
}
}
2017-04-03 13:07:03 +02:00
}
2018-02-06 11:53:50 +01:00
if ( write ) {
2018-03-20 09:37:31 +01:00
if ( ! ( ( I2C_SR2 ( i2c ) & I2C_SR2_TRA ) ) ) { // verify we are in transmit mode (and read SR2 to clear ADDR)
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NOT_TRANSMIT ;
2018-02-06 11:53:50 +01:00
}
} else {
2018-03-20 09:37:31 +01:00
if ( ( I2C_SR2 ( i2c ) & I2C_SR2_TRA ) ) { // verify we are in read mode (and read SR2 to clear ADDR)
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NOT_RECEIVE ;
2017-04-03 13:07:03 +02:00
}
}
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NONE ;
2018-02-06 11:53:50 +01:00
}
2017-04-03 13:07:03 +02:00
2018-03-21 14:13:55 +01:00
enum i2c_master_rc i2c_master_read ( uint32_t i2c , uint8_t * data , size_t data_size )
2018-02-06 11:53:50 +01:00
{
2018-03-20 09:37:31 +01:00
// check I2C peripheral
if ( I2C1 ! = i2c & & I2C2 ! = i2c ) {
while ( true ) ;
}
2018-02-06 11:53:50 +01:00
// sanity check
if ( data = = NULL | | data_size = = 0 ) { // no data to read
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NONE ;
2018-02-06 11:53:50 +01:00
}
2018-03-21 10:10:19 +01:00
if ( ! ( I2C_SR2 ( i2c ) & I2C_SR2_MSL ) ) { // I2C device is not master
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NOT_MASTER ;
2018-03-21 10:10:19 +01:00
}
if ( ( I2C_SR2 ( i2c ) & I2C_SR2_TRA ) ) { // I2C device not in receiver mode
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NOT_RECEIVE ;
}
if ( I2C_SR1 ( i2c ) & I2C_SR1_AF ) { // check if the previous transaction went well
return I2C_MASTER_RC_NOT_READY ;
2017-04-03 13:07:03 +02:00
}
// read data
for ( size_t i = 0 ; i < data_size ; i + + ) { // read bytes
if ( i = = data_size - 1 ) { // prepare to sent NACK for last byte
2018-03-20 09:37:31 +01:00
i2c_disable_ack ( i2c ) ; // NACK received to stop slave transmission
2017-04-03 13:07:03 +02:00
} else {
2018-03-20 09:37:31 +01:00
i2c_enable_ack ( i2c ) ; // ACK received byte to continue slave transmission
2017-04-03 13:07:03 +02:00
}
2018-03-21 10:10:19 +01:00
while ( ! ( I2C_SR1 ( i2c ) & I2C_SR1_RxNE ) ) ; // wait until byte has been received
2018-03-20 09:37:31 +01:00
data [ i ] = i2c_get_data ( i2c ) ; // read received byte
2017-04-03 13:07:03 +02:00
}
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NONE ;
2018-02-06 11:53:50 +01:00
}
2018-03-21 14:13:55 +01:00
enum i2c_master_rc i2c_master_write ( uint32_t i2c , const uint8_t * data , size_t data_size )
2018-02-06 11:53:50 +01:00
{
2018-03-20 09:37:31 +01:00
// check I2C peripheral
if ( I2C1 ! = i2c & & I2C2 ! = i2c ) {
while ( true ) ;
}
2018-02-06 11:53:50 +01:00
// sanity check
if ( data = = NULL | | data_size = = 0 ) { // no data to write
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NONE ;
2018-02-06 11:53:50 +01:00
}
2018-03-21 10:10:19 +01:00
if ( ! ( I2C_SR2 ( i2c ) & I2C_SR2_MSL ) ) { // I2C device is not master
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NOT_MASTER ;
2018-03-21 10:10:19 +01:00
}
if ( ! ( I2C_SR2 ( i2c ) & I2C_SR2_TRA ) ) { // I2C device not in transmitter mode
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NOT_TRANSMIT ;
}
if ( I2C_SR1 ( i2c ) & I2C_SR1_AF ) { // check if the previous transaction went well
return I2C_MASTER_RC_NOT_READY ;
2018-02-06 11:53:50 +01:00
}
// write data
for ( size_t i = 0 ; i < data_size ; i + + ) { // write bytes
2018-03-21 10:10:19 +01:00
I2C_SR1 ( i2c ) & = ~ ( I2C_SR1_AF ) ; // clear acknowledgement failure
2018-03-20 09:37:31 +01:00
i2c_send_data ( i2c , data [ i ] ) ; // send byte to be written in memory
2018-03-21 10:10:19 +01:00
while ( ! ( I2C_SR1 ( i2c ) & ( I2C_SR1_TxE | I2C_SR1_AF ) ) ) ; // wait until byte has been transmitted
if ( I2C_SR1 ( i2c ) & I2C_SR1_AF ) { // data has not been acknowledged
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NAK ;
2018-02-06 11:53:50 +01:00
}
}
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NONE ;
2018-02-06 11:53:50 +01:00
}
2018-03-21 14:13:55 +01:00
enum i2c_master_rc i2c_master_stop ( uint32_t i2c )
2018-02-06 11:53:50 +01:00
{
2018-03-20 09:37:31 +01:00
// check I2C peripheral
if ( I2C1 ! = i2c & & I2C2 ! = i2c ) {
while ( true ) ;
}
2018-02-06 11:53:50 +01:00
// sanity check
2018-03-20 09:37:31 +01:00
if ( ! ( I2C_SR2 ( i2c ) & I2C_SR2_BUSY ) ) { // release is not busy
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NONE ; // bus has probably already been released
2017-04-03 13:07:03 +02:00
}
2018-02-06 11:53:50 +01:00
// send stop condition
2018-03-21 10:10:19 +01:00
if ( I2C_CR1 ( i2c ) & ( I2C_CR1_START | I2C_CR1_STOP ) ) { // ensure start or stop operations are not in progress
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_START_STOP_IN_PROGESS ;
2017-04-03 13:07:03 +02:00
}
2018-03-21 10:10:19 +01:00
i2c_send_stop ( i2c ) ; // send stop to release bus
while ( ( I2C_CR1 ( i2c ) & I2C_CR1_STOP ) ) ; // wait until stop condition is accepted and cleared
while ( ( I2C_SR2 ( i2c ) & I2C_SR2_MSL ) ) ; // wait until bus released (non master mode)
2018-03-21 14:13:55 +01:00
return I2C_MASTER_RC_NONE ;
2017-04-03 13:07:03 +02:00
}
2018-03-21 14:13:55 +01: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
{
2018-03-20 09:37:31 +01:00
// check I2C peripheral
if ( I2C1 ! = i2c & & I2C2 ! = i2c ) {
while ( true ) ;
}
2018-03-21 14:13:55 +01:00
enum i2c_master_rc rc = I2C_MASTER_RC_NONE ; // to store I2C return codes
rc = i2c_master_start ( i2c ) ; // send (re-)start condition
if ( I2C_MASTER_RC_NONE ! = rc ) {
return rc ;
2017-04-03 13:07:03 +02:00
}
2018-03-21 14:13:55 +01:00
rc = i2c_master_select_slave ( i2c , slave , address_10bit , false ) ; // select slave to read
if ( I2C_MASTER_RC_NONE ! = rc ) {
2017-04-03 13:07:03 +02:00
goto error ;
}
2018-03-21 10:10:19 +01:00
if ( NULL ! = data & & data_size > 0 ) { // only read data if needed
2018-03-21 14:13:55 +01:00
rc = i2c_master_read ( i2c , data , data_size ) ;
if ( I2C_MASTER_RC_NONE ! = rc ) {
2018-02-06 11:53:50 +01:00
goto error ;
}
}
2017-04-03 13:07:03 +02:00
2018-03-21 14:13:55 +01:00
rc = I2C_MASTER_RC_NONE ; // all went well
2018-02-06 11:53:50 +01:00
error :
2018-03-20 09:37:31 +01:00
i2c_master_stop ( i2c ) ; // sent stop condition
2018-03-21 14:13:55 +01:00
return rc ;
2018-02-06 11:53:50 +01:00
}
2018-03-21 14:13:55 +01: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-06 11:53:50 +01:00
{
2018-03-20 09:37:31 +01:00
// check I2C peripheral
if ( I2C1 ! = i2c & & I2C2 ! = i2c ) {
while ( true ) ;
}
2018-03-21 14:13:55 +01:00
enum i2c_master_rc rc = I2C_MASTER_RC_NONE ; // to store I2C return codes
rc = i2c_master_start ( i2c ) ; // send (re-)start condition
if ( I2C_MASTER_RC_NONE ! = rc ) {
return rc ;
2017-04-03 13:07:03 +02:00
}
2018-03-21 14:13:55 +01:00
rc = i2c_master_select_slave ( i2c , slave , address_10bit , true ) ; // select slave to write
if ( I2C_MASTER_RC_NONE ! = rc ) {
2017-04-03 13:07:03 +02:00
goto error ;
}
2018-03-21 10:10:19 +01:00
if ( NULL ! = data & & data_size > 0 ) { // write data only is some is available
2018-03-21 14:13:55 +01:00
rc = i2c_master_write ( i2c , data , data_size ) ; // write data
if ( I2C_MASTER_RC_NONE ! = rc ) {
2017-04-03 13:07:03 +02:00
goto error ;
}
}
2018-03-21 14:13:55 +01:00
rc = I2C_MASTER_RC_NONE ; // all went well
2018-02-06 11:53:50 +01:00
error :
2018-03-20 09:37:31 +01:00
i2c_master_stop ( i2c ) ; // sent stop condition
2018-03-21 14:13:55 +01:00
return rc ;
2018-02-06 11:53:50 +01:00
}
2018-03-21 14:13:55 +01: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-06 11:53:50 +01:00
{
2018-03-20 09:37:31 +01:00
// check I2C peripheral
if ( I2C1 ! = i2c & & I2C2 ! = i2c ) {
while ( true ) ;
}
2018-03-21 14:13:55 +01:00
enum i2c_master_rc rc = I2C_MASTER_RC_NONE ; // to store I2C return codes
rc = i2c_master_start ( i2c ) ; // send (re-)start condition
if ( I2C_MASTER_RC_NONE ! = rc ) {
return rc ;
2018-02-06 11:53:50 +01:00
}
2018-03-21 14:13:55 +01:00
rc = i2c_master_select_slave ( i2c , slave , address_10bit , true ) ; // select slave to write
if ( I2C_MASTER_RC_NONE ! = rc ) {
2018-03-21 10:10:19 +01:00
goto error ;
}
2018-02-06 11:53:50 +01:00
// write address
if ( NULL ! = address & & address_size > 0 ) {
2018-03-21 14:13:55 +01:00
rc = i2c_master_write ( i2c , address , address_size ) ; // send memory address
if ( I2C_MASTER_RC_NONE ! = rc ) {
2018-02-06 11:53:50 +01:00
goto error ;
}
}
// read data
if ( NULL ! = data & & data_size > 0 ) {
2018-03-21 14:13:55 +01:00
rc = i2c_master_start ( i2c ) ; // send re-start condition
if ( I2C_MASTER_RC_NONE ! = rc ) {
return rc ;
2018-02-06 11:53:50 +01:00
}
2018-03-21 14:13:55 +01:00
rc = i2c_master_select_slave ( i2c , slave , address_10bit , false ) ; // select slave to read
if ( I2C_MASTER_RC_NONE ! = rc ) {
2018-02-06 11:53:50 +01:00
goto error ;
}
2018-03-21 14:13:55 +01:00
rc = i2c_master_read ( i2c , data , data_size ) ; // read memory
if ( I2C_MASTER_RC_NONE ! = rc ) {
2017-04-03 13:07:03 +02:00
goto error ;
}
}
2018-03-21 14:13:55 +01:00
rc = I2C_MASTER_RC_NONE ; // all went well
2017-04-03 13:07:03 +02:00
error :
2018-03-20 09:37:31 +01:00
i2c_master_stop ( i2c ) ; // sent stop condition
2018-03-21 14:13:55 +01:00
return rc ;
2018-02-06 11:53:50 +01:00
}
2018-03-21 14:13:55 +01: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-06 11:53:50 +01:00
{
2018-03-20 09:37:31 +01:00
// check I2C peripheral
if ( I2C1 ! = i2c & & I2C2 ! = i2c ) {
while ( true ) ;
}
2018-03-21 14:13:55 +01:00
enum i2c_master_rc rc = I2C_MASTER_RC_NONE ; // to store I2C return codes
rc = i2c_master_start ( i2c ) ; // send (re-)start condition
if ( I2C_MASTER_RC_NONE ! = rc ) {
return rc ;
2017-04-03 13:07:03 +02:00
}
2018-03-21 14:13:55 +01:00
rc = i2c_master_select_slave ( i2c , slave , address_10bit , true ) ; // select slave to write
if ( I2C_MASTER_RC_NONE ! = rc ) {
2018-02-06 11:53:50 +01:00
goto error ;
}
2018-03-21 14:13:55 +01:00
// write address
2018-02-06 11:53:50 +01:00
if ( NULL ! = address & & address_size > 0 ) {
2018-03-21 14:13:55 +01:00
rc = i2c_master_write ( i2c , address , address_size ) ; // send memory address
if ( I2C_MASTER_RC_NONE ! = rc ) {
2018-02-06 11:53:50 +01:00
goto error ;
}
2017-04-03 13:07:03 +02:00
}
2018-03-21 14:13:55 +01:00
// read data
2018-02-06 11:53:50 +01:00
if ( NULL ! = data & & data_size > 0 ) {
2018-03-21 14:13:55 +01:00
rc = i2c_master_write ( i2c , data , data_size ) ; // write memory
if ( I2C_MASTER_RC_NONE ! = rc ) {
2018-02-06 11:53:50 +01:00
goto error ;
}
}
2018-03-21 14:13:55 +01:00
rc = I2C_MASTER_RC_NONE ; // all went well
2018-02-06 11:53:50 +01:00
error :
2018-03-20 09:37:31 +01:00
i2c_master_stop ( i2c ) ; // sent stop condition
2018-03-21 14:13:55 +01:00
return rc ;
2017-04-03 13:07:03 +02:00
}