📄 smbus_app_s.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 + -