8 months, 1 week ago.

Can I declare DigitalOut methods as virtual in the header file?

In my project I have a port expander which I use for additional SPI Chip Select (CS) pins. This IC can be configured by using I2C. It would be very comfortable to use all the mbed classes with this new CS pin. Therefore I derive a class DigitalOutVirtual from DigitalOut and overwrite all methods. Hence I can use code like the one blow. The problem is that the methods of DigitalOut are not virtual. What means that I need to change them to virtual in the header file. Now the actuall question. Can you think of problems that might occure by changing the DigitalOut methods to virtual? I know that it is bad practice to modifie the mbed classes but I don't know another way where the effort is manageable. Thank you.

DigitalOut mosi (PIN_1);
DigitalIn  miso (PIN_2);
DigitalOutVirtual cs (PORTEXPANDER, PORT_EXP_PIN_1);

cs = 1; 
SPI mySPI(mosi, miso, cs);
Comment on this question

1 Answer

8 months, 1 week ago.

Hello Dominik,

That's a good idea. If you plan to use DigitalOutVirtual as base class for your new similar classes (and use late binding) then you can do it. But in case you'd just like to have a class that is similar to DigitalOut then you could try composition with a I2C data member:

  • Define a member pointing to I2C class to be shared with all objects (saves resources)
  • Design methods similar to DigitalOut (do not have to be virtual but can be, it's up to you).
    Then a draft (not tested) ChipSelect could look like:

ChipSelect.h

#ifndef CHIPSELECT_H
#define CHIPSELECT_H

#include "mbed.h"

const int   ADDR = 0x4E;        // default address (A2, A1, A0 not grounded) shifted one bit to the left (to use 8 bit address) for PCF8574 chip controlling the CS pins
const int   FREQ = 100;         // 100kHz

class ChipSelect
{
    char    m_pin;              // pattern to select (pull down) the given pin (P0, P1, ..., P7)
    I2C*    m_i2c;              // pointer to a I2C instance shared by all ChipSelect objects
    int     m_addr;             // 8 bit address of the I2C chip

public:
    ChipSelect(int pin, I2C* i2c, int addr = ADDR) : m_pin(~(1 << pin)), m_i2c(i2c), m_addr(addr)
    {
        const char allPins = 0xFF; //  pattern to unselect (pull up) all pins
        
        m_i2c->frequency(FREQ);
        m_i2c->write(m_addr, &allPins, sizeof(allPins));  // unselects (pulls up) all pins
    }

    void write(int value)
    {
        const char pinPattern = ((value == 0) ? m_pin : 0xFF); // value '0' selects (pulls down) the given pin, '1' unselects (pulls up) all pins

        m_i2c->write(m_addr, &pinPattern, sizeof(pinPattern));
    }

    ChipSelect &operator=(int value) { write(value); return *this; }    // for the convenience
};
#endif // CHIPSELECT_H

main.cpp

#include "mbed.h"
#include "ChipSelect.h"

const int   I2C_ADDR = 0x7E;                // PCF8574A address (A2, A1, A0 not grounded) shifted one bit to the left (to use 8 bit address)
DigitalOut  led1(LED1);
I2C         csI2C(D14, D15);                // I2C controlling chip select pins (shared by all ChipSelect objects) - SDA, SCL
ChipSelect  csDevice0(0, &csI2C, I2C_ADDR); // chip select pin P0 for device #0
ChipSelect  csDevice1(1, &csI2C, I2C_ADDR); // chip select pin P1 for device #1
ChipSelect  csDevice2(2, &csI2C, I2C_ADDR); // chip select pin P2 for device #2
SPI         mySPI(D11, D12, D13);           // SPI for communication with devices - MOSI, MISO, SCK

int main()
{
    int data = 0;

    while (true) {
        data++;             // set data
        csDevice0 = 0;      // select device #0
        mySPI.write(data);  // write data to device #0
        csDevice0 = 1;      // unselect device #0
        //
        data++;             // set data
        csDevice1 = 0;      // select device #1
        mySPI.write(data);  // write data to device #1
        csDevice1 = 1;      // unselect device #1
        //
        data++;             // set data
        csDevice2 = 0;      // select device #2
        mySPI.write(data);  // write data to device #2
        csDevice2 = 1;      // unselect device #2
        //...
        led1 = !led1;
        wait(0.5);
    }
}