【STM32】软件I2C的使用 —— 看这一篇就够了(附代码)

这篇具有很好参考价值的文章主要介绍了【STM32】软件I2C的使用 —— 看这一篇就够了(附代码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

欢迎来到我的博客。今天我想向大家介绍一下STM32软件I2C功能。

首先,让我们来了解一下I2C(Inter-Integrated Circuit)总线。I2C是一种串行通信总线,最初由Philips公司开发。它允许多个设备使用同一条总线进行通信,并且每个设备都有唯一的地址。I2C通常用于连接微控制器、传感器和其他外设。

在STM32中,I2C总线被实现为硬件和软件两种方式。硬件I2C功能可以直接使用STM32芯片上的I2C外设,而软件I2C需要通过编程实现。由于某些应用场景不适宜使用硬件I2C功能,所以软件I2C在STM32中也变得非常重要。

STM32软件I2C功能与硬件I2C功能类似,它们之间的主要区别在于数据传输的过程。软件I2C需要使用GPIO口模拟I2C通信过程,因此实现起来相对复杂。但是软件I2C具有很高的灵活性,可以根据需要进行修改和扩展。

在STM32中,软件I2C驱动程序通常由以下几个部分组成:

初始化:这一步包括配置GPIO口、设置时序等操作,以确保I2C通信正常进行。

启动:启动信号是I2C总线上的一个信号,用于指示传输开始。为了在软件I2C中实现“启动”信号,我们需要将SDA(数据线)从高电平拉到低电平,然后将SCL(时钟线)从高电平拉到低电平。

停止:停止信号用于指示传输结束。在软件I2C中,我们需要将SCL从低电平拉到高电平,然后将SDA从低电平拉到高电平。

数据传输:数据传输通过向SDA写入位来完成。在传输数据之前,我们需要向SCL写入一个脉冲来获取ACK(应答)信号,以确保数据已被正确接收。

虽然软件I2C比硬件I2C更加复杂,但它具有很高的灵活性和可扩展性。此外,在某些情况下,软件I2C可以提供更好的性能和功耗优化。

下面上代码。根据野火例程修改而来,已验证。

bsp_i2c_gpio.c
/**
 ******************************************************************************
 * @file    bsp_i2c_ee.c
 * @version V1.0
 * @date    2023-4-12
 * @brief   用gpio模拟i2c总线, 适用于STM32系列CPU。该模块不包括应用层命令帧,仅包括I2C总线基本操作函数。
 ******************************************************************************
#include "bsp_i2c_gpio.h"
#include "stm32f4xx.h"
#include <stdio.h>

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

    /* 
        下面的时间是通过逻辑分析仪测试得到的。
    工作条件:CPU主频72MHz ,MDK编译环境,1级优化

        循环次数为10时,SCL频率 = 205KHz
        循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us
        循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us
    */
    for (i = 0; i < 10; i++)
        ;
}

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

/*
*********************************************************************************************************
*	函 数 名: i2c_Stop
*	功能说明: CPU发起I2C总线停止信号
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
void i2c_Stop(void)
{
    /* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
    BSP_I2C_SDA_0();
    BSP_I2C_SCL_1();
    i2c_Delay();
    BSP_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)
        {
            BSP_I2C_SDA_1();
        }
        else
        {
            BSP_I2C_SDA_0();
        }
        i2c_Delay();
        BSP_I2C_SCL_1();
        i2c_Delay();
        BSP_I2C_SCL_0();
        if (i == 7)
        {
            BSP_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;
        BSP_I2C_SCL_1();
        i2c_Delay();
        if (BSP_I2C_SDA_READ())
        {
            value++;
        }
        BSP_I2C_SCL_0();
        i2c_Delay();
    }
    return value;
}

/*
*********************************************************************************************************
*	函 数 名: i2c_WaitAck
*	功能说明: CPU产生一个时钟,并读取器件的ACK应答信号
*	形    参:无
*	返 回 值: 返回0表示正确应答,1表示无器件响应
*********************************************************************************************************
*/
uint8_t i2c_WaitAck(void)
{
    uint8_t re;

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

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

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

/*
*********************************************************************************************************
*	函 数 名: i2c_CfgGpio
*	功能说明: 配置I2C总线的GPIO,采用模拟IO的方式实现
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
void i2c_CfgGpio(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    I2Cx_SCL_GPIO_CLK_ENABLE();
    I2Cx_SDA_GPIO_CLK_ENABLE();
    /**I2C2 GPIO Configuration
    PB10     ------> I2C2_SCL
    PB9     ------> I2C2_SDA
    */
    GPIO_InitStruct.Pin = BSP_I2C_SCL_PIN | BSP_I2C_SDA_PIN;
    ;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    HAL_GPIO_Init(BSP_GPIO_PORT_I2C, &GPIO_InitStruct);
    /* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */
    i2c_Stop();
}

bsp_i2c_gpio.h
#ifndef _BSP_I2C_GPIO_H
#define _BSP_I2C_GPIO_H

#include <inttypes.h>

#define BSP_I2C_WR 0 /* 写控制bit */
#define BSP_I2C_RD 1 /* 读控制bit */

/* 定义I2C总线连接的GPIO端口时钟控制 */

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

/* 定义I2C总线连接的GPIO端口, 用户只需要修改下面3行代码即可任意改变SCL和SDA的引脚 */
#define BSP_GPIO_PORT_I2C GPIOB    /* GPIO端口 */
#define BSP_I2C_SCL_PIN GPIO_PIN_8 /* 连接到SCL时钟线的GPIO */
#define BSP_I2C_SDA_PIN GPIO_PIN_9 /* 连接到SDA数据线的GPIO */

/* 定义读写SCL和SDA的宏,已增加代码的可移植性和可阅读性 */
#if 0                                                                /* 条件编译: 1 选择GPIO的库函数实现IO读写 */
#define BSP_I2C_SCL_1() digitalH(BSP_GPIO_PORT_I2C, BSP_I2C_SCL_PIN) /* SCL = 1 */
#define BSP_I2C_SCL_0() digitalL(BSP_GPIO_PORT_I2C, BSP_I2C_SCL_PIN) /* SCL = 0 */

#define BSP_I2C_SDA_1() digitalH(BSP_GPIO_PORT_I2C, BSP_I2C_SDA_PIN)         /* SDA = 1 */
#define BSP_I2C_SDA_0() digitalL(BSP_GPIO_PORT_I2C, BSP_I2C_SDA_PIN)         /* SDA = 0 */
	
	//#define BSP_I2C_SDA_READ()  GPIO_ReadInputDataBit(BSP_GPIO_PORT_I2C, BSP_I2C_SDA_PIN)	/* 读SDA口线状态 */
#define BSP_I2C_SDA_READ() ((BSP_GPIO_PORT_I2C->IDR & BSP_I2C_SDA_PIN) != 0) /* 读SDA口线状态 */

#else                                                                              /* 这个分支选择直接寄存器操作实现IO读写 */
/* 注意:如下写法,在IAR最高级别优化时,会被编译器错误优化 */
#define BSP_I2C_SCL_1() BSP_GPIO_PORT_I2C->BSRR = (uint32_t)BSP_I2C_SCL_PIN        /* SCL = 1 */
#define BSP_I2C_SCL_0() BSP_GPIO_PORT_I2C->BSRR = (uint32_t)BSP_I2C_SCL_PIN << 16U /* SCL = 0 */

#define BSP_I2C_SDA_1() BSP_GPIO_PORT_I2C->BSRR = (uint32_t)BSP_I2C_SDA_PIN        /* SDA = 1 */
#define BSP_I2C_SDA_0() BSP_GPIO_PORT_I2C->BSRR = (uint32_t)BSP_I2C_SDA_PIN << 16U /* SDA = 0 */

#define BSP_I2C_SDA_READ() ((BSP_GPIO_PORT_I2C->IDR & BSP_I2C_SDA_PIN) != 0) /* 读SDA口线状态 */
#endif

/* 直接操作寄存器的方法控制IO */
#define digitalH(p, i) \
    {                  \
        p->BSRR = i;   \
    } // 设置为高电平
#define digitalL(p, i)               \
    {                                \
        p->BSRR = (uint32_t)i << 16; \
    } // 输出低电平

void i2c_CfgGpio(void);
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);

#endif

最后,不要忘记在主程序中调用 i2c_CfgGpio(); 完成用于模拟I2C的GPIO初始化。

应用实例

这里我使用的是lis2dw12加速度传感器,在数据手册中给出了I2C通信时序如下。

stm32 软件i2c,STM32,stm32,单片机,嵌入式硬件
Master:主机
Slave:从机
ST:起始信号 START signal
SAD:从机地址 Slave Address
SAK:从机应答 slave acknowledge
DATA :8位的数据内容
SP:停止信号 STOP signal
NMAK :非主机应答 No Master Acknowledge
SUB:8位的子地址 8-bit sub-address
W :读操作
R:写操作

软件模拟这个流程就能实现通讯,对照时序图和程序的每一个步骤阅读,方便理解。

使用前记得包含头文件

#include "bsp_i2c_gpio.h"
软件模拟读操作

stm32 软件i2c,STM32,stm32,单片机,嵌入式硬件

/*
 * @brief  Read generic device register (platform dependent)
 *
 * @param  handle    customizable argument. In this examples is used in
 *                   order to select the correct sensor bus handler.
 * @param  reg       register to read
 * @param  bufp      pointer to buffer that store the data read
 * @param  len       number of consecutive register to read
 *
 */
static int32_t platform_read(void *handle, uint8_t reg, uint8_t *bufp,
                             uint16_t len)
{
  uint16_t i;
  /* 第1步:发起I2C总线启动信号 */
  i2c_Start();
  /* 第2步:发送控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  i2c_SendByte(BSP_I2C_ADD | BSP_I2C_WR); /* 写指令 */
  /* 第3步:等待ACK */
  if (i2c_WaitAck() != 0)
  {
    goto cmd_fail;
  }
  /* 第4步: 发送SUB */
  i2c_SendByte(reg);
  /* 第5步: 等待ACK */
  if (i2c_WaitAck() != 0)
  {
    goto cmd_fail;
  }
  /* 第6步: 发送SR (repeated START) */
  i2c_Start();
  /* 第7步: 发送控制字节 */
  i2c_SendByte(BSP_I2C_ADD | BSP_I2C_RD); /* 读指令 */
                                                    /* 第8步: 发送ACK */
  if (i2c_WaitAck() != 0)
  {
    goto cmd_fail;
  }
  /* 第9步: 循环读取数据 */
  for (i = 0; i < len; i++)
  {
    bufp[i] = i2c_ReadByte(); /* 读1个字节 */

    /* 每读完1个字节后,需要主机发送ACK,最后一个字节发送NACK */
    if (i != len - 1)
    {
      i2c_Ack(); /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */
    }
    else
    {
      i2c_NAck(); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
    }
  }
  /* 第10步:发送停止信号 */
  i2c_Stop();
  return 0;
cmd_fail:
  i2c_Stop();
  return 1;
}
软件模拟写操作

stm32 软件i2c,STM32,stm32,单片机,嵌入式硬件

/*
 * @brief  Write generic device register (platform dependent)
 *
 * @param  handle    customizable argument. In this examples is used in
 *                   order to select the correct sensor bus handler.
 * @param  reg       register to write
 * @param  bufp      pointer to buffer that store the data read
 * @param  len       number of consecutive register to read
 *
 */
static int32_t platform_write(void *handle, uint8_t reg, const uint8_t *bufp,
                              uint16_t len)
{
  uint16_t i;
  /* 第0步: 发送停止信号 */
  i2c_Stop();
  /* 第1步: 发起I2C总线启动信号 */
  i2c_Start();
  /* 第2步: 发送控制字节 */
  i2c_SendByte(BSP_I2C_ADD | BSP_I2C_WR);
  /* 第3步: 等待ACK */
  if (i2c_WaitAck() != 0)
  {
    goto cmd_fail;
  }
  /* 第4步: 发送SUB */
  i2c_SendByte(reg);
  /* 第5步: 等待ACK */
  if (i2c_WaitAck() != 0)
  {
    goto cmd_fail;
  }
  /* 第6步: 循环发送DATA */
  for (i = 0; i < len; i++)
  {
    i2c_SendByte(bufp[i]); /* 发一个数据 */
    i2c_Ack();             /*发完一个数据后等待ACK*/
  }
  /* 第7步: 发送停止信号 */
  i2c_Stop();
  return 0;
cmd_fail:
  i2c_Stop();
  return 1;
}

总而言之,STM32软件I2C是一种非常重要的通信方式,尤其适用于那些不适合使用硬件I2C的应用场景。希望本文对你了解STM32软件I2C功能有所帮助,如果有不理解的地方欢迎私信留言。文章来源地址https://www.toymoban.com/news/detail-741649.html

到了这里,关于【STM32】软件I2C的使用 —— 看这一篇就够了(附代码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【stm32】软件I2C读写MPU6050

    概况 首先建立通信层的.c和.h模块 在通信层里写好I2C底层的GPIO初始化 以及6个时序基本单元 起始、终值、发送一个字节、接收一个字节、发送应答、接收应答 写好I2C通信层之后,再建立MPU6050的.c和.h模块 基于I2C通信的模块,来实现指定地址读、指定地址写 再实现写寄存器对

    2024年04月26日
    浏览(50)
  • 云平台 stm32连接oneNET保姆级别教学只看这一篇就够了~

    ▬▬▬▬▬▶玩转oneNET平台◀▬▬▬▬▬ oneNET点击直达 你现在拥有了一个oneNET账号 如设备ID 鉴权信息 产品ID 以上三个保存好 在代码中需要用到 硬件准备方案1 EPS01S+烧录座 硬件准备方案2 ESP8266-NodeMcu 成本最低选第1个 手头上正好有2就选2个 现在正式开始进入固件烧录 获取

    2024年02月11日
    浏览(48)
  • 【STM32学习】——I2C通信协议&MPU6050姿态传感器&软件I2C读写MPU6050

    ​   目录 前言 一、I2C通信协议 1.简介 2.硬件电路设计 3.I2C时序(软件)

    2024年02月16日
    浏览(52)
  • 【STM32】STM32学习笔记-软件I2C读写MPU6050(33)

    I2C(Inter-Integrated Circuit)总线 是一种由NXP(原PHILIPS)公司开发的两线式串行总线,用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。 串行的 8 位双向数据传输位速率在标准模式下可

    2024年01月21日
    浏览(62)
  • STM32软件模拟I2C从机的实现方法

    在使用I2C通信时,一般会用到软件模拟I2C。目前网络上能搜索到的软件模拟I2C一般都是模拟I2C主机,很少有模拟I2C从机的例程。由于I2C主机在进行数据收发时,有明确的可预见性,也就是主机明确知道什么时候要进行数据的收发操作,而且I2C的同步时钟信号也是由主机产生的

    2024年02月01日
    浏览(60)
  • 云平台 stm32连接阿里云2023最新版本保姆级别教学只看这一篇就够了~

    ▬▬▬▬▬▶玩转阿里云◀▬▬▬▬▬ 阿里云平台点击直达 点击控制台 鼠标悬浮会出现下拉栏 点击物联网 再点击物联网平台 点击公共实例 新用户需要开通 开通需要五分钟的时间 点击创建产品 蓝色显眼字体 参数设置 仔细比对下图 点击查看产品详情 蓝色显眼字体 点击功

    2024年02月11日
    浏览(38)
  • STM32学习笔记(十)丨I2C通信(使用I2C实现MPU6050和STM32之间通信)

    ​  本次课程采用单片机型号为STM32F103C8T6。(鉴于笔者实验时身边只有STM32F103ZET6,故本次实验使基于ZET6进行的) ​  课程链接:江协科技 STM32入门教程   往期笔记链接:   STM32学习笔记(一)丨建立工程丨GPIO 通用输入输出   STM32学习笔记(二)丨STM32程序调试

    2024年01月19日
    浏览(57)
  • 01_STM32软件+硬件I2C读取MPU6050(HAL库)

    目录 1、I2C简介 2、I2C时序单元 2.1 起始条件 2.2 终止条件 2.3 发送一个字节 2.4 接收一个字节 2.5 发送应答 2.6 接收应答 3、I2C完整时序 3.1 指定地址写一个字节 3.2 当前地址读一个字节 3.2 指定地址读一个字节 4、简单软件I2C代码(HAL) 4.1 软件I2C 4.2 软件I2C读MPU6050寄存器 5、ST

    2024年04月17日
    浏览(47)
  • STM32 HAL FreeRTOS 硬件I2C 使用

    因为某个项目想要颜色识别,去识别球的颜色,但是又不想多来个摄像头,所以想尝试一下颜色传感器的方案。但是经过尝试,HAL库生成的 FreeRTOS 硬件 I2C 读写一直在报错。 刚好手头上有九轴陀螺仪的例程代码。最后用FreeRTOS 硬件 I2C 读取数据。 这里提到了阻塞式 HAL 函数(

    2024年02月20日
    浏览(49)
  • STM32 SHT40驱动源码(使用硬件I2C)

    目录 简介: SHT40.c: SHT40.h 测试结果:         SHT40是瑞士Sensirion公司推出的第四代温湿度传感器,内部集成加热器用于去除表面微小液滴。集成I2C接口,典型的相对湿度精度1.8%RH,典型温度精度0.2℃,运行在0-100%RH和-40-125℃的环境中。 主控:STM32H7B0VBT6 平台:STM32CubeIDE SHT4

    2024年03月19日
    浏览(72)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包