前言:写上一篇单一定时器输出四路PWM时就想过能否输出四路不同频率及占空比的PWM波形,最近有空就花时间研究了一下,首先定时器的频率在ARR和PSC设置好之后就已经锁定了,要输出不同频率的波形需要使用TIM_OCMode_Toggle电平翻转模式,在中断中动态修改CRR比较寄存器的值来控制电平不断翻转实现不同频率波形
程序效果演示
STM32单一定时器输出四路不同频率及占空比的PWM波
一、程序思路
因为定时器只有一个CNT计数器所以只能有一个输出频率,若要实现多路不同频率波形,只能通过在当前CNT计数器的基础上不断累加自身CCR寄存器的值来触发中断在中断中翻转波形的方式,此时中断触发的频率即为波形的频率(CLOCK/PSC/ARR*(ARR/CRR每次加的数字大小));PS:CNT计数器与CCR寄存器都不存在数据溢出的风险,当数据值大于65535后会自动将多出的不分从0开始计数,所以不需要单独处理这部分数据。
二、程序实现
1、创建所需变量数组
//定义通道1的CCR比较值
uint16_t CCR_Value[4] = {5000,10000,15000,20000};
//设置各通道的占空比大小
float Duty_Ratio[4] = {0.5,0.6,0.7,0.8};
//定义一个标志位用于存放是高电平中断还是低电平中断,初始为高电平
uint8_t flag[4] = {0,0,0,0};
//用于存放各个通道中断时的CNT计数器值
uint32_t CNT[4] = {0,0,0,0};
2、配置GPIO
//开启相关GPIO外设时钟
RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_GPIOA, ENABLE);
RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_GPIOB, ENABLE);
// 定时器3引脚复用 注:引脚复用函数参数只能有一个引脚不能同一个方法填写多个
GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_TIM3);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_TIM3);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource0,GPIO_AF_TIM3);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource1,GPIO_AF_TIM3);
//定义结构体用于初始化GPIO
GPIO_InitTypeDef GPIO_InitStructure;
//初始化GPIOA
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
//初始化结构体A
GPIO_Init(GPIOA, &GPIO_InitStructure);
//初始化GPIOB
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
//初始化结构体B,相同参数可复用
GPIO_Init(GPIOB, &GPIO_InitStructure);
3、配置定时器
PS:此时定时器需要不断翻转,所以配置为电平翻转模式,且需要失能定时器通道自动重装器,手动装载CCR的值来达到控制翻转的目的
// 开启TIMx时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
//定义时基单元初始化结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
//定义PWM输出参数结构体
TIM_OCInitTypeDef TIM_OCInitStructure;
//PWM结构体预初始化,防止数据未初始化对数据产生影响
TIM_OCStructInit(&TIM_OCInitStructure);
//当定时器从0计数到65535,即为65535次为一个定时周期,定时周期到了之后会产生一个更新或中断,可通过更新中断TIM_IT_Update来捕获
TIM_TimeBaseStructure.TIM_Period = 65535-1;
// 通用控制定时器时钟源TIMxCLK = HCLK/2=84MHz
// 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)=400KHz 此时定时器输出的频率为84000000/65535/21=61hz
TIM_TimeBaseStructure.TIM_Prescaler = 21-1;
// 采样时钟分频,分频含义为几个周期采集一次数据,此处不分频
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数方式,向上计数
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 初始化定时器TIM3
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
//PWM模式配置
//配置为电平翻转模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
//此处设置比较值为5000
TIM_OCInitStructure.TIM_Pulse = CCR_Value[0];
//当定时器计数值小于CCR寄存器值时时为高电平
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
//使能定时器3通道,相同参数可复用,
//使能通道1
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
//此处设置比较值为10000
TIM_OCInitStructure.TIM_Pulse = CCR_Value[1];
//使能通道2
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
//此处设置比较值为15000
TIM_OCInitStructure.TIM_Pulse = CCR_Value[2];
//使能通道3
TIM_OC3Init(TIM3, &TIM_OCInitStructure);
//此处设置比较值为20000
TIM_OCInitStructure.TIM_Pulse = CCR_Value[3];
//使能通道4
TIM_OC4Init(TIM3, &TIM_OCInitStructure);
//重载的目的是在定时器的预加载寄存器中存入要修改的TIM_SetCompareX值,防止修改值立即对当前周期生效影响电平,使得参数可以在上一个周期执行完之后使能
//此处不需要重载功能,若要实时输出不同频率及占空比的波形就需要修改数据后实时生效
//失能定时器3所有通道重载
//失能通道1重载
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable);
//失能通道2重载
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Disable);
//失能通道3重载
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Disable);
//失能通道4重载
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Disable);
4、配置中断源并开启定时器中断
NVIC_InitTypeDef NVIC_InitStructure;
//设置中断组为0
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
//设置中断来源
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
//设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
//设置响应优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//定时器四个通道中断使能,先配置中断源再打开中断保证中断设置可以使能上
TIM_ITConfig(TIM3,TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE);
//使能定时器
TIM_Cmd(TIM3,ENABLE);
5、在中断中捕获电平翻转中断并修改CCR值文章来源:https://www.toymoban.com/news/detail-744815.html
void TIM3_IRQHandler(void)
{
//处理通道1中断
if(TIM_GetITStatus(TIM3,TIM_IT_CC1) == SET){
//存储通道1中断时的计数器值
CNT[0] = TIM_GetCapture1(TIM3);
TIM_ClearITPendingBit(TIM3,TIM_IT_CC1);
if(flag[0] == 0){
flag[0] = 1;
//将下一段高电平持续时间存入CCR
TIM_SetCompare1(TIM3,CNT[0]+CCR_Value[0]*Duty_Ratio[0]);
}else{
flag[0] = 0;
//将下一段低电平持续时间存入CCR
TIM_SetCompare1(TIM3,CNT[0]+CCR_Value[0]*(1-Duty_Ratio[0]));
}
}
//处理通道2中断
if(TIM_GetITStatus(TIM3,TIM_IT_CC2) == SET){
//存储通道2中断时的计数器值
CNT[1] = TIM_GetCapture2(TIM3);
TIM_ClearITPendingBit(TIM3,TIM_IT_CC2);
if(flag[1] == 0){
flag[1] = 1;
//将下一段高电平持续时间存入CCR
TIM_SetCompare2(TIM3,CNT[1]+CCR_Value[1]*Duty_Ratio[1]);
}else{
flag[1] = 0;
//将下一段低电平持续时间存入CCR
TIM_SetCompare2(TIM3,CNT[1]+CCR_Value[1]*(1-Duty_Ratio[1]));
}
}
//处理通道3中断
if(TIM_GetITStatus(TIM3,TIM_IT_CC3) == SET){
//存储通道3中断时的计数器值
CNT[2] = TIM_GetCapture3(TIM3);
TIM_ClearITPendingBit(TIM3,TIM_IT_CC3);
if(flag[2] == 0){
flag[2] = 1;
//将下一段高电平持续时间存入CCR
TIM_SetCompare3(TIM3,CNT[2]+CCR_Value[2]*Duty_Ratio[2]);
}else{
flag[2] = 0;
//将下一段低电平持续时间存入CCR
TIM_SetCompare3(TIM3,CNT[2]+CCR_Value[2]*(1-Duty_Ratio[2]));
}
}
//处理通道4中断
if(TIM_GetITStatus(TIM3,TIM_IT_CC4) == SET){
//存储通道4中断时的计数器值
CNT[3] = TIM_GetCapture4(TIM3);
TIM_ClearITPendingBit(TIM3,TIM_IT_CC4);
if(flag[3] == 0){
flag[3] = 1;
//将下一段高电平持续时间存入CCR
TIM_SetCompare4(TIM3,CNT[3]+CCR_Value[3]*Duty_Ratio[3]);
}else{
flag[3] = 0;
//将下一段低电平持续时间存入CCR
TIM_SetCompare4(TIM3,CNT[3]+CCR_Value[3]*(1-Duty_Ratio[3]));
}
}
}
三、小结
使用此方式输出四路不同频率PWM波需要对定时器有相当的理解,且在定时器初始化时其持续输出高电平无频率,需后续改进或丢弃此段数据文章来源地址https://www.toymoban.com/news/detail-744815.html
到了这里,关于STM32F407单一定时器输出4路不同频率及占空比的PWM波的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!