📄 i2c.sgml
字号:
call, making them suitable for many I/O operations. The alternative
transaction-oriented functions provide greater control when
appropriate, for example if a repeated start is necessary for a
bi-directional data transfer.
</para>
<para>
With the exception of
<function>cyg_i2c_transaction_begin_nb</function> all the functions
will block until completion. The tx routines will return 0 if the
specified device does not respond to its address, or the number of
bytes actually transferred. This may be less than the number requested
if the device sends an early nack, for example because it has run out
of buffer space. The rx routines will return 0 or the number of bytes
received. Usually this will be the same as the
<varname>count</varname> parameter. A slave device has no way of
indicating to the master that no more data is available, so the rx
operation cannot complete early.
</para>
<para>
I2C operations should always be performed at thread-level or during
system initialization, and not inside an ISR or DSR. This greatly
simplifies locking. Also a typical ISR or DSR should not perform a
blocking operation such as an I2C transfer.
</para>
</refsect1>
<refsect1 id="i2c-api-transfer"><title>Simple Transfers</title>
<para>
<function>cyg_i2c_tx</function> and <function>cyg_i2c_rx</function>
can be used for simple data transfers. They both go through the
following steps: lock the bus, generate the start condition, send the
device address and the direction bit, either send or receive the data,
generate the stop condition, and unlock the bus. At the end of a
transfer the bus is back in its idle state, ready for the next
transfer.
</para>
<para>
<function>cyg_i2c_tx</function> returns the number of bytes actually
transmitted. This may be 0 if the device does not respond when its
address is sent out. It may be less than the number of bytes requested
if the device generates an early nack, typically because it has run
out of buffer space.
</para>
<para>
<function>cyg_i2c_rx</function> returns 0 if the device does not
respond when its address is sent out, or the number of bytes actually
received. Usually this will be the number of bytes requested because
an I2C slave device has no way of aborting an rx operation early.
</para>
</refsect1>
<refsect1 id="i2c-api-transaction"><title>Transactions</title>
<para>
To allow multiple threads to access devices on the I2C some locking is
required. This is encapsulated inside transactions. The
<function>cyg_i2c_tx</function> and <function>cyg_i2c_rx</function>
functions implicitly use such transactions, but the functionality is
also available directly to application code. Amongst other things
transactions can be used for more complicated interactions with I2C
devices, in particular ones involving repeated starts.
</para>
<para>
<function>cyg_i2c_transaction_begin</function> must be used at the
start of a transaction. This performs thread-level locking on the bus,
blocking if it is currently in use by another thread.
</para>
<para>
<function>cyg_i2c_transaction_begin_nb</function> is a non-blocking
variant, useful for threads which cannot afford to block for an
indefinite period. If the bus is currently locked the function returns
false immediately. If the bus is not locked then it acts as
<filename>cyg_i2c_transaction_begin</filename> and returns true.
</para>
<para>
Once the bus has been locked it is possible to perform one or more
data transfers by calling
<function>cyg_i2c_transaction_tx</function>,
<function>cyg_i2c_transaction_rx</function> and
<function>cyg_i2c_transaction_stop</function>. Code should ensure that
a stop condition has been generated by the end of a transaction.
</para>
<para>
Once the transaction is complete
<function>cyg_i2c_transaction_end</function> should be called. This
unlocks the bus, allowing other threads to perform I2C I/O to devices
on the same bus.
</para>
<para>
As an example consider reading the registers in an FS6377 programmable
clock generator. The first step is to write a byte 0 to the device,
setting the current register to 0. Then a repeated start condition
should be generated and it is possible to read the 16 byte-wide
registers, starting with the current one. Typical code for this might
look like:
</para>
<programlisting width=72>
cyg_uint8 tx_data[1];
cyg_uint8 rx_data[16];
cyg_i2c_transaction_begin(&hal_alaia_i2c_fs6377);
tx_data[0] = 0x00;
cyg_i2c_transaction_tx(&hal_alaia_i2c_fs6377,
true, tx_data, 1, false);
cyg_i2c_transaction_rx(&hal_alaia_i2c_fs6377,
true, rx_data, 16, true, true);
cyg_i2c_transaction_end(&hal_alaia_i2c_fs6377);
</programlisting>
<para>
Here <varname>hal_alaia_i2c_fs6377</varname> is a
<structname>cyg_i2c_device</structname> structure provided by the
platform HAL. A transaction is begun, locking the bus. Then there is a
transmit for a single byte. This transmit involves generating a start
condition and sending the address and direction bit, but not a stop
condition. Next there is a receive for 16 bytes. This also involves a
start condition, which the device will interpret as a repeated start
because it has not yet seen a stop. The start condition will be
followed by the address and direction bit, and then the device will
start transmitting the register contents. Once all 16 bytes have been
received the rx routine will send a nack rather than an ack, halting
the transfer, and then a stop condition is generated. Finally the
transaction is ended, unlocking the bus.
</para>
<para>
The arguments to <function>cyg_i2c_transaction_tx</function> are as
follows:
</para>
<variablelist>
<varlistentry>
<term><type>const cyg_i2c_device*</type> <varname>device</varname></term>
<listitem><para>
This identifies the I2C device that should be used.
</para></listitem>
</varlistentry>
<varlistentry>
<term><type>cyg_bool</type> <varname>send_start</varname></term>
<listitem><para>
If true, generate a start condition and send the address and direction
bit. If false, skip those steps and go straight to transmitting the
actual data. The latter can be useful if the data to be transmitted is
spread over several buffers. The first tx call will involve generating
the start condition but subsequent tx calls can skip this and just
continue from the previous one.
</para><para>
<varname>send_start</varname> must be true if the tx call is the first
operation in a transaction, or if the previous call was an rx or stop.
</para></listitem>
</varlistentry>
<varlistentry>
<term><type>const cyg_uint8*</type> <varname>tx_data</varname></term>
<term><type>cyg_uint32</type> <varname>count</varname></term>
<listitem><para>
These arguments specify the data to be transmitted to the device.
</para></listitem>
</varlistentry>
<varlistentry>
<term><type>cyg_bool</type> <varname>send_stop</varname></term>
<listitem><para>
If true, generate a stop condition at the end of the transmit. Usually
this is done only if the transmit is the last operation in a
transaction.
</para></listitem>
</varlistentry>
</variablelist>
<para>
The arguments to <function>cyg_i2c_transaction_rx</function> are as
follows:
</para>
<variablelist>
<varlistentry>
<term><type>const cyg_i2c_device*</type> <varname>device</varname></term>
<listitem><para>
This identifies the I2C device that should be used.
</para></listitem>
</varlistentry>
<varlistentry>
<term><type>cyg_bool</type> <varname>send_start</varname></term>
<listitem><para>
If true, generate a start condition and send the address and direction
bit. If false, skip those steps and go straight to receiving the
actual data. The latter can be useful if the incoming data should be
spread over several buffers. The first rx call will involve generating
the start condition but subsequent rx calls can skip this and just
continue from the previous one. Another use is for devices which can
send variable length data, consisting of an initial length and then
the actual data. The first rx will involve generating the start
condition and reading the length, a subsequent rx will then just read
the data.
</para><para>
<varname>send_start</varname> must be true if the rx call is the first
operation in a transaction, if the previous call was a tx or stop, or
if the previous call was an an rx and the <varname>send_nack</varname>
flag was set.
</para></listitem>
</varlistentry>
<varlistentry>
<term><type>cyg_uint8*</type> <varname>rx_data</varname></term>
<term><type>cyg_uint32</type> <varname>count</varname></term>
<listitem><para>
These arguments specify how much data should be received and where it
should be placed.
</para></listitem>
</varlistentry>
<varlistentry>
<term><type>cyg_bool</type> <varname>send_nack</varname></term>
<listitem><para>
If true generate a nack instead of an ack for the last byte received.
This causes the slave to end its transmit. The next operation should
either involve a repeated start or a stop.
<varname>send_nack</varname> should be set to false only if
<varname>send_stop</varname> is also false, the next operation will be
another rx, and that rx does not specify <varname>send_start</varname>.
</para></listitem>
</varlistentry>
<varlistentry>
<term><type>cyg_bool</type> <varname>send_stop</varname></term>
<listitem><para>
If true, generate a stop condition at the end of the transmit. Usually
this is done only if the transmit is the last operation in a
transaction.
</para></listitem>
</varlistentry>
</variablelist>
<para>
The final transaction-oriented function is
<function>cyg_i2c_transaction_stop</function>. This just generates a
stop condition. It should be used if the previous operation was a tx
or rx that, for some reason, did not set the
<varname>send_stop</varname> flag. A stop condition must be generated
before the transaction is ended.
</para>
</refsect1>
<refsect1 id="i2c-api-initialization"><title>Initialization</title>
<para>
The generic package <varname>CYGPKG_IO_I2C</varname> arranges for all
I2C bus devices to be initialized via a single prioritized C++ static
constructor. Usually this constructor will run early on during system
startup, before any application code. The default priority is
<literal>CYG_INIT_DRIVERS</literal>, but this can be changed via the
configuration option <varname>CYGNUM_I2C_INIT_PRIORITY</varname>.
Other code should not try to access any of the I2C devices until after
the buses have been initialized.
</para>
</refsect1>
</refentry>
<refentry id="i2c-porting">
<refmeta>
<refentrytitle>Porting to New Hardware</refentrytitle>
</refmeta>
<refnamediv>
<refname>Porting</refname>
<refpurpose>Adding I2C support to new hardware</refpurpose>
</refnamediv>
<refsect1 id="i2c-porting-description"><title>Description</title>
<para>
Adding I2C support to an eCos port involves a number of steps. The
generic I2C package <varname>CYGPKG_IO_I2C</varname> should be
included in the appropriate <database>ecos.db</database> target entry
or entries. Next <structname>cyg_i2c_device</structname> structures
should be provided for every device on the bus. Usually this is the
responsibility of the platform HAL. In the case of development boards
where the I2C SDA and SCL lines are accessible via an expansion
connector, more devices may have been added and it will be the
application's responsibility to provide the structures. Finally
there is a need for one or more <structname>cyg_i2c_bus</structname>
structures. Amongst other things these structures provide functions
for actually driving the bus. If the processor has dedicated I2C
hardware then this structure will usually be provided by a device
driver package. If the bus is implemented by bit-banging then the bus
structure will usually be provided by the platform HAL.
</para>
</refsect1>
<refsect1 id="i2c-porting-devices"><title>Adding a Device</title>
<para>
The eCos I2C API works in terms of
<structname>cyg_i2c_device</structname> structures, and these provide
the information needed to access the hardware. A
<structname>cyg_i2c_device</structname> structure contains the
following fields:
</para>
<variablelist>
<varlistentry>
<term><type>cyg_i2c_bus*</type> <varname>i2c_bus</varname></term>
<listitem><para>
This specifies the bus which the slave device is connected to. Most
boards will only have a single I2C bus, but multiple buses are possible.
</para></listitem>
</varlistentry>
<varlistentry>
<term><type>cyg_uint16</type> <varname>i2c_address</varname></term>
<listitem><para>
For most devices this will be the 7-bit I2C address the device will
respond to. There is room for future expansion, for example to support
10-bit addresses.
</para></listitem>
</varlistentry>
<varlistentry>
<term><type>cyg_uint16</type> <varname>i2c_flags</varname></term>
<listitem><para>
This field is not used at present. It exists for future expansion, for
example to allow for fast mode or high-speed mode, and incidentally
pads the structure to a 32-bit boundary.
</para></listitem>
</varlistentry>
<varlistentry>
<term><type>cyg_uint32</type> <varname>i2c_delay</varname></term>
<listitem><para>
This holds the clock period which should be used when interacting with
the device, in nanoseconds. Usually this will be 10000 ns,
corresponding to a 100KHz clock, and the header <filename
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -