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

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

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

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

/*  
    程序实现内容:通过定时器触发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

 

相关文章: