From f3ed51dbec5c187697e98544ffdd319f33cb7392 Mon Sep 17 00:00:00 2001 From: Richard Meadows Date: Fri, 13 Mar 2015 11:39:43 +0000 Subject: [PATCH] Added files from previous commits :( --- firmware/inc/system/events.h | 331 ++++++++++++++++++++++++++ firmware/inc/system/extint.h | 437 +++++++++++++++++++++++++++++++++++ firmware/inc/xosc.h | 37 +++ firmware/src/system/events.c | 177 ++++++++++++++ firmware/src/system/extint.c | 311 +++++++++++++++++++++++++ firmware/src/xosc.c | 266 +++++++++++++++++++++ 6 files changed, 1559 insertions(+) create mode 100644 firmware/inc/system/events.h create mode 100644 firmware/inc/system/extint.h create mode 100644 firmware/inc/xosc.h create mode 100644 firmware/src/system/events.c create mode 100644 firmware/src/system/extint.c create mode 100644 firmware/src/xosc.c diff --git a/firmware/inc/system/events.h b/firmware/inc/system/events.h new file mode 100644 index 0000000..6514ece --- /dev/null +++ b/firmware/inc/system/events.h @@ -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 */ diff --git a/firmware/inc/system/extint.h b/firmware/inc/system/extint.h new file mode 100644 index 0000000..9d8efea --- /dev/null +++ b/firmware/inc/system/extint.h @@ -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 */ diff --git a/firmware/inc/xosc.h b/firmware/inc/xosc.h new file mode 100644 index 0000000..3f4dcdb --- /dev/null +++ b/firmware/inc/xosc.h @@ -0,0 +1,37 @@ +/* + * Functions for controlling and calibrating against the external oscillator + * Copyright (C) 2014 Richard Meadows + * + * 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 */ diff --git a/firmware/src/system/events.c b/firmware/src/system/events.c new file mode 100644 index 0000000..3825f02 --- /dev/null +++ b/firmware/src/system/events.c @@ -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; +} diff --git a/firmware/src/system/extint.c b/firmware/src/system/extint.c new file mode 100644 index 0000000..ad48aeb --- /dev/null +++ b/firmware/src/system/extint.c @@ -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; + } +} diff --git a/firmware/src/xosc.c b/firmware/src/xosc.c new file mode 100644 index 0000000..c4ed249 --- /dev/null +++ b/firmware/src/xosc.c @@ -0,0 +1,266 @@ +/* + * Functions for controlling and calibrating against the external oscillator + * Copyright (C) 2015 Richard Meadows + * + * 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 + +#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); + } + } +}