STM32F103C8T6--I2C通信原理及代码

一、I2C基本电路结构

1.1.1串口通信的缺点

串口通信只能实现1对1的通信

2.1.1I2C电路总线的结构

从机有7位地址,理论上来说I2C主机可以控制2^7=128个从机

因为开漏模式适合多个从机连接,所以这里数据线(SDA)和时钟线(SCL)都是开漏输出模式

3.1.1时钟线(SCL)和数据线(SDA)

其中时钟线(SCL)是单向的,数据线(SDA)是双向的

4.1.1主机和从机的关系:与(&)

5.1.1主机如何向从机发送信号

从机时钟线(SCL)全部置1,主机时钟线(SCL)置0时,N_MOS管断开,从机时钟线(SCL)电压被拉低,输出低电压;主机时钟线(SCL)置1时,由于有上拉电阻的存在输出高电压

6.1.1主机如何发送数据

SDA发送数据时,从机SDA全部置1,主机SDA置0,N_MOS管断开,从机SDA电压被拉低,输出低电压;主机SDA置1时,由于有上拉电阻的存在输出高电压

7.1.1从机如何发送数据

举例:主机数据线(SDA)写1断开,除了从机1,其它从机全部写1断开,当从机数据线(SDA)写1时由于有上拉电阻的存在,输出高电压,写0 的时候输出低电压

二、I2C通信协议

1.1.1I2C数据帧格式

串口一帧只能发8~9个字,而I2C一帧可以发送多个字

2.1.1起始位和停止位

起始位:时钟线(SCL)高电压,数据线(SDA)低电压

停止位:时钟线(SCL)高电压,数据线(SDA)高电压

3.1.1寻址

R代表读=0,W代表写=1,#代表0;

ACK:从机对主机进行应答,将数据线(SDA)拉低

4.1.1传输数据

主机向从机发送数据,从机读取:主机发送数据,从机回复ACK

从机向主主发送数据,主机读取:从机发送数据,主机回复ACK

三、I2C模块的使用方法

1.1.1引脚分布

如果使用重映射则,REMAP=1,使用PB8,PB9作为引脚;如果不使用,REMAP=1,使用PB6,PB7作为引脚(这里我们用重映射)

2.1.1连接电路

3.1.1什么是波特率:每秒传输的位数

4.1.1波特率的选择

选择满足需求的最小频率

5.1.1什么是占空比:低电压比上高电压

注意区分这里的占空比和PWM波的占空比

PWM波的占空比:信号处于高电平状态的时间与整个周期时间的比率

6.1.1闭合I2C1总开关

代码部分(main.c)
#include "stm32f10x.h"

void My_I2c_Init(void);
int main(void)
{
	My_I2c_Init();
	while(1)
	{
		
 	}
	
}
void My_I2c_Init(void)
{
//#1.IO引脚初始化
	//对I2C进行重映射
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	
	GPIO_PinRemapConfig(GPIO_Remap_I2C1,ENABLE);
	
	//对PB8和PB9进行初始化
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	GPIO_InitTypeDef 	GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Pin= GPIO_Pin_8| GPIO_Pin_9 ;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_InitStruct.GPIO_Speed =GPIO_Speed_2MHz ;
	
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	
	//#2.初始化I2C1模块
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
	
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);//施加复位信号		
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,DISABLE);//释放复位信号
	
		I2C_InitTypeDef I2C_InitStruct;
		I2C_InitStruct.I2C_ClockSpeed = 400000;
		I2C_InitStruct.I2C_Mode =I2C_Mode_I2C;
		I2C_InitStruct.I2C_DutyCycle =I2C_DutyCycle_2;
		
		I2C_Init(I2C1,&I2C_InitStruct);
		
		I2C_Cmd(I2C1,ENABLE);//使能I2C
}

四、I2C写数据

1.1.1I2C内部结构框图

SR=status register(状态寄存器)

2.1.1数据发送过程

起始位:向START写1

停止位:向STOP写1

3.1.1等待总线空闲

通过BUSY标志位查询总线是否空闲

4.1.1发送起始位

通过SB标志位查询起始位是否发送完成

5.1.1发送地址

通过AF、ADDR标志位查询,是否收到ACK,是否寻址成功

对第8位地址进行清零(Addr&Oxfe)

完成之后

对ADDR进行清零:先读SR1,再读SR2,才能对ADDR标志位进行清零

6.1.1

先查询TXE和AF标志位,数据寄存器是否为空,数据是否被拒收;BTF标志位:移位数据寄存器是否为空,然后进行发送

7.1.1发送停止位

代码实例

#include "stm32f10x.h"

void My_I2c_Init(void);
int MY_SendBytes(I2C_TypeDef *I2Cx,uint8_t Addr,uint8_t *pData,uint16_t Size);
int main(void)
{
	My_I2c_Init();
	uint8_t commands[]={0x00,0x8d,0x14,0xaf,0xa5};
	MY_SendBytes(I2C1,0x78,commands,5);
	while(1)
	{
		
 	}
}

void My_I2c_Init(void)
{
//#1.IO引脚初始化
	//对I2C进行重映射
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	
	GPIO_PinRemapConfig(GPIO_Remap_I2C1,ENABLE);
	
	//对PB8和PB9进行初始化
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	GPIO_InitTypeDef 	GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Pin= GPIO_Pin_8| GPIO_Pin_9 ;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_InitStruct.GPIO_Speed =GPIO_Speed_2MHz ;
	
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	
	//#2.初始化I2C1模块
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
	
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);//施加复位信号		
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,DISABLE);//释放复位信号
	
		I2C_InitTypeDef I2C_InitStruct;
		I2C_InitStruct.I2C_ClockSpeed = 400000;
		I2C_InitStruct.I2C_Mode =I2C_Mode_I2C;
		I2C_InitStruct.I2C_DutyCycle =I2C_DutyCycle_2;
		
		I2C_Init(I2C1,&I2C_InitStruct);
		
		I2C_Cmd(I2C1,ENABLE);//使能I2C
}
int MY_SendBytes(I2C_TypeDef *I2Cx,uint8_t Addr,uint8_t *pData,uint16_t Size)
{
//#1等待总线空闲
	while(I2C_GetFlagStatus(I2Cx,I2C_FLAG_BUSY)==SET)
	
	//#2发送起始位
	I2C_GetFlagStatus(I2Cx,ENABLE);
	
	while(I2C_GetFlagStatus(I2Cx,I2C_FLAG_SB)==RESET)
		
	//#3寻址阶段
	I2C_ClearFlag(I2Cx,I2C_FLAG_AF);
	 
	I2C_SendData(I2Cx,Addr & 0xFE);
	while(1)
{
	if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_ADDR)==SET)
	{
	break;
	}
	if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_AF)==SET)
	{
	I2C_GenerateSTOP(I2Cx,ENABLE);
		return -1;//寻址失败
	}
}
//清除ADDR
	I2C_ReadRegister(I2Cx, I2C_Register_SR1);
	I2C_ReadRegister(I2Cx, I2C_Register_SR2);

//#4发送数据
for(uint16_t i=0;i<Size;i++)
{
	while(1)
	{
	if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_AF)==SET)
	{
	
		I2C_GenerateSTOP(I2Cx,ENABLE);
		return -2;//寻址失败
	}
	if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_BTF)==SET)
	{
	break;
	}
}
}
//#5发送停止位
I2C_GenerateSTOP(I2Cx,ENABLE);
return 0;

}

五、I2C读数据

1.1.1数据读取的流程

和发送的过程不一样的是,从机向主机最后一次应答发送的是NAK表示后续不再接收

1.2.1发送起始位和地址

步骤:先向START写1,再查询SB标志位起始位是完成,检查AF标志位是否收到ACK应答是否成功,若成功则ADDR==1,寻址成功(代码示例)

int My_I2C_ReceiveBytes(I2C_TypeDef *I2Cx,uint8_t Addr,uint8_t *pBuffer,uint16_t Size)
{
	//#1.发送起始位
	I2C_GenerateSTART(I2Cx,ENABLE);
	
	while(I2C_GetFlagStatus(I2Cx,I2C_FLAG_SB)==RESET);
	
	//#2.寻址阶段
	I2C_ClearFlag(I2Cx,I2C_FLAG_AF);
	
	I2C_SendData(I2Cx,Addr|0x01);
	
	while(1)
	{
	if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_AF)==SET)
	{
	I2C_GenerateSTOP(I2Cx,ENABLE);
		return -1;//寻址失败
	}
	if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_ADDR)==SET)
	{
	break;
	}
	}

1.3.1如何发送ACK和NAK(NAK=not ACK)

1.4.1如何发送停止位

向STOP标志位写1

向STOP标志位写1

1.5.1SIZE=1

1.5.2SIZE=2

1.5.3SIZE=3

1.6.1点亮屏幕条件

六、在屏幕上显示图案

1.1.1接线

2.1.1打开这个网站

将图片导入生成代码,将代码放入下方数组uint8_t bitmap{}=[]中

最后就可以在OLED屏幕上显示图片啦。。。。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值