今天和大家分享一下使用STM32F769制作的位移采集装置 Q& X% M! t. a( E
一:STM32F769 ADC 知识分享:
$ q' |* {( M6 O6 W
% s4 Q5 v) J" k# D8 p* C' GSTM32F769xx 系列有3个 ADC,这些 ADC 可以独立使用,也可以使用双重/三重模式(提高采样率)。STM32F769 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它有 19 个通道,可测量 16 个外部源、2 个内部源和 Vbat 通道的信号。这些通道的 A/D 转换可以单次、连续、扫描或间断模式执行。ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。 模拟看门
6 s5 |+ G+ B) F' L狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。STM32F769IGT6 包含有3个ADC。STM32F769的 ADC 最大的转换速率为2.4Mhz,也就是转换时间为 0.41us(在 ADCCLK=36M,采样周期为3个 ADC 时钟下得到),不要让 ADC 的时钟超过 36M,否则将导致结果准确度下降。) g# r" |1 h7 p4 O$ B
STM32F769将ADC的转换分为2个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。 ~ B% c W% ^9 J+ N; N8 |
% w4 v7 J, p/ h; v8 H& B
$ | W2 }1 p( h9 Y3 L
ADC 的主要特征:3 }. `# ]0 ^6 z: S
& Q) W. O# N* R- z2 D9 o5 V/ {' l
可配置 12 位、10 位、8 位或6 位分辨率8 l$ o# ]" D. J
在转换结束、注入转换结束以及发生模拟看门狗或溢出事件时产生中断$ ^' F2 h8 r5 Q7 T. {4 @1 o
单次和连续转换模式# E2 z" Y3 V$ q) @! p: P
扫描模式,自动转化通道 0到通道 n数据& Q+ |! w8 V' D% Z3 O9 ]
数据对齐以保持内置数据一致性! G* }/ {8 M4 _2 o
可独立设置各通道采样时间
7 c2 {& q$ S, E' }+ |0 I外部触发器选项,可为常规转换和注入转换配置极性3 l: `. j$ y+ j) Y, e
不连续采样模式9 q7 u8 S- o3 A! m% A- w5 |
双重/三重模式(具有2个或更多 ADC 的器件提供)# ?) j" g: f4 @4 D' u1 X
双重/三重 ADC 模式下可配置的 DMA 数据存储
0 w! T+ ?' b5 K+ K双重/三重交替模式下可配置的转换间延迟8 I3 I0 i8 b) l/ w$ z3 J6 S
ADC转换类型(参见数据手册)
^7 V- c. g6 c/ ]2 iADC 电源要求:供电在 2.4V 到 3.6V 下可全速运行,供电低至 1.8V 时为慢速运行
/ s' N6 O' S3 J% r2 X: xADC 输入范围:VREF-≤VIN≤ VREF+& w, `2 Q- h7 O* P7 s
常规通道转换期间可产生 DMA 请求
7 X+ O7 ?7 \; y+ R( |2 y) ^1 Q9 K& Z3 j
6 Y# y) z( v5 q; [, T/ ]/ w4 E二:STM32 cube MX 软件的配置如下所示:
7 ^1 o+ { H+ |" M" i4 \
0 g6 L; I; N) e; k& Z
! ~6 o" _/ \' }" r: a/ O+ o三:STM32 cube MX 软件生成代码如下所示:/ w' G1 w c* w# d4 x9 V
: N0 E0 n: Z+ T0 [+ i
- hadc1.Instance = ADC1;
; G ?' H% V9 ?; u$ n+ b# a - hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
8 i% V) c. I2 o, Q8 B: B6 x - hadc1.Init.Resolution = ADC_RESOLUTION_12B;( R3 m y" X. b* O3 G, {2 X) h
- hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
3 a9 ^6 @ Y0 u2 H" L - hadc1.Init.ContinuousConvMode = ENABLE;# Y9 ]# g' H0 p; N; x: x( b
- hadc1.Init.DiscontinuousConvMode = DISABLE;. k. i9 g$ n/ b) o; z2 l
- hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;8 Z" G* [9 g8 K" V" ?1 _# N
- hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;% `$ G* Z) n: a3 F
- hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;% i# h: N, z# o3 U* a& S# Y
- hadc1.Init.NbrOfConversion = 1;9 u; s- I4 L3 h! }3 ?0 L
- hadc1.Init.DMAContinuousRequests = ENABLE;
" X1 L0 K9 c% f7 F3 H1 N - hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;' y1 ^! \1 y: Z9 Z
- if (HAL_ADC_Init(&hadc1) != HAL_OK)
; C! f! Y2 k. X - {% s: p7 d5 e8 w3 G! g$ [+ v& l, s
- Error_Handler();* F: ^2 C% b5 Y# M& b6 r# L
- }
复制代码 上述软件代码是软件配置之后,自动生成的功能,然后我下载之后,发现代码进入的 void NMI_Handler(void) 错误,由于我使用的是DMA的方式接收的ADC的数据,所以DMA还在正常运行,也是搞不懂为什么,串口输出也不正常。
% z* O7 A! [# e然后我查询了下代码,将ADC的初始化修改如下所示代码:* N; Z% _' X. Y9 e$ W: u
- hadc1.Instance = ADC1;
" J# w% {% E( B+ ?$ r - hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
8 M/ S# K+ m f+ x I, ~) L. v - hadc1.Init.Resolution = ADC_RESOLUTION_12B;5 k9 H5 I% o/ {# r5 z; K
- hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
6 m2 m, L2 y- m$ d1 f) g) W9 c - hadc1.Init.ContinuousConvMode = ENABLE;- s3 ^' R6 U4 ~; t
- hadc1.Init.DiscontinuousConvMode = DISABLE;9 e( y, z( f* D* }; A @
- hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
, g- ~* n- l3 U- u - hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;+ K. m7 A. }, {" F! L
- hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
! e; M0 Y0 x9 a, i" F! d! J/ v - hadc1.Init.NbrOfConversion = 1;4 n' ?( r- n) j+ L0 v
- hadc1.Init.DMAContinuousRequests = ENABLE;0 R$ {/ v5 F0 k8 {6 V
- hadc1.Init.EOCSelection = DISABLE;
% X. t5 Q# h' l - if (HAL_ADC_Init(&hadc1) != HAL_OK)
" Z4 r1 V3 h& W5 Q5 y$ [* O2 ^ - {
. J- p9 R4 P; D1 ` - Error_Handler();. ?, w( s, d0 h+ v
- }
复制代码 下面是ADC 结构体的的变量解释; ]2 w% S r4 @7 o5 \: y3 ~
- typedef struct{
5 B# V+ V4 ?3 [, ?9 n - uint32 t ClockPrescaler;//分频系数 2/4/6/8 分频
复制代码 这里发现是 cube MX 软件配置中对下述变量配置不太一样:. U9 h. Q+ o% s+ s
- hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
2 M' M6 o) Z4 p4 U' t - hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
复制代码 配置好ADC代码后,我们就直接读取ADC的数据就可以了, I0 Y1 c8 T4 Y
主程序代码如下:9 [( ?& W1 t$ K0 m
- HAL_Delay(200) ;0 n; ]: d: D3 A1 I0 v. ]8 }5 C
- HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_13) ;" n6 Q& Z5 S: _$ j9 E$ w
- // HAL_Delay(200) ;
) A: d$ t* k5 O; K - // HAL_GPIO_TogglePin(GPIOJ,GPIO_PIN_5) ;( F/ t% F" m G
- // HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_4) ;
7 [% } p- h# n7 H! t+ p - // HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_12) ;
p" a: M b" N9 w% b - if(usart1_flag == 1)
, v/ ^. a" f b - {2 A8 C3 d5 B; s& a, ?% h% l
- usart1_flag = 0 ;
" Y8 x* [* X7 u* D( D - HAL_UART_Transmit(&huart1,Usart1_DEAL_RX_Buf,usart1_rx_len,100);- Y0 ^; _* c& T
- // printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n");6 S2 C+ _. P3 S- \3 I
- - a. o- C9 R) S0 m
- printf("Hello STM32! Hello congcong! Hello STM32f769!\r\n" );
5 m X0 b' O" a' ?5 H* `3 G - }/ j' \+ E6 M2 e; E
9 s) {8 h5 U# k+ p' `, d/ n- printf("ubADCSample data = %5d\r\n" ,ubADCSample);
复制代码 PC截图如下所示:
* v* n7 w. E7 G& u* E8 S; A0 x
( C# J1 C! A2 d5 w- u% `8 g9 l( s; a" ^) e; [* r
调试感想如下:. O, ?) h& }* S4 _( }9 t
虽然使用STM32 读取了ADC的数据,但是数据飘动还是比较大了,对于工业控制并不是很好,所以这里我推荐以下几种处理方式:
! o6 @* v4 ?0 i* ^, d1:中值滤波方式:. v6 E! c+ U w& j
主要是程序在执行的时候,连续采集N次(需要注意下这里下,这里的N必须取值奇数),程序需要按大到小或者从小到达的顺序进行排序,然后取中间数值做为有效值。
[/ y( w2 B" `) g: y- int MidValueDeal(int N)" q1 Y) q& g$ G9 m5 Y* A0 D A: D
- { 2 X6 ^, B8 i( O* P$ y/ e* g
- int value_buf[N]; int i,j,k,temp; ) z: x' Z4 h- E E
- for( i = 0; i < N; ++i) " y, y. W; d1 D
- {
@5 R$ C( b9 x, b+ q! A - value_buf[i] = HAL_ADC_GetValue(&hadc1); 3 k8 l0 P% m5 G% G6 |% m
- }- V8 `3 _& [& y4 f2 {& C* x5 [
- for(j = 0 ; j < N-1; ++j) # \0 D* i# ]: r2 o) Z
- { 4 e6 Z* U) w. ~4 t
- for(k = 0; k < N-j-1; ++k) * [/ ]& ~1 a7 I6 ^% w ?1 {+ s
- { //从小到大排序,冒泡法排序
9 ?" I$ @5 ~4 z; G6 Q - if(value_buf[k] > value_buf[k+1])
4 ]" d: C) D7 Y, b - {
3 O7 {1 A' U' {8 n7 T- a - temp = value_buf[k];
. N4 t1 ^! t. v m, f - value_buf[k] = value_buf[k+1];
0 \& c' W3 v) O4 e* R1 J* L1 }5 Y - value_buf[k+1] = temp;
. }$ R9 B, |2 n: D: t+ I9 M& G" ` - }
& @+ |$ A0 c$ s/ }, d - }
# S0 b1 R. ?& V6 } - }
& G# K$ b+ h# {2 I& g - return value_buf[(N-1)/2];
! w* e& Q/ c7 ~ f; U3 \ - }
复制代码 2:算术平均数滤波:连续取值N个数据,对所有的数据进行取平均值;1 \) @9 t; S1 \
- int AverValueDeal(int N)) E1 n+ O2 G4 m9 y9 U8 o* T, G
- { " z! B& B8 `4 L3 y8 m) e
- int sum = 0; ' U: o, M& b( i( Y& f. p4 S
- unsinged short i; 5 F0 z4 S' W5 H- |& L
- for(i = 0; i < N; ++i)
$ ^3 k6 q5 o( }) j( V* E, c8 ^3 r9 m - { * Z" g* ~; v# s) @2 w* |& x4 n
- sum += HAL_ADC_GetValue(&hadc1);
2 t' s$ e! o. \! r _6 z' s - }
' i5 g/ Y) m" q" v - return sum/N;( c k+ U' Y, G# m2 |
- }
复制代码 相比之下:STM32F769的硬件上面没有过采样的硬件配置,对于STM32U083单片机,已经支持硬件的过采样功能。当时测试时候发现,经过硬件处理之后,数据稳定性得到提高。
2 L3 e2 s) _. s! K2 X0 i2 L不过即使没有硬件的过采样,我们依然可以使用软件模拟过采样、滤波采样的功能,对数据进行处理。$ m3 Q$ I* d6 I" A7 A9 g2 G
! _& v; q% n5 E7 W
# o/ G$ R8 O7 k. s) L+ G最后,大家在调试ADC时候有什么好的建议和想法,麻烦评论区留言。
1 o. z; A+ x! ~) v5 j- O |
用标准的基准源进行观察。打印出ADC的值,使用分析软件进行分析评估。
不知道是不是我配置的问题,这个AD的数据,感觉还没有U083单片机的采集稳定
需要进行滤波算法进行处理吧。这么高的主频处理起来应该不难。但是这个12bit的ADC,他的应用场景不是高精度的ADC采集。如果想稳定,最好使用专业的ADC吧。
[md]嗯,这个正好有个机会研究一下,,感觉不如U0系列的好呢😄
这个也不能这样说,F769在当年可以说是高档产品了。他还有其他的功能,综合性能比U0要强很多。但是U0的使用的内核比F769要新,在能耗性能比上要比F7xx要好。各有所长。