OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 3192|回复: 4

输入捕获

[复制链接]

19

主题

36

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
211
金钱
211
注册时间
2016-10-3
在线时间
32 小时
发表于 2016-10-13 08:58:09 | 显示全部楼层 |阅读模式
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.注意:定时器溢出中断是不停的发送的,除非关闭定时器,而输入捕获中断只有按键操作才有产生。

  上面我的分析可能不够完善和准确,还望各路高手批评指正。在此献丑了。

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165516
金钱
165516
注册时间
2010-12-1
在线时间
2116 小时
发表于 2016-10-14 20:37:26 | 显示全部楼层
你的想法 修改代码测试过了么?
回复

使用道具 举报

19

主题

36

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
211
金钱
211
注册时间
2016-10-3
在线时间
32 小时
 楼主| 发表于 2016-10-15 15:47:12 | 显示全部楼层
原子哥,我测试过了,问题来了,中断标志我放在中断函数里面第一行程序不正常,放在最后才行,这是为什么?想不明白,请原子哥指教。
回复

使用道具 举报

19

主题

36

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
211
金钱
211
注册时间
2016-10-3
在线时间
32 小时
 楼主| 发表于 2016-10-15 15:48:39 | 显示全部楼层
补充上面,是清除中断标志的函数放在一进中断的第一行,程序不正常,放在最后才可以。请原子哥帮忙分析原因。谢谢。
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165516
金钱
165516
注册时间
2010-12-1
在线时间
2116 小时
发表于 2016-10-18 22:45:14 | 显示全部楼层
zxj 发表于 2016-10-15 15:48
补充上面,是清除中断标志的函数放在一进中断的第一行,程序不正常,放在最后才可以。请原子哥帮忙分析原因 ...

是不是中断触发太快了?
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-5-24 01:42

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表