STM32的HAL库开发---单通道ADC采集(DMA读取)实验

一、实验简介

正常单通道ADC采集顺序是先开启ADC采集,然后等待ADC转换完成,也就是判断EOC位置1,然后再读取数据寄存器的值。

如果配置了DMA功能,在EOC位被硬件置1后,自动产生DMA请求,然后DMA进行数据搬运。

1,功能描述 通过DMA读取数据

通过ADC1通道1PA1)采集电位器的电压,并显示ADC转换的数字量及换算后的电压值

2、确定最小刻度

VREF+ = 3.3V ---》 0V ≤ VIN ≤  3.3V ---》最小刻度 = 3.3 / 4096 ,F1的分辨率是12位的,也就是把3.3V分为4096份。F4/F7/H7还可以自己配置分辨率,例如H7可以把分辨率配置为16位的,也就是把3.3V进行65536等分。

3,确定转换时间

采样时间239.5个ADC时钟周期为例,可以得到转换时间为21us。例如配置为最长的采样时间239.5个采样周期,那么采样时间就是239.5 + 12.5  = 252个时钟周期。配置ADC的时钟的12M,则转换时间为252 * (1 / 12000000) = 21us,采样时间设置的越大,准确度越高,设置的越小,准确度越低。

4、模式组合

由于使用了DMA搬运,所以使用连续转换模式、不使用扫描模式

二、单通道ADC采集实验配置步骤 

1、HAL_DMA_Init()函数,初始化DMA

2、__HAL_LINKDMA()宏定义,将DMAADC句柄联系起来

3、HAL_ADC_Init()函数,用于初始化ADC,配置ADC工作参数。

4、HAL_ADCEx_Calibration_Start()函数,用于ADC校准的。

5、HAL_ADC_MspInit()函数, 配置NVIC、CLOCK、GPIO

6、HAL_ADC_ConfigChannel()函数,配置ADC相应通道相关参数

7、HAL_NVIC_SetPriority()HAL_NVIC_EnableIRQ()函数,使能DMA数据流传输完成中断

8、DMAx_Channely_IRQHandler()函数,编写DMA数据流中断服务函数

9、HAL_DMA_Start_IT()函数,启动DMA,开启传输完成中断

10、HAL_ADC_Start_DMA()函数,触发ADC转换,DMA传输数据

三、 实验程序

1、寄存器版本

dma.c源程序

#include "./BSP/DMA/dma.h"
#include <string.h>

uint16_t ADC_data[20];
//配置ADC1的DMA1的通道1请求
void DMA_Init(void)
{
	//开启DMA1时钟
	RCC->AHBENR |= (1 << 0);
	
	//MEM2MEM 设置为非存储器到存储器模式
	DMA1_Channel1->CCR &= ~(1 << 14);
	
	//PL 设置通道优先级为中
	DMA1_Channel1->CCR |= (1 << 12);
	
	//MSIZE 设置存储器数据宽度为16位
	DMA1_Channel1->CCR |= (1 << 10);
	
	//PSIZE 设置外设数据宽度为16位
	DMA1_Channel1->CCR |= (1 << 8);
	
	//MINC 设置存储器增量模式
	DMA1_Channel1->CCR |= (1 << 7);
	
	//PINC 设置外设不增量模式
	DMA1_Channel1->CCR &= ~(1 << 6);
	
	//CIRC 不执行循环模式
	//DMA1_Channel1->CCR &= ~(1 << 5);
	DMA1_Channel1->CCR |= (1 << 5);
	
	//DIR 从外设读取
	DMA1_Channel1->CCR &= ~(1 << 4);	
	
	//设置传输数量
	DMA1_Channel1->CNDTR = 20;
	
	//设置DMA的外地址
	DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR;
	
	//清空ADC_data
	memset((void*)ADC_data,0,20);
	
	DMA1_Channel1->CMAR = (uint32_t)ADC_data;
	
	//EN 开启DMA  这个必须放在最后  不然CNDTR寄存器不能写入
	DMA1_Channel1->CCR |= (1 << 0);
}

adc.c源程序

#include "./BSP/ADC/adc.h"

//配置ADC1的通道1 PA1进行
void ADC_Init(void)
{
	//开启ADC1时钟
	RCC->APB2ENR |= (1 << 9);
	
	//关闭扫描模式
	ADC1->CR1 &= ~(1 << 8);
	
	//开启外部触发转换ADC
	ADC1->CR2 |= (1 << 20);
	
	//EXTSEL 设置软件触发ADC转换
	ADC1->CR2 |= (7 << 17);
	
	//ALIGN 数据右对齐
	ADC1->CR2 &= ~(1 << 11);
	
	//DMA 开启DMA转换
	ADC1->CR2 |= (1 << 8);
	
	//CONT 开启连续转换模式
	ADC1->CR2 |= (1 << 1);
	
	//设置ADC1通道1转换时间为239.5个周期
	ADC1->SMPR2 |= (7 << 3);
	
	//L 设置总共1一个转换通道
	ADC1->SQR1 |= (1 << 20);
	
	//设置ADC第一个转换为通道1
	ADC1->SQR3 |= (1 << 0);
	
	//EXTTRIG 开启GPIOA时钟
	RCC->APB2ENR |= (1 << 2);
	
	//设置PA1为输入模式
	GPIOA->CRL &= ~(0XF << 4);
	
	
	//ADON 开启ADC功能
	ADC1->CR2 |= (1 << 0);
	
	//RSTCAL 初始化ADC校准寄存器
	ADC1->CR2 |= (1 << 3);
	
	//等待ADC校准寄存器初始化完毕
	while(ADC1->CR2 & (1 << 3));
	
	//开启ADC校准
	ADC1->CR2 |= (1 << 2);
	
	//等待ADC校准完成
	while(ADC1->CR2 & (1 << 2));
	
	
	
	//SWSTART 开启规则组转换 软件触发
	ADC1->CR2 |= (1 << 22);
}

2、库函数版本

dam.c源程序

#include "./BSP/DMA/dma.h"
#include <string.h>

extern ADC_HandleTypeDef hadc;
DMA_HandleTypeDef hdma;
//配置ADC1的DMA1的通道1请求
void DMA_Init(void)
{
	//开启DMA1时钟
	__HAL_RCC_DMA1_CLK_ENABLE();
	
	//配置DMA1通道  因为ADC1连接在DMA1的通道1  这个在使用手册可以查找到
	hdma.Instance = DMA1_Channel1;
	
	//外设到内存
	hdma.Init.Direction = DMA_PERIPH_TO_MEMORY;
	//内存为16位
	hdma.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
	//内存递增
	hdma.Init.MemInc = DMA_MINC_ENABLE;
	//循环搬运 
	//当启动了循环模式,数据传输的数目变为0时,将会自动地被恢复成配置通道时设置的初值,DMA操作将会继续进行。
	hdma.Init.Mode = DMA_NORMAL;
	//外设16位
	hdma.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
	//外设地址不递增
	hdma.Init.PeriphInc = DMA_PINC_DISABLE;
	//通道1优先级为中
	hdma.Init.Priority = DMA_PRIORITY_MEDIUM;

	//配置DMA的通道1为外设到内存
	HAL_DMA_Init(&hdma);
	
	//将DMA句柄于ADC句柄连接起来 可以理解为将这个DMA句柄拷贝到ADC句柄里的DMA句柄上
	__HAL_LINKDMA(&hadc,DMA_Handle,hdma);
}

//标志DMA数据搬运完
uint8_t state = 0;
void DMA1_Channel1_IRQHandler(void)
{
	if(DMA1->ISR & (1 << 1))
	{
		state = 1;
		//清除中断标志位
		DMA1->IFCR |= (1 << 1);
	}
	DMA1->IFCR |= (1 << 0);
}

adc.c源程序

#include "./BSP/ADC/adc.h"
#include "string.h"

extern DMA_HandleTypeDef hdma;
uint16_t ADC_data[20];
ADC_HandleTypeDef hadc;
//配置ADC1的通道1 PA1进行
void ADC_Init(void)
{
	hadc.Instance = ADC1;
	//配置ADC连续转换模式 就是ADC转换完成一次后会自动下一次转换
	hadc.Init.ContinuousConvMode = ENABLE;
	//转换结果采用右对齐
	hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
	//不开启间断模式
	hadc.Init.DiscontinuousConvMode = DISABLE;
	//ADC触发选用软件触发
	hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
	//设置ADC转换数量的 SQR1的L位
	hadc.Init.NbrOfConversion = 1;
	//设置间断模式写转换一次转换数量
	hadc.Init.NbrOfDiscConversion = 0;
	//不开启扫描模式 因为就一个通道
	hadc.Init.ScanConvMode = ADC_SCAN_DISABLE;
	
	//设置ADC为软件触发转换
	HAL_ADC_Init(&hadc);
	
	//开启ADC校准
	HAL_ADCEx_Calibration_Start(&hadc);
	
	ADC_ChannelConfTypeDef sConfig;
	
	//配置通道1
	sConfig.Channel = ADC_CHANNEL_1;
	//通道1第一个转换
	sConfig.Rank = ADC_REGULAR_RANK_1;
	//采样周期采用239.5
	sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
	
	//配置ADC通道一为第一个转换
	HAL_ADC_ConfigChannel(&hadc, &sConfig);
	
	memset((void*)ADC_data,0,20);
	
	//这句话最好写 不然下面那个会把半传输中断也开开  这里就会开一个中断 因为下面那个函数回把ADC半传输回调函数复制 
	//这句话不写也行 但是中断里边要清除所有位  不然卡在中断
	HAL_DMA_Start_IT(&hdma, (uint32_t)&ADC1->DR, (uint32_t)ADC_data, 20);
	
	//开启DMA传输 这个函数里边会把中断都打开
	HAL_ADC_Start_DMA(&hadc, (uint32_t* )ADC_data, 20);
}


void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
	//开启ADC1时钟
	__HAL_RCC_ADC1_CLK_ENABLE();
	
	//开启GPIOA时钟
	__HAL_RCC_GPIOA_CLK_ENABLE();
	
	GPIO_InitTypeDef GPIO_Init;
	
	GPIO_Init.Mode = GPIO_MODE_ANALOG;
	GPIO_Init.Pin = GPIO_PIN_1;
	GPIO_Init.Pull = GPIO_NOPULL;
	
	//设置PA1为模拟输入模式
	HAL_GPIO_Init(GPIOA,&GPIO_Init);
	
	//使能DMA1通道1中断
	HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
	
	//设置中断优先级
	HAL_NVIC_SetPriority(DMA1_Channel1_IRQn,2,2);
}

这里主要是要注意先配置DMA中断,再调用ADC的DMA函数,具体原因在代码里写了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/977447.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

eclogy后台运维笔记(写的很乱,只限个人观看)

组织权限&#xff1a; 矩阵管理 这个很重要&#xff0c;比如进行流程操作者的选择时&#xff0c;我们进行需要选择财务部的出纳&#xff0c;会计&#xff0c;总经理。我们不能去直接选定一个人&#xff0c;万一这个人离职了&#xff0c;那所有的流程都要手动修改&#xff0c;…

【网络编程】几个常用命令:ping / netstat / xargs / pidof / watch

ping&#xff1a;检测网络联通 1. ping 的基本功能2. ping 的工作原理3. ping 的常见用法4. ping 的输出解释5. ping 的应用场景6. 注意事项 netstat&#xff1a;查看网络状态 1. netstat 的基本功能2. 常见用法3. 示例4. 输出字段解释5. netstat 的替代工具6. 注意事项 xargs&…

自定义Spring Boot Starter(官网文档解读)

摘要 本文将详细介绍自定义 Spring Boot Starter 的完整过程。要构建自定义 Starter&#xff0c;首先需掌握 Spring Boot 中 Auto-configuration 以及相关注解的工作原理&#xff0c;同时了解 Spring Boot 提供的一系列条件注解。在具备这些知识基础后&#xff0c;再按照特定步…

C++和OpenGL实现3D游戏编程【连载23】——几何着色器和法线可视化

欢迎来到zhooyu的C++和OpenGL游戏专栏,专栏连载的所有精彩内容目录详见下边链接: 🔥C++和OpenGL实现3D游戏编程【总览】 1、本节实现的内容 上一节课,我们在Blend软件中导出经纬球模型时,遇到了经纬球法线导致我们在游戏中模型光照显示问题,我们在Blender软件中可以通过…

我的技术十年

前言 十年一瞬&#xff0c;2014 年毕业至今&#xff0c;刚好十年。《异类》一书曾提到“一万小时定律”&#xff0c;要成为某个领域的专家&#xff0c;需要 10000 小时&#xff0c;按比例计算就是&#xff1a;如果你每天工作八小时&#xff0c;一周工作五天&#xff0c;那么成…

kotlin 知识点一 变量和函数

在Kotlin中定义变量的方式和Java 区别很大&#xff0c;在Java 中如果想要定义一个变 量&#xff0c;需要在变量前面声明这个变量的类型&#xff0c;比如说int a表示a是一个整型变量&#xff0c;String b表 示b是一个字符串变量。而Kotlin中定义一个变量&#xff0c;只允许在变量…

链表-基础训练(二)链表 day14

两两交换链表中的节点 题目示意&#xff1a; 给定一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后的链表。 你不能只是单纯的改变节点内部的值&#xff0c;而是需要实际的进行节点交换。 原先我的思路是图像上的思路&#xff0c;但是我感觉还是很复杂…

智能交通系统(Intelligent Transportation Systems):智慧城市中的交通革新

智能交通系统&#xff08;Intelligent Transportation Systems, ITS&#xff09;是利用先进的信息技术、通信技术、传感技术、计算机技术以及自动化技术等&#xff0c;来提升交通系统效率和安全性的一种交通管理方式。ITS通过收集和分析交通数据&#xff0c;智能化地调度、控制…

数据结构:Map set - 习题(三)

一、只出现一次的数字 题目链接https://leetcode.cn/problems/single-number/description/ 描述&#xff1a; 给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性…

【射频仿真学习笔记】Cadence的Layout EXL与ADS dynamic link联动后仿

一、EXL仿真 1. 绘制教程 当我们使用EMX仿真提取的时候&#xff0c;会遇到各种各样的问题&#xff0c;很不方便。这里我们介绍一种新的方法——EXL。可以更灵活的跑仿真。我们以带有中和电容的差分电路为例进行介绍 在使用EMX的是偶&#xff0c;port是连不到晶体管外围金属上…

C++——list模拟实现

目录 前言 一、list的结构 二、默认成员函数 构造函数 析构函数 clear 拷贝构造 赋值重载 swap 三、容量相关 empty size 四、数据访问 front/back 五、普通迭代器 begin/end 六、const迭代器 begin/end 七、插入数据 insert push_back push_front 八、…

文件包含-session2

[题目信息]&#xff1a; 题目名称题目难度文件包含-session22 [题目考点]&#xff1a; 由于网站功能需求&#xff0c;会让前端用户选择要包含的文件&#xff0c;而开发人员又没有对要包含的文件进行安全考虑&#xff0c;就导致攻击者可以通过修改文件的位置来让后台执行任意…

Hadoop初体验

一、HDFS初体验 1. shell命令操作 hadoop fs -mkdir /itcast hadoop fs -put zookeeper.out /itcast hadoop fs -ls / 2. Web UI页面操作 结论&#xff1a; HDFS本质就是一个文件系统有目录树结构 和Linux类似&#xff0c;分文件、文件夹为什么上传一个小文件也这…

机器学习基础入门——机器学习库介绍(NumPy、pandas、Matplotlib)

机器学习库介绍&#xff08;NumPy、pandas、Matplotlib&#xff09; 在 Python 机器学习的领域中&#xff0c;NumPy、pandas 和 Matplotlib 是三个不可或缺的基础库。它们分别在数值计算、数据处理与分析以及数据可视化方面发挥着关键作用&#xff0c;极大地提升了开发效率与数…

Redis——用户签到BitMap,UV统计

目录 BitMap 使用场景 1. 用户签到系统 2. 用户行为标记 3. 布隆过滤器&#xff08;Bloom Filter&#xff09; BitMap介绍 Redis中的使用 Redis功能示例 添加&#xff1a; 获取&#xff1a; 批量获取&#xff1a; java中实现 统计本月连续签到次数 UV统计 UV 统计…

【数据结构】(12) 反射、枚举、lambda 表达式

一、反射 1、反射机制定义及作用 反射是允许程序在运行时检查和操作类、方法、属性等的机制&#xff0c;能够动态地获取信息、调用方法等。换句话说&#xff0c;在编写程序时&#xff0c;不需要知道要操作的类的具体信息&#xff0c;而是在程序运行时获取和使用。 2、反射机制…

【初探数据结构】时间复杂度和空间复杂度

&#x1f4ac; 欢迎讨论&#xff1a;在阅读过程中有任何疑问&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;如果你觉得这篇文章对你有帮助&#xff0c;记得点赞、收藏&#xff0c;并分享给更多对数据结构感…

基于 MySQL 递归 CTE 实现表,父级id与子级id拼接

1、函数 xr_test.tb_content替换成自己的表 CREATE DEFINERroot% FUNCTION get_related_ids(start_id BIGINT) RETURNS varchar(1000) CHARSET utf8mb4 COLLATE utf8mb4_general_ciDETERMINISTIC BEGINDECLARE result_ids VARCHAR(1000);-- 使用递归 CTE 查找所有相关的 idWI…

Redission可重试、超时续约的实现原理(源码分析)

Redission遇到其他进程已经占用资源的时候会在指定时间waitTime内进行重试。实现过程如下&#xff1a; 执行获取锁的lua脚本时&#xff0c;会返回一个值&#xff0c; 如果获取锁成功&#xff0c;返回nil&#xff0c;也就是java里的null 如果获取锁失败&#xff0c;用语句“PT…

ue----git局域网内部署裸仓库,别的机器进行访问

最近由于经常迁移项目到另一台机器上进行部署更新一点就要整个迁移 弄得麻烦了 就在网上学了一下这个方式 首先我们在想要建立裸仓库的电脑上找到一个文件夹放置我们的裸仓库 在此点击鼠标右键选择 open git bash here 输入命令 创裸仓库 git init --bare gitTestName.git…