今天和大家分享一下使用STM32F769制作的位移采集装置
6 A% ]( Q; t8 h一:STM32F769 ADC 知识分享:
6 E0 Q5 X: W& X# ]+ z- C$ a1 K. }$ S8 i
STM32F769xx 系列有3个 ADC,这些 ADC 可以独立使用,也可以使用双重/三重模式(提高采样率)。STM32F769 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它有 19 个通道,可测量 16 个外部源、2 个内部源和 Vbat 通道的信号。这些通道的 A/D 转换可以单次、连续、扫描或间断模式执行。ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。 模拟看门) L3 s1 w$ c/ A- a% v. s
狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。STM32F769IGT6 包含有3个ADC。STM32F769的 ADC 最大的转换速率为2.4Mhz,也就是转换时间为 0.41us(在 ADCCLK=36M,采样周期为3个 ADC 时钟下得到),不要让 ADC 的时钟超过 36M,否则将导致结果准确度下降。
3 p0 V( n5 }9 m' ]5 FSTM32F769将ADC的转换分为2个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。# B5 `! o( Q' H
6 w4 [- y$ G8 K. q) V! M4 T8 }/ \
# n! c8 W0 _, [. K' t HADC 的主要特征:: T h1 D! X# Y+ Z' S
4 x: z+ l0 L4 L可配置 12 位、10 位、8 位或6 位分辨率
6 Z+ }) v5 e8 r& A: U" b5 W6 Q1 i在转换结束、注入转换结束以及发生模拟看门狗或溢出事件时产生中断" [! {! V2 U0 M1 u
单次和连续转换模式
1 p% N! E/ G" i, w, ~6 y扫描模式,自动转化通道 0到通道 n数据8 t& k8 s) \% q& l3 W- }
数据对齐以保持内置数据一致性7 p. f/ }& K- c1 k. x; l
可独立设置各通道采样时间5 a* z: i' j" i! e* |
外部触发器选项,可为常规转换和注入转换配置极性6 _" V" R Y& k& @
不连续采样模式& S' N* E4 R0 |* t2 G
双重/三重模式(具有2个或更多 ADC 的器件提供)2 p# N# A6 O' c: Y* ^
双重/三重 ADC 模式下可配置的 DMA 数据存储
- @' Z {$ F8 C2 y9 `3 g双重/三重交替模式下可配置的转换间延迟# }/ a: H. r, f9 ?/ ~
ADC转换类型(参见数据手册) k$ I& C1 C6 K$ d1 t! E2 G; h
ADC 电源要求:供电在 2.4V 到 3.6V 下可全速运行,供电低至 1.8V 时为慢速运行
' Q* Z- j" H% T7 A7 FADC 输入范围:VREF-≤VIN≤ VREF+- W' g8 {' r1 B. p* d1 y% y- E
常规通道转换期间可产生 DMA 请求
! g/ Q/ u6 @ k! h; G
' L7 z0 w7 H0 S7 w9 K8 ^9 H
9 _" I/ G& a, i' i二:STM32 cube MX 软件的配置如下所示:5 i+ @4 \4 d! n9 d% [4 D
8 {( ?* g" h. [- @2 k
5 J8 E: s$ s! G! Y( ~ Y, O* m$ g! i三:STM32 cube MX 软件生成代码如下所示:% q! U: ?1 z, r& _0 w7 E7 o
! r+ a, l+ C# \4 ? \
- hadc1.Instance = ADC1;
& j; q. `: ~: V" S8 n- g - hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
$ G# `6 O7 G2 j2 Z0 I9 ~( j - hadc1.Init.Resolution = ADC_RESOLUTION_12B;
7 W$ Q/ F$ H- I - hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
; y4 P4 U* d# k" k% z& k0 M: c - hadc1.Init.ContinuousConvMode = ENABLE;2 l# M9 R5 f5 |2 k% j2 ^; Z
- hadc1.Init.DiscontinuousConvMode = DISABLE;5 V7 q. {! m+ K' D0 b
- hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
5 |) G4 C6 J% v* w - hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;! f$ N z* K# E3 J# e. R) N
- hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;6 b' w: q. h, H& {# ]" _
- hadc1.Init.NbrOfConversion = 1;8 C3 ^8 Z( X4 D3 y. j7 ^. N5 {
- hadc1.Init.DMAContinuousRequests = ENABLE;1 z# G: z q9 H7 D) t
- hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
3 S' y. h' }6 v - if (HAL_ADC_Init(&hadc1) != HAL_OK)$ N! X1 C2 ~9 {/ c7 V8 a
- {. \5 F( E: s" c1 z; d( p
- Error_Handler();
1 O k, y3 j! i: x - }
复制代码 上述软件代码是软件配置之后,自动生成的功能,然后我下载之后,发现代码进入的 void NMI_Handler(void) 错误,由于我使用的是DMA的方式接收的ADC的数据,所以DMA还在正常运行,也是搞不懂为什么,串口输出也不正常。7 D: y6 A; X- A, B
然后我查询了下代码,将ADC的初始化修改如下所示代码:7 z1 Q+ E7 k- L/ h
- hadc1.Instance = ADC1;+ d' n/ R* \7 n F1 e
- hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;% `3 f3 v" e( s, i. w( Q" O
- hadc1.Init.Resolution = ADC_RESOLUTION_12B;
% M4 f1 X) K: p) { - hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;7 K+ A- z2 \9 S8 x( c2 m
- hadc1.Init.ContinuousConvMode = ENABLE;' p* D$ ?1 i- q
- hadc1.Init.DiscontinuousConvMode = DISABLE;5 E* b! t3 h! c- `9 k, `$ `/ t2 m
- hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;; k/ C' x7 Y {- Q* t
- hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;+ e) W2 r! J. n; U$ X/ U( W
- hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;! g: D1 m T3 ]) G( G
- hadc1.Init.NbrOfConversion = 1;# L! y# Y" ^5 n P7 h1 T9 N
- hadc1.Init.DMAContinuousRequests = ENABLE;
+ W" ]8 N$ f+ \$ U M( y+ _4 N - hadc1.Init.EOCSelection = DISABLE;
& ?; U z1 r7 U" R3 T' G( z& } - if (HAL_ADC_Init(&hadc1) != HAL_OK)
0 q# T/ g) a/ ` - {7 `+ o' {1 B9 ~+ [
- Error_Handler();
- W4 j- `7 q6 c. E+ m2 N - }
复制代码 下面是ADC 结构体的的变量解释, _" B# A( `, u. n
- typedef struct{- Y+ B% ]/ z N/ F& F
- uint32 t ClockPrescaler;//分频系数 2/4/6/8 分频
复制代码 这里发现是 cube MX 软件配置中对下述变量配置不太一样:
% h" E( \% h5 h) j- hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV; , g' i" g0 z. T' A
- hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
复制代码 配置好ADC代码后,我们就直接读取ADC的数据就可以了
) z6 p8 a6 }6 q主程序代码如下:; X8 f% ^& d) b0 ^0 S
- HAL_Delay(200) ;4 w9 z1 P# l, T
- HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_13) ;7 S( [5 y! n6 u, x* J
- // HAL_Delay(200) ;8 |$ Y4 }. b) v
- // HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_5) ;
/ b/ `0 Y4 S9 k5 T! N+ f - // HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_4) ;
. r% `* _7 {' d! Q& L4 K - // HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_12) ;9 [8 [- B3 n X z
- if(usart1_flag == 1)
6 p/ X6 ?* \# f e7 C/ s* [ - {
- \5 i8 q# V: y- t1 e/ ?8 B7 V - usart1_flag = 0 ;0 w/ p+ A4 w& L- r1 \+ f
- HAL_UART_Transmit(&huart1,Usart1_DEAL_RX_Buf,usart1_rx_len,100);
; g6 U2 p) a8 }# J( R) X1 n - // printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n");
. G9 Q' A) K4 R% i* ]- O( n' g P - 7 e g$ d( g% G0 n1 g+ g& ~$ |
- printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n" );
+ q2 r. Q+ x/ n - }8 S* G# L# C( [5 }( k. _4 B Q. q
- 5 v$ f. d+ a! Y$ q3 x) i1 M
- printf("ubADCSample data = %5d\r\n" ,ubADCSample);
复制代码 PC截图如下所示:
. Q _$ a% F5 U1 Y. @' G& r
" u6 g3 s5 a" y5 J2 G. v
3 z( j" J3 r0 U6 A7 P- t, m* U: E; D调试感想如下:
B3 }# E( U2 A; F; c" M1 E虽然使用STM32 读取了ADC的数据,但是数据飘动还是比较大了,对于工业控制并不是很好,所以这里我推荐以下几种处理方式:
7 t' h9 o8 c& w; x: T2 z7 ^1:中值滤波方式:
! o5 H' } s- n! Q( W主要是程序在执行的时候,连续采集N次(需要注意下这里下,这里的N必须取值奇数),程序需要按大到小或者从小到达的顺序进行排序,然后取中间数值做为有效值。
: g3 T9 R8 B7 w: Z( b3 [- int MidValueDeal(int N)
9 b% M1 |# B2 S3 H$ _! |+ g+ Q - {
$ o; C' z, ~# |4 W7 U8 S - int value_buf[N]; int i,j,k,temp; - p) `" t! l% d9 e4 C
- for( i = 0; i < N; ++i) g) Y: m5 k3 G+ ?$ A* D# e
- { / {. }# S7 j% _
- value_buf[i] = HAL_ADC_GetValue(&hadc1); ' `% n- ]$ W1 H0 w! F3 N. D
- }( j3 U/ h E" @- V* H( ?
- for(j = 0 ; j < N-1; ++j) x `- r v' n8 w- }* ~% R- ^6 c3 Z
- { * d! j0 t7 Z$ \8 D6 p; W
- for(k = 0; k < N-j-1; ++k) " L r; p8 h$ Q
- { //从小到大排序,冒泡法排序 - J1 o0 z& p3 G5 M
- if(value_buf[k] > value_buf[k+1])
+ w, F a2 Z) L* Y6 g# P7 O - { 6 }; E4 M8 g- o7 m4 H" H
- temp = value_buf[k];
' y% B- J' m/ d) D - value_buf[k] = value_buf[k+1];
3 S, ~/ |, a6 N, Z$ C* C - value_buf[k+1] = temp;
, N6 K. A: a+ s - } ' V! m* |1 l! T5 Q; E5 [! T/ i( J
- }
6 v, A$ A) @ |; F - }
4 y- D3 c8 @+ u9 C7 R1 n# K - return value_buf[(N-1)/2];# W: I/ d: C9 j- m9 o
- }
复制代码 2:算术平均数滤波:连续取值N个数据,对所有的数据进行取平均值; i1 N4 Y5 T' J4 n0 z+ Q
- int AverValueDeal(int N)
+ j0 C' F# \" Q: ]" V3 Y2 D: p - {
" n/ _/ _- x1 Q1 k( s3 f0 ^) C - int sum = 0; + a7 `7 e7 f3 g# U6 \ v; D
- unsinged short i;
* o) ^7 U. n) L2 i - for(i = 0; i < N; ++i)
; Q# H2 T6 F3 ^4 P$ F - { 3 ]* F8 j0 O3 [5 |4 z2 M
- sum += HAL_ADC_GetValue(&hadc1);
8 Z2 Y k- T5 n) J" \2 ?' F2 J/ _ - }
6 N" j, G) w2 O) G% k* Q - return sum/N;
5 u$ K% v# R5 u: K - }
复制代码 相比之下:STM32F769的硬件上面没有过采样的硬件配置,对于STM32U083单片机,已经支持硬件的过采样功能。当时测试时候发现,经过硬件处理之后,数据稳定性得到提高。7 i) {6 T* s4 |
不过即使没有硬件的过采样,我们依然可以使用软件模拟过采样、滤波采样的功能,对数据进行处理。
( \2 N/ l# E) A; z- Z9 W" ]$ h1 b ^0 |8 f8 s' k
, h9 y# V/ `) D2 i4 {/ x最后,大家在调试ADC时候有什么好的建议和想法,麻烦评论区留言。
4 `6 a0 K$ `$ u' @& ~3 ?5 l |
用标准的基准源进行观察。打印出ADC的值,使用分析软件进行分析评估。
不知道是不是我配置的问题,这个AD的数据,感觉还没有U083单片机的采集稳定
需要进行滤波算法进行处理吧。这么高的主频处理起来应该不难。但是这个12bit的ADC,他的应用场景不是高精度的ADC采集。如果想稳定,最好使用专业的ADC吧。
[md]嗯,这个正好有个机会研究一下,,感觉不如U0系列的好呢😄
这个也不能这样说,F769在当年可以说是高档产品了。他还有其他的功能,综合性能比U0要强很多。但是U0的使用的内核比F769要新,在能耗性能比上要比F7xx要好。各有所长。