aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--os/hal/dox/adc.dox16
-rw-r--r--os/hal/include/adc.h49
-rw-r--r--os/hal/platforms/STM32F1xx/adc_lld.c26
-rw-r--r--os/hal/platforms/STM32F1xx/adc_lld.h30
-rw-r--r--os/hal/platforms/STM32L1xx/adc_lld.c51
-rw-r--r--os/hal/platforms/STM32L1xx/adc_lld.h34
-rw-r--r--os/hal/src/adc.c7
-rw-r--r--readme.txt7
-rw-r--r--testhal/STM32F1xx/ADC/main.c7
-rw-r--r--testhal/STM32L1xx/ADC/main.c7
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. */
/*===========================================================================*/
@@ -148,6 +139,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.
*/
typedef struct ADCDriver ADCDriver;
@@ -163,6 +163,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
* operation.
@@ -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. */
/*===========================================================================*/
@@ -176,6 +166,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.
*/
typedef struct ADCDriver ADCDriver;
@@ -191,6 +191,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
* operation.
@@ -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),