【STM32H7教程】第41章 STM32H7的BDMA應用之控制任意IO作PWM和脈衝數控制

完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980php

第41章       STM32H7的BDMA應用之控制任意IO作PWM和脈衝數控制

本章教程爲你們講解定時器觸發DMAMUX,控制BDMA讓GPIO輸出PWM以及脈衝數的控制,實際項目中有必定的使用價值。框架

41.1 初學者重要提示函數

41.2 定時器觸發BDMA驅動設計學習

41.3 BDMA板級支持包(bsp_tim_dma.c)測試

41.4 BDMA驅動移植和使用ui

41.5 實驗例程設計框架spa

41.6 實驗例程說明(MDK)設計

41.7 實驗例程說明(IAR)3d

41.8 總結code

 

 

41.1 初學者重要提示

  1.   學習本章節前,務必優先學習第39章和40章,須要對DMAMUX,BDMA的基礎知識和HAL庫的幾個經常使用API有個認識。
  2.   使用半傳輸完成中斷和傳輸完成中斷實現的雙緩衝效果跟BDMA自己支持的雙緩衝模式實現的效果是同樣的。只是最大傳輸個數只能達到32767次。
  3.   相比定時器自己支持的PWM,這種方式更加靈活,可讓任意IO均可以輸出PWM,並且方便運行中動態修改輸出狀態。

41.2 定時器觸發BDMA驅動設計

定時器觸發DMAMUX,控制BDMA讓GPIO輸出PWM的實現思路框圖以下:

 

下面將程序設計中的相關問題逐一爲你們作個說明。

41.2.1 定時器選擇

使用BDMA的話,請求信號都是來自DMAMUX2,而控制DMA作週期性傳輸的話,可使用定時器觸發,這樣的話就可使用DMAMUX的請求發生器功能,支持以下幾種觸發:

#define HAL_DMAMUX2_REQ_GEN_DMAMUX2_CH0_EVT   0U   
#define HAL_DMAMUX2_REQ_GEN_DMAMUX2_CH1_EVT   1U   
#define HAL_DMAMUX2_REQ_GEN_DMAMUX2_CH2_EVT   2U   
#define HAL_DMAMUX2_REQ_GEN_DMAMUX2_CH3_EVT   3U   
#define HAL_DMAMUX2_REQ_GEN_DMAMUX2_CH4_EVT   4U   
#define HAL_DMAMUX2_REQ_GEN_DMAMUX2_CH5_EVT   5U  
#define HAL_DMAMUX2_REQ_GEN_DMAMUX2_CH6_EVT   6U   
#define HAL_DMAMUX2_REQ_GEN_LPUART1_RX_WKUP   7U   
#define HAL_DMAMUX2_REQ_GEN_LPUART1_TX_WKUP   8U   
#define HAL_DMAMUX2_REQ_GEN_LPTIM2_WKUP       9U   
#define HAL_DMAMUX2_REQ_GEN_LPTIM2_OUT       10U  
#define HAL_DMAMUX2_REQ_GEN_LPTIM3_WKUP      11U   
#define HAL_DMAMUX2_REQ_GEN_LPTIM3_OUT       12U  
#define HAL_DMAMUX2_REQ_GEN_LPTIM4_WKUP      13U   
#define HAL_DMAMUX2_REQ_GEN_LPTIM5_WKUP      14U   
#define HAL_DMAMUX2_REQ_GEN_I2C4_WKUP        15U   
#define HAL_DMAMUX2_REQ_GEN_SPI6_WKUP        16U   
#define HAL_DMAMUX2_REQ_GEN_COMP1_OUT        17U   
#define HAL_DMAMUX2_REQ_GEN_COMP2_OUT        18U   
#define HAL_DMAMUX2_REQ_GEN_RTC_WKUP         19U   
#define HAL_DMAMUX2_REQ_GEN_EXTI0            20U  
#define HAL_DMAMUX2_REQ_GEN_EXTI2            21U   
#define HAL_DMAMUX2_REQ_GEN_I2C4_IT_EVT      22U  
#define HAL_DMAMUX2_REQ_GEN_SPI6_IT          23U  
#define HAL_DMAMUX2_REQ_GEN_LPUART1_TX_IT    24U  
#define HAL_DMAMUX2_REQ_GEN_LPUART1_RX_IT    25U   
#define HAL_DMAMUX2_REQ_GEN_ADC3_IT          26U   
#define HAL_DMAMUX2_REQ_GEN_ADC3_AWD1_OUT    27U  
#define HAL_DMAMUX2_REQ_GEN_BDMA_CH0_IT      28U  
#define HAL_DMAMUX2_REQ_GEN_BDMA_CH1_IT      29U   

 

咱們這裏使用的是LPTIM2_OUT,由於BDMA,LPTIM2和GPIO都在D3域。

接下來就是LPTIM的時鐘配置問題,由前面的LPTIM章節,咱們知道LPTIM2的時鐘能夠由LSE,LSI,APB或者外部輸入時鐘提供。使用LSE,LSI或者外部輸入的好處是停機狀態下,LPTIM1也能夠正常工做。

  •   V7開發板使用的LSE晶振是32768Hz。
  •   STM32H743的LSI頻率約32KHz。
  •   LPTIM1 – LPTIM5的頻率都是100MHz。
System Clock source       = PLL (HSE)
SYSCLK(Hz)                = 400000000 (CPU Clock)
HCLK(Hz)                  = 200000000 (AXI and AHBs Clock)
AHB Prescaler             = 2
D1 APB3 Prescaler         = 2 (APB3 Clock  100MHz)
D2 APB1 Prescaler         = 2 (APB1 Clock  100MHz)
D2 APB2 Prescaler         = 2 (APB2 Clock  100MHz)
D3 APB4 Prescaler         = 2 (APB4 Clock  100MHz)

由於APB1 prescaler != 1, 因此 APB1上的TIMxCLK = APB1 x 2 = 200MHz; 不含這個總線下的LPTIM1
由於APB2 prescaler != 1, 因此 APB2上的TIMxCLK = APB2 x 2 = 200MHz;

APB4上面的TIMxCLK沒有分頻,因此就是100MHz;

APB1 定時器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14,LPTIM1
APB2 定時器有 TIM1, TIM8 , TIM15, TIM16,TIM17

APB4 定時器有 LPTIM2,LPTIM3,LPTIM4,LPTIM5

 

若是選擇APB時鐘的話,配置以下:

RCC_PeriphCLKInitTypeDef   RCC_PeriphCLKInitStruct = {0};

RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM2;
RCC_PeriphCLKInitStruct.Lptim2ClockSelection = RCC_LPTIM2CLKSOURCE_D3PCLK1;
HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);

 

使用APB做爲LPTIM系統時鐘注意如下兩點:

  •     LPTIM1 – LPTIM5的最高主頻都是100MHz。
  •     注意參數RCC_LPTIM2CLKSOURCE_D3PCLK1。

LPTIM1使用的RCC_LPTIM1CLKSOURCE_D2PCLK1。

LPTIM2使用的RCC_LPTIM2CLKSOURCE_D3PCLK1。

LPTIM3-LPTIM5使用的RCC_LPTIM345CLKSOURCE_D3PCLK1。

 

LPTIM2的配置代碼以下:

1.    /*
2.    ******************************************************************************************************
3.    *    函 數 名: LPTIM_Config
4.    *    功能說明: 配置LPTIM,用於觸發DMAMUX的請求發生器
5.    *    形    參: 無
6.    *    返 回 值: 無
7.    ******************************************************************************************************
8.    */
9.    void LPTIM_Config(void)
10.    {
11.        
12.        RCC_PeriphCLKInitTypeDef  PeriphClkInitStruct;
13.    
14.        
15.        /*##-1- 配置LPTIM2使用PCLK時鐘 ##################################################*/
16.        PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM2;
17.        PeriphClkInitStruct.Lptim2ClockSelection = RCC_LPTIM2CLKSOURCE_D3PCLK1;
18.        HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);  
19.    
20.    
21.        /*##-2- 使能LPTIM2時鐘並配置 ####################################################*/
22.        __HAL_RCC_LPTIM2_CLK_ENABLE();
23.    
24.        LptimHandle.Instance                           = LPTIM2;
25.        LptimHandle.Init.CounterSource                 = LPTIM_COUNTERSOURCE_INTERNAL;
26.        LptimHandle.Init.UpdateMode                    = LPTIM_UPDATE_ENDOFPERIOD;
27.        LptimHandle.Init.OutputPolarity                = LPTIM_OUTPUTPOLARITY_HIGH;
28.        LptimHandle.Init.Clock.Source                  = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC;
29.        LptimHandle.Init.Clock.Prescaler               = LPTIM_PRESCALER_DIV1;
30.        LptimHandle.Init.UltraLowPowerClock.Polarity   = LPTIM_CLOCKPOLARITY_RISING;
31.        LptimHandle.Init.UltraLowPowerClock.SampleTime = LPTIM_CLOCKSAMPLETIME_DIRECTTRANSITION;
32.        LptimHandle.Init.Trigger.Source                = LPTIM_TRIGSOURCE_SOFTWARE;
33.        LptimHandle.Init.Trigger.ActiveEdge            = LPTIM_ACTIVEEDGE_RISING;
34.        LptimHandle.Init.Trigger.SampleTime            = LPTIM_TRIGSAMPLETIME_DIRECTTRANSITION;
35.    
36.        /*##-3- 初始化LPTIM2 ##########################################################*/
37.        if(HAL_LPTIM_Init(&LptimHandle) != HAL_OK)
38.        {
39.            Error_Handler(__FILE__, __LINE__);
40.        }
41.    
42.        /*##-4- 啓動LPTIM2的PWM模式,但使用輸出引腳,僅用於DMAMUX的觸發 ##############*/
43.        /* LPTIM2的時鐘主頻是100MHz,這裏配置觸發是100MHz / (10000 - 1 + 1) = 10KHz */
44.        if (HAL_LPTIM_PWM_Start(&LptimHandle, 10000-1, 5000 - 1) != HAL_OK)
45.        {
46.            Error_Handler(__FILE__, __LINE__);
47.        }  
48.    }

 

這裏把幾個關鍵的地方闡釋下:

  •   第16 – 18行,配置LPTIM2使用APB時鐘。
  •   第22 – 40行, 配置LPTIM2的相關參數,具體每一個參數表明的含義能夠看前面LPTIM章節的講解。
  •   第44 – 47行,配置LPTIM2工做在PWM模式,頻率10KHz,佔空比50%。這裏僅僅是用到LPTIM2_OUT的輸出信號做爲DMAMUX的請求發生器觸發源,因此用不到PWM的輸出引腳。

41.2.2 DMMUX和BDMA配置

完整配置以下:

1.    /*
2.    ******************************************************************************************************
3.    *    函 數 名: bsp_InitTimBDMA
4.    *    功能說明: 配置DMAMUX的定時器觸+DMA控制任意IO作PWM和脈衝數控制
5.    *    形    參: 無
6.    *    返 回 值: 無
7.    ******************************************************************************************************
8.    */
9.    void bsp_InitTimBDMA(void)
10.    {
11.        GPIO_InitTypeDef  GPIO_InitStruct;
12.        DMA_HandleTypeDef DMA_Handle = {0};
13.        HAL_DMA_MuxRequestGeneratorConfigTypeDef dmamux_ReqGenParams ={0};
14.    
15.        
16.         /*##-1-  ##################################################*/ 
17.        __HAL_RCC_GPIOB_CLK_ENABLE();
18.          
19.        GPIO_InitStruct.Pin = GPIO_PIN_1;
20.        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
21.        GPIO_InitStruct.Pull = GPIO_NOPULL;
22.        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
23.        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
24.        
25.      
26.        /*##-2- Configure the DMA ##################################################*/
27.        __HAL_RCC_BDMA_CLK_ENABLE();
28.    
29.        DMA_Handle.Instance            = BDMA_Channel0;           /* 使用的BDMA通道0 */
30.        DMA_Handle.Init.Request        = BDMA_REQUEST_GENERATOR0; /* 請求類型採用的DMAMUX請求發生器通道0 */  
31.        DMA_Handle.Init.Direction      = DMA_MEMORY_TO_PERIPH;    /* 傳輸方向是從存儲器到外設 */  
32.        DMA_Handle.Init.PeriphInc      = DMA_PINC_DISABLE;        /* 外設地址自增禁止 */  
33.        DMA_Handle.Init.MemInc         = DMA_MINC_ENABLE;         /* 存儲器地址自增使能 */  
34.        DMA_Handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;  /* 外設數據傳輸位寬選擇字,即32bit */     
35.        DMA_Handle.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;   /* 存儲器數據傳輸位寬選擇字,即32bit */    
36.        DMA_Handle.Init.Mode                = DMA_CIRCULAR;            /* 循環模式 */   
37.        DMA_Handle.Init.Priority            = DMA_PRIORITY_LOW;        /* 優先級低 */  
38.        DMA_Handle.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;    /* BDMA不支持FIFO */ 
39.        DMA_Handle.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL; /* BDMA不支持FIFO閥值設置 */ 
40.        DMA_Handle.Init.MemBurst            = DMA_MBURST_SINGLE;       /* BDMA不支持存儲器突發 */ 
41.        DMA_Handle.Init.PeriphBurst         = DMA_PBURST_SINGLE;       /* BDMA不支持外設突發 */ 
42.        
43.        HAL_DMA_Init(&DMA_Handle);
44.    
45.        /* 開啓BDMA Channel0的中斷 */
46.        HAL_NVIC_SetPriority(BDMA_Channel0_IRQn, 2, 0);
47.        HAL_NVIC_EnableIRQ(BDMA_Channel0_IRQn); 
48.    
49.        /*##-3- 配置DMAMUX #########################################################*/
50.        dmamux_ReqGenParams.SignalID = HAL_DMAMUX2_REQ_GEN_LPTIM2_OUT;     /* 請求觸發器選擇LPTIM2_OUT */
51.        dmamux_ReqGenParams.Polarity  = HAL_DMAMUX_REQ_GEN_RISING_FALLING; /* 上升沿和降低沿都可觸發  */
52.        dmamux_ReqGenParams.RequestNumber = 1;                         /* 觸發後,傳輸進行1次DMA傳輸 */
53.    
54.        HAL_DMAEx_ConfigMuxRequestGenerator(&DMA_Handle, &dmamux_ReqGenParams); /* 配置DMAMUX */
55.        
56.        HAL_DMAEx_EnableMuxRequestGenerator (&DMA_Handle);                      /* 使能DMAMUX請求發生器 */        
57.          
58.        /*##-4- 啓動DMA傳輸 ################################################*/
59.        HAL_DMA_Start_IT(&DMA_Handle, (uint32_t)IO_Toggle, (uint32_t)&GPIOB->BSRRL, 8);
60.        
61.        /* 
62.           默認狀況下,用戶經過註冊回調函數DMA_Handle.XferHalfCpltCallback,而後函數HAL_DMA_Init會開啓半
63.           傳輸完成中斷,
64.           因爲這裏沒有使用HAL庫默認的中斷管理函數HAL_DMA_IRQHandler,直接手動開啓。
65.        */
66.        BDMA_Channel0->CCR |= BDMA_CCR_HTIE;
67.        
68.        LPTIM_Config(); /* 配置LPTIM觸發DMAMUX */
69.    }

 

這裏把幾個關鍵的地方闡釋下:

  •   第12 - 13行,對做爲局部變量的HAL庫結構體作初始化,防止不肯定值配置時出問題。
  •   第17 - 23行,配置PB1推輓輸出。
  •   第27 – 43行,配置BDMA的基本參數,註釋較詳細。
  •   第46 – 47行,配置BDMA的中斷優先級,並使能。
  •   第50 – 56行,配置DMAMUX的請求發生器觸發源選擇的LPTIM2_OUT,上升沿和降低沿均觸發BDMA傳輸。
  •   第59行,採用中斷方式啓動BDAM傳輸,這裏中斷注意第2個參數和第3個參數。第2個原地址,定義以下:
uint32_t IO_Toggle[8]  ={ 
                          0x00000002U,   
                          0x00020000U,  
                          0x00000002U,   
                          0x00020000U,   
                          0x00000002U,   
                          0x00020000U,   
                          0x00000002U,   
                          0x00020000U,  
                       };

 

定義了8個uint32_t類型的變量。第3個參數很是考究,這裏使用的GPIO的BSRR寄存器,這個寄存器的特色就是置1有效,而清零操做對其無效。

 

高16位用於控制GPIO的輸出低電平,而低16位用於輸出高電平工做,因此咱們這裏設置

GPIOB_BSRR = 0x00000002時,表示PB1輸出高電平。

GPIOB_BSRR = 0x00020000時,表示PB1輸出低電平。

經過這種方式就實現了PB1引腳的高低電平控制。

  •   第66行,這裏比較特殊,默認狀況下,用戶經過註冊回調函數DMA_Handle.XferHalfCpltCallback,而後函數HAL_DMA_Init會開啓半傳輸完成中斷,因爲這裏沒有使用HAL庫默認的中斷管理函數HAL_DMA_IRQHandler,直接手動開啓。
  •   第68行,調用LPTIM的初始化配置。

41.2.3 BDMA存儲器選擇注意事項

因爲STM32H7 Cache的存在,凡是CPU和DMA都會操做到的存儲器,咱們都要注意數據一致性問題。對於本章節要實現的功能,若是不須要運行中動態修改BDMA源地址中的數據,能夠不用管這個問題,若是要動態修改,就得注意Cache所帶來的的數據一致性問題,這裏提供兩種解決辦法:

  •   方法一:

設置BDMA所使用SRAM3存儲區的Cache屬性爲Write through, read allocate,no write allocate。保證寫入的數據會當即更新到SRAM3裏面。

/* 配置SRAM3的屬性爲Write through, read allocate,no write allocate */
MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress      = 0x38000000;
MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

HAL_MPU_ConfigRegion(&MPU_InitStruct);

 

  •   方法二:

設置SRAM3的緩衝區作32字節對齊,大小最好也是32字節整數倍,而後調用函數SCB_CleanDCache_by_Addr作Clean操做便可,保證BDMA讀取到的數據是剛更新好的。

 

本章節配套例子是直接使用的方法一。例子中變量的定義方式以下:

/* 方便Cache類的API操做,作32字節對齊 */
#if defined ( __ICCARM__ )
#pragma location = 0x38000000
uint32_t IO_Toggle[8]  =
                      { 
                          0x00000002U,   
                          0x00020000U,  
                          0x00000002U,   
                          0x00020000U,   
                          0x00000002U,   
                          0x00020000U,   
                          0x00000002U,   
                          0x00020000U,  
                      };

#elif defined ( __CC_ARM )
ALIGN_32BYTES(__attribute__((section (".RAM_D3"))) uint32_t IO_Toggle[8]) =
                                                      { 
                                                          0x00000002U,   
                                                          0x00020000U,  
                                                          0x00000002U,   
                                                          0x00020000U,   
                                                          0x00000002U,   
                                                          0x00020000U,   
                                                          0x00000002U,   
                                                          0x00020000U,  
                                                      };
#endif

 

對於IAR須要#pragma location指定位置,而MDK經過分散加載便可實現,詳情看前面第26章,有詳細講解。

41.2.4 BDMA中斷處理

前面的配置中開啓了BDMA的傳輸完成中斷、半傳輸完成中斷和傳輸錯誤中斷。經過半傳輸完整中斷和傳輸完成中斷能夠實現雙緩衝的效果:

/*
*********************************************************************************************************
*    函 數 名: BDMA_Channel0_IRQHandler
*    功能說明: BDMA通道0
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void BDMA_Channel0_IRQHandler(void)
{
    /* 傳輸完成中斷 */
    if((BDMA->ISR & BDMA_FLAG_TC0) != RESET)
    {
        BDMA->IFCR = BDMA_FLAG_TC0;

        /*
           一、傳輸完成開始使用DMA緩衝區的前半部分,此時能夠動態修改後半部分數據
              好比緩衝區大小是IO_Toggle[0] 到 IO_Toggle[7]
              那麼此時能夠修改IO_Toggle[4] 到 IO_Toggle[7]
           二、變量所在的SRAM區已經經過MPU配置爲WT模式,更新變量IO_Toggle會當即寫入。
           三、不配置MPU的話,也能夠經過Cahce的函數SCB_CleanDCache_by_Addr作Clean操做。
        */
    }
 
    /* 半傳輸完成中斷 */    
    if((BDMA->ISR & BDMA_FLAG_HT0) != RESET)
    {
        BDMA->IFCR = BDMA_FLAG_HT0;
        
        /*
           一、半傳輸完成開始使用DMA緩衝區的後半部分,此時能夠動態修改前半部分數據
              好比緩衝區大小是IO_Toggle[0] 到 IO_Toggle[7]
              那麼此時能夠修改IO_Toggle[0] 到 IO_Toggle[3]
           二、變量所在的SRAM區已經經過MPU配置爲WT模式,更新變量IO_Toggle會當即寫入。
           三、不配置MPU的話,也能夠經過Cahce的函數SCB_CleanDCache_by_Addr作Clean操做。
        */
    }
 
    /* 傳輸錯誤中斷 */
    if((BDMA->ISR & BDMA_FLAG_TE0) != RESET)
    {
        BDMA->IFCR = BDMA_FLAG_TE0;
    }
}

 

註釋的比較清楚。若是輸出的PWM頻率較高,建議將BDMA的緩衝區設置的大些,防止BDMA中斷的執行頻率較高。

41.2.5 BDMA脈衝個數控制

藉助本章2.4小節的知識點,若是要實現脈衝個數的控制,在BDMA中斷服務程序裏面動態修改緩衝區便可。好比咱們配置:

  •   BDMA開啓半傳輸完成中斷和傳輸完成中斷。
  •   BDMA傳輸16次爲一輪,每兩次傳輸算一個週期的脈衝。

若是要實現100個脈衝,咱們就能夠在12輪,即12*8=96個脈衝後的傳輸完成中斷裏面修改後半部分輸出低電平便可,進入半傳輸完成中斷後再修改前半部分數據輸出低電平。

41.3 BDMA板級支持包(bsp_tim_dma.c)

BDMA驅動文件bsp_pwm_dma.c提供了以下兩個函數:

  •   LPTIM_Config
  •   bsp_InitTimBDMA

 

函數LPTIM_Config是文件內部調用的,而函數bsp_InitTimBDMA是供用戶調用的。

41.3.1 函數LPTIM_Config

函數原型:

static void LPTIM_Config(void)

函數描述:

此函數用於配置LPTIM2工做在PWM模式,但不初始化GPIO,使用內部的LPTIM2_OUT便可做爲BDMA請求發生器的觸發源。

注意事項:

  1. 函數前面static用於限制做用域,表示僅在本文件裏面調用。
  2. 關於此函數的講解在本章的2.1小節

41.3.2 函數bsp_InitTimBDMA

函數原型:

void bsp_InitTimBDMA(void)

函數描述:

此函數用於配置定時器觸發BDMA,能夠實現任意IO作PWM輸出。

注意事項:

  1. 關於此函數的講解在本章2.2小節。

使用舉例:

做爲初始化函數,直接在bsp.c文件的bsp_Init函數裏面調用便可。

41.4 BDMA驅動移植和使用

低功耗定時器的移植比較簡單:

  •   第1步:複製bsp_tim_dma.c和bsp_tim_dma.h到本身的工程目錄,並添加到工程裏面。
  •   第2步:這幾個驅動文件主要用到HAL庫的GPIO、LPTIM和DMA驅動文件,簡單省事些能夠添加全部HAL庫.C源文件進來。
  •   第3步,應用方法看本章節配套例子便可。

41.5 實驗例程設計框架

經過程序設計框架,讓你們先對配套例程有一個全面的認識,而後再理解細節,本次實驗例程的設計框架以下:

  第1階段,上電啓動階段:

  • 這部分在第14章進行了詳細說明。

  第2階段,進入main函數:

  •  第1步,硬件初始化,主要是MPU,Cache,HAL庫,系統時鐘,滴答定時器,LED和串口。
  •  第2步,藉助按鍵消息實現不一樣的輸出頻率調整,方便測試。

41.6 實驗例程說明(MDK)

配套例子:

V7-010_DMAMUX的定時器觸+BDMA控制任意IO作PWM和脈衝數控制

實驗目的:

  1. 學習DMAMUX的定時器觸+BDMA控制任意IO作PWM和脈衝數控制。

實驗內容:

  1. 經過LPTIM2觸發DMAMUX的請求發生器,控制DMA給任意IO作PWM輸出。

實驗操做:

  1. K1鍵按下,PB1輸出20KHz方波,佔空比50%。
  2. K2鍵按下,PB1輸出10KHz方波,佔空比50%。
  3. K3鍵按下,PB1輸出5KHz方波,佔空比50%。

PB1的位置:

 

上電後串口打印的信息:

波特率 115200,數據位 8,奇偶校驗位無,中止位 1

 

程序設計:

  系統棧大小分配:

 

  RAM空間用的DTCM:

 

  硬件外設初始化

硬件外設的初始化是在 bsp.c 文件實現:

/*
*********************************************************************************************************
*    函 數 名: bsp_Init
*    功能說明: 初始化全部的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只須要調用一次
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 庫初始化,此時系統用的仍是H7自帶的64MHz,HSI時鐘:
       - 調用函數HAL_InitTick,初始化滴答時鐘中斷1ms。
       - 設置NVIV優先級分組爲4。
     */
    HAL_Init();

    /* 
       配置系統時鐘到400MHz
       - 切換使用HSE。
       - 此函數會更新全局變量SystemCoreClock,並從新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用於代碼執行時間測量,MDK5.25及其以上版本才支持,IAR不支持。
       - 默認不開啓,若是要使能此選項,務必看V7開發板用戶手冊第xx章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder並開啓 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
    bsp_InitKey();        /* 按鍵初始化,要放在滴答定時器以前,由於按鈕檢測是經過滴答定時器掃描 */
    bsp_InitTimer();      /* 初始化滴答定時器 */
    bsp_InitUart();    /* 初始化串口 */
    bsp_InitExtIO();    /* 初始化FMC總線74HC574擴展IO. 必須在 bsp_InitLed()前執行 */    
    bsp_InitLed();        /* 初始化LED */    
     
     bsp_InitTimBDMA();  /* 初始化BDMA控制PB1作的PWM輸出 */
}

 

  MPU配置和Cache配置:

數據Cache和指令Cache都開啓。配置了AXI SRAM區(本例子未用到AXI SRAM),FMC的擴展IO區和SRAM4。

/*
*********************************************************************************************************
*    函 數 名: MPU_Config
*    功能說明: 配置MPU
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

    /* 配置AXI SRAM的MPU屬性爲Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    
    /* 配置FMC擴展IO的MPU屬性爲Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /* 配置SRAM4的屬性爲Write through, read allocate,no write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x38000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 數 名: CPU_CACHE_Enable
*    功能說明: 使能L1 Cache
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}

 

  主功能:

主程序實現以下操做:

  •   K1鍵按下,PB1輸出20KHz方波,佔空比50%。
  •   K2鍵按下,PB1輸出10KHz方波,佔空比50%。
  •   K3鍵按下,PB1輸出5KHz方波,佔空比50%。
/*
*********************************************************************************************************
*    函 數 名: main
*    功能說明: c程序入口
*    形    參: 無
*    返 回 值: 錯誤代碼(無需處理)
*********************************************************************************************************
*/
int main(void)
{
    uint8_t ucKeyCode;        /* 按鍵代碼 */
    
    
    bsp_Init();        /* 硬件初始化 */
    
    PrintfLogo();    /* 打印例程名稱和版本等信息 */
    PrintfHelp();    /* 打印操做提示 */

    bsp_StartAutoTimer(0, 100);    /* 啓動1個100ms的自動重裝的定時器 */
    
    /* 進入主程序循環體 */
    while (1)
    {
        bsp_Idle();        /* 這個函數在bsp.c文件。用戶能夠修改這個函數實現CPU休眠和喂狗 */

        /* 判判定時器超時時間 */
        if (bsp_CheckTimer(0))    
        {
            /* 每隔100ms 進來一次 */  
            bsp_LedToggle(2);
        }

        /* 按鍵濾波和檢測由後臺systick中斷服務程序實現,咱們只須要調用bsp_GetKey讀取鍵值便可。 */
        ucKeyCode = bsp_GetKey();    /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:            /* K1鍵按下,PB1輸出20KHz方波,佔空比50% */
                  if (HAL_LPTIM_PWM_Start(&LptimHandle, 5000-1, 2500 - 1) != HAL_OK)
                  {
                      Error_Handler(__FILE__, __LINE__);
                  }  
                  break;

                case KEY_DOWN_K2:            /* K2鍵按下,PB1輸出10KHz方波,佔空比50% */
                  if (HAL_LPTIM_PWM_Start(&LptimHandle, 10000-1, 5000 - 1) != HAL_OK)
                  {
                      Error_Handler(__FILE__, __LINE__);
                  }  
                  break;
                
                case KEY_DOWN_K3:            /* K3鍵按下,PB1輸出5KHz方波,佔空比50% */            
                  if (HAL_LPTIM_PWM_Start(&LptimHandle, 20000-1, 10000 - 1) != HAL_OK)
                  {
                      Error_Handler(__FILE__, __LINE__);
                  }  
                  break;

                default:
                  /* 其它的鍵值不處理 */
                  break;
            }
        }
    }
}

 

41.7 實驗例程說明(IAR)

配套例子:

V7-010_DMAMUX的定時器觸+BDMA控制任意IO作PWM和脈衝數控制

實驗目的:

  1. 學習DMAMUX的定時器觸+BDMA控制任意IO作PWM和脈衝數控制。

實驗內容:

  1. 經過LPTIM2觸發DMAMUX的請求發生器,控制DMA給任意IO作PWM輸出。

實驗操做:

  1. K1鍵按下,PB1輸出20KHz方波,佔空比50%。
  2. K2鍵按下,PB1輸出10KHz方波,佔空比50%。
  3. K3鍵按下,PB1輸出5KHz方波,佔空比50%。

PB1的位置:

 

上電後串口打印的信息:

波特率 115200,數據位 8,奇偶校驗位無,中止位 1

 

程序設計:

  系統棧大小分配:

 

  RAM空間用的DTCM:

 

  硬件外設初始化

硬件外設的初始化是在 bsp.c 文件實現:

/*
*********************************************************************************************************
*    函 數 名: bsp_Init
*    功能說明: 初始化全部的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只須要調用一次
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 庫初始化,此時系統用的仍是H7自帶的64MHz,HSI時鐘:
       - 調用函數HAL_InitTick,初始化滴答時鐘中斷1ms。
       - 設置NVIV優先級分組爲4。
     */
    HAL_Init();

    /* 
       配置系統時鐘到400MHz
       - 切換使用HSE。
       - 此函數會更新全局變量SystemCoreClock,並從新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用於代碼執行時間測量,MDK5.25及其以上版本才支持,IAR不支持。
       - 默認不開啓,若是要使能此選項,務必看V7開發板用戶手冊第xx章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder並開啓 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
    bsp_InitKey();        /* 按鍵初始化,要放在滴答定時器以前,由於按鈕檢測是經過滴答定時器掃描 */
    bsp_InitTimer();      /* 初始化滴答定時器 */
    bsp_InitUart();    /* 初始化串口 */
    bsp_InitExtIO();    /* 初始化FMC總線74HC574擴展IO. 必須在 bsp_InitLed()前執行 */    
    bsp_InitLed();        /* 初始化LED */    
     
     bsp_InitTimBDMA();  /* 初始化BDMA控制PB1作的PWM輸出 */
}

 

  MPU配置和Cache配置:

數據Cache和指令Cache都開啓。配置了AXI SRAM區(本例子未用到AXI SRAM),FMC的擴展IO區和SRAM4。

/*
*********************************************************************************************************
*    函 數 名: MPU_Config
*    功能說明: 配置MPU
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

    /* 配置AXI SRAM的MPU屬性爲Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    
    /* 配置FMC擴展IO的MPU屬性爲Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /* 配置SRAM4的屬性爲Write through, read allocate,no write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x38000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 數 名: CPU_CACHE_Enable
*    功能說明: 使能L1 Cache
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}

 

  主功能:

主程序實現以下操做:

  •  K1鍵按下,PB1輸出20KHz方波,佔空比50%。
  •  K2鍵按下,PB1輸出10KHz方波,佔空比50%。
  •  K3鍵按下,PB1輸出5KHz方波,佔空比50%。
/*
*********************************************************************************************************
*    函 數 名: main
*    功能說明: c程序入口
*    形    參: 無
*    返 回 值: 錯誤代碼(無需處理)
*********************************************************************************************************
*/
int main(void)
{
    uint8_t ucKeyCode;        /* 按鍵代碼 */
    
    
    bsp_Init();        /* 硬件初始化 */
    
    PrintfLogo();    /* 打印例程名稱和版本等信息 */
    PrintfHelp();    /* 打印操做提示 */

    bsp_StartAutoTimer(0, 100);    /* 啓動1個100ms的自動重裝的定時器 */
    
    /* 進入主程序循環體 */
    while (1)
    {
        bsp_Idle();        /* 這個函數在bsp.c文件。用戶能夠修改這個函數實現CPU休眠和喂狗 */

        /* 判判定時器超時時間 */
        if (bsp_CheckTimer(0))    
        {
            /* 每隔100ms 進來一次 */  
            bsp_LedToggle(2);
        }

        /* 按鍵濾波和檢測由後臺systick中斷服務程序實現,咱們只須要調用bsp_GetKey讀取鍵值便可。 */
        ucKeyCode = bsp_GetKey();    /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:            /* K1鍵按下,PB1輸出20KHz方波,佔空比50% */
                  if (HAL_LPTIM_PWM_Start(&LptimHandle, 5000-1, 2500 - 1) != HAL_OK)
                  {
                      Error_Handler(__FILE__, __LINE__);
                  }  
                  break;

                case KEY_DOWN_K2:            /* K2鍵按下,PB1輸出10KHz方波,佔空比50% */
                  if (HAL_LPTIM_PWM_Start(&LptimHandle, 10000-1, 5000 - 1) != HAL_OK)
                  {
                      Error_Handler(__FILE__, __LINE__);
                  }  
                  break;
                
                case KEY_DOWN_K3:            /* K3鍵按下,PB1輸出5KHz方波,佔空比50% */            
                  if (HAL_LPTIM_PWM_Start(&LptimHandle, 20000-1, 10000 - 1) != HAL_OK)
                  {
                      Error_Handler(__FILE__, __LINE__);
                  }  
                  break;

                default:
                  /* 其它的鍵值不處理 */
                  break;
            }
        }
    }
}

 

41.8 總結

本章節就爲你們講解這麼多,控制BDMA讓GPIO輸出PWM以及脈衝數的控制,實際項目中有必定的實用價值,望初學者熟練掌握。

相關文章
相關標籤/搜索