STM32F4--PWM控制LED忽明忽暗(呼吸灯)

一、实验原理

STM32F4--PWM控制LED忽明忽暗(呼吸灯)_第1张图片

 分析:时钟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的时钟。

STM32F4--PWM控制LED忽明忽暗(呼吸灯)_第2张图片

此外,这里的PF9不再作为普通的输入输出I/O口,而是作为定时器14这个内置外设的端口,自然要用到端口复用,即将GPIOF9端口复用为定时器14的端口。

补充端口复用概念:

STM32F4 系列微控制器 IO 引脚通过一个复用器连接到内置外设或模块。该复用器一次只允
许一个外设的复用功能(AF)连接到对应的 IO 口。这样可以确保共用同一个 IO 引脚的外设之
间不会发生冲突。
每个 IO 引脚都有一个复用器,该复用器采用 16 路复用功能输入(AF0 到 AF15),可通过
GPIOx_AFRL(针对引脚 0-7)和 GPIOx_AFRH(针对引脚 8-15)寄存器对这些输入进行配置,每四 位控制一路复用:
1)完成复位后,所有 IO 都会连接到系统的复用功能 0(AF0)。
2)外设的复用功能映射到 AF1 到 AF13。
3)Cortex-M4 EVENTOUT 映射到 AF15。
STM32F4--PWM控制LED忽明忽暗(呼吸灯)_第3张图片
如上图:每一个引脚里面相当于有16个开关,就像单刀多掷开关,左边拨到哪个功能上,引脚x就被连接到了不同的外设。
第二步:初始化GPIOF
    //初始化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口输出速度

这里重点降设置上拉

同 STM32F1 一样,STM32F4 的 IO 可以由软件配置成如下 8 种模式中的任何一种:
1、输入浮空:一般多用于外部按键输入
2、输入上拉:没有输入信号的时候默认输入高电平
3、输入下拉:没有输入信号的时候默认输入低电平
4、模拟输入
5、开漏输出
6、推挽输出
7、推挽式复用功能
8、开漏式复用功能
硬件原理图:
STM32F4--PWM控制LED忽明忽暗(呼吸灯)_第4张图片
由硬件原理可知  LED0=0;   //点亮LED0,所以默认引脚PF9引脚为上拉,也就是为高电平,此时LED0出于熄灭状态,只有LED0熄灭(关于为什么设置上拉or下拉,这里只是个人理解,欢迎大家指正)
第三步:初始化定时器14
    //初始化定时器
	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

的周期。
第一个参数PSC:定时器分频系数,操作的是预分频寄存器(TIMx_PSC)。该寄存器用于设置对时钟进行分频,然后提供给计数器,作为计数器的时钟。
当 APB1 的时钟分频系数为1的时候,通用定时器 TIMx 的时钟就等于 APB1 的时钟,即42Mhz
当 APB1 的时钟分频系数不是1的时候 ,通用 定时器 TIMx 的时钟是 APB1 时钟的 2 倍,也就是84Mhz。
第二个参数自动重装载值:对应操作的是自动重装载寄存器(TIMx_ARR),作用是设置定时器计数到哪个值会溢出然后从新计数
例如分频系数为8400,那么时钟频率就是84Mhz,分频后计数器得到的时钟频率为84Mhz/8400=10000hz=10khz,也就是计数器计数一次的时间是1/10000s,自动重装值设置为5000,那么定时器周期就是1/10000*5000=0.5s。
第三个参数设置计数方式:可以设置为向上计数, 向下计数方式还有中央对齐计数方式,比较常用的是向上计数模式 TIM_CounterMode_Up 和向 下计数模式 TIM_CounterMode_Down。
第四个参数设置时钟分频因子,注意和时钟分频系数是两码事。
最后根据以上设置的参数初始化定时器14。
第四步:初始化TIM14 Channel1 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:低电平有效。

STM32F4--PWM控制LED忽明忽暗(呼吸灯)_第5张图片

 按照以上参数,初始化结构体

第五步:使能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);	//修改比较值,修改占空比
	}
}

你可能感兴趣的:(stm32,单片机,arm)