From 7269dc4e3accc3ed7927708017a3e3af5ec1b4af Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Mon, 8 Jun 2020 16:19:46 +0200 Subject: [PATCH 1/4] cpu/sam0_common: i2c: allow arbitrary I2C frequencies The Atmel I2C peripheral supports arbitrary I2C frequencies. Since the `i2c_speed_t` enum just encodes the raw frequency values, we can just use them in the peripheral definition. We just have to remove the switch-case block that will generate an error for values outside of `i2c_speed_t`. --- cpu/sam0_common/periph/i2c.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/cpu/sam0_common/periph/i2c.c b/cpu/sam0_common/periph/i2c.c index c9cc275469..02e2bee873 100644 --- a/cpu/sam0_common/periph/i2c.c +++ b/cpu/sam0_common/periph/i2c.c @@ -125,23 +125,16 @@ void i2c_init(i2c_t dev) /* 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; + if (i2c_config[dev].speed > I2C_SPEED_FAST) { + bus(dev)->CTRLA.reg |= SERCOM_I2CM_CTRLA_SPEED(2); + } 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)); + (i2c_config[dev].speed > I2C_SPEED_FAST ? 1 : 5)); /* Ensure baudrate is within limits */ if (tmp_baud < 255 && tmp_baud > 0) { bus(dev)->BAUD.reg = SERCOM_I2CM_BAUD_BAUD(tmp_baud); From 2fb0d9061f07b7d139e463e377b3f50c33ca1d7f Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Wed, 10 Jun 2020 14:52:31 +0200 Subject: [PATCH 2/4] cpu/sam0_common: i2c: fix High Speed --- cpu/sam0_common/periph/i2c.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/cpu/sam0_common/periph/i2c.c b/cpu/sam0_common/periph/i2c.c index 02e2bee873..383f1c55fe 100644 --- a/cpu/sam0_common/periph/i2c.c +++ b/cpu/sam0_common/periph/i2c.c @@ -122,23 +122,32 @@ 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 */ - if (i2c_config[dev].speed > I2C_SPEED_FAST) { + /* Set SPEED */ + if (i2c_config[dev].speed > I2C_SPEED_FAST_PLUS) { bus(dev)->CTRLA.reg |= SERCOM_I2CM_CTRLA_SPEED(2); + } else if (i2c_config[dev].speed > 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) / + /* fSCL = fGCLK / (10 + 2 * BAUD) -> BAUD = fGCLK / (2 * fSCL) - 5 */ + /* fSCL = fGCLK / (2 + 2 * HSBAUD) -> HSBAUD = fGCLK / (2 * fSCL) - 1 */ + tmp_baud = (((sam0_gclk_freq(i2c_config[dev].gclk_src) + + (2 * (i2c_config[dev].speed)) - 1) / /* round up */ (2 * (i2c_config[dev].speed))) - - (i2c_config[dev].speed > I2C_SPEED_FAST ? 1 : 5)); + (i2c_config[dev].speed > 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 (i2c_config[dev].speed > 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); From 4df36cbfda0f8b3afbb01bf1396241a759546792 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Wed, 10 Jun 2020 15:03:00 +0200 Subject: [PATCH 3/4] cpu/sam0_common: i2c: improve readability of baud rate calculation Use variables to represent fSCL an fGCLK to make the baud rate calculation more readable. --- cpu/sam0_common/periph/i2c.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/cpu/sam0_common/periph/i2c.c b/cpu/sam0_common/periph/i2c.c index 383f1c55fe..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 */ @@ -123,9 +126,9 @@ void i2c_init(i2c_t dev) bus(dev)->CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN; /* Set SPEED */ - if (i2c_config[dev].speed > I2C_SPEED_FAST_PLUS) { + if (fSCL > I2C_SPEED_FAST_PLUS) { bus(dev)->CTRLA.reg |= SERCOM_I2CM_CTRLA_SPEED(2); - } else if (i2c_config[dev].speed > I2C_SPEED_FAST) { + } 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); @@ -134,15 +137,14 @@ void i2c_init(i2c_t dev) /* Get the baudrate */ /* fSCL = fGCLK / (10 + 2 * BAUD) -> BAUD = fGCLK / (2 * fSCL) - 5 */ /* fSCL = fGCLK / (2 + 2 * HSBAUD) -> HSBAUD = fGCLK / (2 * fSCL) - 1 */ - tmp_baud = (((sam0_gclk_freq(i2c_config[dev].gclk_src) + - (2 * (i2c_config[dev].speed)) - 1) / /* round up */ - (2 * (i2c_config[dev].speed))) - - (i2c_config[dev].speed > I2C_SPEED_FAST_PLUS ? 1 : 5)); + tmp_baud = (fGCLK + (2 * fSCL) - 1) /* round up */ + / (2 * fSCL) + - (fSCL > I2C_SPEED_FAST_PLUS ? 1 : 5); /* Ensure baudrate is within limits */ assert(tmp_baud < 255 && tmp_baud > 0); - if (i2c_config[dev].speed > I2C_SPEED_FAST_PLUS) { + 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); From 1472d2095cc856696b1a18408a8265bdc27c026a Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Thu, 9 Jul 2020 14:49:53 +0200 Subject: [PATCH 4/4] cpu/sam0_common: i2c: document frequency constraints --- cpu/sam0_common/include/periph_cpu_common.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cpu/sam0_common/include/periph_cpu_common.h b/cpu/sam0_common/include/periph_cpu_common.h index d6247437cd..b0ff939df7 100644 --- a/cpu/sam0_common/include/periph_cpu_common.h +++ b/cpu/sam0_common/include/periph_cpu_common.h @@ -383,6 +383,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 */