aboutsummaryrefslogtreecommitdiffstats
path: root/os
diff options
context:
space:
mode:
Diffstat (limited to 'os')
-rw-r--r--os/hal/platforms/SPC5xx/EQADC_v1/adc_lld.c157
-rw-r--r--os/hal/platforms/SPC5xx/EQADC_v1/adc_lld.h160
2 files changed, 301 insertions, 16 deletions
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