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

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

[复制链接]
STMCU小助手 发布时间:2022-3-23 17:00
本篇文章提供两种方法:
6 U# E; L) ~# \" S# O. ]3 J一种是 :IDLE 接收空闲中断+DMA) k- w9 |: ^. d: {
一种是: IDLE 接收空闲中断+RXNE接收数据中断) s$ C* q: K* h- }* c4 l- ~
. z6 V  \/ C8 r# @' o/ j- i
都可完成串口数据的收发/ b7 g8 ?4 @. b0 D" l

5 z2 G+ ^3 c/ l! Y1 g( K8 O' P( j知识点介绍:& C: g- U9 b" r6 g8 x
STM32 IDLE 接收空闲中断
! J5 e2 y% c: r  E2 x
功能:
, [1 R+ X- c- e5 `9 }在使用串口接受字符串时,可以使用空闲中断(IDLEIE置1,即可使能空闲中断),这样在接收完一个字符串,进入空闲状态时(IDLE置1)便会激发一个空闲中断。在中断处理函数,我们可以解析这个字符串。
; e; I- k# c/ h3 b# J3 m  ?
+ |  A# I+ `- C& b6 b# [接受完一帧数据,触发中断
7 i8 \* W8 x. ^7 l7 _! Y7 @
1 f3 t1 u6 B% Y6 j4 ASTM32的IDLE的中断产生条件:
9 P, x2 |6 S5 m  |( B在串口无数据接收的情况下,不会产生,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到数据,即产生IDLE中断$ W/ h  o- T$ x# O: Y( t
: k/ Q3 V1 I+ {
STM32 RXNE接收数据中断
3 {% [$ p2 O6 J" q) D功能:* ]: E( R5 ]: G& S+ X
当串口接收到一个bit的数据时,(读取到一个停止位) 便会触发 RXNE接收数据中断5 {7 {: P1 Z" ^: r* j
! s& k9 \% z$ C' h! c2 Q
接受到一个字节的数据,触发中断
' F  X/ }% }: @, o6 g2 x8 g% G  z2 H( G- Y" i+ i# |& d- Q
比如给上位机给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。; i8 V! H& h- d1 S* E4 J8 H
  ]1 ^6 X) j% a* D- Q
串口CR1寄存器; ^9 c* ?5 y* a3 W  i

5 r+ O6 A  e" t0 y KPGP$%K4Z`P]5~NPWSBD91K.png 2 U" X9 d5 |0 o& d
) W5 I* F6 Y. j: E
对bit4写1开启IDLE接受空闲中断
5 }: z% v  N% }/ D4 ]( ]8 u,对bit5写1开启RXNE接收数据中断。9 y5 W! @+ _0 g

7 X$ t7 C" C3 s& v- M串口ISR寄存器1 `6 Z* S; b  {8 X/ C/ [8 K; U% |
" `" J: W: u# ]8 @
L%TE0SWP0~FE)I7T4TOEER9.png $ u% l( K/ A; S' y: r
, }+ D6 ]7 n! ^( T" I, U
此寄存器为串口状态查询寄存器$ u, ]8 V9 K6 R4 N8 e0 }2 Q

3 y. I1 E" `0 `当串口接收到数据时,bit5 RXNE就会自动变成1,当接收完一帧数据后,bit4就会变成1.5 Q. H5 d& S4 C% P6 G6 o
( u! |+ Z8 J9 x2 y/ T7 k! ?( E
清除RXNE中断标志位的方法为:0 Z- e# f3 ^. f( L+ h
0 c% `6 \& d: e. W
只要把接收到的一个字节读出来,就会清除这个中断! {, g# R% K: y+ E
9 i- J" }  Y6 D( {
在STM32F1 /STM32F4 系列中 清除IDLE中断标志位的方法为:4 u: D  @" y+ k+ M. i

% {# k; x* u% ?* _# ?* P1.先读SR寄存器,
' T8 {3 J( [4 F9 }: w2.再读DR寄存器。
. ^3 j2 T" ?0 y9 x
) I! L/ B; @. T. U% o
3F]Q2F56KOVOR(9K8AF{TS2.png : {2 b7 i( v  l/ p5 l$ G0 V
" a2 u) {& S& k: p) D5 d
memset()函数( P) V: [8 W( D: _5 _6 N
3 j( P5 {9 ?) E9 A5 d6 w0 W9 G. n
  1. extern void *memset(void *buffer, int c, int count)        
复制代码

% |, Z9 c) q' Xbuffer:为指针或是数组
! k$ r0 L0 ?3 |c:是赋给buffer的值
$ R* f3 H$ y$ z5 E8 Gcount:是buffer的长度.
' F- q0 P& N0 z# sUSART采用DMA接收时,如何读取当前接收字节数?9 e# Y  F, R+ P2 f
  1.    #define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR);
复制代码

3 \0 Y& A4 Q5 V2 S( hDMA接收时该宏将返回当前接收空间剩余字节
# `( d8 Q  u& k  V, b- V9 Z! o1 ^3 _+ b* e: R1 u; _6 z
实际接受的字节= 预先定义的接收总字节 - __HAL_DMA_GET_COUNTER()7 X' w: m* Y8 z% f& I2 G: ~  q- E/ D
0 i! a6 J3 Y/ a2 ~- C
其本质就是读取NTDR寄存器,DMA通道结构体中定义了NDTR寄存器,读取该寄存器即可得到未传输的数据数呢,9 s) L4 t/ w% r" M0 S8 s

4 u$ W: x. @* h" m' Q4 C% sNTDR寄存器
0 U: l& Z8 E; {2 w5 H+ M  n4 T# c
% g4 f* K! K1 R, e6 O _[`8QZUY((Y87)B8BSJ36}B.png * {( W2 b$ b, x" u$ T- S5 V

; M) |+ O) F6 v9 w9 ]实现方法:  _4 H  T1 Y( h$ v( F
两种利用串口IDLE空闲中断的方式接收一帧数据,方法如下:
5 [4 U) O% z3 {4 j! e* o4 p( Z. U# M& m/ @0 ^
方法1:实现思路:采用STM32F103的串口1,并配置成空闲中断IDLE模式且使能DMA接收,并同时设置接收缓冲区和初始化DMA。那么初始化完成之后,当外部给单片机发送数据的时候,假设这次接受的数据长度是200个字节,那么在单片机接收到一个字节的时候并不会产生串口中断,而是DMA在后台把数据默默地搬运到你指定的缓冲区数组里面。当整帧数据发送完毕之后串口才会产生一次中断,此时可以利用__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);函数计算出当前DMA接收存储空间剩余字节% \" E2 B$ c/ V& P: M$ }2 B

5 m; k5 K1 R# l本次的数据接受长度=预先定义的接收总字节-接收存储空间剩余字节* m$ [7 s# o! b: H
, C, L  ^, s, l  k4 J+ ?, G
比方说 本次串口接受200个字节,
7 W7 Z' q* n6 f! |9 q4 ~
  1. HAL_UART_Receive_DMA(&huart1,rx_buffer,200);//打开DMA接收
复制代码

$ L+ }" F( V: y9 a然后我发送了 Zxiaoxuan 9个字节的数据长度9 E! h4 c: h3 _$ O9 }! f
& d$ o; i) x3 c4 l1 U! d
那么此时 GET_COUNTER函数读出来 接收存储空间剩余字节 就是191个字节
3 p- Q% Z( l% Z! B7 D1 z5 k9 D: w2 e) S; W
实际接受的字节(9) = 预先定义的接收总字节(200) - __HAL_DMA_GET_COUNTER()(191)  F5 o& O' l% P, R
9 = 200-191% t" c' L3 x3 T0 O
% F& t: }5 r# D- {/ @0 c! Z) ^: @
应用对象:适用于各种串口相关的通信协议,如:MODBUS,PPI ;还有类似于GPS数据接收解析,串口WIFI的数据接收等,都是很好的应用对象。
/ E) J) ^" `8 @, P( e
9 J& @9 w  P% c; p方法2:实现思路:直接利用stm32的RXNE和IDLE中断进行接收不定字节数据。 每次接收到一个字节的数据,触发RXNE中断 将该字节数据存放到数组里,传输完成之后,触发一次IDLE中断,对已经获取到的数据进行处理9 {$ Y3 D2 s5 U; }' ^) H; K5 U
0 d$ `# b" G, U$ s# X, J
例程1
: _2 J4 {/ x- D) n; M" Y本例程功能:

/ I4 E# w0 C# ^* z- O) W
/ L* W: d1 F" l* z. o使用DMA+串口接受空闲中断 实现将接收的数据完整发送到上位机的功能' b) r. x( q8 ^. ~1 n) W5 r
6 [: A* O! |. j- w( M

0 B: R4 r: q8 }6 M
- A( ?& G1 {0 S, b, J2 Y, e" B" e/ A; G接收数据的流程:
' L4 [/ {) V. u8 f: D0 R) o7 v
; T+ I" R5 _) B- T首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,程序此时也不会进入接收中断,在软件上无需做任何事情,要在初始化配置的时候设置好配置就可以了。
+ o0 A6 X/ f& M' D! B2 s
, U7 \* V& e, n; w+ @$ P数据接收完成的流程:
1 S" K* k1 Q; O( K. D0 y/ h
3 v/ v/ a+ A0 y. [3 l当数据接收完成之后产生接收空闲中断④( R! a' R( Q. }) p5 w

+ @! p5 \. i# m0 f$ `: i. p在中断服务函数中做这几件事:9 ?2 _! g2 `  i" F3 J
判断是否为IDLE接受空闲中断
  W0 B9 P  W- H( Z- N3 @- d在中断服务函数中将接收完成标志位置19 \% S" ?  }$ l/ R
关闭DMA防止在处理数据时候接收数据,产生干扰。
# J- E3 j0 [/ e( m计算出接收缓存中的数据长度,清除中断位,# y9 B( ~' Z  {  l
/ {' g$ i; F( g
while循环 主程序流程:( f! W3 A$ |: n
主程序中检测到接收完成标志被置1  l+ X: e  s7 E$ y: P
进入数据处理程序,现将接收完成标志位置0,2 N* r! C+ F, C$ h2 q
将接收到的数据重新发送到上位机3 Z1 u4 ^4 |! U9 `5 j& }, E) `
重新设置DMA下次要接收的数据字节数,使能DMA进入接收数据状态。9 B8 `3 ~% U8 o
7 x8 u  a! j' n
例程代码:! w1 r+ c* H) ~, {: B
uart.c5 e. c+ s, f9 R+ i( x3 @
  1. volatile uint8_t rx_len = 0;  //接收一帧数据的长度
    / f# e/ c; K8 H# D8 p. o! t5 {4 b
  2. volatile uint8_t recv_end_flag = 0; //一帧数据接收完成标志
      X! }& k: |3 r5 d( B# |
  3. uint8_t rx_buffer[100]={0};  //接收数据缓存数组
复制代码
  1. void MX_USART1_UART_Init(void)
    4 s, ~; j2 p5 F- M+ q) x! x
  2. {6 C' I9 Y- o0 `$ R1 M
  3. . m$ s: ]  k9 r; s# d+ L
  4.   huart1.Instance = USART1;
    + P/ ^, @& d9 \; T1 L
  5.   huart1.Init.BaudRate = 115200;
    & n( q6 m0 a4 e$ s4 ?% W
  6.   huart1.Init.WordLength = UART_WORDLENGTH_8B;  G! L1 i8 L$ S/ P
  7.   huart1.Init.StopBits = UART_STOPBITS_1;
    ! S9 ^' t4 ]1 F& e/ ~
  8.   huart1.Init.Parity = UART_PARITY_NONE;
    6 z! e4 b  y# D$ f1 j6 i5 S( {
  9.   huart1.Init.Mode = UART_MODE_TX_RX;
    8 x0 K% L# ]- ^6 x+ I: _/ H
  10.   huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;3 `% u6 A* P4 Q, ~4 {5 P& x2 q
  11.   huart1.Init.OverSampling = UART_OVERSAMPLING_16;
    7 [" R+ X/ n' P# p; G
  12.   if (HAL_UART_Init(&huart1) != HAL_OK)2 _# O* Q2 Z. O# \: P0 E$ f8 e
  13.   {
    . V- i7 \$ J/ h  Z$ s/ X5 P2 ]" W7 P
  14.     Error_Handler();
    . i; N$ _5 R: u9 t& T' I
  15.   }
    8 {0 G. y5 K. S" J5 \
  16. //下方为自己添加的代码
    . J* q2 B1 a0 g
  17.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断
    2 U8 b2 T5 y( o$ ?% d* J8 }
  18. 6 {; x3 z) ^" s1 v
  19. //DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度+ n7 f% C( E( I" ]
  20.         HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);  i, x& C8 e1 W3 M1 |

  21. ! f0 Y7 {0 R2 n, K! K# k
  22.         + i* L( H- t$ o
  23. }
复制代码
6 e0 i% p; s6 L4 y/ o$ Z
uart.h' y  ^8 Q! z5 j$ y7 ]
  1. extern UART_HandleTypeDef huart1;! T) Y. l0 c7 \* ~6 D1 y5 k
  2. extern DMA_HandleTypeDef hdma_usart1_rx;- C* P. W, P$ T$ r0 N
  3. extern DMA_HandleTypeDef hdma_usart1_tx;
    ) [) F2 b4 @8 N6 G# a
  4. /* USER CODE BEGIN Private defines */! [% \  o+ Y8 N8 z1 W

  5. . ~: m7 r; I" y' B$ p! d  W

  6. 9 M7 j9 u+ b. a$ }
  7. #define BUFFER_SIZE  100  
    $ `5 J# H" s) A% ~4 ~+ X: G' I6 q
  8. extern  volatile uint8_t rx_len ;  //接收一帧数据的长度: W; ~& }5 ^& ]% `( h) S0 Z
  9. extern volatile uint8_t recv_end_flag; //一帧数据接收完成标志
    , H2 u/ y5 M" D, e: L$ `* Y
  10. extern uint8_t rx_buffer[100];  //接收数据缓存数组
复制代码

  E$ U7 B  i% Z; nmain.c. e, w. D- B* ^; [: v# N
' `0 e4 `7 ?% i  ^5 K; e# G  ?
  1. /*
    & p% H) b- `- g* ^- u6 v
  2. *********************************************************************************************************5 w/ f) ?4 j  \, k$ s9 R
  3. * 函 数 名: DMA_Usart_Send
    & F) X& _3 j. B- ?/ O2 Q3 H
  4. * 功能说明: 串口发送功能函数
    6 @+ M( W6 t1 \$ n. y
  5. * 形  参: buf,len
    & V. k  G3 [- u' x, c, ~
  6. * 返 回 值: 无/ y3 `+ z( A5 f  @( z, M
  7. *********************************************************************************************************
    5 W: _* j0 I7 C/ N9 L5 K
  8. */- `6 Z4 @, a! I2 M: {" _8 q- E
  9. void DMA_Usart_Send(uint8_t *buf,uint8_t len)//串口发送封装
      _5 l1 g* j9 Y8 C7 K9 {- w
  10. {0 W, ^3 {( M0 v+ m! A6 X. L
  11. if(HAL_UART_Transmit_DMA(&huart1, buf,len)!= HAL_OK)   //判断是否发送正常,如果出现异常则进入异常中断函数/ }; @! c8 i4 m
  12.   {
    - W6 t3 |+ s5 h! T/ F
  13.    Error_Handler();
    " b2 B' X( v8 `7 j" m* P& v% ?3 `
  14.   }
    ! A# E: t6 w" a5 M  ]

  15. 5 U  n7 ^- ]) \6 Z
  16. }7 \8 D# b) r2 |& y

  17. ) N2 n# u$ n' Q+ c! d9 c3 ^
  18. 5 [" z/ [6 R2 p
  19. / ?: l1 Q; K8 w. t0 x2 ^
  20. /*
    ( r3 _3 [* l1 j% l. G7 B
  21. *********************************************************************************************************
    0 P" I& o" ?/ [% D0 v* T: x+ r) t
  22. * 函 数 名: DMA_Usart1_Read& {$ c9 M  ^  a/ ^# m
  23. * 功能说明: 串口接收功能函数
    ! }" n% [/ d4 P7 K0 Z: u; B
  24. * 形  参: Data,len
    7 j4 o/ x$ N7 Z7 Y. A( Q9 E8 B
  25. * 返 回 值: 无
    3 W0 v7 N# j, s! n. m+ b
  26. *********************************************************************************************************
    5 f( f7 k8 O5 k3 {" x7 E/ J
  27. */9 p, ]* G5 n' p0 I0 F) Z& ]
  28. void DMA_Usart1_Read(uint8_t *Data,uint8_t len)//串口接收封装
    ! g% a$ ^! r; S$ f; L" n8 |
  29. {
    8 i3 G3 \) d% K8 Q6 Q2 N0 |
  30.         HAL_UART_Receive_DMA(&huart1,Data,len);//重新打开DMA接收
    * j) w) s# Q+ Z/ Y
  31. }
    ; X  ?1 A# I/ |0 P: Y9 w1 \
复制代码

2 D1 d$ y( G4 P& [- C4 N3 Xwhile循环
9 |% L$ e0 V  x2 ~# O4 F% b! R* \+ \  u
  1. while (1)
    % ?7 h: J* K* P# O
  2.   {5 ^7 D- V) D  B" [8 D0 L4 Q; H
  3.     /* USER CODE END WHILE */4 U8 n2 f- f# `
  4. " y* G: d, d: y, U/ o% s
  5.     /* USER CODE BEGIN 3 */
    1 Z' u+ J5 m6 @
  6.                  if(recv_end_flag == 1)  //接收完成标志
    " s+ v& I0 h% M8 k( X
  7.                 {
    4 @2 x# N- X& N% U
  8.                         * d7 h( M" g# b( e* h, A
  9.                         ! R5 C! R5 o, \+ T" p
  10.                         DMA_Usart_Send(rx_buffer, rx_len);
    4 X) z) i* y1 [% q8 f9 I, A
  11.                         rx_len = 0;//清除计数
    $ V; `1 m" E9 L! d! p! O# q8 a  Q
  12.                         recv_end_flag = 0;//清除接收结束标志位
    ! k$ o4 W* _) M3 ]3 [6 X
  13. //                        for(uint8_t i=0;i<rx_len;i++)
    0 X' p6 q- M7 C! J1 e+ U8 |. G: {$ z* f
  14. //                                {9 U/ z! v6 Q" R( B" z0 ^
  15. //                                        rx_buffer<i>=0;//清接收缓存</i>
    & ?' n  h' q2 }0 \, u2 \+ i
  16. //                                }
    * ]7 ^2 }6 o8 N$ F, [6 k
  17.                                 memset(rx_buffer,0,rx_len);* x. W6 \$ a* b# r+ k3 n! S2 V+ ?
  18.   }
    2 o" q7 n% q; u9 F+ W! X
  19.                 HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//重新打开DMA接收
      s7 z! y. k+ }/ P
  20. }9 {6 R2 L# a9 l3 x) ?
复制代码

) p: ]' t5 V4 l5 j/ `: M( q+ Mstm32f1xx_it.c中
. I9 s8 S5 o* S* V+ u& n; H/ J* _$ N% ~; w+ F/ y" z8 b( p3 G
  1. #include "usart.h"0 ^# C( z% P: I# g& N4 g) a

  2. 3 \, y) Q, z7 o, e3 T1 f5 W6 ?
  3. void USART1_IRQHandler(void)9 g  e$ l9 M' k. Y  ]: d4 h6 Z! v9 w
  4. {8 A" p% M6 m- G. e
  5.         uint32_t tmp_flag = 0;- z7 w8 [- i) i6 l; u$ w% E! U6 ^
  6.         uint32_t temp;
    ; B0 k' r: I1 l5 {# y/ q
  7.         tmp_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位
    ) U+ F# Z. O$ _" V  O  D
  8.         if((tmp_flag != RESET))//idle标志被置位8 A% e- `( O$ T* E5 X# {; _
  9.         {
    ; [' X6 V4 v' T9 p
  10.                 __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位( O7 U) \/ ]' m& q
  11.                 //temp = huart1.Instance->SR;  //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
    " _5 N. i, @% ]
  12.                 //temp = huart1.Instance->DR; //读取数据寄存器中的数据6 k+ R# x8 l# f5 p$ \6 f  `
  13.                 //这两句和上面那句等效( q! O& h& p3 v. J9 M- C0 o" N& Q: [+ D
  14.                 HAL_UART_DMAStop(&huart1); //  停止DMA传输,防止" F& y7 b. a: F0 ^
  15.                 temp  =  __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数   
    + G. F. o, e6 d; S
  16.                 //temp  = hdma_usart1_rx.Instance->NDTR;// 读取NDTR寄存器,获取DMA中未传输的数据个数,  k8 u; E* B: W; }; Z9 X1 _' f
  17.                 rx_len =  BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数  j: j5 ~% `1 i" G
  18.                 recv_end_flag = 1;        // 接受完成标志位置1        / ?4 x# Z/ n0 j$ `2 R
  19.          }
    ) a7 p4 t0 {4 L6 c" r
  20.   HAL_UART_IRQHandler(&huart1);9 b  V1 V1 G$ G" X0 L' x* D

  21. . h8 I9 P  [. @0 S8 X; D0 t. T
  22. }
    0 Q0 Q# O9 F; i' g- m4 t
复制代码

3 N+ J0 v5 U- V/ \注释详解:  ~( O4 H+ b' p2 T! X, F
注释1:

' ], P5 ?5 V! T' I' t- i, H, l
  1. temp = UartHandle.Instance->SR;  //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
    & O) O/ G9 l; I+ ~3 M
  2. temp = UartHandle.Instance->DR; //读取数据寄存器中的数据
复制代码

9 Z. }) ^' Y- G# \  w) E. U这两句被屏蔽的原因是它们实现的功能和这下面串口IDLE状态寄存器SR标志位清零的宏定义实现的功能是一样的:8 o& Z4 s* p( t( T
( E- h) {; D) Y1 P1 y  z5 W
__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位  t1 v3 N& N* `$ h1 Q

. P( k$ ^% s: m我们可以点击这个宏定义进去看看,它实现的功能和上面两句是一样的:
# C) @! B8 `$ Q: C. J& k5 @1 _1 I. J* U: o0 P+ }1 B4 a
#define __HAL_UART_CLEAR_IDLEFLAG(HANDLE) __HAL_UART_CLEAR_PEFLAG(HANDLE)
5 T* A% k+ P! W% K4 L. ]& C, M9 V$ @! v
MGNYWWUU8_60()GL0)P@]RJ.png
; y6 \2 X. K9 T% r$ A: i3 y( [4 k
注释2:% u2 a7 U( D- c9 Q
' \7 c( i0 C6 l1 U, K( c
  1. temp  = hdma_usart1_rx.Instance->NDTR;// 获取DMA中未传输的数据个数,NDTR寄存器分析见下面  
复制代码

3 z' D* w/ ~/ e5 [1 N同理, 这句被屏蔽的原因是因为他和上面的__HAL_DMA_GET_COUNTER的作用也是一样的,都可以获取DMA中未传输的数据个数4 [7 J+ d! k/ E0 _& ]7 f2 v
& Q! E# u6 D+ C0 V2 w. n: K. j) m
ZO1V@ZT1H)]~NR@V])59TT1.png
" S8 u9 V0 o: F( @$ M  }) P& G( B2 X$ Q* s: L7 I
测试正常:% M! ]; O: p$ p, `* Y
0 N9 k2 ~2 _4 ~$ c4 H4 v
J92(YI{0KM`ID{%8[LRX9`M.png 7 f7 D! b( \( ]* R/ K! Z3 N% ?  z

% A+ ?+ V( G, ~$ P6 H/ m! M5 \# U此程序可以进行接收不定长的数据帧,不需像RXNE每次接收到一个字节就进一次中断。
  b, ^; C; p1 R0 Q同时开启DMA传输速率也会加快  S  |, e$ q9 D0 v  |
例程2
2 ^+ O# ~' r8 Y; a7 K4 [2 bUART中断使能函数9 ?; }1 M. L! w
  1. __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__)
复制代码
" j8 g) _5 p5 ?; b5 R/ n  y, p
功能: 该函数的作用是使能对应的UART中断
' C! F% c6 K" t4 U
$ T2 Z3 G: j/ B$ F在stm32f1xx_hal_uart.h中被宏定义
6 g( I" c7 P, m+ p! g1 ^" h$ {& u5 {+ P
DWY99KRLDC0B_J1WG{$N`30.png
, [5 T) K5 @: ^. S
, k! J1 Y+ e& X9 E6 R可以使能的中断一共有这些:1 O, M! _/ r" f$ K) \  g

1 f  p) Q: }: Y: m9 J %H4WBTH~8)WZY49JBXC6}[M.png
# Y( d: E8 r9 m. s  ~! ~3 \$ @" B$ U4 ]- m  u. W# Q
我们现在所用到的为:
/ s- N1 p4 J/ K; e
  1.   *  @arg UART_IT_RXNE: Receive Data register not empty interrupt3 W: D2 @$ p. @
  2.   *  @arg UART_IT_IDLE: Idle line detection interrupt
复制代码

2 {2 F( }( b% o% G9 gRXNE接收数据中断
. s& @' h9 q# i7 U) ^" G4 R) c- r; _# \IDLE 接收空闲中断
3 a, r8 |4 o& k. k! @# S7 B+ L3 a" D2 \
U5PXUTDR$S1Z2XH0U5SHRKC.png 7 y. p# i* X1 v3 F
" Z* Z1 S* T7 A' d$ o' e* X
本例程功能:3 s2 g& c: @. x# d2 Z

0 t3 Y" W" t3 q# J1 _; m7 I5 o使用RXNE接收数据中断+IDLE串口接受空闲中断 实现将接收的数据完整发送到上位机的功能
+ ]# N5 [( f! T) w% J# t; H: e4 |5 S" H9 c% v4 |
接收数据的流程:
+ q3 |( z& a: c. Q' f0 Z8 c: y& Y; \8 F# ~6 O9 H' f' b
首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,同样初始化配置的时候设置好配置就可以了。
6 P  v" N9 S4 w% Y* o
+ s9 L! ^1 |- I" x; o  G接收到一个字节数据:7 i2 _3 v4 ?8 W* E+ E: }: M1 G1 S
接收到一个字节的数据之后,便会进入RXNE接收数据中断,
* d; z) A, j/ i( ]4 g2 k0 f) E( d8 w! p" C
接受完一帧数据之后,便会进入IDLE 接收空闲中断% }3 `  o8 w, [+ H$ n7 `8 m
# c, g1 Z6 p( y9 N3 k, B' A# f
在uart.c中添加中断函数
0 D. {9 U' w/ k# j
$ w+ @0 [& q( V4 ]% Y# P% D
  1. void MX_USART1_UART_Init(void)
    ! u: d, X* ?9 |* H
  2. {7 z- H2 h8 E  S( }# R5 l% {
  3. ) j" m8 s( C+ m! ?! s$ P* b* B1 u: Q6 B
  4.   huart1.Instance = USART1;. @( T7 Z- x. o  v( O8 E( w
  5.   huart1.Init.BaudRate = 115200;
      P, \; o* c3 g+ L' L# r% G+ y* E
  6.   huart1.Init.WordLength = UART_WORDLENGTH_8B;
    ) ^7 S; ^% n# c% v5 k) a
  7.   huart1.Init.StopBits = UART_STOPBITS_1;$ T, }: T3 w4 r0 ~; A4 t2 F
  8.   huart1.Init.Parity = UART_PARITY_NONE;
    ) ]. Z' `1 Y- I: z. r7 ~
  9.   huart1.Init.Mode = UART_MODE_TX_RX;* @$ R+ ]3 ]. x1 P4 a
  10.   huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    ( A" C: r3 w6 \5 ?6 @) u
  11.   huart1.Init.OverSampling = UART_OVERSAMPLING_16;
      z: Z2 O6 g4 C
  12.   if (HAL_UART_Init(&huart1) != HAL_OK)
    0 s' L/ i4 c, ~
  13.   {6 L8 }5 e0 W- k8 Q# ]* }3 H
  14.     Error_Handler();
    " M" M( b: J5 I5 [2 D! ]1 l; S
  15.   }
    ( B# ~; ?3 P; J6 W4 d3 h

  16. # n& g" Y6 B( O0 [* Q$ K: l& n/ f6 r3 C
  17.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); //使能IDLE中断# B6 b( T% D! g# [! C2 F1 e1 x  E
  18.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断1 s. {2 Z: v; g. g8 J
  19. ( H5 ^, P. N& k/ F- e
  20. //DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度# x! ^" X5 U; r
  21.         HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);& }, v- m! W! ?7 O# v' T" n
  22.   E; \1 B) b& [7 i0 ~7 {2 j9 [/ B# |
  23.         + u; I3 A, ]* i  l
  24. }( e, l5 n0 X/ b  i6 l
复制代码
其核心代码就是下面的两句
4 W4 q9 Y$ u' i% G
% g& _! P8 `% L; E* q  q8 G 4R[EVR6ESO3C7R2K_B$FN.png 1 Q/ J( @+ f0 D: N6 K. n- |% s5 P

' D; s' {, a6 J, _/ E* @. I5 J2 L第一条语句用来判断是否接收到1个字节,第二条语句用来判断是否接收到1帧数据; T6 J; q9 B* C# m  M- h+ ~
  1. * w8 D8 @. u' D8 ~& X
  2.         if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!= RESET)  //如果接收到了一个字节的数据
    1 J* W4 o1 O! \- q3 i1 B3 Y
  3.         {
    ( u) `  m. q# Y, w( f
  4.                         HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);    //反转LED
    , e, {# F9 X1 O4 u
  5.         }
    4 s+ @) H& I3 _
  6.         
    6 m0 k9 o' m# b1 ]/ s. h6 k# o
  7.         if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!= RESET)//如果接受到了一帧数据  @  v  [( a; l+ Q' Q! p. }9 ^7 }# @
  8.         {
    " }+ f+ d' ~% y& J- P1 `
  9.                 __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
复制代码

, A+ F! h+ l3 Y: S其余部分于上方例程1基本相同: z# Z+ w- r4 S( @5 y) w. f7 B9 J* q7 d

) Z: t) |5 v3 q* d# `本次测试是检测接收到一个字节就反转LED
. M! U  A6 o# v. o# D# W& T+ m. J7 w经测试,历程正常:1 ^+ `! J( Q3 ~

" `) s5 k% b) r" g8 s. }$ s& H: V: ?% V0 O. v8 G: U$ T0 ]6 F1 a9 {
% E# z9 v- x% y$ p+ Z8 ^6 Z( a
+ m8 _) _% `0 @, i; {# c+ d
# z5 [) b& U3 ^$ H* J3 W' G5 Y
收藏 1 评论0 发布时间:2022-3-23 17:00

举报

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