STM32_HAL---GPIO模拟IIC协议

#include "bsp_i2c.h"
/*
*********************************************************************************************************
*	函 数 名: i2c_Delay
*	功能说明: I2C总线位延迟,最快400KHz
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
static void i2c_Delay(void)
{
	uint16_t i;

	/* 
	 	下面的时间是通过逻辑分析仪测试得到的。
    工作条件:CPU主频216MHz ,MDK编译环境,1级优化
     183 = 5us
		循环次数为200时,SCL频率 = 100KHz  
	 	循环次数为30时,SCL频率 = 400KHz 
  */
	for (i = 0; i < 200; i++);
}

/*
*********************************************************************************************************
*	函 数 名: i2c_Start
*	功能说明: CPU发起I2C总线启动信号
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
void i2c_Start(void)
{
	/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
	I2C_SDA_1();
	I2C_SCL_1();
	i2c_Delay();
	I2C_SDA_0();
	i2c_Delay();
	I2C_SCL_0();
	i2c_Delay();
}
/*
*********************************************************************************************************
*	函 数 名: i2c_Stop
*	功能说明: CPU发起I2C总线停止信号
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
void i2c_Stop(void)
{
	/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
	I2C_SDA_0();
	I2C_SCL_1();
	i2c_Delay();
	I2C_SDA_1();
}

/*
*********************************************************************************************************
*	函 数 名: i2c_SendByte
*	功能说明: CPU向I2C总线设备发送8bit数据
*	形    参:_ucByte : 等待发送的字节
*	返 回 值: 无
*********************************************************************************************************
*/
void i2c_SendByte(uint8_t _ucByte)
{
	uint8_t i;

	/* 先发送字节的高位bit7 */
	for (i = 0; i < 8; i++)
	{		
		if (_ucByte & 0x80)
		{
			I2C_SDA_1();
		}
		else
		{
			I2C_SDA_0();
		}
		i2c_Delay();
		I2C_SCL_1();
		i2c_Delay();	
		I2C_SCL_0();
		if (i == 7)
		{
			 I2C_SDA_1(); // 释放总线
		}
		_ucByte <<= 1;	/* 左移一个bit */
		i2c_Delay();
	}
}

/*
*********************************************************************************************************
*	函 数 名: i2c_ReadByte
*	功能说明: CPU从I2C总线设备读取8bit数据
*	形    参:无
*	返 回 值: 读到的数据
*********************************************************************************************************
*/
uint8_t i2c_ReadByte(void)
{
	uint8_t i;
	uint8_t value;

	/* 读到第1个bit为数据的bit7 */
	value = 0;
	for (i = 0; i < 8; i++)
	{
		value <<= 1;
		I2C_SCL_1();
		i2c_Delay();
		if (I2C_SDA_READ())
		{
			value++;
		}
		I2C_SCL_0();
		i2c_Delay();
	}
	return value;
}

/*
*********************************************************************************************************
*	函 数 名: i2c_WaitAck
*	功能说明: CPU产生一个时钟,并读取器件的ACK应答信号
*	形    参:无
*	返 回 值: 返回0表示正确应答,1表示无器件响应
*********************************************************************************************************
*/
uint8_t i2c_WaitAck(void)
{
	uint8_t re;
	
	I2C_SDA_1();	/* CPU释放SDA总线 */
	i2c_Delay();
	I2C_SCL_1();	/* CPU驱动SCL = 1, 此时器件会返回ACK应答*/
	i2c_Delay();
	if(I2C_SDA_READ())	/* CPU读取SDA口线状态 */
	{
		re = 1;
	}
	else
	{
		re = 0;
	}
	I2C_SCL_0();
	i2c_Delay();
	return re;
}

/*
*********************************************************************************************************
*	函 数 名: i2c_Ack
*	功能说明: CPU产生一个ACK信号
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
void i2c_Ack(void)
{
	I2C_SDA_0();	/* CPU驱动SDA = 0 */
	i2c_Delay();
	I2C_SCL_1();	/* CPU产生1个时钟 */
	i2c_Delay();
	I2C_SCL_0();
	i2c_Delay();
	I2C_SDA_1();	/* CPU释放SDA总线 */
}

/*
*********************************************************************************************************
*	函 数 名: i2c_NAck
*	功能说明: CPU产生1个NACK信号
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
void i2c_NAck(void)
{
	I2C_SDA_1();	/* CPU驱动SDA = 1 */
	i2c_Delay();
	I2C_SCL_1();	/* CPU产生1个时钟 */
	i2c_Delay();
	I2C_SCL_0();
	i2c_Delay();	
}
/*
*********************************************************************************************************
*	函 数 名: i2c_CfgGpio
*	功能说明: 配置I2C总线的GPIO,采用模拟IO的方式实现
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
void i2c_CfgGpio(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	/*开启相关的GPIO外设时钟*/
	I2Cx_SDA_GPIO_CLK_ENABLE();
	I2Cx_SCL_GPIO_CLK_ENABLE();
	
	GPIO_InitStructure.Pin = I2C_SDA_PIN;
	GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;
	GPIO_InitStructure.Pull = GPIO_NOPULL;
	GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
	HAL_GPIO_Init(I2C_GPIO_PORT, &GPIO_InitStructure);	

	GPIO_InitStructure.Pin = I2C_SCL_PIN;
	HAL_GPIO_Init(I2C_GPIO_PORT, &GPIO_InitStructure);
	
	/* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */
	i2c_Stop();
}

/*
*********************************************************************************************************
*	函 数 名: i2c_CheckDevice
*	功能说明: 检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在
*	形    参:_Address:设备的I2C总线地址
*	返 回 值: 返回值 0 表示正确, 返回1表示未探测到
*********************************************************************************************************
*/
uint8_t i2c_CheckDevice(uint8_t _Address)
{
	uint8_t ucAck = 0;

	i2c_Start();		/* 发送启动信号 */

	/* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
	i2c_SendByte(_Address|I2C_WR);
	
	ucAck = i2c_WaitAck();	/* 检测设备的ACK应答 */

	i2c_Stop();			/* 发送停止信号 */

	return ucAck;
}

```c
#ifndef _BSP_I2C_GPIO_H
#define _BSP_I2C_GPIO_H

#include "stm32f4xx.h"
#include <inttypes.h>


#define I2C_WR	0		/* 写控制bit */
#define I2C_RD	1		/* 读控制bit */


/* 定义I2C总线连接的GPIO端口, 用户只需要修改下面4行代码即可任意改变SCL和SDA的引脚 */
#define I2C_GPIO_PORT				GPIOB			/* GPIO端口 */

#define I2C_SCL_PIN					GPIO_PIN_6			/* 连接到SCL时钟线的GPIO */
#define I2C_SDA_PIN					GPIO_PIN_7			/* 连接到SDA数据线的GPIO */

#define I2Cx_SDA_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOB_CLK_ENABLE()
#define I2Cx_SCL_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOB_CLK_ENABLE() 


#define I2C_SCL_1()  	  HAL_GPIO_WritePin(I2C_GPIO_PORT,I2C_SCL_PIN, GPIO_PIN_SET);  /* SCL = 1 */
#define I2C_SCL_0()  	  HAL_GPIO_WritePin(I2C_GPIO_PORT,I2C_SCL_PIN, GPIO_PIN_RESET); /* SCL = 0 */
	
#define I2C_SDA_1()  		HAL_GPIO_WritePin(I2C_GPIO_PORT,I2C_SDA_PIN, GPIO_PIN_SET);  /* SDA = 1 */
#define I2C_SDA_0()  		HAL_GPIO_WritePin(I2C_GPIO_PORT,I2C_SDA_PIN, GPIO_PIN_RESET);  /* SDA = 0 */
	
#define I2C_SDA_READ()  HAL_GPIO_ReadPin(I2C_GPIO_PORT,I2C_SDA_PIN)/* 读SDA口线状态 */

/* 定义读写SCL和SDA的宏,已增加代码的可移植性和可阅读性 */
//#if 1	/* 条件编译: 1 选择GPIO的库函数实现IO读写 */
//	#define I2C_SCL_1()  GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_PIN)		  /* SCL = 1 */
//	#define I2C_SCL_0()  GPIO_ResetBits(I2C_GPIO_PORT, I2C_SCL_PIN)		/* SCL = 0 */
//	
//	#define I2C_SDA_1()  GPIO_SetBits(I2C_GPIO_PORT,I2C_SDA_PIN)		  /* SDA = 1 */
//	#define I2C_SDA_0()  GPIO_ResetBits(I2C_GPIO_PORT,I2C_SDA_PIN)		/* SDA = 0 */
//	
//	#define I2C_SDA_READ()  GPIO_ReadInputDataBit(I2C_GPIO_PORT,I2C_SDA_PIN)	/* 读SDA口线状态 */
//	
//#else	/* 这个分支选择直接寄存器操作实现IO读写 */

//    /* 注意:如下写法,在IAR最高级别优化时,会被编译器错误优化 */
//	#define I2C_SCL_1()  EI2C_GPIO_PORT->BSRRL = I2C_SCL_PIN				/* SCL = 1 */
//	#define I2C_SCL_0()  I2C_GPIO_PORT->BSRRH = I2C_SCL_PIN				/* SCL = 0 */
//	
//	#define I2C_SDA_1()  I2C_GPIO_PORT->BSRRL = I2C_SDA_PIN				/* SDA = 1 */
//	#define EI2C_SDA_0()  I2C_GPIO_PORT->BSRRH = I2C_SDA_PIN				/* SDA = 0 */
//	
//	#define I2C_SDA_READ()  ((I2C_GPIO_PORT->IDR & I2C_SDA_PIN) != 0)	/* 读SDA口线状态 */
//#endif


void i2c_Start(void);
void i2c_Stop(void);
void i2c_SendByte(uint8_t _ucByte);
uint8_t i2c_ReadByte(void);
uint8_t i2c_WaitAck(void);
void i2c_Ack(void);
void i2c_NAck(void);
uint8_t i2c_CheckDevice(uint8_t _Address);
void i2c_CfgGpio(void);

#endif



  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是使用STM32 HAL库编写模拟I2C通信协议的示例代码: ```c #include "stm32fxx_hal.h" #define IIC_SCL_PIN GPIO_PIN_8 #define IIC_SDA_PIN GPIO_PIN_9 GPIO_TypeDef *IIC_SCL_PORT = GPIOB; GPIO_TypeDef *IIC_SDA_PORT = GPIOB; #define IIC_SCL_LOW() HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_RESET) #define IIC_SCL_HIGH() HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_SET) #define IIC_SDA_LOW() HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_RESET) #define IIC_SDA_HIGH() HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_SET) #define IIC_SDA_READ() HAL_GPIO_ReadPin(IIC_SDA_PORT, IIC_SDA_PIN) #define IIC_DELAY() delay_us(2) void iic_delay(uint16_t us) { uint16_t i; while (us--) { i = 10; while (i--) { __NOP(); } } } void iic_start(void) { IIC_SDA_HIGH(); IIC_SCL_HIGH(); iic_delay(1); IIC_SDA_LOW(); iic_delay(1); IIC_SCL_LOW(); } void iic_stop(void) { IIC_SDA_LOW(); IIC_SCL_HIGH(); iic_delay(1); IIC_SDA_HIGH(); iic_delay(1); } uint8_t iic_write_byte(uint8_t data) { uint8_t i; uint8_t ack; for (i = 0; i < 8; i++) { IIC_SCL_LOW(); iic_delay(1); if (data & 0x80) { IIC_SDA_HIGH(); } else { IIC_SDA_LOW(); } iic_delay(1); IIC_SCL_HIGH(); iic_delay(1); data <<= 1; } IIC_SCL_LOW(); iic_delay(1); IIC_SDA_HIGH(); iic_delay(1); IIC_SCL_HIGH(); iic_delay(1); ack = IIC_SDA_READ(); IIC_SCL_LOW(); iic_delay(1); return ack; } uint8_t iic_read_byte(uint8_t ack) { uint8_t i; uint8_t data = 0; for (i = 0; i < 8; i++) { data <<= 1; IIC_SCL_LOW(); iic_delay(1); IIC_SCL_HIGH(); iic_delay(1); if (IIC_SDA_READ()) { data |= 1; } } IIC_SCL_LOW(); iic_delay(1); if (ack) { IIC_SDA_LOW(); } else { IIC_SDA_HIGH(); } iic_delay(1); IIC_SCL_HIGH(); iic_delay(1); IIC_SCL_LOW(); IIC_SDA_HIGH(); iic_delay(1); return data; } void iic_init(void) { GPIO_InitTypeDef GPIO_InitStruct; __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitStruct.Pin = IIC_SCL_PIN | IIC_SDA_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(IIC_SCL_PORT, &GPIO_InitStruct); HAL_GPIO_Init(IIC_SDA_PORT, &GPIO_InitStruct); IIC_SCL_HIGH(); IIC_SDA_HIGH(); } ``` 以上代码中,我们首先定义了IIC的SCL和SDA引脚以及它们所在的端口。接着我们定义了一些宏来对这些引脚进行操作,比如设置引脚为高电平或低电平,读取SDA引脚的状态等等。我们还定义了一些函数,如iic_start、iic_stop、iic_write_byte、iic_read_byte等,来实现IIC的开始信号、停止信号、写入一个字节数据和读取一个字节数据等操作。 在iic_init函数中,我们初始化了IIC的SCL和SDA引脚,并将它们设置为输出模式。在这些函数中,我们使用了STM32 HAL库中提供的GPIO_WritePin和GPIO_ReadPin函数来对引脚进行操作。 当你需要使用模拟IIC协议与其他设备进行通信时,你可以使用以上示例代码作为参考。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值