你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

基于STM32CUBEMX移植雅特力双串口DMA接收不定长数据经验分享

[复制链接]
攻城狮Melo 发布时间:2023-4-15 18:12
概述
    本篇文章主要介绍如何使用STM32CubeMX移植到雅特力AT32F403AVGT7,并通过双串口输出打印。
    直接存储器访问(DMA)用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输。可以在无需任何CPU操作的情况下通过DMA快速传输传输。这样节省的CPU资源可供其它操作使用。
     DMA允许在后台执行数据传输,无需Cortex-MO处理器干预。在此操作过程中,主处理器可以执行其它任务,仅当整个数据块需要处理时,才会中断主处理器。这样即使传输大量数据也不会对系统性能造成太大影响。
     DMA主要用于为不同的外设模块实现集中数据缓冲存储(通常在系统SRAM中)。与分布式解决方案(其中每个外设都需要实现自己的本地数据存储)相比,DMA解决方案在硅片成本和功耗方面的成本较低。
    根据使用的产品型号的不同,有一个或两个DMA模块。
    STM32F0XX DMA控制器总共有5个通道用于DMA1,每个通道都专门管理来自一个或多个外设的存储器访问请求。它具有一个仲裁器,用于处理不同的DMA请求的优先级。
    本篇文章主要介绍如何使用STM32CubeMX实现串口DMA读取,并且打印出去。
8 M3 O2 P- h1 T5 E; t
微信图片_20230415181123.png
硬件准备
    首先需要准备一个开发板,这里我准备的是雅特力AT32F403AVGT7的开发板:

# _* R0 j* M! w, V  U+ e
微信图片_20230415181117.png

  w4 g0 g2 ]3 S+ Y
选择芯片型号
雅特力AT32F403AVGT7兼容STM32F103系列,故选取STM32f103VG进行开发。
  ~$ r' i& V, ?, X% V$ p6 c
微信图片_20230415181107.png
2 P* F9 M7 W' H; P( a& x
配置时钟源
    HSE与LSE分别为外部高速时钟和低速时钟,在本文中使用内置的时钟源,故都选择Disable选项,如下所示:
2 g' {4 {5 @2 y' |. N+ _6 t

  i7 B. {$ \8 a  j+ o
微信图片_20230415181104.png
3 U9 I5 l1 q! ^
配置时钟树
    雅特力AT32F403AVGT7最高频率到240M,但是STM32F1的最高主频到72M,同时使用不使用外部晶振时候,最高速度只能到64M,所以配置64即可:
5 g& E7 M0 w( z8 w) D  A

$ c  {! ?& R- ^- M& Y+ E5 R& ]
微信图片_20230415181051.png

/ y) o- C$ B0 U4 ^. z* ~
串口配置
    本次实验使用的串口2,串口3进行串口通信,波特率配置为115200。
3 d3 ?: f$ o% E5 }3 O
微信图片_20230415181045.png

0 U, J6 J5 P3 y2 z
+ `* b. p; T! z8 {- ?" ^7 u
微信图片_20230415181042.png

$ J2 X7 u% i1 V/ W- @

/ [  c8 ]& g8 Q6 C

1 u& e( C% h& e( N3 M* j/ V. e* @
配置DMA
    在DMA 设置中,Mode有两种模式,一种是普通模式(Normal),使用一次发送语句就发一次,另一种是循环模式(Circula),使用一次发送会一直发送。这里接收选择循环模式。
微信图片_20230415181039.png

/ _; C0 w" j* |$ Q9 }$ W2 ]7 i1 o
微信图片_20230415181032.png
% w% \  ]2 B: r% C

; A  d* p+ B+ O, p8 o+ h中断
    在中断设置里打开串口2,3的中断。
) w& [9 e* J+ J& h. i, K

3 m0 Y7 d" f+ `4 x# g3 Z# p
微信图片_20230415180922.png

, f; ~% `, \8 u3 w" W6 }1 v- b7 [生成工程设置
    注意在生产工程设置中不能出现中文,不然会报错。. S+ F! W  S3 \$ O/ v1 n

$ {9 G( {% d5 a) @# D
微信图片_20230415180919.png
1 _9 K) @- ~- s% I7 ^9 u7 |( u# P
代码生成设置
    最后设置生成独立的初始化文件:
* m+ _' n+ k: R. H7 r/ |
微信图片_20230415180915.png

& `$ r: X; p( Y  y/ a生成代码0 x9 l3 X5 [9 L" f% n! t% z; Q
微信图片_20230415180912.png
( [) r5 X& [4 t1 `8 r8 W! |

$ i6 L# z3 u5 T* }4 {9 U7 i代码编写
    在main.c中,添加头文件,若不添加会出现 identifier "FILE" is undefined报错。
  1. /* USER CODE BEGIN Includes */
    . r' r4 f+ t6 P$ e
  2. #include "stdio.h"
      b! Z7 b8 m9 j% o
  3. /* USER CODE END Includes */
复制代码
( y5 k. p+ j5 u( y
    变量定义 ,在main.c中添加。
  1. /* USER CODE BEGIN PV */
    - i) V: z* z( G# G9 d
  2. #define BUFFERSIZE_usart2 1000           //可以接收的最大字符个数      
    . F, a& W  ?2 n) n4 Z
  3. uint8_t ReceiveBuff_usart2[BUFFERSIZE_usart2]; //接收缓冲区
    $ o7 \# o0 }5 O  Q$ R  V% k
  4. uint8_t recv_end_flag_usart2 = 0,Rx_len_usart2;//接收完成中断标志,接收到字符长度8 m7 ^4 @* z9 y; {

  5. $ T3 |2 v) g$ f% _+ d& s* g( h8 O& y
  6. #define BUFFERSIZE_usart3 1000           //可以接收的最大字符个数       ' m4 W+ N% L* K& F" T; q
  7. uint8_t ReceiveBuff_usart3[BUFFERSIZE_usart3]; //接收缓冲区
    # w7 P4 m% D# W. U$ V2 i! `
  8. uint8_t recv_end_flag_usart3 = 0,Rx_len_usart3;//接收完成中断标志,接收到字符长度+ V/ ?5 o. `8 |1 o- V
  9. int f1_u1 = 0;& T$ r* z4 _8 F& G: j; w* R
  10. /* USER CODE END PV */
复制代码
% _: O5 A! ?: g  @! [4 ~7 n
    函数声明和串口重定向,这里使用串口3进行打印,在main.c中添加。
  1. /* USER CODE BEGIN PFP */
      [9 H4 T. v  x$ Q
  2. void uart2_data(void);          //接收函数
    5 G1 v4 h2 a$ |/ x, A
  3. void uart3_data(void);          //接收函数  k; J  W0 T8 `  W' n" W* ]2 j( k
  4. #ifdef __GNUC__                  //串口重定向5 W2 l! ~5 h& A) w5 t' [* a4 i
  5. #define PUTCHAR_PROTOTYPE int __io_putchar(int ch): x3 `/ v3 Q& i- i+ K- h9 g
  6. #else
    9 Y+ d% S' B8 P) U3 b4 y
  7. #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
    + I! M* e0 \' c6 o
  8. #endif
    * K1 R, S: I" G2 E
  9. PUTCHAR_PROTOTYPE
    $ Y) K! J2 R2 P, s( M
  10. {- Q# j# J; _: k1 m) v$ M
  11.     HAL_UART_Transmit(&huart3 , (uint8_t *)&ch, 1, 0xFFFF);: t, y; `. Z1 X1 q9 s  e
  12.     return ch;' z3 v1 T( U% X! v1 C0 {! s+ m: F
  13. }
    1 h( O3 k$ W. H# l6 ~/ N4 \* [
  14. /* USER CODE END PFP */
复制代码

% f) Y5 X  `7 b$ o% {2 K# |
    开启串口IDLE中断,在main.c中添加。
  1.   /* USER CODE BEGIN 2 */: x6 g7 C, Z; p3 L/ ]
  2.   __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);//使能串口2 IDLE中断 8 a3 ~2 [9 k# v' G# s1 D/ a
  3.   __HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);//使能串口2 IDLE中断 9 |9 A! d9 W- \/ d# J
  4.     printf("双串口DMA接收例程\r\n");
    : }2 R. R: g/ |( f+ n
  5.   /* USER CODE END 2 *
复制代码
7 Q+ [. V6 {; n5 m/ D5 ^
    串口DMA获取,在main.c中添加。
  1. void uart2_data(void)
    1 M$ e8 r7 w$ f
  2. {5 j$ A& i# S  g3 c$ C$ k
  3.   if(recv_end_flag_usart2 ==1)//接收完成标志) A9 I$ B  z4 p
  4.   {: x4 [9 e8 S; P$ M! j, E
  5.     printf("串口2数据长度=%d\r\n",Rx_len_usart2);//打印接收到的数据长度 * s$ h2 Q8 `% [* w2 ]
  6.     printf("串口2数据内容:");# k& w& @  ]! X* r4 q# K( I; b
  7.     for(int i=0;i<Rx_len_usart2;i++). J1 j7 `/ e5 ]9 Z0 R3 K. ^9 v# J
  8.     {7 y- D# a, s/ n" _
  9.       printf("%c",ReceiveBuff_usart2[i]);//向串口打印接收到的数据
    & B6 Q  `1 p* A
  10.             }" _- ?# D, H( Z0 V  H. J4 T
  11.     printf("\r\n");         
    * B. D! T- r- S( [& k- F
  12.     for(int i = 0; i < Rx_len_usart2 ; i++) //清空接收缓存区
    # A; I. q1 x8 z+ W9 j  j# x
  13.     ReceiveBuff_usart2[i]=0;//置0" @7 b$ @3 Q8 `6 Z
  14.     Rx_len_usart2=0;//接收数据长度清零
    ; W7 B+ \+ i+ v' N3 [  \
  15.     recv_end_flag_usart2=0;//接收标志位清零2 h4 o* I: a- i- m, w3 O
  16.     }
    . C2 t5 ^5 K% j9 u8 w
  17.           //开启下一次接收
    - j! E1 ]# `# J! W* v$ T( X
  18.     HAL_UART_Receive_DMA(&huart2,(uint8_t*)ReceiveBuff_usart2,BUFFERSIZE_usart2);
    1 b- U4 x, t8 m& e. e; h
  19. }' r9 D9 h" y" K' C/ i# z. W8 ]
  20. void uart3_data(void)
    - B* J  ]7 S6 a: z- J' ]7 T) k
  21. {
    ' h5 d' M7 ]0 y" R8 R/ v+ Q( N
  22.   if(recv_end_flag_usart3 ==1)//接收完成标志
    , J2 V" |" g- H: {
  23.   {& _& C8 b) }) D0 A) f
  24.     printf("串口3数据长度=%d\r\n",Rx_len_usart3);//打印接收到的数据长度
    4 L! l) ?. w6 N$ u% R2 o- ~/ L
  25.     printf("串口3数据内容:");
    . u) \- a) l$ G& a
  26.     for(int i=0;i<Rx_len_usart3;i++)7 Q0 E2 ^2 m( t# h
  27.     {
    , P6 h# z" L; s# `" l$ y
  28.       printf("%c",ReceiveBuff_usart3[i]);//向串口打印接收到的数据: y8 J5 ~! B2 B0 l
  29.             }$ l' f' G3 j: _' A# q& e* v
  30.     printf("\r\n");          # y( z5 j9 ~: D4 V7 G# d+ ?
  31.     for(int i = 0; i < Rx_len_usart3 ; i++) //清空接收缓存区
    ! {5 ^( z4 s8 E( }( R& S( h
  32.     ReceiveBuff_usart3[i]=0;//置03 t/ ~# f' I6 U
  33.     Rx_len_usart3=0;//接收数据长度清零: e$ J( j( X* v
  34.     recv_end_flag_usart3=0;//接收标志位清零" k. n" l, b- w7 w/ r2 G3 s
  35.     }4 J! B- o! b2 l9 ~0 ^" a
  36.           //开启下一次接收
    $ w. e) C% D% y$ f& G" {% z2 D
  37.     HAL_UART_Receive_DMA(&huart3,(uint8_t*)ReceiveBuff_usart3,BUFFERSIZE_usart3);
    " D4 [* ?  o" [: M0 O. {- X0 c
  38. }
复制代码
' E& c1 D1 ~& W4 f8 v5 Q4 t% J
    主循环,在main.c中添加。
  1.   /* USER CODE BEGIN WHILE */
    - S- S2 u' X. ?$ i, d6 X5 i
  2.   while (1)* w, }# T* f( {/ g; `1 ?
  3.   {
    3 J+ \1 c9 z: |, P% ]8 R, g! k) B% B6 G
  4.     /* USER CODE END WHILE */9 y& U8 p( A7 N+ i! @: m
  5.     uart2_data();
    ) W/ u) u! M. H# d
  6.     uart3_data();( z, ^+ g/ R7 b, M- c
  7.     /* USER CODE BEGIN 3 */5 r* V( Y: ]2 A8 Q7 ~
  8.   }
    : h0 M) x" Q2 I2 B
  9.   /* USER CODE END 3 *
复制代码
7 ]1 K' I+ _* F8 G: |
    中断外部变量引用,在stm32f0xx_it.c中添加。
  1. /* USER CODE BEGIN 0 */
    / E2 k) [  Z1 C* N- a
  2. #define BUFFERSIZE_usart2 255  //可接收的最大数据量
    8 B6 [8 W  V7 j. u8 n, g9 W( |- D
  3. extern uint8_t recv_end_flag_usart2,Rx_len_usart2;
    4 K+ e$ m) K. G9 D" t+ L

  4. $ v. _7 `! `/ u3 K) B
  5.   o9 A& e! x+ v2 {- D
  6. #define BUFFERSIZE_usart3 255  //可接收的最大数据量9 f- K+ j% u* U
  7. extern uint8_t recv_end_flag_usart3,Rx_len_usart3;5 q7 Q; y4 _9 a) J. @/ `
  8. /* USER CODE END 0 */
复制代码

7 P3 E" @2 \- d# K: N4 s
    串口2,3中断函数,在stm32f0xx_it.c中添加。
  1. /**
    9 Q; }; k, ^: z
  2.   * @brief This function handles USART2 global interrupt.
    2 u, G5 a  X. S3 |6 y
  3.   */
    7 f! C& w9 L; Q" D( K, i$ x) ?
  4. void USART2_IRQHandler(void); E, v  r, @0 ]0 n  z
  5. {
    ( P$ N1 @" c8 x
  6.   /* USER CODE BEGIN USART2_IRQn 0 */! ~6 e+ @7 P9 }0 S, G
  7. ' ^. R1 `+ ^# Y+ o- T4 I( A, P8 I* N3 f
  8.   /* USER CODE END USART2_IRQn 0 */- D- K9 c' @! B- x# y
  9.   HAL_UART_IRQHandler(&huart2);! n$ M: b1 g+ F1 Z# `$ |: R
  10.   /* USER CODE BEGIN USART2_IRQn 1 */6 a( [: Z, q) U
  11.     uint32_t temp_usart2;
    # y( E# M6 Q5 [$ q. l7 ]. e. [3 R
  12.   if(USART2 == huart2.Instance)//判断是否为串口2中断
    3 a3 |/ x- G8 I6 d% @; y

  13. - [9 R8 @- J; ]2 \) ?! P1 v7 T/ x
  14.   {      
    5 E- g  @1 v  T2 f" ~
  15.     if(RESET != __HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE))//如果为串口22 g( B2 d5 `0 y4 N& P& R
  16.     {; |8 w- f$ E( K' S2 @
  17.       __HAL_UART_CLEAR_IDLEFLAG(&huart2);//清除中断标志' \/ \. E) C0 ^' u
  18.       HAL_UART_DMAStop(&huart2);//停止DMA接收0 w# |& [& R( l6 K
  19.          temp_usart2  = __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);//获取DMA当前还有多少未填充+ ^  @) A4 h' L! Y
  20.           Rx_len_usart2 =  BUFFERSIZE_usart2 - temp_usart2; //计算串口接收到的数据个数
    ( ]4 d. J2 F& g; x2 q! q
  21.           recv_end_flag_usart2 = 1;6 D6 \" U- `- f& x& [
  22.        }0 O" d: e* k) b+ s( e- G
  23.         }
    # i& X6 h8 B) ?5 W% f
  24.   /* USER CODE END USART2_IRQn 1 */  O1 F0 c# N# o) o1 t' G5 L
  25. }
    7 G" ]) S) l9 L1 }: D6 A

  26. ! O+ J/ _6 V# _
  27. /**! O( w& u4 u" P. C, f- Q/ l
  28.   * @brief This function handles USART3 global interrupt." O/ s4 w( R  w. v" h
  29.   */6 ~3 M" w/ n# d7 ~! |0 r! V
  30. void USART3_IRQHandler(void)
    7 B4 K- @5 `1 s( b% K
  31. {
    5 m( Y1 a5 R& r5 l- d' ?
  32.   /* USER CODE BEGIN USART3_IRQn 0 */
    ) {7 U4 b( }7 \+ P/ [, \

  33. 0 S3 m7 R  {# o* ]" m8 J& g% C
  34.   /* USER CODE END USART3_IRQn 0 */
    $ f; Z2 u3 t* T$ h2 g4 b
  35.   HAL_UART_IRQHandler(&huart3);, R3 N4 g2 c; [, u1 k/ T
  36.   /* USER CODE BEGIN USART3_IRQn 1 */  o+ g3 @4 c! c; L& z
  37.   uint32_t temp_usart3;
    * F* I& y3 P- Q5 q0 \: F! Z) P
  38.   if(USART3 == huart3.Instance)//判断是否为串口3中断
    - @/ C! M& g* i* e2 r" f

  39.   {- N- U3 u# ~& \" e' b( C& ~8 m3 N
  40.   {      3 J) G- g5 f  {0 \$ Y
  41.     if(RESET != __HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE))//如果为串口3" R/ c( ]6 n/ O, U5 m* s2 u" h3 l& Y: l
  42.     {
    2 B9 `6 {& Z: O: o! |
  43.       __HAL_UART_CLEAR_IDLEFLAG(&huart3);//清除中断标志4 f2 `# {0 _4 ?& w9 p) A
  44.       HAL_UART_DMAStop(&huart3);//停止DMA接收9 r& O1 z) z/ N; C7 [) S
  45.          temp_usart3  = __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);//获取DMA当前还有多少未填充/ `& }5 Z( R0 e* _3 Y% R# I
  46.           Rx_len_usart3 =  BUFFERSIZE_usart3 - temp_usart3; //计算串口接收到的数据个数5 l  Z" r+ E, k( @& \$ |& s
  47.           recv_end_flag_usart3 = 1;: a; ]7 c( S3 q$ R
  48.        }
    % N4 o/ V* \5 X+ _% p: F8 G
  49.         }
    0 ]2 v" e. s& [5 ^- M: q
  50.   /* USER CODE END USART3_IRQn 1 */& p4 ?& @0 d9 g; K* A
  51. }
复制代码
, F4 I7 U- z6 V) U& V6 i- o
+ X; ~/ }0 W$ v, O2 ?1 E
演示效果
    开机会显示双串口DMA接收例程。
    串口2发送数据会在串口3中打印,但是会显示接收的是串口2。
    串口3发送数据会在串口3中打印,但是会显示接收的是串口3。
9 B7 j) k2 ~! {  d/ l
微信图片_20230415180909.png
. K2 m, k% K2 E, w& w
转载自:记帖
如有侵权请联系删除; U8 ~) ^1 C4 M' i3 m) U5 _

& G8 a( f5 f0 J) {9 l% W
; p4 ~; t2 b" R& D8 u( ~8 w* k
收藏 评论0 发布时间:2023-4-15 18:12

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版