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

【经验分享】STM32 HAL CubeMX 串口IDLE接收空闲中断+DMA

[复制链接]
STMCU小助手 发布时间:2022-3-23 17:00
本篇文章提供两种方法:
4 x% g; L1 m8 w; \$ c- [4 S' |一种是 :IDLE 接收空闲中断+DMA& H5 s, f- A  b
一种是: IDLE 接收空闲中断+RXNE接收数据中断) e/ X+ @; s1 t9 h! B% [

/ x  Z4 f6 T: Y都可完成串口数据的收发
4 b) L- i* l3 C' x' U4 F3 L. N" d1 J5 n& Q: |5 m- g. i3 m
知识点介绍:9 ?/ q! I' w: N+ `  w; o
STM32 IDLE 接收空闲中断

, E* [5 O0 p2 e) @/ [/ r5 O功能:/ w. [1 h4 v" f
在使用串口接受字符串时,可以使用空闲中断(IDLEIE置1,即可使能空闲中断),这样在接收完一个字符串,进入空闲状态时(IDLE置1)便会激发一个空闲中断。在中断处理函数,我们可以解析这个字符串。
0 ?( f( {( Z1 b8 i5 ]: p; Y
, `9 C+ N) O9 n5 _接受完一帧数据,触发中断
& W1 n0 b/ b$ f! D7 u) r3 g' \# [) F9 I  A+ a" h' _; i' n8 Q
STM32的IDLE的中断产生条件:
, {+ q* I5 h% h6 m. l% Z在串口无数据接收的情况下,不会产生,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到数据,即产生IDLE中断
& [3 J- \' E2 \9 f2 G2 w( b5 x$ Z9 G; t8 e6 ^" x
STM32 RXNE接收数据中断
% D# `! ^0 ]9 A5 R" ?+ x功能:8 l' e7 w1 Q4 W
当串口接收到一个bit的数据时,(读取到一个停止位) 便会触发 RXNE接收数据中断
+ E! f/ P$ }9 z* {9 `# F
/ G& ~! h9 s0 z0 L/ _! M2 K接受到一个字节的数据,触发中断
& v$ ^! D$ |6 F3 f# M0 o0 u1 n, h+ v
& U: N0 E6 {; b  k* S比如给上位机给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。- u' ]6 f/ `1 Y$ R& m; S

# {4 P4 n8 l- i2 f) }3 p7 t9 y/ ~串口CR1寄存器" ]9 Z3 ]9 m5 r' I

" g" k! H) K- _5 [9 q KPGP$%K4Z`P]5~NPWSBD91K.png 7 O; U1 W* N: @* D7 }5 q
! y& G0 I( U4 j! [7 t
对bit4写1开启IDLE接受空闲中断
" k. g" w% Y' a2 X6 @6 H8 ~,对bit5写1开启RXNE接收数据中断。& {0 d/ s( Z' d# t: H, J  `" P

# _) [( D% j; m" Y7 m, ]串口ISR寄存器9 X- T' b4 m. ?1 h$ r# L0 J

0 C2 [* q! F0 @) Q+ [- r L%TE0SWP0~FE)I7T4TOEER9.png
+ H$ O$ t9 C( W; e: H7 I6 C8 H( P5 K! T, }
此寄存器为串口状态查询寄存器4 e& Z5 E( `/ \# t( J

6 C6 ~/ |& k+ H4 {6 g+ n当串口接收到数据时,bit5 RXNE就会自动变成1,当接收完一帧数据后,bit4就会变成1.- O, b0 q6 c3 S, z, a# O

0 H# |' A& e3 f4 t  ~# m清除RXNE中断标志位的方法为:. W, q1 u  v( Q5 H5 H1 q( w% F
+ C: Y8 a" p, a3 l
只要把接收到的一个字节读出来,就会清除这个中断
- k5 f1 B+ E, F" _; c+ S: X/ f% H; t, Z
在STM32F1 /STM32F4 系列中 清除IDLE中断标志位的方法为:
1 e; |5 p3 e" s0 i2 p* x8 b; N4 {6 \+ I! Q
1.先读SR寄存器,8 `$ Q$ w6 t& f' D8 |
2.再读DR寄存器。
. y( B! u0 w& I7 x& I3 T; D3 W

; B4 c+ l0 r4 b9 ]) [ 3F]Q2F56KOVOR(9K8AF{TS2.png ! p! L& z: j/ Y$ r1 f
8 s$ ^6 v8 x- d. o" ~: i
memset()函数3 {7 s8 ^4 `! U% p* m
* E' @3 F4 v6 Q/ L. B3 z. M8 b
  1. extern void *memset(void *buffer, int c, int count)        
复制代码

& h. _( j7 P3 f1 N+ K2 Z# g8 k4 @buffer:为指针或是数组
; }% b1 m: w6 F2 j' mc:是赋给buffer的值7 i. ^, Q1 z, ~
count:是buffer的长度.
: y& Q/ N6 c- T# {& wUSART采用DMA接收时,如何读取当前接收字节数?
6 R( `0 ~3 B' y3 N
  1.    #define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR);
复制代码
& Y# v& X7 m) z$ u) W
DMA接收时该宏将返回当前接收空间剩余字节
% E5 K8 @: m1 Q9 B  V0 L! J; d1 R( A  i/ z4 K
实际接受的字节= 预先定义的接收总字节 - __HAL_DMA_GET_COUNTER()  c- J/ L) D& |. p
! Q( j4 y9 S7 p; S( T) e/ B6 Q$ `
其本质就是读取NTDR寄存器,DMA通道结构体中定义了NDTR寄存器,读取该寄存器即可得到未传输的数据数呢,/ l& j& n* D7 ?) T

; _0 Q7 @2 t) B0 w( ?$ }" RNTDR寄存器9 ?% `0 D* \. v+ F+ [+ O
3 ~  V$ l" C# N7 k) w
_[`8QZUY((Y87)B8BSJ36}B.png   U- l4 y5 F6 ~* V8 q
$ z' S% O0 V$ v% e' Z3 l" p
实现方法:
+ i1 U( f% |  y" L5 K0 W两种利用串口IDLE空闲中断的方式接收一帧数据,方法如下:$ Z+ {) L  W0 C7 }  f2 w# p( h6 ~

) D5 Q' @* O" y' M4 h2 d方法1:实现思路:采用STM32F103的串口1,并配置成空闲中断IDLE模式且使能DMA接收,并同时设置接收缓冲区和初始化DMA。那么初始化完成之后,当外部给单片机发送数据的时候,假设这次接受的数据长度是200个字节,那么在单片机接收到一个字节的时候并不会产生串口中断,而是DMA在后台把数据默默地搬运到你指定的缓冲区数组里面。当整帧数据发送完毕之后串口才会产生一次中断,此时可以利用__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);函数计算出当前DMA接收存储空间剩余字节+ \3 s6 ^" @+ e0 ?3 A
1 Z' ]& _; ]- q5 t
本次的数据接受长度=预先定义的接收总字节-接收存储空间剩余字节- `4 P# V' L  O! q' N9 Q
6 Z8 o1 }2 g8 `
比方说 本次串口接受200个字节,
; H9 g# s; p4 [5 O! F/ w2 H6 U0 F
  1. HAL_UART_Receive_DMA(&huart1,rx_buffer,200);//打开DMA接收
复制代码
1 P) O- U+ _+ C. O% K/ q
然后我发送了 Zxiaoxuan 9个字节的数据长度9 I) K9 R" F7 D; w

! P9 W  W* b0 P4 a0 @那么此时 GET_COUNTER函数读出来 接收存储空间剩余字节 就是191个字节- [! t& `# L0 f* u

' p* D6 `" C; Q) U9 U实际接受的字节(9) = 预先定义的接收总字节(200) - __HAL_DMA_GET_COUNTER()(191)2 {+ U1 R" ], u
9 = 200-191
3 e. m  z4 w3 \3 m4 [+ x9 G3 K/ k1 m: k* i- H5 L6 l- N/ r7 b9 i1 d
应用对象:适用于各种串口相关的通信协议,如:MODBUS,PPI ;还有类似于GPS数据接收解析,串口WIFI的数据接收等,都是很好的应用对象。
( ~  \; ?+ Y* A7 P# A' U1 S. e, h* J( p9 J4 L* u& E7 O
方法2:实现思路:直接利用stm32的RXNE和IDLE中断进行接收不定字节数据。 每次接收到一个字节的数据,触发RXNE中断 将该字节数据存放到数组里,传输完成之后,触发一次IDLE中断,对已经获取到的数据进行处理
& u2 A: S; L8 I& P
. p- C* T$ F* g3 B- X例程1% E% D# ^+ I$ e
本例程功能:

: ]& I+ Z8 N& D7 F+ A9 |
6 P0 z% Z6 c: y0 s- x使用DMA+串口接受空闲中断 实现将接收的数据完整发送到上位机的功能% d3 B$ a8 `' ~( |! G" h0 K+ t
2 B9 [2 u$ s" R3 D, @) `& a; Y
1 ^, O4 G- O" f$ w+ L3 O, d2 X- J
* x3 t6 j# r, [
接收数据的流程:
  Q& Y+ M3 }, V: K
. [. d( t) ?+ V" _/ P; n; B& h6 R首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,程序此时也不会进入接收中断,在软件上无需做任何事情,要在初始化配置的时候设置好配置就可以了。+ z8 d8 b- e2 Q5 Q

6 }( H+ g( ?2 W. s) q数据接收完成的流程:+ x5 q, K; c0 |% E6 f! e

; I( T( m' ~1 r; P) V9 P" P当数据接收完成之后产生接收空闲中断④4 e. g$ C1 I9 n( M
: |' Z$ Q: V* e: z/ h" @
在中断服务函数中做这几件事:
: p* X% S/ w& I: `( |: |" N判断是否为IDLE接受空闲中断
  t1 y& L( v- D在中断服务函数中将接收完成标志位置1
9 @/ [* M; w0 b4 B6 r关闭DMA防止在处理数据时候接收数据,产生干扰。4 U% Z4 f& ~; A; b. S; |
计算出接收缓存中的数据长度,清除中断位,/ p+ \+ x; L4 ]/ ^) ?/ |; e
0 E' N& K/ r  p; B' x
while循环 主程序流程:
. g% ~0 v+ e2 A: j) X/ ^主程序中检测到接收完成标志被置1/ U. Z, ^/ ]+ D- H. {8 L4 U1 e
进入数据处理程序,现将接收完成标志位置0,! @: a$ t: I6 L' m) D
将接收到的数据重新发送到上位机
0 z* Y) L4 K$ y- D: P& T4 O$ N重新设置DMA下次要接收的数据字节数,使能DMA进入接收数据状态。
: j- M, @' |6 n
  z! ^5 h( v5 Y& @  n: C7 j( n例程代码:
7 M) c1 C7 F. R0 b. f, H% vuart.c
) j1 S1 s' K; X! s
  1. volatile uint8_t rx_len = 0;  //接收一帧数据的长度  F1 `( x% L2 y
  2. volatile uint8_t recv_end_flag = 0; //一帧数据接收完成标志
    8 @' B$ P: C& k( }
  3. uint8_t rx_buffer[100]={0};  //接收数据缓存数组
复制代码
  1. void MX_USART1_UART_Init(void)
      x  U" U6 v, n9 x' H- b4 a/ o
  2. {
      g' W; C% a/ j" z

  3. ! e# J- F3 ?% n) c2 q
  4.   huart1.Instance = USART1;9 n0 ~, Y  o* ~6 s3 t" ~0 i
  5.   huart1.Init.BaudRate = 115200;( i2 J3 v5 J) G; q3 K9 [( \- X6 v
  6.   huart1.Init.WordLength = UART_WORDLENGTH_8B;
    - h1 k- `" g. W5 w0 i
  7.   huart1.Init.StopBits = UART_STOPBITS_1;4 K5 w) z/ z! E: B; f
  8.   huart1.Init.Parity = UART_PARITY_NONE;
    7 F. q: K7 d$ C: l9 k
  9.   huart1.Init.Mode = UART_MODE_TX_RX;  C9 K& d6 s' U8 ^
  10.   huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    1 E4 P* E/ O" }. Y. w/ U
  11.   huart1.Init.OverSampling = UART_OVERSAMPLING_16;- Q$ B( g6 D% `" y8 u$ o
  12.   if (HAL_UART_Init(&huart1) != HAL_OK)
    ; A2 R% j: }; p0 C0 h
  13.   {7 Z, {) ]+ {1 E5 v# Y1 D& R4 p
  14.     Error_Handler();
    1 ]' q2 G! E3 m
  15.   }  W- Q2 ~0 T0 q' R
  16. //下方为自己添加的代码) k: T% F' m) b8 t3 R" ^8 N
  17.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断% U( C/ u0 f- t3 P) o, p+ L
  18. . y5 G5 C( i; ~
  19. //DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度
    9 g& |7 s; @" r; t% N% t- D& z
  20.         HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);
    8 b& `" |( ]" j( a, c5 q  P( I
  21. ! \+ s" d" R6 }" P
  22.         
    ; K/ o! |" u5 {# K7 S$ h$ }/ F
  23. }
复制代码

( m3 p( x5 y+ F5 Cuart.h
6 y- j- I3 o3 c1 c% m, I) L1 u
  1. extern UART_HandleTypeDef huart1;
    2 ?; I5 C9 L) G9 Y  {
  2. extern DMA_HandleTypeDef hdma_usart1_rx;6 X2 W& j. V" ]$ o" v
  3. extern DMA_HandleTypeDef hdma_usart1_tx;( X' J6 T# n$ g/ p
  4. /* USER CODE BEGIN Private defines */# {3 k- N! E1 U& x, `6 `
  5. ) W2 v' ?4 F) V6 L" q

  6. # U& c2 W. }' P9 n9 z+ G$ T% u
  7. #define BUFFER_SIZE  100  # w& E+ l% ^8 \* k3 @6 p
  8. extern  volatile uint8_t rx_len ;  //接收一帧数据的长度  c2 k, r; i2 z8 f+ q& \
  9. extern volatile uint8_t recv_end_flag; //一帧数据接收完成标志
    ! `- H$ T0 j, S; p% [9 k& G9 f
  10. extern uint8_t rx_buffer[100];  //接收数据缓存数组
复制代码
/ L' g- i% B1 j8 K% S
main.c( G: M) }# \4 j. H

) y- y! [" F: h9 @
  1. /*5 t' _. C, }# b2 o  \  b# _
  2. *********************************************************************************************************3 E, z1 w2 |8 x; R) s4 H- b+ x
  3. * 函 数 名: DMA_Usart_Send
    ) E1 Z  }# ?9 G9 b: k
  4. * 功能说明: 串口发送功能函数# o8 F5 c, E4 f/ v( L
  5. * 形  参: buf,len/ W& j# l: Z, {7 A! j9 g1 l
  6. * 返 回 值: 无
    5 }# e  t/ f( t8 l3 a& U
  7. *********************************************************************************************************
    + G# _  Q/ Z$ _' U: r/ b3 W# S9 h
  8. */
    4 X$ K# P+ e8 a. D) I1 g
  9. void DMA_Usart_Send(uint8_t *buf,uint8_t len)//串口发送封装! B% u) n# s0 I& o+ R# n  u8 b  f
  10. {
    " m: N" q; t% L
  11. if(HAL_UART_Transmit_DMA(&huart1, buf,len)!= HAL_OK)   //判断是否发送正常,如果出现异常则进入异常中断函数$ A5 V, G- T' @, e
  12.   {1 }9 v: A. M: |. k7 b. M0 Q) W
  13.    Error_Handler();
    3 v1 P6 I0 I) i$ E* n$ E9 Z/ Q
  14.   }
    8 y% h  S5 ?( {4 X2 p" R% v
  15. ; }5 m8 t0 Q- q2 V& _
  16. }
    0 b! I- B% Z; E0 ]0 }
  17. 0 U* M$ _$ v% }; [# t- a  i2 b) ^5 {5 b

  18. % H+ t" t% m8 L: ~0 O$ }4 @1 e4 }
  19. 3 D' X  z1 z+ m6 d% W+ n
  20. /*
    $ H% _! g: @; ~/ l3 H
  21. *********************************************************************************************************: J/ G  n! V! |3 M' c, ?  L
  22. * 函 数 名: DMA_Usart1_Read5 t; Y1 @3 M0 o, l
  23. * 功能说明: 串口接收功能函数! K  `) b4 K( j/ ]/ Y5 l# A8 f5 S3 [
  24. * 形  参: Data,len: P& L! L! f% |4 a2 Q; z
  25. * 返 回 值: 无
    * [6 K9 z0 U  s+ k3 H& O- c+ n
  26. *********************************************************************************************************
    8 Q9 G( F2 L- R: J- w
  27. */* M6 }# ~" |  K# N' L5 L
  28. void DMA_Usart1_Read(uint8_t *Data,uint8_t len)//串口接收封装
    . e$ e: o# r+ Z# ]$ F1 t$ r
  29. {
    : ^5 c: \8 q0 v) h
  30.         HAL_UART_Receive_DMA(&huart1,Data,len);//重新打开DMA接收# L  Z3 Y3 Q/ n
  31. }
    . d/ i1 y' \! N$ T9 W0 l$ m
复制代码
! R: Y6 r$ V7 t; K/ j4 d
while循环9 o- y4 l! D$ U  {
0 a! Y: _. P3 T. w, C7 R
  1. while (1)
    9 p, S# Y( }$ g- k" e) \& i
  2.   {
    ( Q  |( o0 q; l* s7 t( T
  3.     /* USER CODE END WHILE */
    5 ]: D4 B, }6 W1 d0 T

  4. 9 f/ t( x4 {  f+ d% m6 Y
  5.     /* USER CODE BEGIN 3 */7 }" _( r* k: N( V2 f0 h: D1 q
  6.                  if(recv_end_flag == 1)  //接收完成标志
    % b4 P3 ?$ P; j/ r* v2 ~, d) D
  7.                 {. D% m4 s! I1 V/ |1 |
  8.                         " ~/ B; u3 ^7 ^: }0 [& m
  9.                         
    3 Z9 d2 o  X+ m  R; Q# _( z
  10.                         DMA_Usart_Send(rx_buffer, rx_len);, _; H1 _. `: r' T* h- @2 U
  11.                         rx_len = 0;//清除计数
    . B! p0 I6 y* c. H0 }4 ]% B
  12.                         recv_end_flag = 0;//清除接收结束标志位3 A. D2 J7 V3 |. n9 t" g
  13. //                        for(uint8_t i=0;i<rx_len;i++)- q8 Z7 l$ a" I/ V6 N
  14. //                                {8 @+ _. r6 X+ E# F4 s
  15. //                                        rx_buffer<i>=0;//清接收缓存</i>5 t: U9 Z+ b% ?. s: U
  16. //                                }
    7 m" V& S- [$ U( _- |
  17.                                 memset(rx_buffer,0,rx_len);. q' f" v: c% c: G; _: W7 y( l$ x
  18.   }+ y5 d# ], E" k
  19.                 HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//重新打开DMA接收
    ) s/ @/ I( b1 J. P) c
  20. }
    ( j$ \, o$ m+ W  o
复制代码

+ K% _; c: v/ u" j; v- C9 Hstm32f1xx_it.c中8 H9 h7 o% L$ E6 v  ~# k* s
1 X  m  B" P0 j8 D  h& d2 c
  1. #include "usart.h"
    ) b) H" ]- c9 C5 u# D- E  |1 f

  2. , j( }- c6 O' b! P4 c: z9 S8 V
  3. void USART1_IRQHandler(void)
    : e* J% v. S. H4 `# ~# i$ w2 E( K
  4. {. t$ m6 C+ `0 f- D* g6 t
  5.         uint32_t tmp_flag = 0;
    $ a; n- T. r% `
  6.         uint32_t temp;8 F+ _6 w3 V% m1 r
  7.         tmp_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位( E# o, M# E, \; @$ g$ @( K
  8.         if((tmp_flag != RESET))//idle标志被置位( a3 }/ i1 Q% ~1 [( g; H2 V
  9.         {
    ; S% D# Y4 M6 |
  10.                 __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
    & q! G- Z% A4 Q
  11.                 //temp = huart1.Instance->SR;  //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能0 O# c# P" D; Y$ }( r4 k2 F; f  B
  12.                 //temp = huart1.Instance->DR; //读取数据寄存器中的数据6 _) Y2 `. W, t9 |+ p6 Z6 |$ C" z
  13.                 //这两句和上面那句等效9 k: W* F% E# C% f" b. g
  14.                 HAL_UART_DMAStop(&huart1); //  停止DMA传输,防止
    7 B  F, x& C: J7 Q
  15.                 temp  =  __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数   0 U; _/ K4 t& h- b( s7 W% p
  16.                 //temp  = hdma_usart1_rx.Instance->NDTR;// 读取NDTR寄存器,获取DMA中未传输的数据个数,
    $ ?0 z9 |' M: k
  17.                 rx_len =  BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
    5 o- A* \4 a2 j4 Y# Q
  18.                 recv_end_flag = 1;        // 接受完成标志位置1        6 Q1 `4 C: o9 M3 \8 [. ?
  19.          }
    * n  ^7 W8 K" K9 f0 {
  20.   HAL_UART_IRQHandler(&huart1);
    4 I0 U) m% V1 u% h5 \. x
  21. 0 k/ N7 A( f: n9 R: [
  22. }
    5 C7 K/ p7 [# L$ y% A  B
复制代码
. r/ r" G! C. Q7 V0 o6 @) w
注释详解:
; s9 h" ^- g, l3 B  m2 ?. d: m2 R注释1:
6 R4 r, i/ c+ Z) C* c3 J
  1. temp = UartHandle.Instance->SR;  //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
    4 D/ K+ g1 t. h3 m' R
  2. temp = UartHandle.Instance->DR; //读取数据寄存器中的数据
复制代码

( s% |& B% Z1 [* y( o( j- N/ g" J, p9 X4 l这两句被屏蔽的原因是它们实现的功能和这下面串口IDLE状态寄存器SR标志位清零的宏定义实现的功能是一样的:
* G* V4 N' I' V& ~6 M$ \$ q' z
2 L$ \6 Y0 h8 ^; L; m__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
( m& `/ V/ e3 h/ M0 {6 T1 k! A6 Y) N6 [3 U+ {& g
我们可以点击这个宏定义进去看看,它实现的功能和上面两句是一样的:
& h/ w) Y4 }7 M+ o+ ?; i# f( c1 m
#define __HAL_UART_CLEAR_IDLEFLAG(HANDLE) __HAL_UART_CLEAR_PEFLAG(HANDLE)
# C  Z8 _3 m/ `3 g* e- B8 r8 [* C4 |8 J& f2 \
MGNYWWUU8_60()GL0)P@]RJ.png
- u) l+ I  F4 j) l% E5 b4 e: o& ]- \8 c! l) b0 S( S
注释2:
( r3 ^5 }$ z/ ^# h3 n, R2 H3 O/ g4 ^% A
  1. temp  = hdma_usart1_rx.Instance->NDTR;// 获取DMA中未传输的数据个数,NDTR寄存器分析见下面  
复制代码

8 {, @) I% G2 `- O4 w同理, 这句被屏蔽的原因是因为他和上面的__HAL_DMA_GET_COUNTER的作用也是一样的,都可以获取DMA中未传输的数据个数7 t+ A+ k! w4 C; K2 |

2 M# E6 K2 _2 D4 O0 F ZO1V@ZT1H)]~NR@V])59TT1.png ( t4 M+ O. O* d$ _9 t

& |3 W2 T3 t" b0 ]: {. l/ Z测试正常:
; f1 l6 ?9 Z2 O! {5 W  ]
1 Q9 g' U0 J5 Z J92(YI{0KM`ID{%8[LRX9`M.png
: j) k6 K2 w( L/ e
* d. ^& q+ E' O2 U( k, r此程序可以进行接收不定长的数据帧,不需像RXNE每次接收到一个字节就进一次中断。
9 Q, b5 K3 a5 l$ {, w% n) X7 M# w同时开启DMA传输速率也会加快4 i8 ^2 {! ]8 N& h( e# m% W
例程2, i. U: Y9 W) c5 T& U! A0 s. W* \# r# ]
UART中断使能函数
7 X/ l! s; a8 E7 r: W* s$ H
  1. __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__)
复制代码
" w6 U$ L6 r8 k- M0 x0 F
功能: 该函数的作用是使能对应的UART中断1 Y5 J) f8 w$ B  a& T5 F$ n

0 z3 x  D6 `4 W0 w7 r: z; Y; |$ |/ P) C* A在stm32f1xx_hal_uart.h中被宏定义
: I4 S* C, K% p3 }4 X/ d/ ?( [
/ O& z+ c! H+ e6 d' p) `$ k  f DWY99KRLDC0B_J1WG{$N`30.png
  K6 F( D$ s: J6 ^5 a  `
% U) l/ x( x" c' J' h+ J  \0 O可以使能的中断一共有这些:
" m. V" P+ A* \0 b) a8 [! Z
/ F; A7 C6 ^! U; v. @ %H4WBTH~8)WZY49JBXC6}[M.png
4 @1 s" W3 X6 K8 O& t2 J
% I1 f8 n- g5 U! b/ P- ~: z我们现在所用到的为:% A4 o7 ^/ C! l* O5 |, `
  1.   *  @arg UART_IT_RXNE: Receive Data register not empty interrupt
    3 P( w" v  ~; z
  2.   *  @arg UART_IT_IDLE: Idle line detection interrupt
复制代码

7 G- |* V' O/ e/ F; R0 D8 LRXNE接收数据中断
+ q1 \6 b0 y3 |* E( p2 h* CIDLE 接收空闲中断% l1 Q* w2 I' S$ i+ Q% _) m+ ~

* K1 {' k7 h6 i& Q: d U5PXUTDR$S1Z2XH0U5SHRKC.png
. r% m- R' O7 y8 P! _6 ]5 z# N, N! q+ c& W
本例程功能:
, N2 D5 s# |1 T$ ~+ z
. o  ^: N+ h7 P2 `使用RXNE接收数据中断+IDLE串口接受空闲中断 实现将接收的数据完整发送到上位机的功能
6 B" P( Y; e' I& Q2 o  c4 z$ o  K! W
接收数据的流程:& v2 n+ V3 ^1 w' p! L
% X' {7 L5 k2 K2 X% l; M
首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,同样初始化配置的时候设置好配置就可以了。# m1 C2 @% l+ l, f

3 R: q! j' l8 L接收到一个字节数据:
& P/ {% I' S8 \9 i1 w8 h5 `接收到一个字节的数据之后,便会进入RXNE接收数据中断,) m+ F+ r4 Y  a  }; Y1 O  ~
3 t/ `( ?3 u  r: c
接受完一帧数据之后,便会进入IDLE 接收空闲中断  W3 p. a: m; N! K
" G6 I6 t. O: ?  \
在uart.c中添加中断函数! N: D+ |9 m+ ]5 Y# m' t5 ?

& w4 O4 b$ l7 [( R$ W% d2 u) u- w
  1. void MX_USART1_UART_Init(void)
    0 m4 f1 e: C. C
  2. {9 i/ W! ]2 w$ u
  3. 6 ^/ I5 t# Q3 Z9 b+ x  J  J
  4.   huart1.Instance = USART1;, F( s+ K6 E6 {& Y( C
  5.   huart1.Init.BaudRate = 115200;
    " R. K3 h3 P' O3 o% c8 `
  6.   huart1.Init.WordLength = UART_WORDLENGTH_8B;4 U1 `5 E6 _. Z3 s0 E! Z( b
  7.   huart1.Init.StopBits = UART_STOPBITS_1;
    5 N- n1 d. |  c6 D8 z4 @
  8.   huart1.Init.Parity = UART_PARITY_NONE;: h4 g# X" Q- q! X- L# f
  9.   huart1.Init.Mode = UART_MODE_TX_RX;- O+ T; q6 f0 n* E* v  U
  10.   huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
      R. {% T  e+ e  d$ s
  11.   huart1.Init.OverSampling = UART_OVERSAMPLING_16;
    + z) \% i2 [4 q2 Z
  12.   if (HAL_UART_Init(&huart1) != HAL_OK)
    ! C/ k& e) y% o4 V% f1 a4 @% B! `$ K6 s
  13.   {
    + I# R% D2 V% R& x1 d
  14.     Error_Handler();
    + s( \5 ^3 c/ }' d8 M/ y& M
  15.   }
    0 x6 [; V) I- f, v" v; T$ B
  16. # y: w2 X! j; R/ Z9 A- c
  17.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); //使能IDLE中断
    0 @& {# K% h9 u& M3 G5 b" w  Q
  18.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断
    - U2 q% L0 {0 J9 r4 k9 G" U

  19. . b' A  E( h+ q3 d& L
  20. //DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度+ b6 ^- y) ?$ n: t8 G+ M& O
  21.         HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);* Y1 m0 R% a5 W. ?, l- a

  22. 7 e  C9 I. y5 _
  23.         ( `' U# }# z2 I  h! n, z2 ?
  24. }; e* a/ q+ e! K+ L5 i: t
复制代码
其核心代码就是下面的两句
( d7 y& b. i  R" Z- g+ s2 F9 _$ y5 u) F2 @: Q- h* C- B' S7 I" f9 i6 f  q1 l1 Q
4R[EVR6ESO3C7R2K_B$FN.png / N! `' L/ V& i5 z) M5 X

8 z# z$ F4 X" b! ^& d第一条语句用来判断是否接收到1个字节,第二条语句用来判断是否接收到1帧数据. R$ r+ ~7 `. M" ?! a4 x1 s

  1. 4 K" L% [- y* y  ]9 Z  _% f
  2.         if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!= RESET)  //如果接收到了一个字节的数据. R$ C: `# a# P3 k& l
  3.         {
    2 f# O' S% U0 I5 x* }9 }6 {
  4.                         HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);    //反转LED+ v! c& M+ J! W7 O" M0 E
  5.         }+ B  C- ~5 S$ _
  6.         * J% t8 c/ e& ^
  7.         if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!= RESET)//如果接受到了一帧数据' _. X! j% Z  \; \/ _! i7 L
  8.         { + \2 P4 i) }& g# w; B
  9.                 __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
复制代码
$ r( ]% K6 ^% X0 T  u) }8 l8 v
其余部分于上方例程1基本相同( H0 I6 B% R0 }8 O7 @9 {* p
% I- w2 p. z- K2 ]3 _8 T! I
本次测试是检测接收到一个字节就反转LED
* \5 g! s0 O# A1 Z9 @3 ]( S经测试,历程正常:
" z( @! f% _5 Y- u9 _5 u* h
  D: @- A6 ]% U5 `" c, t9 R0 ]# U" ?0 E) \# {' ~

# U% j" k% o, A& R8 }4 J! T$ N! S! `+ X8 L
) E0 J  V# u3 x& N) e5 v7 `
收藏 1 评论0 发布时间:2022-3-23 17:00

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版