完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980php
本章教程爲你們講解定時器觸發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
定時器觸發DMAMUX,控制BDMA讓GPIO輸出PWM的實現思路框圖以下:
下面將程序設計中的相關問題逐一爲你們作個說明。
使用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也能夠正常工做。
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使用的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. }
這裏把幾個關鍵的地方闡釋下:
完整配置以下:
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. }
這裏把幾個關鍵的地方闡釋下:
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引腳的高低電平控制。
因爲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章,有詳細講解。
前面的配置中開啓了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中斷的執行頻率較高。
藉助本章2.4小節的知識點,若是要實現脈衝個數的控制,在BDMA中斷服務程序裏面動態修改緩衝區便可。好比咱們配置:
若是要實現100個脈衝,咱們就能夠在12輪,即12*8=96個脈衝後的傳輸完成中斷裏面修改後半部分輸出低電平便可,進入半傳輸完成中斷後再修改前半部分數據輸出低電平。
BDMA驅動文件bsp_pwm_dma.c提供了以下兩個函數:
函數LPTIM_Config是文件內部調用的,而函數bsp_InitTimBDMA是供用戶調用的。
函數原型:
static void LPTIM_Config(void)
函數描述:
此函數用於配置LPTIM2工做在PWM模式,但不初始化GPIO,使用內部的LPTIM2_OUT便可做爲BDMA請求發生器的觸發源。
注意事項:
函數原型:
void bsp_InitTimBDMA(void)
函數描述:
此函數用於配置定時器觸發BDMA,能夠實現任意IO作PWM輸出。
注意事項:
使用舉例:
做爲初始化函數,直接在bsp.c文件的bsp_Init函數裏面調用便可。
低功耗定時器的移植比較簡單:
經過程序設計框架,讓你們先對配套例程有一個全面的認識,而後再理解細節,本次實驗例程的設計框架以下:
第1階段,上電啓動階段:
第2階段,進入main函數:
配套例子:
V7-010_DMAMUX的定時器觸+BDMA控制任意IO作PWM和脈衝數控制
實驗目的:
實驗內容:
實驗操做:
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(); }
主功能:
主程序實現以下操做:
/* ********************************************************************************************************* * 函 數 名: 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; } } } }
配套例子:
V7-010_DMAMUX的定時器觸+BDMA控制任意IO作PWM和脈衝數控制
實驗目的:
實驗內容:
實驗操做:
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(); }
主功能:
主程序實現以下操做:
/* ********************************************************************************************************* * 函 數 名: 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; } } } }
本章節就爲你們講解這麼多,控制BDMA讓GPIO輸出PWM以及脈衝數的控制,實際項目中有必定的實用價值,望初學者熟練掌握。