本帖最后由 怀揣少年梦 于 2024-8-3 09:43 编辑
一、先来看一下MM32F5330的ADC功能介绍与特性
1、ADC介绍
ADC 是 12 位的逐次逼近型(SAR)模拟数字转换器,可以将模拟信号转换成数字信号。ADC 有可测量内部或外部信号源,其中 ADC1 有 19 路外部输入通道,ADC2 有 17 路外部输入通道和 2 路内部通道。这些 ADC 的通道可以单次、单周期和连续进行转换。根据不同的方式又可以选择普通通道转换、任意通道转换。ADC 的最大输入时钟为 48MHz,它是由 PLL2 和 APB2 时钟(PCLK2)分频产生。
2、特性
- 高达 3Msps 转换速率;
- 支持普通通道转换;
- 单次转换模式:在指定通道完成一次转换;
- 单周期扫描模式:对所有指定通道(从低序号通道到高序号通道,或从高序号通道到低序号通道)完成一个周期转换;
- 连续扫描模式:连续执行单周期扫描模式直到软件停止 A/D 转换。若需要修改转换通道只能停止A/D 转换,等待完成寄存器配置再重新开启转换;
- 支持任意通道转换;
- 单次转换模式:在指定通道完成一次转换; 单周期扫描模式:在所有指定通道按照通道设置完成一个周期转换;连续扫描模式:连续执行单周期扫描模式直到软件停止 A/D 转换。若需要转换期间修改通道,用户不必停止转换,可配置相应通道寄存器,配置的新通道将在下一个扫描周期进行转换;
- 注入通道转换自动注入:在任意通道转换方式下,完成任意通道转换后自动开始进行注入通道转换
- 可编程通道采样时间 最高 12 位可编程分辨率 SAR 支持 DMA 传输 A/D 转换开始条件
- 软件启动;
- 触发启动,可配置触发延时。触发源包括:Timer 和 EXTI 模拟看门狗功能。转换结果与指定的阈值区间进行比较,当转换值超出设定的阈值区间时,如果ADC_ADCR.AWDIE 置位,则产生中断;
- 支持自校准功能,输入时钟 1.5MHz 支持单端、差分、伪差分转换;这个还是很强大的。
- 支持过采样
二、ADC+DMA编程
1、编写ADC代码
void ADC_Configure(void)
{
ADC_InitTypeDef ADC_InitStruct;
DMA_InitTypeDef DMA_InitStruct;
GPIO_InitTypeDef GPIO_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);// 使能GPIOA时钟
ADC_CalibrationConfig(ADC1, 0x1FE);//校准
ADC_StructInit(&ADC_InitStruct);
ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;//12bit模式
ADC_InitStruct.ADC_Prescaler = ADC_Prescaler_16; //时钟分频
ADC_InitStruct.ADC_Mode = ADC_Mode_Continue;//连续采样
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
ADC_Init(ADC1, &ADC_InitStruct);
ADC_DMACmd(ADC1, ENABLE);//使能DMA
ADC_SampleTimeConfig(ADC1, ADC_Channel_1, ADC_SampleTime_79_5);//配置
ADC_SampleTimeConfig(ADC1, ADC_Channel_4, ADC_SampleTime_79_5);
ADC_SampleTimeConfig(ADC1, ADC_Channel_5, ADC_SampleTime_79_5);
//使能ADC通道 1 4 5
ADC_ChannelCmd(ADC1, ADC_Channel_1, ENABLE);
ADC_ChannelCmd(ADC1, ADC_Channel_4, ENABLE);
ADC_ChannelCmd(ADC1, ADC_Channel_5, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
/* PA1(RV1) PA4(RV2) PA5(RV3) */
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5; // 使能GPIOA时钟,配置为模拟输入模式
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStruct);
//使能ADC
ADC_Cmd(ADC1, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(DMA1_Channel1);
//初始化DMA参数
DMA_StructInit(&DMA_InitStruct);
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->ADDATA);
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)ADC_Buffer;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStruct.DMA_BufferSize = 30;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
DMA_InitStruct.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
DMA_InitStruct.DMA_Auto_Reload = DMA_Auto_Reload_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStruct);
//配置DMA中断
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
//使能DMA通道1
DMA_Cmd(DMA1_Channel1, ENABLE);
//配置DMA中断
NVIC_InitStruct.NVIC_IRQChannel = DMA1_CH1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
void ADC_NormalChannel_ContinuousScan_DMA_Interrupt_Sample(void)
{
uint8_t i = 0, j = 0;
uint32_t RVxSum = 0;
float RVxAverage[3], RVxVoltage[3];
printf("\r\nTest %s", __FUNCTION__);
ADC_Configure();
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while (1)
{
if (0 != ADC_DMA_InterruptFlag)
{
ADC_DMA_InterruptFlag = 0;
for (i = 0; i < 3; i++)
{
RVxSum = 0;
for (j = 0; j < 10; j++)
{
RVxSum += ADC_Buffer[i + j * 3];
}
RVxAverage[i] = (float)RVxSum / (float)10.0;
}
printf("\r\n");
RVxVoltage[0] = RVxAverage[0] * (float)3.3 / (float)4096.0;
log_d("RV%d Voltage = %0.2f\t", 1, RVxVoltage[0]);
RVxVoltage[1] = RVxAverage[1] * (float)3.3 / (float)4096.0;
log_i("RV%d Voltage = %0.2f\t", 2, RVxVoltage[1]);
RVxVoltage[2] = RVxAverage[2] * (float)3.3 / (float)4096.0;
log_w("RV%d Voltage = %0.2f\t", 3, RVxVoltage[2]);
PLATFORM_DelayMS(500);
}
}
}
2、验证
1)ADC三个通道的电压,如图所示。
2)旋转RV1的电位器,如图所示。
3)与万用表对比
使用UT39E+万用表对比测试。
万用表的精度如下图:
实测数据对比
| 万用表
(V)
| ADC
(V)
| 差值(MV)
| RV1
| 3.198
| 3.21
| 2
| RV2
| 1.646
| 1.65
| 4
| RV3
| 1.742
| 1.74
| 2
|
总结:
1、检测10分钟,ADC数值没有变化,还是比较稳定。
2、与万用表对比,基本相差2-4mv;在要求不是特别高的情况,精度还是不错的。
|