前言
2 k+ P0 i" _& p- ?" B5 d在串口通信应用中,我们常使用接受和发送中断,相信大家都不陌生。这里有个非常有用的中断可能被大家所忽略,即总线IDLE 中断。当一帧数据传输结束之后,总线会维持高电平状态,此时,就可以触发 MCU 的 IDLE 中断。在本文中,将介绍使用该中断来进行不定长串口数据接收的办法。通过该中断,可以省却很多用于检测数据传输是否完成的判断动作。: X$ M, j- z9 p8 c! r) v. X$ u
实验环境, z) w' L& W$ X: {/ g
STM32F411RE-NUCLEO7 j* v$ S {' e$ V2 r, H) K# ~9 I
STM32CubeMX( ?8 j+ |9 r U2 @1 r
% W5 k% T! r8 n, D i* H% U! |+ A
总线状态分析8 i3 K; E8 k; P; h
下图是发送 0xAA 0x55 的所抓取到的波形。从图中我们可以看到在发送该帧之前和之后,数据线处于 IDLE 的状态。在该帧中,字节与字节之间,没有 IDLE 状态出现,即不会出现 IDLE 误触发的情况。
* P* K$ r( S# }7 F/ a4 ], `8 f( |+ h$ ?& K3 l3 T, D4 B8 b
1 A& q3 [ _+ E, w# x" t* h( z
) S N Z2 H$ D! W
不定长数据接收
4 v' X( m, X0 A( a8 k本次制作的工程是基于 HAL 库。在原生的 HAL 库中,并没有集成 IDLE 中断的处理。所以,在本文我们介绍的方法中,需要修改一些库文件来实现。. V" l6 i) }6 X# f
: w- |4 c+ U4 S# K6 t' m( m使用 STM32CubeMX 生成实验工程
6 Y' e5 Q9 b; E1 R5 C$ Y+ |% h工程的配置如下图:7 z P# ]( Q& S2 @0 s( W
1. 系统始终配置为 100MHz
2 H! V1 x+ H' C2. 配置 USART2 为 Asynchrones,管脚配置为 PA2,PA3。7 A* j! p4 L. G9 N2 q9 N0 f
3. USART2 参数:9600Bits/s, 8bits, None,1Stop7 I6 R1 r! O0 T* B v
) x1 Y. q; b) k2 A
* C! F+ U8 [$ `. r* l% [
( g% _3 S$ |6 e& t- C* P
为了方便打印接收到的相关信息,需要对生成的工程做如下修改来映射 print 函数。% ]: V, K3 }/ X
main.c-声明
1 G8 O0 H; v, G! b! x+ v4 ]- #ifdef __GNUC__
( ?+ |2 {2 r" I: U# J5 Z - /* With GCC, small printf (option LD Linker->Libraries->Small printf3 F$ a1 ^3 N8 L# {! O3 Z5 G
- set to 'Yes') calls __io_putchar() */" t- u' m' w e/ z9 R9 n. X$ i# A
- #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)$ G0 q% g: O0 k9 L# J' y
- #else
3 l% ]( I5 J: E! G+ S& a$ } - #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
, ~5 i# W( t4 V, p - #endif /* __GNUC__ */
复制代码
" ~$ z0 N, |7 `6 T I+ x
0 t7 n5 r q1 _8 X: Qmain.c-Code4 R; f7 D( G, v" i3 U
- /**
; R, a9 h3 r F( z/ I - 7 g8 W6 E4 Y: B E/ I
- * @brief Retargets the C library printf function to the USART.4 c" K w0 z) b8 d
- * @param None
& Y# A$ t7 N% @- Z9 _: k. v- Y7 M - * @retval None
* N' T2 K0 D' r; r+ j+ [. X# o - *// c0 @! p2 v Z7 r
- PUTCHAR_PROTOTYPE
% t. W C" {1 Y ~2 g! J - {
- T) V2 j P; P- p+ K/ k - /* Place your implementation of fputc here */
* B. z0 M( i& c" {4 _3 B7 H - /* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
7 p% e$ I8 N' q% K, _* L - HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);7 L5 s7 {+ y7 Q' e; m; g& |- t( Y
- return ch;, G+ J( y! v& S7 }2 [# o. M! N
- }
复制代码
, [9 v. k/ S* g8 V$ I' f 修改工程代码
9 y" F% D5 Z% G: |) m* F 增加接收 Buffer
/ y- Q9 @0 N, M' g* d7 T. j/ T& ^ main.c; Y$ Y: f$ H1 L- U+ \
- //Modification 0" {7 `7 D2 L2 g* t, M
- //Store the revceived bytes number
" e! k* o1 e$ V$ R- w7 H - uint32_t Rev_Size = 0;
+ H% U7 r+ u) w, ^ - //Receive buffer
X& O5 I: T7 \: Q( ~7 c5 y6 F - uint8_t UART_RX_Buf[15];
复制代码 9 Q4 @' C! b6 e+ \# a o* H
stm32f4xx_hal_uart.c) y6 U7 |9 K0 C" \# Z. m0 m
- //Modification 0
5 M; x$ ? X; n; y7 B3 Q, E3 S* V - extern uint32_t Rev_Size;
复制代码 6 w2 k* P0 i5 x4 d4 W
在接收函数中使能 IDLE 中断
9 @+ [' W: y9 N) J' lstm32f4xx_hal_uart.c -> HAL_UART_IRQHandler ()函数
7 T$ K: X5 q8 R# o7 Q2 f- //Modification 2
& {! a1 `+ h, V8 E. L$ f - if(((isrflags & USART_SR_IDLE) != RESET) && ((cr1its & USART_CR1_IDLEIE) != RESET))/ ]% q2 j6 ?$ P( D5 V# }
- {& [' e2 J' U* Q
- //Record the received bytes number
0 l: Y9 W' {& n1 [ r - Rev_Size = huart->RxXferSize - huart->hdmarx->Instance->NDTR;
# Y( k5 v' M" G4 B, r# K6 D# g - //clear the IDLE flag
# U' A4 W% h8 U& D8 f# X. V' r/ p - __HAL_UART_CLEAR_IDLEFLAG(huart);' M3 `+ p& r" T5 F, S' Z- S; C h
- //Abord the received process" K; W' |, v, T d- M7 B
- HAL_UART_AbortReceive_IT(huart);
5 T6 A6 O: B5 O; y' o, E - return;
% ~" ]% ~4 N* ]/ ~- G. E; E" I9 t - }
复制代码
# w0 D% Q- D% t" m! J, ]9 J
3 d8 r/ Z0 |9 O, w) V J5 ]% _- F接收完成处理(IDLE 产生,一帧数据传输完成)
% B4 K! c0 @! T, v Dstm32f4xx_hal_uart.c -> HAL_UART_AbortReceive_IT ()函数; }, r, D 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>
复制代码
% J( Y, M, L8 |
) e8 Q! ~6 d; q2 f' omain.c+ f9 T7 ?% [8 _" `' S
- <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>
复制代码
3 `6 f5 P. J, \! p; \5 t; k3 E/ D; B1 [& w9 {
# g6 u# d( h! j% S' K使能接收
9 `1 |0 t* S8 |4 Q. v& Mmain.c; J/ Z! U8 h( `( r$ t8 x1 m
- <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>
复制代码
2 h9 W' s( _/ ~! R& G
# ]1 O! }4 P2 w: ]% j# W实验结果3 E6 a; A5 L e" B/ Q; P# G8 ~( K) \
使用串口调试串口,通过 STLINK 的虚拟串口发送数据,MCU 会返回接收多少个字节的数据,并将接收到的数据打印出来。
. r( S' C+ J# I& G) T下图是发送 0xAA 0x55 的实验结果。
# T1 j4 _2 F8 S$ e0 S7 d2 N. S7 k- u( G5 }! g2 H( b* t
6 k6 [9 H9 o `$ }
( z: t- l+ n ~( @1 W3 c: \
6 G% V8 s' R4 e5 _) g小结
# E. D6 L% C" m8 l1 Z% kIDLE 作为指示总线空闲状态的中断,合理的使用能够很大程度的节省代码程序设计工作量。这里只是做个简单示范,抛砖引
! J8 R. E! x& @玉,实际应用中希望大家可以灵活使用满足所需。
0 N+ T1 \2 b) A0 Y
+ |% V+ d( _0 v! X( M! L
$ T$ W7 P E1 } L6 n: i O; E4 A
|