Exemplo n.º 1
0
/**
 * \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 baudrate 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;
	int32_t tmp_baud;
	int32_t tmp_baud_hs;
	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;

	/* Set whether module should run in standby. */
	if (config->run_in_standby || system_is_debugger_present()) {
		tmp_ctrla = SERCOM_I2CM_CTRLA_RUNSTDBY;
	} else {
		tmp_ctrla = 0;
	}

	/* 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 and set transfer speed */
	tmp_ctrla |= config->transfer_speed;

	/* 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. */
	tmp_baud = (int32_t)(div_ceil(
				system_gclk_chan_get_hz(SERCOM0_GCLK_ID_CORE + sercom_index),
				(2000*(config->baud_rate))) - 5);

	/* Check that baudrate is supported at current speed. */
	if (tmp_baud > 255 || tmp_baud < 0) {
		/* Baud rate not supported. */
		tmp_status_code = STATUS_ERR_BAUDRATE_UNAVAILABLE;
	} else {
		/* Find baudrate for high speed */
		tmp_baud_hs = (int32_t)(div_ceil(
				system_gclk_chan_get_hz(SERCOM0_GCLK_ID_CORE + sercom_index),
				(2000*(config->baud_rate_high_speed))) - 1);

		/* Check that baudrate is supported at current speed. */
		if (tmp_baud_hs > 255 || tmp_baud_hs < 0) {
			/* Baud rate not supported. */
			tmp_status_code = STATUS_ERR_BAUDRATE_UNAVAILABLE;
		}
	}
	if (tmp_status_code != STATUS_ERR_BAUDRATE_UNAVAILABLE) {
		/* Baud rate acceptable. */
		i2c_module->BAUD.reg = SERCOM_I2CM_BAUD_BAUD(tmp_baud) |
			SERCOM_I2CM_BAUD_HSBAUD(tmp_baud_hs);
	}

	return tmp_status_code;
}
Exemplo n.º 2
0
Arquivo: i2c.c Projeto: OTAkeys/RIOT
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;
        }
    }
}