STM32中软件模拟I2C程序,使用教程

这段代码实现了一个用于STM32F10x系列微控制器的软件模拟I2C通信协议。I2C(Inter-Integrated Circuit)是一种广泛使用的串行通信协议,主要用于短距离设备通信。通过使用这段代码,用户可以在STM32F10x微控制器上实现I2C总线的基本操作,包括初始化、起始信号、终止信号、发送字节、接收字节以及发送和接收应答位。

代码结构

代码主要分为两个部分:引脚配置层和协议层。

引脚配置层
  1. MyI2C_W_SCL(uint8_t BitValue)

    • 设置SCL(串行时钟线)的电平。
    • 参数 BitValue:需要写入的电平值(0或1)。
    • 延时10微秒以确保I2C时序的正确性。
  2. MyI2C_W_SDA(uint8_t BitValue)

    • 设置SDA(串行数据线)的电平。
    • 参数 BitValue:需要写入的电平值(0或非0)。
    • 延时10微秒以确保I2C时序的正确性。
  3. MyI2C_R_SDA(void)

    • 读取SDA线的电平。
    • 返回值:当前SDA线的电平(0或1)。
    • 延时10微秒以确保I2C时序的正确性。
  4. MyI2C_Init(void)

    • 初始化I2C引脚和设置初始状态。
    • 开启GPIOB时钟。
    • 将PB10和PB11引脚设置为开漏输出模式,速度为50MHz。
    • 将PB10和PB11引脚设置为高电平(释放总线)。
协议层
  1. MyI2C_Start(void)

    • 产生I2C起始信号。
    • 释放SDA和SCL,然后在SCL高电平期间拉低SDA,产生起始信号。
  2. MyI2C_Stop(void)

    • 产生I2C停止信号。
    • 拉低SDA,然后在SCL高电平期间释放SDA,产生停止信号。
  3. MyI2C_SendByte(uint8_t Byte)

    • 发送一个字节的数据。
    • 参数 Byte:要发送的字节数据(0x00到0xFF)。
    • 循环8次,依次发送数据的每一位。
  4. MyI2C_ReceiveByte(void)

    • 接收一个字节的数据。
    • 返回值:接收到的字节数据(0x00到0xFF)。
    • 循环8次,依次接收数据的每一位。
  5. MyI2C_SendAck(uint8_t AckBit)

    • 发送应答位。
    • 参数 AckBit:要发送的应答位(0表示应答,1表示非应答)。
    • 将应答位数据放到SDA线,并在SCL高电平期间发送。
  6. MyI2C_ReceiveAck(void)

    • 接收应答位。
    • 返回值:接收到的应答位(0表示应答,1表示非应答)。
    • 在SCL高电平期间读取SDA线的应答位。

 I2C.c文件

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

/*引脚配置层*/

/**
  * 函    数:I2C写SCL引脚电平
  * 参    数:BitValue 协议层传入的当前需要写入SCL的电平,范围0~1
  * 返 回 值:无
  * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SCL为低电平,当BitValue为1时,需要置SCL为高电平
  */
void MyI2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);		//根据BitValue,设置SCL引脚的电平
	Delay_us(10);												//延时10us,防止时序频率超过要求
}

/**
  * 函    数:I2C写SDA引脚电平
  * 参    数:BitValue 协议层传入的当前需要写入SDA的电平,范围0~0xFF
  * 返 回 值:无
  * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SDA为低电平,当BitValue非0时,需要置SDA为高电平
  */
void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);		//根据BitValue,设置SDA引脚的电平,BitValue要实现非0即1的特性
	Delay_us(10);												//延时10us,防止时序频率超过要求
}

/**
  * 函    数:I2C读SDA引脚电平
  * 参    数:无
  * 返 回 值:协议层需要得到的当前SDA的电平,范围0~1
  * 注意事项:此函数需要用户实现内容,当前SDA为低电平时,返回0,当前SDA为高电平时,返回1
  */
uint8_t MyI2C_R_SDA(void)
{
	uint8_t BitValue;
	BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);		//读取SDA电平
	Delay_us(10);												//延时10us,防止时序频率超过要求
	return BitValue;											//返回SDA电平
}

/**
  * 函    数:I2C初始化
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数需要用户实现内容,实现SCL和SDA引脚的初始化
  */
void MyI2C_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	//开启GPIOB的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);					//将PB10和PB11引脚初始化为开漏输出
	
	/*设置默认电平*/
	GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);			//设置PB10和PB11引脚初始化后默认为高电平(释放总线状态)
}

/*协议层*/

/**
  * 函    数:I2C起始
  * 参    数:无
  * 返 回 值:无
  */
void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);							//释放SDA,确保SDA为高电平
	MyI2C_W_SCL(1);							//释放SCL,确保SCL为高电平
	MyI2C_W_SDA(0);							//在SCL高电平期间,拉低SDA,产生起始信号
	MyI2C_W_SCL(0);							//起始后把SCL也拉低,即为了占用总线,也为了方便总线时序的拼接
}

/**
  * 函    数:I2C终止
  * 参    数:无
  * 返 回 值:无
  */
void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);							//拉低SDA,确保SDA为低电平
	MyI2C_W_SCL(1);							//释放SCL,使SCL呈现高电平
	MyI2C_W_SDA(1);							//在SCL高电平期间,释放SDA,产生终止信号
}

/**
  * 函    数:I2C发送一个字节
  * 参    数:Byte 要发送的一个字节数据,范围:0x00~0xFF
  * 返 回 值:无
  */
void MyI2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i ++)				//循环8次,主机依次发送数据的每一位
	{
		MyI2C_W_SDA(Byte & (0x80 >> i));	//使用掩码的方式取出Byte的指定一位数据并写入到SDA线
		MyI2C_W_SCL(1);						//释放SCL,从机在SCL高电平期间读取SDA
		MyI2C_W_SCL(0);						//拉低SCL,主机开始发送下一位数据
	}
}

/**
  * 函    数:I2C接收一个字节
  * 参    数:无
  * 返 回 值:接收到的一个字节数据,范围:0x00~0xFF
  */
uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t i, Byte = 0x00;					//定义接收的数据,并赋初值0x00,此处必须赋初值0x00,后面会用到
	MyI2C_W_SDA(1);							//接收前,主机先确保释放SDA,避免干扰从机的数据发送
	for (i = 0; i < 8; i ++)				//循环8次,主机依次接收数据的每一位
	{
		MyI2C_W_SCL(1);						//释放SCL,主机机在SCL高电平期间读取SDA
		if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}	//读取SDA数据,并存储到Byte变量
														//当SDA为1时,置变量指定位为1,当SDA为0时,不做处理,指定位为默认的初值0
		MyI2C_W_SCL(0);						//拉低SCL,从机在SCL低电平期间写入SDA
	}
	return Byte;							//返回接收到的一个字节数据
}

/**
  * 函    数:I2C发送应答位
  * 参    数:Byte 要发送的应答位,范围:0~1,0表示应答,1表示非应答
  * 返 回 值:无
  */
void MyI2C_SendAck(uint8_t AckBit)
{
	MyI2C_W_SDA(AckBit);					//主机把应答位数据放到SDA线
	MyI2C_W_SCL(1);							//释放SCL,从机在SCL高电平期间,读取应答位
	MyI2C_W_SCL(0);							//拉低SCL,开始下一个时序模块
}

/**
  * 函    数:I2C接收应答位
  * 参    数:无
  * 返 回 值:接收到的应答位,范围:0~1,0表示应答,1表示非应答
  */
uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t AckBit;							//定义应答位变量
	MyI2C_W_SDA(1);							//接收前,主机先确保释放SDA,避免干扰从机的数据发送
	MyI2C_W_SCL(1);							//释放SCL,主机机在SCL高电平期间读取SDA
	AckBit = MyI2C_R_SDA();					//将应答位存储到变量里
	MyI2C_W_SCL(0);							//拉低SCL,开始下一个时序模块
	return AckBit;							//返回定义应答位变量
}

 I2C.h文件

#ifndef __MYI2C_H
#define __MYI2C_H

void MyI2C_Init(void);
void MyI2C_Start(void);
void MyI2C_Stop(void);
void MyI2C_SendByte(uint8_t Byte);
uint8_t MyI2C_ReceiveByte(void);
void MyI2C_SendAck(uint8_t AckBit);
uint8_t MyI2C_ReceiveAck(void);

#endif

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
STM32是一种广泛应用的嵌入式单片机,具有强大的功能和灵活的软件开发能力。在STM32I2C(Inter-Integrated Circuit)是一种通信协议,用于连接各个外设并实现数据传输。 软件模拟I2C程序是指在STM32使用软件模拟的方式实现I2C协议的功能。这种方法可以在硬件不支持I2C功能或者I2C总线被其他外设占用的情况下,使用GPIO引脚模拟I2C信号线,通过软件控制来实现I2C通信。 实现软件模拟I2C程序的关键步骤如下: 1. 设置GPIO引脚模拟I2C的SDA(数据线)和SCL(时钟线)。 2. 初始化GPIO引脚,并配置为输出模式。 3. 实现I2C的起始信号,将SDA线由高电平变为低电平时,同时将SCL线保持为高电平。 4. 实现I2C的停止信号,将SDA线由低电平变为高电平时,同时将SCL线保持为高电平。 5. 实现I2C的数据传输,包括发送和接收。 6. 在发送数据时,先将数据写入SDA线,再将SCL线由高电平变为低电平,完成一次数据传输。 7. 在接收数据时,先将SCL线由高电平变为低电平,再读取SDA线上的数据。 8. 根据I2C协议的需要,可能还需要设置ACK信号等功能。 需要注意的是,软件模拟I2C程序在速度上可能无法达到硬件I2C的要求,因此在使用时需根据具体应用场景进行性能上的优化调整。 总之,通过软件模拟I2C程序,我们可以在STM32上实现I2C通信的功能,为嵌入式开发提供了更多的灵活性和可扩展性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值