你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32使用HAL库实现ADC单通道转换

[复制链接]
STMCU小助手 发布时间:2022-2-9 22:16
  STM32的ADC转换还是很强大的,它具有多个通道选择,这里我就不细说,不了解的可以自行百度,这里只是选取单通道,实现ADC转换。在文章开始之前,我说一下数据左对齐跟右对齐的差别,以前一直糊里糊涂的,记录下来以免以后自己忘记。12位二进制最大值为 0x0FFF 左对齐操作后的结果是 0xFFF0,右对齐后还是0x0FFF。反过来看 ,若寄存器里左对齐的数据值X (相当于实际数据*16,所以左对齐转换的值要/16才是实际的值),则X>>4才是实际的数据。而右对齐,则是数据保持不变,采集到多少就多少。至于是按左对齐保存到寄存器还是按照右对齐,就看你的配置里如何选了。
  好了,下面就开始说明怎么用STM32CUBEMX实现ADC单通道转换吧。
利用中断模式
1、配置ADC引脚
  
1433771-20181129153031741-539968200.png
- B* c* m# `, _" @: ~
2、开定时跟串口,定时器用来定时打开ADC转换,这样可以达到1S内控制ADC转换次数的目的,不过有个限制,这里样子控制ADC转换次数的话,如果采样次数多,配置ADC采样速度时一定要够  快,正常配置ADC的采样频率可以通过改变其采样速度来设置的,这里我是为了方便处理,就直接用定时器去开启了;而串口则是打印转换后的电压用的。
* t9 @8 K) S3 E9 M" f
1433771-20181129153526549-874517231.png
  
3、配置时钟
  
1433771-20181129153601155-276127748.png
1 ~3 k" J. V2 G: g! G/ h  s0 D
4、配置ADC设置
  `
1433771-20181129153907972-233216619.png
3 i1 q1 [3 y, G+ C. Q
5、开启中断模式
  
1433771-20181129154113690-349965448.png

' z* K! W) d# Q! l' i  u( t
6、串口配置默认即可

* q7 |/ C4 w' {: \; |8 G
1433771-20181129154239218-469781284.png
  
7、定时器配置,定时器配置的是进入定时器中断的频率,定时时间可以根据这个频率换算出来,这里定时器的频率 = 72M / 72 /1000 =1000Hz,所以定时时间为 T = 1S/f = 1S/1000 = 1ms,所以我这里配置定时为1ms。

* G0 p: u' h  q; ~' F
1433771-20181129154729430-591115694.png
  
8、基本配置我们完成了,现在我们生成工程用KEIL5打开
4 ^" \3 U5 @4 o; ~  r, ?' m
1433771-20181129154852211-1292340288.png
  
9、打开工程,我们现在进入代码部分
  这里我们只需要重写定时器中断回调函数跟,ADC转换回调中断函数即可。在main文件里添加这下面这两个函数

( w8 q# A4 S; ~' }
  1. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)    //定时器中断回调
      p3 |) L# f" _# M
  2. {6 L+ z  o1 x1 x' |/ U" r" a
  3.     HAL_ADC_Start_IT(&hadc1); //定时器中断里面开启ADC中断转换,1ms开启一次采集   
    5 j# {+ V8 \3 o
  4. }
    0 A4 T; @4 H3 }5 D1 l6 g5 h

  5. 7 P7 ]+ d) X- o
  6. void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)    //ADC转换完成回调
    ; x: `$ w7 x; z0 O7 ?
  7. {0 U% t/ N7 u. ]
  8.     HAL_ADC_Stop_IT(&hadc1);        //关闭ADC
    : D$ z- O' d% N" l1 T' q5 _
  9.     HAL_TIM_Base_Stop_IT(&htim3);    //关闭定时器9 L6 U. Z4 L& }* b- x. b, _
  10.     AD_Value=HAL_ADC_GetValue(&hadc1);  //获取ADC转换的值
    0 ^1 o' e* K/ j3 |8 J
  11.     Value_1=(float)(AD_Value*3.3/4096);     //ADC换算,这里参考电压3.3V,12位的ADC满量程为2^12=4096,转换出来的单位是V: v0 o& e0 g! x1 e3 `9 H) ^
  12.     printf("%.4f\r\n",Value_2[j-10000]);     //串口打印信息
    * a; P' T! b9 w9 V4 N% F
  13.     HAL_TIM_Base_Start_IT(&htim3);       //开启定时器& l  ]4 L/ G2 M  @* Q9 Y( R% `
  14. }
复制代码
  到这里就完成单通道ADC中断转换的所有步骤啦,通过串口助手实测转换结果误差为0.0008v。
7 N6 v6 T% p* Y7 K! l, m% K; S# R
不使用中断模式
  不使用中断模式的情况下跟使用中断的类似的,首先配置的过程中不需要开启中断,至于定时器开不开看个人需要,想利用定时器定时采集的可以开,不想的不用开,其他的配置一样。生成代码后,在main文件的main函数中的while循环里添加下面代码:
  1. /* USER CODE BEGIN 3 */
    6 z9 {) U. E. i" x8 y; d
  2.         for(char n=0;n<22;n++)
    $ [* i. p1 R: M& S
  3.         {  //取22个值做滤波用* O& I0 q  K/ f5 p
  4.             HAL_ADC_Start(&hadc2);
    0 l- r6 q* y7 s& ~
  5.             HAL_ADC_PollForConversion(&hadc2, 10);    //等待转换完成,第二个参数表示超时时间,单位ms        # s! x$ t# \1 _" s/ B1 E
  6.             if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc2), HAL_ADC_STATE_REG_EOC))% Z* d. w: u0 I. p+ r
  7.             {
    - k+ y- D4 _# G" d) S2 I+ S4 y
  8.                 Value[n]=HAL_ADC_GetValue(&hadc2);0 r8 u+ Z% ]" }$ Q
  9.                 AD_Value += Value[n];0 z; I! t& T6 T! q$ p
  10.             }                7 R& q9 @  S0 a  u3 `8 V
  11.         }
    ) v( O$ E* z8 ]
  12.         max=Value[0];
    9 p3 d! W$ r5 b/ t. r6 Z
  13.         min=Value[0];' f7 w* U# e2 `5 f7 y
  14.         for(char n=0;n<22;n++)//取最大值、最小值
    . |+ v$ ?0 K6 D" \8 ~" I, W/ Q
  15.         {) x: i2 f. ]! o( H9 A! B/ T
  16.             max=(Value[n]<max)?max:Value[n];   
    3 u) ?7 H  e" d8 W4 H( c
  17.             min=(min<Value[n])?min:Value[n];
    $ n! a$ R4 U5 ?) D- x$ i  a
  18.         }   
    " h( }& q; w( ?; K( c! X
  19.         printf("PC0 ADC : %.4f \r\n",(float)((AD_Value -max-min)/20)*(3.1/4096));        ' V% @0 i6 m0 r
  20.         AD_tr=(float)((AD_Value -max-min)/20)*(3.1/4096);    //这里我做了个去掉最大最小值后,取均值的软件滤波   ; u" n. q; V; E  ^: M
  21.         AD_Value=0;
复制代码
% k8 L% t" ^- r4 B/ v" t
  这里面的一些变量就你们自己去定义了,我就不列出来了,实测误差在0.001v以内。
& d2 Z3 E7 b0 L, M/ ^
补充注意事项:
  1、ADC初始化后要进行校准,使用下面函数校准,可以放在ADC初始化函数后面校准
  1. HAL_ADCEx_Calibration_Start(&hadc2);    //AD校准
复制代码

" a5 f* z- ^# S
  2、传入ADC的电压不可以超过3.3V,就是不可以超过你的参考电压,不然结果不准,还有可能烧坏ADC引脚
! \0 A) d: e3 a( D, O
使用DMA模式
 再次写写stm32cubemx中AD采集的问题,这次不用while里面的查询,也不用中断采样了,直接用DMA
6 B# G; M  E: j9 t1 a  f" Y先说下用DMA的好处:无论是中断采样还是查询采样,都需要在主程序中占用好多时间出来,嗯,你可以这样理解
2 P" {0 e/ N  |9 @% B那种采样都需要调用HAL_ADC_GetValue()这个函数,,,就是要取得转换后的值,中断还好点,要是查询的话,有可能会丢失数据啊. 用dma就可以避免了
) g8 A' n/ e/ i( ^DMA用的事总线时间,无线cpu干预,额,这种说法貌似有点问题.管它呢2 e+ X; A. i% p3 t2 x) R$ P
在AD转换结束的时候自动连接你准备存取的变量的地址,数据一步到位.额,省了多少事..- g* Z) n; q9 A0 G6 i+ W7 Y
使用stm32cubemx对AD的配置: I6 [# f( d4 {/ j
, I7 q/ _7 H) ?1 R$ p
" \. v, e5 D9 o* S1 s  H" S* i' f

. V: E  R* v  F3 ^; |9 W9 ]" Z然后对她的DMA配置,并开启DMA的中断
7 n6 O  f/ I. T  g! ~
3 X! j. H" p8 Z5 M
然后生成代码吧
5 C6 [, ?2 `3 O& [, R+ N$ U$ J打开main.c文件,在这个地方添加代码
  1. /[i] USER CODE BEGIN 0 [/i]/
    % V2 k0 d  I4 o
  2. __IO uint16_t uhADCxConvertedValue = 0;
    2 t+ E. W0 J* Z* d7 y2 p
  3. /[i] USER CODE END 0 [/i]/
复制代码

9 w/ F3 C4 a8 V; Q! N
在main()函数里添加
  1.   /[i] USER CODE BEGIN 2 [/i]/* r3 Q$ t' g9 X& ~1 t
  2. HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&uhADCxConvertedValue, 1);# `3 [! l7 E' d3 }& E6 @( w4 t

  3. ( q0 a8 p4 Y4 k& Z7 b9 _
  4.   /[i] USER CODE END 2 [/i]/
复制代码
& I7 r0 d% A. s% s
意思是开启dma传输,传送一个字的数据到uhADCxConvertedValue这个变量里面
# k) s. U- }9 b3 m! f8 y+ u$ m然后再文件的末尾处添加
  1. /[i] USER CODE BEGIN 4 [/i]/
    % w* V! \- e4 `$ W7 P% C" [. u
  2. void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle)! R0 u8 p  G2 a- M
  3. {7 S8 p9 C* @  t; ?: e4 p
  4.   /[i] Turn LED1 on: Transfer process is correct [/i]/
    % I. i8 J3 f* B
  5. // BSP_LED_On(LED1);1 n3 `0 n, I: Q  \
  6.     HAL_GPIO_WritePin (GPIOF,GPIO_PIN_6,GPIO_PIN_SET );6 p& z' M2 l( X# m( `! m
  7. }
    & T8 ]) m$ K0 J8 R3 s
  8. /[i] USER CODE END 4 [/i]/
复制代码
% b$ ?5 T, D+ A* x6 c" V/ k9 N9 N
意思是AD转换完成调用这个函数,函数里使能led% q) P: q; w& j8 Z# W" n" V$ I
也许,你会问,为毛是HAL_ADC_ConvCpltCallback()这个函数啊,这个函数不是当开启AD的中断的时候才调用的吗?+ D( f6 b2 K; B( J# r
嗯,对,这个函数是这样的,但是你仔细去分析下开启AD的DMA中断函数里面,就会发现这个函数也在啊
9 y* Q0 X$ U; i5 F  B如下图.进入HAL_ADC_Start_DMA函数里面,看到. d' T: k6 `* J

6 b4 Z. W& ]  e. f

' `3 d5 [( x: Y: |* }% _! c5 ~  I

$ f5 H1 {6 I  U% r在进入到图中的ADC_DMAConvCplt函数里面看到8 E( L# e/ X. b+ f* W# g. [7 [

+ s+ L5 A) x( l3 W

7 P7 E, ^; _6 c" B4 z/ u
3 M# ~% f7 K  b6 v" x- [% ^& `& f
OK,疑问解决,
* `) X  {9 |2 _( }# Y6 ?以后用到AD就可以直接调用这个CALL了,不要纠结了.

5 R9 u  S3 ?1 }5 t/ G; ]
收藏 评论0 发布时间:2022-2-9 22:16

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版