Mercurial > ecos
changeset 2512:be7755f32fd0
Add Generic ADC support. See Changelogs for details.
| author | nickg |
|---|---|
| date | Fri, 08 Aug 2008 10:47:04 +0000 |
| parents | 1f18f909a18e |
| children | 9454b98df9aa |
| files | doc/ChangeLog doc/sgml/doclist packages/ChangeLog packages/NEWS packages/ecos.db packages/io/adc/current/ChangeLog packages/io/adc/current/cdl/io_adc.cdl packages/io/adc/current/doc/adc.sgml packages/io/adc/current/include/adc.h packages/io/adc/current/src/adc.c |
| diffstat | 10 files changed, 1467 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,7 @@ +2008-08-08 Nick Garnett <nickg@ecoscentric.com> + + * sgml/doclist: Added ADC documentation. + 2008-07-12 Andrew Lunn <andrew.lunn@ascom.ch> * sgml/doclist: added USB Serial documentation
--- a/doc/sgml/doclist +++ b/doc/sgml/doclist @@ -9,6 +9,7 @@ io/flash/current/doc/flash.sgml io/spi/current/doc/spi.sgml io/i2c/current/doc/i2c.sgml io/can/current/doc/can.sgml +io/adc/current/doc/adc.sgml compat/posix/current/doc/posix.sgml compat/uitron/current/doc/uitron.sgml net/common/current/doc/tcpip.sgml
--- a/packages/ChangeLog +++ b/packages/ChangeLog @@ -1,3 +1,8 @@ +2008-08-08 Nick Garnett <nickg@ecoscentric.com> + + * NEWS: + * ecos.db: Add generic ADC package. + 2008-07-08 Uwe Kindler <uwe_kindler@web.de> * NEWS:
--- a/packages/NEWS +++ b/packages/NEWS @@ -1,3 +1,4 @@ +* Generic ADC support contributed by eCosCentric Ltd. * Port to Embedded Artists LPC2468 OEM board by Uwe Kindler. * USB serial class driver by Frank Pagliughi. * FR30 arch, mb91301 variant and skmb91302 platform by Lars Poeschel.
--- a/packages/ecos.db +++ b/packages/ecos.db @@ -2276,6 +2276,15 @@ package CYGPKG_DEVS_I2C_MCF52xx { device provided by the MCF52xx coldfire processor family." } +package CYGPKG_IO_ADC { + alias { "Genric ADC support" adc io_adc } + directory io/adc + script io_adc.cdl + description " + This package contains drivers for basic I/O services on + adc devices." +} + package CYGPKG_KERNEL { alias { "eCos kernel" kernel } directory kernel
new file mode 100644 --- /dev/null +++ b/packages/io/adc/current/ChangeLog @@ -0,0 +1,39 @@ +2008-08-08 Nick Garnett <nickg@ecoscentric.com> + + * cdl/io_adc.cdl: + * doc/adc.sgml: + * include/adc.h: + * src/adc.c: + New package -- ADC generic driver support. + +##========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008 eCosCentric Limited +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +##==========================================================================
new file mode 100644 --- /dev/null +++ b/packages/io/adc/current/cdl/io_adc.cdl @@ -0,0 +1,92 @@ +##========================================================================== +## +## io_adc.cdl +## +## Generic ADC driver layer +## +##========================================================================== +#####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2008 eCosCentric Limited +## +## eCos is free software; you can redistribute it and/or modify it under +## the terms of the GNU General Public License as published by the Free +## Software Foundation; either version 2 or (at your option) any later version. +## +## eCos is distributed in the hope that it will be useful, but WITHOUT ANY +## WARRANTY; without even the implied warranty of MERCHANTABILITY or +## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +## for more details. +## +## You should have received a copy of the GNU General Public License along +## with eCos; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +## +## As a special exception, if other files instantiate templates or use macros +## or inline functions from this file, or you compile this file and link it +## with other works to produce a work based on this file, this file does not +## by itself cause the resulting work to be covered by the GNU General Public +## License. However the source code for this file must still be made available +## in accordance with section (3) of the GNU General Public License. +## +## This exception does not invalidate any other reasons why a work based on +## this file might be covered by the GNU General Public License. +## ------------------------------------------- +#####ECOSGPLCOPYRIGHTEND#### +##========================================================================== +#######DESCRIPTIONBEGIN#### +## +## Author(s): nickg +## Date: 2008-03-31 +## Description: Implements generic layer of ADC drivers. +## +######DESCRIPTIONEND#### +## +##========================================================================*/ + + +cdl_package CYGPKG_IO_ADC { + display "ADC device drivers" + parent CYGPKG_IO + active_if CYGPKG_IO + requires CYGPKG_ERROR + include_dir cyg/io + description " + This option enables drivers for basic I/O services on + ADC devices." + + compile -library=libextras.a adc.c + + cdl_component CYGPKG_IO_ADC_DEVICES { + display "Hardware ADC device drivers" + flavor bool + default_value 0 + description " + This option enables the hardware device drivers + for the current platform." + } + + cdl_option CYGNUM_IO_ADC_SAMPLE_SIZE { + display "Sample size" + flavor data + default_value 16 + legal_values 0 to 32 + + description "This option defines the sample size for the ADC devices. + Given in bits, it will be rounded up to 8, 16 or 32 to define + the cyg_adc_sample_t type." + } + + cdl_option CYGPKG_IO_ADC_SELECT_SUPPORT { + display "Enable ADC device select support" + flavor bool + active_if CYGPKG_IO_FILEIO + requires { CYGFUN_IO_FILEIO_SELECT == 1 } + default_value 1 + description " + This option enables support for the select() API function on all + ADC devices." + } + +}
new file mode 100644 --- /dev/null +++ b/packages/io/adc/current/doc/adc.sgml @@ -0,0 +1,692 @@ +<!-- DOCTYPE part PUBLIC "-//OASIS//DTD DocBook V3.1//EN" --> + +<!-- {{{ Banner --> + +<!-- =============================================================== --> +<!-- --> +<!-- adc.sgml --> +<!-- --> +<!-- Generic ADC documentation. --> +<!-- --> +<!-- =============================================================== --> +<!-- ####COPYRIGHTBEGIN#### --> +<!-- --> +<!-- =============================================================== --> +<!-- Copyright (C) 2008 eCosCentric Limited --> +<!-- This material may be distributed only subject to the terms --> +<!-- and conditions set forth in the Open Publication License, v1.0 --> +<!-- or later (the latest version is presently available at --> +<!-- http://www.opencontent.org/openpub/) --> +<!-- Distribution of the work or derivative of the work in any --> +<!-- standard (paper) book form is prohibited unless prior --> +<!-- permission obtained from the copyright holder --> +<!-- =============================================================== --> +<!-- --> +<!-- ####COPYRIGHTEND#### --> +<!-- =============================================================== --> +<!-- #####DESCRIPTIONBEGIN#### --> +<!-- --> +<!-- Author(s): nickg --> +<!-- Date: 2008/05/12 --> +<!-- --> +<!-- ####DESCRIPTIONEND#### --> +<!-- =============================================================== --> + +<!-- }}} --> + +<part id="io-adc"><title>ADC Support</title> + +<refentry id="adc"> + <refmeta> + <refentrytitle>Overview</refentrytitle> + </refmeta> + <refnamediv> + <refname>Overview</refname> + <refpurpose>eCos Support for Analog/Digital Converters</refpurpose> + </refnamediv> + + <refsect1 id="adc-intro"><title>Introduction</title> + +<para> +ADC support in eCos is based around the standard character device +interface. Hence all device IO function, or file IO functions may be +used to access ADC devices. +</para> + +<para> +ADC devices are presented as read-only serial channels that generate +samples at a given rate. The size of each sample is hardware specific +and is defined by the <type>cyg_adc_sample_t</type> type. The sample +rate may be set at runtime by the application. Most ADC devices +support several channels which are all sampled at the same +rate. Therefore setting the rate for one channel will usually change +the rate for all channels on that device. +</para> + + +</refsect1> + +<refsect1 id="adc-examples"><title>Examples</title> + +<para> +The use of the ADC devices is best shown by example. The following is +a simple example of using the eCos device interface to access the ADC: +</para> + +<programlisting width=72> + + int res; + cyg_io_handle_t handle; + + // Get a handle for ADC device 0 channel 0 + res = cyg_io_lookup( "/dev/adc00", &handle ); + + if( res != ENOERR ) + handle_error(err); + + for(;;) + { + cyg_adc_sample_t sample; + cyg_uint32 len = sizeof(sample); + + // read a sample from the channel + res = cyg_io_read( handle, &sample, &len ); + + if( res != ENOERR ) + handle_error(err); + + use_sample( sample ); + } + +</programlisting> + +<para> +In this example, the required channel is looked up and a handle on it +acquired. Conventionally ADC devices are named "/dev/adcXY" where X is +the device number and Y the channel within that device. Following +this, samples are read from the device sequentially. +</para> + +<para> +ADC devices may also be accessed using FILEIO operations. These allow +more sophisticated usage. The following example shows +<function>select()</function> being used to gather samples from several devices. +</para> + + +<programlisting width=72> + + int fd1, fd2; + + // open channels, non-blocking + fd1 = open( "/dev/adc01", O_RDONLY|O_NONBLOCK ); + fd2 = open( "/dev/adc02", O_RDONLY|O_NONBLOCK ); + + if( fd1 < 0 || fd2 < 0 ) + handle_error( errno ); + + for(;;) + { + fd_set rd; + int maxfd = 0; + int err; + cyg_adc_sample_t samples[128]; + int len; + + FD_ZERO( &rd ); + + FD_SET( fd1, &rd ); + FD_SET( fd2, &rd ); + maxfd = max(fd1,fd2); + + // select on available data on each channel. + err = select( maxfd+1, &rd, NULL, NULL, NULL ); + + if( err < 0 ) + handle_error(errno); + + // If channel 1 has data, handle it + if( FD_ISSET( fd1, &rd ) ) + { + len = read( fd1, &samples, sizeof(samples) ); + + if( len > 0 ) + handle_samples_chan1( &samples, len/sizeof(sample[0]) ); + } + + // If channel 2 has data, handle it + if( FD_ISSET( fd2, &rd ) ) + { + len = read( fd2, &samples, sizeof(samples) ); + + if( len > 0 ) + handle_samples_chan2( &samples, len/sizeof(sample[0]) ); + } + + } + +</programlisting> + +<para> +This test uses FILEIO operations to access ADC channels. It starts by +opening two channels for reading only and with blocking disabled. It +then falls into a loop using select to wake up whenever either channel +has samples available. +</para> + +</refsect1> + + +<refsect1 id="adc-details"><title>Details</title> + +<para> +As indicated, the main interface to ADC devices is via the standard +character device interface. However, there are a number of aspects +that are ADC specific. +</para> + +<refsect2 id="adc-details-sample-t"><title>Sample Type</title> + +<para> +Samples can vary in size depending on the underlying hardware and is +often a non-standard number of bits. The actual number of bits is +defined by the hardware driver package, and the generic ADC package +uses this to define a type <type>cyg_adc_sample_t</type> which can +contain at least the required number of bits. All reads from an ADC +channel should be expressed in multiples of this type, and actual +bytes read will also always be a multiple. +</para> + +</refsect2> + +<refsect2 id="adc-details-rate"><title>Sample Rate</title> + +<para> +The sample rate of an ADC device can be varied by calling a +<function>set_config</function> function, either at the device IO API +level or at the FILEIO level. The following two functions show how +this is done at each: +</para> +<programlisting width=72> + +int set_rate_io( cyg_io_handle_t handle, int rate ) +{ + cyg_adc_info_t info; + cyg_uint32 len = sizeof(info); + + info.rate = rate; + + return cyg_io_set_config( handle, + CYG_IO_SET_CONFIG_ADC_RATE, + &info, + &len); +} + +int set_rate_fileio( int fd, int rate ) +{ + cyg_adc_info_t info; + + info.rate = rate; + + return cyg_fs_fsetinfo( fd, + CYG_IO_SET_CONFIG_ADC_RATE, + &info, + sizeof(info) ); +} +</programlisting> + +</refsect2> + +<refsect2 id="adc-details-enable"><title>Enabling a Channel</title> + +<para> +Channels are initialized in a disabled state and generate no +samples. When a channel is first looked up or opened, then it is +automatically enabled and samples start to accumulate. A channel may +then be disable or re-enabled via a <function>set_config</function> +function: +</para> + +<programlisting width=72> +int disable_io( cyg_io_handle_t handle ) +{ + return cyg_io_set_config( handle, + CYG_IO_SET_CONFIG_ADC_DISABLE, + NULL, + NULL); +} + +int enable_io( cyg_io_handle_t handle ) +{ + return cyg_io_set_config( handle, + CYG_IO_SET_CONFIG_ADC_DISABLE, + NULL, + NULL); +} +</programlisting> + +</refsect2> + +</refsect1> + + +<refsect1 id="adc-config"><title>Configuration</title> + +<para> +The ADC package defines a number of generic configuration options that +apply to all ADC implementations: +</para> + +<variablelist> + +<varlistentry><term>cdl_component CYGPKG_IO_ADC_DEVICES</term> +<listitem><para> +This option enables the hardware device drivers for the current +platform. ADC devices will only be enabled if this option is itself +enabled. +</para></listitem> +</varlistentry> + +<varlistentry><term>cdl_option CYGNUM_IO_ADC_SAMPLE_SIZE</term> +<listitem><para> +This option defines the sample size for the ADC devices. Given in +bits, it will be rounded up to 8, 16 or 32 to define the +<type>cyg_adc_sample_t</type> type. This option is usually set by the +hardware device driver. +</para></listitem> +</varlistentry> + +<varlistentry><term>cdl_option CYGPKG_IO_ADC_SELECT_SUPPORT</term> +<listitem><para> + +This option enables support for the <function>select()</function> API +function on all ADC devices. This option can be disabled if the +<function>select()</function> is not used, saving some code and data +space. +</para></listitem> +</varlistentry> + +</variablelist> + +<para> +In addition to the generic options, each hardware device driver +defines some parameters for each device and channel. The exact names +of the following option depends on the hardware device driver, but +options of this form should be available in all drivers. +</para> + + +<variablelist> + +<varlistentry><term>cdl_option CYGDAT_IO_ADC_EXAMPLE_CHANNELN_NAME</term> +<listitem><para> +This option specifies the name of the device for an ADC +channel. Channel names should be of the form "/dev/adcXY" where X is +the device number and Y the channel within that device. +</para></listitem> +</varlistentry> + +<varlistentry><term>cdl_option CYGNUM_IO_ADC_EXAMPLE_CHANNELN_BUFSIZE</term> +<listitem><para> +This option specifies the buffer size for an ADC channel. The value is +expressed in multiples of <type>cyg_adc_sample_t</type> rather than +bytes. The default value is 128. +</para></listitem> +</varlistentry> + +<varlistentry><term>cdl_option CYGNUM_IO_ADC_EXAMPLE_DEFAULT_RATE</term> +<listitem><para> +This option defines the initial default sample rate for all +channels. The hardware driver may place constraints on the range of +values this option may take. +</para></listitem> +</varlistentry> + +</variablelist> + +</refsect1> + +</refentry> + +<refentry id="adcdev"> + <refmeta> + <refentrytitle>ADC Device Drivers</refentrytitle> + </refmeta> + <refnamediv> + <refname>Overview</refname> + <refpurpose>ADC Device Drivers</refpurpose> + </refnamediv> + +<refsect1 id="adcdev-intro"><title>Introduction</title> + +<para> +This section describes how to write an ADC hardware device. While +users of ADC devices do not need to read it, it may provide added +insight into how the devices work. +</para> + +</refsect1> + + +<refsect1 id="adcdev-data"><title>Data Structures</title> + +<para> +An ADC hardware driver is represented by a number of data +structures. These are generic <literal>device</literal> and +<literal>channel</literal> data structures, a driver private device +data structure, a generic character device table entry and a driver +function table. Most of these structures are instantiated using +macros, which will be described here. +</para> + +<para> +The data structure instantiation for a typical single device, four +channel ADC would look like this: +</para> + +<programlisting width=72> +//========================================================================== +// Instantiate data structures + +// ------------------------------------------------------------------------- +// Driver functions: + +CYG_ADC_FUNCTIONS( example_adc_funs, + example_adc_enable, + example_adc_disable, + example_adc_set_rate ); + +// ------------------------------------------------------------------------- +// Device instance: + +static example_adc_info example_adc_info0 = +{ + .base = CYGARC_HAL_EXAMPLE_ADC_BASE, + .vector = CYGNUM_HAL_INTERRUPT_ADC +}; + +CYG_ADC_DEVICE( example_adc_device, + &example_adc_funs, + &example_adc_info0, + CYGNUM_IO_ADC_EXAMPLE_DEFAULT_RATE ); + +// ------------------------------------------------------------------------- +// Channel instances: + +#define EXAMPLE_ADC_CHANNEL( __chan ) \ +CYG_ADC_CHANNEL( example_adc_channel##__chan, \ + __chan, \ + CYGNUM_IO_ADC_EXAMPLE_CHANNEL##__chan##_BUFSIZE, \ + &example_adc_device ); \ + \ +DEVTAB_ENTRY( example_adc_channel##__chan##_device, \ + CYGDAT_IO_ADC_EXAMPLE_CHANNEL##__chan##_NAME, \ + 0, \ + &cyg_io_adc_devio, \ + example_adc_init, \ + example_adc_lookup, \ + &example_adc_channel##__chan ); + +EXAMPLE_ADC_CHANNEL( 0 ); +EXAMPLE_ADC_CHANNEL( 1 ); +EXAMPLE_ADC_CHANNEL( 2 ); +EXAMPLE_ADC_CHANNEL( 3 ); +</programlisting> + +<para> +The macro <literal>CYG_ADC_FUNCTIONS()</literal> instantiates a +function table called <varname>example_adc_funs</varname> and +populates it with the ADC driver functions (see later for details). +</para> + +<para> +Then an instance of the driver private device data structure is +instantiated. In addition to the device base address and interrupt +vector shown here, this stucture should contain the interrupt object +and handle for attaching to the vector. It may also contain any other +variables needed to manage the device. +</para> + +<para> +The macro <literal>CYG_ADC_DEVICE()</literal> instantiates a +<structname>cyg_adc_device</structname> structure, named +<varname>example_adc_device</varname> which will contain pointers to +the function table and private data structure. The initial sample rate +is also supplied here. +</para> + +<para> +For each channel, an ADC channel structure and a device table entry +must be created. The macro <literal>EXAMPLE_ADC_CHANNEL()</literal> is +defined to simplify this process. The macro +<literal>CYG_ADC_CHANNEL</literal> defines a +<structname>cyg_adc_channel</structname> structure, which contains the +channel number, the buffer size, and a pointer to the device object +defined earlier. The call to <literal>DEVTAB_ENTRY()</literal> +generates a device table entry containing the configured channel name, +a pointer to a device function table defined in the generic ADC +driver, pointers to init and lookup functions implemented here, and a +pointer to the channel data structure just defined. +</para> + +<para> +Finally, four channels, numbered 0 to 3 are created. +</para> + + +</refsect1> + +<refsect1 id="adcdev-functions"><title>Functions</title> + +<para> +There are several classes of function that need to be defined in an +ADC driver. These are those function that go into the channel's device +table, those that go into the ADC device's function table, calls that +the driver makes into the generic ADC package, and interrupt handling +functions. +</para> + +<refsect2 id="adcdev-functions-devtab"><title>Device Table Functions</title> + +<para> +These functions are placed in the standard device table entry for each +channel and handle initialization and location of the device within +the generic driver infrastructure. +</para> + +<para> +<function>static bool example_adc_init(struct cyg_devtab_entry *tab)</function> + +This function is called from the device IO infrastructure to +initialize the device. It should perform any work needed to start up +the device, short of actually starting the generation of samples. This +function will be called for each channel, so if there is +initialization that only needs to be done once, such as creating an +interrupt object, then care should be taken to do this. This function +should also call <function>cyg_adc_device_init()</function> to +initialize the generic parts of the driver. +</para> + +<para> +<function>static Cyg_ErrNo example_adc_lookup(struct cyg_devtab_entry **tab, struct cyg_devtab_entry *sub_tab, const char *name)</function> + +This function is called when a client looks up or opens a channel. It +should call <function>cyg_adc_channel_init()</function> to initialize +the generic part of the channel. It should also perform any operations +needed to start the channel generating samples. +</para> + +</refsect2> + +<refsect2 id="adcdev-functions-driver"><title>Driver Functions</title> + +<para> +These are the functions installed into the driver function table by +the <literal>CYG_ADC_FUNCTIONS()</literal> macro. +</para> + +<para> +<function>static void example_adc_enable( cyg_adc_channel *chan )</function> + +This function is called from the generic ADC package to enable the +channel in response to a +<literal>CYG_IO_SET_CONFIG_ADC_ENABLE</literal> config operation. It +should take any steps needed to start the channel generating samples. +</para> + +<para> +<function>static void example_adc_disable( cyg_adc_channel *chan )</function> + +This function is called from the generic ADC package to enable the +channel in response to a +<literal>CYG_IO_SET_CONFIG_ADC_DISABLE</literal> config operation. It +should take any steps needed to stop the channel generating samples. +</para> + +<para> +<function>static void example_adc_set_rate( cyg_adc_channel *chan, cyg_uint32 rate )</function> + +This function is called from the generic ADC package to enable the +channel in response to a <literal>CYG_IO_SET_CONFIG_ADC_RATE</literal> +config operation. It should take any steps needed to change the sample +rate of the channel, or of the entire device. +</para> + + +</refsect2> + +<refsect2 id="adcdev-functions-generic"><title>Generic Package Functions</title> + +<para> +These functions are called by a hardware ADC device driver to perform +operations in the generic ADC package. +</para> + +<para> +<function>__externC void cyg_adc_device_init( cyg_adc_device *device )</function> + +This function is called from the driver's init function and is used to +initialize the <type>cyg_adc_device</type> object. +</para> + +<para> +<function>__externC void cyg_adc_channel_init(cyg_adc_channel *chan)</function> + +This function is called from the driver's lookup function and is used +to initialize the <type>cyg_adc_channel</type> object. +</para> + +<para> +<function>__externC cyg_uint32 cyg_adc_receive_sample(cyg_adc_channel *chan, cyg_adc_sample_t sample)</function> + +This function is called from the driver's ISR to add a new sample to +the buffer. The return value will be either zero, or +<literal>CYG_ISR_CALL_DSR</literal> and should be ORed with the return +value of the ISR. +</para> + +<para> +<function>__externC void cyg_adc_wakeup(cyg_adc_channel *chan )</function> + +This function is called from the driver's DSR to cause any threads +waiting for data to wake up when a new sample is available. It should +only be called if the <structfield>wakeup</structfield> field of the +channel object is <literal>true</literal>. +</para> + + + +</refsect2> + +<refsect2 id="adcdev-functions-interrupt"><title>Interrupt Functions</title> + +<para> +These functions are internal to the driver, but make calls on generic +package functions. Typically an ADC device will have a single +interrupt vector with which it signals available samples on the +channels and any error conditions such as overruns. +</para> + +<para> +<function>static cyg_uint32 example_adc_isr(cyg_vector_t vector, cyg_addrword_t data)</function> + +This function is the ISR attached to the ADC device's interrupt +vector. It is responsible for reading samples from the channels and +passing them on to the generic layer. It needs to check each channel +for data, and call <function>cyg_adc_receive_sample()</function> for +each new sample available, and then ready the device for the next +interrupt. It's activities are best explained by example: +</para> + +<programlisting width=72> +static cyg_uint32 example_adc_isr(cyg_vector_t vector, cyg_addrword_t data) +{ + cyg_adc_device *example_device = (cyg_adc_device *) data; + example_adc_info *example_info = example_device->dev_priv; + cyg_uint32 res = 0; + int i; + + // Deal with errors if necessary + DEVICE_CHECK_ERRORS( example_info ); + + // Look for all channels with data available + for( i = 0; i < CHANNEL_COUNT; i++ ) + { + if( CHANNEL_SAMPLE_AVALIABLE(i) ) + { + // Fetch data from this channel and pass up to higher + // level. + + cyg_adc_sample_t data = CHANNEL_GET_SAMPLE(i); + + res |= CYG_ISR_HANDLED | cyg_adc_receive_sample( example_info->channel[i], data ); + } + } + + // Clear any interrupt conditions + DEVICE_CLEAR_INTERRUPTS( example_info ); + + cyg_drv_interrupt_acknowledge(example_info->vector); + + return res; +} +</programlisting> + +<para> +<function>static void example_adc_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)</function> + +This function is the DSR attached to the ADC device's interrupt +vector. It is called by the kernel if the ISR return value contains +the <literal>CYG_ISR_HANDLED</literal> bit. It needs to call +<function>cyg_adc_wakeup()</function> for each channel that has its +<structfield>wakeup</structfield> field set. Again, and example should +make it all clear: +</para> + +<programlisting width=72> +static void example_adc_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) +{ + cyg_adc_device *example_device = (cyg_adc_device *) data; + example_adc_info *example_info = example_device->dev_priv; + int i; + + // Look for all channels with pending wakeups + for( i = 0; i < CHANNEL_COUNT; i++ ) + { + if( example_info->channel[i]->wakeup ) + cyg_adc_wakeup( example_info->channel[i] ); + } +} + +</programlisting> + + +</refsect2> + + +</refsect1> + + +</refentry> + +</part>
new file mode 100644 --- /dev/null +++ b/packages/io/adc/current/include/adc.h @@ -0,0 +1,203 @@ +#ifndef CYGONCE_ADC_H +#define CYGONCE_ADC_H +/*========================================================================== +// +// adc.h +// +// Generic ADC driver layer header +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008 eCosCentric Limited +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Date: 2008-03-31 +// Description: Implements generic layer of ADC drivers. +// +//####DESCRIPTIONEND#### +// +//========================================================================*/ + +#include <pkgconf/system.h> +#include <pkgconf/io_adc.h> + +#include <cyg/infra/cyg_type.h> +#include <cyg/io/io.h> +#include <cyg/io/devtab.h> +#include <cyg/io/config_keys.h> +#include <cyg/hal/drv_api.h> + + +#ifdef CYGPKG_IO_ADC_SELECT_SUPPORT +#include <cyg/fileio/fileio.h> +#endif + +//========================================================================== +// Configuration information structure + +typedef struct +{ + cyg_uint32 rate; // Sample rate +} cyg_adc_info_t; + +//========================================================================== +// Sample size type. +// +// Define sample size type depending on hardware capability. + +#if CYGNUM_IO_ADC_SAMPLE_SIZE > 16 +typedef cyg_int32 cyg_adc_sample_t; +#elif CYGNUM_IO_ADC_SAMPLE_SIZE > 8 +typedef cyg_int16 cyg_adc_sample_t; +#else +typedef cyg_int8 cyg_adc_sample_t; +#endif + +//========================================================================== +// Forward type definitions. + +typedef struct cyg_adc_device cyg_adc_device; +typedef struct cyg_adc_channel cyg_adc_channel; +typedef struct cyg_adc_functions cyg_adc_functions; + +//========================================================================== +// Callbacks from hardware drivers to generic driver. + +__externC void cyg_adc_device_init( cyg_adc_device *device ); + +__externC void cyg_adc_channel_init(cyg_adc_channel *chan); + +__externC cyg_uint32 cyg_adc_receive_sample(cyg_adc_channel *chan, cyg_adc_sample_t sample); + +__externC void cyg_adc_wakeup(cyg_adc_channel *chan ); + +//========================================================================== +// Device table functions + +__externC cyg_devio_table_t cyg_io_adc_devio; + +//========================================================================== +// ADC device +// +// A single device may support several channels which share interrupt +// vectors and sample rate settings. + +struct cyg_adc_device +{ + cyg_adc_functions *funs; // Hardware device functions + void *dev_priv; // Hardware device private data + cyg_adc_info_t config; // Current configuration + + cyg_bool init; // Initialized ? + cyg_drv_mutex_t lock; // Device lock +}; + +#define CYG_ADC_DEVICE(__name, __funs, __dev_priv, __rate ) \ +cyg_adc_device __name = \ +{ \ + .funs = __funs, \ + .dev_priv = __dev_priv, \ + .config.rate = __rate, \ + .init = false \ +}; + + +//========================================================================== +// ADC channel +// +// Each device may support several channels, each providing a separate +// stream of samples. + +struct cyg_adc_channel +{ + int channel; // Channel number + + cyg_adc_sample_t *buf; // Sample data buffer + int len; // Buffer length in samples + volatile int put; // Sample insert index + volatile int get; // Sample extract index + + cyg_adc_device *device; // Controlling device + + cyg_bool init; // Initialized ? + cyg_drv_cond_t wait; // Readers wait here for data + cyg_bool waiting; // True if any threads waiting + cyg_bool wakeup; // True if wakeup needed + + cyg_bool enabled; // Channel enabled? + cyg_bool blocking; // Blocking IO + int overflow; // Overflow counter + +#ifdef CYGPKG_IO_ADC_SELECT_SUPPORT + struct CYG_SELINFO_TAG selinfo; // Select info +#endif + +}; + +#define CYG_ADC_CHANNEL( __name, __channel, __bufsize, __device ) \ +static cyg_adc_sample_t __name##_buf[__bufsize]; \ +cyg_adc_channel __name = \ +{ \ + .channel = __channel, \ + .buf = __name##_buf, \ + .len = __bufsize, \ + .put = 0, \ + .get = 0, \ + .device = __device, \ + .init = false \ +}; + +//========================================================================== +// Device functions +// +// These are the functions exported by the hardware device to the +// generic layer. + +struct cyg_adc_functions +{ + void (*enable)( cyg_adc_channel *chan ); + void (*disable)( cyg_adc_channel *chan ); + + void (*set_rate)( cyg_adc_channel *chan, cyg_uint32 rate ); +}; + +#define CYG_ADC_FUNCTIONS( __name, __enable, __disable, __set_rate ) \ +cyg_adc_functions __name = \ +{ \ + .enable = __enable, \ + .disable = __disable, \ + .set_rate = __set_rate \ +}; + +//========================================================================== +#endif // CYGONCE_ADC_H +
new file mode 100644 --- /dev/null +++ b/packages/io/adc/current/src/adc.c @@ -0,0 +1,421 @@ +/*========================================================================== +// +// adc.c +// +// Generic ADC driver layer +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008 eCosCentric Limited +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Date: 2008-03-31 +// Description: Implements generic layer of ADC drivers. +// +//####DESCRIPTIONEND#### +// +//========================================================================*/ + +#include <cyg/io/adc.h> + +//========================================================================== +// Diagnostic support +// +// Switch the #if to 1 to generate some diagnostic messages. + +#if 0 + +#include <cyg/infra/diag.h> + +#define adc_diag( __fmt, ... ) diag_printf("ADC: %30s[%4d]: " __fmt, __FUNCTION__, __LINE__, ## __VA_ARGS__ ); +#define adc_dump_buf( __buf, __size ) diag_dump_buf( __buf, __size ) +#else +#define adc_diag( __fmt, ... ) +#define adc_dump_buf( __buf, __size ) +#endif + +//========================================================================== +// Main device table entry functions + +static Cyg_ErrNo adc_write(cyg_io_handle_t handle, const void *buf, cyg_uint32 *len); +static Cyg_ErrNo adc_read(cyg_io_handle_t handle, void *buf, cyg_uint32 *len); +static cyg_bool adc_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info); +static Cyg_ErrNo adc_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *buf, cyg_uint32 *len); +static Cyg_ErrNo adc_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *buf, cyg_uint32 *len); + +DEVIO_TABLE(cyg_io_adc_devio, + adc_write, + adc_read, + adc_select, + adc_get_config, + adc_set_config + ); + +//========================================================================== +// Device interface functions + +//-------------------------------------------------------------------------- +// Device write +// +// Not supported + +static Cyg_ErrNo adc_write(cyg_io_handle_t handle, const void *buf, cyg_uint32 *len) +{ + adc_diag("write not supported\n"); + return -EDEVNOSUPP; +} + +//-------------------------------------------------------------------------- +// Device Read +// +// Returns a whole number of samples from a channel + +static Cyg_ErrNo adc_read(cyg_io_handle_t handle, void *abuf, cyg_uint32 *len) +{ + Cyg_ErrNo res = ENOERR; + cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle; + cyg_adc_channel *chan = (cyg_adc_channel *)t->priv; + cyg_adc_device *dev = chan->device; + + cyg_adc_sample_t *buf = (cyg_adc_sample_t *)abuf; + cyg_uint32 size = 0; + + adc_diag("chan %d buf %p len %u\n", chan->channel, abuf, *len); + + // Check that supplied buffer address is aligned to sample size. + if( (((CYG_ADDRESS)buf) & (sizeof(cyg_adc_sample_t)-1)) != 0 ) + return -EIO; + + // Check that channel is enabled + if( !chan->enabled ) + return -EIO; + + cyg_drv_mutex_lock( &dev->lock ); + cyg_drv_dsr_lock(); + { + while( (*len-size) >= sizeof(cyg_adc_sample_t) ) + { + if( chan->put != chan->get ) + { + // A sample is available, transfer it to the buffer. + + int next = chan->get+1; + cyg_drv_isr_lock(); + { + *buf++ = chan->buf[chan->get]; + if( next == chan->len ) + next = 0; + chan->get = next; + } + cyg_drv_isr_unlock(); + size += sizeof(cyg_adc_sample_t); + + adc_diag("chan %d sample %d -> %04x\n", chan->channel, size, buf[-1] ); + } + else + { + // If there is no data available, either wait or + // return EAGAIN. + + if( !chan->blocking ) + { + // If non-blocking, return what we have. If + // nothing, return EAGAIN. + *len = size; + if( size == 0 ) + res = -EAGAIN; + break; + } + // Otherwise, we must wait for data to arrive. + + adc_diag("wait for sample\n"); + chan->waiting = true; + if( !cyg_drv_cond_wait( &chan->wait ) ) + { + // Abort the wait, return EINTR + adc_diag("abort\n"); + *len = size; + res = -EINTR; + break; + } + + adc_diag("wake up\n"); + // Loop back round to pick up any samples now + // available. + } + } + } + cyg_drv_dsr_unlock(); + cyg_drv_mutex_unlock( &dev->lock ); + + return res; +} + +//-------------------------------------------------------------------------- +// Select support +// +// If select support is enabled, check for pending samples. + +static cyg_bool adc_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info) +{ +#ifdef CYGPKG_IO_ADC_SELECT_SUPPORT + + cyg_bool res = false; + cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle; + cyg_adc_channel *chan = (cyg_adc_channel *)t->priv; + cyg_adc_device *dev = chan->device; + + // Check that channel is enabled. If not, return false. + if( !chan->enabled ) + return false; + + // Only read select is supported + if( which == CYG_FREAD ) + { + cyg_drv_mutex_lock( &dev->lock ); + cyg_drv_dsr_lock(); + { + cyg_drv_isr_lock(); + if( chan->put == chan->get ) + { + // There is no data in the buffer, register the select. + + cyg_selrecord( info, &chan->selinfo ); + } + else + { + res = true; + } + cyg_drv_isr_unlock(); + } + cyg_drv_dsr_unlock(); + cyg_drv_mutex_unlock( &dev->lock ); + + } + + return res; + +#else + + return true; + +#endif +} + +//-------------------------------------------------------------------------- +// Get config +// +// Return various configuration options. + +static Cyg_ErrNo adc_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *xbuf, cyg_uint32 *len) +{ + cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle; + cyg_adc_channel *chan = (cyg_adc_channel *)t->priv; + cyg_adc_info_t *buf = (cyg_adc_info_t *)xbuf; + Cyg_ErrNo res = ENOERR; + + adc_diag("chan %d\n", chan->channel ); + switch( key ) + { + case CYG_IO_GET_CONFIG_ADC_RATE: + *buf = chan->device->config; + break; + + case CYG_IO_GET_CONFIG_READ_BLOCKING: + if (*len < sizeof(cyg_uint32)) + return -EINVAL; + *(cyg_uint32*)xbuf = (chan->blocking) ? 1 : 0; + break; + + + default: + res = -EINVAL; + } + + return res; +} + +//-------------------------------------------------------------------------- +// Set config +// +// Set configuration options. + +static Cyg_ErrNo adc_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *xbuf, cyg_uint32 *len) +{ + + cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle; + cyg_adc_channel *chan = (cyg_adc_channel *)t->priv; + cyg_adc_info_t *buf = (cyg_adc_info_t *)xbuf; + Cyg_ErrNo res = ENOERR; + + adc_diag("chan %d\n", chan->channel ); + + switch( key ) + { + case CYG_IO_SET_CONFIG_ADC_RATE: + chan->device->config = *buf; + chan->device->funs->set_rate( chan, buf->rate ); + break; + + case CYG_IO_SET_CONFIG_ADC_ENABLE: + chan->enabled = true; + chan->device->funs->enable( chan ); + break; + + case CYG_IO_SET_CONFIG_ADC_DISABLE: + chan->enabled = false; + chan->device->funs->disable( chan ); + break; + + case CYG_IO_SET_CONFIG_READ_BLOCKING: + if (*len < sizeof(cyg_uint32)) + return -EINVAL; + + chan->blocking = (1 == *(cyg_uint32*)xbuf) ? true : false; + break; + + default: + res = -EINVAL; + } + + return res; +} + +//========================================================================== +// Callbacks from hardware driver to generic layer + +//-------------------------------------------------------------------------- +// Initialize generic device structure + +__externC void cyg_adc_device_init( cyg_adc_device *device ) +{ + if( device->init ) + return; + + adc_diag("\n"); + + device->init = true; + cyg_drv_mutex_init( &device->lock ); +} + +//-------------------------------------------------------------------------- +// Initialize generic channel structure + +__externC void cyg_adc_channel_init(cyg_adc_channel *chan) +{ + if( chan->init ) + return; + + adc_diag("chan %d\n", chan->channel ); + + chan->init = true; + cyg_drv_cond_init( &chan->wait, &chan->device->lock ); + chan->enabled = false; + chan->waiting = false; + chan->wakeup = false; + chan->blocking = true; + chan->overflow = 0; + +#ifdef CYGPKG_IO_ADC_SELECT_SUPPORT + cyg_selinit( &chan->selinfo ); +#endif + +} + +//-------------------------------------------------------------------------- +// Add a new sample to the buffer +// +// This function is called from the ISR to store a sample in the +// buffer. + +__externC cyg_uint32 cyg_adc_receive_sample(cyg_adc_channel *chan, cyg_adc_sample_t sample) +{ + cyg_uint32 res = 0; + int next = chan->put+1; + + adc_diag("chan %d sample %04x\n", chan->channel, sample ); + + // Ignore this sample if the channel is not enabled. + if( !chan->enabled ) + return 0; + + if( next == chan->len ) + next = 0; + + if( next == chan->get ) + { + // The buffer is full. Record an overflow and arrange to + // replace the oldest sample with the new one. + + adc_diag("chan %d overflow\n", chan->channel); + chan->overflow++; + + chan->get++; + if( chan->get == chan->len ) + chan->get = 0; + } + + // If this is the first sample into the buffer, wake any waiting + // selectors. Also do this if there are any threads waiting for + // data. + if( chan->put == chan->get || chan->waiting ) + res |= CYG_ISR_CALL_DSR, chan->wakeup = true; + + // Store the new sample + chan->buf[chan->put] = sample; + chan->put = next; + + return res; +} + +//-------------------------------------------------------------------------- +// Wakeup a channel +// +// This function is called from the DSR if the channel wakeup field is +// set. It satisfies any select operations or any waiting readers. + +__externC void cyg_adc_wakeup(cyg_adc_channel *chan ) +{ + adc_diag("chan %d\n", chan->channel ); + +#ifdef CYGPKG_IO_ADC_SELECT_SUPPORT + cyg_selwakeup( &chan->selinfo ); +#endif + + chan->waiting = false; + cyg_drv_cond_signal( &chan->wait ); + + chan->wakeup = false; +} + +//========================================================================== +// End of adc.c
