|
串口不定长接收在项目中经常用到,像MODBUS串口服务器中就会经常传来一大帧数据,数据的长度不定,通常人们的做法是采用环形队列接收的方式,以特定的结束标志位如0x0d 0x0a等用以判断一帧数据的结束,但实际上STM32这种高级的ARM单片机是带有空闲中断和DMA的,不需要用环形队列这种纯软件的做法实现不定长接收,直接用空闲中断+DMA就可以实现了,非常简单,小白也能一看就懂。7 E# z( S4 j# C 首先是要初始化串口不定长接收,比如打开空闲中断,打开DMA,这里我选的是板上的USB转TTL串口即串口3,对应的接收DMA为DMA1流1:
代码如下:" [9 c* F2 R& I4 H void UART3_Init(int baud) { __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_USART3_CLK_ENABLE();1 X S) C' d/ n& R4 ^ __HAL_RCC_DMA1_CLK_ENABLE();& X9 i; ? q+ _. j- ~+ U * O& K& o. V/ n5 q; ? GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;6 {5 m: B7 c( ?0 m GPIO_InitStruct.Alternate = GPIO_AF7_USART3;5 e3 z. ~' a1 }3 h8 w1 ^% g HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);. D( p$ A' g& j y$ w# {# S huart3.Instance = USART3; huart3.Init.BaudRate = baud;# |5 L1 o% t L+ P$ D" ] huart3.Init.WordLength = UART_WORDLENGTH_8B;- ?7 A0 Z6 A9 m& R2 \% E3 t huart3.Init.StopBits = UART_STOPBITS_1;# O% y# C. S/ C) d huart3.Init.Parity = UART_PARITY_NONE; huart3.Init.Mode = UART_MODE_TX_RX; huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart3.Init.OverSampling = UART_OVERSAMPLING_16;9 D& f' m0 i6 f" ^6 V HAL_UART_Init(&huart3);* m. e R3 y, L5 U __HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);! G! ^, r% h- @ HAL_NVIC_SetPriority(USART3_IRQn,0,0);6 D, l5 P# b( e HAL_NVIC_EnableIRQ(USART3_IRQn);- s L3 M8 b8 [8 {# B; D hdma_usart3_rx.Instance = DMA1_Stream1; hdma_usart3_rx.Init.Channel = DMA_CHANNEL_4;8 b4 y! R, ?* p. ^, _6 [! X hdma_usart3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;, u% a5 y }8 [$ p& o# J/ U hdma_usart3_rx.Init.PeriphInc = DMA_PINC_DISABLE;: f* z+ J" Y$ D4 T* m; a- E' f; ~ hdma_usart3_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_usart3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;3 K% n% Z7 l# p V0 d0 `& ^1 E hdma_usart3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;) e$ i) F0 R9 y* | hdma_usart3_rx.Init.Mode = DMA_NORMAL;0 {( ~1 T$ _2 G$ `7 A hdma_usart3_rx.Init.Priority = DMA_PRIORITY_LOW;- H6 [% l N) J* u5 @5 c hdma_usart3_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;6 ?$ u: M) }2 H HAL_DMA_Init(&hdma_usart3_rx);/ X) R3 {0 c6 v$ Q1 F, w1 Y+ h __HAL_LINKDMA(&huart3,hdmarx,hdma_usart3_rx);4 [- y: w" D. I$ z: M* i2 m5 | } & w$ f8 @+ V& T void USART3_IRQHandler()# ^0 I2 s7 `! y3 f# _% A {* T' x) z' ^! a3 a8 { int temp;8 M5 M" A* l! h) |" m if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE)) {( M! _8 ^/ Q: C6 z0 d __HAL_UART_CLEAR_IDLEFLAG(&huart3);8 h& P& e5 l- D! H0 h. Q HAL_UART_DMAStop(&huart3); temp=__HAL_DMA_GET_COUNTER(&hdma_usart3_rx);+ ^( O' s8 M3 |: |4 o$ g rx_len_uart3=BUFFERSIZE-temp; ) t; d" L1 k: V0 r# @# X! A/ N rx_flag_uart3=1;# h! z; u; K4 O! r+ D" C- A8 N2 o }+ ^3 d2 p% d1 i0 ? }# l. i( o* P$ Y! W' c6 P. W+ t! P 在主循环中加入以下语句以让单片机轮询串口不定长接收的标志位,并在液晶彩屏上显示出来: void UART_DMA_Get()" X$ |. C2 o0 r7 V1 C { int i; if(rx_flag_uart1==1)1 m0 s- V0 o0 [( U# P {7 _: M: Y1 X R2 w5 r printf("rx_len=%d\n\n",rx_len_uart1);/ j! t# x. @6 O% X" Z. o printf("%s\n\n",rx_buf_uart1); 7 J; Q; Z2 X0 {9 d& K rx_len_uart1=0; rx_flag_uart1=0;/ d" c! ]9 r# q }7 H/ m; C( N2 \1 m( l if(rx_flag_uart3==1)5 O) ?+ r2 E& b0 H6 q/ C7 { { printf("rx_len=%d\n\n",rx_len_uart3); for(i=0;i<rx_len_uart3;i++) {. j* F3 {9 E" n if(rx_buf_uart3=='\r'||rx_buf_uart3=='\n') rx_buf_uart3=0; }* x9 w! Q) a6 T9 N, q" l/ W& L2 G; \ printf("%s\n\n",rx_buf_uart3);& i9 @0 p( x; C SPILCD_DrawString(0,128," ",BLACK,CYAN,ZF32_NORMAL);3 e' F: a4 n8 L. C SPILCD_DrawString(0,128,rx_buf_uart3,BLACK,CYAN,ZF32_NORMAL);2 H# D, X0 D3 R6 P8 r1 b. H, f) o ' T3 U, b+ E- s" P% i0 K rx_len_uart3=0; rx_flag_uart3=0;* W; Q/ V1 p: X }5 `+ J: e7 h* F' H HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf_uart1,BUFFERSIZE);) M g' |. l! H HAL_UART_Receive_DMA(&huart3,(uint8_t*)rx_buf_uart3,BUFFERSIZE);7 y+ S( d- Y! j" z, G7 g1 D } 看看效果,可以看到,上位机串口程序那边发什么,显示屏就显示什么:9 q* g6 [1 F- O, M/ B9 ]2 {4 v
上传工程文件:' f" s Z* l$ u
STM32F407ZGT6.zip
(551.38 KB, 下载次数: 31)
|
微信公众号
手机版
不过可靠性可能需要认真考虑。
串口属于硬件层,数据链路层协议还是需要考虑的。* ?+ E4 j8 V; e* B
好东西,感谢分享!