/** * \internal Sets configuration 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 Module was configured correctly * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than * previously set */ static enum status_code _i2c_slave_set_config( struct i2c_slave_module *const module, const struct i2c_slave_config *const config) { uint32_t tmp_ctrla; /* Sanity check arguments. */ Assert(module); Assert(module->hw); Assert(config); SercomI2cs *const i2c_hw = &(module->hw->I2CS); Sercom *const sercom_hw = module->hw; module->buffer_timeout = config->buffer_timeout; 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); /* Prepare config to write to register CTRLA */ if (config->run_in_standby || system_is_debugger_present()) { tmp_ctrla = SERCOM_I2CS_CTRLA_RUNSTDBY; } else { tmp_ctrla = 0; } tmp_ctrla |= config->sda_hold_time | (config->scl_low_timeout << SERCOM_I2CS_CTRLA_LOWTOUT_Pos); i2c_hw->CTRLA.reg |= tmp_ctrla; /* Set CTRLB configuration */ i2c_hw->CTRLB.reg = SERCOM_I2CS_CTRLB_SMEN | config->address_mode; i2c_hw->ADDR.reg = config->address << SERCOM_I2CS_ADDR_ADDR_Pos | config->address_mask << SERCOM_I2CS_ADDR_ADDRMASK_Pos | config->enable_general_call_address << SERCOM_I2CS_ADDR_GENCEN_Pos; return STATUS_OK; }
/** * \internal Sets configuration 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 Module was configured correctly * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than * previously set */ static enum status_code _i2c_slave_set_config( struct i2c_slave_module *const module, const struct i2c_slave_config *const config) { /* Sanity check arguments. */ Assert(module); Assert(module->hw); Assert(config); SercomI2cs *const i2c_hw = &(module->hw->I2CS); Sercom *const sercom_hw = module->hw; module->buffer_timeout = config->buffer_timeout; 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); /* Write config to register CTRLA */ i2c_hw->CTRLA.reg |= (uint32_t)(config->sda_hold_time | config->transfer_speed | (config->scl_low_timeout << SERCOM_I2CS_CTRLA_LOWTOUTEN_Pos) | (config->scl_stretch_only_after_ack_bit << SERCOM_I2CS_CTRLA_SCLSM_Pos) | (config->slave_scl_low_extend_timeout << SERCOM_I2CS_CTRLA_SEXTTOEN_Pos) | (config->run_in_standby << SERCOM_I2CS_CTRLA_RUNSTDBY_Pos)); /* Set CTRLB configuration */ i2c_hw->CTRLB.reg = SERCOM_I2CS_CTRLB_SMEN | config->address_mode; i2c_hw->ADDR.reg = config->address << SERCOM_I2CS_ADDR_ADDR_Pos | config->address_mask << SERCOM_I2CS_ADDR_ADDRMASK_Pos | config->enable_general_call_address << SERCOM_I2CS_ADDR_GENCEN_Pos; return STATUS_OK; }
/** * \internal Writes an SPI SERCOM configuration to the hardware module. * * This function will write out a given configuration to the hardware module. * Can only be done when the module is disabled. * * \param[in] module Pointer to the software instance struct * \param[in] config Pointer to the configuration struct * * \return The status of the configuration * \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided. * \retval STATUS_OK If the configuration was written */ static enum status_code _spi_set_config( struct spi_module *const module, const struct spi_config *const config) { /* Sanity check arguments */ Assert(module); Assert(config); Assert(module->hw); SercomSpi *const spi_module = &(module->hw->SPI); Sercom *const hw = module->hw; struct system_pinmux_config pin_conf; system_pinmux_get_config_defaults(&pin_conf); pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_INPUT; uint32_t pad_pinmuxes[] = { config->pinmux_pad0, config->pinmux_pad1, config->pinmux_pad2, config->pinmux_pad3 }; /* Configure the SERCOM pins according to the user configuration */ for (uint8_t pad = 0; pad < 4; pad++) { uint32_t current_pinmux = pad_pinmuxes[pad]; if (current_pinmux == PINMUX_DEFAULT) { current_pinmux = _sercom_get_default_pad(hw, pad); } if (current_pinmux != PINMUX_UNUSED) { pin_conf.mux_position = current_pinmux & 0xFFFF; system_pinmux_pin_set_config(current_pinmux >> 16, &pin_conf); } }
/** * \brief Pin MUX configuration helper * * \param[in] pinmux Pin MUX setting to apply. Special values: * \arg \c PINMUX_UNUSED to do nothing. * \arg \c PINMUX_DEFAULT to use default pin MUX for the SERCOM pad. * \param[in] sercom,padnum SERCOM pad specification, for \ref PINMUX_DEFAULT. */ static inline void _spi_master_vec_pinmux_helper(uint32_t pinmux, Sercom *const sercom, uint8_t padnum) { struct system_pinmux_config pin_conf; if (pinmux == PINMUX_DEFAULT) { pinmux = _sercom_get_default_pad(sercom, padnum); } if (pinmux == PINMUX_UNUSED) { return; } system_pinmux_get_config_defaults(&pin_conf); pin_conf.mux_position = pinmux & 0xFFFF; system_pinmux_pin_set_config(pinmux >> 16, &pin_conf); };
/** * \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; 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 */ 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); } system_pinmux_pin_set_config(pad0 >> 16, pad0 & 0xFFFF, SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK, SYSTEM_PINMUX_PIN_PULL_UP, false); /* SERCOM PAD1 - SCL */ if (pad1 == PINMUX_DEFAULT) { pad1 = _sercom_get_default_pad(sercom_hw, 1); } system_pinmux_pin_set_config(pad1 >> 16, pad1 & 0xFFFF, SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK, SYSTEM_PINMUX_PIN_PULL_UP, false); /* 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 SCL low timeout. */ if (config->scl_low_timeout) { tmp_ctrla |= SERCOM_I2CM_CTRLA_LOWTOUT; } /* Check and set inactive bus timeout. */ if (config->inactive_timeout != I2C_MASTER_INACTIVE_TIMEOUT_DISABLED) { tmp_ctrla |= config->inactive_timeout; } /* 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)(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 = (uint8_t)tmp_baud; } return tmp_status_code; }
/** * \brief Initializes the device * * Initializes the USART device based on the setting specified in the * configuration struct. * * \param[out] module Pointer to USART device * \param[in] hw Pointer to USART hardware instance * \param[in] config Pointer to configuration struct * * \return Status of the initialization. * * \retval STATUS_OK The initialization was successful * \retval STATUS_BUSY The USART module is busy * resetting * \retval STATUS_ERR_DENIED The USART have not been disabled in * advance of initialization * \retval STATUS_ERR_INVALID_ARG The configuration struct contains * invalid configuration * \retval STATUS_ERR_ALREADY_INITIALIZED The SERCOM instance has already been * initialized with different clock * configuration * \retval STATUS_ERR_BAUD_UNAVAILABLE The BAUD rate given by the * configuration * struct cannot be reached with * the current clock configuration */ enum status_code usart_init( struct usart_module *const module, Sercom *const hw, const struct usart_config *const config) { /* Sanity check arguments */ Assert(module); Assert(hw); Assert(config); enum status_code status_code = STATUS_OK; /* Assign module pointer to software instance struct */ module->hw = hw; /* Get a pointer to the hardware module instance */ SercomUsart *const usart_hw = &(module->hw->USART); uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); #if (SAML21) 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; if (usart_hw->CTRLA.reg & SERCOM_USART_CTRLA_SWRST) { /* The module is busy resetting itself */ return STATUS_BUSY; } if (usart_hw->CTRLA.reg & SERCOM_USART_CTRLA_ENABLE) { /* Check the module is enabled */ return STATUS_ERR_DENIED; } /* Turn on module in PM */ system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); /* 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); /* Set character size */ module->character_size = config->character_size; /* Set transmitter and receiver status */ module->receiver_enabled = config->receiver_enable; module->transmitter_enabled = config->transmitter_enable; #ifdef FEATURE_USART_LIN_SLAVE module->lin_slave_enabled = config->lin_slave_enable; #endif #ifdef FEATURE_USART_START_FRAME_DECTION module->start_frame_detection_enabled = config->start_frame_detection_enable; #endif /* Set configuration according to the config struct */ status_code = _usart_set_config(module, config); if(status_code != STATUS_OK) { return status_code; } struct system_pinmux_config pin_conf; system_pinmux_get_config_defaults(&pin_conf); pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_INPUT; pin_conf.input_pull = SYSTEM_PINMUX_PIN_PULL_NONE; uint32_t pad_pinmuxes[] = { config->pinmux_pad0, config->pinmux_pad1, config->pinmux_pad2, config->pinmux_pad3 }; /* Configure the SERCOM pins according to the user configuration */ for (uint8_t pad = 0; pad < 4; pad++) { uint32_t current_pinmux = pad_pinmuxes[pad]; if (current_pinmux == PINMUX_DEFAULT) { current_pinmux = _sercom_get_default_pad(hw, pad); } if (current_pinmux != PINMUX_UNUSED) { pin_conf.mux_position = current_pinmux & 0xFFFF; system_pinmux_pin_set_config(current_pinmux >> 16, &pin_conf); } }
/** * \brief Initialize and enable debug UART * * This function sets up the configured SERCOM with the following static * features: * - asynchronous, internally clocked (UART) * - TX only * - 1 stop bit * - 8-bit data * - LSB first * - no parity bit * * The baud rate, SERCOM signal MUX and pin MUX are set up as configured in * \ref conf_debug_print.h, which also contains the configuration of which * SERCOM to use. * * The SERCOM UART is left enabled after this function call. * * \return Indication whether or not initialization succeeded. * \retval STATUS_OK if initialization was successful. * \retval STATUS_ERR_BAUDRATE_UNAVAILABLE if configured baud rate is too high. * * \note This function is based on \ref usart_init() from ASF, but is modified * to use a single instance in order to reduce code size. */ enum status_code dbg_init(void) { enum status_code status = STATUS_OK; Sercom *const sercom = CONF_DBG_PRINT_SERCOM; struct system_gclk_chan_config gclk_chan_conf; struct system_pinmux_config pin_conf; uint16_t baud; uint32_t sercom_index, pm_index, gclk_index; #if defined(__FREERTOS__) || defined(__DOXYGEN__) dbg_is_free = xSemaphoreCreateMutex(); vSemaphoreCreateBinary(requested_space_is_free); xSemaphoreTake(requested_space_is_free, 0); #else requested_space_is_free = false; #endif // Get required indexes sercom_index = _sercom_get_sercom_inst_index(sercom); pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos; 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_get_config_defaults(&gclk_chan_conf); gclk_chan_conf.source_generator = CONF_DBG_PRINT_GCLK_SOURCE; system_gclk_chan_set_config(gclk_index, &gclk_chan_conf); system_gclk_chan_enable(gclk_index); sercom_set_gclk_generator(CONF_DBG_PRINT_GCLK_SOURCE, false); #if defined(CONF_DBG_PRINT_BAUD_VALUE) baud = CONF_DBG_PRINT_BAUD_VALUE; #else // Compute baud rate, if it is achievable status = _sercom_get_async_baud_val(CONF_DBG_PRINT_BAUD_RATE, system_gclk_chan_get_hz(gclk_index), &baud); if (status != STATUS_OK) { return status; } #endif sercom_uart->BAUD.reg = baud; sercom_uart->CTRLB.reg = SERCOM_USART_CTRLB_TXEN; sercom_uart->CTRLA.reg = SERCOM_USART_CTRLA_MODE_USART_INT_CLK | SERCOM_USART_CTRLA_DORD | SERCOM_USART_CTRLA_ENABLE | CONF_DBG_PRINT_SERCOM_MUX | (system_is_debugger_present() ? SERCOM_USART_CTRLA_RUNSTDBY : 0); // Set up the pin MUXes system_pinmux_get_config_defaults(&pin_conf); pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_INPUT; uint32_t pad_pinmuxes[] = { CONF_DBG_PRINT_PINMUX_PAD0, CONF_DBG_PRINT_PINMUX_PAD1, CONF_DBG_PRINT_PINMUX_PAD2, CONF_DBG_PRINT_PINMUX_PAD3, }; for (uint8_t pad = 0; pad < 4; pad++) { uint32_t current_pinmux = pad_pinmuxes[pad]; if (current_pinmux == PINMUX_DEFAULT) { current_pinmux = _sercom_get_default_pad(sercom, pad); } if (current_pinmux != PINMUX_UNUSED) { pin_conf.mux_position = current_pinmux & 0xFFFF; system_pinmux_pin_set_config(current_pinmux >> 16, &pin_conf); } }
/** * \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; }