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