I2C总线 利用IO口实现方式(1)

1、#define LCD1602_DB = P0    //后面没有 ; 长点记性!

2、mask>>=1     //被自己写成      mask >>1  编译不报错,自己检查还逻辑正确,找了好久才找到错误!!

main().c 文件 I2C子函数

  1. 传输byte
  2. 起始位
  3. 结束位
  4. 确定应答值ack
#include <reg52.h>
#include <intrins.h>

#define I2CDelay()  {_nop_();_nop_();_nop_();_nop_();}
sbit I2C_SCL = P3^7;
sbit I2C_SDA = P3^6;

bit I2CAddressing(unsigned char addr);
extern void InitLcd1602();
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);

void main()
{
    bit ack;
    unsigned char str[10];

    InitLcd1602();   //初始化液晶
    
    ack = I2CAddressing(0x50); //查询地址为0x50的器件
    str[0] = '5';              //将地址和应答值转换为字符串
    str[1] = '0';
    str[2] = ':';
    str[3] = (unsigned char)ack + '0';
    str[4] = '\0';
    LcdShowStr(0, 0, str);     //显示到液晶上
    
    ack = I2CAddressing(0x62); //查询地址为0x62的器件
    str[0] = '6';              //将地址和应答值转换为字符串
    str[1] = '2';
    str[2] = ':';
    str[3] = (unsigned char)ack + '0';
    str[4] = '\0';
    LcdShowStr(8, 0, str);     //显示到液晶上
    
    while (1);
}
/* 产生总线起始信号 */
void I2CStart()
{
    I2C_SDA = 1; //首先确保SDA、SCL都是高电平
    I2C_SCL = 1;
    I2CDelay();
    I2C_SDA = 0; //先拉低SDA
    I2CDelay();
    I2C_SCL = 0; //再拉低SCL
}
/* 产生总线停止信号 */
void I2CStop()
{
    I2C_SCL = 0; //首先确保SDA、SCL都是低电平
    I2C_SDA = 0;
    I2CDelay();
    I2C_SCL = 1; //先拉高SCL
    I2CDelay();
    I2C_SDA = 1; //再拉高SDA
    I2CDelay();
}
/* I2C总线写操作,dat-待写入字节,返回值-从机应答位的值 */
bit I2CWrite(unsigned char dat)
{
    bit ack;  //用于暂存应答位的值
    unsigned char mask;  //用于探测字节内某一位值的掩码变量

这面的函数通过探测掩码来实现一个byte的从高->低输出  思路很好。

 下面的函数单独看,因为我在他身上浪费了两个小时!!!! mask>>=1  这个>>= 的等号千万不能丢!否则死循环
for (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行  注意这里mask>>=1  看仔细了有一个等号!!!
    {
        if ((mask&dat) == 0)  //该位的值输出到SDA上
            I2C_SDA = 0;
        else
            I2C_SDA = 1;
        I2CDelay();
        I2C_SCL = 1;          //拉高SCL
        I2CDelay();
        I2C_SCL = 0;          //再拉低SCL,完成一个位周期
    }
    I2C_SDA = 1;   //8位数据发送完后,主机释放SDA,以检测从机应答
    I2CDelay();
    I2C_SCL = 1;   //拉高SCL
    ack = I2C_SDA; //读取此时的SDA值,即为从机的应答值
    I2CDelay();
    I2C_SCL = 0;   //再拉低SCL完成应答位,并保持住总线

    return ack;    //返回从机应答值
}
/* I2C寻址函数,即检查地址为addr的器件是否存在,返回值-从器件应答值 */
bit I2CAddressing(unsigned char addr)
{
    bit ack;

    I2CStart();  //产生起始位,即启动一次总线操作
    ack = I2CWrite(addr<<1);  //高7位表示地址所以左移了一下,因寻址命令的最低位
                              //为读写位,用于表示之后的操作是读或写
    I2CStop();   //不需进行后续读写,而直接停止本次总线操作
    
    return ack;
}

液晶程序 LCD1602.c文件 子函数

  1. 写cmd
  2. 读状态确定ready
  3. 写dat
  4. 确定显示光标位置
  5. 集成 确定光标并显示dat
  6. 初始化
#include<reg52.h>

#define LCD1602_DB P0     //注意#define 定义后面没有;
sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_E  = P1^5;

/* 等待液晶准备好 */
void LCDWaitReady()
{
	unsigned char sta;
	LCD1602_DB = 0xff;
	LCD1602_RS = 0;
	LCD1602_RW = 1;
	do{
		LCD1602_E = 1;
		sta = LCD1602_DB;		//读取状态字
		LCD1602_E = 0;
	}while(sta&0x80);			//bit7 等于1表示液晶正忙
}

/* 向LCD1602写入一个字节命令,cmd为待写入命令值 */
void LcdWriteCmd(unsigned char cmd)
{
	LCDWaitReady();
	LCD1602_RS = 0;
	LCD1602_RW = 0;
	LCD1602_DB = cmd;
	LCD1602_E = 1;
	LCD1602_E = 0;
}

/* 向LCD1602写入一个字节数据,dat为待写入命令值 */
void LcdWriteDat(unsigned char dat)
{
	LCDWaitReady();
	LCD1602_RS = 1;
	LCD1602_RW = 0;
	LCD1602_DB = dat;
	LCD1602_E = 1;
	LCD1602_E = 0;
}

/* 设置显示器RAM起始位置,亦即光标位置,(x,y)为对应屏幕上的字符坐标 */
void LcdSetCursor(unsigned char x,unsigned char y)
{
	unsigned char addr;
	if(y==0)
		addr = 0x00 + x;
	else
		addr = 0x40 + x;
	LcdWriteCmd(addr|0x80);
}

/* 在液晶屏幕上显示字符串,(x,y) 对应屏幕坐标上的起始坐标,str为字符串指针*/
void LcdShowStr(unsigned char x,unsigned char y,unsigned char *str)
{
	LcdSetCursor(x,y);
	while(*str != '\0')			//用'\0'来判断字符串是否截止
	{
		LcdWriteDat(*str++);	//str先把地址对应内容传递给dat输出后,str++;
	}
}


/* 初始化1602液晶 */
void InitLcd1602()
{
	LcdWriteCmd(0x38);			//16*2显示,5*7点阵,8位数据接口
	LcdWriteCmd(0x0c);			//显示区开,光标关闭
	LcdWriteCmd(0x06);			//文字不动,地址自动加一
	LcdWriteCmd(0x01);			//清屏	
}



猜你喜欢

转载自blog.csdn.net/diiiiiiiiiiiiiiii/article/details/80894731
今日推荐