STM32单片机——IIC概念与协议软件模及固件库配置

STM32单片机——IIC概念与协议软件模及固件库配置I2C 是很常见的一种总线协议 I2C 是飞利浦公司在 80 年代开发的两线式串行总线 用于连接微控制器及其外围设备

大家好,欢迎来到IT知识分享网。

IC2协议简介

I2C 是很常见的一种总线协议,I2C 是飞利浦公司在80年代开发的两线式串行总线,用于连接微控制器及其外围设备。IIC属于半双工同步通信方式。I2C 使用两条线在主控制器和从机之间进行数据通信。

I2C协议总线构成

I2C 总线在物理连接上非常简单,分别由SDA(双向串行数据线)SCL(串行时钟线)上拉电阻组成。通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递。I2C通信方式为半双工,只有一根SDA线,同一时间只可以单向通信。每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备直接的访问。下图是I2C总线系统结构具体电路原理图。
STM32单片机——IIC概念与协议软件模及固件库配置

I2C总线物理层特点

  • 总线通过上拉电阻接到电源。当IIC设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态,由上拉电阻把总线拉成高电平
  • 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定哪个设备占用总线。
  • 具有三种传输模式:标准模式传输速率为100kbit/s,快速模式为400kbit/s,高速模式下可达3.4M/s,但目前大多IIC设备尚不支持高速模式。

软硬件I2C比较

硬件IIC:对应芯片上的IIC外设,有相对应的IIC驱动电路,其所使用的IIC管教也是专用的;
软件IIC:一般是用GPIO管教,用软件控制管脚状态以及模拟IIC通信波形;
区别
 1. 硬件IIC的效率要远高于软件的,而软件IIC不受引脚限制,接口比较灵活。
 2. 软件IIC是通过GPIO,软件模拟寄存器的工作方式,而硬件IIC是直接调用内部寄存器进行配置。



如何区分?
 1.硬件IIC用法复杂,模拟IIC流程更加清楚
 2.硬件IIC速度比模拟快,并且可以用DMA
 3.模拟IIC可以在任何管脚上,硬件IIC在固定管脚上


I2C协议通信方式

  • 宏定义
    #define SCLK_Set() GPIO_SetBits(GPIOB, GPIO_Pin_0) //PB0(SCL)输出高 #define SCLK_CLr() GPIO_ResetBits(GPIOB, GPIO_Pin_0) //PB0(SCL)输出低 #define SDIN_Set() GPIO_SetBits(GPIOB, GPIO_Pin_1) //PB1(SDA)输出高 #define SDIN_CLr() GPIO_ResetBits(GPIOB, GPIO_Pin_1) //PB1(SDA)输出低 #define READ_SDIN() GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) //读取PB1(SDA)电平 #define IIC_ACK 0 //应答 #define IIC_NO_ACK 1 //不应答 //对应模拟I2C引脚IO口初始化 static void OLED_GPIO_Init(void) { 
          GPIO_InitTypeDef GPIO_InitStructure; //定义一个GPIO_InitTypeDef类型的结构体 RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE); //打开GPIOC的外设时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; //选择控制的引脚 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //设置为通用开漏输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置输出速率为50MHz GPIO_Init(GPIOB,&GPIO_InitStructure); //调用库函数初始化GPIOA SCLK_Set(); //设PC2(SCL)为高电平 SDIN_Set(); //设PC3(SDA)为高电平 } 

空闲状态

  • IIC总线的SDASCL两条信号线同时处于高电平时,规定位总线的空闲状态

开始信号

  • SCL高电平期间,SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。
    static void IIC_Start(void) { 
          SCLK_Set(); //时钟线置高 SDIN_Set(); //数据线置高 delay_us(1); SDIN_CLr(); //数据线置低,数据发送从高到低跳变 delay_us(1); SCLK_CLr(); //时钟线置低 delay_us(1); } 

停止信号

  • SCL高电平期间,SDA由低到高的跳变;停止信号也是一种高电平跳变时序信号,而不是一个电平信号。起始信号和停止信号一般由主机产生
    在这里插入图片描述
    static void OLED_IIC_Stop(void)
    { 
           
    	//时钟线高时,数据从低到高跳变
    	SDIN_CLr();	//数据线置低
    	delay_us(1);
    	SCLK_Set();	//时钟线置高
    	delay_us(1);
    	SDIN_Set();	//数据线置高 
    	delay_us(1);		
    }
    


应答信号

  • 发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。
  • 应答信号为低电平时,规定为有效应答位(ACK简称应答位)表示接收器已经成功地接收了该字节;
  • 应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
  • 对于反馈有效应答位ACK的要求是,接收器在第九个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。
    在这里插入图片描述
    //模拟I2C读取从机应答信号
    static unsigned char IIC_Wait_Ack(void)
    { 
           
    	unsigned char ack;
    
    	SCLK_CLr();	//时钟线置低
    	delay_us(1);
    	SDIN_Set();	//信号线置高
    	delay_us(1);
    	SCLK_Set();	//时钟线置高
    	delay_us(1);
    
    	if(READ_SDIN())			//读取SDA的电平
    		ack = IIC_NO_ACK;	//如果为1,则从机没有应答
    	else
    		ack = IIC_ACK;		//如果为0,则从机应答
    
    	SCLK_CLr();//时钟线置低
    	delay_us(1);
    
    	return ack;	//返回读取到的应答信息
    }
    


数据的有效性

  • IIC总线进行数据传输时
  • 时钟信号为高电平,数据线上的数据必须保持稳定
  • 时钟线上为低电平数据线上电平状态才允许变化
  • SDA数据线在SCL的每个时钟周期传输一位数据
    即:数据在SCL的上升沿到来之前就需准备好。并在下降沿到来之前必须稳定
    在这里插入图片描述

数据传输

  • 在IIC总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据,高位先行数据位的传输是边沿触发

I2C读写数据函数

  • I2C读取一个字节Byte
    //I2C读取一个字节Byte static void Write_IIC_Byte(unsigned char IIC_Byte) { 
          unsigned char i; //定义变量 for(i=0;i<8;i++) //for循环8次 { 
          SCLK_CLr(); //时钟线置低,为传输数据做准备 delay_us(1); if(IIC_Byte & 0x80) //读取最高位 SDIN_Set(); //最高位为1 else SDIN_CLr(); //最高位为0 IIC_Byte <<= 1; //数据左移1位 delay_us(1); SCLK_Set(); //时钟线置高,产生上升沿,发送数据 delay_us(1); } SCLK_CLr(); //时钟线置低 delay_us(1); while(IIC_Wait_Ack());//从机应答 } 
  • I2C写命令
    //I2C写命令 static void Write_IIC_Command(unsigned char IIC_Command) { 
          OLED_IIC_Start(); Write_IIC_Byte(0x78); //写入从机地址,SD0 = 0 Write_IIC_Byte(0x00); //写入命令 Write_IIC_Byte(IIC_Command);//数据 OLED_IIC_Stop(); //发送停止信号 } 
  • I2C写入数据
    static void Write_IIC_Data(unsigned char IIC_Data) { 
          OLED_IIC_Start(); Write_IIC_Byte(0x78); //写入从机地址,SD0 = 0 Write_IIC_Byte(0x40); //写入数据 Write_IIC_Byte(IIC_Data);//数据 OLED_IIC_Stop(); //发送停止信号 } 

I2C标准库结构体解析及程序设计

  • STM32 I2C的结构体
    typedef struct { 
          uint32_t I2C_ClockSpeed; //设置SCL时钟频率,不得高于 uint16_t I2C_Mode; //指定I2C工作模式,可选I2C模式及SMBUS模式 uint16_t I2C_DutyCycle; //时钟占空比,可选low/high = 2:0 或 16:9 uint16_t I2C_OwnAddress1; //自身的I2C设备地址 uint16_t I2C_Ack; //使能或关闭相应,一般是使能 uint16_t I2C_AcknowledgedAddress; //指定地址长度,可为7或10 }I2C_InitTypeDef; 
  • STM32 I2C常用库函数
    //配置自身设备地址2 void I2C_OwnAddress2Config(I2C_TypeDef* I2Cx, uint8_t Address); //发送设备地址 void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction); //接收数据 uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx); //停止接收 void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState); //外设开始正常工作 void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState); 
  • I2C1 使能配置
    void I2C_Configuration(void) { 
          I2C_InitTypeDef I2C_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); //使能GPIOB时钟 RCC_APB1PeriphClockCmd( RCC_APB1Periph_I2C1, ENABLE ); //使能I2C1时钟 //PB6 --SCL ;PB7 --SDA GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //复用开漏 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //I2C1 SCL:PB6 SDA: PB7 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIOB结构体 I2C_DeInit(I2C1); //初始化I2C1 I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //使能应答 I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //7位地址 I2C_InitStructure.I2C_ClockSpeed =  ; //时钟速度 400K I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2 ; //2分频 16:9 I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; //I2C模式 I2C_InitStructure.I2C_OwnAddress1 = 0X30 ; //主机地址 随便写 不影响 I2C_Init(I2C1,&I2C_InitStructure ); //初始化结构体 I2C_Cmd(I2C1,ENABLE); //使能I2C1 } 
  • I2C写一个字节
    //I2C写一个字节 void I2C_WriteByte(uint8_t addr,uint8_t data) { 
          while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); //检查I2C总线是否繁忙 I2C_GenerateSTART(I2C1, ENABLE); //开启I2C1 while( !I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); //EV5,主模式 I2C_Send7bitAddress(I2C1,OLED_ADDRESS, I2C_Direction_Transmitter); //发送器件地址 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C1, addr); //寄存器地址 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING)); I2C_SendData(I2C1, data); //发送数据 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING)); I2C_GenerateSTOP( I2C1, ENABLE); //关闭I2C总线 } 
  • I2C写命令
    //写命令 void WriteCmd(unsigned char I2C_Command) { 
          I2C_WriteByte(0X00,I2C_Command); } 
  • I2C写数据
    //写数据 void WriteData(unsigned char I2C_Data) { 
          I2C_WriteByte(0x40,I2C_Data); } 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/130894.html

(0)
上一篇 2025-08-11 21:33
下一篇 2025-08-11 21:45

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信