图片及文章内容摘自江科大自化协B站视频
前言
这一部分的学习完全不知道原理是很难写好代码的,这篇博客主要通过代码实现的方式叙述,在不需要理解原理的部分直接套用模板代码,按照模板书写容易出错的地方我会加以说明,目的是能在最少时间理解原理的条件下快速的使用STM32。
一、大致步骤
在直接展示代码之前,我先大概梳理一下用使用PWM的步骤,方便理解与总结后续的代码
第一步:开启RCC时钟,这里就是把TIM外设和GPIO外设的时钟打开
第二步:选择时钟源并配置时基单元
第三步:配置输出比较单元,包括CCR,输出比较模式等,套模板即可
第四步:配置GPIO为复用推挽输出模式,具体与PWM对应的引脚参考引脚定义表
第五步:运行控制,启动计数器
二、具体代码
1.呼吸灯
这里要用到的公式如下
其他的部分套用模板即可,具体易错点与不明白的点看代码注释,下面先放初始化函数,即一到四步
1. void PWM_Init(void) 2. { 3. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); 4. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); 5. 6. // RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); 7. // GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE); 8. // GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); 9. 10. GPIO_InitTypeDef GPIO_InitStructure; 11. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//想用定时器控制引脚,就要用复用输出 12. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO_Pin_15; 13. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 14. GPIO_Init(GPIOA, &GPIO_InitStructure); 15. 16. TIM_InternalClockConfig(TIM2); 17. 18. TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; 19. TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; 20. TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; 21. TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //ARR 22. TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; //PSC 23. TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; 24. TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); 25. 26. TIM_OCInitTypeDef TIM_OCInitStructure; 27. TIM_OCStructInit(&TIM_OCInitStructure);//如果对于每个变量不想都赋一遍值,则要用这个先帮你赋一遍默认值 28. TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; 29. TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; 30. TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; 31. TIM_OCInitStructure.TIM_Pulse = 0; //CCR 32. TIM_OC1Init(TIM2, &TIM_OCInitStructure); 33. 34. TIM_Cmd(TIM2, ENABLE); 35. }
上述代码我们就已经初始化好了GPIO与时钟,现在我们只需要改变CCR的值去不断改变占空比(高电平占总电平时间)就可以实现呼吸灯,代码如下。
1. void PWM_SetCompare1(uint16_t Compare) 2. { 3. TIM_SetCompare1(TIM2, Compare);//compare后面的1表示的是通道1,这个函数是用来改变CCR的,这里compare的值就是CCR的值 4. } 5. 6. uint8_t i; 7. 8. int main(void) 9. { 10. OLED_Init(); 11. PWM_Init(); 12. 13. while (1) 14. { 15. for (i = 0; i <= 100; i++) 16. { 17. PWM_SetCompare1(i); 18. Delay_ms(10); 19. } 20. for (i = 0; i <= 100; i++) 21. { 22. PWM_SetCompare1(100 - i); 23. Delay_ms(10); 24. } 25. } 26. }
2.引脚重映射
通过引脚定义表我们知道某个定时器的某个通道对应的引脚是固定的,但是单片机给了我们一次重定义的机会,这个过程就叫引脚重映射,代码如下。这里代码的二三行就是改变引脚功能的函数,函数的第一个参数填的数据参考手册。这里的第二个函数是修改定时器2的通道1,但是那个引脚本来就有调试功能,所以第三行是关闭他的调试功能(一般需要关这种功能的有PA15 PB3 PB4,具体看引脚定义表中该引脚的主功能)
1. RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//先打开AFIO的RCC 2. GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);//选择要重映射类型 3. GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//关闭重映射后引脚原来的调试功能
一个定时器是可以同时开多个通道的,但是开出后的频率一定相同,占空比可以各自设定,相位是同步的.
总结
以上的代码都是对于通用定时器的操作,这里补充一下高级定时器的一些不同操作
这个函数仅高级定时器输出PWM时需要调用,需要使能主输出,否则PWM将不能正常输出