你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【屋脊雀F407开发板】串口空闲中断+DMA实现不定长接收&处理

[复制链接]
donatello1996 发布时间:2019-1-7 09:38
    串口不定长接收在项目中经常用到,像MODBUS串口服务器中就会经常传来一大帧数据,数据的长度不定,通常人们的做法是采用环形队列接收的方式,以特定的结束标志位如0x0d 0x0a等用以判断一帧数据的结束,但实际上STM32这种高级的ARM单片机是带有空闲中断和DMA的,不需要用环形队列这种纯软件的做法实现不定长接收,直接用空闲中断+DMA就可以实现了,非常简单,小白也能一看就懂。
    首先是要初始化串口不定长接收,比如打开空闲中断,打开DMA,这里我选的是板上的USB转TTL串口即串口3,对应的接收DMA为DMA1流1:
12.jpg 13.jpg
代码如下:


void UART3_Init(int baud)
{
        __HAL_RCC_GPIOB_CLK_ENABLE();
        __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;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF7_USART3;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
       
  huart3.Instance = USART3;
  huart3.Init.BaudRate = baud;
  huart3.Init.WordLength = UART_WORDLENGTH_8B;
  huart3.Init.StopBits = UART_STOPBITS_1;
  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;
  HAL_UART_Init(&huart3);
        __HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);
        HAL_NVIC_SetPriority(USART3_IRQn,0,0);
  HAL_NVIC_EnableIRQ(USART3_IRQn);
       
        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;
  hdma_usart3_rx.Init.MemInc = DMA_MINC_ENABLE;
  hdma_usart3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  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;
  HAL_DMA_Init(&hdma_usart3_rx);
       
  __HAL_LINKDMA(&huart3,hdmarx,hdma_usart3_rx);
}


void USART3_IRQHandler()
{
        int temp;
        if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE))
        {
                __HAL_UART_CLEAR_IDLEFLAG(&huart3);
                HAL_UART_DMAStop(&huart3);
                temp=__HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
                rx_len_uart3=BUFFERSIZE-temp;
                rx_flag_uart3=1;
        }
}
在主循环中加入以下语句以让单片机轮询串口不定长接收的标志位,并在液晶彩屏上显示出来:
void UART_DMA_Get()
{
        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;
                rx_flag_uart1=0;
        }
        if(rx_flag_uart3==1)
        {
                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')
                                rx_buf_uart3=0;


                }
                printf("%s\n\n",rx_buf_uart3);
                SPILCD_DrawString(0,128,"                              ",BLACK,CYAN,ZF32_NORMAL);
                SPILCD_DrawString(0,128,rx_buf_uart3,BLACK,CYAN,ZF32_NORMAL);


                rx_len_uart3=0;
                rx_flag_uart3=0;
        }
        HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf_uart1,BUFFERSIZE);
        HAL_UART_Receive_DMA(&huart3,(uint8_t*)rx_buf_uart3,BUFFERSIZE);
}


看看效果,可以看到,上位机串口程序那边发什么,显示屏就显示什么:
14.jpg 15.jpg
IMG_20190106_183604.jpg IMG_20190106_183620.jpg IMG_20190106_183643.jpg
上传工程文件:
STM32F407ZGT6.zip (551.38 KB, 下载次数: 28)
收藏 2 评论6 发布时间:2019-1-7 09:38

举报

6个回答
Kevin_G 回答时间:2019-1-7 10:04:17
好文章!
STMCU-管管 回答时间:2019-1-7 10:39:15
支持支持
杨满意 回答时间:2019-1-7 13:07:33
有些外设模块不能用,会存在断帧问题
wujique 回答时间:2019-1-9 14:50:33
空闲中断是个思路,
不过可靠性可能需要认真考虑。
串口属于硬件层,数据链路层协议还是需要考虑的。
david69chen 回答时间:2019-2-24 15:36:53
好东西,感谢分享!
q790198803 回答时间:2020-10-31 23:19:16

好东西,感谢分享!

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版