SDA=1;//最高位为1发送高电平
else
SDA=0;//最高位为0发送低电平
SCL=1;
SCL=0;//产生时钟脉冲
}
return;
}
//接收8位数据,时序同写,保持SCL为高电平,读SDA
unsignedchari2c_receive8bit(void)
{
unsignedchara;//读的位数
unsignedcharb=0;
for(a=0;a<8;a++)
{
SCL=1;//保持SCL为高电平
b=b<<1;//读1位左移1次
if(SDA==1)
b=b|0x01;//按位或,从低位开始接收,移位8次到高位,先接收的是最高位
SCL=0;//结束时钟脉冲
}
//当SDA为低电平时,SCL给一个高电平脉冲为接收确认信号
biti2c_ack(void)
{
bitack;//应答成功信号,为“0”应答成功
SDA=1;//将SDA拉高,保证低电平由I2C总线接口产生
SCL=1;//时钟由低变高
if(SDA==1)
ack=1;//应答不成功
else
ack=0;//应答成功
SCL=0;//SDA为高电平,时钟由高变低,应答成功
return(ack);
}
//i2c_write(地址,数据),写一个字节
voidi2c_write(unsignedcharAddress,unsignedcharData)
{
do{
i2c_start();//启动信号
i2c_send8bit(0xA0);//送器件地址(写)
}while(i2c_ack());//应答成功则器件地址写成功
i2c_send8bit(Address);//送8位地址
i2c_ack();//调应答
i2c_send8bit(Data);//送8位数据
i2c_ack();//调应答
i2c_stop();//调停止
return;
}
//i2c_read(地址,数据),读一个字节
unsignedchari2c_read(unsignedcharAddress)
{
unsignedcharc;//存接收的数据
do{
i2c_start();//开始
i2c_send8bit(0xA0);//注意:
送器件地址(写)
}while(i2c_ack());//=1,表示无确认,再次发送
i2c_send8bit(Address);//送8位地址
i2c_ack();//应答
do{
i2c_start();//开始
i2c_send8bit(0xA1);//注意:
送器件地址(读)
}while(i2c_ack());//应答
c=i2c_receive8bit();//读数据送变量C
i2c_ack();//应答
i2c_stop();
return(c);
}
return(b);
}
//======主程序==========
voidmain(void)
{
unsignedchardd;
for(;;)//无限循环
{
i2c_write(0x00,0x55);//给地址00写数据0x55
_nop_();
dd=i2c_read(0x00);//从地址00开始读
}}
2)下面是采用单片机C语言和汇编语言混合编写的程序
#include
#include
//函数声明
externvoidsend8bit(unsignedchar);
externunsignedcharreceive8bit(char);//注意说明格式
sbitSDA=P1^0;
sbitSCL=P1^1;
//发送开始信号
voidi2c_start(void)
{
SDA=1;
SCL=1;
SDA=0;
SCL=0;
return;
}
//发送结束信号
voidi2c_stop(void)
{
SDA=0;
SCL=1;
SDA=1;
return;
}
//发送接收确认信号
biti2c_ack(void)
{
bitack;
SDA=1;
SCL=1;
if(SDA==1)
ack=1;
else
ack=0;
SCL=0;
return(ack);
}
//i2c_write(地址,数据),写一个字节
voidi2c_write(unsignedcharAddress,unsignedcharData)
{
do{
i2c_start();
send8bit(0xA0);
}while(i2c_ack());
send8bit(Address);
i2c_ack();
send8bit(Data);
i2c_ack();
i2c_stop();
return;
}
//i2c_read(地址,数据),读一个字节
unsignedchari2c_read(unsignedcharAddress)
{
unsignedcharc;
do{
i2c_start();
send8bit(0xA0);
}while(i2c_ack());//=1,表示无确认,再次发送
send8bit(Address);
i2c_ack();
do{
i2c_start();
send8bit(0xA1);
}while(i2c_ack());
c=receive8bit(0x00);//虽然无参数传递到汇编程序中,但必须给参数
i2c_ack();
i2c_stop();
return(c);
}
voidmain(void)
{
unsignedchardd;
for(;;)
{
i2c_write(0x00,0x55);
_nop_();
dd=i2c_read(0x00);
}}
下面是汇编语言程序,循环发送和接收8位数据。
NAMESENDANDREC
SDAEQUP1.0;定义端口
SCLEQUP1.1
;送八位数据
PUBLIC_SEND8BIT
SENDBYTESEGMENTCODE
RSEGSENDBYTE
_SEND8BIT:
MOVA,R7;取数据
MOVB,#08H
SEND8BIT_A:
RLCA
MOVSDA,C
SETBSCL
NOP
NOP
CLRSCL
DJNZB,SEND8BIT_A
RET
;接收八位数据
PUBLIC_RECEIVE8BIT
RECEIVE8BITSEGMENTCODE
RSEGRECEIVE8BIT
_RECEIVE8BIT:
MOVB,#08H
CLRA
SETBSDA
RECEIVE8IT_A:
SETBSCL
NOP
NOP
MOVC,SDA
RLCA
CLRSCL
DJNZB,RECEIVE8IT_A
MOVR7,A;送结果
RET
END
三、键盘和数码显示人机交互的C语言编程
1、行列式键盘与AT89C52的接口
键盘输入信息的主要过程是:
(1)单片机判断是否有键按下。
(2)确定按下的是哪一个键。
(3)把此步骤代表的信息翻译成计算机所能识别的代码,如ASCII或其它特征码。
例44×4键盘的扫描程序。
扫描程序查询的内容为:
(1)查询是否有键按下。
首先单片机向行扫描P1.0~P1.3输出全为"0"扫描码F0H,然后从列检查口P1.4~P1.7输入列扫描信号,只要有一列信号不为"1",即P1口不为F0H,则表示有键按下。
接着要查出按下键所在的行、列位置。
(2)查询按下键所在的行列位置。
单片机将得到的信号取反,P1.4~P1.7中的为1的位便是键所在的列。
接下来要确定键所在的行,需要进行逐行扫描。
单片机首先使P1.0为"0",P1.1~P1.7为"1",即向P1口发送扫描码FEH,接着输入列检查信号,若全为"1",表示不在第一行。
接着使P1.1接地,其余为"1",再读入列信号……这样逐行发"0"扫描码,直到找到按下键所在的行,将该行扫描码取反保留。
当各行都扫描以后仍没有找到,则放弃扫描,认为是键的误动作。
(3)对得到的行号和列号译码,得到键值。
(4)键的抖动处理。
当用手按下一个键时,往往会出现所按键在闭合位置和断开位置之间跳几下才稳定到闭合状态的情况。
在释放一个键时,也会出现类似的情况,这就是键抖动,抖动的持续时间不一,通常不会大于10ms,若抖动问题不解决,就会引起对闭合键的多次读入,对于键抖动最方便的解决方法就是当发现有键按下后,不是立即进行逐行扫描,而是延时10ms后再进行。
由于键按下的时间持续上百毫秒,延时后再也不迟。
扫描函数的返回值为键特征码,若无键按下,返回值为0。
程序如下:
#include
#defineucharunsignedchar
#defineuintunsignedint
voiddlms(void)
voidkbscan(void);
voidmain(void)
{
ucharkey;
while
(1)
{key=kbscan();
dlms();
}
}
voiddlms(void)
{uchari;
for(i=200;i>0;i--){}
}
ucharkbscan(void)//键扫描函数
{ucharscode,recode;
P1=oxf0;
if((P1&0xf0)!
=0xf0)//若有键按下
{dlms();//延时去抖动
if((P1&0xf0)!
=0xf0)
{scode=0xfe;//逐行扫描初值
while((scode&0x10)!
=0)
{P1=scode;//输出扫描码
if((P1&0xf0)!
=0xf0)//本行有键按下
{recode=(P1&0xf0)|0x0f;
return((~scode)+(~recode));//返回特征字节码
}
else
scode=(scode<<1)|0x01;//行扫描左移一位
}
}
}
return(0);
}
例5七段数码显示与AT89C51的接口
LED显示器工作在静态方式时,其阴极(或其阳极)点连接在一起接地(或+5V),每一个的端选线(a,b,c,d,e,f,g,dp)分别与一个8位口相连。
LED显示器工作在动态显示方式时,段选码端口I/O1用来输出显示字符的段选码,I/O2输出位选码。
I/O1不断送待显示字符的段选码,I/O2不断送出不同的位扫描码,并使每位显示字符停留显示一段时间,一般为1-5ms,利用眼睛的视觉惯性,从显示器上便可以见到相当稳定的数字显示。
确定的8155片内4个端口地址如下:
命令/状态口:
FFF0H
口A:
FFF1H
口B:
FFF2H
口C:
FFF3H
6位待显示字符从左到右依次放在dis_buf数组中,显示次序从右向左顺序进行。
程序中的table为段选码表,表中段选码表存放的次序为0-F等。
以下为循环动态显示6位字符的程序,8155命令字为07H。
#include
#include
#defineucharunsignedchar
#defineCOM8155XBYTE[0xfff0]
#