STM32用一个定时器输出多路不同频率及占空比的PWM(输出比较模式)

    我们使用STM32输出PWM时会使用定时器的PWM输出模式来进行生成,但是这样子生成PWM是有局限的,它只能生成四路频率相同的PWM,当你设定了TIMx_PSC(预分频寄存器)和TIMx_ARR(自动重装载寄存器),这时PWM的频率就被定下来了,为系统的时钟/TIMx_PSC+1/TIMx_ARR+1,你可以通过改变各个通道的CCR寄存器来改变占空比。但是如果我们想生成多路不同频率的PWM的话,使用这个方法只能使用多个定时器了,这样对于定时器资源较少的板子无疑是不可取的,在前几周准备蓝桥杯比赛的时候我发现了32定时器有一个输出比较的模式,可以生成多路不同频率及占空比的PWM。

    配置代码如下:

__IO u16 CCR1_Val=40;//72000000/36/40=50KHZ
__IO u16 CCR2_Val=20000;//72000000/36/20000=100HZ
float zhankong1=0.5;
float zhankong2=0.5;
u16 capture=0;
u8 pa6_state=0,pa7_state;

//比较输出PWM配置
void TIM3_PWM_Init2(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);//IO口配置

	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
	
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);//TIM3中断配置
	
	TIM_TimeBaseStructure.TIM_Period = 39999;
	TIM_TimeBaseStructure.TIM_Prescaler = 35;
	TIM_TimeBaseStructure.TIM_ClockDivision = 0;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);//定时器基本配置
	
	/* Output Compare Toggle Mode configuration: Channel1 */
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
	TIM_OC1Init(TIM3, &TIM_OCInitStructure);//通道1设置为输出比较模式

	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable);

	/* Output Compare Toggle Mode configuration: Channel2 */
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse = CCR2_Val;

	TIM_OC2Init(TIM3, &TIM_OCInitStructure);//通道2也设置为输出比较模式

	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Disable);//使能预装载
	
	/* TIM enable counter */
	TIM_Cmd(TIM3, ENABLE);//定时器3使能
	
	/* TIM IT enable */
	TIM_ITConfig(TIM3, TIM_IT_CC1 | TIM_IT_CC2, ENABLE);//通道1、通道2中断使能	
}

    定时器的中断服务函数:

void TIM3_IRQHandler(void)
{
  /* TIM3_CH1 toggling with frequency = 50KHz */
  if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)
  {
    TIM_ClearITPendingBit(TIM3, TIM_IT_CC1 );
    capture = TIM_GetCapture1(TIM3);
	  if(pa6_state==0)
	  {
		  TIM_SetCompare1(TIM3, capture + (u16)CCR1_Val *zhankong1 );
		  pa6_state=1;
	  }
	  else
	  {
		  TIM_SetCompare1(TIM3, capture + (u16)CCR1_Val *(1-zhankong1) );
		  pa6_state=0;
	  }
  }

  /* TIM3_CH2 toggling with frequency = 100 Hz */
  if (TIM_GetITStatus(TIM3, TIM_IT_CC2) != RESET)
  {
    TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);
    capture = TIM_GetCapture2(TIM3);
	   if(pa7_state==0)
	  {
		  TIM_SetCompare2(TIM3, capture + (u16)CCR2_Val *zhankong2 );
		  pa7_state=1;
	  }
	  else
	  {
		  TIM_SetCompare2(TIM3, capture + (u16)CCR2_Val *(1-zhankong2) );
		  pa7_state=0;
	  }
  }
}

    原理讲解:

        输出比较个人感觉原理还是有那么一点复杂的,可能我会讲的不太清楚,如果你看完以后有一点迷糊或者没有看懂,还是希望你可以仔细的阅读官方手册与代码,然后如果有问题或者有不同的看法欢迎留言

        首先关于我们配置了IO口,这个就是TIM3的通道1、通道2对应的IO口,分别是PA6和PA7

        接下来我们设置了定时器3的中断以及定时器的一些基本配置,比如预分频值、重加载值等,我们这里设置预分频值为35(实际会+1为36),重装载值为39999(实际会+1为40000)

        然后我们来配置通道1和通道2,配置为输出比较模式,使能输出,设置比较值为CCR1_Val,设置有效电平为低电平,同样通道2也是如此的配置

        最后我们使能预装载,使能定时器,使能中断

        接下来就到了输出比较模式的核心----中断服务函数了,中断服务函数中的处理是十分关键的,当有中断触发(CNT寄存器的值累加到了某个通道的比较值时),就会触发中断,根据中断的标志来判断是哪个通道触发了中断,紧接着它会查看当前CNT寄存器的值(使用TIM_GetCapturex函数),然后它判断现在是高电平还是低电平(这个是由有效电平控制,并有state标志位来判断),由此来设置新的比较值(使用TIM_SetComparex函数),这样就可以连续的生成固定频率固定占空比的PWM。

        有几个需要注意的点:因为CNT的值是从0计数到ARR寄存器值的,而我们每次会设置CCRx_Val这个值,所以说ARR寄存器的值最好是你每个通道CCRx_Val的公倍数,否则在每次重加载的时候波形会发生混乱。我们可以通过改变CCRx_Val来改变PWM的频率,通过改变zhankongx来改变PWM的占空比,这两个值是随时都可以修改的。

    总结:

       使用输出比较的方法可以在使用1个定时器的情况下有效的生成两路不同频率及占空比的PWM,它对比PWM输出模式的缺点肯定就是它会有中断的处理,如果生成的PWM频率较高时它会频繁的进入比较中断,这可能会给单片机带来较大的负担,但是在输出较低频率的PWM时,这种方法还是很好用的。

  • 97
    点赞
  • 477
    收藏
    觉得还不错? 一键收藏
  • 27
    评论
可以使用STM32F1的多个定时器输出多路不同频率占空比PWM信号。 首先,需要配置定时器的时钟源和分频系数。然后,设置定时器的计数值和自动重载值,以确定PWM信号的频率。接着,设置定时器比较输出模式比较值,以确定PWM信号的占空比。最后,使能定时器输出通道,即可输出PWM信号。 以下是一个使用STM32F1的定时器1和2输出两路不同频率占空比PWM信号的示例代码: ```c #include "stm32f10x.h" void TIM1_PWM_Init(uint16_t arr, uint16_t psc) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 打开GPIO和定时器的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO | RCC_APB2Periph_TIM1, ENABLE); // 配置PA8和PA9为推挽输出模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 定时器时钟源为APB2时钟,分频系数为psc TIM_TimeBaseStructure.TIM_Period = arr; TIM_TimeBaseStructure.TIM_Prescaler = psc; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); // 配置TIM1_CH1和TIM1_CH2为PWM输出模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = arr / 2; // 初始占空比设为50% TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM1, &TIM_OCInitStructure); TIM_OC2Init(TIM1, &TIM_OCInitStructure); // 使能TIM1_CH1和TIM1_CH2的输出通道 TIM_CtrlPWMOutputs(TIM1, ENABLE); // 启动定时器1 TIM_Cmd(TIM1, ENABLE); } void TIM2_PWM_Init(uint16_t arr, uint16_t psc) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 打开GPIO和定时器的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO | RCC_APB1Periph_TIM2, ENABLE); // 配置PA0和PA1为推挽输出模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 定时器时钟源为APB1时钟,分频系数为psc TIM_TimeBaseStructure.TIM_Period = arr; TIM_TimeBaseStructure.TIM_Prescaler = psc; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // 配置TIM2_CH1和TIM2_CH2为PWM输出模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = arr / 4; // 初始占空比设为25% TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM2, &TIM_OCInitStructure); TIM_OC2Init(TIM2, &TIM_OCInitStructure); // 使能TIM2_CH1和TIM2_CH2的输出通道 TIM_CtrlPWMOutputs(TIM2, ENABLE); // 启动定时器2 TIM_Cmd(TIM2, ENABLE); } int main(void) { // 配置定时器1和2输出不同频率占空比PWM信号 TIM1_PWM_Init(999, 71); // PWM频率为72MHz/(999+1)/(71+1)=100Hz,占空比50% TIM2_PWM_Init(1999, 719); // PWM频率为72MHz/(1999+1)/(719+1)=10Hz,占空比25% while (1); } ``` 注意,定时器的计数值和自动重载值、PWM信号的频率占空比的计算方式可能会因为STM32F1的型号和配置而有所不同,具体可以参考相关的芯片手册和数据手册。
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值