11. STM32——硬件I2C

STM32——硬件I2C

  • IIC协议简介
  • IIC总线系统结构
  • IIC总线物理层特点
  • IIC总线
    • 硬件IIC
    • 软件IIC
    • 区别
    • 如何区分
  • IIC总线协议层
    • 1. 空闲状态
    • 2. 开始信号
    • 3. 停止信号
    • 4. 应答信号
    • 5. 数据的有效性
    • 6. 数据传输
  • STM32的IIC特性及架构
    • 软件模拟协议
    • 硬件实现协议
    • 1. 通讯引脚
    • 2. 时钟控制逻辑
      • 计算时钟频率
      • PCLK1 = 36MHz,想要配置400Kbits/s 方法:
    • 3. 数据控制逻辑
  • STM32的IIC的通讯过程
    • 主发送器通讯过程
  • STM32的IIC结构体
    • I2C_ClockSpeed //设置SCL时钟频率,此值要低于400 000
    • I2C_Mode //指定工作模式,可选IIC模式SMBUS模式
    • I2C_DutyCycle //时钟占空比,可选low/high = 2:0或16:9
    • I2C_OwnAddress1 //自身的IIC设备地址
    • I2C_Ack //使能或者关闭响应,一般是使能
    • I2C_AcknowledgedAddress //指定地址长度,可为7或10
  • STM32的IIC库函数
  • 配置I2C
    • 配置GPIO
    • 配置I2C
    • 向OLED寄存器地址写一个byte的数据
  • 整合

IIC协议简介

IIC通讯协议(Inter----Integrted Circuit)是由Phiips飞利浦公司开发的,
由于他引脚少,硬件实现简单,可拓展性强,不需要UASRT,CAN通讯协议的外部收发设备,现在被广泛使用在系统内多个集成电路IC(芯片)间的通讯。
半双工的通讯方式 IIC通讯协议(Inter----Integrted Circuit)是由Phiips飞利浦公司开发的,
由于他引脚少,硬件实现简单,可拓展性强,不需要UASRT,CAN通讯协议的外部收发设备,现在被广泛使用在系统内多个集成电路IC(芯片)间的通讯。
半双工的通讯方式

IIC总线系统结构

他是一个支持多设备的总线。“总线”指多个设备共用的信号线,在一个IIC通讯总线中,可连接多个IIC通讯设备,支持多个通讯主机及多个通讯从机。
一个IIC总线只使用两条总线线路,一条双向串行数据线(SDA)一条串行时钟线(SCL)数据线即用来表示数据时钟线用于数据收发同步
每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备直接的访问。

11. STM32——硬件I2C_第1张图片

IIC总线物理层特点

总线通过上拉电阻接到电源。当IIC设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态,由上拉电阻把总线拉成高电平

多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定哪个设备占用总线。
具有三种传输模式:标准模式传输速率为100kbit/s,快速模式为400kbit/s,高速模式下可达3.4M/s,但目前大多IIC设备尚不支持高速模式。

IIC总线

硬件IIC

对应芯片上的IIC外设,有相对应的IIC驱动电路,其所使用的IIC管教也是专用的;

软件IIC

一般是用GPIO管教,用软件控制管脚状态以及模拟IIC通信波形;

区别

  1. 硬件IIC的效率要远高于软件的,而软件IIC不受引脚限制,接口比较灵活。
  2. 软件IIC是通过GPIO,软件模拟寄存器的工作方式,而硬件IIC是直接调用内部寄存器进行配置。如果要从具体硬件上来看,可以去看下芯片手册。因为固件IIC的端口是固定的,所以会有所区别。

如何区分

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

IIC总线协议层

IIC的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。

① 空闲状态
② 开始信号
③ 停止信号
④ 应答信号
⑤ 数据的有效性
⑥ 数据传输

1. 空闲状态

IIC总线的SDA和SCL两条信号线同时处于高电平时,规定位总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。

2. 开始信号

起始信号:当SCL为高电平期间,SDA有高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。

3. 停止信号

停止信号:当SCL为高电平期间,SDA由低到高的跳变;停止信号也是一种高电平跳变时序信号,而不是一个电平信号。

起始信号和停止信号一般由主机产生

11. STM32——硬件I2C_第2张图片

4. 应答信号

发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK简称应答位)
表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。

对于反馈有效应答位ACK的要求是,接收器在第九个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。

如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器数据发送,并释放SDA线,以便主控接收器发送一个停止信号P。

11. STM32——硬件I2C_第3张图片

5. 数据的有效性

IIC总线进行数据传输时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。SDA数据线在SCL的每个时钟周期传输一位数据。

即:SDA数据在SCL的上升沿到来之前就需准备好,并在下降沿到来之前必须稳定

11. STM32——硬件I2C_第4张图片

6. 数据传输

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

STM32的IIC特性及架构

软件模拟协议

使用CPU直接控制通讯引脚的电平,产生出符合通讯协议标准的逻辑。

硬件实现协议

由STM32的IIC片上外设专门负责实现IIC通讯协议,只要配置好该外设,它就会自动根据协议要求产生通讯信号,收发数据并缓存起来,CPU只要检测该外设的状态和访问数据寄存器,就能完成数据收发。这种由硬件外设处理IIC协议的方式减轻了CPU的工作,且使软件设计更加简单。

STM32的IIC外设可用作通讯的主机及从机,支持100Kbit/s和400Kbits/s的速率,支持7位、10位设备地址,支持DMA数据传输,并具有数据校验功能。

① 通讯引脚
② 时钟控制逻辑
③ 数据控制逻辑
④ 整体控制逻辑

11. STM32——硬件I2C_第5张图片

1. 通讯引脚

STM32芯片有多个IIC外设,它们的IIC通讯信号引出到不同的GPIO引脚上,使用时必须配置这些指定的引脚。

2. 时钟控制逻辑

SCL线的时钟信号,由IIC接口根据时钟控制寄存器(CCR)控制,控制的参数主要位时钟频率。

可选择IIC通讯的“标准/快速”模式,这两个模式分别对应100/400Kbits/s的通讯速率。

在快速模式下可选择SCL时钟的占空比,可选T(low)/T(high) = 2或T(low)/T(high)=16/9模式。

CCR寄存器中12位的配置因子CCR,它与IIC外设的输入时钟源共用作用,产生SCL时钟。STM32的IIC外设输入时钟源位PCKL1。

计算时钟频率

标准模式:

T high = CCR T pckl1 T low= CCRTpclk1

快速模式中 Tlow/Tlow =2时:

Thigh = CCRTpckl1 T low = 2low*Tpckl1

快速模式中 Tlow/Tlow =16/9时:

Thigh = 9CCRTpckl1 T low = 16lowTpckl1

PCLK1 = 36MHz,想要配置400Kbits/s 方法:

PCLK时钟周期:TPCLK1 = 1/36 000 000
目标SCL时钟周期:TSCL = 1/400 000
SCL时钟周期内的高电平时间:Thigh = TSCL/3
SCL时钟周期内的低电平时间:Tlow = 2*TSCL/3
计算CCR的值:CCR = THIGH/TPCLK1 = 30
计算出来的值写入到寄存器即可

3. 数据控制逻辑

IIC的SDA信号主要连接到数据移位寄存器上,数据移位寄存器的数据来源及目标是数据寄存器(DR)、地址寄存器(OAR)、PEC寄存器以及SDA数据线。

当向外发送数据的时候,数据移位寄存器以“数据寄存器”为数据源,把数据一位一位地通过SDA信号线发送出去。
当从外部接收数据的时候,数据移位寄存器把SDA信号线采样到的数据一位一位地存储到”数据寄存器”中。

STM32的IIC的通讯过程

使用IIC外设通讯时,在通讯的不同阶段它会对”状态寄存器(SR1和SR2)”的不同数据位写入参数,通过读取这些寄存器标志来了解通讯状态。

主发送器通讯过程

11. STM32——硬件I2C_第6张图片

  1. 控制产生起始信号(S),当发生起始信号后,它产生事件"EV5",并会对SR1寄存器的 SB 位置1,表示起始信号已经发生。

  2. 发生设备地址并等待应答信号,若有从机应答,则产生时间 EV6 及 EV8,这时SR1寄存器的 ADDR位及 TXE位被置1,ADDR位1 表示地址已经发送,TEX表示数据寄存器为空。

  3. 往IIC的数据寄存器DR写入要发送的数据,这时TXE位会被充值0,表示数据寄存器非空,IIC外设通过SDA信号线一位位把数据发送出去后,又会产生EV8事件,即TXE被置1,重复这个过程,可发送多个字节。

  4. 发送数据完成后,控制IIC设备产生一个停止信号P,这个时候产生EV8_2事件,SR1的TEX位及BTF位被置1,表示通讯结束。

STM32的IIC结构体

typedef struct
{
  uint32_t I2C_ClockSpeed;          //设置SCL时钟频率,此值要低于400 000                                              
  uint16_t I2C_Mode;                //指定工作模式,可选IIC模式及SMBUS模式
  uint16_t I2C_DutyCycle;           //时钟占空比,可选low/high = 2:0或16:9
  uint16_t I2C_OwnAddress1;         //自身的IIC设备地址   
  uint16_t I2C_Ack;                 //使能或者关闭响应,一般是使能
  uint16_t I2C_AcknowledgedAddress; //指定地址长度,可为7或10
}I2C_InitTypeDef;

I2C_ClockSpeed //设置SCL时钟频率,此值要低于400 000

设置IIC的传输速率,在调用初始化函数时,函数会根据我们输入的数值经过运算后把时钟因子写入到IIC的时钟控制寄存器CCR。而我们写入的这个参数值不得高于400Khz

实际上由于CCR寄存器不能写入小数类型的时钟因子,影响到SCL的实际频率可能会低于本成员设置的参数值,这时除了通讯会稍微慢点以外,不会对IIC的标准通讯造成其他影响。

I2C_Mode //指定工作模式,可选IIC模式SMBUS模式

选择IIC的使用方式,有IIC模式(IIC_Mode_IIC)和SMBus主、从模式(IIC_Mode_SMBusHost、IIC_Mode_SMBusDevice)

IIC不需要在此处区分主从模式,直接设置IIC_Mode_IIC即可。

I2C_DutyCycle //时钟占空比,可选low/high = 2:0或16:9

设置IIC的SCL线时钟的占空比。该配置有两个选择,分别为低电平时间比高电平时间为

2:1(IIC_DutyCycle_2)16:9(IIC_DutyCycle_16_9)

其实这两个模式的比例差别并不大,一般要求都不会如此严格,这里随便选就可以了。

I2C_OwnAddress1 //自身的IIC设备地址

配置STM32的IIC设备自己的地址,每个连接到IIC总线上的设备都有一个自己的地址,作为主机也不例外。地址可以设置为7位或10位(受下面IIC_AcknowledgeAddress成员决定),只要该地址是IIC总线上唯一的即可。

STM32的IIC外设可同时使用两个地址,即同时对两个地址作出响应,这个结构体成员IIC_OwnAddress1配置的是默认的,OAR1寄存器存储的地址,若需要设置第二个地址寄存器OAR2,可使用
IIC_OwmAddress2Conig函数来配置,OAR2不支持10位地址

I2C_OwnAddress1是STM32设备本身的地址,一般STM32作为主设备,可以不用关心这个地址设置,随意设置个数就行,但是如果STM32作为从设备使用时,必须进行配置。

这个参数是32作为iic从设备的时候才会使用的。

这是个可读可写的寄存器,可以自由设置地址。

I2C_Ack //使能或者关闭响应,一般是使能

配置IIC应答是否使能,设置位使能则可以发送响应信号,一般配置位允许应答(IIC_Ack_Enable),这是绝大多数遵循IIC标准的设备的通讯要求,改为禁止应答(IIC_Ack_Disable)往往会导致通讯错误。

I2C_AcknowledgedAddress //指定地址长度,可为7或10

选择IIC的寻址模式是7位或者是10位地址,这需要根据实际连接到IIC总线上设备的地址进行选择,这个成员的配置也影响到IIC_OwnAddress成员,只有这里设置成10位模式时,IIC_OwnAddress1才支持10位地址

配置完这些结构体成员的值,调用库函数IIC_Init就可以把结构体的配置写入到对应的寄存器中了。

STM32的IIC库函数

//配置自身设备地址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);

//IIC外设开始正常工作
void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

配置I2C

配置GPIO

		GPIO_InitTypeDef GPIO_InitStructure; //配置GPIO口结构体  -PB6 -PB7
		I2C_InitTypeDef I2C_InitStructure;   //配置硬件I2C结构体
	
		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;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		
		GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIOB结构体

配置I2C

		I2C_DeInit(I2C1);
		I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;        //I2C应答信号使能
		I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //设置从机地址长度为7位
		I2C_InitStructure.I2C_ClockSpeed = 400000;         //设置目标周期 400khz
		I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; //设置占空比为2:1
		I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;         //设置为I2C模式
		I2C_InitStructure.I2C_OwnAddress1 = 0x30;          //设置主机地址
		
		I2C_Init(I2C1, &I2C_InitStructure); //初始化I2C1结构体
		I2C_Cmd(I2C1, ENABLE); //使能I2C1

向OLED寄存器地址写一个byte的数据

//向OLED寄存器地址写一个byte的数据
void I2C_WriteByte(uint8_t addr,uint8_t data)
{
		while( I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) ); //等待总线空闲
		I2C_GenerateSTART(I2C1, ENABLE); //I2C发送起始信号
		
		while( !I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) ); //等待EV5=1,具体可以参考手册上面EV5事件
		I2C_Send7bitAddress(I2C1, OLED_ADDRESS, I2C_Direction_Transmitter); //发送从机7位地址
		
		while( !I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) ); //等待EV6=1
		I2C_SendData(I2C1, addr); //发送地址字节
		
		while( !I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING) ); //等待事件EV8
		I2C_SendData(I2C1, data); //发送数据字节
		
		while( !I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) ); //等待事件EV8_2
		I2C_GenerateSTOP(I2C1, ENABLE); //I2C发送停止信号
}

整合

#include "oled.h"
#include "stm32f10x.h"
#include "SysTick.h"
#include "codetab.h"

//初始化硬件IIC引脚
void I2C_Configuration(void)
{
		GPIO_InitTypeDef GPIO_InitStructure;
		I2C_InitTypeDef I2C_InitStructure;
		
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
		
		//PB6——SCL PB7——SDA
		GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_OD;
		GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6 | GPIO_Pin_7;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		
		GPIO_Init(GPIOB, &GPIO_InitStructure);
		
		I2C_DeInit(I2C1);
		I2C_InitStructure.I2C_Ack                 = I2C_Ack_Enable;
		I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
		I2C_InitStructure.I2C_ClockSpeed          = 400000;
		I2C_InitStructure.I2C_DutyCycle           = I2C_DutyCycle_2;
		I2C_InitStructure.I2C_Mode                = I2C_Mode_I2C;
		I2C_InitStructure.I2C_OwnAddress1         = 0x30;
		
		I2C_Init(I2C1, &I2C_InitStructure);
		I2C_Cmd(I2C1, ENABLE);
}

//向OLED寄存器地址写一个byte的数据
void I2C_WriteByte(uint8_t addr,uint8_t data)
{
		while( I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) );
		I2C_GenerateSTART(I2C1, ENABLE);
		
		while( !I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) );
		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_TRANSMITTED) );
		I2C_GenerateSTOP(I2C1, ENABLE);
}

你可能感兴趣的:(STM32,IIC,stm32,物联网,嵌入式)