/** * \internal Sets configurations to module * * \param[out] module Pointer to software module structure. * \param[in] config Configuration structure with configurations to set. * * \return Status of setting configuration. * \retval STATUS_OK If module was configured correctly * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than * previously set * \retval STATUS_ERR_BAUDRATE_UNAVAILABLE If given baud rate is not compatible * with set GCLK frequency */ static enum status_code _i2c_master_set_config( struct i2c_master_module *const module, const struct i2c_master_config *const config) { /* Sanity check arguments. */ Assert(module); Assert(module->hw); Assert(config); /* Temporary variables. */ uint32_t tmp_ctrla = 0; int32_t tmp_baud; enum status_code tmp_status_code = STATUS_OK; SercomI2cm *const i2c_module = &(module->hw->I2CM); Sercom *const sercom_hw = module->hw; uint8_t sercom_index = _sercom_get_sercom_inst_index(sercom_hw); /* Pin configuration */ struct system_pinmux_config pin_conf; system_pinmux_get_config_defaults(&pin_conf); uint32_t pad0 = config->pinmux_pad0; uint32_t pad1 = config->pinmux_pad1; /* SERCOM PAD0 - SDA */ if (pad0 == PINMUX_DEFAULT) { pad0 = _sercom_get_default_pad(sercom_hw, 0); } pin_conf.mux_position = pad0 & 0xFFFF; pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK; system_pinmux_pin_set_config(pad0 >> 16, &pin_conf); /* SERCOM PAD1 - SCL */ if (pad1 == PINMUX_DEFAULT) { pad1 = _sercom_get_default_pad(sercom_hw, 1); } pin_conf.mux_position = pad1 & 0xFFFF; pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK; system_pinmux_pin_set_config(pad1 >> 16, &pin_conf); /* Save timeout on unknown bus state in software module. */ module->unknown_bus_state_timeout = config->unknown_bus_state_timeout; /* Save timeout on buffer write. */ module->buffer_timeout = config->buffer_timeout; /* Check and set if module should run in standby. */ if (config->run_in_standby) { tmp_ctrla = SERCOM_I2CM_CTRLA_RUNSTDBY; } /* Check and set start data hold timeout. */ if (config->start_hold_time != I2C_MASTER_START_HOLD_TIME_DISABLED) { tmp_ctrla |= config->start_hold_time; } /* Check transfer speed mode */ if (config->baud_rate > I2C_MASTER_BAUD_RATE_400KHZ) { if (config->baud_rate < I2C_MASTER_BAUD_RATE_1000KHZ) { /* Fast-mode Plus up to 1MHz */ tmp_ctrla |= SERCOM_I2CM_CTRLA_SPEED(1); } else if (config->baud_rate < I2C_MASTER_BAUD_RATE_3400KHZ) { /* High-speed mode up to 3.4MHz */ tmp_ctrla |= SERCOM_I2CM_CTRLA_SPEED(2); } } /* Check and set SCL low timeout. */ if (config->scl_low_timeout) { tmp_ctrla |= SERCOM_I2CM_CTRLA_LOWTOUTEN; } /* Check and set inactive bus timeout. */ if (config->inactive_timeout != I2C_MASTER_INACTIVE_TIMEOUT_DISABLED) { tmp_ctrla |= config->inactive_timeout; } /* Check and set SCL clock stretch mode. */ if (config->scl_stretch_only_after_ack_bit) { tmp_ctrla |= SERCOM_I2CM_CTRLA_SCLSM; } /* Check and set slave SCL low extend timeout. */ if (config->slave_scl_low_extend_timeout) { tmp_ctrla |= SERCOM_I2CM_CTRLA_SEXTTOEN; } /* Check and set master SCL low extend timeout. */ if (config->master_scl_low_extend_timeout) { tmp_ctrla |= SERCOM_I2CM_CTRLA_MEXTTOEN; } /* Write config to register CTRLA. */ i2c_module->CTRLA.reg |= tmp_ctrla; /* Set configurations in CTRLB. */ i2c_module->CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN; /* Find and set baudrate. */ if (config->baud_rate <= I2C_MASTER_BAUD_RATE_1000KHZ) { tmp_baud = (int32_t)((system_gclk_chan_get_hz(SERCOM0_GCLK_ID_CORE + sercom_index) / (2000*(config->baud_rate))) - 5); /* Check that baud rate is supported at current speed. */ if (tmp_baud > 255 || tmp_baud < 0) { /* Baud rate not supported. */ tmp_status_code = STATUS_ERR_BAUDRATE_UNAVAILABLE; } else { /* Baud rate acceptable. */ i2c_module->BAUD.reg = SERCOM_I2CM_BAUD_BAUD(tmp_baud); } } else { /* The higher baudrate is not support by the software for now */ tmp_status_code = STATUS_ERR_BAUDRATE_UNAVAILABLE; Assert(false); } return tmp_status_code; }
void i2c_init(i2c_t dev) { uint32_t timeout_counter = 0; int32_t tmp_baud; assert(dev < I2C_NUMOF); /* Initialize mutex */ mutex_init(&locks[dev]); /* DISABLE I2C MASTER */ _i2c_poweroff(dev); /* Reset I2C */ bus(dev)->CTRLA.reg = SERCOM_I2CM_CTRLA_SWRST; while (bus(dev)->SYNCBUSY.reg & SERCOM_I2CM_SYNCBUSY_MASK) {} /* Turn on power manager for sercom */ sercom_clk_en(bus(dev)); /* I2C using CLK GEN 0 */ sercom_set_gen(bus(dev),i2c_config[dev].gclk_src); /* Check if module is enabled. */ if (bus(dev)->CTRLA.reg & SERCOM_I2CM_CTRLA_ENABLE) { DEBUG("STATUS_ERR_DENIED\n"); return; } /* Check if reset is in progress. */ if (bus(dev)->CTRLA.reg & SERCOM_I2CM_CTRLA_SWRST) { DEBUG("STATUS_BUSY\n"); return; } /************ SERCOM PAD0 - SDA and SERCOM PAD1 - SCL *************/ gpio_init_mux(i2c_config[dev].sda_pin, i2c_config[dev].mux); gpio_init_mux(i2c_config[dev].scl_pin, i2c_config[dev].mux); /* I2C CONFIGURATION */ while (bus(dev)->SYNCBUSY.reg & SERCOM_I2CM_SYNCBUSY_MASK) {} /* Set sercom module to operate in I2C master mode and run in Standby if user requests it */ bus(dev)->CTRLA.reg = SERCOM_I2CM_CTRLA_MODE_I2C_MASTER | ((i2c_config[dev].flags & I2C_FLAG_RUN_STANDBY) ? SERCOM_I2CM_CTRLA_RUNSTDBY : 0); /* 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; } /* Get the baudrate */ tmp_baud = (int32_t)(((CLOCK_CORECLOCK + (2 * (i2c_config[dev].speed)) - 1) / (2 * (i2c_config[dev].speed))) - (i2c_config[dev].speed == I2C_SPEED_HIGH ? 1 : 5)); /* Ensure baudrate is within limits */ if (tmp_baud < 255 && tmp_baud > 0) { bus(dev)->BAUD.reg = SERCOM_I2CM_BAUD_BAUD(tmp_baud); } /* ENABLE I2C MASTER */ _i2c_poweron(dev); /* Start timeout if bus state is unknown. */ while ((bus(dev)->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE_Msk) == BUSSTATE_UNKNOWN) { if (timeout_counter++ >= SAMD21_I2C_TIMEOUT) { /* Timeout, force bus state to idle. */ bus(dev)->STATUS.reg = BUSSTATE_IDLE; } } }