串口不定长接收在项目中经常用到,像MODBUS串口服务器中就会经常传来一大帧数据,数据的长度不定,通常人们的做法是采用环形队列接收的方式,以特定的结束标志位如0x0d 0x0a等用以判断一帧数据的结束,但实际上STM32这种高级的ARM单片机是带有空闲中断和DMA的,不需要用环形队列这种纯软件的做法实现不定长接收,直接用空闲中断+DMA就可以实现了,非常简单,小白也能一看就懂。 首先是要初始化串口不定长接收,比如打开空闲中断,打开DMA,这里我选的是板上的USB转TTL串口即串口3,对应的接收DMA为DMA1流1: 代码如下: ' e8 {1 c% k% Z0 p void UART3_Init(int baud) {5 {: ^$ q7 k7 j5 J$ ] __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_USART3_CLK_ENABLE();) A' T. [5 z6 }7 e W* Y __HAL_RCC_DMA1_CLK_ENABLE();% M) ?8 F) U$ y* l7 O GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;$ H# m, C) y! S0 F GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF7_USART3;5 X: m; c2 W( I; s8 B: z HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);. }2 V' h, C; _2 J' |5 F, \3 u8 q " s- K- t" G# o. I huart3.Instance = USART3; huart3.Init.BaudRate = baud; huart3.Init.WordLength = UART_WORDLENGTH_8B; huart3.Init.StopBits = UART_STOPBITS_1;# q3 z+ O) L, r: Y# C( d' P. G huart3.Init.Parity = UART_PARITY_NONE;. Z8 p) t2 f7 V& w( T3 s0 J4 k7 n. ` huart3.Init.Mode = UART_MODE_TX_RX; huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;3 V7 e; k! Y; r9 l6 h& D. |7 }7 Q huart3.Init.OverSampling = UART_OVERSAMPLING_16; HAL_UART_Init(&huart3); __HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);6 `2 b/ y( _7 _ HAL_NVIC_SetPriority(USART3_IRQn,0,0); HAL_NVIC_EnableIRQ(USART3_IRQn);7 p W- K3 J z: ]4 Z' }: t hdma_usart3_rx.Instance = DMA1_Stream1;* B O- |. V$ z; o% M7 A hdma_usart3_rx.Init.Channel = DMA_CHANNEL_4; hdma_usart3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;6 o$ R( N9 ^+ K% K' K2 H/ k D hdma_usart3_rx.Init.PeriphInc = DMA_PINC_DISABLE;; \7 Z& W+ P. ?! d* W hdma_usart3_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_usart3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_usart3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;. K9 p$ y' W2 l& v: b# o. v hdma_usart3_rx.Init.Mode = DMA_NORMAL;) W' b) T1 k* O+ c hdma_usart3_rx.Init.Priority = DMA_PRIORITY_LOW; hdma_usart3_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; HAL_DMA_Init(&hdma_usart3_rx);8 v- {& p" f: v. M0 @ __HAL_LINKDMA(&huart3,hdmarx,hdma_usart3_rx);8 A- s2 o" {7 r$ Q } ( \& V e; u+ G! [+ w! Y+ i void USART3_IRQHandler() { int temp;0 x+ z3 ^2 P3 c1 X if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE)) { __HAL_UART_CLEAR_IDLEFLAG(&huart3);- u* l2 |9 e' t HAL_UART_DMAStop(&huart3); temp=__HAL_DMA_GET_COUNTER(&hdma_usart3_rx);$ l% t( G$ Y$ ~( A0 t3 J) O rx_len_uart3=BUFFERSIZE-temp; rx_flag_uart3=1; } } 在主循环中加入以下语句以让单片机轮询串口不定长接收的标志位,并在液晶彩屏上显示出来: void UART_DMA_Get()0 c$ S1 ~& m6 ~ { int i; if(rx_flag_uart1==1) o+ z6 W& O$ _$ |& e7 R( X {* ]* U! Q% T; o* |& n8 w; I% F! d printf("rx_len=%d\n\n",rx_len_uart1); printf("%s\n\n",rx_buf_uart1); 4 P; U( k% o8 T, w rx_len_uart1=0;4 ^8 _! C) w, s rx_flag_uart1=0;; W7 W" H8 A5 R* C4 O u) u } if(rx_flag_uart3==1)* z) o9 X3 S9 t) A% ^! [; f { printf("rx_len=%d\n\n",rx_len_uart3); for(i=0;i<rx_len_uart3;i++), c# `: M& M/ G- h+ o# j! } { if(rx_buf_uart3=='\r'||rx_buf_uart3=='\n') rx_buf_uart3=0;5 j+ W3 k( G* d+ A 7 k" v; @2 o4 ]$ H& \# A" @ }6 V/ \* }# G1 I/ P printf("%s\n\n",rx_buf_uart3);' k5 {/ i6 v2 m7 L) ?. ` L$ I SPILCD_DrawString(0,128," ",BLACK,CYAN,ZF32_NORMAL);; x# Q4 }. l; d$ X/ D SPILCD_DrawString(0,128,rx_buf_uart3,BLACK,CYAN,ZF32_NORMAL); rx_len_uart3=0; rx_flag_uart3=0;6 x7 ?* N& R8 I } HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf_uart1,BUFFERSIZE); HAL_UART_Receive_DMA(&huart3,(uint8_t*)rx_buf_uart3,BUFFERSIZE); } % v5 L3 _8 l, Z- ^5 i. y 看看效果,可以看到,上位机串口程序那边发什么,显示屏就显示什么:' K5 v: A% B8 ?# b! F 上传工程文件:2 e; k/ ^! b7 @, U( V. z |
不过可靠性可能需要认真考虑。
串口属于硬件层,数据链路层协议还是需要考虑的。8 Q2 i# E4 V5 ]/ m
好东西,感谢分享!