概述
' Z' ?" `& r8 }+ K' `本篇文章主要介绍如何使用STM32CubeMX移植到雅特力AT32F403AVGT7,并通过双串口输出打印。
2 ]4 B0 k2 O+ _/ V+ }直接存储器访问(DMA)用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输。可以在无需任何CPU操作的情况下通过DMA快速传输传输。这样节省的CPU资源可供其它操作使用。
9 i, ~- l( a2 J! ?7 _6 x/ @+ }, S3 Z( r: E( u
DMA允许在后台执行数据传输,无需Cortex-M3处理器干预。在此操作过程中,主处理器可以执行其它任务,仅当整个数据块需要处理时,才会中断主处理器。这样即使传输大量数据也不会对系统性能造成太大影响。
$ c, P! L/ E A. _/ n5 \9 k: {3 t# D2 e- ]/ T8 N3 m
DMA主要用于为不同的外设模块实现集中数据缓冲存储(通常在系统SRAM中)。与分布式解决方案(其中每个外设都需要实现自己的本地数据存储)相比,DMA解决方案在硅片成本和功耗方面的成本较低。
! s7 p& |+ O/ _. i7 n/ _
! L0 y; a' u* k4 X" B根据使用的产品型号的不同,有一个或两个DMA模块。
# }5 ~! C' i; ~$ \% r Q4 ^. Q! L# {8 t* j
本篇文章主要介绍如何使用STM32CubeMX实现串口DMA读取,并且打印出去。
, N6 H; h- S- P1 |5 J
, |! {* R9 f! [, H& J; n
+ T- [2 W- X7 G. y" H0 }2 s9 w& C. v) K3 h
硬件准备
% f5 g, S6 b X& z! D. S, U首先需要准备一个开发板,这里我准备的是雅特力AT32F403AVGT7的开发板:$ }9 {. m7 g: b* `( U! V
$ T6 D; m5 P% w5 S% h$ l5 m8 S6 g
7 i3 E' y8 @1 j5 Z
0 x; ~1 V* r( L. l, f, {* Z选择芯片型号
4 b$ ?4 I2 j( Z; {* o雅特力AT32F403AVGT7兼容STM32F103系列,故选取STM32f103VG进行开发。
6 Y! u Q1 W6 N0 w/ b3 i% |. ]
; F8 {" \# Q; i: x I0 S0 @! f
; o$ W8 ~9 ^0 a9 i
; F/ {; U( Y m! j
配置时钟源& I* \( D3 D- E8 H
HSE与LSE分别为外部高速时钟和低速时钟,在本文中使用内置的时钟源,故都选择Disable选项,如下所示:
8 b$ n# @$ s2 }. t$ }
. N0 w) y( V g& B
5 a. b. W9 y) x4 V* M) }$ P+ y( s1 x) }+ k8 L
配置时钟树
$ W2 L* b- B* n雅特力AT32F403AVGT7最高频率到240M,但是STM32F1的最高主频到72M,同时使用不使用外部晶振时候,最高速度只能到64M,所以配置64即可:& O2 X1 O3 p! S+ W$ o, L8 A
. x& R/ C% A( f8 ]
2 l% L+ |, k, |* q' z* G5 p- f! D, f: d2 R# m
串口配置
4 W! x2 M- S# A5 o! U本次实验使用的串口2,串口3进行串口通信,波特率配置为115200。
# g8 S9 v& o! V5 h
4 }% [9 H2 g8 b' ~ r- e5 c
! W# T0 |" `. N" M7 ~* V4 C, l
/ c+ C4 W+ r$ J! P, V
0 d* X& H* G8 `. O" _
0 b8 ^8 n3 X/ k! s! W0 c7 N
配置DMA5 [# ?! ~- s" Y% M
在DMA 设置中,Mode有两种模式,一种是普通模式(Normal),使用一次发送语句就发一次,另一种是循环模式(Circula),使用一次发送会一直发送。这里接收选择循环模式。/ g) w9 n3 v& y. N2 c. ]+ ^
& J: S* N% @: [+ f- w
. I3 j) v# O2 R5 k. [
2 g: ^' n0 g0 z0 t1 L$ J+ R" U) n$ ~6 X/ r
$ r8 M! o1 j7 I# I
* Y1 V& W0 g, o0 U
中断
. V5 Q# r% q# I X在中断设置里打开串口2,3的中断。
) h Y% S- e: y" H& X
( ]$ A- K ?+ A' d0 f
( q, [/ Z) B3 E6 c
3 H |4 Z, T: @+ {+ \( e0 M
生成工程设置
* S |, i4 b6 }% F3 F注意在生产工程设置中不能出现中文,不然会报错。" [) k3 N! c% B$ G
& \* q, ]1 I. d x
3 h# F( v) _) S$ i
: t$ b8 D1 }7 ^( r
代码生成设置+ m( ~% D i# Q& k6 \! Z
最后设置生成独立的初始化文件" d0 {- J x3 |( X
5 h( ~# U! x( [* q# J% s9 m
. `' F' B3 s" ?: ?1 O
/ B$ [8 w, p) R2 S1 b生成代码
5 L5 G: ]* {# v6 ?! X) u# `; u g
8 A) E/ c/ Q: |, n8 S9 t# m4 k" F: V% L7 _% C. t- S
代码编写
: f S d# ?3 h0 ]# R4 ]- N6 s! k在main.c中,添加头文件,若不添加会出现 identifier “FILE” is undefined报错。# M; k8 [- N8 \
8 h8 p4 d- B8 a! a- /* USER CODE BEGIN Includes */1 {+ N9 q, A) @0 L
- #include "stdio.h"
3 r2 ^% ?8 H: B5 K% q - /* USER CODE END Includes */
复制代码
+ n! @& f. H* v. z$ t变量定义 ,在main.c中添加。
6 a! X& c. i+ m0 x) T3 _, Q& G( k# a7 L- ]; ^
- /* USER CODE BEGIN PV */% _9 [; e7 i/ v" o5 L$ g
- #define BUFFERSIZE_usart2 1000 //可以接收的最大字符个数
! M, y- v y3 N$ f- }% t h; n - uint8_t ReceiveBuff_usart2[BUFFERSIZE_usart2]; //接收缓冲区
; v- b( ]# `! O$ v - uint8_t recv_end_flag_usart2 = 0,Rx_len_usart2;//接收完成中断标志,接收到字符长度1 R; Z& _& }) M- o8 e
, \& }4 w3 ~" m' @# j4 d; ~- #define BUFFERSIZE_usart3 1000 //可以接收的最大字符个数
' V! ~3 p% T, t; b - uint8_t ReceiveBuff_usart3[BUFFERSIZE_usart3]; //接收缓冲区: ]- n) n; r5 ]" N) X; `1 F
- uint8_t recv_end_flag_usart3 = 0,Rx_len_usart3;//接收完成中断标志,接收到字符长度4 U& N" z1 U% B( ?2 f. N
- int f1_u1 = 0;
+ {1 B4 [, S* P8 ]( { - /* USER CODE END PV */
复制代码
Q) [+ a% A0 {函数声明和串口重定向,这里使用串口3进行打印,在main.c中添加。
4 e# [) V, a" q4 ^# x& Q& ^
& e( E( r/ l7 F3 J- /* USER CODE BEGIN PFP */
4 M9 U; F8 k+ \- f+ Z - void uart2_data(void); //接收函数
5 Z0 M! v- C- J1 b3 A' n6 S - void uart3_data(void); //接收函数
/ K$ U8 L$ x' H W - #ifdef __GNUC__ //串口重定向" ^6 F0 W& H: s; t1 D( X& M
- #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)! v a$ _2 i( q# s/ P9 u
- #else, A' Q1 |% H( O. {1 `
- #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
8 ?( M" Q" G# T+ A3 D - #endif
: G6 _, N7 ~' M+ c3 H4 e) }0 z - PUTCHAR_PROTOTYPE
" S' J( W C1 K - {
1 E8 M3 k9 w0 s9 e3 k* Y% Z - HAL_UART_Transmit(&huart3 , (uint8_t *)&ch, 1, 0xFFFF);- J) t+ s7 w6 u/ H
- return ch;0 e& l, [# K* E' q2 V. v( i
- }+ v2 @8 Q: M* Y; m1 Q; Q# R
- /* USER CODE END PFP */
复制代码 ! i" t6 r% w* g/ y
开启串口IDLE中断,在main.c中添加。
6 F" d% N7 W% Z5 k/ M/ y1 \3 d' K! v2 U" ~. k% N
- /* USER CODE BEGIN 2 */
( C6 k1 n6 }; u5 o) R8 V - __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);//使能串口2 IDLE中断 ! S& P% f6 ]; P: n3 e
- __HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);//使能串口2 IDLE中断
2 Y! o; u1 U6 ?0 Y! I - printf("双串口DMA接收例程\r\n");% G7 |& O3 X! h2 S6 T
- /* USER CODE END 2 *
复制代码
; |% H, F3 k8 I5 B2 F* w$ }2 R串口DMA获取,在main.c中添加。
7 y3 x1 C, z+ i0 C6 [9 A& T% u1 [$ N- N* g
- void uart2_data(void)' a. p7 U9 F7 u" M
- {7 C0 n& l5 y( d/ h b. Q
- if(recv_end_flag_usart2 ==1)//接收完成标志
q* E/ i. y, F E# G$ ?( G5 I1 l - {
6 u2 p3 q/ ~0 B7 O9 ^* b3 g - printf("串口2数据长度=%d\r\n",Rx_len_usart2);//打印接收到的数据长度 2 H& ~, b: s2 w+ O0 n K1 C4 H
- printf("串口2数据内容:");
. `# H ~, x9 C" R - for(int i=0;i<Rx_len_usart2;i++)
z- a: z8 a0 u0 ?. ` - {
* p) G) G- P9 u5 k0 W - printf("%c",ReceiveBuff_usart2<i>);//向串口打印接收到的数据
7 I+ y3 u# u3 {2 Q1 P8 o k' \ - }
" y6 D& K8 l! o- s9 l! G |, S! J$ m - printf("\r\n");
% \' k) M( f8 y% J% b" ^; G, P - for(int i = 0; i < Rx_len_usart2 ; i++) //清空接收缓存区
) c4 V: E! G" {3 u1 h - ReceiveBuff_usart2<i>=0;//置0+ [0 p* E) v% ~) j
- Rx_len_usart2=0;//接收数据长度清零
( z4 b: E h" t3 _; Y. p - recv_end_flag_usart2=0;//接收标志位清零
: w. B/ v' K+ { a/ |* s - }# G1 J! L' V, Z, @
- //开启下一次接收1 E' d; p2 O! U4 W1 h7 z3 D0 @6 k
- HAL_UART_Receive_DMA(&huart2,(uint8_t*)ReceiveBuff_usart2,BUFFERSIZE_usart2);2 e5 F; s6 Q" R6 B( k9 F
- }3 W8 w; n% n: R8 _( g4 g
- void uart3_data(void)+ r: F7 M8 O7 z: K
- { Q( ~1 m3 [0 E2 `4 k2 W2 _6 Y. a9 M
- if(recv_end_flag_usart3 ==1)//接收完成标志
9 h. d% y. i8 |- A+ k - {
; K8 T, Z! w) s: m - printf("串口3数据长度=%d\r\n",Rx_len_usart3);//打印接收到的数据长度
# N* G5 y9 d7 ?1 ^* g& }* T - printf("串口3数据内容:");
2 E& H- _6 |7 ?9 p, h - for(int i=0;i<Rx_len_usart3;i++)5 ?; {9 T8 b$ t8 q- P, {1 Z
- {$ v( H) P# g( ]# ~
- printf("%c",ReceiveBuff_usart3<i>);//向串口打印接收到的数据, c1 _; P7 ?; W* Q) J9 O0 T2 b. m
- }
9 O! Y5 C& k3 k( D4 X# i7 |2 ~ - printf("\r\n"); . w# h: w" |: m1 n, }
- for(int i = 0; i < Rx_len_usart3 ; i++) //清空接收缓存区/ J% P& n( D. P9 a- Y* s. ]% K
- ReceiveBuff_usart3<i>=0;//置0+ B6 w: m1 w' G/ W
- Rx_len_usart3=0;//接收数据长度清零
# F0 \1 D, _* q5 h! E. y- B - recv_end_flag_usart3=0;//接收标志位清零
+ h# {1 x( k( Y7 _9 P - }$ I4 t }9 T3 e1 N: f+ `" g5 U
- //开启下一次接收
& O3 F# v7 V' M" p4 }% n8 E5 e - HAL_UART_Receive_DMA(&huart3,(uint8_t*)ReceiveBuff_usart3,BUFFERSIZE_usart3);4 M' I1 M2 j$ ]* O
- }</i></i></i></i>
复制代码 7 k; T7 B6 o* e1 J0 N
主循环,在main.c中添加。
% {# b% Q0 ]4 h3 N
1 w' V! W, b8 U" ]) M* U- /* USER CODE BEGIN WHILE */+ H% X3 r% C, j$ C
- while (1)& I! E7 R# b1 B
- {
: ~/ z- b' a: ?. A, L$ e. n - /* USER CODE END WHILE */
9 q7 X% B4 U& G4 P$ z S - uart2_data();
9 y* V8 I* c6 X/ C' | - uart3_data();
( {$ [+ I3 n5 y6 U" N& x - /* USER CODE BEGIN 3 */
( C% x) l2 C3 @8 Q1 o - }
2 l) `+ }- Q6 S- ?* ] l - /* USER CODE END 3 *
复制代码 / \: D0 x* [/ ^
中断外部变量引用,在stm32f0xx_it.c中添加。
9 q2 s- u3 A! B+ W+ _. ~% d1 b6 n9 b7 F3 v
- /* USER CODE BEGIN 0 */) U7 z/ Z* K. {% }2 H
- #define BUFFERSIZE_usart2 255 //可接收的最大数据量
+ ^/ x, z% k( A4 j% J% t" ~ - extern uint8_t recv_end_flag_usart2,Rx_len_usart2;1 K' C/ s( i& V, x
* G% N7 f' m. m: L0 B; R- + K' n$ Y) U6 L& B* C0 X
- #define BUFFERSIZE_usart3 255 //可接收的最大数据量
" f% b3 P) @$ Q+ |1 V7 \' O - extern uint8_t recv_end_flag_usart3,Rx_len_usart3;4 O, y+ c8 \5 w
- /* USER CODE END 0 */
复制代码
3 _: q) M9 I. E3 j串口2,3中断函数,在stm32f0xx_it.c中添加。
- p; m- u) O4 Y' ~; l
) J$ | v; y5 f2 d- /**
! E. B( d% h) B* N; x' b# V2 a - * @brief This function handles USART2 global interrupt.: a3 \2 B5 G+ z6 I: {, S
- */$ T C4 B4 K% z# o5 s6 d
- void USART2_IRQHandler(void)# ~; X) u# q8 X- W( w6 t
- {8 a5 l$ X" j5 d$ |' O- S+ W8 G
- /* USER CODE BEGIN USART2_IRQn 0 */' G& V- N& w9 D) K4 a7 c! z
* q! ~" Z7 A" q0 H, H- /* USER CODE END USART2_IRQn 0 */
5 t+ q0 Z: S3 {. a, S& y) r' s - HAL_UART_IRQHandler(&huart2);
" h$ ^: e- W: C: Q/ Q - /* USER CODE BEGIN USART2_IRQn 1 */
( U* ~+ x1 j6 {* Q% p6 U - uint32_t temp_usart2;& V8 m+ m. p: I; W8 \9 W
- if(USART2 == huart2.Instance)//判断是否为串口2中断
; r8 Z& R/ J; E/ i& K
7 V- y) |& q! K1 Y7 N- { # A( v5 }8 \. T2 t3 Q$ X
- if(RESET != __HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE))//如果为串口2/ x3 g3 O1 W$ U. j; ~
- {
8 m# L- H$ G, k- r7 r - __HAL_UART_CLEAR_IDLEFLAG(&huart2);//清除中断标志
5 O# @% M" {8 b$ Y5 C2 l+ v - HAL_UART_DMAStop(&huart2);//停止DMA接收! X3 A0 M2 q+ u% M
- temp_usart2 = __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);//获取DMA当前还有多少未填充- O3 c' s. G$ r& c' U# H2 k$ b
- Rx_len_usart2 = BUFFERSIZE_usart2 - temp_usart2; //计算串口接收到的数据个数
2 h3 e5 o' @# s/ D9 D. o1 M0 ? - recv_end_flag_usart2 = 1;* I9 x! o# W {: y3 {
- }
" ^7 t3 T: u: D) j' h - }
+ f. Y/ D" V$ k& B - /* USER CODE END USART2_IRQn 1 */
8 H; ~& t) K$ H, M* G3 C - }
, Z' c5 F- W0 r- t; L1 K/ u8 S6 [0 s
$ i( y' F; `$ [6 g; ] `- /**
9 g4 c2 v7 f# k3 k0 ?0 q - * @brief This function handles USART3 global interrupt.
. G# d3 J- v2 T) j$ J1 R - */
% g4 _2 O2 p7 |1 a) { - void USART3_IRQHandler(void)- c' `+ e! I$ V; @0 H
- {
) F3 l3 Z# Q8 v5 k0 K' _% u - /* USER CODE BEGIN USART3_IRQn 0 */1 j7 J: a+ ?8 h/ Z
- + g$ R3 k7 z, q$ q
- /* USER CODE END USART3_IRQn 0 */
2 i7 @6 I" R6 ~0 ] - HAL_UART_IRQHandler(&huart3);& Y9 R% T5 r* C- u
- /* USER CODE BEGIN USART3_IRQn 1 */0 I4 @. Y/ C2 e0 L- o9 S
- uint32_t temp_usart3;
- |! X# D+ g0 W* x4 M5 f2 | - if(USART3 == huart3.Instance)//判断是否为串口3中断
f. q3 J. t5 V: ?# \/ z
: S; h# Y3 ~, ?, q- {
/ O' E4 x j% C# O7 p& f - if(RESET != __HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE))//如果为串口3
7 }1 Y. v9 U* K/ ?, U7 g - {
2 J# W" C( x% _# U# H0 Y - __HAL_UART_CLEAR_IDLEFLAG(&huart3);//清除中断标志1 {8 @" P: [, T! E+ i5 t: H
- HAL_UART_DMAStop(&huart3);//停止DMA接收+ H# T5 O: m0 y/ I
- temp_usart3 = __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);//获取DMA当前还有多少未填充
6 {1 f8 D2 B- S" c - Rx_len_usart3 = BUFFERSIZE_usart3 - temp_usart3; //计算串口接收到的数据个数
! \. x9 E/ y+ Q6 w$ | - recv_end_flag_usart3 = 1;
) Z. t6 [: w- u6 |5 w9 p3 c - }$ j& i. \) q! h) r7 P) C# W
- }
$ R" O" f; _4 } - /* USER CODE END USART3_IRQn 1 */
" S" U: f5 }7 \8 ^ S) H - }
9 W7 ]; D1 i1 g" C7 _
复制代码
0 S5 Z/ U* |. w( F, w结果演示
+ O' d1 `9 I# ]& z8 O开机会显示双串口DMA接收例程。
* l9 {2 I8 k9 I+ f0 c串口2发送数据会在串口3中打印,但是会显示接收的是串口2。
) y) O+ u# h* U3 j' P' f1 B串口3发送数据会在串口3中打印,但是会显示接收的是串口3。
0 g$ F* A6 J( B1 U5 ?/ G, q d( U& F1 Q! P4 a8 L
! Q+ O4 K6 t4 Q. @9 N
, M9 v2 ?( T/ @3 V. P" j0 [$ D5 \
! }# |) A! D' V! S
& E9 F: j8 X' l" A3 M |