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
2017-04-03 13:07:03 +02:00
* @ note peripherals used : I2C @ ref i2c_master_i2c , timer @ ref i2c_master_timer
*/
/* standard libraries */
# include <stdint.h> // standard integer types
2018-02-06 11:53:50 +01:00
//#include <stdio.h> // standard I/O facilities
2017-04-03 13:07:03 +02:00
# 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
# include <libopencm3/stm32/timer.h> // timer utilities
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
/** @defgroup i2c_master_i2c I2C peripheral used to communicate
* @ {
*/
2018-02-06 11:53:50 +01:00
# define I2C_MASTER_I2C 1 /**< I2C peripheral */
2017-04-03 13:07:03 +02:00
/** @} */
/** @defgroup i2c_master_timer timer peripheral used for timeouts
* @ {
*/
# define I2C_MASTER_TIMER 4 /**< timer peripheral */
# define I2C_MASTER_TIMEOUT 4 /**< timeout factor (compared to expected time) */
/** @} */
void i2c_master_setup ( bool fast )
{
// configure I2C peripheral
rcc_periph_clock_enable ( RCC_I2C_SCL_PORT ( I2C_MASTER_I2C ) ) ; // enable clock for I2C I/O peripheral
gpio_set ( I2C_SCL_PORT ( I2C_MASTER_I2C ) , I2C_SCL_PIN ( I2C_MASTER_I2C ) ) ; // already put signal high to avoid small pulse
gpio_set_mode ( I2C_SCL_PORT ( I2C_MASTER_I2C ) , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN , I2C_SCL_PIN ( I2C_MASTER_I2C ) ) ; // setup I2C I/O pins
rcc_periph_clock_enable ( RCC_I2C_SCL_PORT ( I2C_MASTER_I2C ) ) ; // enable clock for I2C I/O peripheral
gpio_set ( I2C_SDA_PORT ( I2C_MASTER_I2C ) , I2C_SDA_PIN ( I2C_MASTER_I2C ) ) ; // already put signal high to avoid small pulse
gpio_set_mode ( I2C_SDA_PORT ( I2C_MASTER_I2C ) , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN , I2C_SDA_PIN ( I2C_MASTER_I2C ) ) ; // setup I2C I/O pins
rcc_periph_clock_enable ( RCC_AFIO ) ; // enable clock for alternate function
rcc_periph_clock_enable ( RCC_I2C ( I2C_MASTER_I2C ) ) ; // enable clock for I2C peripheral
i2c_reset ( I2C ( I2C_MASTER_I2C ) ) ; // reset configuration
i2c_peripheral_disable ( I2C ( I2C_MASTER_I2C ) ) ; // I2C needs to be disable to be configured
i2c_set_clock_frequency ( I2C ( I2C_MASTER_I2C ) , rcc_apb1_frequency / 1000000 ) ; // configure the peripheral clock to the APB1 freq (where it is connected to)
if ( fast ) {
i2c_set_fast_mode ( I2C ( I2C_MASTER_I2C ) ) ;
i2c_set_ccr ( I2C ( I2C_MASTER_I2C ) , rcc_apb1_frequency / ( 400000 * 2 ) ) ; // set Thigh/Tlow to generate frequency of 400 kHz
i2c_set_trise ( I2C ( I2C_MASTER_I2C ) , ( 300 / ( 1000 / ( rcc_apb1_frequency / 1000000 ) ) ) + 1 ) ; // max rise time for 300 kHz is 300 ns
} else {
i2c_set_standard_mode ( I2C ( I2C_MASTER_I2C ) ) ; // the DS1307 has a maximum I2C SCL freq if 100 kHz (corresponding to the standard mode)
i2c_set_ccr ( I2C ( I2C_MASTER_I2C ) , rcc_apb1_frequency / ( 100000 * 2 ) ) ; // set Thigh/Tlow to generate frequency of 100 kHz
i2c_set_trise ( I2C ( I2C_MASTER_I2C ) , ( 1000 / ( 1000 / ( rcc_apb1_frequency / 1000000 ) ) ) + 1 ) ; // max rise time for 100 kHz is 1000 ns (~1 MHz)
}
i2c_peripheral_enable ( I2C ( I2C_MASTER_I2C ) ) ; // enable I2C after configuration completed
// configure time for timeouts
rcc_periph_clock_enable ( RCC_TIM ( I2C_MASTER_TIMER ) ) ; // enable clock for timer block
timer_reset ( TIM ( I2C_MASTER_TIMER ) ) ; // reset timer state
timer_set_mode ( TIM ( I2C_MASTER_TIMER ) , TIM_CR1_CKD_CK_INT , TIM_CR1_CMS_EDGE , TIM_CR1_DIR_UP ) ; // set timer mode, use undivided timer clock, edge alignment (simple count), and count up
timer_one_shot_mode ( TIM ( I2C_MASTER_TIMER ) ) ; // stop counter after update event (we only need to one timeout and reset before next operation)
if ( fast ) {
timer_set_prescaler ( TIM ( I2C_MASTER_TIMER ) , rcc_ahb_frequency / 400000 - 1 ) ; // set the prescaler so one tick is also one I2C bit (used I2C frequency)
} else {
timer_set_prescaler ( TIM ( I2C_MASTER_TIMER ) , rcc_ahb_frequency / 100000 - 1 ) ; // set the prescaler so one tick is also one I2C bit (used I2C frequency)
}
timer_set_period ( TIM ( I2C_MASTER_TIMER ) , I2C_MASTER_TIMEOUT * 9 ) ; // use factor to wait for all 9 bits to be transmitted
timer_update_on_overflow ( TIM ( I2C_MASTER_TIMER ) ) ; // only use counter overflow as UEV source (use overflow as timeout)
// wait one transaction for the signal to be stable (some slave have issues when an I2C transaction immediately follows)
timer_set_counter ( TIM ( I2C_MASTER_TIMER ) , 0 ) ; // restart timer
timer_clear_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ; // clear flag
timer_enable_counter ( TIM ( I2C_MASTER_TIMER ) ) ; // enable timer for timeouts
while ( ! timer_get_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ) ;
timer_disable_counter ( TIM ( I2C_MASTER_TIMER ) ) ; // disable timer for timeouts
timer_clear_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ; // clear flag
}
2018-02-06 11:53:50 +01:00
bool i2c_master_start ( void )
2017-04-03 13:07:03 +02:00
{
2018-02-06 11:53:50 +01:00
// send (re-)start condition
2017-04-03 13:07:03 +02:00
i2c_send_start ( I2C ( I2C_MASTER_I2C ) ) ; // send start condition to start transaction
2018-02-06 11:53:50 +01:00
timer_set_counter ( TIM ( I2C_MASTER_TIMER ) , 0 ) ; // restart timer
2017-04-03 13:07:03 +02:00
timer_clear_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ; // clear flag
timer_enable_counter ( TIM ( I2C_MASTER_TIMER ) ) ; // enable timer for timeouts
while ( ! ( I2C_SR1 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR1_SB ) & & ! timer_get_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ) ; // wait until start condition is transmitted
timer_disable_counter ( TIM ( I2C_MASTER_TIMER ) ) ; // disable timer for timeouts
if ( timer_get_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ) { // timeout occurred
timer_clear_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ; // clear flag
2018-02-06 11:53:50 +01:00
return false ;
2017-04-03 13:07:03 +02:00
}
if ( ! ( I2C_SR2 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR2_MSL ) ) { // verify if in master mode
2018-02-06 11:53:50 +01:00
return false ;
}
return true ;
}
bool i2c_master_select_slave ( uint8_t slave , bool write )
{
if ( ! ( I2C_SR2 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR2_BUSY ) ) { // I2C device is not busy (start condition has not been sent)
if ( ! i2c_master_start ( ) ) { // send start condition
return false ; // could not send start condition
}
}
if ( ! ( I2C_SR2 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR2_MSL ) ) { // I2C device is already not master mode
return false ;
2017-04-03 13:07:03 +02:00
}
// select slave
2018-02-06 11:53:50 +01:00
i2c_send_7bit_address ( I2C ( I2C_MASTER_I2C ) , slave , write ? I2C_WRITE : I2C_READ ) ; // select slave, with read/write flag
timer_set_counter ( TIM ( I2C_MASTER_TIMER ) , 0 ) ; // restart timer
2017-04-03 13:07:03 +02:00
timer_clear_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ; // clear flag
timer_enable_counter ( TIM ( I2C_MASTER_TIMER ) ) ; // enable timer for timeouts
while ( ! ( I2C_SR1 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR1_ADDR ) & & ! timer_get_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ) ; // wait until address is transmitted
timer_disable_counter ( TIM ( I2C_MASTER_TIMER ) ) ; // disable timer for timeouts
2018-02-06 11:53:50 +01:00
if ( timer_get_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ) { // timeout occurred (no ACK received)
2017-04-03 13:07:03 +02:00
timer_clear_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ; // clear flag
2018-02-06 11:53:50 +01:00
return false ;
2017-04-03 13:07:03 +02:00
}
2018-02-06 11:53:50 +01:00
if ( write ) {
if ( ! ( ( I2C_SR2 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR2_TRA ) ) ) { // verify we are in transmit mode (and read SR2 to clear ADDR)
return false ;
}
} else {
if ( ( I2C_SR2 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR2_TRA ) ) { // verify we are in read mode (and read SR2 to clear ADDR)
return false ;
2017-04-03 13:07:03 +02:00
}
}
2018-02-06 11:53:50 +01:00
return true ;
}
2017-04-03 13:07:03 +02:00
2018-02-06 11:53:50 +01:00
bool i2c_master_read ( uint8_t * data , size_t data_size )
{
// sanity check
if ( data = = NULL | | data_size = = 0 ) { // no data to read
return true ;
}
if ( ! ( I2C_SR2 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR2_BUSY ) ) { // I2C device is not busy (start condition has not been sent)
return false ; // address has probably also not been sent
}
if ( ! ( I2C_SR2 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR2_MSL ) ) { // I2C device not master mode
return false ;
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
i2c_disable_ack ( I2C ( I2C_MASTER_I2C ) ) ; // NACK received to stop slave transmission
i2c_send_stop ( I2C ( I2C_MASTER_I2C ) ) ; // send STOP after receiving byte
} else {
i2c_enable_ack ( I2C ( I2C_MASTER_I2C ) ) ; // ACK received byte to continue slave transmission
}
2018-02-06 11:53:50 +01:00
timer_set_counter ( TIM ( I2C_MASTER_TIMER ) , 0 ) ; // restart timer
2017-04-03 13:07:03 +02:00
timer_clear_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ; // clear flag
timer_enable_counter ( TIM ( I2C_MASTER_TIMER ) ) ; // enable timer for timeouts
while ( ! ( I2C_SR1 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR1_RxNE ) & & ! timer_get_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ) ; // wait until byte has been received
timer_disable_counter ( TIM ( I2C_MASTER_TIMER ) ) ; // disable timer for timeouts
if ( timer_get_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ) { // timeout occurred
timer_clear_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ; // clear flag
2018-02-06 11:53:50 +01:00
return false ;
2017-04-03 13:07:03 +02:00
}
data [ i ] = i2c_get_data ( I2C ( I2C_MASTER_I2C ) ) ; // read received byte
}
2018-02-06 11:53:50 +01:00
return true ;
}
bool i2c_master_write ( const uint8_t * data , size_t data_size )
{
// sanity check
if ( data = = NULL | | data_size = = 0 ) { // no data to write
return true ;
}
if ( ! ( I2C_SR2 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR2_BUSY ) ) { // I2C device is not busy (start condition has not been sent)
return false ; // address has probably also not been sent
}
if ( ! ( I2C_SR2 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR2_MSL ) ) { // I2C device is not master mode
return false ;
}
// write data
for ( size_t i = 0 ; i < data_size ; i + + ) { // write bytes
i2c_send_data ( I2C ( I2C_MASTER_I2C ) , data [ i ] ) ; // send byte to be written in memory
timer_set_counter ( TIM ( I2C_MASTER_TIMER ) , 0 ) ; // restart timer
timer_clear_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ; // clear flag
timer_enable_counter ( TIM ( I2C_MASTER_TIMER ) ) ; // enable timer for timeouts
while ( ! ( I2C_SR1 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR1_TxE ) & & ! timer_get_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ) ; // wait until byte has been transmitted
timer_disable_counter ( TIM ( I2C_MASTER_TIMER ) ) ; // disable timer for timeouts
if ( timer_get_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ) { // timeout occurred (no ACK received)
timer_clear_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ; // clear flag
return false ;
}
}
return true ;
}
void i2c_master_stop ( void )
{
// sanity check
if ( ! ( I2C_SR2 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR2_BUSY ) ) { // release is not busy
return ; // 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
2017-04-03 13:07:03 +02:00
i2c_send_stop ( I2C ( I2C_MASTER_I2C ) ) ; // send stop to release bus
2018-02-06 11:53:50 +01:00
timer_set_counter ( TIM ( I2C_MASTER_TIMER ) , 0 ) ; // restart timer
2017-04-03 13:07:03 +02:00
timer_clear_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ; // clear flag
timer_enable_counter ( TIM ( I2C_MASTER_TIMER ) ) ; // enable timer for timeouts
while ( ( I2C_SR2 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR2_MSL ) & & ! timer_get_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ) ; // wait until bus released (non master mode)
timer_disable_counter ( TIM ( I2C_MASTER_TIMER ) ) ; // disable timer for timeouts
if ( timer_get_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ) { // timeout occurred
timer_clear_flag ( TIM ( I2C_MASTER_TIMER ) , TIM_SR_UIF ) ; // clear flag
}
}
2018-02-06 11:53:50 +01:00
bool i2c_master_slave_read ( uint8_t slave , uint8_t * data , size_t data_size )
2017-04-03 13:07:03 +02:00
{
// sanity check
if ( I2C_SR2 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR2_BUSY ) { // I2C device is busy
return false ;
}
if ( I2C_SR2 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR2_MSL ) { // I2C device is already in master mode
return false ;
}
2018-02-06 11:53:50 +01:00
bool success = false ; // return if read succeeded
2017-04-03 13:07:03 +02:00
// send start condition
2018-02-06 11:53:50 +01:00
if ( ! i2c_master_start ( ) ) {
2017-04-03 13:07:03 +02:00
goto error ;
}
2018-02-06 11:53:50 +01:00
// select slave to write
if ( ! i2c_master_select_slave ( slave , true ) ) {
2017-04-03 13:07:03 +02:00
goto error ;
}
2018-02-06 11:53:50 +01:00
// read data
if ( NULL ! = data & & data_size > 0 ) {
// read data
if ( ! i2c_master_read ( data , data_size ) ) {
goto error ;
}
}
2017-04-03 13:07:03 +02:00
2018-02-06 11:53:50 +01:00
success = true ;
error :
i2c_master_stop ( ) ; // sent stop condition
return success ;
}
bool i2c_master_slave_write ( uint8_t slave , const uint8_t * data , size_t data_size )
{
// sanity check
if ( I2C_SR2 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR2_BUSY ) { // I2C device is busy
return false ;
}
if ( I2C_SR2 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR2_MSL ) { // I2C device is already in master mode
return false ;
}
bool success = false ; // return if read succeeded
// send start condition
if ( ! i2c_master_start ( ) ) {
2017-04-03 13:07:03 +02:00
goto error ;
}
2018-02-06 11:53:50 +01:00
// select slave to write
if ( ! i2c_master_select_slave ( slave , true ) ) {
2017-04-03 13:07:03 +02:00
goto error ;
}
2018-02-06 11:53:50 +01:00
// write data
if ( NULL ! = data & & data_size > 0 ) {
if ( ! i2c_master_write ( data , data_size ) ) {
2017-04-03 13:07:03 +02:00
goto error ;
}
}
2018-02-06 11:53:50 +01:00
success = true ;
error :
i2c_master_stop ( ) ; // sent stop condition
return success ;
}
bool i2c_master_address_read ( uint8_t slave , const uint8_t * address , size_t address_size , uint8_t * data , size_t data_size )
{
// sanity check
if ( I2C_SR2 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR2_BUSY ) { // I2C device is busy
return false ;
}
if ( I2C_SR2 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR2_MSL ) { // I2C device is already in master mode
return false ;
}
bool success = false ; // return if read succeeded
// write address
if ( NULL ! = address & & address_size > 0 ) {
// send start condition
if ( ! i2c_master_start ( ) ) {
goto error ;
}
// select slave to write
if ( ! i2c_master_select_slave ( slave , true ) ) {
goto error ;
}
// send address
if ( ! i2c_master_write ( address , address_size ) ) {
goto error ;
}
}
// read data
if ( NULL ! = data & & data_size > 0 ) {
// send re-start condition
if ( ! i2c_master_start ( ) ) {
goto error ;
}
// select slave to read
if ( ! i2c_master_select_slave ( slave , false ) ) {
goto error ;
}
// read data
if ( ! i2c_master_read ( data , data_size ) ) {
2017-04-03 13:07:03 +02:00
goto error ;
}
}
2018-02-06 11:53:50 +01:00
success = true ;
2017-04-03 13:07:03 +02:00
error :
2018-02-06 11:53:50 +01:00
i2c_master_stop ( ) ; // sent stop condition
return success ;
}
bool i2c_master_address_write ( uint8_t slave , const uint8_t * address , size_t address_size , const uint8_t * data , size_t data_size )
{
// sanity check
if ( I2C_SR2 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR2_BUSY ) { // I2C device is busy
return false ;
}
if ( I2C_SR2 ( I2C ( I2C_MASTER_I2C ) ) & I2C_SR2_MSL ) { // I2C device is already in master mode
return false ;
2017-04-03 13:07:03 +02:00
}
2018-02-06 11:53:50 +01:00
bool success = false ; // return if read succeeded
// send start condition
if ( ! i2c_master_start ( ) ) {
goto error ;
}
// select slave to write
if ( ! i2c_master_select_slave ( slave , true ) ) {
goto error ;
}
// write address
if ( NULL ! = address & & address_size > 0 ) {
// send address
if ( ! i2c_master_write ( address , address_size ) ) {
goto error ;
}
2017-04-03 13:07:03 +02:00
}
2018-02-06 11:53:50 +01:00
// write data
if ( NULL ! = data & & data_size > 0 ) {
if ( ! i2c_master_write ( data , data_size ) ) {
goto error ;
}
}
success = true ;
error :
i2c_master_stop ( ) ; // sent stop condition
return success ;
2017-04-03 13:07:03 +02:00
}