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