从0开始点亮OLED屏幕(一)IIC时序篇

本文详细介绍了IIC通信协议,包括其工作原理、信号线构成(SCL和SDA)、传输速率、起始和停止信号的时序以及在单片机如51和STM32中的具体实现方法,重点讲解了如何发送和接收数据,以及数据的位移过程。
摘要由CSDN通过智能技术生成

 一、IIC(Inter-Integrated Circuit)介绍


    IIC(Inter-Integrated Circuit)是一种具有两线传输的串行通信总线,适用于数据量不大且传输距离短的场合。
    IIC串行总线由两根信号线组成,一根是双向的数据线SDA,另一根是单向的时钟线SCL,在空闲状态时,SDA和SCL线都置’1‘,为高电平。

        IIC为同步的半双工通信方式,常见的传输速率有:100kb/s、300kb/s、3.4Mkb/s。

二、传输协议

IIC由两根通信信号线组成,SCL是由主模块输入的时钟信号,是单向的信号,而SDA是由主机或从机控制的数据信号,是双向信号。
    在空闲状态下,SCL及SDA都是置高的状态,当需要进行一次IIC传输时,由START信号指示当前数据传输开始,由STOP信号指示当前的数据传输结束。

START信号的标识是在SCL高电平情况下,SDA信号由高变低 ,即视为START开始;

STOP信号标识是在SCL高电平情况下,SDA信号由低变高,即视为STOP结束;

 起始和结束时序图如下: 

 用到的IIC引脚定义:

其中SCL接到了PA11,SDA接到了PA12;

//-----------------OLED端口定义---------------- 
#define   GPIO_SCL_PORT			GPIOA
#define   GPIO_SCL_PIN			GPIO_Pin_11                    
#define   GPIO_SCL_ENABLE()     do{ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); }while(0)   /* 所在IO口时钟使能 */

#define   GPIO_SDA_PORT			GPIOA
#define   GPIO_SDA_PIN			GPIO_Pin_12
#define   GPIO_SDA_ENABLE()     do{ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); }while(0)   /* 所在IO口时钟使能 */

#define   OLED_SCL(x)			GPIO_SCL_PORT->BSRR = GPIO_SCL_PIN << (16*(!x))                 
#define   OLED_SDA(x)			GPIO_SDA_PORT->BSRR = GPIO_SDA_PIN << (16*(!x)) 

1.IIC_STAR

//起始信号,SCL高电平期间拉低SDA
void I2C_Start(void)
{
    SDA_OUT();//将SDA口配置成输出
	OLED_SDA(1);
	OLED_SCL(1);
	IIC_delay();
	OLED_SDA(0);
	IIC_delay();
	OLED_SCL(0);
	IIC_delay();
}

2.IIC_STOP

 结束信号,SCL高电平情况下,SDA信号由低变高。故在拉高SCL之前将SDA拉低;

//结束信号,SCL高电平情况下,SDA信号由低变高
void I2C_Stop(void)
{
    SDA_OUT();//将SDA口配置成输出
	OLED_SDA(0);
	OLED_SCL(1);
	IIC_delay();
	OLED_SDA(1);
}

3.等待信号响应 :Wait_Ack

        发送完一个字节后,主机在下一个时钟接收一位数据,判断从机是否应答。0为应答,1为非应答;其时序就是接收时序,只不过是接收一位数据,而接收数据是八位数据。

将其配置为输入模式(51单片机中是释放SDA,即将SDA数据线置1),拉高SCL读取一位数据,再拉低进行后续操作。

//等待信号响应
void I2C_WaitAck(void) //测数据信号的电平
{
	SDA_IN();//SDA设置为输入
	IIC_delay();
	OLED_SCL(1);
	IIC_delay();
	OLED_SCL(0);
	IIC_delay();
}

4.发送应答位

       接收完一个字节后,主机在下一个时钟发送一位数据。0为应答,表示不需要从机交出控制权,即继续接收,1为非应答,表示已经结束,从机交出控制权;

//写入一个字节
void Send_Ack(u8 ack)
{
	u8 i;
	OLED_SCL(0);
    SDA_OUT();//将SDA口配置成输出
    if(ack)
		OLED_SDA(1);
    else
        OLED_SDA(0);
	IIC_delay();
	OLED_SCL(1);
	IIC_delay();
	OLED_SCL(0);
}

 

5.发送一个字节

SCL低电平期间,主机将数据依次放到SDA上(高位在前),再拉高SCL,从机在SCL高电平期间读取SDA。(SCL高电平期间SDA不能有数据变化)循环八次发送一个字节。最后等待ACK结束;

                   

//写入一个字节
void Send_Byte(u8 dat)
{
	u8 i;
	OLED_SCL(0);
    SDA_OUT();//将SDA口配置成输出
	for(i=0;i<8;i++)
	{
		OLED_SDA((BitAction)(dat&(0x80>>i)));
		IIC_delay();
		OLED_SCL(1);
		IIC_delay();
		OLED_SCL(0);
	}
}

由于是高位写入,故将要写入的dat高位写入,与上0x80取出高位。当然这样还不够,应该强制转换成BitAction类型数据,否则会通信失败。我正是一直通信不上才发现是这里的问题。利用for循环循环八次依次写入高位。

6.接收一个字节数据

这里我就不在附上时序图了,至于为什么呢?其实接收与发送的时序大差不差,你听我给你描述一下:

        接收一个字节:SCL低电平期间,从机将数据依次放到SDA上(高位在前),再拉高SCL,主机在SCL高电平期间读取SDA。(SCL高电平期间SDA不能有数据变化)循环八次接收一个字节。

没错,描述几乎一模一样!只是操作对象从机换成了主机,主机换成了从机!也就是谁发谁操作数据线。


        这里拓展一下:

在51单片机做法是从机发主机得先释放SDA数据线(也就是将SDA置1)!只有这样从机才能相应控制SDA数据线。

在32中就不用这么做了,可以用寄存器将SDA配置成输入模式,但相应的前面主机操作都应该加上将SDA配置成输出模式;(也就是在start,stop,发送字节,发送应答都配置为输出!加上SDA_OUT()语句,至于如何利用寄存器去配置可以参考我的STM32通过寄存器来控制GPIO

//IO方向设置
#define SDA_IN()    {GPIOA->CRH &= 0XFFF0FFFF;GPIOA->CRH |= (u32)8<<16;}
#define SDA_OUT()   {GPIOA->CRH &= 0XFFF0FFFF;GPIOA->CRH |= (u32)3<<16;}
#define READ_SDA()  (BitAction)(GPIOA->IDR & GPIO_Pin_12)

当然,oled没有用到读函数,这里顺带将IIC时序都介绍一下。 

 uint8_t Receive_Byte(void)
{
	unsigned char i,dat=0;
	SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        OLED_SCL(0); 
        IIC_delay();
		OLED_SCL(1); 
        dat<<=1;
        if(READ_SDA)
            dat |= 1;   
		IIC_delay();
    }					   
    return dat;
}

首先将SDA配置为输入模式,将SCL拉低接收数据改变,再将SCL拉高读取数据,如果为高电平,dat加1;向高位移动一位,这样运行七次刚好将数据移到最高位!这里的七次并未算第一次,因为第一次位移并不影响第一次的接收,0怎么位移结果还是0;如果放到后面就相当于多位移了一次,数据从第0位移到第八位只需要七次位移就够了。

  • 21
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值