嵌入式系统在微控制领域(温度,湿度,压力检测,四轴飞行器)中占据着重要地位,这些功能的实现是由微处理器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 = ; //ADC通道数目为1
ADC_Init(ADC1, &ADC_InitStructure); //指定ADC转换的通道和转换周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, , ADC_SampleTime_55Cycles5);

#ifdef USE_ADC1_DMA_PER
ADC_DMACmd(ADC1, ENABLE); //ADC1 DMA请求使能
#endif ADC_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*/0x1000);
Voltage = Precent*;
printf("\r\n\n ADCConvertedValue is 0x%x, Percent is %d%%, voltage is %d.%d%dV",
ADValue,Precent,Voltage/,(Voltage%)/,(Voltage%)/);
printf("\r\n ADC output"); ARM_DELAY();

注意:使用了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 = ; //可增加地址的的长度
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*/0x1000);
Voltage = Precent*;
printf("\r\n\n ADCConvertedValue is 0x%x, Percent is %d%%, voltage is %d.%d%dV",
ADCConvertedValueLocal,Precent,Voltage/,(Voltage%)/,(Voltage%)/);
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交替访内存

STM32学习笔记(七) ADC模数转换测电平(普通和DMA模式)的更多相关文章

  1. STM32学习笔记——OLED屏

    STM32学习笔记--OLED屏 OLED屏的特点: 1.  模块有单色和双色可选,单色为纯蓝色,双色为黄蓝双色(本人选用双色): 2.  显示尺寸为0.96寸 3.  分辨率为128*64 4.   ...

  2. STM32学习笔记——点亮LED

    STM32学习笔记——点亮LED 本人学习STM32是直接通过操作stm32的寄存器,使用的开发板是野火ISO-V2版本: 先简单的介绍一下stm32的GPIO: stm32的GPIO有多种模式: 1 ...

  3. stm32学习笔记——外部中断的使用

    stm32学习笔记——外部中断的使用 基本概念 stm32中,每一个GPIO都可以触发一个外部中断,但是,GPIO的中断是以组为一个单位的,同组间的外部中断同一时间只能使用一个.比如说,PA0,PB0 ...

  4. STM32学习笔记(四)——串口控制LED(中断方式)

    目录: 一.时钟使能,包括GPIO的时钟和串口的时钟使能 二.设置引脚复用映射 三.GPIO的初始化配置,注意要设置为复用模式 四.串口参数初始化配置 五.中断分组和中断优先级配置 六.设置串口中断类 ...

  5. stm32学习笔记----双串口同时打开时的printf()问题

    stm32学习笔记----双串口同时打开时的printf()问题 最近因为要使用串口2外接PN532芯片实现通信,另一方面,要使用串口1来将一些提示信息输出到上位机,于是重定义了printf(),使其 ...

  6. (转)Qt Model/View 学习笔记 (七)——Delegate类

    Qt Model/View 学习笔记 (七) Delegate  类 概念 与MVC模式不同,model/view结构没有用于与用户交互的完全独立的组件.一般来讲, view负责把数据展示 给用户,也 ...

  7. Learning ROS for Robotics Programming Second Edition学习笔记(七) indigo PCL xtion pro live

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS forRobotics Pro ...

  8. Typescript 学习笔记七:泛型

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  9. python3.4学习笔记(七) 学习网站博客推荐

    python3.4学习笔记(七) 学习网站博客推荐 深入 Python 3http://sebug.net/paper/books/dive-into-python3/<深入 Python 3& ...

随机推荐

  1. ios数据库SQLite实现

    SQLite嵌入式数据库 1.支持时间,不需要配置,不需要安装,不需要管理员; 2.支持大部分SQL92; 3.完整的数据库保存在磁盘上面的一个文件,同一个数据库文件可以在不同机器上面使用.最大支持数 ...

  2. iOS开发网络篇—简单介绍ASI框架的使用

    iOS开发网络篇—简单介绍ASI框架的使用 说明:本文主要介绍网络编程中常用框架ASI的简单使用. 一.ASI简单介绍 ASI:全称是ASIHTTPRequest,外号“HTTP终结者”,功能十分强大 ...

  3. svn更新报错:svn unable to connect to a repository at url

    出现错误:unable to connect to a repository at url 解决办法1. 右键点击本地副本,TortoiseSVN -> Settings -> Saved ...

  4. C# 中的多线程(转载)

    关于多线程的系列,翻译自国外大牛的文章,值得推荐 原文地址:https://blog.gkarch.com/topic/threading.html

  5. Oracle EBS R12 电子技术参考手册 - eTRM (电子文档)

    http://etrm.oracle.com/pls/etrm/etrm_search.search

  6. 一般处理程序返回json

    一般处理程序:    public void ProcessRequest(HttpContext context)         {             string action = con ...

  7. UNIX操作系统和Linux操作系统介绍

    UNIX操作系统(尤尼斯),是一个强大的多用户.多任务操作系统,支持多种处理器架构,按照操作系统的分类,属于分时操作系统,最早由KenThompson.DennisRitchie和DouglasMcI ...

  8. AJAX-----03远古时期的ajax

    用iframe方法实现 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&qu ...

  9. PoE以太网远程供电

    每个以太网口向下挂设备提供的最大功率为 15.4W 通过3/5 类双绞线的信号线(1.3.2.6)同时传递数据和电流

  10. 搭建自己本地yum源

    1.挂载系统光盘(注:medi下的cdrom是我自己创建的,可以挂载在任意目录) [root@liutao ~]# mount /dev/cdrom /media/cdrom/ 2.修改yum配置文件 ...