⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 smbus_app_s.c

📁 C8051f系列单片机SMBUS应用例程
💻 C
字号:
///////////应用SMBUS实现内存互访从机程序////////////////
////////Smbus_APP_R.c/////////////////////////////////
#include "smbus.h"
#include "absacc.h"
#define FLOATADR 0x0300
#define SLAVE_FLOATADR 0x0000
#define IIC_R 0x01
#define RETRY_MAX 50
/* IIC_R为SMBUS读控制*/
#define IIC_W 0xfe
#define ISREAD 0x01
/* IIC_W为SMBUS写控制*/
union int16{//采用联合体实现16位与8位混合操作
	uint word;
	struct {uchar hi;uchar low;}split;
};
/*注意不能写成如下代码:
union int16{
	uint word;
	uchar hi;
	uchar low;
};
这样的结果是int16.hi与int16.low占用同一个地址,指向的均是int16.word高字节
*/
struct smbus_mes{
	uchar id;
	union int16 target_adr;
	uint self_adr;
	uchar len;
	uchar isread;
	uchar adr_end;
	uchar isbuserror;
};
xdata struct smbus_mes mes_M,mes_S;//将结构体定义到外部内存上
xdata float floattest _at_ FLOATADR;
xdata uchar *p;
void config(){
//看门狗禁止
	WDTCN = 0x07;	
    WDTCN = 0xDE;   
   	WDTCN = 0xAD;
	SFRPAGE = 0x0F;
//交叉开关配置,SMBUS配置到P0.0和P0.1上
	XBR0 = 0x01;	
	XBR1 = 0x00;	
	XBR2 = 0x40;
 	XBR3 = 0x01;   
//管脚输出配置,P0口为开漏输出,其中P0.6接上拉电阻,P0为数字输入口
 	SFRPAGE = 0x0F;
 	P0MDOUT = 0x00; 
 	P1MDIN = 0xFF;  
//晶振配置,采用内部晶振8分频
	SFRPAGE = 0x0F;
 	CLKSEL = 0x00;  
	OSCXCN = 0x00;	
	OSCICN = 0x84;	
}
void master_mes_creat(uchar id,uint target_adr,uint self_adr,uchar len,uchar dir){
	mes_M.id=id<<1;//id数据在7-1位,须右移1位
	mes_M.id&=IIC_W;//主机要发送从机的内存地址,故起始必然是写操作
	mes_M.target_adr.word=target_adr;
	mes_M.self_adr=self_adr;
	mes_M.len=len;
	mes_M.isread=dir;
	mes_M.isbuserror=0;
}
void main(){
	config();
	mes_S.len=0x79;
	smbus_cfg(0x44,0xf1,0x6f);
	/*
	smbus使能,AA=1,SCL高电平和低电平超时禁止
	smbus时钟频率为100khz
	自身从机地址为0x6e/2,即为55(十进制),且广播寻址使能
	*/
	EA=1;//打开全局中断
	SFRPAGE=0x00;
	floattest=1.234567;
	while(1);
}
void int0() interrupt 0{
}
void int1() interrupt 1{
}
void int2() interrupt 2{
}
void int3() interrupt 3{
}
void int4() interrupt 4{
}
void int5() interrupt 5{
}
void int6() interrupt 6{
}
void smbusInt() interrupt 7{//smbus中断
//比较完善的SMBUS中断处理程序,但是由于对各种状态均作出处理,降低处理速度
//读者可根据需要删减一些状态处理
	static uchar retry_time=0;
	SFRPAGE=0x00;
	if(SMB0STA==0x08){//起始位发送成功,主机模式下才会出现这种状态
		SMB0DAT=mes_M.id;//将地址和读写控制装入发送缓冲区
		STA=0;//将STA清零,注意,若不清零则将一直为重复起始状态
		mes_M.adr_end=0;//内存地址没有发送完毕
		goto bus_end;
	}
	if(SMB0STA==0x10){//重复起始条件处理
		SMB0DAT=mes_M.id;
		STA=0;
		mes_M.adr_end=0;//内存地址没有发送完毕
		goto bus_end;
	}
	if(SMB0STA==0x18){//从地址+W发出,收到从机ACK应答
		SMB0DAT=mes_M.target_adr.split.hi;//发送内存高地址
		mes_M.adr_end=0;//内存地址没有发送完毕
		retry_time=0;//将重试计数器清零
		goto bus_end;
	}
	if(SMB0STA==0x20){//从地址+W发出,收到从机NACK应答
		STO=1;
		retry_time++;
		if(retry_time<RETRY_MAX){
			STA=1;//若重试次数小于最大限值,则产生重复起始条件
		}
		else{
			mes_M.isbuserror=1;//isbuserror在初始化时须清零
			retry_time=0;//若大于重试次数,由于没有将STA置1,不会产生重复起始条件
		//且须将retry_time即时清零,以便下一次SMBUS中断时,重试次数从零开始计数
		}
		goto bus_end;
	}
	if(SMB0STA==0x28){//SMBUS数据成功发送,且收到从机ACK应答
		if(mes_M.len>0){
			if(mes_M.adr_end==0){//若内存地址没有发送完毕,继续发送内存地址
				SMB0DAT=mes_M.target_adr.split.low;//发送内存低地址	
				mes_M.adr_end=2;
			}
			else{
				if(mes_M.isread==ISREAD){
				//更换角色,对从机写完内存地址后,读取从机发送过来的数据
					mes_M.id|=IIC_R;//将id中写控制位改成读控制位
					STA=1;//此时不需要将STO置1
				}
				else{
					SMB0DAT=XBYTE[mes_M.self_adr];
					//取自身外部内存相应地址上的数据发送到SMBUS总线上
					mes_M.target_adr.word++;//对从机操作的内存地址自增1
					mes_M.self_adr++;//自身内存地址自增1
					mes_M.len--;//数据长度减1
				/*
				主从机内存地址自增能保证当产生重复起始条件时,对于成功发送的数据不再重发
				*/
					}
				
			}
		}
		else{//若数据长度等于0,则停止发送
			STO=1;
			retry_time=0;
		}
		goto bus_end;
	}
	if(SMB0STA==0x30){//SMBUS数据成功发送,但接收到从机NACK应答
		STO=1;
		STA=1;//产生重复起始条件
		goto bus_end;
	}
	if(SMB0STA==0x38){//总线竞争失败。
		STO=1;//发送停止帧,并把总线错误标志位置1。
		mes_M.isbuserror=1;
		goto bus_end;
	}
	if(SMB0STA==0x40){//从机地址+R发送成功,接收到从机ACK应答信号
		if(mes_M.len>1)
			AA=1;//下一接收数据不是最后数据,故AA置1,以应答从机
		else{
			if(mes_M.len==1)//下一数据为最后数据
				AA=0;
		}
		retry_time=0;//将重试计数器清零
		goto bus_end;
	}
	if(SMB0STA==0x48){//从机地址+R发送成功,但接收到从机NACK应答信号
		retry_time++;
		STO=1;
		if(retry_time<RETRY_MAX){//若小于重试最大限值,则继续重试
			STA=1;
		}
		else{
			mes_M.isbuserror=1;//若大于重试最大限值,则停止总线,且将总线错误标志位置1
			retry_time=0;
			}
		goto bus_end;
	}
	if(SMB0STA==0x50){//接收到从机所发送的数据,并发送ACK应答信号
		if(mes_M.len>1)
			AA=1;//下一接收数据不是最后数据,故AA置1,以应答从机
		else{
			if(mes_M.len==1)//下一数据为最后数据
				AA=0;
		}
		XBYTE[mes_M.self_adr]=SMB0DAT;
		mes_M.self_adr++;
		mes_M.target_adr.word++;
		mes_M.len--;
		goto bus_end;
	}
	if(SMB0STA==0x58){//接收到从机所发送的最后一个数据,并发送NACK应答信号
		XBYTE[mes_M.self_adr]=SMB0DAT;
		mes_M.len--;
		STO=1;//结束总线传输
		goto bus_end;
	}
	if(SMB0STA==0x60){//在从机模式下,自身地址+W被寻址,并发送ACK应答
	//此时该节点为从机接收器
		mes_S.adr_end=0;//被寻址后,首先接收的是内存地址
		AA=1;
		goto bus_end;
	}
	if(SMB0STA==0x68){//在主机模式下发送从机地址+R/W时仲裁竞争失败,且被另一主机寻址
	//此时该节点为从机接收器
		mes_S.adr_end=0;
		AA=1;
		goto bus_end;
		/*mes_M.len,没有被改写,所以在主程序中检查到总线BUSY=0,且mes_M.len大于0,则发送起始位,将该节点变为主机*/
	}
	if(SMB0STA==0x70){//在从机模式下,被广播寻址,并发送ACK应答
	//此时该节点为从机接收器
		mes_S.adr_end=0;
		AA=1;
		goto bus_end;
	}
	if(SMB0STA==0x78){//在主机模式下发送从机地址+R/W时仲裁竞争失败,且被广播寻址
	//此时该节点为从机接收器
		mes_S.adr_end=0;
		AA=1;
		goto bus_end;
		/*处理方式与SMB0STA==0x68相同*/
	}
	if(SMB0STA==0x80){//在从机模式下,成功接收主机发送的数据,并发送ACK应答
		if(mes_S.adr_end==0){//接收的数据为内存高地址
			mes_S.target_adr.split.hi=SMB0DAT;
			mes_S.adr_end=1;
			AA=1;
			goto bus_end;
			}
		if(mes_S.adr_end==1){//接收的数据为内存低地址
			mes_S.target_adr.split.low=SMB0DAT;
			mes_S.adr_end=2;
			mes_S.self_adr=mes_S.target_adr.word;
			AA=1;
			goto bus_end;
			}
		if(mes_S.adr_end>=2){//接收数据
			XBYTE[mes_S.self_adr]=SMB0DAT;
			mes_S.len--;
			mes_S.self_adr++;
			/*判断是否为最后一个数据,一般来说从机mes_S.len可为0x7f,则数据长度由主机决定,否则数据长度由mes_S.len和对方主机mes_M.len的最小值决定*/
			if(mes_S.len>1)
				AA=1;
			else
				AA=0;
			//////////////////////////////////////////
			///////////////////////////////////////
			//需要调试
			goto bus_end;
		}
	}
	if(SMB0STA==0x88){//在从机模式下,成功接收主机发送的数据,且发送NACK应答
		XBYTE[mes_S.self_adr]=SMB0DAT;
		mes_S.len=0;
		STO=1;
		goto bus_end;
	}
	if(SMB0STA==0x90){//在从机模式下,成功接收广播主机发送的数据,并发送ACK应答
		if(mes_S.adr_end==0){//接收的数据为内存高地址
			mes_S.target_adr.split.hi=SMB0DAT;
			mes_S.adr_end=1;
			AA=1;
			goto bus_end;
			}
		if(mes_S.adr_end==1){//接收的数据为内存低地址
			mes_S.target_adr.split.low=SMB0DAT;
			mes_S.adr_end=2;
			mes_S.self_adr=mes_S.target_adr.word;
			AA=1;
			goto bus_end;
			}
		if(mes_S.adr_end>=2){//接收数据
			XBYTE[mes_S.self_adr]=SMB0DAT;
			mes_S.len--;
			mes_S.self_adr++;
			if(mes_S.len>1)
				AA=1;
			else
				AA=0;
			goto bus_end;
		}
	}
	if(SMB0STA==0x98){//在从机模式下,成功接收广播主机发送的数据,且发送NACK应答
		XBYTE[mes_S.self_adr]=SMB0DAT;
		STO=1;
		goto bus_end;
	}
	if(SMB0STA==0xa0){//在从机模式下,接收到停止位或重复起始位
		mes_S.len=0x7f;//以便在下一次被主机寻址时按主机长度发送或接收
		goto bus_end;
	}
	if(SMB0STA==0xa8){//在从机模式下,自身地址+R被寻址,并发送ACK应答
		SMB0DAT=XBYTE[mes_S.self_adr];
		mes_S.self_adr++;
		goto bus_end;
	}
	if(SMB0STA==0xb0){//在主机模式下发送从机地址+R/W时仲裁竞争失败,且被另一主机寻址
	//此时该节点为从机发送器
		SMB0DAT=XBYTE[mes_S.self_adr];
		mes_S.self_adr++;
		goto bus_end;
	}
	if(SMB0STA==0xb8){//在从机模式下,成功发送数据,并接收到主机ACK应答
		SMB0DAT=XBYTE[mes_S.self_adr];
		mes_S.self_adr++;
		goto bus_end;
	}
	if(SMB0STA==0xc0){//在从机模式下,成功发送数据,并接收到主机NACK应答
				
		goto bus_end;//数据发送完毕,等待停止位
	}
	if(SMB0STA==0x00){//出现总线错误
				
		mes_M.isbuserror=1;
		mes_S.isbuserror=1;
		STO=1;
	}
bus_end:
		SI=0;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -