I2C: add frequency support instead of just fast mode

This commit is contained in:
King Kévin 2018-03-20 14:25:51 +01:00
parent 944379fda9
commit 6e160c31af
2 changed files with 31 additions and 16 deletions

View File

@ -21,7 +21,6 @@
/* standard libraries */
#include <stdint.h> // standard integer types
//#include <stdio.h> // standard I/O facilities
#include <stdlib.h> // general utilities
/* STM32 (including CM3) libraries */
@ -61,6 +60,7 @@ static uint32_t RCC_I2C(uint32_t i2c)
while (true);
}
}
/** get RCC for GPIO port for SCL pin based on I2C identifier
* @param[in] i2c I2C base address
* @return RCC GPIO address
@ -76,6 +76,7 @@ static uint32_t RCC_GPIO_PORT_SCL(uint32_t i2c)
while (true);
}
}
/** get RCC for GPIO port for SDA pin based on I2C identifier
* @param[in] i2c I2C base address
* @return RCC GPIO address
@ -91,6 +92,7 @@ static uint32_t RCC_GPIO_PORT_SDA(uint32_t i2c)
while (true);
}
}
/** get GPIO port for SCL pin based on I2C identifier
* @param[in] i2c I2C base address
* @return GPIO address
@ -112,6 +114,7 @@ static uint32_t GPIO_PORT_SCL(uint32_t i2c)
while (true);
}
}
/** get GPIO port for SDA pin based on I2C identifier
* @param[in] i2c I2C base address
* @return GPIO address
@ -133,6 +136,7 @@ static uint32_t GPIO_PORT_SDA(uint32_t i2c)
while (true);
}
}
/** get GPIO pin for SCL pin based on I2C identifier
* @param[in] i2c I2C base address
* @return GPIO address
@ -154,6 +158,7 @@ static uint32_t GPIO_PIN_SCL(uint32_t i2c)
while (true);
}
}
/** get GPIO pin for SDA pin based on I2C identifier
* @param[in] i2c I2C base address
* @return GPIO address
@ -176,7 +181,7 @@ static uint32_t GPIO_PIN_SDA(uint32_t i2c)
}
}
void i2c_master_setup(uint32_t i2c, bool fast)
void i2c_master_setup(uint32_t i2c, uint16_t frequency)
{
// check I2C peripheral
if (I2C1!=i2c && I2C2!=i2c) {
@ -194,15 +199,18 @@ void i2c_master_setup(uint32_t i2c, bool fast)
rcc_periph_clock_enable(RCC_I2C(i2c)); // enable clock for I2C peripheral
i2c_reset(i2c); // reset configuration
i2c_peripheral_disable(i2c); // I2C needs to be disable to be configured
if (frequency>400) { // limit frequency to 400 kHz
frequency = 400;
}
i2c_set_clock_frequency(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_set_ccr(i2c, rcc_apb1_frequency/(400000*2)); // set Thigh/Tlow to generate frequency of 400 kHz
i2c_set_trise(i2c, (300/(1000/(rcc_apb1_frequency/1000000)))+1); // max rise time for 300 kHz is 300 ns
if (frequency>100) { // use fast mode for frequencies > 100 kHz
i2c_set_fast_mode(i2c); // set fast mode
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
} else {
i2c_set_standard_mode(i2c); // the DS1307 has a maximum I2C SCL freq if 100 kHz (corresponding to the standard mode)
i2c_set_ccr(i2c, rcc_apb1_frequency/(100000*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 100 kHz is 1000 ns (~1 MHz)
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)
}
i2c_peripheral_enable(i2c); // enable I2C after configuration completed
@ -212,11 +220,7 @@ void i2c_master_setup(uint32_t i2c, bool fast)
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_prescaler(TIM(I2C_MASTER_TIMER), rcc_ahb_frequency/(frequency*1000)-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)
@ -239,6 +243,11 @@ void i2c_master_setup(uint32_t i2c, bool fast)
void i2c_master_release(uint32_t i2c)
{
// check I2C peripheral
if (I2C1!=i2c && I2C2!=i2c) {
while (true);
}
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
@ -259,6 +268,11 @@ void i2c_master_release(uint32_t i2c)
bool i2c_master_check_signals(uint32_t i2c)
{
// check I2C peripheral
if (I2C1!=i2c && I2C2!=i2c) {
while (true);
}
return (0!=gpio_get(GPIO_PORT_SCL(i2c), GPIO_PIN_SCL(i2c)) && 0!=gpio_get(GPIO_PORT_SDA(i2c), GPIO_PIN_SDA(i2c)));
}
@ -300,7 +314,7 @@ bool i2c_master_select_slave(uint32_t i2c, uint8_t slave, bool write)
return false; // could not send start condition
}
}
if (!(I2C_SR2(i2c) & I2C_SR2_MSL)) { // I2C device is already not master mode
if (!(I2C_SR2(i2c) & I2C_SR2_MSL)) { // I2C device is not in master mode
return false;
}

View File

@ -23,9 +23,10 @@
/** setup I2C peripheral
* @param[in] i2c I2C base address
* @param[in] fast use standard (100 kHz) or fast (400 kHz) mode
* @param[in] frequency frequency to use in kHz (1-400)
* @note Standard mode (Sm) is used for frequencies up to 100 kHz, and Fast mode (Fm) is used for frequencies up to 400 kHz
*/
void i2c_master_setup(uint32_t i2c, bool fast);
void i2c_master_setup(uint32_t i2c, uint16_t frequency);
/** release I2C peripheral
* @param[in] i2c I2C base address
*/