/**
 * \internal
 * Asynchronous write of a buffer with a given length
 *
 * \param[in]  module   Pointer to USART software instance struct
 * \param[in]  tx_data  Pointer to data to be transmitted
 * \param[in]  length   Length of data buffer
 *
 */
enum status_code _usart_write_buffer(
		struct usart_module *const module,
		uint8_t *tx_data,
		uint16_t length)
{
	/* Sanity check arguments */
	Assert(module);
	Assert(module->hw);
	Assert(tx_data);

	/* Get a pointer to the hardware module instance */
	SercomUsart *const usart_hw = &(module->hw->USART);

	system_interrupt_enter_critical_section();

	/* Check if the USART transmitter is busy */
	if (module->remaining_tx_buffer_length > 0) {
		system_interrupt_leave_critical_section();
		return STATUS_BUSY;
	}

	/* Write parameters to the device instance */
	module->remaining_tx_buffer_length = length;

	system_interrupt_leave_critical_section();

	module->tx_buffer_ptr              = tx_data;
	module->tx_status                  = STATUS_BUSY;

	/* Enable the Data Register Empty Interrupt */
	usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_DRE;

	return STATUS_OK;
}
void *platform_configure_timer(platform_hw_timer_callback_t bus_tc_cb_ptr)
{
	struct tc_config timer_config;
	
	system_interrupt_enter_critical_section();
	if (hw_timers[0].timer_usage == 0)
	{
		hw_timers[0].timer_usage = 1;
		platform_cc1_cb = bus_tc_cb_ptr;

		tc_get_config_defaults(&timer_config);
		timer_config.clock_prescaler		= TC_CLOCK_PRESCALER_DIV1;
		timer_config.oneshot				= true;
		timer_config.counter_size			= TC_COUNTER_SIZE_32BIT;
		timer_config.count_direction		= TC_COUNT_DIRECTION_UP;
		tc_init(&bus_tc_instance, CONF_BUS_TC_MODULE, &timer_config);
		timer_count_per_ms = ((system_gclk_gen_get_hz(timer_config.clock_source)) /1000);
		tc_set_count_value(&bus_tc_instance, 0);
		tc_enable(&bus_tc_instance);
		tc_stop_counter(&bus_tc_instance);
		tc_register_callback(&bus_tc_instance, tc_cc1_cb,
		TC_CALLBACK_OVERFLOW);
		tc_enable_callback(&bus_tc_instance, TC_CALLBACK_OVERFLOW);
		
		hw_timers[0].timer_frequency = (system_gclk_gen_get_hz(timer_config.clock_source));
		hw_timers[0].timer_instance = bus_tc_instance;
		system_interrupt_leave_critical_section();
		return (&hw_timers[0]);
	}
	system_interrupt_leave_critical_section();
	return NULL;
}
/**
 * \internal
 * Asynchronous read of a buffer with a given length
 *
 * \param[in]  module   Pointer to USART software instance struct
 * \param[in]  rx_data  Pointer to data to be received
 * \param[in]  length   Length of data buffer
 *
 */
enum status_code _usart_read_buffer(
		struct usart_module *const module,
		uint8_t *rx_data,
		uint16_t length)
{
	/* Sanity check arguments */
	Assert(module);
	Assert(module->hw);
	Assert(rx_data);

	/* Get a pointer to the hardware module instance */
	SercomUsart *const usart_hw = &(module->hw->USART);

	system_interrupt_enter_critical_section();

	/* Check if the USART receiver is busy */
	if (module->remaining_rx_buffer_length > 0) {
		system_interrupt_leave_critical_section();
		return STATUS_BUSY;
	}

	/* Set length for the buffer and the pointer, and let
	 * the interrupt handler do the rest */
	module->remaining_rx_buffer_length = length;

	system_interrupt_leave_critical_section();

	module->rx_buffer_ptr              = rx_data;
	module->rx_status                  = STATUS_BUSY;

	/* Enable the RX Complete Interrupt */
	usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_RXC;

#ifdef FEATURE_USART_LIN_SLAVE
	/* Enable the break character is received Interrupt */
	if(module->lin_slave_enabled) {
		usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_RXBRK;
	}
#endif

#ifdef FEATURE_USART_START_FRAME_DECTION
	/* Enable a start condition is detected Interrupt */
	if(module->start_frame_detection_enabled) {
		usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_RXS;
	}
#endif

	return STATUS_OK;
}
Beispiel #4
0
static uint8_t _events_find_first_free_channel_and_allocate(void)
{
	uint8_t count;
	uint32_t tmp;
	bool allocated = false;

	system_interrupt_enter_critical_section();

	tmp = _events_inst.allocated_channels;

	for(count = 0; count < EVSYS_CHANNELS; ++count) {

		if(!(tmp & 0x00000001)) {
			/* If free channel found, set as allocated and return number */

			_events_inst.allocated_channels |= 1 << count;
			_events_inst.free_channels--;
			allocated = true;

			break;

		}

		tmp = tmp >> 1;
	}

	system_interrupt_leave_critical_section();

	if(!allocated) {
		return EVENTS_INVALID_CHANNEL;
	} else {
		return count;
	}
}
Beispiel #5
0
/**
 * \brief Resets the hardware module
 *
 * This will reset the module to hardware defaults.
 *
 * \param[in,out] module  Pointer to software module structure
 */
void i2c_slave_reset(
		struct i2c_slave_module *const module)
{
	/* Sanity check arguments */
	Assert(module);
	Assert(module->hw);

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

#if I2C_SLAVE_CALLBACK_MODE == true
	/* Reset module instance */
	module->registered_callback = 0;
	module->enabled_callback = 0;
	module->buffer_length = 0;
	module->buffer_remaining = 0;
	module->buffer = NULL;
#endif

	/* Disable module */
	i2c_slave_disable(module);

#if I2C_SLAVE_CALLBACK_MODE == true
	/* Clear all pending interrupts */
	system_interrupt_enter_critical_section();
	system_interrupt_clear_pending(_sercom_get_interrupt_vector(module->hw));
	system_interrupt_leave_critical_section();
#endif

	/* Wait for sync */
	_i2c_slave_wait_for_sync(module);

	/* Reset module */
	i2c_hw->CTRLA.reg = SERCOM_I2CS_CTRLA_SWRST;
}
Beispiel #6
0
/**
 * \brief Disables a Generic Clock that was previously enabled.
 *
 * Stops the clock generation of a Generic Clock that was previously started
 * via a call to \ref system_gclk_chan_enable().
 *
 * \param[in] channel  Generic Clock channel to disable
 */
void system_gclk_chan_disable(
		const uint8_t channel)
{
	system_interrupt_enter_critical_section();

	/* Select the requested generator channel */
	*((uint8_t*)&GCLK->CLKCTRL.reg) = channel;

	/* Sanity check WRTLOCK */
	Assert(!GCLK->CLKCTRL.bit.WRTLOCK);

	/* Switch to known-working source so that the channel can be disabled */
	uint32_t prev_gen_id = GCLK->CLKCTRL.bit.GEN;
	GCLK->CLKCTRL.bit.GEN = 0;

	/* Disable the generic clock */
	GCLK->CLKCTRL.reg &= ~GCLK_CLKCTRL_CLKEN;
	while (GCLK->CLKCTRL.reg & GCLK_CLKCTRL_CLKEN) {
		/* Wait for clock to become disabled */
	}

	/* Restore previous configured clock generator */
	GCLK->CLKCTRL.bit.GEN = prev_gen_id;

	system_interrupt_leave_critical_section();
}
Beispiel #7
0
/**
 * \brief Resets the hardware module
 *
 * Reset the module to hardware defaults.
 *
 * \param[in,out] module Pointer to software module structure
 */
void i2c_master_reset(struct i2c_master_module *const module)
{
	/* Sanity check arguments */
	Assert(module);
	Assert(module->hw);

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

	/* Wait for sync */
	_i2c_master_wait_for_sync(module);

	/* Disable module */
	i2c_master_disable(module);

#if I2C_MASTER_CALLBACK_MODE == true
	/* Clear all pending interrupts */
	system_interrupt_enter_critical_section();
	system_interrupt_clear_pending(_sercom_get_interrupt_vector(module->hw));
	system_interrupt_leave_critical_section();
#endif

	/* Wait for sync */
	_i2c_master_wait_for_sync(module);

	/* Reset module */
	i2c_module->CTRLA.reg = SERCOM_I2CM_CTRLA_SWRST;
}
Beispiel #8
0
Datei: gclk.c Projekt: Gussy/sam0
/**
 * \brief Retrieves the clock frequency of a Generic Clock generator.
 *
 * Determines the clock frequency (in Hz) of a specified Generic Clock
 * generator, used as a source to a Generic Clock Channel module.
 *
 * \param[in] generator  Generic Clock Generator index
 *
 * \return The frequency of the generic clock generator, in Hz.
 */
uint32_t system_gclk_gen_get_hz(
		const uint8_t generator)
{
	while (system_gclk_is_syncing(generator)) {
		/* Wait for synchronization */
	};

	system_interrupt_enter_critical_section();

	/* Get the frequency of the source connected to the GCLK generator */
	uint32_t gen_input_hz = system_clock_source_get_hz(
			(enum system_clock_source)GCLK->GENCTRL[generator].bit.SRC);

	uint8_t divsel = GCLK->GENCTRL[generator].bit.DIVSEL;
	uint32_t divider = GCLK->GENCTRL[generator].bit.DIV;

	system_interrupt_leave_critical_section();

	/* Check if the generator is using fractional or binary division */
	if (!divsel && divider > 1) {
		gen_input_hz /= divider;
	} else if (divsel) {
		gen_input_hz >>= (divider+1);
	}

	return gen_input_hz;
}
int main(void)
{
	system_init();

//! [main_1]
//! [critical_section_start]
	system_interrupt_enter_critical_section();
//! [critical_section_start]

//! [do_critical_code]
	if (is_ready == true) {
		/* Do something in response to the global shared flag */
		is_ready = false;
	}
//! [do_critical_code]

//! [critical_section_end]
	system_interrupt_leave_critical_section();
//! [critical_section_end]
//! [main_1]

//! [main_2]
//! [module_int_enable]
	system_interrupt_enable(SYSTEM_INTERRUPT_MODULE_RTC);
//! [module_int_enable]

//! [global_int_enable]
	system_interrupt_enable_global();
//! [global_int_enable]
//! [main_2]

	while (true) {
		/* Infinite loop */
	}
}
Beispiel #10
0
Datei: gclk.c Projekt: Gussy/sam0
/**
 * \brief Locks a Generic Clock channel from further configuration writes.
 *
 * Locks a generic clock channel from further configuration writes. It is only
 * possible to unlock the channel configuration through a power on reset.
 *
 * \param[in] channel   Generic Clock channel to enable
 */
void system_gclk_chan_lock(
		const uint8_t channel)
{
	system_interrupt_enter_critical_section();

	 GCLK->PCHCTRL[channel].reg |= GCLK_PCHCTRL_WRTLOCK;
	system_interrupt_leave_critical_section();
}
Beispiel #11
0
static void _events_release_channel(uint8_t channel)
{
	system_interrupt_enter_critical_section();

	_events_inst.allocated_channels &= ~(1 << channel);
	_events_inst.free_channels++;

	system_interrupt_leave_critical_section();
}
Beispiel #12
0
Datei: gclk.c Projekt: Gussy/sam0
/**
 * \brief Determins if the specified Generic Clock channel is locked.
 *
 * \param[in] channel  Generic Clock Channel index
 *
 * \return The lock status.
 * \retval true The Generic Clock channel is locked
 * \retval false The Generic Clock channel is not locked
 */
bool system_gclk_chan_is_locked(
		const uint8_t channel)
{
	bool locked;

	system_interrupt_enter_critical_section();
	locked = GCLK->PCHCTRL[channel].bit.WRTLOCK;
	system_interrupt_leave_critical_section();

	return locked;
}
Beispiel #13
0
/*---------------------------------------------------------------------------*/
clock_time_t
clock_time(void)
{
    //  printf("0x%04x\n", tc_get_count_value(&tc_instance));
    //  printf("clock_time ticks %d\n", ticks);
    clock_time_t t;

    system_interrupt_enter_critical_section();
    t = ticks;
    system_interrupt_leave_critical_section();
    return t;
}
Beispiel #14
0
Datei: gclk.c Projekt: Gussy/sam0
/**
 * \brief Retrieves the clock frequency of a Generic Clock channel.
 *
 * Determines the clock frequency (in Hz) of a specified Generic Clock
 * channel, used as a source to a device peripheral module.
 *
 * \param[in] channel  Generic Clock Channel index
 *
 * \return The frequency of the generic clock channel, in Hz.
 */
uint32_t system_gclk_chan_get_hz(
		const uint8_t channel)
{
	uint8_t gen_id;

	system_interrupt_enter_critical_section();
	/* Select the requested generic clock channel */
	gen_id = GCLK->PCHCTRL[channel].bit.GEN;
	system_interrupt_leave_critical_section();

	/* Return the clock speed of the associated GCLK generator */
	return system_gclk_gen_get_hz(gen_id);
}
Beispiel #15
0
/**
 * \brief Locks a Generic Clock channel from further configuration writes.
 *
 * Locks a generic clock channel from further configuration writes. It is only
 * possible to unlock the channel configuration through a power on reset.
 *
 * \param[in] channel   Generic Clock channel to enable
 */
void system_gclk_chan_lock(
		const uint8_t channel)
{
	system_interrupt_enter_critical_section();

	/* Select the requested generator channel */
	*((uint8_t*)&GCLK->CLKCTRL.reg) = channel;

	/* Lock the generic clock */
	GCLK->CLKCTRL.reg |= GCLK_CLKCTRL_WRTLOCK;

	system_interrupt_leave_critical_section();
}
Beispiel #16
0
Datei: gclk.c Projekt: Gussy/sam0
/**
 * \brief Determins if the specified Generic Clock channel is enabled.
 *
 * \param[in] channel  Generic Clock Channel index
 *
 * \return The enabled status.
 * \retval true The Generic Clock channel is enabled
 * \retval false The Generic Clock channel is disabled
 */
bool system_gclk_chan_is_enabled(
		const uint8_t channel)
{
	bool enabled;

	system_interrupt_enter_critical_section();

	/* Select the requested generic clock channel */
	enabled = GCLK->PCHCTRL[channel].bit.CHEN;

	system_interrupt_leave_critical_section();

	return enabled;
}
Beispiel #17
0
Datei: gclk.c Projekt: Gussy/sam0
/**
 * \brief Enables a Generic Clock that was previously configured.
 *
 * Starts the clock generation of a Generic Clock that was previously
 * configured via a call to \ref system_gclk_chan_set_config().
 *
 * \param[in] channel   Generic Clock channel to enable
 */
void system_gclk_chan_enable(
		const uint8_t channel)
{
	system_interrupt_enter_critical_section();

	/* Enable the peripheral channel */
	GCLK->PCHCTRL[channel].reg |= GCLK_PCHCTRL_CHEN;

	while (!(GCLK->PCHCTRL[channel].reg & GCLK_PCHCTRL_CHEN)) {
		/* Wait for clock synchronization */
	}

	system_interrupt_leave_critical_section();
}
Beispiel #18
0
Datei: gclk.c Projekt: Gussy/sam0
/**
 * \brief Determins if the specified Generic Clock Generator is enabled.
 *
 * \param[in] generator  Generic Clock Generator index to check
 *
 * \return The enabled status.
 * \retval true The Generic Clock Generator is enabled
 * \retval false The Generic Clock Generator is disabled
 */
bool system_gclk_gen_is_enabled(
		const uint8_t generator)
{
	bool enabled;

	system_interrupt_enter_critical_section();

	/* Obtain the enabled status */
	enabled = (GCLK->GENCTRL[generator].reg & GCLK_GENCTRL_GENEN);

	system_interrupt_leave_critical_section();

	return enabled;
}
Beispiel #19
0
Datei: gclk.c Projekt: Gussy/sam0
/**
 * \brief Enables a Generic Clock Generator that was previously configured.
 *
 * Starts the clock generation of a Generic Clock Generator that was previously
 * configured via a call to \ref system_gclk_gen_set_config().
 *
 * \param[in] generator  Generic Clock Generator index to enable
 */
void system_gclk_gen_enable(
		const uint8_t generator)
{
	while (system_gclk_is_syncing(generator)) {
		/* Wait for synchronization */
	};

	system_interrupt_enter_critical_section();

	/* Enable generator */
	GCLK->GENCTRL[generator].reg |= GCLK_GENCTRL_GENEN;

	system_interrupt_leave_critical_section();
}
Beispiel #20
0
/**
 * \brief Determins if the specified Generic Clock channel is locked.
 *
 * \param[in] channel  Generic Clock Channel index
 *
 * \return The lock status.
 * \retval true The Generic Clock channel is locked
 * \retval false The Generic Clock channel is not locked
 */
bool system_gclk_chan_is_locked(
		const uint8_t channel)
{
	bool locked;

	system_interrupt_enter_critical_section();

	/* Select the requested generic clock channel */
	*((uint8_t*)&GCLK->CLKCTRL.reg) = channel;
	locked = GCLK->CLKCTRL.bit.WRTLOCK;

	system_interrupt_leave_critical_section();

	return locked;
}
Beispiel #21
0
/**
 * \brief Determins if the specified Generic Clock channel is enabled.
 *
 * \param[in] channel  Generic Clock Channel index
 *
 * \return The enabled status.
 * \retval true The Generic Clock channel is enabled
 * \retval false The Generic Clock channel is disabled
 */
asf_bool_t system_gclk_chan_is_enabled(
		const uint8_t channel)
{
	asf_bool_t enabled;

	system_interrupt_enter_critical_section();

	/* Select the requested generic clock channel */
	*((uint8_t*)&GCLK->CLKCTRL.reg) = channel;
	enabled = GCLK->CLKCTRL.bit.CLKEN;

	system_interrupt_leave_critical_section();

	return enabled;
}
Beispiel #22
0
/**
 * \brief Determins if the specified Generic Clock Generator is enabled.
 *
 * \param[in] generator  Generic Clock Generator index to check
 *
 * \return The enabled status.
 * \retval true The Generic Clock Generator is enabled
 * \retval false The Generic Clock Generator is disabled
 */
bool system_gclk_gen_is_enabled(
		const uint8_t generator)
{
	bool enabled;

	system_interrupt_enter_critical_section();

	/* Select the requested generator */
	*((uint8_t*)&GCLK->GENCTRL.reg) = generator;
	/* Obtain the enabled status */
	enabled = (GCLK->GENCTRL.reg & GCLK_GENCTRL_GENEN);

	system_interrupt_leave_critical_section();

	return enabled;
}
Beispiel #23
0
Datei: gclk.c Projekt: Gussy/sam0
/**
 * \brief Disables a Generic Clock that was previously enabled.
 *
 * Stops the clock generation of a Generic Clock that was previously started
 * via a call to \ref system_gclk_chan_enable().
 *
 * \param[in] channel  Generic Clock channel to disable
 */
void system_gclk_chan_disable(
		const uint8_t channel)
{
	system_interrupt_enter_critical_section();

	/* Sanity check WRTLOCK */
	Assert(!GCLK->PCHCTRL[channel].bit.WRTLOCK);

	/* Disable the peripheral channel */
	GCLK->PCHCTRL[channel].reg &= ~GCLK_PCHCTRL_CHEN;

	while (GCLK->PCHCTRL[channel].reg & GCLK_PCHCTRL_CHEN) {
		/* Wait for clock synchronization */
	}

	system_interrupt_leave_critical_section();
}
Beispiel #24
0
Datei: gclk.c Projekt: Gussy/sam0
/**
 * \brief Disables a Generic Clock Generator that was previously enabled.
 *
 * Stops the clock generation of a Generic Clock Generator that was previously
 * started via a call to \ref system_gclk_gen_enable().
 *
 * \param[in] generator  Generic Clock Generator index to disable
 */
void system_gclk_gen_disable(
		const uint8_t generator)
{
	while (system_gclk_is_syncing(generator)) {
		/* Wait for synchronization */
	};

	system_interrupt_enter_critical_section();

	/* Disable generator */
	GCLK->GENCTRL[generator].reg &= ~GCLK_GENCTRL_GENEN;
	while (GCLK->GENCTRL[generator].reg & GCLK_GENCTRL_GENEN) {
		/* Wait for clock to become disabled */
	}

	system_interrupt_leave_critical_section();
}
Beispiel #25
0
/**
 * \brief Enables a Generic Clock Generator that was previously configured.
 *
 * Starts the clock generation of a Generic Clock Generator that was previously
 * configured via a call to \ref system_gclk_gen_set_config().
 *
 * \param[in] generator  Generic Clock Generator index to enable
 */
void system_gclk_gen_enable(
		const uint8_t generator)
{
	while (system_gclk_is_syncing()) {
		/* Wait for synchronization */
	};

	system_interrupt_enter_critical_section();

	/* Select the requested generator */
	*((uint8_t*)&GCLK->GENCTRL.reg) = generator;
	while (system_gclk_is_syncing()) {
		/* Wait for synchronization */
	};

	/* Enable generator */
	GCLK->GENCTRL.reg |= GCLK_GENCTRL_GENEN;

	system_interrupt_leave_critical_section();
}
Beispiel #26
0
enum status_code events_trigger(struct events_resource *resource)
{
	Assert(resource);

	system_interrupt_enter_critical_section();

	/* Assert if event path is asynchronous */
	if (EVSYS->CHANNEL[resource->channel].reg &
			EVSYS_CHANNEL_PATH(EVENTS_PATH_ASYNCHRONOUS)) {
		return STATUS_ERR_UNSUPPORTED_DEV;
	}

	/* Assert if event edge detection is not set to RISING */
	if (!(EVSYS->CHANNEL[resource->channel].reg &
			EVSYS_CHANNEL_EDGSEL(EVENTS_EDGE_DETECT_RISING))) {
		return STATUS_ERR_UNSUPPORTED_DEV;
	}

	EVSYS->SWEVT.reg = (0x01UL << resource->channel);

	system_interrupt_leave_critical_section();

	return STATUS_OK;
}
Beispiel #27
0
/**
 * \brief Writes a Generic Clock Generator configuration to the hardware module.
 *
 * Writes out a given configuration of a Generic Clock Generator configuration
 * to the hardware module.
 *
 * \note Changing the clock source on the fly (on a running
 *       generator) can take additional time if the clock source is configured
 *       to only run on-demand (ONDEMAND bit is set) and it is not currently
 *       running (no peripheral is requesting the clock source). In this case
 *       the GCLK will request the new clock while still keeping a request to
 *       the old clock source until the new clock source is ready.
 *
 * \note This function will not start a generator that is not already running;
 *       to start the generator, call \ref system_gclk_gen_enable()
 *       after configuring a generator.
 *
 * \param[in] generator  Generic Clock Generator index to configure
 * \param[in] config     Configuration settings for the generator
 */
void system_gclk_gen_set_config(
		const uint8_t generator,
		struct system_gclk_gen_config *const config)
{
	/* Sanity check arguments */
	Assert(config);

	/* Cache new register configurations to minimize sync requirements. */
	uint32_t new_genctrl_config = (generator << GCLK_GENCTRL_ID_Pos);
	uint32_t new_gendiv_config  = (generator << GCLK_GENDIV_ID_Pos);

	/* Select the requested source clock for the generator */
	new_genctrl_config |= config->source_clock << GCLK_GENCTRL_SRC_Pos;

	/* Configure the clock to be either high or low when disabled */
	if (config->high_when_disabled) {
		new_genctrl_config |= GCLK_GENCTRL_OOV;
	}

	/* Configure if the clock output to I/O pin should be enabled. */
	if (config->output_enable) {
		new_genctrl_config |= GCLK_GENCTRL_OE;
	}

	/* Set division factor */
	if (config->division_factor > 1) {
		/* Check if division is a power of two */
		if (((config->division_factor & (config->division_factor - 1)) == 0)) {
			/* Determine the index of the highest bit set to get the
			 * division factor that must be loaded into the division
			 * register */

			uint32_t div2_count = 0;

			uint32_t mask;
			for (mask = (1UL << 1); mask < config->division_factor;
						mask <<= 1) {
				div2_count++;
			}

			/* Set binary divider power of 2 division factor */
			new_gendiv_config  |= div2_count << GCLK_GENDIV_DIV_Pos;
			new_genctrl_config |= GCLK_GENCTRL_DIVSEL;
		} else {
			/* Set integer division factor */

			new_gendiv_config  |=
					(config->division_factor) << GCLK_GENDIV_DIV_Pos;

			/* Enable non-binary division with increased duty cycle accuracy */
			new_genctrl_config |= GCLK_GENCTRL_IDC;
		}

	}

	/* Enable or disable the clock in standby mode */
	if (config->run_in_standby) {
		new_genctrl_config |= GCLK_GENCTRL_RUNSTDBY;
	}

	while (system_gclk_is_syncing()) {
		/* Wait for synchronization */
	};

	system_interrupt_enter_critical_section();

	GCLK->GENDIV.reg  = new_gendiv_config;

	while (system_gclk_is_syncing()) {
		/* Wait for synchronization */
	};
	GCLK->GENCTRL.reg = new_genctrl_config | (GCLK->GENCTRL.reg & GCLK_GENCTRL_GENEN);

	system_interrupt_leave_critical_section();
}
/**
 * \brief Updates an arbitrary section of a page with new data.
 *
 * Writes from a buffer to a given page in the NVM memory, retaining any
 * unmodified data already stored in the page.
 *
 * \note If manual write mode is enable, write command must be executed after
 * this function, otherwise the data will not write to NVM from page buffer.
 *
 * \warning This routine is unsafe if data integrity is critical; a system reset
 *          during the update process will result in up to one row of data being
 *          lost. If corruption must be avoided in all circumstances (including
 *          power loss or system reset) this function should not be used.
 *
 * \param[in]  destination_address  Destination page address to write to
 * \param[in]  buffer               Pointer to buffer where the data to write is
 *                                  stored
 * \param[in]  offset               Number of bytes to offset the data write in
 *                                  the page
 * \param[in]  length               Number of bytes in the page to update
 *
 * \return Status of the attempt to update a page.
 *
 * \retval STATUS_OK               Requested NVM memory page was successfully
 *                                 read
 * \retval STATUS_BUSY             NVM controller was busy when the operation
 *                                 was attempted
 * \retval STATUS_ERR_BAD_ADDRESS  The requested address was outside the
 *                                 acceptable range of the NVM memory region
 * \retval STATUS_ERR_INVALID_ARG  The supplied length and offset was invalid
 */
enum status_code nvm_update_buffer(
		const uint32_t destination_address,
		uint8_t *const buffer,
		uint16_t offset,
		uint16_t length)
{
	enum status_code error_code = STATUS_OK;
	uint8_t row_buffer[NVMCTRL_ROW_PAGES][NVMCTRL_PAGE_SIZE];

	/* Ensure the read does not overflow the page size */
	if ((offset + length) > _nvm_dev.page_size) {
		return STATUS_ERR_INVALID_ARG;
	}

	/* Calculate the starting row address of the page to update */
	uint32_t row_start_address =
			destination_address & ~((_nvm_dev.page_size * NVMCTRL_ROW_PAGES) - 1);

	/* Read in the current row contents */
	for (uint32_t i = 0; i < NVMCTRL_ROW_PAGES; i++) {
		do
		{
			error_code = nvm_read_buffer(
					row_start_address + (i * _nvm_dev.page_size),
					row_buffer[i], _nvm_dev.page_size);
		} while (error_code == STATUS_BUSY);

		if (error_code != STATUS_OK) {
			return error_code;
		}
	}

	/* Calculate the starting page in the row that is to be updated */
	uint8_t page_in_row =
			(destination_address % (_nvm_dev.page_size * NVMCTRL_ROW_PAGES)) /
			_nvm_dev.page_size;

	/* Update the specified bytes in the page buffer */
	for (uint32_t i = 0; i < length; i++) {
		row_buffer[page_in_row][offset + i] = buffer[i];
	}

	system_interrupt_enter_critical_section();

	/* Erase the row */
	do
	{
		error_code = nvm_erase_row(row_start_address);
	} while (error_code == STATUS_BUSY);

	if (error_code != STATUS_OK) {
		system_interrupt_leave_critical_section();
		return error_code;
	}

	/* Write the updated row contents to the erased row */
	for (uint32_t i = 0; i < NVMCTRL_ROW_PAGES; i++) {
		do
		{
			error_code = nvm_write_buffer(
					row_start_address + (i * _nvm_dev.page_size),
					row_buffer[i], _nvm_dev.page_size);
		} while (error_code == STATUS_BUSY);

		if (error_code != STATUS_OK) {
			system_interrupt_leave_critical_section();
			return error_code;
		}
	}

	system_interrupt_leave_critical_section();

	return error_code;
}
void platform_leave_critical_section(void)
{
	system_interrupt_leave_critical_section();
}
Beispiel #30
0
/**
 * \brief Start vectored I/O transfer
 *
 * This function initiates a uni- or bidirectional SPI transfer from/to any
 * number of data buffers. The transfer is interrupt-driven and will run in the
 * background, after this function has returned.
 *
 * The buffers to transmit from or receive into must be described in arrays of
 * buffer descriptors. These arrays \e must end with descriptors that specify
 * zero buffer length. The first descriptor in an array can \e not specify zero
 * length. The number of bytes to transmit and to receive do not have to be
 * equal.
 *
 * If the address for a receive buffer is set to \c NULL, the received bytes
 * corresponding to that buffer descriptor will be discarded. This is useful if
 * slave is already set up to transfer a number of bytes, but the master has no
 * available buffer to receive them into. As an example, to receive the two
 * first bytes and discard the 128 following, the buffer descriptors could be:
\code
	struct spi_master_vec_bufdesc rx_buffers[3] = {
		// Read two status bytes
		{.data = status_buffer, .length = 2},
		// Discard 128 data bytes
		{.data = NULL, .length = 128},
		// End of reception
		{.length = 0},
	};
\endcode
 *
 * To initiate a unidirectional transfer, pass \c NULL as the address of either
 * buffer descriptor array, like this:
\code
	// Transmit some buffers
	spi_master_vec_transceive_buffer_job(&module, tx_buffers, NULL);

	// Receive some buffers
	spi_master_vec_transceive_buffer_job(&module, NULL, rx_buffers);
\endcode
 *
 * \pre \ref spi_master_vec_init() and \ref spi_master_vec_enable() must have
 * been called before this function.
 *
 * \param[in,out] module Driver instance to operate on.
 * \param[in] tx_bufdescs address of buffer descriptor array for bytes to
 * transmit.
 * \arg \c NULL if the transfer is a simplex read.
 * \param[in,out] rx_bufdescs address of buffer descriptor array for storing
 * received bytes.
 * \arg \c NULL if the transfer is a simplex write.
 *
 * \return Status of transfer start.
 * \retval STATUS_OK if transfer was started.
 * \retval STATUS_BUSY if a transfer is already on-going.
 */
enum status_code spi_master_vec_transceive_buffer_job(
		struct spi_master_vec_module *const module,
		struct spi_master_vec_bufdesc tx_bufdescs[],
		struct spi_master_vec_bufdesc rx_bufdescs[])
{
	Assert(module);
	Assert(module->sercom);
	Assert(tx_bufdescs || rx_bufdescs);

	SercomSpi *const spi_hw = &(module->sercom->SPI);
	uint32_t tmp_ctrlb;
	uint8_t tmp_intenset;

	system_interrupt_enter_critical_section();
	if (module->status == STATUS_BUSY) {
		system_interrupt_leave_critical_section();
		return STATUS_BUSY;
	} else {
		module->status = STATUS_BUSY;
		system_interrupt_leave_critical_section();
	}

#ifdef CONF_SPI_MASTER_VEC_OS_SUPPORT
	CONF_SPI_MASTER_VEC_TAKE_SEMAPHORE(module->busy_semaphore);
#endif

	module->tx_bufdesc_ptr = tx_bufdescs;
	module->rx_bufdesc_ptr = rx_bufdescs;

	if (tx_bufdescs && rx_bufdescs) {
		Assert(tx_bufdescs[0].length);
		Assert(rx_bufdescs[0].length);

		module->direction = SPI_MASTER_VEC_DIRECTION_BOTH;
		module->tx_length = tx_bufdescs[0].length;
		module->tx_head_ptr = tx_bufdescs[0].data;
		module->rx_length = rx_bufdescs[0].length;
		module->rx_head_ptr = rx_bufdescs[0].data;
		module->tx_lead_on_rx = 0;
		tmp_ctrlb = SERCOM_SPI_CTRLB_RXEN;
		tmp_intenset = SERCOM_SPI_INTFLAG_DRE | SERCOM_SPI_INTFLAG_RXC;
	} else {
		if (tx_bufdescs) {
			Assert(tx_bufdescs[0].length);

			module->direction = SPI_MASTER_VEC_DIRECTION_WRITE;
			module->tx_length = tx_bufdescs[0].length;
			module->tx_head_ptr = tx_bufdescs[0].data;
			tmp_ctrlb = 0;
			tmp_intenset = SERCOM_SPI_INTFLAG_DRE;
		} else {
			Assert(rx_bufdescs[0].length);

			module->direction = SPI_MASTER_VEC_DIRECTION_READ;
			module->rx_length = rx_bufdescs[0].length;
			module->rx_head_ptr = rx_bufdescs[0].data;
			module->tx_lead_on_rx = 0;
			tmp_ctrlb = SERCOM_SPI_CTRLB_RXEN;
			tmp_intenset = SERCOM_SPI_INTFLAG_DRE | SERCOM_SPI_INTFLAG_RXC;
		}
	}

	/* Ensure the SERCOM is sync'ed before writing these registers */
	_spi_master_vec_wait_for_sync(spi_hw);

	spi_hw->CTRLB.reg = tmp_ctrlb;
	spi_hw->INTENSET.reg = tmp_intenset;

	return STATUS_OK;
}