如何通过i2c从arduino发送4 Pot值到arduino?如何在接收这些值时区分这些值?

 我爱盘小静永远永远 发布于 2023-02-04 13:06

我有一个带有4个盆的Arduino.另一个Arduino通过i2c接收这4个值并将它们打印在显示器上.问题是我不知道如何发送奴隶所知道的4个值,知道哪个值属于哪个Pot.

奴隶代码:

#include 
#include 
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup()
{
  Wire.begin(5);
  Wire.onReceive(receiveEvent);
  Serial.begin(9600);
  lcd.begin(16,2);

}

void loop()
{
}


void receiveEvent(int)
{

 while(Wire.available())
  {
           //How to create this part? How does the Slave know wich value belongs to which pot?
  }

}

主码:

#include 

void setup()

{
  Serial.begin(9600);
  Wire.begin();
  delay(2000);
}

void loop()
{
  int sensor1 = analogRead(A1);
  Wire.beginTransmission(5);
  Wire.write(sensor1);
  Serial.print(sensor1);
  Wire.endTransmission();
  delay(100);

  int sensor2 = analogRead(A2);
  Wire.beginTransmission(5);
  Wire.write(sensor2);
  Serial.print(sensor2);
  Wire.endTransmission();

  delay(500);


}

Mr. Girgitt.. 13

啊,我们这里有一个关于如何设计I2C通信的基本问题.遗憾的是,Arduino IDE中包含的I2C主设备和从设备的示例过于局限,无法提供有关此问题的明确指导.

首先,在您的示例中,主角和从角色交换并应切换.从器件应读取模拟输入的值,主器件应该请求它们.为什么?因为它是主人,应该决定何时请求值并正确解码请求.奴隶应该为给定的请求提供正确的答案,消除数据解释的问题.

I2C通信基于由主控制的requestFunction-(wait)-requestResponse序列.请参阅arduino页面上的测距仪示例.简而言之:

第一:master请求一个测量距离的函数:

  // step 3: instruct sensor to return a particular echo reading 
  Wire.beginTransmission(112); // transmit to device #112
  Wire.write(byte(0x02));      // sets register pointer to echo #1 register (0x02)
  Wire.endTransmission();      // stop transmitting

(有时奴隶需要一些时间,例如10到50毫秒来处理请求,但在示例中我指的是master不会延迟读取)

第二:主要请求响应:

  // step 4: request reading from sensor
  Wire.requestFrom(112, 2);    // request 2 bytes from slave device #112

第三:主人试图阅读和分析回应.

您应该以类似的方式设计可靠的I2C通信.

我就是这样做的; 您可以按照我的模式获得可扩展的从属实现,它将支持一个功能:读取模拟输入,但可以通过向从属主循环添加额外的功能代码和所需的处理实现来轻松扩展

初步评论

    控制从站需要某种简单的协议 - 例如它应该支持请求功能.在读取四个模拟输入这样简单的场景中,并非绝对需要支持函数请求,但我所描述的是您可能在其他项目中使用的更通用的模式.

    从器件不应对请求响应执行任何其他操作(如读取输入),因为I2C通信可能会中断(由于延迟),您将获得部分响应等.这是影响从器件设计的非常重要的要求.

    响应(以及请求,如果需要)可以包含CRC,就像主机等待的时间不够长,它可能会得到空响应.如果没有其他人会使用您的代码,则不需要这样的对策,这里不再描述.其他重要的是Wire库缓冲区限制,它是32字节并且在不修改缓冲区长度的情况下实现CRC校验和将可用数据长度限制为两个字节(如果使用crc16).

奴隶:

#include   // look on the web for an improved wire library which improves reliability by performing re-init on lockups 


// >> put this into a header file you include at the beginning for better clarity
enum { 
  I2C_CMD_GET_ANALOGS = 1
};

enum { 
  I2C_MSG_ARGS_MAX = 32,
  I2C_RESP_LEN_MAX = 32
};

#define I2C_ADDR                 0             
#define TWI_FREQ_SETTING         400000L       // 400KHz for I2C
#define CPU_FREQ                 16000000L     // 16MHz

extern const byte supportedI2Ccmd[] = { 
  1
};
// << put this into a header file you include at the beginning for better clarity



int argsCnt = 0;                        // how many arguments were passed with given command
int requestedCmd = 0;                   // which command was requested (if any)

byte i2cArgs[I2C_MSG_ARGS_MAX];         // array to store args received from master
int i2cArgsLen = 0;                     // how many args passed by master to given command

uint8_t i2cResponse[I2C_RESP_LEN_MAX];  // array to store response
int i2cResponseLen = 0;                 // response length

void setup()
{
  // >> starting i2c
  TWBR = ((CPU_FREQ / TWI_FREQ_SETTING) - 16) / 2;

  Wire.begin(I2C_ADDR);                        // join i2c bus 
  Wire.onRequest(requestEvent);                // register event
  Wire.onReceive(receiveEvent);    
  // << starting i2c
}

void loop()
{

 if(requestedCmd == I2C_CMD_GET_ANALOGS){
    // read inputs and save to response array; example (not tested) below
    i2cResponseLen = 0;
    // analog readings should be averaged and not read one-by-one to reduce noise which is not done in this example
    i2cResponseLen++;
    i2cResponse[i2cResponseLen -1] = analogRead(A0);
    i2cResponseLen++;
    i2cResponse[i2cResponseLen -1] = analogRead(A1);
    i2cResponseLen++;
    i2cResponse[i2cResponseLen -1] = analogRead(A2);
    i2cResponseLen++;
    i2cResponse[i2cResponseLen -1] = analogRead(A3);
    // now slave is ready to send back four bytes each holding analog reading from a specific analog input; you can improve robustness of the protocol by including e.g. crc16 at the end or instead of returning just 4 bytes return 8 where odd bytes indicate analog input indexes and even bytes their values; change master implementation accordingly
    requestedCmd = 0;   // set requestd cmd to 0 disabling processing in next loop

  }
  else if (requestedCmd != 0){
    // log the requested function is unsupported (e.g. by writing to serial port or soft serial

    requestedCmd = 0;   // set requestd cmd to 0 disabling processing in next loop
  }

} 


// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent(){

  Wire.write(i2cResponse, i2cResponseLen);

}


// function that executes when master sends data (begin-end transmission)
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
  //digitalWrite(13,HIGH);
  int cmdRcvd = -1;
  int argIndex = -1; 
  argsCnt = 0;

  if (Wire.available()){
    cmdRcvd = Wire.read();                 // receive first byte - command assumed
    while(Wire.available()){               // receive rest of tramsmission from master assuming arguments to the command
      if (argIndex < I2C_MSG_ARGS_MAX){
        argIndex++;
        i2cArgs[argIndex] = Wire.read();
      }
      else{
        ; // implement logging error: "too many arguments"
      }
      argsCnt = argIndex+1;  
    }
  }
  else{
    // implement logging error: "empty request"
    return;
  }
  // validating command is supported by slave
  int fcnt = -1;
  for (int i = 0; i < sizeof(supportedI2Ccmd); i++) {
    if (supportedI2Ccmd[i] == cmdRcvd) {
      fcnt = i;
    }
  }

  if (fcnt<0){
    // implement logging error: "command not supported"
    return;
  }
  requestedCmd = cmdRcvd;
  // now main loop code should pick up a command to execute and prepare required response when master waits before requesting response
}

主:

#include 

#define I2C_REQ_DELAY_MS         2  // used for IO reads - from node's memory (fast)
#define I2C_REQ_LONG_DELAY_MS    5  //used for configuration etc.

#define TWI_FREQ_SETTING         400000L
#define CPU_FREQ                 16000000L

enum { 
  I2C_CMD_GET_ANALOGS = 1
};

int i2cSlaveAddr = 0;


void setup(){
  // joining i2c as a master
  TWBR = ((CPU_FREQ / TWI_FREQ_SETTING) - 16) / 2;
  Wire.begin(); 
}

void loop(){
  //requesting analogs read: 
  Wire.beginTransmission(i2cSlaveAddr); 
  Wire.write((uint8_t)I2C_CMD_GET_ANALOGS);  
  Wire.endTransmission();  

  delay(I2C_REQ_DELAY_MS);

  // master knows slave should return 4 bytes to the I2C_CMD_GET_ANALOGS command
  int respVals[4];

  Wire.requestFrom(i2cSlaveAddr, 4);

  uint8_t respIoIndex = 0;

  if(Wire.available())
    for (byte r = 0; r < 4; r++)
      if(Wire.available()){ 
        respVals[respIoIndex] = (uint8_t)Wire.read();
        respIoIndex++;      
      }
      else{
        // log or handle error: "missing read"; if you are not going to do so use r index instead of respIoIndex and delete respoIoIndex from this for loop
        break;
      }
  // now the respVals array should contain analog values for each analog input in the same order as defined in slave (respVals[0] - A0, respVals[1] - A1 ...)
}

我希望我的榜样会有所帮助.它基于代码工作数周,从多个从属设备每秒进行40次读取,但是我没有编译它来测试您需要的功能.如果由于某种原因slave不会响应请求,请使用WSWire库作为Wire(至少和Arduino 1.0.3一样)可能偶尔冻结你的master.

编辑: WSWire lib需要用于I2C的外部上拉电阻,除非您修改源并启用内部上拉电路,如Wire.

编辑:您可以尝试EasyTransfer库,而不是创建i2c slave实现.我没有尝试过,但如果发送四个字节就是你需要的一切,它可能更容易使用它.

EDIT [12.2017]:块上有一个新的播放器 - PJON - 一个适合简单多主通信的库,非常适合交换底池值(以及更多).它已经存在了一段时间,但最近几个月获得了大量的开发速度.我部分参与了它的开发,并将我目前使用的所有现场级和本地总线(I2C,MODBUS RTU)通过单线,硬件串行或RF切换到PJON.

1 个回答
  • 啊,我们这里有一个关于如何设计I2C通信的基本问题.遗憾的是,Arduino IDE中包含的I2C主设备和从设备的示例过于局限,无法提供有关此问题的明确指导.

    首先,在您的示例中,主角和从角色交换并应切换.从器件应读取模拟输入的值,主器件应该请求它们.为什么?因为它是主人,应该决定何时请求值并正确解码请求.奴隶应该为给定的请求提供正确的答案,消除数据解释的问题.

    I2C通信基于由主控制的requestFunction-(wait)-requestResponse序列.请参阅arduino页面上的测距仪示例.简而言之:

    第一:master请求一个测量距离的函数:

      // step 3: instruct sensor to return a particular echo reading 
      Wire.beginTransmission(112); // transmit to device #112
      Wire.write(byte(0x02));      // sets register pointer to echo #1 register (0x02)
      Wire.endTransmission();      // stop transmitting
    

    (有时奴隶需要一些时间,例如10到50毫秒来处理请求,但在示例中我指的是master不会延迟读取)

    第二:主要请求响应:

      // step 4: request reading from sensor
      Wire.requestFrom(112, 2);    // request 2 bytes from slave device #112
    

    第三:主人试图阅读和分析回应.

    您应该以类似的方式设计可靠的I2C通信.

    我就是这样做的; 您可以按照我的模式获得可扩展的从属实现,它将支持一个功能:读取模拟输入,但可以通过向从属主循环添加额外的功能代码和所需的处理实现来轻松扩展

    初步评论

      控制从站需要某种简单的协议 - 例如它应该支持请求功能.在读取四个模拟输入这样简单的场景中,并非绝对需要支持函数请求,但我所描述的是您可能在其他项目中使用的更通用的模式.

      从器件不应对请求响应执行任何其他操作(如读取输入),因为I2C通信可能会中断(由于延迟),您将获得部分响应等.这是影响从器件设计的非常重要的要求.

      响应(以及请求,如果需要)可以包含CRC,就像主机等待的时间不够长,它可能会得到空响应.如果没有其他人会使用您的代码,则不需要这样的对策,这里不再描述.其他重要的是Wire库缓冲区限制,它是32字节并且在不修改缓冲区长度的情况下实现CRC校验和将可用数据长度限制为两个字节(如果使用crc16).

    奴隶:

    #include <WSWire.h>  // look on the web for an improved wire library which improves reliability by performing re-init on lockups 
    
    
    // >> put this into a header file you include at the beginning for better clarity
    enum { 
      I2C_CMD_GET_ANALOGS = 1
    };
    
    enum { 
      I2C_MSG_ARGS_MAX = 32,
      I2C_RESP_LEN_MAX = 32
    };
    
    #define I2C_ADDR                 0             
    #define TWI_FREQ_SETTING         400000L       // 400KHz for I2C
    #define CPU_FREQ                 16000000L     // 16MHz
    
    extern const byte supportedI2Ccmd[] = { 
      1
    };
    // << put this into a header file you include at the beginning for better clarity
    
    
    
    int argsCnt = 0;                        // how many arguments were passed with given command
    int requestedCmd = 0;                   // which command was requested (if any)
    
    byte i2cArgs[I2C_MSG_ARGS_MAX];         // array to store args received from master
    int i2cArgsLen = 0;                     // how many args passed by master to given command
    
    uint8_t i2cResponse[I2C_RESP_LEN_MAX];  // array to store response
    int i2cResponseLen = 0;                 // response length
    
    void setup()
    {
      // >> starting i2c
      TWBR = ((CPU_FREQ / TWI_FREQ_SETTING) - 16) / 2;
    
      Wire.begin(I2C_ADDR);                        // join i2c bus 
      Wire.onRequest(requestEvent);                // register event
      Wire.onReceive(receiveEvent);    
      // << starting i2c
    }
    
    void loop()
    {
    
     if(requestedCmd == I2C_CMD_GET_ANALOGS){
        // read inputs and save to response array; example (not tested) below
        i2cResponseLen = 0;
        // analog readings should be averaged and not read one-by-one to reduce noise which is not done in this example
        i2cResponseLen++;
        i2cResponse[i2cResponseLen -1] = analogRead(A0);
        i2cResponseLen++;
        i2cResponse[i2cResponseLen -1] = analogRead(A1);
        i2cResponseLen++;
        i2cResponse[i2cResponseLen -1] = analogRead(A2);
        i2cResponseLen++;
        i2cResponse[i2cResponseLen -1] = analogRead(A3);
        // now slave is ready to send back four bytes each holding analog reading from a specific analog input; you can improve robustness of the protocol by including e.g. crc16 at the end or instead of returning just 4 bytes return 8 where odd bytes indicate analog input indexes and even bytes their values; change master implementation accordingly
        requestedCmd = 0;   // set requestd cmd to 0 disabling processing in next loop
    
      }
      else if (requestedCmd != 0){
        // log the requested function is unsupported (e.g. by writing to serial port or soft serial
    
        requestedCmd = 0;   // set requestd cmd to 0 disabling processing in next loop
      }
    
    } 
    
    
    // function that executes whenever data is requested by master
    // this function is registered as an event, see setup()
    void requestEvent(){
    
      Wire.write(i2cResponse, i2cResponseLen);
    
    }
    
    
    // function that executes when master sends data (begin-end transmission)
    // this function is registered as an event, see setup()
    void receiveEvent(int howMany)
    {
      //digitalWrite(13,HIGH);
      int cmdRcvd = -1;
      int argIndex = -1; 
      argsCnt = 0;
    
      if (Wire.available()){
        cmdRcvd = Wire.read();                 // receive first byte - command assumed
        while(Wire.available()){               // receive rest of tramsmission from master assuming arguments to the command
          if (argIndex < I2C_MSG_ARGS_MAX){
            argIndex++;
            i2cArgs[argIndex] = Wire.read();
          }
          else{
            ; // implement logging error: "too many arguments"
          }
          argsCnt = argIndex+1;  
        }
      }
      else{
        // implement logging error: "empty request"
        return;
      }
      // validating command is supported by slave
      int fcnt = -1;
      for (int i = 0; i < sizeof(supportedI2Ccmd); i++) {
        if (supportedI2Ccmd[i] == cmdRcvd) {
          fcnt = i;
        }
      }
    
      if (fcnt<0){
        // implement logging error: "command not supported"
        return;
      }
      requestedCmd = cmdRcvd;
      // now main loop code should pick up a command to execute and prepare required response when master waits before requesting response
    }
    

    主:

    #include <WSWire.h>
    
    #define I2C_REQ_DELAY_MS         2  // used for IO reads - from node's memory (fast)
    #define I2C_REQ_LONG_DELAY_MS    5  //used for configuration etc.
    
    #define TWI_FREQ_SETTING         400000L
    #define CPU_FREQ                 16000000L
    
    enum { 
      I2C_CMD_GET_ANALOGS = 1
    };
    
    int i2cSlaveAddr = 0;
    
    
    void setup(){
      // joining i2c as a master
      TWBR = ((CPU_FREQ / TWI_FREQ_SETTING) - 16) / 2;
      Wire.begin(); 
    }
    
    void loop(){
      //requesting analogs read: 
      Wire.beginTransmission(i2cSlaveAddr); 
      Wire.write((uint8_t)I2C_CMD_GET_ANALOGS);  
      Wire.endTransmission();  
    
      delay(I2C_REQ_DELAY_MS);
    
      // master knows slave should return 4 bytes to the I2C_CMD_GET_ANALOGS command
      int respVals[4];
    
      Wire.requestFrom(i2cSlaveAddr, 4);
    
      uint8_t respIoIndex = 0;
    
      if(Wire.available())
        for (byte r = 0; r < 4; r++)
          if(Wire.available()){ 
            respVals[respIoIndex] = (uint8_t)Wire.read();
            respIoIndex++;      
          }
          else{
            // log or handle error: "missing read"; if you are not going to do so use r index instead of respIoIndex and delete respoIoIndex from this for loop
            break;
          }
      // now the respVals array should contain analog values for each analog input in the same order as defined in slave (respVals[0] - A0, respVals[1] - A1 ...)
    }
    

    我希望我的榜样会有所帮助.它基于代码工作数周,从多个从属设备每秒进行40次读取,但是我没有编译它来测试您需要的功能.如果由于某种原因slave不会响应请求,请使用WSWire库作为Wire(至少和Arduino 1.0.3一样)可能偶尔冻结你的master.

    编辑: WSWire lib需要用于I2C的外部上拉电阻,除非您修改源并启用内部上拉电路,如Wire.

    编辑:您可以尝试EasyTransfer库,而不是创建i2c slave实现.我没有尝试过,但如果发送四个字节就是你需要的一切,它可能更容易使用它.

    EDIT [12.2017]:块上有一个新的播放器 - PJON - 一个适合简单多主通信的库,非常适合交换底池值(以及更多).它已经存在了一段时间,但最近几个月获得了大量的开发速度.我部分参与了它的开发,并将我目前使用的所有现场级和本地总线(I2C,MODBUS RTU)通过单线,硬件串行或RF切换到PJON.

    2023-02-04 13:08 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有