From 4a3e3fc01ec6dfb4a710db771bee262d5dc9327e Mon Sep 17 00:00:00 2001
From: gdisirio <gdisirio@35acf78f-673a-0410-8e92-d51de3d6d3f4>
Date: Thu, 22 Sep 2011 14:53:42 +0000
Subject: git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@3381
 35acf78f-673a-0410-8e92-d51de3d6d3f4

---
 os/hal/dox/adc.dox                   | 16 ++++++++---
 os/hal/include/adc.h                 | 49 +++++++++++++++++++++++++++++++++-
 os/hal/platforms/STM32F1xx/adc_lld.c | 26 +++++++++---------
 os/hal/platforms/STM32F1xx/adc_lld.h | 30 ++++++++++++++-------
 os/hal/platforms/STM32L1xx/adc_lld.c | 51 +++++++++++++++++++++++++++---------
 os/hal/platforms/STM32L1xx/adc_lld.h | 34 ++++++++++++++++--------
 os/hal/src/adc.c                     |  7 ++++-
 readme.txt                           |  7 ++++-
 testhal/STM32F1xx/ADC/main.c         |  7 +++++
 testhal/STM32L1xx/ADC/main.c         |  7 +++++
 10 files changed, 182 insertions(+), 52 deletions(-)

diff --git a/os/hal/dox/adc.dox b/os/hal/dox/adc.dox
index bb8ff014f..53bfb75d2 100644
--- a/os/hal/dox/adc.dox
+++ b/os/hal/dox/adc.dox
@@ -34,8 +34,9 @@
  * @if LATEX_PDF
  * @dot
   digraph example {
-    size="5, 7";
     rankdir="LR";
+    size="5, 7";
+
     node [shape=circle, fontname=Helvetica, fontsize=8, fixedsize="true", width="0.9", height="0.9"];
     edge [fontname=Helvetica, fontsize=8];
 
@@ -43,6 +44,7 @@
     uninit [label="ADC_UNINIT", style="bold"];
     ready [label="ADC_READY\nClock Enabled"];
     active [label="ADC_ACTIVE\nConverting"];
+    error [label="ADC_ERROR\nError"];
     complete [label="ADC_COMPLETE\nComplete"];
 
     uninit -> stop [label="\n adcInit()", constraint=false];
@@ -53,15 +55,19 @@
     ready -> active [label="\nadcStartConversion() (async)\nadcConvert() (sync)"];
     active -> ready [label="\nadcStopConversion()\nsync return"];
     active -> active [label="\nasync callback (half buffer)\nasync callback (full buffer circular)\n>acg_endcb<"];
-    active -> complete [label="\nasync callback (full buffer)\n>acg_endcb<"];
+    active -> complete [label="\n\nasync callback (full buffer)\n>end_cb<"];
+    active -> error [label="\n\nasync callback (error)\n>error_cb<"];
     complete -> active [label="\nadcStartConversionI()\nthen\ncallback return"];
     complete -> ready [label="\ncallback return"];
+    error -> active [label="\nadcStartConversionI()\nthen\ncallback return"];
+    error -> ready [label="\ncallback return"];
   }
  * @enddot
  * @else
  * @dot
   digraph example {
     rankdir="LR";
+
     node [shape=circle, fontname=Helvetica, fontsize=8, fixedsize="true", width="0.9", height="0.9"];
     edge [fontname=Helvetica, fontsize=8];
 
@@ -69,6 +75,7 @@
     uninit [label="ADC_UNINIT", style="bold"];
     ready [label="ADC_READY\nClock Enabled"];
     active [label="ADC_ACTIVE\nConverting"];
+    error [label="ADC_ERROR\nError"];
     complete [label="ADC_COMPLETE\nComplete"];
 
     uninit -> stop [label="\n adcInit()", constraint=false];
@@ -79,9 +86,12 @@
     ready -> active [label="\nadcStartConversion() (async)\nadcConvert() (sync)"];
     active -> ready [label="\nadcStopConversion()\nsync return"];
     active -> active [label="\nasync callback (half buffer)\nasync callback (full buffer circular)\n>acg_endcb<"];
-    active -> complete [label="\nasync callback (full buffer)\n>acg_endcb<"];
+    active -> complete [label="\n\nasync callback (full buffer)\n>end_cb<"];
+    active -> error [label="\n\nasync callback (error)\n>error_cb<"];
     complete -> active [label="\nadcStartConversionI()\nthen\ncallback return"];
     complete -> ready [label="\ncallback return"];
+    error -> active [label="\nadcStartConversionI()\nthen\ncallback return"];
+    error -> ready [label="\ncallback return"];
   }
  * @enddot
  * @endif
diff --git a/os/hal/include/adc.h b/os/hal/include/adc.h
index 53c7c199a..cb392f4e2 100644
--- a/os/hal/include/adc.h
+++ b/os/hal/include/adc.h
@@ -80,7 +80,8 @@ typedef enum {
   ADC_STOP = 1,                             /**< Stopped.                   */
   ADC_READY = 2,                            /**< Ready.                     */
   ADC_ACTIVE = 3,                           /**< Converting.                */
-  ADC_COMPLETE = 4                          /**< Conversion complete.       */
+  ADC_COMPLETE = 4,                         /**< Conversion complete.       */
+  ADC_ERROR = 5                             /**< Conversion complete.       */
 } adcstate_t;
 
 #include "adc_lld.h"
@@ -144,10 +145,30 @@ typedef enum {
   }                                                                         \
 }
 
+/**
+ * @brief   Wakes up the waiting thread with a timeout message.
+ *
+ * @param[in] adcp      pointer to the @p ADCDriver object
+ *
+ * @notapi
+ */
+#define _adc_timeout_isr(adcp) {                                            \
+  if ((adcp)->thread != NULL) {                                             \
+    Thread *tp;                                                             \
+    chSysLockFromIsr();                                                     \
+    tp = (adcp)->thread;                                                    \
+    (adcp)->thread = NULL;                                                  \
+    tp->p_u.rdymsg = RDY_TIMEOUT;                                           \
+    chSchReadyI(tp);                                                        \
+    chSysUnlockFromIsr();                                                   \
+  }                                                                         \
+}
+
 #else /* !ADC_USE_WAIT */
 #define _adc_reset_i(adcp)
 #define _adc_reset_s(adcp)
 #define _adc_wakeup_isr(adcp)
+#define _adc_timeout_isr(adcp)
 #endif /* !ADC_USE_WAIT */
 
 /**
@@ -220,6 +241,32 @@ typedef enum {
     _adc_wakeup_isr(adcp);                                                  \
   }                                                                         \
 }
+
+/**
+ * @brief   Common ISR code, error event.
+ * @details This code handles the portable part of the ISR code:
+ *          - Callback invocation.
+ *          - Waiting thread timeout signaling, if any.
+ *          - Driver state transitions.
+ *          .
+ * @note    This macro is meant to be used in the low level drivers
+ *          implementation only.
+ *
+ * @param[in] adcp      pointer to the @p ADCDriver object
+ *
+ * @notapi
+ */
+#define _adc_isr_error_code(adcp, err) {                                    \
+  adc_lld_stop_conversion(adcp);                                            \
+  if ((adcp)->grpp->error_cb != NULL) {                                     \
+    (adcp)->state = ADC_ERROR;                                              \
+    (adcp)->grpp->error_cb(adcp, err);                                      \
+    if ((adcp)->state == ADC_ERROR)                                         \
+      (adcp)->state = ADC_READY;                                            \
+  }                                                                         \
+  (adcp)->grpp = NULL;                                                      \
+  _adc_timeout_isr(adcp);                                                   \
+}
 /** @} */
 
 /*===========================================================================*/
diff --git a/os/hal/platforms/STM32F1xx/adc_lld.c b/os/hal/platforms/STM32F1xx/adc_lld.c
index ac74251f8..84b194c0f 100644
--- a/os/hal/platforms/STM32F1xx/adc_lld.c
+++ b/os/hal/platforms/STM32F1xx/adc_lld.c
@@ -57,20 +57,20 @@ ADCDriver ADCD1;
 static void adc_lld_serve_rx_interrupt(ADCDriver *adcp, uint32_t flags) {
 
   /* DMA errors handling.*/
-#if defined(STM32_ADC_DMA_ERROR_HOOK)
   if ((flags & STM32_DMA_ISR_TEIF) != 0) {
-    STM32_ADC_DMA_ERROR_HOOK(spip);
+    /* DMA, this could help only if the DMA tries to access an unmapped
+       address space or violates alignment rules.*/
+    _adc_isr_error_code(adcp, ADC_ERR_DMAFAILURE);
   }
-#else
-  (void)flags;
-#endif
-  if ((flags & STM32_DMA_ISR_HTIF) != 0) {
-    /* Half transfer processing.*/
-    _adc_isr_half_code(adcp);
-  }
-  if ((flags & STM32_DMA_ISR_TCIF) != 0) {
-    /* Transfer complete processing.*/
-    _adc_isr_full_code(adcp);
+  else {
+    if ((flags & STM32_DMA_ISR_HTIF) != 0) {
+      /* Half transfer processing.*/
+      _adc_isr_half_code(adcp);
+    }
+    if ((flags & STM32_DMA_ISR_TCIF) != 0) {
+      /* Transfer complete processing.*/
+      _adc_isr_full_code(adcp);
+    }
   }
 }
 
@@ -146,7 +146,7 @@ void adc_lld_start(ADCDriver *adcp) {
 
     /* ADC setup, the calibration procedure has already been performed
        during initialization.*/
-    adcp->adc->CR1 = ADC_CR1_SCAN;
+    adcp->adc->CR1 = 0;
     adcp->adc->CR2 = 0;
   }
 }
diff --git a/os/hal/platforms/STM32F1xx/adc_lld.h b/os/hal/platforms/STM32F1xx/adc_lld.h
index 1d1052f50..e3a327afa 100644
--- a/os/hal/platforms/STM32F1xx/adc_lld.h
+++ b/os/hal/platforms/STM32F1xx/adc_lld.h
@@ -108,15 +108,6 @@
 #define STM32_ADC_ADC1_IRQ_PRIORITY         5
 #endif
 
-/**
- * @brief   ADC DMA error hook.
- * @note    The default action for DMA errors is a system halt because DMA
- *          error can only happen because programming errors.
- */
-#if !defined(STM32_ADC_DMA_ERROR_HOOK) || defined(__DOXYGEN__)
-#define STM32_ADC_DMA_ERROR_HOOK(adcp)      chSysHalt()
-#endif
-
 /*===========================================================================*/
 /* Derived constants and error checks.                                       */
 /*===========================================================================*/
@@ -147,6 +138,15 @@ typedef uint16_t adcsample_t;
  */
 typedef uint16_t adc_channels_num_t;
 
+/**
+ * @brief   Possible ADC failure causes.
+ * @note    Error codes are architecture dependent and should not relied
+ *          upon.
+ */
+typedef enum {
+  ADC_ERR_DMAFAILURE = 0                    /**< DMA operations failure.    */
+} adcerror_t;
+
 /**
  * @brief   Type of a structure representing an ADC driver.
  */
@@ -162,6 +162,14 @@ typedef struct ADCDriver ADCDriver;
  */
 typedef void (*adccallback_t)(ADCDriver *adcp, adcsample_t *buffer, size_t n);
 
+/**
+ * @brief   ADC error callback type.
+ *
+ * @param[in] adcp      pointer to the @p ADCDriver object triggering the
+ *                      callback
+ */
+typedef void (*adcerrorcallback_t)(ADCDriver *adcp, adcerror_t err);
+
 /**
  * @brief   Conversion group configuration structure.
  * @details This implementation-dependent structure describes a conversion
@@ -183,6 +191,10 @@ typedef struct {
    * @brief   Callback function associated to the group or @p NULL.
    */
   adccallback_t             end_cb;
+  /**
+   * @brief   Error callback or @p NULL.
+   */
+  adcerrorcallback_t        error_cb;
   /* End of the mandatory fields.*/
   /**
    * @brief   ADC CR1 register initialization data.
diff --git a/os/hal/platforms/STM32L1xx/adc_lld.c b/os/hal/platforms/STM32L1xx/adc_lld.c
index 9ccb198e4..a2149b6ae 100644
--- a/os/hal/platforms/STM32L1xx/adc_lld.c
+++ b/os/hal/platforms/STM32L1xx/adc_lld.c
@@ -57,20 +57,20 @@ ADCDriver ADCD1;
 static void adc_lld_serve_rx_interrupt(ADCDriver *adcp, uint32_t flags) {
 
   /* DMA errors handling.*/
-#if defined(STM32_ADC_DMA_ERROR_HOOK)
   if ((flags & STM32_DMA_ISR_TEIF) != 0) {
-    STM32_ADC_DMA_ERROR_HOOK(spip);
+    /* DMA, this could help only if the DMA tries to access an unmapped
+       address space or violates alignment rules.*/
+    _adc_isr_error_code(adcp, ADC_ERR_DMAFAILURE);
   }
-#else
-  (void)flags;
-#endif
-  if ((flags & STM32_DMA_ISR_HTIF) != 0) {
-    /* Half transfer processing.*/
-    _adc_isr_half_code(adcp);
-  }
-  if ((flags & STM32_DMA_ISR_TCIF) != 0) {
-    /* Transfer complete processing.*/
-    _adc_isr_full_code(adcp);
+  else {
+    if ((flags & STM32_DMA_ISR_HTIF) != 0) {
+      /* Half transfer processing.*/
+      _adc_isr_half_code(adcp);
+    }
+    if ((flags & STM32_DMA_ISR_TCIF) != 0) {
+      /* Transfer complete processing.*/
+      _adc_isr_full_code(adcp);
+    }
   }
 }
 
@@ -78,6 +78,29 @@ static void adc_lld_serve_rx_interrupt(ADCDriver *adcp, uint32_t flags) {
 /* Driver interrupt handlers.                                                */
 /*===========================================================================*/
 
+#if STM32_ADC_USE_ADC1 || defined(__DOXYGEN__)
+/**
+ * @brief   ADC1 interrupt handler.
+ *
+ * @isr
+ */
+CH_IRQ_HANDLER(UART5_IRQHandler) {
+  uint32_t sr;
+
+  CH_IRQ_PROLOGUE();
+
+  sr = ADC1->SR;
+  ADC1->SR = 0;
+  if (sr & ADC_SR_OVR) {
+    /* ADC overflow condition, this could happen only if the DMA is unable
+       to read data fast enough.*/
+    _adc_isr_error_code(&ADCD1, ADC_ERR_OVERFLOW);
+  }
+
+  CH_IRQ_EPILOGUE();
+}
+#endif
+
 /*===========================================================================*/
 /* Driver exported functions.                                                */
 /*===========================================================================*/
@@ -145,6 +168,7 @@ void adc_lld_stop(ADCDriver *adcp) {
   if (adcp->state == ADC_READY) {
 #if STM32_ADC_USE_ADC1
     if (&ADCD1 == adcp) {
+      ADC1->CR1 = 0;
       ADC1->CR2 = 0;
       dmaStreamRelease(adcp->dmastp);
       rccDisableADC1(FALSE);
@@ -182,7 +206,7 @@ void adc_lld_start_conversion(ADCDriver *adcp) {
 
   /* ADC setup.*/
   adcp->adc->SR    = 0;
-  adcp->adc->CR1   = grpp->cr1 | ADC_CR1_SCAN;
+  adcp->adc->CR1   = grpp->cr1 | ADC_CR1_OVRIE | ADC_CR1_SCAN;
   adcp->adc->SMPR1 = grpp->smpr1;       /* Writing SMPRx requires ADON=0.   */
   adcp->adc->SMPR2 = grpp->smpr2;
   adcp->adc->SMPR3 = grpp->smpr3;
@@ -211,6 +235,7 @@ void adc_lld_start_conversion(ADCDriver *adcp) {
 void adc_lld_stop_conversion(ADCDriver *adcp) {
 
   dmaStreamDisable(adcp->dmastp);
+  adcp->adc->CR1 = 0;
   adcp->adc->CR2 = 0;
 }
 
diff --git a/os/hal/platforms/STM32L1xx/adc_lld.h b/os/hal/platforms/STM32L1xx/adc_lld.h
index 2889387ab..0ca41c269 100644
--- a/os/hal/platforms/STM32L1xx/adc_lld.h
+++ b/os/hal/platforms/STM32L1xx/adc_lld.h
@@ -39,8 +39,7 @@
  * @name    Triggers selection
  * @{
  */
-#define ADC_CR2_EXTSEL_SRC(n)   ((n) << 17) /**< @brief Trigger source.     */
-#define ADC_CR2_EXTSEL_SWSTART  (7 << 17)   /**< @brief Software trigger.   */
+#define ADC_CR2_EXTSEL_SRC(n)   ((n) << 24) /**< @brief Trigger source.     */
 /** @} */
 
 /**
@@ -136,15 +135,6 @@
 #define STM32_ADC_ADC1_IRQ_PRIORITY         5
 #endif
 
-/**
- * @brief   ADC DMA error hook.
- * @note    The default action for DMA errors is a system halt because DMA
- *          error can only happen because programming errors.
- */
-#if !defined(STM32_ADC_DMA_ERROR_HOOK) || defined(__DOXYGEN__)
-#define STM32_ADC_DMA_ERROR_HOOK(adcp)      chSysHalt()
-#endif
-
 /*===========================================================================*/
 /* Derived constants and error checks.                                       */
 /*===========================================================================*/
@@ -175,6 +165,16 @@ typedef uint16_t adcsample_t;
  */
 typedef uint16_t adc_channels_num_t;
 
+/**
+ * @brief   Possible ADC failure causes.
+ * @note    Error codes are architecture dependent and should not relied
+ *          upon.
+ */
+typedef enum {
+  ADC_ERR_DMAFAILURE = 0,                   /**< DMA operations failure.    */
+  ADC_ERR_OVERFLOW = 1                      /**< ADC overflow condition.    */
+} adcerror_t;
+
 /**
  * @brief   Type of a structure representing an ADC driver.
  */
@@ -190,6 +190,14 @@ typedef struct ADCDriver ADCDriver;
  */
 typedef void (*adccallback_t)(ADCDriver *adcp, adcsample_t *buffer, size_t n);
 
+/**
+ * @brief   ADC error callback type.
+ *
+ * @param[in] adcp      pointer to the @p ADCDriver object triggering the
+ *                      callback
+ */
+typedef void (*adcerrorcallback_t)(ADCDriver *adcp, adcerror_t err);
+
 /**
  * @brief   Conversion group configuration structure.
  * @details This implementation-dependent structure describes a conversion
@@ -211,6 +219,10 @@ typedef struct {
    * @brief   Callback function associated to the group or @p NULL.
    */
   adccallback_t             end_cb;
+  /**
+   * @brief   Error callback or @p NULL.
+   */
+  adcerrorcallback_t        error_cb;
   /* End of the mandatory fields.*/
   /**
    * @brief   ADC CR1 register initialization data.
diff --git a/os/hal/src/adc.c b/os/hal/src/adc.c
index c375818a6..08aa830f0 100644
--- a/os/hal/src/adc.c
+++ b/os/hal/src/adc.c
@@ -162,6 +162,8 @@ void adcStartConversion(ADCDriver *adcp,
 /**
  * @brief   Starts an ADC conversion.
  * @details Starts an asynchronous conversion operation.
+ * @post    The callbacks associated to the conversion group will be invoked
+ *          on buffer fill and error events.
  * @note    The buffer is organized as a matrix of M*N elements where M is the
  *          channels number configured into the conversion group and N is the
  *          buffer depth. The samples are sequentially written into the buffer
@@ -185,7 +187,8 @@ void adcStartConversionI(ADCDriver *adcp,
              ((depth == 1) || ((depth & 1) == 0)),
              "adcStartConversionI");
   chDbgAssert((adcp->state == ADC_READY) ||
-              (adcp->state == ADC_COMPLETE),
+              (adcp->state == ADC_COMPLETE) ||
+              (adcp->state == ADC_ERROR),
               "adcStartConversionI(), #1", "not ready");
 
   adcp->samples  = samples;
@@ -268,6 +271,8 @@ void adcStopConversionI(ADCDriver *adcp) {
  * @retval RDY_RESET    The conversion has been stopped using
  *                      @p acdStopConversion() or @p acdStopConversionI(),
  *                      the result buffer may contain incorrect data.
+ * @retval RDY_TIMEOUT  The conversion has been stopped because an hardware
+ *                      error.
  *
  * @api
  */
diff --git a/readme.txt b/readme.txt
index 005520649..29dfbdca2 100644
--- a/readme.txt
+++ b/readme.txt
@@ -95,7 +95,12 @@
   (backported to 2.2.4).
 - FIX: Fixed timeout problem in the lwIP interface layer (bug 3302420)
   (backported to 2.2.4).
-- NEW: STM32L1xx sub-family support, all STM32 drivers adapted and retested
+- NEW: STM32L ADC driver implementation.
+  (TODO: To be tested.)
+- NEW: Improved ADC driver model, now it is possible to handle error
+  conditions during the conversion process.
+  (TODO: Modify existing STM32 ADC implementation).
+- NEW: STM32L1xx sub-family support, all STM32 drivers adapted and re-tested
   on the new platform except ADC that will need a specific implementation. 
 - NEW: Added new API chThdExitS() in order to allow atomic operations on
   thead exit (backported to 2.2.8).
diff --git a/testhal/STM32F1xx/ADC/main.c b/testhal/STM32F1xx/ADC/main.c
index 214f28b5f..bfb717b17 100644
--- a/testhal/STM32F1xx/ADC/main.c
+++ b/testhal/STM32F1xx/ADC/main.c
@@ -41,6 +41,12 @@ static void adccallback(ADCDriver *adcp, adcsample_t *buffer, size_t n) {
   }
 }
 
+static void adcerrorcallback(ADCDriver *adcp, adcerror_t err) {
+
+  (void)adcp;
+  (void)err;
+}
+
 /*
  * ADC conversion group.
  * Mode:        Streaming, continuous, 16 samples of 8 channels, SW triggered.
@@ -50,6 +56,7 @@ static const ADCConversionGroup adcgrpcfg = {
   TRUE,
   ADC_GRP1_NUM_CHANNELS,
   adccallback,
+  adcerrorcallback,
   0,
   ADC_CR2_TSVREFE,
   0,
diff --git a/testhal/STM32L1xx/ADC/main.c b/testhal/STM32L1xx/ADC/main.c
index 48616f7d8..82ac4c4d8 100644
--- a/testhal/STM32L1xx/ADC/main.c
+++ b/testhal/STM32L1xx/ADC/main.c
@@ -41,6 +41,12 @@ static void adccallback(ADCDriver *adcp, adcsample_t *buffer, size_t n) {
   }
 }
 
+static void adcerrorcallback(ADCDriver *adcp, adcerror_t err) {
+
+  (void)adcp;
+  (void)err;
+}
+
 /*
  * ADC conversion group.
  * Mode:        Streaming, continuous, 16 samples of 8 channels, SW triggered.
@@ -50,6 +56,7 @@ static const ADCConversionGroup adcgrpcfg = {
   TRUE,
   ADC_GRP1_NUM_CHANNELS,
   adccallback,
+  adcerrorcallback,
   0, 0,         /* CR1, CR2 */
   0, 0, 0,      /* SMPR1...SMPR3 */
   ADC_SQR1_NUM_CH(ADC_GRP1_NUM_CHANNELS),
-- 
cgit v1.2.3