赞
踩
对于蓝桥杯的单片机竞赛,主观题很重要,占了百分之70-80的大头,因此主观题做得怎么样决定了比赛是否能拿奖,而客观题的正确率很大程度上决定了你能否获得省一,从而进入决赛。因此我在这里分享一期关于主观题中各个模块的编写规范和注意事项。
本人参加了2022年的第十四届蓝桥杯大赛,并有幸获得了电子类单片机个人赛的省二。同时还手把手教出了一个省二,辅导出了一个国二。很遗憾自己没有进入决赛,刚出成绩的时候,打抱不平,觉得很不甘心。但后来想了想还是自己的个人问题,虽然主观题做的很好,但客观题错了六个。虽然都说客观题,三分靠实力,七分靠运气,但我大二上学期数电模电学的确实不咋滴,这也导致客观题我一遇到数电模电就头大,而恰巧这次蓝桥杯省赛数电模电题占了一大半。
所以我想强调的是学习嵌入式,除了学好应用层的知识,会写代码,我们还要学会数电模电硬件层电路相关知识,这样今后才能成为一名合格的嵌入式攻城狮。我在最近几个月学习STM32的时候,同时也对数电模电进行了相关温故,这使我对理解相关总线协议的工作原理和各种外设的工作方式有了很大帮助。
蓝桥杯全模块代码+部分国赛省赛程序下载
https://download.csdn.net/download/qq_25218501/87965874
因为WR接GND,所以当Y5为低电平,从而使Y5C为高电平(接通锁存器),从而可以使用P0口作为I/O输出,当P0^6输出低电平,从而使Q7为0,关闭蜂鸣器
三种关闭蜂鸣器写法:
//关闭蜂鸣器习惯写法
sbit buzzer=P0^6;//蜂鸣器
P2=0xa0;;buzzer=0;P2=0x00;
//IO控制
#include"stc15.h"
sbit buzzer=P0^6;//蜂鸣器
void main()
{
P2=0xa0;;buzzer=0;P2=0x00;//关闭蜂鸣器
while(1)
{
//...
}
}
MM控制方法,此时WR由P3^6自动控制,因为P2和P0作为地址总线,所以P2接收高8位,P0接收低8位
首先接通Y5C锁存器,然后使RELAY为低电平,从而接通继电器,最后再关闭锁存器
#include"stc15.h"
sbit buzzer=P0^6;//蜂鸣器
sbit relay=P0^4;
void main()
{
P2=0xa0;buzzer=0;relay=0;P2=0x00;//关闭蜂鸣器
while(1)
{
//...
}
}
关于三行代码的理解
trg:触发一次,表示按键被按下,是个瞬时值
cont:表示按键状态,如果按下(未弹起),为对应键值
长按过程trg和cont的变化
假设按下的按键键值为key
刚按下按键时,trg对应的值为key,但key只可读一次,之后都为0。
cont在按键未弹起时值始终为key。
如何实现短按
若不涉及长按,直接进行trg值的判断即可(或使用switch case语句批量实现不同键值的操作),但如果
涉及长按,该方法仍可以实现短按功能,但不可与长按独立开
如何实现长按
显然,可以将trg看作按键按下的标志,由于长按是通过时间T(从按下开始)判断的,则可通过trg触发定
时器工作,产生计数值可与T进行比较,当计数值>T时,此时可停止定时器中计数值的累加,同时抛出
标志位进行规定的长按操作。
如何实现长按与短按的独立
若实现短按使用trg进行判别,不论长按的标志位是否可执行,使用trg进行判别及操作必然会先于长按
执行(且必然执行)。为解决冲突,此时可以在定时器计数值累加且计数值<T时进行判别,在判别时轮询
cont的状态,若按键弹开(cont不为key或者cont为0),此时触发短按功能,停止定时器中计数值的累
加,同时抛出标志位进行规定的短按操作。若在定时器计数值累加且计数值<T时按键始终未弹开,此时
会由计数值累加==T,即达到长按的状态,停止定时器中计数值的累加,同时抛出标志位进行规定的长
按操作。由于长短按键的判别都是通过定时器设定计数值与T进行比较确定的,当停止定时器中计数值的
累加可保证长按与短按功能的独立。
关于共阳极和共阴极的问题,总会有人来问我,怎么看共阴极和共阴极。
答:共阴极、共阳极,顾名思义,公共端口连接电源,就是共阳极;公共端口连接GND,就是共阴极。共阳极就是高电平有效,共阴极为低电平有效。
位选不选择任何数码管(消除影子)->再段选->再位选——(实测最有效)
先段选不选中数码管的任何段->再位选->再段选——(可能仍会有)
写法:
P2=0xc0;P0=0x00;P2=0x00;//位选
P2=0xe0;P0=0xXX;P2=0x00;//段选
P2=0xc0;P0=0xXX;P2=0x00;//位选
注意模式一定要选择16位自动重载(实际比赛中也只用它),时钟选择1T。
补充:IAP15不像89C51,89C51只有定时器工作方式2可以设置自动重新装载并且只有8个位;在IAP15中,每个定时器都可以设置为重新装载,并且还是16位的,大大提高了单片机的工作效率,也节省了程序编写。
void Timer0Init(void) //1毫秒@12.000MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x20; //设置定时初值
TH0 = 0xD1; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
/*中断开启要自己添加*/
ET0 = 1; //开启定时器0中断
EA = 1;//开启总中断
}
void Timer1Init(void) //1毫秒@12.000MHz
{
AUXR |= 0x40; //定时器时钟1T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x20; //设置定时初值
TH1 = 0xD1; //设置定时初值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
/*中断开启要自己添加*/
ET1 = 1; //开启定时器1中断
EA = 1;//开启总中断
}
void Timer2Init(void) //1毫秒@12.000MHz
{
AUXR |= 0x04; //定时器时钟1T模式
T2L = 0x20; //设置定时初值
T2H = 0xD1; //设置定时初值
AUXR |= 0x10; //定时器2开始计时
/*中断开启要自己添加*/
IE2 |= 0x04; //开启定时器2中断
EA = 1;//开启总中断
}
具体使用可如图位置参考例程
单片机中断程序中,例如T0,如果不对TH0和TL0赋值,那么定时器的初值就为0,这种情况常用于计数模式,同样适用于某些计时如超声波
注意P34跳冒接法。
#include"stc15.h" typedef unsigned int u16; typedef unsigned char u8; u8 code t_display[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; u8 code T_COM[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x00}; //位码 u8 buff[8]={0}; u8 Trg,Cont,key_flag,mod; long signal; u16 time; void TimerInit(void) //1毫秒@11.0592MHz { AUXR |= 0x40; //定时器时钟1T模式 TMOD &= 0x0F; //设置定时器模式 TMOD |= 0x15; //设置定时器模式 TL1 = 0xCD; //设置定时初值 TH1 = 0xD4; //设置定时初值 TF1 = 0; //清除TF1标志 TH0=0; TL0=0; ET1=1; EA=1; TR1=1; } void init() { P2=0xa0;P0=0xaf; P2=0x80;P0=0xff; } void Readkey() { u8 temp,ReadDate; P3=0xf0; P42=1;P44=1; P36=P42; P37=P44; temp=P3; P3=0x0f; P42=0; P44=0; ReadDate=0xff^(P3|temp); Trg=ReadDate&(ReadDate^Cont); Cont=ReadDate; } void number_display(u16 num,u8 mod) { buff[0]=~0x40; buff[1]=~t_display[mod]; buff[2]=~0x40; buff[3]=~t_display[num/10000]; buff[4]=~t_display[num/1000%10]; buff[5]=~t_display[num/100%10]; buff[6]=~t_display[num/10%10]; buff[7]=~t_display[num%10]; } void main() { u8 flag=0; init(); TimerInit(); mod=1; while(1) { if(key_flag) { key_flag=0; Readkey(); if(Trg==0x88) { if(mod==1) mod=2; else if(mod==2) mod=1; } if(Trg==0x84) { flag=1; TR0=1; } } if(flag==1) time=1000000/signal; } } void time1() interrupt 3 { static u16 count=0,i=0; TL1 = 0xCD; TH1 = 0xD4; if(mod==1) { number_display(signal,1); }else{ number_display(time,2); } if(++count%2==0) { P2=0xc0;P0=0x00; P2=0xe0;P0=buff[i]; P2=0xc0;P0=T_COM[i++]; if(i==8) i=0; } if(count%10==0) key_flag=1; if(count==1000) { signal=(TH0<<8)+TL0;//注意算数优先级 count=0; TH0=0; TL0=0; } }
DS18B20使用单总线,顾名思义就是使用P14一个I/O口来控制输入和输出
DS18B20初始化(为了检测单片机上是否存在DS18B20)(前半段为单片机向DS18B20发送的信号,后半段为DS18B20向单片机发送的信号):
!
//初始化18B20
bit init_ds18b20(void)
{
bit initflag=0;
DQ=1; //P14
delay(12); //先延时一段时间
DQ=0; //拉低总线
delay(80); //延时大约480-960us
DQ=1; //拉高总线
delay(10); //延时大约15-60us
initflag=DQ;//如果initflag等于1则初始化失败
delay(5); //延时大约60-240us
return initflag;
}
//单片机向18B20发数据函数
void wr_ds18b20(unsigned char byt)
{
unsigned char i;
for(i=0;i<8;i++)
{
DQ=0;
DQ=byt&0x01;
delay(5);//延时约60us
DQ=1;
byt>>=1;
}
delay(5);
}
操作步骤
注意
//单片机读取数据函数 unsigned char rd_ds18b20(void) { unsigned char i; unsigned char byt; for(i=0;i<8;i++) { DQ=0; byt>>=1; //单片机每执行一个指令约为1us DQ=1; if(DQ) { byt|=0x80; } delay(5);//延时约10us } return byt; }
//读取温度函数 float rd_temperature(void) { unsigned int temp; float temperature; unsigned char low,high; init_ds18b20(); wr_ds18b20(0xcc); wr_ds18b20(0x44);//启动温度转换 Delay_OneWire(200); init_ds18b20(); wr_ds18b20(0xcc); wr_ds18b20(0xbe);//读取寄存器 low=rd_ds18b20();//低字节 high=rd_ds18b20();//高字节 /*精度为0.0625摄氏度*/ temp=high; temp<<=8; temp|=low; temperature=temp*0.0625; return temperature; }
红色圈处不能返回,如果返回,数码管就会在实际温度和10000(乘10000在主函数内)之间来回闪烁切换,
正确写法
代码需要如下修改:
延时函数增加t要先乘10
然后主程序如下
SCL为串行时钟线,SDA为串行数据线
启动:SCL为高电平时,SDA由高电平向低电平变化
停止:SCL为高电平时,SDA由低电平向高电平变化
//总线启动函数 void IIC_Start(void) { SCL=1; SDA=1; somenop; //>4.7us SDA=0; somenop; //>4.7us SDL=0; } //总线停止条件 void IIC_Stop(void) { SDA=0; SCL=1; somenop; //>4.7us SDA=1; }
bit IIC_WaitAck(void) { SDA=1; somenop; SCL=1; somenop; if(SDA) { SCL=0; IIC_Stop(); return 0; } else { SCL=0; return 1; } }
void write_adc(unsigned char add)
{
IIC_Start(); //启动IIC协议
IIC_SendByte(0x90); //通过IIC协议发送PCF8591地址和读写命令
IIC_WaitAck(); //单片机等待PCF8951应答
IIC_SendByte(add); //通过IIC发送ADC采样通道控制字,AIN0—AIN3,例如选择通道0,则add=0x00
IIC_WaitAck(); //等待应答
IIC_Stop(); //停止总线
}
unsigned char read_adc(unsigned char add) { unsigned char temp; IIC_Start(); IIC_SendByte(0x90); IIC_WaitAck(); IIC_SendByte(add); IIC_WaitAck(); IIC_Start(); IIC_SendByte(0x91); IIC_WaitAck(); temp=IIC_Recbyte(); IIC_WaitAck();//告诉单片机是否读取成功 IIC_Stop(); return temp; }
unsigned char AD_Get(unsigned char add)
{
unsigned char temp;
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(add);
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
temp=IIC_RecByte();
IIC_Stop();
return temp;
}
单独读取,光敏为0x01,RB2位0x03。
dat取值0—255,对应0—5V。
分辨率=模拟量/2^位数 数字量=分辨率*最大电压量
0x40:DAC输出模式;
电压输出有时会干扰AD读取,其中一种解决方法是在读取之前关中断,然后开中断。
void DAC_out(unsigned char dat)
{
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(0x40);
IIC_WaitAck();
IIC_SendByte(dat);
IIC_WaitAck();
IIC_Stop();
}
如果单独读取,add=1时为光敏,add=3时RB2;若两者都要读,则要交换add的值或者对同一个值读取两次以确保数据正确。
方法1:将读取光敏的地址改为0x03,读取RB2的地址改为0x01。
方法2:地址保持不变,连续读取两次。代码如下:
AD_Get(0x01);
luminance = AD_Get(0x01); //读取光敏
AD_Get(0x01);
humidity = AD_Get(0x01) / 255.0 * 99; //读取RB2
上电瞬间读出的RB2电压值会有异常,可以多次读取RB2电压值或者在判断时多次验证以确保读取值为稳定的电压。
void write_24c02(unsigned char add,unsigned char data1)
{
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(add); //AT245C02为EEPROM芯片,存储单元地址为0x00-0xFF
IIC_WaitAck();
IIC_SendByte(data1); //往单元格地址里面写数据
IIC_WaitAck();
IIC_Stop();
}
unsigned char read_24c02(unsigned char add) { unsigned char temp; IIC_Start(); IIC_SendByte(0xa0); IIC_WaitAck(); IIC_SendByte(add); IIC_WaitAck(); IIC_Start(); IIC_SendByte(0xa1); IIC_WaitAck(); temp=IIC_SendByte(); IIC_WaitAck(); IIC_Stop(); return temp; }
定时器中断有的时候会打断读写数据的时序,所以再读写时序前要关闭中断,读写完再打开。关闭EA即可。方法同样适用于DS18B20实验
左边为超声波发射电路,右边为超声波接受电路。
1-3 2-4短接为超声波
5-3 4-6短接为红外发生器
如果上面一路接受到的是正向方波信号,那么下面一路接收到的就是反向方波信号。其中c7、c8电容起耦合作用。两个反向器并联可以提高超声波发射强度。
N_A1接单片机的一个I/O口
左边为超声波测距程序,右边为超声波发送函数
#define somenop _nop_();_nop_();_nop_();_nop_();_nop_();
sbit TX=P10; sbit RX=P11; unsigned int distance,time; #define somenop _nop_();_nop_();_nop_();_nop_();_nop_(); void send_wave(void) { unsigned char i; for(i=0;i<8;i++) { TX=1; somenop();somenop();somenop();somenop();somenop(); TX=0; somenop();somenop();somenop();somenop();somenop(); } } void main() { while(1) { send_wave(); TR1=0; while(RX==1&&TF0==0); TR1=1; if(TF0==1) { TF0=0; distance=999; }else{ time=TH0; time<<=8; time+=TL0; distance=(unsigned int)(time*0.017); } TH0=0; TL0=0; } }
最好将超声波每隔多长时间发送一次,比如200us,这样可以降低外界别的超声波信号干扰。
写数据
读数据
读数据
写数据
简单的说,十进制数12的BCD码就是0x12,34的BCD就是0x34,56的BCD就是0x56,随便哪个十进制数的BCD码其实就是把这个十进制数的每一位按位填到十六进制位里,所为高半字节低半字节有毛的区分,BCD就是按单个十六进制位只表示0-9的方式来表达一个按十六进制位看起来和所要表示的十进制数一致的一种编码形式。低半+0x06,高半+0x60,就是这么单纯。
void getTime() { unsigned char bcd; bcd = Read_Ds1302_Byte(0x81);//读取秒值 miao = bcd/16*10 + bcd%16; bcd = Read_Ds1302_Byte(0x83);//读取分值 fen = bcd/16*10 + bcd%16; bcd = Read_Ds1302_Byte(0x85);//读取小时值 shi = bcd/16*10 + bcd%16; } void buff_set() { buff[0]=~t_display[shi/10]; buff[1]=~t_display[shi%10]; buff[2]=~0x40; buff[3]=~t_display[fen/10]; buff[4]=~t_display[fen%10]; buff[5]=~0x40; buff[6]=~t_display[miao/10]; buff[7]=~t_display[miao%10]; }
void set_time(u8 shi,u8 fen,u8 miao)
{
Write_Ds1302_Byte(0x8e,0);
Write_Ds1302_Byte(0x80,miao/10*16+miao%10);
Write_Ds1302_Byte(0x82,fen/10*16+fen%10);
Write_Ds1302_Byte(0x84,shi/10*16+shi%10);
Write_Ds1302_Byte(0x8e,0x80);
}
注意键盘扫描和串口通信最好别一起用,非要一起用则再键盘扫描中必须避免使用P30和P31,因为在串口通信中P30为RXD,P31为TXD
#include "stc15.h" #include "onewire.h" #include <stdio.h> typedef unsigned int u16; typedef unsigned char u8; bit busy,temp_flag; u8 RI_cnt; u8 RI_buffer[10]; u8 recv; float temp; u8 str[30]={0}; void Timer1Init(void) //1毫秒@11.0592MHz { AUXR |= 0x40; //定时器时钟1T模式 TMOD &= 0x0F; //设置定时器模式 TL1 = 0xCD; //设置定时初值 TH1 = 0xD4; //设置定时初值 TF1 = 0; //清除TF1标志 TR1 = 1; //定时器1开始计时 ET1=1; } void UartInit(void) //2400bps@11.0592MHz { SCON = 0x50; //8位数据,可变波特率 AUXR |= 0x01; //串口1选择定时器2为波特率发生器 AUXR |= 0x04; //定时器2时钟为Fosc,即1T T2L = 0x80; //设定定时初值 T2H = 0xFB; //设定定时初值 AUXR |= 0x10; //启动定时器2 EA=1; ES=1; } void init() { P2=0xa0;P0=0x00; P2=0x80;P0=0xff; } void PrintString(u8 *str) { for(;*str!='\0';*(str++)) { SBUF=*str; busy=1; while(busy); } } void main() { init(); UartInit(); PrintString("hello"); while(rd_temperature()==85); Timer1Init(); while(1) { temp=rd_temperature(); sprintf(str,"%s%6.3f%c%c","temperature:",temp,'\r','\n'); if(temp_flag) { temp_flag=0; PrintString(str); } } } void time1() interrupt 3 { static u16 count; if(++count==500) { temp_flag=1; count=0; } if(recv!=0){ P2=0x80;P0=~(0x01<<(recv-48-1)); } else { P2=0x80;P0=0xff; } } void Uart2() interrupt 4 using 1 { if(RI) { RI=0; recv=SBUF; } if(TI) { TI=0; busy=0; } }
//存一个大于255的数
uint num=23333,read_num;
write_24C02(1,num/256);
Delay10ms();//如果出错就加延时
write_24C02(2,num%256);
//读取
read_num=read_24C02(1)*256;
Delay10ms();//如果出错就加延时
read_num+=read_24C02(2)
void write_24C02(unsigned char add,unsigned char dat) { IIC_Start(); IIC_SendByte(0xa0); IIC_WaitAck(); IIC_SendByte(add); IIC_WaitAck(); IIC_SendByte(dat); IIC_WaitAck(); IIC_Stop(); } unsigned char read_24C02(unsigned char add) { unsigned char temp; IIC_Start(); IIC_SendByte(0xa0); IIC_WaitAck(); IIC_SendByte(add); IIC_WaitAck(); IIC_Start(); IIC_SendByte(0xa1); IIC_WaitAck(); temp=IIC_RecByte(); IIC_Stop(); return temp; }
因为IAP15的速度远快于外设,读取EEPROM的数据过快会产生错误。所以尽量不要在while(1)里持续循环读取存储器的内容,可以设置一个 read_flag 用于标记是否可读取外设,大约每100ms读取一次,关键代码如下:
void time1() interrupt 3 //1ms { static int read_count; ... if(++read_count==100) { read_count=0; read_flag=1; //每100ms将read_flag置1 } ... } void main() { ... while(1) { if(read_flag)//读EEPROM的存储值 { read_flag=0; read_24C02(*,*); } } ... }
原理同上,写EEPROM存储器时,需要增加间隔时间,连续存储EEPROM时,应在数据存储函数后插入Delay10ms(),防止下一次的操作打断上一次的操作,产生未知错误。
如果仍然无法正确存储,则需要在调用读写数据函数之前要关中断即:EA=0; 读写完数据之后再开中断EA=1。
//第一种写法,优先选择。 for(i=0;i<6;i++)//读地址1-6,的数据存放在数组mm_temp_init数组中,记录上次断电时的密码。 { mm_temp_init[i]=read_24c02(i+1); Delay10ms(); } //第一种写法失效时,第二种 for(i=0;i<6;i++) { EA=0; mm_temp_init[i]=read_24c02(i+1); EA=1; Delay10ms(); } //第二种失效,就第三种,这种就是把延时放在关中断里,关中断的时间更长一些 //可能会影响数码管显示,但这是初始化工作,放在while(1)的前面,只会执行一次。 for(i=0;i<6;i++) { EA=0; mm_temp_init[i]=read_24c02(i+1); Delay10ms(); EA=1; }
放在key_function()里,当有确定功能的按键按下时会触发功能,连续写一组数。
在连续采集的时候,会要储存最近几组采集数据。但采集有时间间隔,不算连续写。一般写操作不会出问题。
//同读操作的代码一样,优先第一种,只需要把读24c02的语句改成写操作的
//这里是示范第二种写法,把m[]数组中的数,写到24c02的一段连续空间内
for(k=0;k<6;k++)
{
EA=0;
write_24c02(k,mm[k]);
EA=1;Delay20ms();
}
如果存取仍有问题,可以试试将数据间隔存储,再读取。
//存储
for(k=0;k<6;k++)
{
EA=0;
write_24c02(2k,mm[k]);
EA=1;Delay20ms();
}
//读取
for(k=0;k<6;k++)
{
EA=0;
read=read_24c02(2k);
EA=1;Delay20ms();
}
#include <stc15.h> typedef unsigned int u16; typedef unsigned char u8; u8 code t_display[]={ //标准字库 // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71, //black - H J K L N o P U t G Q r M y 0x00,0x40,0x76,0x1E,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x37,0x6e, 0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x46}; //0. 1. 2. 3. 4. 5. 6. 7. 8. 9. -1 u8 code T_COM[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; //位码 bit key_flag,s4_press,s4_long_press; u8 Cont,Trg; u16 num; u8 table[8]; void buff_set() { table[0]=16; table[1]=16; table[2]=16; table[3]=16; table[4]=16; table[5]=num>=100?num/100%10:16; table[6]=num>=10?num/10%10:16; table[7]=num%10; } void Timer0Init(void) //1毫秒@11.0592MHz { AUXR |= 0x80; //定时器时钟1T模式 TMOD &= 0xF0; //设置定时器模式 TL0 = 0xCD; //设置定时初值 TH0 = 0xD4; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0=1; EA=1; } void init() { P2=0x80;P0=0xff; P2=0xa0;P0=0x00; } void Readkey() { u8 ReadData,key_press; P3=0xf0; P42=1; P44=1; P36=P42; P37=P44; key_press=P3; P3=0x0f; P42=0; P44=0; ReadData=0xff^(P3|key_press); Trg=ReadData&(ReadData^Cont); Cont=ReadData; } void main() { init(); Timer0Init(); while(1) { buff_set(); if(key_flag) { key_flag=0; Readkey(); if(Trg==0x88) { s4_press=1;; if(++num>100) num=0; } if(Cont==0) { s4_press=0;; } } } } void time0() interrupt 1 { static u16 i,smg_count,key_count,s4_count,long_press_count; if(++key_count==100) { key_flag=1; key_count=0; } if(++smg_count%2==0) { smg_count=0; P2=0xc0;P0=0x00; P2=0xe0;P0=~t_display[table[i]]; P2=0xc0;P0=T_COM[i]; if(++i==8) i=0; } if(s4_press) { if(s4_count<1000)s4_count++; if(s4_count==1000) { s4_long_press=1; } if(s4_long_press) { long_press_count++; if(long_press_count==100) { long_press_count=0; if(++num>100) num=0; } } } else { s4_long_press=0; s4_count=0; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。