今天和大家分享一下使用STM32F769制作的位移采集装置% G7 Y; N/ W& C2 P
一:STM32F769 ADC 知识分享:/ k R5 Y9 i G1 ^. R# y
$ u2 u: p& ~$ {' j6 v
STM32F769xx 系列有3个 ADC,这些 ADC 可以独立使用,也可以使用双重/三重模式(提高采样率)。STM32F769 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它有 19 个通道,可测量 16 个外部源、2 个内部源和 Vbat 通道的信号。这些通道的 A/D 转换可以单次、连续、扫描或间断模式执行。ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。 模拟看门2 g: g" H4 I: U& j, }8 @1 [
狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。STM32F769IGT6 包含有3个ADC。STM32F769的 ADC 最大的转换速率为2.4Mhz,也就是转换时间为 0.41us(在 ADCCLK=36M,采样周期为3个 ADC 时钟下得到),不要让 ADC 的时钟超过 36M,否则将导致结果准确度下降。
- \* z, _9 {5 C' D' S# KSTM32F769将ADC的转换分为2个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。
* p" N) Q* ?* e% c# d1 Z- ^; I1 R
/ N0 _& i3 j7 X* I& i2 i
ADC 的主要特征:0 G" ? b' [6 V! H
9 X& r3 r6 j( E" [可配置 12 位、10 位、8 位或6 位分辨率* E- ~3 z- K2 Y" d: ^# D5 a/ G7 O
在转换结束、注入转换结束以及发生模拟看门狗或溢出事件时产生中断0 t, d: R' y5 T0 c" m: r
单次和连续转换模式 @! J0 V$ n( Z" v( N2 B9 k
扫描模式,自动转化通道 0到通道 n数据. A m7 X9 Z( {% ~: v
数据对齐以保持内置数据一致性' Q& m% d* N+ h# l% v
可独立设置各通道采样时间: T- |4 U3 e( x& O' ~
外部触发器选项,可为常规转换和注入转换配置极性, ~" \4 _' z4 s; i4 X1 D3 U
不连续采样模式2 f5 R% r; K; T7 Y7 }3 v
双重/三重模式(具有2个或更多 ADC 的器件提供)
/ `# ]3 D5 q u. N+ ?5 }双重/三重 ADC 模式下可配置的 DMA 数据存储
2 L0 `& b9 m5 v' L7 q双重/三重交替模式下可配置的转换间延迟
- Q# y l9 T) [ADC转换类型(参见数据手册)$ S+ \! y: @7 b$ C
ADC 电源要求:供电在 2.4V 到 3.6V 下可全速运行,供电低至 1.8V 时为慢速运行
! Z+ R0 j7 I" a( O6 v4 eADC 输入范围:VREF-≤VIN≤ VREF+9 J5 j/ T% o/ J/ \" C* O
常规通道转换期间可产生 DMA 请求: |$ T$ P, X; c- {+ S
3 n$ u1 ?& P1 Q* k d/ H
0 W0 D, s: [/ o二:STM32 cube MX 软件的配置如下所示:. Z3 W" E. {2 q* @1 I/ V: X
1 {) q0 O, O8 N, a, f) Y6 [6 D1 a8 _: y, @" z) n5 R
三:STM32 cube MX 软件生成代码如下所示:/ I ]+ Q9 ?+ w5 M8 x
3 h& J# U4 ^# n- hadc1.Instance = ADC1;. z, C, K9 X% ]8 ]1 i9 d
- hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
8 P9 j! G# j+ n1 y7 F - hadc1.Init.Resolution = ADC_RESOLUTION_12B;" K% f ~6 d0 c9 G1 V/ O, |
- hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; D& z# w) X: i1 F4 V
- hadc1.Init.ContinuousConvMode = ENABLE;! x3 m1 C$ e U4 n
- hadc1.Init.DiscontinuousConvMode = DISABLE;
, F" H3 w2 R& I0 ^ - hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
' @1 F0 P' n# k - hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
9 V/ p) ^$ L, S/ V6 H - hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
/ _; T6 z* B# E: L - hadc1.Init.NbrOfConversion = 1;: O9 u- v2 Q1 X) A% @0 ?
- hadc1.Init.DMAContinuousRequests = ENABLE;
! s8 |8 \9 q( U+ u: ~+ b - hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
+ C/ k/ R6 h; N- B! L# A - if (HAL_ADC_Init(&hadc1) != HAL_OK)7 E1 K' [" k* H) |
- {
" w6 L( k. N3 f$ v* S - Error_Handler();3 ?0 S' ^: h! K- R- C2 J" l
- }
复制代码 上述软件代码是软件配置之后,自动生成的功能,然后我下载之后,发现代码进入的 void NMI_Handler(void) 错误,由于我使用的是DMA的方式接收的ADC的数据,所以DMA还在正常运行,也是搞不懂为什么,串口输出也不正常。8 y* ~! A | m6 i
然后我查询了下代码,将ADC的初始化修改如下所示代码:
$ q( @2 s2 ~9 U. }" g- hadc1.Instance = ADC1;
2 o, h+ W F8 R R - hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
8 h j9 M$ o" V - hadc1.Init.Resolution = ADC_RESOLUTION_12B;
) j: d: H l# ^" v) ~ - hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;8 Z0 }% @9 `& G0 w6 |4 l+ \" v
- hadc1.Init.ContinuousConvMode = ENABLE;
] u, W0 @4 @$ H- h) i2 [, { - hadc1.Init.DiscontinuousConvMode = DISABLE;& [9 a9 T' z% L5 V
- hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;5 t: s8 B; ~9 C$ d/ z; [( B
- hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;4 G0 e7 a2 ?% ]
- hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;: G1 w0 l# N$ B! j$ C8 _, A
- hadc1.Init.NbrOfConversion = 1;# W: g* C' n; W; Q5 r# g
- hadc1.Init.DMAContinuousRequests = ENABLE; Y( H! S6 L" \. D
- hadc1.Init.EOCSelection = DISABLE;
& v. h; [, Y1 @- B: z - if (HAL_ADC_Init(&hadc1) != HAL_OK)
! {* E Y+ S+ r3 ^8 |/ T - {
4 J- c& _- c1 c2 J; N( x - Error_Handler();# S' u; [* q6 G0 ~
- }
复制代码 下面是ADC 结构体的的变量解释0 ?* x; ~, H: [- S! n# x# h s
- typedef struct{# M7 w( H4 p% C N& h" C
- uint32 t ClockPrescaler;//分频系数 2/4/6/8 分频
复制代码 这里发现是 cube MX 软件配置中对下述变量配置不太一样:4 A3 N# K( K3 l# d+ y4 F. ]
- hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV; % L. u2 Q9 b2 v8 E5 @& _: L" G
- hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
复制代码 配置好ADC代码后,我们就直接读取ADC的数据就可以了9 X8 q2 q$ }- B+ T' M0 d: \
主程序代码如下:) _8 L: T3 D# _& L. ~* Z# F6 @
- HAL_Delay(200) ;
- o( J- H+ @* L. `% o - HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_13) ;( L, H1 _) z4 J6 d W1 C/ F
- // HAL_Delay(200) ;( C& }! S- n8 V+ s
- // HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_5) ;
, H# e5 I7 ]7 `. g( \7 e7 B+ @ - // HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_4) ;8 a1 `# ]: r2 I' q! }+ c0 W ^6 z
- // HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_12) ;7 X- m, K& ?. ^6 I/ e0 l
- if(usart1_flag == 1)
! C! E0 P% M8 H9 i* s - {" t2 Y* q Z* e n: c6 K5 X
- usart1_flag = 0 ;- E* S. p! {3 _& d
- HAL_UART_Transmit(&huart1,Usart1_DEAL_RX_Buf,usart1_rx_len,100);
0 j- s+ T2 t# I% o9 K - // printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n");
4 m# ? q7 V8 S$ {( R3 f. K- U
" ^* q9 Z! r/ h( ` R, L- printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n" );# j2 C; U c8 @
- }
1 \3 t6 {& S6 O: M( s6 Y - & _# E+ ?; _2 H7 P7 r
- printf("ubADCSample data = %5d\r\n" ,ubADCSample);
复制代码 PC截图如下所示:
1 Y+ w; G$ E: K+ p1 c
! I6 A) v. Y+ a0 |% v" m
; V9 L- Z; w* A) {/ n调试感想如下:
7 n7 n1 O1 E0 [7 u# k- ]7 U虽然使用STM32 读取了ADC的数据,但是数据飘动还是比较大了,对于工业控制并不是很好,所以这里我推荐以下几种处理方式:
& _0 r. P. s1 @6 j' w& ]1:中值滤波方式:
5 t6 ]; `& [6 w8 D$ S" v* j. j主要是程序在执行的时候,连续采集N次(需要注意下这里下,这里的N必须取值奇数),程序需要按大到小或者从小到达的顺序进行排序,然后取中间数值做为有效值。% {' E7 s1 N1 D/ J
- int MidValueDeal(int N)
8 T* N% Y; i* v! p+ i" Y - { 7 p. D: [5 a0 |4 j- R! n9 P
- int value_buf[N]; int i,j,k,temp; * S& u5 L1 Z% k3 j- [9 ^+ o# H7 }( d
- for( i = 0; i < N; ++i)
1 ~0 y5 _( h2 \ - {
! A( O3 V7 S$ ^$ O6 _" R9 \4 l - value_buf[i] = HAL_ADC_GetValue(&hadc1);
) o" G" h. z* r. ?7 g - }
& M; o0 u; w) f; J- H - for(j = 0 ; j < N-1; ++j)
6 i: F3 ^3 C7 A# W - {
- G9 D% Q" I7 w ^9 |4 d5 U - for(k = 0; k < N-j-1; ++k) , {6 R! Z' z! R& G) R2 V% H
- { //从小到大排序,冒泡法排序
, \. [% Y3 Y' o6 ^- l) S$ k- X5 p3 L0 D+ @ - if(value_buf[k] > value_buf[k+1]) 4 a& p* Y4 K# o/ E4 J9 B
- {
$ C1 F/ X# e2 V7 U - temp = value_buf[k];
( K; V& t9 r# n" o$ D, g* a" P - value_buf[k] = value_buf[k+1];
8 k: u F& }% { U, R - value_buf[k+1] = temp;
) H6 ]. I$ d8 |' O1 L3 e - } , j' t8 p; o: U; e2 f1 Y0 M
- } & f6 q( E8 P8 n* G* e% {
- }
! M0 c# b. R9 Z- |$ z* p - return value_buf[(N-1)/2];% H) ~; I% F) s* M
- }
复制代码 2:算术平均数滤波:连续取值N个数据,对所有的数据进行取平均值;
: f: V$ m: c0 S- int AverValueDeal(int N)
; E) Z0 ~: `4 Z! x0 z" Q) p - {
5 _1 B( `8 u8 P' d - int sum = 0; h, Y/ u: U+ v r% [
- unsinged short i;
# L, u% e/ R/ E - for(i = 0; i < N; ++i) " P3 H( _5 j% U2 P
- { 1 v' F" Y' i$ B. n4 G8 r8 j1 K
- sum += HAL_ADC_GetValue(&hadc1); + ~ \0 s, Y2 s7 g" Y3 [8 |% V& g
- }
. M6 I9 \( ?. P$ ]8 K; ^) L# j - return sum/N;- q' ^9 _3 S* ], g- `1 U+ y
- }
复制代码 相比之下:STM32F769的硬件上面没有过采样的硬件配置,对于STM32U083单片机,已经支持硬件的过采样功能。当时测试时候发现,经过硬件处理之后,数据稳定性得到提高。1 }8 l; P0 x/ C- Q0 @: R
不过即使没有硬件的过采样,我们依然可以使用软件模拟过采样、滤波采样的功能,对数据进行处理。
* g Q+ h. ]! p l. z9 ]# F* U( U: P0 ]- R$ K1 @
4 x* j0 Z: e: M
最后,大家在调试ADC时候有什么好的建议和想法,麻烦评论区留言。3 F2 ~4 E# P) U" ]7 l
|
用标准的基准源进行观察。打印出ADC的值,使用分析软件进行分析评估。
不知道是不是我配置的问题,这个AD的数据,感觉还没有U083单片机的采集稳定
需要进行滤波算法进行处理吧。这么高的主频处理起来应该不难。但是这个12bit的ADC,他的应用场景不是高精度的ADC采集。如果想稳定,最好使用专业的ADC吧。
[md]嗯,这个正好有个机会研究一下,,感觉不如U0系列的好呢😄
这个也不能这样说,F769在当年可以说是高档产品了。他还有其他的功能,综合性能比U0要强很多。但是U0的使用的内核比F769要新,在能耗性能比上要比F7xx要好。各有所长。