目录
一、初始化步骤
二、PWM所需函数
三、PWM初始化与引脚使用
1、输出比较模式
2、极性的选择
3、初始化:
怎么给结构体赋初始值
4、引脚的使用(引脚定义表)
四、代码(PWM呼吸灯)
1、PWM.c
2、PWM.h
3、main.c
4、模式的选择(复用推挽输出)
5、PWM参数的计算
6、效果
一、初始化步骤
图1-1PWM基本结构
第一步,RCC开启时钟,把我们要用的TIM外设和GPIO外设时钟开打
第二步,配置时基单元,包括前面的时钟源选择和时基单元
第三步,配置输出比较单元,包括CCR的值,输出比较模式,极性选择,输出使能这些参数
第四步,配置GPIO,把PWM对应的GPIO口,初始化为复用推挽输出的配置
第五步,运行控制,启动计数器,就能输出PWM
二、PWM所需函数
TIM_OC(1234)Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
这四个函数就是用来配置输出比较模块的,OC就是Output Compare,输出比较,这四个函数是配置上一章节的PWM基本结构中的输出比较单元x4这一块的,一个函数配置一个单元
参数:TIMx,选择定时器;第二个,结构体,输出比较的那些参数
是用结构体来初始化输出比较单元的
补充:这个函数仅高级定时器使用,在使用高级定时器输出PWM时,需要调用这个函数,使能主输出,否则PWM将不能正常输出。
TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
是用来给输出比较结构体赋一个默认值
TIM_ForcedOC(1234)Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
使用来配置前置输出模式的,如果在运行中,想要暂停输出波形并且强制输出高或低电平,则可以使用此函数,但是使用情况不多,因为强制输出高电平和设置100%占空比是一样的
TIM_OC(1234)PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
这四个函数是用来配置CCR寄存器的预装功能(影子寄存器)的;影子寄存器:写入的值不会立刻生效,而是在更新事件才会发生。
TIM_OC(1234)FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
这四个函数是用来配置快速使能的
TIM_ClearOC(1234)Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
外部事件时清除REF信号
TIM_OC(1234)PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
TIM_OC(123)NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
是用来单独设置输出比较的极性的,带个N的就是高级定时器里互补通道的配置,OC4没有互补通道,所以没有OC4N的函数。在结构体初始化的那个函数里可以设置极性。
注:两个地方设置极性的作用是一样的,只不过是用结构体是一起初始化的。这里是单独的函数进行修改的。一般来说,结构体的参数,都会有一个单独的函数可以进行更改。这7个函数就是用来单独更改输出极性的。
TIM_CCxCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCx);
TIM_CCxNCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCxN);
是用来单独修改输出使能参数的。
TIM_SelectOCxM(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode);
选择输出比较模式,这个是用来单独更改输出比较模式的函数。
TIM_SetCompare(1234)(TIM_TypeDef* TIMx, uint16_t Compare1);重要
这四个函数是用来单独更改CCR寄存器值的函数。在运行的时候,更改占空比。
三、PWM初始化与引脚使用
在这次项目的使用引脚是PB6引脚
1、输出比较模式
图3-1强制输出模式
2、极性的选择
图3-2极性的选择
参数1:high,高极性,就是极性不翻转,REF波形直接输出;或者说是有效电平是高电平,REF有效时,输出高电平
参数2:low,低极性,就是REF电平取反,或者说是有效电平为低电平
3、初始化:
1、打开时钟,选择内部时钟,初始化时基单元,因为我们是使用PB6引脚,所以在这里我们使用的是TIM2定时器。
2、在时基单元初始化下,初始化输出比较单元,在TIM.h文件中,TIM_OC(x)Init的4个初始化函数,对应的4个输出比较单元/输出比较通道,需要哪个初始化哪个通道,就调用哪个函数,不同通道的对应GPIO口也是不一样的,按照GPIO口需求来。我们使用的是PB6口,所以对应的是OC1_Init函数
图3-3初始化
注:在这里,我们这个结构体现在没有给所有的成员赋值,对于这个结构体来说,它现在是一个局部变量,如果不给他的成员赋初始值,它成员的值就是不确定的,会导致出现问题。
例:想把高级定时器当做定时器输出PWM时,自然会把TIM2、3、4改成为TIM1,这样的话,这个结构体原本用不到的成员,现在就需要使用到了。而这些之前没有使用到的成员你没有赋值,就会导致高级定时器输出PWM出现一些奇怪的问题。
问题例子:像高级定时器输出4路PWM,如果把初始化函数放在程序的第一行,则不会出现问题,如果初始化函数之前出现了其他代码,那4路PWM就会有3路不能输出。
原因:就是结构体成员没有配置完整,也没有给结构体赋初始值
正确做法:
方法1:把结构体所有成员都配置完整
方法2:先给结构体成员都赋一个初始值,在修改部分结构体成员
怎么给结构体赋初始值
使用Struct_Init函数,Struct_Init函数,就是用来给结构体赋初始值的
用途:使用Struct_Init赋一个初始值,再更改所需要的值即可,无需一一列出所有成员。
4、引脚的使用(引脚定义表)
图3-4引脚定义表
引脚定义表:默认复用功能就是片上外设的端口和GPIO的连接关系,如PA0,有TIM2_CH1_ETR,说明TIM2的ETRE引脚和通道1的引脚,都是借用了PA0这个引脚的位置的。
注:我们要使用TIM2的OC1也就是CH1通道,输出PWM,只能在PA0的引脚上输出,不能任意选择引脚输出,这些关系是定死的,是不能改变的。
重映射:如PA2,如果你想用USART2的TX引脚,又要使用TIM2的CH3通道,无法同时使用,则可以在重定义列表中寻找,如果重定义的列表找不到,那外设复用的GPIO就不能挪位置。配置重映射是用AFIO来完成。
四、代码(PWM呼吸灯)
1、PWM.c
#include "stm32f10x.h" // Device headervoid PWM_Init(void){RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);//注意是开启APB1的时钟函数,因为TIM2是APB1总线的外设RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//选择时基单元的时钟GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStructure);TIM_InternalClockConfig(TIM4);//定时器上电后默认是使用内部时钟,如果不调用,也是使用内部时钟,可以不写//TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00);//通过ETR引脚的外部时钟模式2配置TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision =TIM_CKD_DIV1;//指定时钟分频TIM_TimeBaseInitStructure.TIM_CounterMode =TIM_CounterMode_Up;//计数器模式TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //周期,ARR自动重装器的值TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;//PSC 预分频器的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器的值,是高级计数器才有的TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);//配置时基单元TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);//给结构体赋初始值,不用一一列出TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1 ;//设置输出比较的模式TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCPolarity_High ;//设置输出比较的极性TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能TIM_OCInitStructure.TIM_Pulse = 0;//设置CCRTIM_OC1Init(TIM4, &TIM_OCInitStructure);//周期ARR;预分频PSC;CCR这三个值,共同决定输出PWM的周期和占空比TIM_Cmd(TIM4,ENABLE);//启动定时器}void PWM_Setcompare1(uint16_t Compare){TIM_SetCompare1(TIM4, Compare);}
2、PWM.h
#ifndef __PWM_H#define __PWM_Hvoid PWM_Init(void);void PWM_Setcompare1(uint16_t Comparel);#endif
3、main.c
#include "stm32f10x.h" // Device header#include "Delay.h"#include "Timer.h"//#include "OLED.h"#include "PWM.h"uint8_t i;//定义一个16位得全局变量Numint main(void){OLED_Init(); PWM_Init();while(1){for (i = 0;i <= 100; i++){PWM_Setcompare1(i);//设置CCR寄存器的值,并不是直接占空比,占空比是CCR和ARR+1决定的Delay_ms(10);}for (i = 0;i <= 100; i++){PWM_Setcompare1(100 - i);Delay_ms(10);}}}
4、模式的选择(复用推挽输出)
在PWM.c中,我们选择的GPIO口是PB6引脚,选择的是TIM4定时器(普通定时器),当中的模式我们选择的是复用推挽输出。
图4-1开漏/推挽输出
图4-2复用开漏/推挽输出
由图4-1可以知道普通开漏/推挽输出:引脚的控制权是来自于输出数据寄存器
如果想用定时器的控制引脚,需要使用复用开漏/推挽输出的模式,这里的输出数据寄存器被断开输出控制权将转移给片上外设,片上外设连接的TIM2的CH1通道,所以只有GPIO设置成复用推挽输出,引脚控制权才能交给片上外设,PWM波形才能通过引脚输出。
5、PWM参数的计算
图4-3参数计算
周期ARR;预分频PSC;CCR这三个值,共同决定输出PWM的周期和占空比
假设设置一个1KHz,占空比50%,分辨率为1%的PWM波形
ARR = 99CCR = 50 PSC+1 = 720
程序中,我们需要做的是呼吸灯,所以我们把CCR设置为0,在main.c中使用for函数达到呼吸灯的效果
图4-4for函数
6、效果
呼吸灯
本章写的是PWM的使用,项目是使用PWM的呼吸灯,在参考本章内容中,如果遇到不懂的可以参考上三章的内容
TIM定时中断(定时器介绍)_tz得像个小孩的博客-CSDN博客_tim计时器
定时器定时中断&定时器外部时钟_tz得像个小孩的博客-CSDN博客
TIM输出比较(PWM)_tz得像个小孩的博客-CSDN博客
如果觉得《PWM的驱动使用(呼吸灯)》对你有帮助,请点赞、收藏,并留下你的观点哦!