基于Cubemx的STM32F429多重adc采样DMA传输配置,定时器触发采样率可调,最高可到7.2MHz采样率~

        前言:大家好,本人大学在读生,由于本科电赛学习期间,受到不少github与CSDN的帮助,又发现有些文章重理论轻实践,对于刚接触单片机一窍不通的小白来说许多地方很难理解,最终无法配置成功实现相应功能,挫伤学习积极性(比如说我)。因此我本着回馈的初衷,想介绍STM32F4系列单片机如何具体配置一些重要外设的功能(主要是STM32F407与STM32F429)。

(本人电赛期间使用的STM32F429核心板与自制底板)

        本篇是关于基于cubemx的多重adc采样DMA传输配置方式,工程是STM32F429IGT6,理论部分可能会讲的少一些,主要还是手把手教大家配置的过程。第一次写文,如有问题还请多多包涵~

一、原理讲解

        如果要我简单介绍adc的话,它就像一个长长的阶梯,阶梯顶端是标定电压,而每一格所对应的电压值,则由标定电压与adc的位数共同确定。比如3.3V的标定电压,位数是10位,那么台阶总数就是2的10次方,1024,而每一格就是3.3/1024 V,约为3mV,这也是adc的最小识别精度。

        当外界电压加到adc对应IO口上时,首先会将该电压与标定电压进行比较,得到其对应的阶梯序号,并被单片机所读取。比如外加电压是1.65V,标定电压为3.3V,位数10位,那么对应阶梯序号就是1.65V/3.3V *1024 = 512,512就是单片机读到的值。如果要将它转换成实际电压并输出,则需要在软件上进行数值转换,即512/1024 *3.3V = 1.65V。

        众所周知,STM32F429系列单个adc最高可以实现2.4MHz采样率(亲测完全可行,理论上可以更高,但不推荐去配),根据奈奎斯特采样定律可以采1.2MHz的信号。但实际用过的人就明白,想要得到相对漂亮的信号(比如正弦波),一般采样率要比信号频率高7-8倍左右,否则会存在较大失真。

        然而,多重adc采样可以实现三个adc交替采一个通道,即adc1采完后adc2采,adc2采完adc3采,adc3采完又回到adc1,以此循环。如果只有单个adc,在一次采样后,必须等待转换完成才能进行下一次采样!而多重adc则可以在adc的转换时间里,调用其他adc去采样,这样大大提高了采样率,并且理论上可以实现7.2MHz的采样率!以下是示意图。

(有关三重adc模式的介绍)

         话不多说,教学开始!

二、Cubmx配置

        1.工程建立

        在搜索框中找到STM32F429IGT6型号,选中后点击“start project”

        2.基本环境配置

        首先RCC配置为外部时钟晶振,SYS中debug选择“SW”即线网模式,这些都是基操了。

        然后在时钟配置这里修改频率,这里需要注意的地方比较多,友好起见我一一列出:

        1.首先input frequency栏里,这个表示的是外部晶振频率,STM32F429是25MHz,而STM32F407是8MHz,以前在这里吃过很多亏,希望大家一定要根据自己的型号确定晶振频率!!!

        2.选择HSE,并且在"System Clock Mux"处选择PLLCLK(锁相环模式),否则sysclk(系统时钟)频率直接就等于HSE的频率了。

        3.将APB1时钟频率改成72MHz,因为这里我用的是TIM3定时器触发adc采样,而TIM3是挂在APB1总线上的,因此这里的频率对我们后面修改采样率而言至关重要!(当然对定时器触发adc非常了解的朋友可以不配成72MHz,只要最后能得到想要的采样率即可)。

        3.TIM3定时器触发多重adc的配置方式

        重头戏来啦~

        (1)定时器的配置

        首先选中TIM3,将时钟源设为"Internal Clock",然后预分频设为1,周期设为14,根据计算公式即可得到定时器触发adc的频率为:72MHz/[(1+1)*(14+1)] = 2.4MHz,而定时器触发一次会带动三个adc交替采样一次,从而实现3*2.4MHz = 7.2MHz的采样率!

        此外,我们需要使能自动重装载,并且将触发事件改为更新事件(图中红框都有标出)。

        定时器配置完成!

        (2)三重adc的配置

        此处是最为核心,也是最容易遗漏东西的点,敲黑板!!

        首先,开启所有的adc(总共就三个),并为它们选择相同的通道,我这里选择的就是通道0

       adc1的配置:

        将Mode调成"Triple interleaved mode only", 即三重adc交替模式,然后开启DMA连续传输请求,并将触发转换源改成"TIM3 Trigger Out event",其他设为默认或是依照我的参数配即可。当然了,在开启DMA请求前,你需要在"DMA Settings"中使能它。

        adc1的DMA使能方式如下:

        选中"DMA Settings",单击下方"Add",选择"ADC1"。

        还没完!首先要将DMA的数据宽度,即"Data Width"修改为Word格式,并确定是"Memory"。至于Mode,我个人习惯用Normal模式,所以没试过配成Circular,感兴趣的朋友可以试试。

        如果选择配成"Normal"模式,则需要在每次DMA传输完一组数据后重新开启DMA,重启代码我写在了最后的main.c里。

        这些完成后,就可以回到参数设置里,将“DMA连续请求”那里使能啦~

          adc2和adc3的配置:

        如果你的adc1已经配好,那么这两个adc只需要开启DMA请求即可(注意,不需要额外配置DMA了,用adc1的就行)!但是我的Cubemx可能是版本原因存在bug,无法直接在Cubemx中开启!

(不骗你,确实无法开启)

        那么,我们就只能在Keil中自行配置开启了。

4.Keil软件编程

        在adc.c文件中,找到adc2的初始化,将DMA连续请求设置为"ENABLE",对adc3也进行同样的配置。

       最后,附上我的main.c给大家,内含dma重开启的方法以及如何读取dma搬运的数据,存到你设定的数组里:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "dma.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#define data_len 1024 //数组长度
uint32_t dmabuffer[data_len];
float adc_123[data_len*2];
int conv_done=0;//DMA传输完成标志
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

int fputc(int ch, FILE *f)//printf重定义
{
	HAL_UART_Transmit(&huart1, (uint8_t *)(&ch), 1, 0xFFF);
	return 1;
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
	conv_done=1;
	int i=0,j=0;
	for(i=0,j=0;j<data_len && i<data_len*2;)
	{
		adc_123[i]=(dmabuffer[j] & 0x0000FFFF)*3.3/4096 ;//取低16位
		i++;
		adc_123[i]=((dmabuffer[j] & 0xFFFF0000)>>16)*3.3/4096 ;//取高16位
		i++;
		j++;
	}//取出DMA传输的数据		
}

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void resetdma_adc(void) //dma重开启函数
{
		conv_done=0;
		HAL_TIM_Base_Stop(&htim3);
		HAL_ADC_Stop(&hadc1); 
		HAL_ADC_Stop(&hadc2);
		HAL_ADC_Stop(&hadc3); 	
//		HAL_ADC_Stop_DMA(&hadc1);
//		HAL_ADC_Start_DMA(&hadc1,(uint32_t *)dmabuffer,data_len);
		HAL_ADC_Start(&hadc3);
		HAL_ADC_Start(&hadc2);
		HAL_ADCEx_MultiModeStart_DMA(&hadc1,(uint32_t *)dmabuffer,data_len);
		HAL_TIM_Base_Start(&htim3);
		while(!conv_done);//开启——等待完成
	//DMA传输时间=858* 1/72us 约等于12us
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_TIM3_Init();
  MX_USART1_UART_Init();
  MX_ADC2_Init();
  MX_ADC3_Init();
  /* USER CODE BEGIN 2 */
	HAL_ADC_Start(&hadc3);
	HAL_ADC_Start(&hadc2);
	HAL_ADCEx_MultiModeStart_DMA(&hadc1,(uint32_t *)dmabuffer,data_len);
 //多重adc开启,并让dma把数据搬运到dmabuffer数组中
	HAL_TIM_Base_Start(&htim3);
	
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		while(!conv_done) ; //等待dma传输完成
		for(int i=0;i<data_len*2;i++)
		{
			printf("%.3f\r\n",adc_123[i]); //打印采样得到的值
		}
		conv_done=0;
		resetdma_adc();
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

        实测可以实现7.2MHz的采样率,当时是用800kHz的正弦波,一个周期能采到9个点~

写在最后

        最后补充一些细节部分吧:

        首先,并不是任意的采样率都可以通过这个多重adc实现!因为我们假定定时器触发频率是f1,而由数据手册可知,adc1采样后经过固定时间 t(假定这个t对应频率是f2,f2=1/t)后触发adc2采样,再经过时间 t 触发adc3采样,直到下一次定时器触发,才重新开始这个过程。那么你很容易发现,其实一旦f1远小于f2,就容易变成短时间采集三个点的情况(那其实跟一次只采一个点也没多大区别)!只有当f1和f2相等时,才能够实现每个采样点的间隔是相同的,才是一个好的采样方式!

        其次,之所以DMA传输数据设置为"Word",是因为DMA一次可以传输32位的数据,而adc转换完成后是16位的数据。所以dma传输一个32位的数据,其实是两个adc的转换结果拼接而成,至于谁是高16位谁是低16位,与转换次序有关,想了解更多的可以看看下面这张图。可以综合这个知识看看我上面是如何编写代码读取dma传输数据的~

        最后的最后:当时自己做这个工程的时候也是花费了很多精力,有不少体会,所以码字可能有点多。第一次写文,无关乎热度与关注,只希望能对来到CSDN的大家有些许帮助,笔者就十分满足了。

  • 14
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
### 回答1: STM32F4的ADC采样使用单通道、DMA定时器触发FFT是一种常见的应用场景。 首先,单通道表示只使用一个ADC通道进行采样STM32F4系列微控制器通常具有多个ADC通道,可以选择适合的通道进行采样。通过配置ADC的控制寄存器和通道选择寄存器,可以设置ADC的工作模式和采样通道。 接下来,DMA(Direct Memory Access)是一种数据传输方式,可以在不经过CPU的情况下将数据从ADC缓冲区传输到存储器中。使用DMA可以提高系统性能,减轻CPU的负担。在配置DMA时,需要设置DMA的起始地址和目标地址,使得ADC采样数据可以直接传输到存储器中。 然后,定时器触发是指使用定时器的计时功能来触发ADC采样。通过配置定时器的计数器、预分频器和计时器模式,可以设置ADC采样采样间隔。 最后,FFT(快速傅里叶变换)是一种信号处理算法,可以将时域信号转换为频域信号。在采样数据传输到存储器后,可以使用FFT算法对采样数据进行处理,提取出频域信息。 综上所述,STM32F4的ADC采样单通道、DMA定时器触发FFT的应用流程如下:首先,选择合适的ADC通道并配置ADC的控制寄存器和通道选择寄存器。接着,配置DMA的起始地址和目标地址,使得ADC采样数据可以直接传输到存储器。然后,配置定时器的计数器、预分频器和计时器模式,设置ADC采样采样间隔。最后,将采样数据传输到存储器后,使用FFT算法对采样数据进行处理,提取出频域信息。这种应用场景可以实现对信号的快速采样和频谱分析,广泛应用于音频信号处理、通信系统等领域。 ### 回答2: stm32f4的ADC采样是指通过ADC模块对外部模拟信号进行转换,并将转换结果存储在内部寄存器中。以下是以单通道、DMA定时器触发FFT为例的ADC采样过程的详细描述: 首先,需要配置ADC模块的参数。可以选择单通道采样,即只使用一个模拟信号通道进行采样。可以选择采样采样精度,并设置对应的转换模式。 然后,需要配置DMA通道,以实现ADC数据的直接存储。DMA通道负责从ADC的数据寄存器中读取转换结果,并将其存储到指定的存储器区域中。通过使用DMA,可以在ADC转换过程中同时进行其他任务,提高采样。 接下来,需要配置一个定时器触发ADC的转换。定时器可以生成一个周期性的触发信号,用于精确控制采样的时间间隔。通过将定时器ADC触发源相连接,可以在每个定时器触发事件上开始一次ADC转换。 最后,可以将采样到的数据应用于FFT算法。FFT即快速傅里叶变换,可以将时域信号转换为频域信号。通过对ADC采样得到的数据进行FFT分析,可以获取信号的频谱信息,用于进一步的数据处理和分析。 综上所述,通过配置ADC模块的参数、设置DMA传输定时器触发,可以实现stm32f4的ADC单通道采样DMA传输定时器触发FFT分析。通过这种方式,可以有效地进行模拟信号的采样和频域分析,用于各种应用场景中。 ### 回答3: STM32F4系列的ADC采样单通道、DMA定时器触发FFT是一种常见的硬件实现方法,适用于实时信号分析和处理的应用场景。 首先,STM32F4系列的微控制器内部集成了一种精确的ADC模块,可实现模拟信号的数字化转换。采样单通道即表示一次只对一个模拟输入通道进行采样,这有利于简化系统设计和提高采样精度。 其次,通过使用DMA(直接内存访问)控制器,可以实现高效的数据传输DMA可以在ADC转换完成后,自动将采样数据从ADC模块中读出,并传输到指定的存储区域(如数组或缓冲区)。这样可以减少CPU的负担,提高系统的实时性和效。 同时,使用定时器触发ADC转换,可以实现定时采样。通过配置定时器的参数,如采样采样时间等,可以实现对模拟信号的周期性采样。这对于实时信号分析和处理非常要,可以确保采样数据的一致性和准确性。 最后,采样后的数据可以通过FFT(快速傅里叶变换)算法进行频谱分析。FFT算法可以将时域上的采样数据转换为频域上的频谱信息,从而可以分析信号的频成分和谱线。这对于信号处理和频谱显示非常有用,可以帮助工程师更好地了解信号的特性和用途。 总之,STM32F4的ADC采样单通道、DMA定时器触发FFT是一种有效的硬件实现方法,适用于实时信号分析和处理的应用场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值