赞
踩
目录
关于ADC,相信大家都比较了解,关于STM32的学习教程都会有所讲解,但以查询方式、单通道讲解的较多,主要告诉大家基本的原理。关于ADC多重模式讲解的较少。本文主要通过讲解ADC转换器的双重工作模式,让大家更好的理解ADC的多重模式。
参考资料《STM32F4参考手册》。
STM32单片机内部集成了12位ADC转换器,是逐次趋近型模数转换器。具有多达19个复用通道,可测量来自16个外部源、两个内部源和VBAT通道的信号。这些通道的AD转换可在单次、连续、扫描和不连续采样模式下进行。
在具有两个或多个ADC的器件中,可以使用两重(具有2个ADC)或三重(具有3个)ADC模式。在多重 ADC 模式下,通过 ADC1 主器件到 ADC2 和 ADC3 从器件的交替触发或同时触发来启动转换,具体取决于 ADC_CCR 寄存器中的 MULTI[4:0] 位所选的模式。在多重 ADC 模式下,配置外部事件触发转换时,应用必须设置为仅主器件触发而禁止从器件触发,以防止出现意外触发而启动不需要的从转换。
可实现以下四种模式:
● 注入同时模式
● 规则同时模式
● 交替模式
● 交替触发模式
也可按以下方式组合使用上述模式:
● 注入同时模式 + 规则同时模式
● 规则同时模式 + 交替触发模式
图 1 多重ADC框图
从图中可以看出:
此模式可用于规则通道组。外部触发源来自 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定时器+DMA+ADC工作时序图
如图 3所示,完成DMA、定时器、ADC初始化后,先使能DMA,再使能ADC,最后启动定时器Timer。当定时器发生更新事件时,触发ADC开始采样、转换,转换结束后触发DMA保存数据到缓冲区,完成一次变换。定时器的周期设定要确保完成ADC转换及DMA数据存储,否则将溢出错误。当DMA检测到转换次数达到其预设值时,触发DMA接收完成中断,关闭定时器。一个大的AD采集循环完成。
初始化过程包括ADC初始化、DMA初始化、定时器初始化。ADC初始化和大部分MCU外设初始化流程一样,先使能时钟,包括GPIO时钟、ADC时钟、ADC配置,这里的ADC配置包含ADC1和ADC2两个外设的配置。DMA初始化包含DMA时钟(本文用DMA将ADC转换结果拷贝到RAM中)使能、DMA配置,在后面的实现代码中,将DMA初始化合并在ADC_MspInit函数中完成。因为要用定时器实现定期触发ADC转化,所以在初始化时要对定时器初始化,完成时钟使能、定时器配置。
图 4 初始化流程图
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();
- }
- 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);
- }
本文介绍了定时器+DMA+ADC双重工作模式下ADC驱动设计思路,介绍了ADC初始化、配置的主要函数。文中代码截取自实际工程中部分代码,旨在为读者提供设计思路,可根据自己的程序做简单修改即可。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。