求大神指导!
红外程序单独使用正常,如果加入一些现实及定时程序后就不正常,然后仿真单步,发现适中不进入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),启动后继电器应该先通电动作。
}
}
}
|