本次研究一下APM32F411的ADC外设功能。 该MCU有 2 个 ADC,精度为 12 位,每个 ADC 最多有 16 个外部通道和 3 个内部通道, 各通道 A/D 转换模式有单次、连续、扫描或间断, ADC 转换结果可以左对齐或右对齐存储在 16 位数据寄存器中。支持模拟看门狗,支持 DMA功能。 内置 1 个温度传感器(TSensor),内部连接 ADC_IN18 通道,传感器产生的电压随着温度线性变化,可通过 ADC 获取转换的电压值换算成温度。
内置 1 个 VBAT 监控器,内部连接 ADC_IN18 通道。 当同时设置温度传感器和 VBAT转换时,仅执行 VBAT转换。
内置参考电压 VREFINT,内部连接 ADC_IN17 通道,可通过 ADC 获取该 VREFINT; VREFINT 为ADC、比较器提供稳定的电压输出。
下面开始测试ADC转换功能。使用了PA0,PA1,PA2,PA3这4个ADC通道引脚。
(框内有个书写错误)。
间断模式和扫描模式差别:
扫描模式是一次触发,组内通道全部转换,然后产生标志位。
间断模式是一次触发转换一个通道,等组内所有通道转换完,再产生标志位。
ADC规则序列转换测试:
先使用ADC的规则序列转换功能。初始化ADC如下,间断模式和单次转换模式。使用中断方式读取数据:
void ADC_Init(void)
{
GPIO_Config_T gpioConfig;
ADC_Config_T adcConfig;
ADC_CommonConfig_T adcCommonConfig;
/* Enable GPIOA clock */
RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOA);
/* ADC channel 0 configuration */
GPIO_ConfigStructInit(&gpioConfig);
gpioConfig.mode = GPIO_MODE_AN;
gpioConfig.pupd = GPIO_PUPD_NOPULL;
gpioConfig.pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
GPIO_Config(GPIOA, &gpioConfig);
/* Enable ADC clock */
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC1);
/* ADC configuration */
ADC_Reset();
adcConfig.resolution = ADC_RESOLUTION_12BIT;
adcConfig.scanConvMode = DISABLE;
adcConfig.continuousConvMode = DISABLE;
adcConfig.extTrigEdge = ADC_EXT_TRIG_EDGE_NONE;
adcConfig.extTrigConv = ADC_EXT_TRIG_CONV_TMR1_CC1;
adcConfig.dataAlign = ADC_DATA_ALIGN_RIGHT;
adcConfig.nbrOfChannel = 4;
ADC_Config(ADC1, &adcConfig);
ADC_CommonConfigStructInit(&adcCommonConfig);
/*Set ADC Clock Prescaler. ADC_Clock = APB2_Clock/4, 84/4=21MHz*/
adcCommonConfig.prescaler = ADC_PRESCALER_DIV4;
ADC_CommonConfig(&adcCommonConfig);
/* ADC channel 0-3 Convert configuration */
ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_112CYCLES);
ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_1, 2, ADC_SAMPLETIME_112CYCLES);
ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_2, 3, ADC_SAMPLETIME_112CYCLES);
ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_3, 4, ADC_SAMPLETIME_112CYCLES);
/* Enable complete conversion interupt */
ADC_EnableInterrupt(ADC1, ADC_INT_EOC);
/* NVIC configuration */
NVIC_EnableIRQRequest(ADC_IRQn, 1, 1);
/* Enable ADC */
ADC_Enable(ADC1);
}
中断函数:
uint16_t adcData[4] = {0};
uint16_t adcIndex = 0;
void ADC_IRQHandler(void)
{
if (ADC_ReadStatusFlag(ADC1, ADC_FLAG_EOC))
{
ADC_ClearStatusFlag(ADC1, ADC_FLAG_EOC);
adcData[adcIndex++] = ADC_ReadConversionValue(ADC1);
if(adcIndex == 4)
{
// adcIndex = 0;
}else{
ADC_SoftwareStartConv(ADC1);
}
}
}
在使用规则通道采样时要注意:多路采样时如果不用DMA方式读取数据的话,不要使用连续扫描模式。因为规则采样在采集多路ADC时只有一个转换完成标志。而且数据寄存器也只有1个。
这里我使用的是触发一次采样一次。没有使用连续扫描模式。
测试代码:
#include "nr_micro_shell.h"
void adc_read(char argc, char *argv)
{
ADC_SoftwareStartConv(ADC1); //ADC_EnableSoftwareStartInjectedConv();
printf("ADC:\r\n");
while(adcIndex < 4);
adcIndex = 0;
printf("1=%X\r\n",adcData[0]);
printf("2=%X\r\n",adcData[1]);
printf("3=%X\r\n",adcData[2]);
printf("4=%X\r\n",adcData[3]);
}
NR_SHELL_CMD_EXPORT(adc, adc_read, "adc data");
通过串口查看ADC采样值:
ADC注入序列采样模式:
接下来试试注入序列采样模式。
初始化如下:
void ADC_Init(void)
{
GPIO_Config_T gpioConfig;
ADC_Config_T adcConfig;
ADC_CommonConfig_T adcCommonConfig;
/* Enable GPIOA clock */
RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOA);
/* ADC channel 0 configuration */
GPIO_ConfigStructInit(&gpioConfig);
gpioConfig.mode = GPIO_MODE_AN;
gpioConfig.pupd = GPIO_PUPD_NOPULL;
gpioConfig.pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
GPIO_Config(GPIOA, &gpioConfig);
/* Enable ADC clock */
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC1);
/* ADC configuration */
ADC_Reset();
adcConfig.resolution = ADC_RESOLUTION_12BIT;
adcConfig.scanConvMode = ENABLE; //DISABLE;
adcConfig.continuousConvMode = DISABLE;
adcConfig.extTrigEdge = ADC_EXT_TRIG_EDGE_NONE;
adcConfig.extTrigConv = ADC_EXT_TRIG_CONV_TMR1_CC1;
adcConfig.dataAlign = ADC_DATA_ALIGN_RIGHT;
adcConfig.nbrOfChannel = 4;
ADC_Config(ADC1, &adcConfig);
ADC_CommonConfigStructInit(&adcCommonConfig);
/*Set ADC Clock Prescaler. ADC_Clock = APB2_Clock/4, 84/4=21MHz*/
adcCommonConfig.prescaler = ADC_PRESCALER_DIV4;
ADC_CommonConfig(&adcCommonConfig);
// /* ADC channel 0-3 Convert configuration */
// ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_112CYCLES);
// ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_1, 2, ADC_SAMPLETIME_112CYCLES);
// ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_2, 3, ADC_SAMPLETIME_112CYCLES);
// ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_3, 4, ADC_SAMPLETIME_112CYCLES);
// /* Enable complete conversion interupt */
// ADC_EnableInterrupt(ADC1, ADC_INT_EOC);
ADC_ConfigInjectedSequencerLength(ADC1, 4);
ADC_ConfigInjectedChannel(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_112CYCLES);
ADC_ConfigInjectedChannel(ADC1, ADC_CHANNEL_1, 2, ADC_SAMPLETIME_112CYCLES);
ADC_ConfigInjectedChannel(ADC1, ADC_CHANNEL_2, 3, ADC_SAMPLETIME_112CYCLES);
ADC_ConfigInjectedChannel(ADC1, ADC_CHANNEL_3, 4, ADC_SAMPLETIME_112CYCLES);
ADC_EnableInterrupt(ADC1, ADC_INT_INJEOC);
/* NVIC configuration */
NVIC_EnableIRQRequest(ADC_IRQn, 1, 1);
/* Enable ADC */
ADC_Enable(ADC1);
}
中断读取数据:
uint16_t adcData[4] = {0};
uint16_t adcIndex = 0;
void ADC_IRQHandler(void)
{
// if (ADC_ReadStatusFlag(ADC1, ADC_FLAG_EOC))
// {
// ADC_ClearStatusFlag(ADC1, ADC_FLAG_EOC);
//
// adcData[adcIndex++] = ADC_ReadConversionValue(ADC1);
// if(adcIndex == 4)
// {
// }else{
// ADC_SoftwareStartConv(ADC1);
// }
// }
if (ADC_ReadStatusFlag(ADC1, ADC_FLAG_INJEOC))
{
ADC_ClearStatusFlag(ADC1, ADC_FLAG_INJEOC);
adcData[0] = ADC_ReadInjectedConversionValue(ADC1,1);
adcData[1] = ADC_ReadInjectedConversionValue(ADC1,2);
adcData[2] = ADC_ReadInjectedConversionValue(ADC1,3);
adcData[3] = ADC_ReadInjectedConversionValue(ADC1,4);
adcIndex = 4;
}
}
测试代码:
void adcin_read(char argc, char *argv)
{
ADC_EnableSoftwareStartInjectedConv(ADC1);
printf("ADC1 Inject:\r\n");
while(adcIndex < 4);
adcIndex = 0;
printf("1=%X\r\n",adcData[0]);
printf("2=%X\r\n",adcData[1]);
printf("3=%X\r\n",adcData[2]);
printf("4=%X\r\n",adcData[3]);
}
NR_SHELL_CMD_EXPORT(adc, adcin_read, "adc data");
最终串口打印数据:
|
|