STM32CubeMX教程14 ADC - 多通道DMA转换

目录

1、准备材料

2、实验目标

3、实验流程

3.0、前提知识

3.1、CubeMX相关配置

3.1.0、工程基本配置

3.1.1、时钟树配置

3.1.2、外设参数配置

3.1.3、外设中断配置

3.2、生成代码

3.2.0、配置Project Manager页面

3.2.1、外设初始化调用流程

3.2.2、外设中断调用流程

3.2.3、添加其他必要代码

4、常用函数

5、烧录验证

6、注释详解


读者可访问 GitHub - lc-guo/STM32CubeMX-Series-Tutorial 获取原始工程代码


1、准备材料

开发板(正点原子stm32f407探索者开发板V2.4

STM32CubeMX软件(Version 6.10.0

野火DAP仿真器

keil µVision5 IDE(MDK-Arm

CH340G Windows系统驱动程序(CH341SER.EXE

XCOM V2.6串口助手

三个滑动变阻器

2、实验目标

使用STM32CubeMX软件配置STM32F407开发板的ADC实现ADC多通道DMA采集,具体为使用ADC_IN5/6/7三个通道进行DMA连续ADC转换

3、实验流程

3.0、前提知识

STM32CubeMX教程13 ADC - 单通道转换”实验中提到过,规则通道只有一个16位的数据寄存器,因此规则通道同时只能转换一个ADC通道,而且每次转化完一个ADC通道就需要及时从数据寄存器中取出转化的数据,否则会被后面转化完毕的通道数据覆盖

这个时间非常短,一般不采用像单通道转化中使用中断提取处理每个单通道数据的方法,而是采用DMA连续转化的方法,将多通道转化完毕之后,在DMA的数据存储中将采集到的所有通道的数据一起处理 

ADC是利用片上的模数转换器将外部的模拟量转化为数字量存储到内存中,数据传输方向应该只有从外设到内存这一种方向,因此可知ADC的DMA方向也只有外设到内存一种

从“STM32CubeMX DMA 直接内存读取”实验中可知ADC1的DMA通道有DMA2_Stream0 CH0 和 DMA2_Stream4 CH0 两个通道

ADC的DMA请求模式一般选择循环模式,在多通道ADC采集时,配合使能扫描转化模式,这样就可以连续转化多通道而不停止

由于ADC采集后的数据一般需要存储在内存中,因此在选择地址递增时,ADC外设地址不增加,内存地址选择递增

使用HAL_ADC_Start_DMA()以DMA方式启动ADC采集时需要指定存储的内存首地址,从函数的定义可知其为uint32_t*类型,因此在DMA配置时我们需要选择的数据宽度为字Word

3.1、CubeMX相关配置

3.1.0、工程基本配置

打开STM32CubeMX软件,单击ACCESS TO MCU SELECTOR选择开发板MCU(选择你使用开发板的主控MCU型号),选中MCU型号后单击页面右上角Start Project开始工程,具体如下图所示

开始工程之后在配置主页面System Core/RCC中配置HSE/LSE晶振,在System Core/SYS中配置Debug模式,具体如下图所示

详细工程建立内容读者可以阅读“STM32CubeMX教程1 工程建立

3.1.1、时钟树配置

系统时钟使用8MHz外部高速时钟HSE,HCLK、PCLK1和PCLK2均设置为STM32F407能达到的最高时钟频率,具体如下图所示

3.1.2、外设参数配置

本实验需要需要初始化USART1作为输出信息渠道,具体配置步骤请阅读“STM32CubeMX教程9 USART/UART 异步通信

设置TIM3通用定时器溢出时间100ms,外部触发事件选择更新事件,参数详解请阅读“STM32CubeMX教程6 TIM 通用定时器 - 生成PWM波”实验,具体配置如下图所示

在Pinout & Configuration页面左边功能分类栏目Analog中单击其中ADC1,勾选IN5/6/7三个通道,在下方的参数设置中以ADC - 单通道转换实验为模板修改部分参数

 Scan Conversion Mode 使能扫描转换模式,因此现在需要转换5/6/7三个通道,因此使能该模式之后,在规则通道转换为其中一个通道后就会接收转换下一个通道

 DMA Continuous Requests  使能DMA连续转换请求,该参数的使能需要在配置完DMA请求之后才可选,配合参数 Scan Conversion Mode 可以实现连续不间断的对三个通道数据进行采集

 End Of Conversion Selection 选择EOC flag at the end of all conversions,该参数表示当转换完毕一组ADC中的所有通道之后再产生EOC标志,进入中断

 Number Of Conversion 规则通道转换数量现在为3,对应三个不同的通道,通道转换顺序及每个通道的采样时间由Rank及其下参数决定

具体参数配置如下图所示

单击Configuration中的DMA Settings选项卡对ADC1的DMA请求进行设置,单击ADD按键增加DMA请求,这里可选的只有一个ADC1

选择想要使用的DMA Stream,并设置优先级,将DMA请求模式设置为循环模式,外设地址不增加,内存地址递增,数据宽度选择字Word

为何如此配置?

请阅读本实验“3.0、前提知识”

如下图所示为ADC1的DMA请求具体设置

3.1.3、外设中断配置

在Pinout & Configuration页面左边System Core/NVIC中勾选DMA2 Stream0 全局中断,然后选择合适的中断优先级即可

注意这里没有勾选ADC1/2/3的全局中断,因为外设DMA中断使用的回调函数和外设本身中断的回调函数一般是同一个回调函数(为什么?请阅读本实验3.2.2小节),如果同时开始两者中断可能会导致重复进入中断函数

但是有些外设使用DMA时必须开启自身的中断,不同外设情况不一样

建议在外设使用DMA时,尽量不开启外设全局中断,必须开启的可以禁用外设主要事件源产生的硬件中断(注释1)

上述步骤如下图所示

3.2、生成代码

3.2.0、配置Project Manager页面

单击进入Project Manager页面,在左边Project分栏中修改工程名称、工程目录和工具链,然后在Code Generator中勾选“Gnerate peripheral initialization as a pair of 'c/h' files per peripheral”,最后单击页面右上角GENERATE CODE生成工程,具体如下图所示

详细Project Manager配置内容读者可以阅读“STM32CubeMX教程1 工程建立”实验3.4.3小节

3.2.1、外设初始化调用流程

首先在生成的工程主函数main()中调用MX_DMA_Init()函数对ADC1用到的DMA时钟及其流的中断进行了配置

然后调用MX_ADC1_Init()函数对ADC1的基本参数、通道和通道参数进行了配置,并调用了HAL_ADC_Init()使用配置的参数初始化了ADC1

在初始化函数HAL_ADC_Init()中又调用了HAL_ADC_MspInit()函数,在该函数中使能了ADC1/GPIOA的时钟,对ADC1_IN5/6/7的输入引脚做了复用设置,然后对ADC1的DMA参数配置并进行了初始化,最后调用了__HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1)将adc1外设与DMA流对象关联

具体的ADC DMA初始化调用流程如下图所示

3.2.2、外设中断调用流程

CubeMX中勾选DMA2_Stream0的全局中断后,会在stm32f4xx_it.c中增加DMA的中断服务函数DMA2_Stream0_IRQHandler()

在中断服务函数DMA2_Stream0_IRQHandler()中调用了HAL库的DMA全局中断处理函数,该函数中根据各种标志判断DMA传输完成/失败/一半完成等事件,然后根据不同的事件调用不同的回调函数,这里DMA传输完成之后调用了hdma->->XferCpltCal1back()

上述过程如下图所示

这个函数指针在以DMA方式启动ADC采集时被指向DMA传输完成回调ADC_DMAConvCplt()函数

在该DMA传输完成回调ADC_DMAConvCplt()函数中最终调用了ADC采集完成回调HAL_ADC_ConvCpltCallback()函数,该函数上一个实验我们重新实现过

上述过程如下图所示

之前所有的外设回调函数都是直接调用了HAL库提前准备好的虚函数,比如ADC的采集完成回调函数HAL_ADC_ConvCpltCallback(),用户直接实现该虚函数即可

但是DMA不是一个外设,而是数据传输手段,大多数外设都可以使用,因此DMA的各种事件回调函数不是一个真正的函数,而是一个函数指针

当我们以DMA传输的方式启动某个外设的时候,就会将该外设对应事件的中断服务函数地址赋值给对应事件DMA中断回调函数指针

3.2.3、添加其他必要代码

在主函数中以DMA的方式启动ADC采集传输,然后启动ADC1的触发源TIM3定时器,具体代码如下图所示

在adc.c中重新实现DMA传输完成回调函数,在该函数中取出ADC转换完成的三通道采集值,然后处理并通过串口输出,具体代码如下图所示

一些定义及函数源代码如下

/*main.c中的全局变量定义*/
uint32_t DataBuffer[BATCH_DATA_LEN];

/*main.h中的变量外扩及宏定义*/
#define BATCH_DATA_LEN 3
extern uint32_t DataBuffer[BATCH_DATA_LEN];

/*DMA转换完成中断回调*/
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
    /*定时器DMA启动多通道转换*/
    uint32_t val=0,Volt=0;
    for(uint8_t i=0;i<BATCH_DATA_LEN;i++)
    {
        val=DataBuffer[i];
        Volt=(3300*val)>>12;
        printf("ADC_IN%d, val:%d, Volt:%d\r\n",i+5,val,Volt);
    }
    printf("\r\n");
}

4、常用函数

/*以DMA方式启动ADC采集*/
HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef *hadc, uint32_t *pData, uint32_t Length)

/*结束以DMA方式启动的ADC采集*/
HAL_StatusTypeDef HAL_ADC_Stop_DMA(ADC_HandleTypeDef *hadc)

5、烧录验证

烧录程序,单片机上电之后,串口不断的输出三个通道的ADC采集值,笔者将三个滑动变阻器按照通道5、通道6和通道7的顺序,分别从一端缓慢拧到另一端,可以从串口输出的数据看到,通道5/6/7三个通道采集到的ADC数据从最大4095慢慢变到最小值0

6、注释详解

注释1:详细内容请阅读STM32Cube高效开发教程(基础篇)14.5.1小节内容

  • 50
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为了实现ADC多通道DMA滤波,需要进行以下步骤: 1. 使用STM32CubeMX配置ADCDMASTM32CubeMX中,选择正确的芯片型号,打开“Pinout & Configuration”选项卡,然后配置ADCDMA的参数。在“ADC”选项卡中,选择要使用的通道和采样时间,然后在“DMA”选项卡中,配置DMA通道和传输模式。 2. 设置ADC DMA模式 在代码中,需要将ADC设置为DMA模式。这可以通过在HAL_ADC_Start_DMA函数中设置相应的参数来实现。例如,以下代码将ADC设置为连续DMA模式: HAL_ADC_Start_DMA(&hadc1, (uint32_t*)ADCBuffer, ADC_BUFFER_SIZE); 3. 实现数据滤波 在ADC数据传输到缓冲区之后,可以对数据进行滤波处理。可以使用各种滤波算法,例如移动平均、中值滤波等。以下是一个使用移动平均滤波的示例代码: #define N 10 uint32_t FilteredData[ADC_NUM_CHANNELS]; void FilterData(uint32_t* ADCData) { static uint32_t FilterBuffer[ADC_NUM_CHANNELS][N]; static uint32_t FilterIndex[ADC_NUM_CHANNELS] = {0}; uint32_t i, j; for(i=0; i<ADC_NUM_CHANNELS; i++) { FilterBuffer[i][FilterIndex[i]] = ADCData[i]; FilterIndex[i] = (FilterIndex[i]+1) % N; FilteredData[i] = 0; for(j=0; j<N; j++) { FilteredData[i] += FilterBuffer[i][j]; } FilteredData[i] /= N; } } 在此示例中,FilterBuffer是一个二维数组,用于存储ADC数据。FilterIndex是一个数组,用于跟踪每个通道的滤波器缓冲区中的位置。FilteredData是一个数组,用于存储滤波后的数据。 4. 处理滤波后的数据 滤波后的数据现在可以用于您的应用程序。可以将其发送到计算机或其他设备,或者用于控制硬件。以下是一个将滤波后的数据通过串口发送到计算机的示例代码: #define UART_TX_BUFFER_SIZE 256 uint8_t UARTTxBuffer[UART_TX_BUFFER_SIZE]; void SendData(void) { uint32_t i; uint32_t len = 0; for(i=0; i<ADC_NUM_CHANNELS; i++) { len += sprintf((char*)(UARTTxBuffer+len), "%d,", FilteredData[i]); } UARTTxBuffer[len-1] = '\n'; HAL_UART_Transmit(&huart1, UARTTxBuffer, len, 100); } 在此示例中,使用sprintf函数将滤波后的数据转换为字符串,并将其存储在UARTTxBuffer中。然后,使用HAL_UART_Transmit函数将数据发送到计算机。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值