Problema con descriptores de ficheros

Iniciado por iapellaniz, 15 Octubre 2014, 19:01 PM

0 Miembros y 1 Visitante están viendo este tema.

iapellaniz

Hola a todos:

Lo primero es agradecer las posibles respuestas que me podáis dar.

Os cuento mi problema. Estoy desarrollando un programa en C++, para la Raspberry, que hace uso de un LCD (Kit Adafruit LCD 16x2 ). Este LCD hace uso de un chip MCP23017 y se comunica con la Rasp mediante i2c. El caso es que existe una librería en Python para este LCD y lo que estoy intentando es traducirlo a C++.

La comunicación del sistema operativo (Raspbian) con i2c es mediante el uso de descriptores de ficheros. Compruebo que abro bien el fichero, pero a la hora de hacer operaciones con él me cambia el descriptor de fichero y no encuentro la causa.

Este es el resultado que obtengo:
root@raspberrypi:/liquidcrystal# ./prueba
openI2C - File Descriptor: 3
i2c_smbus - File Descriptor: 0

Os copio la clase que controla el acceso a i2c.

#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <string>

#include "i2c8Bit.h"

using namespace std;

/*****************************************************************
* This is the default constructor for the class. It assigns
* all private variables to default values and calls the openI2C()
* function to open the default I2C device "/dev/i2c-0".
*****************************************************************/
i2c8Bit::i2c8Bit(uint8_t dev_addr){
   int revision = this->piBoardRev();
   if (revision == 1) i2c8Bit(dev_addr, string("/dev/i2c-0"));
   else if (revision == 2) i2c8Bit(dev_addr, string("/dev/i2c-1"));
   else {printf("Error"); exit(1);}
}

/*******************************************************************
* This is the overloaded constructor. It allows the programmer to
* specify a custom I2C device & device address
* The device descriptor is determined by the openI2C() private member
* function call.
* *****************************************************************/

i2c8Bit::i2c8Bit(uint8_t dev_addr, string i2c_file_name){
   this->i2cFileName = i2c_file_name;
   this->deviceAddress = dev_addr;
   this->i2cDescriptor = 0;

   this->openI2C();
}
/**********************************************************************
* This is the class destructor it simply closes the open I2C device
* by calling the closeI2C() which in turn calls the close() system call
* *********************************************************************/

i2c8Bit::~i2c8Bit(void){
   //cout << " Closing I2C Device" << endl;
   this->closeI2C();
}

/**********************************************************************
* This function opens the I2C device by simply calling the open system
* call on the I2C device specified in the i2cFileName string. The I2C
* device is opened for writing and reading. The i2cDescriptor private
* variable is set by the return value of the open() system call.
* This variable will be used to reference the opened I2C device by the
* ioctl() & close() system calls.
* ********************************************************************/

int i2c8Bit::openI2C(){
   
   if (this->i2cDescriptor)  this->closeI2C();

   this->i2cDescriptor = open(this->i2cFileName.c_str(), O_RDWR);
   if (this->i2cDescriptor < 0) {
      perror("Could not open file");
       exit(1);
   }

   if (ioctl(this->i2cDescriptor, I2C_SLAVE, this->deviceAddress) < 0)
      {
         perror("Unable to select I2C device");
         exit(1);
      }

   printf("openI2C - File Descriptor: %i \n", this->i2cDescriptor);
   return 0;
}

/*********************************************************************
* This function closes the I2C device by calling the close() system call
* on the I2C device descriptor.  
* *******************************************************************/

int i2c8Bit::closeI2C(){
   int retVal = -1;

   if (this->i2cDescriptor)
    {
      retVal = close(this->i2cDescriptor);
      if(retVal < 0){
         perror("Could not close file (1)");
         exit(1);
      }
      this->i2cDescriptor = 0;
    }   
   
return retVal;
}

int i2c8Bit::piBoardRev (void)
{
 FILE *cpuFd ;
 char line [120] ;
 char *c ;
 static int  boardRev = -1 ;

 if (boardRev != -1)   // No point checking twice
   return boardRev ;

 if ((cpuFd = fopen ("/proc/cpuinfo", "r")) == NULL)
  { perror("Unable to open /proc/cpuinfo"); exit(1); }

 while (fgets (line, 120, cpuFd) != NULL)
   if (strncmp (line, "Revision", 8) == 0)
     break ;

 fclose (cpuFd) ;

 if (strncmp (line, "Revision", 8) != 0)
  { perror("No \"Revision\" line"); exit(1); }

// Chomp trailing CR/NL

 for (c = &line [strlen (line) - 1] ; (*c == '\n') || (*c == '\r') ; --c)
   *c = 0 ;
 
// Scan to first digit

 for (c = line ; *c ; ++c)
   if (isdigit (*c))
     break ;

 if (!isdigit (*c))
  { perror("No numeric revision string"); exit(1); }
   

// Make sure its long enough

 if (strlen (c) < 4)
   { perror("Bogus \"Revision\" line (too small)"); exit(1); }
 

// Isolate  last 4 characters:

 c = c + strlen (c) - 4 ;

 if ( (strcmp (c, "0002") == 0) || (strcmp (c, "0003") == 0))
  boardRev = 1 ;
 else
  boardRev = 2 ;

 return boardRev ;
}

uint8_t i2c8Bit::i2cRead()
{
   union i2c_smbus_data data ;
   
   if (this->i2c_smbus_operation(I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data)) return -1;
   
   return data.byte & 0xFF;
}

uint8_t i2c8Bit::i2cReadReg8(uint8_t reg)
{
 union i2c_smbus_data data;

 if (this->i2c_smbus_operation(I2C_SMBUS_READ, reg, I2C_SMBUS_BYTE_DATA, &data)) return -1 ;
 
 return data.byte & 0xFF;
}

uint16_t i2c8Bit::i2cReadReg16(uint8_t reg)
{
 union i2c_smbus_data data;

 if (this->i2c_smbus_operation(I2C_SMBUS_READ, reg, I2C_SMBUS_WORD_DATA, &data)) return -1 ;
 
 return data.word & 0xFFFF;
}

int i2c8Bit::i2cWrite(uint8_t data)
{
 return this->i2c_smbus_operation(I2C_SMBUS_WRITE, data, I2C_SMBUS_BYTE, NULL) ;
}

int i2c8Bit::i2cWriteList(uint8_t reg, uint8_t *value, int lenghtValue)
{
 union i2c_smbus_data data;
 
 data.block[0] = lenghtValue;
 
 for(int i = 0; i < lenghtValue; i++)
   data.block[i+1] = value;
 
//  return this->i2c_smbus_operation(I2C_SMBUS_WRITE, reg, I2C_SMBUS_BLOCK_DATA, &data);
 return this->i2c_smbus_operation(I2C_SMBUS_WRITE, reg, I2C_SMBUS_I2C_BLOCK_DATA, &data);
 
}

int i2c8Bit::i2cWriteReg8 (uint8_t reg, uint8_t value)
{
 union i2c_smbus_data data;

 data.byte = value ;
 return this->i2c_smbus_operation(I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA, &data) ;
 //return this->writeReg(reg, value);
}

int i2c8Bit::i2cWriteReg16 (uint8_t reg, uint16_t value)
{
 union i2c_smbus_data data ;

 data.word = value ;
 return this->i2c_smbus_operation(I2C_SMBUS_WRITE, reg, I2C_SMBUS_WORD_DATA, &data) ;
}


int i2c8Bit::i2c_smbus_operation(char rw, uint8_t command, int size, union i2c_smbus_data *data)
{
 struct i2c_smbus_ioctl_data args ;
 int operation;

 args.read_write = rw ;
 args.command    = command ;
 args.size       = size ;
 args.data       = data ;
 printf("i2c_smbus - File Descriptor: %i \n", this->i2cDescriptor);

 operation = ioctl(this->i2cDescriptor, I2C_SMBUS, &args);
 if (operation < 0)
  {
      perror("Unable to make operation"); exit(1);
  }
 
 return operation;
}

De nuevo, gracias a todos.

Saludos.




Hola de nuevo:

He seguido investigando y he descubierto que en cuanto creo el objeto en el programa principal, automáticamente se destruye. Por eso cambia el descriptor de fichero.

Lo que no sé es porque se destruye el objeto antes de la finalización del programa.

Os dejo la cabecera del objeto y el fichero de prueba.

#ifndef I2C8BIT_H
#define I2C8BIT_H

#include <string>
#include <stdint.h>
#include <linux/i2c.h>

class i2c8Bit {
    private:
        std::string i2cFileName; //i2c device name e.g."/dev/i2c-0" or "/dev/i2c-1"
        int i2cDescriptor;  // i2c device descriptor
        uint8_t deviceAddress; // i2c device address

        //private member functions
        int openI2C(); //open an I2C device. Called only in constructors
        int closeI2C(); // close an I2C device. Called only in destructor

        int piBoardRev();
      int i2c_smbus_operation(char, uint8_t, int, union i2c_smbus_data *);

    public:
        i2c8Bit(uint8_t); // default constructor
        i2c8Bit(uint8_t, std::string); //over loaded constructor
        ~i2c8Bit(); // destructor
       
      uint8_t i2cRead();
      uint8_t i2cReadReg8(uint8_t);
      uint16_t i2cReadReg16(uint8_t);

      int i2cWrite(uint8_t);
      int i2cWriteList(uint8_t, uint8_t *, int);
      int i2cWriteReg8(uint8_t, uint8_t);
      int i2cWriteReg16(uint8_t, uint16_t);

};

#endif



///========

Fichero main:

#include "i2c8Bit.h"

i2c8Bit *smbus;

int main()
{
    smbus = new i2c8Bit(0x20);
   
    smbus->i2cWriteReg8(0x0A,0x28);
   
   return 0;
}

El comando de compilación es: g++-4.8 prueba.c i2c8Bit.cpp -o prueba


Saludos.

Eternal Idol

El codigo de los constructores no concuerda con el comentario. ¿Lo cambiaste vos? Lo que estas haciendo al llamar al constructor de esa manera es crear un objeto temporario, podes comprobarlo con un depurador o imprimiendo en pantalla (en el constructor por defecto observa this, en el constructor sobrecargado tambien y finalmente en el destructor, despues cuando hagas delete en el main del objeto veras la direccion del this en el constructor por defecto), es es el que se destruye y es el unico que llama al metodo openI2C. Solucion sencilla llama al metodo openI2C desde el constructor ...
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

iapellaniz

Hola Eternal Idol:

Efectívamente, el constuctor lo modifiqué yo, me pareció más optimo. Sin embargo ha causado el efecto que dices. Dentro del constuctor por defecto, creaba un objeto temporal que era destruido inmediatamente.

Siguiendo tu consejo, este es el código que ha quedado en el constructor por defecto:

Código (cpp) [Seleccionar]

i2c8Bit::i2c8Bit(uint8_t dev_addr)
{
int revision = this->piBoardRev();
this->deviceAddress = dev_addr;

if (revision == 1){
this->i2cFileName ="/dev/i2c-0";
}
else if (revision == 2){
this->i2cFileName ="/dev/i2c-1";
}
else {printf("Error"); exit(1);}

    this->i2cDescriptor = 0;
   
this->openI2C();

}


Ahora funciona perfectamente. Muchísimas gracias.

Saludos.

Eternal Idol

La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón