В связи с соседней темой про запуск библиотеки SD.h на аппаратном или софтовом SPI хотел спросить опытных…
Чтобы готовая библиотека могла работать как с аппаратным, так и софтовым СПИ, создадим два класса Hard & Soft SPI . Интерфейс у них, понятно, будет одинаковый - совместимый со стандартным SPI Arduino:
Классы
class HardSPI {
public:
// var
uint32_t _clock = 4000000;
uint16_t _bitOrder = LSBFIRST;
uint8_t _dataMode = SPI_MODE0;
// methods
HardSPI();
void SPI_Settings(uint32_t clock, uint16_t bitOrder, uint8_t dataMode);
void beginTransaction(SPISettings settings);
void endTransaction();
uint8_t transfer(uint8_t);
};
class SoftSPI {
public:
// var
uint32_t _clock = 4000000;
uint16_t _bitOrder = LSBFIRST;
uint8_t _dataMode = SPI_MODE0;
// methods
SoftSPI(uint8_t mosi, uint8_t miso, uint8_t sck);
void SPI_Settings(uint32_t clock, uint16_t bitOrder, uint8_t dataMode);
void beginTransaction(SPISettings settings);
void endTransaction();
uint8_t transfer(uint8_t);
};
Чтобы обращаться к ним единообразно, создадим для них виртуальный базовый класс и унаследуем реальные классы от базового:
BaseSPI
class BaseSPI {
public:
// var
uint32_t _clock = 4000000;
uint16_t _bitOrder = LSBFIRST;
uint8_t _dataMode = SPI_MODE0;
// methods
BaseSPI() {};
virtual void SPI_Settings(uint32_t clock, uint16_t bitOrder, uint8_t dataMode) =0;
virtual void beginTransaction(SPISettings settings) =0;
virtual void endTransaction() =0;
virtual uint8_t transfer(uint8_t) =0;
};
class HardSPI : public BaseSPI {
public:
// var
// defined in base class
// methods
HardSPI();
void SPI_Settings(uint32_t clock, uint16_t bitOrder, uint8_t dataMode);
void beginTransaction(SPISettings settings);
void endTransaction();
uint8_t transfer(uint8_t);
};
class SoftSPI : public BaseSPI {
public:
// var
// defined in base class
// methods
SoftSPI(uint8_t mosi, uint8_t miso, uint8_t sck);
void SPI_Settings(uint32_t clock, uint16_t bitOrder, uint8_t dataMode);
void beginTransaction(SPISettings settings);
void endTransaction();
uint8_t transfer(uint8_t);
};
Теперь мы можем, в зависимости от потребности, работать через общий интерфейс с обоими SPI классами:
BaseSPI *SPI = new SoftSPI(mosi, miso, sck);
Собственно, наконец я подхожу к вопросу, ради которого все это написал.
В строчке выше мы создаем идентификатор SPI как ссылку на базовый класс. Однако большинство из библиотек Ардуино обращаются к SPI непосредственно, а не по ссылке.
Например в той же либе SD.h обращения идут как
SPI.endTransaction();
Но к нашему базовому классу надо обращаться как
SPI->endTransaction();
Можно, конечно, определить подобный дефайн
#define mySPI (*SPI)
но это же костыль…
Читая книжки, я понял что есть вариант создать класс-обертку, котоорый будет содержать ссылку на наш SPI класс и вызывать его методы по ссылке:
class HandlerSPI {
public:
// var
BaseSPI *pSPI;
// methods
HandlerSPI(BaseSPI *ptr): pSPI(ptr);
void SPI_Settings(uint32_t clock, uint16_t bitOrder, uint8_t dataMode);
void beginTransaction(SPISettings settings);
void endTransaction();
uint8_t transfer(uint8_t);
};
но получается тогда в нем нам придется продублировать все методы базового класса
void HandlerSPI ::beginTransaction(SPISettings settings)
{pSPI -> :beginTransaction(settings);}
Дублирование это плохо, разименовывыние ссылки через дефайн - костыль.
Может кто-нибудь предложит что-нибудь еще для единообразного обращения к дочерним классам?
У меня ощущение, что я не вижу каких-то очевидных вещей.