STM32产生PWM信号
首先我们必须肯定ST公司的实力,也承认STM32的确是一款非常不错的Cortex-M3核单片机,但是,手册实在是让人觉得无法理解,尤其是其中的TIM模块,没有条理可言,看了两天几乎还是不知所云,让人很是郁闷。同时配套的固件库的说明也很难和手册上的寄存器对应起来,研究起来非常费劲!功能强大倒是真的,但至少也应该配套一个让人看的明白的说明吧~~ 两天时间研究了STM32定时器的最最基础的部分,把定时器最基础的两个功能实现了,余下的功能有待继续学习。 首先有一点需要注意:FWLib固件库目前的最新版应该是V2.0.x,V1.0.x版本固件库中,TIM1模块被独立出来,调用的函数与其他定时器不同;在V2.0系列版本中,取消了TIM1.h,所有的TIM模块统一调用TIM.h即可。网络上流传的各种代码有许多是基于v1版本的固件库,在移植到v2版本固件库时,需要做些修改。本文的所有程序都是基于V2.0固件库。 以下是定时器向上溢出示例代码: C语言: TIM1模块产生向上溢出事件 //Step1.时钟设置:启动TIM1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //Step2.中断NVIC设置:允许中断,设置优先级 NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQChannel; //更新事件 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级0 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级1 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //允许中断 NVIC_Init(&NVIC_InitStructure); //写入设置 //Step3.TIM1模块设置 void TIM_Configuration(void) { TIM_TimeBaseInitTypeDef TIM_BaseInitStructure; TIM_OCInitTypeDef TIM_OCInitStructure; //TIM1 使用内部时钟 //TIM_InternalClockConfig(TIM1); //TIM1基本设置 //设置预分频器分频系数71,即APB2=72M, TIM1_CLK=72/72=1MHz //TIM_Period(TIM1_ARR)=1000,计数器向上计数到1000后产生更新事件,计数值归零 //向上计数模式 //TIM_RepetitionCounter(TIM1_RCR)=0,每次向上溢出都产生更新事件 TIM_BaseInitStructure.TIM_Period = 1000; TIM_BaseInitStructure.TIM_Prescaler = 71; TIM_BaseInitStructure.TIM_ClockDivision = 0; TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_BaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM1, &TIM_BaseInitStructure); //清中断,以免一启用中断后立即产生中断 TIM_ClearFlag(TIM1, TIM_FLAG_Update); //使能TIM1中断源 TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); //TIM1总开关:开启 TIM_Cmd(TIM1, ENABLE); } //Step4.中断服务子程序: void TIM1_UP_IRQHandler(void) { GPIOC->ODR ^= (1<<4); //闪灯 TIM_ClearITPendingBit(TIM1, TIM_FLAG_Update); //清中断 } 下面是输出比较功能实现TIM1_CH1管脚输出指定频率的脉冲: C语言: TIM1模块实现输出比较,自动翻转并触发中断 //Step1.启动TIM1,同时还要注意给相应功能管脚启动时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //Step2. PA.8口设置为TIM1的OC1输出口 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //Step3.使能TIM1的输出比较匹配中断 NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQChannel; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); //Step4. TIM模块设置 void TIM_Configuration(void) { TIM_TimeBaseInitTypeDefTIM_BaseInitStructure; TIM_OCInitTypeDefTIM_OCInitStructure; //TIM1基本计数器设置 TIM_BaseInitStructure.TIM_Period =0xffff; //这里必须是65535 TIM_BaseInitStructure.TIM_Prescaler= 71; //预分频71,即72分频,得1M TIM_BaseInitStructure.TIM_ClockDivision = 0; //时钟分屏 TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数模式为往上计算 TIM_BaseInitStructure.TIM_RepetitionCounter = 0; //重复的次数为0,也即是只计算一次 TIM_TimeBaseInit(TIM1, &TIM_BaseInitStructure); //写入配置 //TIM1_OC1模块设置 TIM_OCStructInit(&TIM_OCInitStructure); TIM_OCInitStructure.TIM_OCMode =TIM_OCMode_Toggle; //管脚输出模式:翻转 TIM_OCInitStructure.TIM_Pulse =2000; //翻转周期:2000个脉冲 TIM_OCInitStructure.TIM_OutputState= TIM_OutputState_Enable; //使能TIM1_CH1通道 TIM_OCInitStructure.TIM_OCPolarity =TIM_OCPolarity_High; //输出为正逻辑 TIM_OC1Init(TIM1,&TIM_OCInitStructure); //写入配置 //清中断 TIM_ClearFlag(TIM1, TIM_FLAG_CC1); //TIM1中断源设置,开启相应通道的捕捉比较中断 TIM_ITConfig(TIM1, TIM_IT_CC1,ENABLE); //TIM1开启 TIM_Cmd(TIM1, ENABLE); //通道输出使能 TIM_CtrlPWMOutputs(TIM1, ENABLE); } Step5.中断服务子程序 void TIM1_CC_IRQHandler(void) { u16 capture; if(TIM_GetITStatus(TIM1, TIM_IT_CC1)== SET) { TIM_ClearITPendingBit(TIM1,TIM_IT_CC1 ); capture = TIM_GetCapture1(TIM1); TIM_SetCompare1(TIM1, capture +2000); //这里解释下: //将TIM1_CCR1的值增加2000,使得下一个TIM事件也需要2000个脉冲, //另一种方式是清零脉冲计数器 //TIM_SetCounter(TIM2,0x0000); } } 关于TIM的操作,要注意的是STM32处理器因为低功耗的需要,各模块需要分别独立开启时钟,所以,一定不要忘记给用到的模块和管脚使能时钟,因为这个原因,浪费了我好多时间阿~~! 九九的STM32笔记(二)TIM模块产生PWM 这个是STM32的PWM输出模式,STM32的TIM1模块是增强型的定时器模块,天生就是为电机控制而生,可以产生3组6路PWM,同时每组2路PWM为互补,并可以带有死区,可以用来驱动H桥。 下面的代码,是利用TIM1模块的1、2通道产生一共4路PWM的代码例子,类似代码也可以参考ST的固件库中相应example C语言: TIM1模块产生PWM,带死区 //Step1.开启TIM和相应端口时钟 //启动GPIO RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | \ RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD,\ ENABLE); //启动AFIO RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //启动TIM1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //Step2. GPIO做相应设置,为AF输出 //PA.8/9口设置为TIM1的OC1输出口 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); //PB.13/14口设置为TIM1_CH1N和TIM1_CH2N输出口 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); //Step3. TIM模块初始化 void TIM_Configuration(void) { TIM_TimeBaseInitTypeDefTIM_BaseInitStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_BDTRInitTypeDefTIM_BDTRInitStructure; //TIM1基本计数器设置(设置PWM频率) //频率=TIM1_CLK/(ARR+1) TIM_BaseInitStructure.TIM_Period =1000-1; TIM_BaseInitStructure.TIM_Prescaler= 72-1; TIM_BaseInitStructure.TIM_ClockDivision= 0; TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_BaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM1,&TIM_BaseInitStructure); //启用ARR的影子寄存器(直到产生更新事件才更改设置) TIM_ARRPreloadConfig(TIM1, ENABLE); //TIM1_OC1模块设置(设置1通道占空比) TIM_OCInitStructure.TIM_OCMode =TIM_OCMode_PWM1; 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_Pulse = 120; TIM_OC1Init(TIM1,&TIM_OCInitStructure); //启用CCR1寄存器的影子寄存器(直到产生更新事件才更改设置) TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable); //TIM2_OC2模块设置(设置2通道占空比) TIM_OCInitStructure.TIM_OutputState= TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OutputNState= TIM_OutputNState_Enable; TIM_OCInitStructure.TIM_Pulse = 680; TIM_OC2Init(TIM1, &TIM_OCInitStructure); //启用CCR2寄存器的影子寄存器(直到产生更新事件才更改设置) TIM_OC2PreloadConfig(TIM1,TIM_OCPreload_Enable); //死区设置 TIM_BDTRInitStructure.TIM_OSSRState= TIM_OSSRState_Enable; TIM_BDTRInitStructure.TIM_OSSIState= TIM_OSSIState_Enable; TIM_BDTRInitStructure.TIM_LOCKLevel= TIM_LOCKLevel_OFF; TIM_BDTRInitStructure.TIM_DeadTime =0x90; //这里调整死区大小0-0xff TIM_BDTRInitStructure.TIM_Break =TIM_Break_Disable; TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High; TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable; TIM_BDTRConfig(TIM1,&TIM_BDTRInitStructure); //TIM1开启 TIM_Cmd(TIM1, ENABLE); //TIM1_OC通道输出PWM(一定要加) TIM_CtrlPWMOutputs(TIM1, ENABLE); } 其实,PWM模块还可以有很多花样可以玩,比方在异常时(如CPU时钟有问题),可以紧急关闭输出,以免发生电路烧毁等严重事故。 死区,简单解释: 通常,大功率电机、变频器等,末端都是由大功率管、IGBT等元件组成的H桥或3相桥。 每个桥的上半桥和下半桥是是绝对不能同时导通的,但高速的PWM驱动信号在达到功率元件的控制极时,往往会由于各种各样的原因产生延迟的效果,造成某个半桥元件在应该关断时没有关断,造成功率元件烧毁。 死区就是在上半桥关断后,延迟一段时间再打开下半桥或在下半桥关断后,延迟一段时间再打开上半桥,从而避免功率元件烧毁。这段延迟时间就是死区。(就是上、下半桥的元件都是关断的)死区时间控制在通常的低端单片机所配备的PWM中是没有的。 死区设置详解: // 设置刹车特性,死区时间,锁电平,OSSI,OSSR状态和AOE(自动输出使能 /* Automatic Output enable, Break, deadtime and lock configuration*/ // TIM1_OSSRState设置在运行模式下非工作状态选项 // TIM1_OSSRState_Enable,使能TIM1 OSSR状态 // TIM1_OSSRState_Disable,失能TIM1 OSSR状态 TIM_BDTRInitStructure.TIM_OSSRState =TIM_OSSRState_Disable; /* TIM1_OSSIState设置在运行模式下非工作状态选项。该参数取值见下表。 TIM1_OSSIState_Enable 使能TIM1 OSSI状态 TIM1_OSSIState_Disable 失能TIM1 OSSI状态 */ TIM_BDTRInitStructure.TIM_OSSIState =TIM_OSSIState_Disable; /* TIM1_LOCKLevel设置了锁电平参数。该参数取值见下表。 TIM1_LOCKLevel_OFF 不锁任何位 TIM1_LOCKLevel_1 使用锁电平1 TIM1_LOCKLevel_2 使用锁电平2 TIM1_LOCKLevel_3 使用锁电平3 */ TIM_BDTRInitStructure.TIM_LOCKLevel =TIM_LOCKLevel_1; // TIM1_DeadTIM1指定了输出打开和关闭状态之间的延时。 TIM_BDTRInitStructure.TIM_DeadTime = 5; /* TIM1_Break使能或者失能TIM1刹车输入。该参数取值见下表。 TIM1_Break_Enable 使能TIM1刹车输入 TIM1_Break_Disable 失能TIM1刹车输入 */ TIM_BDTRInitStructure.TIM_Break =TIM_Break_Disable; /* TIM1_BreakPolarity设置TIM1刹车输入管脚极性。该参数取值见下表。 TIM1_BreakPolarity_Low TIM1刹车输入管脚极性低 TIM1_BreakPolarity_High TIM1刹车输入管脚极性高 */ TIM_BDTRInitStructure.TIM_BreakPolarity= TIM_BreakPolarity_High; /* TIM1_AutomaticOutput使能或者失能自动输出功能,该参数取值见下表。 TIM1_AutomaticOutput_Enable 自动输出功能使能 TIM1_AutomaticOutput_Disable 自动输出功能失能 */ TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Disable; TIM_BDTRConfig(TIM1,&TIM_BDTRInitStructure); // 这条语句可以不执行 // 这条语句可以不执行 TIM_SelectOutputTrigger(TIM1,TIM_TRGOSource_Update); /* Master Mode selection */ // 这条语句可以不执行 TIM_ARRPreloadConfig(TIM1,ENABLE); // 使能TIMx在ARR上的预装载寄存器 TIM_ITConfig(TIM1,TIM_IT_Update, ENABLE); TIM_ITConfig(TIM1,TIM_IT_CC4, ENABLE); TIM_Cmd(TIM1, DISABLE); |
STM32
超强工具——STM32CubeMX 你会用吗?
集结出发! STM32全国研讨会系列之一:ST智能门铃中国首秀
关于STM32启动文件的几个小问题
【银杏科技ARM+FPGA双核心应用】STM32H7系列35——USB_VCP_FS
【银杏科技ARM+FPGA双核心应用】STM32H7系列28——USB_HID
粉丝分享 | 图说CRC原理应用及STM32硬件CRC外设
STM32L151进入低功耗,并由RTC唤醒的故事
[转]stm32控制NFC模块(PN532)源码(P2P,模拟卡,读写卡等
STM32G070RB+LVGL移植
微信公众号
手机版