I wrote this for the PineTime, and all available sensors (accelerometer,
step counter, temperature sensor) do work.
This commit also includes two "configuration files", that actually
appear to be firmware files to run on the accelerometer for special
features like step counting.
I'm not sure where they originally came for, and I don't know the
copyright status of them. However, Bosch has open-sourced the BMA423
driver which includes a similar binary blob and the InfiniTime and
Wasp-OS projects have been shipping these blobs without issues, so I
think it's reasonably safe to include these binary blobs directly in the
source.
The rotation as configured using st7789.Config was rotated 180°: 0° was
configured as 180°, 90° was configured as 270°, etc. Presumably with the
original test display, the ribbon cable was seen as the top of the
screen while if you look at product photos it is usually at the bottom.
Example:
https://www.buydisplay.com/wide-angle-1-3-inch-240x240-color-ips-tft-display-st7789-controller
Only Adafruit seems to sell these displays upside down:
https://www.adafruit.com/product/3787
This patch fixes this mistake. It should be noted that this is backwards
incompatible: all code that uses a st7789 will have to be modified to
use the correct rotation instead of the previous incorrect rotation.
If this is too big of a change, we could just keep things as-is and
pretend that all displays are upside down.
The existing code was broken in a few ways:
- It didn't use the correct operator precedence for the VSCRDEF VSA
variable: it needed some extra parentheses to be correct.
- It used the configured height instead of the actual display height
for calculating VSA, which is incorrect. TFA+VSA+BFA must always be
exactly 320, even if a lower value is configured.
- If a lower than 320 pixel height is configured, the bottomFixedArea
parameter applied to the whole 320 pixel screen height. Because this
seems counter intuitive (and relies on properties of any given
screen), I've changed it to work from the actual visible bottom of
the screen (which may be smaller than 320 pixels).
TODO: this doesn't take RowOffset into account, while it probably
should.
I haven't fixed the st7735 implementation, because I didn't have example
code on hand that would easily work on a st7735 screen. This is left as
a TODO for the future.
This is only really useful for applications that use DrawRGBBitmap8. The
RGB444 format has fewer colors, but can be up to 25% faster than the
default RGB565.
I have tested this change on the Gopher Badge and the PineTime. Both can
be substantially faster once the code is modified to output RGB444
instead of RGB565.
Many displays don't have the TE pin exposed. But those that do have the
pin (for example, the PyPortal) can use it to synchronize writing a new
image to the display. When implemented correctly, tearing can be avoided
entirely.
This commit also changes the LCD refresh direction to either
top-to-bottom or left-to-right depending on the rotation. Previously it
might refresh from right-to-left or bottom-to-top. This has little
impact on code that doesn't use the TE line, but code that does now only
needs to worry about two cases (top-to-bottom and left-to-right) instead
of four.
(Unfortunately, it appears that the hardware doesn't support changing
the major LCD refresh order so code that wants to do tear-free rendering
still needs to care about these two cases).
This should make the software I2C driver more portable, and potentially
usable on chips that aren't yet supported. More importantly, it avoids
some guesswork in the various chip-specific timing code.
This is implemented in inline assembly using machine.CPUFrequency() to
know how long a single CPU cycle takes. As long as it is called with a
constant duration, it should be fully inlined and all values can be
const-propagated resulting in very tight inline assembly.
For example, when I convert i2csoft to use this delay function, the
entire delay function compiles to something like this:
8784: movs r6, #100
8786: mov r0, r6
8788: nop
878a: nop
878c: nop
878e: nop
8790: nop
8792: subs r0, #1
8794: bne 0x8788
That means that all the math to calculate the number of cycles is
entirely optimized away (in this case, to 100 loops).
I ran the example on a few boards to see how well it works:
| board | 100ms wait | CPU core
|-----------------------|------------|------
| microbit | 121.6ms | Cortex-M0 so it has 12% overhead
| circuitplay-express | 100.1ms | Cortex-M0+ so it is cycle accurate
| pico | 100.2ms | Cortex-M0+
| pyportal | 100.3ms | Cortex-M4
| circuitplay-bluefruit | 125.8ms | Cortex-M4
| esp8266 | 125.1ms |
This shows that there is some loop overhead because of conservative
estimates, but note that even though there may be a 25% overhead, the
actual overhead per `delay.Sleep()` call is very small. It should be
good enough for software I2C at least, and can potentially be improved
in the future.
This speeds up the smoke tests by over 5x on my laptop, but keeps two
features that I find very important:
1. A stable output, which is used by tools like sizediff.
2. An easy to change text file with all the smoke tests.
This means that parsing is a little bit harder, but with the help of
packages like shlex and flag this is actually not very difficult.
It is needed to control the chip select pin when the st7789 display is
wired together with some other SPI device on the same bus, for example
if it shares the bus with SPI flash.
This required some refactoring of the code to correctly set the CS pin
everywhere. Notably, this removes the public Command, Data, Tx, and Rx
methods which poke into private details of the st7789 driver and are
therefore best hidden in my opinion.
* Unify the Rotation type, so that SetRotation can be used in
interfaces.
* Add Rotation() method to these displays, so that the current
rotation can be read.
* Change SetRotation() so that it can return an error (if the given
rotation isn't supported).
The API looks like this and is based on the PCA9685 PWM chip:
Sleep(sleepEnabled bool) error
It will set the LCD panel to a low power state (not displaying any
image) but memory contents will be preserved.
Heap allocations can result in significant and unpredictable slowdowns.
For example, they can take over 10ms on the Gopher Badge, which results
in visible tearing (when tearing is otherwise avoided).
Some displays need other values than the default to look right.
Therefore, add a configuration option to set the gamma table for a
particular LCD panel.
This is needed for high performance graphics.
Using this API, and using partial updates to the screen (no full screen
refreshes), I've been able to get well over 60fps of updates.