这次我们利用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);
复制代码
+ r' ?' y" \* h- }6 y0 X* z
8 K4 w( f) ?( x/ @* I+ g" V1、IO及UART初始化,并使能空闲中断: - // UART.c! { Q) d" y+ _5 K/ X! C1 g4 y" ]
3 m8 i) u! `4 @5 Q8 s R% T& i; n- int UARTInit(void)
+ H$ T% J1 Y! s8 W+ |+ v - {
. u' y+ m2 P- \& {/ h - GPIO_InitTypeDef GPIO_InitStruct = {0};
. _; s4 N) c* P7 A7 \' |/ y, p0 V3 s - ; a7 T" U4 F4 k) g+ u" T
- /* IO初始化 */
' ^! ]& h0 g9 U8 G3 J* e - __HAL_RCC_GPIOA_CLK_ENABLE();
2 J' C3 n6 c C/ x, V0 }0 J - # B: k+ b7 O% Z- Z# y" D. M
- GPIO_InitStruct.Pin = GPIO_PIN_9;
. H1 L3 A: F; A - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;- F( m4 \* ]8 H+ H
- GPIO_InitStruct.Pull = GPIO_PULLUP;
/ h4 w" h. z; o' J% L - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;& \% Q. {0 P$ z6 B k
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/ ~/ Z/ B4 O; U3 D1 r( L& M* S: Z
8 G8 Z: t; j2 O- GPIO_InitStruct.Pin = GPIO_PIN_10;
' ?9 T4 U3 }; x; d' C* P3 Y, k - GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT;" I% I3 T+ h9 ^
- GPIO_InitStruct.Pull = GPIO_NOPULL;
* ~9 n0 _! d5 v' h% U6 [7 n - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;# x0 X( `2 ]$ e- [
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
6 ?; X. `# D8 x2 O - * P) D0 o- J! T
- /* UART1初始化 */
9 l5 i9 H6 w/ p" z: v$ \ - __HAL_RCC_USART1_CLK_ENABLE();& W5 j7 }! H* ?! Y) G* [
- * Q2 j7 N3 j" ^0 A6 {1 Q: \; J
- UARTDev1.UART_Handle.Instance = USART1;% J% ~5 H, d+ \) ?5 M
- UARTDev1.UART_Handle.Init.BaudRate = 115200;6 f( ~+ J `+ Z9 B y; `9 q
- UARTDev1.UART_Handle.Init.Parity = UART_PARITY_NONE; b; j: R5 `+ y6 Z) H
- UARTDev1.UART_Handle.Init.WordLength = UART_WORDLENGTH_8B;! g! e* |8 `% ^; U T8 [
- UARTDev1.UART_Handle.Init.StopBits = UART_STOPBITS_1;
8 a) _ C0 I" G8 O# N - UARTDev1.UART_Handle.Init.Mode = UART_MODE_TX_RX;
) `/ G- m1 j( r5 t3 E; R - HAL_UART_Init(&UARTDev1.UART_Handle);
3 ?4 y+ i6 R4 f# o8 n- T" |
( O- K" h: }7 ?% k; p# j- y- /* UART1中断初始化 */
+ A% Y0 v7 f; [. p - HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
9 d3 H# f a3 @* }& r+ ] - HAL_NVIC_EnableIRQ(USART1_IRQn);
# H/ @, [* [8 e - 3 F3 U' p& U; C) u3 \
- ) l5 Q0 h' i; n: T4 \7 M
- /* UART1启动接收 */
& \) {9 V/ y! A - HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);
, {' T# [7 y4 \$ U/ r* T/ g- a - & W+ `6 ? g) E& z+ ~, k$ z: A* i+ \/ @
- /** UART1开启空闲中断 **/+ o. \5 \9 I# \. u& d* [2 I1 u
- __HAL_UART_ENABLE_IT(&UARTDev1.UART_Handle, UART_IT_IDLE);# w- E& g8 d- Z8 H% a
-
: P9 ^; M D( R - return 0;: e9 b L5 j* c- b
- }
复制代码
+ [* Y7 F9 l" k7 F1 b2、中断处理函数,这里接收完一帧数据后会原样发出去: - // UART.c
: L$ P2 `1 H' d6 i! n! F3 y - void USART1_IRQHandler(void)6 m2 Z: G' c D4 G6 R5 c
- {9 I) l4 M+ t: D* O9 x5 i2 k( w7 w
- UARTDef *huart = &UARTDev1;# [$ k; Y) x) P+ L9 Q. x
- . ?/ n$ @& M- ]- K- x. F$ {7 h
- HAL_UART_IRQHandler(&huart->UART_Handle);5 r9 L! v' ^3 s& r, }
- /* F1的HAL库中未实现IDLE相关功能,固自行实现 */
6 A; A: E0 ?# G7 b- n% Z3 w4 `! B) k$ g - if(__HAL_UART_GET_FLAG(&huart->UART_Handle, UART_FLAG_IDLE))3 G" z0 ]9 H* @# H. ?
- { /* 接收到完整一帧数据 */
" ]1 ?3 t& a" J4 N* u) d - UARTSendData(huart, UARTDev1.RxBuffer, UART_RX_BUFFSIZE - UARTDev1.UART_Handle.RxXferCount);$ ^) _/ w1 |9 n( h' B6 ^
-
# W9 v! d* Z: E# J9 V4 c4 C - /* 重新启动接收 */
- B+ e. l# ~; R( R$ q - HAL_UART_AbortReceive_IT(&UARTDev1.UART_Handle);
" L$ s2 p: T. m- d$ e8 |# l - HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);- l* A1 H) ?4 Z) I; L
- /* 清空闲中断 */
7 @" N+ V9 g' K2 y' z - __HAL_UART_CLEAR_IDLEFLAG(&huart->UART_Handle);
5 ]1 i: @* o7 ^7 B5 X8 U - } E6 @, ~4 T+ ^9 ^$ u
- }
复制代码
7 ]- Q$ Q7 `+ h5 K3 a1 S" P0 H' A! j3、主函数里别忘了添加初始化函数,因为所有处理都在中断中完成了,这里主循环里就放了个led闪烁的代码: - int main()- z7 `% Y* y2 E m- L* f* s4 i
- {
4 |# T ]( Z1 ~6 J# n! K& c+ z - HAL_Init();
* r4 q. ^8 r- R7 ?! r, W, h - SystemClockInit();
. d0 C( Q/ s( [! L0 j - GPIOInit();
1 x% h- n! }, l/ E, e# B1 h - UARTInit();; \, Q/ A- k4 _
- 5 T) ]+ R9 K0 U2 j/ ^6 r
- while(1)2 v4 b. O" v% I
- {
% q5 \7 W' D+ @' w3 U8 m# `$ Y; X - HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8);: b- {( r) r% }; x
- HAL_Delay(500);9 z' `# ?1 Z% `0 v7 U/ S, ~
- }
6 p5 E# Z8 f1 t2 }% j; {% w - }
复制代码
2 W7 |) e/ d1 l; |% u) Q6 Z三、效果演示我们当前实现了从串口1接收了一帧数据后原样返回的功能,我们拿出USB转TTL模块,把RXD、TXD分别与板子的PA9、PA10连接,打开串口调试助手,设置好对应波特率,发送一条信息可以看到我们收到了相同的字符。
& s9 ?* X/ z( t, D
z; l1 F3 h3 h" o
完整代码可以从文章最后方下载,不同文章的代码在不同branch。下次我们讨论下SPI的使用。
( ~! ?6 l' m" f1 B" Q
转载自:嵌入式技术栈 如有侵权请联系删除 $ C. ?- v$ z$ {
! O2 J2 F6 Y ]0 i9 N3 V2 v' H |
学习了