STM32定时器触发DMA循环完成数据搬运

      通过TIM8的事件触发DMA,从内存中的地址搬运数据到外设的寄存器,例子中的中断部分可以关闭,与功能无关,仅为测试时观察方便。 定时器每产生一次事件(本文以UPDATE为例,CC等其他事件也可实现),DMA被启动一次,搬运预设的若干个数据到指定位置。

先看下下面两个DMA通道表,TIM8的UPDATE对应的DMA通道为DMA2-->stream1-->channel7,通道一定选择对,否则神仙也没辙!!!,实际使用中要注意!

/* 
  程序实现内容:通过定时器触发DMA,实现DMA传输的可控性
   
  程序通过 定时器8 触发 DMA2_Stream1开启,DMA2_Stream1从内存到外设传递数据,每次传递数据的个数为5
  TIM8 每触发一次, DMA2_Stream1 开启一次传输,TIM8 触发100次后,关闭TIM8,清空 外设寄存器。
   
  主函数中循环打印外设寄存器的值,100次触发之内,外设寄存器的数据一直在更新,
  关闭定时器后,外设寄存器中的数据为0,表示TI8关闭后,DMA不再被触发。
   
  注:本测试总TIM8开启的了中断,用来观察和测试使用,实际应用中可以不开中断,TIM8同样会触发启动DMA.
  */
  unsigned int timer8_times_count = 0; // 计数用变量
  unsigned int config_CR[5] = {0x06030440,0x06030446,0x06030442,0x06030448,0x06030444}; // 外部数据
  /*
  通过 DMA2_Stream1_DMA_Channel_7 向 DMA2_Stream3的 CR 寄存器中搬运数据
  注:数据为测试数据,无实际意义,仅用于测试功能
  */
  void DMA_Auto_Config_CR(void)
  {
  

             DMA_InitTypeDef DMA_InitStructure;

  
               RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1|RCC_AHB1Periph_DMA2,ENABLE);
               DMA_DeInit(DMA2_Stream1);
               while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE){}                                     // 等待DMA可配置
   
               DMA_InitStructure.DMA_Channel = DMA_Channel_7;                                              // 通道选择
               DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&DMA2_Stream3->CR); // DMA外设地址(SOURCE ADDR)
               DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)(config_CR);                      // DMA 存储器0地址
               DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;                             // 内存到外设
               DMA_InitStructure.DMA_BufferSize = 5;                                                                   // 数据传输量
               DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                   // 外设增量模式
               DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                          // 存储器非增量模式
               DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;  // 外设数据长度:16位
               DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;        // 存储器数据长度:16位
               DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 必须使用循环模式,否则无法实现循环触发启动
               DMA_InitStructure.DMA_Priority = DMA_Priority_High;                                          // 优先级
               DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
   
               DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
               DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; // 存储器突发单次传输
               DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; // 外设突发单次传输
               DMA_Init(DMA2_Stream1, &DMA_InitStructure);
  }
  void hf_timer8_init(void)
  {
              GPIO_InitTypeDef GPIO_InitStructure;
              TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
              TIM_OCInitTypeDef TIM_OCInitStructure;
              NVIC_InitTypeDef NVIC_InitStructure;
   
              RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
   
             TIM_TimeBaseStructure.TIM_Period = 100-1;
             TIM_TimeBaseStructure.TIM_Prescaler = 60000-1;
             TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
             TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
             TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure);
   
            NVIC_InitStructure.NVIC_IRQChannel = TIM8_UP_TIM13_IRQn;       // 为方便观察,配置TIM8 UPDATE 中断
            NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
            NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
            NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
            NVIC_Init(&NVIC_InitStructure);
   
           TIM_ClearFlag(TIM8,TIM_FLAG_Update);
           TIM_ITConfig(TIM8, TIM_IT_Update, ENABLE);                 // 开中断便于观察
           TIM_SelectOutputTrigger(TIM8, TIM_TRGOSource_Update);
   
           TIM_DMACmd(TIM8,TIM_DMA_Update,ENABLE);            // 开启 TIM8 UPDATE 触发 DMA ,即 DMA2_Stream1
           TIM_Cmd(TIM8,ENABLE); // 开启 TIM8
           DMA_Cmd(DMA2_Stream1, ENABLE); // 开启 DMA
  }
  

/*

    每产生一次UPDATE事件,在中断中计数一次,达到100次时,关闭定时器(关闭定时器后,DMA将不再被触发),同时清空目的寄存器。

*/

  void TIM8_UP_TIM13_IRQHandler(void) // TIM8 UP 溢出中断
  {
          if(TIM_GetITStatus(TIM8,TIM_IT_Update)==SET)
          {
                     TIM_Cmd(TIM8, DISABLE);
                     TIM_ClearITPendingBit(TIM8,TIM_IT_Update);            // 清除中断标志位
                     printf("TIMER_8 update update update update update update update !\n");
                     timer8_times_count++;
                     if(timer8_times_count>=100)                                       // 100次中断后,关闭定时,即停止TIM8 触发 DMA
                     {
                                  TIM_Cmd(TIM8, DISABLE);
                                  DMA2_Stream3->CR = 0;                              // 清空外设寄存器
                     }
                    else // 100次以内,继续触发DMA
                    {
                                  TIM_Cmd(TIM8, ENABLE);
                    }
           }
  }
  

/*

在主函数中通过循环打印目的寄存器的值,观察DMA是否被循环触发,通过打印可以看到目的寄存器的值在不断变化,说明DMA在一直工作,定时器停止后(计数达到100次),目的寄存器一直为0,没有再变化,说明定时器开启过程中,UPDATE每产生一次,就会触发DMA工作一次,完成数据搬运,当定时器被关闭后,目的寄存器的值始终保持为0(关闭定时器时被手动清空),DMA也不再被触发。

*/

  int main(void)
  {
           uart5_init();                                        // 串口初始化 8 N 1 ,115200 仅用于 printf 输出
           DMA_Auto_Config_CR();                  // DMA配置数据到寄存器(内存到外设,循环模式,TIM8_UPDATE触发)
           hf_timer8_init();                                 // TIM8 配置为update事件,打开中断(仅用于观察现象,不开中断也可以触发DMA)
          while(1)
          {
                      printf("DMA2_Stream3->CR = 0x%x\n",DMA2_Stream3->CR);
                      delay_ms(10);
          }
  }
   
  /*
   
  打印结果:
  ......
  DMA2_Stream3->CR = 0x06030442;
  TIMER_8 update update update update update update update !
  TIMER_8 update update update update update update update !
  TIMER_8 update update update update update update update !
   
  DMA2_Stream3->CR = 0x06030444;
  TIMER_8 update update update update update update update !
  TIMER_8 update update update update update update update !
  TIMER_8 update update update update update update update !
   
  DMA2_Stream3->CR = 0;
  DMA2_Stream3->CR = 0;
  DMA2_Stream3->CR = 0;
  DMA2_Stream3->CR = 0;
  DMA2_Stream3->CR = 0;
  DMA2_Stream3->CR = 0;
  DMA2_Stream3->CR = 0;
  DMA2_Stream3->CR = 0;
  DMA2_Stream3->CR = 0;
  DMA2_Stream3->CR = 0;
  .......
  */

2019.09.30 by c67890

 

可以通过以下步骤实现 STM32 定时器触发 DMA: 1. 配置定时器 TIM 的计数模式和时钟源,使其能够按照一定频率产生定时中断。 2. 配置 DMA 的通道和传输方向,设置传输数据的大小和目的地址。 3. 配置 DMA触发源为定时器 TIM 的更新事件,即在 TIM 定时中断触发 DMA 传输。 4. 在 TIM 的中断服务函数中启动 DMA 传输,将数据从源地址传输到目的地址。 以下是一个示例代码: ```c /* 配置 TIM 定时器 */ TIM_HandleTypeDef htim; htim.Instance = TIMx; htim.Init.Prescaler = xxx; htim.Init.Period = xxx; htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim.Init.CounterMode = TIM_COUNTERMODE_UP; HAL_TIM_Base_Init(&htim); HAL_TIM_Base_Start_IT(&htim); /* 配置 DMA 传输 */ DMA_HandleTypeDef hdma; hdma.Instance = DMAx_Channelx; hdma.Init.Direction = DMA_MEMORY_TO_MEMORY; hdma.Init.PeriphInc = DMA_PINC_ENABLE; hdma.Init.MemInc = DMA_MINC_ENABLE; hdma.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; hdma.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; hdma.Init.Mode = DMA_NORMAL; hdma.Init.Priority = DMA_PRIORITY_HIGH; HAL_DMA_Init(&hdma); __HAL_DMA_ENABLE_IT(&hdma, DMA_IT_TC); /* 配置 DMA 触发源为 TIM 更新事件 */ __HAL_DMA_DISABLE(&hdma); HAL_DMA_Start_IT(&hdma, (uint32_t)src_addr, (uint32_t)dst_addr, data_size); __HAL_DMA_ENABLE(&hdma); __HAL_TIM_ENABLE_DMA(&htim, TIM_DMA_UPDATE); /* 在 TIM 中断服务函数中启动 DMA 传输 */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIMx) { HAL_DMA_Start_IT(&hdma, (uint32_t)src_addr, (uint32_t)dst_addr, data_size); } } ``` 以上代码仅供参考,具体实现还需根据实际需求进行调整。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值