IIC 时序分析,stm32软件模拟驱动编写

这几天一直在学习iic从理论到实操,尝试这自己写iic的stm32驱动程序,今天终于成功的实现使用iic协议发送数据和读数据。驱动程序涉及stm32的位带操作,关于位带操作我写在上一篇博客:stm32位带操作。整个工程托管在github:点击打开链接

一、要点

1.iic总线只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL,所有操作都靠这两根线完成。

2.iic总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。参考图1可以看出SDA高低电平的变化都是在SCL低电平期间。

图一

二、时序图详解

        要写的驱动程序包括起始信号、终止信号、主机读取数据的应答信号、主机读取最后一个数据的非应答信号、数据发送、数据读取。

        1.起动和停止驱动程序

        SCL信号线为高电平期间,SDA线由高电平向低电平变化表示起始信号,如图所示:

图二

/*
函数名:iic_start
功  能:启动iic,启动方式,在SCL高电平期间将SDA由高置低
参  数:无
返回值:无
*/
void iic_start(void)
{
	IIC_SDA_OUT();//设置SDA口为输出
	SDA_PIN_OUT=1;//因为启动要求从高到低变化所以我们先把SDA置高
	delay_us(5);  //
	SCL_PIN_OUT=1;//SDA要在SCL高电平期间变化,由图中阴影部分可以看出
	delay_us(5); 
	SDA_PIN_OUT=0;//SDA置低
	delay_us(5);
}
/*
函数名:iic_stop
功  能:停止传输数据,实现方式在SCL高电平期间将SDA由低置高
参  数:无
返回值:无
*/
void iic_stop(void)
{
	IIC_SDA_OUT();//因为stm32IO有个输出方向问题,所以要先设置IO口输出方向,本代码采用位带操作	
	SDA_PIN_OUT=0;//终止信号要求SDA在SCL高电平期间由低电平置高电平,因此先把SDA拉低
	delay_us(5);
	SCL_PIN_OUT=1;//SCL置高电平
	delay_us(5);
	SDA_PIN_OUT=1;//拉高SDA实现终止信号
	delay_us(5);
}

2.等待从机应答驱动代码

     数据传送格式:每一个字节必须保证时8位长度。数据传送时,先传高位,每个被传送的字节后面必须跟随一位应答位,在主机向从机传送数据时应答位由从机产生,主机检测从机产生的应答信号,如果从机应答时间超时,主机默认数据传输完成,本段代码就是检测从机的应答信号。

图三

/*
函数名:iic_ack
功  能:接收从机应答信号,释放掉总线读取SDA置看是否有负脉冲,
        当一段时间无应答默认接收完毕
参  数:无
返回值:无
*/
u8 iic_wait_ack(void)
{
	u8 i=0;
	IIC_SDA_IN();              //SDA设置为输入
        SDA_PIN_OUT=1;delay_us(5); //SCL和SDA置高同时释放总线,留给从机产生应答信号。
	SCL_PIN_OUT=1;delay_us(5);
	while(SDA_PIN_IN)          //检测从机是否产生负脉冲,或者从机超时,主机默认从机成功接收数据
	{
		i++;
		if(i>250) 
		{		
            iic_stop();			//如果i>255则产生非应答信号,iic停止
			return 1;
		}
		
	}
	SCL_PIN_OUT=0;//时钟输出0 
	return 0;
}

3.主机读数据产生的应答信号或非应答信号

         主机读数据时有这样的规定:当主机每读一个数据主机都要回复应答信号。当最后一个字节数据读完后,主机要返回以“非应答”,并终止信号读出操作,这里的应答信号和非应答信号作用是给从机判断是否继续读取数据。由图可以看出应答信号是在SDA低电平期间,SCL由低置高再置低。非应答信号是在SDA位高电平期间SCL由低置高再置低。

图4

/*
函数名:iic_ask
功  能:产生ask应答
参  数:无
返  回:无
*/
void iic_ack(void)
{
	IIC_SDA_OUT();
	SCL_PIN_OUT=0;
	delay_us(5);
	SDA_PIN_OUT=0;//产生一正个脉冲
	delay_us(5);  //
	SCL_PIN_OUT=1;//
	delay_us(5);
	SCL_PIN_OUT=0;
}
/*
函数名:iic_nask
功  能:产生非应答信号
参  数:无
返  回:无
*/
void iic_nack(void)
{
 IIC_SDA_OUT();
 SCL_PIN_OUT=0;
 delay_us(5);
 SDA_PIN_OUT=1;
 delay_us(5);
 SCL_PIN_OUT=1;
 delay_us(5);
 SCL_PIN_OUT=0;
}

4.字节传送
字节传送分为发送和读取,主要根据图三的时序图进行编写,代码如下:

/*
函数名:iic_bit_write
功  能:传送一个字节
参  数:u8
返回值:无
*/
void iic_byte_write(u8 buf)
{
    u8 i;
	IIC_SDA_OUT();
	for(i=1;i<=8;i++)
	{
		SCL_PIN_OUT=0;       
		delay_us(5);
		SDA_PIN_OUT=(buf>>(8-i))&0x01;//先传高位再传地位
		delay_us(5);
		SCL_PIN_OUT=1;
		delay_us(5);	
	}
	SCL_PIN_OUT=0;
}
/*
函数名:iic_bit_read
功  能:主机读取一个字节,ask参数主要用来发送信号给从机判断主机还需不需要继续读取字节,如果主机发送应答信号表示继续读取,反之则说明已经读完最后一个字节。
参  数:ask
返回值:
*/
u8 iic_byte_read(unsigned char ask)
{
	unsigned char  i,buf=0;
	IIC_SDA_IN();
	for(i=0;i<8;i++)
	{
	   SCL_PIN_OUT=0;
	   delay_us(5);
	   SCL_PIN_OUT=1;
	   buf <<=1;
	   if(SDA_PIN_IN)
		   buf++;
	    delay_us(5);
	}
	if(!ask)
		iic_nack();
	else
		iic_ack();
	return buf;
}

5.综合调试

(1)写操作:在main()函数中实现写一个字节到从机中,然后在都出来用串口打印。从机使用eeprom AT24c系列,单片机在写操作时,应该先发送器件地址,入下图所示,其中器件地址为7位第8位为数据传送方向设置,途中非影音部分代表从机向主机传送数据的过程,A表应答的意思,S为启动信号,传送数据时,单片机首先发送一个字节的被写入器件的存储区首地址,收到存储器器件的应答后,单片机就逐个发送数据字节,但每个字节都要等待应答。当要写入的数据传送完成后,单片机应发送终止信号以结束写入操作。

图5

(2)读操作

单片机先发送该器件的7位地址码和写方向位“0”(“伪写”),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作

图6

int main(void)
{
	u8 iic_buf=0;
	 USART1_Config();
     delay_init();
	 iic_init();
	 
	
	 iic_start();
	 iic_byte_write(0xa0);//写器件地址
	 iic_wait_ack();
	 iic_byte_write(3);   //写存储器地址
	 iic_wait_ack();
	 iic_byte_write('a'); //写存储的数据
	 iic_wait_ack();
	 iic_stop();          //停止总线
	 delay_ms(10);
	
	 iic_start();          //读刚才写的数据
	 iic_byte_write(0xa0); //先写器件地址
	 iic_wait_ack();
	 iic_byte_write(3);    //在写要读取的存储器地址
	 iic_wait_ack();
	
	 iic_start();          //重启一次iic
	 iic_byte_write(0xa1); //把传递方向设为读取,前7位为器件地址,最后一位为判断传递方向
	 iic_wait_ack();
	 iic_buf=iic_byte_read(0);//读取数据
	 iic_stop();
	
  	 while(1)
	 {
		 printf("%c",iic_buf); //这里利用stm32串口直接打印 
		 delay_ms(500);
	 };
}
          整个工程亲测可用。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。






  • 6
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shenhuaifeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值