STM32F4XX 学习日志:定时器中断模拟PWM波实现呼吸灯
- 前言
-
- 任务目标
- 解决办法
- 过程
- 定时器配置
- 标准库时钟主频配置出现问题
- 中断服务函数
- 主函数
- 小结
- 以上代码亲测有效。
前言
使用反客科技STM32F407VET6 M1的核心板,板载8M主时钟晶振(HSE),32.768kHz低速外部晶振(LSE)。含有一个用户LED以及一个用户按键。
任务目标
初学使用标准库开发,学长布置了使用定时器产生PWM波来实现呼吸灯的任务。
但是这块板子上的LED灯接在PC13的引脚上。查询了最小原理图以及数据手册后发现,PC13并没有定时器复用功能。
解决办法
使用更新中断以及输出比较中断实现模拟pwm波
过程
配置一个定时器两个中断,定时器设置为向上计数。设置TIM1_CC_IRQHandler(void)
TIM1_UP_TIM10_IRQHandler(void)
其中更新中断比较常用我就不说了。但是这一个TIM1_CC_IRQHandler中断服务函数在网上见的很少,我在网上多方查找没有结果之后,去翻了数据手册,看到这样的一段介绍。
以上为比较中断服务函数,当该位置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;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1|RCC_APB2Periph_TIM8, ENABLE);
//TIM_DeInit(TIM1);
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); //这里就是参考手册里说的禁用预装载寄存器
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);
TIM_ITConfig(TIM1, TIM_IT_Update , ENABLE);
TIM_ITConfig(TIM1, TIM_FLAG_CC1 , ENABLE);
TIM_Cmd(TIM1, ENABLE);
}
标准库时钟主频配置出现问题
配置结束后测试时出现了一点问题,调试之后发现是时钟频率有问题导致分给定时器的时钟出现问题,TIM1挂载在APB2上,而库函数默认配置HSE作为系统时钟源,设置主频168MHz。但是当我获取PCLK2时钟频率后发现该线上的时钟频率是一个非常奇怪的数字,由此我判断是系统时钟出现问题。
对此我重新设置了高速内部时钟源作为系统时钟源。
下面贴出代码
#include "systemclk.h"
#define PLL_M 8
#define PLL_N 168
#define PLL_P 2
#define PLL_Q 7
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)
{
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_VOS;
// HCLK = SYSCLK / 1
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
#if defined(STM32F40_41xxx) || defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F412xG) || defined(STM32F446xx) || defined(STM32F469_479xx)
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
#endif
#if defined(STM32F401xx) || defined(STM32F413_423xx)
RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
#endif
#if defined(STM32F40_41xxx) || defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F401xx) || defined(STM32F469_479xx)
RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
(RCC_PLLCFGR_PLLSRC_HSI) | (PLL_Q << 24);
#endif
#if defined(STM32F412xG) || defined(STM32F413_423xx) || defined(STM32F446xx)
RCC->PLLCFGR = HSI_PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
(RCC_PLLCFGR_PLLSRC_HSI) | (PLL_Q << 24) | (PLL_R << 28);
#endif
RCC->CR |= RCC_CR_PLLON;
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {
}
#if defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F446xx) || defined(STM32F469_479xx)
PWR->CR |= PWR_CR_ODEN;
while((PWR->CSR & PWR_CSR_ODRDY) == 0)
{
}
PWR->CR |= PWR_CR_ODSWEN;
while((PWR->CSR & PWR_CSR_ODSWRDY) == 0)
{
}
FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
#endif
#if defined(STM32F40_41xxx) || defined(STM32F412xG)
FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
#endif
#if defined(STM32F413_423xx)
FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_3WS;
#endif
#if defined(STM32F401xx)
FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_2WS;
#endif
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= RCC_CFGR_SW_PLL;
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中断,定时器等配置。实际运用时由于不停的触发中断,造成系统处理效率极低。不宜使用。
**
以上代码亲测有效。
**