Ejemplo n.º 1
0
/**
 * \brief Initializes the requested I<SUP>2</SUP>C hardware module
 *
 * Initializes the 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]  config  Pointer to the configuration struct
 *
 * \return Status of initialization.
 * \retval STATUS_OK                        Module initiated correctly
 * \retval STATUS_ERR_INVALID_ARG           Invalid argument in module or config structure.
 * \retval STATUS_ERR_ALREADY_INITIALIZED   If the Pinmux is not a valid one for I2C signals.
 *
 */
enum status_code i2c_slave_init(
		struct i2c_slave_module *const module,
		I2c *const hw,
		const struct i2c_slave_config *const config)
{
	/* Sanity check */
	Assert(module);
	Assert(module->hw);
	Assert(config);

	module->hw = hw;
	
	/* Sanity check arguments. */
	if ((module == NULL) || (config == NULL))
		return STATUS_ERR_INVALID_ARG;

	i2c_disable(module->hw);
	
	if (module->hw == I2C0)
		system_peripheral_reset(PERIPHERAL_I2C0_CORE);
	else if (module->hw == I2C1) {
		system_peripheral_reset(PERIPHERAL_I2C1_CORE);
	} else {
		return STATUS_ERR_INVALID_ARG;
	}

#if I2C_SLAVE_CALLBACK_MODE == true
	/* Initialize values in module. */
	module->registered_callback = 0;
	module->enabled_callback    = 0;
	module->buffer_length       = 0;
	module->buffer_remaining    = 0;
	module->buffer              = NULL;
	module->status              = STATUS_OK;

	_i2c_instances = (void*)module;
	if (module->hw == I2C0) {
		system_register_isr(RAM_ISR_TABLE_I2CRX0_INDEX, (uint32_t)_i2c_slave_rx_isr_handler);
		system_register_isr(RAM_ISR_TABLE_I2CTX0_INDEX, (uint32_t)_i2c_slave_tx_isr_handler);
		NVIC_EnableIRQ(I2C0_RX_IRQn);
		NVIC_EnableIRQ(I2C0_TX_IRQn);
	} else if (module->hw == I2C1) {
		system_register_isr(RAM_ISR_TABLE_I2CRX1_INDEX, (uint32_t)_i2c_slave_rx_isr_handler);
		system_register_isr(RAM_ISR_TABLE_I2CTX1_INDEX, (uint32_t)_i2c_slave_tx_isr_handler);
		NVIC_EnableIRQ(I2C1_RX_IRQn);
		NVIC_EnableIRQ(I2C1_TX_IRQn);
	}
#endif

	/* Set config and return status. */
	if(_i2c_slave_set_config(module, config) != STATUS_OK)
		return STATUS_ERR_NOT_INITIALIZED;

	return STATUS_OK;
}
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);
}