今天和大家分享一下使用STM32F769制作的位移采集装置9 M2 C3 R2 r7 J, B3 L. d
一:STM32F769 ADC 知识分享:" f7 J' J9 q! M7 H" A0 L
1 p: {+ y) U3 [& U( i0 HSTM32F769xx 系列有3个 ADC,这些 ADC 可以独立使用,也可以使用双重/三重模式(提高采样率)。STM32F769 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它有 19 个通道,可测量 16 个外部源、2 个内部源和 Vbat 通道的信号。这些通道的 A/D 转换可以单次、连续、扫描或间断模式执行。ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。 模拟看门: G+ Z, O; s* D- {( ~4 m
狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。STM32F769IGT6 包含有3个ADC。STM32F769的 ADC 最大的转换速率为2.4Mhz,也就是转换时间为 0.41us(在 ADCCLK=36M,采样周期为3个 ADC 时钟下得到),不要让 ADC 的时钟超过 36M,否则将导致结果准确度下降。+ O# w! K* r2 `/ w! U
STM32F769将ADC的转换分为2个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。8 P! \: Z$ E) E$ S, F+ N: P" ^
' W% J2 I; ^; n8 y+ E
. S8 i* D5 n+ N# _8 s1 dADC 的主要特征:6 U2 u9 K# [5 n. [8 _0 f3 F2 @' |
7 H& U: R" l R+ j% m7 K
可配置 12 位、10 位、8 位或6 位分辨率5 S, h4 `, Z# b5 M& g
在转换结束、注入转换结束以及发生模拟看门狗或溢出事件时产生中断8 b) C6 m4 G+ |9 s
单次和连续转换模式
' }- z7 [4 G' Y9 N/ N扫描模式,自动转化通道 0到通道 n数据
, a3 K# ^4 v! {' f1 q数据对齐以保持内置数据一致性2 v2 F( U4 @ a" ? k$ d3 d, ?
可独立设置各通道采样时间
1 m, N9 U5 {& ~! J7 }5 @: _/ ~外部触发器选项,可为常规转换和注入转换配置极性
1 ~7 N& R% T2 P& C# A7 D不连续采样模式# ^3 B1 g( s# b$ {3 I
双重/三重模式(具有2个或更多 ADC 的器件提供)1 X7 ^; U- x* ~ V
双重/三重 ADC 模式下可配置的 DMA 数据存储0 r9 g- `. g' D, A
双重/三重交替模式下可配置的转换间延迟
2 Z4 b! ^& r1 p* WADC转换类型(参见数据手册)
$ o$ U4 z8 T6 u+ I1 ~$ Q3 x. ^ADC 电源要求:供电在 2.4V 到 3.6V 下可全速运行,供电低至 1.8V 时为慢速运行. e' V+ ^2 R% \) Y" x; b* D0 i
ADC 输入范围:VREF-≤VIN≤ VREF+
. C3 K; N3 L3 k+ x6 `7 {6 v常规通道转换期间可产生 DMA 请求
1 F/ x6 K& |! |" P! ~+ `6 S |% ^/ u# L0 O& Q) E; H* h" R4 M* R0 Q
9 P8 F+ k. i5 l$ v3 \/ S( m+ M二:STM32 cube MX 软件的配置如下所示:+ G5 R( y% a! `8 \% @
/ W' U9 y2 e* L3 U+ p/ q3 K
& S, v, B- z' o% \# z3 F# D三:STM32 cube MX 软件生成代码如下所示:
( T9 p2 a* n8 Z3 u: i& @3 z$ E1 Z; {3 _/ ~
- hadc1.Instance = ADC1;4 u& F9 V4 V4 A" C! i5 Z' | K
- hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;8 Q/ v0 T/ x6 ?; q S( w( i7 e$ }. I
- hadc1.Init.Resolution = ADC_RESOLUTION_12B;
4 ? _6 z: ~9 A; x - hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
* R6 ?9 E5 @5 E" e _# [# K - hadc1.Init.ContinuousConvMode = ENABLE;
9 ~( H6 m! r+ w4 D3 R - hadc1.Init.DiscontinuousConvMode = DISABLE;
. R! g8 ~( H) f8 s$ M - hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
5 ?: P o; K: L+ L - hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
) H" _* \! K1 p9 X) t - hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;! g7 g0 o! v# Y& s) N+ E; N( y0 c7 @
- hadc1.Init.NbrOfConversion = 1;
2 ^, z h/ N3 A6 {: }5 {; ~ - hadc1.Init.DMAContinuousRequests = ENABLE;- c! R$ u* I( t N+ ?6 w0 @
- hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;; O4 F8 l. z8 j0 L
- if (HAL_ADC_Init(&hadc1) != HAL_OK)
/ k3 V6 J+ G- [+ X' r @ - {' `! i0 ^( j! B4 x
- Error_Handler();
) `! R8 W) v$ S1 w4 z - }
复制代码 上述软件代码是软件配置之后,自动生成的功能,然后我下载之后,发现代码进入的 void NMI_Handler(void) 错误,由于我使用的是DMA的方式接收的ADC的数据,所以DMA还在正常运行,也是搞不懂为什么,串口输出也不正常。
' g: u% Z9 D7 Q. n- K/ n0 {% u1 R然后我查询了下代码,将ADC的初始化修改如下所示代码:
' Q. S( i% }0 Q8 A; o3 Z- hadc1.Instance = ADC1;
6 y& W. o/ _1 D" H7 J, _ - hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
. W- k f _: O% C( l - hadc1.Init.Resolution = ADC_RESOLUTION_12B;
% m! ?* J$ A/ l1 V# ?! C' v - hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;# [* @% e* B% J3 f# z
- hadc1.Init.ContinuousConvMode = ENABLE;
2 p" ]1 U; B* j - hadc1.Init.DiscontinuousConvMode = DISABLE;
6 s2 q7 Y7 R; t1 |! p+ J2 F! D - hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;. a @; ]1 O3 b
- hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
1 m/ s; w- p1 w6 Q - hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
; U$ k% J$ r: g$ `, | - hadc1.Init.NbrOfConversion = 1;
/ U) t. U, B! q# f+ @ - hadc1.Init.DMAContinuousRequests = ENABLE;' B C2 t4 q! ~. w
- hadc1.Init.EOCSelection = DISABLE;9 k% ~7 j- H* B! z" H4 d
- if (HAL_ADC_Init(&hadc1) != HAL_OK)
# @! o4 i) T5 b4 t - {
6 F$ z% V _% R2 ]+ E2 a/ u$ z$ R - Error_Handler();
' `, X% F, n7 R/ | - }
复制代码 下面是ADC 结构体的的变量解释
/ n. |. G% G* |/ t9 u& l3 L- typedef struct{
# F5 d7 ?* I8 O' w+ \1 G2 x4 N - uint32 t ClockPrescaler;//分频系数 2/4/6/8 分频
复制代码 这里发现是 cube MX 软件配置中对下述变量配置不太一样:! V0 ~( L1 q4 Q5 H1 ^; ~$ H( v
- hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
: t8 V# f0 I4 F( F, w* L8 L - hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
复制代码 配置好ADC代码后,我们就直接读取ADC的数据就可以了9 j# X- R' p! P. @4 j" S
主程序代码如下:* ~$ d1 o" u9 ~
- HAL_Delay(200) ;0 q* p) i; O4 {4 z
- HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_13) ;
. |- F) K$ B/ j - // HAL_Delay(200) ;
D. A7 y" @. s6 _ - // HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_5) ;
5 z/ J7 T* n c8 Q6 Y8 U h' H% m - // HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_4) ;
' z4 ] ~2 `# q# D# ?; u; V - // HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_12) ;8 l7 _# X) c# B9 v
- if(usart1_flag == 1)
% _; \% s" Q) b8 p" b8 H/ s - {
( a; _; j1 z' }: w$ Q. H3 N9 ~% o - usart1_flag = 0 ;) b! @! l. d" E0 @2 n/ b: x
- HAL_UART_Transmit(&huart1,Usart1_DEAL_RX_Buf,usart1_rx_len,100);
; ?! d) b$ c% I - // printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n");) k$ q; k) C; p( h
- , ^8 M5 Z. ?) I4 J+ ?1 r. o
- printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n" );$ Z u. ]# X' @7 t8 o
- }1 \. Z4 h! H/ ?, |, O3 [- X9 J
- ! @. e4 s7 M& J C
- printf("ubADCSample data = %5d\r\n" ,ubADCSample);
复制代码 PC截图如下所示:
: J$ p s4 ?" j$ X1 x$ E6 H3 m
5 Y$ Q. ]7 l) i6 F
: K/ p2 |1 v- b调试感想如下:
0 x& X8 v8 p* H) c虽然使用STM32 读取了ADC的数据,但是数据飘动还是比较大了,对于工业控制并不是很好,所以这里我推荐以下几种处理方式:
& q; F" c# h+ A4 p. ~( A v9 A1:中值滤波方式:' ^) A6 K; w/ I; ?* P% Q
主要是程序在执行的时候,连续采集N次(需要注意下这里下,这里的N必须取值奇数),程序需要按大到小或者从小到达的顺序进行排序,然后取中间数值做为有效值。3 _6 O7 a( j5 Y( ^
- int MidValueDeal(int N)
3 R/ S( C2 u1 A9 R! ~* {# u$ V; ^ I - { 3 p/ f9 y, Z% F: S$ O
- int value_buf[N]; int i,j,k,temp;
3 u) [5 l9 w( ]+ `, Y8 s - for( i = 0; i < N; ++i) 8 k9 ]% t Z) o9 z y/ a" L
- {
! r0 r0 N; e3 w5 ~% P$ j" i6 V* p - value_buf[i] = HAL_ADC_GetValue(&hadc1); - a& I' E, e+ \2 q1 i
- }
. d7 p( ]# x" @' M: k$ f8 N - for(j = 0 ; j < N-1; ++j) + Q% P7 M- o3 A4 ~; c. S
- {
: O+ @$ P: k3 q' G4 b# y5 v - for(k = 0; k < N-j-1; ++k) 4 ?) O" M8 h) ^, j: p9 d& E
- { //从小到大排序,冒泡法排序
; x' v y% o2 a& v% J - if(value_buf[k] > value_buf[k+1])
: w$ V6 a# P" k3 Q# y) p3 ` - { 3 X* ^- m& f' x7 b
- temp = value_buf[k];
8 y1 } V9 t/ l) s0 v1 D- D" L" E - value_buf[k] = value_buf[k+1];
7 k& S4 b8 f9 u - value_buf[k+1] = temp;
+ e/ r2 _5 Q) S; c3 _* u r - }
6 n7 J0 w; N2 B, v - } ) p7 x( a. p% h+ s& T2 e# q: Z6 p) |
- }
$ g0 f4 R4 |* ` - return value_buf[(N-1)/2];
" l+ |, X. c5 ^6 O - }
复制代码 2:算术平均数滤波:连续取值N个数据,对所有的数据进行取平均值;
' H, E7 q4 e U- int AverValueDeal(int N)8 W( p/ T3 q0 D" x+ ], I6 D
- { ( O; p' j0 {1 D1 x' a
- int sum = 0; 3 x% `0 `7 B( r: H2 S
- unsinged short i;
; K5 O" n+ U' P# d - for(i = 0; i < N; ++i)
- S' Q( E4 i7 r' M$ C' d - { & P- p w6 R1 v* p# M1 v- h/ x0 _; p- y! v
- sum += HAL_ADC_GetValue(&hadc1);
# n+ Y$ X5 x( }1 W0 O9 c - }
' T( U. A( H# q4 w0 p6 y - return sum/N;
: v' M5 G& m5 V( V' u1 P; i) X5 n - }
复制代码 相比之下:STM32F769的硬件上面没有过采样的硬件配置,对于STM32U083单片机,已经支持硬件的过采样功能。当时测试时候发现,经过硬件处理之后,数据稳定性得到提高。0 Y9 N- e) x; |4 K/ z) l8 r4 M
不过即使没有硬件的过采样,我们依然可以使用软件模拟过采样、滤波采样的功能,对数据进行处理。- p5 g6 g- L" d G
7 X4 s* W* S1 k$ j
3 C- c* k. [7 v; R, E% |3 Z最后,大家在调试ADC时候有什么好的建议和想法,麻烦评论区留言。
/ R( D) `, n- l- T, s# B H8 l |
用标准的基准源进行观察。打印出ADC的值,使用分析软件进行分析评估。
不知道是不是我配置的问题,这个AD的数据,感觉还没有U083单片机的采集稳定
需要进行滤波算法进行处理吧。这么高的主频处理起来应该不难。但是这个12bit的ADC,他的应用场景不是高精度的ADC采集。如果想稳定,最好使用专业的ADC吧。
[md]嗯,这个正好有个机会研究一下,,感觉不如U0系列的好呢😄
这个也不能这样说,F769在当年可以说是高档产品了。他还有其他的功能,综合性能比U0要强很多。但是U0的使用的内核比F769要新,在能耗性能比上要比F7xx要好。各有所长。