一、实验原理
分析:时钟84Mhz,分频84,ARR设置500,计数器得到的时钟84M/84=1 Mhz,计数一次时间为0.5ms.在主函数中,我设置的修改时间是2ms一次,如图,设置PWM1模式,输出极性低电平有效。即当计数值小于比较值,输出低电平。修改前,CCR的值比较小,输出低电平的时间短,在2ms时间里,获得的平均电压小,LED亮度较暗。修改CCR的值,这样输出低电平的时间长,LED的平均电压高,灯光亮度变亮,以此类推,每隔2ms修改一次数值,低电平的占比越来越高,直到和ARR相等,达到最亮状态。然后递减,亮度接着慢慢变暗,循环往复,达到呼吸灯的效果。
二、程序配置分析
第一步:使能定时器14的时钟和GPIO口的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14,ENABLE); //开启定时器TIM14时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); //开启GPIOF的时钟,要使用某个IO口必须要对时钟进行使能
//不作为普通的引脚,而是作为定时器外设的引脚
GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14); //GPIOF9复用为定时器14
分析:首先,实验的目的是用PWM去控制LED的明暗,这里我们选用的是正点F407开发板,LED0(红灯)对应的引脚是PF9,查询芯片手册可知,PF9可作为定时器14的CH1通道的输出,因此这里选择使用定时器14,理所当然地,我们要开启GPIOF和定时器14的时钟。
此外,这里的PF9不再作为普通的输入输出I/O口,而是作为定时器14这个内置外设的端口,自然要用到端口复用,即将GPIOF9端口复用为定时器14的端口。
补充端口复用概念:
![](https://i-blog.csdnimg.cn/blog_migrate/7acbdb60e3554c22fd4adb9501b9a27e.png)
//初始化GPIOF9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //GPIOF9
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOF,&GPIO_InitStructure); //初始化PF9
分析:首先选择初始化的引脚,这里选择PF9,然后设置为复用功能,设置I/O口输出速度
这里重点降设置上拉
![](https://i-blog.csdnimg.cn/blog_migrate/df85b454843e082c671230ad35df4ee5.png)
//初始化定时器
TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;//时钟分频因子
TIM_TimeBaseInit(TIM14,&TIM_TimeBaseStructure);//初始化定时器14
分析:在开启了 TIM14 的时钟之后,我们要设置 ARR 和 PSC 两个寄存器的值来控制输出 PWM
//初始化TIM14 Channel1 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能 CCER
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
TIM_OC1Init(TIM14, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM1 4OC1
分析:设置 TIM14_CH1 为 PWM 模式(默认是冻结的),因为我们的 DS0 是低电 平亮,而我们希望当 CCR1 的值小的时候,DS0 就暗,CCR1 值大的时候,DS0 就亮,所以我 们要通过配置 TIM14_CCMR1 的相关位来控制 TIM14_CH1 的模式。在库函数中,PWM 通道 设置是通过函数 TIM_OC1Init()~TIM_OC4Init()来设置的,不同的通道的设置函数不一样,这里 我们使用的是通道 1,所以使用的函数是 TIM_OC1Init()。
第一个参数:设置模式是 PWM 还是输出比较,这里我们是 PWM1模式时,在向上计数时候
当计时器值小于比较器设定值时,则TIMX输出脚此时输出有效电位。
当计时器值大于或等于比较器设定值时,则TIMX输出脚此时输出无效电位。
此时输出的电平高低还是不确定的,只是设置输出在低于或者高于比较值CCR1时是否有效。
第二个参数:CCER:CC1E位:输入/捕获1输出使能。0:关闭,1:打开。要想 PWM 从 IO 口输出,这个位必须设置为 1
第三个参数:具体输出是高电平还是低电平,要设置TIM14_ CCER 寄存器中的CCER:CC1P位:输入/捕获1输出极性。0:高电平有效,1:低电平有效。
按照以上参数,初始化结构体
第五步:使能TIM14在CCR1上的预装载寄存器
TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable); //使能TIM14在CCR1上的预装载寄存器
分析:4个通道的捕获/比较寄存器,有两个寄存器,CCRx的预装载寄存器和影子寄存器 ,从CCRx的预装载寄存器传送到影子寄存器,有两种方式:一种是修改后立即执行新的比较值,一种是在下一个周期改变比较值。这个函数的作用是设定以哪种方式更新比较值CCR1
第六步:ARPE使能
TIM_ARRPreloadConfig(TIM14,ENABLE);//ARPE使能
分析:主要作用是ARR预装载寄存器传送到影子寄存器。这个函数的作用是确定以何种方式修改的ARR,也就是设定的计数值.TIM_ARRPreloadConfig函数是修改CR1->APRE位 根 据 TIMx_CR1 寄存器中 APRE 位的设置:APRE=0 时,预装载寄存器的内容可以随时传送到影 子寄存器,此时 2 者是连通的;而 APRE=1 时,在每一次更新事件(UEV)时,才把预装载寄 存器(ARR)的内容传送到影子寄存器。具体可看:http://t.csdn.cn/6U3Mh
第七步:使能TIM14,配置都好了,还得打开开关,相当于家里装修好了,现在要开门接客了。
TIM_Cmd(TIM14, ENABLE); //使能TIM14
---------------------------------------------------------------------------------------------------------------------------------
完 整 代 码
---------------------------------------------------------------------------------------------------------------------------------
初始化代码:
void TIM14_PWM_Init(u32 arr,u32 psc)
{
//此部分需手动修改IO口设置
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14,ENABLE); //开启定时器TIM14时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); //开启GPIOF的时钟,要使用某个IO口必须要对时钟进行使能
//不作为普通的引脚,而是作为定时器外设的引脚
GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14); //GPIOF9复用为定时器14
//初始化GPIOF9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //GPIOF9
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOF,&GPIO_InitStructure); //初始化PF9
//初始化定时器
TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;//时钟分频因子
TIM_TimeBaseInit(TIM14,&TIM_TimeBaseStructure);//初始化定时器14
//初始化TIM14 Channel1 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //TIM14_CCRM1_OCM1[2:0]选择定时器模式:TIM脉冲宽度调制模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //TIM14_CC1E_比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //TIM14_CCER_CC1P 输出极性:TIM输出比较极性低
TIM_OC1Init(TIM14, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM1 4OC1
TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable); //TIM14_CCRM1_OC1P1 使能TIM14在CCR1上的预装载寄存器
TIM_ARRPreloadConfig(TIM14,ENABLE);//ARPE使能 --TIMx_CR1_ARPE 寄存器
TIM_Cmd(TIM14, ENABLE); //使能TIM14---TIMx_CR1_CEN 寄存器
}
主函数代码:
int main(void)
{
u16 led0pwmval=0;
u8 dir=1;
//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
delay_init(168); //初始化延时函数
//uart_init(115200);//初始化串口波特率为115200
TIM14_PWM_Init(500-1,84-1); //84M/84=1Mhz的计数频率,重装载值500,所以PWM频率为 1M/500=2Khz.
while(1) //实现比较值从0-300递增,到300后从300-0递减,循环
{
delay_ms(2); //不延时还真不行
if(dir) led0pwmval++;//dir==1 led0pwmval递增
else led0pwmval--; //dir==0 led0pwmval递减
if(led0pwmval>300)dir=0;//led0pwmval到达300后,方向为递减
if(led0pwmval==0)dir=1; //led0pwmval递减到0后,方向改为递增
TIM_SetCompare1(TIM14,led0pwmval); //修改比较值,修改占空比
}
}