概述
+ X9 o" y5 h. |+ z直接存储器访问(DMA)用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输。可以在无需任何CPU操作的情况下通过DMA快速传输传输。这样节省的CPU资源可供其它操作使用。
8 s/ }& F9 d/ r- Q |$ [DMA允许在后台执行数据传输,无需Cortex-MO处理器干预。在此操作过程中,主处理器可以执行其它任务,仅当整个数据块需要处理时,才会中断主处理器。这样即使传输大量数据也不会对系统性能造成太大影响。
3 Z. r0 |1 S' C) r: I1 b/ e) PDMA主要用于为不同的外设模块实现集中数据缓冲存储(通常在系统SRAM中)。与分布式解决方案(其中每个外设都需要实现自己的本地数据存储)相比,DMA解决方案在硅片成本和功耗方面的成本较低。& W* K/ H$ y5 `% Y2 U8 Q
根据使用的产品型号的不同,有一个或两个DMA模块。* B9 q* u) U9 f ]( d
STM32F0XX DMA控制器总共有5个通道用于DMA1,每个通道都专门管理来自一个或多个外设的存储器访问请求。它具有一个仲裁器,用于处理不同的DMA请求的优先级。
) y @$ S# |2 z; _本篇文章主要介绍如何使用STM32CubeMX实现ADC在DMA下快速采集,并且打印出去。1 j3 C0 ^- N w( N% a3 k
' D" b) L/ U$ g" Y
- H$ g d/ }9 t& t硬件准备
" V0 L! y% \& j0 A$ g$ b首先需要准备一个开发板,这里我准备的是NUCLEO-F030R8的开发板:& O; h& U) P0 I: b
3 Y0 N8 l* `; A9 o; K
. k0 j' T% f2 i8 y, O5 |2 I
( P$ a3 s2 E, s5 U选择芯片型号6 a( H) C z: K& U9 }1 c
使用STM32CUBEMX选择芯片stm32f030r8,如下所示:
0 [" x# w% B; W* h) u! P" r
9 D! z0 R q! l7 m$ R! T B
0 q% y% O$ {! D$ w9 ^
! h0 q" P% t* n2 {( M" E0 K配置时钟源( y9 O; ?; b) h2 Y8 M
HSE与LSE分别为外部高速时钟和低速时钟,在本文中使用内置的时钟源,故都选择Disable选项,如下所示:
) j6 Y( B# r# x' \6 i4 W# Q1 i
: V: \ P1 ^" Q- E( a& A
/ m8 q% u4 t1 e- \' p+ c+ q$ I: v
5 ?8 j! S5 g' d2 z9 _配置时钟树% c& Z$ A* N) v c5 I% l0 S
STM32F0的最高主频到48M,所以配置48即可:; M4 }' @1 s5 I$ P1 Z
- p- A2 X7 Q: n" r7 B0 h/ g
7 ~7 Z# S. Q% [5 O/ E( u8 W' f
: f4 m2 V( `+ n/ L
串口配置. b, L8 [3 q( [7 z5 i% i
本次实验使用的串口1进行串口通信,波特率配置为115200。1 q# u+ Y/ |) k+ w4 }3 ~
: H2 y- \( e. l$ C* t! Y- L: ?+ R
8 y* d! f6 v, x6 C
' \3 W( V/ V4 B* t# h/ I( nADC配置! W G8 @* r5 Q5 h6 Q( g8 ~' y" h% S
STM32f030中,有一个ADC(模拟/数字转换器),每个 ADC 有 12 位、 10 位、 8 位和 6 位可选,每个ADC有16个外部通道、2个内部通道和一个VBAT 通道的信号。 T q; l$ W9 p! f/ q
本文将开ADC的IN0、IN1、IN4一共三个通道,来分别读取ADC,由于串口2和IN2,IN3复用,故不使用IN2、IN3。配置如下:
9 q3 [2 v* U( y% T0 E$ W2 H
9 [0 ^- B, C/ M8 I; [
) _+ g0 E; z0 E9 N @; T; J# @+ {3 f4 l* Q/ \ ]4 q
开启DMA:
* u- K# ^: u" J( k L, ~$ I# z; G" G
/ |5 r) O3 k" J. T0 x7 [7 w( B9 m
2 R' [; ] T c: s打开中断:+ J4 P2 e6 J5 J- h8 R
* f v' b4 W* p5 n9 Q
6 @% M% N- q1 P: E8 d* r/ d3 A
/ y" u U F) ? v- N7 D
生成工程设置
/ g7 P! e/ B2 G- z! q! J1 b最后设置生成独立的初始化文件:
6 x3 R6 [5 C4 X/ X* l! [; L- X- T( |
- v) {. p: |7 X7 l t+ @$ H$ b! H7 i6 Y0 B. ~) ^. i- D# n$ E
生成代码
& @: f4 ^ o) Q k% W$ x5 M7 ?3 n' ~0 K# _/ P, H
: ]5 L# `' Q# s
" w! O/ E) B9 \# G: t配置keil
. `. v9 U+ Q! v1 `, b% }$ T1 r: I( w Y* @- i
6 s* t# c/ s8 S _# A B1 r6 u3 u; @0 R8 C5 i
代码
7 V2 W, [+ C0 A在main.c中,添加头文件,若不添加会出现 identifier “FILE” is undefined报错。
+ X) e$ v2 a) E$ a7 P5 G3 b2 X) o! g' @, B- X* m E0 \1 j
- /* USER CODE BEGIN Includes */
' M! a+ A; X( t! h% g! T# b" _7 U - #include "stdio.h"1 e' F( O7 G: f) `" Z6 x4 G, @- W
- /* USER CODE END Includes */
复制代码
8 V8 S! x3 e6 b- P% k函数声明和串口重定向:, p: @! N4 Z; B# B
. U: G7 a2 j* x- /* USER CODE BEGIN PFP */
6 w' [* }, S8 p- k* Q& n8 ? - #ifdef __GNUC__8 \' B/ b) c* T7 L7 a+ W) i
- #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)# Z) H7 o& G& b% c r
- #else" Y) `$ `1 f0 N' h
- #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)& E1 C2 [( \3 } o1 T6 Y
- #endif /* __GNUC__ */
$ s9 [1 u+ n+ R' N5 S5 A - /* USER CODE END PFP */
复制代码
2 g: i# y0 z0 b/ v* I2 m, t" Z* D8 n- /* USER CODE BEGIN 0 */
! W; \( _* j z - PUTCHAR_PROTOTYPE) g$ ? G" r5 r1 Z0 r7 w) V
- {
J0 l; n6 n- \( [) ]& z - HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
8 h+ n0 ]5 `4 d3 X5 R - return ch;. d1 |/ Z4 I, z5 s( ^& J) \. E3 k2 s
- }! \* I3 M! L- V$ Q
- /* USER CODE END 0 */
复制代码 6 U7 T3 v+ {. ?- h
变量定义:
% H; I) l) Z, K' w/ {( T$ z4 U" g, p# u5 q" `" W; {3 P/ Z
- /* USER CODE BEGIN 1 */
0 `4 N7 ?4 z% R+ y$ u - uint32_t ADC_1, ADC_2,ADC_3;" o4 S& Z! u! K5 H8 S
- //各采样30次,故30*3为90
3 W$ R9 {/ f* S0 A3 X - uint32_t ADC_Value[90];* }& q9 d0 i2 n* n
- uint8_t i;; S$ M' o1 `9 J: i& o
- /* USER CODE END 1 */
复制代码
2 G' }8 }% O" R) V7 q使用DMA传输:! h+ t7 j/ z# }8 _
# r8 t9 M* ?& E
- /* USER CODE BEGIN 2 */
; R+ s8 a3 g9 o. K - HAL_ADC_Start_DMA(&hadc,(uint32_t*)&ADC_Value,90); //使用DMA传输% t3 d3 y* C% l
- /* USER CODE END 2 */
复制代码 % ?* o! | q. S4 c5 f
主循环:
. l4 c/ S2 _ F) f! Y% A3 Z% F) n! c( t( }& v5 @
- /* USER CODE BEGIN WHILE */
6 y) s. N5 F2 l% A$ _3 e - while (1)
/ I' X% S( C1 I T8 \ - {# H) [4 ?9 r8 D* i0 k
- //放个延迟,防止程序运行第一次读出数据有误( b2 d- R+ r+ G$ |
- HAL_Delay(100);
- m ?0 j2 Z6 L - ADC_1=0;
3 r+ s% W* B4 }8 Z9 W" _ - ADC_2=0;
7 _- Q2 I$ A0 _: K5 x - ADC_3=0;* t$ P3 K, C+ t3 r
- for(i=0,ADC_1=0,ADC_2=0;i<90;)( x/ J. f; X, \$ ^0 Z* a2 q& X
- {6 z5 N, f# M" X2 o4 _
- ADC_1+=ADC_Value[i++]; $ u! {! X" L8 h- Q% F7 i
- ADC_2+=ADC_Value[i++];
B1 ~1 |# a7 a# t& A6 X" U - ADC_3+=ADC_Value[i++];
2 v( |+ v+ A6 R5 p7 B* l - }
+ U; k# o2 M7 ^# G3 v - printf("ADC数据如下\n");
6 ?4 O% m7 O3 d F9 t - //除以30为求30次平均ADC值,乘以3.3为以3.3电压为基准,除以4096为ADC配置为12位4 e! F" x0 o3 o: U- h4 }
- t; [2 s0 d" `
- printf("ADC_IN0=%1.4f\r\n",ADC_1/30*3.3f/4096);' [2 p) ]5 U7 G+ [* e) J2 V2 @; `
- printf("ADC_IN1=%1.4f\r\n",ADC_2/30*3.3f/4096);1 Y# A& C' ]/ i
- printf("ADC_IN2=%1.4f\r\n",ADC_3/30*3.3f/4096);
# f! M! l7 ]$ v5 {/ c1 p# p - /* USER CODE END WHILE */
* _' p1 A. o A - , M% _, l O, q/ X' O% X& d% j
- ; t" S# Z0 a1 g
- /* USER CODE BEGIN 3 */9 c, o" z& v9 c2 l( i: k
- HAL_Delay(900);
) U9 z0 @& Q9 ^1 L) w& O$ m# f - }
' W. [( f j0 K+ m8 | - /* USER CODE END 3 */
复制代码
/ [) \& { a3 k+ q2 O* z演示效果
3 T( w; ~: S7 n设定adc1口接3.3V,adc2口接GND,adc口接1.5V,输出如下。
w e& @2 d% P& g
% A% w5 S8 G2 |, t$ `" K! P+ y# G a/ Y. B& t$ B" U
1 \7 d8 D0 y) k! S
2 @0 M& v3 R$ F" T" A# |( ~/ P5 G4 p5 }% H. r: @+ K
5 A1 |0 U+ V- d5 `$ f i |