找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1257|回复: 5
打印 上一主题 下一主题
收起左侧

为什么C语言程序while里第一和第二个if语句仿真时会跳过?

[复制链接]
跳转到指定楼层
楼主
求大神指导!
红外程序单独使用正常,如果加入一些现实及定时程序后就不正常,然后仿真单步,发现适中不进入while循环里的下面两个语句,求大神指点!
              if(IR_Flag)                                //如果有红外数据传入并接收完成
                {
                        IR_decode();                //调用红外解码函数(用于得到键值)
                        IR_Flag=0;                        //清除红外数据接收完成标志位
                }
                if(IR_KEY)                                //如果红外解析成功,认定按键按下
                {
                        IR_control();                //调用红外控制函数(用户控制LED灯)
                }

源码如下:

/*********************************************/
/*                     timer                   */
/*                     创建者 :jeremy.zhu                   */
/*                     创建时间:2023/04/29            */
/*********************************************/

#include "STC8.H"
#include "TM1637.H"
#include "intrins.h"

#define u8  uint8_t
#define u16 uint16_t
#define u32 uint32_t
typedef unsigned char    uint8_t;
typedef unsigned int     uint16_t;
typedef unsigned long    uint32_t;

/************************端口/引脚定义区域************************/
sbit RED=P1^2;                        //RGB指示灯1
sbit GREEN=P1^3;                //RGB指示灯2
sbit BLUE=P1^4;                        //RGB指示灯3
sbit time_hr = P3^0;                //按键1,时间设置:小时+
sbit time_min = P3^1;                //按键2,时间设置:分钟+
sbit on_off = P3^2;                //按键3,开关
sbit mode_set = P3^3;        //按键4,功能选择
sbit plasma = P3^4;
sbit ozone = P3^5;
//----------------------------------------------------------------

sbit KEY=P1^7;                        //用户按键

/************************用户自定义数据区域***********************/
bit IR_KEY=0;                                                //红外按键按下标志
bit IR_Flag=0;                                        //红外数据接收完成标志位
u8 IR_time=0;                                                //下降沿之间的时间计数值
u8 IR_code[4];                                        //用户码+用户码+数据码+数据反码
u8 IR_data[33];                                        //引导码+32个位数据各自占用的时间

u8 mode=0;                //mode为工作模式0,1 分别表示停止模式和运行模式        
u8 hour=0,minute=0,second=0,time_count=0;   //真实计时的时,分,秒,10ms计数器
u8 show[6]={0,0,0,0,0,0}; //TM1637四位数码管显示数组:小时的十位和个位,分钟的十位和个位,最后两个为小时和分钟。
u16 uiKeyTimeCnt1;   //按键1计数
u16 uiKeyTimeCnt2;   //按键2计数
u16 uiKeyTimeCnt3;   //按键3计数
u16 uiKeyTimeCnt4;   //按键4计数
bit ShortTouchFlag1=0;     //按键1短按按键有效标志
bit ShortTouchFlag2=0;      //按键2短按按键有效标志
bit ShortTouchFlag3=0;     //按键3短按按键有效标志
bit ShortTouchFlag4=0;     //按键4短按按键有效标志
//bit delay_OnOff=0;                   //延时上电和延时断电选择位,开机检查Key3设置此位,0表示延时断电,1表示延时上电。
bit flag_1s=1;                //秒任务标志位
bit flag_500ms=1;        //半秒标志位
bit stat_500ms=0;        //半秒状态位,控制时间冒号闪烁

/**************************函数声明区域***************************/
void delay(u16 Count);        //延时函数声明
void Time0_init(void);        //定时计数器0初始化函数
void Int0_init(void);                //外部中断0初始化函数
void IR_decode(void);                //红外解码函数(用于得到键值)
void IR_control(void);        //红外控制函数(用户控制LED灯)
void Key_scan(void);
void Key_process(void);

/***************************主函数区域****************************/
void main(void)
{
        //STC8G/H系列单片机除P3.0和P3.1外,所有I/O上电均为高阻输入状态
        //所以大家必须要先配置I/O模式再去用引脚
        P1M0|=0x1C;                //P1.2至1.4引脚为推挽输出模式
        P1M1&=0xE3;                //P1.2至1.4引脚为推挽输出模式
        P1M0&=0x7F;                //P1.7引脚为准双向模式
        P1M1&=0x7F;                //P1.7引脚为准双向模式
        P3M1=0x00;           //将P3口设置为推挽输出
        P3M0=0xFF;
        BLUE=RED=GREEN=1;                //上电后三个LED都保持熄灭状态
        Time0_init();        //定时计数器0初始化函数
        Int0_init();        //外部中断0初始化函数
        TM1637_display(0,0,0,0,1); //上电后默认显示00:00
//        delay(100);                //等待配置稳定
        while(1)
        {
                if(IR_Flag)                                //如果有红外数据传入并接收完成
                {
                        IR_decode();                //调用红外解码函数(用于得到键值)
                        IR_Flag=0;                        //清除红外数据接收完成标志位
                }
                if(IR_KEY)                                //如果红外解析成功,认定按键按下
                {
                        IR_control();                //调用红外控制函数(用户控制LED灯)
                }
                Key_process();          //调用按键处理函数        
                                if(flag_500ms==1)        //每500毫秒进行一次显示处理,主要是为了实现时间冒号的闪烁
                {
                        flag_500ms=0;        //清除半秒标志位
                        stat_500ms=~stat_500ms;         //时间冒号每秒亮灭一次
                        if(mode==1)                //只在启动定时的状态下刷新显示时间
                        {
                                if(second>0) //以下为将实际计时的时间转换为数码管显示的时间。比如定时还剩20秒时,数码管实际显示的是00:01。
                                        show[5]=minute+1;
                                else
                                        show[5]=minute;
                                if(show[5]==60)
                                {
                                        show[5]=0;
                                        show[4]=hour+1;
                                }
                                else
                                        show[4]=hour;
                                show[0]=show[4]/10%10;         //显示的小时十位数
                                show[1]=show[4]%10;                 //显示的小时个位数
                                show[2]=show[5]/10%10;         //显示的分钟十位数
                                show[3]=show[5]%10;                 //显示的分钟个位数               
                                TM1637_display(show[0],show[1],show[2],show[3],stat_500ms);        //显示时间,冒号位是0还是1由stat_500ms确定,每半秒变化一次。
                        }
                        if(mode==1&&flag_1s==1)                 //以下为定时状态下更新计时时间,当秒任务标志位为1时进行倒计时
                        {
                                flag_1s=0;         //清除秒任务标志位
                                if(second>0) //如果秒大于0,则减1秒
                                {
                                        second--;
                                }
                                else if(minute>0) //如果秒等于0,则从分钟借位,分钟减1,秒变为59
                                {
                                        minute--;
                                        second=59;
                                }
                                else if(hour>0)          //如果秒和分钟都为0,则从小时借位,小时减1,分钟和秒都为59
                                {
                                        hour--;
                                        minute=59;
                                        second=59;
                                }
                                else                          //如果时分秒都为0
                                {
                                        mode=0;                  //倒计时结束,进入停止状态
//                                        relay=delay_OnOff;        //依据开机时设置的延时断电或延时上电,设置计时完成后的继电器状态。如果设置的是延时断电,计时到后继电器驱动为0。
                                        TM1637_display(0,0,0,0,1);        //计时到后时间显示00:00,时间冒号不闪烁
                                }
                        }
                }
        }
}
/****************************************************************/
//延时函数delay(),有形参Count无返回值
/****************************************************************/
void delay(u16 Count)
{
        while(Count--)
        {
                _nop_();
        }
}

void Time0_init(void)//初始化函数
{

AUXR=0x80;      //定时器0设为1T模式,和定时器1设为12T      
TMOD = 0x00; //设置定时器0,定时器1为模式0-16位自动重载
TL0 = 0xFE;  //设置定时器0初值
TH0 = 0xF3;    //定时278us
TL1 = 0x00;  //设置定时器1初值
TH1 = 0xDC;  //定时10ms
TF0 = 0;  //清除TF0标志
TF1 = 0;  //清除TF1标志
TR0 = 1;  //定时器0开始计时
TR1 = 1;  //定时器1开始计时
ET0 = 1;        //开定时器0中断
ET1 = 1;        //开定时器1中断
}

void Int0_init(void)
{
        IT0=1;        //配置外部中断0信号触发方式为边沿触发(下降沿有效)
        EX0=1;        //使能INT0中断
        EA = 1;         //开总中断
}
/****************************************************************/
//红外解码函数IR_decode(),用于得到键值,无形参,无返回值
/****************************************************************/
void IR_decode(void)
{
        u8 i,j,k;                                                               
        //变量i控制循环次数,用于最终得到4个字节数据(2个用户码+2个数据码)
        //变量j控制循环次数,通过循环和时长判断把8个时间间隔分析为“0码”
        //和“1码”,然后最终通过按位或运算及右移运算得到红外数据,变量k用
        //于控制IR_data[]数组的下标变化。
        u8 Timer_Value,IR_Value;
        //变量Timer_Value用于从IR_data[]数组中取出“时间间隔”数据
        //变量IR_Value用于存放最终的红外数据
        k=1;                                                                                                        //先让变量k等于1,因为k为0时取出的将会是“引导码的时间间隔”
        for(i=0;i<4;i++)                                                        //外层循环4次为了得到4个数据字节
        {
                for(j=0;j<=7;j++)                                                //内层循环8次为了拼合8个数据位为1个字节
                {
                        Timer_Value=IR_data[k];                //取出相应红外位的“时间间隔”数据
                        if(Timer_Value>7)                                        //若“时间间隔”比7大那肯定是“1码”反之为“0码”
                                IR_Value|=0x80;                                        //通过按位或运算高位填1
                        if(j<7)                                                                                //若数据没有拼合完8次
                                IR_Value>>=1;                                                //通过右移运算“腾出”位置准备下一位判定
                        k++;                                                                                        //下标变量自增
                }
                IR_code=IR_Value;                                //得到红外数据后放回IR_code[]数组
                IR_Value=0;                                                                        //清零IR_Value变量
        }
        IR_KEY=1;                                                                                        //红外按键按下标志位置1,提示系统有按键按下
}
/****************************************************************/
//红外控制函数IR_control(),用户控制LED灯,无形参,无返回值
/****************************************************************/
void IR_control(void)
{
        switch(IR_code[2])//只需要判断数据码即可
        //IR_code[0]是用户码第1字节,IR_code[1]是用户码第2字节
        //IR_code[3]是数据码字节,IR_code[4]是取反后的数据码字节
        {
                case 0x0C:{RED=0;GREEN=BLUE=1;ShortTouchFlag2=1;};break;
                //若按下遥控板的“1”则红灯亮起
                case 0x18:{GREEN=0;RED=BLUE=1;ShortTouchFlag1=1;};break;
                //若按下遥控板的“2”则绿灯亮起
                case 0x5E:{BLUE=0;RED=GREEN=1;ShortTouchFlag3=1;};break;
                //若按下遥控板的“3”则蓝灯亮起
//                case 0x08:{BLUE=RED=GREEN=0;};break;
                //若按下遥控板的“4”则全部亮起,发白光
//                default:{BLUE=RED=GREEN=1;};break;
                //若按下遥控板的其它键则RGB全部熄灭
        }
        IR_KEY=0;//遥控控制完成,清零该标志位
}
/****************************************************************/
//外部中断0中断服务函数INT0_ISR(),无形参,无返回值
/****************************************************************/
void INT0_ISR() interrupt 0
{
        static u8 IR_bit;                                                  //变量IR_bit用于指示红外数据的位数
        static bit Start_Flag;                                //位变量Start_Flag用于指示是否开始处理
        if(Start_Flag)                                                               
        {
                if(IR_time<70&&IR_time>32)        //判断引导码(9ms+4.5ms)
                //IR_time大约要溢出32次(9ms/0.278ms)到70次(可以大于(9+4.5)/0.278)
                        IR_bit=0;                                                                        //清除位数变量,确保当前IR_bit为0,表示引导码
                IR_data[IR_bit]=IR_time;                //存储相应位时间宽度
                IR_time=0;                                                                        //清零时间宽度计数值
                IR_bit++;                                                                                //位数变量自增
                if(IR_bit==33)                                                        //如果达到了33位(引导码+32个数据位)
                {
                        IR_Flag=1;                                                                //红外数据接收完成标志位置1
                        IR_bit=0;                                                                        //位数变量清零
                }
        }
        else                                                                                                        //外部中断0检测到下降沿,即将开始引导码
        {
                IR_time=0;                                                                        //清零时间计数值
                Start_Flag=1;                                                                //红外数据产生第一次下降沿,意味着数据即将开始
        }
}
/****************************************************************/
//定时计数器0中断服务函数TIMER0_ISR(),无形参,无返回值
/****************************************************************/
void TIMER0_ISR() interrupt 1
{
        IR_time++;
/*每过0.278ms则T0溢出一次,就是想要用溢出次数去衡量两个下降沿之间
的时间长短:前导码9ms+4.5ms,则IR_time大约要溢出32次(9ms/0.278ms)
到70次(可以大于(9+4.5)/0.278)。如果是“0码”,则周期是1.125ms,则
IR_time大约要溢出4次(1.125ms/0.278ms),如果是“1码”,周期是2.25ms,
则IR_time大约要溢出8次(2.25ms/0.278ms)。这样就可以区分不同的红外位
含义了.*/
}

void time1(void) interrupt 3   //T1中断服务,每10ms响应一次
{
        time_count++;                 //计数加1
        Key_scan();                         //每10ms扫描一次按键状态
        if(time_count==50)         //每500ms时设置一次半秒标志位
                flag_500ms=1;
        if(time_count==100)         //每1秒设置一次半秒标志位和秒任务标志位
        {
                time_count=0;
                flag_500ms=1;
                flag_1s=1;
        }  
}
/********************************************************************/
/********************************************************************/
/********************************************************************/
/******************************
函数说明:按键短按长按检测
******************************/
void Key_scan(void)                //在中断里调用,每10ms检查一次按键状态
{
        if(time_hr==0)                        //如果按键1按下
        {
            uiKeyTimeCnt1++; //累加按键计时
        }
        if(time_hr==1)          //当按键松开(也可能是抖动弹开)
        {
                if(uiKeyTimeCnt1>2&&uiKeyTimeCnt1<=200)         //只有大于20mS,且小于2秒,才判为短按
                {
                        uiKeyTimeCnt1=0;        //清零按键计时
                        ShortTouchFlag1=1;        //短按标志位置1
                }
        }
        
        if(time_min==0)                  //如果按键2按下
        {
            uiKeyTimeCnt2++; //累加按键计时
        }
        if(time_min==1)                  //当按键松开(也可能是抖动弹开)
        {
                if(uiKeyTimeCnt2>2&&uiKeyTimeCnt2<=200)          //只有大于20mS,且小于2秒,才判为短按
                {
                        uiKeyTimeCnt2=0;        //清零按键计时
                        ShortTouchFlag2=1;        //短按标志位置1
                }
        }
        
        if(on_off==0)                  //如果按键2按下
        {
            uiKeyTimeCnt3++; //累加按键计时
        }
        if(on_off==1)                  //当按键松开(也可能是抖动弹开)
        {
                if(uiKeyTimeCnt3>2&&uiKeyTimeCnt3<=200)          //只有大于20mS,且小于2秒,才判为短按
                {
                        uiKeyTimeCnt3=0;        //清零按键计时
                        ShortTouchFlag3=1;        //短按标志位置1
                }
        }
        
        if(mode_set==0)                  //如果按键2按下
        {
            uiKeyTimeCnt4++; //累加按键计时
        }
        if(mode_set==1)                  //当按键松开(也可能是抖动弹开)
        {
                if(uiKeyTimeCnt4>2&&uiKeyTimeCnt4<=200)          //只有大于20mS,且小于2秒,才判为短按
                {
                        uiKeyTimeCnt4=0;        //清零按键计时
                        ShortTouchFlag4=1;        //短按标志位置1
                }
        }
}

/******************************
函数说明:按键处理
******************************/
void Key_process(void)
{
        if(ShortTouchFlag2 == 1)         //当按键1短按标志位为1时进行按键1短按处理
        {
               
                ShortTouchFlag2=0;        //清除短按标志位
                minute++;                        //短按按键1的效果:分钟加1
                if(minute==60)                //如果设置分钟达到60分
                        minute=0;                //将分钟清零
                if(second>0)                //以下将实际计时的小时和分钟转换为数码管显示的小时和分钟。比如计时还有00:00:20秒时,数码管显示的是00:01。
                        show[5]=minute+1;  //只要秒不为零,则显示出来的分钟应该比实际计时的分钟加1
                else
                        show[5]=minute;           //只有秒为零时,显示的分钟才和计时的分钟一致
                if(show[5]==60)                   //如果显示的分钟为60分,则显示的小时比实际计时的小时+1。
                {
                        show[5]=0;
                        show[4]=hour+1;
                }
                else
                        show[4]=hour;           //如果显示的分钟比实际的加1后还不到60分钟,则显示的小时和计时的小时一致。
                show[0]=show[4]/10%10; //显示的小时十位数
                show[1]=show[4]%10;           //显示的小时个位数
                show[2]=show[5]/10%10; //显示的分钟十位数
                show[3]=show[5]%10;           //显示的分钟个位数                        
                TM1637_display(show[0],show[1],show[2],show[3],1); //显示定时时间。在按键调时状态下,时间的冒号固定显示不闪烁。
        }
        if(ShortTouchFlag1==1)         //当按键1长按标志位为1时进行按键1长按处理
        {
                ShortTouchFlag1=0;        //清除长按标志位
                hour++;                                //按键1长按的效果:小时加1
                if(hour==100)                //如果设定的小时到100了
                        hour=0;                        //小时清零。定时时间最高只能设定99小时59分钟。
                if(second>0)                //以下将实际计时的小时和分钟转换为数码管显示的小时和分钟,同上。
                        show[5]=minute+1;
                else
                        show[5]=minute;
                if(show[5]==60)
                {
                        show[5]=0;
                        show[4]=hour+1;
                }
                else
                        show[4]=hour;
                show[0]=show[4]/10%10;        //显示的小时十位数
                show[1]=show[4]%10;                //显示的小时个位数
                show[2]=show[5]/10%10;        //显示的分钟十位数
                show[3]=show[5]%10;                //显示的分钟个位数               
                TM1637_display(show[0],show[1],show[2],show[3],1); //显示定时时间。在按键调时状态下,时间的冒号固定显示不闪烁。
        }        
        if(ShortTouchFlag3==1)        //按键2短按处理,在停止状态下短按为启动运行
        {
                ShortTouchFlag3=0;        //清除短按标志位
                if(mode==0&&(minute>0||hour>0))         //只在停止状态下且设定了有效的定时时间,才能响应短按
                {
                        mode=1;                        //短按的效果:启动定时
//                        relay=~delay_OnOff;         //开始定时后,根据开机时设定的延时断电或延时上电,继电器进行相应动作。如设定的是延时断电(delay_OnOff为0),启动后继电器应该先通电动作。
                }
        }

}

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:213173 发表于 2023-5-13 15:59 | 只看该作者
始终不能进入if(IR_Flag) 和 if(IR_KEY) 当然是这两个判断条件不能满足,要在红外解码函数和外部中断0函数中找原因。
回复

使用道具 举报

板凳
ID:202023 发表于 2023-5-13 16:31 | 只看该作者
你在程序中找IR_Flag,发现他受 if(Start_Flag)                                                               
        {
                if(IR_time<70&&IR_time>32)        //判断引导码(9ms+4.5ms)
                //IR_time大约要溢出32次(9ms/0.278ms)到70次(可以大于(9+4.5)/0.278)
                        IR_bit=0;                                                                        //清除位数变量,确保当前IR_bit为0,表示引导码
                IR_data[IR_bit]=IR_time;                //存储相应位时间宽度
                IR_time=0;                                                                        //清零时间宽度计数值
                IR_bit++;                                                                                //位数变量自增
                if(IR_bit==33)                                                        //如果达到了33位(引导码+32个数据位)
                {
                        IR_Flag=1;                                                                //红外数据接收完成标志位置1
                        IR_bit=0;                                                                        //位数变量清零
                }
        }控制,这个语句有2个条件,1,Start_Flag必须等于1,    2。IR_bit==33,那你继续向下追踪,看这两个条件怎样才能成立。
回复

使用道具 举报

地板
ID:227393 发表于 2023-5-13 21:06 来自手机 | 只看该作者
划线处加IR_FLAG=1;

IMG_20230513_210538.jpg (182.87 KB, 下载次数: 65)

IMG_20230513_210538.jpg
回复

使用道具 举报

5#
ID:883242 发表于 2023-5-13 21:49 | 只看该作者
既然都用上仿真了,IR_Flag和IR_KEY的值会看不到?
回复

使用道具 举报

6#
ID:468415 发表于 2023-5-17 16:04 | 只看该作者
谢谢各位,问题是P3.0和P3.1口的问题,已经改掉,仿真的时候这个口会有问题!
回复

使用道具 举报

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

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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