软件模拟IIC主从机

软件模拟IIC

  • 从机部分
    • 从机接收部分
    • 从机发送部分
  • 主机部分
    • 阻塞式发送
    • 定时器中断方式发送

从机部分

因为项目简单,就只有数据接收,数据命令处理,显示。显示部分使用定时器中断动态扫描方式,主函数用来处理接收,主函数阻塞式接收,设立一个标志位IICTimerOut,触发之后直接跳过这次数据接收,处理接收超时程序。定时器去接收感觉不太好,容易漏接,但是卡死主程序,有好的资料分享一下
下面是h文件

#define  SLAVE_ADDR 		0x00

#define  MASTER_WRITE 		0x00
#define  MASTER_READ 		0x01
#define  Adderss_NOMatch 	0x02

#define CHANGE_DATA         0x00
#define START_SIGNAL		0x01
#define STOP_SIGNAL			0x02

#define DataError			0x00
#define DataCorrect			0x01
#define SET_SDA_LOW  IO_SDA = 0
#define SET_SDA_HIGH IO_SDA = 1
#define SDA_SET_OUT IOSTB &= ~SDABit
#define SDA_SET_IN	IOSTB |= SDABit
//
//#define WAIT_IIC_SCL_HIGH   while ( (!GET_SCL_DAT))
//#define WAIT_IIC_SCL_LOW    while ( GET_SCL_DAT )
//#define WAIT_IIC_SDA_HIGH   while ( !GET_SDA_DAT )
//#define WAIT_IIC_SDA_LOW    while ( GET_SDA_DAT )

#define WAIT_IIC_SCL_HIGH   while ( (!GET_SCL_DAT)&&(!DataReciveTimerOut))
#define WAIT_IIC_SCL_LOW    while ( GET_SCL_DAT&&(!DataReciveTimerOut) )
#define WAIT_IIC_SDA_HIGH   while ( !GET_SDA_DAT&&(!DataReciveTimerOut) )
#define WAIT_IIC_SDA_LOW    while ( GET_SDA_DAT&&(!DataReciveTimerOut) )

//#define IIC_WAIT_START      WAIT_IIC_SCL_HIGH;  WAIT_IIC_SDA_LOW  
//#define IIC_WAIT_STOP       WAIT_IIC_SCL_LOW; SDA_SET_IN; WAIT_IIC_SCL_HIGH; WAIT_IIC_SDA_HIGH 
#define IIC_WAIT_START      while(START_SIGNAL != IICSignal)
#define IIC_WAIT_STOP       SDA_SET_IN; while(STOP_SIGNAL != IICSignal) 

#define WAIT_IIC_MASTER_ACK WAIT_IIC_SCL_LOW; SDA_SET_IN;WAIT_IIC_SDA_HIGH; WAIT_IIC_SCL_HIGH   
#define WAIT_IIC_MASTER_NAK WAIT_IIC_SCL_LOW; SDA_SET_IN; WAIT_IIC_SCL_HIGH   

#define IIC_SLAVE_SEND_LOW  WAIT_IIC_SCL_LOW; SDA_SET_OUT; SET_SDA_LOW; WAIT_IIC_SCL_HIGH      
#define IIC_SLAVE_SEND_HIGH WAIT_IIC_SCL_LOW; SDA_SET_OUT; SET_SDA_HIGH; WAIT_IIC_SCL_HIGH

#define IIC_SLAVE_SEND_ACK  IIC_SLAVE_SEND_LOW
#define IIC_SLAVE_SEND_NAK  IIC_SLAVE_SEND_HIGH

U8 IICJudgeAddress(void);
void IICSlaverInit(void);
U8 IICReciveByteData(void);
U8 IICReciveArray(U8 *Buffer);
U8 CheckData(const U8 *Buffer,U8 Length);
void ClearRecive(U8 *Buffer,U8 Lenght);
#endif

从机接收部分

接收采用的是外部中断+主函数阻塞等待的方式,在外部中断中判断起始信号和停止信号,程序暂时还未对停止信号做特殊处理(默认主机不会发送错误,提前发送终止信号)SDA配置成外部中断
核心部分

//外部中断代码
	  	if(GET_SCL_DAT){
	  		if( !GET_SDA_DAT )IICSignal = START_SIGNAL;
	  		else if( GET_SDA_DAT ) IICSignal = STOP_SIGNAL;
	  	} else IICSignal = CHANGE_DATA; 	

//返回 1 主机读数据
//返回 0 主机写数据
U8 IICJudgeAddress(void)
{
	U8 BitCount;
	U8 SlaveAddress = 0;
	U8 IIC_Master_RW = 0;
	
	IIC_WAIT_START;
	for(BitCount = 0; BitCount < 7; BitCount++)  	// 读取7位地址
	{
		WAIT_IIC_SCL_LOW;                   
		WAIT_IIC_SCL_HIGH;
		SlaveAddress <<= 1;  //移位,读数
		if(GET_SDA_DAT) SlaveAddress |= 0x01; //SDA =1 bit置1
		else ;   //SDA =0 bit置0
	} 
	WAIT_IIC_SCL_LOW;
	WAIT_IIC_SCL_HIGH;
	if(GET_SDA_DAT) IIC_Master_RW = 1; //	 读写标志位	
	else IIC_Master_RW = 0;
	if(SLAVE_ADDR==SlaveAddress){
		IIC_SLAVE_SEND_ACK;    // 地址正确,从机发送ACK信号
	} else {
		WAIT_IIC_SCL_LOW;    //地址对应错误,不应答,但是符合时序不能乱了时序
		WAIT_IIC_SCL_HIGH;
		return Adderss_NOMatch ;
	}
	return IIC_Master_RW;
}
//高位先进
//没有写接收完成回应
U8 IICReciveByteData(void) 
{
	U8 Data = 0;
	U8 BitCount;
	for(BitCount = 0; BitCount < 8; BitCount ++){
		WAIT_IIC_SCL_LOW;                   
		WAIT_IIC_SCL_HIGH;
		Data <<= 1;  //移位,读数
		if(GET_SDA_DAT) Data |= 0x01; //SDA =1 bit置1
		else ; //SDA=0 不用操作
	}
	return Data;
}

数据包不固定长度,暂时只想到了用do while 处理(第一个byte是数据长度),如果固定长度还是可以把这一部分封装成一个函数。
协议数据包长度不包括最后的校验byte,所以要多加一个byte数据接收
if(DataLength==ProcessTimes) while ((ProcessTimes<=DataLength) 实际是加了一byte

#define IICReciveData()                                                                   \
                          do {                                                            \
								ReciveData[ProcessTimes] =IICReciveByteData();            \
								IIC_SLAVE_SEND_ACK;                                       \
		    					if(0==ProcessTimes) DataLength=ReciveData[ProcessTimes];  \
		    					ProcessTimes++;	                                          \
							} while ((ProcessTimes<=DataLength)&&(!DataReciveTimerOut));  \
							IIC_WAIT_STOP	

从机发送部分

void IICSendByteData(U8 DATA)
{
	U8 i;
	for(i= 0;i<8;i++)
	{
		if(DATA&(0x01<<i)) IIC_SLAVE_SEND_HIGH;
		else IIC_SLAVE_SEND_LOW;
	}
}

void IICSendBuffer(U8 *Buffer,U8 Lenght )
{
	U8 i;
	for(i= 0;i<Lenght;i++)
	{
		IIC_WAIT_START;
		IICSendByteData(Buffer[i]);
		if((Lenght - 1)==i) WAIT_IIC_MASTER_NAK;
		else WAIT_IIC_MASTER_ACK;
		IIC_WAIT_STOP;
	}	
}

这一段还未具体测试,目前只是想要做出这个功能,用两个外部中断完成这个功能,不阻塞主程序,要求ic必须要有2个io口可以上升下降沿外部中断,能够返回主机发送的数据长度,以及主机发送完成flag,采用SCL下降沿跟新数据,上升沿读取数据。有兴趣的可以自行测试
update 2020.7.15 最近的方案中又使用到了iic从机,所以将下述功能已完全实现,上述方案属第一次写,主体接收功能实现,但基本上项目的其他工作只能放在定时器中断中做,可移植性比较差,下面的思路适合移植,通用性高 博客链接


#define iic_sda_set_out()    P01_PushPull_Mode   
#define iic_sda_set_in()     P01_Quasi_Mode  
#define iic_sda_read()            GetDINStatus    
#define iic_sda_send_high()  set_P01  
#define iic_sda_send_low()   clr_P01
enum{
	STEP_IIC_JETECT_ADDR = 0,
	STEP_IIC_DETECT_WRITE_OR_READ,
	STEP_IIC_SEND_DATA,
	STEP_IIC_READ_ACK,
	STEP_IIC_STOP,
	STEP_IIC_RESTART,
	STEP_IIC_READ_DATA,
	STEP_IIC_SEND_ACK,
};
enum{
	ACK = 0,
	NACK
};
enum{
	IIC_NO_DATA = 0,
    IIC_READ_FINSH
};
enum{
	IIC_NO_OPERAT = 0,
	IIC_ERROR ,
	IIC_WRITE,
    IIC_READ,
};
#define IIC_BUFF_SIZE 10
#define ON_SCL_INTERRUPT() 10
#define OFF_SCL_INTERRUPT() 10
/*
两个typedef 分别模拟的是C++中的共有成员和私有成员,g全局变量,s静态私有变量
为什么要用 结构体,一开始用的是普通变量,做出来的效果感觉对外接口的感觉不够
加上结构体感觉好很多,不创建成函数的局部变量是因为创建函数还有局部变量都需要时间
*/
typedef struct {
    u8 status 	: 3; 
	u8 recive 	: 1; 
    u8 buff_len ;
	u8 buff[IIC_BUFF_SIZE];
}g_soft_iic_slaver;
typedef struct {
	u8 temp;
	u8 number;
	u8 scl_level	: 1; 
	u8 sda_level	: 1; 
	u8 bit_cnt  	: 4; 
	u8 step			: 4; 
	u8 address 		: 7; 
}s_soft_iic_slaver;
g_soft_iic_slaver g_iic_slaver;
static s_soft_iic_slaver s_iic_slaver;
// sda 中断
void iic_detect_start_or_stop_signal(void)
{
	s_iic_slaver.sda_level = iic_sda_read();
	s_iic_slaver.scl_level = iic_scl_read();
	if(1 == s_iic_slaver.scl_level){
		if(0 == s_iic_slaver.sda_level){
			s_iic_slaver.step = STEP_IIC_JETECT_ADDR;
			ON_SCL_INTERRUPT();
		} else if(1 == s_iic_slaver.sda_level){
			if (IIC_ERROR == g_iic_slaver.status){

			}else{
				g_iic_slaver.recive = IIC_READ_FINSH;
				g_iic_slaver.buff_len = s_iic_slaver.number ;
				OFF_SCL_INTERRUPT();
			}
			s_iic_slaver.step = STEP_IIC_STOP;
			g_iic_slaver.status = IIC_NO_OPERAT;
		} 
	} else {
		// IICSignal = CHANGE_DATA; //scl低电平,数据更换
	}
}
//scl 中断
void iic_send_or_recive_data(void)
{
	s_iic_slaver.scl_level = iic_scl_read();
	if(1 == s_iic_slaver.scl_level ){ //上升沿,读io数据
		switch (s_iic_slaver.step){
			case STEP_IIC_JETECT_ADDR:
				s_iic_slaver.sda_level = iic_sda_read();
				s_iic_slaver.temp <<= 1;
				s_iic_slaver.temp += s_iic_slaver.sda_level;
				s_iic_slaver.bit_cnt++;
				if (7 == s_iic_slaver.bit_cnt){
					if (s_iic_slaver.temp == s_iic_slaver.address){
						s_iic_slaver.step = STEP_IIC_DETECT_WRITE_OR_READ;
					}else{ //地址不匹配
						s_iic_slaver.step = STEP_IIC_STOP;
						OFF_SCL_INTERRUPT();
					}
					s_iic_slaver.bit_cnt = 0;
					s_iic_slaver.temp = 0;
				}
				break;
			case STEP_IIC_DETECT_WRITE_OR_READ:
				s_iic_slaver.sda_level = iic_sda_read();
				if (1 == s_iic_slaver.sda_level){ 		//主机要读数据,从机发数据
					s_iic_slaver.step = STEP_IIC_READ_DATA;
					g_iic_slaver.status = IIC_READ;
				}else if (0 == s_iic_slaver.sda_level){ //主机要发数据,从机读数据
					s_iic_slaver.step = STEP_IIC_SEND_DATA;
					g_iic_slaver.status = IIC_WRITE;
				}
				break;
			case STEP_IIC_READ_DATA:
				s_iic_slaver.sda_level = iic_sda_read();
				s_iic_slaver.temp <<= 1;
				s_iic_slaver.temp += s_iic_slaver.sda_level;
				s_iic_slaver.bit_cnt++;
				if (8 == s_iic_slaver.bit_cnt){
					s_iic_slaver.step = STEP_IIC_SEND_ACK;
				}
				break;
			case STEP_IIC_SEND_DATA:
				if (8 == s_iic_slaver.bit_cnt){
					s_iic_slaver.bit_cnt = 0;
					s_iic_slaver.step = STEP_IIC_READ_ACK;
				}
				break;
			case STEP_IIC_SEND_ACK:
				s_iic_slaver.step = STEP_IIC_READ_DATA;
				break;
			case STEP_IIC_READ_ACK:
				s_iic_slaver.sda_level = iic_sda_read();
				if (1 == s_iic_slaver.sda_level){ //主机不应答
					OFF_SCL_INTERRUPT();
					s_iic_slaver.number  = 0;//flag 清零
				}else if (0 == s_iic_slaver.sda_level){ //主机应答
					s_iic_slaver.number ++;
					if (s_iic_slaver.number >= g_iic_slaver.buff_len){ //溢出
						OFF_SCL_INTERRUPT();
						s_iic_slaver.number = 0;
					}else {
						s_iic_slaver.temp = g_iic_slaver.buff[s_iic_slaver.number];
						s_iic_slaver.step = STEP_IIC_SEND_DATA;	
					}	
				}
				break;
			default:
				break;
		}
	} else { //下降沿 发送应答信号
			switch (s_iic_slaver.step){
			case STEP_IIC_JETECT_ADDR:

				break;
			case STEP_IIC_DETECT_WRITE_OR_READ:

				break;
			case STEP_IIC_READ_DATA:
				iic_sda_set_in();
				break;
			case STEP_IIC_SEND_DATA:
				iic_sda_set_out();
				if (s_iic_slaver.temp&(0x80 >> s_iic_slaver.bit_cnt)){
					iic_sda_send_high();
				}else{
					iic_sda_send_low();
				}
				s_iic_slaver.bit_cnt++;
				break;
			case STEP_IIC_SEND_ACK:
				iic_sda_set_out();
				iic_sda_send_low(); //应答
				g_iic_slaver.buff[s_iic_slaver.number++] = s_iic_slaver.temp;
				s_iic_slaver.temp = 0;
				s_iic_slaver.bit_cnt = 0;
				if (s_iic_slaver.number >= g_iic_slaver.buff_len){//溢出
					OFF_SCL_INTERRUPT();
					s_iic_slaver.number = 0;
					g_iic_slaver.status = IIC_ERROR;
				}
				break;
			case STEP_IIC_READ_ACK:
				//设置sda输入,准备读主机应答信号
				iic_sda_set_in();
				break;
			default:
				break;
		}
	}
}

主机部分

阻塞式发送

用于测试的主机使用的是原子哥的软件模拟IIC,感觉时序写的不是很好,自己修改了一下。
h文件,发送的速度修改delay就行,其实多用delay让主程序卡死不太好,之后写一篇用定时器做延时的iic主机程序

//设置数据方向
#define iic_scl_set_out()   P03_PushPull_Mode     
#define iic_scl_send_high() set_P03
#define iic_scl_send_low()  clr_P03  

#define iic_sda_set_out()    P01_PushPull_Mode   
#define iic_sda_set_in()     P01_Quasi_Mode  
#define iic_sda_read            GetDINStatus    
#define iic_sda_send_high()  set_P01  
#define iic_sda_send_low()   clr_P01 

//IIC所有操作函数
void IIC_Init(void);                //初始化IIC的IO口				 
void IIC_Start(void);				//发送IIC开始信号
void IIC_Stop(void);	  			//发送IIC停止信号
void IIC_Send_Byte(u8 txd);			//IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 IIC_Wait_Ack(void); 				//IIC等待ACK信号
void IIC_Ack(void);					//IIC发送ACK信号
void IIC_NAck(void);				//IIC不发送ACK信号

c文件

void IIC_Start(void)
{
	iic_sda_set_out();     //sdaÏßÊä³ö
	iic_sda_send_high();	  	  
	iic_scl_send_high();
	delay_us(500);
 	iic_sda_send_low();//START:when CLK is high,DATA change form high to low 
	delay_us(500);
	iic_scl_send_low();//ǯסI2C×ÜÏߣ¬×¼±¸·¢ËÍ»ò½ÓÊÕÊý¾Ý 
}	  
void IIC_Stop(void)
{
    iic_sda_send_high();
	iic_sda_set_out();//sdaÏßÊä³ö	
	iic_scl_send_low();
	delay_us(250);
	iic_sda_send_low();//STOP:when CLK is high DATA change form low to high
 	delay_us(250);	  	  
	iic_scl_send_high();  
	delay_us(250);
	iic_sda_send_high();//·¢ËÍI2C×ÜÏß½áÊøÐźÅ
	delay_us(250);								   	
}
u8 IIC_Wait_Ack(void)
{
	u8 no_ack_time=0;
	iic_scl_send_low();
	iic_sda_send_high();	 
	iic_sda_set_in();      //SDAÉèÖÃΪÊäÈë  
	delay_us(100);  
	iic_scl_send_high();
    delay_us(100);	 
	while(iic_sda_read()){
		no_ack_time++;
		delay_us(2);
		if(no_ack_time>250){
			IIC_Stop();
			return 1;
		}
	}
	delay_us(50);
	iic_scl_send_low();//ʱÖÓÊä³ö0
	iic_sda_set_out();	
	return 0;  
} 
void IIC_Ack(void)
{
	iic_scl_send_low();
    iic_sda_send_low();
	iic_sda_set_out();
	delay_us(150);
	iic_scl_send_high();
	delay_us(150);
	iic_scl_send_low();
}
	    
void IIC_NAck(void)
{
	iic_scl_send_low();
    iic_sda_send_high();
	iic_sda_set_out();
	delay_us(150);
	iic_scl_send_high();
	delay_us(150);
	iic_scl_send_low();
}					 				     		  
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;       
    iic_scl_send_low();//À­µÍʱÖÓ¿ªÊ¼Êý¾Ý´«Êä
	iic_sda_set_out();
    for(t=0;t<8;t++){  
        iic_scl_send_low();
		delay_us(150);
		if((txd&0x80)>>7)
			iic_sda_send_high();
		else
			iic_sda_send_low();
		txd<<=1; 	  
		delay_us(150); 
		iic_scl_send_high();
		delay_us(300); 
    }
} 	    
u8 IIC_Read_Byte(unsigned char ack)
{
	u8 i,receive=0;
	iic_sda_set_in();//SDAÉèÖÃΪÊäÈë
  	for(i=0;i<8;i++ ){
		iic_scl_send_low() ; 
		delay_us(300);
		iic_scl_send_high();
		delay_us(150);
		receive<<=1;
		if(iic_sda_read())receive++;   
		delay_us(150); 
    }					 
    if (!ack)
        IIC_NAck();//·¢ËÍnACK
    else
        IIC_Ack(); //·¢ËÍACK   
    return receive;
}

应用

static u8 IIC_write_buffer(u8 adress,const u8 *buffer,u8 lenght)
{
	u8 i;
	u8 send_error;
	IIC_Start();
	IIC_Send_Byte(address<<1);
	send_error = IIC_Wait_Ack();
	if(send_error) return 1;//发送出错
	for(i=0;i<lenght;i++){
		IIC_Send_Byte(buffer[i]);
		send_error = IIC_Wait_Ack();
		if(send_error){
			//printf("iic send error no ack\n");
			return 1;
		}
	}
	IIC_Stop();
	return 0;
}
static u8 IIC_read_buffer(u8 adress,u8 *buffer,u8 lenght)
{
	u8 i;
	u8 send_error;
	IIC_Start();
	IIC_Send_Byte((address<<1)+1);
	send_error = IIC_Wait_Ack();
	if(send_error) return 1;//发送出错
	for(i=0;i<lenght;i++){
		buffer[i]= IIC_Read_Byte(1);//发送应答
		send_error = IIC_Wait_Ack();
	}
	IIC_Stop();
	return 0;
}

定时器中断方式发送

做了好几个项目之后对iic的工作方式有了比较深的理解,写了一篇使用定时器中断的方式软件iic主机,只做了测试,能正常发送和接收,实际项目使用还没有,待更新,其实也就是一些发送错误标志位处理之类的应用没测试程序不好封装,目前不支持类似读寄存器地址的方式,所以见谅,后面改进的时候尽量把这个功能做出来,并且封装在一个函数中.
h文件

#define IIC_PORT			JL_PORTC
#define IIC_DAT				5
#define IIC_CLK				4

#define iic_scl_set_out()       do{IIC_PORT->DIR &= ~BIT(IIC_CLK);IIC_PORT->PU &= ~BIT(IIC_CLK);}while(0)
#define iic_scl_send_high()     do{IIC_PORT->OUT |=  BIT(IIC_CLK);IIC_PORT->DIR &=~BIT(IIC_CLK);}while(0)
#define iic_scl_send_low()      do{IIC_PORT->OUT &= ~BIT(IIC_CLK);IIC_PORT->DIR &=~BIT(IIC_CLK);}while(0)


#define iic_sda_set_out()      do{IIC_PORT->DIR &= ~BIT(IIC_DAT);IIC_PORT->PU &= ~BIT(IIC_DAT);}while(0)
#define iic_sda_set_in()       do{IIC_PORT->DIR |=  BIT(IIC_DAT);IIC_PORT->PU |=  BIT(IIC_DAT);}while(0)
#define iic_sda_read()         (IIC_PORT->IN&BIT(IIC_DAT))
#define iic_sda_send_high()    do{IIC_PORT->OUT |=  BIT(IIC_DAT);IIC_PORT->DIR &= ~BIT(IIC_DAT);}while(0)
#define iic_sda_send_low()     do{IIC_PORT->OUT &= ~BIT(IIC_DAT);IIC_PORT->DIR &= ~BIT(IIC_DAT);}while(0)
enum{
	STEP_IIC_START = 0,
	STEP_IIC_SEND_DATA,
	STEP_IIC_READ_ACK,
	STEP_IIC_STOP,
	STEP_IIC_RESTART,
	STEP_IIC_READ_DATA,
	STEP_IIC_SEND_ACK,
};
enum{
	ACK = 0,
	NACK
};
enum{
	CORRECT = 0,
	ERROR,
    READ_FINSH
};
enum{
	IIC_NO_OPERAT = 0,
	IIC_WRITE,
    IIC_READ,
};
#define IIC_BUFF_SIZE 10
/*
两个typedef 分别模拟的是C++中的共有成员和私有成员,g全局变量,s静态私有变量
为什么要用 结构体,一开始用的是普通变量,做出来的效果感觉对外接口的感觉不够
加上结构体感觉好很多,不创建成函数的局部变量是因为创建函数还有局部变量都需要时间
*/
typedef struct {
	unsigned flag_operate 	: 4; 
    unsigned status 		: 4; 
    u8 buff_len ;
	u8 buff[IIC_BUFF_SIZE];
}g_soft_iic_master;
typedef struct {
	u8 number;
	unsigned ack		: 1; 
	unsigned bit_cnt    : 4; 
	unsigned conunt 	: 3; 
	unsigned step		: 5; 
}s_soft_iic_master;
extern g_soft_iic_master g_soft_iic;

c文件

g_soft_iic_master g_soft_iic= {0,0}; //初始化
static s_soft_iic_master s_soft_iic ={2,0,};//初始化  时序步count 2
void IIC_Init(void)
{	
    iic_sda_set_out();         //SDA设置成输出				     
    iic_sda_send_high();
    iic_scl_set_out();         	//SCL设置成输出
    iic_scl_send_high();
    
}
//根据所使用的的定时器去写,iic专用定时器,不用就关了,如果不能关就自己加个flag限定调用函数
#define OFF_TIMER() clr_TR0		//关闭定时器
#define ON_TIMER() 	set_TR0		//开启定时器
void star_iic_send_buff(u8 *buffer,u8 len)
{
	u8 i;
	for (i = 0; i < len; i++){
		g_soft_iic.buff[i] = buffer[i];
	}
	g_soft_iic.buff_len = len;
	g_soft_iic.flag_operate = IIC_WRITE;
	ON_TIMER();	
}
void star_iic_read_buff(u8 adress,u8 len)
{
	g_soft_iic.adress = adress;
	g_soft_iic.buff_len = len;
	g_soft_iic.flag_operate = IIC_READ;
	ON_TIMER();	
}
void iic_send_buffer(void)
{
	if (0 == s_soft_iic.conunt){ //操作scl 翻转scl电平
		iic_scl_send_low();
		if (STEP_IIC_READ_ACK == s_soft_iic.step){
			iic_sda_set_in();
		}else if (STEP_IIC_SEND_DATA == s_soft_iic.step){
			iic_sda_set_out();
		}else if (STEP_IIC_STOP == s_soft_iic.step){
			iic_sda_set_out();
		}
	}else if (1 == s_soft_iic.conunt){ // 操作sda scl 低电平
		switch (s_soft_iic.step){
			case STEP_IIC_SEND_DATA:
				if (g_soft_iic.buff[s_soft_iic.number]&(0x80>>s_soft_iic.bit_cnt)){ //发送1bit数据
					iic_sda_send_high();
				}else {
					iic_sda_send_low();
				}
				break;
			case STEP_IIC_STOP:
				iic_sda_send_low(); //拉低
				break;
		}
	}else if (2 == s_soft_iic.conunt){ //操作scl 翻转scl电平
		iic_scl_send_high();
	}else if (3 == s_soft_iic.conunt){  // 操作sda scl 高电平
		switch (s_soft_iic.step){
			case STEP_IIC_START:
				iic_sda_send_low(); //拉低发送停止信号
				s_soft_iic.step = STEP_IIC_SEND_DATA;
				break;
			case STEP_IIC_SEND_DATA:
				s_soft_iic.bit_cnt++;
				if (8 == s_soft_iic.bit_cnt){
					s_soft_iic.bit_cnt = 0;
					s_soft_iic.step = STEP_IIC_READ_ACK;
				}
				break;
			case STEP_IIC_READ_ACK:
				s_soft_iic.ack = iic_sda_read();
				if (NACK == s_soft_iic.ack){	 //从机无应答
					s_soft_iic.number = 0;
					g_soft_iic.iic_status = ERROR;
					s_soft_iic.step = STEP_IIC_STOP;	
				}else if (ACK == s_soft_iic.ack){
					s_soft_iic.number ++;
					if (s_soft_iic.number >= g_soft_iic.buff_len){ //发送完成
						g_soft_iic.iic_status = CORRECT;
						s_soft_iic.step = STEP_IIC_STOP;
					}else{
						s_soft_iic.step = STEP_IIC_SEND_DATA;	
					}
				}
				break;
			case STEP_IIC_STOP:
				iic_sda_send_high(); //拉高发送停止信号
				s_soft_iic.conunt = 1; //发送完成(退出函数后还会++),起始信号从计数2开始,从零开始的话,scl会白跑一个bit
				s_soft_iic.step = STEP_IIC_START;
				// g_soft_iic.flag_operate = IIC_NO_OPERAT;
				OFF_TIMER();//关闭定时器
				break;
		}	
	}
	s_soft_iic.conunt++;
	if (s_soft_iic.conunt >= 4){
		s_soft_iic.conunt = 0;
	}
}
void iic_recive_buffer(void)
{
	if (0 == s_soft_iic.conunt){ //操作scl 翻转scl电平
		iic_scl_send_low();
		if (STEP_IIC_READ_ACK == s_soft_iic.step){
			iic_sda_set_in();
		}else if (STEP_IIC_STOP == s_soft_iic.step){
			iic_sda_set_out();
		}else if (STEP_IIC_SEND_ACK == s_soft_iic.step){
			iic_sda_set_out();
		}else if (STEP_IIC_READ_DATA == s_soft_iic.step){
			iic_sda_set_in();
		}
	}else if (1 == s_soft_iic.conunt){ // 操作sda scl 低电平
		switch (s_soft_iic.step){
			case STEP_IIC_SEND_DATA:
				if (g_soft_iic.address&(1<<s_soft_iic.bit_cnt)){ //发送1bit数据
					iic_sda_send_high();
				}else {
					iic_sda_send_low();
				}			
				break;
			case STEP_IIC_STOP:
				iic_sda_send_low(); //拉低
				break;
			case STEP_IIC_SEND_ACK:
				iic_sda_send_low(); //每一帧都应答
				break;
		}
	}else if (2 == s_soft_iic.conunt){ //操作scl 翻转scl电平
		iic_scl_send_high();
	}else if (3 == s_soft_iic.conunt){  // 操作sda scl 高电平
		switch (s_soft_iic.step){
			case STEP_IIC_START:
				iic_sda_send_low(); //拉低发送停止信号
				s_soft_iic.step = STEP_IIC_SEND_DATA;
				break;
			case STEP_IIC_SEND_DATA:
				s_soft_iic.bit_cnt++;
				if (8 == s_soft_iic.bit_cnt){
					s_soft_iic.bit_cnt = 0;
					s_soft_iic.step = STEP_IIC_READ_ACK;
				}
				break;
			case STEP_IIC_READ_ACK:
				s_soft_iic.ack = iic_sda_read();
				if (NACK == s_soft_iic.ack){	 //从机无应答
					s_soft_iic.number = 0;
					g_soft_iic.iic_status = ERROR;
					s_soft_iic.step = STEP_IIC_STOP;	
				}else if (ACK == s_soft_iic.ack){
					s_soft_iic.number ++;
					if (s_soft_iic.number >= g_soft_iic.buff_len-1){ //发送完成
						g_soft_iic.iic_status = READ_FINSH;
						s_soft_iic.step = STEP_IIC_STOP;
					}else{
						s_soft_iic.step = STEP_IIC_READ_DATA;	
					}
				}
				break;
			case STEP_IIC_READ_DATA:
				g_soft_iic.buff[s_soft_iic.number] <<= s_soft_iic.bit_cnt; //先左移一位,再赋值
				g_soft_iic.buff[s_soft_iic.number] += iic_sda_read();
				s_soft_iic.bit_cnt++;
				if (8 == s_soft_iic.bit_cnt){ //接收完一帧数据发送应答信号
					s_soft_iic.bit_cnt = 0;  
					s_soft_iic.ack = ACK;
					s_soft_iic.step = STEP_IIC_SEND_ACK;
				}
				break;
			case STEP_IIC_SEND_ACK:
				s_soft_iic.number++;
				if (s_soft_iic.number>=g_soft_iic.buff_len){ //接收完成
					s_soft_iic.step = STEP_IIC_STOP;
				}else{
					s_soft_iic.step = STEP_IIC_READ_DATA;	
				}
				break;
			case STEP_IIC_STOP:
				iic_sda_send_high(); //拉高发送停止信号
				s_soft_iic.conunt = 1; //发送完成(退出函数后还会++),起始信号从计数2开始,从零开始的话,scl会白跑一个bit
				s_soft_iic.step = STEP_IIC_START;
				// g_soft_iic.flag_operate = IIC_NO_OPERAT;定时器关闭了,不用重新重置flag,而且判断iic error时候可以能会用的到这个flag
				OFF_TIMER();//关闭定时器
				break;
		} 	
	}
	s_soft_iic.conunt++;
	if (s_soft_iic.conunt >= 4){
		s_soft_iic.conunt = 0;
	}
}

应用

if (IIC_WRITE == g_soft_iic.flag_operate){
	iic_send_buffer();
}else if (IIC_READ == g_soft_iic.flag_operate){
	iic_recive_buffer();
}

你可能感兴趣的:(单片机常用驱动)