蓝桥杯备赛笔记.docx
- 文档编号:15673471
- 上传时间:2023-07-06
- 格式:DOCX
- 页数:97
- 大小:3.84MB
蓝桥杯备赛笔记.docx
《蓝桥杯备赛笔记.docx》由会员分享,可在线阅读,更多相关《蓝桥杯备赛笔记.docx(97页珍藏版)》请在冰点文库上搜索。
蓝桥杯备赛笔记
1、P0口的复用
P2口高3位的值
38译码器低电平端口
功能
备注
000(0x00)
Y0
无
001(0x20)
Y1
010(0x40)
Y2
011(0x60)
Y3
8255的CE引脚
0:
使能8255
1:
禁用8255
100(0x80)
Y4
LED灯锁存信号
0:
点亮LED灯
1:
熄灭LED灯
101(0xA0)
Y5
UNL2003输出锁存信号
0:
断开继电器、禁用直流电机、关闭蜂鸣器
1:
闭合继电器、转动直流电机、开启蜂鸣器
110(0xC0)
Y6
数码管位选锁存信号
0:
禁用该位
1:
使能该位
111(0xE0)
Y7
数码管段选锁存信号
0:
点亮该段
1:
熄灭该段
PS1:
上电后需给所有锁存器初始化(Y5,Y6初始化为0x00,其余初始化为0xFF)
PS2:
使用P0口时,按如下方式:
禁用所有寄存器——P0口赋值——打开目标寄存器——禁用所用寄存器
PS3:
锁存器高电平选通,低电平关闭
/**********************************************
*@brief初始化开发板
*@paramnone
*@returnnone
************************************************/
voidinitial_board(void)
{
P0_BUS_COMcom;
P2&=0x1F;//禁用所有锁存器
for(com=3;com<8;com++){
if(com==UNL2003||com==DIGITAL_BIT)
P0=0x00;
else
P0=0xFF;
P2|=com<<5;
_nop_();
P2&=0x1F;
}
}
/**********************************************
*@brief通过P0总线传输数据
*@paramcom:
总线占用的端口;databuf:
传输的数据
*@returnnone
************************************************/
voidP0_BUS(unsignedcharcom,unsignedchardatabuf)
{
P2&=0x1F;//禁用所有锁存器
P0=databuf;
P2|=com<<5;
_nop_();
P2&=0x1f;
}
PS4:
数码管段码
unsignedcharcodeNUM[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};
2、按键
一、单按键
单按键按如下流程图获取按键值:
分层思想在按键上的应用:
1、硬件层
从I/0口获取按键信息映射到keybuf(按键寄存器)上
2、驱动层
根据keybuf的值,分析按键是否有效(是否有抖动产生的),并返回按键编码
3、应用层
对不同按键的响应
分层的好处:
如果按键连接的I/O变化,或者按键所在的I/O口不连续,只需修改硬件层的程序,驱动层和应用层则不受影响。
/**********************************************
*@brief初始化键盘
*@paramnone
*@returnnone
************************************************/
voidinitial_key(void)
{
KEY0=1;
KEY1=1;
KEY2=1;
KEY3=1;
}
/**********************************************
*@brief读键值(硬件层)
*@paramnone
*@return读取的键值
************************************************/
staticuint8get_keybuf(void)
{
uint8keybuf;
keybuf=P3&0x0F;
returnkeybuf;
}
/**********************************************
*@brief获取按键编码值(驱动层)
*@paramnone
*@return按键的编码值
************************************************/
uint8readkey(void)
{
staticuint8lastkey=NOKEY;//上一次获取的键值
staticuint16keycount=0;//获取同一键值的次数
staticuint16keyovertime=KEY_OVER_TIME;//进入连击延时
staticuint16upspeed=0;//加速相应(长按越久连击延时越短)
uint8keytemp=NOKEY;//保存当前获取的键值
keytemp=get_keybuf();
if(keytemp==NOKEY){//是否有按键按下
keycount=0;
upspeed=0;
keyovertime=KEY_OVER_TIME;//退出连击模式
returnNOKEY;
}else{
if(keytemp==lastkey){//是否和上一次键值相同
if(++keycount==KEY_WOBBLE_TIME){//是否到达有效按键次数
returnkeytemp;
}else{
if(keycount>keyovertime){//是否长按进入连击模式
keycount=0;
if(upspeed upspeed+=50; keyovertime=KEY_QUICK_TIME-upspeed;//进入连击模式 } returnNOKEY; }//else }else{ lastkey=keytemp;//保留上一次按键值 keycount=0; upspeed=0; keyovertime=KEY_OVER_TIME; returnNOKEY; }//else }//else } 二、矩阵键盘 矩阵键盘原理介绍: 如图,当按下S7时P30和P37将相连。 这个时候有四种情况: 按下S7前P30的状态 按下S7前P37的状态 按下S7后 0 0 都为0 0 1 都为0(I/O口特性,P37会被P30拉低) 1 0 都为0 1 1 都为1 由上表可知,在S7按下前,只有在P30和P37处于不同状态下,S7才能使P30或P37状态产生变化,这样单片机才能检测到按键。 因此,矩阵键盘不能像单按键一样,把I/O全部拉高就可以(如果全部拉高,即P30与P37都为1,这样不管按键怎么按,两个I/O的状态始终不变,单片机当然无法检测到)。 其他按键同理可得,既然状态设为不同按键才能产生响应,那么要初始化为0xF0H还是0x0FH呢? 如果初始化为0x0FH,同一行按键按下时(例如S7,S11,S15,S19)都会使P30变为低电平,无法分别是同一行中哪一个按键产生反应。 若初始化为0XF0H,则会无法分辨同列的按键。 怎么办? 通常有以下两种方法: 1、扫描法 1)初始化为0xFFH 2)拉低P30(即先扫描第一行) 3)检测P34~P37中有无被拉低的引脚 4)若检测到P34~P37中有引脚被拉低,即可以确定按键,若无引脚被拉低,初始化为0xFFH后扫描下一行。 charget_num(void) { unsignedchari,j,m,n; for(i=1,m=0x01;i<=4;i++,m=m<<1) { P3=0xFF&~m; for(j=1,n=0x10;j<=4;j++,n=n<<1) if((P3&n)==0) returni*4-(4-j)-1; } return16; } 2、线翻转法 1)令P3=0x0F; 2)获取P3口的值,获取行按键,保存到KEY_ROW里 3)令P3=0xF0; 4)获取P3口的值,获取列按键,保存到KEY_COL里 5)结合KEY_ROW和KEY_COL(KEY_COL|KEY_ROW),即可获取按键值。 假设按下的按键为S4,按以上步骤,则,KEY_ROW的值为0x07H,KEY_COL的值为0x70H。 KEY_COL|KEY_ROW=0x77。 其他按键同理,可以获得不同编码。 结合单按键的算法,对矩阵键盘进行消抖(把行按键当成单按键看待,当行按键消抖完成后,再获取一次列按键,生成按键编码返回)。 /********************************************** *@brief初始化键盘 *@paramnone *@returnnone ************************************************/ voidinitial_key(void) { KEYBOARD=0xFF; } /********************************************** *@brief读键值(硬件层) *@paramRowOrCol: 0为读取行键值,1为读取列键值 *@return读取的键值 ************************************************/ staticuint8get_keybuf(BOOLRowOrCol) { uint8keybuf; if(RowOrCol){ KEYBOARD=0xF0; keybuf=KEYBOARD&0xF0; }else{ KEYBOARD=0x0F; keybuf=KEYBOARD&0x0F; } returnkeybuf; } /********************************************** *@brief获取按键编码值(驱动层) *@paramnone *@return按键的编码值 ************************************************/ uint8readkey(void) { staticuint8lastkey=NOKEY;//上一次获取的键值 staticuint16keycount=0;//获取同一键值的次数 staticuint16keyovertime=KEY_OVER_TIME;//进入连击延时 staticuint16upspeed=0;//加速相应(长按越久连击延时越短) uint8keytemp_row=NOKEY;//保存当前获取的行键值 uint8keytemp_col=NOKEY;//保存当前获取的列键值 uint8keyvalue=NOKEY; keytemp_row=get_keybuf(0); if(keytemp_row==(NOKEY&0x0F)){//是否有按键按下 keycount=0; upspeed=0; keyovertime=KEY_OVER_TIME;//退出连击模式 returnNOKEY; }else{ if(keytemp_row==lastkey){//是否和上一次键值相同 if(++keycount==KEY_WOBBLE_TIME){//是否到达有效按键次数 keytemp_col=get_keybuf (1); switch(keytemp_row|keytemp_col){ case0x77: keyvalue=0;break; case0xB7: keyvalue=1;break; case0xD7: keyvalue=2;break; case0xE7: keyvalue=3;break; case0x7B: keyvalue=4;break; case0xBB: keyvalue=5;break; case0xDB: keyvalue=6;break; case0xEB: keyvalue=7;break; case0x7D: keyvalue=8;break; case0xBD: keyvalue=9;break; case0xDD: keyvalue=10;break; case0xED: keyvalue=11;break; case0x7E: keyvalue=12;break; case0xBE: keyvalue=13;break; case0xDE: keyvalue=14;break; case0xEE: keyvalue=15;break; default: break; } returnkeyvalue; }else{ if(keycount>keyovertime){//是否长按进入连击模式 keycount=0; if(upspeed upspeed+=25;//增加连击响应的速度 keyovertime=KEY_QUICK_TIME-upspeed;//进入连击模式 } returnNOKEY; }//else }else{ lastkey=keytemp_row;//保留上一次按键值 keycount=0; upspeed=0; keyovertime=KEY_OVER_TIME; returnNOKEY; }//else }//else } 如果相对多键同时按也产生响应时(视为不同与单按键),只需在switch添加响应的键值即可。 例如同时按下S4,S5,添加的键值应为: 0x73H PS: 以上程序若使用IAP15F2K61S2-89C52转换板,着会发现,按下键值键盘第一列与第二列时没有反应(从左往右),这是应为,IAP15F2K61S2-89C52转换板默认用P42和P44代替P36和P37。 按照分层思想,只需更改硬件层的代码,即可解决问题。 /********************************************** *@brief读键值 *@paramRowOrCol: 0为读取行键值,1为读取列键值 *@return读取的键值 ************************************************/ staticuint8get_keybuf(BOOLRowOrCol) { uint8keybuf; if(RowOrCol){ KEYBOARD=0x30; P4|=0x14;//由于转接板问题,键盘列3、列4分别接在P42、P44上 keybuf=KEYBOARD&0x30; if(P4&0x04) keybuf|=0x40; if(P4&0x10) keybuf|=0x80; }else{ KEYBOARD=0x0F; P4&=0xEB; keybuf=KEYBOARD&0x0F; } returnkeybuf; } 三、外部中断 IAP15有5个外部中断,分别为 中断类型 占用引脚 中断号 相关寄存器 INT0 P3.2 0 IE(B0): 是否开启中断 TCON(B0,B1): 中断的触发类型、 中断标志位 INT1 P3.3 2 IE(B2): 是否开启中断 TCON(B2,B3): 中断的触发类型、 中断标志位 INT2 P3.6 10 INT_CLKO(B3): 是否开启中断 INT3 P3.7 11 INT_CLKO(B4): 是否开启中断 INT4 P3.0 16 INT_CLKO(B6): 是否开启中断 在CT107D89训练板上,可以直接使用有INT0,INT1,INT4三个外部中断,其中INT0和INT1可以通过配置TCON寄存器中的IT0,IT1位选择下降沿触发 (1)或上升沿与下降沿触发(0),INT4仅能使用下降沿触发 使用外部中断通常需要以下两步: ①: 初始化所需中断(配置相关寄存器) ②: 编写中断服务程序 ①初始化中断 开启相关中断位即总中断,选择合适的中断模式。 以上是初始化需要做的。 INT0和INT1中断允许位在IE寄存器 EX0: 0: 关闭INT0;1: 开启INT0; EX1: 0: 关闭INT1;1: 开启INT1; IE寄存器可以按位寻址,以开启INT0为例, 可以用IE|=0x01H;来开启INT0。 或者可以使用EX0=1;来开启INT0。 不管使用哪种方式,最后都不要忘记打开总中断开关EA。 INT4中断允许位在INT_CLKO寄存器 EX4: 0: 关闭INT4;1: 开启INT4; 该寄存器不能按位寻址,因此只能用INT_CLKO|=0x40H;来开启INT4。 允许中断中后,可以在TCON寄存器配置外部中断类型 IT0: 0: INT0仅下降沿触发;1: INT0可以由上升沿和下降沿触发 IE0: INT0中断标志位,产生INT0中断后置‘1’,响应中断后硬件自动清0 IT1: 0: INT1仅下降沿触发;1: INT1可以由上升沿和下降沿触发 IE1: INT1中断标志位,产生INT1中断后置‘1’,响应中断后硬件自动清0 仅INT0和INT1支持中断类型的选择,INT4只能使用下降沿触发中断。 当IT0=0时,INT0上升沿与下降沿触发中断;当IT0=1时,INT0下降沿触发中断。 INT1与IT1的关系同上 若上电后开启INT0和INT1中断,不配置IT0与IT1,默认为上升沿与下降沿触发中断。 当产生INT0中断时,IE0会被置1,(INT1中断置位IE1),然后进入中断服务程序,完成中断服务后,硬件自动清零。 INT4的中断标志隐藏,用户不可见。 (INT2,INT3的中断标志位对用户也是隐藏的)。 ②编写中断服务程序 中断服务程序就是个特殊的函数。 通常函数要程序要主动在主函数或其他函数内调用,中断函数不能直接调用,只有在相应的中断触发后,CPU会自动进入中断函数。 voidINT0_Routine(void)interrupt0 { /*dosomething*/ } 以上是INT0的中断函数。 中断函数一般有以下几个特点: ①返回值和参数都为空 ②函数名后跟随“interrupt+中断号” 中断函数名可以任意命名,只要名称符合C语言的规范即可,没有特殊的要求。 以下是使用INT0的例子: voidinitial_exinterrupt(void) { INT0=1; IT0=1;//0: 上升沿和下降沿触发;1: 仅下降沿触发 EX0=1;//使能外部中断1 EA=1; } voidINT0_Routine(void)interrupt0 { COUNT++; } 四、定时器 IAP15拥有3个定时器: 定时器0,定时器1,定时器2 定时器 溢出中断号 拥有模式 相关寄存器 定时器0 1 模式0 : 16位自动重装模式 模式1: 16位不可重装模式 模式2 : 8位自动重装模式 模式3 : 16位自动重装模式(产生不可屏蔽中断) AUXR(B7): 配置1T或12T INT_CLKO(B0) : 是否输出时钟 TMOD(B0~3) : 选择定时器模式 TL0,TH0 IE(B1): 是否开启中断 TCON(B4,B5) : 是否启动定时器、 定时器中断标志位 定时器1 3 模式0 : 16位自动重装模式 模式1: 16位不可重装模式 模式2 : 8位自动重装模式 AUXR(B6): 配置1T或12T INT_CLKO(B1) : 是否输出时钟 TMOD(B4~7) : 选择定时器模式 TL1,TH1 IE(B3): 是否开启中断 TCON(B6,B7) : 是否启动定时器、 定时器中断标志位 定时器2 12 模式0 : 16位自动重装模式 AUXR(B2,B3,B4): 配置1T或12T、 定时还是计数、 是否启动定时器 INT_CLKO(B2) : 是否输出时钟 IE2(B2) : 是否开启中断 T2L,T2H 使用定时器时,一般需要以下几个步骤: ①选择1T模式或者12T模式(配置AUXR寄存器); ②是否输出时钟(配置INT_CLKO寄存器); ③设置定时器模式(配置TMOD、AUXR寄存器); ④设置初值(TL0,TH0,TL1,TH1,T2L,T2H); ⑤是否开启中断(配置IE、IE2寄存器); ⑥开始运行定时器(配置TCON、AUXR寄存器) ⑦编写相应的中断服务函数(开启定时器中断的情况下)。 下面详细介绍上面步骤: ①选择1T模式或者12T模式(配置AUXR寄存器); T0x12: 0: 定时器0以12T模式运行;1: 定时器0以1T模式运行 T1x12: 0: 定时器1以12T模式运行;1: 定时器1以1T模式运行 T2x12: 0: 定时器2以12T模式运行;1: 定时器2以1T模式运行 当对计时的精度较高,时间较短(微妙级)时,应选用1T模式;当对计时的精度要求较低,时间较长(毫秒级),最
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 蓝桥杯备赛 笔记
![提示](https://static.bingdoc.com/images/bang_tan.gif)