串口不定长接收在项目中经常用到,像MODBUS串口服务器中就会经常传来一大帧数据,数据的长度不定,通常人们的做法是采用环形队列接收的方式,以特定的结束标志位如0x0d 0x0a等用以判断一帧数据的结束,但实际上STM32这种高级的ARM单片机是带有空闲中断和DMA的,不需要用环形队列这种纯软件的做法实现不定长接收,直接用空闲中断+DMA就可以实现了,非常简单,小白也能一看就懂。1 j# ?9 `* v- M" D) @ 首先是要初始化串口不定长接收,比如打开空闲中断,打开DMA,这里我选的是板上的USB转TTL串口即串口3,对应的接收DMA为DMA1流1: 代码如下:0 O1 Z7 ]; y% c 7 M) r' y& z6 B( `+ F8 d0 G void UART3_Init(int baud)6 m @4 I4 @, o8 F' s {4 U- ]/ D8 S* f! m: ^ __HAL_RCC_GPIOB_CLK_ENABLE();8 F' {; N8 u" \6 ?# d1 @* u __HAL_RCC_USART3_CLK_ENABLE(); __HAL_RCC_DMA1_CLK_ENABLE();6 T& X! j8 S% i' o1 X GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP;1 S3 D- E5 E) U( ` GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF7_USART3; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);5 M9 I% x4 N9 b huart3.Instance = USART3; huart3.Init.BaudRate = baud; huart3.Init.WordLength = UART_WORDLENGTH_8B;! t' ^' D+ h' U& \6 z huart3.Init.StopBits = UART_STOPBITS_1;5 g6 D1 I* z1 _- b b" s+ Y huart3.Init.Parity = UART_PARITY_NONE; huart3.Init.Mode = UART_MODE_TX_RX;9 I% T/ @. C4 H+ i huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart3.Init.OverSampling = UART_OVERSAMPLING_16; HAL_UART_Init(&huart3);7 D9 Y# t- x$ ~3 {# I* X4 ]5 Q! ~3 x3 Y __HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);0 s9 @9 a' \4 V& `" z9 m HAL_NVIC_SetPriority(USART3_IRQn,0,0);' V9 m& b, S1 g3 u; K* G: @+ W HAL_NVIC_EnableIRQ(USART3_IRQn); hdma_usart3_rx.Instance = DMA1_Stream1;6 C9 x) T/ {: g hdma_usart3_rx.Init.Channel = DMA_CHANNEL_4;! S; a3 N- P9 R) s" Q hdma_usart3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_usart3_rx.Init.PeriphInc = DMA_PINC_DISABLE;, P8 Y: D2 r. w- d hdma_usart3_rx.Init.MemInc = DMA_MINC_ENABLE;' w3 y: }1 B* ? hdma_usart3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_usart3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;& `/ X$ ]+ N1 s9 H2 a; F/ k hdma_usart3_rx.Init.Mode = DMA_NORMAL; hdma_usart3_rx.Init.Priority = DMA_PRIORITY_LOW; hdma_usart3_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;' d! ?% @( p8 X8 g# |0 V+ i( ]3 ]' L HAL_DMA_Init(&hdma_usart3_rx); ; Q2 ^6 C" y1 y/ _& t- Y' y __HAL_LINKDMA(&huart3,hdmarx,hdma_usart3_rx);! m( Y3 v. K0 x# w3 y } 9 g' R3 w/ x. Z( H/ f0 v" | void USART3_IRQHandler(): x/ g& Y6 F3 p- ^4 P3 u) y { int temp;" M; s' |( g+ K7 d5 G. H if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE)) {" ?$ F8 d# Q" a3 |- J __HAL_UART_CLEAR_IDLEFLAG(&huart3); HAL_UART_DMAStop(&huart3);- a, ^8 W/ [5 Z% _% O& x& y temp=__HAL_DMA_GET_COUNTER(&hdma_usart3_rx); rx_len_uart3=BUFFERSIZE-temp; rx_flag_uart3=1; }8 }3 O9 H$ U( g$ s4 y9 ~# z } 在主循环中加入以下语句以让单片机轮询串口不定长接收的标志位,并在液晶彩屏上显示出来:& u2 c0 i/ `: B" F void UART_DMA_Get()* j' G. r& ~4 t) f) ^/ L6 h+ a {' K2 B" C6 N! ?$ U# m5 F6 f8 V int i;7 r2 X- k' |$ E: b if(rx_flag_uart1==1)9 T) }# V3 F7 P" }0 f- Q8 ^. n. O {% y: ]+ f" p1 J# q! } printf("rx_len=%d\n\n",rx_len_uart1);4 F2 t ]9 O8 d& d printf("%s\n\n",rx_buf_uart1); 2 F' O: Y6 K2 e. J rx_len_uart1=0;/ f' N3 r2 b( s7 G rx_flag_uart1=0; } if(rx_flag_uart3==1): k% W3 F* c; k/ K/ u { printf("rx_len=%d\n\n",rx_len_uart3);7 h+ d& T4 t. L) F. p for(i=0;i<rx_len_uart3;i++)8 C: a( i: w9 Q! U1 u {) G% v4 A! b* k/ g7 l if(rx_buf_uart3=='\r'||rx_buf_uart3=='\n') rx_buf_uart3=0; : \- A& S p ~# P; ?* N) \ } printf("%s\n\n",rx_buf_uart3);- |. h" J0 s2 i5 U8 o- k. i- [8 I SPILCD_DrawString(0,128," ",BLACK,CYAN,ZF32_NORMAL);0 ?# L1 u0 p) Y: z: o' r; J- F SPILCD_DrawString(0,128,rx_buf_uart3,BLACK,CYAN,ZF32_NORMAL);) u; {! R1 k* N. y5 h) I3 V L7 y3 B5 e* U& { rx_len_uart3=0; rx_flag_uart3=0;4 W/ O) p+ T) R* L9 ] } HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf_uart1,BUFFERSIZE); a8 G/ i5 n5 s5 y$ z2 q7 ~ HAL_UART_Receive_DMA(&huart3,(uint8_t*)rx_buf_uart3,BUFFERSIZE);7 K- \. {0 B: @0 E2 c/ N5 l" P2 [ } ! ]/ c* c* ?, C. Q( T 看看效果,可以看到,上位机串口程序那边发什么,显示屏就显示什么: 上传工程文件:' L' [ s2 }- e |
不过可靠性可能需要认真考虑。
串口属于硬件层,数据链路层协议还是需要考虑的。+ F" x: w) L0 l& E5 ~! z @
好东西,感谢分享!