mirror of
https://github.com/oopuuu/zTC1.git
synced 2025-12-17 23:48:13 +08:00
318 lines
11 KiB
C
318 lines
11 KiB
C
/**
|
|
******************************************************************************
|
|
* @file MicoDriverAdc.c
|
|
* @author William Xu
|
|
* @version V1.0.0
|
|
* @date 05-May-2014
|
|
* @brief This file provide ADC driver functions.
|
|
******************************************************************************
|
|
* UNPUBLISHED PROPRIETARY SOURCE CODE
|
|
* Copyright (c) 2016 MXCHIP Inc.
|
|
*
|
|
* The contents of this file may not be disclosed to third parties, copied or
|
|
* duplicated in any form, in whole or in part, without the prior written
|
|
* permission of MXCHIP Corporation.
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
#include "mico_platform.h"
|
|
#include "mico_rtos.h"
|
|
|
|
#include "platform.h"
|
|
#include "platform_peripheral.h"
|
|
#include "chip.h" // Magicoe "stm32f2xx.h"
|
|
#include "platform_logging.h"
|
|
#include "Packing.h"
|
|
|
|
/******************************************************
|
|
* Constants
|
|
******************************************************/
|
|
|
|
/******************************************************
|
|
* Enumerations
|
|
******************************************************/
|
|
|
|
/******************************************************
|
|
* Type Definitions
|
|
******************************************************/
|
|
|
|
/** @brief ADC SEQ Options */
|
|
#define TRIG_SOFT (0UL << 12) /*!< Software Trigger */
|
|
#define TRIG_PININT0 (0UL << 12) /*!< Hardware Trigger on PININT0 */
|
|
#define TRIG_PININT1 (1UL << 12) /*!< Hardware Trigger on PININT1 */
|
|
#define TRIG_ARMTXEV (5UL << 12) /*!< Hardware Trigger on ARM_TXEV */
|
|
#define TRIG_POL_NEG 0 /*!< Trigger polarity is negative */
|
|
#define TRIG_POL_POS (1UL << 18) /*!< Trigger polarity is positive */
|
|
#define BYPASS_SYNC (1UL << 19) /*!< Bypass Synchronization Filp-Flop */
|
|
#define MODE_BURST (1UL << 27) /*!< Enable Burst mode */
|
|
#define MODE_SINGLE (1UL << 28) /*!< Enable Single mode */
|
|
#define SEQA_PIRO_HIGH (1UL << 29) /*!< Set SEQA as HIGH Priority */
|
|
#define MODE_EOC (0UL << 30) /*!< Event after end of Conversion */
|
|
#define MODE_EOS (1UL << 30) /*!< Event after end of sequence */
|
|
#define ENABLE_CH(ch) (1UL << (ch)) /*!< Enable the channel number */
|
|
|
|
/* Gets the ADC trigger from configuration */
|
|
#define ADC_TRIGGER(cfg) (((cfg) & 0x3F000) >> 12)
|
|
|
|
/** @brief ADC CTRL Options */
|
|
#define MODE_SYNC (0UL << 8) /*!< Set ADC to synchoronous mode */
|
|
#define MODE_ASYNC (1UL << 8) /*!< Set ADC to asynchoronous mode */
|
|
#define RESOL_6BIT (0UL << 9) /*!< Set ADC Resolution to 6 bits */
|
|
#define RESOL_8BIT (1UL << 9) /*!< Set ADC Resolution to 8 bits */
|
|
#define RESOL_10BIT (2UL << 9) /*!< Set ADC Resolution to 10 bits */
|
|
#define RESOL_12BIT (3UL << 9) /*!< Set ADC Resolution to 12 bits */
|
|
#define BYPASS_CALIB (1UL << 11) /*!< Bypass calibration data */
|
|
#define SAMPLE_TIME(x) (((x) & 7) << 12) /*!< Set the Sample Time to @a x */
|
|
#define ENABLE_OVR (1UL << 24) /*!< Enable Overflow interrupt */
|
|
|
|
/* Internal defines */
|
|
#define SEQ_A_MASK 0x7C0C5FFF
|
|
#define SEQ_B_MASK 0x5C0C5FFF
|
|
#define CTRL_MASK 0x00007F00
|
|
|
|
/******************************************************
|
|
* Structures
|
|
******************************************************/
|
|
|
|
/******************************************************
|
|
* Variables Definitions
|
|
******************************************************/
|
|
uint32_t valSeq[2]; /* Stored SEQ A/B Values */
|
|
|
|
/******************************************************
|
|
* Function Declarations
|
|
******************************************************/
|
|
static ErrorCode_t ADC_Handler_SeqPoll_test( const platform_adc_t* adc, ADC_SEQ_IDX_T seqIndex, uint16_t* output);
|
|
static ErrorCode_t ADC_Handler_Seq_test( const platform_adc_t* adc, ADC_SEQ_IDX_T seqIndex, uint16_t* output);
|
|
static ErrorCode_t ADC_ReadData_test(const platform_adc_t* adc, ADC_SEQ_IDX_T seqIndex, uint16_t* output);
|
|
static ErrorCode_t ADC_GetData_test(uint32_t data, uint16_t* output);
|
|
static ErrorCode_t ADC_Calibrate_test(LPC_ADC_T *pREGS, uint32_t sysclk_freq);
|
|
|
|
/******************************************************
|
|
* Function Definitions
|
|
******************************************************/
|
|
|
|
|
|
OSStatus platform_adc_init( const platform_adc_t* adc, uint32_t sample_cycle )
|
|
{
|
|
OSStatus err = kNoErr;
|
|
|
|
platform_mcu_powersave_disable();
|
|
|
|
require_action_quiet( adc != NULL, exit, err = kParamErr);
|
|
|
|
/* Initialize the associated GPIO */
|
|
Chip_IOCON_PinMuxSet(LPC_IOCON, adc->pin->port, adc->pin->pin_number, IOCON_MODE_INACT | IOCON_FUNC1 | IOCON_ANALOG_EN);
|
|
|
|
/* Initialize the ADC CLK */
|
|
Chip_SYSCON_PowerUp(SYSCON_PDRUNCFG_PD_ADC0 | SYSCON_PDRUNCFG_PD_VDDA_ENA | SYSCON_PDRUNCFG_PD_VREFP);
|
|
Chip_Clock_EnablePeriphClock(SYSCON_CLOCK_ADC0);
|
|
Chip_Clock_SetADCClockSource(SYSCON_ADCCLKSELSRC_MAINCLK);
|
|
Chip_Clock_SetADCClockDiv(0x1);
|
|
|
|
/* To be safe stop the ADC in case it is not stopped */
|
|
adc->port->SEQ_CTRL[0] = 0x0;
|
|
adc->port->SEQ_CTRL[1] = 0x0;
|
|
adc->port->INTEN = 1 << adc->seqIndex;
|
|
|
|
/* Configure the ADC */
|
|
valSeq[adc->seqIndex] = ADC_SEQ_CTRL_SEQ_ENA | ADC_SEQ_CTRL_START | ((TRIG_SOFT | TRIG_POL_POS | MODE_EOC | ENABLE_CH(adc->channel)) & SEQ_A_MASK );
|
|
adc->port->CTRL = ( MODE_SYNC | RESOL_12BIT | SAMPLE_TIME(sample_cycle) ) & CTRL_MASK;
|
|
Chip_ADC_SetClockRate( adc->port , adc->adc_peripheral_clock );
|
|
|
|
/* Calibrate the ADC */
|
|
if (ADC_Calibrate_test( adc->port , Chip_Clock_GetSystemClockRate()) != LPC_OK) {
|
|
printf("ERROR: Calibrating ADC0_%d\r\n",adc->channel);
|
|
err = kInProgressErr;
|
|
goto exit;
|
|
}
|
|
//printf("ADC0_%d Initialized and Calibrated successfully!\r\n",adc->channel);
|
|
|
|
exit:
|
|
platform_mcu_powersave_enable();
|
|
return err;
|
|
}
|
|
|
|
OSStatus platform_adc_take_sample( const platform_adc_t* adc, uint16_t* output )
|
|
{
|
|
OSStatus err = kNoErr;
|
|
|
|
platform_mcu_powersave_disable();
|
|
|
|
require_action_quiet( adc != NULL, exit, err = kParamErr);
|
|
|
|
/* Start analog to digital conversion on selected sequence */
|
|
adc->port->SEQ_CTRL[adc->seqIndex] = valSeq[adc->seqIndex] & ~(ADC_SEQ_CTRL_SEQ_ENA | ADC_SEQ_CTRL_START);
|
|
adc->port->SEQ_CTRL[adc->seqIndex] = valSeq[adc->seqIndex];
|
|
|
|
/* Wait required number of samples */
|
|
if (ADC_Handler_SeqPoll_test( adc, adc->seqIndex , output ) != LPC_OK) {
|
|
printf("ERROR: required number of samples \r\n");
|
|
err = kInProgressErr;
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
platform_mcu_powersave_enable();
|
|
return err;
|
|
}
|
|
|
|
OSStatus platform_adc_take_sample_stream( const platform_adc_t* adc, void* buffer, uint16_t buffer_length )
|
|
{
|
|
UNUSED_PARAMETER(adc);
|
|
UNUSED_PARAMETER(buffer);
|
|
UNUSED_PARAMETER(buffer_length);
|
|
platform_log("unimplemented");
|
|
return kNotPreparedErr;
|
|
}
|
|
|
|
OSStatus platform_adc_deinit( const platform_adc_t* adc )
|
|
{
|
|
UNUSED_PARAMETER(adc);
|
|
OSStatus err = kNoErr;
|
|
|
|
platform_mcu_powersave_disable();
|
|
|
|
require_action_quiet( adc != NULL, exit, err = kParamErr);
|
|
|
|
Chip_ADC_DeInit( adc->port );
|
|
|
|
exit:
|
|
platform_mcu_powersave_enable();
|
|
return err;
|
|
}
|
|
/* EXPORTED API: Calibrate the ADC */
|
|
static ErrorCode_t ADC_Calibrate_test(LPC_ADC_T *pREGS, uint32_t sysclk_freq)
|
|
{
|
|
volatile uint32_t i;
|
|
|
|
pREGS->STARTUP = ADC_STARTUP_ENABLE;
|
|
for ( i = 0; i < 0x10; i++ ) {}
|
|
if ( !(pREGS->STARTUP & ADC_STARTUP_ENABLE) ) {
|
|
return ERR_ADC_NO_POWER;
|
|
}
|
|
|
|
/* If not in by-pass mode do the calibration */
|
|
if ( (pREGS->CALIBR & ADC_CALREQD) && !(pREGS->CTRL & ADC_CR_BYPASS) ) {
|
|
uint32_t ctrl = pREGS->CTRL & (CTRL_MASK | 0xFF);
|
|
uint32_t tmp = ctrl;
|
|
|
|
/* Set ADC to SYNC mode */
|
|
tmp &= ~ADC_CR_ASYNC_MODE;
|
|
|
|
/* To be safe run calibration at 1MHz UM permits upto 30MHz */
|
|
if (sysclk_freq > 1000000UL) {
|
|
pREGS->CTRL = tmp | (((sysclk_freq / 1000000UL) - 1) & 0xFF);
|
|
}
|
|
|
|
/* Calibration is needed, do it now. */
|
|
pREGS->CALIBR = ADC_CALIB;
|
|
i = 0xF0000;
|
|
while ( (pREGS->CALIBR & ADC_CALIB) && --i ) {}
|
|
pREGS->CTRL = ctrl;
|
|
return i ? LPC_OK : ERR_TIME_OUT;
|
|
}
|
|
|
|
/* A dummy conversion cycle will be performed. */
|
|
pREGS->STARTUP = (pREGS->STARTUP | ADC_STARTUP_INIT) & 0x03;
|
|
i = 0x7FFFF;
|
|
while ( (pREGS->STARTUP & ADC_STARTUP_INIT) && --i ) {}
|
|
return i ? LPC_OK : ERR_TIME_OUT;
|
|
}
|
|
/* PRIVATE: ADC sequence handler polling mode */
|
|
static ErrorCode_t ADC_Handler_SeqPoll_test( const platform_adc_t* adc, ADC_SEQ_IDX_T seqIndex, uint16_t* output)
|
|
{
|
|
ErrorCode_t ret = LPC_OK;
|
|
/* Poll as long as the sequence is enabled */
|
|
while (adc->port->SEQ_CTRL[seqIndex] & ADC_SEQ_CTRL_SEQ_ENA) {
|
|
|
|
if (!(adc->port->FLAGS & ADC_FLAGS_SEQN_INT_MASK(seqIndex))) {
|
|
continue;
|
|
}
|
|
|
|
ret = ADC_Handler_Seq_test(adc, seqIndex, output);
|
|
if (ret != LPC_OK) {
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* PRIVATE: ADC Sequence event handler function */
|
|
static ErrorCode_t ADC_Handler_Seq_test( const platform_adc_t* adc, ADC_SEQ_IDX_T seqIndex, uint16_t* output)
|
|
{
|
|
uint32_t flag = adc->port->FLAGS;
|
|
uint32_t tmp;
|
|
*output = 0xffff;
|
|
|
|
/* Check if overrun is enabled and got an overrun */
|
|
tmp = flag & ADC_FLAGS_SEQN_OVRRUN_MASK(seqIndex);
|
|
|
|
if (!(flag & ADC_FLAGS_SEQN_INT_MASK(seqIndex))) {
|
|
return ERR_ADC_INVALID_SEQUENCE;
|
|
}
|
|
|
|
if (ADC_ReadData_test(adc, seqIndex, output) != LPC_OK) {
|
|
return ERR_FAILED;
|
|
}
|
|
|
|
/* Handle the overflow */
|
|
if (tmp) {
|
|
printf("ADC Handle the overflow!\r\n");
|
|
}
|
|
|
|
/* Clear the interrupt if it is for EOS and not EOC */
|
|
if (valSeq[seqIndex] & ADC_SEQ_CTRL_MODE_EOS) {
|
|
adc->port->FLAGS = ADC_FLAGS_SEQN_INT_MASK(seqIndex);
|
|
}
|
|
|
|
if ( *output != 0xffff ){
|
|
|
|
/* Disable interrupts */
|
|
adc->port->INTEN &= ~(1 << seqIndex);
|
|
|
|
/* Stop and disable the sequence */
|
|
adc->port->SEQ_CTRL[seqIndex] = valSeq[seqIndex] &
|
|
~(ADC_SEQ_CTRL_SEQ_ENA | ADC_SEQ_CTRL_BURST | ADC_SEQ_CTRL_START);
|
|
return LPC_OK;
|
|
}
|
|
|
|
/* If we are not in burst mode we must trigger next sample */
|
|
if (!((valSeq[seqIndex] >> 12) & 0x1F) && !(valSeq[seqIndex] & ADC_SEQ_CTRL_BURST)) {
|
|
adc->port->SEQ_CTRL[seqIndex] = valSeq[seqIndex];
|
|
}
|
|
|
|
return LPC_OK;
|
|
}
|
|
/* PRIVATE: Reads data from the GDAT or DAT register based on mode of operation */
|
|
static ErrorCode_t ADC_ReadData_test(const platform_adc_t* adc, ADC_SEQ_IDX_T seqIndex, uint16_t* output)
|
|
{
|
|
int i;
|
|
/* Check if this is End-of-Seq or End-of-SingleConversion */
|
|
if (!(valSeq[seqIndex] & ADC_SEQ_CTRL_MODE_EOS)) {
|
|
return ADC_GetData_test(adc->port->SEQ_GDAT[seqIndex], output);
|
|
}
|
|
/* Read channels having conversion data */
|
|
for (i = 0; i < sizeof(adc->port->DAT) / sizeof(adc->port->DAT[0]); i++) {
|
|
if (valSeq[seqIndex] & ADC_SEQ_CTRL_CHANSEL(i)) {
|
|
if (ADC_GetData_test(adc->port->DAT[i], output) != LPC_OK) {
|
|
return ERR_FAILED;
|
|
}
|
|
}
|
|
}
|
|
return LPC_OK;
|
|
}
|
|
/* PRIVATE: Extract, format data and store into user buffer */
|
|
static ErrorCode_t ADC_GetData_test(uint32_t data, uint16_t* output)
|
|
{
|
|
/* If data is not vaild something is wrong! */
|
|
if (!(data & ADC_SEQ_GDAT_DATAVALID)) {
|
|
return ERR_FAILED;
|
|
}
|
|
|
|
/* Read ADC conversion result */
|
|
*output = (uint16_t) ADC_DR_RESULT(data);
|
|
return LPC_OK;
|
|
}
|