今天和大家分享一下使用STM32F769制作的位移采集装置/ ^+ M% a5 e5 ^+ X( z5 U
一:STM32F769 ADC 知识分享:
( n3 h' U) e! T; |# X/ t Y1 ? c2 }
STM32F769xx 系列有3个 ADC,这些 ADC 可以独立使用,也可以使用双重/三重模式(提高采样率)。STM32F769 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它有 19 个通道,可测量 16 个外部源、2 个内部源和 Vbat 通道的信号。这些通道的 A/D 转换可以单次、连续、扫描或间断模式执行。ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。 模拟看门
% u* G# z% p, f狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。STM32F769IGT6 包含有3个ADC。STM32F769的 ADC 最大的转换速率为2.4Mhz,也就是转换时间为 0.41us(在 ADCCLK=36M,采样周期为3个 ADC 时钟下得到),不要让 ADC 的时钟超过 36M,否则将导致结果准确度下降。' |4 L+ K5 |0 i' |
STM32F769将ADC的转换分为2个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。
7 q/ T- j$ m6 [0 s3 {: s
# \& u' ]' ~# m* o% }
& H) p$ ?* e$ M& {1 p2 r* z$ iADC 的主要特征:( f- m2 p5 e$ t3 d0 [
% w: m; k3 E/ j' x4 P, t
可配置 12 位、10 位、8 位或6 位分辨率
: z& _# a' _9 e0 `9 J# k在转换结束、注入转换结束以及发生模拟看门狗或溢出事件时产生中断" u) d0 O% _$ X
单次和连续转换模式
9 L) ? L' \& V: x/ o扫描模式,自动转化通道 0到通道 n数据 s2 y' s) I2 B
数据对齐以保持内置数据一致性. d9 J/ B3 a9 G7 E% ^7 m4 [
可独立设置各通道采样时间# o$ R% L; m/ z) M1 [
外部触发器选项,可为常规转换和注入转换配置极性
+ p7 Q+ a ~& b0 Z8 Q不连续采样模式$ W0 k: k0 ^% t5 E! u' e
双重/三重模式(具有2个或更多 ADC 的器件提供)3 U8 H8 P, x' z+ I- E0 o5 t
双重/三重 ADC 模式下可配置的 DMA 数据存储
. ]9 A, U: ?0 X; s双重/三重交替模式下可配置的转换间延迟
" F: B3 \2 y, ~ADC转换类型(参见数据手册)
$ _; E+ W0 i+ X5 d, c; ^6 [ADC 电源要求:供电在 2.4V 到 3.6V 下可全速运行,供电低至 1.8V 时为慢速运行- z! T2 o, T Z
ADC 输入范围:VREF-≤VIN≤ VREF+1 u3 `+ |- Z" J) B; b$ u
常规通道转换期间可产生 DMA 请求
# S$ v- H2 H( B2 p: ^/ O
% ^ e6 q, c4 z
" Q5 _* Q$ e( ]0 z二:STM32 cube MX 软件的配置如下所示:% k5 m- z& O' p8 m' `; W: h
. _$ H$ J3 `8 O* U' q1 ^
# M" m! p" K5 o5 y三:STM32 cube MX 软件生成代码如下所示:
) i5 s8 H5 Y7 X
0 b" c: S5 p9 l- f- hadc1.Instance = ADC1;0 a4 O- I3 R, l3 d
- hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;. g3 K/ v/ c6 f! p
- hadc1.Init.Resolution = ADC_RESOLUTION_12B;
! J2 l3 \ X, E! k: C- y1 q - hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;) r; P/ \2 s5 {# Q+ W
- hadc1.Init.ContinuousConvMode = ENABLE;, J% W' }; ]( k
- hadc1.Init.DiscontinuousConvMode = DISABLE;
8 q; w+ y+ z2 x6 K - hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
9 E5 h/ N5 Y! c - hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
+ s. V. a/ e1 ^: o5 S2 G - hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
- J4 G, s4 Q- b7 Y: n% z - hadc1.Init.NbrOfConversion = 1;. u* a6 c( u# y; E2 Q
- hadc1.Init.DMAContinuousRequests = ENABLE;
: u; g# b% _( E - hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;; y$ D4 U4 `4 N' E$ z
- if (HAL_ADC_Init(&hadc1) != HAL_OK)
' |* e) y- R* p9 Z5 V2 q# j' Y- W* ~ - {
& s$ ^+ N! B/ a( f - Error_Handler();
( z3 h" l) n0 t% O - }
复制代码 上述软件代码是软件配置之后,自动生成的功能,然后我下载之后,发现代码进入的 void NMI_Handler(void) 错误,由于我使用的是DMA的方式接收的ADC的数据,所以DMA还在正常运行,也是搞不懂为什么,串口输出也不正常。
6 d3 w7 w7 F* o. O; x7 k: Q7 m" o然后我查询了下代码,将ADC的初始化修改如下所示代码:
/ ^' \! z1 T7 a- hadc1.Instance = ADC1;# L/ r( A, K8 i$ m% T; y
- hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;3 D% q* A: ]% D1 H
- hadc1.Init.Resolution = ADC_RESOLUTION_12B;1 D5 j; H2 `6 L6 l ^2 I8 _
- hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
1 O! ^3 V- |! {: F/ ? - hadc1.Init.ContinuousConvMode = ENABLE;! J3 C& [6 n8 O5 r6 G; Y3 U
- hadc1.Init.DiscontinuousConvMode = DISABLE;: ]# w4 e) A$ b3 O! C5 N0 T' G
- hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
$ O: F3 V/ a% U# K5 C+ Q4 o# E. g. Z - hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
5 D: D( @7 G( J c - hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;! `' n( \* }/ S5 P
- hadc1.Init.NbrOfConversion = 1;
: D( I1 O- j/ _# G6 w* V: F- Q1 F - hadc1.Init.DMAContinuousRequests = ENABLE;. Q7 t; ^' r, B& E
- hadc1.Init.EOCSelection = DISABLE;
( {5 P; Y, [: U" Y r - if (HAL_ADC_Init(&hadc1) != HAL_OK)
, Y8 j# ]$ m+ r - {
4 {6 A4 P$ i2 L, y+ f# A) G - Error_Handler();
# l! U8 I! b' X% o1 N( I& `+ F - }
复制代码 下面是ADC 结构体的的变量解释
, g6 L E& H0 }" M' S2 k- typedef struct{1 F) i& o* y3 s$ t" B8 D
- uint32 t ClockPrescaler;//分频系数 2/4/6/8 分频
复制代码 这里发现是 cube MX 软件配置中对下述变量配置不太一样:
; I; o F% w |- hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
! ~: U P- x; J) p - hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
复制代码 配置好ADC代码后,我们就直接读取ADC的数据就可以了
+ T' C6 z. I" B3 x主程序代码如下:, ]) g$ @, C. U) \; l8 m- k0 C
- HAL_Delay(200) ;
, S, V, i- b T& b3 Q - HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_13) ;& @. f! V# Q7 J
- // HAL_Delay(200) ;
. @- L) g9 Z/ X! p- E - // HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_5) ;4 a8 F2 \, H0 j1 N
- // HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_4) ;
1 A+ @% A T! O - // HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_12) ;
" @+ ^* a R+ e5 @) H - if(usart1_flag == 1): N$ u) e# A/ `0 J$ ~: @
- {
: F9 P- H- |% p2 y - usart1_flag = 0 ;0 c( z+ E3 I% D! W$ |
- HAL_UART_Transmit(&huart1,Usart1_DEAL_RX_Buf,usart1_rx_len,100);) n; Q; I2 x* Z6 j( e9 m
- // printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n");
* p3 \1 x# |2 {4 E1 Y* l" F
% z g6 q5 i e6 s1 p0 K8 L- printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n" );
- ?% N; R2 l& g: R& U1 A6 U - }
8 T' v, w- ~( s9 @ }& C - ) k8 e4 p' E9 U, J) @( T+ k
- printf("ubADCSample data = %5d\r\n" ,ubADCSample);
复制代码 PC截图如下所示: F* x8 m: E& v, A
0 |# f( Y* M, R% E+ n
/ r( U2 L- \/ q$ P+ }$ j, w% ]调试感想如下:: u/ Q! w7 Y; o8 H& G' I0 Z
虽然使用STM32 读取了ADC的数据,但是数据飘动还是比较大了,对于工业控制并不是很好,所以这里我推荐以下几种处理方式:
; J7 |2 v' y, c9 G1:中值滤波方式:
* l/ j! D: s1 K# W" l主要是程序在执行的时候,连续采集N次(需要注意下这里下,这里的N必须取值奇数),程序需要按大到小或者从小到达的顺序进行排序,然后取中间数值做为有效值。) i. H2 k' c7 \9 l4 ~
- int MidValueDeal(int N)( a4 T/ F3 ~6 F" l
- {
7 o+ C6 P( W& V# x/ O - int value_buf[N]; int i,j,k,temp;
# [' }6 O# ^; i) z+ f - for( i = 0; i < N; ++i)
2 t: i I7 n1 S/ s( B$ ^$ B' ]' ` - {
/ B4 c& x- B7 n5 x - value_buf[i] = HAL_ADC_GetValue(&hadc1);
6 S* ?% i8 h: M+ I( f! k3 S - }6 U; }7 f8 v9 U% `- h) f, U
- for(j = 0 ; j < N-1; ++j) . P, [" \5 Q8 q) B( B( `+ N6 F; s9 G' z
- { . N4 v6 ]/ V' B& F2 ?
- for(k = 0; k < N-j-1; ++k) 3 p0 \) a% B# h5 H. z4 c: b; y e
- { //从小到大排序,冒泡法排序 ) y' g! U* z) J. A
- if(value_buf[k] > value_buf[k+1]) 0 C" c% Q$ C. u! B& @8 S
- { , b* J6 H2 h6 Y7 C
- temp = value_buf[k]; ; d9 T, s6 k- A4 R
- value_buf[k] = value_buf[k+1]; 7 B0 N) m$ ~% z1 P
- value_buf[k+1] = temp; & C) b# x) y% v+ x
- }
! Q2 s2 L8 }8 A& ?4 u" @7 i - } 3 D$ z7 W6 U1 C6 h
- }4 `% k# S& Z3 n4 |; `
- return value_buf[(N-1)/2];1 ]1 f. e" [' E8 B: Y0 f6 d
- }
复制代码 2:算术平均数滤波:连续取值N个数据,对所有的数据进行取平均值;
' e; ]' N' q9 z4 S0 O/ T- int AverValueDeal(int N)
1 `. o/ J, \6 J' O! S - { ' c# ?# C0 K8 g. _/ k+ K
- int sum = 0; 1 b0 y5 C0 {+ j$ N F' v; @3 t2 w
- unsinged short i;
' a9 ?$ n# @$ c3 j+ x - for(i = 0; i < N; ++i) 7 h& m2 ^8 l/ \+ R& C( K' I
- {
( z% B: d" U t8 k! i - sum += HAL_ADC_GetValue(&hadc1); 2 z8 F7 f; S: t" O3 ~( ^1 O$ x
- }
' E# g, l( e" I" t/ V - return sum/N;
7 Z* p' @' U/ x" ]! a5 Z( t. S8 F - }
复制代码 相比之下:STM32F769的硬件上面没有过采样的硬件配置,对于STM32U083单片机,已经支持硬件的过采样功能。当时测试时候发现,经过硬件处理之后,数据稳定性得到提高。" t4 A$ e' w9 c3 G R5 W
不过即使没有硬件的过采样,我们依然可以使用软件模拟过采样、滤波采样的功能,对数据进行处理。& f2 A b! \0 a2 T% w
! o' R6 t1 N) S9 H) U" z
) D. f% C% b4 {6 ~: \ S$ n! q' ]最后,大家在调试ADC时候有什么好的建议和想法,麻烦评论区留言。
/ V: q) N1 b. n7 v" _ |
用标准的基准源进行观察。打印出ADC的值,使用分析软件进行分析评估。
不知道是不是我配置的问题,这个AD的数据,感觉还没有U083单片机的采集稳定
需要进行滤波算法进行处理吧。这么高的主频处理起来应该不难。但是这个12bit的ADC,他的应用场景不是高精度的ADC采集。如果想稳定,最好使用专业的ADC吧。
[md]嗯,这个正好有个机会研究一下,,感觉不如U0系列的好呢😄
这个也不能这样说,F769在当年可以说是高档产品了。他还有其他的功能,综合性能比U0要强很多。但是U0的使用的内核比F769要新,在能耗性能比上要比F7xx要好。各有所长。