stm32---软件I2C通信

文章详细描述了如何在STM32F10x单片机上实现I2C通信,包括SCL和SDA引脚的操作、I2C初始化、时序控制以及起始、停止、发送字节、接收字节和应答的处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

图文概述:

代码:

#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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值