Skip to content
Snippets Groups Projects
Commit 43528031 authored by EmilioCorigliano's avatar EmilioCorigliano Committed by Alberto Nidasio
Browse files

[I2C] Fixed clock parameters

parent d7aee2b4
No related branches found
No related tags found
No related merge requests found
......@@ -40,9 +40,54 @@ static const int N_SCL_BITBANG =
16; ///< Number of clocks created for slave locked bus recovery
static const uint8_t I2C_PIN_ALTERNATE_FUNCTION =
4; ///< Alternate Function number of the I2C peripheral pins
static uint8_t f; ///< APB peripheral clock frequency
static uint8_t PRESC_STD; ///< PRESC for STANDARD speed
static uint8_t SCLH_STD; ///< SCLH for STANDARD speed
static uint8_t SCLL_STD; ///< SCLL for STANDARD speed
static uint8_t PRESC_F; ///< PRESC for FAST speed
static uint8_t SCLH_F; ///< SCLH for FAST speed
static uint8_t SCLL_F; ///< SCLL for FAST speed
static uint8_t PRESC_FP; ///< PRESC for FAST PLUS speed
static uint8_t SCLH_FP; ///< SCLH for FAST PLUS speed
static uint8_t SCLL_FP; ///< SCLL for FAST PLUS speed
} // namespace I2CConsts
/**
* @brief Helper function for calculating the timing parameters for the
* peripheral
*/
void calculateTimings(uint32_t f, uint32_t fi2c, uint8_t *presc, uint8_t *sclh,
uint8_t *scll)
{
// The formula for the clock is:
// t_SCL = [(SCLL + 1) + (SCLH + 1)] * (PRESC + 1) * t_I2CCLK + t_sync;
// calculating the "smallest" prescaler so that we can handle in a more
// refined way (with SCLL and SCLH) the length of high and low phases.
uint32_t temp_presc = f / (256 * fi2c);
// presc is 4 bit long, so avoiding overflow
if (temp_presc >= 16)
{
*presc = 15;
}
else if (temp_presc == 0)
{
*presc = 0;
}
else
{
*presc = temp_presc - 1;
}
// calculating SCLL and SCLH in order to have duty cycle of 50%. Also,
// correcting for the 250ns delay of the peripheral (250ns = 12 I2C clocks)
// distributing the correction on SCLL and SCLH
*sclh = *scll = (f / (fi2c * 2 * (*presc + 1)) - 1) - (7 / (*presc + 1));
}
#ifdef I2C1
/**
* I2C1 event interrupt
......@@ -328,11 +373,20 @@ void I2CDriver::init()
i2c->CR1 = 0;
// Retrieving the frequency of the APB relative to the I2C peripheral
// [MHz] (I2C peripherals are always connected to APB1, Low speed bus). In
// [kHz] (I2C peripherals are always connected to APB1, Low speed bus). In
// fact by default the I2C peripheral is clocked by the APB1 bus; anyway HSI
// and SYSCLK can be chosen.
I2CConsts::f =
ClockUtils::getAPBPeripheralsClock(ClockUtils::APB::APB1) / 1000000;
uint32_t f =
ClockUtils::getAPBPeripheralsClock(ClockUtils::APB::APB1) / 1000;
calculateTimings(f, 100, &I2CConsts::PRESC_STD, &I2CConsts::SCLH_STD,
&I2CConsts::SCLL_STD);
calculateTimings(f, 400, &I2CConsts::PRESC_F, &I2CConsts::SCLH_F,
&I2CConsts::SCLL_F);
calculateTimings(f, 1000, &I2CConsts::PRESC_FP, &I2CConsts::SCLH_FP,
&I2CConsts::SCLL_FP);
// I2CCLK < (t_low - t_filters) / 4
// I2CCLK < t_high
......@@ -458,44 +512,33 @@ void I2CDriver::setupPeripheral(const I2CSlaveConfig &slaveConfig)
i2c->CR1 &= ~I2C_CR1_PE;
// setting the SCLH and SCLL bits in I2C_TIMINGR register to generate
// correct timings for each speed modes
// correct timings for each speed modes.
// SDADEL and SCLDEL are set to 1 just to provide a bit of margin but not
// too much (in order not to slow down communication)
if (slaveConfig.speed == Speed::STANDARD)
{
// PRESC = 0xb
// SCLL = 0x13
// SCLH = 0xf
// SDADEL = 0x2
// SCLDEL = 0x4
i2c->TIMINGR = (0xb << I2C_TIMINGR_PRESC_Pos) | // PRESC
(0x13 << I2C_TIMINGR_SCLL_Pos) | // SCLL
(0xf << I2C_TIMINGR_SCLH_Pos) | // SCLH
(0x2 << I2C_TIMINGR_SDADEL_Pos) | // SDADEL
(0x4 << I2C_TIMINGR_SCLDEL_Pos); // SCLDEL
i2c->TIMINGR =
(I2CConsts::PRESC_STD << I2C_TIMINGR_PRESC_Pos) | // PRESC
(I2CConsts::SCLL_STD << I2C_TIMINGR_SCLL_Pos) | // SCLL
(I2CConsts::SCLH_STD << I2C_TIMINGR_SCLH_Pos) | // SCLH
(0x1 << I2C_TIMINGR_SDADEL_Pos) | // SDADEL
(0x1 << I2C_TIMINGR_SCLDEL_Pos); // SCLDEL
}
else if (slaveConfig.speed == Speed::FAST)
{
// PRESC = 0x5
// SCLL = 0x9
// SCLH = 0x3
// SDADEL = 0x3
// SCLDEL = 0x3
i2c->TIMINGR = (0x5 << I2C_TIMINGR_PRESC_Pos) | // PRESC
(0x9 << I2C_TIMINGR_SCLL_Pos) | // SCLL
(0x3 << I2C_TIMINGR_SCLH_Pos) | // SCLH
(0x3 << I2C_TIMINGR_SDADEL_Pos) | // SDADEL
(0x3 << I2C_TIMINGR_SCLDEL_Pos); // SCLDEL
i2c->TIMINGR = (I2CConsts::PRESC_F << I2C_TIMINGR_PRESC_Pos) | // PRESC
(I2CConsts::SCLL_F << I2C_TIMINGR_SCLL_Pos) | // SCLL
(I2CConsts::SCLH_F << I2C_TIMINGR_SCLH_Pos) | // SCLH
(0x1 << I2C_TIMINGR_SDADEL_Pos) | // SDADEL
(0x1 << I2C_TIMINGR_SCLDEL_Pos); // SCLDEL
}
else if (slaveConfig.speed == Speed::FAST_PLUS)
{
// PRESC = 0x5
// SCLL = 0x3
// SCLH = 0x1
// SDADEL = 0x0
// SCLDEL = 0x1
i2c->TIMINGR = (0x5 << I2C_TIMINGR_PRESC_Pos) | // PRESC
(0x3 << I2C_TIMINGR_SCLL_Pos) | // SCLL
(0x1 << I2C_TIMINGR_SCLH_Pos) | // SCLH
(0x0 << I2C_TIMINGR_SDADEL_Pos) | // SDADEL
i2c->TIMINGR =
(I2CConsts::PRESC_FP << I2C_TIMINGR_PRESC_Pos) | // PRESC
(I2CConsts::SCLL_FP << I2C_TIMINGR_SCLL_Pos) | // SCLL
(I2CConsts::SCLH_FP << I2C_TIMINGR_SCLH_Pos) | // SCLH
(0x1 << I2C_TIMINGR_SDADEL_Pos) | // SDADEL
(0x1 << I2C_TIMINGR_SCLDEL_Pos); // SCLDEL
}
else
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment