~aleteoryx/muditaos

870250433ad59ca7754bb0bcc95781eae96d9b3c — Paweł Joński 3 years ago f7de6ce
[MOS-298] HALize ServiceEink

Add HAL to ServiceEink
Remove dead code from Linux eink substitute
Fix LUTS.bin refresh problems on simulator
M module-apps/application-meditation/CMakeLists.txt => module-apps/application-meditation/CMakeLists.txt +1 -0
@@ 47,6 47,7 @@ target_link_libraries(application-meditation
        module-vfs
        service-audio
        utils-time
        Microsoft.GSL::GSL
    PUBLIC
        apps-common
        module-gui

M module-apps/application-onboarding/CMakeLists.txt => module-apps/application-onboarding/CMakeLists.txt +1 -0
@@ 51,6 51,7 @@ target_link_libraries(application-onboarding
        i18n
        log
        module-gui
        Microsoft.GSL::GSL
    PUBLIC
        apps-common
)

M module-bsp/board/linux/CMakeLists.txt => module-bsp/board/linux/CMakeLists.txt +1 -0
@@ 11,6 11,7 @@ target_sources(module-bsp
                eeprom/eeprom.cpp
                eink_frontlight/eink_frontlight.cpp
                eink/ED028TC1.c
                eink/LinuxEinkDisplay.cpp
                headset/headset.cpp
                keypad_backlight/keypad_backlight.cpp
                light_sensor/light_sensor.cpp

M module-bsp/board/linux/eink/ED028TC1.c => module-bsp/board/linux/eink/ED028TC1.c +6 -32
@@ 49,10 49,6 @@ int shared_fd = 0;

static uint8_t s_einkIsPoweredOn = false; //  Variable which contains the state of the power of the EPD display

/* Function bodies */
void EinkChangeDisplayUpdateTimings(EinkDisplayTimingsMode_e timingsMode)
{}

uint8_t EinkIsPoweredOn()
{
    return s_einkIsPoweredOn;


@@ 73,11 69,6 @@ void EinkPowerDown(void)
    EinkPowerOff();
}

int16_t EinkGetTemperatureInternal()
{
    return 25;
}

static shared_memory_header *createSHMBuffer(const char *name)
{
    if (shared_header != NULL)


@@ 128,30 119,14 @@ EinkStatus_e EinkResetAndInitialize()
    return EinkOK;
}

EinkStatus_e EinkUpdateWaveform(const EinkWaveformSettings_t *settings)
{
    return EinkOK;
}

EinkStatus_e EinkWaitTillPipelineBusy()
{
    return EinkOK;
}

EinkStatus_e EinkDitherDisplay()
{
    return EinkOK;
}

EinkStatus_e EinkUpdateFrame(
    uint16_t X, uint16_t Y, uint16_t W, uint16_t H, uint8_t *buffer, EinkBpp_e bpp, EinkDisplayColorMode_e invertColors)
EinkStatus_e EinkUpdateFrame(EinkFrame_t frame, uint8_t *buffer)
{
    uint32_t offset_eink   = Y * BOARD_EINK_DISPLAY_RES_X + X;
    uint32_t offset_eink   = frame.pos_y * BOARD_EINK_DISPLAY_RES_X + frame.pos_x;
    uint32_t offset_buffer = 0;
    for (uint32_t h = 0; h < H; ++h) {
        memcpy(shared_buffer + offset_eink, buffer + offset_buffer, W);
    for (uint32_t h = 0; h < frame.height; ++h) {
        memcpy(shared_buffer + offset_eink, buffer + offset_buffer, frame.width);
        offset_eink += BOARD_EINK_DISPLAY_RES_X;
        offset_buffer += W;
        offset_buffer += frame.width;
    }

    shared_header->frameCount++;


@@ 168,8 143,7 @@ EinkStatus_e EinkFillScreenWithColor(EinkDisplayColorFilling_e colorFill)
    return EinkError;
}

EinkStatus_e EinkRefreshImage(
    uint16_t X, uint16_t Y, uint16_t W, uint16_t H, EinkDisplayTimingsMode_e refreshTimingsMode)
EinkStatus_e EinkRefreshImage(EinkFrame_t frame)
{
    return EinkOK;
}

M module-bsp/board/linux/eink/ED028TC1.h => module-bsp/board/linux/eink/ED028TC1.h +9 -314
@@ 22,218 22,6 @@ extern "C"
{
#endif /* __cplusplus */

/* Exported macro ------------------------------------------------------------*/
/**
 * @brief ED028TC1 register definitions
 */
#define EinkPanelSetting            (0x00U)
#define EinkPowerSetting            (0x01U)
#define EinkPowerOFF                (0x02U)
#define EinkPowerOFFSequenceSetting (0x03U)
#define EinkPowerON                 (0x04U)
#define EinkDPC                     (0x05U)
#define EinkDSLP                    (0x06U)
#define EinkBoosterSoftStart        (0x07U)

#define EinkDataStartTransmission1 (0x10U)
#define EinkDisplayRefresh         (0x12U)
#define EinkDTM2                   (0x13U)
#define EinkAWM1                   (0x15U)
#define EinkAWM2                   (0x16U)

#define EinkLUTC        (0x20U)
#define EinkLUTD        (0x21U)
#define EinkLUTR        (0x22U)
#define EinkPowerSaving (0x26U)
#define EinkMISCS       (0x27U)

#define EinkPLLControl (0x30U)

#define EinkTemperatureSensorCalibration (0x40U)
#define EinkTemperatureSensorSelection   (0x41U)

#define EinkVcomAndDataIntervalSetting (0x50U)
#define EinkLPD                        (0x51U)

#define EinkTCONSetting       (0x60U)
#define EinkResolutionSetting (0x61U)
#define EinkDAM               (0x65U)

#define EinkREV          (0x70U)
#define EinkFLG          (0x71U)
#define EinkLUT_COL_FLG  (0x72U)
#define EinkLUT_BUSY_FLG (0x73U)

#define EinkAutoMeasurementVcom         (0x80U)
#define EinkReadVcomValue               (0x81U)
#define EinkVCM_DCSetting               (0x82U)
#define EinkDataStartTransmissionWindow (0x83U)
#define EinkEDS                         (0x84U)
#define EinkXONS                        (0x85U)

#define EinkLEDDS (0x90U)
#define EinkPBC   (0x91U)
#define EinkPBCS  (0x92U)
#define EinkEXTRS (0x93U)
#define EinkNTRS  (0x94U)

#define EinkGDOrderSetting (0xE0U)

/**
 * @brief ED028TC1 register bit definitions
 */
// PanelSetting
#define XON     (1 << 7)
#define RES0    (1 << 6)
#define LUT_SEL (1 << 5)
#define DM      (1 << 4)
#define SHL     (1 << 2)
#define SPIWM   (1 << 1)
#define RST_N   (1 << 0)
#define SFT1PX  (0 << 0)
#define SFT2PX  (1 << 0)
#define SFT3PX  (2 << 0)
#define SFT4PX  (3 << 0)

// PowerSetting
#define VSource_EN (1 << 0)
#define VGate_EN   (1 << 1)
#define VG_LVL17V  (0 << 0)
#define VG_LVL18V  (1 << 0)
#define VG_LVL19V  (2 << 0)
#define VG_LVL20V  (3 << 0)
#define VG_LVL21V  (4 << 0)
#define VSLV_LVL   (0) // this is: 7-bit 00h=2.4V, 7Fh=15.0V
#define VSL_LVL15V (0 << 2)
#define VSL_LVL14V (1 << 2)
#define VSL_LVL13V (2 << 2)
#define VSL_LVL12V (3 << 2)
#define VSH_LVL15V (0 << 0)
#define VSH_LVL14V (1 << 0)
#define VSH_LVL13V (2 << 0)
#define VSH_LVL12V (3 << 0)

// PowerOFFSequenceSetting
#define T_VDS_OFF_1F (0 << 4)
#define T_VDS_OFF_2F (1 << 4)
#define T_VDS_OFF_3F (2 << 4)
#define T_VDS_OFF_4F (3 << 4)

// BoosterSoftStart
#define BTPHx_SSP_10ms (0 << 6) // soft start period of phase A,B
#define BTPHx_SSP_20ms (1 << 6)
#define BTPHx_SSP_30ms (2 << 6)
#define BTPHx_SSP_40ms (3 << 6)
#define BTPHx_DS_1     (0 << 3) // driving strength of phase A,B,C
#define BTPHx_DS_2     (1 << 3)
#define BTPHx_DS_3     (2 << 3)
#define BTPHx_DS_4     (3 << 3)
#define BTPHx_DS_5     (4 << 3)
#define BTPHx_DS_6     (5 << 3)
#define BTPHx_DS_7     (6 << 3)
#define BTPHx_DS_8     (7 << 3)
#define BTPHx_OT_027   (0 << 0) // minimum OFF time setting of phase A,B,C
#define BTPHx_OT_034   (1 << 0)
#define BTPHx_OT_040   (2 << 0)
#define BTPHx_OT_054   (3 << 0)
#define BTPHx_OT_080   (4 << 0)
#define BTPHx_OT_154   (5 << 0)
#define BTPHx_OT_334   (6 << 0)
#define BTPHx_OT_658   (7 << 0)

#define EINK_TEMPERATURE_SENSOR_USE_INTERNAL (0 << 6)
#define EINK_TEMPERATURE_SENSOR_USE_EXTERNAL (2 << 6)

// DataStartTransmission1
#define Cur_BPP1 (0 << 0)
#define Cur_BPP2 (1 << 0)
#define Cur_BPP3 (2 << 0)
#define Cur_BPP4 (3 << 0)

// DisplayRefresh
#define AC_DCVCOM      (1 << 7)
#define WFMode0        (0 << 4)
#define WFMode1        (1 << 4)
#define WFMode2        (2 << 4)
#define WFMode3        (3 << 4)
#define WFMode4        (4 << 4)
#define UPD_CPY_TO_PRE (1 << 3)
#define DN_EN          (1 << 2)
#define Regal_EN_DIS   (0 << 0)
#define Regal_EN_K     (1 << 0)
#define Regal_EN_W     (2 << 0)
#define Regal_EN_KW    (3 << 0)

// PLLControl
#define OSC_RATE_SEL2_5 (0 << 1)
#define OSC_RATE_SEL5   (1 << 1)
#define OSC_RATE_SEL8   (2 << 1)
#define OSC_RATE_SEL10  (3 << 1)
#define OSC_RATE_SEL16  (4 << 1)
#define OSC_RATE_SEL18  (5 << 1)
#define OSC_RATE_SEL19  (6 << 1)
#define OSC_RATE_SEL20  (7 << 1)

// VcomAndDataIntervalSetting
#define VBD_CON        (1 << 3)
#define VBD_OT_G0_G0   (0 << 1)
#define VBD_OT_G0_G15  (2 << 1)
#define VBD_OT_G15_G0  (3 << 1)
#define VBD_OT_G15_G15 (4 << 1)
#define DDX            (1 << 0)
#define CDI            (4) // Vcom data interval: 0h=2hsync -> Fh=32hsync, step=2
#define DCI            (0) // Data to Vcom interval: 0h=1hsync -> Fh=16hsync, step=1

// AutoMeasurementVcom
#define VCM_EN  (1 << 6)
#define AMVT3s  (0 << 4)
#define AMVT5s  (1 << 4)
#define AMVT6s  (2 << 4)
#define AMVT10s (3 << 4)
#define AMVX    (1 << 3)
#define AMVS    (1 << 2)
#define AMV     (1 << 1)
#define AMVE    (1 << 0)

// GDOrderSetting
#define VBD_EN_SEL (1 << 3)
#define GDOS_M0    (0 << 0)
#define GDOS_M1    (1 << 0)
#define GDOS_M2    (2 << 0)
#define GDOS_M3    (3 << 0)
#define GDOS_M4    (4 << 0)
#define GDOS_M5    (5 << 0)
#define GDOS_M6    (6 << 0)
#define GDOS_M7    (7 << 0)
#define VBD_FN     (0) // VBorder frame number seting: 0-VBD disabled, 1-VBD=8, ... 1Fh-VBD=248

// Dither
#define EINK_DITHER_START     0x01
#define EINK_DITHER_4BPP_MODE (0 << 1)
#define EINK_DITHER_2BPP_MODE (1 << 1)

#define EINK_FLAG_BUSY_N 0x0001 ///< This flag informs that the driver is busy. 0 - busy, 1 - idle
#define EINK_FLAG_POWER_OFF_IN_PROGRESS                                                                                \
    0x0002 ///< This flag informs that the Power Off sequence is in progress. 1 - in progress
#define EINK_FLAG_POWER_ON_IN_PROGRESS                                                                                 \
    0x0004 ///< This flag informs that the Power On sequence is in progress. 1 - in progress
#define EINK_FLAG_ENTIRE_FRAME_RECEIVED                                                                                \
    0x0008 ///< This flag informs that the driver received data for entire frame defined in the \ref
           ///< EinkDataStartTransmissionWindow command
#define EINK_FLAG_I2C_BUSY           0x0010 ///< This flag informs that the I2C master periph is busy. Active low
#define EINK_FLAG_I2C_ERROR          0x0020 ///< This flag informs that the I2C master acquired an error
#define EINK_FLAG_PIPELINE_COLLISION 0x0040 ///< This flag informs that two frames sent in the pipelin overlap
#define EINK_FLAG_PIPELINE_BUSY      0x0080 ///< This flag informs that pipeline insertion is in progress
#define EINK_FLAG_REAGL_BUSY         0x0100 ///< This flag informs that the REAGLS function processing is in progress
#define EINK_FLAG_DITHER_IN_PROGRESS 0x0200 ///< This flag informs that the Dither process is in progress
#define EINK_FLAG_DISP_REFRESH_IN_PROGRESS                                                                             \
    0x0400 ///< This flag informs that the Display Refreshing process is in progress
#define EINK_FLAG_AUTO_MEASURE_VCOM_IN_PROGRESS                                                                        \
    0x0800 ///< This flag informs that the auto measurement of the VCOM is in progress
#define EINK_FLAG_EPD_DISCHARGE_IN_PROGRESS 0x1000 ///< This flag informs that the EPD display discharge is in progress
#define EINK_FLAG_BOOST_VOLTAGE_READY       0x4000 ///< This flag informs that the Boost voltage is ready
#define EINK_FLAG_RAM_TEST_FLAG             0x8000 ///< This flag is for internal SRAM memory testing

    /* Exported types ------------------------------------------------------------*/
    /**
     * @enum EinkStatus_e


@@ 251,64 39,19 @@ extern "C"
        EinkWaveformsFileOpenFail, //!< Could not open the file with the waveforms for EPD display
    } EinkStatus_e;

    /**
     * @enum EinkBpp_e
     */
    typedef enum
    {
        Eink1Bpp = 1, //!< Eink1Bpp
        Eink2Bpp,     //!< Eink2Bpp
        Eink3Bpp,     //!< Eink3Bpp
        Eink4Bpp      //!< Eink4Bpp
    } EinkBpp_e;

    typedef enum
    {
        EinkWaveformINIT,  ///< Clears deeply the display
        EinkWaveformA2,    ///< Fastest, direct update, no flashing. Severe ghosting effect
        EinkWaveformDU2,   ///< Fast, direct update, no flashing. Medium ghosting effect
        EinkWaveformGLD16, ///< Slow, little flashing. Light ghosting mode
        EinkWaveformGC16,  ///< Slow, strong flashing. Next to none ghosting
    } EinkWaveforms_e;

    typedef enum
    {
        EinkDisplayTimingsDeepCleanMode,
        EinkDisplayTimingsHighContrastMode,
        EinkDisplayTimingsFastRefreshMode
    } EinkDisplayTimingsMode_e;

    typedef enum
    {
        EinkDisplayColorBlack = 0,
        EinkDisplayColorWhite = 0xFF
    } EinkDisplayColorFilling_e;

    typedef enum
    {
        EinkDisplayColorModeStandard,
        EinkDisplayColorModeInverted
    } EinkDisplayColorMode_e;

    typedef struct
    {
        // type of eink's waveform
        EinkWaveforms_e mode;
        // temperature of surrounding
        int32_t temperature;
        // use counter to keep track if need to change
        uint32_t useCounter;
        // pointer to lookup table for lut c
        uint8_t *LUTCData;
        // sizeo of lutc data
        uint32_t LUTCSize;
        // pointer to lookup table for lut d
        uint8_t *LUTDData;
        // size of lutd data
        uint32_t LUTDSize;
    } EinkWaveformSettings_t;

    /* Exported constants --------------------------------------------------------*/
        uint16_t pos_x;
        uint16_t pos_y;
        uint16_t width;
        uint16_t height;
    } EinkFrame_t;

    /* Exported functions ------------------------------------------------------- */



@@ 334,51 77,20 @@ extern "C"
    void EinkPowerDown(void);

    /**
     * This function measures the ambient temperature using the ED028TC1 display internal temperature sensor.
     * @note The display needs to be powered on
     *
     * @return Ambient temperature in the degrees of Celsius
     */
    int16_t EinkGetTemperatureInternal();

    /**
     * @brief This function resets the eink display and setups the initial configuration
     */
    EinkStatus_e EinkResetAndInitialize();

    /**
     * TODO: Fill The doxy when got info what does it do
     * @return
     */
    EinkStatus_e EinkWaitTillPipelineBusy();

    /**
     * TODO: Fill The doxy when got info what does it do
     * @return
     */
    EinkStatus_e EinkDitherDisplay();

    /**
     * @brief This function sends the part of image from the given buffer to the internal memory of the display. It
     * makes not screen to update.
     * @param X [in] - image start position X in pixels
     * @param Y [in] - image start position Y in pixels
     * @param W [in] - image width in pixels
     * @param H [in] - image height in pixels
     * @param frame [in] - draw buffer on specified part of screen
     * @param buffer [in] -  pointer to image encoded according to \ref bpp set in initialization
     * @param bpp [in] - The format of the \ref buffer (number of the bits per pixel)
     * @param invertColors[in] - true if colors of the image are to be inverted, false otherwise
     *
     * @return  EinkNoMem - Could not allocate the temporary buffer
     *          EinkOK - Part of image send successfully
     */
    EinkStatus_e EinkUpdateFrame(uint16_t X,
                                 uint16_t Y,
                                 uint16_t W,
                                 uint16_t H,
                                 uint8_t *buffer,
                                 EinkBpp_e bpp,
                                 EinkDisplayColorMode_e invertColors);
    EinkStatus_e EinkUpdateFrame(EinkFrame_t frame, uint8_t *buffer);

    /**
     * @brief This function sets the waveform to the \ref EinkWaveformINIT to make the display clearing more deep and


@@ 399,28 111,11 @@ extern "C"
    /**
     * @brief Refresh window on the screen. E-paper display tends to loose contrast over time. To Keep the image sharp
     * refresh is needed.
     * @param X refresh window position X in pixels
     * @param Y refresh window position Y in pixels
     * @param W refresh window width in pixels
     * @param H refresh window height in pixels
     * @param refreshTimingsMode [in] - EinkDisplayTimingsDeepCleanMode - if image is to be cleared precisely
     *                                  EinkDisplayTimingsHighContrastMode - if image is displayed in the high contrast
     * mode EinkDisplayTimingsFastRefreshMode - if image is to be displayed fast
     * @param frame refresh specified part of screen
     *
     * @return EinkOK
     */
    EinkStatus_e EinkRefreshImage(
        uint16_t X, uint16_t Y, uint16_t W, uint16_t H, EinkDisplayTimingsMode_e refreshTimingsMode);

    /**
     * @brief This function sends the proper waveform consisting from the LUTC and LUTD data,
     *          based on the requested waveform (see \ref Mode) and the given temperature (see \ref temperature)
     * @param Mode [in] - type of the waveform
     * @param temperature [in] - current ambient temperature in Celsius degrees
     * @param LUTCData [in] - Data
     * @return
     */
    EinkStatus_e EinkUpdateWaveform(const EinkWaveformSettings_t *settings);
    EinkStatus_e EinkRefreshImage(EinkFrame_t frame);

    /**
     * This function converts the ARGB image to the L4 format

A module-bsp/board/linux/eink/LinuxEinkDisplay.cpp => module-bsp/board/linux/eink/LinuxEinkDisplay.cpp +95 -0
@@ 0,0 1,95 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <board.h>

#include <log/log.hpp>

#include <cstring>
#include <memory>

#include "LinuxEinkDisplay.hpp"
#include "ED028TC1.h"

namespace hal::eink
{
    EinkStatus translateStatus(const EinkStatus_e status_e)
    {
        switch (status_e) {
        case EinkOK:
            return EinkStatus::EinkOK;
        case EinkError:
            return EinkStatus::EinkError;
        case EinkSPIErr:
            return EinkStatus::EinkSPIErr;
        case EinkSPINotInitializedErr:
            return EinkStatus::EinkSPINotInitializedErr;
        case EinkDMAErr:
            return EinkStatus::EinkDMAErr;
        case EinkInitErr:
            return EinkStatus::EinkInitErr;
        case EinkTimeout:
            return EinkStatus::EinkTimeout;
        case EinkNoMem:
            return EinkStatus::EinkNoMem;
        case EinkWaveformsFileOpenFail:
            return EinkStatus::EinkWaveformsFileOpenFail;
        default:
            return EinkStatus::EinkUnknown;
        }
    }

    LinuxEinkDisplay::LinuxEinkDisplay(FrameSize size) : size{size}
    {}

    void LinuxEinkDisplay::setMode(const EinkDisplayColorMode mode) noexcept
    {
        displayColorMode = mode;
    }

    EinkStatus LinuxEinkDisplay::showImage(std::uint8_t *frameBuffer, const EinkRefreshMode refreshMode)
    {
        return translateStatus(EinkUpdateFrame(EinkFrame_t{0, 0, size.width, size.height}, frameBuffer));
    }

    void LinuxEinkDisplay::prepareEarlyRequest([[maybe_unused]] const EinkRefreshMode refreshMode,
                                               [[maybe_unused]] const WaveformTemperature behaviour)
    {}

    void LinuxEinkDisplay::dither()
    {}

    void LinuxEinkDisplay::powerOn()
    {
        EinkPowerOn();
    }

    void LinuxEinkDisplay::powerOff()
    {
        EinkPowerOff();
    }

    void LinuxEinkDisplay::shutdown()
    {}

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

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

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

    std::unique_ptr<AbstractEinkDisplay> AbstractEinkDisplay::Factory::create(FrameSize size)
    {
        return std::make_unique<LinuxEinkDisplay>(size);
    }
} // namespace hal::eink

A module-bsp/board/linux/eink/LinuxEinkDisplay.hpp => module-bsp/board/linux/eink/LinuxEinkDisplay.hpp +32 -0
@@ 0,0 1,32 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <hal/eink/AbstractEinkDisplay.hpp>

namespace hal::eink
{

    class LinuxEinkDisplay : public AbstractEinkDisplay
    {
      public:
        explicit LinuxEinkDisplay(FrameSize size);

      private:
        void setMode(const EinkDisplayColorMode mode) noexcept override;
        EinkStatus showImage(std::uint8_t *frameBuffer, 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 resetAndInit() override;
        [[nodiscard]] std::shared_ptr<devices::Device> getDevice() const noexcept override;

        FrameSize size;
        EinkDisplayColorMode displayColorMode{EinkDisplayColorMode::EinkDisplayColorModeStandard};
    };
} // namespace hal::eink

M module-bsp/board/rt1051/CMakeLists.txt => module-bsp/board/rt1051/CMakeLists.txt +3 -1
@@ 13,7 13,9 @@ target_sources(module-bsp
		bsp/eink_frontlight/eink_frontlight.cpp
		bsp/eink/bsp_eink.cpp
		bsp/eink/ED028TC1.cpp
		bsp/eink/EinkDisplay.cpp
		bsp/eink/eink_binarization_luts.c
		bsp/eink/eink_dimensions.cpp
		bsp/eMMC/fsl_mmc.c
		bsp/eMMC/fsl_sdmmc_common.c
		bsp/eMMC/fsl_sdmmc_event.c


@@ 75,7 77,7 @@ add_subdirectory(common/fsl_drivers)
add_subdirectory(os)
add_subdirectory(${BOARD})

target_link_libraries(module-bsp PUBLIC cmsis fsl)
target_link_libraries(module-bsp PUBLIC cmsis fsl Microsoft.GSL::GSL)

add_library(system-stats-sink-board)
target_sources(system-stats-sink-board

M module-bsp/board/rt1051/bsp/eink/ED028TC1.cpp => module-bsp/board/rt1051/bsp/eink/ED028TC1.cpp +70 -62
@@ 21,6 21,7 @@
#include "board.h"
#include "eink_binarization_luts.h"
#include "macros.h"
#include "eink_dimensions.hpp"

#include <magic_enum.hpp>
#include "drivers/pll/DriverPLL.hpp"


@@ 54,22 55,6 @@
#define EPD_BOOSTER_OFF_TIME_GDR_6_58uS 7
#define EPD_BOOSTER_OFF_TIME_GDR_POS    0

#if defined(EINK_ROTATE_90_CLOCKWISE)
#define EINK_DISPLAY_RES_X         (BOARD_EINK_DISPLAY_RES_Y)
#define EINK_DISPLAY_RES_Y         (BOARD_EINK_DISPLAY_RES_X)
#define EINK_DISPLAY_X_AXIS        (BOARD_EINK_DISPLAY_RES_Y - Y - H)
#define EINK_DISPLAY_Y_AXIS        (BOARD_EINK_DISPLAY_RES_X - X - W)
#define EINK_DISPLAY_WINDOW_WIDTH  (H)
#define EINK_DISPLAY_WINDOW_HEIGHT (W)
#else
#define EINK_DISPLAY_RES_X         (BOARD_EINK_DISPLAY_RES_X)
#define EINK_DISPLAY_RES_Y         (BOARD_EINK_DISPLAY_RES_Y)
#define EINK_DISPLAY_X_AXIS        (BOARD_EINK_DISPLAY_RES_X - X - W)
#define EINK_DISPLAY_Y_AXIS        (BOARD_EINK_DISPLAY_RES_Y - Y - H)
#define EINK_DISPLAY_WINDOW_WIDTH  (W)
#define EINK_DISPLAY_WINDOW_HEIGHT (H)
#endif

#define EINK_BLACK_PIXEL_MASK      0x00 // This is the mask for the black pixel value
#define EINK_1BPP_WHITE_PIXEL_MASK 0x01 // This is the mask for the white pixel in 1bpp mode
#define EINK_2BPP_WHITE_PIXEL_MASK 0x03 // This is the mask for the white pixel in 2bpp mode


@@ 814,8 799,7 @@ EinkStatus_e EinkDitherDisplay()
    return EinkOK;
}

EinkStatus_e EinkUpdateFrame(
    uint16_t X, uint16_t Y, uint16_t W, uint16_t H, uint8_t *buffer, EinkBpp_e bpp, EinkDisplayColorMode_e invertColors)
EinkStatus_e EinkUpdateFrame(EinkFrame_t frame, uint8_t *buffer, EinkBpp_e bpp, EinkDisplayColorMode_e invertColors)
{
    uint8_t buf[10];
    uint8_t pixelsInByte = 8 / bpp;


@@ 826,20 810,24 @@ EinkStatus_e EinkUpdateFrame(
    if ((s_einkConfiguredWaveform == EinkWaveformA2) || (s_einkConfiguredWaveform == EinkWaveformDU2)) {
        switch (bpp) {
        case Eink1Bpp: {
            s_EinkTransformAnimationFrameCoordinateSystem_1Bpp(buffer, W, H, s_einkServiceRotatedBuf + 2, invertColors);
            s_EinkTransformAnimationFrameCoordinateSystem_1Bpp(
                buffer, frame.width, frame.height, s_einkServiceRotatedBuf + 2, invertColors);
        } break;
        case Eink2Bpp: {
            s_EinkTransformAnimationFrameCoordinateSystem_2Bpp(buffer, W, H, s_einkServiceRotatedBuf + 2, invertColors);
            s_EinkTransformAnimationFrameCoordinateSystem_2Bpp(
                buffer, frame.width, frame.height, s_einkServiceRotatedBuf + 2, invertColors);
        } break;
        case Eink3Bpp: {
            s_EinkTransformAnimationFrameCoordinateSystem_3Bpp(buffer, W, H, s_einkServiceRotatedBuf + 2, invertColors);
            s_EinkTransformAnimationFrameCoordinateSystem_3Bpp(
                buffer, frame.width, frame.height, s_einkServiceRotatedBuf + 2, invertColors);
        } break;
        case Eink4Bpp: {
#if defined(EINK_ROTATE_90_CLOCKWISE)
            s_EinkTransformFrameCoordinateSystem_4Bpp(buffer, W, H, s_einkServiceRotatedBuf + 2, invertColors);
            s_EinkTransformFrameCoordinateSystem_4Bpp(
                buffer, frame.width, frame.height, s_einkServiceRotatedBuf + 2, invertColors);
#else
            s_EinkTransformFrameCoordinateSystemNoRotation_4Bpp(
                buffer, W, H, s_einkServiceRotatedBuf + 2, invertColors);
                buffer, frame.width, frame.height, s_einkServiceRotatedBuf + 2, invertColors);
#endif
        } break;
        }


@@ 847,49 835,61 @@ EinkStatus_e EinkUpdateFrame(
    else {
        switch (bpp) {
        case Eink1Bpp: {
            s_EinkTransformFrameCoordinateSystem_1Bpp(buffer, W, H, s_einkServiceRotatedBuf + 2, invertColors);
            s_EinkTransformFrameCoordinateSystem_1Bpp(
                buffer, frame.width, frame.height, s_einkServiceRotatedBuf + 2, invertColors);
        } break;
        case Eink2Bpp: {
            s_EinkTransformFrameCoordinateSystem_2Bpp(buffer, W, H, s_einkServiceRotatedBuf + 2, invertColors);
            s_EinkTransformFrameCoordinateSystem_2Bpp(
                buffer, frame.width, frame.height, s_einkServiceRotatedBuf + 2, invertColors);
        } break;
        case Eink3Bpp: {
            s_EinkTransformFrameCoordinateSystem_3Bpp(buffer, W, H, s_einkServiceRotatedBuf + 2, invertColors);
            s_EinkTransformFrameCoordinateSystem_3Bpp(
                buffer, frame.width, frame.height, s_einkServiceRotatedBuf + 2, invertColors);
        } break;
        case Eink4Bpp: {
#if defined(EINK_ROTATE_90_CLOCKWISE)
            s_EinkTransformFrameCoordinateSystem_4Bpp(buffer, W, H, s_einkServiceRotatedBuf + 2, invertColors);
            s_EinkTransformFrameCoordinateSystem_4Bpp(
                buffer, frame.width, frame.height, s_einkServiceRotatedBuf + 2, invertColors);
#else
            s_EinkTransformFrameCoordinateSystemNoRotation_4Bpp(
                buffer, W, H, s_einkServiceRotatedBuf + 2, invertColors);
                buffer, frame.width, frame.height, s_einkServiceRotatedBuf + 2, invertColors);
#endif
        } break;
        }
    }

    buf[0] = EinkDataStartTransmissionWindow;           // set display window
    buf[1] = (uint8_t)(EINK_DISPLAY_X_AXIS >> 8);       // MSB of the X axis in the EPD display. Value converted
                                                        // from the standard GUI coords system to the ED028TC1 one
    buf[2] = (uint8_t)EINK_DISPLAY_X_AXIS;              // LSB of the X axis in the EPD display. Value converted from
                                                        // the standard GUI coords system to the ED028TC1 one
    buf[3] = (uint8_t)(EINK_DISPLAY_Y_AXIS >> 8);       // MSB of the Y axis in the EPD display. Value converted
                                                        // from the standard GUI coords system to the ED028TC1 one
    buf[4] = (uint8_t)EINK_DISPLAY_Y_AXIS;              // LSB of the Y axis in the EPD display. Value converted from
                                                        // the standard GUI coords system to the ED028TC1 one
    buf[5] = (uint8_t)(EINK_DISPLAY_WINDOW_WIDTH >> 8); // MSB of the window height in the EPD display. Value converted
                                                        // from the standard GUI coords system to the ED028TC1 one
    buf[6] = (uint8_t)EINK_DISPLAY_WINDOW_WIDTH; // LSB of the window height in the EPD display. Value converted from
                                                 // the standard GUI coords system to the ED028TC1 one
    buf[7] = (uint8_t)(EINK_DISPLAY_WINDOW_HEIGHT >> 8); // MSB of the window width in the EPD display. Value converted
                                                         // from the standard GUI coords system to the ED028TC1 one
    buf[8] = (uint8_t)EINK_DISPLAY_WINDOW_HEIGHT; // LSB of the window width in the EPD display. Value converted from
    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>(
        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>(
        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>(
        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>(
        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

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

    uint32_t msgSize = 2 + ((uint32_t)W * (uint32_t)H /
    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)
    // Send the part of the image to the display memory



@@ 952,13 952,13 @@ EinkStatus_e EinkFillScreenWithColor(EinkDisplayColorFilling_e colorFill)

    BSP_EinkWriteCS(BSP_Eink_CS_Set);

    EinkRefreshImage(0, 0, BOARD_EINK_DISPLAY_RES_X, BOARD_EINK_DISPLAY_RES_Y, EinkDisplayTimingsDeepCleanMode);
    EinkRefreshImage(EinkFrame_t{0, 0, BOARD_EINK_DISPLAY_RES_X, BOARD_EINK_DISPLAY_RES_Y},
                     EinkDisplayTimingsDeepCleanMode);

    return EinkOK;
}

EinkStatus_e EinkRefreshImage(
    uint16_t X, uint16_t Y, uint16_t W, uint16_t H, EinkDisplayTimingsMode_e refreshTimingsMode)
EinkStatus_e EinkRefreshImage(EinkFrame_t frame, EinkDisplayTimingsMode_e refreshTimingsMode)
{
    EinkChangeDisplayUpdateTimings(refreshTimingsMode);



@@ 969,22 969,30 @@ EinkStatus_e EinkRefreshImage(
    buf[0] = EinkDisplayRefresh;
    buf[1] = UPD_CPY_TO_PRE;

    buf[2] = (uint8_t)(EINK_DISPLAY_X_AXIS >> 8);       // MSB of the X axis in the EPD display. Value converted
                                                        // from the standard GUI coords system to the ED028TC1 one
    buf[3] = (uint8_t)EINK_DISPLAY_X_AXIS;              // LSB of the X axis in the EPD display. Value converted from
                                                        // the standard GUI coords system to the ED028TC1 one
    buf[4] = (uint8_t)(EINK_DISPLAY_Y_AXIS >> 8);       // MSB of the Y axis in the EPD display. Value converted
                                                        // from the standard GUI coords system to the ED028TC1 one
    buf[5] = (uint8_t)EINK_DISPLAY_Y_AXIS;              // LSB of the Y axis in the EPD display. Value converted from
                                                        // the standard GUI coords system to the ED028TC1 one
    buf[6] = (uint8_t)(EINK_DISPLAY_WINDOW_WIDTH >> 8); // MSB of the window height in the EPD display. Value converted
                                                        // from the standard GUI coords system to the ED028TC1 one
    buf[7] = (uint8_t)EINK_DISPLAY_WINDOW_WIDTH; // LSB of the window height in the EPD display. Value converted from
                                                 // the standard GUI coords system to the ED028TC1 one
    buf[8] = (uint8_t)(EINK_DISPLAY_WINDOW_HEIGHT >> 8); // MSB of the window width in the EPD display. Value converted
                                                         // from the standard GUI coords system to the ED028TC1 one
    buf[9] = (uint8_t)EINK_DISPLAY_WINDOW_HEIGHT; // LSB of the window width in the EPD display. Value converted from
    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>(
        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>(
        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>(
        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>(
        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

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

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

/**


@@ 312,6 312,14 @@ extern "C"
        uint32_t LUTDSize;
    } EinkWaveformSettings_t;

    typedef struct
    {
        uint16_t pos_x;
        uint16_t pos_y;
        uint16_t width;
        uint16_t height;
    } EinkFrame_t;

    /* Exported constants --------------------------------------------------------*/

    /* Exported functions ------------------------------------------------------- */


@@ 365,10 373,7 @@ extern "C"
    /**
     * @brief This function sends the part of image from the given buffer to the internal memory of the display. It
     * makes not screen to update.
     * @param X [in] - image start position X in pixels
     * @param Y [in] - image start position Y in pixels
     * @param W [in] - image width in pixels
     * @param H [in] - image height in pixels
     * @param frame [in] - part of screen on which the image will be written
     * @param buffer [in] -  pointer to image encoded according to \ref bpp set in initialization
     * @param bpp [in] - The format of the \ref buffer (number of the bits per pixel)
     * @param invertColors[in] - true if colors of the image are to be inverted, false otherwise


@@ 376,10 381,7 @@ extern "C"
     * @return  EinkNoMem - Could not allocate the temporary buffer
     *          EinkOK - Part of image send successfully
     */
    EinkStatus_e EinkUpdateFrame(uint16_t X,
                                 uint16_t Y,
                                 uint16_t W,
                                 uint16_t H,
    EinkStatus_e EinkUpdateFrame(EinkFrame_t frame,
                                 uint8_t *buffer,
                                 EinkBpp_e bpp,
                                 EinkDisplayColorMode_e invertColors);


@@ 403,18 405,14 @@ extern "C"
    /**
     * @brief Refresh window on the screen. E-paper display tends to loose contrast over time. To Keep the image sharp
     * refresh is needed.
     * @param X refresh window position X in pixels
     * @param Y refresh window position Y in pixels
     * @param W refresh window width in pixels
     * @param H refresh window height in pixels
     * @param frame - part of screen on which image will be written
     * @param refreshTimingsMode [in] - EinkDisplayTimingsDeepCleanMode - if image is to be cleared precisely
     *                                  EinkDisplayTimingsHighContrastMode - if image is displayed in the high contrast
     * mode EinkDisplayTimingsFastRefreshMode - if image is to be displayed fast
     *
     * @return EinkOK
     */
    EinkStatus_e EinkRefreshImage(
        uint16_t X, uint16_t Y, uint16_t W, uint16_t H, EinkDisplayTimingsMode_e refreshTimingsMode);
    EinkStatus_e EinkRefreshImage(EinkFrame_t frame, EinkDisplayTimingsMode_e refreshTimingsMode);

    /**
     * @brief This function sends the proper waveform consisting from the LUTC and LUTD data,

R module-services/service-eink/EinkDisplay.cpp => module-bsp/board/rt1051/bsp/eink/EinkDisplay.cpp +145 -75
@@ 3,17 3,16 @@

#include "EinkDisplay.hpp"

#if defined(TARGET_RT1051)
#include <board/BoardDefinitions.hpp>
#endif

#include <log/log.hpp>
#include <gui/core/Color.hpp>

#include <gsl/util>
#include <stdexcept>
#include <cstdio>
#include <cstring>

namespace service::eink
namespace hal::eink
{
    namespace
    {


@@ 44,10 43,81 @@ namespace service::eink
            settings.LUTDSize    = 0;
            return settings;
        }

        unsigned int toWaveformTemperatureOffset(std::int32_t temperature) noexcept
        {
            if (temperature >= LUTTemperatureCritical) {
                return LUTTemperatureOffsetCritical;
            }
            if (temperature >= LUTTemperatureSubcritical) {
                return LUTTemperatureOffsetSubcritical;
            }
            if (temperature < LUTTemperatureMinimal) {
                temperature = LUTTemperatureMinimal;
            }
            return temperature / LUTTemperatureOffsetInterval;
        }

        unsigned int toWaveformOffset(unsigned short LUTbank, unsigned int temperatureOffset) noexcept
        {
            constexpr auto singleLUTOffset = (LUTTemperatureOffsetCritical + 1);
            return LUTSTotalSize * (singleLUTOffset * LUTbank + temperatureOffset);
        }

        unsigned int toWaveformOffset(EinkWaveforms_e mode, unsigned int temperatureOffset)
        {
            switch (mode) {
            case EinkWaveformINIT:
                return toWaveformOffset(0, temperatureOffset);
            case EinkWaveformA2:
                return toWaveformOffset(1, temperatureOffset);
            case EinkWaveformDU2:
                return toWaveformOffset(2, temperatureOffset);
            case EinkWaveformGLD16:
                return toWaveformOffset(3, temperatureOffset);
            case EinkWaveformGC16:
                return toWaveformOffset(4, temperatureOffset);
            default:
                throw std::invalid_argument{"Invalid waveform mode."};
            }
        }
    } // namespace

    EinkDisplay::EinkDisplay(::gui::Size screenSize)
        : size{screenSize}, currentWaveform{createDefaultWaveFormSettings(EinkWaveformGC16)},
    EinkDisplayColorMode_e translateDisplayColorMode(const EinkDisplayColorMode mode)
    {
        return mode == EinkDisplayColorMode::EinkDisplayColorModeStandard
                   ? EinkDisplayColorMode_e::EinkDisplayColorModeStandard
                   : EinkDisplayColorMode_e::EinkDisplayColorModeInverted;
    }

    EinkStatus translateStatus(const EinkStatus_e status_e)
    {
        switch (status_e) {
        case EinkOK:
            return EinkStatus::EinkOK;
        case EinkError:
            return EinkStatus::EinkError;
        case EinkSPIErr:
            return EinkStatus::EinkSPIErr;
        case EinkSPINotInitializedErr:
            return EinkStatus::EinkSPINotInitializedErr;
        case EinkDMAErr:
            return EinkStatus::EinkDMAErr;
        case EinkInitErr:
            return EinkStatus::EinkInitErr;
        case EinkTimeout:
            return EinkStatus::EinkTimeout;
        case EinkNoMem:
            return EinkStatus::EinkNoMem;
        case EinkWaveformsFileOpenFail:
            return EinkStatus::EinkWaveformsFileOpenFail;
        default:
            return EinkStatus::EinkUnknown;
        }
    }

    EinkDisplay::EinkDisplay(FrameSize size)
        : size{size}, currentWaveform{createDefaultWaveFormSettings(EinkWaveformGC16)},
          displayMode{EinkDisplayColorMode_e::EinkDisplayColorModeStandard}
    {
#if defined(TARGET_RT1051)


@@ 62,9 132,59 @@ namespace service::eink
        delete[] currentWaveform.LUTDData;
    }

    EinkStatus_e EinkDisplay::resetAndInit()
    EinkStatus EinkDisplay::updateDisplay(std::uint8_t *frameBuffer, [[maybe_unused]] const EinkRefreshMode refreshMode)
    {
        return update(frameBuffer);
    }

    EinkStatus EinkDisplay::refreshDisplay(const EinkRefreshMode refreshMode)
    {
        const auto isDeepRefresh = refreshMode == EinkRefreshMode::REFRESH_DEEP;
        return refresh(isDeepRefresh ? EinkDisplayTimingsDeepCleanMode : EinkDisplayTimingsFastRefreshMode);
    }

    EinkStatus EinkDisplay::prepareDisplay(const EinkRefreshMode refreshMode, const WaveformTemperature behaviour)
    {
        return EinkResetAndInitialize();
        powerOn();

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

        if (refreshMode == EinkRefreshMode::REFRESH_DEEP) {
            auto status = setWaveform(EinkWaveforms_e::EinkWaveformGC16, temperature);
            if (status == EinkStatus::EinkOK) {
                dither();
            }
            return status;
        }
        return setWaveform(EinkWaveforms_e::EinkWaveformDU2, temperature);
    }

    EinkStatus EinkDisplay::showImage(std::uint8_t *frameBuffer, const EinkRefreshMode refreshMode)
    {
        if (const auto status = prepareDisplay(refreshMode, WaveformTemperature::KEEP_CURRENT);
            status != EinkStatus::EinkOK) {
            return status;
        }

        if (const auto status = updateDisplay(frameBuffer, refreshMode); status != EinkStatus::EinkOK) {
            return status;
        }

        if (const auto status = refreshDisplay(refreshMode); status != EinkStatus::EinkOK) {
            return status;
        }
        return EinkStatus::EinkOK;
    }

    void EinkDisplay::prepareEarlyRequest(const EinkRefreshMode refreshMode, const WaveformTemperature behaviour)
    {
        prepareDisplay(refreshMode, behaviour);
    }

    EinkStatus EinkDisplay::resetAndInit()
    {
        return translateStatus(EinkResetAndInitialize());
    }

    void EinkDisplay::dither()


@@ 77,9 197,6 @@ namespace service::eink
        if (driverLPSPI) {
            driverLPSPI->Enable();
        }
        if (eInkSentinel) {
            eInkSentinel->HoldMinimumFrequency();
        }
        EinkPowerOn();
    }



@@ 89,9 206,6 @@ namespace service::eink
        if (driverLPSPI) {
            driverLPSPI->Disable();
        }
        if (eInkSentinel) {
            eInkSentinel->ReleaseMinimumFrequency();
        }
    }

    void EinkDisplay::shutdown()


@@ 104,15 218,12 @@ namespace service::eink
        EinkFillScreenWithColor(EinkDisplayColorFilling_e::EinkDisplayColorWhite);
    }

    EinkStatus_e EinkDisplay::update(std::uint8_t *displayBuffer)
    EinkStatus EinkDisplay::update(std::uint8_t *displayBuffer)
    {
        return EinkUpdateFrame(pointTopLeft.x,
                               pointTopLeft.y,
                               size.width,
                               size.height,
                               displayBuffer,
                               getCurrentBitsPerPixelFormat(),
                               displayMode);
        return translateStatus(EinkUpdateFrame(EinkFrame_t{0, 0, size.width, size.height},
                                               displayBuffer,
                                               getCurrentBitsPerPixelFormat(),
                                               translateDisplayColorMode(displayMode)));
    }

    EinkBpp_e EinkDisplay::getCurrentBitsPerPixelFormat() const noexcept


@@ 123,13 234,13 @@ namespace service::eink
        return Eink4Bpp;
    }

    EinkStatus_e EinkDisplay::refresh(EinkDisplayTimingsMode_e refreshMode)
    EinkStatus EinkDisplay::refresh(const EinkDisplayTimingsMode_e refreshMode)
    {
        currentWaveform.useCounter += 1;
        return EinkRefreshImage(pointTopLeft.x, pointTopLeft.y, size.width, size.height, refreshMode);
        return translateStatus(EinkRefreshImage(EinkFrame_t{0, 0, size.width, size.height}, refreshMode));
    }

    bool EinkDisplay::isNewWaveformNeeded(EinkWaveforms_e newMode, std::int32_t newTemperature) const
    bool EinkDisplay::isNewWaveformNeeded(const EinkWaveforms_e newMode, const std::int32_t newTemperature) const
    {
        constexpr auto lenientTemperatureUseCounter = 50; // arbitrary. not documented
        auto alloweLenientTemperature               = currentWaveform.useCounter < lenientTemperatureUseCounter;


@@ 160,10 271,10 @@ namespace service::eink
        return true;
    }

    EinkStatus_e EinkDisplay::setWaveform(EinkWaveforms_e mode, std::int32_t temperature)
    EinkStatus EinkDisplay::setWaveform(const EinkWaveforms_e mode, const std::int32_t temperature)
    {
        if (!isNewWaveformNeeded(mode, temperature)) {
            return EinkOK;
            return EinkStatus::EinkOK;
        }

        auto currentOffset =


@@ 177,13 288,13 @@ namespace service::eink

        if (offset == currentOffset) {
            // current waveform is still the best fit
            return EinkOK;
            return EinkStatus::EinkOK;
        }

        auto file = std::fopen(LutsFileName, "rb");
        if (file == nullptr) {
            LOG_FATAL("Could not find the LUTS.bin file. Returning");
            return EinkWaveformsFileOpenFail;
            return EinkStatus::EinkWaveformsFileOpenFail;
        }
        auto fileHandlerCleanup = gsl::finally([&file]() { std::fclose(file); });



@@ 201,43 312,7 @@ namespace service::eink
        std::fread(&currentWaveform.LUTCData[1], 1, LUTCSize, file);

        EinkUpdateWaveform(&currentWaveform);
        return EinkOK;
    }

    unsigned int EinkDisplay::toWaveformTemperatureOffset(std::int32_t temperature) noexcept
    {
        if (temperature >= LUTTemperatureCritical)
            return LUTTemperatureOffsetCritical;
        if (temperature >= LUTTemperatureSubcritical)
            return LUTTemperatureOffsetSubcritical;
        if (temperature < LUTTemperatureMinimal) {
            temperature = LUTTemperatureMinimal;
        }
        return temperature / LUTTemperatureOffsetInterval;
    }

    unsigned int EinkDisplay::toWaveformOffset(unsigned short LUTbank, unsigned int temperatureOffset) noexcept
    {
        constexpr auto singleLUTOffset = (LUTTemperatureOffsetCritical + 1);
        return LUTSTotalSize * (singleLUTOffset * LUTbank + temperatureOffset);
    }

    unsigned int EinkDisplay::toWaveformOffset(EinkWaveforms_e mode, unsigned int temperatureOffset)
    {
        switch (mode) {
        case EinkWaveformINIT:
            return toWaveformOffset(0, temperatureOffset);
        case EinkWaveformA2:
            return toWaveformOffset(1, temperatureOffset);
        case EinkWaveformDU2:
            return toWaveformOffset(2, temperatureOffset);
        case EinkWaveformGLD16:
            return toWaveformOffset(3, temperatureOffset);
        case EinkWaveformGC16:
            return toWaveformOffset(4, temperatureOffset);
        default:
            throw std::invalid_argument{"Invalid waveform mode."};
        }
        return EinkStatus::EinkOK;
    }

    void EinkDisplay::resetWaveformSettings()


@@ 253,7 328,7 @@ namespace service::eink
        currentWaveform.LUTCData[0] = EinkLUTC;
    }

    void EinkDisplay::setMode(EinkDisplayColorMode_e mode) noexcept
    void EinkDisplay::setMode(const EinkDisplayColorMode mode) noexcept
    {
        displayMode = mode;
    }


@@ 263,19 338,14 @@ namespace service::eink
        return currentWaveform.temperature;
    }

    ::gui::Size EinkDisplay::getSize() const noexcept
    {
        return size;
    }

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

    void EinkDisplay::setEinkSentinel(std::shared_ptr<EinkSentinel> sentinel)
    std::unique_ptr<AbstractEinkDisplay> AbstractEinkDisplay::Factory::create(FrameSize size)
    {
        eInkSentinel = std::move(sentinel);
        return std::make_unique<EinkDisplay>(size);
    }

} // namespace service::eink
} // namespace hal::eink

A module-bsp/board/rt1051/bsp/eink/EinkDisplay.hpp => module-bsp/board/rt1051/bsp/eink/EinkDisplay.hpp +65 -0
@@ 0,0 1,65 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <cstdint>
#include <memory>

#include <hal/eink/AbstractEinkDisplay.hpp>

#include "drivers/lpspi/DriverLPSPI.hpp"
#include "ED028TC1.h"

namespace hal::eink
{
    /**
     * Specifies the Eink display.
     * Responsible for handling low-level Eink display operations, e.g. switching power modes, updating, refreshing,
     * etc.
     */
    class EinkDisplay : public AbstractEinkDisplay
    {
      public:
        EinkDisplay(FrameSize size);

        ~EinkDisplay() noexcept;

        void setMode(EinkDisplayColorMode mode) noexcept override;
        EinkStatus showImage(std::uint8_t *frameBuffer, const EinkRefreshMode refreshMode) override;
        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;

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

      private:
        bool isNewWaveformNeeded(const EinkWaveforms_e newMode, const int32_t newTemperature) const;
        void resetWaveformSettings();

        EinkBpp_e getCurrentBitsPerPixelFormat() const noexcept;

        EinkStatus setWaveform(const EinkWaveforms_e mode, const std::int32_t temperature);

        std::int32_t getLastTemperature() const noexcept;

        EinkStatus update(std::uint8_t *displayBuffer);
        EinkStatus refresh(const EinkDisplayTimingsMode_e refreshMode);

        EinkStatus updateDisplay(std::uint8_t *frameBuffer, const EinkRefreshMode refreshMode);
        EinkStatus refreshDisplay(const EinkRefreshMode refreshMode);
        EinkStatus prepareDisplay(const EinkRefreshMode refreshMode, const WaveformTemperature behaviour);

        FrameSize size;

        EinkWaveformSettings_t currentWaveform;
        EinkDisplayColorMode displayMode;

        std::shared_ptr<drivers::DriverLPSPI> driverLPSPI;
    };
} // namespace hal::eink

A module-bsp/board/rt1051/bsp/eink/eink_dimensions.cpp => module-bsp/board/rt1051/bsp/eink/eink_dimensions.cpp +47 -0
@@ 0,0 1,47 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "eink_dimensions.hpp"

#include <eink-config.h>

namespace hal::eink
{

    std::uint32_t getDisplayXAxis(EinkFrame_t frame)
    {
#if defined(EINK_ROTATE_90_CLOCKWISE)
        return BOARD_EINK_DISPLAY_RES_Y - frame.pos_y - frame.height;
#else
        return BOARD_EINK_DISPLAY_RES_X - frame.pos_x - frame.width;
#endif
    }

    std::uint32_t getDisplayYAxis(EinkFrame_t frame)
    {
#if defined(EINK_ROTATE_90_CLOCKWISE)
        return BOARD_EINK_DISPLAY_RES_X - frame.pos_x - frame.width;
#else
        return BOARD_EINK_DISPLAY_RES_Y - frame.pos_y - frame.height;
#endif
    }

    std::uint32_t getDisplayWindowWidth(EinkFrame_t frame)
    {
#if defined(EINK_ROTATE_90_CLOCKWISE)
        return frame.height;
#else
        return frame.width;
#endif
    }

    std::uint32_t getDisplayWindowHeight(EinkFrame_t frame)
    {
#if defined(EINK_ROTATE_90_CLOCKWISE)
        return frame.width;
#else
        return frame.height;
#endif
    }

} // namespace hal::eink

A module-bsp/board/rt1051/bsp/eink/eink_dimensions.hpp => module-bsp/board/rt1051/bsp/eink/eink_dimensions.hpp +25 -0
@@ 0,0 1,25 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <cstdint>

#include <eink-config.h>
#include "ED028TC1.h"

#if defined(EINK_ROTATE_90_CLOCKWISE)
#define EINK_DISPLAY_RES_X (BOARD_EINK_DISPLAY_RES_Y)
#define EINK_DISPLAY_RES_Y (BOARD_EINK_DISPLAY_RES_X)
#else
#define EINK_DISPLAY_RES_X (BOARD_EINK_DISPLAY_RES_X)
#define EINK_DISPLAY_RES_Y (BOARD_EINK_DISPLAY_RES_Y)
#endif

namespace hal::eink
{
    std::uint32_t getDisplayXAxis(EinkFrame_t frame);
    std::uint32_t getDisplayYAxis(EinkFrame_t frame);
    std::uint32_t getDisplayWindowWidth(EinkFrame_t frame);
    std::uint32_t getDisplayWindowHeight(EinkFrame_t frame);
} // namespace hal::eink

M module-bsp/hal/CMakeLists.txt => module-bsp/hal/CMakeLists.txt +1 -0
@@ 11,6 11,7 @@ target_sources(hal
        include/hal/GenericFactory.hpp
        include/hal/battery_charger/AbstractBatteryCharger.hpp
		include/hal/cellular/SIM.hpp
        include/hal/eink/AbstractEinkDisplay.hpp
        include/hal/key_input/RawKey.hpp
        include/hal/key_input/KeyEventDefinitions.hpp
        include/hal/key_input/AbstractKeyInput.hpp

A module-bsp/hal/include/hal/eink/AbstractEinkDisplay.hpp => module-bsp/hal/include/hal/eink/AbstractEinkDisplay.hpp +80 -0
@@ 0,0 1,80 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <memory>

#include <devices/Device.hpp>

namespace hal::eink
{

    enum class EinkDisplayColorMode
    {
        EinkDisplayColorModeStandard,
        EinkDisplayColorModeInverted
    };

    enum class WaveformTemperature
    {
        KEEP_CURRENT,
        MEASURE_NEW,
    };

    enum class EinkRefreshMode
    {
        REFRESH_NONE,
        REFRESH_FAST = 1,
        REFRESH_DEEP
    };

    enum class EinkStatus
    {
        EinkOK, //!< EinkOK
        EinkError,
        EinkSPIErr,                //!< EinkSPIErr
        EinkSPINotInitializedErr,  //!< EinkSPINotInitializedErr
        EinkDMAErr,                //!< EinkDMAErr
        EinkInitErr,               //!< EinkInitErr
        EinkTimeout,               //!< Timeout occured while waiting for not busy signal from EINK
        EinkNoMem,                 //!< Could not allocate memory
        EinkWaveformsFileOpenFail, //!< Could not open the file with the waveforms for EPD display

        EinkUnknown,
    };

    struct FrameSize
    {
        uint16_t width;
        uint16_t height;
    };

    struct EinkFrame
    {
        uint16_t pos_x;
        uint16_t pos_y;
        FrameSize size;
    };

    class AbstractEinkDisplay
    {
      public:
        struct Factory
        {
            static std::unique_ptr<AbstractEinkDisplay> create(FrameSize size);
        };

        virtual ~AbstractEinkDisplay() = default;

        virtual void setMode(const EinkDisplayColorMode mode) noexcept                                     = 0;
        virtual EinkStatus showImage(std::uint8_t *frameBuffer, const EinkRefreshMode refreshMode)         = 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 resetAndInit()                                   = 0;
        virtual std::shared_ptr<devices::Device> getDevice() const noexcept = 0;
    };
} // namespace hal::eink

M module-gui/CMakeLists.txt => module-gui/CMakeLists.txt +1 -0
@@ 27,6 27,7 @@ target_link_libraries(${PROJECT_NAME}
        json::json
        pugixml::pugixml
        utils-time
        Microsoft.GSL::GSL
)

# Board specific compilation definitions,options,include directories and features

M module-services/service-cellular/call/CMakeLists.txt => module-services/service-cellular/call/CMakeLists.txt +1 -0
@@ 25,6 25,7 @@ target_include_directories(${PROJECT_NAME}

target_link_libraries(${PROJECT_NAME}
    PRIVATE
        Microsoft.GSL::GSL
        module-sys
        sml::utils::logger
    PUBLIC

M module-services/service-eink/CMakeLists.txt => module-services/service-eink/CMakeLists.txt +4 -3
@@ 3,7 3,6 @@ message( "${PROJECT_NAME}  ${CMAKE_CURRENT_LIST_DIR}" )

set(SOURCES
    ServiceEink.cpp
    EinkDisplay.cpp
    api/ServiceEinkApi.cpp
    internal/StaticData.cpp
    messages/ImageMessage.cpp


@@ 14,11 13,13 @@ add_library(${PROJECT_NAME} STATIC ${SOURCES})
add_board_subdirectory(board)

target_link_libraries( ${PROJECT_NAME}
        module-utils
    PUBLIC
        module-bsp
        Microsoft.GSL::GSL
        messagetype
        module-sys
    PRIVATE
        Microsoft.GSL::GSL
        module-utils
)

target_include_directories(${PROJECT_NAME}

D module-services/service-eink/EinkDisplay.hpp => module-services/service-eink/EinkDisplay.hpp +0 -62
@@ 1,62 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <EinkIncludes.hpp>
#include <gui/Common.hpp>
#include "Common.hpp"

#include <cstdint>
#include <memory>
#include "drivers/lpspi/DriverLPSPI.hpp"
#include "EinkSentinel.hpp"

namespace service::eink
{
    /**
     * Specifies the Eink display.
     * Responsible for handling low-level Eink display operations, e.g. switching power modes, updating, refreshing,
     * etc.
     */
    class EinkDisplay
    {
      public:
        explicit EinkDisplay(::gui::Size screenSize);
        ~EinkDisplay() noexcept;

        EinkStatus_e resetAndInit();
        EinkStatus_e update(std::uint8_t *displayBuffer);
        EinkStatus_e refresh(EinkDisplayTimingsMode_e refreshMode);
        void dither();
        void powerOn();
        void powerOff();
        void shutdown();
        void wipeOut();

        EinkStatus_e setWaveform(EinkWaveforms_e mode, std::int32_t temperature);
        void setMode(EinkDisplayColorMode_e mode) noexcept;

        std::int32_t getLastTemperature() const noexcept;
        ::gui::Size getSize() const noexcept;

        [[nodiscard]] auto getDevice() const noexcept -> std::shared_ptr<devices::Device>;
        void setEinkSentinel(std::shared_ptr<EinkSentinel> sentinel);

      private:
        static unsigned int toWaveformTemperatureOffset(std::int32_t temperature) noexcept;
        static unsigned int toWaveformOffset(unsigned short LUTbank, unsigned int temperatureOffset) noexcept;
        static unsigned int toWaveformOffset(EinkWaveforms_e mode, unsigned int temperatureOffset);
        bool isNewWaveformNeeded(EinkWaveforms_e newMode, int32_t newTemperature) const;
        void resetWaveformSettings();

        EinkBpp_e getCurrentBitsPerPixelFormat() const noexcept;
        static constexpr ::gui::Point pointTopLeft{0, 0};
        const ::gui::Size size;
        EinkWaveformSettings_t currentWaveform;
        EinkDisplayColorMode_e displayMode;

        std::shared_ptr<drivers::DriverLPSPI> driverLPSPI;
        std::shared_ptr<EinkSentinel> eInkSentinel;
    };
} // namespace service::eink

M module-services/service-eink/ServiceEink.cpp => module-services/service-eink/ServiceEink.cpp +42 -72
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <board.h>


@@ 29,22 29,38 @@ namespace service::eink
        constexpr auto ServceEinkStackDepth = 4096U;
        constexpr std::chrono::milliseconds displayPowerOffTimeout{2000};

        std::string toSettingString(EinkModeMessage::Mode mode)
        std::string toSettingString(const EinkModeMessage::Mode mode)
        {
            if (mode == EinkModeMessage::Mode::Normal) {
                return "0";
            }
            return "1";
        }

        hal::eink::EinkRefreshMode translateToEinkRefreshMode(const gui::RefreshModes guiRefreshMode)
        {
            switch (guiRefreshMode) {
            case gui::RefreshModes::GUI_REFRESH_DEEP:
                return hal::eink::EinkRefreshMode::REFRESH_DEEP;
            case gui::RefreshModes::GUI_REFRESH_FAST:
                return hal::eink::EinkRefreshMode::REFRESH_FAST;
            default:
                return hal::eink::EinkRefreshMode::REFRESH_NONE;
            }
        }
    } // namespace

    ServiceEink::ServiceEink(ExitAction exitAction, const std::string &name, std::string parent)
        : sys::Service(name, std::move(parent), ServceEinkStackDepth),
          exitAction{exitAction}, display{{BOARD_EINK_DISPLAY_RES_X, BOARD_EINK_DISPLAY_RES_Y}},
          currentState{State::Running}, settings{std::make_unique<settings::Settings>()}
        : sys::Service(name, std::move(parent), ServceEinkStackDepth), exitAction{exitAction},
          currentState{State::Running}, display{hal::eink::AbstractEinkDisplay::Factory::create(
                                            hal::eink::FrameSize{BOARD_EINK_DISPLAY_RES_X, BOARD_EINK_DISPLAY_RES_Y})},
          settings{std::make_unique<settings::Settings>()}
    {
        displayPowerOffTimer = sys::TimerFactory::createSingleShotTimer(
            this, "einkDisplayPowerOff", displayPowerOffTimeout, [this](sys::Timer &) { display.powerOff(); });
            this, "einkDisplayPowerOff", displayPowerOffTimeout, [this](sys::Timer &) {
                display->powerOff();
                eInkSentinel->ReleaseMinimumFrequency();
            });
        connect(typeid(EinkModeMessage),
                [this](sys::Message *message) -> sys::MessagePointer { return handleEinkModeChangedMessage(message); });



@@ 55,7 71,6 @@ namespace service::eink
                [this](sys::Message *request) -> sys::MessagePointer { return handlePrepareEarlyRequest(request); });

        eInkSentinel = std::make_shared<EinkSentinel>(name::eink, this);
        display.setEinkSentinel(eInkSentinel);
    }

    sys::MessagePointer ServiceEink::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *response)


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


@@ 74,13 89,14 @@ namespace service::eink
        settings->init(service::ServiceProxy(shared_from_this()));
        initStaticData();

        auto deviceRegistrationMsg = std::make_shared<sys::DeviceRegistrationMessage>(display.getDevice());
        auto deviceRegistrationMsg = std::make_shared<sys::DeviceRegistrationMessage>(display->getDevice());
        bus.sendUnicast(deviceRegistrationMsg, service::name::system_manager);

        auto sentinelRegistrationMsg = std::make_shared<sys::SentinelRegistrationMessage>(eInkSentinel);
        bus.sendUnicast(sentinelRegistrationMsg, service::name::system_manager);

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

        return sys::ReturnCodes::Success;
    }


@@ 96,9 112,9 @@ namespace service::eink
    sys::ReturnCodes ServiceEink::DeinitHandler()
    {
        if (exitAction == ExitAction::WipeOut) {
            display.wipeOut();
            display->wipeOut();
        }
        display.shutdown();
        display->shutdown();
        settings->deinit();
        return sys::ReturnCodes::Success;
    }


@@ 123,17 139,19 @@ namespace service::eink
    {
        setState(State::Running);

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

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

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


@@ 149,10 167,10 @@ namespace service::eink
    {
        auto invertedModeRequested = mode == EinkModeMessage::Mode::Invert;
        if (invertedModeRequested) {
            display.setMode(EinkDisplayColorMode_e::EinkDisplayColorModeInverted);
            display->setMode(hal::eink::EinkDisplayColorMode::EinkDisplayColorModeInverted);
        }
        else {
            display.setMode(EinkDisplayColorMode_e::EinkDisplayColorModeStandard);
            display->setMode(hal::eink::EinkDisplayColorMode::EinkDisplayColorModeStandard);
        }
        internal::StaticData::get().setInvertedMode(invertedModeRequested);
    }


@@ 166,70 184,22 @@ namespace service::eink
        }
        utils::time::Scoped measurement("ImageMessage");

        showImage(message->getData(), message->getRefreshMode());
        return std::make_shared<service::eink::ImageDisplayedNotification>(message->getContextId());
    }

    void ServiceEink::showImage(std::uint8_t *frameBuffer, ::gui::RefreshModes refreshMode)
    {
        displayPowerOffTimer.stop();

        auto displayPowerOffTimerReload = gsl::finally([this]() { displayPowerOffTimer.start(); });

        if (const auto status = prepareDisplay(refreshMode, WaveformTemperature::KEEP_CURRENT);
            status != EinkStatus_e::EinkOK) {
            LOG_FATAL("Failed to prepare frame");
            return;
        }

        if (const auto status = updateDisplay(frameBuffer, refreshMode); status != EinkStatus_e::EinkOK) {
            LOG_FATAL("Failed to update frame");
            return;
        auto status = display->showImage(message->getData(), translateToEinkRefreshMode(message->getRefreshMode()));
        if (status != hal::eink::EinkStatus::EinkOK) {
            LOG_ERROR("Error during drawing image on eink: %s", magic_enum::enum_name(status).data());
        }

        if (const auto status = refreshDisplay(refreshMode); status != EinkStatus_e::EinkOK) {
            LOG_FATAL("Failed to refresh frame");
            return;
        }
    }

    EinkStatus_e ServiceEink::updateDisplay(std::uint8_t *frameBuffer, ::gui::RefreshModes refreshMode)
    {
        return display.update(frameBuffer);
    }

    EinkStatus_e ServiceEink::refreshDisplay(::gui::RefreshModes refreshMode)
    {
        const auto isDeepRefresh = refreshMode == ::gui::RefreshModes::GUI_REFRESH_DEEP;
        return display.refresh(isDeepRefresh ? EinkDisplayTimingsDeepCleanMode : EinkDisplayTimingsFastRefreshMode);
    }

    EinkStatus_e ServiceEink::prepareDisplay(::gui::RefreshModes refreshMode, WaveformTemperature behaviour)
    {
        EinkStatus_e status;

        displayPowerOffTimer.stop();
        display.powerOn();

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

        if (refreshMode == ::gui::RefreshModes::GUI_REFRESH_DEEP) {
            status = display.setWaveform(EinkWaveforms_e::EinkWaveformGC16, temperature);
            if (status == EinkOK) {
                display.dither();
            }
        }
        else {
            status = display.setWaveform(EinkWaveforms_e::EinkWaveformDU2, temperature);
        }
        return status;
        return std::make_shared<service::eink::ImageDisplayedNotification>(message->getContextId());
    }

    sys::MessagePointer ServiceEink::handlePrepareEarlyRequest(sys::Message *message)
    {
        const auto waveformUpdateMsg = static_cast<service::eink::PrepareDisplayEarlyRequest *>(message);
        prepareDisplay(waveformUpdateMsg->getRefreshMode(), WaveformTemperature::MEASURE_NEW);
        display->prepareEarlyRequest(translateToEinkRefreshMode(waveformUpdateMsg->getRefreshMode()),
                                     hal::eink::WaveformTemperature::MEASURE_NEW);
        return sys::MessageNone{};
    }


M module-services/service-eink/ServiceEink.hpp => module-services/service-eink/ServiceEink.hpp +5 -14
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 9,7 9,6 @@
#include <Timers/TimerHandle.hpp>

#include "EinkSentinel.hpp"
#include "EinkDisplay.hpp"

#include <service-db/DBServiceName.hpp>
#include <service-db/Settings.hpp>


@@ 18,6 17,9 @@
#include <cstdint>
#include <string>
#include <module-services/service-eink/messages/EinkModeMessage.hpp>
#include <hal/eink/AbstractEinkDisplay.hpp>

#include "Common.hpp"

namespace service::eink
{


@@ 46,23 48,12 @@ namespace service::eink
            Suspended
        };

        /// It takes 25ms to get a new measurement
        enum class WaveformTemperature
        {
            KEEP_CURRENT,
            MEASURE_NEW,
        };

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

        void enterActiveMode();
        void suspend();

        void showImage(std::uint8_t *frameBuffer, ::gui::RefreshModes refreshMode);
        EinkStatus_e prepareDisplay(::gui::RefreshModes refreshMode, WaveformTemperature behaviour);
        EinkStatus_e refreshDisplay(::gui::RefreshModes refreshMode);
        EinkStatus_e updateDisplay(uint8_t *frameBuffer, ::gui::RefreshModes refreshMode);
        void setDisplayMode(EinkModeMessage::Mode mode);

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


@@ 72,8 63,8 @@ namespace service::eink
        void initStaticData();

        ExitAction exitAction;
        EinkDisplay display;
        State currentState;
        std::unique_ptr<hal::eink::AbstractEinkDisplay> display;
        sys::TimerHandle displayPowerOffTimer;
        std::shared_ptr<EinkSentinel> eInkSentinel;
        std::unique_ptr<settings::Settings> settings;

M module-services/service-gui/CMakeLists.txt => module-services/service-gui/CMakeLists.txt +7 -3
@@ 26,9 26,13 @@ target_sources(service-gui


target_link_libraries(service-gui
    service-eink
    module-gui
    module-apps
    PUBLIC
        service-eink
        module-gui
        module-apps
    PRIVATE
        Microsoft.GSL::GSL
        
)

target_include_directories(service-gui

M module-sys/SystemManager/CMakeLists.txt => module-sys/SystemManager/CMakeLists.txt +1 -0
@@ 47,6 47,7 @@ target_link_libraries(sys-manager
        service-desktop
        msgpack11
        system-stats-sink
        Microsoft.GSL::GSL
)

if (${ENABLE_TESTS})

M products/BellHybrid/apps/application-bell-background-sounds/CMakeLists.txt => products/BellHybrid/apps/application-bell-background-sounds/CMakeLists.txt +1 -0
@@ 48,6 48,7 @@ target_link_libraries(application-bell-background-sounds
        apps-common
        bell::audio
        bell::alarms
        Microsoft.GSL::GSL

    PUBLIC
        module-gui

M products/BellHybrid/apps/application-bell-powernap/CMakeLists.txt => products/BellHybrid/apps/application-bell-powernap/CMakeLists.txt +1 -0
@@ 48,6 48,7 @@ target_link_libraries(application-bell-powernap
        bell::keymap
        bell::alarms
        bell::db
        Microsoft.GSL::GSL

    PUBLIC
        module-gui

M products/BellHybrid/apps/common/CMakeLists.txt => products/BellHybrid/apps/common/CMakeLists.txt +1 -0
@@ 127,6 127,7 @@ target_link_libraries(application-bell-common
        bell::audio
        module-gui
        bell::db
        Microsoft.GSL::GSL
        )

if (${ENABLE_TESTS})

M products/PurePhone/test/test-settings/CMakeLists.txt => products/PurePhone/test/test-settings/CMakeLists.txt +1 -0
@@ 13,6 13,7 @@ add_catch2_executable(
            module-vfs
            service-audio
            service-cellular
            Microsoft.GSL::GSL
        DEPS
            module-sys
)