目录
文章来源地址https://www.toymoban.com/news/detail-692724.html
1 前言
2 ADC介绍
2.1 多重工作模式
2.2 多重ADC框图
2.3 规则同时模式
3 程序设计
3.1 时序图
3.2 初始化流程图
3.3 初始化代码
4 结论
1 前言
关于ADC,相信大家都比较了解,关于STM32的学习教程都会有所讲解,但以查询方式、单通道讲解的较多,主要告诉大家基本的原理。关于ADC多重模式讲解的较少。本文主要通过讲解ADC转换器的双重工作模式,让大家更好的理解ADC的多重模式。
参考资料《STM32F4参考手册》。
2 ADC介绍
STM32单片机内部集成了12位ADC转换器,是逐次趋近型模数转换器。具有多达19个复用通道,可测量来自16个外部源、两个内部源和VBAT通道的信号。这些通道的AD转换可在单次、连续、扫描和不连续采样模式下进行。
2.1 多重工作模式
在具有两个或多个ADC的器件中,可以使用两重(具有2个ADC)或三重(具有3个)ADC模式。在多重 ADC 模式下,通过 ADC1 主器件到 ADC2 和 ADC3 从器件的交替触发或同时触发来启动转换,具体取决于 ADC_CCR 寄存器中的 MULTI[4:0] 位所选的模式。在多重 ADC 模式下,配置外部事件触发转换时,应用必须设置为仅主器件触发而禁止从器件触发,以防止出现意外触发而启动不需要的从转换。
可实现以下四种模式:
● 注入同时模式
● 规则同时模式
● 交替模式
● 交替触发模式
也可按以下方式组合使用上述模式:
● 注入同时模式 + 规则同时模式
● 规则同时模式 + 交替触发模式
2.2 多重ADC框图
图 1 多重ADC框图
从图中可以看出:
- 尽管 ADC2 和 ADC3 上存在外部触发,但它们并未显示在此图中。
- 在双重 ADC 模式下,不存在 ADC3 从器件部分。
- 在三重 ADC 模式下, ADC 通用数据寄存器 (ADC_CDR) 包含 ADC1、 ADC2 和 ADC3 的规则转换数据。按照所选的存储顺序使用全部 32 个寄存器位。在双重 ADC 模式下, ADC 通用数据寄存器 (ADC_CDR) 包含 ADC1 和 ADC2 的规则转换数据。使用全部32 个寄存器位。
2.3 规则同时模式
此模式可用于规则通道组。外部触发源来自 ADC1 的规则组多路复用器。在规则同时模式下,必须使用同一长度来转换序列,或必须确保触发之间的间隔长于 2 个序列(双重 ADC 模式)中的较长转换时间。否则,当序列较长的ADC 完成上一次转换时,序列较短的 ADC 可能重新开始转换。必须禁止注入转换。
双重 ADC 模式,在 ADC1 或 ADC2 转换事件结束时: 会生成一个 32 位 DMA 传输请求(如果 ADC_CCR 寄存器中的 DMA[1:0] 位等于0b10)。此请求会将存储在 ADC_CDR 32 位寄存器高位半字中的 ADC2 转换数据传输到 SRAM,然后将存储在 ADC_CCR 低位半字中的 ADC1 转换数据传输到 SRAM。
当 ADC1/ADC2 的规则通道全部完成转换后,会生成一个 EOC 中断(如果已在两个 ADC接口中的一个接口上使能)。
16通道规则同时模式如下图所示。
图 2 双重ADC模式
3 程序设计
3.1 时序图
图 3定时器+DMA+ADC工作时序图
如图 3所示,完成DMA、定时器、ADC初始化后,先使能DMA,再使能ADC,最后启动定时器Timer。当定时器发生更新事件时,触发ADC开始采样、转换,转换结束后触发DMA保存数据到缓冲区,完成一次变换。定时器的周期设定要确保完成ADC转换及DMA数据存储,否则将溢出错误。当DMA检测到转换次数达到其预设值时,触发DMA接收完成中断,关闭定时器。一个大的AD采集循环完成。
3.2 初始化流程图
初始化过程包括ADC初始化、DMA初始化、定时器初始化。ADC初始化和大部分MCU外设初始化流程一样,先使能时钟,包括GPIO时钟、ADC时钟、ADC配置,这里的ADC配置包含ADC1和ADC2两个外设的配置。DMA初始化包含DMA时钟(本文用DMA将ADC转换结果拷贝到RAM中)使能、DMA配置,在后面的实现代码中,将DMA初始化合并在ADC_MspInit函数中完成。因为要用定时器实现定期触发ADC转化,所以在初始化时要对定时器初始化,完成时钟使能、定时器配置。
图 4 初始化流程图
3.3 初始化代码
ADC初始化相关函数如表 1所示
表 1 ADC初始化相关函数
序号 |
函数名称 |
函数功能说明 |
1 |
HAL_ADC_MspInit () |
时钟使能、IO、DMA初始化 |
2 |
MX_ADC1_Init() |
配置ADC1 |
3 |
MX_ADC2_Init() |
配置ADC2 |
4 |
ADC_Trigger_Timer_Start() |
启动定时器 |
5 |
ADC_Trigger_Timer_Stop() |
停止定时器 |
6 |
DMA2_Stream0_IRQHandler() |
DMA中断处理函数 |
7 |
HAL_ADC_ConvCpltCallback() |
ADC转换完成回调函数 |
8 |
MX_Timer2_Init() |
配置Timer2配置为TIM_TRGO_UPDATE输出触发模式 |
1)HAL_ADC_MspInit ()代码
在ADC1底层初始化时,使能了ADC、DMA、GPIO时钟,引脚配置为模拟输入模式。对DMA2初始化,使能DMA中断。具体如下:
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(adcHandle->Instance==ADC1)
{
__HAL_RCC_ADC1_CLK_ENABLE(); //使能ADC0时钟
__HAL_RCC_DMA2_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
hdma_adc1.Instance = DMA2_Stream0;
hdma_adc1.Init.Channel = DMA_CHANNEL_0;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_adc1.Init.MemDataAlignment = DMA_PDATAALIGN_WORD;
hdma_adc1.Init.Mode = DMA_NORMAL;
hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_Init(&hdma_adc1);
__HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);
HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 1, 1); //DMA中断配置
HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}
else if(adcHandle->Instance==ADC2)
{
__HAL_RCC_ADC2_CLK_ENABLE();
}
}
2)MX_ADC1_Init()代码
void MX_ADC1_Init(void)
{
ADC_MultiModeTypeDef multimode;
ADC_ChannelConfTypeDef sConfig;
/**配置 ADC通用参数*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B; //设置分辨率
hadc1.Init.ScanConvMode = ENABLE; //扫描方式
hadc1.Init.ContinuousConvMode = DISABLE; //关闭连续扫描
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING; //触发模式
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_TRGO; //触发源
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; //对齐方式
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
HAL_ADC_Init(&hadc1);
/**配置 ADC工作模式 */
multimode.Mode = ADC_DUALMODE_REGSIMULT; //规则同时模式
multimode.DMAAccessMode =ADC_DMAACCESSMODE_2; //ADC同步DMA模式2
multimode.TwoSamplingDelay = ADC_TWOSAMPLINGDELAY_5CYCLES; //
HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode);
/**配置 ADC采集通道*/
sConfig.Channel = ADC_CHANNEL_VREFINT;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_56CYCLES;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
}
3)MX_ADC2_Init()代码
void MX_ADC2_Init(void)
{
ADC_ChannelConfTypeDef sConfig;
/**配置 ADC通用参数*/
hadc2.Instance = ADC2;
hadc2.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc2.Init.Resolution = ADC_RESOLUTION_12B;
hadc2.Init.ScanConvMode = ENABLE;
hadc2.Init.ContinuousConvMode = DISABLE;
hadc2.Init.DiscontinuousConvMode = DISABLE;
hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc2.Init.NbrOfConversion = 1;
hadc2.Init.DMAContinuousRequests = DISABLE;
hadc2.Init.EOCSelection = ADC_EOC_SEQ_CONV;
HAL_ADC_Init(&hadc2);
/**配置 ADC采集通道*/
sConfig.Channel = ADC_CHANNEL_13;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_56CYCLES;
HAL_ADC_ConfigChannel(&hadc2, &sConfig);
HAL_ADC_Start(&hadc2);
}
4)ADC_Trigger_Timer_Start()代码
static void ADC_Trigger_Timer_Start(void)
{
__HAL_TIM_ENABLE(&htim2);
}
5)ADC_Trigger_Timer_Stop()代码
static void ADC_Trigger_Timer_Stop(void)
{
__HAL_TIM_DISABLE(&htim2);
}
6)HAL_ADC_ConvCpltCallback()代码
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
ADC_Trigger_Timer_Stop();
HAL_ADCEx_MultiModeStop_DMA(&hadc1);
ADC_ConvCpltCallback();
}
- MX_Timer2_Init()代码
void MX_Timer2_Init(void)
{
TIM_MasterConfigTypeDef sMasterConfig;
uint32_t uwPrescalerValue = 0;
uwPrescalerValue = (uint32_t) (SystemCoreClock /2/1000000) - 1; //timer2挂在APB2上,倍频后最大时钟为系统时钟/2,
htim2.Instance = TIM2;
htim2.Init.Prescaler = uwPrescalerValue; //定时器时钟频率为1M
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 200-1; //定时器溢出时间,1uS*100 = 200uS
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htim2);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);
}
4 结论
本文介绍了定时器+DMA+ADC双重工作模式下ADC驱动设计思路,介绍了ADC初始化、配置的主要函数。文中代码截取自实际工程中部分代码,旨在为读者提供设计思路,可根据自己的程序做简单修改即可。文章来源:https://www.toymoban.com/news/detail-692724.html
到了这里,关于【STM32】- 定时器+DMA+ADC 双重模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!