由于最近在做一个用按键控制多级菜单的项目。以前稍有接触,通过自己在网上找的一些资料,以及别人提供的资料。感觉很有用处,并且操作简单,特来分享,希望能对大家有所帮助。
#include "REG52.H"
/* 注释一:
* 本程序用到的变量比较多,所以在keil编译模式里要设置一下编译模式memory model,
* 否则编译会出错.右键单击Target选择“Options for Target'Target1'”就会出来一个框
* 在memory model中选择compact:variables in pdata 就可以了。
*/
#define const_voice_short 40 //蜂鸣器短叫的持续时间
#define const_key_time1 20 //按键去抖动延时的时间
#define const_key_time2 20 //按键去抖动延时的时间
#define const_key_time3 20 //按键去抖动延时的时间
#define const_key_time4 20 //按键去抖动延时的时间
sbit key_sr1=P0^0; //对应朱兆祺学习板的S1键
sbit key_sr2=P0^1; //对应朱兆祺学习板的S5键
sbit key_sr3=P0^2; //对应朱兆祺学习板的S9键
sbit key_sr4=P0^3; //对应朱兆祺学习板的S13键
sbit key_gnd_dr=P0^4; //模拟独立按键的地GND,因此必须一直输出低电平
sbit beep_dr=P2^7; //蜂鸣器的驱动IO口
sbit LCDCS_dr = P1^6; //片选线
sbit LCDSID_dr = P1^7; //串行数据线
sbit LCDCLK_dr = P3^2; //串行时钟线
sbit LCDRST_dr = P3^4; //复位线
void SendByteToLcd(unsigned char ucData); //发送一个字节数据到液晶模块
void SPIWrite(unsigned char ucWData, unsigned char ucWRS); //模拟SPI发送一个字节的命令或者数据给液晶模块的底层驱动
void WriteCommand(unsigned char ucCommand); //发送一个字节的命令给液晶模块
void LCDWriteData(unsigned char ucData); //发送一个字节的数据给液晶模块
void LCDInit(void); //初始化 函数内部包括液晶模块的复位
void display_clear(unsigned char ucFillDate); // 清屏 全部显示空填充0x00 全部显示点阵用0xff
void insert_buffer_to_canvas(unsigned int x,unsigned int y,const unsigned char *ucArray,unsigned char ucFbFlag,unsigned int x_amount,unsigned int y_amount);//把字模插入画布.
void display_lattice(unsigned int x,unsigned int y,const unsigned char *ucArray,unsigned char ucFbFlag,unsigned int x_amount,unsigned int y_amount,unsigned int uiOffSetAddr); //显示任意点阵函数
unsigned char *number_to_matrix(unsigned char ucBitNumber); //把一位数字转换成字模首地址的函数
void delay_short(unsigned int uiDelayshort); //延时
void delay_long(unsigned int uiDelayLong);
void T0_time(); //定时中断函数
void key_service(void); //按键服务的应用程序
void key_scan(void);//按键扫描函数 放在定时中断里
void initial_myself();
void initial_peripheral();
void lcd_display_service(void); //应用层面的液晶屏显示程序
void clear_all_canvas(void); //把画布全部清零
void wd1(void);//窗口1 主菜单
void wd2(void);//窗口2 设置时间
void wd3(void);//窗口3 设置速度
void wd4(void);//窗口4 设置频率
code unsigned char Zf816_0[]=
{
/*-- 文字: 0 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
0x00,0x00,0x00,0x18,0x24,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x24,0x18,0x00,0x00,
};
code unsigned char Zf816_1[]=
{
/*-- 文字: 1 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
0x00,0x00,0x00,0x10,0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x7C,0x00,0x00,
};
code unsigned char Zf816_2[]=
{
/*-- 文字: 2 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x04,0x04,0x08,0x10,0x20,0x42,0x7E,0x00,0x00,
};
code unsigned char Zf816_3[]=
{
/*-- 文字: 3 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
0x00,0x00,0x00,0x3C,0x42,0x42,0x04,0x18,0x04,0x02,0x02,0x42,0x44,0x38,0x00,0x00,
};
code unsigned char Zf816_4[]=
{
/*-- 文字: 4 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
0x00,0x00,0x00,0x04,0x0C,0x14,0x24,0x24,0x44,0x44,0x7E,0x04,0x04,0x1E,0x00,0x00,
};
code unsigned char Zf816_5[]=
{
/*-- 文字: 5 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
0x00,0x00,0x00,0x7E,0x40,0x40,0x40,0x58,0x64,0x02,0x02,0x42,0x44,0x38,0x00,0x00,
};
code unsigned char Zf816_6[]=
{
/*-- 文字: 6 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
0x00,0x00,0x00,0x1C,0x24,0x40,0x40,0x58,0x64,0x42,0x42,0x42,0x24,0x18,0x00,0x00,
};
code unsigned char Zf816_7[]=
{
/*-- 文字: 7 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
0x00,0x00,0x00,0x7E,0x44,0x44,0x08,0x08,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,
};
code unsigned char Zf816_8[]=
{
/*-- 文字: 8 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x24,0x18,0x24,0x42,0x42,0x42,0x3C,0x00,0x00,
};
code unsigned char Zf816_9[]=
{
/*-- 文字: 9 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
0x00,0x00,0x00,0x18,0x24,0x42,0x42,0x42,0x26,0x1A,0x02,0x02,0x24,0x38,0x00,0x00,
};
code unsigned char Zf816_nc[]= //空字模
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
code unsigned char Zf816_mao_hao[]= //冒号
{
/*-- 文字: : --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,
};
code unsigned char Hz1616_zhu[]=
{
/*-- 文字: 主 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
0x02,0x00,0x01,0x80,0x01,0x00,0x00,0x08,0x3F,0xFC,0x01,0x00,0x01,0x00,0x01,0x08,
0x3F,0xFC,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x04,0x7F,0xFE,0x00,0x00,0x00,0x00,
};
code unsigned char Hz1616_cai[]=
{
/*-- 文字: 菜 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
0x04,0x40,0xFF,0xFE,0x04,0x40,0x04,0x40,0x3F,0xF8,0x22,0x08,0x11,0x10,0x08,0x20,
0x01,0x00,0x7F,0xFE,0x03,0x80,0x05,0x40,0x09,0x30,0x11,0x1C,0x61,0x08,0x01,0x00,
};
code unsigned char Hz1616_dan[]=
{
/*-- 文字: 单 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
0x08,0x20,0x06,0x30,0x04,0x40,0x3F,0xF8,0x21,0x08,0x3F,0xF8,0x21,0x08,0x21,0x08,
0x3F,0xF8,0x21,0x08,0x01,0x00,0xFF,0xFE,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
};
code unsigned char Hz1616_she[]=
{
/*-- 文字: 设 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
0x40,0x00,0x21,0xF0,0x31,0x10,0x21,0x10,0x01,0x10,0x01,0x10,0xE2,0x0E,0x25,0xF8,
0x21,0x08,0x21,0x08,0x20,0x90,0x20,0x90,0x28,0x60,0x30,0x90,0x23,0x0E,0x0C,0x04,
};
code unsigned char Hz1616_zhi[]=
{
/*-- 文字: 置 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
0x3F,0xF8,0x24,0x48,0x24,0x48,0x3F,0xF8,0x01,0x00,0x7F,0xFC,0x02,0x00,0x1F,0xF0,
0x10,0x10,0x1F,0xF0,0x10,0x10,0x1F,0xF0,0x10,0x10,0x1F,0xF0,0x10,0x10,0xFF,0xFE,
};
code unsigned char Hz1616_su[]=
{
/*-- 文字: 速 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
0x00,0x80,0x40,0x80,0x2F,0xFC,0x20,0x80,0x00,0x80,0x07,0xF8,0xE4,0x88,0x24,0x88,
0x27,0xF8,0x21,0xA0,0x22,0x98,0x2C,0x88,0x20,0x80,0x50,0x80,0x8F,0xFE,0x00,0x00,
};
code unsigned char Hz1616_du[]=
{
/*-- 文字: 度 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
0x01,0x00,0x00,0x80,0x3F,0xFE,0x22,0x20,0x22,0x20,0x2F,0xFC,0x22,0x20,0x23,0xE0,
0x20,0x00,0x27,0xF8,0x22,0x10,0x21,0x20,0x20,0xC0,0x41,0x30,0x46,0x0E,0x98,0x04,
};
code unsigned char Hz1616_shi[]=
{
/*-- 文字: 时 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
0x00,0x10,0x00,0x10,0x7C,0x10,0x44,0x10,0x47,0xFE,0x44,0x10,0x7C,0x10,0x45,0x10,
0x44,0x90,0x44,0x90,0x7C,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x50,0x00,0x20,
};
code unsigned char Hz1616_jian[]=
{
/*-- 文字: 间 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
0x20,0x00,0x13,0xFC,0x10,0x04,0x40,0x04,0x47,0xE4,0x44,0x24,0x44,0x24,0x47,0xE4,
0x44,0x24,0x44,0x24,0x47,0xE4,0x40,0x04,0x40,0x04,0x40,0x04,0x40,0x14,0x40,0x08,
};
code unsigned char Hz1616_pin[]=
{
/*-- 文字: 频 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
0x08,0x00,0x08,0xFE,0x4E,0x20,0x48,0x40,0x48,0xFC,0xFE,0x84,0x00,0xA4,0x08,0xA4,
0x4A,0xA4,0x4A,0xA4,0x84,0xA4,0x08,0x50,0x10,0x48,0x20,0x86,0xC3,0x02,0x00,0x00,
};
code unsigned char Hz1616_lv[]=
{
/*-- 文字: 率 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
0x02,0x00,0x01,0x00,0x7F,0xFE,0x41,0x00,0x22,0x28,0x17,0xD0,0x04,0x80,0x11,0x10,
0x22,0x48,0x47,0xC4,0x01,0x20,0xFF,0xFE,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
};
code unsigned char Hz1616_fan[]=
{
/*-- 文字: 范 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
0x04,0x20,0x04,0x20,0xFF,0xFE,0x04,0x60,0x40,0x00,0x31,0xF8,0x91,0x08,0x61,0x08,
0x49,0x08,0x09,0x38,0x11,0x10,0xE1,0x00,0x21,0x04,0x21,0x04,0x20,0xFC,0x20,0x00,
};
code unsigned char Hz1616_wei[]=
{
/*-- 文字: 围 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
0x7F,0xFC,0x42,0x04,0x42,0x04,0x5F,0xF4,0x42,0x04,0x4F,0xE4,0x42,0x04,0x5F,0xE4,
0x42,0x24,0x42,0x24,0x42,0x24,0x42,0xA4,0x42,0x44,0x40,0x04,0x7F,0xFC,0x40,0x04,
};
code unsigned char Hz1616_shang[]=
{
/*-- 文字: 上 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0xF8,0x01,0x00,
0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x04,0x7F,0xFE,0x00,0x00,
};
code unsigned char Hz1616_xia[]=
{
/*-- 文字: 下 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
0x00,0x04,0x7F,0xFE,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0xC0,0x01,0x60,0x01,0x30,
0x01,0x20,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x00,0x00,
};
code unsigned char Hz1616_xian[]=
{
/*-- 文字: 限 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
0x00,0x00,0xFB,0xF8,0x92,0x08,0x93,0xF8,0xA2,0x08,0xA2,0x08,0x93,0xF8,0x8A,0x80,
0x8A,0x48,0xAA,0x50,0x92,0x20,0x82,0x20,0x82,0x10,0x82,0x8E,0x83,0x04,0x82,0x00,
};
unsigned char ucCanvasBuffer[]= //画布显示数组。注意,这里没有code关键字,是全局变量。初始化全部填充0x00
{
0x00,0x00,0x00,0x00, //上半屏
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
//------------上半屏和下半屏的分割线-----------
0x00,0x00,0x00,0x00, //下半屏
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
};
unsigned char ucKeySec=0; //被触发的按键编号
unsigned int uiVoiceCnt=0; //蜂鸣器鸣叫的持续时间计数器
unsigned char ucWd=1; //窗口变量
unsigned char ucWd1Part=1; //窗口1的局部变量,代表选中某一行。
unsigned char ucWd1Update=1; //窗口1的整屏更新显示变量 1代表更新显示,响应函数内部会清零
unsigned char ucWd1Part1Update=0; //窗口1的第1个局部更新显示变量 1代表更新显示,响应函数内部会清零
unsigned char ucWd1Part2Update=0; //窗口1的第2个局部更新显示变量 1代表更新显示,响应函数内部会清零
unsigned char ucWd1Part3Update=0; //窗口1的第3个局部更新显示变量 1代表更新显示,响应函数内部会清零
unsigned char ucWd2Part=1; //窗口2的局部变量,代表选中某一行。
unsigned char ucWd2Update=0; //窗口2的整屏更新显示变量 1代表更新显示,响应函数内部会清零
unsigned char ucWd2Part1Update=0; //窗口2的第1个局部更新显示变量 1代表更新显示,响应函数内部会清零
unsigned char ucWd2Part2Update=0; //窗口2的第2个局部更新显示变量 1代表更新显示,响应函数内部会清零
unsigned char ucWd3Part=1; //窗口3的局部变量,代表选中某一行。
unsigned char ucWd3Update=0; //窗口3的整屏更新显示变量 1代表更新显示,响应函数内部会清零
unsigned char ucWd3Part1Update=0; //窗口3的第1个局部更新显示变量 1代表更新显示,响应函数内部会清零
unsigned char ucWd3Part2Update=0; //窗口3的第2个局部更新显示变量 1代表更新显示,响应函数内部会清零
unsigned char ucWd4Part=1; //窗口4的局部变量,代表选中某一行。
unsigned char ucWd4Update=0; //窗口4的整屏更新显示变量 1代表更新显示,响应函数内部会清零
unsigned char ucWd4Part1Update=0; //窗口4的第1个局部更新显示变量 1代表更新显示,响应函数内部会清零
unsigned char ucWd4Part2Update=0; //窗口4的第2个局部更新显示变量 1代表更新显示,响应函数内部会清零
unsigned char ucTimeH=2; //设置时间的上限数据
unsigned char ucTimeL=1; //设置时间的下限数据
unsigned char ucSpeedH=4; //设置速度的上限数据
unsigned char ucSpeedL=3; //设置速度的下限数据
unsigned char ucFreqH=6; //设置频率的上限数据
unsigned char ucFreqL=5; //设置频率的下限数据
void main()
{
initial_myself(); //第一区,上电后马上初始化
delay_long(100); //一线,延时线。延时一段时间
initial_peripheral(); //第二区,上电后延时一段时间再初始化
while(1) //第三区
{
key_service(); //按键服务的应用程序
lcd_display_service(); //应用层面的液晶屏显示程序
}
}
void initial_myself() //第一区 上电后马上初始化
{
/* 注释二:
* 矩阵键盘也可以做独立按键,前提是把某一根公共输出线输出低电平,
* 模拟独立按键的触发地,本程序中,把key_gnd_dr输出低电平。
* 坚鸿51学习板的S1和S5两个按键就是本程序中用到的两个独立按键。
*/
key_gnd_dr=0; //模拟独立按键的地GND,因此必须一直输出低电平
beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。
TMOD=0x01; //设置定时器0为工作方式1
TH0=0xf8; //重装初始值(65535-2000)=63535=0xf82f
TL0=0x2f;
}
void initial_peripheral() //第二区 上电后延时一段时间再初始化
{
LCDInit(); //初始化12864 内部包含液晶模块的复位
EA=1; //开总中断
ET0=1; //允许定时中断
TR0=1; //启动定时中断
}
void T0_time() interrupt 1
{
TF0=0; //清除中断标志
TR0=0; //关中断
key_scan(); //按键扫描函数
if(uiVoiceCnt!=0)
{
uiVoiceCnt--; //每次进入定时中断都自减1,直到等于零为止。才停止鸣叫
beep_dr=0; //蜂鸣器是PNP三极管控制,低电平就开始鸣叫。
}
else
{
; //此处多加一个空指令,想维持跟if括号语句的数量对称,都是两条指令。不加也可以。
beep_dr=1; //蜂鸣器是PNP三极管控制,高电平就停止鸣叫。
}
TH0=0xf8; //重装初始值(65535-2000)=63535=0xf82f
TL0=0x2f;
TR0=1; //开中断
}
void key_scan(void)//按键扫描函数 放在定时中断里
{
static unsigned int uiKeyTimeCnt1=0; //按键去抖动延时计数器
static unsigned char ucKeyLock1=0; //按键触发后自锁的变量标志
static unsigned int uiKeyTimeCnt2=0; //按键去抖动延时计数器
static unsigned char ucKeyLock2=0; //按键触发后自锁的变量标志
static unsigned int uiKeyTimeCnt3=0; //按键去抖动延时计数器
static unsigned char ucKeyLock3=0; //按键触发后自锁的变量标志
static unsigned int uiKeyTimeCnt4=0; //按键去抖动延时计数器
static unsigned char ucKeyLock4=0; //按键触发后自锁的变量标志
if(key_sr1==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
{
ucKeyLock1=0; //按键自锁标志清零
uiKeyTimeCnt1=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。
}
else if(ucKeyLock1==0)//有按键按下,且是第一次被按下
{
uiKeyTimeCnt1++; //累加定时中断次数
if(uiKeyTimeCnt1>const_key_time1)
{
uiKeyTimeCnt1=0;
ucKeyLock1=1; //自锁按键置位,避免一直触发
ucKeySec=1; //触发1号键
}
}
if(key_sr2==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
{
ucKeyLock2=0; //按键自锁标志清零
uiKeyTimeCnt2=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。
}
else if(ucKeyLock2==0)//有按键按下,且是第一次被按下
{
uiKeyTimeCnt2++; //累加定时中断次数
if(uiKeyTimeCnt2>const_key_time2)
{
uiKeyTimeCnt2=0;
ucKeyLock2=1; //自锁按键置位,避免一直触发
ucKeySec=2; //触发2号键
}
}
if(key_sr3==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
{
ucKeyLock3=0; //按键自锁标志清零
uiKeyTimeCnt3=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。
}
else if(ucKeyLock3==0)//有按键按下,且是第一次被按下
{
uiKeyTimeCnt3++; //累加定时中断次数
if(uiKeyTimeCnt3>const_key_time3)
{
uiKeyTimeCnt3=0;
ucKeyLock3=1; //自锁按键置位,避免一直触发
ucKeySec=3; //触发3号键
}
}
if(key_sr4==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
{
ucKeyLock4=0; //按键自锁标志清零
uiKeyTimeCnt4=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。
}
else if(ucKeyLock4==0)//有按键按下,且是第一次被按下
{
uiKeyTimeCnt4++; //累加定时中断次数
if(uiKeyTimeCnt4>const_key_time4)
{
uiKeyTimeCnt4=0;
ucKeyLock4=1; //自锁按键置位,避免一直触发
ucKeySec=4; //触发4号键
}
}
}
|