DMA发送全部历史记录数据到串口

news2025/3/1 11:41:23

背景

博主参与的项目中,有个读取全部历史记录的功能,如果下位机在主程序中将全部历史记录单纯地通过串口传输会比较占用cpu资源,影响主程序中别的功能。最后商量得出以下实现方案:

定义两个发送缓冲区DMATxbuf1和DMATxbuf2,以及这两个发送缓冲区的标志DMATxbuf1Flag和DMATxbuf2Flag,发送缓冲区的标志取值有三种:可搬运、可发送和发送中。主main中负责搬运数据到这两个发送缓冲区中以及如果存在可发送的缓冲区且不存在发送中的缓冲区,那么主main开启一下这个DMA的通道使能。在DMA传输完成中断中,需要更新刚刚发送缓冲区的标志即可。

相关代码

初始化串口和DMA

void InitDebug_Sub(void)
{
//  stc_gpio_cfg_t stcGpioCfg;
//  stc_uart_cfg_t    stcCfg;
  /***********************RAM口初始化******************************************/
  Rxput_232 = 0;
  Rxget_232 = 0;
  Txput_232 = 0;
  Txget_232 = 0;
  TXing_232 = 0;
    /***********************GPIO口初始化**********************************************/      
  stc_gpio_cfg_t stcGpioCfg;
  DDL_ZERO_STRUCT(stcGpioCfg);
  Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio,TRUE);
   /***********TX************/
  stcGpioCfg.enDir =  GpioDirOut;
  Gpio_Init(GpioPortA,GpioPin0,&stcGpioCfg);
  Gpio_SetAfMode(GpioPortA,GpioPin0,GpioAf2); //配置PC10为LPUART1_TX
  /**********RX**************/
  stcGpioCfg.enDir =  GpioDirIn;
  Gpio_Init(GpioPortA,GpioPin1,&stcGpioCfg);
  Gpio_SetAfMode(GpioPortA,GpioPin1,GpioAf2); //配置PC11为LPUART1_RX
  /***********************串口初始化**********************************************/    
  stc_lpuart_cfg_t  stcCfg;
  DDL_ZERO_STRUCT(stcCfg);
  Sysctrl_SetPeripheralGate(SysctrlPeripheralLpUart1,TRUE);  //<外设模块时钟使能  
  stcCfg.enStopBit = LPUart1bit;                   ///<1停止位    
  stcCfg.enMmdorCk = LPUartEven;                   ///<偶校验
  stcCfg.stcBaud.enSclkSel = LPUartMskPclk;        ///<传输时钟源
  stcCfg.stcBaud.u32Sclk = Sysctrl_GetPClkFreq();  ///<PCLK获取
  stcCfg.stcBaud.enSclkDiv = LPUartMsk4Or8Div;     ///<采样分频
  stcCfg.stcBaud.u32Baud = 38400;                   ///<波特率
  stcCfg.enRunMode = LPUartMskMode1;               ///<工作模式1,异步模式全双工
  LPUart_Init(M0P_LPUART1, &stcCfg);
  /***********************DMA初始化**********************************************/    
  Sysctrl_SetPeripheralGate(SysctrlPeripheralDma,TRUE); //开启DMA外设时钟
  stc_dma_cfg_t dmaCfg;
  //CONFB配置
  dmaCfg.enMode = DmaMskBlock;  //Block传输
  dmaCfg.enTransferWidth = DmaMsk8Bit; // 传输宽度 8bit(适用于 UART)
  dmaCfg.enSrcAddrMode = DmaMskSrcAddrInc;  // 源地址递增
  dmaCfg.enDstAddrMode = DmaMskDstAddrFix;  // 目标地址固定
  dmaCfg.enSrcAddrReloadCtl = DmaMskSrcAddrReloadEnable;//使能源地址重载
  dmaCfg.enDestAddrReloadCtl = DmaMskDstAddrReloadEnable;//使能目标地址重载
  dmaCfg.enSrcBcTcReloadCtl = DmaMskBcTcReloadEnable; //使能BC/TC重载
  dmaCfg.enTransferMode = DmaMskOneTransfer;  // 单次传输模式
  //CONFA配置
  dmaCfg.u16BlockSize = 1;  // 每次传输 1 字节
  dmaCfg.u16TransferCnt = 131;  //实际传输的时候会设置长度
  dmaCfg.enRequestNum = DmaLpUart1TxTrig;                //触发源配置:LPUART1 SBUF为空
  
  dmaCfg.u32SrcAddress = (uint32_t)&DMATxbuf1.DATA[0];      // 源地址:发送缓冲区
  dmaCfg.u32DstAddress = (uint32_t)&M0P_LPUART1->SBUF; // 目标地址:LPUART1 发送寄存器
  dmaCfg.enPriority = DmaMskPriorityFix;  // 固定优先级
  
  Dma_InitChannel(DmaCh1, &dmaCfg);     //初始化DMA通道
  Dma_EnableChannelIrq(DmaCh1); //使能DMA传输完成中断
  NVIC_EnableIRQ(DMAC_IRQn);    //使能NVIC DMA中断
  sendingHistoryRemaingTime = 0; //初始化发送历史记录剩余时间为0
  carryHistoryFinishFlag = 1;   //初始化不需要搬运历史记录
  DMATxbuf1Flag = 1;            //初始化DMA缓冲区1可搬运
  DMATxbuf2Flag = 1;            //初始化DMA缓冲区2可搬运
  /**********************LPUART 中断使能*********************************/
  LPUart_ClrStatus(M0P_LPUART1,LPUartRC);          //<清接收中断请求
  LPUart_ClrStatus(M0P_LPUART1,LPUartTC);          //<清发送中断请求
  LPUart_EnableIrq(M0P_LPUART1,LPUartRxIrq);       //<使能接收中断
  LPUart_DisableIrq(M0P_LPUART1,LPUartTxIrq);      //发送完成之后关闭发送中断
  //  LPUart_EnableIrq(M0P_LPUART1,LPUartTxIrq);       //<不使能发送中断,
  EnableNvic(LPUART1_IRQn,IrqLevel3,TRUE);         //<系统中断使能
  LPUart_EnableFunc(M0P_LPUART1,LPUartDmaTxFunc);  //DMA发送LPUART使能
  P_RD_DBG = 0;//初始化调试口485方向为接收

收到获取全部指令时

  case 12:      //读取全部历史记录
    sendingHistoryRemaingTime = 2000; //设置发送超时时间,同时也标记着此时在传输历史及记录
    FlashReadSP = W25QX_RECSTART_ADDR; //初始化历史记录读取地址为开始地址
    //重置两个缓冲区状态为可搬运
    DMATxbuf1Flag = 1;
    DMATxbuf2Flag = 1;
    carryHistoryFinishFlag = 0;//重置历史记录没有搬运完
    break;

主main中的搬运、发送历史数据

  //发送历史记录期间且历史记录没有搬运完
  if(sendingHistoryRemaingTime)
  {
    //尝试搬运历史记录到缓冲区
    if(!carryHistoryFinishFlag)
    {
      carryHistory();
    }
    //尝试并触发DMA发送
    dmaSendHistoryRecord();
  }

搬运、发送历史数据方法详情

void carryHistory()
{
  typedef_DMATXBUF* dmaTxbuf;
  u8 carryBufType = 0; //记录搬的是哪个缓冲区
  //选择可搬运的缓冲区
  if(DMATxbuf1Flag == 1)
  {
    dmaTxbuf = &DMATxbuf1;
    carryBufType = 1;
  }
  else if(DMATxbuf2Flag == 1)
  {
    dmaTxbuf = &DMATxbuf2;
    carryBufType = 2;
  }else
  {
    //目前没有可搬运的
    return;
  }
  u8 count = 0;
  dmaTxbuf->LEN = 0;
  while(count < 3 && !carryHistoryFinishFlag)
  {
    u8 cmd2=0;
    u8 buf[300];
    structHistory_TypeDef* ptr = (structHistory_TypeDef*)buf;
    //历史记录已经读完了
    if(FlashReadSP>=W25QX_RECEND_ADDR)
    {
      cmd2 = 0x01;
      FlashReadSP = W25QX_RECSTART_ADDR;     //首条记录
      carryHistoryFinishFlag = 1;
    }
    W25QX_BufferRead((u8*)&ptr->HistoryData,FlashReadSP,sizeof(structEEP_GET_HISTOR_TypeDef)); //读取记录
    if(ptr->HistoryData.FLAG != 0x7e)
    {
      FlashReadSP += 4096;
      FlashReadSP &= 0xfffff000;
      W25QX_BufferRead((u8*)&ptr->HistoryData,FlashReadSP,sizeof(structEEP_GET_HISTOR_TypeDef)); //读取记录索引
      if(ptr->HistoryData.FLAG != 0x7e) 
      {
        cmd2 = 0x01;
	    FlashReadSP = W25QX_RECSTART_ADDR;     //首条记录
	    carryHistoryFinishFlag = 1;
      }
    }
    ptr->CMD1 = 0x8c;
    ptr->CMD2 = cmd2;
    ptr->ADDR = 0x01;
    ptr->SoftVer = SOFTVER;      
    ptr->checksum = CRC16(buf,sizeof(structHistory_TypeDef)-2);
    dmaTxbuf->DATA[dmaTxbuf->LEN++] = 0x10;
    dmaTxbuf->DATA[dmaTxbuf->LEN++] = 0x02;
    for(u16 i = 0; i < sizeof(structHistory_TypeDef); i++)
    {
      u8 qq = buf[i];
      dmaTxbuf->DATA[dmaTxbuf->LEN++] = qq;
      /* -----0x10加发一个 -----*/
      if(qq == DLE) dmaTxbuf->DATA[dmaTxbuf->LEN++] = DLE;
    }
    dmaTxbuf->DATA[dmaTxbuf->LEN++] = DLE;
    dmaTxbuf->DATA[dmaTxbuf->LEN++] = ETX;
    dmaTxbuf->DATA[dmaTxbuf->LEN++] = 0x16; 
    count++;
    FlashReadSP += 128;
  }
  //说明搬运了一些历史记录,需要发送,更新标记
  if(count > 0)
  {
    //更新缓冲区1的标志
    if(carryBufType == 1)
    {
      DMATxbuf1Flag = 2;//缓冲区1可发送
    }
    if(carryBufType == 2)
    {
      DMATxbuf2Flag = 2;//缓冲区2可发送
    }
    //说明还有数据待发送,更新过期时间
    sendingHistoryRemaingTime = 2000;
  }
}

void dmaSendHistoryRecord()
{
  //存在发送中的数据
  if(DMATxbuf1Flag == 3 || DMATxbuf2Flag == 3)
  {
    return;
  }
  if(DMATxbuf1Flag == 2)
  {
    UART_DMA_Send_History(1);
  }else if(DMATxbuf2Flag == 2)
  {
    UART_DMA_Send_History(2);
  }
}

DMA发送中断处理函数

void DMAC_IRQHandler(void)
{
#if (INT_CALLBACK_ON == INT_CALLBACK_DMAC)    
    Dmac_IRQHandler();
#endif 
        // DMA 中断处理代码
    if (M0P_DMAC->CONFB1_f.STAT == 5)  // 传输完成标志101
    {
        // 清除传输完成标志
        M0P_DMAC->CONFB1_f.STAT = 0;  
        //更新缓冲区标志
        if(DMATxbuf1Flag == 3)
        {
          DMATxbuf1Flag = 1;
        }else if (DMATxbuf2Flag == 3)
        {
          DMATxbuf2Flag = 1;
        }
    }
    M0P_DMAC->CONFB1_f.STAT = 0; 
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2307871.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基因型—环境两向表数据分析——品种生态区划分

参考资料&#xff1a;农作物品种试验数据管理与分析 用于品种生态区划分的GGE双标图有两种功能图&#xff1a;试点向量功能图和“谁赢在哪里”功能图。双标图的具体模型基于SD定标和h加权和试点中心化的数据。本例中籽粒产量的GGE双标图仅解释了G和GE总变异的53.6%&#xff0c;…

电路中如何计算电容容值大小

一个例题&#xff1a; 【电路中电容容值是怎么算出来的&#xff1f;】https://www.bilibili.com/video/BV1RQ4y1c7i1?vd_source3cc3c07b09206097d0d8b0aefdf07958

GPT大语言模型与搜索引擎:技术本质与应用场景的深度解析

引言 在人工智能和自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;GPT&#xff08;Generative Pre-trained Transformer&#xff09;大语言模型和搜索引擎是两个备受关注的技术。尽管它们都涉及到信息检索和生成&#xff0c;但它们在技术原理、应用场景和用户体验上…

FreeRTOS-中断管理

实验目的 创建一个队列及一个任务&#xff0c;按下按键 KEY1 触发中断&#xff0c;在中断服务函数里向队列里发送数据&#xff0c;任务则阻塞接 收队列数据。 实验代码 实验结果 这样就实现了&#xff0c;使用中断往队列的发送信息&#xff0c;用任务阻塞接收信息

计算机毕业设计SpringBoot+Vue.js音乐网站(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

更换k8s容器运行时环境为docker

更换k8s容器运行时环境为docker k8s-V1.24之后容器运行时默认是containerd&#xff0c;若想改为熟悉的docker作为运行时&#xff0c;需要做以下操作 在每个节点安装containerd、docker; 每个节点安装cri-docker&#xff1b; 调整kubelet配置并重启验证。 1.安装docker、con…

知识图谱-资源网

知识图谱-资源网 http://openkg.cn/datasets-type/https://www.ownthink.com/knowledge.html

小程序Three Dof识别 实现景区AR体验

代码工程 GitCode - 全球开发者的开源社区,开源代码托管平台 dof

2020 年英语(一)考研真题 笔记(更新中)

Section I Use of English&#xff08;完型填空&#xff09; 原题 Directions&#xff1a;Read the following text. Choose the best word (s) for each numbered blank and mark A, B, C or D on the ANSWER SHEET. (10 points) Even if families are less likely to si…

YOLO11改进加入ResNet网络

文章目录 1.改进目的2.demo引入2.1代码2.2 结果展示2.3 BottleNeck详解 1.改进目的 原始YOLO11模型训练好以后&#xff0c;检测结果mAP结果很低&#xff0c;视频检测结果很差&#xff0c;于是想到改进网络&#xff0c;这里介绍改进主干网络。 2.demo引入 2.1代码 # File: 2…

硬编码(三)经典变长指令一

我们在前两节的硬编码中学习了定长指令&#xff0c;接下来学习变长指令 对于定长指令&#xff0c;我们通过opcode便可知该指令的长度&#xff0c;但是对于变长指令却是不可知的。变长指令长度由opcode&#xff0c;ModR/M&#xff0c;SIB共同决定。变长指令通常在需要操作内存的…

在线VS离线TTS(语音合成芯片)有哪些优势-AIOT智能语音产品方案

离线 TTS 存在语音质量欠佳、音色选择有限、语言支持单一更新困难、占用资源多、适应性差、难以个性化定制等痛点 01更新维护困难 由于是离线模式&#xff0c;难以及时获取最新的语音数据和算法更新&#xff0c;无法得到持续改进。 02占用本地资源 需要在设备本地存储较大的…

【Spring Boot】掌握 Spring 事务:隔离级别与传播机制解读与应用

前言 ???本期讲解关于spring 事务传播机制介绍~~~ ??感兴趣的小伙伴看一看小编主页&#xff1a;-CSDN博客 ?? 你的点赞就是小编不断更新的最大动力 ??那么废话不多说直接开整吧~~ 目录 ???1.事务的隔离级别 ??1.1MySQL事务隔离级别 ??1.2Spring事务隔离…

PySide(PyQT)重新定义contextMenuEvent()实现鼠标右键弹出菜单

在 PySide中&#xff0c;contextMenuEvent() 是 QWidget 类&#xff08;以及继承自它的所有子类&#xff09;的一个事件处理方法&#xff0c;主要用于处理上下文菜单事件&#xff0c;也就是当用户在控件上右键点击时触发的事件。 • 通过重新定义contextMenuEvent()来实现自定…

Redis 持久化方式:RDB(Redis Database)和 AOF(Append Only File)

本部分内容是关于博主在学习 Redis 时关于持久化部分的记录&#xff0c;介绍了 RDB 和 AOF 两种持久化方式&#xff0c;详细介绍了持久化的原理、配置、使用方式、优缺点和使用场景。并对两种持久化方式做了对比。文章最后介绍了 Redis 持久化的意义并与其他常见的缓存技术做了…

数据库测试

TPCH 22条SQL语句分析 - xibuhaohao - 博客园 TPCH模型规范、测试说明及22条语句 - zhjh256 - 博客园 TPC-DS 性能比较&#xff1a;TiDB 与 Impala-PingCAP | 平凯星辰 揭秘Oracle TPC-H性能优化&#xff1a;如何提升数据库查询速度&#xff0c;揭秘实战技巧与挑战 引言 T…

< 自用文儿 > Gobuster 暴力扫描工具与 SecLists 安全测试词表集合

Ethice 道德问题 GFW 的保护下&#xff0c;很多的设备操作系统是停留在更老的版本&#xff0c;应用软件也是&#xff0c;因此很多的漏洞没有被修复。通讯没有使用加密&#xff0c;例如网页没有使用 HTTPS 网站很多。几乎是半裸的在网络上等着被食。 不做恶是下限。 环境&…

关于网页地图的坐标系

EPSG:4326地理坐标系 和 EPSG:3857Web 墨卡托投影 EPSG:4326 定义&#xff1a;EPSG:4326 是基于 WGS84 椭球的地理坐标系&#xff0c;使用经度&#xff08;Longitude&#xff09;和纬度&#xff08;Latitude&#xff09;表示地球上的位置。特点&#xff1a; 经度范围为 -180 …

华为云之使用鲲鹏弹性云服务器部署Node.js环境【玩转华为云】

华为云之使用鲲鹏弹性云服务器部署Node.js环境【玩转华为云】 一、本次实践介绍1.1 实践环境简介1.3 本次实践完成目标 二、 相关服务介绍2.1 华为云ECS云服务器介绍2.2 Node.js介绍 三、环境准备工作3.1 预置实验环境3.2 查看预置环境信息 四、登录华为云4.1 登录华为云4.2 查…

C语言整体梳理-基础篇-结构体

结构体详解 1.1结构体是什么&#xff1f; 结构体是一些值的集合&#xff0c;这些值成为成员变量&#xff0c;结构体的每个成员可以是不同类型的变量。 数组是相同类型的元素组成的集合&#xff0c;结构体可以是不同类型元素组成的集合。 1.2结构体的声明 1.2.1常规声明 s…