STM32教程:PWM的配置以及实现呼吸灯(基于STM32F103C8T6最小系统板标准库开发)*详细教程*

前言:

本文介绍了STM32怎么编写PWM,以及如何利用PWM实现LED呼吸灯效果的代码项目实战

结尾有代码源码附录

STM32定时器输出比较单元输出PWM原理:

用到了PWM输出,

STM32的PWM用到了定时器的输出比较模块

首先看一下PWM基本结构,方便我们编写程序代码,配置寄存器

大体流程:

第1步,RCC开启时钟,把需要的TIM外设和GPIO外设的时钟打开

第2步,配置时基单元,包括时钟源选择

第3步,配置输出比较单元,包括CCR的值、输出比较模式、极性选择、输出使能

第4步,配置GPIO,把PWM对应的GPIO口,初始化为复用推挽输出配置

第5步,运行控制,启动计数器,这样就能输出PWM了

以下是STM32标准库函数tim.h的一些关于输出比较模式的函数介绍(详细代码实战部分可以跳转目录后面的代码编写部分)

库函数stm32f10x_tim.h函数关于输出比较函数的介绍

void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

这四个函数用来配置输出比较模块(输出比较单元有4个,一个函数对应配置一个单元)

第一个参数:选择定时器

第二个参数:结构体(包含输出比较的参数)

void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);

这个函数用来给输出比较结构体赋一个默认值的

void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);

这个函数,仅高级定时器使用,在使用高级定时器输出PWM时,需要调用这个函数,使能主输出,否则PWM将不能正常输出

void TIM_ForcedOC1Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC2Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC3Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC4Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);

这四个函数,用来配置强制输出模式(如果在运行中想要暂停输出波形并且强制输出高或低电平)(一般用的不多)

void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC3PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC4PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);

这四个函数用来配置CCR寄存器的预装功能(影子寄存器:写入的值不会立即生效,而是更新事件才会生效)(一般也用不到)

void TIM_OC1FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC2FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC3FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC4FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);

这四个函数用来配置快速使能的(一般用的也不多)

void TIM_ClearOC1Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC2Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC3Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC4Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);

外部事件时清除REF信号(用的也不多)

void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC2PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC2NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC3PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC3NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC4PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);

这些用来单独(因为前面结构体里面就可以配置,这个函数只是用来单独修改的)设置输出比较极性的,函数名带N的就是高级定时器的互补通道的配置

void TIM_CCxCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCx);
void TIM_CCxNCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCxN);

用来单独修改输出使能参数的

void TIM_SelectOCxM(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode);

选择输出比较模式,用来单独更改输出比较模式的函数

void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);

这四个函数,用来单独修改CCR寄存器值的函数 ,在运行的时候,更改占空比 (**** 比较重要 ****)

代码编写

0.创建一个PWM.c文件,用来编写PWM代码

1.首先是开启RCC时钟

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);  //开启时钟

2.配置时基单元

注意:

PWM的占空比和频率周期是由CCR和RR共同决定的,所以配置时基单元和输出比较单元的时候,需要配置对应的参数

配置时基单元

	TIM_InternalClockConfig(TIM2);	   //TIM2选择为内部时钟

	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;   // 指定时钟分频,为1分频
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;   // 选择计数模式,为向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;           //周期,预重装器的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;         //预分频器的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;  //重复计数器的值,这里是高级定时器才需要,这里给0
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);       //传入结构体,初始化

3.初始化输出比较单元

在库函数tim.h里找到初始化函数(上面库函数也有介绍过),有四个函数,对应四个输出比较通道,根据需求来,这里我们用GPIO口的PA0,我们用第一个函数

void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

复制粘贴,定义结构体配置,调用函数

以下是TIM_OCInitTypeDef结构体内部参数的分析

有一些机构体成员是配置高级定时器用的,与通用定时器无关。

TIM_OCMode有这几种模式

1、TIM_OCMode_Timing          冻结模式

2、TIM_OCMode_Active          相等时置有效电平

3、TIM_OCMode_Inactive       相等时置无效电平

4、TIM_OCMode_Toggle        相等时电平翻转

5、TIM_OCMode_PWM1         PWM模式1 

6、TIM_OCMode_PWM2         PWM模式2

TIM_OCPolarity有这几种模式

1、TIM_OCPolarity_High        高极性,极性不翻转,REF有效时,输出高电平

2、TIM_OCPolarity_Low        低极性,REF电平取反

TIM_OutputState有使能和失能模式

1、TIM_OutputState_Disable        失能

2、TIM_OutputState_Enable        使能

TIM_Pulse设置CCR捕获比较寄存器

可以是0x0000到0xFFFF的值

初始化输出比较单元

	TIM_OCInitTypeDef TIM_OCInitStructure;          //创建一个OC初始化结构体变量
	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 = 50;       //用来设置CCR
	TIM_OC1Init(TIM2,&TIM_OCInitStructure);

配置完输出比较单元,在TIM2的OC1通道上就可以输出PWM波形了

还要借助GPIO口输出,所以在配置一下GPIO

4.配置GPIO

打开STM32的引脚口定义表,找到TIM2的OC1通道用到的GPIO用到的是PA0口

注意:引脚输出模式选择复用推挽输出,引脚的输出控制才能交给TIM2的PWM输出

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    //注意:选择复用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);

这样子,PWM的配置就基本完成了,但是要实现呼吸灯效果,就需要不断更改CCR的值,来不断变换占空比,所以再封装一个更改CCR值的函数

5.封装不断变化修改CCR值的函数

用到库函数,可以修改CCR值的函数TIM_SetCompare1

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

6.创建PWM.h的头文件,声明函数

#ifndef __PWM_H
#define __PWM_H

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

#endif

7.主函数main.c里调用函数

这里用到for循环,加上Delay,简单应用一下,调用更改CCR值的函数,可以变换占空比,从而来实时变换呼吸灯的亮度,达到呼吸灯的效果。

程序现象:

可以看到,LED灯从亮到灭,从灭到暗,逐渐变化

STM32呼吸灯

示波器现象:

可以看到,示波器检测的PWM波占空比也是实时变化的

STM32呼吸灯示波器

附录:程序源码

1.PWM.c

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);  //开启时钟
	
	TIM_InternalClockConfig(TIM2);	   //TIM2选择为内部时钟

	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;   // 指定时钟分频,为1分频
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;   // 选择计数模式,为向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;           //周期,预重装器的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;         //预分频器的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;  //重复计数器的值,这里是高级定时器才需要,这里给0
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);       //传入结构体,初始化

	
	TIM_OCInitTypeDef TIM_OCInitStructure;          //创建一个OC初始化结构体变量
	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 = 50;       //用来设置CCR
	TIM_OC1Init(TIM2,&TIM_OCInitStructure);
	
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    //注意:选择复用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	
	TIM_Cmd(TIM2,ENABLE);      //启动定时器TIM2

}

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

2.PWM.h

#ifndef __PWM_H
#define __PWM_H

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


#endif

3.main.c

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

int main(void)
{
	PWM_Init();
	while(1)
	{
		for(uint8_t i=0;i<=100;i++)
		{
			PWM_SetCompare1(i);       //占空比逐渐变大,LED逐渐变亮
			Delay_ms(10);
		}
		for(uint8_t i=0;i<=100;i++)
		{
			PWM_SetCompare1(100-i);		//占空比逐渐变小,LED逐渐变暗
			Delay_ms(10);
		}																
	
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值