蓝桥杯笔记
免责声明 ( ?? ω ?? )?
代码没有完全验证,可能存在BUG,如果你发现错误,欢迎纠正。如果你不想纠正,你可以把它当作看不见。 所有的解释只代表作者的个人想法
CT107D硬件概况
首先是国信长天CT107D怎么说开发板的硬件概况,一言难尽,268软妹币,血亏

虽然板上有一些外设的开口,但实际上没有附带模块。是的,268板连接起来LED没有点阵へ ̄
程序
控制基础设备
由于开发板的设计,在这个板上点灯有点复杂。根据电路结构,有8个LED需要通过74HC138去操作。
P2 = (P2&0x1F)|0xA0;
/* 0x80: 100 --4-->Y4C: LED灯 0xA0: 101 --5-->Y5C: 挂在ULN2003上的外设 0xC0: 110 --6-->Y6C: 数码管位选 0xE0: 111 --7-->Y7C: 数码管段选 */
P0 = ctrl;
P2 &= 0x1F;
[目录](# “免责声明” ( •̀ ω •́ )✧)
数码管驱动
位选为Y6C选定的575,段选则为Y7C,即P25-P27组成6和7;
即110和111,那P2就是C0H和E0H;
C0位选,E0段选;
void Display(void) { static dispcom; /*消隐*/ P0 = 0xFF; //
芯片选定到P0复制完成的间隙会产生残影,故先赋值 P2 = (P2&0x1F)|0xE0; // 控制选定的575且不改变P2其他管脚状态 P0 = 0xFF; P2 &= 0x1F; // 关闭选定 /*位选*/ P2 = (P2&0x1F)|0xC0; P0 = 0x01<<dispcom; // 循环显示 P2 &= 0x1F; /*段选*/ P0 = 0xFF; P2 = (P2&0x1F)|0xE0; P0 = DispTab[DispBuf[dispcom]]; // DispTab : 共阳数码管段选码 DispBuf : 显示缓冲区 P2 &= 0x1F; dispcom++; if(dispcom==8)dispcom = 0; // 防止dispcom超出范围 }
按键
§ 独立按键
J5接至BTN,P3低4位控制4个按键;
unsigned char KeyValue = 0xFF; //全局变量记录按键值
------------------------------------------------------------
void BTN(void)
{
/*变量*/
static unsigned char keyvalue; //临时记录键值
static unsigned char keypress; //记录扫描按下次数
static bit keyfree = 1; //按键按下与否
unsigned char temp; //为方便判断
/*扫描*/
P3 |= 0x0F; //将P3口低4位设为高
temp = P3&0x0F; //取P3口低4位,其余为0,赋给temp,便于判断
/*消抖*/
if(temp!=0x0F)keypress++; //如果temp不等于0x0F,说明有键按下
else keypress = 0; //如果在keypress加到5之前temp回归0x0F,就不算作按下了
/*识别*/
if(keypress==5&&keyfree) //如果按键按下持续5个扫描,且按键在未按下的状态,就算按键按下了
{
keypress = 0; //归0keypress
keyfree = 0; //将按键状态设为按下,即不自由(free)(~ ̄▽ ̄)~
switch(temp) //按键识别
{
case 0x07:keyvalue = 4;break;
case 0x0B:keyvalue = 5;break;
case 0x0D:keyvalue = 6;break;
case 0x0E:keyvalue = 7;break;
}
}
/*松手检测*/
if(temp==0x0F&&keyfree==0) //若temp回归0x0F,且按键状态为按下,说明松手了,返回键值
{
keyfree = 1; //松手后将keyfree改为1,它免费了(~ ̄▽ ̄)~
KeyValue = keyvalue;
}
else Keyvalue = 0xFF; //其他情况返回0xFF
}
§ 矩阵按键
J5接至KBD,P3(不存在P36、P37)和P42、P44共同控制按键;
unsigned char KeyValue = 0xFF; //全局变量记录按键值
------------------------------------------------------------
void KBD(void)
{
/*变量*/
unsigned char S1=0x00,S2=0x00; //按键键值的行,列数据
static unsigned char keyvalue; //临时记录键值
static unsigned char keypress; //记录扫描按下次数
static bit keyfree = 1; //按键按下与否
unsigned char temp = 0xFF; //临时存放扫描数据
/*扫描*/
P3 = 0x0F; //将P3口低4位设为高
P42 = 0;P44 = 0; //P3高2位和P42P44组成高4位设为低
temp = (P3&0x0F);
/*消抖*/
if(temp!=0x0F)keypress++; //如果P3低4位不等于0x0F,说明疑似有键按下
//此处容易出现P3&0x0F!=0x0F这类错误
else keypress = 0; //如果在keypress加到5之前P3低4位回归0x0F,就不算作按下了
/*识别*/
if(keypress==5&&keyfree) //如果按键按下持续5个扫描,且按键在未按下的状态,就算按键按下了
{
keypress = 0; //归0keypress
keyfree = 0; //将按键状态设为按下,即不自由(free)(~ ̄▽ ̄)~
S1 = temp; //记录按键行值
P3 = 0xF0;
P42 = 1;P44 = 1; //反转扫描,确定列
if(!P42) S2 = 0xB0; //如果是P42=0;说明按下的键就在这列
else if(!P44) S2 = 0x70; //同上
/*这一句一定要用else if 否则S8-S11失效 如果不用else if,!P42确实为1,但是!P44为0会导致else的执行覆盖S2*/
else S2 = temp; //否则数据在P3中,记录列
switch(S1|S2) //按键识别
{
/*S4~S7*/
case 0x77:keyvalue = 4;break;
case 0x7B:keyvalue = 5;break;
case 0x7D:keyvalue = 6;break;
case 0x7E:keyvalue = 7;break;
/*S8~S11*/
case 0xB7:keyvalue = 8;break;
case 0xBB:keyvalue = 9;break;
case 0xBD:keyvalue = 10;break;
case 0xBE:keyvalue = 11;break;
/*S12~S15*/
case 0xD7:keyvalue = 12;break;
case 0xDB:keyvalue = 13;break;
case 0xDD:keyvalue = 14;break;
case 0xDE:keyvalue = 15;break;
/*S15~S19*/
case 0xE7:keyvalue = 16;break;
case 0xEB:keyvalue = 17;break;
case 0xED:keyvalue = 18;break;
case 0xEE:keyvalue = 19;break;
}
}
/*松手检测*/
if(temp==0x0F&&keyfree==0) //若P3回归0x0F,且按键状态为按下,说明松手了,返回键值
{
keyfree = 1;
KeyValue = keyvalue;
}
else Keyvalue = 0xFF; //其他情况返回0xFF
}
IIC
§ AT24C02
不用从头写起,但是需要自己写最后使用的发送和接收函数;
数据包给出了启动停止应答等操作的函数,只需要知道IIC通信的时序或者步骤即可;
我们需要从数据手册中得到这个时序;
在AT24C02的数据手册中,我们可以在Read Operation下面找到上面这张图。乍一看看不出到底Byte Write,有多少个步骤,但实际上重点有两个图表,还包括上面那个;如下:
Figure 7表明:MSB,R/W,LSB都属于同一个字节,而在赛方给出的IIC参考程序中,有两种操作函数,电平变化和字节传输;
所以将Figure 8划分一下也变得非常简单:
易得它的顺序是
IIC_Start();
IIC_SendByte(?);
IIC_WaitAck();
IIC_SendByte(?);
IIC_WaitAck();
IIC_SendByte(?);
IIC_WaitAck();
IIC_Stop();
接下来需要知道“?”里填啥?
首先是DEVICE ADDRESS,其实甚至可以从图中出答案(当然,给出的数据里也有),图中的就是正确的(必须的呀),即0xA0;板上AT24C02芯片地址为000;
第二个发送的字节是WORD ADDRESS,即数据要写在AT24C02的哪里?这个位置是由使用情况决定的,于是设置一个输入参数,add,最后是发送的数据,自然,也是参数;
所以IIC写函数最后是:
void IIC_Write(unsigned char add,unsigned char data)
{
IIC_Start();
IIC_SendByte(0xA0); //或者写给出的SlaveAddrW
IIC_WaitAck();
IIC_SendByte(add);
IIC_WaitAck();
IIC_SendByte(data);
IIC_WaitAck();
IIC_Stop();
}
那么读函数亦是如此啦;
当然,我们看到读取并不简单,它有三种模式;即 Current Address Read、 Random Read、 Sequential Read.
类型 | 描述 |
---|---|
Current Address Read | 未断电时,读上一次读的地址(即当前地址)。断电后,地址归为0x00 |
Random Read | 指定地址读取(这是我们需要的) |
Sequential Read | 连续读,先这样,再这样,再那样,就可以一个地址接下一个地址连续读 |
显然,传输步骤是:
IIC_Start(); IIC_SendByte(?); //看图,"?"应该是0xA0(或SlaveAddrW); IIC_WaitAck(); IIC_SendByte(?);