[Abandonné] Communication I2C entre 2 arduino + un mini-joystick

[ J'ai édité le titre et ce premier post sachant que si mon projet reste identique, mes avancées, si je puis dire, ont fait évoluer mes questions. (Mais peut-être aurais-je dû créer un nouveau sujet ?)]

Bonjour,
Je veux à remplacer un mini-joystick numérique à effet Hall N35P112 qui se trouve sur un hotas de sim aérienne par un mini-joystick ALPS RKJXV1224005 analogique couplé à un ATtiny441. Le hotas fonctionne sur le bus I2C.
J'ai deux cartes Mega 2560. L'une comme maître simulant la manette de jeu et l'autre comme esclave avec le joystick RKJXV1224005 sur les ports A2 et A3. Les deux échangeant sur le bus I2C : Mega 2560 => SDA20, SCL21
Coté maitre, j'utilise le code trouvé ici. Il a été fait pour une arduino Pro 8MHz en 2012 (si la Pro à changée depuis 2012) .
Coté esclave, je suis en train de réaliser un code de manière à satisfaire les requêtes du maître.

[Je met de coté cet aspect qui suit et la configuration des connexions. Pour l'instant, les pins INT, RESET et PB seront traitées plus tard]

Mais pour ce faire, j'ai besoin de savoir comment configurer les entrées /sorties de l'esclave.

Coté esclave, les broches sont les suivantes :

  EasyPoint RESET -> D6   (6)
  EasyPoint INT   -> INT0 (2)
  EasyPoint PB    -> A1   (15)
  EasyPoint SDA   -> A4   (18)
  EasyPoint SCL   -> A5   (19)

Coté maître (Mega 2560), il y a 5 entrées / sorties reliées aux broches coté esclave du mini-joystick simulé (Atmega8) :

 EasyPoint RESET -> A0   (14)
 EasyPoint INT   -> A1   (15)
 EasyPoint PB    -> A2   (16)
 EasyPoint SDA   -> A4   (18)
 EasyPoint SCL   -> A5   (19)

Déjà je ne comprends pas à quoi correspondent les nombres entre paranthèses ... Sur la Pro avec un Atmega328, ça ne correspond pas aux pins analogiques indiqués.

Dans ce code EasyPoint_demo.ino, respectivement, les pins A1, A2 et A0 sont configurés comme ci-dessous :

   pinMode(INT, INPUT);  // The interrupt pin from the EasyPoint should be input...
  digitalWrite(INT, HIGH);  // ...and is open drain, so it needs a pull-up.
  pinMode(PB, INPUT);   // The pushbutton pin from the EasyPoint should be input...
  digitalWrite(PB, HIGH);  // ...and is a NO button, so it needs a pull-up.
  pinMode(nRESET, OUTPUT); // The reset line to the EasyPoint should be output.

Chacune de ces pins sont reliées à l'esclave.
Mais comment doivent être configurées les pins de l'esclave ?
Je pensais si on a une pin en INPUT sur le maître, alors elle doit être en OUTPUT sur l'esclave et vis et versa.
Qu'en est-il ?

Le code maître EasyPoint_demo.ino :

  
  /************************************************************************
Created 25 Jan 2012 by Mike Hord @ SparkFun Electronics.

This code is beerware- feel free to make use of it, with or without
attribution, in your own projects. If you find it helpful, buy me a beer
the next time you see me at the local- or better yet, shop SparkFun!

Filename: EasyPoint_Demo.ino- basic demo code and functions for using
   the Austria Microsystems N35P112 EasyPoint mini-joystick module.

Created in Arduino 1.0
Tested with an Arduino Pro @ 8MHz with the following connections:
  EasyPoint RESET -> A0 (14)
  EasyPoint INT   -> A1 (15)
  EasyPoint PB    -> A2 (16)
  EasyPoint SDA   -> A4 (18)
  EasyPoint SCL   -> A5 (19)
************************************************************************/
#include <Wire.h>    // Include the I2C library. The EasyPoint is a pretty
                     //  standard I2C peripheral.

#define JS_ADDR 0x41 // this is the default I2C address of the joystick.
                     //  it can be changed to 0x40 by bridging a solder
                     //  jumper on the board.
                     
// Pin definitions for this example code
#define nRESET 14    // Pull this signal LOW to reset the joystick.
#define INT 15       // When the joystick controller is properly configured,
                     //  this line will go LOW if the controller detects a
                     //  deviation of greater than a certain value, and stay
                     //  LOW until the values move back into the "dead zone".
                     //  Default behavior is to go "high" whenever a conversion
                     //  is completed- it will go low again when you read the
                     //  Y-value register.
#define PB 16        // Pushbutton output. This pin is grounded when the stem
                     //  of the joystick is pushed in, and floating otherwise.
                     //  No pull-up is provided on the board!

// N35P112 Register definitions
//  NB- while there are other registers listed in the datasheet, these are the
//  only ones necessary for correct operation. The others are read-only or 
//  initialize to the right value and SHOULD NOT be messed with.

#define JS_CTRL_REG_1 0x0F  // 8-bit configuration register. (def: 11110000)
//  bit 7     1 = active mode, 0 = low power mode
//             EasyPoint MUST be in low power mode for INT on threshold mode!
//  bits 6:4  low power readout time base register. Wake-up interval for low
//             power mode output:
//             000 -> 20ms, 001 -> 40ms, 010 -> 80ms, 011 -> 100ms
//             100 -> 140ms, 101 -> 200ms, 110 -> 260ms, 111 -> 320ms
//  bit 3     1 = interrupt disabled, 0 = interrupt enabled
//  bit 2     0 = INT pin low on every new coordinate calculation
//            1 = INT pin low when threshold values exceeded and while
//                threshold values are exceeded
//  bit 1     write to 1 to reset device
//  bit 0     1 = conversion complete, data valid. Read only!

#define JS_T_CTRL_REG 0x2D    // Scaling factor for X/Y coordinates.
//  Datasheet says to set this to 0x06 which is NOT the default power-up value.
//  No further information is provided, so make sure you write it after power on
//  or reset of the joystick!

#define JS_ID_CODE_REG 0x0C   // 8-bit manufacturer ID code. Read only.
//  Reads as 0x0C- useful to test data communications with the device.

#define JS_X_REG 0x10         // 8-bit register containing a signed 8-bit
                                   //  value representing the current position of
                                   //  the joystick in X.
                                   
#define JS_Y_REG 0x11         // Y position register. Reading this registerJS_ID_CODE_REG
                                   //  resets the status of the INT output, so it
                                   //  should be read AFTER the X value.
                            pinMode(INT, INPUT);  // The interrupt pin from the EasyPoint should be input...
  digitalWrite(INT, HIGH);  // ...and is open drain, so it needs a pull-up.
  pinMode(PB, INPUT);   // The pushbutton pin from the EasyPoint should be input...
  digitalWrite(PB, HIGH);  // ...and is a NO button, so it needs a pull-up.
  pinMode(nRESET, OUTPUT); // The reset line to the EasyPoint should be output.

// INT threshold registers. If the X or Y value is greater than the value in the
//  corresponding "P" register or less than the value in the "N" register, and 
//  JS_Control_Register_1 bit 2 = 1 and bit 3 = 0, and the device is set to poll
//  its read values automatically, 
#define JS_XP_REG 0x12
#define JS_XN_REG 0x13
#define JS_YP_REG 0x14
#define JS_YN_REG 0x15

// X and Y zero-state values. The EasyPointInit() function will populate these values
//  with the initial at-rest offset of the joystick. This can be used later to determine
//  the true offset from center when reading the value, as well as to calculate a
Coté maitre, j'utilise le code trouvé ici : [http://cdn.sparkfun.com/datasheets/BreakoutBoard
//  reasonable threshold for setting interrupt levels. The datatype "char" is used because
//  it is a signed 8-bit value, which is the same size as the X/Y values.
char XZero = 0;
char YZero = 0;

void setup() 
{ 
  Serial.begin(57600);  // Intialize the Serial library.
  Wire.begin();         // Initialize the I2C library.
  pinMode(INT, INPUT);  // The interrupt pin from the EasyPoint should be input...
  digitalWrite(INT, HIGH);  // ...and is open drain, so it needs a pull-up.
  pinMode(PB, INPUT);   // The pushbutton pin from the EasyPoint should be input...
  digitalWrite(PB, HIGH);  // ...and is a NO button, so it needs a pull-up.
  pinMode(nRESET, OUTPUT); // The reset line to the EasyPoint should be output.
  
  EasyPointInit();  // This is a nice, packaged function that sets up the EasyPoint.
  EasyPointIntSetup(XZero, YZero, 25, 25); // Set up the interrupt function. Not
                                           //  needed, but nice to include! This is
                                           //  setting it to flag an interrupt if the
                                           //  reading is 25 points off center or more.
  Serial.println("Setup completed successfully.");
} 

// This example loop is really simple- it just polls the INT pin and PB pin, looking
//  for transitions and announcing them over serial.
void loop() 
{ 
  // As mentioned above, the INT pin will go low if the joystick is moved off center.
  //  No delay is necessary in this loop because the polling rate of the EasyPoint is
  //  set to 200ms, which gives us a decently slow polling rate to prevent huge data
  //  rates from overwhelming the serial console.
  if(digitalRead(INT)==LOW)
  {
    Serial.println("INT!!!!");
    Serial.println(readXAxis(), DEC); // readXAxis() returns a signed 8-bit result.
    Serial.println(readYAxis(), DEC); // readYAxis() returns a signed 8-bit result.
  }
  // The pushbutton output (i.e., what happens when you push down on the joystick) is
  //  a NO button- when you push it down, it grounds the pin. Thus, we look for a LOW
  //  on that pin to detect a press.
  if(digitalRead(PB)==LOW)
  {
    Serial.println("BUTTON!!!!");
    delay(250);  // debounce/throttle the data rate.
  }
}

// A simple function for setting the internal interrupt function to flag the
//  processor if the joystick moves more that x_delta or y_delta units from
//  the points specified by x_null and y_null. A function to set up an
//  asymmetrical dead zone is left as an exercise for the reader; the principle
//  is the same- write the appropriate registers, etc etc.
void EasyPointIntSetup(char x_null, char y_null, byte x_delta, byte y_delta)
{
  char xp, xn, yp, yn;  // 8-bit signed values for the trigger points.
  xp = x_null + x_delta;  // calculate the positive trigger point for x
  writeI2CReg(JS_ADDR, JS_XP_REG, xp);  // set the positive trigger point for x
  xn = x_null - x_delta;  // calculate the negative trigger point for x
  writeI2CReg(JS_ADDR, JS_XN_REG, xn);  // set the negative trigger point for x
  yp = y_null + y_delta;  // cJS_ID_CODE_REGJS_ID_CODE_REGalculate the positive trigger point for y
  writeI2CReg(JS_ADDR, JS_YP_REG, yp);  // set the positive trigger point for y
  yn = y_null - y_delta;  // calculate the negative trigger point for y
  writeI2CReg(JS_ADDR, JS_YN_REG, yn);  // set the negative trigger point for y
  // Now we need to actually tell the device we want to interrupt on values outside
  //  the deadzone, and to activate the interrupt pin. No assumptions- let's be
  //  responsible and change ONLY the bits we need. Start by reading the value
  //  of Control Register 1.
  byte temp = readI2CReg(JS_ADDR, JS_CTRL_REG_1);
  // Next, we'll clear bit 3, to ensure that the INT pin is active.
  temp = temp & 0b11110111;
  // Then, clear bit 7, to put the EasyPoint into self-timed mode (otherwise,
  //  it will only do a conversion after a read, which means you need to poll
  //  it to initiate a read, which makes the INT pin useless).
  temp = temp & 0b01111111;
  // Now, clear bits 6:4, so we can set the polling rate. See register description
  //  above for details on various values of these bits.
  temp = temp & 0b10001111;
  // Set the bits according to the desired polling rate. Here, I'll set it to
  //  poll every 200ms.
  temp = temp | 0b01010000;
  // Then, set bit 2, to tell the device to interrupt based on range and not
  //  every time a conversion is complete.
  temp = temp | 0b00000100;
      // NB- for clarity, I broke the bit sets/clears up into smaller operations. We
      //  COULD combine the bit clearing/setting operations into two lines, like this:
      //  temp = temp & 0b00000111;
      //  temp = temp | 0b01010100;
  // Finally, write the adjusted value back into Control Register 1.
  writeI2CReg(JS_ADDR, JS_CTRL_REG_1, temp);
}

// This function handles most of the EasyPoint specific initialization stuff-
//  registers that MUST be set to a certain value, calculation of zero-point
//  offsets, verification of connection.
void EasyPointInit()
{    
  // First things first: let's reset the joystick controller to make sure it's
  //  in a known state before we start playing with it. THIS IS VERY IMPORTANT-
  //  if you do NOT properly reset the EasyPoint, it won't work right!!!
  digitalWrite(nRESET, HIGH);
  delay(1);
  digitalWrite(nRESET, LOW);
  delay(1);
  digitalWrite(nRESET, HIGH);
  delay(100);  // give the joystick time to come online
  // The datasheet tells us to initialize the T Control Register to 0x06 for this
  //  product but doesn't say why, nor why it doesn't power up to this value.
  //  Don't forget to do this!
  writeI2CReg(JS_ADDR, JS_T_CTRL_REG, 0x06);
  // Now let's look at the X/Y offset. Ideally, the joystick would be at 0,0 at
  //  rest; practically, it could be as much as 30-50 counts positive or negative.
  char X, Y;                // char is the signed 8-bit data type in Arduino.
  char X_temp=0, Y_temp=0;
  readXAxis();  // read and discard a couple of values- the first read after boot
  readYAxis();  //  seems to be a little off on the low side.
  delay(5);
  // Now we collect four data points from each axis and calculate the average.
  //  We delay between conversions to allow a new conversion to complete- we COULD
  //  write some code to poll the conversion bit, or configure the INT pin, but
  //  let's be lazy instead.
  for (byte i = 0; i<4; i++)
  {
    X_temp += readXAxis();
    Y_temp += readYAxis();
    delay(5);
  }
  XZero = X_temp/4;
  YZero = Y_temp/4;
  if (readI2CReg(JS_ADDR, JS_ID_CODE_REG) != 0x0C) Serial.println("EasyPoint failed to respond.");
}

// readXAxis() and readYAxis() are just nice little wrappers around the I2C read function which
//  automatically send the appropriate register addresses and convert the result from an unsigned
//  8-bit value to a signed 8-bit value.
char readXAxis()
{
  return readI2CReg(JS_ADDR, JS_X_REG);
}

char readYAxis()
{
  return readI2CReg(JS_ADDR, JS_Y_REG);
}
  
// The Wire library contains no discrete "read" or "write" functionality, so let's build our own.
//  I included an option to change the slave device's address but NOT the ability to send or
//  receive multiple bytes- generalizing that would have involved some unneccessarily ugly
//  programming for the simplicity of this application.
byte readI2CReg(byte slave_addr, byte reg_addr)
{
  // An I2C read transaction with the EasyPoint using the Wire library involves five steps:
  // Wire.beginTransmission() begins setting up a packet to transfer over I2C- the 7-bit
  //  address plus the R/W bit set to W mode.
  Wire.beginTransmission(slave_addr);
  // Wire.write() adds the 8-bit value passed to it to the packet. This can be repeated as
  //  necessary for multiple byte transfers; for the EasyPoint, the first byte transmitted
  //  is ALWAYS the address of interest.
  Wire.write(reg_addr);
  // Wire.endTransmission() transmits the packet that the previous two functions assembled.
  Wire.endTransmission();
  // Wire.requestFrom() pushes out the slave address followed by the R/W bit set to R mode.
  //  The result is that the device starts spitting out data based on (in this case) the
  //  current value in the address register. We need to clarify how many bytes we expect back;
  //  the (byte) typecast prevents an error in the compiler caused by an ambiguity in the
  //  expression of constants. It's dumb, but it works.
  Wire.requestFrom(slave_addr, (byte)1);
  // Wire.read() returns a byte read into the buffer by the Wire.requestFrom() function.
  //  Since we're only worried about reading one register at a time, we can simply return
  //  that value directly.
  return Wire.read();
}

void writeI2CReg(byte slave_addr, byte reg_addr, byte d_buff)
{
  // Writing the EasyPoint over I2C is a little easier than reading from it:
  // Wire.beginTransmission() starts assembling a packet for the device at slave_addr.
  Wire.beginTransmission(slave_addr);
  // Wire.write() adds data to the packet- the first piece of data is the address we
  //  of the register we want to write data into.
  Wire.write(reg_addr);
  // The second piece of data is the actual data we want to write.
  Wire.write(d_buff);
  // Wire.endTransmission() sends the packet. No return value is expected or needed for
  //  our purposes here.
  Wire.endTransmission();
}

Mon code coté esclave (en construction) :

#include "Wire.h"

#define JS_ADDR 0x41 // this is the default I2C address of the joystick.
                    //  it can be changed to 0x40 by bridging a solder
                    //  jumper on the board.
                    
// Pin definitions for this example code
#define nRESET 14   // Pull this signal LOW to reset the joystick.
#define INT 15    // When the joystick controller is properly configured,
                    //  this line will go LOW if the controller detects a
                    //  deviation of greater than a certain value, and stay
                    //  LOW until the values move back into the "dead zone".
                    //  Default behavior is to go "high" whenever a conversion
                    //  is completed- it will go low again when you read the
                    //  Y-value register.
#define PB A1     // Pushbutton output. This pin is grounded when the stem
                    //  of the joystick is pushed in, and floating otherwise.
                    //  No pull-up is provided on the board!



/*// N35P112 Register definitions
//  NB- while there are other registers listed in the datasheet, these are the
//  only ones necessary for correct operation. The others are read-only or 
//  initialize to the right value and SHOULD NOT be messed with.

#define JS_CTRL_REG_1 0x0F  // 8-bit configuration register. (def: 11110000)
//  bit 7     1 = active mode, 0 = low power mode
//             EasyPoint MUST be in low power mode for INT on threshold mode!
//  bits 6:4  low power readout time base register. Wake-up interval for low
//             power mode output:
//             000 -> 20ms, 001 -> 40ms, 010 -> 80ms, 011 -> 100ms
//             100 -> 140ms, 101 -> 200ms, 110 -> 260ms, 111 -> 320msb11110001);
//  bit 3     1 = interrupt disabled, 0 = interrupt enabled
//  bit 2     0 = INT pin low on every new coordinate calculation
//            1 = INT pin low when threshold values exceeded and while
//                threshold values are exceeded
//  bit 1     write to 1 to reset device
//  bit 0     1 = conversion complete, data valid. Read only!*/

#define JS_T_CTRL_REG 0x2D    // Scaling factor for X/Y coordinates.
//  Datasheet says to set this to 0x06 which is NOT the default power-up value.
//  No further information is provided, so make sure you write it after power on
//  or reset of the joystick!

#define JS_ID_CODE_REG 0x0C   // 8-bit manufacturer ID code. Read only.
//  Reads as 0x0C- useful to test data communications with the device.

#define JS_X_REG 0x10         // 8-bit register containing a signed 8-bit
                                  //  value representing the current position of
                                  //  the joystick in X.
                                  
#define JS_Y_REG 0x11         // Y position register. Reading this register
                                  //  resets the status of the INT output, so it
                                  //  should be read AFTER the X value.
                          
// INT threshold registers. If the X or Y value is greater than the value in the
//  corresponding "P" register or less than the value in the "N" register, and 
//  JS_Control_Register_1 bit 2 = 1 and bit 3 = 0, and the device is set to poll
//  its read values automatically, 
#define JS_XP_REG 0x12
#define JS_XN_REG 0x13
#define JS_YP_REG 0x14
#define JS_YN_REG 0x15

void setup() {
 Wire.begin(JS_ADDR);
 pinMode(nRESET, INPUT);
 pinMode(PB, OUTPUT);
 pinMode(INT, OUTPUT);
 Wire.onReceive(receiveEvent);    // I2C events
 Wire.onRequest(requestEvent);
 Serial.begin(57600);
}

volatile byte JS_CTRL_REG_1 = 0;  //stores last byte transmitted
/*volatile byte JS_T_CTRL_REG  = 0;
volatile byte JS_X_REG = 0;
volatile byte JS_Y_REG = 0;
volatile byte JS_XP_REG = 0;
volatile byte JS_XN_REG = 0;
volatile byte JS_YP_REG = 0;
volatile byte JS_YN_REG = 0;*/

volatile bool flag = 0;     //flag to set after y val sent

int8_t x_pos = 0;  //stores x and y vals to be sent
int8_t y_pos = 0;
int8_t temp_x = 0;
int8_t temp_y = 0;

void loop() {
 delay(500);
 int8_t temp_x = map(analogRead(A2), 1023, 0, -128, 127);  //scales analog inputs to correct size
 int8_t temp_y = map(analogRead(A3), 0, 1023, -128, 127);
 Serial.print("temp_x = ");
 Serial.print(temp_x);
 Serial.print(" temp_y = ");
 Serial.println(temp_y);
 if (flag) {
   noInterrupts();
   flag = 0;
   x_pos = temp_x;
   y_pos = temp_y;
   interrupts();
 }
 Serial.print("x_pos=");
 Serial.print(x_pos);
 Serial.print(" y_pos=");
 Serial.println(y_pos);
}


void receiveEvent(int) {
 while (Wire.available()) {
   JS_REG = Wire.read();
   Serial.print("JS_REG = ");
   Serial.println(JS_REG);
 }
}

void requestEvent() {
 switch (JS_REG) {
   case JS_X_REG:
     Wire.write(x_pos); //x val
     break;
   case JS_Y_REG:
     Wire.write(y_pos); //y val
     flag = 1;
     break;
   case JS_CTRL_REG_1: //Ctrl 1 register
     Wire.write (JS_ID_CODE_REG);
     //Wire.write();
     break;
   case JS_ID_REG
     Wire.write(0x0C);
     break;
   //case JS_
    //Wire.write();
    //break;
    
 }
}

Bonsoir moricef

Par exemple, 14 correspond à la valeur qui se "cache" derrière la variable A0 et ainsi de suite, voire dans le fichier pins_arduino.h

Si tu as un IDE un tant soit peu évolué comme l'IDE 2.0, il suffit de laisser le curseur de la souris sur A0 par exemple pour découvrir, dans une info-bulle, ce qui se cache derrière cet A0
image
Où l'on retrouve le 14.

Cordialement
jpbbricole

Merci pour l'explication.

Je me réponds.
Oui, les pins du coté esclave doivent être à l'inverse du maître : OUTPUT pour INPUT.
Par contre comment faire passer les valeurs négatives lues sur les potentiomètres dans le bus i2c ? je ne reçois que les valeurs positives et les valeurs négatives sont toutes lues comme le max de valeur soit 127.
Est-ce un débordement de valeur ?

@jpbbricole, j'ai lu une de vos réponses à propos du bus i2c https://forum.arduino.cc/t/probleme-envoie-de-valeurs-entre-2-arduino-avec-i2c/971275, faute de compétences en c, j'ai du mal à comprendre la forme Wire.readBytes((byte*)&mmDataPacket, sizeof mmDataPacket);*

Bonsoir moricef

Je n'ai pas les connaissances suffisantes pour te détailler tout ça, mais, en gros, on lis d'un coup une quantité d'octets définie par la taille de la structure envoyée, mmDataPacket. Ca permet d'envoyer, en une fois, des données plus "complexes" et de les récupérer, à l'autre bout, en une opération (readBytes).

Autrement il faudrait faire tant qu'il y a des octets à lire:

  while(1 < Wire.available()) // loop through all but the last
  {
    char c = Wire.read(); // receive byte as a character
  }

Cordialement
jpbbricole

Oui, c'est bien ce que j'ai sur l'esclave mais rien n'est transmis sur le maître. L'esclave reçoit bien du maître les codes registres en hexa, le maître ne reçoit rien de sa part sinon des valeurs n'ayant aucun sens. J'ai testé en débranchant les fils sda et scl. J'ai aussi essayé avec d'autres sketchs en exemple maître pour vérifier.

Comment faire pour faire passer les valeurs négatives en i2c ?

Bonsoir moricef

Si tu me fait un résumé de ce qui doit aller de maître à esclave et l'inverse, je te fais un exemple.
Comment sont générées les valeurs négatives ?

A+
Cordialement
jpbbricole

readBytes lit une certain nombre de bytes sur le bus I²C et les range en mémoire. Elle attend comme arguments:

  • un pointeur vers une zone mémoire (une variable, un tableau, une structure, ...). Le pointeur doit être de type pointeur sur des bytes.
  • le nombre de bytes à lire

Dans l'exemple :

  • La zone de destination est une variable de type structure nommée mmDataPacket.
  • &mmDataPacket permet d'obtenir l'adresse en mémoire de ladite structure
  • (byte*) permet de faire une conversion de type, transformant le pointeur sur une structure en un pointeur vers des bytes.
  • sizeof mmDataPacket retourne la taille de la structure

Dans mon exemple, les valeurs envoyées par l'esclave vont de -128 à 127. Ce sont les valeurs analogiques lues sur les axes x et y des 2 potentiomètres du mini joystick. Aprés reception des codes registres JS_X _REG 0x10 et JS_Y_REG 0x11 envoyés par le maître.

Bonjour jpbbricole

En relisant ton post #1, je me rends compte que mon exemple ne te servirai pas à grand chose.

Mais, par contre, côté maître,

Essaies de changer ça en char

char readI2CReg(byte slave_addr, byte reg_addr)

Surtout que c'est appelé depuis:

char readXAxis()
{
  return readI2CReg(JS_ADDR, JS_X_REG);
}

char readYAxis()
{
  return readI2CReg(JS_ADDR, JS_Y_REG);
}

Cordialement
jpbbricole

Bonjour @jpbbricole

Merci de vos réponses. Ainsi que @fdufnews.

Pour l'instant, je teste deux sketchs trouvés ici dont j'ai modifié le map coté maître afin d'avoir l'échelonnement des valeurs des potentiomètres x et y entre -128 et 127 :

#include <Wire.h>

// Définition des entrées sur lesquelles sont branchés les poentiomètres.
#define POTX_PIN 0
#define POTY_PIN 2

#define I2C_SLAVE_ADDRESS 65

// Afin de n'envoyer QUE des informations utiles sur le canal I2C, on
// mémorise les valeurs précédentes des potentiomètres.
int previousPotXValue = 0;
int previousPotYValue = 0;

void setup()
{
  // Ouvre le port série et fixe le debit de communication à 9600 bauds 
  Serial.begin(57600);
  
  // Initialise la library Wire et se connecte au bus I2C en tant que maître 
  Wire.begin();

  // Déclare les pins de l'arduino POTENTIOMETER_LED_PIN et POTENTIOMETER_ENGINE_PIN 
  // en entrée 
  pinMode(POTX_PIN, INPUT);
  pinMode(POTY_PIN, INPUT);
}

void readRegisterAndSendValueX(int potentiometerPin, int *previousValue, int slaveAddress) {

  int potValue = analogRead(potentiometerPin);
 
  potValue= map(potValue, 1023, 0, -128, 127);  

  // Teste : la valeur est elle différente de la lecture précédente (autrement dit, est-ce
  // que le potentiomètre a été tourné ?).
  if (*previousValue != potValue) {
    // C'est le cas => sauvegarde de la valeur, pour les prochaines lectures/testes.
    *previousValue = potValue;

    // Envoie de la valeur à l'esclave I2C.
    // Principe et description :
    // 1 : Commencez une transmission vers la carte esclave I2C avec l'adresse donnée. 
    // 2 : Ensuite, mettez en file d'attente les octets à transmettre avec la fonction write () 
    // 3 : transmettez-les en appelant la fonction endTransmission () . 

    Wire.beginTransmission(slaveAddress);
    Wire.write(*previousValue);
    Wire.endTransmission();

    Serial.print("[Input");
    Serial.print(potentiometerPin);
    Serial.print("] ");
    Serial.println(*previousValue);
  }
}

void readRegisterAndSendValueY(int potentiometerPin, int *previousValue, int slaveAddress) {

  int potValue = analogRead(potentiometerPin);
 
  potValue= map(potValue, 0, 1023, -128, 127);  

  // Teste : la valeur est elle différente de la lecture précédente (autrement dit, est-ce
  // que le potentiomètre a été tourné ?).
  if (*previousValue != potValue) {
    // C'est le cas => sauvegarde de la valeur, pour les prochaines lectures/testes.
    *previousValue = potValue;

  
    Wire.beginTransmission(slaveAddress);
    Wire.write(*previousValue);
    Wire.endTransmission();

    Serial.print("[Input");
    Serial.print(potentiometerPin);
    Serial.print("] ");
    Serial.println(*previousValue);
    }
}

void loop()
{delay(100);
  // Grâce à la fonction readRegisterAndSendValue, il est facilement possible 
  // de gérer autant de potentiomètre 
  // (ou autre contrôleur d'entrée), et d'esclave I2C.

  // Lecture du premier potentiomètre, sauvegarde dans la variable *previousPotentiometer1Value*
  // (dont l'adresse est transmise grâce à l'opérateur &),
  //  et émission de la valeur lue (si différente), sur le premier esclave I2C.
  readRegisterAndSendValueX(POTX_PIN, &previousPotXValue, I2C_SLAVE_ADDRESS);

  // Idem pour le second potentiomètre, et le second esclave I2C.
  readRegisterAndSendValueY(POTY_PIN, &previousPotYValue, I2C_SLAVE_ADDRESS);
} 

Est-ce vraiment nécessaire d'inverser la lecture des valeurs 0 à 1023 dans les map par rapport à x et y ?

potValue= map(potValue, 1023, 0, -128, 127); 
potValue= map(potValue, 0, 1023, -128, 127);

Dans le cas contraire j'économise la seconde fonction readRegisterAndSendValueY()

Coté esclave :

#include <Wire.h>

#define I2C_SLAVE_ADDRESS 65

void setup()
{
 Serial.begin(57600);
 Wire.begin(I2C_SLAVE_ADDRESS);
 Wire.onReceive(manageEvent);
}

void manageEvent() {
 if (Wire.available() > 0) {  
   int8_t readValue = Wire.read();
   Serial.println(readValue, DEC);
 }
}

void loop()
{
 
}

J'ai juste rajouté la lecture en décimale Serial.println(readValue, DEC); pour avoir toutes les valeurs négatives.

Serait-il nécessaire de créer une structure avec les valeurs x et y afin de ne pas encombrer le bus I2C ?

Inverser les 2 derniers arguments de map permet de choisir le sens de variation du potentiomètre. Donc c'est toi qui voit si c'est le bon sens ou pas. tu as aussi l'option de modifier le cablâge du potentiomètre pour faire ça.

Tu peux ne faire qu'une seule fonction et tu lui passes un argument supplémentaire pour lui dire si elle travaille sur x ou y et elle choisit le map qui va bien.

Si la seule charge du bus c'est l'émission de 2 valeurs il ne va pas être trop surchargé.

Avec un if ...else de ce style ? :


char X = 0;
char Y = 0;

void readRegisterAndSendValue(int8_t potentiometerPin, int8_t *previousValue, int8_t slaveAddress, char axe) {

  int potValue = analogRead(potentiometerPin);
  
 if (axe = X){ 
    potValue= map(potValue, 1023, 0, -128, 127);
  }
  else
  {
   potValue= map(potValue, 0, 1023, -128, 127);
  }
...
}

void loop()
{delay(100);
  readRegisterAndSendValue(POTX_PIN, &previousPotXValue, I2C_SLAVE_ADDRESS, X);
  readRegisterAndSendValue(POTY_PIN, &previousPotYValue, I2C_SLAVE_ADDRESS, Y);
}

Pour l'instant je jongle avec les seules valeurs des potars pour me faire la main avec I2C, mais dans les codes finaux, il y a pas mal d'échanges, limités à pas plus d'1 octet comme indiqué dans le code maître de mon #post1.

Pour un test on fait ==
Si tu veux passer le caractère X pour identifier l'axe alors le test à faire c'est

if (axe == 'X')

Si tu n'envisages pas de gérer d'autres axes tu pourrais aussi utiliser un bool au lieu d'un char

void readRegisterAndSendValue(int8_t potentiometerPin, int8_t *previousValue, int8_t slaveAddress, bool axeX) {

et faire le test

if(axeX)

:face_with_open_eyes_and_hand_over_mouth: Oui, la boulette quoi ...

Oui, c'est ça.

NOn, il y a les deux axes x et y du joystick

J'ai laissé tomber le flag pour utiliser une fonction avec pointeur.

Je me réponds.
La variable i2c_reg est bien lue par l'esclave sur le bus I2C, mais reste cantonnée à la fonction receiveEvent lorsque les données sont envoyées par le maître depuis

Wire.beginTransmission();
Wire.write();
Wire.endTransmission();

Par contre elles sont accessibles aussi bien dans receiveEvent que dans requestEvent lorsqu'elles sont envoyées par une fonction contenant Wire.requestFrom().

À mon post#1, la fonction readI2CReg(byte slave_addr, byte reg_addr) transmet dans les 2 sur l'esclave.
La fonction writeI2CReg(byte slave_addr, byte reg_addr, byte d_buff) ne transmet que dans receiveEvent.

byte readI2CReg(byte slave_addr, byte reg_addr) {
  Wire.beginTransmission(slave_addr);
  Wire.write(reg_addr);
  Wire.endTransmission();
  Wire.requestFrom(slave_addr, (byte)1);
 return Wire.read();
}

void writeI2CReg(byte slave_addr, byte reg_addr, byte d_buff) {
  Wire.beginTransmission(slave_addr);
  Wire.write(reg_addr);
  Wire.write(d_buff);
  Wire.endTransmission();
}

Comment alors faire pour ajuster la portée de i2c_reg à receiveEvent ET requestEvent ?

Peu importe la portée de i2c_reg, de toute manière pas plus un switch qu'un if ne fonctionnent dans requestEvent().

Je rame laborieusement, mais j'avance.

Finalement à force de Serial.print par ci, par là sur l'esclave j'ai obtenu des valeurs transmises par le maître qui varient en fonction des variations du joystick. Est-ce les valeurs attendues ? je ne sais pas mais elles ne dépassent pas l'octet.

Sur le maître les valeurs du joystick sont bien transmises et comprises entre -128 et 127. En sortie série sur l'esclave

receiveEvent i2c_reg = 2D | JS_T_CTRL_REG                                                       |                                                   |
receiveEvent i2c_reg = 6  |                                                                     |                                                   |
------------------------------------------------------------------------------------------------|                                                   |
receiveEvent i2c_reg = 10 | première lecture après le boot                                      |                                                   |
receiveEvent i2c_reg = 11 |                                                                     |                                                   |
------------------------------------------------------------------------------------------------|                                                   |
receiveEvent i2c_reg = 10 | 4 points de  données pour chaque axe pour calculer la moyenne       |                                                   |
receiveEvent i2c_reg = 11 |                                                                     |                                                   |
receiveEvent i2c_reg = 10 |                                                                     |   void EasyPointInit()                            |
receiveEvent i2c_reg = 11 |                                                                     |                                                   |
receiveEvent i2c_reg = 10 |                                                                     |                                                   |
receiveEvent i2c_reg = 11 |                                                                     |                                                   |
receiveEvent i2c_reg = 10 |                                                                     |                                                   |
receiveEvent i2c_reg = 11 |                                                                     |                                                   |
------------------------------------------------------------------------------------------------|                                                   |
receiveEvent i2c_reg = C  | ? boucle if ?                                                       |                                                   |
----------------------------------------------------------------------------------------------------------------------------------------------------|
receiveEvent i2c_reg = 12 | JS_XP_REG                                                           |                                                   |
receiveEvent i2c_reg = 18 | xp                                                                  |                                                   |
receiveEvent i2c_reg = 13 | JS_XN_REG                                                           |                                                   |
receiveEvent i2c_reg = E6 | xn                                                                  |                                                   |
receiveEvent i2c_reg = 14 | JS_YP_REG                                                           |                                                   |
receiveEvent i2c_reg = 14 | yp                                                                  |   EasyPointIntSetup(char x_null, char y_null,     |
receiveEvent i2c_reg = 15 | JS_YN_REG                                                           |                     byte x_delta, byte y_delta)   |
receiveEvent i2c_reg = E2 | yn                                                                  |                                                   |
------------------------------------------------------------------------------------------------|                                                   |                                             
receiveEvent i2c_reg = F  | JS_CTRL_REG_1 | byte temp = readI2CReg(JS_ADDR, JS_CTRL_REG_1);     |                                                   |
------------------------------------------------------------------------------------------------|                                                   |
receiveEvent i2c_reg = F  | JS_CTRL_REG_1 | writeI2CReg(JS_ADDR, JS_CTRL_REG_1, temp);          |                                                   |
receiveEvent i2c_reg = 54 | temp          |                                                     |                                                   |
----------------------------------------------------------------------------------------------------------------------------------------------------|
receiveEvent i2c_reg = 10 | Serial.print des readAxis() sur maître                                                                                  |
receiveEvent i2c_reg = 11 |                                                                                                                         |
----------------------------------------------------------------------------------------------------------------------------------------------------|


Trouvé sur Github : https://github.com/ServError/Thrustmaster_Warthog_Throttle_Alps_Slew_Mod
J'étais loin du compte en voulant passer par l'ide arduino et la bibliothèque Wire.
En fait cette bibliothèque wire n'est adaptée que pour des choses très basiques, mais dès qu'il s'agit d'agir sur les timers ou les interruptions, ça coince vraiment. Dommage que nulle part dans les tutos français il ne soit abordé les limites de Wire. Ça éviterait certainement les pertes de temps inutiles à vouloir jouer avec ces handlers.
Y a-t-il une doc complète sur Wire quelque part sans avoir à éplucher le code lui-même ? C'est-à-dire quelque chose de complet et qui soit accessible pour les non-experts ?