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

【经验分享】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
. B8 {1 L' a; N
2、开定时跟串口,定时器用来定时打开ADC转换,这样可以达到1S内控制ADC转换次数的目的,不过有个限制,这里样子控制ADC转换次数的话,如果采样次数多,配置ADC采样速度时一定要够  快,正常配置ADC的采样频率可以通过改变其采样速度来设置的,这里我是为了方便处理,就直接用定时器去开启了;而串口则是打印转换后的电压用的。
: r; t$ v2 u# d5 l$ m
1433771-20181129153526549-874517231.png
  
3、配置时钟
  
1433771-20181129153601155-276127748.png

  E7 i! w+ y2 r# N6 H3 {
4、配置ADC设置
  `
1433771-20181129153907972-233216619.png
% \* \4 T+ i  `; ]
5、开启中断模式
  
1433771-20181129154113690-349965448.png
- M% v! `' l# h2 n9 O
6、串口配置默认即可
# ], X; W* r( S3 Z3 j
1433771-20181129154239218-469781284.png
  
7、定时器配置,定时器配置的是进入定时器中断的频率,定时时间可以根据这个频率换算出来,这里定时器的频率 = 72M / 72 /1000 =1000Hz,所以定时时间为 T = 1S/f = 1S/1000 = 1ms,所以我这里配置定时为1ms。
3 S0 x9 z: G2 V$ f& _
1433771-20181129154729430-591115694.png
  
8、基本配置我们完成了,现在我们生成工程用KEIL5打开
% L0 ^/ V5 K: r2 |! t7 x9 u
1433771-20181129154852211-1292340288.png
  
9、打开工程,我们现在进入代码部分
  这里我们只需要重写定时器中断回调函数跟,ADC转换回调中断函数即可。在main文件里添加这下面这两个函数
; p+ q$ W' P: W# ~. ~6 Q( u6 t
  1. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)    //定时器中断回调) c! e" L; t3 a  T; S
  2. {
    * I; F+ r4 c% v& Q+ y/ `
  3.     HAL_ADC_Start_IT(&hadc1); //定时器中断里面开启ADC中断转换,1ms开启一次采集    ) s/ u8 Q1 Z: o( S4 ~* U0 M
  4. }/ L" `8 i: g' v  k

  5. ( x5 [1 r( O' }% i; a# M, {
  6. void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)    //ADC转换完成回调* `9 ]' B, i. u9 o
  7. {  e  }* J' W" S- x2 R$ L
  8.     HAL_ADC_Stop_IT(&hadc1);        //关闭ADC
    7 C$ i7 L8 G  M" {+ ?/ q4 U8 _- D
  9.     HAL_TIM_Base_Stop_IT(&htim3);    //关闭定时器9 j" Z0 C5 @- X7 s
  10.     AD_Value=HAL_ADC_GetValue(&hadc1);  //获取ADC转换的值. g$ T6 X8 U; a7 I0 P0 e: F# U
  11.     Value_1=(float)(AD_Value*3.3/4096);     //ADC换算,这里参考电压3.3V,12位的ADC满量程为2^12=4096,转换出来的单位是V8 V$ N2 R' [% W
  12.     printf("%.4f\r\n",Value_2[j-10000]);     //串口打印信息
    5 _0 s/ B+ I  Q! i3 W" I8 s
  13.     HAL_TIM_Base_Start_IT(&htim3);       //开启定时器
    9 v* e: l  U4 E' h$ ~: [" V: l
  14. }
复制代码
  到这里就完成单通道ADC中断转换的所有步骤啦,通过串口助手实测转换结果误差为0.0008v。
/ P, _5 d( n: V/ Q6 `1 ]- ?5 c) K# p% m" M: k
不使用中断模式
  不使用中断模式的情况下跟使用中断的类似的,首先配置的过程中不需要开启中断,至于定时器开不开看个人需要,想利用定时器定时采集的可以开,不想的不用开,其他的配置一样。生成代码后,在main文件的main函数中的while循环里添加下面代码:
  1. /* USER CODE BEGIN 3 */! `/ T& |" r# P! U9 y
  2.         for(char n=0;n<22;n++)
    # U# G9 e, y# ~0 j% [
  3.         {  //取22个值做滤波用! r7 n, |( n( p& v5 l; G' N
  4.             HAL_ADC_Start(&hadc2);( @7 L6 _) N( s# J
  5.             HAL_ADC_PollForConversion(&hadc2, 10);    //等待转换完成,第二个参数表示超时时间,单位ms        ( F' u0 W) U4 t2 U3 p# n
  6.             if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc2), HAL_ADC_STATE_REG_EOC))1 \$ Q7 k" e: G  V: h1 q; ^3 o0 H
  7.             {
    0 b& s% V  D% ^$ ]4 W
  8.                 Value[n]=HAL_ADC_GetValue(&hadc2);
    . {7 L( ~0 |3 a3 ~$ X
  9.                 AD_Value += Value[n];
    7 H; r) m7 S6 T* ?2 s4 s
  10.             }                ( M1 d3 U; j4 V- o: F
  11.         }
    3 `* F4 ]1 l) @9 |& d
  12.         max=Value[0];
    2 P' D- [2 y0 W" c5 v
  13.         min=Value[0];& I# R. h$ ~0 m& V% V+ O
  14.         for(char n=0;n<22;n++)//取最大值、最小值
    * k, l9 b9 [1 S) R; p
  15.         {
      L! f% I. O( z: _: F7 n
  16.             max=(Value[n]<max)?max:Value[n];   
    - U- g( T% v$ D: [) u( |
  17.             min=(min<Value[n])?min:Value[n];' t2 D  _5 \/ a8 u% T* x
  18.         }   
    1 h9 U- Y' B, X  z8 s
  19.         printf("PC0 ADC : %.4f \r\n",(float)((AD_Value -max-min)/20)*(3.1/4096));        " J2 X' ~$ \6 O2 H
  20.         AD_tr=(float)((AD_Value -max-min)/20)*(3.1/4096);    //这里我做了个去掉最大最小值后,取均值的软件滤波   ' u+ @- J% E& J/ u1 o$ x4 h/ i
  21.         AD_Value=0;
复制代码
" n  k- c+ N$ C- }
  这里面的一些变量就你们自己去定义了,我就不列出来了,实测误差在0.001v以内。
/ @* h* q# i1 y  n% M
补充注意事项:
  1、ADC初始化后要进行校准,使用下面函数校准,可以放在ADC初始化函数后面校准
  1. HAL_ADCEx_Calibration_Start(&hadc2);    //AD校准
复制代码

# L( C: \7 f+ c3 c$ h) ?
  2、传入ADC的电压不可以超过3.3V,就是不可以超过你的参考电压,不然结果不准,还有可能烧坏ADC引脚
& u8 g: ?; D* g4 p8 S! u
使用DMA模式
 再次写写stm32cubemx中AD采集的问题,这次不用while里面的查询,也不用中断采样了,直接用DMA
5 L: Q8 o8 s# A+ ^先说下用DMA的好处:无论是中断采样还是查询采样,都需要在主程序中占用好多时间出来,嗯,你可以这样理解
$ P: d, n! v& A4 ?那种采样都需要调用HAL_ADC_GetValue()这个函数,,,就是要取得转换后的值,中断还好点,要是查询的话,有可能会丢失数据啊. 用dma就可以避免了8 @0 |6 x% C) X- U5 u
DMA用的事总线时间,无线cpu干预,额,这种说法貌似有点问题.管它呢/ A% X# q, A+ a5 d0 }# s
在AD转换结束的时候自动连接你准备存取的变量的地址,数据一步到位.额,省了多少事..6 f7 v# ]5 J% R  {5 O4 h! w$ J
使用stm32cubemx对AD的配置
7 F7 U& y; B5 u1 D* j: J6 |! @+ u, ^. X7 e$ M- D

. D8 |/ A% {/ b5 q4 Z
6 _3 b% R6 M6 m# m. `
然后对她的DMA配置,并开启DMA的中断

. [3 U% y1 L/ e) B# i& |
7 K! t; S  t" R& C8 R
然后生成代码吧# V3 S$ ~/ z% A
打开main.c文件,在这个地方添加代码
  1. /[i] USER CODE BEGIN 0 [/i]/
    & p, O" V" Y4 ~' O$ d: I
  2. __IO uint16_t uhADCxConvertedValue = 0;
    ' t5 r. X/ t+ Y( R
  3. /[i] USER CODE END 0 [/i]/
复制代码

4 u# K: O) s" c
在main()函数里添加
  1.   /[i] USER CODE BEGIN 2 [/i]/
    7 v# y) y8 J" x9 @/ {; b
  2. HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&uhADCxConvertedValue, 1);; b5 \% @# C  u, u
  3. # H" x" O5 ~9 u% _$ {) C+ u) t* h
  4.   /[i] USER CODE END 2 [/i]/
复制代码
2 u9 E+ y8 t% g
意思是开启dma传输,传送一个字的数据到uhADCxConvertedValue这个变量里面
+ f$ T9 {0 r! H- a- F! F& x然后再文件的末尾处添加
  1. /[i] USER CODE BEGIN 4 [/i]/
    & {, q/ G, F# W- s, B
  2. void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle)8 h& a2 E0 e) L3 E; i6 i" i
  3. {4 R2 K# `: j# ~8 y8 F6 n. t; @
  4.   /[i] Turn LED1 on: Transfer process is correct [/i]/% N! t( B/ r+ q6 p$ E
  5. // BSP_LED_On(LED1);( g0 n& X' Q* G
  6.     HAL_GPIO_WritePin (GPIOF,GPIO_PIN_6,GPIO_PIN_SET );
      o9 {( b+ y5 ~3 N/ W8 l, B8 T
  7. }3 R6 I/ f& j2 r% D
  8. /[i] USER CODE END 4 [/i]/
复制代码
" `2 s' B. W0 H8 ^. p
意思是AD转换完成调用这个函数,函数里使能led, }& _- k9 G/ R& Y0 v0 m2 U! b& X  Z
也许,你会问,为毛是HAL_ADC_ConvCpltCallback()这个函数啊,这个函数不是当开启AD的中断的时候才调用的吗?- o2 N6 V4 T( t! J6 D% A
嗯,对,这个函数是这样的,但是你仔细去分析下开启AD的DMA中断函数里面,就会发现这个函数也在啊" q- Y8 ~* n* M5 m: N
如下图.进入HAL_ADC_Start_DMA函数里面,看到1 x. l3 ]0 c9 u. H9 {

4 h- }0 J" G$ _
& p) B& D+ k- g8 n7 o2 D* ?" V
5 J% P1 k. W' Y8 b' E3 b# A; @6 R4 [5 q
在进入到图中的ADC_DMAConvCplt函数里面看到0 J2 q$ S& L& N
* h* Q, i- Q' r0 z, w3 V0 C4 ~; ~
5 `2 h7 H' g; I

! a  J) _  z. v0 KOK,疑问解决,* E, x' n) L% C7 q: Q, f
以后用到AD就可以直接调用这个CALL了,不要纠结了.

, x6 d+ a, Z% @7 @
收藏 评论0 发布时间:2022-2-9 22:16

举报

0个回答

所属标签

相似分享

官网相关资源

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