中级会员
 
- 积分
- 211
- 金钱
- 211
- 注册时间
- 2016-10-3
- 在线时间
- 32 小时
|
1金钱
参考原子哥的输入捕获例程(硬件是原子哥MINI板),写出来的都不行,我搞了三天都没搞懂到底是哪里出问题,问题现象是当你按下WK_UP时,再弹起,串口调试助手偶尔会显示高电平数值,大部分时间在按下按键再弹起时,串口调试助手不显示高电平值,而且DS0的亮度变化也停止(好像程序处于死机状态,),DS0是在TIM2_CH1的PWM控制下工作的。我找了很久的问题,今天早上终于被我发现问题。废话少说。先附上代码,然后再分析原因。
u8 TIM5CH1_CAPTURE_STA = 0;
u16 TIM5CH1_CAPTURE_VAL;
//TIM5输入捕获初始化函数
void TIM5_Capture_Init(u16 pres,u16 reload_val)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);//使能TIM5时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能GPIOA时钟
//GPIOA初始化,配置GPIOA.0下拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;//下拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
//配置TIM5时基,ARR,PSC
TIM_TimeBaseInitStructure.TIM_Prescaler = pres;//计数器时钟分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInitStructure.TIM_Period = reload_val;//确定自动重装载的值
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分割,确定捕获采样频率与定时器频率的分频比例。
TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStructure);
//初始化输入捕获通道
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//设置通道
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //极性,即是上升沿捕获还是下降沿捕获
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//映射关系
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//每个边沿捕获一次
TIM_ICInitStructure.TIM_ICFilter = 0;//不采样(即不滤波)
TIM_ICInit(TIM5,&TIM_ICInitStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//使能TIM5更新中断和捕获中断
TIM_Cmd(TIM5,ENABLE);//使能计数器5
}
//TIM5中断服务函数
void TIM5_IRQHandler(void)
{
if((TIM5CH1_CAPTURE_STA&0x80)==RESET)//如果捕获未完成。
{
if(TIM_GetITStatus(TIM5,TIM_IT_Update)==SET)//如果更新中断发生
{
if((TIM5CH1_CAPTURE_STA&0x40)!=RESET)//且如果捕获到了高电平
{
TIM5CH1_CAPTURE_STA++;//捕获到高电平后,记录定时器溢出次数。
if((TIM5CH1_CAPTURE_STA&0x3f)==0x3f)//如果高电平时间太长
{
TIM5CH1_CAPTURE_STA |= 0x80;//位7置位,强制标示捕获成功
TIM5CH1_CAPTURE_VAL = 0xffff;
}
}
//TIM_ClearITPendingBit(TIM5,TIM_IT_Update);//清除更新中断标志位
}
if(TIM_GetITStatus(TIM5,TIM_IT_CC1)==SET)//如果发生捕获,因为初始化设置的是上升沿捕获。因此第一次来的必定是上升沿。
{
if((TIM5CH1_CAPTURE_STA&0x40)==RESET)//TIM5CH1_CAPTURE_STA初值为0
{
TIM5CH1_CAPTURE_VAL = 0;//记录捕获值的变量清0
TIM5CH1_CAPTURE_STA = 0;//清零捕获辅助变量
TIM_SetCounter(TIM5,0);//清0计数器
TIM5->CCR1 = 0;//清0捕获比较寄存器
TIM5CH1_CAPTURE_STA |= 0x40;//捕获到第一个上升沿位6置位
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);//配置下次下降沿捕获
//TIM5->CCER |= 1<<1;
}
else //来了下降沿,上次捕获的是上升沿
{
TIM5CH1_CAPTURE_STA |= 0x80;//完成一次完整的捕获,位8如果不清0,即使来了边沿信号和更新也不会执行里面的内容。
TIM5CH1_CAPTURE_VAL = TIM_GetCapture1(TIM5);//获取CCR1的值
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);//配置下次上升沿捕获
//TIM5->CCER &= ~(1<<1);
}
//TIM_ClearITPendingBit(TIM5,TIM_IT_CC1);//清除捕获中断标志位
}
}
TIM_ClearITPendingBit(TIM5,TIM_IT_Update|TIM_IT_CC1);
}
//下面是主函数部分
#include"led.h"
#include"delay.h"
#include"sys.h"
#include"key.h"
#include"timer.h"
#include "usart.h"
extern u8 TIM5CH1_CAPTURE_STA;
extern u16 TIM5CH1_CAPTURE_VAL;
int main(void)
{
u8 DIR = 1;
u16 temp = 0;
u16 TIM5_Overflow_Count = 0; //计数器溢出次数
u32 TIM5_Overflow_0ne_Time = 0; //计数器溢出一次的时间
u32 High_Level_Val;//高电平值
LED_Init();//调用LED初始化函数
delay_init();
uart_init(9600); //9600
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
PWM_Init(0,255);
TIM5_Capture_Init(71,0xffff);//72M/(71+1) = 1M(计数器计数频率,周期则为1微妙)
while(1)
{
delay_ms(10);
if(DIR==1)
{
temp++;
if(temp>255)
DIR = 0;
}
else
{
temp--;
if(temp==0)
DIR = 1;
}
TIM1->CCR1 = temp;//赋值捕获/比较寄存器,与重装载计数器值比较。
if(TIM5CH1_CAPTURE_STA&0x80)//如果完成一次捕获
{
TIM5_Overflow_Count = TIM5CH1_CAPTURE_STA&0x3f; //脉冲高电平期间计数器溢出的次数
TIM5_Overflow_0ne_Time = 65536;//计数器溢出一次的时间 65535+1)*1us
High_Level_Val = TIM5_Overflow_Count*TIM5_Overflow_0ne_Time;
High_Level_Val += TIM5CH1_CAPTURE_VAL;//计数周期为1US
printf("HIGH:%ldus\r\n",High_Level_Val);
TIM5CH1_CAPTURE_STA = 0;//捕获完成后,获取高电平的值,然后开启下次捕获。
}
}
}
问题就是出在TIM5中断服务函数中的“TIM_ClearITPendingBit(TIM5,TIM_IT_Update);//清除更新中断标志位”和“//TIM_ClearITPendingBit(TIM5,TIM_IT_CC1);//清除捕获中断标志位”这两个函数,他们的位置放得不对,导致出现上面描述的现象。我分析如下:如果把这两句放在注释的位置,我来分析下:当捕获一次未完成时(先捕获到高电平,然后再捕获到低电平为捕获一次完成)TIM5CH1_CAPTURE_STA&0x80==0,所以进中断后,中断请求标志位可以被清0,但是当完成一次捕获后,TIM5CH1_CAPTURE_STA&0x80==1,也就是说即使更新中断和输入捕获产生了,程序也不会进入到if(TIM5CH1_CAPTURE_STA&0x80==0){}里面处理,所以中断标志位没有被清0,也就是中断标志位一直置位,所以程序就不停的进中断函数,所以主函数就一直在等待中断完成,可惜中断标志位没被清0,程序一直进中断函数,这样就导致在主函数中TIM5CH1_CAPTURE_STA最高位不能清0,不能清0就不能进行下一次的捕获。这样就导致DS0亮度不变了。貌似死机一样的。最后把这个两个函数合并放在void TIM5_IRQHandler(void){}这个函数的最后面,这样的话,即使TIM5CH1_CAPTURE_STA最高位没被清0,标志位照样可以被清0,这样程序就正常运行了。我的想法是只要写中断服务函数,进中断后先把标志位清0.注意:定时器溢出中断是不停的发送的,除非关闭定时器,而输入捕获中断只有按键操作才有产生。
上面我的分析可能不够完善和准确,还望各路高手批评指正。在此献丑了。
|
|