流程
CPU配置PA0为模拟输入
↓
CPU配置ADC3为12位、单通道、连续转换
↓
CPU配置DMA2 Stream0
↓
DMA源地址 = ADC3->DR
DMA目标地址 = uhADCxConvertedValue
↓
CPU配置并开启DMA中断
↓
CPU开启DMA,DMA开始等待
↓
CPU开启ADC3的DMA请求功能
↓
CPU软件启动ADC3
↓
ADC3采样PA0
↓
ADC3完成12位转换
↓
结果写入ADC3->DR
↓
ADC3向DMA2 Stream0发送请求
↓
DMA读取ADC3->DR
↓
DMA写入uhADCxConvertedValue
↓
DMA剩余数量从1变成0
↓
DMA产生TC传输完成中断
↓
NVIC通知CPU
↓
CPU进入DMA2_Stream0_IRQHandler()
↓
打印ADC值和电压
↓
清除DMA中断标志
↓
ADC连续转换、DMA循环搬运,重新开始
开启时钟配置
PeripheralEnable(PeripheralGPIOA, true); //GPIOA的时钟
PeripheralEnable(PeripheralADC3, true); //ADC3的时钟
IO脚配置
GPIO_InitTypeDef GPIO_InitStructure = {0};
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模拟输入
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//无上下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC公共配置
所谓“公共配置”,是ADC1、ADC2、ADC3可能共用的配置。
ADC_CommonInitTypeDef ADC_CommonInitStructure = {0};
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;//ADC时钟四分频
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;//
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;//多ADC采样间隔
ADC_CommonInit(&ADC_CommonInitStructure);
-
ADC_CommonInitStructure.ADC_Mode =ADC_Mode_Independent;
表示ADC3独立工作。
一些芯片支持多ADC协同工作,例如:
- ADC1和ADC2同时采样
- ADC1和ADC2交替采样
- 三个ADC同时采样
-
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
禁止多ADC公共DMA模式
这一项主要用于双ADC或三ADC模式。
例如多个ADC联合工作时,多个ADC结果可能被打包到公共数据寄存器,再由DMA一起搬运。
ADC_Mode_Independent 所以不需要多ADC公共DMA模式,设置为Disabled
ADC自身配置
ADC_InitTypeDef ADC_InitStructure = {0};
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;//设置12位分辨率0~4095
//关闭扫描模式,扫描模式用于连续转换多个通道。例如需要测量三个通道:Channel 0 → Channel 3 → Channel 7
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
//开启连续转换模式,连续转换意味着ADC只需要启动一次,之后会自动不断转换:
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
//禁止外部触发边沿ADC可以由很多事件启动,例如:- 软件命令 - 定时器更新事件 - 定时器比较事件- 外部引脚
//设置成None,表示不使用外部触发边沿,后面由软件启动 ADC_SoftwareStartConv(ADC3);
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
//ADC结果右对齐,ADC数据寄存器通常是16位或32位,而有效结果只有12位。
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
//规则序列包含一次转换
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADCx, &ADC_InitStructure);
// 配置具体ADC通道
// 哪个ADC:ADC3
// 哪个通道:Channel 0
// 序列中的位置:第1位
// 采样时间:3个ADC周期
ADC_RegularChannelConfig(ADC3, ADC_Channel_0, 1, ADC_SampleTime_3Cycles);
//允许ADC产生DMA请求
ADC_DMARequestAfterLastTransferCmd(ADCx, ENABLE);
//开启ADC的DMA接口
ADC_DMACmd(ADC3, ENABLE);
//开启ADC3
ADC_Cmd(ADC3, ENABLE);
开始ADC转换
必须配置好对应的DMA之后才能开启这个ADC
ADC_SoftwareStartConv(ADC3);
DMA相关配置
DMA_InitTypeDef DMA_InitStructure = {0};
PeripheralEnable(PeripheralDMA2, true); //时钟
DMA_InitStructure.DMA_Channel = DMA_Channel_2;//DMA请求源配置
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC3->DR;//配置DMA源地址
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&uhADCxConvertedValue;//配置DMA目标地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;//设置DMA传输方向,外设 → 内存
DMA_InitStructure.DMA_BufferSize = 1;//设置每轮传输数量,表示DMA每轮只搬一个数据。
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//禁止外设地址递增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;//禁止内存地址递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//设置外设数据宽度
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//设置内存数据宽度
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//设置DMA循环模式,通模式下,传输数量变成0以后DMA会停止,循环模式下,传输完成后自动重新装载数量
DMA_InitStructure.DMA_Priority = DMA_Priority_High;//设置DMA优先级
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;//关闭DMA FIFO
DMA_Init(DMA2_Stream0, &DMA_InitStructure);
DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);//开启DMA传输完成中断
DMA_Cmd(DMA2_Stream0, ENABLE);//启动DMA Stream
NVIC中断配置
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;//选择DMA2 Stream0中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//子优先级,两个中断具有相同抢占优先级,并且同时等待CPU处理,子优先级决定先处理谁。
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//允许NVIC接收该中断
NVIC_Init(&NVIC_InitStructure);
}
中断处理函数
void DMA2_Stream0_IRQHandler(void)
{
printf(
"ADC data = %d, voltage = %f mV\n",
uhADCxConvertedValue, //读取DMA搬运后的数据
(float)uhADCxConvertedValue * 3300 / 4096 //把ADC值换算成电压
);
DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF);//清除DMA传输完成标志
NVIC_ClearPendingIRQ(DMA2_Stream0_IRQn);//清除NVIC挂起状态
}