From 03ef7de020002f6d48447eebdec16e3b16eaf7a3 Mon Sep 17 00:00:00 2001 From: gdisirio Date: Thu, 28 Feb 2013 14:12:58 +0000 Subject: git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@5338 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- demos/PPC-SPC563M-GCC/mcuconf.h | 8 +- os/hal/platforms/SPC5xx/EQADC_v1/adc_lld.c | 157 +++++++++++++++++++++++++++- os/hal/platforms/SPC5xx/EQADC_v1/adc_lld.h | 160 ++++++++++++++++++++++++++--- 3 files changed, 308 insertions(+), 17 deletions(-) diff --git a/demos/PPC-SPC563M-GCC/mcuconf.h b/demos/PPC-SPC563M-GCC/mcuconf.h index da2b259e9..c3d3be8f3 100644 --- a/demos/PPC-SPC563M-GCC/mcuconf.h +++ b/demos/PPC-SPC563M-GCC/mcuconf.h @@ -45,7 +45,13 @@ /* * ADC driver settings. */ -#define SPC5_ADC_USE_EQADC_Q0 TRUE +#define SPC5_ADC_USE_ADC0_Q0 TRUE +#define SPC5_ADC_USE_ADC0_Q1 FALSE +#define SPC5_ADC_USE_ADC0_Q2 FALSE +#define SPC5_ADC_USE_ADC1_Q3 TRUE +#define SPC5_ADC_USE_ADC1_Q4 FALSE +#define SPC5_ADC_USE_ADC1_Q5 FALSE +#define SPC5_ADC_CR_CLK_PS ADC_CR_CLK_PS(5) /* * SERIAL driver system settings. diff --git a/os/hal/platforms/SPC5xx/EQADC_v1/adc_lld.c b/os/hal/platforms/SPC5xx/EQADC_v1/adc_lld.c index 5bd1113f7..7aa82fb5d 100644 --- a/os/hal/platforms/SPC5xx/EQADC_v1/adc_lld.c +++ b/os/hal/platforms/SPC5xx/EQADC_v1/adc_lld.c @@ -29,6 +29,12 @@ /* Driver local definitions. */ /*===========================================================================*/ +/** + * @brief Calibration constant. + * @details Ideal conversion result for 75%(VRH - VRL) minus 2. + */ +#define ADC_IDEAL_RES75_2 12286 + /*===========================================================================*/ /* Driver exported variables. */ /*===========================================================================*/ @@ -36,16 +42,51 @@ /** * @brief ADCD1 driver identifier. */ -#if SPC5_ADC_USE_EQADC_Q0 || defined(__DOXYGEN__) +#if SPC5_ADC_USE_ADC0_Q0 || defined(__DOXYGEN__) ADCDriver ADCD1; #endif +/** + * @brief ADCD2 driver identifier. + */ +#if SPC5_ADC_USE_ADC0_Q1 || defined(__DOXYGEN__) +ADCDriver ADCD2; +#endif + +/** + * @brief ADCD3 driver identifier. + */ +#if SPC5_ADC_USE_ADC0_Q2 || defined(__DOXYGEN__) +ADCDriver ADCD3; +#endif + +/** + * @brief ADCD4 driver identifier. + */ +#if SPC5_ADC_USE_ADC1_Q3 || defined(__DOXYGEN__) +ADCDriver ADCD4; +#endif + +/** + * @brief ADCD5 driver identifier. + */ +#if SPC5_ADC_USE_ADC1_Q4 || defined(__DOXYGEN__) +ADCDriver ADCD5; +#endif + +/** + * @brief ADCD6 driver identifier. + */ +#if SPC5_ADC_USE_ADC1_Q5 || defined(__DOXYGEN__) +ADCDriver ADCD6; +#endif + /*===========================================================================*/ /* Driver local variables. */ /*===========================================================================*/ /*===========================================================================*/ -/* Driver local functions. */ +/* Driver local functions and macros. */ /*===========================================================================*/ /** @@ -121,6 +162,93 @@ static void cfifo0_wait_rfifo(uint32_t n) { EQADC.FISR[0].R = EQADC_FISR_CLEAR_MASK; } +/** + * @brief Reads a sample from the RFIFO0. + * + * @notapi + */ +#define rfifo0_get_value() (EQADC.RFPR[0].R) + +/** + * @brief Writes an internal ADC register. + * + * @param[in] adc the ADC unit + * @param[in] reg the register index + * @param[in] value value to be written into the register + * + * @notapi + */ +#define adc_write_register(adc, reg, value) \ + cfifo0_push_command(EQADC_RW_WRITE | (adc) | EQADC_RW_REG_ADDR(reg) | \ + EQADC_RW_VALUE(value)) + + +/** + * @brief Enables both ADCs. + * + * @notapi + */ +static void adc_enable(void) { + + /* Both ADCs must be enabled because this sentence in the reference manual: + "Both ADC0 and ADC1 of an eQADC module pair must be enabled before + calibrating or using either ADC0 or ADC1 of the pair. Failure to + enable both ADC0 and ADC1 of the pair can result in inaccurate + conversions.".*/ + adc_write_register(EQADC_RW_BN_ADC0, ADC_REG_CR, + SPC5_ADC_CR_CLK_PS | ADC_CR_EN); + adc_write_register(EQADC_RW_BN_ADC1, ADC_REG_CR, + SPC5_ADC_CR_CLK_PS | ADC_CR_EN); +} + +/** + * @brief Disables both ADCs. + * + * @notapi + */ +static void adc_disable(void) { + + adc_write_register(EQADC_RW_BN_ADC0, ADC_REG_CR, + SPC5_ADC_CR_CLK_PS); + adc_write_register(EQADC_RW_BN_ADC1, ADC_REG_CR, + SPC5_ADC_CR_CLK_PS); +} + +/** + * @brief Calibrates both ADCs. + * + * @notapi + */ +static void adc_calibrate(uint32_t adc) { + uint16_t res25, res75; + uint32_t gcc, occ; + + /* Starts the calibration, write command messages to sample 25% and + 75% VREF.*/ + cfifo0_push_command(0x00002C00 | adc); /* Vref 25%.*/ + cfifo0_push_command(0x00002B00 | adc); /* Vref 75%.*/ + cfifo0_wait_rfifo(2); + + /* Reads the results and compute calibration register values.*/ + res25 = rfifo0_get_value(); + res75 = rfifo0_get_value(); + + gcc = 0x08000000UL / ((uint32_t)res75 - (uint32_t)res25); + occ = (uint32_t)ADC_IDEAL_RES75_2 - ((gcc * (uint32_t)res75) >> 14); + + /* Loads the gain and offset values (default configuration, 12 bits).*/ + adc_write_register(adc, ADC_REG_GCCR, gcc); + adc_write_register(adc, ADC_REG_OCCR, occ & 0xFFFF); + + /* Loads gain and offset values (alternate configuration 1, 10 bits).*/ + adc_write_register(adc, ADC_REG_AC1GCCR, gcc); + adc_write_register(adc, ADC_REG_AC1OCCR, occ & 0xFFFF); + + /* Loads gain and offset values (alternate configuration 1, 8 bits).*/ + adc_write_register(adc, ADC_REG_AC2GCCR, gcc); + adc_write_register(adc, ADC_REG_AC2OCCR, occ & 0xFFFF); +} + /*===========================================================================*/ /* Driver interrupt handlers. */ /*===========================================================================*/ @@ -140,6 +268,27 @@ void adc_lld_init(void) { /* Driver initialization.*/ adcObjectInit(&ADCD1); #endif /* SPC5_ADC_USE_EQADC_Q0 */ + + /* Temporarily enables CFIFO0 for calibration and initialization.*/ + cfifo_enable(ADC_FIFO_0, EQADC_CFCR_SSE | EQADC_CFCR_MODE_SWCS, 0); + adc_enable(); + + /* Calibration of both ADC units then programming alternate configs + one and two for 10 and 8 bits operations.*/ +#if SPC5_ADC_USE_ADC0 + adc_calibrate(EQADC_RW_BN_ADC0); + adc_write_register(EQADC_RW_BN_ADC0, ADC_REG_AC1CR, ADC_ACR_RESSEL_10BITS); + adc_write_register(EQADC_RW_BN_ADC0, ADC_REG_AC2CR, ADC_ACR_RESSEL_8BITS); +#endif +#if SPC5_ADC_USE_ADC1 + adc_calibrate(EQADC_RW_BN_ADC1); + adc_write_register(EQADC_RW_BN_ADC1, ADC_REG_AC1CR, ADC_ACR_RESSEL_10BITS); + adc_write_register(EQADC_RW_BN_ADC1, ADC_REG_AC2CR, ADC_ACR_RESSEL_8BITS); +#endif + + /* ADCs disabled until the driver is started by the application.*/ + adc_disable(); + cfifo_disable(ADC_FIFO_0); } /** @@ -153,7 +302,7 @@ void adc_lld_start(ADCDriver *adcp) { if (adcp->state == ADC_STOP) { /* Enables the peripheral.*/ -#if SPC5_ADC_USE_EQADC_Q0 +#if SPC5_ADC_USE_ADC0 if (&ADCD1 == adcp) { } @@ -176,7 +325,7 @@ void adc_lld_stop(ADCDriver *adcp) { /* Resets the peripheral.*/ /* Disables the peripheral.*/ -#if SPC5_ADC_USE_EQADC_Q0 +#if SPC5_ADC_USE_ADC0 if (&ADCD1 == adcp) { } diff --git a/os/hal/platforms/SPC5xx/EQADC_v1/adc_lld.h b/os/hal/platforms/SPC5xx/EQADC_v1/adc_lld.h index 5e63489f2..a946e3ee8 100644 --- a/os/hal/platforms/SPC5xx/EQADC_v1/adc_lld.h +++ b/os/hal/platforms/SPC5xx/EQADC_v1/adc_lld.h @@ -78,7 +78,7 @@ #define EQADC_CFCR_MODE_HWSS_FE EQADC_CFCR_MODE(4) #define EQADC_CFCR_MODE_HWSS_RE EQADC_CFCR_MODE(5) #define EQADC_CFCR_MODE_HWSS_BE EQADC_CFCR_MODE(6) -#define EQADC_CFCR_MODE_MODE_SWCS EQADC_CFCR_MODE(9) +#define EQADC_CFCR_MODE_SWCS EQADC_CFCR_MODE(9) #define EQADC_CFCR_MODE_HWCS_LL EQADC_CFCR_MODE(10) #define EQADC_CFCR_MODE_HWCS_HL EQADC_CFCR_MODE(11) #define EQADC_CFCR_MODE_HWCS_FE EQADC_CFCR_MODE(12) @@ -112,10 +112,18 @@ /** @} */ /** - * @name EQADC Conversion commands + * @name EQADC conversion/configuration commands * @{ */ -#define EQADC_CONV_CHANNEL_MASK (0xFFU << 8)/**< @brief Channel number mask.*/ +#define EQADC_CONV_CONFIG_SEL1 (8U << 0) /**< @brief Alt.config.1. */ +#define EQADC_CONV_CONFIG_SEL2 (9U << 0) /**< @brief Alt.config.2. */ +#define EQADC_CONV_CONFIG_SEL3 (10U << 0) /**< @brief Alt.config.3. */ +#define EQADC_CONV_CONFIG_SEL4 (11U << 0) /**< @brief Alt.config.4. */ +#define EQADC_CONV_CONFIG_SEL5 (12U << 0) /**< @brief Alt.config.5. */ +#define EQADC_CONV_CONFIG_SEL6 (13U << 0) /**< @brief Alt.config.6. */ +#define EQADC_CONV_CONFIG_SEL7 (14U << 0) /**< @brief Alt.config.7. */ +#define EQADC_CONV_CONFIG_SEL8 (15U << 0) /**< @brief Alt.config.8. */ +#define EQADC_CONV_CHANNEL_MASK (255U << 8) /**< @brief Channel number mask.*/ #define EQADC_CONV_CHANNEL(n) ((n) << 8) /**< @brief Channel number. */ #define EQADC_CONV_FMT_RJU (0U << 16) /**< @brief Unsigned samples. */ #define EQADC_CONV_FMT_RJS (1U << 16) /**< @brief Signed samples. */ @@ -130,12 +138,60 @@ #define EQADC_CONV_MSG_NULL (6U << 20) /**< @brief Null message. */ #define EQADC_CONV_CAL (1U << 24) /**< @brief Calibrated result. */ #define EQADC_CONV_BN_MASK (1U << 25) /**< @brief Buffer number mask. */ -#define EQADC_CONV_BN(n) ((n) << 25) /**< @brief Buffer number. */ +#define EQADC_CONV_BN_ADC0 (0U << 25) /**< @brief ADC0 selection. */ +#define EQADC_CONV_BN_ADC1 (1U << 25) /**< @brief ADC1 selection. */ #define EQADC_CONV_REP (1U << 29) /**< @brief Repeat loop flag. */ #define EQADC_CONV_PAUSE (1U << 30) /**< @brief Pause flag. */ #define EQADC_CONV_EOQ (1U << 31) /**< @brief End of queue flag. */ /** @} */ +/** + * @name EQADC read/write commands + * @{ + */ +#define EQADC_RW_REG_ADDR_MASK (255U << 0) +#define EQADC_RW_REG_ADDR(n) ((n) << 0) +#define EQADC_RW_VALUE_MASK (0xFFFF << 8) +#define EQADC_RW_VALUE(n) ((n) << 8) +#define EQADC_RW_WRITE (0U << 24) +#define EQADC_RW_READ (1U << 24) +#define EQADC_RW_BN_ADC0 (0U << 25) +#define EQADC_RW_BN_ADC1 (1U << 25) +#define EQADC_RW_REP (1U << 29) +#define EQADC_RW_PAUSE (1U << 30) +#define EQADC_RW_EOQ (1U << 31) +/** @} */ + +/** + * @name ADC CR register definitions + * @{ + */ +#define ADC_CR_CLK_PS_MASK (31U << 0) +#define ADC_CR_CLK_PS(n) ((((n) >> 1) - 1) | ((n) & 1 ? ADC_CR_ODD_PS\ + : 0)) +#define ADC_CR_CLK_SEL (1U << 5) +#define ADC_CR_CLK_DTY (1U << 6) +#define ADC_CR_ODD_PS (1U << 7) +#define ADC_CR_TBSEL_MASK (3U << 8) +#define ADC_CR_TBSEL(n) ((n) << 8) +#define ADC_CR_EMUX (1U << 11) +#define ADC_CR_EN (1U << 15) +/** @} */ + +/** + * @name ADC AxCR registers definitions + * @{ + */ +#define ADC_ACR_PRE_GAIN_MASK (3U << 0) +#define ADC_ACR_PRE_GAIN_X1 (0U << 0) +#define ADC_ACR_PRE_GAIN_X2 (1U << 0) +#define ADC_ACR_PRE_GAIN_X4 (2U << 0) +#define ADC_ACR_RESSEL_MASK (3U << 6) +#define ADC_ACR_RESSEL_12BITS (0U << 6) +#define ADC_ACR_RESSEL_10BITS (1U << 6) +#define ADC_ACR_RESSEL_8BITS (2U << 6) +/** @} */ + /*===========================================================================*/ /* Driver pre-compile time settings. */ /*===========================================================================*/ @@ -145,12 +201,65 @@ * @{ */ /** - * @brief ADCD10 driver enable switch. - * @details If set to @p TRUE the support for EQADC1 queue 0 is included. + * @brief ADCD1 driver enable switch. + * @details If set to @p TRUE the support for ADC0 queue 0 is included. + * @note The default is @p FALSE. + */ +#if !defined(SPC5_ADC_USE_ADC0_Q0) || defined(__DOXYGEN__) +#define SPC5_ADC_USE_ADC0_Q0 FALSE +#endif + +/** + * @brief ADCD2 driver enable switch. + * @details If set to @p TRUE the support for ADC0 queue 1 is included. + * @note The default is @p FALSE. + */ +#if !defined(SPC5_ADC_USE_ADC0_Q1) || defined(__DOXYGEN__) +#define SPC5_ADC_USE_ADC0_Q1 FALSE +#endif + +/** + * @brief ADCD3 driver enable switch. + * @details If set to @p TRUE the support for ADC0 queue 2 is included. + * @note The default is @p FALSE. + */ +#if !defined(SPC5_ADC_USE_ADC0_Q2) || defined(__DOXYGEN__) +#define SPC5_ADC_USE_ADC0_Q2 FALSE +#endif + +/** + * @brief ADCD4 driver enable switch. + * @details If set to @p TRUE the support for ADC1 queue 3 is included. + * @note The default is @p FALSE. + */ +#if !defined(SPC5_ADC_USE_ADC1_Q3) || defined(__DOXYGEN__) +#define SPC5_ADC_USE_ADC1_Q3 FALSE +#endif + +/** + * @brief ADCD5 driver enable switch. + * @details If set to @p TRUE the support for ADC1 queue 4 is included. * @note The default is @p FALSE. */ -#if !defined(SPC5_ADC_USE_EQADC_Q0) || defined(__DOXYGEN__) -#define SPC5_ADC_USE_EQADC_Q0 FALSE +#if !defined(SPC5_ADC_USE_ADC1_Q4) || defined(__DOXYGEN__) +#define SPC5_ADC_USE_ADC1_Q4 FALSE +#endif + +/** + * @brief ADCD6 driver enable switch. + * @details If set to @p TRUE the support for ADC1 queue 5 is included. + * @note The default is @p FALSE. + */ +#if !defined(SPC5_ADC_USE_ADC1_Q5) || defined(__DOXYGEN__) +#define SPC5_ADC_USE_ADC1_Q5 FALSE +#endif + +/** + * + * @brief EQADC clock prescaler value. + */ +#if !defined(SPC5_ADC_CR_CLK_PS) || defined(__DOXYGEN__) +#define SPC5_ADC_CR_CLK_PS ADC_CR_CLK_PS(5) #endif /** @} */ @@ -158,12 +267,19 @@ /* Derived constants and error checks. */ /*===========================================================================*/ -#if SPC5_ADC_USE_EQADC_Q0 && !SPC5_HAS_EQADC +#if !SPC5_HAS_EQADC #error "EQADC1 not present in the selected device" #endif -#if !SPC5_ADC_USE_EQADC_Q0 -#error "ADC driver activated but no EQADC peripheral assigned" +#define SPC5_ADC_USE_ADC0 (SPC5_ADC_USE_ADC0_Q0 | \ + SPC5_ADC_USE_ADC0_Q1 | \ + SPC5_ADC_USE_ADC0_Q2) +#define SPC5_ADC_USE_ADC1 (SPC5_ADC_USE_ADC1_Q3 | \ + SPC5_ADC_USE_ADC1_Q4 | \ + SPC5_ADC_USE_ADC1_Q5) + +#if !SPC5_ADC_USE_ADC0 && !SPC5_ADC_USE_ADC1 +#error "ADC driver activated but no ADC peripheral assigned" #endif /*===========================================================================*/ @@ -334,10 +450,30 @@ struct ADCDriver { /* External declarations. */ /*===========================================================================*/ -#if SPC5_ADC_USE_EQADC_Q0 && !defined(__DOXYGEN__) +#if SPC5_ADC_USE_ADC0_Q0 && !defined(__DOXYGEN__) extern ADCDriver ADCD1; #endif +#if SPC5_ADC_USE_ADC0_Q1 && !defined(__DOXYGEN__) +extern ADCDriver ADCD2; +#endif + +#if SPC5_ADC_USE_ADC0_Q2 && !defined(__DOXYGEN__) +extern ADCDriver ADCD3; +#endif + +#if SPC5_ADC_USE_ADC1_Q3 && !defined(__DOXYGEN__) +extern ADCDriver ADCD4; +#endif + +#if SPC5_ADC_USE_ADC1_Q4 && !defined(__DOXYGEN__) +extern ADCDriver ADCD5; +#endif + +#if SPC5_ADC_USE_ADC1_Q5 && !defined(__DOXYGEN__) +extern ADCDriver ADCD6; +#endif + #ifdef __cplusplus extern "C" { #endif -- cgit v1.2.3