以下为.h文件:
定义了PA1为SDA,PA2为SCL
#ifndef __I2C_H
#define __I2C_H
#include "stm8s.h"
#include "stm8s_gpio.h"
#include "tim1.h"
#include "uart.h"
#include <iostm8s103f3.h>
#include <intrinsics.h>
#define SCL PA_ODR_ODR2
#define SDA PA_ODR_ODR1
#define SDAM PA_IDR_IDR1
#define SET_SCL_OUT() {PA_DDR_DDR2=1; PA_CR1_C12 = 1; PA_CR2_C22 = 0;}
#define SET_SDA_OUT() {PA_DDR_DDR1=1; PA_CR1_C11 = 1; PA_CR2_C21 = 0;}
#define SET_SDA_IN() {PA_DDR_DDR1=0; PA_CR1_C11 = 0; PA_CR2_C21 = 0;}
void IIC_Init(void);
void Delay_us(u8 z);
void I2C_Start(void);
void I2C_Stop(void);
void IIC_Ack(void);
void IIC_NAck(void);
uint8_t IIC_Wait_Ack(void);
void IIC_Send_Byte(uint8_t txd);
uint8_t IIC_Read_Byte(uint8_t ack);
void Device_WriteData(uint8_t DeciveAddr,uint8_t DataAddr,uint8_t Data);
void Decive_ReadData(uint8_t DeciveAddr,uint8_t DataAddr,uint8_t *ReciveData,uint8_t num);
#endif
以下为.c文件:
#include "I2C.h"
//--------------------------------------------------------------
// Prototype : void I2C_Start(void)
// Calls : Delay_5us()
// Description : Start Singnal
//--------------------------------------------------------------
void IIC_Init(void)
{
SET_SCL_OUT();
SET_SDA_OUT();
SCL = 1;
SDA = 1;
}
//--------------------------------------------------------------
// Prototype : void Delay_5us(void)
// Description : 大约延时5us
//--------------------------------------------------------------
void Delay_us(u8 z)
{
//u8 i; //fcpu 8MHz 时
//for (i=50; i>0; i--);
while(z--)
{
nop();nop();nop();nop();
}
}
//--------------------------------------------------------------
// Prototype : void I2C_Start(void)
// Calls : Delay_5us()
// Description : Start Singnal
//--------------------------------------------------------------
void I2C_Start(void)
{
// SDA 1->0 while SCL High
//SCL高电平期间,SDA出现一个下降沿表示起始信号
SET_SDA_OUT();
SDA = 1; //数据线先保持为高,起始信号要该口的下降沿
Delay_us(4);
SCL = 1; //时钟线保持为高
Delay_us(40); //有一个大概5us的延时具体以器件而定
SDA = 0; //数据线拉低出现下降沿
Delay_us(4); //延时 一小会,保证可靠的下降沿
SCL = 0; //拉低时钟线,保证接下来数据线允许改变
}
//--------------------------------------------------------------
// Prototype : void I2C_Stop(void)
// Calls : Delay_5us()
// Description : Stop Singnal
//--------------------------------------------------------------
void I2C_Stop(void)
{
// SDA 0->1 while SCL High
//SCL高电平期间,SDA产生一个上升沿 表示停止
SET_SDA_OUT();
SCL = 0;
Delay_us(2);
SDA = 0; //保证数据线为低电平
Delay_us(40);
SCL = 1; //先保证时钟线为高电平
Delay_us(10); //延时 以得到一个可靠的电平信号
SDA = 1; //数据线出现上升沿
Delay_us(40); //延时 保证一个可靠的高电平
}
//应答函数
void IIC_Ack(void)
{
//数据线一直保持为低电平,时钟线出现上升沿即为应答
SET_SDA_OUT();
Delay_us(10);
SDA = 0;
Delay_us(10);
SCL = 0;
Delay_us(40);
SCL = 1;
Delay_us(40);
//应答完成后 将时钟线拉低 允许数据修改
SCL = 0;
}
//非应答
void IIC_NAck(void)
{
//非应答即相反 与应答区别即为数据线保持高电平即可
SET_SDA_OUT();
Delay_us(10);
SDA = 1;
Delay_us(10);
SCL = 0;
Delay_us(40);
SCL = 1;
Delay_us(40);
//最后要将时钟线拉低 允许数据变化
SCL = 0;
}
//等待应答
uint8_t IIC_Wait_Ack(void)//0为有应答,1为无应答
{
//应答等待计数
uint8_t ackTime = 0;
//先将数据线要设置成输入模式本程序未体现,有应答则会出现下降沿
SCL = 0;
SET_SDA_OUT();
Delay_us(10);
SDA = 1;//
Delay_us(30);
SET_SDA_IN();//切换为输入模式
//时钟线拉高
SCL = 1;
Delay_us(30);
//等待数据线拉低应答
while(SDAM){
//如果在该时间内仍未拉低
ackTime ++;
if(ackTime > 250)
{
//认为非应答 停止信号
I2C_Stop();
return 1;
}
}
SCL = 0;
return 0 ;
}
void IIC_Send_Byte(uint8_t txd)
{
//定义一个计数变量
uint8_t i;
SET_SDA_OUT();
//将时钟线拉低允许数据改变
// SCL = 0;
//按位发送数据
for(i = 0;i < 8; i ++)
{
Delay_us(2);
if((txd&0x80)>>7) //0x80 1000 0000
SDA=1;
else
SDA=0;
txd<<=1;
Delay_us(20);
SCL=1;
Delay_us(20);
SCL=0;
Delay_us(20);
}
}
//返回值为收到的数据
//参数为是否应答1应答0不应答
uint8_t IIC_Read_Byte(uint8_t ack)
{
//定义计数变量
uint8_t i = 0;
//定义接收变量
uint8_t receive = 0;
//此时要把数据线的模式切换为输入模式 本程序中不予体现
SET_SDA_IN();//切换为输入模式
for(i = 0;i < 8; i ++)
{
Delay_us(50);
SCL = 0;
Delay_us(50);
//时钟线拉高 读数据保证对方数据不改变
SCL = 1;
//来一个延时保证电平可靠
// Delay_us(5);
//先左移接收变量,防止循环结束时改变该变量
receive<<=1;
//判断数据线电平
if(SDAM)
{
//高电平的话接收变量自加,低电平不变化只左移,即保证了该位为0
receive++;
}
//延时一小会 保证一个可靠的电平
// Delay_us(1);
//时钟线拉低,允许下一位数据改变
//SCL = 0;
}
Delay_us(50);
SCL = 0;
// if(!ack)
// {
//不需要应答 则给出非应答信号,不再继续
// IIC_NAck();
// }
// else
// {
//需要应答 则给应答
// IIC_Ack();
// }
return receive;
}
void Device_WriteData(uint8_t DeciveAddr,uint8_t DataAddr,uint8_t Data)
{
//起始信号
I2C_Start();
//发送器件地址
IIC_Send_Byte(DeciveAddr);
//等待应答
IIC_Wait_Ack();
//发送数据地址
IIC_Send_Byte(DataAddr);
//等待应答
IIC_Wait_Ack();
//发送数据
IIC_Send_Byte(Data);
//等待应答
IIC_Wait_Ack();
//结束信号
I2C_Stop();
}
//读数据
//参数一 器件地址
//参数二 数据地址
//参数三 接收数据存储
//参数四 接收长度
//
void Decive_ReadData(uint8_t DeciveAddr,uint8_t DataAddr,uint8_t *ReciveData,uint8_t num)
{
//定义计数变量
uint8_t i;
//起始信号
I2C_Start();
//发送器件地址
IIC_Send_Byte(DeciveAddr);
//等待应答
IIC_Wait_Ack();
//发送数据地址
IIC_Send_Byte(DataAddr);
//等待应答
IIC_Wait_Ack();
//起始信号
I2C_Start();
//发送器件地址读模式
IIC_Send_Byte(DeciveAddr + 1);
//等待应答
IIC_Wait_Ack();
//读数据
for(i = 0;i < (num-1);i ++)
{
//前num-1位数据时需要给应答的因为要继续读
*ReciveData= IIC_Read_Byte(1);
ReciveData++;
}
//最后一位数据不需要给应答 因为不用读了
*ReciveData = IIC_Read_Byte(0);
//停止信号
I2C_Stop();
}
注:I2C通讯需外接上拉电阻!