Ejemplo n.º 1
0
/**
 * \brief Initializes the requested I<SUP>2</SUP>C hardware module
 *
 * Initializes the SERCOM I<SUP>2</SUP>C master device requested and sets the provided
 * software module struct. Run this function before any further use of
 * the driver.
 *
 * \param[out] module  Pointer to software module struct
 * \param[in]  hw      Pointer to the hardware instance
 * \param[in]  config  Pointer to the configuration struct
 *
 * \return Status of initialization.
 * \retval STATUS_OK                        Module initiated correctly
 * \retval STATUS_ERR_DENIED                If module is enabled
 * \retval STATUS_BUSY                      If module is busy resetting
 * \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
 *
 */
enum status_code i2c_master_init(
		struct i2c_master_module *const module,
		Sercom *const hw,
		const struct i2c_master_config *const config)
{
	/* Sanity check arguments. */
	Assert(module);
	Assert(hw);
	Assert(config);

	/* Initialize software module */
	module->hw = hw;

	SercomI2cm *const i2c_module = &(module->hw->I2CM);

	uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw);
	uint32_t pm_index     = sercom_index + PM_APBCMASK_SERCOM0_Pos;
	uint32_t gclk_index   = sercom_index + SERCOM0_GCLK_ID_CORE;

	/* Turn on module in PM */
	system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index);

	/* Set up the GCLK for the module */



	system_gclk_chan_set_config(gclk_index, config->generator_source);
	system_gclk_chan_enable(gclk_index);
	_sercom_set_gclk_generator(config->generator_source);

	/* Check if module is enabled. */
	if (i2c_module->CTRLA.reg & SERCOM_I2CM_CTRLA_ENABLE) {
		return STATUS_ERR_DENIED;
	}

	/* Check if reset is in progress. */
	if (i2c_module->CTRLA.reg & SERCOM_I2CM_CTRLA_SWRST) {
		return STATUS_BUSY;
	}

#if I2C_MASTER_CALLBACK_MODE == true
	/* Get sercom instance index and register callback. */
	uint8_t instance_index = _sercom_get_sercom_inst_index(module->hw);
	_sercom_set_handler(instance_index, _i2c_master_interrupt_handler);
	_sercom_instances[instance_index] = module;

	/* Initialize values in module. */
	module->registered_callback = 0;
	module->enabled_callback = 0;
	module->buffer_length = 0;
	module->buffer_remaining = 0;

	module->status = STATUS_OK;
	module->buffer = NULL;
#endif

	/* Set sercom module to operate in I2C master mode. */
	i2c_module->CTRLA.reg = SERCOM_I2CM_CTRLA_MODE_I2C_MASTER;

	/* Set config and return status. */
	return _i2c_master_set_config(module, config);
}
Ejemplo n.º 2
0
/**
 * \brief Initializes the requested I<SUP>2</SUP>C hardware module
 *
 * Initializes the SERCOM I<SUP>2</SUP>C slave device requested and sets the provided
 * software module struct.  Run this function before any further use of
 * the driver.
 *
 * \param[out] module  Pointer to software module struct
 * \param[in]  hw      Pointer to the hardware instance
 * \param[in]  config  Pointer to the configuration struct
 *
 * \return Status of initialization.
 * \retval STATUS_OK                       Module initiated correctly
 * \retval STATUS_ERR_DENIED               If module is enabled
 * \retval STATUS_BUSY                     If module is busy resetting
 * \retval STATUS_ERR_ALREADY_INITIALIZED  If setting other GCLK generator than
 *                                         previously set
 */
enum status_code i2c_slave_init(
		struct i2c_slave_module *const module,
		Sercom *const hw,
		const struct i2c_slave_config *const config)
{
	/* Sanity check arguments */
	Assert(module);
	Assert(hw);
	Assert(config);

	/* Initialize software module */
	module->hw = hw;

	SercomI2cs *const i2c_hw = &(module->hw->I2CS);

	/* Check if module is enabled */
	if (i2c_hw->CTRLA.reg & SERCOM_I2CS_CTRLA_ENABLE) {
		return STATUS_ERR_DENIED;
	}

	/* Check if reset is in progress */
	if (i2c_hw->CTRLA.reg & SERCOM_I2CS_CTRLA_SWRST) {
		return STATUS_BUSY;
	}

	uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw);
	uint32_t pm_index, gclk_index; 
#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21)
#if (SAML21)
	if (sercom_index == 5) {
		pm_index     = MCLK_APBDMASK_SERCOM5_Pos;
		gclk_index   = SERCOM5_GCLK_ID_CORE;
	} else {
		pm_index     = sercom_index + MCLK_APBCMASK_SERCOM0_Pos;
		gclk_index   = sercom_index + SERCOM0_GCLK_ID_CORE;
	}
#else
	pm_index     = sercom_index + MCLK_APBCMASK_SERCOM0_Pos;
	gclk_index   = sercom_index + SERCOM0_GCLK_ID_CORE;
#endif
#else
	pm_index     = sercom_index + PM_APBCMASK_SERCOM0_Pos;
	gclk_index   = sercom_index + SERCOM0_GCLK_ID_CORE;
#endif
	
	/* Turn on module in PM */
#if (SAML21)
	if (sercom_index == 5) {
		system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBD, 1 << pm_index);
	} else {
		system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index);	
	}
#else
	system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index);
#endif

	/* Set up the GCLK for the module */
	struct system_gclk_chan_config gclk_chan_conf;
	system_gclk_chan_get_config_defaults(&gclk_chan_conf);
	gclk_chan_conf.source_generator = config->generator_source;
	system_gclk_chan_set_config(gclk_index, &gclk_chan_conf);
	system_gclk_chan_enable(gclk_index);
	sercom_set_gclk_generator(config->generator_source, false);

#if I2C_SLAVE_CALLBACK_MODE == true
	/* Get sercom instance index */
	uint8_t instance_index = _sercom_get_sercom_inst_index(module->hw);

	/* Save software module in interrupt handler */
	_sercom_set_handler(instance_index, _i2c_slave_interrupt_handler);

	/* Save software module */
	_sercom_instances[instance_index] = module;

	/* Initialize values in module */
	module->registered_callback = 0;
	module->enabled_callback = 0;
	module->buffer_length = 0;
	module->nack_on_address = config->enable_nack_on_address;
#endif

	/* Set SERCOM module to operate in I2C slave mode */
	i2c_hw->CTRLA.reg = SERCOM_I2CS_CTRLA_MODE(0x4);

	/* Set config and return status */
	return _i2c_slave_set_config(module, config);
}
Ejemplo n.º 3
0
/**
 * \brief Initialize hardware and driver instance
 *
 * This function configures the clock system for the specified SERCOM module,
 * sets up the related pins and their MUX, initializes the SERCOM in SPI master
 * mode, and prepares the driver instance for operation.
 *
 * \pre \ref system_init() must have been called prior to this function.
 *
 * The SERCOM SPI module is left disabled after initialization, and must be
 * enabled with \ref spi_master_vec_enable() before a transfer can be done.
 *
 * \param[out] module Driver instance to initialize.
 * \param[in,out] sercom SERCOM module to initialize and associate driver
 * instance with.
 * \param[in] config Driver configuration to use.
 *
 * \return Status of initialization.
 * \retval STATUS_OK if initialization succeeded.
 * \retval STATUS_ERR_INVALID_ARG if driver has been misconfigured.
 */
enum status_code spi_master_vec_init(struct spi_master_vec_module *const module,
		Sercom *const sercom, const struct spi_master_vec_config *const config)
{
	Assert(module);
	Assert(sercom);
	Assert(config);

	enum status_code status;
	SercomSpi *const spi_hw = &(sercom->SPI);
	struct system_gclk_chan_config gclk_chan_conf;
	uint16_t tmp_baud;
	uint32_t sercom_index = _sercom_get_sercom_inst_index((Sercom *)spi_hw);
#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30)
	uint32_t pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos;
#else
	uint32_t pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos;
#endif
	uint32_t gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE;
	uint32_t gclk_hz;

	module->sercom = sercom;

	/* Enable clock for the module interface */
	system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index);

	/* Set up the GCLK for the module */
	system_gclk_chan_get_config_defaults(&gclk_chan_conf);
	gclk_chan_conf.source_generator = config->gclk_generator;
	system_gclk_chan_set_config(gclk_index, &gclk_chan_conf);
	system_gclk_chan_enable(gclk_index);
	sercom_set_gclk_generator(config->gclk_generator, false);

	_spi_master_vec_wait_for_sync(spi_hw);

	/* Set up the SERCOM SPI module as master */
	spi_hw->CTRLA.reg = SERCOM_SPI_CTRLA_MODE(0x3);
	spi_hw->CTRLA.reg |= (uint32_t)config->mux_setting
			| config->transfer_mode
			| config->data_order
			| ((config->run_in_standby || system_is_debugger_present()) ?
					SERCOM_SPI_CTRLA_RUNSTDBY : 0);

	/* Get baud value from configured baudrate and internal clock rate */
	gclk_hz = system_gclk_chan_get_hz(gclk_index);
	status = _sercom_get_sync_baud_val(config->baudrate, gclk_hz, &tmp_baud);

	if (status != STATUS_OK) {
		/* Baud rate calculation error! */
		return STATUS_ERR_INVALID_ARG;
	}

	spi_hw->BAUD.reg = (uint8_t)tmp_baud;

	/* Configure the pin multiplexers */
	_spi_master_vec_pinmux_helper(config->pinmux_pad0, sercom, 0);
	_spi_master_vec_pinmux_helper(config->pinmux_pad3, sercom, 3);

	/* SERCOM PAD1 and PAD2 are used for slave SS.
	 * This is a SPI master driver, so control of slave SS must be left to
	 * the PORT module, i.e., peripheral MUX should not be set for that pin.
	 * DOPO controls which PAD is used for slave SS:
	 * If DOPO is odd, SERCOM_PAD1 is SS: SERCOM_PAD2 can be MUXed.
	 * If DOPO is even, SERCOM_PAD2 is SS: SERCOM_PAD1 can be MUXed.
	 */
	if (config->mux_setting & (1 << SERCOM_SPI_CTRLA_DOPO_Pos)) {
		_spi_master_vec_pinmux_helper(config->pinmux_pad2, sercom, 2);
	} else {
		_spi_master_vec_pinmux_helper(config->pinmux_pad1, sercom, 1);
	}

	/* Initialize our instance and register interrupt handler + data */
	module->rx_bufdesc_ptr = NULL;
	module->tx_bufdesc_ptr = NULL;
	module->direction = SPI_MASTER_VEC_DIRECTION_IDLE;
	module->status = STATUS_OK;
#ifdef CONF_SPI_MASTER_VEC_OS_SUPPORT
	CONF_SPI_MASTER_VEC_CREATE_SEMAPHORE(module->busy_semaphore);
#endif

	_sercom_set_handler(sercom_index, _spi_master_vec_int_handler);
	_sercom_instances[sercom_index] = module;

	return STATUS_OK;
}