init
This commit is contained in:
154
lib_i2c/lib_i2c/.cproject
Normal file
154
lib_i2c/lib_i2c/.cproject
Normal file
@@ -0,0 +1,154 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
|
||||
<storageModule moduleId="org.eclipse.cdt.core.settings">
|
||||
<cconfiguration id="com.xmos.cdt.toolchain.854505739">
|
||||
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="com.xmos.cdt.toolchain.854505739" moduleId="org.eclipse.cdt.core.settings" name="Default">
|
||||
<externalSettings/>
|
||||
<extensions>
|
||||
<extension id="com.xmos.cdt.core.XEBinaryParser" point="org.eclipse.cdt.core.BinaryParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GNU_ELF" point="org.eclipse.cdt.core.BinaryParser"/>
|
||||
<extension id="com.xmos.cdt.core.XdeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
</extensions>
|
||||
</storageModule>
|
||||
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||
<configuration buildProperties="" description="" id="com.xmos.cdt.toolchain.854505739" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
|
||||
<folderInfo id="com.xmos.cdt.toolchain.854505739.1680930365" name="/" resourcePath="">
|
||||
<toolChain id="com.xmos.cdt.toolchain.1700184562" name="com.xmos.cdt.toolchain" superClass="com.xmos.cdt.toolchain">
|
||||
<targetPlatform archList="all" binaryParser="com.xmos.cdt.core.XEBinaryParser;org.eclipse.cdt.core.GNU_ELF" id="com.xmos.cdt.core.platform.482398777" isAbstract="false" osList="linux,win32,macosx" superClass="com.xmos.cdt.core.platform"/>
|
||||
<builder arguments="-f .makefile" id="com.xmos.cdt.builder.base.621132499" keepEnvironmentInBuildfile="false" managedBuildOn="false" superClass="com.xmos.cdt.builder.base">
|
||||
<outputEntries>
|
||||
<entry flags="VALUE_WORKSPACE_PATH" kind="outputPath" name="bin"/>
|
||||
</outputEntries>
|
||||
</builder>
|
||||
<tool id="com.xmos.cdt.xc.compiler.277328952" name="com.xmos.cdt.xc.compiler" superClass="com.xmos.cdt.xc.compiler">
|
||||
<option id="com.xmos.xc.compiler.option.include.paths.713968690" superClass="com.xmos.xc.compiler.option.include.paths" valueType="includePath">
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lib_logging/src}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lib_logging}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lib_logging/api}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lib_xassert/src}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lib_xassert}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lib_xassert/api}""/>
|
||||
</option>
|
||||
<inputType id="com.xmos.cdt.xc.compiler.input.1007607740" name="XC" superClass="com.xmos.cdt.xc.compiler.input"/>
|
||||
</tool>
|
||||
<tool id="com.xmos.cdt.c.compiler.1384555284" name="com.xmos.cdt.c.compiler" superClass="com.xmos.cdt.c.compiler">
|
||||
<option id="com.xmos.c.compiler.option.include.paths.168966593" superClass="com.xmos.c.compiler.option.include.paths" valueType="includePath">
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lib_logging/src}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lib_logging}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lib_logging/api}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lib_xassert/src}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lib_xassert}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lib_xassert/api}""/>
|
||||
</option>
|
||||
<inputType id="com.xmos.cdt.c.compiler.input.c.25685430" name="C" superClass="com.xmos.cdt.c.compiler.input.c"/>
|
||||
</tool>
|
||||
<tool id="com.xmos.cdt.cxx.compiler.163182848" name="com.xmos.cdt.cxx.compiler" superClass="com.xmos.cdt.cxx.compiler">
|
||||
<option id="com.xmos.cxx.compiler.option.include.paths.1925843647" superClass="com.xmos.cxx.compiler.option.include.paths" valueType="includePath">
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lib_logging/src}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lib_logging}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lib_logging/api}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lib_xassert/src}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lib_xassert}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lib_xassert/api}""/>
|
||||
</option>
|
||||
<inputType id="com.xmos.cdt.cxx.compiler.input.cpp.377092751" name="C++" superClass="com.xmos.cdt.cxx.compiler.input.cpp"/>
|
||||
</tool>
|
||||
</toolChain>
|
||||
</folderInfo>
|
||||
<sourceEntries>
|
||||
<entry excluding=".build*" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
|
||||
</sourceEntries>
|
||||
</configuration>
|
||||
</storageModule>
|
||||
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
|
||||
</cconfiguration>
|
||||
</storageModule>
|
||||
<storageModule moduleId="scannerConfiguration">
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
|
||||
<buildOutputProvider>
|
||||
<openAction enabled="true" filePath=""/>
|
||||
<parser enabled="true"/>
|
||||
</buildOutputProvider>
|
||||
<scannerInfoProvider id="specsFile">
|
||||
<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
|
||||
<parser enabled="true"/>
|
||||
</scannerInfoProvider>
|
||||
</profile>
|
||||
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
|
||||
<buildOutputProvider>
|
||||
<openAction enabled="true" filePath=""/>
|
||||
<parser enabled="true"/>
|
||||
</buildOutputProvider>
|
||||
<scannerInfoProvider id="makefileGenerator">
|
||||
<runAction arguments="-E -P -v -dD" command="" useDefault="true"/>
|
||||
<parser enabled="true"/>
|
||||
</scannerInfoProvider>
|
||||
</profile>
|
||||
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
|
||||
<buildOutputProvider>
|
||||
<openAction enabled="true" filePath=""/>
|
||||
<parser enabled="true"/>
|
||||
</buildOutputProvider>
|
||||
<scannerInfoProvider id="specsFile">
|
||||
<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
|
||||
<parser enabled="true"/>
|
||||
</scannerInfoProvider>
|
||||
</profile>
|
||||
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
|
||||
<buildOutputProvider>
|
||||
<openAction enabled="true" filePath=""/>
|
||||
<parser enabled="true"/>
|
||||
</buildOutputProvider>
|
||||
<scannerInfoProvider id="specsFile">
|
||||
<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
|
||||
<parser enabled="true"/>
|
||||
</scannerInfoProvider>
|
||||
</profile>
|
||||
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
|
||||
<buildOutputProvider>
|
||||
<openAction enabled="true" filePath=""/>
|
||||
<parser enabled="true"/>
|
||||
</buildOutputProvider>
|
||||
<scannerInfoProvider id="specsFile">
|
||||
<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
|
||||
<parser enabled="true"/>
|
||||
</scannerInfoProvider>
|
||||
</profile>
|
||||
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
|
||||
<buildOutputProvider>
|
||||
<openAction enabled="true" filePath=""/>
|
||||
<parser enabled="true"/>
|
||||
</buildOutputProvider>
|
||||
<scannerInfoProvider id="specsFile">
|
||||
<runAction arguments="-c 'gcc -E -P -v -dD "${plugin_state_location}/${specs_file}"'" command="sh" useDefault="true"/>
|
||||
<parser enabled="true"/>
|
||||
</scannerInfoProvider>
|
||||
</profile>
|
||||
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
|
||||
<buildOutputProvider>
|
||||
<openAction enabled="true" filePath=""/>
|
||||
<parser enabled="true"/>
|
||||
</buildOutputProvider>
|
||||
<scannerInfoProvider id="specsFile">
|
||||
<runAction arguments="-c 'g++ -E -P -v -dD "${plugin_state_location}/specs.cpp"'" command="sh" useDefault="true"/>
|
||||
<parser enabled="true"/>
|
||||
</scannerInfoProvider>
|
||||
</profile>
|
||||
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
|
||||
<buildOutputProvider>
|
||||
<openAction enabled="true" filePath=""/>
|
||||
<parser enabled="true"/>
|
||||
</buildOutputProvider>
|
||||
<scannerInfoProvider id="specsFile">
|
||||
<runAction arguments="-c 'gcc -E -P -v -dD "${plugin_state_location}/specs.c"'" command="sh" useDefault="true"/>
|
||||
<parser enabled="true"/>
|
||||
</scannerInfoProvider>
|
||||
</profile>
|
||||
</storageModule>
|
||||
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||
<project id="lib_i2c.null.793113907" name="lib_i2c"/>
|
||||
</storageModule>
|
||||
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
|
||||
</cproject>
|
||||
8
lib_i2c/lib_i2c/.makefile
Normal file
8
lib_i2c/lib_i2c/.makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
all:
|
||||
@echo "** Module only - only builds as part of application **"
|
||||
|
||||
|
||||
clean:
|
||||
@echo "** Module only - only builds as part of application **"
|
||||
|
||||
|
||||
105
lib_i2c/lib_i2c/.project
Normal file
105
lib_i2c/lib_i2c/.project
Normal file
@@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>lib_i2c</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>com.xmos.cdt.core.LegacyProjectCheckerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.xmos.cdt.core.ProjectCheckerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.xmos.cdt.core.BuildMarkersBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.xmos.cdt.core.ProjectInfoSyncBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.xmos.cdt.core.IncludePathBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.xmos.cdt.core.ModulePathBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
|
||||
<triggers>clean,full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>?children?</key>
|
||||
<value>?name?=outputEntries\|?children?=?name?=entry\\\\|\\|\||</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>?name?</key>
|
||||
<value></value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.append_environment</key>
|
||||
<value>true</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.buildArguments</key>
|
||||
<value>CONFIG=Debug</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.buildCommand</key>
|
||||
<value>xmake</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.cleanBuildTarget</key>
|
||||
<value>clean</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.contents</key>
|
||||
<value>org.eclipse.cdt.make.core.activeConfigSettings</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.enableAutoBuild</key>
|
||||
<value>false</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.enableCleanBuild</key>
|
||||
<value>true</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.enableFullBuild</key>
|
||||
<value>true</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.stopOnError</key>
|
||||
<value>true</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>
|
||||
<value>false</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.cdt.core.cnature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
|
||||
<nature>com.xmos.cdt.core.XdeProjectNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
4
lib_i2c/lib_i2c/.xproject
Normal file
4
lib_i2c/lib_i2c/.xproject
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xproject><repository>lib_i2c</repository>
|
||||
<version>5.0.0dc6fb354094</version>
|
||||
</xproject>
|
||||
676
lib_i2c/lib_i2c/api/i2c.h
Normal file
676
lib_i2c/lib_i2c/api/i2c.h
Normal file
@@ -0,0 +1,676 @@
|
||||
// Copyright 2014-2021 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#ifndef _i2c_h_
|
||||
#define _i2c_h_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/** This type is used in I2C functions to report back on whether the
|
||||
* slave performed an ACK or NACK on the last piece of data sent
|
||||
* to it.
|
||||
*/
|
||||
typedef enum {
|
||||
I2C_NACK, ///< the slave has NACKed the last byte
|
||||
I2C_ACK, ///< the slave has ACKed the last byte
|
||||
} i2c_res_t;
|
||||
|
||||
#ifdef __XC__
|
||||
|
||||
#define BIT_TIME(KBITS_PER_SEC) ((XS1_TIMER_MHZ * 1000) / KBITS_PER_SEC)
|
||||
#define BIT_MASK(BIT_POS) (1 << BIT_POS)
|
||||
|
||||
/** This interface is used to communication with an I2C master component.
|
||||
* It provides facilities for reading and writing to the bus.
|
||||
*
|
||||
*/
|
||||
typedef interface i2c_master_if {
|
||||
|
||||
/** Write data to an I2C bus.
|
||||
*
|
||||
* \param device_addr the address of the slave device to write to.
|
||||
* \param buf the buffer containing data to write.
|
||||
* \param n the number of bytes to write.
|
||||
* \param num_bytes_sent the function will set this value to the
|
||||
* number of bytes actually sent. On success, this
|
||||
* will be equal to ``n`` but it will be less if the
|
||||
* slave sends an early NACK on the bus and the
|
||||
* transaction fails.
|
||||
* \param send_stop_bit if this is non-zero then a stop bit
|
||||
* will be sent on the bus after the transaction.
|
||||
* This is usually required for normal operation. If
|
||||
* this parameter is zero then no stop bit will
|
||||
* be omitted. In this case, no other task can use
|
||||
* the component until a stop bit has been sent.
|
||||
*
|
||||
* \returns ``I2C_ACK`` if the write was acknowledged by the slave
|
||||
* device, otherwise ``I2C_NACK``.
|
||||
*/
|
||||
[[guarded]]
|
||||
i2c_res_t write(uint8_t device_addr, uint8_t buf[n], size_t n,
|
||||
size_t &num_bytes_sent, int send_stop_bit);
|
||||
|
||||
/** Read data from an I2C bus.
|
||||
*
|
||||
* \param device_addr the address of the slave device to read from
|
||||
* \param buf the buffer to fill with data
|
||||
* \param n the number of bytes to read
|
||||
* \param send_stop_bit if this is non-zero then a stop bit
|
||||
* will be sent on the bus after the transaction.
|
||||
* This is usually required for normal operation. If
|
||||
* this parameter is zero then no stop bit will
|
||||
* be omitted. In this case, no other task can use
|
||||
* the component until a stop bit has been sent.
|
||||
*
|
||||
* \returns ``I2C_ACK`` if the read was acknowledged by the slave
|
||||
* device, otherwise ``I2C_NACK``.
|
||||
*/
|
||||
[[guarded]]
|
||||
i2c_res_t read(uint8_t device_addr, uint8_t buf[n], size_t n,
|
||||
int send_stop_bit);
|
||||
|
||||
/** Send a stop bit.
|
||||
*
|
||||
* This function will cause a stop bit to be sent on the bus. It should
|
||||
* be used to complete/abort a transaction if the ``send_stop_bit`` argument
|
||||
* was not set when calling the read() or write() functions.
|
||||
*/
|
||||
void send_stop_bit(void);
|
||||
|
||||
/** Shutdown the I2C component.
|
||||
*
|
||||
* This function will cause the I2C task to shutdown and return.
|
||||
*/
|
||||
void shutdown();
|
||||
} i2c_master_if;
|
||||
|
||||
/** This type is used by the supplementary I2C register read/write functions to
|
||||
* report back on whether the operation was a success or not.
|
||||
*/
|
||||
typedef enum {
|
||||
I2C_REGOP_SUCCESS, ///< the operation was successful
|
||||
I2C_REGOP_DEVICE_NACK, ///< the operation was NACKed when sending the device address, so either the device is missing or busy
|
||||
I2C_REGOP_INCOMPLETE ///< the operation was NACKed halfway through by the slave
|
||||
} i2c_regop_res_t;
|
||||
|
||||
|
||||
extends client interface i2c_master_if : {
|
||||
|
||||
/** Read an 8-bit register on a slave device.
|
||||
*
|
||||
* This function reads an 8-bit addressed, 8-bit register from the i2c
|
||||
* bus. The function reads data by
|
||||
* transmitting the register addr and then reading the data from the slave
|
||||
* device.
|
||||
*
|
||||
* Note that no stop bit is transmitted between the write and the read.
|
||||
* The operation is performed as one transaction using a repeated start.
|
||||
*
|
||||
* \param i the interface to the I2C master
|
||||
* \param device_addr the address of the slave device to read from
|
||||
* \param reg the address of the register to read
|
||||
* \param result indicates whether the read completed successfully. Will
|
||||
* be set to ``I2C_REGOP_DEVICE_NACK`` if the slave NACKed,
|
||||
* and ``I2C_REGOP_SUCCESS`` on successful completion of the
|
||||
* read.
|
||||
*
|
||||
* \returns the value of the register
|
||||
*/
|
||||
inline uint8_t read_reg(client interface i2c_master_if i,
|
||||
uint8_t device_addr, uint8_t reg,
|
||||
i2c_regop_res_t &result) {
|
||||
uint8_t a_reg[1] = {reg};
|
||||
uint8_t data[1] = {0};
|
||||
size_t n;
|
||||
i2c_res_t res;
|
||||
res = i.write(device_addr, a_reg, 1, n, 0);
|
||||
if (n != 1) {
|
||||
result = I2C_REGOP_DEVICE_NACK;
|
||||
i.send_stop_bit();
|
||||
return 0;
|
||||
}
|
||||
res = i.read(device_addr, data, 1, 1);
|
||||
if (res == I2C_ACK) {
|
||||
result = I2C_REGOP_SUCCESS;
|
||||
} else {
|
||||
result = I2C_REGOP_DEVICE_NACK;
|
||||
}
|
||||
return data[0];
|
||||
}
|
||||
|
||||
/** Write an 8-bit register on a slave device.
|
||||
*
|
||||
* This function writes an 8-bit addressed, 8-bit register from the i2c
|
||||
* bus. The function writes data by
|
||||
* transmitting the register addr and then
|
||||
* transmitting the data to the slave device.
|
||||
*
|
||||
* \param i the interface to the I2C master
|
||||
* \param device_addr the address of the slave device to write to
|
||||
* \param reg the address of the register to write
|
||||
* \param data the 8-bit value to write
|
||||
*/
|
||||
inline i2c_regop_res_t write_reg(client interface i2c_master_if i,
|
||||
uint8_t device_addr, uint8_t reg, uint8_t data)
|
||||
{
|
||||
uint8_t a_data[2] = {reg, data};
|
||||
size_t n;
|
||||
i.write(device_addr, a_data, 2, n, 1);
|
||||
if (n == 0) {
|
||||
return I2C_REGOP_DEVICE_NACK;
|
||||
}
|
||||
if (n < 2) {
|
||||
return I2C_REGOP_INCOMPLETE;
|
||||
}
|
||||
return I2C_REGOP_SUCCESS;
|
||||
}
|
||||
|
||||
/** Read an 8-bit register on a slave device from a 16-bit register address.
|
||||
*
|
||||
* This function reads a 16-bit addressed, 8-bit register from the i2c
|
||||
* bus. The function reads data by
|
||||
* transmitting the register addr and then reading the data from the slave
|
||||
* device.
|
||||
*
|
||||
* Note that no stop bit is transmitted between the write and the read.
|
||||
* The operation is performed as one transaction using a repeated start.
|
||||
*
|
||||
* \param i the interface to the I2C master
|
||||
* \param device_addr the address of the slave device to read from
|
||||
* \param reg the 16-bit address of the register to read
|
||||
* (most significant byte first)
|
||||
* \param result indicates whether the read completed successfully. Will
|
||||
* be set to ``I2C_REGOP_DEVICE_NACK`` if the slave NACKed,
|
||||
* and ``I2C_REGOP_SUCCESS`` on successful completion of the
|
||||
* read.
|
||||
*
|
||||
* \returns the value of the register
|
||||
*/
|
||||
inline uint8_t read_reg8_addr16(client interface i2c_master_if i,
|
||||
uint8_t device_addr, uint16_t reg,
|
||||
i2c_regop_res_t &result)
|
||||
{
|
||||
uint8_t a_reg[2] = {reg >> 8, reg};
|
||||
uint8_t data[1];
|
||||
size_t n;
|
||||
i2c_res_t res;
|
||||
i.write(device_addr, a_reg, 2, n, 0);
|
||||
if (n != 2) {
|
||||
result = I2C_REGOP_DEVICE_NACK;
|
||||
i.send_stop_bit();
|
||||
return 0;
|
||||
}
|
||||
res = i.read(device_addr, data, 1, 1);
|
||||
if (res == I2C_NACK) {
|
||||
result = I2C_REGOP_DEVICE_NACK;
|
||||
} else {
|
||||
result = I2C_REGOP_SUCCESS;
|
||||
}
|
||||
return data[0];
|
||||
}
|
||||
|
||||
/** Write an 8-bit register on a slave device from a 16-bit register address.
|
||||
*
|
||||
* This function writes a 16-bit addressed, 8-bit register from the i2c
|
||||
* bus. The function writes data by
|
||||
* transmitting the register addr and then
|
||||
* transmitting the data to the slave device.
|
||||
*
|
||||
* \param i the interface to the I2C master
|
||||
* \param device_addr the address of the slave device to write to
|
||||
* \param reg the 16-bit address of the register to write
|
||||
* (most significant byte first)
|
||||
* \param data the 8-bit value to write
|
||||
*/
|
||||
inline i2c_regop_res_t write_reg8_addr16(client interface i2c_master_if i,
|
||||
uint8_t device_addr, uint16_t reg,
|
||||
uint8_t data) {
|
||||
uint8_t a_data[3] = {reg >> 8, reg, data};
|
||||
size_t n;
|
||||
i.write(device_addr, a_data, 3, n, 1);
|
||||
if (n == 0) {
|
||||
return I2C_REGOP_DEVICE_NACK;
|
||||
}
|
||||
if (n < 3) {
|
||||
return I2C_REGOP_INCOMPLETE;
|
||||
}
|
||||
return I2C_REGOP_SUCCESS;
|
||||
}
|
||||
|
||||
/** Read an 16-bit register on a slave device from a 16-bit register address.
|
||||
*
|
||||
* This function reads a 16-bit addressed, 16-bit register from the i2c
|
||||
* bus. The function reads data by transmitting the register addr and then
|
||||
* reading the data from the slave device. It is assumed the data is returned
|
||||
* most significant byte first on the bus.
|
||||
*
|
||||
* Note that no stop bit is transmitted between the write and the read.
|
||||
* The operation is performed as one transaction using a repeated start.
|
||||
*
|
||||
* \param i the interface to the I2C master
|
||||
* \param device_addr the address of the slave device to read from
|
||||
* \param reg the address of the register to read (most
|
||||
* significant byte first)
|
||||
* \param result indicates whether the read completed successfully. Will
|
||||
* be set to ``I2C_REGOP_DEVICE_NACK`` if the slave NACKed,
|
||||
* and ``I2C_REGOP_SUCCESS`` on successful completion of the
|
||||
* read.
|
||||
*
|
||||
* \returns the 16-bit value of the register
|
||||
*/
|
||||
inline uint16_t read_reg16(client interface i2c_master_if i,
|
||||
uint8_t device_addr, uint16_t reg,
|
||||
i2c_regop_res_t &result)
|
||||
{
|
||||
uint8_t a_reg[2] = {reg >> 8, reg};
|
||||
uint8_t data[2];
|
||||
size_t n;
|
||||
i2c_res_t res;
|
||||
i.write(device_addr, a_reg, 2, n, 0);
|
||||
if (n != 2) {
|
||||
result = I2C_REGOP_DEVICE_NACK;
|
||||
i.send_stop_bit();
|
||||
return 0;
|
||||
}
|
||||
res = i.read(device_addr, data, 2, 1);
|
||||
if (res == I2C_NACK) {
|
||||
result = I2C_REGOP_DEVICE_NACK;
|
||||
} else {
|
||||
result = I2C_REGOP_SUCCESS;
|
||||
}
|
||||
return ((uint16_t) data[0] << 8) | data[1];
|
||||
}
|
||||
|
||||
/** Write an 16-bit register on a slave device from a 16-bit register address.
|
||||
*
|
||||
* This function writes a 16-bit addressed, 16-bit register from the i2c
|
||||
* bus. The function writes data by transmitting the register addr and then
|
||||
* transmitting the data to the slave device.
|
||||
*
|
||||
* \param i the interface to the I2C master
|
||||
* \param device_addr the address of the slave device to write to
|
||||
* \param reg the 16-bit address of the register to write
|
||||
* (most significant byte first)
|
||||
* \param data the 16-bit value to write (most significant
|
||||
* byte first)
|
||||
*
|
||||
* \returns ``I2C_REGOP_DEVICE_NACK`` if the address is NACKed,
|
||||
* ``I2C_REGOP_INCOMPLETE`` if not all data was ACKed and
|
||||
* ``I2C_REGOP_SUCCESS`` on successful completion of the
|
||||
* write with every byte being ACKed.
|
||||
*/
|
||||
inline i2c_regop_res_t write_reg16(client interface i2c_master_if i,
|
||||
uint8_t device_addr, uint16_t reg,
|
||||
uint16_t data) {
|
||||
uint8_t a_data[4] = {reg >> 8, reg, data >> 8, data};
|
||||
size_t n;
|
||||
i.write(device_addr, a_data, 4, n, 1);
|
||||
if (n == 0) {
|
||||
return I2C_REGOP_DEVICE_NACK;
|
||||
}
|
||||
if (n < 4) {
|
||||
return I2C_REGOP_INCOMPLETE;
|
||||
}
|
||||
return I2C_REGOP_SUCCESS;
|
||||
}
|
||||
|
||||
/** Read an 16-bit register on a slave device from a 8-bit register address.
|
||||
*
|
||||
* This function reads a 8-bit addressed, 16-bit register from the i2c
|
||||
* bus. The function reads data by transmitting the register addr and
|
||||
* then reading the data from the slave device. It is assumed that the data
|
||||
* is return most significant byte first on the bus.
|
||||
*
|
||||
* Note that no stop bit is transmitted between the write and the read.
|
||||
* The operation is performed as one transaction using a repeated start.
|
||||
*
|
||||
* \param i the interface to the I2C master
|
||||
* \param device_addr the address of the slave device to read from
|
||||
* \param reg the address of the register to read
|
||||
* \param result indicates whether the read completed successfully. Will
|
||||
* be set to ``I2C_REGOP_DEVICE_NACK`` if the slave NACKed,
|
||||
* and ``I2C_REGOP_SUCCESS`` on successful completion of the
|
||||
* read.
|
||||
*
|
||||
* \returns the 16-bit value of the register
|
||||
*/
|
||||
inline uint16_t read_reg16_addr8(client interface i2c_master_if i,
|
||||
uint8_t device_addr, uint8_t reg,
|
||||
i2c_regop_res_t &result)
|
||||
{
|
||||
uint8_t a_reg[1] = {reg};
|
||||
uint8_t data[2];
|
||||
size_t n;
|
||||
i2c_res_t res;
|
||||
i.write(device_addr, a_reg, 1, n, 0);
|
||||
if (n != 1) {
|
||||
result = I2C_REGOP_DEVICE_NACK;
|
||||
i.send_stop_bit();
|
||||
return 0;
|
||||
}
|
||||
res = i.read(device_addr, data, 2, 1);
|
||||
if (res == I2C_NACK) {
|
||||
result = I2C_REGOP_DEVICE_NACK;
|
||||
} else {
|
||||
result = I2C_REGOP_SUCCESS;
|
||||
}
|
||||
return ((uint16_t) data[0] << 8) | data[1];
|
||||
}
|
||||
|
||||
/** Write an 16-bit register on a slave device from a 8-bit register address.
|
||||
*
|
||||
* This function writes a 8-bit addressed, 16-bit register from the i2c
|
||||
* bus. The function writes data by transmitting the register addr and then
|
||||
* transmitting the data to the slave device.
|
||||
*
|
||||
* \param i the interface to the I2C master
|
||||
* \param device_addr the address of the slave device to write to
|
||||
* \param reg the address of the register to write
|
||||
* \param data the 16-bit value to write (most significant byte first)
|
||||
*
|
||||
* \returns ``I2C_REGOP_DEVICE_NACK`` if the address is NACKed,
|
||||
* ``I2C_REGOP_INCOMPLETE`` if not all data was ACKed and
|
||||
* ``I2C_REGOP_SUCCESS`` on successful completion of the
|
||||
* write with every byte being ACKed.
|
||||
*/
|
||||
inline i2c_regop_res_t write_reg16_addr8(client interface i2c_master_if i,
|
||||
uint8_t device_addr, uint8_t reg,
|
||||
uint16_t data) {
|
||||
uint8_t a_data[3] = {reg, data >> 8, data};
|
||||
size_t n;
|
||||
i.write(device_addr, a_data, 3, n, 1);
|
||||
if (n == 0) {
|
||||
return I2C_REGOP_DEVICE_NACK;
|
||||
}
|
||||
if (n < 3) {
|
||||
return I2C_REGOP_INCOMPLETE;
|
||||
}
|
||||
return I2C_REGOP_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Implements I2C on the i2c_master_if interface using two ports.
|
||||
*
|
||||
* \param i an array of server interface connections for clients
|
||||
* to connect to
|
||||
* \param n the number of clients connected
|
||||
* \param p_scl the SCL port of the I2C bus
|
||||
* \param p_sda the SDA port of the I2C bus
|
||||
* \param kbits_per_second the speed of the I2C bus
|
||||
**/
|
||||
[[distributable]] void i2c_master(server interface i2c_master_if i[n],
|
||||
size_t n,
|
||||
port p_scl, port p_sda,
|
||||
static const unsigned kbits_per_second);
|
||||
|
||||
#if (defined(__XS2A__) || defined(__XS3A__) || defined(__DOXYGEN__))
|
||||
/** Implements I2C on a single multi-bit port.
|
||||
*
|
||||
* This function implements an I2C master bus using a single port. It is only
|
||||
* supported on xCORE-200 devices.
|
||||
*
|
||||
* \param c an array of server interface connections for clients
|
||||
* to connect to
|
||||
* \param n the number of clients connected
|
||||
* \param p_i2c the multi-bit port containing both SCL and SDA.
|
||||
* the bit positions of SDA and SCL are configured using the
|
||||
* ``sda_bit_position`` and ``scl_bit_position`` arguments.
|
||||
* \param kbits_per_second the speed of the I2C bus
|
||||
* \param sda_bit_position the bit of the SDA line on the port
|
||||
* \param scl_bit_position the bit of the SCL line on the port
|
||||
* \param other_bits_mask a value that is ORed into the port value driven
|
||||
* to ``p_i2c``. The SDA and SCL bit values for this
|
||||
* variable must be set to 0. Note that ``p_i2c`` is
|
||||
* configured with set_port_drive_low() and
|
||||
* therefore external pullup resistors are required
|
||||
* to produce a value 1 on a bit.
|
||||
*/
|
||||
[[distributable]]
|
||||
void i2c_master_single_port(server interface i2c_master_if c[n], static const size_t n,
|
||||
port p_i2c, static const unsigned kbits_per_second,
|
||||
static const unsigned scl_bit_position,
|
||||
static const unsigned sda_bit_position,
|
||||
static const unsigned other_bits_mask);
|
||||
#endif
|
||||
|
||||
|
||||
/** This interface is used to communicate with an I2C master component
|
||||
* asynchronously.
|
||||
* It provides facilities for reading and writing to the bus.
|
||||
*
|
||||
*/
|
||||
typedef interface i2c_master_async_if {
|
||||
|
||||
/** Initialize a write to an I2C bus.
|
||||
*
|
||||
* \param device_addr the address of the slave device to write to
|
||||
* \param buf the buffer containing data to write
|
||||
* \param n the number of bytes to write
|
||||
* \param send_stop_bit if this is non-zero then a stop bit
|
||||
* will be sent on the bus after the transaction.
|
||||
* This is usually required for normal operation. If
|
||||
* this parameter is zero then no stop bit will
|
||||
* be omitted. In this case, no other task can use
|
||||
* the component until a stop bit has been sent.
|
||||
*/
|
||||
[[guarded]]
|
||||
void write(uint8_t device_addr, uint8_t buf[n], size_t n,
|
||||
int send_stop_bit);
|
||||
|
||||
/** Initialize a read to an I2C bus.
|
||||
*
|
||||
* \param device_addr the address of the slave device to read from.
|
||||
* \param n the number of bytes to read.
|
||||
* \param send_stop_bit if this is non-zero then a stop bit
|
||||
* will be sent on the bus after the transaction.
|
||||
* This is usually required for normal operation. If
|
||||
* this parameter is zero then no stop bit will
|
||||
* be omitted. In this case, no other task can use
|
||||
* the component until a stop bit has been sent.
|
||||
*/
|
||||
[[guarded]]
|
||||
void read(uint8_t device_addr, size_t n, int send_stop_bit);
|
||||
|
||||
/** Completed operation notification.
|
||||
*
|
||||
* This notification will fire when a read or write is completed.
|
||||
*/
|
||||
[[notification]]
|
||||
slave void operation_complete(void);
|
||||
|
||||
/** Get write result.
|
||||
*
|
||||
* This function should be called after a write has completed.
|
||||
*
|
||||
* \param num_bytes_sent the function will set this value to the
|
||||
* number of bytes actually sent. On success, this
|
||||
* will be equal to ``n`` but it will be less if the
|
||||
* slave sends an early NACK on the bus and the
|
||||
* transaction fails.
|
||||
*
|
||||
* \returns ``I2C_ACK`` if the write was acknowledged by the slave
|
||||
* device, otherwise ``I2C_NACK``.
|
||||
*/
|
||||
[[clears_notification]]
|
||||
i2c_res_t get_write_result(size_t &num_bytes_sent);
|
||||
|
||||
|
||||
/** Get read result.
|
||||
*
|
||||
* This function should be called after a read has completed.
|
||||
*
|
||||
* \param buf the buffer to fill with data.
|
||||
* \param n the number of bytes to read, this should be the same
|
||||
* as the number of bytes specified in read(),
|
||||
* otherwise the behavior is undefined.
|
||||
*
|
||||
* \returns ``I2C_ACK`` if the write was acknowledged by the slave
|
||||
* device, otherwise ``I2C_NACK``.
|
||||
*/
|
||||
[[clears_notification]]
|
||||
i2c_res_t get_read_data(uint8_t buf[n], size_t n);
|
||||
|
||||
/** Send a stop bit.
|
||||
*
|
||||
* This function will cause a stop bit to be sent on the bus. It should
|
||||
* be used to complete/abort a transaction if the ``send_stop_bit`` argument
|
||||
* was not set when calling the read() or write() functions.
|
||||
*/
|
||||
void send_stop_bit(void);
|
||||
|
||||
|
||||
/** Shutdown the I2C component.
|
||||
*
|
||||
* This function will cause the I2C task to shutdown and return.
|
||||
*/
|
||||
void shutdown();
|
||||
} i2c_master_async_if;
|
||||
|
||||
/** I2C master component (asynchronous API).
|
||||
*
|
||||
* This function implements I2C and allows clients to asynchronously
|
||||
* perform operations on the bus.
|
||||
*
|
||||
* \param i the interfaces to connect the component to its clients
|
||||
* \param n the number of clients connected to the component
|
||||
* \param p_scl the SCL port of the I2C bus
|
||||
* \param p_sda the SDA port of the I2C bus
|
||||
* \param kbits_per_second the speed of the I2C bus
|
||||
* \param max_transaction_size the size of the local buffer in bytes. Any
|
||||
* transactions exceeding this size will cause a
|
||||
* run-time exception.
|
||||
*
|
||||
*/
|
||||
void i2c_master_async(server interface i2c_master_async_if i[n],
|
||||
size_t n,
|
||||
port p_scl, port p_sda,
|
||||
static const unsigned kbits_per_second,
|
||||
static const size_t max_transaction_size);
|
||||
|
||||
/** I2C master component (asynchronous API, combinable).
|
||||
*
|
||||
* This function implements I2C and allows clients to asynchronously
|
||||
* perform operations on the bus.
|
||||
* Note that this component can be run on the same logical core as other
|
||||
* tasks (i.e. it is [[combinable]]). However, care must be taken that the
|
||||
* other tasks do not take too long in their select cases otherwise this
|
||||
* component may miss I2C transactions.
|
||||
*
|
||||
* \param i the interfaces to connect the component to its clients
|
||||
* \param n the number of clients connected to the component
|
||||
* \param p_scl the SCL port of the I2C bus
|
||||
* \param p_sda the SDA port of the I2C bus
|
||||
* \param kbits_per_second the speed of the I2C bus
|
||||
* \param max_transaction_size the size of the local buffer in bytes. Any
|
||||
* transactions exceeding this size will cause a
|
||||
* run-time exception.
|
||||
*/
|
||||
[[combinable]]
|
||||
void i2c_master_async_comb(server interface i2c_master_async_if i[n],
|
||||
size_t n,
|
||||
port p_scl, port p_sda,
|
||||
static const unsigned kbits_per_second,
|
||||
static const size_t max_transaction_size);
|
||||
|
||||
|
||||
|
||||
typedef enum i2c_slave_ack_t {
|
||||
I2C_SLAVE_ACK,
|
||||
I2C_SLAVE_NACK,
|
||||
} i2c_slave_ack_t;
|
||||
|
||||
/** This interface is used to communicate with an I2C slave component.
|
||||
* It provides facilities for reading and writing to the bus. The I2C slave
|
||||
* component acts a *client* to this interface. So the application must
|
||||
* respond to these calls (i.e. the members of the interface are callbacks
|
||||
* to the application).
|
||||
*
|
||||
*/
|
||||
typedef interface i2c_slave_callback_if {
|
||||
|
||||
/** Master has requested a read.
|
||||
*
|
||||
* This callback function is called by the component
|
||||
* if the bus master requests a read from this slave device.
|
||||
*
|
||||
* At this point the slave can choose to accept the request (and
|
||||
* drive an ACK signal back to the master) or not (and drive a NACK
|
||||
* signal).
|
||||
*
|
||||
* \returns the callback must return either ``I2C_SLAVE_ACK`` or
|
||||
* ``I2C_SLAVE_NACK``.
|
||||
*/
|
||||
[[guarded]]
|
||||
i2c_slave_ack_t ack_read_request(void);
|
||||
|
||||
|
||||
/** Master has requested a write.
|
||||
*
|
||||
* This callback function is called by the component
|
||||
* if the bus master requests a write from this slave device.
|
||||
*
|
||||
* At this point the slave can choose to accept the request (and
|
||||
* drive an ACK signal back to the master) or not (and drive a NACK
|
||||
* signal).
|
||||
*
|
||||
* \returns the callback must return either ``I2C_SLAVE_ACK`` or
|
||||
* ``I2C_SLAVE_NACK``.
|
||||
*/
|
||||
[[guarded]]
|
||||
i2c_slave_ack_t ack_write_request(void);
|
||||
|
||||
/** Master requires data.
|
||||
*
|
||||
* This callback function will be called when the I2C master requires data
|
||||
* from the slave.
|
||||
*
|
||||
* \return the data to pass to the master.
|
||||
*/
|
||||
[[guarded]]
|
||||
uint8_t master_requires_data();
|
||||
|
||||
/** Master has sent some data.
|
||||
*
|
||||
* This callback function will be called when the I2C master has transferred
|
||||
* a byte of data to the slave.
|
||||
*/
|
||||
[[guarded]]
|
||||
i2c_slave_ack_t master_sent_data(uint8_t data);
|
||||
|
||||
|
||||
/** Stop bit.
|
||||
*
|
||||
* This callback function will be called by the component when a stop bit
|
||||
* is sent by the master.
|
||||
*/
|
||||
void stop_bit(void);
|
||||
|
||||
/** Shutdown the I2C component.
|
||||
*
|
||||
* This function will cause the I2C slave task to shutdown and return.
|
||||
*/
|
||||
[[notification]] slave void shutdown();
|
||||
} i2c_slave_callback_if;
|
||||
|
||||
|
||||
/** I2C slave task.
|
||||
*
|
||||
* This function instantiates an i2c_slave component.
|
||||
*
|
||||
* \param i the client end of the i2c_slave_callback_if interface. The component
|
||||
* takes the client end and will make calls on the interface when
|
||||
* the master performs reads or writes.
|
||||
* \param p_scl the SCL port of the I2C bus
|
||||
* \param p_sda the SDA port of the I2C bus
|
||||
* \param device_addr the address of the slave device
|
||||
*
|
||||
*/
|
||||
[[combinable]]
|
||||
void i2c_slave(client i2c_slave_callback_if i,
|
||||
port p_scl, port p_sda,
|
||||
uint8_t device_addr);
|
||||
|
||||
#endif // __XC__
|
||||
|
||||
#endif
|
||||
7
lib_i2c/lib_i2c/lib_build_info.cmake
Normal file
7
lib_i2c/lib_i2c/lib_build_info.cmake
Normal file
@@ -0,0 +1,7 @@
|
||||
set(LIB_NAME lib_i2c)
|
||||
set(LIB_VERSION 6.2.0)
|
||||
set(LIB_INCLUDES api)
|
||||
set(LIB_DEPENDENT_MODULES "lib_xassert(4.2.0)")
|
||||
set(LIB_COMPILER_FLAGS -Os)
|
||||
|
||||
XMOS_REGISTER_MODULE()
|
||||
14
lib_i2c/lib_i2c/module_build_info
Normal file
14
lib_i2c/lib_i2c/module_build_info
Normal file
@@ -0,0 +1,14 @@
|
||||
VERSION = 6.2.0
|
||||
|
||||
DEPENDENT_MODULES = lib_xassert(>=4.2.0)
|
||||
|
||||
MODULE_XCC_FLAGS = $(XCC_FLAGS) \
|
||||
-Os
|
||||
|
||||
OPTIONAL_HEADERS +=
|
||||
|
||||
EXPORT_INCLUDE_DIRS = api
|
||||
|
||||
INCLUDE_DIRS = $(EXPORT_INCLUDE_DIRS)
|
||||
|
||||
SOURCE_DIRS = src
|
||||
294
lib_i2c/lib_i2c/src/i2c_master.xc
Normal file
294
lib_i2c/lib_i2c/src/i2c_master.xc
Normal file
@@ -0,0 +1,294 @@
|
||||
// Copyright 2011-2021 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#include <i2c.h>
|
||||
#include <xs1.h>
|
||||
#include <xclib.h>
|
||||
#include <timer.h>
|
||||
|
||||
#include "xassert.h"
|
||||
|
||||
/* NOTE: the kbits_per_second needs to be passed around due to the fact that the
|
||||
* compiler won't compute a new static const from a static const.
|
||||
*/
|
||||
|
||||
/** Return the number of 10ns timer ticks required to meet the timing as defined
|
||||
* in the standards.
|
||||
*/
|
||||
static const unsigned inline compute_low_period_ticks(
|
||||
static const unsigned kbits_per_second)
|
||||
{
|
||||
unsigned ticks = 0;
|
||||
if (kbits_per_second <= 100) {
|
||||
const unsigned four_point_seven_micro_seconds_in_ticks = 470;
|
||||
ticks = four_point_seven_micro_seconds_in_ticks;
|
||||
} else if (kbits_per_second <= 400) {
|
||||
const unsigned one_point_three_micro_seconds_in_ticks = 130;
|
||||
ticks = one_point_three_micro_seconds_in_ticks;
|
||||
} else {
|
||||
fail("Fast-mode Plus not implemented");
|
||||
}
|
||||
|
||||
// There is some jitter on the falling edges of the clock. In order to ensure
|
||||
// that the low period is respected we need to extend the minimum low period.
|
||||
const unsigned jitter_ticks = 3;
|
||||
return ticks + jitter_ticks;
|
||||
}
|
||||
|
||||
static const unsigned inline compute_bus_off_ticks(
|
||||
static const unsigned kbits_per_second)
|
||||
{
|
||||
const unsigned bit_time = BIT_TIME(kbits_per_second);
|
||||
|
||||
// Ensure the bus off time is respected. This is just over 1/2 bit time in
|
||||
// the case of the Fast-mode I2C so adding bit_time/16 ensures the timing
|
||||
// will be enforced
|
||||
return bit_time/2 + bit_time/16;
|
||||
}
|
||||
|
||||
/** Releases the SCL line, reads it back and waits until it goes high (in
|
||||
* case the slave is clock stretching).
|
||||
* Since the line going high may be delayed, the fall_time value may
|
||||
* need to be adjusted
|
||||
*/
|
||||
static void release_clock_and_wait(
|
||||
port p_scl,
|
||||
unsigned &fall_time,
|
||||
unsigned delay)
|
||||
{
|
||||
p_scl when pinseq(1) :> void;
|
||||
|
||||
timer tmr;
|
||||
unsigned time;
|
||||
tmr when timerafter(fall_time + delay) :> time;
|
||||
|
||||
// Adjust timing due to support clock stretching without clock drift in the
|
||||
// normal case.
|
||||
|
||||
// If the time is beyond the time it takes simply to wake up and start
|
||||
// executing then the clock needs to be adjusted
|
||||
const int wake_up_ticks = 10;
|
||||
if (time > fall_time + delay + wake_up_ticks) {
|
||||
fall_time = time - delay - wake_up_ticks;
|
||||
}
|
||||
}
|
||||
|
||||
/** 'Pulse' the clock line high and in the middle of the high period
|
||||
* sample the data line (if required). Timing is done via the fall_time
|
||||
* reference and bit_time period supplied.
|
||||
*/
|
||||
[[always_inline]]
|
||||
static int inline high_pulse_sample(
|
||||
port p_scl,
|
||||
port p_sda,
|
||||
static const unsigned kbits_per_second,
|
||||
unsigned &fall_time)
|
||||
{
|
||||
const unsigned bit_time = BIT_TIME(kbits_per_second);
|
||||
|
||||
int sample_value = 0;
|
||||
timer tmr;
|
||||
p_sda :> int _;
|
||||
tmr when timerafter(fall_time + compute_low_period_ticks(kbits_per_second)) :> void;
|
||||
release_clock_and_wait(p_scl, fall_time, (bit_time * 3) / 4);
|
||||
p_sda :> sample_value;
|
||||
fall_time = fall_time + bit_time;
|
||||
tmr when timerafter(fall_time) :> void;
|
||||
p_scl <: 0;
|
||||
// Mask off all but lowest bit of input - allows use of bit[0] of multibit port
|
||||
return sample_value & 1;
|
||||
}
|
||||
|
||||
/** 'Pulse' the clock line high. Timing is done via the fall_time
|
||||
* reference and bit_time period supplied.
|
||||
*/
|
||||
[[always_inline]]
|
||||
static void inline high_pulse(
|
||||
port p_scl,
|
||||
static const unsigned kbits_per_second,
|
||||
unsigned &fall_time)
|
||||
{
|
||||
const unsigned bit_time = BIT_TIME(kbits_per_second);
|
||||
|
||||
timer tmr;
|
||||
tmr when timerafter(fall_time + compute_low_period_ticks(kbits_per_second)) :> void;
|
||||
release_clock_and_wait(p_scl, fall_time, (bit_time * 3) / 4);
|
||||
fall_time = fall_time + bit_time;
|
||||
tmr when timerafter(fall_time) :> void;
|
||||
p_scl <: 0;
|
||||
}
|
||||
|
||||
/** Output a start bit. The function returns the 'fall time' i.e. the
|
||||
* reference clock time when the SCL line transitions to low.
|
||||
*/
|
||||
static void start_bit(
|
||||
port p_scl,
|
||||
port p_sda,
|
||||
static const unsigned kbits_per_second,
|
||||
unsigned &fall_time,
|
||||
int stopped)
|
||||
{
|
||||
const unsigned bit_time = BIT_TIME(kbits_per_second);
|
||||
|
||||
timer tmr;
|
||||
|
||||
if (!stopped) {
|
||||
fall_time += compute_low_period_ticks(kbits_per_second);
|
||||
tmr when timerafter(fall_time) :> fall_time;
|
||||
release_clock_and_wait(p_scl, fall_time, compute_bus_off_ticks(kbits_per_second));
|
||||
}
|
||||
|
||||
// Drive SDA low
|
||||
p_sda <: 0;
|
||||
delay_ticks(bit_time / 2);
|
||||
// Drive SCL low
|
||||
p_scl <: 0;
|
||||
|
||||
// Record
|
||||
tmr :> fall_time;
|
||||
}
|
||||
|
||||
/** Output a stop bit.
|
||||
*/
|
||||
static void stop_bit(
|
||||
port p_scl,
|
||||
port p_sda,
|
||||
static const unsigned kbits_per_second,
|
||||
unsigned &fall_time)
|
||||
{
|
||||
const unsigned bit_time = BIT_TIME(kbits_per_second);
|
||||
|
||||
timer tmr;
|
||||
p_sda <: 0;
|
||||
tmr when timerafter(fall_time + compute_low_period_ticks(kbits_per_second)) :> void;
|
||||
release_clock_and_wait(p_scl, fall_time, bit_time);
|
||||
p_sda :> void;
|
||||
delay_ticks(compute_bus_off_ticks(kbits_per_second));
|
||||
}
|
||||
|
||||
/** Transmit 8 bits of data, then read the ack back from the slave and return
|
||||
* that value.
|
||||
*/
|
||||
static int tx8(
|
||||
port p_scl,
|
||||
port p_sda,
|
||||
unsigned data,
|
||||
static const unsigned kbits_per_second,
|
||||
unsigned &fall_time)
|
||||
{
|
||||
// Data is transmitted MSB first
|
||||
data = bitrev(data) >> 24;
|
||||
for (int i = 8; i != 0; i--) {
|
||||
p_sda <: data & 0x1;
|
||||
data >>= 1;
|
||||
high_pulse(p_scl, kbits_per_second, fall_time);
|
||||
}
|
||||
return high_pulse_sample(p_scl, p_sda, kbits_per_second, fall_time);
|
||||
}
|
||||
|
||||
[[distributable]]
|
||||
void i2c_master(
|
||||
server interface i2c_master_if c[n],
|
||||
size_t n,
|
||||
port p_scl,
|
||||
port p_sda,
|
||||
static const unsigned kbits_per_second)
|
||||
{
|
||||
const unsigned bit_time = BIT_TIME(kbits_per_second);
|
||||
|
||||
unsigned last_fall_time = 0;
|
||||
unsigned locked_client = -1;
|
||||
p_scl :> void;
|
||||
p_sda :> void;
|
||||
while (1) {
|
||||
select {
|
||||
|
||||
case (size_t i =0; i < n; i++)
|
||||
(n == 1 || (locked_client == -1 || i == locked_client)) =>
|
||||
c[i].read(uint8_t device, uint8_t buf[m], size_t m,
|
||||
int send_stop_bit) -> i2c_res_t result:
|
||||
|
||||
const int stopped = locked_client == -1;
|
||||
unsigned fall_time = last_fall_time;
|
||||
start_bit(p_scl, p_sda, kbits_per_second, fall_time, stopped);
|
||||
int ack = tx8(p_scl, p_sda, (device << 1) | 1, kbits_per_second, fall_time);
|
||||
|
||||
if (ack == 0) {
|
||||
for (int j = 0; j < m; j++){
|
||||
unsigned char data = 0;
|
||||
timer tmr;
|
||||
for (int i = 8; i != 0; i--) {
|
||||
int temp = high_pulse_sample(p_scl, p_sda, kbits_per_second, fall_time);
|
||||
data = (data << 1) | temp;
|
||||
}
|
||||
buf[j] = data;
|
||||
|
||||
tmr when timerafter(fall_time + bit_time/4) :> void;
|
||||
// ACK after every read byte until the final byte then NACK.
|
||||
if (j == m-1)
|
||||
p_sda :> void;
|
||||
else {
|
||||
p_sda <: 0;
|
||||
}
|
||||
// High pulse but make sure SDA is not driving before lowering SCL
|
||||
tmr when timerafter(fall_time + compute_low_period_ticks(kbits_per_second)) :> void;
|
||||
high_pulse(p_scl, kbits_per_second, fall_time);
|
||||
p_sda :> void;
|
||||
}
|
||||
}
|
||||
if (send_stop_bit) {
|
||||
stop_bit(p_scl, p_sda, kbits_per_second, fall_time);
|
||||
locked_client = -1;
|
||||
}
|
||||
else {
|
||||
locked_client = i;
|
||||
}
|
||||
|
||||
result = (ack == 0) ? I2C_ACK : I2C_NACK;
|
||||
|
||||
// Remember the last fall time to ensure the next start bit is valid
|
||||
last_fall_time = fall_time;
|
||||
break;
|
||||
|
||||
case (size_t i = 0; i < n; i++)
|
||||
(n == 1 || (locked_client == -1 || i == locked_client)) =>
|
||||
c[i].write(uint8_t device, uint8_t buf[n], size_t n,
|
||||
size_t &num_bytes_sent,
|
||||
int send_stop_bit) -> i2c_res_t result:
|
||||
unsigned fall_time = last_fall_time;
|
||||
const int stopped = locked_client == -1;
|
||||
start_bit(p_scl, p_sda, kbits_per_second, fall_time, stopped);
|
||||
int ack = tx8(p_scl, p_sda, (device << 1), kbits_per_second, fall_time);
|
||||
int j = 0;
|
||||
for (; j < n; j++) {
|
||||
if (ack != 0) {
|
||||
break;
|
||||
}
|
||||
ack = tx8(p_scl, p_sda, buf[j], kbits_per_second, fall_time);
|
||||
}
|
||||
if (send_stop_bit) {
|
||||
stop_bit(p_scl, p_sda, kbits_per_second, fall_time);
|
||||
locked_client = -1;
|
||||
} else {
|
||||
locked_client = i;
|
||||
}
|
||||
num_bytes_sent = j;
|
||||
result = (ack == 0) ? I2C_ACK : I2C_NACK;
|
||||
|
||||
// Remember the last fall time to ensure the next start bit is valid
|
||||
last_fall_time = fall_time;
|
||||
break;
|
||||
|
||||
case c[int i].send_stop_bit(void):
|
||||
timer tmr;
|
||||
unsigned fall_time;
|
||||
tmr :> fall_time;
|
||||
stop_bit(p_scl, p_sda, kbits_per_second, fall_time);
|
||||
locked_client = -1;
|
||||
break;
|
||||
|
||||
case c[int i].shutdown(void):
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
526
lib_i2c/lib_i2c/src/i2c_master_async.xc
Normal file
526
lib_i2c/lib_i2c/src/i2c_master_async.xc
Normal file
@@ -0,0 +1,526 @@
|
||||
// Copyright 2015-2021 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#include <i2c.h>
|
||||
#include <xs1.h>
|
||||
#include <string.h>
|
||||
#include <print.h>
|
||||
#include <syscall.h>
|
||||
#include <xassert.h>
|
||||
|
||||
enum i2c_async_master_state_t {
|
||||
IDLE,
|
||||
REPEATED_START_CLOCK_LOW,
|
||||
REPEATED_START_WAIT_FOR_CLOCK_HIGH,
|
||||
REPEATED_START_HOLD_CLOCK_HIGH,
|
||||
START_BIT_0,
|
||||
START_BIT_1,
|
||||
WRITE_0,
|
||||
WRITE_1,
|
||||
WRITE_ACK_0,
|
||||
WRITE_ACK_1,
|
||||
WRITE_ACK_2,
|
||||
READ_0,
|
||||
READ_1,
|
||||
READ_2,
|
||||
READ_ACK_0,
|
||||
READ_ACK_1,
|
||||
STOP_BIT_0,
|
||||
STOP_BIT_1,
|
||||
STOP_BIT_2,
|
||||
STOP_BIT_3,
|
||||
STOP_BIT_4,
|
||||
DONE_NO_STOP,
|
||||
};
|
||||
|
||||
enum optype_t {
|
||||
WRITE = 0, READ = 1, SEND_STOP_BIT = 2
|
||||
};
|
||||
|
||||
enum ack_t {
|
||||
ACKED = 0, NACKED = 1,
|
||||
};
|
||||
|
||||
|
||||
void i2c_master_async_aux(
|
||||
server interface i2c_master_async_if i[n],
|
||||
size_t n,
|
||||
client interface i2c_master_if i2c,
|
||||
static const size_t max_transaction_size)
|
||||
{
|
||||
uint8_t buf[max_transaction_size];
|
||||
uint8_t device_addr;
|
||||
size_t num_bytes, num_bytes_sent;
|
||||
int send_stop_bit = 0;
|
||||
int optype = 0;
|
||||
int cur_client = -1;
|
||||
i2c_res_t res = I2C_ACK;
|
||||
|
||||
while (1) {
|
||||
select {
|
||||
case i[int j].write(uint8_t addr, uint8_t buf0[n], size_t n,
|
||||
int ssb):
|
||||
device_addr = addr;
|
||||
send_stop_bit = ssb;
|
||||
optype = WRITE;
|
||||
num_bytes = n;
|
||||
memcpy(buf, buf0, n);
|
||||
cur_client = j;
|
||||
break;
|
||||
case i[int j].read(uint8_t addr, size_t n, int ssb):
|
||||
device_addr = addr;
|
||||
send_stop_bit = ssb;
|
||||
optype = READ;
|
||||
num_bytes = n;
|
||||
cur_client = j;
|
||||
break;
|
||||
case i[int j].send_stop_bit():
|
||||
optype = WRITE;
|
||||
cur_client = j;
|
||||
break;
|
||||
case i[int j].shutdown():
|
||||
return;
|
||||
case i[int j].get_write_result(size_t &nbs) -> i2c_res_t result:
|
||||
// ERROR
|
||||
break;
|
||||
case i[int j].get_read_data(uint8_t buf0[n], size_t n) -> i2c_res_t result:
|
||||
// ERROR
|
||||
break;
|
||||
|
||||
}
|
||||
switch (optype) {
|
||||
case WRITE:
|
||||
res = i2c.write(device_addr, buf, num_bytes, num_bytes_sent,
|
||||
send_stop_bit);
|
||||
break;
|
||||
case READ:
|
||||
res = i2c.read(device_addr, buf, num_bytes, send_stop_bit);
|
||||
break;
|
||||
case SEND_STOP_BIT:
|
||||
i2c.send_stop_bit();
|
||||
break;
|
||||
}
|
||||
|
||||
i[cur_client].operation_complete();
|
||||
|
||||
select {
|
||||
case i[int j].get_write_result(size_t &nbs) -> i2c_res_t result:
|
||||
nbs = num_bytes_sent;
|
||||
result = res;
|
||||
break;
|
||||
case i[int j].get_read_data(uint8_t buf0[n], size_t n) -> i2c_res_t result:
|
||||
memcpy(buf0, buf, n);
|
||||
result = res;
|
||||
break;
|
||||
case i[int j].shutdown():
|
||||
return;
|
||||
case i[int j].send_stop_bit():
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void i2c_master_async(
|
||||
server interface i2c_master_async_if i[n],
|
||||
size_t n,
|
||||
port p_scl, port p_sda,
|
||||
static const unsigned kbits_per_second,
|
||||
static const size_t max_transaction_size)
|
||||
{
|
||||
i2c_master_if i2c_dist[1];
|
||||
par {
|
||||
i2c_master(i2c_dist, 1, p_scl, p_sda, kbits_per_second);
|
||||
// Disable 'slices interface preventing analysis' warning
|
||||
#pragma warning disable
|
||||
i2c_master_async_aux(i, n, i2c_dist[0], max_transaction_size);
|
||||
#pragma warning enable
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Adjust for time slip.
|
||||
*
|
||||
* All timings of the state machine in i2c_master_async_comb are made
|
||||
* w.r.t the measured fall_time of the SCL line.
|
||||
* If the task keeps up then we will get the speed requested. However,
|
||||
* if the task falls behind due to other processing on the core then
|
||||
* this function will adjust both the next event time and the fall time
|
||||
* reference to slip so that subsequent timings are correct.
|
||||
*/
|
||||
static void inline adjust_for_slip(
|
||||
int now,
|
||||
int &event_time,
|
||||
int &?fall_time)
|
||||
{
|
||||
// This value is the minimum number of timer ticks we estimate we can
|
||||
// get to a new event to from now.
|
||||
const int SLIP_THRESHOLD = 100;
|
||||
if (event_time - now < SLIP_THRESHOLD) {
|
||||
int new_event_time = now + SLIP_THRESHOLD;
|
||||
if (!isnull(fall_time)) {
|
||||
fall_time += new_event_time - event_time;
|
||||
}
|
||||
event_time = new_event_time;
|
||||
}
|
||||
}
|
||||
|
||||
static int inline adjust_fall(
|
||||
int event_time,
|
||||
int now,
|
||||
int fall_time)
|
||||
{
|
||||
const int SLIP_THRESHOLD = 100;
|
||||
if (now - event_time > SLIP_THRESHOLD) {
|
||||
fall_time = fall_time + (now - event_time);
|
||||
}
|
||||
return fall_time;
|
||||
}
|
||||
|
||||
[[combinable]]
|
||||
void i2c_master_async_comb(
|
||||
server interface i2c_master_async_if i[n],
|
||||
size_t n,
|
||||
port p_scl, port p_sda,
|
||||
static const unsigned kbits_per_second,
|
||||
static const size_t max_transaction_size)
|
||||
{
|
||||
const unsigned bit_time = BIT_TIME(kbits_per_second);
|
||||
|
||||
uint8_t buf[max_transaction_size] = {};
|
||||
timer tmr;
|
||||
int state = IDLE;
|
||||
int waiting_for_clock_release = 0, timer_enabled = 0;
|
||||
int optype = 0;
|
||||
int fall_time = 0;
|
||||
int data = 0;
|
||||
int bitnum = 0;
|
||||
int bytes_sent = 0;
|
||||
int num_bytes = 0;
|
||||
int event_time = 0;
|
||||
int cur_client = -1;
|
||||
int send_stop_bit = 0;
|
||||
int stopped = 1;
|
||||
i2c_res_t res = I2C_ACK;
|
||||
|
||||
/* These select cases represent the main state machine for the I2C master
|
||||
component. The state machine will change state based on a timer event to
|
||||
progress the transaction or on an event from the SCL line when waiting
|
||||
for the clock to be released (supporting clock stretching). */
|
||||
while (1) {
|
||||
select {
|
||||
|
||||
case waiting_for_clock_release => p_scl when pinseq(1) :> void:
|
||||
int now;
|
||||
tmr :> now;
|
||||
switch (state) {
|
||||
case WRITE_0:
|
||||
case WRITE_ACK_0:
|
||||
case READ_ACK_0:
|
||||
case STOP_BIT_0:
|
||||
case REPEATED_START_WAIT_FOR_CLOCK_HIGH:
|
||||
case READ_0:
|
||||
fall_time = fall_time + bit_time;
|
||||
event_time = fall_time;
|
||||
adjust_for_slip(now, event_time, fall_time);
|
||||
break;
|
||||
case WRITE_ACK_1:
|
||||
fall_time = fall_time + bit_time;
|
||||
event_time = fall_time - bit_time / 4;
|
||||
adjust_for_slip(now, event_time, fall_time);
|
||||
state = WRITE_ACK_2;
|
||||
break;
|
||||
case STOP_BIT_2:
|
||||
event_time = now + bit_time / 2;
|
||||
adjust_for_slip(now, event_time, null);
|
||||
state = STOP_BIT_3;
|
||||
break;
|
||||
case READ_1:
|
||||
fall_time = fall_time + bit_time;
|
||||
event_time = fall_time - bit_time / 4;
|
||||
adjust_for_slip(now, event_time, fall_time);
|
||||
state = READ_2;
|
||||
break;
|
||||
}
|
||||
waiting_for_clock_release = 0;
|
||||
timer_enabled = 1;
|
||||
break;
|
||||
|
||||
case timer_enabled => tmr when timerafter(event_time) :> int now:
|
||||
switch (state) {
|
||||
case REPEATED_START_CLOCK_LOW:
|
||||
// The operation is finished, but no stop bit is being written
|
||||
p_scl <: 0;
|
||||
event_time = now + bit_time / 2;
|
||||
state = REPEATED_START_WAIT_FOR_CLOCK_HIGH;
|
||||
break;
|
||||
case REPEATED_START_WAIT_FOR_CLOCK_HIGH:
|
||||
p_scl :> void;
|
||||
timer_enabled = 0;
|
||||
waiting_for_clock_release = 1;
|
||||
state = REPEATED_START_HOLD_CLOCK_HIGH;
|
||||
break;
|
||||
case REPEATED_START_HOLD_CLOCK_HIGH:
|
||||
event_time = now + bit_time / 2;
|
||||
state = START_BIT_0;
|
||||
break;
|
||||
case START_BIT_0:
|
||||
p_sda <: 0;
|
||||
event_time = now + bit_time / 2;
|
||||
state = START_BIT_1;
|
||||
break;
|
||||
#pragma fallthrough
|
||||
case START_BIT_1:
|
||||
fall_time = now;
|
||||
// Fallthrough to WRITE_0 state
|
||||
case WRITE_0:
|
||||
p_scl <: 0;
|
||||
fall_time = adjust_fall(event_time, now, fall_time);
|
||||
p_sda <: data >> 7;
|
||||
data <<= 1;
|
||||
bitnum++;
|
||||
state = WRITE_1;
|
||||
event_time = fall_time + bit_time / 2 + bit_time / 32;
|
||||
adjust_for_slip(now, event_time, fall_time);
|
||||
break;
|
||||
case WRITE_1:
|
||||
p_scl :> void;
|
||||
timer_enabled = 0;
|
||||
waiting_for_clock_release = 1;
|
||||
if (bitnum == 8) {
|
||||
state = WRITE_ACK_0;
|
||||
} else {
|
||||
state = WRITE_0;
|
||||
}
|
||||
break;
|
||||
case WRITE_ACK_0:
|
||||
p_scl <: 0;
|
||||
p_sda :> void;
|
||||
fall_time = adjust_fall(event_time, now, fall_time);
|
||||
event_time = fall_time + bit_time / 2 + bit_time / 32;
|
||||
adjust_for_slip(now, event_time, fall_time);
|
||||
state = WRITE_ACK_1;
|
||||
break;
|
||||
case WRITE_ACK_1:
|
||||
p_scl :> void;
|
||||
timer_enabled = 0;
|
||||
waiting_for_clock_release = 1;
|
||||
break;
|
||||
case WRITE_ACK_2:
|
||||
int ack;
|
||||
p_sda :> ack;
|
||||
event_time = fall_time;
|
||||
adjust_for_slip(now, event_time, fall_time);
|
||||
if (ack == ACKED && optype == WRITE) {
|
||||
bytes_sent++;
|
||||
int all_data_sent = (bytes_sent == num_bytes);
|
||||
if (all_data_sent) {
|
||||
// The master and slave disagree since the slave should nack
|
||||
// the last byte.
|
||||
res = I2C_ACK;
|
||||
if (send_stop_bit) {
|
||||
state = STOP_BIT_0;
|
||||
} else {
|
||||
state = DONE_NO_STOP;
|
||||
}
|
||||
} else {
|
||||
// get next byte of data.
|
||||
data = buf[bytes_sent];
|
||||
bitnum = 0;
|
||||
// Now go back to the transmitting
|
||||
state = WRITE_0;
|
||||
}
|
||||
} else if (ack == NACKED && optype == WRITE) {
|
||||
bytes_sent++;
|
||||
int all_data_sent = (bytes_sent == num_bytes);
|
||||
if (all_data_sent) {
|
||||
// The master and slave agree that this is the end of the operation.
|
||||
res = I2C_NACK;
|
||||
if (send_stop_bit) {
|
||||
state = STOP_BIT_0;
|
||||
} else {
|
||||
state = DONE_NO_STOP;
|
||||
}
|
||||
} else {
|
||||
// The slave has aborted the operation.
|
||||
res = I2C_NACK;
|
||||
if (send_stop_bit) {
|
||||
state = STOP_BIT_0;
|
||||
} else {
|
||||
state = DONE_NO_STOP;
|
||||
}
|
||||
}
|
||||
} else if (ack == ACKED && optype == READ) {
|
||||
// The slave has acked the addr, we can go ahead with the operation.
|
||||
data = 0;
|
||||
bitnum = 0;
|
||||
bytes_sent++;
|
||||
state = READ_0;
|
||||
res = I2C_ACK;
|
||||
} else if (ack == NACKED && optype == READ) {
|
||||
// The slave has nacked the addr (or the slave isn't there). Abort.
|
||||
res = I2C_NACK;
|
||||
if (send_stop_bit) {
|
||||
state = STOP_BIT_0;
|
||||
} else {
|
||||
state = DONE_NO_STOP;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case READ_0:
|
||||
p_scl <: 0;
|
||||
p_sda :> void;
|
||||
fall_time = adjust_fall(event_time, now, fall_time);
|
||||
bitnum++;
|
||||
state = READ_1;
|
||||
event_time = fall_time + bit_time / 2 + bit_time / 32;
|
||||
adjust_for_slip(now, event_time, fall_time);
|
||||
break;
|
||||
case READ_1:
|
||||
p_scl :> void;
|
||||
timer_enabled = 0;
|
||||
waiting_for_clock_release = 1;
|
||||
break;
|
||||
case READ_2:
|
||||
int bit;
|
||||
p_sda :> bit;
|
||||
data <<= 1;
|
||||
data += bit&1;
|
||||
event_time = fall_time;
|
||||
adjust_for_slip(now, event_time, fall_time);
|
||||
if (bitnum == 8) {
|
||||
buf[bytes_sent] = data;
|
||||
bytes_sent++;
|
||||
state = READ_ACK_0;
|
||||
} else {
|
||||
state = READ_0;
|
||||
}
|
||||
break;
|
||||
case READ_ACK_0:
|
||||
p_scl <: 0;
|
||||
if (bytes_sent == num_bytes) {
|
||||
p_sda :> void;
|
||||
} else {
|
||||
p_sda <: 0;
|
||||
}
|
||||
fall_time = adjust_fall(event_time, now, fall_time);
|
||||
state = READ_ACK_1;
|
||||
event_time = fall_time + bit_time / 2 + bit_time / 32;
|
||||
adjust_for_slip(now, event_time, fall_time);
|
||||
break;
|
||||
case READ_ACK_1:
|
||||
p_scl :> void;
|
||||
timer_enabled = 0;
|
||||
waiting_for_clock_release = 1;
|
||||
if (bytes_sent == num_bytes) {
|
||||
if (send_stop_bit) {
|
||||
state = STOP_BIT_0;
|
||||
} else {
|
||||
state = DONE_NO_STOP;
|
||||
}
|
||||
} else {
|
||||
data = 0;
|
||||
bitnum = 0;
|
||||
state = READ_0;
|
||||
}
|
||||
break;
|
||||
case STOP_BIT_0:
|
||||
p_scl <: 0;
|
||||
fall_time = adjust_fall(event_time, now, fall_time);
|
||||
event_time = fall_time + bit_time / 4;
|
||||
adjust_for_slip(now, event_time, fall_time);
|
||||
state = STOP_BIT_1;
|
||||
break;
|
||||
case STOP_BIT_1:
|
||||
p_sda <: 0;
|
||||
event_time = fall_time + bit_time / 2;
|
||||
adjust_for_slip(now, event_time, fall_time);
|
||||
state = STOP_BIT_2;
|
||||
break;
|
||||
case STOP_BIT_2:
|
||||
p_scl :> void;
|
||||
timer_enabled = 0;
|
||||
waiting_for_clock_release = 1;
|
||||
break;
|
||||
case STOP_BIT_3:
|
||||
p_sda :> void;
|
||||
event_time = now + bit_time/4;
|
||||
state = STOP_BIT_4;
|
||||
|
||||
// Know that the next transaction can start from the stopped state
|
||||
stopped = 1;
|
||||
break;
|
||||
#pragma fallthrough
|
||||
case DONE_NO_STOP:
|
||||
// Know that the next transaction needs to create a repeated start
|
||||
stopped = 0;
|
||||
// Fallthrough to STOP_BIT_4 code
|
||||
case STOP_BIT_4:
|
||||
i[cur_client].operation_complete();
|
||||
cur_client = -1;
|
||||
timer_enabled = 0;
|
||||
state = IDLE;
|
||||
break;
|
||||
case IDLE:
|
||||
fail("");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case i[int j].write(uint8_t device_addr, uint8_t buf0[n], size_t n,
|
||||
int _send_stop_bit):
|
||||
data = (device_addr << 1) | 0;
|
||||
bitnum = 0;
|
||||
optype = WRITE;
|
||||
num_bytes = n;
|
||||
send_stop_bit = _send_stop_bit;
|
||||
memcpy(buf, buf0, n);
|
||||
// The 'bytes_sent' variable gets increment after every byte *including*
|
||||
// the addr bytes. So we set it to -1 to be 0 after the addr byte.
|
||||
bytes_sent = -1;
|
||||
timer_enabled = 1;
|
||||
cur_client = j;
|
||||
tmr :> event_time;
|
||||
if (!stopped) {
|
||||
state = REPEATED_START_CLOCK_LOW;
|
||||
} else {
|
||||
state = START_BIT_0;
|
||||
}
|
||||
break;
|
||||
|
||||
case i[int j].read(uint8_t device_addr, size_t n, int _send_stop_bit):
|
||||
data = (device_addr << 1) | 1;
|
||||
bitnum = 0;
|
||||
optype = READ;
|
||||
num_bytes = n;
|
||||
send_stop_bit = _send_stop_bit;
|
||||
// The 'bytes_sent' variable gets increment after every byte *including*
|
||||
// the addr bytes. So we set it to -1 to be 0 after the addr byte.
|
||||
bytes_sent = -1;
|
||||
timer_enabled = 1;
|
||||
cur_client = j;
|
||||
tmr :> event_time;
|
||||
if (!stopped) {
|
||||
state = REPEATED_START_CLOCK_LOW;
|
||||
} else {
|
||||
state = START_BIT_0;
|
||||
}
|
||||
break;
|
||||
|
||||
case i[int j].send_stop_bit():
|
||||
break;
|
||||
|
||||
case i[int j].get_write_result(size_t &num_bytes_sent) -> i2c_res_t result:
|
||||
num_bytes_sent = bytes_sent;
|
||||
result = res;
|
||||
break;
|
||||
|
||||
case i[int j].get_read_data(uint8_t buf0[n], size_t n) -> i2c_res_t result:
|
||||
memcpy(buf0, buf, n);
|
||||
result = res;
|
||||
break;
|
||||
|
||||
case i[int j].shutdown():
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
57
lib_i2c/lib_i2c/src/i2c_master_ext.xc
Normal file
57
lib_i2c/lib_i2c/src/i2c_master_ext.xc
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2014-2021 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#include <i2c.h>
|
||||
|
||||
/* This file provides external definitions for the inline functions declared in
|
||||
i2c.h (using C99 inlining semantics) */
|
||||
|
||||
extends client interface i2c_master_if : {
|
||||
extern inline uint8_t read_reg_8_8(client interface i2c_master_if i,
|
||||
uint8_t device_addr, uint8_t reg,
|
||||
i2c_regop_res_t &res);
|
||||
|
||||
extern inline uint8_t read_reg(client interface i2c_master_if i,
|
||||
uint8_t device_addr, uint8_t reg,
|
||||
i2c_regop_res_t &res);
|
||||
|
||||
extern inline i2c_regop_res_t write_reg_n_m(client interface i2c_master_if i,
|
||||
uint8_t device_addr,
|
||||
uint8_t reg[m],
|
||||
size_t m,
|
||||
uint8_t data[n],
|
||||
size_t n);
|
||||
|
||||
extern inline i2c_regop_res_t write_reg_8_8(client interface i2c_master_if i,
|
||||
uint8_t device_addr, uint8_t reg,
|
||||
uint8_t data);
|
||||
|
||||
extern inline i2c_regop_res_t write_reg(client interface i2c_master_if i,
|
||||
uint8_t device_addr,
|
||||
uint8_t reg, uint8_t data);
|
||||
|
||||
extern inline uint8_t read_reg8_addr16(client interface i2c_master_if i,
|
||||
uint8_t device_addr, uint16_t reg,
|
||||
i2c_regop_res_t &res);
|
||||
|
||||
extern inline i2c_regop_res_t write_reg8_addr16(client interface i2c_master_if i,
|
||||
uint8_t device_addr, uint16_t reg,
|
||||
uint8_t data);
|
||||
|
||||
extern inline uint16_t read_reg16(client interface i2c_master_if i,
|
||||
uint8_t device_addr, uint16_t reg,
|
||||
i2c_regop_res_t &res);
|
||||
|
||||
extern inline i2c_regop_res_t write_reg16(client interface i2c_master_if i,
|
||||
uint8_t device_addr, uint16_t reg,
|
||||
uint16_t data);
|
||||
|
||||
extern inline i2c_regop_res_t write_reg16_addr8(client interface i2c_master_if i,
|
||||
uint8_t device_addr, uint8_t reg,
|
||||
uint16_t data);
|
||||
|
||||
extern inline uint16_t read_reg16_addr8(client interface i2c_master_if i,
|
||||
uint8_t device_addr, uint8_t reg,
|
||||
i2c_regop_res_t &result);
|
||||
|
||||
|
||||
}
|
||||
324
lib_i2c/lib_i2c/src/i2c_master_single_port.xc
Normal file
324
lib_i2c/lib_i2c/src/i2c_master_single_port.xc
Normal file
@@ -0,0 +1,324 @@
|
||||
// Copyright 2013-2021 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#if (defined(__XS2A__) || defined(__XS3A__))
|
||||
|
||||
#include "i2c.h"
|
||||
#include <xs1.h>
|
||||
#include <xclib.h>
|
||||
#include <stdio.h>
|
||||
#include <timer.h>
|
||||
|
||||
#include "xassert.h"
|
||||
|
||||
#define SDA_LOW 0
|
||||
#define SCL_LOW 0
|
||||
|
||||
/* NOTE: the kbits_per_second needs to be passed around due to the fact that the
|
||||
* compiler won't compute a new static const from a static const.
|
||||
*/
|
||||
|
||||
/** Return the number of 10ns timer ticks required to meet the timing as defined
|
||||
* in the standards.
|
||||
*/
|
||||
static const unsigned inline compute_low_period_ticks(
|
||||
static const unsigned kbits_per_second)
|
||||
{
|
||||
unsigned ticks = 0;
|
||||
if (kbits_per_second <= 100) {
|
||||
const unsigned four_point_seven_micro_seconds_in_ticks = 470;
|
||||
ticks = four_point_seven_micro_seconds_in_ticks;
|
||||
} else if (kbits_per_second <= 400) {
|
||||
const unsigned one_point_three_micro_seconds_in_ticks = 130;
|
||||
ticks = one_point_three_micro_seconds_in_ticks;
|
||||
} else {
|
||||
fail("Fast-mode Plus not implemented");
|
||||
}
|
||||
|
||||
// There is some jitter on the falling edges of the clock. In order to ensure
|
||||
// that the low period is respected we need to extend the minimum low period.
|
||||
const unsigned jitter_ticks = 3;
|
||||
return ticks + jitter_ticks;
|
||||
}
|
||||
|
||||
static const unsigned inline compute_bus_off_ticks(
|
||||
static const unsigned kbits_per_second)
|
||||
{
|
||||
const unsigned bit_time = BIT_TIME(kbits_per_second);
|
||||
|
||||
// Ensure the bus off time is respected. This is just over 1/2 bit time in
|
||||
// the case of the Fast-mode I2C so adding bit_time/16 ensures the timing
|
||||
// will be enforced
|
||||
return bit_time/2 + bit_time/16;
|
||||
}
|
||||
|
||||
/** Reads back the SCL line, waiting until it goes high (in
|
||||
* case the slave is clock stretching). It is assumed that the clock
|
||||
* line has been release (driven high) before calling this function.
|
||||
* Since the line going high may be delayed, the fall_time value may
|
||||
* need to be adjusted
|
||||
*/
|
||||
static void wait_for_clock_high(
|
||||
port p_i2c,
|
||||
static const unsigned scl_bit_position,
|
||||
unsigned &fall_time,
|
||||
unsigned delay)
|
||||
{
|
||||
const unsigned SCL_HIGH = BIT_MASK(scl_bit_position);
|
||||
|
||||
unsigned val = peek(p_i2c);
|
||||
while (!(val & SCL_HIGH)) {
|
||||
val = peek(p_i2c);
|
||||
}
|
||||
|
||||
timer tmr;
|
||||
unsigned time;
|
||||
tmr when timerafter(fall_time + delay) :> time;
|
||||
|
||||
// Adjust timing due to support clock stretching without clock drift in the
|
||||
// normal case.
|
||||
|
||||
// If the time is beyond the time it takes simply to wake up and start
|
||||
// executing then the clock needs to be adjusted
|
||||
const int wake_up_ticks = 10;
|
||||
if (time > fall_time + delay + wake_up_ticks) {
|
||||
fall_time = time - delay - wake_up_ticks;
|
||||
}
|
||||
}
|
||||
|
||||
static void high_pulse_drive(
|
||||
port p_i2c,
|
||||
int sdaValue,
|
||||
static const unsigned kbits_per_second,
|
||||
static const unsigned scl_bit_position,
|
||||
static const unsigned sda_bit_position,
|
||||
static const unsigned other_bits_mask,
|
||||
unsigned &fall_time)
|
||||
{
|
||||
const unsigned bit_time = BIT_TIME(kbits_per_second);
|
||||
const unsigned SCL_HIGH = BIT_MASK(scl_bit_position);
|
||||
const unsigned SDA_HIGH = BIT_MASK(sda_bit_position);
|
||||
|
||||
timer tmr;
|
||||
sdaValue = sdaValue ? SDA_HIGH : SDA_LOW;
|
||||
p_i2c <: SCL_LOW | sdaValue | other_bits_mask;
|
||||
tmr when timerafter(fall_time + compute_low_period_ticks(kbits_per_second)) :> void;
|
||||
p_i2c <: SCL_HIGH | sdaValue | other_bits_mask;
|
||||
wait_for_clock_high(p_i2c, scl_bit_position, fall_time, (bit_time * 3) / 4);
|
||||
fall_time = fall_time + bit_time;
|
||||
tmr when timerafter(fall_time) :> void;
|
||||
p_i2c <: SCL_LOW | sdaValue | other_bits_mask;
|
||||
}
|
||||
|
||||
static int high_pulse_sample(
|
||||
port p_i2c,
|
||||
static const unsigned kbits_per_second,
|
||||
static const unsigned scl_bit_position,
|
||||
static const unsigned sda_bit_position,
|
||||
static const unsigned other_bits_mask,
|
||||
unsigned &fall_time)
|
||||
{
|
||||
const unsigned bit_time = BIT_TIME(kbits_per_second);
|
||||
const unsigned SCL_HIGH = BIT_MASK(scl_bit_position);
|
||||
const unsigned SDA_HIGH = BIT_MASK(sda_bit_position);
|
||||
|
||||
timer tmr;
|
||||
|
||||
p_i2c <: SCL_LOW | SDA_HIGH | other_bits_mask;
|
||||
tmr when timerafter(fall_time + compute_low_period_ticks(kbits_per_second)) :> void;
|
||||
p_i2c <: SCL_HIGH | SDA_HIGH | other_bits_mask;
|
||||
wait_for_clock_high(p_i2c, scl_bit_position, fall_time, (bit_time * 3) / 4);
|
||||
|
||||
int sample_value = peek(p_i2c);
|
||||
if (sample_value & SDA_HIGH)
|
||||
sample_value = 1;
|
||||
else
|
||||
sample_value = 0;
|
||||
|
||||
fall_time = fall_time + bit_time;
|
||||
tmr when timerafter(fall_time) :> void;
|
||||
p_i2c <: SCL_LOW | SDA_HIGH | other_bits_mask;
|
||||
|
||||
return sample_value;
|
||||
}
|
||||
|
||||
static void start_bit(
|
||||
port p_i2c,
|
||||
static const unsigned kbits_per_second,
|
||||
static const unsigned scl_bit_position,
|
||||
static const unsigned sda_bit_position,
|
||||
static const unsigned other_bits_mask,
|
||||
unsigned &fall_time,
|
||||
int stopped)
|
||||
{
|
||||
const unsigned bit_time = BIT_TIME(kbits_per_second);
|
||||
const unsigned SCL_HIGH = BIT_MASK(scl_bit_position);
|
||||
const unsigned SDA_HIGH = BIT_MASK(sda_bit_position);
|
||||
|
||||
timer tmr;
|
||||
|
||||
if (!stopped) {
|
||||
fall_time += compute_low_period_ticks(kbits_per_second);
|
||||
tmr when timerafter(fall_time) :> fall_time;
|
||||
p_i2c <: SCL_HIGH | SDA_HIGH | other_bits_mask;
|
||||
wait_for_clock_high(p_i2c, scl_bit_position, fall_time, compute_bus_off_ticks(kbits_per_second));
|
||||
}
|
||||
|
||||
p_i2c <: SCL_HIGH | SDA_LOW | other_bits_mask;
|
||||
delay_ticks(bit_time / 2);
|
||||
p_i2c <: SCL_LOW | SDA_LOW | other_bits_mask;
|
||||
tmr :> fall_time;
|
||||
}
|
||||
|
||||
static void stop_bit(
|
||||
port p_i2c,
|
||||
static const unsigned kbits_per_second,
|
||||
static const unsigned scl_bit_position,
|
||||
static const unsigned sda_bit_position,
|
||||
static const unsigned other_bits_mask,
|
||||
unsigned fall_time)
|
||||
{
|
||||
const unsigned bit_time = BIT_TIME(kbits_per_second);
|
||||
const unsigned SCL_HIGH = BIT_MASK(scl_bit_position);
|
||||
const unsigned SDA_HIGH = BIT_MASK(sda_bit_position);
|
||||
|
||||
timer tmr;
|
||||
|
||||
p_i2c <: SCL_LOW | SDA_LOW | other_bits_mask;
|
||||
tmr when timerafter(fall_time + compute_low_period_ticks(kbits_per_second)) :> void;
|
||||
p_i2c <: SCL_HIGH | SDA_LOW | other_bits_mask;
|
||||
wait_for_clock_high(p_i2c, scl_bit_position, fall_time, bit_time);
|
||||
p_i2c <: SCL_HIGH | SDA_HIGH | other_bits_mask;
|
||||
delay_ticks(compute_bus_off_ticks(kbits_per_second));
|
||||
}
|
||||
|
||||
static int tx8(
|
||||
port p_i2c, unsigned data,
|
||||
static const unsigned kbits_per_second,
|
||||
static const unsigned scl_bit_position,
|
||||
static const unsigned sda_bit_position,
|
||||
static const unsigned other_bits_mask,
|
||||
unsigned &fall_time)
|
||||
{
|
||||
unsigned bit_rev_data = ((unsigned) bitrev(data)) >> 24;
|
||||
for (int i = 8; i != 0; i--) {
|
||||
high_pulse_drive(p_i2c, bit_rev_data & 1, kbits_per_second, scl_bit_position, sda_bit_position, other_bits_mask, fall_time);
|
||||
bit_rev_data >>= 1;
|
||||
}
|
||||
return high_pulse_sample(p_i2c, kbits_per_second, scl_bit_position, sda_bit_position, other_bits_mask, fall_time);
|
||||
}
|
||||
|
||||
[[distributable]]
|
||||
void i2c_master_single_port(
|
||||
server interface i2c_master_if c[n],
|
||||
static const size_t n,
|
||||
port p_i2c,
|
||||
static const unsigned kbits_per_second,
|
||||
static const unsigned scl_bit_position,
|
||||
static const unsigned sda_bit_position,
|
||||
static const unsigned other_bits_mask)
|
||||
{
|
||||
const unsigned bit_time = BIT_TIME(kbits_per_second);
|
||||
const unsigned SCL_HIGH = BIT_MASK(scl_bit_position);
|
||||
const unsigned SDA_HIGH = BIT_MASK(sda_bit_position);
|
||||
|
||||
unsigned last_fall_time = 0;
|
||||
unsigned locked_client = -1;
|
||||
set_port_drive_low(p_i2c);
|
||||
p_i2c <: SCL_HIGH | SDA_HIGH | other_bits_mask;
|
||||
|
||||
while (1) {
|
||||
select {
|
||||
case (size_t i =0; i < n; i++)
|
||||
(n == 1 || (locked_client == -1 || i == locked_client)) =>
|
||||
c[i].read(uint8_t device, uint8_t buf[m], size_t m,
|
||||
int send_stop_bit) -> i2c_res_t result:
|
||||
|
||||
const int stopped = locked_client == -1;
|
||||
unsigned fall_time = last_fall_time;
|
||||
start_bit(p_i2c, kbits_per_second, scl_bit_position, sda_bit_position, other_bits_mask, fall_time, stopped);
|
||||
int ack = tx8(p_i2c, (device << 1) | 1, kbits_per_second, scl_bit_position, sda_bit_position, other_bits_mask, fall_time);
|
||||
if (ack == 0) {
|
||||
for (int j = 0; j < m; j++){
|
||||
unsigned char data = 0;
|
||||
timer tmr;
|
||||
for (int i = 8; i != 0; i--) {
|
||||
int temp = high_pulse_sample(p_i2c, kbits_per_second, scl_bit_position, sda_bit_position, other_bits_mask, fall_time);
|
||||
data = (data << 1) | temp;
|
||||
}
|
||||
buf[j] = data;
|
||||
|
||||
// ACK after every read byte until the final byte then NACK.
|
||||
unsigned sda = SDA_LOW;
|
||||
if (j == m-1) {
|
||||
sda = SDA_HIGH;
|
||||
}
|
||||
|
||||
p_i2c <: SCL_LOW | sda | other_bits_mask;
|
||||
tmr when timerafter(fall_time + compute_low_period_ticks(kbits_per_second)) :> void;
|
||||
p_i2c <: SCL_HIGH | sda | other_bits_mask;
|
||||
wait_for_clock_high(p_i2c, scl_bit_position, fall_time, (bit_time * 3) / 4);
|
||||
fall_time = fall_time + bit_time;
|
||||
tmr when timerafter(fall_time) :> void;
|
||||
|
||||
// Release the data bus
|
||||
p_i2c <: SCL_LOW | SDA_HIGH | other_bits_mask;
|
||||
}
|
||||
}
|
||||
if (send_stop_bit) {
|
||||
stop_bit(p_i2c, kbits_per_second, scl_bit_position, sda_bit_position, other_bits_mask, fall_time);
|
||||
locked_client = -1;
|
||||
} else {
|
||||
locked_client = i;
|
||||
}
|
||||
|
||||
result = (ack == 0) ? I2C_ACK : I2C_NACK;
|
||||
|
||||
// Remember the last fall time to ensure the next start bit is valid
|
||||
last_fall_time = fall_time;
|
||||
break;
|
||||
|
||||
case (size_t i = 0; i < n; i++)
|
||||
(n == 1 || (locked_client == -1 || i == locked_client)) =>
|
||||
c[i].write(uint8_t device, uint8_t buf[n], size_t n,
|
||||
size_t &num_bytes_sent,
|
||||
int send_stop_bit) -> i2c_res_t result:
|
||||
|
||||
const int stopped = locked_client == -1;
|
||||
unsigned fall_time = last_fall_time;
|
||||
start_bit(p_i2c, kbits_per_second, scl_bit_position, sda_bit_position, other_bits_mask, fall_time, stopped);
|
||||
int ack = tx8(p_i2c, device<<1, kbits_per_second, scl_bit_position, sda_bit_position, other_bits_mask, fall_time);
|
||||
int j = 0;
|
||||
for (; j < n; j++) {
|
||||
if (ack != 0)
|
||||
break;
|
||||
|
||||
ack = tx8(p_i2c, buf[j], kbits_per_second, scl_bit_position, sda_bit_position, other_bits_mask, fall_time);
|
||||
}
|
||||
if (send_stop_bit) {
|
||||
stop_bit(p_i2c, kbits_per_second, scl_bit_position, sda_bit_position, other_bits_mask, fall_time);
|
||||
locked_client = -1;
|
||||
} else {
|
||||
locked_client = i;
|
||||
}
|
||||
num_bytes_sent = j;
|
||||
result = (ack == 0) ? I2C_ACK : I2C_NACK;
|
||||
|
||||
// Remember the last fall time to ensure the next start bit is valid
|
||||
last_fall_time = fall_time;
|
||||
break;
|
||||
|
||||
case c[int i].send_stop_bit(void):
|
||||
timer tmr;
|
||||
unsigned fall_time;
|
||||
tmr :> fall_time;
|
||||
stop_bit(p_i2c, kbits_per_second, scl_bit_position, sda_bit_position, other_bits_mask, fall_time);
|
||||
locked_client = -1;
|
||||
break;
|
||||
|
||||
case c[int i].shutdown():
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
265
lib_i2c/lib_i2c/src/i2c_slave.xc
Normal file
265
lib_i2c/lib_i2c/src/i2c_slave.xc
Normal file
@@ -0,0 +1,265 @@
|
||||
// Copyright 2014-2021 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#include <i2c.h>
|
||||
#include <xs1.h>
|
||||
#include <xclib.h>
|
||||
|
||||
enum i2c_slave_state {
|
||||
WAITING_FOR_START_OR_STOP,
|
||||
READING_ADDR,
|
||||
ACK_ADDR,
|
||||
ACK_WAIT_HIGH,
|
||||
ACK_WAIT_LOW,
|
||||
IGNORE_ACK,
|
||||
MASTER_WRITE,
|
||||
MASTER_READ
|
||||
};
|
||||
|
||||
static inline void ensure_setup_time()
|
||||
{
|
||||
// The I2C spec requires a 100ns setup time
|
||||
delay_ticks(10);
|
||||
}
|
||||
|
||||
[[combinable]]
|
||||
void i2c_slave(client i2c_slave_callback_if i,
|
||||
port p_scl, port p_sda,
|
||||
uint8_t device_addr)
|
||||
{
|
||||
enum i2c_slave_state state = WAITING_FOR_START_OR_STOP;
|
||||
enum i2c_slave_state next_state = WAITING_FOR_START_OR_STOP;
|
||||
int sda_val = 0;
|
||||
int scl_val;
|
||||
int bitnum = 0;
|
||||
int data;
|
||||
int rw = 0;
|
||||
int stop_bit_check = 0;
|
||||
int ignore_stop_bit = 1;
|
||||
p_sda when pinseq(1) :> void;
|
||||
while (1) {
|
||||
select {
|
||||
case i.shutdown():
|
||||
return;
|
||||
case state != WAITING_FOR_START_OR_STOP => p_scl when pinseq(scl_val) :> void:
|
||||
switch (state) {
|
||||
case READING_ADDR:
|
||||
// If clock has gone low, wait for it to go high before doing anything
|
||||
if (scl_val == 0) {
|
||||
scl_val = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
int bit;
|
||||
p_sda :> bit;
|
||||
if (bitnum < 7) {
|
||||
data = (data << 1) | bit;
|
||||
bitnum++;
|
||||
scl_val = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// We have gathered the whole device address sent by the master
|
||||
if (data != device_addr) {
|
||||
state = IGNORE_ACK;
|
||||
} else {
|
||||
state = ACK_ADDR;
|
||||
rw = bit;
|
||||
}
|
||||
scl_val = 0;
|
||||
break;
|
||||
|
||||
case IGNORE_ACK:
|
||||
// This request is not for us, ignore the ACK
|
||||
next_state = WAITING_FOR_START_OR_STOP;
|
||||
scl_val = 1;
|
||||
state = ACK_WAIT_HIGH;
|
||||
break;
|
||||
|
||||
case ACK_ADDR:
|
||||
// Stretch clock (hold low) while application code is called
|
||||
p_scl <: 0;
|
||||
|
||||
// Callback to the application to determine whether to ACK
|
||||
// or NACK the address.
|
||||
int ack;
|
||||
if (rw) {
|
||||
ack = i.ack_read_request();
|
||||
} else {
|
||||
ack = i.ack_write_request();
|
||||
}
|
||||
|
||||
ignore_stop_bit = 0;
|
||||
if (ack == I2C_SLAVE_NACK) {
|
||||
// Release the data line so that it is pulled high
|
||||
p_sda :> void;
|
||||
next_state = WAITING_FOR_START_OR_STOP;
|
||||
} else {
|
||||
// Drive the ACK low
|
||||
p_sda <: 0;
|
||||
if (rw) {
|
||||
next_state = MASTER_READ;
|
||||
} else {
|
||||
next_state = MASTER_WRITE;
|
||||
}
|
||||
}
|
||||
scl_val = 1;
|
||||
state = ACK_WAIT_HIGH;
|
||||
|
||||
ensure_setup_time();
|
||||
|
||||
// Release the clock
|
||||
p_scl :> void;
|
||||
break;
|
||||
|
||||
case ACK_WAIT_HIGH:
|
||||
// Rising edge of clock, hold ack to the falling edge
|
||||
state = ACK_WAIT_LOW;
|
||||
scl_val = 0;
|
||||
break;
|
||||
|
||||
case ACK_WAIT_LOW:
|
||||
// ACK done, release the data line
|
||||
p_sda :> void;
|
||||
if (next_state == MASTER_READ) {
|
||||
scl_val = 0;
|
||||
} else if (next_state == MASTER_WRITE) {
|
||||
data = 0;
|
||||
scl_val = 1;
|
||||
} else { // WAITING_FOR_START_OR_STOP
|
||||
sda_val = 0;
|
||||
}
|
||||
state = next_state;
|
||||
bitnum = 0;
|
||||
break;
|
||||
|
||||
case MASTER_READ:
|
||||
if (scl_val == 1) {
|
||||
// Rising edge
|
||||
if (bitnum == 8) {
|
||||
// Sample ack from master
|
||||
int bit;
|
||||
p_sda :> bit;
|
||||
if (bit) {
|
||||
// Master has NACKed so the transaction is finished
|
||||
state = WAITING_FOR_START_OR_STOP;
|
||||
sda_val = 0;
|
||||
} else {
|
||||
bitnum = 0;
|
||||
scl_val = 0;
|
||||
}
|
||||
} else {
|
||||
// Wait for next falling edge
|
||||
scl_val = 0;
|
||||
bitnum++;
|
||||
}
|
||||
} else {
|
||||
// Falling edge, drive data
|
||||
if (bitnum < 8) {
|
||||
if (bitnum == 0) {
|
||||
// Stretch clock (hold low) while application code is called
|
||||
p_scl <: 0;
|
||||
data = i.master_requires_data();
|
||||
// Data is transmitted MSB first
|
||||
data = bitrev(data) >> 24;
|
||||
|
||||
// Send first bit of data
|
||||
p_sda <: data & 0x1;
|
||||
|
||||
ensure_setup_time();
|
||||
|
||||
// Release the clock
|
||||
p_scl :> void;
|
||||
} else {
|
||||
p_sda <: data & 0x1;
|
||||
}
|
||||
data >>= 1;
|
||||
} else {
|
||||
// Release the bus for the master to be able to ACK/NACK
|
||||
p_sda :> void;
|
||||
}
|
||||
scl_val = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case MASTER_WRITE:
|
||||
if (scl_val == 1) {
|
||||
// Rising edge
|
||||
int bit;
|
||||
p_sda :> bit;
|
||||
data = (data << 1) | (bit & 0x1);
|
||||
if (bitnum == 0) {
|
||||
if (bit) {
|
||||
sda_val = 0;
|
||||
} else {
|
||||
sda_val = 1;
|
||||
}
|
||||
// First bit could be a start or stop bit
|
||||
stop_bit_check = 1;
|
||||
}
|
||||
scl_val = 0;
|
||||
bitnum++;
|
||||
} else {
|
||||
// Falling edge
|
||||
|
||||
// Not a start or stop bit
|
||||
stop_bit_check = 0;
|
||||
|
||||
if (bitnum == 8) {
|
||||
// Stretch clock (hold low) while application code is called
|
||||
p_scl <: 0;
|
||||
int ack = i.master_sent_data(data);
|
||||
if (ack == I2C_SLAVE_NACK) {
|
||||
// Release the data bus so it is pulled high to signal NACK
|
||||
p_sda :> void;
|
||||
} else {
|
||||
// Drive data bus low to signal ACK
|
||||
p_sda <: 0;
|
||||
}
|
||||
state = ACK_WAIT_HIGH;
|
||||
|
||||
ensure_setup_time();
|
||||
|
||||
// Release the clock
|
||||
p_scl :> void;
|
||||
}
|
||||
scl_val = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case (state == WAITING_FOR_START_OR_STOP) || stop_bit_check =>
|
||||
p_sda when pinseq(sda_val) :> void:
|
||||
if (sda_val == 1) {
|
||||
// SDA has transitioned from low to high, if SCL is high
|
||||
// then it is a stop bit.
|
||||
int val;
|
||||
p_scl :> val;
|
||||
if (val) {
|
||||
if (!ignore_stop_bit) {
|
||||
i.stop_bit();
|
||||
}
|
||||
state = WAITING_FOR_START_OR_STOP;
|
||||
ignore_stop_bit = 1;
|
||||
stop_bit_check = 0;
|
||||
}
|
||||
sda_val = 0;
|
||||
} else {
|
||||
// SDA has transitioned from high to low, if SCL is high
|
||||
// then it is a start bit.
|
||||
int val;
|
||||
p_scl :> val;
|
||||
if (val == 1) {
|
||||
state = READING_ADDR;
|
||||
bitnum = 0;
|
||||
data = 0;
|
||||
scl_val = 0;
|
||||
stop_bit_check = 0;
|
||||
} else {
|
||||
sda_val = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user