模拟 模拟/ 数字转换(ADC)

来源:互联网 发布:苹果电脑用于软件开发 编辑:程序博客网 时间:2024/05/17 03:30

一,ADC介绍

12位ADC是一种逐次逼近型模拟数字转换器。它有多达18个通道,可测量16个外部和2个内部
信号源。各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右
对齐方式存储在16位数据寄存器中。
模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。
ADC的输入时钟不得超过14MHz,它是由PCLK2经分频产生。


1、1MHz转换速率、12位转换结果(2^12=4096 )
          STM32F103系列:在56MHz时转换时间为:1μs

                                            在72MHz时转换时间为:1.17μs
2、转换范围:0~3.6V  (3.6v---->当你需要将采集的数据用电压来显示的话:设你采集的数据为:x[0~4095],此时的计算公式就为:(x / 4096) * 3.6))
3、ADC供电要求:2.4V~3.6 V
4、ADC输入范围:VREF-≤ VIN ≤VREF+

5.只有ADC1有DMA功能


二,ADC功能

1.当第一次设置ADON位时,它将ADC从断电状态下唤醒。
2.ADC上电延迟一段时间后(t STAB ),再次设置ADON位时开始进行转换.


通道选择

●  规则组由多达16个转换组成。规则通道和它们的转换顺序在ADC_SQRx寄存器中选择。规
则组中转换的总数应写入ADC_SQR1寄存器的L[3:0]位中。


●  注入组由多达4个转换组成。注入通道和它们的转换顺序在ADC_JSQR寄存器中选择。注入
组里的转换总数目应写入ADC_JSQR寄存器的L[1:0]位中


对于规则组与注入组的理解:

STM32的ADC可以对一组指定的通道,按照指定的顺序,逐个转换这组通道,转换结束后,再从头循环;这指定的通道组就称为规则组。但是实际应用中,有可能需要临时中断规则组的转换,对某些通道进行转换,这些需要中断规则组而进行转换的通道组,就称为注入组。


转换模式

1.单次转换模式

单次转换模式下,ADC只执行一次转换。该模式既可通过设置ADC_CR2寄存器的ADON位(只
适用于规则通道)启动也可通过外部触发启动(适用于规则通道或注入通道),这时CONT位为0。
一旦选择通道的转换完成:


● 如果一个规则通道被转换:
─  转换数据被储存在16位ADC_DR寄存器中
 EOC(转换结束)标志被设置
─  如果设置了EOCIE,则产生中断。


● 如果一个注入通道被转换:
─  转换数据被储存在16位的ADC_DRJ1寄存器中
─  JEOC(注入转换结束)标志被设置
─  如果设置了JEOCIE位,则产生中断。

然后ADC停止。

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

引用某帖:

手册说了每一个规则通道转换完毕都置位EOC,那就是每转换一个通道都可以进中断喽,那这样的话,咱还用啥DMA啊,直接中断吧,好吧。就这么办,程序写好了,跟踪调试发现,中断虽然进了,但是只有第二组数据。为什么不行呢,手册中讲的好好的,怎么就不行了呢,带着这个疑问,楼主开始了探索之旅
首先继续不用DMA,在主函数中采用以下语句读取ADC数据。
    

while (1)    {                if((ADC1->SR)&0x02)//读取判断EOC位                {                        adcx = ADC_GetConversionValue(ADC1);        //返回最近一次ADC1规则组的转换结果                        Filter_Table[ Filter_Tab_Count++ ] = adcx;                }    }


还是只有最后一组数据。好吧,那这样好了:
    
while (1)    {                adcx = ADC_GetConversionValue(ADC1);        //返回最近一次ADC1规则组的转换结果                Filter_Table[ Filter_Tab_Count++ ] = adcx;    }
终于凑效了,可以在跟踪的时候,在不同时间停下的时候读到第一组和第二组数据了,这是为什么呢,这说明第一通道也是在转换的,只是在转换完毕的时候没有置位EOC罢了,当然也就不能触发ADC中断了。

看来又被手册忽悠了,多个通道用中断的方式是不行的。

PS:手册中的一个规则通道应该指的是一个规则通道组。

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

2.连续转换模式

在连续转换模式中,当前面ADC转换一结束马上就启动另一次转换。此模式可通过外部触发启
动或通过设置ADC_CR2寄存器上的ADON位启动,此时CONT位是1。


每个转换后:
● 如果一个规则通道被转换:
─  转换数据被储存在16位的ADC_DR寄存器中
─  EOC(转换结束)标志被设置
─  如果设置了EOCIE,则产生中断。


● 如果一个注入通道被转换:
─  转换数据被储存在16位的ADC_DRJ1寄存器中
─  JEOC(注入转换结束)标志被设置
─  如果设置了JEOCIE位,则产生中断。


转换模式的流程:

用ADC1,Regular通道的顺序为Ch0,Ch1,Ch2,Ch3,启动Scan模式


在单次转换模式下:
启动ADC1,则
1. 开始转换Ch0
2. 转换完成后自动开始转换Ch1
3. 转换完成后自动开始转换Ch2
4. 转换完成后自动开始转换Ch3
5. 转换完成后停止,等待ADC的下一次启动。下一次ADC启动从第一步开始


在连续转换模式下:
启动ADC1,则
1. 开始转换Ch0
2. 转换完成后自动开始转换Ch1
3. 转换完成后自动开始转换Ch2
4. 转换完成后自动开始转换Ch3
5. 转换完成后回到第一步
如果没启动Sacn模式则上述过程中没有2、3、4这三个步骤

3.时序图

如下图所示,ADC在开始精确转换前需要一个稳定时间t STAB 。在开始ADC转换和14个时钟周期
后,EOC标志被设置,16位ADC数据寄存器包含转换的结果。


4.扫描模式


EOC置位后DMA才来搬运数据,那么岂不是要丢好多数据!当然中文手册也讲了,以英文手册为准,既然有疑问,那咱翻翻英文手册好了


手册中讲到,在ADC_DR寄存器每次更新后,DMA才会搬运数据。总结:ADC在采用规则通道组采集的时候必须启用扫描模式,并且启用DMA传输支持。

5.注入通道管理



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

ADC的规则通道和注入通道混合使用

之前完成了规则通道DMA的数据传输了,不过平时在使用ADC的时候可能就会遇到很多情况,不可能就这样简单的按规则通道来采样,DMA存储,使用数据的;可能有时候会需要立刻采样,那样我们就需要利用到注入通道了。文档关于注入通道的解释:

  • 1      利用外部触发或通过设置ADC_CR2寄存器的ADON位,启动一组规则通道的转换。
  • 2      如果在规则通道转换期间产生一外部注入触发,当前转换被复位,注入通道序列被以单次扫描方式进行转换。
  • 3      然后,恢复上次被中断的规则组通道转换。如果在注入转换期间产生一规则事件,注入转换不会被中断,但是规则序列将在注入序列结束后被执行。

将变阻器的那路ADC设置为注入通道:

        ADC_InjectedSequencerLengthConfig(ADC1, 1);\\设置注入通道长度

  1. ADC_InjectedChannelConfig(ADC1,ADC_Channel_10,1,ADC_SampleTime_7Cycles5);\\配置注入通道
  2. ADC_SoftwareStartInjectedConvCmd(ADC1, ENABLE);\\开始注入通道数据采样和转换
复制代码
  开始之后,延迟足够的时间,让ADC采样和转换完成。
     用ADC_GetInjectedConversionValue(ADC1,ADC_InjectedChannel_1);读取注入通道1的数据,结果发现数据一直不变,那肯定是哪里设置出错了,找了下别人的设置,并做了一些尝试,发现了原来是设置的问题,注入采样的触发方式没有设置:
  1. ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_None);
复制代码
这个函数设置注入方式使用软件触发方式,设置完之后用开始采样和读取数据函数,就能采到正确的数据。

     上面的例子使用触发注入完成的,下面又尝试了自动注入。这样每次进行规则通道采样时,也会顺便把注入通道也进行采样了,而启动注入通道采样则不会对规则通道进行采样。
如果设置了 JAUTO 位,在规则组通道之后,注入组通道被自动转换。这可以用来转换在 ADC_SQRx 和 ADC_JSQR 寄存器中设置的多至 20 个转换序列。
          还有在规则通道使用DMA数据传输,且使用注入通道采样时,不知道会不会对数据有影响?
——查了下文档,只有在规则通道的转换结束时才产生 DMA 请求,并将转换的数据从 ADC_DR 寄存器传输到用户指定的目的地址,还有注入方式转换后数据存储到 ADC_DRJx寄存器和规则方式转换后数据存储在ADC_DR寄存器中。
  在注入通道和规则通道的混合使用中,我花了不少时间去找正确的设置,问题是不知道哪些库函数是必要的,哪些是非必要的,后来对着例子尝试之后才知道。后面我还想了解下具体的寄存器设置,看了几个初始化的函数,发现其实很多设置都是对ADC_CR1的设置,有不少不明白的看了寄存器就知道了,看来函数的使用还是要和寄存器对应的位结合起来,这样才能理解的透彻点。
  下面是我整个代码的设置,其他设置和上篇例子一样,只改了ADC设置:
  1. static void Protect_AdcInit(void)
  2. {
  3.     ADC_InitTypeDef ADC_InitStructure;
  4.     
  5.     ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
  6.     ADC_InitStructure.ADC_ScanConvMode = ENABLE;
  7.     ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
  8.     ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//软件触发
  9.     ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  10.     ADC_InitStructure.ADC_NbrOfChannel = 2;//规则通道的数量
  11.     ADC_Init(ADC1, &ADC_InitStructure);//这个大部分是初始化规则通道的
  12.     
  13.     ADC_TampSensorVrefintCmd(ENABLE);//使能温度传感器和内部参考电压通道
  14.     ADC_RegularChannelConfig(ADC1,ADC_Channel_TampSensor,1,ADC_SampleTime_239Cycles5);
  15.     ADC_RegularChannelConfig(ADC1,ADC_Channel_Vrefint,2,ADC_SampleTime_239Cycles5);
  16.     
  17.     ADC_InjectedSequencerLengthConfig(ADC1, 1);
  18.      ADC_InjectedChannelConfig(ADC1,ADC_Channel_10,1,ADC_SampleTime_7Cycles5);
  19.     ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_None);//设置规则通道软件触发
  20.     /* Enable automatic injected conversion start after regular one */
  21.   //  ADC_AutoInjectedConvCmd(ADC1, ENABLE);

  22.     
  23.     ADC_DMACmd(ADC1, ENABLE);
  24.     /* Enable ADC1 external trigger */
  25.     ADC_ExternalTrigConvCmd(ADC1, DISABLE);
  26.     ADC_ExternalTrigInjectedConvCmd(ADC1, DISABLE);

  27.     ADC_Cmd(ADC1, ENABLE);

  28.     ADC_ResetCalibration(ADC1);

  29.     while(ADC_GetResetCalibrationStatus(ADC1));

  30.     ADC_StartCalibration(ADC1);

  31.     while(ADC_GetCalibrationStatus(ADC1));

  32. }
复制代码

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

间断模式
规则组 
此模式通过设置ADC_CR1寄存器上的DISCEN位激活。它可以用来执行一个短序列的n次转换
(n<=8),此转换是ADC_SQRx寄存器所选择的转换序列的一部分。数值n由ADC_CR1寄存器的
DISCNUM[2:0]位给出。
一个外部触发信号可以启动ADC_SQRx寄存器中描述的下一轮n次转换,直到此序列所有的转
换完成为止。总的序列长度由ADC_SQR1寄存器的L[3:0]定义。


举例:
n=3,被转换的通道 = 0、1、2、3、6、7、9、10
第一次触发:转换的序列为 0、1、2
第二次触发:转换的序列为 3、6、7
第三次触发:转换的序列为 9、10,并产生EOC事件
第四次触发:转换的序列 0、1、2


注意: 当以间断模式转换一个规则组时,转换序列结束后不自动从头开始。
当所有子组被转换完成,下一次触发启动第一个子组的转换。在上面的例子中,第四次触发重
新转换第一子组的通道 0 、 1 和 2 。
注入组 
此模式通过设置ADC_CR1寄存器的JDISCEN位激活。在一个外部触发事件后,该模式按通道
顺序逐个转换ADC_JSQR寄存器中选择的序列。
一个外部触发信号可以启动ADC_JSQR寄存器选择的下一个通道序列的转换,直到序列中所有
的转换完成为止。总的序列长度由ADC_JSQR寄存器的JL[1:0]位定义。


例子:
n=1,被转换的通道 = 1、2、3
第一次触发:通道1被转换
第二次触发:通道2被转换
第三次触发:通道3被转换,并且产生EOC和JEOC事件
第四次触发:通道1被转换


注意:

1.  当完成所有注入通道转换,下个触发启动第 1 个注入通道的转换。在上述例子中,第四个
触发重新转换第 1 个注入通道 1 。
2 . 不能同时使用自动注入和间断模式。

3  .必须避免同时为规则和注入组设置间断模式。间断模式只能作用于一组转换.


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

可编程的通道采样时间 
ADC使用若干个ADC_CLK周期对输入电压采样,采样周期数目可以通过ADC_SMPR1和
ADC_SMPR2寄存器中的SMP[2:0]位更改。每个通道可以分别用不同的时间采样。
总转换时间如下计算:
T CONV = 采样时间+ 12.5个周期

例如:
当ADCCLK=14MHz,采样时间为1.5周期
T CONV = 1.5 + 12.5 = 14周期 = 1μs


DMA 请求

只有 ADC1 和 ADC3 拥有 DMA 功能。由 ADC2 转化的数据可以通过双 ADC 模式,利用 ADC1 的
DMA 功能传输。


双ADC 模式

未完待续.......................................


剩余的部分可以去参考手册,双ADC的代码还没有整理,日后贴出............