其实在上学期就用了IIC通信写OLED屏幕,一直没有写,今天有时间写出来进行记录。
(PS:图片内容来自于野火教程)
一.物理层
它的物理层有如下特点:
(1) 它是一个支持设备的总线。“总线”指多个设备共用的信号线。在一个 I2C 通讯总线中,可连接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机。
(2) 一个 I2C 总线只使用两条总线线路,一条双向串行数据线 (SDA) ,一条串行时钟线 (SCL)。数据线即用来表示数据,时钟线用于数据收发同步。
(3) 每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。
(4) 总线通过上拉电阻接到电源。当 I2C 设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
(5) 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。
(6) 具有三种传输模式:标准模式传输速率为 100kbit/s ,快速模式为 400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多 I2C 设备尚不支持高速模式。
(7) 连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制。
二.数据层
IIC的数据层分读数据和写数据,在读写之前,也要了解它的起始信号以及如何保证数据的有效性。
1.起始和结束
起始信号:时钟线高电平的时候,数据线处于下降沿
结束信号:时钟线高电平的时候,数据线处于上升沿
2.数据有效性
I2C 使用 SDA 信号线来传输数据,使用 SCL 信号线进行数据同步。SDA 数据线在SCL 的每个时钟周期传输一位数据。传输时,SCL 为高电平的时候 SDA 表示的数据有效,即此时的 SDA 为高电平时表示数据“1”,为低电平时表示数据“0”。当 SCL 为低电平时,SDA 的数据无效,一般在这个时候 SDA 进行电平切换,为下一次表示数据做好准备。
三.响应
I2C 的数据和地址传输都带响应。响应包括“应答 (ACK)”和“非应答 (NACK)”两种信号。作为数据接收端时,当设备 (无论主从机) 接收到 I2C 传输的一字节数据或地址后,若希望对方继续发送数据,则需要向对方发送“应答 (ACK)”信号,发送方会继续发送下一个数据;若接收端希望结束数据传输,则向对方发送“非应答 (NACK)”信号,发送方接收到该信号后会产生一个停止信号,结束信号传输。传输时主机产生时钟,在第 9 个时钟时,数据发送端会释放 SDA 的控制权,由数据接收端控制SDA,若 SDA 为高电平,表示非应答信号 (NACK),低电平表示应答信号 (ACK)。
四.代码
#include "IIC.h"void delay_us(uint32_t time)
{ uint8_t i=0; while(time--){i=10; //自己定义while(i--) ; }
}void IIC_Start(void) //起始信号
{SDA_SET;SCL_SET;delay_us(2);SDA_RESET;delay_us(2);SCL_RESET;delay_us(2);
}void IIC_Stop(void) //终止信号
{SDA_RESET;delay_us(2);//eroorSCL_SET;delay_us(2);SDA_SET;delay_us(2);
}void IIC_Ack() //应答信号
{IIC1_SDA(GPIO_PIN_RESET);IIC1_SCL(GPIO_PIN_SET);IIC1_SCL (GPIO_PIN_RESET);IIC1_SCL(GPIO_PIN_SET);
}void IIC1_NAck() //菲应答信号
{SCL_RESET;delay_us(10);SDA_SET;delay_us(10);SCL_SET;delay_us(10);SCL_RESET;delay_us(10);
}u8 IIC_Wait_Ack()
{u8 count=0;IIC1_SDA(GPIO_PIN_SET);IIC1_SCL(GPIO_PIN_SET);while(IIC1_SDA_IS_HIGH()){count++;if(count==250){IIC_Stop();return 1;}}IIC1_SCL(GPIO_PIN_RESET);return 0;
}
//SCL高时传输数据会稳定,
void Write_IIC_Byte(u8 TX)
{u8 i=0;IIC1_SCL(GPIO_PIN_RESET);for(i=0;i<8;i++){if((TX&0x80)>0){IIC1_SDA(GPIO_PIN_SET); //SDA负责数据传输,}else{IIC1_SDA(GPIO_PIN_RESET);}TX<<=1;IIC1_SCL(GPIO_PIN_SET);//SCL高时传输数据会稳定,delay_us(10); //此处延时为了让数据做停留IIC1_SCL(GPIO_PIN_RESET);}
}u8 IIC_Read_Byte(u8 ack)
{u8 i=0,res=0;for(i=0;i<8;i++){IIC1_SCL(GPIO_PIN_SET);res<<=1;if(IIC1_SDA_IS_HIGH ()){res++;}IIC1_SCL(GPIO_PIN_RESET);}if(ack==0){IIC1_NAck();}else{IIC_Ack();}return res;
}
在写IIC通信时序的时候,如果出现无法通信的情况,可以用逻辑分析仪对波进行分析,然后修改时序。
在用cubemx进行配置时,可以选择硬件IIC和软件IIC,这里我比较推荐软件IIC,比较灵活,CUBEMX配置的时候,将IO口配置为输出模式并且上拉,源文件中进行适当修改,我把代码贴在下面
#ifndef __IIC_H
#define __IIC_H
#include "stm32f1xx.h"
//#include "stdint.h"typedef unsigned char u8;
#define IIC1_SCL(pin_status) HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, pin_status);
#define IIC1_SDA(pin_status) HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, pin_status);
#define IIC1_SCL_IS_HIGH() (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) != GPIO_PIN_RESET)
#define IIC1_SDA_IS_HIGH() (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) != GPIO_PIN_RESET)#define SCL_SET HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);
#define SCL_RESET HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);
#define SDA_SET HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET);
#define SDA_RESET HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);#include "stdio.h"
#include "stdint.h"void IIC_Start(void);
void IIC_Stop(void);
void IIC_Ack(void);
void IIC_NAck(void);
u8 IIC_Wait_Ack(void);
void Write_IIC_Byte(u8 TX);
u8 IIC_Read_Byte(u8 ack);
void E2PROM_Init(void);
void delay_us(uint32_t time);
#endif
这里我用的是PA6和PA7,大家可以自行修改。
IIC可以用来写OLED,代码太多我就不贴出来了,发个下载链接,大家自取。这个是用DS18B20测温度然后用OLED显示的,含有IIC通信
链接