STM32初学记录4(I2C通信)

八、软件I2C

1.简介

        I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。

两根通信线:SCL(Serial Clock)、SDA(Serial Data) 同步,半双工

设备的SCL和SDA均要配置成开漏输出模式

SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右

 2.时序基本单元

        起始条件:SCL高电平期间,SDA从高电平切换到低电平

        终止条件:SCL高电平期间,SDA从低电平切换到高电平

  

        发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节

        接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)

发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答

接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)

 3.通信时序

对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)

对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)

对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data)

4.软件I2C读取EEPROM代码实现

使用两个IO口按照I2C协议输出的信号来作为SCL和SDA通信线,具体代码如下:

#define EEPROM_I2C_SCL_PIN		GPIO_Pin_2			/* 连接到SCL时钟线的GPIO */
#define EEPROM_I2C_SDA_PIN		GPIO_Pin_3			/* 连接到SDA数据线的GPIO */

//控制SCL输出高低电平
void MyI2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, EEPROM_I2C_SCL_PIN, (BitAction)BitValue);
	Delay_us(10);
}
//控制SDA输出高低电平
void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, EEPROM_I2C_SDA_PIN, (BitAction)BitValue);
	Delay_us(10);
}
//读取SDA此时电平
uint8_t MyI2C_R_SDA(void)
{
	uint8_t BitValue;
	BitValue = GPIO_ReadInputDataBit(GPIOA, EEPROM_I2C_SDA_PIN);
	Delay_us(10);
	return BitValue;
}

//初始化GPIO
void MyI2C_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;			//开漏输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;	
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOA, GPIO_Pin_2 | GPIO_Pin_3);        
}

//I2C总线启动信号
void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);
}
//I2C总线停止信号
void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(1);
}
//发送应答或非应答
void MyI2C_SendAck(uint8_t AckBit)
{
	MyI2C_W_SDA(AckBit);
	MyI2C_W_SCL(1);
	MyI2C_W_SCL(0);
}
//接收应答
uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t AckBit;
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	AckBit = MyI2C_R_SDA();
	MyI2C_W_SCL(0);
	return AckBit;
}

//发送一个字节
void MyI2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i ++)
	{
        //Byte & (1000 0000)、Byte & (0100 0000)……	高位先行
		MyI2C_W_SDA(Byte & (0x80 >> i));   
 
		MyI2C_W_SCL(1);
		MyI2C_W_SCL(0);
	}
}
//读取一个字节  返回接收到的Byte
uint8_t MyI2C_ReceiveByte(void)
{
	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)
{
	MyI2C_W_SDA(AckBit);
	MyI2C_W_SCL(1);
	MyI2C_W_SCL(0);
}
//接收应答
uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t AckBit;
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	AckBit = MyI2C_R_SDA();
	MyI2C_W_SCL(0);
	return AckBit;
}
#include "stm32f10x.h"                  // Device header
#include "MyI2C.h"

#define EEPROM_Address	0xA0

void EEPROM_WriteReg(uint8_t RegAddress, uint8_t Data)
{
	MyI2C_Start();
	MyI2C_SendByte(EEPROM_Address);
	MyI2C_ReceiveAck();
	
	MyI2C_SendByte(RegAddress);
	MyI2C_ReceiveAck();
	MyI2C_SendByte(Data);
	MyI2C_ReceiveAck();
	MyI2C_Stop();
}

uint8_t EEPROM_ReadReg(uint8_t *_pReadBuf, uint8_t RegAddress, uint8_t Len)
{
	uint8_t i;
	
	MyI2C_Start();
	MyI2C_SendByte(EEPROM_Address);
	MyI2C_ReceiveAck();
	MyI2C_SendByte(RegAddress);
	MyI2C_ReceiveAck();
	
	MyI2C_Start();
	MyI2C_SendByte(EEPROM_Address | 0x01);
	
	for(i = 0;i < Len; i++)
	{
		_pReadBuf[i] = MyI2C_ReceiveByte();
	}
	MyI2C_SendAck(1);
	MyI2C_Stop();
	
	return 1;
}

void EEPROM_Init(void)
{
	MyI2C_Init();
}


九、硬件I2C

1.I2C外设简介

        STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担

支持多主机模型 支持7位/10位地址模式

支持不同的通讯速度,标准速度(高达100 kHz),快速(高达400 kHz)

支持DMA 兼容SMBus协议

2.I2C框图

 

 3.传送序列图
3.1 主机发送

 3.2 主机接收
 4.硬件I2C代码实现

        读写流程按照传送序列图。

#define EEPROM_Address	0xA0

//I2C_CheckEvent函数检测标志位,事件未完成进入此函数。
//时间完成或超时(防止进入死循环卡死)退出
void EEPROM_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
	uint16_t Timeout = 10000;
	while(I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS)
	{
		Timeout--;
		if(Timeout == 0)
		{
			break;
		}
	}
}


void EEPROM_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;			//复用开漏输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	I2C_InitTypeDef I2C_InitStructure;
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
	I2C_InitStructure.I2C_ClockSpeed = 50000;				//波特率50k
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;	//占空比2:1
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;			
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	I2C_InitStructure.I2C_OwnAddress1 = 0x00;			//STM32的I2C地址
	I2C_Init(I2C2, &I2C_InitStructure);
	
	I2C_Cmd(I2C2, ENABLE);		
}

//写数据
void EEPROM_WriteReg(uint8_t RegAddress, uint8_t Data)
{

	I2C_GenerateSTART(I2C2, ENABLE);
	EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
	
	I2C_Send7bitAddress(I2C2, EEPROM_Address, I2C_Direction_Transmitter);
	EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) ;
	
	I2C_SendData(I2C2, RegAddress);
	EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING) ;
	
	I2C_SendData(I2C2, Data);
	EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
	
	I2C_GenerateSTOP(I2C2, ENABLE);
}
//读数据
uint8_t EEPROM_ReadReg( uint8_t RegAddress)
{
	uint8_t Data;

	I2C_GenerateSTART(I2C2, ENABLE);
	EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT) ;
	
	I2C_Send7bitAddress(I2C2, EEPROM_Address, I2C_Direction_Transmitter);
	EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);

	I2C_SendData(I2C2, RegAddress);
	EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);

	I2C_GenerateSTART(I2C2, ENABLE);
	EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
	
	I2C_Send7bitAddress(I2C2, EEPROM_Address, I2C_Direction_Receiver);
	EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);
	
	I2C_AcknowledgeConfig(I2C2, DISABLE);
	I2C_GenerateSTOP(I2C2, ENABLE);
	
	EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);	//等待EV7事件
	Data = I2C_ReceiveData(I2C2);
	
	I2C_AcknowledgeConfig(I2C2, ENABLE);
	return Data;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值