通用定时器 ----输出
1,输出一个PWM
2,检测脉冲宽度
1》PWM---脉冲宽度调制
占空比:高电平占整个周期的百分比
2》PWM作用:调节灯的亮度,声音的大小,速度的快慢----平均电压值
什么是PWM信号?
PWM,英文名Pulse Width Modulation,是脉冲宽度调制(记住这个名词)缩写,它是通过对一系列脉冲的宽度进行调制,等效出所需要的波形(包含形状以及幅值),对模拟信号电平进行数字编码,也就是说通过调节占空比的变化来调节信号、能量等的变化,占空比就是指在一个周期内,信号处于高电平的时间占据整个信号周期的百分比,例如方波的占空比就是50%.
PWM脉冲宽度调制,实际上就是脉冲信号,但是这个脉冲信号的高/低电平在一个周期所占时间可以调节。
占空比:
是指高电平在一个周期之内所占的时间比率,方波的占空比为50%,占空比为0.5,说明正电平所占时间为0.5个周期。若信号的周期为T,每周期高电平时间为t1,低电平时间为t2,T=t1+t2,则占空比D=t1/T。
当我们思考pwm给外设供电的时候,可以从能量的角度去思考。高电平*其持续的时间 = 提供的能量,所以占空比越大,提供的能量就越大,此时外设工作的动能就越大。
但是,我们在思考pwm的时候,必须要考虑周期。如下图:
请问:这两个pwm提供的总能量都一样,其占空比也一样。但是它们对外设的影响有什么区别?
它俩的周期不一样,虽然对外设提供的总能量大小一样,但是对外设提供能量的频率却不一样。一个是隔一小段时间提供一小段能量,而另外一个却是,持续的将能量提供完。不同的能量供应导致外设的工作状态就会有所不同。
这也就是为什么,对LED灯提供PWM供电。当占空比变大时,LED灯就会变亮,当占空比变小时,LED灯就会变暗。因为,此时LED是隔一小段时间亮一小段,增大占空比,就是增大能量的供给。由于电能转换为光能,电能供给增大,光的亮度就增大。注意:我们人眼对光识别得频率为50hz,PWM的频率要大于我们人眼识别频率,要不然会出现闪烁现象。
片上外设PWM模块的好处是什么?
如果MCU没有内置pwm模块,一般我们用GPIO输出高/低电平来模拟PWM。当我需要输出pwm时,就进入中断,用GPIO来模拟输出PWM信号,但是,由于在中断中,那么此时MCU就只能执行这个中断,而不能做其他事情,MCU的资源就被占用了。
内置的片上外设pwm模块是用单独的硬件模块去自动生成输出pwm波形,而不是利用MCU去控制GPIO模拟的pwm。所以此时MCU的资源并没有被占用。如下图:
通用定时器
想要使用PWM,我们需要使用通用定时器来对PWM的脉宽进行调制。
向上/向下自动装载计数器:就是中央对齐模式。
4个独立通道:PWM模块有4个输出通道,不同的通道在不同的引脚上,它们互不干扰,可以各自输出不同频率、不同占空比的PWM波形
注意:通道TIMx的4路通道,如果某一个通道用作输出,那么该通道就不可以用作输入。用作输入,就不可以用作输出。所以,图中看似有输入、输出一共8路通道,实际上,只有4路。
捕获/比较寄存器(CCR):(CCR寄存器是16位的)
一开始捕获/比较寄存器(CCR)输出的是低电平(高电平),然后我们给捕获/比较寄存器中赋一个数值。当16为计数器(CNT)中的值计数超过捕获/比较寄存器中的数值的时候,捕获/比较寄存器就会输出一个高电平(低电平)。当计数器计数溢出后,重新开始计数,计数器的数值又低于捕获/比较寄存器中的数值了,那么此时捕获/比较寄存器的输出就会恢复原来的状态,即输出一个低电平(高电平)。然后,如此反复。这个实际上就是PWM波的形成过程。如下图:
有效/无效电平:有效/无效电平是我们根据外部的外设来设置的,有效电平就是可以使外部的外设正常工作的电平,无效电平就是不能使外部的外设正常工作的电平。
PWM波的输出有4种方式:(我们设置低电平为有效电平,ARR=100,CRR=60)
1. PWM1递增方式:
CNT计数器递增方式计数(从0开始递增计数),我们的有效电平是低电平。 一开始CNT计数器中的数值小于CRR的数值60,所以输出有效电平——低电平。当CNT计数器中的数值大于CRR的数值60的时候,输出无效电平——高电平。然后,当CNT计数器计数溢出的时候,计数器重新开始计数,此时CNT计数器中的数值小于CRR的数值60,此时又输出了有效电平——低电平。反复如此。
2. PWM1递减方式:
CNT计数器递减方式计数(从设定值开始递减计数到0),我们的有效电平是低电平。 一开始CNT计数器中的数值大于CRR的数值60,所以输出无效电平——高电平。当CNT计数器中的数值小于CRR的数值60的时候,输出有效电平——低电平。然后,当CNT计数器计数到0的时候,计数器重新开始计数,此时CNT计数器中的数值大于CRR的数值60,此时又输出了无效电平——高电平。反复如此。
3. PWM2递增方式:
CNT计数器递增方式计数(从0开始递增计数),我们的有效电平是低电平。 但是与PWM1的不一样,CNT计数器中的数值小于CRR的数值60,输出的是无效电平。而CNT计数器中的数值大于CRR的数值60,输出的是有效电平。所以,一开始CNT计数器中的数值小于CRR的数值60,所以输出无效电平——高电平。当CNT计数器中的数值大于CRR的数值60的时候,输出有效电平——低电平。然后,当CNT计数器计数溢出的时候,计数器重新开始计数,此时CNT计数器中的数值小于CRR的数值60,此时又输出了无效电平——高电平。反复如此。
4. PWM2递减方式:
CNT计数器递减方式计数(从设定值开始递减计数到0),我们的有效电平是低电平。 但是与PWM1的不一样,CNT计数器中的数值大于CRR的数值60,输出的是有效电平。而CNT计数器中的数值大于CRR的数值60,输出的是无效电平。所以,一开始CNT计数器中的数值大于CRR的数值60,所以输出有效电平——低电平。当CNT计数器中的数值小于CRR的数值60的时候,输出无效电平——高电平。然后,当CNT计数器计数到0的时候,计数器重新开始计数,此时CNT计数器中的数值大于CRR的数值60,此时又输出了有效电平——低电平。反复如此。
注意:输出的高低电平,是由捕获/比较寄存器后面的输出控制单元输出的。
影响PWM波形的因素:
1.模式PWM1或PWM2
2.计数方式:向上计数、向下计数、中央对齐计数
3.CCR寄存器中的数值(改变CRR寄存器中设置的数值,就可以改变PWM波形的占空比)
4.ARR寄存器中的数值(改变ARR寄存器中设置的数值,就可以改变PWM波形的周期/频率
5.有效电平的设置
6.PSC预分频器
重映射:
如果,我们的原理图上面,包含TIM3模块的引脚被其他外设占用了,我们就只能使用重映射,将TIM3的功能映射到其他引脚上面,使用其他引脚来驱动设备。
现在,我们想要使用PC6~PC8来驱动LED灯进行呼吸灯,需要使用到TIM3的CH1~CH3通道。但是TIM3的CH1~CH3通道却是在PA6 PA7 PB0引脚上面的复用功能。所以我们需要使用重映射,将TIM3的CH1~CH3通道功能映射到PC6~PC8上面。如下图:
我们使用完全重映射就可以将TIM3的CH1~CH3通道,映射到PC6~PC8上面。这样,PC6~PC8引脚就可以使用IM3的CH1~CH3功能了。
预装载寄存器----影子寄存器:
我们在图中便可以看到影子寄存器,图中红框框住的黑色阴影就是影子寄存器。其它寄存器没有阴影,说明其他寄存器没有影子寄存器。
这个寄存器是实际存在的寄存器,但是是你不可以直接操作的寄存器。实际上真正参加工作的寄存器,是影子寄存器而不是我们设置的ARR、CCR寄存器,ARR、CCR寄存器只是相当于一个缓冲寄存器。我们把数值设置到ARR、CCR寄存器中,在当前周期完成后(即:计数器计数溢出后),ARR、CCR寄存器中的数值才会传给它们各自的影子寄存器。在进数值行比较和数值修改时,真正起作用和参与数值比较等一系列工作的是影子寄存器,而不是ARR、CCR寄存器。
注意:当你需要改变周期值和比较值时,影子寄存器不会当即发生改变,会在当前周期完成后(即:计数器计数溢出后),它才会被改变。也就是说,需要等当前周期完成后,我们修改的值才会起作用。
编程步骤:
1,打开时钟----GPIOC,TIM3,AFIO
2,初始化GPIOC
----GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8
----复用推挽输出
----速度---50MHZ
3,完全重映射TIM3----将GPIOC6,7,8的功能给TIM3控制
4,初始化定时器3
----ARR的值:255
----预分频器的值:1-1
----计数方式:向上
5,输出通道初始化
----通道1,2,3
----模式:PWM1
----有效电平:低电平
----捕获比较寄存器的值:0
6,使能ARR的预装载寄存器
7,使能通道1,2,3的预装载寄存器
8,使能定时器3
一些问题:
1.为什么在初始化的时候,CCR的值设置为0?
因为,在CNT计数器计数方式为向上计数,且在PWM1模式下。此时CCR的值如果设置为0的话,那么CNT计数器的值会一直大于0,这样的话,通道输出的一直就是无效电平——高电平,那么此时不会产生PWM波形(因为电平一直是某种状态)。当我们想要TIM输出有效电平的时候或者说我们想要输出PWM的时候,我们再来将CRR的值进行改变,此时PWM波形就会产生了。也就是说,当我们想要输出PWM波的时候,再去产生PWM波。下图是设置TIM1~TIM3通道的CCR寄存器的库函数:
有可能我们会在程序运行的过程中临时改变CRR中的值,那么就用这个函数来改变。
下图是设置ARR寄存器中数值的库函数:
有可能我们会在程序运行的过程中临时改变ARR中的值,那么就用这个函数来改变。
注意:下图中的库函数是改变CNT计数器中的值而不是ARR中的值
2.为什么PSC预分频器的值要写成1-1?
因为它内部的值会自动加1,所以如果我们设置1分频,那么它内部就会自动+1,变成2分频。所以,我们这里设置为0,那么0+1= 1,即1分频。
编写程序:
void TIM3_PWM_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_OCInitTypeDef TIM_OCInitStruct;
// 1,打开时钟----GPIOC,TIM3,AFIO ,注意:用到复用功能,就需要打开复用功能的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOC,ENABLE);
// 2,初始化GPIOC
GPIO_InitStruct.GPIO_Pin =GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8;
GPIO_InitStruct.GPIO_Mode =GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed =GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStruct);
// 3,完全重映射TIM3----将GPIOC6,7,8的功能给TIM3控制
GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE);
// 4,初始化定时器3
TIM_TimeBaseInitStruct.TIM_CounterMode =TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period =255; //ARR寄存器的值为255
TIM_TimeBaseInitStruct.TIM_Prescaler =1-1; //1分频
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
// 5,输出通道初始化
TIM_OCInitStruct.TIM_OCMode =TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OCPolarity =TIM_OCPolarity_Low;
TIM_OCInitStruct.TIM_OutputState =TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse =0;
TIM_OC1Init(TIM3,&TIM_OCInitStruct);
TIM_OC2Init(TIM3,&TIM_OCInitStruct);
TIM_OC3Init(TIM3,&TIM_OCInitStruct);
// 6,使能ARR的预装载寄存器
TIM_ARRPreloadConfig(TIM3,ENABLE);
// 7,使能通道1,2,3的预装载寄存器
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
// 8,使能定时器3
TIM_Cmd(TIM3,ENABLE);
}
//通过PWM的占空比来控制三个小灯的不同亮度
void PWM_CompareValue(uint32_t R,uint32_t G,uint32_t B)
{
TIM_SetCompare1(TIM3, B); //设置通道1中CCR寄存器中的数值,即改变通道1输出PWM波的占空比
TIM_SetCompare2(TIM3, G);
TIM_SetCompare3(TIM3, R);
}
int main(void)
{
RCC_ConfigTo72M();//将系统时钟配置成72MHZ
Systick_Config(72);
TIM3_PWM_Config(); /*注意:由于PWM的配置需要用到完全重映射,可能会导致与串口配置产生冲突,所以如果用到串口,我们需要将TIM3的配置写在串口配置之前,否则可能无法产生PWM波形*/
while(1){
PWM_CompareValue(136, 49, 121); /*不同的占空比,LED的亮度不同,通道1的CRR寄存器的值设置为136,通道2的CRR寄存器的值设置为49,通道3的CRR寄存器的值设置为121*/
}
}
注意:在输出PWM波形的过程中,如果此时发送某个中断,那么中断执行的时间不能长,也不要在中断中加延时。否则当执行完中断后,回到main中的PWM输出,此时PWM无法正常输出。
呼吸灯代码
uint8_t red=0;
uint8_t blue=0;
int main(void)
{
RCC_ConfigTo72M();//将系统时钟配置成72MHZ
Systick_Config(72);
TIM3_PWM_Config();
while(1){
if(redflag==0){
red++;
blue++;
if(red==255){
redflag=1;
}
}else{
red--;
blue--;
if(red<10){
redflag=0;
}
}
PWM_CompareValue(red, 0, blue);
Systick_NmsDelay(10); //延时10ms
}
}