KEIL C51 IIC programming


foreword

使用的单片机型号为STC15F2K60S2,程序的测试对象为DS3231时钟模块。基本所有51内核的单片机都可以直接使用。


IIC programming

Personal understanding of IIC communication is the arrangement and combination of 6 basic functions: start function, stop function, send reply/reply non, receive reply/reply non, receive byte data, send byte data . Because the reply signal is generally not processed, the send reply/reply not and the receive reply/reply not are combined into the send reply , that is, there are only 5 types of basic functions. As long as the response signal is not processed, the above two methods can be used normally. The following will list the examples of the above two IIC communication methods respectively, hoping to be helpful to your study and understanding.
Learning reference materials:

http://sumcu.suda.edu.cn/_upload/article/files/74/e5/d4eb93de45808d71ad8aad542ede/a3cb5873-aaf4-4af0-9e5f-521793fbba46.pdf
https://blog.csdn.net/helloworld573/article/details/105629235
https://blog.csdn.net/zzm260270267/article/details/51258349

Design ideas are given in the form of comments in the code, which can be copied and viewed locally.
Code porting process:

1. Modify the header file (in the .c file), replace <STC15F2K60S2.h> with your own header file.
2. Modify the pin (in the .c file)
3. Modify the delay function (in the .c file), just modify the content in the function body directly

Separate IIC communication for receiving and sending responses

.c file

#include <STC15F2K60S2.h>
#include "IIC.h"
#include "intrins.h"//引入_nop_()函数,用于软件延时

//开始信号:时钟线SCL为高电平的条件下,数据线SDA有一个 下降沿。
//结束信号:时钟线SCL为高电平的条件下,数据线SDA有一个 上升沿。
//应答信号:应答是一个低电平信号。
/* 注:
*读取数据线时,需要先释放总线,即 SDA = 1;
*为了提高程序结构的严谨性和简洁性,这里限定IIC基础操作结束时SCL电平
		*开始信号:SCL = 0
		*结束信号:SCL = 1
		*应答信号:SCL = 0
		*发送数据:SCL = 0
		*接受数据:SCL = 0
*默认Device_Addr是从设备写地址,(Device_Addr+1)为从设备读地址。
*理论上说接收数据时,主机发送应答;发送数据时,主机接收应答。但是由于无需对从机应答进行处理,所以这里全部用的是主机发送应答。
*/

sbit IIC_SDA = P4^6;        //声明IIC总线的数据线接在单片机的P4.6端口。
sbit IIC_SCL = P4^5;        //声明IIC总线的时钟线接在单片机的P4.5端口。

/****************************************
*函数功能:延时函数,此处设置延时为10us
*参数:		
			无
*返回值:	
			无
****************************************/
void delay_IIC(void)
{
    
    
    unsigned char Number = 8;
    while (Number--){
    
    
        _nop_();
        _nop_();
    }
}
/****************************************
*函数功能:IIC开始信号
*参数:		
			无
*返回值:	
			无
****************************************/
void IIC_start(void)
{
    
    
	IIC_SDA = 1;
	
	IIC_SCL = 1;delay_IIC();

	IIC_SDA = 0;delay_IIC();

	IIC_SCL = 0;delay_IIC();		//拉低时钟线
	
}
/****************************************
*函数功能:IIC结束信号
*参数:		
			无
*返回值:	
			无
****************************************/
void IIC_stop(void)
{
    
    
	IIC_SDA = 0;delay_IIC();
	
	IIC_SCL = 1;delay_IIC();
	
	IIC_SDA = 1;delay_IIC();
}

/****************************************
*函数功能:IIC 主机接收 应答信号
*参数:	
			无
*返回值:	
			0------从机发送 应答
			1------从机发送 非应答
****************************************/
unsigned char IIC_Rack(void)
{
    
    
	unsigned char Ack;
	IIC_SDA = 1;delay_IIC();//释放数据线
	
	IIC_SCL = 1;delay_IIC();
	
	Ack = IIC_SDA;delay_IIC();
	
	IIC_SCL = 0;delay_IIC();
	
    return Ack;
}
/****************************************
*函数功能:IIC 主机发送 应答信号
*参数:	
			Ack = 0 ,主机发送 应答
			Ack = 1 ,主机发送 非应答
*返回值:	
			无
****************************************/
void IIC_Tack(unsigned char Ack)
{
    
    
	
	
	IIC_SDA = Ack;delay_IIC();//发送应答或非应答
	
	IIC_SCL = 1;delay_IIC();
	
	IIC_SCL = 0;delay_IIC();
}

/****************************************
*函数功能:IIC 发送 一字节数据
*参数:		
			Data------等待发送的数据
*返回值:	
			无
****************************************/
void IIC_write_byte(unsigned char Data)
{
    
    
	unsigned char i;
	for(i = 0;i < 8;i++)	//有8位数据
	{
    
    
		if(Data & 0x80){
    
    	//发送高位数据
			IIC_SDA = 1;
		}else{
    
    
			IIC_SDA = 0;
		}delay_IIC();
		
		IIC_SCL = 1;delay_IIC();
		
		IIC_SCL = 0;delay_IIC();
		//Data <<= 1;
		Data = Data << 1;
	}
}
/****************************************
*函数功能:IIC 接收 一字节数据
*参数:		
			无
*返回值:	
			接收的一字节数据
****************************************/
unsigned char IIC_recv_byte(void)
{
    
    
    unsigned char i;
    unsigned char Data;
	IIC_SDA = 1;		//先释放数据线,再读取数据
    for(i=0;i<8;i++)
    {
    
    
        IIC_SCL = 1;delay_IIC();
        Data = Data << 1; delay_IIC();
        if(IIC_SDA){
    
    
            Data |= 0x01;
		}
        IIC_SCL = 0;delay_IIC();
    }
    return Data;
}
/****************************************
*函数功能:向从机某一地址单元写入一字节数据
*参数:		
			Device_Addr       ----------从设备地址(写)
			Write_Addr        ----------待写入的地址
			Data              ----------待写入的数据
*返回值:	
			无
****************************************/
void IIC_write_single_byte(unsigned char Device_Addr,unsigned char Write_Addr,unsigned char Data)
{
    
    
    IIC_start();
	
    IIC_write_byte(Device_Addr);
    IIC_Rack();//接收从机应答信号,但不进行处理
	
    IIC_write_byte(Write_Addr);
    IIC_Rack();
	
    IIC_write_byte(Data);
	IIC_Rack();
	
    IIC_stop();
}
/****************************************
*函数功能:获取从机某一地址单元的一字节数据
*参数:		
			Device_Addr ----------从设备地址(写)
			Recv_Addr   ----------待读取的地址
*返回值:	
			接收到的一字节数据
****************************************/
unsigned char IIC_recv_single_byte(unsigned char Device_Addr,unsigned char Recv_Addr)
{
    
    
    unsigned char Data;

    IIC_start();

    IIC_write_byte(Device_Addr);//设备地址(写)。
    IIC_Rack();

    IIC_write_byte(Recv_Addr);
    IIC_Rack();

    IIC_start();

    IIC_write_byte(Device_Addr + 1);//设备地址(读)。
    IIC_Rack();

    Data = IIC_recv_byte();
	IIC_Tack(1);//若想连续读,就不断发送应答信号,否则发送应答非

    IIC_stop();				//产生停止符号。
    return Data;			//返回读取的一个字节数据。
}
/****************************************
*函数功能:连续写入从机地址单元
*参数:		
			Device_Addr ----------从设备地址(写)
			Write_Addr  ----------待写入的首地址
			Data		----------用于保存数据的数组名或指针
			Length		----------指定读取数据的长度
*返回值:	
			无
****************************************/
void IIC_write_bytes(unsigned char Device_Addr,unsigned char Write_Addr,unsigned char *Data, unsigned char Length)
{
    
    
	unsigned char i;
	
    IIC_start();
	
    IIC_write_byte(Device_Addr);
    IIC_Rack();
	
    IIC_write_byte(Write_Addr);
	IIC_Rack();

	for(i = 0; i < Length; i++){
    
    
		IIC_write_byte(*(Data + i));
		IIC_Rack();
	}
	
    IIC_stop();
}
/****************************************
*函数功能:连续读取从机地址单元
*参数:		
			Device_Addr ----------从设备地址(写)
			Recv_Addr   ----------待写入的首地址
			Data		----------待写入数据的数组名或指针
			Length		----------指定写入数据的长度
*返回值:	
			无
****************************************/
void IIC_recv_bytes(unsigned char Device_Addr,unsigned char Recv_Addr,unsigned char *Data, unsigned char Length)
{
    
    
	unsigned char i;
    IIC_start();

    IIC_write_byte(Device_Addr);//写入设备地址(写)。
    IIC_Rack();

    IIC_write_byte(Recv_Addr);
    IIC_Rack();

    IIC_start();

    IIC_write_byte(Device_Addr + 1);//写入设备地址(读)。
    IIC_Rack();
	
	for(i = 0; i < Length; i++){
    
    
		*(Data + i) = IIC_recv_byte();
		IIC_Tack(0);
	}
	
    IIC_stop();				//产生停止符号。
}


.h file

#ifndef _IIC_H_
#define _IIC_H_
//--------------------------------------------
/*IIC基础函数*/
void delay_IIC(void);
void IIC_start(void);
void IIC_stop(void);
unsigned char IIC_Rack(void);
void IIC_Tack(unsigned char Ack);
void IIC_write_byte(unsigned char Data);
unsigned char IIC_recv_byte(void);

//--------------------------------------------
/*接收/发送 单 字节数据*/
void IIC_write_single_byte(unsigned char Device_Addr,unsigned char Write_Addr,unsigned char Data);
unsigned char IIC_recv_single_byte(unsigned char Device_Addr,unsigned char Recv_Addr);

//--------------------------------------------
/*接收/发送 多 字节数据*/
void IIC_write_bytes(unsigned char Device_Addr,unsigned char Write_Addr,unsigned char *Data, unsigned char Length);
void IIC_recv_bytes(unsigned char Device_Addr,unsigned char Recv_Addr,unsigned char *Data, unsigned char Length);

#endif

IIC communication that only sends responses

.c file

#include <STC15F2K60S2.h>
#include "IIC.h"
#include "intrins.h"//引入_nop_()函数,用于软件延时

//开始信号:时钟线SCL为高电平的条件下,数据线SDA有一个 下降沿。
//结束信号:时钟线SCL为高电平的条件下,数据线SDA有一个 上升沿。
//应答信号:应答是一个低电平信号。
/* 注:
*读取数据线时,需要先释放总线,即 SDA = 1;
*为了提高程序结构的严谨性和简洁性,这里限定IIC基础操作结束时SCL电平
		*开始信号:SCL = 0
		*结束信号:SCL = 1
		*应答信号:SCL = 0
		*发送数据:SCL = 0
		*接受数据:SCL = 0
*默认Device_Addr是从设备写地址,(Device_Addr+1)为从设备读地址。
*理论上说接收数据时,主机发送应答;发送数据时,主机接收应答。但是由于无需对从机应答进行处理,所以这里全部用的是主机发送应答。
*/

sbit IIC_SDA = P4^6;        //声明IIC总线的数据线接在单片机的P4.6端口。
sbit IIC_SCL = P4^5;        //声明IIC总线的时钟线接在单片机的P4.5端口。

/****************************************
*函数功能:延时函数,此处设置延时为10us
*参数:		
			无
*返回值:	
			无
****************************************/
void delay_IIC(void)
{
    
    
    unsigned char Number = 8;
    while (Number--){
    
    
        _nop_();
        _nop_();
    }
}
/****************************************
*函数功能:IIC开始信号
*参数:		
			无
*返回值:	
			无
****************************************/
void IIC_start(void)
{
    
    
	IIC_SDA = 1;
	
	IIC_SCL = 1;delay_IIC();

	IIC_SDA = 0;delay_IIC();

	IIC_SCL = 0;delay_IIC();		//拉低时钟线
	
}
/****************************************
*函数功能:IIC结束信号
*参数:		
			无
*返回值:	
			无
****************************************/
void IIC_stop(void)
{
    
    
	IIC_SDA = 0;delay_IIC();
	
	IIC_SCL = 1;delay_IIC();
	
	IIC_SDA = 1;delay_IIC();
}

/****************************************
*函数功能:IIC 主机发送 应答信号
*参数:	
			无
*返回值:	
			无
****************************************/
void IIC_Ack(void)
{
    
    
	
	
	IIC_SDA = 0;delay_IIC();
	
	IIC_SCL = 1;delay_IIC();
	
	IIC_SCL = 0;delay_IIC();
}

/****************************************
*函数功能:IIC 发送 一字节数据
*参数:		
			Data------等待发送的数据
*返回值:	
			无
****************************************/
void IIC_write_byte(unsigned char Data)
{
    
    
	unsigned char i;
	for(i = 0;i < 8;i++)	//有8位数据
	{
    
    
		if(Data & 0x80){
    
    	//发送高位数据
			IIC_SDA = 1;
		}else{
    
    
			IIC_SDA = 0;
		}delay_IIC();
		
		IIC_SCL = 1;delay_IIC();
		
		IIC_SCL = 0;delay_IIC();
		//Data <<= 1;
		Data = Data << 1;
	}
}
/****************************************
*函数功能:IIC 接收 一字节数据
*参数:		
			无
*返回值:	
			接收的一字节数据
****************************************/
unsigned char IIC_recv_byte(void)
{
    
    
    unsigned char i;
    unsigned char Data;
	IIC_SDA = 1;		//先释放数据线,再读取数据
    for(i=0;i<8;i++)
    {
    
    
        IIC_SCL = 1;delay_IIC();
        Data = Data << 1; delay_IIC();
        if(IIC_SDA){
    
    
            Data |= 0x01;
		}
        IIC_SCL = 0;delay_IIC();
    }
    return Data;
}
/****************************************
*函数功能:向从机某一地址单元写入一字节地址
*参数:		
			Device_Addr       ----------从设备地址(写)
			Write_Addr        ----------待写入的地址
			Data              ----------待写入的数据
*返回值:	
			无
****************************************/
void IIC_write_single_byte(unsigned char Device_Addr,unsigned char Write_Addr,unsigned char Data)
{
    
    
    IIC_start();
	
    IIC_write_byte(Device_Addr);
    IIC_Ack();
	
    IIC_write_byte(Write_Addr);
    IIC_Ack();
	
    IIC_write_byte(Data);
	IIC_Ack();
	
    IIC_stop();
}
/****************************************
*函数功能:获取从机某一地址单元的一字节数据
*参数:		
			Device_Addr ----------从设备地址(写)
			Recv_Addr   ----------待读取的地址
*返回值:	
			接收到的一字节数据
****************************************/
unsigned char IIC_recv_single_byte(unsigned char Device_Addr,unsigned char Recv_Addr)
{
    
    
    unsigned char Data;

    IIC_start();

    IIC_write_byte(Device_Addr);//设备地址(写)。
    IIC_Ack();

    IIC_write_byte(Recv_Addr);
    IIC_Ack();

    IIC_start();

    IIC_write_byte(Device_Addr + 1);//设备地址(读)。
    IIC_Ack();

    Data = IIC_recv_byte();
	IIC_Ack();

    IIC_stop();				//产生停止符号。
    return Data;			//返回读取的一个字节数据。
}
/****************************************
*函数功能:连续写入从机地址单元
*参数:		
			Device_Addr ----------从设备地址(写)
			Write_Addr  ----------待写入的首地址
			Data		----------用于保存数据的数组名或指针
			Length		----------指定读取数据的长度
*返回值:	
			无
****************************************/
void IIC_write_bytes(unsigned char Device_Addr,unsigned char Write_Addr,unsigned char *Data, unsigned char Length)
{
    
    
	unsigned char i;
	
    IIC_start();
	
    IIC_write_byte(Device_Addr);
	IIC_Ack();
	
    IIC_write_byte(Write_Addr);
	IIC_Ack();

	for(i = 0; i < Length; i++){
    
    
		IIC_write_byte(*(Data + i));
		IIC_Ack();
	}
	
    IIC_stop();
}
/****************************************
*函数功能:连续写入从机地址单元
*参数:		
			Device_Addr ----------从设备地址(写)
			Recv_Addr   ----------待写入的首地址
			Data		----------待写入数据的数组名或指针
			Length		----------指定写入数据的长度
*返回值:	
			无
****************************************/
void IIC_recv_bytes(unsigned char Device_Addr,unsigned char Recv_Addr,unsigned char *Data, unsigned char Length)
{
    
    
	unsigned char i;
    IIC_start();

    IIC_write_byte(Device_Addr);//写入设备地址(写)。
    IIC_Ack();

    IIC_write_byte(Recv_Addr);
    IIC_Ack();

    IIC_start();

    IIC_write_byte(Device_Addr + 1);//写入设备地址(读)。
    IIC_Ack();
	
	for(i = 0; i < Length; i++){
    
    
		*(Data + i) = IIC_recv_byte();
		IIC_Ack();
	}
	
    IIC_stop();				//产生停止符号。
}

.h file

#ifndef _IIC_H_
#define _IIC_H_
//--------------------------------------------
/*IIC基础函数*/
void delay_IIC(void);
void IIC_start(void);
void IIC_stop(void);
void IIC_Ack(void);
void IIC_write_byte(unsigned char Data);
unsigned char IIC_recv_byte(void);

//--------------------------------------------
/*接收/发送 单 字节数据*/
void IIC_write_single_byte(unsigned char Device_Addr,unsigned char Write_Addr,unsigned char Data);
unsigned char IIC_recv_single_byte(unsigned char Device_Addr,unsigned char Recv_Addr);

//--------------------------------------------
/*接收/发送 多 字节数据*/
void IIC_write_bytes(unsigned char Device_Addr,unsigned char Write_Addr,unsigned char *Data, unsigned char Length);
void IIC_recv_bytes(unsigned char Device_Addr,unsigned char Recv_Addr,unsigned char *Data, unsigned char Length);

#endif

Guess you like

Origin blog.csdn.net/m0_54346266/article/details/128591014