今天和大家分享一下使用STM32F769制作的位移采集装置
Y$ R" R5 z' Z+ s4 K一:STM32F769 ADC 知识分享:
+ y" n) W6 E# }+ i
" S) e+ z& h' I1 n# _STM32F769xx 系列有3个 ADC,这些 ADC 可以独立使用,也可以使用双重/三重模式(提高采样率)。STM32F769 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它有 19 个通道,可测量 16 个外部源、2 个内部源和 Vbat 通道的信号。这些通道的 A/D 转换可以单次、连续、扫描或间断模式执行。ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。 模拟看门
3 l+ ~) \/ I9 |3 V狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。STM32F769IGT6 包含有3个ADC。STM32F769的 ADC 最大的转换速率为2.4Mhz,也就是转换时间为 0.41us(在 ADCCLK=36M,采样周期为3个 ADC 时钟下得到),不要让 ADC 的时钟超过 36M,否则将导致结果准确度下降。; l8 |3 m( I( n ~( [ j) @/ x
STM32F769将ADC的转换分为2个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。; _7 x+ z5 N2 M1 f: p4 t% i7 G
9 d9 m+ J* d+ e1 M6 u- ]9 ^ c* _& B4 P& b2 |: u
ADC 的主要特征:7 m8 l0 l: R6 O& X' g
8 g2 e1 J9 \* O' x" P- J, p可配置 12 位、10 位、8 位或6 位分辨率
' l: g! g7 [7 h0 Z4 G* L3 Y5 y在转换结束、注入转换结束以及发生模拟看门狗或溢出事件时产生中断/ E1 s) R& k2 Z& U6 W" X- C& X9 t
单次和连续转换模式
9 P# N8 F9 O4 j! v O扫描模式,自动转化通道 0到通道 n数据
9 X1 s8 E5 Q0 y% m) q$ v& S数据对齐以保持内置数据一致性% C+ c/ j, D; Q4 g
可独立设置各通道采样时间6 b6 _& g2 ~! S# s; v
外部触发器选项,可为常规转换和注入转换配置极性
7 p2 u6 I* T4 m不连续采样模式. S* m! @" E' i
双重/三重模式(具有2个或更多 ADC 的器件提供)) w& `, L* a! }; R
双重/三重 ADC 模式下可配置的 DMA 数据存储9 d. D! ]% @8 X8 G
双重/三重交替模式下可配置的转换间延迟9 K+ W3 y- ~( K$ n" a* K8 I
ADC转换类型(参见数据手册)7 K$ ]" [& A& ~" M6 s7 n! B
ADC 电源要求:供电在 2.4V 到 3.6V 下可全速运行,供电低至 1.8V 时为慢速运行
, e3 u- B$ j: F" MADC 输入范围:VREF-≤VIN≤ VREF+) z- ]- M' V! Q* G) n2 L3 [
常规通道转换期间可产生 DMA 请求/ Q4 P/ c! P: I. F: u/ z
, q( Q7 h4 i; G3 z7 L, s
; R* u1 F5 s1 P3 e: V2 Z二:STM32 cube MX 软件的配置如下所示:/ s3 a1 w# |- b: e3 B% a
& B9 F" w5 D. j) L3 o
( R/ K% ]' ?: T# `$ v( ]三:STM32 cube MX 软件生成代码如下所示:5 G( F9 b1 n' ?
: Z7 o4 `# y# C0 \# v6 G4 w
- hadc1.Instance = ADC1;/ [. h0 T3 ]6 h. g8 @- y% t
- hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;. @% h* ~; b) ~3 w8 ^% n" f
- hadc1.Init.Resolution = ADC_RESOLUTION_12B;/ ^& A9 a6 \# E7 M% K1 y
- hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
+ c4 E' ?; R5 g5 k - hadc1.Init.ContinuousConvMode = ENABLE;
) |! F" A6 ~- n0 Y1 c& z* G0 R% n, _8 ^ - hadc1.Init.DiscontinuousConvMode = DISABLE;- k4 c' j( g: q% O" {, T, g! l! ]& x5 _
- hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
2 ~, T3 k7 r6 h) N% V' c1 |( _ - hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;) [; w) d( X7 {& S9 u. D
- hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
B% Z% v0 ^; x$ y4 q7 ]! { - hadc1.Init.NbrOfConversion = 1;0 l; j! J3 e) J; e" m
- hadc1.Init.DMAContinuousRequests = ENABLE;+ f' j* R K, ?1 c0 t+ }
- hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
. D4 d+ O/ r2 h- V, w* y/ l2 T9 r - if (HAL_ADC_Init(&hadc1) != HAL_OK)
- I* T7 u$ a/ Z$ e y( \) F h - {
) Y& l5 A- W6 C6 o - Error_Handler();
' z; L; }* j; ^! b5 m1 c" W - }
复制代码 上述软件代码是软件配置之后,自动生成的功能,然后我下载之后,发现代码进入的 void NMI_Handler(void) 错误,由于我使用的是DMA的方式接收的ADC的数据,所以DMA还在正常运行,也是搞不懂为什么,串口输出也不正常。
, e, c! J% ~1 i然后我查询了下代码,将ADC的初始化修改如下所示代码: g# O* F4 \4 t" b F: g
- hadc1.Instance = ADC1;/ ~+ O( k$ {2 `1 R
- hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;& e$ q- {3 s" Y3 s
- hadc1.Init.Resolution = ADC_RESOLUTION_12B;. _# |9 E' [2 e4 C
- hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;' _! o N5 u2 b9 w% |, r
- hadc1.Init.ContinuousConvMode = ENABLE;0 k3 D! g% @$ ]+ ~+ C7 |
- hadc1.Init.DiscontinuousConvMode = DISABLE;. D& {: M o" ~6 N% M
- hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;+ U+ {! G6 A8 e4 U9 i
- hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;/ H4 L" i# Z5 D- l! V$ X+ ?
- hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
, B }" [# U3 K+ s* C6 } - hadc1.Init.NbrOfConversion = 1;! o9 j5 v: y$ v( ~
- hadc1.Init.DMAContinuousRequests = ENABLE;
* V5 N( K- g# C2 o4 W% H8 o2 r - hadc1.Init.EOCSelection = DISABLE;1 m/ Z, P6 I7 k$ n
- if (HAL_ADC_Init(&hadc1) != HAL_OK)( t# z* P2 n3 O H% |& T; @5 F' J
- {: E! c* e9 I' v! K* q
- Error_Handler();
: k1 I# n6 |3 H2 E - }
复制代码 下面是ADC 结构体的的变量解释
3 n- T4 k2 G/ Z; h- typedef struct{4 q# o2 E8 d" @
- uint32 t ClockPrescaler;//分频系数 2/4/6/8 分频
复制代码 这里发现是 cube MX 软件配置中对下述变量配置不太一样:- Z9 t5 r8 o& w* K5 v
- hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
V8 _" P5 ~5 \3 ]7 z1 D6 V - hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
复制代码 配置好ADC代码后,我们就直接读取ADC的数据就可以了
( V- X5 O% v8 I* o主程序代码如下:* z5 Y, ?6 p+ d1 v. ]2 f" w
- HAL_Delay(200) ;
/ \, E0 ~; R& z$ @$ k. s* ^3 Q- i - HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_13) ;
g! I0 t: ]/ d) I7 m. X - // HAL_Delay(200) ;
4 q1 Z" L" e1 m# N - // HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_5) ;2 W! L% l+ R# J4 ~4 N9 M6 u' U
- // HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_4) ;
2 ?( F5 g+ ]; q9 K' G) p - // HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_12) ;/ b& E- x4 O1 ]# D. R3 U6 ?$ v
- if(usart1_flag == 1)
* J' B& u, h8 V& r. B - {
( S0 p: C: u1 N2 i5 |2 z Y - usart1_flag = 0 ;3 |! o- ~, R) y# f6 q' ]
- HAL_UART_Transmit(&huart1,Usart1_DEAL_RX_Buf,usart1_rx_len,100);
) p" d% _& `1 Z7 V/ ^5 g& q0 f - // printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n");, x; P* | y3 v6 x+ C8 [: ]) j
% U6 `+ p4 Z. I8 s" |1 p; l- printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n" ); Z5 T5 V9 z3 ]2 i
- }
7 ^7 c( W$ ~" h3 V$ P
/ q% h/ g9 S) c. T) i! k- printf("ubADCSample data = %5d\r\n" ,ubADCSample);
复制代码 PC截图如下所示:
# J5 b: p; ?$ r( k0 F" Z
8 U7 J- [* T' u4 A8 c
! ]+ I, s6 y) o7 X2 k调试感想如下:
) m& ~! M2 r- n8 |+ w8 {) K& R虽然使用STM32 读取了ADC的数据,但是数据飘动还是比较大了,对于工业控制并不是很好,所以这里我推荐以下几种处理方式:
4 H2 E$ ?! g( p) d/ M% Q6 e/ o1:中值滤波方式:
' t' T! ~5 P- b9 R" m/ F; X, O主要是程序在执行的时候,连续采集N次(需要注意下这里下,这里的N必须取值奇数),程序需要按大到小或者从小到达的顺序进行排序,然后取中间数值做为有效值。5 ?1 E, b& t( t8 O% q4 u
- int MidValueDeal(int N)
& M5 c' q9 P9 ]! e# H* m - {
& m. W/ P. r8 \$ x+ T% r4 g b t - int value_buf[N]; int i,j,k,temp; 2 a8 X# P5 z1 O- G; p# }
- for( i = 0; i < N; ++i) : B! D2 q: x) [; E. Y
- {
- ?, k Z/ P6 [- S$ c - value_buf[i] = HAL_ADC_GetValue(&hadc1); 6 E+ s, F% c6 y0 n/ R, ^( x B, ~ [" ]
- }
6 I1 _5 f& d4 ]8 t$ }& B! ~ - for(j = 0 ; j < N-1; ++j)
* c, _0 a9 `: i3 N) ]" [# g - {
4 D. P: U# B2 B* D - for(k = 0; k < N-j-1; ++k) ( l# f6 [, Q, y
- { //从小到大排序,冒泡法排序 0 i/ c0 v, Z1 ~4 I
- if(value_buf[k] > value_buf[k+1])
* }8 b! R# |, a - { ( E/ V1 J# `) V: l' O( O
- temp = value_buf[k]; 6 L1 k+ ]4 N" S$ O3 l' }9 X; f0 v
- value_buf[k] = value_buf[k+1];
/ b8 K5 b" ~- k0 \5 Y - value_buf[k+1] = temp; 0 w* v$ u' _+ `8 W8 h# G
- }
8 r6 ~5 _2 `/ n5 b - } 2 p5 J& l5 p i+ D' C6 L% i7 X: S
- }1 O- z1 m* _& |* d% u4 S
- return value_buf[(N-1)/2];" ^! Y: b3 I T- y0 z2 G3 S
- }
复制代码 2:算术平均数滤波:连续取值N个数据,对所有的数据进行取平均值;
" I4 O M* X* p! N- [3 i0 `- int AverValueDeal(int N)2 y& Q5 g+ v( h7 ]( t7 x
- { " D% U" _- C- ]# Q: N* r
- int sum = 0; & o7 B' X. P& I0 n5 j0 m1 L/ ]8 q
- unsinged short i; . w, o6 k, o& }
- for(i = 0; i < N; ++i) ' H1 K3 D3 T. a5 E, g5 Z
- { ; ?+ R& k# Z& N$ X# r+ f
- sum += HAL_ADC_GetValue(&hadc1); 0 ~! g' ] A9 u% `+ s& z
- } ; y5 Q1 `6 y9 l8 \& q4 r
- return sum/N;
4 `* R( i# G& J - }
复制代码 相比之下:STM32F769的硬件上面没有过采样的硬件配置,对于STM32U083单片机,已经支持硬件的过采样功能。当时测试时候发现,经过硬件处理之后,数据稳定性得到提高。# [3 b- O2 `" E6 m
不过即使没有硬件的过采样,我们依然可以使用软件模拟过采样、滤波采样的功能,对数据进行处理。
3 |9 \0 x9 P# R) w- g$ I6 W* R+ {; C8 U9 n
! u* Z6 ]) w m
最后,大家在调试ADC时候有什么好的建议和想法,麻烦评论区留言。. G* B2 T% n9 B- x! q0 d9 g
|
用标准的基准源进行观察。打印出ADC的值,使用分析软件进行分析评估。
不知道是不是我配置的问题,这个AD的数据,感觉还没有U083单片机的采集稳定
需要进行滤波算法进行处理吧。这么高的主频处理起来应该不难。但是这个12bit的ADC,他的应用场景不是高精度的ADC采集。如果想稳定,最好使用专业的ADC吧。
[md]嗯,这个正好有个机会研究一下,,感觉不如U0系列的好呢😄
这个也不能这样说,F769在当年可以说是高档产品了。他还有其他的功能,综合性能比U0要强很多。但是U0的使用的内核比F769要新,在能耗性能比上要比F7xx要好。各有所长。