软件模拟IIC主从机

本文详细介绍了如何软件模拟IIC协议的主从机操作。从机部分采用外部中断+主函数阻塞等待的方式接收数据,处理接收超时情况。主机部分则涉及阻塞式发送和定时器中断方式发送,对IIC协议有深入探讨,并提供相关代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从机部分

因为项目简单,就只有数据接收,数据命令处理,显示。显示部分使用定时器中断动态扫描方式,主函数用来处理接收,主函数阻塞式接收,设立一个标志位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 = i
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值