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.
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.