-
-
Notifications
You must be signed in to change notification settings - Fork 116
add GDEW0154x e-paper driver #902
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Should we separate code into 3 package nuget ?
(on draft state, as I am waiting some specs confirmation from M5 team) |
Ellerbach
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amazing start! And I love the fact that the interface makes sense :-) that's the first real different driver added here! So it's great to see! Few comments mainly about getting clarity for maintainability.
Also, you'll need to bump the minor version in the version file.
| { | ||
| _spiDevice = spiDevice ?? throw new ArgumentNullException(nameof(spiDevice)); | ||
| _gpioController = gpioController ?? throw new ArgumentNullException(nameof(gpioController)); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you add a "_shouldDispose" to check if the gpio controller should be disposed? See the pattern here for example:
| private readonly bool _shouldDispose; |
In short, you can pass a null GpioController and you'll create it, also you'll dispose it in this case. If it's not null and the shouldDispose is true, you'll dispose it otherwise no.
| WaitMs(5); | ||
|
|
||
| SendCommand((byte)Command.DisplayRefresh); | ||
| WaitMs(500); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you add a little comment on why to wait 5 ms and then 500 ms? Just a comment like "// 500 ms needed as per documentation page 5" will do it. So it makes it easier when maintaining and debugging all this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did, but I think there are some non-sense between samples I found on M5 repo and the one provided by the screen manufacturer...
| } | ||
|
|
||
| SendCommand((byte)Command.PanelSetting); | ||
| SendData(0xdf); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you point out where the initilization sequence is coming form? So it makes it easier for maintenance
| /// <inheritdoc/> | ||
| public virtual void PerformFullRefresh() | ||
| { | ||
| WaitMs(10); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why 10 milliseconds?
| public virtual void PowerOn() | ||
| { | ||
| HardwareReset(); | ||
| WaitMs(1000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
few comments on the sequence will be greatly appreciated as well
| // make sure we are setting data/command pin to low (command mode) | ||
| _dataCommandPin.Write(PinValue.Low); | ||
|
|
||
| foreach (var b in command) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe for performance reasons, it could be faster to write all the bytes at once? As every time, the data is sent independently with a low and high on the chip select. So it's much slower than sending in a single write with a buffer.
| // 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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comment as in the command one
| { | ||
| while (_busyPin.Read() != pinValue) | ||
| { | ||
| WaitMs(5); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if waiting is a matter of few milliseconds you can use the Thread.Spin. If it's indeed much more, then keep the Thread.Sleep
| { | ||
| while (_busyPin.Read() == PinValue.Low) | ||
| { | ||
| WaitMs(5); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same remark here as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did a little rewrite for the WaitBusy function as it was waiting indefinitely
I guess it's ok to have one only for the moment (it simplify a bit things), the code overhead is not that high |
Ellerbach
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better and better ;-)
| { | ||
| while (_busyPin.Read() == PinValue.High) | ||
| int currentWait = 0; | ||
| while (currentWait < waitingTime && _busyPin.Read() == PinValue.High) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
create a cancellation token, it's done for that. And you can use it in the loop as well to be non blocking
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is cancelling the wait on the busy pin a good idea here? If the waitingTime is set to 5 and after 25ms the display busy pin is still reading High, should we ignore that and move on with executing the rest of the commands knowing that they will not execute properly as the display is still stuck performing something else?
In my opinion, this will mask issues from the dev and might be harder to debug. If we must, I'd suggest throwing an exception so we have a clear signal to the caller/dev that the device has not responded in due time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like (just did it from memory) this is much cleaner and you won't have to wait an extra 5 ms (which is ultimately going to be 20 ms) unnecessary. this shows as well how to set a default time.
public virtual bool WaitReady(CancellationToken waitingTime = default)
{
if (waitingTime == default)
{
// With a default time of 5 seconds as an example
waitingTime = new CancellationTokenSource(5000);
}
while(!waitingTime.IsCancelledRequest && _busyPin.Read() == PinValue.High)
{
waitingTime.WaitHandle.WaitOne(5, true);
}
return !waitingTime.IsCancelledRequest;
}| int dataCommandPin, | ||
| int width, | ||
| int height, | ||
| GpioController gpioController, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can even set this one as null and create it internally if not given.
| { | ||
| while (_busyPin.Read() == PinValue.High) | ||
| int currentWait = 0; | ||
| while (currentWait < waitingTime && _busyPin.Read() == PinValue.High) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is cancelling the wait on the busy pin a good idea here? If the waitingTime is set to 5 and after 25ms the display busy pin is still reading High, should we ignore that and move on with executing the rest of the commands knowing that they will not execute properly as the display is still stuck performing something else?
In my opinion, this will mask issues from the dev and might be harder to debug. If we must, I'd suggest throwing an exception so we have a clear signal to the caller/dev that the device has not responded in due time.
|
|
||
| SendCommand((byte)Command.MasterActivation); | ||
| WaitReady(); | ||
| WaitReady(_maxWaitingTime); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The return value of WaitReady is being ignored here. Would be good if it can be used to signal failure to the caller.
| /// <summary> | ||
| /// Base class for GDEW0154-based ePaper devices. | ||
| /// </summary> | ||
| public abstract class Gdew0154x : IEPaperDisplay |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you rename the class to use the Controller model number instead which is 'JD79653A' ? GDEW0154x is an epaper product model number and not the controller name. As you can see in the other implemented driver, we're using SSD168x which is the controller name rather than the product number from GoodDisplay.
Also, please rename the namespace to match this.
| /// <param name="sleepMode">The sleep mode to use when powering down the display.</param> | ||
| public virtual void PowerDown(SleepMode sleepMode = SleepMode.Normal) | ||
| { | ||
| SendCommand(0x50); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you have the Command enum, these byte values should go there too.
| HardwareReset(); | ||
| } | ||
|
|
||
| SendCommand(0x50); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here. Please move the byte value to Command enum.
|
For the busy pin question, I am more on the solution of warn the developer, but not block him I think it should be the developer role to decide what to do if after a while the busy pin is still up. I am also against raising an exception as you could crash the developer's app. I am more on a warning policy: "hey be careful, seems there are still work on the screen, but continue with the rest" |
|
BTW, is it really necessary I don't think a class and a namespace should share a same name. Why the class |
no it's not. Fine to use directly the Driver namespace! |
Not necessary, no. What I was thinking about is how JD79653A seems to be part of a family of drivers. I found references to another epd driver |
* 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
* namespace driver renaming * fix missing command map * allow gpioController parameter to be null
Ellerbach
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple of small details. Great to see the PR now real review :-)
| /// <summary> | ||
| /// Base class for GDEW0154-based ePaper devices. | ||
| /// </summary> | ||
| public abstract class JD79653A : IEPaperDisplay |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically the name should be Jd79653A to follow our guidelines. Same for the namespace Jd796xx
| /// </summary> | ||
| public abstract class Ssd168x : IEPaperDisplay | ||
| { | ||
| private readonly int _maxWaitingTime = 500; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not a const?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to leave the possibility that become a configurable property when instantiating it.
Ellerbach
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, just a little detail on the wait function using cancellation token
| { | ||
| while (_busyPin.Read() == PinValue.High) | ||
| int currentWait = 0; | ||
| while (currentWait < waitingTime && _busyPin.Read() == PinValue.High) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like (just did it from memory) this is much cleaner and you won't have to wait an extra 5 ms (which is ultimately going to be 20 ms) unnecessary. this shows as well how to set a default time.
public virtual bool WaitReady(CancellationToken waitingTime = default)
{
if (waitingTime == default)
{
// With a default time of 5 seconds as an example
waitingTime = new CancellationTokenSource(5000);
}
while(!waitingTime.IsCancelledRequest && _busyPin.Read() == PinValue.High)
{
waitingTime.WaitHandle.WaitOne(5, true);
}
return !waitingTime.IsCancelledRequest;
}9de53d1 to
c8ac54b
Compare
Ellerbach
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks all good now! Thanks!
|
@xas you still have to fix the versions between the nuget and the nfproj and what's missing. See the build result. |
Ellerbach
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we're good to go :-) HUGE thanks for the work on this contribution! Great to see this additional epaper screen!
Description
Motivation and Context
How Has This Been Tested?
Screenshots
Types of changes
Checklist: