Files
zTC1/mico-os/platform/MCU/LPC5410x/peripherals/platform_adc.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;
}