软件模拟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_HIGHU8 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置1else ;   //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置1else ; //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 highdelay_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();//ʱÖÓÊä³ö0iic_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();elseiic_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();//·¢ËÍnACKelseIIC_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会白跑一个bits_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会白跑一个bits_soft_iic.step = STEP_IIC_START;// g_soft_iic.flag_operate = IIC_NO_OPERAT;定时器关闭了,不用重新重置flag,而且判断iic error时候可以能会用的到这个flagOFF_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();
}

软件模拟IIC主从机相关推荐

  1. STM32基于软件模拟IIC进行AHT21B温湿度采集

    STM32基于软件模拟IIC进行AHT21B温湿度采集 一. IIC简介 1.1 IIC简介 1.2 实现方式 1.3 实现方式对比 二. AHT21B简介 2.1 简介 2.2 产品特点 2.3 外 ...

  2. STM32 Cube MX 之hal库软件模拟IIC 可直接移植使用

    此为软件模拟IIC,可以直接移植到HAL库使用..h文件需要自己做函数声明这里就不再放出,如有问题大家可以讨论. 使用的时候只需要更改SDA 和SCL引脚的宏定义就可以移植使用,当然IIC协议其实就是 ...

  3. STM32F103单片机软件模拟IIC并读取TMP112数字温度传感器

    本文利用STM32F103系列单片机读取TMP112数字温度传感器的温度信息,TMP112数字传感器采用IIC总线协议通信.STM32自身含有硬件IIC资源,分别是PB6-->SCL.PB7-- ...

  4. STM32 软件模拟IIC

    0.系列目录 STM32 软件模拟IIC STM32 使用DMP库处理MPU6050数据 STM32 MPU6050与匿名上位机通讯(V2.6版) 1.IIC通讯过程 SCL和SDA在空闲时候均为高电 ...

  5. STM32 软件模拟 IIC 代码,标准库、HAL库可用

    1 #ifndef _IIC_H 2 #define _IIC_H 3 4 #include "stdio.h" 5 #include "stm32f1xx_hal.h& ...

  6. 基于FPGA的iic主从机驱动实现 Verilog实现iic slave和iic master

    基于FPGA的iic主从机驱动实现 Verilog实现iic slave和iic master 顶层模块实现master对slave自定义的寄存器读取 带仿真模块 ID:153069768739840 ...

  7. IO口软件模拟IIC

    一.IIC时序 IIC(Inter-Integrated Circuit, 内部集成电路)总线是飞利浦公司开发的两线式串行总线,用于短距离传输,常用语微控制器及其外围设备.它是由数据线SDA和时钟线S ...

  8. 使用51单片机模拟IIC从机,实现主机(51单片机)对模拟从机的读写操作

    51单片机.IIC从机模拟.IIC协议.iiC读写 1.思路 1.1写数据 1.2读数据 2.从机IIC协议 2.1起始信号和结束信号 2.2从机读取和写数据 2.3从机发送和接收ACK 2.4判断主 ...

  9. 【蓝桥杯嵌入式】【STM32】5_IICEEPROM之软件模拟IIC

    文章目录 1.原理图 2.源代码   下载工程文件:   https://gitee.com/Joseph_Cooper/blue-bridge-embedded 1.原理图 顺便指出了该器件的地址. ...

最新文章

  1. appcontroller.php,php用什么开发app接口
  2. 信息化建设规划_苏交集团施工企业信息化建设规划分享会顺利召开
  3. abap al设置单元格可编辑 oo_excel表格操作: 图形和图表编辑技巧汇总(二)
  4. Keras-11 GAN MNIST
  5. oracle设置保存时间为12或是24小时格式
  6. 【算法基础三】算法如何入门?零基础入门算法应该学些什么?
  7. 45% 72% 100sRGB 色域
  8. 直系同源基因分析(orthofinder方法)
  9. 教你如何鉴别iPhone翻新机!
  10. Docer容器的介绍(一)-------Docker基本概念和框架
  11. 金额数字转换(小写转大写)
  12. 08s01 mysql_mysql报错 code:08S01,msg:SQLSTATE
  13. 开箱即用,Hexo博客的github+server自动部署
  14. Android Hal层回调APP应用接口
  15. TIPOP 出货单单头
  16. chatbot使用_如何使用Python构建Chatbot项目
  17. Windows10系统下从库中删除文件夹(保留原始位置的文件)
  18. Android即时通讯--仿QQ即时聊天:(一)初识Socket
  19. 2019.06.17
  20. 微信公众号文章爬取下载各种格式

热门文章

  1. 如何一步步从数据产品菜鸟走到骨干数据产品
  2. 电脑网页打不开html文件,网络连接上打不开网页 电脑可以连上网,但无法打开网页?...
  3. 解决网页上的文字不能复制
  4. confluence wiki如何部署
  5. Oracle JDK和 OpenJDK 之间的区别
  6. 同创永益亮相“中国电子云·数聚未来峰会”,共助金融数字化转型
  7. cocos2d html5 mysql_cocos2d-html5基础知识
  8. 定义一个账 户类,可以创建账户、存款、取款 、查询余额、以及销户等操作
  9. 以下系统不属于计算机软件,微型计算机的微处理器芯片上集成了(以下不属于计算机软件系统的是...
  10. 利用html-minifier和uglify-js对前端HTML/CSS/JS文件进行压缩