STM8S103K3 I2C

以下为.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通讯需外接上拉电阻!

上一篇:dd命令


下一篇:linux 分析和排查系统故障