前言6 Q6 l* W1 \* b6 _* E
在串口通信应用中,我们常使用接受和发送中断,相信大家都不陌生。这里有个非常有用的中断可能被大家所忽略,即总线IDLE 中断。当一帧数据传输结束之后,总线会维持高电平状态,此时,就可以触发 MCU 的 IDLE 中断。在本文中,将介绍使用该中断来进行不定长串口数据接收的办法。通过该中断,可以省却很多用于检测数据传输是否完成的判断动作。9 V i% J$ y W- |, U
实验环境' [% z" t0 Y# k5 g; w5 j- e
STM32F411RE-NUCLEO. Z2 }6 d0 }' n% K J
STM32CubeMX5 p* _% E2 l n: Y1 Y; G% a
7 F, @; K4 P; L5 ]
总线状态分析
! s' X. @- d" s4 L下图是发送 0xAA 0x55 的所抓取到的波形。从图中我们可以看到在发送该帧之前和之后,数据线处于 IDLE 的状态。在该帧中,字节与字节之间,没有 IDLE 状态出现,即不会出现 IDLE 误触发的情况。3 [8 E$ l1 \8 X9 R9 X8 ]; r
+ {# @% w9 X/ U) [* `
' P, _+ @1 g' v6 d' E! U9 P: D' Z4 P8 K( j, o! P
不定长数据接收8 u' U8 M+ K% n1 @0 @9 t
本次制作的工程是基于 HAL 库。在原生的 HAL 库中,并没有集成 IDLE 中断的处理。所以,在本文我们介绍的方法中,需要修改一些库文件来实现。
i$ W' C: d: X3 ]/ }$ p5 A: Y
8 x$ _7 ^- ^# _; V8 U+ W使用 STM32CubeMX 生成实验工程
7 c# h) w1 E4 A$ h工程的配置如下图:
J" o' q$ s }" z) Y) m0 k. K1. 系统始终配置为 100MHz
0 |3 @6 H2 ?+ j& T% y& a3 u1 j" b2. 配置 USART2 为 Asynchrones,管脚配置为 PA2,PA3。
% J5 N+ s8 _# h4 }3. USART2 参数:9600Bits/s, 8bits, None,1Stop
8 c u9 M3 U) j* t- _& N- D' \4 S2 }" Y+ B
3 W% Q7 h( R1 y |2 S
/ p+ P$ H& N+ D: ]4 R为了方便打印接收到的相关信息,需要对生成的工程做如下修改来映射 print 函数。
# e3 R+ m2 ?' Z3 t. O: k. Hmain.c-声明
9 z3 p5 B Z% {2 F+ p( k- #ifdef __GNUC__0 d5 C# u; h. w( i) U) e
- /* With GCC, small printf (option LD Linker->Libraries->Small printf
\/ y+ b2 R) E1 M7 o6 ?7 u6 q1 j - set to 'Yes') calls __io_putchar() */( N6 b# Z: g2 M9 m" d' I
- #define PUTCHAR_PROTOTYPE int __io_putchar(int ch): {; s9 M2 F3 i3 H* {' Z. N
- #else
- L T& Q% ?( ~7 F* L - #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
- N A3 b, |6 P3 g - #endif /* __GNUC__ */
复制代码 + X* _4 V9 H. }3 a5 G3 O" k7 N5 Z
$ a7 W: Y/ T! s. y/ ^
main.c-Code' a% \+ ~" o$ t4 c
- /**
' I" N1 `8 i2 A8 K- X! ~% K
- d: G Z6 S- ]) \- * @brief Retargets the C library printf function to the USART.2 Q" a. J ]# t5 ?
- * @param None( P$ X* |% D( `; `1 y5 N1 h
- * @retval None
2 }3 G* q' o% d1 m2 ] - */
5 T; y8 M0 Z4 m7 V - PUTCHAR_PROTOTYPE, l5 ?6 |8 z: P/ x
- {9 v6 x/ \/ |: s' _3 x) O* N
- /* Place your implementation of fputc here */
5 m6 X) {2 _% K8 Q* ]/ s7 I) \" n; k - /* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */5 @: n4 c* q, r- Y/ K& g9 R
- HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);* Y& F% D( s) b5 t
- return ch;9 x2 [# s. P! h+ o0 J5 I
- }
复制代码
; _6 h# W0 u) p$ [% s 修改工程代码
4 x; ?( ^: I. s/ m, }6 R% C 增加接收 Buffer& n" K# f3 ~5 W5 m
main.c6 G# E s/ `3 E$ z9 @4 e I
- //Modification 0% Y7 f; v9 `1 f8 L. G7 m7 H
- //Store the revceived bytes number
: s% J- E6 ^- Q - uint32_t Rev_Size = 0;
, U) [0 n" s5 A# _1 ? - //Receive buffer- S( F" m$ `1 T. \5 `/ Z
- uint8_t UART_RX_Buf[15];
复制代码 ; r/ K! r% J5 ?, Q, p. j
stm32f4xx_hal_uart.c$ X @0 t( f% }8 o5 C% D
- //Modification 04 [3 @5 M: D/ S. [! i% U' {
- extern uint32_t Rev_Size;
复制代码 4 f; Q' i. ]& b) c
在接收函数中使能 IDLE 中断2 t# [2 @4 k- v( \ t; |0 A
stm32f4xx_hal_uart.c -> HAL_UART_IRQHandler ()函数* S7 b& q, C1 ~- p
- //Modification 26 B) \; I3 |* J7 T8 C! y: p
- if(((isrflags & USART_SR_IDLE) != RESET) && ((cr1its & USART_CR1_IDLEIE) != RESET))
1 x. V% V3 O; w$ W4 v- X5 a$ j - {9 w5 g# x2 E9 N8 z M
- //Record the received bytes number
; ]$ g. h7 B9 r' m- e; @ - Rev_Size = huart->RxXferSize - huart->hdmarx->Instance->NDTR;) b- n @, U3 t! y/ }, x5 J
- //clear the IDLE flag1 Z) {. x2 U/ `0 }
- __HAL_UART_CLEAR_IDLEFLAG(huart);
; H" ?. j x0 I. B/ q4 `; Q7 P - //Abord the received process
2 u6 x$ b; c8 U, @, A4 [. b - HAL_UART_AbortReceive_IT(huart);/ |/ Z( q% r- c) x- K, L
- return;
3 u1 l& D# ?% l' X* D( g - }
复制代码
3 p- \$ I/ x; B" T
( g% [2 E1 N: i8 F接收完成处理(IDLE 产生,一帧数据传输完成)
6 j0 ^5 `' h$ d& Y; istm32f4xx_hal_uart.c -> HAL_UART_AbortReceive_IT ()函数. W2 m! l/ ^3 q% \6 q& W* k
- <div>//Modification 3</div><div>CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE| USART_CR1_IDLEIE));</div><div>// CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));</div>
复制代码 * T; U6 N a7 @' b$ F9 y8 f% D; u
7 s" B: P& b7 x! a
main.c2 v5 l. f5 o: _
- <div><div>//Modification 4</div><div>void HAL_UART_AbortReceiveCpltCallback (UART_HandleTypeDef *huart)</div><div>{</div><div> //Print received Bytes</div><div>printf("\n\r[IDLE]Received %d Bytes:",Rev_Size);</div></div><div><div>for(uint16_t i = 0; i < Rev_Size; i++)</div><div>{</div><div>printf(" 0x%02X", UART_RX_Buf[i]);</div><div>}</div><div>//Re-start receiving</div><div>HAL_UART_Receive_DMA(&huart2, UART_RX_Buf, 15);</div><div> /* NOTE : This function should not be modified, when the callback is needed,</div><div> the HAL_UART_AbortTransmitCpltCallback can be implemented in the user file.</div><div> */</div><div>}</div></div>
复制代码
- A$ z2 ~) U/ \& a7 @
7 f: x B$ s0 c5 b, ^
) q; ~4 [2 [" m( m$ v使能接收
* S* s, a5 }, }) F5 e/ _& ]7 umain.c
* K+ y- ^% u1 a- <div>//Modification 5. ‘15’ is the total number to be received. Make sure it can cover the</div><div>longest frame.</div><div> HAL_UART_Receive_DMA(&huart2, UART_RX_Buf, 15);</div>
复制代码
1 I- |/ Y t5 _- e g# c% D4 p7 S& v( R
实验结果
1 m: g$ h0 L, E7 j: j使用串口调试串口,通过 STLINK 的虚拟串口发送数据,MCU 会返回接收多少个字节的数据,并将接收到的数据打印出来。7 }2 S0 ~9 j; W2 T0 H h( A
下图是发送 0xAA 0x55 的实验结果。/ u- T) x, w3 K
. m i/ J1 P8 z
8 ]6 H# Z1 V" l, f# S5 p
' ?* R( o4 g: e8 T/ ^4 x; x: T" _2 F; ?. m1 _; ~
小结
: `3 d( a( g8 G) X, _' CIDLE 作为指示总线空闲状态的中断,合理的使用能够很大程度的节省代码程序设计工作量。这里只是做个简单示范,抛砖引
1 B g: h8 G- e& h1 D, P* D玉,实际应用中希望大家可以灵活使用满足所需。
3 {% A/ g$ c" A' }3 o& |! l7 c+ D6 U
6 s: E, I. e$ E3 B0 p$ P3 p, s5 |! f
1 G) j( n. _6 x! {0 q( q2 y9 c |