git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@1315 35acf78f-673a-0410-8e92-d51de3d6d3f4

master
gdisirio 2009-11-19 19:03:18 +00:00
parent a943eaecc7
commit 2ab27d3c01
10 changed files with 199 additions and 92 deletions

View File

@ -42,9 +42,12 @@ void adcInit(void) {
*/
void adcObjectInit(ADCDriver *adcp) {
adcp->ad_state = ADC_STOP;
adcp->ad_config = NULL;
adcp->ad_state = ADC_STOP;
adcp->ad_config = NULL;
adcp->ad_callback = NULL;
adcp->ad_samples = NULL;
adcp->ad_depth = 0;
adcp->ad_grpp = NULL;
chSemInit(&adcp->ad_sem, 0);
}
@ -119,7 +122,7 @@ void adcStop(ADCDriver *adcp) {
*/
bool_t adcStartConversion(ADCDriver *adcp,
ADCConversionGroup *grpp,
void *samples,
adcsample_t *samples,
size_t depth,
adccallback_t callback) {
@ -137,7 +140,10 @@ bool_t adcStartConversion(ADCDriver *adcp,
return TRUE;
}
adcp->ad_callback = callback;
adc_lld_start_conversion(adcp, grpp, samples, depth);
adcp->ad_samples = samples;
adcp->ad_depth = depth;
adcp->ad_grpp = grpp;
adc_lld_start_conversion(adcp);
adcp->ad_state = ADC_RUNNING;
chSysUnlock();
return FALSE;
@ -157,8 +163,11 @@ void adcStopConversion(ADCDriver *adcp) {
(adcp->ad_state == ADC_RUNNING),
"adcStopConversion(), #1",
"invalid state");
adc_lld_stop_conversion(adcp);
adcp->ad_state = ADC_READY;
if (adcp->ad_state == ADC_RUNNING) {
adc_lld_stop_conversion(adcp);
adcp->ad_grpp = NULL;
adcp->ad_state = ADC_READY;
}
chSysUnlock();
}

View File

@ -38,7 +38,7 @@ typedef enum {
ADC_UNINIT = 0, /**< @brief Not initialized. */
ADC_STOP = 1, /**< @brief Stopped. */
ADC_READY = 2, /**< @brief Ready. */
ADC_RUNNING = 3 /**< @brief Conversion complete.*/
ADC_RUNNING = 3 /**< @brief Conversion running. */
} adcstate_t;
#include "adc_lld.h"
@ -52,7 +52,7 @@ extern "C" {
void adcStop(ADCDriver *adcp);
bool_t adcStartConversion(ADCDriver *adcp,
ADCConversionGroup *grpp,
void *samples,
adcsample_t *samples,
size_t depth,
adccallback_t callback);
void adcStopConversion(ADCDriver *adcp);

View File

@ -29,11 +29,19 @@
#include <stm32_dma.h>
#include <nvic.h>
/*===========================================================================*/
/* Low Level Driver exported variables. */
/*===========================================================================*/
#if USE_STM32_ADC1 || defined(__DOXYGEN__)
/** @brief ADC1 driver identifier.*/
ADCDriver ADCD1;
#endif
/*===========================================================================*/
/* Low Level Driver local variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Low Level Driver local functions. */
/*===========================================================================*/
@ -47,13 +55,43 @@ ADCDriver ADCD1;
* @brief ADC1 DMA interrupt handler (channel 1).
*/
CH_IRQ_HANDLER(Vector6C) {
uint32_t isr;
CH_IRQ_PROLOGUE();
if ((DMA1->ISR & DMA_ISR_TEIF1) != 0)
isr = DMA1->ISR;
if ((isr & DMA_ISR_HTIF1) != 0) {
/* Half transfer processing.*/
if (ADCD1.ad_callback != NULL) {
/* Invokes the callback passing the 1st half of the buffer.*/
ADCD1.ad_callback(ADCD1.ad_samples, ADCD1.ad_depth / 2);
}
}
if ((isr & DMA_ISR_TCIF1) != 0) {
/* Transfer complete processing.*/
if (!ADCD1.ad_grpp->acg_circular) {
/* End conversion.*/
adc_lld_stop_conversion(&ADCD1);
ADCD1.ad_grpp = NULL;
ADCD1.ad_state = ADC_READY;
chSemResetI(&ADCD1.ad_sem, 0);
}
/* Callback handling.*/
if (ADCD1.ad_callback != NULL) {
if (ADCD1.ad_depth > 1) {
/* Invokes the callback passing the 2nd half of the buffer.*/
size_t half = ADCD1.ad_depth / 2;
ADCD1.ad_callback(ADCD1.ad_samples + half, half);
}
else {
/* Invokes the callback passing the while buffer.*/
ADCD1.ad_callback(ADCD1.ad_samples, ADCD1.ad_depth);
}
}
}
if ((isr & DMA_ISR_TEIF1) != 0) {
/* DMA error processing.*/
STM32_ADC1_DMA_ERROR_HOOK();
else {
/* */
}
DMA1->IFCR |= DMA_IFCR_CGIF1 | DMA_IFCR_CTCIF1 |
DMA_IFCR_CHTIF1 | DMA_IFCR_CTEIF1;
@ -72,10 +110,28 @@ CH_IRQ_HANDLER(Vector6C) {
void adc_lld_init(void) {
#if USE_STM32_ADC1
/* Driver initialization.*/
adcObjectInit(&ADCD1);
ADCD1.ad_adc = ADC1;
ADCD1.ad_dma = DMA1_Channel1;
ADCD1.ad_dmaprio = STM32_ADC1_DMA_PRIORITY << 12;
/* Temporary activation.*/
ADC1->CR1 = 0;
ADC1->CR2 = ADC_CR2_ADON;
/* Reset calibration just to be safe.*/
ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_RSTCAL;
while ((ADC1->CR2 & ADC_CR2_RSTCAL) != 0)
;
/* Calibration.*/
ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_CAL;
while ((ADC1->CR2 & ADC_CR2_CAL) != 0)
;
/* Return the ADC in low power mode.*/
ADC1->CR2 = 0;
#endif
}
@ -90,8 +146,8 @@ void adc_lld_start(ADCDriver *adcp) {
if (adcp->ad_state == ADC_STOP) {
#if USE_STM32_ADC1
if (&ADCD1 == adcp) {
dmaEnable(DMA1_ID); /* NOTE: Must be enabled before the IRQs.*/
NVICEnableVector(DMA1_Channel1_IRQn, STM32_ADC1_IRQ_PRIORITY);
dmaEnable(DMA1_ID);
DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR;
/* RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_ADCPRE) |
adcp->ad_config->ac_prescaler;*/
@ -99,19 +155,10 @@ void adc_lld_start(ADCDriver *adcp) {
}
#endif
/* ADC activation.*/
adcp->ad_adc->CR1 = 0;
/* ADC activation, the calibration procedure has already been performed
during initialization.*/
adcp->ad_adc->CR1 = ADC_CR1_SCAN;
adcp->ad_adc->CR2 = ADC_CR2_ADON;
/* Reset calibration just to be safe.*/
adcp->ad_adc->CR2 |= ADC_CR2_RSTCAL;
while ((adcp->ad_adc->CR2 & ADC_CR2_RSTCAL) != 0)
;
/* Calibration.*/
adcp->ad_adc->CR2 |= ADC_CR2_CAL;
while ((adcp->ad_adc->CR2 & ADC_CR2_CAL) != 0)
;
}
}
@ -130,7 +177,6 @@ void adc_lld_stop(ADCDriver *adcp) {
ADC1->CR2 = 0;
NVICDisableVector(DMA1_Channel1_IRQn);
dmaDisable(DMA1_ID);
/* RCC->CFGR &= ~RCC_CFGR_ADCPRE;*/
RCC->APB2ENR &= ~RCC_APB2ENR_ADC1EN;
}
#endif
@ -141,35 +187,27 @@ void adc_lld_stop(ADCDriver *adcp) {
* @brief Starts an ADC conversion.
*
* @param[in] adcp pointer to the @p ADCDriver object
* @param[in] grpp pointer to a @p ADCConversionGroup object
* @param[out] samples pointer to the samples buffer
* @param[in] depth buffer depth (matrix rows number). The buffer depth
* must be one or an even number.
*
* @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
* with no gaps.
*/
void adc_lld_start_conversion(ADCDriver *adcp,
ADCConversionGroup *grpp,
void *samples,
size_t depth) {
void adc_lld_start_conversion(ADCDriver *adcp) {
uint32_t ccr, n;
ADCConversionGroup *grpp = adcp->ad_grpp;
/* DMA setup.*/
adcp->ad_dma->CMAR = (uint32_t)samples;
if (depth > 1) {
adcp->ad_dma->CNDTR = (uint32_t)grpp->acg_num_channels * (uint32_t)depth;
adcp->ad_dma->CCR = adcp->ad_dmaprio |
DMA_CCR1_MSIZE_0 | DMA_CCR1_PSIZE_0 | DMA_CCR1_MINC |
DMA_CCR1_TCIE | DMA_CCR1_TEIE | DMA_CCR1_HTIE;
}
else {
adcp->ad_dma->CNDTR = (uint32_t)grpp->acg_num_channels;
adcp->ad_dma->CCR = adcp->ad_dmaprio |
DMA_CCR1_MSIZE_0 | DMA_CCR1_PSIZE_0 | DMA_CCR1_MINC |
DMA_CCR1_TCIE | DMA_CCR1_TEIE;
adcp->ad_dma->CMAR = (uint32_t)adcp->ad_samples;
ccr = adcp->ad_dmaprio | DMA_CCR1_EN | DMA_CCR1_MSIZE_0 | DMA_CCR1_PSIZE_0 |
DMA_CCR1_MINC | DMA_CCR1_TCIE | DMA_CCR1_TEIE;
if (grpp->acg_circular)
ccr |= DMA_CCR1_CIRC;
if (adcp->ad_depth > 1) {
/* If the buffer depth is greater than one then the half transfer interrupt
interrupt is enabled in order to allows streaming processing.*/
ccr |= DMA_CCR1_HTIE;
n = (uint32_t)grpp->acg_num_channels * (uint32_t)adcp->ad_depth;
}
else
n = (uint32_t)grpp->acg_num_channels;
adcp->ad_dma->CNDTR = n;
adcp->ad_dma->CCR = ccr;
/* ADC setup.*/
adcp->ad_adc->SMPR1 = grpp->acg_smpr1;

View File

@ -63,6 +63,8 @@
/**
* @brief ADC1 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_ADC1_DMA_ERROR_HOOK) || defined(__DOXYGEN__)
#define STM32_ADC1_DMA_ERROR_HOOK() chSysHalt()
@ -99,6 +101,11 @@ typedef void (*adccallback_t)(adcsample_t *buffer, size_t n);
* operation.
*/
typedef struct {
/**
* @brief Enables the circular buffer mode for the group.
*/
bool_t acg_circular;
/* End of the mandatory fields.*/
/**
* @brief Number of the analog channels belonging to the conversion group.
*/
@ -140,6 +147,7 @@ typedef struct {
/**
* @brief Driver configuration structure.
* @note It could be empty on some architectures.
*/
typedef struct {
/* * <----------
@ -164,13 +172,25 @@ typedef struct {
*/
const ADCConfig *ad_config;
/**
* @brief Semaphore for completion synchronization.
* @brief Synchronization semaphore.
*/
Semaphore ad_sem;
/**
* @brief Current callback function or @p NULL.
*/
adccallback_t ad_callback;
/**
* @brief Current samples buffer pointer or @p NULL.
*/
adcsample_t *ad_samples;
/**
* @brief Current samples buffer depth or @p 0.
*/
size_t ad_depth;
/**
* @brief Current conversion group pointer or @p NULL.
*/
ADCConversionGroup *ad_grpp;
/* End of the mandatory fields.*/
/**
* @brief Pointer to the ADCx registers block.
@ -190,20 +210,19 @@ typedef struct {
/* External declarations. */
/*===========================================================================*/
/** @cond never*/
#ifdef __cplusplus
extern "C" {
#endif
void adc_lld_init(void);
void adc_lld_start(ADCDriver *adcp);
void adc_lld_stop(ADCDriver *adcp);
void adc_lld_start_conversion(ADCDriver *adcp,
ADCConversionGroup *grpp,
void *samples,
size_t depth);
void adc_lld_start_conversion(ADCDriver *adcp);
void adc_lld_stop_conversion(ADCDriver *adcp);
#ifdef __cplusplus
}
#endif
/** @endcond*/
#endif /* _ADC_LLD_H_ */

View File

@ -34,11 +34,19 @@
SPIDriver SPID1;
#endif
/*===========================================================================*/
/* Low Level Driver exported variables. */
/*===========================================================================*/
#if USE_STM32_SPI2 || defined(__DOXYGEN__)
/** @brief SPI2 driver identifier.*/
SPIDriver SPID2;
#endif
/*===========================================================================*/
/* Low Level Driver local variables. */
/*===========================================================================*/
static uint16_t dummyrx;
static uint16_t dummytx;
@ -107,8 +115,9 @@ CH_IRQ_HANDLER(Vector70) {
CH_IRQ_PROLOGUE();
spi_stop(&SPID1);
if ((DMA1->ISR & DMA_ISR_TEIF2) != 0)
chEvtBroadcastI(&SPID1.spd_dmaerror);
if ((DMA1->ISR & DMA_ISR_TEIF2) != 0) {
STM32_SPI1_DMA_ERROR_HOOK();
}
DMA1->IFCR |= DMA_IFCR_CGIF2 | DMA_IFCR_CTCIF2 |
DMA_IFCR_CHTIF2 | DMA_IFCR_CTEIF2;
@ -122,7 +131,7 @@ CH_IRQ_HANDLER(Vector74) {
CH_IRQ_PROLOGUE();
chEvtBroadcastI(&SPID1.spd_dmaerror);
STM32_SPI1_DMA_ERROR_HOOK();
DMA1->IFCR |= DMA_IFCR_CGIF3 | DMA_IFCR_CTCIF3 |
DMA_IFCR_CHTIF3 | DMA_IFCR_CTEIF3;
@ -139,8 +148,9 @@ CH_IRQ_HANDLER(Vector78) {
CH_IRQ_PROLOGUE();
spi_stop(&SPID2);
if ((DMA1->ISR & DMA_ISR_TEIF4) != 0)
chEvtBroadcastI(&SPID2.spd_dmaerror);
if ((DMA1->ISR & DMA_ISR_TEIF4) != 0) {
STM32_SPI2_DMA_ERROR_HOOK();
}
DMA1->IFCR |= DMA_IFCR_CGIF4 | DMA_IFCR_CTCIF4 |
DMA_IFCR_CHTIF4 | DMA_IFCR_CTEIF4;
@ -154,7 +164,7 @@ CH_IRQ_HANDLER(Vector7C) {
CH_IRQ_PROLOGUE();
chEvtBroadcastI(&SPID2.spd_dmaerror);
STM32_SPI2_DMA_ERROR_HOOK();
DMA1->IFCR |= DMA_IFCR_CGIF5 | DMA_IFCR_CTCIF5 |
DMA_IFCR_CHTIF5 | DMA_IFCR_CTEIF5;
@ -180,7 +190,6 @@ void spi_lld_init(void) {
SPID1.spd_dmarx = DMA1_Channel2;
SPID1.spd_dmatx = DMA1_Channel3;
SPID1.spd_dmaprio = STM32_SPI1_DMA_PRIORITY << 12;
chEvtInit(&SPID1.spd_dmaerror);
GPIOA->CRL = (GPIOA->CRL & 0x000FFFFF) | 0xB4B00000;
#endif
@ -191,7 +200,6 @@ void spi_lld_init(void) {
SPID2.spd_dmarx = DMA1_Channel4;
SPID2.spd_dmatx = DMA1_Channel5;
SPID2.spd_dmaprio = STM32_SPI2_DMA_PRIORITY << 12;
chEvtInit(&SPID2.spd_dmaerror);
GPIOB->CRH = (GPIOB->CRH & 0x000FFFFF) | 0xB4B00000;
#endif
}
@ -207,7 +215,7 @@ void spi_lld_start(SPIDriver *spip) {
if (spip->spd_state == SPI_STOP) {
#if USE_STM32_SPI1
if (&SPID1 == spip) {
dmaEnable(DMA1_ID);
dmaEnable(DMA1_ID); /* NOTE: Must be enabled before the IRQs.*/
NVICEnableVector(DMA1_Channel2_IRQn, STM32_SPI1_IRQ_PRIORITY);
NVICEnableVector(DMA1_Channel3_IRQn, STM32_SPI1_IRQ_PRIORITY);
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
@ -215,7 +223,7 @@ void spi_lld_start(SPIDriver *spip) {
#endif
#if USE_STM32_SPI2
if (&SPID2 == spip) {
dmaEnable(DMA1_ID);
dmaEnable(DMA1_ID); /* NOTE: Must be enabled before the IRQs.*/
NVICEnableVector(DMA1_Channel4_IRQn, STM32_SPI2_IRQ_PRIORITY);
NVICEnableVector(DMA1_Channel5_IRQn, STM32_SPI2_IRQ_PRIORITY);
RCC->APB1ENR |= RCC_APB1ENR_SPI2EN;

View File

@ -93,6 +93,24 @@
#define STM32_SPI2_IRQ_PRIORITY 0x60
#endif
/**
* @brief SPI1 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_SPI1_DMA_ERROR_HOOK) || defined(__DOXYGEN__)
#define STM32_SPI1_DMA_ERROR_HOOK() chSysHalt()
#endif
/**
* @brief SPI2 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_SPI2_DMA_ERROR_HOOK) || defined(__DOXYGEN__)
#define STM32_SPI2_DMA_ERROR_HOOK() chSysHalt()
#endif
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
@ -158,10 +176,6 @@ typedef struct {
* @brief DMA priority bit mask.
*/
uint32_t spd_dmaprio;
/**
* @brief DMA error event.
*/
EventSource spd_dmaerror;
} SPIDriver;
/*===========================================================================*/

View File

@ -27,6 +27,14 @@
#include <ch.h>
#include <adc.h>
/*===========================================================================*/
/* Low Level Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Low Level Driver local variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Low Level Driver local functions. */
/*===========================================================================*/
@ -49,7 +57,7 @@ void adc_lld_init(void) {
/**
* @brief Configures and activates the ADC peripheral.
*
* @param[in] adcp pointer to the @p ADCDriver object
* @param[in] adcp pointer to the @p ADCDriver object
*/
void adc_lld_start(ADCDriver *adcp) {
@ -62,7 +70,7 @@ void adc_lld_start(ADCDriver *adcp) {
/**
* @brief Deactivates the ADC peripheral.
*
* @param[in] adcp pointer to the @p ADCDriver object
* @param[in] adcp pointer to the @p ADCDriver object
*/
void adc_lld_stop(ADCDriver *adcp) {
@ -71,28 +79,16 @@ void adc_lld_stop(ADCDriver *adcp) {
/**
* @brief Starts an ADC conversion.
*
* @param[in] adcp pointer to the @p ADCDriver object
* @param[in] grpp pointer to a @p ADCConversionGroup object
* @param[out] samples pointer to the samples buffer
* @param[in] depth buffer depth (matrix rows number). The buffer depth
* must be one or an even number.
*
* @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
* with no gaps.
* @param[in] adcp pointer to the @p ADCDriver object
*/
void adc_lld_start_conversion(ADCDriver *adcp,
ADCConversionGroup *grpp,
void *samples,
size_t depth) {
void adc_lld_start_conversion(ADCDriver *adcp) {
}
/**
* @brief Stops an ongoing conversion.
*
* @param[in] adcp pointer to the @p ADCDriver object
* @param[in] adcp pointer to the @p ADCDriver object
*/
void adc_lld_stop_conversion(ADCDriver *adcp) {

View File

@ -70,6 +70,7 @@ typedef struct {
/**
* @brief Driver configuration structure.
* @note It could be empty on some architectures.
*/
typedef struct {
@ -88,13 +89,25 @@ typedef struct {
*/
const ADCConfig *ad_config;
/**
* @brief Semaphore for completion synchronization.
* @brief Synchronization semaphore.
*/
Semaphore ac_sem;
Semaphore ad_sem;
/**
* @brief Current callback function or @p NULL.
*/
adccallback_t ad_callback;
/**
* @brief Current samples buffer pointer or @p NULL.
*/
adcsample_t *ad_samples;
/**
* @brief Current samples buffer depth or @p 0.
*/
size_t ad_depth;
/**
* @brief Current conversion group pointer or @p NULL.
*/
ADCConversionGroup *ad_grpp;
/* End of the mandatory fields.*/
} ADCDriver;
@ -108,10 +121,7 @@ extern "C" {
void adc_lld_init(void);
void adc_lld_start(ADCDriver *adcp);
void adc_lld_stop(ADCDriver *adcp);
void adc_lld_start_conversion(ADCDriver *adcp,
ADCConversionGroup *grpp,
void *samples,
size_t depth);
void adc_lld_start_conversion(ADCDriver *adcp);
void adc_lld_stop_conversion(ADCDriver *adcp);
#ifdef __cplusplus
}

View File

@ -27,6 +27,14 @@
#include <ch.h>
#include <spi.h>
/*===========================================================================*/
/* Low Level Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Low Level Driver local variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Low Level Driver local functions. */
/*===========================================================================*/

View File

@ -3,8 +3,13 @@
*****************************************************************************
*** 1.3.5 ***
- NEW: STM32 ADC driver implementation with DMA support.
- CHANGE: In the STM32 drivers now the DMA errors are handled by hook macros
rather than by events. The default action is to halt the system but users
are able to override this and define custom handling.
- CHANGE: In the Cortex-M3 port, modified the NVICEnableVector() function
to make it clear pending interrupts.
- CHANGE: Minor changes to the ADC driver model.
*** 1.3.4 ***
- FIX: Fixed bug in STM32 PAL port driver (bug 2897636).