From 45cbffe78d63bb641f3a6f7dc91cf271082118b7 Mon Sep 17 00:00:00 2001 From: xas Date: Mon, 23 Oct 2023 18:06:12 +0200 Subject: [PATCH 01/12] feat: add GDEW0154x e-paper driver --- devices/ePaper/Drivers/Gdew0154x/Command.cs | 123 ++++ .../ePaper/Drivers/Gdew0154x/Gdew0154m09.cs | 44 ++ devices/ePaper/Drivers/Gdew0154x/Gdew0154x.cs | 636 ++++++++++++++++++ devices/ePaper/Drivers/Gdew0154x/SleepMode.cs | 33 + devices/ePaper/Fonts/Font8x12.cs | 4 +- devices/ePaper/ePaper.nfproj | 198 +++--- 6 files changed, 939 insertions(+), 99 deletions(-) create mode 100644 devices/ePaper/Drivers/Gdew0154x/Command.cs create mode 100644 devices/ePaper/Drivers/Gdew0154x/Gdew0154m09.cs create mode 100644 devices/ePaper/Drivers/Gdew0154x/Gdew0154x.cs create mode 100644 devices/ePaper/Drivers/Gdew0154x/SleepMode.cs diff --git a/devices/ePaper/Drivers/Gdew0154x/Command.cs b/devices/ePaper/Drivers/Gdew0154x/Command.cs new file mode 100644 index 0000000000..b6d0ad1c86 --- /dev/null +++ b/devices/ePaper/Drivers/Gdew0154x/Command.cs @@ -0,0 +1,123 @@ +// Copyright (c) 2022 The nanoFramework project contributors +// See LICENSE file in the project root for full license information. + +namespace Iot.Device.EPaper.Drivers.GDEW0154x +{ + /// + /// Commands supported by SSD1681. + /// + public enum Command : byte + { + /// + /// Sets the settings for the screen. + /// + /// + /// Refer to the datasheet for detailed information about the command and its parameters. + /// + PanelSetting = 0x00, + + /// + /// Power off command. + /// + /// + /// After power off command, driver will power off base on power off sequence. + /// After power off command, BUSY_N signal will drop from high to low. + /// When finish the power off sequence, BUSY_N singal will rise from low to high. + /// Power off command will turn off charge pump, T-con, source driver, gate driver, VCOM, + /// temperature sensor, but register and SRAM data will keep until VDD off. + /// + PowerOff = 0x02, + + /// + /// Power on command. + /// + /// + /// After power on command, driver will power on base on power on sequence. + /// After power on command, BUSY_N signal will drop from high to low. + /// When finishing the power on sequence, BUSY_N signal will rise from low to high. + /// + PowerOn = 0x04, + + /// + /// Sets the screen in deep sleep mode. + /// + /// + /// The command define as follows: After this command is transmitted, the chip would + /// enter the deep-sleep mode to save power. + /// The deep sleep mode would return to standby by hardware reset. + /// + DeepSleepMode = 0x07, + + /// + /// Start a data pixel value writing. + /// + /// + /// The command define as follows: + /// The register is indicates that user start to transmit data, then write to SRAM. + /// While data transmission complete, user must send command 11H. + /// Then chip will start to send data/VCOM for panel. + /// In BW mode, this command writes “OLD” data to SRAM. + /// In BWR mode, this command writes “B/W” data to SRAM. + /// In Program mode, this command writes “OTP” data to SRAM for programming. + /// + DataStartTransmission1 = 0x10, + + /// + /// Sets the settings for the screen. + /// + /// + /// The command defines as : + /// While users send this command, driver will refresh display + /// (data/VCOM) base on SRAM data and LUT. + /// After display refresh command, BUSY_N signal will become “0”. + /// + DisplayRefresh = 0x12, + + /// + /// Start a data pixel value writing. + /// + /// + /// The command define as follows: + /// The register is indicates that user start to transmit data, then write to SRAM. + /// While data transmission complete, user must send command 11H. + /// Then chip will start to send data/VCOM for panel. + /// In B/W mode, this command writes “NEW” data to SRAM. + /// In B/W/Red mode, this command writes “RED” data to SRAM. + /// + DataStartTransmission2 = 0x13, + + /// + /// Defines non-overlap period of Gate and Source. + /// + /// + /// Refer to the datasheet for detailed information about the command and its parameters. + /// + TCONSetting = 0x60, + + /// + /// Define screen resolution setting. + /// + /// + /// This command defines as follow: + /// HRES[7:3]: Horizontal Display Resolution (first data) + /// VRES[8:0]: Vertical Display Resolution (second and third data) + /// Active channel calculation: + /// - GD: First active gate = G0(Fixed); LAST active gate = first active + VRES[8:0] – 1 + /// - SD: First active source = S0(Fixed); LAST active source = first active+HRES[7:3]*8–1 + /// + ResolutionSetting = 0x61, + + /// + /// Defines power saving settings. + /// + /// + /// This command is set for saving power during fresh period. + /// If the output voltage of VCOM / Source is from negative to positive or from positive to negative, + /// the power saving mechanism will be activated. + /// The active period width is defined by the following two parameters. + /// - VCOM_W[3:0]: VCOM power saving width (unit = line period) + /// - SD_W[3:0]: Source power saving width (unit = 660nS) + /// + PowerSaving = 0xe3 + } +} diff --git a/devices/ePaper/Drivers/Gdew0154x/Gdew0154m09.cs b/devices/ePaper/Drivers/Gdew0154x/Gdew0154m09.cs new file mode 100644 index 0000000000..a03e31e235 --- /dev/null +++ b/devices/ePaper/Drivers/Gdew0154x/Gdew0154m09.cs @@ -0,0 +1,44 @@ +// Copyright (c) 2022 The nanoFramework project contributors +// See LICENSE file in the project root for full license information. + +using System; +using System.Device.Gpio; +using System.Device.Spi; + +namespace Iot.Device.EPaper.Drivers.GDEW0154x +{ + /// + /// A driver class for the GDEW0154M09 display controller. + /// + public class Gdew0154m09 : Gdew0154x + { + /// + /// Initializes a new instance of the class. + /// + /// The communication channel to the GDEW0154M09-based dispay. + /// The reset GPIO pin. Passing an invalid pin number such as -1 will prevent this driver from opening the pin. Caller should handle hardware resets. + /// The busy GPIO pin. + /// The data/command GPIO pin. + /// The to use when initializing the pins. + /// Page the frame buffer and all operations to use less memory. + /// is null. + /// Display width and height can't be less than 0 or greater than 200. + /// + /// For a 200x200 GDEW0154M09 display, a full Frame requires about 5KB of RAM ((200 * 200) / 8). + /// If you can't guarantee 5KB to be available to the driver then enable paging by setting to true. + /// A page uses about 1KB (5KB / PagesPerFrame). + /// + public Gdew0154m09( + SpiDevice spiDevice, + int resetPin, + int busyPin, + int dataCommandPin, + GpioController gpioController = null, + bool enableFramePaging = false) : base(spiDevice, resetPin, busyPin, dataCommandPin, 200, 200, gpioController, enableFramePaging) + { + } + + /// + protected override int PagesPerFrame { get; } = 5; + } +} diff --git a/devices/ePaper/Drivers/Gdew0154x/Gdew0154x.cs b/devices/ePaper/Drivers/Gdew0154x/Gdew0154x.cs new file mode 100644 index 0000000000..95d86a259f --- /dev/null +++ b/devices/ePaper/Drivers/Gdew0154x/Gdew0154x.cs @@ -0,0 +1,636 @@ +// Copyright (c) 2022 The nanoFramework project contributors +// See LICENSE file in the project root for full license information. + +using System; +using System.Device.Gpio; +using System.Device.Spi; +using System.Drawing; +using System.Threading; +using Iot.Device.EPaper.Buffers; +using Iot.Device.EPaper.Enums; +using nanoFramework.UI; + +namespace Iot.Device.EPaper.Drivers.GDEW0154x +{ + /// + /// Base class for GDEW0154-based ePaper devices. + /// + public abstract class Gdew0154x : IEPaperDisplay + { + private SpiDevice _spiDevice; + private GpioController _gpioController; + private GpioPin _resetPin; + private GpioPin _busyPin; + private GpioPin _dataCommandPin; + + private bool _disposed; + + /// + /// The max supported clock frequency for the GDEW0154x controller. 10MHz. + /// + public const int SpiClockFrequency = 10_000_000; + + /// + /// The supported by the GDEW0154x controller. + /// + public const SpiMode SpiMode = System.Device.Spi.SpiMode.Mode0; + + /// + /// Initializes a new instance of the class. + /// + /// The communication channel to the SSD1681-based dispay. + /// The reset GPIO pin. Passing an invalid pin number such as -1 will prevent this driver from opening the pin. Caller should handle hardware resets. + /// The busy GPIO pin. + /// The data/command GPIO pin. + /// The width of the display. + /// The height of the display. + /// The to use when initializing the pins. + /// Page the frame buffer and all operations to use less memory. + /// is null. + /// Display width and height can't be less than 0 or greater than 200. + protected Gdew0154x( + SpiDevice spiDevice, + int resetPin, + int busyPin, + int dataCommandPin, + int width, + int height, + GpioController gpioController, + bool enableFramePaging = false) + { + _spiDevice = spiDevice ?? throw new ArgumentNullException(nameof(spiDevice)); + _gpioController = gpioController ?? throw new ArgumentNullException(nameof(gpioController)); + + // setup the gpio pins + _resetPin = resetPin >= 0 ? _gpioController.OpenPin(resetPin, PinMode.Output) : null; + _dataCommandPin = _gpioController.OpenPin(dataCommandPin, PinMode.Output); + _busyPin = _gpioController.OpenPin(busyPin, PinMode.Input); + + Width = width; + Height = height; + PagedFrameDrawEnabled = enableFramePaging; + + PowerState = PowerState.Unknown; + + InitializeFrameBuffer(width, height, enableFramePaging); + CalculateFrameBufferPageBounds(); + FrameBuffer1bpp.Clear(Color.White); + } + + /// + public virtual int Width { get; protected set; } + + /// + public virtual int Height { get; protected set; } + + /// + public virtual IFrameBuffer FrameBuffer + { + get + { + return FrameBuffer1bpp; + } + + protected set + { + FrameBuffer1bpp = (FrameBuffer1BitPerPixel)value; + } + } + + /// + public virtual bool PagedFrameDrawEnabled { get; protected set; } + + /// + /// Gets or sets the current power state of the display panel. + /// + public virtual PowerState PowerState { get; protected set; } + + /// + /// Gets or sets the current frame buffer page index. + /// + protected int CurrentFrameBufferPage { get; set; } + + /// + /// Gets or sets the current frame buffer page lower bounds. + /// + protected int CurrentFrameBufferPageLowerBound { get; set; } + + /// + /// Gets or sets the current frame buffer page upper bounds. + /// + protected int CurrentFrameBufferPageUpperBound { get; set; } + + /// + /// Gets or sets the current frame buffer start X-Position. + /// + protected int CurrentFrameBufferStartXPosition { get; set; } + + /// + /// Gets or sets the current frame buffer start Y-Position. + /// + protected int CurrentFrameBufferStartYPosition { get; set; } + + /// + /// Gets the number of pages in every frame buffer. + /// + protected abstract int PagesPerFrame { get; } + + /// + /// Gets or sets the used internally by devices to represents the frame. + /// + protected FrameBuffer1BitPerPixel FrameBuffer1bpp { get; set; } + + /// + /// Gets the index of the first frame page. + /// + protected int FirstPageIndex { get; } = 0; + + /// + public virtual void BeginFrameDraw() + { + // make sure we start from the first page with clear buffers + SetFrameBufferPage(FirstPageIndex); + } + + /// + /// Calculates the upper and lower bounds of the current frame buffer page. + /// + protected virtual void CalculateFrameBufferPageBounds() + { + CurrentFrameBufferPageLowerBound = CurrentFrameBufferPage * FrameBuffer.BufferByteCount / FrameBuffer.BitDepth; + CurrentFrameBufferPageUpperBound = (CurrentFrameBufferPage + 1) * FrameBuffer.BufferByteCount / FrameBuffer.BitDepth; + + CurrentFrameBufferStartXPosition = GetXPositionFromFrameBufferIndex(CurrentFrameBufferPageLowerBound); + CurrentFrameBufferStartYPosition = GetYPositionFromFrameBufferIndex(CurrentFrameBufferPageLowerBound); + + FrameBuffer.StartPoint = new Point(CurrentFrameBufferStartXPosition, CurrentFrameBufferStartYPosition); + FrameBuffer.CurrentFramePage = CurrentFrameBufferPage; + } + + /// + public virtual void Clear(bool triggerPageRefresh = false) + { + SetFrameBufferPage(FirstPageIndex); + + // paging is enabled, flush as per number of pages to ensure all display RAM is cleared + if (PagedFrameDrawEnabled) + { + do + { + Flush(); + + // sleep for 20ms to allow other threads to have their chance to execute + Thread.Sleep(20); + + CurrentFrameBufferPage++; + CalculateFrameBufferPageBounds(); + } + while (CurrentFrameBufferPage < PagesPerFrame); + + CurrentFrameBufferPage = FirstPageIndex; + CalculateFrameBufferPageBounds(); + } + else + { + // paging is disabled, so the internal frame covers the entire display frame. we only need to flush once. + Flush(); + } + + if (triggerPageRefresh) + { + PerformFullRefresh(); + } + } + + /// + /// Draws the specified buffer directly to the Black/White RAM on the display. + /// Call after to update the display. + /// + /// The buffer array to draw. + public void DirectDrawBuffer(params byte[] buffer) + { + SendCommand((byte)Command.DataStartTransmission2); + SendData(buffer); + WaitMs(5); + + SendCommand((byte)Command.DisplayRefresh); + WaitMs(500); + } + + /// + /// Draws a single pixel to the appropriate frame buffer. + /// + /// The X Position. + /// The Y Position. + /// True to invert the pixel from white to black. + /// + /// The GDEW0154x comes with 2 RAMs: a Black and White RAM and a Red RAM. + /// Writing to the B/W RAM draws B/W pixels on the panel. While writing to the Red RAM draws red pixels on the panel (if the panel supports red). + /// However, the GDEW0154x doesn't support specifying the color level (no grayscaling), therefore the way the buffer is selected + /// is by performing a simple binary check: + /// if R >= 128 and G == 0 and B == 0 then write a red pixel to the Red Buffer/RAM + /// if R == 0 and G == 0 and B == 0 then write a black pixel to B/W Buffer/RAM + /// else, assume white pixel and write to B/W Buffer/RAM. + /// + public void DrawPixel(int x, int y, bool inverted) + { + DrawPixel(x, y, inverted ? Color.Black : Color.White); + } + + /// + /// + /// The GDEW0154x comes with 2 RAMs: a Black and White RAM and a Red RAM. + /// Writing to the B/W RAM draws B/W pixels on the panel. While writing to the Red RAM draws red pixels on the panel (if the panel supports red). + /// However, the GDEW0154x doesn't support specifying the color level (no grayscaling), therefore the way the buffer is selected + /// is by performing a simple binary check: + /// if R >= 128 and G == 0 and B == 0 then write a red pixel to the Red Buffer/RAM + /// if R == 0 and G == 0 and B == 0 then write a black pixel to B/W Buffer/RAM + /// else, assume white pixel and write to B/W Buffer/RAM. + /// + public virtual void DrawPixel(int x, int y, Color color) + { + // ignore out of bounds draw attempts + if (x < 0 || x >= Width || y < 0 || y >= Height) + { + return; + } + + var frameByteIndex = GetFrameBufferIndex(x, y); + var pageByteIndex = frameByteIndex - CurrentFrameBufferPageLowerBound; + + // if the specified point falls in the current page, update the buffer + if (CurrentFrameBufferPageLowerBound <= frameByteIndex + && frameByteIndex < CurrentFrameBufferPageUpperBound) + { + /* + * Lookup Table for colors on GDEW0154x + * + * LUT for Black and White ePaper display with GDEW0154x + * | | | + * | Data B/W RAM | Result Pixel Color | + * |----------------|--------------------| + * | 0 | Black | + * | 1 | White | + */ + + if (color == Color.Black) + { + // black pixel + FrameBuffer1bpp.Buffer[pageByteIndex] &= (byte)~(128 >> (x & 7)); + } + else + { + // white pixel + FrameBuffer1bpp.Buffer[pageByteIndex] |= (byte)(128 >> (x & 7)); + } + } + } + + /// + public virtual void EndFrameDraw() + { + Flush(); + } + + /// + /// Snaps the provided coordinates to the lower bounds of the display if out of allowed range. + /// + /// The X position. + /// The Y position. + protected virtual void EnforceBounds(ref int x, ref int y) + { + x = x < 0 || x > Width - 1 ? 0 : x; + y = y < 0 || y > Height - 1 ? 0 : y; + } + + /// + public virtual void Flush() + { + // write B/W frame to the display's RAM. + DirectDrawBuffer(FrameBuffer1bpp.Buffer); + } + + /// + /// Gets the index of the byte containing the pixel specified by the and parameters. + /// + /// The X position of the pixel. + /// The Y position of the pixel. + /// The index of the byte in the frame buffer which contains the specified pixel. + protected virtual int GetFrameBufferIndex(int x, int y) + { + // x specifies the column + return (x + (y * Width)) / 8; + } + + /// + /// Gets the X position from a buffer index. + /// + /// The buffer index. + /// The X position of a pixel. + protected virtual int GetXPositionFromFrameBufferIndex(int index) + { + if (index <= 0) + { + return 0; + } + + return (index * 8) % Width; + } + + /// + /// Gets the Y position from a buffer index. + /// + /// The buffer index. + /// The Y position of a pixel. + protected virtual int GetYPositionFromFrameBufferIndex(int index) + { + if (index <= 0) + { + return 0; + } + + return index * 8 / Height; + } + + /// + /// Performs the hardware reset commands sequence on the display. + /// + protected virtual void HardwareReset() + { + if (_resetPin == null) + { + // caller opted to reset outside of the driver by not passing a valid reset pin number. + // do nothing. + return; + } + + // specs say to wait 10ms after supplying voltage to the display + WaitMs(10); + + _resetPin.Write(PinValue.High); + WaitMs(100); + + _resetPin.Write(PinValue.Low); + WaitMs(100); + + _resetPin.Write(PinValue.High); + WaitMs(100); + } + + /// + /// Perform the required initialization steps to set up the display. + /// + /// Unable to initialize the display until it has been powered on. Call PowerOn() first. + public virtual void Initialize() + { + if (PowerState != PowerState.PoweredOn) + { + throw new InvalidOperationException(); + } + + SendCommand((byte)Command.PanelSetting); + SendData(0xdf); + SendData(0x0e); + + // FITIinternal code + SendCommand(0x4d); + SendData(0x55); + + SendCommand(0xaa); + SendData(0x0f); + + SendCommand(0xe9); + SendData(0x02); + + SendCommand(0xb6); + SendData(0x11); + + SendCommand(0xf3); + SendData(0x0a); + + SendCommand((byte)Command.ResolutionSetting); + SendData(0xc8); + SendData(0x00); + SendData(0xc8); + + SendCommand((byte)Command.TCONSetting); + SendData(0x00); + + SendCommand((byte)Command.PowerSaving); + SendData(0x00); + + SendCommand((byte)Command.PowerOn); + + WaitReady(); + } + + /// + /// Initializes a new instance of the intenral frame buffer. Supports paging. + /// + /// The width of the frame buffer in pixels. + /// The height of the frame buffer in pixels. + /// If , enables paging the frame. + protected virtual void InitializeFrameBuffer(int width, int height, bool enableFramePaging) + { + var frameBufferHeight = enableFramePaging ? height / PagesPerFrame : height; + FrameBuffer1bpp = new FrameBuffer1BitPerPixel(frameBufferHeight, width); + } + + /// + public virtual bool NextFramePage() + { + if (PagedFrameDrawEnabled && CurrentFrameBufferPage < PagesPerFrame - 1) + { + Flush(); + + CurrentFrameBufferPage++; + SetFrameBufferPage(CurrentFrameBufferPage); + + return true; + } + + return false; + } + + /// + public virtual void PerformFullRefresh() + { + WaitMs(10); + SendCommand((byte)Command.DisplayRefresh); + WaitReady(); + } + + /// + public virtual void PerformPartialRefresh() + { + // No partial refresh implemented + PerformFullRefresh(); + } + + /// + /// Puts the display to sleep using the specified . + /// + /// The sleep mode to use when powering down the display. + public virtual void PowerDown(SleepMode sleepMode = SleepMode.Normal) + { + SendCommand(0x50); + SendData(0x07); + SendCommand((byte)Command.PowerOff); + WaitReady(); + + if (sleepMode == SleepMode.DeepSleepMode) + { + SendCommand((byte)Command.DeepSleepMode); + SendData(0xa5); + } + + PowerState = PowerState.PoweredOff; + } + + /// + /// Performs the required steps to "power on" the display. + /// + public virtual void PowerOn() + { + HardwareReset(); + WaitMs(1000); + SendCommand(0x50); + SendData(0xd7); + SendCommand((byte)Command.PowerOn); + WaitReady(); + + PowerState = PowerState.PoweredOn; + } + + /// + public virtual void SendCommand(params byte[] command) + { + // make sure we are setting data/command pin to low (command mode) + _dataCommandPin.Write(PinValue.Low); + + foreach (var b in command) + { + // write the command byte to the display controller + _spiDevice.WriteByte(b); + } + } + + /// + public virtual void SendData(params byte[] data) + { + // set the data/command pin to high to indicate to the display we will be sending data + _dataCommandPin.Write(PinValue.High); + + foreach (var @byte in data) + { + _spiDevice.WriteByte(@byte); + } + + // go back to low (command mode) + _dataCommandPin.Write(PinValue.Low); + } + + /// + /// Sets the current active frame buffer page to the specified page index. + /// Existing frame buffer is reused by clearing it first and page bounds are recalculated. + /// Make sure to call or + /// to persist the frame to the display's RAM before calling this method. + /// + /// The frame buffer page index to move to. + protected virtual void SetFrameBufferPage(int page) + { + if (page < 0 || page >= PagesPerFrame) + { + page = 0; + } + + FrameBuffer1bpp.Clear(); + CurrentFrameBufferPage = page; + + CalculateFrameBufferPageBounds(); + } + + /// + public virtual void SetPosition(int x, int y) + { + throw new NotImplementedException(); + } + + /// + /// Performs the software reset commands sequence on the display. + /// + protected virtual void SoftwareReset() + { + SendCommand(0x04); + + WaitReady(PinValue.High); + WaitMs(10); + } + + /// + /// A simple wait method that wraps . + /// + /// Number of milliseconds to sleep. + protected virtual void WaitMs(int milliseconds) + { + Thread.Sleep(milliseconds); + } + + /// + public virtual void WaitReady(PinValue pinValue) + { + while (_busyPin.Read() != pinValue) + { + WaitMs(5); + } + } + + /// + public virtual void WaitReady() + { + while (_busyPin.Read() == PinValue.Low) + { + WaitMs(5); + } + } + + #region IDisposable + + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _resetPin?.Dispose(); + _resetPin = null; + + _busyPin?.Dispose(); + _busyPin = null; + + _dataCommandPin?.Dispose(); + _dataCommandPin = null; + + _gpioController = null; + + _spiDevice = null; + _gpioController = null; + + FrameBuffer = null; + } + + _disposed = true; + } + } + + /// + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + #endregion + } +} diff --git a/devices/ePaper/Drivers/Gdew0154x/SleepMode.cs b/devices/ePaper/Drivers/Gdew0154x/SleepMode.cs new file mode 100644 index 0000000000..4095723c89 --- /dev/null +++ b/devices/ePaper/Drivers/Gdew0154x/SleepMode.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2022 The nanoFramework project contributors +// See LICENSE file in the project root for full license information. + +namespace Iot.Device.EPaper.Drivers.GDEW0154x +{ + /// + /// SSD1681 Supported Sleep Modes. + /// + public enum SleepMode : byte + { + /// + /// Normal Sleep Mode. + /// In this mode: + /// - DC/DC Off + /// - No Clock + /// - No Input load + /// - MCU Interface Access: ON + /// - RAM Data Access: ON. + /// + Normal = 0x00, + + /// + /// Deep Sleep Mode. + /// In this mode: + /// - DC/DC Off + /// - No Clock + /// - No Input load + /// - MCU Interface Access: OFF + /// - RAM Data Access: OFF (RAM contents NOT retained). + /// + DeepSleepMode = 0x01, + } +} diff --git a/devices/ePaper/Fonts/Font8x12.cs b/devices/ePaper/Fonts/Font8x12.cs index c7883af207..4ca488bfe8 100644 --- a/devices/ePaper/Fonts/Font8x12.cs +++ b/devices/ePaper/Fonts/Font8x12.cs @@ -111,7 +111,7 @@ public class Font8x12 : IFont }; /// - public new int Width + public override byte Width { get { @@ -120,7 +120,7 @@ public class Font8x12 : IFont } /// - public new int Height + public override byte Height { get { diff --git a/devices/ePaper/ePaper.nfproj b/devices/ePaper/ePaper.nfproj index 4bb052e8da..d4a6e7ec84 100644 --- a/devices/ePaper/ePaper.nfproj +++ b/devices/ePaper/ePaper.nfproj @@ -1,98 +1,102 @@ - - - - - $(MSBuildExtensionsPath)\nanoFramework\v1.0\ - - - - Debug - AnyCPU - {11A8DD76-328B-46DF-9F39-F559912D0360};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 3d8ca135-f17d-4c14-be85-bab9ddb266de - Library - Properties - 512 - Iot.Device.EPaper - Iot.Device.ePaper - v1.0 - bin\$(Configuration)\Iot.Device.ePaper.xml - false - true - true - - - true - - - ..\key.snk - - - false - - - - - - - - - - - - - - - - - - - - - - - - - packages\nanoFramework.CoreLibrary.1.14.2\lib\mscorlib.dll - True - - - packages\nanoFramework.Graphics.Core.1.1.37\lib\nanoFramework.Graphics.Core.dll - True - - - packages\nanoFramework.Runtime.Events.1.11.6\lib\nanoFramework.Runtime.Events.dll - True - - - packages\nanoFramework.System.Device.Gpio.1.1.28\lib\System.Device.Gpio.dll - True - - - packages\nanoFramework.System.Device.Spi.1.3.37\lib\System.Device.Spi.dll - True - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - + + + + + $(MSBuildExtensionsPath)\nanoFramework\v1.0\ + + + + Debug + AnyCPU + {11A8DD76-328B-46DF-9F39-F559912D0360};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 3d8ca135-f17d-4c14-be85-bab9ddb266de + Library + Properties + 512 + Iot.Device.EPaper + Iot.Device.ePaper + v1.0 + bin\$(Configuration)\Iot.Device.ePaper.xml + false + true + true + + + true + + + ..\key.snk + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + packages\nanoFramework.CoreLibrary.1.14.2\lib\mscorlib.dll + True + + + packages\nanoFramework.Graphics.Core.1.1.37\lib\nanoFramework.Graphics.Core.dll + True + + + packages\nanoFramework.Runtime.Events.1.11.6\lib\nanoFramework.Runtime.Events.dll + True + + + packages\nanoFramework.System.Device.Gpio.1.1.28\lib\System.Device.Gpio.dll + True + + + packages\nanoFramework.System.Device.Spi.1.3.37\lib\System.Device.Spi.dll + True + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file From 4ba4eba34c64308d818c258ba9b42efcf7c05004 Mon Sep 17 00:00:00 2001 From: xas Date: Tue, 24 Oct 2023 09:52:40 +0200 Subject: [PATCH 02/12] feat: add GDEW0154x e-paper driver * typo --- devices/ePaper/Drivers/Gdew0154x/Command.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devices/ePaper/Drivers/Gdew0154x/Command.cs b/devices/ePaper/Drivers/Gdew0154x/Command.cs index b6d0ad1c86..ffcdbfe397 100644 --- a/devices/ePaper/Drivers/Gdew0154x/Command.cs +++ b/devices/ePaper/Drivers/Gdew0154x/Command.cs @@ -103,7 +103,7 @@ public enum Command : byte /// VRES[8:0]: Vertical Display Resolution (second and third data) /// Active channel calculation: /// - GD: First active gate = G0(Fixed); LAST active gate = first active + VRES[8:0] – 1 - /// - SD: First active source = S0(Fixed); LAST active source = first active+HRES[7:3]*8–1 + /// - SD: First active source = S0(Fixed); LAST active source = first active+HRES[7:3] * 8 – 1. /// ResolutionSetting = 0x61, @@ -116,7 +116,7 @@ public enum Command : byte /// the power saving mechanism will be activated. /// The active period width is defined by the following two parameters. /// - VCOM_W[3:0]: VCOM power saving width (unit = line period) - /// - SD_W[3:0]: Source power saving width (unit = 660nS) + /// - SD_W[3:0]: Source power saving width (unit = 660nS). /// PowerSaving = 0xe3 } From 8d531665b1530cd8eb32814b914a44931a92904c Mon Sep 17 00:00:00 2001 From: xas Date: Thu, 26 Oct 2023 11:05:38 +0200 Subject: [PATCH 03/12] fix: reviews * add "shouldDispose" property * fix data sending protocol * clean up time delays * improve data write buffering * use of SpinWait for short time * add a time limit to "WaitReady" to avoid infinite wait * add a deep sleep power state --- .../ePaper/Drivers/Gdew0154x/Gdew0154m09.cs | 4 +- devices/ePaper/Drivers/Gdew0154x/Gdew0154x.cs | 119 +++++++++++------- devices/ePaper/Drivers/IePaperDisplay.cs | 7 +- devices/ePaper/Drivers/Ssd168x/Ssd168x.cs | 15 ++- devices/ePaper/Enums/PowerState.cs | 5 + 5 files changed, 95 insertions(+), 55 deletions(-) diff --git a/devices/ePaper/Drivers/Gdew0154x/Gdew0154m09.cs b/devices/ePaper/Drivers/Gdew0154x/Gdew0154m09.cs index a03e31e235..d3a02acae5 100644 --- a/devices/ePaper/Drivers/Gdew0154x/Gdew0154m09.cs +++ b/devices/ePaper/Drivers/Gdew0154x/Gdew0154m09.cs @@ -21,6 +21,7 @@ public class Gdew0154m09 : Gdew0154x /// The data/command GPIO pin. /// The to use when initializing the pins. /// Page the frame buffer and all operations to use less memory. + /// True to dispose the Gpio Controller. /// is null. /// Display width and height can't be less than 0 or greater than 200. /// @@ -34,7 +35,8 @@ public Gdew0154m09( int busyPin, int dataCommandPin, GpioController gpioController = null, - bool enableFramePaging = false) : base(spiDevice, resetPin, busyPin, dataCommandPin, 200, 200, gpioController, enableFramePaging) + bool enableFramePaging = false, + bool shouldDispose = true) : base(spiDevice, resetPin, busyPin, dataCommandPin, 200, 200, gpioController, enableFramePaging, shouldDispose) { } diff --git a/devices/ePaper/Drivers/Gdew0154x/Gdew0154x.cs b/devices/ePaper/Drivers/Gdew0154x/Gdew0154x.cs index 95d86a259f..fa24fb7797 100644 --- a/devices/ePaper/Drivers/Gdew0154x/Gdew0154x.cs +++ b/devices/ePaper/Drivers/Gdew0154x/Gdew0154x.cs @@ -17,14 +17,17 @@ namespace Iot.Device.EPaper.Drivers.GDEW0154x /// public abstract class Gdew0154x : IEPaperDisplay { + private readonly int _maxWaitingTime = 500; + private readonly bool _shouldDispose; + private readonly byte[] _whiteBuffer; + private bool _disposed; + private SpiDevice _spiDevice; private GpioController _gpioController; private GpioPin _resetPin; private GpioPin _busyPin; private GpioPin _dataCommandPin; - private bool _disposed; - /// /// The max supported clock frequency for the GDEW0154x controller. 10MHz. /// @@ -46,6 +49,7 @@ public abstract class Gdew0154x : IEPaperDisplay /// The height of the display. /// The to use when initializing the pins. /// Page the frame buffer and all operations to use less memory. + /// True to dispose the Gpio Controller. /// is null. /// Display width and height can't be less than 0 or greater than 200. protected Gdew0154x( @@ -56,10 +60,12 @@ protected Gdew0154x( int width, int height, GpioController gpioController, - bool enableFramePaging = false) + bool enableFramePaging = false, + bool shouldDispose = true) { _spiDevice = spiDevice ?? throw new ArgumentNullException(nameof(spiDevice)); - _gpioController = gpioController ?? throw new ArgumentNullException(nameof(gpioController)); + _gpioController = gpioController ?? new GpioController(); + _shouldDispose = shouldDispose || gpioController is null; // setup the gpio pins _resetPin = resetPin >= 0 ? _gpioController.OpenPin(resetPin, PinMode.Output) : null; @@ -70,6 +76,12 @@ protected Gdew0154x( Height = height; PagedFrameDrawEnabled = enableFramePaging; + _whiteBuffer = new byte[Width * Height]; + for (int i = 0; i < _whiteBuffer.Length; i++) + { + _whiteBuffer[i] = 0xff; + } + PowerState = PowerState.Unknown; InitializeFrameBuffer(width, height, enableFramePaging); @@ -209,12 +221,13 @@ public virtual void Clear(bool triggerPageRefresh = false) /// The buffer array to draw. public void DirectDrawBuffer(params byte[] buffer) { + SendCommand((byte)Command.DataStartTransmission1); + SendData(_whiteBuffer); SendCommand((byte)Command.DataStartTransmission2); SendData(buffer); WaitMs(5); - SendCommand((byte)Command.DisplayRefresh); - WaitMs(500); + PerformFullRefresh(); } /// @@ -368,29 +381,30 @@ protected virtual void HardwareReset() WaitMs(10); _resetPin.Write(PinValue.High); - WaitMs(100); + WaitMs(10); _resetPin.Write(PinValue.Low); - WaitMs(100); + WaitMs(10); _resetPin.Write(PinValue.High); + + // as per samples from screen manufacturer, wait finally 100ms for reset IC + select BUS WaitMs(100); } /// /// Perform the required initialization steps to set up the display. /// - /// Unable to initialize the display until it has been powered on. Call PowerOn() first. public virtual void Initialize() { - if (PowerState != PowerState.PoweredOn) + if (PowerState == PowerState.DeepSleep) { - throw new InvalidOperationException(); + // When in deep sleep mode, hardware init is required to set the screen on stand-by. + HardwareReset(); } SendCommand((byte)Command.PanelSetting); - SendData(0xdf); - SendData(0x0e); + SendData(0xdf, 0x0e); // FITIinternal code SendCommand(0x4d); @@ -409,9 +423,7 @@ public virtual void Initialize() SendData(0x0a); SendCommand((byte)Command.ResolutionSetting); - SendData(0xc8); - SendData(0x00); - SendData(0xc8); + SendData(0xc8, 0x00, 0xc8); SendCommand((byte)Command.TCONSetting); SendData(0x00); @@ -421,7 +433,15 @@ public virtual void Initialize() SendCommand((byte)Command.PowerOn); - WaitReady(); + if (WaitReady(_maxWaitingTime)) + { + PowerState = PowerState.PoweredOn; + } + else + { + // Set to deep sleep to force an hardware reset on next request + PowerState = PowerState.DeepSleep; + } } /// @@ -455,9 +475,12 @@ public virtual bool NextFramePage() /// public virtual void PerformFullRefresh() { - WaitMs(10); SendCommand((byte)Command.DisplayRefresh); - WaitReady(); + + // as per samples from screen manufacturer, refresh wait should be at least 200µs + // using a spin wait of 5ms, any value should be ok + // and use a large waiting time in case of something unexpected happens. + WaitReady(_maxWaitingTime); } /// @@ -476,15 +499,21 @@ public virtual void PowerDown(SleepMode sleepMode = SleepMode.Normal) SendCommand(0x50); SendData(0x07); SendCommand((byte)Command.PowerOff); - WaitReady(); + WaitReady(_maxWaitingTime); + + // as per samples from screen manufacturer, power off request a time delay of 1s before continue. + WaitMs(1000); if (sleepMode == SleepMode.DeepSleepMode) { SendCommand((byte)Command.DeepSleepMode); SendData(0xa5); + PowerState = PowerState.DeepSleep; + } + else + { + PowerState = PowerState.PoweredOff; } - - PowerState = PowerState.PoweredOff; } /// @@ -492,12 +521,16 @@ public virtual void PowerDown(SleepMode sleepMode = SleepMode.Normal) /// public virtual void PowerOn() { - HardwareReset(); - WaitMs(1000); + if (PowerState == PowerState.DeepSleep) + { + // When in deep sleep mode, hardware init is required to set the screen on stand-by. + HardwareReset(); + } + SendCommand(0x50); SendData(0xd7); SendCommand((byte)Command.PowerOn); - WaitReady(); + WaitReady(_maxWaitingTime); PowerState = PowerState.PoweredOn; } @@ -508,11 +541,8 @@ public virtual void SendCommand(params byte[] command) // make sure we are setting data/command pin to low (command mode) _dataCommandPin.Write(PinValue.Low); - foreach (var b in command) - { - // write the command byte to the display controller - _spiDevice.WriteByte(b); - } + // write the command byte to the display controller + _spiDevice.Write(command); } /// @@ -521,10 +551,7 @@ public virtual void SendData(params byte[] data) // set the data/command pin to high to indicate to the display we will be sending data _dataCommandPin.Write(PinValue.High); - foreach (var @byte in data) - { - _spiDevice.WriteByte(@byte); - } + _spiDevice.Write(data); // go back to low (command mode) _dataCommandPin.Write(PinValue.Low); @@ -563,7 +590,7 @@ protected virtual void SoftwareReset() { SendCommand(0x04); - WaitReady(PinValue.High); + WaitReady(_maxWaitingTime); WaitMs(10); } @@ -577,21 +604,16 @@ protected virtual void WaitMs(int milliseconds) } /// - public virtual void WaitReady(PinValue pinValue) + public virtual bool WaitReady(int waitingTime) { - while (_busyPin.Read() != pinValue) + int currentWait = 0; + while (currentWait < waitingTime && _busyPin.Read() == PinValue.Low) { - WaitMs(5); + SpinWait.SpinUntil(5); + currentWait += 5; } - } - /// - public virtual void WaitReady() - { - while (_busyPin.Read() == PinValue.Low) - { - WaitMs(5); - } + return currentWait >= waitingTime; } #region IDisposable @@ -611,7 +633,10 @@ private void Dispose(bool disposing) _dataCommandPin?.Dispose(); _dataCommandPin = null; - _gpioController = null; + if (_shouldDispose) + { + _gpioController?.Dispose(); + } _spiDevice = null; _gpioController = null; diff --git a/devices/ePaper/Drivers/IePaperDisplay.cs b/devices/ePaper/Drivers/IePaperDisplay.cs index 00e45f1dfc..c5d06e1265 100644 --- a/devices/ePaper/Drivers/IePaperDisplay.cs +++ b/devices/ePaper/Drivers/IePaperDisplay.cs @@ -92,7 +92,10 @@ public interface IEPaperDisplay : IDisposable /// /// Blocks the current thread until the display is in idle mode again. /// - void WaitReady(); + /// The maximum time to wait in ms before exiting the method. -1 to wait infinitely. + /// True if it returns before the time to wait, false otherwise. + /// Be careful if waiting infinitely as the thread will be block without any possibilities to exit. + bool WaitReady(int waitingTime); /// /// Begins a frame draw operation with frame paging support. @@ -109,7 +112,7 @@ public interface IEPaperDisplay : IDisposable /// /// Moves the current buffers to the next frame page and returns true if successful. /// - /// True if the next frame page is available and the internal buffers have moved to it, otherwise; false. + /// True if the next frame page is available and the internal buffers have moved to it, false otherwise. bool NextFramePage(); /// diff --git a/devices/ePaper/Drivers/Ssd168x/Ssd168x.cs b/devices/ePaper/Drivers/Ssd168x/Ssd168x.cs index 47802c7641..fc1285b275 100644 --- a/devices/ePaper/Drivers/Ssd168x/Ssd168x.cs +++ b/devices/ePaper/Drivers/Ssd168x/Ssd168x.cs @@ -18,6 +18,7 @@ namespace Iot.Device.EPaper.Drivers.Ssd168x /// public abstract class Ssd168x : IEPaperDisplay { + private readonly int _maxWaitingTime = 500; private SpiDevice _spiDevice; private GpioController _gpioController; private GpioPin _resetPin; @@ -507,7 +508,7 @@ public virtual void PerformFullRefresh() SendData((byte)RefreshMode.FullRefresh); // Display Mode 1 (Full Refresh) SendCommand((byte)Command.MasterActivation); - WaitReady(); + WaitReady(_maxWaitingTime); } /// @@ -520,7 +521,7 @@ public virtual void PerformPartialRefresh() SendData((byte)RefreshMode.PartialRefresh); // Display Mode 2 (Partial Refresh) SendCommand((byte)Command.MasterActivation); - WaitReady(); + WaitReady(_maxWaitingTime); } /// @@ -613,7 +614,7 @@ protected virtual void SoftwareReset() { SendCommand((byte)Command.SoftwareReset); - WaitReady(); + WaitReady(_maxWaitingTime); WaitMs(10); } @@ -627,12 +628,16 @@ protected virtual void WaitMs(int milliseconds) } /// - public virtual void WaitReady() + public virtual bool WaitReady(int waitingTime) { - while (_busyPin.Read() == PinValue.High) + int currentWait = 0; + while (currentWait < waitingTime && _busyPin.Read() == PinValue.High) { WaitMs(5); + currentWait += 5; } + + return currentWait >= waitingTime; } #region IDisposable diff --git a/devices/ePaper/Enums/PowerState.cs b/devices/ePaper/Enums/PowerState.cs index e484aee3c0..561f51b7d9 100644 --- a/devices/ePaper/Enums/PowerState.cs +++ b/devices/ePaper/Enums/PowerState.cs @@ -22,5 +22,10 @@ public enum PowerState /// The display is powered off (sleeping) and depending on the sleep mode it may or may not allow some functions. /// PoweredOff, + + /// + /// The display is powered off and in deep sleep mode. + /// + DeepSleep, } } From f4d250af5729f422e49c84617a5aaa5af8724384 Mon Sep 17 00:00:00 2001 From: xas Date: Mon, 6 Nov 2023 16:01:54 +0100 Subject: [PATCH 04/12] fix: reviews * namespace driver renaming * fix missing command map * allow gpioController parameter to be null --- .../Drivers/{Gdew0154x => JD796xx}/Command.cs | 10 +++++- .../{Gdew0154x => JD796xx}/Gdew0154m09.cs | 4 +-- .../Gdew0154x.cs => JD796xx/JD79653A.cs} | 32 +++++++++---------- .../{Gdew0154x => JD796xx}/SleepMode.cs | 2 +- devices/ePaper/ePaper.nfproj | 8 ++--- 5 files changed, 32 insertions(+), 24 deletions(-) rename devices/ePaper/Drivers/{Gdew0154x => JD796xx}/Command.cs (94%) rename devices/ePaper/Drivers/{Gdew0154x => JD796xx}/Gdew0154m09.cs (96%) rename devices/ePaper/Drivers/{Gdew0154x/Gdew0154x.cs => JD796xx/JD79653A.cs} (95%) rename devices/ePaper/Drivers/{Gdew0154x => JD796xx}/SleepMode.cs (94%) diff --git a/devices/ePaper/Drivers/Gdew0154x/Command.cs b/devices/ePaper/Drivers/JD796xx/Command.cs similarity index 94% rename from devices/ePaper/Drivers/Gdew0154x/Command.cs rename to devices/ePaper/Drivers/JD796xx/Command.cs index ffcdbfe397..d5f3253805 100644 --- a/devices/ePaper/Drivers/Gdew0154x/Command.cs +++ b/devices/ePaper/Drivers/JD796xx/Command.cs @@ -1,7 +1,7 @@ // Copyright (c) 2022 The nanoFramework project contributors // See LICENSE file in the project root for full license information. -namespace Iot.Device.EPaper.Drivers.GDEW0154x +namespace Iot.Device.EPaper.Drivers.JD796xx { /// /// Commands supported by SSD1681. @@ -86,6 +86,14 @@ public enum Command : byte /// DataStartTransmission2 = 0x13, + /// + /// VCOM and DATA interval setting. + /// + /// + /// Refer to the datasheet for detailed information about the command and its parameters. + /// + IntervalSetting = 0x50, + /// /// Defines non-overlap period of Gate and Source. /// diff --git a/devices/ePaper/Drivers/Gdew0154x/Gdew0154m09.cs b/devices/ePaper/Drivers/JD796xx/Gdew0154m09.cs similarity index 96% rename from devices/ePaper/Drivers/Gdew0154x/Gdew0154m09.cs rename to devices/ePaper/Drivers/JD796xx/Gdew0154m09.cs index d3a02acae5..63980308c7 100644 --- a/devices/ePaper/Drivers/Gdew0154x/Gdew0154m09.cs +++ b/devices/ePaper/Drivers/JD796xx/Gdew0154m09.cs @@ -5,12 +5,12 @@ using System.Device.Gpio; using System.Device.Spi; -namespace Iot.Device.EPaper.Drivers.GDEW0154x +namespace Iot.Device.EPaper.Drivers.JD796xx { /// /// A driver class for the GDEW0154M09 display controller. /// - public class Gdew0154m09 : Gdew0154x + public class Gdew0154m09 : JD79653A { /// /// Initializes a new instance of the class. diff --git a/devices/ePaper/Drivers/Gdew0154x/Gdew0154x.cs b/devices/ePaper/Drivers/JD796xx/JD79653A.cs similarity index 95% rename from devices/ePaper/Drivers/Gdew0154x/Gdew0154x.cs rename to devices/ePaper/Drivers/JD796xx/JD79653A.cs index fa24fb7797..604dfe721d 100644 --- a/devices/ePaper/Drivers/Gdew0154x/Gdew0154x.cs +++ b/devices/ePaper/Drivers/JD796xx/JD79653A.cs @@ -10,12 +10,12 @@ using Iot.Device.EPaper.Enums; using nanoFramework.UI; -namespace Iot.Device.EPaper.Drivers.GDEW0154x +namespace Iot.Device.EPaper.Drivers.JD796xx { /// /// Base class for GDEW0154-based ePaper devices. /// - public abstract class Gdew0154x : IEPaperDisplay + public abstract class JD79653A : IEPaperDisplay { private readonly int _maxWaitingTime = 500; private readonly bool _shouldDispose; @@ -29,17 +29,17 @@ public abstract class Gdew0154x : IEPaperDisplay private GpioPin _dataCommandPin; /// - /// The max supported clock frequency for the GDEW0154x controller. 10MHz. + /// The max supported clock frequency for the JD79653A controller. 10MHz. /// public const int SpiClockFrequency = 10_000_000; /// - /// The supported by the GDEW0154x controller. + /// The supported by the JD79653A controller. /// public const SpiMode SpiMode = System.Device.Spi.SpiMode.Mode0; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The communication channel to the SSD1681-based dispay. /// The reset GPIO pin. Passing an invalid pin number such as -1 will prevent this driver from opening the pin. Caller should handle hardware resets. @@ -52,14 +52,14 @@ public abstract class Gdew0154x : IEPaperDisplay /// True to dispose the Gpio Controller. /// is null. /// Display width and height can't be less than 0 or greater than 200. - protected Gdew0154x( + protected JD79653A( SpiDevice spiDevice, int resetPin, int busyPin, int dataCommandPin, int width, int height, - GpioController gpioController, + GpioController gpioController = null, bool enableFramePaging = false, bool shouldDispose = true) { @@ -148,7 +148,7 @@ protected set protected abstract int PagesPerFrame { get; } /// - /// Gets or sets the used internally by devices to represents the frame. + /// Gets or sets the used internally by devices to represents the frame. /// protected FrameBuffer1BitPerPixel FrameBuffer1bpp { get; set; } @@ -237,9 +237,9 @@ public void DirectDrawBuffer(params byte[] buffer) /// The Y Position. /// True to invert the pixel from white to black. /// - /// The GDEW0154x comes with 2 RAMs: a Black and White RAM and a Red RAM. + /// The JD79653A comes with 2 RAMs: a Black and White RAM and a Red RAM. /// Writing to the B/W RAM draws B/W pixels on the panel. While writing to the Red RAM draws red pixels on the panel (if the panel supports red). - /// However, the GDEW0154x doesn't support specifying the color level (no grayscaling), therefore the way the buffer is selected + /// However, the JD79653A doesn't support specifying the color level (no grayscaling), therefore the way the buffer is selected /// is by performing a simple binary check: /// if R >= 128 and G == 0 and B == 0 then write a red pixel to the Red Buffer/RAM /// if R == 0 and G == 0 and B == 0 then write a black pixel to B/W Buffer/RAM @@ -252,9 +252,9 @@ public void DrawPixel(int x, int y, bool inverted) /// /// - /// The GDEW0154x comes with 2 RAMs: a Black and White RAM and a Red RAM. + /// The JD79653A comes with 2 RAMs: a Black and White RAM and a Red RAM. /// Writing to the B/W RAM draws B/W pixels on the panel. While writing to the Red RAM draws red pixels on the panel (if the panel supports red). - /// However, the GDEW0154x doesn't support specifying the color level (no grayscaling), therefore the way the buffer is selected + /// However, the JD79653A doesn't support specifying the color level (no grayscaling), therefore the way the buffer is selected /// is by performing a simple binary check: /// if R >= 128 and G == 0 and B == 0 then write a red pixel to the Red Buffer/RAM /// if R == 0 and G == 0 and B == 0 then write a black pixel to B/W Buffer/RAM @@ -276,9 +276,9 @@ public virtual void DrawPixel(int x, int y, Color color) && frameByteIndex < CurrentFrameBufferPageUpperBound) { /* - * Lookup Table for colors on GDEW0154x + * Lookup Table for colors on JD79653A * - * LUT for Black and White ePaper display with GDEW0154x + * LUT for Black and White ePaper display with JD79653A * | | | * | Data B/W RAM | Result Pixel Color | * |----------------|--------------------| @@ -496,7 +496,7 @@ public virtual void PerformPartialRefresh() /// The sleep mode to use when powering down the display. public virtual void PowerDown(SleepMode sleepMode = SleepMode.Normal) { - SendCommand(0x50); + SendCommand((byte)Command.IntervalSetting); SendData(0x07); SendCommand((byte)Command.PowerOff); WaitReady(_maxWaitingTime); @@ -527,7 +527,7 @@ public virtual void PowerOn() HardwareReset(); } - SendCommand(0x50); + SendCommand((byte)Command.IntervalSetting); SendData(0xd7); SendCommand((byte)Command.PowerOn); WaitReady(_maxWaitingTime); diff --git a/devices/ePaper/Drivers/Gdew0154x/SleepMode.cs b/devices/ePaper/Drivers/JD796xx/SleepMode.cs similarity index 94% rename from devices/ePaper/Drivers/Gdew0154x/SleepMode.cs rename to devices/ePaper/Drivers/JD796xx/SleepMode.cs index 4095723c89..f24bfaa9fe 100644 --- a/devices/ePaper/Drivers/Gdew0154x/SleepMode.cs +++ b/devices/ePaper/Drivers/JD796xx/SleepMode.cs @@ -1,7 +1,7 @@ // Copyright (c) 2022 The nanoFramework project contributors // See LICENSE file in the project root for full license information. -namespace Iot.Device.EPaper.Drivers.GDEW0154x +namespace Iot.Device.EPaper.Drivers.JD796xx { /// /// SSD1681 Supported Sleep Modes. diff --git a/devices/ePaper/ePaper.nfproj b/devices/ePaper/ePaper.nfproj index d4a6e7ec84..e9ebc870ee 100644 --- a/devices/ePaper/ePaper.nfproj +++ b/devices/ePaper/ePaper.nfproj @@ -36,10 +36,10 @@ - - - - + + + + From caf865ef71f5d058ddb6b3af1fcd255cdf5dbd01 Mon Sep 17 00:00:00 2001 From: xas Date: Tue, 7 Nov 2023 09:34:40 +0100 Subject: [PATCH 05/12] feat: PerformRefresh functions returns boolean for state refresh --- devices/ePaper/Drivers/IePaperDisplay.cs | 6 ++++-- devices/ePaper/Drivers/JD796xx/JD79653A.cs | 14 ++++++++------ devices/ePaper/Drivers/Ssd168x/Ssd168x.cs | 8 ++++---- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/devices/ePaper/Drivers/IePaperDisplay.cs b/devices/ePaper/Drivers/IePaperDisplay.cs index c5d06e1265..20f512dcb3 100644 --- a/devices/ePaper/Drivers/IePaperDisplay.cs +++ b/devices/ePaper/Drivers/IePaperDisplay.cs @@ -55,12 +55,14 @@ public interface IEPaperDisplay : IDisposable /// /// Initiates the full refresh sequence on the display. /// - void PerformFullRefresh(); + /// True if the screen sucessfully refresh. False otherwise (still busy after a predefined waiting time). + bool PerformFullRefresh(); /// /// Initiates the partial refresh sequence on the display if the panel supports it. /// - void PerformPartialRefresh(); + /// True if the screen sucessfully refresh. False otherwise (still busy after a predefined waiting time). + bool PerformPartialRefresh(); /// /// Sets the drawing position on the display. diff --git a/devices/ePaper/Drivers/JD796xx/JD79653A.cs b/devices/ePaper/Drivers/JD796xx/JD79653A.cs index 604dfe721d..b66511894e 100644 --- a/devices/ePaper/Drivers/JD796xx/JD79653A.cs +++ b/devices/ePaper/Drivers/JD796xx/JD79653A.cs @@ -473,21 +473,21 @@ public virtual bool NextFramePage() } /// - public virtual void PerformFullRefresh() + public virtual bool PerformFullRefresh() { SendCommand((byte)Command.DisplayRefresh); // as per samples from screen manufacturer, refresh wait should be at least 200µs // using a spin wait of 5ms, any value should be ok // and use a large waiting time in case of something unexpected happens. - WaitReady(_maxWaitingTime); + return WaitReady(_maxWaitingTime); } /// - public virtual void PerformPartialRefresh() + public virtual bool PerformPartialRefresh() { // No partial refresh implemented - PerformFullRefresh(); + return PerformFullRefresh(); } /// @@ -530,9 +530,11 @@ public virtual void PowerOn() SendCommand((byte)Command.IntervalSetting); SendData(0xd7); SendCommand((byte)Command.PowerOn); - WaitReady(_maxWaitingTime); - PowerState = PowerState.PoweredOn; + if (WaitReady(_maxWaitingTime)) + { + PowerState = PowerState.PoweredOn; + } } /// diff --git a/devices/ePaper/Drivers/Ssd168x/Ssd168x.cs b/devices/ePaper/Drivers/Ssd168x/Ssd168x.cs index fc1285b275..8ff7938a3d 100644 --- a/devices/ePaper/Drivers/Ssd168x/Ssd168x.cs +++ b/devices/ePaper/Drivers/Ssd168x/Ssd168x.cs @@ -499,7 +499,7 @@ public virtual bool NextFramePage() } /// - public virtual void PerformFullRefresh() + public virtual bool PerformFullRefresh() { SendCommand((byte)Command.BoosterSoftStartControl); SendData(0x8b, 0x9c, 0x96, 0x0f); @@ -508,11 +508,11 @@ public virtual void PerformFullRefresh() SendData((byte)RefreshMode.FullRefresh); // Display Mode 1 (Full Refresh) SendCommand((byte)Command.MasterActivation); - WaitReady(_maxWaitingTime); + return WaitReady(_maxWaitingTime); } /// - public virtual void PerformPartialRefresh() + public virtual bool PerformPartialRefresh() { SendCommand((byte)Command.BoosterSoftStartControl); SendData(0x8b, 0x9c, 0x96, 0x0f); @@ -521,7 +521,7 @@ public virtual void PerformPartialRefresh() SendData((byte)RefreshMode.PartialRefresh); // Display Mode 2 (Partial Refresh) SendCommand((byte)Command.MasterActivation); - WaitReady(_maxWaitingTime); + return WaitReady(_maxWaitingTime); } /// From a7e0cfb9200972bd3808fcdd080d021a43835d4c Mon Sep 17 00:00:00 2001 From: xas Date: Tue, 7 Nov 2023 09:45:26 +0100 Subject: [PATCH 06/12] fix: source for screen initialization sequence --- devices/ePaper/Drivers/JD796xx/JD79653A.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/devices/ePaper/Drivers/JD796xx/JD79653A.cs b/devices/ePaper/Drivers/JD796xx/JD79653A.cs index b66511894e..27b6f844ba 100644 --- a/devices/ePaper/Drivers/JD796xx/JD79653A.cs +++ b/devices/ePaper/Drivers/JD796xx/JD79653A.cs @@ -395,6 +395,7 @@ protected virtual void HardwareReset() /// /// Perform the required initialization steps to set up the display. /// + /// Sequence order can be found at https://github.com/m5stack/M5Core-Ink/blob/master/src/utility/WFT0154CZB3_INIT.h public virtual void Initialize() { if (PowerState == PowerState.DeepSleep) From 4f3ca61f984bb5bc68cc190b080952b4e8573b09 Mon Sep 17 00:00:00 2001 From: xas Date: Mon, 20 Nov 2023 09:02:13 +0100 Subject: [PATCH 07/12] fix: conflicts --- devices/ePaper/ePaper.nfproj | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/devices/ePaper/ePaper.nfproj b/devices/ePaper/ePaper.nfproj index e9ebc870ee..7fda5c4141 100644 --- a/devices/ePaper/ePaper.nfproj +++ b/devices/ePaper/ePaper.nfproj @@ -56,24 +56,24 @@ - - packages\nanoFramework.CoreLibrary.1.14.2\lib\mscorlib.dll + + packages\nanoFramework.CoreLibrary.1.15.5\lib\mscorlib.dll True - - packages\nanoFramework.Graphics.Core.1.1.37\lib\nanoFramework.Graphics.Core.dll + + packages\nanoFramework.Graphics.Core.1.2.4\lib\nanoFramework.Graphics.Core.dll True - - packages\nanoFramework.Runtime.Events.1.11.6\lib\nanoFramework.Runtime.Events.dll + + packages\nanoFramework.Runtime.Events.1.11.15\lib\nanoFramework.Runtime.Events.dll True - - packages\nanoFramework.System.Device.Gpio.1.1.28\lib\System.Device.Gpio.dll + + packages\nanoFramework.System.Device.Gpio.1.1.38\lib\System.Device.Gpio.dll True - - packages\nanoFramework.System.Device.Spi.1.3.37\lib\System.Device.Spi.dll + + packages\nanoFramework.System.Device.Spi.1.3.43\lib\System.Device.Spi.dll True From ad7f8c28ec4524b24f23706634b3c7b9d2f21305 Mon Sep 17 00:00:00 2001 From: xas Date: Tue, 28 Nov 2023 10:26:25 +0100 Subject: [PATCH 08/12] fix: Jd796 typo --- devices/ePaper/Drivers/JD796xx/Command.cs | 2 +- devices/ePaper/Drivers/JD796xx/Gdew0154m09.cs | 4 +-- devices/ePaper/Drivers/JD796xx/JD79653A.cs | 26 +++++++++---------- devices/ePaper/Drivers/JD796xx/SleepMode.cs | 2 +- devices/ePaper/ePaper.nfproj | 2 +- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/devices/ePaper/Drivers/JD796xx/Command.cs b/devices/ePaper/Drivers/JD796xx/Command.cs index d5f3253805..c4684cc7c5 100644 --- a/devices/ePaper/Drivers/JD796xx/Command.cs +++ b/devices/ePaper/Drivers/JD796xx/Command.cs @@ -1,7 +1,7 @@ // Copyright (c) 2022 The nanoFramework project contributors // See LICENSE file in the project root for full license information. -namespace Iot.Device.EPaper.Drivers.JD796xx +namespace Iot.Device.EPaper.Drivers.Jd796xx { /// /// Commands supported by SSD1681. diff --git a/devices/ePaper/Drivers/JD796xx/Gdew0154m09.cs b/devices/ePaper/Drivers/JD796xx/Gdew0154m09.cs index 63980308c7..09f07d57aa 100644 --- a/devices/ePaper/Drivers/JD796xx/Gdew0154m09.cs +++ b/devices/ePaper/Drivers/JD796xx/Gdew0154m09.cs @@ -5,12 +5,12 @@ using System.Device.Gpio; using System.Device.Spi; -namespace Iot.Device.EPaper.Drivers.JD796xx +namespace Iot.Device.EPaper.Drivers.Jd796xx { /// /// A driver class for the GDEW0154M09 display controller. /// - public class Gdew0154m09 : JD79653A + public class Gdew0154m09 : Jd79653A { /// /// Initializes a new instance of the class. diff --git a/devices/ePaper/Drivers/JD796xx/JD79653A.cs b/devices/ePaper/Drivers/JD796xx/JD79653A.cs index 27b6f844ba..df73f6a0bb 100644 --- a/devices/ePaper/Drivers/JD796xx/JD79653A.cs +++ b/devices/ePaper/Drivers/JD796xx/JD79653A.cs @@ -10,12 +10,12 @@ using Iot.Device.EPaper.Enums; using nanoFramework.UI; -namespace Iot.Device.EPaper.Drivers.JD796xx +namespace Iot.Device.EPaper.Drivers.Jd796xx { /// /// Base class for GDEW0154-based ePaper devices. /// - public abstract class JD79653A : IEPaperDisplay + public abstract class Jd79653A : IEPaperDisplay { private readonly int _maxWaitingTime = 500; private readonly bool _shouldDispose; @@ -29,17 +29,17 @@ public abstract class JD79653A : IEPaperDisplay private GpioPin _dataCommandPin; /// - /// The max supported clock frequency for the JD79653A controller. 10MHz. + /// The max supported clock frequency for the Jd79653A controller. 10MHz. /// public const int SpiClockFrequency = 10_000_000; /// - /// The supported by the JD79653A controller. + /// The supported by the Jd79653A controller. /// public const SpiMode SpiMode = System.Device.Spi.SpiMode.Mode0; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The communication channel to the SSD1681-based dispay. /// The reset GPIO pin. Passing an invalid pin number such as -1 will prevent this driver from opening the pin. Caller should handle hardware resets. @@ -52,7 +52,7 @@ public abstract class JD79653A : IEPaperDisplay /// True to dispose the Gpio Controller. /// is null. /// Display width and height can't be less than 0 or greater than 200. - protected JD79653A( + protected Jd79653A( SpiDevice spiDevice, int resetPin, int busyPin, @@ -148,7 +148,7 @@ protected set protected abstract int PagesPerFrame { get; } /// - /// Gets or sets the used internally by devices to represents the frame. + /// Gets or sets the used internally by devices to represents the frame. /// protected FrameBuffer1BitPerPixel FrameBuffer1bpp { get; set; } @@ -237,9 +237,9 @@ public void DirectDrawBuffer(params byte[] buffer) /// The Y Position. /// True to invert the pixel from white to black. /// - /// The JD79653A comes with 2 RAMs: a Black and White RAM and a Red RAM. + /// The Jd79653A comes with 2 RAMs: a Black and White RAM and a Red RAM. /// Writing to the B/W RAM draws B/W pixels on the panel. While writing to the Red RAM draws red pixels on the panel (if the panel supports red). - /// However, the JD79653A doesn't support specifying the color level (no grayscaling), therefore the way the buffer is selected + /// However, the Jd79653A doesn't support specifying the color level (no grayscaling), therefore the way the buffer is selected /// is by performing a simple binary check: /// if R >= 128 and G == 0 and B == 0 then write a red pixel to the Red Buffer/RAM /// if R == 0 and G == 0 and B == 0 then write a black pixel to B/W Buffer/RAM @@ -252,9 +252,9 @@ public void DrawPixel(int x, int y, bool inverted) /// /// - /// The JD79653A comes with 2 RAMs: a Black and White RAM and a Red RAM. + /// The Jd79653A comes with 2 RAMs: a Black and White RAM and a Red RAM. /// Writing to the B/W RAM draws B/W pixels on the panel. While writing to the Red RAM draws red pixels on the panel (if the panel supports red). - /// However, the JD79653A doesn't support specifying the color level (no grayscaling), therefore the way the buffer is selected + /// However, the Jd79653A doesn't support specifying the color level (no grayscaling), therefore the way the buffer is selected /// is by performing a simple binary check: /// if R >= 128 and G == 0 and B == 0 then write a red pixel to the Red Buffer/RAM /// if R == 0 and G == 0 and B == 0 then write a black pixel to B/W Buffer/RAM @@ -276,9 +276,9 @@ public virtual void DrawPixel(int x, int y, Color color) && frameByteIndex < CurrentFrameBufferPageUpperBound) { /* - * Lookup Table for colors on JD79653A + * Lookup Table for colors on Jd79653A * - * LUT for Black and White ePaper display with JD79653A + * LUT for Black and White ePaper display with Jd79653A * | | | * | Data B/W RAM | Result Pixel Color | * |----------------|--------------------| diff --git a/devices/ePaper/Drivers/JD796xx/SleepMode.cs b/devices/ePaper/Drivers/JD796xx/SleepMode.cs index f24bfaa9fe..522ede3a75 100644 --- a/devices/ePaper/Drivers/JD796xx/SleepMode.cs +++ b/devices/ePaper/Drivers/JD796xx/SleepMode.cs @@ -1,7 +1,7 @@ // Copyright (c) 2022 The nanoFramework project contributors // See LICENSE file in the project root for full license information. -namespace Iot.Device.EPaper.Drivers.JD796xx +namespace Iot.Device.EPaper.Drivers.Jd796xx { /// /// SSD1681 Supported Sleep Modes. diff --git a/devices/ePaper/ePaper.nfproj b/devices/ePaper/ePaper.nfproj index 7fda5c4141..152c1b7829 100644 --- a/devices/ePaper/ePaper.nfproj +++ b/devices/ePaper/ePaper.nfproj @@ -38,7 +38,7 @@ - + From aa7b578cd7b0523b7348639044329b9d53c1cdb2 Mon Sep 17 00:00:00 2001 From: xas Date: Thu, 4 Jan 2024 16:47:50 +0100 Subject: [PATCH 09/12] fix: MR reviews for ePaper project --- .../ePaper/Buffers/FrameBuffer2BitPerPixel.cs | 37 ++++--- devices/ePaper/Buffers/FrameBufferBase.cs | 33 +++--- devices/ePaper/Buffers/IFrameBuffer.cs | 19 ++-- devices/ePaper/Drivers/IePaperDisplay.cs | 9 +- devices/ePaper/Drivers/JD796xx/JD79653A.cs | 23 ++-- devices/ePaper/Drivers/Ssd168x/Ssd168x.cs | 25 +++-- devices/ePaper/ePaper.nfproj | 3 + devices/ePaper/packages.config | 19 ++-- devices/ePaper/packages.lock.json | 102 +++++++++--------- 9 files changed, 146 insertions(+), 124 deletions(-) diff --git a/devices/ePaper/Buffers/FrameBuffer2BitPerPixel.cs b/devices/ePaper/Buffers/FrameBuffer2BitPerPixel.cs index 3ae546c114..e374111489 100644 --- a/devices/ePaper/Buffers/FrameBuffer2BitPerPixel.cs +++ b/devices/ePaper/Buffers/FrameBuffer2BitPerPixel.cs @@ -5,7 +5,6 @@ using System.Drawing; using Iot.Device.EPaper.Enums; -using nanoFramework.UI; namespace Iot.Device.EPaper.Buffers { @@ -15,7 +14,7 @@ namespace Iot.Device.EPaper.Buffers /// public sealed class FrameBuffer2BitPerPixel : IFrameBuffer { - private System.Drawing.Point _startPoint; + private Point _startPoint; /// > public int Height { get; } @@ -24,7 +23,7 @@ public sealed class FrameBuffer2BitPerPixel : IFrameBuffer public int Width { get; } /// - public System.Drawing.Point StartPoint + public Point StartPoint { get => _startPoint; set @@ -70,7 +69,7 @@ public byte this[int index] } /// - public byte this[System.Drawing.Point point] + public byte this[Point point] { get => throw new NotSupportedException(); set => throw new NotSupportedException(); @@ -124,11 +123,11 @@ public void Clear(Color color) /// > public void Fill(Color color) { - Fill(new System.Drawing.Point(0, 0), Width, Height, color); + Fill(new Point(0, 0), Width, Height, color); } /// - public void Fill(System.Drawing.Point start, int width, int height, Color color) + public void Fill(Point start, int width, int height, Color color) { if (!IsPointWithinFrameBuffer(start)) { @@ -163,7 +162,7 @@ public void Fill(System.Drawing.Point start, int width, int height, Color color) /// into a struct. We don't know what color /// represents in the actual display hardware. /// - public Color GetPixel(System.Drawing.Point point) + public Color GetPixel(Point point) { throw new InvalidOperationException(); } @@ -173,8 +172,8 @@ public Color GetPixel(System.Drawing.Point point) /// /// The pixel position. /// True if pixel is a color other than black/white, otherwise; false. - /// Make sure to consider the result of as this is not enough on its own. - public bool IsColoredPixel(System.Drawing.Point point) + /// Make sure to consider the result of as this is not enough on its own. + public bool IsColoredPixel(Point point) { return ColorBuffer.GetPixel(point) == Color.White; // white indicates the pixel value is 1 (on) } @@ -184,7 +183,7 @@ public bool IsColoredPixel(System.Drawing.Point point) /// /// The pixel position. /// True if pixel is black color, otherwise; false. - public bool IsBlackPixel(System.Drawing.Point point) + public bool IsBlackPixel(Point point) { return IsColoredPixel(point) == false && BlackBuffer.GetPixel(point) == Color.Black; @@ -195,14 +194,14 @@ public bool IsBlackPixel(System.Drawing.Point point) /// /// The pixel position. /// True if pixel is white color, otherwise; false. - /// Make sure to consider the result of as this is not enough on its own. - public bool IsWhitePixel(System.Drawing.Point point) + /// Make sure to consider the result of as this is not enough on its own. + public bool IsWhitePixel(Point point) { return IsBlackPixel(point) == false; } /// - public void SetPixel(System.Drawing.Point point, Color pixelColor) + public void SetPixel(Point point, Color pixelColor) { if (!IsPointWithinFrameBuffer(point)) { @@ -231,17 +230,17 @@ public void SetPixel(System.Drawing.Point point, Color pixelColor) /// > public void WriteBuffer(IFrameBuffer buffer) { - WriteBuffer(buffer, new System.Drawing.Point(0, 0)); + WriteBuffer(buffer, new Point(0, 0)); } /// > - public void WriteBuffer(IFrameBuffer buffer, System.Drawing.Point destinationStart) + public void WriteBuffer(IFrameBuffer buffer, Point destinationStart) { - WriteBuffer(buffer, new System.Drawing.Point(0, 0), new System.Drawing.Point(buffer.Width, buffer.Height), destinationStart); + WriteBuffer(buffer, new Point(0, 0), new Point(buffer.Width, buffer.Height), destinationStart); } /// - public void WriteBuffer(IFrameBuffer buffer, System.Drawing.Point start, System.Drawing.Point end, System.Drawing.Point destinationStart) + public void WriteBuffer(IFrameBuffer buffer, Point start, Point end, Point destinationStart) { if (buffer is FrameBuffer1BitPerPixel buffer1bpp) { @@ -261,14 +260,14 @@ public void WriteBuffer(IFrameBuffer buffer, System.Drawing.Point start, System. } /// - public bool IsPointWithinFrameBuffer(System.Drawing.Point point) + public bool IsPointWithinFrameBuffer(Point point) { return point.X >= StartPoint.X && point.X < (StartPoint.X + Width) && point.Y >= StartPoint.Y && point.Y < (StartPoint.Y + Height); } /// > - public bool IsRangeWithinFrameBuffer(System.Drawing.Point start, System.Drawing.Point end) + public bool IsRangeWithinFrameBuffer(Point start, Point end) { return IsPointWithinFrameBuffer(start) && IsPointWithinFrameBuffer(end); } diff --git a/devices/ePaper/Buffers/FrameBufferBase.cs b/devices/ePaper/Buffers/FrameBufferBase.cs index 6ded2563a5..df8d14cd6f 100644 --- a/devices/ePaper/Buffers/FrameBufferBase.cs +++ b/devices/ePaper/Buffers/FrameBufferBase.cs @@ -5,7 +5,6 @@ using System.Drawing; using Iot.Device.EPaper.Enums; -using nanoFramework.UI; namespace Iot.Device.EPaper.Buffers { @@ -57,7 +56,7 @@ public virtual int BufferByteCount public abstract ColorFormat ColorFormat { get; } /// - public virtual System.Drawing.Point StartPoint { get; set; } + public virtual Point StartPoint { get; set; } /// public virtual int CurrentFramePage @@ -79,7 +78,7 @@ public virtual byte this[int index] } /// - public virtual byte this[System.Drawing.Point point] + public virtual byte this[Point point] { get => this[GetFrameBufferIndexForPoint(point)]; set @@ -134,17 +133,17 @@ public void Clear() /// > public void WriteBuffer(IFrameBuffer buffer) { - WriteBuffer(buffer, new System.Drawing.Point(0, 0), new System.Drawing.Point(buffer.Width, buffer.Height), new System.Drawing.Point(0, 0)); + WriteBuffer(buffer, new Point(0, 0), new Point(buffer.Width, buffer.Height), new Point(0, 0)); } /// > - public void WriteBuffer(IFrameBuffer buffer, System.Drawing.Point destinationStart) + public void WriteBuffer(IFrameBuffer buffer, Point destinationStart) { - WriteBuffer(buffer, new System.Drawing.Point(0, 0), new System.Drawing.Point(buffer.Width, buffer.Height), destinationStart); + WriteBuffer(buffer, new Point(0, 0), new Point(buffer.Width, buffer.Height), destinationStart); } /// > - public virtual void WriteBuffer(IFrameBuffer buffer, System.Drawing.Point start, System.Drawing.Point end, System.Drawing.Point destinationStart) + public virtual void WriteBuffer(IFrameBuffer buffer, Point start, Point end, Point destinationStart) { WriteBufferSlow(buffer, start, end, destinationStart); } @@ -152,27 +151,27 @@ public virtual void WriteBuffer(IFrameBuffer buffer, System.Drawing.Point start, /// > public void Fill(Color color) { - Fill(new System.Drawing.Point(0, 0), Width, Height, color); + Fill(new Point(0, 0), Width, Height, color); } /// > - public abstract void Fill(System.Drawing.Point start, int width, int height, Color color); + public abstract void Fill(Point start, int width, int height, Color color); /// > - public abstract Color GetPixel(System.Drawing.Point point); + public abstract Color GetPixel(Point point); /// > - public abstract void SetPixel(System.Drawing.Point point, Color pixelColor); + public abstract void SetPixel(Point point, Color pixelColor); /// > - public virtual bool IsPointWithinFrameBuffer(System.Drawing.Point point) + public virtual bool IsPointWithinFrameBuffer(Point point) { return point.X >= StartPoint.X && point.X < (StartPoint.X + Width) && point.Y >= StartPoint.Y && point.Y < (StartPoint.Y + Height); } /// > - public virtual bool IsRangeWithinFrameBuffer(System.Drawing.Point start, System.Drawing.Point end) + public virtual bool IsRangeWithinFrameBuffer(Point start, Point end) { return IsPointWithinFrameBuffer(start) && IsPointWithinFrameBuffer(end); } @@ -182,7 +181,7 @@ public virtual bool IsRangeWithinFrameBuffer(System.Drawing.Point start, System. /// /// The point to get its index within . /// The index within the for the byte that contains the specified pixe location. - protected int GetFrameBufferIndexForPoint(System.Drawing.Point point) + protected int GetFrameBufferIndexForPoint(Point point) { return GetFrameBufferIndexForPoint(point.X, point.Y); } @@ -203,7 +202,7 @@ protected int GetFrameBufferIndexForPoint(int x, int y) /// /// The point within the frame buffer. /// A byte mask pattern with the pixel bit flipped to 1. - protected byte GetPointByteMask(System.Drawing.Point point) + protected byte GetPointByteMask(Point point) { return (byte)(128 >> (point.X & 7)); } @@ -216,13 +215,13 @@ protected byte GetPointByteMask(System.Drawing.Point point) /// The starting point to copy from and write to. /// The point at which copying from the buffer will stop. /// The start point to begin writing to. - protected virtual void WriteBufferSlow(IFrameBuffer buffer, System.Drawing.Point start, System.Drawing.Point end, System.Drawing.Point destinationStart) + protected virtual void WriteBufferSlow(IFrameBuffer buffer, Point start, Point end, Point destinationStart) { for (var x = start.X; x < buffer.Width; x++) { for (var y = start.Y; y < buffer.Height; y++) { - var currentPoint = new System.Drawing.Point(x, y); + var currentPoint = new Point(x, y); SetPixel(currentPoint, buffer.GetPixel(currentPoint)); } } diff --git a/devices/ePaper/Buffers/IFrameBuffer.cs b/devices/ePaper/Buffers/IFrameBuffer.cs index 8cd359d495..3d730271b1 100644 --- a/devices/ePaper/Buffers/IFrameBuffer.cs +++ b/devices/ePaper/Buffers/IFrameBuffer.cs @@ -4,7 +4,6 @@ using System.Drawing; using Iot.Device.EPaper.Enums; -using nanoFramework.UI; namespace Iot.Device.EPaper.Buffers { @@ -26,7 +25,7 @@ public interface IFrameBuffer /// /// Gets or sets the position from which this frame's area starts. /// - System.Drawing.Point StartPoint { get; set; } + Point StartPoint { get; set; } /// /// Gets or sets the current frame page index which this frame instance is covering. @@ -65,21 +64,21 @@ public interface IFrameBuffer /// /// The point to get the byte containing the pixel. /// A byte that contains the pixel specified by the point. - byte this[System.Drawing.Point point] { get; set; } + byte this[Point point] { get; set; } /// /// Gets the pixel at the specified position. /// /// The position to get the pixel from. /// representing the specified pixel. - Color GetPixel(System.Drawing.Point point); + Color GetPixel(Point point); /// /// Sets the value of the pixel at the specified position. /// /// The position to set the pixel. /// The pixel color value to use. - void SetPixel(System.Drawing.Point point, Color pixelColor); + void SetPixel(Point point, Color pixelColor); /// /// Fill the entire frame buffer using the specified color. @@ -94,7 +93,7 @@ public interface IFrameBuffer /// The width of the area. /// The height of the area. /// The color value to use. - void Fill(System.Drawing.Point start, int width, int height, Color color); + void Fill(Point start, int width, int height, Color color); /// /// Copies the entire specified into the current frame buffer. @@ -107,7 +106,7 @@ public interface IFrameBuffer /// /// The to copy from. /// The start point to begin writing to. - void WriteBuffer(IFrameBuffer buffer, System.Drawing.Point destinationStart); + void WriteBuffer(IFrameBuffer buffer, Point destinationStart); /// /// Copies the specified into the current frame buffer. @@ -116,7 +115,7 @@ public interface IFrameBuffer /// The start position to copy from. /// The point at which copying from the buffer will stop. /// The start point to begin writing to. - void WriteBuffer(IFrameBuffer buffer, System.Drawing.Point start, System.Drawing.Point end, System.Drawing.Point destinationStart); + void WriteBuffer(IFrameBuffer buffer, Point start, Point end, Point destinationStart); /// /// Resets the current frame buffer to its default starting values (all pixels set to 0). @@ -134,7 +133,7 @@ public interface IFrameBuffer /// /// The point to check. /// True if the point is within this frame's area, otherwise; false. - bool IsPointWithinFrameBuffer(System.Drawing.Point point); + bool IsPointWithinFrameBuffer(Point point); /// /// Checks if the specified range of points is within the current frame buffer. @@ -142,6 +141,6 @@ public interface IFrameBuffer /// The starting point of the range. /// The end point of the range. /// True if the range is within this frame's area, otherwise; false. - bool IsRangeWithinFrameBuffer(System.Drawing.Point start, System.Drawing.Point end); + bool IsRangeWithinFrameBuffer(Point start, Point end); } } diff --git a/devices/ePaper/Drivers/IePaperDisplay.cs b/devices/ePaper/Drivers/IePaperDisplay.cs index 20f512dcb3..6b2d9ba6b6 100644 --- a/devices/ePaper/Drivers/IePaperDisplay.cs +++ b/devices/ePaper/Drivers/IePaperDisplay.cs @@ -3,7 +3,7 @@ using System; using System.Drawing; - +using System.Threading; using Iot.Device.EPaper.Buffers; using nanoFramework.UI; @@ -95,9 +95,10 @@ public interface IEPaperDisplay : IDisposable /// Blocks the current thread until the display is in idle mode again. /// /// The maximum time to wait in ms before exiting the method. -1 to wait infinitely. - /// True if it returns before the time to wait, false otherwise. - /// Be careful if waiting infinitely as the thread will be block without any possibilities to exit. - bool WaitReady(int waitingTime); + /// The to be able to cancel the waiting time. + /// True if it returns before the expires, false otherwise. + /// If _waitingTime_ is set to -1, _cancellationToken_ could not be null, and a exception will be throw. + bool WaitReady(int waitingTime, CancellationTokenSource cancellationToken); /// /// Begins a frame draw operation with frame paging support. diff --git a/devices/ePaper/Drivers/JD796xx/JD79653A.cs b/devices/ePaper/Drivers/JD796xx/JD79653A.cs index df73f6a0bb..24bf05f4e6 100644 --- a/devices/ePaper/Drivers/JD796xx/JD79653A.cs +++ b/devices/ePaper/Drivers/JD796xx/JD79653A.cs @@ -8,7 +8,6 @@ using System.Threading; using Iot.Device.EPaper.Buffers; using Iot.Device.EPaper.Enums; -using nanoFramework.UI; namespace Iot.Device.EPaper.Drivers.Jd796xx { @@ -395,7 +394,7 @@ protected virtual void HardwareReset() /// /// Perform the required initialization steps to set up the display. /// - /// Sequence order can be found at https://github.com/m5stack/M5Core-Ink/blob/master/src/utility/WFT0154CZB3_INIT.h + /// Sequence order can be found at https://github.com/m5stack/M5Core-Ink/blob/master/src/utility/WFT0154CZB3_INIT.h. public virtual void Initialize() { if (PowerState == PowerState.DeepSleep) @@ -607,16 +606,24 @@ protected virtual void WaitMs(int milliseconds) } /// - public virtual bool WaitReady(int waitingTime) + public virtual bool WaitReady(int waitingTime, CancellationTokenSource cancellationToken = default) { - int currentWait = 0; - while (currentWait < waitingTime && _busyPin.Read() == PinValue.Low) + if (cancellationToken == default) { - SpinWait.SpinUntil(5); - currentWait += 5; + if (waitingTime < 0) + { + throw new ArgumentNullException(nameof(cancellationToken), $"{nameof(cancellationToken)} cannot be null with {nameof(waitingTime)} < 0"); + } + + cancellationToken = new CancellationTokenSource(waitingTime); + } + + while (!cancellationToken.IsCancellationRequested && _busyPin.Read() == PinValue.Low) + { + cancellationToken.Token.WaitHandle.WaitOne(5, true); } - return currentWait >= waitingTime; + return !cancellationToken.IsCancellationRequested; } #region IDisposable diff --git a/devices/ePaper/Drivers/Ssd168x/Ssd168x.cs b/devices/ePaper/Drivers/Ssd168x/Ssd168x.cs index 0206a5743a..ed512df04b 100644 --- a/devices/ePaper/Drivers/Ssd168x/Ssd168x.cs +++ b/devices/ePaper/Drivers/Ssd168x/Ssd168x.cs @@ -9,7 +9,6 @@ using Iot.Device.EPaper.Buffers; using Iot.Device.EPaper.Enums; -using nanoFramework.UI; namespace Iot.Device.EPaper.Drivers.Ssd168x { @@ -19,13 +18,13 @@ namespace Iot.Device.EPaper.Drivers.Ssd168x public abstract class Ssd168x : IEPaperDisplay { private readonly int _maxWaitingTime = 500; + private readonly bool _shouldDispose; private SpiDevice _spiDevice; private GpioController _gpioController; private GpioPin _resetPin; private GpioPin _busyPin; private GpioPin _dataCommandPin; - private bool _shouldDispose; private bool _disposed; /// @@ -169,7 +168,7 @@ protected virtual void CalculateFrameBufferPageBounds() CurrentFrameBufferStartXPosition = GetXPositionFromFrameBufferIndex(CurrentFrameBufferPageLowerBound); CurrentFrameBufferStartYPosition = GetYPositionFromFrameBufferIndex(CurrentFrameBufferPageLowerBound); - FrameBuffer.StartPoint = new System.Drawing.Point(CurrentFrameBufferStartXPosition, CurrentFrameBufferStartYPosition); + FrameBuffer.StartPoint = new Point(CurrentFrameBufferStartXPosition, CurrentFrameBufferStartYPosition); FrameBuffer.CurrentFramePage = CurrentFrameBufferPage; } @@ -628,16 +627,24 @@ protected virtual void WaitMs(int milliseconds) } /// - public virtual bool WaitReady(int waitingTime) + public virtual bool WaitReady(int waitingTime, CancellationTokenSource cancellationToken = default) { - int currentWait = 0; - while (currentWait < waitingTime && _busyPin.Read() == PinValue.High) + if (cancellationToken == default) { - WaitMs(5); - currentWait += 5; + if (waitingTime < 0) + { + throw new ArgumentNullException(nameof(cancellationToken), $"{nameof(cancellationToken)} cannot be null with {nameof(waitingTime)} < 0"); + } + + cancellationToken = new CancellationTokenSource(waitingTime); + } + + while (!cancellationToken.IsCancellationRequested && _busyPin.Read() == PinValue.High) + { + cancellationToken.Token.WaitHandle.WaitOne(5, true); } - return currentWait >= waitingTime; + return !cancellationToken.IsCancellationRequested; } #region IDisposable diff --git a/devices/ePaper/ePaper.nfproj b/devices/ePaper/ePaper.nfproj index 152c1b7829..5d4430a5cb 100644 --- a/devices/ePaper/ePaper.nfproj +++ b/devices/ePaper/ePaper.nfproj @@ -76,6 +76,9 @@ packages\nanoFramework.System.Device.Spi.1.3.43\lib\System.Device.Spi.dll True + + packages\nanoFramework.System.Threading.1.1.32\lib\System.Threading.dll + diff --git a/devices/ePaper/packages.config b/devices/ePaper/packages.config index 319649a7e6..d25b1d7aab 100644 --- a/devices/ePaper/packages.config +++ b/devices/ePaper/packages.config @@ -1,10 +1,11 @@ - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/devices/ePaper/packages.lock.json b/devices/ePaper/packages.lock.json index 544169fc3d..e6edceb1b0 100644 --- a/devices/ePaper/packages.lock.json +++ b/devices/ePaper/packages.lock.json @@ -1,49 +1,55 @@ -{ - "version": 1, - "dependencies": { - ".NETnanoFramework,Version=v1.0": { - "nanoFramework.CoreLibrary": { - "type": "Direct", - "requested": "[1.15.5, 1.15.5]", - "resolved": "1.15.5", - "contentHash": "u2+GvAp1uxLrGdILACAZy+EVKOs28EQ52j8Lz7599egXZ3GBGejjnR2ofhjMQwzrJLlgtyrsx8nSLngDfJNsAg==" - }, - "nanoFramework.Graphics.Core": { - "type": "Direct", - "requested": "[1.2.4, 1.2.4]", - "resolved": "1.2.4", - "contentHash": "rr9GjMQ3Fucn3LQllQOR1D6DAwEF731GRCBxMIqz4YYpPtidy4XsCSyetZeW/ArMoqYRFwyPqaMtKDhrIHwnDg==" - }, - "nanoFramework.Runtime.Events": { - "type": "Direct", - "requested": "[1.11.15, 1.11.15]", - "resolved": "1.11.15", - "contentHash": "3uDNSTfiaewDAyi6fOMWYru0JCn/gr8DEv+Ro/V12SzojU9Dyxl5nSVOBtBXts7vErfIthB6SPiK180AMnrI8A==" - }, - "nanoFramework.System.Device.Gpio": { - "type": "Direct", - "requested": "[1.1.38, 1.1.38]", - "resolved": "1.1.38", - "contentHash": "t2em7yn9sZpWWj52Xh4f4JjzUQX8/UB7C+XMnMiSGileGhRg86IwszA5/0bGUgc/dKC/AYRSqraPiEB0Q6jhBA==" - }, - "nanoFramework.System.Device.Spi": { - "type": "Direct", - "requested": "[1.3.43, 1.3.43]", - "resolved": "1.3.43", - "contentHash": "LR5ccTCLm4nOxM18YRn4P54ZtK668K7D2LqEdETE2GiDtr3f1Wrc2ehtQsjdDiebX9OHqfMXIYBEvbBoJAjyqw==" - }, - "Nerdbank.GitVersioning": { - "type": "Direct", - "requested": "[3.6.133, 3.6.133]", - "resolved": "3.6.133", - "contentHash": "VZWMd5YAeDxpjWjAP/X6bAxnRMiEf6tES/ITN0X5CHJgkWLLeHGmEALivmTAfYM6P+P/3Szy6VCITUAkqjcHVw==" - }, - "StyleCop.MSBuild": { - "type": "Direct", - "requested": "[6.2.0, 6.2.0]", - "resolved": "6.2.0", - "contentHash": "6J51Kt5X+Os+Ckp20SFP1SlLu3tZl+3qBhCMtJUJqGDgwSr4oHT+eg545hXCdp07tRB/8nZfXTOBDdA1XXvjUw==" - } - } - } +{ + "version": 1, + "dependencies": { + ".NETnanoFramework,Version=v1.0": { + "nanoFramework.CoreLibrary": { + "type": "Direct", + "requested": "[1.15.5, 1.15.5]", + "resolved": "1.15.5", + "contentHash": "u2+GvAp1uxLrGdILACAZy+EVKOs28EQ52j8Lz7599egXZ3GBGejjnR2ofhjMQwzrJLlgtyrsx8nSLngDfJNsAg==" + }, + "nanoFramework.Graphics.Core": { + "type": "Direct", + "requested": "[1.2.4, 1.2.4]", + "resolved": "1.2.4", + "contentHash": "rr9GjMQ3Fucn3LQllQOR1D6DAwEF731GRCBxMIqz4YYpPtidy4XsCSyetZeW/ArMoqYRFwyPqaMtKDhrIHwnDg==" + }, + "nanoFramework.Runtime.Events": { + "type": "Direct", + "requested": "[1.11.15, 1.11.15]", + "resolved": "1.11.15", + "contentHash": "3uDNSTfiaewDAyi6fOMWYru0JCn/gr8DEv+Ro/V12SzojU9Dyxl5nSVOBtBXts7vErfIthB6SPiK180AMnrI8A==" + }, + "nanoFramework.System.Device.Gpio": { + "type": "Direct", + "requested": "[1.1.38, 1.1.38]", + "resolved": "1.1.38", + "contentHash": "t2em7yn9sZpWWj52Xh4f4JjzUQX8/UB7C+XMnMiSGileGhRg86IwszA5/0bGUgc/dKC/AYRSqraPiEB0Q6jhBA==" + }, + "nanoFramework.System.Device.Spi": { + "type": "Direct", + "requested": "[1.3.43, 1.3.43]", + "resolved": "1.3.43", + "contentHash": "LR5ccTCLm4nOxM18YRn4P54ZtK668K7D2LqEdETE2GiDtr3f1Wrc2ehtQsjdDiebX9OHqfMXIYBEvbBoJAjyqw==" + }, + "nanoFramework.System.Threading": { + "type": "Direct", + "requested": "[1.1.32, 1.1.32]", + "resolved": "1.1.32", + "contentHash": "6o7Y4gH15FLuo2FWGLecABiCD57V5QMf5g/hEneV64VmhoXI8Bk7r6BDBPTfAePs738xbc1ECpA5dJmbSmtilg==" + }, + "Nerdbank.GitVersioning": { + "type": "Direct", + "requested": "[3.6.133, 3.6.133]", + "resolved": "3.6.133", + "contentHash": "VZWMd5YAeDxpjWjAP/X6bAxnRMiEf6tES/ITN0X5CHJgkWLLeHGmEALivmTAfYM6P+P/3Szy6VCITUAkqjcHVw==" + }, + "StyleCop.MSBuild": { + "type": "Direct", + "requested": "[6.2.0, 6.2.0]", + "resolved": "6.2.0", + "contentHash": "6J51Kt5X+Os+Ckp20SFP1SlLu3tZl+3qBhCMtJUJqGDgwSr4oHT+eg545hXCdp07tRB/8nZfXTOBDdA1XXvjUw==" + } + } + } } \ No newline at end of file From c3f71444a646c969b71b5f8779e1c2bde8e7157e Mon Sep 17 00:00:00 2001 From: xas Date: Tue, 9 Jan 2024 16:08:58 +0100 Subject: [PATCH 10/12] fix: update nuspec file according to nfproj file --- devices/ePaper/ePaper.nuspec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devices/ePaper/ePaper.nuspec b/devices/ePaper/ePaper.nuspec index 48ee143fc5..07047b599c 100644 --- a/devices/ePaper/ePaper.nuspec +++ b/devices/ePaper/ePaper.nuspec @@ -19,11 +19,12 @@ Iot.Device.ePaper assembly for .NET nanoFramework C# projects nanoFramework C# csharp netmf netnf epd ePaper Iot.Device.Display.ePaper - + + From eb86cc9cdca23f1756a40facc3115318c34d7a3d Mon Sep 17 00:00:00 2001 From: xas Date: Tue, 9 Jan 2024 17:58:07 +0100 Subject: [PATCH 11/12] fix: update nuspec file according to nfproj file --- devices/ePaper/ePaper.nfproj | 2 +- devices/ePaper/ePaper.nuspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/devices/ePaper/ePaper.nfproj b/devices/ePaper/ePaper.nfproj index 5d4430a5cb..ee95fc90ff 100644 --- a/devices/ePaper/ePaper.nfproj +++ b/devices/ePaper/ePaper.nfproj @@ -56,7 +56,7 @@ - + packages\nanoFramework.CoreLibrary.1.15.5\lib\mscorlib.dll True diff --git a/devices/ePaper/ePaper.nuspec b/devices/ePaper/ePaper.nuspec index 07047b599c..4f896f5d5a 100644 --- a/devices/ePaper/ePaper.nuspec +++ b/devices/ePaper/ePaper.nuspec @@ -19,7 +19,7 @@ Iot.Device.ePaper assembly for .NET nanoFramework C# projects nanoFramework C# csharp netmf netnf epd ePaper Iot.Device.Display.ePaper - + From 448b4fd7f3118337331fd579fc9b209cd86dcde6 Mon Sep 17 00:00:00 2001 From: xas Date: Wed, 10 Jan 2024 09:59:46 +0100 Subject: [PATCH 12/12] fix: update nuspec file according to nfproj file --- devices/ePaper/ePaper.nfproj | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/devices/ePaper/ePaper.nfproj b/devices/ePaper/ePaper.nfproj index ee95fc90ff..3d0fec67d6 100644 --- a/devices/ePaper/ePaper.nfproj +++ b/devices/ePaper/ePaper.nfproj @@ -56,36 +56,31 @@ - + + + + + + + packages\nanoFramework.CoreLibrary.1.15.5\lib\mscorlib.dll - True - + packages\nanoFramework.Graphics.Core.1.2.4\lib\nanoFramework.Graphics.Core.dll - True - + packages\nanoFramework.Runtime.Events.1.11.15\lib\nanoFramework.Runtime.Events.dll - True - + packages\nanoFramework.System.Device.Gpio.1.1.38\lib\System.Device.Gpio.dll - True - + packages\nanoFramework.System.Device.Spi.1.3.43\lib\System.Device.Spi.dll - True packages\nanoFramework.System.Threading.1.1.32\lib\System.Threading.dll - - - - - -