43.1 初学者重要提示
学习本章节前,务必优先学习第39章和42章,需要对DMAMUX,DMA的基础知识和HAL库的几个常用API有个认识。
相比定时器本身支持的PWM,这种方式更加灵活,可以让任意IO都可以输出PWM,而且方便运行中动态修改输出状态。
43.2 定时器触发DMA驱动设计
定时器触发DMAMUX,控制DMA让GPIO输出PWM的实现思路框图如下:
下面将程序设计中的相关问题逐一为大家做个说明。
43.2.1 定时器选择
使用DMA的话,请求信号都是来自DMAMUX2,而控制DMA做周期性传输的话,可以使用定时器触发,这样的话就可以使用DMAMUX的请求发生器功能,支持如下几种触发:
#define HAL_DMAMUX1_REQ_GEN_DMAMUX1_CH0_EVT 0U
#define HAL_DMAMUX1_REQ_GEN_DMAMUX1_CH1_EVT 1U
#define HAL_DMAMUX1_REQ_GEN_DMAMUX1_CH2_EVT 2U
#define HAL_DMAMUX1_REQ_GEN_LPTIM1_OUT 3U
#define HAL_DMAMUX1_REQ_GEN_LPTIM2_OUT 4U
#define HAL_DMAMUX1_REQ_GEN_LPTIM3_OUT 5U
#define HAL_DMAMUX1_REQ_GEN_EXTI0 6U
#define HAL_DMAMUX1_REQ_GEN_TIM12_TRGO 7U
我们这里使用的是TIM12_TRGO。
接下来就是TIM12的时钟配置问题,代码如下:
1. /*
2. ******************************************************************************************************
3. * 函 数 名: TIM12_Config
4. * 功能说明: 配置TIM12,用于触发DMAMUX的请求发生器
5. * 形 参: _Mode
6. * 0 配置为100KHz触发频率,如果DMAMUX配置为单边沿触发,那么输出PWM频率是50KHz,双边沿是
7. 100KHz。
8. * 1 配置为10KHz触发频率,如果DMAMUX配置为单边沿触发,那么输出PWM频率是5KHz,双边沿是10KHz。
9. * 返 回 值: 无
10. ******************************************************************************************************
11. */
12. void TIM12_Config(uint8_t _Mode)
13. {
14. TIM_HandleTypeDef htim ={0};
15. TIM_MasterConfigTypeDef sMasterConfig = {0};
16. TIM_OC_InitTypeDef sConfig = {0};
17. uint32_t Period[2] = {1999, 19999};
18. uint32_t Pulse[2] = {1000, 10000};
19.
20. /* 使能时钟 */
21. __HAL_RCC_TIM12_CLK_ENABLE();
22.
23. /*-----------------------------------------------------------------------
24. bsp.c 文件中 void SystemClock_Config(void) 函数对时钟的配置如下:
25.
26. System Clock source = PLL (HSE)
27. SYSCLK(Hz) = 400000000 (CPU Clock)
28. HCLK(Hz) = 200000000 (AXI and AHBs Clock)
29. AHB Prescaler = 2
30. D1 APB3 Prescaler = 2 (APB3 Clock 100MHz)
31. D2 APB1 Prescaler = 2 (APB1 Clock 100MHz)
32. D2 APB2 Prescaler = 2 (APB2 Clock 100MHz)
33. D3 APB4 Prescaler = 2 (APB4 Clock 100MHz)
34.
35. 因为APB1 prescaler != 1, 所以 APB1上的TIMxCLK = APB1 x 2 = 200MHz;
36. 因为APB2 prescaler != 1, 所以 APB2上的TIMxCLK = APB2 x 2 = 200MHz;
37. APB4上面的TIMxCLK没有分频,所以就是100MHz;
38.
39. APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14,LPTIM1
40. APB2 定时器有 TIM1, TIM8 , TIM15, TIM16,TIM17
41.
42. APB4 定时器有 LPTIM2,LPTIM3,LPTIM4,LPTIM5
43.
44. TIM12CLK = 200MHz/(Period + 1) / (Prescaler + 1)
45. 函数bsp_InitTimDMA1中DMAMUX1选择的是单边沿触发,每个时钟可以触发两次。
46. ----------------------------------------------------------------------- */
47. HAL_TIM_Base_DeInit(&htim);
48.
49. htim.Instance = TIM12;
50. htim.Init.Period = Period[_Mode];
51. htim.Init.Prescaler = 0;
52. htim.Init.ClockDivision = 0;
53. htim.Init.CounterMode = TIM_COUNTERMODE_UP;
54. htim.Init.RepetitionCounter = 0;
55. HAL_TIM_Base_Init(&htim);
56.
57. sConfig.OCMode = TIM_OCMODE_PWM1;
58. sConfig.OCPolarity = TIM_OCPOLARITY_LOW;
59.
60. /* 占空比50% */
61. sConfig.Pulse = Pulse[_Mode];
62. if(HAL_TIM_OC_ConfigChannel(&htim, &sConfig, TIM_CHANNEL_1) != HAL_OK)
63. {
64. Error_Handler(__FILE__, __LINE__);
65. }
66.
67. /* 启动OC1 */
68. if(HAL_TIM_OC_Start(&htim, TIM_CHANNEL_1) != HAL_OK)
69. {
70. Error_Handler(__FILE__, __LINE__);
71. }
72.
73. /* TIM12的TRGO用于触发DMAMUX的请求发生器 */
74. sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1REF;
75. sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
76. sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
77.
78. HAL_TIMEx_MasterConfigSynchronization(&htim, &sMasterConfig);
79. }
这里把几个关键的地方阐释下:
第14 – 16行,对作为局部变量的HAL库结构体做初始化,防止不确定值配置时出问题。
第17 – 18行,定义了两组周期变量和占空比变量,用来设置TIM12。
第20 – 71行,注释已经比较详细。
当选择第1组配置时,
TIM12CLK = 200MHz / (Period + 1) / (Prescaler + 1) = 200MHz/(1999+1) = 100KHz
占空比 = Pulse / (Period + 1) = 1000 / (1999+1)= 50%
当选择第2组配置时,
TIM12CLK = 200MHz / (Period + 1) / (Prescaler + 1) = 200MHz/(19999+1) = 10KHz
占空比 = Pulse / (Period + 1) = 10000 /(19999+1)= 50%
第22 – 40行, TIM12的TRGO用于触发DMAMUX的请求发生器。
这些知识点在前面的定时器章节有更详细的说明。
43.2.2 DMAMUX和DMA配置
完整配置如下:
1. /*
2. ******************************************************************************************************
3. * 函 数 名: bsp_InitTimDMA
4. * 功能说明: 配置DMAMUX的定时器触+DMA双缓冲控制任意IO做PWM和脉冲数控制
5. * 形 参: 无
6. * 返 回 值: 无
7. ******************************************************************************************************
8. */
9. void bsp_InitTimDMA(void)
10. {
11. GPIO_InitTypeDef GPIO_InitStruct;
12. DMA_HandleTypeDef DMA_Handle = {0};
13. HAL_DMA_MuxRequestGeneratorConfigTypeDef dmamux_ReqGenParams = {0};
14.
15. /*##-1- 配置PB1用于PWM输出 ##################################################*/
16. __HAL_RCC_GPIOB_CLK_ENABLE();
17. GPIO_InitStruct.Pin = GPIO_PIN_1;
18. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
19. GPIO_InitStruct.Pull = GPIO_NOPULL;
20. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
21. HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
22.
23. /*##-2- 使能DMA1时钟并配置 ##################################################*/
24. __HAL_RCC_DMA1_CLK_ENABLE();
25. DMA_Handle.Instance = DMA1_Stream1; /* 使用的DMA1 Stream1 */
26. DMA_Handle.Init.Request = DMA_REQUEST_GENERATOR0; /* 请求类型采用的DMAMUX请求发生器通道0 */
27. DMA_Handle.Init.Direction = DMA_MEMORY_TO_PERIPH;/* 传输方向是从存储器到外设 */
28. DMA_Handle.Init.PeriphInc = DMA_PINC_DISABLE; /* 外设地址自增禁止 */
29. DMA_Handle.Init.MemInc = DMA_MINC_ENABLE; /* 存储器地址自增使能 */
30. DMA_Handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; /* 外设数据传输位宽选择字,即32bit */
31. DMA_Handle.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; /* 存储器数据传输位宽选择字,即32bit */
32. DMA_Handle.Init.Mode = DMA_CIRCULAR; /* 循环模式 */
33. DMA_Handle.Init.Priority = DMA_PRIORITY_LOW; /* 优先级低 */
34. DMA_Handle.Init.FIFOMode = DMA_FIFOMODE_DISABLE; /* 禁止FIFO*/