前言
回顾下之前的章节:
第一章节中我们描述了整个框架的核心设计思路以及主要的文件架构
第二章节中我们基于一个简单的定时器OS实现了串口的数据打印,并完成了通用crc模块的设计和测试
第三章节中我们给出了真随机数和伪随机数的概念和代码示例,并在架构上对接口进行了重构
第四章节中我们回顾了FMC的基本知识,并给出了示例,后面我们将在设计IAP的时候再次使用到FMC
第五章节中我们使用ADC和DMA搭建了一个通用的采样框架,并通过串口给出了采样的数据示例
第六章节中我们总结了DAC的基本使用方法,并通过DAC生成了任意频率的正弦波,三角波和方波
第七章节中我们总结下时钟的概念,并给出了获取系统中各模块的时钟频率的代码
第八章节中我们介绍了如何通过串口的DMA来实现串口数据的收发
本文我们再介绍下定时器的使用,以及如何产生普通占空比PWM以及互补带死区的PWM。
定时器
定时是一个很重要的功能,人类无法改变时间但可以想办法掌控利用时间。
人类最早使用的工具是沙漏或水漏,这一瓢水漏完就是一个时辰,该下课了;这一桶水漏完就是三个时辰,该下班了。
在沙漏或者水漏的例子中,时间是如何计算的呢?
t = count * △t
△t是一坨沙/一滴水落下的时间,count为计数,这就是定时器的基本原理。
在嵌入式软件领域,定时器是系统工作的基础,什么时候该做什么事情,要求的极其精确,它的工作原理就是时基+计数。
一般参数定义为prescaler和period,假设MCU的时钟频率为100MHz,prescaler=999,period=9,那么:
我们软件就可以基于这个100us的定时进行业务处理。
具体代码可以参见:串口和CRC那一篇。
PWM
PWM的全称是脉冲宽度调制(Pulse Width Modulation),从应用的角度来理解,就是频率可以控制,宽度也可以控制的方波信号;在工业控制领域,可以通过PWM来调节明暗,快慢,大小,使用范围极其广泛。
固定占空比的PWM
PWM的介绍比较多了,直接给代码:
GD32:
static?uint32_t?get_alt_func_num(uint32_t?TIMx)
{
????switch?(TIMx)
????{
????????case?TIMER0:
????????case?TIMER1:
????????????return?GPIO_AF_1;
????????case?TIMER2:
????????case?TIMER3:
????????case?TIMER4:
????????????return?GPIO_AF_2;
????????case?TIMER7:
????????case?TIMER8:
????????case?TIMER9:
????????case?TIMER10:
????????????return?GPIO_AF_3;
????????default:
????????????return?GPIO_AF_9;
????}
}
static?void?timerx_PWM_init(uint32_t?TIMx,?uint16_t?TIMCHx,?rcu_periph_enum?rcu,?uint32_t?gpio,?uint16_t?pin,?uint16_t?pulse)
{
????timer_oc_parameter_struct?timer_ocintpara;
????gpio_init(rcu,?gpio,?GPIO_MODE_AF,?get_alt_func_num(TIMx),?GPIO_OTYPE_PP,?GPIO_OSPEED_2MHZ,?pin);
????timer_ocintpara.outputstate??=?TIMER_CCX_ENABLE;
????timer_ocintpara.outputnstate?=?TIMER_CCXN_DISABLE;
????timer_ocintpara.ocpolarity???=?TIMER_OC_POLARITY_HIGH;
????timer_ocintpara.ocnpolarity??=?TIMER_OCN_POLARITY_HIGH;????
????timer_ocintpara.ocidlestate??=?TIMER_OC_IDLE_STATE_LOW;
????timer_ocintpara.ocnidlestate?=?TIMER_OCN_IDLE_STATE_LOW;
????timer_channel_output_config(TIMx,?TIMCHx,?&timer_ocintpara);
????timer_channel_output_pulse_value_config(TIMx,?TIMCHx,?pulse);
????timer_channel_output_mode_config(TIMx,?TIMCHx,?TIMER_OC_MODE_PWM0);
????timer_channel_output_shadow_config(TIMx,?TIMCHx,?TIMER_OC_SHADOW_DISABLE);
????timer_primary_output_config(TIMx,?ENABLE);
????timer_auto_reload_shadow_enable(TIMx);
????timer_enable(TIMx);
}
STM32:
static?void?get_oc_func(uint32_t?TIMCHx,?void?(**TIM_OCInit)(TIM_TypeDef*,?TIM_OCInitTypeDef*),?void?(**TIM_OCPreloadConfig)(TIM_TypeDef*,?uint16_t))
{
????switch?(TIMCHx)
????{
????????case?TIM_Channel_1:
????????????*TIM_OCInit?=?TIM_OC1Init;
????????????*TIM_OCPreloadConfig?=?TIM_OC1PreloadConfig;
????????break;
????????case?TIM_Channel_2:
????????????*TIM_OCInit?=?TIM_OC2Init;
????????????*TIM_OCPreloadConfig?=?TIM_OC2PreloadConfig;
????????break;
????????case?TIM_Channel_3:
????????????*TIM_OCInit?=?TIM_OC3Init;
????????????*TIM_OCPreloadConfig?=?TIM_OC3PreloadConfig;
????????break;
????????case?TIM_Channel_4:
????????????*TIM_OCInit?=?TIM_OC4Init;
????????????*TIM_OCPreloadConfig?=?TIM_OC4PreloadConfig;
????????break;
????}
}
static?void?timerx_PWM_init(TIM_TypeDef?*TIMx,?uint16_t?TIMCHx,?uint32_t?rcc,?GPIO_TypeDef?*gpio,?uint16_t?pin,?uint16_t?pulse)
{
????TIM_OCInitTypeDef?TIM_OCInitStructure;
????void?(*TIM_OCInit)(TIM_TypeDef*,?TIM_OCInitTypeDef*);
????void?(*TIM_OCPreloadConfig)(TIM_TypeDef*,?uint16_t);
????gpio_init(rcc,?gpio,?GPIO_Mode_AF_PP,?GPIO_Speed_2MHz,?pin);
????TIM_OCInitStructure.TIM_OutputState?=?TIM_OutputState_Enable;
????TIM_OCInitStructure.TIM_OutputNState?=?TIM_OutputNState_Disable;
????TIM_OCInitStructure.TIM_OCPolarity?=?TIM_OCPolarity_High;
????TIM_OCInitStructure.TIM_OCNPolarity?=?TIM_OCNPolarity_High;
????TIM_OCInitStructure.TIM_OCIdleState?=?TIM_OCIdleState_Reset;
????TIM_OCInitStructure.TIM_OCNIdleState?=?TIM_OCIdleState_Reset;
????TIM_OCInitStructure.TIM_OCMode?=?TIM_OCMode_PWM1;
????TIM_OCInitStructure.TIM_Pulse?=?pulse;
????get_oc_func(TIMCHx,?&TIM_OCInit,?&TIM_OCPreloadConfig);
????TIM_OCInit(TIMx,?&TIM_OCInitStructure);
????TIM_OCPreloadConfig(TIMx,?TIM_OCPreload_Enable);
????TIM_ARRPreloadConfig(TIMx,?ENABLE);
????TIM_CtrlPWMOutputs(TIMx,?ENABLE);
????TIM_Cmd(TIMx,?ENABLE);
}
占空比30%:
占空比30%和50%的波形:
占空比30%和50%的波形
互补带死区的PWM
代码比较简单,这里说一下死区时间的配置。
芯片手册中的描述:
死区芯片手册描述
推导下计算公式:
画个图:
死区公式
GD32代码:
static?void?timerx_PWM_ON_init(uint32_t?TIMx,?uint16_t?TIMCHx,?rcu_periph_enum?rcu,?uint32_t?gpio,?uint16_t?pin,?rcu_periph_enum?rcuN,?uint32_t?gpioN,?uint16_t?pinN,?uint16_t?pulse,?uint16_t?deadtime)
{
????timer_oc_parameter_struct?timer_ocintpara;
????timer_break_parameter_struct?breakpara;
????gpio_init(rcu,?gpio,?GPIO_MODE_AF,?get_alt_func_num(TIMx),?GPIO_OTYPE_PP,?GPIO_OSPEED_2MHZ,?pin);
????gpio_init(rcuN,?gpioN,?GPIO_MODE_AF,?get_alt_func_num(TIMx),?GPIO_OTYPE_PP,?GPIO_OSPEED_2MHZ,?pinN);
????timer_ocintpara.outputstate??=?TIMER_CCX_ENABLE;
????timer_ocintpara.outputnstate?=?TIMER_CCXN_ENABLE;
????timer_ocintpara.ocpolarity???=?TIMER_OC_POLARITY_HIGH;
????timer_ocintpara.ocnpolarity??=?TIMER_OCN_POLARITY_HIGH;??
????timer_ocintpara.ocidlestate??=?TIMER_OC_IDLE_STATE_LOW;
????timer_ocintpara.ocnidlestate?=?TIMER_OCN_IDLE_STATE_LOW;
????timer_channel_output_config(TIMx,?TIMCHx,?&timer_ocintpara);
????timer_channel_output_pulse_value_config(TIMx,?TIMCHx,?pulse);
????timer_channel_output_mode_config(TIMx,?TIMCHx,?TIMER_OC_MODE_PWM0);
????timer_channel_output_shadow_config(TIMx,?TIMCHx,?TIMER_OC_SHADOW_DISABLE);
????timer_break_struct_para_init(&breakpara);
????breakpara.deadtime?=?deadtime;
????breakpara.outputautostate?=?TIMER_OUTAUTO_ENABLE;
????timer_break_config(TIMx,?&breakpara);
????timer_primary_output_config(TIMx,?ENABLE);
????timer_auto_reload_shadow_enable(TIMx);
????timer_enable(TIMx);
}
STM32代码:
static?void?timerx_PWM_ON_init(TIM_TypeDef?*TIMx,?uint16_t?TIMCHx,?uint32_t?rcc,?GPIO_TypeDef?*gpio,?uint16_t?pin,?uint32_t?rccN,?GPIO_TypeDef?*gpioN,?uint16_t?pinN,?uint16_t?pulse,?uint16_t?deadtime)
{
????TIM_OCInitTypeDef?TIM_OCInitStructure;
????TIM_BDTRInitTypeDef?TIM_BDTRInitStructure;
????void?(*TIM_OCInit)(TIM_TypeDef*,?TIM_OCInitTypeDef*);
????void?(*TIM_OCPreloadConfig)(TIM_TypeDef*,?uint16_t);
????gpio_init(rcc,?gpio,?GPIO_Mode_AF_PP,?GPIO_Speed_2MHz,?pin);
????gpio_init(rccN,?gpioN,?GPIO_Mode_AF_PP,?GPIO_Speed_2MHz,?pinN);
????TIM_OCInitStructure.TIM_OutputState?=?TIM_OutputState_Enable;
????TIM_OCInitStructure.TIM_OutputNState?=?TIM_OutputNState_Enable;
????TIM_OCInitStructure.TIM_OCPolarity?=?TIM_OCPolarity_High;
????TIM_OCInitStructure.TIM_OCNPolarity?=?TIM_OCNPolarity_High;
????TIM_OCInitStructure.TIM_OCIdleState?=?TIM_OCIdleState_Reset;
????TIM_OCInitStructure.TIM_OCNIdleState?=?TIM_OCIdleState_Reset;
????TIM_OCInitStructure.TIM_OCMode?=?TIM_OCMode_PWM2;
????TIM_OCInitStructure.TIM_Pulse?=?pulse;
????get_oc_func(TIMCHx,?&TIM_OCInit,?&TIM_OCPreloadConfig);
????TIM_OCInit(TIMx,?&TIM_OCInitStructure);
????TIM_OCPreloadConfig(TIMx,?TIM_OCPreload_Enable);
????TIM_BDTRStructInit(&TIM_BDTRInitStructure);
????TIM_BDTRInitStructure.TIM_DeadTime?=?deadtime;
????TIM_BDTRInitStructure.TIM_AutomaticOutput?=?TIM_AutomaticOutput_Enable;
????TIM_BDTRConfig(TIMx,?&TIM_BDTRInitStructure);
????TIM_CtrlPWMOutputs(TIMx,?ENABLE);
????TIM_ARRPreloadConfig(TIMx,?ENABLE);
????TIM_Cmd(TIMx,?ENABLE);
}
在STM32中,复用的管脚需要配置,示例:
void?timer1_ch2_pwm_ON_init(void)
{
????RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,?ENABLE);
????GPIO_PinRemapConfig(GPIO_FullRemap_TIM1,?ENABLE);
????timerx_PWM_ON_init(TIM1,?TIM_Channel_2,?RCC_APB2Periph_GPIOE,?GPIOE,?GPIO_Pin_11,?RCC_APB2Periph_GPIOE,?GPIOE,?GPIO_Pin_10,?70,?0xFF);
}
互补PWM波形:
互补PWM
死区时间5.12us:
死区时间5.12us
死区时间10.08us:
死区时间10.08us
--EOF--
例行求粉,谢谢!
求粉