51单片机-按键上升沿/下降沿检测

在这段C51代码中,上升沿和下降沿的检测是通过位运算状态变量实现的,具体逻辑如下:


下降沿检测(Key_Down)​

  1. Key_Val ^ Key_Old:对当前键值和上一次键值进行异或操作。若键值从1变为0(下降沿),异或结果为1;否则为0
  2. Key_Val & ...:当前键值为0时(按键被按下),保留异或结果;若当前键值为1(按键未按下),则结果为0

上升沿检测(Key_Up)​

  1. ~Key_Val:对当前键值取反。若按键被松开(当前键值为0),取反后为1
  2. Key_Val ^ Key_Old:若键值从0变为1(上升沿),异或结果为1
  3. ~Key_Val & ...:仅在按键从“按下”变为“松开”时(即上升沿),Key_Up1;否则为0。
/*按键读取*/
unsigned char Key_Read()
{
	unsigned char temp=0;//要给初始值0,否则可能报错
	if(P3_4==0)temp=1;
	if(P3_5==0)temp=2;
	if(P3_6==0)temp=3;
	if(P3_7==0)temp=4;
	
	return temp;
}


/*变量声明*/
unsigned char Key_Val,Key_Down,Key_Up,Key_Old;


/*Main*/
void main()
{

	while(1)
	{
		Key_Val=Key_Read();//读取键码值,while中不断扫描,每次扫描都会将temp重置为0并判断
		Key_Down=Key_Val&(Key_Val^Key_Old);//检测下降沿,按下瞬间		
		Key_Up=~Key_Val&(Key_Val^Key_Old);//检测上升沿,抬手瞬间	
		Key_Old=Key_Val;//扫描辅助变量,一直按着的时候

        //测试代码
		if(Key_Down==1)P1_0=0;
		if(Key_Up==2)P1_0=1;
		if(Key_Old==3)
			P1_1=0;
		else
			P1_1=1;
		if(Key_Down==4)
			P1_3=~P1_3;
	}
}

在按下瞬间,为什么能检测不同数字呢
这个问题我问了gpt,它说只能是0或1,但通过仿真是对的
于是我深究原理

下降沿检测(Key_Down)​

此时我们假设我们要按下按键P3_6,那么temp的值将由0->3
Key_Val=3;//0011
Key_Old=0;//0000
那么,Key_Down=0011&(0011^0000),此处只有11和00不同,及为1,异或结果为0011,所以0011&0011还是得到0011,即为3。

上升沿检测(Key_Up)

接下来我们假设我们要松开按键P3_6,那么temp的值将由3->0
Key_Val=0;//0000
Key_Old=3;//0011
那么,Key_Up=1111&(0000^0011),此处只有11和00不同,异或结果为0011,所以1111&0011还是得到0011,即为3。

    现在我需要做一个基于单片机STC89C52RC的心率仪 我将给你两段程序 一段是语音播报程序 一端是心率仪测量程序 我希望你将二者进行组合 同时改变一些功能 即最后实现自动在10s内测量心率且进行心率播报 以下是代码部分: 1.语音播报部分 //-----------------------------------------------------------------------------// //---------心率仪语音播报C51程序框架---by tjx----------------------------------// //---------------------2023.04-------------------------------------------------// //------------------crystal=6MHz-----------------------------------------------// //-----------------------------------------------------------------------------// //-----------------------------------------------------------------------------// //包含头文件、定义各个接口-----------------------------------------------------// //-----------------------------------------------------------------------------// #include <AT89X52.H> #define uint unsigned int //定义一下方便编程 #define uchar unsigned char //定义一下方便编程 #define keystart P1_0 //定义测量开始启动按钮口 #define SOUND_RESET P1_6 //定义语音芯片复位口 #define SOUND_DATA P1_5 //定义语音芯片数据口 #define SOUND_BUSY P1_4 //定义语音芯片忙碌口 uint heartrate=123; //定义心率值变量并赋初值 uchar ge,shi,bai; //定义个位、十位、百位要显示、播报的数值 //-----------------------------------------------------------------------------// //函数声明---------------------------------------------------------------------// //-----------------------------------------------------------------------------// void delay1ms(uint count); //毫秒级延时(不是很准) void sound_reset(void); //复位语音芯片 void speek(unsigned char num); //播报地址对应的声音 void speekone(void); //播报一声“叮” void speekheartrate(void); //播报心率值 //-----------------------------------------------------------------------------// //主函数,开机报报叮叮咚咚的声音-----------------------------------------------// //-----------------------------------------------------------------------------// void main(void) { speek(28); //开机播报“叮叮咚咚”的声音 while(1) { if(keystart==0) { delay1ms(80); if(keystart==0) { speekone(); bai=heartrate/100; //计算百位数值 shi=(heartrate/10)%10; //计算十位数值 ge=heartrate%10; //计算个位数值 speekheartrate(); //播报心率值(需补全该函数) } } } } //-----------------------------------------------------------------------------// //延时1ms的函数----------------------------------------------------------------// //-----------------------------------------------------------------------------// void delay1ms(uint count) { int m,n; for(m=0;m<count;m++) for(n=0;n<59;n++) ; } /*以下为语音芯片复位的函数*/ void sound_reset(void) { SOUND_RESET=1; delay1ms(2); SOUND_RESET=0; delay1ms(2); } /*以下为播报某个地址声音的函数*/ void speek(unsigned char num) { unsigned char i=0; while(SOUND_BUSY==0); sound_reset(); if((num>=0)&&(num<=9)) { for(i=0;i<num+2;i++) { SOUND_DATA=1; delay1ms(1); SOUND_DATA=0; delay1ms(1); } } else { for(i=0;i<num;i++) { SOUND_DATA=1; delay1ms(1); SOUND_DATA=0; delay1ms(1); } } } /*以下为播报“叮——”的函数*/ void speekone(void) { while(SOUND_BUSY==0); speek(33); } /*以下为播报心率值的函数*/ void speekheartrate(void) { //后面的自己补全 //播报百位 //播报十位 //播报个位 } //-----------------------------------------------------------------------------// //程序完毕---------------------------------------------------------------------// //-----------------------------------------------------------------------------// 2.下面是心率仪框架: //-----------------------------------------------------------------------------// //---------------心率仪C51程序框架---by tjx------------------------------------// //---------------------2022.12-------------------------------------------------// //------------------crystal=6MHz-----------------------------------------------// //-----------------------------------------------------------------------------// //-----------------------------------------------------------------------------// //包含头文件、定义各个接口-----------------------------------------------------// //-----------------------------------------------------------------------------// #include <AT89X52.H> #define uint unsigned int //定义一下方便编程 #define uchar unsigned char //定义一下方便编程 #define keystart P1_0 //定义心率测量启动按键输入口------不按输入为1电平,按下输入为0电平 #define ledtest P3_4 //定义测量指示灯控制口------------输出1电平灯不亮,输出0电平灯亮 #define signal P2_0 //定义心跳信号输入口--------------根据心跳信号电平高低变化 #define keymodel P1_3 //定义测量模式设置按键输入口------按下输入0电平,松开输入1电平 #define ledmodle P1_7 //定义工作模式指示灯控制口--------输出1电平灯不亮,输出0电平灯亮,用来指示测量模式 #define baiwei P2_1 //定义百位驱动口------------------百位数码管输出控制端,输出0电平对应的百位点亮,输出1电平对应的百位不亮 #define shiwei P2_4 //定义十位驱动口------------------十位数码管输出控制端,输出0电平对应的十位点亮,输出1电平对应的十位不亮 #define gewei P2_7 //定义个位驱动口------------------个位数码管输出控制端,输出0电平对应的个位点亮,输出1电平对应的个位不亮 code uchar datab[10]={0xA0,0xAF,}; //定义共阳极七段译码值,对应0-9的10个数字,请补全10个数字的码值。也可以自己编制特别字符 //用code关键字是为了把数组定义在程序存储区 //-----------------------------------------------------------------------------// //函数声明---------------------------------------------------------------------// //-----------------------------------------------------------------------------// void mcuint(void); //单片机初始化函数 void delay1ms(uint count); //毫秒级延时函数 void measure_slow(void); //心率测量函数 快速测量心率的函数自行考虑 #define SOUND_RESET P1_6 //定义语音芯片复位口 #define SOUND_DATA P1_5 //定义语音芯片数据口 #define SOUND_BUSY P1_4 //定义语音芯片忙碌口 uint heartrate=123; //定义心率值变量并赋初值 uchar ge,shi,bai; //定义个位、十位、百位要显示、播报的数值 //-----------------------------------------------------------------------------// //主函数,开机初始化单片机,并显示2s的888,用于检测笔画是否接正确--------------// //-----------------------------------------------------------------------------// void main(void) { mcuint(); //heartrate=888; //delay1ms(800); heartrate=321; while(1) { measure_slow(); } } //-----------------------------------------------------------------------------// //单片机初始化函数-------------------------------------------------------------// //-----------------------------------------------------------------------------// void mcuint(void) { P0=0xFF; //使单片机上电后P0口都输出1 P1=0xFF; //使单片机上电后P1口都输出1 P2=0xFF; //使单片机上电后P2口都输出1 P3=0xFF; //使单片机上电后P3口都输出1 TMOD=0x11; //设定定时器T0、T1的工作模式为16位定时器 TL0=(65536-display_period*500)%256; //设定定时器T0的低8位初值---T0用于刷新数码管显示 TH0=(65536-display_period*500)/256; //设定定时器T0的高8位初值---T0用于刷新数码管显示 TL1=(65536-100*500)%256; //设定定时器T1的低8位初值---100为100毫秒,1次计时100毫秒,600次就是1分钟 TH1=(65536-100*500)/256; //设定定时器T1的高8位初值 IE=0x8A; //开总中断、定时器T0中断、定时器T1中断 IP=0x08; //设定中断优先级,定时器T1中断优先 TR0=1; //开启定时器T0,数码管开始定时刷新显示 } //-----------------------------------------------------------------------------// //延时1ms的函数----------------------------------------------------------------// //-----------------------------------------------------------------------------// void delay1ms(uint count) { int m,n; for(m=0;m<count;m++) for(n=0;n<59;n++) ; } //-----------------------------------------------------------------------------// //T0中断服务函数,作用是每5ms扫描一次数码管------------------------------------// //-----------------------------------------------------------------------------// void timer0() interrupt 1 { TL0=0x3C; TH0=0xF6; //假设每分钟跳135次 ,heartrate=321 bai=heartrate/100; //计算百位数值 shi=(heartrate/10)%10; //计算十位数值 ge=heartrate%10; //计算个位数值 //以下三个语句为显示个位的数值 P0=datab[ge]; //把各位显示数值的码值取出来给P0 gewei=0; //P2_7=0; 相当于开启个位的电源 delay1ms(1); gewei=1; //P2_7=1; 相当于关闭个位的电源 //请补全后面的显示十位、百位的语句 } //-----------------------------------------------------------------------------// //T1中断函数,作用是1分钟计时,时间到置标志位为1------------------------------// //-----------------------------------------------------------------------------// void timer1() interrupt 3 { TL1=0xB0; TH1=0x3C; i++; if(i==600) //600次计时到,则1分钟时间到 { i=0; //计数变量归0 flag=1; //时间到标志位置1 } } //-----------------------------------------------------------------------------// //慢速测量心率函数-------------------------------------------------------------// //-----------------------------------------------------------------------------// void measure_slow(void) { if(keystart==0) { TR1=1; //开启定时器,开始60秒计时 ledtest=0; //点亮测量指示灯 //剩下的自己思考后补全 } } 请你学习代码中的注释 并完成任务
    最新发布
    09-26
    这是一个基于单片机STC89C52RC而编写的程序而实现的心率仪的项目 我将给你一段代码 但不完整 你需要帮我实现以下功能 可以实现自动测心率 即把手指放到传感器上时 不用按键也可自动测心率 时间在30s内完成 以下是代码: //-----------------------------------------------------------------------------// //---------------心率仪C51程序框架---by tjx------------------------------------// //---------------------2022.12-------------------------------------------------// //------------------crystal=6MHz-----------------------------------------------// //-----------------------------------------------------------------------------// //-----------------------------------------------------------------------------// //包含头文件、定义各个接口-----------------------------------------------------// //-----------------------------------------------------------------------------// #include <AT89X52.H> #define uint unsigned int //定义一下方便编程 #define uchar unsigned char //定义一下方便编程 #define keystart P1_0 //定义心率测量启动按键输入口------不按输入为1电平,按下输入为0电平 #define ledtest P3_4 //定义测量指示灯控制口------------输出1电平灯不亮,输出0电平灯亮 #define signal P2_0 //定义心跳信号输入口--------------根据心跳信号电平高低变化 #define keymodel P1_3 //定义测量模式设置按键输入口------按下输入0电平,松开输入1电平 #define ledmodle P1_7 //定义工作模式指示灯控制口--------输出1电平灯不亮,输出0电平灯亮,用来指示测量模式 #define baiwei P2_1 //定义百位驱动口------------------百位数码管输出控制端,输出0电平对应的百位点亮,输出1电平对应的百位不亮 #define shiwei P2_4 //定义十位驱动口------------------十位数码管输出控制端,输出0电平对应的十位点亮,输出1电平对应的十位不亮 #define gewei P2_7 //定义个位驱动口------------------个位数码管输出控制端,输出0电平对应的个位点亮,输出1电平对应的个位不亮 code uchar datab[10]={0xA0,0xAF,}; //定义共阳极七段译码值,对应0-9的10个数字,请补全10个数字的码值。也可以自己编制特别字符 //用code关键字是为了把数组定义在程序存储区 uint heartrate=0; //定义心率值存储变量 uchar ge,shi,bai; //定义心率计数三个位的显示值 uint i=0; //定义全局变量,用于定时计数 bit flag=0; //定义时间标志位变量 uchar display_period=5; //一个显示周期的时间长度,单位为ms,5ms=5000us,与实习报告所测得的一致 //-----------------------------------------------------------------------------// //函数声明---------------------------------------------------------------------// //-----------------------------------------------------------------------------// void mcuint(void); //单片机初始化函数 void delay1ms(uint count); //毫秒级延时函数 void measure_slow(void); //心率测量函数 快速测量心率的函数自行考虑 //-----------------------------------------------------------------------------// //主函数,开机初始化单片机,并显示2s的888,用于检测笔画是否接正确--------------// //-----------------------------------------------------------------------------// void main(void) { mcuint(); //heartrate=888; //delay1ms(800); heartrate=321; while(1) { measure_slow(); } } //-----------------------------------------------------------------------------// //单片机初始化函数-------------------------------------------------------------// //-----------------------------------------------------------------------------// void mcuint(void) { P0=0xFF; //使单片机上电后P0口都输出1 P1=0xFF; //使单片机上电后P1口都输出1 P2=0xFF; //使单片机上电后P2口都输出1 P3=0xFF; //使单片机上电后P3口都输出1 TMOD=0x11; //设定定时器T0、T1的工作模式为16位定时器 TL0=(65536-display_period*500)%256; //设定定时器T0的低8位初值---T0用于刷新数码管显示 TH0=(65536-display_period*500)/256; //设定定时器T0的高8位初值---T0用于刷新数码管显示 TL1=(65536-100*500)%256; //设定定时器T1的低8位初值---100为100毫秒,1次计时100毫秒,600次就是1分钟 TH1=(65536-100*500)/256; //设定定时器T1的高8位初值 IE=0x8A; //开总中断、定时器T0中断、定时器T1中断 IP=0x08; //设定中断优先级,定时器T1中断优先 TR0=1; //开启定时器T0,数码管开始定时刷新显示 } //-----------------------------------------------------------------------------// //延时1ms的函数----------------------------------------------------------------// //-----------------------------------------------------------------------------// void delay1ms(uint count) { int m,n; for(m=0;m<count;m++) for(n=0;n<59;n++) ; } //-----------------------------------------------------------------------------// //T0中断服务函数,作用是每5ms扫描一次数码管------------------------------------// //-----------------------------------------------------------------------------// void timer0() interrupt 1 { TL0=0x3C; TH0=0xF6; //假设每分钟跳135次 ,heartrate=321 bai=heartrate/100; //计算百位数值 shi=(heartrate/10)%10; //计算十位数值 ge=heartrate%10; //计算个位数值 //以下三个语句为显示个位的数值 P0=datab[ge]; //把各位显示数值的码值取出来给P0 gewei=0; //P2_7=0; 相当于开启个位的电源 delay1ms(1); gewei=1; //P2_7=1; 相当于关闭个位的电源 //请补全后面的显示十位、百位的语句 } //-----------------------------------------------------------------------------// //T1中断函数,作用是1分钟计时,时间到置标志位为1------------------------------// //-----------------------------------------------------------------------------// void timer1() interrupt 3 { TL1=0xB0; TH1=0x3C; i++; if(i==600) //600次计时到,则1分钟时间到 { i=0; //计数变量归0 flag=1; //时间到标志位置1 } } //-----------------------------------------------------------------------------// //慢速测量心率函数-------------------------------------------------------------// //-----------------------------------------------------------------------------// void measure_slow(void) { if(keystart==0) { TR1=1; //开启定时器,开始60秒计时 ledtest=0; //点亮测量指示灯 //剩下的自己思考后补全 } }
    09-25
    评论
    成就一亿技术人!
    拼手气红包6.0元
    还能输入1000个字符
     
    红包 添加红包
    表情包 插入表情
     条评论被折叠 查看
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值