kopia lustrzana https://github.com/bristol-seds/pico-tracker
Added files from previous commits :(
rodzic
9c581b5a05
commit
f3ed51dbec
|
@ -0,0 +1,331 @@
|
|||
/**
|
||||
* SAM D20/D21/R21 Event System Driver
|
||||
*
|
||||
* Copyright (C) 2012-2014 Atmel Corporation. All rights reserved.
|
||||
*
|
||||
* \asf_license_start
|
||||
*
|
||||
* \page License
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of Atmel may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 4. This software may only be redistributed and used in connection with an
|
||||
* Atmel microcontroller product.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
|
||||
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* \asf_license_stop
|
||||
*
|
||||
*/
|
||||
#ifndef EVENTS_H_INCLUDED
|
||||
#define EVENTS_H_INCLUDED
|
||||
|
||||
#include "samd20.h"
|
||||
#include "system/system.h"
|
||||
|
||||
/**
|
||||
* Event System Driver (EVENTS)
|
||||
*
|
||||
* This driver for SAM D20/D21/R21 devices provides an interface for the configuration
|
||||
* and management of the device's peripheral event resources and users within
|
||||
* the device, including enabling and disabling of peripheral source selection
|
||||
* and synchronization of clock domains between various modules. The following API
|
||||
* modes is covered by this manual:
|
||||
* - Polled API
|
||||
*
|
||||
* The following peripherals are used by this module:
|
||||
*
|
||||
* - EVSYS (Event System Management)
|
||||
*
|
||||
* Module Overview
|
||||
*
|
||||
* Peripherals within the SAM D20/D21/R21 devices are capable of generating two types of
|
||||
* actions in response to given stimulus: set a register flag for later
|
||||
* intervention by the CPU (using interrupt or polling methods), or generate
|
||||
* event signals which can be internally routed directly to other
|
||||
* peripherals within the device. The use of events allows for direct actions
|
||||
* to be performed in one peripheral in response to a stimulus in another
|
||||
* without CPU intervention. This can lower the overall power consumption of the
|
||||
* system if the CPU is able to remain in sleep modes for longer periods (SleepWalking™), and
|
||||
* lowers the latency of the system response.
|
||||
*
|
||||
* The event system is comprised of a number of freely configurable Event
|
||||
* resources, plus a number of fixed Event Users. Each Event resource can be
|
||||
* configured to select the input peripheral that will generate the events
|
||||
* signal, as well as the synchronization path and edge detection mode.
|
||||
* The fixed-function Event Users, connected to peripherals within the device,
|
||||
* can then subscribe to an Event resource in a one-to-many relationship in order
|
||||
* to receive events as they are generated. An overview of the event system
|
||||
* chain is shown in
|
||||
*
|
||||
* There are many different events that can be routed in the device, which can
|
||||
* then trigger many different actions. For example, an Analog Comparator module
|
||||
* could be configured to generate an event when the input signal rises above
|
||||
* the compare threshold, which then triggers a Timer Counter module to capture
|
||||
* the current count value for later use.
|
||||
*
|
||||
* Event Channels
|
||||
*
|
||||
* The Event module in each device consists of several channels, which can be
|
||||
* freely linked to an event generator (i.e. a peripheral within the device
|
||||
* that is capable of generating events). Each channel can be individually
|
||||
* configured to select the generator peripheral, signal path and edge detection
|
||||
* applied to the input event signal, before being passed to any event user(s).
|
||||
*
|
||||
* Event channels can support multiple users within the device in a standardized
|
||||
* manner; when an Event User is linked to an Event Channel, the channel will
|
||||
* automatically handshake with all attached users to ensure that all modules
|
||||
* correctly receive and acknowledge the event.
|
||||
*
|
||||
* Event Users
|
||||
*
|
||||
* Event Users are able to subscribe to an Event Channel, once it has been
|
||||
* configured. Each Event User consists of a fixed connection to one of the
|
||||
* peripherals within the device (for example, an ADC module or Timer module)
|
||||
* and is capable of being connected to a single Event Channel.
|
||||
*
|
||||
* Edge Detection
|
||||
*
|
||||
* For asynchronous events, edge detection on the event input is not possible,
|
||||
* and the event signal must be passed directly between the event generator and
|
||||
* event user. For synchronous and re-synchronous events, the input signal from
|
||||
* the event generator must pass through an edge detection unit, so that only
|
||||
* the rising, falling or both edges of the event signal triggers an action in
|
||||
* the event user.
|
||||
*
|
||||
* Path Selection
|
||||
*
|
||||
* The event system in the SAM D20/D21/R21 devices supports three signal path types from
|
||||
* the event generator to event users: asynchronous, synchronous and
|
||||
* re-synchronous events.
|
||||
*
|
||||
* Asynchronous Paths
|
||||
*
|
||||
* Asynchronous event paths allow for an asynchronous connection between the
|
||||
* event generator and event user(s), when the source and destination
|
||||
* peripherals share the same "Generic Clock"
|
||||
* channel. In this mode the event is propagated between the source and
|
||||
* destination directly to reduce the event latency, thus no edge detection is
|
||||
* possible.
|
||||
*
|
||||
* Synchronous Paths
|
||||
*
|
||||
* The Synchronous event path should be used when edge detection or interrupts
|
||||
* from the event channel are required, and the source event generator and the
|
||||
* event channel shares the same Generic Clock channel.
|
||||
*
|
||||
* Re-synchronous Paths
|
||||
*
|
||||
* Re-synchronous event paths are a special form of synchronous events, where
|
||||
* when edge detection or interrupts from the event channel are required, but
|
||||
* the event generator and the event channel use different Generic Clock
|
||||
* channels. The re-synchronous path allows the Event System to synchronize the
|
||||
* incoming event signal from the Event Generator to the clock of the Event
|
||||
* System module to avoid missed events, at the cost of a higher latency due to
|
||||
* the re-synchronization process.
|
||||
*
|
||||
*
|
||||
* Configuring Events
|
||||
*
|
||||
* For SAM D20/D21/R21 devices, several steps are required to properly configure an
|
||||
* event chain, so that hardware peripherals can respond to events generated by
|
||||
* each other, listed below.
|
||||
*
|
||||
* Source Peripheral
|
||||
* -# The source peripheral (that will generate events) must be configured and
|
||||
* enabled.
|
||||
* -# The source peripheral (that will generate events) must have an output
|
||||
* event enabled.
|
||||
*
|
||||
* Event System
|
||||
* -# An event system channel must be allocated and configured with the
|
||||
* correct source peripheral selected as the channel's event generator.
|
||||
* -# The event system user must be configured and enabled, and attached to
|
||||
# event channel previously allocated.
|
||||
*
|
||||
* Destination Peripheral
|
||||
* -# The destination peripheral (that will receive events) must be configured
|
||||
* and enabled.
|
||||
* -# The destination peripheral (that will receive events) must have an input
|
||||
* event enabled.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
enum status_code {
|
||||
STATUS_OK,
|
||||
STATUS_BUSY,
|
||||
STATUS_ERR_INVALID_ARG,
|
||||
STATUS_ERR_OVERFLOW,
|
||||
STATUS_ERR_DENIED
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Edge detect enum
|
||||
*
|
||||
* Event channel edge detect setting
|
||||
*
|
||||
*/
|
||||
enum events_edge_detect {
|
||||
/** No event output */
|
||||
EVENTS_EDGE_DETECT_NONE,
|
||||
/** Event on rising edge */
|
||||
EVENTS_EDGE_DETECT_RISING,
|
||||
/** Event on falling edge */
|
||||
EVENTS_EDGE_DETECT_FALLING,
|
||||
/** Event on both edges */
|
||||
EVENTS_EDGE_DETECT_BOTH,
|
||||
};
|
||||
|
||||
/**
|
||||
* Path selection enum
|
||||
*
|
||||
* Event channel path selection
|
||||
*
|
||||
*/
|
||||
enum events_path_selection {
|
||||
/** Select the synchronous path for this event channel */
|
||||
EVENTS_PATH_SYNCHRONOUS,
|
||||
/** Select the resynchronizer path for this event channel */
|
||||
EVENTS_PATH_RESYNCHRONIZED,
|
||||
/** Select the asynchronous path for this event channel */
|
||||
EVENTS_PATH_ASYNCHRONOUS,
|
||||
};
|
||||
|
||||
/** Definition for no generator selection */
|
||||
#define EVSYS_ID_GEN_NONE 0
|
||||
/** Definition for no user selection */
|
||||
#define EVSYS_ID_USER_NONE 0
|
||||
|
||||
|
||||
|
||||
void system_events_init(void);
|
||||
|
||||
/**
|
||||
* Allocate an event channel and set configuration
|
||||
*
|
||||
* Allocates an event channel from the event channel pool and sets
|
||||
* the channel configuration.
|
||||
*
|
||||
* \param[out] resource Pointer to a \ref events_resource struct instance
|
||||
* \param[in] config Pointer to a \ref events_config struct
|
||||
*
|
||||
* \return Status of the configuration procedure
|
||||
* \retval STATUS_OK Allocation and configuration went successful
|
||||
* \retval STATUS_ERR_NOT_FOUND No free event channel found
|
||||
*
|
||||
*/
|
||||
enum status_code events_allocate(uint8_t channel,
|
||||
enum events_edge_detect edge_detect, /** edge detection mode */
|
||||
enum events_path_selection path, /** events channel path */
|
||||
uint8_t generator, /** event generator for the channel */
|
||||
uint8_t clock_source); /** clock source for the event channel */
|
||||
|
||||
/**
|
||||
* Attach user to the event channel
|
||||
*
|
||||
* Attach a user peripheral to the event channel to receive events.
|
||||
*
|
||||
* \param[in] resource Pointer to an \ref events_resource struct instance
|
||||
* \param[in] user_id A number identifying the user peripheral found in the device header file.
|
||||
*
|
||||
* \return Status of the user attach procedure
|
||||
* \retval STATUS_OK No errors detected when attaching the event user
|
||||
*/
|
||||
enum status_code events_attach_user(uint8_t channel, uint8_t user_id);
|
||||
|
||||
/**
|
||||
* Check if a channel is busy
|
||||
*
|
||||
* Check if a channel is busy, a channels stays busy until all users connected to the channel
|
||||
* has handled an event
|
||||
*
|
||||
* \param[in] resource Pointer to a \ref events_resource struct instance
|
||||
*
|
||||
* \return Status of the channels busy state
|
||||
* \retval true One or more users connected to the channel has not handled the last event
|
||||
* \retval false All users are ready handle new events
|
||||
*/
|
||||
bool events_is_busy(uint8_t channel);
|
||||
|
||||
/**
|
||||
* Trigger software event
|
||||
*
|
||||
* Trigger an event by software
|
||||
*
|
||||
* \param[in] resource Pointer to an \ref events_resource struct
|
||||
*
|
||||
* \return Status of the event software procedure
|
||||
* \retval STATUS_OK No error was detected when software tigger signal was issued
|
||||
* \retval STATUS_ERR_UNSUPPORTED_DEV If the channel path is asynchronous and/or the
|
||||
* edge detection is not set to RISING
|
||||
*/
|
||||
enum status_code events_trigger(uint8_t channel);
|
||||
|
||||
/**
|
||||
* Check if all users connected to the channel is ready
|
||||
*
|
||||
* Check if all users connected to the channel is ready to handle incomming events
|
||||
*
|
||||
* \param[in] resource Pointer to an \ref events_resource struct
|
||||
*
|
||||
* \return The ready status of users connected to an event channel
|
||||
* \retval true All users connect to event channel is ready handle incomming events
|
||||
* \retval false One or more users connect to event channel is not ready to handle incomming events
|
||||
*/
|
||||
bool events_is_users_ready(uint8_t channel);
|
||||
|
||||
/**
|
||||
* Check if event is detected on event channel
|
||||
*
|
||||
* Check if an event has been detected on the channel
|
||||
*
|
||||
* \note This function will clear the event detected interrupt flag
|
||||
*
|
||||
* \param[in] resource Pointer to an \ref events_resource struct
|
||||
*
|
||||
* \return Status of the event detection interrupt flag
|
||||
* \retval true Event has been detected
|
||||
* \retval false Event has not been detected
|
||||
*/
|
||||
bool events_is_detected(uint8_t channel);
|
||||
|
||||
/**
|
||||
* Check if there has been an overrun situation on this channel
|
||||
*
|
||||
* Check if there has been an overrun situation on this channel
|
||||
*
|
||||
* \note This function will clear the event overrun detected interrupt flag
|
||||
*
|
||||
* \param[in] resource Pointer to an \ref events_resource struct
|
||||
*
|
||||
* \return Status of the event overrun interrupt flag
|
||||
* \retval true Event overrun has been detected
|
||||
* \retval false Event overrun has not been detected
|
||||
*/
|
||||
bool events_is_overrun(uint8_t channel);
|
||||
|
||||
|
||||
#endif /* EVENTS_H_INCLUDED */
|
|
@ -0,0 +1,437 @@
|
|||
/**
|
||||
* SAM D20/D21/R21 External Interrupt Driver
|
||||
*
|
||||
* Copyright (C) 2012-2014 Atmel Corporation. All rights reserved.
|
||||
*
|
||||
* \asf_license_start
|
||||
*
|
||||
* \page License
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of Atmel may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 4. This software may only be redistributed and used in connection with an
|
||||
* Atmel microcontroller product.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
|
||||
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* \asf_license_stop
|
||||
*
|
||||
*/
|
||||
#ifndef EXTINT_H_INCLUDED
|
||||
#define EXTINT_H_INCLUDED
|
||||
|
||||
/**
|
||||
* SAM D20/D21/R21 External Interrupt Driver (EXTINT)
|
||||
*
|
||||
* This driver for SAM D20/D21/R21 devices provides an interface for the configuration
|
||||
* and management of external interrupts generated by the physical device pins,
|
||||
* including edge detection.
|
||||
*
|
||||
* Module Overview
|
||||
*
|
||||
* The External Interrupt (EXTINT) module provides a method of asynchronously
|
||||
* detecting rising edge, falling edge or specific level detection on individual
|
||||
* I/O pins of a device. This detection can then be used to trigger a software
|
||||
* interrupt or event, or polled for later use if required. External interrupts
|
||||
* can also optionally be used to automatically wake up the device from sleep
|
||||
* mode, allowing the device to conserve power while still being able to react
|
||||
* to an external stimulus in a timely manner.
|
||||
*
|
||||
* Logical Channels
|
||||
*
|
||||
* The External Interrupt module contains a number of logical channels, each of
|
||||
* which is capable of being individually configured for a given pin routing,
|
||||
* detection mode and filtering/wake up characteristics.
|
||||
*
|
||||
* Each individual logical external interrupt channel may be routed to a single
|
||||
* physical device I/O pin in order to detect a particular edge or level of the
|
||||
* incoming signal.
|
||||
*
|
||||
* NMI Channels
|
||||
*
|
||||
* One or more Non Maskable Interrupt (NMI) channels are provided within each
|
||||
* physical External Interrupt Controller module, allowing a single physical pin
|
||||
* of the device to fire a single NMI interrupt in response to a particular
|
||||
* edge or level stimulus. A NMI cannot, as the name suggests, be disabled in
|
||||
* firmware and will take precedence over any in-progress interrupt sources.
|
||||
*
|
||||
* NMIs can be used to implement critical device features such as forced
|
||||
* software reset or other functionality where the action should be executed in
|
||||
* preference to all other running code with a minimum amount of latency.
|
||||
*
|
||||
* Input Filtering and Detection
|
||||
*
|
||||
* To reduce the possibility of noise or other transient signals causing
|
||||
* unwanted device wake-ups, interrupts and/or events via an external interrupt
|
||||
* channel, a hardware signal filter can be enabled on individual channels. This
|
||||
* filter provides a Majority-of-Three voter filter on the incoming signal, so
|
||||
* that the input state is considered to be the majority vote of three
|
||||
* subsequent samples of the pin input buffer.
|
||||
*
|
||||
* Events and Interrupts
|
||||
*
|
||||
* Channel detection states may be polled inside the application for synchronous
|
||||
* detection, or events and interrupts may be used for asynchronous behavior.
|
||||
* Each channel can be configured to give an asynchronous hardware event (which
|
||||
* may in turn trigger actions in other hardware modules) or an asynchronous
|
||||
* software interrupt.
|
||||
*
|
||||
* Special Considerations
|
||||
*
|
||||
* Not all devices support disabling of the NMI channel(s) detection mode - see
|
||||
* your device datasheet.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "samd20.h"
|
||||
#include "system/pinmux.h"
|
||||
|
||||
/**
|
||||
* Configuration option, setting the EIC clock source which can be used for
|
||||
* EIC edge detection or filtering.
|
||||
*/
|
||||
#define EXTINT_CLOCK_SOURCE GCLK_GENERATOR_0
|
||||
|
||||
/**
|
||||
* External interrupt edge detection configuration enum.
|
||||
*
|
||||
* Enum for the possible signal edge detection modes of the External
|
||||
* Interrupt Controller module.
|
||||
*/
|
||||
enum extint_detect {
|
||||
/** No edge detection. Not allowed as a NMI detection mode on some
|
||||
* devices. */
|
||||
EXTINT_DETECT_NONE = 0,
|
||||
/** Detect rising signal edges. */
|
||||
EXTINT_DETECT_RISING = 1,
|
||||
/** Detect falling signal edges. */
|
||||
EXTINT_DETECT_FALLING = 2,
|
||||
/** Detect both signal edges. */
|
||||
EXTINT_DETECT_BOTH = 3,
|
||||
/** Detect high signal levels. */
|
||||
EXTINT_DETECT_HIGH = 4,
|
||||
/** Detect low signal levels. */
|
||||
EXTINT_DETECT_LOW = 5,
|
||||
};
|
||||
|
||||
/**
|
||||
* External interrupt internal pull configuration enum.
|
||||
*
|
||||
* Enum for the possible pin internal pull configurations.
|
||||
*
|
||||
* \note Disabling the internal pull resistor is not recommended if the driver
|
||||
* is used in interrupt (callback) mode, due the possibility of floating
|
||||
* inputs generating continuous interrupts.
|
||||
*/
|
||||
enum extint_pull {
|
||||
/** Internal pull-up resistor is enabled on the pin. */
|
||||
EXTINT_PULL_UP = SYSTEM_PINMUX_PIN_PULL_UP,
|
||||
/** Internal pull-down resistor is enabled on the pin. */
|
||||
EXTINT_PULL_DOWN = SYSTEM_PINMUX_PIN_PULL_DOWN,
|
||||
/** Internal pull resistor is disconnected from the pin. */
|
||||
EXTINT_PULL_NONE = SYSTEM_PINMUX_PIN_PULL_NONE,
|
||||
};
|
||||
|
||||
/**
|
||||
* External Interrupt Controller channel configuration structure.
|
||||
*
|
||||
* Configuration structure for the edge detection mode of an external
|
||||
* interrupt channel.
|
||||
*/
|
||||
struct extint_chan_conf {
|
||||
/** GPIO pin the NMI should be connected to. */
|
||||
uint32_t gpio_pin;
|
||||
/** MUX position the GPIO pin should be configured to. */
|
||||
uint32_t gpio_pin_mux;
|
||||
/** Internal pull to enable on the input pin. */
|
||||
enum extint_pull gpio_pin_pull;
|
||||
/** Wake up the device if the channel interrupt fires during sleep mode. */
|
||||
bool wake_if_sleeping;
|
||||
/** Filter the raw input signal to prevent noise from triggering an
|
||||
* interrupt accidentally, using a 3 sample majority filter. */
|
||||
bool filter_input_signal;
|
||||
/** Edge detection mode to use. */
|
||||
enum extint_detect detection_criteria;
|
||||
};
|
||||
|
||||
/**
|
||||
* External Interrupt event enable/disable structure.
|
||||
*
|
||||
* Event flags for the \ref extint_enable_events() and
|
||||
* \ref extint_disable_events().
|
||||
*/
|
||||
struct extint_events {
|
||||
/** If \c true, an event will be generated when an external interrupt
|
||||
* channel detection state changes. */
|
||||
bool generate_event_on_detect[32 * EIC_INST_NUM];
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief External Interrupt Controller NMI configuration structure.
|
||||
*
|
||||
* Configuration structure for the edge detection mode of an external
|
||||
* interrupt NMI channel.
|
||||
*/
|
||||
struct extint_nmi_conf {
|
||||
/** GPIO pin the NMI should be connected to. */
|
||||
uint32_t gpio_pin;
|
||||
/** MUX position the GPIO pin should be configured to. */
|
||||
uint32_t gpio_pin_mux;
|
||||
/** Internal pull to enable on the input pin. */
|
||||
enum extint_pull gpio_pin_pull;
|
||||
/** Filter the raw input signal to prevent noise from triggering an
|
||||
* interrupt accidentally, using a 3 sample majority filter. */
|
||||
bool filter_input_signal;
|
||||
/** Edge detection mode to use. Not all devices support all possible
|
||||
* detection modes for NMIs.
|
||||
*/
|
||||
enum extint_detect detection_criteria;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
void system_extint_init(void);
|
||||
void extint_enable(void);
|
||||
void extint_disable(void);
|
||||
|
||||
/**
|
||||
* Retrieves the base EIC module address from a given channel number.
|
||||
*
|
||||
* Retrieves the base address of a EIC hardware module associated with the
|
||||
* given external interrupt channel.
|
||||
*
|
||||
* \param[in] channel External interrupt channel index to convert.
|
||||
*
|
||||
* \return Base address of the associated EIC module.
|
||||
*/
|
||||
static inline Eic * _extint_get_eic_from_channel(
|
||||
const uint8_t channel)
|
||||
{
|
||||
uint8_t eic_index = (channel / 32);
|
||||
|
||||
if (eic_index < EIC_INST_NUM) {
|
||||
/* Array of available EICs. */
|
||||
Eic *const eics[EIC_INST_NUM] = EIC_INSTS;
|
||||
|
||||
return eics[eic_index];
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the base EIC module address from a given NMI channel number.
|
||||
*
|
||||
* Retrieves the base address of a EIC hardware module associated with the
|
||||
* given non-maskable external interrupt channel.
|
||||
*
|
||||
* \param[in] nmi_channel Non-Maskable interrupt channel index to convert.
|
||||
*
|
||||
* \return Base address of the associated EIC module.
|
||||
*/
|
||||
static inline Eic * _extint_get_eic_from_nmi(
|
||||
const uint8_t nmi_channel)
|
||||
{
|
||||
uint8_t eic_index = nmi_channel;
|
||||
|
||||
if (eic_index < EIC_INST_NUM) {
|
||||
/* Array of available EICs. */
|
||||
Eic *const eics[EIC_INST_NUM] = EIC_INSTS;
|
||||
|
||||
return eics[eic_index];
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the hardware module(s) are currently synchronizing to the bus.
|
||||
*
|
||||
* Checks to see if the underlying hardware peripheral module(s) are currently
|
||||
* synchronizing across multiple clock domains to the hardware bus, This
|
||||
* function can be used to delay further operations on a module until such time
|
||||
* that it is ready, to prevent blocking delays for synchronization in the
|
||||
* user application.
|
||||
*
|
||||
* \return Synchronization status of the underlying hardware module(s).
|
||||
*
|
||||
* \retval true If the module has completed synchronization
|
||||
* \retval false If the module synchronization is ongoing
|
||||
*/
|
||||
static inline bool extint_is_syncing(void)
|
||||
{
|
||||
Eic *const eics[EIC_INST_NUM] = EIC_INSTS;
|
||||
|
||||
for (uint32_t i = 0; i < EIC_INST_NUM; i++) {
|
||||
if (eics[i]->STATUS.reg & EIC_STATUS_SYNCBUSY) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void extint_enable_events(
|
||||
struct extint_events *const events);
|
||||
|
||||
void extint_disable_events(
|
||||
struct extint_events *const events);
|
||||
|
||||
/**
|
||||
* Initializes an External Interrupt channel configuration structure to defaults.
|
||||
*
|
||||
* Initializes a given External Interrupt channel configuration structure to a
|
||||
* set of known default values. This function should be called on all new
|
||||
* instances of these configuration structures before being modified by the
|
||||
* user application.
|
||||
*
|
||||
* The default configuration is as follows:
|
||||
* \li Wake the device if an edge detection occurs whilst in sleep
|
||||
* \li Input filtering disabled
|
||||
* \li Internal pull-up enabled
|
||||
* \li Detect falling edges of a signal
|
||||
*
|
||||
* \param[out] config Configuration structure to initialize to default values
|
||||
*/
|
||||
static inline void extint_chan_get_config_defaults(
|
||||
struct extint_chan_conf *const config)
|
||||
{
|
||||
/* Default configuration values */
|
||||
config->gpio_pin = 0;
|
||||
config->gpio_pin_mux = 0;
|
||||
config->gpio_pin_pull = EXTINT_PULL_UP;
|
||||
config->wake_if_sleeping = true;
|
||||
config->filter_input_signal = false;
|
||||
config->detection_criteria = EXTINT_DETECT_FALLING;
|
||||
}
|
||||
|
||||
void extint_chan_set_config(
|
||||
const uint8_t channel,
|
||||
const struct extint_chan_conf *const config);
|
||||
|
||||
/**
|
||||
* Initializes an External Interrupt NMI channel configuration structure to defaults.
|
||||
*
|
||||
* Initializes a given External Interrupt NMI channel configuration structure
|
||||
* to a set of known default values. This function should be called on all new
|
||||
* instances of these configuration structures before being modified by the
|
||||
* user application.
|
||||
*
|
||||
* The default configuration is as follows:
|
||||
* \li Input filtering disabled
|
||||
* \li Detect falling edges of a signal
|
||||
*
|
||||
* \param[out] config Configuration structure to initialize to default values
|
||||
*/
|
||||
static inline void extint_nmi_get_config_defaults(
|
||||
struct extint_nmi_conf *const config)
|
||||
{
|
||||
/* Default configuration values */
|
||||
config->gpio_pin = 0;
|
||||
config->gpio_pin_mux = 0;
|
||||
config->gpio_pin_pull = EXTINT_PULL_UP;
|
||||
config->filter_input_signal = false;
|
||||
config->detection_criteria = EXTINT_DETECT_FALLING;
|
||||
}
|
||||
|
||||
void extint_nmi_set_config(
|
||||
const uint8_t nmi_channel,
|
||||
const struct extint_nmi_conf *const config);
|
||||
|
||||
/**
|
||||
* Retrieves the edge detection state of a configured channel.
|
||||
*
|
||||
* Reads the current state of a configured channel, and determines
|
||||
* if the detection criteria of the channel has been met.
|
||||
*
|
||||
* \param[in] channel External Interrupt channel index to check.
|
||||
*
|
||||
* \return Status of the requested channel's edge detection state.
|
||||
* \retval true If the channel's edge/level detection criteria was met
|
||||
* \retval false If the channel has not detected its configured criteria
|
||||
*/
|
||||
static inline bool extint_chan_is_detected(
|
||||
const uint8_t channel)
|
||||
{
|
||||
Eic *const eic_module = _extint_get_eic_from_channel(channel);
|
||||
uint32_t eic_mask = (1UL << (channel % 32));
|
||||
|
||||
return (eic_module->INTFLAG.reg & eic_mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the edge detection state of a configured channel.
|
||||
*
|
||||
* Clears the current state of a configured channel, readying it for
|
||||
* the next level or edge detection.
|
||||
*
|
||||
* \param[in] channel External Interrupt channel index to check.
|
||||
*/
|
||||
static inline void extint_chan_clear_detected(
|
||||
const uint8_t channel)
|
||||
{
|
||||
Eic *const eic_module = _extint_get_eic_from_channel(channel);
|
||||
uint32_t eic_mask = (1UL << (channel % 32));
|
||||
|
||||
eic_module->INTFLAG.reg = eic_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the edge detection state of a configured NMI channel.
|
||||
*
|
||||
* Reads the current state of a configured NMI channel, and determines
|
||||
* if the detection criteria of the NMI channel has been met.
|
||||
*
|
||||
* \param[in] nmi_channel External Interrupt NMI channel index to check.
|
||||
*
|
||||
* \return Status of the requested NMI channel's edge detection state.
|
||||
* \retval true If the NMI channel's edge/level detection criteria was met
|
||||
* \retval false If the NMI channel has not detected its configured criteria
|
||||
*/
|
||||
static inline bool extint_nmi_is_detected(
|
||||
const uint8_t nmi_channel)
|
||||
{
|
||||
Eic *const eic_module = _extint_get_eic_from_nmi(nmi_channel);
|
||||
|
||||
return (eic_module->NMIFLAG.reg & EIC_NMIFLAG_NMI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the edge detection state of a configured NMI channel.
|
||||
*
|
||||
* Clears the current state of a configured NMI channel, readying it for
|
||||
* the next level or edge detection.
|
||||
*
|
||||
* \param[in] nmi_channel External Interrupt NMI channel index to check.
|
||||
*/
|
||||
static inline void extint_nmi_clear_detected(
|
||||
const uint8_t nmi_channel)
|
||||
{
|
||||
Eic *const eic_module = _extint_get_eic_from_nmi(nmi_channel);
|
||||
|
||||
eic_module->NMIFLAG.reg = EIC_NMIFLAG_NMI;
|
||||
}
|
||||
|
||||
#endif /* EXTINT_H_INCLUDED */
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Functions for controlling and calibrating against the external oscillator
|
||||
* Copyright (C) 2014 Richard Meadows <richardeoin>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef XOSC_H
|
||||
#define XOSC_H
|
||||
|
||||
enum xosc_measurement_t {
|
||||
XOSC_MEASURE_OSC8M,
|
||||
XOSC_MEASURE_TIMEPULSE,
|
||||
};
|
||||
|
||||
void xosc_init(void);
|
||||
|
||||
void measure_xosc(enum xosc_measurement_t measurement_t);
|
||||
|
||||
#endif /* XOSC_H */
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* SAM D20/D21/R21 Event System Controller Driver
|
||||
*
|
||||
* Copyright (C) 2013-2014 Atmel Corporation. All rights reserved.
|
||||
*
|
||||
* \asf_license_start
|
||||
*
|
||||
* \page License
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of Atmel may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 4. This software may only be redistributed and used in connection with an
|
||||
* Atmel microcontroller product.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
|
||||
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* \asf_license_stop
|
||||
*
|
||||
*/
|
||||
|
||||
#include "system/events.h"
|
||||
#include "system/system.h"
|
||||
#include "samd20.h"
|
||||
|
||||
#define EVENTS_INVALID_CHANNEL 0xff
|
||||
#define _EVENTS_START_OFFSET_BUSY_BITS 8
|
||||
#define _EVENTS_START_OFFSET_USER_READY_BIT 0
|
||||
#define _EVENTS_START_OFFSET_DETECTION_BIT 8
|
||||
#define _EVENTS_START_OFFSET_OVERRUN_BIT 0
|
||||
|
||||
/**
|
||||
* internal
|
||||
*
|
||||
*/
|
||||
uint32_t _events_find_bit_position(uint8_t channel, uint8_t start_offset)
|
||||
{
|
||||
uint8_t byte_offset = channel >> 3;
|
||||
uint32_t pos;
|
||||
|
||||
pos = (((channel % 8) + 1) << start_offset) * ((0xffff * byte_offset) + 1);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
void system_events_init(void)
|
||||
{
|
||||
/* Enable EVSYS register interface */
|
||||
system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, PM_APBCMASK_EVSYS);
|
||||
|
||||
/* Make sure the EVSYS module is properly reset */
|
||||
EVSYS->CTRL.reg = EVSYS_CTRL_SWRST;
|
||||
|
||||
while (EVSYS->CTRL.reg & EVSYS_CTRL_SWRST);
|
||||
}
|
||||
|
||||
|
||||
enum status_code events_allocate(uint8_t channel,
|
||||
enum events_edge_detect edge_detect, /** edge detection mode */
|
||||
enum events_path_selection path, /** events channel path */
|
||||
uint8_t generator, /** event generator for the channel */
|
||||
uint8_t clock_source) /** clock source for the event channel */
|
||||
{
|
||||
if (path != EVENTS_PATH_ASYNCHRONOUS) {
|
||||
/* Set up a GLCK channel to use with the specific channel */
|
||||
system_gclk_chan_set_config(EVSYS_GCLK_ID_0 + channel, (enum gclk_generator)clock_source);
|
||||
system_gclk_chan_enable(EVSYS_GCLK_ID_0 + channel);
|
||||
}
|
||||
|
||||
EVSYS->CHANNEL.reg = EVSYS_CHANNEL_CHANNEL(channel) |
|
||||
EVSYS_CHANNEL_EVGEN(generator) |
|
||||
EVSYS_CHANNEL_PATH(path) |
|
||||
EVSYS_CHANNEL_EDGSEL(edge_detect);
|
||||
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
enum status_code events_trigger(uint8_t channel)
|
||||
{
|
||||
/* Because of indirect access the channel must be set first */
|
||||
((uint8_t*)&EVSYS->CHANNEL)[0] = EVSYS_CHANNEL_CHANNEL(channel);
|
||||
|
||||
/* Assert if event path is asynchronous */
|
||||
if (EVSYS->CHANNEL.reg & EVSYS_CHANNEL_PATH(EVENTS_PATH_ASYNCHRONOUS)) {
|
||||
return STATUS_ERR_DENIED;
|
||||
}
|
||||
|
||||
/* Assert if event edge detection is not set to RISING */
|
||||
if (!(EVSYS->CHANNEL.reg & EVSYS_CHANNEL_EDGSEL(EVENTS_EDGE_DETECT_RISING))) {
|
||||
return STATUS_ERR_DENIED;
|
||||
}
|
||||
|
||||
|
||||
/* The GCLKREQ bit has to be set while triggering the software event */
|
||||
EVSYS->CTRL.reg = EVSYS_CTRL_GCLKREQ;
|
||||
|
||||
((uint16_t*)&EVSYS->CHANNEL)[0] = EVSYS_CHANNEL_CHANNEL(channel) |
|
||||
EVSYS_CHANNEL_SWEVT;
|
||||
|
||||
EVSYS->CTRL.reg &= ~EVSYS_CTRL_GCLKREQ;
|
||||
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
bool events_is_busy(uint8_t channel)
|
||||
{
|
||||
return EVSYS->CHSTATUS.reg & (_events_find_bit_position(channel, _EVENTS_START_OFFSET_BUSY_BITS));
|
||||
}
|
||||
|
||||
bool events_is_users_ready(uint8_t channel)
|
||||
{
|
||||
return EVSYS->CHSTATUS.reg & (_events_find_bit_position(channel, _EVENTS_START_OFFSET_USER_READY_BIT));
|
||||
}
|
||||
|
||||
bool events_is_detected(uint8_t channel)
|
||||
{
|
||||
uint32_t flag = _events_find_bit_position(channel, _EVENTS_START_OFFSET_DETECTION_BIT);
|
||||
|
||||
/* Clear flag when read */
|
||||
if (EVSYS->INTFLAG.reg & flag) {
|
||||
EVSYS->INTFLAG.reg = flag;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool events_is_overrun(uint8_t channel)
|
||||
{
|
||||
uint32_t flag = _events_find_bit_position(channel, _EVENTS_START_OFFSET_OVERRUN_BIT);
|
||||
|
||||
/* Clear flag when read */
|
||||
if (EVSYS->INTFLAG.reg & flag) {
|
||||
EVSYS->INTFLAG.reg = flag;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
enum status_code events_attach_user(uint8_t channel, uint8_t user_id)
|
||||
{
|
||||
/* Channel number is n + 1 */
|
||||
EVSYS->USER.reg = EVSYS_USER_CHANNEL(channel + 1) |
|
||||
EVSYS_USER_USER(user_id);
|
||||
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
enum status_code events_detach_user(uint8_t channel, uint8_t user_id)
|
||||
{
|
||||
/* Write 0 to the channel bit field to select no input */
|
||||
EVSYS->USER.reg = EVSYS_USER_USER(user_id);
|
||||
|
||||
return STATUS_OK;
|
||||
}
|
|
@ -0,0 +1,311 @@
|
|||
/**
|
||||
* SAM D20/D21/R21 External Interrupt Driver
|
||||
*
|
||||
* Copyright (C) 2012-2014 Atmel Corporation. All rights reserved.
|
||||
*
|
||||
* \asf_license_start
|
||||
*
|
||||
* \page License
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of Atmel may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 4. This software may only be redistributed and used in connection with an
|
||||
* Atmel microcontroller product.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
|
||||
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* \asf_license_stop
|
||||
*
|
||||
*/
|
||||
|
||||
#include "system/extint.h"
|
||||
#include "system/system.h"
|
||||
#include "system/interrupt.h"
|
||||
#include "system/gclk.h"
|
||||
#include "system/pinmux.h"
|
||||
|
||||
/**
|
||||
* \brief Determin if the general clock is required
|
||||
*
|
||||
* \param[in] filter_input_signal Filter the raw input signal to prevent noise
|
||||
* \param[in] detection_criteria Edge detection mode to use (\ref extint_detect)
|
||||
*/
|
||||
#define _extint_is_gclk_required(filter_input_signal, detection_criteria) \
|
||||
((filter_input_signal) ? true : (\
|
||||
(EXTINT_DETECT_RISING == (detection_criteria)) ? true : (\
|
||||
(EXTINT_DETECT_FALLING == (detection_criteria)) ? true : (\
|
||||
(EXTINT_DETECT_BOTH == (detection_criteria)) ? true : false))))
|
||||
|
||||
/**
|
||||
* Initializes and enables the External Interrupt driver.
|
||||
*
|
||||
* Enable the clocks used by External Interrupt driver.
|
||||
*
|
||||
* Resets the External Interrupt driver, resetting all hardware
|
||||
* module registers to their power-on defaults, then enable it for further use.
|
||||
*
|
||||
* Reset the callback list if callback mode is used.
|
||||
*
|
||||
* This function must be called before attempting to use any NMI or standard
|
||||
* external interrupt channel functions.
|
||||
*
|
||||
* \note When SYSTEM module is used, this function will be invoked by
|
||||
* \ref system_init() automatically if the module is included.
|
||||
*/
|
||||
void system_extint_init(void)
|
||||
{
|
||||
Eic *const eics[EIC_INST_NUM] = EIC_INSTS;
|
||||
|
||||
/* Turn on the digital interface clock */
|
||||
system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBA, PM_APBAMASK_EIC);
|
||||
|
||||
/* Configure the generic clock for the module and enable it */
|
||||
system_gclk_chan_set_config(EIC_GCLK_ID, EXTINT_CLOCK_SOURCE);
|
||||
|
||||
/* Enable the clock anyway, since when needed it will be requested
|
||||
* by External Interrupt driver */
|
||||
system_gclk_chan_enable(EIC_GCLK_ID);
|
||||
|
||||
/* Reset all EIC hardware modules. */
|
||||
for (uint32_t i = 0; i < EIC_INST_NUM; i++) {
|
||||
eics[i]->CTRL.reg |= EIC_CTRL_SWRST;
|
||||
}
|
||||
|
||||
while (extint_is_syncing()) {
|
||||
/* Wait for all hardware modules to complete synchronization */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the External Interrupt driver.
|
||||
*
|
||||
* Enables EIC modules.
|
||||
* Registered callback list will not be affected if callback mode is used.
|
||||
*/
|
||||
void extint_enable(void)
|
||||
{
|
||||
Eic *const eics[EIC_INST_NUM] = EIC_INSTS;
|
||||
|
||||
/* Enable all EIC hardware modules. */
|
||||
for (uint32_t i = 0; i < EIC_INST_NUM; i++) {
|
||||
eics[i]->CTRL.reg |= EIC_CTRL_ENABLE;
|
||||
}
|
||||
|
||||
while (extint_is_syncing()) {
|
||||
/* Wait for all hardware modules to complete synchronization */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the External Interrupt driver.
|
||||
*
|
||||
* Disables EIC modules that were previously started via a call to
|
||||
* \ref _extint_enable().
|
||||
* Registered callback list will not be affected if callback mode is used.
|
||||
*/
|
||||
void extint_disable(void)
|
||||
{
|
||||
Eic *const eics[EIC_INST_NUM] = EIC_INSTS;
|
||||
|
||||
/* Disable all EIC hardware modules. */
|
||||
for (uint32_t i = 0; i < EIC_INST_NUM; i++) {
|
||||
eics[i]->CTRL.reg &= ~EIC_CTRL_ENABLE;
|
||||
}
|
||||
|
||||
while (extint_is_syncing()) {
|
||||
/* Wait for all hardware modules to complete synchronization */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an External Interrupt channel configuration to the hardware module.
|
||||
*
|
||||
* Writes out a given configuration of an External Interrupt channel
|
||||
* configuration to the hardware module. If the channel is already configured,
|
||||
* the new configuration will replace the existing one.
|
||||
*
|
||||
* \param[in] channel External Interrupt channel to configure
|
||||
* \param[in] config Configuration settings for the channel
|
||||
|
||||
*/
|
||||
void extint_chan_set_config(
|
||||
const uint8_t channel,
|
||||
const struct extint_chan_conf *const config)
|
||||
{
|
||||
/* Sanity check clock requirements */
|
||||
// Assert(!(!system_gclk_gen_is_enabled(EXTINT_CLOCK_SOURCE) &&
|
||||
// _extint_is_gclk_required(config->filter_input_signal,
|
||||
// config->detection_criteria)));
|
||||
|
||||
system_pinmux_pin_set_config(config->gpio_pin,
|
||||
config->gpio_pin_mux,
|
||||
SYSTEM_PINMUX_PIN_DIR_INPUT,
|
||||
(enum system_pinmux_pin_pull)config->gpio_pin_pull,
|
||||
false);
|
||||
|
||||
/* Get a pointer to the module hardware instance */
|
||||
Eic *const EIC_module = _extint_get_eic_from_channel(channel);
|
||||
|
||||
uint32_t config_pos = (4 * (channel % 8));
|
||||
uint32_t new_config;
|
||||
|
||||
/* Determine the channel's new edge detection configuration */
|
||||
new_config = (config->detection_criteria << EIC_CONFIG_SENSE0_Pos);
|
||||
|
||||
/* Enable the hardware signal filter if requested in the config */
|
||||
if (config->filter_input_signal) {
|
||||
new_config |= EIC_CONFIG_FILTEN0;
|
||||
}
|
||||
|
||||
/* Clear the existing and set the new channel configuration */
|
||||
EIC_module->CONFIG[channel / 8].reg
|
||||
= (EIC_module->CONFIG[channel / 8].reg &
|
||||
~((EIC_CONFIG_SENSE0_Msk | EIC_CONFIG_FILTEN0) << config_pos)) |
|
||||
(new_config << config_pos);
|
||||
|
||||
/* Set the channel's new wake up mode setting */
|
||||
if (config->wake_if_sleeping) {
|
||||
EIC_module->WAKEUP.reg |= (1UL << channel);
|
||||
} else {
|
||||
EIC_module->WAKEUP.reg &= ~(1UL << channel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an External Interrupt NMI channel configuration to the hardware module.
|
||||
*
|
||||
* Writes out a given configuration of an External Interrupt NMI channel
|
||||
* configuration to the hardware module. If the channel is already configured,
|
||||
* the new configuration will replace the existing one.
|
||||
*
|
||||
* \param[in] nmi_channel External Interrupt NMI channel to configure
|
||||
* \param[in] config Configuration settings for the channel
|
||||
*
|
||||
* \returns Status code indicating the success or failure of the request.
|
||||
* \retval STATUS_OK Configuration succeeded
|
||||
* \retval STATUS_ERR_PIN_MUX_INVALID An invalid pin mux value was supplied
|
||||
* \retval STATUS_ERR_BAD_FORMAT An invalid detection mode was requested
|
||||
*/
|
||||
void extint_nmi_set_config(
|
||||
const uint8_t nmi_channel,
|
||||
const struct extint_nmi_conf *const config)
|
||||
{
|
||||
/* Sanity check clock requirements */
|
||||
// Assert(!(!system_gclk_gen_is_enabled(EXTINT_CLOCK_SOURCE) &&
|
||||
// _extint_is_gclk_required(config->filter_input_signal,
|
||||
// config->detection_criteria)));
|
||||
|
||||
system_pinmux_pin_set_config(config->gpio_pin,
|
||||
config->gpio_pin_mux,
|
||||
SYSTEM_PINMUX_PIN_DIR_INPUT,
|
||||
(enum system_pinmux_pin_pull)config->gpio_pin_pull,
|
||||
false);
|
||||
|
||||
/* Get a pointer to the module hardware instance */
|
||||
Eic *const EIC_module = _extint_get_eic_from_channel(nmi_channel);
|
||||
|
||||
uint32_t new_config;
|
||||
|
||||
/* Determine the NMI's new edge detection configuration */
|
||||
new_config = (config->detection_criteria << EIC_NMICTRL_NMISENSE_Pos);
|
||||
|
||||
/* Enable the hardware signal filter if requested in the config */
|
||||
if (config->filter_input_signal) {
|
||||
new_config |= EIC_NMICTRL_NMIFILTEN;
|
||||
}
|
||||
|
||||
/* Disable EIC and general clock to configure NMI */
|
||||
extint_disable();
|
||||
system_gclk_chan_disable(EIC_GCLK_ID);
|
||||
|
||||
EIC_module->NMICTRL.reg = new_config;
|
||||
|
||||
/* Enable the general clock and EIC after configure NMI */
|
||||
system_gclk_chan_enable(EIC_GCLK_ID);
|
||||
extint_enable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables an External Interrupt event output.
|
||||
*
|
||||
* Enables one or more output events from the External Interrupt module. See
|
||||
* \ref extint_events "here" for a list of events this module supports.
|
||||
*
|
||||
* \note Events cannot be altered while the module is enabled.
|
||||
*
|
||||
* \param[in] events Struct containing flags of events to enable
|
||||
*/
|
||||
void extint_enable_events(
|
||||
struct extint_events *const events)
|
||||
{
|
||||
/* Array of available EICs. */
|
||||
Eic *const eics[EIC_INST_NUM] = EIC_INSTS;
|
||||
|
||||
/* Update the event control register for each physical EIC instance */
|
||||
for (uint32_t i = 0; i < EIC_INST_NUM; i++) {
|
||||
uint32_t event_mask = 0;
|
||||
|
||||
/* Create an enable mask for the current EIC module */
|
||||
for (uint32_t j = 0; j < 32; j++) {
|
||||
if (events->generate_event_on_detect[(32 * i) + j]) {
|
||||
event_mask |= (1UL << j);
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable the masked events */
|
||||
eics[i]->EVCTRL.reg |= event_mask;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables an External Interrupt event output.
|
||||
*
|
||||
* Disables one or more output events from the External Interrupt module. See
|
||||
* \ref extint_events "here" for a list of events this module supports.
|
||||
*
|
||||
* \note Events cannot be altered while the module is enabled.
|
||||
*
|
||||
* \param[in] events Struct containing flags of events to disable
|
||||
*/
|
||||
void extint_disable_events(
|
||||
struct extint_events *const events)
|
||||
{
|
||||
/* Array of available EICs. */
|
||||
Eic *const eics[EIC_INST_NUM] = EIC_INSTS;
|
||||
|
||||
/* Update the event control register for each physical EIC instance */
|
||||
for (uint32_t i = 0; i < EIC_INST_NUM; i++) {
|
||||
uint32_t event_mask = 0;
|
||||
|
||||
/* Create a disable mask for the current EIC module */
|
||||
for (uint32_t j = 0; j < 32; j++) {
|
||||
if (events->generate_event_on_detect[(32 * i) + j]) {
|
||||
event_mask |= (1UL << j);
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable the masked events */
|
||||
eics[i]->EVCTRL.reg &= ~event_mask;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,266 @@
|
|||
/*
|
||||
* Functions for controlling and calibrating against the external oscillator
|
||||
* Copyright (C) 2015 Richard Meadows <richardeoin>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "samd20.h"
|
||||
#include "system/clock.h"
|
||||
#include "system/gclk.h"
|
||||
#include "system/interrupt.h"
|
||||
#include "system/pinmux.h"
|
||||
#include "system/events.h"
|
||||
#include "system/extint.h"
|
||||
#include "tc/tc_driver.h"
|
||||
#include "hw_config.h"
|
||||
#include "xosc.h"
|
||||
|
||||
|
||||
enum measure_state_t {
|
||||
MEASURE_WAIT_FOR_FIRST_EVENT,
|
||||
MEASURE_MEASUREMENT,
|
||||
} measure_state = MEASURE_WAIT_FOR_FIRST_EVENT;
|
||||
enum xosc_measurement_t _measurement_t;
|
||||
|
||||
/**
|
||||
* Configures external oscillator, waits for it to stabilise
|
||||
*/
|
||||
void xosc_init(void) {
|
||||
system_clock_source_xosc_set_config(SYSTEM_CLOCK_EXTERNAL_CLOCK,
|
||||
SYSTEM_XOSC_STARTUP_1,
|
||||
true,
|
||||
XOSC_FREQUENCY,
|
||||
false,
|
||||
false);
|
||||
system_clock_source_enable(SYSTEM_CLOCK_SOURCE_XOSC);
|
||||
|
||||
while (!system_clock_source_is_ready(SYSTEM_CLOCK_SOURCE_XOSC));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Configure timer 4 to generate events at 1Hz of OSC8M
|
||||
*/
|
||||
void osc8m_event_source(void) {
|
||||
|
||||
/* Timer 4 runs on GCLK0 (4MHz) */
|
||||
bool t4_capture_channel_enables[] = {false, false};
|
||||
uint32_t t4_compare_channel_values[] = {15625, 0x0000};
|
||||
/* Divide by 256*15625 = 1Hz events */
|
||||
tc_init(TC4,
|
||||
GCLK_GENERATOR_0,
|
||||
TC_COUNTER_SIZE_16BIT,
|
||||
TC_CLOCK_PRESCALER_DIV256,
|
||||
TC_WAVE_GENERATION_NORMAL_FREQ,
|
||||
TC_RELOAD_ACTION_GCLK,
|
||||
TC_COUNT_DIRECTION_UP,
|
||||
TC_WAVEFORM_INVERT_OUTPUT_NONE,
|
||||
false, /* Oneshot */
|
||||
false, /* Run in standby */
|
||||
0x0000, /* Initial value */
|
||||
0xFFFF, /* Top value */
|
||||
t4_capture_channel_enables, /* Capture Channel Enables */
|
||||
t4_compare_channel_values); /* Compare Channels Values */
|
||||
|
||||
/* Timer 4 generates an event on compare channel 0 */
|
||||
struct tc_events events;
|
||||
events.generate_event_on_compare_channel[0] = true;
|
||||
events.generate_event_on_compare_channel[1] = false;
|
||||
events.generate_event_on_overflow = false;
|
||||
events.invert_event_input = false;
|
||||
events.on_event_perform_action = true;
|
||||
events.event_action = TC_EVENT_ACTION_RETRIGGER;
|
||||
tc_enable_events(TC4, &events);
|
||||
|
||||
events_attach_user(0, 4); // Timer 4 is event user on channel 1
|
||||
|
||||
/* This event is picked up on event channel 0 */
|
||||
events_allocate(0,
|
||||
EVENTS_EDGE_DETECT_NONE,
|
||||
EVENTS_PATH_ASYNCHRONOUS,
|
||||
0x29, /* TC4 MC0 */
|
||||
0);
|
||||
|
||||
/* This event is picked up on event channel 1 */
|
||||
events_allocate(1,
|
||||
EVENTS_EDGE_DETECT_NONE,
|
||||
EVENTS_PATH_ASYNCHRONOUS,
|
||||
0x29, /* TC4 MC0 */
|
||||
0);
|
||||
|
||||
tc_enable(TC4); /* Retrigger event means counter doesn't start yet */
|
||||
tc_start_counter(TC4); /* We start it manually now */
|
||||
}
|
||||
void osc8m_event_source_disable(void) {
|
||||
tc_disable(TC4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the timepulse extint to generate events
|
||||
*/
|
||||
void timepulse_extint_event_source(void) {
|
||||
|
||||
/* Enable extint events for gps timepulse */
|
||||
struct extint_events events;
|
||||
memset(&events, 0, sizeof(struct extint_events));
|
||||
events.generate_event_on_detect[GPS_TIMEPULSE_EXTINT] = true;
|
||||
extint_enable_events(&events);
|
||||
|
||||
/* Configure extinit channel */
|
||||
struct extint_chan_conf config;
|
||||
config.gpio_pin = GPS_TIMEPULSE_PIN;
|
||||
config.gpio_pin_mux = GPS_TIMEPULSE_PINMUX;
|
||||
config.gpio_pin_pull = EXTINT_PULL_NONE; // ???
|
||||
config.wake_if_sleeping = false; // ???
|
||||
config.filter_input_signal = false;
|
||||
config.detection_criteria = EXTINT_DETECT_RISING;
|
||||
extint_chan_set_config(GPS_TIMEPULSE_EXTINT, &config);
|
||||
|
||||
/* We route this event to event channel 0 */
|
||||
events_allocate(0,
|
||||
EVENTS_EDGE_DETECT_NONE,
|
||||
EVENTS_PATH_ASYNCHRONOUS,
|
||||
0x11, /* External Interrupt 5 */
|
||||
0);
|
||||
|
||||
extint_enable();
|
||||
}
|
||||
void timepulse_extint_event_source_disable(void) {
|
||||
// Oh I don't know
|
||||
}
|
||||
|
||||
void EIC_Handler(void) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a measurements the number of cycles on XOSC
|
||||
*/
|
||||
void measure_xosc(enum xosc_measurement_t measurement_t) {
|
||||
|
||||
measure_state = MEASURE_WAIT_FOR_FIRST_EVENT;
|
||||
_measurement_t = measurement_t;
|
||||
|
||||
/* Configure GCLK1 to XOSC */
|
||||
system_gclk_gen_set_config(GCLK_GENERATOR_1,
|
||||
GCLK_SOURCE_XOSC, /* Source */
|
||||
false, /* High When Disabled */
|
||||
XOSC_COUNT_RESOLUTION,/* Division Factor */
|
||||
false, /* Run in standby */
|
||||
false); /* Output Pin Enable */
|
||||
|
||||
|
||||
/* Enable GCLK1 */
|
||||
system_gclk_gen_enable(GCLK_GENERATOR_1);
|
||||
|
||||
/* Timer 2 runs on GLCK1 */
|
||||
bool t2_capture_channel_enables[] = {true, true};
|
||||
uint32_t t2_compare_channel_values[] = {0x0000, 0x0000};
|
||||
|
||||
tc_init(TC2,
|
||||
GCLK_GENERATOR_1,
|
||||
TC_COUNTER_SIZE_32BIT,
|
||||
TC_CLOCK_PRESCALER_DIV1,
|
||||
TC_WAVE_GENERATION_NORMAL_FREQ,
|
||||
TC_RELOAD_ACTION_GCLK,
|
||||
TC_COUNT_DIRECTION_UP,
|
||||
TC_WAVEFORM_INVERT_OUTPUT_NONE,
|
||||
false, /* Oneshot */
|
||||
false, /* Run in standby */
|
||||
0x0000, /* Initial value */
|
||||
0xFFFFFFFF, /* Top value */
|
||||
t2_capture_channel_enables, /* Capture Channel Enables */
|
||||
t2_compare_channel_values); /* Compare Channels Values */
|
||||
|
||||
/* Timer 2 event input captures period in CC0, pulse width in CC1 */
|
||||
struct tc_events events;
|
||||
events.generate_event_on_compare_channel[0] = false;
|
||||
events.generate_event_on_compare_channel[1] = false;
|
||||
events.generate_event_on_overflow = false;
|
||||
events.invert_event_input = false;
|
||||
events.on_event_perform_action = true;
|
||||
events.event_action = TC_EVENT_ACTION_PPW;
|
||||
tc_enable_events(TC2, &events);
|
||||
|
||||
/* Enable Interrupt */
|
||||
TC2->COUNT32.INTENSET.reg = (1 << 4); // MC0
|
||||
irq_register_handler(TC2_IRQn, 2); /* Lowish Priority */
|
||||
|
||||
/* Timer 2 is event user on channel 0 */
|
||||
events_attach_user(0, 2);
|
||||
|
||||
/* Configure an event source */
|
||||
switch (measurement_t) {
|
||||
case XOSC_MEASURE_OSC8M:
|
||||
osc8m_event_source(); // osc8m issues events
|
||||
break;
|
||||
case XOSC_MEASURE_TIMEPULSE:
|
||||
timepulse_extint_event_source(); // timepulse issues events
|
||||
break;
|
||||
}
|
||||
|
||||
tc_enable(TC2);
|
||||
}
|
||||
void measure_xosc_disable(enum xosc_measurement_t measurement_t) {
|
||||
|
||||
tc_disable(TC2);
|
||||
|
||||
switch (measurement_t) {
|
||||
case XOSC_MEASURE_OSC8M:
|
||||
osc8m_event_source_disable();
|
||||
break;
|
||||
case XOSC_MEASURE_TIMEPULSE:
|
||||
timepulse_extint_event_source_disable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Triggered on timer 2 capture
|
||||
*/
|
||||
void TC2_Handler(void) {
|
||||
uint32_t capture_value;
|
||||
float frequency;
|
||||
|
||||
if (tc_get_status(TC2) & TC_STATUS_CHANNEL_0_MATCH) {
|
||||
tc_clear_status(TC2, TC_STATUS_CHANNEL_0_MATCH);
|
||||
|
||||
switch (measure_state) {
|
||||
case MEASURE_WAIT_FOR_FIRST_EVENT:
|
||||
measure_state = MEASURE_MEASUREMENT; /* Start measurement */
|
||||
break;
|
||||
case MEASURE_MEASUREMENT:
|
||||
/* Measurement done. Read off data */
|
||||
capture_value = tc_get_capture_value(TC2, 0);
|
||||
|
||||
frequency = capture_value * XOSC_COUNT_RESOLUTION;
|
||||
|
||||
/* Disable measurement system */
|
||||
measure_xosc_disable(_measurement_t);
|
||||
}
|
||||
}
|
||||
}
|
Ładowanie…
Reference in New Issue