今天和大家分享一下使用STM32F769制作的位移采集装置5 F* G/ i- Y- t* f
一:STM32F769 ADC 知识分享:
! a2 e4 n$ Y/ x0 t! y9 v. |7 i/ ^* ]
STM32F769xx 系列有3个 ADC,这些 ADC 可以独立使用,也可以使用双重/三重模式(提高采样率)。STM32F769 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它有 19 个通道,可测量 16 个外部源、2 个内部源和 Vbat 通道的信号。这些通道的 A/D 转换可以单次、连续、扫描或间断模式执行。ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。 模拟看门1 \( I% h3 \+ `. E
狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。STM32F769IGT6 包含有3个ADC。STM32F769的 ADC 最大的转换速率为2.4Mhz,也就是转换时间为 0.41us(在 ADCCLK=36M,采样周期为3个 ADC 时钟下得到),不要让 ADC 的时钟超过 36M,否则将导致结果准确度下降。! E! z( X/ X% y8 i
STM32F769将ADC的转换分为2个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。
X1 u5 b4 s) x/ G3 _/ M% `( e) D: U
6 s# o" V! j6 ^% L9 S" L3 a9 I
5 j0 f1 N+ b- s6 jADC 的主要特征:
; L& d3 t) b/ ^5 q, D. K' b6 U
) ~' d+ v) b1 |( U* e, {可配置 12 位、10 位、8 位或6 位分辨率2 }7 J( R/ c( g& n& o
在转换结束、注入转换结束以及发生模拟看门狗或溢出事件时产生中断
- v3 A. G7 B: V% X: B单次和连续转换模式
9 ]) J& [4 J, b' }扫描模式,自动转化通道 0到通道 n数据
: S! J7 S) D. U# Y4 J5 W' B数据对齐以保持内置数据一致性
; y5 i6 \8 s6 X0 c; d可独立设置各通道采样时间
9 F V+ q2 r8 p+ S& a外部触发器选项,可为常规转换和注入转换配置极性$ `6 _4 h7 K. j! y; z! w7 r, [
不连续采样模式3 K. q; w+ v0 P; r. \1 C" k
双重/三重模式(具有2个或更多 ADC 的器件提供)# f3 M# h7 `! K. u* D* y
双重/三重 ADC 模式下可配置的 DMA 数据存储
+ \: U7 {" k/ |4 `& f+ J- p双重/三重交替模式下可配置的转换间延迟
/ h. L8 e% G" Q8 |; I9 e# CADC转换类型(参见数据手册)
6 T5 D% H+ G. J3 j' b9 fADC 电源要求:供电在 2.4V 到 3.6V 下可全速运行,供电低至 1.8V 时为慢速运行/ O; k/ l# l4 _0 U! `
ADC 输入范围:VREF-≤VIN≤ VREF+* a4 A; r6 p( D$ _5 J4 h
常规通道转换期间可产生 DMA 请求2 n$ l) m0 g3 B
7 @8 X: U' u7 J8 u7 P- I3 r! t R% @- c1 T6 o6 ?# ^* q
二:STM32 cube MX 软件的配置如下所示:4 d. h4 Y( E9 d2 H
6 h5 q& J. v# s* E6 r4 p$ m* t; E; ]3 K- k2 c
三:STM32 cube MX 软件生成代码如下所示:1 h5 L2 a' k* s. f6 T7 |
: Y: d- f4 k3 ?
- hadc1.Instance = ADC1; K1 D# v. J( M2 {
- hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;# u1 s3 P1 {9 \% ], r* I2 E5 O% n
- hadc1.Init.Resolution = ADC_RESOLUTION_12B;
3 J4 N! N3 u3 ] - hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
& e4 H- t/ e6 e; I - hadc1.Init.ContinuousConvMode = ENABLE;! o/ m) f9 y& }$ V7 }, N4 m% S
- hadc1.Init.DiscontinuousConvMode = DISABLE;
2 J" u. I* L2 B9 j$ O. [3 L& Y - hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
' I0 P! d- G2 P5 P2 F - hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;" A- c; r( t) }$ D F# Y+ E, V
- hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;3 N, c9 t9 _* q& ~
- hadc1.Init.NbrOfConversion = 1;8 t! i# y ~3 ~1 X4 o. m* v
- hadc1.Init.DMAContinuousRequests = ENABLE;( h" I+ C" H& w u: x R# `2 m" o
- hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
I' b% t" M& Q& A7 a - if (HAL_ADC_Init(&hadc1) != HAL_OK)
) o) L- @3 r* ]5 R1 p - {
' x. D1 `: M* E6 W - Error_Handler();
6 W% c2 N5 Y2 N6 I8 m - }
复制代码 上述软件代码是软件配置之后,自动生成的功能,然后我下载之后,发现代码进入的 void NMI_Handler(void) 错误,由于我使用的是DMA的方式接收的ADC的数据,所以DMA还在正常运行,也是搞不懂为什么,串口输出也不正常。
6 y8 @' Q0 n" i+ |. M( D然后我查询了下代码,将ADC的初始化修改如下所示代码:& _* f3 L' a2 I( T
- hadc1.Instance = ADC1;
3 i; H- T% I) @' R) w4 P - hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
6 H" ?% |) c @- J- ~4 A3 d - hadc1.Init.Resolution = ADC_RESOLUTION_12B;
! H" R- z* a# Z. f$ X - hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;7 ?/ G( P, [) w2 b( j* [( g1 h- G
- hadc1.Init.ContinuousConvMode = ENABLE;
7 X. i4 x. a! m1 A, } - hadc1.Init.DiscontinuousConvMode = DISABLE;
0 k7 v1 n9 ]3 ^ - hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;$ C- t1 L" r# t( a& a' \1 v% x
- hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
& V n9 o& C2 z4 [( A1 M - hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;1 n6 X6 ?2 R+ J/ T9 V+ w: \4 U
- hadc1.Init.NbrOfConversion = 1;
2 |# W% \1 @6 C! k3 j - hadc1.Init.DMAContinuousRequests = ENABLE;
6 E6 x1 ?* x# z- C4 r. ^4 v! [6 T - hadc1.Init.EOCSelection = DISABLE;6 C7 Q, R0 P; Z4 }3 {: g
- if (HAL_ADC_Init(&hadc1) != HAL_OK)
: Z/ w/ k. x. Z' {2 H# _% h - {
% j; p; k) x0 e. Q4 i7 e - Error_Handler();3 z% m* H. T! ]; s7 I' ~
- }
复制代码 下面是ADC 结构体的的变量解释4 L. ^! |; n& S8 R ]
- typedef struct{+ m8 }# Q. c G: j" w5 L- t
- uint32 t ClockPrescaler;//分频系数 2/4/6/8 分频
复制代码 这里发现是 cube MX 软件配置中对下述变量配置不太一样:+ M4 k7 F) L' v4 d
- hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
' R3 G3 v/ }9 e6 q - hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
复制代码 配置好ADC代码后,我们就直接读取ADC的数据就可以了3 G" `% [5 A& }: w$ c
主程序代码如下:: ]; H$ B) J. n$ c. {8 i
- HAL_Delay(200) ;
; @, H+ Q; e. y& R! K" P4 q5 k - HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_13) ;
^( d: u2 j* Y3 k0 n9 ~ - // HAL_Delay(200) ;
% b7 j: o5 i, g* I - // HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_5) ;9 p8 U5 x) i' _1 _* [% \0 _
- // HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_4) ;" r! q% d; ?2 i; \' h) t
- // HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_12) ;
' [1 X- n, D* {2 i4 B - if(usart1_flag == 1)8 I4 i$ L) q( I/ W% B
- {
, V, @7 v- I3 L; \6 L$ } - usart1_flag = 0 ;0 p5 S: Q2 x. @3 N4 \8 i* w i$ z
- HAL_UART_Transmit(&huart1,Usart1_DEAL_RX_Buf,usart1_rx_len,100);: ]9 D! f( Y+ Q) }- A
- // printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n");
! h+ W& v) W6 V3 g6 B; ~3 s
- \( N" j+ S0 u n5 w- printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n" );
0 G, ?8 s1 s1 o& d6 _* L6 s - }
8 P6 X. K% Y% @# I# M
6 B8 S2 b4 o/ `- printf("ubADCSample data = %5d\r\n" ,ubADCSample);
复制代码 PC截图如下所示:% [2 r6 ^3 n7 T$ L$ R w8 \
* c1 a7 m' |2 V S) @- `! I6 H6 r, ?3 R0 z2 C
调试感想如下:
) F8 u% W, R7 Z1 P) u) \# F虽然使用STM32 读取了ADC的数据,但是数据飘动还是比较大了,对于工业控制并不是很好,所以这里我推荐以下几种处理方式:
% c+ @" O: y% ?. }2 e1:中值滤波方式:; n- s9 u7 {$ t
主要是程序在执行的时候,连续采集N次(需要注意下这里下,这里的N必须取值奇数),程序需要按大到小或者从小到达的顺序进行排序,然后取中间数值做为有效值。% t" g/ z) s# J7 }! k
- int MidValueDeal(int N)- b$ a, _! Q2 K
- { - v- ~ P- r' c5 O
- int value_buf[N]; int i,j,k,temp;
& l$ X4 _ \1 Z4 b; H8 h - for( i = 0; i < N; ++i)
! C1 X" k3 Z* {$ P: Z4 i8 \# E - {
' J6 |; A ?6 c" ?1 \! F- S/ X, n - value_buf[i] = HAL_ADC_GetValue(&hadc1);
6 U3 N1 _ L( g+ y- g; q1 H) _! } O - }2 k7 A& Q* i; q
- for(j = 0 ; j < N-1; ++j) / d c5 Y% N' z, {
- { ' ~3 K+ f2 P3 g; _; K
- for(k = 0; k < N-j-1; ++k)
* x. F% H# c* G9 \1 H( f$ Q - { //从小到大排序,冒泡法排序 " _( x' T o2 U) y+ g
- if(value_buf[k] > value_buf[k+1])
/ H& r$ O2 d# n9 c/ x- E k& H. x* o - {
/ x+ b [: R) |8 L; E) t8 B" {8 @) C- U - temp = value_buf[k]; 9 F4 s8 e$ B+ J) o1 K: h8 ]' x: ]
- value_buf[k] = value_buf[k+1];
0 o4 q* G# q/ g2 R. \& c, E - value_buf[k+1] = temp;
' L/ K& b0 O, T - }
. w& q# `" Q6 w - }
- ~, b' D! B5 Q/ l& T U( J - }
% w* [+ q( Q! B' C1 A$ @ - return value_buf[(N-1)/2];
, e' E6 s) W1 s3 H( d4 ] - }
复制代码 2:算术平均数滤波:连续取值N个数据,对所有的数据进行取平均值;6 i/ ~/ K# @8 C6 F9 X/ m4 q1 _; {
- int AverValueDeal(int N), n0 J+ a: G+ V2 t: M) A A
- { : s ~) b6 G. i/ v
- int sum = 0; 8 E% N7 o6 [1 f% y+ U- c- m
- unsinged short i; * p6 b: ]% g& ~) t! r
- for(i = 0; i < N; ++i)
/ c6 G) Q2 H' }' R# n3 _; k - {
! i0 v0 ~) N7 O u! e - sum += HAL_ADC_GetValue(&hadc1); 1 _ H7 L4 L$ O% M/ @2 F( U" S! S+ f
- }
$ ]/ ` F6 k n. Z$ y4 A - return sum/N;
6 H+ x* v. J4 }, y: P% { - }
复制代码 相比之下:STM32F769的硬件上面没有过采样的硬件配置,对于STM32U083单片机,已经支持硬件的过采样功能。当时测试时候发现,经过硬件处理之后,数据稳定性得到提高。% e7 Z9 F) G8 {0 Y" g }
不过即使没有硬件的过采样,我们依然可以使用软件模拟过采样、滤波采样的功能,对数据进行处理。
; P7 G! z" j6 `9 v9 x8 E- ] a2 L- |4 i0 R8 v. H7 [
8 E6 |6 S- A8 L: l P最后,大家在调试ADC时候有什么好的建议和想法,麻烦评论区留言。8 P9 M h; i m4 r9 D6 d
|
用标准的基准源进行观察。打印出ADC的值,使用分析软件进行分析评估。
不知道是不是我配置的问题,这个AD的数据,感觉还没有U083单片机的采集稳定
需要进行滤波算法进行处理吧。这么高的主频处理起来应该不难。但是这个12bit的ADC,他的应用场景不是高精度的ADC采集。如果想稳定,最好使用专业的ADC吧。
[md]嗯,这个正好有个机会研究一下,,感觉不如U0系列的好呢😄
这个也不能这样说,F769在当年可以说是高档产品了。他还有其他的功能,综合性能比U0要强很多。但是U0的使用的内核比F769要新,在能耗性能比上要比F7xx要好。各有所长。