😂oi退役选手,Java、大数据、单片机、IoT均有所涉猎,热爱技术,技术无罪
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
获取源码,添加WX
目录
- 前言
- 一、I2C总线的概念
- [二、 I2C的协议层](#二、 I2C的协议层)
- [三、STM32 I2C实战](#三、STM32 I2C实战)
- 最后
前言
首先明确一个概念,关于MCU中通信总线和通信协议,通信总线是一种用于连接各种外设和模块的物理接口,它可以传输数据和控制信息。通信协议则是指在通信总线上传输数据时所遵循的规则和约定,以确保不同设备之间能够正确地交换信息,我们也可以把他叫做通信总线协议。
系列文章,主要讲解以下几个总线协议,读者可以按需选择:
- UART和USART
- RS232、RS485总线
- IIC总线
- SPI总线
- CAN总线
- USB总线
一、I2C总线的概念
其实站在我个人开发的角度来说,I2C使用的比SPI多,他的主要概念如下:
I2C(Inter-Integrated Circuit)是一种串行通信协议,用于连接微控制器和外部设备,例如传感器、存储器芯片、显示屏等。
特点:
- I2C是一个支持设备的总线,多个设备共用的信号线,他支持多个主机或者多个从机
- 一个I2C总线只使用两条总线线路,一条双向串行数据线(SDA),一条串行时钟线(SCL)。数据线用来表示数据,时钟线用于同步数据的收发。
- I2C总线上的从机设备,都有一个单独的地址,主机通过这个地址来实现对不同设备的访问
- 总线通过上拉电阻接到电源。当I2C设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
- I2C还有一个仲裁模式,即当多个从机都传输数据的时候,为了防止数据冲突,会产生仲裁,决定哪个设备来使用总线
- 具有3种传输模式:标准模式传输速率为100kbps,快速模式为400kbps,高速模式可达3.4Mbps,但目前大多I2C设备尚不支持高速模式。
- 连接到相同总线的IC数量受到总线的最大电容400p F限制。
二、 I2C的协议层
I2C协议层规定了在I2C通信中需要遵循的一些基本规则和标准,其中包括以下内容:
- 物理层规定 :
- 定义了I2C总线的物理结构、传输介质(通常是双绞线)、电气特性(如电压电平、上拉电阻)等。
- 规定了起始条件(Start condition)和停止条件(Stop condition)的时序和电平要求。
通信的起始和停止:
起始(S)和停止(P)信号是两种特殊的状态,SCL线是高电平时SDA线从高电平向低电平切换,表示通信的起始。当SCL是高电平时SDA线由低电平向高电平切换,表示通信的停止。
- 数据链路层规定 :
- 定义了数据帧的格式,包括地址帧和数据帧的组成结构。
- 规定了主设备和从设备之间的地址识别机制,包括7位或10位地址模式的规定。
I2C总线上的每个设备都有自己的独立地址,主机发起通信时,通过SDA信号线发送设备地址(SLAVE_ADDRESS)来查找从机。I2C协议规定设备地址可以是7位或10位,实际中7位的地址应用比较广泛 。
- 时序规定 :
- 规定了时钟脉冲的频率、数据传输的时序要求,以确保通信的稳定性和可靠性。
- 定义了如何进行数据的读取和写入,包括数据的传输顺序和速率的限制。
主机写数据到从机
主机读数据在从机
复和形式
读和写数据除了基本的读写,I2C通信更常用的是复合格式,该传输过程有两次起始信号(S)。一般在第1次传输中,主机通过SLAVE_ADDRESS寻找到从设备后,发送一段"数据",这段数据通常用于表示从设备内部的寄存器或存储器地址(注意区分它与SLAVE_ADDRESS的区别);在第2次的传输中,对该地址的内容进行读或写。也就是说,第1次通信是告诉从机读写地址,第2次则是读写的实际内容。
- 错误处理规则 :
- 规定了在通信过程中出现错误时的处理方式,例如发生冲突、丢失数据等情况应当如何处理。
SDA数据线在SCL的每个时钟周期传输一位数据。传输时,SCL为高电平的时候SDA表示的数据有效 ,即此时的SDA为高电平时表示数据"1",为低电平时表示数据"0"。当SCL为低电平时,SDA的数据无效,一般在这个时候SDA进行电平切换,为下一次表示数据做好准备。
-
多点连接规定:
- 规定了多个从设备共享同一条总线时的地址分配和冲突解决机制。
-
高级功能规定:
- 例如10位地址模式、扩展寄存器等高级功能的使用规范。
有了这些规定,每个厂家生产出来支持I2C的东西也可实现互通
三、STM32 I2C实战
c
#include "I2C.h"
#define SCL_H GPIOB->BSRR = GPIO_Pin_8
#define SCL_L GPIOB->BRR = GPIO_Pin_8
#define SDA_H GPIOB->BSRR = GPIO_Pin_9
#define SDA_L GPIOB->BRR = GPIO_Pin_9
#define SCL_read GPIOB->IDR & GPIO_Pin_8
#define SDA_read GPIOB->IDR & GPIO_Pin_9
static void I2C_delay(void)
{
volatile int i = 7;
while (i)
i--;
}
static bool I2C_Start(void)
{
SDA_H;
SCL_H;
I2C_delay();
if (!SDA_read)
return false;
SDA_L;
I2C_delay();
if (SDA_read)
return false;
SDA_L;
I2C_delay();
return true;
}
static void I2C_Stop(void)
{
SCL_L;
I2C_delay();
SDA_L;
I2C_delay();
SCL_H;
I2C_delay();
SDA_H;
I2C_delay();
}
static void I2C_Ack(void)
{
SCL_L;
I2C_delay();
SDA_L;
I2C_delay();
SCL_H;
I2C_delay();
SCL_L;
I2C_delay();
}
static void I2C_NoAck(void)
{
SCL_L;
I2C_delay();
SDA_H;
I2C_delay();
SCL_H;
I2C_delay();
SCL_L;
I2C_delay();
}
static bool I2C_WaitAck(void)
{
SCL_L;
I2C_delay();
SDA_H;
I2C_delay();
SCL_H;
I2C_delay();
if (SDA_read) {
SCL_L;
return false;
}
SCL_L;
return true;
}
static void I2C_SendByte(uint8_t byte)
{
uint8_t i = 8;
while (i--) {
SCL_L;
I2C_delay();
if (byte & 0x80)
SDA_H;
else
SDA_L;
byte <<= 1;
I2C_delay();
SCL_H;
I2C_delay();
}
SCL_L;
}
static uint8_t I2C_ReceiveByte(void)
{
uint8_t i = 8;
uint8_t byte = 0;
SDA_H;
while (i--) {
byte <<= 1;
SCL_L;
I2C_delay();
SCL_H;
I2C_delay();
if (SDA_read) {
byte |= 0x01;
}
}
SCL_L;
return byte;
}
void i2cInit(void)
{
GPIO_InitTypeDef gpio;
//已更改
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);
gpio.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
gpio.GPIO_Speed = GPIO_Speed_2MHz;
gpio.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(GPIOB, &gpio);
}
bool i2cWriteBuffer(uint8_t addr, uint8_t reg, uint8_t len, uint8_t * data)
{
int i;
if (!I2C_Start())
return false;
I2C_SendByte(addr << 1 | I2C_Direction_Transmitter);
if (!I2C_WaitAck()) {
I2C_Stop();
return false;
}
I2C_SendByte(reg);
I2C_WaitAck();
for (i = 0; i < len; i++) {
I2C_SendByte(data[i]);
if (!I2C_WaitAck()) {
I2C_Stop();
return false;
}
}
I2C_Stop();
return true;
}
/
int8_t i2cwrite(uint8_t addr, uint8_t reg, uint8_t len, uint8_t * data)
{
if(i2cWriteBuffer(addr,reg,len,data))
{
return TRUE;
}
else
{
return FALSE;
}
//return FALSE;
}
int8_t i2cread(uint8_t addr, uint8_t reg, uint8_t len, uint8_t *buf)
{
if(i2cRead(addr,reg,len,buf))
{
return TRUE;
}
else
{
return FALSE;
}
//return FALSE;
}
//
bool i2cWrite(uint8_t addr, uint8_t reg, uint8_t data)
{
if (!I2C_Start())
return false;
I2C_SendByte(addr << 1 | I2C_Direction_Transmitter);
if (!I2C_WaitAck()) {
I2C_Stop();
return false;
}
I2C_SendByte(reg);
I2C_WaitAck();
I2C_SendByte(data);
I2C_WaitAck();
I2C_Stop();
return true;
}
bool i2cRead(uint8_t addr, uint8_t reg, uint8_t len, uint8_t *buf)
{
if (!I2C_Start())
return false;
I2C_SendByte(addr << 1 | I2C_Direction_Transmitter);
if (!I2C_WaitAck()) {
I2C_Stop();
return false;
}
I2C_SendByte(reg);
I2C_WaitAck();
I2C_Start();
I2C_SendByte(addr << 1 | I2C_Direction_Receiver);
I2C_WaitAck();
while (len) {
*buf = I2C_ReceiveByte();
if (len == 1)
I2C_NoAck();
else
I2C_Ack();
buf++;
len--;
}
I2C_Stop();
return true;
}
uint16_t i2cGetErrorCounter(void)
{
// TODO maybe fix this, but since this is test code, doesn't matter.
return 0;
}
最后
如果本文对你有所帮助,还请三连支持一下博主!