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值。
### 校准
用拟合步骤中得到的校准公式对采样值进行校准。