kopia lustrzana https://github.com/bristol-seds/pico-tracker
Added I2C module. Currently configured for rocketry / hf dev
rodzic
7f58ddee7b
commit
03fc8a176d
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* A wrapper around the samd20 i2c functions. Single master only
|
||||
* Copyright (C) 2015 Richard Meadows <richardeoin>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef I2C_H
|
||||
#define I2C_H
|
||||
|
||||
#include "sercom/i2c_master.h"
|
||||
|
||||
/**
|
||||
* We just declare our single instance here because. Very naughty
|
||||
*/
|
||||
struct i2c_master_module i2c_master_instance;
|
||||
|
||||
/**
|
||||
* I2C Write.
|
||||
*
|
||||
* address is the full write address like 0xEE
|
||||
*/
|
||||
static void i2c_master_write(uint8_t address, uint8_t* data, uint16_t data_length)
|
||||
{
|
||||
uint32_t timeout = 0;
|
||||
struct i2c_master_packet packet = {
|
||||
.address = address >> 1,
|
||||
.data_length = data_length,
|
||||
.data = data,
|
||||
.ten_bit_address = false,
|
||||
.high_speed = false,
|
||||
.hs_master_code = 0x0,
|
||||
};
|
||||
|
||||
while (i2c_master_write_packet_wait(&i2c_master_instance, &packet) !=
|
||||
STATUS_OK) {
|
||||
/* Increment timeout counter and check if timed out. */
|
||||
if (timeout++ > 1000) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* I2C Read.
|
||||
*
|
||||
* address is the full write address like 0xEE
|
||||
*/
|
||||
static void i2c_master_read(uint8_t address, uint8_t* data, uint16_t data_length)
|
||||
{
|
||||
uint32_t timeout = 0;
|
||||
struct i2c_master_packet packet = {
|
||||
.address = address >> 1,
|
||||
.data_length = data_length,
|
||||
.data = data,
|
||||
.ten_bit_address = false,
|
||||
.high_speed = false,
|
||||
.hs_master_code = 0x0,
|
||||
};
|
||||
|
||||
while (i2c_master_read_packet_wait(&i2c_master_instance, &packet) !=
|
||||
STATUS_OK) {
|
||||
/* Increment timeout counter and check if timed out. */
|
||||
if (timeout++ > 1000) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* I2C bus master.
|
||||
*/
|
||||
static void i2c_init(void)
|
||||
{
|
||||
struct i2c_master_config config_i2c_master;
|
||||
i2c_master_get_config_defaults(&config_i2c_master);
|
||||
|
||||
/* Config */
|
||||
config_i2c_master.buffer_timeout = 10000;
|
||||
config_i2c_master.baud_rate = 10;
|
||||
|
||||
/* Pinmux */
|
||||
config_i2c_master.pinmux_pad0 = PINMUX_PA04D_SERCOM0_PAD0;
|
||||
config_i2c_master.pinmux_pad1 = PINMUX_PA05D_SERCOM0_PAD1;
|
||||
|
||||
/* Initialize and enable device with config. */
|
||||
i2c_master_init(&i2c_master_instance, SERCOM0, &config_i2c_master);
|
||||
i2c_master_enable(&i2c_master_instance);
|
||||
}
|
||||
|
||||
#endif /* I2C_H */
|
|
@ -0,0 +1,553 @@
|
|||
/**
|
||||
* \file
|
||||
*
|
||||
* \brief SAM SERCOM I2C Common Driver
|
||||
*
|
||||
* Copyright (C) 2012-2014 Atmel Corporation. All rights reserved.
|
||||
*
|
||||
* \asf_license_start
|
||||
*
|
||||
* \page License
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of Atmel may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 4. This software may only be redistributed and used in connection with an
|
||||
* Atmel microcontroller product.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
|
||||
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* \asf_license_stop
|
||||
*
|
||||
*/
|
||||
#ifndef I2C_COMMON_H_INCLUDED
|
||||
#define I2C_COMMON_H_INCLUDED
|
||||
|
||||
#include <sercom/sercom.h>
|
||||
|
||||
/**
|
||||
* \if (I2C_MASTER_MODE && I2C_SLAVE_MODE)
|
||||
* \defgroup asfdoc_sam0_sercom_i2c_group SAM D20/D21/R21 I2C Driver (SERCOM I2C)
|
||||
* \elseif I2C_MASTER_MODE
|
||||
* \defgroup asfdoc_sam0_sercom_i2c_group SAM D20/D21/R21 I2C Master Mode Driver (SERCOM I2C)
|
||||
* \elseif I2C_SLAVE_MODE
|
||||
* \defgroup asfdoc_sam0_sercom_i2c_group SAM D20/D21/R21 I2C Slave Mode Driver (SERCOM I2C)
|
||||
* \endif
|
||||
*
|
||||
* This driver for SAM D20/D21/R21 devices provides an interface for the configuration
|
||||
* and management of the device's SERCOM I<SUP>2</SUP>C module, for the transfer
|
||||
* of data via an I<SUP>2</SUP>C 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 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
|
||||
* <table>
|
||||
* <tr>
|
||||
* <th>Driver Feature Macro</th>
|
||||
* <th>Supported devices</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED</td>
|
||||
* <td>SAM D21/R21</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>FEATURE_I2C_10_BIT_ADDRESS</td>
|
||||
* <td>SAM D21/R21</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>FEATURE_I2C_SCL_STRETCH_MODE</td>
|
||||
* <td>SAM D21/R21</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>FEATURE_I2C_SCL_EXTEND_TIMEOUT</td>
|
||||
* <td>SAM D21/R21</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* \note The specific features are only available in the driver when the
|
||||
* selected device supports those features.
|
||||
*
|
||||
* \subsection asfdoc_sam0_sercom_i2c_functional_desc Functional Description
|
||||
* The I<SUP>2</SUP>C 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 I<SUP>2</SUP>C 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 I<SUP>2</SUP>C 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
|
||||
* I<SUP>2</SUP>C 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 I<SUP>2</SUP>C 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 I<SUP>2</SUP>C 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 I<SUP>2</SUP>C 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 I<SUP>2</SUP>C 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 I<SUP>2</SUP>C 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
|
||||
* <table>
|
||||
* <caption>I2C standby operations</caption>
|
||||
* <tr>
|
||||
* <th>Run in standby</th>
|
||||
* <th>Slave</th>
|
||||
* <th>Master</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>false</td>
|
||||
* <td>Disabled, all reception is dropped</td>
|
||||
* <td>GCLK disabled when master is idle</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>true</td>
|
||||
* <td>Wake on address match when enabled</td>
|
||||
* <td>GCLK enabled while in sleep modes</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
*
|
||||
* \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 SERCOME I2C driver features set according to different device family.
|
||||
*
|
||||
* \note The high speed mode and 10-bit address feature are not
|
||||
* supported by the driver now.
|
||||
* @{
|
||||
*/
|
||||
#if (SAMD21) || (SAMR21) || 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 {
|
||||
I2C_TRANSFER_WRITE = 0,
|
||||
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
|
||||
* <table>
|
||||
* <caption>Acronyms</caption>
|
||||
* <tr>
|
||||
* <th>Acronym</th>
|
||||
* <th>Description</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>SDA</td>
|
||||
* <td>Serial Data Line</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>SCL</td>
|
||||
* <td>Serial Clock Line</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>SERCOM</td>
|
||||
* <td>Serial Communication Interface</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>DMA</td>
|
||||
* <td>Direct Memory Access</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* \section asfdoc_sam0_sercom_i2c_extra_dependencies Dependencies
|
||||
* The I<SUP>2</SUP>C 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
|
||||
* <table>
|
||||
* <caption>Module History</caption>
|
||||
* <tr>
|
||||
* <th>Changelog</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>
|
||||
* \li Added 10-bit addressing and high speed support in SAM D21.
|
||||
* \li Seperate structure i2c_packet into i2c_master_packet and i2c_slave packet.
|
||||
* </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>
|
||||
* \li Added support for SCL stretch and extended timeout hardware features in SAM D21.
|
||||
* \li Added fast mode plus support in SAM D21.
|
||||
* </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>Fixed incorrect logical mask for determining if a bus error has
|
||||
* occurred in I2C Slave mode.
|
||||
* </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>Initial Release</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*/
|
||||
|
||||
/**
|
||||
* \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 QSGs 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
|
||||
*
|
||||
* <table>
|
||||
* <tr>
|
||||
* <th>Doc. Rev.</td>
|
||||
* <th>Date</td>
|
||||
* <th>Comments</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>E</td>
|
||||
* <td>03/2014</td>
|
||||
* <td>Added SAM R21 support.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>D</td>
|
||||
* <td>03/2014</td>
|
||||
* <td>Added 10-bit addressing and high speed support in SAM D21.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>C</td>
|
||||
* <td>01/2014</td>
|
||||
* <td>Added the SAM D21 to the application note.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>B</td>
|
||||
* <td>06/2013</td>
|
||||
* <td>Corrected documentation typos. Updated I2C Bus State Diagram.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>A</td>
|
||||
* <td>06/2013</td>
|
||||
* <td>Initial release</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#endif /* I2C_COMMON_H_INCLUDED */
|
|
@ -0,0 +1,591 @@
|
|||
/**
|
||||
* \file
|
||||
*
|
||||
* \brief SAM SERCOM I2C Master Driver
|
||||
*
|
||||
* Copyright (C) 2012-2014 Atmel Corporation. All rights reserved.
|
||||
*
|
||||
* \asf_license_start
|
||||
*
|
||||
* \page License
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of Atmel may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 4. This software may only be redistributed and used in connection with an
|
||||
* Atmel microcontroller product.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
|
||||
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* \asf_license_stop
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef I2C_MASTER_H_INCLUDED
|
||||
#define I2C_MASTER_H_INCLUDED
|
||||
|
||||
#include "sercom/i2c_common.h"
|
||||
#include <sercom/sercom.h>
|
||||
#include <system/pinmux.h>
|
||||
|
||||
#if I2C_MASTER_CALLBACK_MODE == true
|
||||
# include <sercom_interrupt.h>
|
||||
#endif
|
||||
|
||||
#ifndef PINMUX_DEFAULT
|
||||
# define PINMUX_DEFAULT 0
|
||||
#endif
|
||||
|
||||
#include <system/events.h>
|
||||
#define Assert assert
|
||||
/**
|
||||
* \addtogroup asfdoc_sam0_sercom_i2c_group
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief I<SUP>2</SUP>C master packet for read/write
|
||||
*
|
||||
* Structure to be used when transferring I<SUP>2</SUP>C 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 I<SUP>2</SUP>C 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 (50-60us) */
|
||||
I2C_MASTER_INACTIVE_TIMEOUT_55US = SERCOM_I2CM_CTRLA_INACTOUT(1),
|
||||
/** Inactive bus time-out 10-11 SCL cycle time-out (100-110us) */
|
||||
I2C_MASTER_INACTIVE_TIMEOUT_105US = SERCOM_I2CM_CTRLA_INACTOUT(2),
|
||||
/** Inactive bus time-out 20-21 SCL cycle time-out (200-210us) */
|
||||
I2C_MASTER_INACTIVE_TIMEOUT_205US = SERCOM_I2CM_CTRLA_INACTOUT(3),
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief I<SUP>2</SUP>C frequencies
|
||||
*
|
||||
* Values for I<SUP>2</SUP>C 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 100 kHz and Fast-mode (Fm) up to 400 kHz */
|
||||
I2C_MASTER_SPEED_STANDARD_AND_FAST = SERCOM_I2CM_CTRLA_SPEED(0),
|
||||
/** Fast-mode Plus (Fm+) up to 1 MHz */
|
||||
I2C_MASTER_SPEED_FAST_MODE_PLUS = SERCOM_I2CM_CTRLA_SPEED(1),
|
||||
/** High-speed mode (Hs-mode) up to 3.4 MHz */
|
||||
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 I<SUP>2</SUP>C 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 I<SUP>2</SUP>C Master driver software device instance structure.
|
||||
*
|
||||
* SERCOM I<SUP>2</SUP>C 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 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 I<SUP>2</SUP>C Master device
|
||||
*
|
||||
* This is the configuration structure for the I<SUP>2</SUP>C 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 I<SUP>2</SUP>C 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 I<SUP>2</SUP>C 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
|
||||
};
|
||||
|
||||
/**
|
||||
* \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 I<SUP>2</SUP>C 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 availale 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
|
||||
* - maser 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
|
||||
}
|
||||
|
||||
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 I<SUP>2</SUP>C module
|
||||
*
|
||||
* Enables the requested I<SUP>2</SUP>C 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 I<SUP>2</SUP>C module
|
||||
*
|
||||
* Disables the requested I<SUP>2</SUP>C 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);
|
||||
|
||||
/* Wait for module to sync. */
|
||||
_i2c_master_wait_for_sync(module);
|
||||
|
||||
/* Disable module. */
|
||||
i2c_module->CTRLA.reg &= ~SERCOM_I2CM_CTRLA_ENABLE;
|
||||
|
||||
#if I2C_MASTER_CALLBACK_MODE == true
|
||||
/* Disable module interrupts */
|
||||
system_interrupt_disable(_sercom_get_interrupt_vector(module->hw));
|
||||
#endif
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/** @} */
|
||||
|
||||
#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
|
||||
|
||||
#endif /* I2C_MASTER_H_INCLUDED */
|
|
@ -178,7 +178,11 @@ enum status_code {
|
|||
STATUS_BUSY,
|
||||
STATUS_ERR_INVALID_ARG,
|
||||
STATUS_ERR_OVERFLOW,
|
||||
STATUS_ERR_DENIED
|
||||
STATUS_ERR_DENIED,
|
||||
STATUS_ERR_BAUDRATE_UNAVAILABLE,
|
||||
STATUS_ERR_PACKET_COLLISION,
|
||||
STATUS_ERR_BAD_ADDRESS,
|
||||
STATUS_ERR_TIMEOUT
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,739 @@
|
|||
/**
|
||||
* \file
|
||||
*
|
||||
* \brief SAM D20 I2C Master Driver
|
||||
*
|
||||
* Copyright (C) 2012-2014 Atmel Corporation. All rights reserved.
|
||||
*
|
||||
* \asf_license_start
|
||||
*
|
||||
* \page License
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of Atmel may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 4. This software may only be redistributed and used in connection with an
|
||||
* Atmel microcontroller product.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
|
||||
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* \asf_license_stop
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sercom/i2c_master.h"
|
||||
#include "system/pinmux.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 */
|
||||
|
||||
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);
|
||||
}
|
||||
system_pinmux_pin_set_config(pad0 >> 16,
|
||||
pad0 & 0xFFFF,
|
||||
SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK,
|
||||
SYSTEM_PINMUX_PIN_PULL_UP,
|
||||
false);
|
||||
|
||||
/* SERCOM PAD1 - SCL */
|
||||
if (pad1 == PINMUX_DEFAULT) {
|
||||
pad1 = _sercom_get_default_pad(sercom_hw, 1);
|
||||
}
|
||||
system_pinmux_pin_set_config(pad1 >> 16,
|
||||
pad1 & 0xFFFF,
|
||||
SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK,
|
||||
SYSTEM_PINMUX_PIN_PULL_UP,
|
||||
false);
|
||||
|
||||
|
||||
/* 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)(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 I<SUP>2</SUP>C hardware module
|
||||
*
|
||||
* Initializes the SERCOM I<SUP>2</SUP>C 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 */
|
||||
|
||||
|
||||
|
||||
system_gclk_chan_set_config(gclk_index, config->generator_source);
|
||||
system_gclk_chan_enable(gclk_index);
|
||||
_sercom_set_gclk_generator(config->generator_source);
|
||||
|
||||
/* 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 I<SUP>2</SUP>C 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 (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);
|
||||
}
|
||||
|
||||
/* 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 I<SUP>2</SUP>C
|
||||
* 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 I<SUP>2</SUP>C 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;
|
||||
|
||||
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 I<SUP>2</SUP>C
|
||||
* 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 I<SUP>2</SUP>C 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;
|
||||
|
||||
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 I<SUP>2</SUP>C 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_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 I<SUP>2</SUP>C 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 I<SUP>2</SUP>C packet to transfer
|
||||
*
|
||||
* \return Status of reading packet.
|
||||
* \retval STATUS_OK If packet was read
|
||||
* \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;
|
||||
|
||||
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 I<SUP>2</SUP>C 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 I<SUP>2</SUP>C packet to transfer
|
||||
*
|
||||
* \return Status of reading packet.
|
||||
* \retval STATUS_OK If packet was read
|
||||
* \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;
|
||||
|
||||
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] 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);
|
||||
}
|
Ładowanie…
Reference in New Issue