|
这次我们利用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);
复制代码 5 j8 ~; R4 l$ q T& _) D
0 E: X* }6 o) j& O1、IO及UART初始化,并使能空闲中断: - // UART.c8 {4 g: f8 s9 s4 ~& C
- 6 |1 n8 j1 }9 p" k+ Y
- int UARTInit(void)
) P' v5 [" u; f$ L% r2 i - {
6 a9 ]" I$ Z! F+ ^- P - GPIO_InitTypeDef GPIO_InitStruct = {0};4 e, M! y& O5 y" O
-
* e% p w3 d' M; B( m; ^+ z - /* IO初始化 */
" {8 z8 {& W" E' U$ p2 y2 R& A - __HAL_RCC_GPIOA_CLK_ENABLE();
2 x' j. X, O; h' f -
( \7 t1 a+ Z5 h, M% I - GPIO_InitStruct.Pin = GPIO_PIN_9;
( [' t3 A9 k7 i3 \* a/ P1 ~ - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
# V8 j: F/ a) d" V9 x - GPIO_InitStruct.Pull = GPIO_PULLUP;
( `$ V: o2 ^0 r1 q8 e - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;" H0 z1 _' J7 U0 h
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
" h% p& R7 o9 C2 r+ m' E( a - ; U, \ ~4 o" F4 t) ^& h
- GPIO_InitStruct.Pin = GPIO_PIN_10;/ k9 l1 n' g& F
- GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT;
* }$ j6 I3 `2 p/ f) b) H+ ` - GPIO_InitStruct.Pull = GPIO_NOPULL; T0 Y3 g- E, y- q
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
6 j4 s. m k& {1 y - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);. x( R& Z x d6 m/ w
- . Z ^1 s. G0 _+ o0 T3 L
- /* UART1初始化 */+ B# k/ O& A d: G M" [/ w9 K
- __HAL_RCC_USART1_CLK_ENABLE();' J5 f m |8 Q6 k
- ! g8 q% P0 D0 B5 r
- UARTDev1.UART_Handle.Instance = USART1;1 w. Y( x% Y7 e8 l% b4 B# x9 ~
- UARTDev1.UART_Handle.Init.BaudRate = 115200;
! h6 U& m- ~) \) |, D - UARTDev1.UART_Handle.Init.Parity = UART_PARITY_NONE;
o) A. l. r# h( @( N - UARTDev1.UART_Handle.Init.WordLength = UART_WORDLENGTH_8B;
0 ] N' C9 G; d6 l- H* O% L - UARTDev1.UART_Handle.Init.StopBits = UART_STOPBITS_1;! i6 ]; C8 B' K( X0 ~
- UARTDev1.UART_Handle.Init.Mode = UART_MODE_TX_RX;
& W, V. i2 g' i" n+ Z' S% w - HAL_UART_Init(&UARTDev1.UART_Handle);
5 c; L% g) t% I# v6 a9 Q: k3 F$ y - " _) R$ ^. K" @; _0 {. C& A& h
- /* UART1中断初始化 */* f0 ^% R" z% q- \0 T
- HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);* g2 x. m. M. f4 i/ Y3 X
- HAL_NVIC_EnableIRQ(USART1_IRQn);5 Q0 Z0 l$ D# @9 |! L% U' S3 S
- , U* e" E1 R3 _( S& M' n4 M) K* d
- ' M% I v8 X5 o" `: V6 ]5 G$ ]! s! a
- /* UART1启动接收 */& \& c# k* C3 g; n- r5 H
- HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);# s- w! f4 d7 L8 B% B9 t
- - j0 j! } b4 g' ^0 u, i
- /** UART1开启空闲中断 **/
0 q5 z" s* ?( K! ~5 J. B - __HAL_UART_ENABLE_IT(&UARTDev1.UART_Handle, UART_IT_IDLE);/ j' T5 h5 u; E# A" q' U
- : d0 h0 h9 s2 G' o
- return 0;/ [6 ]% C( k6 ?$ e$ ?% g
- }
复制代码
/ }* R7 q8 @9 M3 X2、中断处理函数,这里接收完一帧数据后会原样发出去: - // UART.c
j o/ w G; N, p+ i4 ? - void USART1_IRQHandler(void)
3 j* u6 R$ E/ a. \ - {
- P# C: k+ h2 d0 a' r( a - UARTDef *huart = &UARTDev1;/ ?" Y6 T+ `0 Q
-
3 Q$ m9 W% g) E - HAL_UART_IRQHandler(&huart->UART_Handle);7 F4 d( j, i2 M9 h1 [7 B
- /* F1的HAL库中未实现IDLE相关功能,固自行实现 */0 W' s: x" _6 R1 Z0 S
- if(__HAL_UART_GET_FLAG(&huart->UART_Handle, UART_FLAG_IDLE))
& O" f2 ^! `$ f+ K/ |& v7 [ - { /* 接收到完整一帧数据 */
0 R5 Y& D" t0 `+ W. U; u/ ]' H1 V - UARTSendData(huart, UARTDev1.RxBuffer, UART_RX_BUFFSIZE - UARTDev1.UART_Handle.RxXferCount);. T- D' s8 s% ~0 a1 @; f
-
6 h$ @0 \# I1 C' E - /* 重新启动接收 */2 ^ W$ G/ t! j, S2 B' Y
- HAL_UART_AbortReceive_IT(&UARTDev1.UART_Handle);
( a: O7 P! i! {4 b0 L# N- J: x - HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);. p8 l+ s, H3 W7 Q& H, }6 U j
- /* 清空闲中断 */
8 [: v2 \: `* I" U - __HAL_UART_CLEAR_IDLEFLAG(&huart->UART_Handle);
4 M% F5 s* A# ~* E - }
- c3 j6 ] B+ _2 U T - }
复制代码 / Y* z* N( v7 t6 n3 a! Q* z. b
3、主函数里别忘了添加初始化函数,因为所有处理都在中断中完成了,这里主循环里就放了个led闪烁的代码: - int main()
w# F% v/ R1 H - {+ i! R$ C7 O4 t# F
- HAL_Init();
# h8 K" @9 Q+ V4 Z ] - SystemClockInit();+ ^+ f* ]$ e7 Y0 e
- GPIOInit();! w. k0 T* f3 H6 V
- UARTInit();* m' }' Z" ?, n
-
/ V1 ?( A, J3 N ?' O - while(1)1 L$ E# @, y9 O- [8 B. j2 g: h
- {% N. g" x8 S, t: ]4 W6 D) w
- HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8);" N0 p+ U5 E. p1 O5 c# y5 I
- HAL_Delay(500);
( f7 L+ `* N( G) \! @# B - }
( |+ X+ V7 F8 x6 i; i8 v - }
复制代码
% ^7 J; z5 K1 Z) S" @# y8 e1 j三、效果演示我们当前实现了从串口1接收了一帧数据后原样返回的功能,我们拿出USB转TTL模块,把RXD、TXD分别与板子的PA9、PA10连接,打开串口调试助手,设置好对应波特率,发送一条信息可以看到我们收到了相同的字符。
2 b, ~2 N: W! L, U8 y: n" r
# e- k! u5 e. G# P2 }4 i
完整代码可以从文章最后方下载,不同文章的代码在不同branch。下次我们讨论下SPI的使用。
, ^7 j. H7 G& u" l2 |
转载自:嵌入式技术栈 如有侵权请联系删除 0 ]4 b% v: k& \% w* F8 d# T
, d; h" S% v1 ]6 G |
学习了