Reputation: 100
I'm writing an embedded device driver library (Arduino Mega) where code size and performance goes hand in hand.
The SPI controller has a sequence of operation that goes:
The select/deselect are the start/stop of a transaction. This is done by asserting a pin either high or low. The SPI slave node circuitry may use standard, non-inverted digital signals (select == high voltage, deselect == ground) or inverted signals (select == ground, deselect == high voltage).
select() can be implemented using a run-time condition for checking if the pin is inverted (then issue a clearPin()) or not (issue a setPin()). The same goes for deselect(). Using consts and static definitions, the compiler should be able to solve the condition at compile time. As it turns out, the compiler (Avr-gcc 4.9) doesn't always realize that these expressions can be reduced (and inlined).
So, to get around this I'm trying to use conditional templates like this:
enum class SelectPinType : const bool
{
STANDARD = false,
INVERTED = true
};
template <SelectPinType tInvertedSelect>
struct SelectPin;
template<>
struct SelectPin<SelectPinType::INVERTED>
{
const Pin::IOPin &mChipSelectPin;
inline void select() const __attribute__((always_inline))
{
Pin::clr(mChipSelectPin); // Select inverted pin
}
inline void deselect() const __attribute__((always_inline))
{
Pin::set(mChipSelectPin); // Deselect inverted pin
}
};
template<>
struct SelectPin<SelectPinType::STANDARD>
{
const Pin::IOPin &mChipSelectPin;
inline void select() const __attribute__((always_inline))
{
Pin::set(mChipSelectPin); // Select standard pin
}
inline void deselect() const __attribute__((always_inline))
{
Pin::clr(mChipSelectPin); // Deselect standard pin
}
};
The select() and deselect() can then be used like:
// Uses digital pin # 33 as Select pin
static struct SelectPin<SelectPinType::INVERTED> SpiSel
{
Pin::PIN_DIO_33
};
...
SpiSel.select();
doTransfer(dataBuffer);
SpiSel.deselect();
Everything works fine and the select()/deselect() compiles to less than 5 assembler instructions. However, I can't find a solution of how to pass an instance of the struct SelectPin without having to specify the template condition in the function parameter list. In other words, if I want to wrapp the Select-Transfer-Deselect in a function I have to do the following:
// Inverted select pin transaction
inline void doTransaction(const SelectPin<SelectPinType::INVERTED> &fSelectPin, Spi::Transfer &fTransfer)
{
// Perform the SPI transaction
fSelectPin.select();
doTransfer(fTransfer);
fSelectPin.deselect();
// Done
return;
}
// Non-inverted select pin transaction
inline void doTransaction(const SelectPin<SelectPinType::STANDARD> &fSelectPin, Spi::Transfer &fTransfer)
{
// Perform the SPI transaction
fSelectPin.select();
doTransfer(fTransfer);
fSelectPin.deselect();
// Done
return;
}
Since I could have written overloaded function for inverted and non-inverted select pins without using templates, i'm basically back to square one.
Is there a way arround this so that I in essence can use the base template as a parameter in a single function definition? Something along the lines of:
// Accept any condition for the SelectPin template
inline void doTransaction(const SelectPin<SelectPinType::*> &fSelectPin, Spi::Transfer &fTransfer)
{
// Perform the SPI transaction
fSelectPin.select();
doTransfer(fTransfer);
fSelectPin.deselect();
// Done
return;
}
Note Yes, I realize that 2 overloaded functions would do the job right away but I feel that this may have generic implications besides my crapy select pins.
Upvotes: 0
Views: 97
Reputation: 217398
The syntax you want is:
template <SelectPinType E>
void doTransaction(const SelectPin<E> &fSelectPin, Spi::Transfer &fTransfer)
{
// Perform the SPI transaction
fSelectPin.select();
doTransfer(fTransfer);
fSelectPin.deselect();
}
or even shorter, but with less constraint:
template <typename T>
void doTransaction(const T& fSelectPin, Spi::Transfer &fTransfer)
{
// Perform the SPI transaction
fSelectPin.select();
doTransfer(fTransfer);
fSelectPin.deselect();
}
Upvotes: 1