当前位置: 首页 > article >正文

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

背景

博主参与的项目中,有个读取全部历史记录的功能,如果下位机在主程序中将全部历史记录单纯地通过串口传输会比较占用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.kler.cn/a/566220.html

相关文章:

  • Spring全家桶简介
  • Blender调整最佳渲染清晰度
  • 如何配置虚拟机的IP上网
  • 纯代码实战--用Deepseek+SQLite+Ollama搭建数据库助手
  • yolo位姿估计实验
  • idea不同的项目使用不同的java版本
  • Linux操作系统5-进程信号2(信号的4种产生方式,signal系统调用)
  • 【Linux网络编程】五种IO模型 多路转接(select)
  • Windows文件资源管理器左侧导航窗格没有WSL的Linux图标的解决方法
  • Python 课堂点名桌面小程序
  • C++11:工厂方法模式
  • Java中的ArrayDeque
  • AF3 DataPipeline类process_multiseq_fasta 方法解读
  • 代理服务器与内网穿透/打洞
  • MVCC,MySQL中常见的锁
  • 数据库基础二(数据库安装配置)
  • 汽车开放系统架构(AUTOSAR)中运行时环境(RTE)生成过程剖析
  • AI智能体与大语言模型:重塑SaaS系统的未来航向
  • 在 IntelliJ IDEA 中启动多个注册到 Nacos 的服务
  • 基因型—环境两向表数据分析——品种生态区划分