STM32—I2C软件模拟

I2C简介

I2C 通讯协议 (Inter - Integrated Circuit) 是由 Phiilps 公司开发的,由于它引脚少,硬件实现简单, 可扩展性强,不需要 USART、CAN 等通讯协议的外部收发设备,现在被广泛地使用在系统内多 个集成电路 (IC) 间的通讯。

软件模拟PK硬件

虽然说STM32是有I2C集成芯片的,但是目前来说是存在很大问题,多用几次就能明显的感觉出来,很容易出现玄学问题。那么既然集成芯片没做好的话,那我们就只能使用软件模拟了,而且我感觉软件模拟相对与硬件来说更加的简单明了。

I2C物理协议层

要想实现I2C协议的交流,实际就是操控上面的总线进行交流。

SDA——数据线

SCL——时钟线

I2C协议

bsp_i2c.h

后面参数为了简便,通过宏定义到该头文件。


#ifndef _BSP_I2C_GPIO_H
#define _BSP_I2C_GPIO_H


#include <inttypes.h>


#define EEPROM_I2C_WR    0        /* 写控制bit */
#define EEPROM_I2C_RD    1        /* 读控制bit */


/* 定义I2C总线连接的GPIO端口, 用户只需要修改下面4行代码即可任意改变SCL和SDA的引脚 */
#define EEPROM_GPIO_PORT_I2C    GPIOB                             /* GPIO端口 */
#define EEPROM_RCC_I2C_PORT     RCC_APB2Periph_GPIOB           /* GPIO端口时钟 */
#define EEPROM_I2C_SCL_PIN      GPIO_Pin_6            /* 连接到SCL时钟线的GPIO */
#define EEPROM_I2C_SDA_PIN      GPIO_Pin_7            /* 连接到SDA数据线的GPIO */

//直接操作寄存器,进行读写操作
#define EEPROM_I2C_SCL_1()      EEPROM_GPIO_PORT_I2C->BSRR = EEPROM_I2C_SCL_PIN                /* SCL = 1 */
    #define EEPROM_I2C_SCL_0()  EEPROM_GPIO_PORT_I2C->BRR = EEPROM_I2C_SCL_PIN                /* SCL = 0 */
    
    #define EEPROM_I2C_SDA_1()  EEPROM_GPIO_PORT_I2C->BSRR = EEPROM_I2C_SDA_PIN                /* SDA = 1 */
    #define EEPROM_I2C_SDA_0()  EEPROM_GPIO_PORT_I2C->BRR = EEPROM_I2C_SDA_PIN                /* SDA = 0 */
    
    #define EEPROM_I2C_SDA_READ()  ((EEPROM_GPIO_PORT_I2C->IDR & EEPROM_I2C_SDA_PIN) != 0)    /* 读SDA口线状态 */


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);

起始


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

依据上面的时序图,写出相应的代码,这里没有太多的技巧,看图说话。

停止


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

等待响应


uint8_t i2c_WaitAck(void)
{
    uint8_t re;

    EEPROM_I2C_SDA_1();    /* CPU释放SDA总线 */
    i2c_Delay();
    EEPROM_I2C_SCL_1();    /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
    i2c_Delay();
    if (EEPROM_I2C_SDA_READ())    /* CPU读取SDA口线状态 */
    {
        re = 1;
    }
    else
    {
        re = 0;
    }
    EEPROM_I2C_SCL_0();
    i2c_Delay();
    return re;
}

SDA从主机到从机,中间需要把SDA的主权交换到从机,这个过程就是拉高SDA。

之后就是利用SCL读取SDA的状态了,依据返回值可以判断从机是否响应,返回值为0时,表示收到响应,为1则未收到响应。

响应


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

主机表示响应过程,SDA拉低,SCL采集SDA的状态,之后两条线都回到原位。

非响应


/*
*********************************************************************************************************
*    函 数 名: i2c_NAck
*    功能说明: CPU产生1个NACK信号
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
void i2c_NAck(void)
{
    EEPROM_I2C_SDA_1();    /* CPU驱动SDA = 1 */
    i2c_Delay();
    EEPROM_I2C_SCL_1();    /* CPU产生1个时钟 */
    i2c_Delay();
    EEPROM_I2C_SCL_0();
    i2c_Delay();    
}

主机主动产生非响应信号,也就是SCL读取的时候SDA处于高电位状态。

引脚选择

由于是软件模拟I2C,所以选择引脚就是随机的,当然这需要符合实际安排。


/*
*********************************************************************************************************
*    函 数 名: i2c_CfgGpio
*    功能说明: 配置I2C总线的GPIO,采用模拟IO的方式实现
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
static void i2c_CfgGpio(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(EEPROM_RCC_I2C_PORT, ENABLE);    /* 打开GPIO时钟 */

    GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SCL_PIN | EEPROM_I2C_SDA_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;      /* 开漏输出 */
    GPIO_Init(EEPROM_GPIO_PORT_I2C, &GPIO_InitStructure);

    /* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */
    i2c_Stop();
}

由于这里的实验是要和EEPROM结合起来的,所以选择的引脚就是EEPROM的引脚。

最后的I2C_Stop( )停止使用协议,等要使用的时候再打开,这样减少功耗

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32是一种广泛应用的嵌入式单片机,具有强大的功能和灵活的软件开发能力。在STM32中,I2C(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通信的功能,为嵌入式开发提供了更多的灵活性和可扩展性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值