大家好,欢迎来到IT知识分享网。
一、IIC(Inter-Integrated Circuit)介绍
IIC为同步的半双工通信方式,常见的传输速率有:100kb/s、300kb/s、3.4Mkb/s。
二、传输协议
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位移到第八位只需要七次位移就够了。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/119744.html

