/** * \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); }
/** * \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); }
/** * \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; }