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

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

[复制链接]
STMCU小助手 发布时间:2022-3-23 17:00
本篇文章提供两种方法:! R: b# Q( w/ H
一种是 :IDLE 接收空闲中断+DMA
% ]' Y: k0 J3 F1 N7 l一种是: IDLE 接收空闲中断+RXNE接收数据中断
+ ^" W% @8 }" K7 z" z# j
3 T% e! T# b$ u# {, l* f! E都可完成串口数据的收发" |8 m/ H* E" y6 c3 K

8 Q% A1 g) w+ W% P: o知识点介绍:
* _3 j" M: j3 FSTM32 IDLE 接收空闲中断

: I5 D" P% A5 i( ?功能:
+ E" t' ?, H$ k+ F, e+ V在使用串口接受字符串时,可以使用空闲中断(IDLEIE置1,即可使能空闲中断),这样在接收完一个字符串,进入空闲状态时(IDLE置1)便会激发一个空闲中断。在中断处理函数,我们可以解析这个字符串。7 t/ O( v1 X8 P# i( o7 B

; t, x& c! x" l2 b2 `接受完一帧数据,触发中断1 H9 X. ?6 I( s( F9 M
3 J8 V5 G* X* J4 G7 B  Z% e+ o
STM32的IDLE的中断产生条件:9 q7 D9 g6 q4 m
在串口无数据接收的情况下,不会产生,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到数据,即产生IDLE中断
6 z4 f0 w* S0 w9 n$ \
: O3 R/ W+ i$ ]! S; cSTM32 RXNE接收数据中断
; S' W/ ]0 ~! C) D( Y. T3 F- r功能:
. g- Z$ t% m4 T. K- |当串口接收到一个bit的数据时,(读取到一个停止位) 便会触发 RXNE接收数据中断2 ^) a" Q# f: D  K! C% p' K+ a

1 i3 A9 i' _+ M# M! M3 u, K接受到一个字节的数据,触发中断
. l8 N! c# L" G, `
* t: ]. {( D/ I. ~' w- ]4 [7 }2 o# r. {比如给上位机给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。: T9 ]3 L0 m# d, a
# C* b: a3 P3 v' K4 e; y/ }, V
串口CR1寄存器( c& [7 ^+ s: Q
) h% a0 h$ l: m/ @3 l# U: h
KPGP$%K4Z`P]5~NPWSBD91K.png 5 k: Q! @* f* s0 [5 P5 W8 [- H) L6 ]' i

* s* U6 |- K' R1 x! q对bit4写1开启IDLE接受空闲中断
/ H/ u/ e9 k6 k2 s! ^; {& [,对bit5写1开启RXNE接收数据中断。
. P+ w* {5 Q5 N" G. H1 ]3 D& c% {8 d" s# Y; s# e
串口ISR寄存器
5 p3 A8 [# Q  v4 a
0 K; j+ f" m9 F" A0 A8 N L%TE0SWP0~FE)I7T4TOEER9.png
8 `# Z  _( ~7 ^$ Y. N) {, b
! e: b& j7 N3 ~% j; A! K! Y0 ~此寄存器为串口状态查询寄存器
  ^$ E& v) ~) _3 o- |
. O- q3 w* u& I( l- I/ S) J! c当串口接收到数据时,bit5 RXNE就会自动变成1,当接收完一帧数据后,bit4就会变成1.
6 }; d/ h% ?* _+ n" e* R  @: s
4 y$ p% Q6 b6 |1 P8 F# x! A清除RXNE中断标志位的方法为:* F5 N; x1 k) r1 g
* x2 k5 g9 ?& Y6 Q: F
只要把接收到的一个字节读出来,就会清除这个中断- n+ b, w' ?9 D+ O; E9 V2 S' e

4 `+ [/ o( `/ z2 F1 ^/ G1 N! }在STM32F1 /STM32F4 系列中 清除IDLE中断标志位的方法为:
1 d+ V9 h7 ]6 s  L: T* T$ R5 z- Y, r0 L! N0 ^& U
1.先读SR寄存器,7 h6 v/ r2 W5 ?( u$ v
2.再读DR寄存器。

& a2 L& r5 e# V* y1 }9 p2 W" }/ ?9 e1 a( {' A: t  B, O
3F]Q2F56KOVOR(9K8AF{TS2.png
# ]8 i6 R% ^: K. F$ o1 c0 h7 O8 ?' ~* l$ O  _6 a
memset()函数" c! N( I$ U6 Y+ H! a$ v

3 M* v/ [6 ?' M- p9 B
  1. extern void *memset(void *buffer, int c, int count)        
复制代码
% p: Q% D1 e: H5 R) [1 s  s
buffer:为指针或是数组
; O# W5 O/ a/ I) k: Lc:是赋给buffer的值
; M2 h3 Y5 k8 ^! d3 A  qcount:是buffer的长度.
/ V3 h# K5 ?* P, S( P* HUSART采用DMA接收时,如何读取当前接收字节数?6 D: g  [! t0 v$ l2 @
  1.    #define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR);
复制代码

7 }" P; g0 {3 Y- bDMA接收时该宏将返回当前接收空间剩余字节1 g+ r% J/ _! Z0 e( u& i' O
# L( H+ d. {  S% O
实际接受的字节= 预先定义的接收总字节 - __HAL_DMA_GET_COUNTER()
( x& X; b+ X" A/ ~; J% ^1 E) }4 d; Q* e4 K% t2 N
其本质就是读取NTDR寄存器,DMA通道结构体中定义了NDTR寄存器,读取该寄存器即可得到未传输的数据数呢,; d7 k8 C. ~, _' H) [) R1 c" V0 S
% U' C2 j( m, U. }6 z6 R
NTDR寄存器
) T; N; z7 n6 K' U7 [# d% _+ Y8 ^+ m$ x
_[`8QZUY((Y87)B8BSJ36}B.png
/ R, _/ C- f- T2 h6 d6 d- ~% r1 D1 O6 x. c* _% n/ @
实现方法:
: r& }! K' s+ m% P8 I4 P1 i两种利用串口IDLE空闲中断的方式接收一帧数据,方法如下:( `7 j5 G- ^/ W2 l# _0 ?# A+ T

+ r- m- R: z7 D% Z方法1:实现思路:采用STM32F103的串口1,并配置成空闲中断IDLE模式且使能DMA接收,并同时设置接收缓冲区和初始化DMA。那么初始化完成之后,当外部给单片机发送数据的时候,假设这次接受的数据长度是200个字节,那么在单片机接收到一个字节的时候并不会产生串口中断,而是DMA在后台把数据默默地搬运到你指定的缓冲区数组里面。当整帧数据发送完毕之后串口才会产生一次中断,此时可以利用__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);函数计算出当前DMA接收存储空间剩余字节2 [; u0 ^# I- v. c/ ?1 f

  J, `: F/ K4 k* t! L本次的数据接受长度=预先定义的接收总字节-接收存储空间剩余字节
: `) n5 v  U2 t# t/ H/ v
1 `# A& d: d1 U5 ~  t* r比方说 本次串口接受200个字节,
4 l% k7 f" v; _/ C" r/ S+ b. X
  1. HAL_UART_Receive_DMA(&huart1,rx_buffer,200);//打开DMA接收
复制代码

2 q* A6 Z9 O9 [6 [0 m  g然后我发送了 Zxiaoxuan 9个字节的数据长度+ _) i4 w( e3 \% j' \

  l9 u1 Q: C# {2 g! G那么此时 GET_COUNTER函数读出来 接收存储空间剩余字节 就是191个字节! `) s0 _1 g6 d6 T/ x, A" k7 w

8 C3 F6 t4 f3 h/ r. `9 I/ R$ Y实际接受的字节(9) = 预先定义的接收总字节(200) - __HAL_DMA_GET_COUNTER()(191)8 W+ `# _" N9 p, V% Y1 f
9 = 200-191# F0 H: j2 |  {8 r4 F

, N4 T' D/ \) c0 K7 n; h应用对象:适用于各种串口相关的通信协议,如:MODBUS,PPI ;还有类似于GPS数据接收解析,串口WIFI的数据接收等,都是很好的应用对象。
  k. w$ X& R- O; i  X( B" f5 Q! G9 e/ ~: l* e
方法2:实现思路:直接利用stm32的RXNE和IDLE中断进行接收不定字节数据。 每次接收到一个字节的数据,触发RXNE中断 将该字节数据存放到数组里,传输完成之后,触发一次IDLE中断,对已经获取到的数据进行处理# q+ Z4 A* S) a' M% a; t2 a; I
7 e6 ]' V4 W3 c& x
例程1
7 e  Q3 _/ ~3 J+ T) K& J5 z. [本例程功能:

/ }" }2 V' S: ?! z4 \9 J) w+ F7 Z2 K5 P) z4 x8 D0 U
使用DMA+串口接受空闲中断 实现将接收的数据完整发送到上位机的功能
. Z8 L8 L7 Q0 F7 }3 a1 Y% {: C& w8 g# {" g' _6 R5 m. o
/ M7 L+ \; p6 E1 j3 V. B  x
& O( c  \' {* E2 i7 M
接收数据的流程:
- J, ?5 c( I9 q7 @7 H& n9 @. b& M5 K
首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,程序此时也不会进入接收中断,在软件上无需做任何事情,要在初始化配置的时候设置好配置就可以了。( ^! I1 \" W2 o
9 J+ b, [, L$ f+ D4 c
数据接收完成的流程:
# F" f6 j. l. ~. M) Z$ Y
8 L7 p4 G: U' X# T5 L( M3 |+ |  l当数据接收完成之后产生接收空闲中断④% p; N% _7 X3 Y% m( [7 ^- Y

' e; L3 _5 n: m. T* w在中断服务函数中做这几件事:& H2 Q. k# W/ k; v3 b, c6 v6 Y7 i
判断是否为IDLE接受空闲中断
7 N) K! I5 _7 V: u( q' v, p" _  j在中断服务函数中将接收完成标志位置1
4 U6 @9 p- M" ?* j% c关闭DMA防止在处理数据时候接收数据,产生干扰。
4 @, W" b0 `) p! b: ^5 V. Y) k计算出接收缓存中的数据长度,清除中断位,( a2 i! R4 c) F+ u

/ m  k. T3 s5 {6 x3 q4 bwhile循环 主程序流程:
: K) O! y% i- G/ e3 z6 U& T$ t( n3 @主程序中检测到接收完成标志被置1. Q+ [8 k! ~2 D
进入数据处理程序,现将接收完成标志位置0,) E; P* q5 T& ]9 G- q0 o/ I* y5 }4 v
将接收到的数据重新发送到上位机, y/ c: X" y, l
重新设置DMA下次要接收的数据字节数,使能DMA进入接收数据状态。3 a; d: Y! c; t$ v7 N- w, H2 [) a
% W! z% R4 G! x" Z5 r
例程代码:
1 l0 y' J, M! V3 t( Fuart.c( T6 M. N0 w4 q% f3 [% R* s/ u
  1. volatile uint8_t rx_len = 0;  //接收一帧数据的长度
    % i8 Q8 F6 v" C  E  e
  2. volatile uint8_t recv_end_flag = 0; //一帧数据接收完成标志7 V2 q# _+ N; M, h" C
  3. uint8_t rx_buffer[100]={0};  //接收数据缓存数组
复制代码
  1. void MX_USART1_UART_Init(void)
    9 g+ G/ M* ?# T8 g8 Q7 x3 K# k
  2. {
    5 \/ M! L" {0 A

  3. 3 W$ P- l/ v( C# H4 Q
  4.   huart1.Instance = USART1;6 y4 ]2 r) [& f: x+ U
  5.   huart1.Init.BaudRate = 115200;# W+ g6 x, b4 p% ^$ G8 B# s6 _
  6.   huart1.Init.WordLength = UART_WORDLENGTH_8B;, k/ V$ I6 B: {. _: K( j0 N2 w
  7.   huart1.Init.StopBits = UART_STOPBITS_1;
    6 t8 n0 R1 C5 H* c( z
  8.   huart1.Init.Parity = UART_PARITY_NONE;  X9 G( T; x+ I5 ]) {2 b0 u, T
  9.   huart1.Init.Mode = UART_MODE_TX_RX;
    3 k3 g* V; ^9 c/ D8 C
  10.   huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;. b3 p8 Q# z2 h
  11.   huart1.Init.OverSampling = UART_OVERSAMPLING_16;
    . U* B: ?$ _# c( P% N& f; t, V# j
  12.   if (HAL_UART_Init(&huart1) != HAL_OK)
    ; t: V: V  S2 Q% i7 R- W
  13.   {$ b/ j  |5 {( ~* `4 H4 T3 f
  14.     Error_Handler();  ?1 r6 s8 }5 n' @9 T
  15.   }
    $ {7 z1 v& u5 ]1 k2 _# P  e
  16. //下方为自己添加的代码1 a' Z* C9 G. a1 G% U" D
  17.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断
    2 M6 ~; |' [0 o. M$ Y$ V) f; v
  18. ) H2 h1 E+ V0 h9 o: Y# c) X% I
  19. //DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度( [( c) N  k5 }# Z- g0 }
  20.         HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);
    4 g5 Q  S5 G+ \' k
  21. + W4 C! E; U- s! h# `! R( p6 A
  22.         
    & G. k; {3 p. B6 A" q
  23. }
复制代码
2 r# H- F7 {% D
uart.h
! s$ N$ w2 g% \8 a9 y
  1. extern UART_HandleTypeDef huart1;9 G3 [( Z) s, o! L4 o
  2. extern DMA_HandleTypeDef hdma_usart1_rx;" c( Y; _# ?5 d) v4 q. Y" \  `
  3. extern DMA_HandleTypeDef hdma_usart1_tx;6 y* C0 d# b  w4 ^  p0 Y1 g
  4. /* USER CODE BEGIN Private defines */
    & `2 a$ N  W0 n4 E4 m/ o6 r
  5. 3 y" T. x6 x; T: ^1 }! ]

  6. : X% I9 z/ B0 }) T
  7. #define BUFFER_SIZE  100  
    5 n. x0 N: K, S- q# |0 \, ~
  8. extern  volatile uint8_t rx_len ;  //接收一帧数据的长度
    6 i) ?6 s2 n) b# d2 I" G9 @1 S
  9. extern volatile uint8_t recv_end_flag; //一帧数据接收完成标志
    0 v" I6 u8 O5 p' G6 M' G% S" w
  10. extern uint8_t rx_buffer[100];  //接收数据缓存数组
复制代码

- n5 r. M! g- _0 Gmain.c3 |( f6 V/ {8 a4 u. ?  R  n7 H: r% B

: t0 D  D$ a( p! C# n, m
  1. /*
    : \+ _' W8 K' C# |2 c0 u" s3 Z
  2. *********************************************************************************************************
    / ~: @2 Z$ ]( e! o  H9 M
  3. * 函 数 名: DMA_Usart_Send
    , e: R; k+ |  @6 f3 E
  4. * 功能说明: 串口发送功能函数
    1 I+ u" e% K# A! F! y+ J4 K' E
  5. * 形  参: buf,len5 N' z* A: D2 {+ j- J2 [
  6. * 返 回 值: 无8 @! ]% Q1 k* i. _2 S$ x7 _, a5 q
  7. *********************************************************************************************************
    9 J$ `3 I# ~) H# a8 h0 ^% v0 y4 e' P& W
  8. */- ]; v; T! q. n6 J3 E
  9. void DMA_Usart_Send(uint8_t *buf,uint8_t len)//串口发送封装
    # i5 F$ D4 A$ }, V$ Y$ H6 K; t! N
  10. {3 q& l/ Q) Y1 B
  11. if(HAL_UART_Transmit_DMA(&huart1, buf,len)!= HAL_OK)   //判断是否发送正常,如果出现异常则进入异常中断函数
    , g, w* f, D$ f' ?0 ?+ @+ ?
  12.   {
    9 C9 U& K( ~( f+ o
  13.    Error_Handler();
    ' p& b+ _2 Q% f. m4 p8 c# I* ?/ e
  14.   }5 L) T/ t' S1 f

  15. # W& ]* ?  |8 L3 N' X  v+ |) `
  16. }
    8 ~( z# w( R. x

  17. 8 s; ^9 `- t2 J) d1 e: B
  18. 9 P9 I6 J# M8 X* P# L
  19. ; J! ^8 D7 U$ V& a. V
  20. /*
    / {  d6 G5 |) ^
  21. *********************************************************************************************************- g$ y5 ~; P6 F! O4 y" D! H
  22. * 函 数 名: DMA_Usart1_Read
    ( ^; I& _4 C- y7 t
  23. * 功能说明: 串口接收功能函数6 Z1 Z7 ]( v( |% ~8 m  i$ w6 c
  24. * 形  参: Data,len
    5 ^- C2 x9 k2 H1 ?
  25. * 返 回 值: 无' |- B1 b2 J1 v- Z0 a
  26. *********************************************************************************************************( ~1 V- f0 F: e9 c  I) F, _
  27. */, s% Q- V$ Y* m4 I
  28. void DMA_Usart1_Read(uint8_t *Data,uint8_t len)//串口接收封装2 _* y5 r' l% X
  29. {) A9 }, ~$ @) U0 q6 J
  30.         HAL_UART_Receive_DMA(&huart1,Data,len);//重新打开DMA接收
    ! W6 p. g: V/ w3 b, D
  31. }
    $ O2 s6 G& `8 I+ v- ~+ {
复制代码

2 y2 d! x6 h- @9 [6 M7 m+ jwhile循环
$ E2 s' b2 r% A5 x# Y, p
* U9 p6 |. ?4 v
  1. while (1)/ w, ]7 C2 B0 Y& r, o
  2.   {
    # c5 ?* R9 r0 |1 q! @" v
  3.     /* USER CODE END WHILE */
    $ ?! d8 C( w8 Y

  4. 8 }3 e4 B" [, I: I5 @; v. v  _8 t
  5.     /* USER CODE BEGIN 3 */
    $ r/ c8 F( k- P3 s8 o  R
  6.                  if(recv_end_flag == 1)  //接收完成标志
    # R7 M0 F+ X8 z& h# M5 ~  S+ q
  7.                 {: `9 s! h2 M' `9 y
  8.                         
    5 g+ ^! M" t) ^
  9.                         9 D) X& g2 t: X  M0 `
  10.                         DMA_Usart_Send(rx_buffer, rx_len);$ ?9 p) K0 p' \0 u. h" E- T
  11.                         rx_len = 0;//清除计数
    0 a& }4 T2 L2 s2 M  [
  12.                         recv_end_flag = 0;//清除接收结束标志位2 G# b8 I5 D4 ^1 Y
  13. //                        for(uint8_t i=0;i<rx_len;i++)/ x5 Y) N% W$ k9 n4 O4 s
  14. //                                {# e$ j# p8 a% o0 @: S# @/ M
  15. //                                        rx_buffer<i>=0;//清接收缓存</i>
    0 m9 s. u; Q1 v* C: f; s& Q* j: K. o
  16. //                                }
    ; g7 z- V6 W7 c& {$ r' t6 `
  17.                                 memset(rx_buffer,0,rx_len);/ _- c- Q; E2 e+ d' J
  18.   }- l$ L0 V# Z. D7 b& m5 L- C8 S" Y, ^/ r
  19.                 HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//重新打开DMA接收
    % h8 }  t9 Z2 |0 h' i8 Q
  20. }& a1 H2 U$ g% w3 ~- b- \
复制代码
8 }$ ]' E4 D. l$ S7 N$ H5 r' i
stm32f1xx_it.c中
: C* @  N) I. i* @! S1 W: L8 l/ a+ G' ?: H+ |  f7 o
  1. #include "usart.h"
    6 N9 @( T" p  y& F9 ~
  2. : j% ^( O3 c& o- @( f
  3. void USART1_IRQHandler(void)8 l8 m% m1 E( i+ W) |1 ~
  4. {9 p6 D2 r9 I4 w% i
  5.         uint32_t tmp_flag = 0;3 ~( l. S& t" q7 h2 _+ p$ y
  6.         uint32_t temp;
    + w) B: \1 d" c' D
  7.         tmp_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位
    % f( ]& M. [$ \% e
  8.         if((tmp_flag != RESET))//idle标志被置位6 w9 b, u3 I9 W* z& }% A
  9.         { : l! C. G  t) X3 y
  10.                 __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位! z9 I2 X) A* V3 }. h2 H
  11.                 //temp = huart1.Instance->SR;  //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
      \, `2 A, F- k: i4 L
  12.                 //temp = huart1.Instance->DR; //读取数据寄存器中的数据* T, M* n. m- R, i( T7 J. x
  13.                 //这两句和上面那句等效+ ]9 a- |3 e) I. t7 s0 p# \( u4 }/ r
  14.                 HAL_UART_DMAStop(&huart1); //  停止DMA传输,防止
    1 Z7 d, U& ]# Y+ d: N8 j2 V
  15.                 temp  =  __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数   2 u8 Z1 D# M8 f" X" Q7 B/ a# q
  16.                 //temp  = hdma_usart1_rx.Instance->NDTR;// 读取NDTR寄存器,获取DMA中未传输的数据个数,
    0 E8 b8 S; \8 a" V0 ^3 n
  17.                 rx_len =  BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数" q+ f# \1 e. D# W+ Q
  18.                 recv_end_flag = 1;        // 接受完成标志位置1        
    ! q" ^% U$ A( t( g( v
  19.          }) [% D* u: C+ M( s* s2 d+ ?, D
  20.   HAL_UART_IRQHandler(&huart1);0 l. @; n$ P9 |- g5 A" Y$ h
  21. : v0 L7 z# {# E
  22. }
    1 C- w/ V. I8 B
复制代码

# t& x: u  i" q% s+ E注释详解:3 }& s9 `: O" Y6 v, P( c8 H
注释1:
! s$ L( _5 a2 j8 Y! H* `" b
  1. temp = UartHandle.Instance->SR;  //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
    * B) M5 o. _$ f5 q" ]/ \0 }/ A# h
  2. temp = UartHandle.Instance->DR; //读取数据寄存器中的数据
复制代码
6 o4 K" I) w. R) |$ ?5 f1 f3 U3 u
这两句被屏蔽的原因是它们实现的功能和这下面串口IDLE状态寄存器SR标志位清零的宏定义实现的功能是一样的:
, A, w# N; P0 o( j6 d# M+ F; ~3 Y' a  n5 r- V8 k
__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位; G6 I$ r( z! \8 H* f

, w: R0 H1 l+ N/ e( V9 E( O3 @我们可以点击这个宏定义进去看看,它实现的功能和上面两句是一样的:
- c- q7 _* x5 C0 }5 ~3 P$ Y
$ L5 t0 |8 A  \4 z* r" ^6 \#define __HAL_UART_CLEAR_IDLEFLAG(HANDLE) __HAL_UART_CLEAR_PEFLAG(HANDLE)0 f% b8 j' P  R: b

) `5 l* C- n% |7 f. I2 w MGNYWWUU8_60()GL0)P@]RJ.png
2 M0 ?) H% T/ `- h( f7 P3 X& y8 |$ m* n
注释2:
. Z/ v" ~" \: j4 U) ^
8 _! e4 c; V. W& M
  1. temp  = hdma_usart1_rx.Instance->NDTR;// 获取DMA中未传输的数据个数,NDTR寄存器分析见下面  
复制代码

% `( L" b3 Y: a+ c同理, 这句被屏蔽的原因是因为他和上面的__HAL_DMA_GET_COUNTER的作用也是一样的,都可以获取DMA中未传输的数据个数
% c3 W& W3 Y8 w4 L3 r
  V& a' b  u5 O) c ZO1V@ZT1H)]~NR@V])59TT1.png
+ d) ?# Y5 I& c0 ]7 @
5 w% \( [7 Z& S* B1 u: q测试正常:4 o- J( D$ c4 `' P9 z: {' @

& D1 R& {. a( c J92(YI{0KM`ID{%8[LRX9`M.png
' i% j3 I& g  H
5 b1 s  m7 J0 [1 E7 Q& ^此程序可以进行接收不定长的数据帧,不需像RXNE每次接收到一个字节就进一次中断。
1 f- F& x4 U, X+ G) C7 Y1 [( J同时开启DMA传输速率也会加快
5 d/ s* K" U  e0 ?# {: ]$ Z例程2# O, d* s/ N# d
UART中断使能函数8 p8 y8 }$ M2 t1 U
  1. __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__)
复制代码

+ I8 ^# S+ Y3 W6 @8 x: D功能: 该函数的作用是使能对应的UART中断
# a, s, Y  O4 k; \; B( O' S! [
8 I, ^4 b/ p$ q1 p8 f$ ?, w在stm32f1xx_hal_uart.h中被宏定义9 F0 s: u: h+ G4 h2 r  e8 k1 f
1 [* l1 N! Z0 Y+ e; c: _( y6 P
DWY99KRLDC0B_J1WG{$N`30.png
+ T5 J% I4 L* A! A
: Y, s( Z" T: y可以使能的中断一共有这些:; T8 z8 j3 H0 {0 }6 N6 c; y7 I
% \- V2 L3 @! a* c" Q
%H4WBTH~8)WZY49JBXC6}[M.png , ^- g1 P  G3 Z7 W) r6 L+ h
1 ]- A' m% ~5 ~
我们现在所用到的为:
" }9 x* \6 w2 R( m* P0 R
  1.   *  @arg UART_IT_RXNE: Receive Data register not empty interrupt
    " i# ^- o# m# _, L0 H0 G7 i9 |, @
  2.   *  @arg UART_IT_IDLE: Idle line detection interrupt
复制代码

2 K" K! Q7 h# [9 ERXNE接收数据中断
0 g" x; q3 A: b' aIDLE 接收空闲中断2 e' C8 L, Y. f& F) W

( c7 |, m$ M  t) B! A$ p& V U5PXUTDR$S1Z2XH0U5SHRKC.png 7 w  A) s  o& Q) h6 H7 O$ I

+ K" I, S. Z2 S; i本例程功能:( Z$ H+ c9 B! j8 n! X5 {8 W- c
8 i7 k1 g( b: z* g: f7 X% i
使用RXNE接收数据中断+IDLE串口接受空闲中断 实现将接收的数据完整发送到上位机的功能+ C/ D9 W6 i! |) M* N; d
( h8 u* H+ {( t/ I/ _5 G
接收数据的流程:, [, X- d* j) d( ]; l) Z

; g5 c, S# u# m9 E首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,同样初始化配置的时候设置好配置就可以了。, T4 q5 A3 K& O, X5 U
. X% t+ w2 O! I' `, t: x
接收到一个字节数据:* ?2 K: d+ O3 B3 P
接收到一个字节的数据之后,便会进入RXNE接收数据中断,
' S0 b* C2 {) h9 W0 m* K) M
% J3 r' R2 O/ D  Q7 c接受完一帧数据之后,便会进入IDLE 接收空闲中断
" v5 y, H3 x0 f7 s- {  ?/ j/ w7 U8 n+ K/ m  n
在uart.c中添加中断函数
% l2 a8 q, A0 h% G; j4 B' n2 }( _$ T
  1. void MX_USART1_UART_Init(void)
    4 f2 b# K& T8 j& Y9 O4 T+ z
  2. {
    , X: a6 \) _( e% i+ f% [; O

  3.   O9 J2 y3 }7 |
  4.   huart1.Instance = USART1;
    4 ?( n9 G1 r# I
  5.   huart1.Init.BaudRate = 115200;
    6 [3 N: Q6 Z9 S" K1 u% h" `) }
  6.   huart1.Init.WordLength = UART_WORDLENGTH_8B;
    . l) L8 W" o$ Y# H
  7.   huart1.Init.StopBits = UART_STOPBITS_1;
    ! b& E+ r& h, k$ c  U0 W) Z: A
  8.   huart1.Init.Parity = UART_PARITY_NONE;
    + y3 j! R' Z2 V. P) G9 J6 g& W
  9.   huart1.Init.Mode = UART_MODE_TX_RX;6 M6 }1 }$ o2 }: |# l
  10.   huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    $ Q* d0 b6 t5 M, Y; Z5 n
  11.   huart1.Init.OverSampling = UART_OVERSAMPLING_16;
    1 b# [" d0 Y) q; y4 i1 X, V( i
  12.   if (HAL_UART_Init(&huart1) != HAL_OK). Z$ R2 j6 c: ^# O. C3 O$ {
  13.   {
    9 l) S& y% P( N; t2 l
  14.     Error_Handler();
    . P$ b( V# D% ^% M
  15.   }4 H5 D6 t9 }$ s5 x& ]2 U" b' C: L

  16. / I4 A. Y  q% c( X$ P* |
  17.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); //使能IDLE中断
      A2 L8 O) B/ z2 P: k! s
  18.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断* e5 x5 K0 ~3 d0 G0 K' I$ O
  19. 9 w3 ^3 I4 r% E
  20. //DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度
    2 M5 a) e3 O" E% l9 K
  21.         HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);
    4 E5 w! M: A2 G  a5 P$ l
  22. 7 [, j7 w' _! `, P  t2 I6 }3 }' e
  23.         
    % A' H1 R& p# M  T" n. g
  24. }
    # U" R1 G& s5 B. H% t
复制代码
其核心代码就是下面的两句
5 `; C2 L" H9 H& ?6 ?& n. U* T( e! v3 v( I  Z: c- {
4R[EVR6ESO3C7R2K_B$FN.png ; n1 D' Q5 `/ V8 q/ i' E/ B$ r

3 q& Z1 M. T6 U: i第一条语句用来判断是否接收到1个字节,第二条语句用来判断是否接收到1帧数据
+ H- A' l" a/ e
  1. 7 `- h" A+ n, ~( E+ M
  2.         if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!= RESET)  //如果接收到了一个字节的数据
    % h/ L8 }. V; b! f0 v5 L* j& w
  3.         {6 G) G% C1 d# f  ?. r" e
  4.                         HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);    //反转LED
      i3 E' N, `( A5 Z
  5.         }. o2 W3 ^7 z; j
  6.         1 y# J4 B3 J, O6 ~% L: s! q
  7.         if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!= RESET)//如果接受到了一帧数据2 g8 i/ Q6 B) f; B+ x
  8.         { 4 o( e# @: y% _" ?- C
  9.                 __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
复制代码
# r3 I0 b# ~( P  W
其余部分于上方例程1基本相同
# c$ o3 x, `% e9 A2 O
8 g( q  J, O2 L" j7 _! b- u& Y本次测试是检测接收到一个字节就反转LED% u8 v2 E" z/ L5 a+ N, Q, V
经测试,历程正常:" H* u( _& N; C2 {5 `  [6 M: I; A

4 [7 w! O$ x2 C2 t1 B9 a! O
* D$ {: S( `8 x& b+ J1 D. U  N$ n! c7 p5 }

- s6 t% E! T3 U
) X1 q4 D4 A- t$ [: U
赞 收藏 1 评论0 发布时间:2022-3-23 17:00

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版