【STM32】I2C篇:初探I2C片上外设应用

I2C

  • 1、片上外设
  • 1.1:寄存器与内部结构
  • 2、通过I2C向外发送数据
  • 2.1:I2C的初始化
  • 2.1.1:初始化SCL和SDA
  • 2.1.2:使能时钟PCLK1(APB1)
  • 2.1.3:配置I2C1的参数
  • 2.2:发送数据
  • 2.2.1:等待总线空闲(BUSY标志位)
  • 2.2.2:发送起始位(START,SB标志位)
  • 2.2.3:发送地址(AF,ADDR标志位)
  • 2.2.4:发送数据

  • 具体的通过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;
    	}
    }
    

    最终代码①:

    	//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模式发送指令将其屏幕点亮

    #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);
    }
    

    作者:浅陌pa

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【STM32】I2C篇:初探I2C片上外设应用

    发表回复