rm-hull / luma.oled

Python module to drive a SSD1306 / SSD1309 / SSD1322 / SSD1325 / SSD1327 / SSD1331 / SSD1351 / SH1106 OLED
https://luma-oled.readthedocs.io
MIT License
810 stars 164 forks source link

Feedback upon non-successful --spi-bus-speed? #302

Closed mattblovell closed 3 years ago

mattblovell commented 4 years ago

This is really more of a question for luma.oled rather than necessarily an issue. Are the demos in luma.example expected to indicate anything if --spi-bus-speed is not successful? I'll explain the question more below.

Type of Raspberry Pi

Odroid C4

Linux Kernel version

This is a CoreELEC installation, with additional packages installed via entware. So, prepare yourself...

Linux C4 4.9.113 #1 SMP PREEMPT Fri Aug 14 20:30:41 UTC 2020 aarch64 GNU/Linux

For GPIO access, this installation is using RPi.GPIO-Odroid from

https://github.com/awesometic/RPi.GPIO-Odroid

with pip3 reporting the following package versions:

Behavior

Upon invoking /opt/bin/python3 bounce.py --interface spi -d ssd1309 everything runs fine, with 80 fps of bouncing balls in the 128x64 display.

One of the advertising points for the fairly new Odroid C4 board is supposed to be high SPI frequency. Running

cat /sys/class/spidev/spidev32766.0/device/of_node/spi-max-frequency | hexdump -C

yields 05 f5 e1 00 or 100000000 (decimal). Given that, I thought I would try out 16 and 32 MHz operation. (There is a softlink to spidev32766.0 that provides the more usual spidev0.0 numbering.)

Invoking /opt/bin/python3 bounce.py --interface spi --spi-bus-speed 16000000 -d ssd1309 gets me the output

Version: luma.oled 3.6.0 (luma.core 1.17.1)
Display: ssd1309
Interface: spi
Dimensions: 128 x 64
------------------------------------------------------------

but the display remains blank. The same occurs when trying --spi-bus-speed 32000000.

I can make use of slower speeds. Running --spi-bus-speed 500000 gets me a 33 fps demo.

Does the blank display match expectations?

I have also asked the Odroid user community how one is supposed to set/change SPI frequency, via

https://forum.odroid.com/viewtopic.php?f=205&t=40464

since that's unclear to me.

Many thanks! Matt

rm-hull commented 4 years ago

Does the blank display match expectations?

Yes, inasmuch as the init sequence is sent to the device too fast, and so it never initializes, and so nothing is displayed.

While it is possible that the RPi (or Odroid in your case) is capable of driving the SPI bus at high bus speeds, you must also consider if the display device at the other end of the bus is also capable of ingesting said data packets at such frenetic rates.

I have found some displays will be stable at 16MHz and higher while others are unstable at 16MHz (i.e wont initialize), so I would repeatedly decrease the bus speed and try again, until the device works in a stable manner. I guess if you dig into the datasheet for the specific display it will may give you the max speed it is designed to work against.

mattblovell commented 4 years ago

You're right, of course! Thinking back, I only started playing with SPI frequency upon connecting the larger (320x240) LCD panel. I never tried anything but the default 8 MHz with the OLED panel on the RPi.

Trying out 16 Mhz and 32 MHz with the ili9341 LCD panel connected to the Odroid C4, both of those speeds work -- as they did with the RPi.

Looking at the SSD1309 datasheet (Rev 1.1, July 2011), Table 13-4 ends up specifying clock low and high minimums that amount to a maximum frequency of 10 MHz. So, not a surprise that it's happy at 8 MHz and not so content going faster!

The ILI9341 datasheet (v1.02) in Sections 19.3.3 and 19.3.4 seems to specify, for writes, a serial clock max frequency of 10 MHz as well (tscycw minimum of 100 ns). Beats me why the faster frequencies appear to be working.

Out of curiosity, is the power-of-two (in MHz) restriction inherent in /dev/spidev operation or is it just a simplifying approach for luma.core.interface.serial?

Thanks for your help! I think this issue can safely be closed.

rm-hull commented 4 years ago

Out of curiosity, is the power-of-two (in MHz) restriction inherent in /dev/spidev operation or is it just a simplifying approach for luma.core.interface.serial?

Not a restriction in the luma implementation, I’m sure I read it somewhere on the main RPi site a long time ago, but this is all I could find just now: https://elinux.org/RPi_SPI#Speed

mattblovell commented 4 years ago

This page:

https://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md

appears to have made the correction briefly discussed on that elinux.org page you cited.

Speed The CDIV (Clock Divider) field of the CLK register sets the SPI clock speed:

SCLK = Core Clock / CDIV If CDIV is set to 0, the divisor is 65536. The divisor must be a multiple of 2, with odd numbers rounded down. Note that not all possible clock rates are usable because of analogue electrical issues (rise times, drive strengths, etc.) See the Linux driver section for more info.

The Linux driver link then points to a page with this content:

Speed The driver supports all speeds which are even integer divisors of the core clock, although as said above not all of these speeds will support data transfer due to limits in the GPIOs and in the devices attached. As a rule of thumb, anything over 50MHz is unlikely to work, but your mileage may vary.

mattblovell commented 4 years ago

This project, although not achievable in Python, is at least entertaining:

https://github.com/juj/fbcp-ili9341

Besides partial screen updates, its list of tricks also includes:

  • The program directly communicates with the BCM2835 ARM Peripherals controller registers, bypassing the usual Linux software stack.
  • A hybrid of both Polled Mode SPI and DMA based transfers are utilized. Long sequential transfer bursts are performed using DMA, and when DMA would have too much latency, Polled Mode SPI is applied instead.
  • Undocumented BCM2835 features are used to squeeze out maximum bandwidth: SPI CDIV is driven at even numbers (and not just powers of two), and the SPI DLEN register is forced in non-DMA mode to avoid an idle 9th clock cycle for each transferred byte.
  • Good old interlacing is added into the mix:
mattblovell commented 3 years ago

All my original questions were answered. Closing this issue.