stm32串口dma加空闲中断 实现fifo接收数据 串口高效收发思路

2023-05-16

我做这个串口数据接收 dma+空闲中断 加fifo  实现串口的高效收发  ,主要是串口接收的数据长度不定长,时间超时也不好做,还要串口收发的效率要高,采用串口数据的接收 dma+空闲中断+fifo的方式  速度快和效率高,占用cpu的时间短

对比了其他几种方式

1:采用串口中断的话,每接收1byte就得中断一次。这样太消耗CPU资源! 频繁进中断,占用中断,特别是对时间和时序要求比较严格的时候 串口频繁进入中断导致其他中断时序有影响

 

2:采用DMA方式接收数据,接收的数据长度必须是固定的  对于接收数据长度不固定就不怎么好弄了,特别像gprs通信,接收长度不固定,这些都是困扰我

 

3:采用dma方式接收数据+定时器超时中断,这样来确定一帧数据完成,需要开关定时器,操作比较复杂,超时时间还不太好设置,stm32f1和f4 没有超时中断还只能采用定时器或者把rxd引脚接到stm32定时器触发引脚上来实现超时, 

4:stm32串口dma方式接收数据+空闲中断或者超时中断+fifo 这种分内事来实现不定长的数据和高效的串口数据接收   效率比其他的方式要快,消耗cpu的时间比较少,这样应用可以做数据超时

所以我采用了stm32串口dma方式接收数据+空闲中断或者超时中断+fifo方式来实现

像stm32f103和stm32f407芯片没有时间超时中断  需要定时器来做超时 比较麻烦,nxp的部分芯片串口有超时中断,atmel的部分芯片串口有超时中断,stm32的h7和f7系列才有超时中断

要实现stm32串口dma方式接收数据+空闲中断或者超时中断+fifo方式来实现

第一步 stm32的串口dma配置 ,串口的初始化,还有串口的空闲中断

1,通过stm32的cubemx软件来生成串口+dma配置的初始化

cubemx生成代码都是HAL库的模式,我就以stm32f4的HAL库的方式实现

UART_HandleTypeDef   huart1={0}; 

huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
  }

dma的配置可以通过cubemx软件串口+dma方式来生成代码

  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();
  
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART1 GPIO Configuration    
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USART1 DMA Init */
    /* USART1_RX Init */
    hdma_usart1_rx.Instance = DMA2_Stream2;
    hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4;
    hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_rx.Init.Mode = DMA_CIRCULAR;
    hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(huart,hdmarx,hdma_usart1_rx);

    /* USART1 interrupt Init */
    HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);

2,stm32生成的代码是不支持空闲中断的,需要自己增加空闲中断和空闲中断的处理

    __HAL_UART_ENABLE_IT(uart->h, UART_IT_IDLE);//使能空闲中断HAL代码
    __HAL_UART_CLEAR_IDLEFLAG(uart->h);//使能空闲中断HAL代码

串口中断的处理

void USART1_IRQHandler(void)
{ 

 HAL_UART_IRQHandler(&huart1); 

if((__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) ? SET : RESET) == SET)

{

//串口空闲中断数据处理 

}

}

第二步 了解环形fifo的buff特点,通过分析环形buff的特点,其实串口的dma接收 dma模式设置循环模式 就是环形buff

环形buff的说明讲网站:https://blog.csdn.net/jiejiemcu/article/details/80563422

转载环形fifo说明出处:    STM32进阶之串口环形缓冲区实现

以下内容是引用了原文的内容 

转载原文链接:https://blog.csdn.net/jiejiemcu/article/details/80563422 

 

实现的原理:初始化的时候,列队头与列队尾都指向0,当有数据存储的时候,数据存储在‘0’的地址空间,列队尾指向下一个可以存储数据的地方‘1’,再有数据来的时候,存储数据到地址‘1’,然后队列尾指向下一个地址‘2’。当数据要进行处理的时候,肯定是先处理‘0’空间的数据,也就是列队头的数据,处理完了数据,‘0’地址空间的数据进行释放掉,列队头指向下一个可以处理数据的地址‘1’。从而实现整个环形缓冲区的数据读写。

  看图,队列头就是指向已经存储的数据,并且这个数据是待处理的。下一个CPU处理的数据就是1;而队列尾则指向可以进行写数据的地址。当1处理了,就会把1释放掉。并且把队列头指向2。当写入了一个数据6,那么队列尾的指针就会指向下一个可以写的地址。

如果你懂了环形队列,那就跟着一步步用代码实现吧:

是不是跟dma的circular的模式跟这个环形队列非常相似呢

第3步就开始写串口dma+空闲中断或超时中断+fifo的代码啦

重要的代码 串口dma的fifo的数据长度的处理

//假设dma的接收缓存大小为512       环形buff的位置为 500    
//接收到数据长度为48 这个时候的环形buff的位置为548                        
// 548-500=48      但是dma环形buff的特性,
//dma实际的长度为36 这个时候如何计算长度            
// 这个算法如512-500 12      548-512       36
rev_dma_lens =  DMA接收的数据长度
//buff_index-----串口接收和app的buff的序列号
//环形数组
if (puart->ndtr_last != rev_dma_lens) //上次与这次不同,表示有新数据
{

        start_addr = start_addr + len;//环形数据地址偏移量
        //
        if (start_addr >= 512)
        {
            puart->start_addr = start_addr - 512;
        }
        //
        if (rev_dma_lens > ndtr_last)
        {
            len = rev_dma_lens - ndtr_last; //接收数据长度=上次长度-这次长度
        }
        else
        {
            len =  512 - ndtr_last + rev_dma_lens; //环形数据到头后,总数-这次剩余+上次剩余
        }
        //

       ndtr_last = rev_dma_lens;

}

但是这种方式会2个问题:

1,串口一直有数据接收 不产生空闲中断 这个时候会有问题  解决的方法采用超时方法来解决  或者采用一个一个的数据处理方式采用每来一个数据产生一个中断方式

2, 一个数据包超过dma的最大buff长度  已经越界 导致了部分数据被覆盖啦   解决方法就是把DMA的接收buff定义更大一点 最好定义为2倍的一个最大数据包 考虑资源也可以考虑1.5或1倍的一个最大数据包 或者采用一个一个的数据处理方式  采用每来一个数据产生一个中断方式

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

stm32串口dma加空闲中断 实现fifo接收数据 串口高效收发思路 的相关文章

  • URL格式

    一 URL基本格式 一个完整的url包含方案 用户名 密码 主机名 端口 路径 参数 查询和片段 xff0c 格式如下 xff1a lt scheme gt lt user gt lt password gt 64 lt host gt l
  • __IO uint16_t

    STM32里的类型定义 xff0c 见如下说明 xff1a typedef volatile unsigned short vu16 typedef IO uint16 t vu16 IO definitions access restri
  • 串口波形分析(TTL,RS232,RS485)

    TTL xff0c RS232 xff0c RS485波形分析 本文转自 xff1a http blog 163 com qiu zhi2008 blog static 60140977201092651854445 http www cn
  • Java数字类型转byte数组

    文章目录 方法1 自己写int转byte数组byte数组转int参考 xff1a https blog csdn net qq 41054313 article details 88424454 方法2 使用java NIO包的功能int转
  • 头文件和库函数的区别

    1 头文件中有函数的申明 xff0c 库文件实现函数的定义 比如 xff0c printf函数 使用时应包括stdio h xff0c 打开stdio h你只能看到 xff0c printf这 个函数的申明 却看不到printf具体是怎么实
  • C语言--字符串的截取

    今天碰到了一个字符串截取的功能实现问题 xff0c 比较常见所以就做下记录 一般的实现是这样的 xff1a include lt stdio h gt include lt string h gt int main void char de
  • 使用 JWT 让你的 RESTful API 更安全

    传统的 cookie session 机制可以保证的接口安全 xff0c 在没有通过认证的情况下会跳转至登入界面或者调用失败 在如今 RESTful 化的 API 接口下 xff0c cookie session 已经不能很好发挥其余热保护
  • CAN报文解析—案例

    1 CAN报文定义 CAN报文是指发送单元向接受单元传送数据的帧 我们通常所说的CAN报文是指在CAN线 xff08 内部CAN 整车CAN 充电CAN xff09 上利用ECU和CAN卡接收到的十六进制报文 2 CAN协议中CAN报文种类
  • 单片机中,intrins.h头文件中各函数详解:空指令_nop_(),移位函数_crol_、_cror_

    intrins h 在 C51单片机编程中 xff0c 我们经常使用到 nop 延时一个机器周期 如果晶振是12M xff0c 则延时1 us xff0c 该空函数在头文件intrins h中 头文件 INTRINS H 中的函数使用很方便
  • Linux 下模拟Http 的get or post请求(curl和wget两种方法)

    一 get请求 xff1a 1 使用curl命令 xff1a curl 34 http www baidu com 34 如果这里的URL指向的是一个文件或者一幅图都可以直接下载到本地 curl i 34 http www baidu co
  • QT入门基础认知(三个常用类、三种对话框类型、信号和槽)

    1 简单介绍 xff1a 1 1 三个常用类 xff08 Qwidget类 QDialog类 QMainwindow类 xff09 Qwidget类 xff1a 继承与QObject类和QPaintdevice类 xff0c 所有用户界面对
  • socket函数的domain、type、protocol解析

    socket 函数的 domain type protocol 解析 lxg 64 2015 04 09 内核中的 socket 概览 图一 xff1a socket 概览 内核中套接字是一层一层进行抽象展示的 xff0c 把共性的东西抽取
  • code的用法

    今天写程序的时候用了const xff0c 想到之前遇到的code的用法 xff0c 那是第一次遇到code的那样的用法 xff0c 查了一下 xff0c 解释如下 xff1a 在单片机使用C语言进行编程的时候 xff0c 经常使用到cod
  • Linux的system()和popen()差异

    Linux的system 和popen 差异 1 system 和popen 简介 在linux中我们可以通过system 来执行一个shell命令 xff0c popen 也是执行shell命令并且通过管道和shell命令进行通信 sys
  • HTTP的303、307状态码

    之前在 http权威指南 中看到了HTTP的307状态码 xff0c 当时因为没有找到可以实验的网站所以没有比较深的印象 xff0c 今天在排查一个问题的时候恰巧遇到了HTTP 1 1 307 TemporaryRedirect xff0c
  • 如果获得页面跳转的最终URL

    最近做一个小功能 xff0c 就是获取一个页面经过跳转后的最终页面URL xff0c 这里的跳转方式包含但不仅限于HTTP 301 302 js meta refresh 下面是我想到的三种可能的解决方式 xff0c 可能会有更优的方法 x
  • pykafka的NoBrokersAvailableError原因

    今天在使用pykafka的时候遇到一个问题 xff0c 我的kafka和zookeeper运行在一台机器上面 xff0c 然后应用程序跑在另外一台机器上面 当我调用pykafka中的KafkaClient zookeeper host 61
  • pyspark的pickle.PicklingError

    今天在用pyspark的时候在一个类中调用rdd的map的时候报错 xff0c 代码如下 xff1a rdd 61 df filter size df emission gt span class hljs number 50 span r
  • 一行代码引发的"血案"

    昨天在使用pykafka的时候又遇到了之前我遇到过的PartitionOwnedError ConsumerStoppedException异常 xff0c 关于这个异常我之前写过一篇分析的文章 链接在这里 xff0c 我自认为之前应该是把
  • X-Pack的machine learning

    如何使用X Pack的machine learning 最近在使用X Pack中的机器学习功能 xff0c 主要的就是利用非监督的时间序列模型 xff0c 用来检测流量的变化 xff0c 关于X Pack这块的文章好像可以参考的比较少 xf

随机推荐