资讯详情

[蓝桥杯单片机] - 蓝桥杯单片机CT107D竞赛板各模块代码分析

蓝桥杯笔记


免责声明 ( ?? ω ?? )?

代码没有完全验证,可能存在BUG,如果你发现错误,欢迎纠正。如果你不想纠正,你可以把它当作看不见。 所有的解释只代表作者的个人想法



CT107D硬件概况

首先是国信长天CT107D怎么说开发板的硬件概况,一言难尽,268软妹币,血亏

虽然板上有一些外设的开口,但实际上没有附带模块。是的,268板连接起来LED没有点阵へ ̄


程序

控制基础设备

由于开发板的设计,在这个板上点灯有点复杂。根据电路结构,有8个LED需要通过74HC138去操作。

STC15
74HC138
74HC02
P2
STC15F2
Y0-Y7
P25+P26+P27
Y4C-Y7C
Y4-Y7
Y4C
LED灯
Y5C
SETP/RELAY...
Y6C
7SEG位选
Y7C
7SEG段选
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(?); 			 

标签: d5d电阻

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台