|
这次我们利用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);
复制代码
$ i5 _( ~ i9 n& O; r/ x7 H9 b- F6 X. L" N" O; m7 g8 d& b3 m
1、IO及UART初始化,并使能空闲中断: - // UART.c) Z, j9 d4 q, d/ e$ m; I# N
7 _0 ^8 a& b k0 I: _$ ]- int UARTInit(void)
, d: I$ v( Z) f& T7 Y! V - {
; M u6 K+ } ]* R' z - GPIO_InitTypeDef GPIO_InitStruct = {0};7 C2 v* a' N. B, d
- 7 j' M% E4 J# c# T" h4 }
- /* IO初始化 */* b3 ]5 Y( D" j7 v2 S
- __HAL_RCC_GPIOA_CLK_ENABLE();
6 Z3 @3 s' ~, ], p* f' m - 7 N; ~9 g3 d0 y8 w% ~: C
- GPIO_InitStruct.Pin = GPIO_PIN_9;
$ {2 I7 h O/ R7 q- g. T/ F8 T - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;8 @2 v! f" G1 j/ q) w& E
- GPIO_InitStruct.Pull = GPIO_PULLUP;
5 R5 X" }4 c) G! [! ] Q, f - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
2 M9 F. Y7 a4 G& I3 Y - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
0 g, d8 q2 b0 ~9 \( k
9 I" w8 f3 B# t2 ?- m- GPIO_InitStruct.Pin = GPIO_PIN_10;7 J/ E f9 P- b* {) q7 C
- GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT;6 Y! M+ w* l4 ]3 i
- GPIO_InitStruct.Pull = GPIO_NOPULL;
9 j' n) d# M' k/ C& d/ ~ - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
; Y& p' u9 B( H4 p - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
4 \. ?3 Y7 l: Q: d+ b - 5 ^) ~, p* |. `/ r% p$ g+ n J
- /* UART1初始化 */
; R, h! j) {- X; w - __HAL_RCC_USART1_CLK_ENABLE();
8 \: y7 A$ M# t, ^; Q9 c% g/ Q -
: J. p& x; Y/ n - UARTDev1.UART_Handle.Instance = USART1;
2 [8 e2 `6 A2 |6 A& i9 V8 t: f V - UARTDev1.UART_Handle.Init.BaudRate = 115200;
4 y& i0 x& _7 s- W3 x# N5 K - UARTDev1.UART_Handle.Init.Parity = UART_PARITY_NONE;4 Z) k! ]( D" `' Z! B
- UARTDev1.UART_Handle.Init.WordLength = UART_WORDLENGTH_8B;) B! d3 m+ |) v& p
- UARTDev1.UART_Handle.Init.StopBits = UART_STOPBITS_1;' O# Q4 U; K8 ^8 S9 M
- UARTDev1.UART_Handle.Init.Mode = UART_MODE_TX_RX;
* O1 j- n" H; R/ I+ }; r - HAL_UART_Init(&UARTDev1.UART_Handle);
; y9 Y* G! p7 B- L x. r - # I& z B+ a( r9 `9 h/ [* T
- /* UART1中断初始化 */
8 a# P# `, ?8 i/ i# x- F - HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
2 f$ S% _ ]4 [& k5 c: c O - HAL_NVIC_EnableIRQ(USART1_IRQn);
% r+ V1 T/ a) @. X7 O6 s - 4 ~1 z) {' F9 k1 k3 h( e
- 1 D. l& c% ?. o) j" S
- /* UART1启动接收 */
1 M: f# \9 r) P% P2 Q7 i4 g& J - HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);% P* h: s! k$ l- N* U
- 4 G: ] f* K- F% X4 B0 }
- /** UART1开启空闲中断 **/6 h$ \3 h" C) P, D' \7 m
- __HAL_UART_ENABLE_IT(&UARTDev1.UART_Handle, UART_IT_IDLE);
, O/ @- J: C4 [ - 3 P( ]0 k q4 X V5 B/ h
- return 0;
- ]- n- L( M0 u - }
复制代码 . i% L9 W. N7 O" v
2、中断处理函数,这里接收完一帧数据后会原样发出去: - // UART.c
2 A4 G4 V% S# s. o8 Z+ h - void USART1_IRQHandler(void)
4 V+ v; |! Y* c' l - {
" y2 w0 M/ F6 ]5 Q: o, P' r - UARTDef *huart = &UARTDev1;" F8 V; f! L# t3 ? k
- 9 ~2 T! [$ B9 l& W
- HAL_UART_IRQHandler(&huart->UART_Handle);! f. g% U5 @& v7 B1 i+ s7 n3 Y
- /* F1的HAL库中未实现IDLE相关功能,固自行实现 */
3 W- w$ P- ]- j1 ]$ }7 [% X: ~ - if(__HAL_UART_GET_FLAG(&huart->UART_Handle, UART_FLAG_IDLE))
, e4 Z4 e \* |. Z - { /* 接收到完整一帧数据 */
7 a. a- [6 J4 ]" B0 }1 ? - UARTSendData(huart, UARTDev1.RxBuffer, UART_RX_BUFFSIZE - UARTDev1.UART_Handle.RxXferCount);
$ _+ O6 h5 X. D" x -
3 f/ {2 a [$ o2 L$ T5 a- v - /* 重新启动接收 */4 S4 Q/ A& R$ o; ?
- HAL_UART_AbortReceive_IT(&UARTDev1.UART_Handle);+ l( U$ |6 M$ w' [& p' N' _
- HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);
. H# k _7 `# E/ q$ J; @ - /* 清空闲中断 */
' v8 h' f' C8 G. K' r; D/ m - __HAL_UART_CLEAR_IDLEFLAG(&huart->UART_Handle);3 Q& ]3 D" B" ?' W( Z$ I
- }
, E8 K* ^: t2 [# ^# f ~: m - }
复制代码 0 O2 e. z- G- h; H! y3 G# s4 Y' o
3、主函数里别忘了添加初始化函数,因为所有处理都在中断中完成了,这里主循环里就放了个led闪烁的代码: - int main()
8 k3 n* O; D6 m! p% Q. D - {4 e/ B$ `/ @) Z' V
- HAL_Init();
1 g* f2 ?; L" R8 c7 o7 C u; L - SystemClockInit();
@) w$ S2 K8 L( X9 u - GPIOInit();& F. V( C, a* }/ W* ?+ @, U, Q
- UARTInit();
# L& l2 x& |% P -
2 X8 q8 x# t. z! d - while(1)9 I @' n( C) _# Z; B$ Q1 B
- {, n7 F% t5 j7 @# ]2 i
- HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8);6 @$ r% B6 o8 y% K' y
- HAL_Delay(500);
w' W% J# j8 l. b - }. J. F; u; h s9 s4 a
- }
复制代码 & O" d6 {2 O3 _
三、效果演示我们当前实现了从串口1接收了一帧数据后原样返回的功能,我们拿出USB转TTL模块,把RXD、TXD分别与板子的PA9、PA10连接,打开串口调试助手,设置好对应波特率,发送一条信息可以看到我们收到了相同的字符。
3 b+ J; K' E; d& i7 O
# H; S6 `7 I, v) z; f% X: j
完整代码可以从文章最后方下载,不同文章的代码在不同branch。下次我们讨论下SPI的使用。 7 A* a6 `; P, h- Y
转载自:嵌入式技术栈 如有侵权请联系删除
. Z4 G8 c: D9 ?4 \% e4 z0 a! z1 L( E
|
学习了