细说STM32F407单片机1个ADC使用DMA同时采集3个输入通道的方法

目录

一、示例说明

二、工程配置 

1、RCC、DEBUG、CodeGenerator

2、USART6

3、TIM3 

(1)Mode

(2)参数设置

(3) TRGO

4、ADC3

(1)Mode

(2) ADC_Settings

(3)ADC_Regular_ConversionMode

(4)DMA设置

5、NVIC 

三、软件设计

1、main.c

四、运行与调试


        当ADC只有一个输入通道,在转换结束后可以及时读出结果数据寄存器的内容。

        当规则转换组有多个通道时,应该使用扫描转换模式(Scan Conversion Mode),ADC在转换完一个通道后立刻转换下一个通道,直到规则组内的通道序列转换完。规则转换只有一个转换结果数据寄存器,虽然可以设置在每个通道转换完之后就产生EOC事件中断,但是在多通道情况下,在EOC事件中断里读取转换结果数据可能是来不及的,更谈不上对数据进行显示或处理

        因此,如果规则转换组有多个输入通道,应该使用DMA,使转换结果数据通过DMA传输自动保存到缓冲区中,在一个规则组转换结束后再对数据进行处理,或者在采集多次数据后再处理。

一、示例说明

        本文将继续使用旺宝红龙开发板STM32F407ZGT6 KIT V1.0。

        本实例,为ADC3的规则组设置3个输入通道,使用扫描转换模式,通过DMA方式传输ADC转换结果数据。

        图片是开发板原理图中关于板上可调电位器RV1与MCU管脚PF8的线路图。当ADC3_IN6时,复选为PF8。再设置ADC3_IN5和ADC3_IN4,依次复选到MCU管脚PF7和PF6,然后通过导线在开发板上的P7双排针接插件上,把PF8、PF7和PF6相互连接起来。这样一个电位器信号,就同时提供给PF8、PF7和PF6了,实现了1个ADC同时采集3个通道的信号输入了。

        之所以这样处理,还有一个原因,那就是STM32F407ZGT6的ADC3没有任何内部信号通道。

 

二、工程配置 

1、RCC、DEBUG、CodeGenerator

        RCC:外部晶振25MHz,HCLK=168MHz,PCLK1=42MHz,PCLK2=84MHz;

        DEBUG:Serial Wire;

        CodeGenerator:勾选☑Generate peripheral initialization as a pair of '.c/.h' files per peripheral

2、USART6

        配置PG9、PG14为USART6,全部参数默认;

3、TIM3 

(1)Mode

        选择internal clock。

(2)参数设置

  • 预分频因子:49999。
  • 计数器方向:Up.
  • 计数器周期:499.
  • 内部时钟分频:No.
  • 自动重装载:启用。

(3) TRGO

  • MSM bit:Disable。
  • Trigger Event:Update Envent。

        使用TIM3的TRGO信号作为ADC3的外部触发信号,TIM3的定时周期为500ms,ADC3以DMA模式启动转换,在ADC的转换完成中断里读取转换结果数据。 

4、ADC3

(1)Mode

         配置ADC3,并勾选IN4、IN5、IN6。分别对应管脚PF6、PF7、PF8。并且管脚PF8连接开发板上的可调电位器,+3.3V。

(2) ADC_Settings

  • Clock Prescaler:PCLK2 divided by 4
  • Resolution:12 bits(15 ADC Clock cycles)
  • Data Alignment:Right alignment
  • Scan Conversion Mode:Enabled
  • Continuous Conversion Mode:Disabled. 启用连续转换模式后,ADC结束一个转换后立即启动一个新的转换。开不开启这种模式,对于结果是有区别的。至于什么样的区别,将在运行与调试里介绍。
  • Discontinuous Conversion Mode:Disabled. 这种模式一般用于外部触发时,将一组输入通道分为多个短的序列,分批次转换。
  • DMA Continuous Requests:Enabled
  • End Of Conversion Selection:EOC flag at the end of single channel conversion

(3)ADC_Regular_ConversionMode

  •  Number Of Conversion:3
  • External Trigger Conversion Source:Timer 3 Trigger Out event
  • External Trigger Conversion Edge:Trigger detecton on the rising edge
  • Rank1:通道6,采样周期15个周期;
  • Rank2:通道5,采样周期15个周期;
  • Rank2:通道4,采样周期15个周期;

 

        开启扫描转换模式(Scan Conversion Mode)和DMA连续请求(DMA Continuous Requests) 

       设置转换个数为3,下面会自动生成3个Rank的设置,分别设置每个Rank的输入通道和采样时间,每个通道的采样时间可以不一样。3个Rank里模拟通道出现的顺序就是规则组转换的顺序。

        ADC_Settings组里的参数End of Conversion Selection的设定值不变,仍然是在每个通道转换完之后产生EOC信号

(4)DMA设置

        ADC3只有一个DMA请求,为这个DMA请求配置DMA流DMA2 Stream0,设置DMA传输属性参数,DMA传输方向自动设置为Peripheral To Memory(外设到存储器)在DMA Request Settings组中将Mode(工作模式)设置为Circular(循环模式),将外设和存储器的数据宽度都设置为Word,因为ADC转换结果数据寄存器是32位的。存储器设置为地址自增加。 

        Mode:如果选择Normal时,运行的时候,只采集1次,就停下来,不再工作;如果选择Circular,那么就循环不停地采集。选择什么模式,对于结果是有区别的,至于什么样的区别,将在运行与调试介绍。

        ADC的数据都是32位的,数据宽度一律选择word。 

5、NVIC 

        在使用ADC3的DMA方式传输时发现:即使不开启ADC3的全局中断,DMA传输功能也能正常工作,所以在设置NVIC时可以关闭ADC3的全局中断。同理,TIM3的全局中断设不设置都可以,不影响结果。

三、软件设计

1、main.c

/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */
/* USER CODE BEGIN PV */
#define	BATCH_DATA_LEN 3				// DMA data buffer length, must be an integer multiple channels
uint32_t dmaDataBuffer[BATCH_DATA_LEN];	// DMA data buffer
/* USER CODE END PV */

        其中,dmaDataBuffer是DMA传输数据的缓冲区,是元素类型为uint32_t的数组,该数组的长度为BATCH_DATA_LEN。程序中定义BATCH_DATA_LEN的值为3,所以,发生DMA传输完成事件中断时,数组dmaDataBuffer中就存储了3个通道的一次转换结果,在DMA传输完成事件中断的回调函数里,就可以读出3个通道的转换结果。因为设置DMA工作模式为循环模式,所以会在定时器TIM3驱动下一直进行ADC转换和数据传输。

        DMA缓冲区长度BATCH_DATA_LEN也可以设置为3的整数倍,例如,设置为30。那么发生DMA传输完成中断时,数组dmaDataBuffer就存储了3个通道10个采样点的数据。在实际的ADC数据采集中,一般是采集一定的数据点之后再处理、显示或存储,还可以使用双缓冲区进一步提高效率。

        菜单设计,启动DMA模式的ADC3、启动定时器TIM3。 

/* USER CODE BEGIN 2 */
  //菜单设计
  uint8_t hello1[] = "Demo14_3_ADC_dual_IN:TEST ADC多个通道切换输入.\r\n";
  HAL_UART_Transmit(&huart6,hello1,sizeof(hello1),500);	//阻塞模式
  HAL_Delay(10);
  uint8_t hello2[] = "ADC多通道和DMA传输。\r\n";
  HAL_UART_Transmit_IT(&huart6,hello2,sizeof(hello2));	//非阻塞模式
  HAL_Delay(10);

  printf("当规则转换组有多个通道时,应该使用扫描转换模式(Scan Conversion Mode),\r\n");
  printf("ADC在转换完一个通道后立刻转换下一个通道,直到规则组内的通道序列转换完。\r\n");
  printf("如果规则转换组有多个输入通道,应该使用DMA,使转换结果数据通过DMA传输自动保存到缓冲区中,\r\n");
  printf("在一个规则组转换结束后再对数据进行处理,或者在采集多次数据后再处理。\r\n");
  printf("开发板上的可调电位器连接MCU管脚PF8.\r\n");
  printf("ADC3_IN6对应MCU管脚PF8,是外部模拟量输入通道,接可调电阻产生的可变电压。\r\n");
  printf("ADC3_IN5对应MCU管脚PF7,用导线把PF7连接PF8。\r\n");
  printf("ADC3_IN4对应MCU管脚PF6,用导线把PF6连接PF8。\r\n\r\n");

  HAL_ADC_Start_DMA(&hadc3, dmaDataBuffer, BATCH_DATA_LEN); // Start ADC1, DMA mode
  HAL_TIM_Base_Start(&htim3);								// Start TIM3
  /* USER CODE END 2 */

        ADC3采集完成回调函数。 

/* USER CODE BEGIN 4 */
/* Callback function for DMA stream transfer completion event interrupt */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
	uint32_t adcValue=0, Volt;
	for(uint8_t i=0; i<BATCH_DATA_LEN;i++)
	{
		// The buffer contains the conversion results of 3 channels.
		adcValue=dmaDataBuffer[i];
		Volt=3300*adcValue;
		Volt=Volt>>12;
		printf("ADC[%d] = %ld\r\n",i,Volt );
	}
}

//串口打印
int __io_putchar(int ch)
{
	HAL_UART_Transmit(&huart6,(uint8_t *)&ch,1,0xFFFF);
	return ch;
}
/* USER CODE END 4 */

        通过跟踪分析函数HAL_ADC_Start_DMA()和DMA流中断处理函数HAL_DMA_IRQHandler()的源代码,可以发现DMA流中断事件与ADC回调函数之间的关系,DMA流的传输完成中断事件(DMA_IT_TC)关联着ADC的回调函数HAL_ADC_ConvCpltCallback()。

四、运行与调试

        首次下载运行或复位后,串口助手显示程序最初的菜单项:

        连续运行后,串口助手上不断刷新显示3个通道的采集值。调节开发板上的电位器,可以看到3个通道的电压值明显变化:

        如果,DMA的模式修改为Normal,那么程序只采集一次ADC数据(见第一张图)。

        如果, 当Continuous Conversion Mode设置为Enable时,ADC将在完成一次转换后立即开始下一次转换,无需额外的启动命令。这种模式适用于需要连续监测输入信号变化的应用。在这种模式下,数据更新速度会明显加快,因为转换过程是连续的 。现象就是,串口助手上显示频率更高的数据,字节好像快到跳起来了。

        在禁用连续转换模式时,ADC执行单次转换操作。即完成设定的转换任务(单通道一次转换或多通道一轮转换)后,就会停止转换,若需要再次转换,需要重新启动ADC。 

         实际上在串口助手上除了看到数据更新的快之外,看不到其他的差别的。

### STM32F407 ADC通道 DMA 采集标准库实现 对于STM32F407微控制器,利用其内置的模数转换器(ADC)进行多通道数据采集时,DMA(直接存储器访问)技术可以显著提高效率并减少CPU负载。下面介绍如何通过ST官方提供的标准外设库来配置和使用这一功能。 #### 配置ADC参数 为了启动一个多通道扫描模式下的连续转换过程,在初始化阶段需要设置如下几个重要参数: - **ADC_ContinuousConvMode**: 启用连续转换模式以便能够持续获取输入信号样本。 - **ADC_ScanConvMode**: 开启扫描模式允许依次读取多个通道的数据。 - **ADC_DataAlign**: 设置数据对齐方式为右对齐(Right Alignment),这是最常用的方式[^1]。 ```c // 初始化结构体定义 ADC_InitTypeDef ADC_InitStructure; DMA_InitTypeDef DMA_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); // 使能ADC1时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE); // 使能DMA2时钟 GPIO_InitTypeDef GPIO_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; // PA0 和 PA1作为模拟输入引脚 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); ``` #### 配置DMA传输 接下来要做的就是准备DMA的相关设定,确保它能够在每次完成一次完整的序列采样之后自动将结果转移到指定缓冲区中去。 - **DMA_PeripheralBaseAddr**: 设定为目标寄存器地址即`ADC_DR_Address`. - **DMA_MemoryBaseAddr**: 定义接收数据的目标内存起始位置. - **DMA_DIR**: 数据流向应是从外设到内存(DMA_DIR_PeripheralSRC). - **DMA_BufferSize**: 缓冲区内可容纳的最大项数目等于所选通道数量乘以预期循环次数. ```c // 配置DMADMA_DeInit(DMA2_Stream0); DMA_InitStructure.DMA_Channel = DMA_Channel_0; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)aADC_ConvertedValue; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStructure.DMA_BufferSize = NUMBER_OF_CHANNELS * BUFFER_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA 生命周期管理部分省略... DMA_Init(DMA2_Stream0, &DMA_InitStructure); DMA_Cmd(DMA2_Stream0, ENABLE); ``` #### 启动ADC转换 最后一步是激活ADC模块本身以及关联好的DMA请求机制,从而正式开启整个测量流程。 ```c ADC_DMACmd(ADC1, ENABLE); ADC_Cmd(ADC1, ENABLE); ADC_StartConversion(ADC1); ``` 上述代码片段展示了基于STM32标准外设库的一个典型应用实例,用于说明怎样构建一个支持多路并发采样的高效解决方案.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wenchm

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值