概述( V# a1 R* p: `7 h/ E
直接存储器访问(DMA)用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输。可以在无需任何CPU操作的情况下通过DMA快速传输传输。这样节省的CPU资源可供其它操作使用。! c% L/ u! A9 H3 l
8 H( h9 @# V3 u, x
DMA允许在后台执行数据传输,无需Cortex-MO处理器干预。在此操作过程中,主处理器可以执行其它任务,仅当整个数据块需要处理时,才会中断主处理器。这样即使传输大量数据也不会对系统性能造成太大影响。
3 G1 X! O- X& w# |1 T, e# O7 r
) a8 i |; B" q* G: TDMA主要用于为不同的外设模块实现集中数据缓冲存储(通常在系统SRAM中)。与分布式解决方案(其中每个外设都需要实现自己的本地数据存储)相比,DMA解决方案在硅片成本和功耗方面的成本较低。9 M0 u& G: g0 H" g# o3 P
. v2 C9 f S/ K+ t根据使用的产品型号的不同,有一个或两个DMA模块。7 L7 D& |. X9 {' \2 Q* C$ b7 ]" Z
6 {6 N! S8 ?7 _- P+ F! G6 DSTM32F0XX DMA控制器总共有5个通道用于DMA1,每个通道都专门管理来自一个或多个外设的存储器访问请求。它具有一个仲裁器,用于处理不同的DMA请求的优先级。( K& U. C. U% }4 c& a
( _) a }7 l7 m9 e1 K: ^
本篇文章主要介绍如何使用STM32CubeMX实现串口DMA读取,并且打印出去。
: \9 r) W7 d) s8 m1 b6 i
, B. `) g. t% s2 ]- t, m
1 X7 x7 k. q2 z2 b$ @+ W硬件准备# }" P# ^# ]1 s' }. R; W5 C/ `
首先需要准备一个开发板,这里我准备的是NUCLEO-F030R8的开发板:
7 s3 r( u4 k) _/ ~9 }
3 H7 Q" n; C. W+ C
- O% ]8 K" M4 k. T0 C% c$ d6 V选择芯片型号
( o p1 q! M! g! Z* ^使用STM32CUBEMX选择芯片stm32f030r8,如下所示:( U% Y) v4 s+ K3 z6 y+ U; r
' J0 f9 f% D5 x; N
' L% M) k/ T( W9 ]" t
- U2 v) A$ D( b f- z, T1 S
" R5 ]4 _3 P* t1 R2 Q# U配置时钟源$ q& q' \8 w! l; X
HSE与LSE分别为外部高速时钟和低速时钟,在本文中使用内置的时钟源,故都选择Disable选项,如下所示:
& C) {( v3 v: ^$ |2 _, v# Q' C5 W4 x) Q
& h% i# u& X. h* V c2 s
P; R. M8 l9 }+ Z }
3 O6 a' R- @. y% p配置时钟树
: |7 B$ f; N+ u8 I/ ] \9 _6 @4 uSTM32F0的最高主频到48M,所以配置48即可:
# G" \# g+ `7 X- o3 B! N5 w4 L$ T* ]3 ~2 @
! V2 J c Q1 q' A, ~* l& b) M9 r, N9 K9 \
串口配置, T0 x7 k" k0 j/ x
本次实验使用的串口1进行串口通信,波特率配置为115200。2 c7 F) q7 l. J7 _) K$ l9 n
, l3 k4 ]3 n$ h$ V
1 _: f" X# d, g4 H
F, p% @& e0 v' ?& b8 s配置DMA
- ^3 o2 V+ c: o% ]8 I$ e2 V9 }- u% d% O5 \# J0 W
- A! a' T8 I: a, p% ~% s3 e, d9 _ M$ U; r
中断
. g/ q) S/ L$ ^& Z5 g& f' w! k! H9 L" e5 a
' [: H! O6 _) b2 f8 j6 ?/ N
& n, c: [- |; Y* I5 a
生成工程设置& _5 X& y i+ b; Z `
; f0 s- R4 k4 I5 ^
5 c. ?2 {9 m: `, c& y+ ~
. ?+ Y9 s& N! ^8 ]" ~
代码生成设置& J6 r- o* U+ G4 @
最后设置生成独立的初始化文件:# p6 |. l% B, v/ g
8 p) r3 _" e* [ J1 h
" x! r' F/ C m3 D" t, u. w8 q8 P
( Q0 h# y6 c" x- F6 E8 t% ]/ }
生成代码
# A: e/ g0 T3 u8 c4 R
. N& V" I- Z: P# ^- v
% `9 R0 O% o1 @( D* K+ V0 o$ t: b* \8 J
配置keil
# T+ H4 T g4 Q, t
. R. A: j6 _ d* I* Z- W3 ^9 l
, C$ n" V. L5 R3 Q5 F3 m" f
# B. M; U1 N3 D4 ]( _8 |代码- c: S3 t0 D$ r0 b- Z7 X% ~9 p& ?
在main.c中,添加头文件,若不添加会出现 identifier “FILE” is undefined报错。" W1 }1 U1 u, g& A# ^: y' C6 V
4 G# A k2 z- r% Z' I- /* USER CODE BEGIN Includes */
+ h& q/ x- h# G, o1 J - #include "stdio.h"1 d- i! \8 v6 U" \
- /* USER CODE END Includes */
复制代码 z2 r1 T0 i; m9 ^$ B* n, _
变量定义:3 w+ O4 Z8 a' ]& \4 Q$ Z
+ T6 _ v# m. [; ?; @
- /* USER CODE BEGIN PV */
4 O9 h' k/ Y$ N - #define BUFFERSIZE 255 //可以接收的最大字符个数
- j) b$ A0 v3 Z$ o" ]! x& \ - uint8_t ReceiveBuff[BUFFERSIZE]; //接收缓冲区
8 C9 ]( O* H! E - uint8_t recv_end_flag = 0,Rx_len;//接收完成中断标志,接收到字符长度
: ?8 s$ N* e( R7 E9 V! n - /* USER CODE END PV */
复制代码 + x% R- s3 {6 N3 d: ~7 M$ Q q
函数声明和串口重定向:
9 W4 V& S4 T+ U v/ q0 W. U: @ s: n9 D; p1 ]
- /* USER CODE BEGIN PFP */
( B5 m, n, w6 b8 A$ D - void uart1_data(void); //接收函数
' a8 X8 c! |/ s F' {3 V - #ifdef __GNUC__ //串口重定向
* v5 h; N) l" m+ R3 ^% U5 v - #define PUTCHAR_PROTOTYPE int __io_putchar(int ch): P7 K& G% O3 ?( f2 H1 F4 A
- #else' x+ p! i: x" S/ z$ E; j8 I
- #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
; a6 Z: k; b6 w - #endif
* e$ }* ?/ g/ Y9 _1 S$ { - PUTCHAR_PROTOTYPE
: w/ V: ]0 i! D! R - {! U/ C7 ^: j/ l3 y R
- HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
' F4 y/ Q% X3 E9 C - return ch;
" ^) t- F! e6 U: {( A \ - }- Y3 [! x8 v5 ~) a1 G
- /* USER CODE END PFP */
复制代码
/ t) V: Q5 x3 J5 X开启串口IDLE中断:
3 g+ i( H8 V; M2 v# z9 S5 `* r% T2 @4 `* Y
- /* USER CODE BEGIN 2 */
/ S$ \9 g& S% p4 E; |% g - printf("串口1DMA例程\r\n");
7 l3 A+ B" Z+ h" _ - __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);//使能串口1 IDLE中断 ! N/ O, }3 T; q' E( X8 c
- /* USER CODE END 2 */
复制代码 + R0 q# V# k( A Z7 m" a
主循环:1 ^% H* e! Z, w% ^: z! Q, s9 ?
* U8 }( i/ O1 r- /* USER CODE BEGIN WHILE */
1 v! F1 X' S: x2 c - while (1)
( r: T- f6 B' M) t- e% b2 h - {
& B8 z1 _& E2 U5 C# |$ c - /* USER CODE END WHILE */6 F# l# y Z+ g: ] S) u6 [: y
- uart1_data();//串口数据处理
0 F1 y1 s* b/ z, F- q# T* Z) t - /* USER CODE BEGIN 3 */4 V! r. R$ L7 [& Q4 o5 j/ b
- }# E/ G6 N. R1 N( o8 n; E6 y4 b( ?6 t
- /* USER CODE END 3 */
复制代码
) \! x6 m0 z3 d( r$ B串口DMA获取:
b. k ~5 L: j# K( ]6 |$ C
) u; }# ?! y5 y: h- /* USER CODE BEGIN 4 */
, h: R& K Q# U3 K1 ~. Q' G5 U - void uart1_data(void)
. {6 T: z0 E6 |: Y: `1 N - {
$ ?4 s8 i5 J' B; ?$ E. h7 y - if(recv_end_flag ==1)//接收完成标志7 |2 v3 H; w, j, N, V9 \" _" v% O
- {
+ R. R, ?( Q0 _+ d5 c, [ - printf("数据长度=%d\r\n",Rx_len);//打印接收到的数据长度 ! p7 t6 i Q& c- {9 V
- printf("数据内容:");
* d9 V& U% W" ~ P - for(int i=0;i<Rx_len;i++)$ K4 r* [) }5 o
- {
5 j. R% r4 j n6 `3 Y - printf("%c",ReceiveBuff<i>);//向串口打印接收到的数据
2 L% c! E! `, B1 g1 } - }
0 c/ I2 W4 U/ s - printf("\r\n");
5 x; Y, a; U/ l9 P; p5 y# f - for(int i = 0; i < Rx_len ; i++) //清空接收缓存区- E0 |, V# I0 S0 @# Q1 V
- ReceiveBuff<i>=0;//置0
6 |0 `. ~9 s. w" F - Rx_len=0;//接收数据长度清零
1 ~8 q# \4 b; N* q% ^6 d - recv_end_flag=0;//接收标志位清零
! z0 Z5 K& e3 d( z( ^ - }8 s' a8 l3 G5 M0 F: j0 b: }
- //开启下一次接收( b+ B1 X( k6 Y% I, f$ Y
- HAL_UART_Receive_DMA(&huart1,(uint8_t*)ReceiveBuff,BUFFERSIZE);
6 H# F/ z( r8 |# c) V; p( E - }- T6 S4 c# r/ B5 w* k
- /* USER CODE END 4 */</i></i>
复制代码 % |5 n* o; ~$ N5 R3 P5 C0 _9 A
#include "stm32f0xx_it.c"文件中断外部变量引用:- /* USER CODE BEGIN 0 */
$ S. n# e8 B' K' V5 l/ Q4 p. H - #define BUFFERSIZE 255 //可接收的最大数据量, I* J: ~& ?( A* g8 W; G
- extern uint8_t recv_end_flag,Rx_len,bootfirst;
* U) j* l% S$ A - /* USER CODE END 0 */
复制代码 " g I3 a7 q$ n: ], W0 i) m
串口1中断函数:' u1 m5 P) [* K* W2 M g
3 g- {7 b4 C, p6 o0 P- /**
: Y$ s: P6 h0 f! J6 |8 f3 F7 \ - * @brief This function handles USART1 global interrupt.
& ?& J7 ?+ D. s% H3 A$ Q - */
" k% t8 I* q8 ~3 z6 a' d2 q - void USART1_IRQHandler(void)1 u5 ^! v" U4 D7 [! U5 @
- {
. n# X# F8 w: Y/ J+ J5 W - /* USER CODE BEGIN USART1_IRQn 0 */( K" n F/ i+ ~
- 6 U, e! F5 ?- g9 {
- /* USER CODE END USART1_IRQn 0 */
8 u- b& \: Q6 } o: F - HAL_UART_IRQHandler(&huart1);
W* O% X$ H g6 G. a8 j2 ` - /* USER CODE BEGIN USART1_IRQn 1 */
9 R" g4 i |4 x2 T' a/ z4 W" f - uint32_t temp;
0 a7 R2 h- U- J0 W7 { - if(USART1 == huart1.Instance)//判断是否为串口1中断
/ G4 k$ H4 g) k* q$ g- d - 3 p7 ?- ]4 y5 F* {* s$ Y( G* M
- { 4 F" R$ d7 k4 [9 f+ o) J, g
- if(RESET != __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE))//如果为串口1+ |/ F8 b: |7 k4 y9 r- w
- {' P" {9 l: N# V" o* x8 \5 C
- __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除中断标志
8 ~& f' g1 r, r3 W- Z - HAL_UART_DMAStop(&huart1);//停止DMA接收& g# O9 u. Y4 v; l& m' X+ Y
- temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);//获取DMA当前还有多少未填充
/ l3 U% U9 m: o3 e - Rx_len = BUFFERSIZE - temp; //计算串口接收到的数据个数
, \; g; q2 V) y6 w( a/ _, {5 [ - recv_end_flag = 1;/ u. Z6 K" Z! I! l. _" e) N
- }+ U& _. @: ], W
- } ' w' W# L7 o; |2 A8 H/ N
- /* USER CODE END USART1_IRQn 1 */! S' _3 a$ H' L2 C! v% a
- }
复制代码
* |* _7 ~( X- l7 V! a7 F, T9 I# l& w* [ k3 K/ A
演示效果
8 g+ |* j5 c* @3 ]/ j3 r' t
" u! C+ a) T4 p, O ^" b1 g
. @) P2 r0 X$ f0 f# n; T) b3 V: ^0 F
- v/ Z5 j" r/ W5 A |