CubeMX_ADC学习笔记——ADC(DMA模式)的使用
学生党,写博客的目的纯粹是为了记录自己的学习经历,方便以后翻看,所以写的不是很专业和规范。如有解释错误、理解错误、术语使用错误等问题,亦可指出。
一、在CubeMX上创建项目工程
1、创建工程
2、搜索芯片型号
3、双击选择芯片型号(STM32G071RBTX)
4、进入界面,选择串口SWD调试方式
5、选择外部陶瓷晶振
6、GPIO配置
7、ADC配置
注 需要关注的有:
在设置通道优先级时要对应住:
synchronous clock mode divided by 同步时钟模式分频
asynchronous clock mode divided 异步时钟模式分频
resolution分辨率
data alignment 数据对齐(右)
Sequencer 扫描顺序
Sequencer fully configurable 描通道的顺序与通道号channel number无关
Sequencer not fully configurable 通道扫描的顺序由通道编号定义
scan conversion mode 扫描转换模式(需要选择Sequencer not fully configurable)
continuous conversion mode 自动连续扫描模式(需要选择Sequencer not fully configurable)
Discontinuous Conversion Mode 非连续转换模式
MDA continuous Requests DMA连续请求
(在开启DMA连续请求前 需先开启DMA配置)
End of conversion selection 转换结束选择
End of single conversion 单次转换结束
End of sequence of conversion 转换序列结束
overrun behaviour 侵占行为
overrun data preserved 溢出的数据保存
overrun data overwritten 溢出的数据覆盖
external trigger conversion edge 外部触发机制
8、定时器配置
记得在NVIC里使能定时器中断:
注 需要关注的有:
定时器的定时时间计算公式:
这里配置定时器是为了每1ms取出一次DMA的值。
ARR: 自动重装载寄存器( AutoReload Register )(16位数据)
PSC:预分频器(16位数据)
TIM_CLK: ADC时钟频率
主要要修改的就是上面的参数,但计算的时候要注意TIM_CLK的单位是HZ不是MHZ
9、串口配置(默认配置即可,最后记得在NVIC里使能串口中断)
10、配置DMA
注 需要关注的有:
一定要选择Circular模式(循环模式)
11、时钟树配置
注 需要关注的有:
(由于芯片的特性,所以最大主频只能到64而不是下面的72。)
HSI:内部高速时钟,RC振荡器,频率为8MHz。可作为系统时钟或PLL锁相环的输入。
HSE:外部高速时钟,可通过外接一个频率范围是4-16MHz的时钟或者晶振,HSE可以作为系统时钟和PLL锁相环输入,还可以经过128分频后输入给RTC。
LSI:内部低速时钟,RC振荡器,频率大约为40K,可供独立看门狗和RTC使用,并且独立看门狗只能使用LSI时钟。
LSE:外部低速时钟,通常在外部低速时钟管脚上外接一个32.768KHz的晶振,供RTC使用。
PLL:锁相环,用于倍频输出,因为开发板外部高速晶振也只有8M,如果要使芯片的最大时钟频率是72M,可通过PLL锁相环来倍频。从图标5中可以看到,PLL时钟输入源可选择为HSI/2、HSE或者HSE/2,时钟源经过2-16倍频后输入给PLLCLK,如果系统时钟选择由PLLCLK提供,则PLLCLK最大值不要超过72M。
SYSCLK:系统时钟。它是STM32中绝大部分部件工作的时钟源。它的时钟来源可以由HSI、HSE、PLLCLK提供。
系统复位后, HSI振荡器被选为系统时钟,当时钟源被直接或通过PLL间接作为系统时钟时,它将不能被停止。只有当目标时钟源准备就绪了(经过启动稳定阶段的延迟或PLL稳定),从一个时钟源到另一个时钟源的切换才会发生。在被选择时钟源没有就绪时,系统时钟的切换不会发生。直至目标时钟源就绪,才发生切换。
二、在MDK中修改CubeMX上创建的项目工程:
1、头文件、数据类型及函数声明的添加位置
在修改工程代码前需要先了解在MDK中把用户代码添加在什么位置才不会在再次生成CubeMX代码时被覆盖:
2、添加用户头文件(用于调用sprintf函数)
3、添加变量定义与声明
4、编写用户函数(求平均值函数)
5、串口打印函数
注意:
其中真实的电压值应该是ADC寄存器去除的值value*3.3/4096
6、重写ADC中断回调函数 和 定时器TIM1中断回调函数
注意:
我的ADC中断服务函数只用来记录ADC采集次数。
在定时器中断服务函数里是将MDA的缓存数据取出来传给待计算值channel1、channel2、channel3。即使用定时器每秒采集一个DMA的数据(ADC_MDA的频率远高于每毫秒1次)来实现精确的1KHZ的ADC采集频率其中channel3 2ms/次 即500KHZ。
7、定时器和DMA初始化
说明:
__HAL_TIM_CLEAR_IT(&htim1 ,TIM_IT_UPDATE ); 清除TIM1中断挂起
HAL_TIM_Base_Start_IT(&htim1 ); 启动定时器TIM1中断
HAL_ADC_Start_DMA (&hadc1,buffer,6); 启动ADC_MDA
8、主函数循环体while(1):
说明:
主函数使用average()函数求出ADC的平均值并每秒通过串口传输函数printf_ADCx()传输。
三、效果展示
1、未使用平均值函数的:(有明显突变和不稳定)
2、使用平均值函数的:(无明显突变)
对比数据可得出: 使用平均值的ADC更加平稳。ADC采集源稳定时,没有突变。
四、最后附加一张main.c完整的程序
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2020 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* 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 ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
TIM_HandleTypeDef htim1;
UART_HandleTypeDef huart1;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_ADC1_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_TIM1_Init(void);
/* USER CODE BEGIN PFP */
char TxData[10