~aleteoryx/muditaos

21171bb474701d70252dd5852205581334710e60 — Lukasz Mastalerz 2 years ago 55b4a8e
[BH-1714] Eink refactor and error handling

Cleanup and refactor for eink code.
Changed turning on/off procedure.
Add error handling.
M harmony_changelog.md => harmony_changelog.md +1 -0
@@ 24,6 24,7 @@
### Changed / Improved

* Information about device memory is now sent to MC in floating points numbers
* General improvement in Eink display and error handling

## [2.0.0 2023-06-29]


M module-bsp/board/linux/eink/LinuxEinkDisplay.cpp => module-bsp/board/linux/eink/LinuxEinkDisplay.cpp +20 -8
@@ 78,25 78,31 @@ namespace hal::eink
                                               [[maybe_unused]] const WaveformTemperature behaviour)
    {}

    void LinuxEinkDisplay::dither()
    {}
    EinkStatus LinuxEinkDisplay::dither()
    {
        return EinkStatus::EinkOK;
    }

    void LinuxEinkDisplay::powerOn()
    EinkStatus LinuxEinkDisplay::powerOn()
    {
        EinkPowerOn();
        return EinkStatus::EinkOK;
    }

    void LinuxEinkDisplay::powerOff()
    EinkStatus LinuxEinkDisplay::powerOff()
    {
        EinkPowerOff();
        return EinkStatus::EinkOK;
    }

    void LinuxEinkDisplay::shutdown()
    {}
    EinkStatus LinuxEinkDisplay::shutdown()
    {
        return EinkStatus::EinkOK;
    }

    void LinuxEinkDisplay::wipeOut()
    EinkStatus LinuxEinkDisplay::wipeOut()
    {
        EinkFillScreenWithColor(EinkDisplayColorFilling_e::EinkDisplayColorWhite);
        return translateStatus(EinkFillScreenWithColor(EinkDisplayColorFilling_e::EinkDisplayColorWhite));
    }

    EinkStatus LinuxEinkDisplay::resetAndInit()


@@ 105,6 111,12 @@ namespace hal::eink
        return EinkStatus::EinkOK;
    }

    EinkStatus LinuxEinkDisplay::reinitAndPowerOn()
    {
        resetAndInit();
        return EinkStatus::EinkOK;
    }

    [[nodiscard]] auto LinuxEinkDisplay::getDevice() const noexcept -> std::shared_ptr<devices::Device>
    {
        return {};

M module-bsp/board/linux/eink/LinuxEinkDisplay.hpp => module-bsp/board/linux/eink/LinuxEinkDisplay.hpp +6 -5
@@ 26,12 26,13 @@ namespace hal::eink
                             const EinkRefreshMode refreshMode) override;
        void prepareEarlyRequest(const EinkRefreshMode refreshMode, const WaveformTemperature behaviour) override;

        void dither() override;
        void powerOn() override;
        void powerOff() override;
        void shutdown() override;
        void wipeOut() override;
        EinkStatus dither() override;
        EinkStatus powerOn() override;
        EinkStatus powerOff() override;
        EinkStatus shutdown() override;
        EinkStatus wipeOut() override;
        EinkStatus resetAndInit() override;
        EinkStatus reinitAndPowerOn() override;
        [[nodiscard]] std::shared_ptr<devices::Device> getDevice() const noexcept override;

        FrameSize size;

M module-bsp/board/rt1051/bsp/eink/ED028TC1.cpp => module-bsp/board/rt1051/bsp/eink/ED028TC1.cpp +251 -281
@@ 1,32 1,15 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "dma_config.h"
#include "fsl_dmamux.h"
#include "fsl_edma.h"
#include "ED028TC1.h"
#include "macros.h"
#include "bsp_eink.h"
#include "board.h"
#include "eink_dimensions.hpp"
#include "eink_binarization_luts.h"

#include <stdbool.h>
#include <assert.h>
#include <math.h>
#include <string.h>

#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

#include <log/log.hpp>
#include "board.h"
#include "eink_binarization_luts.h"
#include "macros.h"
#include "eink_dimensions.hpp"

#include <magic_enum.hpp>
#include "drivers/pll/DriverPLL.hpp"
#include "drivers/dmamux/DriverDMAMux.hpp"
#include "drivers/dma/DriverDMA.hpp"
#include "board/BoardDefinitions.hpp"

#define EPD_BOOSTER_START_PERIOD_10MS 0


@@ 60,13 43,6 @@
#define EINK_2BPP_WHITE_PIXEL_MASK 0x03 // This is the mask for the white pixel in 2bpp mode
#define EINK_4BPP_WHITE_PIXEL_MASK 0x0F // This is the mask for the white pixel in 4bpp mode

#define ED028TC1_BUSY_STATE_TIMEOUT_MS 2000 // Time after the display should for sure exit the busy state

using namespace drivers;
using namespace magic_enum;
static std::shared_ptr<drivers::DriverDMA> dma;
static std::shared_ptr<drivers::DriverDMAMux> dmamux;

/* Internal variable definitions */
static bool s_einkIsPoweredOn = false; //  Variable which contains the state of the power of the EPD display



@@ 79,12 55,12 @@ static CACHEABLE_SECTION_SDRAM(uint8_t s_einkServiceRotatedBuf[BOARD_EINK_DISPLA
/**
 * @brief This lut is used for convertion of the 4bp input grayscale pixel to the 1bpp output pixel
 */
static uint8_t s_einkMaskLut_1Bpp[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1};
static std::uint8_t s_einkMaskLut_1Bpp[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1};

/**
 * @brief This lut is used for convertion of the 4bp input grayscale pixel to the 2bpp output pixel
 */
static uint8_t s_einkMaskLut_2Bpp[16] = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3};
static std::uint8_t s_einkMaskLut_2Bpp[16] = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3};

/* External variable definitions */



@@ 133,11 109,11 @@ static uint8_t s_einkMaskLut_2Bpp[16] = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 
 *
 * @return
 */
static uint8_t *s_EinkTransformFrameCoordinateSystem_1Bpp(const uint8_t *dataIn,
                                                          uint16_t windowWidthPx,
                                                          uint16_t windowHeightPx,
                                                          uint8_t *dataOut,
                                                          EinkDisplayColorMode_e invertColors);
static std::uint8_t *s_EinkTransformFrameCoordinateSystem_1Bpp(const std::uint8_t *dataIn,
                                                               std::uint16_t windowWidthPx,
                                                               std::uint16_t windowHeightPx,
                                                               std::uint8_t *dataOut,
                                                               EinkDisplayColorMode_e invertColors);

/**
 *  This function makes rotation of the image from the standard GUI coordinate system to the coord system used by the


@@ 182,11 158,11 @@ static uint8_t *s_EinkTransformFrameCoordinateSystem_1Bpp(const uint8_t *dataIn,
 *
 * @return
 */
static uint8_t *s_EinkTransformFrameCoordinateSystem_2Bpp(const uint8_t *dataIn,
                                                          uint16_t windowWidthPx,
                                                          uint16_t windowHeightPx,
                                                          uint8_t *dataOut,
                                                          EinkDisplayColorMode_e invertColors);
static std::uint8_t *s_EinkTransformFrameCoordinateSystem_2Bpp(const std::uint8_t *dataIn,
                                                               std::uint16_t windowWidthPx,
                                                               std::uint16_t windowHeightPx,
                                                               std::uint8_t *dataOut,
                                                               EinkDisplayColorMode_e invertColors);

/**
 *  This function makes rotation of the image from the standard GUI coordinate system to the coord system used by the


@@ 231,11 207,11 @@ static uint8_t *s_EinkTransformFrameCoordinateSystem_2Bpp(const uint8_t *dataIn,
 *
 * @return
 */
static uint8_t *s_EinkTransformFrameCoordinateSystem_3Bpp(const uint8_t *dataIn,
                                                          uint16_t windowWidthPx,
                                                          uint16_t windowHeightPx,
                                                          uint8_t *dataOut,
                                                          EinkDisplayColorMode_e invertColors);
static std::uint8_t *s_EinkTransformFrameCoordinateSystem_3Bpp(const std::uint8_t *dataIn,
                                                               std::uint16_t windowWidthPx,
                                                               std::uint16_t windowHeightPx,
                                                               std::uint8_t *dataOut,
                                                               EinkDisplayColorMode_e invertColors);

/**
 *  This function makes rotation of the image from the standard GUI coordinate system to the coord system used by the


@@ 280,11 256,11 @@ static uint8_t *s_EinkTransformFrameCoordinateSystem_3Bpp(const uint8_t *dataIn,
 *
 * @return
 */
static uint8_t *s_EinkTransformFrameCoordinateSystem_4Bpp(const uint8_t *dataIn,
                                                          uint16_t windowWidthPx,
                                                          uint16_t windowHeightPx,
                                                          uint8_t *dataOut,
                                                          EinkDisplayColorMode_e invertColors);
static std::uint8_t *s_EinkTransformFrameCoordinateSystem_4Bpp(const std::uint8_t *dataIn,
                                                               std::uint16_t windowWidthPx,
                                                               std::uint16_t windowHeightPx,
                                                               std::uint8_t *dataOut,
                                                               EinkDisplayColorMode_e invertColors);

/**
 *  This function makes rotation of the image from the standard GUI coordinate system to the coord system used by the


@@ 329,11 305,11 @@ static uint8_t *s_EinkTransformFrameCoordinateSystem_4Bpp(const uint8_t *dataIn,
 *
 * @return
 */
static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_1Bpp(const uint8_t *dataIn,
                                                                   uint16_t windowWidthPx,
                                                                   uint16_t windowHeightPx,
                                                                   uint8_t *dataOut,
                                                                   EinkDisplayColorMode_e invertColors);
static std::uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_1Bpp(const std::uint8_t *dataIn,
                                                                        std::uint16_t windowWidthPx,
                                                                        std::uint16_t windowHeightPx,
                                                                        std::uint8_t *dataOut,
                                                                        EinkDisplayColorMode_e invertColors);

/**
 *  This function makes rotation of the image from the standard GUI coordinate system to the coord system used by the


@@ 378,11 354,11 @@ static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_1Bpp(const uint8_t
 *
 * @return
 */
static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_2Bpp(const uint8_t *dataIn,
                                                                   uint16_t windowWidthPx,
                                                                   uint16_t windowHeightPx,
                                                                   uint8_t *dataOut,
                                                                   EinkDisplayColorMode_e invertColors);
static std::uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_2Bpp(const std::uint8_t *dataIn,
                                                                        std::uint16_t windowWidthPx,
                                                                        std::uint16_t windowHeightPx,
                                                                        std::uint8_t *dataOut,
                                                                        EinkDisplayColorMode_e invertColors);

/**
 *  This function makes rotation of the image from the standard GUI coordinate system to the coord system used by the


@@ 427,11 403,11 @@ static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_2Bpp(const uint8_t
 *
 * @return
 */
static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_3Bpp(const uint8_t *dataIn,
                                                                   uint16_t windowWidthPx,
                                                                   uint16_t windowHeightPx,
                                                                   uint8_t *dataOut,
                                                                   EinkDisplayColorMode_e invertColors);
static std::uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_3Bpp(const std::uint8_t *dataIn,
                                                                        std::uint16_t windowWidthPx,
                                                                        std::uint16_t windowHeightPx,
                                                                        std::uint8_t *dataOut,
                                                                        EinkDisplayColorMode_e invertColors);

/**
 *  This function makes rotation of the image from the standard GUI coordinate system to the coord system used by the


@@ 482,11 458,11 @@ static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_3Bpp(const uint8_t
 * It is used when EINK_ROTATE_90_CLOCKWISE is not defined.
 */

static uint8_t *s_EinkTransformFrameCoordinateSystemNoRotation_4Bpp(const uint8_t *dataIn,
                                                                    uint16_t windowWidthPx,
                                                                    uint16_t windowHeightPx,
                                                                    uint8_t *dataOut,
                                                                    EinkDisplayColorMode_e invertColors);
static std::uint8_t *s_EinkTransformFrameCoordinateSystemNoRotation_4Bpp(const std::uint8_t *dataIn,
                                                                         std::uint16_t windowWidthPx,
                                                                         std::uint16_t windowHeightPx,
                                                                         std::uint8_t *dataOut,
                                                                         EinkDisplayColorMode_e invertColors);

/* Function bodies */



@@ 516,85 492,90 @@ void EinkChangeDisplayUpdateTimings(EinkDisplayTimingsMode_e timingsMode)
    }

    if (BSP_EinkWriteData(tmpbuf, 4, SPI_AUTOMATIC_CS) != 0) {
        EinkResetAndInitialize();
        return;
    }
}

uint8_t EinkIsPoweredOn()
bool EinkIsPoweredOn()
{
    return s_einkIsPoweredOn;
}

void EinkPowerOn()
EinkStatus_e EinkPowerOn()
{
    if (s_einkIsPoweredOn) {
        return;
        return EinkOK;
    }

    BSP_EinkLogicPowerOn();

    uint8_t cmd = EinkPowerON; // 0x04
    std::uint8_t cmd = EinkPowerON; // 0x04
    if (BSP_EinkWriteData(&cmd, sizeof(cmd), SPI_AUTOMATIC_CS) != 0) {
        EinkResetAndInitialize();
        return;
        return EinkSPIErr;
    }
    BSP_EinkWaitUntilDisplayBusy(pdMS_TO_TICKS(BSP_EinkBusyTimeout));

    s_einkIsPoweredOn = true;

    if (BSP_EinkWaitUntilDisplayBusy(pdMS_TO_TICKS(BSP_EinkBusyTimeout)) == 0) {
        return EinkSPIErr;
    }

    return EinkOK;
}

void EinkPowerOff()
EinkStatus_e EinkPowerOff()
{
    if (!s_einkIsPoweredOn) {
        return;
        return EinkOK;
    }

    uint8_t cmd = EinkPowerOFF; // 0x02
    std::uint8_t cmd = EinkPowerOFF; // 0x02
    if (BSP_EinkWriteData(&cmd, sizeof(cmd), SPI_AUTOMATIC_CS) != 0) {
        EinkResetAndInitialize();
        return;
        return EinkSPIErr;
    }
    BSP_EinkWaitUntilDisplayBusy(pdMS_TO_TICKS(BSP_EinkBusyTimeout));

    const auto ret = BSP_EinkWaitUntilDisplayBusy(pdMS_TO_TICKS(BSP_EinkBusyTimeout)) == 1 ? EinkOK : EinkSPIErr;

    // continue procedure regardless result
    BSP_EinkLogicPowerOff();

    s_einkIsPoweredOn = false;
    return ret;
}

void EinkPowerDown(void)
EinkStatus_e EinkPowerDown(void)
{
    EinkPowerOff();
    const auto powerOffStatus = EinkPowerOff();
    BSP_EinkDeinit();

    return powerOffStatus;
}

int16_t EinkGetTemperatureInternal()
std::int16_t EinkGetTemperatureInternal()
{
    uint8_t cmd[1];
    int8_t temp[2] = {0, 0};
    std::uint8_t cmd;
    std::int8_t temp[2] = {0, 0};

    cmd[0] = EinkTemperatureSensorCalibration;
    cmd = EinkTemperatureSensorCalibration;

    BSP_EinkWriteCS(BSP_Eink_CS_Clr);

    if (BSP_EinkWriteData(cmd, sizeof(cmd), SPI_MANUAL_CS) != 0) {
    if (BSP_EinkWriteData(&cmd, sizeof(cmd), SPI_MANUAL_CS) != 0) {
        BSP_EinkWriteCS(BSP_Eink_CS_Set);
        EinkResetAndInitialize();
        return -1;
    }

    BSP_EinkWaitUntilDisplayBusy(pdMS_TO_TICKS(BSP_EinkBusyTimeout));
    if (BSP_EinkWaitUntilDisplayBusy(pdMS_TO_TICKS(BSP_EinkBusyTimeout)) == 0) {
        return -1;
    }

    if (BSP_EinkReadData(temp, sizeof(temp), SPI_MANUAL_CS) != 0) {
        BSP_EinkWriteCS(BSP_Eink_CS_Set);
        EinkResetAndInitialize();
        return -1;
    }

    BSP_EinkWriteCS(BSP_Eink_CS_Set);

    // First byte of the temp describes the integer part of the temperature in degrees Celsius
    int8_t temperatureInteger = temp[0];
    const std::int8_t temperatureInteger = temp[0];
    // The MSB bit of the second byte describes the fraction of the temperature. Bit value of 1 means .5 degree Celsius,
    // bit value of 0 means .0 degree Celsius
    // int8_t temperatureFraction =    ((temp[1] & 0x80) >> 7);


@@ 604,14 585,13 @@ int16_t EinkGetTemperatureInternal()

static void s_EinkSetGateOrder()
{
    uint8_t buf[3];
    std::uint8_t buf[3];

    // Set the order of gate refreshing
    buf[0] = EinkGDOrderSetting;
    buf[1] = 0x02; // Magic value required by the ED028TC1 display manufacturer
    buf[2] = 0x00;
    if (BSP_EinkWriteData(buf, 3, SPI_AUTOMATIC_CS) != 0) {
        EinkResetAndInitialize();
    if (BSP_EinkWriteData(buf, sizeof(buf), SPI_AUTOMATIC_CS) != 0) {
        return;
    }
}


@@ 712,6 692,8 @@ static void s_EinkSetInitialConfig()

EinkStatus_e EinkResetAndInitialize()
{
    BSP_EinkLogicPowerOn();

    // Initialize the synchronization resources, SPI and GPIOs for the Eink BSP
    BSP_EinkInit(NULL);
    // Reset the display


@@ 728,13 710,11 @@ EinkStatus_e EinkUpdateWaveform(const EinkWaveformSettings_t *settings)
{
    /// LUTD
    if (BSP_EinkWriteData(settings->LUTDData, settings->LUTDSize, SPI_AUTOMATIC_CS) != 0) {
        EinkResetAndInitialize();
        return EinkSPIErr;
    }

    /// LUTC
    if (BSP_EinkWriteData(settings->LUTCData, settings->LUTCSize + 1, SPI_AUTOMATIC_CS) != 0) {
        EinkResetAndInitialize();
        return EinkSPIErr;
    }



@@ 743,21 723,19 @@ EinkStatus_e EinkUpdateWaveform(const EinkWaveformSettings_t *settings)
    return EinkOK;
}

static EinkStatus_e s_EinkReadFlagsRegister(uint16_t *flags)
static EinkStatus_e s_EinkReadFlagsRegister(std::uint16_t *flags)
{
    uint8_t cmd = EinkFLG;
    std::uint8_t cmd = EinkFLG;

    BSP_EinkWriteCS(BSP_Eink_CS_Clr);

    if (BSP_EinkWriteData(&cmd, sizeof(cmd), SPI_MANUAL_CS) != 0) {
        BSP_EinkWriteCS(BSP_Eink_CS_Set);
        EinkResetAndInitialize();
        return EinkSPIErr;
    }

    if (BSP_EinkReadData(flags, sizeof(uint16_t), SPI_MANUAL_CS) != 0) {
    if (BSP_EinkReadData(flags, sizeof(std::uint16_t), SPI_MANUAL_CS) != 0) {
        BSP_EinkWriteCS(BSP_Eink_CS_Set);
        EinkResetAndInitialize();
        return EinkSPIErr;
    }



@@ 768,7 746,7 @@ static EinkStatus_e s_EinkReadFlagsRegister(uint16_t *flags)

EinkStatus_e EinkWaitTillPipelineBusy()
{
    uint16_t flags = 0;
    std::uint16_t flags = 0;

    s_EinkReadFlagsRegister(&flags);



@@ 782,33 760,30 @@ EinkStatus_e EinkWaitTillPipelineBusy()

EinkStatus_e EinkDitherDisplay()
{
    uint8_t cmdWithArgs[2] = {EinkDPC, EINK_DITHER_4BPP_MODE | EINK_DITHER_START};
    std::uint8_t cmdWithArgs[2] = {EinkDPC, EINK_DITHER_4BPP_MODE | EINK_DITHER_START};

    if (BSP_EinkWriteData(cmdWithArgs, sizeof(cmdWithArgs), SPI_AUTOMATIC_CS) != 0) {
        EinkResetAndInitialize();
        return EinkSPIErr;
    }

    uint16_t flags = 0;
    std::uint16_t flags = 0;

    s_EinkReadFlagsRegister(&flags);

    // Wait for the dither operation finish
    while (flags & EINK_FLAG_DITHER_IN_PROGRESS) {
        vTaskDelay(pdMS_TO_TICKS(1));
        s_EinkReadFlagsRegister(&flags);
    if ((flags & EINK_FLAG_DITHER_IN_PROGRESS)) {
        return EinkSPIErr;
    }

    return EinkOK;
}

EinkStatus_e EinkUpdateFrame(EinkFrame_t frame,
                             const uint8_t *buffer,
                             const std::uint8_t *buffer,
                             EinkBpp_e bpp,
                             EinkDisplayColorMode_e invertColors)
{
    uint8_t buf[10];
    uint8_t pixelsInByte = 8 / bpp;
    std::uint8_t buf[10];
    std::uint8_t pixelsInByte = 8 / bpp;

    s_einkServiceRotatedBuf[0] = EinkDataStartTransmission1;
    s_einkServiceRotatedBuf[1] = bpp - 1; //  0 - 1Bpp, 1 - 2Bpp, 2 - 3Bpp, 3 - 4Bpp


@@ 865,42 840,40 @@ EinkStatus_e EinkUpdateFrame(EinkFrame_t frame,
    }

    buf[0] = EinkDataStartTransmissionWindow; // set display window
    buf[1] = static_cast<uint8_t>(hal::eink::getDisplayXAxis(frame) >>
                                  8); // MSB of the X axis in the EPD display. Value converted
                                      // from the standard GUI coords system to the ED028TC1 one
    buf[2] = static_cast<uint8_t>(
    buf[1] = static_cast<std::uint8_t>(hal::eink::getDisplayXAxis(frame) >>
                                       8); // MSB of the X axis in the EPD display. Value converted
                                           // from the standard GUI coords system to the ED028TC1 one
    buf[2] = static_cast<std::uint8_t>(
        hal::eink::getDisplayXAxis(frame)); // LSB of the X axis in the EPD display. Value converted from
                                            // the standard GUI coords system to the ED028TC1 one
    buf[3] = static_cast<uint8_t>(hal::eink::getDisplayYAxis(frame) >>
                                  8); // MSB of the Y axis in the EPD display. Value converted
                                      // from the standard GUI coords system to the ED028TC1 one
    buf[4] = static_cast<uint8_t>(
    buf[3] = static_cast<std::uint8_t>(hal::eink::getDisplayYAxis(frame) >>
                                       8); // MSB of the Y axis in the EPD display. Value converted
                                           // from the standard GUI coords system to the ED028TC1 one
    buf[4] = static_cast<std::uint8_t>(
        hal::eink::getDisplayYAxis(frame)); // LSB of the Y axis in the EPD display. Value converted from
                                            // the standard GUI coords system to the ED028TC1 one
    buf[5] = static_cast<uint8_t>(hal::eink::getDisplayWindowWidth(frame) >>
                                  8); // MSB of the window height in the EPD display. Value converted
                                      // from the standard GUI coords system to the ED028TC1 one
    buf[6] = static_cast<uint8_t>(
    buf[5] = static_cast<std::uint8_t>(hal::eink::getDisplayWindowWidth(frame) >>
                                       8); // MSB of the window height in the EPD display. Value converted
                                           // from the standard GUI coords system to the ED028TC1 one
    buf[6] = static_cast<std::uint8_t>(
        hal::eink::getDisplayWindowWidth(frame)); // LSB of the window height in the EPD display. Value converted from
                                                  // the standard GUI coords system to the ED028TC1 one
    buf[7] = static_cast<uint8_t>(hal::eink::getDisplayWindowHeight(frame) >>
                                  8); // MSB of the window width in the EPD display. Value converted
                                      // from the standard GUI coords system to the ED028TC1 one
    buf[8] = static_cast<uint8_t>(
    buf[7] = static_cast<std::uint8_t>(hal::eink::getDisplayWindowHeight(frame) >>
                                       8); // MSB of the window width in the EPD display. Value converted
                                           // from the standard GUI coords system to the ED028TC1 one
    buf[8] = static_cast<std::uint8_t>(
        hal::eink::getDisplayWindowHeight(frame)); // LSB of the window width in the EPD display. Value converted from
                                                   // the standard GUI coords system to the ED028TC1 one
    // the standard GUI coords system to the ED028TC1 one

    if (BSP_EinkWriteData(buf, 9, SPI_AUTOMATIC_CS) != 0) {
        EinkResetAndInitialize();
        return EinkSPIErr;
    }

    uint32_t msgSize = 2 + (static_cast<uint32_t>(frame.width) * static_cast<uint32_t>(frame.height) /
                            pixelsInByte); // command (1 byte) + bpp (1 byte) + dataSize(W*H/pixelsInByte bytes)
    std::uint32_t msgSize = 2 + (static_cast<std::uint32_t>(frame.width) * static_cast<std::uint32_t>(frame.height) /
                                 pixelsInByte); // command (1 byte) + bpp (1 byte) + dataSize(W*H/pixelsInByte bytes)
    // Send the part of the image to the display memory

    if (BSP_EinkWriteData(s_einkServiceRotatedBuf, msgSize, SPI_AUTOMATIC_CS) != 0) {
        EinkResetAndInitialize();
        return EinkSPIErr;
    }



@@ 909,7 882,7 @@ EinkStatus_e EinkUpdateFrame(EinkFrame_t frame,

EinkStatus_e EinkFillScreenWithColor(EinkDisplayColorFilling_e colorFill)
{
    uint8_t buf[10];
    std::uint8_t buf[10];

    // Set the window to the entire screen
    buf[0] = EinkDataStartTransmissionWindow; // 0x83


@@ 923,7 896,6 @@ EinkStatus_e EinkFillScreenWithColor(EinkDisplayColorFilling_e colorFill)
    buf[8] = EINK_DISPLAY_RES_Y & 0x00FF;

    if (BSP_EinkWriteData(buf, 9, SPI_AUTOMATIC_CS) != 0) {
        EinkResetAndInitialize();
        return EinkSPIErr;
    }



@@ 933,11 905,10 @@ EinkStatus_e EinkFillScreenWithColor(EinkDisplayColorFilling_e colorFill)
    buf[1] = Eink1Bpp - 1;
    if (BSP_EinkWriteData(buf, 2, SPI_MANUAL_CS) != 0) {
        BSP_EinkWriteCS(BSP_Eink_CS_Set);
        EinkResetAndInitialize();
        return EinkSPIErr;
    }

    uint8_t background = colorFill;
    std::uint8_t background = colorFill;

    std::unique_ptr<char[]> bg;
    try {


@@ 952,7 923,6 @@ EinkStatus_e EinkFillScreenWithColor(EinkDisplayColorFilling_e colorFill)

    if (BSP_EinkWriteData(bg.get(), BOARD_EINK_DISPLAY_RES_Y * BOARD_EINK_DISPLAY_RES_X / 8, SPI_MANUAL_CS) != 0) {
        BSP_EinkWriteCS(BSP_Eink_CS_Set);
        EinkResetAndInitialize();
        return EinkSPIErr;
    }



@@ 970,71 940,71 @@ EinkStatus_e EinkRefreshImage(EinkFrame_t frame, EinkDisplayTimingsMode_e refres

    s_EinkSetGateOrder();

    uint8_t buf[10];
    std::uint8_t buf[10];

    buf[0] = EinkDisplayRefresh;
    buf[1] = UPD_CPY_TO_PRE;

    buf[2] = static_cast<uint8_t>(hal::eink::getDisplayXAxis(frame) >>
                                  8); // MSB of the X axis in the EPD display. Value converted
                                      // from the standard GUI coords system to the ED028TC1 one
    buf[3] = static_cast<uint8_t>(
    buf[2] = static_cast<std::uint8_t>(hal::eink::getDisplayXAxis(frame) >>
                                       8); // MSB of the X axis in the EPD display. Value converted
                                           // from the standard GUI coords system to the ED028TC1 one
    buf[3] = static_cast<std::uint8_t>(
        hal::eink::getDisplayXAxis(frame)); // LSB of the X axis in the EPD display. Value converted from
                                            // the standard GUI coords system to the ED028TC1 one
    buf[4] = static_cast<uint8_t>(hal::eink::getDisplayYAxis(frame) >>
                                  8); // MSB of the Y axis in the EPD display. Value converted
                                      // from the standard GUI coords system to the ED028TC1 one
    buf[5] = static_cast<uint8_t>(
    buf[4] = static_cast<std::uint8_t>(hal::eink::getDisplayYAxis(frame) >>
                                       8); // MSB of the Y axis in the EPD display. Value converted
                                           // from the standard GUI coords system to the ED028TC1 one
    buf[5] = static_cast<std::uint8_t>(
        hal::eink::getDisplayYAxis(frame)); // LSB of the Y axis in the EPD display. Value converted from
                                            // the standard GUI coords system to the ED028TC1 one
    buf[6] = static_cast<uint8_t>(hal::eink::getDisplayWindowWidth(frame) >>
                                  8); // MSB of the window height in the EPD display. Value converted
                                      // from the standard GUI coords system to the ED028TC1 one
    buf[7] = static_cast<uint8_t>(
    buf[6] = static_cast<std::uint8_t>(hal::eink::getDisplayWindowWidth(frame) >>
                                       8); // MSB of the window height in the EPD display. Value converted
                                           // from the standard GUI coords system to the ED028TC1 one
    buf[7] = static_cast<std::uint8_t>(
        hal::eink::getDisplayWindowWidth(frame)); // LSB of the window height in the EPD display. Value converted from
                                                  // the standard GUI coords system to the ED028TC1 one
    buf[8] = static_cast<uint8_t>(hal::eink::getDisplayWindowHeight(frame) >>
                                  8); // MSB of the window width in the EPD display. Value converted
                                      // from the standard GUI coords system to the ED028TC1 one
    buf[9] = static_cast<uint8_t>(
    buf[8] = static_cast<std::uint8_t>(hal::eink::getDisplayWindowHeight(frame) >>
                                       8); // MSB of the window width in the EPD display. Value converted
                                           // from the standard GUI coords system to the ED028TC1 one
    buf[9] = static_cast<std::uint8_t>(
        hal::eink::getDisplayWindowHeight(frame)); // LSB of the window width in the EPD display. Value converted from
                                                   // the standard GUI coords system to the ED028TC1 one
    // the standard GUI coords system to the ED028TC1 one

    if (BSP_EinkWriteData(buf, sizeof(buf), SPI_AUTOMATIC_CS) != 0) {
        EinkResetAndInitialize();
        return EinkSPIErr;
    }

    BSP_EinkWaitUntilDisplayBusy(pdMS_TO_TICKS(BSP_EinkBusyTimeout));
    if (BSP_EinkWaitUntilDisplayBusy(pdMS_TO_TICKS(BSP_EinkBusyTimeout)) == 0) {
        return EinkSPIErr;
    }

    return EinkOK;
}

__attribute__((optimize("O3"))) void EinkARGBToLuminance(const uint8_t *dataIn,
                                                         uint8_t *dataOut,
                                                         uint32_t displayWidth,
                                                         uint32_t displayHeight)
__attribute__((optimize("O3"))) void EinkARGBToLuminance(const std::uint8_t *dataIn,
                                                         std::uint8_t *dataOut,
                                                         std::uint32_t displayWidth,
                                                         std::uint32_t displayHeight)
{
    //    uint32_t i, j;
    uint8_t r, g, b;
    std::uint8_t r, g, b;
    float fi;
    uint32_t *src;
    uint8_t *dst;
    std::uint32_t *src;
    std::uint8_t *dst;

    src = (uint32_t *)dataIn;
    dst = (uint8_t *)dataOut;
    src = (std::uint32_t *)dataIn;
    dst = (std::uint8_t *)dataOut;

    for (uint32_t i = 0; i < (displayWidth * displayHeight);
    for (std::uint32_t i = 0; i < (displayWidth * displayHeight);
         i += 2) // increase by 8 pixels - 32bit word is 8 pixels in 4BPP
    {
        *dst = 0x00000000;
        for (uint8_t j = 0; j < 8; j += 4) {
            r  = (uint8_t)((*(src)) >> 16);
            g  = (uint8_t)((*(src)) >> 8);
            b  = (uint8_t) * (src);
        for (std::uint8_t j = 0; j < 8; j += 4) {
            r  = (std::uint8_t)((*(src)) >> 16);
            g  = (std::uint8_t)((*(src)) >> 8);
            b  = (std::uint8_t) * (src);
            fi = (r + g + b) / 3;

            *dst |= ((uint32_t)(floor(fi / 16))) << (4 - j);
            *dst |= ((std::uint32_t)(floor(fi / 16))) << (4 - j);

            src++;
        }


@@ 1042,36 1012,36 @@ __attribute__((optimize("O3"))) void EinkARGBToLuminance(const uint8_t *dataIn,
    }
}

__attribute__((optimize("O1"))) static uint8_t *s_EinkTransformFrameCoordinateSystem_1Bpp(
    const uint8_t *dataIn,
    uint16_t windowWidthPx,
    uint16_t windowHeightPx,
    uint8_t *dataOut,
__attribute__((optimize("O1"))) static std::uint8_t *s_EinkTransformFrameCoordinateSystem_1Bpp(
    const std::uint8_t *dataIn,
    std::uint16_t windowWidthPx,
    std::uint16_t windowHeightPx,
    std::uint8_t *dataOut,
    EinkDisplayColorMode_e invertColors)
{
    // In 1bpp mode there are 8 pixels in the byte
    const uint8_t pixelsInByte = 8;
    const std::uint8_t pixelsInByte = 8;

    uint8_t pixels    = 0;
    uint8_t *outArray = dataOut;
    std::uint8_t pixels    = 0;
    std::uint8_t *outArray = dataOut;

    for (int32_t inputCol = windowWidthPx - 1; inputCol >= 0; --inputCol) {
        for (int32_t inputRow = windowHeightPx - 1; inputRow >= 7; inputRow -= pixelsInByte) {
    for (std::int32_t inputCol = windowWidthPx - 1; inputCol >= 0; --inputCol) {
        for (std::int32_t inputRow = windowHeightPx - 1; inputRow >= 7; inputRow -= pixelsInByte) {
            // HACK: Did not create the loop for accessing pixels and merging them in the single byte for better
            // performance.
            //       Wanted to avoid unneeded loop count increasing and jump operations which for large amount of data
            //       take considerable amount of time.
            uint32_t index = inputRow * BOARD_EINK_DISPLAY_RES_X + inputCol;
            std::uint32_t index = inputRow * BOARD_EINK_DISPLAY_RES_X + inputCol;

            // Use the LUT to convert the input pixel from 4bpp to 1bpp
            uint8_t firstPixel   = s_einkMaskLut_1Bpp[dataIn[index - 0 * BOARD_EINK_DISPLAY_RES_X]];
            uint8_t secondPixel  = s_einkMaskLut_1Bpp[dataIn[index - 1 * BOARD_EINK_DISPLAY_RES_X]];
            uint8_t thirdPixel   = s_einkMaskLut_1Bpp[dataIn[index - 2 * BOARD_EINK_DISPLAY_RES_X]];
            uint8_t fourthPixel  = s_einkMaskLut_1Bpp[dataIn[index - 3 * BOARD_EINK_DISPLAY_RES_X]];
            uint8_t fifthPixel   = s_einkMaskLut_1Bpp[dataIn[index - 4 * BOARD_EINK_DISPLAY_RES_X]];
            uint8_t sixthPixel   = s_einkMaskLut_1Bpp[dataIn[index - 5 * BOARD_EINK_DISPLAY_RES_X]];
            uint8_t seventhPixel = s_einkMaskLut_1Bpp[dataIn[index - 6 * BOARD_EINK_DISPLAY_RES_X]];
            uint8_t eightPixel   = s_einkMaskLut_1Bpp[dataIn[index - 7 * BOARD_EINK_DISPLAY_RES_X]];
            std::uint8_t firstPixel   = s_einkMaskLut_1Bpp[dataIn[index - 0 * BOARD_EINK_DISPLAY_RES_X]];
            std::uint8_t secondPixel  = s_einkMaskLut_1Bpp[dataIn[index - 1 * BOARD_EINK_DISPLAY_RES_X]];
            std::uint8_t thirdPixel   = s_einkMaskLut_1Bpp[dataIn[index - 2 * BOARD_EINK_DISPLAY_RES_X]];
            std::uint8_t fourthPixel  = s_einkMaskLut_1Bpp[dataIn[index - 3 * BOARD_EINK_DISPLAY_RES_X]];
            std::uint8_t fifthPixel   = s_einkMaskLut_1Bpp[dataIn[index - 4 * BOARD_EINK_DISPLAY_RES_X]];
            std::uint8_t sixthPixel   = s_einkMaskLut_1Bpp[dataIn[index - 5 * BOARD_EINK_DISPLAY_RES_X]];
            std::uint8_t seventhPixel = s_einkMaskLut_1Bpp[dataIn[index - 6 * BOARD_EINK_DISPLAY_RES_X]];
            std::uint8_t eightPixel   = s_einkMaskLut_1Bpp[dataIn[index - 7 * BOARD_EINK_DISPLAY_RES_X]];

            // Put the pixels in order: Most left positioned pixel at the most significant side of byte
            pixels = (firstPixel << 7) | (secondPixel << 6) | (thirdPixel << 5) | (fourthPixel << 4) |


@@ 1089,26 1059,26 @@ __attribute__((optimize("O1"))) static uint8_t *s_EinkTransformFrameCoordinateSy
    return dataOut;
}

__attribute__((optimize("O1"))) static uint8_t *s_EinkTransformFrameCoordinateSystem_2Bpp(
    const uint8_t *dataIn,
    uint16_t windowWidthPx,
    uint16_t windowHeightPx,
    uint8_t *dataOut,
__attribute__((optimize("O1"))) static std::uint8_t *s_EinkTransformFrameCoordinateSystem_2Bpp(
    const std::uint8_t *dataIn,
    std::uint16_t windowWidthPx,
    std::uint16_t windowHeightPx,
    std::uint8_t *dataOut,
    EinkDisplayColorMode_e invertColors)
{
    // In 2bpp mode there are 4 pixels in the byte
    const uint8_t pixelsInByte = 8;
    uint16_t pixels            = 0;
    uint16_t *outArray         = (uint16_t *)dataOut;
    uint8_t temp               = 0;
    const std::uint8_t pixelsInByte = 8;
    std::uint16_t pixels            = 0;
    std::uint16_t *outArray         = (std::uint16_t *)dataOut;
    std::uint8_t temp               = 0;

    for (int32_t inputCol = windowWidthPx - 1; inputCol >= 0; --inputCol) {
        for (int32_t inputRow = windowHeightPx - 1; inputRow >= 7; inputRow -= pixelsInByte) {
    for (std::int32_t inputCol = windowWidthPx - 1; inputCol >= 0; --inputCol) {
        for (std::int32_t inputRow = windowHeightPx - 1; inputRow >= 7; inputRow -= pixelsInByte) {
            // HACK: Did not create the loop for accessing pixels and merging them in the single byte for better
            // performance.
            //       Wanted to avoid unneeded loop count increasing and jump operations which for large amount of data
            //       take considerable amount of time.
            uint32_t index = inputRow * BOARD_EINK_DISPLAY_RES_X + inputCol;
            std::uint32_t index = inputRow * BOARD_EINK_DISPLAY_RES_X + inputCol;

            // Use the LUT to convert the input pixel from 4bpp to 2bpp and put 4 pixels in single byte
            temp = (s_einkMaskLut_2Bpp[dataIn[index - 0 * BOARD_EINK_DISPLAY_RES_X]] << 6);


@@ 1140,47 1110,47 @@ __attribute__((optimize("O1"))) static uint8_t *s_EinkTransformFrameCoordinateSy
    return dataOut;
}

__attribute__((optimize("O1"))) static uint8_t *s_EinkTransformFrameCoordinateSystem_3Bpp(
    const uint8_t *dataIn,
    uint16_t windowWidthPx,
    uint16_t windowHeightPx,
    uint8_t *dataOut,
__attribute__((optimize("O1"))) static std::uint8_t *s_EinkTransformFrameCoordinateSystem_3Bpp(
    const std::uint8_t *dataIn,
    std::uint16_t windowWidthPx,
    std::uint16_t windowHeightPx,
    std::uint8_t *dataOut,
    EinkDisplayColorMode_e invertColors)
{
    // The 4bpp is coded the same way as the 3bpp
    return s_EinkTransformFrameCoordinateSystem_4Bpp(dataIn, windowWidthPx, windowHeightPx, dataOut, invertColors);
}

__attribute__((optimize("O1"))) static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_1Bpp(
    const uint8_t *dataIn,
    uint16_t windowWidthPx,
    uint16_t windowHeightPx,
    uint8_t *dataOut,
__attribute__((optimize("O1"))) static std::uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_1Bpp(
    const std::uint8_t *dataIn,
    std::uint16_t windowWidthPx,
    std::uint16_t windowHeightPx,
    std::uint8_t *dataOut,
    EinkDisplayColorMode_e invertColors)
{
    // In 1bpp mode there are 8 pixels in the byte
    const uint8_t pixelsInByte = 8;
    uint8_t pixels             = 0;
    uint8_t *outArray          = dataOut;
    const std::uint8_t pixelsInByte = 8;
    std::uint8_t pixels             = 0;
    std::uint8_t *outArray          = dataOut;

    for (int32_t inputCol = windowWidthPx - 1; inputCol >= 0; --inputCol) {
        for (int32_t inputRow = windowHeightPx - 1; inputRow >= 7; inputRow -= pixelsInByte) {
    for (std::int32_t inputCol = windowWidthPx - 1; inputCol >= 0; --inputCol) {
        for (std::int32_t inputRow = windowHeightPx - 1; inputRow >= 7; inputRow -= pixelsInByte) {
            // HACK: Did not create the loop for accessing pixels and merging them in the single byte for better
            // performance.
            //       Wanted to avoid unneeded loop count increasing and jump operations which for large amount of data
            //       take considerable amount of time.

            uint32_t index = inputRow * BOARD_EINK_DISPLAY_RES_X + inputCol;
            std::uint32_t index = inputRow * BOARD_EINK_DISPLAY_RES_X + inputCol;

            // Use the LUT to convert the input pixel from 4bpp to 1bpp
            uint8_t firstPixel   = s_einkMaskLut_1Bpp[dataIn[index - 0 * BOARD_EINK_DISPLAY_RES_X]];
            uint8_t secondPixel  = s_einkMaskLut_1Bpp[dataIn[index - 1 * BOARD_EINK_DISPLAY_RES_X]];
            uint8_t thirdPixel   = s_einkMaskLut_1Bpp[dataIn[index - 2 * BOARD_EINK_DISPLAY_RES_X]];
            uint8_t fourthPixel  = s_einkMaskLut_1Bpp[dataIn[index - 3 * BOARD_EINK_DISPLAY_RES_X]];
            uint8_t fifthPixel   = s_einkMaskLut_1Bpp[dataIn[index - 4 * BOARD_EINK_DISPLAY_RES_X]];
            uint8_t sixthPixel   = s_einkMaskLut_1Bpp[dataIn[index - 5 * BOARD_EINK_DISPLAY_RES_X]];
            uint8_t seventhPixel = s_einkMaskLut_1Bpp[dataIn[index - 6 * BOARD_EINK_DISPLAY_RES_X]];
            uint8_t eightPixel   = s_einkMaskLut_1Bpp[dataIn[index - 7 * BOARD_EINK_DISPLAY_RES_X]];
            std::uint8_t firstPixel   = s_einkMaskLut_1Bpp[dataIn[index - 0 * BOARD_EINK_DISPLAY_RES_X]];
            std::uint8_t secondPixel  = s_einkMaskLut_1Bpp[dataIn[index - 1 * BOARD_EINK_DISPLAY_RES_X]];
            std::uint8_t thirdPixel   = s_einkMaskLut_1Bpp[dataIn[index - 2 * BOARD_EINK_DISPLAY_RES_X]];
            std::uint8_t fourthPixel  = s_einkMaskLut_1Bpp[dataIn[index - 3 * BOARD_EINK_DISPLAY_RES_X]];
            std::uint8_t fifthPixel   = s_einkMaskLut_1Bpp[dataIn[index - 4 * BOARD_EINK_DISPLAY_RES_X]];
            std::uint8_t sixthPixel   = s_einkMaskLut_1Bpp[dataIn[index - 5 * BOARD_EINK_DISPLAY_RES_X]];
            std::uint8_t seventhPixel = s_einkMaskLut_1Bpp[dataIn[index - 6 * BOARD_EINK_DISPLAY_RES_X]];
            std::uint8_t eightPixel   = s_einkMaskLut_1Bpp[dataIn[index - 7 * BOARD_EINK_DISPLAY_RES_X]];

            // Put the pixels in order: Most left positioned pixel at the most significant side of byte
            pixels = (firstPixel << 7) | (secondPixel << 6) | (thirdPixel << 5) | (fourthPixel << 4) |


@@ 1198,25 1168,25 @@ __attribute__((optimize("O1"))) static uint8_t *s_EinkTransformAnimationFrameCoo
    return dataOut;
}

__attribute__((optimize("O1"))) static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_2Bpp(
    const uint8_t *dataIn,
    uint16_t windowWidthPx,
    uint16_t windowHeightPx,
    uint8_t *dataOut,
__attribute__((optimize("O1"))) static std::uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_2Bpp(
    const std::uint8_t *dataIn,
    std::uint16_t windowWidthPx,
    std::uint16_t windowHeightPx,
    std::uint8_t *dataOut,
    EinkDisplayColorMode_e invertColors)
{
    // In 2bpp mode there are 4 pixels in the byte
    const uint8_t pixelsInByte = 8;
    uint16_t pixels            = 0;
    uint16_t *outArray         = (uint16_t *)dataOut;
    uint8_t temp               = 0;
    for (int32_t inputCol = windowWidthPx - 1; inputCol >= 0; --inputCol) {
        for (int32_t inputRow = windowHeightPx - 1; inputRow >= 7; inputRow -= pixelsInByte) {
    const std::uint8_t pixelsInByte = 8;
    std::uint16_t pixels            = 0;
    std::uint16_t *outArray         = (std::uint16_t *)dataOut;
    std::uint8_t temp               = 0;
    for (std::int32_t inputCol = windowWidthPx - 1; inputCol >= 0; --inputCol) {
        for (std::int32_t inputRow = windowHeightPx - 1; inputRow >= 7; inputRow -= pixelsInByte) {
            // HACK: Did not create the loop for accessing pixels and merging them in the single byte for better
            // performance.
            //       Wanted to avoid unneeded loop count increasing and jump operations which for large amount of data
            //       take considerable amount of time.
            uint32_t index = inputRow * BOARD_EINK_DISPLAY_RES_X + inputCol;
            std::uint32_t index = inputRow * BOARD_EINK_DISPLAY_RES_X + inputCol;

            // Use the LUT to convert the input pixel from 4bpp to 2bpp and put 4 pixels in single byte
            temp = (s_einkMaskLut_2Bpp[dataIn[index - 0 * BOARD_EINK_DISPLAY_RES_X]] << 6);


@@ 1247,46 1217,46 @@ __attribute__((optimize("O1"))) static uint8_t *s_EinkTransformAnimationFrameCoo
    return dataOut;
}

__attribute__((optimize("O3"))) static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_3Bpp(
    const uint8_t *dataIn,
    uint16_t windowWidthPx,
    uint16_t windowHeightPx,
    uint8_t *dataOut,
__attribute__((optimize("O3"))) static std::uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_3Bpp(
    const std::uint8_t *dataIn,
    std::uint16_t windowWidthPx,
    std::uint16_t windowHeightPx,
    std::uint8_t *dataOut,
    EinkDisplayColorMode_e invertColors)
{
    // The 4bpp is coded the same way as the 3bpp
    return s_EinkTransformFrameCoordinateSystem_4Bpp(dataIn, windowWidthPx, windowHeightPx, dataOut, invertColors);
}

__attribute__((optimize("O1"))) static uint8_t *s_EinkTransformFrameCoordinateSystem_4Bpp(
    const uint8_t *dataIn,
    uint16_t windowWidthPx,
    uint16_t windowHeightPx,
    uint8_t *dataOut,
__attribute__((optimize("O1"))) static std::uint8_t *s_EinkTransformFrameCoordinateSystem_4Bpp(
    const std::uint8_t *dataIn,
    std::uint16_t windowWidthPx,
    std::uint16_t windowHeightPx,
    std::uint8_t *dataOut,
    EinkDisplayColorMode_e invertColors)
{
    // In 3bpp and 4bpp modes there are 2 pixels in the byte. Using 8bpp to process the whole uint32_t at once for
    // faster execution
    const uint8_t pixelsInByte = 8;
    const std::uint8_t pixelsInByte = 8;

    uint32_t pixels    = 0;
    uint32_t *outArray = (uint32_t *)dataOut;
    std::uint32_t pixels    = 0;
    std::uint32_t *outArray = (std::uint32_t *)dataOut;

    for (int32_t inputCol = windowWidthPx - 1; inputCol >= 0; --inputCol) {
        for (int32_t inputRow = windowHeightPx - 1; inputRow >= 7; inputRow -= pixelsInByte) {
    for (std::int32_t inputCol = windowWidthPx - 1; inputCol >= 0; --inputCol) {
        for (std::int32_t inputRow = windowHeightPx - 1; inputRow >= 7; inputRow -= pixelsInByte) {
            // HACK: Did not create the loop for accessing pixels and merging them in the single byte for better
            // performance.
            //       Wanted to avoid unneeded loop count increasing and jump operations which for large amount of data
            //       take considerable amount of time. Using 8 pixels at a time for better performance
            uint32_t index = inputRow * BOARD_EINK_DISPLAY_RES_X + inputCol;
            std::uint32_t index = inputRow * BOARD_EINK_DISPLAY_RES_X + inputCol;

            uint8_t firstPixelPair =
            std::uint8_t firstPixelPair =
                (dataIn[index - 0 * BOARD_EINK_DISPLAY_RES_X] << 4) | dataIn[index - 1 * BOARD_EINK_DISPLAY_RES_X];
            uint8_t secondPixelPair =
            std::uint8_t secondPixelPair =
                (dataIn[index - 2 * BOARD_EINK_DISPLAY_RES_X] << 4) | dataIn[index - 3 * BOARD_EINK_DISPLAY_RES_X];
            uint8_t thirdPixelPair =
            std::uint8_t thirdPixelPair =
                (dataIn[index - 4 * BOARD_EINK_DISPLAY_RES_X] << 4) | dataIn[index - 5 * BOARD_EINK_DISPLAY_RES_X];
            uint8_t fourthPixelPair =
            std::uint8_t fourthPixelPair =
                (dataIn[index - 6 * BOARD_EINK_DISPLAY_RES_X] << 4) | dataIn[index - 7 * BOARD_EINK_DISPLAY_RES_X];

            // Put the pixels in the uint32_t for faster processing


@@ 1305,21 1275,21 @@ __attribute__((optimize("O1"))) static uint8_t *s_EinkTransformFrameCoordinateSy
    return dataOut;
}

__attribute__((optimize("O1"))) static uint8_t *s_EinkTransformFrameCoordinateSystemNoRotation_4Bpp(
    const uint8_t *dataIn,
    uint16_t windowWidthPx,
    uint16_t windowHeightPx,
    uint8_t *dataOut,
__attribute__((optimize("O1"))) static std::uint8_t *s_EinkTransformFrameCoordinateSystemNoRotation_4Bpp(
    const std::uint8_t *dataIn,
    std::uint16_t windowWidthPx,
    std::uint16_t windowHeightPx,
    std::uint8_t *dataOut,
    EinkDisplayColorMode_e invertColors)
{
    // In 3bpp and 4bpp modes there are 2 pixels in the byte. Using 8bpp to process the whole uint32_t at once for
    // faster execution
    const uint8_t pixelsInByte = 8;
    const std::uint8_t pixelsInByte = 8;

    uint32_t pixels    = 0;
    uint32_t *outArray = (uint32_t *)dataOut;
    int32_t inputRow   = 0;
    int32_t inputCol   = 0;
    std::uint32_t pixels    = 0;
    std::uint32_t *outArray = (std::uint32_t *)dataOut;
    std::int32_t inputRow   = 0;
    std::int32_t inputCol   = 0;

    for (inputRow = 0; inputRow < windowHeightPx; ++inputRow) {
        for (inputCol = windowWidthPx - 7; inputCol >= 0; inputCol -= pixelsInByte) {


@@ 1327,13 1297,13 @@ __attribute__((optimize("O1"))) static uint8_t *s_EinkTransformFrameCoordinateSy
            // performance.
            //       Wanted to avoid unneeded loop count increasing and jump operations which for large amount of data
            //       take considerable amount of time. Using 8 pixels at a time for better performance
            uint32_t index = inputRow * BOARD_EINK_DISPLAY_RES_X + inputCol;
            std::uint32_t index = inputRow * BOARD_EINK_DISPLAY_RES_X + inputCol;

            // Get 4x 2 adjacent pixels to process them as uint32_t for better execution timings
            uint8_t firstPixelPair  = (dataIn[index]) | (dataIn[index + 1] << 4);
            uint8_t secondPixelPair = (dataIn[index + 2]) | (dataIn[index + 3] << 4);
            uint8_t thirdPixelPair  = (dataIn[index + 4]) | (dataIn[index + 5] << 4);
            uint8_t fourthPixelPair = (dataIn[index + 6]) | (dataIn[index + 7] << 4);
            std::uint8_t firstPixelPair  = (dataIn[index]) | (dataIn[index + 1] << 4);
            std::uint8_t secondPixelPair = (dataIn[index + 2]) | (dataIn[index + 3] << 4);
            std::uint8_t thirdPixelPair  = (dataIn[index + 4]) | (dataIn[index + 5] << 4);
            std::uint8_t fourthPixelPair = (dataIn[index + 6]) | (dataIn[index + 7] << 4);

            // Put the pixels in the uint32_t for faster processing
            pixels = (firstPixelPair << 24) | (secondPixelPair << 16) | (thirdPixelPair << 8) | (fourthPixelPair);

M module-bsp/board/rt1051/bsp/eink/ED028TC1.h => module-bsp/board/rt1051/bsp/eink/ED028TC1.h +9 -9
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/**


@@ 14,10 14,6 @@
#ifndef __ED028TC1_H
#define __ED028TC1_H

/* Includes ------------------------------------------------------------------*/
#include <stdio.h>
#include <stdbool.h>
#include <functional>
#include "FreeRTOS.h"
#include "semphr.h"



@@ 328,22 324,22 @@ extern "C"
     * This function returns the state of the EPD siplay powe
     * @return 1 if is currently powered on, 0 otherwise
     */
    uint8_t EinkIsPoweredOn();
    bool EinkIsPoweredOn();

    /**
     * This function powers on the display. Needed for refreshing, measuring the temperature
     */
    void EinkPowerOn();
    EinkStatus_e EinkPowerOn();

    /**
     * This functions powers off the display
     */
    void EinkPowerOff();
    EinkStatus_e EinkPowerOff();

    /**
     * @brief This function is responsible for turning eink of and releasing all resources.
     */
    void EinkPowerDown(void);
    EinkStatus_e EinkPowerDown(void);

    /**
     * This function measures the ambient temperature using the ED028TC1 display internal temperature sensor.


@@ 433,6 429,10 @@ extern "C"
     */
    void EinkARGBToLuminance(const uint8_t *dataIn, uint8_t *dataOut, uint32_t displayWidth, uint32_t displayHeight);

    /**
     * This function gets resolutions settings from EINK display
     */
    EinkStatus_e EinkGetResolutionSettings();
#if defined(__cplusplus)
}
#endif /* __cplusplus */

M module-bsp/board/rt1051/bsp/eink/EinkDisplay.cpp => module-bsp/board/rt1051/bsp/eink/EinkDisplay.cpp +42 -13
@@ 136,7 136,9 @@ namespace hal::eink

    EinkStatus EinkDisplay::prepareDisplay(const EinkRefreshMode refreshMode, const WaveformTemperature behaviour)
    {
        powerOn();
        if (const auto status = reinitAndPowerOn(); status != EinkStatus::EinkOK) {
            return status;
        }

        const auto temperature =
            behaviour == WaveformTemperature::KEEP_CURRENT ? getLastTemperature() : EinkGetTemperatureInternal();


@@ 144,7 146,9 @@ namespace hal::eink
        if (refreshMode == EinkRefreshMode::REFRESH_DEEP) {
            auto status = setWaveform(EinkWaveforms_e::EinkWaveformGC16, temperature);
            if (status == EinkStatus::EinkOK) {
                dither();
                if (const auto ditherStatus = dither(); ditherStatus != EinkStatus::EinkOK) {
                    return ditherStatus;
                }
            }
            return status;
        }


@@ 173,7 177,9 @@ namespace hal::eink

    EinkStatus EinkDisplay::showImageUpdate(const std::vector<EinkFrame> &updateFrames, const std::uint8_t *frameBuffer)
    {
        powerOn();
        if (const auto status = reinitAndPowerOn(); status != EinkStatus::EinkOK) {
            return status;
        }

        for (const EinkFrame &frame : updateFrames) {
            const std::uint8_t *buffer = frameBuffer + frame.pos_y * frame.size.width;


@@ 233,35 239,43 @@ namespace hal::eink
        return translateStatus(EinkResetAndInitialize());
    }

    void EinkDisplay::dither()
    EinkStatus EinkDisplay::dither()
    {
        EinkDitherDisplay();
        return translateStatus(EinkDitherDisplay());
    }

    void EinkDisplay::powerOn()
    EinkStatus EinkDisplay::powerOn()
    {
        if (driverLPSPI) {
            driverLPSPI->Enable();
        }
        EinkPowerOn();
        return translateStatus(EinkPowerOn());
    }

    void EinkDisplay::powerOff()
    EinkStatus EinkDisplay::powerOff()
    {
        EinkPowerOff();
        const auto status = translateStatus(EinkPowerOff());
        if (driverLPSPI) {
            driverLPSPI->Disable();
        }
        return status;
    }

    void EinkDisplay::shutdown()
    EinkStatus EinkDisplay::shutdown()
    {
        EinkPowerDown();
        const auto status = translateStatus(EinkPowerDown());
        if (driverLPSPI) {
            driverLPSPI->Disable();
        }
        return status;
    }

    void EinkDisplay::wipeOut()
    EinkStatus EinkDisplay::wipeOut()
    {
        EinkFillScreenWithColor(EinkDisplayColorFilling_e::EinkDisplayColorWhite);
        if (const auto status = reinitAndPowerOn(); status != EinkStatus::EinkOK) {
            return status;
        }
        return translateStatus(EinkFillScreenWithColor(EinkDisplayColorFilling_e::EinkDisplayColorWhite));
    }

    EinkBpp_e EinkDisplay::getCurrentBitsPerPixelFormat() const noexcept


@@ 303,6 317,7 @@ namespace hal::eink
    EinkStatus EinkDisplay::setWaveform(const EinkWaveforms_e mode, const std::int32_t temperature)
    {
        if (!isNewWaveformNeeded(mode, temperature)) {
            EinkUpdateWaveform(&currentWaveform);
            return EinkStatus::EinkOK;
        }



@@ 382,4 397,18 @@ namespace hal::eink
        return std::make_unique<EinkDisplay>(size);
    }

    EinkStatus EinkDisplay::reinitAndPowerOn()
    {
        if (EinkIsPoweredOn()) {
            return EinkStatus::EinkOK;
        }
        if (resetAndInit() != EinkStatus::EinkOK) {
            return EinkStatus::EinkError;
        }
        if (powerOn() != EinkStatus::EinkOK) {
            return EinkStatus::EinkError;
        }
        return EinkStatus::EinkOK;
    }

} // namespace hal::eink

M module-bsp/board/rt1051/bsp/eink/EinkDisplay.hpp => module-bsp/board/rt1051/bsp/eink/EinkDisplay.hpp +6 -5
@@ 38,11 38,12 @@ namespace hal::eink
        void prepareEarlyRequest(EinkRefreshMode refreshMode, const WaveformTemperature behaviour) override;

        EinkStatus resetAndInit() override;
        void dither() override;
        void powerOn() override;
        void powerOff() override;
        void shutdown() override;
        void wipeOut() override;
        EinkStatus dither() override;
        EinkStatus powerOn() override;
        EinkStatus powerOff() override;
        EinkStatus shutdown() override;
        EinkStatus wipeOut() override;
        EinkStatus reinitAndPowerOn() override;

        [[nodiscard]] auto getDevice() const noexcept -> std::shared_ptr<devices::Device> override;


M module-bsp/board/rt1051/bsp/eink/bsp_eink.cpp => module-bsp/board/rt1051/bsp/eink/bsp_eink.cpp +69 -59
@@ 2,18 2,17 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "bsp_eink.h"

#include "board.h"
#include "fsl_lpspi.h"
#include "fsl_lpspi_edma.h"
#include "fsl_common.h"

#include "dma_config.h"
#include "bsp/eink/eink_gpio.hpp"

#include "FreeRTOS.h"
#include "semphr.h"
#include "queue.h"

#include "drivers/pll/DriverPLL.hpp"
#include "drivers/dmamux/DriverDMAMux.hpp"
#include "drivers/dma/DriverDMA.hpp"
#include "drivers/gpio/DriverGPIO.hpp"


@@ 39,12 38,12 @@ typedef enum
typedef const struct _lpspi_edma_resource
{
    DMA_Type *txEdmaBase;
    uint32_t txEdmaChannel;
    uint8_t txDmaRequest;
    std::uint32_t txEdmaChannel;
    std::uint8_t txDmaRequest;

    DMA_Type *rxEdmaBase;
    uint32_t rxEdmaChannel;
    uint8_t rxDmaRequest;
    std::uint32_t rxEdmaChannel;
    std::uint8_t rxDmaRequest;

#if (defined(FSL_FEATURE_SOC_DMAMUX_COUNT) && FSL_FEATURE_SOC_DMAMUX_COUNT)
    DMAMUX_Type *txDmamuxBase;


@@ 55,8 54,8 @@ typedef const struct _lpspi_edma_resource
typedef const struct _lpspi_resource
{
    LPSPI_Type *base;
    uint32_t instance;
    uint32_t (*GetFreq)(void);
    std::uint32_t instance;
    std::uint32_t (*GetFreq)(void);
} lpspi_resource_t;

typedef struct _bsp_eink_driver


@@ 66,8 65,8 @@ typedef struct _bsp_eink_driver
    lpspi_master_edma_handle_t *handle;
    edma_handle_t *edmaRxRegToRxDataHandle;
    edma_handle_t *edmaTxDataToTxRegHandle;
    uint32_t baudRate_Bps;
    uint8_t flags; /*!< Control and state flags. */
    std::uint32_t baudRate_Bps;
    std::uint8_t flags; /*!< Control and state flags. */

    bsp_eink_BusyEvent event;
    /* Current transfer chip select configuration( automatic of manual ) */


@@ 77,16 76,18 @@ typedef struct _bsp_eink_driver

} bsp_eink_driver_t;

using namespace drivers;
static lpspi_master_config_t s_eink_lpspi_master_config;
static std::shared_ptr<drivers::DriverGPIO> gpio;
static std::shared_ptr<drivers::DriverPLL> pll;
static std::shared_ptr<drivers::DriverDMA> dma;
static std::shared_ptr<drivers::DriverDMAMux> dmamux;
static std::unique_ptr<drivers::DriverDMAHandle> rxDMAHandle;
static std::unique_ptr<drivers::DriverDMAHandle> txDMAHandle;

static uint32_t BSP_EINK_LPSPI_GetFreq(void)
namespace
{
    using namespace drivers;
    static lpspi_master_config_t s_eink_lpspi_master_config;
    static std::shared_ptr<drivers::DriverGPIO> gpio;
    static std::shared_ptr<drivers::DriverDMA> dma;
    static std::shared_ptr<drivers::DriverDMAMux> dmamux;
    static std::unique_ptr<drivers::DriverDMAHandle> rxDMAHandle;
    static std::unique_ptr<drivers::DriverDMAHandle> txDMAHandle;
} // namespace

static std::uint32_t BSP_EINK_LPSPI_GetFreq(void)
{
    return GetPerphSourceClock(PerphClock_LPSPI);
}


@@ 120,6 121,8 @@ static bsp_eink_driver_t BSP_EINK_LPSPI_EdmaDriverState = {
    EventWaitRegistered,
};

static bool bsp_eink_IsInitialised = false;

static SemaphoreHandle_t bsp_eink_TransferComplete;

static SemaphoreHandle_t bsp_eink_busySemaphore; //  This semaphore suspends the task until the EPD display is busy


@@ 146,6 149,9 @@ static void s_LPSPI_MasterEdmaCallback(LPSPI_Type *base,

status_t BSP_EinkInit(bsp_eink_BusyEvent event)
{
    if (bsp_eink_IsInitialised) {
        return kStatus_Success;
    }
    bsp_eink_driver_t *lpspi = &BSP_EINK_LPSPI_EdmaDriverState;
    // lpspi_edma_resource_t *dmaResource = lpspi->dmaResource;



@@ 169,7 175,7 @@ status_t BSP_EinkInit(bsp_eink_BusyEvent event)
    }

    // Create new queue
    bsp_eink_TransferComplete = xQueueCreate(1, sizeof(uint32_t));
    bsp_eink_TransferComplete = xQueueCreate(1, sizeof(std::uint32_t));
    if (bsp_eink_TransferComplete == NULL) {
        vSemaphoreDelete(bsp_eink_busySemaphore);
        bsp_eink_busySemaphore = NULL;


@@ 206,11 212,11 @@ status_t BSP_EinkInit(bsp_eink_BusyEvent event)
    dmamux = DriverDMAMux::Create(static_cast<DMAMuxInstances>(BoardDefinitions::EINK_DMAMUX), DriverDMAMuxParams{});
    dma    = DriverDMA::Create(static_cast<DMAInstances>(BoardDefinitions::EINK_DMA), DriverDMAParams{});

    txDMAHandle = dma->CreateHandle(static_cast<uint32_t>(BoardDefinitions::EINK_TX_DMA_CHANNEL));
    rxDMAHandle = dma->CreateHandle(static_cast<uint32_t>(BoardDefinitions::EINK_RX_DMA_CHANNEL));
    dmamux->Enable(static_cast<uint32_t>(BoardDefinitions::EINK_TX_DMA_CHANNEL),
    txDMAHandle = dma->CreateHandle(static_cast<std::uint32_t>(BoardDefinitions::EINK_TX_DMA_CHANNEL));
    rxDMAHandle = dma->CreateHandle(static_cast<std::uint32_t>(BoardDefinitions::EINK_RX_DMA_CHANNEL));
    dmamux->Enable(static_cast<std::uint32_t>(BoardDefinitions::EINK_TX_DMA_CHANNEL),
                   BSP_EINK_LPSPI_DMA_TX_PERI_SEL); // TODO: M.P fix BSP_EINK_LPSPI_DMA_TX_PERI_SEL
    dmamux->Enable(static_cast<uint32_t>(BoardDefinitions::EINK_RX_DMA_CHANNEL),
    dmamux->Enable(static_cast<std::uint32_t>(BoardDefinitions::EINK_RX_DMA_CHANNEL),
                   BSP_EINK_LPSPI_DMA_RX_PERI_SEL); // TODO: M.P fix BSP_EINK_LPSPI_DMA_RX_PERI_SEL

    BSP_EINK_LPSPI_EdmaDriverState.edmaTxDataToTxRegHandle =


@@ 226,11 232,16 @@ status_t BSP_EinkInit(bsp_eink_BusyEvent event)
                                         lpspi->edmaRxRegToRxDataHandle,
                                         lpspi->edmaTxDataToTxRegHandle);

    bsp_eink_IsInitialised = true;

    return kStatus_Success;
}

void BSP_EinkDeinit(void)
{
    if (!bsp_eink_IsInitialised) {
        return;
    }
    LPSPI_Enable(BSP_EINK_LPSPI_BASE, false);

    if (bsp_eink_busySemaphore != NULL) {


@@ 243,17 254,16 @@ void BSP_EinkDeinit(void)
        bsp_eink_TransferComplete = NULL;
    }

    pll.reset();

    dmamux->Disable(static_cast<uint32_t>(BoardDefinitions::EINK_TX_DMA_CHANNEL));
    dmamux->Disable(static_cast<uint32_t>(BoardDefinitions::EINK_RX_DMA_CHANNEL));

    dma.reset();
    dmamux->Disable(static_cast<std::uint32_t>(BoardDefinitions::EINK_TX_DMA_CHANNEL));
    dmamux->Disable(static_cast<std::uint32_t>(BoardDefinitions::EINK_RX_DMA_CHANNEL));
    dmamux.reset();
    dma.reset();

    gpio->DisableInterrupt(1 << static_cast<uint32_t>(BoardDefinitions::EINK_BUSY_PIN));
    gpio->ClearPortInterrupts(1 << static_cast<uint32_t>(BoardDefinitions::EINK_BUSY_PIN));
    gpio->DisableInterrupt(1 << static_cast<std::uint32_t>(BoardDefinitions::EINK_BUSY_PIN));
    gpio->ClearPortInterrupts(1 << static_cast<std::uint32_t>(BoardDefinitions::EINK_BUSY_PIN));
    gpio.reset();

    bsp_eink_IsInitialised = false;
}

void BSP_EinkLogicPowerOn()


@@ 266,9 276,9 @@ void BSP_EinkLogicPowerOff()
    bsp::eink::eink_gpio_power_off();
}

status_t BSP_EinkChangeSpiFrequency(uint32_t frequencyHz)
status_t BSP_EinkChangeSpiFrequency(std::uint32_t frequencyHz)
{
    uint32_t tcrPrescalerValue = 0;
    std::uint32_t tcrPrescalerValue = 0;

    LPSPI_Enable(BSP_EINK_LPSPI_BASE, false);
    LPSPI_MasterSetBaudRate(


@@ 284,10 294,10 @@ status_t BSP_EinkChangeSpiFrequency(uint32_t frequencyHz)
    return 0;
}

status_t BSP_EinkWriteData(void *txBuffer, uint32_t len, eink_spi_cs_config_e cs)
status_t BSP_EinkWriteData(void *txBuffer, std::uint32_t len, eink_spi_cs_config_e cs)
{
    const uint32_t TX_TIMEOUT_MS = 2000;
    status_t tx_status           = 0;
    constexpr std::uint32_t TX_TIMEOUT_MS = 2000;
    status_t tx_status                    = -1;
    status_t status;
    lpspi_transfer_t xfer = {};



@@ 295,9 305,9 @@ status_t BSP_EinkWriteData(void *txBuffer, uint32_t len, eink_spi_cs_config_e cs
        BSP_EinkWriteCS(BSP_Eink_CS_Clr);
    }

    const uint8_t loopCnt = (len / (DMA_MAX_SINGLE_TRANSACTION_PAYLOAD + 1)) + 1;
    uint32_t frameSize = 0;
    uint32_t bytesSent = 0;
    const std::uint8_t loopCnt = (len / (DMA_MAX_SINGLE_TRANSACTION_PAYLOAD + 1)) + 1;
    std::uint32_t frameSize    = 0;
    std::uint32_t bytesSent    = 0;

    // Increase the SPI frequency to the SPI WRITE value
    BSP_EinkChangeSpiFrequency(BSP_EINK_TRANSFER_WRITE_CLOCK);


@@ 308,15 318,15 @@ status_t BSP_EinkWriteData(void *txBuffer, uint32_t len, eink_spi_cs_config_e cs
    // Clean the TX complete queue
    xQueueReset(bsp_eink_TransferComplete);
    // Clear the BUSY Pin IRQ Flag
    gpio->ClearPortInterrupts(1 << static_cast<uint32_t>(BoardDefinitions::EINK_BUSY_PIN));
    gpio->ClearPortInterrupts(1 << static_cast<std::uint32_t>(BoardDefinitions::EINK_BUSY_PIN));
    // Enable the BUSY Pin IRQ
    gpio->EnableInterrupt(1 << static_cast<uint32_t>(BoardDefinitions::EINK_BUSY_PIN));
    gpio->EnableInterrupt(1 << static_cast<std::uint32_t>(BoardDefinitions::EINK_BUSY_PIN));
    // Take the BUSY semaphore without timeout just in case the transmission makes the BUSY pin state change. It
    // enables the driver to block then on the bsp_eink_busySemaphore until the BUSY pin is deasserted
    xSemaphoreTake(bsp_eink_busySemaphore, 0);

    // The MAJOR loop of the DMA can be maximum of value 32767
    for (uint8_t i = 0; i < loopCnt; ++i) {
    for (std::uint8_t i = 0; i < loopCnt; ++i) {
        if (len > DMA_MAX_SINGLE_TRANSACTION_PAYLOAD) {
            frameSize = DMA_MAX_SINGLE_TRANSACTION_PAYLOAD;
        }


@@ 325,7 335,7 @@ status_t BSP_EinkWriteData(void *txBuffer, uint32_t len, eink_spi_cs_config_e cs
        }

        xfer.rxData      = NULL;
        xfer.txData      = (uint8_t *)txBuffer + bytesSent;
        xfer.txData      = (std::uint8_t *)txBuffer + bytesSent;
        xfer.dataSize    = frameSize;
        xfer.configFlags = /*RTE_SPI1_MASTER_PCS_PIN_SEL |*/ kLPSPI_MasterByteSwap | kLPSPI_MasterPcsContinuous;



@@ 365,15 375,15 @@ status_t BSP_EinkWriteData(void *txBuffer, uint32_t len, eink_spi_cs_config_e cs
    return tx_status;
}

status_t BSP_EinkReadData(void *rxBuffer, uint32_t len, eink_spi_cs_config_e cs)
status_t BSP_EinkReadData(void *rxBuffer, std::uint32_t len, eink_spi_cs_config_e cs)
{
    const int RX_TIMEOUT_MS = 2000;
    status_t tx_status      = 0;
    status_t tx_status      = -1;
    status_t status;
    lpspi_transfer_t xfer = {};

    xfer.txData      = NULL;
    xfer.rxData      = (uint8_t *)rxBuffer;
    xfer.rxData      = (std::uint8_t *)rxBuffer;
    xfer.dataSize    = len;
    xfer.configFlags = /*RTE_SPI1_MASTER_PCS_PIN_SEL |*/ kLPSPI_MasterByteSwap | kLPSPI_MasterPcsContinuous;



@@ 390,9 400,9 @@ status_t BSP_EinkReadData(void *rxBuffer, uint32_t len, eink_spi_cs_config_e cs)
    // Clean the TX complete queue
    xQueueReset(bsp_eink_TransferComplete);
    // Clear the BUSY Pin IRQ Flag
    gpio->ClearPortInterrupts(1 << static_cast<uint32_t>(BoardDefinitions::EINK_BUSY_PIN));
    gpio->ClearPortInterrupts(1 << static_cast<std::uint32_t>(BoardDefinitions::EINK_BUSY_PIN));
    // Enable the BUSY Pin IRQ
    gpio->EnableInterrupt(1 << static_cast<uint32_t>(BoardDefinitions::EINK_BUSY_PIN));
    gpio->EnableInterrupt(1 << static_cast<std::uint32_t>(BoardDefinitions::EINK_BUSY_PIN));
    // Take the BUSY semaphore without timeout just in case the transmission makes the BUSY pin state change. It
    // enables the driver to block then on the bsp_eink_busySemaphore until the BUSY pin is deasserted
    xSemaphoreTake(bsp_eink_busySemaphore, 0);


@@ 401,7 411,7 @@ status_t BSP_EinkReadData(void *rxBuffer, uint32_t len, eink_spi_cs_config_e cs)
        BSP_EINK_LPSPI_EdmaDriverState.resource->base, BSP_EINK_LPSPI_EdmaDriverState.handle, &xfer);
    if (status != kStatus_Success) {
        // in case of error just flush transfer complete queue
        uint32_t dummy = 0;
        std::uint32_t dummy = 0;
        xQueueReceive(bsp_eink_TransferComplete, &dummy, 0);
        if (cs == SPI_AUTOMATIC_CS) {
            BSP_EinkWriteCS(BSP_Eink_CS_Set);


@@ 427,9 437,9 @@ status_t BSP_EinkReadData(void *rxBuffer, uint32_t len, eink_spi_cs_config_e cs)
    return tx_status;
}

uint8_t BSP_EinkWaitUntilDisplayBusy(uint32_t timeout)
std::uint8_t BSP_EinkWaitUntilDisplayBusy(std::uint32_t timeout)
{
    uint8_t ret                                  = 0;
    std::uint8_t ret                             = 0;
    BSP_EINK_LPSPI_EdmaDriverState.eventRegister = EventWaitRegistered;
    if (xSemaphoreTake(bsp_eink_busySemaphore, timeout) != pdPASS) {
        ret = 0;


@@ 447,19 457,19 @@ void BSP_EinkResetDisplayController(void)
{
    BSP_EinkWriteCS(BSP_Eink_CS_Set);

    gpio->WritePin(static_cast<uint32_t>(BoardDefinitions::EINK_RESET_PIN), 0);
    gpio->WritePin(static_cast<std::uint32_t>(BoardDefinitions::EINK_RESET_PIN), 0);
    vTaskDelay(10);
    gpio->WritePin(static_cast<uint32_t>(BoardDefinitions::EINK_RESET_PIN), 1);
    gpio->WritePin(static_cast<std::uint32_t>(BoardDefinitions::EINK_RESET_PIN), 1);
    vTaskDelay(10);
}

void BSP_EinkWriteCS(bsp_eink_cs_ctrl_t ctrl)
{
    if (ctrl == BSP_Eink_CS_Clr) {
        gpio->WritePin(static_cast<uint32_t>(BoardDefinitions::EINK_CS_PIN), 0);
        gpio->WritePin(static_cast<std::uint32_t>(BoardDefinitions::EINK_CS_PIN), 0);
    }
    else if (ctrl == BSP_Eink_CS_Set) {
        gpio->WritePin(static_cast<uint32_t>(BoardDefinitions::EINK_CS_PIN), 1);
        gpio->WritePin(static_cast<std::uint32_t>(BoardDefinitions::EINK_CS_PIN), 1);
    }
}



@@ 469,7 479,7 @@ BaseType_t BSP_EinkBusyPinStateChangeHandler(void)

    /* Give semaphore only if something is waiting on it */
    if (BSP_EINK_LPSPI_EdmaDriverState.eventRegister == EventWaitRegistered) {
        gpio->DisableInterrupt(1 << static_cast<uint32_t>(BoardDefinitions::EINK_BUSY_PIN));
        gpio->DisableInterrupt(1 << static_cast<std::uint32_t>(BoardDefinitions::EINK_BUSY_PIN));

        if (xSemaphoreGiveFromISR(bsp_eink_busySemaphore, &xHigherPriorityTaskWoken) != pdPASS) {
            // shouldn't get here!

M module-bsp/board/rt1051/bsp/eink/bsp_eink.h => module-bsp/board/rt1051/bsp/eink/bsp_eink.h +2 -2
@@ 4,7 4,7 @@
#ifndef EINK_BSP_EINK_H_
#define EINK_BSP_EINK_H_

#include <stdint.h>
#include <cstdint>
#include "fsl_common.h"
#include "FreeRTOS.h"



@@ 36,7 36,7 @@ extern "C"
    void BSP_EinkLogicPowerOff();

    void BSP_EinkWriteCS(bsp_eink_cs_ctrl_t ctrl);
    uint8_t BSP_EinkWaitUntilDisplayBusy(uint32_t timeout);
    std::uint8_t BSP_EinkWaitUntilDisplayBusy(std::uint32_t timeout);
    void BSP_EinkResetDisplayController(void);

    status_t BSP_EinkChangeSpiFrequency(uint32_t frequencyHz);

M module-bsp/drivers/pll/DriverPLL.cpp => module-bsp/drivers/pll/DriverPLL.cpp +4 -4
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "DriverPLL.hpp"


@@ 14,7 14,7 @@
namespace drivers
{

    std::weak_ptr<DriverPLL> DriverPLL::singleton[static_cast<uint32_t>(PLLInstances::COUNT)];
    std::weak_ptr<DriverPLL> DriverPLL::singleton[static_cast<std::uint32_t>(PLLInstances::COUNT)];

    std::shared_ptr<DriverPLL> DriverPLL::Create(const drivers::PLLInstances instance,
                                                 const drivers::DriverPLLParams &params)


@@ 22,7 22,7 @@ namespace drivers
        {

            cpp_freertos::CriticalSection::Enter();
            std::shared_ptr<DriverPLL> inst = singleton[static_cast<uint32_t>(instance)].lock();
            std::shared_ptr<DriverPLL> inst = singleton[static_cast<std::uint32_t>(instance)].lock();

            if (!inst) {
#if defined(TARGET_RT1051)


@@ 32,7 32,7 @@ namespace drivers
#error "Unsupported target"
#endif

                singleton[static_cast<uint32_t>(instance)] = inst;
                singleton[static_cast<std::uint32_t>(instance)] = inst;
            }

            cpp_freertos::CriticalSection::Exit();

M module-bsp/drivers/pll/DriverPLL.hpp => module-bsp/drivers/pll/DriverPLL.hpp +3 -5
@@ 1,8 1,7 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#ifndef PUREPHONE_DRIVERPLL_HPP
#define PUREPHONE_DRIVERPLL_HPP
#pragma once

#include <memory>
#include <cstdint>


@@ 44,9 43,8 @@ namespace drivers
        const DriverPLLParams parameters;

      private:
        static std::weak_ptr<DriverPLL> singleton[static_cast<uint32_t>(PLLInstances::COUNT)];
        static std::weak_ptr<DriverPLL> singleton[static_cast<std::uint32_t>(PLLInstances::COUNT)];
    };

} // namespace drivers

#endif // PUREPHONE_DRIVERPLL_HPP

M module-bsp/hal/include/hal/eink/AbstractEinkDisplay.hpp => module-bsp/hal/include/hal/eink/AbstractEinkDisplay.hpp +7 -6
@@ 77,14 77,15 @@ namespace hal::eink
                                     const EinkFrame &refreshFrame,
                                     const std::uint8_t *frameBuffer,
                                     const EinkRefreshMode refreshMode)                                       = 0;
        virtual void prepareEarlyRequest(EinkRefreshMode refreshMode, const WaveformTemperature behaviour) = 0;
        virtual void prepareEarlyRequest(EinkRefreshMode refreshMode, const WaveformTemperature behaviour)    = 0;

        virtual void dither()                                               = 0;
        virtual void powerOn()                                              = 0;
        virtual void powerOff()                                             = 0;
        virtual void shutdown()                                             = 0;
        virtual void wipeOut()                                              = 0;
        virtual EinkStatus dither()                                         = 0;
        virtual EinkStatus powerOn()                                        = 0;
        virtual EinkStatus powerOff()                                       = 0;
        virtual EinkStatus shutdown()                                       = 0;
        virtual EinkStatus wipeOut()                                        = 0;
        virtual EinkStatus resetAndInit()                                   = 0;
        virtual std::shared_ptr<devices::Device> getDevice() const noexcept = 0;
        virtual EinkStatus reinitAndPowerOn()                               = 0;
    };
} // namespace hal::eink

M module-services/service-eink/ServiceEink.cpp => module-services/service-eink/ServiceEink.cpp +13 -13
@@ 56,7 56,9 @@ namespace service::eink
    {
        displayPowerOffTimer = sys::TimerFactory::createSingleShotTimer(
            this, "einkDisplayPowerOff", displayPowerOffTimeout, [this](sys::Timer &) {
                display->powerOff();
                if (display->shutdown() != hal::eink::EinkStatus::EinkOK) {
                    LOG_ERROR("Error during display powerOff.");
                }
                eInkSentinel->ReleaseMinimumFrequency();
            });
        connect(typeid(EinkModeMessage),


@@ 86,7 88,7 @@ namespace service::eink
    sys::ReturnCodes ServiceEink::InitHandler()
    {
        LOG_INFO("Initializing Eink");
        if (const auto status = display->resetAndInit(); status != hal::eink::EinkStatus::EinkOK) {
        if (const auto status = display->reinitAndPowerOn(); status != hal::eink::EinkStatus::EinkOK) {
            LOG_FATAL("Error: Could not initialize Eink display!");
            return sys::ReturnCodes::Failure;
        }


@@ 100,7 102,6 @@ namespace service::eink
        const auto sentinelRegistrationMsg = std::make_shared<sys::SentinelRegistrationMessage>(eInkSentinel);
        bus.sendUnicast(sentinelRegistrationMsg, service::name::system_manager);

        display->powerOn();
        eInkSentinel->HoldMinimumFrequency();

        return sys::ReturnCodes::Success;


@@ 117,7 118,7 @@ namespace service::eink
    sys::ReturnCodes ServiceEink::DeinitHandler()
    {
        // Eink must be turned on before wiping out the display
        display->powerOn();
        display->reinitAndPowerOn();

        if ((exitAction == ExitAction::WipeOut) ||
            ((display->getMode() == hal::eink::EinkDisplayColorMode::EinkDisplayColorModeInverted) &&


@@ 160,20 161,11 @@ namespace service::eink
    void ServiceEink::enterActiveMode()
    {
        setState(State::Running);

        if (const auto status = display->resetAndInit(); status != hal::eink::EinkStatus::EinkOK) {
            LOG_FATAL("Error: Could not initialize Eink display!");
        }
        eInkSentinel->HoldMinimumFrequency();
        display->powerOn();
        display->powerOff();
        eInkSentinel->ReleaseMinimumFrequency();
    }

    void ServiceEink::suspend()
    {
        setState(State::Suspended);
        display->shutdown();
    }

    sys::MessagePointer ServiceEink::handleEinkModeChangedMessage(sys::Message *message)


@@ 351,6 343,9 @@ namespace service::eink
                previousContext->insert(0, 0, ctx);
            }
        }
        if (previousRefreshStatus == RefreshStatus::Failed) {
            updateFrames.front() = {0, 0, BOARD_EINK_DISPLAY_RES_X, BOARD_EINK_DISPLAY_RES_Y};
        }

        // If parts of the screen were changed, update eink
        bool isImageUpdated = false;


@@ 416,11 411,16 @@ namespace service::eink
        const auto message = static_cast<service::eink::RefreshMessage *>(request);

        if (einkDisplayState == EinkDisplayState::NeedRefresh) {
            if (previousRefreshStatus == RefreshStatus::Failed) {
                refreshModeSum        = hal::eink::EinkRefreshMode::REFRESH_DEEP;
                previousRefreshStatus = RefreshStatus::Success;
            }
            const auto status = display->showImageRefresh(refreshFramesSum, refreshModeSum);
            if (status != hal::eink::EinkStatus::EinkOK) {
                previousContext.reset();
                previousRefreshMode = hal::eink::EinkRefreshMode::REFRESH_NONE;
                LOG_ERROR("Error during drawing image on eink: %s", magic_enum::enum_name(status).data());
                previousRefreshStatus = RefreshStatus::Failed;
            }

            einkDisplayState        = EinkDisplayState::Idle;

M module-services/service-eink/ServiceEink.hpp => module-services/service-eink/ServiceEink.hpp +7 -0
@@ 58,6 58,12 @@ namespace service::eink
            Canceled
        };

        enum class RefreshStatus
        {
            Success,
            Failed
        };

        void setState(State state) noexcept;
        bool isInState(State state) const noexcept;
        void restartDisplayPowerOffTimer();


@@ 88,6 94,7 @@ namespace service::eink
        hal::eink::EinkFrame refreshFramesSum;
        hal::eink::EinkRefreshMode refreshModeSum = hal::eink::EinkRefreshMode::REFRESH_NONE;
        bool isRefreshFramesSumValid              = false;
        RefreshStatus previousRefreshStatus       = RefreshStatus::Success;

        sys::CloseReason systemCloseReason = sys::CloseReason::RegularPowerDown;
    };

M module-services/service-gui/ServiceGUI.cpp => module-services/service-gui/ServiceGUI.cpp +2 -1
@@ 202,7 202,8 @@ namespace service::gui
    void ServiceGUI::sendOnDisplay(::gui::Context *context, int contextId, ::gui::RefreshModes refreshMode)
    {
        isDisplaying = true;
        auto msg     = std::make_shared<service::eink::ImageMessage>(contextId, context, refreshMode);

        auto msg = std::make_shared<service::eink::ImageMessage>(contextId, context, refreshMode);
        bus.sendUnicast(std::move(msg), service::name::eink);
        scheduleContextRelease(contextId);
    }

M pure_changelog.md => pure_changelog.md +5 -0
@@ 3,10 3,15 @@
## Unreleased

### Added

* Added VoLTE support in Poland, Germany, Denmark, United Kingdom, Netherlands, Canada and Austria
* Added extended information to crashdump filename
* Added extended information to log filename

### Changed / Improved

* General improvement in Eink display and error handling

### Fixed

* Fixed unsupported character in several quotes