学习STM32第六课:制作流畅呼吸效果的LED灯

        在STM32的第六课,我们来学习如何通过STM32来输出PWM波形,进而控制LED灯实现呼吸灯的一个效果,相信大家学会的话,也会是一个比较有意思的事情。

 1.1 PWM简介

         PWMPulse-width modulation)是脉冲宽度调制的缩写。脉冲宽度调制是一种模拟信号电平数字编码方法。脉冲宽度调制PWM是通过将有效的电信号分散成离散形式从而来降低电信号所传递的平均功率的一种方式。PWM是脉冲宽度调制,具有两个非常重要的参数: 频率和占空比。 频率和周期是互为倒数的,占空比是指一个周期内高电平所占的比例。 PWM信号就是根据需求调节占空比的大小以实现负载端电压的线性变化。

        PWM在各种领域中有广泛的应用,以下是几个常见的应用领域:

        电机控制:PWM被广泛应用于电机驱动控制中,通过调整PWM信号的占空比来控制电机的转速和扭矩。
        照明系统:PWM可用于LED照明系统,通过调节PWM信号的占空比来控制LED的亮度。
        电源管理:PWM被用于开关电源中,通过调节PWM信号的占空比来实现高效的能量转换和稳定的输出电压。
        音频处理:PWM也被应用于音频数字模拟转换(DAC)中,通过调节PWM信号的占空比来产生模拟音频信号。
        无线通信:PWM被用于调制无线通信系统中的载波信号,如无线电、蓝牙和红外线通信等。

 1.2 电路接线方法

        本次电路接线方法非常简单,仅仅把小灯泡接好就行,LED小灯泡的正极接到PA0端口上即可。 

 1.3 代码编写思路

         首先,在标准固件库文件里面创建一个Hardware文件夹,同时在keil里面也创建一个名为Hardware文件夹,用于存放个人创建的LED呼吸的驱动文件。

        其次,创建PWM.c和PWM.h文件。PWM.c文件主要用于存放LED呼吸灯的驱动代码,LED.h主要用于外部引用。

        在PWM.c文件中,我们首先添加一个头文件#include "stm32f10x.h"  ,这是每一个驱动文件和主函数文件里面必备的头文件。PWM是通过时钟进行配置的,所以一开始我们要打开TIM2和GPIOA的时钟。

    //打开TIM2,GPIOA的时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

 然后定义GPIOA的各种参数体,GPIOA口的引脚模式调整为复用推挽模式;采用50Hz的传输速率,代码结构体如下:

	GPIO_InitTypeDef GPIO_InitStructure;  //定义InitStructure结构体
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //定义为复用推挽模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;		//GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  //设置传输速率为50M Hz
	GPIO_Init(GPIOA, &GPIO_InitStructure);

然后就初始化TIM2时钟,配置时钟的各种参数结构体。(注意:我们只需要配置如下代码所示的结构体即可,其余的涉及高级计时器暂不用配置。)

    //定义TIM_TimeBaseInitStructure结构体
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;  
    //选择时钟模式,选择TIM_CKD_DIV1模式即可
	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(TIM2, &TIM_TimeBaseInitStructure);

对于代码中数值的设置有一套计算方法,大家会计算即可。

         接下来,调用TIM_OCInitTypeDef函数定义结构体,初始化定义各个结构体的初始参数。

        不啰嗦了,直接上代码。

PWM.c

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:PWM初始化
  * 参    数:无
  * 返 回 值:无
  */
void PWM_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	/*GPIO重映射*/
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);			//开启AFIO的时钟,重映射必须先开启AFIO的时钟
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);			//将TIM2的引脚部分重映射,具体的映射方案需查看参考手册
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);		//将JTAG引脚失能,作为普通GPIO引脚使用
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;		//GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							//将PA0引脚初始化为复用推挽输出	
																	//受外设控制的引脚,均需要配置为复用模式		
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*时基单元初始化*/
	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(TIM2, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
	
	/*输出比较初始化*/
	TIM_OCInitTypeDef TIM_OCInitStructure;							//定义结构体变量
	TIM_OCStructInit(&TIM_OCInitStructure);							//结构体初始化,若结构体没有完整赋值
																	//则最好执行此函数,给结构体所有成员都赋一个默认值
																	//避免结构体初值不确定的问题
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;				//输出比较模式,选择PWM模式1
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;		//输出极性,选择为高,若选择极性为低,则输出高低电平取反
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	//输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;								//初始的CCR值
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);						//将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1
	
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

/**
  * 函    数:PWM设置CCR
  * 参    数:Compare 要写入的CCR的值,范围:0~100
  * 返 回 值:无
  * 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比
  *           占空比Duty = CCR / (ARR + 1)
  */
void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);		//设置CCR1的值
}


void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);
}

 PWM.h

#ifndef __PWM_H
#define __PWM_H

void PWM_Init(void);
void PWM_SetCompare1(uint16_t Compare);

#endif

        出现报错的话不用理会,或者实在看不下去,该文件加上#include "stm32f10x.h"头文件即可

         对于主函数main.c函数编写的话,我们想要实现呼吸灯的一个效果,实际上只需要慢慢调整占空比即可,占空比越大,LED灯就越亮;反之,则越暗。我们可以定义一个参数i,在while循环里面,使用for循环函数慢慢循环增大PWM的占空比即可,代码如下:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"

uint8_t i;			//定义for循环的变量

int main(void)
{
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	PWM_Init();			//PWM初始化
	
	while (1)
	{
		for (i = 0; i <= 100; i++)
		{
			PWM_SetCompare1(i);			//依次将定时器的CCR寄存器设置为0~100,PWM占空比逐渐增大,LED逐渐变亮
			Delay_ms(10);				//延时10ms
		}
		for (i = 0; i <= 100; i++)
		{
			PWM_SetCompare1(100 - i);	//依次将定时器的CCR寄存器设置为100~0,PWM占空比逐渐减小,LED逐渐变暗
			Delay_ms(10);				//延时10ms
		}
	}
}

1.4 实操效果展示

与君共勉 დ,关注我,持续更新中~ 

物联沃分享整理
物联沃-IOTWORD物联网 » 学习STM32第六课:制作流畅呼吸效果的LED灯

发表评论