STM32学习笔记3---ADC,DMA

news2024/11/15 22:30:24

目录

ADC模拟数字转换器

规则组的四种转换模式

AD单通道 

AD多通道

 常用代码函数相关

DMA直接存储器 存取(访问)

两个应用

DMA存储器到存储器的转运

ADC+DMA 


ADC模拟数字转换器

stm32数字电路,只有高低电平,无几V电压的概念

ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量

12位(分辨率,0-2的12次方减1)逐次逼近型(工作模式)ADC,1us(转换频率1MHz)转换时间

输入电压范围:0~3.3V,转换结果范围:0~4095

18个输入通道,可测量16(16GPIO)个外部(最多)和2个内部(内部温度传感器,内部参考电压)信号源

规则组和注入组两个转换单元

STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道(该型号不够16)

(ADC模拟到数字)(DAC,PWM数字到模拟)

根据引脚定义表,PAO到PB1这10个引脚是ADC的10个通道,但是其他的这些引脚。不是ADC的通道,就不能接模拟电压了

规则组的四种转换模式

连续转换or单次转换

扫描模式or非扫描模式

非扫描模式,菜单列表只用第一个,序列1-16,只有第一个有效

单次转换:触发一次仅进行一次转换,若需下一次需要再次触发

连续转换:一次转换后不会停止,立刻开始下一轮,一直持续下去


AD单通道 

1.初始化

RCC(GPIO , ADC1(APB2),ADC CLK(clock)的分频器

GPIO(模拟输入)

规则组通道配置,多路开关(左边通道,接入右边的规则组)(点菜)(指定通道通道0,序列,指定通道采样时间)

配置ADC转换器

开关控制,开启ADC

ADC校准


main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t ADValue;			//定义AD值变量
float Voltage;				//定义电压变量

int main(void)
{
	
	OLED_Init();			
	AD_Init();				
	
	
	OLED_ShowString(1, 1, "ADValue:");
	OLED_ShowString(2, 1, "Voltage:0.00V");
	
	while (1)
	{
		ADValue = AD_GetValue();					//AD转换的值(0-4095)
		Voltage = (float)ADValue / 4095 * 3.3;		//AD值线性变换到0~3.3的范围,表电压
		
		OLED_ShowNum(1, 9, ADValue, 4);				//显示AD值
		OLED_ShowNum(2, 9, Voltage, 1);				//显示电压值整数部分
		OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2);	//显示电压值的小数部分
		
		Delay_ms(100);			//延时100ms,手动增加一些转换的间隔时间!!!!!
	}
}
//AD.c
#include "stm32f10x.h"                  // Device header

void AD_Init(void)
{
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	
	
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//ADCCLK,6分频(ADCCLK=72/6=12MHz)						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
	
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);					
	
	//规则组通道配置
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
	//ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 2, ADC_SampleTime_55Cycles5);//若想填充菜单列表
	
	//ADC初始化
	ADC_InitTypeDef ADC_InitStructure;						
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;		//独立模式(而非双ADC模式)
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//数据对齐右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//触发源,内部软件触发
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;		//连续转换,单次转换
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;			//扫描模式,非扫描
	ADC_InitStructure.ADC_NbrOfChannel = 1;					//通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1
	ADC_Init(ADC1, &ADC_InitStructure);						
	
	//ADC使能
	ADC_Cmd(ADC1, ENABLE);									
	
	//ADC校准
	ADC_ResetCalibration(ADC1);//复位校准								
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);//返回复位校准的状态(等待校准完成)
	ADC_StartCalibration(ADC1);//启动校准
	while (ADC_GetCalibrationStatus(ADC1) == SET);//等待校准完成
}


uint16_t AD_GetValue(void)//获取返回电压值
{
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//软件触发AD转换
	while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	//等待EOC标志位置1,等待AD转换结束
	return ADC_GetConversionValue(ADC1);					//读数据寄存器
}

AD多通道

后面两种扫描模式,这个最好配合DMA(数据覆盖)

利用单次转换非扫描模式实现多通道

每次触发转换之前,手动更改列表第一个位置的通道

依次启动四次转换,并在转换前,指定转换通道,每次转换完成,把结果存在四个里面

uint16_t AD_GetValue(uint8_t ADC_Channel)
{
	ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);	//在每次转换前,根据函数形参灵活更改规则组的通道1
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//软件触发AD转换一次
	while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	//等待EOC标志位,即等待AD转换结束
	return ADC_GetConversionValue(ADC1);					//读数据寄存器,得到AD转换的结果
}
/
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t AD0, AD1, AD2, AD3;	//定义AD值变量

int main(void)
{
	/*模块初始化*/
	OLED_Init();				//OLED初始化
	AD_Init();					//AD初始化
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "AD0:");
	OLED_ShowString(2, 1, "AD1:");
	OLED_ShowString(3, 1, "AD2:");
	OLED_ShowString(4, 1, "AD3:");
	
	while (1)
	{
		AD0 = AD_GetValue(ADC_Channel_0);		//单次启动ADC,转换通道0
		AD1 = AD_GetValue(ADC_Channel_1);		//单次启动ADC,转换通道1
		AD2 = AD_GetValue(ADC_Channel_2);		//单次启动ADC,转换通道2
		AD3 = AD_GetValue(ADC_Channel_3);		//单次启动ADC,转换通道3
		
		OLED_ShowNum(1, 5, AD0, 4);				//显示通道0的转换结果AD0
		OLED_ShowNum(2, 5, AD1, 4);				//显示通道1的转换结果AD1
		OLED_ShowNum(3, 5, AD2, 4);				//显示通道2的转换结果AD2
		OLED_ShowNum(4, 5, AD3, 4);				//显示通道3的转换结果AD3
		
		Delay_ms(100);			//延时100ms,手动增加一些转换的间隔时间
	}
}

 常用代码函数相关

//配置ADCCLK分频器
void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);
//给ADC上电
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
//ADC 中断输出控制
void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);
//复位校准
void ADC_ResetCalibration(ADC_TypeDef* ADCx);
//获取复位校准状态
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);
//开始校准
void ADC_StartCalibration(ADC_TypeDef* ADCx);
//获取开始校准状态
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);
//软件触发转换(触发控制)
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
 //ADC规则组通道配置(给序列的每个位置填写指定的通道)
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
//读取转换结果
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);

DMA直接存储器 存取(访问)

数据转运

提供外设(外设寄存器:ADC、串口的数据寄存器)(硬件触发)存储器(运行内存SRAM和程序存储器Flash)(软件触发)或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源

数据转运的路径:12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)

每个通道都支持软件触发和特定的硬件触发

STM32F103C8T6 DMA资源:DMA1(7个通道)

stm32中存储器:

DMA

所以DMA,即是总线矩阵的主动单元,可以读写各种存储器,也是AHB总线上的被动单元

CPU通过这一条线路,就可以对DMA进行配置了

用于访问各个存储器的DMA总线

内部的多个通道,可以进行独立的数据转运

仲裁器,用于调度各个通道,防止产生冲突
AHB从设备。用于配置DMA参数

DMA请求,用于硬件触发DMA的数据转运

总之就是CPU或者DMA直接访问Flash(只读存储器的一种)的话。是只可以读而不可以写的

然后SRAM是运行内存,可以任意读写。没有问题

注意一下。写传输计数器时。必须要先关闭DMA,再进行

两个应用

外设地址显然应该填DataA数组的首地址

所以数据宽度都是按8位的字节传输

均自增

打印变量的地址,确定其存在的位置

uint8_t aa=0x66

(uint8_t)&aa

(uint32_t)&ADC1->DR(stm32结构体访问寄存器)

在STM32中,使用const定义的变量,是存储在Flash里面的

。自动重装和软件触发不能同时使用

DMA存储器到存储器的转运

1.初始化

开启时钟

DMA是AHB总线的设备,所以要用AHB开启时钟的函数

DMA1初始化

使能

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"

uint8_t DataA[] = {0x01, 0x02, 0x03, 0x04};	//DMA转运的源端数组(数组名就是地址)
uint8_t DataB[] = {0, 0, 0, 0};///DMA转运的目的数组

int main(void)
{
	OLED_Init();					
	MyDMA_Init((uint32_t)DataA, (uint32_t)DataB, 4);//传输四次
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "DataA");
	OLED_ShowString(3, 1, "DataB");
	
	/*显示数组的首地址*/
	OLED_ShowHexNum(1, 8, (uint32_t)DataA, 8);
	OLED_ShowHexNum(3, 8, (uint32_t)DataB, 8);
		
	while (1)
	{
		DataA[0] ++;		//变换测试数据
		DataA[1] ++;
		DataA[2] ++;
		DataA[3] ++;
		
		OLED_ShowHexNum(2, 1, DataA[0], 2);		//显示数组DataA
		OLED_ShowHexNum(2, 4, DataA[1], 2);
		OLED_ShowHexNum(2, 7, DataA[2], 2);
		OLED_ShowHexNum(2, 10, DataA[3], 2);
		OLED_ShowHexNum(4, 1, DataB[0], 2);		//显示数组DataB
		OLED_ShowHexNum(4, 4, DataB[1], 2);
		OLED_ShowHexNum(4, 7, DataB[2], 2);
		OLED_ShowHexNum(4, 10, DataB[3], 2);
		
		Delay_ms(1000);		//延时1s,观察转运前的现象
		
		MyDMA_Transfer();	//使用DMA转运数组,从DataA转运到DataB
		
		OLED_ShowHexNum(2, 1, DataA[0], 2);		//显示数组DataA
		OLED_ShowHexNum(2, 4, DataA[1], 2);
		OLED_ShowHexNum(2, 7, DataA[2], 2);
		OLED_ShowHexNum(2, 10, DataA[3], 2);
		OLED_ShowHexNum(4, 1, DataB[0], 2);		//显示数组DataB
		OLED_ShowHexNum(4, 4, DataB[1], 2);
		OLED_ShowHexNum(4, 7, DataB[2], 2);
		OLED_ShowHexNum(4, 10, DataB[3], 2);

		Delay_ms(1000);		//延时1s,观察转运后的现象
	}
}

///MyDMA.c
#include "stm32f10x.h"                  // Device header

uint16_t MyDMA_Size;					//定义全局变量,用于记住Init函数的Size,供Transfer函数使用

void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{
	MyDMA_Size = Size;					//将Size写入到全局变量,记住参数Size
	
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);						
	
	DMA_InitTypeDef DMA_InitStructure;										
	DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;//外设站点起始地址
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//数据宽度(以字节的方式传输)
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;//外设地址自增(数组之间的转运地址要自增)

	DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;//存储器站点起始地址
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//数据宽度
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器地址自增

	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//数据传输方向,选择由外设到存储器(外设站点作为源头)
	DMA_InitStructure.DMA_BufferSize = Size;//缓存区大小(传输计数器)?!!
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//传输模式(自动重装?)(因为是存储器到存储器)
	DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;//存储器到存储器(软件触发)
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级,选择中等

	DMA_Init(DMA1_Channel1, &DMA_InitStructure);//将结构体变量交给DMA_Init,配置DMA1的通道1
	
	//DMA使能
	DMA_Cmd(DMA1_Channel1, DISABLE);
}

//调用一次这个函数,就再次启动一次DMA转运
void MyDMA_Transfer(void)//需要重新给传输计数器赋值(DATAA变化了也能继续转运)
{
	DMA_Cmd(DMA1_Channel1, DISABLE);//DMA失能
 
	DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);	//写入传输计数器,指定将要转运的次数
	DMA_Cmd(DMA1_Channel1, ENABLE);//DMA使能
	
	while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);	//等待DMA工作完成
	DMA_ClearFlag(DMA1_FLAG_TC1);//清除工作完成标志位
}

ADC+DMA 

AD多通道+DMA数据转运功能

AD扫描模式(PA0-PA3)

菜单点4个(菜做好后放在了ADC_DR寄存器中)

DMA服务员

源地址((uint32_t)&ADC1->DR)

数据宽度,我们想要DR奇存器低16位的数据,所以数据宽度,就是HalfWord,以半字,16位来转运

就是开启ADC到DMA的输出

这里有3个硬件触发源,具体使用哪个,取决于把哪个的DMA输出给开启了

ADC_DMACmd(ADC1,ENABLE)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL:常用函数

MySQL:常用函数 日期时间函数字符串函数数学函数加密函数 在MySQL中,存在许多现成的函数,可以简化部分操作,本博客讲解MySQL中的常用函数。 日期时间函数 current_date current_date函数用于输出当前的日期: curren…

一道关于php文件包含的CTF题

一、源码 这是index.php的页面。 点击login后会发现url里多了action的参数&#xff0c;那么我们就可以通过它来获取源码。 ?actionphp://filter/readconvert.base64-encode/resourcelogin.php 再通过base64的解码可以查看源码。 index.php源码&#xff1a; <?php erro…

【编码解码】CyberChef v10.18.9

下载地址 【编码解码神器】CyberChef v10.18.9 在线地址 CyberChef (gchq.github.io) 简介 CyberChef 是一个简单易用的网页应用&#xff0c;&#xff0c;包含了四百多种在线编解码工具。它在浏览器中执行各种“网络安全”操作。这些操作包括简单的 XOR 和 Base64 编码、复…

基于单片机的无线空气质量检测系统设计

本设计以STC89C52单片机为核心&#xff0c;其中包含了温湿度检测模块、光照检测模块、PM2.5检测模块、报警电路、LCD显示屏显示电路、按键输入模块和无线传输模块来完成工作。首先&#xff0c;系统可以通过按键输入模块设置当前的时间和报警值&#xff1b;使用检测模块检测当前…

spring boot(学习笔记第十九课)

spring boot(学习笔记第十九课) Spring boot的batch框架&#xff0c;以及Swagger3(OpenAPI)整合 学习内容&#xff1a; Spring boot的batch框架Spring boot的Swagger3&#xff08;OpenAPI&#xff09;整合 1. Spring boot batch框架 Spring Batch是什么 Spring Batch 是一个…

个人网站免费上线

声明一下&#xff0c;小科用的是natapp&#xff0c;进行的 1.起步-下载安装 去浏览器搜索" natapp "&#xff0c;在官网下载&#xff0c;或者直接 点击下列网站 NATAPP-内网穿透 基于ngrok的国内高速内网映射工具https://natapp.cn/ 打开后下滑找到下载&#xff…

JMeter Plugins之内网插件问题解决

JMeter Plugins之内网插件问题解决 背景 在我司内部进行JMeter工具进行性能脚本开发时&#xff0c;为了提高测试效率&#xff0c;我们会用到部分JMeter提供的插件&#xff0c;但是在我司内网的情况下&#xff0c;我们如果直接点击JMeter界面右上角的插件按钮 弹出来的JMeter…

洛谷刷题(4)

P1089 [NOIP2004 提高组] 津津的储蓄计划 题目描述 津津的零花钱一直都是自己管理。每个月的月初妈妈给津津 300 元钱&#xff0c;津津会预算这个月的花销&#xff0c;并且总能做到实际花销和预算的相同。 为了让津津学习如何储蓄&#xff0c;妈妈提出&#xff0c;津津可以随…

零基础5分钟上手亚马逊云科技 - AI模型内容安全过滤

在上一篇文章中&#xff0c;小李哥带大家深入调研亚马逊云科技AI模型平台Amazon Bedrock热门开发功能&#xff0c;了解了模型平台的文字/图片生成、模型表现评估和模型内容安全审核的实践操作。这次我们将继续介绍如何利用API的形式&#xff0c;利用Python代码的形式对AI模型内…

OpenSearch的快照还原

本次测试选择把索引快照备份到Amazon S3&#xff0c;所以需要使用S3 repository plugin&#xff0c;这个插件添加了对使用 Amazon S3 作为快照/恢复存储库的支持。 OpenSearch集群自带了这个插件&#xff0c;所以无需额外安装。 由于需要和Amazon Web Services打交道&#xf…

工厂数字化转型中工业一体机起到什么作用?

近年来工厂数字化转型成为企业提升竞争力的关键路径。而在这场转型浪潮中&#xff0c;工业一体机扮演着至关重要的角色&#xff0c;它不仅是推动工厂数字化转型的关键工具&#xff0c;更是赋能企业实现更高效、智能、灵活生产的关键要素。 一、工业一体机&#xff1a;连接物理与…

CAN通信之波特率相关配置

由于 CAN 属于异步通讯&#xff0c;没有时钟信号线&#xff0c;连接在同一个总线网络中的各个节点会像串口异步通讯那样&#xff0c;节点间使用约定好的波特率进行通讯。 首先我们要明确几个概念&#xff1a; 波特率&#xff1a;can 1s传输的位数&#xff0c;其单位为bps。 T…

Vue3学习笔记之插槽

目录 前言 一、基础 (一) 默认插槽 (二) 具名插槽 (三) 作用域插槽 (四) 动态插槽 二、实战案例 前言 插槽&#xff08;Slots&#xff09;&#xff1f; 插槽可以实现父组件自定义内容传递给子组件展示&#xff0c;相当于一块画板&#xff0c;画板就是我们的子组件&…

速速报名|数据治理与数据建模workshop报名开启

由Datamodeling社区出品的「数据治理与数据建模workshop 」将在9月份正式启动上海站和深圳站。 本课程由社区特邀讲师王琤老师、黄峰老师授课&#xff0c;两位老师基于丰富的数据管理经验提炼出知识体系&#xff0c;以面对面带练的方式&#xff0c;帮助学习者快速掌握数据建模…

dp+差分数组

前言&#xff1a;怎么也没想到要用dp来做&#xff0c;并且这个题目中如果列为1的话还要特殊考虑 题目地址 #include<bits/stdc.h> using namespace std;//#define int long long const int N (int)5e3 10; int dp[N][N][2]; // 0 表示上端点&#xff0c;1表示下端点 in…

正确枚举 Winlogon 桌面窗口层次

目录 前言 原理解释 原理实现 Winlogon 桌面窗口层次 本文出处链接&#xff1a;https://blog.csdn.net/qq_59075481/article/details/141608316。 前言 众所周知&#xff0c;从 Windows 7 开始&#xff0c;Winlogon 桌面不再使用 SASWindow 作为背景窗口&#xff0c;而是采…

翻斗雨量监测站

翻斗雨量监测站通常用于测量和记录降雨量&#xff0c;其主要功能包括&#xff1a; 测量降雨量&#xff1a;翻斗雨量监测站使用翻斗式测量原理&#xff0c;通过记录翻斗倒转的次数或翻斗中积累的水量来测量降雨量。可以准确地记录降雨量的变化。 记录降雨时间&#xff1a;翻斗雨…

PowerDesigner生成数据字典文档

PowerDesigner生成数据字典文档 目录 1. 设置报告 2. 导出报告 3. 查看报告 设置报告 删除多余的选项&#xff0c;只保留【LIst of Table Columns -表%PARENT%的栏的清单】选项。 只显示Name、Code、Data Type、Length、Is Key等列 导出报告 查看报告

如何用Java SpringBoot+Vue构建房产信息管理系统?详解开发流程

&#x1f393; 作者&#xff1a;计算机毕设小月哥 | 软件开发专家 &#x1f5a5;️ 简介&#xff1a;8年计算机软件程序开发经验。精通Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等技术栈。 &#x1f6e0;️ 专业服务 &#x1f6e0;️ 需求定制化开发源码提…

互联网应用主流框架整合之Spring缓存机制和Redis结合

Redis和数据库的结合 在实际的商用软件使用中&#xff0c;通常都是Redis和关系型数据配置使用&#xff0c;单纯使用Redis来存数据成本太高&#xff0c;并且其持久化和计算能力偏差&#xff0c;这两块无法和关系型数据相比较&#xff0c;而Redis和关系型数据库共存的场景就会带…