STM32学习笔记(七) ADC模数转换测电平(普通和DMA模式)
嵌入式系统在微控制领域(温度,湿度,压力检测,四轴飞行器)中占据着重要地位,这些功能的实现是由微处理器cpu(如stm32)和传感器以及控制器共同完成的,而连接他们,使它们能够互相正常交流的正是本小节要讲诉的模块,ADC模数转换外设。下面从最简单的实验说起,逐渐深入了解这个外设。
本次ADC模数转换设计实现并不复杂,步骤可简化为以下三步:
1. 接收板上电位器的输入电压
2. 经过A/D转换获得数字量,并传送给cpu
3. 通过串口在PC机上输出。
解析上面三个步骤,分析要求,就会发现ADC、GPIO、USART以及RCC模块就是本次实验所需要的用到的外设,因为除ADC模块,其它外设前面已经学习和实践了,那么理解和学习ADC模块,就可开始程序的设计实现了。
根据stm32f系列微控制器手册ADC章节
ADC转换的后数字量为12位(分辨率),在参考开发板用户手册和原理图,可知电位器的端口为PC0,输入电压范围0~3.3V,可知精度为3.3/(2^12)V.
查询stm32f107的引脚定义分配,可知PC0对应ADC12_IN10,也就是说采集电位器电压用ADC1和ADC2都可以,但必须采用通道10。
目前来说,用库函数操作可以避免出现漏错,因此我还是推荐使用库函数配置寄存器,但是了解库函数的含义还是十分有必要的:
typedef struct {u32 ADC_Mode; //明确ADC1和ADC2的工作方式,独立或其它组合 FunctionalState ADC_ScanConvMode; //通道工作方式,单通道还是多通道(扫描) FunctionalState ADC_ContinuousConvMode; //工作在连续还是单次模式(ADC转换工作在连续模式 u32 ADC_ExternalTrigConv; //A/D转换启动规则 u32 ADC_DataAlign; //判断转换数据的对齐方式 u8 ADC_NbrOfChannel; //明确规则转换通道的具体数目1~16 }ADC_InitTypeDef
了解上述结构体代表含义,下面就可以初始化相关寄存器实现ADC外设的配置:
GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; ADC_DeInit(ADC1); //ADC模块外设时钟需在APB2时钟基础上设置,决定单个周期的时钟长度(因为ADC时钟不能大于14MHZ,注意)RCC_ADCCLKConfig(RCC_PCLK2_Div4); //使能ADC对应GPIO口,外设区域及复用功能时钟 RCC_APB2PeriphClockCmd(RCC_ADC1, ENABLE);//初始化ADC模块对应GPIO GPIO_InitStructure.GPIO_Pin = GPIO_ADC1_Pin; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIO_ADC1, &GPIO_InitStructure);ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC1和ADC2工作在独立模式 ADC_InitStructure.ADC_ScanConvMode = ENABLE; //工作在扫描模式ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //转换工作在连续模式ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件触发,启动需调用ADC_Cmd程序 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐 ADC_InitStructure.ADC_NbrOfChannel = 1; //ADC通道数目为1 ADC_Init(ADC1, &ADC_InitStructure);//指定ADC转换的通道和转换周期 ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_55Cycles5); #ifdef USE_ADC1_DMA_PERADC_DMACmd(ADC1, ENABLE); //ADC1 DMA请求使能 #endifADC_Cmd(ADC1, ENABLE); //ADC1使能 ADC_ResetCalibration(ADC1); //重置ADC1校准寄存器 while (ADC_GetCalibrationStatus(ADC1)) //等待ADC1校准寄存器重置完成 { } ADC_StartCalibration(ADC1); //ADC1进入校准状态 while(ADC_GetCalibrationStatus(ADC1)) //等待ADC1校准完成 { } ADC_SoftwareStartConvCmd(ADC1, ENABLE); //ADC1软件触发方式启动
这里有一个重要知识点:ADC通道的规则组和注入组
在AD转换中,规则组定义的是ADC扫描通道的顺序,按照规则组配置时的采样顺序从小到大依次扫描ADC通道,而注入组的优先级高于规则组,当注入组转换触发时就打断规则组的扫描而执行注入组的通道扫描,具体流程类似于中断中的抢占。本次ADC的转换仅仅使用到一个端口,这些不用考虑,但是在多通道AD/DA采集时,规则组和注入组要根据实际情况进行配置。
注意:配置通道的规则组和注入组是一定要在使能ADC转换之前的。
完成了初始化后,剩下的就简单了,只要获得ADC处理后的数字量,在转换成整形变量,就可以通过串口发送接收了,如下:
//直接获得当前ADC转换后的值,转换并输出,CPU参与传送 ADValue = ADC_GetConversionValue(ADC1); Precent = (ADValue*100/0x1000); Voltage = Precent*33; printf("\r\n\n ADCConvertedValue is 0x%x, Percent is %d%%, voltage is %d.%d%dV",ADValue,Precent,Voltage/1000,(Voltage%1000)/100,(Voltage%100)/10); printf("\r\n ADC output");ARM_DELAY(2000000);
注意:使用了printf函数作为输入输出时,包含头文件#include ”stdio.h” ;Target下要选择use MicroLib,否则是不会有输出的(串口章节已经说明,重要)。
如此便实现了电位记电压的采集和输出,不过这并不是结束,因为今天我们还要学习另一个同样用途广泛的外设-DMA模块。
首先我们要知道DMA是干什么的?DMA模块的主要作用是将内存或者外设中的数据自由移动,而不需要cpu的参与,同时通过存储指针的自偏移,实现大量数据的顺序存储(这一点在通讯领域具有重要意义)。和上面一样,学习DMA,肯定首先查询手册了:
、
从这上面我们可以得出,ADC1对应的传输通道为通道1,在了解下面的结构体后:
typedef struct {u32 DMA_PeripheralBaseAddr; //定义的外设基地址u32 DMA_MemoryBaseAddr; //定义的内存基地址u32 DMA_DIR; //外设作为数据传输的来源还是目的地u32 DMA_BufferSize; //DMA通道的 DMA缓存的大小,单位为数据单位u32 DMA_PeripheralInc; //外设地址寄存器递增或不变u32 DMA_MemoryInc; //内存地址寄存器递增或不变u32 DMA_PeripheralDataSize; //外设数据宽度u32 DMA_MemoryDataSize; //内存数据宽度u32 DMA_Mode; //DMA缓存工作方式u32 DMA_Priority; //DMA工作优先级u32 DMA_M2M; //DMA工作是内存到内存,还是外设到内存 } DMA_InitTypeDef;
我们可以得出以下结论:
定义uint16_t ADCConvertedValue; //接收内存地址
1.外设基地址为(uint32_t)&(ADC1->DR)
2.数据来源于ADC外设,传送地址为内存
3.工作在循环模式,且外设和内存地址都不自增
4.数据传输使用DMA1的通道1
按照上面的结构体依此配置DMA_InitStructrue的各项参数,初始化如下:
DMA_InitTypeDef DMA_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(DMA1_Channel1); //复位ADC1对应DMA通道DMA1_Channel1 DMA_InitStructure.DMA_PeripheralBaseAddr =(uint32_t)&(ADC1->DR); //ADC1规则组转换值寄存器地址作为基地址 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADCConvertedValue; //数据传输至内存的基地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外设作为数据来源地 DMA_InitStructure.DMA_BufferSize = 1; //可增加地址的的长度 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不允许自增 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; //内存地址不允许自增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据为半字16bit DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //内存数据为半字16bit DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //DMA工作在循环模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA请求优先级高 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA是外设到内存传递 DMA_Init(DMA1_Channel1, &DMA_InitStructure); DMA_Cmd(DMA1_Channel1, ENABLE); /*DMA使能*/
同时在main函数中添加:
//将DMA从ADC处传送的数字量经过处理,转换并输出,DMA控制器用于传送 ADCConvertedValueLocal = ADCConvertedValue; Precent = (ADCConvertedValueLocal*100/0x1000); Voltage = Precent*33; printf("\r\n\n ADCConvertedValue is 0x%x, Percent is %d%%, voltage is %d.%d%dV",ADCConvertedValueLocal,Precent,Voltage/1000,(Voltage%1000)/100,(Voltage%100)/10); printf("\r\n ADC DMA output");
如此便实现了通过ADC通过DMA的传输。
实验现象如下图:
代码下载:http://files.cnblogs.com/files/zc110747/5.ADC-DMA.7z
以下来自外部资料及个人总结,希望对理解DMA模块有用处:
1.DMA传输将数据从一个地址空间复制到另外一个地址空间,这部分是由DMA控制器实现的,不需要依靠CPU的大量的数据采集传送,节省cpu资源。
2.DMA工作包含四个过程
DMA请求-〉DMA响应-〉DMA传输-〉DMA结束
3.DMA传送方式有以下三种
(1)停止CPU访内存
当外围器件有一批数据需要传送时,DMA给CPU发送停止信号,CPU停止访问内存,释放相关总线控制权,DMA获得总线控制权后开始传递数据,完成后将总线控制权交给CPU。一次DMA传送结束。
优点:控制简单,用于速率很高的组传送
缺点:内存的效能没有发挥,一部分时间内存处于空闲状态。这是因为DMA传送阶段有很多时间是在读取外设的数据,总线一段时间肯定是空闲的,而这部分时间足够CPU进行内存的访问。
(2)周期挪用;(ADC转换采用的正是这种方式)
当I/O设备没有DMA请求时,CPU按程序要求访问内存;一旦I/O设备有DMA请求,则由I/O设备挪用一个或几个内存周期。
(3)DMA与CPU交替访内存
转载于:https://www.cnblogs.com/zc110747/p/4738645.html
STM32学习笔记(七) ADC模数转换测电平(普通和DMA模式)相关推荐
- STM32学习笔记(七)---SysTick
STM32学习笔记(七)-SysTick 文章目录 STM32学习笔记(七)---SysTick 一.SysTick简介 二.SysTick功能框图 三.SysTick寄存器 四.SYSTICK使用 ...
- STM32学习笔记一一ADC
前言: 为了方便查看博客,特意申请了一个公众号,附上二维码,有兴趣的朋友可以关注,和我一起讨论学习,一起享受技术,一起成长. github:my github 注:博客所涉及的关于 stm32 的代码 ...
- STM32学习笔记(13)——模数转换ADC
这个月一直忙于准备考试,已经考完一半科目了,偷闲写了这篇文章.因为还没考完试,估计还得咕一段时间了. STM32学习笔记(13)--模数转换ADC 第一部分:ADC功能框图 一.输入电压 二.输入通道 ...
- STM32学习笔记(八)丨ADC模数转换器(ADC单、双通道转换)
本篇文章包含的内容 一.ADC 模数转换器 1.1 ADC简介 1.2 逐次逼近型ADC工作原理 1.3 STM32中的ADC基本结构 1.4 STM32中ADC的输入通道 1.5 STM32中的AD ...
- STM32学习笔记(三)丨中断系统丨EXTI外部中断(对射式红外传感器计次、旋转编码器计次)
本篇文章包含的内容 一.中断系统 1.1 中断的定义 1.2 中断优先级 1.3 中断的嵌套 1.4 STM32中的中断系统 1.4.1 STM32的中断资源 1.4.2 嵌套中断向量控制器 NVIC ...
- 《STM32学习笔记》4——核心功能电路与编程(下)
接上文,文中的图片,大多数来自视频的截图(来自洋桃电子). 欢迎大家批评指正! STM32学习笔记-专栏 文章目录 一.蜂鸣器驱动 1.蜂鸣器介绍 2.蜂鸣器电路 3.蜂鸣器程序 二. MIDI 音乐 ...
- STM32学习笔记(六)丨TIM定时器及其应用(输入捕获丨测量PWM波形的频率和占空比)
本篇文章包含的内容 一.输入捕获 1.1 输入捕获简介 1.2 输入捕获通道的工作原理 1.3 输入捕获的主从触发模式 1.4 输入捕获和PWMI结构 二.频率的测量方法 2.1 测频法 2.2 测周 ...
- STM32学习笔记(10)——高级定时器TIM
前排提示:本笔记参考了野火PPT的大部分内容.本人初学定时器,倍感冗杂,有错烦请指出,谢谢! STM32学习笔记(10)--高级定时器TIM 一.时钟源 1. 内部时钟源 2. 外部时钟模式 1 (1 ...
- STM32F407霸天虎HAL库学习笔记——使用ADC采集MQ135的数据并通过OLED显示
STM32F407霸天虎HAL库学习笔记--使用ADC采集MQ135的数据并通过OLED显示 一.软件准备 二.硬件准备 三.CubeMX配置 四.Keil MQ135.c MQ135.h main函 ...
最新文章
- 第三周项目四-穷举法解决组合问题
- Consul入门04 - Consul集群 1
- 澎思科技新出行人再识别(ReID)算法,刷新三大数据集最高记录
- 【JAVA】java 堆溢出分析
- Android如何使用WebView访问https的网站
- numpy将所有数据变为0和1_PyTorch 学习笔记(二):张量、变量、数据集的读取、模组、优化、模型的保存和加载...
- apache支持.htaccess
- 想做个磁力链搜索引擎 1
- IDEA插件系列(81):Shifter插件——字符串操作
- CentOS 7 安全加固、检测、审计
- 文件或目录损坏且无法读取 删除不了 错误0x80070091:目录不是空的 解决方案
- 【C语言】基础练习题
- Minitab 安装教程
- virtualbox TensorFlow 缺失avx指令集
- 在ABAQUS中使用修正DPC模型
- Google App Engine技术架构资料大盘点
- IPv6之初使用,及IPv4与IPv6的socket通信
- AE484 3D大气电影风格LOGO视频片头爆炸烟雾粒子碎片特效动画制作ae模板
- [Go实战]CGO 入门系列-手把手教程1
- springboot论坛交流小程序毕业设计毕设作品开题报告开题答辩PPT
热门文章
- python与excel的应用-Python利用pandas处理Excel数据的应用
- 精通python能干嘛-很多人说精通Python?对于精通我们存在哪些误区
- python主要运用于-python主要用于
- python绘制3d图-使用python绘制3d的图形
- python全套视频教程下载-老男孩python全套视频教程百度云资源下载
- python培训班一般多少钱-报个python培训班多少钱
- 报班学python到底怎么样-你们都是怎么学 Python 的?
- python与php8-Python比php发展快的十大理由
- arcgis python-ArcGIS Python 资源收藏
- python哪本好-最好的Python入门教材是哪本?