串口不定长接收在项目中经常用到,像MODBUS串口服务器中就会经常传来一大帧数据,数据的长度不定,通常人们的做法是采用环形队列接收的方式,以特定的结束标志位如0x0d 0x0a等用以判断一帧数据的结束,但实际上STM32这种高级的ARM单片机是带有空闲中断和DMA的,不需要用环形队列这种纯软件的做法实现不定长接收,直接用空闲中断+DMA就可以实现了,非常简单,小白也能一看就懂。 首先是要初始化串口不定长接收,比如打开空闲中断,打开DMA,这里我选的是板上的USB转TTL串口即串口3,对应的接收DMA为DMA1流1:* l$ `& S4 D5 M- d+ F 代码如下:* G9 d- F4 g1 f, @, W' L 4 I+ ?. X7 u/ D% a void UART3_Init(int baud) {+ K* ?1 X& J6 p5 | __HAL_RCC_GPIOB_CLK_ENABLE();2 K0 a5 s0 ?6 ^( M __HAL_RCC_USART3_CLK_ENABLE(); __HAL_RCC_DMA1_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;: M* A5 ]) d! k* L4 u GPIO_InitStruct.Pull = GPIO_PULLUP;. F. F2 s1 B! Z3 W: u GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF7_USART3; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);: @; b7 h5 r% x; A* { & ~" M2 x3 z$ Q$ K* J6 w6 f huart3.Instance = USART3;0 H8 U6 `, t/ y9 n' Y* D huart3.Init.BaudRate = baud; huart3.Init.WordLength = UART_WORDLENGTH_8B;2 {: _) ?$ S9 y$ c huart3.Init.StopBits = UART_STOPBITS_1;+ Q6 R' O; @3 b& y huart3.Init.Parity = UART_PARITY_NONE;: q, {: w" a+ F8 x- B huart3.Init.Mode = UART_MODE_TX_RX; huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;4 z! y$ G4 p2 l; c- w( [ huart3.Init.OverSampling = UART_OVERSAMPLING_16;6 O# v1 }# k1 O8 h3 a; L# [ HAL_UART_Init(&huart3); __HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);! o5 W. G2 h+ J5 o% a4 R HAL_NVIC_SetPriority(USART3_IRQn,0,0); HAL_NVIC_EnableIRQ(USART3_IRQn);+ {' r7 v, K p" i. E. X ? 9 w% }7 t* U% O hdma_usart3_rx.Instance = DMA1_Stream1;2 |6 I: m! W# W; S' C 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; hdma_usart3_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_usart3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;' q$ m! r! F) d4 I4 E' `8 t' _5 m3 Q hdma_usart3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_usart3_rx.Init.Mode = DMA_NORMAL; hdma_usart3_rx.Init.Priority = DMA_PRIORITY_LOW; hdma_usart3_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;+ ]9 }* a6 u: A2 S* ^/ S) {3 @3 `( T HAL_DMA_Init(&hdma_usart3_rx); __HAL_LINKDMA(&huart3,hdmarx,hdma_usart3_rx); }! f) J* T* p' m3 y 3 i- l; p3 t* B 2 @' V, `) b* U( [. Z7 ^ void USART3_IRQHandler() E/ ]" E& n' a {6 T& t1 j* |, c. e int temp; if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE)) { __HAL_UART_CLEAR_IDLEFLAG(&huart3);: I0 O! D5 v- _, |3 L; H2 H/ o HAL_UART_DMAStop(&huart3); temp=__HAL_DMA_GET_COUNTER(&hdma_usart3_rx);- G: m% U% @4 r- T1 W3 s* L1 X- B rx_len_uart3=BUFFERSIZE-temp; rx_flag_uart3=1;: K% `. n; k/ s% ?4 d3 L; p }& K# t$ h3 z1 e4 P" {6 R. d2 M }4 W1 p+ m8 w# Y5 c 在主循环中加入以下语句以让单片机轮询串口不定长接收的标志位,并在液晶彩屏上显示出来: void UART_DMA_Get() { int i;# ?4 ^5 d& E! P1 _/ W if(rx_flag_uart1==1) { printf("rx_len=%d\n\n",rx_len_uart1);: A. M6 c2 _, U! _. L* x printf("%s\n\n",rx_buf_uart1); , {0 V4 Q3 P2 t; Y6 h2 ? rx_len_uart1=0;8 k M2 a/ x( b/ r9 C rx_flag_uart1=0;2 n. p2 R: x; l. y! \3 d. l' Y } if(rx_flag_uart3==1) {3 p- `5 h4 d. q! ~) `' H printf("rx_len=%d\n\n",rx_len_uart3); for(i=0;i<rx_len_uart3;i++) { if(rx_buf_uart3=='\r'||rx_buf_uart3=='\n')* _/ U, y5 o, d3 I; o. U1 P* u rx_buf_uart3=0; 1 P* W3 X" V9 ]! H1 V q' D, v) Y } printf("%s\n\n",rx_buf_uart3); SPILCD_DrawString(0,128," ",BLACK,CYAN,ZF32_NORMAL);3 o1 z8 t# {& Q0 P SPILCD_DrawString(0,128,rx_buf_uart3,BLACK,CYAN,ZF32_NORMAL); 3 [% i3 n! j! i * n9 m y+ Y1 z& {( \( v/ l! F rx_len_uart3=0; rx_flag_uart3=0; } HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf_uart1,BUFFERSIZE);2 |' K S" s( O# y8 l5 @ HAL_UART_Receive_DMA(&huart3,(uint8_t*)rx_buf_uart3,BUFFERSIZE);# o1 B6 S! l' r' A, R/ k }, G- Z, ]2 I8 u0 K& M: I 3 L7 t# m4 M) v' T9 ?* ? 看看效果,可以看到,上位机串口程序那边发什么,显示屏就显示什么:$ g- h1 A, ]. t8 x8 ~& b 上传工程文件: |
不过可靠性可能需要认真考虑。
串口属于硬件层,数据链路层协议还是需要考虑的。
好东西,感谢分享!