PWM驱动LED呼吸灯
本文最后更新于31天前,其中的信息可能已经有所发展或是发生改变。

配置PWM

第一步,开启RCC时钟,把TIM外设和GPIO的外设时钟打开
第二步,配置时基单元,包括时钟源选择
第三步,配置输出比较单元,包括CCR的值、输出比较模式、极性选择、输出使能这些参数,库函数里也是统一用结构体来配置的
第四步,配置GPIO,把PWM对应的GPIO口,初始化复用推挽输出的配置
第五步,运行控制,启动计数器,这样才能输出PWM

初始化GPIO口输出PWM波形

初始化时基单元
初始化输出比较单元
设置输出比较的模式
设置输出使能
设置CCR

  TIM_InternalClockConfig(TIM2);// 初始化时基单元
  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);
  //初始化输出比较单元,参考stm32f10x_tim.h
  /*
  有4个初始化函数,对应4个输出比较单元(输出比较通道)
  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);
  需要初始化哪个通道,就调用哪个函数,不同的通道对应的GPIO口也是不一样的
  本例中,使用PA0口,对应的是第一个输出比较通道。
  */
  TIM_OCInitTypeDef TIM_OCInitStructure;
  TIM_OCStructInit(&TIM_OCInitStructure);
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //设置输出比较的模式
  /*
  #define TIM_OCMode_Timing                 冻结模式
  #define TIM_OCMode_Active                 相等时置有效电平
  #define TIM_OCMode_Inactive               相等时置无效电平
  #define TIM_OCMode_Toggle                 相等时电平反转
  #define TIM_OCMode_PWM1                   PWM模式一
  #define TIM_OCMode_PWM2                   PWM模式二
  TIM_ForcedAction_Active和TIM_ForcedAction_InActive 强制输出的两种模式
  */
  TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCPolarity_High;// 设置输出比较的极性
  /*
  #define TIM_OCPolarity_High               高极性,有效电平是高电平,就是极性不翻转,REF波形直接输出
  #define TIM_OCPolarity_Low                 低极性,有效电平为低电平,就是REF电平取反
  */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能
  /*
  #define TIM_OutputState_Disable           失能
  #define TIM_OutputState_Enable             使能
  */
  TIM_OCInitStructure.TIM_Pulse =50 ;//脉冲,设置CCR
  //TIM_OCInitStructure.TIM_Pulse =0 ;//脉冲,设置CCR
  //实现呼吸灯的效果,就是不断更改CCR的值即可
  /*
  72M/(PSC+1)/(ARR+1) = 1000
  CRR/(ARR+1) = 50%
  1/(ARR+1) = 1%
  解方程,ARR+1 = 100,PSC+1 = 720 ,CCR=50
  现在就是频率为1KHz,占空比为50%的PWM波形了
  */
  TIM_OC1Init(TIM2,&TIM_OCInitStructure);//初始化输出比较单元
  TIM_Cmd(TIM2, ENABLE);
 ​

端口复用

参考引脚定义表中默认复用功能,就是片上外设的端口和GPIO的连接关系,在这里可以看到,有TIM2_CH1_ETR(外部触发输入)它是在这个PA0这一行的。这就说明,TIM2的ETR引脚和通道1的引脚,都是借用了PA0这个引脚的位置的,换句话说就是TM2的引脚复用在了PA0引脚上,所以说如果我们要使用TIM2的OC1也就是CH1通道,输出PWM,那它就只能在PA0的脚上输出一而不能任意选择引脚输出。同样,如果使用TIM2的CH2,那就只能在PA1端口输出,TIM2的CH3就只能是PA2,CH4就只能是PA3。不过,但是虽然它是定死的,STM32还是给了我们一次更改的机会的,这就是重定义,或者叫重映射。比如如果你既要用USART2的TX引脚,又要用TIM2的CH3通道,它俩冲突了,没办法同时用那我们就可以在这个重映射的列表里找一下。比如这里我们找到了TIM2的CH3,那TIM2的CH3就可以从原来的引脚,换到这里的引脚PB10,这样就避免了两个外设引脚的冲突。如果这个重映射的列表里找不到,那外设复用的GPIO就不能挪位置,配置重映射是用AFIO来完成的。如果使用重映射,它可以人PA0挪到这个PA15的引脚上,再其他的引脚,就没有机会作为这个通道的输出引脚了。

表43 TIM2复用功能重映像的表。如果把PA0改到PA15,就可以选择这个部分重映射方式1,或者完全重映射。 GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE); //引脚重映射配置 手册8.3节。8.3.5节 充映射的方式和引脚更改的关系。
 @arg GPIO_PartialRemap1_TIM2     : TIM2 Partial1 Alternate Function mapping
 @arg GPIO_PartialRemap2_TIM2     : TIM2 Partial2 Alternate Function mapping
 @arg GPIO_FullRemap_TIM2         : TIM2 Full Alternate Function mapping

引脚定义表中, 这个PA15,是没给加粗的,因为它上电后已经默认复用为了调试端口JTDI,所以如果想让他作为普通的GPIO或者复用定时器的通道,那还需要先关闭调试端口的复用,也是用这个GPIO PinRemapConfig函数 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
 @arg GPIO_Remap_SWJ_NoJTRST     : Full SWJ Enabled (JTAG-DP + SW-DP) but without JTRST
     //解除JTRST引脚的复用 就是这个NJTRST也就是PB4 如果使用这个参数,那么这个PB4就变为正常的GPIO口了
 @arg GPIO_Remap_SWJ_JTAGDisable : JTAG-DP Disabled and SW-DP Enabled  
     //解除JTAG调试端口的复用,在引脚定义里就是,PA15、PB3、PB4这三个端口变回GPIO,PA13和PA14,仍为SWD的调试端口
 @arg GPIO_Remap_SWJ_Disable     : Full SWJ Disabled (JTAG-DP + SW-DP)
     //这个参数就是把SWD和JTAG的调试端口全部解除 PB3 PB4 PA13 PA14 PA15 这5个引脚全部变成普通的GPIO,没有调试功能了。

不要随便调用,一但你调用这个参数并且下载程序之后,那么你的调试端口就都没有了,再使用STLINK就下载不进去程序了。 这时就只能使用串口下载,下载一个新的没有解除调试端的程序(不小心用了这个函数可以试试,断电,拉低BOOT0,下载程序,上电,拉高BooT0,下载程序就可以恢复正常了)

表35 调试端口映像 在这里如果我们需要用PA15、PB3、PB4这三个引脚,那通常就是解除JTAG的复用,保留SWD的复用。所以这里,参数我们就选GPIO_Remap_SWJ_JTAGDisable,这三个参数,就是用来解除调试端口的复用的

总结一下,如果你想让PA15、PB3、PB4这二个引脚当做GPIO来使用的话 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); 先打开AFIO时钟,在用AFIO将JTAG解除掉,即可。 如果想重映射定时器或者其他外设的复用引脚, RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE); 先打开AFIO时钟,再用AFIO重映射外设复用的引脚,即可。 如果你重映射的引脚又正好是调试端口, RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE); GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE); 打开AFIO时钟,重映射引脚,解除调试端口。

初始化时基单元

TIM_InternalClockConfig(TIM2);// 初始化时基单元
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);

初始化输出比较单元

初始化输出比较单元,参考stm32f10x_tim.h 有4个初始化函数,对应4个输出比较单元(输出比较通道) 

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);

需要初始化哪个通道,就调用哪个函数,不同的通道对应的GPIO口也是不一样的。本例中,使用PA0口,对应的是第一个输出比较通道。 

TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //设置输出比较的模式
/*
#define TIM_OCMode_Timing                  冻结模式
#define TIM_OCMode_Active                  相等时置有效电平
#define TIM_OCMode_Inactive                相等时置无效电平
#define TIM_OCMode_Toggle                  相等时电平反转
#define TIM_OCMode_PWM1                    PWM模式一
#define TIM_OCMode_PWM2                    PWM模式二
TIM_ForcedAction_Active和TIM_ForcedAction_InActive 强制输出的两种模式*/
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCPolarity_High;// 设置输出比较的极性
	/*
	#define TIM_OCPolarity_High                高极性,有效电平是高电平,就是极性不翻转,REF波形直接输出
	#define TIM_OCPolarity_Low                 低极性,有效电平为低电平,就是REF电平取反	*/

设置输出使能 

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能
/*
#define TIM_OutputState_Disable            失能
#define TIM_OutputState_Enable             使能
*/
TIM_OCInitStructure.TIM_Pulse =50 ;//脉冲,设置CCR
//TIM_OCInitStructure.TIM_Pulse =0 ;//脉冲,设置CCR
//实现呼吸灯的效果,就是不断更改CCR的值即可
/*
72M/(PSC+1)/(ARR+1) =  1000
CRR/(ARR+1) = 50%
1/(ARR+1) = 1%
解方程,ARR+1 = 100,PSC+1 = 720 ,CCR=50
现在就是频率为1KHz,占空比为50%的PWM波形了
*/
TIM_OC1Init(TIM2,&TIM_OCInitStructure);//初始化输出比较单元
TIM_Cmd(TIM2, ENABLE);

配置GPIO

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; //AFIO重映射后的代码
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA, &GPIO_InitStructure);

PWM.c

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //打开时钟、选择内部时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  //RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO的时钟, 通过端口复用把TIM2的CH1从PA0挪到PA15引脚上 APB2的设备
  //GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE); //引脚重映射配置 手册8.3节。8.3.5节 充映射的方式和引脚更改的关系。
	//表43 TIM2复用功能重映像的表。如果把PA0改到PA15,就可以选择这个部分重映射方式1,或者完全重映射。
	/**
		@arg GPIO_PartialRemap1_TIM2     : TIM2 Partial1 Alternate Function mapping
		@arg GPIO_PartialRemap2_TIM2     : TIM2 Partial2 Alternate Function mapping
		@arg GPIO_FullRemap_TIM2         : TIM2 Full Alternate Function mapping
	*/
	/*
	引脚定义表中, 这个PA15,是没给加粗的,因为它上电后已经默认复用为了调试端口JTDI,
	所以如果想让他作为普通的GPIO或者复用定时器的通道,那还需要先关闭调试端口的复用,也是用这个GPIO PinRemapConfig函数
	*   @arg GPIO_Remap_SWJ_NoJTRST      : Full SWJ Enabled (JTAG-DP + SW-DP) but without JTRST 
	解除JTRST引脚的复用 就是这个NJTRST也就是PB4 如果使用这个参数,那么这个PB4就变为正常的GPIO口了
	*   @arg GPIO_Remap_SWJ_JTAGDisable  : JTAG-DP Disabled and SW-DP Enabled  
	解除JTAG调试端口的复用,在引脚定义里就是,PA15、PB3、PB4这三个端口变回GPIO,PA13和PA14,仍为SWD的调试端口
	*   @arg GPIO_Remap_SWJ_Disable      : Full SWJ Disabled (JTAG-DP + SW-DP) 
	这个参数就是把SWD和JTAG的调试端口全部解除 PB3 PB4 PA13 PA14 PA15 这5个引脚全部变成普通的GPIO,没有调试功能了。
	不要随便调用,一但你调用这个参数并且下载程序之后,那么你的调试端口就都没有了,再使用STLINK就下载不进去程序了。
	这时就只能使用串口下载,下载一个新的没有解除调试端的程序(不小心用了这个函数可以试试,断电,拉低BOOT0,下载程序,上电,拉高BooT0,下载程序就可以恢复正常了)
	表35 调试端口映像
	在这里如果我们需要用PA15、PB3、PB4这三个引脚,那通常就是解除JTAG的复用,保留SWD的复用。所以这里,参数我们就选SWJ JTAGDisable,
	这三个参数,就是用来解除调试端口的复用的
	*/
    
  //GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //正常使用PA15引脚。
    
	/*
	总结一下,如果你想让PA15、PB3、PB4这二个引脚当做GPIO来使用的话
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
	先打开AFIO时钟,在用AFIO将JTAG解除掉,即可。
	如果想重映射定时器或者其他外设的复用引脚,
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); 
	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
	先打开AFIO时钟,再用AFIO重映射外设复用的引脚,即可。
	如果你重映射的引脚又正好是调试端口,
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); 
	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
	打开AFIO时钟,重映射引脚,解除调试端口。
	*/
	GPIO_InitTypeDef GPIO_InitStructure;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
	/*
	*/
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; //AFIO重映射后的代码
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	TIM_InternalClockConfig(TIM2);// 初始化时基单元
	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);
	
	//初始化输出比较单元,参考stm32f10x_tim.h
	/*
		有4个初始化函数,对应4个输出比较单元(输出比较通道)
		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);
		需要初始化哪个通道,就调用哪个函数,不同的通道对应的GPIO口也是不一样的
		本例中,使用PA0口,对应的是第一个输出比较通道。
	*/
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //设置输出比较的模式
	/*
		#define TIM_OCMode_Timing                  冻结模式
		#define TIM_OCMode_Active                  相等时置有效电平
		#define TIM_OCMode_Inactive                相等时置无效电平
		#define TIM_OCMode_Toggle                  相等时电平反转
		#define TIM_OCMode_PWM1                    PWM模式一
		#define TIM_OCMode_PWM2                    PWM模式二
		TIM_ForcedAction_Active和TIM_ForcedAction_InActive 强制输出的两种模式
	*/
	TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCPolarity_High;// 设置输出比较的极性
	/*
	#define TIM_OCPolarity_High                高极性,有效电平是高电平,就是极性不翻转,REF波形直接输出
	#define TIM_OCPolarity_Low                 低极性,有效电平为低电平,就是REF电平取反	
	
	*/
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能
	/*
	#define TIM_OutputState_Disable            失能
	#define TIM_OutputState_Enable             使能
	*/
	TIM_OCInitStructure.TIM_Pulse =50 ;//脉冲,设置CCR
	//TIM_OCInitStructure.TIM_Pulse =0 ;//脉冲,设置CCR
	//实现呼吸灯的效果,就是不断更改CCR的值即可
	/*
	72M/(PSC+1)/(ARR+1) =  1000
	CRR/(ARR+1) = 50%
	1/(ARR+1) = 1%
	解方程,ARR+1 = 100,PSC+1 = 720 ,CCR=50
	现在就是频率为1KHz,占空比为50%的PWM波形了
	*/
	TIM_OC1Init(TIM2,&TIM_OCInitStructure);//初始化输出比较单元
	TIM_Cmd(TIM2, ENABLE);
}


void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2,Compare); //用来单独修改CCR的值的函数
}

点击数:0

    暂无评论

    发送评论 编辑评论

    
    				
    |´・ω・)ノ
    ヾ(≧∇≦*)ゝ
    (☆ω☆)
    (╯‵□′)╯︵┴─┴
     ̄﹃ ̄
    (/ω\)
    ∠( ᐛ 」∠)_
    (๑•̀ㅁ•́ฅ)
    →_→
    ୧(๑•̀⌄•́๑)૭
    ٩(ˊᗜˋ*)و
    (ノ°ο°)ノ
    (´இ皿இ`)
    ⌇●﹏●⌇
    (ฅ´ω`ฅ)
    (╯°A°)╯︵○○○
    φ( ̄∇ ̄o)
    ヾ(´・ ・`。)ノ"
    ( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
    (ó﹏ò。)
    Σ(っ °Д °;)っ
    ( ,,´・ω・)ノ"(´っω・`。)
    ╮(╯▽╰)╭
    o(*////▽////*)q
    >﹏<
    ( ๑´•ω•) "(ㆆᴗㆆ)
    😂
    😀
    😅
    😊
    🙂
    🙃
    😌
    😍
    😘
    😜
    😝
    😏
    😒
    🙄
    😳
    😡
    😔
    😫
    😱
    😭
    💩
    👻
    🙌
    🖕
    👍
    👫
    👬
    👭
    🌚
    🌝
    🙈
    💊
    😶
    🙏
    🍦
    🍉
    😣
    Source: github.com/k4yt3x/flowerhd
    颜文字
    Emoji
    小恐龙
    花!
    上一篇
    下一篇