diff --git a/cpu/sam0_common/include/periph_cpu_common.h b/cpu/sam0_common/include/periph_cpu_common.h index 1b01f68a86..a79010a369 100644 --- a/cpu/sam0_common/include/periph_cpu_common.h +++ b/cpu/sam0_common/include/periph_cpu_common.h @@ -402,6 +402,15 @@ typedef enum { /** * @brief I2C device configuration + * The frequency f() of the clock `gclk_src` must fulfill the condition + * + * 4 * speed ≤ f(gclk_src) ≤ 512 * speed + * + * if speed ≤ 1 MHz and + * + * 12 * speed ≤ f(gclk_src) ≤ 520 * speed + * + * if speed > 1 MHz */ typedef struct { SercomI2cm *dev; /**< pointer to the used I2C device */ diff --git a/cpu/sam0_common/periph/i2c.c b/cpu/sam0_common/periph/i2c.c index c9cc275469..32f27207a5 100644 --- a/cpu/sam0_common/periph/i2c.c +++ b/cpu/sam0_common/periph/i2c.c @@ -80,6 +80,9 @@ void i2c_init(i2c_t dev) assert(dev < I2C_NUMOF); + const uint32_t fSCL = i2c_config[dev].speed; + const uint32_t fGCLK = sam0_gclk_freq(i2c_config[dev].gclk_src); + /* Initialize mutex */ mutex_init(&locks[dev]); /* DISABLE I2C MASTER */ @@ -122,30 +125,31 @@ void i2c_init(i2c_t dev) /* Enable Smart Mode (ACK is sent when DATA.DATA is read) */ bus(dev)->CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN; - /* Find and set baudrate. Read speed configuration. Set transfer - * speed: SERCOM_I2CM_CTRLA_SPEED(0): Standard-mode (Sm) up to 100 - * kHz and Fast-mode (Fm) up to 400 kHz */ - switch (i2c_config[dev].speed) { - case I2C_SPEED_NORMAL: - case I2C_SPEED_FAST: - bus(dev)->CTRLA.reg |= SERCOM_I2CM_CTRLA_SPEED(0); - break; - case I2C_SPEED_HIGH: - bus(dev)->CTRLA.reg |= SERCOM_I2CM_CTRLA_SPEED(2); - break; - default: - DEBUG("BAD BAUDRATE\n"); - return; + /* Set SPEED */ + if (fSCL > I2C_SPEED_FAST_PLUS) { + bus(dev)->CTRLA.reg |= SERCOM_I2CM_CTRLA_SPEED(2); + } else if (fSCL > I2C_SPEED_FAST) { + bus(dev)->CTRLA.reg |= SERCOM_I2CM_CTRLA_SPEED(1); + } else { + bus(dev)->CTRLA.reg |= SERCOM_I2CM_CTRLA_SPEED(0); } + /* Get the baudrate */ - tmp_baud = (int32_t)(((sam0_gclk_freq(i2c_config[dev].gclk_src) + - (2 * (i2c_config[dev].speed)) - 1) / - (2 * (i2c_config[dev].speed))) - - (i2c_config[dev].speed == I2C_SPEED_HIGH ? 1 : 5)); + /* fSCL = fGCLK / (10 + 2 * BAUD) -> BAUD = fGCLK / (2 * fSCL) - 5 */ + /* fSCL = fGCLK / (2 + 2 * HSBAUD) -> HSBAUD = fGCLK / (2 * fSCL) - 1 */ + tmp_baud = (fGCLK + (2 * fSCL) - 1) /* round up */ + / (2 * fSCL) + - (fSCL > I2C_SPEED_FAST_PLUS ? 1 : 5); + /* Ensure baudrate is within limits */ - if (tmp_baud < 255 && tmp_baud > 0) { + assert(tmp_baud < 255 && tmp_baud > 0); + + if (fSCL > I2C_SPEED_FAST_PLUS) { + bus(dev)->BAUD.reg = SERCOM_I2CM_BAUD_HSBAUD(tmp_baud); + } else { bus(dev)->BAUD.reg = SERCOM_I2CM_BAUD_BAUD(tmp_baud); } + /* ENABLE I2C MASTER */ _i2c_poweron(dev);