From ccb309bd5c0b283ea847b18621038bf5ef817f38 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 30 Aug 2016 13:10:52 -0700 Subject: [PATCH] atmel/samd: Add basic PWM support which only allows for changing the duty. --- atmel-samd/Makefile | 4 + .../asf/sam0/drivers/tc/docimg/overview.svg | 434 +++ .../sam0/drivers/tc/docimg/pwm_match_ex.svg | 431 +++ .../sam0/drivers/tc/docimg/pwm_normal_ex.svg | 386 +++ atmel-samd/asf/sam0/drivers/tc/tc.h | 1790 ++++++++++++ atmel-samd/asf/sam0/drivers/tc/tc_interrupt.c | 199 ++ atmel-samd/asf/sam0/drivers/tc/tc_interrupt.h | 179 ++ .../asf/sam0/drivers/tc/tc_sam_d_r/tc.c | 685 +++++ .../asf/sam0/drivers/tcc/docimg/overview.svg | 2278 +++++++++++++++ .../sam0/drivers/tcc/docimg/pwm_match_ex.svg | 1464 ++++++++++ .../sam0/drivers/tcc/docimg/pwm_single_ex.svg | 1387 +++++++++ atmel-samd/asf/sam0/drivers/tcc/tcc.c | 1604 +++++++++++ atmel-samd/asf/sam0/drivers/tcc/tcc.h | 2494 +++++++++++++++++ .../asf/sam0/drivers/tcc/tcc_callback.c | 245 ++ .../asf/sam0/drivers/tcc/tcc_callback.h | 93 + atmel-samd/boards/arduino_zero/pins.c | 69 +- atmel-samd/boards/feather_m0_ble/pins.c | 70 +- atmel-samd/modmachine.c | 2 + atmel-samd/modmachine_pin.h | 43 +- atmel-samd/modmachine_pwm.c | 192 ++ atmel-samd/modmachine_pwm.h | 27 + 21 files changed, 14036 insertions(+), 40 deletions(-) create mode 100644 atmel-samd/asf/sam0/drivers/tc/docimg/overview.svg create mode 100644 atmel-samd/asf/sam0/drivers/tc/docimg/pwm_match_ex.svg create mode 100644 atmel-samd/asf/sam0/drivers/tc/docimg/pwm_normal_ex.svg create mode 100644 atmel-samd/asf/sam0/drivers/tc/tc.h create mode 100644 atmel-samd/asf/sam0/drivers/tc/tc_interrupt.c create mode 100644 atmel-samd/asf/sam0/drivers/tc/tc_interrupt.h create mode 100644 atmel-samd/asf/sam0/drivers/tc/tc_sam_d_r/tc.c create mode 100644 atmel-samd/asf/sam0/drivers/tcc/docimg/overview.svg create mode 100644 atmel-samd/asf/sam0/drivers/tcc/docimg/pwm_match_ex.svg create mode 100644 atmel-samd/asf/sam0/drivers/tcc/docimg/pwm_single_ex.svg create mode 100644 atmel-samd/asf/sam0/drivers/tcc/tcc.c create mode 100644 atmel-samd/asf/sam0/drivers/tcc/tcc.h create mode 100644 atmel-samd/asf/sam0/drivers/tcc/tcc_callback.c create mode 100644 atmel-samd/asf/sam0/drivers/tcc/tcc_callback.h create mode 100644 atmel-samd/modmachine_pwm.c create mode 100644 atmel-samd/modmachine_pwm.h diff --git a/atmel-samd/Makefile b/atmel-samd/Makefile index 6ce1d0d1b3..9e02d7f73e 100644 --- a/atmel-samd/Makefile +++ b/atmel-samd/Makefile @@ -58,6 +58,7 @@ INC += $(addprefix -Iasf/sam0/,\ drivers/system/power/power_sam_d_r \ drivers/system/reset/reset_sam_d_r \ drivers/sercom/ \ + drivers/tc/ \ drivers/usb/ \ utils \ utils/cmsis/samd21/include \ @@ -107,6 +108,8 @@ SRC_ASF = $(addprefix asf/sam0/,\ drivers/system/interrupt/system_interrupt.c \ drivers/system/pinmux/pinmux.c \ drivers/system/system.c \ + drivers/tc/tc_sam_d_r/tc.c \ + drivers/tcc/tcc.c \ drivers/usb/stack_interface/usb_device_udd.c \ drivers/usb/stack_interface/usb_dual.c \ drivers/usb/usb_sam_d_r/usb.c \ @@ -118,6 +121,7 @@ SRC_C = \ modmachine_adc.c \ modmachine_dac.c \ modmachine_pin.c \ + modmachine_pwm.c \ modutime.c \ mphalport.c \ pin_named_pins.c \ diff --git a/atmel-samd/asf/sam0/drivers/tc/docimg/overview.svg b/atmel-samd/asf/sam0/drivers/tc/docimg/overview.svg new file mode 100644 index 0000000000..895ba938c7 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/tc/docimg/overview.svg @@ -0,0 +1,434 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Master slide + + + + + + + + + + + C + o + u + n + t + e + r + + + + + + P + r + e + s + c + a + l + e + r + + + + + + B + a + s + e + + C + o + u + n + t + e + r + + + + + + C + o + n + t + r + o + l + + L + o + g + i + c + + + + + + + + + + + + + + + + + + C + o + m + p + a + r + e + + C + a + p + t + u + r + e + + n + + + + + + + + + C + o + m + p + a + r + e + + C + a + p + t + u + r + e + + . + . + . + + + + + + + + + C + o + m + p + a + r + e + + C + a + p + t + u + r + e + + 1 + + + + + + + + + C + o + n + t + r + o + l + + L + o + g + i + c + + + + + + C + o + m + p + a + r + e + + C + a + p + t + u + r + e + + 0 + + + + + + W + a + v + e + f + o + r + m + + G + e + n + e + r + a + t + i + o + n + + + + + + + + + + C + C + 0 + + + + + + + + + + = + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + G + C + L + K + _ + T + C + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + I + n + t + e + r + r + u + p + t + / + E + v + e + n + t + + C + h + a + n + n + e + l + + + + + + I + n + t + e + r + r + u + p + t + / + E + v + e + n + t + + C + h + a + n + n + e + l + + + + + + W + O + x + + O + u + t + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/atmel-samd/asf/sam0/drivers/tc/docimg/pwm_match_ex.svg b/atmel-samd/asf/sam0/drivers/tc/docimg/pwm_match_ex.svg new file mode 100644 index 0000000000..3eed8c70f3 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/tc/docimg/pwm_match_ex.svg @@ -0,0 +1,431 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Master slide + + + + + + + + + + + + + + + + T + i + m + e + + + + + + + + + + + + + + + + + + + + + + + + C + o + m + p + a + r + e + + m + a + t + c + h + + v + a + l + u + e + + + + + + + + + + + + + + + + + + + + + R + e + l + o + a + d + + t + o + + T + O + P + + v + a + l + u + e + + + + + + + + + + + + + + + + + + C + o + u + n + t + + i + s + + w + r + i + t + t + e + n + + u + s + i + n + g + + t + c + _ + s + e + t + _ + c + o + u + n + t + _ + v + a + l + u + e + ( + ) + + + + + + + + + + + + + + T + O + P + + v + a + l + u + e + + h + a + s + + b + e + e + n + + c + h + a + n + g + e + d + + u + s + i + n + g + + t + c + _ + s + e + t + _ + t + o + p + _ + v + a + l + u + e + ( + ) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + M + a + t + c + h + + + + + + + + + P + W + M + + i + n + v + e + r + t + e + d + + o + u + t + p + u + t + + + + + + M + a + x + + + + + + C + o + u + n + t + e + r + + v + a + l + u + e + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/atmel-samd/asf/sam0/drivers/tc/docimg/pwm_normal_ex.svg b/atmel-samd/asf/sam0/drivers/tc/docimg/pwm_normal_ex.svg new file mode 100644 index 0000000000..072e1fc893 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/tc/docimg/pwm_normal_ex.svg @@ -0,0 +1,386 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Master slide + + + + + + + + C + o + u + n + t + e + r + + v + a + l + u + e + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + T + i + m + e + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + M + a + t + c + h + + + + + + + + + + + + + + + + + + + + + + + + P + W + M + + o + u + t + p + u + t + + + + + + R + e + l + o + a + d + + c + o + u + n + t + e + r + + t + o + + z + e + r + o + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + C + o + m + p + a + r + e + + v + a + l + u + e + + h + a + s + + b + e + e + n + + c + h + a + n + g + e + d + + w + i + t + h + + t + c + _ + s + e + t + _ + c + o + m + p + a + r + e + _ + v + a + l + u + e + ( + ) + + + + + + + + + + + + + + + + + + + T + O + P + + = + + M + a + x + + + + + + C + o + m + p + a + r + e + + m + a + t + c + h + + v + a + l + u + e + + + + \ No newline at end of file diff --git a/atmel-samd/asf/sam0/drivers/tc/tc.h b/atmel-samd/asf/sam0/drivers/tc/tc.h new file mode 100644 index 0000000000..7bf7420461 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/tc/tc.h @@ -0,0 +1,1790 @@ +/** + * \file + * + * \brief SAM TC - Timer Counter Driver + * + * Copyright (C) 2013-2016 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 + * + */ +/* + * Support and FAQ: visit Atmel Support + */ + +#ifndef TC_H_INCLUDED +#define TC_H_INCLUDED + +/** + * \defgroup asfdoc_sam0_tc_group SAM Timer/Counter (TC) Driver + * + * This driver for Atmel® | SMART ARM®-based microcontrollers provides an interface for the configuration + * and management of the timer modules within the device, for waveform + * generation and timing operations. The following driver API modes are covered + * by this manual: + * + * - Polled APIs + * \if TC_CALLBACK_MODE + * - Callback APIs + * \endif + * + * + * The following peripheral is used by this module: + * - TC (Timer/Counter) + * + * The following devices can use this module: + * - Atmel | SMART SAM D20/D21 + * - Atmel | SMART SAM R21 + * - Atmel | SMART SAM D09/D10/D11 + * - Atmel | SMART SAM L21/L22 + * - Atmel | SMART SAM DA1 + * - Atmel | SMART SAM C20/C21 + * + * The outline of this documentation is as follows: + * - \ref asfdoc_sam0_tc_prerequisites + * - \ref asfdoc_sam0_tc_module_overview + * - \ref asfdoc_sam0_tc_special_considerations + * - \ref asfdoc_sam0_tc_extra_info + * - \ref asfdoc_sam0_tc_examples + * - \ref asfdoc_sam0_tc_api_overview + * + * + * \section asfdoc_sam0_tc_prerequisites Prerequisites + * + * There are no prerequisites for this module. + * + * + * \section asfdoc_sam0_tc_module_overview Module Overview + * + * The Timer/Counter (TC) module provides a set of timing and counting related + * functionality, such as the generation of periodic waveforms, the capturing + * of a periodic waveform's frequency/duty cycle, and software timekeeping for + * periodic operations. TC modules can be configured to use an 8-, 16-, or + * 32-bit counter size. + * + * This TC module for the SAM is capable of the following functions: + * + * - Generation of PWM signals + * - Generation of timestamps for events + * - General time counting + * - Waveform period capture + * - Waveform frequency capture + * + * \ref asfdoc_sam0_tc_block_diagram "The diagram below" shows the overview + * of the TC module design. + * + * \anchor asfdoc_sam0_tc_block_diagram + * \image html overview.svg "Basic Overview of the TC Module" + * + * + * \subsection asfdoc_sam0_tc_features Driver Feature Macro Definition + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Driver Feature MacroSupported devices
FEATURE_TC_DOUBLE_BUFFEREDSAM L21/L22/C20/C21
FEATURE_TC_SYNCBUSY_SCHEME_VERSION_2SAM L21/L22/C20/C21
FEATURE_TC_STAMP_PW_CAPTURESAM L21/L22/C20/C21
FEATURE_TC_READ_SYNCSAM L21/L22/C20/C21
FEATURE_TC_IO_CAPTURESAM L21/L22/C20/C21
FEATURE_TC_GENERATE_DMA_TRIGGERSAM L21/L22
+ * \note The specific features are only available in the driver when the + * selected device supports those features. + * + * \subsection asfdoc_sam0_tc_module_overview_func_desc Functional Description + * Independent of the configured counter size, each TC module can be set up + * in one of two different modes; capture and compare. + * + * In capture mode, the counter value is stored when a configurable event + * occurs. This mode can be used to generate timestamps used in event capture, + * or it can be used for the measurement of a periodic input signal's + * frequency/duty cycle. + * + * In compare mode, the counter value is compared against one or more of the + * configured channel compare values. When the counter value coincides with a + * compare value an action can be taken automatically by the module, such as + * generating an output event or toggling a pin when used for frequency or + * Pulse Width Modulation (PWM) signal generation. + * + * \note The connection of events between modules requires the use of the + * \ref asfdoc_sam0_events_group "SAM Event System Driver (EVENTS)" + * to route output event of one module to the input event of another. + * For more information on event routing, refer to the event driver + * documentation. + * + * \subsection asfdoc_sam0_tc_module_overview_tc_size Timer/Counter Size + * Each timer module can be configured in one of three different counter + * sizes; 8-, 16-, and 32-bit. The size of the counter determines the maximum + * value it can count to before an overflow occurs and the count is reset back + * to zero. \ref asfdoc_sam0_tc_count_size_vs_top "The table below" shows the + * maximum values for each of the possible counter sizes. + * + * \anchor asfdoc_sam0_tc_count_size_vs_top + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Timer Counter Sizes and Their Maximum Count Values
Counter sizeMax. (hexadecimal)Max. (decimal)
8-bit0xFF255
16-bit0xFFFF65,535
32-bit0xFFFFFFFF4,294,967,295
+ * + * When using the counter in 16- or 32-bit count mode, Compare Capture + * register 0 (CC0) is used to store the period value when running in PWM + * generation match mode. + * + * When using 32-bit counter size, two 16-bit counters are chained together + * in a cascade formation. Except in SAM D09/D10/D11. Even numbered TC modules + * (e.g. TC0, TC2) can be configured as 32-bit counters. The odd numbered + * counters will act as slaves to the even numbered masters, and will not + * be reconfigurable until the master timer is disabled. The pairing of timer + * modules for 32-bit mode is shown in \ref asfdoc_sam0_tc_module_ms_pairs + * "the table below". + * + * \anchor asfdoc_sam0_tc_module_ms_pairs + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
TC Master and Slave Module Pairings
Master TC moduleSlave TC module
TC0TC1
TC2TC3
......
TCn-1TCn
+ * + * In SAM D09/D10/D11, odd numbered TC modules (e.g. TC1) can be configured as 32-bit + * counters. The even numbered (e.g. TC2) counters will act as slaves to the odd + * numbered masters. + * + * \subsection asfdoc_sam0_tc_module_overview_clock Clock Settings + * + * \subsubsection asfdoc_sam0_tc_module_overview_clock_selection Clock Selection + * Each TC peripheral is clocked asynchronously to the system clock by a GCLK + * (Generic Clock) channel. The GCLK channel connects to any of the GCLK + * generators. The GCLK generators are configured to use one of the available + * clock sources on the system such as internal oscillator, external crystals, + * etc. See the \ref asfdoc_sam0_system_clock_group "Generic Clock driver" + *for + * more information. + * + * \subsubsection asfdoc_sam0_tc_module_overview_clock_prescaler Prescaler + * Each TC module in the SAM has its own individual clock prescaler, which + * can be used to divide the input clock frequency used in the counter. This + * prescaler only scales the clock used to provide clock pulses for the counter + * to count, and does not affect the digital register interface portion of + * the module, thus the timer registers will synchronize to the raw GCLK + * frequency input to the module. + * + * As a result of this, when selecting a GCLK frequency and timer prescaler + * value the user application should consider both the timer resolution + * required and the synchronization frequency, to avoid lengthy + * synchronization times of the module if a very slow GCLK frequency is fed + * into the TC module. It is preferable to use a higher module GCLK frequency + * as the input to the timer, and prescale this down as much as possible to + * obtain a suitable counter frequency in latency-sensitive applications. + * + * \subsubsection asfdoc_sam0_tc_module_overview_clock_reloading Reloading + * Timer modules also contain a configurable reload action, used when a + * re-trigger event occurs. Examples of a re-trigger event are the counter + * reaching the maximum value when counting up, or when an event from the event + * system tells the counter to re-trigger. The reload action determines if the + * prescaler should be reset, and when this should happen. The counter will + * always be reloaded with the value it is set to start counting from. The user + * can choose between three different reload actions, described in + * \ref asfdoc_sam0_tc_module_reload_act "the table below". + * + * \anchor asfdoc_sam0_tc_module_reload_act + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
TC Module Reload Actions
Reload actionDescription
\ref TC_RELOAD_ACTION_GCLK Reload TC counter value on next GCLK cycle. Leave prescaler + * as-is.
\ref TC_RELOAD_ACTION_PRESC Reloads TC counter value on next prescaler clock. Leave prescaler + * as-is.
\ref TC_RELOAD_ACTION_RESYNC Reload TC counter value on next GCLK cycle. Clear prescaler to + * zero.
+ * + * The reload action to use will depend on the specific application being + * implemented. One example is when an external trigger for a reload occurs; if + * the TC uses the prescaler, the counter in the prescaler should not have a + * value between zero and the division factor. The TC counter and the counter + * in the prescaler should both start at zero. When the counter is set to + * re-trigger when it reaches the maximum value on the other hand, this is not the + * right option to use. In such a case it would be better if the prescaler is + * left unaltered when the re-trigger happens, letting the counter reset on the + * next GCLK cycle. + * + * \subsection asfdoc_sam0_tc_module_overview_compare_match Compare Match Operations + * In compare match operation, Compare/Capture registers are used in comparison + * with the counter value. When the timer's count value matches the value of a + * compare channel, a user defined action can be taken. + * + * \subsubsection asfdoc_sam0_tc_module_overview_compare_match_timer Basic Timer + * + * A Basic Timer is a simple application where compare match operations are used + * to determine when a specific period has elapsed. In Basic Timer operations, + * one or more values in the module's Compare/Capture registers are used to + * specify the time (as a number of prescaled GCLK cycles) when an action should + * be taken by the microcontroller. This can be an Interrupt Service Routine + * (ISR), event generator via the event system, or a software flag that is + * polled via the user application. + * + * \subsubsection asfdoc_sam0_tc_module_overview_compare_match_wg Waveform Generation + * + * Waveform generation enables the TC module to generate square waves, or if + * combined with an external passive low-pass filter; analog waveforms. + * + * \subsubsection asfdoc_sam0_tc_module_overview_compare_match_wg_pwm Waveform Generation - PWM + * + * Pulse width modulation is a form of waveform generation and a signalling + * technique that can be useful in many situations. When PWM mode is used, + * a digital pulse train with a configurable frequency and duty cycle can be + * generated by the TC module and output to a GPIO pin of the device. + * + * Often PWM is used to communicate a control or information parameter to an + * external circuit or component. Differing impedances of the source generator + * and sink receiver circuits are less of an issue when using PWM compared to + * using an analog voltage value, as noise will not generally affect the + * signal's integrity to a meaningful extent. + * + * \ref asfdoc_sam0_tc_module_pwm_normal_diag "The figure below" illustrates + * operations and different states of the counter and its output when running + * the counter in PWM normal mode. As can be seen, the TOP value is unchanged + * and is set to MAX. The compare match value is changed at several points to + * illustrate the resulting waveform output changes. The PWM output is set to + * normal (i.e. non-inverted) output mode. + * + * \anchor asfdoc_sam0_tc_module_pwm_normal_diag + * \image html pwm_normal_ex.svg "Example of PWM in Normal Mode, and Different Counter Operations" + * + * + * In \ref asfdoc_sam0_tc_module_pwm_match_diag "the figure below", the + * counter is set to generate PWM in Match mode. The PWM output is inverted via + * the appropriate configuration option in the TC driver configuration + * structure. In this example, the counter value is changed once, but the + * compare match value is kept unchanged. As can be seen, it is possible to + * change the TOP value when running in PWM match mode. + * + * \anchor asfdoc_sam0_tc_module_pwm_match_diag + * \image html pwm_match_ex.svg "Example of PWM in Match Mode and Different Counter Operations" + * + * \subsubsection asfdoc_sam0_tc_module_overview_compare_match_wg_freq Waveform Generation - Frequency + * + * Frequency Generation mode is in many ways identical to PWM + * generation. However, in Frequency Generation a toggle only occurs + * on the output when a match on a capture channels occurs. When the + * match is made, the timer value is reset, resulting in a variable + * frequency square wave with a fixed 50% duty cycle. + * + * \subsubsection asfdoc_sam0_tc_module_overview_compare_match_capt Capture Operations + * + * In capture operations, any event from the event system or a pin change can + * trigger a capture of the counter value. This captured counter value can be + * used as a timestamp for the event, or it can be used in frequency and pulse + * width capture. + * + * \subsubsection asfdoc_sam0_tc_module_overview_compare_match_capt_event_capture Capture Operations - Event + * + * Event capture is a simple use of the capture functionality, + * designed to create timestamps for specific events. When the TC + * module's input capture pin is externally toggled, the current timer + * count value is copied into a buffered register which can then be + * read out by the user application. + * + * Note that when performing any capture operation, there is a risk that the + * counter reaches its top value (MAX) when counting up, or the bottom value + * (zero) when counting down, before the capture event occurs. This can distort + * the result, making event timestamps to appear shorter than reality; the + * user application should check for timer overflow when reading a capture + * result in order to detect this situation and perform an appropriate + * adjustment. + * + * Before checking for a new capture, \ref TC_STATUS_COUNT_OVERFLOW + * should be checked. The response to an overflow error is left to the user + * application, however it may be necessary to clear both the capture overflow + * flag and the capture flag upon each capture reading. + * + * \subsubsection asfdoc_sam0_tc_module_overview_compare_match_capt_pwc Capture Operations - Pulse Width + * + * Pulse Width Capture mode makes it possible to measure the pulse width and + * period of PWM signals. This mode uses two capture channels of the counter. + * This means that the counter module used for Pulse Width Capture can not be + * used for any other purpose. There are two modes for pulse width capture; + * Pulse Width Period (PWP) and Period Pulse Width (PPW). In PWP mode, capture + * channel 0 is used for storing the pulse width and capture channel 1 stores + * the observed period. While in PPW mode, the roles of the two capture channels + * are reversed. + * + * As in the above example it is necessary to poll on interrupt flags to see + * if a new capture has happened and check that a capture overflow error has + * not occurred. + * + * \subsection asfdoc_sam0_tc_module_overview_oneshot One-shot Mode + * + * TC modules can be configured into a one-shot mode. When configured in this + * manner, starting the timer will cause it to count until the next overflow + * or underflow condition before automatically halting, waiting to be manually + * triggered by the user application software or an event signal from the event + * system. + * + * \subsubsection asfdoc_sam0_tc_module_overview_inversion Wave Generation Output Inversion + * + * The output of the wave generation can be inverted by hardware if desired, + * resulting in the logically inverted value being output to the configured + * device GPIO pin. + * + * + * \section asfdoc_sam0_tc_special_considerations Special Considerations + * + * The number of capture compare registers in each TC module is dependent on + * the specific SAM device being used, and in some cases the counter size. + * + * The maximum amount of capture compare registers available in any SAM + * device is two when running in 32-bit mode and four in 8- and 16-bit modes. + * + * + * \section asfdoc_sam0_tc_extra_info Extra Information + * + * For extra information, see \ref asfdoc_sam0_tc_extra. This includes: + * - \ref asfdoc_sam0_tc_extra_acronyms + * - \ref asfdoc_sam0_tc_extra_dependencies + * - \ref asfdoc_sam0_tc_extra_errata + * - \ref asfdoc_sam0_tc_extra_history + * + * + * \section asfdoc_sam0_tc_examples Examples + * + * For a list of examples related to this driver, see + * \ref asfdoc_sam0_tc_exqsg. + * + * \section asfdoc_sam0_tc_api_overview API Overview + * @{ + */ + +#include +#include +#include +#include + +/** + * Define port features set according to different device family + * @{ +*/ +#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30) || defined(__DOXYGEN__) +/** TC double buffered. */ +# define FEATURE_TC_DOUBLE_BUFFERED +/** SYNCBUSY scheme version 2. */ +# define FEATURE_TC_SYNCBUSY_SCHEME_VERSION_2 +/** TC time stamp capture and pulse width capture. */ +# define FEATURE_TC_STAMP_PW_CAPTURE +/** Read synchronization of COUNT. */ +# define FEATURE_TC_READ_SYNC +/** I/O pin edge capture. */ +# define FEATURE_TC_IO_CAPTURE +#endif + +#if (SAML21XXXB) || (SAMR30) || defined(__DOXYGEN__) +/** Generate Direct Memory Access (DMA) triggers. */ +# define FEATURE_TC_GENERATE_DMA_TRIGGER +#endif +/*@}*/ + +#if !defined(__DOXYGEN__) +#if SAMD20 || SAML21 || SAML22 || SAMC20 || SAMC21 || SAMR30 +# define TC_INSTANCE_OFFSET 0 +#endif +#if SAMD21 || SAMR21 || SAMDA1 +# define TC_INSTANCE_OFFSET 3 +#endif +#if SAMD09 || SAMD10 || SAMD11 +# define TC_INSTANCE_OFFSET 1 +#endif + +#if SAMD20 +# define NUMBER_OF_COMPARE_CAPTURE_CHANNELS TC0_CC8_NUM +#elif SAML21 || SAML22 || SAMC20 || SAMC21 || SAMR30 +# define NUMBER_OF_COMPARE_CAPTURE_CHANNELS TC0_CC_NUM +#elif SAMD09 || SAMD10 || SAMD11 +# define NUMBER_OF_COMPARE_CAPTURE_CHANNELS TC1_CC8_NUM +#else +# define NUMBER_OF_COMPARE_CAPTURE_CHANNELS TC3_CC8_NUM + /* Same number for 8-, 16- or 32-bit TC and all TC instances */ +#endif + +/** TC Instance MAX ID Number. */ +#if SAMD20E || SAMD20G || SAMD21G || SAMD21E || SAMR21 +# if SAMD21GXXL +# define TC_INST_MAX_ID 7 +# else +# define TC_INST_MAX_ID 5 +# endif +#elif SAML21 || SAMC20 || SAMC21 || SAMR30 +# define TC_INST_MAX_ID 4 +#elif SAML22 +# define TC_INST_MAX_ID 3 +#elif SAMD09 || SAMD10 || SAMD11 +# define TC_INST_MAX_ID 2 +#else +# define TC_INST_MAX_ID 7 +#endif + +#endif + +#if TC_ASYNC == true +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if TC_ASYNC == true +/** Enum for the possible callback types for the TC module. */ +enum tc_callback { + /** Callback for TC overflow */ + TC_CALLBACK_OVERFLOW, + /** Callback for capture overflow error */ + TC_CALLBACK_ERROR, + /** Callback for capture compare channel 0 */ + TC_CALLBACK_CC_CHANNEL0, + /** Callback for capture compare channel 1 */ + TC_CALLBACK_CC_CHANNEL1, +# if !defined(__DOXYGEN__) + /** Number of available callbacks */ + TC_CALLBACK_N, +# endif +}; +#endif + +/** + * \name Module Status Flags + * + * TC status flags, returned by \ref tc_get_status() and cleared by + * \ref tc_clear_status(). + * + * @{ + */ + +/** Timer channel 0 has matched against its compare value, or has captured a + * new value. + */ +#define TC_STATUS_CHANNEL_0_MATCH (1UL << 0) + +/** Timer channel 1 has matched against its compare value, or has captured a + * new value. + */ +#define TC_STATUS_CHANNEL_1_MATCH (1UL << 1) + +/** Timer register synchronization has completed, and the synchronized count + * value may be read. + */ +#define TC_STATUS_SYNC_READY (1UL << 2) + +/** A new value was captured before the previous value was read, resulting in + * lost data. + */ +#define TC_STATUS_CAPTURE_OVERFLOW (1UL << 3) + +/** The timer count value has overflowed from its maximum value to its minimum + * when counting upward, or from its minimum value to its maximum when + * counting downward. + */ +#define TC_STATUS_COUNT_OVERFLOW (1UL << 4) + +#ifdef FEATURE_TC_DOUBLE_BUFFERED +/** Channel 0 compare or capture buffer valid. */ +#define TC_STATUS_CHN0_BUFFER_VALID (1UL << 5) +/** Channel 1 compare or capture buffer valid. */ +#define TC_STATUS_CHN1_BUFFER_VALID (1UL << 6) +/** Period buffer valid. */ +#define TC_STATUS_PERIOD_BUFFER_VALID (1UL << 7) +#endif +/** @} */ + +/** + * \brief Index of the compare capture channels. + * + * This enum is used to specify which capture/compare channel to do + * operations on. + */ +enum tc_compare_capture_channel { + /** Index of compare capture channel 0 */ + TC_COMPARE_CAPTURE_CHANNEL_0, + /** Index of compare capture channel 1 */ + TC_COMPARE_CAPTURE_CHANNEL_1, +}; + +/** + * \name TC Wave Generation Mode + * + * @{ + */ +#if SAML21 || SAML22 || SAMC20 || SAMC21 || SAMR30 +/** TC wave generation mode: normal frequency. */ +#define TC_WAVE_GENERATION_NORMAL_FREQ_MODE TC_WAVE_WAVEGEN_NFRQ +/** TC wave generation mode: match frequency. */ +#define TC_WAVE_GENERATION_MATCH_FREQ_MODE TC_WAVE_WAVEGEN_MFRQ +/** TC wave generation mode: normal PWM. */ +#define TC_WAVE_GENERATION_NORMAL_PWM_MODE TC_WAVE_WAVEGEN_NPWM +/** TC wave generation mode: match PWM. */ +#define TC_WAVE_GENERATION_MATCH_PWM_MODE TC_WAVE_WAVEGEN_MPWM +#else +/** TC wave generation mode: normal frequency. */ +#define TC_WAVE_GENERATION_NORMAL_FREQ_MODE TC_CTRLA_WAVEGEN_NFRQ +/** TC wave generation mode: match frequency. */ +#define TC_WAVE_GENERATION_MATCH_FREQ_MODE TC_CTRLA_WAVEGEN_MFRQ +/** TC wave generation mode: normal PWM. */ +#define TC_WAVE_GENERATION_NORMAL_PWM_MODE TC_CTRLA_WAVEGEN_NPWM +/** TC wave generation mode: match PWM. */ +#define TC_WAVE_GENERATION_MATCH_PWM_MODE TC_CTRLA_WAVEGEN_MPWM +#endif +/** @} */ + +/** + * \brief TC wave generation mode enum. + * + * This enum is used to select which mode to run the wave + * generation in. + * + */ +enum tc_wave_generation { + /** Top is maximum, except in 8-bit counter size where it is the PER + * register + */ + TC_WAVE_GENERATION_NORMAL_FREQ = TC_WAVE_GENERATION_NORMAL_FREQ_MODE, + + /** Top is CC0, except in 8-bit counter size where it is the PER + * register + */ + TC_WAVE_GENERATION_MATCH_FREQ = TC_WAVE_GENERATION_MATCH_FREQ_MODE, + + /** Top is maximum, except in 8-bit counter size where it is the PER + * register + */ + TC_WAVE_GENERATION_NORMAL_PWM = TC_WAVE_GENERATION_NORMAL_PWM_MODE, + + /** Top is CC0, except in 8-bit counter size where it is the PER + * register + */ + TC_WAVE_GENERATION_MATCH_PWM = TC_WAVE_GENERATION_MATCH_PWM_MODE, +}; + +/** + * \brief Specifies if the counter is 8-, 16-, or 32-bit. + * + * This enum specifies the maximum value it is possible to count to. + */ +enum tc_counter_size { + /** The counter's maximum value is 0xFF, the period register is + * available to be used as top value + */ + TC_COUNTER_SIZE_8BIT = TC_CTRLA_MODE_COUNT8, + + /** The counter's maximum value is 0xFFFF. There is no separate + * period register, to modify top one of the capture compare + * registers has to be used. This limits the amount of + * available channels. + */ + TC_COUNTER_SIZE_16BIT = TC_CTRLA_MODE_COUNT16, + + /** The counter's maximum value is 0xFFFFFFFF. There is no separate + * period register, to modify top one of the capture compare + * registers has to be used. This limits the amount of + * available channels. + */ + TC_COUNTER_SIZE_32BIT = TC_CTRLA_MODE_COUNT32, +}; + +/** + * \brief TC Counter reload action enum. + * + * This enum specify how the counter and prescaler should reload. + */ +enum tc_reload_action { + /** The counter is reloaded/reset on the next GCLK and starts + * counting on the prescaler clock + */ + TC_RELOAD_ACTION_GCLK = TC_CTRLA_PRESCSYNC_GCLK, + + /** The counter is reloaded/reset on the next prescaler clock + */ + TC_RELOAD_ACTION_PRESC = TC_CTRLA_PRESCSYNC_PRESC, + + /** The counter is reloaded/reset on the next GCLK, and the + * prescaler is restarted as well + */ + TC_RELOAD_ACTION_RESYNC = TC_CTRLA_PRESCSYNC_RESYNC, +}; + +/** + * \brief TC clock prescaler values. + * + * This enum is used to choose the clock prescaler + * configuration. The prescaler divides the clock frequency of the TC + * module to make the counter count slower. + */ +enum tc_clock_prescaler { + /** Divide clock by 1 */ + TC_CLOCK_PRESCALER_DIV1 = TC_CTRLA_PRESCALER(0), + /** Divide clock by 2 */ + TC_CLOCK_PRESCALER_DIV2 = TC_CTRLA_PRESCALER(1), + /** Divide clock by 4 */ + TC_CLOCK_PRESCALER_DIV4 = TC_CTRLA_PRESCALER(2), + /** Divide clock by 8 */ + TC_CLOCK_PRESCALER_DIV8 = TC_CTRLA_PRESCALER(3), + /** Divide clock by 16 */ + TC_CLOCK_PRESCALER_DIV16 = TC_CTRLA_PRESCALER(4), + /** Divide clock by 64 */ + TC_CLOCK_PRESCALER_DIV64 = TC_CTRLA_PRESCALER(5), + /** Divide clock by 256 */ + TC_CLOCK_PRESCALER_DIV256 = TC_CTRLA_PRESCALER(6), + /** Divide clock by 1024 */ + TC_CLOCK_PRESCALER_DIV1024 = TC_CTRLA_PRESCALER(7), +}; + +/** + * \brief TC module count direction. + * + * Timer/Counter count direction. + */ +enum tc_count_direction { + /** Timer should count upward from zero to MAX */ + TC_COUNT_DIRECTION_UP, + + /** Timer should count downward to zero from MAX */ + TC_COUNT_DIRECTION_DOWN, +}; + +/** + * \name Waveform Inversion Mode + * + * @{ + */ +#if SAML21 || SAML22 || SAMC20 || SAMC21 || SAMR30 +/** Waveform inversion CC0 mode. */ +#define TC_WAVEFORM_INVERT_CC0_MODE TC_DRVCTRL_INVEN(1) +/** Waveform inversion CC1 mode. */ +#define TC_WAVEFORM_INVERT_CC1_MODE TC_DRVCTRL_INVEN(2) +#else +/** Waveform inversion CC0 mode. */ +#define TC_WAVEFORM_INVERT_CC0_MODE TC_CTRLC_INVEN(1) +/** Waveform inversion CC1 mode. */ +#define TC_WAVEFORM_INVERT_CC1_MODE TC_CTRLC_INVEN(2) +#endif + +/** + * \brief Waveform inversion mode. + * + * Output waveform inversion mode. + */ +enum tc_waveform_invert_output { + /** No inversion of the waveform output */ + TC_WAVEFORM_INVERT_OUTPUT_NONE = 0, + /** Invert output from compare channel 0 */ + TC_WAVEFORM_INVERT_OUTPUT_CHANNEL_0 = TC_WAVEFORM_INVERT_CC0_MODE, + /** Invert output from compare channel 1 */ + TC_WAVEFORM_INVERT_OUTPUT_CHANNEL_1 = TC_WAVEFORM_INVERT_CC1_MODE, +}; + +/** + * \brief Action to perform when the TC module is triggered by an event. + * + * Event action to perform when the module is triggered by an event. + */ +enum tc_event_action { + /** No event action */ + TC_EVENT_ACTION_OFF = TC_EVCTRL_EVACT_OFF, + /** Re-trigger on event */ + TC_EVENT_ACTION_RETRIGGER = TC_EVCTRL_EVACT_RETRIGGER, + /** Increment counter on event */ + TC_EVENT_ACTION_INCREMENT_COUNTER = TC_EVCTRL_EVACT_COUNT, + /** Start counter on event */ + TC_EVENT_ACTION_START = TC_EVCTRL_EVACT_START, + + /** Store period in capture register 0, pulse width in capture + * register 1 + */ + TC_EVENT_ACTION_PPW = TC_EVCTRL_EVACT_PPW, + + /** Store pulse width in capture register 0, period in capture + * register 1 + */ + TC_EVENT_ACTION_PWP = TC_EVCTRL_EVACT_PWP, +#ifdef FEATURE_TC_STAMP_PW_CAPTURE + /** Time stamp capture */ + TC_EVENT_ACTION_STAMP = TC_EVCTRL_EVACT_STAMP, + /** Pulse width capture */ + TC_EVENT_ACTION_PW = TC_EVCTRL_EVACT_PW, +#endif +}; + +/** + * \brief TC event enable/disable structure. + * + * Event flags for the \ref tc_enable_events() and \ref tc_disable_events(). + */ +struct tc_events { + /** Generate an output event on a compare channel match */ + bool generate_event_on_compare_channel + [NUMBER_OF_COMPARE_CAPTURE_CHANNELS]; + /** Generate an output event on counter overflow */ + bool generate_event_on_overflow; + /** Perform the configured event action when an incoming event is signalled */ + bool on_event_perform_action; + /** Specifies if the input event source is inverted, when used in PWP or + * PPW event action modes + */ + bool invert_event_input; + /** Specifies which event to trigger if an event is triggered */ + enum tc_event_action event_action; +}; + +/** + * \brief Configuration struct for TC module in 8-bit size counter mode. + */ +struct tc_8bit_config { + /** Initial timer count value */ + uint8_t value; + /** Where to count to or from depending on the direction on the counter */ + uint8_t period; + /** Value to be used for compare match on each channel */ + uint8_t compare_capture_channel[NUMBER_OF_COMPARE_CAPTURE_CHANNELS]; +}; + +/** + * \brief Configuration struct for TC module in 16-bit size counter mode. + */ +struct tc_16bit_config { + /** Initial timer count value */ + uint16_t value; + /** Value to be used for compare match on each channel */ + uint16_t compare_capture_channel[NUMBER_OF_COMPARE_CAPTURE_CHANNELS]; +}; + +/** + * \brief Configuration struct for TC module in 32-bit size counter mode. + */ +struct tc_32bit_config { + /** Initial timer count value */ + uint32_t value; + /** Value to be used for compare match on each channel */ + uint32_t compare_capture_channel[NUMBER_OF_COMPARE_CAPTURE_CHANNELS]; +}; + +/** + * \brief Configuration struct for TC module in 32-bit size counter mode. + */ +struct tc_pwm_channel { + /** When \c true, PWM output for the given channel is enabled */ + bool enabled; + /** Specifies pin output for each channel */ + uint32_t pin_out; + /** Specifies Multiplexer (MUX) setting for each output channel pin */ + uint32_t pin_mux; +}; + +/** + * \brief TC configuration structure. + * + * Configuration struct for a TC instance. This structure should be + * initialized by the \ref tc_get_config_defaults function before being + * modified by the user application. + */ +struct tc_config { + /** GCLK generator used to clock the peripheral */ + enum gclk_generator clock_source; + + /** When \c true the module is enabled during standby */ + bool run_in_standby; +#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30) + /** Run on demand */ + bool on_demand; +#endif + /** Specifies either 8-, 16-, or 32-bit counter size */ + enum tc_counter_size counter_size; + /** Specifies the prescaler value for GCLK_TC */ + enum tc_clock_prescaler clock_prescaler; + /** Specifies which waveform generation mode to use */ + enum tc_wave_generation wave_generation; + + /** Specifies the reload or reset time of the counter and prescaler + * resynchronization on a re-trigger event for the TC + */ + enum tc_reload_action reload_action; + + /** Specifies which channel(s) to invert the waveform on. + For SAM L21/L22/C20/C21, it's also used to invert I/O input pin. */ + uint8_t waveform_invert_output; + + /** Specifies which channel(s) to enable channel capture + * operation on + */ + bool enable_capture_on_channel[NUMBER_OF_COMPARE_CAPTURE_CHANNELS]; +#ifdef FEATURE_TC_IO_CAPTURE + /** Specifies which channel(s) to enable I/O capture + * operation on + */ + bool enable_capture_on_IO[NUMBER_OF_COMPARE_CAPTURE_CHANNELS]; +#endif + + /** When \c true, one-shot will stop the TC on next hardware or software + * re-trigger event or overflow/underflow + */ + bool oneshot; + + /** Specifies the direction for the TC to count */ + enum tc_count_direction count_direction; + + /** Specifies the PWM channel for TC */ + struct tc_pwm_channel pwm_channel[NUMBER_OF_COMPARE_CAPTURE_CHANNELS]; + + /** Access the different counter size settings through this configuration member. */ + union { + /** Struct for 8-bit specific timer configuration */ + struct tc_8bit_config counter_8_bit; + /** Struct for 16-bit specific timer configuration */ + struct tc_16bit_config counter_16_bit; + /** Struct for 32-bit specific timer configuration */ + struct tc_32bit_config counter_32_bit; + }; + +#ifdef FEATURE_TC_DOUBLE_BUFFERED + /** Set to \c true to enable double buffering write. When enabled any write + * through \ref tc_set_top_value(), \ref tc_set_compare_value() and + * will direct to the buffer register as buffered + * value, and the buffered value will be committed to effective register + * on UPDATE condition, if update is not locked. + */ + bool double_buffering_enabled; +#endif +}; + +#if TC_ASYNC == true +/** Forward Declaration for the device instance. */ +struct tc_module; + +/** Type of the callback functions. */ +typedef void (*tc_callback_t)(struct tc_module *const module); +#endif + +/** + * \brief TC software device instance structure. + * + * TC software instance structure, used to retain software state information + * of an associated hardware module instance. + * + * \note The fields of this structure should not be altered by the user + * application; they are reserved for module-internal use only. + */ +struct tc_module { +#if !defined(__DOXYGEN__) + /** Hardware module pointer of the associated Timer/Counter peripheral */ + Tc *hw; + + /** Size of the initialized Timer/Counter module configuration */ + enum tc_counter_size counter_size; +# if TC_ASYNC == true + /** Array of callbacks */ + tc_callback_t callback[TC_CALLBACK_N]; + /** Bit mask for callbacks registered */ + uint8_t register_callback_mask; + /** Bit mask for callbacks enabled */ + uint8_t enable_callback_mask; +# endif +#ifdef FEATURE_TC_DOUBLE_BUFFERED + /** Set to \c true to enable double buffering write */ + bool double_buffering_enabled; +#endif +#endif +}; + +#if !defined(__DOXYGEN__) +uint8_t _tc_get_inst_index( + Tc *const hw); +#endif + +/** + * \name Driver Initialization and Configuration + * @{ + */ + +/** + * \brief 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. + * + * \param[in] module_inst Pointer to the software module instance struct + * + * \return Synchronization status of the underlying hardware module(s). + * + * \retval false If the module has completed synchronization + * \retval true If the module synchronization is ongoing + */ +static inline bool tc_is_syncing( + const struct tc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + TcCount8 *const tc_module = &(module_inst->hw->COUNT8); + +#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30) + return (tc_module->SYNCBUSY.reg); +#else + return (tc_module->STATUS.reg & TC_STATUS_SYNCBUSY); +#endif +} + +/** + * \brief Initializes config with predefined default values. + * + * This function will initialize a given TC configuration structure to + * a set of known default values. This function should be called on + * any new instance of the configuration structures before being + * modified by the user application. + * + * The default configuration is as follows: + * \li GCLK generator 0 (GCLK main) clock source + * \li 16-bit counter size on the counter + * \li No prescaler + * \li Normal frequency wave generation + * \li GCLK reload action + * \li Don't run in standby + * \li Don't run on demand for SAM L21/L22/C20/C21 + * \li No inversion of waveform output + * \li No capture enabled + * \li No I/O capture enabled for SAM L21/L22/C20/C21 + * \li No event input enabled + * \li Count upward + * \li Don't perform one-shot operations + * \li No event action + * \li No channel 0 PWM output + * \li No channel 1 PWM output + * \li Counter starts on 0 + * \li Capture compare channel 0 set to 0 + * \li Capture compare channel 1 set to 0 + * \li No PWM pin output enabled + * \li Pin and MUX configuration not set + * \li Double buffer disabled (if have this feature) + * + * \param[out] config Pointer to a TC module configuration structure to set + */ +static inline void tc_get_config_defaults( + struct tc_config *const config) +{ + /* Sanity check arguments */ + Assert(config); + + /* Write default config to config struct */ + config->clock_source = GCLK_GENERATOR_0; + config->counter_size = TC_COUNTER_SIZE_16BIT; + config->clock_prescaler = TC_CLOCK_PRESCALER_DIV1; + config->wave_generation = TC_WAVE_GENERATION_NORMAL_FREQ; + config->reload_action = TC_RELOAD_ACTION_GCLK; + config->run_in_standby = false; +#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30) + config->on_demand = false; +#endif + config->waveform_invert_output = TC_WAVEFORM_INVERT_OUTPUT_NONE; + config->enable_capture_on_channel[TC_COMPARE_CAPTURE_CHANNEL_0] = false; + config->enable_capture_on_channel[TC_COMPARE_CAPTURE_CHANNEL_1] = false; +#ifdef FEATURE_TC_IO_CAPTURE + config->enable_capture_on_IO[TC_COMPARE_CAPTURE_CHANNEL_0] = false; + config->enable_capture_on_IO[TC_COMPARE_CAPTURE_CHANNEL_1] = false; +#endif + + config->count_direction = TC_COUNT_DIRECTION_UP; + config->oneshot = false; + + config->pwm_channel[TC_COMPARE_CAPTURE_CHANNEL_0].enabled = false; + config->pwm_channel[TC_COMPARE_CAPTURE_CHANNEL_0].pin_out = 0; + config->pwm_channel[TC_COMPARE_CAPTURE_CHANNEL_0].pin_mux = 0; + + config->pwm_channel[TC_COMPARE_CAPTURE_CHANNEL_1].enabled = false; + config->pwm_channel[TC_COMPARE_CAPTURE_CHANNEL_1].pin_out = 0; + config->pwm_channel[TC_COMPARE_CAPTURE_CHANNEL_1].pin_mux = 0; + + config->counter_16_bit.value = 0x0000; + config->counter_16_bit.compare_capture_channel\ + [TC_COMPARE_CAPTURE_CHANNEL_0] = 0x0000; + config->counter_16_bit.compare_capture_channel\ + [TC_COMPARE_CAPTURE_CHANNEL_1] = 0x0000; +#ifdef FEATURE_TC_DOUBLE_BUFFERED + config->double_buffering_enabled = false; +#endif + +} + +enum status_code tc_init( + struct tc_module *const module_inst, + Tc *const hw, + const struct tc_config *const config); + +/** @} */ + +/** + * \name Event Management + * @{ + */ + +/** + * \brief Enables a TC module event input or output. + * + * Enables one or more input or output events to or from the TC module. + * See \ref tc_events for a list of events this module supports. + * + * \note Events cannot be altered while the module is enabled. + * + * \param[in] module_inst Pointer to the software module instance struct + * \param[in] events Struct containing flags of events to enable + */ +static inline void tc_enable_events( + struct tc_module *const module_inst, + struct tc_events *const events) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + Assert(events); + + Tc *const tc_module = module_inst->hw; + + uint32_t event_mask = 0; + + if (events->invert_event_input == true) { + event_mask |= TC_EVCTRL_TCINV; + } + + if (events->on_event_perform_action == true) { + event_mask |= TC_EVCTRL_TCEI; + } + + if (events->generate_event_on_overflow == true) { + event_mask |= TC_EVCTRL_OVFEO; + } + + for (uint8_t i = 0; i < NUMBER_OF_COMPARE_CAPTURE_CHANNELS; i++) { + if (events->generate_event_on_compare_channel[i] == true) { + event_mask |= (TC_EVCTRL_MCEO(1) << i); + } + } + + tc_module->COUNT8.EVCTRL.reg |= event_mask | events->event_action; +} + +/** + * \brief Disables a TC module event input or output. + * + * Disables one or more input or output events to or from the TC module. + * See \ref tc_events for a list of events this module supports. + * + * \note Events cannot be altered while the module is enabled. + * + * \param[in] module_inst Pointer to the software module instance struct + * \param[in] events Struct containing flags of events to disable + */ +static inline void tc_disable_events( + struct tc_module *const module_inst, + struct tc_events *const events) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + Assert(events); + + Tc *const tc_module = module_inst->hw; + + uint32_t event_mask = 0; + + if (events->invert_event_input == true) { + event_mask |= TC_EVCTRL_TCINV; + } + + if (events->on_event_perform_action == true) { + event_mask |= TC_EVCTRL_TCEI; + } + + if (events->generate_event_on_overflow == true) { + event_mask |= TC_EVCTRL_OVFEO; + } + + for (uint8_t i = 0; i < NUMBER_OF_COMPARE_CAPTURE_CHANNELS; i++) { + if (events->generate_event_on_compare_channel[i] == true) { + event_mask |= (TC_EVCTRL_MCEO(1) << i); + } + } + + tc_module->COUNT8.EVCTRL.reg &= ~event_mask; +} + +/** @} */ + +/** + * \name Enable/Disable/Reset + * @{ + */ + +enum status_code tc_reset( + const struct tc_module *const module_inst); + +/** + * \brief Enable the TC module. + * + * Enables a TC module that has been previously initialized. The counter will + * start when the counter is enabled. + * + * \note When the counter is configured to re-trigger on an event, the counter + * will not start until the start function is used. + * + * \param[in] module_inst Pointer to the software module instance struct + */ +static inline void tc_enable( + const struct tc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + TcCount8 *const tc_module = &(module_inst->hw->COUNT8); + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + /* Enable TC module */ + tc_module->CTRLA.reg |= TC_CTRLA_ENABLE; +} + +/** + * \brief Disables the TC module. + * + * Disables a TC module and stops the counter. + * + * \param[in] module_inst Pointer to the software module instance struct + */ +static inline void tc_disable( + const struct tc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + TcCount8 *const tc_module = &(module_inst->hw->COUNT8); + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + /* Disbale interrupt */ + tc_module->INTENCLR.reg = TC_INTENCLR_MASK; + /* Clear interrupt flag */ + tc_module->INTFLAG.reg = TC_INTFLAG_MASK; + + /* Disable TC module */ + tc_module->CTRLA.reg &= ~TC_CTRLA_ENABLE; +} + +/** @} */ + +/** + * \name Get/Set Count Value + * @{ + */ + +uint32_t tc_get_count_value( + const struct tc_module *const module_inst); + +enum status_code tc_set_count_value( + const struct tc_module *const module_inst, + const uint32_t count); + +/** @} */ + +/** + * \name Start/Stop Counter + * @{ + */ + +/** + * \brief Stops the counter. + * + * This function will stop the counter. When the counter is stopped + * the value in the count value is set to 0 if the counter was + * counting up, or maximum if the counter was counting + * down when stopped. + * + * \param[in] module_inst Pointer to the software module instance struct + */ +static inline void tc_stop_counter( + const struct tc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + TcCount8 *const tc_module = &(module_inst->hw->COUNT8); + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + /* Write command to execute */ + tc_module->CTRLBSET.reg = TC_CTRLBSET_CMD(TC_CTRLBSET_CMD_STOP_Val); +} + +/** + * \brief Starts the counter. + * + * Starts or restarts an initialized TC module's counter. + * + * \param[in] module_inst Pointer to the software module instance struct + */ +static inline void tc_start_counter( + const struct tc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + TcCount8 *const tc_module = &(module_inst->hw->COUNT8); + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + /* Make certain that there are no conflicting commands in the register */ + tc_module->CTRLBCLR.reg = TC_CTRLBCLR_CMD_NONE; + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + /* Write command to execute */ + tc_module->CTRLBSET.reg = TC_CTRLBSET_CMD(TC_CTRLBSET_CMD_RETRIGGER_Val); +} + +/** @} */ + +#ifdef FEATURE_TC_DOUBLE_BUFFERED +/** + * \name Double Buffering + * @{ + */ + +/** + * \brief Update double buffer. + * + * Update double buffer. + * + * \param[in] module_inst Pointer to the software module instance struct + */ +static inline void tc_update_double_buffer( + const struct tc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + TcCount8 *const tc_module = &(module_inst->hw->COUNT8); + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + /* Make certain that there are no conflicting commands in the register */ + tc_module->CTRLBCLR.reg = TC_CTRLBCLR_CMD_NONE; + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + /* Write command to execute */ + tc_module->CTRLBSET.reg = TC_CTRLBSET_CMD(TC_CTRLBSET_CMD_UPDATE_Val); +} +/** @} */ +#endif + +#ifdef FEATURE_TC_READ_SYNC +/** + * \name Count Read Synchronization + * @{ + */ + +/** + * \brief Read synchronization of COUNT. + * + * Read synchronization of COUNT. + * + * \param[in] module_inst Pointer to the software module instance struct + */ +static inline void tc_sync_read_count( + const struct tc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + TcCount8 *const tc_module = &(module_inst->hw->COUNT8); + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + /* Make certain that there are no conflicting commands in the register */ + tc_module->CTRLBCLR.reg = TC_CTRLBCLR_CMD_NONE; + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + /* Write command to execute */ + tc_module->CTRLBSET.reg = TC_CTRLBSET_CMD(TC_CTRLBSET_CMD_READSYNC_Val); +} +/** @} */ +#endif + +#ifdef FEATURE_TC_GENERATE_DMA_TRIGGER +/** + * \name Generate TC DMA Triggers Command + * @{ + */ + +/** + * \brief TC DMA Trigger. + * + * TC DMA trigger command. + * + * \param[in] module_inst Pointer to the software module instance struct + */ +static inline void tc_dma_trigger_command( + const struct tc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + TcCount8 *const tc_module = &(module_inst->hw->COUNT8); + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + /* Make certain that there are no conflicting commands in the register */ + tc_module->CTRLBCLR.reg = TC_CTRLBCLR_CMD_NONE; + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + +#if (SAMC20) || (SAMC21) || (SAML22) || (SAML21XXXB) || (SAMR30) + /* Write command to execute */ + tc_module->CTRLBSET.reg = TC_CTRLBSET_CMD(TC_CTRLBSET_CMD_DMAOS_Val); +#endif +} +/** @} */ +#endif + +/** + * \name Get Capture Set Compare + * @{ + */ + +uint32_t tc_get_capture_value( + const struct tc_module *const module_inst, + const enum tc_compare_capture_channel channel_index); + +enum status_code tc_set_compare_value( + const struct tc_module *const module_inst, + const enum tc_compare_capture_channel channel_index, + const uint32_t compare_value); + +/** @} */ + +/** + * \name Set Top Value + * @{ + */ + +enum status_code tc_set_top_value( + const struct tc_module *const module_inst, + const uint32_t top_value); + +/** @} */ + +/** + * \name Status Management + * @{ + */ + +/** + * \brief Retrieves the current module status. + * + * Retrieves the status of the module, giving overall state information. + * + * \param[in] module_inst Pointer to the TC software instance struct + * + * \return Bitmask of \c TC_STATUS_* flags. + * + * \retval TC_STATUS_CHANNEL_0_MATCH Timer channel 0 compare/capture match + * \retval TC_STATUS_CHANNEL_1_MATCH Timer channel 1 compare/capture match + * \retval TC_STATUS_SYNC_READY Timer read synchronization has completed + * \retval TC_STATUS_CAPTURE_OVERFLOW Timer capture data has overflowed + * \retval TC_STATUS_COUNT_OVERFLOW Timer count value has overflowed + * \retval TC_STATUS_CHN0_BUFFER_VALID Timer count channel 0 compare/capture buffer valid + * \retval TC_STATUS_CHN1_BUFFER_VALID Timer count channel 1 compare/capture buffer valid + * \retval TC_STATUS_PERIOD_BUFFER_VALID Timer count period buffer valid + */ +static inline uint32_t tc_get_status( + struct tc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + TcCount8 *const tc_module = &(module_inst->hw->COUNT8); + + uint32_t int_flags = tc_module->INTFLAG.reg; + + uint32_t status_flags = 0; + + /* Check for TC channel 0 match */ + if (int_flags & TC_INTFLAG_MC(1)) { + status_flags |= TC_STATUS_CHANNEL_0_MATCH; + } + + /* Check for TC channel 1 match */ + if (int_flags & TC_INTFLAG_MC(2)) { + status_flags |= TC_STATUS_CHANNEL_1_MATCH; + } + +#if !defined(FEATURE_TC_SYNCBUSY_SCHEME_VERSION_2) + /* Check for TC read synchronization ready */ + if (int_flags & TC_INTFLAG_SYNCRDY) { + status_flags |= TC_STATUS_SYNC_READY; + } +#endif + + /* Check for TC capture overflow */ + if (int_flags & TC_INTFLAG_ERR) { + status_flags |= TC_STATUS_CAPTURE_OVERFLOW; + } + + /* Check for TC count overflow */ + if (int_flags & TC_INTFLAG_OVF) { + status_flags |= TC_STATUS_COUNT_OVERFLOW; + } +#ifdef FEATURE_TC_DOUBLE_BUFFERED + uint8_t double_buffer_valid_status = tc_module->STATUS.reg; + + /* Check channel 0 compare or capture buffer valid */ + if (double_buffer_valid_status & TC_STATUS_CCBUFV0) { + status_flags |= TC_STATUS_CHN0_BUFFER_VALID; + } + /* Check channel 0 compare or capture buffer valid */ + if (double_buffer_valid_status & TC_STATUS_CCBUFV1) { + status_flags |= TC_STATUS_CHN1_BUFFER_VALID; + } + /* Check period buffer valid */ + if (double_buffer_valid_status & TC_STATUS_PERBUFV) { + status_flags |= TC_STATUS_PERIOD_BUFFER_VALID; + } +#endif + + return status_flags; +} + +/** + * \brief Clears a module status flag. + * + * Clears the given status flag of the module. + * + * \param[in] module_inst Pointer to the TC software instance struct + * \param[in] status_flags Bitmask of \c TC_STATUS_* flags to clear + */ +static inline void tc_clear_status( + struct tc_module *const module_inst, + const uint32_t status_flags) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + TcCount8 *const tc_module = &(module_inst->hw->COUNT8); + + uint32_t int_flags = 0; + + /* Check for TC channel 0 match */ + if (status_flags & TC_STATUS_CHANNEL_0_MATCH) { + int_flags |= TC_INTFLAG_MC(1); + } + + /* Check for TC channel 1 match */ + if (status_flags & TC_STATUS_CHANNEL_1_MATCH) { + int_flags |= TC_INTFLAG_MC(2); + } + +#if !defined(FEATURE_TC_SYNCBUSY_SCHEME_VERSION_2) + /* Check for TC read synchronization ready */ + if (status_flags & TC_STATUS_SYNC_READY) { + int_flags |= TC_INTFLAG_SYNCRDY; + } +#endif + + /* Check for TC capture overflow */ + if (status_flags & TC_STATUS_CAPTURE_OVERFLOW) { + int_flags |= TC_INTFLAG_ERR; + } + + /* Check for TC count overflow */ + if (status_flags & TC_STATUS_COUNT_OVERFLOW) { + int_flags |= TC_INTFLAG_OVF; + } + + /* Clear interrupt flag */ + tc_module->INTFLAG.reg = int_flags; +} + +/** @} */ + +/** @} */ + +#ifdef __cplusplus +} +#endif + +/** + * \page asfdoc_sam0_tc_extra Extra Information for TC Driver + * + * \section asfdoc_sam0_tc_extra_acronyms Acronyms + * The table below presents the acronyms used in this module: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
AcronymDescription
DMADirect Memory Access
TCTimer Counter
PWMPulse Width Modulation
PWPPulse Width Period
PPWPeriod Pulse Width
+ * + * + * \section asfdoc_sam0_tc_extra_dependencies Dependencies + * This driver has the following dependencies: + * + * - \ref asfdoc_sam0_system_pinmux_group "System Pin Multiplexer Driver" + * + * + * \section asfdoc_sam0_tc_extra_errata Errata + * There are no errata related to this driver. + * + * + * \section asfdoc_sam0_tc_extra_history Module History + * An overview of the module history is presented in the table below, with + * details on the enhancements and fixes made to the module since its first + * release. The current version of this corresponds to the newest version in + * the table. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Changelog
Added support for SAM D21 and do some modifications as below: + * \li Clean up in the configuration structure, the counter size + * setting specific registers is accessed through the counter_8_bit, + * counter_16_bit, and counter_32_bit structures + * \li All event related settings moved into the tc_event structure
Added automatic digital clock interface enable for the slave TC + * module when a timer is initialized in 32-bit mode
Initial release
+ */ + +/** + * \page asfdoc_sam0_tc_exqsg Examples for TC Driver + * + * This is a list of the available Quick Start guides (QSGs) and example + * applications for \ref asfdoc_sam0_tc_group. QSGs are simple examples with + * step-by-step instructions to configure and use this driver in a selection of + * use cases. Note that QSGs can be compiled as a standalone application or be + * added to the user application. + * + * - \subpage asfdoc_sam0_tc_basic_use_case + * - \subpage asfdoc_sam0_tc_macth_freq_use_case + * \if TC_CALLBACK_MODE + * - \subpage asfdoc_sam0_tc_timer_use_case + * - \subpage asfdoc_sam0_tc_callback_use_case + * \endif + * - \subpage asfdoc_sam0_tc_dma_use_case + * + * \page asfdoc_sam0_tc_document_revision_history Document Revision History + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Doc. Rev. + * Date + * Comments + *
42123E12/2015Added support for SAM L21/L22, SAM DA1, SAM D09, and SAM C21
42123D12/2014Added timer use case. + * Added support for SAM R21 and SAM D10/D11
42123C01/2014Added support for SAM D21
42123B06/2013Corrected documentation typos
42123A06/2013Initial document release
+ */ + +#endif /* TC_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/tc/tc_interrupt.c b/atmel-samd/asf/sam0/drivers/tc/tc_interrupt.c new file mode 100644 index 0000000000..3a5cf7e45a --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/tc/tc_interrupt.c @@ -0,0 +1,199 @@ +/** + * \file + * + * \brief SAM TC - Timer Counter Callback Driver + * + * Copyright (C) 2013-2016 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 + * + */ + +/* + * Support and FAQ: visit Atmel Support + */ + +#include "tc_interrupt.h" + +void *_tc_instances[TC_INST_NUM]; + +void _tc_interrupt_handler(uint8_t instance); + +/** + * \brief Registers a callback. + * + * Registers a callback function which is implemented by the user. + * + * \note The callback must be enabled by \ref tc_enable_callback, + * in order for the interrupt handler to call it when the conditions for the + * callback type is met. + * + * \param[in] module Pointer to TC software instance struct + * \param[in] callback_func Pointer to callback function + * \param[in] callback_type Callback type given by an enum + */ +enum status_code tc_register_callback( + struct tc_module *const module, + tc_callback_t callback_func, + const enum tc_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module); + Assert(callback_func); + + /* Register callback function */ + module->callback[callback_type] = callback_func; + + /* Set the bit corresponding to the callback_type */ + if (callback_type == TC_CALLBACK_CC_CHANNEL0) { + module->register_callback_mask |= TC_INTFLAG_MC(1); + } + else if (callback_type == TC_CALLBACK_CC_CHANNEL1) { + module->register_callback_mask |= TC_INTFLAG_MC(2); + } + else { + module->register_callback_mask |= (1 << callback_type); + } + return STATUS_OK; +} + +/** + * \brief Unregisters a callback. + * + * Unregisters a callback function implemented by the user. The callback should be + * disabled before it is unregistered. + * + * \param[in] module Pointer to TC software instance struct + * \param[in] callback_type Callback type given by an enum + */ +enum status_code tc_unregister_callback( + struct tc_module *const module, + const enum tc_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module); + + /* Unregister callback function */ + module->callback[callback_type] = NULL; + + /* Clear the bit corresponding to the callback_type */ + if (callback_type == TC_CALLBACK_CC_CHANNEL0) { + module->register_callback_mask &= ~TC_INTFLAG_MC(1); + } + else if (callback_type == TC_CALLBACK_CC_CHANNEL1) { + module->register_callback_mask &= ~TC_INTFLAG_MC(2); + } + else { + module->register_callback_mask &= ~(1 << callback_type); + } + return STATUS_OK; +} + +/** + * \internal ISR handler for TC + * + * Auto-generate a set of interrupt handlers for each TC in the device. + */ +#define _TC_INTERRUPT_HANDLER(n, m) \ + void TC##n##_Handler(void) \ + { \ + _tc_interrupt_handler(m); \ + } + +#if (SAML21E) || (SAML21G) || (SAMR30E) || (SAMR30G) + _TC_INTERRUPT_HANDLER(0,0) + _TC_INTERRUPT_HANDLER(1,1) + _TC_INTERRUPT_HANDLER(4,2) +#else + MRECURSION(TC_INST_NUM, _TC_INTERRUPT_HANDLER, TC_INST_MAX_ID) +#endif + + +/** + * \internal Interrupt Handler for TC module + * + * Handles interrupts as they occur, it will run the callback functions + * that are registered and enabled. + * + * \param[in] instance ID of the TC instance calling the interrupt + * handler + */ +void _tc_interrupt_handler( + uint8_t instance) +{ + /* Temporary variable */ + uint8_t interrupt_and_callback_status_mask; + + /* Get device instance from the look-up table */ + struct tc_module *module + = (struct tc_module *)_tc_instances[instance]; + + /* Read and mask interrupt flag register */ + interrupt_and_callback_status_mask = module->hw->COUNT8.INTFLAG.reg & + module->register_callback_mask & + module->enable_callback_mask; + + /* Check if an Overflow interrupt has occurred */ + if (interrupt_and_callback_status_mask & TC_INTFLAG_OVF) { + /* Invoke registered and enabled callback function */ + (module->callback[TC_CALLBACK_OVERFLOW])(module); + /* Clear interrupt flag */ + module->hw->COUNT8.INTFLAG.reg = TC_INTFLAG_OVF; + } + + /* Check if an Error interrupt has occurred */ + if (interrupt_and_callback_status_mask & TC_INTFLAG_ERR) { + /* Invoke registered and enabled callback function */ + (module->callback[TC_CALLBACK_ERROR])(module); + /* Clear interrupt flag */ + module->hw->COUNT8.INTFLAG.reg = TC_INTFLAG_ERR; + } + + /* Check if an Match/Capture Channel 0 interrupt has occurred */ + if (interrupt_and_callback_status_mask & TC_INTFLAG_MC(1)) { + /* Invoke registered and enabled callback function */ + (module->callback[TC_CALLBACK_CC_CHANNEL0])(module); + /* Clear interrupt flag */ + module->hw->COUNT8.INTFLAG.reg = TC_INTFLAG_MC(1); + } + + /* Check if an Match/Capture Channel 1 interrupt has occurred */ + if (interrupt_and_callback_status_mask & TC_INTFLAG_MC(2)) { + /* Invoke registered and enabled callback function */ + (module->callback[TC_CALLBACK_CC_CHANNEL1])(module); + /* Clear interrupt flag */ + module->hw->COUNT8.INTFLAG.reg = TC_INTFLAG_MC(2); + } +} diff --git a/atmel-samd/asf/sam0/drivers/tc/tc_interrupt.h b/atmel-samd/asf/sam0/drivers/tc/tc_interrupt.h new file mode 100644 index 0000000000..56c7264ad7 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/tc/tc_interrupt.h @@ -0,0 +1,179 @@ +/** + * \file + * + * \brief SAM TC - Timer Counter Callback Driver + * + * Copyright (C) 2013-2016 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 + * + */ + +/* + * Support and FAQ: visit Atmel Support + */ + +#ifndef TC_INTERRUPT_H_INCLUDED +#define TC_INTERRUPT_H_INCLUDED + +#include "tc.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(__DOXYGEN__) +extern void *_tc_instances[TC_INST_NUM]; + +# define _TC_INTERRUPT_VECT_NUM(n, unused) \ + SYSTEM_INTERRUPT_MODULE_TC##n, +/** + * \internal Get the interrupt vector for the given device instance + * + * \param[in] TC module instance number + * + * \return Interrupt vector for of the given TC module instance. + */ +static enum system_interrupt_vector _tc_interrupt_get_interrupt_vector( + uint32_t inst_num) +{ + static uint8_t tc_interrupt_vectors[TC_INST_NUM] = + { +#if (SAML21E) || (SAML21G) || (SAMR30E) || (SAMR30G) + SYSTEM_INTERRUPT_MODULE_TC0, + SYSTEM_INTERRUPT_MODULE_TC1, + SYSTEM_INTERRUPT_MODULE_TC4 +#else + MRECURSION(TC_INST_NUM, _TC_INTERRUPT_VECT_NUM, TC_INST_MAX_ID) +#endif + }; + + return (enum system_interrupt_vector)tc_interrupt_vectors[inst_num]; +} +#endif /* !defined(__DOXYGEN__) */ + +/** + * \name Callback Management + * {@ + */ + +enum status_code tc_register_callback( + struct tc_module *const module, + tc_callback_t callback_func, + const enum tc_callback callback_type); + +enum status_code tc_unregister_callback( + struct tc_module *const module, + const enum tc_callback callback_type); + +/** + * \brief Enables callback. + * + * Enables the callback function registered by the \ref + * tc_register_callback. The callback function will be called from the + * interrupt handler when the conditions for the callback type are + * met. This function will also enable the appropriate interrupts. + * + * \param[in] module Pointer to TC software instance struct + * \param[in] callback_type Callback type given by an enum + */ +static inline void tc_enable_callback( + struct tc_module *const module, + const enum tc_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module); + + + /* Enable interrupts for this TC module */ + system_interrupt_enable(_tc_interrupt_get_interrupt_vector(_tc_get_inst_index(module->hw))); + + /* Enable callback */ + if (callback_type == TC_CALLBACK_CC_CHANNEL0) { + module->enable_callback_mask |= TC_INTFLAG_MC(1); + module->hw->COUNT8.INTENSET.reg = TC_INTFLAG_MC(1); + } + else if (callback_type == TC_CALLBACK_CC_CHANNEL1) { + module->enable_callback_mask |= TC_INTFLAG_MC(2); + module->hw->COUNT8.INTENSET.reg = TC_INTFLAG_MC(2); + } + else { + module->enable_callback_mask |= (1 << callback_type); + module->hw->COUNT8.INTENSET.reg = (1 << callback_type); + } +} + +/** + * \brief Disables callback. + * + * Disables the callback function registered by the \ref + * tc_register_callback, and the callback will not be called from the + * interrupt routine. The function will also disable the appropriate + * interrupts. + * + * \param[in] module Pointer to TC software instance struct + * \param[in] callback_type Callback type given by an enum + */ +static inline void tc_disable_callback( + struct tc_module *const module, + const enum tc_callback callback_type){ + /* Sanity check arguments */ + Assert(module); + + /* Disable callback */ + if (callback_type == TC_CALLBACK_CC_CHANNEL0) { + module->hw->COUNT8.INTENCLR.reg = TC_INTFLAG_MC(1); + module->enable_callback_mask &= ~TC_INTFLAG_MC(1); + } + else if (callback_type == TC_CALLBACK_CC_CHANNEL1) { + module->hw->COUNT8.INTENCLR.reg = TC_INTFLAG_MC(2); + module->enable_callback_mask &= ~TC_INTFLAG_MC(2); + } + else { + module->hw->COUNT8.INTENCLR.reg = (1 << callback_type); + module->enable_callback_mask &= ~(1 << callback_type); + } +} + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* TC_INTERRUPT_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/tc/tc_sam_d_r/tc.c b/atmel-samd/asf/sam0/drivers/tc/tc_sam_d_r/tc.c new file mode 100644 index 0000000000..546c36cd17 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/tc/tc_sam_d_r/tc.c @@ -0,0 +1,685 @@ +/** + * \file + * + * \brief SAM TC - Timer Counter Driver + * + * Copyright (C) 2013-2015 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 + * + */ +/* + * Support and FAQ: visit Atmel Support + */ + +#include "tc.h" + +#if TC_ASYNC == true +# include "tc_interrupt.h" +# include + +/** \internal + * Converts a given TC index to its interrupt vector index. + */ +# define _TC_INTERRUPT_VECT_NUM(n, unused) \ + SYSTEM_INTERRUPT_MODULE_TC##n, +#endif + +#if !defined(__DOXYGEN__) +# define _TC_GCLK_ID(n,unused) TPASTE3(TC,n,_GCLK_ID) , +# define _TC_PM_APBCMASK(n,unused) TPASTE2(PM_APBCMASK_TC,n) , + +# define TC_INST_GCLK_ID { MRECURSION(TC_INST_NUM, _TC_GCLK_ID, TC_INST_MAX_ID) } +# define TC_INST_PM_APBCMASK { MRECURSION(TC_INST_NUM, _TC_PM_APBCMASK, TC_INST_MAX_ID) } + +#endif + +/** + * \internal Find the index of given TC module instance. + * + * \param[in] TC module instance pointer + * + * \return Index of the given TC module instance. + */ +uint8_t _tc_get_inst_index( + Tc *const hw) +{ + /* List of available TC modules. */ + Tc *const tc_modules[TC_INST_NUM] = TC_INSTS; + + /* Find index for TC instance. */ + for (uint32_t i = 0; i < TC_INST_NUM; i++) { + if (hw == tc_modules[i]) { + return i; + } + } + + /* Invalid data given. */ + Assert(false); + return 0; +} + + +/** + * \brief Initializes a hardware TC module instance. + * + * Enables the clock and initializes the TC module, based on the given + * configuration values. + * + * \param[in,out] module_inst Pointer to the software module instance struct + * \param[in] hw Pointer to the TC hardware module + * \param[in] config Pointer to the TC configuration options struct + * + * \return Status of the initialization procedure. + * + * \retval STATUS_OK The module was initialized successfully + * \retval STATUS_BUSY Hardware module was busy when the + * initialization procedure was attempted + * \retval STATUS_INVALID_ARG An invalid configuration option or argument + * was supplied + * \retval STATUS_ERR_DENIED Hardware module was already enabled, or the + * hardware module is configured in 32-bit + * slave mode + */ +enum status_code tc_init( + struct tc_module *const module_inst, + Tc *const hw, + const struct tc_config *const config) +{ + /* Sanity check arguments */ + Assert(hw); + Assert(module_inst); + Assert(config); + + /* Temporary variable to hold all updates to the CTRLA + * register before they are written to it */ + uint16_t ctrla_tmp = 0; + /* Temporary variable to hold all updates to the CTRLBSET + * register before they are written to it */ + uint8_t ctrlbset_tmp = 0; + /* Temporary variable to hold all updates to the CTRLC + * register before they are written to it */ + uint8_t ctrlc_tmp = 0; + /* Temporary variable to hold TC instance number */ + uint8_t instance = _tc_get_inst_index(hw); + + /* Array of GLCK ID for different TC instances */ + uint8_t inst_gclk_id[] = TC_INST_GCLK_ID; + /* Array of PM APBC mask bit position for different TC instances */ + uint16_t inst_pm_apbmask[] = TC_INST_PM_APBCMASK; + + struct system_pinmux_config pin_config; + struct system_gclk_chan_config gclk_chan_config; + +#if TC_ASYNC == true + /* Initialize parameters */ + for (uint8_t i = 0; i < TC_CALLBACK_N; i++) { + module_inst->callback[i] = NULL; + } + module_inst->register_callback_mask = 0x00; + module_inst->enable_callback_mask = 0x00; + + /* Register this instance for callbacks*/ + _tc_instances[instance] = module_inst; +#endif + + /* Associate the given device instance with the hardware module */ + module_inst->hw = hw; + +#if SAMD09 || SAMD10 || SAMD11 + /* Check if even numbered TC modules are being configured in 32-bit + * counter size. Only odd numbered counters are allowed to be + * configured in 32-bit counter size. + */ + if ((config->counter_size == TC_COUNTER_SIZE_32BIT) && + !((instance + TC_INSTANCE_OFFSET) & 0x01)) { + Assert(false); + return STATUS_ERR_INVALID_ARG; + } +#else + /* Check if odd numbered TC modules are being configured in 32-bit + * counter size. Only even numbered counters are allowed to be + * configured in 32-bit counter size. + */ + if ((config->counter_size == TC_COUNTER_SIZE_32BIT) && + ((instance + TC_INSTANCE_OFFSET) & 0x01)) { + Assert(false); + return STATUS_ERR_INVALID_ARG; + } +#endif + + /* Make the counter size variable in the module_inst struct reflect + * the counter size in the module + */ + module_inst->counter_size = config->counter_size; + + if (hw->COUNT8.CTRLA.reg & TC_CTRLA_SWRST) { + /* We are in the middle of a reset. Abort. */ + return STATUS_BUSY; + } + + if (hw->COUNT8.STATUS.reg & TC_STATUS_SLAVE) { + /* Module is used as a slave */ + return STATUS_ERR_DENIED; + } + + if (hw->COUNT8.CTRLA.reg & TC_CTRLA_ENABLE) { + /* Module must be disabled before initialization. Abort. */ + return STATUS_ERR_DENIED; + } + + /* Set up the TC PWM out pin for channel 0 */ + if (config->pwm_channel[0].enabled) { + system_pinmux_get_config_defaults(&pin_config); + pin_config.mux_position = config->pwm_channel[0].pin_mux; + pin_config.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT; + system_pinmux_pin_set_config( + config->pwm_channel[0].pin_out, &pin_config); + } + + /* Set up the TC PWM out pin for channel 1 */ + if (config->pwm_channel[1].enabled) { + system_pinmux_get_config_defaults(&pin_config); + pin_config.mux_position = config->pwm_channel[1].pin_mux; + pin_config.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT; + system_pinmux_pin_set_config( + config->pwm_channel[1].pin_out, &pin_config); + } + + /* Enable the user interface clock in the PM */ + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, + inst_pm_apbmask[instance]); + + /* Enable the slave counter if counter_size is 32-bit */ + if ((config->counter_size == TC_COUNTER_SIZE_32BIT)) + { + /* Enable the user interface clock in the PM */ + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, + inst_pm_apbmask[instance + 1]); + } + + /* Setup clock for module */ + system_gclk_chan_get_config_defaults(&gclk_chan_config); + gclk_chan_config.source_generator = config->clock_source; + system_gclk_chan_set_config(inst_gclk_id[instance], &gclk_chan_config); + system_gclk_chan_enable(inst_gclk_id[instance]); + + /* Set ctrla register */ + ctrla_tmp = + (uint32_t)config->counter_size | + (uint32_t)config->wave_generation | + (uint32_t)config->reload_action | + (uint32_t)config->clock_prescaler; + + if (config->run_in_standby) { + ctrla_tmp |= TC_CTRLA_RUNSTDBY; + } + + /* Write configuration to register */ + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + hw->COUNT8.CTRLA.reg = ctrla_tmp; + + /* Set ctrlb register */ + if (config->oneshot) { + ctrlbset_tmp = TC_CTRLBSET_ONESHOT; + } + + if (config->count_direction) { + ctrlbset_tmp |= TC_CTRLBSET_DIR; + } + + /* Clear old ctrlb configuration */ + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + hw->COUNT8.CTRLBCLR.reg = 0xFF; + + /* Check if we actually need to go into a wait state. */ + if (ctrlbset_tmp) { + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + /* Write configuration to register */ + hw->COUNT8.CTRLBSET.reg = ctrlbset_tmp; + } + + /* Set ctrlc register*/ + ctrlc_tmp = config->waveform_invert_output; + for (uint8_t i = 0; i < NUMBER_OF_COMPARE_CAPTURE_CHANNELS; i++) { + if (config->enable_capture_on_channel[i] == true) { + ctrlc_tmp |= (TC_CTRLC_CPTEN(1) << i); + } + } + + /* Write configuration to register */ + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + hw->COUNT8.CTRLC.reg = ctrlc_tmp; + + /* Write configuration to register */ + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + /* Switch for TC counter size */ + switch (module_inst->counter_size) { + case TC_COUNTER_SIZE_8BIT: + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + hw->COUNT8.COUNT.reg = + config->counter_8_bit.value; + + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + hw->COUNT8.PER.reg = + config->counter_8_bit.period; + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + hw->COUNT8.CC[0].reg = + config->counter_8_bit.compare_capture_channel[0]; + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + hw->COUNT8.CC[1].reg = + config->counter_8_bit.compare_capture_channel[1]; + + return STATUS_OK; + + case TC_COUNTER_SIZE_16BIT: + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + hw->COUNT16.COUNT.reg + = config->counter_16_bit.value; + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + hw->COUNT16.CC[0].reg = + config->counter_16_bit.compare_capture_channel[0]; + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + hw->COUNT16.CC[1].reg = + config->counter_16_bit.compare_capture_channel[1]; + + return STATUS_OK; + + case TC_COUNTER_SIZE_32BIT: + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + hw->COUNT32.COUNT.reg + = config->counter_32_bit.value; + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + hw->COUNT32.CC[0].reg = + config->counter_32_bit.compare_capture_channel[0]; + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + hw->COUNT32.CC[1].reg = + config->counter_32_bit.compare_capture_channel[1]; + + return STATUS_OK; + } + + Assert(false); + return STATUS_ERR_INVALID_ARG; +} + +/** + * \brief Sets TC module count value. + * + * Sets the current timer count value of a initialized TC module. The + * specified TC module may be started or stopped. + * + * \param[in] module_inst Pointer to the software module instance struct + * \param[in] count New timer count value to set + * + * \return Status of the count update procedure. + * + * \retval STATUS_OK The timer count was updated successfully + * \retval STATUS_ERR_INVALID_ARG An invalid timer counter size was specified + */ +enum status_code tc_set_count_value( + const struct tc_module *const module_inst, + const uint32_t count) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance*/ + Tc *const tc_module = module_inst->hw; + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + /* Write to based on the TC counter_size */ + switch (module_inst->counter_size) { + case TC_COUNTER_SIZE_8BIT: + tc_module->COUNT8.COUNT.reg = (uint8_t)count; + return STATUS_OK; + + case TC_COUNTER_SIZE_16BIT: + tc_module->COUNT16.COUNT.reg = (uint16_t)count; + return STATUS_OK; + + case TC_COUNTER_SIZE_32BIT: + tc_module->COUNT32.COUNT.reg = (uint32_t)count; + return STATUS_OK; + + default: + return STATUS_ERR_INVALID_ARG; + } +} + +/** + * \brief Get TC module count value. + * + * Retrieves the current count value of a TC module. The specified TC module + * may be started or stopped. + * + * \param[in] module_inst Pointer to the software module instance struct + * + * \return Count value of the specified TC module. + */ +uint32_t tc_get_count_value( + const struct tc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + Tc *const tc_module = module_inst->hw; + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + /* Read from based on the TC counter size */ + switch (module_inst->counter_size) { + case TC_COUNTER_SIZE_8BIT: + return (uint32_t)tc_module->COUNT8.COUNT.reg; + + case TC_COUNTER_SIZE_16BIT: + return (uint32_t)tc_module->COUNT16.COUNT.reg; + + case TC_COUNTER_SIZE_32BIT: + return tc_module->COUNT32.COUNT.reg; + } + + Assert(false); + return 0; +} + +/** + * \brief Gets the TC module capture value. + * + * Retrieves the capture value in the indicated TC module capture channel. + * + * \param[in] module_inst Pointer to the software module instance struct + * \param[in] channel_index Index of the Compare Capture channel to read + * + * \return Capture value stored in the specified timer channel. + */ +uint32_t tc_get_capture_value( + const struct tc_module *const module_inst, + const enum tc_compare_capture_channel channel_index) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + Tc *const tc_module = module_inst->hw; + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + /* Read out based on the TC counter size */ + switch (module_inst->counter_size) { + case TC_COUNTER_SIZE_8BIT: + if (channel_index < + NUMBER_OF_COMPARE_CAPTURE_CHANNELS) { + return tc_module->COUNT8.CC[channel_index].reg; + } + + case TC_COUNTER_SIZE_16BIT: + if (channel_index < + NUMBER_OF_COMPARE_CAPTURE_CHANNELS) { + return tc_module->COUNT16.CC[channel_index].reg; + } + + case TC_COUNTER_SIZE_32BIT: + if (channel_index < + NUMBER_OF_COMPARE_CAPTURE_CHANNELS) { + return tc_module->COUNT32.CC[channel_index].reg; + } + } + + Assert(false); + return 0; +} + +/** + * \brief Sets a TC module compare value. + * + * Writes a compare value to the given TC module compare/capture channel. + * + * \param[in] module_inst Pointer to the software module instance struct + * \param[in] channel_index Index of the compare channel to write to + * \param[in] compare New compare value to set + * + * \return Status of the compare update procedure. + * + * \retval STATUS_OK The compare value was updated successfully + * \retval STATUS_ERR_INVALID_ARG An invalid channel index was supplied + */ +enum status_code tc_set_compare_value( + const struct tc_module *const module_inst, + const enum tc_compare_capture_channel channel_index, + const uint32_t compare) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + Tc *const tc_module = module_inst->hw; + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + /* Read out based on the TC counter size */ + switch (module_inst->counter_size) { + case TC_COUNTER_SIZE_8BIT: + if (channel_index < + NUMBER_OF_COMPARE_CAPTURE_CHANNELS) { + tc_module->COUNT8.CC[channel_index].reg = + (uint8_t)compare; + return STATUS_OK; + } + + case TC_COUNTER_SIZE_16BIT: + if (channel_index < + NUMBER_OF_COMPARE_CAPTURE_CHANNELS) { + tc_module->COUNT16.CC[channel_index].reg = + (uint16_t)compare; + return STATUS_OK; + } + + case TC_COUNTER_SIZE_32BIT: + if (channel_index < + NUMBER_OF_COMPARE_CAPTURE_CHANNELS) { + tc_module->COUNT32.CC[channel_index].reg = + (uint32_t)compare; + return STATUS_OK; + } + } + + return STATUS_ERR_INVALID_ARG; +} + +/** + * \brief Resets the TC module. + * + * Resets the TC module, restoring all hardware module registers to their + * default values and disabling the module. The TC module will not be + * accessible while the reset is being performed. + * + * \note When resetting a 32-bit counter only the master TC module's instance + * structure should be passed to the function. + * + * \param[in] module_inst Pointer to the software module instance struct + * + * \return Status of the procedure. + * \retval STATUS_OK The module was reset successfully + * \retval STATUS_ERR_UNSUPPORTED_DEV A 32-bit slave TC module was passed to + * the function. Only use reset on master + * TC + */ +enum status_code tc_reset( + const struct tc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module hardware instance */ + TcCount8 *const tc_module = &(module_inst->hw->COUNT8); + + if (tc_module->STATUS.reg & TC_STATUS_SLAVE) { + return STATUS_ERR_UNSUPPORTED_DEV; + } + + /* Disable this module if it is running */ + if (tc_module->CTRLA.reg & TC_CTRLA_ENABLE) { + tc_disable(module_inst); + while (tc_is_syncing(module_inst)) { + /* wait while module is disabling */ + } + } + + /* Reset this TC module */ + tc_module->CTRLA.reg |= TC_CTRLA_SWRST; + + return STATUS_OK; +} + +/** + * \brief Set the timer TOP/period value. + * + * For 8-bit counter size this function writes the top value to the period + * register. + * + * For 16- and 32-bit counter size this function writes the top value to + * Capture Compare register 0. The value in this register can not be used for + * any other purpose. + * + * \note This function is designed to be used in PWM or frequency + * match modes only, when the counter is set to 16- or 32-bit counter + * size. In 8-bit counter size it will always be possible to change the + * top value even in normal mode. + * + * \param[in] module_inst Pointer to the software module instance struct + * \param[in] top_value New timer TOP value to set + * + * \return Status of the TOP set procedure. + * + * \retval STATUS_OK The timer TOP value was updated successfully + * \retval STATUS_ERR_INVALID_ARG The configured TC module counter size in the + * module instance is invalid + */ +enum status_code tc_set_top_value ( + const struct tc_module *const module_inst, + const uint32_t top_value) +{ + Assert(module_inst); + Assert(module_inst->hw); + Assert(top_value); + + Tc *const tc_module = module_inst->hw; + + while (tc_is_syncing(module_inst)) { + /* Wait for sync */ + } + + switch (module_inst->counter_size) { + case TC_COUNTER_SIZE_8BIT: + tc_module->COUNT8.PER.reg = (uint8_t)top_value; + return STATUS_OK; + + case TC_COUNTER_SIZE_16BIT: + tc_module->COUNT16.CC[0].reg = (uint16_t)top_value; + return STATUS_OK; + + case TC_COUNTER_SIZE_32BIT: + tc_module->COUNT32.CC[0].reg = (uint32_t)top_value; + return STATUS_OK; + + default: + Assert(false); + return STATUS_ERR_INVALID_ARG; + } +} diff --git a/atmel-samd/asf/sam0/drivers/tcc/docimg/overview.svg b/atmel-samd/asf/sam0/drivers/tcc/docimg/overview.svg new file mode 100644 index 0000000000..b3d7ef0b38 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/tcc/docimg/overview.svg @@ -0,0 +1,2278 @@ + + + + + TCC_Block + + + + + image/svg+xml + + TCC_Block + + + + + + + + Base Counter + + + + + + + + + Compare + / + Capture + ( + Unit x + = + { + 0 + , + 1 + , + … + , + 3 + } + ) + + + Counter + + + + + + + = + + + CCx + + + CCBx + + + + + + + Waveform + Generation + + + + + + + + + + + + BV + + + + + = + + + PER + B + + + PER + + + COUNT + + + BV + + + = + 0 + " + count + " + " + clear + " + " + direction + " + " + load + " + + + + + + + + + + + Control Logic + + + Prescaler + + + OVF + ( + INT + / + Event + / + DMA Req + .) + + + ERR + ( + INT Req + .) + TOP + " + match + " + + + + + + + + + MC + x + ( + INT + / + Event + / + DMA Req + .) + + + + + Control Logic + " + capture + " + + + + + + + " + e + v + " + U + P + D + A + T + E + BOTTOM + + + + + + + + Recoverable + Faults + + + + + + + O + u + t + p + u + t + M + a + t + r + i + x + + + + D + e + a + d + - + T + i + m + e + I + n + s + e + r + t + i + o + n + + + SWAP + + P + a + t + t + e + r + n + G + e + n + e + r + a + t + i + o + n + + N + o + n + - + r + e + c + o + v + e + r + a + b + l + e + F + a + u + l + t + s + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WO + [ + 0 + ] + WO + [ + 1 + ] + WO + [ + 2 + ] + WO + [ + 3 + ] + WO + [ + 4 + ] + WO + [ + 5 + ] + WO + [ + 6 + ] + WO + [ + 7 + ] + + + Event + System + + + "TCCx_EV0" + + + + + + + + + "TCCx_EV1" + "TCCx_MCx" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/atmel-samd/asf/sam0/drivers/tcc/docimg/pwm_match_ex.svg b/atmel-samd/asf/sam0/drivers/tcc/docimg/pwm_match_ex.svg new file mode 100644 index 0000000000..dbce68d31f --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/tcc/docimg/pwm_match_ex.svg @@ -0,0 +1,1464 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + Master slide + + + + + + + + + + + + + + + T + i + m + e + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + R + e + l + o + a + d + + t + o + + T + O + P + + v + a + l + u + e + + + + + + + + + + + + + + + + + + C + o + u + n + t + + i + s + + w + r + i + t + t + e + n + + + + + + + + + + + + + + + + T + O + P + + v + a + l + u + e + + h + a + s + + b + e + e + n + + c + h + a + n + g + e + d + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + M + a + t + c + h + + + + + + + + + P + W + M + + i + n + v + e + r + t + e + d + + o + u + t + p + u + t + + + + + + M + a + x + + + + + + C + o + u + n + t + e + r + + v + a + l + u + e + + + + + + + + + + + + + + + + + + + + + + + (COUNT) + (CC0) + (CC0) + (COUNT) + Compare/Matchvalue(CCx) + + diff --git a/atmel-samd/asf/sam0/drivers/tcc/docimg/pwm_single_ex.svg b/atmel-samd/asf/sam0/drivers/tcc/docimg/pwm_single_ex.svg new file mode 100644 index 0000000000..8450d04408 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/tcc/docimg/pwm_single_ex.svg @@ -0,0 +1,1387 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + Master slide + + + + + + + C + o + u + n + t + e + r + + v + a + l + u + e + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + T + i + m + e + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + M + a + t + c + h + + + + + + + + + + + + + + + + + + + + + + + + P + W + M + + o + u + t + p + u + t + + + + + + R + e + l + o + a + d + + c + o + u + n + t + e + r + + t + o + + z + e + r + o + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + C + o + m + p + a + r + e + + v + a + l + u + e + + h + a + s + + b + e + e + n + + c + h + a + n + g + e + d + + + + + + + + + + + + + + + + + + + + + + + TOP/Period= Max(PER) + + + + + Compare/Matchvalue(CCx) + + + + + + (CCx) + (COUNT) + + diff --git a/atmel-samd/asf/sam0/drivers/tcc/tcc.c b/atmel-samd/asf/sam0/drivers/tcc/tcc.c new file mode 100644 index 0000000000..6b5f8fb5c7 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/tcc/tcc.c @@ -0,0 +1,1604 @@ +/** + * \file + * + * \brief SAM TCC - Timer Counter for Control Applications Driver + * + * Copyright (C) 2013-2016 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 + * + */ +/* + * Support and FAQ: visit Atmel Support + */ + +#include "tcc.h" + +#if TCC_ASYNC == true +# include "tcc_callback.h" +# include + +/** \internal + * Converts a given TCC index to its interrupt vector index. + */ +# define _TCC_INTERRUPT_VECT_NUM(n, unused) \ + SYSTEM_INTERRUPT_MODULE_TCC##n, +#endif + +#define _SIZE_MAX(size) ((size==32u) ? 0xFFFFFFFF : ( \ + (1u << size) - 1)) + +#define _SIZE_MAX_WITH_DITHER 0x03FFFFFF + +/* Extension support mapping bits */ +#define _TCC_DITHERING_B 16u +#define _TCC_PG_B 8u +#define _TCC_SWAP_B 4u +#define _TCC_DTI_B 2u +#define _TCC_OTMX_B 1u + +#if !defined(__DOXYGEN__) + +# define _TCC_GCLK_ID(n,unused) TPASTE3(TCC,n,_GCLK_ID), +# if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30) +# define _TCC_APBCMASK(n,unused) TPASTE2(MCLK_APBCMASK_TCC,n), +# else +# define _TCC_APBCMASK(n,unused) TPASTE2(PM_APBCMASK_TCC,n), +# endif + +# define _TCC_SIZE(n,unused) TPASTE3(TCC,n,_SIZE), +# define _TCC_MAX(n,unused) _SIZE_MAX(TPASTE3(TCC,n,_SIZE)), +# define _TCC_EXT(n,unused) TPASTE3(TCC,n,_EXT), +# define _TCC_CC_NUM(n,unused) min(TPASTE3(TCC,n,_CC_NUM),TCC_NUM_CHANNELS), +# define _TCC_OW_NUM(n,unused) min(TPASTE3(TCC,n,_OW_NUM),TCC_NUM_WAVE_OUTPUTS), + +# define TCC_GCLK_IDS { MREPEAT(TCC_INST_NUM, _TCC_GCLK_ID, 0) } +# define TCC_APBCMASKS { MREPEAT(TCC_INST_NUM, _TCC_APBCMASK, 0) } + +# define TCC_SIZES { MREPEAT(TCC_INST_NUM, _TCC_SIZE, 0) } +# define TCC_MAXS { MREPEAT(TCC_INST_NUM, _TCC_MAX, 0) } +# define TCC_EXTS { MREPEAT(TCC_INST_NUM, _TCC_EXT, 0) } +# define TCC_CC_NUMS { MREPEAT(TCC_INST_NUM, _TCC_CC_NUM, 0) } +# define TCC_OW_NUMS { MREPEAT(TCC_INST_NUM, _TCC_OW_NUM, 0) } + +#endif + +/* List of available TCC modules. */ +const Tcc *const tcc_modules[TCC_INST_NUM] = TCC_INSTS; + +/* List of TCC GCLK IDs */ +const uint8_t _tcc_gclk_ids[TCC_INST_NUM] = TCC_GCLK_IDS; + +/* List of TCC APBC Masks */ +const uint32_t _tcc_apbcmasks[TCC_INST_NUM] = TCC_APBCMASKS; + +/* List of extension support of TCC modules. */ +const uint8_t _tcc_exts[TCC_INST_NUM] = TCC_EXTS; + +/* List of sizes support of TCC modules. */ +const uint8_t _tcc_sizes[TCC_INST_NUM] = TCC_SIZES; + +/* List of maximumvalues supported of TCC modules. */ +const uint32_t _tcc_maxs[TCC_INST_NUM] = TCC_MAXS; + +/* List of available channel number of TCC modules. */ +const uint8_t _tcc_cc_nums[TCC_INST_NUM] = TCC_CC_NUMS; + +/* List of available output number of TCC modules. */ +const uint8_t _tcc_ow_nums[TCC_INST_NUM] = TCC_OW_NUMS; + +/** + * \internal Find the index of the given TCC module instance. + * + * \param[in] The TCC module instance pointer + * + * \return Index of the given TCC module instance. + */ +uint8_t _tcc_get_inst_index( + Tcc *const hw) +{ + /* Find index for TCC instance. */ + for (uint32_t i = 0; i < TCC_INST_NUM; i++) { + if (hw == tcc_modules[i]) { + return i; + } + } + + /* Invalid data given. */ + Assert(false); + return 0; +} + +/** + * \brief Initializes config with predefined default values. + * + * This function will initialize a given TCC configuration structure to + * a set of known default values. This function should be called on + * any new instance of the configuration structures before being + * modified by the user application. + * + * The default configuration is as follows: + * \li Don't run in standby + * \li When setting top, compare, or pattern by API, do double buffering write + * \li The base timer/counter configurations: + * - GCLK generator 0 clock source + * - No prescaler + * - GCLK reload action + * - Count upward + * - Don't perform one-shot operations + * - Counter starts on 0 + * - Period/top value set to maximum + * \li The match/capture configurations: + * - All Capture compare channel value set to 0 + * - No capture enabled (all channels use compare function) + * - Normal frequency wave generation + * - Waveform generation polarity set to 0 + * - Don't perform ramp on waveform + * \li The waveform extension configurations: + * - No recoverable fault is enabled, fault actions are disabled, filter + * is set to 0 + * - No non-recoverable fault state output is enabled and filter is 0 + * - No inversion of waveform output + * \li No channel output enabled + * \li No PWM pin output enabled + * \li Pin and MUX configuration not set + * + * \param[out] config Pointer to a TCC module configuration structure to set + * \param[in] hw Pointer to the TCC hardware module + * + */ +void tcc_get_config_defaults( + struct tcc_config *const config, + Tcc *const hw) +{ + /* TCC instance index */ + uint8_t module_index = _tcc_get_inst_index(hw); + + /* Base counter defaults */ + config->counter.count = 0; + + config->counter.period = _tcc_maxs[module_index]; + + config->counter.clock_source = GCLK_GENERATOR_0; + config->counter.clock_prescaler = TCC_CLOCK_PRESCALER_DIV1; + config->counter.reload_action = TCC_RELOAD_ACTION_GCLK; + + config->counter.direction = TCC_COUNT_DIRECTION_UP; + config->counter.oneshot = false; + +#ifdef FEATURE_TCC_GENERATE_DMA_TRIGGER + config->counter.dma_trigger_mode = TCC_COUNT_OVERFLOW_DMA_TRIGGER_MODE_CONTINUE; +#endif + + /* Match/Capture defaults */ +# define _TCC_CHANNEL_MATCH_VALUE_INIT(n, value) \ + config->compare.match[n] = value; + MREPEAT(TCC_NUM_CHANNELS, + _TCC_CHANNEL_MATCH_VALUE_INIT, 0) +# undef _TCC_CHANNEL_MATCH_VALUE_INIT + + /* Wave polarity defaults */ +# define _TCC_CHANNEL_WAVE_POLARITY_INIT(n, value) \ + config->compare.wave_polarity[n] = value; + MREPEAT(TCC_NUM_CHANNELS, + _TCC_CHANNEL_WAVE_POLARITY_INIT, TCC_WAVE_POLARITY_0) +# undef _TCC_CHANNEL_WAVE_POLARITY_INIT + + config->compare.wave_generation = TCC_WAVE_GENERATION_NORMAL_FREQ; + config->compare.wave_ramp = TCC_RAMP_RAMP1; + +# define _TCC_CHANNEL_FUNCTION_INIT(n, value) \ + config->compare.channel_function[n] = value; + MREPEAT(TCC_NUM_CHANNELS, + _TCC_CHANNEL_FUNCTION_INIT, TCC_CHANNEL_FUNCTION_COMPARE) +# undef _TCC_CHANNEL_FUNCTION_INIT + + /* Recoverable fault defaults */ +# define _TCC_FAULT_FUNCTION_INIT(n, dummy) \ + config->wave_ext.recoverable_fault[n].filter_value = 0; \ + config->wave_ext.recoverable_fault[n].blanking_cycles = 0; \ + config->wave_ext.recoverable_fault[n].restart = false; \ + config->wave_ext.recoverable_fault[n].keep = false; \ + config->wave_ext.recoverable_fault[n].qualification = false; \ + config->wave_ext.recoverable_fault[n].source = TCC_FAULT_SOURCE_DISABLE; \ + config->wave_ext.recoverable_fault[n].blanking = TCC_FAULT_BLANKING_DISABLE; \ + config->wave_ext.recoverable_fault[n].halt_action = TCC_FAULT_HALT_ACTION_DISABLE; \ + config->wave_ext.recoverable_fault[n].capture_action = TCC_FAULT_CAPTURE_DISABLE; \ + config->wave_ext.recoverable_fault[n].capture_channel = TCC_FAULT_CAPTURE_CHANNEL_0; + MREPEAT(TCC_NUM_FAULTS, _TCC_FAULT_FUNCTION_INIT, 0) +# undef _TCC_FAULT_FUNCTION_INIT + + /* Non-recoverable fault defaults */ +# define _TCC_NRF_FUNCTION_INIT(n, dummy) \ + config->wave_ext.non_recoverable_fault[n].filter_value = 0; \ + config->wave_ext.non_recoverable_fault[n].output = TCC_FAULT_STATE_OUTPUT_OFF; + MREPEAT(TCC_NUM_WAVE_OUTPUTS, _TCC_NRF_FUNCTION_INIT, 0) +# undef _TCC_NRF_FUNCTION_INIT + + /* Output inversion defaults */ +# define _TCC_OUT_INVERT_INIT(n, value) \ + config->wave_ext.invert[n] = value; + MREPEAT(TCC_NUM_WAVE_OUTPUTS, _TCC_OUT_INVERT_INIT, false) +# undef _TCC_OUT_INVERT_INIT + +# define _TCC_CHANNEL_OUT_PIN_INIT(n, dummy) \ + config->pins.enable_wave_out_pin[n] = false;\ + config->pins.wave_out_pin[TCC_WAVE_OUTPUT_##n] = 0; \ + config->pins.wave_out_pin_mux[TCC_WAVE_OUTPUT_##n] = 0; + MREPEAT(TCC_NUM_WAVE_OUTPUTS, _TCC_CHANNEL_OUT_PIN_INIT, 0) +# undef _TCC_CHANNEL_OUT_PIN_INIT + + config->double_buffering_enabled = true; + config->run_in_standby = false; +} + + +/** + * \brief Build CTRLA register value from configuration. + * + * \param[in] module_index The software module instance index + * \param[in] config Pointer to the TCC configuration options struct + * \param[out] value_buffer Pointer to the buffer to fill with built value + * + * \return Configuration validation status. + * + * \retval STATUS_OK Configuration values are good and register + * value built and save to buffer + * \retval STATUS_ERR_INVALID_ARG Invalid parameter found: + * assigned dither mode is invalid for module; + * used capture channel is invalid for module + */ +static inline enum status_code _tcc_build_ctrla( + const uint8_t module_index, + const struct tcc_config *const config, + uint32_t *value_buffer) +{ + uint32_t ctrla = 0; + + int i; + for (i = 0; i < TCC_NUM_CHANNELS; i ++) { + if (config->capture.channel_function[i] == + TCC_CHANNEL_FUNCTION_CAPTURE) { + + if (i > _tcc_cc_nums[module_index]) { + /* Channel not supported */ + return STATUS_ERR_INVALID_ARG; + } + ctrla |= (TCC_CTRLA_CPTEN0 << i); + } + } + + if (config->run_in_standby) { + ctrla |= TCC_CTRLA_RUNSTDBY; + } + ctrla |= config->counter.reload_action << TCC_CTRLA_PRESCSYNC_Pos; + ctrla |= config->counter.clock_prescaler << TCC_CTRLA_PRESCALER_Pos; + + *value_buffer = ctrla; + return STATUS_OK; +} + +/** + * \brief Build CTRLB register value from configuration. + * + * \param[in] module_index The software module instance index + * \param[in] config Pointer to the TCC configuration options struct + * \param[out] value_buffer Pointer to the buffer to fill with built value + */ +static inline void _tcc_build_ctrlb( + const uint8_t module_index, + const struct tcc_config *const config, + uint8_t *value_buffer) +{ + uint8_t ctrlb = 0; + + if (config->counter.oneshot) { + ctrlb |= TCC_CTRLBSET_ONESHOT; + } + if (config->counter.direction == TCC_COUNT_DIRECTION_DOWN) { + ctrlb |= TCC_CTRLBSET_DIR; + } + + *value_buffer = ctrlb; +} + +/** + * \brief Build FAULTs register values from configuration. + * + * \param[in] module_index The software module instance index + * \param[in] config Pointer to the TCC configuration options struct + * \param[out] value_buffer Pointer to the buffer to fill with built values + * + * \retval STATUS_OK Configuration values are good and register + * value built and save to buffer + * \retval STATUS_ERR_INVALID_ARG Invalid parameter found: assigned fault + * capture channel is invalid; assigned filter + * value is invalid + */ +static inline enum status_code _tcc_build_faults( + const uint8_t module_index, + const struct tcc_config *const config, + uint32_t *value_buffer) +{ + struct tcc_recoverable_fault_config *cfg; + uint8_t cc_num = _tcc_cc_nums[module_index]; + uint32_t fault; + int i; + for (i = 0; i < TCC_NUM_FAULTS; i ++) { + cfg = (struct tcc_recoverable_fault_config *) + &config->wave_ext.recoverable_fault[i]; + if (cfg->capture_channel >= cc_num) { + return STATUS_ERR_INVALID_ARG; + } + if (cfg->filter_value > 0xF) { + return STATUS_ERR_INVALID_ARG; + } + fault = TCC_FCTRLA_FILTERVAL(cfg->filter_value) + | TCC_FCTRLA_BLANKVAL(cfg->blanking_cycles) + | (cfg->restart ? TCC_FCTRLA_RESTART : 0) + | (cfg->keep ? TCC_FCTRLA_KEEP : 0) + | (cfg->qualification ? TCC_FCTRLA_QUAL : 0) + | TCC_FCTRLA_SRC(cfg->source) + | TCC_FCTRLA_BLANK(cfg->blanking) + | TCC_FCTRLA_HALT(cfg->halt_action) + | TCC_FCTRLA_CAPTURE(cfg->capture_action) + | TCC_FCTRLA_CHSEL(cfg->capture_channel); + value_buffer[i] = fault; + } + return STATUS_OK; +} + +/** + * \brief Build DRVCTRL register values from configuration. + * + * \param[in] module_index The software module instance index + * \param[in] config Pointer to the TCC configuration options struct + * \param[out] value_buffer Pointer to the buffer to fill with built value + * + * \retval STATUS_OK Configuration values are good and register + * value built and save to buffer + * \retval STATUS_ERR_INVALID_ARG Invalid parameter found: assigned output line + * is invalid; filter value is invalid + */ +static inline enum status_code _tcc_build_drvctrl( + const uint8_t module_index, + const struct tcc_config *const config, + uint32_t *value_buffer) +{ + uint32_t i; + uint8_t ow_num = _tcc_ow_nums[module_index]; + uint32_t drvctrl; + + drvctrl = 0; + + for (i = 0; i < TCC_NUM_WAVE_OUTPUTS; i ++) { + if (config->wave_ext.invert[i]) { + if (i >= ow_num) { + return STATUS_ERR_INVALID_ARG; + } + drvctrl |= (TCC_DRVCTRL_INVEN0 << i); + } + if (config->wave_ext.non_recoverable_fault[i].output != + TCC_FAULT_STATE_OUTPUT_OFF) { + if (i >= ow_num) { + return STATUS_ERR_INVALID_ARG; + } + if (config->wave_ext.non_recoverable_fault[i].output == + TCC_FAULT_STATE_OUTPUT_1) { + drvctrl |= (TCC_DRVCTRL_NRE0 | TCC_DRVCTRL_NRV0) << i; + } else { + drvctrl |= (TCC_DRVCTRL_NRE0) << i; + } + } + } + *value_buffer = drvctrl; + return STATUS_OK; +} + +/** + * \brief Build WAVE & WAVEB register values from configuration. + * + * \param[in] module_index The software module instance index + * \param[in] config Pointer to the TCC configuration options struct + * \param[out] value_buffer Pointer to the buffer to fill with built value + * + * \retval STATUS_OK Configuration values are good and register + * value built and save to buffer + * \retval STATUS_ERR_INVALID_ARG Invalid parameter found: assigned output line + * is invalid; circular and double buffering + * conflict; assigned function not supported by + * module + */ +static inline enum status_code _tcc_build_waves( + const uint8_t module_index, + const struct tcc_config *const config, + uint32_t *value_buffer) +{ + int n; + + uint8_t cc_num = _tcc_cc_nums[module_index]; + struct tcc_match_wave_config const *wav_cfg = &config->compare; + + uint32_t wave; + + wave = TCC_WAVE_RAMP(wav_cfg->wave_ramp) | + TCC_WAVE_WAVEGEN(wav_cfg->wave_generation); + + for (n = 0; n < TCC_NUM_CHANNELS; n++) { + if (wav_cfg->wave_polarity[n]) { + if (n >= cc_num) { + return STATUS_ERR_INVALID_ARG; + } + wave |= (TCC_WAVE_POL0 << n); + } + } + + value_buffer[0] = wave; + + return STATUS_OK; +} + +/** + * \brief Initializes a hardware TCC module instance. + * + * Enables the clock and initializes the given TCC module, based on the given + * configuration values. + * + * \param[in,out] module_inst Pointer to the software module instance struct + * \param[in] hw Pointer to the TCC hardware module + * \param[in] config Pointer to the TCC configuration options struct + * + * \return Status of the initialization procedure. + * + * \retval STATUS_OK The module was initialized successfully + * \retval STATUS_BUSY The hardware module was busy when the + * initialization procedure was attempted + * \retval STATUS_INVALID_ARG An invalid configuration option or argument + * was supplied + * \retval STATUS_ERR_DENIED The hardware module was already enabled + */ +enum status_code tcc_init( + struct tcc_module *const module_inst, + Tcc *const hw, + const struct tcc_config *const config) +{ + int i; + + /* Sanity check arguments */ + Assert(hw); + Assert(module_inst); + Assert(config); + + /* TCC instance index */ + uint8_t module_index = _tcc_get_inst_index(hw); + + /* Enable the user interface clock for TCC */ + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, + _tcc_apbcmasks[module_index]); + + /* Check if it's enabled. */ + if (hw->CTRLA.reg & TCC_CTRLA_ENABLE) { + return STATUS_ERR_DENIED; + } + /* Check if it's resetting */ + if (hw->CTRLA.reg & TCC_CTRLA_SWRST) { + return STATUS_ERR_DENIED; + } + + enum status_code status; + + /* Check COUNT, PER, CCx */ + uint32_t count_max = _tcc_maxs[module_index]; + + /* Check all counter values */ + if ((config->counter.count > count_max) + || (config->counter.period > count_max) + ) { + return STATUS_ERR_INVALID_ARG; + } + + /* Check all channel values */ + for (i = 0; i < TCC_NUM_CHANNELS; i ++) { + if ((config->compare.match[i] > count_max) + ) { + return STATUS_ERR_INVALID_ARG; + } + } + + /* Check all outputs */ + for (i = 0; i < TCC_NUM_WAVE_OUTPUTS; i ++) { + if (!config->pins.enable_wave_out_pin[i]) { + continue; + } + /* Output line is not supported */ + if (i >= _tcc_ow_nums[module_index]) { + return STATUS_ERR_INVALID_ARG; + } + } + + /* CTRLA settings */ + uint32_t ctrla = 0; + status = _tcc_build_ctrla(module_index, config, &ctrla); + if (STATUS_OK != status) { + return status; + } + + /* CTRLB settings */ + uint8_t ctrlb; + _tcc_build_ctrlb(module_index, config, &ctrlb); + + /* FAULTs settings */ + uint32_t faults[TCC_NUM_FAULTS]; + + status = _tcc_build_faults(module_index, config, faults); + if (STATUS_OK != status) { + return status; + } + + /* DRVCTRL */ + uint32_t drvctrl = 0; + + status = _tcc_build_drvctrl(module_index, config, &drvctrl); + if (STATUS_OK != status) { + return status; + } + + /* WAVE */ + uint32_t waves[1]; + + status = _tcc_build_waves(module_index, config, waves); + if (STATUS_OK != status) { + return status; + } + + /* Initialize module */ +#if TCC_ASYNC + /* Initialize parameters */ + for (i = 0; i < TCC_CALLBACK_N; i ++) { + module_inst->callback[i] = NULL; + } + module_inst->register_callback_mask = 0; + module_inst->enable_callback_mask = 0; + _tcc_instances[module_index] = module_inst; +#endif + + module_inst->hw = hw; + + module_inst->double_buffering_enabled = config->double_buffering_enabled; + + /* Setup clock for module */ + struct system_gclk_chan_config gclk_chan_config; + system_gclk_chan_get_config_defaults(&gclk_chan_config); + gclk_chan_config.source_generator = config->counter.clock_source; + system_gclk_chan_set_config(_tcc_gclk_ids[module_index], &gclk_chan_config); + system_gclk_chan_enable(_tcc_gclk_ids[module_index]); + + /* Initialize pins */ + struct system_pinmux_config pin_config; + for (i = 0; i < _tcc_ow_nums[module_index]; i ++) { + if (!config->pins.enable_wave_out_pin[i]) { + continue; + } + + system_pinmux_get_config_defaults(&pin_config); + pin_config.mux_position = config->pins.wave_out_pin_mux[i]; + pin_config.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT; + system_pinmux_pin_set_config( + config->pins.wave_out_pin[i], &pin_config); + } + + /* Write to registers */ + + hw->CTRLA.reg = ctrla; + while (hw->SYNCBUSY.reg & TCC_SYNCBUSY_CTRLB) { + /* Wait for sync */ + } + + hw->CTRLBCLR.reg = 0xFF; + while (hw->SYNCBUSY.reg & TCC_SYNCBUSY_CTRLB) { + /* Wait for sync */ + } + hw->CTRLBSET.reg = ctrlb; + + hw->FCTRLA.reg = faults[0]; + hw->FCTRLB.reg = faults[1]; + + hw->DRVCTRL.reg = drvctrl; + +#if (!SAML21) && (!SAMC20) && (!SAMC21) && (!SAML22) && (!SAMR30) + while (hw->SYNCBUSY.reg & (TCC_SYNCBUSY_WAVE | TCC_SYNCBUSY_WAVEB)) { + /* Wait for sync */ + } +#endif + hw->WAVE.reg = waves[0]; + + while (hw->SYNCBUSY.reg & TCC_SYNCBUSY_COUNT) { + /* Wait for sync */ + } + hw->COUNT.reg = config->counter.count; + +#if (!SAML21) && (!SAMC20) && (!SAMC21) && (!SAML22) && (!SAMR30) + while (hw->SYNCBUSY.reg & (TCC_SYNCBUSY_PER | TCC_SYNCBUSY_PERB)) { + /* Wait for sync */ + } +#endif + hw->PER.reg = (config->counter.period); + + for (i = 0; i < _tcc_cc_nums[module_index]; i ++) { +#if (!SAML21) && (!SAMC20) && (!SAMC21) && (!SAML22) && (!SAMR30) + while (hw->SYNCBUSY.reg & ( + (TCC_SYNCBUSY_CC0 | TCC_SYNCBUSY_CCB0) << i)) { + /* Wait for sync */ + } +#endif + hw->CC[i].reg = (config->compare.match[i]); + } + + return STATUS_OK; +} + + +/** + * \brief Enables the TCC module event input or output. + * + * Enables one or more input or output events to or from the TCC module. + * See \ref tcc_events for a list of events this module supports. + * + * \note Events cannot be altered while the module is enabled. + * + * \param[in] module_inst Pointer to the software module instance struct + * \param[in] events Struct containing flags of events to enable or + * configure + * + * \return Status of the events setup procedure. + * + * \retval STATUS_OK The module was initialized successfully + * \retval STATUS_INVALID_ARG An invalid configuration option or argument + * was supplied + */ +enum status_code tcc_enable_events( + struct tcc_module *const module_inst, + struct tcc_events *const events) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + Assert(events); + + Tcc *const tcc_module = module_inst->hw; + + /* Check if it's enabled or resetting. */ + if (tcc_module->CTRLA.reg & (TCC_CTRLA_ENABLE | TCC_CTRLA_SWRST)) { + return STATUS_ERR_DENIED; + } + + uint32_t evctrl = tcc_module->EVCTRL.reg; + + /* Setup event output action */ + if (events->output_config.modify_generation_selection) { + evctrl &= ~ TCC_EVCTRL_CNTSEL_Msk; + switch(events->output_config.generation_selection) { + case TCC_EVENT_GENERATION_SELECTION_START: + evctrl |= TCC_EVCTRL_CNTSEL_START; + break; + case TCC_EVENT_GENERATION_SELECTION_END: + evctrl |= TCC_EVCTRL_CNTSEL_END; + break; + case TCC_EVENT_GENERATION_SELECTION_BETWEEN: + evctrl |= TCC_EVCTRL_CNTSEL_BETWEEN; + break; + case TCC_EVENT_GENERATION_SELECTION_BOUNDARY: + evctrl |= TCC_EVCTRL_CNTSEL_BOUNDARY; + break; + default: + Assert(false); + /* Wrong configuration */ + return STATUS_ERR_INVALID_ARG; + } + } + /* Setup input event0 */ + if (events->on_input_event_perform_action[0]) { + evctrl |= TCC_EVCTRL_TCEI0; + } + if (events->input_config[0].invert) { + evctrl |= TCC_EVCTRL_TCINV0; + } + if (events->input_config[0].modify_action) { + evctrl &= ~ TCC_EVCTRL_EVACT0_Msk; + switch(events->input_config[0].action) { + case TCC_EVENT0_ACTION_OFF: + evctrl |= TCC_EVCTRL_EVACT0_OFF; + break; + case TCC_EVENT0_ACTION_RETRIGGER: + evctrl |= TCC_EVCTRL_EVACT0_RETRIGGER; + break; + case TCC_EVENT0_ACTION_COUNT_EVENT: + evctrl |= TCC_EVCTRL_EVACT0_COUNTEV; + break; + case TCC_EVENT0_ACTION_START: + evctrl |= TCC_EVCTRL_EVACT0_START; + break; + case TCC_EVENT0_ACTION_INCREMENT: + evctrl |= TCC_EVCTRL_EVACT0_INC; + break; + case TCC_EVENT0_ACTION_COUNT_DURING_ACTIVE: + evctrl |= TCC_EVCTRL_EVACT0_COUNT; + break; + case TCC_EVENT0_ACTION_NON_RECOVERABLE_FAULT: + evctrl |= TCC_EVCTRL_EVACT0_FAULT; + break; + default: + Assert(false); + /* Wrong configuration */ + return STATUS_ERR_INVALID_ARG; + } + } + /* Setup input event1 */ + if (events->on_input_event_perform_action[1]) { + evctrl |= TCC_EVCTRL_TCEI1; + } + if (events->input_config[1].invert) { + evctrl |= TCC_EVCTRL_TCINV1; + } + if (events->input_config[1].modify_action) { + evctrl &= ~ TCC_EVCTRL_EVACT1_Msk; + switch(events->input_config[1].action) { + case TCC_EVENT1_ACTION_OFF: + evctrl |= TCC_EVCTRL_EVACT1_OFF; + break; + case TCC_EVENT1_ACTION_RETRIGGER: + evctrl |= TCC_EVCTRL_EVACT1_RETRIGGER; + break; + case TCC_EVENT1_ACTION_DIR_CONTROL: + evctrl |= TCC_EVCTRL_EVACT1_DIR; + break; + case TCC_EVENT1_ACTION_STOP: + evctrl |= TCC_EVCTRL_EVACT1_STOP; + break; + case TCC_EVENT1_ACTION_DECREMENT: + evctrl |= TCC_EVCTRL_EVACT1_DEC; + break; + case TCC_EVENT1_ACTION_PERIOD_PULSE_WIDTH_CAPTURE: + evctrl |= TCC_EVCTRL_EVACT1_PPW | + TCC_EVCTRL_MCEI0 | TCC_EVCTRL_MCEI1; + break; + case TCC_EVENT1_ACTION_PULSE_WIDTH_PERIOD_CAPTURE: + evctrl |= TCC_EVCTRL_EVACT1_PWP | + TCC_EVCTRL_MCEI0 | TCC_EVCTRL_MCEI1; + break; + case TCC_EVENT1_ACTION_NON_RECOVERABLE_FAULT: + evctrl |= TCC_EVCTRL_EVACT1_FAULT; + break; + default: + Assert(false); + /* Wrong configuration */ + return STATUS_ERR_INVALID_ARG; + } + } + uint32_t ch; + for(ch = 0; ch < TCC_NUM_CHANNELS; ch ++) { + if (events->generate_event_on_channel[ch]) { + evctrl |= (TCC_EVCTRL_MCEO(1) << ch); + } + if (events->on_event_perform_channel_action[ch]) { + evctrl |= (TCC_EVCTRL_MCEI(1) << ch); + } + } + if (events->generate_event_on_counter_overflow) { + evctrl |= TCC_EVCTRL_OVFEO; + } + if (events->generate_event_on_counter_retrigger) { + evctrl |= TCC_EVCTRL_TRGEO; + } + if (events->generate_event_on_counter_event) { + evctrl |= TCC_EVCTRL_CNTEO; + } + + tcc_module->EVCTRL.reg = evctrl; + + return STATUS_OK; +} + +/** + * \brief Disables the event input or output of a TCC instance. + * + * Disables one or more input or output events for the given TCC module. + * See \ref tcc_events for a list of events this module supports. + * + * \note Events cannot be altered while the module is enabled. + * + * \param[in] module_inst Pointer to the software module instance struct + * \param[in] events Struct containing flags of events to disable + */ +void tcc_disable_events( + struct tcc_module *const module_inst, + struct tcc_events *const events) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + Assert(events); + + Tcc *const tcc_module = module_inst->hw; + + /* Check if it's enabled or resetting. */ + if (tcc_module->CTRLA.reg & (TCC_CTRLA_ENABLE | TCC_CTRLA_SWRST)) { + return; + } + + + uint32_t evctrl = 0; + uint32_t ch; + for(ch = 0; ch < TCC_NUM_CHANNELS; ch ++) { + if (events->generate_event_on_channel[ch]) { + evctrl |= (TCC_EVCTRL_MCEO(1) << ch); + } + if (events->on_event_perform_channel_action[ch]) { + evctrl |= (TCC_EVCTRL_MCEI(1) << ch); + } + } + if (events->generate_event_on_counter_overflow) { + evctrl |= TCC_EVCTRL_OVFEO; + } + if (events->generate_event_on_counter_retrigger) { + evctrl |= TCC_EVCTRL_TRGEO; + } + if (events->generate_event_on_counter_event) { + evctrl |= TCC_EVCTRL_CNTEO; + } + if (events->on_input_event_perform_action[0]) { + evctrl |= TCC_EVCTRL_TCEI0; + } + if (events->on_input_event_perform_action[1]) { + evctrl |= TCC_EVCTRL_TCEI1; + } + if (events->input_config[0].invert) { + evctrl |= TCC_EVCTRL_TCINV0; + } + if (events->input_config[1].invert) { + evctrl |= TCC_EVCTRL_TCINV1; + } + + tcc_module->EVCTRL.reg &= ~evctrl; +} + + + +/** + * \brief Sets count value for the given TCC module. + * + * Sets the timer count value of an initialized TCC module. The + * specified TCC module can remain running or stopped. + * + * \param[in] module_inst Pointer to the software module instance struct + * \param[in] count New timer count value to set + * + * \return Status which indicates whether the new value is set. + * + * \retval STATUS_OK The timer count was updated successfully + * \retval STATUS_ERR_INVALID_ARG An invalid timer counter size was specified + */ +enum status_code tcc_set_count_value( + const struct tcc_module *const module_inst, + const uint32_t count) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance*/ + Tcc *const tcc_module = module_inst->hw; + /* Get a index of the module */ + uint8_t module_index = _tcc_get_inst_index(tcc_module); + + uint32_t max_count = _tcc_maxs[module_index]; + + if (count > max_count) { + return STATUS_ERR_INVALID_ARG; + } + + while (tcc_module->SYNCBUSY.reg & TCC_SYNCBUSY_COUNT) { + /* Wait for sync */ + } + + /* Write to based on the TCC dithering */ + tcc_module->COUNT.reg = (count); + + return STATUS_OK; +} + +/** + * \brief Get count value of the given TCC module. + * + * Retrieves the current count value of a TCC module. The specified TCC module + * can remain running or stopped. + * + * \param[in] module_inst Pointer to the software module instance struct + * + * \return Count value of the specified TCC module. + */ +uint32_t tcc_get_count_value( + const struct tcc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance*/ + Tcc *const tcc_module = module_inst->hw; + uint32_t last_cmd; + + /* Wait last command done */ + do { + while (tcc_module->SYNCBUSY.reg & TCC_SYNCBUSY_CTRLB) { + /* Wait for sync */ + } + last_cmd = tcc_module->CTRLBSET.reg & TCC_CTRLBSET_CMD_Msk; + if (TCC_CTRLBSET_CMD_NONE == last_cmd) { + /* Issue read command and break */ + tcc_module->CTRLBSET.bit.CMD = TCC_CTRLBSET_CMD_READSYNC_Val; + while (tcc_module->SYNCBUSY.reg & TCC_SYNCBUSY_CTRLB) { + /* Wait for sync */ + } + break; + } else if (TCC_CTRLBSET_CMD_READSYNC == last_cmd) { + /* Command have been issued */ + break; + } + } while (1); + + while (tcc_module->SYNCBUSY.reg & TCC_SYNCBUSY_COUNT) { + /* Wait for sync */ + } + return (tcc_module->COUNT.reg); +} + + + +/** + * \brief Gets the TCC module capture value. + * + * Retrieves the capture value in the indicated TCC module capture channel. + * + * \param[in] module_inst Pointer to the software module instance struct + * \param[in] channel_index Index of the Compare Capture channel to read + * + * \return Capture value stored in the specified timer channel. + */ +uint32_t tcc_get_capture_value( + const struct tcc_module *const module_inst, + const enum tcc_match_capture_channel channel_index) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + Assert(channel_index < _tcc_cc_nums[_tcc_get_inst_index(module_inst->hw)]); + + /* Get a pointer to the module's hardware instance */ + Tcc *const tcc_module = module_inst->hw; + + while(tcc_module->SYNCBUSY.reg & (TCC_SYNCBUSY_CC0 << channel_index)) { + /* Sync wait */ + } + + return tcc_module->CC[channel_index].reg; +} + +/** + * \internal + * \brief Sets a TCC module compare value/buffer. + * + * Writes a compare value to the given TCC module compare/capture channel or + * buffer one. + * + * \param[in] module_inst Pointer to the software module instance struct + * \param[in] channel_index Index of the compare channel to write to + * \param[in] compare New compare value/buffer value to set + * \param[in] double_buffering_enabled Set to \c true to write to CCBx + * + * \return Status of the compare update procedure. + * + * \retval STATUS_OK The compare value was updated successfully + * \retval STATUS_ERR_INVALID_ARG An invalid channel index was supplied or + * compare value exceed resolution + */ +static enum status_code _tcc_set_compare_value( + const struct tcc_module *const module_inst, + const enum tcc_match_capture_channel channel_index, + const uint32_t compare, + const bool double_buffering_enabled) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + Tcc *const tcc_module = module_inst->hw; + /* Get a index of the module */ + uint8_t module_index = _tcc_get_inst_index(tcc_module); + + /* Check index */ + if (channel_index >= _tcc_cc_nums[module_index]) { + return STATUS_ERR_INVALID_ARG; + } + + uint32_t max_count = _tcc_maxs[module_index]; + + /* Check compare value */ + if (compare > max_count) { + return STATUS_ERR_INVALID_ARG; + } + + if (double_buffering_enabled) { +#if (SAML21) || (SAMC20) || (SAMC21) || (SAML22) || (SAMR30) + tcc_module->CCBUF[channel_index].reg = compare; +#else + while(tcc_module->SYNCBUSY.reg & + (TCC_SYNCBUSY_CCB0 << channel_index)) { + /* Sync wait */ + } + tcc_module->CCB[channel_index].reg = compare; +#endif + } else { + while(tcc_module->SYNCBUSY.reg & (TCC_SYNCBUSY_CC0 << channel_index)) { + /* Sync wait */ + } + tcc_module->CC[channel_index].reg = compare; + } + return STATUS_OK; +} + + +/** + * \brief Sets a TCC module compare value. + * + * Writes a compare value to the given TCC module compare/capture channel. + * + * If double buffering is enabled it always write to the buffer + * register. The value will then be updated immediately by calling + * \ref tcc_force_double_buffer_update(), or be updated when the lock update bit + * is cleared and the UPDATE condition happen. + * + * \param[in] module_inst Pointer to the software module instance struct + * \param[in] channel_index Index of the compare channel to write to + * \param[in] compare New compare value to set + * + * \return Status of the compare update procedure. + * + * \retval STATUS_OK The compare value was updated successfully + * \retval STATUS_ERR_INVALID_ARG An invalid channel index was supplied or + * compare value exceed resolution + */ +enum status_code tcc_set_compare_value( + const struct tcc_module *const module_inst, + const enum tcc_match_capture_channel channel_index, + const uint32_t compare) +{ + /* Sanity check arguments */ + Assert(module_inst); + + return _tcc_set_compare_value(module_inst, channel_index, compare, + module_inst->double_buffering_enabled); +} + +/** + * \brief Sets a TCC module compare value and buffer value. + * + * Writes compare value and buffer to the given TCC module compare/capture + * channel. Usually as preparation for double buffer or circulared double buffer + * (circular buffer). + * + * \param[in] module_inst Pointer to the software module instance struct + * \param[in] channel_index Index of the compare channel to write to + * \param[in] compare New compare value to set + * \param[in] compare_buffer New compare buffer value to set + * + * \return Status of the compare update procedure. + * + * \retval STATUS_OK The compare value was updated successfully + * \retval STATUS_ERR_INVALID_ARG An invalid channel index was supplied or + * compare value exceed resolution + */ +enum status_code tcc_set_double_buffer_compare_values( + struct tcc_module *const module_inst, + const enum tcc_match_capture_channel channel_index, + const uint32_t compare, const uint32_t compare_buffer) +{ + /* Sanity check arguments */ + Assert(module_inst); + + enum status_code status; + status = _tcc_set_compare_value(module_inst, channel_index, compare, false); + if (status != STATUS_OK) { + return status; + } + return _tcc_set_compare_value(module_inst, channel_index, compare_buffer, + true); +} + + +/** + * \internal + * \brief Set the timer TOP/PERIOD buffer/value. + * + * This function writes the given value to the PER/PERB register. + * + * \param[in] module_inst Pointer to the software module instance struct + * \param[in] top_value New value to be loaded into the PER/PERB register + * \param[in] double_buffering_enabled Set to \c true to write to PERB + * + * \return Status of the TOP set procedure. + * + * \retval STATUS_OK The timer TOP value was updated successfully + * \retval STATUS_ERR_INVALID_ARG An invalid channel index was supplied or + * top/period value exceed resolution + */ +static enum status_code _tcc_set_top_value( + const struct tcc_module *const module_inst, + const uint32_t top_value, + const bool double_buffering_enabled) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + Tcc *const tcc_module = module_inst->hw; + /* Get a index of the module */ + uint8_t module_index = _tcc_get_inst_index(tcc_module); + + uint32_t max_count = _tcc_maxs[module_index]; + + /* Check compare value */ + if (top_value > max_count) { + return STATUS_ERR_INVALID_ARG; + } + + if (double_buffering_enabled) { +#if (SAML21) || (SAMC20) || (SAMC21) || (SAML22) || (SAMR30) + tcc_module->PERBUF.reg = top_value; +#else + while(tcc_module->SYNCBUSY.reg & TCC_SYNCBUSY_PERB) { + /* Sync wait */ + } + tcc_module->PERB.reg = top_value; +#endif + } else { + while(tcc_module->SYNCBUSY.reg & TCC_SYNCBUSY_PER) { + /* Sync wait */ + } + tcc_module->PER.reg = top_value; + } + return STATUS_OK; +} + + +/** + * \brief Set the timer TOP/PERIOD value. + * + * This function writes the given value to the PER/PERB register. + * + * If double buffering is enabled it always write to the buffer + * register (PERB). The value will then be updated immediately by calling + * \ref tcc_force_double_buffer_update(), or be updated when the lock update bit + * is cleared and the UPDATE condition happen. + * + * When using MFRQ, the top value is defined by the CC0 register value and the + * PER value is ignored, so + * \ref tcc_set_compare_value (module,channel_0,value) must be used instead of + * this function to change the actual top value in that case. + * For all other waveforms operation the top value is defined by PER register + * value. + * + * \param[in] module_inst Pointer to the software module instance struct + * \param[in] top_value New value to be loaded into the PER/PERB register + * + * \return Status of the TOP set procedure. + * + * \retval STATUS_OK The timer TOP value was updated successfully + * \retval STATUS_ERR_INVALID_ARG An invalid channel index was supplied or + * top/period value exceed resolution + */ +enum status_code tcc_set_top_value( + const struct tcc_module *const module_inst, + const uint32_t top_value) +{ + /* Sanity check arguments */ + Assert(module_inst); + + return _tcc_set_top_value(module_inst, top_value, + module_inst->double_buffering_enabled); +} + +/** + * \brief Set the timer TOP/PERIOD value and buffer value. + * + * This function writes the given value to the PER and PERB register. Usually as + * preparation for double buffer or circulared double buffer (circular buffer). + * + * When using MFRQ, the top values are defined by the CC0 and CCB0, the PER and + * PERB values are ignored, so + * \ref tcc_set_double_buffer_compare_values (module,channel_0,value,buffer) must + * be used instead of this function to change the actual top values in that + * case. For all other waveforms operation the top values are defined by PER and + * PERB registers values. + * + * \param[in] module_inst Pointer to the software module instance struct + * \param[in] top_value New value to be loaded into the PER register + * \param[in] top_buffer_value New value to be loaded into the PERB register + * + * \return Status of the TOP set procedure. + * + * \retval STATUS_OK The timer TOP value was updated successfully + * \retval STATUS_ERR_INVALID_ARG An invalid channel index was supplied or + * top/period value exceed resolution + */ +enum status_code tcc_set_double_buffer_top_values( + const struct tcc_module *const module_inst, + const uint32_t top_value, const uint32_t top_buffer_value) +{ + /* Sanity check arguments */ + Assert(module_inst); + + enum status_code status; + status = _tcc_set_top_value(module_inst, top_value, false); + if (status != STATUS_OK) { + return status; + } + return _tcc_set_top_value(module_inst, top_buffer_value, true); +} + + +/** + * \brief Sets the TCC module waveform output pattern. + * + * Force waveform output line to generate specific pattern (0, 1, or as is). + * + * If double buffering is enabled it always write to the buffer + * register. The value will then be updated immediately by calling + * \ref tcc_force_double_buffer_update(), or be updated when the lock update bit + * is cleared and the UPDATE condition happen. + * + * \param[in] module_inst Pointer to the software module instance struct + * \param[in] line_index Output line index + * \param[in] pattern Output pattern to use (\ref tcc_output_pattern) + * + * \return Status of the pattern set procedure. + * + * \retval STATUS_OK The PATT register is updated successfully + * \retval STATUS_ERR_INVALID_ARG An invalid line index was supplied + */ +enum status_code tcc_set_pattern( + const struct tcc_module *const module_inst, + const uint32_t line_index, + const enum tcc_output_pattern pattern) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + Tcc *const tcc_module = module_inst->hw; + /* Get a index of the module */ + uint8_t module_index = _tcc_get_inst_index(tcc_module); + /* Get number of output lines */ + uint8_t ow_num = _tcc_ow_nums[module_index]; + + /* Check if line number is OK */ + if (line_index >= ow_num) { + return STATUS_ERR_INVALID_ARG; + } + + uint32_t patt_value; + + while(tcc_module->SYNCBUSY.reg & TCC_SYNCBUSY_PATT) { + /* Sync wait */ + } + patt_value = tcc_module->PATT.reg; + if (TCC_OUTPUT_PATTERN_DISABLE == pattern) { + patt_value &= ~(TCC_PATT_PGE0 << line_index); + } else if (TCC_OUTPUT_PATTERN_0 == pattern) { + patt_value &= ~(TCC_PATT_PGV0 << line_index); + patt_value |= (TCC_PATT_PGE0 << line_index); + } else { + patt_value |= ((TCC_PATT_PGE0 | TCC_PATT_PGV0) << line_index); + } + + if (module_inst->double_buffering_enabled) { +#if (SAML21) || (SAMC20) || (SAMC21) || (SAML22) || (SAMR30) + tcc_module->PATTBUF.reg = patt_value; +#else + while(tcc_module->SYNCBUSY.reg & TCC_SYNCBUSY_PATTB) { + /* Sync wait */ + } + tcc_module->PATTB.reg = patt_value; +#endif + } else { + tcc_module->PATT.reg = patt_value; + } + return STATUS_OK; +} + +/** + * \brief Retrieves the current module status. + * + * Retrieves the status of the module, giving overall state information. + * + * \param[in] module_inst Pointer to the TCC software instance struct + * + * \return Bitmask of \c TCC_STATUS_* flags. + * + * \retval TCC_STATUS_CHANNEL_MATCH_CAPTURE(n) Channel n match/capture has occured + * \retval TCC_STATUS_CHANNEL_OUTPUT(n) Channel n match/capture output state + * \retval TCC_STATUS_NON_RECOVERABLE_FAULT_OCCUR(x) Non-recoverable fault x has occured + * \retval TCC_STATUS_RECOVERABLE_FAULT_OCCUR(n) Recoverable fault n has occured + * \retval TCC_STATUS_NON_RECOVERABLE_FAULT_PRESENT(x) Non-recoverable fault x input present + * \retval TCC_STATUS_RECOVERABLE_FAULT_PRESENT(n) Recoverable fault n input present + * \retval TCC_STATUS_SYNC_READY None of register is syncing + * \retval TCC_STATUS_CAPTURE_OVERFLOW Timer capture data has overflowed + * \retval TCC_STATUS_COUNTER_EVENT Timer counter event has occurred + * \retval TCC_STATUS_COUNT_OVERFLOW Timer count value has overflowed + * \retval TCC_STATUS_COUNTER_RETRIGGERED Timer counter has been retriggered + * \retval TCC_STATUS_STOP Timer counter has been stopped + * \retval TCC_STATUS_RAMP_CYCLE_INDEX Wave ramp index for cycle + */ +uint32_t tcc_get_status( + struct tcc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + uint32_t int_flags = module_inst->hw->INTFLAG.reg; + uint32_t status_flags = module_inst->hw->STATUS.reg; + uint32_t status = 0; + int i; + + /* SYNC */ + if (module_inst->hw->SYNCBUSY.reg == 0) { + status |= TCC_STATUS_SYNC_READY; + } + + /* Channels */ + for (i = 0; i < TCC_NUM_CHANNELS; i++) { + if (int_flags & TCC_INTFLAG_MC(i)) { + status |= TCC_STATUS_CHANNEL_MATCH_CAPTURE(i); + } + if (status_flags & TCC_STATUS_CMP(i)) { + status |= TCC_STATUS_CHANNEL_OUTPUT(i); + } + } + /* Non-recoverable fault state */ + if ((int_flags & TCC_INTFLAG_FAULT1) || + (status_flags & TCC_STATUS_FAULT1)) { + status |= TCC_STATUS_NON_RECOVERABLE_FAULT_OCCUR(1); + } + if ((int_flags & TCC_INTFLAG_FAULT0) || + (status_flags & TCC_STATUS_FAULT0)) { + status |= TCC_STATUS_NON_RECOVERABLE_FAULT_OCCUR(0); + } + /* Non-recoverable fault inputs */ + if (status_flags & TCC_STATUS_FAULT0IN) { + status |= TCC_STATUS_NON_RECOVERABLE_FAULT_PRESENT(0); + } + if (status_flags & TCC_STATUS_FAULT1IN) { + status |= TCC_STATUS_NON_RECOVERABLE_FAULT_PRESENT(1); + } + /* Recoverable fault state */ + if ((int_flags & TCC_INTFLAG_FAULTB) || + (status_flags & TCC_STATUS_FAULTB)) { + status |= TCC_STATUS_RECOVERABLE_FAULT_OCCUR(1); + } + if ((int_flags & TCC_INTFLAG_FAULTA) || + (status_flags & TCC_STATUS_FAULTA)) { + status |= TCC_STATUS_RECOVERABLE_FAULT_OCCUR(0); + } + /* Recoverable fault inputs */ + if (status_flags & TCC_STATUS_FAULTAIN) { + status |= TCC_STATUS_RECOVERABLE_FAULT_PRESENT(0); + } + if (status_flags & TCC_STATUS_FAULTBIN) { + status |= TCC_STATUS_RECOVERABLE_FAULT_PRESENT(1); + } + + /* Check for TCC capture overflow */ + if (int_flags & TCC_INTFLAG_ERR) { + status |= TCC_STATUS_CAPTURE_OVERFLOW; + } + /* Check for TCC count counter */ + if (int_flags & TCC_INTFLAG_CNT) { + status |= TCC_STATUS_COUNTER_EVENT; + } + /* Check for TCC count retrigger */ + if (int_flags & TCC_INTFLAG_TRG) { + status |= TCC_STATUS_COUNTER_RETRIGGERED; + } + /* Check for TCC count overflow */ + if (int_flags & TCC_INTFLAG_OVF) { + status |= TCC_STATUS_COUNT_OVERFLOW; + } + /* Check for TCC count stop */ + if (status_flags & TCC_STATUS_STOP) { + status |= TCC_STATUS_STOPPED; + } + /* Check for TCC RAMP index */ + if (status_flags & TCC_STATUS_IDX) { + status |= TCC_STATUS_RAMP_CYCLE_INDEX; + } + return status; +} + +/** + * \brief Clears a module status flag. + * + * Clears the given status flag of the module. + * + * \param[in] module_inst Pointer to the TCC software instance struct + * \param[in] status_flags Bitmask of \c TCC_STATUS_* flags to clear + */ +void tcc_clear_status( + struct tcc_module *const module_inst, + const uint32_t status_flags) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + uint32_t int_clr = 0; + uint32_t status_clr = 0; + int i; + + /* Channels */ + for (i = 0; i < TCC_NUM_CHANNELS; i++) { + if (status_flags & TCC_STATUS_CHANNEL_MATCH_CAPTURE(i)) { + int_clr |= TCC_INTFLAG_MC(i); + } + } + /* Faults */ + if (status_flags & TCC_STATUS_NON_RECOVERABLE_FAULT_OCCUR(1)) { + int_clr |= TCC_INTFLAG_FAULT1; + status_clr |= TCC_STATUS_FAULT1; + } + if (status_flags & TCC_STATUS_NON_RECOVERABLE_FAULT_OCCUR(0)) { + int_clr |= TCC_INTFLAG_FAULT0; + status_clr |= TCC_STATUS_FAULT0; + } + if (status_flags & TCC_STATUS_RECOVERABLE_FAULT_OCCUR(1)) { + int_clr |= TCC_INTFLAG_FAULTB; + status_clr |= TCC_STATUS_FAULTB; + } + if (status_flags & TCC_STATUS_RECOVERABLE_FAULT_OCCUR(0)) { + int_clr |= TCC_INTFLAG_FAULTA; + status_clr |= TCC_STATUS_FAULTA; + } + /* Check for TCC capture overflow */ + if (status_flags & TCC_STATUS_CAPTURE_OVERFLOW) { + int_clr |= TCC_INTFLAG_ERR; + } + /* Check for TCC count counter */ + if (status_flags & TCC_STATUS_COUNTER_EVENT) { + int_clr |= TCC_INTFLAG_CNT; + } + /* Check for TCC count retrigger */ + if (status_flags & TCC_STATUS_COUNTER_RETRIGGERED) { + int_clr = TCC_INTFLAG_TRG; + } + /* Check for TCC count overflow */ + if (status_flags & TCC_STATUS_COUNT_OVERFLOW) { + int_clr |= TCC_INTFLAG_OVF; + } + /* Clear status flag */ + module_inst->hw->STATUS.reg = status_clr; + /* Clear interrupt flag */ + module_inst->hw->INTFLAG.reg = int_clr; +} + +/** + * \brief Enable circular option for double buffered compare values. + * + * Enable circular option for the double buffered channel compare values. + * On each UPDATE condition, the contents of CCBx and CCx are switched, meaning + * that the contents of CCBx are transferred to CCx and the contents of CCx are + * transferred to CCBx. + * + * \param[in] module_inst Pointer to the TCC software instance struct + * \param[in] channel_index Index of the compare channel to set up to + * + * \retval STATUS_OK The module was initialized successfully + * \retval STATUS_INVALID_ARG An invalid channel index is supplied + */ +enum status_code tcc_enable_circular_buffer_compare( + struct tcc_module *const module_inst, + enum tcc_match_capture_channel channel_index) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + Tcc *const tcc_module = module_inst->hw; + /* Get a index of the module */ + uint8_t module_index = _tcc_get_inst_index(tcc_module); + + /* Check index */ + if (channel_index > 3) { + return STATUS_ERR_INVALID_ARG; + } + if (channel_index >= _tcc_cc_nums[module_index]) { + return STATUS_ERR_INVALID_ARG; + } + + tcc_module->WAVE.reg |= (TCC_WAVE_CICCEN0 << channel_index); + + return STATUS_OK; +} + +/** + * \brief Disable circular option for double buffered compare values. + * + * Stop circularing the double buffered compare values. + * + * \param[in] module_inst Pointer to the TCC software instance struct + * \param[in] channel_index Index of the compare channel to set up to + * + * \retval STATUS_OK The module was initialized successfully + * \retval STATUS_INVALID_ARG An invalid channel index is supplied + */ +enum status_code tcc_disable_circular_buffer_compare( + struct tcc_module *const module_inst, + enum tcc_match_capture_channel channel_index) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + Tcc *const tcc_module = module_inst->hw; + /* Get a index of the module */ + uint8_t module_index = _tcc_get_inst_index(tcc_module); + + /* Check index */ + if (channel_index > 3) { + return STATUS_ERR_INVALID_ARG; + } + if (channel_index >= _tcc_cc_nums[module_index]) { + return STATUS_ERR_INVALID_ARG; + } + + tcc_module->WAVE.reg &= ~(TCC_WAVE_CICCEN0 << channel_index); + + return STATUS_OK; +} diff --git a/atmel-samd/asf/sam0/drivers/tcc/tcc.h b/atmel-samd/asf/sam0/drivers/tcc/tcc.h new file mode 100644 index 0000000000..782c4322b4 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/tcc/tcc.h @@ -0,0 +1,2494 @@ +/** + * \file + * + * \brief SAM TCC - Timer Counter for Control Applications Driver + * + * Copyright (C) 2013-2016 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 + * + */ +/* + * Support and FAQ: visit Atmel Support + */ + +#ifndef TCC_H_INCLUDED +#define TCC_H_INCLUDED + +/** + * \defgroup asfdoc_sam0_tcc_group SAM Timer Counter for Control Applications (TCC) Driver + * + * This driver for Atmel® | SMART ARM®-based microcontrollers provides an interface for the configuration + * and management of the TCC module within the device, for waveform + * generation and timing operations. It also provides extended options for + * control applications. + * + * The following driver API modes are covered + * by this manual: + * + * - Polled APIs + * \if TCC_CALLBACK_MODE + * - Callback APIs + * \endif + * + * The following peripheral is used by this module: + * - TCC (Timer/Counter for Control Applications) + * + * The following devices can use this module: + * - Atmel | SMART SAM D21 + * - Atmel | SMART SAM R21 + * - Atmel | SMART SAM D10/D11 + * - Atmel | SMART SAM L21/L22 + * - Atmel | SMART SAM DA1 + * - Atmel | SMART SAM C20/C21 + * - Atmel | SMART SAM R30 + * + * The outline of this documentation is as follows: + * - \ref asfdoc_sam0_tcc_prerequisites + * - \ref asfdoc_sam0_tcc_module_overview + * - \ref asfdoc_sam0_tcc_special_considerations + * - \ref asfdoc_sam0_tcc_extra_info + * - \ref asfdoc_sam0_tcc_examples + * - \ref asfdoc_sam0_tcc_api_overview + * + * \section asfdoc_sam0_tcc_prerequisites Prerequisites + * + * There are no prerequisites for this module. + * + * \section asfdoc_sam0_tcc_module_overview Module Overview + * + * The Timer/Counter for Control Applications (TCC) module provides a set of + * timing and counting related functionality, such as the generation of periodic + * waveforms, the capturing of a periodic waveform's frequency/duty cycle, + * software timekeeping for periodic operations, waveform extension control, + * fault detection etc. + * + * The counter size of the TCC modules can be 16- or 24-bit depending on + * the TCC instance. + * Refer \ref asfdoc_sam0_tcc_special_considerations_tcc_d21 and + * \ref asfdoc_sam0_tcc_special_considerations_tcc_d11 for details on TCC instances. + * + * The TCC module for the SAM includes the following functions: + * + * - Generation of PWM signals + * - Generation of timestamps for events + * - General time counting + * - Waveform period capture + * - Waveform frequency capture + * - Additional control for generated waveform outputs + * - Fault protection for waveform generation + * + * \ref asfdoc_sam0_tcc_block_diagram "The diagram below" shows the overview + * of the TCC Module. + * + * \anchor asfdoc_sam0_tcc_block_diagram + * \image html overview.svg "Overview of the TCC Module" + * + * \subsection asfdoc_sam0_tcc_module_overview_parts Functional Description + * The TCC module consists of following sections: + * - Base Counter + * - Compare/Capture channels, with waveform generation + * - Waveform extension control and fault detection + * - Interface to the event system, DMAC, and the interrupt system + * + * The base counter can be configured to either count a prescaled generic + * clock or events from the event system.(TCEx, with event action configured + * to counting). + * The counter value can be used by compare/capture channels which can be + * set up either in compare mode or capture mode. + * + * In capture mode, the counter value is stored when a configurable event + * occurs. This mode can be used to generate timestamps used in event capture, + * or it can be used for the measurement of a periodic input signal's + * frequency/duty cycle. + * + * In compare mode, the counter value is compared against one or more of the + * configured channels' compare values. When the counter value coincides with a + * compare value an action can be taken automatically by the module, such as + * generating an output event or toggling a pin when used for frequency or PWM + * signal generation. + * + * \note The connection of events between modules requires the use of the + * \ref asfdoc_sam0_events_group "SAM Event System Driver (EVENTS)" + * to route output event of one module to the the input event of another. + * For more information on event routing, refer to the event driver + * documentation. + * + * In compare mode, when output signal is generated, extended waveform controls + * are available, to arrange the compare outputs into specific formats. + * The Output matrix can change the channel output routing. Pattern generation + * unit can overwrite the output signal line to specific state. + * The Fault protection feature of the TCC supports recoverable and + * non-recoverable faults. + * + * \subsection asfdoc_sam0_tcc_module_overview_tc Base Timer/Counter + * + * \subsubsection asfdoc_sam0_tcc_module_overview_tc_size Timer/Counter Size + * Each TCC has a counter size of either 16- or 24-bits. The size of the + * counter determines the maximum value it can count to before an overflow + * occurs. + * \ref asfdoc_sam0_tcc_count_size_vs_top "The table below" shows the + * maximum values for each of the possible counter sizes. + * + * \anchor asfdoc_sam0_tcc_count_size_vs_top + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Timer Counter Sizes and Their Maximum Count Values
Counter sizeMax. (hexadecimal)Max. (decimal)
16-bit0xFFFF65,535
24-bit0xFFFFFF16,777,215
+ * + * The period/top value of the counter can be set, to define counting period. + * This will allow the counter to overflow when the counter value reaches the + * period/top value. + * + * \subsubsection asfdoc_sam0_tcc_module_overview_tc_clk Timer/Counter Clock and Prescaler + * TCC is clocked asynchronously to the system clock by a GCLK + * (Generic Clock) channel. The GCLK channel can be connected to any of the GCLK + * generators. The GCLK generators are configured to use one of the available + * clock sources in the system such as internal oscillator, external crystals, + * etc. See the \ref asfdoc_sam0_system_clock_group "Generic Clock driver" for + * more information. + * + * Each TCC module in the SAM has its own individual clock prescaler, which + * can be used to divide the input clock frequency used by the counter. This + * prescaler only scales the clock used to provide clock pulses for the counter + * to count, and does not affect the digital register interface portion of + * the module, thus the timer registers will be synchronized to the raw GCLK + * frequency input to the module. + * + * As a result of this, when selecting a GCLK frequency and timer prescaler + * value, the user application should consider both the timer resolution + * required and the synchronization frequency to avoid lengthy + * synchronization times of the module if a very slow GCLK frequency is fed + * into the TCC module. It is preferable to use a higher module GCLK frequency + * as the input to the timer, and prescale this down as much as possible to + * obtain a suitable counter frequency in latency-sensitive applications. + * + * \subsubsection asfdoc_sam0_tcc_module_overview_tc_ctrl Timer/Counter Control Inputs (Events) + * + * The TCC can take several actions on the occurrence of an input event. + * The event actions are listed + * in \ref asfdoc_sam0_tcc_module_event_act "events action settings". + * + * \anchor asfdoc_sam0_tcc_module_event_act + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
TCC Module Event Actions
Event actionDescriptionApplied event
TCC_EVENT_ACTION_OFFNo action on the event inputAll
TCC_EVENT_ACTION_RETRIGGERRe-trigger Counter on eventAll
TCC_EVENT_ACTION_NON_RECOVERABLE_FAULTGenerate Non-Recoverable Fault on eventAll
TCC_EVENT_ACTION_STARTCounter start on eventEV0
TCC_EVENT_ACTION_DIR_CONTROLCounter direction controlEV0
TCC_EVENT_ACTION_DECREMENTCounter decrement on eventEV0
TCC_EVENT_ACTION_PERIOD_PULSE_WIDTH_CAPTURECapture pulse period and pulse widthEV0
TCC_EVENT_ACTION_PULSE_WIDTH_PERIOD_CAPTURECapture pulse width and pulse periodEV0
TCC_EVENT_ACTION_STOPCounter stop on eventEV1
TCC_EVENT_ACTION_COUNT_EVENTCounter count on eventEV1
TCC_EVENT_ACTION_INCREMENTCounter increment on eventEV1
TCC_EVENT_ACTION_COUNT_DURING_ACTIVECounter count during active state of asynchronous eventEV1
+ * + * \subsubsection asfdoc_sam0_tcc_module_overview_tc_reload Timer/Counter Reloading + * + * The TCC also has a configurable reload action, used when a + * re-trigger event occurs. Examples of a re-trigger event could be the counter + * reaching the maximum value when counting up, or when an event from the event + * system makes the counter to re-trigger. The reload action determines if the + * prescaler should be reset, and on which clock. The counter will + * always be reloaded with the value it is set to start counting. The user + * can choose between three different reload actions, described in + * \ref asfdoc_sam0_tcc_module_reload_act "the table below". + * + * \anchor asfdoc_sam0_tcc_module_reload_act + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
TCC Module Reload Actions
Reload actionDescription
TCC_RELOAD_ACTION_GCLKReload TCC counter value on next GCLK cycle. Leave prescaler + * as-is.
TCC_RELOAD_ACTION_PRESCReloads TCC counter value on next prescaler clock. Leave prescaler + * as-is.
TCC_RELOAD_ACTION_RESYNCReload TCC counter value on next GCLK cycle. Clear prescaler to + * zero.
+ * + * The reload action to use will depend on the specific application being + * implemented. One example is when an external trigger for a reload occurs; if + * the TCC uses the prescaler, the counter in the prescaler should not have a + * value between zero and the division factor. The counter in the TCC module + * and the counter in the prescaler should both start at zero. + * If the counter is set to re-trigger when it reaches the maximum value, + * this is not the right option to use. In such a case it would be better if + * the prescaler is left unaltered when the re-trigger happens, letting the + * counter reset on the next GCLK cycle. + * + * \subsubsection asfdoc_sam0_tcc_module_overview_tc_oneshot One-shot Mode + * + * The TCC module can be configured in one-shot mode. When configured in this + * manner, starting the timer will cause it to count until the next overflow + * or underflow condition before automatically halting, waiting to be manually + * triggered by the user application software or an event from the event + * system. + * + * \subsection asfdoc_sam0_tcc_module_overview_capt Capture Operations + * + * In capture operations, any event from the event system or a pin change can + * trigger a capture of the counter value. This captured counter value can be + * used as timestamps for the events, or it can be used in frequency and pulse + * width capture. + * + * \subsubsection asfdoc_sam0_tcc_module_overview_capt_ev Capture Operations - Event + * + * Event capture is a simple use of the capture functionality, + * designed to create timestamps for specific events. When the input event + * appears, the current counter value is copied into the corresponding + * compare/capture register, which can then be read by the user application. + * + * Note that when performing any capture operation, there is a risk that the + * counter reaches its top value (MAX) when counting up, or the bottom value + * (zero) when counting down, before the capture event occurs. This can distort + * the result, making event timestamps to appear shorter than they really are. + * In this case, the user application should check for timer overflow when + * reading a capture result in order to detect this situation and perform an + * appropriate adjustment. + * + * Before checking for a new capture, \ref TCC_STATUS_COUNT_OVERFLOW + * should be checked. The response to an overflow error is left to the user + * application, however, it may be necessary to clear both the overflow + * flag and the capture flag upon each capture reading. + * + * \subsubsection asfdoc_sam0_tcc_module_overview_capt_pulse Capture Operations - Pulse Width + * + * Pulse Width Capture mode makes it possible to measure the pulse width and + * period of PWM signals. This mode uses two capture channels of the counter. + * There are two modes for pulse width capture; + * Pulse Width Period (PWP) and Period Pulse Width (PPW). In PWP mode, capture + * channel 0 is used for storing the pulse width and capture channel 1 stores + * the observed period. While in PPW mode, the roles of the two capture channels + * are reversed. + * + * As in the above example it is necessary to poll on interrupt flags to see + * if a new capture has happened and check that a capture overflow error has + * not occurred. + * + * Refer to \ref asfdoc_sam0_tcc_module_overview_tc_ctrl to set up the input + * event to perform pulse width capture. + * + * \subsection asfdoc_sam0_tcc_module_overview_mc Compare Match Operation + * + * In compare match operation, Compare/Capture registers are compared + * with the counter value. When the timer's count value matches the value of a + * compare channel, a user defined action can be taken. + * + * \subsubsection asfdoc_sam0_tcc_module_overview_mc_timer Basic Timer + * + * A Basic Timer is a simple application where compare match operation is used + * to determine when a specific period has elapsed. In Basic Timer operations, + * one or more values in the module's Compare/Capture registers are used to + * specify the time (in terms of the number of prescaled GCLK cycles, or + * input events) at which + * an action should be taken by the microcontroller. This can be an Interrupt + * Service Routine (ISR), event generation via the event system, or a software + * flag that is polled from the user application. + * + * \subsubsection asfdoc_sam0_tcc_module_overview_mc_wave Waveform Generation + * + * Waveform generation enables the TCC module to generate square waves, or, if + * combined with an external passive low-pass filter, analog waveforms. + * + * \subsubsection asfdoc_sam0_tcc_module_overview_mc_wave_pwm Waveform Generation - PWM + * + * Pulse width modulation is a form of waveform generation and a signalling + * technique that can be useful in many applications. When PWM mode is used, + * a digital pulse train with a configurable frequency and duty cycle can be + * generated by the TCC module and output to a GPIO pin of the device. + * + * Often PWM is used to communicate a control or information parameter to an + * external circuit or component. Differing impedances of the source generator + * and sink receiver circuits is less of an issue when using PWM compared to + * using an analog voltage value, as noise will not generally affect the + * signal's integrity to a meaningful extent. + * + * \ref asfdoc_sam0_tcc_module_pwm_single_diag "The figure below" illustrates + * operations and different states of the counter and its output when using + * the timer in Normal PWM mode (Single Slope). As can be seen, the TOP/PERIOD + * value is + * unchanged and is set to MAX. The compare match value is changed at several + * points to illustrate the resulting waveform output changes. The PWM output is + * set to normal (i.e. non-inverted) output mode. + * + * \anchor asfdoc_sam0_tcc_module_pwm_single_diag + * \image html pwm_single_ex.svg "Example Of PWM In Single-Slope Mode, and Different Counter Operations" + * + * Several PWM modes are supported by the TCC module, refer to + * datasheet for the details on PWM waveform generation. + * + * \subsubsection asfdoc_sam0_tcc_module_overview_mc_wave_freq Waveform Generation - Frequency + * + * Normal Frequency Generation is in many ways identical to PWM generation. + * However, only in Frequency Generation, a toggle occurs on the output when a + * match on a compare channels occurs. + * + * When the Match Frequency Generation is used, the timer value is reset on + * match condition, resulting in a variable frequency square wave with a + * fixed 50% duty cycle. + * + * \subsection asfdoc_sam0_tcc_module_overview_ext Waveform Extended Controls + * + * \subsubsection asfdoc_sam0_tcc_module_overview_ext_pat Pattern Generation + * + * Pattern insertion allows the TCC module to change the actual pin output level + * without modifying the compare/match settings. + * + * \anchor asfdoc_sam0_tcc_module_pattern_gen + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
TCC Module Output Pattern Generation
PatternDescription
TCC_OUTPUT_PATTERN_DISABLEPattern disabled, generate output as is
TCC_OUTPUT_PATTERN_0Generate pattern 0 on output (keep the output LOW)
TCC_OUTPUT_PATTERN_1Generate pattern 1 on output (keep the output HIGH)
+ * + * \subsubsection asfdoc_sam0_tcc_module_overview_ext_r_fault Recoverable Faults + * + * The recoverable faults can trigger one or several of following fault actions: + * -# *Halt* action: The recoverable faults can halt the TCC timer/counter, + * so that the final output wave is kept at a defined state. When the fault + * state is removed it is possible to recover the counter and waveform + * generation. The halt action is defined as: + * \anchor asfdoc_sam0_tcc_module_fault_halt_action + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
TCC Module Recoverable Fault Halt Actions
ActionDescription
TCC_FAULT_HALT_ACTION_DISABLEHalt action is disabled
TCC_FAULT_HALT_ACTION_HW_HALTThe timer/counter is halted as long as the corresponding fault is + * present
TCC_FAULT_HALT_ACTION_SW_HALTThe timer/counter is halted until the corresponding fault is removed + * and fault state cleared by software
TCC_FAULT_HALT_ACTION_NON_RECOVERABLEForce all the TCC output pins to a pre-defined level, as what + * Non-Recoverable Fault do
+ * -# *Restart* action: When enabled, the recoverable faults can restart the TCC + * timer/counter. + * -# *Keep* action: When enabled, the recoverable faults can keep the + * corresponding channel output to zero when the fault condition is present. + * -# *Capture* action: When the recoverable fault occurs, the capture action can + * time stamps the corresponding fault. The following capture mode is + * supported: + * \anchor asfdoc_sam0_tcc_module_fault_capt_action + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
TCC Module Recoverable Fault Capture Actions
ActionDescription
TCC_FAULT_CAPTURE_DISABLECapture action is disabled
TCC_FAULT_CAPTURE_EACHEquivalent to standard capture operation, on each fault occurrence + * the time stamp is captured
TCC_FAULT_CAPTURE_MINIMUMGet the minimum time stamped value in all time stamps
TCC_FAULT_CAPTURE_MAXIMUMGet the maximum time stamped value in all time stamps
TCC_FAULT_CAPTURE_SMALLERTime stamp the fault input if the value is smaller than last one
TCC_FAULT_CAPTURE_BIGGERTime stamp the fault input if the value is bigger than last one
TCC_FAULT_CAPTURE_CHANGETime stamp the fault input if the time stamps changes its increment + * direction
+ * + * In TCC module, only the first two compare channels (CC0 and CC1) can work + * with recoverable fault inputs. The corresponding event inputs (TCCx MC0 + * and TCCx MC1) are then used as fault inputs respectively. + * The faults are called Fault A and Fault B. + * + * The recoverable fault can be filtered or effected by corresponding channel + * output. On fault condition there are many other settings that can be chosen. + * Refer to data sheet for more details about the recoverable fault + * operations. + * + * \subsubsection asfdoc_sam0_tcc_module_overview_ext_n_fault Non-Recoverable Faults + * + * The non-recoverable faults force all the TCC output pins to a pre-defined + * level (can be forced to 0 or 1). The input control signal of non-recoverable + * fault is from timer/counter event (TCCx EV0 and TCCx EV1). + * To enable non-recoverable fault, + * corresponding TCEx event action must be set to non-recoverable fault action + * (\ref TCC_EVENT_ACTION_NON_RECOVERABLE_FAULT). + * Refer to \ref asfdoc_sam0_tcc_module_overview_tc_ctrl to see the available + * event input action. + * + * \subsection asfdoc_sam0_tcc_module_overview_buffering Double and Circular Buffering + * + * The pattern, period, and the compare channels registers are double buffered. + * For these options there are effective registers (PATT, PER, and CCx) and + * buffer registers (PATTB, PERB, and CCx). When writing to the buffer + * registers, the values are buffered and will be committed to effective + * registers on UPDATE condition. + * + * Usually the buffered value is cleared after it is committed, but there is also + * an option to circular the register buffers. The period (PER) and four lowest + * compare channels register (CCx, x is 0 ~ 3) support this function. When + * circular buffer is used, on UPDATE the previous period or compare values are + * copied back into the corresponding period buffer and compare buffers. + * This way, the register value and its buffer register value is actually + * switched on UPDATE condition, and will be switched back on next UPDATE + * condition. + * + * For input capture, the buffer register (CCBx) and the corresponding capture + * channel register (CCx) act like a FIFO. When regular register (CCx) is empty + * or read, any content in the buffer register is passed to regular one. + * + * In TCC module driver, when the double buffering write is enabled, any + * write through \ref tcc_set_top_value(), \ref tcc_set_compare_value(), and + * \ref tcc_set_pattern() will be done to the corresponding buffer register. + * Then the value in the buffer register will be transferred to the regular + * register on the next UPDATE condition or by a force UPDATE using + * \ref tcc_force_double_buffer_update(). + * + * \subsection asfdoc_sam0_tcc_module_overview_sleep Sleep Mode + * + * TCC modules can be configured to operate in any sleep mode, with its "run + * in standby" function enabled. It can wake up the device using interrupts or + * perform internal actions with the help of the Event System. + * + * \section asfdoc_sam0_tcc_special_considerations Special Considerations + * + * \subsection asfdoc_sam0_tcc_special_considerations_specific_features Driver Feature Macro Definition + * \ref asfdoc_sam0_tcc_feature_table "The table below" shows some specific features + * of the TCC Module. + * + * \anchor asfdoc_sam0_tcc_feature_table + * + * + * + * + * + * + * + * + * + * + *
TCC Module Specific Features
Driver Feature MacroSupported devices
FEATURE_TCC_GENERATE_DMA_TRIGGERSAM L21/L22/R30
+ * + * \note The specific features are only available in the driver when the + * selected device supports those features. + * + * \subsection asfdoc_sam0_tcc_special_considerations_tcc_feature Module Features + * + * The features of TCC, such as timer/counter size, number of compare capture + * channels, and number of outputs, are dependent on the TCC module instance being + * used. + * + * \subsubsection asfdoc_sam0_tcc_special_considerations_tcc_d21 SAM TCC Feature List + * For SAM D21/R21/L21/L22/DA1/C21/R30, the TCC features are: + * \anchor asfdoc_sam0_tcc_features_d21 + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
TCC module features for SAM D21/R21/L21/L22/DA1/C21/R30
TCC#Match/Capture channelsWave outputsCounter size [bits]FaultDitheringOutput matrixDead-Time insertionSWAPPattern
04824YYYYYY
12424YYY
22216Y
+ * + * \subsubsection asfdoc_sam0_tcc_special_considerations_tcc_d11 SAM D10/D11 TCC Feature List + * For SAM D10/D11, the TCC features are: + * \anchor asfdoc_sam0_tcc_features_d11 + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
TCC Module Features For SAM D10/D11
TCC#Match/Capture channelsWave outputsCounter size [bits]FaultDitheringOutput matrixDead-Time insertionSWAPPattern
04824YYYYYY
+ * + * \subsection asfdoc_sam0_tcc_special_considerations_tcc_pin Channels vs. Pinouts + * + * As the TCC module may have more waveform output pins than the number of + * compare/capture channels, the free pins (with number higher than number of + * channels) will reuse the waveform generated by channels subsequently. E.g., + * if the number of channels is four and the number of wave output pins is eight, channel + * 0 output will be available on out pin 0 and 4, channel 1 output + * on wave out pin 1 and 5, and so on. + * + * \section asfdoc_sam0_tcc_extra_info Extra Information + * + * For extra information, see \ref asfdoc_sam0_tcc_extra. This includes: + * - \ref asfdoc_sam0_tcc_extra_acronyms + * - \ref asfdoc_sam0_tcc_extra_dependencies + * - \ref asfdoc_sam0_tcc_extra_errata + * - \ref asfdoc_sam0_tcc_extra_history + * + * + * \section asfdoc_sam0_tcc_examples Examples + * + * For a list of examples related to this driver, see + * \ref asfdoc_sam0_tcc_exqsg. + * + * \section asfdoc_sam0_tcc_api_overview API Overview + * @{ + */ + +#include +#include +#include +#include + +/** Maximum number of channels supported by the driver + * (Channel index from 0 to \c TCC_NUM_CHANNELS - 1). + */ +#define TCC_NUM_CHANNELS 4 + +/** Maximum number of wave outputs lines supported by the driver + * (Output line index from 0 to \c TCC_NUM_WAVE_OUTPUTS - 1). + */ +#define TCC_NUM_WAVE_OUTPUTS 8 + +/** Maximum number of (recoverable) faults supported by the driver. */ +#define TCC_NUM_FAULTS 2 + +#if TCC_ASYNC == true +# include +#endif + +/** + * \name Driver Feature Definition + * Define port features set according to different device family. + * @{ +*/ +#if (SAML21) || (SAML22) || (SAMR30) || defined(__DOXYGEN__) +/** Generate DMA triggers */ +# define FEATURE_TCC_GENERATE_DMA_TRIGGER +#endif +/*@}*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Generates a table enum list entry for a given type + and index (e.g. "TCC_CALLBACK_MC_CHANNEL_0,"). */ +#define _TCC_ENUM(n, type) TCC_##type##_##n, + +/** Generates table enum list entries for all channels of a + given type and channel number on TCC module. */ +#define _TCC_CHANNEL_ENUM_LIST(type) \ + MREPEAT(TCC_NUM_CHANNELS, _TCC_ENUM, type##_CHANNEL) +/** Generates table enum list entries for all output of a + given type and waveform output number on TCC module. */ +#define _TCC_WO_ENUM_LIST(type) \ + MREPEAT(TCC_NUM_WAVE_OUTPUTS, _TCC_ENUM, type) + + +#if TCC_ASYNC == true +/** Enum for the possible callback types for the TCC module. */ +enum tcc_callback { + /** Callback for TCC overflow */ + TCC_CALLBACK_OVERFLOW, + /** Callback for TCC Retrigger */ + TCC_CALLBACK_RETRIGGER, + /** Callback for TCC counter event */ + TCC_CALLBACK_COUNTER_EVENT, + /** Callback for capture overflow error */ + TCC_CALLBACK_ERROR, + /** Callback for Recoverable Fault A */ + TCC_CALLBACK_FAULTA, + /** Callback for Recoverable Fault B */ + TCC_CALLBACK_FAULTB, + /** Callback for Non-Recoverable Fault 0 */ + TCC_CALLBACK_FAULT0, + /** Callback for Non-Recoverable Fault 1 */ + TCC_CALLBACK_FAULT1, + +# if defined(__DOXYGEN__) + /** Channel callback type table for TCC + * + * Each TCC module may contain several callback types for channels; each + * channel will have its own callback type in the table, with the channel + * index number substituted for "n" in the channel callback type + * (e.g. \c TCC_MATCH_CAPTURE_CHANNEL_0). + */ + TCC_CALLBACK_CHANNEL_n = n, +# else + /** Callbacks for Match/Capture channels, e.g., TCC_CALLBACK_CHANNEL_0 */ + _TCC_CHANNEL_ENUM_LIST(CALLBACK) +# endif + +# if !defined(__DOXYGEN__) + /** Number of available callbacks */ + TCC_CALLBACK_N +# endif +}; +#endif /* #if TCC_ASYNC == true */ + +/** + * \name Module Status Flags + * + * TCC status flags, returned by \ref tcc_get_status() and cleared by + * \ref tcc_clear_status(). + * + * @{ + */ + +/** Timer channel \c ch (0 ~ 3) has matched against its compare value, + * or has captured a new value. + */ +#define TCC_STATUS_CHANNEL_MATCH_CAPTURE(ch) (1UL << (ch)) +/** Timer channel \c ch (0 ~ 3) match/compare output state. */ +#define TCC_STATUS_CHANNEL_OUTPUT(ch) (1UL << ((ch)+8)) +/** A Non-Recoverable Fault \c x (0 ~ 1) has occurred. */ +#define TCC_STATUS_NON_RECOVERABLE_FAULT_OCCUR(x) (1UL << ((x)+16)) +/** A Recoverable Fault \c n (0 ~ 1 representing A ~ B) has occured. */ +#define TCC_STATUS_RECOVERABLE_FAULT_OCCUR(n) (1UL << ((n)+18)) +/** The Non-Recoverable Fault \c x (0 ~ 1) input is present. */ +#define TCC_STATUS_NON_RECOVERABLE_FAULT_PRESENT(x) (1UL << ((x)+20)) +/** A Recoverable Fault \c n (0 ~ 1 representing A ~ B) is present. */ +#define TCC_STATUS_RECOVERABLE_FAULT_PRESENT(n) (1UL << ((n)+22)) +/** Timer registers synchronization has completed, and the synchronized count + * value may be read. + */ +#define TCC_STATUS_SYNC_READY (1UL << 23) +/** A new value was captured before the previous value was read, resulting in + * lost data. + */ +#define TCC_STATUS_CAPTURE_OVERFLOW (1UL << 24) +/** A counter event occurred. */ +#define TCC_STATUS_COUNTER_EVENT (1UL << 25) +/** A counter retrigger occurred. */ +#define TCC_STATUS_COUNTER_RETRIGGERED (1UL << 26) +/** The timer count value has overflowed from its maximum value to its minimum + * when counting upward, or from its minimum value to its maximum when + * counting downward. + */ +#define TCC_STATUS_COUNT_OVERFLOW (1UL << 27) +/** Ramp period cycle index. + * In ramp operation, each two period cycles are marked as cycle A and B, + * the index 0 represents cycle A and 1 represents cycle B. */ +#define TCC_STATUS_RAMP_CYCLE_INDEX (1UL << 28) +/** The counter has been stopped (due to disable, stop command, or one-shot). */ +#define TCC_STATUS_STOPPED (1UL << 29) + +/** @} */ + +/** + * \brief Index of the match capture channels + * + * This enum is used to specify which capture/match channel to do + * operations on. + */ +enum tcc_match_capture_channel { +# if defined(__DOXYGEN__) + /** Match capture channel index table for TCC + * + * Each TCC module may contain several match capture channels; each channel + * will have its own index in the table, with the index number substituted + * for "n" in the index name (e.g. \c TCC_MATCH_CAPTURE_CHANNEL_0). + */ + TCC_MATCH_CAPTURE_CHANNEL_n = n, +# else + /** Indexes of match capture channels, e.g., TCC_MATCH_CAPTURE_CHANNEL_0 */ + _TCC_CHANNEL_ENUM_LIST(MATCH_CAPTURE) +# endif +# if !defined(__DOXYGEN__) + /** Number of supported channels */ + TCC_MATCH_CAPTURE_CHANNEL_N +# endif +}; + +/** + * \brief Index of the wave outputs + * + * This enum is used to specify which wave output to do + * operations on. + */ +enum tcc_wave_output { +# if defined(__DOXYGEN__) + /** Waveform output index table for TCC + * + * Each TCC module may contain several wave outputs; each output + * will have its own index in the table, with the index number substituted + * for "n" in the index name (e.g. \c TCC_WAVE_OUTPUT_0). + */ + TCC_WAVE_OUTPUT_n = n, +# else + /** Indexes of match capture channels, e.g., TCC_WAVEFORM_OUTPUT_0 */ + _TCC_WO_ENUM_LIST(WAVE_OUTPUT) +# endif +# if !defined(__DOXYGEN__) + /** Number of supported channels */ + TCC_WAVE_OUTPUT_N +# endif +}; + +/** + * \brief TCC wave generation mode enum + * + * This enum is used to specify the waveform generation mode. + * + */ +enum tcc_wave_generation { + /** Normal Frequency: Top is the PER register, output toggled on each + * compare match */ + TCC_WAVE_GENERATION_NORMAL_FREQ = 0, + /** Match Frequency: Top is CC0 register, output toggles on each update + * condition */ + TCC_WAVE_GENERATION_MATCH_FREQ = 1, + /** Single-Slope PWM: Top is the PER register, CCx controls duty cycle + * (output active when count is greater than CCx) */ + TCC_WAVE_GENERATION_SINGLE_SLOPE_PWM = 2, + + /** Double-slope (count up and down), non centre-aligned: Top is the PER + * register, CC[x] controls duty cycle while counting up and CC[x+N/2] + * controls it while counting down */ + TCC_WAVE_GENERATION_DOUBLE_SLOPE_CRITICAL = 4, + /** Double-slope (count up and down), interrupt/event at Bottom (Top is the + * PER register, output active when count is greater than CCx) */ + TCC_WAVE_GENERATION_DOUBLE_SLOPE_BOTTOM = 5, + /** Double-slope (count up and down), interrupt/event at Bottom and Top: (Top is the + * PER register, output active when count is lower than CCx) */ + TCC_WAVE_GENERATION_DOUBLE_SLOPE_BOTH = 6, + /** Double-slope (count up and down), interrupt/event at Top (Top is the + * PER register, output active when count is greater than CCx) */ + TCC_WAVE_GENERATION_DOUBLE_SLOPE_TOP = 7, +}; + +/** + * \brief Polarity of TCC wave generation on channels + * + * Specifies whether the wave output needs to be inverted or not. + */ +enum tcc_wave_polarity { + /** Wave output is not inverted */ + TCC_WAVE_POLARITY_0, + /** Wave output is inverted */ + TCC_WAVE_POLARITY_1 +}; + +/** + * \brief TCC pattern generator for outputs + * + * Used when disabling output pattern or when selecting a specific pattern. + */ +enum tcc_output_pattern { + /** SWAP output pattern is not used */ + TCC_OUTPUT_PATTERN_DISABLE, + /** Pattern 0 is applied to SWAP output */ + TCC_OUTPUT_PATTERN_0, + /** Pattern 1 is applied to SWAP output */ + TCC_OUTPUT_PATTERN_1 +}; + +/** + * \brief Ramp Operations which are supported in single-slope PWM generation + * + * Ramp operations which are supported in single-slope PWM generation. + */ +enum tcc_ramp { + /** Default timer/counter PWM operation */ + TCC_RAMP_RAMP1 = 0, + + /** Uses a single channel (CC0) to control both CC0/CC1 compare outputs. + * In cycle A, the channel 0 output is disabled, and + * in cycle B, the channel 1 output is disabled. */ + TCC_RAMP_RAMP2A, + + /** Uses channels CC0 and CC1 to control compare outputs. + * In cycle A, the channel 0 output is disabled, and + * in cycle B, the channel 1 output is disabled.*/ + TCC_RAMP_RAMP2 +}; + +/** + * \brief Ramp Index for TCC wave generation + * + * In ramp operation, each two period cycles are marked as cycle A and B, + * the index 0 represents cycle A and 1 represents cycle B. + */ +enum tcc_ramp_index { + /** Default, cycle index toggles. */ + TCC_RAMP_INDEX_DEFAULT, + /** Force next cycle to be cycle B (set to 1) */ + TCC_RAMP_INDEX_FORCE_B, + /** Force next cycle to be cycle A (clear to 0) */ + TCC_RAMP_INDEX_FORCE_A, + /** Force next cycle keeping the same as current */ + TCC_RAMP_INDEX_FORCE_KEEP +}; + +/** + * \brief TCC output inversion + * + * Used when enabling or disabling output inversion. + */ +enum tcc_output_invertion { + /** Output inversion not to be enabled */ + TCC_OUTPUT_INVERTION_DISABLE, + /** Invert the output from WO[x] */ + TCC_OUTPUT_INVERTION_ENABLE +}; + +/** + * \brief TCC Counter reload action enum + * + * This enum specify how the counter is reloaded and whether the prescaler + * should be restarted. + */ +enum tcc_reload_action { + /** The counter is reloaded/reset on the next GCLK and starts + * counting on the prescaler clock + */ + TCC_RELOAD_ACTION_GCLK, + /** The counter is reloaded/reset on the next prescaler clock + */ + TCC_RELOAD_ACTION_PRESC, + /** The counter is reloaded/reset on the next GCLK, and the + * prescaler is restarted as well + */ + TCC_RELOAD_ACTION_RESYNC +}; + + +/** + * \brief TCC clock prescaler values + * + * This enum is used to choose the clock prescaler + * configuration. The prescaler divides the clock frequency of the TCC + * module to operate TCC at a slower clock rate. + */ +enum tcc_clock_prescaler { + /** Divide clock by 1 */ + TCC_CLOCK_PRESCALER_DIV1, + /** Divide clock by 2 */ + TCC_CLOCK_PRESCALER_DIV2, + /** Divide clock by 4 */ + TCC_CLOCK_PRESCALER_DIV4, + /** Divide clock by 8 */ + TCC_CLOCK_PRESCALER_DIV8, + /** Divide clock by 16 */ + TCC_CLOCK_PRESCALER_DIV16, + /** Divide clock by 64 */ + TCC_CLOCK_PRESCALER_DIV64, + /** Divide clock by 256 */ + TCC_CLOCK_PRESCALER_DIV256, + /** Divide clock by 1024 */ + TCC_CLOCK_PRESCALER_DIV1024 +}; + +/** + * \brief TCC module count direction + * + * Used when selecting the Timer/Counter count direction. + */ +enum tcc_count_direction { + /** Timer should count upward */ + TCC_COUNT_DIRECTION_UP, + /** Timer should count downward */ + TCC_COUNT_DIRECTION_DOWN, +}; + +#ifdef FEATURE_TCC_GENERATE_DMA_TRIGGER +/** + * \brief TCC module counter overflow DMA request mode + * + * Used when selecting the Timer/Counter overflow DMA request mode. + */ +enum tcc_count_overflow_dma_trigger_mode { + /** TCC generates a DMA request on each cycle when an update condition + * is detected + */ + TCC_COUNT_OVERFLOW_DMA_TRIGGER_MODE_CONTINUE, + /** When an update condition is detected, the TCC generates a DMA trigger + * on the cycle following the DMA One-Shot Command written to the Control + * B register + */ + TCC_COUNT_OVERFLOW_DMA_TRIGGER_MODE_ONE_SHOT, +}; +#endif + +/** + * \brief Action to perform when the TCC module is triggered by events + * + * Event action to perform when the module is triggered by events. + */ +enum tcc_event_action { + /** No event action */ + TCC_EVENT_ACTION_OFF, + /** Stop counting, the counter will maintain its current value, waveforms + * are set to a defined Non-Recoverable State output + * (\ref tcc_non_recoverable_state_output). */ + TCC_EVENT_ACTION_STOP, + /** Re-trigger counter on event, may generate an event if the re-trigger + * event output is enabled. + * \note When re-trigger event action is enabled, enabling the counter + * will not start until the next incoming event appears. */ + TCC_EVENT_ACTION_RETRIGGER, + + /** Start counter when previously stopped. + * Start counting on the event rising edge. Further events will not + * restart the counter; + * the counter keeps on counting using prescaled GCLK_TCCx, until it + * reaches TOP or Zero + * depending on the direction. */ + TCC_EVENT_ACTION_START, + /** Count events; i.e. Increment or decrement depending on count + * direction. */ + TCC_EVENT_ACTION_COUNT_EVENT, + /** The event source must be an asynchronous event, input value will + * overrides the direction settings (input low: counting up, input high: + * counting down). */ + TCC_EVENT_ACTION_DIR_CONTROL, + /** Increment the counter on event, irrespective of count direction */ + TCC_EVENT_ACTION_INCREMENT, + /** Decrement the counter on event, irrespective of count direction */ + TCC_EVENT_ACTION_DECREMENT, + /** Count during active state of asynchronous event. In this case, + * depending on the count direction, the count will be incremented + * or decremented on each prescaled GCLK_TCCx, as long as the input + * event remains active. */ + TCC_EVENT_ACTION_COUNT_DURING_ACTIVE, + + /** Store period in capture register 0, pulse width in capture + * register 1 + */ + TCC_EVENT_ACTION_PERIOD_PULSE_WIDTH_CAPTURE, + /** Store pulse width in capture register 0, period in capture + * register 1 + */ + TCC_EVENT_ACTION_PULSE_WIDTH_PERIOD_CAPTURE, + + /** Generate Non-Recoverable Fault on event */ + TCC_EVENT_ACTION_NON_RECOVERABLE_FAULT, +}; + + +/** + * \brief Action to be performed when the TCC module is triggered by event0 + * + * Event action to perform when the module is triggered by event0. + */ +enum tcc_event0_action { + /** No event action */ + TCC_EVENT0_ACTION_OFF = TCC_EVENT_ACTION_OFF, + /** Re-trigger Counter on event */ + TCC_EVENT0_ACTION_RETRIGGER = TCC_EVENT_ACTION_RETRIGGER, + /** Count events (increment or decrement, depending on count direction) + */ + TCC_EVENT0_ACTION_COUNT_EVENT = TCC_EVENT_ACTION_COUNT_EVENT, + /** Start counter on event */ + TCC_EVENT0_ACTION_START = TCC_EVENT_ACTION_START, + /** Increment counter on event */ + TCC_EVENT0_ACTION_INCREMENT = TCC_EVENT_ACTION_INCREMENT, + /** Count during active state of asynchronous event */ + TCC_EVENT0_ACTION_COUNT_DURING_ACTIVE = TCC_EVENT_ACTION_COUNT_DURING_ACTIVE, + + /** Generate Non-Recoverable Fault on event */ + TCC_EVENT0_ACTION_NON_RECOVERABLE_FAULT = TCC_EVENT_ACTION_NON_RECOVERABLE_FAULT +}; + +/** + * \brief Action to perform when the TCC module is triggered by event1 + * + * Event action to perform when the module is triggered by event1. + */ +enum tcc_event1_action { + /** No event action */ + TCC_EVENT1_ACTION_OFF = TCC_EVENT_ACTION_OFF, + /** Re-trigger Counter on event */ + TCC_EVENT1_ACTION_RETRIGGER = TCC_EVENT_ACTION_RETRIGGER, + /** The event source must be an asynchronous event, and the input value + * will override the direction settings. + * If TCEINVx is 0 and input event is LOW: counter will count up. + * If TCEINVx is 0 and input event is HIGH: counter will count down. + */ + TCC_EVENT1_ACTION_DIR_CONTROL = TCC_EVENT_ACTION_DIR_CONTROL, + /** Stop counter on event */ + TCC_EVENT1_ACTION_STOP = TCC_EVENT_ACTION_STOP, + /** Decrement on event */ + TCC_EVENT1_ACTION_DECREMENT = TCC_EVENT_ACTION_DECREMENT, + + /** Store period in capture register 0, pulse width in capture + * register 1 + */ + TCC_EVENT1_ACTION_PERIOD_PULSE_WIDTH_CAPTURE = TCC_EVENT_ACTION_PERIOD_PULSE_WIDTH_CAPTURE, + /** Store pulse width in capture register 0, period in capture + * register 1 + */ + TCC_EVENT1_ACTION_PULSE_WIDTH_PERIOD_CAPTURE = TCC_EVENT_ACTION_PULSE_WIDTH_PERIOD_CAPTURE, + + /** Generate Non-Recoverable Fault on event */ + TCC_EVENT1_ACTION_NON_RECOVERABLE_FAULT = TCC_EVENT_ACTION_NON_RECOVERABLE_FAULT +}; + +/** + * \brief On which part of the counter cycle the counter event output is generated + * + * This enum is used to define the point at which the counter event is generated. + */ +enum tcc_event_generation_selection { + /** Counter Event is generated when a new counter cycle starts */ + TCC_EVENT_GENERATION_SELECTION_START, + /** Counter Event is generated when a counter cycle ends */ + TCC_EVENT_GENERATION_SELECTION_END, + /** Counter Event is generated when a counter cycle ends, except for the + * first and last cycles */ + TCC_EVENT_GENERATION_SELECTION_BETWEEN, + /** Counter Event is generated when a new counter cycle starts or ends */ + TCC_EVENT_GENERATION_SELECTION_BOUNDARY +}; + +/** + * \brief TCC channel operation modes + * + * To set a timer channel either in compare or in capture mode. + */ +enum tcc_channel_function { + /** TCC channel performs compare operation */ + TCC_CHANNEL_FUNCTION_COMPARE, + /** TCC channel performs capture operation */ + TCC_CHANNEL_FUNCTION_CAPTURE +}; + +/** + * \brief TCC (recoverable) fault Halt action + */ +enum tcc_fault_halt_action { + /** Halt action disabled. */ + TCC_FAULT_HALT_ACTION_DISABLE, + /** Hardware halt action, counter is halted until restart */ + TCC_FAULT_HALT_ACTION_HW_HALT, + /** Software halt action, counter is halted until fault bit cleared */ + TCC_FAULT_HALT_ACTION_SW_HALT, + /** Non-Recoverable fault, force output to pre-defined level */ + TCC_FAULT_HALT_ACTION_NON_RECOVERABLE +}; + +/** + * \brief TCC (recoverable) fault Capture action + */ +enum tcc_fault_capture_action { + /** Capture disabled */ + TCC_FAULT_CAPTURE_DISABLE, + /** Capture on Fault, each value is captured */ + TCC_FAULT_CAPTURE_EACH, + /** Capture the minimum detection, but notify on smaller ones */ + TCC_FAULT_CAPTURE_MINIMUM, + /** Capture the maximum detection, but notify on bigger ones */ + TCC_FAULT_CAPTURE_MAXIMUM, + /** Capture if the value is smaller than last, notify event or interrupt + * if previous stamp is confirmed to be "local minimum" (not bigger than + * current stamp). */ + TCC_FAULT_CAPTURE_SMALLER, + /** Capture if the value is bigger than last, notify event or interrupt + * if previous stamp is confirmed to be "local maximum" (not smaller than + * current stamp). */ + TCC_FAULT_CAPTURE_BIGGER, + /** Capture if the time stamps changes its increment direction */ + TCC_FAULT_CAPTURE_CHANGE +}; + +/** + * \brief Capture Channel triggered by TCC (recoverable) fault + */ +enum tcc_fault_capture_channel { + /** Recoverable fault triggers channel 0 capture operation */ + TCC_FAULT_CAPTURE_CHANNEL_0, + /** Recoverable fault triggers channel 1 capture operation */ + TCC_FAULT_CAPTURE_CHANNEL_1, + /** Recoverable fault triggers channel 2 capture operation */ + TCC_FAULT_CAPTURE_CHANNEL_2, + /** Recoverable fault triggers channel 3 capture operation */ + TCC_FAULT_CAPTURE_CHANNEL_3 +}; + +/** + * \brief TCC (recoverable) fault Input Source + */ +enum tcc_fault_source { + /** Fault input is disabled */ + TCC_FAULT_SOURCE_DISABLE, + /** Match Capture Event x (x=0,1) input */ + TCC_FAULT_SOURCE_ENABLE, + /** Inverted MCEx (x=0,1) event input */ + TCC_FAULT_SOURCE_INVERT, + /** Alternate fault (A or B) state at the end of the previous period */ + TCC_FAULT_SOURCE_ALTFAULT +}; + +/** + * \brief TCC (recoverable) fault Input Blanking Start Point + */ +enum tcc_fault_blanking { + /** No blanking */ + TCC_FAULT_BLANKING_DISABLE, + /** Blanking applied from rising edge of the output waveform */ + TCC_FAULT_BLANKING_RISING_EDGE, + /** Blanking applied from falling edge of the output waveform */ + TCC_FAULT_BLANKING_FALLING_EDGE, + /** Blanking applied from each toggle of the output waveform */ + TCC_FAULT_BLANKING_BOTH_EDGE +}; + +/** + * \brief TCC (recoverable) fault Input Qualification Action + */ +enum tcc_fault_qualification { + /** The input is not disabled on compare condition */ + TCC_FAULT_QUALIFICATION_DISABLE, + /** The input is disabled when match output signal is at inactive level */ + TCC_FAULT_QUALIFICATION_BY_OUTPUT +}; + +/** + * \brief TCC (recoverable) fault Output Keep Action + */ +enum tcc_fault_keep { + /** Disable keeping, wave output released as soon as fault is released */ + TCC_FAULT_KEEP_DISABLE, + /** Keep wave output until end of TCC cycle */ + TCC_FAULT_KEEP_TILL_END +}; + +/** + * \brief TCC Non-recoverable State Outupt + */ +enum tcc_fault_state_output { + /** Non-recoverable fault output is tri-stated */ + TCC_FAULT_STATE_OUTPUT_OFF, + /** Non-recoverable fault force output 0 */ + TCC_FAULT_STATE_OUTPUT_0, + /** Non-recoverable fault force output 1 */ + TCC_FAULT_STATE_OUTPUT_1 +}; + +/** + * \brief TCC (recoverable) fault Restart Action + */ +enum tcc_fault_restart { + /** Restart Action disabled */ + TCC_FAULT_RESTART_DISABLE, + /** Restart Action enabled */ + TCC_FAULT_RESTART_ENABLE +}; + +/** + * \brief Configuration struct for TCC module recoverable fault + */ +struct tcc_recoverable_fault_config { + /** Fault filter value applied on MCEx event input line (0x0 ~ 0xF). + * Must be 0 when MCEx event is used as synchronous event. + * Apply to both recoverable and non-recoverable fault. */ + uint8_t filter_value; + /** Fault blanking value (0 ~ 255), disable input source for several TCC + * clocks after the detection of the waveform edge */ + uint8_t blanking_cycles; + + /** Set to \c true to enable restart action */ + bool restart; + /** Set to \c true to enable keep action (keep until end of TCC cycle) */ + bool keep; + + /** Set to \c true to enable input qualification + * (disable input when output is inactive) */ + bool qualification; + + /** Specifies if the event input generates recoverable Fault. + * The event system channel connected to MCEx event input must be + * configured as asynchronous. + */ + enum tcc_fault_source source; + /** Fault Blanking Start Point for recoverable Fault */ + enum tcc_fault_blanking blanking; + + /** Halt action for recoverable Fault */ + enum tcc_fault_halt_action halt_action; + /** Capture action for recoverable Fault */ + enum tcc_fault_capture_action capture_action; + /** Channel triggered by recoverable Fault */ + enum tcc_fault_capture_channel capture_channel; +}; + +/** + * \brief Configuration struct for TCC module non-recoverable fault + */ +struct tcc_non_recoverable_fault_config { + /** Fault filter value applied on TCEx event input line (0x0 ~ 0xF). + * Must be 0 when TCEx event is used as synchronous event. */ + uint8_t filter_value; + /** Output */ + enum tcc_fault_state_output output; +}; + +/** + * \brief TCC input event enable/disable/configure structure + * + * For configuring an input event. + */ +struct tcc_input_event_config { + /** Event action on incoming event */ + enum tcc_event_action action; + /** Modify event action */ + bool modify_action; + /** Invert incoming event input line */ + bool invert; +}; + +/** + * \brief TCC output event enable/disable/configure structure + * + * Structure used for configuring an output event. + */ +struct tcc_output_event_config { + /** It decides which part of the counter cycle the counter event output + * is generated */ + enum tcc_event_generation_selection generation_selection; + /** A switch to allow enable/disable of events, without modifying the + * event output configuration + */ + bool modify_generation_selection; +}; + +/** + * \brief TCC event enable/disable structure + * + * Event flags for the \ref tcc_enable_events() and \ref tcc_disable_events(). + */ +struct tcc_events { + /** Input events configuration */ + struct tcc_input_event_config input_config[2]; + /** Output event configuration */ + struct tcc_output_event_config output_config; + + /** Perform the configured event action when an incoming event is + * signalled */ + bool on_input_event_perform_action[2]; + + /** Perform the configured event action when an incoming channel event is + * signalled */ + bool on_event_perform_channel_action[TCC_NUM_CHANNELS]; + /** Generate an output event on a channel capture/match. + * Specify which channels will generate events */ + bool generate_event_on_channel[TCC_NUM_CHANNELS]; + + /** Generate an output event on counter overflow/underflow */ + bool generate_event_on_counter_overflow; + /** Generate an output event on counter retrigger */ + bool generate_event_on_counter_retrigger; + /** Generate an output event on counter boundary. + * See \ref tcc_event_output_action. */ + bool generate_event_on_counter_event; +}; + +/** + * \brief Configuration struct for the TCC module base counter + * + * Structure for configuring a TCC as a counter. + */ +struct tcc_counter_config { + /** Value to initialize the count register */ + uint32_t count; + /** Period/top and period/top buffer values for counter */ + uint32_t period; + + /** When \c true, the counter will be stopped on the next hardware or + * software re-trigger event or overflow/underflow + */ + bool oneshot; + +#ifdef FEATURE_TCC_GENERATE_DMA_TRIGGER + /** Counter overflow trigger a DMA request mode */ + enum tcc_count_overflow_dma_trigger_mode dma_trigger_mode; +#endif + + /** Specifies the direction for the TCC to count */ + enum tcc_count_direction direction; + + /** GCLK generator used to clock the peripheral */ + enum gclk_generator clock_source; + /** Specifies the prescaler value for GCLK_TCC */ + enum tcc_clock_prescaler clock_prescaler; + /** Specifies the reload or reset time of the counter and prescaler + * resynchronization on a re-trigger event for the TCC + */ + enum tcc_reload_action reload_action; +}; + +/** + * \brief Configuration struct for the TCC module capture + * + * Structure used when configuring TCC channels in capture mode. + */ +struct tcc_capture_config { + /** Channel functions selection (capture/match) */ + enum tcc_channel_function channel_function[TCC_NUM_CHANNELS]; +}; + +/** + * \brief Configuration struct for the TCC module match/wave generation + * + * The structure, which helps to configure a TCC channel for compare + * operation and wave generation. + */ +struct tcc_match_wave_config { + /** Channel functions selection (capture/match) */ + enum tcc_channel_function channel_function[TCC_NUM_CHANNELS]; + + /** Specifies polarity for match output waveform generation */ + enum tcc_wave_polarity wave_polarity[TCC_NUM_CHANNELS]; + /** Specifies which waveform generation mode to use */ + enum tcc_wave_generation wave_generation; + /** Specifies Ramp mode for waveform generation */ + enum tcc_ramp wave_ramp; + + /** Value to be used for compare match on each channel */ + uint32_t match[TCC_NUM_CHANNELS]; +}; + +/** + * \brief Configuration struct for the TCC module waveform extension + * + * This structure is used to specify the waveform extension features for TCC. + */ +struct tcc_wave_extension_config { + /** Configuration for recoverable faults */ + struct tcc_recoverable_fault_config + recoverable_fault[TCC_NUM_FAULTS]; + /** Configuration for non-recoverable faults */ + struct tcc_non_recoverable_fault_config + non_recoverable_fault[TCC_NUM_WAVE_OUTPUTS]; + + /** Invert waveform final outputs lines */ + bool invert[TCC_NUM_WAVE_OUTPUTS]; +}; + +/** + * \brief Configuration struct for the TCC module output pins + * + * Structure which is used when taking wave output from TCC. + */ +struct tcc_pins_config { + /** Specifies pin output for each channel */ + uint32_t wave_out_pin[TCC_NUM_WAVE_OUTPUTS]; + /** Specifies MUX setting for each output channel pin */ + uint32_t wave_out_pin_mux[TCC_NUM_WAVE_OUTPUTS]; + /** When \c true, PWM output pin for the given channel is enabled */ + bool enable_wave_out_pin[TCC_NUM_WAVE_OUTPUTS]; +}; + +/** + * \brief TCC configuration structure + * + * Configuration struct for a TCC instance. This structure should be + * initialized by the \ref tcc_get_config_defaults function before being + * modified by the user application. + */ +struct tcc_config { + /** Structure for configuring TCC base timer/counter */ + struct tcc_counter_config counter; + /** TCC match/capture configurations */ + union { + /** Helps to configure a TCC channel in capture mode */ + struct tcc_capture_config capture; + /** For configuring a TCC channel in compare mode */ + struct tcc_match_wave_config compare; + /** Serves the same purpose as compare. Used as an alias for + * compare, + * when a TCC channel is configured for wave generation */ + struct tcc_match_wave_config wave; + }; + + /** Structure for configuring TCC waveform extension */ + struct tcc_wave_extension_config wave_ext; + + /** Structure for configuring TCC output pins */ + struct tcc_pins_config pins; + + /** Set to \c true to enable double buffering write. When enabled any write + * through \ref tcc_set_top_value(), \ref tcc_set_compare_value() and + * \ref tcc_set_pattern() will direct to the buffer register as buffered + * value, and the buffered value will be committed to effective register + * on UPDATE condition, if update is not locked. + * + * \note The init values in \ref tcc_config for \ref tcc_init are always + * filled to effective registers, no matter if double buffering is + * enabled or not. + */ + bool double_buffering_enabled; + + /** When \c true the module is enabled during standby */ + bool run_in_standby; +}; + +#if TCC_ASYNC == true +/* Forward Declaration for the device instance. */ +struct tcc_module; + +/** Type definition for the TCC callback function. */ +typedef void (*tcc_callback_t)(struct tcc_module *const module); +#endif + +/** + * \brief TCC software device instance structure + * + * TCC software instance structure, used to retain software state information + * of an associated hardware module instance. + * + * \note The fields of this structure should not be altered by the user + * application; they are reserved only for module-internal use. + */ +struct tcc_module { + /** Hardware module pointer of the associated Timer/Counter peripheral. */ + Tcc *hw; + +# if TCC_ASYNC == true + /** Array of callbacks */ + tcc_callback_t callback[TCC_CALLBACK_N]; + /** Bit mask for callbacks registered */ + uint32_t register_callback_mask; + /** Bit mask for callbacks enabled */ + uint32_t enable_callback_mask; +# endif + + /** Set to \c true to write to buffered registers */ + bool double_buffering_enabled; +}; + +#if !defined(__DOXYGEN__) +uint8_t _tcc_get_inst_index( + Tcc *const hw); +#endif + +/** + * \name Driver Initialization and Configuration + * @{ + */ + +/** + * \brief Determines if the hardware module is currently synchronizing to the bus + * + * Checks to see if the underlying hardware peripheral module is 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. + * + * \param[in] module_inst Pointer to the software module instance struct + * + * \return Synchronization status of the underlying hardware module. + * + * \retval false If the module has completed synchronization + * \retval true If the module synchronization is ongoing + */ +static inline bool tcc_is_syncing( + const struct tcc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + return (module_inst->hw->SYNCBUSY.reg > 0); +} + + +void tcc_get_config_defaults( + struct tcc_config *const config, + Tcc *const hw); + +enum status_code tcc_init( + struct tcc_module *const module_inst, + Tcc *const hw, + const struct tcc_config *const config); + +/** @} */ + +/** + * \name Event Management + * @{ + */ + +enum status_code tcc_enable_events( + struct tcc_module *const module_inst, + struct tcc_events *const events); + +void tcc_disable_events( + struct tcc_module *const module_inst, + struct tcc_events *const events); + +/** @} */ + +/** + * \name Enable/Disable/Reset + * @{ + */ + +/** + * \brief Enable the TCC module + * + * Enables a TCC module that has been previously initialized. The counter will + * start when the counter is enabled. + * + * \note When the counter is configured to re-trigger on an event, the counter + * will not start until the next incoming event appears. Then it + * restarts on any following event. + * + * \param[in] module_inst Pointer to the software module instance struct + */ +static inline void tcc_enable( + const struct tcc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + Tcc *const tcc_module = module_inst->hw; + + while (tcc_module->SYNCBUSY.reg & TCC_SYNCBUSY_ENABLE) { + /* Wait for sync */ + } + + /* Enable the TCC module */ + tcc_module->CTRLA.reg |= TCC_CTRLA_ENABLE; +} + +/** + * \brief Disables the TCC module + * + * Disables a TCC module and stops the counter. + * + * \param[in] module_inst Pointer to the software module instance struct + */ +static inline void tcc_disable( + const struct tcc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + Tcc *const tcc_module = module_inst->hw; + + while (tcc_module->SYNCBUSY.reg & TCC_SYNCBUSY_ENABLE) { + /* Wait for sync */ + } + + /* Disbale interrupt */ + tcc_module->INTENCLR.reg = TCC_INTENCLR_MASK; + /* Clear interrupt flag */ + tcc_module->INTFLAG.reg = TCC_INTFLAG_MASK; + + /* Disable the TCC module */ + tcc_module->CTRLA.reg &= ~TC_CTRLA_ENABLE; +} + +/** + * \brief Resets the TCC module + * + * Resets the TCC module, restoring all hardware module registers to their + * default values and disabling the module. The TCC module will not be + * accessible while the reset is being performed. + * + * \note When resetting a 32-bit counter only the master TCC module's instance + * structure should be passed to the function. + * + * \param[in] module_inst Pointer to the software module instance struct + * + */ +static inline void tcc_reset( + const struct tcc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module hardware instance */ + Tcc *const tcc_module = module_inst->hw; + + /* Disable this module if it is running */ + if (tcc_module->CTRLA.reg & TCC_CTRLA_ENABLE) { + tcc_disable(module_inst); + while (tcc_is_syncing(module_inst)) { + /* wait while module is disabling */ + } + } + + /* Reset this TC module */ + tcc_module->CTRLA.reg |= TCC_CTRLA_SWRST; +} + +/** @} */ + + +/** + * \name Set/Toggle Count Direction + * @{ + */ + +/** + * \brief Sets the TCC module count direction + * + * Sets the count direction of an initialized TCC module. The + * specified TCC module can remain running or stopped. + * + * \param[in] module_inst Pointer to the software module instance struct + * \param[in] dir New timer count direction to set + */ +static inline void tcc_set_count_direction( + const struct tcc_module *const module_inst, + enum tcc_count_direction dir) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + Tcc *const tcc_module = module_inst->hw; + + while (tcc_module->SYNCBUSY.reg & TCC_SYNCBUSY_CTRLB) { + /* Wait for sync */ + } + + /* Set count direction */ + if (TCC_COUNT_DIRECTION_DOWN == dir) { + tcc_module->CTRLBSET.reg = TCC_CTRLBSET_DIR; + return; + } + tcc_module->CTRLBCLR.reg = TCC_CTRLBCLR_DIR; +} + +/** + * \brief Toggles the TCC module count direction + * + * Toggles the count direction of an initialized TCC module. The + * specified TCC module can remain running or stopped. + * + * \param[in] module_inst Pointer to the software module instance struct + */ +static inline void tcc_toggle_count_direction( + const struct tcc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + Tcc *const tcc_module = module_inst->hw; + + while (tcc_module->SYNCBUSY.reg & TCC_SYNCBUSY_CTRLB) { + /* Wait for sync */ + } + bool dir_value_1 = tcc_module->CTRLBSET.bit.DIR; + if (dir_value_1) { + tcc_module->CTRLBCLR.reg = TCC_CTRLBCLR_DIR; + } else { + tcc_module->CTRLBSET.reg = TCC_CTRLBSET_DIR; + } +} + +/** @} */ + +/** + * \name Get/Set Count Value + * @{ + */ + +uint32_t tcc_get_count_value( + const struct tcc_module *const module_inst); + +enum status_code tcc_set_count_value( + const struct tcc_module *const module_inst, + const uint32_t count); + +/** @} */ + +/** + * \name Stop/Restart Counter + * @{ + */ + +/** + * \brief Stops the counter + * + * This function will stop the counter. When the counter is stopped + * the value in the count register is set to 0 if the counter was + * counting up, or maximum or the top value if the counter was counting + * down. + * + * \param[in] module_inst Pointer to the software module instance struct + */ +static inline void tcc_stop_counter( + const struct tcc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + Tcc *const tcc_module = module_inst->hw; + uint32_t last_cmd; + + /* Wait until last command is done */ + do { + while (tcc_module->SYNCBUSY.reg & TCC_SYNCBUSY_CTRLB) { + /* Wait for sync */ + } + last_cmd = tcc_module->CTRLBSET.reg & TCC_CTRLBSET_CMD_Msk; + if (last_cmd == TCC_CTRLBSET_CMD_NONE) { + break; + } else if (last_cmd == TCC_CTRLBSET_CMD_STOP) { + /* Command have been issued */ + return; + } else if (last_cmd == TCC_CTRLBSET_CMD_RETRIGGER) { + /* Cancel RETRIGGER command and issue STOP */ + tcc_module->CTRLBCLR.reg = TCC_CTRLBCLR_CMD_Msk; + } + } while (1); + + /* Write command to execute */ + tcc_module->CTRLBSET.reg = TCC_CTRLBSET_CMD_STOP; +} + +/** + * \brief Starts the counter from beginning + * + * Restarts an initialized TCC module's counter. + * + * \param[in] module_inst Pointer to the software module instance struct + */ +static inline void tcc_restart_counter( + const struct tcc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + Tcc *const tcc_module = module_inst->hw; + uint32_t last_cmd; + + /* Wait until last command is done */ + do { + while (tcc_module->SYNCBUSY.reg & TCC_SYNCBUSY_CTRLB) { + /* Wait for sync */ + } + last_cmd = tcc_module->CTRLBSET.reg & TCC_CTRLBSET_CMD_Msk; + if (last_cmd == TCC_CTRLBSET_CMD_NONE) { + break; + } else if (last_cmd == TCC_CTRLBSET_CMD_RETRIGGER) { + /* Command have been issued */ + return; + } else if (last_cmd == TCC_CTRLBSET_CMD_STOP) { + /* Cancel STOP command and issue RETRIGGER */ + tcc_module->CTRLBCLR.reg = TCC_CTRLBCLR_CMD_Msk; + } + } while (1); + + /* Write command to execute */ + tcc_module->CTRLBSET.reg = TCC_CTRLBSET_CMD_RETRIGGER; +} + +/** @} */ + +#ifdef FEATURE_TCC_GENERATE_DMA_TRIGGER +/** + * \name Generate TCC DMA Triggers Command + * @{ + */ + +/** + * \brief TCC DMA Trigger. + * + * TCC DMA trigger command. + * + * \param[in] module_inst Pointer to the software module instance struct + */ +static inline void tcc_dma_trigger_command( + const struct tcc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + Tcc *const tcc_module = module_inst->hw; + + while (tcc_module->SYNCBUSY.reg & TCC_SYNCBUSY_CTRLB) { + /* Wait for sync */ + } + + /* Make certain that there are no conflicting commands in the register */ + tcc_module->CTRLBCLR.reg = TCC_CTRLBCLR_CMD_NONE; + + while (tcc_module->SYNCBUSY.reg & TCC_SYNCBUSY_CTRLB) { + /* Wait for sync */ + } + +#if !(SAML21 || SAML22 || SAMR30) + /* Write command to execute */ + tcc_module->CTRLBSET.reg = TCC_CTRLBSET_CMD_DMATRG; +#endif + +#if (SAML21XXXB) || (SAML22) || (SAMR30) + /* Write command to execute */ + tcc_module->CTRLBSET.reg = TCC_CTRLBSET_CMD_DMAOS; +#endif +} +/** @} */ +#endif + +/** + * \name Get/Set Compare/Capture Register + * @{ + */ + +uint32_t tcc_get_capture_value( + const struct tcc_module *const module_inst, + const enum tcc_match_capture_channel channel_index); + +enum status_code tcc_set_compare_value( + const struct tcc_module *const module_inst, + const enum tcc_match_capture_channel channel_index, + const uint32_t compare); + +/** @} */ + +/** + * \name Set Top Value + * @{ + */ + +enum status_code tcc_set_top_value( + const struct tcc_module *const module_inst, + const uint32_t top_value); + +/** @} */ + + +/** + * \name Set Output Pattern + * @{ + */ + +enum status_code tcc_set_pattern( + const struct tcc_module *const module_inst, + const uint32_t line_index, + const enum tcc_output_pattern pattern); + +/** @} */ + + +/** + * \name Set Ramp Index + * @{ + */ + +/** + * \brief Sets the TCC module ramp index on next cycle + * + * In RAMP2 and RAMP2A operation, we can force either cycle A or cycle B at + * the output, on the next clock cycle. + * When ramp index command is disabled, cycle A and cycle B will appear at + * the output, on alternate clock cycles. + * See \ref tcc_ramp. + * + * \param[in] module_inst Pointer to the software module instance struct + * \param[in] ramp_index Ramp index (\ref tcc_ramp_index) of the next cycle + */ +static inline void tcc_set_ramp_index( + const struct tcc_module *const module_inst, + const enum tcc_ramp_index ramp_index) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + Tcc *const tcc_module = module_inst->hw; + uint32_t last_cmd; + + /* Wait until last command is done */ + do { + while (tcc_module->SYNCBUSY.reg & TCC_SYNCBUSY_CTRLB) { + /* Wait for sync */ + } + if (TCC_RAMP_INDEX_DEFAULT == ramp_index) { + /* Cancel pending command */ + tcc_module->CTRLBCLR.reg = TCC_CTRLBSET_IDXCMD_HOLD; + return; + } + last_cmd = tcc_module->CTRLBSET.reg & TCC_CTRLBSET_IDXCMD_Msk; + if (last_cmd == TCC_CTRLBSET_IDXCMD_DISABLE) { + break; + } else if (last_cmd == TCC_CTRLBSET_IDXCMD(ramp_index)) { + /* Command have been issued */ + return; + } + } while (1); + + /* Write command to execute */ + tcc_module->CTRLBSET.reg = TCC_CTRLBSET_IDXCMD(ramp_index); +} + +/** @} */ + +/** + * \name Status Management + * @{ + */ + +/** + * \brief Checks if the timer/counter is running + * + * \param[in] module_inst Pointer to the TCC software instance struct + * + * \return Status which indicates whether the module is running. + * + * \retval true The timer/counter is running + * \retval false The timer/counter is stopped + */ +static inline bool tcc_is_running( + struct tcc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + return !module_inst->hw->STATUS.bit.STOP; +} + +uint32_t tcc_get_status( + struct tcc_module *const module_inst); + +void tcc_clear_status( + struct tcc_module *const module_inst, + const uint32_t status_flags); + +/** @} */ + +/** + * \name Double Buffering Management + * @{ + */ + +/** + * \brief Enable TCC double buffering write + * + * When double buffering write is enabled, the following function will write + * values to buffered registers instead of effective ones (buffered): + * - PERB: through \ref tcc_set_top_value() + * - CCBx(x is 0~3): through \ref tcc_set_compare_value() + * - PATTB: through \ref tcc_set_pattern() + * + * Then, on UPDATE condition the buffered registers are committed to regular ones + * to take effect. + * + * \param[in] module_inst Pointer to the TCC software instance struct + */ +static inline void tcc_enable_double_buffering( + struct tcc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + + module_inst->double_buffering_enabled = true; +} + +/** + * \brief Disable TCC double buffering Write + * + * When double buffering write is disabled, following function will write values + * to effective registers (not buffered): + * - PER: through \ref tcc_set_top_value() + * - CCx(x is 0~3): through \ref tcc_set_compare_value() + * - PATT: through \ref tcc_set_pattern() + * + * \note This function does not lock double buffer update, which means on next + * UPDATE condition the last written buffered values will be committed to + * take effect. Invoke \ref tcc_lock_double_buffer_update() before this + * function to disable double buffering update, if this change is not + * expected. + * + * \param[in] module_inst Pointer to the TCC software instance struct + */ +static inline void tcc_disable_double_buffering( + struct tcc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + module_inst->double_buffering_enabled = false; +} + +/** + * \brief Lock the TCC double buffered registers updates + * + * Locks the double buffered registers so they will not be updated through + * their buffered values on UPDATE conditions. + * + * \param[in] module_inst Pointer to the TCC software instance struct + * + */ +static inline void tcc_lock_double_buffer_update( + struct tcc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + while (module_inst->hw->SYNCBUSY.reg & TCC_SYNCBUSY_CTRLB) { + /* Wait for sync */ + } + module_inst->hw->CTRLBSET.reg = TCC_CTRLBSET_LUPD; +} + +/** + * \brief Unlock the TCC double buffered registers updates + * + * Unlock the double buffered registers so they will be updated through + * their buffered values on UPDATE conditions. + * + * \param[in] module_inst Pointer to the TCC software instance struct + * + */ +static inline void tcc_unlock_double_buffer_update( + struct tcc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + while (module_inst->hw->SYNCBUSY.reg & TCC_SYNCBUSY_CTRLB) { + /* Wait for sync */ + } + module_inst->hw->CTRLBCLR.reg = TCC_CTRLBCLR_LUPD; +} + +/** + * \brief Force the TCC double buffered registers to update once + * + * \param[in] module_inst Pointer to the TCC software instance struct + * + */ +static inline void tcc_force_double_buffer_update( + struct tcc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Get a pointer to the module's hardware instance */ + Tcc *const tcc_module = module_inst->hw; + uint32_t last_cmd; + + /* Wait until last command is done */ + do { + while (tcc_module->SYNCBUSY.reg & TCC_SYNCBUSY_CTRLB) { + /* Wait for sync */ + } + last_cmd = tcc_module->CTRLBSET.reg & TCC_CTRLBSET_CMD_Msk; + if (last_cmd == TCC_CTRLBSET_CMD_NONE) { + break; + } else if (last_cmd == TCC_CTRLBSET_CMD_UPDATE) { + /* Command have been issued */ + return; + } + } while (1); + + /* Write command to execute */ + tcc_module->CTRLBSET.reg = TCC_CTRLBSET_CMD_UPDATE; +} + +/** + * \brief Enable Circular option for double buffered Top/Period Values + * + * Enable circular option for the double buffered top/period values. + * On each UPDATE condition, the contents of PERB and PER are switched, meaning + * that the contents of PERB are transferred to PER and the contents of PER are + * transferred to PERB. + * + * \param[in] module_inst Pointer to the TCC software instance struct + */ +static inline void tcc_enable_circular_buffer_top( + struct tcc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + module_inst->hw->WAVE.reg |= TCC_WAVE_CIPEREN; +} + +/** + * \brief Disable Circular option for double buffered Top/Period Values + * + * Stop circularing the double buffered top/period values. + * + * \param[in] module_inst Pointer to the TCC software instance struct + */ +static inline void tcc_disable_circular_buffer_top( + struct tcc_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + module_inst->hw->WAVE.reg &= ~TCC_WAVE_CIPEREN; +} + +enum status_code tcc_set_double_buffer_top_values( + const struct tcc_module *const module_inst, + const uint32_t top_value, const uint32_t top_buffer_value); + + +enum status_code tcc_enable_circular_buffer_compare( + struct tcc_module *const module_inst, + enum tcc_match_capture_channel channel_index); +enum status_code tcc_disable_circular_buffer_compare( + struct tcc_module *const module_inst, + enum tcc_match_capture_channel channel_index); +enum status_code tcc_set_double_buffer_compare_values( + struct tcc_module *const module_inst, + enum tcc_match_capture_channel channel_index, + const uint32_t compare, + const uint32_t compare_buffer); + + +/** @} */ + +/** @} */ + +#ifdef __cplusplus +} +#endif + +/** + * \page asfdoc_sam0_tcc_extra Extra Information for TCC Driver + * + * \section asfdoc_sam0_tcc_extra_acronyms Acronyms + * The table below presents the acronyms used in this module: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
AcronymDescription
DMADirect Memory Access
TCCTimer Counter for Control Applications
PWMPulse Width Modulation
PWPPulse Width Period
PPWPeriod Pulse Width
+ * + * + * \section asfdoc_sam0_tcc_extra_dependencies Dependencies + * This driver has the following dependencies: + * + * - \ref asfdoc_sam0_system_pinmux_group "System Pin Multiplexer Driver" + * + * + * \section asfdoc_sam0_tcc_extra_errata Errata + * There are no errata related to this driver. + * + * + * \section asfdoc_sam0_tcc_extra_history Module History + * An overview of the module history is presented in the table below, with + * details on the enhancements and fixes made to the module since its first + * release. The current version of this corresponds to the newest version in + * the table. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Changelog
Add double buffering functionality
Add fault handling functionality
Initial Release
+ */ + +/** + * \page asfdoc_sam0_tcc_exqsg Examples for TCC Driver + * + * This is a list of the available Quick Start guides (QSGs) and example + * applications for \ref asfdoc_sam0_tcc_group. QSGs are simple examples with + * step-by-step instructions to configure and use this driver in a selection of + * use cases. Note that QSGs can be compiled as a standalone application or be + * added to the user application. + * + * - \subpage asfdoc_sam0_tcc_basic_use_case + * - \subpage asfdoc_sam0_tcc_buffering_use_case + * \if TCC_CALLBACK_MODE + * - \subpage asfdoc_sam0_tcc_timer_use_case + * - \subpage asfdoc_sam0_tcc_callback_use_case + * - \subpage asfdoc_sam0_tcc_faultx_use_case + * - \subpage asfdoc_sam0_tcc_faultn_use_case + * \endif + * - \subpage asfdoc_sam0_tcc_dma_use_case + * + * \page asfdoc_sam0_tcc_document_revision_history Document Revision History + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Doc. Rev.DateComments
42256C12/2015Added support for SAM L21/L22, SAM DA1, and SAM C20/C21
42256B12/2014Added fault handling functionality. + * Added double buffering functionality with use case. + * Added timer use case. + * Added SAM R21/D10/D11 support.
42256A01/2014Initial release
+ */ + +#endif /* TCC_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/tcc/tcc_callback.c b/atmel-samd/asf/sam0/drivers/tcc/tcc_callback.c new file mode 100644 index 0000000000..aa1d5e3e94 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/tcc/tcc_callback.c @@ -0,0 +1,245 @@ +/** + * \file + * + * \brief SAM TCC - Timer Counter for Control Applications Callback Driver + * + * Copyright (C) 2013-2016 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 + * + */ +/* + * Support and FAQ: visit Atmel Support + */ + +#include "tcc_callback.h" + +void *_tcc_instances[TCC_INST_NUM]; + +void _tcc_interrupt_handler(uint8_t module_index); + +const uint32_t _tcc_intflag[TCC_CALLBACK_N] = { + TCC_INTFLAG_OVF, + TCC_INTFLAG_TRG, + TCC_INTFLAG_CNT, + TCC_INTFLAG_ERR, + TCC_INTFLAG_FAULTA, + TCC_INTFLAG_FAULTB, + TCC_INTFLAG_FAULT0, + TCC_INTFLAG_FAULT1, +#define _TCC_INTFLAG_MC(n,dummy) TCC_INTFLAG_MC##n, + /* TCC_INTFLAG_MC0 ~ ... */ + MREPEAT(TCC_NUM_CHANNELS, _TCC_INTFLAG_MC, 0) +#undef _TCC_INTFLAG_MC +}; + +# define _TCC_INTERRUPT_VECT_NUM(n, unused) \ + SYSTEM_INTERRUPT_MODULE_TCC##n, +/** + * \internal Get the interrupt vector for the given device instance + * + * \param[in] The TCC module instance number + * + * \return Interrupt vector for of the given TCC module instance. + */ +static enum system_interrupt_vector _tcc_interrupt_get_interrupt_vector( + uint32_t inst_num) +{ + static uint8_t tcc_interrupt_vectors[TCC_INST_NUM] = { + MREPEAT(TCC_INST_NUM, _TCC_INTERRUPT_VECT_NUM, 0) + }; + + return (enum system_interrupt_vector)tcc_interrupt_vectors[inst_num]; +} + +/** + * \brief Registers a callback + * + * Registers a callback function which is implemented by the user. + * + * \note The callback must be enabled by \ref tcc_enable_callback, + * in order for the interrupt handler to call it when the conditions for the + * callback type is met. + * + * \param[in] module Pointer to TCC software instance struct + * \param[in] callback_func Pointer to callback function + * \param[in] callback_type Callback type given by an enum + */ +enum status_code tcc_register_callback( + struct tcc_module *const module, + tcc_callback_t callback_func, + const enum tcc_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module); + Assert(callback_func); + + /* Register callback function */ + module->callback[callback_type] = callback_func; + + /* Set the bit corresponding to the callback_type */ + module->register_callback_mask |= _tcc_intflag[callback_type]; + + return STATUS_OK; +} + +/** + * \brief Unregisters a callback + * + * Unregisters a callback function implemented by the user. The callback should + * be disabled before it is unregistered. + * + * \param[in] module Pointer to TCC software instance struct + * \param[in] callback_type Callback type given by an enum + */ +enum status_code tcc_unregister_callback( + struct tcc_module *const module, + const enum tcc_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module); + + /* Unregister callback function */ + module->callback[callback_type] = NULL; + + /* Clear the bit corresponding to the callback_type */ + module->register_callback_mask &= ~_tcc_intflag[callback_type]; + + return STATUS_OK; +} + +/** + * \brief Enables callback + * + * Enables the callback function registered by the \ref + * tcc_register_callback. The callback function will be called from the + * interrupt handler when the conditions for the callback type are + * met. This function will also enable the appropriate interrupts. + * + * \param[in] module Pointer to TCC software instance struct + * \param[in] callback_type Callback type given by an enum + */ +void tcc_enable_callback( + struct tcc_module *const module, + const enum tcc_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Enable interrupts for this TCC module */ + system_interrupt_enable(_tcc_interrupt_get_interrupt_vector( + _tcc_get_inst_index(module->hw))); + + /* Enable channel or other callbacks */ + module->enable_callback_mask |= _tcc_intflag[callback_type]; + module->hw->INTENSET.reg = _tcc_intflag[callback_type]; +} + +/** + * \brief Disables callback + * + * Disables the callback function registered by the \ref + * tcc_register_callback, and the callback will not be called from the + * interrupt routine. The function will also disable the appropriate + * interrupts. + * + * \param[in] module Pointer to TCC software instance struct + * \param[in] callback_type Callback type given by an enum + */ +void tcc_disable_callback( + struct tcc_module *const module, + const enum tcc_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Disable interrupts for this TCC module */ + system_interrupt_disable(_tcc_interrupt_get_interrupt_vector( + _tcc_get_inst_index(module->hw))); + + /* Disable channel or other callbacks */ + module->enable_callback_mask &= ~_tcc_intflag[callback_type]; + module->hw->INTENCLR.reg = _tcc_intflag[callback_type]; +} + + +/** + * \internal ISR handler for TCC + * + * Auto-generate a set of interrupt handlers for each TCC in the device. + */ +#define _TCC_INTERRUPT_HANDLER(n, m) \ + void TCC##n##_Handler(void) \ + { \ + _tcc_interrupt_handler(n); \ + } + +MREPEAT(TCC_INST_NUM, _TCC_INTERRUPT_HANDLER, 0) + +/** + * \internal Interrupt handler for the TCC module + * + * Handles interrupts as they occur, it will run the callback functions + * that are registered and enabled. + * + * \param[in] module_index ID of the TCC instance calling the interrupt + * handler + */ +void _tcc_interrupt_handler( + uint8_t module_index) +{ + int i; + + uint32_t interrupt_and_callback_status_mask; + + struct tcc_module *module = + (struct tcc_module *)_tcc_instances[module_index]; + + interrupt_and_callback_status_mask = (module->hw->INTFLAG.reg & + module->register_callback_mask & + module->enable_callback_mask); + + /* Check if callback interrupt has occured */ + for (i = 0; i < TCC_CALLBACK_N; i ++) { + if (interrupt_and_callback_status_mask & _tcc_intflag[i]) { + /* Invoke the registered and enabled callback function */ + (module->callback[i])(module); + /* Clear interrupt flag */ + module->hw->INTFLAG.reg = _tcc_intflag[i]; + } + } +} diff --git a/atmel-samd/asf/sam0/drivers/tcc/tcc_callback.h b/atmel-samd/asf/sam0/drivers/tcc/tcc_callback.h new file mode 100644 index 0000000000..37b0bbb727 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/tcc/tcc_callback.h @@ -0,0 +1,93 @@ +/** + * \file + * + * \brief SAM TCC - Timer Counter for Control Applications Callback Driver + * + * Copyright (C) 2013-2015 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 + * + */ +/* + * Support and FAQ: visit Atmel Support + */ + + +#ifndef TCC_CALLBACK_H_INCLUDED +#define TCC_CALLBACK_H_INCLUDED + +#include "tcc.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(__DOXYGEN__) +extern void *_tcc_instances[TCC_INST_NUM]; +#endif + + +/** + * \name Callback Management + * {@ + */ + +enum status_code tcc_register_callback( + struct tcc_module *const module, + tcc_callback_t callback_func, + const enum tcc_callback callback_type); + +enum status_code tcc_unregister_callback( + struct tcc_module *const module, + const enum tcc_callback callback_type); + +void tcc_enable_callback( + struct tcc_module *const module, + const enum tcc_callback callback_type); + +void tcc_disable_callback( + struct tcc_module *const module, + const enum tcc_callback callback_type); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* TCC_CALLBACK_H_INCLUDED */ diff --git a/atmel-samd/boards/arduino_zero/pins.c b/atmel-samd/boards/arduino_zero/pins.c index fb9794fa45..8f866a232f 100644 --- a/atmel-samd/boards/arduino_zero/pins.c +++ b/atmel-samd/boards/arduino_zero/pins.c @@ -1,25 +1,54 @@ #include "pins.h" #include "asf/sam0/drivers/system/system.h" -PIN(PA02, true, ADC_POSITIVE_INPUT_PIN0); -PIN(PB08, true, ADC_POSITIVE_INPUT_PIN8); -PIN(PB09, true, ADC_POSITIVE_INPUT_PIN9); -PIN(PA04, true, ADC_POSITIVE_INPUT_PIN4); -PIN(PA05, true, ADC_POSITIVE_INPUT_PIN5); -PIN(PB02, true, ADC_POSITIVE_INPUT_PIN10); -PIN(PA11, true, ADC_POSITIVE_INPUT_PIN19); -PIN(PA10, true, ADC_POSITIVE_INPUT_PIN18); -PIN(PA14, false, NO_ADC_INPUT); -PIN(PA09, true, ADC_POSITIVE_INPUT_PIN17); -PIN(PA08, true, ADC_POSITIVE_INPUT_PIN16); -PIN(PA15, false, NO_ADC_INPUT); -PIN(PA20, false, NO_ADC_INPUT); -PIN(PA06, true, ADC_POSITIVE_INPUT_PIN6); -PIN(PA07, true, ADC_POSITIVE_INPUT_PIN7); -PIN(PA18, false, NO_ADC_INPUT); -PIN(PA16, false, NO_ADC_INPUT); -PIN(PA19, false, NO_ADC_INPUT); -PIN(PA17, false, NO_ADC_INPUT); +PIN_NO_TIMERS_(PA02, true, ADC_POSITIVE_INPUT_PIN0); +PIN_ONE_TIMER_(PB08, true, ADC_POSITIVE_INPUT_PIN8, + TC4, 0, 0, 0, PIN_PB08E_TC4_WO0, MUX_PB08E_TC4_WO0); +PIN_ONE_TIMER_(PB09, true, ADC_POSITIVE_INPUT_PIN9, + TC4, 0, 1, 1, PIN_PB09E_TC4_WO1, MUX_PB09E_TC4_WO1); +PIN_ONE_TIMER_(PA04, true, ADC_POSITIVE_INPUT_PIN4, + 0, TCC0, 0, 0, PIN_PA04E_TCC0_WO0, MUX_PA04E_TCC0_WO0); +PIN_ONE_TIMER_(PA05, true, ADC_POSITIVE_INPUT_PIN5, + 0, TCC0, 1, 1, PIN_PA05E_TCC0_WO1, MUX_PA05E_TCC0_WO1); +PIN_NO_TIMERS_(PB02, true, ADC_POSITIVE_INPUT_PIN10); +PIN_TWO_TIMERS(PA11, true, ADC_POSITIVE_INPUT_PIN19, + 0, TCC1, 1, 1, PIN_PA11E_TCC1_WO1, MUX_PA11E_TCC1_WO1, + 0, TCC0, 3, 3, PIN_PA11F_TCC0_WO3, MUX_PA11F_TCC0_WO3); +PIN_TWO_TIMERS(PA10, true, ADC_POSITIVE_INPUT_PIN18, + 0, TCC1, 0, 0, PIN_PA10E_TCC1_WO0, MUX_PA10E_TCC1_WO0, + 0, TCC0, 2, 2, PIN_PA10F_TCC0_WO2, MUX_PA10F_TCC0_WO2); +PIN_TWO_TIMERS(PA14, false, NO_ADC_INPUT, + TC3, 0, 0, 0, PIN_PA14E_TC3_WO0, MUX_PA14E_TC3_WO0, + 0, TCC0, 0, 4, PIN_PA14F_TCC0_WO4, MUX_PA14F_TCC0_WO4); +PIN_TWO_TIMERS(PA09, true, ADC_POSITIVE_INPUT_PIN17, + 0, TCC0, 1, 1, PIN_PA09E_TCC0_WO1, MUX_PA09E_TCC0_WO1, + 0, TCC1, 3, 3, PIN_PA09F_TCC1_WO3, MUX_PA09F_TCC1_WO3); +PIN_TWO_TIMERS(PA08, true, ADC_POSITIVE_INPUT_PIN16, + 0, TCC0, 0, 0, PIN_PA08E_TCC0_WO0, MUX_PA08E_TCC0_WO0, + 0, TCC1, 2, 2, PIN_PA08F_TCC1_WO2, MUX_PA08F_TCC1_WO2); +PIN_TWO_TIMERS(PA15, false, NO_ADC_INPUT, + TC3, 0, 1, 1, PIN_PA15E_TC3_WO1, MUX_PA15E_TC3_WO1, + 0, TCC0, 1, 5, PIN_PA15F_TCC0_WO5, MUX_PA15F_TCC0_WO5); +PIN_ONE_TIMER_(PA20, false, NO_ADC_INPUT, + 0, TCC0, 2, 6, PIN_PA20F_TCC0_WO6, MUX_PA20F_TCC0_WO6); +PIN_ONE_TIMER_(PA21, false, NO_ADC_INPUT, + 0, TCC0, 3, 7, PIN_PA21F_TCC0_WO7, MUX_PA21F_TCC0_WO7); +PIN_ONE_TIMER_(PA06, true, ADC_POSITIVE_INPUT_PIN6, + 0, TCC1, 0, 0, PIN_PA06E_TCC1_WO0, MUX_PA06E_TCC1_WO0); +PIN_ONE_TIMER_(PA07, true, ADC_POSITIVE_INPUT_PIN7, + 0, TCC1, 1, 1, PIN_PA07E_TCC1_WO1, MUX_PA07E_TCC1_WO1); +PIN_TWO_TIMERS(PA18, false, NO_ADC_INPUT, + TC3, 0, 0, 0, PIN_PA18E_TC3_WO0, MUX_PA18E_TC3_WO0, + 0, TCC0, 2, 2, PIN_PA18F_TCC0_WO2, MUX_PA18F_TCC0_WO2); +PIN_TWO_TIMERS(PA16, false, NO_ADC_INPUT, + 0, TCC2, 0, 0, PIN_PA16E_TCC2_WO0, MUX_PA16E_TCC2_WO0, + 0, TCC0, 2, 6, PIN_PA16F_TCC0_WO6, MUX_PA16F_TCC0_WO6); +PIN_TWO_TIMERS(PA19, false, NO_ADC_INPUT, + TC3, 0, 1, 1, PIN_PA19E_TC3_WO1, MUX_PA19E_TC3_WO1, + 0, TCC0, 3, 3, PIN_PA19F_TCC0_WO3, MUX_PA19F_TCC0_WO3); +PIN_TWO_TIMERS(PA17, false, NO_ADC_INPUT, + 0, TCC2, 1, 1, PIN_PA17E_TCC2_WO1, MUX_PA17E_TCC2_WO1, + 0, TCC0, 3, 7, PIN_PA17F_TCC0_WO7, MUX_PA17F_TCC0_WO7); STATIC const mp_map_elem_t pin_cpu_pins_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_PA02), (mp_obj_t)&pin_PA02 }, @@ -35,6 +64,7 @@ STATIC const mp_map_elem_t pin_cpu_pins_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_PA08), (mp_obj_t)&pin_PA08 }, { MP_OBJ_NEW_QSTR(MP_QSTR_PA15), (mp_obj_t)&pin_PA15 }, { MP_OBJ_NEW_QSTR(MP_QSTR_PA20), (mp_obj_t)&pin_PA20 }, + { MP_OBJ_NEW_QSTR(MP_QSTR_PA21), (mp_obj_t)&pin_PA21 }, { MP_OBJ_NEW_QSTR(MP_QSTR_PA06), (mp_obj_t)&pin_PA06 }, { MP_OBJ_NEW_QSTR(MP_QSTR_PA07), (mp_obj_t)&pin_PA07 }, { MP_OBJ_NEW_QSTR(MP_QSTR_PA18), (mp_obj_t)&pin_PA18 }, @@ -58,6 +88,7 @@ STATIC const mp_map_elem_t pin_board_pins_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_D4), (mp_obj_t)&pin_PA08 }, { MP_OBJ_NEW_QSTR(MP_QSTR_D5), (mp_obj_t)&pin_PA15 }, { MP_OBJ_NEW_QSTR(MP_QSTR_D6), (mp_obj_t)&pin_PA20 }, + { MP_OBJ_NEW_QSTR(MP_QSTR_D7), (mp_obj_t)&pin_PA21 }, { MP_OBJ_NEW_QSTR(MP_QSTR_D8), (mp_obj_t)&pin_PA06 }, { MP_OBJ_NEW_QSTR(MP_QSTR_D9), (mp_obj_t)&pin_PA07 }, { MP_OBJ_NEW_QSTR(MP_QSTR_D10), (mp_obj_t)&pin_PA18 }, diff --git a/atmel-samd/boards/feather_m0_ble/pins.c b/atmel-samd/boards/feather_m0_ble/pins.c index 9911160310..b60d7562fe 100644 --- a/atmel-samd/boards/feather_m0_ble/pins.c +++ b/atmel-samd/boards/feather_m0_ble/pins.c @@ -1,26 +1,56 @@ #include "pins.h" #include "asf/sam0/drivers/system/system.h" -PIN(PA02, true, ADC_POSITIVE_INPUT_PIN0); -PIN(PB08, true, ADC_POSITIVE_INPUT_PIN8); -PIN(PB09, true, ADC_POSITIVE_INPUT_PIN9); -PIN(PA04, true, ADC_POSITIVE_INPUT_PIN4); -PIN(PA05, true, ADC_POSITIVE_INPUT_PIN5); -PIN(PB02, true, ADC_POSITIVE_INPUT_PIN10); -PIN(PB11, false, NO_ADC_INPUT); -PIN(PB10, false, NO_ADC_INPUT); -PIN(PA12, false, NO_ADC_INPUT); -PIN(PA11, true, ADC_POSITIVE_INPUT_PIN19); -PIN(PA10, true, ADC_POSITIVE_INPUT_PIN18); -PIN(PA22, false, NO_ADC_INPUT); -PIN(PA23, false, NO_ADC_INPUT); -PIN(PA15, false, NO_ADC_INPUT); -PIN(PA20, false, NO_ADC_INPUT); -PIN(PA07, true, ADC_POSITIVE_INPUT_PIN7); -PIN(PA18, false, NO_ADC_INPUT); -PIN(PA16, false, NO_ADC_INPUT); -PIN(PA19, false, NO_ADC_INPUT); -PIN(PA17, false, NO_ADC_INPUT); +PIN_NO_TIMERS_(PA02, true, ADC_POSITIVE_INPUT_PIN0); +PIN_ONE_TIMER_(PB08, true, ADC_POSITIVE_INPUT_PIN8, + TC4, 0, 0, 0, PIN_PB08E_TC4_WO0, MUX_PB08E_TC4_WO0); +PIN_ONE_TIMER_(PB09, true, ADC_POSITIVE_INPUT_PIN9, + TC4, 0, 1, 1, PIN_PB09E_TC4_WO1, MUX_PB09E_TC4_WO1); +PIN_ONE_TIMER_(PA04, true, ADC_POSITIVE_INPUT_PIN4, + 0, TCC0, 0, 0, PIN_PA04E_TCC0_WO0, MUX_PA04E_TCC0_WO0); +PIN_ONE_TIMER_(PA05, true, ADC_POSITIVE_INPUT_PIN5, + 0, TCC0, 1, 1, PIN_PA05E_TCC0_WO1, MUX_PA05E_TCC0_WO1); +PIN_NO_TIMERS_(PB02, true, ADC_POSITIVE_INPUT_PIN10); +PIN_TWO_TIMERS(PB11, false, NO_ADC_INPUT, + TC5, 0, 1, 1, PIN_PB11E_TC5_WO1, MUX_PB11E_TC5_WO1, + 0, TCC0, 1, 5, PIN_PB11F_TCC0_WO5, MUX_PB11F_TCC0_WO5); +PIN_TWO_TIMERS(PB10, false, NO_ADC_INPUT, + TC5, 0, 0, 0, PIN_PB10E_TC5_WO0, MUX_PB10E_TC5_WO0, + 0, TCC0, 0, 4, PIN_PB10F_TCC0_WO4, MUX_PB10F_TCC0_WO4); +PIN_TWO_TIMERS(PA12, false, NO_ADC_INPUT, + 0, TCC2, 0, 0, PIN_PA12E_TCC2_WO0, MUX_PA12E_TCC2_WO0, + 0, TCC0, 2, 6, PIN_PA12F_TCC0_WO6, MUX_PA12F_TCC0_WO6); +PIN_TWO_TIMERS(PA11, true, ADC_POSITIVE_INPUT_PIN19, + 0, TCC1, 1, 1, PIN_PA11E_TCC1_WO1, MUX_PA11E_TCC1_WO1, + 0, TCC0, 3, 3, PIN_PA11F_TCC0_WO3, MUX_PA11F_TCC0_WO3); +PIN_TWO_TIMERS(PA10, true, ADC_POSITIVE_INPUT_PIN18, + 0, TCC1, 0, 0, PIN_PA10E_TCC1_WO0, MUX_PA10E_TCC1_WO0, + 0, TCC0, 2, 2, PIN_PA10F_TCC0_WO2, MUX_PA10F_TCC0_WO2); +PIN_TWO_TIMERS(PA22, false, NO_ADC_INPUT, + TC4, 0, 0, 0, PIN_PA22E_TC4_WO0, MUX_PA22E_TC4_WO0, + 0, TCC0, 0, 4, PIN_PA22F_TCC0_WO4, MUX_PA22F_TCC0_WO4); +PIN_TWO_TIMERS(PA23, false, NO_ADC_INPUT, + TC4, 0, 1, 1, PIN_PA23E_TC4_WO1, MUX_PA23E_TC4_WO1, + 0, TCC0, 1, 5, PIN_PA23F_TCC0_WO5, MUX_PA23F_TCC0_WO5); +PIN_TWO_TIMERS(PA15, false, NO_ADC_INPUT, + TC3, 0, 1, 1, PIN_PA15E_TC3_WO1, MUX_PA15E_TC3_WO1, + 0, TCC0, 1, 5, PIN_PA15F_TCC0_WO5, MUX_PA15F_TCC0_WO5); +PIN_ONE_TIMER_(PA20, false, NO_ADC_INPUT, + 0, TCC0, 2, 6, PIN_PA20F_TCC0_WO6, MUX_PA20F_TCC0_WO6); +PIN_ONE_TIMER_(PA07, true, ADC_POSITIVE_INPUT_PIN7, + 0, TCC1, 1, 1, PIN_PA07E_TCC1_WO1, MUX_PA07E_TCC1_WO1); +PIN_TWO_TIMERS(PA18, false, NO_ADC_INPUT, + TC3, 0, 0, 0, PIN_PA18E_TC3_WO0, MUX_PA18E_TC3_WO0, + 0, TCC0, 2, 2, PIN_PA18F_TCC0_WO2, MUX_PA18F_TCC0_WO2); +PIN_TWO_TIMERS(PA16, false, NO_ADC_INPUT, + 0, TCC2, 0, 0, PIN_PA16E_TCC2_WO0, MUX_PA16E_TCC2_WO0, + 0, TCC0, 2, 6, PIN_PA16F_TCC0_WO6, MUX_PA16F_TCC0_WO6); +PIN_TWO_TIMERS(PA19, false, NO_ADC_INPUT, + TC3, 0, 1, 1, PIN_PA19E_TC3_WO1, MUX_PA19E_TC3_WO1, + 0, TCC0, 3, 3, PIN_PA19F_TCC0_WO3, MUX_PA19F_TCC0_WO3); +PIN_TWO_TIMERS(PA17, false, NO_ADC_INPUT, + 0, TCC2, 1, 1, PIN_PA17E_TCC2_WO1, MUX_PA17E_TCC2_WO1, + 0, TCC0, 3, 7, PIN_PA17F_TCC0_WO7, MUX_PA17F_TCC0_WO7); STATIC const mp_map_elem_t pin_cpu_pins_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_PA02), (mp_obj_t)&pin_PA02 }, diff --git a/atmel-samd/modmachine.c b/atmel-samd/modmachine.c index 4abe1c7421..d59cf84cfa 100644 --- a/atmel-samd/modmachine.c +++ b/atmel-samd/modmachine.c @@ -34,6 +34,7 @@ #include "modmachine_adc.h" #include "modmachine_dac.h" #include "modmachine_pin.h" +#include "modmachine_pwm.h" #if MICROPY_PY_MACHINE @@ -42,6 +43,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&adc_type) }, { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&dac_type) }, { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&pin_type) }, + { MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&pwm_type) }, }; STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table); diff --git a/atmel-samd/modmachine_pin.h b/atmel-samd/modmachine_pin.h index 4a02f41db6..8ecafe7984 100644 --- a/atmel-samd/modmachine_pin.h +++ b/atmel-samd/modmachine_pin.h @@ -35,27 +35,68 @@ #include "asf/sam0/drivers/adc/adc_sam_d_r/adc_feature.h" // This macro is used to simplify pin definition in boards//pins.c -#define PIN(p_name, p_has_adc, p_adc_input) \ +#define PIN_TWO_TIMERS(p_name, p_has_adc, p_adc_input, p_primary_tc, p_primary_tcc, \ + p_primary_channel, p_primary_wave_output, p_primary_pin, p_primary_mux, \ + p_secondary_tc, p_secondary_tcc, p_secondary_channel, p_secondary_wave_output, \ + p_secondary_pin, p_secondary_mux) \ const pin_obj_t pin_## p_name = { \ { &pin_type }, \ .name = MP_QSTR_ ## p_name, \ .pin = (PIN_## p_name), \ .has_adc = p_has_adc, \ .adc_input = p_adc_input, \ + .primary_timer = { \ + .tc = p_primary_tc, \ + .tcc = p_primary_tcc, \ + .channel = p_primary_channel, \ + .wave_output = p_primary_wave_output, \ + .pin = p_primary_pin, \ + .mux = p_primary_mux \ + }, \ + .secondary_timer = { \ + .tc = p_secondary_tc, \ + .tcc = p_secondary_tcc, \ + .channel = p_secondary_channel, \ + .wave_output = p_secondary_wave_output, \ + .pin = p_secondary_pin, \ + .mux = p_secondary_mux \ + } \ } +#define PIN_NO_TIMERS_(p_name, p_has_adc, p_adc_input) \ + PIN_TWO_TIMERS(p_name, p_has_adc, p_adc_input, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + +#define PIN_ONE_TIMER_(p_name, p_has_adc, p_adc_input, p_primary_tcc, \ + p_primary_tc, p_primary_channel, p_primary_wave_output, \ + p_primary_pin, p_primary_mux) \ + PIN_TWO_TIMERS(p_name, p_has_adc, p_adc_input, p_primary_tcc, p_primary_tc, \ + p_primary_channel, p_primary_wave_output, p_primary_pin, p_primary_mux, \ + 0, 0, 0, 0, 0, 0) + #define NO_ADC_INPUT (0) #include "mpconfigport.h" #include "py/obj.h" +typedef struct { + Tc *const tc; + Tcc *const tcc; + uint8_t channel; + uint8_t wave_output; + uint32_t pin; + uint32_t mux; +} pin_timer_t; + typedef struct { mp_obj_base_t base; qstr name; uint32_t pin; bool has_adc; enum adc_positive_input adc_input; + pin_timer_t primary_timer; + pin_timer_t secondary_timer; } pin_obj_t; extern const mp_obj_type_t pin_type; diff --git a/atmel-samd/modmachine_pwm.c b/atmel-samd/modmachine_pwm.c new file mode 100644 index 0000000000..214210f504 --- /dev/null +++ b/atmel-samd/modmachine_pwm.c @@ -0,0 +1,192 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * 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 + +#include "py/runtime.h" +#include "modmachine_pin.h" +#include "modmachine_pwm.h" + +#include "asf/sam0/drivers/tc/tc.h" +#include "asf/sam0/drivers/tcc/tcc.h" + +typedef struct _pwm_obj_t { + mp_obj_base_t base; + const pin_obj_t *pin; + bool using_primary_timer; + struct tc_module tc_instance; + struct tcc_module tcc_instance; +} pwm_obj_t; + +/******************************************************************************/ +// MicroPython bindings for PWM + +STATIC void pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "PWM(%s", self->pin->name); + mp_printf(print, ", freq=%u, duty=%u", 0, 0); + mp_printf(print, ")"); +} +/// \classmethod \constructor(pin) +/// Create a PWM object associated with the given pin. This allows you to write PWM +/// signals out on the given pin. Frequency is currently fixed at ~735Hz like Arduino. +STATIC mp_obj_t pwm_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + mp_obj_t pin_obj = args[0]; + const pin_obj_t *pin = pin_find(pin_obj); + + // create PWM object from the given pin + pwm_obj_t *self = m_new_obj(pwm_obj_t); + self->base.type = &pwm_type; + self->pin = pin; + self->using_primary_timer = true; + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + enum { ARG_duty }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_duty, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + }; + mp_arg_val_t parsed_args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, args + 1, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, parsed_args); + + if (pin->primary_timer.tc == 0 && pin->primary_timer.tcc == 0 && + pin->secondary_timer.tc == 0 && pin->secondary_timer.tcc == 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "PWM not supported on pin %q", self->pin->name)); + } + + if (parsed_args[ARG_duty].u_int < 0 || parsed_args[ARG_duty].u_int > 255) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "PWM duty must be between 0 and 255 (8 bit resolution), not %d", + parsed_args[ARG_duty].u_int)); + } + uint8_t duty = parsed_args[ARG_duty].u_int; + + // TODO(tannewt): Support output on multiple timer channels at once. + const pin_timer_t* t = &pin->primary_timer; + if (t->tcc != 0) { + struct tcc_config config_tcc; + tcc_get_config_defaults(&config_tcc, t->tcc); + + config_tcc.counter.clock_prescaler = TCC_CLOCK_PRESCALER_DIV256; + config_tcc.counter.period = 0xFF; + config_tcc.compare.wave_generation = TCC_WAVE_GENERATION_SINGLE_SLOPE_PWM; + config_tcc.compare.match[t->channel] = duty; + + config_tcc.pins.enable_wave_out_pin[t->wave_output] = true; + config_tcc.pins.wave_out_pin[t->wave_output] = t->pin; + config_tcc.pins.wave_out_pin_mux[t->wave_output] = t->mux; + + tcc_init(&self->tcc_instance, t->tcc, &config_tcc); + + tcc_enable(&self->tcc_instance); + } else { + struct tc_config config_tc; + tc_get_config_defaults(&config_tc); + + config_tc.counter_size = TC_COUNTER_SIZE_8BIT; + config_tc.wave_generation = TC_WAVE_GENERATION_NORMAL_PWM; + config_tc.clock_prescaler = TC_CLOCK_PRESCALER_DIV256; + config_tc.counter_8_bit.period = 0xff; + config_tc.counter_8_bit.compare_capture_channel[t->channel] = duty; + + config_tc.pwm_channel[t->wave_output].enabled = true; + config_tc.pwm_channel[t->wave_output].pin_out = t->pin; + config_tc.pwm_channel[t->wave_output].pin_mux = t->mux; + + tc_init(&self->tc_instance, t->tc, &config_tc); + + tc_enable(&self->tc_instance); + } + + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t pwm_del(mp_obj_t self_in) { + pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); + const pin_timer_t* t = &self->pin->primary_timer; + if (!self->using_primary_timer) { + t = &self->pin->secondary_timer; + } + if (t->tcc != 0) { + tcc_disable(&self->tcc_instance); + } else { + tc_disable(&self->tc_instance); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pwm_del_obj, pwm_del); + +// 8 bit resolution +STATIC mp_obj_t pwm_duty(size_t n_args, const mp_obj_t *args) { + pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + const pin_timer_t* t = &self->pin->primary_timer; + if (!self->using_primary_timer) { + t = &self->pin->secondary_timer; + } + + if (n_args == 1) { + uint8_t duty; + if (t->tcc != 0) { + duty = tcc_get_capture_value(&self->tcc_instance, t->channel); + } else { + duty = tc_get_capture_value(&self->tc_instance, t->channel); + } + return MP_OBJ_NEW_SMALL_INT(duty); + } else { + mp_int_t duty = mp_obj_get_int(args[1]); + if (duty < 0 || duty > 255) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "PWM duty must be between 0 and 255 (8 bit resolution), not %d", + duty)); + } + if (t->tcc != 0) { + tcc_set_compare_value(&self->tcc_instance, t->channel, duty); + } else { + tc_set_compare_value(&self->tc_instance, t->channel, duty); + } + return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pwm_duty_obj, 1, 2, pwm_duty); + +STATIC const mp_rom_map_elem_t pwm_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&pwm_del_obj) }, + { MP_ROM_QSTR(MP_QSTR_duty), MP_ROM_PTR(&pwm_duty_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(pwm_locals_dict, pwm_locals_dict_table); + +const mp_obj_type_t pwm_type = { + { &mp_type_type }, + .name = MP_QSTR_PWM, + .print = pwm_print, + .make_new = pwm_make_new, + .locals_dict = (mp_obj_dict_t*)&pwm_locals_dict, +}; diff --git a/atmel-samd/modmachine_pwm.h b/atmel-samd/modmachine_pwm.h new file mode 100644 index 0000000000..1989b6e52e --- /dev/null +++ b/atmel-samd/modmachine_pwm.h @@ -0,0 +1,27 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ + +extern const mp_obj_type_t pwm_type;