Tenonymous
2018-11-03 02:22

Często spotykam się z opinią, że C++ w Embedded to zły wybór, a modern C++ jest niemożliwy do użycia, jeżeli pracujemy blisko sprzętu.
Tymczasem płytka LPC1347 + RGB Led Ring z 12 diodami jak najbardziej daje rade.
Popełniłem prosty program pozwalający zapalać diody od 1 do 12, potem 12 do 1, itd. Z kodu zostały wycięte elementy charakterystyczne dla samego sprzętu(na życzenie, w wiadomości prywatnej mogę udostępnić całość).

class Neopixel
{
    public:
        Neopixel(LPC_SSP_T *pPort, uint32_t n);
        virtual ~Neopixel();
        void Send();
        std::vector<uint32_t> values;

        enum class Mode
        {
            UP,
            DOWN
        };

        Mode mode;

    private:
        LPC_SSP_T *pSSP;
        uint16_t LED_data;

        constexpr static const uint32_t CODE_0{0x7C00};
        constexpr static const uint32_t CODE_1{0x7F80};
        constexpr inline bool GET_BIT(uint32_t k, uint32_t n) const { return (k &   (1 << (n))); }
        constexpr inline bool SET_BIT(uint32_t k, uint32_t n) const { return (k |=  (1 << (n))); }
        constexpr inline bool CLR_BIT(uint32_t k, uint32_t n) const { return (k &= ~(1 << (n))); }
};
Neopixel::Neopixel(LPC_SSP_T *pPort, uint32_t n)
    : values(n), mode(Mode::UP)
{
    values.shrink_to_fit();

    for (auto& val : values) val = 0;
        (...).
}

Funkcja wywołująca:

constexpr static const uint32_t LED_COUNT{12};
constexpr static const uint32_t COLOR_S{6};

std::array<uint32_t, COLOR_S> colors{
        0xFFC000, //Amber
        0x0000FF, //Blue
        0x00FF00, //Lime
        0xFF0000, //Red
        0xFF00FF, //Magenta
        0xFFD700, //Gold
};

std::unique_ptr<Neopixel> LedRing;

uint32_t cnt{0};
uint32_t dnt{11};

extern "C" void SysTick_Handler(void) {
    if (LedRing -> mode == Neopixel::Mode::UP) {
        LedRing -> values[cnt % LED_COUNT] = colors[cnt % 6];
        LedRing -> Send();

        if (LedRing -> values[LED_COUNT -1] != 0) {
            for (auto& val : LedRing -> values) val = 0;
            LedRing -> mode = Neopixel::Mode::DOWN;
        }
        ++cnt;
    }

    if (LedRing -> mode == Neopixel::Mode::DOWN) {
        LedRing -> values[dnt] = colors[dnt % 6];
        LedRing -> Send();

        if (LedRing -> values[0] != 0) {
            for (auto& val : LedRing -> values) val = 0;
            LedRing -> mode = Neopixel::Mode::UP;
            dnt = 12;
        }
        --dnt;
    }

}

int main(void) {

    (...)

    LedRing = std::make_unique<Neopixel>(LPC_SSP0, LED_COUNT);
    (void) SysTick_Config(SystemCoreClock / 6);
    (...)
}
Pijak

constexpr static const uint32_t LED_COUNT{12}; lol constexpr jest obliczane w czasie kompilacji więc static const nic nie zmienia

Pijak

for (auto& val : LedRing -> values) val = 0; to chyba można zrobić tak: std::fill(std::begin(LedRing->values), std::end(LedRing->values), 0);

Tenonymous

@Pijak: słuszna uwaga właściwie do obu. Przy pierwszym sprawdzałem co na to kompilator[nic]. Co do drugiego, nie wiem czy to cokolwiek zmieni, pod jakimkolwiek względem. std::fil nie jest constepxr(ma być w C++20), więc tutaj chyba nie wiele zyskamy.

Natomiast for będzie miało przewagę, jak zamienię klasę na klasę szablonową z parametrem jawnym, vectora na array. Wtedy mam constexpr konstruktor.

vpiotr

@Tenonymous: Po co inline w GET_BIT? Nie czytałem jeszcze, ale możliwe że przydało by Ci się przeczytać Real-Time C++: Efficient Object-Oriented and Template Microcontroller Programming, czyli w skrócie jak robić duże rzeczy na małym kompie.

Tenonymous

@vpiotr: Chip_SSP_SendFrame(pSSP, LED_data) - ta metoda potrzebuje odpowiedniego bitu dla ramki. Łatwiej mi to było zapisać jako funkcję niż na sztywno w kodzie.

Tenonymous

@vpiotr: ojezus. Miałem już pisać, że doskonale wiem, że funkcje inline nie są tym, czym większość myśli, że są, ale zerknąłem wcześniej do tego co podrzuciłeś i aż mi wstyd. :v