今天和大家分享一下使用STM32F769制作的位移采集装置
8 W( \' W- W5 T; Q. x一:STM32F769 ADC 知识分享:7 w' F! b9 V/ k+ A+ b, L, T* [3 u
) q" e& r0 B' v
STM32F769xx 系列有3个 ADC,这些 ADC 可以独立使用,也可以使用双重/三重模式(提高采样率)。STM32F769 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它有 19 个通道,可测量 16 个外部源、2 个内部源和 Vbat 通道的信号。这些通道的 A/D 转换可以单次、连续、扫描或间断模式执行。ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。 模拟看门9 ~. r2 S& Y2 K! W8 k/ Z. M
狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。STM32F769IGT6 包含有3个ADC。STM32F769的 ADC 最大的转换速率为2.4Mhz,也就是转换时间为 0.41us(在 ADCCLK=36M,采样周期为3个 ADC 时钟下得到),不要让 ADC 的时钟超过 36M,否则将导致结果准确度下降。
4 j. _8 D( ]1 f* V' s/ k- _' MSTM32F769将ADC的转换分为2个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。
: o% [, p1 N7 n9 ~6 V& |5 ^' m% S7 p' S( G" @
( R4 w6 r6 {" I# {) m
ADC 的主要特征:3 h" {* @9 o' k7 ~9 O+ X: [
8 @ G5 o# ~# e4 `2 [; M& G. }可配置 12 位、10 位、8 位或6 位分辨率
3 ^2 Y1 Q* ?, k, n6 c. a- A6 r: k在转换结束、注入转换结束以及发生模拟看门狗或溢出事件时产生中断/ C! u. o1 N! G. Z7 I3 ]$ a( S% l
单次和连续转换模式6 }' a, p$ k3 A4 F. E8 y& x x
扫描模式,自动转化通道 0到通道 n数据
, Q0 K% ^1 R( l% o; k% j: f数据对齐以保持内置数据一致性
0 s# T; K( @. }; _" `6 w可独立设置各通道采样时间5 ~, H( e2 c( |/ o
外部触发器选项,可为常规转换和注入转换配置极性
. b* C5 r8 i0 B1 L$ ^2 y不连续采样模式" @+ Y& t# E* L$ ^5 j
双重/三重模式(具有2个或更多 ADC 的器件提供)
. O7 ^! N, J" t. ?( U双重/三重 ADC 模式下可配置的 DMA 数据存储) T4 y0 n/ A! C, S- T9 U+ h
双重/三重交替模式下可配置的转换间延迟
) V, M+ n% @6 Q! Z" f/ t/ ^* [ADC转换类型(参见数据手册)
& T8 }6 V% X5 r" @8 h) n! xADC 电源要求:供电在 2.4V 到 3.6V 下可全速运行,供电低至 1.8V 时为慢速运行
/ K+ Q& a7 a# d+ k0 l% VADC 输入范围:VREF-≤VIN≤ VREF+! P; c* @7 N1 v' J6 E0 Y1 a
常规通道转换期间可产生 DMA 请求3 ?8 v0 ?/ X& ?, j0 A
' q) [" Q0 P+ J( M( R: t6 D
- }* P' V' a6 F m' h3 M+ q二:STM32 cube MX 软件的配置如下所示:/ E8 c7 r" C }/ j2 L; c
( X; M) [$ H9 q+ M1 C
/ c) i) H4 S4 d" [' C/ H三:STM32 cube MX 软件生成代码如下所示:
6 |1 V0 w( b( j' ~* f6 L2 ?& ]* w0 B4 B
- hadc1.Instance = ADC1;
" v- E" v. ~4 \' |: ]$ J, F - hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;/ {, H. l- T$ ?2 y
- hadc1.Init.Resolution = ADC_RESOLUTION_12B;) W0 P& F7 u+ \ D3 C, p9 s0 p
- hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
' `# E9 O# R6 q% D5 W+ \ ^ - hadc1.Init.ContinuousConvMode = ENABLE;
" {+ V# i# h. ^1 |2 D ~9 N - hadc1.Init.DiscontinuousConvMode = DISABLE;
& C# C$ y7 u3 B4 k& [: S1 @ - hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;$ j2 _5 j1 X( p1 x9 h3 S: f
- hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;+ o5 J& h! N- v7 w8 B# p
- hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
% J9 J; z* c+ a+ S1 {+ E+ G - hadc1.Init.NbrOfConversion = 1;
* r1 O$ @3 X4 g0 A$ P - hadc1.Init.DMAContinuousRequests = ENABLE;
7 V4 G5 ?, S. ^$ P V- y f+ v1 P - hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
4 j; ^2 \; ^$ g# o2 { - if (HAL_ADC_Init(&hadc1) != HAL_OK)6 a: x% w" T7 O/ I7 J. _
- { d- e4 M5 d: o/ n! `) K( h
- Error_Handler();; l/ B5 v& p, a7 B* Q& a- h4 t" |3 J
- }
复制代码 上述软件代码是软件配置之后,自动生成的功能,然后我下载之后,发现代码进入的 void NMI_Handler(void) 错误,由于我使用的是DMA的方式接收的ADC的数据,所以DMA还在正常运行,也是搞不懂为什么,串口输出也不正常。4 H9 M$ l3 Q6 ^ B
然后我查询了下代码,将ADC的初始化修改如下所示代码:
: {- T8 |; @: S3 o- hadc1.Instance = ADC1;
: c0 q) V5 B" S2 l- ?- c - hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
; e: D% ~) u3 n# ~; \1 } - hadc1.Init.Resolution = ADC_RESOLUTION_12B;
9 n$ z9 s: p* a$ q& l3 Q - hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;1 V. F3 J- m$ N2 |" J
- hadc1.Init.ContinuousConvMode = ENABLE;
2 P. ~7 _6 Q6 |1 I4 a- Y5 C - hadc1.Init.DiscontinuousConvMode = DISABLE;
& L _- R, `4 X+ H+ P4 t - hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
3 Y) v" w9 V: B2 N0 D8 e( z, ] - hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
# z0 n; \. c' J0 s- w1 a - hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;) y9 k* k, S" p
- hadc1.Init.NbrOfConversion = 1;" l* K% }, z9 n* l" @
- hadc1.Init.DMAContinuousRequests = ENABLE;
, ]. ~- _+ B, o% y4 D - hadc1.Init.EOCSelection = DISABLE;% f- r, w2 J& A3 i
- if (HAL_ADC_Init(&hadc1) != HAL_OK)
: K1 ]+ _5 F( x* h4 E5 ^ - {
) l9 B8 d1 t5 U1 n3 c0 \ - Error_Handler();
' o f3 ~% y& a9 K$ |3 X - }
复制代码 下面是ADC 结构体的的变量解释
+ I9 n' ~ }, W+ s. [: _8 x" c- typedef struct{, {- R/ o$ _% w. l, x( G
- uint32 t ClockPrescaler;//分频系数 2/4/6/8 分频
复制代码 这里发现是 cube MX 软件配置中对下述变量配置不太一样: l }' [0 O" ~) v1 I
- hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV; ! @3 n1 L- m* x/ H; C2 m
- hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
复制代码 配置好ADC代码后,我们就直接读取ADC的数据就可以了
5 v6 l4 W/ [9 X主程序代码如下:
# N8 {/ h' q/ r. n( s& f; Q' s9 v: g- J- HAL_Delay(200) ;
5 s9 u' a0 A2 c/ U% T" s* ` - HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_13) ;4 T- U8 t% ` `, a+ c' t( u- v/ \
- // HAL_Delay(200) ;
' H; |* D& \* Y6 I7 A* w - // HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_5) ;4 P6 L. Q& J0 J O
- // HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_4) ;; p& `& i; m* m ?$ q: k% D
- // HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_12) ;3 Q+ m- s+ x. l; f! T, W
- if(usart1_flag == 1)) j: |7 I4 }: |8 l
- {* v& R) \0 {. [" O# F9 M1 g
- usart1_flag = 0 ;5 Z1 _. q$ x* K- M: ]( J, C, ]
- HAL_UART_Transmit(&huart1,Usart1_DEAL_RX_Buf,usart1_rx_len,100);) l. _- o! L- e9 a- X: t4 ?
- // printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n");. Z" y5 S7 L }( |. K2 w4 }, w% V2 t# \
- 4 D; |8 @, m6 z) \0 z
- printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n" );0 V% c$ t+ ]7 X: }- ~7 ]1 y
- }
( Y, T( a% d$ q6 `$ R3 v - * r7 L7 J0 R: c, r' Z; x
- printf("ubADCSample data = %5d\r\n" ,ubADCSample);
复制代码 PC截图如下所示:( }# Z6 F, S. Y. X7 z
" x2 F/ J: g' H& w1 y, U. @% D9 r) d' C0 K1 x; F
调试感想如下:
5 `2 c4 [' D. t" {" P; b/ A虽然使用STM32 读取了ADC的数据,但是数据飘动还是比较大了,对于工业控制并不是很好,所以这里我推荐以下几种处理方式:) [0 Z" n N( B* U# ~; `
1:中值滤波方式:
9 Q8 \; I; a% |; ~* s2 ^# m6 s主要是程序在执行的时候,连续采集N次(需要注意下这里下,这里的N必须取值奇数),程序需要按大到小或者从小到达的顺序进行排序,然后取中间数值做为有效值。) ?: q$ W0 u. Y7 P& p
- int MidValueDeal(int N)
0 ~% ~2 \# H! Z* z4 {* G - { / s; @5 r ^+ |4 h; K% [
- int value_buf[N]; int i,j,k,temp;
F- v0 p3 c# I. u1 O - for( i = 0; i < N; ++i) 1 _9 V( [! c# |( @9 F- l
- { % T$ Y" O: {- e
- value_buf[i] = HAL_ADC_GetValue(&hadc1); 8 j3 U) s" R0 \4 |& @) c, ^
- }
# W8 j! H1 w# x H; R* A/ v0 \ - for(j = 0 ; j < N-1; ++j) 3 a. x, s3 h* w5 o; f* M
- {
, X8 O- M4 H( A, v7 G# g/ Y0 D: L - for(k = 0; k < N-j-1; ++k) + M6 R( b# A( Y) f$ [8 o- i) l8 U- w
- { //从小到大排序,冒泡法排序
2 |' u8 b, V* {# Q+ I) M - if(value_buf[k] > value_buf[k+1])
, X7 K8 J) i2 Y8 e. K6 z8 b+ p - { % C: H+ f$ }$ C' `2 n- `7 N' U
- temp = value_buf[k];
- C3 g% ?0 V' ]& P7 i) f! Z - value_buf[k] = value_buf[k+1]; 8 @! N! ^% X3 v0 i- j' d
- value_buf[k+1] = temp; 9 a8 ]0 E" m2 c5 b9 P% `
- } 7 u' {3 M, Y2 O1 C v
- } $ S1 ?) O# e6 ^9 r& U: D) I
- }' _( x" D7 S% v3 \" I
- return value_buf[(N-1)/2];
8 J2 K( e7 u Q4 L4 L - }
复制代码 2:算术平均数滤波:连续取值N个数据,对所有的数据进行取平均值;! s( ]2 L( c6 i
- int AverValueDeal(int N)
# n% p0 T0 w1 |2 i - { * b( w. x$ _% g+ x& E
- int sum = 0; 3 M. ~6 C" S0 o5 k- o- Y# y3 A
- unsinged short i; - e5 q' A1 n a8 o
- for(i = 0; i < N; ++i) 2 P9 S6 Z" h$ v; Q
- {
" [/ p" U0 N1 h% _- @. }; Z) M - sum += HAL_ADC_GetValue(&hadc1);
. A. \& }3 ^4 U0 Z - }
* ~( `; D+ n$ F/ z b+ E& S - return sum/N;( o- I) n6 a3 H" x- E
- }
复制代码 相比之下:STM32F769的硬件上面没有过采样的硬件配置,对于STM32U083单片机,已经支持硬件的过采样功能。当时测试时候发现,经过硬件处理之后,数据稳定性得到提高。8 v+ b/ q, V* o5 Q- L7 `
不过即使没有硬件的过采样,我们依然可以使用软件模拟过采样、滤波采样的功能,对数据进行处理。
& w7 H E6 s8 s8 K5 W" O
# @, v8 J" `, m! E( w) ~
4 `" {4 M, V, G$ C3 [3 `最后,大家在调试ADC时候有什么好的建议和想法,麻烦评论区留言。
; J2 {/ z8 R( ^8 b9 i2 ] |
用标准的基准源进行观察。打印出ADC的值,使用分析软件进行分析评估。
不知道是不是我配置的问题,这个AD的数据,感觉还没有U083单片机的采集稳定
需要进行滤波算法进行处理吧。这么高的主频处理起来应该不难。但是这个12bit的ADC,他的应用场景不是高精度的ADC采集。如果想稳定,最好使用专业的ADC吧。
[md]嗯,这个正好有个机会研究一下,,感觉不如U0系列的好呢😄
这个也不能这样说,F769在当年可以说是高档产品了。他还有其他的功能,综合性能比U0要强很多。但是U0的使用的内核比F769要新,在能耗性能比上要比F7xx要好。各有所长。