ADC 代表模数转换,它用于将模拟值从现实世界转换为数字值,如 1 和 0。
	
01 ADC简介
01 ADC简介
	APM32F003的ADC是一个12位,ADC时钟最高可选择Fmaster时钟的一半。带有9个通道,可测量8个外部和1个内部信号源。各通道的A/D转换可选择单次、连续和扫描模式执行。而ADC的转换结果也可以以左对齐或右对齐的方式存储在16位数据寄存器中。
	02 ADC特点
	APM32F003的ADC相比于F0x,F10x,F4x系列有所简化,但也保留了相对核心的功能,能够满足普通场合下的应用需求。

	03 ADC应用
	## 单通道转换
	### 定义公共信息
	/** ADC Channel Definition*/
	#define ADC_CH0_CHANNEL                     ADC_CHANNEL_0
	/** ADC GPIO Definition*/
	#define ADC_CH0_GPIO_PIN                    GPIO_PIN_5
	#define ADC_CH_GPIO_PORTC                   GPIOC
	/** ADC Info*/
	#define VDD_VOLTAGE                         ((uint32_t)3300)
	#define ADC_RANGE                           ((uint32_t)4095)
	### 设置ADC通道和方式
	/** ADC Single Convert Init*/
	void ADC_SingleConvInit(void)
	{
	    ADC_Config_T adcConfig;
	    ADC_GPIOConfig();
	    ADC_SetMode(ADC_MODE_SINGLE_END);
	    /** ADC Configuration*/
	    ADC_ConfigStructInit(&adcConfig);
	    adcConfig.channel = ADC_CH0_CHANNEL;
	    adcConfig.convMode = ADC_CONV_MODE_CONTINUOUS;
	    adcConfig.interrupt = ADC_INT_CC;
	    ADC_Config(&adcConfig);
	    /** Calibration*/
	    ADC_Calibration();
	    ADC_Enable();
	    ADC_StartConversion();
	}
	### 轮询转换值
	/** ADC Poll for Single Conversion*/
	void ADC_PollSingleConv(void)
	{
	    uint16_t adcData;
	    uint16_t voltage;
	    if(ADC_ReadStatusFlag(ADC_FLAG_CC) == SET)
	    {
	        ADC_ClearStatusFlag(ADC_FLAG_CC);
	        adcData = ADC_ReadData();
	        voltage = (adcData * VDD_VOLTAGE) / ADC_RANGE;
	        printf("ADC Channel 0 Voltage: %d\r\n",adcData);
	    }
	}
	## 多通道扫描
	### 定义公共信息
	/** ADC Channel Definition*/
	#define ADC_CH0_CHANNEL                     ADC_CHANNEL_0
	#define ADC_CH1_CHANNEL                     ADC_CHANNEL_1
	#define ADC_CH2_CHANNEL                     ADC_CHANNEL_2
	/** ADC GPIO Definition*/
	#define ADC_CH0_GPIO_PIN                    GPIO_PIN_5
	#define ADC_CH1_GPIO_PIN                    GPIO_PIN_6
	#define ADC_CH2_GPIO_PIN                    GPIO_PIN_4
	#define ADC_CH_GPIO_PORTC                   GPIOC
	/** ADC Info*/
	#define VDD_VOLTAGE                         ((uint32_t)3300)
	#define ADC_RANGE                           ((uint32_t)4095)
	### 设置ADC通道和方式
	> 注意扫描的通道数量和所设置的通道一致,比如设置了通道CH2,则ADC会从ADC CH0扫描到ADC CH2,共三个通道。
	/** ADC GPIO Config*/
	void ADC_GPIOConfig(void)
	{
	    GPIO_Config_T gpioConfig;
	    gpioConfig.intEn = GPIO_EINT_DISABLE;
	    gpioConfig.mode = GPIO_MODE_IN_FLOATING;
	    gpioConfig.pin = ADC_CH0_GPIO_PIN | ADC_CH1_GPIO_PIN | ADC_CH2_GPIO_PIN;
	    GPIO_Config(ADC_CH_GPIO_PORTC, &gpioConfig);
	}
	/** ADC Continuous Scan Init*/
	void ADC_ContinuousScanInit(void)
	{
	    ADC_Config_T adcConfig;
	    ADC_GPIOConfig();
	    ADC_SetMode(ADC_MODE_SINGLE_END);
	    /** ADC Configuration*/
	    ADC_ConfigStructInit(&adcConfig);
	    adcConfig.div = ADC_DIV_4;
	    adcConfig.channel = ADC_CH2_CHANNEL;
	    adcConfig.convMode = ADC_CONV_MODE_CONTINUOUS;
	    adcConfig.scanMode = ADC_SCAN_MODE_ENABLE;
	    adcConfig.interrupt = ADC_INT_CC;
	    ADC_Config(&adcConfig);
	    /** Calibration*/
	    ADC_Calibration();
	    ADC_Enable();
	    ADC_StartConversion();
	}
	### 轮询转换值
	/** ADC Poll for Conversion*/
	void ADC_PollForConv(void)
	{
	    uint16_t adcArray[10] = {0};
	    uint16_t voltage;
	    uint8_t bufferIndex;
	    if(ADC_ReadStatusFlag(ADC_FLAG_CC) == SET)
	    {
	        ADC_ClearStatusFlag(ADC_FLAG_CC);
	        for(bufferIndex = 0; bufferIndex < 10; bufferIndex)
	        {
	            adcArray[bufferIndex] = ADC_ReadBufferData((ADC_BUFFER_IDX_T)(bufferIndex));
	        }
	        for(bufferIndex = 0; bufferIndex < 3; bufferIndex++)
	        {
	            voltage = (adcArray[bufferIndex] * VDD_VOLTAGE) / ADC_RANGE;
	            printf("ADC Channel %d Voltage: %d\r\n",bufferIndex, voltage);
	        }
	    }
	}
	## 软件滤波
	在硬件抗干扰能力不足的情况下,我们可以牺牲采用速率,采用软件方法进行滤波,从而得到更加稳定的采样值。一般应用的情况下,“抗脉冲干扰均值滤波”算法就能够满足需求。
	/** Anti-interference Average Filtering*/
	uint16_t Anti_AverageFilter(uint16_t *buffer, uint16_t length)
	{
	    uint16_t temp;
	    uint16_t i,j;
	    /**Bubbling Sort*/
	    for(j = 0; j < length - 1; j++)
	    {
	        for(i = 0; i < length - j - 1; i++)
	        {
	            if(buffer > buffer[i + 1])
	            {
	                temp = buffer;
	                buffer = buffer[i + 1];
	                buffer[i + 1] = temp;
	            }
	        }
	    }
	    temp = 0;
	    for(i = 1; i < length - 1; i++)
	    {
	        temp += buffer;
	    }
	    temp /= (length - 2);
	    return temp;
	}
	## 软件校准
	如果采样值较为固定,但离目标值相差大,还可以利用线性拟合(线性校准曲线)的方式让采样值更接近目标值。
	### 采样
	首先基于标准源表采样足够多的点(点数越多,拟合越准确)。

	### 拟合
	在数学工具(Matlab或Excel等)中做线性拟合(线性、多项式、指数皆可),得到校准公式和相关系数R值。

	### 校准
	用拟合步骤中得到的校准公式对采样值进行校准。
	


