esp32使用arduino从机i2c读写,类似EEPROM的协议

I2C从机的工作机制

I2C协议中从机设备确实需要等待主机发起通信。主机控制时钟线(SCL)和数据线(SDA),从机只能在主机允许的时序内响应。从机无法主动发起通信,必须由主机发送地址帧并匹配从机地址后,才能进行数据传输。

主机在任何时刻都可以通过发送停止条件(STOP condition)终止当前通信。停止条件由SCL高电平时SDA的上升沿构成,从机必须立即释放总线。

部分代码解释

Wire.onRequest(I2C_write);  // 主机请求数据时调用括号内的函数一次

  1. 在I2C_write要把所有要发送的数据发送出去,但是Wire 库内部定义默认缓冲区只有32个,如果需要更多缓冲区需要去他的头文件里修改
  2. 在 I2C 通信中,ESP32 的 Wire 库会在主机发送 STOP 信号后自动处理缓冲区,无需手动清除发送缓冲区。

 Wire.onReceive(I2C_read);  // 主机发送数据时调用调用括号内的函数一次

  1. 同理只会在接收后调用一次,所以要把所有数据读完

类似EEPROM的协议

  1. 该协议采用类似 EEPROM 的通信方式,格式固定:首先接收一个字节作为寄存器地址,然后根据操作类型进行数据读写。

既然I2C_read()只调用一次为什么不能在函数末尾清除地址和接收标志位,要单独开一个空闲函数

  1. 部分单片机的库是会在发送完成第一个地址后发送结束信号的,目的为了兼容更多单片机
  2. 发送数据也需要等待接收完成后的停止信号才发送数据

代码

注意事项

  1. 每次传输后需要等待10ms以上才能在次传输,因为需要空闲去清除地址标志位
  2. 缓冲区大小默认是32字节,需要自己去库文件修改
  3. 使用方法只需要在void setup() 里调用I2C_int()一次即可

MY_I2C.cpp文件

#include "MY_I2C.h"
TaskHandle_t I2C;
uint32_t uwTick=0;
uint8_t ram[256];
struct I2C i2c={.offset=0,.first_byte_state=1};

void I2C_int()
{
    // 注册 I2C 请求和接收函数
    Wire.onRequest(I2C_write);  // 主机请求数据时调用
    Wire.onReceive(I2C_read);  // 主机发送数据时调用
  Wire.begin((uint8_t)SLAVE_ADDR,5,6,100000);//地址,sda,scl,频率hz
  xTaskCreatePinnedToCore(  I2C_task,      //任务函数名
                "I2C_task",  //任务标识
                4096,           //任务堆栈大小
                NULL,
                1,              //任务优先级
                &I2C,
                1
              );
}

void I2C_read(int bytesReceived)
{
    while(Wire.available())//缓冲区里还有多少数据
    {
        if(i2c.first_byte_state==0)//按地址接收
        {
            ram[i2c.offset++]=Wire.read();
            
        }
        if(i2c.first_byte_state==1)//第一次读地址
        {
            i2c.offset=Wire.read();
            i2c.first_byte_state=0;
        }

    }
}

void I2C_write()
{
    if(i2c.first_byte_state==0)//按地址发送
    {
          uint8_t i = i2c.offset;
          while (i < sizeof(ram) && Wire.availableForWrite() > 0)//发送剩余的数据&&缓冲区还有空间 
            Wire.write(ram[i++]);
    }

}
void I2C_task(void *pvParameters)
{
  while(1)
  {
   
    if(i2c.first_byte_state==1)
    {
        i2c.tick=millis();
    }
    else
    {
        if(millis()-i2c.tick>10)//空闲10m清空标志位
        {
            i2c.first_byte_state=1;
            i2c.offset=0;
        }
    }
    vTaskDelay( 1);
  }
  vTaskDelete(NULL);
}

MY-I2C.h文件 

#ifndef _MY_I2C_H_
#define _MY_I2C_H_
#include <Wire.h>
#include <Arduino.h>
#define SLAVE_ADDR 0x42  // 从机 I2C 地址
extern uint32_t uwTick;
extern uint8_t ram[256];
struct I2C {                
uint8_t offset;//地址
uint8_t first_byte_state;//开始标志位
uint32_t tick;
};
void I2C_int();//初始化
void I2C_read(int bytesReceived);
void I2C_write();
void I2C_task(void *pvParameters);
#endif

博主也是看gpt和互联网的一些资料,如有错误请见谅

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值