一.I2C通讯
1.I2C通讯简介
I2C(Inter IC Bus)是由Philips公司开发的一种通用数据总线;
两根通信线:SCL(Serial Clock)、SDA(Serial Data);
同步,半双工,带数据应答;
支持总线挂载多设备(一主多从、多主多从)
2.硬件电路
所有I2C设备的SCL连在一起,SDA连在一起;
设备的SCL和SDA均要配置成开漏输出模式;
SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右
3.I2C时序基本单元
I2C总线处于空闲状态时,默认情况下SCL和SDA上拉电阻拉高高电平,SCL和SDA先均处于高电平状态;
起始条件:SCL高电平期间,SDA从高电平切换到低电平;
终止条件:SCL高电平期间,SDA从低电平切换到高电平
起始条件和终止条件都是由主机产生的;
发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节;
主机如果发送0,就拉低SDA到低电平;
主机如果发送1,就有上拉电阻拉高到高电平;
SCL高电平期间(如下图B7位置),从机读取SDA;
SCL低电平期间,主机把数据放在SDA线上;数据放完之后,主机再次放手SCL拉高电电平,从机读取SDA线上的数据,一次运行八次,读到一个字节的数据;
接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)
接收与发送的区别:
发送:SCL低电平,主机将数据放在SDA线上,SCL高电平,从机读取SDA线上的数据;
接收:SCL低电平,从机将数据放在SDA线上,SCL高电平,主机读取SDA线上的数据;
发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)
4.I2C外设简介
STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担
支持多主机模型
支持7位/10位地址模式
支持不同的通讯速度,标准速度(高达100 kHz),快速(高达400 kHz)
支持DMA
STM32F103C8T6 硬件I2C资源:I2C1、I2C2
5.I2C框图
二.I2C程序标准库(此程序以MPU6065模块演示)
1.软件I2C程序
MyI2C.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
void MyI2C_W_SCL(uint8_t BitValue) //写SCL引脚反转 直接调用这个函数,传参给1或0就可释放或拉低SCL端口
{
GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue); //bitactiion是一个枚举类型的变量,表示这个位是高电平还是低电平, 将uint8_t 强转BitValue(0或1)
Delay_us(10); //防止一些高性能单片机端口反转速度快,丛机速度跟不上 ,引脚延时10us
}
void MyI2C_W_SDA(uint8_t BitValue) //写SDA引脚反转 直接调用这个函数,传参给1或0就可释放或拉低SDA端口
{
GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue);
Delay_us(10); //防止一些高性能单片机端口反转速度快,丛机速度跟不上 ,引脚延时10us
}
uint8_t MyI2C_R_SDA(void) //读SDA引脚
{
uint8_t BitValue;
BitValue = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11); //读PB11引脚的状态
Delay_us(10);
return BitValue; //将读到的参数返回 SDA线的电平
}
void MyI2C_Init(void) //软件I2C不需要库函数,
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //软件I2C需要将端口配置开漏输出模式,开漏输出模式仍可以输入,输入时,端口输出1,再读取数据寄存器
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_10 | GPIO_Pin_11); //将PA12和PA13置位高电平,SCL和SDA高电平空闲状态
}
//配置I2C 6个时序基本单元
//1.起始条件
void MyI2C_Start(void) //起始条件函数:先拉低SDA,再拉低SCL,就能触发起始条件
{
MyI2C_W_SDA(1); //先释放SDA引脚,输出1 ,高电平空闲
MyI2C_W_SCL(1); //释放SCL引脚,输出1 ,高电平空闲
MyI2C_W_SDA(0); //先拉低SDA引脚,输出0 ,低电平有效
MyI2C_W_SCL(0); //再拉低SCL引脚,输出0 ,低电平有效
}
//2.终止条件
void MyI2C_Stop(void)
{
MyI2C_W_SDA(0); //先拉低SDA引脚,输出0 ,低电平有效
MyI2C_W_SCL(1); //然后释放SCL引脚,输出1 ,高电平空闲
MyI2C_W_SDA(1); //再释放SDA引脚,输出1 ,高电平空闲
}
//3.发送一个字节
void MyI2C_SendByte(uint8_t Byte)
{
uint8_t i;
for(i = 0; i < 8; i++) //循环八次, (0x80 &g