今天和大家分享一下使用STM32F769制作的位移采集装置
. t; `, T! s3 s) S/ d一:STM32F769 ADC 知识分享:% b& v" D0 O; Z6 j
) r: r t# L. A
STM32F769xx 系列有3个 ADC,这些 ADC 可以独立使用,也可以使用双重/三重模式(提高采样率)。STM32F769 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它有 19 个通道,可测量 16 个外部源、2 个内部源和 Vbat 通道的信号。这些通道的 A/D 转换可以单次、连续、扫描或间断模式执行。ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。 模拟看门
+ _# y4 t1 i1 u狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。STM32F769IGT6 包含有3个ADC。STM32F769的 ADC 最大的转换速率为2.4Mhz,也就是转换时间为 0.41us(在 ADCCLK=36M,采样周期为3个 ADC 时钟下得到),不要让 ADC 的时钟超过 36M,否则将导致结果准确度下降。# B3 r1 ^$ D' Q. s
STM32F769将ADC的转换分为2个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。" ~8 P' a5 W. P- m
; V* V) S/ {: U8 u, {' Q; P) b3 \ i
ADC 的主要特征:' ^3 p2 W$ O3 B! ]+ N' |9 b
5 c# {# v3 `# m8 Z& e: K
可配置 12 位、10 位、8 位或6 位分辨率/ W4 i% {# k1 h; J- S4 Q; Y2 `
在转换结束、注入转换结束以及发生模拟看门狗或溢出事件时产生中断
# N4 b$ F) }, C( B1 P: x单次和连续转换模式
2 y' Q% D* X6 Z3 x O( J扫描模式,自动转化通道 0到通道 n数据6 O2 G9 s! c4 A
数据对齐以保持内置数据一致性0 x1 z$ A( y' S* s3 c0 H
可独立设置各通道采样时间! _: }, L V" z; p, D4 L, z4 e: f
外部触发器选项,可为常规转换和注入转换配置极性
! S3 H# ]8 i1 R7 i8 K不连续采样模式7 n# C' y" k/ _( ?8 {( a5 B& ^; H3 [( E
双重/三重模式(具有2个或更多 ADC 的器件提供)7 S. L/ O: L4 q- r, H/ v
双重/三重 ADC 模式下可配置的 DMA 数据存储( y2 R+ P2 o E! ~
双重/三重交替模式下可配置的转换间延迟
) P$ _1 ^) U8 P2 A# n, EADC转换类型(参见数据手册)
4 k+ d/ B/ D2 k) k" RADC 电源要求:供电在 2.4V 到 3.6V 下可全速运行,供电低至 1.8V 时为慢速运行
+ y! q: s& D! q7 x+ |8 {7 Y/ u. BADC 输入范围:VREF-≤VIN≤ VREF+
' i4 b# x! ]! T6 w. @" k K6 I常规通道转换期间可产生 DMA 请求* n( `# b8 J) p/ q4 C, Z
: E& [9 B4 C$ ]/ z- q: `
4 y' K' V4 H: a L% t4 i二:STM32 cube MX 软件的配置如下所示:& K2 }9 F8 R5 l3 h
8 j0 C; I4 Y j% W/ e" o: s8 F8 l
# Z; `' Z; X+ b. x4 r三:STM32 cube MX 软件生成代码如下所示:
- }/ T; y a. b% z; o4 s
9 Q: g7 r' V k, y- hadc1.Instance = ADC1;
, {9 b3 b5 ^; y2 E( D' K - hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
) B8 _$ {7 }6 c - hadc1.Init.Resolution = ADC_RESOLUTION_12B;
% A- U# L) I! O; p: e - hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;; r1 |& F% ^- d" \/ T
- hadc1.Init.ContinuousConvMode = ENABLE;
# X" ~- a" m0 @" m/ U - hadc1.Init.DiscontinuousConvMode = DISABLE;
- |0 C) `' p( K0 ? - hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;( ]2 U5 n! T" e9 q
- hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
' x' y! `/ I" ?1 g# C, @6 P - hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;; M- g3 @4 L( m7 Z* f3 E
- hadc1.Init.NbrOfConversion = 1;
8 E2 |$ a1 N6 c2 r |. y+ t$ f - hadc1.Init.DMAContinuousRequests = ENABLE;
9 |4 ?. ~8 @# x. l, n- R0 i6 b8 C - hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;# Z$ y+ Z5 H2 j% h: k
- if (HAL_ADC_Init(&hadc1) != HAL_OK)
3 ~) E3 v4 N( K1 A) G/ Y - {3 ?6 t3 [, F7 [ j) S
- Error_Handler();
. \4 x O2 U6 `0 J+ ^ - }
复制代码 上述软件代码是软件配置之后,自动生成的功能,然后我下载之后,发现代码进入的 void NMI_Handler(void) 错误,由于我使用的是DMA的方式接收的ADC的数据,所以DMA还在正常运行,也是搞不懂为什么,串口输出也不正常。; Y6 S* Z- {: C: R Q# ~
然后我查询了下代码,将ADC的初始化修改如下所示代码:+ k& I- R0 x7 b$ `6 |7 k
- hadc1.Instance = ADC1;
: A7 A, X/ z0 S+ ?' D. j a# r - hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
7 V, P6 ~; Z! B2 z/ ]7 r - hadc1.Init.Resolution = ADC_RESOLUTION_12B;
/ t f* m% s! d z) C - hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;% @! Y5 b; ]1 {) m+ V/ g
- hadc1.Init.ContinuousConvMode = ENABLE;
( G5 B/ l4 D( }. S+ A( T# y1 g - hadc1.Init.DiscontinuousConvMode = DISABLE;. ~; t- Y; {1 U; R& B- y
- hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
$ Y8 c+ O8 A, P5 |9 `; {8 ]. v - hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
: K! r9 R: Q' q. j" F8 g - hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
9 `( O( O; a5 O7 Z& ^ - hadc1.Init.NbrOfConversion = 1;
5 d2 O. z' L+ H* ~ - hadc1.Init.DMAContinuousRequests = ENABLE;
, j; P: {! Q8 h+ b4 r! p. `' ]0 {8 w - hadc1.Init.EOCSelection = DISABLE;9 d# B( I2 z: {; C0 i) Z
- if (HAL_ADC_Init(&hadc1) != HAL_OK)
$ U4 t6 X" j9 }0 H - {
0 l9 p% X0 j9 r/ P: v8 r" p - Error_Handler();
" X4 Q( W! }; ] - }
复制代码 下面是ADC 结构体的的变量解释
$ }' Q9 A L5 N8 s% ]8 [6 d8 }$ H- typedef struct{4 N+ I. o; D" @$ | s! j4 H/ p
- uint32 t ClockPrescaler;//分频系数 2/4/6/8 分频
复制代码 这里发现是 cube MX 软件配置中对下述变量配置不太一样:' d/ e' R& e. E: O, u/ V% G% N/ v" A
- hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV; 9 I' d! p) P0 p$ R$ l$ M
- hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
复制代码 配置好ADC代码后,我们就直接读取ADC的数据就可以了- S Y9 e: \+ L; i/ _
主程序代码如下:, }7 H5 Q- y3 s# |5 i: M; t: b/ _
- HAL_Delay(200) ;
4 C" Z. L k T& g3 U, L# ~' J - HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_13) ;
+ k: k3 C. c9 o! l( e# w* L) @6 q - // HAL_Delay(200) ;
' F" z4 R% L' j; V2 w- r - // HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_5) ;
- G: C' w# z& \: u |" k* o! c - // HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_4) ;
, @9 ~* P4 C' y1 Z# c% Z - // HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_12) ;
' H0 W ?$ I$ I7 ^+ a4 S - if(usart1_flag == 1)+ `. H; P, ?7 T3 W( U2 n
- {- m3 }% i5 g$ a8 f
- usart1_flag = 0 ;) {3 I+ D9 B4 r4 T( G
- HAL_UART_Transmit(&huart1,Usart1_DEAL_RX_Buf,usart1_rx_len,100);
/ \/ o/ ~3 P! r5 D t - // printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n");8 g6 x6 W4 O( R5 b$ B8 t9 t
' S5 p! b2 f7 i1 S: b- r. x8 ?- printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n" );
, b% R- r3 G: Y' Y - }6 j* d. @/ }3 G% y, E; q
- 7 [& M* N0 \2 e
- printf("ubADCSample data = %5d\r\n" ,ubADCSample);
复制代码 PC截图如下所示:
4 L$ I- e O7 K- G; d7 A2 ^! y) S- z: u
3 Z: }7 T6 h+ M7 R5 r. l. s) o L
) ^2 U& U7 f- e9 i% H1 u调试感想如下:
- ?* H) i# ]5 N- `4 @虽然使用STM32 读取了ADC的数据,但是数据飘动还是比较大了,对于工业控制并不是很好,所以这里我推荐以下几种处理方式:
+ k: D. A. m2 e2 ? J1:中值滤波方式:+ r5 H, E# K l2 N) I2 ]7 b
主要是程序在执行的时候,连续采集N次(需要注意下这里下,这里的N必须取值奇数),程序需要按大到小或者从小到达的顺序进行排序,然后取中间数值做为有效值。
4 A. k. u2 Y# A0 D) @ O- int MidValueDeal(int N)
- V- y+ F0 q5 g0 `6 N - {
% ?) u ]4 [/ z: Y4 a7 L - int value_buf[N]; int i,j,k,temp;
& ?, Z$ l0 U! | c0 l - for( i = 0; i < N; ++i)
$ z- ~8 p+ |) C+ q - { / F5 D: @$ E% T) k- _/ X! R
- value_buf[i] = HAL_ADC_GetValue(&hadc1);
g7 ~3 v: b7 ~& s6 \$ `' g% H - }
# M4 E4 z) ?; N; U - for(j = 0 ; j < N-1; ++j)
( I6 O) k5 q, ?7 ]/ ~2 z8 i! o+ D - {
1 S* I( I) |' r, T" I( v6 W; B - for(k = 0; k < N-j-1; ++k) 3 z) v+ N5 h0 A+ ^( k
- { //从小到大排序,冒泡法排序
* @2 j) K& [4 b: h3 }- s" u# J8 w! e. I) G - if(value_buf[k] > value_buf[k+1])
4 c9 ^' z- w' m+ b, Z1 J+ z - {
2 a' b: `+ @. l3 h5 x) i9 ^- ~6 d$ | - temp = value_buf[k]; ( p; u( s: [0 l- v
- value_buf[k] = value_buf[k+1];
" L. r9 e# N4 i* Z- x7 O. n" H: C - value_buf[k+1] = temp; * w X$ q' T' O; J
- }
3 E4 i: Z% p4 j3 t( y. p7 E - } - O3 e1 d0 h8 b z. W, m) ?( Y
- }
- S& \* m8 u% ` j7 j2 S& s& W2 m1 g - return value_buf[(N-1)/2];
9 T6 [6 Y; f# `: k - }
复制代码 2:算术平均数滤波:连续取值N个数据,对所有的数据进行取平均值;9 Y) ^. W( d7 w- ]4 W# o4 f
- int AverValueDeal(int N)
0 ?6 h0 w0 j/ }9 d, d - { + a( V$ w& v. c; r6 n
- int sum = 0;
9 i$ ?4 O) j0 P+ y. \' w6 K* I& n - unsinged short i;
d( m( I1 _7 w9 {( z; B - for(i = 0; i < N; ++i) 0 }6 x) n: D Y5 D4 O
- { 7 Z7 J/ r7 W3 }1 f8 v
- sum += HAL_ADC_GetValue(&hadc1); ( ]' i. |+ w& E3 b/ a5 P7 S( |
- }
2 K# Z$ z+ K4 ]2 J8 O8 k! {3 d% t8 E - return sum/N;( l" b% D6 N! e) |5 Q0 @- i
- }
复制代码 相比之下:STM32F769的硬件上面没有过采样的硬件配置,对于STM32U083单片机,已经支持硬件的过采样功能。当时测试时候发现,经过硬件处理之后,数据稳定性得到提高。
) s$ T: F# z9 V+ S! W不过即使没有硬件的过采样,我们依然可以使用软件模拟过采样、滤波采样的功能,对数据进行处理。
- F/ P; Q" R1 S9 N, y# F& E9 ]
" ]7 r* b4 v2 T$ ?0 Z: o* A8 _. @5 E7 I6 S' r
最后,大家在调试ADC时候有什么好的建议和想法,麻烦评论区留言。
' {) f" v, W9 k |
用标准的基准源进行观察。打印出ADC的值,使用分析软件进行分析评估。
不知道是不是我配置的问题,这个AD的数据,感觉还没有U083单片机的采集稳定
需要进行滤波算法进行处理吧。这么高的主频处理起来应该不难。但是这个12bit的ADC,他的应用场景不是高精度的ADC采集。如果想稳定,最好使用专业的ADC吧。
[md]嗯,这个正好有个机会研究一下,,感觉不如U0系列的好呢😄
这个也不能这样说,F769在当年可以说是高档产品了。他还有其他的功能,综合性能比U0要强很多。但是U0的使用的内核比F769要新,在能耗性能比上要比F7xx要好。各有所长。