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

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

[复制链接]
STMCU小助手 发布时间:2022-3-23 17:00
本篇文章提供两种方法:/ _: h- _* X% ?7 J# f4 T- ]6 T
一种是 :IDLE 接收空闲中断+DMA
; L# m* _5 p& `3 N$ Z. N( M. h一种是: IDLE 接收空闲中断+RXNE接收数据中断
, Q, U! b! ~% z1 b; y) m
! |7 b1 t5 r0 Z+ a都可完成串口数据的收发
0 a9 d/ g4 _6 e+ ?/ k- \% \
; ?1 u9 N8 G1 z3 e知识点介绍:
8 u! k1 \/ Y9 D0 T- T; ESTM32 IDLE 接收空闲中断
9 @+ ~' Z4 t% n. `6 u0 E, I
功能:
4 D$ }( R2 C' ]在使用串口接受字符串时,可以使用空闲中断(IDLEIE置1,即可使能空闲中断),这样在接收完一个字符串,进入空闲状态时(IDLE置1)便会激发一个空闲中断。在中断处理函数,我们可以解析这个字符串。: F( B# p0 Z9 H' \2 N, P% T

4 H7 R9 U3 J! H& f. }4 @, M接受完一帧数据,触发中断
1 V/ [2 z; y  ~" S2 K1 s5 m( {0 x
# R" i. u7 n3 nSTM32的IDLE的中断产生条件:$ _+ t: L1 c) U; b
在串口无数据接收的情况下,不会产生,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到数据,即产生IDLE中断* v1 E0 |8 z' B% {( @+ x, M1 M

& m  w- Y: X( m3 c! N% k- l7 bSTM32 RXNE接收数据中断
* t$ C8 i$ H  J* X/ {* P功能:
9 O5 e7 M# ?' m  E) U当串口接收到一个bit的数据时,(读取到一个停止位) 便会触发 RXNE接收数据中断! h0 U0 p$ F* {  i8 ], J) K5 {! e

# b" [! q1 v$ ~! L5 W) ~接受到一个字节的数据,触发中断# A, Z( g4 s9 n- ^, L( P, E& |

5 c& y. ]) j4 t4 `  h& @3 p# ^比如给上位机给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。
8 r0 ]) O% [5 [! u& t6 N( O) _6 n: n) J+ y" j3 F/ p' q7 w  e
串口CR1寄存器- v6 Y" F1 B  Z" _$ r

* t# r& c2 [8 Z* V( e) Y1 G) d KPGP$%K4Z`P]5~NPWSBD91K.png
& w- W( a0 b- A1 J6 l# Q: u  D* q
5 E+ \3 H8 _' ^. a对bit4写1开启IDLE接受空闲中断5 X, N( _/ ~! @( P% S  \: {, @
,对bit5写1开启RXNE接收数据中断。; S1 \2 P0 m) I1 O$ B) z

& v$ S; I+ P- a! I+ D4 M( M+ k0 A串口ISR寄存器
- [" N  z* \/ c# H4 a, B: r4 W3 P9 h
L%TE0SWP0~FE)I7T4TOEER9.png
: O4 B7 I2 M3 E8 U+ p4 }* p7 j$ I0 R" a! w1 r% c
此寄存器为串口状态查询寄存器- C" p, w' `5 J3 M& J! S

) J6 W/ c  I7 A: K$ m5 C- d7 d" N' i) }当串口接收到数据时,bit5 RXNE就会自动变成1,当接收完一帧数据后,bit4就会变成1.: _( @& }, U! N- P' p% [3 x4 c

+ ?+ @4 G* ^9 w" S1 C* u/ U" [清除RXNE中断标志位的方法为:+ }' J; q( L3 C) Z! T; v9 Q' `. D
# S! B: D& p  `4 S
只要把接收到的一个字节读出来,就会清除这个中断9 w9 @1 K5 S1 p0 y
4 W3 d# L) Y+ E5 V
在STM32F1 /STM32F4 系列中 清除IDLE中断标志位的方法为:8 M4 @+ B' r+ @, C) B2 ]
) A! f9 d6 }1 O; N0 w
1.先读SR寄存器,/ n. ]5 \# A  I$ A" k! }5 p
2.再读DR寄存器。
% ?, i' J/ o; I' t0 o3 t
' G" K# h4 J/ X! \
3F]Q2F56KOVOR(9K8AF{TS2.png , a. ^) j: z. `- I8 B& X

0 F9 B4 k7 z& J6 gmemset()函数
+ Y' b3 W& c- x3 Q; u
( S( `) v' k: P- z4 ~3 E  s
  1. extern void *memset(void *buffer, int c, int count)        
复制代码

5 Q& r% v' Y; W0 v: _1 u7 Kbuffer:为指针或是数组7 x; F8 R& }6 u; G$ I1 i
c:是赋给buffer的值; B2 E+ C* A: I: W
count:是buffer的长度.( ~5 m+ I/ Y. M9 M9 I
USART采用DMA接收时,如何读取当前接收字节数?- Z, i$ a" Y, O1 `7 \
  1.    #define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR);
复制代码

& Y/ e) q  e4 R1 d) ]DMA接收时该宏将返回当前接收空间剩余字节8 a: |& r3 f$ r1 h: Z# Q

# u. S' L# X, e* s' H实际接受的字节= 预先定义的接收总字节 - __HAL_DMA_GET_COUNTER()! T, Z0 W. `3 y' t4 m

/ w2 P( y. |' D% T- @1 U其本质就是读取NTDR寄存器,DMA通道结构体中定义了NDTR寄存器,读取该寄存器即可得到未传输的数据数呢,
% x1 E# p- I5 q
% a4 q* \6 P; }& U6 D; ^& TNTDR寄存器
( X3 m9 N$ W9 [' v2 ~( U% \$ {3 x
/ S% s9 e/ p' y5 i) r! { _[`8QZUY((Y87)B8BSJ36}B.png ( ]9 `3 Z5 B. c/ y% f* b" L( M

+ c2 R- a$ [+ @0 ^实现方法:
5 i& }* Z6 ]* u" Z; _  D) E. x两种利用串口IDLE空闲中断的方式接收一帧数据,方法如下:
2 L+ |+ @) p7 [1 z* i  h, n* Y) n- l: i" p) ~
方法1:实现思路:采用STM32F103的串口1,并配置成空闲中断IDLE模式且使能DMA接收,并同时设置接收缓冲区和初始化DMA。那么初始化完成之后,当外部给单片机发送数据的时候,假设这次接受的数据长度是200个字节,那么在单片机接收到一个字节的时候并不会产生串口中断,而是DMA在后台把数据默默地搬运到你指定的缓冲区数组里面。当整帧数据发送完毕之后串口才会产生一次中断,此时可以利用__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);函数计算出当前DMA接收存储空间剩余字节6 V- O3 L8 @1 ?6 v, w( Q7 f. ?# ?

; @, D, S4 C% s+ b4 p# X  I7 ?4 M本次的数据接受长度=预先定义的接收总字节-接收存储空间剩余字节
7 ~/ b, y, u( R7 A) g/ y  I* b" e! x& g% g5 I
比方说 本次串口接受200个字节,
/ }! ^6 s* a" A$ ]6 G$ r. p
  1. HAL_UART_Receive_DMA(&huart1,rx_buffer,200);//打开DMA接收
复制代码

& D1 F; C! O. \然后我发送了 Zxiaoxuan 9个字节的数据长度
# Y/ z# ]6 e" a1 w& L! U8 _
* S$ _' v' y( t那么此时 GET_COUNTER函数读出来 接收存储空间剩余字节 就是191个字节! K' l6 s  e! o% h' T3 g

+ N& c* Q  ~1 J6 |7 F  j* v; P实际接受的字节(9) = 预先定义的接收总字节(200) - __HAL_DMA_GET_COUNTER()(191)' t1 t' a% u7 X0 c" l
9 = 200-1914 f) [( p" n8 e( Y& x, L6 f) F
& k# c9 z2 U2 W& O0 n/ y
应用对象:适用于各种串口相关的通信协议,如:MODBUS,PPI ;还有类似于GPS数据接收解析,串口WIFI的数据接收等,都是很好的应用对象。+ c5 Y! M5 U/ ?5 y" `  a0 s2 Y

+ z  ^: X4 t+ A( I, i方法2:实现思路:直接利用stm32的RXNE和IDLE中断进行接收不定字节数据。 每次接收到一个字节的数据,触发RXNE中断 将该字节数据存放到数组里,传输完成之后,触发一次IDLE中断,对已经获取到的数据进行处理
5 w  R( A% C' K9 O$ W6 \1 O, A! F" H5 }; n/ T% x3 T% M3 S2 K2 q
例程1  U3 m- V0 z6 x$ k! c0 d" [
本例程功能:
! B9 i, X) Z9 ]

. U6 [. \" W  d" p# e/ p使用DMA+串口接受空闲中断 实现将接收的数据完整发送到上位机的功能
2 Z% d# p% F8 l, U& a* f) y1 N: N) f: l

" E7 ~% q  x* I( K
7 u" f% ]! S7 k接收数据的流程:. H/ Q7 Q6 P2 ~( x. I

# O  A! K- V$ q. w8 G首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,程序此时也不会进入接收中断,在软件上无需做任何事情,要在初始化配置的时候设置好配置就可以了。
( s4 R* t. v4 V; G- [' r
* F* T" a, U) L# x8 f数据接收完成的流程:7 x- @. `8 i" s* P( B4 x3 ^
/ `$ Q" m) g1 `9 `; `; A2 m# R, H
当数据接收完成之后产生接收空闲中断④$ W  e' _) j( n( Q! _1 I

# S/ U0 ]+ E$ b; F/ h在中断服务函数中做这几件事:
/ ^! w* P  h; x1 z: c* ?; x判断是否为IDLE接受空闲中断
: ^2 K1 m  b0 ]2 O( L在中断服务函数中将接收完成标志位置1
% ^, W: ~6 n3 T) S+ C+ t' C1 m. @关闭DMA防止在处理数据时候接收数据,产生干扰。+ t& i0 X$ t/ Y  J
计算出接收缓存中的数据长度,清除中断位,8 S8 ]: Y$ l2 g" c9 [

- q& L# ~- k5 |6 {0 k& h% f6 qwhile循环 主程序流程:  S' c! C8 c3 E. s
主程序中检测到接收完成标志被置1
$ n& o+ h9 s1 W3 }* j; Y5 T进入数据处理程序,现将接收完成标志位置0,, X" ]8 P$ S8 Z6 K  x" [4 M; _
将接收到的数据重新发送到上位机
" {/ p  W6 W! I  {$ t+ T重新设置DMA下次要接收的数据字节数,使能DMA进入接收数据状态。, o, v2 k9 W- @8 C
( B, p% |  a" M) l3 x
例程代码:
' u2 V% ?. T2 a9 D) I7 Uuart.c
+ V8 ]% k/ H& U8 T, K% v
  1. volatile uint8_t rx_len = 0;  //接收一帧数据的长度
    0 V" U! \7 ?% K1 x3 V# u
  2. volatile uint8_t recv_end_flag = 0; //一帧数据接收完成标志6 j! K* Y1 D# j. z/ w% N! Z
  3. uint8_t rx_buffer[100]={0};  //接收数据缓存数组
复制代码
  1. void MX_USART1_UART_Init(void)# D* Q1 O$ v$ s/ F+ I% y
  2. {
    & x8 s! {# w9 ~

  3. ! N  I' k  ^4 J
  4.   huart1.Instance = USART1;
    ) W4 c/ q! L. y% l% }
  5.   huart1.Init.BaudRate = 115200;
    % o6 d  R. j& x1 r
  6.   huart1.Init.WordLength = UART_WORDLENGTH_8B;
    2 J7 g* c* p3 A2 G
  7.   huart1.Init.StopBits = UART_STOPBITS_1;
    & `" F; L6 k% x
  8.   huart1.Init.Parity = UART_PARITY_NONE;3 J% `% c8 Z9 V# C0 C
  9.   huart1.Init.Mode = UART_MODE_TX_RX;& ?0 p! S0 C7 C* B
  10.   huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;; |8 w) n3 k5 b$ V" B# G7 Y
  11.   huart1.Init.OverSampling = UART_OVERSAMPLING_16;
    4 {! n9 S. X; X3 [4 f. m$ ^  u
  12.   if (HAL_UART_Init(&huart1) != HAL_OK)
    $ v7 \8 t0 V* F) M8 Z
  13.   {
    8 u: l" P; ^- A) N+ s8 y( v
  14.     Error_Handler();
    ; H8 Q+ b/ m2 M" _0 Q6 i
  15.   }3 M6 ?# |5 M1 h& ^4 d6 U
  16. //下方为自己添加的代码1 P& s% ^6 V8 u  {
  17.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断4 }7 u( n  n# F# z  i9 B- ]& K

  18. ) W3 N8 s8 P$ z4 ^/ W; s: R4 o
  19. //DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度
    ' {9 K' }5 N0 p; I
  20.         HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);
    + _  _% y+ ]6 c6 z( t! i2 X
  21. ; i+ k. J  S, ?3 P( i& d! a
  22.         - m; x1 {7 w9 {1 r' Y/ S2 Y
  23. }
复制代码
. {' i& k* B! d; Y
uart.h- E$ i0 G5 Z" Z) L0 C: B" a
  1. extern UART_HandleTypeDef huart1;
    ( k# W! M/ L. ?6 G9 Z- Q
  2. extern DMA_HandleTypeDef hdma_usart1_rx;
    & r% v7 Y( @# M) l4 q
  3. extern DMA_HandleTypeDef hdma_usart1_tx;$ |+ J3 N2 V; F* `
  4. /* USER CODE BEGIN Private defines */& H3 {! B- d  ^# G% t8 h: z+ e
  5. 7 }: f+ ?% S/ p( Y

  6. 1 }/ f; @; {9 a& q- X9 b
  7. #define BUFFER_SIZE  100  . ^9 G. p0 f: L3 z+ i
  8. extern  volatile uint8_t rx_len ;  //接收一帧数据的长度
    " T$ L! s+ N  u0 v1 y  K% s; _' ~
  9. extern volatile uint8_t recv_end_flag; //一帧数据接收完成标志
    * V5 p0 i; R+ R; f7 A. l# n3 ]
  10. extern uint8_t rx_buffer[100];  //接收数据缓存数组
复制代码
+ R3 S1 i; j9 q! D5 h1 J" ^, Y- j
main.c7 n8 O! h' I: J2 J9 A
: I7 S- ]) L4 ]: m
  1. /*
    ) n' }- b9 ~5 v* c9 V3 l: X
  2. *********************************************************************************************************/ w% b5 H2 U! y% |8 c# K
  3. * 函 数 名: DMA_Usart_Send
    : l$ s, @6 o( e8 e, g; t  v" W
  4. * 功能说明: 串口发送功能函数
    $ P# k- q  b$ R* ?/ p
  5. * 形  参: buf,len8 Q: H0 h* x" ^6 _
  6. * 返 回 值: 无6 f% ?/ A( \* k  Y% I3 T
  7. *********************************************************************************************************4 P- G7 l! U2 x- D) P! Y
  8. */
    4 ^& [' o, ?* n% L0 v( X
  9. void DMA_Usart_Send(uint8_t *buf,uint8_t len)//串口发送封装" T3 t* N* j; P  `7 I& h# y
  10. {
    ! F$ o( Q/ |) S9 {7 D' A0 P
  11. if(HAL_UART_Transmit_DMA(&huart1, buf,len)!= HAL_OK)   //判断是否发送正常,如果出现异常则进入异常中断函数& q0 P( `8 u7 w9 V" @
  12.   {
    % j7 V( y5 j+ l/ S$ [( w) J
  13.    Error_Handler();7 \$ o1 i) E7 ^; T6 t6 I  n/ ~: c
  14.   }* w2 m6 Z+ {% x# l/ Y0 T* s  ]

  15. $ ]. D+ ]* I2 d. C+ x7 H$ n0 S
  16. }
    # e! ^- A; R3 B5 |, C8 h0 M8 I) g

  17. , J8 z" q/ r9 ~. d1 y

  18. # F# |- w& Z3 `, w- l
  19. & u% V' c0 {& j0 T
  20. /*
    # Q; b- i+ o, I& ?. _( [+ a
  21. *********************************************************************************************************
    - u5 y, J( G, j* x
  22. * 函 数 名: DMA_Usart1_Read2 f! z/ `3 Z, ~9 U  V' Q
  23. * 功能说明: 串口接收功能函数
    8 G' K9 u# \$ h% |5 j8 ^
  24. * 形  参: Data,len" D+ E5 m$ q- F4 Z
  25. * 返 回 值: 无
    * |4 r. x0 v8 R3 k& x
  26. *********************************************************************************************************
    0 O( l( W& S4 \% Q( N. L
  27. */
    # M* L- G, U: V2 T6 ^
  28. void DMA_Usart1_Read(uint8_t *Data,uint8_t len)//串口接收封装4 `+ Y/ \& _* \
  29. {
    $ ^9 h; j: V  K; A$ v5 [
  30.         HAL_UART_Receive_DMA(&huart1,Data,len);//重新打开DMA接收
    # S* H/ l' k3 o/ o' g! \
  31. }1 [. I* s8 p- \( I4 S
复制代码
" O, Z% D* {2 _6 F. a0 O+ u3 m
while循环- A; P! W/ w% C: l9 _9 p

1 e/ J% _; u0 H- E1 h
  1. while (1)
    " q" `. W: F) w( R
  2.   {
    % |' O' _! e: O, o* Q$ h
  3.     /* USER CODE END WHILE */
    . W% x. x( i* X5 ?

  4. ' y4 o# e0 {8 z/ v% G
  5.     /* USER CODE BEGIN 3 */
    7 @' o2 }! f' b9 J: ]
  6.                  if(recv_end_flag == 1)  //接收完成标志$ q& O# I) T5 H* Q9 r  S9 b* V
  7.                 {. E) j7 Z) W5 {; q+ c
  8.                         
    / m5 |1 k/ H( D- Y- q
  9.                         
    4 x/ c) X& ?+ L9 d$ k8 @
  10.                         DMA_Usart_Send(rx_buffer, rx_len);% c6 }4 k  A3 H
  11.                         rx_len = 0;//清除计数4 l; w7 k, O4 V$ i) @, B6 n
  12.                         recv_end_flag = 0;//清除接收结束标志位
    2 f; m. ^, @0 Z# G3 ~
  13. //                        for(uint8_t i=0;i<rx_len;i++)
    % c- D" V$ i" }. J- H. x/ `
  14. //                                {
    % l6 q& m8 ^. B: W' T+ J
  15. //                                        rx_buffer<i>=0;//清接收缓存</i>
    6 U! e4 _- q" m+ i
  16. //                                }! s7 j. X$ G; l: p; H6 N& y% {
  17.                                 memset(rx_buffer,0,rx_len);
    ) N; `% A) R3 d1 z' x4 I! A
  18.   }
    & D7 m5 x5 W+ l5 m
  19.                 HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//重新打开DMA接收+ z* u$ e7 e6 E, K2 T
  20. }
    2 a4 @& u, z9 j) ~9 W9 o. x) Y, M
复制代码
. e3 a( y1 p: ^" }+ [* t
stm32f1xx_it.c中  S, u) ]$ M, b3 S1 E' f" S0 |
1 B: g" ~0 w- n4 E: A
  1. #include "usart.h"& \$ z% f" c, F! ]  |6 }

  2. ' f7 |& S* F4 ~9 o
  3. void USART1_IRQHandler(void)
    1 s# C/ w- k& D+ J
  4. {) c1 S9 D; T; D6 I$ @; H/ f
  5.         uint32_t tmp_flag = 0;5 T1 t5 {7 ~" |$ \" ?. C$ ]; I. D
  6.         uint32_t temp;
    4 ?& t8 j) }4 w- L, z& o3 @+ s+ J
  7.         tmp_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位# C7 j7 Q* `) ^7 q$ v+ q+ w: A  X
  8.         if((tmp_flag != RESET))//idle标志被置位, O( e, J, I- t) x5 _
  9.         {
    & A+ x) e+ f2 T) u/ r! s
  10.                 __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
    0 a9 Y6 x% D4 ?+ Z" S
  11.                 //temp = huart1.Instance->SR;  //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
    + T! `' ?/ J2 F# O7 y5 H/ B
  12.                 //temp = huart1.Instance->DR; //读取数据寄存器中的数据# `8 E) V; y2 [9 q+ k0 k
  13.                 //这两句和上面那句等效4 N6 Y' x" m0 [& M$ T6 H0 p
  14.                 HAL_UART_DMAStop(&huart1); //  停止DMA传输,防止
    3 d3 [, w4 i# D  y) V; a- L6 {/ L) b
  15.                 temp  =  __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数   / f( _3 N. N% ~5 X7 t4 R3 A
  16.                 //temp  = hdma_usart1_rx.Instance->NDTR;// 读取NDTR寄存器,获取DMA中未传输的数据个数,
      M9 q( M! D: E8 z  n5 T( k/ ?' E
  17.                 rx_len =  BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
    1 t: z' `: e0 Y$ T: h6 J+ T
  18.                 recv_end_flag = 1;        // 接受完成标志位置1        
    ; k- i. z, R. `" P5 q) V
  19.          }
    0 n) x" T8 B% `/ o% W6 ?
  20.   HAL_UART_IRQHandler(&huart1);
    : O% `- N8 S) ]) `: p

  21. * V, g$ M+ `8 R' g: q! ?# M0 y
  22. }
    ; c2 O7 X, J) ]& q2 {. F- j; E
复制代码
7 Y( h. t0 X& T) B
注释详解:
" n& K$ d$ O. G  v注释1:
/ [% B4 U) H# a) G2 R
  1. temp = UartHandle.Instance->SR;  //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能' ]6 a9 ~7 ?0 }. s0 ?$ m, L# b
  2. temp = UartHandle.Instance->DR; //读取数据寄存器中的数据
复制代码
) S+ {( R+ k& U' H) I; C
这两句被屏蔽的原因是它们实现的功能和这下面串口IDLE状态寄存器SR标志位清零的宏定义实现的功能是一样的:
: d: Q2 d4 T* i% O+ ~# F9 o/ h" j: g$ F5 T' k
__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
/ e3 [- M( G3 ~- _0 d) A+ U4 D% H) d2 ?# y& s# r
我们可以点击这个宏定义进去看看,它实现的功能和上面两句是一样的:
& q* A4 q3 U# m( l( o6 p6 p( @: z) x
#define __HAL_UART_CLEAR_IDLEFLAG(HANDLE) __HAL_UART_CLEAR_PEFLAG(HANDLE)- i  l+ Q7 _, H! v. Q/ g9 s$ j' t/ L6 L$ A
8 {% c* E, A8 U/ q- }) B, H4 V1 C. f+ g
MGNYWWUU8_60()GL0)P@]RJ.png
9 N9 P' W" c. w% B# ^. X3 ?4 t
6 }1 y9 B) N- q8 I& g4 s* e+ n注释2:8 `! K' A( j% e$ l. f! i7 U8 `/ M

# U; n0 B, Q6 Q' Y+ ^3 M2 r
  1. temp  = hdma_usart1_rx.Instance->NDTR;// 获取DMA中未传输的数据个数,NDTR寄存器分析见下面  
复制代码

- j  K  M/ i8 g5 f3 u3 H同理, 这句被屏蔽的原因是因为他和上面的__HAL_DMA_GET_COUNTER的作用也是一样的,都可以获取DMA中未传输的数据个数
+ N9 e6 I0 I* a  V
8 Q' U  H* h0 k/ l; M( j ZO1V@ZT1H)]~NR@V])59TT1.png / h: M, D7 e1 I* H

4 H. s. l0 ]+ Y8 G4 Z测试正常:
& ^; A: e" w% }8 U/ R
$ ^! M) ]- q, h* h1 W: l2 ~- c J92(YI{0KM`ID{%8[LRX9`M.png / H: r% }8 g' W* x$ g( X

4 ?( }% M1 _/ L! s: M9 Y# ?此程序可以进行接收不定长的数据帧,不需像RXNE每次接收到一个字节就进一次中断。
4 S$ [5 {' r5 T9 _; H2 k; _同时开启DMA传输速率也会加快1 I. p' Z- A! |
例程2* T0 U" `+ a) f+ ?# T' @
UART中断使能函数% I" ?* w/ B  z) M5 L3 c7 T
  1. __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__)
复制代码
6 T1 w7 ~% i% N) G
功能: 该函数的作用是使能对应的UART中断
7 _7 t# P+ Z8 K+ I
8 E7 H3 d1 r9 w0 Q在stm32f1xx_hal_uart.h中被宏定义4 g' f0 {3 v1 ^# a, Z
) R, Z9 G) @/ E5 |2 @6 i
DWY99KRLDC0B_J1WG{$N`30.png
" e) Z. C& |" r3 r, j7 k  o1 M& O' O3 o- h. W
可以使能的中断一共有这些:
! }$ @8 n: I& W. C4 a- a# j
. K; L# o: ~. g% |" A3 @5 L8 q! i %H4WBTH~8)WZY49JBXC6}[M.png
9 O" B# ?. j' k6 {. W4 r6 B3 U
  q- Z$ t* z. m. O  l0 O* ?& E! x我们现在所用到的为:
: n0 I" w5 O. t$ c6 y
  1.   *  @arg UART_IT_RXNE: Receive Data register not empty interrupt2 D, t" h$ a) _  S. b8 ?
  2.   *  @arg UART_IT_IDLE: Idle line detection interrupt
复制代码
0 z* x$ e/ `8 c/ D
RXNE接收数据中断$ L4 x$ `$ j+ {1 ?* b% o4 e
IDLE 接收空闲中断6 I3 n4 c0 M% M! Q2 C( L
+ E9 ?$ S! [2 G" ~
U5PXUTDR$S1Z2XH0U5SHRKC.png . V! r0 t8 O2 Y% e: M
6 _9 D0 K4 I. u' P0 V1 |0 \% ]
本例程功能:. s/ c! c5 l/ U" u5 v: T* z

0 l) I" f+ B/ C, o使用RXNE接收数据中断+IDLE串口接受空闲中断 实现将接收的数据完整发送到上位机的功能3 s  [# C* e$ B6 p, Z

% v: Q1 N, y5 d接收数据的流程:
% f+ V1 i/ d* W+ @; A) X6 K' g( _! j% N! m8 Z  I7 H+ g
首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,同样初始化配置的时候设置好配置就可以了。
/ a8 F3 N7 O9 N( l* G
6 w2 I9 B0 M9 P. k接收到一个字节数据:& \8 ~8 Y% y% ~% K
接收到一个字节的数据之后,便会进入RXNE接收数据中断,  n+ G- @/ e+ \0 o9 m

/ b& o+ l# U& @接受完一帧数据之后,便会进入IDLE 接收空闲中断
+ s0 z$ B  O0 ?) s4 z4 v/ x  D7 q) {! R5 G3 }" }4 D& U! S
在uart.c中添加中断函数
7 X4 P% Z! ^/ F  R* m" {" I' M7 M9 L2 D1 N7 A3 _. A. c% [
  1. void MX_USART1_UART_Init(void). l# i" g. w1 i& Y% ?
  2. {
    , N1 `' J6 @+ |% @; ?5 M( h

  3. + e. m/ Q. P3 F% l8 T0 Y
  4.   huart1.Instance = USART1;
    / G* d3 k  u8 l8 s- ]: @2 D2 g- k# |
  5.   huart1.Init.BaudRate = 115200;3 d0 L" D. K9 l* L, T+ a; y( q9 j; a
  6.   huart1.Init.WordLength = UART_WORDLENGTH_8B;
    ; A4 M$ M* U8 v0 X' q% x
  7.   huart1.Init.StopBits = UART_STOPBITS_1;6 b5 ?9 `% c1 d5 N/ _
  8.   huart1.Init.Parity = UART_PARITY_NONE;
    - s* {7 o+ q- h! h0 j" h
  9.   huart1.Init.Mode = UART_MODE_TX_RX;
    7 j" M5 o5 g+ ^1 k/ E' W. ^
  10.   huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;6 J3 `% }* ^( v
  11.   huart1.Init.OverSampling = UART_OVERSAMPLING_16;/ l% u4 R0 n* `0 y2 q! R
  12.   if (HAL_UART_Init(&huart1) != HAL_OK)
    " K. H( z! a, v5 F, L6 Q
  13.   {( _7 k5 C: V- H( {7 z" ]4 P
  14.     Error_Handler();
    + I8 ]" @" }: ^' U6 {& y( P) S7 v
  15.   }4 a4 g- h8 ?3 O! v

  16. 2 l) X% y% O2 ~) N
  17.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); //使能IDLE中断
    4 m, U1 m9 G1 c' M0 H) W9 f3 N0 S
  18.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断
    2 D& Y, t# s, F# N

  19.   A2 E3 v) p# D/ o
  20. //DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度3 }2 G- o6 w1 E
  21.         HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);
    " ]$ s' k- |( P. D8 [$ c( S* D* @- _
  22. 3 r: q/ w3 y# g* X
  23.         8 o/ f& `6 j' i# \
  24. }
    6 ^& H7 w7 R$ F  s1 l3 l
复制代码
其核心代码就是下面的两句
- a7 W! H0 g$ u, }9 }
, `4 a8 S+ c2 x 4R[EVR6ESO3C7R2K_B$FN.png # e6 G( \7 \5 g* l3 d# c) l
5 j$ T  t5 b; r' }
第一条语句用来判断是否接收到1个字节,第二条语句用来判断是否接收到1帧数据
: r. {9 l$ v+ _, [3 y. P

  1. 9 u3 g; E  @1 ?1 V
  2.         if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!= RESET)  //如果接收到了一个字节的数据0 W! S8 h& [& w2 l( a
  3.         {
    ' \* D3 ~* p1 l+ {5 u  p: E6 O( J* P
  4.                         HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);    //反转LED# @% _2 E, L! [+ A; i7 U' q/ U
  5.         }& Q2 ^8 U8 C- e; T, C- k7 s
  6.         ( S  k6 `. g& T6 G
  7.         if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!= RESET)//如果接受到了一帧数据5 @- y& h1 ~- G
  8.         {
    / B* V, G8 _7 M8 M. F- Z
  9.                 __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
复制代码

9 h: v2 Q" @) f; s! y其余部分于上方例程1基本相同
: E8 w7 p% m2 s) B9 q
; |# m7 \) W- ?8 k1 D本次测试是检测接收到一个字节就反转LED  ^0 D! N! x6 }8 h$ r4 `
经测试,历程正常:
9 b) }6 c* O8 B# s) o3 [9 D" w# r0 a1 A/ j6 g

& S4 I& T3 H1 l9 u1 M8 ^5 Z3 k+ W( f2 ]0 z- a* I" Q) d6 n" U% @2 G3 P

" |3 q  Z) _6 Q5 M. z3 F+ M8 {4 F5 m; h6 n, f
收藏 1 评论0 发布时间:2022-3-23 17:00

举报

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