|
这次我们利用STM32F103的UART内部的空闲中断来实现对串口任意长数据的接收,通过简洁的手段解决了接收端在事前无法得知数据长度的问题。本次教程我们需要一块STM32核心板与一个USB转TTL工具。 一、原理介绍STM32的异步串口接收寄存器可以存放1个字节,当我们开启接收中断(RXNEIE)时,当串口外设接收到一个字节的数据时 数据接收(RXNE)标志位置1,同时触发串口中断,此时我们可以把接收寄存器RDR中的数据转移至我们自定的缓存区中。 此种方式我们只能一个字节一个字节的接收数据,如果我们事先不知道需要接收的数据长度或未规定帧尾内容,我们便无法判断数据是否已经接收完毕。这时我们可以采用总线空闲标志(IDLE),使能空闲中断(IDLEIE)当检测到线路空闲时会触发串口中断,我们便知道一帧数据接收完成,可以送往上层应用中进行处理了。 二、代码编写这里我们使用串口1,波特率初始化为115200,因为stm32f1的hal库似乎实现不完全,没有总线空闲相关的函数,我们这里自己实现下。如果使用的是stm32f4及以上可以使用函数: - HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
复制代码 - b: h9 a1 w& c: l$ [2 T2 m
`5 W2 E2 R$ p/ ^ n2 L1、IO及UART初始化,并使能空闲中断: - // UART.c
0 @6 i7 d# L' B - & ~6 P' i$ Z/ ]$ s9 i# ~2 [1 S
- int UARTInit(void) u3 c9 T2 v% Y. E
- {
+ R$ v: N$ v9 H9 | - GPIO_InitTypeDef GPIO_InitStruct = {0};
2 y# t- H; ^, i: A. [ -
6 r2 B/ Z0 d3 |' F - /* IO初始化 */
; e% b% l0 b0 e1 q - __HAL_RCC_GPIOA_CLK_ENABLE();
# h* E8 a$ o2 M( ~ v' B -
) x! T6 q0 d% \% R" o& m9 q. E - GPIO_InitStruct.Pin = GPIO_PIN_9;
4 B T- q) W7 e, @ - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;) C/ Y, k& F' C% Y+ I3 e6 Q
- GPIO_InitStruct.Pull = GPIO_PULLUP;
- ?2 b0 ~ ~& B. h. Y2 I - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
7 B* g: Z2 k6 W* b' e9 H - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
' ?6 @$ v s: Y7 v- H
* c8 ]# v Y3 [2 \+ Y& E. ?- GPIO_InitStruct.Pin = GPIO_PIN_10;
- r) t0 c& C4 I' r! x% v3 [2 r - GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT;
+ Z' t% s7 K6 D% P$ g/ D5 a - GPIO_InitStruct.Pull = GPIO_NOPULL;, }1 H- i: T4 I: g! O& R
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;" y& M2 H$ D% {! P. A
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
9 s6 L# w" m! P1 O& e0 u -
% G3 a# r5 \" j! ~0 w2 N - /* UART1初始化 */
4 s0 E7 n+ L- p+ s - __HAL_RCC_USART1_CLK_ENABLE();
4 r7 U: `6 H5 W. f - / P S6 `# i, ], Z6 S
- UARTDev1.UART_Handle.Instance = USART1;
& g! h7 V1 `2 Q6 F+ t! j# ` - UARTDev1.UART_Handle.Init.BaudRate = 115200;+ n3 J% J# i9 j' ^* r
- UARTDev1.UART_Handle.Init.Parity = UART_PARITY_NONE;
$ i* D1 U/ `, t1 V# i7 [$ M - UARTDev1.UART_Handle.Init.WordLength = UART_WORDLENGTH_8B;
: ^& e2 w- {: O& ~/ j# o# s5 Q - UARTDev1.UART_Handle.Init.StopBits = UART_STOPBITS_1;
7 F- Z" D( x; p2 h% h - UARTDev1.UART_Handle.Init.Mode = UART_MODE_TX_RX;5 K; [# Z( n a$ z& W. i8 P
- HAL_UART_Init(&UARTDev1.UART_Handle);3 |6 q0 q0 d5 b w' I. |1 m
- ) ?& @% Y3 D% \& F/ N6 C% c3 E) e
- /* UART1中断初始化 */
) m, Y/ [) M. g# j - HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);+ d2 g' C- w/ I$ k3 M
- HAL_NVIC_EnableIRQ(USART1_IRQn);& W2 D& o5 p1 \+ T) \# l
-
. J7 w {; d( h- r -
' q. o5 G. y7 j# w$ T/ k% ]+ r0 g# s - /* UART1启动接收 */6 y0 s- {) {. P) A/ ]& j0 Y
- HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);/ l* m5 u( [# Q
-
4 }4 O/ C, R. g9 b* q - /** UART1开启空闲中断 **/
- U( T3 X) f; p" Y w, e - __HAL_UART_ENABLE_IT(&UARTDev1.UART_Handle, UART_IT_IDLE);
6 c( M0 F8 F2 b! O* W -
& h( R7 M4 C; G6 u& q7 Z - return 0;
$ K& ~6 s0 @9 V# F! @ - }
复制代码
% {, B( ^% Q1 d1 E+ U- D) u2、中断处理函数,这里接收完一帧数据后会原样发出去: - // UART.c8 [2 [# Q6 i$ e- v% H
- void USART1_IRQHandler(void)! B: X. V4 U/ s
- {
) q. J/ Q1 s& \5 ]. `/ F, U - UARTDef *huart = &UARTDev1;
# f" W5 Q& q9 }& i6 x -
( y: n, i" } @% @ - HAL_UART_IRQHandler(&huart->UART_Handle);
3 j& z* e1 V7 G; r! O) y+ Y - /* F1的HAL库中未实现IDLE相关功能,固自行实现 */
( k2 {: R9 Z7 v: B: S! p0 S; z - if(__HAL_UART_GET_FLAG(&huart->UART_Handle, UART_FLAG_IDLE))
& }8 E8 ] w# | - { /* 接收到完整一帧数据 */
7 f/ i+ e3 D# k, L - UARTSendData(huart, UARTDev1.RxBuffer, UART_RX_BUFFSIZE - UARTDev1.UART_Handle.RxXferCount);7 |9 A9 O- p: Y$ b
-
1 o. B! m2 X* Q: C# F - /* 重新启动接收 */
, A' v! \% l9 C% E' G# l - HAL_UART_AbortReceive_IT(&UARTDev1.UART_Handle);% I/ o% I P' X8 Q& }- [8 |) ?$ s; z
- HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);$ x" ]8 \! B6 F
- /* 清空闲中断 */; ]9 S8 \& C/ M0 C
- __HAL_UART_CLEAR_IDLEFLAG(&huart->UART_Handle);% @( h; h) [1 P* s! _7 [+ y6 K8 ~
- }
: ~ e r' L' V - }
复制代码 . I- ~; A2 S9 q; b; I
3、主函数里别忘了添加初始化函数,因为所有处理都在中断中完成了,这里主循环里就放了个led闪烁的代码: - int main()" z( B2 t; m! g' d( j ?* [
- {0 ~' x: ^6 P, E9 ^6 e% s1 b
- HAL_Init();4 S4 c! B8 A; \2 @0 b
- SystemClockInit();
5 M6 F# E; R4 C9 D8 n- | - GPIOInit();, U& k& ` l0 ?; S
- UARTInit();0 x2 k3 @# ^) }( h
-
! M/ }# e% A6 H X - while(1)4 x9 v7 H {- R0 F
- {! d3 U. G. {: Q1 q
- HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8);! E) U; O1 ~8 w$ L1 h, F5 n
- HAL_Delay(500);
4 M+ I, U4 X' |' i - }
7 J. L9 L# O! D - }
复制代码 2 T6 p# ^ L: X7 y3 F* B4 K
三、效果演示我们当前实现了从串口1接收了一帧数据后原样返回的功能,我们拿出USB转TTL模块,把RXD、TXD分别与板子的PA9、PA10连接,打开串口调试助手,设置好对应波特率,发送一条信息可以看到我们收到了相同的字符。
" f5 {; T( B% J" m
0 I; { I6 ~' U* E$ F! T" Z. B
完整代码可以从文章最后方下载,不同文章的代码在不同branch。下次我们讨论下SPI的使用。
' B p- F$ K% c9 G% |4 h) j
转载自:嵌入式技术栈 如有侵权请联系删除 . ~! B0 o' `7 L
" c, o9 B4 L4 W! L8 q* R, g/ @
|
学习了