From 9485634d41369e896a0ef0f7504f2195b68679e7 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 23 Aug 2016 16:47:53 -0700 Subject: Add support for REPL on Arduino Zero through EDBG via UART. --- .../sercom/i2c/docimg/bus_state_diagram.svg | 533 ++++++ .../drivers/sercom/i2c/docimg/bus_topology.svg | 351 ++++ .../sam0/drivers/sercom/i2c/docimg/i2c_read.svg | 1037 +++++++++++ .../sam0/drivers/sercom/i2c/docimg/i2c_write.svg | 1031 +++++++++++ .../asf/sam0/drivers/sercom/i2c/i2c_common.h | 613 +++++++ .../asf/sam0/drivers/sercom/i2c/i2c_master.h | 629 +++++++ .../sam0/drivers/sercom/i2c/i2c_master_interrupt.h | 214 +++ .../sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.c | 1050 ++++++++++++ .../sercom/i2c/i2c_sam0/i2c_master_interrupt.c | 762 +++++++++ .../sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave.c | 745 ++++++++ .../sercom/i2c/i2c_sam0/i2c_slave_interrupt.c | 434 +++++ .../drivers/sercom/i2c/i2c_samd20/i2c_master.c | 881 ++++++++++ .../sercom/i2c/i2c_samd20/i2c_master_interrupt.c | 670 ++++++++ .../sam0/drivers/sercom/i2c/i2c_samd20/i2c_slave.c | 693 ++++++++ .../sercom/i2c/i2c_samd20/i2c_slave_interrupt.c | 434 +++++ atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_slave.h | 753 ++++++++ .../sam0/drivers/sercom/i2c/i2c_slave_interrupt.h | 215 +++ atmel-samd/asf/sam0/drivers/sercom/sercom.c | 296 ++++ atmel-samd/asf/sam0/drivers/sercom/sercom.h | 118 ++ .../asf/sam0/drivers/sercom/sercom_interrupt.c | 141 ++ .../asf/sam0/drivers/sercom/sercom_interrupt.h | 72 + atmel-samd/asf/sam0/drivers/sercom/sercom_pinout.h | 526 ++++++ .../drivers/sercom/spi/module_config/conf_spi.h | 55 + atmel-samd/asf/sam0/drivers/sercom/spi/spi.c | 1265 ++++++++++++++ atmel-samd/asf/sam0/drivers/sercom/spi/spi.h | 1807 ++++++++++++++++++++ .../asf/sam0/drivers/sercom/spi/spi_interrupt.c | 741 ++++++++ .../asf/sam0/drivers/sercom/spi/spi_interrupt.h | 190 ++ .../sercom/spi_master_vec/module-config/conf_spi.h | 55 + .../module-config/conf_spi_master_vec.h | 68 + .../drivers/sercom/spi_master_vec/spi_master_vec.c | 588 +++++++ .../drivers/sercom/spi_master_vec/spi_master_vec.h | 692 ++++++++ .../drivers/sercom/usart/docimg/usart_frame.svg | 272 +++ atmel-samd/asf/sam0/drivers/sercom/usart/usart.c | 817 +++++++++ atmel-samd/asf/sam0/drivers/sercom/usart/usart.h | 1593 +++++++++++++++++ .../sam0/drivers/sercom/usart/usart_interrupt.c | 666 ++++++++ .../sam0/drivers/sercom/usart/usart_interrupt.h | 177 ++ .../utils/cmsis/samd21/source/gcc/startup_samd21.c | 2 +- atmel-samd/asf/sam0/utils/syscalls/gcc/syscalls.c | 8 +- 38 files changed, 21189 insertions(+), 5 deletions(-) create mode 100644 atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/bus_state_diagram.svg create mode 100644 atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/bus_topology.svg create mode 100644 atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/i2c_read.svg create mode 100644 atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/i2c_write.svg create mode 100644 atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_common.h create mode 100644 atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_master.h create mode 100644 atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_master_interrupt.h create mode 100644 atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.c create mode 100644 atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master_interrupt.c create mode 100644 atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave.c create mode 100644 atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave_interrupt.c create mode 100644 atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_master.c create mode 100644 atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_master_interrupt.c create mode 100644 atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_slave.c create mode 100644 atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_slave_interrupt.c create mode 100644 atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_slave.h create mode 100644 atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_slave_interrupt.h create mode 100644 atmel-samd/asf/sam0/drivers/sercom/sercom.c create mode 100644 atmel-samd/asf/sam0/drivers/sercom/sercom.h create mode 100644 atmel-samd/asf/sam0/drivers/sercom/sercom_interrupt.c create mode 100644 atmel-samd/asf/sam0/drivers/sercom/sercom_interrupt.h create mode 100644 atmel-samd/asf/sam0/drivers/sercom/sercom_pinout.h create mode 100644 atmel-samd/asf/sam0/drivers/sercom/spi/module_config/conf_spi.h create mode 100644 atmel-samd/asf/sam0/drivers/sercom/spi/spi.c create mode 100644 atmel-samd/asf/sam0/drivers/sercom/spi/spi.h create mode 100644 atmel-samd/asf/sam0/drivers/sercom/spi/spi_interrupt.c create mode 100644 atmel-samd/asf/sam0/drivers/sercom/spi/spi_interrupt.h create mode 100644 atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/module-config/conf_spi.h create mode 100644 atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/module-config/conf_spi_master_vec.h create mode 100644 atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/spi_master_vec.c create mode 100644 atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/spi_master_vec.h create mode 100644 atmel-samd/asf/sam0/drivers/sercom/usart/docimg/usart_frame.svg create mode 100644 atmel-samd/asf/sam0/drivers/sercom/usart/usart.c create mode 100644 atmel-samd/asf/sam0/drivers/sercom/usart/usart.h create mode 100644 atmel-samd/asf/sam0/drivers/sercom/usart/usart_interrupt.c create mode 100644 atmel-samd/asf/sam0/drivers/sercom/usart/usart_interrupt.h (limited to 'atmel-samd/asf') diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/bus_state_diagram.svg b/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/bus_state_diagram.svg new file mode 100644 index 000000000..4bd4175a0 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/bus_state_diagram.svg @@ -0,0 +1,533 @@ + + + +image/svg+xmlP + Timeout + +RESET + +Write ADDR(S) + +IDLE + +(0b01) + +S + +BUSY + +(0b11) + +P + Timeout + +UNKNOWN + +(0b00) + +OWNER + +(0b10) + +ArbitrationLost + +Command PWrite ADDR (Sr) + +Sr + + diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/bus_topology.svg b/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/bus_topology.svg new file mode 100644 index 000000000..80df58f30 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/bus_topology.svg @@ -0,0 +1,351 @@ + + + + + + + + + + Page-1 + + + Sheet.1 + + Box.448 + I2C DEVICE #1 + + + + I2C DEVICE #1 + + Box.450 + + + + Box.451 + + + + Bottom to top 1 + + + + + + + Bottom to top 1.453 + + + + + + + Bottom to top 1.454 + + + + + + + Bottom to top 1.455 + + + + + + + Box.449 + + + + Bottom to top 1.456 + + + + + + + Bottom to top 1.457 + + + + + + + Box.447 + + + + Bottom to top 1.458 + + + + + + + Bottom to top 1.459 + + + + + + + Sheet.15 + RP + + + + RP + + Sheet.16 + RP + + + + RP + + Sheet.17 + RS + + + + RS + + Sheet.18 + RS + + + + RS + + Box.211 + SDA + + + + SDA + + Box.465 + SCL + + + + SCL + + Side to side 1 + + + + + + + Side to side 1.467 + + + + + + + Bottom to top 1.468 + + + + + + + Side to side 1.469 + + + + + + + Box.470 + VCC + + + + VCC + + Box.471 + I2C DEVICE #2 + + + + I2C DEVICE #2 + + Box.472 + + + + Box.473 + + + + Bottom to top 1.474 + + + + + + + Bottom to top 1.475 + + + + + + + Bottom to top 1.476 + + + + + + + Bottom to top 1.477 + + + + + + + Sheet.33 + RS + + + + RS + + Sheet.34 + RS + + + + RS + + Bottom to top 1.480 + + + + + + + Box.481 + I2C DEVICE #N + + + + I2C DEVICE #N + + Box.482 + + + + Box.483 + + + + Bottom to top 1.484 + + + + + + + Bottom to top 1.485 + + + + + + + Bottom to top 1.486 + + + + + + + Bottom to top 1.487 + + + + + + + Sheet.43 + RS + + + + RS + + Sheet.44 + RS + + + + RS + + Bottom to top 1.490 + + + + + + + Sheet.46 + + + + Sheet.47 + + + + Sheet.48 + + + + Sheet.49 + Note: RS is optional + + + + Note: RS is optional + + + diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/i2c_read.svg b/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/i2c_read.svg new file mode 100644 index 000000000..5be8085d9 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/i2c_read.svg @@ -0,0 +1,1037 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + ACK + + + + + + + + START + + + + + Bit 0 + + + + + + ADDRESS + + + + + + Bit 1 + + + + + + Bit 2 + + + + + + Bit 3 + + + + + + Bit 4 + + + + + + Bit 5 + + + + + + Bit 6 + + + + + + Bit 7 + + + + + + + READ + + + + + + Bit 8 + + + + + + Bit 9 + + + + + + + DATA + + + + + + + Bit 10 + + + + + + Bit 11 + + + + + + Bit 12 + + + + + + Bit 13 + + + + + + Bit 14 + + + + + + Bit 15 + + + + + + Bit 16 + + + + + + Bit 17 + + + + + + + ACK + + + + + Bit 18 + + + + + + + + DATA + + + + + + + Bit 19 + + + + + + Bit 20 + + + + + + Bit 21 + + + + + + Bit 22 + + + + + + Bit 23 + + + + + + Bit 24 + + + + + + Bit 25 + + + + + + Bit 26 + + + + + + + + + NACK + + + + Bit 27 + + + + + + + STOP + + + + + Bit 28 + + + + diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/i2c_write.svg b/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/i2c_write.svg new file mode 100644 index 000000000..6f2628100 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/i2c_write.svg @@ -0,0 +1,1031 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + START + + + + + Bit 0 + + + + + + + ADDRESS + + + + + + Bit 1 + + + + + + Bit 2 + + + + + + Bit 3 + + + + + + Bit 4 + + + + + + Bit 5 + + + + + + Bit 6 + + + + + + Bit 7 + + + + + + + + + WRITE + + + + + + Bit 8 + + + + + + + + + ACK + + + + + + + Bit 9 + + + + + + DATA + + + + + Bit 10 + + + + + + Bit 11 + + + + + + Bit 12 + + + + + + Bit 13 + + + + + + Bit 14 + + + + + + Bit 15 + + + + + + Bit 16 + + + + + + Bit 17 + + + + + + + + ACK + + + + + Bit 18 + + + + + + + + DATA + + + + + + Bit 19 + + + + Bit 20 + + + + + Bit 21 + + + + + + Bit 22 + + + + + + Bit 23 + + + + + + Bit 24 + + + + + + Bit 25 + + + + + + Bit 26 + + + + + + + + ACK + + + + Bit 27 + + + + + + + STOP + + + + + Bit 28 + + + + diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_common.h b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_common.h new file mode 100644 index 000000000..de4f0d23f --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_common.h @@ -0,0 +1,613 @@ +/** + * \file + * + * \brief SAM SERCOM I2C Common Driver + * + * Copyright (c) 2012-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 I2C_COMMON_H_INCLUDED +#define I2C_COMMON_H_INCLUDED + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \if (I2C_MASTER_MODE && I2C_SLAVE_MODE) + * \defgroup asfdoc_sam0_sercom_i2c_group SAM I2C (SERCOM I2C) Driver + * \elseif I2C_MASTER_MODE + * \defgroup asfdoc_sam0_sercom_i2c_group SAM I2C Master Mode (SERCOM I2C) Driver + * \elseif I2C_SLAVE_MODE + * \defgroup asfdoc_sam0_sercom_i2c_group SAM I2C Slave Mode (SERCOM I2C) Driver + * \endif + * + * This driver for Atmel® | SMART ARM®-based microcontrollers provides + * an interface for the configuration and management of the device's SERCOM + * I2C module, for the transfer of data via an I2C bus. + * The following driver API modes are covered by this manual: + * + * \if I2C_MASTER_MODE + * - Master Mode Polled APIs + * \endif + * \if I2C_MASTER_CALLBACK_MODE + * - Master Mode Callback APIs + * \endif + * \if I2C_SLAVE_MODE + * - Slave Mode Polled APIs + * \endif + * \if I2C_SLAVE_CALLBACK_MODE + * - Slave Mode Callback APIs + * \endif + * + * The following peripheral is used by this module: + * - SERCOM (Serial Communication Interface) + * + * 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_sercom_i2c_prerequisites + * - \ref asfdoc_sam0_sercom_i2c_overview + * - \ref asfdoc_sam0_sercom_i2c_special_considerations + * - \ref asfdoc_sam0_sercom_i2c_extra + * - \ref asfdoc_sam0_sercom_i2c_examples + * - \ref asfdoc_sam0_sercom_i2c_api_overview + * + * \section asfdoc_sam0_sercom_i2c_prerequisites Prerequisites + * There are no prerequisites. + * + * \section asfdoc_sam0_sercom_i2c_overview Module Overview + * The outline of this section is as follows: + * - \ref asfdoc_sam0_sercom_i2c_module_features + * - \ref asfdoc_sam0_sercom_i2c_functional_desc + * - \ref asfdoc_sam0_sercom_i2c_bus_topology + * - \ref asfdoc_sam0_sercom_i2c_transactions + * - \ref asfdoc_sam0_sercom_i2c_multi_master + * - \ref asfdoc_sam0_sercom_i2c_bus_states + * - \ref asfdoc_sam0_sercom_i2c_timeout + * - \ref asfdoc_sam0_sercom_i2c_sleep_modes + * + * \subsection asfdoc_sam0_sercom_i2c_module_features Driver Feature Macro Definition + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Driver Feature MacroSupported devices
FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEEDSAM D21/R21/D10/D11/L21/L22/DA1/C20/C21
FEATURE_I2C_10_BIT_ADDRESSSAM D21/R21/D10/D11/L21/L22/DA1/C20/C21
FEATURE_I2C_SCL_STRETCH_MODESAM D21/R21/D10/D11/L21/L22/DA1/C20/C21
FEATURE_I2C_SCL_EXTEND_TIMEOUTSAM D21/R21/D10/D11/L21/L22/DA1/C20/C21
+ * \note The specific features are only available in the driver when the selected + * device supports those features. + * \note When using the I2C high-speed mode for off-board communication, + * there are various high frequency interference, which can lead to distortion of the signals + * and communication failure. When using Xplained Pro boards in order to test I2C high-speed + * communication, the following recommendation should be followed: + * - Use the SDA-line on PA08 and SCL-line on PA09 for both boards. This will provide stronger + * pull-ups on both SDA and SCL. + * - The SCL should not be higher than 1.5MHz. + * + * \subsection asfdoc_sam0_sercom_i2c_functional_desc Functional Description + * The I2C provides a simple two-wire bidirectional bus consisting of a + * wired-AND type serial clock line (SCL) and a wired-AND type serial data line + * (SDA). + * + * The I2C bus provides a simple, but efficient method of interconnecting + * multiple master and slave devices. An arbitration mechanism is provided for + * resolving bus ownership between masters, as only one master device may own + * the bus at any given time. The arbitration mechanism relies on the wired-AND + * connections to avoid bus drivers short-circuiting. + * + * A unique address is assigned to all slave devices connected to the bus. A + * device can contain both master and slave logic, and can emulate multiple + * slave devices by responding to more than one address. + * + * \subsection asfdoc_sam0_sercom_i2c_bus_topology Bus Topology + * The I2C bus topology is illustrated in + * \ref asfdoc_sam0_sercom_i2c_bus_topology_figure "the figure below". The pull-up + * resistors (Rs) will provide a high level on the bus lines when none of the + * I2C devices are driving the bus. These are optional, and can be + * replaced with a constant current source. + * + * \anchor asfdoc_sam0_sercom_i2c_bus_topology_figure + * \image html bus_topology.svg "I2C Bus Topology" Width=100% + * + * \subsection asfdoc_sam0_sercom_i2c_transactions Transactions + * The I2C standard defines three fundamental transaction formats: + * - Master Write + * - The master transmits data packets to the slave after addressing it + * - Master Read + * - The slave transmits data packets to the master after being addressed + * - Combined Read/Write + * - A combined transaction consists of several write and read transactions + * + * A data transfer starts with the master issuing a \b Start condition on the + * bus, followed by the address of the slave together with a bit to indicate + * whether the master wants to read from or write to the slave. + * The addressed slave must respond to this by sending an \b ACK back to the + * master. + * + * After this, data packets are sent from the master or slave, according to the + * read/write bit. Each packet must be acknowledged (ACK) or not + * acknowledged (NACK) by the receiver. + * + * If a slave responds with a NACK, the master must assume that the slave + * cannot receive any more data and cancel the write operation. + * + * The master completes a transaction by issuing a \b Stop condition. + * + * A master can issue multiple \b Start conditions during a transaction; this + * is then called a \b Repeated \b Start condition. + * + * \subsubsection asfdoc_sam0_sercom_i2c_address_packets Address Packets + * The slave address consists of seven bits. The 8th bit in the transfer + * determines the data direction (read or write). An address packet always + * succeeds a \b Start or \b Repeated \b Start condition. The 8th bit is handled + * in the driver, and the user will only have to provide the 7-bit address. + * + * \subsubsection asfdoc_sam0_sercom_i2c_data_packets Data Packets + * Data packets are nine bits long, consisting of one 8-bit data byte, and an + * acknowledgement bit. Data packets follow either an address packet or another + * data packet on the bus. + * + * \subsubsection asfdoc_sam0_sercom_i2c_trans_examples Transaction Examples + * The gray bits in the following examples are sent from master to slave, and + * the white bits are sent from slave to master. + * Example of a read transaction is shown in + * \ref asfdoc_sam0_sercom_i2c_trans_examples_i2c_read "the figure below". Here, the + * master first issues a \b Start condition and gets ownership of the bus. An + * address packet with the direction flag set to read is then sent and + * acknowledged by the slave. Then the slave sends one data packet which is + * acknowledged by the master. The slave sends another packet, which is not + * acknowledged by the master and indicates that the master will terminate the + * transaction. In the end, the transaction is terminated by the master issuing + * a \b Stop condition. + * + * \anchor asfdoc_sam0_sercom_i2c_trans_examples_i2c_read + * \image html i2c_read.svg "I2C Packet Read" Width=100% + * + * Example of a write transaction is shown in + * \ref asfdoc_sam0_sercom_i2c_trans_examples_i2c_write "the figure below". Here, the + * master first issues a \b Start condition and gets ownership of the bus. An + * address packet with the dir flag set to write is then sent and acknowledged + * by the slave. Then the master sends two data packets, each acknowledged by + * the slave. In the end, the transaction is terminated by the master issuing + * a \b Stop condition. + * + * \anchor asfdoc_sam0_sercom_i2c_trans_examples_i2c_write + * \image html i2c_write.svg "I2C Packet Write" Width=100% + * + * \subsubsection asfdoc_sam0_sercom_i2c_packet_timeout Packet Timeout + * When a master sends an I2C packet, there is no way of + * being sure that a slave will acknowledge the packet. To avoid stalling the + * device forever while waiting for an acknowledge, a user selectable timeout + * is provided in the \ref i2c_master_config struct which + * lets the driver exit a read or write operation after the specified time. + * The function will then return the STATUS_ERR_TIMEOUT flag. + * + * This is also the case for the slave when using the functions postfixed + * \c _wait. + * + * The time before the timeout occurs, will be the same as + * for \ref asfdoc_sam0_sercom_i2c_unknown_bus_timeout "unknown bus state" timeout. + * + * \subsubsection asfdoc_sam0_sercom_i2c_repeated_start Repeated Start + * To issue a \b Repeated \b Start, the functions postfixed \c _no_stop must be + * used. + * These functions will not send a \b Stop condition when the transfer is done, + * thus the next transfer will start with a \b Repeated \b Start. To end the + * transaction, the functions without the \c _no_stop postfix must be used + * for the last read/write. + * + * \subsection asfdoc_sam0_sercom_i2c_multi_master Multi Master + * In a multi master environment, arbitration of the bus is important, as only + * one master can own the bus at any point. + * + * \subsubsection asfdoc_sam0_sercom_i2c_arbitration Arbitration + * + * \par Clock stretching + * The serial clock line is always driven by a master device. However, all + * devices connected to the bus are allowed stretch the low period of the clock + * to slow down the overall clock frequency or to insert wait states while + * processing data. + * Both master and slave can randomly stretch the clock, which will force the + * other device into a wait-state until the clock line goes high again. + * + * \par Arbitration on the data line + * If two masters start transmitting at the same time, they will both transmit + * until one master detects that the other master is pulling the data line low. + * When this is detected, the master not pulling the line low, will stop the + * transmission and wait until the bus is idle. + * As it is the master trying to contact the slave with the lowest address that + * will get the bus ownership, this will create an arbitration scheme always + * prioritizing the slaves with the lowest address in case of a bus collision. + * + * \subsubsection asfdoc_sam0_sercom_i2c_clock_sync Clock Synchronization + * In situations where more than one master is trying to control the bus clock + * line at the same time, a clock synchronization algorithm based on the same + * principles used for clock stretching is necessary. + * + * + * \subsection asfdoc_sam0_sercom_i2c_bus_states Bus States + * As the I2C bus is limited to one transaction at the time, + * a master that wants to perform a bus transaction must wait until the bus is + * free. + * Because of this, it is necessary for all masters in a multi-master system to + * know the current status of the bus to be able to avoid conflicts and to + * ensure data integrity. + * \li \b IDLE No activity on the bus (between a \b Stop and a new \b Start + * condition) + * \li \b OWNER If the master initiates a transaction successfully + * \li \b BUSY If another master is driving the bus + * \li \b UNKNOWN If the master has recently been enabled or connected to + * the bus. Is forced to \b IDLE after given + * \ref asfdoc_sam0_sercom_i2c_unknown_bus_timeout "timeout" when + * the master module is enabled + * + * The bus state diagram can be seen in + * \ref asfdoc_sam0_sercom_i2c_bus_states_figure "the figure below". + * \li S: Start condition + * \li P: Stop condition + * \li Sr: Repeated start condition + * \anchor asfdoc_sam0_sercom_i2c_bus_states_figure + * \image html bus_state_diagram.svg "I2C Bus State Diagram" Width=100% + * + * \subsection asfdoc_sam0_sercom_i2c_timeout Bus Timing + * Inactive bus timeout for the master and SDA hold time is configurable in the + * drivers. + * + * \subsubsection asfdoc_sam0_sercom_i2c_unknown_bus_timeout Unknown Bus State Timeout + * When a master is enabled or connected to the bus, the bus state will be + * unknown until either a given timeout or a stop command has occurred. The + * timeout is configurable in the \ref i2c_master_config struct. + * The timeout time will depend on toolchain and optimization level used, as + * the timeout is a loop incrementing a value until it reaches the specified + * timeout value. + * + * \subsubsection sda_hold SDA Hold Timeout + * When using the I2C in slave mode, it will be important to + * set a SDA hold time which assures that the master will be able to pick up + * the bit sent from the slave. The SDA hold time makes sure that this is the + * case by holding the data line low for a given period after the negative edge + * on the clock. + * + * The SDA hold time is also available for the master driver, but is not a + * necessity. + * + * \subsection asfdoc_sam0_sercom_i2c_sleep_modes Operation in Sleep Modes + * The I2C module can operate in all sleep modes by setting + * the run_in_standby Boolean in the \ref i2c_master_config or + * \ref i2c_slave_config struct. + * The operation in slave and master mode is shown in + * \ref asfdoc_sam0_sercom_i2c_sleep_modes_table "the table below". + * + * \anchor asfdoc_sam0_sercom_i2c_sleep_modes_table + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
I2C Standby Operations
Run in standbySlaveMaster
falseDisabled, all reception is droppedGeneric Clock (GCLK) disabled when master is idle
trueWake on address match when enabledGCLK enabled while in sleep modes
+ * + * + * \section asfdoc_sam0_sercom_i2c_special_considerations Special Considerations + * + * \if (I2C_MASTER_CALLBACK_MODE || I2C_SLAVE_CALLBACK_MODE) + * \subsection asfdoc_sam0_sercom_i2c_common_interrupt Interrupt-driven Operation + * While an interrupt-driven operation is in progress, subsequent calls to a + * write or read operation will return the STATUS_BUSY flag, indicating that + * only one operation is allowed at any given time. + * + * To check if another transmission can be initiated, the user can either call + * another transfer operation, or use the + * \ref i2c_master_get_job_status/\ref i2c_slave_get_job_status functions + * depending on mode. + * + * If the user would like to get callback from operations while using the + * interrupt-driven driver, the callback must be registered and then enabled + * using the "register_callback" and "enable_callback" functions. + * \else + * There are no special considerations for this driver for the APIs listed in + * this document. + * \endif + * + * \section asfdoc_sam0_sercom_i2c_extra Extra Information + * For extra information, see \ref asfdoc_sam0_sercom_i2c_extra_info_page. + * This includes: + * - \ref asfdoc_sam0_sercom_i2c_acronyms + * - \ref asfdoc_sam0_sercom_i2c_extra_dependencies + * - \ref asfdoc_sam0_sercom_i2c_extra_errata + * - \ref asfdoc_sam0_sercom_i2c_extra_history + * + * \section asfdoc_sam0_sercom_i2c_examples Examples + * + * For a list of examples related to this driver, see + * \ref asfdoc_sam0_sercom_i2c_exqsg. + * + * \section asfdoc_sam0_sercom_i2c_api_overview API Overview + * @{ + */ + +/** + * \name Driver Feature Definition + * Define SERCOM I2C driver features set according to different device family. + * + * @{ + */ +#if (SAMD21) || (SAMR21) || (SAMD10) || (SAMD11) || (SAML21) || (SAMDA1) || \ + (SAML22) || (SAMC20) || (SAMC21) || (SAMD09) || (SAMR30) || defined(__DOXYGEN__) +/** Fast mode plus and high speed support. */ +# define FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED +/** 10-bit address support */ +# define FEATURE_I2C_10_BIT_ADDRESS +/** SCL stretch mode support */ +# define FEATURE_I2C_SCL_STRETCH_MODE +/** SCL extend timeout support */ +# define FEATURE_I2C_SCL_EXTEND_TIMEOUT +# define FEATURE_I2C_DMA_SUPPORT +#endif +/*@}*/ + +/** \brief Transfer direction + * + * For master: transfer direction or setting direction bit in address. + * For slave: direction of request from master. + */ +enum i2c_transfer_direction { + /** Master write operation is in progress */ + I2C_TRANSFER_WRITE = 0, + /** Master read operation is in progress */ + I2C_TRANSFER_READ = 1, +}; + +/** @} */ + +#ifdef __cplusplus +} +#endif + +/** + * \page asfdoc_sam0_sercom_i2c_extra_info_page Extra Information for SERCOM I2C Driver + * + * \section asfdoc_sam0_sercom_i2c_acronyms Acronyms + * \ref asfdoc_sam0_sercom_i2c_acronyms_table "Below" is a table listing the acronyms + * used in this module, along with their intended meanings. + * + * \anchor asfdoc_sam0_sercom_i2c_acronyms_table + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Acronyms
AcronymDescription
SDASerial Data Line
SCLSerial Clock Line
SERCOMSerial Communication Interface
DMADirect Memory Access
+ * + * \section asfdoc_sam0_sercom_i2c_extra_dependencies Dependencies + * The I2C driver has the following dependencies: + * \li \ref asfdoc_sam0_system_pinmux_group "System Pin Multiplexer Driver" + * + * + * \section asfdoc_sam0_sercom_i2c_extra_errata Errata + * There are no errata related to this driver. + * + * \section asfdoc_sam0_sercom_i2c_extra_history Module History + * \ref asfdoc_sam0_sercom_i2c_extra_history_table "Below" is an overview of the + * module history, detailing enhancements and fixes made to the module since + * its first release. The current version of this corresponds to the newest + * version listed in + * \ref asfdoc_sam0_sercom_i2c_extra_history_table "the table below". + * + * \anchor asfdoc_sam0_sercom_i2c_extra_history_table + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Module History
Changelog
+ * \li Added 10-bit addressing and high speed support in SAM D21 + * \li Separate structure i2c_packet into i2c_master_packet and i2c_slave packet + *
+ * \li Added support for SCL stretch and extended timeout hardware features in SAM D21 + * \li Added fast mode plus support in SAM D21 + *
Fixed incorrect logical mask for determining if a bus error has + * occurred in I2C Slave mode + *
Initial Release
+ */ + +/** + * \page asfdoc_sam0_sercom_i2c_exqsg Examples for SERCOM I2C Driver + * + * This is a list of the available Quick Start guides (QSGs) and example + * applications for \ref asfdoc_sam0_sercom_i2c_group. QSGs are simple examples with + * step-by-step instructions to configure and use this driver in a selection of + * use cases. Note that a QSG can be compiled as a standalone application or be + * added to the user application. + * + * \if I2C_MASTER_MODE + * - \subpage asfdoc_sam0_sercom_i2c_master_basic_use_case "Quick Start Guide for the I2C Master module - Basic Use Case" + * \endif + * \if I2C_MASTER_CALLBACK_MODE + * - \subpage asfdoc_sam0_sercom_i2c_master_callback_use_case "Quick Start Guide for the I2C Master module - Callback Use Case" + * - \subpage asfdoc_sam0_sercom_i2c_master_dma_use_case "Quick Start Guide for the I2C Master module - DMA Use Case" + * \endif + * \if I2C_SLAVE_MODE + * - \subpage asfdoc_sam0_sercom_i2c_slave_basic_use_case "Quick Start Guide for the I2C Slave module - Basic Use Case" + * \endif + * \if I2C_SLAVE_CALLBACK_MODE + * - \subpage asfdoc_sam0_sercom_i2c_slave_callback_use_case "Quick Start Guide for the I2C Slave module - Callback Use Case" + * - \subpage asfdoc_sam0_sercom_i2c_slave_dma_use_case "Quick Start Guide for the I2C Slave module - DMA Use Case" + * \endif + * + * \page asfdoc_sam0_sercom_i2c_document_revision_history Document Revision History + * + * \if (I2C_MASTER_MODE || I2C_MASTER_CALLBACK_MODE) + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Doc. Rev.DateComments
42117E12/2015Added support for SAM L21/L22, SAM DA1, SAM D09, and SAM C21
42117D12/2014Added support for 10-bit addressing and high speed in SAM D21. + * Added support for SAM R21 and SAM D10/D11.
42117C01/2014Added support for SAM D21
42117B06/2013Corrected documentation typos. Updated I2C Bus State Diagram.
42117A06/2013Initial release
+ * \else + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Doc. Rev.DateComments
42116E12/2015Added support for SAM L21/L22, SAM DA1, SAM D09, and SAM C21
42116D12/2014Added support for 10-bit addressing and high speed in SAM D21. + * Added support for SAM R21 and SAM D10/D11.
42116C01/2014Added support for SAM D21
42116B06/2013Corrected documentation typos. Updated I2C Bus State Diagram.
42116A06/2013Initial release
+ *\endif + */ + +#endif /* I2C_COMMON_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_master.h b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_master.h new file mode 100644 index 000000000..79c2effe8 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_master.h @@ -0,0 +1,629 @@ +/** + * \file + * + * \brief SAM SERCOM I2C Master Driver + * + * Copyright (C) 2012-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 I2C_MASTER_H_INCLUDED +#define I2C_MASTER_H_INCLUDED + +#include "i2c_common.h" +#include +#include + +#if I2C_MASTER_CALLBACK_MODE == true +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef PINMUX_DEFAULT +# define PINMUX_DEFAULT 0 +#endif + +/** + * \addtogroup asfdoc_sam0_sercom_i2c_group + * + * @{ + */ + +/** + * \brief I2C master packet for read/write + * + * Structure to be used when transferring I2C master packets. + */ +struct i2c_master_packet { + /** Address to slave device */ + uint16_t address; + /** Length of data array */ + uint16_t data_length; + /** Data array containing all data to be transferred */ + uint8_t *data; + /** Use 10-bit addressing. Set to false if the feature is not supported by the device */ + bool ten_bit_address; + /** Use high speed transfer. Set to false if the feature is not supported by the device */ + bool high_speed; + /** High speed mode master code (0000 1XXX), valid when high_speed is true */ + uint8_t hs_master_code; +}; + +/** \brief Interrupt flags + * + * Flags used when reading or setting interrupt flags. + */ +enum i2c_master_interrupt_flag { + /** Interrupt flag used for write */ + I2C_MASTER_INTERRUPT_WRITE = 0, + /** Interrupt flag used for read */ + I2C_MASTER_INTERRUPT_READ = 1, +}; + +/** + * \brief Values for hold time after start bit. + * + * Values for the possible I2C master mode SDA internal hold times after start + * bit has been sent. + */ +enum i2c_master_start_hold_time { + /** Internal SDA hold time disabled */ + I2C_MASTER_START_HOLD_TIME_DISABLED = SERCOM_I2CM_CTRLA_SDAHOLD(0), + /** Internal SDA hold time 50ns - 100ns */ + I2C_MASTER_START_HOLD_TIME_50NS_100NS = SERCOM_I2CM_CTRLA_SDAHOLD(1), + /** Internal SDA hold time 300ns - 600ns */ + I2C_MASTER_START_HOLD_TIME_300NS_600NS = SERCOM_I2CM_CTRLA_SDAHOLD(2), + /** Internal SDA hold time 400ns - 800ns */ + I2C_MASTER_START_HOLD_TIME_400NS_800NS = SERCOM_I2CM_CTRLA_SDAHOLD(3), +}; + +/** + * \brief Values for inactive bus time-out. + * + * If the inactive bus time-out is enabled and the bus is inactive for + * longer than the time-out setting, the bus state logic will be set to idle. + */ +enum i2c_master_inactive_timeout { + /** Inactive bus time-out disabled */ + I2C_MASTER_INACTIVE_TIMEOUT_DISABLED = SERCOM_I2CM_CTRLA_INACTOUT(0), + /** Inactive bus time-out 5-6 SCL cycle time-out */ + I2C_MASTER_INACTIVE_TIMEOUT_55US = SERCOM_I2CM_CTRLA_INACTOUT(1), + /** Inactive bus time-out 10-11 SCL cycle time-out */ + I2C_MASTER_INACTIVE_TIMEOUT_105US = SERCOM_I2CM_CTRLA_INACTOUT(2), + /** Inactive bus time-out 20-21 SCL cycle time-out */ + I2C_MASTER_INACTIVE_TIMEOUT_205US = SERCOM_I2CM_CTRLA_INACTOUT(3), +}; + +/** + * \brief I2C frequencies + * + * Values for I2C speeds supported by the module. The driver + * will also support setting any other value, in which case set + * the value in the \ref i2c_master_config at desired value divided by 1000. + * + * Example: If 10KHz operation is required, give baud_rate in the configuration + * structure the value 10. + */ +enum i2c_master_baud_rate { + /** Baud rate at 100KHz (Standard-mode) */ + I2C_MASTER_BAUD_RATE_100KHZ = 100, + /** Baud rate at 400KHz (Fast-mode) */ + I2C_MASTER_BAUD_RATE_400KHZ = 400, +#ifdef FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED + /** Baud rate at 1MHz (Fast-mode Plus) */ + I2C_MASTER_BAUD_RATE_1000KHZ = 1000, + /** Baud rate at 3.4MHz (High-speed mode) */ + I2C_MASTER_BAUD_RATE_3400KHZ = 3400, +#endif +}; + +#ifdef FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED +/** + * \brief Enum for the transfer speed + * + * Enum for the transfer speed. + */ +enum i2c_master_transfer_speed { + /** Standard-mode (Sm) up to 100KHz and Fast-mode (Fm) up to 400KHz */ + I2C_MASTER_SPEED_STANDARD_AND_FAST = SERCOM_I2CM_CTRLA_SPEED(0), + /** Fast-mode Plus (Fm+) up to 1MHz */ + I2C_MASTER_SPEED_FAST_MODE_PLUS = SERCOM_I2CM_CTRLA_SPEED(1), + /** High-speed mode (Hs-mode) up to 3.4MHz */ + I2C_MASTER_SPEED_HIGH_SPEED = SERCOM_I2CM_CTRLA_SPEED(2), +}; +#endif + +#if I2C_MASTER_CALLBACK_MODE == true +/** + * \brief Callback types + * + * The available callback types for the I2C master module. + */ +enum i2c_master_callback { + /** Callback for packet write complete */ + I2C_MASTER_CALLBACK_WRITE_COMPLETE = 0, + /** Callback for packet read complete */ + I2C_MASTER_CALLBACK_READ_COMPLETE = 1, + /** Callback for error */ + I2C_MASTER_CALLBACK_ERROR = 2, +# if !defined(__DOXYGEN__) + /** Total number of callbacks */ + _I2C_MASTER_CALLBACK_N = 3, +# endif +}; + +# if !defined(__DOXYGEN__) +/* Prototype for software module */ +struct i2c_master_module; + +typedef void (*i2c_master_callback_t)( + struct i2c_master_module *const module); +# endif +#endif + +/** + * \brief SERCOM I2C Master driver software device instance structure. + * + * SERCOM I2C Master driver 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 i2c_master_module { +#if !defined(__DOXYGEN__) + /** Hardware instance initialized for the struct */ + Sercom *hw; + /** Module lock */ + volatile bool locked; + /** Unknown bus state timeout */ + uint16_t unknown_bus_state_timeout; + /** Buffer write timeout value */ + uint16_t buffer_timeout; + /** If true, stop condition will be sent after a read/write */ + bool send_stop; + /** If true, nack signal will be sent after a read/write */ + bool send_nack; +# if I2C_MASTER_CALLBACK_MODE == true + /** Pointers to callback functions */ + volatile i2c_master_callback_t callbacks[_I2C_MASTER_CALLBACK_N]; + /** Mask for registered callbacks */ + volatile uint8_t registered_callback; + /** Mask for enabled callbacks */ + volatile uint8_t enabled_callback; + /** The total number of bytes to transfer */ + volatile uint16_t buffer_length; + /** + * Counter used for bytes left to send in write and to count number of + * obtained bytes in read + */ + volatile uint16_t buffer_remaining; + /** Data buffer for packet write and read */ + volatile uint8_t *buffer; + /** Save direction of async request. 1 = read, 0 = write */ + volatile enum i2c_transfer_direction transfer_direction; + /** Status for status read back in error callback */ + volatile enum status_code status; +# endif +#endif +}; + +/** + * \brief Configuration structure for the I2C Master device + * + * This is the configuration structure for the I2C Master device. It + * is used as an argument for \ref i2c_master_init to provide the desired + * configurations for the module. The structure should be initialized using the + * \ref i2c_master_get_config_defaults. + */ +struct i2c_master_config { + /** Baud rate (in KHz) for I2C operations in + * standard-mode, Fast-mode, and Fast-mode Plus Transfers, + * \ref i2c_master_baud_rate */ + uint32_t baud_rate; +#ifdef FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED + /** Baud rate (in KHz) for I2C operations in + * High-speed mode, \ref i2c_master_baud_rate */ + uint32_t baud_rate_high_speed; + /** Transfer speed mode */ + enum i2c_master_transfer_speed transfer_speed; +#endif + /** GCLK generator to use as clock source */ + enum gclk_generator generator_source; + /** Bus hold time after start signal on data line */ + enum i2c_master_start_hold_time start_hold_time; + /** Unknown bus state \ref asfdoc_sam0_sercom_i2c_unknown_bus_timeout "timeout" */ + uint16_t unknown_bus_state_timeout; + /** Timeout for packet write to wait for slave */ + uint16_t buffer_timeout; + /** Set to keep module active in sleep modes */ + bool run_in_standby; + /** PAD0 (SDA) pinmux */ + uint32_t pinmux_pad0; + /** PAD1 (SCL) pinmux */ + uint32_t pinmux_pad1; + /** Set to enable SCL low time-out */ + bool scl_low_timeout; + /** Inactive bus time out */ + enum i2c_master_inactive_timeout inactive_timeout; +#ifdef FEATURE_I2C_SCL_STRETCH_MODE + /** Set to enable SCL stretch only after ACK bit (required for high speed) */ + bool scl_stretch_only_after_ack_bit; +#endif +#ifdef FEATURE_I2C_SCL_EXTEND_TIMEOUT + /** Set to enable slave SCL low extend time-out */ + bool slave_scl_low_extend_timeout; + /** Set to enable maser SCL low extend time-out */ + bool master_scl_low_extend_timeout; +#endif + /** Get more accurate BAUD, considering rise time(required for standard-mode and Fast-mode) */ + uint16_t sda_scl_rise_time_ns; +}; + +/** + * \name Lock/Unlock + * @{ + */ + +/** + * \brief Attempt to get lock on driver instance + * + * This function checks the instance's lock, which indicates whether or not it + * is currently in use, and sets the lock if it was not already set. + * + * The purpose of this is to enable exclusive access to driver instances, so + * that, e.g., transactions by different services will not interfere with each + * other. + * + * \param[in,out] module Pointer to the driver instance to lock + * + * \retval STATUS_OK If the module was locked + * \retval STATUS_BUSY If the module was already locked + */ +static inline enum status_code i2c_master_lock( + struct i2c_master_module *const module) +{ + enum status_code status; + + system_interrupt_enter_critical_section(); + + if (module->locked) { + status = STATUS_BUSY; + } else { + module->locked = true; + status = STATUS_OK; + } + + system_interrupt_leave_critical_section(); + + return status; +} + +/** + * \brief Unlock driver instance + * + * This function clears the instance lock, indicating that it is available for + * use. + * + * \param[in,out] module Pointer to the driver instance to lock + * + * \retval STATUS_OK If the module was locked + * \retval STATUS_BUSY If the module was already locked + */ +static inline void i2c_master_unlock(struct i2c_master_module *const module) +{ + module->locked = false; +} + +/** @} */ + +/** + * \name Configuration and Initialization + * @{ + */ + +/** + * \brief Returns the synchronization status of the module + * + * Returns the synchronization status of the module. + * + * \param[in] module Pointer to software module structure + * + * \return Status of the synchronization. + * \retval true Module is busy synchronizing + * \retval false Module is not synchronizing + */ +static inline bool i2c_master_is_syncing ( + const struct i2c_master_module *const module) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_hw = &(module->hw->I2CM); + +#if defined(FEATURE_SERCOM_SYNCBUSY_SCHEME_VERSION_1) + return (i2c_hw->STATUS.reg & SERCOM_I2CM_STATUS_SYNCBUSY); +#elif defined(FEATURE_SERCOM_SYNCBUSY_SCHEME_VERSION_2) + return (i2c_hw->SYNCBUSY.reg & SERCOM_I2CM_SYNCBUSY_MASK); +#else +# error Unknown SERCOM SYNCBUSY scheme! +#endif +} + +#if !defined(__DOXYGEN__) +/** + * \internal + * Wait for hardware module to sync + * + * \param[in] module Pointer to software module structure + */ +static void _i2c_master_wait_for_sync( + const struct i2c_master_module *const module) +{ + /* Sanity check */ + Assert(module); + + while (i2c_master_is_syncing(module)) { + /* Wait for I2C module to sync. */ + } +} +#endif + +/** + * \brief Gets the I2C master default configurations + * + * Use to initialize the configuration structure to known default values. + * + * The default configuration is as follows: + * - Baudrate 100KHz + * - GCLK generator 0 + * - Do not run in standby + * - Start bit hold time 300ns - 600ns + * - Buffer timeout = 65535 + * - Unknown bus status timeout = 65535 + * - Do not run in standby + * - PINMUX_DEFAULT for SERCOM pads + * + * Those default configuration only available if the device supports it: + * - High speed baudrate 3.4MHz + * - Standard-mode and Fast-mode transfer speed + * - SCL stretch disabled + * - Slave SCL low extend time-out disabled + * - Master SCL low extend time-out disabled + * + * \param[out] config Pointer to configuration structure to be initiated + */ +static inline void i2c_master_get_config_defaults( + struct i2c_master_config *const config) +{ + /*Sanity check argument */ + Assert(config); + config->baud_rate = I2C_MASTER_BAUD_RATE_100KHZ; +#ifdef FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED + config->baud_rate_high_speed = I2C_MASTER_BAUD_RATE_3400KHZ; + config->transfer_speed = I2C_MASTER_SPEED_STANDARD_AND_FAST; +#endif + config->generator_source = GCLK_GENERATOR_0; + config->run_in_standby = false; + config->start_hold_time = I2C_MASTER_START_HOLD_TIME_300NS_600NS; + config->buffer_timeout = 65535; + config->unknown_bus_state_timeout = 65535; + config->pinmux_pad0 = PINMUX_DEFAULT; + config->pinmux_pad1 = PINMUX_DEFAULT; + config->scl_low_timeout = false; + config->inactive_timeout = I2C_MASTER_INACTIVE_TIMEOUT_DISABLED; +#ifdef FEATURE_I2C_SCL_STRETCH_MODE + config->scl_stretch_only_after_ack_bit = false; +#endif +#ifdef FEATURE_I2C_SCL_EXTEND_TIMEOUT + config->slave_scl_low_extend_timeout = false; + config->master_scl_low_extend_timeout = false; +#endif + /* The typical value is 215ns */ + config->sda_scl_rise_time_ns = 215; +} + +enum status_code i2c_master_init( + struct i2c_master_module *const module, + Sercom *const hw, + const struct i2c_master_config *const config); + +/** + * \brief Enables the I2C module + * + * Enables the requested I2C module and set the bus state to IDLE + * after the specified \ref asfdoc_sam0_sercom_i2c_timeout "timeout" period if no + * stop bit is detected. + * + * \param[in] module Pointer to the software module struct + */ +static inline void i2c_master_enable( + const struct i2c_master_module *const module) +{ + /* Sanity check of arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Timeout counter used to force bus state */ + uint32_t timeout_counter = 0; + + /* Wait for module to sync */ + _i2c_master_wait_for_sync(module); + + /* Enable module */ + i2c_module->CTRLA.reg |= SERCOM_I2CM_CTRLA_ENABLE; + +#if I2C_MASTER_CALLBACK_MODE == true + /* Enable module interrupts */ + system_interrupt_enable(_sercom_get_interrupt_vector(module->hw)); +#endif + /* Start timeout if bus state is unknown */ + while (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(1))) { + timeout_counter++; + if(timeout_counter >= (module->unknown_bus_state_timeout)) { + /* Timeout, force bus state to idle */ + i2c_module->STATUS.reg = SERCOM_I2CM_STATUS_BUSSTATE(1); + /* Workaround #1 */ + return; + } + } +} + +/** + * \brief Disable the I2C module + * + * Disables the requested I2C module. + * + * \param[in] module Pointer to the software module struct + */ +static inline void i2c_master_disable( + const struct i2c_master_module *const module) +{ + /* Sanity check of arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Disable module interrupts */ + system_interrupt_disable(_sercom_get_interrupt_vector(module->hw)); +#endif + + /* Wait for module to sync */ + _i2c_master_wait_for_sync(module); + + /* Disbale interrupt */ + i2c_module->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MASK; + /* Clear interrupt flag */ + i2c_module->INTFLAG.reg = SERCOM_I2CM_INTFLAG_MASK; + + /* Disable module */ + i2c_module->CTRLA.reg &= ~SERCOM_I2CM_CTRLA_ENABLE; + +} + +void i2c_master_reset(struct i2c_master_module *const module); + +/** @} */ + +/** +* \name Read and Write +* @{ +*/ + +enum status_code i2c_master_read_packet_wait( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_read_packet_wait_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_write_packet_wait( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_write_packet_wait_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +void i2c_master_send_stop(struct i2c_master_module *const module); + +void i2c_master_send_nack(struct i2c_master_module *const module); + +enum status_code i2c_master_read_byte( + struct i2c_master_module *const module, + uint8_t *byte); + +enum status_code i2c_master_write_byte( + struct i2c_master_module *const module, + uint8_t byte); + +enum status_code i2c_master_read_packet_wait_no_nack( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +/** @} */ + +#ifdef FEATURE_I2C_DMA_SUPPORT +/** +* \name SERCOM I2C Master with DMA Interfaces +* @{ +*/ + +/** + * \brief Set I2C for DMA transfer with slave address and transfer size. + * + * This function will set the slave address, transfer size and enable the auto transfer + * mode for DMA. + * + * \param[in,out] module Pointer to the driver instance to lock + * \param[in] addr I2C slave address + * \param[in] length I2C transfer length with DMA + * \param[in] direction I2C transfer direction + * + */ +static inline void i2c_master_dma_set_transfer(struct i2c_master_module *const module, + uint16_t addr, uint8_t length, enum i2c_transfer_direction direction) +{ + module->hw->I2CM.ADDR.reg = + SERCOM_I2CM_ADDR_ADDR(addr<<1) | + SERCOM_I2CM_ADDR_LENEN | + SERCOM_I2CM_ADDR_LEN(length) | + direction; +} + +/** @} */ +#endif + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* I2C_MASTER_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_master_interrupt.h b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_master_interrupt.h new file mode 100644 index 000000000..249525d3c --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_master_interrupt.h @@ -0,0 +1,214 @@ +/** + * \file + * + * \brief SAM SERCOM I2C Master Interrupt Driver + * + * Copyright (C) 2012-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 I2C_MASTER_INTERRUPT_H_INCLUDED +#define I2C_MASTER_INTERRUPT_H_INCLUDED + +#include "i2c_master.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup asfdoc_sam0_sercom_i2c_group + * @{ + * + */ + +/** + * \name Callbacks + * @{ + */ +#if !defined(__DOXYGEN__) +void _i2c_master_interrupt_handler( + uint8_t instance); +#endif + +void i2c_master_register_callback( + struct i2c_master_module *const module, + i2c_master_callback_t callback, + enum i2c_master_callback callback_type); + +void i2c_master_unregister_callback( + struct i2c_master_module *const module, + enum i2c_master_callback callback_type); + +/** + * \brief Enables callback + * + * Enables the callback specified by the callback_type. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback_type Callback type to enable + */ +static inline void i2c_master_enable_callback( + struct i2c_master_module *const module, + enum i2c_master_callback callback_type) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + /* Mark callback as enabled */ + module->enabled_callback |= (1 << callback_type); +} + +/** + * \brief Disables callback + * + * Disables the callback specified by the callback_type. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback_type Callback type to disable + */ +static inline void i2c_master_disable_callback( + struct i2c_master_module *const module, + enum i2c_master_callback callback_type) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + /* Mark callback as disabled */ + module->enabled_callback &= ~(1 << callback_type); +} + +/** @} */ + +/** + * \name Read and Write, Interrupt-driven + * @{ + */ + +enum status_code i2c_master_read_bytes( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_read_packet_job( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_read_packet_job_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_read_packet_job_no_nack( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_write_bytes( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_write_packet_job( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_write_packet_job_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +/** + * \brief Cancel any currently ongoing operation + * + * Terminates the running transfer operation. + * + * \param[in,out] module Pointer to software module structure + */ +static inline void i2c_master_cancel_job( + struct i2c_master_module *const module) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + /* Set buffer to 0 */ + module->buffer_remaining = 0; + /* Update status */ + module->status = STATUS_ABORTED; +} + +/** + * \brief Get status from ongoing job + * + * Will return the status of a transfer operation. + * + * \param[in] module Pointer to software module structure + * + * \return Last status code from transfer operation. + * \retval STATUS_OK No error has occurred + * \retval STATUS_BUSY If transfer is in progress + * \retval STATUS_BUSY If master module is busy + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + * \retval STATUS_ERR_TIMEOUT If timeout occurred + * \retval STATUS_ERR_OVERFLOW If slave did not acknowledge last sent + * data, indicating that slave does not + * want more data and was not able to read + */ +static inline enum status_code i2c_master_get_job_status( + struct i2c_master_module *const module) +{ + /* Check sanity */ + Assert(module); + Assert(module->hw); + + /* Return current status code */ + return module->status; +} + +/** @} */ + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* I2C_MASTER_INTERRUPT_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.c b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.c new file mode 100644 index 000000000..4e37b4f09 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.c @@ -0,0 +1,1050 @@ +/** + * \file + * + * \brief SAM I2C Master Driver + * + * Copyright (C) 2012-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 "i2c_master.h" + +#if I2C_MASTER_CALLBACK_MODE == true +# include "i2c_master_interrupt.h" +#endif + +/* Forward declaration */ +enum status_code _i2c_master_wait_for_bus( + struct i2c_master_module *const module); + +enum status_code _i2c_master_address_response( + struct i2c_master_module *const module); + +enum status_code _i2c_master_send_hs_master_code( + struct i2c_master_module *const module, + uint8_t hs_master_code); + +#if !defined(__DOXYGEN__) + +/** + * \internal Sets configurations to module + * + * \param[out] module Pointer to software module structure + * \param[in] config Configuration structure with configurations to set + * + * \return Status of setting configuration. + * \retval STATUS_OK If module was configured correctly + * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than + * previously set + * \retval STATUS_ERR_BAUDRATE_UNAVAILABLE If given baudrate is not compatible + * with set GCLK frequency + */ +static enum status_code _i2c_master_set_config( + struct i2c_master_module *const module, + const struct i2c_master_config *const config) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + Assert(config); + + /* Temporary variables. */ + uint32_t tmp_ctrla; + int32_t tmp_baud = 0; + int32_t tmp_baud_hs = 0; + int32_t tmp_baudlow_hs = 0; + enum status_code tmp_status_code = STATUS_OK; + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + Sercom *const sercom_hw = module->hw; + + uint8_t sercom_index = _sercom_get_sercom_inst_index(sercom_hw); + + /* Pin configuration */ + struct system_pinmux_config pin_conf; + system_pinmux_get_config_defaults(&pin_conf); + + uint32_t pad0 = config->pinmux_pad0; + uint32_t pad1 = config->pinmux_pad1; + + /* SERCOM PAD0 - SDA */ + if (pad0 == PINMUX_DEFAULT) { + pad0 = _sercom_get_default_pad(sercom_hw, 0); + } + pin_conf.mux_position = pad0 & 0xFFFF; + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK; + system_pinmux_pin_set_config(pad0 >> 16, &pin_conf); + + /* SERCOM PAD1 - SCL */ + if (pad1 == PINMUX_DEFAULT) { + pad1 = _sercom_get_default_pad(sercom_hw, 1); + } + pin_conf.mux_position = pad1 & 0xFFFF; + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK; + system_pinmux_pin_set_config(pad1 >> 16, &pin_conf); + + /* Save timeout on unknown bus state in software module. */ + module->unknown_bus_state_timeout = config->unknown_bus_state_timeout; + + /* Save timeout on buffer write. */ + module->buffer_timeout = config->buffer_timeout; + + /* Set whether module should run in standby. */ + if (config->run_in_standby || system_is_debugger_present()) { + tmp_ctrla = SERCOM_I2CM_CTRLA_RUNSTDBY; + } else { + tmp_ctrla = 0; + } + + /* Check and set start data hold timeout. */ + if (config->start_hold_time != I2C_MASTER_START_HOLD_TIME_DISABLED) { + tmp_ctrla |= config->start_hold_time; + } + + /* Check and set transfer speed */ + tmp_ctrla |= config->transfer_speed; + + /* Check and set SCL low timeout. */ + if (config->scl_low_timeout) { + tmp_ctrla |= SERCOM_I2CM_CTRLA_LOWTOUTEN; + } + + /* Check and set inactive bus timeout. */ + if (config->inactive_timeout != I2C_MASTER_INACTIVE_TIMEOUT_DISABLED) { + tmp_ctrla |= config->inactive_timeout; + } + + /* Check and set SCL clock stretch mode. */ + if (config->scl_stretch_only_after_ack_bit || (config->transfer_speed == I2C_MASTER_SPEED_HIGH_SPEED)) { + tmp_ctrla |= SERCOM_I2CM_CTRLA_SCLSM; + } + + /* Check and set slave SCL low extend timeout. */ + if (config->slave_scl_low_extend_timeout) { + tmp_ctrla |= SERCOM_I2CM_CTRLA_SEXTTOEN; + } + + /* Check and set master SCL low extend timeout. */ + if (config->master_scl_low_extend_timeout) { + tmp_ctrla |= SERCOM_I2CM_CTRLA_MEXTTOEN; + } + + /* Write config to register CTRLA. */ + i2c_module->CTRLA.reg |= tmp_ctrla; + + /* Set configurations in CTRLB. */ + i2c_module->CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN; + + /* Find and set baudrate, considering sda/scl rise time */ + uint32_t fgclk = system_gclk_chan_get_hz(SERCOM0_GCLK_ID_CORE + sercom_index); + uint32_t fscl = 1000 * config->baud_rate; + uint32_t fscl_hs = 1000 * config->baud_rate_high_speed; + uint32_t trise = config->sda_scl_rise_time_ns; + + tmp_baud = (int32_t)(div_ceil( + fgclk - fscl * (10 + fgclk * trise * 0.000000001), 2 * fscl)); + + /* For High speed mode, set the SCL ratio of high:low to 1:2. */ + if (config->transfer_speed == I2C_MASTER_SPEED_HIGH_SPEED) { + tmp_baudlow_hs = (int32_t)((fgclk * 2.0) / (3.0 * fscl_hs) - 1); + if (tmp_baudlow_hs) { + tmp_baud_hs = (int32_t)(fgclk / fscl_hs) - 2 - tmp_baudlow_hs; + } else { + tmp_baud_hs = (int32_t)(div_ceil(fgclk, 2 * fscl_hs)) - 1; + } + } + + /* Check that baudrate is supported at current speed. */ + if (tmp_baud > 255 || tmp_baud < 0 || tmp_baud_hs > 255 || tmp_baud_hs < 0) { + /* Baud rate not supported. */ + tmp_status_code = STATUS_ERR_BAUDRATE_UNAVAILABLE; + } + if (tmp_status_code != STATUS_ERR_BAUDRATE_UNAVAILABLE) { + /* Baud rate acceptable. */ + i2c_module->BAUD.reg = SERCOM_I2CM_BAUD_BAUD(tmp_baud) | + SERCOM_I2CM_BAUD_HSBAUD(tmp_baud_hs) | SERCOM_I2CM_BAUD_HSBAUDLOW(tmp_baudlow_hs); + } + + return tmp_status_code; +} +#endif /* __DOXYGEN__ */ + +/** + * \brief Initializes the requested I2C hardware module + * + * Initializes the SERCOM I2C master device requested and sets the provided + * software module struct. Run this function before any further use of + * the driver. + * + * \param[out] module Pointer to software module struct + * \param[in] hw Pointer to the hardware instance + * \param[in] config Pointer to the configuration struct + * + * \return Status of initialization. + * \retval STATUS_OK Module initiated correctly + * \retval STATUS_ERR_DENIED If module is enabled + * \retval STATUS_BUSY If module is busy resetting + * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than + * previously set + * \retval STATUS_ERR_BAUDRATE_UNAVAILABLE If given baudrate is not compatible + * with set GCLK frequency + * + */ +enum status_code i2c_master_init( + struct i2c_master_module *const module, + Sercom *const hw, + const struct i2c_master_config *const config) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(hw); + Assert(config); + + /* Initialize software module */ + module->hw = hw; + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + uint32_t pm_index, gclk_index; + +#if (SAML22) || (SAMC20) + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; +#elif (SAML21) || (SAMR30) + if (sercom_index == 5) { + pm_index = MCLK_APBDMASK_SERCOM5_Pos; + gclk_index = SERCOM5_GCLK_ID_CORE; + } else { + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + } +#elif (SAMC21) + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + if (sercom_index == 5) { + gclk_index = SERCOM5_GCLK_ID_CORE; + } else { + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + } +#else + pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; +#endif + + /* Turn on module in PM */ +#if (SAML21) || (SAMR30) + if (sercom_index == 5) { + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBD, 1 << pm_index); + } else { + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); + } +#else + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); +#endif + + /* Set up the GCLK for the module */ + struct system_gclk_chan_config gclk_chan_conf; + system_gclk_chan_get_config_defaults(&gclk_chan_conf); + gclk_chan_conf.source_generator = config->generator_source; + system_gclk_chan_set_config(gclk_index, &gclk_chan_conf); + system_gclk_chan_enable(gclk_index); + sercom_set_gclk_generator(config->generator_source, false); + + /* Check if module is enabled. */ + if (i2c_module->CTRLA.reg & SERCOM_I2CM_CTRLA_ENABLE) { + return STATUS_ERR_DENIED; + } + + /* Check if reset is in progress. */ + if (i2c_module->CTRLA.reg & SERCOM_I2CM_CTRLA_SWRST) { + return STATUS_BUSY; + } + +#if I2C_MASTER_CALLBACK_MODE == true + /* Get sercom instance index and register callback. */ + uint8_t instance_index = _sercom_get_sercom_inst_index(module->hw); + _sercom_set_handler(instance_index, _i2c_master_interrupt_handler); + _sercom_instances[instance_index] = module; + + /* Initialize values in module. */ + module->registered_callback = 0; + module->enabled_callback = 0; + module->buffer_length = 0; + module->buffer_remaining = 0; + + module->status = STATUS_OK; + module->buffer = NULL; +#endif + + /* Set sercom module to operate in I2C master mode. */ + i2c_module->CTRLA.reg = SERCOM_I2CM_CTRLA_MODE(0x5); + + /* Set config and return status. */ + return _i2c_master_set_config(module, config); +} + +/** + * \brief Resets the hardware module + * + * Reset the module to hardware defaults. + * + * \param[in,out] module Pointer to software module structure + */ +void i2c_master_reset(struct i2c_master_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Wait for sync */ + _i2c_master_wait_for_sync(module); + + /* Disable module */ + i2c_master_disable(module); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Clear all pending interrupts */ + system_interrupt_enter_critical_section(); + system_interrupt_clear_pending(_sercom_get_interrupt_vector(module->hw)); + system_interrupt_leave_critical_section(); +#endif + + /* Wait for sync */ + _i2c_master_wait_for_sync(module); + + /* Reset module */ + i2c_module->CTRLA.reg = SERCOM_I2CM_CTRLA_SWRST; +} + +#if !defined(__DOXYGEN__) +/** + * \internal + * Address response. Called when address is answered or timed out. + * + * \param[in,out] module Pointer to software module structure + * + * \return Status of address response. + * \retval STATUS_OK No error has occurred + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code _i2c_master_address_response( + struct i2c_master_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Check for error and ignore bus-error; workaround for BUSSTATE stuck in + * BUSY */ + if (i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB) { + + /* Clear write interrupt flag */ + i2c_module->INTFLAG.reg = SERCOM_I2CM_INTFLAG_SB; + + /* Check arbitration. */ + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_ARBLOST) { + /* Return packet collision. */ + return STATUS_ERR_PACKET_COLLISION; + } + /* Check that slave responded with ack. */ + } else if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { + /* Slave busy. Issue ack and stop command. */ + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + + /* Return bad address value. */ + return STATUS_ERR_BAD_ADDRESS; + } + + return STATUS_OK; +} + +/** + * \internal + * Waits for answer on bus. + * + * \param[in,out] module Pointer to software module structure + * + * \return Status of bus. + * \retval STATUS_OK If given response from slave device + * \retval STATUS_ERR_TIMEOUT If no response was given within specified timeout + * period + */ +enum status_code _i2c_master_wait_for_bus( + struct i2c_master_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Wait for reply. */ + uint16_t timeout_counter = 0; + while (!(i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) && + !(i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB)) { + + /* Check timeout condition. */ + if (++timeout_counter >= module->buffer_timeout) { + return STATUS_ERR_TIMEOUT; + } + } + return STATUS_OK; +} +#endif /* __DOXYGEN__ */ + +/** + * \internal + * Send master code for high speed transfer. + * + * \param[in,out] module Pointer to software module structure + * \param[in] hs_master_code 8-bit master code (0000 1XXX) + * + * \return Status of bus. + * \retval STATUS_OK No error happen + */ +enum status_code _i2c_master_send_hs_master_code( + struct i2c_master_module *const module, + uint8_t hs_master_code) +{ + SercomI2cm *const i2c_module = &(module->hw->I2CM); + /* Return value. */ + enum status_code tmp_status; + + /* Set NACK for high speed code */ + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + /* Send high speed code */ + i2c_module->ADDR.reg = hs_master_code; + /* Wait for response on bus. */ + tmp_status = _i2c_master_wait_for_bus(module); + /* Clear write interrupt flag */ + i2c_module->INTFLAG.reg = SERCOM_I2CM_INTENCLR_MB; + + return tmp_status; +} + + +/** + * \internal + * Starts blocking read operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of reading packet. + * \retval STATUS_OK The packet was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + * + */ +static enum status_code _i2c_master_read_packet( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + Assert(packet); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Return value. */ + enum status_code tmp_status; + uint16_t tmp_data_length = packet->data_length; + + /* Written buffer counter. */ + uint16_t counter = 0; + + bool sclsm_flag = i2c_module->CTRLA.bit.SCLSM; + + /* Switch to high speed mode */ + if (packet->high_speed) { + _i2c_master_send_hs_master_code(module, packet->hs_master_code); + } + + /* Set action to ACK. */ + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + + /* Set address and direction bit. Will send start command on bus. */ + if (packet->ten_bit_address) { + /* + * Write ADDR.ADDR[10:1] with the 10-bit address. ADDR.TENBITEN must + * be set and read/write bit (ADDR.ADDR[0]) equal to 0. + */ + i2c_module->ADDR.reg = (packet->address << 1) | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) | + SERCOM_I2CM_ADDR_TENBITEN; + + /* Wait for response on bus. */ + tmp_status = _i2c_master_wait_for_bus(module); + + /* Set action to ack. */ + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + + /* Check for address response error unless previous error is + * detected. */ + if (tmp_status == STATUS_OK) { + tmp_status = _i2c_master_address_response(module); + } + + if (tmp_status == STATUS_OK) { + /* + * Write ADDR[7:0] register to "11110 address[9:8] 1" + * ADDR.TENBITEN must be cleared + */ + i2c_module->ADDR.reg = (((packet->address >> 8) | 0x78) << 1) | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) | + I2C_TRANSFER_READ; + } else { + return tmp_status; + } + } else { + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_READ | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos); + } + + /* Wait for response on bus. */ + tmp_status = _i2c_master_wait_for_bus(module); + + /* Set action to ack or nack. */ + if ((sclsm_flag) && (packet->data_length == 1)) { + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + } else { + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + } + + /* Check for address response error unless previous error is + * detected. */ + if (tmp_status == STATUS_OK) { + tmp_status = _i2c_master_address_response(module); + } + + /* Check that no error has occurred. */ + if (tmp_status == STATUS_OK) { + /* Read data buffer. */ + while (tmp_data_length--) { + /* Check that bus ownership is not lost. */ + if (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) { + return STATUS_ERR_PACKET_COLLISION; + } + + if (module->send_nack && (((!sclsm_flag) && (tmp_data_length == 0)) || + ((sclsm_flag) && (tmp_data_length == 1)))) { + /* Set action to NACK */ + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + } else { + /* Save data to buffer. */ + _i2c_master_wait_for_sync(module); + packet->data[counter++] = i2c_module->DATA.reg; + /* Wait for response. */ + tmp_status = _i2c_master_wait_for_bus(module); + } + + /* Check for error. */ + if (tmp_status != STATUS_OK) { + break; + } + } + + if (module->send_stop) { + /* Send stop command unless arbitration is lost. */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } + + /* Save last data to buffer. */ + _i2c_master_wait_for_sync(module); + packet->data[counter] = i2c_module->DATA.reg; + } + + return tmp_status; +} + +/** + * \brief Reads data packet from slave + * + * Reads a data packet from the specified slave address on the I2C + * bus and sends a stop condition when finished. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of reading packet. + * \retval STATUS_OK The packet was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_read_packet_wait( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = true; + module->send_nack = true; + + return _i2c_master_read_packet(module, packet); +} + +/** + * \brief Reads data packet from slave without sending a stop condition when done + * + * Reads a data packet from the specified slave address on the I2C + * bus without sending a stop condition when done, thus retaining ownership of + * the bus when done. To end the transaction, a + * \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition must be + * performed. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of reading packet. + * \retval STATUS_OK The packet was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_read_packet_wait_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = false; + module->send_nack = true; + + return _i2c_master_read_packet(module, packet); +} + +/** + * \internal + * Starts blocking read operation. + * \brief Reads data packet from slave without sending a nack signal and a stop + * condition when done + * + * Reads a data packet from the specified slave address on the I2C + * bus without sending a nack signal and a stop condition when done, + * thus retaining ownership of the bus when done. To end the transaction, a + * \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition must be + * performed. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of reading packet. + * \retval STATUS_OK The packet was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_read_packet_wait_no_nack( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = false; + module->send_nack = false; + + return _i2c_master_read_packet(module, packet); +} + +/** + * \internal + * Starts blocking write operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of write packet. + * \retval STATUS_OK The packet was write successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +static enum status_code _i2c_master_write_packet( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Return value. */ + enum status_code tmp_status; + uint16_t tmp_data_length = packet->data_length; + + _i2c_master_wait_for_sync(module); + + /* Switch to high speed mode */ + if (packet->high_speed) { + _i2c_master_send_hs_master_code(module, packet->hs_master_code); + } + + /* Set action to ACK. */ + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + + /* Set address and direction bit. Will send start command on bus. */ + if (packet->ten_bit_address) { + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_WRITE | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) | + SERCOM_I2CM_ADDR_TENBITEN; + } else { + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_WRITE | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos); + } + /* Wait for response on bus. */ + tmp_status = _i2c_master_wait_for_bus(module); + + /* Check for address response error unless previous error is + * detected. */ + if (tmp_status == STATUS_OK) { + tmp_status = _i2c_master_address_response(module); + } + + /* Check that no error has occurred. */ + if (tmp_status == STATUS_OK) { + /* Buffer counter. */ + uint16_t buffer_counter = 0; + + /* Write data buffer. */ + while (tmp_data_length--) { + /* Check that bus ownership is not lost. */ + if (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) { + return STATUS_ERR_PACKET_COLLISION; + } + + /* Write byte to slave. */ + _i2c_master_wait_for_sync(module); + i2c_module->DATA.reg = packet->data[buffer_counter++]; + + /* Wait for response. */ + tmp_status = _i2c_master_wait_for_bus(module); + + /* Check for error. */ + if (tmp_status != STATUS_OK) { + break; + } + + /* Check for NACK from slave. */ + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { + /* Return bad data value. */ + tmp_status = STATUS_ERR_OVERFLOW; + break; + } + } + + if (module->send_stop) { + /* Stop command */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } + } + + return tmp_status; +} + +/** + * \brief Writes data packet to slave + * + * Writes a data packet to the specified slave address on the I2C bus + * and sends a stop condition when finished. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of write packet. + * \retval STATUS_OK If packet was write successfully + * \retval STATUS_BUSY If master module is busy with a job + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + * \retval STATUS_ERR_TIMEOUT If timeout occurred + * \retval STATUS_ERR_OVERFLOW If slave did not acknowledge last sent + * data, indicating that slave does not + * want more data and was not able to read + * last data sent + */ +enum status_code i2c_master_write_packet_wait( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = true; + module->send_nack = true; + + return _i2c_master_write_packet(module, packet); +} + +/** + * \brief Writes data packet to slave without sending a stop condition when done + * + * Writes a data packet to the specified slave address on the I2C bus + * without sending a stop condition, thus retaining ownership of the bus when + * done. To end the transaction, a \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition or sending a + * stop with the \ref i2c_master_send_stop function must be performed. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of write packet. + * \retval STATUS_OK If packet was write successfully + * \retval STATUS_BUSY If master module is busy + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + * \retval STATUS_ERR_TIMEOUT If timeout occurred + * \retval STATUS_ERR_OVERFLOW If slave did not acknowledge last sent + * data, indicating that slave do not want + * more data + */ +enum status_code i2c_master_write_packet_wait_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = false; + module->send_nack = true; + + return _i2c_master_write_packet(module, packet); +} + +/** + * \brief Sends stop condition on bus + * + * Sends a stop condition on bus. + * + * \note This function can only be used after the + * \ref i2c_master_write_packet_wait_no_stop function. If a stop condition + * is to be sent after a read, the \ref i2c_master_read_packet_wait + * function must be used. + * + * \param[in,out] module Pointer to the software instance struct + */ +void i2c_master_send_stop(struct i2c_master_module *const module) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Send stop command */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); +} + +/** + * \brief Sends nack signal on bus + * + * Sends a nack signal on bus. + * + * \note This function can only be used after the + * \ref i2c_master_write_packet_wait_no_nack function, + * or \ref i2c_master_read_byte function. + * \param[in,out] module Pointer to the software instance struct + */ +void i2c_master_send_nack(struct i2c_master_module *const module) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Send nack signal */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; +} + +/** + * \brief Reads one byte data from slave + * + * \param[in,out] module Pointer to software module struct + * \param[out] byte Read one byte data to slave + * + * \return Status of reading byte. + * \retval STATUS_OK One byte was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_read_byte( + struct i2c_master_module *const module, + uint8_t *byte) +{ + enum status_code tmp_status; + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + /* Write byte to slave. */ + _i2c_master_wait_for_sync(module); + *byte = i2c_module->DATA.reg; + /* Wait for response. */ + tmp_status = _i2c_master_wait_for_bus(module); + + return tmp_status; +} + +/** + * \brief Write one byte data to slave + * + * \param[in,out] module Pointer to software module struct + * \param[in] byte Send one byte data to slave + * + * \return Status of writing byte. + * \retval STATUS_OK One byte was write successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_write_byte( + struct i2c_master_module *const module, + uint8_t byte) +{ + enum status_code tmp_status; + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Write byte to slave. */ + _i2c_master_wait_for_sync(module); + i2c_module->DATA.reg = byte; + /* Wait for response. */ + tmp_status = _i2c_master_wait_for_bus(module); + return tmp_status; +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master_interrupt.c b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master_interrupt.c new file mode 100644 index 000000000..7551b2912 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master_interrupt.c @@ -0,0 +1,762 @@ +/** + * \file + * + * \brief SAM I2C Master Interrupt Driver + * + * Copyright (C) 2012-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 "i2c_master_interrupt.h" + +extern enum status_code _i2c_master_wait_for_bus( + struct i2c_master_module *const module); + +extern enum status_code _i2c_master_address_response( + struct i2c_master_module *const module); + +extern enum status_code _i2c_master_send_hs_master_code( + struct i2c_master_module *const module, + uint8_t hs_master_code);; + +/** + * \internal + * Read next data. Used by interrupt handler to get next data byte from slave. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_master_read( + struct i2c_master_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + bool sclsm_flag = i2c_module->CTRLA.bit.SCLSM; + + /* Find index to save next value in buffer */ + uint16_t buffer_index = module->buffer_length; + buffer_index -= module->buffer_remaining; + + module->buffer_remaining--; + + if (sclsm_flag) { + if (module->send_nack && module->buffer_remaining == 1) { + /* Set action to NACK. */ + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + } + } else { + if (module->send_nack && module->buffer_remaining == 0) { + /* Set action to NACK. */ + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + } + } + + if (module->buffer_remaining == 0) { + if (module->send_stop) { + /* Send stop condition */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } + } + + /* Read byte from slave and put in buffer */ + _i2c_master_wait_for_sync(module); + module->buffer[buffer_index] = i2c_module->DATA.reg; +} + +/** + * \internal + * + * Write next data. Used by interrupt handler to send next data byte to slave. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_master_write(struct i2c_master_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Check for ack from slave */ + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) + { + /* Set status */ + module->status = STATUS_ERR_OVERFLOW; + /* Do not write more data */ + return; + } + + /* Find index to get next byte in buffer */ + uint16_t buffer_index = module->buffer_length; + buffer_index -= module->buffer_remaining; + + module->buffer_remaining--; + + /* Write byte from buffer to slave */ + _i2c_master_wait_for_sync(module); + i2c_module->DATA.reg = module->buffer[buffer_index]; +} + +/** + * \internal + * Acts on slave address response. Checks for errors concerning master->slave + * handshake. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_master_async_address_response( + struct i2c_master_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Check for error. Ignore bus-error; workaround for bus state stuck in + * BUSY. + */ + if (i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) + { + /* Clear write interrupt flag */ + i2c_module->INTFLAG.reg = SERCOM_I2CM_INTENCLR_MB; + + /* Check arbitration */ + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_ARBLOST) { + /* Return busy */ + module->status = STATUS_ERR_PACKET_COLLISION; + } + /* No slave responds */ + else if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { + module->status = STATUS_ERR_BAD_ADDRESS; + module->buffer_remaining = 0; + + if (module->send_stop) { + /* Send stop condition */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } + } + } + + module->buffer_length = module->buffer_remaining; + + /* Check for status OK. */ + if (module->status == STATUS_BUSY) { + /* Call function based on transfer direction. */ + if (module->transfer_direction == I2C_TRANSFER_WRITE) { + _i2c_master_write(module); + } else { + _i2c_master_read(module); + } + } +} + +/** + * \brief Registers callback for the specified callback type + * + * Associates the given callback function with the + * specified callback type. + * + * To enable the callback, the \ref i2c_master_enable_callback function + * must be used. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback Pointer to the function desired for the + * specified callback + * \param[in] callback_type Callback type to register + */ +void i2c_master_register_callback( + struct i2c_master_module *const module, + const i2c_master_callback_t callback, + enum i2c_master_callback callback_type) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(callback); + + /* Register callback */ + module->callbacks[callback_type] = callback; + + /* Set corresponding bit to set callback as registered */ + module->registered_callback |= (1 << callback_type); +} + +/** + * \brief Unregisters callback for the specified callback type + * + * When called, the currently registered callback for the given callback type + * will be removed. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback_type Specifies the callback type to unregister + */ +void i2c_master_unregister_callback( + struct i2c_master_module *const module, + enum i2c_master_callback callback_type) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + /* Register callback */ + module->callbacks[callback_type] = NULL; + + /* Clear corresponding bit to set callback as unregistered */ + module->registered_callback &= ~(1 << callback_type); +} + +/** + * \internal + * Starts a read bytes operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting reading I2C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_master_read_bytes( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Save packet to software module */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->transfer_direction = I2C_TRANSFER_READ; + module->status = STATUS_BUSY; + module->send_stop = false; + module->send_nack = false; + + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + return STATUS_OK; +} + +/** + * \internal + * Starts a read packet operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting reading I2C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +static enum status_code _i2c_master_read_packet( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + enum status_code tmp_status; + + /* Save packet to software module */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->transfer_direction = I2C_TRANSFER_READ; + module->status = STATUS_BUSY; + + bool sclsm_flag = i2c_module->CTRLA.bit.SCLSM; + + /* Switch to high speed mode */ + if (packet->high_speed) { + _i2c_master_send_hs_master_code(module, packet->hs_master_code); + } + + /* Set action to ACK or NACK. */ + if ((sclsm_flag) && (packet->data_length == 1)) { + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + } else { + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + } + + if (packet->ten_bit_address) { + /* + * Write ADDR.ADDR[10:1] with the 10-bit address. ADDR.TENBITEN must + * be set and read/write bit (ADDR.ADDR[0]) equal to 0. + */ + i2c_module->ADDR.reg = (packet->address << 1) | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) | + SERCOM_I2CM_ADDR_TENBITEN; + + /* Wait for response on bus. */ + tmp_status = _i2c_master_wait_for_bus(module); + + /* Set action to ack. */ + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + + /* Check for address response error unless previous error is + * detected. */ + if (tmp_status == STATUS_OK) { + tmp_status = _i2c_master_address_response(module); + } + + if (tmp_status == STATUS_OK) { + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + /* + * Write ADDR[7:0] register to "11110 address[9:8] 1" + * ADDR.TENBITEN must be cleared + */ + i2c_module->ADDR.reg = (((packet->address >> 8) | 0x78) << 1) | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) | + I2C_TRANSFER_READ; + } else { + return tmp_status; + } + } else { + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + /* Set address and direction bit. Will send start command on bus */ + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_READ | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos); + } + + return STATUS_OK; +} + +/** + * \brief Initiates a read packet operation + * + * Reads a data packet from the specified slave address on the I2C + * bus. This is the non-blocking equivalent of \ref i2c_master_read_packet_wait. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting reading I2C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_master_read_packet_job( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Make sure we send STOP */ + module->send_stop = true; + module->send_nack = true; + /* Start reading */ + return _i2c_master_read_packet(module, packet); +} + +/** + * \brief Initiates a read packet operation without sending a STOP condition when done + * + * Reads a data packet from the specified slave address on the I2C bus without + * sending a stop condition, thus retaining ownership of the bus when done. + * To end the transaction, a \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition must be + * performed. + * + * This is the non-blocking equivalent of \ref i2c_master_read_packet_wait_no_stop. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting reading I2C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another operation + */ +enum status_code i2c_master_read_packet_job_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Make sure we don't send STOP */ + module->send_stop = false; + module->send_nack = true; + /* Start reading */ + return _i2c_master_read_packet(module, packet); +} + +/** + * \brief Initiates a read packet operation without sending a NACK signal and a + * STOP condition when done + * + * Reads a data packet from the specified slave address on the I2C bus without + * sending a nack and a stop condition, thus retaining ownership of the bus when done. + * To end the transaction, a \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition must be + * performed. + * + * This is the non-blocking equivalent of \ref i2c_master_read_packet_wait_no_stop. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting reading I2C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another operation + */ +enum status_code i2c_master_read_packet_job_no_nack( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Make sure we don't send STOP */ + module->send_stop = false; + module->send_nack = false; + /* Start reading */ + return _i2c_master_read_packet(module, packet); +} + +/** + * \internal + * Starts a write bytes operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting write I2C bytes. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_master_write_bytes( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Save packet to software module */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->transfer_direction = I2C_TRANSFER_WRITE; + module->status = STATUS_BUSY; + module->send_stop = false; + module->send_nack = false; + + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + return STATUS_OK; +} + +/** + * \internal Initiates a write packet operation + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting writing I2C packet job. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +static enum status_code _i2c_master_write_packet( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Switch to high speed mode */ + if (packet->high_speed) { + _i2c_master_send_hs_master_code(module, packet->hs_master_code); + } + + /* Set action to ACK. */ + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + + /* Save packet to software module */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->transfer_direction = I2C_TRANSFER_WRITE; + module->status = STATUS_BUSY; + + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + /* Set address and direction bit, will send start command on bus */ + if (packet->ten_bit_address) { + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_WRITE | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) | + SERCOM_I2CM_ADDR_TENBITEN; + } else { + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_WRITE | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos); + } + + return STATUS_OK; +} + +/** + * \brief Initiates a write packet operation + * + * Writes a data packet to the specified slave address on the I2C + * bus. This is the non-blocking equivalent of \ref i2c_master_write_packet_wait. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting writing I2C packet job. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_master_write_packet_job( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with another job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Make sure we send STOP at end*/ + module->send_stop = true; + module->send_nack = true; + /* Start write operation */ + return _i2c_master_write_packet(module, packet); +} + +/** + * \brief Initiates a write packet operation without sending a STOP condition when done + * + * Writes a data packet to the specified slave address on the I2C bus + * without sending a stop condition, thus retaining ownership of the bus when + * done. To end the transaction, a \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition or sending + * a stop with the \ref i2c_master_send_stop function must be performed. + * + * This is the non-blocking equivalent of \ref i2c_master_write_packet_wait_no_stop. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting writing I2C packet job. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another + */ +enum status_code i2c_master_write_packet_job_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with another job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Do not send stop condition when done */ + module->send_stop = false; + module->send_nack = true; + /* Start write operation */ + return _i2c_master_write_packet(module, packet); +} + +/** + * \internal + * Interrupt handler for I2C master. + * + * \param[in] instance SERCOM instance that triggered the interrupt + */ +void _i2c_master_interrupt_handler( + uint8_t instance) +{ + /* Get software module for callback handling */ + struct i2c_master_module *module = + (struct i2c_master_module*)_sercom_instances[instance]; + + Assert(module); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + bool sclsm_flag = i2c_module->CTRLA.bit.SCLSM; + + /* Combine callback registered and enabled masks */ + uint8_t callback_mask = module->enabled_callback; + callback_mask &= module->registered_callback; + + /* Check if the module should respond to address ack */ + if ((module->buffer_length <= 0) && (module->buffer_remaining > 0)) { + /* Call function for address response */ + _i2c_master_async_address_response(module); + + /* Check if buffer write is done */ + } else if ((module->buffer_length > 0) && (module->buffer_remaining <= 0) && + (module->status == STATUS_BUSY) && + (module->transfer_direction == I2C_TRANSFER_WRITE)) { + /* Stop packet operation */ + i2c_module->INTENCLR.reg = + SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; + + module->buffer_length = 0; + module->status = STATUS_OK; + + if (module->send_stop) { + /* Send stop condition */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } else { + /* Clear write interrupt flag */ + i2c_module->INTFLAG.reg = SERCOM_I2CM_INTFLAG_MB; + } + + if (callback_mask & (1 << I2C_MASTER_CALLBACK_WRITE_COMPLETE)) { + module->callbacks[I2C_MASTER_CALLBACK_WRITE_COMPLETE](module); + } + + /* Continue buffer write/read */ + } else if ((module->buffer_length > 0) && (module->buffer_remaining > 0)){ + /* Check that bus ownership is not lost */ + if ((!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) && + (!(sclsm_flag && (module->buffer_remaining == 1)))) { + module->status = STATUS_ERR_PACKET_COLLISION; + } else if (module->transfer_direction == I2C_TRANSFER_WRITE) { + _i2c_master_write(module); + } else { + _i2c_master_read(module); + } + } + + /* Check if read buffer transfer is complete */ + if ((module->buffer_length > 0) && (module->buffer_remaining <= 0) && + (module->status == STATUS_BUSY) && + (module->transfer_direction == I2C_TRANSFER_READ)) { + + /* Clear read interrupt flag */ + if (i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB) { + i2c_module->INTFLAG.reg = SERCOM_I2CM_INTFLAG_SB; + } + /* Stop packet operation */ + i2c_module->INTENCLR.reg = + SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; + module->buffer_length = 0; + module->status = STATUS_OK; + + /* Call appropriate callback if enabled and registered */ + if ((callback_mask & (1 << I2C_MASTER_CALLBACK_READ_COMPLETE)) + && (module->transfer_direction == I2C_TRANSFER_READ)) { + module->callbacks[I2C_MASTER_CALLBACK_READ_COMPLETE](module); + } else if ((callback_mask & (1 << I2C_MASTER_CALLBACK_WRITE_COMPLETE)) + && (module->transfer_direction == I2C_TRANSFER_WRITE)) { + module->callbacks[I2C_MASTER_CALLBACK_WRITE_COMPLETE](module); + } + } + + /* Check for error */ + if ((module->status != STATUS_BUSY) && (module->status != STATUS_OK)) { + /* Stop packet operation */ + i2c_module->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MB | + SERCOM_I2CM_INTENCLR_SB; + + module->buffer_length = 0; + module->buffer_remaining = 0; + + /* Send nack and stop command unless arbitration is lost */ + if ((module->status != STATUS_ERR_PACKET_COLLISION) && + module->send_stop) { + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | + SERCOM_I2CM_CTRLB_CMD(3); + } + + /* Call error callback if enabled and registered */ + if (callback_mask & (1 << I2C_MASTER_CALLBACK_ERROR)) { + module->callbacks[I2C_MASTER_CALLBACK_ERROR](module); + } + } +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave.c b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave.c new file mode 100644 index 000000000..113087794 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave.c @@ -0,0 +1,745 @@ +/** + * \file + * + * \brief SAM I2C Slave 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 "i2c_slave.h" +#if I2C_SLAVE_CALLBACK_MODE == true +# include "i2c_slave_interrupt.h" +#endif + +/** + * \internal Sets configuration to module + * + * \param[out] module Pointer to software module structure + * \param[in] config Configuration structure with configurations to set + * + * \return Status of setting configuration. + * \retval STATUS_OK Module was configured correctly + * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than + * previously set + */ +static enum status_code _i2c_slave_set_config( + struct i2c_slave_module *const module, + const struct i2c_slave_config *const config) +{ + uint32_t tmp_ctrla; + + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + Assert(config); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + Sercom *const sercom_hw = module->hw; + + module->buffer_timeout = config->buffer_timeout; + module->ten_bit_address = config->ten_bit_address; + + struct system_pinmux_config pin_conf; + system_pinmux_get_config_defaults(&pin_conf); + + uint32_t pad0 = config->pinmux_pad0; + uint32_t pad1 = config->pinmux_pad1; + + /* SERCOM PAD0 - SDA */ + if (pad0 == PINMUX_DEFAULT) { + pad0 = _sercom_get_default_pad(sercom_hw, 0); + } + pin_conf.mux_position = pad0 & 0xFFFF; + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK; + system_pinmux_pin_set_config(pad0 >> 16, &pin_conf); + + /* SERCOM PAD1 - SCL */ + if (pad1 == PINMUX_DEFAULT) { + pad1 = _sercom_get_default_pad(sercom_hw, 1); + } + pin_conf.mux_position = pad1 & 0xFFFF; + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK; + system_pinmux_pin_set_config(pad1 >> 16, &pin_conf); + + /* Prepare config to write to register CTRLA */ + if (config->run_in_standby || system_is_debugger_present()) { + tmp_ctrla = SERCOM_I2CS_CTRLA_RUNSTDBY; + } else { + tmp_ctrla = 0; + } + + /* Check and set SCL clock stretch mode. */ + if (config->scl_stretch_only_after_ack_bit || (config->transfer_speed == I2C_SLAVE_SPEED_HIGH_SPEED)) { + tmp_ctrla |= SERCOM_I2CM_CTRLA_SCLSM; + } + + tmp_ctrla |= ((uint32_t)config->sda_hold_time | + config->transfer_speed | + (config->scl_low_timeout << SERCOM_I2CS_CTRLA_LOWTOUTEN_Pos) | + (config->slave_scl_low_extend_timeout << SERCOM_I2CS_CTRLA_SEXTTOEN_Pos)); + + i2c_hw->CTRLA.reg |= tmp_ctrla; + + /* Set CTRLB configuration */ + i2c_hw->CTRLB.reg = SERCOM_I2CS_CTRLB_SMEN | config->address_mode; + + i2c_hw->ADDR.reg = config->address << SERCOM_I2CS_ADDR_ADDR_Pos | + config->address_mask << SERCOM_I2CS_ADDR_ADDRMASK_Pos | + config->ten_bit_address << SERCOM_I2CS_ADDR_TENBITEN_Pos | + config->enable_general_call_address << SERCOM_I2CS_ADDR_GENCEN_Pos; + + return STATUS_OK; +} + +/** + * \brief Initializes the requested I2C hardware module + * + * Initializes the SERCOM I2C slave device requested and sets the provided + * software module struct. Run this function before any further use of + * the driver. + * + * \param[out] module Pointer to software module struct + * \param[in] hw Pointer to the hardware instance + * \param[in] config Pointer to the configuration struct + * + * \return Status of initialization. + * \retval STATUS_OK Module initiated correctly + * \retval STATUS_ERR_DENIED If module is enabled + * \retval STATUS_BUSY If module is busy resetting + * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than + * previously set + */ +enum status_code i2c_slave_init( + struct i2c_slave_module *const module, + Sercom *const hw, + const struct i2c_slave_config *const config) +{ + /* Sanity check arguments */ + Assert(module); + Assert(hw); + Assert(config); + + /* Initialize software module */ + module->hw = hw; + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Check if module is enabled */ + if (i2c_hw->CTRLA.reg & SERCOM_I2CS_CTRLA_ENABLE) { + return STATUS_ERR_DENIED; + } + + /* Check if reset is in progress */ + if (i2c_hw->CTRLA.reg & SERCOM_I2CS_CTRLA_SWRST) { + return STATUS_BUSY; + } + + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + uint32_t pm_index, gclk_index; +#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30) +#if (SAML21) || (SAMR30) + if (sercom_index == 5) { + pm_index = MCLK_APBDMASK_SERCOM5_Pos; + gclk_index = SERCOM5_GCLK_ID_CORE; + } else { + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + } +#else + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; +#endif +#else + pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; +#endif + + /* Turn on module in PM */ +#if (SAML21) || (SAMR30) + if (sercom_index == 5) { + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBD, 1 << pm_index); + } else { + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); + } +#else + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); +#endif + + /* Set up the GCLK for the module */ + struct system_gclk_chan_config gclk_chan_conf; + system_gclk_chan_get_config_defaults(&gclk_chan_conf); + gclk_chan_conf.source_generator = config->generator_source; + system_gclk_chan_set_config(gclk_index, &gclk_chan_conf); + system_gclk_chan_enable(gclk_index); + sercom_set_gclk_generator(config->generator_source, false); + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Get sercom instance index */ + uint8_t instance_index = _sercom_get_sercom_inst_index(module->hw); + + /* Save software module in interrupt handler */ + _sercom_set_handler(instance_index, _i2c_slave_interrupt_handler); + + /* Save software module */ + _sercom_instances[instance_index] = module; + + /* Initialize values in module */ + module->registered_callback = 0; + module->enabled_callback = 0; + module->buffer_length = 0; + module->nack_on_address = config->enable_nack_on_address; +#endif + + /* Set SERCOM module to operate in I2C slave mode */ + i2c_hw->CTRLA.reg = SERCOM_I2CS_CTRLA_MODE(0x4); + + /* Set config and return status */ + return _i2c_slave_set_config(module, config); +} + +/** + * \brief Resets the hardware module + * + * This will reset the module to hardware defaults. + * + * \param[in,out] module Pointer to software module structure + */ +void i2c_slave_reset( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Reset module instance */ + module->registered_callback = 0; + module->enabled_callback = 0; + module->buffer_length = 0; + module->buffer_remaining = 0; + module->buffer = NULL; +#endif + + /* Disable module */ + i2c_slave_disable(module); + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Clear all pending interrupts */ + system_interrupt_enter_critical_section(); + system_interrupt_clear_pending(_sercom_get_interrupt_vector(module->hw)); + system_interrupt_leave_critical_section(); +#endif + + /* Wait for sync */ + _i2c_slave_wait_for_sync(module); + + /* Reset module */ + i2c_hw->CTRLA.reg = SERCOM_I2CS_CTRLA_SWRST; +} + +/** + * \internal Waits for answer on bus + * + * \param[in] module Pointer to software module structure + * + * \return Status of bus. + * \retval STATUS_OK If given response from slave device + * \retval STATUS_ERR_TIMEOUT If no response was given within specified timeout + * period + */ +static enum status_code _i2c_slave_wait_for_bus( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Wait for reply */ + uint16_t timeout_counter = 0; + while ((!(i2c_module->INTFLAG.reg & SERCOM_I2CS_INTFLAG_DRDY)) && + (!(i2c_module->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC)) && + (!(i2c_module->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH))) { + + /* Check timeout condition */ + if (++timeout_counter >= module->buffer_timeout) { + return STATUS_ERR_TIMEOUT; + } + } + return STATUS_OK; +} + +/** + * \brief Writes a packet to the master + * + * Writes a packet to the master. This will wait for the master to issue + * a request. + * + * \param[in] module Pointer to software module structure + * \param[in] packet Packet to write to master + * + * \return Status of packet write. + * \retval STATUS_OK Packet was written successfully + * \retval STATUS_ERR_DENIED Start condition not received, another + * interrupt flag is set + * \retval STATUS_ERR_IO There was an error in the previous transfer + * \retval STATUS_ERR_BAD_FORMAT Master wants to write data + * \retval STATUS_ERR_INVALID_ARG Invalid argument(s) was provided + * \retval STATUS_ERR_BUSY The I2C module is busy with a job + * \retval STATUS_ERR_ERR_OVERFLOW Master NACKed before entire packet was + * transferred + * \retval STATUS_ERR_TIMEOUT No response was given within the timeout + * period + */ +enum status_code i2c_slave_write_packet_wait( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + Assert(packet); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + uint16_t length = packet->data_length; + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Check if the module is busy with a job or AMATCH is enabled */ + if (module->buffer_remaining > 0 || + (i2c_hw->INTENSET.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + return STATUS_BUSY; + } +#endif + + enum status_code status; + /* Wait for master to send address packet */ + status = _i2c_slave_wait_for_bus(module); + + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + /* Not address interrupt, something is wrong */ + return STATUS_ERR_DENIED; + } + + if (module->ten_bit_address) { + /* ACK the first address */ + i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT; + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); + + /* Wait for address interrupt */ + status = _i2c_slave_wait_for_bus(module); + + if (status != STATUS_OK) { + /* Timeout, return */ + return STATUS_ERR_TIMEOUT; + } + + if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + /* Not address interrupt, something is wrong */ + return STATUS_ERR_DENIED; + } + } + + /* Check if there was an error in last transfer */ + if (i2c_hw->STATUS.reg & (SERCOM_I2CS_STATUS_BUSERR | + SERCOM_I2CS_STATUS_COLL | SERCOM_I2CS_STATUS_LOWTOUT)) { + return STATUS_ERR_IO; + } + + /* Check direction */ + if (!(i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR)) { + /* Write request from master, send NACK and return */ + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT; + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); + return STATUS_ERR_BAD_FORMAT; + } + + /* Read request from master, ACK address */ + i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT; + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); + + uint16_t i = 0; + + /* Wait for data interrupt */ + status = _i2c_slave_wait_for_bus(module); + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + + while (length--) { + /* Write data */ + _i2c_slave_wait_for_sync(module); + i2c_hw->DATA.reg = packet->data[i++]; + + /* Wait for response from master */ + status = _i2c_slave_wait_for_bus(module); + + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + + if (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_RXNACK && + length !=0) { + /* NACK from master, abort */ + /* Release line */ + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x02); + + return STATUS_ERR_OVERFLOW; + } + /* ACK from master, continue writing */ + } + + /* Release line */ + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x02); + + return STATUS_OK; +} + +/** + * \brief Reads a packet from the master + * + * Reads a packet from the master. This will wait for the master to issue a + * request. + * + * \param[in] module Pointer to software module structure + * \param[out] packet Packet to read from master + * + * \return Status of packet read. + * \retval STATUS_OK Packet was read successfully + * \retval STATUS_ABORTED Master sent stop condition or repeated + * start before specified length of bytes + * was received + * \retval STATUS_ERR_IO There was an error in the previous transfer + * \retval STATUS_ERR_DENIED Start condition not received, another + * interrupt flag is set + * \retval STATUS_ERR_INVALID_ARG Invalid argument(s) was provided + * \retval STATUS_ERR_BUSY The I2C module is busy with a job + * \retval STATUS_ERR_BAD_FORMAT Master wants to read data + * \retval STATUS_ERR_ERR_OVERFLOW Last byte received overflows buffer + */ +enum status_code i2c_slave_read_packet_wait( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + Assert(packet); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + uint16_t length = packet->data_length; + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Check if the module is busy with a job or AMATCH is enabled */ + if (module->buffer_remaining > 0 || + (i2c_hw->INTENSET.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + return STATUS_BUSY; + } +#endif + + enum status_code status; + + /* Wait for master to send address packet */ + status = _i2c_slave_wait_for_bus(module); + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + + if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + /* Not address interrupt, something is wrong */ + return STATUS_ERR_DENIED; + } + + /* Check if there was an error in the last transfer */ + if (i2c_hw->STATUS.reg & (SERCOM_I2CS_STATUS_BUSERR | + SERCOM_I2CS_STATUS_COLL | SERCOM_I2CS_STATUS_LOWTOUT)) { + return STATUS_ERR_IO; + } + /* Check direction */ + if ((i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR)) { + /* Read request from master, send NACK and return */ + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT; + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); + return STATUS_ERR_BAD_FORMAT; + } + + /* Write request from master, ACK address */ + i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT; + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); + + uint16_t i = 0; + while (length--) { + + /* Wait for next byte or stop condition */ + status = _i2c_slave_wait_for_bus(module); + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + + if ((i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) || + i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH) { + /* Master sent stop condition, or repeated start, read done */ + /* Clear stop flag */ + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; + return STATUS_ABORTED; + } + + /* Read data */ + _i2c_slave_wait_for_sync(module); + packet->data[i++] = i2c_hw->DATA.reg; + + } + + /* Packet read done, wait for packet to NACK, Stop or repeated start */ + status = _i2c_slave_wait_for_bus(module); + + if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_DRDY) { + /* Buffer is full, send NACK */ + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT; + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2); + } + if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) { + /* Clear stop flag */ + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; + } + return STATUS_OK; +} + +/** + * \brief Waits for a start condition on the bus + * + * \note This function is only available for 7-bit slave addressing. + * + * Waits for the master to issue a start condition on the bus. + * + * \note This function does not check for errors in the last transfer, + * this will be discovered when reading or writing. + * + * \param[in] module Pointer to software module structure + * + * \return Direction of the current transfer, when in slave mode. + * \retval I2C_SLAVE_DIRECTION_NONE No request from master within timeout + * period + * \retval I2C_SLAVE_DIRECTION_READ Write request from master + * \retval I2C_SLAVE_DIRECTION_WRITE Read request from master + */ +enum i2c_slave_direction i2c_slave_get_direction_wait( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + enum status_code status; + + /* Wait for address interrupt */ + status = _i2c_slave_wait_for_bus(module); + + if (status != STATUS_OK) { + /* Timeout, return */ + return I2C_SLAVE_DIRECTION_NONE; + } + + if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + /* Not address interrupt, something is wrong */ + return I2C_SLAVE_DIRECTION_NONE; + } + + /* Check direction */ + if ((i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR)) { + /* Read request from master */ + return I2C_SLAVE_DIRECTION_WRITE; + } else { + /* Write request from master */ + return I2C_SLAVE_DIRECTION_READ; + } +} + +/** + * \brief Retrieves the current module status + * + * Checks the status of the module and returns it as a bitmask of status + * flags. + * + * \param[in] module Pointer to the I2C slave software device struct + * + * \return Bitmask of status flags. + * + * \retval I2C_SLAVE_STATUS_ADDRESS_MATCH A valid address has been received + * \retval I2C_SLAVE_STATUS_DATA_READY A I2C slave byte transmission is + * successfully completed + * \retval I2C_SLAVE_STATUS_STOP_RECEIVED A stop condition is detected for a + * transaction being processed + * \retval I2C_SLAVE_STATUS_CLOCK_HOLD The slave is holding the SCL line + * low + * \retval I2C_SLAVE_STATUS_SCL_LOW_TIMEOUT An SCL low time-out has occurred + * \retval I2C_SLAVE_STATUS_REPEATED_START Indicates a repeated start, only + * valid if \ref + * I2C_SLAVE_STATUS_ADDRESS_MATCH is + * set + * \retval I2C_SLAVE_STATUS_RECEIVED_NACK The last data packet sent was not + * acknowledged + * \retval I2C_SLAVE_STATUS_COLLISION The I2C slave was not able to + * transmit a high data or NACK bit + * \retval I2C_SLAVE_STATUS_BUS_ERROR An illegal bus condition has + * occurred on the bus + */ +uint32_t i2c_slave_get_status( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + uint8_t intflags = i2c_hw->INTFLAG.reg; + uint8_t status = i2c_hw->STATUS.reg; + uint32_t status_flags = 0; + + /* Check Address Match flag */ + if (intflags & SERCOM_I2CS_INTFLAG_AMATCH) { + status_flags |= I2C_SLAVE_STATUS_ADDRESS_MATCH; + } + /* Check Data Ready flag */ + if (intflags & SERCOM_I2CS_INTFLAG_DRDY) { + status_flags |= I2C_SLAVE_STATUS_DATA_READY; + } + /* Check Stop flag */ + if (intflags & SERCOM_I2CS_INTFLAG_PREC) { + status_flags |= I2C_SLAVE_STATUS_STOP_RECEIVED; + } + /* Check Clock Hold */ + if (status & SERCOM_I2CS_STATUS_CLKHOLD) { + status_flags |= I2C_SLAVE_STATUS_CLOCK_HOLD; + } + /* Check SCL Low Timeout */ + if (status & SERCOM_I2CS_STATUS_LOWTOUT) { + status_flags |= I2C_SLAVE_STATUS_SCL_LOW_TIMEOUT; + } + /* Check Repeated Start */ + if (status & SERCOM_I2CS_STATUS_SR) { + status_flags |= I2C_SLAVE_STATUS_REPEATED_START; + } + /* Check Received Not Acknowledge */ + if (status & SERCOM_I2CS_STATUS_RXNACK) { + status_flags |= I2C_SLAVE_STATUS_RECEIVED_NACK; + } + /* Check Transmit Collision */ + if (status & SERCOM_I2CS_STATUS_COLL) { + status_flags |= I2C_SLAVE_STATUS_COLLISION; + } + /* Check Bus Error */ + if (status & SERCOM_I2CS_STATUS_BUSERR) { + status_flags |= I2C_SLAVE_STATUS_BUS_ERROR; + } + + return status_flags; +} + +/** + * \brief Clears a module status flag + * + * Clears the given status flag of the module. + * + * \note Not all status flags can be cleared. + * + * \param[in] module Pointer to the I2C software device struct + * \param[in] status_flags Bit mask of status flags to clear + * + */ +void i2c_slave_clear_status( + struct i2c_slave_module *const module, + uint32_t status_flags) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Clear Address Match flag */ + if (status_flags & I2C_SLAVE_STATUS_ADDRESS_MATCH) { + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_AMATCH; + } + /* Clear Data Ready flag */ + if (status_flags & I2C_SLAVE_STATUS_DATA_READY) { + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_DRDY; + } + /* Clear Stop flag */ + if (status_flags & I2C_SLAVE_STATUS_STOP_RECEIVED) { + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; + } + /* Clear SCL Low Timeout */ + if (status_flags & I2C_SLAVE_STATUS_SCL_LOW_TIMEOUT) { + i2c_hw->STATUS.reg = SERCOM_I2CS_STATUS_LOWTOUT; + } + /* Clear Transmit Collision */ + if (status_flags & I2C_SLAVE_STATUS_COLLISION) { + i2c_hw->STATUS.reg = SERCOM_I2CS_STATUS_COLL; + } + /* Clear Bus Error */ + if (status_flags & I2C_SLAVE_STATUS_BUS_ERROR) { + i2c_hw->STATUS.reg = SERCOM_I2CS_STATUS_BUSERR; + } +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave_interrupt.c b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave_interrupt.c new file mode 100644 index 000000000..0a6f234f8 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave_interrupt.c @@ -0,0 +1,434 @@ +/** + * \file + * + * \brief SAM I2C Slave Interrupt 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 "i2c_slave_interrupt.h" + +/** + * \brief Enables sending of NACK on address match + * + * Enables sending of NACK on address match, thus discarding + * any incoming transaction. + * + * \param[in,out] module Pointer to software module structure + */ +void i2c_slave_enable_nack_on_address( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + + module->nack_on_address = true; +} + +/** + * \brief Disables sending NACK on address match + * + * Disables sending of NACK on address match, thus + * acknowledging incoming transactions. + * + * \param[in,out] module Pointer to software module structure + */ +void i2c_slave_disable_nack_on_address( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + + module->nack_on_address = false; +} + +/** + * \internal + * Reads next data. Used by interrupt handler to get next data byte from master. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_slave_read( + struct i2c_slave_module *const module) +{ + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Read byte from master and put in buffer. */ + *(module->buffer++) = i2c_hw->DATA.reg; + + /*Decrement remaining buffer length */ + module->buffer_remaining--; +} + +/** + * \internal + * Writes next data. Used by interrupt handler to send next data byte to master. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_slave_write( + struct i2c_slave_module *const module) +{ + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Write byte from buffer to master */ + i2c_hw->DATA.reg = *(module->buffer++); + + /*Decrement remaining buffer length */ + module->buffer_remaining--; +} + +/** + * \brief Registers callback for the specified callback type + * + * Associates the given callback function with the + * specified callback type. To enable the callback, the + * \ref i2c_slave_enable_callback function must be used. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback Pointer to the function desired for the + * specified callback + * \param[in] callback_type Callback type to register + */ +void i2c_slave_register_callback( + struct i2c_slave_module *const module, + i2c_slave_callback_t callback, + enum i2c_slave_callback callback_type) +{ + /* Sanity check. */ + Assert(module); + Assert(module->hw); + Assert(callback); + + /* Register callback. */ + module->callbacks[callback_type] = callback; + + /* Set corresponding bit to set callback as initiated. */ + module->registered_callback |= (1 << callback_type); +} + +/** + * \brief Unregisters callback for the specified callback type + * + * Removes the currently registered callback for the given callback + * type. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback_type Callback type to unregister + */ +void i2c_slave_unregister_callback( + struct i2c_slave_module *const module, + enum i2c_slave_callback callback_type) +{ + /* Sanity check. */ + Assert(module); + Assert(module->hw); + + /* Register callback. */ + module->callbacks[callback_type] = NULL; + + /* Set corresponding bit to set callback as initiated. */ + module->registered_callback &= ~(1 << callback_type); +} + +/** + * \brief Initiates a reads packet operation + * + * Reads a data packet from the master. A write request must be initiated by + * the master before the packet can be read. + * + * The \ref I2C_SLAVE_CALLBACK_WRITE_REQUEST callback can be used to call this + * function. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting asynchronously reading I2C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_slave_read_packet_job( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy doing async operation. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Save packet to software module. */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->buffer_length = packet->data_length; + module->status = STATUS_BUSY; + + /* Enable interrupts */ + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + i2c_hw->INTENSET.reg = SERCOM_I2CS_INTFLAG_AMATCH | + SERCOM_I2CS_INTFLAG_DRDY | SERCOM_I2CS_INTFLAG_PREC; + + /* Read will begin when master initiates the transfer */ + return STATUS_OK; +} + +/** + * \brief Initiates a write packet operation + * + * Writes a data packet to the master. A read request must be initiated by + * the master before the packet can be written. + * + * The \ref I2C_SLAVE_CALLBACK_READ_REQUEST callback can be used to call this + * function. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting writing I2C packet. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_slave_write_packet_job( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy doing async operation. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Save packet to software module. */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->buffer_length = packet->data_length; + module->status = STATUS_BUSY; + + /* Enable interrupts */ + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + i2c_hw->INTENSET.reg = SERCOM_I2CS_INTFLAG_AMATCH | + SERCOM_I2CS_INTFLAG_DRDY | SERCOM_I2CS_INTFLAG_PREC; + + return STATUS_OK; +} + +/** + * \internal Interrupt handler for I2C slave + * + * \param[in] instance Sercom instance that triggered the interrupt + */ +void _i2c_slave_interrupt_handler( + uint8_t instance) +{ + /* Get software module for callback handling. */ + struct i2c_slave_module *module = + (struct i2c_slave_module*)_sercom_instances[instance]; + + Assert(module); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Combine callback registered and enabled masks. */ + uint8_t callback_mask = module->enabled_callback; + callback_mask &= module->registered_callback; + + + if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH) { + /* Address match */ + /* Check if last transfer is done - repeated start */ + if (module->buffer_length != module->buffer_remaining && + module->transfer_direction == I2C_TRANSFER_WRITE) { + + module->status = STATUS_OK; + module->buffer_length = 0; + module->buffer_remaining = 0; + + if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_COMPLETE))) { + module->callbacks[I2C_SLAVE_CALLBACK_READ_COMPLETE](module); + } + } else if (module->buffer_length != module->buffer_remaining && + module->transfer_direction == I2C_TRANSFER_READ) { + module->status = STATUS_OK; + module->buffer_length = 0; + module->buffer_remaining = 0; + + if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_COMPLETE))) { + module->callbacks[I2C_SLAVE_CALLBACK_WRITE_COMPLETE](module); + } + } + + if (i2c_hw->STATUS.reg & (SERCOM_I2CS_STATUS_BUSERR | + SERCOM_I2CS_STATUS_COLL | SERCOM_I2CS_STATUS_LOWTOUT)) { + /* An error occurred in last packet transfer */ + module->status = STATUS_ERR_IO; + + if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_ERROR_LAST_TRANSFER))) { + module->callbacks[I2C_SLAVE_CALLBACK_ERROR_LAST_TRANSFER](module); + } + } + if (module->nack_on_address) { + /* NACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, false); + } else if (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR) { + /* Set transfer direction in module instance */ + module->transfer_direction = I2C_TRANSFER_READ; + + /* Read request from master */ + if (callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_REQUEST)) { + module->callbacks[I2C_SLAVE_CALLBACK_READ_REQUEST](module); + } + + if (module->buffer_length == 0) { + /* Data buffer not set up, NACK address, workaround 13574*/ + _i2c_slave_set_ctrlb_ackact(module, false); + } else { + /* ACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, true); + } + } else { + /* Set transfer direction in dev inst */ + module->transfer_direction = I2C_TRANSFER_WRITE; + + /* Write request from master */ + if (callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_REQUEST)) { + module->callbacks[I2C_SLAVE_CALLBACK_WRITE_REQUEST](module); + } + + if (module->buffer_length == 0) { + /* Data buffer not set up, NACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, false); + } else { + /* ACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, true); + } + } + + /* ACK or NACK address, Workaround 13574 */ + _i2c_slave_set_ctrlb_cmd3(module); + + /* ACK next incoming packet, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, true); + + } else if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) { + /* Stop condition on bus - current transfer done */ + + /* Clear Stop interrupt */ + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; + + /* Disable interrupts */ + i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_PREC | SERCOM_I2CS_INTFLAG_DRDY; + + if (!((module->enabled_callback & (1 << I2C_SLAVE_CALLBACK_READ_REQUEST)) + || (module->enabled_callback == (1 << I2C_SLAVE_CALLBACK_WRITE_REQUEST)))) { + /* Disable address match if read/write request is not enabled */ + i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_AMATCH; + } + + if (!(module->status == STATUS_ERR_OVERFLOW || module->status == STATUS_ERR_IO)) { + module->status = STATUS_OK; + module->buffer_length = 0; + module->buffer_remaining = 0; + + /* Call appropriate callback if enabled and registered */ + if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_COMPLETE)) + && (module->transfer_direction == I2C_TRANSFER_WRITE)) { + /* Read from master complete */ + module->callbacks[I2C_SLAVE_CALLBACK_READ_COMPLETE](module); + } else if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_COMPLETE)) + && (module->transfer_direction == I2C_TRANSFER_READ)) { + /* Write to master complete */ + module->callbacks[I2C_SLAVE_CALLBACK_WRITE_COMPLETE](module); + } + } + } else if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_DRDY) { + /* Check if buffer is full, or NACK from master */ + if (module->buffer_remaining <= 0 || + (module->transfer_direction == I2C_TRANSFER_READ && + (module->buffer_length > module->buffer_remaining) && + (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_RXNACK))) { + + module->buffer_remaining = 0; + module->buffer_length = 0; + + if (module->transfer_direction == I2C_TRANSFER_WRITE) { + /* Buffer is full, send NACK, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, false); + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2); + + /* Set status, new character in DATA register will overflow + * buffer */ + module->status = STATUS_ERR_OVERFLOW; + + if (callback_mask & (1 << I2C_SLAVE_CALLBACK_ERROR)) { + /* Read complete */ + module->callbacks[I2C_SLAVE_CALLBACK_ERROR](module); + } + } else { + /* Release SCL and wait for new start condition */ + _i2c_slave_set_ctrlb_ackact(module, false); + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2); + + /* Transfer successful */ + module->status = STATUS_OK; + + /* Disable interrupts */ + i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_DRDY; + } + + /* Continue buffer write/read */ + } else if (module->buffer_length > 0 && module->buffer_remaining > 0) { + /* Call function based on transfer direction */ + if (module->transfer_direction == I2C_TRANSFER_WRITE) { + _i2c_slave_read(module); + } else { + _i2c_slave_write(module); + } + } + } +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_master.c b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_master.c new file mode 100644 index 000000000..ce4f58c23 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_master.c @@ -0,0 +1,881 @@ +/** + * \file + * + * \brief SAM D20 I2C Master Driver + * + * Copyright (C) 2012-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 "i2c_master.h" + +#if I2C_MASTER_CALLBACK_MODE == true +# include "i2c_master_interrupt.h" +#endif + +#if !defined(__DOXYGEN__) + +/** + * \internal Sets configurations to module + * + * \param[out] module Pointer to software module structure + * \param[in] config Configuration structure with configurations to set + * + * \return Status of setting configuration. + * \retval STATUS_OK If module was configured correctly + * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than + * previously set + * \retval STATUS_ERR_BAUDRATE_UNAVAILABLE If given baud rate is not compatible + * with set GCLK frequency + */ +static enum status_code _i2c_master_set_config( + struct i2c_master_module *const module, + const struct i2c_master_config *const config) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + Assert(config); + + /* Temporary variables. */ + uint32_t tmp_ctrla; + int32_t tmp_baud; + enum status_code tmp_status_code = STATUS_OK; + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + Sercom *const sercom_hw = module->hw; + + uint8_t sercom_index = _sercom_get_sercom_inst_index(sercom_hw); + + /* Pin configuration */ + struct system_pinmux_config pin_conf; + system_pinmux_get_config_defaults(&pin_conf); + + uint32_t pad0 = config->pinmux_pad0; + uint32_t pad1 = config->pinmux_pad1; + + /* SERCOM PAD0 - SDA */ + if (pad0 == PINMUX_DEFAULT) { + pad0 = _sercom_get_default_pad(sercom_hw, 0); + } + pin_conf.mux_position = pad0 & 0xFFFF; + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK; + system_pinmux_pin_set_config(pad0 >> 16, &pin_conf); + + /* SERCOM PAD1 - SCL */ + if (pad1 == PINMUX_DEFAULT) { + pad1 = _sercom_get_default_pad(sercom_hw, 1); + } + pin_conf.mux_position = pad1 & 0xFFFF; + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK; + system_pinmux_pin_set_config(pad1 >> 16, &pin_conf); + + /* Save timeout on unknown bus state in software module. */ + module->unknown_bus_state_timeout = config->unknown_bus_state_timeout; + + /* Save timeout on buffer write. */ + module->buffer_timeout = config->buffer_timeout; + + /* Set whether module should run in standby. */ + if (config->run_in_standby || system_is_debugger_present()) { + tmp_ctrla = SERCOM_I2CM_CTRLA_RUNSTDBY; + } else { + tmp_ctrla = 0; + } + + /* Check and set start data hold timeout. */ + if (config->start_hold_time != I2C_MASTER_START_HOLD_TIME_DISABLED) { + tmp_ctrla |= config->start_hold_time; + } + + /* Check and set SCL low timeout. */ + if (config->scl_low_timeout) { + tmp_ctrla |= SERCOM_I2CM_CTRLA_LOWTOUT; + } + + /* Check and set inactive bus timeout. */ + if (config->inactive_timeout != I2C_MASTER_INACTIVE_TIMEOUT_DISABLED) { + tmp_ctrla |= config->inactive_timeout; + } + + /* Write config to register CTRLA. */ + i2c_module->CTRLA.reg |= tmp_ctrla; + + /* Set configurations in CTRLB. */ + i2c_module->CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN; + + /* Find and set baudrate. */ + tmp_baud = (int32_t)(div_ceil( + system_gclk_chan_get_hz(SERCOM0_GCLK_ID_CORE + sercom_index), + (2000*(config->baud_rate))) - 5); + + /* Check that baud rate is supported at current speed. */ + if (tmp_baud > 255 || tmp_baud < 0) { + /* Baud rate not supported. */ + tmp_status_code = STATUS_ERR_BAUDRATE_UNAVAILABLE; + } else { + /* Baud rate acceptable. */ + i2c_module->BAUD.reg = (uint8_t)tmp_baud; + } + + return tmp_status_code; +} +#endif /* __DOXYGEN__ */ + +/** + * \brief Initializes the requested I2C hardware module + * + * Initializes the SERCOM I2C master device requested and sets the provided + * software module struct. Run this function before any further use of + * the driver. + * + * \param[out] module Pointer to software module struct + * \param[in] hw Pointer to the hardware instance + * \param[in] config Pointer to the configuration struct + * + * \return Status of initialization. + * \retval STATUS_OK Module initiated correctly + * \retval STATUS_ERR_DENIED If module is enabled + * \retval STATUS_BUSY If module is busy resetting + * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than + * previously set + * \retval STATUS_ERR_BAUDRATE_UNAVAILABLE If given baudrate is not compatible + * with set GCLK frequency + * + */ +enum status_code i2c_master_init( + struct i2c_master_module *const module, + Sercom *const hw, + const struct i2c_master_config *const config) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(hw); + Assert(config); + + /* Initialize software module */ + module->hw = hw; + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + uint32_t pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos; + uint32_t gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + + /* Turn on module in PM */ + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); + + /* Set up the GCLK for the module */ + struct system_gclk_chan_config gclk_chan_conf; + system_gclk_chan_get_config_defaults(&gclk_chan_conf); + gclk_chan_conf.source_generator = config->generator_source; + system_gclk_chan_set_config(gclk_index, &gclk_chan_conf); + system_gclk_chan_enable(gclk_index); + sercom_set_gclk_generator(config->generator_source, false); + + /* Check if module is enabled. */ + if (i2c_module->CTRLA.reg & SERCOM_I2CM_CTRLA_ENABLE) { + return STATUS_ERR_DENIED; + } + + /* Check if reset is in progress. */ + if (i2c_module->CTRLA.reg & SERCOM_I2CM_CTRLA_SWRST) { + return STATUS_BUSY; + } + +#if I2C_MASTER_CALLBACK_MODE == true + /* Get sercom instance index and register callback. */ + uint8_t instance_index = _sercom_get_sercom_inst_index(module->hw); + _sercom_set_handler(instance_index, _i2c_master_interrupt_handler); + _sercom_instances[instance_index] = module; + + /* Initialize values in module. */ + module->registered_callback = 0; + module->enabled_callback = 0; + module->buffer_length = 0; + module->buffer_remaining = 0; + + module->status = STATUS_OK; + module->buffer = NULL; +#endif + + /* Set sercom module to operate in I2C master mode. */ + i2c_module->CTRLA.reg = SERCOM_I2CM_CTRLA_MODE_I2C_MASTER; + + /* Set config and return status. */ + return _i2c_master_set_config(module, config); +} + +/** + * \brief Resets the hardware module + * + * Reset the module to hardware defaults. + * + * \param[in,out] module Pointer to software module structure + */ +void i2c_master_reset(struct i2c_master_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Wait for sync */ + _i2c_master_wait_for_sync(module); + + /* Disable module */ + i2c_master_disable(module); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Clear all pending interrupts */ + system_interrupt_enter_critical_section(); + system_interrupt_clear_pending(_sercom_get_interrupt_vector(module->hw)); + system_interrupt_leave_critical_section(); +#endif + + /* Wait for sync */ + _i2c_master_wait_for_sync(module); + + /* Reset module */ + i2c_module->CTRLA.reg = SERCOM_I2CM_CTRLA_SWRST; +} + +#if !defined(__DOXYGEN__) +/** + * \internal + * Address response. Called when address is answered or timed out. + * + * \param[in,out] module Pointer to software module structure + * + * \return Status of address response. + * \retval STATUS_OK No error has occurred + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +static enum status_code _i2c_master_address_response( + struct i2c_master_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Check for error and ignore bus-error; workaround for BUSSTATE stuck in + * BUSY */ + if (i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB) { + + /* Clear write interrupt flag */ + i2c_module->INTFLAG.reg = SERCOM_I2CM_INTFLAG_SB; + + /* Check arbitration. */ + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_ARBLOST) { + /* Return packet collision. */ + return STATUS_ERR_PACKET_COLLISION; + } + /* Check that slave responded with ack. */ + } else if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { + /* Slave busy. Issue ack and stop command. */ + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + + /* Return bad address value. */ + return STATUS_ERR_BAD_ADDRESS; + } + + return STATUS_OK; +} + +/** + * \internal + * Waits for answer on bus. + * + * \param[in,out] module Pointer to software module structure + * + * \return Status of bus. + * \retval STATUS_OK If given response from slave device + * \retval STATUS_ERR_TIMEOUT If no response was given within specified timeout + * period + */ +static enum status_code _i2c_master_wait_for_bus( + struct i2c_master_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Wait for reply. */ + uint16_t timeout_counter = 0; + while (!(i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) && + !(i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB)) { + + /* Check timeout condition. */ + if (++timeout_counter >= module->buffer_timeout) { + return STATUS_ERR_TIMEOUT; + } + } + return STATUS_OK; +} +#endif /* __DOXYGEN__ */ + +/** + * \internal + * Starts blocking read operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of reading packet. + * \retval STATUS_OK The packet was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + * + */ +static enum status_code _i2c_master_read_packet( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + Assert(packet); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Return value. */ + enum status_code tmp_status; + uint16_t tmp_data_length = packet->data_length; + + /* Written buffer counter. */ + uint16_t counter = 0; + + /* Set address and direction bit. Will send start command on bus. */ + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_READ; + + /* Wait for response on bus. */ + tmp_status = _i2c_master_wait_for_bus(module); + + /* Set action to ack. */ + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + + /* Check for address response error unless previous error is + * detected. */ + if (tmp_status == STATUS_OK) { + tmp_status = _i2c_master_address_response(module); + } + + /* Check that no error has occurred. */ + if (tmp_status == STATUS_OK) { + /* Read data buffer. */ + while (tmp_data_length--) { + /* Check that bus ownership is not lost. */ + if (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) { + return STATUS_ERR_PACKET_COLLISION; + } + + if (module->send_nack && tmp_data_length == 0) { + /* Set action to NACK */ + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + } else { + /* Save data to buffer. */ + _i2c_master_wait_for_sync(module); + packet->data[counter++] = i2c_module->DATA.reg; + /* Wait for response. */ + tmp_status = _i2c_master_wait_for_bus(module); + } + + /* Check for error. */ + if (tmp_status != STATUS_OK) { + break; + } + } + + if (module->send_stop) { + /* Send stop command unless arbitration is lost. */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } else { + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(1); + } + + /* Save last data to buffer. */ + _i2c_master_wait_for_sync(module); + packet->data[counter] = i2c_module->DATA.reg; + } + + return tmp_status; +} + +/** + * \brief Reads data packet from slave + * + * Reads a data packet from the specified slave address on the I2C + * bus and sends a stop condition when finished. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of reading packet. + * \retval STATUS_OK The packet was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_read_packet_wait( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = true; + module->send_nack = true; + + return _i2c_master_read_packet(module, packet); +} + +/** + * \brief Reads data packet from slave without sending a stop condition when done + * + * Reads a data packet from the specified slave address on the I2C + * bus without sending a stop condition when done, thus retaining ownership of + * the bus when done. To end the transaction, a + * \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition must be + * performed. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of reading packet. + * \retval STATUS_OK The packet was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_read_packet_wait_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = false; + module->send_nack = true; + + return _i2c_master_read_packet(module, packet); +} + +/** + * \internal + * Starts blocking read operation. + * \brief Reads data packet from slave without sending a nack signal and a stop + * condition when done + * + * Reads a data packet from the specified slave address on the I2C + * bus without sending a nack signal and a stop condition when done, + * thus retaining ownership of the bus when done. To end the transaction, a + * \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition must be + * performed. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of reading packet. + * \retval STATUS_OK The packet was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_read_packet_wait_no_nack( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = false; + module->send_nack = false; + + return _i2c_master_read_packet(module, packet); +} + +/** + * \internal + * Starts blocking write operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of write packet. + * \retval STATUS_OK The packet was write successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +static enum status_code _i2c_master_write_packet( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Return value. */ + enum status_code tmp_status; + uint16_t tmp_data_length = packet->data_length; + + _i2c_master_wait_for_sync(module); + + /* Set address and direction bit. Will send start command on bus. */ + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_WRITE; + + /* Wait for response on bus. */ + tmp_status = _i2c_master_wait_for_bus(module); + + /* Check for address response error unless previous error is + * detected. */ + if (tmp_status == STATUS_OK) { + tmp_status = _i2c_master_address_response(module); + } + + /* Check that no error has occurred. */ + if (tmp_status == STATUS_OK) { + /* Buffer counter. */ + uint16_t buffer_counter = 0; + + /* Write data buffer. */ + while (tmp_data_length--) { + /* Check that bus ownership is not lost. */ + if (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) { + return STATUS_ERR_PACKET_COLLISION; + } + + /* Write byte to slave. */ + _i2c_master_wait_for_sync(module); + i2c_module->DATA.reg = packet->data[buffer_counter++]; + + /* Wait for response. */ + tmp_status = _i2c_master_wait_for_bus(module); + + /* Check for error. */ + if (tmp_status != STATUS_OK) { + break; + } + + /* Check for NACK from slave. */ + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { + /* Return bad data value. */ + tmp_status = STATUS_ERR_OVERFLOW; + break; + } + } + + if (module->send_stop) { + /* Stop command */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } + } + + return tmp_status; +} + +/** + * \brief Writes data packet to slave + * + * Writes a data packet to the specified slave address on the I2C bus + * and sends a stop condition when finished. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of write packet. + * \retval STATUS_OK If packet was write successfully + * \retval STATUS_BUSY If master module is busy with a job + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + * \retval STATUS_ERR_TIMEOUT If timeout occurred + * \retval STATUS_ERR_OVERFLOW If slave did not acknowledge last sent + * data, indicating that slave does not + * want more data and was not able to read + * last data sent + */ +enum status_code i2c_master_write_packet_wait( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = true; + module->send_nack = true; + + return _i2c_master_write_packet(module, packet); +} + +/** + * \brief Writes data packet to slave without sending a stop condition when done + * + * Writes a data packet to the specified slave address on the I2C bus + * without sending a stop condition, thus retaining ownership of the bus when + * done. To end the transaction, a \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition or sending a + * stop with the \ref i2c_master_send_stop function must be performed. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of write packet. + * \retval STATUS_OK If packet was write successfully + * \retval STATUS_BUSY If master module is busy + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + * \retval STATUS_ERR_TIMEOUT If timeout occurred + * \retval STATUS_ERR_OVERFLOW If slave did not acknowledge last sent + * data, indicating that slave do not want + * more data + */ +enum status_code i2c_master_write_packet_wait_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = false; + module->send_nack = true; + + return _i2c_master_write_packet(module, packet); +} + +/** + * \brief Sends stop condition on bus + * + * Sends a stop condition on bus. + * + * \note This function can only be used after the + * \ref i2c_master_write_packet_wait_no_stop function. If a stop condition + * is to be sent after a read, the \ref i2c_master_read_packet_wait + * function must be used. + * + * \param[in,out] module Pointer to the software instance struct + */ +void i2c_master_send_stop(struct i2c_master_module *const module) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Send stop command */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); +} + +/** + * \brief Sends nack signal on bus + * + * Sends a nack signal on bus. + * + * \note This function can only be used after the + * \ref i2c_master_write_packet_wait_no_nack function, + * or \ref i2c_master_read_byte function. + * \param[in,out] module Pointer to the software instance struct + */ +void i2c_master_send_nack(struct i2c_master_module *const module) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Send nack signal */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; +} + +/** + * \brief Reads one byte data from slave + * + * \param[in,out] module Pointer to software module struct + * \param[out] byte Read one byte data to slave + * + * \return Status of reading byte. + * \retval STATUS_OK One byte was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_read_byte( + struct i2c_master_module *const module, + uint8_t *byte) +{ + enum status_code tmp_status; + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + /* Write byte to slave. */ + _i2c_master_wait_for_sync(module); + *byte = i2c_module->DATA.reg; + /* Wait for response. */ + tmp_status = _i2c_master_wait_for_bus(module); + + return tmp_status; +} + +/** + * \brief Write one byte data to slave + * + * \param[in,out] module Pointer to software module struct + * \param[in] byte Send one byte data to slave + * + * \return Status of writing byte. + * \retval STATUS_OK One byte was write successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_write_byte( + struct i2c_master_module *const module, + uint8_t byte) +{ + enum status_code tmp_status; + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Write byte to slave. */ + _i2c_master_wait_for_sync(module); + i2c_module->DATA.reg = byte; + /* Wait for response. */ + tmp_status = _i2c_master_wait_for_bus(module); + return tmp_status; +} \ No newline at end of file diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_master_interrupt.c b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_master_interrupt.c new file mode 100644 index 000000000..07c892a03 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_master_interrupt.c @@ -0,0 +1,670 @@ +/** + * \file + * + * \brief SAM D20 I2C Master Interrupt Driver + * + * Copyright (C) 2012-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 "i2c_master_interrupt.h" + +/** + * \internal + * Read next data. Used by interrupt handler to get next data byte from slave. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_master_read( + struct i2c_master_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Find index to save next value in buffer */ + uint16_t buffer_index = module->buffer_length - module->buffer_remaining; + + module->buffer_remaining--; + + if (!module->buffer_remaining) { + /* Send nack */ + if (module->send_nack) + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + if (module->send_stop) { + /* Send stop condition */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } else { + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(1); + } + } else { + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + } + + /* Read byte from slave and put in buffer */ + _i2c_master_wait_for_sync(module); + module->buffer[buffer_index] = i2c_module->DATA.reg; +} + +/** + * \internal + * + * Write next data. Used by interrupt handler to send next data byte to slave. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_master_write(struct i2c_master_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Check for ack from slave */ + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) + { + /* Set status */ + module->status = STATUS_ERR_OVERFLOW; + /* Do not write more data */ + return; + } + + /* Find index to get next byte in buffer */ + uint16_t buffer_index = module->buffer_length - module->buffer_remaining; + + module->buffer_remaining--; + + /* Write byte from buffer to slave */ + _i2c_master_wait_for_sync(module); + i2c_module->DATA.reg = module->buffer[buffer_index]; +} + +/** + * \internal + * Acts on slave address response. Checks for errors concerning master->slave + * handshake. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_master_async_address_response( + struct i2c_master_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Check for error. Ignore bus-error; workaround for bus state stuck in + * BUSY. + */ + if (i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) + { + /* Clear write interrupt flag */ + i2c_module->INTFLAG.reg = SERCOM_I2CM_INTENCLR_MB; + + /* Check arbitration */ + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_ARBLOST) { + /* Return busy */ + module->status = STATUS_ERR_PACKET_COLLISION; + } + } + + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { + /* Return bad address value */ + module->status = STATUS_ERR_BAD_ADDRESS; + module->buffer_remaining = 0; + + if (module->send_stop) { + /* Send stop condition */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } + } + + module->buffer_length = module->buffer_remaining; + + /* Check for status OK. */ + if (module->status == STATUS_BUSY) { + /* Call function based on transfer direction. */ + if (module->transfer_direction == I2C_TRANSFER_WRITE) { + _i2c_master_write(module); + } else { + _i2c_master_read(module); + } + } +} + +/** + * \brief Registers callback for the specified callback type + * + * Associates the given callback function with the + * specified callback type. + * + * To enable the callback, the \ref i2c_master_enable_callback function + * must be used. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback Pointer to the function desired for the + * specified callback + * \param[in] callback_type Callback type to register + */ +void i2c_master_register_callback( + struct i2c_master_module *const module, + const i2c_master_callback_t callback, + enum i2c_master_callback callback_type) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(callback); + + /* Register callback */ + module->callbacks[callback_type] = callback; + + /* Set corresponding bit to set callback as registered */ + module->registered_callback |= (1 << callback_type); +} + +/** + * \brief Unregisters callback for the specified callback type + * + * When called, the currently registered callback for the given callback type + * will be removed. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback_type Specifies the callback type to unregister + */ +void i2c_master_unregister_callback( + struct i2c_master_module *const module, + enum i2c_master_callback callback_type) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + /* Register callback */ + module->callbacks[callback_type] = NULL; + + /* Clear corresponding bit to set callback as unregistered */ + module->registered_callback &= ~(1 << callback_type); +} + +/** + * \internal + * Starts a read bytes operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting reading I2C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_master_read_bytes( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Save packet to software module */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->transfer_direction = I2C_TRANSFER_READ; + module->status = STATUS_BUSY; + module->send_stop = false; + module->send_nack = false; + + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + return STATUS_OK; +} + +/** + * \internal + * Starts a read packet operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting reading I2C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +static enum status_code _i2c_master_read_packet( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Save packet to software module */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->transfer_direction = I2C_TRANSFER_READ; + module->status = STATUS_BUSY; + + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + /* Set address and direction bit. Will send start command on bus */ + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_READ; + + return STATUS_OK; +} + +/** + * \brief Initiates a read packet operation + * + * Reads a data packet from the specified slave address on the I2C + * bus. This is the non-blocking equivalent of \ref i2c_master_read_packet_wait. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting reading I2C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_master_read_packet_job( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Make sure we send STOP */ + module->send_stop = true; + module->send_nack = true; + /* Start reading */ + return _i2c_master_read_packet(module, packet); +} + +/** + * \brief Initiates a read packet operation without sending a STOP condition when done + * + * Reads a data packet from the specified slave address on the I2C bus without + * sending a stop condition, thus retaining ownership of the bus when done. + * To end the transaction, a \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition must be + * performed. + * + * This is the non-blocking equivalent of \ref i2c_master_read_packet_wait_no_stop. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting reading I2C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another operation + */ +enum status_code i2c_master_read_packet_job_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Make sure we don't send STOP */ + module->send_stop = false; + module->send_nack = true; + /* Start reading */ + return _i2c_master_read_packet(module, packet); +} + +/** + * \brief Initiates a read packet operation without sending a NACK signal and a + * STOP condition when done + * + * Reads a data packet from the specified slave address on the I2C bus without + * sending a nack and a stop condition, thus retaining ownership of the bus when done. + * To end the transaction, a \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition must be + * performed. + * + * This is the non-blocking equivalent of \ref i2c_master_read_packet_wait_no_stop. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting reading I2C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another operation + */ +enum status_code i2c_master_read_packet_job_no_nack( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Make sure we don't send STOP */ + module->send_stop = false; + module->send_nack = false; + /* Start reading */ + return _i2c_master_read_packet(module, packet); +} + +/** + * \internal + * Starts a write bytes operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting write I2C bytes. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_master_write_bytes( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Save packet to software module */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->transfer_direction = I2C_TRANSFER_WRITE; + module->status = STATUS_BUSY; + module->send_stop = false; + module->send_nack = false; + + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + return STATUS_OK; +} + +/** + * \internal Initiates a write packet operation + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting writing I2C packet job. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +static enum status_code _i2c_master_write_packet( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Save packet to software module */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->transfer_direction = I2C_TRANSFER_WRITE; + module->status = STATUS_BUSY; + + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + /* Set address and direction bit, will send start command on bus */ + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_WRITE; + + return STATUS_OK; +} + +/** + * \brief Initiates a write packet operation + * + * Writes a data packet to the specified slave address on the I2C + * bus. This is the non-blocking equivalent of \ref i2c_master_write_packet_wait. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting writing I2C packet job. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_master_write_packet_job( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with another job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Make sure we send STOP at end*/ + module->send_stop = true; + module->send_nack = true; + /* Start write operation */ + return _i2c_master_write_packet(module, packet); +} + +/** + * \brief Initiates a write packet operation without sending a STOP condition when done + * + * Writes a data packet to the specified slave address on the I2C bus + * without sending a stop condition, thus retaining ownership of the bus when + * done. To end the transaction, a \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition or sending + * a stop with the \ref i2c_master_send_stop function must be performed. + * + * This is the non-blocking equivalent of \ref i2c_master_write_packet_wait_no_stop. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting writing I2C packet job. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another + */ +enum status_code i2c_master_write_packet_job_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with another job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Do not send stop condition when done */ + module->send_stop = false; + module->send_nack = true; + /* Start write operation */ + return _i2c_master_write_packet(module, packet); +} + +/** + * \internal + * Interrupt handler for I2C master. + * + * \param[in] instance SERCOM instance that triggered the interrupt + */ +void _i2c_master_interrupt_handler( + uint8_t instance) +{ + /* Get software module for callback handling */ + struct i2c_master_module *module = + (struct i2c_master_module*)_sercom_instances[instance]; + + Assert(module); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Combine callback registered and enabled masks */ + uint8_t callback_mask = module->enabled_callback & + module->registered_callback; + + /* Check if the module should respond to address ack */ + if ((module->buffer_length <= 0) && (module->buffer_remaining > 0)) { + /* Call function for address response */ + _i2c_master_async_address_response(module); + + /* Check if buffer write is done */ + } else if ((module->buffer_length > 0) && (module->buffer_remaining <= 0) && + (module->status == STATUS_BUSY) && + (module->transfer_direction == I2C_TRANSFER_WRITE)) { + /* Stop packet operation */ + i2c_module->INTENCLR.reg = + SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; + + module->buffer_length = 0; + module->status = STATUS_OK; + + if (module->send_stop) { + /* Send stop condition */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } else { + /* Clear write interrupt flag */ + i2c_module->INTFLAG.reg = SERCOM_I2CM_INTFLAG_MB; + } + if (callback_mask & (1 << I2C_MASTER_CALLBACK_WRITE_COMPLETE)) { + module->callbacks[I2C_MASTER_CALLBACK_WRITE_COMPLETE](module); + } + + /* Continue buffer write/read */ + } else if ((module->buffer_length > 0) && (module->buffer_remaining > 0)){ + /* Check that bus ownership is not lost */ + if (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) { + module->status = STATUS_ERR_PACKET_COLLISION; + } else if (module->transfer_direction == I2C_TRANSFER_WRITE) { + _i2c_master_write(module); + } else { + _i2c_master_read(module); + } + } + + /* Check if read buffer transfer is complete */ + if ((module->buffer_length > 0) && (module->buffer_remaining <= 0) && + (module->status == STATUS_BUSY) && + (module->transfer_direction == I2C_TRANSFER_READ)) { + + /* Stop packet operation */ + i2c_module->INTENCLR.reg = + SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; + module->buffer_length = 0; + module->status = STATUS_OK; + + /* Call appropriate callback if enabled and registered */ + if ((callback_mask & (1 << I2C_MASTER_CALLBACK_READ_COMPLETE)) + && (module->transfer_direction == I2C_TRANSFER_READ)) { + module->callbacks[I2C_MASTER_CALLBACK_READ_COMPLETE](module); + } else if ((callback_mask & (1 << I2C_MASTER_CALLBACK_WRITE_COMPLETE)) + && (module->transfer_direction == I2C_TRANSFER_WRITE)) { + module->callbacks[I2C_MASTER_CALLBACK_WRITE_COMPLETE](module); + } + } + + /* Check for error */ + if ((module->status != STATUS_BUSY) && (module->status != STATUS_OK)) { + /* Stop packet operation */ + i2c_module->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MB | + SERCOM_I2CM_INTENCLR_SB; + + module->buffer_length = 0; + module->buffer_remaining = 0; + + /* Send nack and stop command unless arbitration is lost */ + if ((module->status != STATUS_ERR_PACKET_COLLISION) && + module->send_stop) { + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | + SERCOM_I2CM_CTRLB_CMD(3); + } + + /* Call error callback if enabled and registered */ + if (callback_mask & (1 << I2C_MASTER_CALLBACK_ERROR)) { + module->callbacks[I2C_MASTER_CALLBACK_ERROR](module); + } + } +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_slave.c b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_slave.c new file mode 100644 index 000000000..c403479a3 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_slave.c @@ -0,0 +1,693 @@ +/** + * \file + * + * \brief SAM D20 I2C Slave 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 "i2c_slave.h" +#if I2C_SLAVE_CALLBACK_MODE == true +# include "i2c_slave_interrupt.h" +#endif + +/** + * \internal Sets configuration to module + * + * \param[out] module Pointer to software module structure + * \param[in] config Configuration structure with configurations to set + * + * \return Status of setting configuration. + * \retval STATUS_OK Module was configured correctly + * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than + * previously set + */ +static enum status_code _i2c_slave_set_config( + struct i2c_slave_module *const module, + const struct i2c_slave_config *const config) +{ + uint32_t tmp_ctrla; + + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + Assert(config); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + Sercom *const sercom_hw = module->hw; + + module->buffer_timeout = config->buffer_timeout; + + struct system_pinmux_config pin_conf; + system_pinmux_get_config_defaults(&pin_conf); + + uint32_t pad0 = config->pinmux_pad0; + uint32_t pad1 = config->pinmux_pad1; + + /* SERCOM PAD0 - SDA */ + if (pad0 == PINMUX_DEFAULT) { + pad0 = _sercom_get_default_pad(sercom_hw, 0); + } + pin_conf.mux_position = pad0 & 0xFFFF; + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK; + system_pinmux_pin_set_config(pad0 >> 16, &pin_conf); + + /* SERCOM PAD1 - SCL */ + if (pad1 == PINMUX_DEFAULT) { + pad1 = _sercom_get_default_pad(sercom_hw, 1); + } + pin_conf.mux_position = pad1 & 0xFFFF; + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK; + system_pinmux_pin_set_config(pad1 >> 16, &pin_conf); + + /* Prepare config to write to register CTRLA */ + if (config->run_in_standby || system_is_debugger_present()) { + tmp_ctrla = SERCOM_I2CS_CTRLA_RUNSTDBY; + } else { + tmp_ctrla = 0; + } + + tmp_ctrla |= config->sda_hold_time | + (config->scl_low_timeout << SERCOM_I2CS_CTRLA_LOWTOUT_Pos); + + i2c_hw->CTRLA.reg |= tmp_ctrla; + + /* Set CTRLB configuration */ + i2c_hw->CTRLB.reg = SERCOM_I2CS_CTRLB_SMEN | config->address_mode; + + i2c_hw->ADDR.reg = config->address << SERCOM_I2CS_ADDR_ADDR_Pos | + config->address_mask << SERCOM_I2CS_ADDR_ADDRMASK_Pos | + config->enable_general_call_address << SERCOM_I2CS_ADDR_GENCEN_Pos; + + return STATUS_OK; +} + +/** + * \brief Initializes the requested I2C hardware module + * + * Initializes the SERCOM I2C slave device requested and sets the provided + * software module struct. Run this function before any further use of + * the driver. + * + * \param[out] module Pointer to software module struct + * \param[in] hw Pointer to the hardware instance + * \param[in] config Pointer to the configuration struct + * + * \return Status of initialization. + * \retval STATUS_OK Module initiated correctly + * \retval STATUS_ERR_DENIED If module is enabled + * \retval STATUS_BUSY If module is busy resetting + * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than + * previously set + */ +enum status_code i2c_slave_init( + struct i2c_slave_module *const module, + Sercom *const hw, + const struct i2c_slave_config *const config) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(hw); + Assert(config); + + /* Initialize software module */ + module->hw = hw; + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Check if module is enabled. */ + if (i2c_hw->CTRLA.reg & SERCOM_I2CS_CTRLA_ENABLE) { + return STATUS_ERR_DENIED; + } + + /* Check if reset is in progress. */ + if (i2c_hw->CTRLA.reg & SERCOM_I2CS_CTRLA_SWRST) { + return STATUS_BUSY; + } + + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + uint32_t pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos; + uint32_t gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + + /* Turn on module in PM */ + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); + + /* Set up the GCLK for the module */ + struct system_gclk_chan_config gclk_chan_conf; + system_gclk_chan_get_config_defaults(&gclk_chan_conf); + gclk_chan_conf.source_generator = config->generator_source; + system_gclk_chan_set_config(gclk_index, &gclk_chan_conf); + system_gclk_chan_enable(gclk_index); + sercom_set_gclk_generator(config->generator_source, false); + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Get sercom instance index. */ + uint8_t instance_index = _sercom_get_sercom_inst_index(module->hw); + + /* Save software module in interrupt handler. */ + _sercom_set_handler(instance_index, _i2c_slave_interrupt_handler); + + /* Save software module. */ + _sercom_instances[instance_index] = module; + + /* Initialize values in module. */ + module->registered_callback = 0; + module->enabled_callback = 0; + module->buffer_length = 0; + module->nack_on_address = config->enable_nack_on_address; +#endif + + /* Set SERCOM module to operate in I2C slave mode. */ + i2c_hw->CTRLA.reg = SERCOM_I2CS_CTRLA_MODE_I2C_SLAVE; + + /* Set config and return status. */ + return _i2c_slave_set_config(module, config); +} + +/** + * \brief Resets the hardware module + * + * This will reset the module to hardware defaults. + * + * \param[in,out] module Pointer to software module structure + */ +void i2c_slave_reset( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Reset module instance. */ + module->registered_callback = 0; + module->enabled_callback = 0; + module->buffer_length = 0; + module->buffer_remaining = 0; + module->buffer = NULL; +#endif + + /* Disable module */ + i2c_slave_disable(module); + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Clear all pending interrupts. */ + system_interrupt_enter_critical_section(); + system_interrupt_clear_pending(_sercom_get_interrupt_vector(module->hw)); + system_interrupt_leave_critical_section(); +#endif + + /* Wait for sync. */ + _i2c_slave_wait_for_sync(module); + + /* Reset module. */ + i2c_hw->CTRLA.reg = SERCOM_I2CS_CTRLA_SWRST; +} + +/** + * \internal Waits for answer on bus + * + * \param[in] module Pointer to software module structure + * + * \return Status of bus. + * \retval STATUS_OK If given response from slave device + * \retval STATUS_ERR_TIMEOUT If no response was given within specified timeout + * period + */ +static enum status_code _i2c_slave_wait_for_bus( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Wait for reply. */ + uint16_t timeout_counter = 0; + while ((!(i2c_module->INTFLAG.reg & SERCOM_I2CS_INTFLAG_DRDY)) && + (!(i2c_module->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC)) && + (!(i2c_module->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH))) { + + /* Check timeout condition. */ + if (++timeout_counter >= module->buffer_timeout) { + return STATUS_ERR_TIMEOUT; + } + } + return STATUS_OK; +} + +/** + * \brief Writes a packet to the master + * + * Writes a packet to the master. This will wait for the master to issue + * a request. + * + * \param[in] module Pointer to software module structure + * \param[in] packet Packet to write to master + * + * \return Status of packet write. + * \retval STATUS_OK Packet was written successfully + * \retval STATUS_ERR_DENIED Start condition not received, another + * interrupt flag is set + * \retval STATUS_ERR_IO There was an error in the previous transfer + * \retval STATUS_ERR_BAD_FORMAT Master wants to write data + * \retval STATUS_ERR_INVALID_ARG Invalid argument(s) was provided + * \retval STATUS_ERR_BUSY The I2C module is busy with a job + * \retval STATUS_ERR_ERR_OVERFLOW Master NACKed before entire packet was + * transferred + * \retval STATUS_ERR_TIMEOUT No response was given within the timeout + * period + */ +enum status_code i2c_slave_write_packet_wait( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + Assert(packet); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + uint16_t length = packet->data_length; + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Check if the module is busy with a job or AMATCH is enabled */ + if (module->buffer_remaining > 0 || + (i2c_hw->INTENSET.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + return STATUS_BUSY; + } +#endif + + enum status_code status; + /* Wait for master to send address packet */ + status = _i2c_slave_wait_for_bus(module); + + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + /* Not address interrupt, something is wrong */ + return STATUS_ERR_DENIED; + } + + /* Check if there was an error in last transfer */ + if (i2c_hw->STATUS.reg & (SERCOM_I2CS_STATUS_BUSERR | + SERCOM_I2CS_STATUS_COLL | SERCOM_I2CS_STATUS_LOWTOUT)) { + return STATUS_ERR_IO; + } + + /* Check direction */ + if (!(i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR)) { + /* Write request from master, send NACK and return, workaround 13574*/ + _i2c_slave_set_ctrlb_ackact(module, false); + _i2c_slave_set_ctrlb_cmd3(module); + return STATUS_ERR_BAD_FORMAT; + } + + /* Read request from master, ACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, true); + _i2c_slave_set_ctrlb_cmd3(module); + + uint16_t i = 0; + + /* Wait for data interrupt */ + status = _i2c_slave_wait_for_bus(module); + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + + while (length--) { + /* Write data */ + _i2c_slave_wait_for_sync(module); + i2c_hw->DATA.reg = packet->data[i++]; + + /* Wait for response from master */ + status = _i2c_slave_wait_for_bus(module); + + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + + if (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_RXNACK && + length !=0) { + /* NACK from master, abort */ + /* Release line */ + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x02); + + return STATUS_ERR_OVERFLOW; + /* Workaround: PIF will probably not be set, ignore */ + } + /* ACK from master, continue writing */ + } + + /* Release line */ + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x02); + + return STATUS_OK; +} + +/** + * \brief Reads a packet from the master + * + * Reads a packet from the master. This will wait for the master to issue a + * request. + * + * \param[in] module Pointer to software module structure + * \param[out] packet Packet to read from master + * + * \return Status of packet read. + * \retval STATUS_OK Packet was read successfully + * \retval STATUS_ABORTED Master sent stop condition or repeated + * start before specified length of bytes + * was received + * \retval STATUS_ERR_IO There was an error in the previous transfer + * \retval STATUS_ERR_DENIED Start condition not received, another + * interrupt flag is set + * \retval STATUS_ERR_INVALID_ARG Invalid argument(s) was provided + * \retval STATUS_ERR_BUSY The I2C module is busy with a job + * \retval STATUS_ERR_BAD_FORMAT Master wants to read data + * \retval STATUS_ERR_ERR_OVERFLOW Last byte received overflows buffer + */ +enum status_code i2c_slave_read_packet_wait( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + Assert(packet); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + uint16_t length = packet->data_length; + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Check if the module is busy with a job or AMATCH is enabled */ + if (module->buffer_remaining > 0 || + (i2c_hw->INTENSET.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + return STATUS_BUSY; + } +#endif + + enum status_code status; + + /* Wait for master to send address packet */ + status = _i2c_slave_wait_for_bus(module); + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + + if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + /* Not address interrupt, something is wrong */ + return STATUS_ERR_DENIED; + } + + /* Check if there was an error in the last transfer */ + if (i2c_hw->STATUS.reg & (SERCOM_I2CS_STATUS_BUSERR | + SERCOM_I2CS_STATUS_COLL | SERCOM_I2CS_STATUS_LOWTOUT)) { + return STATUS_ERR_IO; + } + /* Check direction */ + if ((i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR)) { + /* Read request from master, send NACK and return, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, false); + _i2c_slave_set_ctrlb_cmd3(module); + return STATUS_ERR_BAD_FORMAT; + } + + /* Write request from master, ACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, true); + _i2c_slave_set_ctrlb_cmd3(module); + + uint16_t i = 0; + while (length--) { + + /* Wait for next byte or stop condition */ + status = _i2c_slave_wait_for_bus(module); + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + + if ((i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) || + i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH) { + /* Master sent stop condition, or repeated start, read done */ + /* Clear stop flag */ + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; + return STATUS_ABORTED; + } + + /* Read data */ + _i2c_slave_wait_for_sync(module); + packet->data[i++] = i2c_hw->DATA.reg; + + } + + /* Packet read done, wait for packet to NACK, Stop or repeated start */ + status = _i2c_slave_wait_for_bus(module); + + if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_DRDY) { + /* Buffer is full, send NACK */ + _i2c_slave_set_ctrlb_ackact(module, false); + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2); + } + if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) { + /* Clear stop flag */ + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; + } + return STATUS_OK; +} + +/** + * \brief Waits for a start condition on the bus + * + * \note This function is only available for 7-bit slave addressing. + * + * Waits for the master to issue a start condition on the bus. + * \note This function does not check for errors in the last transfer, + * this will be discovered when reading or writing. + * + * \param[in] module Pointer to software module structure + * + * \return Direction of the current transfer, when in slave mode. + * \retval I2C_SLAVE_DIRECTION_NONE No request from master within timeout + * period + * \retval I2C_SLAVE_DIRECTION_READ Write request from master + * \retval I2C_SLAVE_DIRECTION_WRITE Read request from master + */ +enum i2c_slave_direction i2c_slave_get_direction_wait( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + enum status_code status; + + /* Wait for address interrupt */ + status = _i2c_slave_wait_for_bus(module); + + if (status != STATUS_OK) { + /* Timeout, return */ + return I2C_SLAVE_DIRECTION_NONE; + } + + if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + /* Not address interrupt, something is wrong */ + return I2C_SLAVE_DIRECTION_NONE; + } + + /* Check direction */ + if ((i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR)) { + /* Read request from master */ + return I2C_SLAVE_DIRECTION_WRITE; + } else { + /* Write request from master */ + return I2C_SLAVE_DIRECTION_READ; + } +} + +/** + * \brief Retrieves the current module status + * + * Checks the status of the module and returns it as a bitmask of status + * flags. + * + * \param[in] module Pointer to the I2C slave software device struct + * + * \return Bitmask of status flags. + * + * \retval I2C_SLAVE_STATUS_ADDRESS_MATCH A valid address has been received + * \retval I2C_SLAVE_STATUS_DATA_READY A I2C slave byte transmission is + * successfully completed + * \retval I2C_SLAVE_STATUS_STOP_RECEIVED A stop condition is detected for a + * transaction being processed + * \retval I2C_SLAVE_STATUS_CLOCK_HOLD The slave is holding the SCL line + * low + * \retval I2C_SLAVE_STATUS_SCL_LOW_TIMEOUT An SCL low time-out has occurred + * \retval I2C_SLAVE_STATUS_REPEATED_START Indicates a repeated start, only + * valid if \ref + * I2C_SLAVE_STATUS_ADDRESS_MATCH is + * set + * \retval I2C_SLAVE_STATUS_RECEIVED_NACK The last data packet sent was not + * acknowledged + * \retval I2C_SLAVE_STATUS_COLLISION The I2C slave was not able to + * transmit a high data or NACK bit + * \retval I2C_SLAVE_STATUS_BUS_ERROR An illegal bus condition has + * occurred on the bus + */ +uint32_t i2c_slave_get_status( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + uint8_t intflags = i2c_hw->INTFLAG.reg; + uint8_t status = i2c_hw->STATUS.reg; + uint32_t status_flags = 0; + + /* Check Address Match flag */ + if (intflags & SERCOM_I2CS_INTFLAG_AMATCH) { + status_flags |= I2C_SLAVE_STATUS_ADDRESS_MATCH; + } + /* Check Data Ready flag */ + if (intflags & SERCOM_I2CS_INTFLAG_DRDY) { + status_flags |= I2C_SLAVE_STATUS_DATA_READY; + } + /* Check Stop flag */ + if (intflags & SERCOM_I2CS_INTFLAG_PREC) { + status_flags |= I2C_SLAVE_STATUS_STOP_RECEIVED; + } + /* Check Clock Hold */ + if (status & SERCOM_I2CS_STATUS_CLKHOLD) { + status_flags |= I2C_SLAVE_STATUS_CLOCK_HOLD; + } + /* Check SCL Low Timeout */ + if (status & SERCOM_I2CS_STATUS_LOWTOUT) { + status_flags |= I2C_SLAVE_STATUS_SCL_LOW_TIMEOUT; + } + /* Check Repeated Start */ + if (status & SERCOM_I2CS_STATUS_SR) { + status_flags |= I2C_SLAVE_STATUS_REPEATED_START; + } + /* Check Received Not Acknowledge */ + if (status & SERCOM_I2CS_STATUS_RXNACK) { + status_flags |= I2C_SLAVE_STATUS_RECEIVED_NACK; + } + /* Check Transmit Collision */ + if (status & SERCOM_I2CS_STATUS_COLL) { + status_flags |= I2C_SLAVE_STATUS_COLLISION; + } + /* Check Bus Error */ + if (status & SERCOM_I2CS_STATUS_BUSERR) { + status_flags |= I2C_SLAVE_STATUS_BUS_ERROR; + } + + return status_flags; +} + +/** + * \brief Clears a module status flag + * + * Clears the given status flag of the module. + * + * \note Not all status flags can be cleared. + * + * \param[in] module Pointer to the I2C software device struct + * \param[in] status_flags Bit mask of status flags to clear + * + */ +void i2c_slave_clear_status( + struct i2c_slave_module *const module, + uint32_t status_flags) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Clear Address Match flag */ + if (status_flags & I2C_SLAVE_STATUS_ADDRESS_MATCH) { + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_AMATCH; + } + /* Clear Data Ready flag */ + if (status_flags & I2C_SLAVE_STATUS_DATA_READY) { + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_DRDY; + } + /* Clear Stop flag */ + if (status_flags & I2C_SLAVE_STATUS_STOP_RECEIVED) { + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; + } + /* Clear SCL Low Timeout */ + if (status_flags & I2C_SLAVE_STATUS_SCL_LOW_TIMEOUT) { + i2c_hw->STATUS.reg = SERCOM_I2CS_STATUS_LOWTOUT; + } + /* Clear Transmit Collision */ + if (status_flags & I2C_SLAVE_STATUS_COLLISION) { + i2c_hw->STATUS.reg = SERCOM_I2CS_STATUS_COLL; + } + /* Clear Bus Error */ + if (status_flags & I2C_SLAVE_STATUS_BUS_ERROR) { + i2c_hw->STATUS.reg = SERCOM_I2CS_STATUS_BUSERR; + } +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_slave_interrupt.c b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_slave_interrupt.c new file mode 100644 index 000000000..29d222bd2 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_slave_interrupt.c @@ -0,0 +1,434 @@ +/** + * \file + * + * \brief SAM D20 I2C Slave Interrupt 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 "i2c_slave_interrupt.h" + +/** + * \brief Enables sending of NACK on address match + * + * Enables sending of NACK on address match, thus discarding + * any incoming transaction. + * + * \param[in,out] module Pointer to software module structure + */ +void i2c_slave_enable_nack_on_address( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + + module->nack_on_address = true; +} + +/** + * \brief Disables sending NACK on address match + * + * Disables sending of NACK on address match, thus + * acknowledging incoming transactions. + * + * \param[in,out] module Pointer to software module structure + */ +void i2c_slave_disable_nack_on_address( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + + module->nack_on_address = false; +} + +/** + * \internal + * Reads next data. Used by interrupt handler to get next data byte from master. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_slave_read( + struct i2c_slave_module *const module) +{ + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Read byte from master and put in buffer. */ + *(module->buffer++) = i2c_hw->DATA.reg; + + /*Decrement remaining buffer length */ + module->buffer_remaining--; +} + +/** + * \internal + * Writes next data. Used by interrupt handler to send next data byte to master. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_slave_write( + struct i2c_slave_module *const module) +{ + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Write byte from buffer to master */ + i2c_hw->DATA.reg = *(module->buffer++); + + /*Decrement remaining buffer length */ + module->buffer_remaining--; +} + +/** + * \brief Registers callback for the specified callback type + * + * Associates the given callback function with the + * specified callback type. To enable the callback, the + * \ref i2c_slave_enable_callback function must be used. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback Pointer to the function desired for the + * specified callback + * \param[in] callback_type Callback type to register + */ +void i2c_slave_register_callback( + struct i2c_slave_module *const module, + i2c_slave_callback_t callback, + enum i2c_slave_callback callback_type) +{ + /* Sanity check. */ + Assert(module); + Assert(module->hw); + Assert(callback); + + /* Register callback. */ + module->callbacks[callback_type] = callback; + + /* Set corresponding bit to set callback as initiated. */ + module->registered_callback |= (1 << callback_type); +} + +/** + * \brief Unregisters callback for the specified callback type + * + * Removes the currently registered callback for the given callback + * type. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback_type Callback type to unregister + */ +void i2c_slave_unregister_callback( + struct i2c_slave_module *const module, + enum i2c_slave_callback callback_type) +{ + /* Sanity check. */ + Assert(module); + Assert(module->hw); + + /* Register callback. */ + module->callbacks[callback_type] = NULL; + + /* Set corresponding bit to set callback as initiated. */ + module->registered_callback &= ~(1 << callback_type); +} + +/** + * \brief Initiates a reads packet operation + * + * Reads a data packet from the master. A write request must be initiated by + * the master before the packet can be read. + * + * The \ref I2C_SLAVE_CALLBACK_WRITE_REQUEST callback can be used to call this + * function. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting asynchronously reading I2C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_slave_read_packet_job( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy doing async operation. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Save packet to software module. */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->buffer_length = packet->data_length; + module->status = STATUS_BUSY; + + /* Enable interrupts */ + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + i2c_hw->INTENSET.reg = SERCOM_I2CS_INTFLAG_AMATCH | + SERCOM_I2CS_INTFLAG_DRDY | SERCOM_I2CS_INTFLAG_PREC; + + /* Read will begin when master initiates the transfer */ + return STATUS_OK; +} + +/** + * \brief Initiates a write packet operation + * + * Writes a data packet to the master. A read request must be initiated by + * the master before the packet can be written. + * + * The \ref I2C_SLAVE_CALLBACK_READ_REQUEST callback can be used to call this + * function. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting writing I2C packet. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_slave_write_packet_job( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy doing async operation. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Save packet to software module. */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->buffer_length = packet->data_length; + module->status = STATUS_BUSY; + + /* Enable interrupts */ + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + i2c_hw->INTENSET.reg = SERCOM_I2CS_INTFLAG_AMATCH | + SERCOM_I2CS_INTFLAG_DRDY | SERCOM_I2CS_INTFLAG_PREC; + + return STATUS_OK; +} + +/** + * \internal Interrupt handler for I2C slave + * + * \param[in] instance Sercom instance that triggered the interrupt + */ +void _i2c_slave_interrupt_handler( + uint8_t instance) +{ + /* Get software module for callback handling. */ + struct i2c_slave_module *module = + (struct i2c_slave_module*)_sercom_instances[instance]; + + Assert(module); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Combine callback registered and enabled masks. */ + uint8_t callback_mask = + module->enabled_callback & module->registered_callback; + + + if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH) { + /* Address match */ + /* Check if last transfer is done - repeated start */ + if (module->buffer_length != module->buffer_remaining && + module->transfer_direction == I2C_TRANSFER_WRITE) { + + module->status = STATUS_OK; + module->buffer_length = 0; + module->buffer_remaining = 0; + + if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_COMPLETE))) { + module->callbacks[I2C_SLAVE_CALLBACK_READ_COMPLETE](module); + } + } else if (module->buffer_length != module->buffer_remaining && + module->transfer_direction == I2C_TRANSFER_READ) { + module->status = STATUS_OK; + module->buffer_length = 0; + module->buffer_remaining = 0; + + if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_COMPLETE))) { + module->callbacks[I2C_SLAVE_CALLBACK_WRITE_COMPLETE](module); + } + } + + if (i2c_hw->STATUS.reg & (SERCOM_I2CS_STATUS_BUSERR | + SERCOM_I2CS_STATUS_COLL | SERCOM_I2CS_STATUS_LOWTOUT)) { + /* An error occurred in last packet transfer */ + module->status = STATUS_ERR_IO; + + if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_ERROR_LAST_TRANSFER))) { + module->callbacks[I2C_SLAVE_CALLBACK_ERROR_LAST_TRANSFER](module); + } + } + if (module->nack_on_address) { + /* NACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, false); + } else if (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR) { + /* Set transfer direction in module instance */ + module->transfer_direction = I2C_TRANSFER_READ; + + /* Read request from master */ + if (callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_REQUEST)) { + module->callbacks[I2C_SLAVE_CALLBACK_READ_REQUEST](module); + } + + if (module->buffer_length == 0) { + /* Data buffer not set up, NACK address, workaround 13574*/ + _i2c_slave_set_ctrlb_ackact(module, false); + } else { + /* ACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, true); + } + } else { + /* Set transfer direction in dev inst */ + module->transfer_direction = I2C_TRANSFER_WRITE; + + /* Write request from master */ + if (callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_REQUEST)) { + module->callbacks[I2C_SLAVE_CALLBACK_WRITE_REQUEST](module); + } + + if (module->buffer_length == 0) { + /* Data buffer not set up, NACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, false); + } else { + /* ACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, true); + } + } + + /* ACK or NACK address, Workaround 13574 */ + _i2c_slave_set_ctrlb_cmd3(module); + + /* ACK next incoming packet, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, true); + + } else if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) { + /* Stop condition on bus - current transfer done */ + + /* Clear Stop interrupt */ + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; + + /* Disable interrupts */ + i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_PREC | SERCOM_I2CS_INTFLAG_DRDY; + + if (!((module->enabled_callback & (1 << I2C_SLAVE_CALLBACK_READ_REQUEST)) + || (module->enabled_callback == (1 << I2C_SLAVE_CALLBACK_WRITE_REQUEST)))) { + /* Disable address match if read/write request is not enabled */ + i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_AMATCH; + } + + if (!(module->status == STATUS_ERR_OVERFLOW || module->status == STATUS_ERR_IO)) { + module->status = STATUS_OK; + module->buffer_length = 0; + module->buffer_remaining = 0; + + /* Call appropriate callback if enabled and registered */ + if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_COMPLETE)) + && (module->transfer_direction == I2C_TRANSFER_WRITE)) { + /* Read from master complete */ + module->callbacks[I2C_SLAVE_CALLBACK_READ_COMPLETE](module); + } else if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_COMPLETE)) + && (module->transfer_direction == I2C_TRANSFER_READ)) { + /* Write to master complete */ + module->callbacks[I2C_SLAVE_CALLBACK_WRITE_COMPLETE](module); + } + } + } else if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_DRDY) { + /* Check if buffer is full, or NACK from master */ + if (module->buffer_remaining <= 0 || + (module->transfer_direction == I2C_TRANSFER_READ && + (module->buffer_length > module->buffer_remaining) && + (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_RXNACK))) { + + module->buffer_remaining = 0; + module->buffer_length = 0; + + if (module->transfer_direction == I2C_TRANSFER_WRITE) { + /* Buffer is full, send NACK, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, false); + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2); + + /* Set status, new character in DATA register will overflow + * buffer */ + module->status = STATUS_ERR_OVERFLOW; + + if (callback_mask & (1 << I2C_SLAVE_CALLBACK_ERROR)) { + /* Read complete */ + module->callbacks[I2C_SLAVE_CALLBACK_ERROR](module); + } + } else { + /* Release SCL and wait for new start condition */ + _i2c_slave_set_ctrlb_ackact(module, false); + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2); + + /* Transfer successful */ + module->status = STATUS_OK; + + /* Disable interrupts */ + i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_DRDY; + } + + /* Continue buffer write/read */ + } else if (module->buffer_length > 0 && module->buffer_remaining > 0) { + /* Call function based on transfer direction */ + if (module->transfer_direction == I2C_TRANSFER_WRITE) { + _i2c_slave_read(module); + } else { + _i2c_slave_write(module); + } + } + } +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_slave.h b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_slave.h new file mode 100644 index 000000000..20eb78ec0 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_slave.h @@ -0,0 +1,753 @@ +/** + * \file + * + * \brief SAM SERCOM I2C Slave 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 I2C_SLAVE_H_INCLUDED +#define I2C_SLAVE_H_INCLUDED + +#include "i2c_common.h" +#include +#include + +#if I2C_SLAVE_CALLBACK_MODE == true +# include +#endif + +#ifndef PINMUX_DEFAULT +# define PINMUX_DEFAULT 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup asfdoc_sam0_sercom_i2c_group + * + * @{ + * + */ + +/** + * \name I2C Slave Status Flags + * + * I2C slave status flags, returned by \ref i2c_slave_get_status() and cleared + * by \ref i2c_slave_clear_status(). + * @{ + */ + +/** Address Match. + * \note Should only be cleared internally by driver. + */ +#define I2C_SLAVE_STATUS_ADDRESS_MATCH (1UL << 0) +/** Data Ready. */ +#define I2C_SLAVE_STATUS_DATA_READY (1UL << 1) +/** Stop Received. */ +#define I2C_SLAVE_STATUS_STOP_RECEIVED (1UL << 2) +/** Clock Hold. + * \note Cannot be cleared, only valid when I2C_SLAVE_STATUS_ADDRESS_MATCH is + * set. + */ +#define I2C_SLAVE_STATUS_CLOCK_HOLD (1UL << 3) +/** SCL Low Timeout. */ +#define I2C_SLAVE_STATUS_SCL_LOW_TIMEOUT (1UL << 4) +/** Repeated Start. + * \note Cannot be cleared, only valid when I2C_SLAVE_STATUS_ADDRESS_MATCH is + * set. + */ +#define I2C_SLAVE_STATUS_REPEATED_START (1UL << 5) +/** Received not acknowledge. + * \note Cannot be cleared. + */ +#define I2C_SLAVE_STATUS_RECEIVED_NACK (1UL << 6) +/** Transmit Collision. */ +#define I2C_SLAVE_STATUS_COLLISION (1UL << 7) +/** Bus error. */ +#define I2C_SLAVE_STATUS_BUS_ERROR (1UL << 8) + +/** @} */ + +/** + * \brief I2C slave packet for read/write + * + * Structure to be used when transferring I2C slave packets. + */ +struct i2c_slave_packet { + /** Length of data array */ + uint16_t data_length; + /** Data array containing all data to be transferred */ + uint8_t *data; +}; + +#if I2C_SLAVE_CALLBACK_MODE == true + /** + * \brief Callback types + * + * The available callback types for the I2C slave. + */ +enum i2c_slave_callback { + /** Callback for packet write complete */ + I2C_SLAVE_CALLBACK_WRITE_COMPLETE, + /** Callback for packet read complete */ + I2C_SLAVE_CALLBACK_READ_COMPLETE, + /** + * Callback for read request from master - can be used to + * issue a write + */ + I2C_SLAVE_CALLBACK_READ_REQUEST, + /** + * Callback for write request from master - can be used to issue a read + */ + I2C_SLAVE_CALLBACK_WRITE_REQUEST, + /** Callback for error */ + I2C_SLAVE_CALLBACK_ERROR, + /** + * Callback for error in last transfer. Discovered on a new address + * interrupt. + */ + I2C_SLAVE_CALLBACK_ERROR_LAST_TRANSFER, +# if !defined(__DOXYGEN__) + /** Total number of callbacks */ + _I2C_SLAVE_CALLBACK_N, +# endif +}; + +# if !defined(__DOXYGEN__) +/** Software module prototype */ +struct i2c_slave_module; + +/** Callback type */ +typedef void (*i2c_slave_callback_t)( + struct i2c_slave_module *const module); +# endif +#endif + +/** + * \brief Enum for the possible SDA hold times with respect to the negative + * edge of SCL + * + * Enum for the possible SDA hold times with respect to the negative edge + * of SCL. + */ +enum i2c_slave_sda_hold_time { + /** SDA hold time disabled */ + I2C_SLAVE_SDA_HOLD_TIME_DISABLED = + ((SERCOM_I2CS_CTRLA_SDAHOLD_Msk & ((0) << SERCOM_I2CS_CTRLA_SDAHOLD_Pos))), + /** SDA hold time 50ns - 100ns */ + I2C_SLAVE_SDA_HOLD_TIME_50NS_100NS = + ((SERCOM_I2CS_CTRLA_SDAHOLD_Msk & ((1) << SERCOM_I2CS_CTRLA_SDAHOLD_Pos))), + /** SDA hold time 300ns - 600ns */ + I2C_SLAVE_SDA_HOLD_TIME_300NS_600NS = + ((SERCOM_I2CS_CTRLA_SDAHOLD_Msk & ((2) << SERCOM_I2CS_CTRLA_SDAHOLD_Pos))), + /** SDA hold time 400ns - 800ns */ + I2C_SLAVE_SDA_HOLD_TIME_400NS_800NS = + ((SERCOM_I2CS_CTRLA_SDAHOLD_Msk & ((3) << SERCOM_I2CS_CTRLA_SDAHOLD_Pos))), +}; + +/** + * \brief Enum for the possible address modes + * + * Enum for the possible address modes. + */ +enum i2c_slave_address_mode { + /** Address match on address_mask used as a mask to address */ + I2C_SLAVE_ADDRESS_MODE_MASK = SERCOM_I2CS_CTRLB_AMODE(0), + /** Address math on both address and address_mask */ + I2C_SLAVE_ADDRESS_MODE_TWO_ADDRESSES = SERCOM_I2CS_CTRLB_AMODE(1), + /** + * Address match on range of addresses between and including address and + * address_mask + */ + I2C_SLAVE_ADDRESS_MODE_RANGE = SERCOM_I2CS_CTRLB_AMODE(2), +}; + +/** + * \brief Enum for the direction of a request + * + * Enum for the direction of a request. + */ +enum i2c_slave_direction { + /** Read */ + I2C_SLAVE_DIRECTION_READ, + /** Write */ + I2C_SLAVE_DIRECTION_WRITE, + /** No direction */ + I2C_SLAVE_DIRECTION_NONE, +}; + +#ifdef FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED +/** + * \brief Enum for the transfer speed + * + * Enum for the transfer speed. + */ +enum i2c_slave_transfer_speed { + /** Standard-mode (Sm) up to 100KHz and Fast-mode (Fm) up to 400KHz */ + I2C_SLAVE_SPEED_STANDARD_AND_FAST = SERCOM_I2CS_CTRLA_SPEED(0), + /** Fast-mode Plus (Fm+) up to 1MHz */ + I2C_SLAVE_SPEED_FAST_MODE_PLUS = SERCOM_I2CS_CTRLA_SPEED(1), + /** High-speed mode (Hs-mode) up to 3.4MHz */ + I2C_SLAVE_SPEED_HIGH_SPEED = SERCOM_I2CS_CTRLA_SPEED(2), +}; +#endif + +/** + * \brief SERCOM I2C slave driver software device instance structure. + * + * SERCOM I2C slave driver 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 i2c_slave_module { +#if !defined(__DOXYGEN__) + /** Hardware instance initialized for the struct */ + Sercom *hw; + /** Module lock */ + volatile bool locked; + /** Timeout value for polled functions */ + uint16_t buffer_timeout; +# ifdef FEATURE_I2C_10_BIT_ADDRESS + /** Using 10-bit addressing for the slave */ + bool ten_bit_address; +# endif +# if I2C_SLAVE_CALLBACK_MODE == true + /** Nack on address match */ + bool nack_on_address; + /** Pointers to callback functions */ + volatile i2c_slave_callback_t callbacks[_I2C_SLAVE_CALLBACK_N]; + /** Mask for registered callbacks */ + volatile uint8_t registered_callback; + /** Mask for enabled callbacks */ + volatile uint8_t enabled_callback; + /** The total number of bytes to transfer */ + volatile uint16_t buffer_length; + /** + * Counter used for bytes left to send in write and to count number of + * obtained bytes in read + */ + uint16_t buffer_remaining; + /** Data buffer for packet write and read */ + volatile uint8_t *buffer; + /** Save direction of request from master. 1 = read, 0 = write. */ + volatile enum i2c_transfer_direction transfer_direction; + /** Status for status read back in error callback */ + volatile enum status_code status; +# endif +#endif +}; + +/** + * \brief Configuration structure for the I2C slave device + * + * This is the configuration structure for the I2C slave device. It is used + * as an argument for \ref i2c_slave_init to provide the desired + * configurations for the module. The structure should be initialized using the + * \ref i2c_slave_get_config_defaults. + */ +struct i2c_slave_config { + /** Set to enable the SCL low timeout */ + bool enable_scl_low_timeout; + /** SDA hold time with respect to the negative edge of SCL */ + enum i2c_slave_sda_hold_time sda_hold_time; + /** Timeout to wait for master in polled functions */ + uint16_t buffer_timeout; + /** Addressing mode */ + enum i2c_slave_address_mode address_mode; + /** Address or upper limit of address range */ + uint16_t address; + /** Address mask, second address, or lower limit of address range */ + uint16_t address_mask; +#ifdef FEATURE_I2C_10_BIT_ADDRESS + /** Enable 10-bit addressing */ + bool ten_bit_address; +#endif + /** + * Enable general call address recognition (general call address + * is defined as 0000000 with direction bit 0). + */ + bool enable_general_call_address; + +#ifdef FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED + /** Transfer speed mode */ + enum i2c_slave_transfer_speed transfer_speed; +#endif + +#if I2C_SLAVE_CALLBACK_MODE == true + /** + * Enable NACK on address match (this can be changed after initialization + * via the \ref i2c_slave_enable_nack_on_address and + * \ref i2c_slave_disable_nack_on_address functions). + */ + bool enable_nack_on_address; +#endif + /** GCLK generator to use as clock source */ + enum gclk_generator generator_source; + /** Set to keep module active in sleep modes */ + bool run_in_standby; + /** PAD0 (SDA) pinmux */ + uint32_t pinmux_pad0; + /** PAD1 (SCL) pinmux */ + uint32_t pinmux_pad1; + /** Set to enable SCL low time-out */ + bool scl_low_timeout; +#ifdef FEATURE_I2C_SCL_STRETCH_MODE + /** Set to enable SCL stretch only after ACK bit (required for high speed) */ + bool scl_stretch_only_after_ack_bit; +#endif +#ifdef FEATURE_I2C_SCL_EXTEND_TIMEOUT + /** Set to enable slave SCL low extend time-out */ + bool slave_scl_low_extend_timeout; +#endif +}; + + +/** + * \name Lock/Unlock + * @{ + */ + +/** + * \brief Attempt to get lock on driver instance + * + * This function checks the instance's lock, which indicates whether or not it + * is currently in use, and sets the lock if it was not already set. + * + * The purpose of this is to enable exclusive access to driver instances, so + * that, e.g., transactions by different services will not interfere with each + * other. + * + * \param[in,out] module Pointer to the driver instance to lock + * + * \retval STATUS_OK If the module was locked + * \retval STATUS_BUSY If the module was already locked + */ +static inline enum status_code i2c_slave_lock( + struct i2c_slave_module *const module) +{ + enum status_code status; + + system_interrupt_enter_critical_section(); + + if (module->locked) { + status = STATUS_BUSY; + } else { + module->locked = true; + status = STATUS_OK; + } + + system_interrupt_leave_critical_section(); + + return status; +} + +/** + * \brief Unlock driver instance + * + * This function clears the instance lock, indicating that it is available for + * use. + * + * \param[in,out] module Pointer to the driver instance to lock + * + * \retval STATUS_OK If the module was locked + * \retval STATUS_BUSY If the module was already locked + */ +static inline void i2c_slave_unlock(struct i2c_slave_module *const module) +{ + module->locked = false; +} + +/** @} */ + +/** + * \name Configuration and Initialization + * @{ + */ + +/** + * \brief Returns the synchronization status of the module + * + * Returns the synchronization status of the module. + * + * \param[out] module Pointer to software module structure + * + * \return Status of the synchronization. + * \retval true Module is busy synchronizing + * \retval false Module is not synchronizing + */ +static inline bool i2c_slave_is_syncing( + const struct i2c_slave_module *const module) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Return sync status */ +#if defined(FEATURE_SERCOM_SYNCBUSY_SCHEME_VERSION_1) + return (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_SYNCBUSY); +#elif defined(FEATURE_SERCOM_SYNCBUSY_SCHEME_VERSION_2) + return (i2c_hw->SYNCBUSY.reg & SERCOM_I2CS_SYNCBUSY_MASK); +#else +# error Unknown SERCOM SYNCBUSY scheme! +#endif +} + +#if !defined(__DOXYGEN__) +/** + * \internal Wait for hardware module to sync + * + * \param[in] module Pointer to software module structure + */ +static void _i2c_slave_wait_for_sync( + const struct i2c_slave_module *const module) +{ + /* Sanity check */ + Assert(module); + + while (i2c_slave_is_syncing(module)) { + /* Wait for I2C module to sync */ + } +} +#endif + +///@cond INTERNAL +/** + * \internal Workaround for errata 13574 + * Instead set ACK/NACK of CTRLB + * + * This errata exist in part revisions of SAM D20/D21/D09 + * D10/D11/L21/L22/DA1/C20/C21/R30, but workaround can be works in all + * revision of those device. As this function operation + * should be use less cpu time as possible, so caller + * function can ignore to check revision number, and use + * this workaround in all revision of those device. + * + * \param[in,out] module Pointer to software module structure + * \param[in] send_ack true send ACK, false send NACK + */ +static inline void _i2c_slave_set_ctrlb_ackact( + struct i2c_slave_module *const module, + bool send_ack) +{ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + +#if (SAMD20 || SAMD21 || SAMD09 || SAMD10 || SAMD11 || SAML21 || SAMDA1 || \ + SAML22 || SAMC20 || SAMC21 || SAMR30) + /* Workaround, Following two write are atomic */ + system_interrupt_enter_critical_section(); + i2c_hw->STATUS.reg = 0; + + if (send_ack == true) { + i2c_hw->CTRLB.reg = 0; + } + else { + i2c_hw->CTRLB.reg = SERCOM_I2CS_CTRLB_ACKACT; + } + system_interrupt_leave_critical_section(); +#else + /* Normal operation */ + if (send_ack == true) { + i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT; + } + else { + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT; + } +#endif + return; +} + +/** + * \internal Workaround for SAM0 errata 13574, + * instead Set CMD3 of CTRLB + * + * This errata exist in part revisions of SAM D20/D21/D09 + * D10/D11/L21/DA1/C20/C21/R30, but workaround can be works in all + * revision of those device. As this function operation + * should be use less cpu time as possible, so caller + * function can ignore to check revision number, and use + * this workaround in all revision of those device. + * + * \param[in,out] module Pointer to software module structure + */ +static inline void _i2c_slave_set_ctrlb_cmd3( + struct i2c_slave_module *const module) +{ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + +#if (SAMD20 || SAMD21 || SAMD09 || SAMD10 || SAMD11 || SAML21 || SAML22 || \ + SAMDA1 || SAMC20 || SAMC21 || SAMR30) + /* Workaround */ + /* + * Below code instead i2c_hw->CTRLB.reg = SERCOM_I2CS_CTRLB_CMD(0x3); + * CMD=0x3 clears all interrupts, so to keep the result similar + * PREC is cleared if it was set + */ + if (i2c_hw->INTFLAG.bit.PREC) { + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; + } + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_AMATCH; +#else + /* Normal operation */ + i2c_hw->CTRLB.reg = SERCOM_I2CS_CTRLB_CMD(0x3); +#endif + return; +} +///@endcond + +/** + * \brief Gets the I2C slave default configurations + * + * This will initialize the configuration structure to known default values. + * + * The default configuration is as follows: + * - Disable SCL low timeout + * - 300ns - 600ns SDA hold time + * - Buffer timeout = 65535 + * - Address with mask + * - Address = 0 + * - Address mask = 0 (one single address) + * - General call address disabled + * - Address nack disabled if the interrupt driver is used + * - GCLK generator 0 + * - Do not run in standby + * - PINMUX_DEFAULT for SERCOM pads + * + * Those default configuration only available if the device supports it: + * - Not using 10-bit addressing + * - Standard-mode and Fast-mode transfer speed + * - SCL stretch disabled + * - Slave SCL low extend time-out disabled + * + * \param[out] config Pointer to configuration structure to be initialized + */ +static inline void i2c_slave_get_config_defaults( + struct i2c_slave_config *const config) +{ + /*Sanity check argument */ + Assert(config); + config->enable_scl_low_timeout = false; + config->sda_hold_time = I2C_SLAVE_SDA_HOLD_TIME_300NS_600NS; + config->buffer_timeout = 65535; + config->address_mode = I2C_SLAVE_ADDRESS_MODE_MASK; + config->address = 0; + config->address_mask = 0; +#ifdef FEATURE_I2C_10_BIT_ADDRESS + config->ten_bit_address = false; +#endif + config->enable_general_call_address = false; +#ifdef FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED + config->transfer_speed = I2C_SLAVE_SPEED_STANDARD_AND_FAST; +#endif +#if I2C_SLAVE_CALLBACK_MODE == true + config->enable_nack_on_address = false; +#endif + config->generator_source = GCLK_GENERATOR_0; + config->run_in_standby = false; + config->pinmux_pad0 = PINMUX_DEFAULT; + config->pinmux_pad1 = PINMUX_DEFAULT; + config->scl_low_timeout = false; +#ifdef FEATURE_I2C_SCL_STRETCH_MODE + config->scl_stretch_only_after_ack_bit = false; +#endif +#ifdef FEATURE_I2C_SCL_EXTEND_TIMEOUT + config->slave_scl_low_extend_timeout = false; +#endif +} + +enum status_code i2c_slave_init(struct i2c_slave_module *const module, + Sercom *const hw, + const struct i2c_slave_config *const config); + +/** + * \brief Enables the I2C module + * + * This will enable the requested I2C module. + * + * \param[in] module Pointer to the software module struct + */ +static inline void i2c_slave_enable( + const struct i2c_slave_module *const module) +{ + /* Sanity check of arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Enable global interrupt for module */ + system_interrupt_enable(_sercom_get_interrupt_vector(module->hw)); +#endif + + /* Wait for module to sync */ + _i2c_slave_wait_for_sync(module); + + /* Enable module */ + i2c_hw->CTRLA.reg |= SERCOM_I2CS_CTRLA_ENABLE; +} + + +/** + * \brief Disables the I2C module + * + * This will disable the I2C module specified in the provided software module + * structure. + * + * \param[in] module Pointer to the software module struct + */ +static inline void i2c_slave_disable( + const struct i2c_slave_module *const module) +{ + /* Sanity check of arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Disable interrupts */ + i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTENSET_PREC | + SERCOM_I2CS_INTENSET_AMATCH | SERCOM_I2CS_INTENSET_DRDY; + + /* Clear interrupt flags */ + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC | SERCOM_I2CS_INTFLAG_AMATCH | + SERCOM_I2CS_INTFLAG_DRDY; + + /* Disable global interrupt for module */ + system_interrupt_disable(_sercom_get_interrupt_vector(module->hw)); +#endif + + /* Wait for module to sync */ + _i2c_slave_wait_for_sync(module); + + /* Disable module */ + i2c_hw->CTRLA.reg &= ~SERCOM_I2CS_CTRLA_ENABLE; +} + +void i2c_slave_reset( + struct i2c_slave_module *const module); + +/** @} */ + +/** + * \name Read and Write + * @{ + */ + +enum status_code i2c_slave_write_packet_wait( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet); +enum status_code i2c_slave_read_packet_wait( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet); +enum i2c_slave_direction i2c_slave_get_direction_wait( + struct i2c_slave_module *const module); + +/** @} */ + +/** + * \name Status Management + * @{ + */ +uint32_t i2c_slave_get_status( + struct i2c_slave_module *const module); +void i2c_slave_clear_status( + struct i2c_slave_module *const module, + uint32_t status_flags); +/** @} */ + +#ifdef FEATURE_I2C_DMA_SUPPORT +/** + * \name SERCOM I2C slave with DMA Interfaces + * @{ + */ + +/** + * \brief Read SERCOM I2C interrupt status. + * + * Read I2C interrupt status for DMA transfer. + * + * \param[in,out] module Pointer to the driver instance to lock + * + */ +static inline uint8_t i2c_slave_dma_read_interrupt_status(struct i2c_slave_module *const module) +{ + return (uint8_t)module->hw->I2CS.INTFLAG.reg; +} + +/** + * \brief Write SERCOM I2C interrupt status. + * + * Write I2C interrupt status for DMA transfer. + * + * \param[in,out] module Pointer to the driver instance to lock + * \param[in] flag Interrupt flag status + * + */ +static inline void i2c_slave_dma_write_interrupt_status(struct i2c_slave_module *const module, + uint8_t flag) +{ + module->hw->I2CS.INTFLAG.reg = flag; +} + +/** @} */ +#endif + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* I2C_SLAVE_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_slave_interrupt.h b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_slave_interrupt.h new file mode 100644 index 000000000..cf1c4362c --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_slave_interrupt.h @@ -0,0 +1,215 @@ +/** + * \file + * + * \brief SAM SERCOM I2C Slave Interrupt 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 I2C_SLAVE_INTERRUPT_H_INCLUDED +#define I2C_SLAVE_INTERRUPT_H_INCLUDED + +#include "i2c_slave.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup asfdoc_sam0_sercom_i2c_group + * @{ + * + */ + +/** + * \name Address Match Functionality + * @{ + */ + +void i2c_slave_enable_nack_on_address( + struct i2c_slave_module *const module); +void i2c_slave_disable_nack_on_address( + struct i2c_slave_module *const module); + +/** @} */ + +/** + * \name Callbacks + * @{ + */ +#if !defined(__DOXYGEN__) +void _i2c_slave_interrupt_handler(uint8_t instance); +#endif + +void i2c_slave_register_callback( + struct i2c_slave_module *const module, + i2c_slave_callback_t callback, + enum i2c_slave_callback callback_type); + +void i2c_slave_unregister_callback( + struct i2c_slave_module *const module, + enum i2c_slave_callback callback_type); + +/** + * \brief Enables callback + * + * Enables the callback specified by the callback_type. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback_type Callback type to enable + */ +static inline void i2c_slave_enable_callback( + struct i2c_slave_module *const module, + enum i2c_slave_callback callback_type) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + /* Mark callback as enabled */ + module->enabled_callback |= (1 << callback_type); + + /* Enable address callback */ + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + if (callback_type == I2C_SLAVE_CALLBACK_READ_REQUEST || + callback_type == I2C_SLAVE_CALLBACK_WRITE_REQUEST) { + i2c_hw->INTENSET.reg = SERCOM_I2CS_INTFLAG_AMATCH; + } +} + +/** + * \brief Disables callback + * + * Disables the callback specified by the callback_type. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback_type Callback type to disable + */ +static inline void i2c_slave_disable_callback( + struct i2c_slave_module *const module, + enum i2c_slave_callback callback_type) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + /* Mark callback as disabled */ + module->enabled_callback &= ~(1 << callback_type); + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + if (callback_type == I2C_SLAVE_CALLBACK_READ_REQUEST || + callback_type == I2C_SLAVE_CALLBACK_WRITE_REQUEST || + module->status != STATUS_BUSY) { + i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_AMATCH; + } +} + +/** @} */ + +/** + * \name Read and Write, Interrupt-Driven + * @{ + */ + + +enum status_code i2c_slave_read_packet_job( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet); + +enum status_code i2c_slave_write_packet_job( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet); + +/** + * \brief Cancels any currently ongoing operation + * + * Terminates the running transfer operation. + * + * \param[in,out] module Pointer to software module structure + */ +static inline void i2c_slave_cancel_job( + struct i2c_slave_module *const module) +{ + /* Sanity check. */ + Assert(module); + Assert(module->hw); + + /* Set buffer to 0. */ + module->buffer_remaining = 0; + module->buffer_length = 0; +} + +/** + * \brief Gets status of ongoing job + * + * Will return the status of the ongoing job, or the error that occurred + * in the last transfer operation. + * The status will be cleared when starting a new job. + * + * \param[in,out] module Pointer to software module structure + * + * \return Status of job. + * \retval STATUS_OK No error has occurred + * \retval STATUS_BUSY Transfer is in progress + * \retval STATUS_ERR_IO A collision, timeout or bus error happened in + * the last transfer + * \retval STATUS_ERR_TIMEOUT A timeout occurred + * \retval STATUS_ERR_OVERFLOW Data from master overflows receive buffer + */ +static inline enum status_code i2c_slave_get_job_status( + struct i2c_slave_module *const module) +{ + /* Check sanity. */ + Assert(module); + Assert(module->hw); + + /* Return current status code. */ + return module->status; +} + +/** @} */ + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* I2C_SLAVE_INTERRUPT_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/sercom.c b/atmel-samd/asf/sam0/drivers/sercom/sercom.c new file mode 100644 index 000000000..637f3947f --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/sercom.c @@ -0,0 +1,296 @@ +/** + * \file + * + * \brief SAM Serial Peripheral Interface Driver + * + * Copyright (C) 2012-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 "sercom.h" + +#define SHIFT 32 +#define BAUD_INT_MAX 8192 +#define BAUD_FP_MAX 8 + +#if !defined(__DOXYGEN__) +/** + * \internal Configuration structure to save current gclk status. + */ +struct _sercom_conf { + /* Status of gclk generator initialization */ + bool generator_is_set; + /* Sercom gclk generator used */ + enum gclk_generator generator_source; +}; + +static struct _sercom_conf _sercom_config; + + +/** + * \internal Calculate 64 bit division, ref can be found in + * http://en.wikipedia.org/wiki/Division_algorithm#Long_division + */ +static uint64_t long_division(uint64_t n, uint64_t d) +{ + int32_t i; + uint64_t q = 0, r = 0, bit_shift; + for (i = 63; i >= 0; i--) { + bit_shift = (uint64_t)1 << i; + + r = r << 1; + + if (n & bit_shift) { + r |= 0x01; + } + + if (r >= d) { + r = r - d; + q |= bit_shift; + } + } + + return q; +} + +/** + * \internal Calculate synchronous baudrate value (SPI/UART) + */ +enum status_code _sercom_get_sync_baud_val( + const uint32_t baudrate, + const uint32_t external_clock, + uint16_t *const baudvalue) +{ + /* Baud value variable */ + uint16_t baud_calculated = 0; + uint32_t clock_value = external_clock; + + + /* Check if baudrate is outside of valid range */ + if (baudrate > (external_clock / 2)) { + /* Return with error code */ + return STATUS_ERR_BAUDRATE_UNAVAILABLE; + } + + /* Calculate BAUD value from clock frequency and baudrate */ + clock_value = external_clock / 2; + while (clock_value >= baudrate) { + clock_value = clock_value - baudrate; + baud_calculated++; + } + baud_calculated = baud_calculated - 1; + + /* Check if BAUD value is more than 255, which is maximum + * for synchronous mode */ + if (baud_calculated > 0xFF) { + /* Return with an error code */ + return STATUS_ERR_BAUDRATE_UNAVAILABLE; + } else { + *baudvalue = baud_calculated; + return STATUS_OK; + } +} + +/** + * \internal Calculate asynchronous baudrate value (UART) +*/ +enum status_code _sercom_get_async_baud_val( + const uint32_t baudrate, + const uint32_t peripheral_clock, + uint16_t *const baudval, + enum sercom_asynchronous_operation_mode mode, + enum sercom_asynchronous_sample_num sample_num) +{ + /* Temporary variables */ + uint64_t ratio = 0; + uint64_t scale = 0; + uint64_t baud_calculated = 0; + uint8_t baud_fp; + uint32_t baud_int = 0; + uint64_t temp1, temp2; + + /* Check if the baudrate is outside of valid range */ + if ((baudrate * sample_num) > peripheral_clock) { + /* Return with error code */ + return STATUS_ERR_BAUDRATE_UNAVAILABLE; + } + + if(mode == SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC) { + /* Calculate the BAUD value */ + temp1 = ((sample_num * (uint64_t)baudrate) << SHIFT); + ratio = long_division(temp1, peripheral_clock); + scale = ((uint64_t)1 << SHIFT) - ratio; + baud_calculated = (65536 * scale) >> SHIFT; + } else if(mode == SERCOM_ASYNC_OPERATION_MODE_FRACTIONAL) { + for(baud_fp = 0; baud_fp < BAUD_FP_MAX; baud_fp++) { + temp1 = BAUD_FP_MAX * (uint64_t)peripheral_clock; + temp2 = ((uint64_t)baudrate * sample_num); + baud_int = long_division(temp1, temp2); + baud_int -= baud_fp; + baud_int = baud_int / BAUD_FP_MAX; + if(baud_int < BAUD_INT_MAX) { + break; + } + } + if(baud_fp == BAUD_FP_MAX) { + return STATUS_ERR_BAUDRATE_UNAVAILABLE; + } + baud_calculated = baud_int | (baud_fp << 13); + } + + *baudval = baud_calculated; + return STATUS_OK; +} +#endif + +/** + * \brief Set GCLK channel to generator. + * + * This will set the appropriate GCLK channel to the requested GCLK generator. + * This will set the generator for all SERCOM instances, and the user will thus + * only be able to set the same generator that has previously been set, if any. + * + * After the generator has been set the first time, the generator can be changed + * using the \c force_change flag. + * + * \param[in] generator_source The generator to use for SERCOM. + * \param[in] force_change Force change the generator. + * + * \return Status code indicating the GCLK generator change operation. + * \retval STATUS_OK If the generator update request was + * successful. + * \retval STATUS_ERR_ALREADY_INITIALIZED If a generator was already configured + * and the new configuration was not + * forced. + */ +enum status_code sercom_set_gclk_generator( + const enum gclk_generator generator_source, + const bool force_change) +{ + /* Check if valid option */ + if (!_sercom_config.generator_is_set || force_change) { + /* Create and fill a GCLK configuration structure for the new config */ + struct system_gclk_chan_config gclk_chan_conf; + system_gclk_chan_get_config_defaults(&gclk_chan_conf); + gclk_chan_conf.source_generator = generator_source; + system_gclk_chan_set_config(SERCOM_GCLK_ID, &gclk_chan_conf); + system_gclk_chan_enable(SERCOM_GCLK_ID); + + /* Save config */ + _sercom_config.generator_source = generator_source; + _sercom_config.generator_is_set = true; + + return STATUS_OK; + } else if (generator_source == _sercom_config.generator_source) { + /* Return status OK if same config */ + return STATUS_OK; + } + + /* Return invalid config to already initialized GCLK */ + return STATUS_ERR_ALREADY_INITIALIZED; +} + +/** \internal + * Creates a switch statement case entry to convert a SERCOM instance and pad + * index to the default SERCOM pad MUX setting. + */ +#define _SERCOM_PAD_DEFAULTS_CASE(n, pad) \ + case (uintptr_t)SERCOM##n: \ + switch (pad) { \ + case 0: \ + return SERCOM##n##_PAD0_DEFAULT; \ + case 1: \ + return SERCOM##n##_PAD1_DEFAULT; \ + case 2: \ + return SERCOM##n##_PAD2_DEFAULT; \ + case 3: \ + return SERCOM##n##_PAD3_DEFAULT; \ + } \ + break; + +/** + * \internal Gets the default PAD pinout for a given SERCOM. + * + * Returns the pinmux settings for the given SERCOM and pad. This is used + * for default configuration of pins. + * + * \param[in] sercom_module Pointer to the SERCOM module + * \param[in] pad PAD to get default pinout for + * + * \returns The default pinmux for the given SERCOM instance and PAD + * + */ +uint32_t _sercom_get_default_pad( + Sercom *const sercom_module, + const uint8_t pad) +{ + switch ((uintptr_t)sercom_module) { + /* Auto-generate a lookup table for the default SERCOM pad defaults */ + MREPEAT(SERCOM_INST_NUM, _SERCOM_PAD_DEFAULTS_CASE, pad) + } + + Assert(false); + return 0; +} + +/** + * \internal + * Find index of given instance. + * + * \param[in] sercom_instance Instance pointer. + * + * \return Index of given instance. + */ +uint8_t _sercom_get_sercom_inst_index( + Sercom *const sercom_instance) +{ + /* Save all available SERCOM instances for compare */ + Sercom *sercom_instances[SERCOM_INST_NUM] = SERCOM_INSTS; + + /* Find index for sercom instance */ + for (uint32_t i = 0; i < SERCOM_INST_NUM; i++) { + if ((uintptr_t)sercom_instance == (uintptr_t)sercom_instances[i]) { + return i; + } + } + + /* Invalid data given */ + Assert(false); + return 0; +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/sercom.h b/atmel-samd/asf/sam0/drivers/sercom/sercom.h new file mode 100644 index 000000000..60ce6f8ad --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/sercom.h @@ -0,0 +1,118 @@ +/** + * \file + * + * \brief SAM Serial Peripheral Interface Driver + * + * Copyright (C) 2012-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 SERCOM_H_INCLUDED +#define SERCOM_H_INCLUDED + +#include +#include +#include +#include +#include "sercom_pinout.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* SERCOM modules should share same slow GCLK channel ID */ +#define SERCOM_GCLK_ID SERCOM0_GCLK_ID_SLOW + +#if (0x1ff >= REV_SERCOM) +# define FEATURE_SERCOM_SYNCBUSY_SCHEME_VERSION_1 +#elif (0x400 >= REV_SERCOM) +# define FEATURE_SERCOM_SYNCBUSY_SCHEME_VERSION_2 +#else +# error "Unknown SYNCBUSY scheme for this SERCOM revision" +#endif + +/** + * \brief sercom asynchronous operation mode + * + * Select sercom asynchronous operation mode + */ +enum sercom_asynchronous_operation_mode { + SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC = 0, + SERCOM_ASYNC_OPERATION_MODE_FRACTIONAL, +}; + +/** + * \brief sercom asynchronous samples per bit + * + * Select number of samples per bit + */ +enum sercom_asynchronous_sample_num { + SERCOM_ASYNC_SAMPLE_NUM_3 = 3, + SERCOM_ASYNC_SAMPLE_NUM_8 = 8, + SERCOM_ASYNC_SAMPLE_NUM_16 = 16, +}; + +enum status_code sercom_set_gclk_generator( + const enum gclk_generator generator_source, + const bool force_change); + +enum status_code _sercom_get_sync_baud_val( + const uint32_t baudrate, + const uint32_t external_clock, + uint16_t *const baudval); + +enum status_code _sercom_get_async_baud_val( + const uint32_t baudrate, + const uint32_t peripheral_clock, + uint16_t *const baudval, + enum sercom_asynchronous_operation_mode mode, + enum sercom_asynchronous_sample_num sample_num); + +uint32_t _sercom_get_default_pad( + Sercom *const sercom_module, + const uint8_t pad); + +uint8_t _sercom_get_sercom_inst_index( + Sercom *const sercom_instance); +#ifdef __cplusplus +} +#endif + +#endif //__SERCOM_H_INCLUDED diff --git a/atmel-samd/asf/sam0/drivers/sercom/sercom_interrupt.c b/atmel-samd/asf/sam0/drivers/sercom/sercom_interrupt.c new file mode 100644 index 000000000..b26d05b6e --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/sercom_interrupt.c @@ -0,0 +1,141 @@ +/** + * \file + * + * \brief SAM Serial Peripheral Interface Driver + * + * Copyright (C) 2012-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 "sercom_interrupt.h" + +void *_sercom_instances[SERCOM_INST_NUM]; + +/** Save status of initialized handlers */ +static bool _handler_table_initialized = false; + +/** Void pointers for saving device instance structures */ +static void (*_sercom_interrupt_handlers[SERCOM_INST_NUM])(const uint8_t instance); + +/** + * \internal + * Default interrupt handler. + * + * \param[in] instance SERCOM instance used. + */ +static void _sercom_default_handler( + const uint8_t instance) +{ + Assert(false); +} + +/** + * \internal + * Saves the given callback handler. + * + * \param[in] instance Instance index. + * \param[in] interrupt_handler Pointer to instance callback handler. + */ +void _sercom_set_handler( + const uint8_t instance, + const sercom_handler_t interrupt_handler) +{ + /* Initialize handlers with default handler and device instances with 0 */ + if (_handler_table_initialized == false) { + for (uint32_t i = 0; i < SERCOM_INST_NUM; i++) { + _sercom_interrupt_handlers[i] = &_sercom_default_handler; + _sercom_instances[i] = NULL; + } + + _handler_table_initialized = true; + } + + /* Save interrupt handler */ + _sercom_interrupt_handlers[instance] = interrupt_handler; +} + + +/** \internal + * Converts a given SERCOM index to its interrupt vector index. + */ +#define _SERCOM_INTERRUPT_VECT_NUM(n, unused) \ + SYSTEM_INTERRUPT_MODULE_SERCOM##n, + +/** \internal + * Generates a SERCOM interrupt handler function for a given SERCOM index. + */ +#define _SERCOM_INTERRUPT_HANDLER(n, unused) \ + void SERCOM##n##_Handler(void) \ + { \ + _sercom_interrupt_handlers[n](n); \ + } + +/** + * \internal + * Returns the system interrupt vector. + * + * \param[in] sercom_instance Instance pointer + * + * \return Enum of system interrupt vector + * \retval SYSTEM_INTERRUPT_MODULE_SERCOM0 + * \retval SYSTEM_INTERRUPT_MODULE_SERCOM1 + * \retval SYSTEM_INTERRUPT_MODULE_SERCOM2 + * \retval SYSTEM_INTERRUPT_MODULE_SERCOM3 + * \retval SYSTEM_INTERRUPT_MODULE_SERCOM4 + * \retval SYSTEM_INTERRUPT_MODULE_SERCOM5 + * \retval SYSTEM_INTERRUPT_MODULE_SERCOM6 + * \retval SYSTEM_INTERRUPT_MODULE_SERCOM7 + */ +enum system_interrupt_vector _sercom_get_interrupt_vector( + Sercom *const sercom_instance) +{ + const uint8_t sercom_int_vectors[SERCOM_INST_NUM] = + { + MREPEAT(SERCOM_INST_NUM, _SERCOM_INTERRUPT_VECT_NUM, ~) + }; + + /* Retrieve the index of the SERCOM being requested */ + uint8_t instance_index = _sercom_get_sercom_inst_index(sercom_instance); + + /* Get the vector number from the lookup table for the requested SERCOM */ + return (enum system_interrupt_vector)sercom_int_vectors[instance_index]; +} + +/** Auto-generate a set of interrupt handlers for each SERCOM in the device */ +MREPEAT(SERCOM_INST_NUM, _SERCOM_INTERRUPT_HANDLER, ~) diff --git a/atmel-samd/asf/sam0/drivers/sercom/sercom_interrupt.h b/atmel-samd/asf/sam0/drivers/sercom/sercom_interrupt.h new file mode 100644 index 000000000..18945f518 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/sercom_interrupt.h @@ -0,0 +1,72 @@ +/** + * \file + * + * \brief SAM Serial Peripheral Interface Driver + * + * Copyright (C) 2012-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 SERCOM_INTERRUPT_H_INCLUDED +#define SERCOM_INTERRUPT_H_INCLUDED + +#include "sercom.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Look-up table for device instances */ +extern void *_sercom_instances[SERCOM_INST_NUM]; + +typedef void (*sercom_handler_t)(uint8_t instance); + +enum system_interrupt_vector _sercom_get_interrupt_vector( + Sercom *const sercom_instance); + +void _sercom_set_handler( + const uint8_t instance, + const sercom_handler_t interrupt_handler); + +#ifdef __cplusplus +} +#endif + +#endif /* SERCOM_INTERRUPT_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/sercom_pinout.h b/atmel-samd/asf/sam0/drivers/sercom/sercom_pinout.h new file mode 100644 index 000000000..94ebc9cc0 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/sercom_pinout.h @@ -0,0 +1,526 @@ +/** + * \file + * + * \brief SAM SERCOM Module Pinout Definitions + * + * Copyright (C) 2012-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 SERCOM_PINOUT_H_INCLUDED +#define SERCOM_PINOUT_H_INCLUDED + +#include + +#if SAMR21E + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA08C_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA09C_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA16C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA17C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA18C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA19C_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA08D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA09D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA14C_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA15C_SERCOM2_PAD3 + + /* SERCOM3 */ +# if SAM_PART_IS_DEFINED(SAMR21E19A) + #define SERCOM3_PAD0_DEFAULT PINMUX_PA16D_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA17D_SERCOM3_PAD1 +# else + #define SERCOM3_PAD0_DEFAULT PINMUX_PA27F_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA28F_SERCOM3_PAD1 +#endif + #define SERCOM3_PAD2_DEFAULT PINMUX_PA24C_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PA25C_SERCOM3_PAD3 + + /* SERCOM4 */ +# if SAM_PART_IS_DEFINED(SAMR21E19A) + #define SERCOM4_PAD0_DEFAULT PINMUX_PB08D_SERCOM4_PAD0 + #define SERCOM4_PAD1_DEFAULT PINMUX_PB09D_SERCOM4_PAD1 + #define SERCOM4_PAD2_DEFAULT PINMUX_PA14D_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PA15D_SERCOM4_PAD3 +# else + #define SERCOM4_PAD0_DEFAULT PINMUX_PC19F_SERCOM4_PAD0 + #define SERCOM4_PAD1_DEFAULT PINMUX_PB31F_SERCOM4_PAD1 + #define SERCOM4_PAD2_DEFAULT PINMUX_PB30F_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PC18F_SERCOM4_PAD3 +# endif + + /* SERCOM5 */ + #define SERCOM5_PAD0_DEFAULT PINMUX_PB30D_SERCOM5_PAD0 + #define SERCOM5_PAD1_DEFAULT PINMUX_PB31D_SERCOM5_PAD1 + #define SERCOM5_PAD2_DEFAULT PINMUX_PA24D_SERCOM5_PAD2 + #define SERCOM5_PAD3_DEFAULT PINMUX_PA25D_SERCOM5_PAD3 + +#elif SAMR21G + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA00D_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA01D_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA30D_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA31D_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA12C_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA13C_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA14C_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA15C_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PA16D_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA17D_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PA18D_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PA19D_SERCOM3_PAD3 + + /* SERCOM4 */ + #define SERCOM4_PAD0_DEFAULT PINMUX_PC19F_SERCOM4_PAD0 + #define SERCOM4_PAD1_DEFAULT PINMUX_PB31F_SERCOM4_PAD1 + #define SERCOM4_PAD2_DEFAULT PINMUX_PB30F_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PC18F_SERCOM4_PAD3 + + /* SERCOM5 */ + #define SERCOM5_PAD0_DEFAULT PINMUX_PA22D_SERCOM5_PAD0 + #define SERCOM5_PAD1_DEFAULT PINMUX_PA23D_SERCOM5_PAD1 + #define SERCOM5_PAD2_DEFAULT PINMUX_PA24D_SERCOM5_PAD2 + #define SERCOM5_PAD3_DEFAULT PINMUX_PA25D_SERCOM5_PAD3 + +#elif (SAMD09) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA08D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA09D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA30C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA31C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA24C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA25C_SERCOM1_PAD3 + +#elif (SAMD10DS) || (SAMD10DM) || (SAMD10DU) || (SAMD11DS) || (SAMD11DM) || (SAMD11DU) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA22C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA23C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA30D_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA31D_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA22D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA23D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA16D_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA25D_SERCOM2_PAD3 + +#elif (SAMD10C) || (SAMD11C) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA08D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA09D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA30C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA31C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA24C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA25C_SERCOM1_PAD3 + +#elif SAM_PART_IS_DEFINED(SAMD21E15L) || SAM_PART_IS_DEFINED(SAMD21E16L) + + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA16C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA17C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA18C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA19C_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA08D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA09D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA10D_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA11D_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PA22C_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA23C_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PA24C_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PA25C_SERCOM3_PAD3 + +#elif (SAML22N) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA08C_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA09C_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA10C_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA11C_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA16C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA17C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA18C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA19C_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA22D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA23D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA20D_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA21D_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PB02C_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PB21C_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PB00C_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PB01C_SERCOM3_PAD3 + + /* SERCOM4 */ + #define SERCOM4_PAD0_DEFAULT PINMUX_PA12C_SERCOM4_PAD0 + #define SERCOM4_PAD1_DEFAULT PINMUX_PA13C_SERCOM4_PAD1 + #define SERCOM4_PAD2_DEFAULT PINMUX_PA14C_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PA15C_SERCOM4_PAD3 + + /* SERCOM5 */ + #define SERCOM5_PAD0_DEFAULT PINMUX_PB30D_SERCOM5_PAD0 + #define SERCOM5_PAD1_DEFAULT PINMUX_PB31D_SERCOM5_PAD1 + #define SERCOM5_PAD2_DEFAULT PINMUX_PB22D_SERCOM5_PAD2 + #define SERCOM5_PAD3_DEFAULT PINMUX_PB23D_SERCOM5_PAD3 +#elif (SAML22J) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA08C_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA09C_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA10C_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA11C_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA16C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA17C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA18C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA19C_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA22D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA23D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA20D_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA21D_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PB02C_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PB13C_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PB00C_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PB01C_SERCOM3_PAD3 +#elif (SAML22G) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA08C_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA09C_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA10C_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA11C_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA16C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA17C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA18C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA19C_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA22D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA23D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA20D_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA21D_SERCOM2_PAD3 +#elif (SAMC20E) || (SAMC21E) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA16C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA17C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA18C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA19C_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA08D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA09D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA10D_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA11D_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PA22C_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA23C_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PA24C_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PA25C_SERCOM3_PAD3 + +#elif (SAMC20G) || (SAMC21G) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA16C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA17C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA18C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA19C_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA12C_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA13C_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA14C_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA15C_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PA22C_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA23C_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PA24C_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PA25C_SERCOM3_PAD3 + + #ifdef ID_SERCOM4 + /* SERCOM4 */ + #define SERCOM4_PAD0_DEFAULT PINMUX_PB08D_SERCOM4_PAD0 + #define SERCOM4_PAD1_DEFAULT PINMUX_PB09D_SERCOM4_PAD1 + #define SERCOM4_PAD2_DEFAULT PINMUX_PB10D_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PB11D_SERCOM4_PAD3 + #endif + + #ifdef ID_SERCOM5 + /* SERCOM5 */ + #define SERCOM5_PAD0_DEFAULT PINMUX_PB02D_SERCOM5_PAD0 + #define SERCOM5_PAD1_DEFAULT PINMUX_PB03D_SERCOM5_PAD1 + #define SERCOM5_PAD2_DEFAULT PINMUX_PB22D_SERCOM5_PAD2 + #define SERCOM5_PAD3_DEFAULT PINMUX_PB23D_SERCOM5_PAD3 + #endif + +#elif (SAMC20J) || (SAMC21J) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA16C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA17C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA18C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA19C_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA12C_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA13C_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA14C_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA15C_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PA22C_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA23C_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PA24C_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PA25C_SERCOM3_PAD3 + + #ifdef ID_SERCOM4 + /* SERCOM4 */ + #define SERCOM4_PAD0_DEFAULT PINMUX_PB08D_SERCOM4_PAD0 + #define SERCOM4_PAD1_DEFAULT PINMUX_PB09D_SERCOM4_PAD1 + #define SERCOM4_PAD2_DEFAULT PINMUX_PB10D_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PB11D_SERCOM4_PAD3 + #endif + + #ifdef ID_SERCOM5 + /* SERCOM5 */ + #define SERCOM5_PAD0_DEFAULT PINMUX_PB02D_SERCOM5_PAD0 + #define SERCOM5_PAD1_DEFAULT PINMUX_PB03D_SERCOM5_PAD1 + #define SERCOM5_PAD2_DEFAULT PINMUX_PB00D_SERCOM5_PAD2 + #define SERCOM5_PAD3_DEFAULT PINMUX_PB01D_SERCOM5_PAD3 + #endif + +#elif (SAMDA1) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA00D_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA01D_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA30D_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA31D_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA08D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA09D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA10D_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA11D_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PA16D_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA17D_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PA18D_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PA19D_SERCOM3_PAD3 + + #if (SAMDA1E) + /* SERCOM4 */ + #define SERCOM4_PAD0_DEFAULT 0 /* No available pin */ + #define SERCOM4_PAD1_DEFAULT 0 /* No available pin */ + #define SERCOM4_PAD2_DEFAULT PINMUX_PA14D_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PA15D_SERCOM4_PAD3 + #else + /* SERCOM4 */ + #define SERCOM4_PAD0_DEFAULT PINMUX_PA12D_SERCOM4_PAD0 + #define SERCOM4_PAD1_DEFAULT PINMUX_PA13D_SERCOM4_PAD1 + #define SERCOM4_PAD2_DEFAULT PINMUX_PA14D_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PA15D_SERCOM4_PAD3 + #endif + + /* SERCOM5 */ + #define SERCOM5_PAD0_DEFAULT PINMUX_PA22D_SERCOM5_PAD0 + #define SERCOM5_PAD1_DEFAULT PINMUX_PA23D_SERCOM5_PAD1 + #define SERCOM5_PAD2_DEFAULT PINMUX_PA24D_SERCOM5_PAD2 + #define SERCOM5_PAD3_DEFAULT PINMUX_PA25D_SERCOM5_PAD3 + +#elif (SAML21E) || (SAMR30E) + + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA00D_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA01D_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA30D_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA31D_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA08D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA09D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA10D_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA11D_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PA16D_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA17D_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PA18D_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PA19D_SERCOM3_PAD3 + + #if !SAM_PART_IS_DEFINED(SAML21E18A) && !SAM_PART_IS_DEFINED(SAMR30E18A) + /* SERCOM4 */ + #define SERCOM4_PAD0_DEFAULT 0 /* No available pin */ + #define SERCOM4_PAD1_DEFAULT 0 /* No available pin */ + #define SERCOM4_PAD2_DEFAULT PINMUX_PA14D_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PA15D_SERCOM4_PAD3 + + /* SERCOM5 */ + #define SERCOM5_PAD0_DEFAULT PINMUX_PA22D_SERCOM5_PAD0 + #define SERCOM5_PAD1_DEFAULT PINMUX_PA23D_SERCOM5_PAD1 + #define SERCOM5_PAD2_DEFAULT PINMUX_PA24D_SERCOM5_PAD2 + #define SERCOM5_PAD3_DEFAULT PINMUX_PA25D_SERCOM5_PAD3 + #endif + +#else + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ +#if SAM_PART_IS_DEFINED(SAMD21G15L) || SAM_PART_IS_DEFINED(SAMD21G16L) + #define SERCOM1_PAD0_DEFAULT PINMUX_PA16C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA17C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA18C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA19C_SERCOM1_PAD3 +#else + #define SERCOM1_PAD0_DEFAULT PINMUX_PA00D_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA01D_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA30D_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA31D_SERCOM1_PAD3 +#endif + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA08D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA09D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA10D_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA11D_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PA16D_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA17D_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PA18D_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PA19D_SERCOM3_PAD3 + + #if !(SAMD20E || SAMD21E) + /* SERCOM4 */ + #define SERCOM4_PAD0_DEFAULT PINMUX_PA12D_SERCOM4_PAD0 + #define SERCOM4_PAD1_DEFAULT PINMUX_PA13D_SERCOM4_PAD1 + #define SERCOM4_PAD2_DEFAULT PINMUX_PA14D_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PA15D_SERCOM4_PAD3 + + /* SERCOM5 */ + #define SERCOM5_PAD0_DEFAULT PINMUX_PA22D_SERCOM5_PAD0 + #define SERCOM5_PAD1_DEFAULT PINMUX_PA23D_SERCOM5_PAD1 + #define SERCOM5_PAD2_DEFAULT PINMUX_PA24D_SERCOM5_PAD2 + #define SERCOM5_PAD3_DEFAULT PINMUX_PA25D_SERCOM5_PAD3 + #endif + +#endif + +#endif /* SERCOM_PINOUT_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/spi/module_config/conf_spi.h b/atmel-samd/asf/sam0/drivers/sercom/spi/module_config/conf_spi.h new file mode 100644 index 000000000..18b4f76ef --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/spi/module_config/conf_spi.h @@ -0,0 +1,55 @@ +/** + * \file + * + * \brief SAM SPI configuration + * + * 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 CONF_SPI_H_INCLUDED +# define CONF_SPI_H_INCLUDED + +# define CONF_SPI_MASTER_ENABLE true +# define CONF_SPI_SLAVE_ENABLE true + +#endif /* CONF_SPI_H_INCLUDED */ + diff --git a/atmel-samd/asf/sam0/drivers/sercom/spi/spi.c b/atmel-samd/asf/sam0/drivers/sercom/spi/spi.c new file mode 100644 index 000000000..c0b4acb4a --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/spi/spi.c @@ -0,0 +1,1265 @@ +/** + * \file + * + * \brief SAM Serial Peripheral Interface Driver + * + * Copyright (C) 2012-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 "spi.h" + +/** + * \brief Resets the SPI module + * + * This function will reset the SPI module to its power on default values and + * disable it. + * + * \param[in,out] module Pointer to the software instance struct + */ +void spi_reset( + struct spi_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + + /* Disable the module */ + spi_disable(module); + + while (spi_is_syncing(module)) { + /* Wait until the synchronization is complete */ + } + + /* Software reset the module */ + spi_module->CTRLA.reg |= SERCOM_SPI_CTRLA_SWRST; +} + +/** + * \brief Set the baudrate of the SPI module + * + * This function will set the baudrate of the SPI module. + * + * \param[in] module Pointer to the software instance struct + * \param[in] baudrate The baudrate wanted + * + * \return The status of the configuration. + * \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided + * \retval STATUS_OK If the configuration was written + */ +enum status_code spi_set_baudrate( + struct spi_module *const module, + uint32_t baudrate) +{ + /* Sanity check arguments */ + Assert(module); + Assert(baudrate); + Assert(module->hw); + + /* Value to write to BAUD register */ + uint16_t baud = 0; + + SercomSpi *const spi_module = &(module->hw->SPI); + + /* Disable the module */ + spi_disable(module); + + while (spi_is_syncing(module)) { + /* Wait until the synchronization is complete */ + } + + /* Find frequency of the internal SERCOMi_GCLK_ID_CORE */ + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + uint32_t gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + uint32_t internal_clock = system_gclk_chan_get_hz(gclk_index); + + /* Get baud value, based on baudrate and the internal clock frequency */ + enum status_code error_code = _sercom_get_sync_baud_val( + baudrate, internal_clock, &baud); + + if (error_code != STATUS_OK) { + /* Baud rate calculation error, return status code */ + return STATUS_ERR_INVALID_ARG; + } + + spi_module->BAUD.reg = (uint8_t)baud; + + while (spi_is_syncing(module)) { + /* Wait until the synchronization is complete */ + } + + /* Enable the module */ + spi_enable(module); + + while (spi_is_syncing(module)) { + /* Wait until the synchronization is complete */ + } + + return STATUS_OK; +} + +# if CONF_SPI_SLAVE_ENABLE == true +/** + * \internal Clears the Transmit Complete interrupt flag. + * + * \param[in] module Pointer to the software instance struct + */ +static void _spi_clear_tx_complete_flag( + struct spi_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + + /* Clear interrupt flag */ + spi_module->INTFLAG.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE; +} +# endif + +/** + * \internal Writes an SPI SERCOM configuration to the hardware module. + * + * This function will write out a given configuration to the hardware module. + * Can only be done when the module is disabled. + * + * \param[in] module Pointer to the software instance struct + * \param[in] config Pointer to the configuration struct + * + * \return The status of the configuration. + * \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided + * \retval STATUS_OK If the configuration was written + */ +static enum status_code _spi_set_config( + struct spi_module *const module, + const struct spi_config *const config) +{ + /* Sanity check arguments */ + Assert(module); + Assert(config); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + Sercom *const hw = module->hw; + + struct system_pinmux_config pin_conf; + system_pinmux_get_config_defaults(&pin_conf); + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_INPUT; + if(config->mode == SPI_MODE_SLAVE) { + pin_conf.input_pull = SYSTEM_PINMUX_PIN_PULL_NONE; + } + + uint32_t pad_pinmuxes[] = { + config->pinmux_pad0, config->pinmux_pad1, + config->pinmux_pad2, config->pinmux_pad3 + }; + + /* Configure the SERCOM pins according to the user configuration */ + for (uint8_t pad = 0; pad < 4; pad++) { + uint32_t current_pinmux = pad_pinmuxes[pad]; + + if (current_pinmux == PINMUX_DEFAULT) { + current_pinmux = _sercom_get_default_pad(hw, pad); + } + + if (current_pinmux != PINMUX_UNUSED) { + pin_conf.mux_position = current_pinmux & 0xFFFF; + system_pinmux_pin_set_config(current_pinmux >> 16, &pin_conf); + } + } + + module->mode = config->mode; + module->character_size = config->character_size; + module->receiver_enabled = config->receiver_enable; +# ifdef FEATURE_SPI_HARDWARE_SLAVE_SELECT + module->master_slave_select_enable = config->master_slave_select_enable; +# endif + +# if CONF_SPI_MASTER_ENABLE == true + /* Value to write to BAUD register */ + uint16_t baud = 0; +# endif + /* Value to write to CTRLA register */ + uint32_t ctrla = 0; + /* Value to write to CTRLB register */ + uint32_t ctrlb = 0; + +# if CONF_SPI_MASTER_ENABLE == true + /* Find baud value and write it */ + if (config->mode == SPI_MODE_MASTER) { + /* Find frequency of the internal SERCOMi_GCLK_ID_CORE */ + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + uint32_t gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + uint32_t internal_clock = system_gclk_chan_get_hz(gclk_index); + + /* Get baud value, based on baudrate and the internal clock frequency */ + enum status_code error_code = _sercom_get_sync_baud_val( + config->mode_specific.master.baudrate, + internal_clock, &baud); + + if (error_code != STATUS_OK) { + /* Baud rate calculation error, return status code */ + return STATUS_ERR_INVALID_ARG; + } + + spi_module->BAUD.reg = (uint8_t)baud; + } +# endif +# if CONF_SPI_SLAVE_ENABLE == true + if (config->mode == SPI_MODE_SLAVE) { + /* Set frame format */ + ctrla = config->mode_specific.slave.frame_format; + + /* Set address mode */ + ctrlb = config->mode_specific.slave.address_mode; + + /* Set address and address mask*/ + spi_module->ADDR.reg |= + (config->mode_specific.slave.address << SERCOM_SPI_ADDR_ADDR_Pos) | + (config->mode_specific.slave.address_mask << SERCOM_SPI_ADDR_ADDRMASK_Pos); + + if (config->mode_specific.slave.preload_enable) { + /* Enable pre-loading of shift register */ + ctrlb |= SERCOM_SPI_CTRLB_PLOADEN; + } + } +# endif + /* Set data order */ + ctrla |= config->data_order; + + /* Set clock polarity and clock phase */ + ctrla |= config->transfer_mode; + + /* Set MUX setting */ + ctrla |= config->mux_setting; + + /* Set SPI character size */ + ctrlb |= config->character_size; + + /* Set whether module should run in standby. */ + if (config->run_in_standby || system_is_debugger_present()) { + ctrla |= SERCOM_SPI_CTRLA_RUNSTDBY; + } + + if (config->receiver_enable) { + /* Enable receiver */ + ctrlb |= SERCOM_SPI_CTRLB_RXEN; + } +# ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT + if (config->select_slave_low_detect_enable) { + /* Enable Slave Select Low Detect */ + ctrlb |= SERCOM_SPI_CTRLB_SSDE; + } +# endif +# ifdef FEATURE_SPI_HARDWARE_SLAVE_SELECT + if (config->master_slave_select_enable) { + /* Enable Master Slave Select */ + ctrlb |= SERCOM_SPI_CTRLB_MSSEN; + } +# endif + /* Write CTRLA register */ + spi_module->CTRLA.reg |= ctrla; + + /* Write CTRLB register */ + spi_module->CTRLB.reg |= ctrlb; + + return STATUS_OK; +} + +#if SPI_CALLBACK_MODE == false +/** + * \internal Checks an SPI config against current set config + * + * This function will check that the config does not alter the + * configuration of the module. If the new config changes any + * setting, the initialization will be discarded. + * + * \param[in] module Pointer to the software instance struct + * \param[in] config Pointer to the configuration struct + * + * \return The status of the configuration. + * \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided + * \retval STATUS_ERR_DENIED If configuration was different from previous + * \retval STATUS_OK If the configuration was written + */ +static enum status_code _spi_check_config( + struct spi_module *const module, + const struct spi_config *const config) +{ + /* Sanity check arguments */ + Assert(module); + Assert(config); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + Sercom *const hw = module->hw; + + uint32_t pad_pinmuxes[] = { + config->pinmux_pad0, config->pinmux_pad1, + config->pinmux_pad2, config->pinmux_pad3 + }; + + /* Compare the current SERCOM pins against the user configuration */ + for (uint8_t pad = 0; pad < 4; pad++) { + uint32_t current_pinmux = pad_pinmuxes[pad]; + + if (current_pinmux == PINMUX_DEFAULT) { + current_pinmux = _sercom_get_default_pad(hw, pad); + } + + if (current_pinmux == PINMUX_UNUSED) { + continue; + } + + if ((current_pinmux & 0xFFFF) != + system_pinmux_pin_get_mux_position(current_pinmux >> 16)) { + module->hw = NULL; + return STATUS_ERR_DENIED; + } + } + +# if CONF_SPI_MASTER_ENABLE == true + /* Value to read BAUD register */ + uint16_t baud; + uint32_t external_clock = system_gclk_chan_get_hz(SERCOM_GCLK_ID); +# endif + /* Value to read CTRLA, CTRLB and ADDR register */ + uint32_t ctrla = 0; + uint32_t ctrlb = 0; +# if CONF_SPI_SLAVE_ENABLE == true + uint32_t addr = 0; +# endif + +# if CONF_SPI_MASTER_ENABLE == true + /* Find baud value and compare it */ + if (config->mode == SPI_MODE_MASTER) { + enum status_code error_code = _sercom_get_sync_baud_val( + config->mode_specific.master.baudrate, + external_clock, &baud); + + if (error_code != STATUS_OK) { + /* Baud rate calculation error, return status code */ + return STATUS_ERR_INVALID_ARG; + } + + if (spi_module->BAUD.reg != (uint8_t)baud) { + return STATUS_ERR_DENIED; + } + + ctrla |= SERCOM_SPI_CTRLA_MODE(0x3); + } +# endif + +# if CONF_SPI_SLAVE_ENABLE == true + if (config->mode == SPI_MODE_SLAVE) { + + /* Set frame format */ + ctrla |= config->mode_specific.slave.frame_format; + + /* Set address mode */ + ctrlb |= config->mode_specific.slave.address_mode; + + /* Set address and address mask*/ + addr |= (config->mode_specific.slave.address << SERCOM_SPI_ADDR_ADDR_Pos) | + (config->mode_specific.slave.address_mask << SERCOM_SPI_ADDR_ADDRMASK_Pos); + if (spi_module->CTRLA.reg != addr) { + return STATUS_ERR_DENIED; + } + + if (config->mode_specific.slave.preload_enable) { + /* Enable pre-loading of shift register */ + ctrlb |= SERCOM_SPI_CTRLB_PLOADEN; + } + ctrla |= SERCOM_SPI_CTRLA_MODE(0x2); + } +# endif + /* Set data order */ + ctrla |= config->data_order; + + /* Set clock polarity and clock phase */ + ctrla |= config->transfer_mode; + + /* Set MUX setting */ + ctrla |= config->mux_setting; + + /* Set SPI character size */ + ctrlb |= config->character_size; + + if (config->run_in_standby) { + /* Enable in sleep mode */ + ctrla |= SERCOM_SPI_CTRLA_RUNSTDBY; + } + + if (config->receiver_enable) { + /* Enable receiver */ + ctrlb |= SERCOM_SPI_CTRLB_RXEN; + } + +# ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT + if (config->select_slave_low_detect_enable) { + /* Enable Slave Select Low Detect */ + ctrlb |= SERCOM_SPI_CTRLB_SSDE; + } +# endif +# ifdef FEATURE_SPI_HARDWARE_SLAVE_SELECT + if (config->master_slave_select_enable) { + /* Enable Master Slave Select */ + ctrlb |= SERCOM_SPI_CTRLB_MSSEN; + } +# endif + + ctrla |= SERCOM_SPI_CTRLA_ENABLE; + + /* Check that same config is set */ + if (spi_module->CTRLA.reg == ctrla && + spi_module->CTRLB.reg == ctrlb) { + module->mode = config->mode; + module->character_size = config->character_size; + return STATUS_OK; + } + + /* Not same config, wipe module pointer and return */ + module->hw = NULL; + + return STATUS_ERR_DENIED; +} +#endif + +/** + * \brief Initializes the SERCOM SPI module + * + * This function will initialize the SERCOM SPI module, based on the values + * of the config struct. + * + * \param[out] module Pointer to the software instance struct + * \param[in] hw Pointer to hardware instance + * \param[in] config Pointer to the config struct + * + * \return Status of the initialization. + * \retval STATUS_OK Module initiated correctly + * \retval STATUS_ERR_DENIED If module is enabled + * \retval STATUS_BUSY If module is busy resetting + * \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided + */ +enum status_code spi_init( + struct spi_module *const module, + Sercom *const hw, + const struct spi_config *const config) +{ + + /* Sanity check arguments */ + Assert(module); + Assert(hw); + Assert(config); + + /* Initialize device instance */ + module->hw = hw; + + SercomSpi *const spi_module = &(module->hw->SPI); + + /* Check if module is enabled. */ + if (spi_module->CTRLA.reg & SERCOM_SPI_CTRLA_ENABLE) { +# if SPI_CALLBACK_MODE == false + /* Check if config is valid */ + return _spi_check_config(module, config); +# else + return STATUS_ERR_DENIED; +# endif + } + + /* Check if reset is in progress. */ + if (spi_module->CTRLA.reg & SERCOM_SPI_CTRLA_SWRST){ + return STATUS_BUSY; + } + + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + uint32_t pm_index, gclk_index; +#if (SAML21) || (SAMR30) + if (sercom_index == 5) { +# ifdef ID_SERCOM5 + pm_index = MCLK_APBDMASK_SERCOM5_Pos; + gclk_index = SERCOM5_GCLK_ID_CORE; +# else + return STATUS_ERR_INVALID_ARG; +# endif + } else { + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + } +#elif (SAMC21) + if (sercom_index == 5) { +# ifdef ID_SERCOM5 + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = SERCOM5_GCLK_ID_CORE; +# else + return STATUS_ERR_INVALID_ARG; +# endif + } else { + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + } +#elif (SAMC20) || (SAML22) + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; +#else + pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; +#endif + + /* Turn on module in PM */ +#if (SAML21) || (SAMR30) + if (sercom_index == 5) { +# ifdef ID_SERCOM5 + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBD, 1 << pm_index); +# else + return STATUS_ERR_INVALID_ARG; +# endif + } else { + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); + } +#else + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); +#endif + + /* Set up the GCLK for the module */ + struct system_gclk_chan_config gclk_chan_conf; + system_gclk_chan_get_config_defaults(&gclk_chan_conf); + gclk_chan_conf.source_generator = config->generator_source; + system_gclk_chan_set_config(gclk_index, &gclk_chan_conf); + system_gclk_chan_enable(gclk_index); + sercom_set_gclk_generator(config->generator_source, false); + +# if CONF_SPI_MASTER_ENABLE == true + if (config->mode == SPI_MODE_MASTER) { + /* Set the SERCOM in SPI master mode */ + spi_module->CTRLA.reg |= SERCOM_SPI_CTRLA_MODE(0x3); + } +# endif + +# if CONF_SPI_SLAVE_ENABLE == true + if (config->mode == SPI_MODE_SLAVE) { + /* Set the SERCOM in SPI slave mode */ + spi_module->CTRLA.reg |= SERCOM_SPI_CTRLA_MODE(0x2); + } +# endif + +#if SPI_CALLBACK_MODE == true + /* Temporary variables */ + uint8_t i; + uint8_t instance_index; + + /* Initialize parameters */ + for (i = 0; i < SPI_CALLBACK_N; i++) { + module->callback[i] = NULL; + } + module->tx_buffer_ptr = NULL; + module->rx_buffer_ptr = NULL; + module->remaining_tx_buffer_length = 0x0000; + module->remaining_rx_buffer_length = 0x0000; + module->registered_callback = 0x00; + module->enabled_callback = 0x00; + module->status = STATUS_OK; + module->dir = SPI_DIRECTION_IDLE; + module->locked = false; + /* + * Set interrupt handler and register SPI software module struct in + * look-up table + */ + instance_index = _sercom_get_sercom_inst_index(module->hw); + _sercom_set_handler(instance_index, _spi_interrupt_handler); + _sercom_instances[instance_index] = module; +#endif + + /* Write configuration to module and return status code */ + return _spi_set_config(module, config); +} + +/** + * \brief Reads buffer of \c length SPI characters + * + * This function will read a buffer of data from an SPI peripheral by sending + * dummy SPI character if in master mode, or by waiting for data in slave mode. + * + * \note If address matching is enabled for the slave, the first character + * received and placed in the buffer will be the address. + * + * \param[in] module Pointer to the software instance struct + * \param[out] rx_data Data buffer for received data + * \param[in] length Length of data to receive + * \param[in] dummy 8- or 9-bit dummy byte to shift out in master mode + * + * \return Status of the read operation. + * \retval STATUS_OK If the read was completed + * \retval STATUS_ABORTED If transaction was ended by master before + * the entire buffer was transferred + * \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided + * \retval STATUS_ERR_TIMEOUT If the operation was not completed within the + * timeout in slave mode + * \retval STATUS_ERR_DENIED If the receiver is not enabled + * \retval STATUS_ERR_OVERFLOW If the data is overflown + */ +enum status_code spi_read_buffer_wait( + struct spi_module *const module, + uint8_t *rx_data, + uint16_t length, + uint16_t dummy) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + +# if SPI_CALLBACK_MODE == true + if (module->status == STATUS_BUSY) { + /* Check if the SPI module is busy with a job */ + return STATUS_BUSY; + } +# endif + + /* Sanity check arguments */ + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + + if (!(module->receiver_enabled)) { + return STATUS_ERR_DENIED; + } +# if CONF_SPI_SLAVE_ENABLE == true + if ((module->mode == SPI_MODE_SLAVE) && (spi_is_write_complete(module))) { + /* Clear TX complete flag */ + _spi_clear_tx_complete_flag(module); + } +# endif + uint16_t rx_pos = 0; + + while (length--) { +# if CONF_SPI_MASTER_ENABLE == true + if (module->mode == SPI_MODE_MASTER) { + /* Wait until the module is ready to write a character */ + while (!spi_is_ready_to_write(module)) { + } + + /* Send dummy SPI character to read in master mode */ + spi_write(module, dummy); + } +# endif + +# if CONF_SPI_SLAVE_ENABLE == true + /* Start timeout period for slave */ + if (module->mode == SPI_MODE_SLAVE) { + for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { + if (spi_is_ready_to_read(module)) { + break; + } + } + /* Check if master has ended the transaction */ + if (spi_is_write_complete(module)) { + _spi_clear_tx_complete_flag(module); + return STATUS_ABORTED; + } + + if (!spi_is_ready_to_read(module)) { + /* Not ready to read data within timeout period */ + return STATUS_ERR_TIMEOUT; + } + } +# endif + + /* Wait until the module is ready to read a character */ + while (!spi_is_ready_to_read(module)) { + } + + uint16_t received_data = 0; + enum status_code retval = spi_read(module, &received_data); + + if (retval != STATUS_OK) { + /* Overflow, abort */ + return retval; + } + + /* Read value will be at least 8-bits long */ + rx_data[rx_pos++] = received_data; + + /* If 9-bit data, write next received byte to the buffer */ + if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { + rx_data[rx_pos++] = (received_data >> 8); + } + } + + return STATUS_OK; +} + +/** + * \brief Sends and reads a single SPI character + * + * This function will transfer a single SPI character via SPI and return the + * SPI character that is shifted into the shift register. + * + * In master mode the SPI character will be sent immediately and the received + * SPI character will be read as soon as the shifting of the data is + * complete. + * + * In slave mode this function will place the data to be sent into the transmit + * buffer. It will then block until an SPI master has shifted a complete + * SPI character, and the received data is available. + * + * \note The data to be sent might not be sent before the next transfer, as + * loading of the shift register is dependent on SCK. + * \note If address matching is enabled for the slave, the first character + * received and placed in the buffer will be the address. + * + * \param[in] module Pointer to the software instance struct + * \param[in] tx_data SPI character to transmit + * \param[out] rx_data Pointer to store the received SPI character + * + * \return Status of the operation. + * \retval STATUS_OK If the operation was completed + * \retval STATUS_ERR_TIMEOUT If the operation was not completed within the + * timeout in slave mode + * \retval STATUS_ERR_DENIED If the receiver is not enabled + * \retval STATUS_ERR_OVERFLOW If the incoming data is overflown + */ +enum status_code spi_transceive_wait( + struct spi_module *const module, + uint16_t tx_data, + uint16_t *rx_data) +{ + /* Sanity check arguments */ + Assert(module); + + if (!(module->receiver_enabled)) { + return STATUS_ERR_DENIED; + } + +# if SPI_CALLBACK_MODE == true + if (module->status == STATUS_BUSY) { + /* Check if the SPI module is busy with a job */ + return STATUS_BUSY; + } +# endif + +# if CONF_SPI_SLAVE_ENABLE == true + uint16_t j; +# endif + enum status_code retval = STATUS_OK; + +# if CONF_SPI_SLAVE_ENABLE == true + /* Start timeout period for slave */ + if (module->mode == SPI_MODE_SLAVE) { + for (j = 0; j <= SPI_TIMEOUT; j++) { + if (spi_is_ready_to_write(module)) { + break; + } else if (j == SPI_TIMEOUT) { + /* Not ready to write data within timeout period */ + return STATUS_ERR_TIMEOUT; + } + } + } +# endif + /* Wait until the module is ready to write the character */ + while (!spi_is_ready_to_write(module)) { + } + + /* Write data */ + spi_write(module, tx_data); + +# if CONF_SPI_SLAVE_ENABLE == true + /* Start timeout period for slave */ + if (module->mode == SPI_MODE_SLAVE) { + for (j = 0; j <= SPI_TIMEOUT; j++) { + if (spi_is_ready_to_read(module)) { + break; + } else if (j == SPI_TIMEOUT) { + /* Not ready to read data within timeout period */ + return STATUS_ERR_TIMEOUT; + } + } + } +# endif + + /* Wait until the module is ready to read the character */ + while (!spi_is_ready_to_read(module)) { + } + + /* Read data */ + retval = spi_read(module, rx_data); + + return retval; +} + + /** + * \brief Selects slave device + * + * This function will drive the slave select pin of the selected device low or + * high depending on the select Boolean. + * If slave address recognition is enabled, the address will be sent to the + * slave when selecting it. + * + * \param[in] module Pointer to the software module struct + * \param[in] slave Pointer to the attached slave + * \param[in] select Boolean stating if the slave should be selected or + * deselected + * + * \return Status of the operation. + * \retval STATUS_OK If the slave device was selected + * \retval STATUS_ERR_UNSUPPORTED_DEV If the SPI module is operating in slave + * mode + * \retval STATUS_BUSY If the SPI module is not ready to write + * the slave address + */ +enum status_code spi_select_slave( + struct spi_module *const module, + struct spi_slave_inst *const slave, + const bool select) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + Assert(slave); + + /* Check that the SPI module is operating in master mode */ + if (module->mode != SPI_MODE_MASTER) { + return STATUS_ERR_UNSUPPORTED_DEV; + } +# ifdef FEATURE_SPI_HARDWARE_SLAVE_SELECT + if(!(module->master_slave_select_enable)) +# endif + { + if (select) { + /* Check if address recognition is enabled */ + if (slave->address_enabled) { + /* Check if the module is ready to write the address */ + if (!spi_is_ready_to_write(module)) { + /* Not ready, do not select slave and return */ + port_pin_set_output_level(slave->ss_pin, true); + return STATUS_BUSY; + } + + /* Drive Slave Select low */ + port_pin_set_output_level(slave->ss_pin, false); + + /* Write address to slave */ + spi_write(module, slave->address); + + if (!(module->receiver_enabled)) { + /* Flush contents of shift register shifted back from slave */ + while (!spi_is_ready_to_read(module)) { + } + uint16_t flush = 0; + spi_read(module, &flush); + } + } else { + /* Drive Slave Select low */ + port_pin_set_output_level(slave->ss_pin, false); + } + } else { + /* Drive Slave Select high */ + port_pin_set_output_level(slave->ss_pin, true); + } + } + return STATUS_OK; +} + +/** + * \brief Sends a buffer of \c length SPI characters + * + * This function will send a buffer of SPI characters via the SPI + * and discard any data that is received. To both send and receive a buffer of + * data, use the \ref spi_transceive_buffer_wait function. + * + * Note that this function does not handle the _SS (slave select) pin(s) in + * master mode; this must be handled by the user application. + * + * \param[in] module Pointer to the software instance struct + * \param[in] tx_data Pointer to the buffer to transmit + * \param[in] length Number of SPI characters to transfer + * + * \return Status of the write operation. + * \retval STATUS_OK If the write was completed + * \retval STATUS_ABORTED If transaction was ended by master before + * entire buffer was transferred + * \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided + * \retval STATUS_ERR_TIMEOUT If the operation was not completed within the + * timeout in slave mode + */ +enum status_code spi_write_buffer_wait( + struct spi_module *const module, + const uint8_t *tx_data, + uint16_t length) +{ + /* Sanity check arguments */ + Assert(module); + +# if SPI_CALLBACK_MODE == true + if (module->status == STATUS_BUSY) { + /* Check if the SPI module is busy with a job */ + return STATUS_BUSY; + } +# endif + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + +# if CONF_SPI_SLAVE_ENABLE == true + if ((module->mode == SPI_MODE_SLAVE) && (spi_is_write_complete(module))) { + /* Clear TX complete flag */ + _spi_clear_tx_complete_flag(module); + } +# endif + + uint16_t tx_pos = 0; + uint16_t flush_length = length; + + /* Write block */ + while (length--) { +# if CONF_SPI_SLAVE_ENABLE == true + /* Start timeout period for slave */ + if (module->mode == SPI_MODE_SLAVE) { + for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { + if (spi_is_ready_to_write(module)) { + break; + } + } + /* Check if master has ended the transaction */ + if (spi_is_write_complete(module)) { + _spi_clear_tx_complete_flag(module); + return STATUS_ABORTED; + } + + if (!spi_is_ready_to_write(module)) { + /* Not ready to write data within timeout period */ + return STATUS_ERR_TIMEOUT; + } + } +# endif + + /* Wait until the module is ready to write a character */ + while (!spi_is_ready_to_write(module)) { + } + + /* Write value will be at least 8-bits long */ + uint16_t data_to_send = tx_data[tx_pos++]; + + /* If 9-bit data, get next byte to send from the buffer */ + if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { + data_to_send |= (tx_data[tx_pos++] << 8); + } + + /* Write the data to send */ + spi_write(module, data_to_send); + + if (module->receiver_enabled) { +# if CONF_SPI_SLAVE_ENABLE == true + /* Start timeout period for slave */ + if (module->mode == SPI_MODE_SLAVE) { + for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { + if (length && spi_is_ready_to_write(module)) { + data_to_send = tx_data[tx_pos++]; + /* If 9-bit data, get next byte to send from the buffer */ + if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { + data_to_send |= (tx_data[tx_pos++] << 8); + } + + /* Write the data to send */ + spi_write(module, data_to_send); + length--; + } + if (spi_is_ready_to_read(module)) { + break; + } + } + + /* Check if master has ended the transaction */ + if (spi_is_write_complete(module)) { + _spi_clear_tx_complete_flag(module); + return STATUS_ABORTED; + } + + if (!spi_is_ready_to_read(module)) { + /* Not ready to read data within timeout period */ + return STATUS_ERR_TIMEOUT; + } + } +# endif + + while (!spi_is_ready_to_read(module)) { + } + + /* Flush read buffer */ + uint16_t flush; + spi_read(module, &flush); + flush_length--; + } + } + +# if CONF_SPI_MASTER_ENABLE == true + if (module->mode == SPI_MODE_MASTER) { + /* Wait for last byte to be transferred */ + while (!spi_is_write_complete(module)) { + } + } +# endif + +# if CONF_SPI_SLAVE_ENABLE == true + if (module->mode == SPI_MODE_SLAVE) { + if (module->receiver_enabled) { + while (flush_length) { + /* Start timeout period for slave */ + for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { + if (spi_is_ready_to_read(module)) { + break; + } + } + if (!spi_is_ready_to_read(module)) { + /* Not ready to read data within timeout period */ + return STATUS_ERR_TIMEOUT; + } + /* Flush read buffer */ + uint16_t flush; + spi_read(module, &flush); + flush_length--; + } + } + } +# endif + return STATUS_OK; +} + +/** + * \brief Sends and receives a buffer of \c length SPI characters + * + * This function will send and receive a buffer of data via the SPI. + * + * In master mode the SPI characters will be sent immediately and the + * received SPI character will be read as soon as the shifting of the SPI + * character is complete. + * + * In slave mode this function will place the data to be sent into the transmit + * buffer. It will then block until an SPI master has shifted the complete + * buffer and the received data is available. + * + * \param[in] module Pointer to the software instance struct + * \param[in] tx_data Pointer to the buffer to transmit + * \param[out] rx_data Pointer to the buffer where received data will be stored + * \param[in] length Number of SPI characters to transfer + * + * \return Status of the operation. + * \retval STATUS_OK If the operation was completed + * \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided + * \retval STATUS_ERR_TIMEOUT If the operation was not completed within the + * timeout in slave mode + * \retval STATUS_ERR_DENIED If the receiver is not enabled + * \retval STATUS_ERR_OVERFLOW If the data is overflown + */ +enum status_code spi_transceive_buffer_wait( + struct spi_module *const module, + uint8_t *tx_data, + uint8_t *rx_data, + uint16_t length) +{ + /* Sanity check arguments */ + Assert(module); + +# if SPI_CALLBACK_MODE == true + if (module->status == STATUS_BUSY) { + /* Check if the SPI module is busy with a job */ + return STATUS_BUSY; + } +# endif + + /* Sanity check arguments */ + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + + if (!(module->receiver_enabled)) { + return STATUS_ERR_DENIED; + } + +# if CONF_SPI_SLAVE_ENABLE == true + if ((module->mode == SPI_MODE_SLAVE) && (spi_is_write_complete(module))) { + /* Clear TX complete flag */ + _spi_clear_tx_complete_flag(module); + } +# endif + + uint16_t tx_pos = 0; + uint16_t rx_pos = 0; + uint16_t rx_length = length; + + /* Send and receive buffer */ + while (length--) { +# if CONF_SPI_SLAVE_ENABLE == true + /* Start timeout period for slave */ + if (module->mode == SPI_MODE_SLAVE) { + for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { + if (spi_is_ready_to_write(module)) { + break; + } + } + /* Check if master has ended the transaction */ + if (spi_is_write_complete(module)) { + _spi_clear_tx_complete_flag(module); + return STATUS_ABORTED; + } + + if (!spi_is_ready_to_write(module)) { + /* Not ready to write data within timeout period */ + return STATUS_ERR_TIMEOUT; + } + } +# endif + + /* Wait until the module is ready to write a character */ + while (!spi_is_ready_to_write(module)) { + } + + /* Write value will be at least 8-bits long */ + uint16_t data_to_send = tx_data[tx_pos++]; + + /* If 9-bit data, get next byte to send from the buffer */ + if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { + data_to_send |= (tx_data[tx_pos++] << 8); + } + + /* Write the data to send */ + spi_write(module, data_to_send); + +# if CONF_SPI_SLAVE_ENABLE == true + /* Start timeout period for slave */ + if (module->mode == SPI_MODE_SLAVE) { + for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { + if (spi_is_ready_to_write(module)) { + data_to_send = tx_data[tx_pos++]; + /* If 9-bit data, get next byte to send from the buffer */ + if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { + data_to_send |= (tx_data[tx_pos++] << 8); + } + + /* Write the data to send */ + spi_write(module, data_to_send); + length--; + } + if (spi_is_ready_to_read(module)) { + break; + } + } + /* Check if master has ended the transaction */ + if (spi_is_write_complete(module)) { + _spi_clear_tx_complete_flag(module); + return STATUS_ABORTED; + } + + if (!spi_is_ready_to_read(module)) { + /* Not ready to read data within timeout period */ + return STATUS_ERR_TIMEOUT; + } + } +# endif + + /* Wait until the module is ready to read a character */ + while (!spi_is_ready_to_read(module)) { + } + + enum status_code retval; + uint16_t received_data = 0; + rx_length--; + + retval = spi_read(module, &received_data); + + if (retval != STATUS_OK) { + /* Overflow, abort */ + return retval; + } + + /* Read value will be at least 8-bits long */ + rx_data[rx_pos++] = received_data; + + /* If 9-bit data, write next received byte to the buffer */ + if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { + rx_data[rx_pos++] = (received_data >> 8); + } + } + +# if CONF_SPI_MASTER_ENABLE == true + if (module->mode == SPI_MODE_MASTER) { + /* Wait for last byte to be transferred */ + while (!spi_is_write_complete(module)) { + } + } +# endif + +# if CONF_SPI_SLAVE_ENABLE == true + if (module->mode == SPI_MODE_SLAVE) { + while (rx_length) { + /* Start timeout period for slave */ + for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { + if (spi_is_ready_to_read(module)) { + break; + } + } + if (!spi_is_ready_to_read(module)) { + /* Not ready to read data within timeout period */ + return STATUS_ERR_TIMEOUT; + } + enum status_code retval; + uint16_t received_data = 0; + rx_length--; + + retval = spi_read(module, &received_data); + + if (retval != STATUS_OK) { + /* Overflow, abort */ + return retval; + } + /* Read value will be at least 8-bits long */ + rx_data[rx_pos++] = received_data; + + /* If 9-bit data, write next received byte to the buffer */ + if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { + rx_data[rx_pos++] = (received_data >> 8); + } + } + } +# endif + return STATUS_OK; +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/spi/spi.h b/atmel-samd/asf/sam0/drivers/sercom/spi/spi.h new file mode 100644 index 000000000..01d21a21e --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/spi/spi.h @@ -0,0 +1,1807 @@ +/** + * \file + * + * \brief SAM Serial Peripheral Interface Driver + * + * Copyright (C) 2012-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 SPI_H_INCLUDED +#define SPI_H_INCLUDED + +/** + * \defgroup asfdoc_sam0_sercom_spi_group SAM Serial Peripheral Interface (SERCOM SPI) Driver + * + * This driver for Atmel® | SMART ARM®-based microcontrollers provides + * an interface for the configuration and management of the SERCOM module in + * its SPI mode to transfer SPI data frames. The following driver API modes + * are covered by this manual: + * + * - Polled APIs + * \if SPI_CALLBACK_MODE + * - Callback APIs + * \endif + * + * The following peripheral is used by this module: + * - SERCOM (Serial Communication Interface) + * + * 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 + * - Atmel | SMART SAM R30 + * + * The outline of this documentation is as follows: + * - \ref asfdoc_sam0_sercom_spi_prerequisites + * - \ref asfdoc_sam0_sercom_spi_module_overview + * - \ref asfdoc_sam0_sercom_spi_special_considerations + * - \ref asfdoc_sam0_sercom_spi_extra_info + * - \ref asfdoc_sam0_sercom_spi_examples + * - \ref asfdoc_sam0_sercom_spi_api_overview + * + * \section asfdoc_sam0_sercom_spi_prerequisites Prerequisites + * There are no prerequisites. + * + * + * \section asfdoc_sam0_sercom_spi_module_overview Module Overview + * The Serial Peripheral Interface (SPI) is a high-speed synchronous data + * transfer interface using three or four pins. It allows fast communication + * between a master device and one or more peripheral devices. + * + * A device connected to the bus must act as a master or a slave. The master + * initiates and controls all data transactions. + * The SPI master initiates a communication cycle by pulling low the Slave + * Select (SS) pin of the desired slave. The Slave Select pin is active low. + * Master and slave prepare data to be sent in their respective shift + * registers, and the master generates the required clock pulses on the SCK + * line to interchange data. Data is always shifted from master to slave on + * the Master Out - Slave In (MOSI) line, and from slave to master on the + * Master In - Slave Out (MISO) line. After each data transfer, the master can + * synchronize to the slave by pulling the SS line high. + * + * \subsection asfdoc_sam0_sercom_spi_module_features Driver Feature Macro Definition + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Driver feature macroSupported devices
FEATURE_SPI_SLAVE_SELECT_LOW_DETECTSAM D21/R21/D10/D11/L21/L22/DA1/C20/C21/R30
FEATURE_SPI_HARDWARE_SLAVE_SELECTSAM D21/R21/D10/D11/L21/L22/DA1/C20/C21/R30
FEATURE_SPI_ERROR_INTERRUPTSAM D21/R21/D10/D11/L21/L22/DA1/C20/C21/R30
FEATURE_SPI_SYNC_SCHEME_VERSION_2SAM D21/R21/D10/D11/L21/L22/DA1/C20/C21/R30
+ * \note The specific features are only available in the driver when the + * selected device supports those features. + * + * \subsection asfdoc_sam0_sercom_spi_bus SPI Bus Connection + * In \ref asfdoc_sam0_spi_connection_example "the figure below", the + * connection between one master and one slave is shown. + * + * \anchor asfdoc_sam0_spi_connection_example + * \dot + * digraph spi_slaves_par { + * subgraph cluster_spi_master { + * shift_reg [label="Shift register", shape=box]; + * mosi_m [label="MOSI", shape=none]; + * miso_m [label="MISO", shape=none]; + * sck_m [label="SCK", shape=none]; + * ss_m [label="GPIO pin", shape=none]; + * {rank=same; mosi_m miso_m sck_m ss_m} + * label="SPI Master"; + * } + * subgraph cluster_spi_slave { + * mosi_s [label="MOSI", shape=none]; + * miso_s [label="MISO", shape=none]; + * sck_s [label="SCK", shape=none]; + * ss_s [label="SS", shape=none]; + * shift_reg_s [label="Shift register", shape=box]; + * {rank=same; mosi_s miso_s sck_s ss_s} + * label="SPI Slave"; + * rankdir=LR; + * } + * shift_reg:e -> mosi_m:w [label=""]; + * mosi_m:e -> mosi_s:w [label=""]; + * mosi_s:e -> shift_reg_s:w [label=""]; + * miso_s:w -> miso_m:e [label=""]; + * sck_m -> sck_s; + * ss_m -> ss_s; + * shift_reg_s:se -> miso_s:e [label=""]; + * miso_m:w -> shift_reg:sw [label=""]; + * rankdir=LR; + * } + * \enddot + * + * The different lines are as follows: + * - \b MISO Master Input Slave Output. The line where the data is shifted + * out from the slave and into the master. + * - \b MOSI Master Output Slave Input. The line where the data is shifted + * out from the master and into the slave. + * - \b SCK Serial Clock. Generated by the master device. + * - \b SS Slave Select. To initiate a transaction, the master must pull this + * line low. + * + * If the bus consists of several SPI slaves, they can be connected in parallel + * and the SPI master can use general I/O pins to control separate SS lines to + * each slave on the bus. + * + * It is also possible to connect all slaves in series. In this configuration, + * a common SS is provided to \c N slaves, enabling them simultaneously. The + * MISO from the \c N-1 slaves is connected to the MOSI on the next slave. The + * \c Nth slave connects its MISO back to the master. For a + * complete transaction, the master must shift \c N+1 characters. + * + * \subsection asfdoc_sam0_sercom_spi_chsize SPI Character Size + * The SPI character size is configurable to eight or nine bits. + * + * \subsection asfdoc_sam0_sercom_spi_master_mode Master Mode + * When configured as a master, the SS pin will be configured as an output. + * + * \subsubsection asfdoc_sam0_sercom_spi_master_mode_data_transfer Data Transfer + * Writing a character will start the SPI clock generator, and + * the character is transferred to the shift register when the shift + * register is empty. + * Once this is done, a new character can be written. + * As each character is shifted out from the master, a character is shifted in + * from the slave. If the receiver is enabled, the data is moved to the receive + * buffer at the completion of the frame and can be read. + * + * \subsection asfdoc_sam0_sercom_spi_slave_mode Slave Mode + * When configured as a slave, the SPI interface will remain inactive with MISO + * tri-stated as long as the SS pin is driven high. + * + * \subsubsection asfdoc_sam0_sercom_spi_slave_mode_data_transfer_slave Data Transfer + * The data register can be updated at any time. + * As the SPI slave shift register is clocked by SCK, a minimum of three SCK + * cycles are needed from the time new data is written, until the character is + * ready to be shifted out. If the shift register has not been loaded with + * data, the current contents will be transmitted. + * + * If constant transmission of data is needed in SPI slave mode, the system + * clock should be faster than SCK. + * If the receiver is enabled, the received character can be read from the + * receive buffer. When SS line is driven high, the slave will not receive any + * additional data. + * + * \subsubsection asfdoc_sam0_sercom_spi_slave_mode_addr_recognition Address Recognition + * When the SPI slave is configured with address recognition, the first + * character in a transaction is checked for an address match. If there is a + * match, the MISO output is enabled and the transaction is processed. + * If the address does not match, the complete transaction is ignored. + * + * If the device is asleep, it can be woken up by an address match in order + * to process the transaction. + * + * \note In master mode, an address packet is written by the + * \ref spi_select_slave function if the address_enabled configuration is + * set in the \ref spi_slave_inst_config struct. + * + * \subsection asfdoc_sam0_sercom_spi_data_modes Data Modes + * There are four combinations of SCK phase and polarity with respect to + * serial data. \ref asfdoc_sam0_spi_mode_table "The table below" shows the + * clock polarity (CPOL) and clock phase (CPHA) in the different modes. + * Leading edge is the first clock edge in a clock cycle and + * trailing edge is the last clock edge in a clock cycle. + * + * \anchor asfdoc_sam0_spi_mode_table + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
SPI Data Modes
ModeCPOLCPHALeading EdgeTrailing Edge
0 0 0 Rising, Sample Falling, Setup
1 0 1 Rising, Setup Falling, Sample
2 1 0 Falling, Sample Rising, Setup
3 1 1 Falling, Setup Rising, Sample
+ * + * + * \subsection asfdoc_sam0_sercom_spi_pads SERCOM Pads + * The SERCOM pads are automatically configured as seen in + * \ref asfdoc_sam0_spi_sercom_pad_table "the table below". If the receiver + * is disabled, the data input (MISO for master, MOSI for slave) can be used + * for other purposes. + * + * In master mode, the SS pin(s) must be configured using the \ref spi_slave_inst + * struct. + * + * \anchor asfdoc_sam0_spi_sercom_pad_table + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
SERCOM SPI Pad Usages
Pin Master SPI Slave SPI
MOSI Output Input
MISO Input Output
SCK Output Input
SS User defined output enable Input
+ * + * \subsection asfdoc_sam0_sercom_spi_sleep_modes Operation in Sleep Modes + * The SPI module can operate in all sleep modes by setting the run_in_standby + * option in the \ref spi_config struct. The operation in slave and master mode + * is shown in the table below. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
run_in_standby Slave Master
false Disabled, all reception is dropped GCLK is disabled when master is idle, wake on transmit complete
true Wake on reception GCLK is enabled while in sleep modes, wake on all interrupts
+ * + * \subsection asfdoc_sam0_sercom_spi_clock_generation Clock Generation + * In SPI master mode, the clock (SCK) is generated internally using the + * SERCOM baudrate generator. In SPI slave mode, the clock is provided by + * an external master on the SCK pin. This clock is used to directly clock + * the SPI shift register. + * + * \section asfdoc_sam0_sercom_spi_special_considerations Special Considerations + * \subsection pin_mux pinmux Settings + * The pin MUX settings must be configured properly, as not all settings + * can be used in different modes of operation. + * + * \section asfdoc_sam0_sercom_spi_extra_info Extra Information + * For extra information, see \ref asfdoc_sam0_sercom_spi_extra. This includes: + * - \ref asfdoc_sam0_sercom_spi_extra_acronyms + * - \ref asfdoc_sam0_sercom_spi_extra_dependencies + * - \ref asfdoc_sam0_sercom_spi_extra_workarounds + * - \ref asfdoc_sam0_sercom_spi_extra_history + * + * \section asfdoc_sam0_sercom_spi_examples Examples + * + * For a list of examples related to this driver, see + * \ref asfdoc_sam0_sercom_spi_exqsg. + * + * \section asfdoc_sam0_sercom_spi_api_overview API Overview + * @{ + */ + +#include +#include +#include +#include +#include +#include + +# if SPI_CALLBACK_MODE == true +# include +# endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if (CONF_SPI_MASTER_ENABLE == false) && (CONF_SPI_SLAVE_ENABLE == false) +#error "Not possible compile SPI driver, invalid driver configuration. Make sure that either/both CONF_SPI_MASTER_ENABLE/CONF_SPI_SLAVE_ENABLE is set to true." +#endif + +/** + * \name Driver Feature Definition + * Define SERCOM SPI features set according to different device family. + * @{ + */ +# if (SAMD21) || (SAMR21) || (SAMD11) || (SAMD10) || (SAML21) || (SAMDA1) || \ + (SAML22) || (SAMC20) || (SAMC21) || (SAMD09) || (SAMR30) || defined(__DOXYGEN__) +/** SPI slave select low detection. */ +# define FEATURE_SPI_SLAVE_SELECT_LOW_DETECT +/** Slave select can be controlled by hardware. */ +# define FEATURE_SPI_HARDWARE_SLAVE_SELECT +/** SPI with error detect feature. */ +# define FEATURE_SPI_ERROR_INTERRUPT +/** SPI sync scheme version 2. */ +# define FEATURE_SPI_SYNC_SCHEME_VERSION_2 +# endif +/*@}*/ + +# ifndef PINMUX_DEFAULT +/** Default pinmux. */ +# define PINMUX_DEFAULT 0 +# endif + +# ifndef PINMUX_UNUSED +/** Unused pinmux. */ +# define PINMUX_UNUSED 0xFFFFFFFF +# endif + +# ifndef SPI_TIMEOUT +/** SPI timeout value. */ +# define SPI_TIMEOUT 10000 +# endif + +# if SPI_CALLBACK_MODE == true +/** + * \brief SPI Callback enum + * + * Callbacks for SPI callback driver. + * + * \note For slave mode, these callbacks will be called when a transaction + * is ended by the master pulling Slave Select high. + * + */ +enum spi_callback { + /** Callback for buffer transmitted */ + SPI_CALLBACK_BUFFER_TRANSMITTED, + /** Callback for buffer received */ + SPI_CALLBACK_BUFFER_RECEIVED, + /** Callback for buffers transceived */ + SPI_CALLBACK_BUFFER_TRANSCEIVED, + /** Callback for error */ + SPI_CALLBACK_ERROR, + /** + * Callback for transmission ended by master before the entire buffer was + * read or written from slave + */ + SPI_CALLBACK_SLAVE_TRANSMISSION_COMPLETE, +# ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT + /** Callback for slave select low */ + SPI_CALLBACK_SLAVE_SELECT_LOW, +# endif +# ifdef FEATURE_SPI_ERROR_INTERRUPT + /** Callback for combined error happen */ + SPI_CALLBACK_COMBINED_ERROR, +# endif +# if !defined(__DOXYGEN__) + /** Number of available callbacks */ + SPI_CALLBACK_N, +# endif +}; +# endif + +#if SPI_CALLBACK_MODE == true +# if !defined(__DOXYGEN__) +/** + * \internal SPI transfer directions + */ +enum _spi_direction { + /** Transfer direction is read */ + SPI_DIRECTION_READ, + /** Transfer direction is write */ + SPI_DIRECTION_WRITE, + /** Transfer direction is read and write */ + SPI_DIRECTION_BOTH, + /** No transfer */ + SPI_DIRECTION_IDLE, +}; +# endif +#endif + +/** + * \brief SPI Interrupt Flags + * + * Interrupt flags for the SPI module. + * + */ +enum spi_interrupt_flag { + /** + * This flag is set when the contents of the data register has been moved + * to the shift register and the data register is ready for new data + */ + SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY = SERCOM_SPI_INTFLAG_DRE, + /** + * This flag is set when the contents of the shift register has been + * shifted out + */ + SPI_INTERRUPT_FLAG_TX_COMPLETE = SERCOM_SPI_INTFLAG_TXC, + /** This flag is set when data has been shifted into the data register */ + SPI_INTERRUPT_FLAG_RX_COMPLETE = SERCOM_SPI_INTFLAG_RXC, +# ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT + /** This flag is set when slave select low */ + SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW = SERCOM_SPI_INTFLAG_SSL, +# endif +# ifdef FEATURE_SPI_ERROR_INTERRUPT + /** This flag is set when combined error happen */ + SPI_INTERRUPT_FLAG_COMBINED_ERROR = SERCOM_SPI_INTFLAG_ERROR, +# endif +}; + +/** + * \brief SPI transfer modes enum + * + * SPI transfer mode. + */ +enum spi_transfer_mode { + /** Mode 0. Leading edge: rising, sample. Trailing edge: falling, setup */ + SPI_TRANSFER_MODE_0 = 0, + /** Mode 1. Leading edge: rising, setup. Trailing edge: falling, sample */ + SPI_TRANSFER_MODE_1 = SERCOM_SPI_CTRLA_CPHA, + /** Mode 2. Leading edge: falling, sample. Trailing edge: rising, setup */ + SPI_TRANSFER_MODE_2 = SERCOM_SPI_CTRLA_CPOL, + /** Mode 3. Leading edge: falling, setup. Trailing edge: rising, sample */ + SPI_TRANSFER_MODE_3 = SERCOM_SPI_CTRLA_CPHA | SERCOM_SPI_CTRLA_CPOL, +}; + +/** + * \brief SPI frame format enum + * + * Frame format for slave mode. + */ +enum spi_frame_format { + /** SPI frame */ + SPI_FRAME_FORMAT_SPI_FRAME = SERCOM_SPI_CTRLA_FORM(0), + /** SPI frame with address */ + SPI_FRAME_FORMAT_SPI_FRAME_ADDR = SERCOM_SPI_CTRLA_FORM(2), +}; + +/** + * \brief SPI signal MUX settings + * + * Set the functionality of the SERCOM pins. As not all combinations can be used + * in different modes of operation, proper combinations must be chosen according + * to the rest of the configuration. + * + * \note In master operation: DI is MISO, DO is MOSI. + * In slave operation: DI is MOSI, DO is MISO. + * + * See \ref asfdoc_sam0_sercom_spi_mux_settings for a description of the + * various MUX setting options. + */ +enum spi_signal_mux_setting { + /** SPI MUX combination A. DOPO: 0x0, DIPO: 0x0 */ + SPI_SIGNAL_MUX_SETTING_A = + (0x0 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x0 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination B. DOPO: 0x0, DIPO: 0x1 */ + SPI_SIGNAL_MUX_SETTING_B = + (0x0 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x1 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination C. DOPO: 0x0, DIPO: 0x2 */ + SPI_SIGNAL_MUX_SETTING_C = + (0x0 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x2 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination D. DOPO: 0x0, DIPO: 0x3 */ + SPI_SIGNAL_MUX_SETTING_D = + (0x0 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x3 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination E. DOPO: 0x1, DIPO: 0x0 */ + SPI_SIGNAL_MUX_SETTING_E = + (0x1 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x0 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination F. DOPO: 0x1, DIPO: 0x1 */ + SPI_SIGNAL_MUX_SETTING_F = + (0x1 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x1 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination G. DOPO: 0x1, DIPO: 0x2 */ + SPI_SIGNAL_MUX_SETTING_G = + (0x1 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x2 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination H. DOPO: 0x1, DIPO: 0x3 */ + SPI_SIGNAL_MUX_SETTING_H = + (0x1 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x3 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination I. DOPO: 0x2, DIPO: 0x0 */ + SPI_SIGNAL_MUX_SETTING_I = + (0x2 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x0 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination J. DOPO: 0x2, DIPO: 0x1 */ + SPI_SIGNAL_MUX_SETTING_J = + (0x2 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x1 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination K. DOPO: 0x2, DIPO: 0x2 */ + SPI_SIGNAL_MUX_SETTING_K = + (0x2 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x2 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination L. DOPO: 0x2, DIPO: 0x3 */ + SPI_SIGNAL_MUX_SETTING_L = + (0x2 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x3 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination M. DOPO: 0x3, DIPO: 0x0 */ + SPI_SIGNAL_MUX_SETTING_M = + (0x3 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x0 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination N. DOPO: 0x3, DIPO: 0x1 */ + SPI_SIGNAL_MUX_SETTING_N = + (0x3 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x1 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination O. DOPO: 0x3, DIPO: 0x2 */ + SPI_SIGNAL_MUX_SETTING_O = + (0x3 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x2 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination P. DOPO: 0x3, DIPO: 0x3 */ + SPI_SIGNAL_MUX_SETTING_P = + (0x3 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x3 << SERCOM_SPI_CTRLA_DIPO_Pos), +}; + +/** + * \brief SPI address modes enum + * + * For slave mode when using the SPI frame with address format. + * + */ +enum spi_addr_mode { + /** + * \c address_mask in the \ref spi_config struct is used as a mask to the register + */ + SPI_ADDR_MODE_MASK = SERCOM_SPI_CTRLB_AMODE(0), + /** + * The slave responds to the two unique addresses in \c address and + * \c address_mask in the \ref spi_config struct + */ + SPI_ADDR_MODE_UNIQUE = SERCOM_SPI_CTRLB_AMODE(1), + /** + * The slave responds to the range of addresses between and including \c address + * and \c address_mask in the \ref spi_config struct + */ + SPI_ADDR_MODE_RANGE = SERCOM_SPI_CTRLB_AMODE(2), +}; + +/** + * \brief SPI modes enum + * + * SPI mode selection. + */ +enum spi_mode { + /** Master mode */ + SPI_MODE_MASTER = 1, + /** Slave mode */ + SPI_MODE_SLAVE = 0, +}; + +/** + * \brief SPI data order enum + * + * SPI data order. + * + */ +enum spi_data_order { + /** The LSB of the data is transmitted first */ + SPI_DATA_ORDER_LSB = SERCOM_SPI_CTRLA_DORD, + /** The MSB of the data is transmitted first */ + SPI_DATA_ORDER_MSB = 0, +}; + +/** + * \brief SPI character size enum + * + * SPI character size. + * + */ +enum spi_character_size { + /** 8-bit character */ + SPI_CHARACTER_SIZE_8BIT = SERCOM_SPI_CTRLB_CHSIZE(0), + /** 9-bit character */ + SPI_CHARACTER_SIZE_9BIT = SERCOM_SPI_CTRLB_CHSIZE(1), +}; + +# if SPI_CALLBACK_MODE == true +/** Prototype for the device instance */ +struct spi_module; + +/** Type of the callback functions */ +typedef void (*spi_callback_t)(struct spi_module *const module); + +# if !defined(__DOXYGEN__) +/** Prototype for the interrupt handler */ +extern void _spi_interrupt_handler(uint8_t instance); +# endif +# endif + +/** + * \brief SERCOM SPI driver software device instance structure. + * + * SERCOM SPI driver 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 spi_module { +# if !defined(__DOXYGEN__) + /** SERCOM hardware module */ + Sercom *hw; + /** Module lock */ + volatile bool locked; + /** SPI mode */ + enum spi_mode mode; + /** SPI character size */ + enum spi_character_size character_size; + /** Receiver enabled */ + bool receiver_enabled; +# ifdef FEATURE_SPI_HARDWARE_SLAVE_SELECT + /** Enable Hardware Slave Select */ + bool master_slave_select_enable; +# endif +# if SPI_CALLBACK_MODE == true + /** Direction of transaction */ + volatile enum _spi_direction dir; + /** Array to store callback function pointers in */ + spi_callback_t callback[SPI_CALLBACK_N]; + /** Buffer pointer to where the next received character will be put */ + volatile uint8_t *rx_buffer_ptr; + /** Buffer pointer to where the next character will be transmitted from + **/ + volatile uint8_t *tx_buffer_ptr; + /** Remaining characters to receive */ + volatile uint16_t remaining_rx_buffer_length; + /** Remaining dummy characters to send when reading */ + volatile uint16_t remaining_dummy_buffer_length; + /** Remaining characters to transmit */ + volatile uint16_t remaining_tx_buffer_length; + /** Bit mask for callbacks registered */ + uint8_t registered_callback; + /** Bit mask for callbacks enabled */ + uint8_t enabled_callback; + /** Holds the status of the ongoing or last operation */ + volatile enum status_code status; +# endif +# endif +}; + +/** + * \brief SPI peripheral slave instance structure + * + * SPI peripheral slave software instance structure, used to configure the + * correct SPI transfer mode settings for an attached slave. See + * \ref spi_select_slave. + */ +struct spi_slave_inst { + /** Pin to use as slave select */ + uint8_t ss_pin; + /** Address recognition enabled in slave device */ + bool address_enabled; + /** Address of slave device */ + uint8_t address; +}; + +/** + * \brief SPI peripheral slave configuration structure + * + * SPI Peripheral slave configuration structure. + */ +struct spi_slave_inst_config { + /** Pin to use as slave select */ + uint8_t ss_pin; + /** Enable address */ + bool address_enabled; + /** Address of slave */ + uint8_t address; +}; + +/** + * \brief SPI Master configuration structure + * + * SPI Master configuration structure. + */ +struct spi_master_config { + /** Baud rate */ + uint32_t baudrate; +}; + +/** + * \brief SPI slave configuration structure + * + * SPI slave configuration structure. + */ +struct spi_slave_config { + /** Frame format */ + enum spi_frame_format frame_format; + /** Address mode */ + enum spi_addr_mode address_mode; + /** Address */ + uint8_t address; + /** Address mask */ + uint8_t address_mask; + /** Preload data to the shift register while SS is high */ + bool preload_enable; +}; + +/** + * \brief SPI configuration structure + * + * Configuration structure for an SPI instance. This structure should be + * initialized by the \ref spi_get_config_defaults function before being + * modified by the user application. + */ +struct spi_config { + /** SPI mode */ + enum spi_mode mode; + /** Data order */ + enum spi_data_order data_order; + /** Transfer mode */ + enum spi_transfer_mode transfer_mode; + /** MUX setting */ + enum spi_signal_mux_setting mux_setting; + /** SPI character size */ + enum spi_character_size character_size; + /** Enabled in sleep modes */ + bool run_in_standby; + /** Enable receiver */ + bool receiver_enable; +# ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT + /** Enable Slave Select Low Detect */ + bool select_slave_low_detect_enable; +# endif +# ifdef FEATURE_SPI_HARDWARE_SLAVE_SELECT + /** Enable Master Slave Select */ + bool master_slave_select_enable; +# endif + /** Union for slave or master specific configuration */ + union { + /** Slave specific configuration */ + struct spi_slave_config slave; + /** Master specific configuration */ + struct spi_master_config master; + } mode_specific; + /** GCLK generator to use as clock source */ + enum gclk_generator generator_source; + /** PAD0 pinmux */ + uint32_t pinmux_pad0; + /** PAD1 pinmux */ + uint32_t pinmux_pad1; + /** PAD2 pinmux */ + uint32_t pinmux_pad2; + /** PAD3 pinmux */ + uint32_t pinmux_pad3; +}; + +/** + * \brief Determines if the SPI module is currently synchronizing to the bus. + * + * This function will check 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 the module until it + * is ready. + * + * \param[in] module SPI hardware module + * + * \return Synchronization status of the underlying hardware module. + * \retval true Module synchronization is ongoing + * \retval false Module synchronization is not ongoing + * + */ +static inline bool spi_is_syncing( + struct spi_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + +# ifdef FEATURE_SPI_SYNC_SCHEME_VERSION_2 + /* Return synchronization status */ + return (spi_module->SYNCBUSY.reg); +# else + /* Return synchronization status */ + return (spi_module->STATUS.reg & SERCOM_SPI_STATUS_SYNCBUSY); +# endif +} + +/** + * \name Driver Initialization and Configuration + * @{ + */ + +/** + * \brief Initializes an SPI configuration structure to default values + * + * This function will initialize a given SPI 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 Master mode enabled + * \li MSB of the data is transmitted first + * \li Transfer mode 0 + * \li MUX Setting D + * \li Character size eight bits + * \li Not enabled in sleep mode + * \li Receiver enabled + * \li Baudrate 100000 + * \li Default pinmux settings for all pads + * \li GCLK generator 0 + * + * \param[out] config Configuration structure to initialize to default values + */ +static inline void spi_get_config_defaults( + struct spi_config *const config) +{ + /* Sanity check arguments */ + Assert(config); + + /* Default configuration values */ + config->mode = SPI_MODE_MASTER; + config->data_order = SPI_DATA_ORDER_MSB; + config->transfer_mode = SPI_TRANSFER_MODE_0; + config->mux_setting = SPI_SIGNAL_MUX_SETTING_D; + config->character_size = SPI_CHARACTER_SIZE_8BIT; + config->run_in_standby = false; + config->receiver_enable = true; +# ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT + config->select_slave_low_detect_enable= true; +# endif +# ifdef FEATURE_SPI_HARDWARE_SLAVE_SELECT + config->master_slave_select_enable= false; +# endif + config->generator_source = GCLK_GENERATOR_0; + + /* Clear mode specific config */ + memset(&(config->mode_specific), 0, sizeof(config->mode_specific)); + + /* Master config defaults */ + config->mode_specific.master.baudrate = 100000; + + /* pinmux config defaults */ + config->pinmux_pad0 = PINMUX_DEFAULT; + config->pinmux_pad1 = PINMUX_DEFAULT; + config->pinmux_pad2 = PINMUX_DEFAULT; + config->pinmux_pad3 = PINMUX_DEFAULT; + +}; + +/** + * \brief Initializes an SPI peripheral slave device configuration structure to default values + * + * This function will initialize a given SPI slave device 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 Slave Select on GPIO pin 10 + * \li Addressing not enabled + * + * \param[out] config Configuration structure to initialize to default values + */ +static inline void spi_slave_inst_get_config_defaults( + struct spi_slave_inst_config *const config) +{ + Assert(config); + + config->ss_pin = 10; + config->address_enabled = false; + config->address = 0; +} + +/** + * \brief Attaches an SPI peripheral slave + * + * This function will initialize the software SPI peripheral slave, based on + * the values of the config struct. The slave can then be selected and + * optionally addressed by the \ref spi_select_slave function. + * + * \param[out] slave Pointer to the software slave instance struct + * \param[in] config Pointer to the config struct + * + */ +static inline void spi_attach_slave( + struct spi_slave_inst *const slave, + const struct spi_slave_inst_config *const config) +{ + Assert(slave); + Assert(config); + + slave->ss_pin = config->ss_pin; + slave->address_enabled = config->address_enabled; + slave->address = config->address; + + /* Get default config for pin */ + struct port_config pin_conf; + port_get_config_defaults(&pin_conf); + + /* Edit config to set the pin as output */ + pin_conf.direction = PORT_PIN_DIR_OUTPUT; + + /* Set config on Slave Select pin */ + port_pin_set_config(slave->ss_pin, &pin_conf); + port_pin_set_output_level(slave->ss_pin, true); +} + +enum status_code spi_init( + struct spi_module *const module, + Sercom *const hw, + const struct spi_config *const config); + +/** @} */ + +/** + * \name Enable/Disable + * @{ + */ + +/** + * \brief Enables the SERCOM SPI module + * + * This function will enable the SERCOM SPI module. + * + * \param[in,out] module Pointer to the software instance struct + */ +static inline void spi_enable( + struct spi_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + +# if SPI_CALLBACK_MODE == true + system_interrupt_enable(_sercom_get_interrupt_vector(module->hw)); +# endif + + while (spi_is_syncing(module)) { + /* Wait until the synchronization is complete */ + } + + /* Enable SPI */ + spi_module->CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE; +} + +/** + * \brief Disables the SERCOM SPI module + * + * This function will disable the SERCOM SPI module. + * + * \param[in,out] module Pointer to the software instance struct + */ +static inline void spi_disable( + struct spi_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + +# if SPI_CALLBACK_MODE == true + system_interrupt_disable(_sercom_get_interrupt_vector(module->hw)); +# endif + + while (spi_is_syncing(module)) { + /* Wait until the synchronization is complete */ + } + + /* Disbale interrupt */ + spi_module->INTENCLR.reg = SERCOM_SPI_INTENCLR_MASK; + /* Clear interrupt flag */ + spi_module->INTFLAG.reg = SERCOM_SPI_INTFLAG_MASK; + + /* Disable SPI */ + spi_module->CTRLA.reg &= ~SERCOM_SPI_CTRLA_ENABLE; +} + +void spi_reset( + struct spi_module *const module); + +/** @} */ + +enum status_code spi_set_baudrate( + struct spi_module *const module, + uint32_t baudrate); + +/** + * \name Lock/Unlock + * @{ + */ + +/** + * \brief Attempt to get lock on driver instance + * + * This function checks the instance's lock, which indicates whether or not it + * is currently in use, and sets the lock if it was not already set. + * + * The purpose of this is to enable exclusive access to driver instances, so + * that, e.g., transactions by different services will not interfere with each + * other. + * + * \param[in,out] module Pointer to the driver instance to lock + * + * \retval STATUS_OK If the module was locked + * \retval STATUS_BUSY If the module was already locked + */ +static inline enum status_code spi_lock(struct spi_module *const module) +{ + enum status_code status; + + system_interrupt_enter_critical_section(); + + if (module->locked) { + status = STATUS_BUSY; + } else { + module->locked = true; + status = STATUS_OK; + } + + system_interrupt_leave_critical_section(); + + return status; +} + +/** + * \brief Unlock driver instance + * + * This function clears the instance lock, indicating that it is available for + * use. + * + * \param[in,out] module Pointer to the driver instance to lock + * + * \retval STATUS_OK If the module was locked + * \retval STATUS_BUSY If the module was already locked + */ +static inline void spi_unlock(struct spi_module *const module) +{ + module->locked = false; +} + +/** @} */ + +/** + * \name Ready to Write/Read + * @{ + */ + + /** + * \brief Checks if the SPI in master mode has shifted out last data, or if the master has ended the transfer in slave mode. + * + * This function will check if the SPI master module has shifted out last data, + * or if the slave select pin has been drawn high by the master for the SPI + * slave module. + * + * \param[in] module Pointer to the software instance struct + * + * \return Indication of whether any writes are ongoing. + * \retval true If the SPI master module has shifted out data, or slave select + * has been drawn high for SPI slave + * \retval false If the SPI master module has not shifted out data + */ +static inline bool spi_is_write_complete( + struct spi_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + + /* Check interrupt flag */ + return (spi_module->INTFLAG.reg & SERCOM_SPI_INTFLAG_TXC); +} + + /** + * \brief Checks if the SPI module is ready to write data + * + * This function will check if the SPI module is ready to write data. + * + * \param[in] module Pointer to the software instance struct + * + * \return Indication of whether the module is ready to read data or not. + * \retval true If the SPI module is ready to write data + * \retval false If the SPI module is not ready to write data + */ +static inline bool spi_is_ready_to_write( + struct spi_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + + /* Check interrupt flag */ + return (spi_module->INTFLAG.reg & SERCOM_SPI_INTFLAG_DRE); +} + +/** + * \brief Checks if the SPI module is ready to read data + * + * This function will check if the SPI module is ready to read data. + * + * \param[in] module Pointer to the software instance struct + * + * \return Indication of whether the module is ready to read data or not. + * \retval true If the SPI module is ready to read data + * \retval false If the SPI module is not ready to read data + */ +static inline bool spi_is_ready_to_read( + struct spi_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + + /* Check interrupt flag */ + return (spi_module->INTFLAG.reg & SERCOM_SPI_INTFLAG_RXC); +} +/** @} */ + +/** + * \name Read/Write + * @{ + */ + + /** + * \brief Transfers a single SPI character + * + * This function will send a single SPI character via SPI and ignore any data + * shifted in by the connected device. To both send and receive data, use the + * \ref spi_transceive_wait function or use the \ref spi_read function after + * writing a character. The \ref spi_is_ready_to_write function + * should be called before calling this function. + * + * Note that this function does not handle the SS (Slave Select) + * pin(s) in master mode; this must be handled from the user application. + * + * \note In slave mode, the data will not be transferred before a master + * initiates a transaction. + * + * \param[in] module Pointer to the software instance struct + * \param[in] tx_data Data to transmit + * + * \return Status of the procedure. + * \retval STATUS_OK If the data was written + * \retval STATUS_BUSY If the last write was not completed + */ +static inline enum status_code spi_write( + struct spi_module *module, + uint16_t tx_data) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + + /* Check if the data register has been copied to the shift register */ + if (!spi_is_ready_to_write(module)) { + /* Data register has not been copied to the shift register, return */ + return STATUS_BUSY; + } + + /* Write the character to the DATA register */ + spi_module->DATA.reg = tx_data & SERCOM_SPI_DATA_MASK; + + return STATUS_OK; +} + +enum status_code spi_write_buffer_wait( + struct spi_module *const module, + const uint8_t *tx_data, + uint16_t length); + +/** + * \brief Reads last received SPI character + * + * This function will return the last SPI character shifted into the receive + * register by the \ref spi_write function. + * + * \note The \ref spi_is_ready_to_read function should be called before calling + * this function. + * + * \note Receiver must be enabled in the configuration. + * + * \param[in] module Pointer to the software instance struct + * \param[out] rx_data Pointer to store the received data + * + * \returns Status of the read operation. + * \retval STATUS_OK If data was read + * \retval STATUS_ERR_IO If no data is available + * \retval STATUS_ERR_OVERFLOW If the data is overflown + */ +static inline enum status_code spi_read( + struct spi_module *const module, + uint16_t *rx_data) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + + /* Check if data is ready to be read */ + if (!spi_is_ready_to_read(module)) { + /* No data has been received, return */ + return STATUS_ERR_IO; + } + + /* Return value */ + enum status_code retval = STATUS_OK; + + /* Check if data is overflown */ + if (spi_module->STATUS.reg & SERCOM_SPI_STATUS_BUFOVF) { + retval = STATUS_ERR_OVERFLOW; + /* Clear overflow flag */ + spi_module->STATUS.reg = SERCOM_SPI_STATUS_BUFOVF; + } + + /* Read the character from the DATA register */ + if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { + *rx_data = (spi_module->DATA.reg & SERCOM_SPI_DATA_MASK); + } else { + *rx_data = (uint8_t)spi_module->DATA.reg; + } + + return retval; +} + +enum status_code spi_read_buffer_wait( + struct spi_module *const module, + uint8_t *rx_data, + uint16_t length, + uint16_t dummy); + +enum status_code spi_transceive_wait( + struct spi_module *const module, + uint16_t tx_data, + uint16_t *rx_data); + +enum status_code spi_transceive_buffer_wait( + struct spi_module *const module, + uint8_t *tx_data, + uint8_t *rx_data, + uint16_t length); + +enum status_code spi_select_slave( + struct spi_module *const module, + struct spi_slave_inst *const slave, + bool select); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +/** @} */ + + +/** + * \page asfdoc_sam0_sercom_spi_extra Extra Information for SERCOM SPI Driver + * + * \section asfdoc_sam0_sercom_spi_extra_acronyms Acronyms + * Below is a table listing the acronyms used in this module, along with their + * intended meanings. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
AcronymDescription
SERCOMSerial Communication Interface
SPISerial Peripheral Interface
SCKSerial Clock
MOSIMaster Output Slave Input
MISOMaster Input Slave Output
SSSlave Select
DIOData Input Output
DOData Output
DIData Input
DMADirect Memory Access
+ * + * \section asfdoc_sam0_sercom_spi_extra_dependencies Dependencies + * The SPI driver has the following dependencies: + * \li \ref asfdoc_sam0_system_pinmux_group "System Pin Multiplexer Driver" + * + * + * \section asfdoc_sam0_sercom_spi_extra_workarounds Workarounds Implemented by Driver + * No workarounds in driver. + * + * \section asfdoc_sam0_sercom_spi_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 new features as below: + * \li Slave select low detect + * \li Hardware slave select + * \li DMA support
Edited slave part of write and transceive buffer functions to ensure + * that second character is sent at the right time
Renamed the anonymous union in \c struct spi_config to + * \c mode_specific
Initial Release
+ */ + +/** + * \page asfdoc_sam0_sercom_spi_exqsg Examples for SERCOM SPI Driver + * + * This is a list of the available Quick Start guides (QSGs) and example + * applications for \ref asfdoc_sam0_sercom_spi_group. QSGs are simple examples with + * step-by-step instructions to configure and use this driver in a selection of + * use cases. Note that a QSG can be compiled as a standalone application or be + * added to the user application. + * + * - \subpage asfdoc_sam0_sercom_spi_master_basic_use + * - \subpage asfdoc_sam0_sercom_spi_slave_basic_use + * \if SPI_CALLBACK_MODE + * - \subpage asfdoc_sam0_sercom_spi_master_callback_use + * - \subpage asfdoc_sam0_sercom_spi_slave_callback_use + * \endif + * - \subpage asfdoc_sam0_sercom_spi_dma_use_case + */ + + /** + * \page asfdoc_sam0_sercom_spi_mux_settings MUX Settings + * + * The following lists the possible internal SERCOM module pad function + * assignments for the four SERCOM pads in both SPI Master and SPI Slave + * modes. They are combinations of DOPO and DIPO in CTRLA. + * Note that this is in addition to the physical GPIO pin MUX of the device, + * and can be used in conjunction to optimize the serial data pin-out. + * + * \section asfdoc_sam0_sercom_spi_mux_settings_master Master Mode Settings + * The following table describes the SERCOM pin functionalities for the various + * MUX settings, whilst in SPI Master mode. + * + * \note If MISO is unlisted, the SPI receiver must not be enabled for the + * given MUX setting. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
CombinationDOPO / DIPOSERCOM PAD[0]SERCOM PAD[1]SERCOM PAD[2]SERCOM PAD[3]
A0x0 / 0x0MOSISCK--
B0x0 / 0x1MOSISCK--
C0x0 / 0x2MOSISCKMISO-
D0x0 / 0x3MOSISCK-MISO
E0x1 / 0x0MISO-MOSISCK
F0x1 / 0x1-MISOMOSISCK
G0x1 / 0x2--MOSISCK
H0x1 / 0x3--MOSISCK
I0x2 / 0x0MISOSCK-MOSI
J0x2 / 0x1-SCK-MOSI
K0x2 / 0x2-SCKMISOMOSI
L0x2 / 0x3-SCK-MOSI
M0x3 / 0x0MOSI--SCK
N0x3 / 0x1MOSIMISO-SCK
O0x3 / 0x2MOSI-MISOSCK
P0x3 / 0x3MOSI--SCK
+ * + * + * \section asfdoc_sam0_sercom_spi_mux_settings_slave Slave Mode Settings + * The following table describes the SERCOM pin functionalities for the various + * MUX settings, whilst in SPI Slave mode. + * + * \note If MISO is unlisted, the SPI receiver must not be enabled for the + * given MUX setting. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
CombinationDOPO / DIPOSERCOM PAD[0]SERCOM PAD[1]SERCOM PAD[2]SERCOM PAD[3]
A0x0 / 0x0MISOSCK/SS-
B0x0 / 0x1MISOSCK/SS-
C0x0 / 0x2MISOSCK/SS-
D0x0 / 0x3MISOSCK/SSMOSI
E0x1 / 0x0MOSI/SSMISOSCK
F0x1 / 0x1-/SSMISOSCK
G0x1 / 0x2-/SSMISOSCK
H0x1 / 0x3-/SSMISOSCK
I0x2 / 0x0MOSISCK/SSMISO
J0x2 / 0x1-SCK/SSMISO
K0x2 / 0x2-SCK/SSMISO
L0x2 / 0x3-SCK/SSMISO
M0x3 / 0x0MISO/SS-SCK
N0x3 / 0x1MISO/SS-SCK
O0x3 / 0x2MISO/SSMOSISCK
P0x3 / 0x3MISO/SS-SCK
+ * + * + * + * \page asfdoc_sam0_sercom_spi_document_revision_history Document Revision History + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Doc. Rev.DateComments
42115E12/2015Add SAM L21/L22, SAM DA1, SAM D09, SAMR30 and SAM C21 support
42115D12/2014Add SAM R21/D10/D11 support
42115C01/2014Add SAM D21 support
42115B11/2013Replaced the pad multiplexing documentation with a condensed table
42115A06/2013Initial release
+ */ + +#endif /* SPI_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/spi/spi_interrupt.c b/atmel-samd/asf/sam0/drivers/sercom/spi/spi_interrupt.c new file mode 100644 index 000000000..5909843b2 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/spi/spi_interrupt.c @@ -0,0 +1,741 @@ +/** + * \file + * + * \brief SAM Serial Peripheral Interface 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 "spi_interrupt.h" + +/** + * \internal + * + * Dummy byte to send when reading in master mode. + */ +uint16_t dummy_write; + +/** + * \internal + * Starts transceive of buffers with a given length + * + * \param[in] module Pointer to SPI software instance struct + * \param[in] rx_data Pointer to data to be received + * \param[in] tx_data Pointer to data to be transmitted + * \param[in] length Length of data buffer + * + */ +static void _spi_transceive_buffer( + struct spi_module *const module, + uint8_t *tx_data, + uint8_t *rx_data, + uint16_t length) +{ + Assert(module); + Assert(tx_data); + + /* Write parameters to the device instance */ + module->remaining_tx_buffer_length = length; + module->remaining_rx_buffer_length = length; + module->rx_buffer_ptr = rx_data; + module->tx_buffer_ptr = tx_data; + module->status = STATUS_BUSY; + + module->dir = SPI_DIRECTION_BOTH; + + /* Get a pointer to the hardware module instance */ + SercomSpi *const hw = &(module->hw->SPI); + + /* Enable the Data Register Empty and RX Complete Interrupt */ + hw->INTENSET.reg = (SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY | + SPI_INTERRUPT_FLAG_RX_COMPLETE); + +# if CONF_SPI_SLAVE_ENABLE == true + if (module->mode == SPI_MODE_SLAVE) { + /* Clear TXC flag if set */ + hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE; + /* Enable transmit complete interrupt for slave */ + hw->INTENSET.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE; + } +# endif +} + +/** + * \internal + * Starts write of a buffer with a given length + * + * \param[in] module Pointer to SPI software instance struct + * \param[in] tx_data Pointer to data to be transmitted + * \param[in] length Length of data buffer + * + */ +static void _spi_write_buffer( + struct spi_module *const module, + uint8_t *tx_data, + uint16_t length) +{ + Assert(module); + Assert(tx_data); + + /* Write parameters to the device instance */ + module->remaining_tx_buffer_length = length; + module->remaining_dummy_buffer_length = length; + module->tx_buffer_ptr = tx_data; + module->status = STATUS_BUSY; + + module->dir = SPI_DIRECTION_WRITE; + + /* Get a pointer to the hardware module instance */ + SercomSpi *const hw = &(module->hw->SPI); + +# if CONF_SPI_SLAVE_ENABLE == true + if (module->mode == SPI_MODE_SLAVE) { + /* Clear TXC flag if set */ + hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE; + /* Enable transmit complete interrupt for slave */ + hw->INTENSET.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE; + } +# endif + + if (module->receiver_enabled) { + /* Enable the Data Register Empty and RX Complete interrupt */ + hw->INTENSET.reg = (SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY | + SPI_INTERRUPT_FLAG_RX_COMPLETE); + } else { + /* Enable the Data Register Empty interrupt */ + hw->INTENSET.reg = SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY; + } +} + +/** + * \internal + * Setup SPI to read a buffer with a given length + * + * \param[in] module Pointer to SPI software instance struct + * \param[in] rx_data Pointer to data to be received + * \param[in] length Length of data buffer + * + */ +static void _spi_read_buffer( + struct spi_module *const module, + uint8_t *rx_data, + uint16_t length) +{ + Assert(module); + Assert(rx_data); + + uint8_t tmp_intenset = 0; + + /* Set length for the buffer and the pointer, and let + * the interrupt handler do the rest */ + module->remaining_rx_buffer_length = length; + module->remaining_dummy_buffer_length = length; + module->rx_buffer_ptr = rx_data; + module->status = STATUS_BUSY; + + module->dir = SPI_DIRECTION_READ; + + /* Get a pointer to the hardware module instance */ + SercomSpi *const hw = &(module->hw->SPI); + + /* Enable the RX Complete Interrupt */ + tmp_intenset = SPI_INTERRUPT_FLAG_RX_COMPLETE; + +# if CONF_SPI_MASTER_ENABLE == true + if (module->mode == SPI_MODE_MASTER && module->dir == SPI_DIRECTION_READ) { + /* Enable Data Register Empty interrupt for master */ + tmp_intenset |= SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY; + } +# endif +# if CONF_SPI_SLAVE_ENABLE == true + if (module->mode == SPI_MODE_SLAVE) { + /* Clear TXC flag if set */ + hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE; + /* Enable transmit complete interrupt for slave */ + tmp_intenset |= SPI_INTERRUPT_FLAG_TX_COMPLETE; + + /* Workaround for SSL flag enable */ +#ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT + /* Clear SSL flag if set */ + hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW; + /* Enable Slave Select Low Interrupt for slave */ + tmp_intenset |= SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW; +#endif + } +# endif + + /* Enable all interrupts simultaneously */ + hw->INTENSET.reg = tmp_intenset; +} + +/** + * \brief Registers a SPI callback function + * + * Registers a callback function which is implemented by the user. + * + * \note The callback must be enabled by \ref spi_enable_callback, in order + * for the interrupt handler to call it when the conditions for the + * callback type are met. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] callback_func Pointer to callback function + * \param[in] callback_type Callback type given by an enum + * + */ +void spi_register_callback( + struct spi_module *const module, + spi_callback_t callback_func, + enum spi_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->registered_callback |= (1 << callback_type); +} + +/** + * \brief Unregisters a SPI callback function + * + * Unregisters a callback function which is implemented by the user. + * + * \param[in] module Pointer to SPI software instance struct + * \param[in] callback_type Callback type given by an enum + * + */ +void spi_unregister_callback( + struct spi_module *const module, + enum spi_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->registered_callback &= ~(1 << callback_type); +} + +/** + * \brief Asynchronous buffer write + * + * Sets up the driver to write to the SPI from a given buffer. If registered + * and enabled, a callback function will be called when the write is finished. + * + * \param[in] module Pointer to SPI software instance struct + * \param[out] tx_data Pointer to data buffer to receive + * \param[in] length Data buffer length + * + * \returns Status of the write request operation. + * \retval STATUS_OK If the operation completed successfully + * \retval STATUS_ERR_BUSY If the SPI was already busy with a write + * operation + * \retval STATUS_ERR_INVALID_ARG If requested write length was zero + */ +enum status_code spi_write_buffer_job( + struct spi_module *const module, + uint8_t *tx_data, + uint16_t length) +{ + Assert(module); + Assert(tx_data); + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + + /* Check if the SPI is busy transmitting or slave waiting for TXC*/ + if (module->status == STATUS_BUSY) { + return STATUS_BUSY; + } + + /* Issue internal write */ + _spi_write_buffer(module, tx_data, length); + + return STATUS_OK; +} + +/** + * \brief Asynchronous buffer read + * + * Sets up the driver to read from the SPI to a given buffer. If registered + * and enabled, a callback function will be called when the read is finished. + * + * \note If address matching is enabled for the slave, the first character + * received and placed in the RX buffer will be the address. + * + * \param[in] module Pointer to SPI software instance struct + * \param[out] rx_data Pointer to data buffer to receive + * \param[in] length Data buffer length + * \param[in] dummy Dummy character to send when reading in master mode + * + * \returns Status of the operation. + * \retval STATUS_OK If the operation completed successfully + * \retval STATUS_ERR_BUSY If the SPI was already busy with a read + * operation + * \retval STATUS_ERR_DENIED If the receiver is not enabled + * \retval STATUS_ERR_INVALID_ARG If requested read length was zero + */ +enum status_code spi_read_buffer_job( + struct spi_module *const module, + uint8_t *rx_data, + uint16_t length, + uint16_t dummy) +{ + /* Sanity check arguments */ + Assert(module); + Assert(rx_data); + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + + if (!(module->receiver_enabled)) { + return STATUS_ERR_DENIED; + } + + /* Check if the SPI is busy transmitting or slave waiting for TXC*/ + if (module->status == STATUS_BUSY) { + return STATUS_BUSY; + } + + dummy_write = dummy; + /* Issue internal read */ + _spi_read_buffer(module, rx_data, length); + return STATUS_OK; +} + +/** + * \brief Asynchronous buffer write and read + * + * Sets up the driver to write and read to and from given buffers. If registered + * and enabled, a callback function will be called when the transfer is finished. + * + * \note If address matching is enabled for the slave, the first character + * received and placed in the RX buffer will be the address. + * + * \param[in] module Pointer to SPI software instance struct + * \param[in] tx_data Pointer to data buffer to send + * \param[out] rx_data Pointer to data buffer to receive + * \param[in] length Data buffer length + * + * \returns Status of the operation. + * \retval STATUS_OK If the operation completed successfully + * \retval STATUS_ERR_BUSY If the SPI was already busy with a read + * operation + * \retval STATUS_ERR_DENIED If the receiver is not enabled + * \retval STATUS_ERR_INVALID_ARG If requested read length was zero + */ +enum status_code spi_transceive_buffer_job( + struct spi_module *const module, + uint8_t *tx_data, + uint8_t *rx_data, + uint16_t length) +{ + /* Sanity check arguments */ + Assert(module); + Assert(rx_data); + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + + if (!(module->receiver_enabled)) { + return STATUS_ERR_DENIED; + } + + /* Check if the SPI is busy transmitting or slave waiting for TXC*/ + if (module->status == STATUS_BUSY) { + return STATUS_BUSY; + } + + /* Issue internal transceive */ + _spi_transceive_buffer(module, tx_data, rx_data, length); + + return STATUS_OK; +} +/** + * \brief Aborts an ongoing job + * + * This function will abort the specified job type. + * + * \param[in] module Pointer to SPI software instance struct + */ +void spi_abort_job( + struct spi_module *const module) +{ + /* Pointer to the hardware module instance */ + SercomSpi *const spi_hw + = &(module->hw->SPI); + + /* Abort ongoing job */ + + /* Disable interrupts */ + spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_RX_COMPLETE | + SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY | + SPI_INTERRUPT_FLAG_TX_COMPLETE; + + module->status = STATUS_ABORTED; + module->remaining_rx_buffer_length = 0; + module->remaining_dummy_buffer_length = 0; + module->remaining_tx_buffer_length = 0; + + module->dir = SPI_DIRECTION_IDLE; +} + +# if CONF_SPI_SLAVE_ENABLE == true || CONF_SPI_MASTER_ENABLE == true +/** + * \internal + * Writes a character from the TX buffer to the Data register. + * + * \param[in,out] module Pointer to SPI software instance struct + */ +static void _spi_write( + struct spi_module *const module) +{ + /* Pointer to the hardware module instance */ + SercomSpi *const spi_hw = &(module->hw->SPI); + + /* Write value will be at least 8-bits long */ + uint16_t data_to_send = *(module->tx_buffer_ptr); + /* Increment 8-bit pointer */ + (module->tx_buffer_ptr)++; + + if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { + data_to_send |= ((*(module->tx_buffer_ptr)) << 8); + /* Increment 8-bit pointer */ + (module->tx_buffer_ptr)++; + } + + /* Write the data to send*/ + spi_hw->DATA.reg = data_to_send & SERCOM_SPI_DATA_MASK; + + /* Decrement remaining buffer length */ + (module->remaining_tx_buffer_length)--; +} +# endif + +# if CONF_SPI_MASTER_ENABLE == true +/** + * \internal + * Writes a dummy character to the Data register. + * + * \param[in,out] module Pointer to SPI software instance struct + */ +static void _spi_write_dummy( + struct spi_module *const module) +{ + /* Pointer to the hardware module instance */ + SercomSpi *const spi_hw = &(module->hw->SPI); + + /* Write dummy byte */ + spi_hw->DATA.reg = dummy_write; + + /* Decrement remaining dummy buffer length */ + module->remaining_dummy_buffer_length--; +} +# endif + +/** + * \internal + * Writes a dummy character from the to the Data register. + * + * \param[in,out] module Pointer to SPI software instance struct + */ +static void _spi_read_dummy( + struct spi_module *const module) +{ + /* Pointer to the hardware module instance */ + SercomSpi *const spi_hw = &(module->hw->SPI); + uint16_t flush = 0; + + /* Read dummy byte */ + flush = spi_hw->DATA.reg; + UNUSED(flush); + + /* Decrement remaining dummy buffer length */ + module->remaining_dummy_buffer_length--; +} + +/** + * \internal + * Reads a character from the Data register to the RX buffer. + * + * \param[in,out] module Pointer to SPI software instance struct + */ +static void _spi_read( + struct spi_module *const module) +{ + /* Pointer to the hardware module instance */ + SercomSpi *const spi_hw = &(module->hw->SPI); + + uint16_t received_data = (spi_hw->DATA.reg & SERCOM_SPI_DATA_MASK); + + /* Read value will be at least 8-bits long */ + *(module->rx_buffer_ptr) = received_data; + /* Increment 8-bit pointer */ + module->rx_buffer_ptr += 1; + + if(module->character_size == SPI_CHARACTER_SIZE_9BIT) { + /* 9-bit data, write next received byte to the buffer */ + *(module->rx_buffer_ptr) = (received_data >> 8); + /* Increment 8-bit pointer */ + module->rx_buffer_ptr += 1; + } + + /* Decrement length of the remaining buffer */ + module->remaining_rx_buffer_length--; +} + +/** + * \internal + * + * Handles interrupts as they occur, and it will run callback functions + * which are registered and enabled. + * + * \note This function will be called by the Sercom_Handler, and should + * not be called directly from any application code. + * + * \param[in] instance ID of the SERCOM instance calling the interrupt + * handler. + */ +void _spi_interrupt_handler( + uint8_t instance) +{ + /* Get device instance from the look-up table */ + struct spi_module *module + = (struct spi_module *)_sercom_instances[instance]; + + /* Pointer to the hardware module instance */ + SercomSpi *const spi_hw = &(module->hw->SPI); + + /* Combine callback registered and enabled masks. */ + uint8_t callback_mask = + module->enabled_callback & module->registered_callback; + + /* Read and mask interrupt flag register */ + uint16_t interrupt_status = spi_hw->INTFLAG.reg; + interrupt_status &= spi_hw->INTENSET.reg; + + /* Data register empty interrupt */ + if (interrupt_status & SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY) { +# if CONF_SPI_MASTER_ENABLE == true + if ((module->mode == SPI_MODE_MASTER) && + (module->dir == SPI_DIRECTION_READ)) { + /* Send dummy byte when reading in master mode */ + _spi_write_dummy(module); + if (module->remaining_dummy_buffer_length == 0) { + /* Disable the Data Register Empty Interrupt */ + spi_hw->INTENCLR.reg + = SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY; + } + } +# endif + + if (0 +# if CONF_SPI_MASTER_ENABLE == true + || ((module->mode == SPI_MODE_MASTER) && + (module->dir != SPI_DIRECTION_READ)) +# endif +# if CONF_SPI_SLAVE_ENABLE == true + || ((module->mode == SPI_MODE_SLAVE) && + (module->dir != SPI_DIRECTION_READ)) +# endif + ) { + /* Write next byte from buffer */ + _spi_write(module); + if (module->remaining_tx_buffer_length == 0) { + /* Disable the Data Register Empty Interrupt */ + spi_hw->INTENCLR.reg + = SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY; + + if (module->dir == SPI_DIRECTION_WRITE && + !(module->receiver_enabled)) { + /* Enable the Data Register transmit complete Interrupt */ + spi_hw->INTENSET.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE; + } + } + } + } + + /* Receive complete interrupt*/ + if (interrupt_status & SPI_INTERRUPT_FLAG_RX_COMPLETE) { + /* Check for overflow */ + if (spi_hw->STATUS.reg & SERCOM_SPI_STATUS_BUFOVF) { + if (module->dir != SPI_DIRECTION_WRITE) { + /* Store the error code */ + module->status = STATUS_ERR_OVERFLOW; + + /* End transaction */ + module->dir = SPI_DIRECTION_IDLE; + + spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_RX_COMPLETE | + SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY; + /* Run callback if registered and enabled */ + if (callback_mask & (1 << SPI_CALLBACK_ERROR)) { + (module->callback[SPI_CALLBACK_ERROR])(module); + } + } + /* Flush */ + uint16_t flush = spi_hw->DATA.reg; + UNUSED(flush); + /* Clear overflow flag */ + spi_hw->STATUS.reg = SERCOM_SPI_STATUS_BUFOVF; + } else { + if (module->dir == SPI_DIRECTION_WRITE) { + /* Flush receive buffer when writing */ + _spi_read_dummy(module); + if (module->remaining_dummy_buffer_length == 0) { + spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_RX_COMPLETE; + module->status = STATUS_OK; + module->dir = SPI_DIRECTION_IDLE; + /* Run callback if registered and enabled */ + if (callback_mask & + (1 << SPI_CALLBACK_BUFFER_TRANSMITTED)){ + (module->callback[SPI_CALLBACK_BUFFER_TRANSMITTED])(module); + } + } + } else { + /* Read data register */ + _spi_read(module); + + /* Check if the last character have been received */ + if (module->remaining_rx_buffer_length == 0) { + module->status = STATUS_OK; + /* Disable RX Complete Interrupt and set status */ + spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_RX_COMPLETE; + if(module->dir == SPI_DIRECTION_BOTH) { + if (callback_mask & (1 << SPI_CALLBACK_BUFFER_TRANSCEIVED)) { + (module->callback[SPI_CALLBACK_BUFFER_TRANSCEIVED])(module); + } + } else if (module->dir == SPI_DIRECTION_READ) { + if (callback_mask & (1 << SPI_CALLBACK_BUFFER_RECEIVED)) { + (module->callback[SPI_CALLBACK_BUFFER_RECEIVED])(module); + } + } + } + } + } + } + + /* Transmit complete */ + if (interrupt_status & SPI_INTERRUPT_FLAG_TX_COMPLETE) { +# if CONF_SPI_SLAVE_ENABLE == true + if (module->mode == SPI_MODE_SLAVE) { + /* Transaction ended by master */ + + /* Disable interrupts */ + spi_hw->INTENCLR.reg = + SPI_INTERRUPT_FLAG_TX_COMPLETE | + SPI_INTERRUPT_FLAG_RX_COMPLETE | + SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY; + /* Clear interrupt flag */ + spi_hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE; + + + /* Reset all status information */ + module->dir = SPI_DIRECTION_IDLE; + module->remaining_tx_buffer_length = 0; + module->remaining_rx_buffer_length = 0; + module->status = STATUS_OK; + + if (callback_mask & + (1 << SPI_CALLBACK_SLAVE_TRANSMISSION_COMPLETE)) { + (module->callback[SPI_CALLBACK_SLAVE_TRANSMISSION_COMPLETE]) + (module); + } + + } +# endif +# if CONF_SPI_MASTER_ENABLE == true + if ((module->mode == SPI_MODE_MASTER) && + (module->dir == SPI_DIRECTION_WRITE) && !(module->receiver_enabled)) { + /* Clear interrupt flag */ + spi_hw->INTENCLR.reg + = SPI_INTERRUPT_FLAG_TX_COMPLETE; + /* Buffer sent with receiver disabled */ + module->dir = SPI_DIRECTION_IDLE; + module->status = STATUS_OK; + /* Run callback if registered and enabled */ + if (callback_mask & (1 << SPI_CALLBACK_BUFFER_TRANSMITTED)){ + (module->callback[SPI_CALLBACK_BUFFER_TRANSMITTED]) + (module); + } + } +#endif + } + +# ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT +# if CONF_SPI_SLAVE_ENABLE == true + /* When a high to low transition is detected on the _SS pin in slave mode */ + if (interrupt_status & SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW) { + if (module->mode == SPI_MODE_SLAVE) { + /* Disable interrupts */ + spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW; + /* Clear interrupt flag */ + spi_hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW; + + if (callback_mask & (1 << SPI_CALLBACK_SLAVE_SELECT_LOW)) { + (module->callback[SPI_CALLBACK_SLAVE_SELECT_LOW])(module); + } + } + } +# endif +# endif + +# ifdef FEATURE_SPI_ERROR_INTERRUPT + /* When combined error happen */ + if (interrupt_status & SPI_INTERRUPT_FLAG_COMBINED_ERROR) { + /* Disable interrupts */ + spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_COMBINED_ERROR; + /* Clear interrupt flag */ + spi_hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_COMBINED_ERROR; + + if (callback_mask & (1 << SPI_CALLBACK_COMBINED_ERROR)) { + (module->callback[SPI_CALLBACK_COMBINED_ERROR])(module); + } + } +# endif +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/spi/spi_interrupt.h b/atmel-samd/asf/sam0/drivers/sercom/spi/spi_interrupt.h new file mode 100644 index 000000000..bb7168250 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/spi/spi_interrupt.h @@ -0,0 +1,190 @@ +/** + * \file + * + * \brief SAM Serial Peripheral Interface Driver (Callback Mode) + * + * 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 SPI_INTERRUPT_H_INCLUDED +#define SPI_INTERRUPT_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup asfdoc_sam0_sercom_spi_group + * + * @{ + */ + +#include "spi.h" + +/** + * \name Callback Management + * @{ + */ + +void spi_register_callback( + struct spi_module *const module, + spi_callback_t callback_func, + enum spi_callback callback_type); + +void spi_unregister_callback( + struct spi_module *module, + enum spi_callback callback_type); + +/** + * \brief Enables an SPI callback of a given type + * + * Enables the callback function registered by the \ref spi_register_callback. + * The callback function will be called from the interrupt handler when the + * conditions for the callback type are met. + * + * \param[in] module Pointer to SPI software instance struct + * \param[in] callback_type Callback type given by an enum + */ +static inline void spi_enable_callback( + struct spi_module *const module, + enum spi_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module); + + /* Enable callback */ + module->enabled_callback |= (1 << callback_type); +} + +/** + * \brief Disables callback + * + * Disables the callback function registered by the \ref spi_register_callback, + * and the callback will not be called from the interrupt routine. + * + * \param[in] module Pointer to SPI software instance struct + * \param[in] callback_type Callback type given by an enum + */ +static inline void spi_disable_callback( + struct spi_module *const module, + enum spi_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module); + + /* Disable callback */ + module->enabled_callback &= ~(1 << callback_type); +} + +/** @} */ + + +/** + * \name Writing and Reading + * @{ + */ +enum status_code spi_write_buffer_job( + struct spi_module *const module, + uint8_t *tx_data, + uint16_t length); + +enum status_code spi_read_buffer_job( + struct spi_module *const module, + uint8_t *rx_data, + uint16_t length, + uint16_t dummy); + +enum status_code spi_transceive_buffer_job( + struct spi_module *const module, + uint8_t *tx_data, + uint8_t *rx_data, + uint16_t length); + +void spi_abort_job( + struct spi_module *const module); + +/** + * \brief Retrieves the current status of a job. + * + * Retrieves the current status of a job that was previously issued. + * + * \param[in] module Pointer to SPI software instance struct + * + * \return Current job status. + */ +static inline enum status_code spi_get_job_status( + const struct spi_module *const module) +{ + return module->status; +} + +/** + * \brief Retrieves the status of job once it ends. + * + * Waits for current job status to become non-busy, then returns its value. + * + * \param[in] module Pointer to SPI software instance struct + * + * \return Current non-busy job status. + */ +static inline enum status_code spi_get_job_status_wait( + const struct spi_module *const module) +{ + enum status_code status; + + do { + status = spi_get_job_status(module); + } while (status == STATUS_BUSY); + + return status; +} + +/** @} */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* SPI_INTERRUPT_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/module-config/conf_spi.h b/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/module-config/conf_spi.h new file mode 100644 index 000000000..3bb66ff5b --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/module-config/conf_spi.h @@ -0,0 +1,55 @@ +/** + * \file + * + * \brief common SPI configuration + * + * 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 CONF_SPI_H_INCLUDED +# define CONF_SPI_H_INCLUDED + +# define CONF_SPI_MASTER_ENABLE true +# define CONF_SPI_SLAVE_ENABLE false + +#endif /* CONF_SPI_H_INCLUDED */ + diff --git a/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/module-config/conf_spi_master_vec.h b/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/module-config/conf_spi_master_vec.h new file mode 100644 index 000000000..394b1fa61 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/module-config/conf_spi_master_vec.h @@ -0,0 +1,68 @@ +/** + * \file + * + * \brief SERCOM SPI master with vectored I/O driver configuration + * + * 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 CONF_SPI_MASTER_VEC_H +#define CONF_SPI_MASTER_VEC_H + +#if defined(__FREERTOS__) || defined(__DOXYGEN__) +# include +# include + +# define CONF_SPI_MASTER_VEC_OS_SUPPORT +# define CONF_SPI_MASTER_VEC_SEMAPHORE_TYPE xSemaphoreHandle +# define CONF_SPI_MASTER_VEC_CREATE_SEMAPHORE(semaphore) \ + vSemaphoreCreateBinary(semaphore) +# define CONF_SPI_MASTER_VEC_DELETE_SEMAPHORE(semaphore) \ + vSemaphoreDelete(semaphore) +# define CONF_SPI_MASTER_VEC_TAKE_SEMAPHORE(semaphore) \ + xSemaphoreTake((semaphore), portMAX_DELAY) +# define CONF_SPI_MASTER_VEC_GIVE_SEMAPHORE(semaphore) \ + xSemaphoreGive((semaphore)) +# define CONF_SPI_MASTER_VEC_GIVE_SEMAPHORE_FROM_ISR(semaphore) \ + xSemaphoreGiveFromISR((semaphore), NULL) +#endif + +#endif // CONF_SPI_MASTER_VEC_H \ No newline at end of file diff --git a/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/spi_master_vec.c b/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/spi_master_vec.c new file mode 100644 index 000000000..89de7dd0d --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/spi_master_vec.c @@ -0,0 +1,588 @@ +/** + * \file + * + * \brief SERCOM SPI master with vectored I/O driver implementation + * + * 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 "spi_master_vec.h" +#include +#include +#include + +/** + * \ingroup asfdoc_sam0_sercom_spi_master_vec_group + * + * @{ + */ + +/** + * \name Internal functions + * @{ + */ + +/** \cond INTERNAL */ + +static void _spi_master_vec_int_handler(uint8_t sercom_index); + +/** + * \brief Wait for SERCOM SPI to synchronize + * + * \param[in] sercom_spi SERCOM SPI to check for synchronization. + * + * \note The implementation of this function depends on the SERCOM revision. + */ +static inline void _spi_master_vec_wait_for_sync(SercomSpi *const sercom_spi) +{ +#if defined(FEATURE_SERCOM_SYNCBUSY_SCHEME_VERSION_1) + while (sercom_spi->STATUS.reg & SERCOM_SPI_STATUS_SYNCBUSY) { + /* Intentionally left empty */ + } +#elif defined(FEATURE_SERCOM_SYNCBUSY_SCHEME_VERSION_2) + while (sercom_spi->SYNCBUSY.reg) { + /* Intentionally left empty */ + } +#else +# error Unknown SERCOM SYNCBUSY scheme! +#endif +} + +/** + * \brief Pin MUX configuration helper + * + * \param[in] pinmux Pin MUX setting to apply. Special values: + * \arg \c PINMUX_UNUSED to do nothing. + * \arg \c PINMUX_DEFAULT to use default pin MUX for the SERCOM pad. + * \param[in] sercom,padnum SERCOM pad specification, for \ref PINMUX_DEFAULT. + */ +static inline void _spi_master_vec_pinmux_helper(uint32_t pinmux, + Sercom *const sercom, uint8_t padnum) +{ + struct system_pinmux_config pin_conf; + + if (pinmux == PINMUX_DEFAULT) { + pinmux = _sercom_get_default_pad(sercom, padnum); + } + + if (pinmux == PINMUX_UNUSED) { + return; + } + + system_pinmux_get_config_defaults(&pin_conf); + pin_conf.mux_position = pinmux & 0xFFFF; + system_pinmux_pin_set_config(pinmux >> 16, &pin_conf); +}; + +/** \endcond */ + +/** @} */ + +/** + * \brief Initialize hardware and driver instance + * + * This function configures the clock system for the specified SERCOM module, + * sets up the related pins and their MUX, initializes the SERCOM in SPI master + * mode, and prepares the driver instance for operation. + * + * \pre \ref system_init() must have been called prior to this function. + * + * The SERCOM SPI module is left disabled after initialization, and must be + * enabled with \ref spi_master_vec_enable() before a transfer can be done. + * + * \param[out] module Driver instance to initialize. + * \param[in,out] sercom SERCOM module to initialize and associate driver + * instance with. + * \param[in] config Driver configuration to use. + * + * \return Status of initialization. + * \retval STATUS_OK if initialization succeeded. + * \retval STATUS_ERR_INVALID_ARG if driver has been misconfigured. + */ +enum status_code spi_master_vec_init(struct spi_master_vec_module *const module, + Sercom *const sercom, const struct spi_master_vec_config *const config) +{ + Assert(module); + Assert(sercom); + Assert(config); + + enum status_code status; + SercomSpi *const spi_hw = &(sercom->SPI); + struct system_gclk_chan_config gclk_chan_conf; + uint16_t tmp_baud; + uint32_t sercom_index = _sercom_get_sercom_inst_index((Sercom *)spi_hw); +#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30) + uint32_t pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; +#else + uint32_t pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos; +#endif + uint32_t gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + uint32_t gclk_hz; + + module->sercom = sercom; + + /* Enable clock for the module interface */ + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); + + /* Set up the GCLK for the module */ + system_gclk_chan_get_config_defaults(&gclk_chan_conf); + gclk_chan_conf.source_generator = config->gclk_generator; + system_gclk_chan_set_config(gclk_index, &gclk_chan_conf); + system_gclk_chan_enable(gclk_index); + sercom_set_gclk_generator(config->gclk_generator, false); + + _spi_master_vec_wait_for_sync(spi_hw); + + /* Set up the SERCOM SPI module as master */ + spi_hw->CTRLA.reg = SERCOM_SPI_CTRLA_MODE(0x3); + spi_hw->CTRLA.reg |= (uint32_t)config->mux_setting + | config->transfer_mode + | config->data_order + | ((config->run_in_standby || system_is_debugger_present()) ? + SERCOM_SPI_CTRLA_RUNSTDBY : 0); + + /* Get baud value from configured baudrate and internal clock rate */ + gclk_hz = system_gclk_chan_get_hz(gclk_index); + status = _sercom_get_sync_baud_val(config->baudrate, gclk_hz, &tmp_baud); + + if (status != STATUS_OK) { + /* Baud rate calculation error! */ + return STATUS_ERR_INVALID_ARG; + } + + spi_hw->BAUD.reg = (uint8_t)tmp_baud; + + /* Configure the pin multiplexers */ + _spi_master_vec_pinmux_helper(config->pinmux_pad0, sercom, 0); + _spi_master_vec_pinmux_helper(config->pinmux_pad3, sercom, 3); + + /* SERCOM PAD1 and PAD2 are used for slave SS. + * This is a SPI master driver, so control of slave SS must be left to + * the PORT module, i.e., peripheral MUX should not be set for that pin. + * DOPO controls which PAD is used for slave SS: + * If DOPO is odd, SERCOM_PAD1 is SS: SERCOM_PAD2 can be MUXed. + * If DOPO is even, SERCOM_PAD2 is SS: SERCOM_PAD1 can be MUXed. + */ + if (config->mux_setting & (1 << SERCOM_SPI_CTRLA_DOPO_Pos)) { + _spi_master_vec_pinmux_helper(config->pinmux_pad2, sercom, 2); + } else { + _spi_master_vec_pinmux_helper(config->pinmux_pad1, sercom, 1); + } + + /* Initialize our instance and register interrupt handler + data */ + module->rx_bufdesc_ptr = NULL; + module->tx_bufdesc_ptr = NULL; + module->direction = SPI_MASTER_VEC_DIRECTION_IDLE; + module->status = STATUS_OK; +#ifdef CONF_SPI_MASTER_VEC_OS_SUPPORT + CONF_SPI_MASTER_VEC_CREATE_SEMAPHORE(module->busy_semaphore); +#endif + + _sercom_set_handler(sercom_index, _spi_master_vec_int_handler); + _sercom_instances[sercom_index] = module; + + return STATUS_OK; +} + +/** + * \brief Enable the SERCOM SPI module + * + * This function must be called after \ref spi_master_vec_init() before a + * transfer can be started. + * + * \param[in,out] module Driver instance to operate on. + */ +void spi_master_vec_enable(const struct spi_master_vec_module *const module) +{ + Assert(module); + Assert(module->sercom); + + SercomSpi *const spi_hw = &(module->sercom->SPI); + + spi_hw->INTENCLR.reg = SERCOM_SPI_INTFLAG_DRE | SERCOM_SPI_INTFLAG_RXC + | SERCOM_SPI_INTFLAG_TXC; + + _spi_master_vec_wait_for_sync(spi_hw); + + spi_hw->CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE; + + system_interrupt_enable(_sercom_get_interrupt_vector(module->sercom)); +} + +/** + * \brief Disable the SERCOM SPI module + * + * \param[in,out] module Driver instance to operate on. + */ +void spi_master_vec_disable(struct spi_master_vec_module *const module) +{ + Assert(module); + Assert(module->sercom); + + SercomSpi *const spi_hw = &(module->sercom->SPI); + + system_interrupt_disable(_sercom_get_interrupt_vector(module->sercom)); + + _spi_master_vec_wait_for_sync(spi_hw); + + spi_hw->INTENCLR.reg = SERCOM_SPI_INTENCLR_MASK; + spi_hw->INTFLAG.reg = SERCOM_SPI_INTFLAG_MASK; + spi_hw->CTRLB.reg = 0; + spi_hw->CTRLA.reg &= ~SERCOM_SPI_CTRLA_ENABLE; + module->rx_bufdesc_ptr = NULL; + module->tx_bufdesc_ptr = NULL; + module->direction = SPI_MASTER_VEC_DIRECTION_IDLE; + module->status = STATUS_OK; +} + +/** + * \brief Reset the SERCOM SPI module + * + * This function will disable and reset the SPI module to its power on default + * values. + * + * \param[in,out] module Pointer to a driver instance. + */ +void spi_master_vec_reset(struct spi_master_vec_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->sercom); + + SercomSpi *const spi_hw = &(module->sercom->SPI); + + /* Disable the module */ + spi_master_vec_disable(module); + + _spi_master_vec_wait_for_sync(spi_hw); + + /* Software reset the module */ + spi_hw->CTRLA.reg |= SERCOM_SPI_CTRLA_SWRST; + +#ifdef CONF_SPI_MASTER_VEC_OS_SUPPORT + CONF_SPI_MASTER_VEC_DELETE_SEMAPHORE(module->busy_semaphore); +#endif +} + +/** + * \brief Start vectored I/O transfer + * + * This function initiates a uni- or bidirectional SPI transfer from/to any + * number of data buffers. The transfer is interrupt-driven and will run in the + * background, after this function has returned. + * + * The buffers to transmit from or receive into must be described in arrays of + * buffer descriptors. These arrays \e must end with descriptors that specify + * zero buffer length. The first descriptor in an array can \e not specify zero + * length. The number of bytes to transmit and to receive do not have to be + * equal. + * + * If the address for a receive buffer is set to \c NULL, the received bytes + * corresponding to that buffer descriptor will be discarded. This is useful if + * slave is already set up to transfer a number of bytes, but the master has no + * available buffer to receive them into. As an example, to receive the two + * first bytes and discard the 128 following, the buffer descriptors could be: +\code + struct spi_master_vec_bufdesc rx_buffers[3] = { + // Read two status bytes + {.data = status_buffer, .length = 2}, + // Discard 128 data bytes + {.data = NULL, .length = 128}, + // End of reception + {.length = 0}, + }; +\endcode + * + * To initiate a unidirectional transfer, pass \c NULL as the address of either + * buffer descriptor array, like this: +\code + // Transmit some buffers + spi_master_vec_transceive_buffer_job(&module, tx_buffers, NULL); + + // Receive some buffers + spi_master_vec_transceive_buffer_job(&module, NULL, rx_buffers); +\endcode + * + * \pre \ref spi_master_vec_init() and \ref spi_master_vec_enable() must have + * been called before this function. + * + * \param[in,out] module Driver instance to operate on. + * \param[in] tx_bufdescs address of buffer descriptor array for bytes to + * transmit. + * \arg \c NULL if the transfer is a simplex read. + * \param[in,out] rx_bufdescs address of buffer descriptor array for storing + * received bytes. + * \arg \c NULL if the transfer is a simplex write. + * + * \return Status of transfer start. + * \retval STATUS_OK if transfer was started. + * \retval STATUS_BUSY if a transfer is already on-going. + */ +enum status_code spi_master_vec_transceive_buffer_job( + struct spi_master_vec_module *const module, + struct spi_master_vec_bufdesc tx_bufdescs[], + struct spi_master_vec_bufdesc rx_bufdescs[]) +{ + Assert(module); + Assert(module->sercom); + Assert(tx_bufdescs || rx_bufdescs); + + SercomSpi *const spi_hw = &(module->sercom->SPI); + uint32_t tmp_ctrlb; + uint8_t tmp_intenset; + + system_interrupt_enter_critical_section(); + if (module->status == STATUS_BUSY) { + system_interrupt_leave_critical_section(); + return STATUS_BUSY; + } else { + module->status = STATUS_BUSY; + system_interrupt_leave_critical_section(); + } + +#ifdef CONF_SPI_MASTER_VEC_OS_SUPPORT + CONF_SPI_MASTER_VEC_TAKE_SEMAPHORE(module->busy_semaphore); +#endif + + module->tx_bufdesc_ptr = tx_bufdescs; + module->rx_bufdesc_ptr = rx_bufdescs; + + if (tx_bufdescs && rx_bufdescs) { + Assert(tx_bufdescs[0].length); + Assert(rx_bufdescs[0].length); + + module->direction = SPI_MASTER_VEC_DIRECTION_BOTH; + module->tx_length = tx_bufdescs[0].length; + module->tx_head_ptr = tx_bufdescs[0].data; + module->rx_length = rx_bufdescs[0].length; + module->rx_head_ptr = rx_bufdescs[0].data; + module->tx_lead_on_rx = 0; + tmp_ctrlb = SERCOM_SPI_CTRLB_RXEN; + tmp_intenset = SERCOM_SPI_INTFLAG_DRE | SERCOM_SPI_INTFLAG_RXC; + } else { + if (tx_bufdescs) { + Assert(tx_bufdescs[0].length); + + module->direction = SPI_MASTER_VEC_DIRECTION_WRITE; + module->tx_length = tx_bufdescs[0].length; + module->tx_head_ptr = tx_bufdescs[0].data; + tmp_ctrlb = 0; + tmp_intenset = SERCOM_SPI_INTFLAG_DRE; + } else { + Assert(rx_bufdescs[0].length); + + module->direction = SPI_MASTER_VEC_DIRECTION_READ; + module->rx_length = rx_bufdescs[0].length; + module->rx_head_ptr = rx_bufdescs[0].data; + module->tx_lead_on_rx = 0; + tmp_ctrlb = SERCOM_SPI_CTRLB_RXEN; + tmp_intenset = SERCOM_SPI_INTFLAG_DRE | SERCOM_SPI_INTFLAG_RXC; + } + } + + /* Ensure the SERCOM is sync'ed before writing these registers */ + _spi_master_vec_wait_for_sync(spi_hw); + + spi_hw->CTRLB.reg = tmp_ctrlb; + spi_hw->INTENSET.reg = tmp_intenset; + + return STATUS_OK; +} + +/** + * \brief Interrupt handler + * + * \param[in] sercom_index SERCOM instance number passed from the master SERCOM + * driver. + */ +static void _spi_master_vec_int_handler(uint8_t sercom_index) +{ + struct spi_master_vec_module *const module = + _sercom_instances[sercom_index]; + enum _spi_master_vec_direction dir = module->direction; + SercomSpi *const spi_hw = &(module->sercom->SPI); + uint8_t int_status; + + int_status = spi_hw->INTFLAG.reg ; + int_status &= spi_hw->INTENSET.reg; + + if (int_status & SERCOM_SPI_INTFLAG_DRE) { + uint_fast8_t tx_lead_on_rx = module->tx_lead_on_rx; + + /* If TX is ahead of RX by 2+ bytes, allow RX to catch up. + * Note: will only happen _once_ per READ or BOTH. + */ + if ((tx_lead_on_rx >= 2) && (dir != SPI_MASTER_VEC_DIRECTION_WRITE)) { + Assert((dir == SPI_MASTER_VEC_DIRECTION_READ) + || (dir == SPI_MASTER_VEC_DIRECTION_BOTH)); + Assert(int_status & SERCOM_SPI_INTFLAG_RXC); + /* Otherwise, we can send more bytes */ + } else { + module->tx_lead_on_rx = ++tx_lead_on_rx; + + /* If doing a READ, just send 0 to trigger the transfer */ + if (dir == SPI_MASTER_VEC_DIRECTION_READ) { + uint32_t tx_lead_limit; + + spi_hw->DATA.reg = 0; + +check_for_read_end: + /* With current TX'ed bytes, will we get the last RX byte? + * If so, we can disable the DRE interrupt to stop transmitting. + * + * Since a buffer can have minimum 1 byte length, this check is + * simplified by first checking if the RX end is so close that + * the max. 2 byte lead of TX may actually fill the buffers. + */ + tx_lead_limit = (module->rx_bufdesc_ptr + 1)->length; + + if (!tx_lead_limit || !(module->rx_bufdesc_ptr + 2)->length) { + tx_lead_limit += module->rx_length; + + if (tx_lead_on_rx >= tx_lead_limit) { + spi_hw->INTENCLR.reg = SERCOM_SPI_INTFLAG_DRE; + } + } + /* For WRITE and BOTH, output current byte */ + } else { + spi_master_vec_buflen_t tx_length; + uint8_t *tx_head_ptr; + + tx_head_ptr = module->tx_head_ptr; + spi_hw->DATA.reg = *(tx_head_ptr++); + + /* Check if this was the last byte to send */ + tx_length = module->tx_length - 1; + + if (tx_length) { + module->tx_head_ptr = tx_head_ptr; + module->tx_length = tx_length; + } else { + /* Any more buffers left to send, perhaps? */ + tx_length = (++module->tx_bufdesc_ptr)->length; + + if (tx_length) { + module->tx_head_ptr = module->tx_bufdesc_ptr->data; + module->tx_length = tx_length; + } else { + if (dir == SPI_MASTER_VEC_DIRECTION_WRITE) { + /* Disable DRE and enable TXC to end WRITE */ + spi_hw->INTENCLR.reg = SERCOM_SPI_INTFLAG_DRE; + spi_hw->INTENSET.reg = SERCOM_SPI_INTFLAG_TXC; + } else { + /* For BOTH, check if we still have bytes to read */ + dir = SPI_MASTER_VEC_DIRECTION_READ; + module->direction = dir; + goto check_for_read_end; + } + } + } + } + } + } + + /* For READ and BOTH, store the received byte */ + if (int_status & SERCOM_SPI_INTFLAG_RXC) { + spi_master_vec_buflen_t rx_length; + uint8_t *rx_head_ptr; + + rx_head_ptr = module->rx_head_ptr; + if (rx_head_ptr != NULL) { + *(rx_head_ptr++) = spi_hw->DATA.reg; + } else { + uint8_t dummy; + dummy = spi_hw->DATA.reg; + UNUSED(dummy); + } + module->tx_lead_on_rx--; + + /* Check if this was the last byte to receive */ + rx_length = module->rx_length - 1; + + if (rx_length) { + module->rx_head_ptr = rx_head_ptr; + module->rx_length = rx_length; + } else { + /* Any more buffers left to receive into? */ + rx_length = (++module->rx_bufdesc_ptr)->length; + + if (rx_length) { + module->rx_head_ptr = module->rx_bufdesc_ptr->data; + module->rx_length = rx_length; + } else { + /* Disable the SPI receiver (instant effect) and RX interrupt */ + spi_hw->CTRLB.reg = 0; + spi_hw->INTENCLR.reg = SERCOM_SPI_INTFLAG_RXC; + + if (dir == SPI_MASTER_VEC_DIRECTION_READ) { + /* If doing READ, end the transaction here */ + dir = SPI_MASTER_VEC_DIRECTION_IDLE; + module->direction = dir; + module->status = STATUS_OK; +#ifdef CONF_SPI_MASTER_VEC_OS_SUPPORT + CONF_SPI_MASTER_VEC_GIVE_SEMAPHORE_FROM_ISR(module->busy_semaphore); +#endif + } else { + /* If doing BOTH, change direction to WRITE */ + dir = SPI_MASTER_VEC_DIRECTION_WRITE; + module->direction = dir; + } + } + } + } + + /* For WRITE */ + if (int_status & SERCOM_SPI_INTFLAG_TXC) { + /* End transaction here, since last byte has been sent */ + spi_hw->INTENCLR.reg = SERCOM_SPI_INTFLAG_TXC; + + dir = SPI_MASTER_VEC_DIRECTION_IDLE; + module->direction = dir; + module->status = STATUS_OK; +#ifdef CONF_SPI_MASTER_VEC_OS_SUPPORT + CONF_SPI_MASTER_VEC_GIVE_SEMAPHORE_FROM_ISR(module->busy_semaphore); +#endif + } +} + +/** + * @} + */ \ No newline at end of file diff --git a/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/spi_master_vec.h b/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/spi_master_vec.h new file mode 100644 index 000000000..a10ae22f1 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/spi_master_vec.h @@ -0,0 +1,692 @@ +/** + * \file + * + * \brief SERCOM SPI master with vectored I/O driver include + * + * 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 SPI_MASTER_VEC_H +#define SPI_MASTER_VEC_H + +#include +#include +#include +#include +#include +#include + +/** + * \defgroup asfdoc_sam0_sercom_spi_master_vec_group SAM Serial Peripheral Interface Master Driver w/ Vectored I/O (SERCOM SPI) + * + * This driver for Atmel® | SMART SAM devices provides an interface for the configuration + * and operation of the SERCOM module in SPI master mode and uses vectored I/O + * for data transfers. + * + * The following peripherals are used by this driver: + * - SERCOM (Serial Communication Interface) + * + * The following devices can use this driver: + * - 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 reader is assumed to be familiar with the regular SERCOM SPI driver, and + * how it is configured and operated. Configuration of this driver is done a + * similar way and actually re-uses several enumerations (configuration values) + * from the regular SERCOM SPI driver. + * + * The outline of this documentation is as follows: + * - \ref asfdoc_sam0_sercom_spi_master_vec_prerequisites + * - \ref asfdoc_sam0_sercom_spi_master_vec_module_overview + * - \ref asfdoc_sam0_sercom_spi_master_vec_special_considerations + * - \ref asfdoc_sam0_sercom_spi_master_vec_extra_info + * - \ref asfdoc_sam0_sercom_spi_master_vec_examples + * - \ref asfdoc_sam0_sercom_spi_master_vec_api_overview + * + * + * \section asfdoc_sam0_sercom_spi_master_vec_prerequisites Prerequisites + * + * This driver uses the \ref asfdoc_sam0_system_clock_group "SYSTEM clock driver" + * to select the SERCOM's clock source and to configure the SERCOM for the + * desired baud rate. Ensure that the selected clock source is configured and + * that the clock system is initialized. This is typically done indirectly with + * \ref system_init(), or directly with \ref system_clock_init(). + * + * According to the datasheet, the minimum and maximum limits for the baud rate + * is given by: \f$ \frac{1}{2^{17}} \times f_{clk} \le f_{baud} + * \le \frac{1}{2} \times f_{clk} \f$. + * + * + * \section asfdoc_sam0_sercom_spi_master_vec_module_overview Module Overview + * + * This SERCOM SPI master driver supports uni- and bidirectional transfers of + * 8-bit data with vectored I/O, also know as scatter/gather. + * It does not implement control of SS or slave addressing since the intended + * usage is in stacks which usually have their own protocols and handshaking + * schemes. + * + * + * \subsection asfdoc_sam0_sercom_spi_master_vectored_io Vectored I/O + * + * Vectored I/O enables the transfer of data from/to any number of buffers with + * arbitrary memory locations without having to do several transfers, i.e., one + * buffer at a time. This feature is useful in stacks because it allows each + * layer of the stack to have a dedicated data buffer, thus avoiding the need + * for a centralized data buffer that the different layers must use in + * cooperation. + * + * The vectored I/O relies on arrays of buffer descriptors which must be passed + * to the driver to start a transfer. These buffer descriptors specify where in + * memory each buffer is, and how large they are. + * \ref asfdoc_sam0_vectored_io_example "The figure below" illustrates this + * for an example with three buffers of varying sizes that are transmitted. + * + * \anchor asfdoc_sam0_vectored_io_example + * \dot +digraph bufptr_to_spiord { + rankdir=LR; + subgraph cluster_bufptr { + style=invis; + bufptr_label [shape=none, label="Buffer descriptors"]; + bufptrs [shape=record, label=" [0]| [1]| [2]| [3]"]; + } + subgraph cluster_buf { + style=invis; + buf_label [shape=none, label="Memory layout"]; + bufs [shape=record, label="...|"yy"|...|"z"|"xxx"|..."]; + } + subgraph cluster_spiord { + style=invis; + spiord_label [shape=none, label="SPI transmission"]; + spiord [shape=record, label=""yy"|"xxx"|"z""]; + } + bufptrs:bf1 -> bufs:b1 -> spiord:s1; + bufptrs:bf2 -> bufs:b2 -> spiord:s2; + bufptrs:bf3 -> bufs:b3 -> spiord:s3; + bufptrs:bf4 -> "none"; +} + * \enddot + * + * Note that the last descriptor \e must indicate no next buffer in order for + * the driver to detect that the end of the buffer list has been reached. This + * means that for \c N buffers, \c N+1 buffer descriptors are needed. + * + * Bidirectional transfers are supported without any restrictions on the buffer + * descriptors, so the number of bytes and buffers to receive and transmit do + * \e not have to be the same. + * + * \sa spi_master_vec_transceive_buffer_job() for details on starting transfers. + * + * + * \subsection asfdoc_sam0_sercom_spi_master_os_support OS support + * + * Since this driver is interrupt-driven, it is possible for the MCU to run + * other code while a transfer is on-going. + * + * In a single-threaded application, this can be achieved by starting a transfer + * and then avoid any waiting for completion until absolutely required, e.g., + * when a new transfer is needed. + * + * But in a multi-threaded application, for example based on FreeRTOS, one can + * utilize \e semaphores to let the OS know when a function is waiting and thus + * blocking the thread, and that other threads can be run instead. Put another + * way, the waiting can be made efficient. + * + * This driver has an internal semaphore which is used to signal to the OS + * whenever a function is waiting for a transfer to complete. And since the + * semaphore datatypes and functions are OS-specific, the support has been made + * configurable by the use of macros. Note that support for FreeRTOS is already + * implemented, but must be enabled. + * + * \sa CONF_SPI_MASTER_VEC_OS_SUPPORT for more on the configurable OS support. + * + * + * \section asfdoc_sam0_sercom_spi_master_vec_special_considerations Special Considerations + * + * \subsection asfdoc_sam0_sercom_spi_master_vec_special_isr Interrupt safety + * + * This driver should not be used within interrupt contexts. The reason for this + * is that the driver itself is interrupt driven. Further, the configurable OS + * support is implemented with the assumption that transfers are only started + * in threads, not in interrupt service routines, because it gives the simplest + * API. + * + * + * \subsection asfdoc_sam0_sercom_spi_master_vec_special_mux Signal MUX + * + * The SERCOM module has two layers of signal multiplexing in SPI mode: + * -# SERCOM pad MUX: This routes the SPI signals to internal lines. + * -# PORT pin MUX: This routes the internal line to a device pin. + * + * Both of these layers are configured in the \ref spi_master_vec_config + * "configuration structure", using the members named \c mux_setting and + * \c pinmux_padN. These must be set in combination. + * + * The driver supplies values for the + * \ref spi_master_vec_config::mux_setting "SERCOM pad MUX" from the standard + * ASF SERCOM SPI driver. For the PORT pin MUX configuration, refer to the + * peripheral include file for the device (\c pio_samd20XNN.h ) and use the + * macros that are prefixed with \c PINMUX_, such as + * \c PINMUX_PA04D_SERCOM0_PAD0. It is also possible to use the default pin MUX + * setting for a SERCOM pad by using the \ref PINMUX_DEFAULT macro. The defaults + * are defined in the file \ref sercom_pinout.h. + * + * Note that for \ref spi_master_vec_init() to function properly with the macro + * \ref PINMUX_DEFAULT, the order of the values in \c pinmux_padN \e must be + * correct, i.e., \c pinmux_pad0 must contain the pin MUX setting for + * multiplexing SERCOM pad 0, and so on. + * + * + * \section asfdoc_sam0_sercom_spi_master_vec_extra_info Extra Information + * + * For extra information, see + * \subpage asfdoc_sam0_sercom_spi_master_vec_extra. This includes: + * - \ref asfdoc_sam0_sercom_spi_master_vec_extra_acronyms + * - \ref asfdoc_sam0_sercom_spi_master_vec_extra_dependencies + * - \ref asfdoc_sam0_sercom_spi_master_vec_extra_errata + * - \ref asfdoc_sam0_sercom_spi_master_vec_extra_history + * + * + * \section asfdoc_sam0_sercom_spi_master_vec_examples Examples + * + * For a list of examples related to this driver, see + * \ref asfdoc_sam0_sercom_spi_master_vec_exqsg. + * + * + * \section asfdoc_sam0_sercom_spi_master_vec_api_overview API Overview + * + * @{ + */ + +/** + * Driver configuration structure + * + * \sa asfdoc_sam0_sercom_spi_master_vec_special_considerations for more + * information regarding SERCOM pad and pin MUX. + */ +struct spi_master_vec_config { + /** Baud rate in Hertz. */ + uint32_t baudrate; + /** GCLK generator to use for the SERCOM. */ + enum gclk_generator gclk_generator; + /** Enabled in sleep modes. */ + bool run_in_standby; + /** SERCOM pad MUX setting. */ + enum spi_signal_mux_setting mux_setting; + /** Transfer mode. */ + enum spi_transfer_mode transfer_mode; + /** Data order. */ + enum spi_data_order data_order; + /** Pin MUX setting for SERCOM pad 0. */ + uint32_t pinmux_pad0; + /** Pin MUX setting for SERCOM pad 1. */ + uint32_t pinmux_pad1; + /** Pin MUX setting for SERCOM pad 2. */ + uint32_t pinmux_pad2; + /** Pin MUX setting for SERCOM pad 4. */ + uint32_t pinmux_pad3; +}; + +/** Buffer length container. */ +typedef uint16_t spi_master_vec_buflen_t; + +/** Buffer descriptor structure. */ +struct spi_master_vec_bufdesc { + /** Pointer to buffer start. */ + void *data; + /** Length of buffer. */ + spi_master_vec_buflen_t length; +}; + +/** Transfer direction */ +enum _spi_master_vec_direction { + SPI_MASTER_VEC_DIRECTION_READ, + SPI_MASTER_VEC_DIRECTION_WRITE, + SPI_MASTER_VEC_DIRECTION_BOTH, + SPI_MASTER_VEC_DIRECTION_IDLE, +}; + +/** Driver instance. */ +struct spi_master_vec_module { +#if !defined(__DOXYGEN__) + Sercom *volatile sercom; + volatile bool locked; + volatile enum _spi_master_vec_direction direction; + volatile enum status_code status; + volatile spi_master_vec_buflen_t rx_length; + volatile spi_master_vec_buflen_t tx_length; + uint8_t *volatile rx_head_ptr; + uint8_t *volatile tx_head_ptr; + volatile uint_fast8_t tx_lead_on_rx; + struct spi_master_vec_bufdesc *volatile rx_bufdesc_ptr; + struct spi_master_vec_bufdesc *volatile tx_bufdesc_ptr; +# ifdef CONF_SPI_MASTER_VEC_OS_SUPPORT + CONF_SPI_MASTER_VEC_SEMAPHORE_TYPE busy_semaphore; +# endif +#endif +}; + +/** + * \name Configuration and Initialization + * @{ + */ + +/** + * \brief Initialize configuration with default values. + * + * \param[out] config Configuration struct to initialize. + */ +static inline void spi_master_vec_get_config_defaults( + struct spi_master_vec_config *const config) +{ + config->baudrate = 100000; + config->gclk_generator = GCLK_GENERATOR_0; + config->run_in_standby = false; + config->mux_setting = SPI_SIGNAL_MUX_SETTING_D; + config->transfer_mode = SPI_TRANSFER_MODE_0; + config->data_order = SPI_DATA_ORDER_MSB; + config->pinmux_pad0 = PINMUX_DEFAULT; + config->pinmux_pad1 = PINMUX_DEFAULT; + config->pinmux_pad2 = PINMUX_DEFAULT; + config->pinmux_pad3 = PINMUX_DEFAULT; +} + +#ifdef __cplusplus +extern "C" { +#endif + +enum status_code spi_master_vec_init(struct spi_master_vec_module *const module, + Sercom *const sercom, const struct spi_master_vec_config *const config); + +/** @} */ + +/** + * \name Enable/Disable and Reset + * @{ + */ + +void spi_master_vec_enable(const struct spi_master_vec_module *const module); +void spi_master_vec_disable(struct spi_master_vec_module *const module); +void spi_master_vec_reset(struct spi_master_vec_module *const module); + +/** @} */ + +/** + * \name Lock/Unlock + * @{ + */ + +/** + * \brief Attempt to get lock on driver instance + * + * This function checks the instance's lock, which indicates whether or not it + * is currently in use, and sets the lock if it was not already set. + * + * The purpose of this is to enable exclusive access to driver instances, so + * that, e.g., transactions by different services will not interfere with each + * other. + * + * \param[in,out] module Pointer to the driver instance to lock. + * + * \retval STATUS_OK if the module was locked. + * \retval STATUS_BUSY if the module was already locked. + */ +static inline enum status_code spi_master_vec_lock( + struct spi_master_vec_module *const module) +{ + enum status_code status; + + system_interrupt_enter_critical_section(); + + if (module->locked) { + status = STATUS_BUSY; + } else { + module->locked = true; + status = STATUS_OK; + } + + system_interrupt_leave_critical_section(); + + return status; +} + +/** + * \brief Unlock driver instance + * + * This function clears the instance lock, indicating that it is available for + * use. + * + * \param[in,out] module Pointer to the driver instance to lock. + */ +static inline void spi_master_vec_unlock( + struct spi_master_vec_module *const module) +{ + module->locked = false; +} + +/** @} */ + +/** + * \name Read/Write and Status + * @{ + */ + +enum status_code spi_master_vec_transceive_buffer_job( + struct spi_master_vec_module *const module, + struct spi_master_vec_bufdesc tx_bufdescs[], + struct spi_master_vec_bufdesc rx_bufdescs[]); + +/** + * \brief Get current status of transfer. + * + * \param[in] module Driver instance to operate on. + * + * \return Current status of driver instance. + * \retval STATUS_OK if idle and previous transfer succeeded. + * \retval STATUS_BUSY if a transfer is ongoing. + * \retval if previous transfer failed. + */ +static inline enum status_code spi_master_vec_get_job_status( + const struct spi_master_vec_module *const module) +{ + return module->status; +} + +/** + * \brief Get status of transfer upon job end. + * + * \param[in] module Driver instance to operate on. + * + * \return Current status of driver instance. + * \retval STATUS_OK if idle and previous transfer succeeded. + * \retval if previous transfer failed. + */ +static inline enum status_code spi_master_vec_get_job_status_wait( + const struct spi_master_vec_module *const module) +{ + enum status_code status; + +#ifdef CONF_SPI_MASTER_VEC_OS_SUPPORT + CONF_SPI_MASTER_VEC_TAKE_SEMAPHORE(module->busy_semaphore); + status = spi_master_vec_get_job_status(module); + CONF_SPI_MASTER_VEC_GIVE_SEMAPHORE(module->busy_semaphore); +#else + do { + status = spi_master_vec_get_job_status(module); + } while (status == STATUS_BUSY); +#endif + + return status; +} + + +/** + * \brief Start vectored I/O transfer, wait for it to end. + * + * \param[in,out] module Driver instance to operate on. + * \param[in] tx_bufdescs address of buffer descriptor array for bytes to + * transmit. + * \arg \c NULL if the transfer is a simplex read. + * \param[in,out] rx_bufdescs address of buffer descriptor array for storing + * received bytes. + * \arg \c NULL if the transfer is a simplex write. + * + * \return Status of transfer start. + * \retval STATUS_OK if transfer succeeded. + * \retval STATUS_BUSY if a transfer was already on-going. + * \retval if transfer failed. + */ +static inline enum status_code spi_master_vec_transceive_buffer_wait( + struct spi_master_vec_module *const module, + struct spi_master_vec_bufdesc tx_bufdescs[], + struct spi_master_vec_bufdesc rx_bufdescs[]) +{ + enum status_code status; + + status = spi_master_vec_transceive_buffer_job(module, tx_bufdescs, + rx_bufdescs); + + if (status == STATUS_BUSY) { + return status; + } + + return spi_master_vec_get_job_status_wait(module); +} + +/** @} */ + +#ifdef __cplusplus +} +#endif + +/** + * \name OS Support Configuration + * @{ + */ + +/** + * \def CONF_SPI_MASTER_VEC_OS_SUPPORT + * \brief Enable support for OS + * + * Defining this symbol will enable support for an OS, e.g., FreeRTOS, by using + * the macros in this group: + * - \ref CONF_SPI_MASTER_VEC_SEMAPHORE_TYPE + * - \ref CONF_SPI_MASTER_VEC_CREATE_SEMAPHORE + * - \ref CONF_SPI_MASTER_VEC_DELETE_SEMAPHORE + * - \ref CONF_SPI_MASTER_VEC_TAKE_SEMAPHORE + * - \ref CONF_SPI_MASTER_VEC_GIVE_SEMAPHORE + * - \ref CONF_SPI_MASTER_VEC_GIVE_SEMAPHORE_FROM_ISR + * + * The user must ensure that these macros map to the implementation for the OS + * that is used. Further, required includes must be added alongside these macros + * in \ref conf_spi_master_vec.h. + * + * The purpose of this configuration is to enable usage of the OS' semaphore + * system to wait for transfers to end rather than continuously polling the + * transfer status, which is an inefficient approach. + */ + +/** + * \def CONF_SPI_MASTER_VEC_SEMAPHORE_TYPE + * \brief Semaphore datatype + */ + +/** + * \def CONF_SPI_MASTER_VEC_CREATE_SEMAPHORE + * \brief Create/initialize semaphore + * + * \param semaphore Semaphore member in driver instance. + */ + +/** + * \def CONF_SPI_MASTER_VEC_DELETE_SEMAPHORE + * \brief Delete semaphore + * + * \param semaphore Semaphore member in driver instance. + */ + +/** + * \def CONF_SPI_MASTER_VEC_TAKE_SEMAPHORE + * \brief Wait for and take semaphore + * + * \param semaphore Semaphore member in driver instance. + */ + +/** + * \def CONF_SPI_MASTER_VEC_GIVE_SEMAPHORE + * \brief Give semaphore back + * + * \param semaphore Semaphore member in driver instance. + */ + +/** + * \def CONF_SPI_MASTER_VEC_GIVE_SEMAPHORE_FROM_ISR + * \brief Give semaphore back from an ISR + * + * \param semaphore Semaphore member in driver instance. + */ + +/** @} */ + +/** + * @} + */ + +/** + * \page asfdoc_sam0_sercom_spi_master_vec_extra Extra Information for SERCOM SPI Master Driver w/ Vectored I/O + * + * + * \section asfdoc_sam0_sercom_spi_master_vec_extra_acronyms Acronyms + * + * The table below presents the acronyms used in this module. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Acronym + * Description + *
GCLKGeneric Clock
MISOMaster Input, Slave Output
MOSIMaster Output, Slave Input
SCKSerial Clock
SPISerial Peripheral Interface
SSSlave Select
+ * + * + * \section asfdoc_sam0_sercom_spi_master_vec_extra_dependencies Dependencies + * + * This driver has the following dependencies: + * - \ref asfdoc_sam0_system_group "System driver" + * - \ref group_sam0_utils "Compiler driver" + * - \ref asfdoc_sam0_port_group "Port driver" + * - \ref Common SERCOM driver base + * + * + * \section asfdoc_sam0_sercom_spi_master_vec_extra_errata Errata + * + * There are no errata related to this driver. + * + * + * \section asfdoc_sam0_sercom_spi_master_vec_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
Initial Release
+ */ + +/** + * \page asfdoc_sam0_sercom_spi_master_vec_exqsg Examples for SERCOM SPI Master Driver w/ Vectored I/O + * + * This is a list of the available Quick Start guides (QSGs) and example + * applications for \ref asfdoc_sam0_sercom_spi_master_vec_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_sercom_spi_master_vec_basic + */ + + /** + * \page asfdoc_sam0_sercom_spi_master_vec_document_revision_history Document Revision History + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Doc. Rev. + * Date + * Comments + *
D12/2014Add SAM L21/C21 support.
C04/2014Add SAM D10/D11 support.
B03/2014Add SAMR21 support.
A01/2014Initial release
+ */ + +#endif /* SPI_MASTER_VEC_H */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/usart/docimg/usart_frame.svg b/atmel-samd/asf/sam0/drivers/sercom/usart/docimg/usart_frame.svg new file mode 100644 index 000000000..2a7550184 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/usart/docimg/usart_frame.svg @@ -0,0 +1,272 @@ + + + + + + + + + + + + + + + + + + + + + + Page-1 + + + Sheet.3 + + Sheet.1 + + + + Sheet.2 + + + + + Sheet.4 + + Sheet.5 + + + + Sheet.6 + + + + + Sheet.7 + + Sheet.8 + + + + Sheet.9 + + + + + Sheet.10 + + Sheet.11 + + + + Sheet.12 + + + + + Sheet.13 + + Sheet.14 + + + + Sheet.15 + + + + + Sheet.16 + + Sheet.17 + + + + Sheet.18 + + + + + Sheet.19 + + Sheet.20 + + + + Sheet.21 + + + + + Sheet.22 + 1 + + + + 1 + + Sheet.23 + 2 + + + + 2 + + Sheet.24 + 3 + + + + 3 + + Sheet.25 + 4 + + + + 4 + + Sheet.26 + [5] + + + + [5] + + Sheet.27 + [6] + + + + [6] + + Sheet.28 + [7] + + + + [7] + + Sheet.29 + [8] + + + + [8] + + Sheet.30 + + + + Sheet.31 + + Sheet.32 + + + + Sheet.33 + + + + + Sheet.34 + + + + Sheet.35 + + + + Sheet.37 + + + + Sheet.38 + 0 + + + + 0 + + Sheet.39 + St + + + + St + + Sheet.40 + (IDLE) + + + + (IDLE) + + Sheet.41 + Sp1 + + + + Sp1 + + Sheet.42 + [Sp2] + + + + [Sp2] + + Sheet.43 + (St/IDLE) + + + + (St/IDLE) + + Sheet.44 + + + + Sheet.45 + + + + Sheet.240 + + + + Sheet.36 + + + + Sheet.241 + [P] + + + + [P] + + Sheet.242 + Frame + + + + + Frame + + diff --git a/atmel-samd/asf/sam0/drivers/sercom/usart/usart.c b/atmel-samd/asf/sam0/drivers/sercom/usart/usart.c new file mode 100644 index 000000000..e0abee9b8 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/usart/usart.c @@ -0,0 +1,817 @@ +/** + * \file + * + * \brief SAM SERCOM USART Driver + * + * Copyright (C) 2012-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 "usart.h" +#include +#if USART_CALLBACK_MODE == true +# include "usart_interrupt.h" +#endif + +/** + * \internal + * Set Configuration of the USART module + */ +static enum status_code _usart_set_config( + struct usart_module *const module, + const struct usart_config *const config) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + /* Index for generic clock */ + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + uint32_t gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + + /* Cache new register values to minimize the number of register writes */ + uint32_t ctrla = 0; + uint32_t ctrlb = 0; +#ifdef FEATURE_USART_ISO7816 + uint32_t ctrlc = 0; +#endif + uint16_t baud = 0; + uint32_t transfer_mode; + + enum sercom_asynchronous_operation_mode mode = SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC; + enum sercom_asynchronous_sample_num sample_num = SERCOM_ASYNC_SAMPLE_NUM_16; + +#ifdef FEATURE_USART_OVER_SAMPLE + switch (config->sample_rate) { + case USART_SAMPLE_RATE_16X_ARITHMETIC: + mode = SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC; + sample_num = SERCOM_ASYNC_SAMPLE_NUM_16; + break; + case USART_SAMPLE_RATE_8X_ARITHMETIC: + mode = SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC; + sample_num = SERCOM_ASYNC_SAMPLE_NUM_8; + break; + case USART_SAMPLE_RATE_3X_ARITHMETIC: + mode = SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC; + sample_num = SERCOM_ASYNC_SAMPLE_NUM_3; + break; + case USART_SAMPLE_RATE_16X_FRACTIONAL: + mode = SERCOM_ASYNC_OPERATION_MODE_FRACTIONAL; + sample_num = SERCOM_ASYNC_SAMPLE_NUM_16; + break; + case USART_SAMPLE_RATE_8X_FRACTIONAL: + mode = SERCOM_ASYNC_OPERATION_MODE_FRACTIONAL; + sample_num = SERCOM_ASYNC_SAMPLE_NUM_8; + break; + } +#endif + + /* Set data order, internal muxing, and clock polarity */ + ctrla = (uint32_t)config->data_order | + (uint32_t)config->mux_setting | + #ifdef FEATURE_USART_OVER_SAMPLE + config->sample_adjustment | + config->sample_rate | + #endif + #ifdef FEATURE_USART_IMMEDIATE_BUFFER_OVERFLOW_NOTIFICATION + (config->immediate_buffer_overflow_notification << SERCOM_USART_CTRLA_IBON_Pos) | + #endif + (config->clock_polarity_inverted << SERCOM_USART_CTRLA_CPOL_Pos); + + enum status_code status_code = STATUS_OK; + + transfer_mode = (uint32_t)config->transfer_mode; +#ifdef FEATURE_USART_ISO7816 + if(config->iso7816_config.enabled) { + transfer_mode = config->iso7816_config.protocol_t; + } +#endif + /* Get baud value from mode and clock */ +#ifdef FEATURE_USART_ISO7816 + if(config->iso7816_config.enabled) { + baud = config->baudrate; + } else { +#endif + switch (transfer_mode) + { + case USART_TRANSFER_SYNCHRONOUSLY: + if (!config->use_external_clock) { + status_code = _sercom_get_sync_baud_val(config->baudrate, + system_gclk_chan_get_hz(gclk_index), &baud); + } + + break; + + case USART_TRANSFER_ASYNCHRONOUSLY: + if (config->use_external_clock) { + status_code = + _sercom_get_async_baud_val(config->baudrate, + config->ext_clock_freq, &baud, mode, sample_num); + } else { + status_code = + _sercom_get_async_baud_val(config->baudrate, + system_gclk_chan_get_hz(gclk_index), &baud, mode, sample_num); + } + + break; + } + + /* Check if calculating the baudrate failed */ + if (status_code != STATUS_OK) { + /* Abort */ + return status_code; + } +#ifdef FEATURE_USART_ISO7816 + } +#endif + +#ifdef FEATURE_USART_IRDA + if(config->encoding_format_enable) { + usart_hw->RXPL.reg = config->receive_pulse_length; + } +#endif + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + /*Set baud val */ + usart_hw->BAUD.reg = baud; + + /* Set sample mode */ + ctrla |= transfer_mode; + + if (config->use_external_clock == false) { + ctrla |= SERCOM_USART_CTRLA_MODE(0x1); + } + else { + ctrla |= SERCOM_USART_CTRLA_MODE(0x0); + } + + /* Set stopbits and enable transceivers */ + ctrlb = + #ifdef FEATURE_USART_IRDA + (config->encoding_format_enable << SERCOM_USART_CTRLB_ENC_Pos) | + #endif + #ifdef FEATURE_USART_START_FRAME_DECTION + (config->start_frame_detection_enable << SERCOM_USART_CTRLB_SFDE_Pos) | + #endif + #ifdef FEATURE_USART_COLLISION_DECTION + (config->collision_detection_enable << SERCOM_USART_CTRLB_COLDEN_Pos) | + #endif + (config->receiver_enable << SERCOM_USART_CTRLB_RXEN_Pos) | + (config->transmitter_enable << SERCOM_USART_CTRLB_TXEN_Pos); + +#ifdef FEATURE_USART_ISO7816 + if(config->iso7816_config.enabled) { + ctrla |= SERCOM_USART_CTRLA_FORM(0x07); + if (config->iso7816_config.enable_inverse) { + ctrla |= SERCOM_USART_CTRLA_TXINV | SERCOM_USART_CTRLA_RXINV; + } + ctrlb |= USART_CHARACTER_SIZE_8BIT; + + switch(config->iso7816_config.protocol_t) { + case ISO7816_PROTOCOL_T_0: + ctrlb |= (uint32_t)config->stopbits; + ctrlc |= SERCOM_USART_CTRLC_GTIME(config->iso7816_config.guard_time) | \ + (config->iso7816_config.inhibit_nack) | \ + (config->iso7816_config.successive_recv_nack) | \ + SERCOM_USART_CTRLC_MAXITER(config->iso7816_config.max_iterations); + break; + case ISO7816_PROTOCOL_T_1: + ctrlb |= USART_STOPBITS_1; + break; + } + } else { +#endif + ctrlb |= (uint32_t)config->character_size; + /* Check parity mode bits */ + if (config->parity != USART_PARITY_NONE) { + ctrla |= SERCOM_USART_CTRLA_FORM(1); + ctrlb |= config->parity; + } else { +#ifdef FEATURE_USART_LIN_SLAVE + if(config->lin_slave_enable) { + ctrla |= SERCOM_USART_CTRLA_FORM(0x4); + } else { + ctrla |= SERCOM_USART_CTRLA_FORM(0); + } +#else + ctrla |= SERCOM_USART_CTRLA_FORM(0); +#endif + } +#ifdef FEATURE_USART_ISO7816 + } +#endif + +#ifdef FEATURE_USART_LIN_MASTER + usart_hw->CTRLC.reg = ((usart_hw->CTRLC.reg) & SERCOM_USART_CTRLC_GTIME_Msk) + | config->lin_header_delay + | config->lin_break_length; + + if (config->lin_node != LIN_INVALID_MODE) { + ctrla &= ~(SERCOM_USART_CTRLA_FORM(0xf)); + ctrla |= config->lin_node; + } +#endif + + /* Set whether module should run in standby. */ + if (config->run_in_standby || system_is_debugger_present()) { + ctrla |= SERCOM_USART_CTRLA_RUNSTDBY; + } + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + /* Write configuration to CTRLB */ + usart_hw->CTRLB.reg = ctrlb; + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + /* Write configuration to CTRLA */ + usart_hw->CTRLA.reg = ctrla; + +#ifdef FEATURE_USART_RS485 + if ((usart_hw->CTRLA.reg & SERCOM_USART_CTRLA_FORM_Msk) != \ + SERCOM_USART_CTRLA_FORM(0x07)) { + usart_hw->CTRLC.reg &= ~(SERCOM_USART_CTRLC_GTIME(0x7)); + usart_hw->CTRLC.reg |= SERCOM_USART_CTRLC_GTIME(config->rs485_guard_time); + } +#endif + +#ifdef FEATURE_USART_ISO7816 + if(config->iso7816_config.enabled) { + _usart_wait_for_sync(module); + usart_hw->CTRLC.reg = ctrlc; + } +#endif + + return STATUS_OK; +} + +/** + * \brief Initializes the device + * + * Initializes the USART device based on the setting specified in the + * configuration struct. + * + * \param[out] module Pointer to USART device + * \param[in] hw Pointer to USART hardware instance + * \param[in] config Pointer to configuration struct + * + * \return Status of the initialization. + * + * \retval STATUS_OK The initialization was successful + * \retval STATUS_BUSY The USART module is busy + * resetting + * \retval STATUS_ERR_DENIED The USART has not been disabled in + * advance of initialization + * \retval STATUS_ERR_INVALID_ARG The configuration struct contains + * invalid configuration + * \retval STATUS_ERR_ALREADY_INITIALIZED The SERCOM instance has already been + * initialized with different clock + * configuration + * \retval STATUS_ERR_BAUD_UNAVAILABLE The BAUD rate given by the + * configuration + * struct cannot be reached with + * the current clock configuration + */ +enum status_code usart_init( + struct usart_module *const module, + Sercom *const hw, + const struct usart_config *const config) +{ + /* Sanity check arguments */ + Assert(module); + Assert(hw); + Assert(config); + + enum status_code status_code = STATUS_OK; + + /* Assign module pointer to software instance struct */ + module->hw = hw; + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + uint32_t pm_index, gclk_index; +#if (SAML22) || (SAMC20) + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; +#elif (SAML21) || (SAMR30) + if (sercom_index == 5) { + pm_index = MCLK_APBDMASK_SERCOM5_Pos; + gclk_index = SERCOM5_GCLK_ID_CORE; + } else { + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + } +#elif (SAMC21) + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + + if (sercom_index == 5){ + gclk_index = SERCOM5_GCLK_ID_CORE; + } else { + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + } +#else + pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; +#endif + + if (usart_hw->CTRLA.reg & SERCOM_USART_CTRLA_SWRST) { + /* The module is busy resetting itself */ + return STATUS_BUSY; + } + + if (usart_hw->CTRLA.reg & SERCOM_USART_CTRLA_ENABLE) { + /* Check the module is enabled */ + return STATUS_ERR_DENIED; + } + + /* Turn on module in PM */ +#if (SAML21) || (SAMR30) + if (sercom_index == 5) { + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBD, 1 << pm_index); + } else { + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); + } +#else + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); +#endif + + /* Set up the GCLK for the module */ + struct system_gclk_chan_config gclk_chan_conf; + system_gclk_chan_get_config_defaults(&gclk_chan_conf); + gclk_chan_conf.source_generator = config->generator_source; + system_gclk_chan_set_config(gclk_index, &gclk_chan_conf); + system_gclk_chan_enable(gclk_index); + sercom_set_gclk_generator(config->generator_source, false); + + /* Set character size */ + module->character_size = config->character_size; + + /* Set transmitter and receiver status */ + module->receiver_enabled = config->receiver_enable; + module->transmitter_enabled = config->transmitter_enable; + +#ifdef FEATURE_USART_LIN_SLAVE + module->lin_slave_enabled = config->lin_slave_enable; +#endif +#ifdef FEATURE_USART_START_FRAME_DECTION + module->start_frame_detection_enabled = config->start_frame_detection_enable; +#endif +#ifdef FEATURE_USART_ISO7816 + module->iso7816_mode_enabled = config->iso7816_config.enabled; +#endif + /* Set configuration according to the config struct */ + status_code = _usart_set_config(module, config); + if(status_code != STATUS_OK) { + return status_code; + } + + struct system_pinmux_config pin_conf; + system_pinmux_get_config_defaults(&pin_conf); + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_INPUT; + pin_conf.input_pull = SYSTEM_PINMUX_PIN_PULL_NONE; + + uint32_t pad_pinmuxes[] = { + config->pinmux_pad0, config->pinmux_pad1, + config->pinmux_pad2, config->pinmux_pad3 + }; + + /* Configure the SERCOM pins according to the user configuration */ + for (uint8_t pad = 0; pad < 4; pad++) { + uint32_t current_pinmux = pad_pinmuxes[pad]; + + if (current_pinmux == PINMUX_DEFAULT) { + current_pinmux = _sercom_get_default_pad(hw, pad); + } + + if (current_pinmux != PINMUX_UNUSED) { + pin_conf.mux_position = current_pinmux & 0xFFFF; + system_pinmux_pin_set_config(current_pinmux >> 16, &pin_conf); + } + } + +#if USART_CALLBACK_MODE == true + /* Initialize parameters */ + for (uint32_t i = 0; i < USART_CALLBACK_N; i++) { + module->callback[i] = NULL; + } + + module->tx_buffer_ptr = NULL; + module->rx_buffer_ptr = NULL; + module->remaining_tx_buffer_length = 0x0000; + module->remaining_rx_buffer_length = 0x0000; + module->callback_reg_mask = 0x00; + module->callback_enable_mask = 0x00; + module->rx_status = STATUS_OK; + module->tx_status = STATUS_OK; + + /* Set interrupt handler and register USART software module struct in + * look-up table */ + uint8_t instance_index = _sercom_get_sercom_inst_index(module->hw); + _sercom_set_handler(instance_index, _usart_interrupt_handler); + _sercom_instances[instance_index] = module; +#endif + + return status_code; +} + +/** + * \brief Transmit a character via the USART + * + * This blocking function will transmit a single character via the + * USART. + * + * \param[in] module Pointer to the software instance struct + * \param[in] tx_data Data to transfer + * + * \return Status of the operation. + * \retval STATUS_OK If the operation was completed + * \retval STATUS_BUSY If the operation was not completed, due to the USART + * module being busy + * \retval STATUS_ERR_DENIED If the transmitter is not enabled + */ +enum status_code usart_write_wait( + struct usart_module *const module, + const uint16_t tx_data) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + /* Check that the transmitter is enabled */ + if (!(module->transmitter_enabled)) { + return STATUS_ERR_DENIED; + } + +#if USART_CALLBACK_MODE == true + /* Check if the USART is busy doing asynchronous operation. */ + if (module->remaining_tx_buffer_length > 0) { + return STATUS_BUSY; + } + +#else + /* Check if USART is ready for new data */ + if (!(usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_DRE)) { + /* Return error code */ + return STATUS_BUSY; + } +#endif + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + /* Write data to USART module */ + usart_hw->DATA.reg = tx_data; + + while (!(usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_TXC)) { + /* Wait until data is sent */ + } + + return STATUS_OK; +} + +/** + * \brief Receive a character via the USART + * + * This blocking function will receive a character via the USART. + * + * \param[in] module Pointer to the software instance struct + * \param[out] rx_data Pointer to received data + * + * \return Status of the operation. + * \retval STATUS_OK If the operation was completed + * \retval STATUS_BUSY If the operation was not completed, + * due to the USART module being busy + * \retval STATUS_ERR_BAD_FORMAT If the operation was not completed, + * due to configuration mismatch between USART + * and the sender + * \retval STATUS_ERR_BAD_OVERFLOW If the operation was not completed, + * due to the baudrate being too low or the + * system frequency being too high + * \retval STATUS_ERR_BAD_DATA If the operation was not completed, due to + * data being corrupted + * \retval STATUS_ERR_DENIED If the receiver is not enabled + */ +enum status_code usart_read_wait( + struct usart_module *const module, + uint16_t *const rx_data) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Error variable */ + uint8_t error_code; + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + /* Check that the receiver is enabled */ + if (!(module->receiver_enabled)) { + return STATUS_ERR_DENIED; + } + +#if USART_CALLBACK_MODE == true + /* Check if the USART is busy doing asynchronous operation. */ + if (module->remaining_rx_buffer_length > 0) { + return STATUS_BUSY; + } +#endif + + /* Check if USART has new data */ + if (!(usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_RXC)) { + /* Return error code */ + return STATUS_BUSY; + } + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + /* Read out the status code and mask away all but the 3 LSBs*/ + error_code = (uint8_t)(usart_hw->STATUS.reg & SERCOM_USART_STATUS_MASK); + + /* Check if an error has occurred during the receiving */ + if (error_code) { + /* Check which error occurred */ + if (error_code & SERCOM_USART_STATUS_FERR) { + /* Clear flag by writing a 1 to it and + * return with an error code */ + usart_hw->STATUS.reg = SERCOM_USART_STATUS_FERR; + + return STATUS_ERR_BAD_FORMAT; + } else if (error_code & SERCOM_USART_STATUS_BUFOVF) { + /* Clear flag by writing a 1 to it and + * return with an error code */ + usart_hw->STATUS.reg = SERCOM_USART_STATUS_BUFOVF; + + return STATUS_ERR_OVERFLOW; + } else if (error_code & SERCOM_USART_STATUS_PERR) { + /* Clear flag by writing a 1 to it and + * return with an error code */ + usart_hw->STATUS.reg = SERCOM_USART_STATUS_PERR; + + return STATUS_ERR_BAD_DATA; + } +#ifdef FEATURE_USART_LIN_SLAVE + else if (error_code & SERCOM_USART_STATUS_ISF) { + /* Clear flag by writing 1 to it and + * return with an error code */ + usart_hw->STATUS.reg = SERCOM_USART_STATUS_ISF; + + return STATUS_ERR_PROTOCOL; + } +#endif +#ifdef FEATURE_USART_COLLISION_DECTION + else if (error_code & SERCOM_USART_STATUS_COLL) { + /* Clear flag by writing 1 to it + * return with an error code */ + usart_hw->STATUS.reg = SERCOM_USART_STATUS_COLL; + + return STATUS_ERR_PACKET_COLLISION; + } +#endif + } + + /* Read data from USART module */ + *rx_data = usart_hw->DATA.reg; + + return STATUS_OK; +} + +/** + * \brief Transmit a buffer of characters via the USART + * + * This blocking function will transmit a block of \c length characters + * via the USART. + * + * \note Using this function in combination with the interrupt (\c _job) functions is + * not recommended as it has no functionality to check if there is an + * ongoing interrupt driven operation running or not. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] tx_data Pointer to data to transmit + * \param[in] length Number of characters to transmit + * + * \note If using 9-bit data, the array that *tx_data point to should be defined + * as uint16_t array and should be casted to uint8_t* pointer. Because it + * is an address pointer, the highest byte is not discarded. For example: + * \code + #define TX_LEN 3 + uint16_t tx_buf[TX_LEN] = {0x0111, 0x0022, 0x0133}; + usart_write_buffer_wait(&module, (uint8_t*)tx_buf, TX_LEN); + \endcode + * + * \return Status of the operation. + * \retval STATUS_OK If operation was completed + * \retval STATUS_ERR_INVALID_ARG If operation was not completed, due to invalid + * arguments + * \retval STATUS_ERR_TIMEOUT If operation was not completed, due to USART + * module timing out + * \retval STATUS_ERR_DENIED If the transmitter is not enabled + */ +enum status_code usart_write_buffer_wait( + struct usart_module *const module, + const uint8_t *tx_data, + uint16_t length) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Check if the buffer length is valid */ + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + + /* Check that the transmitter is enabled */ + if (!(module->transmitter_enabled)) { + return STATUS_ERR_DENIED; + } + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + uint16_t tx_pos = 0; + + /* Blocks while buffer is being transferred */ + while (length--) { + /* Wait for the USART to be ready for new data and abort + * operation if it doesn't get ready within the timeout*/ + for (uint32_t i = 0; i <= USART_TIMEOUT; i++) { + if (usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_DRE) { + break; + } else if (i == USART_TIMEOUT) { + return STATUS_ERR_TIMEOUT; + } + } + + /* Data to send is at least 8 bits long */ + uint16_t data_to_send = tx_data[tx_pos++]; + + /* Check if the character size exceeds 8 bit */ + if (module->character_size == USART_CHARACTER_SIZE_9BIT) { + data_to_send |= (tx_data[tx_pos++] << 8); + } + + /* Send the data through the USART module */ + usart_write_wait(module, data_to_send); + } + + /* Wait until Transmit is complete or timeout */ + for (uint32_t i = 0; i <= USART_TIMEOUT; i++) { + if (usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_TXC) { + break; + } else if (i == USART_TIMEOUT) { + return STATUS_ERR_TIMEOUT; + } + } + + return STATUS_OK; +} + +/** + * \brief Receive a buffer of \c length characters via the USART + * + * This blocking function will receive a block of \c length characters + * via the USART. + * + * \note Using this function in combination with the interrupt (\c *_job) + * functions is not recommended as it has no functionality to check if + * there is an ongoing interrupt driven operation running or not. + * + * \param[in] module Pointer to USART software instance struct + * \param[out] rx_data Pointer to receive buffer + * \param[in] length Number of characters to receive + * + * \note If using 9-bit data, the array that *rx_data point to should be defined + * as uint16_t array and should be casted to uint8_t* pointer. Because it + * is an address pointer, the highest byte is not discarded. For example: + * \code + #define RX_LEN 3 + uint16_t rx_buf[RX_LEN] = {0x0,}; + usart_read_buffer_wait(&module, (uint8_t*)rx_buf, RX_LEN); + \endcode + * + * \return Status of the operation. + * \retval STATUS_OK If operation was completed + * \retval STATUS_ERR_INVALID_ARG If operation was not completed, due to an + * invalid argument being supplied + * \retval STATUS_ERR_TIMEOUT If operation was not completed, due + * to USART module timing out + * \retval STATUS_ERR_BAD_FORMAT If the operation was not completed, + * due to a configuration mismatch + * between USART and the sender + * \retval STATUS_ERR_BAD_OVERFLOW If the operation was not completed, + * due to the baudrate being too low or the + * system frequency being too high + * \retval STATUS_ERR_BAD_DATA If the operation was not completed, due + * to data being corrupted + * \retval STATUS_ERR_DENIED If the receiver is not enabled + */ +enum status_code usart_read_buffer_wait( + struct usart_module *const module, + uint8_t *rx_data, + uint16_t length) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Check if the buffer length is valid */ + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + + /* Check that the receiver is enabled */ + if (!(module->receiver_enabled)) { + return STATUS_ERR_DENIED; + } + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + uint16_t rx_pos = 0; + + /* Blocks while buffer is being received */ + while (length--) { + /* Wait for the USART to have new data and abort operation if it + * doesn't get ready within the timeout*/ + for (uint32_t i = 0; i <= USART_TIMEOUT; i++) { + if (usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_RXC) { + break; + } else if (i == USART_TIMEOUT) { + return STATUS_ERR_TIMEOUT; + } + } + + enum status_code retval; + uint16_t received_data = 0; + + retval = usart_read_wait(module, &received_data); + + if (retval != STATUS_OK) { + /* Overflow, abort */ + return retval; + } + + /* Read value will be at least 8-bits long */ + rx_data[rx_pos++] = received_data; + + /* If 9-bit data, write next received byte to the buffer */ + if (module->character_size == USART_CHARACTER_SIZE_9BIT) { + rx_data[rx_pos++] = (received_data >> 8); + } + } + + return STATUS_OK; +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/usart/usart.h b/atmel-samd/asf/sam0/drivers/sercom/usart/usart.h new file mode 100644 index 000000000..ac79c1116 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/usart/usart.h @@ -0,0 +1,1593 @@ +/** + * + * \file + * + * \brief SAM SERCOM USART Driver + * + * Copyright (C) 2012-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 USART_H_INCLUDED +#define USART_H_INCLUDED + +/** + * \defgroup asfdoc_sam0_sercom_usart_group SAM Serial USART (SERCOM USART) Driver + * + * This driver for Atmel® | SMART ARM®-based microcontrollers provides + * an interface for the configuration and management of the SERCOM module in + * its USART mode to transfer or receive USART data frames. The following driver + * API modes are covered by this manual: + * + * - Polled APIs + * \if USART_CALLBACK_MODE + * - Callback APIs + * \endif + * + * The following peripheral is used by this module: + * - SERCOM (Serial Communication Interface) + * + * 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 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_sercom_usart_prerequisites + * - \ref asfdoc_sam0_sercom_usart_overview + * - \ref asfdoc_sam0_sercom_usart_special_considerations + * - \ref asfdoc_sam0_sercom_usart_extra_info + * - \ref asfdoc_sam0_sercom_usart_examples + * - \ref asfdoc_sam0_sercom_usart_api_overview + * + * \section asfdoc_sam0_sercom_usart_prerequisites Prerequisites + * + * To use the USART you need to have a GCLK generator enabled and running + * that can be used as the SERCOM clock source. This can either be configured + * in conf_clocks.h or by using the system clock driver. + * + * \section asfdoc_sam0_sercom_usart_overview Module Overview + * + * This driver will use one (or more) SERCOM interface(s) in the system + * and configure it to run as a USART interface in either synchronous + * or asynchronous mode. + * + * \subsection asfdoc_sam0_sercom_usart_features Driver Feature Macro Definition + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Driver Feature MacroSupported devices
FEATURE_USART_SYNC_SCHEME_V2SAM D21/R21/D09/D10/D11/L21/L22/DA1/C20/C21
FEATURE_USART_OVER_SAMPLESAM D21/R21/D09/D10/D11/L21/L22/DA1/C20/C21
FEATURE_USART_HARDWARE_FLOW_CONTROLSAM D21/R21/D09/D10/D11/L21/L22/DA1/C20/C21
FEATURE_USART_IRDASAM D21/R21/D09/D10/D11/L21/L22/DA1/C20/C21
FEATURE_USART_LIN_SLAVESAM D21/R21/D09/D10/D11/L21/L22/DA1/C20/C21
FEATURE_USART_COLLISION_DECTIONSAM D21/R21/D09/D10/D11/L21/L22/DA1/C20/C21
FEATURE_USART_START_FRAME_DECTIONSAM D21/R21/D09/D10/D11/L21/L22/DA1/C20/C21
FEATURE_USART_IMMEDIATE_BUFFER_OVERFLOW_NOTIFICATIONSAM D21/R21/D09/D10/D11/L21/L22/DA1/C20/C21
FEATURE_USART_RS485SAM C20/C21
FEATURE_USART_LIN_MASTERSAM L22/C20/C21
+ * \note The specific features are only available in the driver when the + * selected device supports those features. + * + * \subsection asfdoc_sam0_sercom_usart_overview_frame_format Frame Format + * + * Communication is based on frames, where the frame format can be customized + * to accommodate a wide range of standards. A frame consists of a start bit, + * a number of data bits, an optional parity bit for error detection as well + * as a configurable length stop bit(s) - see + * \ref asfdoc_sam0_sercom_usart_frame_diagram "the figure below". + * \ref asfdoc_sam0_sercom_usart_frame_params "The table below" shows the + * available parameters you can change in a frame. + * + * \anchor asfdoc_sam0_sercom_usart_frame_params + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
USART Frame Parameters
ParameterOptions
Start bit1
Data bits5, 6, 7, 8, 9
Parity bitNone, Even, Odd
Stop bits1, 2
+ * + * \anchor asfdoc_sam0_sercom_usart_frame_diagram + * \image html usart_frame.svg "USART Frame Overview" width=100% + * + * \subsection asfdoc_sam0_sercom_usart_overview_sync Synchronous Mode + * + * In synchronous mode a dedicated clock line is provided; either by the USART + * itself if in master mode, or by an external master if in slave mode. + * Maximum transmission speed is the same as the GCLK clocking the USART + * peripheral when in slave mode, and the GCLK divided by two if in + * master mode. In synchronous mode the interface needs three lines to + * communicate: + * - TX (Transmit pin) + * - RX (Receive pin) + * - XCK (Clock pin) + * + * \subsubsection asfdoc_sam0_sercom_usart_overview_sync_sampling Data Sampling + * In synchronous mode the data is sampled on either the rising or falling edge + * of the clock signal. This is configured by setting the clock polarity in the + * configuration struct. + * + * \subsection asfdoc_sam0_sercom_usart_overview_async Asynchronous Mode + * + * In asynchronous mode no dedicated clock line is used, and the communication + * is based on matching the clock speed on the transmitter and receiver. The + * clock is generated from the internal SERCOM baudrate generator, and the + * frames are synchronized by using the frame start bits. Maximum transmission + * speed is limited to the SERCOM GCLK divided by 16. + * In asynchronous mode the interface only needs two lines to communicate: + * - TX (Transmit pin) + * - RX (Receive pin) + * + * \subsubsection asfdoc_sam0_sercom_usart_overview_async_clock_matching Transmitter/receiver Clock Matching + * + * For successful transmit and receive using the asynchronous mode the receiver + * and transmitter clocks needs to be closely matched. When receiving a frame + * that does not match the selected baudrate closely enough the receiver will + * be unable to synchronize the frame(s), and garbage transmissions will + * result. + * + * \subsection asfdoc_sam0_sercom_usart_parity Parity + * Parity can be enabled to detect if a transmission was in error. This is done + * by counting the number of "1" bits in the frame. When using even parity the + * parity bit will be set if the total number of "1"s in the frame are an even + * number. If using odd parity the parity bit will be set if the total number + * of "1"s are odd. + * + * When receiving a character the receiver will count the number of "1"s in the + * frame and give an error if the received frame and parity bit disagree. + * + * \subsection asfdoc_sam0_sercom_usart_overview_pin_configuration GPIO Configuration + * + * The SERCOM module has four internal pads; the RX pin can be placed freely on + * any one of the four pads, and the TX and XCK pins have two predefined + * positions that can be selected as a pair. The pads can then be routed to an + * external GPIO pin using the normal pin multiplexing scheme on the SAM. + * + * \section asfdoc_sam0_sercom_usart_special_considerations Special Considerations + * + * \if USART_CALLBACK_MODE + * Never execute large portions of code in the callbacks. These + * are run from the interrupt routine, and thus having long callbacks will + * keep the processor in the interrupt handler for an equally long time. + * A common way to handle this is to use global flags signaling the + * main application that an interrupt event has happened, and only do the + * minimal needed processing in the callback. + * \else + * No special considerations. + * \endif + * + * \section asfdoc_sam0_sercom_usart_extra_info Extra Information + * + * For extra information, see \ref asfdoc_sam0_sercom_usart_extra. This includes: + * - \ref asfdoc_sam0_sercom_usart_extra_acronyms + * - \ref asfdoc_sam0_sercom_usart_extra_dependencies + * - \ref asfdoc_sam0_sercom_usart_extra_errata + * - \ref asfdoc_sam0_sercom_usart_extra_history + * + * \section asfdoc_sam0_sercom_usart_examples Examples + * + * For a list of examples related to this driver, see + * \ref asfdoc_sam0_sercom_usart_exqsg. + * + * \section asfdoc_sam0_sercom_usart_api_overview API Overview + * @{ + */ + +#include +#include +#include + +#if USART_CALLBACK_MODE == true +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \name Driver Feature Definition + * Define SERCOM USART features set according to different device family. + * @{ + */ +#if (SAMD21) || (SAMR21) || (SAMD09) || (SAMD10) || (SAMD11) || (SAML21) || \ + (SAML22) ||(SAMDA1) || (SAMC20) || (SAMC21) || (SAMR30) || defined(__DOXYGEN__) +/** USART sync scheme version 2. */ +# define FEATURE_USART_SYNC_SCHEME_V2 +/** USART oversampling. */ +# define FEATURE_USART_OVER_SAMPLE +/** USART hardware control flow. */ +# define FEATURE_USART_HARDWARE_FLOW_CONTROL +/** IrDA mode. */ +# define FEATURE_USART_IRDA +/** LIN slave mode. */ +# define FEATURE_USART_LIN_SLAVE +/** USART collision detection. */ +# define FEATURE_USART_COLLISION_DECTION +/** USART start frame detection. */ +# define FEATURE_USART_START_FRAME_DECTION +/** USART start buffer overflow notification. */ +# define FEATURE_USART_IMMEDIATE_BUFFER_OVERFLOW_NOTIFICATION +#endif + +#if (SAML22) || defined(__DOXYGEN__) +/** ISO7816 for smart card interfacing. */ +#define FEATURE_USART_ISO7816 +#endif +#if (SAMC20) || (SAMC21) || defined(__DOXYGEN__) +/** LIN master mode. */ +#define FEATURE_USART_LIN_MASTER +#endif +#if (SAML22) || (SAMC20) || (SAMC21) || defined(__DOXYGEN__) +/** RS485 mode. */ +# define FEATURE_USART_RS485 +#endif +/*@}*/ + +#ifdef FEATURE_USART_LIN_MASTER +/** + * \brief LIN node type + * + * LIN node type. + */ +enum lin_node_type { + /** LIN master mode */ + LIN_MASTER_NODE = SERCOM_USART_CTRLA_FORM(0x02), + /** LIN slave mode */ + LIN_SLAVE_NODE = SERCOM_USART_CTRLA_FORM(0x04), + /** Neither LIN master nor LIN slave mode */ + LIN_INVALID_MODE = SERCOM_USART_CTRLA_FORM(0x00), +}; + +/** + * \brief LIN master command enum + * + * LIN master command enum. + */ +enum lin_master_cmd { + /** LIN master software control transmission command */ + LIN_MASTER_SOFTWARE_CONTROL_TRANSMIT_CMD = SERCOM_USART_CTRLB_LINCMD(0x01), + /** LIN master automatically transmission command */ + LIN_MASTER_AUTO_TRANSMIT_CMD = SERCOM_USART_CTRLB_LINCMD(0x02), +}; + +/** + * \brief LIN master header delay + * + * LIN master header delay between break and sync transmission, + * and between the sync and identifier (ID) fields. + * This field is only valid when using automatically transmission command + */ +enum lin_master_header_delay { + /** Delay between break and sync transmission is 1 bit time. + Delay between sync and ID transmission is 1 bit time. */ + LIN_MASTER_HEADER_DELAY_0 = SERCOM_USART_CTRLC_HDRDLY(0x0), + /** Delay between break and sync transmission is 4 bit time. + Delay between sync and ID transmission is 4 bit time. */ + LIN_MASTER_HEADER_DELAY_1 = SERCOM_USART_CTRLC_HDRDLY(0x01), + /** Delay between break and sync transmission is 8 bit time. + Delay between sync and ID transmission is 4 bit time. */ + LIN_MASTER_HEADER_DELAY_2 = SERCOM_USART_CTRLC_HDRDLY(0x02), + /** Delay between break and sync transmission is 14 bit time. + Delay between sync and ID transmission is 4 bit time. */ + LIN_MASTER_HEADER_DELAY_3 = SERCOM_USART_CTRLC_HDRDLY(0x03), +}; + +/** + * \brief LIN master break length + * + * Length of the break field transmitted when in LIN master mode + */ +enum lin_master_break_length { + /** Break field transmission is 13 bit times */ + LIN_MASTER_BREAK_LENGTH_13_BIT = SERCOM_USART_CTRLC_BRKLEN(0x0), + /** Break field transmission is 17 bit times */ + LIN_MASTER_BREAK_LENGTH_17_BIT = SERCOM_USART_CTRLC_BRKLEN(0x1), + /** Break field transmission is 21 bit times */ + LIN_MASTER_BREAK_LENGTH_21_BIT = SERCOM_USART_CTRLC_BRKLEN(0x2), + /** Break field transmission is 26 bit times */ + LIN_MASTER_BREAK_LENGTH_26_BIT = SERCOM_USART_CTRLC_BRKLEN(0x3), +}; +#endif +#ifdef FEATURE_USART_ISO7816 +/** + * \brief ISO7816 protocol type + * + * ISO7816 protocol type. + */ +enum iso7816_protocol_type { + /** ISO7816 protocol type 0 */ + ISO7816_PROTOCOL_T_0 = SERCOM_USART_CTRLA_CMODE, + /** ISO7816 protocol type 1 */ + ISO7816_PROTOCOL_T_1 = (0x0ul << SERCOM_USART_CTRLA_CMODE_Pos), +}; + +/** + * \brief ISO7816 guard time + * + * The value of ISO7816 guard time. + */ +enum iso7816_guard_time { + /** The guard time is 2-bit times */ + ISO7816_GUARD_TIME_2_BIT = 2, + /** The guard time is 3-bit times */ + ISO7816_GUARD_TIME_3_BIT, + /** The guard time is 4-bit times */ + ISO7816_GUARD_TIME_4_BIT, + /** The guard time is 5-bit times */ + ISO7816_GUARD_TIME_5_BIT, + /** The guard time is 6-bit times */ + ISO7816_GUARD_TIME_6_BIT, + /** The guard time is 7-bit times */ + ISO7816_GUARD_TIME_7_BIT, +}; + +/** + * \brief ISO7816 receive NACK inhibit + * + * The value of ISO7816 receive NACK inhibit. + */ +enum iso7816_inhibit_nack { + /** The NACK is generated */ + ISO7816_INHIBIT_NACK_DISABLE = (0x0ul << SERCOM_USART_CTRLC_INACK_Pos), + /** The NACK is not generated */ + ISO7816_INHIBIT_NACK_ENABLE = SERCOM_USART_CTRLC_INACK, +}; + +/** + * \brief ISO7816 disable successive receive NACK + * + * The value of ISO7816 disable successive receive NACK. + */ +enum iso7816_successive_recv_nack { + /** The successive receive NACK is enable. */ + ISO7816_SUCCESSIVE_RECV_NACK_DISABLE = (0x0ul << SERCOM_USART_CTRLC_INACK_Pos), + /** The successive receive NACK is disable. */ + ISO7816_SUCCESSIVE_RECV_NACK_ENABLE = SERCOM_USART_CTRLC_DSNACK, +}; + +/** + * \brief ISO7816 configuration struct + * + * ISO7816 configuration structure. + */ +struct iso7816_config_t { + /* ISO7816 mode enable */ + bool enabled; + /** ISO7816 protocol type */ + enum iso7816_protocol_type protocol_t; + /** Enable inverse transmission and reception */ + bool enable_inverse; + /** Guard time, which lasts two bit times */ + enum iso7816_guard_time guard_time; + /** + * Inhibit Non Acknowledge: + * - 0: the NACK is generated; + * - 1: the NACK is not generated. + */ + enum iso7816_inhibit_nack inhibit_nack; + /** + * Disable successive NACKs. + * - 0: NACK is sent on the ISO line as soon as a parity error occurs + * in the received character. Successive parity errors are counted up to + * the value in the max_iterations field. These parity errors generate + * a NACK on the ISO line. As soon as this value is reached, no additional + * NACK is sent on the ISO line. The ITERATION flag is asserted. + */ + enum iso7816_successive_recv_nack successive_recv_nack; + /* Max number of repetitions */ + uint32_t max_iterations; +}; +#endif + +#ifndef PINMUX_DEFAULT +/** Default pinmux */ +# define PINMUX_DEFAULT 0 +#endif + +#ifndef PINMUX_UNUSED +/** Unused pinmux */ +# define PINMUX_UNUSED 0xFFFFFFFF +#endif + +#ifndef USART_TIMEOUT +/** USART timeout value */ +# define USART_TIMEOUT 0xFFFF +#endif + +#if USART_CALLBACK_MODE == true +/** + * \brief USART callback enum + * + * Callbacks for the Asynchronous USART driver. + */ +enum usart_callback { + /** Callback for buffer transmitted */ + USART_CALLBACK_BUFFER_TRANSMITTED, + /** Callback for buffer received */ + USART_CALLBACK_BUFFER_RECEIVED, + /** Callback for error */ + USART_CALLBACK_ERROR, +#ifdef FEATURE_USART_LIN_SLAVE + /** Callback for break character is received */ + USART_CALLBACK_BREAK_RECEIVED, +#endif +#ifdef FEATURE_USART_HARDWARE_FLOW_CONTROL + /** Callback for a change is detected on the CTS pin */ + USART_CALLBACK_CTS_INPUT_CHANGE, +#endif +#ifdef FEATURE_USART_START_FRAME_DECTION + /** Callback for a start condition is detected on the RxD line */ + USART_CALLBACK_START_RECEIVED, +#endif +# if !defined(__DOXYGEN__) + /** Number of available callbacks */ + USART_CALLBACK_N, +# endif +}; +#endif + +/** + * \brief USART Data Order enum + * + * The data order decides which MSB or LSB is shifted out first when data is + * transferred. + */ +enum usart_dataorder { + /** The MSB will be shifted out first during transmission, + * and shifted in first during reception */ + USART_DATAORDER_MSB = 0, + /** The LSB will be shifted out first during transmission, + * and shifted in first during reception */ + USART_DATAORDER_LSB = SERCOM_USART_CTRLA_DORD, +}; + +/** + * \brief USART Transfer mode enum + * + * Select USART transfer mode. + */ +enum usart_transfer_mode { + /** Transfer of data is done synchronously */ + USART_TRANSFER_SYNCHRONOUSLY = (SERCOM_USART_CTRLA_CMODE), + /** Transfer of data is done asynchronously */ + USART_TRANSFER_ASYNCHRONOUSLY = (0x0ul << SERCOM_USART_CTRLA_CMODE_Pos), +}; + +/** + * \brief USART Parity enum + * + * Select parity USART parity mode. + */ +enum usart_parity { + /** For odd parity checking, the parity bit will be set if number of + * ones being transferred is even */ + USART_PARITY_ODD = SERCOM_USART_CTRLB_PMODE, + + /** For even parity checking, the parity bit will be set if number of + * ones being received is odd */ + USART_PARITY_EVEN = 0, + + /** No parity checking will be executed, and there will be no parity bit + * in the received frame */ + USART_PARITY_NONE = 0xFF, +}; + +/** + * \brief USART signal MUX settings + * + * Set the functionality of the SERCOM pins. + * + * See \ref asfdoc_sam0_sercom_usart_mux_settings for a description of the + * various MUX setting options. + */ +enum usart_signal_mux_settings { +#ifdef FEATURE_USART_HARDWARE_FLOW_CONTROL + /** MUX setting RX_0_TX_0_XCK_1 */ + USART_RX_0_TX_0_XCK_1 = (SERCOM_USART_CTRLA_RXPO(0) | SERCOM_USART_CTRLA_TXPO(0)), + /** MUX setting RX_0_TX_2_XCK_3 */ + USART_RX_0_TX_2_XCK_3 = (SERCOM_USART_CTRLA_RXPO(0) | SERCOM_USART_CTRLA_TXPO(1)), + /** MUX setting USART_RX_0_TX_0_RTS_2_CTS_3 */ + USART_RX_0_TX_0_RTS_2_CTS_3 = (SERCOM_USART_CTRLA_RXPO(0) | SERCOM_USART_CTRLA_TXPO(2)), + /** MUX setting RX_1_TX_0_XCK_1 */ + USART_RX_1_TX_0_XCK_1 = (SERCOM_USART_CTRLA_RXPO(1) | SERCOM_USART_CTRLA_TXPO(0)), + /** MUX setting RX_1_TX_2_XCK_3 */ + USART_RX_1_TX_2_XCK_3 = (SERCOM_USART_CTRLA_RXPO(1) | SERCOM_USART_CTRLA_TXPO(1)), + /** MUX setting USART_RX_1_TX_0_RTS_2_CTS_3 */ + USART_RX_1_TX_0_RTS_2_CTS_3 = (SERCOM_USART_CTRLA_RXPO(1) | SERCOM_USART_CTRLA_TXPO(2)), + /** MUX setting RX_2_TX_0_XCK_1 */ + USART_RX_2_TX_0_XCK_1 = (SERCOM_USART_CTRLA_RXPO(2) | SERCOM_USART_CTRLA_TXPO(0)), + /** MUX setting RX_2_TX_2_XCK_3 */ + USART_RX_2_TX_2_XCK_3 = (SERCOM_USART_CTRLA_RXPO(2) | SERCOM_USART_CTRLA_TXPO(1)), + /** MUX setting USART_RX_2_TX_0_RTS_2_CTS_3 */ + USART_RX_2_TX_0_RTS_2_CTS_3 = (SERCOM_USART_CTRLA_RXPO(2) | SERCOM_USART_CTRLA_TXPO(2)), + /** MUX setting RX_3_TX_0_XCK_1 */ + USART_RX_3_TX_0_XCK_1 = (SERCOM_USART_CTRLA_RXPO(3) | SERCOM_USART_CTRLA_TXPO(0)), + /** MUX setting RX_3_TX_2_XCK_3 */ + USART_RX_3_TX_2_XCK_3 = (SERCOM_USART_CTRLA_RXPO(3) | SERCOM_USART_CTRLA_TXPO(1)), + /** MUX setting USART_RX_3_TX_0_RTS_2_CTS_3 */ + USART_RX_3_TX_0_RTS_2_CTS_3 = (SERCOM_USART_CTRLA_RXPO(3) | SERCOM_USART_CTRLA_TXPO(2)), +#ifdef FEATURE_USART_RS485 + /** MUX setting USART_RX_0_TX_0_XCK_1_TE_2 */ + USART_RX_0_TX_0_XCK_1_TE_2 = (SERCOM_USART_CTRLA_RXPO(0) | SERCOM_USART_CTRLA_TXPO(3)), + /** MUX setting USART_RX_1_TX_0_XCK_1_TE_2 */ + USART_RX_1_TX_0_XCK_1_TE_2 = (SERCOM_USART_CTRLA_RXPO(1) | SERCOM_USART_CTRLA_TXPO(3)), + /** MUX setting USART_RX_2_TX_0_XCK_1_TE_2 */ + USART_RX_2_TX_0_XCK_1_TE_2 = (SERCOM_USART_CTRLA_RXPO(2) | SERCOM_USART_CTRLA_TXPO(3)), + /** MUX setting USART_RX_3_TX_0_XCK_1_TE_2 */ + USART_RX_3_TX_0_XCK_1_TE_2 = (SERCOM_USART_CTRLA_RXPO(3) | SERCOM_USART_CTRLA_TXPO(3)), +#endif +#else + /** MUX setting RX_0_TX_0_XCK_1 */ + USART_RX_0_TX_0_XCK_1 = (SERCOM_USART_CTRLA_RXPO(0)), + /** MUX setting RX_0_TX_2_XCK_3 */ + USART_RX_0_TX_2_XCK_3 = (SERCOM_USART_CTRLA_RXPO(0) | SERCOM_USART_CTRLA_TXPO), + /** MUX setting RX_1_TX_0_XCK_1 */ + USART_RX_1_TX_0_XCK_1 = (SERCOM_USART_CTRLA_RXPO(1)), + /** MUX setting RX_1_TX_2_XCK_3 */ + USART_RX_1_TX_2_XCK_3 = (SERCOM_USART_CTRLA_RXPO(1) | SERCOM_USART_CTRLA_TXPO), + /** MUX setting RX_2_TX_0_XCK_1 */ + USART_RX_2_TX_0_XCK_1 = (SERCOM_USART_CTRLA_RXPO(2)), + /** MUX setting RX_2_TX_2_XCK_3 */ + USART_RX_2_TX_2_XCK_3 = (SERCOM_USART_CTRLA_RXPO(2) | SERCOM_USART_CTRLA_TXPO), + /** MUX setting RX_3_TX_0_XCK_1 */ + USART_RX_3_TX_0_XCK_1 = (SERCOM_USART_CTRLA_RXPO(3)), + /** MUX setting RX_3_TX_2_XCK_3 */ + USART_RX_3_TX_2_XCK_3 = (SERCOM_USART_CTRLA_RXPO(3) | SERCOM_USART_CTRLA_TXPO), +#endif +}; + +/** + * \brief USART Stop Bits enum + * + * Number of stop bits for a frame. + */ +enum usart_stopbits { + /** Each transferred frame contains one stop bit */ + USART_STOPBITS_1 = 0, + /** Each transferred frame contains two stop bits */ + USART_STOPBITS_2 = SERCOM_USART_CTRLB_SBMODE, +}; + +/** + * \brief USART Character Size + * + * Number of bits for the character sent in a frame. + */ +enum usart_character_size { + /** The char being sent in a frame is five bits long */ + USART_CHARACTER_SIZE_5BIT = SERCOM_USART_CTRLB_CHSIZE(5), + /** The char being sent in a frame is six bits long */ + USART_CHARACTER_SIZE_6BIT = SERCOM_USART_CTRLB_CHSIZE(6), + /** The char being sent in a frame is seven bits long */ + USART_CHARACTER_SIZE_7BIT = SERCOM_USART_CTRLB_CHSIZE(7), + /** The char being sent in a frame is eight bits long */ + USART_CHARACTER_SIZE_8BIT = SERCOM_USART_CTRLB_CHSIZE(0), + /** The char being sent in a frame is nine bits long */ + USART_CHARACTER_SIZE_9BIT = SERCOM_USART_CTRLB_CHSIZE(1), +}; + +#ifdef FEATURE_USART_OVER_SAMPLE +/** + * \brief USART Sample Rate + * + * The value of sample rate and baudrate generation mode. + */ +enum usart_sample_rate { + /** 16x over-sampling using arithmetic baudrate generation */ + USART_SAMPLE_RATE_16X_ARITHMETIC = SERCOM_USART_CTRLA_SAMPR(0), + /** 16x over-sampling using fractional baudrate generation */ + USART_SAMPLE_RATE_16X_FRACTIONAL = SERCOM_USART_CTRLA_SAMPR(1), + /** 8x over-sampling using arithmetic baudrate generation */ + USART_SAMPLE_RATE_8X_ARITHMETIC = SERCOM_USART_CTRLA_SAMPR(2), + /** 8x over-sampling using fractional baudrate generation */ + USART_SAMPLE_RATE_8X_FRACTIONAL = SERCOM_USART_CTRLA_SAMPR(3), + /** 3x over-sampling using arithmetic baudrate generation */ + USART_SAMPLE_RATE_3X_ARITHMETIC = SERCOM_USART_CTRLA_SAMPR(4), +}; + +/** + * \brief USART Sample Adjustment + * + * The value of sample number used for majority voting. + */ +enum usart_sample_adjustment { + /** The first, middle and last sample number used for majority voting is 7-8-9 */ + USART_SAMPLE_ADJUSTMENT_7_8_9 = SERCOM_USART_CTRLA_SAMPA(0), + /** The first, middle and last sample number used for majority voting is 9-10-11 */ + USART_SAMPLE_ADJUSTMENT_9_10_11 = SERCOM_USART_CTRLA_SAMPA(1), + /** The first, middle and last sample number used for majority voting is 11-12-13 */ + USART_SAMPLE_ADJUSTMENT_11_12_13 = SERCOM_USART_CTRLA_SAMPA(2), + /** The first, middle and last sample number used for majority voting is 13-14-15 */ + USART_SAMPLE_ADJUSTMENT_13_14_15 = SERCOM_USART_CTRLA_SAMPA(3), +}; +#endif + +#ifdef FEATURE_USART_RS485 +/** + * \brief RS485 Guard Time + * + * The value of RS485 guard time. + */ +enum rs485_guard_time { + /** The guard time is 0-bit time */ + RS485_GUARD_TIME_0_BIT = 0, + /** The guard time is 1-bit time */ + RS485_GUARD_TIME_1_BIT, + /** The guard time is 2-bit times */ + RS485_GUARD_TIME_2_BIT, + /** The guard time is 3-bit times */ + RS485_GUARD_TIME_3_BIT, + /** The guard time is 4-bit times */ + RS485_GUARD_TIME_4_BIT, + /** The guard time is 5-bit times */ + RS485_GUARD_TIME_5_BIT, + /** The guard time is 6-bit times */ + RS485_GUARD_TIME_6_BIT, + /** The guard time is 7-bit times */ + RS485_GUARD_TIME_7_BIT, +}; +#endif + +/** + * \brief USART Transceiver + * + * Select Receiver or Transmitter. + */ +enum usart_transceiver_type { + /** The parameter is for the Receiver */ + USART_TRANSCEIVER_RX, + /** The parameter is for the Transmitter */ + USART_TRANSCEIVER_TX, +}; + +/** + * \brief USART configuration struct + * + * Configuration options for USART. + */ +struct usart_config { + /** USART bit order (MSB or LSB first) */ + enum usart_dataorder data_order; + /** USART in asynchronous or synchronous mode */ + enum usart_transfer_mode transfer_mode; + /** USART parity */ + enum usart_parity parity; + /** Number of stop bits */ + enum usart_stopbits stopbits; + /** USART character size */ + enum usart_character_size character_size; + /** USART pin out */ + enum usart_signal_mux_settings mux_setting; +#ifdef FEATURE_USART_OVER_SAMPLE + /** USART sample rate */ + enum usart_sample_rate sample_rate; + /** USART sample adjustment */ + enum usart_sample_adjustment sample_adjustment; +#endif +#ifdef FEATURE_USART_IMMEDIATE_BUFFER_OVERFLOW_NOTIFICATION + /** Controls when the buffer overflow status bit is asserted when a buffer overflow occurs */ + bool immediate_buffer_overflow_notification; +#endif +#ifdef FEATURE_USART_IRDA + /** Enable IrDA encoding format */ + bool encoding_format_enable; + /** The minimum pulse length required for a pulse to be accepted by the IrDA receiver */ + uint8_t receive_pulse_length; +#endif +#ifdef FEATURE_USART_LIN_SLAVE + /** Enable LIN Slave Support */ + bool lin_slave_enable; +#endif + +#ifdef FEATURE_USART_LIN_MASTER + /** LIN node type */ + enum lin_node_type lin_node; + /** LIN master header delay */ + enum lin_master_header_delay lin_header_delay; + /** LIN Master Break Length */ + enum lin_master_break_length lin_break_length; +#endif + +#ifdef FEATURE_USART_START_FRAME_DECTION + /** Enable start of frame dection */ + bool start_frame_detection_enable; +#endif +#ifdef FEATURE_USART_ISO7816 + /** Enable ISO7816 for smart card interfacing */ + struct iso7816_config_t iso7816_config; +#endif +#ifdef FEATURE_USART_RS485 + /** RS485 guard time */ + enum rs485_guard_time rs485_guard_time; +#endif +#ifdef FEATURE_USART_COLLISION_DECTION + /** Enable collision dection */ + bool collision_detection_enable; +#endif + /** USART baudrate */ + uint32_t baudrate; + /** Enable receiver */ + bool receiver_enable; + /** Enable transmitter */ + bool transmitter_enable; + + /** USART Clock Polarity. + * If true, data changes on falling XCK edge and + * is sampled at rising edge. + * If false, data changes on rising XCK edge and + * is sampled at falling edge. + * */ + bool clock_polarity_inverted; + + /** States whether to use the external clock applied to the XCK pin. + * In synchronous mode the shift register will act directly on the XCK clock. + * In asynchronous mode the XCK will be the input to the USART hardware module. + */ + bool use_external_clock; + /** External clock frequency in synchronous mode. + * This must be set if \c use_external_clock is true. */ + uint32_t ext_clock_freq; + /** If true the USART will be kept running in Standby sleep mode */ + bool run_in_standby; + /** GCLK generator source */ + enum gclk_generator generator_source; + /** PAD0 pinmux. + * + * If current USARTx has several alternative multiplexing I/O pins for PAD0, then + * only one peripheral multiplexing I/O can be enabled for current USARTx PAD0 + * function. Make sure that no other alternative multiplexing I/O is associated + * with the same USARTx PAD0. + */ + uint32_t pinmux_pad0; + /** PAD1 pinmux. + * + * If current USARTx has several alternative multiplexing I/O pins for PAD1, then + * only one peripheral multiplexing I/O can be enabled for current USARTx PAD1 + * function. Make sure that no other alternative multiplexing I/O is associated + * with the same USARTx PAD1. + */ + uint32_t pinmux_pad1; + /** PAD2 pinmux. + * + * If current USARTx has several alternative multiplexing I/O pins for PAD2, then + * only one peripheral multiplexing I/O can be enabled for current USARTx PAD2 + * function. Make sure that no other alternative multiplexing I/O is associated + * with the same USARTx PAD2. + */ + uint32_t pinmux_pad2; + /** PAD3 pinmux. + * + * If current USARTx has several alternative multiplexing I/O pins for PAD3, then + * only one peripheral multiplexing I/O can be enabled for current USARTx PAD3 + * function. Make sure that no other alternative multiplexing I/O is associated + * with the same USARTx PAD3. + */ + uint32_t pinmux_pad3; +}; + +#if USART_CALLBACK_MODE == true +/** + * \brief USART module instance + * + * Forward Declaration for the device instance. + */ +struct usart_module; + +/** + * \brief USART callback type + * + * Type of the callback functions. + */ +typedef void (*usart_callback_t)(struct usart_module *const module); +#endif + +/** + * \brief SERCOM USART driver software device instance structure. + * + * SERCOM USART driver 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 usart_module { +#if !defined(__DOXYGEN__) + /** Pointer to the hardware instance */ + Sercom *hw; + /** Module lock */ + volatile bool locked; + /** Character size of the data being transferred */ + enum usart_character_size character_size; + /** Receiver enabled */ + bool receiver_enabled; + /** Transmitter enabled */ + bool transmitter_enabled; +#ifdef FEATURE_USART_LIN_SLAVE + /** LIN Slave Support enabled */ + bool lin_slave_enabled; +#endif +#ifdef FEATURE_USART_START_FRAME_DECTION + /** Start of frame dection enabled */ + bool start_frame_detection_enabled; +#endif +#ifdef FEATURE_USART_ISO7816 + /** ISO7816 mode enable */ + bool iso7816_mode_enabled; +#endif +# if USART_CALLBACK_MODE == true + /** Array to store callback function pointers in */ + usart_callback_t callback[USART_CALLBACK_N]; + /** Buffer pointer to where the next received character will be put */ + volatile uint8_t *rx_buffer_ptr; + + /** Buffer pointer to where the next character will be transmitted from + **/ + volatile uint8_t *tx_buffer_ptr; + /** Remaining characters to receive */ + volatile uint16_t remaining_rx_buffer_length; + /** Remaining characters to transmit */ + volatile uint16_t remaining_tx_buffer_length; + /** Bit mask for callbacks registered */ + uint8_t callback_reg_mask; + /** Bit mask for callbacks enabled */ + uint8_t callback_enable_mask; + /** Holds the status of the ongoing or last read operation */ + volatile enum status_code rx_status; + /** Holds the status of the ongoing or last write operation */ + volatile enum status_code tx_status; +# endif +#endif +}; + + /** + * \name Lock/Unlock + * @{ + */ + +/** + * \brief Attempt to get lock on driver instance + * + * This function checks the instance's lock, which indicates whether or not it + * is currently in use, and sets the lock if it was not already set. + * + * The purpose of this is to enable exclusive access to driver instances, so + * that, e.g., transactions by different services will not interfere with each + * other. + * + * \param[in,out] module Pointer to the driver instance to lock + * + * \retval STATUS_OK If the module was locked + * \retval STATUS_BUSY If the module was already locked + */ +static inline enum status_code usart_lock( + struct usart_module *const module) +{ + enum status_code status; + + system_interrupt_enter_critical_section(); + + if (module->locked) { + status = STATUS_BUSY; + } else { + module->locked = true; + status = STATUS_OK; + } + + system_interrupt_leave_critical_section(); + + return status; +} + +/** + * \brief Unlock driver instance + * + * This function clears the instance lock, indicating that it is available for + * use. + * + * \param[in,out] module Pointer to the driver instance to lock + * + */ +static inline void usart_unlock(struct usart_module *const module) +{ + module->locked = false; +} + +/** @} */ + +/** + * \brief Check if peripheral is busy syncing registers across clock domains + * + * Return peripheral synchronization status. If doing a non-blocking + * implementation this function can be used to check the sync state and hold of + * any new actions until sync is complete. If this function is not run; the + * functions will block until the sync has completed. + * + * \param[in] module Pointer to peripheral module + * + * \return Peripheral sync status. + * + * \retval true Peripheral is busy syncing + * \retval false Peripheral is not busy syncing and can be read/written without + * stalling the bus + */ +static inline bool usart_is_syncing( + const struct usart_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomUsart *const usart_hw = &(module->hw->USART); + +#ifdef FEATURE_USART_SYNC_SCHEME_V2 + return (usart_hw->SYNCBUSY.reg); +#else + return (usart_hw->STATUS.reg & SERCOM_USART_STATUS_SYNCBUSY); +#endif +} + +#if !defined (__DOXYGEN__) +/** + * \internal + * Waits until synchronization is complete + */ +static inline void _usart_wait_for_sync( + const struct usart_module *const module) +{ + /* Sanity check */ + Assert(module); + + while (usart_is_syncing(module)) { + /* Wait until the synchronization is complete */ + } +} +#endif + +/** + * \brief Initializes the device to predefined defaults + * + * Initialize the USART device to predefined defaults: + * - 8-bit asynchronous USART + * - No parity + * - One stop bit + * - 9600 baud + * - Transmitter enabled + * - Receiver enabled + * - GCLK generator 0 as clock source + * - Default pin configuration + * + * The configuration struct will be updated with the default + * configuration. + * + * \param[in,out] config Pointer to configuration struct + */ +static inline void usart_get_config_defaults( + struct usart_config *const config) +{ + /* Sanity check arguments */ + Assert(config); + + /* Set default config in the config struct */ + config->data_order = USART_DATAORDER_LSB; + config->transfer_mode = USART_TRANSFER_ASYNCHRONOUSLY; + config->parity = USART_PARITY_NONE; + config->stopbits = USART_STOPBITS_1; + config->character_size = USART_CHARACTER_SIZE_8BIT; + config->baudrate = 9600; + config->receiver_enable = true; + config->transmitter_enable = true; + config->clock_polarity_inverted = false; + config->use_external_clock = false; + config->ext_clock_freq = 0; + config->mux_setting = USART_RX_1_TX_2_XCK_3; + config->run_in_standby = false; + config->generator_source = GCLK_GENERATOR_0; + config->pinmux_pad0 = PINMUX_DEFAULT; + config->pinmux_pad1 = PINMUX_DEFAULT; + config->pinmux_pad2 = PINMUX_DEFAULT; + config->pinmux_pad3 = PINMUX_DEFAULT; +#ifdef FEATURE_USART_OVER_SAMPLE + config->sample_adjustment = USART_SAMPLE_ADJUSTMENT_7_8_9; + config->sample_rate = USART_SAMPLE_RATE_16X_ARITHMETIC; +#endif +#ifdef FEATURE_USART_LIN_SLAVE + config->lin_slave_enable = false; +#endif + +#ifdef FEATURE_USART_LIN_MASTER + config->lin_node = LIN_INVALID_MODE; + config->lin_header_delay = LIN_MASTER_HEADER_DELAY_0; + config->lin_break_length = LIN_MASTER_BREAK_LENGTH_13_BIT; +#endif + +#ifdef FEATURE_USART_IMMEDIATE_BUFFER_OVERFLOW_NOTIFICATION + config->immediate_buffer_overflow_notification = false; +#endif +#ifdef FEATURE_USART_START_FRAME_DECTION + config->start_frame_detection_enable = false; +#endif +#ifdef FEATURE_USART_IRDA + config->encoding_format_enable = false; + config->receive_pulse_length = 19; +#endif +#ifdef FEATURE_USART_ISO7816 + config->iso7816_config.enabled = false; + config->iso7816_config.guard_time = ISO7816_GUARD_TIME_2_BIT; + config->iso7816_config.protocol_t = ISO7816_PROTOCOL_T_0; + config->iso7816_config.enable_inverse = false; + config->iso7816_config.inhibit_nack = ISO7816_INHIBIT_NACK_DISABLE; + config->iso7816_config.successive_recv_nack = ISO7816_SUCCESSIVE_RECV_NACK_DISABLE; + config->iso7816_config.max_iterations = 7; +#endif +#ifdef FEATURE_USART_COLLISION_DECTION + config->collision_detection_enable = false; +#endif +#ifdef FEATURE_USART_RS485 + config->rs485_guard_time = RS485_GUARD_TIME_0_BIT; +#endif +} + +enum status_code usart_init( + struct usart_module *const module, + Sercom *const hw, + const struct usart_config *const config); + +/** + * \brief Enable the module + * + * Enables the USART module. + * + * \param[in] module Pointer to USART software instance struct + */ +static inline void usart_enable( + const struct usart_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + +#if USART_CALLBACK_MODE == true + /* Enable Global interrupt for module */ + system_interrupt_enable(_sercom_get_interrupt_vector(module->hw)); +#endif + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + /* Enable USART module */ + usart_hw->CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE; +} + +/** + * \brief Disable module + * + * Disables the USART module. + * + * \param[in] module Pointer to USART software instance struct + */ +static inline void usart_disable( + const struct usart_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + +#if USART_CALLBACK_MODE == true + /* Disable Global interrupt for module */ + system_interrupt_disable(_sercom_get_interrupt_vector(module->hw)); +#endif + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + /* Disable USART module */ + usart_hw->CTRLA.reg &= ~SERCOM_USART_CTRLA_ENABLE; +} + +/** + * \brief Resets the USART module + * + * Disables and resets the USART module. + * + * \param[in] module Pointer to the USART software instance struct + */ +static inline void usart_reset( + const struct usart_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + usart_disable(module); + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + /* Reset module */ + usart_hw->CTRLA.reg = SERCOM_USART_CTRLA_SWRST; +} + +/** + * \name Writing and Reading + * @{ + */ +enum status_code usart_write_wait( + struct usart_module *const module, + const uint16_t tx_data); + +enum status_code usart_read_wait( + struct usart_module *const module, + uint16_t *const rx_data); + +enum status_code usart_write_buffer_wait( + struct usart_module *const module, + const uint8_t *tx_data, + uint16_t length); + +enum status_code usart_read_buffer_wait( + struct usart_module *const module, + uint8_t *rx_data, + uint16_t length); +/** @} */ + +/** + * \name Enabling/Disabling Receiver and Transmitter + * @{ + */ + +/** + * \brief Enable Transceiver + * + * Enable the given transceiver. Either RX or TX. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] transceiver_type Transceiver type + */ +static inline void usart_enable_transceiver( + struct usart_module *const module, + enum usart_transceiver_type transceiver_type) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + switch (transceiver_type) { + case USART_TRANSCEIVER_RX: + /* Enable RX */ + usart_hw->CTRLB.reg |= SERCOM_USART_CTRLB_RXEN; + module->receiver_enabled = true; + break; + + case USART_TRANSCEIVER_TX: + /* Enable TX */ + usart_hw->CTRLB.reg |= SERCOM_USART_CTRLB_TXEN; + module->transmitter_enabled = true; + break; + } + _usart_wait_for_sync(module); +} + +/** + * \brief Disable Transceiver + * + * Disable the given transceiver (RX or TX). + * + * \param[in] module Pointer to USART software instance struct + * \param[in] transceiver_type Transceiver type + */ +static inline void usart_disable_transceiver( + struct usart_module *const module, + enum usart_transceiver_type transceiver_type) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + switch (transceiver_type) { + case USART_TRANSCEIVER_RX: + /* Disable RX */ + usart_hw->CTRLB.reg &= ~SERCOM_USART_CTRLB_RXEN; + module->receiver_enabled = false; + break; + + case USART_TRANSCEIVER_TX: + /* Disable TX */ + usart_hw->CTRLB.reg &= ~SERCOM_USART_CTRLB_TXEN; + module->transmitter_enabled = false; + break; + } +} + +/** @} */ + +#ifdef FEATURE_USART_LIN_MASTER +/** + * \name LIN Master Command and Status + * @{ + */ + +/** + * \brief Sending LIN command. + * + * Sending LIN command. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] cmd Cammand type + */ +static inline void lin_master_send_cmd( + struct usart_module *const module, + enum lin_master_cmd cmd) +{ + SercomUsart *const usart_hw = &(module->hw->USART); + _usart_wait_for_sync(module); + usart_hw->CTRLB.reg |= cmd; +} + +/** + * \brief Get LIN transmission status + * + * Get LIN transmission status. + * + * \param[in] module Pointer to USART software instance struct + * + * \return Status of LIN master transmission. + * \retval true Data transmission completed + * \retval false Transmission is ongoing + */ +static inline bool lin_master_transmission_status(struct usart_module *const module) +{ + SercomUsart *const usart_hw = &(module->hw->USART); + return ((usart_hw->STATUS.reg & SERCOM_USART_STATUS_TXE)? true:false); +} + +/** @} */ +#endif + +#ifdef __cplusplus +} +#endif + +/** @} */ + +/** +* \page asfdoc_sam0_sercom_usart_extra Extra Information for SERCOM USART Driver +* +* \section asfdoc_sam0_sercom_usart_extra_acronyms Acronyms +* +* Below is a table listing the acronyms used in this module, along with their +* intended meanings. +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +*
AcronymDescription
SERCOMSerial Communication Interface
USARTUniversal Synchronous and Asynchronous Serial Receiver and Transmitter
LSBLeast Significant Bit
MSBMost Significant Bit
DMADirect Memory Access
+* +* +* \section asfdoc_sam0_sercom_usart_extra_dependencies Dependencies +* This driver has the following dependencies: +* +* - \ref asfdoc_sam0_system_pinmux_group "System Pin Multiplexer Driver" +* - \ref asfdoc_sam0_system_clock_group "System clock configuration" +* +* +* \section asfdoc_sam0_sercom_usart_extra_errata Errata +* There are no errata related to this driver. +* +* +* \section asfdoc_sam0_sercom_usart_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 new feature as below: + * \li ISO7816 + *
Added new features as below: + * \li LIN master + * \li RS485 + *
Added new features as below: + * \li Oversample + * \li Buffer overflow notification + * \li Irda + * \li Lin slave + * \li Start frame detection + * \li Hardware flow control + * \li Collision detection + * \li DMA support
\li Added new \c transmitter_enable and \c receiver_enable Boolean + * values to \c struct usart_config + * \li Altered \c usart_write_* and usart_read_* functions to abort with + * an error code if the relevant transceiver is not enabled + * \li Fixed \c usart_write_buffer_wait() and \c usart_read_buffer_wait() + * not aborting correctly when a timeout condition occurs
Initial Release
+*/ + +/** + * \page asfdoc_sam0_sercom_usart_exqsg Examples for SERCOM USART Driver + * + * This is a list of the available Quick Start guides (QSGs) and example + * applications for \ref asfdoc_sam0_sercom_usart_group. QSGs are simple examples with + * step-by-step instructions to configure and use this driver in a selection of + * use cases. Note that a QSG can be compiled as a standalone application or be + * added to the user application. + * + * - \subpage asfdoc_sam0_sercom_usart_basic_use_case + * \if USART_CALLBACK_MODE + * - \subpage asfdoc_sam0_sercom_usart_callback_use_case + * \endif + * - \subpage asfdoc_sam0_sercom_usart_dma_use_case + * - \subpage asfdoc_sam0_sercom_usart_lin_use_case + */ + +/** + * \page asfdoc_sam0_sercom_usart_mux_settings SERCOM USART MUX Settings + * + * The following lists the possible internal SERCOM module pad function + * assignments, for the four SERCOM pads when in USART mode. Note that this is + * in addition to the physical GPIO pin MUX of the device, and can be used in + * conjunction to optimize the serial data pin-out. + * + * When TX and RX are connected to the same pin, the USART will operate in + * half-duplex mode if both one transmitter and several receivers are enabled. + * + * \note When RX and XCK are connected to the same pin, the receiver must not + * be enabled if the USART is configured to use an external clock. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
MUX/PadPAD 0PAD 1PAD 2PAD 3
RX_0_TX_0_XCK_1TX / RXXCK--
RX_0_TX_2_XCK_3RX-TXXCK
RX_1_TX_0_XCK_1TXRX / XCK--
RX_1_TX_2_XCK_3-RXTXXCK
RX_2_TX_0_XCK_1TXXCKRX-
RX_2_TX_2_XCK_3--TX / RXXCK
RX_3_TX_0_XCK_1TXXCK-RX
RX_3_TX_2_XCK_3--TXRX / XCK
+ * + * \page asfdoc_sam0_sercom_usart_document_revision_history Document Revision History + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Doc. Rev. + * Date + * Comments + *
42118F12/2015Added support for SAM L21/L22, SAM DA1, SAM D09, SAMR30 and SAM C20/C21
42118E12/2014Added support for SAM R21 and SAM D10/D11
42118D01/2014Added support for SAM D21
42118C10/2013Replaced the pad multiplexing documentation with a condensed table
42118B06/2013Corrected documentation typos
42118A06/2013Initial release
+ */ +#endif /* USART_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/usart/usart_interrupt.c b/atmel-samd/asf/sam0/drivers/sercom/usart/usart_interrupt.c new file mode 100644 index 000000000..cc042894c --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/usart/usart_interrupt.c @@ -0,0 +1,666 @@ +/** + * \file + * + * \brief SAM SERCOM USART Asynchronous Driver + * + * Copyright (C) 2012-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 "usart_interrupt.h" + +/** + * \internal + * Asynchronous write of a buffer with a given length + * + * \param[in] module Pointer to USART software instance struct + * \param[in] tx_data Pointer to data to be transmitted + * \param[in] length Length of data buffer + * + */ +enum status_code _usart_write_buffer( + struct usart_module *const module, + uint8_t *tx_data, + uint16_t length) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + Assert(tx_data); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + system_interrupt_enter_critical_section(); + + /* Check if the USART transmitter is busy */ + if (module->remaining_tx_buffer_length > 0) { + system_interrupt_leave_critical_section(); + return STATUS_BUSY; + } + + /* Write parameters to the device instance */ + module->remaining_tx_buffer_length = length; + + system_interrupt_leave_critical_section(); + + module->tx_buffer_ptr = tx_data; + module->tx_status = STATUS_BUSY; + + /* Enable the Data Register Empty Interrupt */ + usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_DRE; + + return STATUS_OK; +} + +/** + * \internal + * Asynchronous read of a buffer with a given length + * + * \param[in] module Pointer to USART software instance struct + * \param[in] rx_data Pointer to data to be received + * \param[in] length Length of data buffer + * + */ +enum status_code _usart_read_buffer( + struct usart_module *const module, + uint8_t *rx_data, + uint16_t length) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + Assert(rx_data); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + system_interrupt_enter_critical_section(); + + /* Check if the USART receiver is busy */ + if (module->remaining_rx_buffer_length > 0) { + system_interrupt_leave_critical_section(); + return STATUS_BUSY; + } + + /* Set length for the buffer and the pointer, and let + * the interrupt handler do the rest */ + module->remaining_rx_buffer_length = length; + + system_interrupt_leave_critical_section(); + + module->rx_buffer_ptr = rx_data; + module->rx_status = STATUS_BUSY; + + /* Enable the RX Complete Interrupt */ + usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_RXC; + +#ifdef FEATURE_USART_LIN_SLAVE + /* Enable the break character is received Interrupt */ + if(module->lin_slave_enabled) { + usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_RXBRK; + } +#endif + +#ifdef FEATURE_USART_START_FRAME_DECTION + /* Enable a start condition is detected Interrupt */ + if(module->start_frame_detection_enabled) { + usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_RXS; + } +#endif + + return STATUS_OK; +} + +/** + * \brief Registers a callback + * + * Registers a callback function, which is implemented by the user. + * + * \note The callback must be enabled by \ref usart_enable_callback + * in order for the interrupt handler to call it when the conditions for + * the callback type are met. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] callback_func Pointer to callback function + * \param[in] callback_type Callback type given by an enum + * + */ +void usart_register_callback( + struct usart_module *const module, + usart_callback_t callback_func, + enum usart_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->callback_reg_mask |= (1 << callback_type); +} + +/** + * \brief Unregisters a callback + * + * Unregisters a callback function, which is implemented by the user. + * + * \param[in,out] module Pointer to USART software instance struct + * \param[in] callback_type Callback type given by an enum + * + */ +void usart_unregister_callback( + struct usart_module *const module, + enum usart_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->callback_reg_mask &= ~(1 << callback_type); +} + +/** + * \brief Asynchronous write a single char + * + * Sets up the driver to write the data given. If registered and enabled, + * a callback function will be called when the transmit is completed. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] tx_data Data to transfer + * + * \returns Status of the operation. + * \retval STATUS_OK If operation was completed + * \retval STATUS_BUSY If operation was not completed, due to the + * USART module being busy + * \retval STATUS_ERR_DENIED If the transmitter is not enabled + */ +enum status_code usart_write_job( + struct usart_module *const module, + const uint16_t *tx_data) +{ + /* Sanity check arguments */ + Assert(module); + Assert(tx_data); + + + /* Check that the transmitter is enabled */ + if (!(module->transmitter_enabled)) { + return STATUS_ERR_DENIED; + } + + /* Call internal write buffer function with length 1 */ + return _usart_write_buffer(module, (uint8_t *)tx_data, 1); +} + +/** + * \brief Asynchronous read a single char + * + * Sets up the driver to read data from the USART module to the data + * pointer given. If registered and enabled, a callback will be called + * when the receiving is completed. + * + * \param[in] module Pointer to USART software instance struct + * \param[out] rx_data Pointer to where received data should be put + * + * \returns Status of the operation. + * \retval STATUS_OK If operation was completed + * \retval STATUS_BUSY If operation was not completed + */ +enum status_code usart_read_job( + struct usart_module *const module, + uint16_t *const rx_data) +{ + /* Sanity check arguments */ + Assert(module); + Assert(rx_data); + + /* Call internal read buffer function with length 1 */ + return _usart_read_buffer(module, (uint8_t *)rx_data, 1); +} + +/** + * \brief Asynchronous buffer write + * + * Sets up the driver to write a given buffer over the USART. If registered and + * enabled, a callback function will be called. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] tx_data Pointer do data buffer to transmit + * \param[in] length Length of the data to transmit + * + * \note If using 9-bit data, the array that *tx_data point to should be defined + * as uint16_t array and should be casted to uint8_t* pointer. Because it + * is an address pointer, the highest byte is not discarded. For example: + * \code + #define TX_LEN 3 + uint16_t tx_buf[TX_LEN] = {0x0111, 0x0022, 0x0133}; + usart_write_buffer_job(&module, (uint8_t*)tx_buf, TX_LEN); + \endcode + * + * \returns Status of the operation. + * \retval STATUS_OK If operation was completed successfully. + * \retval STATUS_BUSY If operation was not completed, due to the + * USART module being busy + * \retval STATUS_ERR_INVALID_ARG If operation was not completed, due to invalid + * arguments + * \retval STATUS_ERR_DENIED If the transmitter is not enabled + */ +enum status_code usart_write_buffer_job( + struct usart_module *const module, + uint8_t *tx_data, + uint16_t length) +{ + /* Sanity check arguments */ + Assert(module); + Assert(tx_data); + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + + /* Check that the transmitter is enabled */ + if (!(module->transmitter_enabled)) { + return STATUS_ERR_DENIED; + } + + /* Issue internal asynchronous write */ + return _usart_write_buffer(module, tx_data, length); +} + +/** + * \brief Asynchronous buffer read + * + * Sets up the driver to read from the USART to a given buffer. If registered + * and enabled, a callback function will be called. + * + * \param[in] module Pointer to USART software instance struct + * \param[out] rx_data Pointer to data buffer to receive + * \param[in] length Data buffer length + * + * \note If using 9-bit data, the array that *rx_data point to should be defined + * as uint16_t array and should be casted to uint8_t* pointer. Because it + * is an address pointer, the highest byte is not discarded. For example: + * \code + #define RX_LEN 3 + uint16_t rx_buf[RX_LEN] = {0x0,}; + usart_read_buffer_job(&module, (uint8_t*)rx_buf, RX_LEN); + \endcode + * + * \returns Status of the operation. + * \retval STATUS_OK If operation was completed + * \retval STATUS_BUSY If operation was not completed, due to the + * USART module being busy + * \retval STATUS_ERR_INVALID_ARG If operation was not completed, due to invalid + * arguments + * \retval STATUS_ERR_DENIED If the transmitter is not enabled + */ +enum status_code usart_read_buffer_job( + struct usart_module *const module, + uint8_t *rx_data, + uint16_t length) +{ + /* Sanity check arguments */ + Assert(module); + Assert(rx_data); + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + + /* Check that the receiver is enabled */ + if (!(module->receiver_enabled)) { + return STATUS_ERR_DENIED; + } + + /* Issue internal asynchronous read */ + return _usart_read_buffer(module, rx_data, length); +} + +/** + * \brief Cancels ongoing read/write operation + * + * Cancels the ongoing read/write operation modifying parameters in the + * USART software struct. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] transceiver_type Transfer type to cancel + */ +void usart_abort_job( + struct usart_module *const module, + enum usart_transceiver_type transceiver_type) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + switch(transceiver_type) { + case USART_TRANSCEIVER_RX: + /* Clear the interrupt flag in order to prevent the receive + * complete callback to fire */ + usart_hw->INTFLAG.reg = SERCOM_USART_INTFLAG_RXC; + + /* Clear the software reception buffer */ + module->remaining_rx_buffer_length = 0; + + break; + + case USART_TRANSCEIVER_TX: + /* Clear the interrupt flag in order to prevent the receive + * complete callback to fire */ + usart_hw->INTFLAG.reg = SERCOM_USART_INTFLAG_TXC; + + /* Clear the software reception buffer */ + module->remaining_tx_buffer_length = 0; + + break; + } +} + +/** + * \brief Get status from the ongoing or last asynchronous transfer operation + * + * Returns the error from a given ongoing or last asynchronous transfer operation. + * Either from a read or write transfer. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] transceiver_type Transfer type to check + * + * \return Status of the given job. + * \retval STATUS_OK No error occurred during the last transfer + * \retval STATUS_BUSY A transfer is ongoing + * \retval STATUS_ERR_BAD_DATA The last operation was aborted due to a + * parity error. The transfer could be affected + * by external noise + * \retval STATUS_ERR_BAD_FORMAT The last operation was aborted due to a + * frame error + * \retval STATUS_ERR_OVERFLOW The last operation was aborted due to a + * buffer overflow + * \retval STATUS_ERR_INVALID_ARG An invalid transceiver enum given + */ +enum status_code usart_get_job_status( + struct usart_module *const module, + enum usart_transceiver_type transceiver_type) +{ + /* Sanity check arguments */ + Assert(module); + + /* Variable for status code */ + enum status_code status_code; + + switch(transceiver_type) { + case USART_TRANSCEIVER_RX: + status_code = module->rx_status; + break; + + case USART_TRANSCEIVER_TX: + status_code = module->tx_status; + break; + + default: + status_code = STATUS_ERR_INVALID_ARG; + break; + } + + return status_code; +} + +/** + * \internal + * Handles interrupts as they occur, and it will run callback functions + * which are registered and enabled. + * + * \param[in] instance ID of the SERCOM instance calling the interrupt + * handler. + */ +void _usart_interrupt_handler( + uint8_t instance) +{ + /* Temporary variables */ + uint16_t interrupt_status; + uint16_t callback_status; + uint8_t error_code; + + + /* Get device instance from the look-up table */ + struct usart_module *module + = (struct usart_module *)_sercom_instances[instance]; + + /* Pointer to the hardware module instance */ + SercomUsart *const usart_hw + = &(module->hw->USART); + + /* Wait for the synchronization to complete */ + _usart_wait_for_sync(module); + + /* Read and mask interrupt flag register */ + interrupt_status = usart_hw->INTFLAG.reg; + interrupt_status &= usart_hw->INTENSET.reg; + callback_status = module->callback_reg_mask & + module->callback_enable_mask; + + /* Check if a DATA READY interrupt has occurred, + * and if there is more to transfer */ + if (interrupt_status & SERCOM_USART_INTFLAG_DRE) { + if (module->remaining_tx_buffer_length) { + /* Write value will be at least 8-bits long */ + uint16_t data_to_send = *(module->tx_buffer_ptr); + /* Increment 8-bit pointer */ + (module->tx_buffer_ptr)++; + + if (module->character_size == USART_CHARACTER_SIZE_9BIT) { + data_to_send |= (*(module->tx_buffer_ptr) << 8); + /* Increment 8-bit pointer */ + (module->tx_buffer_ptr)++; + } + /* Write the data to send */ + usart_hw->DATA.reg = (data_to_send & SERCOM_USART_DATA_MASK); + + if (--(module->remaining_tx_buffer_length) == 0) { + /* Disable the Data Register Empty Interrupt */ + usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_DRE; + /* Enable Transmission Complete interrupt */ + usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_TXC; + + } + } else { + usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_DRE; + } + } + + /* Check if the Transmission Complete interrupt has occurred and + * that the transmit buffer is empty */ + if (interrupt_status & SERCOM_USART_INTFLAG_TXC) { + + /* Disable TX Complete Interrupt, and set STATUS_OK */ + usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_TXC; + module->tx_status = STATUS_OK; + + /* Run callback if registered and enabled */ + if (callback_status & (1 << USART_CALLBACK_BUFFER_TRANSMITTED)) { + (*(module->callback[USART_CALLBACK_BUFFER_TRANSMITTED]))(module); + } + } + + /* Check if the Receive Complete interrupt has occurred, and that + * there's more data to receive */ + if (interrupt_status & SERCOM_USART_INTFLAG_RXC) { + + if (module->remaining_rx_buffer_length) { + /* Read out the status code and mask away all but the 4 LSBs*/ + error_code = (uint8_t)(usart_hw->STATUS.reg & SERCOM_USART_STATUS_MASK); +#if !SAMD20 + /* CTS status should not be considered as an error */ + if(error_code & SERCOM_USART_STATUS_CTS) { + error_code &= ~SERCOM_USART_STATUS_CTS; + } +#endif +#ifdef FEATURE_USART_LIN_MASTER + /* TXE status should not be considered as an error */ + if(error_code & SERCOM_USART_STATUS_TXE) { + error_code &= ~SERCOM_USART_STATUS_TXE; + } +#endif + /* Check if an error has occurred during the receiving */ + if (error_code) { + /* Check which error occurred */ + if (error_code & SERCOM_USART_STATUS_FERR) { + /* Store the error code and clear flag by writing 1 to it */ + module->rx_status = STATUS_ERR_BAD_FORMAT; + usart_hw->STATUS.reg = SERCOM_USART_STATUS_FERR; + } else if (error_code & SERCOM_USART_STATUS_BUFOVF) { + /* Store the error code and clear flag by writing 1 to it */ + module->rx_status = STATUS_ERR_OVERFLOW; + usart_hw->STATUS.reg = SERCOM_USART_STATUS_BUFOVF; + } else if (error_code & SERCOM_USART_STATUS_PERR) { + /* Store the error code and clear flag by writing 1 to it */ + module->rx_status = STATUS_ERR_BAD_DATA; + usart_hw->STATUS.reg = SERCOM_USART_STATUS_PERR; + } +#ifdef FEATURE_USART_LIN_SLAVE + else if (error_code & SERCOM_USART_STATUS_ISF) { + /* Store the error code and clear flag by writing 1 to it */ + module->rx_status = STATUS_ERR_PROTOCOL; + usart_hw->STATUS.reg = SERCOM_USART_STATUS_ISF; + } +#endif +#ifdef FEATURE_USART_COLLISION_DECTION + else if (error_code & SERCOM_USART_STATUS_COLL) { + /* Store the error code and clear flag by writing 1 to it */ + module->rx_status = STATUS_ERR_PACKET_COLLISION; + usart_hw->STATUS.reg = SERCOM_USART_STATUS_COLL; + } +#endif + + /* Run callback if registered and enabled */ + if (callback_status + & (1 << USART_CALLBACK_ERROR)) { + (*(module->callback[USART_CALLBACK_ERROR]))(module); + } + + } else { + + /* Read current packet from DATA register, + * increment buffer pointer and decrement buffer length */ + uint16_t received_data = (usart_hw->DATA.reg & SERCOM_USART_DATA_MASK); + + /* Read value will be at least 8-bits long */ + *(module->rx_buffer_ptr) = received_data; + /* Increment 8-bit pointer */ + module->rx_buffer_ptr += 1; + + if (module->character_size == USART_CHARACTER_SIZE_9BIT) { + /* 9-bit data, write next received byte to the buffer */ + *(module->rx_buffer_ptr) = (received_data >> 8); + /* Increment 8-bit pointer */ + module->rx_buffer_ptr += 1; + } + + /* Check if the last character have been received */ + if(--(module->remaining_rx_buffer_length) == 0) { + /* Disable RX Complete Interrupt, + * and set STATUS_OK */ + usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_RXC; + module->rx_status = STATUS_OK; + + /* Run callback if registered and enabled */ + if (callback_status + & (1 << USART_CALLBACK_BUFFER_RECEIVED)) { + (*(module->callback[USART_CALLBACK_BUFFER_RECEIVED]))(module); + } + } + } + } else { + /* This should not happen. Disable Receive Complete interrupt. */ + usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_RXC; + } + } + +#ifdef FEATURE_USART_HARDWARE_FLOW_CONTROL + if (interrupt_status & SERCOM_USART_INTFLAG_CTSIC) { + /* Disable interrupts */ + usart_hw->INTENCLR.reg = SERCOM_USART_INTENCLR_CTSIC; + /* Clear interrupt flag */ + usart_hw->INTFLAG.reg = SERCOM_USART_INTFLAG_CTSIC; + + /* Run callback if registered and enabled */ + if (callback_status & (1 << USART_CALLBACK_CTS_INPUT_CHANGE)) { + (*(module->callback[USART_CALLBACK_CTS_INPUT_CHANGE]))(module); + } + } +#endif + +#ifdef FEATURE_USART_LIN_SLAVE + if (interrupt_status & SERCOM_USART_INTFLAG_RXBRK) { + /* Disable interrupts */ + usart_hw->INTENCLR.reg = SERCOM_USART_INTENCLR_RXBRK; + /* Clear interrupt flag */ + usart_hw->INTFLAG.reg = SERCOM_USART_INTFLAG_RXBRK; + + /* Run callback if registered and enabled */ + if (callback_status & (1 << USART_CALLBACK_BREAK_RECEIVED)) { + (*(module->callback[USART_CALLBACK_BREAK_RECEIVED]))(module); + } + } +#endif + +#ifdef FEATURE_USART_START_FRAME_DECTION + if (interrupt_status & SERCOM_USART_INTFLAG_RXS) { + /* Disable interrupts */ + usart_hw->INTENCLR.reg = SERCOM_USART_INTENCLR_RXS; + /* Clear interrupt flag */ + usart_hw->INTFLAG.reg = SERCOM_USART_INTFLAG_RXS; + + /* Run callback if registered and enabled */ + if (callback_status & (1 << USART_CALLBACK_START_RECEIVED)) { + (*(module->callback[USART_CALLBACK_START_RECEIVED]))(module); + } + } +#endif +} + diff --git a/atmel-samd/asf/sam0/drivers/sercom/usart/usart_interrupt.h b/atmel-samd/asf/sam0/drivers/sercom/usart/usart_interrupt.h new file mode 100644 index 000000000..26595d0a0 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/usart/usart_interrupt.h @@ -0,0 +1,177 @@ +/** + * \file + * + * \brief SAM SERCOM USART Asynchronous Driver + * + * Copyright (C) 2012-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 USART_INTERRUPT_H_INCLUDED +#define USART_INTERRUPT_H_INCLUDED + +#include "usart.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(__DOXYGEN__) +enum status_code _usart_write_buffer( + struct usart_module *const module, + uint8_t *tx_data, + uint16_t length); + +enum status_code _usart_read_buffer( + struct usart_module *const module, + uint8_t *rx_data, + uint16_t length); + +void _usart_interrupt_handler( + uint8_t instance); +#endif + +/** + * \addtogroup asfdoc_sam0_sercom_usart_group + * + * @{ + */ + +/** + * \name Callback Management + * @{ + */ +void usart_register_callback( + struct usart_module *const module, + usart_callback_t callback_func, + enum usart_callback callback_type); + +void usart_unregister_callback( + struct usart_module *module, + enum usart_callback callback_type); + +/** + * \brief Enables callback + * + * Enables the callback function registered by the \ref usart_register_callback. + * The callback function will be called from the interrupt handler when the + * conditions for the callback type are met. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] callback_type Callback type given by an enum + */ +static inline void usart_enable_callback( + struct usart_module *const module, + enum usart_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module); + + /* Enable callback */ + module->callback_enable_mask |= (1 << callback_type); + +} + +/** + * \brief Disable callback + * + * Disables the callback function registered by the \ref usart_register_callback, + * and the callback will not be called from the interrupt routine. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] callback_type Callback type given by an enum + */ +static inline void usart_disable_callback( + struct usart_module *const module, + enum usart_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module); + + /* Disable callback */ + module->callback_enable_mask &= ~(1 << callback_type); +} + +/** + * @} + */ + +/** + * \name Writing and Reading + * @{ + */ +enum status_code usart_write_job( + struct usart_module *const module, + const uint16_t *tx_data); + +enum status_code usart_read_job( + struct usart_module *const module, + uint16_t *const rx_data); + +enum status_code usart_write_buffer_job( + struct usart_module *const module, + uint8_t *tx_data, + uint16_t length); + +enum status_code usart_read_buffer_job( + struct usart_module *const module, + uint8_t *rx_data, + uint16_t length); + +void usart_abort_job( + struct usart_module *const module, + enum usart_transceiver_type transceiver_type); + +enum status_code usart_get_job_status( + struct usart_module *const module, + enum usart_transceiver_type transceiver_type); +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* USART_INTERRUPT_H_INCLUDED */ + diff --git a/atmel-samd/asf/sam0/utils/cmsis/samd21/source/gcc/startup_samd21.c b/atmel-samd/asf/sam0/utils/cmsis/samd21/source/gcc/startup_samd21.c index d2611b3d5..c5561b316 100644 --- a/atmel-samd/asf/sam0/utils/cmsis/samd21/source/gcc/startup_samd21.c +++ b/atmel-samd/asf/sam0/utils/cmsis/samd21/source/gcc/startup_samd21.c @@ -263,7 +263,7 @@ void Reset_Handler(void) NVMCTRL->CTRLB.bit.MANW = 1; /* Initialize the C library */ - __libc_init_array(); + //__libc_init_array(); /* Branch to main function */ main(); diff --git a/atmel-samd/asf/sam0/utils/syscalls/gcc/syscalls.c b/atmel-samd/asf/sam0/utils/syscalls/gcc/syscalls.c index c8c7ae605..45859e862 100644 --- a/atmel-samd/asf/sam0/utils/syscalls/gcc/syscalls.c +++ b/atmel-samd/asf/sam0/utils/syscalls/gcc/syscalls.c @@ -109,10 +109,10 @@ extern int _lseek(int file, int ptr, int dir) return 0; } -extern void _exit(int status) -{ - asm("BKPT #0"); -} +// extern void _exit(int status) +// { +// asm("BKPT #0"); +// } extern void _kill(int pid, int sig) { -- cgit v1.2.3