pwm输出是stm32最常用的外设之一,我比较习惯使用库函数配置,我通常移植做过的工程中的程序的配置代码,然后改一改相应的参数,配置方法也十分简单,即使很简单,但对于初学者有时候还是很容易出错。有时候会一点波形没有输出,在找不到其他原因的情况下,这个时候以自己以前的经验来说,配置代码里面有一句GPIO_PinAFConfig(GPIOB,GPIO_PinSource1, GPIO_AF_TIM3); (以stm32f4 time3 ch4为例)比较容易出错同时被忽略,其中的GPIO_PinSource1很容易稍不注意会写成GPIO_Pin_1这样子会导致引脚复用没有成功,同时输入捕获的配置的时候也有可能导致配置没有成功的情况,然后其中一个比较可能的原因也是这个参数的宏写错,因为这两个宏有点像,以上是我之前遇到的配置pwm或输入捕获的时候由于自己的粗心出现的问题,当时也查了比较久的时间,所以记录下来。
但前阵子在写一个程序是出现了一个更加奇怪的现象,出现了类似积分电路的波形。改工程程序的描述如下
该工程中需要配置一些外部中断和pwm输出于是写了这两个一下两个函数
LimitSwitchInit();
PWM3_Configuration();//tim3ch4
两个函数的具体定义如下:
void LimitSwitchInit()
{
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
OutGPIOInit(); //外部中断IO初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能SYSCFG时钟
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource2);//PA2 连接到中断线2
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource5);//PA5 连接到中断线5
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource6);//PA6 连接到中断线6
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource7);//PA7 连接到中断线7
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource15);//PB15 连接到中断线15
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource8);//PA8 连接到中断线8
/* 配置EXTI_Line2 */
EXTI_InitStructure.EXTI_Line = EXTI_Line2|EXTI_Line5|EXTI_Line6|EXTI_Line7|EXTI_Line8|EXTI_Line15;//LINE2,LINE5,LINE6,LINE7,LINE15,
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断事件
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能
EXTI_Init(&EXTI_InitStructure);//配置
//中断配置
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;//外部中断2
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;//子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
NVIC_Init(&NVIC_InitStructure);//配置
//
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;//外部中断7
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;//子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
NVIC_Init(&NVIC_InitStructure);//配置
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//外部中断7
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;//子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
NVIC_Init(&NVIC_InitStructure);//配置
}
void PWM3_Configuration(void) //TIM3
{
GPIO_InitTypeDef gpio;
TIM_TimeBaseInitTypeDef tim;
TIM_OCInitTypeDef oc;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //TIM1--TIM8使用内部时钟时,由APB2提供
gpio.GPIO_Pin = GPIO_Pin_1;
gpio.GPIO_Mode = GPIO_Mode_AF;
gpio.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOB,&gpio);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource1, GPIO_AF_TIM3);
tim.TIM_Prescaler = 12;
tim.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
tim.TIM_Period = 260; //25khz 计数周期
tim.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割,不为1的话会乘2
TIM_TimeBaseInit(TIM3,&tim);
oc.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式
oc.TIM_OutputState = TIM_OutputState_Enable; //选择输出比较状态
oc.TIM_OutputNState = TIM_OutputState_Disable; //选择互补输出比较状态
oc.TIM_Pulse = 150; //设置待装入捕获比较器的脉冲值
oc.TIM_OCPolarity = TIM_OCPolarity_Low; //设置输出极性
oc.TIM_OCNPolarity = TIM_OCPolarity_High; //设置互补输出极性
oc.TIM_OCIdleState = TIM_OCIdleState_Reset; //选择空闲状态下的非工作状态
oc.TIM_OCNIdleState = TIM_OCIdleState_Set; //选择互补空闲状态下的非工作状态
TIM_OC4Init(TIM3,&oc); //通道4
TIM_OC4PreloadConfig(TIM3,TIM_OCPreload_Enable);
TIM_Cmd(TIM3,ENABLE);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
}
运行程序之后的波形:
5.仿真调试
发现波形输出不是标准的方波,有点像是积分的波形,于是开始找原因,然后无意之间调换了LimitSwitchInit(); PWM3_Configuration();//tim3ch4这两个配置函数的位置即:
PWM3_Configuration();//tim3ch4
LimitSwitchInit();
结果输出波形就变得很正常:
于是很自然变想到了是第一个配置的外部中断影响到了正常的pwm输出,于是在这个方向上找了好久的原因,但是始终找不到外部中断有什么问题,同时外部中断的功能也很正常,而且还有一个很奇怪的问题,就是如果外部中断的配置影响了pwm的外设,那么应该是外部中断放在pwm后面才会出现问题,因为放在后面的pwm配置应该会覆盖原来的配置,但是实际情况刚好相反,pwm放在后面的时候便会出错,放在前面反而正常。
最后把void PWM3_Configuration(void)//TIM3这个函数的定义里面,配置gpio的gpio这个结构体放到了函数外面,变成一个全局变量,然后再次运行程序然后发现无论两个函数的配置位置是如何的都pwm输出都非常正常,到这里时问题就变得比较清晰了,我们应该知道局部变量和全局变量的区别,首先这两种类型的变量的作用域和生存期不同,局部变量作用域是该函数,生存期时这个函数的运行的时间,即函数返回,内存被回收利用,而全局变量的作用域是整个文件,生存期就是整个程序的运行时间内长期存在,内存不会被回收,其次他们还有另外一个问题就是,当定义一个全局变量的时候,分配的内存会全部清零,即所有的变量的初始值会是0,而当定义一个局部变量时,内存将不会清零。分配到的内存里面是什么数据,便是什么数据,也就说,局部变量不初始化的话,变量的值是很随机的。
回到之前的程序,由于我定义了一个局部变量GPIO_InitTypeDef gpio;这个结构体,然后这个结构体的数据便是原先内存的数据,里面成员的值是比较随机的,通过打断点观察成员的数据得到
当LimitSwitchInit(); PWM3_Configuration();//tim3ch4这个顺序时GPIO_InitTypeDef gpio里面的成员数据如左图,调换顺序之后变成右图:
可以发现后面的两个成员的数据改变了,查看原来void PWM3_Configuration(void)//TIM3函数的定义可以知道里面对这个结构体的赋值只有前三个成员而少了后两个成员,然后导致调换程序效果不同的原因是当前一个函数改变了某些内存,然后定义后面函数的局部变量的时候导致回收的内存里面的数据残留,造成了效果不同,至于是哪个变量不同呢,测试得到当补充配置gpio.GPIO_OType = GPIO_OType_PP;(推挽输出)时程序就没问题,当当配置为gpio.GPIO_OType = GPIO_OType_OD;(开漏输出)时便会出现那种异常波形。
以上得到的教训就是在定义了局部变量时,一定要把所有成员初始化一遍,不然会导致很多奇怪而又难以发现的问题,如以上遇到的问题。
上一篇:STM32 自定义频率与占空比PWM输出的方法
下一篇:STM32 PWM简介及呼吸灯实验
推荐阅读最新更新时间:2024-03-16 16:07