今天和大家分享一下使用STM32F769制作的位移采集装置
) u1 ?$ T' `( a0 s! w7 g8 @2 T一:STM32F769 ADC 知识分享:4 X. P- }1 K* T! Q9 ?* @+ F6 D) ?3 D
! {' p1 ^2 c2 D' L4 k9 I: s+ W" a
STM32F769xx 系列有3个 ADC,这些 ADC 可以独立使用,也可以使用双重/三重模式(提高采样率)。STM32F769 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它有 19 个通道,可测量 16 个外部源、2 个内部源和 Vbat 通道的信号。这些通道的 A/D 转换可以单次、连续、扫描或间断模式执行。ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。 模拟看门
, ~6 f/ w( M x8 I- a狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。STM32F769IGT6 包含有3个ADC。STM32F769的 ADC 最大的转换速率为2.4Mhz,也就是转换时间为 0.41us(在 ADCCLK=36M,采样周期为3个 ADC 时钟下得到),不要让 ADC 的时钟超过 36M,否则将导致结果准确度下降。
% z4 V, F! T# ~/ lSTM32F769将ADC的转换分为2个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。
7 d% l' j: e& c' ^7 x% t4 ?; K8 R4 E' Y
}* _0 B0 I& o3 s2 N3 |: mADC 的主要特征:
8 H* Y. z% Y- K& Z( N# L S9 k6 L/ p: {7 [
可配置 12 位、10 位、8 位或6 位分辨率9 z9 ^6 N( N/ Y# h
在转换结束、注入转换结束以及发生模拟看门狗或溢出事件时产生中断
: g% v# U# O x, S单次和连续转换模式. {+ Q* X6 T2 J j/ r
扫描模式,自动转化通道 0到通道 n数据
( `/ G2 v. g" N3 w G数据对齐以保持内置数据一致性1 A; ]) C) e" Y
可独立设置各通道采样时间
7 ]8 p& p+ M- h8 M8 T; \外部触发器选项,可为常规转换和注入转换配置极性" ~" A3 r3 F$ i: ~8 I. C8 H
不连续采样模式$ @ W" w, P0 s# y5 a
双重/三重模式(具有2个或更多 ADC 的器件提供)4 r- }. X3 I2 Q) I, C6 u0 v9 g6 x0 Y
双重/三重 ADC 模式下可配置的 DMA 数据存储, }) Z4 S) o& D& P; B" @) U
双重/三重交替模式下可配置的转换间延迟* `0 K E. M( j6 m0 Y
ADC转换类型(参见数据手册)
: f% F9 [9 l' y/ b" R6 H% pADC 电源要求:供电在 2.4V 到 3.6V 下可全速运行,供电低至 1.8V 时为慢速运行9 G& I$ h4 a$ O8 u4 {& }1 x# W
ADC 输入范围:VREF-≤VIN≤ VREF+7 Z% Y6 D# U3 w8 D6 N
常规通道转换期间可产生 DMA 请求
# d; n; |" O1 `& C2 x l& x$ h7 Z5 [; b3 N8 X8 U% r
! d3 [3 i; R) G& D二:STM32 cube MX 软件的配置如下所示:
! k0 S* u1 C- z
) r b$ t+ N; e5 @ a+ j- R5 v! }. F
三:STM32 cube MX 软件生成代码如下所示:
* Z/ P7 ?: T* m5 N: h# E8 e, R% k: e1 C" n7 i" V
- hadc1.Instance = ADC1; l. t; L$ B' `0 U& R" [
- hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
8 ?. e9 C c7 ~: _" ` - hadc1.Init.Resolution = ADC_RESOLUTION_12B;
# u; L D5 Y' ~ - hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
% l& U6 e1 w# V, y! ~ - hadc1.Init.ContinuousConvMode = ENABLE;
& S: l9 c$ c3 |- M4 R - hadc1.Init.DiscontinuousConvMode = DISABLE;% R9 \! c1 Z! `- p8 U" `1 \' Q6 }% p/ l
- hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;& A/ h# P# b) F4 w& ~# M
- hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
$ @ y' W8 F, Q: V4 Z9 A - hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
% A2 x1 E+ T9 ^9 X0 r( J - hadc1.Init.NbrOfConversion = 1;
6 f; f5 Q( v# e/ }( b) C: z! [+ _0 b - hadc1.Init.DMAContinuousRequests = ENABLE;- Z5 L h' \+ b) t g
- hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
4 h9 \; P8 f* ~8 F2 L - if (HAL_ADC_Init(&hadc1) != HAL_OK), O- x) f1 j* H& _0 ? k% T
- {
! [! y4 A9 R5 W8 a - Error_Handler();
+ Z) _6 r8 K+ U9 s- k- S - }
复制代码 上述软件代码是软件配置之后,自动生成的功能,然后我下载之后,发现代码进入的 void NMI_Handler(void) 错误,由于我使用的是DMA的方式接收的ADC的数据,所以DMA还在正常运行,也是搞不懂为什么,串口输出也不正常。
. I- |) ]5 S. _然后我查询了下代码,将ADC的初始化修改如下所示代码:3 p' I+ w* C9 D2 E" M) i1 p
- hadc1.Instance = ADC1;& m# O( w8 \9 U" N# c: {
- hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
3 E- ]: X: H8 X. f9 p# G - hadc1.Init.Resolution = ADC_RESOLUTION_12B;
5 v0 m1 g8 ^$ Q$ a; P6 K - hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;6 N4 q( O m4 m: D: M5 d1 m
- hadc1.Init.ContinuousConvMode = ENABLE;
- x& y% j+ w5 ~1 H4 c* ]5 P - hadc1.Init.DiscontinuousConvMode = DISABLE;
( E: G1 y' T3 d( J1 N+ m1 y# o. U - hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
- Q$ `: z) {# s& h4 I% H" J. T - hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
* f9 `1 `9 F$ P @ T. q - hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
! P4 n" z9 L, d6 s - hadc1.Init.NbrOfConversion = 1;( J- `5 x7 [( S# l; Y, r
- hadc1.Init.DMAContinuousRequests = ENABLE;; C! a J7 f: J! o1 s/ A1 o Q
- hadc1.Init.EOCSelection = DISABLE;
3 F+ V, z# ^6 P* x6 {: x - if (HAL_ADC_Init(&hadc1) != HAL_OK)# e9 S/ U8 U% i$ R
- {
' e$ r+ \4 C" `2 q+ H& w - Error_Handler();# U0 X. d$ {. F# I7 Y, ^
- }
复制代码 下面是ADC 结构体的的变量解释
: d* i5 l" Q7 G0 D2 W5 I" m- typedef struct{% t, Y, @: Z# B
- uint32 t ClockPrescaler;//分频系数 2/4/6/8 分频
复制代码 这里发现是 cube MX 软件配置中对下述变量配置不太一样:
! A! b. q' b& e1 L6 ]& J- hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV; 4 Y& t3 {" p5 B4 ?7 \; R- Z- x0 f6 t% W
- hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
复制代码 配置好ADC代码后,我们就直接读取ADC的数据就可以了& g9 {( w! s1 n. C
主程序代码如下:+ z% f3 z* D" l$ P6 A" c! r6 O, F# ?& I
- HAL_Delay(200) ;
2 ^; N2 C! B9 a" ~0 q. b/ S - HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_13) ;3 ?) c9 j6 \, z2 i* i" M7 q
- // HAL_Delay(200) ;' u/ @. l; g1 t" p4 G
- // HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_5) ;( U1 D+ b/ q! v4 V2 e! l+ u2 L S
- // HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_4) ;3 k2 `% s) M% c8 Z a
- // HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_12) ;
! T0 x% Z9 r: k% T6 H6 W! x3 e - if(usart1_flag == 1)2 q9 p+ C4 d4 d: n" n3 I/ F
- {
1 m3 W c9 o5 q; Y - usart1_flag = 0 ;
3 `+ h& r( _% J# G+ i3 _ m* b - HAL_UART_Transmit(&huart1,Usart1_DEAL_RX_Buf,usart1_rx_len,100);, u+ M3 X: m2 W5 `0 i! I6 n6 }
- // printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n");
/ `8 O; t# x) u! P8 z3 d
! {5 t7 |4 |- G* }- printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n" );* i6 z/ Y+ a6 C7 d
- }2 z& ]- G7 w i( C% y7 b3 Q
- * d+ n7 d( U# O; K/ c& `! F0 K* {
- printf("ubADCSample data = %5d\r\n" ,ubADCSample);
复制代码 PC截图如下所示:
! s A( B; v: t
H4 c" ^; s- u3 e7 g+ Q8 r
0 j8 c, ]( X% b1 F7 z: n- ?# o* f
调试感想如下:/ V; q8 Q8 c# q0 [
虽然使用STM32 读取了ADC的数据,但是数据飘动还是比较大了,对于工业控制并不是很好,所以这里我推荐以下几种处理方式:
2 b* H+ ]2 A9 R5 B" ^1 ^1:中值滤波方式:
' O2 Q7 g: r" |主要是程序在执行的时候,连续采集N次(需要注意下这里下,这里的N必须取值奇数),程序需要按大到小或者从小到达的顺序进行排序,然后取中间数值做为有效值。
$ n0 W e' h7 r) e- int MidValueDeal(int N)
6 o! B8 E1 |3 e5 ?: J - { ! n( L, A8 h) u; @) P
- int value_buf[N]; int i,j,k,temp; 9 ?, P9 D+ n4 c5 P0 u
- for( i = 0; i < N; ++i) 6 @; U2 j) g, y; v/ J/ h: ~
- {
) k9 L$ ]1 _" p# F9 m) i' u - value_buf[i] = HAL_ADC_GetValue(&hadc1); % u( {9 J9 B* ^- ~3 A7 K& L
- }0 V/ [6 g* b z8 T
- for(j = 0 ; j < N-1; ++j)
& x* G" ~7 d0 K+ {" t% V - {
. ^! W2 T3 ?& N4 A - for(k = 0; k < N-j-1; ++k) * L: d0 i( D6 ^* v* G
- { //从小到大排序,冒泡法排序 7 U5 B) W3 ?' H( j; V; ~6 E
- if(value_buf[k] > value_buf[k+1]) 1 m( a6 S2 c; h4 p' e4 h6 M
- { * _" Q$ G1 ~+ ~2 P) L( N5 ^& D
- temp = value_buf[k];
& r. f$ p2 G5 j+ ?2 N% u- F - value_buf[k] = value_buf[k+1];
8 F6 g( R- _, w+ a - value_buf[k+1] = temp; 2 J* n& z" `* d
- } 0 o. q- w0 [$ r. F, T6 \2 k% z6 M
- }
8 H! E8 d+ [6 }' k& b$ k5 f7 k0 W - }
0 {/ ^$ U2 V( u- r; j - return value_buf[(N-1)/2];
6 W% {8 ]7 X j! i: m - }
复制代码 2:算术平均数滤波:连续取值N个数据,对所有的数据进行取平均值;
) V1 R/ T1 L0 z" {3 w f- j. w- int AverValueDeal(int N)
& |4 k6 J1 h) C" x; A* D, ] - { 7 E) b+ T9 Z% V2 w% c& f: S
- int sum = 0; % ^3 S& ]; e# P: _# Z
- unsinged short i; % y, o3 n7 K% Q% x/ r
- for(i = 0; i < N; ++i)
- y" Q1 a* f# n8 N+ G/ I. k - {
- e* ], U* |/ e# g - sum += HAL_ADC_GetValue(&hadc1);
; R* K: b+ K( ^) g0 Y/ r! b - } ; N _- P" v" v0 f
- return sum/N;
0 U8 K+ A7 \& d) J) | - }
复制代码 相比之下:STM32F769的硬件上面没有过采样的硬件配置,对于STM32U083单片机,已经支持硬件的过采样功能。当时测试时候发现,经过硬件处理之后,数据稳定性得到提高。4 _# k% d; x% N; X+ e
不过即使没有硬件的过采样,我们依然可以使用软件模拟过采样、滤波采样的功能,对数据进行处理。
$ `' T& m; [: {1 j3 g. M, r
% k; C- P/ L7 e, F+ U1 Q& R+ M5 t+ u
最后,大家在调试ADC时候有什么好的建议和想法,麻烦评论区留言。& g$ r5 y. l6 S, a
|
用标准的基准源进行观察。打印出ADC的值,使用分析软件进行分析评估。
不知道是不是我配置的问题,这个AD的数据,感觉还没有U083单片机的采集稳定
需要进行滤波算法进行处理吧。这么高的主频处理起来应该不难。但是这个12bit的ADC,他的应用场景不是高精度的ADC采集。如果想稳定,最好使用专业的ADC吧。
[md]嗯,这个正好有个机会研究一下,,感觉不如U0系列的好呢😄
这个也不能这样说,F769在当年可以说是高档产品了。他还有其他的功能,综合性能比U0要强很多。但是U0的使用的内核比F769要新,在能耗性能比上要比F7xx要好。各有所长。