diff options
Diffstat (limited to 'ecos/packages/devs/adc')
22 files changed, 5384 insertions, 0 deletions
diff --git a/ecos/packages/devs/adc/arm/at91/current/ChangeLog b/ecos/packages/devs/adc/arm/at91/current/ChangeLog new file mode 100644 index 0000000..2c3576e --- /dev/null +++ b/ecos/packages/devs/adc/arm/at91/current/ChangeLog @@ -0,0 +1,31 @@ +2010-05-18 ccoutand <ccoutand@stmi.com> + + * AT91 ADC driver package created + * cdl/adc_at91.cdl + * src/adc_at91.c + * include/adc_at91.inl + * tests/at91_adc_test.c + +//=========================================================================== +// ####GPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2010 Free Software Foundation, Inc. +// +// This program 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. +// +// This program 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 this program; if not, write to the +// Free Software Foundation, Inc., 51 Franklin Street, +// Fifth Floor, Boston, MA 02110-1301, USA. +// ------------------------------------------- +// ####GPLCOPYRIGHTEND#### +//=========================================================================== diff --git a/ecos/packages/devs/adc/arm/at91/current/cdl/adc_at91.cdl b/ecos/packages/devs/adc/arm/at91/current/cdl/adc_at91.cdl new file mode 100644 index 0000000..14062fb --- /dev/null +++ b/ecos/packages/devs/adc/arm/at91/current/cdl/adc_at91.cdl @@ -0,0 +1,355 @@ +# ==================================================================== +# +# adc_at91.cdl +# +# eCos AT91 ADC configuration data +# +# ==================================================================== +## ####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2008 Free Software Foundation, Inc. +## +## 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., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +## +## 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): ccoutand@stmi.com +# Contributors: +# Date: 2010-02-12 +# +#####DESCRIPTIONEND#### +# +# ==================================================================== + + +cdl_package CYGPKG_DEVS_ADC_ARM_AT91 { + display "ADC hardware device driver for AT91 family of ARM controllers" + + parent CYGPKG_IO_ADC_DEVICES + active_if CYGPKG_IO_ADC_DEVICES + active_if CYGPKG_HAL_ARM_AT91 + description " + This package provides a generic ADC device driver for the on-chip + ADC peripherals in AT91 processors." + + include_dir cyg/io + compile -library=libextras.a adc_at91.c + + define_proc { + puts $::cdl_system_header "#define CYGDAT_DEVS_ADC_ARM_AT91_INL <cyg/io/adc_at91.inl>" + } + + # + # Primary ADC ( ADC0 ) + # + cdl_component CYGPKG_DEVS_ADC_ARM_AT91_ADC0 { + display "Atmel AT91 ADC port 0 driver" + flavor bool + default_value 1 + description " + This option includes the device driver for the on-chip ADC 0 of the + AT91 processors" + + + cdl_interface CYGINT_DEVS_ADC_ARM_AT91_ADC0_CHANNELS { + display "Number of ADC0 channels" + } + + cdl_option CYGNUM_DEVS_ADC_ARM_AT91_ADC0_SELECT_TIMER { + display "Interrupt priority" + flavor data + legal_values {0 1 2} + default_value 1 + description " + This option selects the timer channel to be used for + generating the sampling interval. Timer channel 0 can + be assigned as Real Time Kernel clock so timer channel + 1 is set to be the default value." + } + + cdl_option CYGNUM_DEVS_ADC_ARM_AT91_ADC0_PRESCAL { + display "ADC clock setting" + flavor data + legal_values 0 to 255 + default_value 128 + description " + This option sets the AT91 ADC PRESCAL value. + ADCClock = MCK / ((PRESCAL + 1) * 2)" + } + + cdl_option CYGNUM_DEVS_ADC_ARM_AT91_ADC0_STARTUP_TIME { + display "ADC start-up time" + flavor data + legal_values 0 to 255 + default_value 128 + description " + This option sets the AT91 ADC start-up time value. + ADC start-up time = (STARTUP+1) * 8 / ADCClock" + } + + cdl_option CYGNUM_DEVS_ADC_ARM_AT91_ADC0_SHTIM { + display "ADC start up time" + flavor data + legal_values 0 to 15 + default_value 7 + description " + This option sets the AT91 ADC Sample and Hold Time. + Sample and Hold Time = SHTIM / ADCClock" + } + + cdl_option CYGNUM_DEVS_ADC_ARM_AT91_ADC0_INTPRIO { + display "Interrupt priority" + flavor data + legal_values 0 to 15 + default_value 15 + description " + This option selects the interrupt priority for the ADC + interrupts. Timer x is used for generating the sample + clock. So this option configures the interrupt priority + for timer x. There are 16 priority levels corresponding to + the values 0 through 15 decimal, of which 15 is the lowest + priority. The reset value of these registers defaults all + interrupts to the lowest priority, allowing a single write + to elevate the priority of an individual interrupt." + } + + cdl_option CYGNUM_DEVS_ADC_ARM_AT91_ADC0_DEFAULT_RATE { + display "Default sample rate" + flavor data + legal_values 1 to 10000 + default_value 100 + description " + The driver will be initialized with the default sample rate. + If you raise the default sample rate you might need to increase + the buffer size for each channel." + } + + # Support up to 8 ADC channels + for { set ::channel 0 } { $::channel < 8 } { incr ::channel } { + + cdl_component CYGPKG_DEVS_ADC_ARM_AT91_ADC0_CHANNEL[set ::channel] { + display "Access ADC channel [set ::channel]" + flavor bool + default_value [set ::channel] == 0 + implements CYGINT_DEVS_ADC_ARM_AT91_ADC0_CHANNELS + description " + If the application needs to access the on-chip ADC + channel [set ::channel] via an eCos ADC driver then + this option should be enabled." + + cdl_option CYGDAT_DEVS_ADC_ARM_AT91_ADC0_CHANNEL[set ::channel]_NAME { + display "Device name" + flavor data + default_value [format {"\"/dev/adc0%d\""} $::channel] + description " + This option controls the name that an eCos application + should use to access this device via cyg_io_lookup(), + open(), or similar calls." + } + + cdl_option CYGDAT_DEVS_ADC_ARM_AT91_ADC0_CHANNEL[set ::channel]_BUFSIZE { + display "Size of data buffer" + flavor data + legal_values 0x01 to 0x2000000 + default_value 512 + description " + This option controls the number of samples the + buffer can store. The required RAM depends on the + sample size and on the number of samples. If the + sample size is <= 8 bit the the required RAM = + size of data buffer. If the sample size is 9 or 10 + bit then required RAM = size of data buffer * 2." + } + } + } + } + + # + # ADC1 + # + cdl_component CYGPKG_DEVS_ADC_ARM_AT91_ADC1 { + display "Atmel AT91 ADC port 1 driver" + flavor bool + default_value 0 + + requires { CYGHWR_HAL_ARM_AT91 == "M55800A" } + + description " + This option includes the device driver for the on-chip ADC 1 of the + AT91 processors" + + + cdl_interface CYGINT_DEVS_ADC_ARM_AT91_ADC1_CHANNELS { + display "Number of ADC1 channels" + } + + cdl_option CYGNUM_DEVS_ADC_ARM_AT91_ADC1_SELECT_TIMER { + display "Interrupt priority" + flavor data + legal_values {0 1 2} + default_value 2 + description " + This option selects the timer channel to be used for + generating the sampling interval. Timer channel 0 can + be assigned as Real Time Kernel clock so timer channel + 1 is set to be the default value." + } + + cdl_option CYGNUM_DEVS_ADC_ARM_AT91_ADC1_PRESCAL { + display "ADC clock setting" + flavor data + legal_values 0 to 255 + default_value 128 + description " + This option sets the AT91 ADC PRESCAL value. + ADCClock = MCK / ((PRESCAL + 1) * 2)" + } + + cdl_option CYGNUM_DEVS_ADC_ARM_AT91_ADC1_STARTUP_TIME { + display "ADC start-up time" + flavor data + legal_values 0 to 255 + default_value 128 + description " + This option sets the AT91 ADC start-up time value. + ADC start-up time = (STARTUP+1) * 8 / ADCClock" + } + + cdl_option CYGNUM_DEVS_ADC_ARM_AT91_ADC1_SHTIM { + display "ADC start up time" + flavor data + legal_values 0 to 15 + default_value 7 + description " + This option sets the AT91 ADC Sample and Hold Time. + Sample and Hold Time = SHTIM / ADCClock" + } + + cdl_option CYGNUM_DEVS_ADC_ARM_AT91_ADC1_INTPRIO { + display "Interrupt priority" + flavor data + legal_values 0 to 15 + default_value 15 + description " + This option selects the interrupt priority for the ADC + interrupts. Timer x is used for generating the sample + clock. So this option configures the interrupt priority + for timer x. There are 16 priority levels corresponding to + the values 0 through 15 decimal, of which 15 is the lowest + priority. The reset value of these registers defaults all + interrupts to the lowest priority, allowing a single write + to elevate the priority of an individual interrupt." + } + + cdl_option CYGNUM_DEVS_ADC_ARM_AT91_ADC1_DEFAULT_RATE { + display "Default sample rate" + flavor data + legal_values 1 to 10000 + default_value 100 + description " + The driver will be initialized with the default sample rate. + If you raise the default sample rate you might need to increase + the buffer size for each channel." + } + + # Support up to 8 ADC channels + for { set ::channel 0 } { $::channel < 8 } { incr ::channel } { + + cdl_component CYGPKG_DEVS_ADC_ARM_AT91_ADC1_CHANNEL[set ::channel] { + display "Access ADC channel [set ::channel]" + flavor bool + default_value [set ::channel] == 0 + implements CYGINT_DEVS_ADC_ARM_AT91_ADC1_CHANNELS + description " + If the application needs to access the on-chip ADC + channel [set ::channel] via an eCos ADC driver then + this option should be enabled." + + cdl_option CYGDAT_DEVS_ADC_ARM_AT91_ADC1_CHANNEL[set ::channel]_NAME { + display "Device name" + flavor data + default_value [format {"\"/dev/adc1%d\""} $::channel] + description " + This option controls the name that an eCos application + should use to access this device via cyg_io_lookup(), + open(), or similar calls." + } + + cdl_option CYGDAT_DEVS_ADC_ARM_AT91_ADC1_CHANNEL[set ::channel]_BUFSIZE { + display "Size of data buffer" + flavor data + legal_values 0x01 to 0x2000000 + default_value 512 + description " + This option controls the number of samples the + buffer can store. The required RAM depends on the + sample size and on the number of samples. If the + sample size is <= 8 bit the the required RAM = + size of data buffer. If the sample size is 9 or 10 + bit then required RAM = size of data buffer * 2." + } + } + } + } + + cdl_option CYGPKG_DEVS_ADC_ARM_AT91_DEBUG_LEVEL { + display "Driver debug output level" + flavor data + legal_values {0 1} + default_value 0 + description " + This option specifies the level of debug data output by + the AT91 ADC device driver. A value of 0 signifies + no debug data output; 1 signifies normal debug data + output. If an overrun occurred then this can only be + detected by debug output messages." + } + + cdl_component CYGSEM_DEVS_ADC_ARM_AT91_SAMPLE_SIZE_LIMIT { + display "Sample size limit" + flavor bool + calculated 1 + requires { ( CYGNUM_IO_ADC_SAMPLE_SIZE == 8 ) + || ( CYGNUM_IO_ADC_SAMPLE_SIZE == 10 ) } + description " + This component forces a limit (or rounds) the sample + size for AT91 ADC channels which in the most are 10-bit." + } + + cdl_option CYGPKG_DEVS_ADC_ARM_AT91_TESTS { + display "Tests for AT91 ADC driver" + flavor data + no_define + calculated { "tests/at91_adc_test" } + description " + This option specifies the set of tests for the AT91 + ADC device driver." + } + +} diff --git a/ecos/packages/devs/adc/arm/at91/current/include/adc_at91.inl b/ecos/packages/devs/adc/arm/at91/current/include/adc_at91.inl new file mode 100644 index 0000000..523cbf6 --- /dev/null +++ b/ecos/packages/devs/adc/arm/at91/current/include/adc_at91.inl @@ -0,0 +1,200 @@ +//========================================================================== +// +// adc_at91.inl +// +// ADC driver for AT91 on chip ADC +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008 Free Software Foundation, Inc. +// +// 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): Atmel AT91 on-chip ADC device driver, ccoutand +// +// Contributors: +// Date: 2010-05-27 +// Purpose: +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#ifndef CYGONCE_DEVS_ADC_ARM_AT91_INL +#define CYGONCE_DEVS_ADC_ARM_AT91_INL + +// Some AT91 HAL are defining the timer 0 vector as TC0 and other as TIMER0 +#ifndef CYGNUM_HAL_INTERRUPT_TC0 +#define CYGNUM_HAL_INTERRUPT_TC0 CYGNUM_HAL_INTERRUPT_TIMER0 +#endif + +// Declare ADC0 +#ifdef CYGPKG_DEVS_ADC_ARM_AT91_ADC0 + +static at91_adc_info at91_adc0_info = +{ + .adc_base = AT91_ADC, + .timer_base = AT91_TC, + .tc_base = AT91_TC + (AT91_TC_TC_SIZE * CYGNUM_DEVS_ADC_ARM_AT91_ADC0_SELECT_TIMER), + .timer_vector = CYGNUM_HAL_INTERRUPT_TC0 + CYGNUM_DEVS_ADC_ARM_AT91_ADC0_SELECT_TIMER, + .timer_intprio = CYGNUM_DEVS_ADC_ARM_AT91_ADC0_INTPRIO, + .timer_id = CYGNUM_DEVS_ADC_ARM_AT91_ADC0_SELECT_TIMER, + .int_handle = 0, + .adc_prescal = CYGNUM_DEVS_ADC_ARM_AT91_ADC0_PRESCAL, + .adc_startup_time = CYGNUM_DEVS_ADC_ARM_AT91_ADC0_STARTUP_TIME, + .adc_shtim = CYGNUM_DEVS_ADC_ARM_AT91_ADC0_SHTIM, +#if CYGNUM_IO_ADC_SAMPLE_SIZE > 8 + .resolution = AT91_ADC_MR_LOWREC_10BITS, +#else + .resolution = AT91_ADC_MR_LOWRES_8BITS, +#endif + .chan_mask = 0 +}; + +CYG_ADC_DEVICE( at91_adc0_device, + &at91_adc_funs, + &at91_adc0_info, + CYGNUM_DEVS_ADC_ARM_AT91_ADC0_DEFAULT_RATE); + +#define AT91_ADC0_CHANNEL( __chan ) \ +CYG_ADC_CHANNEL( at91_adc0_channel##__chan, \ + __chan, \ + CYGDAT_DEVS_ADC_ARM_AT91_ADC0_CHANNEL##__chan##_BUFSIZE, \ + &at91_adc0_device ); \ + \ +DEVTAB_ENTRY( at91_adc0_channel##__chan##_device, \ + CYGDAT_DEVS_ADC_ARM_AT91_ADC0_CHANNEL##__chan##_NAME, \ + 0, \ + &cyg_io_adc_devio, \ + at91_adc_init, \ + at91_adc_lookup, \ + &at91_adc0_channel##__chan ); + +#ifdef CYGPKG_DEVS_ADC_ARM_AT91_ADC0_CHANNEL0 +AT91_ADC0_CHANNEL(0); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_AT91_ADC0_CHANNEL1 +AT91_ADC0_CHANNEL(1); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_AT91_ADC0_CHANNEL2 +AT91_ADC0_CHANNEL(2); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_AT91_ADC0_CHANNEL3 +AT91_ADC0_CHANNEL(3); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_AT91_ADC0_CHANNEL4 +AT91_ADC0_CHANNEL(4); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_AT91_ADC0_CHANNEL5 +AT91_ADC0_CHANNEL(5); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_AT91_ADC0_CHANNEL6 +AT91_ADC0_CHANNEL(6); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_AT91_ADC0_CHANNEL7 +AT91_ADC0_CHANNEL(7); +#endif + +#endif // CYGPKG_DEVS_ADC_ARM_AT91_ADC0 + + + +// Declare ADC1 +#ifdef CYGPKG_DEVS_ADC_ARM_AT91_ADC1 + +static at91_adc_info at91_adc1_info = +{ + .adc_base = AT91_ADC1, + .timer_base = AT91_TC, + .tc_base = AT91_TC + (AT91_TC_TC_SIZE * CYGNUM_DEVS_ADC_ARM_AT91_ADC1_SELECT_TIMER), + .timer_vector = CYGNUM_HAL_INTERRUPT_TC0 + CYGNUM_DEVS_ADC_ARM_AT91_ADC1_SELECT_TIMER, + .timer_intprio = CYGNUM_DEVS_ADC_ARM_AT91_ADC1_INTPRIO, + .timer_id = CYGNUM_DEVS_ADC_ARM_AT91_ADC1_SELECT_TIMER, + .int_handle = 0, + .adc_prescal = CYGNUM_DEVS_ADC_ARM_AT91_ADC1_PRESCAL, + .adc_startup_time = CYGNUM_DEVS_ADC_ARM_AT91_ADC1_STARTUP_TIME, + .adc_shtim = CYGNUM_DEVS_ADC_ARM_AT91_ADC1_SHTIM, +#if CYGNUM_IO_ADC_SAMPLE_SIZE > 8 + .resolution = AT91_ADC_MR_LOWREC_10BITS, +#else + .resolution = AT91_ADC_MR_LOWRES_8BITS, +#endif + .chan_mask = 0 +}; +CYG_ADC_DEVICE( at91_adc1_device, + &at91_adc_funs, + &at91_adc1_info, + CYGNUM_DEVS_ADC_ARM_AT91_ADC1_DEFAULT_RATE); + +#define AT91_ADC1_CHANNEL( __chan ) \ +CYG_ADC_CHANNEL( at91_adc1_channel##__chan, \ + __chan, \ + CYGDAT_DEVS_ADC_ARM_AT91_ADC1_CHANNEL##__chan##_BUFSIZE, \ + &at91_adc1_device ); \ + \ +DEVTAB_ENTRY( at91_adc1_channel##__chan##_device, \ + CYGDAT_DEVS_ADC_ARM_AT91_ADC1_CHANNEL##__chan##_NAME, \ + 0, \ + &cyg_io_adc_devio, \ + at91_adc_init, \ + at91_adc_lookup, \ + &at91_adc1_channel##__chan ); + +#ifdef CYGPKG_DEVS_ADC_ARM_AT91_ADC1_CHANNEL0 +AT91_ADC1_CHANNEL(0); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_AT91_ADC1_CHANNEL1 +AT91_ADC1_CHANNEL(1); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_AT91_ADC1_CHANNEL2 +AT91_ADC1_CHANNEL(2); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_AT91_ADC1_CHANNEL3 +AT91_ADC1_CHANNEL(3); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_AT91_ADC1_CHANNEL4 +AT91_ADC1_CHANNEL(4); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_AT91_ADC1_CHANNEL5 +AT91_ADC1_CHANNEL(5); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_AT91_ADC1_CHANNEL6 +AT91_ADC1_CHANNEL(6); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_AT91_ADC1_CHANNEL7 +AT91_ADC1_CHANNEL(7); +#endif + +#endif // CYGPKG_DEVS_ADC_ARM_AT91_ADC1 + +#endif // CYGONCE_DEVS_ADC_ARM_AT91_INL diff --git a/ecos/packages/devs/adc/arm/at91/current/src/adc_at91.c b/ecos/packages/devs/adc/arm/at91/current/src/adc_at91.c new file mode 100644 index 0000000..6364bf1 --- /dev/null +++ b/ecos/packages/devs/adc/arm/at91/current/src/adc_at91.c @@ -0,0 +1,457 @@ +//========================================================================== +// +// adc_at91.c +// +// ADC driver for AT91 on chip ADC +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008 Free Software Foundation, Inc. +// +// 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): Uwe Kindler <uwe_kindler@web.de> +// Updated for Atmel AT91 device, ccoutand <ccoutand@stmi.com> +// Contributors: +// Date: 2010-02-15 +// Purpose: +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + + +//========================================================================== +// INCLUDES +//========================================================================== +#include <pkgconf/system.h> +#include <pkgconf/devs_adc_arm_at91.h> + +#include <cyg/infra/cyg_type.h> +#include <cyg/infra/cyg_ass.h> +#include <cyg/infra/diag.h> +#include <cyg/io/adc.h> +#include <cyg/hal/hal_arch.h> +#include <cyg/hal/hal_io.h> +#include <cyg/hal/hal_intr.h> +#include <cyg/hal/drv_api.h> + +#if CYGPKG_DEVS_ADC_ARM_AT91_DEBUG_LEVEL > 0 + #define at91_adc_printf(args...) diag_printf(args) +#else + #define at91_adc_printf(args...) +#endif + +#define AT91_ADC_CHER_CHx(_ch_) (0x1 << _ch_) +#define AT91_ADC_CHER_CDRx(_ch_) (_ch_ << 2) + +//========================================================================== +// DATA TYPES +//========================================================================== +typedef struct at91_adc_info +{ + cyg_uint32 adc_base; // base address of ADC peripheral + cyg_uint8 adc_prescal; // ADC prescal value + cyg_uint8 adc_startup_time; // ADC Startup Time value + cyg_uint8 adc_shtim; // ADC SHTIM value + cyg_uint8 timer_id; // select timer + cyg_uint32 timer_base; // base address of Timer peripheral + cyg_uint32 tc_base; // base address of Timer channel + cyg_vector_t timer_vector; // interrupt vector number + int timer_intprio; // interrupt priority of ADC interrupt + cyg_uint32 timer_cnt; // Timer value + cyg_uint8 timer_clk; // Timer clock setting + cyg_uint32 resolution; + cyg_handle_t int_handle; // For initializing the interrupt + cyg_interrupt int_data; + struct cyg_adc_channel *channel[AT91_MAX_ADC_CHAN]; // stores references to channel objects + cyg_uint8 chan_mask; // mask that indicates channels used + // by ADC driver +} at91_adc_info; + + +//========================================================================== +// DECLARATIONS +//========================================================================== +static bool at91_adc_init(struct cyg_devtab_entry *tab); +static Cyg_ErrNo at91_adc_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name); +static void at91_adc_enable( cyg_adc_channel *chan ); +static void at91_adc_disable( cyg_adc_channel *chan ); +static void at91_adc_set_rate( cyg_adc_channel *chan, cyg_uint32 rate ); +static cyg_uint32 at91_adc_isr(cyg_vector_t vector, cyg_addrword_t data); +static void at91_adc_dsr(cyg_vector_t vector, + cyg_ucount32 count, + cyg_addrword_t data); + +// ------------------------------------------------------------------------- +// Driver functions: +CYG_ADC_FUNCTIONS( at91_adc_funs, + at91_adc_enable, + at91_adc_disable, + at91_adc_set_rate ); + + +#include CYGDAT_DEVS_ADC_ARM_AT91_INL // Instantiate ADCs + +//========================================================================== +// 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 and interrupt object, then care should +// be taken to do this. This function should also call cyg_adc_device_init() +// to initialize the generic parts of the driver. +//========================================================================== +static bool at91_adc_init(struct cyg_devtab_entry *tab) +{ + cyg_adc_channel *chan = (cyg_adc_channel *)tab->priv; + cyg_adc_device *device = chan->device; + at91_adc_info *info = device->dev_priv; + cyg_uint32 regval; + + if (!info->int_handle) + { + cyg_drv_interrupt_create(info->timer_vector, + info->timer_intprio, + (cyg_addrword_t)device, + &at91_adc_isr, + &at91_adc_dsr, + &(info->int_handle), + &(info->int_data)); + cyg_drv_interrupt_attach(info->int_handle); + cyg_drv_interrupt_mask(info->timer_vector); + + // Reset ADC + HAL_WRITE_UINT32((info->adc_base + AT91_ADC_CR), AT91_ADC_CR_SWRST); + + // Disable counter interrupts + HAL_WRITE_UINT32(info->tc_base+AT91_TC_CCR, AT91_TC_CCR_CLKDIS); + HAL_WRITE_UINT32(info->tc_base+AT91_TC_IDR, 0xffffffff); + + // Clear status bit + HAL_READ_UINT32(info->tc_base + AT91_TC_SR, regval); + + // Enable peripheral clocks for TC + HAL_WRITE_UINT32(AT91_PMC+AT91_PMC_PCER, \ + ((AT91_PMC_PCER_TC0) << info->timer_id)); + + // + // Disable all interrupts, all channels + // + HAL_WRITE_UINT32((info->adc_base + AT91_ADC_CHDR), \ + AT91_ADC_CHER_CH0 |\ + AT91_ADC_CHER_CH1 |\ + AT91_ADC_CHER_CH2 |\ + AT91_ADC_CHER_CH3 |\ + AT91_ADC_CHER_CH4 |\ + AT91_ADC_CHER_CH5 |\ + AT91_ADC_CHER_CH6 |\ + AT91_ADC_CHER_CH7); + HAL_WRITE_UINT32((info->adc_base + AT91_ADC_IDR), \ + AT91_ADC_CHER_CH0 |\ + AT91_ADC_CHER_CH1 |\ + AT91_ADC_CHER_CH2 |\ + AT91_ADC_CHER_CH3 |\ + AT91_ADC_CHER_CH4 |\ + AT91_ADC_CHER_CH5 |\ + AT91_ADC_CHER_CH6 |\ + AT91_ADC_CHER_CH7); + + // + // setup the default sample rate + // + at91_adc_set_rate(chan, chan->device->config.rate); + + // setup ADC mode + HAL_WRITE_UINT32((info->adc_base + AT91_ADC_MR), \ + ( ( info->adc_prescal << AT91_ADC_MR_PRESCAL_SHIFT ) & \ + AT91_ADC_MR_PRESCAL_MASK ) | \ + ( ( info->adc_startup_time << AT91_ADC_MR_STARTUP_SHIFT ) & \ + AT91_ADC_MR_STARTUP_MASK ) | \ + ( ( info->adc_shtim << AT91_ADC_MR_SHTIM_SHIFT ) & \ + AT91_ADC_MR_SHTIM_MASK ) | \ + AT91_ADC_MR_TRGSEL_TIOA0 | \ + info->resolution); + + + } // if (!info->int_handle) + + cyg_adc_device_init(device); // initialize generic parts of driver + + return true; +} + + +//========================================================================== +// This function is called when a client looks up or opens a channel. It +// should call cyg_adc_channel_init() to initialize the generic part of +// the channel. It should also perform any operations needed to start the +// channel generating samples. +//========================================================================== +static Cyg_ErrNo at91_adc_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name) +{ + cyg_adc_channel *chan = (cyg_adc_channel *)(*tab)->priv; + at91_adc_info *info = chan->device->dev_priv; + + info->channel[chan->channel] = chan; + cyg_adc_channel_init(chan); // initialize generic parts of channel + + // + // The generic ADC manual says: When a channel is first looked up or + // opened, then it is automatically enabled and samples start to + // accumulate - so we start the channel now + // + chan->enabled = true; + at91_adc_enable(chan); + + return ENOERR; +} + + +//========================================================================== +// This function is called from the generic ADC package to enable the +// channel in response to a CYG_IO_SET_CONFIG_ADC_ENABLE config operation. +// It should take any steps needed to start the channel generating samples +//========================================================================== +static void at91_adc_enable(cyg_adc_channel *chan) +{ + at91_adc_info *info = chan->device->dev_priv; + + // Enable the channel + HAL_WRITE_UINT32((info->adc_base + AT91_ADC_CHER), \ + AT91_ADC_CHER_CHx(chan->channel)); + + // + // Unmask interrupt as soon as 1 channel is enable + // + if (!info->chan_mask) + { + cyg_drv_interrupt_unmask(info->timer_vector); + + // Enable timer interrupt + HAL_WRITE_UINT32(info->tc_base+AT91_TC_IER, AT91_TC_IER_CPC); + + // Enable the clock + HAL_WRITE_UINT32(info->tc_base+AT91_TC_CCR, AT91_TC_CCR_TRIG | AT91_TC_CCR_CLKEN); + + // Start timer + HAL_WRITE_UINT32(info->tc_base+AT91_TC_CCR, AT91_TC_CCR_TRIG); + + // Start ADC sampling + HAL_WRITE_UINT32((info->adc_base + AT91_ADC_CR), AT91_ADC_CR_START); + + } + + info->chan_mask |= AT91_ADC_CHER_CHx(chan->channel); + +} + + +//========================================================================== +// This function is called from the generic ADC package to enable the +// channel in response to a CYG_IO_SET_CONFIG_ADC_DISABLE config operation. +// It should take any steps needed to stop the channel generating samples. +//========================================================================== +static void at91_adc_disable(cyg_adc_channel *chan) +{ + at91_adc_info *info = chan->device->dev_priv; + cyg_uint32 sr; + + info->chan_mask &= ~ AT91_ADC_CHER_CHx(chan->channel); + + // Disable the channel + HAL_WRITE_UINT32((info->adc_base + AT91_ADC_CHDR), \ + AT91_ADC_CHER_CHx(chan->channel)); + + // + // If no channel is enabled the we disable interrupts now + // + if (!info->chan_mask) + { + cyg_drv_interrupt_mask(info->timer_vector); + + // Clear interrupt + HAL_READ_UINT32(info->tc_base+AT91_TC_SR, sr); + + // Disable timer interrupt + HAL_WRITE_UINT32(info->tc_base+AT91_TC_IDR, AT91_TC_IER_CPC); + + // Disable the clock + HAL_WRITE_UINT32(info->tc_base+AT91_TC_CCR, AT91_TC_CCR_CLKDIS); + + } +} + + +//========================================================================== +// This function is called from the generic ADC package to enable the +// channel in response to a CYG_IO_SET_CONFIG_ADC_RATE config operation. +// It should take any steps needed to change the sample rate of the channel, +// or of the entire device. +// We use a timer channel to generate the interrupts for sampling the +// analog channels +//========================================================================== +static void at91_adc_set_rate( cyg_adc_channel *chan, cyg_uint32 rate) +{ + cyg_adc_device *device = chan->device; + at91_adc_info *info = (at91_adc_info *)device->dev_priv; + cyg_uint8 timer_clk = AT91_TC_CMR_CLKS_MCK2; + cyg_uint32 tmr_period = CYGNUM_HAL_ARM_AT91_CLOCK_SPEED / ( rate << 1); + + if( tmr_period > 0xffff ) + { + tmr_period = CYGNUM_HAL_ARM_AT91_CLOCK_SPEED / ( rate << 5); + timer_clk = AT91_TC_CMR_CLKS_MCK32; + } + + if( tmr_period > 0xffff ) + { + tmr_period = CYGNUM_HAL_ARM_AT91_CLOCK_SPEED / ( rate << 7); + timer_clk = AT91_TC_CMR_CLKS_MCK128; + } + + if( tmr_period > 0xffff ) + { + tmr_period = CYGNUM_HAL_ARM_AT91_CLOCK_SPEED / ( rate << 10); + timer_clk = AT91_TC_CMR_CLKS_MCK1024; + } + + if( tmr_period > 0xffff ) + { + tmr_period = 0xffff; + timer_clk = AT91_TC_CMR_CLKS_MCK1024; + at91_adc_printf("AT91 ADC timer, rate too high!"); + } + + device->config.rate = rate; + info->timer_clk = timer_clk; + info->timer_cnt = tmr_period; + + // Set timer values + HAL_WRITE_UINT32(info->tc_base+AT91_TC_CMR, AT91_TC_CMR_CPCTRG | info->timer_clk); + HAL_WRITE_UINT32(info->tc_base+AT91_TC_RC, info->timer_cnt); + + at91_adc_printf("AT91 ADC Timer settings %d, %d", info->timer_clk, info->timer_cnt); + + return; +} + + +//========================================================================== +// 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 +// cyg_adc_receive_sample() for each new sample available, and then ready the +// device for the next interrupt. +//========================================================================== +static cyg_uint32 at91_adc_isr(cyg_vector_t vector, cyg_addrword_t data) +{ + cyg_adc_device *device = (cyg_adc_device *) data; + at91_adc_info *info = (at91_adc_info *)device->dev_priv; + cyg_uint32 regval, adc_status; + cyg_uint32 res = 0; + cyg_adc_sample_t adcdata; + cyg_uint32 sr; + + cyg_uint8 active_channels = info->chan_mask; + cyg_uint8 channel_no = 0; + + // Clear timer interrupt + HAL_READ_UINT32(info->tc_base+AT91_TC_SR, sr); + + // Check on channel conversion done + HAL_READ_UINT32(info->adc_base + AT91_ADC_SR, adc_status); + + while (active_channels) + { + if (active_channels & 0x01) + { + // If ADC conversion done, save sample + if(adc_status & AT91_ADC_CHER_CHx(channel_no)) + { + HAL_READ_UINT32((info->adc_base + AT91_ADC_CDR0 + AT91_ADC_CHER_CDRx(channel_no)), regval); + adcdata = regval & 0x3FF; + res |= CYG_ISR_HANDLED + | cyg_adc_receive_sample(info->channel[channel_no], + adcdata); + } + } // if (active_channels & 0x01) + active_channels >>= 1; + channel_no++; + } // while (active_channels) + + // Restart sampling + HAL_WRITE_UINT32((info->adc_base + AT91_ADC_CR), AT91_ADC_CR_START); + + cyg_drv_interrupt_acknowledge(info->timer_vector); + + return res; +} + + +//========================================================================== +// 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 +// CYG_ISR_HANDLED bit. It needs to call cyg_adc_wakeup() for each channel +// that has its wakeup field set. +//========================================================================== +static void at91_adc_dsr(cyg_vector_t vector, + cyg_ucount32 count, + cyg_addrword_t data) +{ + cyg_adc_device *device = (cyg_adc_device *) data; + at91_adc_info *info = device->dev_priv; + cyg_uint8 active_channels = info->chan_mask; + cyg_uint8 chan_no = 0; + + while (active_channels) + { + if (active_channels & 0x01) + { + if(info->channel[chan_no]->wakeup) + { + cyg_adc_wakeup(info->channel[chan_no]); + } + } + chan_no++; + active_channels >>= 1; + } +} + + +//--------------------------------------------------------------------------- +// eof adc_at91.c diff --git a/ecos/packages/devs/adc/arm/at91/current/tests/at91_adc_test.c b/ecos/packages/devs/adc/arm/at91/current/tests/at91_adc_test.c new file mode 100644 index 0000000..9199b04 --- /dev/null +++ b/ecos/packages/devs/adc/arm/at91/current/tests/at91_adc_test.c @@ -0,0 +1,290 @@ +//========================================================================== +// +// at91_adc_test.c +// +// ADC driver for AT91 on chip ADC +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008 Free Software Foundation, Inc. +// +// 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): Uwe Kindler <uwe_kindler@web.de> +// Updated for Atmel AT91 device, ccoutand <ccoutand@stmi.com> +// Contributors: +// Date: 2010-02-15 +// Purpose: +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/system.h> + +#include <cyg/infra/testcase.h> // test macros +#include <cyg/infra/cyg_ass.h> // assertion macros +#include <cyg/infra/diag.h> +#include <cyg/hal/hal_diag.h> +#include <cyg/hal/hal_arch.h> // CYGNUM_HAL_STACK_SIZE_TYPICAL + +// Package requirements +#if defined(CYGPKG_IO_ADC) && defined(CYGPKG_KERNEL) + +#include <pkgconf/kernel.h> +#include <cyg/io/io.h> +#include <cyg/io/adc.h> +#include <pkgconf/devs_adc_arm_at91.h> + +// Package option requirements +#if defined(CYGFUN_KERNEL_API_C) + +#include <cyg/kernel/kapi.h> + +#if CYGINT_DEVS_ADC_ARM_AT91_ADC0_CHANNELS > 0 + +#define MAX_ADC_CHANNEL_TO_TEST 4 + +//=========================================================================== +// DATA TYPES +//=========================================================================== +typedef struct st_thread_data +{ + cyg_thread obj; + long stack[CYGNUM_HAL_STACK_SIZE_TYPICAL]; + cyg_handle_t hdl; +} thread_data_t; + + +//=========================================================================== +// LOCAL DATA +//=========================================================================== +cyg_thread_entry_t adc_thread; +thread_data_t adc_thread_data; + + +//=========================================================================== +// ADC THREAD +//=========================================================================== +void adc_thread(cyg_addrword_t data) +{ + int res; + cyg_io_handle_t handle[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + cyg_uint32 sample_cnt[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + cyg_uint32 cfg_data; + cyg_uint32 len; + cyg_uint32 start_time; + cyg_uint32 end_time; + int i; + cyg_uint8 seconds = 0; + float final_seconds; + cyg_uint32 samples_expected; + + + diag_printf("This test reads samples from all enabled ADC channels.\n" + "Each second the number of already acquired samples\n" + "will be printed. After 10 seconds all ADC channels\n" + "will be stopped and each ADC buffer will be read until\n" + "it is empty. If the number of acquired samples is much\n" + "smaller than the number of expected samples, then you\n" + "should lower the sample rate.\n\n"); + + // Get a handle for ADC device 0 channel 0 - 3 (lookup also trigger a channel enable) + res = cyg_io_lookup( "/dev/adc00", &handle[0]); + res = cyg_io_lookup( "/dev/adc01", &handle[1]); + res = cyg_io_lookup( "/dev/adc02", &handle[2]); + res = cyg_io_lookup( "/dev/adc03", &handle[3]); + + // + // switch all channels to non blocking + // + for (i = 0; i < MAX_ADC_CHANNEL_TO_TEST; ++i) + { + if (handle[i]) + { + cfg_data = 0; + len = sizeof(cfg_data); + res = cyg_io_set_config(handle[i], + CYG_IO_SET_CONFIG_READ_BLOCKING, + &cfg_data, + &len); + if (ENOERR != res) + { + CYG_TEST_FAIL_FINISH("Error switching ADC channel to non blocking"); + } + sample_cnt[i] = 0; + } + } + + start_time = cyg_current_time(); + do + { + for (i = 0; i < MAX_ADC_CHANNEL_TO_TEST; ++i) + { + if (handle[i]) + { + cyg_adc_sample_t sample; + + // read a sample from the channel + do + { + cyg_uint32 len = sizeof(sample); + res = cyg_io_read( handle[i], &sample, &len ); + } + while (-EAGAIN == res); + if (ENOERR == res) + { + sample_cnt[i]++; + } + } // if (handle[i]) + } + // + // print number of acquired samples - if one second is expired. + // we expect that the number of acquired samples is nearly the + // sample rate + // + end_time = cyg_current_time(); + if ((end_time - start_time) >= 100) + { + start_time = end_time; + diag_printf("%d\t %d\t %d\t %d\n", + sample_cnt[0], + sample_cnt[1], + sample_cnt[2], + sample_cnt[3]); + seconds++; + } // if ((end_time - start_time) >= 100) + } while (seconds < 10); + + // + // Now stop all channels + // + for (i = 0; i < MAX_ADC_CHANNEL_TO_TEST; ++i) + { + if (handle[i]) + { + res = cyg_io_set_config(handle[i], + CYG_IO_SET_CONFIG_ADC_DISABLE, + 0, + 0); + if (ENOERR != res) + { + CYG_TEST_FAIL_FINISH("Error disabling ADC channel"); + } + } // if (handle[i]) + } + end_time = cyg_current_time(); + end_time = seconds * 1000 + (end_time - start_time) * 10; + final_seconds = end_time / 1000.0; + + // + // Now read all remaining samples from buffer + // + for (i = 0; i < MAX_ADC_CHANNEL_TO_TEST; ++i) + { + if (handle[i]) + { + do + { + cyg_adc_sample_t sample; + cyg_uint32 len = sizeof(sample); + res = cyg_io_read( handle[i], &sample, &len ); + if (ENOERR == res) + { + sample_cnt[i]++; + } + } while (ENOERR == res); + } // if (handle[i]) + } + + diag_printf("\n\n----------------------------------------\n"); + samples_expected = final_seconds * CYGNUM_DEVS_ADC_ARM_AT91_ADC0_DEFAULT_RATE; + diag_printf("Samples expected after %d milliseconds: %d\n", + end_time, samples_expected); + diag_printf("Samples read (per channel):\n"); + diag_printf("%d\t %d\t %d\t %d\n", + sample_cnt[0], + sample_cnt[1], + sample_cnt[2], + sample_cnt[3]); + + CYG_TEST_PASS_FINISH("ADC test OK"); +} + + +void +cyg_start(void) +{ + CYG_TEST_INIT(); + + // + // create the main ADC test thread + // + cyg_thread_create(4, adc_thread, + (cyg_addrword_t) 0, + "at91_adc_thread", + (void *) adc_thread_data.stack, + 1024 * sizeof(long), + &adc_thread_data.hdl, + &adc_thread_data.obj); + + cyg_thread_resume(adc_thread_data.hdl); + + cyg_scheduler_start(); +} +#else // CYGINT_DEVS_ADC_ARM_AT91_CHANNELS > 0 +#define N_A_MSG "Needs at least one enabled ADC channel" +#endif + +#else // CYGFUN_KERNEL_API_C +#define N_A_MSG "Needs kernel C API" +#endif + +#else // CYGPKG_IO_ADC && CYGPKG_KERNEL +#define N_A_MSG "Needs Kernel and ADC support" +#endif + +#ifdef N_A_MSG +void +cyg_start( void ) +{ + CYG_TEST_INIT(); + CYG_TEST_NA(N_A_MSG); +} +#endif // N_A_MSG + + +// EOF can_tx.c + +//--------------------------------------------------------------------------- +// eof at91_adc_test.c diff --git a/ecos/packages/devs/adc/arm/lpc24xx/current/ChangeLog b/ecos/packages/devs/adc/arm/lpc24xx/current/ChangeLog new file mode 100755 index 0000000..b2b727f --- /dev/null +++ b/ecos/packages/devs/adc/arm/lpc24xx/current/ChangeLog @@ -0,0 +1,30 @@ +2008-11-01 Uwe Kindler <uwe_kindler@web.de> + + * LPC24xx ADC driver package created + * cdl/adc_lpc24xx.cdl + * src/adc_lpc24xx.c + * tests/lpc24xx_adc_test.c + +//=========================================================================== +// ####GPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008 Free Software Foundation, Inc. +// +// This program 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. +// +// This program 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 this program; if not, write to the +// Free Software Foundation, Inc., 51 Franklin Street, +// Fifth Floor, Boston, MA 02110-1301, USA. +// ------------------------------------------- +// ####GPLCOPYRIGHTEND#### +//=========================================================================== diff --git a/ecos/packages/devs/adc/arm/lpc24xx/current/cdl/adc_lpc24xx.cdl b/ecos/packages/devs/adc/arm/lpc24xx/current/cdl/adc_lpc24xx.cdl new file mode 100755 index 0000000..75c737d --- /dev/null +++ b/ecos/packages/devs/adc/arm/lpc24xx/current/cdl/adc_lpc24xx.cdl @@ -0,0 +1,160 @@ +# ==================================================================== +# +# adc_lpc24xx.cdl +# +# eCos LPC24xx ADC configuration data +# +# ==================================================================== +## ####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2008 Free Software Foundation, Inc. +## +## 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., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +## +## 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): Uwe Kindler <uwe_kindler@web.de> +# Contributors: +# Date: 2008-09-24 +# +#####DESCRIPTIONEND#### +# +# ==================================================================== + + +cdl_package CYGPKG_DEVS_ADC_ARM_LPC24XX { + display "ADC hardware device driver for LPC24xx family of ARM controllers" + + parent CYGPKG_IO_ADC_DEVICES + active_if CYGPKG_IO_ADC_DEVICES + active_if CYGPKG_HAL_ARM_LPC24XX + requires {CYGNUM_IO_ADC_SAMPLE_SIZE <= 10} + requires {CYGNUM_IO_ADC_SAMPLE_SIZE >= 3} + description " + This package provides a generic ADC device driver for the on-chip + ADC peripherals in LPX24xx processors." + + include_dir cyg/io + compile -library=libextras.a adc_lpc24xx.c + + cdl_interface CYGINT_DEVS_ADC_ARM_LPC24XX_CHANNELS { + display "Number of ADC channels" + } + + cdl_option CYGPKG_DEVS_ADC_ARM_LPC24XX_DEBUG_LEVEL { + display "Driver debug output level" + flavor data + legal_values {0 1 2} + default_value 0 + description " + This option specifies the level of debug data output by + the LPC24XX ADC device driver. A value of 0 signifies + no debug data output; 1 signifies normal debug data + output. If an overrun occurred then this can only be + detected by debug output messages." + } + + cdl_option CYGNUM_DEVS_ADC_ARM_LPC24XX_INTPRIO { + display "Interrupt priority" + flavor data + legal_values 0 to 15 + default_value 15 + description " + This option selects the interrupt priority for the ADC + interrupts. Timer 1 is used for generating the sample + clock. So this option configures the interrupt priority + for timer 1. There are 16 priority levels corresponding to + the values 0 through 15 decimal, of which 15 is the lowest + priority. The reset value of these registers defaults all + interrupts to the lowest priority, allowing a single write + to elevate the priority of an individual interrupt." + } + + + cdl_option CYGNUM_DEVS_ADC_ARM_LPC24XX_DEFAULT_RATE { + display "Default sample rate" + flavor data + legal_values 1 to 10000 + default_value 100 + description " + The driver will be initialized with the default sample rate. + If you raise the default sample rate you might need to increase + the buffer size for each channel." + } + + # Support up to 8 ADC channels + for { set ::channel 0 } { $::channel < 8 } { incr ::channel } { + + cdl_component CYGPKG_DEVS_ADC_ARM_LPC24XX_CHANNEL[set ::channel] { + display "Access ADC channel [set ::channel]" + flavor bool + default_value [set ::channel] == 0 + implements CYGINT_DEVS_ADC_ARM_LPC24XX_CHANNELS + description " + If the application needs to access the on-chip ADC + channel [set ::channel] via an eCos ADC driver then + this option should be enabled." + + cdl_option CYGDAT_DEVS_ADC_ARM_LPC24XX_CHANNEL[set ::channel]_NAME { + display "Device name" + flavor data + default_value [format {"\"/dev/adc0%d\""} $::channel] + description " + This option controls the name that an eCos application + should use to access this device via cyg_io_lookup(), + open(), or similar calls." + } + + cdl_option CYGDAT_DEVS_ADC_ARM_LPC24XX_CHANNEL[set ::channel]_BUFSIZE { + display "Size of data buffer" + flavor data + legal_values 0x01 to 0x2000000 + default_value 512 + description " + This option controls the number of samples the + buffer can store. The required RAM depends on the + sample size and on the number of samples. If the + sample size is <= 8 bit the the required RAM = + size of data buffer. If the sample size is 9 or 10 + bit then required RAM = size of data buffer * 2." + } + } + } + + cdl_option CYGPKG_DEVS_ADC_ARM_LPC24XX_TESTS { + display "Tests for LPC24xx ADC driver" + flavor data + no_define + calculated { "tests/lpc24xx_adc_test" } + description " + This option specifies the set of tests for the LPC24xx + ADC device driver." + } + +} diff --git a/ecos/packages/devs/adc/arm/lpc24xx/current/src/adc_lpc24xx.c b/ecos/packages/devs/adc/arm/lpc24xx/current/src/adc_lpc24xx.c new file mode 100755 index 0000000..5f8863f --- /dev/null +++ b/ecos/packages/devs/adc/arm/lpc24xx/current/src/adc_lpc24xx.c @@ -0,0 +1,509 @@ +//========================================================================== +// +// adc_lpc24xx.c +// +// ADC driver for LPC24xx on chip ADC +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008 Free Software Foundation, Inc. +// +// 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): Uwe Kindler <uwe_kindler@web.de> +// Contributors: +// Date: 2008-09-21 +// Purpose: +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + + +//========================================================================== +// INCLUDES +//========================================================================== +#include <pkgconf/system.h> +#include <pkgconf/devs_adc_arm_lpc24xx.h> + +#include <cyg/infra/cyg_type.h> +#include <cyg/infra/cyg_ass.h> +#include <cyg/infra/diag.h> +#include <cyg/io/adc.h> +#include <cyg/hal/hal_arch.h> +#include <cyg/hal/hal_io.h> +#include <cyg/hal/hal_intr.h> +#include <cyg/hal/drv_api.h> + + + +//========================================================================== +// DEFINES +//========================================================================== + +//-------------------------------------------------------------------------- +// Register definition +// +#define ADC_BASE CYGARC_HAL_LPC24XX_REG_AD_BASE +#define ADC_CR (ADC_BASE + 0x0000) +#define ADC_GDR (ADC_BASE + 0x0004) +#define ADC_INTEN (ADC_BASE + 0x000C) +#define ADC_DR(_chan_) (ADC_BASE + 0x0010 + ((_chan_) << 2)) +#define ADC_STAT (ADC_BASE + 0x0030) + +#define DR_OVR (0x01 << 30) +#define DR_DONE (0x01 << 31) +#define CR_BURST (0x01 << 16) +#define CR_PDN (0x01 << 21) + + +#if CYGPKG_DEVS_ADC_ARM_LPC24XX_DEBUG_LEVEL > 0 + #define debug1_printf(args...) diag_printf(args) +#else + #define debug1_printf(args...) +#endif + +#define LPC2XXX_CHAN_CNT 8 // maximum number of channels for LPC2xxx device + + +//========================================================================== +// DATA TYPES +//========================================================================== +typedef struct lpc2xxx_adc_info +{ + cyg_uint32 base; // base address of ADC peripheral + cyg_vector_t vector; // interrupt vector number + int intprio; // interrupt priority of ADC interrupt + cyg_handle_t int_handle; // For initializing the interrupt + cyg_interrupt int_data; + struct cyg_adc_channel* channel[LPC2XXX_CHAN_CNT]; // stores references to + // channel objects +#if CYGPKG_DEVS_ADC_ARM_LPC24XX_DEBUG_LEVEL > 1 + cyg_uint32 isr_cnt; // number of ISR = number of samples + cyg_uint32 zero_time; +#endif // CYGPKG_DEVS_ADC_ARM_LPC24XX_DEBUG_LEVEL > 1 + cyg_uint8 chan_mask; // mask that indicates channels used + // by ADC driver +} lpc2xxx_adc_info; + + +//========================================================================== +// DECLARATIONS +//========================================================================== +static bool lpc2xxx_adc_init(struct cyg_devtab_entry *tab); +static Cyg_ErrNo lpc2xxx_adc_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name); +static void lpc2xxx_adc_enable( cyg_adc_channel *chan ); +static void lpc2xxx_adc_disable( cyg_adc_channel *chan ); +static void lpc2xxx_adc_set_rate( cyg_adc_channel *chan, cyg_uint32 rate ); +static cyg_uint32 lpc2xxx_adc_isr(cyg_vector_t vector, cyg_addrword_t data); +static void lpc2xxx_adc_dsr(cyg_vector_t vector, + cyg_ucount32 count, + cyg_addrword_t data); + + +//========================================================================== +// Instantiate data structures + +// ------------------------------------------------------------------------- +// Driver functions: +CYG_ADC_FUNCTIONS( lpc2xxx_adc_funs, + lpc2xxx_adc_enable, + lpc2xxx_adc_disable, + lpc2xxx_adc_set_rate ); + +// ------------------------------------------------------------------------- +// Device instance: +static lpc2xxx_adc_info lpc2xxx_adc_info0 = +{ + .base = CYGARC_HAL_LPC2XXX_REG_AD_BASE, + .vector = CYGNUM_HAL_INTERRUPT_TIMER1, + .intprio = CYGNUM_DEVS_ADC_ARM_LPC24XX_INTPRIO, + .int_handle = 0, +#if CYGPKG_DEVS_ADC_ARM_LPC24XX_DEBUG_LEVEL > 0 + .isr_cnt = 0, +#endif + .chan_mask = 0 +}; + +CYG_ADC_DEVICE( lpc2xxx_adc_device, + &lpc2xxx_adc_funs, + &lpc2xxx_adc_info0, + CYGNUM_DEVS_ADC_ARM_LPC24XX_DEFAULT_RATE); + +// ------------------------------------------------------------------------- +// Channel instances: + +#define LPC2XXX_ADC_CHANNEL( __chan ) \ +CYG_ADC_CHANNEL( lpc2xxx_adc_channel##__chan, \ + __chan, \ + CYGDAT_DEVS_ADC_ARM_LPC24XX_CHANNEL##__chan##_BUFSIZE, \ + &lpc2xxx_adc_device ); \ + \ +DEVTAB_ENTRY( lpc2xxx_adc_channel##__chan##_device, \ + CYGDAT_DEVS_ADC_ARM_LPC24XX_CHANNEL##__chan##_NAME, \ + 0, \ + &cyg_io_adc_devio, \ + lpc2xxx_adc_init, \ + lpc2xxx_adc_lookup, \ + &lpc2xxx_adc_channel##__chan ); + +#ifdef CYGPKG_DEVS_ADC_ARM_LPC24XX_CHANNEL0 +LPC2XXX_ADC_CHANNEL(0); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_LPC24XX_CHANNEL1 +LPC2XXX_ADC_CHANNEL(1); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_LPC24XX_CHANNEL2 +LPC2XXX_ADC_CHANNEL(2); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_LPC24XX_CHANNEL3 +LPC2XXX_ADC_CHANNEL(3); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_LPC24XX_CHANNEL4 +LPC2XXX_ADC_CHANNEL(4); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_LPC24XX_CHANNEL5 +LPC2XXX_ADC_CHANNEL(5); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_LPC24XX_CHANNEL6 +LPC2XXX_ADC_CHANNEL(6); +#endif +#ifdef CYGPKG_DEVS_ADC_ARM_LPC24XX_CHANNEL7 +LPC2XXX_ADC_CHANNEL(7); +#endif + +//========================================================================== +// 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 and interrupt object, then care should +// be taken to do this. This function should also call cyg_adc_device_init() +// to initialize the generic parts of the driver. +//========================================================================== +static bool lpc2xxx_adc_init(struct cyg_devtab_entry *tab) +{ + cyg_adc_channel *chan = (cyg_adc_channel *)tab->priv; + cyg_adc_device *device = chan->device; + lpc2xxx_adc_info *info = device->dev_priv; + + if (!info->int_handle) + { + cyg_drv_interrupt_create(info->vector, + info->intprio, + (cyg_addrword_t)device, + &lpc2xxx_adc_isr, + &lpc2xxx_adc_dsr, + &(info->int_handle), + &(info->int_data)); + cyg_drv_interrupt_attach(info->int_handle); + cyg_drv_interrupt_unmask(info->vector); + + // + // The APB clock (PCLK) is divided by (this value plus one) to produce + // the clock for the A/D converter, which should be less than or equal + // to 4.5 MHz. Typically, software should program the smallest value in + // this field that yields a clock of 4.5 MHz or slightly less, but in + // certain cases (such as a high-impedance analog source) a slower + // clock may be desirable. + // Set clock division factor so ADC clock is <= 4.5 MHz + // + cyg_uint8 clkdiv = CYGNUM_HAL_ARM_LPC24XX_ADC_CLK / 4500001; + + // + // Enable A/D converter and setup the configured sample size + // The eCos ADC I/O manual says: Channels are initialized in a disabled + // state and generate no samples - let's do this now + // We initialize the device to operate in burst mode and we enable + // conversion for all channels here + // + HAL_WRITE_UINT32(ADC_INTEN, 0); // disables all interrupts + HAL_WRITE_UINT32(ADC_CR, CR_BURST // burst mode + | CR_PDN // A/D converter is operational + | 0xFF // enable all channels + | ((10 - CYGNUM_IO_ADC_SAMPLE_SIZE) << 17) + | (clkdiv << 8));// set clock divider + + // + // setup the default sample rate + // + lpc2xxx_adc_set_rate(chan, chan->device->config.rate); + } // if (!info->int_handle) + + cyg_adc_device_init(device); // initialize generic parts of driver + return true; +} + + +//========================================================================== +// This function is called when a client looks up or opens a channel. It +// should call cyg_adc_channel_init() to initialize the generic part of +// the channel. It should also perform any operations needed to start the +// channel generating samples. +//========================================================================== +static Cyg_ErrNo lpc2xxx_adc_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name) +{ + typedef struct adc_pin_cfg_st + { + cyg_uint8 port; + cyg_uint8 pin; + cyg_uint8 func; + } adc_pin_cfg_t; + static const adc_pin_cfg_t acd_pin_cfg_tbl[] = + { + {0, 23, 1}, + {0, 24, 1}, + {0, 25, 1}, + {0, 26, 1}, + {1, 30, 3}, + {1, 31, 3}, + {0, 12, 3}, + {0, 13, 3}, + }; + cyg_adc_channel *chan = (cyg_adc_channel *)(*tab)->priv; + lpc2xxx_adc_info *info = chan->device->dev_priv; + adc_pin_cfg_t *pin_cfg = (adc_pin_cfg_t *)&acd_pin_cfg_tbl[chan->channel]; + + // + // This ADC driver is quite LP24xx specific. The pin function of each pin + // is well defined in the LP24xx specification. Therefore we can setup + // the pin function here. If someone decides that this driver can be used + // by other LPC2xxx or LPC3xxx variants too and that the driver should + // become more generic, then we might need to move the pin configuration + // out of this driver an into the variant / platform HAL + // + CYG_HAL_ARM_LPC24XX_PIN_CFG(pin_cfg->port, pin_cfg->pin, pin_cfg->func); + info->channel[chan->channel] = chan; + cyg_adc_channel_init(chan); // initialize generic parts of channel + + // + // The generic ADC manual says: When a channel is first looked up or + // opened, then it is automatically enabled and samples start to + // accumulate - so we start the channel now + // + chan->enabled = true; + lpc2xxx_adc_enable(chan); + + return ENOERR; +} + + +//========================================================================== +// This function is called from the generic ADC package to enable the +// channel in response to a CYG_IO_SET_CONFIG_ADC_ENABLE config operation. +// It should take any steps needed to start the channel generating samples +//========================================================================== +static void lpc2xxx_adc_enable(cyg_adc_channel *chan) +{ + cyg_uint32 regval; + lpc2xxx_adc_info *info = chan->device->dev_priv; + + // + // Enable interrupts for timer to start generation of samples in timer + // ISR if this is the first channel that is enabled. If there are + // already some channels enabled, then the interrupt is already enabled + // + if (!info->chan_mask) + { + HAL_READ_UINT32(CYGARC_HAL_LPC24XX_REG_TIMER1_BASE + + CYGARC_HAL_LPC24XX_REG_TxMCR, regval); +#if CYGPKG_DEVS_ADC_ARM_LPC24XX_DEBUG_LEVEL > 0 + info->zero_time = cyg_current_time() * 10; +#endif + regval |= CYGARC_HAL_LPC24XX_REG_TxMCR_MR0_INT; + HAL_WRITE_UINT32(CYGARC_HAL_LPC24XX_REG_TIMER1_BASE + + CYGARC_HAL_LPC24XX_REG_TxMCR, regval); + } + + info->chan_mask |= (0x01 << chan->channel); +} + + +//========================================================================== +// This function is called from the generic ADC package to enable the +// channel in response to a CYG_IO_SET_CONFIG_ADC_DISABLE config operation. +// It should take any steps needed to stop the channel generating samples. +//========================================================================== +static void lpc2xxx_adc_disable(cyg_adc_channel *chan) +{ + cyg_uint32 regval; + lpc2xxx_adc_info *info = chan->device->dev_priv; + + info->chan_mask &= ~(0x01 << chan->channel); + + // + // If no channel is enabled the we disable interrupts now + // + if (!info->chan_mask) + { + HAL_READ_UINT32(CYGARC_HAL_LPC24XX_REG_TIMER1_BASE + + CYGARC_HAL_LPC24XX_REG_TxMCR, regval); + regval &= ~CYGARC_HAL_LPC24XX_REG_TxMCR_MR0_INT; + HAL_WRITE_UINT32(CYGARC_HAL_LPC24XX_REG_TIMER1_BASE + + CYGARC_HAL_LPC24XX_REG_TxMCR, regval); + } +} + + +//========================================================================== +// This function is called from the generic ADC package to enable the +// channel in response to a CYG_IO_SET_CONFIG_ADC_RATE config operation. +// It should take any steps needed to change the sample rate of the channel, +// or of the entire device. +// We use a timer channel to generate the interrupts for sampling the +// analog channels +//========================================================================== +static void lpc2xxx_adc_set_rate( cyg_adc_channel *chan, cyg_uint32 rate) +{ + cyg_adc_device *device = chan->device; + cyg_uint32 regval; + + cyg_uint32 tmr_period = hal_lpc_get_pclk(CYNUM_HAL_LPC24XX_PCLK_TIMER1) / + rate; + device->config.rate = rate; + + // + // Disable and reset counter, set prescale register to 0 and + // Set up match register + // + HAL_WRITE_UINT32(CYGARC_HAL_LPC24XX_REG_TIMER1_BASE + + CYGARC_HAL_LPC24XX_REG_TxTCR, + CYGARC_HAL_LPC24XX_REG_TxTCR_CTR_RESET); + HAL_WRITE_UINT32(CYGARC_HAL_LPC24XX_REG_TIMER1_BASE + + CYGARC_HAL_LPC24XX_REG_TxPR, 0); + HAL_WRITE_UINT32(CYGARC_HAL_LPC24XX_REG_TIMER1_BASE + + CYGARC_HAL_LPC24XX_REG_TxMR0, tmr_period); + // + // Reset on match and Enable counter + // + HAL_READ_UINT32(CYGARC_HAL_LPC24XX_REG_TIMER1_BASE + + CYGARC_HAL_LPC24XX_REG_TxMCR, regval); + regval |= CYGARC_HAL_LPC24XX_REG_TxMCR_MR0_RESET; // reset on match + regval &= ~CYGARC_HAL_LPC24XX_REG_TxMCR_MR0_STOP; // do not stop on match + HAL_WRITE_UINT32(CYGARC_HAL_LPC24XX_REG_TIMER1_BASE + + CYGARC_HAL_LPC24XX_REG_TxMCR, regval); + HAL_WRITE_UINT32(CYGARC_HAL_LPC24XX_REG_TIMER1_BASE + + CYGARC_HAL_LPC24XX_REG_TxTCR, + CYGARC_HAL_LPC24XX_REG_TxTCR_CTR_ENABLE); +} + + +//========================================================================== +// 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 +// cyg_adc_receive_sample() for each new sample available, and then ready the +// device for the next interrupt. +//========================================================================== +static cyg_uint32 lpc2xxx_adc_isr(cyg_vector_t vector, cyg_addrword_t data) +{ + cyg_adc_device *device = (cyg_adc_device *) data; + lpc2xxx_adc_info *info = (lpc2xxx_adc_info *)device->dev_priv; + cyg_uint32 regval; + cyg_uint32 res = 0; + cyg_adc_sample_t adcdata; + +#if CYGPKG_DEVS_ADC_ARM_LPC24XX_DEBUG_LEVEL > 1 + // + // Print debug information for channel 1 - this is the channel that + // triggers the interrupt and that is used for measuring lost samples + // + if (!(++info->isr_cnt % device->config.rate)) + { + cyg_uint32 current_time_ms = cyg_current_time() * 10; + debug1_printf("ms %d smpl. %d\n", + current_time_ms - info->zero_time, info->isr_cnt); + info->zero_time = current_time_ms; + } // if (!(info->isr_count % device->config.rate)) +#endif // CYGPKG_DEVS_ADC_ARM_LPC24XX_DEBUG_LEVEL > 1 + + cyg_uint8 active_channels = info->chan_mask; + cyg_uint8 channel_no = 0; + while (active_channels) + { + if (active_channels & 0x01) + { + HAL_READ_UINT32(ADC_DR(channel_no), regval); + adcdata = (regval >> 6) & 0x3FF; + res |= CYG_ISR_HANDLED + | cyg_adc_receive_sample(info->channel[channel_no], + adcdata); + } // if (active_channels & 0x01) + active_channels >>= 1; + channel_no++; + } // while (active_channels) + + HAL_WRITE_UINT32(CYGARC_HAL_LPC24XX_REG_TIMER1_BASE + + CYGARC_HAL_LPC24XX_REG_TxIR, + CYGARC_HAL_LPC24XX_REG_TxIR_MR0); // Clear interrupt + cyg_drv_interrupt_acknowledge(info->vector); + return res; +} + + +//========================================================================== +// 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 +// CYG_ISR_HANDLED bit. It needs to call cyg_adc_wakeup() for each channel +// that has its wakeup field set. +//========================================================================== +static void lpc2xxx_adc_dsr(cyg_vector_t vector, + cyg_ucount32 count, + cyg_addrword_t data) +{ + cyg_adc_device *device = (cyg_adc_device *) data; + lpc2xxx_adc_info *info = device->dev_priv; + cyg_uint8 active_channels = info->chan_mask; + cyg_uint8 chan_no = 0; + + while (active_channels) + { + if (active_channels & 0x01) + { + if(info->channel[chan_no]->wakeup) + { + cyg_adc_wakeup(info->channel[chan_no]); + } + } + chan_no++; + active_channels >>= 1; + } +} + + +//--------------------------------------------------------------------------- +// eof adc_lpc24xx.c diff --git a/ecos/packages/devs/adc/arm/lpc24xx/current/tests/lpc24xx_adc_test.c b/ecos/packages/devs/adc/arm/lpc24xx/current/tests/lpc24xx_adc_test.c new file mode 100755 index 0000000..dae9531 --- /dev/null +++ b/ecos/packages/devs/adc/arm/lpc24xx/current/tests/lpc24xx_adc_test.c @@ -0,0 +1,300 @@ +//========================================================================== +// +// lpc24xx_adc_test.c +// +// ADC performance test for LPC24xxx +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008 Free Software Foundation, Inc. +// +// 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): Uwe Kindler +// Contributors: +// Date: 2008-11-01 +// Description: ADC performance test for LPC24xxx +//####DESCRIPTIONEND#### + + +//=========================================================================== +// INCLUDES +//=========================================================================== +#include <pkgconf/system.h> + +#include <cyg/infra/testcase.h> // test macros +#include <cyg/infra/cyg_ass.h> // assertion macros +#include <cyg/infra/diag.h> +#include <cyg/hal/hal_diag.h> +#include <cyg/hal/hal_arch.h> // CYGNUM_HAL_STACK_SIZE_TYPICAL + +// Package requirements +#if defined(CYGPKG_IO_ADC) && defined(CYGPKG_KERNEL) + +#include <pkgconf/kernel.h> +#include <cyg/io/io.h> +#include <cyg/io/adc.h> +#include <pkgconf/devs_adc_arm_lpc24xx.h> + +// Package option requirements +#if defined(CYGFUN_KERNEL_API_C) + +#include <cyg/kernel/kapi.h> + +#if CYGINT_DEVS_ADC_ARM_LPC24XX_CHANNELS > 0 + +//=========================================================================== +// DATA TYPES +//=========================================================================== +typedef struct st_thread_data +{ + cyg_thread obj; + long stack[CYGNUM_HAL_STACK_SIZE_TYPICAL]; + cyg_handle_t hdl; +} thread_data_t; + + +//=========================================================================== +// LOCAL DATA +//=========================================================================== +cyg_thread_entry_t adc_thread; +thread_data_t adc_thread_data; + + +//=========================================================================== +// ADC THREAD +//=========================================================================== +void adc_thread(cyg_addrword_t data) +{ + int res; + cyg_io_handle_t handle[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + cyg_uint32 sample_cnt[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + cyg_uint32 cfg_data; + cyg_uint32 len; + cyg_uint32 start_time; + cyg_uint32 end_time; + int i; + cyg_uint8 seconds = 0; + float final_seconds; + cyg_uint32 samples_expected; + + + diag_printf("This test reads samples from all enabled ADC channels.\n" + "Each second the number of already acquired samples\n" + "will be printed. After 10 seconds all ADC channels\n" + "will be stopped and each ADC buffer will be read until\n" + "it is empty. If the number of acquired samples is much\n" + "smaller than the number of expected samples, then you\n" + "should lower the sample rate.\n\n"); + + // Get a handle for ADC device 0 channel 1 - 8 + res = cyg_io_lookup( "/dev/adc00", &handle[0]); + res = cyg_io_lookup( "/dev/adc01", &handle[1]); + res = cyg_io_lookup( "/dev/adc02", &handle[2]); + res = cyg_io_lookup( "/dev/adc03", &handle[3]); + res = cyg_io_lookup( "/dev/adc04", &handle[4]); + res = cyg_io_lookup( "/dev/adc05", &handle[5]); + res = cyg_io_lookup( "/dev/adc06", &handle[6]); + res = cyg_io_lookup( "/dev/adc07", &handle[7]); + + // + // switch all channels to non blocking + // + for (i = 0; i < 8; ++i) + { + if (handle[i]) + { + cfg_data = 0; + len = sizeof(cfg_data); + res = cyg_io_set_config(handle[i], + CYG_IO_SET_CONFIG_READ_BLOCKING, + &cfg_data, + &len); + if (ENOERR != res) + { + CYG_TEST_FAIL_FINISH("Error switching ADC channel to non blocking"); + } + sample_cnt[i] = 0; + } // if (handle[i]) + } // for (i = 0; i < 8; ++i) + + start_time = cyg_current_time(); + do + { + for (i = 0; i < 8; ++i) + { + if (handle[i]) + { + cyg_adc_sample_t sample; + + // read a sample from the channel + do + { + cyg_uint32 len = sizeof(sample); + res = cyg_io_read( handle[i], &sample, &len ); + } + while (-EAGAIN == res); + if (ENOERR == res) + { + sample_cnt[i]++; + } + } // if (handle[i]) + } + // + // print number of acquired samples - if one second is expired. + // we expect that the number of acquired samples is nearly the + // sample rate + // + end_time = cyg_current_time(); + if ((end_time - start_time) >= 100) + { + start_time = end_time; + diag_printf("%d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\n", + sample_cnt[0], + sample_cnt[1], + sample_cnt[2], + sample_cnt[3], + sample_cnt[4], + sample_cnt[5], + sample_cnt[6], + sample_cnt[7]); + seconds++; + } // if ((end_time - start_time) >= 100) + } while (seconds < 10); + + // + // Now stop all channels + // + for (i = 0; i < 8; ++i) + { + if (handle[i]) + { + res = cyg_io_set_config(handle[i], + CYG_IO_SET_CONFIG_ADC_DISABLE, + 0, + 0); + if (ENOERR != res) + { + CYG_TEST_FAIL_FINISH("Error disabling ADC channel"); + } + } // if (handle[i]) + } + end_time = cyg_current_time(); + end_time = seconds * 1000 + (end_time - start_time) * 10; + final_seconds = end_time / 1000.0; + + // + // Now read all remaining samples from buffer + // + for (i = 0; i < 8; ++i) + { + if (handle[i]) + { + do + { + cyg_adc_sample_t sample; + cyg_uint32 len = sizeof(sample); + res = cyg_io_read( handle[i], &sample, &len ); + if (ENOERR == res) + { + sample_cnt[i]++; + } + } while (ENOERR == res); + } // if (handle[i]) + } + + diag_printf("\n\n----------------------------------------\n"); + samples_expected = final_seconds * CYGNUM_DEVS_ADC_ARM_LPC24XX_DEFAULT_RATE; + diag_printf("Samples expected after %d milliseconds: %d\n", + end_time, samples_expected); + diag_printf("Samples read (per channel):\n"); + diag_printf("%d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\n", + sample_cnt[0], + sample_cnt[1], + sample_cnt[2], + sample_cnt[3], + sample_cnt[4], + sample_cnt[5], + sample_cnt[6], + sample_cnt[7]); + + CYG_TEST_PASS_FINISH("ADC test OK"); +} + + +void +cyg_start(void) +{ + CYG_TEST_INIT(); + + // + // create the main ADC test thread + // + cyg_thread_create(4, adc_thread, + (cyg_addrword_t) 0, + "lpc24xx_adc_thread", + (void *) adc_thread_data.stack, + 1024 * sizeof(long), + &adc_thread_data.hdl, + &adc_thread_data.obj); + + cyg_thread_resume(adc_thread_data.hdl); + + cyg_scheduler_start(); +} +#else // CYGINT_DEVS_ADC_ARM_LPC24XX_CHANNELS > 0 +#define N_A_MSG "Needs at least one enabled ADC channel" +#endif + +#else // CYGFUN_KERNEL_API_C +#define N_A_MSG "Needs kernel C API" +#endif + +#else // CYGPKG_IO_ADC && CYGPKG_KERNEL +#define N_A_MSG "Needs Kernel and ADC support" +#endif + +#ifdef N_A_MSG +void +cyg_start( void ) +{ + CYG_TEST_INIT(); + CYG_TEST_NA(N_A_MSG); +} +#endif // N_A_MSG + + +// EOF can_tx.c + +//--------------------------------------------------------------------------- +// eof i2c_test.c + diff --git a/ecos/packages/devs/adc/cortexm/lm3s/current/ChangeLog b/ecos/packages/devs/adc/cortexm/lm3s/current/ChangeLog new file mode 100644 index 0000000..baabea6 --- /dev/null +++ b/ecos/packages/devs/adc/cortexm/lm3s/current/ChangeLog @@ -0,0 +1,31 @@ +2011-01-18 Christophe Coutand <ccoutand@stmi.com> + + * cdl/adc_lm3s.cdl: + * src/adc_lm3s.c: + * include/adc_lm3s.inl: + * tests/lm3s_adc_test.c: + New package -- Stellaris Cortex M3 ADC driver package. + +//=========================================================================== +// ####GPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2011 Free Software Foundation, Inc. +// +// This program 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. +// +// This program 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 this program; if not, write to the +// Free Software Foundation, Inc., 51 Franklin Street, +// Fifth Floor, Boston, MA 02110-1301, USA. +// ------------------------------------------- +// ####GPLCOPYRIGHTEND#### +//=========================================================================== diff --git a/ecos/packages/devs/adc/cortexm/lm3s/current/cdl/adc_lm3s.cdl b/ecos/packages/devs/adc/cortexm/lm3s/current/cdl/adc_lm3s.cdl new file mode 100644 index 0000000..ab2b765 --- /dev/null +++ b/ecos/packages/devs/adc/cortexm/lm3s/current/cdl/adc_lm3s.cdl @@ -0,0 +1,217 @@ +# ==================================================================== +# +# adc_lm3s.cdl +# +# eCos Stellaris Cortex M3 ADC configuration data +# +# ==================================================================== +## ####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2011 Free Software Foundation, Inc. +## +## 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., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +## +## 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): ccoutand +# Contributors: +# Date: 2011-01-12 +# +#####DESCRIPTIONEND#### +# +# ==================================================================== + +cdl_package CYGPKG_DEVS_ADC_CORTEXM_LM3S { + display "ADC driver for Stellaris Cortex M3 microcontroller family" + + parent CYGPKG_IO_ADC_DEVICES + active_if CYGPKG_IO_ADC_DEVICES + active_if CYGPKG_HAL_CORTEXM_LM3S + include_dir cyg/io + description " + This package provides a generic ADC device driver for the on-chip + ADC peripherals in Stellaris microcontroller." + + compile -library=libextras.a adc_lm3s.c + + define_proc { + puts $::cdl_system_header "#define CYGDAT_DEVS_ADC_CORTEXM_LM3S_INL <cyg/io/adc_lm3s.inl>" + } + + # --------------------------------------------------------------------- + # Primary ADC ( ADC0 ) port + + cdl_component CYGPKG_DEVS_ADC_CORTEXM_LM3S_ADC0 { + display "ADC port 0" + flavor bool + default_value 1 + description " + This option includes the device driver for the on-chip ADC0 + of the Stellaris device." + + cdl_interface CYGINT_DEVS_ADC_CORTEXM_LM3S_ADC0_CHANNELS { + display "Number of ADC0 channels" + } + + cdl_option CYGNUM_DEVS_ADC_CORTEXM_LM3S_ADC0_SELECT_TIMER { + display "ADC sampling timer" + flavor data + legal_values { 0 1 2 } + default_value 0 + description " + This option selects the timer channel generating the + ADC sampling interval." + } + + cdl_option CYGNUM_DEVS_ADC_CORTEXM_LM3S_ADC0_INTPRIO { + display "ADC Interrupt priority" + flavor data + default_value 0x60 + description " + This option selects the interrupt priority for the ADC + interrupts." + } + + cdl_option CYGNUM_DEVS_ADC_CORTEXM_LM3S_ADC0_DEFAULT_RATE { + display "Default sample rate" + flavor data + legal_values 1 to 10000 + default_value 100 + description " + The driver will be initialized with the default sample + rate. If you raise the default sample rate you might + need to increase the buffer size for each channel." + } + + cdl_option CYGNUM_DEVS_ADC_CORTEXM_LM3S_ADC0_AVERAGING { + display "Sample averaging" + flavor data + legal_values { 0 2 4 8 16 32 64 } + default_value 0 + description " + Select the ADC sample averaging." + } + + cdl_component CYGPKG_DEVS_ADC_CORTEXM_LM3S_ADC0_TEMP_SENSOR { + display "Internal Temperature Sensor" + flavor bool + default_value 0 + description " + Enable one ADC channel for internal temperature sensor + reading." + + cdl_option CYGNUM_DEVS_ADC_CORTEXM_LM3S_ADC0_TEMP_SENSOR_CHANNEL { + display "Select sensor channel" + flavor data + legal_values { 0 to (CYGINT_DEVS_ADC_CORTEXM_LM3S_ADC0_CHANNELS-1) } + default_value 1 + description " + This option select ADC channel reserved for + temperature sensor reading." + } + } + + # ----------------------------------------------------------------- + # Support up to 8 ADC channels + + for { set ::channel 0 } { $::channel < 8 } { incr ::channel } { + cdl_component CYGPKG_DEVS_ADC_CORTEXM_LM3S_ADC0_CHANNEL[set ::channel] { + display "Access ADC channel [set ::channel]" + flavor bool + default_value [set ::channel] == 0 + implements CYGINT_DEVS_ADC_CORTEXM_LM3S_ADC0_CHANNELS + description " + If the application needs to access the on-chip ADC0 + channel [set ::channel] via an eCos ADC driver then + this option should be enabled." + + cdl_option CYGDAT_DEVS_ADC_CORTEXM_LM3S_ADC0_CHANNEL[set ::channel]_NAME { + display "Device name" + flavor data + default_value [format {"\"/dev/adc0%d\""} $::channel] + description " + This option controls the name that an eCos + application should use to access this device + via cyg_io_lookup(), open(), or similar calls." + } + + cdl_option CYGDAT_DEVS_ADC_CORTEXM_LM3S_ADC0_CHANNEL[set ::channel]_BUFSIZE { + display "Size of data buffer" + flavor data + legal_values 0x01 to 0x16 + default_value 0x4 + description " + This option controls the number of samples the + buffer can store. The required RAM depends on the + sample size and on the number of samples. If the + sample size is <= 8 bit the the required RAM = + size of data buffer. If the sample size is 9 or 10 + bit then required RAM = size of data buffer * 2." + } + } + } + } + + cdl_option CYGPKG_DEVS_ADC_CORTEXM_LM3S_DEBUG_LEVEL { + display "Driver debug output level" + flavor data + legal_values { 0 1 } + default_value 0 + description " + This option specifies the level of debug data output by + the Stellaris ADC device driver. A value of 0 signifies no + debug data output; 1 signifies normal debug data output. If + an overrun occurred then this can only be detected by debug + output messages." + } + + cdl_component CYGSEM_DEVS_ADC_CORTEXM_LM3S_SAMPLE_SIZE_LIMIT { + display "Sample size limit" + flavor bool + calculated 1 + requires { ( CYGNUM_IO_ADC_SAMPLE_SIZE == 10 ) } + description " + This component forces a limit (or rounds) the sample size + for Stellaris ADC channels which in the most are 10-bit." + } + + cdl_option CYGPKG_DEVS_ADC_CORTEXM_LM3S_TESTS { + display "Tests for Stellaris 800 Series ADC driver" + flavor data + no_define + active_if CYGPKG_KERNEL + active_if CYGPKG_IO_ADC + calculated { "tests/lm3s_adc_test" } + description " + This option specifies the set of tests for the Stellaris + ADC device driver." + } +} + +# EOF adc_lm3s.cdl diff --git a/ecos/packages/devs/adc/cortexm/lm3s/current/include/adc_lm3s.inl b/ecos/packages/devs/adc/cortexm/lm3s/current/include/adc_lm3s.inl new file mode 100644 index 0000000..655da59 --- /dev/null +++ b/ecos/packages/devs/adc/cortexm/lm3s/current/include/adc_lm3s.inl @@ -0,0 +1,140 @@ +//========================================================================== +// +// adc_lm3s.inl +// +// ADC driver for Stellaris Cortex M3 microcontroller +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2011 Free Software Foundation, Inc. +// +// 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): ccoutand +// +// Contributors: +// Date: 2011-01-08 +// Purpose: +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#ifndef CYGONCE_DEVS_ADC_CORTEXM_LM3S_INL +#define CYGONCE_DEVS_ADC_CORTEXM_LM3S_INL + +// Declare ADC0 +#ifdef CYGPKG_DEVS_ADC_CORTEXM_LM3S_ADC0 + +static lm3s_adc_info lm3s_adc0_info = +{ + .timer_base = CYGHWR_HAL_LM3S_GPTIM0 + ( 0x1000 * CYGNUM_DEVS_ADC_CORTEXM_LM3S_ADC0_SELECT_TIMER ), + .adc_intprio = CYGNUM_DEVS_ADC_CORTEXM_LM3S_ADC0_INTPRIO, + .adc_vector = CYGNUM_HAL_INTERRUPT_ADC0_S0, + .adc_base = CYGHWR_HAL_LM3S_ADC0, + .adc_periph = CYGHWR_HAL_LM3S_P_ADC0, + .timer_periph = (CYGHWR_HAL_LM3S_SC_RCGC1_TIMER0 << CYGNUM_DEVS_ADC_CORTEXM_LM3S_ADC0_SELECT_TIMER), +#if CYGNUM_DEVS_ADC_CORTEXM_LM3S_ADC0_AVERAGING == 0 + .adc_avg = 0, +#elif CYGNUM_DEVS_ADC_CORTEXM_LM3S_ADC0_AVERAGING == 2 + .adc_avg = 1, +#elif CYGNUM_DEVS_ADC_CORTEXM_LM3S_ADC0_AVERAGING == 4 + .adc_avg = 2, +#elif CYGNUM_DEVS_ADC_CORTEXM_LM3S_ADC0_AVERAGING == 8 + .adc_avg = 3, +#elif CYGNUM_DEVS_ADC_CORTEXM_LM3S_ADC0_AVERAGING == 16 + .adc_avg = 4, +#elif CYGNUM_DEVS_ADC_CORTEXM_LM3S_ADC0_AVERAGING == 32 + .adc_avg = 5, +#elif CYGNUM_DEVS_ADC_CORTEXM_LM3S_ADC0_AVERAGING == 64 + .adc_avg = 6, +#endif +#if defined(CYGPKG_DEVS_ADC_CORTEXM_LM3S_ADC0_TEMP_SENSOR) + .sensor_channel = CYGNUM_DEVS_ADC_CORTEXM_LM3S_ADC0_TEMP_SENSOR_CHANNEL, +#else + .sensor_channel = 0xff, +#endif + .max_channel = CYGINT_DEVS_ADC_CORTEXM_LM3S_ADC0_CHANNELS, + .int_handle = 0, + .chan_mask = 0 +}; + +CYG_ADC_DEVICE( \ + lm3s_adc0_device, \ + &lm3s_adc_funs, \ + &lm3s_adc0_info, \ + CYGNUM_DEVS_ADC_CORTEXM_LM3S_ADC0_DEFAULT_RATE); + +#define LM3S_ADC0_CHANNEL( __chan ) \ +CYG_ADC_CHANNEL( \ + lm3s_adc0_channel##__chan, \ + __chan, \ + CYGDAT_DEVS_ADC_CORTEXM_LM3S_ADC0_CHANNEL##__chan##_BUFSIZE, \ + &lm3s_adc0_device ); \ +DEVTAB_ENTRY( \ + lm3s_adc0_channel##__chan##_device, \ + CYGDAT_DEVS_ADC_CORTEXM_LM3S_ADC0_CHANNEL##__chan##_NAME, \ + 0, \ + &cyg_io_adc_devio, \ + lm3s_adc_init, \ + lm3s_adc_lookup, \ + &lm3s_adc0_channel##__chan ); + +#ifdef CYGPKG_DEVS_ADC_CORTEXM_LM3S_ADC0_CHANNEL0 +LM3S_ADC0_CHANNEL(0); +#endif +#ifdef CYGPKG_DEVS_ADC_CORTEXM_LM3S_ADC0_CHANNEL1 +LM3S_ADC0_CHANNEL(1); +#endif +#ifdef CYGPKG_DEVS_ADC_CORTEXM_LM3S_ADC0_CHANNEL2 +LM3S_ADC0_CHANNEL(2); +#endif +#ifdef CYGPKG_DEVS_ADC_CORTEXM_LM3S_ADC0_CHANNEL3 +LM3S_ADC0_CHANNEL(3); +#endif +#ifdef CYGPKG_DEVS_ADC_CORTEXM_LM3S_ADC0_CHANNEL4 +LM3S_ADC0_CHANNEL(4); +#endif +#ifdef CYGPKG_DEVS_ADC_CORTEXM_LM3S_ADC0_CHANNEL5 +LM3S_ADC0_CHANNEL(5); +#endif +#ifdef CYGPKG_DEVS_ADC_CORTEXM_LM3S_ADC0_CHANNEL6 +LM3S_ADC0_CHANNEL(6); +#endif +#ifdef CYGPKG_DEVS_ADC_CORTEXM_LM3S_ADC0_CHANNEL7 +LM3S_ADC0_CHANNEL(7); +#endif + +#endif // CYGPKG_DEVS_ADC_CORTEXM_LM3S_ADC0 + +#endif // CYGONCE_DEVS_ADC_CORTEXM_LM3S_INL diff --git a/ecos/packages/devs/adc/cortexm/lm3s/current/src/adc_lm3s.c b/ecos/packages/devs/adc/cortexm/lm3s/current/src/adc_lm3s.c new file mode 100644 index 0000000..027e9c1 --- /dev/null +++ b/ecos/packages/devs/adc/cortexm/lm3s/current/src/adc_lm3s.c @@ -0,0 +1,489 @@ +//========================================================================== +// +// adc_lm3s.c +// +// ADC driver for Stellaris Cortex M3 microcontroller +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2011 Free Software Foundation, Inc. +// +// 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): Uwe Kindler <uwe_kindler@web.de> +// Updated for Stellaris device, ccoutand +// Contributors: +// Date: 2011-01-08 +// Purpose: +// Description: +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== + + +//========================================================================== +// INCLUDES +//========================================================================== +#include <pkgconf/system.h> +#include <pkgconf/devs_adc_cortexm_lm3s.h> + +#include <cyg/infra/cyg_type.h> +#include <cyg/infra/cyg_ass.h> +#include <cyg/infra/diag.h> +#include <cyg/io/adc.h> +#include <cyg/hal/hal_arch.h> +#include <cyg/hal/hal_io.h> +#include <cyg/hal/hal_intr.h> +#include <cyg/hal/drv_api.h> + +#if CYGPKG_DEVS_ADC_CORTEXM_LM3S_DEBUG_LEVEL > 0 +# define lm3s_adc_diag(args...) diag_printf(args) +#else +# define lm3s_adc_diag(args...) +#endif + +#define CYGHWR_HAL_LM3S_ADC_MAX_CHAN 8 + +//========================================================================== +// DATA TYPES +//========================================================================== +typedef struct lm3s_adc_info { + cyg_uint32 adc_base; // ADC base address + cyg_uint32 adc_periph; // ADC peripheral mask + cyg_vector_t adc_vector; // Interrupt vector number + cyg_priority_t adc_intprio; // Interrupt priority of ADC interrupt + cyg_uint32 timer_base; // Base address of Timer peripheral + cyg_uint32 timer_interval; // Timer value + cyg_uint32 timer_periph; // Timer peripheral mask + cyg_uint8 sensor_channel; // Temperature sensor channel if any + cyg_uint8 max_channel; // Number of ADC channel + cyg_handle_t int_handle; // For initializing the interrupt + cyg_interrupt int_data; + cyg_uint8 adc_avg; // Sample averaging + // Stores references to channel objects + struct cyg_adc_channel *channel[CYGHWR_HAL_LM3S_ADC_MAX_CHAN]; + cyg_uint8 chan_mask; // Mask that indicates channels used + // by ADC driver +} lm3s_adc_info; + + +//========================================================================== +// DECLARATIONS +//========================================================================== +static bool lm3s_adc_init(struct cyg_devtab_entry *tab); +static Cyg_ErrNo lm3s_adc_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name); +static void lm3s_adc_enable(cyg_adc_channel * chan); +static void lm3s_adc_disable(cyg_adc_channel * chan); +static void lm3s_adc_set_rate(cyg_adc_channel * chan, cyg_uint32 rate); +static cyg_uint32 lm3s_adc_isr(cyg_vector_t vector, cyg_addrword_t data); +static void lm3s_adc_dsr(cyg_vector_t vector, + cyg_ucount32 count, cyg_addrword_t data); + +static void lm3s_adc_disable_sequencer0(cyg_uint32); +static void lm3s_adc_enable_sequencer0(cyg_uint32); +static void lm3s_adc_flush(cyg_uint32); +static void lm3s_adc_update_sequencer0(cyg_adc_channel *); + +// ------------------------------------------------------------------------- +// Driver functions: +CYG_ADC_FUNCTIONS( lm3s_adc_funs, + lm3s_adc_enable, + lm3s_adc_disable, + lm3s_adc_set_rate ); + + +#include CYGDAT_DEVS_ADC_CORTEXM_LM3S_INL // Instantiate ADCs + + +//========================================================================== +// +// The eCos Sellaris ADC drivers uses a single sequencer ( sequencer 0 ). +// The same sequencer is used to sample all channels. +// Sampling of the different channel is triggered from a timer interrupt. +// The ADC driver flexibility does not allow to trigger sampling from +// external GPIO or analog comparator event. It should be noted that enabling +// / disabling an ADC channel disturbs the sampling of other channels since it +// requires to stop sampling to re-organize the sequencer. Also the FIFO +// is flushed to ensure correct sample order out of the sequencer FIFO. +// +//========================================================================== +static bool +lm3s_adc_init(struct cyg_devtab_entry *tab) +{ + cyg_adc_channel *chan = (cyg_adc_channel *) tab->priv; + cyg_adc_device *device = chan->device; + lm3s_adc_info *info = device->dev_priv; + + lm3s_adc_diag("ADC: Init\n"); + + if (!info->int_handle) { + lm3s_adc_diag("ADC: IRQ vect %d, pri %d\n", + info->adc_vector, info->adc_intprio); + + cyg_drv_interrupt_create(info->adc_vector, + info->adc_intprio, + (cyg_addrword_t)device, + &lm3s_adc_isr, + &lm3s_adc_dsr, + &(info->int_handle), &(info->int_data)); + cyg_drv_interrupt_attach(info->int_handle); + cyg_drv_interrupt_mask(info->adc_vector); + + // Enable ADC and sampling timer peripheral + CYGHWR_HAL_LM3S_PERIPH_SET(info->adc_periph, 1); + CYGHWR_HAL_LM3S_PERIPH_SET((CYGHWR_HAL_LM3S_PERIPH_GC1 | info-> + timer_periph), 1); + + // Disable timer + HAL_WRITE_UINT32(info->timer_base + CYGHWR_HAL_LM3S_GPTIM_CTL, 0); + + // Disable / reset sequencer + HAL_WRITE_UINT32(info->adc_base + CYGHWR_HAL_LM3S_ADC_ACTSS, 0); + HAL_WRITE_UINT32(info->adc_base + CYGHWR_HAL_LM3S_ADC_SS_MUX0, 0); + HAL_WRITE_UINT32(info->adc_base + CYGHWR_HAL_LM3S_ADC_SS_CTL0, 0); + + // Trigger sampling from timer + HAL_WRITE_UINT32(info->adc_base + CYGHWR_HAL_LM3S_ADC_EMUX, + CYGHWR_HAL_LM3S_ADC_EMUX_EM_TIMER(0)); + + // Set Averaging + HAL_WRITE_UINT32(info->adc_base + CYGHWR_HAL_LM3S_ADC_SAC, + info->adc_avg); + + // Setup timer + HAL_WRITE_UINT32(info->timer_base + CYGHWR_HAL_LM3S_GPTIM_CFG, + CYGHWR_HAL_LM3S_GPTIM_CFG_32BIT); + HAL_WRITE_UINT32(info->timer_base + CYGHWR_HAL_LM3S_GPTIM_TAMR, + CYGHWR_HAL_LM3S_GPTIM_TAMR_PERIODIC); + + // Setup the default sample rate + lm3s_adc_set_rate(chan, chan->device->config.rate); + + } + + // Initialize generic parts of driver + cyg_adc_device_init(device); + + return true; +} + + +//========================================================================== +// This function is called when a client looks up or opens a channel. It +// should call cyg_adc_channel_init() to initialize the generic part of +// the channel. It should also perform any operations needed to start the +// channel generating samples. +//========================================================================== +static Cyg_ErrNo +lm3s_adc_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, const char *name) +{ + cyg_adc_channel *chan = (cyg_adc_channel *) (*tab)->priv; + lm3s_adc_info *info = chan->device->dev_priv; + + lm3s_adc_diag("ADC: Opening channel %d\n", chan->channel); + + if (chan->channel > info->max_channel) + return ENOENT; + + info->channel[chan->channel] = chan; + + // Initialize generic parts of channel + cyg_adc_channel_init(chan); + + // The generic ADC manual says: When a channel is first looked up or + // opened, then it is automatically enabled and samples start to + // accumulate - so we start the channel now + chan->enabled = true; + lm3s_adc_enable(chan); + + return ENOERR; +} + + +//========================================================================== +// This function is called from the generic ADC package to enable the +// channel in response to a CYG_IO_SET_CONFIG_ADC_ENABLE config operation. +// It should take any steps needed to start the channel generating samples +//========================================================================== +static void +lm3s_adc_enable(cyg_adc_channel * chan) +{ + lm3s_adc_info *info = chan->device->dev_priv; + cyg_uint32 ctl = + CYGHWR_HAL_LM3S_GPTIM_CTL_TAEN | CYGHWR_HAL_LM3S_GPTIM_CTL_TAOTE; + cyg_uint32 start = !info->chan_mask; + + // Disable ADC sequencer 0 and timer + HAL_WRITE_UINT32(info->timer_base + CYGHWR_HAL_LM3S_GPTIM_CTL, 0); + lm3s_adc_disable_sequencer0(info->adc_base); + + // Update sequencer + info->chan_mask |= (1 << chan->channel); + lm3s_adc_update_sequencer0(chan); + + // Unmask interrupt as soon as 1 channel is enable + if (start) { + cyg_drv_interrupt_unmask(info->adc_vector); + } + // Enable sequencer and timer + lm3s_adc_enable_sequencer0(info->adc_base); + HAL_WRITE_UINT32(info->timer_base + CYGHWR_HAL_LM3S_GPTIM_CTL, ctl); +} + + +//========================================================================== +// This function is called from the generic ADC package to enable the +// channel in response to a CYG_IO_SET_CONFIG_ADC_DISABLE config operation. +// It should take any steps needed to stop the channel generating samples. +//========================================================================== +static void +lm3s_adc_disable(cyg_adc_channel * chan) +{ + lm3s_adc_info *info = chan->device->dev_priv; + cyg_uint32 ctl = + CYGHWR_HAL_LM3S_GPTIM_CTL_TAEN | CYGHWR_HAL_LM3S_GPTIM_CTL_TAOTE; + + // Disable ADC sequencer 0 and timer + HAL_WRITE_UINT32(info->timer_base + CYGHWR_HAL_LM3S_GPTIM_CTL, 0); + lm3s_adc_disable_sequencer0(info->adc_base); + + // Update sequencer + info->chan_mask &= ~(1 << chan->channel); + lm3s_adc_update_sequencer0(chan); + + // Stop scanning when no channel is active + if (!info->chan_mask) { + cyg_drv_interrupt_mask(info->adc_vector); + return; + } + // Enable sequencer and timer + lm3s_adc_enable_sequencer0(info->adc_base); + HAL_WRITE_UINT32(info->timer_base + CYGHWR_HAL_LM3S_GPTIM_CTL, ctl); +} + + +//========================================================================== +// This function is called from the generic ADC package to enable the +// channel in response to a CYG_IO_SET_CONFIG_ADC_RATE config operation. +// It should take any steps needed to change the sample rate of the channel, +// or of the entire device. +// We use a timer channel to generate the interrupts for sampling the +// analog channels +//========================================================================== +static void +lm3s_adc_set_rate(cyg_adc_channel * chan, cyg_uint32 rate) +{ + cyg_adc_device *device = chan->device; + lm3s_adc_info *info = (lm3s_adc_info *) device->dev_priv; + + info->timer_interval = hal_lm3s_timer_clock() / rate; + + lm3s_adc_diag("ADC: Timer interval %d\n", info->timer_interval); + + HAL_WRITE_UINT32(info->timer_base + CYGHWR_HAL_LM3S_GPTIM_TAILR, + info->timer_interval); +} + + +//========================================================================== +// 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 +// cyg_adc_receive_sample() for each new sample available, and then ready the +// device for the next interrupt. +//========================================================================== +static cyg_uint32 +lm3s_adc_isr(cyg_vector_t vector, cyg_addrword_t data) +{ + cyg_adc_device *device = (cyg_adc_device *) data; + lm3s_adc_info *info = (lm3s_adc_info *) device->dev_priv; + cyg_uint32 regval; + cyg_uint32 res = 0; + cyg_adc_sample_t adcdata; + cyg_uint32 sr; + + cyg_uint8 active_channels = info->chan_mask; + cyg_uint8 channel_no = 0; + + while (active_channels) { + HAL_READ_UINT32(info->adc_base + CYGHWR_HAL_LM3S_ADC_SS_FIFO0_SR, sr); + // Check FIFO Full + if ((sr & CYGHWR_HAL_LM3S_ADC_SS_FIFO_SR_FULL)) { + lm3s_adc_diag("ADC: FIFO Full\n"); + } + // Check FIFO Empty + if ((sr & CYGHWR_HAL_LM3S_ADC_SS_FIFO_SR_EMPTY)) { + lm3s_adc_diag("ADC: FIFO Empty\n"); + } + if (active_channels & 0x01) { + // If ADC conversion done, save sample + if (!(sr & CYGHWR_HAL_LM3S_ADC_SS_FIFO_SR_EMPTY)) { + HAL_READ_UINT32(info->adc_base + CYGHWR_HAL_LM3S_ADC_SS_FIFO0, + regval); + adcdata = regval & 0x3FF; + res |= CYG_ISR_HANDLED + | cyg_adc_receive_sample(info->channel[channel_no], + adcdata); + } + } + active_channels >>= 1; + channel_no++; + } + + HAL_WRITE_UINT32(info->adc_base + CYGHWR_HAL_LM3S_ADC_ISCR, + CYGHWR_HAL_LM3S_ADC_ISCR_IN(0)); + + cyg_drv_interrupt_acknowledge(info->adc_vector); + + return res; +} + + +//========================================================================== +// 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 +// CYG_ISR_HANDLED bit. It needs to call cyg_adc_wakeup() for each channel +// that has its wakeup field set. +//========================================================================== +static void +lm3s_adc_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) +{ + cyg_adc_device *device = (cyg_adc_device *) data; + lm3s_adc_info *info = device->dev_priv; + cyg_uint8 active_channels = info->chan_mask; + cyg_uint8 chan_no = 0; + + while (active_channels) { + if (active_channels & 0x01) { + if (info->channel[chan_no]->wakeup) { + cyg_adc_wakeup(info->channel[chan_no]); + } + } + chan_no++; + active_channels >>= 1; + } +} + + +static void +lm3s_adc_disable_sequencer0(cyg_uint32 base) +{ + cyg_uint32 reg; + + HAL_WRITE_UINT32(base + CYGHWR_HAL_LM3S_ADC_IMR, 0); + HAL_WRITE_UINT32(base + CYGHWR_HAL_LM3S_ADC_ISCR, + CYGHWR_HAL_LM3S_ADC_ISCR_IN(0)); + HAL_READ_UINT32(base + CYGHWR_HAL_LM3S_ADC_ACTSS, reg); + reg &= ~(CYGHWR_HAL_LM3S_ADC_ACTSS_ASEN(0)); + HAL_WRITE_UINT32(base + CYGHWR_HAL_LM3S_ADC_ACTSS, reg); +} + + +static void +lm3s_adc_enable_sequencer0(cyg_uint32 base) +{ + cyg_uint32 reg; + + HAL_READ_UINT32(base + CYGHWR_HAL_LM3S_ADC_ACTSS, reg); + reg |= (CYGHWR_HAL_LM3S_ADC_ACTSS_ASEN(0)); + HAL_WRITE_UINT32(base + CYGHWR_HAL_LM3S_ADC_ACTSS, reg); + HAL_WRITE_UINT32(base + CYGHWR_HAL_LM3S_ADC_IMR, + CYGHWR_HAL_LM3S_ADC_IMR_MASK(0)); +} + + +static void +lm3s_adc_flush(cyg_uint32 base) +{ + volatile cyg_uint32 d; + volatile cyg_uint32 i; + + HAL_READ_UINT32(base + CYGHWR_HAL_LM3S_ADC_SS_FIFO0_SR, i); + while (!(i & CYGHWR_HAL_LM3S_ADC_SS_FIFO_SR_EMPTY)) { + HAL_READ_UINT32(base + CYGHWR_HAL_LM3S_ADC_SS_FIFO0, d); + HAL_READ_UINT32(base + CYGHWR_HAL_LM3S_ADC_SS_FIFO0_SR, i); + } +} + + +static void +lm3s_adc_update_sequencer0(cyg_adc_channel * chan) +{ + lm3s_adc_info *info = chan->device->dev_priv; + cyg_uint8 i; + cyg_uint8 cnt = 0; + cyg_uint32 mux = 0; + cyg_uint32 ctl = 0; + + lm3s_adc_diag("ADC: Update sequencer for channel %d\n", chan->channel); + + // Update sequencer + for (i = 0; i < info->max_channel; i++) { + if (!(info->chan_mask & (1 << i))) + continue; + + // Clear and update MUX register + mux &= ~(CYGHWR_HAL_LM3S_ADC_SS_MUX0_M(cnt)); + mux |= CYGHWR_HAL_LM3S_ADC_SS_MUX0_V(i, cnt); + + // Temperature sensor channel + if (i == info->sensor_channel) { + ctl |= CYGHWR_HAL_LM3S_ADC_SS_CTL0_TS(cnt); + lm3s_adc_diag("ADC: Channel %d mapped to temperature sensor\n", + i); + } + + cnt++; + } + + lm3s_adc_diag("ADC: MUX0 Register: 0x%x\n", mux); + HAL_WRITE_UINT32(info->adc_base + CYGHWR_HAL_LM3S_ADC_SS_MUX0, mux); + + if (info->chan_mask) { + ctl |= CYGHWR_HAL_LM3S_ADC_SS_CTL0_END((cnt - 1)); + ctl |= CYGHWR_HAL_LM3S_ADC_SS_CTL0_IE((cnt - 1)); + } + + lm3s_adc_diag("ADC: CTL0 Register: 0x%x\n", ctl); + HAL_WRITE_UINT32(info->adc_base + CYGHWR_HAL_LM3S_ADC_SS_CTL0, ctl); + + lm3s_adc_flush(info->adc_base); +} + + +//--------------------------------------------------------------------------- +// EOF adc_lm3s.c diff --git a/ecos/packages/devs/adc/cortexm/lm3s/current/tests/lm3s_adc_test.c b/ecos/packages/devs/adc/cortexm/lm3s/current/tests/lm3s_adc_test.c new file mode 100644 index 0000000..ff0f161 --- /dev/null +++ b/ecos/packages/devs/adc/cortexm/lm3s/current/tests/lm3s_adc_test.c @@ -0,0 +1,256 @@ +//========================================================================== +// +// lm3s_adc_test.c +// +// ADC driver for Stellaris Cortex M3 microcontroller +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2011 Free Software Foundation, Inc. +// +// 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): Uwe Kindler <uwe_kindler@web.de> +// Updated for Stellaris Cortex microcontroller, ccoutand +// Contributors: +// Date: 2011-01-11 +// Purpose: +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/system.h> + +#include <cyg/infra/testcase.h> // test macros +#include <cyg/infra/cyg_ass.h> // assertion macros +#include <cyg/infra/diag.h> +#include <cyg/hal/hal_diag.h> +#include <cyg/hal/hal_arch.h> // CYGNUM_HAL_STACK_SIZE_TYPICAL + +#include <pkgconf/kernel.h> +#include <cyg/io/io.h> +#include <cyg/io/adc.h> +#include <pkgconf/devs_adc_cortexm_lm3s.h> + +// Package option requirements +#if defined(CYGFUN_KERNEL_API_C) + +# include <cyg/kernel/kapi.h> + +# if CYGINT_DEVS_ADC_CORTEXM_LM3S_ADC0_CHANNELS > 0 + +# define MAX_ADC_CHANNEL_TO_TEST 4 + +//=========================================================================== +// DATA TYPES +//=========================================================================== +typedef struct st_thread_data { + cyg_thread obj; + int stack[(CYGNUM_HAL_STACK_SIZE_MINIMUM / sizeof(int))]; + cyg_handle_t hdl; +} thread_data_t; + + +//=========================================================================== +// LOCAL DATA +//=========================================================================== +cyg_thread_entry_t adc_thread; +thread_data_t adc_thread_data; + + +//=========================================================================== +// ADC THREAD +//=========================================================================== +void +adc_thread(cyg_addrword_t data) +{ + int res; + cyg_io_handle_t handle[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + cyg_uint32 sample_cnt[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + cyg_uint32 cfg_data; + cyg_uint32 len; + cyg_uint32 start_time; + cyg_uint32 end_time; + int i; + cyg_uint8 seconds = 0; + float final_seconds; + cyg_uint32 samples_expected; + + + diag_printf("This test reads samples from all enabled ADC channels.\n" + "Each second the number of already acquired samples\n" + "will be printed. After 10 seconds all ADC channels\n" + "will be stopped and each ADC buffer will be read until\n" + "it is empty. If the number of acquired samples is much\n" + "smaller than the number of expected samples, then you\n" + "should lower the sample rate.\n\n"); + + // Get a handle for ADC device 0 channel 0 - 3 (lookup also trigger a + // channel enable) + res = cyg_io_lookup("/dev/adc00", &handle[0]); + res = cyg_io_lookup("/dev/adc01", &handle[1]); + res = cyg_io_lookup("/dev/adc02", &handle[2]); + res = cyg_io_lookup("/dev/adc03", &handle[3]); + + // Switch all channels to non blocking + for (i = 0; i < MAX_ADC_CHANNEL_TO_TEST; ++i) { + if (handle[i]) { + cfg_data = 0; + len = sizeof(cfg_data); + res = cyg_io_set_config(handle[i], + CYG_IO_SET_CONFIG_READ_BLOCKING, + &cfg_data, &len); + if (ENOERR != res) { + CYG_TEST_FAIL_FINISH + ("Error switching ADC channel to non blocking"); + } + sample_cnt[i] = 0; + } + } + + start_time = cyg_current_time(); + + do { + for (i = 0; i < MAX_ADC_CHANNEL_TO_TEST; ++i) { + if (handle[i]) { + cyg_adc_sample_t sample; + + // read a sample from the channel + do { + cyg_uint32 len = sizeof(sample); + res = cyg_io_read(handle[i], &sample, &len); + } + while (-EAGAIN == res); + if (ENOERR == res) { + sample_cnt[i]++; + } + } + } + + end_time = cyg_current_time(); + + // Print number of acquired samples - if one second is expired. + // we expect that the number of acquired samples is nearly the + // sample rate. + if ((end_time - start_time) >= 100) { + start_time = end_time; + diag_printf("%d\t %d\t %d\t %d\n", + sample_cnt[0], + sample_cnt[1], sample_cnt[2], sample_cnt[3]); + seconds++; + } + } while (seconds < 10); + + // Now stop all channels + for (i = 0; i < MAX_ADC_CHANNEL_TO_TEST; ++i) { + if (handle[i]) { + res = cyg_io_set_config(handle[i], + CYG_IO_SET_CONFIG_ADC_DISABLE, 0, 0); + if (ENOERR != res) { + CYG_TEST_FAIL_FINISH("Error disabling ADC channel"); + } + } + } + + end_time = cyg_current_time(); + end_time = seconds * 1000 + (end_time - start_time) * 10; + final_seconds = end_time / 1000.0; + + // Now read all remaining samples from buffer + for (i = 0; i < MAX_ADC_CHANNEL_TO_TEST; ++i) { + if (handle[i]) { + do { + cyg_adc_sample_t sample; + cyg_uint32 len = sizeof(sample); + res = cyg_io_read(handle[i], &sample, &len); + if (ENOERR == res) { + sample_cnt[i]++; + } + } while (ENOERR == res); + } + } + + diag_printf("\n\n----------------------------------------\n"); + samples_expected = + final_seconds * CYGNUM_DEVS_ADC_CORTEXM_LM3S_ADC0_DEFAULT_RATE; + diag_printf("Samples expected after %d milliseconds: %d\n", end_time, + samples_expected); + diag_printf("Samples read (per channel):\n"); + diag_printf("%d\t %d\t %d\t %d\n", + sample_cnt[0], sample_cnt[1], sample_cnt[2], sample_cnt[3]); + + CYG_TEST_PASS_FINISH("ADC test OK"); +} + + +void +cyg_start(void) +{ + CYG_TEST_INIT(); + + // Create the main ADC test thread + cyg_thread_create(4, + adc_thread, + (cyg_addrword_t)0, + "lm3s_adc_thread", + (void *)adc_thread_data.stack, + CYGNUM_HAL_STACK_SIZE_MINIMUM, + &adc_thread_data.hdl, + &adc_thread_data.obj + ); + + cyg_thread_resume(adc_thread_data.hdl); + + cyg_scheduler_start(); +} +# else// CYGINT_DEVS_ADC_CORTEXM_LM3S_ADC0_CHANNELS > 0 +# define N_A_MSG "Needs at least one enabled ADC channel" +# endif + +#else // CYGFUN_KERNEL_API_C +# define N_A_MSG "Needs kernel C API" +#endif + +#ifdef N_A_MSG +void +cyg_start(void) +{ + CYG_TEST_INIT(); + CYG_TEST_NA(N_A_MSG); +} +#endif // N_A_MSG + + +//--------------------------------------------------------------------------- +// EOF lm3s_adc_test.c diff --git a/ecos/packages/devs/adc/cortexm/stm32/current/ChangeLog b/ecos/packages/devs/adc/cortexm/stm32/current/ChangeLog new file mode 100644 index 0000000..30f9008 --- /dev/null +++ b/ecos/packages/devs/adc/cortexm/stm32/current/ChangeLog @@ -0,0 +1,53 @@ +2012-04-13 Christophe Coutand <ecos@hotmail.co.uk> + + * src/adc_stm32.c: + * src/adc1.inl: + * src/adc3.inl: + Peripheral clocks are enable / disable using new + CYGHWR_HAL_STM32_CLOCK_DISABLE / CYGHWR_HAL_STM32_CLOCK_ENABLE + macro supplied from the variant HAL. In addition, make sure timer + clock is enable. + +2011-05-02 Christophe Coutand <ecos@hotmail.co.uk> + + * cdl/adc_stm32.cdl: + * src/adc_stm32.c: + Make sure DMA clock is enabled. Fix ADC clock divider not set in + stm32_adc_init_clock. + +2009-03-05 Simon Kallweit <simon.kallweit@intefo.ch> + + * src/adc_stm32.c: + Fixed a bug in setup and usage of the timer. + +2009-02-24 Simon Kallweit <simon.kallweit@intefo.ch> + + * cdl/adc_stm32.cdl: + * src/adc_stm32.c: + * src/adc1.inl: + * src/adc3.inl: + STM32 ADC driver package created. + +//=========================================================================== +// ####GPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2009, 2011 Free Software Foundation, Inc. +// +// This program 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. +// +// This program 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 this program; if not, write to the +// Free Software Foundation, Inc., 51 Franklin Street, +// Fifth Floor, Boston, MA 02110-1301, USA. +// ------------------------------------------- +// ####GPLCOPYRIGHTEND#### +//=========================================================================== diff --git a/ecos/packages/devs/adc/cortexm/stm32/current/cdl/adc_stm32.cdl b/ecos/packages/devs/adc/cortexm/stm32/current/cdl/adc_stm32.cdl new file mode 100644 index 0000000..4187a50 --- /dev/null +++ b/ecos/packages/devs/adc/cortexm/stm32/current/cdl/adc_stm32.cdl @@ -0,0 +1,242 @@ +# ==================================================================== +# +# adc_stm32.cdl +# +# eCos STM32 ADC configuration data +# +# ==================================================================== +## ####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2009, 2011 Free Software Foundation, Inc +## +## 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., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +## +## 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): Simon Kallweit <simon.kallweit@intefo.ch> +# Contributors: +# Date: 2009-02-24 +# +#####DESCRIPTIONEND#### +# +# ==================================================================== + + +cdl_package CYGPKG_DEVS_ADC_CORTEXM_STM32 { + display "ST STM32 ADC device driver" + + parent CYGPKG_IO_ADC_DEVICES + active_if CYGPKG_IO_ADC_DEVICES + active_if CYGPKG_HAL_CORTEXM_STM32 + requires {CYGNUM_IO_ADC_SAMPLE_SIZE >= 12} + description " + This option enables the ADC device drivers for the ST STM32. The STM32 + has up to 3 ADC devices. The driver supports both ADC1 and ADC3. ADC2 + is not supported as it does cover the same inputs as ADC2 and does not + support DMA directly." + + include_dir cyg/io + compile -library=libextras.a adc_stm32.c + + cdl_option CYGPKG_DEVS_ADC_CORTEXM_STM32_TRACE { + display "ADC driver tracing" + flavor bool + default_value 0 + description " + Enable tracing of the ADC driver. Select to debug the driver." + } + + cdl_option CYGNUM_DEVS_ADC_CORTEXM_STM32_CLOCK_DIV { + display "ADC clock divider" + flavor data + legal_values { 2 4 6 8 } + default_value 8 + description " + This option specifies the ADC clock divider value. The + ADC clock frequency is defined as PCLK2 / ADC divider. " + } + + cdl_component CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1 { + display "ADC1" + default_value 0 + description " + ADC1 supports 16 analog input channels as well as additional + channels for CPU temperature and internal VREF. This is a total of + 18 channels. Note that only 16 channels may be active at once! + ADC1 uses TIM3 to generate scan events and DMA1 channel 1 for data + transmission." + + cdl_interface CYGINT_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNELS { + display "Number of ADC channels" + } + + cdl_option CYGNUM_DEVS_ADC_CORTEXM_STM32_ADC1_SAMPLE_TIME { + display "Sample time" + flavor data + legal_values 1 to 1000 + default_value 20 + description " + Sampling time in us. When sampling the internal temperatur, + this needs to be at least 17.1 us." + } + + cdl_option CYGNUM_DEVS_ADC_CORTEXM_STM32_ADC1_DEFAULT_RATE { + display "Default sample rate" + flavor data + legal_values 1 to 10000 + default_value 100 + description " + The driver will be initialized with the default sample rate. + If you raise the default sample rate you might need to increase + the buffer size for each channel." + } + + cdl_option CYGNUM_DEVS_ADC_CORTEXM_STM32_ADC1_DMA_INT_PRI { + display "DMA interrupt priority" + flavor data + default_value 0x80 + description " + Priority of the DMA request interrupt." + } + + # ADC1 supports 16 analog inputs + 2 additional channels (temperature/vref) + for { set ::channel 0 } { $::channel < 18 } { incr ::channel } { + + cdl_component CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL[set ::channel] { + display "ADC channel [set ::channel]" + flavor bool + default_value [set ::channel] == 0 + implements CYGINT_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNELS + description " + If the application needs to access the on-chip ADC + channel [set ::channel] via an eCos ADC driver then + this option should be enabled." + + cdl_option CYGDAT_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL[set ::channel]_NAME { + display "Device name" + flavor data + default_value [format {"\"/dev/adc0%d\""} $::channel] + description " + This option controls the name that an eCos application + should use to access this device via cyg_io_lookup(), + open(), or similar calls." + } + + cdl_option CYGDAT_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL[set ::channel]_BUFSIZE { + display "Size of data buffer" + flavor data + legal_values 1 to 65536 + default_value 128 + description " + This option controls the number of samples the + buffer can store. The required RAM is = size of + data buffer * 2." + } + } + } + } + + cdl_component CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC3 { + display "ADC3" + default_value 0 + description " + ADC3 supports 16 analog input channels. All channels may be active + at once. ADC3 uses TIM8 to generate scan events and DMA2 channel 5 + for data transmission." + + cdl_interface CYGINT_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNELS { + display "Number of ADC channels" + } + + cdl_option CYGNUM_DEVS_ADC_CORTEXM_STM32_ADC3_SAMPLE_TIME { + display "Sample time" + flavor data + legal_values 1 to 1000 + default_value 20 + description " + Sampling time in us. When sampling the internal temperatur, + this needs to be at least 17.1 us." + } + + cdl_option CYGNUM_DEVS_ADC_CORTEXM_STM32_ADC3_DEFAULT_RATE { + display "Default sample rate" + flavor data + legal_values 1 to 10000 + default_value 100 + description " + The driver will be initialized with the default sample rate. + If you raise the default sample rate you might need to increase + the buffer size for each channel." + } + + cdl_option CYGNUM_DEVS_ADC_CORTEXM_STM32_ADC3_DMA_INT_PRI { + display "DMA interrupt priority" + flavor data + default_value 0x80 + description " + Priority of the DMA request interrupt." + } + + # ADC3 supports 16 analog inputs + for { set ::channel 0 } { $::channel < 16 } { incr ::channel } { + + cdl_component CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNEL[set ::channel] { + display "ADC channel [set ::channel]" + flavor bool + default_value [set ::channel] == 0 + implements CYGINT_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNELS + description " + If the application needs to access the on-chip ADC + channel [set ::channel] via an eCos ADC driver then + this option should be enabled." + + cdl_option CYGDAT_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNEL[set ::channel]_NAME { + display "Device name" + flavor data + default_value [format {"\"/dev/adc1%d\""} $::channel] + description " + This option controls the name that an eCos application + should use to access this device via cyg_io_lookup(), + open(), or similar calls." + } + + cdl_option CYGDAT_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNEL[set ::channel]_BUFSIZE { + display "Size of data buffer" + flavor data + legal_values 1 to 65536 + default_value 128 + description " + This option controls the number of samples the + buffer can store. The required RAM is = size of + data buffer * 2." + } + } + } + } +} diff --git a/ecos/packages/devs/adc/cortexm/stm32/current/src/adc1.inl b/ecos/packages/devs/adc/cortexm/stm32/current/src/adc1.inl new file mode 100644 index 0000000..0394c1f --- /dev/null +++ b/ecos/packages/devs/adc/cortexm/stm32/current/src/adc1.inl @@ -0,0 +1,163 @@ +//========================================================================== +// +// adc1.inl +// +// Parameters for ADC device 1 +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2009 Free Software Foundation, Inc. +// +// 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): Simon Kallweit <simon.kallweit@intefo.ch> +// Contributors: +// Date: 2009-02-24 +// Purpose: +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +// ADC input pins +static const cyg_uint32 stm32_adc_pins1[] = { + CYGHWR_HAL_STM32_ADC1_IN0, + CYGHWR_HAL_STM32_ADC1_IN1, + CYGHWR_HAL_STM32_ADC1_IN2, + CYGHWR_HAL_STM32_ADC1_IN3, + CYGHWR_HAL_STM32_ADC1_IN4, + CYGHWR_HAL_STM32_ADC1_IN5, + CYGHWR_HAL_STM32_ADC1_IN6, + CYGHWR_HAL_STM32_ADC1_IN7, + CYGHWR_HAL_STM32_ADC1_IN8, + CYGHWR_HAL_STM32_ADC1_IN9, + CYGHWR_HAL_STM32_ADC1_IN10, + CYGHWR_HAL_STM32_ADC1_IN11, + CYGHWR_HAL_STM32_ADC1_IN12, + CYGHWR_HAL_STM32_ADC1_IN13, + CYGHWR_HAL_STM32_ADC1_IN14, + CYGHWR_HAL_STM32_ADC1_IN15, + CYGHWR_HAL_STM32_GPIO_NONE, + CYGHWR_HAL_STM32_GPIO_NONE, +}; + +// ADC setup +static const stm32_adc_setup stm32_adc_setup1 = { + .adc_base = CYGHWR_HAL_STM32_ADC1, + .dma_base = CYGHWR_HAL_STM32_DMA1, + .dma_int_vector = CYGNUM_HAL_INTERRUPT_DMA1_CH1, + .dma_int_pri = CYGNUM_DEVS_ADC_CORTEXM_STM32_ADC1_DMA_INT_PRI, + .dma_channel = 1, + .tim_base = CYGHWR_HAL_STM32_TIM3, + .pins = stm32_adc_pins1, + .extsel = 4, + .sample_time = CYGNUM_DEVS_ADC_CORTEXM_STM32_ADC1_SAMPLE_TIME, + .adc_clkena = CYGHWR_HAL_STM32_ADC1_CLOCK, + .tim_clkena = CYGHWR_HAL_STM32_TIM3_CLOCK, +}; + +// ADC DMA buffer +static cyg_uint16 + stm32_adc_dma_buf1[CYGINT_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNELS] + __attribute__((aligned(2), section(".sram"))); + +// ADC device info +static stm32_adc_info stm32_adc_info1 = { + .setup = &stm32_adc_setup1, + .dma_buf = stm32_adc_dma_buf1, +}; + +// ADC device instance +CYG_ADC_DEVICE(stm32_adc_device1, + &stm32_adc_funs, + &stm32_adc_info1, + CYGNUM_DEVS_ADC_CORTEXM_STM32_ADC1_DEFAULT_RATE); + +// ADC channels +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL0 +STM32_ADC_CHANNEL(1, 0) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL1 +STM32_ADC_CHANNEL(1, 1) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL2 +STM32_ADC_CHANNEL(1, 2) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL3 +STM32_ADC_CHANNEL(1, 3) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL4 +STM32_ADC_CHANNEL(1, 4) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL5 +STM32_ADC_CHANNEL(1, 5) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL6 +STM32_ADC_CHANNEL(1, 6) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL7 +STM32_ADC_CHANNEL(1, 7) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL8 +STM32_ADC_CHANNEL(1, 8) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL9 +STM32_ADC_CHANNEL(1, 9) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL10 +STM32_ADC_CHANNEL(1, 10) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL11 +STM32_ADC_CHANNEL(1, 11) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL12 +STM32_ADC_CHANNEL(1, 12) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL13 +STM32_ADC_CHANNEL(1, 13) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL14 +STM32_ADC_CHANNEL(1, 14) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL15 +STM32_ADC_CHANNEL(1, 15) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL16 +STM32_ADC_CHANNEL(1, 16) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1_CHANNEL17 +STM32_ADC_CHANNEL(1, 17) +#endif + +//----------------------------------------------------------------------------- +// End of adc1.inl diff --git a/ecos/packages/devs/adc/cortexm/stm32/current/src/adc3.inl b/ecos/packages/devs/adc/cortexm/stm32/current/src/adc3.inl new file mode 100644 index 0000000..790c1ac --- /dev/null +++ b/ecos/packages/devs/adc/cortexm/stm32/current/src/adc3.inl @@ -0,0 +1,155 @@ +//========================================================================== +// +// adc3.inl +// +// Parameters for ADC device 3 +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2009 Free Software Foundation, Inc. +// +// 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): Simon Kallweit <simon.kallweit@intefo.ch> +// Contributors: +// Date: 2009-02-24 +// Purpose: +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +// ADC input pins +static const cyg_uint32 stm32_adc_pins3[] = { + CYGHWR_HAL_STM32_ADC3_IN0, + CYGHWR_HAL_STM32_ADC3_IN1, + CYGHWR_HAL_STM32_ADC3_IN2, + CYGHWR_HAL_STM32_ADC3_IN3, + CYGHWR_HAL_STM32_ADC3_IN4, + CYGHWR_HAL_STM32_ADC3_IN5, + CYGHWR_HAL_STM32_ADC3_IN6, + CYGHWR_HAL_STM32_ADC3_IN7, + CYGHWR_HAL_STM32_ADC3_IN8, + CYGHWR_HAL_STM32_ADC3_IN9, + CYGHWR_HAL_STM32_ADC3_IN10, + CYGHWR_HAL_STM32_ADC3_IN11, + CYGHWR_HAL_STM32_ADC3_IN12, + CYGHWR_HAL_STM32_ADC3_IN13, + CYGHWR_HAL_STM32_ADC3_IN14, + CYGHWR_HAL_STM32_ADC3_IN15, +}; + +// ADC setup +static const stm32_adc_setup stm32_adc_setup3 = { + .adc_base = CYGHWR_HAL_STM32_ADC3, + .dma_base = CYGHWR_HAL_STM32_DMA2, + .dma_int_vector = CYGNUM_HAL_INTERRUPT_DMA2_CH4_5, + .dma_int_pri = 0x80, + .dma_channel = 5, + .tim_base = CYGHWR_HAL_STM32_TIM8, + .pins = stm32_adc_pins3, + .extsel = 4, + .sample_time = CYGNUM_DEVS_ADC_CORTEXM_STM32_ADC3_SAMPLE_TIME, + .adc_clkena = CYGHWR_HAL_STM32_ADC3_CLOCK, + .tim_clkena = CYGHWR_HAL_STM32_TIM8_CLOCK, +}; + +// ADC DMA buffer +static cyg_uint16 + stm32_adc_dma_buf3[CYGINT_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNELS] + __attribute__((aligned(2), section(".sram"))); + +// ADC device info +static stm32_adc_info stm32_adc_info3 = { + .setup = &stm32_adc_setup3, + .dma_buf = stm32_adc_dma_buf3, +}; + +// ADC device instance +CYG_ADC_DEVICE(stm32_adc_device3, + &stm32_adc_funs, + &stm32_adc_info3, + CYGNUM_DEVS_ADC_CORTEXM_STM32_ADC3_DEFAULT_RATE); + +// ADC channels +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNEL0 +STM32_ADC_CHANNEL(3, 0) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNEL1 +STM32_ADC_CHANNEL(3, 1) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNEL2 +STM32_ADC_CHANNEL(3, 2) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNEL3 +STM32_ADC_CHANNEL(3, 3) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNEL4 +STM32_ADC_CHANNEL(3, 4) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNEL5 +STM32_ADC_CHANNEL(3, 5) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNEL6 +STM32_ADC_CHANNEL(3, 6) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNEL7 +STM32_ADC_CHANNEL(3, 7) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNEL8 +STM32_ADC_CHANNEL(3, 8) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNEL9 +STM32_ADC_CHANNEL(3, 9) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNEL10 +STM32_ADC_CHANNEL(3, 10) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNEL11 +STM32_ADC_CHANNEL(3, 11) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNEL12 +STM32_ADC_CHANNEL(3, 12) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNEL13 +STM32_ADC_CHANNEL(3, 13) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNEL14 +STM32_ADC_CHANNEL(3, 14) +#endif +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC3_CHANNEL15 +STM32_ADC_CHANNEL(3, 15) +#endif + +//----------------------------------------------------------------------------- +// End of adc3.inl diff --git a/ecos/packages/devs/adc/cortexm/stm32/current/src/adc_stm32.c b/ecos/packages/devs/adc/cortexm/stm32/current/src/adc_stm32.c new file mode 100644 index 0000000..5a7a0e6 --- /dev/null +++ b/ecos/packages/devs/adc/cortexm/stm32/current/src/adc_stm32.c @@ -0,0 +1,631 @@ +//========================================================================== +// +// adc_stm32.c +// +// ADC driver for STM32 on chip ADC +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2009, 2011 Free Software Foundation, Inc. +// +// 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): Simon Kallweit <simon.kallweit@intefo.ch> +// Contributors: +// Date: 2009-02-24 +// Purpose: +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/system.h> +#include <pkgconf/devs_adc_cortexm_stm32.h> + +#include <cyg/infra/cyg_type.h> +#include <cyg/infra/cyg_ass.h> +#include <cyg/io/adc.h> +#include <cyg/hal/hal_arch.h> +#include <cyg/hal/hal_io.h> +#include <cyg/hal/hal_intr.h> +#include <cyg/hal/drv_api.h> + +//----------------------------------------------------------------------------- +// Diagnostic support +// Switch the #if to 1 to generate some diagnostic messages. + +#ifdef CYGPKG_DEVS_ADC_CORTEXM_STM32_TRACE +# include <cyg/infra/diag.h> +# define adc_diag( __fmt, ... ) diag_printf("ADC: %30s[%4d]: " __fmt, __FUNCTION__, __LINE__, ## __VA_ARGS__ ); +#else +# define adc_diag( __fmt, ... ) +#endif + + +//----------------------------------------------------------------------------- +// STM32 ADC device setup + +typedef struct stm32_adc_setup { + CYG_ADDRESS adc_base; // ADC registers base address + CYG_ADDRESS dma_base; // DMA registers base address + cyg_vector_t dma_int_vector; // DMA interrupt vector + cyg_priority_t dma_int_pri; // DMA interrupt priority + cyg_uint8 dma_channel; // DMA channel to use + CYG_ADDRESS tim_base; // Timer registers base address + const cyg_uint32 *pins; // ADC associated GPIO pins + cyg_uint8 extsel; // ADC EXTSEL value (timer event) + cyg_uint32 sample_time; // ADC sampling time in us + cyg_uint32 adc_clkena; // ADC clock enable + cyg_uint32 tim_clkena; // Timer clock enable +} stm32_adc_setup; + +//----------------------------------------------------------------------------- +// STM32 ADC device + +typedef struct stm32_adc_info { + const stm32_adc_setup *setup; // ADC setup + cyg_handle_t dma_int_handle; // DMA interrupt handle + cyg_interrupt dma_int_data; // DMA interrupt data + cyg_uint16 *dma_buf; // DMA buffer + cyg_adc_channel *chan[18]; // Channel references by channel no + cyg_uint32 chan_mask; // Channel mask +} stm32_adc_info; + +//----------------------------------------------------------------------------- +// API function call forward references + +static bool stm32_adc_init(struct cyg_devtab_entry *tab); +static Cyg_ErrNo stm32_adc_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name); + +static void stm32_adc_enable(cyg_adc_channel *chan); +static void stm32_adc_disable(cyg_adc_channel *chan); +static void stm32_adc_set_rate(cyg_adc_channel *chan, cyg_uint32 rate); + +static cyg_uint32 stm32_dma_isr(cyg_vector_t vector, cyg_addrword_t data); +static void stm32_dma_dsr(cyg_vector_t vector, cyg_ucount32 count, + cyg_addrword_t data); + +static void stm32_adc_init_clock(void); +static void stm32_adc_init_device(cyg_adc_device *device); +static void stm32_adc_update_sequence(cyg_adc_device *device); + +CYG_ADC_FUNCTIONS(stm32_adc_funs, + stm32_adc_enable, + stm32_adc_disable, + stm32_adc_set_rate); + +//----------------------------------------------------------------------------- +// STM32 ADC channel instance macro + +#define STM32_ADC_CHANNEL(_device_, _chan_) \ +CYG_ADC_CHANNEL( \ + stm32_adc##_device_##_channel##_chan_, \ + _chan_, \ + CYGDAT_DEVS_ADC_CORTEXM_STM32_ADC##_device_##_CHANNEL##_chan_##_BUFSIZE,\ + &stm32_adc_device##_device_ \ +); \ +DEVTAB_ENTRY( \ + stm32_adc##_device_##_channel##_chan_##_device, \ + CYGDAT_DEVS_ADC_CORTEXM_STM32_ADC##_device_##_CHANNEL##_chan_##_NAME, \ + 0, \ + &cyg_io_adc_devio, \ + stm32_adc_init, \ + stm32_adc_lookup, \ + &stm32_adc##_device_##_channel##_chan_ \ +); + +//----------------------------------------------------------------------------- +// STM32 ADC device instances + +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1 +#include "adc1.inl" +#endif + +#ifdef CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC3 +#include "adc3.inl" +#endif + +static cyg_bool initialized; +static cyg_uint32 adc_clock; + +__externC cyg_uint32 hal_stm32_pclk1; +__externC cyg_uint32 hal_stm32_pclk2; + +//----------------------------------------------------------------------------- +// 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 and interrupt object, then care should be taken to do +// this. This function should also call cyg_adc_device_init() to initialize the +// generic parts of the driver. + +static bool +stm32_adc_init(struct cyg_devtab_entry *tab) +{ + cyg_adc_channel *chan = (cyg_adc_channel *) tab->priv; + cyg_adc_device *device = chan->device; + stm32_adc_info *info = device->dev_priv; + + adc_diag("Initializing device\n"); + + // Initialize ADC clock + if (!initialized) { + stm32_adc_init_clock(); + initialized = true; + } + + // Keep reference to channel + info->chan[chan->channel] = chan; + + if (!info->dma_int_handle) { + // Initialize ADC device + stm32_adc_init_device(device); + + // Set default rate + stm32_adc_set_rate(chan, chan->device->config.rate); + + // Initialize DMA interrupt + cyg_drv_interrupt_create(info->setup->dma_int_vector, + info->setup->dma_int_pri, + (cyg_addrword_t) device, + &stm32_dma_isr, + &stm32_dma_dsr, + &info->dma_int_handle, + &info->dma_int_data); + cyg_drv_interrupt_attach(info->dma_int_handle); + cyg_drv_interrupt_unmask(info->setup->dma_int_vector); + } + + // Initialize generic parts of ADC device + cyg_adc_device_init(device); + + return true; +} + +//----------------------------------------------------------------------------- +// This function is called when a client looks up or opens a channel. It should +// call cyg_adc_channel_init() to initialize the generic part of the channel. +// It should also perform any operations needed to start the channel generating +// samples. + +static Cyg_ErrNo +stm32_adc_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name) +{ + cyg_adc_channel *chan = (cyg_adc_channel *) (*tab)->priv; + stm32_adc_info *info = chan->device->dev_priv; + cyg_uint32 cr; + + adc_diag("Opening device\n"); + + // Configure the input pin, if available + if (info->setup->pins[chan->channel] != CYGHWR_HAL_STM32_GPIO_NONE) + CYGHWR_HAL_STM32_GPIO_SET(info->setup->pins[chan->channel]); + + // Activate temperature and VREF if necessary + if (chan->channel >= 16) { + HAL_READ_UINT32(info->setup->adc_base + CYGHWR_HAL_STM32_ADC_CR2, cr); + cr |= CYGHWR_HAL_STM32_ADC_CR2_TSVREFE; + HAL_WRITE_UINT32(info->setup->adc_base + CYGHWR_HAL_STM32_ADC_CR2, cr); + } + + // Initialize generic parts of the channel + cyg_adc_channel_init(chan); + + // The generic ADC manual says: When a channel is first looked up or + // opened, then it is automatically enabled and samples start to + // accumulate - so we start the channel now + chan->enabled = true; + stm32_adc_enable(chan); + + return ENOERR; +} + +//----------------------------------------------------------------------------- +// This function is called from the generic ADC package to enable the channel +// in response to a CYG_IO_SET_CONFIG_ADC_ENABLE config operation. It should +// take any steps needed to start the channel generating samples + +static void +stm32_adc_enable(cyg_adc_channel *chan) +{ + stm32_adc_info *info = chan->device->dev_priv; + cyg_uint32 cr; + cyg_bool start; + + adc_diag("Enabling channel\n"); + + start = !info->chan_mask; + + // Update the scanning sequence + info->chan_mask |= (1 << chan->channel); + stm32_adc_update_sequence(chan->device); + + // Start scanning when first channel was activated + if (start) { + // Enable timer + adc_diag("Starting scanning\n"); + HAL_READ_UINT32(info->setup->tim_base + CYGHWR_HAL_STM32_TIM_CR1, cr); + cr |= CYGHWR_HAL_STM32_TIM_CR1_CEN; + HAL_WRITE_UINT32(info->setup->tim_base + CYGHWR_HAL_STM32_TIM_CR1, cr); + } +} + +//----------------------------------------------------------------------------- +// This function is called from the generic ADC package to enable the channel +// in response to a CYG_IO_SET_CONFIG_ADC_DISABLE config operation. It should +// take any steps needed to stop the channel generating samples. + +static void +stm32_adc_disable(cyg_adc_channel *chan) +{ + stm32_adc_info *info = chan->device->dev_priv; + cyg_uint32 cr; + + adc_diag("Disabling channel\n"); + + // Update scanning sequence + info->chan_mask &= ~(1 << chan->channel); + stm32_adc_update_sequence(chan->device); + + // Stop scanning when no channel is active + if (!info->chan_mask) { + // Disable timer + adc_diag("Stopping scanning\n"); + HAL_READ_UINT32(info->setup->tim_base + CYGHWR_HAL_STM32_TIM_CR1, cr); + cr &= ~CYGHWR_HAL_STM32_TIM_CR1_CEN; + HAL_WRITE_UINT32(info->setup->tim_base + CYGHWR_HAL_STM32_TIM_CR1, cr); + } +} + +//----------------------------------------------------------------------------- +// This function is called from the generic ADC package to enable the channel +// in response to a CYG_IO_SET_CONFIG_ADC_RATE config operation. It should take +// any steps needed to change the sample rate of the channel, or of the entire +// device. We use a timer channel to generate the interrupts for sampling the +// analog channels + +static void +stm32_adc_set_rate( cyg_adc_channel *chan, cyg_uint32 rate) +{ + cyg_adc_device *device = chan->device; + stm32_adc_info *info = device->dev_priv; + cyg_uint32 clock; + cyg_uint32 period, prescaler; + cyg_uint32 cr; + + adc_diag("Setting rate to %d\n", rate); + + device->config.rate = rate; + + clock = hal_stm32_timer_clock(info->setup->tim_base); + + period = clock / rate; + prescaler = (period / 0x10000) + 1; + period = period / prescaler; + + HAL_WRITE_UINT32(info->setup->tim_base + CYGHWR_HAL_STM32_TIM_PSC, + prescaler - 1); + HAL_WRITE_UINT32(info->setup->tim_base + CYGHWR_HAL_STM32_TIM_ARR, + period - 1); + + // Reinitialize timer + cr = CYGHWR_HAL_STM32_TIM_EGR_UG; + HAL_WRITE_UINT32(info->setup->tim_base + CYGHWR_HAL_STM32_TIM_EGR, cr); +} + +//----------------------------------------------------------------------------- +// This function is the ISR attached to the ADC device's DMA channel interrupt +// vector. It is responsible for reading samples from the DMA buffer and +// passing them on to the generic layer. + +static cyg_uint32 +stm32_dma_isr(cyg_vector_t vector, cyg_addrword_t data) +{ + cyg_adc_device *device = (cyg_adc_device *) data; + stm32_adc_info *info = (stm32_adc_info *) device->dev_priv; + cyg_uint32 chan_active = info->chan_mask; + cyg_uint16 *sample = info->dma_buf; + cyg_adc_channel **chan = info->chan; + cyg_uint32 isr; + cyg_uint32 res = CYG_ISR_HANDLED; + + HAL_READ_UINT32(info->setup->dma_base + CYGHWR_HAL_STM32_DMA_ISR, isr); + if (!(isr & CYGHWR_HAL_STM32_DMA_ISR_MASK(info->setup->dma_channel))) + return 0; + + while (chan_active) { + if (chan_active & 0x1) + res |= cyg_adc_receive_sample(*chan, *sample++ & 0xfff); + chan_active >>= 1; + chan++; + } + + HAL_WRITE_UINT32(info->setup->dma_base + CYGHWR_HAL_STM32_DMA_IFCR, + CYGHWR_HAL_STM32_DMA_IFCR_MASK(info->setup->dma_channel)); + + cyg_drv_interrupt_acknowledge(vector); + + return res; +} + +//----------------------------------------------------------------------------- +// This function is the DSR attached to the ADC device's DMA channel interrupt +// vector. It is called by the kernel if the ISR return value contains the +// CYG_ISR_CALL_DSR bit. It needs to call cyg_adc_wakeup() for each channel +// that has its wakeup field set. + +static void +stm32_dma_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) +{ + cyg_adc_device *device = (cyg_adc_device *) data; + stm32_adc_info *info = (stm32_adc_info *) device->dev_priv; + cyg_uint32 chan_active = info->chan_mask; + cyg_adc_channel **chan = info->chan; + + while (chan_active) { + if (chan_active & 0x1) + if ((*chan)->wakeup) + cyg_adc_wakeup(*chan); + chan_active >>= 1; + chan++; + } +} + +//----------------------------------------------------------------------------- +// Initializes the ADC system clock. + +static void +stm32_adc_init_clock(void) +{ + CYG_ADDRESS rcc = CYGHWR_HAL_STM32_RCC; + cyg_uint32 cfgr; + + adc_diag("Initializing ADC system clock\n"); + + HAL_READ_UINT32(rcc + CYGHWR_HAL_STM32_RCC_CFGR, cfgr); + cfgr &= ~CYGHWR_HAL_STM32_RCC_CFGR_ADCPRE_XXX; + +#if CYGNUM_DEVS_ADC_CORTEXM_STM32_CLOCK_DIV == 2 + cfgr |= CYGHWR_HAL_STM32_RCC_CFGR_ADCPRE_2; + adc_clock = hal_stm32_pclk2 / 2; +#elif CYGNUM_DEVS_ADC_CORTEXM_STM32_CLOCK_DIV == 4 + cfgr |= CYGHWR_HAL_STM32_RCC_CFGR_ADCPRE_4; + adc_clock = hal_stm32_pclk2 / 4; +#elif CYGNUM_DEVS_ADC_CORTEXM_STM32_CLOCK_DIV == 6 + cfgr |= CYGHWR_HAL_STM32_RCC_CFGR_ADCPRE_6; + adc_clock = hal_stm32_pclk2 / 6; +#elif CYGNUM_DEVS_ADC_CORTEXM_STM32_CLOCK_DIV == 8 + cfgr |= CYGHWR_HAL_STM32_RCC_CFGR_ADCPRE_8; + adc_clock = hal_stm32_pclk2 / 8; +#endif + + HAL_WRITE_UINT32(rcc + CYGHWR_HAL_STM32_RCC_CFGR, cfgr); +} + +//----------------------------------------------------------------------------- +// Initializes an ADC device. + +static void +stm32_adc_init_device(cyg_adc_device *device) +{ + stm32_adc_info *info = device->dev_priv; + cyg_uint32 cr; + cyg_uint64 tmp; + cyg_uint32 cycles; + cyg_uint32 smpr; + int i; + + static const cyg_uint32 cycles_table[] = + { 15, 75, 135, 285, 415, 555, 715, 2395 }; + + CYGHWR_HAL_STM32_CLOCK_ENABLE( info->setup->adc_clkena ); + CYGHWR_HAL_STM32_CLOCK_ENABLE( info->setup->tim_clkena ); + + // Make sure ADC is powered on + cr = CYGHWR_HAL_STM32_ADC_CR2_ADON; + HAL_WRITE_UINT32(info->setup->adc_base + CYGHWR_HAL_STM32_ADC_CR2, cr); + + // Reset calibration + cr |= CYGHWR_HAL_STM32_ADC_CR2_RSTCAL; + HAL_WRITE_UINT32(info->setup->adc_base + CYGHWR_HAL_STM32_ADC_CR2, cr); + do { + HAL_READ_UINT32(info->setup->adc_base + CYGHWR_HAL_STM32_ADC_CR2, cr); + } while (cr & CYGHWR_HAL_STM32_ADC_CR2_RSTCAL); + + // Do calibration + cr |= CYGHWR_HAL_STM32_ADC_CR2_CAL; + HAL_WRITE_UINT32(info->setup->adc_base + CYGHWR_HAL_STM32_ADC_CR2, cr); + do { + HAL_READ_UINT32(info->setup->adc_base + CYGHWR_HAL_STM32_ADC_CR2, cr); + } while (cr & CYGHWR_HAL_STM32_ADC_CR2_CAL); + + // Power off ADC + cr &= ~CYGHWR_HAL_STM32_ADC_CR2_ADON; + HAL_WRITE_UINT32(info->setup->adc_base + CYGHWR_HAL_STM32_ADC_CR2, cr); + + // Enable external triggering and DMA + cr |= CYGHWR_HAL_STM32_ADC_CR2_DMA | + CYGHWR_HAL_STM32_ADC_CR2_EXTTRIG | + CYGHWR_HAL_STM32_ADC_CR2_EXTSEL(info->setup->extsel); + HAL_WRITE_UINT32(info->setup->adc_base + CYGHWR_HAL_STM32_ADC_CR2, cr); + + // Enable scanning + cr = CYGHWR_HAL_STM32_ADC_CR1_SCAN; + HAL_WRITE_UINT32(info->setup->adc_base + CYGHWR_HAL_STM32_ADC_CR1, cr); + + + // Set timer direction = down, clock divider = 1 + cr = CYGHWR_HAL_STM32_TIM_CR1_DIR | CYGHWR_HAL_STM32_TIM_CR1_CKD_1; + HAL_WRITE_UINT32(info->setup->tim_base + CYGHWR_HAL_STM32_TIM_CR1, cr); + + // Enable generation of TRGO event + cr = CYGHWR_HAL_STM32_TIM_CR2_MMS_UPDATE; + HAL_WRITE_UINT32(info->setup->tim_base + CYGHWR_HAL_STM32_TIM_CR2, cr); + + + // Setup DMA channel + // Ensure that the DMA clocks are enabled. + if (info->setup->dma_base == CYGHWR_HAL_STM32_DMA1) + CYGHWR_HAL_STM32_CLOCK_ENABLE( CYGHWR_HAL_STM32_DMA1_CLOCK ); + else + CYGHWR_HAL_STM32_CLOCK_ENABLE( CYGHWR_HAL_STM32_DMA2_CLOCK ); + + HAL_WRITE_UINT32(info->setup->dma_base + + CYGHWR_HAL_STM32_DMA_CPAR(info->setup->dma_channel), + info->setup->adc_base + CYGHWR_HAL_STM32_ADC_DR); + HAL_WRITE_UINT32(info->setup->dma_base + + CYGHWR_HAL_STM32_DMA_CMAR(info->setup->dma_channel), + (CYG_ADDRESS) info->dma_buf); + HAL_WRITE_UINT32(info->setup->dma_base + + CYGHWR_HAL_STM32_DMA_CNDTR(info->setup->dma_channel), + 0); + HAL_WRITE_UINT32(info->setup->dma_base + + CYGHWR_HAL_STM32_DMA_CCR(info->setup->dma_channel), + CYGHWR_HAL_STM32_DMA_CCR_TCIE | + CYGHWR_HAL_STM32_DMA_CCR_TEIE | + CYGHWR_HAL_STM32_DMA_CCR_CIRC | + CYGHWR_HAL_STM32_DMA_CCR_MINC | + CYGHWR_HAL_STM32_DMA_CCR_PSIZE16 | + CYGHWR_HAL_STM32_DMA_CCR_MSIZE16); + + // Compute duration of a single cycle in pico-seconds + tmp = 1000000000000LL / adc_clock; + // Compute tenths of cycles for target sample time + tmp = (info->setup->sample_time * 1000000 * 10) / tmp; + cycles = tmp; + + adc_diag("Setting ADC sample time to %d us (%d.%d cycles)\n", + info->setup->sample_time, cycles / 10, cycles % 10); + + // Find best matching SMPR value + if (cycles > cycles_table[7]) { + adc_diag("ADC sample time too long\n"); + smpr = 7; + } else { + for (smpr = 7; smpr > 0; smpr--) + if (cycles > cycles_table[smpr]) + break; + } + + // Expand SMPR value to all channels + for (i = 0; i < 10; i++) + smpr |= smpr << 3; + + // Set sampling time + HAL_WRITE_UINT32(info->setup->adc_base + CYGHWR_HAL_STM32_ADC_SMPR1, smpr); + HAL_WRITE_UINT32(info->setup->adc_base + CYGHWR_HAL_STM32_ADC_SMPR2, smpr); +} + +//----------------------------------------------------------------------------- +// Updates the sequence for the regular group. ADC and DMA are disabled during +// the update. The sequence registers and DMA count registers are rewritten. +// Note: As the regular group consists of 16 channels max, we cannot activate +// the theoretical maximum of 18 channels (analog ins + temperature/VREF). + +static void +stm32_adc_update_sequence(cyg_adc_device *device) +{ + stm32_adc_info *info = device->dev_priv; + int i; + int count = 0; + cyg_uint32 cr; + cyg_uint32 sqr1 = 0; + cyg_uint32 sqr2 = 0; + cyg_uint32 sqr3 = 0; + + adc_diag("Updating regular group\n"); + + // Disable ADC + HAL_READ_UINT32(info->setup->adc_base + CYGHWR_HAL_STM32_ADC_CR2, cr); + cr &= ~CYGHWR_HAL_STM32_ADC_CR2_ADON; + HAL_WRITE_UINT32(info->setup->adc_base + CYGHWR_HAL_STM32_ADC_CR2, cr); + + // Disable DMA + HAL_READ_UINT32(info->setup->dma_base + + CYGHWR_HAL_STM32_DMA_CCR(info->setup->dma_channel), cr); + cr &= ~CYGHWR_HAL_STM32_DMA_CCR_EN; + HAL_WRITE_UINT32(info->setup->dma_base + + CYGHWR_HAL_STM32_DMA_CCR(info->setup->dma_channel), cr); + + // Initialize scanning sequence (regular group) + for (i = 0; i < 18; i++) { + if (!(info->chan_mask & (1 << i))) + continue; + + if (count < 6) { + sqr3 |= CYGHWR_HAL_STM32_ADC_SQRx_SQ(count, i); + } else if (count < 12) { + sqr2 |= CYGHWR_HAL_STM32_ADC_SQRx_SQ(count - 6, i); + } else if (count < 16) { + sqr1 |= CYGHWR_HAL_STM32_ADC_SQRx_SQ(count - 12, i); + } else { + CYG_FAIL("Too many active channels\n"); + } + count++; + } + + sqr1 |= CYGHWR_HAL_STM32_ADC_SQR1_L(count - 1); + + adc_diag("sqr1: %p sqr2: %p sqr3: %p\n", + (void *) sqr1, (void *) sqr2, (void *) sqr3); + + // Write sequence registers + HAL_WRITE_UINT32(info->setup->adc_base + CYGHWR_HAL_STM32_ADC_SQR1, sqr1); + HAL_WRITE_UINT32(info->setup->adc_base + CYGHWR_HAL_STM32_ADC_SQR2, sqr2); + HAL_WRITE_UINT32(info->setup->adc_base + CYGHWR_HAL_STM32_ADC_SQR3, sqr3); + + // Update DMA + HAL_WRITE_UINT32(info->setup->dma_base + + CYGHWR_HAL_STM32_DMA_CNDTR(info->setup->dma_channel), + count); + + // Enable DMA + HAL_READ_UINT32(info->setup->dma_base + + CYGHWR_HAL_STM32_DMA_CCR(info->setup->dma_channel), cr); + cr |= CYGHWR_HAL_STM32_DMA_CCR_EN; + HAL_WRITE_UINT32(info->setup->dma_base + + CYGHWR_HAL_STM32_DMA_CCR(info->setup->dma_channel), cr); + + // Enable ADC + HAL_READ_UINT32(info->setup->adc_base + CYGHWR_HAL_STM32_ADC_CR2, cr); + cr |= CYGHWR_HAL_STM32_ADC_CR2_ADON; + HAL_WRITE_UINT32(info->setup->adc_base + CYGHWR_HAL_STM32_ADC_CR2, cr); +} + +//----------------------------------------------------------------------------- +// End of adc_stm32.c diff --git a/ecos/packages/devs/adc/synth/current/ChangeLog b/ecos/packages/devs/adc/synth/current/ChangeLog new file mode 100644 index 0000000..c0f8049 --- /dev/null +++ b/ecos/packages/devs/adc/synth/current/ChangeLog @@ -0,0 +1,35 @@ +2009-03-05 Simon Kallweit <simon.kallweit@intefo.ch> + + * cdl/adc_synth.cdl: + * cdl/adc_synth.c: + Fixed some typos. + +2009-02-27 Simon Kallweit <simon.kallweit@intefo.ch> + + * cdl/adc_synth.cdl + * src/adc_synth.c + Synthetic ADC driver package created. + +//=========================================================================== +// ####GPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2009 Free Software Foundation, Inc. +// +// This program 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. +// +// This program 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 this program; if not, write to the +// Free Software Foundation, Inc., 51 Franklin Street, +// Fifth Floor, Boston, MA 02110-1301, USA. +// ------------------------------------------- +// ####GPLCOPYRIGHTEND#### +//=========================================================================== diff --git a/ecos/packages/devs/adc/synth/current/cdl/adc_synth.cdl b/ecos/packages/devs/adc/synth/current/cdl/adc_synth.cdl new file mode 100644 index 0000000..ac471f7 --- /dev/null +++ b/ecos/packages/devs/adc/synth/current/cdl/adc_synth.cdl @@ -0,0 +1,158 @@ +# ==================================================================== +# +# adc_synth.cdl +# +# eCos Synthetic ADC configuration data +# +# ==================================================================== +## ####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2009 Free Software Foundation, Inc. +## +## 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., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +## +## 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): Simon Kallweit <simon.kallweit@intefo.ch> +# Contributors: +# Date: 2009-02-27 +# +#####DESCRIPTIONEND#### +# +# ==================================================================== + + +cdl_package CYGPKG_DEVS_ADC_SYNTH { + display "Synthetic ADC device driver" + + parent CYGPKG_IO_ADC_DEVICES + active_if CYGPKG_IO_ADC_DEVICES + active_if CYGPKG_HAL_SYNTH + requires {CYGNUM_IO_ADC_SAMPLE_SIZE >= CYGNUM_DEVS_ADC_SYNTH_SAMPLE_SIZE} + description " + This option enables the ADC device drivers for Synthetic target." + + include_dir cyg/io + compile -library=libextras.a adc_synth.c + + cdl_interface CYGINT_DEVS_ADC_SYNTH_CHANNELS { + display "Number of ADC channels" + } + + cdl_option CYGNUM_DEVS_ADC_SYNTH_SAMPLE_SIZE { + display "Sample size" + flavor data + legal_values 1 to 32 + default_value 16 + description " + Sample size provided by the ADC channels." + } + + cdl_option CYGNUM_DEVS_ADC_SYNTH_DEFAULT_RATE { + display "Default sample rate" + flavor data + legal_values 1 to 10000 + default_value 100 + description " + The driver will be initialized with the default sample rate. + If you raise the default sample rate you might need to increase + the buffer size for each channel." + } + + # Support 16 channels + for { set ::channel 0 } { $::channel < 16 } { incr ::channel } { + + cdl_component CYGHWR_DEVS_ADC_SYNTH_CHANNEL[set ::channel] { + display "ADC channel [set ::channel]" + flavor none + implements CYGINT_DEVS_ADC_SYNTH_CHANNELS + description " + If the application needs to access the ADC + channel [set ::channel] via an eCos ADC driver then + this option should be enabled." + + cdl_option CYGDAT_DEVS_ADC_SYNTH_CHANNEL[set ::channel]_NAME { + display "Device name" + flavor data + default_value [format {"\"/dev/adc0%d\""} $::channel] + description " + This option controls the name that an eCos application + should use to access this device via cyg_io_lookup(), + open(), or similar calls." + } + + cdl_option CYGNUM_DEVS_ADC_SYNTH_CHANNEL[set ::channel]_BUFSIZE { + display "Size of data buffer" + flavor data + legal_values 1 to 65536 + default_value 128 + description " + This option controls the number of samples the + buffer can store. The required RAM is = size of + data buffer * size of sample." + } + + cdl_option CYGDAT_DEVS_ADC_SYNTH_CHANNEL[set ::channel]_SOURCE { + display "Channel source" + flavor data + legal_values { "CONST" "RANDOM" "FILE" } + default_value { "CONST" } + description " + This option controls the sample source of the virtual ADC + channel. CONST mode always returns a constant sample value. + RANDOM mode returns random samples. FILE returns samples as + read by a file on the host." + } + + cdl_option CYGNUM_DEVS_ADC_SYNTH_CHANNEL[set ::channel]_CONST_VALUE { + display "Constant sample value" + flavor data + default_value 0 + description " + Constant sample value returned when CONST mode is selected." + } + + cdl_option CYGDAT_DEVS_ADC_SYNTH_CHANNEL[set ::channel]_FILENAME { + display "Sample data filename" + flavor data + default_value [format {"\"adc0%d\""} $::channel] + description " + Filename of sample data file used in FILE mode." + } + + cdl_option CYGNUM_DEVS_ADC_SYNTH_CHANNEL[set ::channel]_MODE { + display "Channel mode" + flavor data + calculated CYGDAT_DEVS_ADC_SYNTH_CHANNEL[set ::channel]_SOURCE == { "CONST" } ? 0 : \ + CYGDAT_DEVS_ADC_SYNTH_CHANNEL[set ::channel]_SOURCE == { "RANDOM" } ? 1 : \ + CYGDAT_DEVS_ADC_SYNTH_CHANNEL[set ::channel]_SOURCE == { "FILE" } ? 2 : -1 + } + } + } +} diff --git a/ecos/packages/devs/adc/synth/current/src/adc_synth.c b/ecos/packages/devs/adc/synth/current/src/adc_synth.c new file mode 100644 index 0000000..42ddab2 --- /dev/null +++ b/ecos/packages/devs/adc/synth/current/src/adc_synth.c @@ -0,0 +1,482 @@ +//========================================================================== +// +// adc_synth.c +// +// ADC driver for Synthetic ADC +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2009 Free Software Foundation, Inc. +// +// 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): Simon Kallweit <simon.kallweit@intefo.ch> +// Contributors: +// Date: 2009-02-27 +// Purpose: +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/kernel.h> +#include <pkgconf/devs_adc_synth.h> + +#include <cyg/infra/cyg_type.h> +#include <cyg/infra/cyg_ass.h> +#include <cyg/io/adc.h> +#include <cyg/hal/hal_arch.h> +#include <cyg/hal/hal_io.h> +#include <cyg/hal/hal_intr.h> +#include <cyg/hal/drv_api.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__ ); +#else +#define adc_diag( __fmt, ... ) +#endif + +#define NUM_CHANNELS 16 + +#define MODE_CONST 0 +#define MODE_RANDOM 1 +#define MODE_FILE 2 + +#define SAMPLE_BITS ((1 << CYGNUM_DEVS_ADC_SYNTH_SAMPLE_SIZE) - 1) + +//----------------------------------------------------------------------------- +// Synthetic ADC channel + +typedef struct synth_adc_channel_info { + cyg_uint32 mode; // Channel mode + cyg_uint32 const_value; // Const sample value + char *filename; // Sampling data filename + + int fd; // File descriptor of sample file + cyg_uint32 num_samples; // Number of samples in the file + cyg_adc_sample_t *base; // Base address of mapped sample file + cyg_adc_sample_t *sample; // Current sample + + cyg_adc_sample_t (*get_sample)(cyg_adc_channel *chan); +} synth_adc_channel_info; + +//----------------------------------------------------------------------------- +// Synthetic ADC device + +typedef struct synth_adc_info { + synth_adc_channel_info *chan_info; // Channel infos + cyg_adc_channel *chan[NUM_CHANNELS]; // Channel references + cyg_uint32 chan_mask; // Active channels + cyg_handle_t alarm_handle; // Alarm handle + cyg_alarm alarm_data; // Alarm data + cyg_tick_count_t alarm_interval; // Alarm interval in ticks + cyg_uint32 alarm_samples; // Number of samples per tick +} synth_adc_info; + +//----------------------------------------------------------------------------- +// API function call forward references + +static bool synth_adc_init(struct cyg_devtab_entry *tab); +static Cyg_ErrNo synth_adc_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name); + +static void synth_adc_enable(cyg_adc_channel *chan); +static void synth_adc_disable(cyg_adc_channel *chan); +static void synth_adc_set_rate(cyg_adc_channel *chan, cyg_uint32 rate); + +static void alarm_handler(cyg_handle_t alarm, cyg_addrword_t data); + +static cyg_adc_sample_t synth_adc_get_sample_const(cyg_adc_channel *chan); +static cyg_adc_sample_t synth_adc_get_sample_random(cyg_adc_channel *chan); +static cyg_adc_sample_t synth_adc_get_sample_file(cyg_adc_channel *chan); + +static cyg_uint32 rand(void); + +CYG_ADC_FUNCTIONS(synth_adc_funs, + synth_adc_enable, + synth_adc_disable, + synth_adc_set_rate); + +//----------------------------------------------------------------------------- +// Synthetic ADC channel info macro + +#define SYNTH_ADC_CHANNEL_INFO(_chan_) \ +{ \ + .mode = CYGNUM_DEVS_ADC_SYNTH_CHANNEL##_chan_##_MODE, \ + .const_value = CYGNUM_DEVS_ADC_SYNTH_CHANNEL##_chan_##_CONST_VALUE, \ + .filename = CYGDAT_DEVS_ADC_SYNTH_CHANNEL##_chan_##_FILENAME, \ +} + +//----------------------------------------------------------------------------- +// Synthetic ADC channel instance macro + +#define SYNTH_ADC_CHANNEL(_chan_) \ +CYG_ADC_CHANNEL( \ + synth_adc_channel##_chan_, \ + _chan_, \ + CYGNUM_DEVS_ADC_SYNTH_CHANNEL##_chan_##_BUFSIZE, \ + &synth_adc_device \ +); \ +DEVTAB_ENTRY( \ + synth_adc_channel##_chan_##_device, \ + CYGDAT_DEVS_ADC_SYNTH_CHANNEL##_chan_##_NAME, \ + 0, \ + &cyg_io_adc_devio, \ + synth_adc_init, \ + synth_adc_lookup, \ + &synth_adc_channel##_chan_ \ +); + +//----------------------------------------------------------------------------- +// Synthetic ADC device instance + +static synth_adc_channel_info synth_adc_channel_infos[NUM_CHANNELS] = { + SYNTH_ADC_CHANNEL_INFO(0), + SYNTH_ADC_CHANNEL_INFO(1), + SYNTH_ADC_CHANNEL_INFO(2), + SYNTH_ADC_CHANNEL_INFO(3), + SYNTH_ADC_CHANNEL_INFO(4), + SYNTH_ADC_CHANNEL_INFO(5), + SYNTH_ADC_CHANNEL_INFO(6), + SYNTH_ADC_CHANNEL_INFO(7), + SYNTH_ADC_CHANNEL_INFO(8), + SYNTH_ADC_CHANNEL_INFO(9), + SYNTH_ADC_CHANNEL_INFO(10), + SYNTH_ADC_CHANNEL_INFO(11), + SYNTH_ADC_CHANNEL_INFO(12), + SYNTH_ADC_CHANNEL_INFO(13), + SYNTH_ADC_CHANNEL_INFO(14), + SYNTH_ADC_CHANNEL_INFO(15), +}; + +static synth_adc_info synth_adc_info0 = { + .chan_info = synth_adc_channel_infos, +}; + +CYG_ADC_DEVICE(synth_adc_device, + &synth_adc_funs, + &synth_adc_info0, + CYGNUM_DEVS_ADC_SYNTH_DEFAULT_RATE); + +SYNTH_ADC_CHANNEL(0) +SYNTH_ADC_CHANNEL(1) +SYNTH_ADC_CHANNEL(2) +SYNTH_ADC_CHANNEL(3) +SYNTH_ADC_CHANNEL(4) +SYNTH_ADC_CHANNEL(5) +SYNTH_ADC_CHANNEL(6) +SYNTH_ADC_CHANNEL(7) +SYNTH_ADC_CHANNEL(8) +SYNTH_ADC_CHANNEL(9) +SYNTH_ADC_CHANNEL(10) +SYNTH_ADC_CHANNEL(11) +SYNTH_ADC_CHANNEL(12) +SYNTH_ADC_CHANNEL(13) +SYNTH_ADC_CHANNEL(14) +SYNTH_ADC_CHANNEL(15) + + +//----------------------------------------------------------------------------- +// 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 and interrupt object, then care should be taken to do +// this. This function should also call cyg_adc_device_init() to initialize the +// generic parts of the driver. + +static bool +synth_adc_init(struct cyg_devtab_entry *tab) +{ + static cyg_bool initialized = false; + cyg_adc_channel *chan = (cyg_adc_channel *) tab->priv; + cyg_adc_device *device = chan->device; + synth_adc_info *info = device->dev_priv; + synth_adc_channel_info *chan_info = &info->chan_info[chan->channel]; + cyg_handle_t counter; + + adc_diag("Initializing device\n"); + + // Initialize channel + info->chan[chan->channel] = chan; + switch (chan_info->mode) { + case MODE_CONST: + chan_info->get_sample = synth_adc_get_sample_const; + break; + case MODE_RANDOM: + chan_info->get_sample = synth_adc_get_sample_random; + break; + case MODE_FILE: + chan_info->get_sample = synth_adc_get_sample_file; + break; + } + + // Set default rate + if (!initialized) { + // Initialize alarm + cyg_clock_to_counter(cyg_real_time_clock(), &counter); + cyg_alarm_create(counter, alarm_handler, (cyg_addrword_t) device, + &info->alarm_handle, &info->alarm_data); + + synth_adc_set_rate(chan, chan->device->config.rate); + initialized = true; + } + + // Initialize generic parts of ADC device + cyg_adc_device_init(device); + + return true; +} + +//----------------------------------------------------------------------------- +// This function is called when a client looks up or opens a channel. It should +// call cyg_adc_channel_init() to initialize the generic part of the channel. +// It should also perform any operations needed to start the channel generating +// samples. + +static Cyg_ErrNo +synth_adc_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name) +{ + cyg_adc_channel *chan = (cyg_adc_channel *) (*tab)->priv; + synth_adc_info *info = chan->device->dev_priv; + synth_adc_channel_info *chan_info = &info->chan_info[chan->channel]; + + adc_diag("Opening device\n"); + + // When this channel is in file mode, initialize file access + if (chan_info->mode == MODE_FILE) { + struct cyg_hal_sys_new_stat stat; + + // Open the file + chan_info->fd = cyg_hal_sys_open(chan_info->filename, + CYG_HAL_SYS_O_RDONLY, 0); + if (chan_info->fd == -ENOENT) { + adc_diag("Cannot open sampling file '%s' for channel '%s'\n", + chan_info->filename, (*tab)->name); + CYG_FAIL("Cannot open sampling file\n"); + } + + // Get file size + if (cyg_hal_sys_newfstat(chan_info->fd, &stat) != 0) { + CYG_FAIL("Cannot stat sampling file\n"); + } + chan_info->num_samples = stat.st_size / sizeof(cyg_adc_sample_t); + if (chan_info->num_samples <= 0) + CYG_FAIL("Sampling file too small\n"); + + // Memory map + chan_info->base = (cyg_adc_sample_t *) cyg_hal_sys_mmap( + NULL, + chan_info->num_samples * sizeof(cyg_adc_sample_t), + CYG_HAL_SYS_PROT_READ, + CYG_HAL_SYS_MAP_SHARED, + chan_info->fd, + 0); + if (chan_info->base == (void *) -1) + CYG_FAIL("Cannot memory map sampling file\n"); + chan_info->sample = chan_info->base; + + adc_diag("Mapped to %p\n", chan_info->base); + } + + // Initialize generic parts of the channel + cyg_adc_channel_init(chan); + + // The generic ADC manual says: When a channel is first looked up or + // opened, then it is automatically enabled and samples start to + // accumulate - so we start the channel now + chan->enabled = true; + synth_adc_enable(chan); + + return ENOERR; +} + +//----------------------------------------------------------------------------- +// This function is called from the generic ADC package to enable the channel +// in response to a CYG_IO_SET_CONFIG_ADC_ENABLE config operation. It should +// take any steps needed to start the channel generating samples + +static void +synth_adc_enable(cyg_adc_channel *chan) +{ + synth_adc_info *info = chan->device->dev_priv; + cyg_bool start; + + adc_diag("Enabling channel\n"); + + start = !info->chan_mask; + info->chan_mask |= (1 << chan->channel); + + // Start scanning when first channel was activated + if (start) { + // Enable timer + adc_diag("Starting scanning\n"); + cyg_alarm_initialize(info->alarm_handle, + cyg_current_time() + info->alarm_interval, + info->alarm_interval); + } +} + +//----------------------------------------------------------------------------- +// This function is called from the generic ADC package to enable the channel +// in response to a CYG_IO_SET_CONFIG_ADC_DISABLE config operation. It should +// take any steps needed to stop the channel generating samples. + +static void +synth_adc_disable(cyg_adc_channel *chan) +{ + synth_adc_info *info = chan->device->dev_priv; + + adc_diag("Disabling channel\n"); + + info->chan_mask &= ~(1 << chan->channel); + + // Stop scanning when no channel is active + if (!info->chan_mask) { + // Disable timer + adc_diag("Stopping scanning\n"); + cyg_alarm_disable(info->alarm_handle); + } +} + +//----------------------------------------------------------------------------- +// This function is called from the generic ADC package to enable the channel +// in response to a CYG_IO_SET_CONFIG_ADC_RATE config operation. It should take +// any steps needed to change the sample rate of the channel, or of the entire +// device. We use a timer channel to generate the interrupts for sampling the +// analog channels + +static void +synth_adc_set_rate(cyg_adc_channel *chan, cyg_uint32 rate) +{ + cyg_adc_device *device = chan->device; + synth_adc_info *info = device->dev_priv; + cyg_uint64 interval; + + adc_diag("Setting rate to %d\n", rate); + + interval = 1000000000000LL / rate; + interval /= (CYGNUM_HAL_RTC_NUMERATOR / CYGNUM_HAL_RTC_DENOMINATOR); + + if (interval > 1000) { + info->alarm_interval = interval / 1000; + info->alarm_samples = 1; + } else { + info->alarm_interval = 1; + info->alarm_samples = 1000 / interval; + } + + if (info->chan_mask) + cyg_alarm_initialize(info->alarm_handle, + cyg_current_time() + info->alarm_interval, + info->alarm_interval); + + device->config.rate = rate; +} + +static void +alarm_handler(cyg_handle_t alarm, cyg_addrword_t data) +{ + cyg_adc_device *device = (cyg_adc_device *) data; + synth_adc_info *info = device->dev_priv; + cyg_adc_channel *chan; + synth_adc_channel_info *chan_info; + cyg_uint32 active_mask; + int i, j; + + if (!info->chan_mask) + return; + + active_mask = info->chan_mask; + + for (i = 0; i < NUM_CHANNELS; i++) { + if (active_mask & 0x01) { + chan = info->chan[i]; + chan_info = &info->chan_info[chan->channel]; + for (j = 0; j < info->alarm_samples; j++) + cyg_adc_receive_sample(chan, chan_info->get_sample(chan)); + cyg_adc_wakeup(info->chan[i]); + } + active_mask >>= 1; + } +} + +static cyg_adc_sample_t +synth_adc_get_sample_const(cyg_adc_channel *chan) +{ + synth_adc_info *info = chan->device->dev_priv; + synth_adc_channel_info *chan_info = &info->chan_info[chan->channel]; + + return chan_info->const_value; +} + +static cyg_adc_sample_t +synth_adc_get_sample_random(cyg_adc_channel *chan) +{ + return rand() & SAMPLE_BITS; +} + +static cyg_adc_sample_t +synth_adc_get_sample_file(cyg_adc_channel *chan) +{ + synth_adc_info *info = chan->device->dev_priv; + synth_adc_channel_info *chan_info = &info->chan_info[chan->channel]; + cyg_adc_sample_t sample; + + sample = *chan_info->sample++; + if (chan_info->sample >= chan_info->base + chan_info->num_samples) + chan_info->sample = chan_info->base; + + return sample; +} + +//----------------------------------------------------------------------------- +// Simple random number generator + +static cyg_uint32 rand(void) +{ + static cyg_uint32 seed; + + seed = (seed * 1103515245) + 12345; // permutate seed + + return seed; +} + |