expanders

Ciao a tutti, vorrei un aiuto per risolvere un mio crucio.
sto da poco facendo dei test su arduino per poterlo espandere tramite I2C.
ho optato per l' integrato PCF8574 che controllo ormai in maniera molto semplice tramite delle librerie recuperate dalla rete.
il mio problema è ora capire come faccio a far prendere ai pin digitali dell' espansione una numerazione coerente con la numerazione dei pin di arduino.
per esempio io ho un servo collegato sul pin 1 dell'expander2, quale codifica devo dare allo sketch per poter usare un comando tipo : servo1.attach(x); della libreria servo.h?
cosa devo mettere al posto della x? normalmente metti il pin della scheda dove è collegato il servo.

immagino che ci sia una libreria che mi permette di far ciò ma non son riuscito a trovar nulla, la mia esperienza attuale purtroppo non mi consente di arrivare a scrivere librerie ad-hoc per i miei progetti.
grazie a tutti

io ho votato la prima opzione :stuck_out_tongue_closed_eyes:

non ho ben capito che fossero....pardon :blush:

La libreria servo.h funziona solo con i Pin del Arduino.
Se vuoi pilotare tanti Servo prendi un Arduino MEGA oppure una scheda come queste Pololu - RC Servo Controllers
Gli expander sono ideati per avere entrate/uscite statiche e non modiulate in PWM o PPM
Altra possibilitá: usa i pin del Arduino per pilotare i Servo e le entrate/uscite del expander per i pulsanti o per il display.

Ciao Uwe

innanzitutto grazie per aver risposto.
ok per quanto riguarda la gestione dei servo, ma supponiamo che io voglia in qualche modo remotizzare uno o più ricevitori ir che normalmente generano un output digitale. come posso fare in modo che il mio arduino legga attraverso I2C questa segnalazione?

chiedo scusa se faccio richieste strane...

É possibile usare un expander I2C ma é complicato e un gran impegno scrivere un codice ben funzionante. Te lo sconsiglio.
Ciao Uwe

mmm....pensavo che fosse complicato per me che sono inesperto....
questo mi consola un po.
fatto stà che non mi risolve il dilemma :slight_smile:
in sostanza se io avessi la necessità di leggere ed interpretare sensori a distanza su I2C non lo posso fare dagli ingressi digitali delle mie espansioni.
dovrei usare queste solo per gestire ingressi e uscite digitali e per i sensori ed eventuali attuatori dovrei affidarmi a integrati specifici posti su bus I2C.
Grazie comunque per i preziosi consigli. e grazie a chi si prodiga così tanto per rendere sempre più potente l'open!

ciao speleoduracell
potresti indicarmi un collegamento per le librerie che hai usato per la gestione del PCF8574.
vorrei provare a gestire una serie di relè ma dal esempio del playground ho capito poco.

di seguito intervallate dagli * le librerie e un piccolo esempio di funzionamento.

la PCF8574.cpp:****

#include "PCF8574.h"
#include "PCint.h"

PCF8574::PCF8574(){
resetRegister();
}

PCF8574::~PCF8574(){
/* DO NOTHING */
}

void PCF8574::begin(int SADR){
PCFaddress=SADR;
Wire.begin();
}

void PCF8574::pinMode(int pin, int mode){
bitWrite(PCFTRISA,pin,mode);
}

void PCF8574::digitalWrite(int pin,int value){
if((pin >= 0) && (pin < 8)){
bitWrite(PCFPORTA,pin,value);
i2cSend();
}
}

int PCF8574::digitalRead(int pin){
if((pin >= 0) && (pin < 8)){
i2cRead();
return bitRead(PCFPORTA,pin);
}
}

void PCF8574::write(int value){
PCFPORTA = value;
i2cSend();
}

void PCF8574::pullUp(int pin){
if((pin >= 0) && (pin < 8)){
PCFPULL[pin] == 1;
i2cRead();
bitWrite(PCFPORTA,pin,1);
i2cSend(PCFPORTA);
}
}

void PCF8574::pullDown(int pin){
if((pin >= 0) && (pin < 8)){
PCFPULL[pin] == 2;
i2cRead();
bitWrite(PCFPORTA,pin,0);
i2cSend(PCFPORTA);
}
}

int PCF8574::read(){
i2cRead();
return PCFPORTA;
}

void PCF8574::clear(){
PCFPORTA = 0x00;
i2cSend();
}

void PCF8574::set(){
PCFPORTA = 0xFF;
i2cSend();
}

void PCF8574::toggle(int pin){
if((pin >= 0) && (pin < 8)){
i2cRead();
bitWrite(PCFPORTA,pin,!bitRead(PCFPORTA,pin));
i2cSend();
}
}

void PCF8574::blink(int pin,int time,int wait){
if((pin >= 0) && (pin < 8)){
i2cRead();
for(int i=0;i<time;i++){
bitWrite(PCFPORTA,pin,0);
i2cSend();
delay(wait);
bitWrite(PCFPORTA,pin,1);
i2cSend();
delay(wait);
}
}
}

void PCF8574::enableInterrupt(int pin,void(* selfCheckFunction)(void)){
PCFIntPin = pin;
pinMode(PCFIntPin,INPUT);
PCattachInterrupt(PCFIntPin,selfCheckFunction,FALLING);
}

void PCF8574::disableInterrupt(){
PCdetachInterrupt(PCFIntPin);
}

#ifdef INT_INPUT_ONLY
#define INT_CHECK (PCFTRISA == INPUT)
#else
#define INT_CHECK true
#endif
void PCF8574::checkForInterrupt(){

  • sei();*
  • PCFBUFFER = i2cRead(0x00);*
  • for(int i=0;i<8;i++){*
    _ switch(PCFINT*){_
    _
    case CHANGE:_
    if(INT_CHECK && (bitRead(PCFPORTA,i) != bitRead(PCFBUFFER,i)))
    _ PCFJMP();
    break;
    case LOW:_
    if(INT_CHECK && (bitRead(PCFBUFFER,i) == 0))
    _ PCFJMP();
    break;
    case FALLING:_

    if(INT_CHECK && (bitRead(PCFPORTA,i) == 1) && (bitRead(PCFBUFFER,i) == 0))
    _ PCFJMP();
    break;
    case RISING:_

    if(INT_CHECK && (bitRead(PCFPORTA,i) == 0) && (bitRead(PCFBUFFER,i) == 1))
    _ PCFJMP();
    break;
    case 255:
    break;
    }
    }
    }
    void PCF8574::attachInterrupt(int pin,void (*userFunc)(void),int mode){
    if((pin >= 0) && (pin < 8)){
    PCFINT[pin] = mode;
    PCFJMP[pin] = userFunc;
    }
    }
    void PCF8574::detachInterrupt(int pin){
    if((pin >= 0) && (pin < 8)){
    PCFINT[pin] = 255;
    }
    }
    void PCF8574::resetRegister(){
    PCFPORTA =0;
    PCFTRISA =0;
    PCFBUFFER =0;
    for(int i=0;i<8;i++)
    PCFINT = 255;
    }
    void PCF8574::i2cSend(){
    PCFBUFFER = i2cRead(0x00);
    for(int i=0;i<8;i++){
    if(bitRead(PCFTRISA,i) == INPUT)
    bitWrite(PCFPORTA,i,bitRead(PCFBUFFER,i));
    if(PCFPULL == 1) bitWrite(PCFPORTA,i,1); // Required for unblock pull level
    if(PCFPULL == 2) bitWrite(PCFPORTA,i,0); // Required for unblock pull level
    }
    Wire.beginTransmission(PCFaddress);
    Wire.write(PCFPORTA);
    Wire.endTransmission();
    }
    void PCF8574::i2cSend(int mask){
    Wire.beginTransmission(PCFaddress);
    Wire.write(PCFPORTA);
    Wire.endTransmission();
    }
    void PCF8574::i2cRead(){_

    uint8_t value = 0x00;
    _ Wire.requestFrom(PCFaddress, 1);
    if(Wire.available()) {
    value = Wire.read();
    }
    PCFPORTA = (int)value;
    }
    int PCF8574::i2cRead(uint8_t value){
    Wire.requestFrom(PCFaddress, 1);
    if(Wire.available()) {
    value = Wire.read();
    }
    return (int)value;
    }
    ***************************************** la libreria PCF8574.h :_
    #ifndef PCF8574_H

    #define PCF8574_H

    _#include <Arduino.h>

    #include <Wire.h>

    typedef void (*FunctionPointer)(void);
    class PCF8574 {
    public:
    PCF8574();

    ~PCF8574();

    void begin(int SADR);

    void pinMode(int pin, int mode);

    void digitalWrite(int pin,int value);

    int digitalRead(int pin);

    void write(int value);

    int read();

    void clear();

    void set();

    void pullUp(int pin);

    void pullDown(int pin);

    void toggle(int pin);

    void blink(int pin,int time,int wait);

    void enableInterrupt(int pin,void( selfCheckFunction)(void)); // Require pointer to an user create gateway function because checkForInterrupt() is in PCF8574 namespace.

    void disableInterrupt();

    void attachInterrupt(int pin,void (userFunc)(void),int mode);
    void detachInterrupt(int pin);

    void checkForInterrupt();

    private:
    volatile int PCFPORTA;

    volatile int PCFPULL[8];

    volatile int PCFTRISA;

    volatile int PCFBUFFER;

    volatile int PCFaddress;

    int PCFIntPin;

    * volatile int PCFINT[8];
    volatile FunctionPointer PCFJMP[8];
    void resetRegister();
    void i2cSend();
    void i2cSend(int mask);
    void i2cRead();_

    int i2cRead(uint8_t value);
    _};*_

#endif
*************************************un piccolo esempio che ho usato per testare il funzionamento:*****************************************************
#include <PCF8574.h> // Required for ... all
#include <Wire.h> // Required for I2C communication
PCF8574 A; // Create object from PCF8574 class
PCF8574 B;
void setup(){
* B.begin(0x20);*
* A.begin(0x27); *
* pinMode(13,OUTPUT);*
* Serial.begin(9600); // Setup serial for read echo*

* A.pinMode(0,OUTPUT); // Setup pin D0 as output*
* A.pinMode(1,OUTPUT); // Setup pin D1 as output*
* A.pinMode(2,OUTPUT); // Setup pin D2 as output*
* A.pinMode(3,OUTPUT); // Setup pin D3 as input*
* A.pinMode(4,INPUT); // Setup pin D0 as output*
* B.pinMode(5,INPUT); // Setup pin D1 as output*
* B.pinMode(6,INPUT); // Setup pin D2 as output*
* B.pinMode(7,INPUT); // Setup pin D3 as input*

* }*
* void loop(){*
* if(A.digitalRead(4))*
* {*
* A.digitalWrite(0,HIGH);*
* delay(500);*
* A.digitalWrite(1,HIGH);*
* delay(500);*
* A.digitalWrite(2,HIGH);*
* delay(500);*
* A.digitalWrite(3,HIGH);*
* delay(500);*
* digitalWrite(13,HIGH);*
* delay(500);*
* }*
* else*
* {*
* A.digitalWrite(0,LOW);*
* A.digitalWrite(1,LOW);*
* A.digitalWrite(2,LOW);*
* A.digitalWrite(3,LOW);*
* digitalWrite(13,LOW);*
* }*

}
ciao Speleoduracell.

grazie
questa sera provo a vedere se ci capisco qualche cosa.
poi se non ne salto fuori penso che tornerò a chiedere.

a grandi linee tu hai usato il PCF dando
da 0 a 3 come ingressi
da 4 a 7 come uscite

poi non ho capito perchè dai due indirizzi al PCF

B.begin(0x20);
A.begin(0x27);

lo sketch in base al cambio di stato degli ingressi dovrebbe accendere o spegnere dei led.

un altra cosa che ho notato ora

guardando il tuo messaggio
riguardo la PCF8574.cpp:

#include "PCF8574.h" OK la trovo nel tuo precedente post
#include "PCint.h" non è inclusa

che libreria è questa?

ciao scusa il delay...
ho impostato ingressi ed uscite a naso, il software ti permette di usare ingressi e uscite come se fossero i pin digitali di arduino, con notevole semplificazione di sintassi.
il fatto che do due indirizzi è perchè ho messo nell' esempio due pcf8574 collegati sullo stesso bus nessuno ti vieta di usarne uno solo oppure du usarne fino a 8 della stessa specie(se integri gli pcf8574af puoi arrivare a 16 indirizzi....trovi tutto sul datasheet).

la libreria PCint.h dovrebbe gestire gli interrupt, mi ricordavo fosse inclusa nel software di arduino e invece no... te la copio:

#include "pins_arduino.h"

volatile uint8_t *port_to_pcmask[] = {
&PCMSK0,
&PCMSK1,
&PCMSK2
};

static int PCintMode[24];

typedef void (*voidFuncPtr)(void);

volatile static voidFuncPtr PCintFunc[24] = {
NULL };

volatile static uint8_t PCintLast[3];

/*

  • attach an interrupt to a specific pin using pin change interrupts.
    */
    void PCattachInterrupt(uint8_t pin, void (*userFunc)(void), int mode) {
    uint8_t bit = digitalPinToBitMask(pin);
    uint8_t port = digitalPinToPort(pin);
    uint8_t slot;
    volatile uint8_t *pcmask;

// map pin to PCIR register
if (port == NOT_A_PORT) {
return;
}
else {
port -= 2;
pcmask = port_to_pcmask[port];
}

// -- Fix by Baziki. In the original sources it was a little bug, which cause analog ports to work incorrectly.
if (port == 1) {
slot = port * 8 + (pin - 14);
}
else {
slot = port * 8 + (pin % 8);
}
// --Fix end
PCintMode[slot] = mode;
PCintFunc[slot] = userFunc;
// set the mask
*pcmask |= bit;
// enable the interrupt
PCICR |= 0x01 << port;
}

void PCdetachInterrupt(uint8_t pin) {
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
volatile uint8_t *pcmask;

// map pin to PCIR register
if (port == NOT_A_PORT) {
return;
}
else {
port -= 2;
pcmask = port_to_pcmask[port];
}

// disable the mask.
*pcmask &= ~bit;
// if that's the last one, disable the interrupt.
if (*pcmask == 0) {
PCICR &= ~(0x01 << port);
}
}

// common code for isr handler. "port" is the PCINT number.
// there isn't really a good way to back-map ports and masks to pins.
static void PCint(uint8_t port) {
uint8_t bit;
uint8_t curr;
uint8_t mask;
uint8_t pin;

// get the pin states for the indicated port.
curr = *portInputRegister(port+2);
mask = curr ^ PCintLast[port];
PCintLast[port] = curr;
// mask is pins that have changed. screen out non pcint pins.
if ((mask &= *port_to_pcmask[port]) == 0) {
return;
}
// mask is pcint pins that have changed.
for (uint8_t i=0; i < 8; i++) {
bit = 0x01 << i;
if (bit & mask) {
pin = port * 8 + i;
// Trigger interrupt if mode is CHANGE, or if mode is RISING and
// the bit is currently high, or if mode is FALLING and bit is low.
if ((PCintMode[pin] == CHANGE
|| ((PCintMode[pin] == RISING) && (curr & bit))
|| ((PCintMode[pin] == FALLING) && !(curr & bit)))
&& (PCintFunc[pin] != NULL)) {
PCintFuncpin;
}
}
}
}

SIGNAL(PCINT0_vect) {
PCint(0);
}
SIGNAL(PCINT1_vect) {
PCint(1);
}
SIGNAL(PCINT2_vect) {
PCint(2);
}

non mi riesco a ricordare da dove attinsi tutto questo ma devo dire che è veramente ben fatto e io mi son trovato davvero bene, riesco ad avere 128 ingressi/uscite sul mio arduino lasciando liberi totalmente i pin della scheda!

ciaoo

grazie

Comunque è necessaria la libreria PCint.h perchè se non erro dovrevve servire se collego il piedino INT dell'integrato.
Se ho detto una cosa giusta riguardo l'uso della libreria,ti dico questo perchè ho visto su altri post che non collegano anche il pin INT per la gestione degli IO.

a questo punto aspetto che mi arrivi il PCF e provo realmente la gestione degli IO
in modo da andare a step e capire come funziona.

esatto, quel pin è l' uscita interrupt appunto. vedi la pagina dodici del datasheet è tutto scritto li.
questa libreria la usi quando colleghi questo pin quindi quando di questo hai bisogno.
se non usi il pin non ti serve inserirla.

ok
ma in pratica a cosa serve il pin INT.

Subia75:
ok
ma in pratica a cosa serve il pin INT.

Serve a presentare su quel pin un'onda quadra con una determinata frequenza, generata partendo dall'oscillatore interno del micro, le frequenze disponibili sono riportare sul datasheet. Serve se vuoi sincronizzare o pilotare dispositivi esterni.

in pratica se devo comandare un contatto di un relè devo usarlo o posso fare a meno?