【STM32】RTT-Studio中HAL库开发教程七:IIC通信--EEPROM存储器FM24C04

文章目录

    • 一、简介
    • 二、模拟IIC时序
    • 三、读写流程
    • 四、完整代码
    • 五、测试验证

一、简介

  FM24C04D,4K串行EEPROM:内部32页,每个16字节,4K需要一个11位的数据字地址进行随机字寻址。FM24C04D提供4096位串行电可擦除和可编程只读存储器(EEPROM),大小为512字,每个8位,具有128位UID和16字节安全扇区。该设备被优化用于许多工业和商业应用中,其中低功率和低压操作是必不可少的。

芯片优点:FM24C04芯片手册

  • 低工作电压: VCC = 1.7V到5.5V
  • 2-线串行接口,施密特触发器,滤波输入噪声抑制,双向数据传输协议
  • 1 MHz(2.5V~5.5V)和400 kHz (1.7V)兼容性
  • 写保护针硬件数据保护

  安全扇区:FM24C04D提供了16字节的安全扇区,可以写入和(以后)永久锁定在只读模式。这些寄存器可以被系统制造商用来从存储主内存阵列分开存储安全性和其他重要信息
  唯一ID:FM24C04D使用了一个单独的内存块,其中包含一个工厂编程的128位唯一ID。通过以“1011”(Bh)序列开始设备地址字来访问此内存位置
在这里插入图片描述
数据存储器:
(1)第七位是内存页面地址位(P0),第八位是读/写操作选择位。
(2)地址位P0和<7:4>定义页面地址,A<3:0>定义字节地址。总共32页,每页16个字节。设备地址P0为0(0xA0)时,地址从第0页开始;设备地址为1(0xA2)时,地址从第16页开始写数据。
安全扇区:
(3)第七位并不在乎,第八位是读/写操作选择位。
(4)地址位<7:6>必须是00”,A<3:0>定义字节地址,其他位不在乎。
唯一性标识:
(5)第七位并不在乎,第八位是读/写操作选择位,它必须是“1”。
(6)地址位<7:6>必须是10”,A<3:0>定义字节地址,其他位不在乎。


二、模拟IIC时序

1.起始信号: 在SCL为高电平期间,SDA由高变低;
在这里插入图片描述

/*** @brief 产生I2C起始信号* @note*       请参考I2C通信协议,I2C起始信号:当SCL为高电平时,SDA由高变低*       如下图所示:方框部分表示I2C起始信号*           _____     |*        __|__   |    |  ___  ___  ___  ___  ___  ___  ___  ___*   SDA:   |  \__|____|_/   \/   \/   \/   \/   \/   \/   \/   \     /*          |     |    | \___/\___/\___/\___/\___/\___/\___/\___/\___/*        __|_____|_   |   _    _    _    _    _    _    _    _    _*   SCL:   |     | \__|__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \_*          |_____|    |*           start         D7   D6   D5   D4   D3   D2   D1   D0   ACK*/
void I2C_Start(void)
{uint32_t i2c_delay = i2c_speed;SDA_OUT();      // SDA设置为输出I2C_SDA_HIGH;   // SDA: 高I2C_SCL_HIGH;   // SCL: 高BSP_I2C_Delay(i2c_delay);   // 延时>4.7usI2C_SDA_LOW;                // 当SCL为高电平时,SDA由高变低BSP_I2C_Delay(i2c_delay);   // 延时>4usI2C_SCL_LOW;    // SCL变低,钳住I2C总线,准备发送或接收数据
}

2.停止信号: 在SCL为高电平期间,SDA由低变高。
在这里插入图片描述

/*** @brief 产生I2C停止信号* @note*       请参考I2C通信协议,I2C停止信号:当SCL为高电平时,SDA由低变高*       发送完STOP信号后,SCL和SDA都为高电平,即释放了I2C总线*       如下图所示:方框部分表示I2C起始信号*                                    _____*         ___  ___  ___  ___        |   __|_*   SDA: /   \/   \/   \/   \       |  /  |*        \___/\___/\___/\___/\______|_/   |*         _    _    _    _    _    _|_____|_*   SCL: / \__/ \__/ \__/ \__/ \__/ |     |*                                   |_____|*        D3   D2   D1   D0   ACK     stop*/
void I2C_Stop(void)
{uint32_t i2c_delay = i2c_speed;SDA_OUT();                  // SDA设置为输出I2C_SDA_LOW;                // SDA低电平I2C_SCL_HIGH;               // SCL高电平BSP_I2C_Delay(i2c_delay);   // 延时>4usI2C_SDA_HIGH;               // STOP:当SCL为高电平时,SDA由低变高BSP_I2C_Delay(i2c_delay);   // 延时>4.7us
}

3.等待ACK应答信号: 在8位数据信号传输完成之后,第9位就是应答信号。应答信号分为应答和非应答,应答为低电平,非应答为高电平。
在这里插入图片描述

/*** @brief  等待ACK应答信号* @retval 1 - 未接收到应答信号ACK;0 - 接收到应答信号ACK* @note*       请参考I2C通信协议,检测ACK应答信号:当SCL为高电平时,读取SDA为低电平*       如下图所示:方框部分表示I2C起始信号*                             ________     _____*         ___  ___  ___  ___ | _      |   |   __|_*   SDA: /   \/   \/   \/   \|/ \     |   |  /  |*        \___/\___/\___/\___/|   \____|___|_/   |*         _    _    _    _   | _____  |  _|_____|*   SCL: / \__/ \__/ \__/ \__|/     \_|_/ |     |*                            |________|   |_____|*        D3   D2   D1   D0      ACK        stop*/
uint8_t I2C_Wait_ACK(void)
{uint32_t i2c_delay = i2c_speed;uint8_t timeout = 0;SDA_IN();           // SDA设置为输入I2C_SDA_HIGH;       // SDA上拉输入I2C_SCL_HIGH;       // SCL设置为高电平BSP_I2C_Delay(i2c_delay);while (I2C_SDA_READ() == 1)  // 等待ACK{if (timeout++ > 250){I2C_Stop();return 1;}}I2C_SCL_LOW;        // 钳住I2C总线:时钟信号设为低电平return 0;
}/*** @brief  产生ACK应答信号* @note*       请参考I2C通信协议,产生ACK应答信号: 在SDA为低电平时,SCL产生一个正脉冲*       如下图所示:方框部分表示I2C起始信号*                             _____     _____*         ___  ___  ___  ___ |     |   |   __|_*   SDA: /   \/   \/   \/   \|     |   |  /  |*        \___/\___/\___/\___/|\____|___|_/   |*         _    _    _    _   |  _  |  _|_____|_*   SCL: / \__/ \__/ \__/ \__|_/ \_|_/ |     |*                            |_____|   |_____|*        D3   D2   D1   D0     ACK      stop*/
void I2C_ACK(void)
{uint32_t i2c_delay = i2c_speed;I2C_SCL_LOW;                // 低电平SDA_OUT();                  // 设置SDA为输出I2C_SDA_LOW;                // ACK信号BSP_I2C_Delay(i2c_delay);   // 延时>4usI2C_SCL_HIGH;               // 高电平BSP_I2C_Delay(i2c_delay);   // 延时>4usI2C_SCL_LOW;                // 钳住I2C总线:时钟信号设为低电平
}/*** @brief  产生非应答信号NACK* @note*       请参考I2C通信协议,产生ACK应答信号: 在SDA为高电平时,SCL产生一个正脉冲*       如下图所示:方框部分表示I2C起始信号*                             _____      ______*         ___  ___  ___  ___ | ____|_   |    __|_*   SDA: /   \/   \/   \/   \|/    | \  |   /  |*        \___/\___/\___/\___/|     |  \_|__/   |*         _    _    _    _   |  _  |  __|______|_*   SCL: / \__/ \__/ \__/ \__|_/ \_|_/  |      |*                            |_____|    |______|*        D3   D2   D1   D0    NACK        stop*/
void I2C_NACK(void)
{uint32_t i2c_delay = i2c_speed;I2C_SCL_LOW;                // 低电平SDA_OUT();                  // SDA设置为输出I2C_SDA_HIGH;               // NACK信号BSP_I2C_Delay(i2c_delay);   // 延时>4usI2C_SCL_HIGH;               // 高电平BSP_I2C_Delay(i2c_delay);   // 延时>4usI2C_SCL_LOW;                // 钳住I2C总线:时钟信号设为低电平
}

4.读写一个字节数据

/*** @brief  I2C发送一个字节* @note*       请参考I2C通信协议,产生ACK应答信号: 在SDA为高电平时,SCL产生一个正脉冲*       如下图所示:方框部分表示I2C起始信号**           _____     |<------------I2C数据发送周期------------>|*        __|__   |    |  ___  ___  ___  ___  ___  ___  ___  ___ | _*   SDA:   |  \__|____|_/   \/   \/   \/   \/   \/   \/   \/   \|/*          |     |    | \___/\___/\___/\___/\___/\___/\___/\___/|\_*        __|_____|_   |   _    _    _    _    _    _    _    _  |*   SCL:   |     | \__|__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \_|_*          |_____|    |                                         |*           start     |   D7   D6   D5   D4   D3   D2   D1   D0 |*/
void I2C_Send_Byte(uint8_t data)
{uint8_t i = 0;uint32_t i2c_delay = i2c_speed;SDA_OUT();                          // SDA设为输出I2C_SCL_LOW;                        // 钳住I2C总线:SCL设为低电平for (i = 0; i < 8; i++){if (data & 0x80)I2C_SDA_HIGH;               // 高位先传elseI2C_SDA_LOW;BSP_I2C_Delay(i2c_delay);       // 延时>4usI2C_SCL_HIGH;                   // 在SCL上产生一个正脉冲BSP_I2C_Delay(i2c_delay);       // 延时>4usI2C_SCL_LOW;BSP_I2C_Delay(i2c_delay / 3);   // 延时>1usdata <<= 1;                     // 右移一位}
}/*** @brief  从I2C读取一个字节* @param  ack : 0 - NACK; 1 - ACK* @retval 接收到的数据* @note*       请参考I2C通信协议,产生ACK应答信号: 在SDA为高电平时,SCL产生一个正脉冲*       如下图所示:方框部分表示I2C起始信号**           _____     |<------------I2C数据读取周期(ACK)------------>|*        __|__   |    |  ___  ___  ___  ___  ___  ___  ___  ___      |*   SDA:   |  \__|____|_/   \/   \/   \/   \/   \/   \/   \/   \     |*          |     |    | \___/\___/\___/\___/\___/\___/\___/\___/\____|_*        __|_____|_   |   _    _    _    _    _    _    _    _    _  |*   SCL:   |     | \__|__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \_|_*          |_____|    |                                              |*           start     |   D7   D6   D5   D4   D3   D2   D1   D0   ACK**           _____     |<------------I2C数据读取周期(NACK)----------->|*        __|__   |    |  ___  ___  ___  ___  ___  ___  ___  ___  ____|_*   SDA:   |  \__|____|_/   \/   \/   \/   \/   \/   \/   \/   \/    |*          |     |    | \___/\___/\___/\___/\___/\___/\___/\___/     |*        __|_____|_   |   _    _    _    _    _    _    _    _    _  |*   SCL:   |     | \__|__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \_|_*          |_____|    |                                              |*           start     |   D7   D6   D5   D4   D3   D2   D1   D0  NACK*/
uint8_t I2C_Read_Byte(uint8_t ack)
{uint8_t i, receive = 0x00;uint32_t i2c_delay = i2c_speed;I2C_SCL_LOW;                    // SCL低电平SDA_IN();                       // SDA设置为输入for (i = 0; i < 8; i++){BSP_I2C_Delay(i2c_delay);I2C_SCL_HIGH;               // 高电平BSP_I2C_Delay(i2c_delay);receive <<= 1;if (I2C_SDA_READ())receive |= 1;           // 高位在前I2C_SCL_LOW;}if (ack == 0)I2C_NACK();                 // 发送NACKelseI2C_ACK();                  // 发送ACKreturn receive;                 // 返回接收到的数据
}

三、读写流程

1.写数据流程
  START–>DEVICE ADDRESS(写)–>ACK–>WORD ADDRESS–>ACK–>DATA–>ACK–>STOP

  • START:起始信号
  • DEVICE-ADDRESS:设备地址,以及页地址设置
  • ACK:从设备发送的应答信号(低电平),主机接收应答。
  • WORD-ADDRESS:写的数据存入的地址,即页地址
  • DATA:写入的数据
  • STOP:结束信号
    在这里插入图片描述

2.读数据流程
  START–>DEVICE ADDRESS(写)–>ACK–>WORD ADDRESS–>START–>DEVICE ADDRESS(读)–>ACK–>DATA–>NACK–>STOP。

  • START:起始信号
  • DEVICE-ADDRESS:设备地址,以及页地址设置
  • ACK:从设备发送的应答信号(低电平),主机接收应答。
  • WORD-ADDRESS:写的数据存入的地址,即页地址
  • DATA:写入的数据
  • NACK:主机发送的非应答信号,高电平有效。
  • STOP:结束信号
    在这里插入图片描述

四、完整代码

1.fm24c04.h
  主要是定义IIC通信的引脚,以及引脚高低电平变化,设置数据线的模式。

#ifndef APPLICATIONS_FM24C04_H_
#define APPLICATIONS_FM24C04_H_#include <drv_common.h>
#include <string.h>/**====================================================###### 宏定义 ######==================================================*/
/* 定义读写SCL和SDA的宏 */
#define I2C_SDA_PIN     GET_PIN(B, 11)
#define I2C_SCL_PIN     GET_PIN(B, 10)#define I2C_SCL_HIGH    rt_pin_write(I2C_SCL_PIN, 1)
#define I2C_SCL_LOW     rt_pin_write(I2C_SCL_PIN, 0)#define I2C_SDA_HIGH    rt_pin_write(I2C_SDA_PIN, 1)
#define I2C_SDA_LOW     rt_pin_write(I2C_SDA_PIN, 0)#define I2C_SDA_READ()  rt_pin_read(I2C_SDA_PIN)#define SDA_IN()        rt_pin_mode(I2C_SDA_PIN, PIN_MODE_INPUT);
#define SDA_OUT()       rt_pin_mode(I2C_SDA_PIN, PIN_MODE_OUTPUT);/* 根据处理器速度设置,这里处理器速度是72MHz */
#define I2C_SPEED_1K        5000/* 状态枚举 */
typedef enum
{I2C_SUCCESS = 0,I2C_TIMEOUT,I2C_ERROR,} I2C_StatusTypeDef;uint32_t i2c_speed;                             // I2C访问速度 = I2C_SPEED_1K / i2c_speed
/**====================================================#######  END  #######=================================================*//**=========================================##### 依照I2C协议编写的时序函数 #####=========================================*/
extern void BSP_I2C_Init(void);                 // 初始化I2C的IO口
extern void I2C_Start(void);                    // 发送I2C开始信号
extern void I2C_Stop(void);                     // 发送I2C停止信号
extern uint8_t I2C_Wait_ACK(void);              // I2C等待ACK信号
extern void I2C_ACK(void);                      // I2C发送ACK信号
extern void I2C_NACK(void);                     // I2C不发送ACK信号
extern void I2C_Send_Byte(uint8_t data);        // I2C发送一个字节
extern uint8_t I2C_Read_Byte(uint8_t ack);      // I2C读取一个字节
extern uint16_t I2C_SetSpeed(uint16_t speed);   // 设置I2C速度(1Kbps~400Kbps,speed单位,Kbps)
/**================================================#######  END  #######=====================================================*//**============================================##### 封装好的I2C读写函数 #####===========================================*/
//具体到某一个器件,请仔细阅读器件规格书关于I2C部分的说明,因为某些器件在I2C的读写操作会
//有一些差异,下面的代码我们在绝大多数的I2C器件中,都是验证OK的!
I2C_StatusTypeDef I2C_WriteOneByte(uint8_t DevAddr, uint8_t DataAddr, uint8_t Data);              // 向I2C从设备写入一个字节
I2C_StatusTypeDef I2C_WriteBurst(uint8_t DevAddr, uint8_t DataAddr, uint8_t* pData, uint32_t Num);// 向I2C从设备连续写入Num个字节
I2C_StatusTypeDef I2C_ReadOneByte(uint8_t DevAddr, uint8_t DataAddr, uint8_t* Data);              // 从I2C从设备读取一个字节
I2C_StatusTypeDef I2C_ReadBurst(uint8_t DevAddr, uint8_t DataAddr, uint8_t* pData, uint32_t Num); // 从I2C设备连续读取Num个字节
I2C_StatusTypeDef I2C_WriteBit(uint8_t DevAddr, uint8_t DataAddr, uint8_t Bitx, uint8_t BitSet);  // 设置数据的某一位
/**================================================#######  END  #######=====================================================*/#endif /* _bsp_FRAM_H */

2.fm24c04.c
  主要实现IIC的模式时序,编写发送和接收字节的函数,同时根据读取和写入的流程,编写读取和写入的函数,便于在存储的时候直接使用。

#include "fm24c04.h"/*==========================================##### 依照I2C协议编写的时序函数 #####=========================================*/
/*** @brief 延时函数*/
static void BSP_I2C_Delay(uint32_t us)
{while (us--);
}/*** @brief  设置I2C速度* @param  speed : I2C速度,单位Kbps* @retval 返回设置前的I2C速度* @note   I2C速度设置范围是: 1Kbps ~ 400Kbps*/
uint16_t I2C_SetSpeed(uint16_t speed)
{uint16_t temp;// I2C速度必须小于400Kbps,大于 1Kbpsif ((speed > 400) || (speed < 1))return 0;temp = I2C_SPEED_1K / i2c_speed;    //备份原来的i2c速度i2c_speed = I2C_SPEED_1K / speed;   //设置新的i2c速度return temp;    //返回设置前的i2c速度
}/*** @brief 产生I2C起始信号* @note*       请参考I2C通信协议,I2C起始信号:当SCL为高电平时,SDA由高变低*       如下图所示:方框部分表示I2C起始信号*           _____     |*        __|__   |    |  ___  ___  ___  ___  ___  ___  ___  ___*   SDA:   |  \__|____|_/   \/   \/   \/   \/   \/   \/   \/   \     /*          |     |    | \___/\___/\___/\___/\___/\___/\___/\___/\___/*        __|_____|_   |   _    _    _    _    _    _    _    _    _*   SCL:   |     | \__|__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \_*          |_____|    |*           start         D7   D6   D5   D4   D3   D2   D1   D0   ACK*/
void I2C_Start(void)
{uint32_t i2c_delay = i2c_speed;SDA_OUT();      // SDA设置为输出I2C_SDA_HIGH;   // SDA: 高I2C_SCL_HIGH;   // SCL: 高BSP_I2C_Delay(i2c_delay);   // 延时>4.7usI2C_SDA_LOW;                // 当SCL为高电平时,SDA由高变低BSP_I2C_Delay(i2c_delay);   // 延时>4usI2C_SCL_LOW;    // SCL变低,钳住I2C总线,准备发送或接收数据
}/*** @brief 产生I2C停止信号* @note*       请参考I2C通信协议,I2C停止信号:当SCL为高电平时,SDA由低变高*       发送完STOP信号后,SCL和SDA都为高电平,即释放了I2C总线*       如下图所示:方框部分表示I2C起始信号*                                    _____*         ___  ___  ___  ___        |   __|_*   SDA: /   \/   \/   \/   \       |  /  |*        \___/\___/\___/\___/\______|_/   |*         _    _    _    _    _    _|_____|_*   SCL: / \__/ \__/ \__/ \__/ \__/ |     |*                                   |_____|*        D3   D2   D1   D0   ACK     stop*/
void I2C_Stop(void)
{uint32_t i2c_delay = i2c_speed;SDA_OUT();                  // SDA设置为输出I2C_SDA_LOW;                // SDA低电平I2C_SCL_HIGH;               // SCL高电平BSP_I2C_Delay(i2c_delay);   // 延时>4usI2C_SDA_HIGH;               // STOP:当SCL为高电平时,SDA由低变高BSP_I2C_Delay(i2c_delay);   // 延时>4.7us
}/*** @brief  等待ACK应答信号* @retval 1 - 未接收到应答信号ACK;0 - 接收到应答信号ACK* @note*       请参考I2C通信协议,检测ACK应答信号:当SCL为高电平时,读取SDA为低电平*       如下图所示:方框部分表示I2C起始信号*                             ________     _____*         ___  ___  ___  ___ | _      |   |   __|_*   SDA: /   \/   \/   \/   \|/ \     |   |  /  |*        \___/\___/\___/\___/|   \____|___|_/   |*         _    _    _    _   | _____  |  _|_____|*   SCL: / \__/ \__/ \__/ \__|/     \_|_/ |     |*                            |________|   |_____|*        D3   D2   D1   D0      ACK        stop*/
uint8_t I2C_Wait_ACK(void)
{uint32_t i2c_delay = i2c_speed;uint8_t timeout = 0;SDA_IN();           // SDA设置为输入I2C_SDA_HIGH;       // SDA上拉输入I2C_SCL_HIGH;       // SCL设置为高电平BSP_I2C_Delay(i2c_delay);while (I2C_SDA_READ() == 1)  // 等待ACK{if (timeout++ > 250){I2C_Stop();return 1;}}I2C_SCL_LOW;        // 钳住I2C总线:时钟信号设为低电平return 0;
}/*** @brief  产生ACK应答信号* @note*       请参考I2C通信协议,产生ACK应答信号: 在SDA为低电平时,SCL产生一个正脉冲*       如下图所示:方框部分表示I2C起始信号*                             _____     _____*         ___  ___  ___  ___ |     |   |   __|_*   SDA: /   \/   \/   \/   \|     |   |  /  |*        \___/\___/\___/\___/|\____|___|_/   |*         _    _    _    _   |  _  |  _|_____|_*   SCL: / \__/ \__/ \__/ \__|_/ \_|_/ |     |*                            |_____|   |_____|*        D3   D2   D1   D0     ACK      stop*/
void I2C_ACK(void)
{uint32_t i2c_delay = i2c_speed;I2C_SCL_LOW;                // 低电平SDA_OUT();                  // 设置SDA为输出I2C_SDA_LOW;                // ACK信号BSP_I2C_Delay(i2c_delay);   // 延时>4usI2C_SCL_HIGH;               // 高电平BSP_I2C_Delay(i2c_delay);   // 延时>4usI2C_SCL_LOW;                // 钳住I2C总线:时钟信号设为低电平
}/*** @brief  产生非应答信号NACK* @note*       请参考I2C通信协议,产生ACK应答信号: 在SDA为高电平时,SCL产生一个正脉冲*       如下图所示:方框部分表示I2C起始信号*                             _____      ______*         ___  ___  ___  ___ | ____|_   |    __|_*   SDA: /   \/   \/   \/   \|/    | \  |   /  |*        \___/\___/\___/\___/|     |  \_|__/   |*         _    _    _    _   |  _  |  __|______|_*   SCL: / \__/ \__/ \__/ \__|_/ \_|_/  |      |*                            |_____|    |______|*        D3   D2   D1   D0    NACK        stop*/
void I2C_NACK(void)
{uint32_t i2c_delay = i2c_speed;I2C_SCL_LOW;                // 低电平SDA_OUT();                  // SDA设置为输出I2C_SDA_HIGH;               // NACK信号BSP_I2C_Delay(i2c_delay);   // 延时>4usI2C_SCL_HIGH;               // 高电平BSP_I2C_Delay(i2c_delay);   // 延时>4usI2C_SCL_LOW;                // 钳住I2C总线:时钟信号设为低电平
}/*** @brief  I2C发送一个字节* @note*       请参考I2C通信协议,产生ACK应答信号: 在SDA为高电平时,SCL产生一个正脉冲*       如下图所示:方框部分表示I2C起始信号**           _____     |<------------I2C数据发送周期------------>|*        __|__   |    |  ___  ___  ___  ___  ___  ___  ___  ___ | _*   SDA:   |  \__|____|_/   \/   \/   \/   \/   \/   \/   \/   \|/*          |     |    | \___/\___/\___/\___/\___/\___/\___/\___/|\_*        __|_____|_   |   _    _    _    _    _    _    _    _  |*   SCL:   |     | \__|__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \_|_*          |_____|    |                                         |*           start     |   D7   D6   D5   D4   D3   D2   D1   D0 |*/
void I2C_Send_Byte(uint8_t data)
{uint8_t i = 0;uint32_t i2c_delay = i2c_speed;SDA_OUT();                          // SDA设为输出I2C_SCL_LOW;                        // 钳住I2C总线:SCL设为低电平for (i = 0; i < 8; i++){if (data & 0x80)I2C_SDA_HIGH;               // 高位先传elseI2C_SDA_LOW;BSP_I2C_Delay(i2c_delay);       // 延时>4usI2C_SCL_HIGH;                   // 在SCL上产生一个正脉冲BSP_I2C_Delay(i2c_delay);       // 延时>4usI2C_SCL_LOW;BSP_I2C_Delay(i2c_delay / 3);   // 延时>1usdata <<= 1;                     // 右移一位}
}/*** @brief  从I2C读取一个字节* @param  ack : 0 - NACK; 1 - ACK* @retval 接收到的数据* @note*       请参考I2C通信协议,产生ACK应答信号: 在SDA为高电平时,SCL产生一个正脉冲*       如下图所示:方框部分表示I2C起始信号**           _____     |<------------I2C数据读取周期(ACK)------------>|*        __|__   |    |  ___  ___  ___  ___  ___  ___  ___  ___      |*   SDA:   |  \__|____|_/   \/   \/   \/   \/   \/   \/   \/   \     |*          |     |    | \___/\___/\___/\___/\___/\___/\___/\___/\____|_*        __|_____|_   |   _    _    _    _    _    _    _    _    _  |*   SCL:   |     | \__|__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \_|_*          |_____|    |                                              |*           start     |   D7   D6   D5   D4   D3   D2   D1   D0   ACK**           _____     |<------------I2C数据读取周期(NACK)----------->|*        __|__   |    |  ___  ___  ___  ___  ___  ___  ___  ___  ____|_*   SDA:   |  \__|____|_/   \/   \/   \/   \/   \/   \/   \/   \/    |*          |     |    | \___/\___/\___/\___/\___/\___/\___/\___/     |*        __|_____|_   |   _    _    _    _    _    _    _    _    _  |*   SCL:   |     | \__|__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \_|_*          |_____|    |                                              |*           start     |   D7   D6   D5   D4   D3   D2   D1   D0  NACK*/
uint8_t I2C_Read_Byte(uint8_t ack)
{uint8_t i, receive = 0x00;uint32_t i2c_delay = i2c_speed;I2C_SCL_LOW;                    // SCL低电平SDA_IN();                       // SDA设置为输入for (i = 0; i < 8; i++){BSP_I2C_Delay(i2c_delay);I2C_SCL_HIGH;               // 高电平BSP_I2C_Delay(i2c_delay);receive <<= 1;if (I2C_SDA_READ())receive |= 1;           // 高位在前I2C_SCL_LOW;}if (ack == 0)I2C_NACK();                 // 发送NACKelseI2C_ACK();                  // 发送ACKreturn receive;                 // 返回接收到的数据
}
/*=================================================#######  END  #######=====================================================*//*=============================================##### 封装好的I2C读写函数 #####===========================================*/
/*** @brief  模拟I2C接口初始化* @note*       SCL:    PB10*       SDA:    PB11*/
void BSP_I2C_Init(void)
{I2C_SetSpeed(100); //设置I2C访问速度为100Kbpsrt_pin_mode(I2C_SCL_PIN, PIN_MODE_OUTPUT);rt_pin_mode(I2C_SDA_PIN, PIN_MODE_OUTPUT);I2C_SCL_HIGH;I2C_SDA_HIGH;
}/*** @brief  向设备指定地址写入单一Byte数据* @param  DevAddr : I2C从设备地址* @param  DataAddr: 需要访问的设备内地址(如寄存器地址,EEPROM地址等)* @param  Data    : 写入的数据* @retval I2C访问的结果: I2C_SUCCESS / I2C_TIMEOUT / I2C_ERROR* @note*   1 - 设备地址DevAddr高7bit是固定的,最低位是读/写(R/W)位,1为读,0为写*   2 - 时序:*           _______________________________________*          | |         |   |        |   |    |   | |*   Master:|S|DevAddr+W|   |DataAddr|   |Data|   |P|*          |_|_________|___|________|___|____|___|_|*           _______________________________________*          | |         |   |        |   |    |   | |*   Slave: | |         |ACK|        |ACK|    |ACK| |*          |_|_________|___|________|___|____|___|_|*/
I2C_StatusTypeDef I2C_WriteOneByte(uint8_t DevAddr, uint8_t DataAddr, uint8_t Data)
{I2C_Start();                          // Master发送起始信号I2C_Send_Byte(DevAddr);               // Master发送从设备地址if (I2C_Wait_ACK())return I2C_TIMEOUT;               // 等待ACK超时错误#ifdef FM24CL64I2C_Send_Byte(DataAddr >> 8);     // 发送高8位数据地址if(I2C_Wait_ACK()){return I2C_TIMEOUT;           // 等待ACK超时错误}I2C_Send_Byte(DataAddr);          // 发送低八位数据地址if(I2C_Wait_ACK()){return I2C_TIMEOUT;           // 等待ACK超时错误}
#elseI2C_Send_Byte(DataAddr);              // 发送低八位数据地址if (I2C_Wait_ACK())return I2C_TIMEOUT;               // 等待ACK超时错误
#endifI2C_Send_Byte(Data);                  // 发送数据if (I2C_Wait_ACK())return I2C_TIMEOUT;               // 等待ACK超时错误I2C_Stop();                           // 发送停止信号return I2C_SUCCESS;
}/*** @brief  向设备指定地址连续写入数据(Burst写模式)* @param  DevAddr : I2C从设备地址* @param  DataAddr: 需要访问的设备内地址(如寄存器地址,EEPROM地址等)*                   对于Burst模式,DataAddr一般是设备的FIFO,缓存,或存储设备的数据地址* @param  *pData  : 写入的数据首地址* @param     Num  : 连续写入的数据个数* @retval I2C访问的结果: I2C_SUCCESS / I2C_TIMEOUT / I2C_ERROR* @note*   1 - 设备地址DevAddr高7bit是固定的,最低为是读/写(R/W)位,1为读,0为写*       2 - 时序:*           ____________________________________________________*          | |         |   |        |   |    |   |   |    |   | |*   Master:|S|DevAddr+W|   |DataAddr|   |Data|   |...|Data|   |P|*          |_|_________|___|________|___|____|___|___|____|___|_|*           ____________________________________________________*          | |         |   |        |   |    |   |   |    |   | |*   Slave: | |         |ACK|        |ACK|    |ACK|...|    |ACK| |*          |_|_________|___|________|___|____|___|___|____|___|_|*/
I2C_StatusTypeDef I2C_WriteBurst(uint8_t DevAddr, uint8_t DataAddr, uint8_t* pData, uint32_t Num)
{uint32_t i = 0;I2C_Start();                                // Master发送起始信号I2C_Send_Byte(DevAddr);                     // Master发送从设备地址if (I2C_Wait_ACK())return I2C_TIMEOUT;                     // 等待ACK超时错误#ifdef FM24CL64I2C_Send_Byte(DataAddr >> 8);           // 发送高8位数据地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;  // 等待ACK超时错误I2C_Send_Byte(DataAddr);                // 发送低八位数据地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;  // 等待ACK超时错误
#elseI2C_Send_Byte(DataAddr);                    // 发送低八位数据地址if (I2C_Wait_ACK())return I2C_TIMEOUT;                     // 等待ACK超时错误
#endiffor (i = 0; i < Num; i++){I2C_Send_Byte(*(pData + i));            // 发送数据if (I2C_Wait_ACK())return I2C_TIMEOUT;                 // 等待ACK超时错误}I2C_Stop();                                 // 发送停止信号return I2C_SUCCESS;
}/*** @brief  从指定设备读取1Byte数据* @param  DevAddr : I2C从设备地址* @param  DataAddr: 需要访问的设备内地址(如寄存器地址,EEPROM地址等)* @param  *Data   : 数据的存放地址* @retval I2C访问的结果: I2C_SUCCESS / I2C_TIMEOUT / I2C_ERROR* @note*   1 - 设备地址DevAddr高7bit是固定的,最低为是读/写(R/W)位,1为读,0为写*       2 - 时序:*           _________________________________________________________*          | |         |   |        |    | |         |   |    |    | |*   Master:|S|DevAddr+W|   |DataAddr|    |S|DevAddr+R|   |    |NACK|P|*          |_|_________|___|________|____|_|_________|___|____|____|_|*           _________________________________________________________*          | |         |   |        |    | |         |   |    |    | |*   Slave: | |         |ACK|        |ACK | |         |ACK|Data|    | |*          |_|_________|___|________|____|_|_________|___|____|____|_|*/
I2C_StatusTypeDef I2C_ReadOneByte(uint8_t DevAddr, uint8_t DataAddr, uint8_t* Data)
{I2C_Start();                                // Master发送起始信号I2C_Send_Byte(DevAddr);                     // Master发送从设备地址if (I2C_Wait_ACK())return I2C_TIMEOUT;                     // 等待ACK超时错误#ifdef FM24CL64I2C_Send_Byte(DataAddr >> 8);           // 发送高8位数据地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;  // 等待ACK超时错误I2C_Send_Byte(DataAddr);                // 发送低八位数据地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;  // 等待ACK超时错误
#elseI2C_Send_Byte(DataAddr);                    // 发送低八位数据地址if (I2C_Wait_ACK())return I2C_TIMEOUT;                     // 等待ACK超时错误
#endifI2C_Start();                                // Master发送起始信号I2C_Send_Byte(DevAddr + 1);                 // Master发送从设备读地址if (I2C_Wait_ACK())return I2C_TIMEOUT;                     // 等待ACK超时错误*Data = I2C_Read_Byte(0);                   // 读数据,NACKI2C_Stop();                                 // 发送停止信号return I2C_SUCCESS;
}/*** @brief  向设备指定地址连续读取数据(Burst写模式)* @param  DevAddr : I2C从设备地址* @param  DataAddr: 需要访问的设备内地址(如寄存器地址,EEPROM地址等)*                   对于Burst模式,DataAddr一般是设备的FIFO,缓存,或存储设备的数据地址* @param  *pData  : 写入的数据首地址* @param     Num  : 连续写入的数据个数* @retval I2C访问的结果: I2C_SUCCESS / I2C_TIMEOUT / I2C_ERROR* @note*   1 - 设备地址DevAddr高7bit是固定的,最低为是读/写(R/W)位,1为读,0为写*       2 - 时序:*           _____________________________________________________________________*          | |         |   |        |   | |         |   |    |   |   |    |    | |*   Master:|S|DevAddr+W|   |DataAddr|   |S|DevAddr+R|   |    |ACK|...|    |NACK|P|*          |_|_________|___|________|___|_|_________|___|____|___|___|____|____|_|*           _____________________________________________________________________*          | |         |   |        |   | |         |   |    |   |   |    |    | |*   Slave: | |         |ACK|        |ACK| |         |ACK|Data|   |...|Data|    | |*          |_|_________|___|________|___|_|_________|___|____|___|___|____|____|_|*/
I2C_StatusTypeDef I2C_ReadBurst(uint8_t DevAddr, uint8_t DataAddr, uint8_t* pData, uint32_t Num)
{uint32_t i = 0;I2C_Start();                                // Master发送起始信号I2C_Send_Byte(DevAddr);                     // Master发送从设备地址if (I2C_Wait_ACK())return I2C_TIMEOUT;                     // 等待ACK超时错误#ifdef FM24CL64I2C_Send_Byte(DataAddr >> 8);           // 发送高8位数据地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;  // 等待ACK超时错误I2C_Send_Byte(DataAddr);                // 发送低八位数据地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;  // 等待ACK超时错误
#elseI2C_Send_Byte(DataAddr);                    // 发送低八位数据地址if (I2C_Wait_ACK())return I2C_TIMEOUT;                     // 等待ACK超时错误
#endifI2C_Start();                                // Master发送起始信号I2C_Send_Byte(DevAddr + 1);                 // Master发送从设备读地址if (I2C_Wait_ACK())return I2C_TIMEOUT;                     // 等待ACK超时错误for (i = 0; i < (Num - 1); i++){*(pData + i) = I2C_Read_Byte(1);        // 读数据,ACK}*(pData + i) = I2C_Read_Byte(0);            // 读数据,NACKI2C_Stop();                                 // 发送停止信号return I2C_SUCCESS;
}/*** @brief  设置数据的某一位* @param  DevAddr : I2C从设备地址* @param  DataAddr: 需要访问的设备内地址(如寄存器地址,EEPROM地址等)* @param  Bitx  : 第几位* @param  BitSet: 需要设置的值* @retval I2C访问的结果: I2C_SUCCESS / I2C_TIMEOUT / I2C_ERROR* @note*/
I2C_StatusTypeDef I2C_WriteBit(uint8_t DevAddr, uint8_t DataAddr, uint8_t Bitx, uint8_t BitSet)
{I2C_StatusTypeDef status = I2C_ERROR;uint8_t tempdata = 0;status = I2C_ReadOneByte(DevAddr, DataAddr, &tempdata); // 获取原有数据if (status != I2C_SUCCESS)return status;                                      // I2C错误,则返回tempdata &= ~(1 << Bitx);                               // 将要设定的位清零tempdata |= (BitSet << Bitx);                           // 设置指定的bitstatus = I2C_WriteOneByte(DevAddr, DataAddr, tempdata); // 写入数据return status;                                          // 返回状态
}
/*=================================================#######  END  #######=====================================================*/

3.main.c
  主要是使用函数来进行数据的读取和写入。

#include <rtthread.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include "fm24c04.h"I2C_StatusTypeDef eeStatus = I2C_ERROR;
uint8_t wrEE[5] = {0x10, 0x11, 0x12, 0x13, 0x14};
uint8_t rdEE[5] = {0x00};int main(void)
{rt_pin_mode(GET_PIN(D, 13), PIN_MODE_OUTPUT);rt_pin_write(GET_PIN(D, 13), 1);BSP_I2C_Init();#if 1// 使用多字节写数据I2C_ReadBurst(0xA0, 0, rdEE, 5);I2C_WriteBurst(0xA0, 0, wrEE, 5);I2C_ReadBurst(0xA0, 0, rdEE, 5);#else// 使用单个字节写数据for (int i = 0; i < 5; i++){eeStatus = I2C_ReadOneByte(0xA0, i, &rdEE[i]);}for (int i = 0; i < 5; i++){eeStatus = I2C_WriteOneByte(0xA0, i, wrEE[i]);}eeStatus = I2C_ERROR;HAL_Delay(10);for (int i = 0; i < 5; i++){eeStatus = I2C_ReadOneByte(0xA0, i, &rdEE[i]);}#endifwhile (1){rt_thread_mdelay(1000);}
}

五、测试验证

  通过将测试代码下载到控制板中,先读取存储芯片中的数据,并打印出存储的数据,将接收到的数据用于实际参数中即可。在存储数据的时候,一般可以使用自动存储的方式,即定时进行存储或者当数据稳定后,将需要存储的数据进行存储,存储的时候,可以自己定义存储的位置,根据上述的内容,进行数据的分区。
  本次实验的IIC模拟时序函数,可以应用于大部分的模拟IIC时序,并且封装的读写函数也可以使用于大部分模拟IIC通信。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/53632.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2.1 HuggingFists系统架构(一)

系统架构 HuggingFists的前端主体开发语言为HtmlJavascript&#xff0c;后端的主体开发语言为Java。在算子部分有一定份额的Python代码&#xff0c;用于整合Python在数据处理方面强大能力。 功能架构 HuggingFists的功能架构如上&#xff0c;由下向上各层为&#xff1a; 数据存…

从文本图片到多模态:3D 数字人打开企业全域商业增长新空间

摘要&#xff1a;数字化与AI浪潮推动各行业变革&#xff0c;内容形式也发生巨变&#xff0c;从文本到多媒体的多模态表达&#xff0c;标志着内容创造走向升维。AIGC 3D生成技术的突飞猛进&#xff0c;彻底打破了传统3D内容生产门槛高、周期长、成本高昂的问题。将3D数字人的打造…

若依 Vue3 前端分离 3.8.8 版实现去除首页,登录后跳转至动态路由的第一个路由的页面

一、前言 某些项目可能并不需要首页&#xff0c;但在若依中想要实现不显示首页&#xff0c;并根据不同角色登录后跳转至该角色的第一个动态路由的页面需要自己实现&#xff0c;若依中没有实现该功能的特定代码。 二、代码 1. src\permission.js 在 src\permission.js 中添加…

VSCode编程配置再次总结

VScode 中C++编程再次总结 0.简介 1.配置总结 1.1 launch jsion文件 launch.json文件主要用于运行和调试的配置,具有程序启动调试功能。launch.json文件会启用tasks.json的任务,并能实现调试功能。 左侧任务栏的第四个选项运行和调试,点击创建launch.json {"conf…

探索 ShellGPT:终端中的 AI 助手

文章目录 探索 ShellGPT&#xff1a;终端中的 AI 助手背景介绍ShellGPT 是什么&#xff1f;如何安装 ShellGPT&#xff1f;简单的库函数使用方法场景应用常见问题及解决方案总结 探索 ShellGPT&#xff1a;终端中的 AI 助手 背景介绍 在当今快速发展的技术领域&#xff0c;命…

查询最近正在执行的sql(DM8 : 达梦数据库)

查询最近正在执行的sql DM8 : 达梦数据库 1 查询最近正在执行的sql2 更多达梦数据库学习使用列表 1 查询最近正在执行的sql 迁移数据时 , 业务无响应 , 查看最近活动的sql , 有没有迁移相关的表 , 通过最后的时间字段 , 判断会话是否正在执行 SELECT SESS_ID, SQL_TEXT, STATE…

redis哨兵启动出现 +sdown master mymaster 192.168.x.x

场景&#xff1a; 搭建好哨兵之后&#xff0c;哨兵一启动&#xff0c;过了30秒就会判断master sdown&#xff0c;但是检查配置是没有问题。 日志&#xff1a; Redis-master启动日志&#xff1a;没看到任何异常&#xff0c;所以master无异常 Redis-哨兵启动日志&#xff1a; …

(已解决)vscode如何传入argparse参数来调试/运行python程序

文章目录 前言调试传入参数运行传入参数延申 前言 以前&#xff0c;我都是用Pycharm专业版的&#xff0c;由于其好像在外网的时候&#xff0c;不能够通过VPN来连接内网服务器&#xff0c;我就改用了vscode。改用了之后&#xff0c;遇到一个问题&#xff0c;调试或者运行python…

QT编译之后的debug包下运行程序双击运行出现无法定位程序输入点__gxx_personlity_seh0于动态链接库

1.出现这个错误的原因是&#xff1a; 缺少如下文件&#xff1a; 2.解决方法&#xff1a; 在运行程序.exe所在的目录执行&#xff1a;windeployqt untitled.exe&#xff08;指打包的运行程序&#xff09; 3.如果执行提示由于找不到qt5core.dll,无法继续执行代码和无法识别win…

pgvector docker版安装;稀疏向量使用;psycopg2 python连接使用

参看: https://cloud.tencent.com/developer/article/2359831 https://hub.docker.com/r/pgvector/pgvector/tags https://github.com/pgvector/pgvector 一、安装 拉取0.7版本 docker pull pgvector/pgvector:0.7.4-pg16运行: docker run --name pgvector -v $(pwd)/dat…

linux StarRocks 安装

一、检查服务器是否支持avx2&#xff0c;如果执行命令显示空&#xff0c;则不支持&#xff0c;那么安装后无法启动BE cat /proc/cpuinfo |grep avx2我的支持显示如下&#xff1a; 二、安装 docker run -p 9030:9030 -p 8030:8030 -p 8040:8040 -p 9001:9000 --privilegedtrue…

随机掉落的项目足迹:Vue3 + wangEditor5富文本编辑器——toolbar.getConfig() 查看工具栏的默认配置

问题引入 小提示&#xff1a;问题引入是一个讲故事的废话环节&#xff0c;各位小伙伴可以直接跳到第二大点&#xff1a;问题解决 我的项目不需要在富文本编辑器中引入添加代码块的功能&#xff0c;于是我寻思在工具栏上把操作代码的菜单删一删 于是我来到官网文档工具栏配置 …

MyBatis 深层次 Map 自动嵌套:解锁数据映射新境界

在 Java 开发的征程中&#xff0c;MyBatis 以其强大的数据库映射功能而备受青睐。其中&#xff0c;深层次 Map 自动嵌套这一特性更是为开发者带来了诸多惊喜与便利。 前提&#xff1a;首先开启自动映射&#xff01; java和mysql命名规则不一样&#xff0c;每次在mybatis中起别…

我的AI工具箱Tauri版-MicrosoftTTS文本转语音

本教程基于自研的AI工具箱Tauri版进行MicrosoftTTS文本转语音服务。 MicrosoftTTS文本转语音服务 是自研的AI工具箱Tauri版中的一款功能模块&#xff0c;专为实现高效的文本转语音操作而设计。通过集成微软TTS服务&#xff0c;用户可以将大量文本自动转换为自然流畅的语音文件…

9/24作业

1. 分文件编译 分什么要分文件编译&#xff1f; 防止主文件过大&#xff0c;不好修改&#xff0c;简化编译流程 1) 分那些文件 头文件&#xff1a;所有需要提前导入的库文件&#xff0c;函数声明 功能函数&#xff1a;所有功能函数的定义 主函数&#xff1a;main函数&…

828华为云征文|使用Flexus X实例集成ES搜索引擎

目录 一、应用场景 1.1 Flexus X实例概述 1.2 ES搜索引擎 二、安装相关服务 2.1 安装Elasticsearch7.17.0 2.2 安装kibana7.17.0 三、开通安全组规则 四、整体感受 4.1 Flexus X实例 4.2 使用感觉 一、应用场景 1.1 Flexus X实例概述 Flexus X实例是华为云推出的一款…

力扣最热一百题——最小覆盖子串

目录 题目链接&#xff1a;76. 最小覆盖子串 - 力扣&#xff08;LeetCode&#xff09; 题目描述 示例 提示&#xff1a; 解法一&#xff1a;滑动窗口 1. 初始化 2. 构建 mapT 3. 滑动窗口 4. checkT 方法 5. 返回结果 Java写法&#xff1a; 运行时间 C写法&#x…

Hbase日常运维

1 Hbase日常运维 1.1 监控Hbase运行状况 1.1.1 操作系统 1.1.1.1 IO 群集网络IO&#xff0c;磁盘IO&#xff0c;HDFS IO IO越大说明文件读写操作越多。当IO突然增加时&#xff0c;有可能&#xff1a;1.compact队列较大&#xff0c;集群正在进行大量压缩操作。 2.正在执行…

微服务Docker相关指令

1、拉取容器到镜像仓库 docker pull xxx //拉取指令到 镜像仓库 例如 docker pull mysql 、docker pull nginx docker images //查看镜像仓库 2、删除资源 2.1、删除镜像仓库中的资源 docker rmi mysql:latest //删除方式一&#xff1a;格式 docker rmi 要…

安卓系统常见问题如native crash,卡顿卡死定位工具命令技巧-android framework实战开发

背景&#xff1a; 有学员朋友近来有问到一些安卓系统开发过程中的一些核心小技能小技巧等&#xff0c;比如native crash在企业里面该如何准确定位具体代码函数&#xff0c;程序卡住&#xff0c;或者长时间没反应&#xff0c;想要看看卡在代码的哪里。针对以上的一些问题&#…