图文概述:
代码:
#include "stm32f10x.h" // Device header
#include "Delay.h"
/* 软件制定I2C,不需要stm的固件库函数 */
/*对应的引脚号*/
#define SCL GPIO_Pin_10
#define SDA GPIO_Pin_11
/*模块需要使用到的端口:GPIOA 或 GPIOB*/
#define BUS GPIOB
/**
* @brief MyI2C_W_SCL---写入SCL引脚的状态(0为低电平,1为高电平)
* @param BitValue---写入的状态(0或1)
* @retval 无
*/
void MyI2C_W_SCL(uint8_t BitValue)
{
GPIO_WriteBit(BUS, SCL, (BitAction)BitValue);
Delay_us(10);//防止单片机芯片主频比较快,引脚翻转速度MPU6050跟不上
}
/**
* @brief MyI2C_W_SDA---写入SDA引脚的状态(0为低电平,1为高电平)
* @param BitValue---写入的状态(0或1)
* @retval 无
*/
void MyI2C_W_SDA(uint8_t BitValue)
{
GPIO_WriteBit(BUS, SDA, (BitAction)BitValue);
Delay_us(10);//防止单片机芯片主频比较快,引脚翻转速度MPU6050跟不上
}
/**
* @brief MyI2C_R_SDA---读SDA引脚的状态(0为低电平,1为高电平)
* @param 无
* @retval BitValue---返回读取的值(0或1)
*/
uint8_t MyI2C_R_SDA(void)
{
uint8_t BitValue;
BitValue = GPIO_ReadInputDataBit(BUS, SDA);
Delay_us(10);//防止单片机芯片主频比较快,引脚翻转速度MPU6050跟不上
return BitValue;
}
/**
* @brief MyI2C_Init---初始化I2C的配置
* @param 无
* @retval 无
*/
void MyI2C_Init(void)
{
//1. 初始化GPIO时钟以及释放SCL和SDA
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//开启时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //开漏输出模式
GPIO_InitStructure.GPIO_Pin = SCL | SDA;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(BUS, &GPIO_InitStructure);
GPIO_SetBits(BUS, SCL | SDA);
}
/* 完成I2C的6个时序单元:
1.起始条件
2.发送一个字节
3.发送应答
4.接收一个字节
5.接收应答
6.终止条件
*/
void MyI2C_Start(void)
{
//起始条件:SCL高电平期间,SDA从高电平切换到低电平
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
MyI2C_W_SDA(0);
MyI2C_W_SCL(0);
}
void MyI2C_Stop(void)
{
//终止条件:SCL高电平期间,SDA从低电平切换到高电平
MyI2C_W_SDA(0);
MyI2C_W_SCL(1);
MyI2C_W_SDA(1);
}
void MyI2C_SendByte(uint8_t Byte)
{
/*
发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),
然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不
允许有数据变化,依次循环上述过程8次,即可发送一个字节
*/
uint8_t i;
for(i = 0; i < 8; i++)
{
MyI2C_W_SDA(Byte & (0x80>>i));
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
}
uint8_t MyI2C_ReceiveByte(void)
{
/*
接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),
然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不
允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前
,需要释放SDA)
*/
uint8_t i, Byte = 0x00;
MyI2C_W_SDA(1);
for(i = 0; i < 8; i++)
{
MyI2C_W_SCL(1);
if(MyI2C_R_SDA() == 1){ Byte |= (0x80>>i); }
MyI2C_W_SCL(0);
}
return Byte;
}
void MyI2C_SendAck(uint8_t AckBit)
{
/*
发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,
数据0表示应答,数据1表示非应答
*/
MyI2C_W_SDA(AckBit);
MyI2C_W_SCL(1); //SCL高电平期间读取应答位
MyI2C_W_SCL(0); //SCL低电平进入下一个时序单元
}
uint8_t MyI2C_ReceiveAck(void)
{
/*
接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,
判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收
之前,需要释放SDA)
*/
uint8_t AckBit;
MyI2C_W_SDA(1); //主机释放SDA,防止干扰从机(同时从机自动把应答位放在SDA上)
MyI2C_W_SCL(1); //SCL高电平期间,主机读取应答位
AckBit = MyI2C_R_SDA();
MyI2C_W_SCL(0); //SCL低电平进入下一个时序单元
return AckBit;
}
I2C时序基本单元:
起始条件和终止条件 :
void MyI2C_Start(void)
{
//起始条件:SCL高电平期间,SDA从高电平切换到低电平
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
MyI2C_W_SDA(0);
MyI2C_W_SCL(0);
}
void MyI2C_Stop(void)
{
//终止条件:SCL高电平期间,SDA从低电平切换到高电平
MyI2C_W_SDA(0);
MyI2C_W_SCL(1);
MyI2C_W_SDA(1);
}
发送一个字节:
void MyI2C_SendByte(uint8_t Byte)
{
/*
发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),
然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不
允许有数据变化,依次循环上述过程8次,即可发送一个字节
*/
uint8_t i;
for(i = 0; i < 8; i++)
{
MyI2C_W_SDA(Byte & (0x80>>i));
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
}
接收一个字节:
uint8_t MyI2C_ReceiveByte(void)
{
/*
接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),
然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不
允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前
,需要释放SDA)
*/
uint8_t i, Byte = 0x00;
MyI2C_W_SDA(1);
for(i = 0; i < 8; i++)
{
MyI2C_W_SCL(1);
if(MyI2C_R_SDA() == 1){ Byte |= (0x80>>i); }
MyI2C_W_SCL(0);
}
return Byte;
}
发送应答和接收应答:
void MyI2C_SendAck(uint8_t AckBit)
{
/*
发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,
数据0表示应答,数据1表示非应答
*/
MyI2C_W_SDA(AckBit);
MyI2C_W_SCL(1); //SCL高电平期间读取应答位
MyI2C_W_SCL(0); //SCL低电平进入下一个时序单元
}
uint8_t MyI2C_ReceiveAck(void)
{
/*
接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,
判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收
之前,需要释放SDA)
*/
uint8_t AckBit;
MyI2C_W_SDA(1); //主机释放SDA,防止干扰从机(同时从机自动把应答位放在SDA上)
MyI2C_W_SCL(1); //SCL高电平期间,主机读取应答位
AckBit = MyI2C_R_SDA();
MyI2C_W_SCL(0); //SCL低电平进入下一个时序单元
return AckBit;
}