接上次的博客,上一篇博客介绍的是产生两路频率可调、占空比固定为50%的方波,但是更多情况下也需要调节占空比,这篇博客简单介绍下。其实和频率可调的输出基本上是一样的,只需要再加上一个Duty来控制CCRx_Val的值。
先来看下配置程序:

u16 CCR1_Val = 2000;//频率初始化为500Hz
u16 CCR2_Val = 2000;//频率初始化为500Hz
float Duty1 = 0.8;//占空比初始值为80%
float Duty2 = 0.2;//占空比初始值为20%
void PWMOut_Init_Adjust_Duty_Fre(void)//输出两路频率可调、占空比可调的PWM波
{TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;TIM_OCInitTypeDef  TIM_OCInitStructure;NVIC_InitTypeDef NVIC_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);/* GPIOA and GPIOB clock enable */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,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);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);/* Time base configuration */TIM_TimeBaseStructure.TIM_Period = 65535;//必须是这个数值65535TIM_TimeBaseStructure.TIM_Prescaler = 71;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;//跳变值,也可初始化为CCR1_Val*Duty1TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;TIM_OC1Init(TIM3, &TIM_OCInitStructure);TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable); //使能或者失能TIMx在CCR1上的预装载寄存器/* Output Compare Toggle Mode configuration: Channel2 */TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructure.TIM_Pulse = CCR2_Val;//跳变值,也可初始化为CCR2_Val*Duty2TIM_OC2Init(TIM3, &TIM_OCInitStructure);TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Disable);TIM_Cmd(TIM3,ENABLE);TIM_ITConfig(TIM3,TIM_IT_CC1 | TIM_IT_CC2,ENABLE);
}

再来看下中断服务函数:

void TIM3_IRQHandler(void)
{if(TIM_GetITStatus(TIM3,TIM_IT_CC1) != RESET)//TIM_IT_CC1为捕获中断{TIM_ClearITPendingBit(TIM3,TIM_IT_CC1);capture = TIM_GetCapture1(TIM3);//获取当前计数器中的值if(PA6_state == 0){TIM_SetCompare1(TIM3,capture + (u16)(CCR1_Val*Duty1));//注意此时的CCR1_Val是指的整个一个PWM周期内的总计数值,而在频率可调、占空比固定位50%的程序中,CCR1_Val指的是半个周期的计数值PA6_state = 1;}else if(PA6_state == 1){TIM_SetCompare1(TIM3,capture + (u16)(CCR1_Val*(1-Duty1)));PA6_state = 0;}}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*Duty2));PA7_state = 1;}else if(PA7_state == 1){TIM_SetCompare2(TIM3,capture + (u16)(CCR2_Val*(1-Duty2)));PA7_state = 0;}}
}

我们看到定时器及PWM波的配置程序基本上没什么变化,变化的主要就是在中断服务函数里面加上了一个Duty1、Duty2 的东西;另外需要注意的就是在上篇博客中的频率可调、占空比固定位50%的程序中的CCR1_Val和CCR2_Val指的都是PWM波的半个周期的计数值,而在本次的频率可调、占空比可调的程序中,CCR1_Val和CCR2_Val指的都是PWM波的一个周期内的总的计数值,高电平和低电平的时间分配(也就是占空比的分配就是通过Duty1Duty2来控制的)。
以下面这个为例:

TIM_SetCompare1(TIM3,capture + (u16)(CCR1_Val*Duty1));

TIM_SetCompare1(TIM3,capture + (u16)(CCR1_Val*(1-Duty1)));

观察就可以发现,Duty1把CCR1_Val在一个PWM周期内分成了两部分,两部分分别控制的就是高低电平的持续时间。在上篇博客中,假设CCR1_Val为800,那么每次当计数器的值和比较寄存器的值相等的时候,都会把比较寄存器中的值更新为capture+CCR1_Val,以保证下次还是以相同的时间间隔触发中断,但是今天不是直接把比较寄存器中更新为capture+CCR1_Val,而是在前半个周期内更新为capture + (u16)(CCR1_Val*Duty1),后半个周期内更新为capture + (u16)(CCR1_Val*(1-Duty1))。相信经过上篇博客的详细讲解,这里的程序理解起来应该就相对容易一些了,我就不过多解释了。
再解释下刚开始初始化的时候,频率怎么计算:

u16 CCR1_Val = 2000;//频率初始化为500Hz
u16 CCR2_Val = 2000;//频率初始化为500Hz

其实计算方法类似于上篇博客,唯一的区别就是这里的CCRx_Val指的是一个周期内的计数值,上篇博客指的是半个周期内的计数值。
配置时钟72分频,即计数频率为1MHz,那么一个周期内的计数值就是2000*1us=2ms,也就是500Hz。
然后我们通过按键去改变CCR1_Val、CCR2_Val、Duty1、Duty2的数值,就可以改变PWM波的频率以及占空比了。
看下按键程序:(我是用外部中断检测按键的)

extern void Delay_Ms(u32 nTime);
extern float Duty1,Duty2;
extern u16 CCR1_Val,CCR2_Val;
u16 pinlv1 = 500,pinlv2 = 500;//注意pinlv1和pinlv2的值必须分别初始化为1000000/CCR1_Val和1000000/CCR2_Val;void Key_Init(void)
{EXTI_InitTypeDef   EXTI_InitStructure;GPIO_InitTypeDef   GPIO_InitStructure;NVIC_InitTypeDef   NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_8;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;GPIO_Init(GPIOB, &GPIO_InitStructure);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);//PA0EXTI_InitStructure.EXTI_Line = EXTI_Line0;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;  EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource8);//PA8EXTI_InitStructure.EXTI_Line = EXTI_Line8;EXTI_Init(&EXTI_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;NVIC_Init(&NVIC_InitStructure);GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);//PB1EXTI_InitStructure.EXTI_Line = EXTI_Line1;EXTI_Init(&EXTI_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;NVIC_Init(&NVIC_InitStructure);GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource2);//PB2EXTI_InitStructure.EXTI_Line = EXTI_Line2;EXTI_Init(&EXTI_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;NVIC_Init(&NVIC_InitStructure);
}void EXTI0_IRQHandler(void)
{Delay_Ms(5);if(EXTI_GetITStatus(EXTI_Line0) != RESET){Led_Control(LED1,1);pinlv1 += 100;//pinlv1增加,意味着CCR1_Val减小,CCR1_Val减小意味着频率增大,即增加PWM频率,+100表示频率每次增加100HzCCR1_Val = (u16)(1000000/pinlv1);EXTI_ClearITPendingBit(EXTI_Line0);}
}void EXTI9_5_IRQHandler(void)
{Delay_Ms(5);if(EXTI_GetITStatus(EXTI_Line8) != RESET){Led_Control(LED2,1);pinlv1 -= 100;//pinlv1减小,意味着CCR1_Val增大,CCR1_Val增大意味着频率减小,即减小PWM频率,-100表示频率每次减小100HzCCR1_Val = (u16)(1000000/pinlv1);EXTI_ClearITPendingBit(EXTI_Line8);}
}void EXTI1_IRQHandler(void)
{Delay_Ms(5);if(EXTI_GetITStatus(EXTI_Line1) != RESET){Led_Control(LED3,1);Duty1 += 0.1;//增加PWM占空比,+0.1表示占空比每次增加10%EXTI_ClearITPendingBit(EXTI_Line1);}
}void EXTI2_IRQHandler(void)
{Delay_Ms(5);if(EXTI_GetITStatus(EXTI_Line2) != RESET){Led_Control(LEDALL,0);Duty1 -= 0.1;//减小PWM占空比,-0.1表示占空比每次减小10%EXTI_ClearITPendingBit(EXTI_Line2);}
}

看结果:(只看上面一路信号即可)

按下按键使频率减少100Hz:

按下按键使占空比减小10%:

由于开发版上只有4个按键,为了演示方便,我只调节了一路PWM,这个程序是支持同时产生两路频率可调、占空比可调的PWM波输出。
就是当输出频率高的时候波形有时会出现下面这种情况:

至于具体的原因,目前我也不是很懂。
参考博客:
1、使用定时器产生频率可调、占空比固定的PWM波
https://blog.csdn.net/qq_36554582/article/details/88143195
2、蓝桥杯嵌入式备赛手册
https://blog.csdn.net/Zach_z/article/details/80548423

如何使用定时器产生两路频率可调、占空比可调的PWM波相关推荐

  1. 如何使用定时器产生两路频率可调的PWM波

    输出频率可调的方波(占空比不可调,默认为50%) 使用定时器来产生PWM波大家应该都很熟悉,但是如何使用定时器产生频率可调的PWM波呢?这就需要使用定时器的输出比较翻转模式TIM_OCMode_Tog ...

  2. 函数信号发生器:本设计中要求输出方波、三角波、锯齿波、正弦波,电压0-10V可调,调节精度0.1V;输出信号的频率0-100Hz;占空比可调

    函数信号发生器:本设计中要求输出方波.三角波.锯齿波.正弦波,电压0-10V可调,调节精度0.1V:输出信号的频率0-100Hz:占空比可调. 本次课题设计将利用单片机控制来实现信号发生系统所要求的功 ...

  3. STM8L051F3P6TR 定时器2两路PWM输出+死区控制+端口重映射PC5PC6

    使用定时器2的通道1.2输出PWM CLK_PeripheralClockConfig(CLK_Peripheral_TIM2,ENABLE);//打开串口时钟源TIM2_DeInit();TIM2_ ...

  4. 如何使用定时器捕获一路PWM波信号的频率和占空比

    本次实验将采用定时器2的通道2产生两路频率和占空比均可调的PWM信号,然后使用定时器3的通道1来捕获其中的一路PWM波的频率和占空比. 1.首先来看下产生PWM波的程序,也就是和上篇博客是一样的,只不 ...

  5. 【定时器/中断/PWM】利用一个定时器实现一路PWM波的输出---点亮LED

    ------------------------------------------凑热闹的分割线------------------------------------------------ /* ...

  6. STM32H743+CubeMX-低功耗定时器LPTIM输出PWM波

    文章目录 一.前言 二.CubeMX 2.1.Clock Configuration 2.2.LPTIM2 Mode and Configuration 2.3.Parameter Settings ...

  7. 使用STM8S003定时器的PWM功能输出PWM波

    一.使用定时器TIM2的CH1.CH2.CH3通道PWM输出 1. 在使用stm8s系列的单片机的时候,使用的头文件大部分都是通用的stm8s.,第一步修改头文件: /* Uncomment the ...

  8. STM32单片机:定时器TIM输出PWM波

    学习32单片机过程中使用的工具:MDK Keil5 + 正点原子精英版(STM32F103ZET6)+ STM32CubeMX + HAL开发 一.PWM的产生原理 关于PWM的产生原理网上有很多教程 ...

  9. c语言定时器1khz占空比,STM32高级定时器TIM1产生两路互补的PWM波(带死区)

    测试环境:Keil 5.20.0.0 STM32F103RBT6 固件库版本:STM32F10x_StdPeriph_Lib_V3.5.0(2011) 本文使用TIM1的通道1,通道2,产生两路1kh ...

最新文章

  1. java实现验证码功能
  2. TrackMouseEvent介绍
  3. matlab如何配置weka,matlab调用weka
  4. 【机器学习】脑机接口利器:错误率仅3%
  5. 奇葩面试官让我回去等通知!看我怎么虐他!
  6. java 多个timer_java – Timer正在创建多个计时器实例
  7. 【Scala】Scala中特殊函数的使用(代码)
  8. 从前端到后端的跨域攻击与防御
  9. pid算法matlab仿真程序和c程序,pid算法matlab仿真程序和c程序.doc
  10. Vue条件渲染(v-if和v-show的区别)
  11. 韩顺平java基础学习笔记
  12. SAS入门 (二)--宏
  13. SQL常见的一些面试题
  14. Rocksdb 的compaction_filter和table_properties_collector 用法 及 其底层实现
  15. 织梦CMS插件合集覆盖几十插件功能采集推送等
  16. 用js超简单判断图片地址是否存在(404问题)
  17. xamarin其实也是一个鸡肋
  18. 2022年《网络安全法》迎来首修,拟对多处加大处罚力度
  19. 程序人生:企业网站解决方案
  20. 2019年CS224N课程笔记-Lecture 3: Word Window Classification, Neural Networks, and Matrix Calculus

热门文章

  1. 全局安装gulp 报错问题解决
  2. 【免费下载】2021年7月热门报告盘点(附热门报告列表及下载链接)
  3. 一文梳理序列化推荐算法模型进展
  4. 谁说男生不能美美哒?2020中国男士美妆市场洞察报告.pdf(附下载链接)
  5. 亿级规模的Feed流推荐系统,如何轻松设计?
  6. 程序员面试问题资源经验
  7. 从Google Scholar看各大科技公司科研水平
  8. qt 进度条最小_QT:圆形进度条设计
  9. python绝对值函数fabs_Python中abs()和math.fabs()区别
  10. game module 停止运行_恒温摇床长时间运行的注意事项