STM32学习日志

STM32F4XX 学习日志:定时器中断模拟PWM波实现呼吸灯

  • 前言
    • 任务目标
    • 解决办法
    • 过程
    • 定时器配置
    • 标准库时钟主频配置出现问题
    • 中断服务函数
    • 主函数
    • 小结
    • 以上代码亲测有效。

前言

使用反客科技STM32F407VET6 M1的核心板,板载8M主时钟晶振(HSE),32.768kHz低速外部晶振(LSE)。含有一个用户LED以及一个用户按键。

任务目标

初学使用标准库开发,学长布置了使用定时器产生PWM波来实现呼吸灯的任务。
但是这块板子上的LED灯接在PC13的引脚上。查询了最小原理图以及数据手册后发现,PC13并没有定时器复用功能。
STM32学习日志_第1张图片

STM32学习日志_第2张图片

解决办法

使用更新中断以及输出比较中断实现模拟pwm波

过程

配置一个定时器两个中断,定时器设置为向上计数。设置TIM1_CC_IRQHandler(void)
TIM1_UP_TIM10_IRQHandler(void)
其中更新中断比较常用我就不说了。但是这一个TIM1_CC_IRQHandler中断服务函数在网上见的很少,我在网上多方查找没有结果之后,去翻了数据手册,看到这样的一段介绍。
STM32学习日志_第3张图片
以上为比较中断服务函数,当该位置1的时候表示定时器计数值与设定值相等,即

TIM_OCInitStructure.TIM_Pulse = 0;	

基数值等于该值的时候,也就是

TIM1->CNT=TIM1->CCR1

这两个寄存器的值相等时,发生中断。
对此就有了两个中断。
假设主频168MHZ设置预分频168-1
计数值100-1
该定定时器上溢中断发生的周期就为 168 000 000 / 168 =1us*100=100us
则将此周期视为pwm频率
而占空比可以通过控制输出比较中断触发的事件来设置。
即设定CCR1的值

TIM1->CCR1

该值与定时器重装载值的商即为占空比。通过在主函数里调整CCR1的值以此来模拟占空比可调的PWM波。

定时器配置

#include "tim.h"

void Tim_Init(void)
{
       	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

    /* 1. 使能时钟 */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1|RCC_APB2Periph_TIM8, ENABLE);

	//TIM_DeInit(TIM1); 

    /* 2. 配置定时器参数 */
	TIM_TimeBaseStructure.TIM_Prescaler         = 168 - 1;              /* 定时器时钟分频系数 */   
	TIM_TimeBaseStructure.TIM_Period            = 1000 - 1;               /* 定时器重装载值 */              
	TIM_TimeBaseStructure.TIM_ClockDivision     = TIM_CKD_DIV1;     
	TIM_TimeBaseStructure.TIM_CounterMode       = TIM_CounterMode_Up;     /* 计数器模式 */
	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;                      /* 重复计数值 */
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); 

	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;					//设置每次进入中断为电平翻转模式
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;		//输出开
	TIM_OCInitStructure.TIM_Pulse = 0;											//设置最初CCR为0,这样一配置完就进去中断服务程序
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;			//设置最开始的电平为高电平
	TIM_OC1Init(TIM1, &TIM_OCInitStructure);									//载入寄存器	
	
//	TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Disable); //这里就是参考手册里说的禁用预装载寄存器
	
    /* 3. 配置定时器中断优先级 */
	NVIC_InitStructure.NVIC_IRQChannel                   = TIM1_UP_TIM10_IRQn|TIM1_CC_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  
	NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 1;        
	NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;  
	NVIC_Init(&NVIC_InitStructure);

	NVIC_InitStructure.NVIC_IRQChannel                   = TIM1_UP_TIM10_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  
	NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;        
	NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;  
	NVIC_Init(&NVIC_InitStructure);   

	TIM_ClearFlag(TIM1, TIM_FLAG_Update);
	TIM_ClearFlag(TIM1, TIM_FLAG_CC1);

	/* 4. 使能定时器中断 */
	TIM_ITConfig(TIM1, TIM_IT_Update , ENABLE);
	TIM_ITConfig(TIM1, TIM_FLAG_CC1 , ENABLE);

	/* 5. 使能定时器 */
	TIM_Cmd(TIM1, ENABLE); 
}  

标准库时钟主频配置出现问题

配置结束后测试时出现了一点问题,调试之后发现是时钟频率有问题导致分给定时器的时钟出现问题,TIM1挂载在APB2上,而库函数默认配置HSE作为系统时钟源,设置主频168MHz。但是当我获取PCLK2时钟频率后发现该线上的时钟频率是一个非常奇怪的数字,由此我判断是系统时钟出现问题。
对此我重新设置了高速内部时钟源作为系统时钟源。
下面贴出代码

#include "systemclk.h"
#define PLL_M      8
#define PLL_N      168
/* SYSCLK = PLL_VCO / PLL_P */
#define PLL_P      2
#define PLL_Q      7
/**
  * @brief  Configures HSI as the System clock source
  **/  
void HSI_SetSysClock(void)
{
     
  __IO uint32_t HSIStartUpStatus = 0;
  
  RCC_DeInit();
 
  //set HSI
  RCC_HSICmd(ENABLE);
 
  HSIStartUpStatus = RCC->CR & RCC_CR_HSIRDY;
 
  if (HSIStartUpStatus == RCC_CR_HSIRDY)
  {
         
    /* Select regulator voltage output Scale 1 mode */
     RCC->APB1ENR |= RCC_APB1ENR_PWREN;
     PWR->CR |= PWR_CR_VOS;
 
     // HCLK = SYSCLK / 1
     RCC_HCLKConfig(RCC_SYSCLK_Div1);
 
    /* HCLK = SYSCLK / 1*/
    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
 
#if defined(STM32F40_41xxx) || defined(STM32F427_437xx) || defined(STM32F429_439xx) ||  defined(STM32F412xG) || defined(STM32F446xx) || defined(STM32F469_479xx)    
    /* PCLK2 = HCLK / 2*/
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
    
    /* PCLK1 = HCLK / 4*/
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
#endif /* STM32F40_41xxx || STM32F427_437x || STM32F429_439xx  || STM32F412xG || STM32F446xx || STM32F469_479xx */
 
#if defined(STM32F401xx) || defined(STM32F413_423xx)
    /* PCLK2 = HCLK / 1*/
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
    
    /* PCLK1 = HCLK / 2*/
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
#endif /* STM32F401xx || STM32F413_423xx */
 
    #if defined(STM32F40_41xxx) || defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F401xx) || defined(STM32F469_479xx)    
    /* Configure the main PLL */
    RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
                   (RCC_PLLCFGR_PLLSRC_HSI) | (PLL_Q << 24);
#endif /* STM32F40_41xxx || STM32F401xx || STM32F427_437x || STM32F429_439xx || STM32F469_479xx */
 
#if  defined(STM32F412xG) || defined(STM32F413_423xx) || defined(STM32F446xx)
    /* Configure the main PLL */
    RCC->PLLCFGR = HSI_PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
                   (RCC_PLLCFGR_PLLSRC_HSI) | (PLL_Q << 24) | (PLL_R << 28);
#endif /* STM32F412xG || STM32F413_423xx || STM32F446xx */
    
    /* Enable the main PLL */
    RCC->CR |= RCC_CR_PLLON;
    /* Wait till the main PLL is ready */
    while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {
     
    }
 
    #if defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F446xx) || defined(STM32F469_479xx)
    /* Enable the Over-drive to extend the clock frequency to 180 Mhz */
    PWR->CR |= PWR_CR_ODEN;
    while((PWR->CSR & PWR_CSR_ODRDY) == 0)
    {
     
    }
    PWR->CR |= PWR_CR_ODSWEN;
    while((PWR->CSR & PWR_CSR_ODSWRDY) == 0)
    {
     
    }      
    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
#endif /* STM32F427_437x || STM32F429_439xx || STM32F446xx || STM32F469_479xx */
 
#if defined(STM32F40_41xxx)  || defined(STM32F412xG)  
    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
#endif /* STM32F40_41xxx  || STM32F412xG */
 
#if defined(STM32F413_423xx)  
    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_3WS;
#endif /* STM32F413_423xx */
 
#if defined(STM32F401xx)
    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_2WS;
#endif /* STM32F401xx */
 
    /* Select the main PLL as system clock source */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= RCC_CFGR_SW_PLL;
 
    /* Wait till the main PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL)
    {
     
    }
  }
}

之后获取时钟频率,HCLK,PCLK2总线频率为168MHz,正常。
继续下面的步骤

中断服务函数

void TIM1_UP_TIM10_IRQHandler(void)
{
     
	GPIO_SetBits(GPIOC,GPIO_Pin_13);
//	GPIO_ToggleBits(GPIOC,GPIO_Pin_13);
	TIM_ClearFlag(TIM1, TIM_FLAG_Update);  //清除标志位
}

void TIM1_CC_IRQHandler(void)
{
     	
	GPIO_ResetBits(GPIOC,GPIO_Pin_13);	
//	GPIO_ToggleBits(GPIOC,GPIO_Pin_13);
   TIM_ClearFlag(TIM1, TIM_FLAG_CC1);  //清除标志位
}

主函数

#include "main.h"
#include "gpio.h"
#include "delay.h"
#include "systemclk.h"
static uint16_t count=0,flag=1;
int main(void)
{
     
	HSI_SetSysClock();
    delay_init();
	GPIO_init();
	Tim_Init();
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
	while(1)
	{
     
	for(count=1;count<1000;count++)
	{
     
		TIM1->CCR1 = count;  	//设置占空比
		STD_Delay_ms(10);
	}
	for(count=999;count>0;count--)
	{
     
		TIM1->CCR1 = count;
		STD_Delay_ms(10);
	}
//		RCC_ClocksTypeDef Get_RCC_Clocks;
	}
}

小结

本例子只用于学习熟悉了STM32,TIM1_CC_IRQHandler中断,定时器等配置。实际运用时由于不停的触发中断,造成系统处理效率极低。不宜使用。

**

以上代码亲测有效。

**

你可能感兴趣的:(学习日志,stm32,单片机,嵌入式)