16:【stm32】I2C的使用一:I2C片上外设的使用

I2C

具体的通过I2C通信协议传输数据帧的相关信息参考《C51单片机》里面的第13节OLED屏的使用

1、片上外设

如图:I2C片上外设挂载在APB1总线上面,并且I2C1由复用重映射,I2C2没有重映射。

1.1:寄存器与内部结构

复制代码
TDR -Transmit Data Register -发送数据寄存器
RDR -Receive Data Register -接收数据寄存器
CR  -Control Register -控制寄存器
SR1 -Status Register 1 -状态寄存器1
SR2 -Status Register 2 -状态寄存器2
CCR -Clock Control Register -时钟控制寄存器
  • SR1:状态寄存器1

    复制代码
    SB:起始位发送完成,0代表没有起始位产生,1代表检测到了起始位。
    AF/ADDR:AF代表接收到NAK,寻址失败AF=1;ADDR代表选址成功,寻址成功-ADDR=1。
    TXE:发送数据寄存器TDR空,1代表空,0代表非空。
    RXNE:接收数据寄存器RDR非空,1代表非空,0代表空。
    BTF:发送完成。1代表发送完成
  • SR2:状态寄存器2

    复制代码
    TRA
    BUSY:用于查询总线的空闲状态,0为空闲,1为忙。
    MSL
  • CR:控制寄存器

    复制代码
    PE:SCL的开关。
    START:用于产生一个起始位,写入1代表发送一个起始位。
    ACK:用于产生一个应答信号。

2、通过I2C向外发送数据

2.1:I2C的初始化

2.1.1:初始化SCL和SDA

我们使用I2C1,初始化SCL和SDA所连接的IO口,把他们配置为复用开漏模式。

复制代码
//1 .开启GPIOB挂载的总线时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//初始化IO引脚
GPIO_InitTypeDef GPIOInitStruct;
GPIOInitStruct.GPIO_Mode = GPIO_Mode_AF_OD;//复用输出开漏模式
GPIOInitStruct.GPIO_Pin = GPIO_Pin_6 |GPIO_Pin_7;
GPIOInitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB,&GPIOInitStruct);

2.1.2:使能时钟PCLK1(APB1)

复制代码
//2 .开启I2C挂载APB1总线的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);//开启时钟
RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,ENABLE);//对I2C1进行复位
RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,DISABLE);//对I2C1进行释放

2.1.3:配置I2C1的参数

标志模式下:波特率max = 100K,当波特率>=100K时,就按照快速模式进行。快速模式下:波特率max = 400K。

I2C_DutyCycle:在快速模式下,配置时钟信号的形状,即时钟信号的占空比。2:1 = 低电平:高电平/16:9 = 低电平:高电平

下面灰色是工作在从机模式下才需要进行配置的。

复制代码
//3 .配置I2C1的参数
I2C_InitTypeDef I2CInitStruct;
I2CInitStruct.I2C_ClockSpeed = 400000;//配置波特率,最高400K
I2CInitStruct.I2C_Mode = I2C_Mode_I2C;//标准的I2C模式,一般不使用SMBus
I2CInitStruct.I2C_DutyCycle = I2C_DutyCycle_2;//2:1
I2C_Init(I2C1,&I2CInitStruct);
//4 .使能I2C
I2C_Cmd(I2C1,ENABLE);//闭合开关,PE

2.2:发送数据

2.2.1:等待总线空闲(BUSY标志位)

复制代码
//1 .等待总线空闲 
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY) == SET);

2.2.2:发送起始位(START,SB标志位)

复制代码
//2 .发送起始位
I2C_GenerateSTART(I2C1,ENABLE);
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_SB) == RESET);//起始位发送完成

2.2.3:发送地址(AF,ADDR标志位)

发送地址前先清除AF标志位,

复制代码
//3 .发送地址
  //3.1 清除标志位AF
I2C_ClearFlag(I2C1,I2C_FLAG_AF);
  //3.2 发送8为数据,7+1
I2C_SendData(I2C1,SlaveADDr & 0xfe);
  //3.3 等待发送成功
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_ADDR) == RESET)
{
	if(I2C_GetFlagStatus(I2C1,I2C_FLAG_AF) == SET)//地址发送失败了
	{
		ret = ERROR;
		goto STOP;
	}
}
STOP:
	I2C_GenerateSTOP(I2C1,ENABLE);//发送停止位
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY) == SET);//等待总线空闲

2.2.4:发送数据

发送数据前先清除ADDR标志位,清除ADDR(先读SR1寄存器,再度SR2寄存器)。

复制代码
//4 .发送数据
  //4.1 清除ADDR标志位
I2C_ReadRegister(I2C1,I2C_Register_SR1);//读取SR1
I2C_ReadRegister(I2C1,I2C_Register_SR2);//读取SR2

  //4.2 向TDR寄存器写入数据
uint32_t i;
for(i = 0; i < Size ;i++)//Size是数据数组的个数
{
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_TXE) == RESET)//等待TXE为1
	{
		if(I2C_GetFlagStatus(I2C1,I2C_FLAG_AF) == SET)
		{
			ret = ERROR;
			goto STOP;
		}
	}
	I2C_SendData(I2C1,pData[i]);//向TDR写入数据
}

while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BTF) == RESET)//等待发送完成
{
	if(I2C_GetFlagStatus(I2C1,I2C_FLAG_AF) == SET)
	{
		ret = ERROR;
		goto STOP;
	}
}

最终代码①:

c 复制代码
	//1 .等待总线空闲 
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY) == SET);
	
	//2 .发送起始位
	I2C_GenerateSTART(I2C1,ENABLE);
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_SB) == RESET);//起始位发送完成
	
	//3 .发送地址
	  //3.1 清除标志位AF
	I2C_ClearFlag(I2C1,I2C_FLAG_AF);
	  //3.2 发送8为数据,7+1
	I2C_SendData(I2C1,SlaveADDr & 0xfe);
	  //3.3 等待发送成功
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_ADDR) == RESET)
	{
		if(I2C_GetFlagStatus(I2C1,I2C_FLAG_AF) == SET)//地址发送失败了
		{
			ret = ERROR;
			goto STOP;
		}
	}

	//4 .发送数据
	  //4.1 清除ADDR标志位
	I2C_ReadRegister(I2C1,I2C_Register_SR1);//读取SR1
	I2C_ReadRegister(I2C1,I2C_Register_SR2);//读取SR2
	
	  //4.2 向TDR寄存器写入数据
	uint32_t i;
	for(i = 0; i < Size ;i++)
	{
		while(I2C_GetFlagStatus(I2C1,I2C_FLAG_TXE) == RESET)//等待TXE为1
		{
			if(I2C_GetFlagStatus(I2C1,I2C_FLAG_AF) == SET)
			{
				ret = ERROR;
				goto STOP;
			}
		}
		I2C_SendData(I2C1,pData[i]);
	}
	
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BTF) == RESET)//等待发送完成
	{
		if(I2C_GetFlagStatus(I2C1,I2C_FLAG_AF) == SET)
		{
			ret = ERROR;
			goto STOP;
		}
	}
	
	//5 .发送STOP
STOP:
	I2C_GenerateSTOP(I2C1,ENABLE);
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY) == SET);

代码测试:通过向OLED模式发送指令将其屏幕点亮

c 复制代码
#include "stm32f10x.h"

void App_I2C_Init(void);
void App_I2C_Send(uint8_t SlaveADDr,uint8_t* pData,uint16_t Size);

int main(void)
{
	//OLED指令
  uint8_t oled_init_command[] = {
  0x00, // Command Stream
  0xa8, 0x3f, // Set MUX Ratio
  0xd3, 0x00, // Set Display Offset
  0x40, // Set Display Start Line
  0xa0, // Set Segment re-map
  0xc0, // Set COM Output Scan Direction
  0xda, 0x02, // Set COM Pins hardware configuration
  0x81, 0x7f, // Set Contrast Control
  0xa5, // Enable Entire Display On
  0xa6, // Set Normal Display
  0xd5, 0x80, // Set OSC Frequency
  0x8d, 0x14, // Enable charge pump regulator
  0xaf, // Display on
	};
	App_I2C_Init();
	App_I2C_Send(0x78, oled_init_command, sizeof(oled_init_command)/sizeof(uint8_t));
	
	while(1)
	{
		
	}
}

void App_I2C_Init(void)
{
	//1 .开启GPIOB挂载的总线时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	//初始化IO引脚
	GPIO_InitTypeDef GPIOInitStruct;
	GPIOInitStruct.GPIO_Mode = GPIO_Mode_AF_OD;//复用输出开漏模式
	GPIOInitStruct.GPIO_Pin = GPIO_Pin_6 |GPIO_Pin_7;
	GPIOInitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOB,&GPIOInitStruct);
	
	//2 .开启I2C挂载APB1总线的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);//开启时钟
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,ENABLE);//对I2C1进行复位
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,DISABLE);//对I2C1进行释放
	
	//3 .配置I2C1的参数
	I2C_InitTypeDef I2CInitStruct;
	I2CInitStruct.I2C_ClockSpeed = 400000;//配置波特率,最高400K
	I2CInitStruct.I2C_Mode = I2C_Mode_I2C;//一般不使用SMBus
	I2CInitStruct.I2C_DutyCycle = I2C_DutyCycle_2;//2:1
	I2C_Init(I2C1,&I2CInitStruct);
	
	//4 .使能I2C
	I2C_Cmd(I2C1,ENABLE);
}

void App_I2C_Send(uint8_t SlaveADDr,uint8_t* pData,uint16_t Size)
{
	ErrorStatus ret = SUCCESS;
	//1 .等待总线空闲 
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY) == SET);
	
	//2 .发送起始位
	I2C_GenerateSTART(I2C1,ENABLE);
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_SB) == RESET);//起始位发送完成
	
	//3 .发送地址
	  //3.1 清除标志位AF
	I2C_ClearFlag(I2C1,I2C_FLAG_AF);
	  //3.2 发送8为数据,7+1
	I2C_SendData(I2C1,SlaveADDr & 0xfe);
	  //3.3 等待发送成功
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_ADDR) == RESET)
	{
		if(I2C_GetFlagStatus(I2C1,I2C_FLAG_AF) == SET)//地址发送失败了
		{
			ret = ERROR;
			goto STOP;
		}
	}

	//4 .发送数据
	  //4.1 清除ADDR标志位
	I2C_ReadRegister(I2C1,I2C_Register_SR1);//读取SR1
	I2C_ReadRegister(I2C1,I2C_Register_SR2);//读取SR2
	
	  //4.2 向TDR寄存器写入数据
	uint32_t i;
	for(i = 0; i < Size ;i++)
	{
		while(I2C_GetFlagStatus(I2C1,I2C_FLAG_TXE) == RESET)//等待TXE为1
		{
			if(I2C_GetFlagStatus(I2C1,I2C_FLAG_AF) == SET)
			{
				ret = ERROR;
				goto STOP;
			}
		}
		I2C_SendData(I2C1,pData[i]);
	}
	
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BTF) == RESET)//等待发送完成
	{
		if(I2C_GetFlagStatus(I2C1,I2C_FLAG_AF) == SET)
		{
			ret = ERROR;
			goto STOP;
		}
	}
	
	//5 .发送STOP
STOP:
	I2C_GenerateSTOP(I2C1,ENABLE);
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY) == SET);
}
相关推荐
Geek__199211 分钟前
GD32 蓝牙模块调试
c语言·单片机·蓝牙
Miuney_MAX9 小时前
【单片机】之HC32F460中断向量选择
单片机·嵌入式硬件
XINVRY-FPGA11 小时前
XC3S1000-4FGG320I Xilinx AMD Spartan-3 SRAM-based FPGA
嵌入式硬件·机器学习·计算机视觉·fpga开发·硬件工程·dsp开发·fpga
猫猫的小茶馆14 小时前
【ARM】ARM的介绍
c语言·开发语言·arm开发·stm32·单片机·嵌入式硬件·物联网
猫猫的小茶馆14 小时前
【PCB工艺】数模电及射频电路基础
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·pcb工艺
点灯小铭14 小时前
基于单片机的智能药物盒设计与实现
数据库·单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
梓德原15 小时前
【基础】详细分析带隙型稳压电路的工作原理
单片机·嵌入式硬件·物联网
国科安芯16 小时前
航天医疗领域AS32S601芯片的性能分析与适配性探讨
大数据·网络·人工智能·单片机·嵌入式硬件·fpga开发·性能优化
小李做物联网16 小时前
【物联网毕业设计】60.1基于单片机物联网嵌入式项目程序开发之图像厨房监测系统
stm32·单片机·嵌入式硬件·物联网
贝塔实验室17 小时前
新手如何使用Altium Designer创建第一张原理图(三)
arm开发·单片机·嵌入式硬件·fpga开发·射频工程·基带工程·嵌入式实时数据库