|
串口不定长接收在项目中经常用到,像MODBUS串口服务器中就会经常传来一大帧数据,数据的长度不定,通常人们的做法是采用环形队列接收的方式,以特定的结束标志位如0x0d 0x0a等用以判断一帧数据的结束,但实际上STM32这种高级的ARM单片机是带有空闲中断和DMA的,不需要用环形队列这种纯软件的做法实现不定长接收,直接用空闲中断+DMA就可以实现了,非常简单,小白也能一看就懂。4 Y4 t* _7 q+ O5 A& ~ 首先是要初始化串口不定长接收,比如打开空闲中断,打开DMA,这里我选的是板上的USB转TTL串口即串口3,对应的接收DMA为DMA1流1:# f2 R- r1 V4 b0 }3 J
代码如下:5 |" Q1 ?' C9 i , [' E3 P" D! D2 M8 ~) }8 L: [2 T void UART3_Init(int baud)) i! u9 @/ A- E! U! p { __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_USART3_CLK_ENABLE(); __HAL_RCC_DMA1_CLK_ENABLE(); * Z& s8 T L; c: ?: u$ f3 ]4 N2 Z GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;8 y6 n9 e* O2 t# [8 o GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP;. [) i, C# I6 A8 V# h GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;" k* Q' Y( P P+ ~' l. L5 I GPIO_InitStruct.Alternate = GPIO_AF7_USART3;1 ]* o* F, j' ]; d6 E9 u HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);2 S3 [) x' ?& b$ B & X+ v- N6 ~$ x+ Y huart3.Instance = USART3;* C3 J1 E1 S3 N huart3.Init.BaudRate = baud;+ N- H9 N1 l) \% h% I7 f8 ~( c+ O huart3.Init.WordLength = UART_WORDLENGTH_8B; huart3.Init.StopBits = UART_STOPBITS_1;- S! e5 J6 \# D; W huart3.Init.Parity = UART_PARITY_NONE; huart3.Init.Mode = UART_MODE_TX_RX;$ m5 k- p( N- B huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart3.Init.OverSampling = UART_OVERSAMPLING_16; HAL_UART_Init(&huart3); __HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);) K9 W2 {. W/ |' v! _ HAL_NVIC_SetPriority(USART3_IRQn,0,0);+ E6 X& M3 f2 T$ }9 f p# L, z HAL_NVIC_EnableIRQ(USART3_IRQn);/ u" C: o9 s2 w hdma_usart3_rx.Instance = DMA1_Stream1; hdma_usart3_rx.Init.Channel = DMA_CHANNEL_4; hdma_usart3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_usart3_rx.Init.PeriphInc = DMA_PINC_DISABLE;; `" r. M/ Y7 n3 i2 G& H hdma_usart3_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_usart3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; [& Y: l8 Q+ x0 s. h8 O$ w2 t hdma_usart3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_usart3_rx.Init.Mode = DMA_NORMAL; hdma_usart3_rx.Init.Priority = DMA_PRIORITY_LOW;' ~0 q/ I4 p" b0 b, n! c' ?8 e hdma_usart3_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;' j* C& ^( j! ^4 G9 C; U+ X HAL_DMA_Init(&hdma_usart3_rx);2 |, C# D: c4 w* R/ m - C1 E% |' Y h3 O% Y/ q$ e( _7 J __HAL_LINKDMA(&huart3,hdmarx,hdma_usart3_rx); } + z# h) a! l/ s( E & j1 B s: f& U/ R* G2 U7 U void USART3_IRQHandler() { int temp; if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE)) { __HAL_UART_CLEAR_IDLEFLAG(&huart3);6 L9 G2 J$ k1 F- Q& f8 U) { R HAL_UART_DMAStop(&huart3);/ W8 ?8 J4 T8 A temp=__HAL_DMA_GET_COUNTER(&hdma_usart3_rx); rx_len_uart3=BUFFERSIZE-temp; 2 a: I1 A' C8 W* C& A& Z8 B rx_flag_uart3=1;& P; A5 _; J) N/ f! [5 x1 Y } } 在主循环中加入以下语句以让单片机轮询串口不定长接收的标志位,并在液晶彩屏上显示出来: void UART_DMA_Get()5 |0 d) c6 E4 ~' {6 _ {6 w1 N; X! x9 Z$ s int i; if(rx_flag_uart1==1) { printf("rx_len=%d\n\n",rx_len_uart1); printf("%s\n\n",rx_buf_uart1); rx_len_uart1=0;$ n/ s. _$ }& K3 x3 \' p, K rx_flag_uart1=0; a; c, G8 o4 i z& w9 E9 r }& U) E4 x* e f) a if(rx_flag_uart3==1)) a. I! t% R$ o( @) U* l/ ~6 V { printf("rx_len=%d\n\n",rx_len_uart3); for(i=0;i<rx_len_uart3;i++)* q9 \+ ]+ z3 g" r3 g, X {; E7 U& Q& g' N) } if(rx_buf_uart3=='\r'||rx_buf_uart3=='\n') rx_buf_uart3=0;7 F9 K- y' G* ^% p. D S % J- g( \& W' ?, z' S2 C! K } printf("%s\n\n",rx_buf_uart3);5 a: p5 o! \# o+ T* P, M SPILCD_DrawString(0,128," ",BLACK,CYAN,ZF32_NORMAL);' j1 M+ h3 b- N( q2 m- g/ S" B0 Z SPILCD_DrawString(0,128,rx_buf_uart3,BLACK,CYAN,ZF32_NORMAL); / H7 o5 M( _7 d% G rx_len_uart3=0;/ x. S# L% q' x1 U$ D rx_flag_uart3=0;. j7 g7 V. n% w) b3 Q }3 }4 q9 o6 `) A HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf_uart1,BUFFERSIZE);8 {. n* Z5 _3 C HAL_UART_Receive_DMA(&huart3,(uint8_t*)rx_buf_uart3,BUFFERSIZE);0 S- B) ?9 w: u: }$ S# N# | }4 \( ]" D* q9 p0 i2 [ 9 R$ X# o1 b- G4 n 看看效果,可以看到,上位机串口程序那边发什么,显示屏就显示什么:
上传工程文件:
STM32F407ZGT6.zip
(551.38 KB, 下载次数: 31)
|
微信公众号
手机版
不过可靠性可能需要认真考虑。& `. v" j. r' U( m7 W( U% X4 _" c1 v
串口属于硬件层,数据链路层协议还是需要考虑的。
好东西,感谢分享!