嵌入式系统在微控制领域(温度,湿度,压力检测,四轴飞行器)中占据着重要地位,这些功能的实现是由微处理器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模式)相关推荐

  1. STM32学习笔记(七)---SysTick

    STM32学习笔记(七)-SysTick 文章目录 STM32学习笔记(七)---SysTick 一.SysTick简介 二.SysTick功能框图 三.SysTick寄存器 四.SYSTICK使用 ...

  2. STM32学习笔记一一ADC

    前言: 为了方便查看博客,特意申请了一个公众号,附上二维码,有兴趣的朋友可以关注,和我一起讨论学习,一起享受技术,一起成长. github:my github 注:博客所涉及的关于 stm32 的代码 ...

  3. STM32学习笔记(13)——模数转换ADC

    这个月一直忙于准备考试,已经考完一半科目了,偷闲写了这篇文章.因为还没考完试,估计还得咕一段时间了. STM32学习笔记(13)--模数转换ADC 第一部分:ADC功能框图 一.输入电压 二.输入通道 ...

  4. STM32学习笔记(八)丨ADC模数转换器(ADC单、双通道转换)

    本篇文章包含的内容 一.ADC 模数转换器 1.1 ADC简介 1.2 逐次逼近型ADC工作原理 1.3 STM32中的ADC基本结构 1.4 STM32中ADC的输入通道 1.5 STM32中的AD ...

  5. STM32学习笔记(三)丨中断系统丨EXTI外部中断(对射式红外传感器计次、旋转编码器计次)

    本篇文章包含的内容 一.中断系统 1.1 中断的定义 1.2 中断优先级 1.3 中断的嵌套 1.4 STM32中的中断系统 1.4.1 STM32的中断资源 1.4.2 嵌套中断向量控制器 NVIC ...

  6. 《STM32学习笔记》4——核心功能电路与编程(下)

    接上文,文中的图片,大多数来自视频的截图(来自洋桃电子). 欢迎大家批评指正! STM32学习笔记-专栏 文章目录 一.蜂鸣器驱动 1.蜂鸣器介绍 2.蜂鸣器电路 3.蜂鸣器程序 二. MIDI 音乐 ...

  7. STM32学习笔记(六)丨TIM定时器及其应用(输入捕获丨测量PWM波形的频率和占空比)

    本篇文章包含的内容 一.输入捕获 1.1 输入捕获简介 1.2 输入捕获通道的工作原理 1.3 输入捕获的主从触发模式 1.4 输入捕获和PWMI结构 二.频率的测量方法 2.1 测频法 2.2 测周 ...

  8. STM32学习笔记(10)——高级定时器TIM

    前排提示:本笔记参考了野火PPT的大部分内容.本人初学定时器,倍感冗杂,有错烦请指出,谢谢! STM32学习笔记(10)--高级定时器TIM 一.时钟源 1. 内部时钟源 2. 外部时钟模式 1 (1 ...

  9. STM32F407霸天虎HAL库学习笔记——使用ADC采集MQ135的数据并通过OLED显示

    STM32F407霸天虎HAL库学习笔记--使用ADC采集MQ135的数据并通过OLED显示 一.软件准备 二.硬件准备 三.CubeMX配置 四.Keil MQ135.c MQ135.h main函 ...

最新文章

  1. 第三周项目四-穷举法解决组合问题
  2. Consul入门04 - Consul集群 1
  3. 澎思科技新出行人再识别(ReID)算法,刷新三大数据集最高记录
  4. 【JAVA】java 堆溢出分析
  5. Android如何使用WebView访问https的网站
  6. numpy将所有数据变为0和1_PyTorch 学习笔记(二):张量、变量、数据集的读取、模组、优化、模型的保存和加载...
  7. apache支持.htaccess
  8. 想做个磁力链搜索引擎 1
  9. IDEA插件系列(81):Shifter插件——字符串操作
  10. CentOS 7 安全加固、检测、审计
  11. 文件或目录损坏且无法读取 删除不了 错误0x80070091:目录不是空的 解决方案
  12. 【C语言】基础练习题
  13. Minitab 安装教程
  14. virtualbox TensorFlow 缺失avx指令集
  15. 在ABAQUS中使用修正DPC模型
  16. Google App Engine技术架构资料大盘点
  17. IPv6之初使用,及IPv4与IPv6的socket通信
  18. AE484 3D大气电影风格LOGO视频片头爆炸烟雾粒子碎片特效动画制作ae模板
  19. [Go实战]CGO 入门系列-手把手教程1
  20. springboot论坛交流小程序毕业设计毕设作品开题报告开题答辩PPT

热门文章

  1. python与excel的应用-Python利用pandas处理Excel数据的应用
  2. 精通python能干嘛-很多人说精通Python?对于精通我们存在哪些误区
  3. python主要运用于-python主要用于
  4. python绘制3d图-使用python绘制3d的图形
  5. python全套视频教程下载-老男孩python全套视频教程百度云资源下载
  6. python培训班一般多少钱-报个python培训班多少钱
  7. 报班学python到底怎么样-你们都是怎么学 Python 的?
  8. python与php8-Python比php发展快的十大理由
  9. arcgis python-ArcGIS Python 资源收藏
  10. python哪本好-最好的Python入门教材是哪本?