This is the mail archive of the
ecos-patches@sourceware.org
mailing list for the eCos project.
STM32 ADC driver
- From: Simon Kallweit <simon dot kallweit at intefo dot ch>
- To: eCos Patches List <ecos-patches at ecos dot sourceware dot org>
- Date: Fri, 27 Feb 2009 13:55:33 +0100
- Subject: STM32 ADC driver
ADC driver for STM32 targets. Would be glad for reviews. This patch
depends on the previous patch I sent (ADC register definitions).
diff --git a/packages/devs/adc/cortexm/stm32/current/ChangeLog b/packages/devs/adc/cortexm/stm32/current/ChangeLog
new file mode 100755
index 0000000..c416579
--- /dev/null
+++ b/packages/devs/adc/cortexm/stm32/current/ChangeLog
@@ -0,0 +1,30 @@
+2009-02-24 Simon Kallweit <simon.kallweit@intefo.ch>
+
+ * STM32 ADC driver package created
+ * cdl/adc_stm32.cdl
+ * src/adc_stm32.c
+ * tests/stm32_adc_test.c
+
+//===========================================================================
+// ####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/packages/devs/adc/cortexm/stm32/current/cdl/adc_stm32.cdl b/packages/devs/adc/cortexm/stm32/current/cdl/adc_stm32.cdl
new file mode 100755
index 0000000..19f54b5
--- /dev/null
+++ b/packages/devs/adc/cortexm/stm32/current/cdl/adc_stm32.cdl
@@ -0,0 +1,254 @@
+# ====================================================================
+#
+# adc_stm32.cdl
+#
+# eCos STM32 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-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 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 level of debug data output by
+ the STM32 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 CYGHWR_DEVS_ADC_CORTEXM_STM32_ADC1 {
+ display "ADC1"
+ 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"
+ 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."
+ }
+ }
+ }
+ }
+
+ cdl_component CYGPKG_DEVS_ADC_CORTEXM_STM32_TESTS {
+ display "Tests for STM32 ADC driver"
+ flavor data
+ no_define
+ calculated { "tests/stm32_adc_test" }
+ description "
+ This option specifies the set of tests for the STM32
+ ADC device driver."
+
+ cdl_option CYGNUM_DEVS_ADC_CORTEXM_STM32_ADC_TEST_RATE {
+ display "Test sample rate"
+ flavor data
+ legal_values 1 to 10000
+ default_value 100
+ description "
+ Sample rate to use for adc test."
+ }
+ }
+}
diff --git a/packages/devs/adc/cortexm/stm32/current/src/adc1.inl b/packages/devs/adc/cortexm/stm32/current/src/adc1.inl
new file mode 100644
index 0000000..5219c57
--- /dev/null
+++ b/packages/devs/adc/cortexm/stm32/current/src/adc1.inl
@@ -0,0 +1,108 @@
+
+// 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 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
diff --git a/packages/devs/adc/cortexm/stm32/current/src/adc3.inl b/packages/devs/adc/cortexm/stm32/current/src/adc3.inl
new file mode 100644
index 0000000..afcc151
--- /dev/null
+++ b/packages/devs/adc/cortexm/stm32/current/src/adc3.inl
@@ -0,0 +1,100 @@
+
+// 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 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
diff --git a/packages/devs/adc/cortexm/stm32/current/src/adc_stm32.c b/packages/devs/adc/cortexm/stm32/current/src/adc_stm32.c
new file mode 100755
index 0000000..e4848e7
--- /dev/null
+++ b/packages/devs/adc/cortexm/stm32/current/src/adc_stm32.c
@@ -0,0 +1,611 @@
+//==========================================================================
+//
+// 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 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.
+
+#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
+
+
+//-----------------------------------------------------------------------------
+// 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
+} 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);
+
+ // Set 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);
+
+ // Reinitialize timer
+ cr = CYGHWR_HAL_STM32_TIM_EGR_UG;
+ HAL_WRITE_UINT32(info->setup->tim_base + CYGHWR_HAL_STM32_TIM_EGR, 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);
+}
+
+//-----------------------------------------------------------------------------
+// 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 res = 0;
+
+ while (chan_active) {
+ if (chan_active & 0x1)
+ res |= (CYG_ISR_HANDLED |
+ 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_READ_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 };
+
+ // 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);
+
+ // Setup DMA channel
+ 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("Updateing 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);
+}
diff --git a/packages/devs/adc/cortexm/stm32/current/tests/stm32_adc_test.c b/packages/devs/adc/cortexm/stm32/current/tests/stm32_adc_test.c
new file mode 100755
index 0000000..d25afb3
--- /dev/null
+++ b/packages/devs/adc/cortexm/stm32/current/tests/stm32_adc_test.c
@@ -0,0 +1,264 @@
+//==========================================================================
+//
+// stmp32_adc_test.c
+//
+// ADC performance test for STM32
+//
+//==========================================================================
+// ####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
+// Description: ADC performance test for STM32
+//####DESCRIPTIONEND####
+
+#include <pkgconf/system.h>
+
+#include <cyg/infra/testcase.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/diag.h>
+#include <cyg/hal/hal_diag.h>
+#include <cyg/hal/hal_arch.h>
+
+// 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_cortexm_stm32.h>
+
+// Package option requirements
+#if defined(CYGFUN_KERNEL_API_C)
+
+#include <cyg/kernel/kapi.h>
+
+#define MAX_DEVICES 2
+#define MAX_CHANNELS_PER_DEVICE 18
+#define MAX_CHANNELS (MAX_DEVICES * MAX_CHANNELS_PER_DEVICE)
+
+// Test channel
+typedef struct test_channel {
+ int device;
+ int channel;
+ cyg_io_handle_t handle;
+ cyg_uint32 count;
+ cyg_adc_sample_t sample;
+} test_channel;
+
+// Test channels
+static test_channel channels[MAX_CHANNELS];
+
+// Thread data
+cyg_handle_t thread_handle;
+cyg_thread thread_data;
+cyg_uint8 thread_stack[CYGNUM_HAL_STACK_SIZE_TYPICAL];
+
+
+void
+adc_thread(cyg_addrword_t data)
+{
+ cyg_uint32 num = 0;
+ char dev[] = "/dev/adcXXX";
+ test_channel *chan;
+ cyg_adc_sample_t sample;
+ int res;
+ cyg_uint32 cfg_data;
+ cyg_uint32 len;
+ cyg_uint32 start_time;
+ cyg_uint32 end_time;
+ int i, j;
+ cyg_uint8 seconds = 0;
+ float final_seconds;
+ cyg_uint32 samples_expected;
+
+ diag_printf("\n\nThis 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");
+
+ // Open available channels
+ for (i = 0; i < MAX_DEVICES; i++) {
+ for (j = 0; j < MAX_CHANNELS_PER_DEVICE; j++) {
+ chan = &channels[num];
+ chan->device = i;
+ chan->channel = j;
+ chan->count = 0;
+
+ dev[8] = '0' + i;
+ if (j < 10) {
+ dev[9] = '0' + j;
+ dev[10] = '\0';
+ } else {
+ dev[9] = '0' + (j / 10);
+ dev[10] = '0' + (j % 10);
+ }
+
+ if (cyg_io_lookup(dev, &chan->handle) == ENOERR) {
+ diag_printf("Opened ADC channel '%s'\n", dev);
+ num++;
+ }
+ }
+ }
+
+ // Make channels non-blocking
+ for (i = 0; i < num; i++) {
+ chan = &channels[i];
+ cfg_data = 0;
+ len = sizeof(cfg_data);
+ res = cyg_io_set_config(chan->handle,
+ CYG_IO_SET_CONFIG_READ_BLOCKING,
+ &cfg_data, &len);
+ if (res != ENOERR)
+ CYG_TEST_FAIL_FINISH("Error switching ADC channel to non blocking");
+
+ chan->count = 0;
+ }
+
+ start_time = cyg_current_time();
+ do {
+ for (i = 0; i < num; i++) {
+ chan = &channels[i];
+
+ // Read a samples from the channel
+ do {
+ cyg_uint32 len = sizeof(sample);
+ res = cyg_io_read(chan->handle, &sample, &len);
+ if (res == ENOERR) {
+ chan->count++;
+ chan->sample = sample;
+ }
+ } while (res == ENOERR);
+ }
+
+ // 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("\n");
+ for (i = 0; i < num; i++) {
+ chan = &channels[i];
+ diag_printf("adc%d%d\t= %d\n", chan->device, chan->channel, chan->count);
+ }
+ seconds++;
+ }
+ } while (seconds < 10);
+
+ // Now stop all channels
+ for (i = 0; i < num; i++) {
+ chan = &channels[i];
+ res = cyg_io_set_config(chan->handle, CYG_IO_SET_CONFIG_ADC_DISABLE, 0, 0);
+ if (res != ENOERR)
+ 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 and disable the channels
+ for (i = 0; i < num; i++) {
+ chan = &channels[i];
+ do {
+ len = sizeof(sample);
+ res = cyg_io_read(chan->handle, &sample, &len);
+ if (res == ENOERR)
+ chan->count++;
+ } while (res == ENOERR);
+ }
+
+ diag_printf("\n\n----------------------------------------\n");
+
+ samples_expected = final_seconds * CYGNUM_DEVS_ADC_CORTEXM_STM32_ADC_TEST_RATE;
+ diag_printf("Samples expected after %d milliseconds: %d\n",
+ end_time, samples_expected);
+
+ diag_printf("Samples read (per channel):\n");
+ for (i = 0; i < num; i++) {
+ chan = &channels[i];
+ diag_printf("adc%d%d\t= %d\n", chan->device, chan->channel, chan->count);
+ }
+
+ diag_printf("Last read sample (per channel):\n");
+ for (i = 0; i < num; i++) {
+ chan = &channels[i];
+ diag_printf("adc%d%d\t= %d\n", chan->device, chan->channel, chan->sample);
+ }
+
+ 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,
+ "stm32_adc_thread",
+ thread_stack,
+ sizeof(thread_stack),
+ &thread_handle,
+ &thread_data
+ );
+ cyg_thread_resume(thread_handle);
+ cyg_scheduler_start();
+}
+
+#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
diff --git a/packages/ecos.db b/packages/ecos.db
index b8b2613..ce7282d 100644
--- a/packages/ecos.db
+++ b/packages/ecos.db
@@ -6848,6 +6848,16 @@ package CYGPKG_DEVS_WALLCLOCK_STM32 {
STM32 controller and compatibles"
}
+package CYGPKG_DEVS_ADC_CORTEXM_STM32 {
+ alias { "STM32 ADC driver" adc_stm32 }
+ hardware
+ directory devs/adc/cortexm/stm32
+ script adc_stm32.cdl
+ description "
+ This package provides a driver for the ADC interfaces found on the
+ ST STM32 microcontroller family."
+}
+
target stm3210e_eval {
alias { "ST STM3210E EVAL board" stm3210e }
packages { CYGPKG_HAL_CORTEXM