41.1 初学者重要提示
学习本章节前,务必优先学习第39章和40章,需要对DMAMUX,BDMA的基础知识和HAL库的几个常用API有个认识。
使用半传输完成中断和传输完成中断实现的双缓冲效果跟BDMA本身支持的双缓冲模式实现的效果是一样的。只是最大传输个数只能达到32767次。
相比定时器本身支持的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 */