STM32之HAL库和标准库的PWM输出(呼吸灯)


前言:本文只讨论具体实现,对原理涉及较少。

一些寄存器:
(TIM3_CH4)
1、CCR4寄存器: 捕获/比较值寄存器:设置比较值;

计数器值TIMx_CNT与通道4捕获比较寄存器CCR4进行比较,通过比较结果输出有效电平和无效电平

  OC4REF=0 无效电平
  OC4REF=1 无效电平

2、TIMx_CCMR2寄存器: OC4M[2:0]位:用于设置PWM模式

  110:PWM模式1
  111:PWM模式2

	TIM3->CCMR2|=6<<12;  	//CH4 PWM1模式		 
	TIM3->CCMR2|=1<<11; 	//CH4 预装载使能

3、CCER寄存器: CC4P位:输入/捕获4输出极性。

  0:高电平为有效电平
  1:低电平为有效电平

	TIM3->CCER|=1<<13;   	//OC4 低电平有效	 

4、CCER寄存器: CC4E位:输入/捕获4输出使能。

  0:关闭使能
  1:打开使能

	TIM3->CCER|=1<<12;   	//OC4 输出使能	

HAL库

使用CubeMX自动生成代码。

1.定时器配置

  通用定时器3初始化(对于定时器的选择,查数据手册LED对应引脚的定时器,比如STM32F407应该使用定时器14通道1,对应引脚PF9。这里,查看手册发现:PB1 ------> TIM3_CH4 )
在这里插入图片描述
  通用定时器TIM2-TIM5一般在APB1上,APB1的定时器时钟频率为84MHz

  TIM_HandleTypeDef htim3;

  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 83;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;	//向上计数
  htim3.Init.Period = 499;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;

  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)		//初始化定时器,开启时钟,设置中断优先级
  {
    Error_Handler();
  }

结构体变量类型:TIM_HandleTypeDef,结构体变量:htim3作为TIM3的句柄,
同时Init也是一个结构体变量,其结构体类型为TIM_Base_InitTypeDefhtim3的成员。

typedef struct
{
  uint32_t Prescaler;         /*!< Specifies the prescaler value used to divide the TIM clock.
                                   This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */

  uint32_t CounterMode;       /*!< Specifies the counter mode.
                                   This parameter can be a value of @ref TIM_Counter_Mode */

  uint32_t Period;            /*!< Specifies the period value to be loaded into the active
                                   Auto-Reload Register at the next update event.
                                   This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF.  */

  uint32_t ClockDivision;     /*!< Specifies the clock division.
                                   This parameter can be a value of @ref TIM_ClockDivision */

  uint32_t RepetitionCounter;  /*!< Specifies the repetition counter value. Each time the RCR downcounter
                                    reaches zero, an update event is generated and counting restarts
                                    from the RCR value (N).
                                    This means in PWM mode that (N+1) corresponds to:
                                        - the number of PWM periods in edge-aligned mode
                                        - the number of half PWM period in center-aligned mode
                                     GP timers: this parameter must be a number between Min_Data = 0x00 and Max_Data = 0xFF.
                                     Advanced timers: this parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */

  uint32_t AutoReloadPreload;  /*!< Specifies the auto-reload preload.
                                   This parameter can be a value of @ref TIM_AutoReloadPreload */
} TIM_Base_InitTypeDef;

下面将结构体部分成员做个说明:
  (1)Prescaler:定时器分频。
  (2)CounterMode:计数模式
  (3)Period:自动重装载值
  (4)ClockDivision时钟分频因子
  (5)AutoReloadPreload:使能/不使能自动重装载

定时器溢出时间计算方法(周期):

   T o u t = ( a r r + 1 ) ( p s c + 1 ) F t . Tout=\frac{(arr+1)(psc+1)}{Ft}. Tout=Ft(arr+1)(psc+1).

Ft=定时器工作频率,单位:Mhz
arr:自动重装值。
psc:时钟预分频数

PWM频率:

   F p w m = T c l k ( a r r + 1 ) ( p s c + 1 ) Fpwm =\frac{Tclk }{(arr+1)(psc+1)} Fpwm=(arr+1)(psc+1)Tclk(单位:Hz)

我们设置系统时钟频率为84MHz,配置预分频系数为 83,计数周期(自动加载值)为 499,则定时器溢出频率,即PWM的频率,就是
   84 M H z ( 83 + 1 ) ( 499 + 1 ) = 2 k H z \frac{84MHz}{(83+1)(499+1) } = 2kHz (83+1)(499+1)84MHz=2kHz

设置定时器时钟源为内部时钟源

TIM_ClockConfigTypeDef sClockSourceConfig = {0};

sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
  Error_Handler();
}

2.TIM3 Channel4 PWM输出初始化

  TIM_OC_InitTypeDef sConfigOC = {0};
  
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  
  if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)		//初始化PWM
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC,       TIM_CHANNEL_4) != HAL_OK)		//配置TIM3通道4
  {
    Error_Handler();
  }
  HAL_TIM_MspPostInit(&htim3);	//调用定时器底层驱动,时钟使能,引脚配置

结构体TIM_OC_InitTypeDef

typedef struct
{
  uint32_t OCMode;        /*!< Specifies the TIM mode.
                               This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes */

  uint32_t Pulse;         /*!< Specifies the pulse value to be loaded into the Capture Compare Register.
                               This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */

  uint32_t OCPolarity;    /*!< Specifies the output polarity.
                               This parameter can be a value of @ref TIM_Output_Compare_Polarity */

  uint32_t OCNPolarity;   /*!< Specifies the complementary output polarity.
                               This parameter can be a value of @ref TIM_Output_Compare_N_Polarity
                               @note This parameter is valid only for timer instances supporting break feature. */

  uint32_t OCFastMode;    /*!< Specifies the Fast mode state.
                               This parameter can be a value of @ref TIM_Output_Fast_State
                               @note This parameter is valid only in PWM1 and PWM2 mode. */


  uint32_t OCIdleState;   /*!< Specifies the TIM Output Compare pin state during Idle state.
                               This parameter can be a value of @ref TIM_Output_Compare_Idle_State
                               @note This parameter is valid only for timer instances supporting break feature. */

  uint32_t OCNIdleState;  /*!< Specifies the TIM Output Compare pin state during Idle state.
                               This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State
                               @note This parameter is valid only for timer instances supporting break feature. */
} TIM_OC_InitTypeDef;

下面将结构体部分成员做个说明:
  (1)OCMode:模式选择PWM1/PWM2
  (2)Pulse:设置比较值,此值用来确定占空比
  (3)OCPolarity:输出比较极性为低

PWM模式

PWM模式1:
在向上计数时,一旦TIMx_CNT<TIMx_CCR4时通道4为有效电平,否则为无效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR4时通道4为无效电平(OC4REF=0),否则为有效电平(OC4REF=1)。

PWM模式2:
在向上计数时,一旦TIMx_CNT<TIMx_CCR4时通道4为无效电平,否则为有效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR4时通道4为有效电平,否则为无效电平。
(向下计数同理)

在两种模式下TIMx_CNT(计数器当前值)与TIMx_CCR4(捕获/比较4) 只是决定是有效电平还是无效电平

占空比:

  duty circle = T I M 3 − > C C R 4 a r r =\frac{ TIM3->CCR4}{arr} =arrTIM3>CCR4(单位:%)

TIM3->CCR4 用户设定值
修改CCR4可以修改占空比,修改arr可以修改频率

比较极性:

  TIMx_CCER寄存器的CC4P位,设置输入/捕获通道输出极性

0:有效电平为高电平
1:有效电平为低电平

若为向上计数,且CCER寄存器的CC4P位为0,则当TIMx_CNT<TIMx_CCR4时,有效电平,输出高电平;

同样向上计数,且CCER寄存器的CC4P位为1,则当TIMx_CNT<TIMx_CCR4时,有效电平,输出低电平

TIM3->CCER|=1<<12;   	//OC4 输出使能	
TIM3->CCER|=1<<13;   	//OC4 低电平有效	 

我们在这里设置低电平为有效电平。

3.时钟使能,GPIO引脚配置

此函数被MX_TIM3_Init(void)调用

void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(timHandle->Instance==TIM3)
  {
    __HAL_RCC_GPIOB_CLK_ENABLE();
    
    GPIO_InitStruct.Pin = GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  }
}

具体GPIO配置见另一篇。

4.开启时钟,设置中断优先级

此函数会被HAL_TIM_Base_Init()函数调用

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
  if(tim_baseHandle->Instance==TIM3)
  {
    __HAL_RCC_TIM3_CLK_ENABLE();				//开启定时器3时钟
    HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);		//设置中断优先级,函数有三个形参,作用分别是选择中断源,抢占优先级,响应优先级
    HAL_NVIC_EnableIRQ(TIM3_IRQn);				//使能中断
  }
}

抢占优先级和响应优先级

STM32有抢占优先级和响应优先级。低优先级的中断在执行时遇到高优先级的中断可以被响应,即执行完高优先级的中断后再执行低优先级中断。同级优先级不同时遇到时得按顺序执行,若同时遇到则要比较其子优先级的高低决定执行顺序。优先级分组有0-4组,抢占优先级和响应优先级都有0-4的选择,且数字越小优先级越高。

5.中断回调函数

当程序产生中断时,自动调用中断函数
由于Cube默认不开启中断需要手动开启

HAL_TIM_Base_Start_IT(&htim3);
//定时器3中断服务函数
void TIM3_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&htim3);	
    HAL_TIM_PeriodElapsedCallback(&htim3);	
}

//定时器3中断服务函数调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim==(&htim3))
    {
         HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1);         //LED电平反转(使用LED所在的引脚)
         //注意由于产生中断过快,反转实现不了,需要另外的逻辑,比如设置一个int变量,当进入中断500次,反转一次。
    }
}

6.整合代码

TIM_HandleTypeDef htim3;

/* TIM3 init function */
void MX_TIM3_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 83;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 499;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;		//占空比默认0
  sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
  {
    Error_Handler();
  }
  HAL_TIM_MspPostInit(&htim3);
  HAL_TIM_Base_Start_IT(&htim3);	//开启中断
}

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
  if(tim_baseHandle->Instance==TIM3)
  {
    __HAL_RCC_TIM3_CLK_ENABLE();
    HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(TIM3_IRQn);
  }
}
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(timHandle->Instance==TIM3)
  {
    __HAL_RCC_GPIOB_CLK_ENABLE();
    GPIO_InitStruct.Pin = GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  }
}
//定时器3中断服务函数
void TIM3_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&htim3);	
    HAL_TIM_PeriodElapsedCallback(&htim3);	
}

//定时器3中断服务函数调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim==(&htim3))
    {
          HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1);         //LED电平反转(使用LED所在的引脚)
         //注意由于产生中断过快,反转实现不了,需要另外的逻辑,比如设置一个int变量,当进入中断500次,反转一次。
    }
}

6.main()函数调用

定义变量

uint16_t pwmVal=0; //PWM占空比

使能TIM3的PWM 通道4 输出

HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_4);

while(1)逻辑

__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_4, pwmVal);

#define __HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__) \
  (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCR1 = (__COMPARE__)) :\
   ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCR2 = (__COMPARE__)) :\
   ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCR3 = (__COMPARE__)) :\
   ((__HANDLE__)->Instance->CCR4 = (__COMPARE__)))

作用:修改比较值,修改占空比
同样可以直接修改寄存器TIM3->CCR4 = pwmVal;

while (1)
  {
	  while (pwmVal< 500)	//arr = 499 + 1
	  {
		  pwmVal++;
		  __HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_4, pwmVal);    //修改比较值,修改占空比
//		  TIM3->CCR4 = pwmVal;    与上方相同
		  HAL_Delay(1);
	  }
	  while (pwmVal)
	  {
		  pwmVal--;
		  __HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_4, pwmVal);    //修改比较值,修改占空比
//		  TIM3->CCR4 = pwmVal;     与上方相同
		  HAL_Delay(1);
	  }
	  HAL_Delay(200);
  }

整合代码:

int main(void)
{
  uint16_t pwmVal=0;   //PWM占空比  

  HAL_Init();

  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM3_Init();
  
  HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_4);
  while (1)
  {
	  while (pwmVal< 500)	//arr = 499 + 1
	  {
		  pwmVal++;
		  __HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_4, pwmVal);    //修改比较值,修改占空比
//		  TIM3->CCR4 = pwmVal;    与上方相同
		  HAL_Delay(1);
	  }
	  while (pwmVal)
	  {
		  pwmVal--;
		  __HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_4, pwmVal);    //修改比较值,修改占空比
//		  TIM3->CCR4 = pwmVal;     与上方相同
		  HAL_Delay(1);
	  }
	  HAL_Delay(200);
  }
}

标准库

1.时钟使能

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14,ENABLE);  	//TIM14时钟使能    
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); 	//使能PORTF时钟	

2.GPIO配置

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14); //GPIOF9复用为定时器14

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

3.定时器配置

TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

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
TIM_ARRPreloadConfig(TIM14,ENABLE);//ARR使能 
TIM_Cmd(TIM14, ENABLE);  //使能TIM14

其中,结构体TIM_TimeBaseInitTypeDef

typedef struct
{
  uint16_t TIM_Prescaler;         /*!< Specifies the prescaler value used to divide the TIM clock.
                                       This parameter can be a number between 0x0000 and 0xFFFF */

  uint16_t TIM_CounterMode;       /*!< Specifies the counter mode.
                                       This parameter can be a value of @ref TIM_Counter_Mode */

  uint32_t TIM_Period;            /*!< Specifies the period value to be loaded into the active
                                       Auto-Reload Register at the next update event.
                                       This parameter must be a number between 0x0000 and 0xFFFF.  */ 

  uint16_t TIM_ClockDivision;     /*!< Specifies the clock division.
                                      This parameter can be a value of @ref TIM_Clock_Division_CKD */

  uint8_t TIM_RepetitionCounter;  /*!< Specifies the repetition counter value. Each time the RCR downcounter
                                       reaches zero, an update event is generated and counting restarts
                                       from the RCR value (N).
                                       This means in PWM mode that (N+1) corresponds to:
                                          - the number of PWM periods in edge-aligned mode
                                          - the number of half PWM period in center-aligned mode
                                       This parameter must be a number between 0x00 and 0xFF. 
                                       @note This parameter is valid only for TIM1 and TIM8. */
} TIM_TimeBaseInitTypeDef; 

可以看到和HAL库中的定义是类似的。少了AutoReloadPreload因此需要手动使能TIM_ARRPreloadConfig(TIM14,ENABLE);//ARR使能

4.初始化TIM14 Channel1 PWM模式

TIM_OCInitTypeDef  TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
TIM_OC1Init(TIM14, &TIM_OCInitStructure);  //根据指定的参数初始化外设TIM14OC1

TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable);  //使能TIM14在CCR1上的预装载寄存器

5.整合代码

//TIM14 PWM部分初始化 
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
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); 	//使能PORTF时钟	
	
	GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14); //GPIOF9复用为定时器14
	
	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; //选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
	TIM_OC1Init(TIM14, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM1 4OC1

	TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable);  //使能TIM14在CCR1上的预装载寄存器
    TIM_ARRPreloadConfig(TIM14,ENABLE);//ARR使能 
	TIM_Cmd(TIM14, ENABLE);  //使能TIM14
}  

6.main()函数调用

虽然与上文hal库具体实现不一样,但逻辑是一致的。

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(10);	 
		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);	//修改比较值,修改占空比
	}
}

  • 14
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
要实现呼吸灯效果,可以使用PWM输出控制LED的亮度。具体实现步骤如下: 1. 初始化TIM定时器和GPIO引脚,将GPIO引脚配置为PWM输出模式。 2. 配置TIM定时器的自动重载寄存器ARR和占空比寄存器CCR,使得PWM输出的频率和占空比符合呼吸灯效果的要求。 3. 启动TIM定时器,开始PWM输出。 下面是一个简单的示例代码: ```c #include "stm32f4xx_hal.h" TIM_HandleTypeDef htim; TIM_OC_InitTypeDef sConfig; void PWM_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_TIM2_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_5; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF1_TIM2; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); htim.Instance = TIM2; htim.Init.Prescaler = 0; htim.Init.CounterMode = TIM_COUNTERMODE_UP; htim.Init.Period = 1000; htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(&htim); sConfig.OCMode = TIM_OCMODE_PWM1; sConfig.Pulse = 500; sConfig.OCPolarity = TIM_OCPOLARITY_HIGH; sConfig.OCFastMode = TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(&htim, &sConfig, TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim, TIM_CHANNEL_1); } int main(void) { HAL_Init(); PWM_Init(); while (1) { for (int i = 0; i <= 1000; i++) { __HAL_TIM_SET_COMPARE(&htim, TIM_CHANNEL_1, i); HAL_Delay(5); } for (int i = 1000; i >= 0; i--) { __HAL_TIM_SET_COMPARE(&htim, TIM_CHANNEL_1, i); HAL_Delay(5); } } } ``` 在上面的代码中,TIM2的时钟频率为84MHz,ARR设置为1000,占空比寄存器CCR的值从0到1000递增和递减,每次递增或递减的时间间隔为5ms,这样就可以实现呼吸灯效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值