Posts Tagged RPi
Ex post facto notes from the third Squidwrench Electronics Workshop.
Exhibit various 50 Ω resistors, including my all-time favorite, a 600 W 3 GHz dummy load:
… down to a 1/8 Ω metal film resistor.
The dummy load’s N connector triggered a regrettable digression into RF, belatedly squelched because I wasn’t prepared to extemporize on AC concepts like reactance which we haven’t covered yet.
Discussion of resistor applications, power handling, power derating with temperature, etc:
Why you generally won’t find 50 Ω load resistors in Raspberry Pi circuits. Cartridge heaters for 3D printers, not aluminum power resistors, although everyone agrees they look great:
Discussion of voltage vs. current sources, why voltage sources want low internal resistances and current sources want high resistances. Bungled discussion of current sources by putting diodes in parallel; they should go in series to show how added voltage doesn’t change current (much!) in sources driven from higher voltages through higher resistances:
Use Siglent SDM3045X DMM in diode test mode to measure forward drop of power / signal / colored LEDs, discuss voltage variation with color / photon energy. Measure 1.000 mA test current for all forward voltages.
Compute series resistor (500 Ω) to convert adjustable power supply (the digital tattoo box, a lesson in itself) into reasonable current source; roughly 10 V → 20 mA. Find suitable resistor (560 Ω) in SqWr
junk box parts assortment, digression into color band reading.
Wire circuit with meters to measure diode current (series!) and voltage (parallel!), measure same hulking power diode (after discovering insulating washers now in full effect) as before in 1 mA steps to 10 mA, then 15 and 20 mA, tabulate & plot results:
Discover warm resistor, compute power at 20 mA, introduce cautionary tales.
Lesson learned about never returning parts to inventory, with 560 Ω resistor appearing in diode drawer. Cautionary tales about having benchtop can of used parts as front-end cache for inventory backing store.
Another intense day of bench work!
Adding delays around the SPI control signal changes reduced the OLED glitch rate from maybe a few a week to once a week, but didn’t completely solve the problem.
However, (nearly) all the remaining glitches seem to occur while writing a single row of pixels, which trashes the rest of the display and resolves on the next track update. That suggests slowing the timing during the initial hardware setup did change the results.
Another look at the Luma code showed I missed the Chip Enable (a.k.a. Chip Select in the SH1106 doc) change in
def _write_bytes(self, data): gpio = self._gpio if self._CE: time.sleep(1.0e-3) gpio.output(self._CE, gpio.LOW) # Active low time.sleep(1.0e-3) for byte in data: for _ in range(8): gpio.output(self._SDA, byte & 0x80) gpio.output(self._SCLK, gpio.HIGH) byte <<= 1 gpio.output(self._SCLK, gpio.LOW) if self._CE: time.sleep(1.0e-3) gpio.output(self._CE, gpio.HIGH)
What remains unclear (to me, anyway) is how the code in Luma's
bitbang class interacts with the hardware-based SPI code in Python’s underlying
spidev library. I think what I just changed shouldn’t make any difference, because the code should be using the hardware driver, but the failure rate is now low enough I can’t be sure for another few weeks (and maybe not even then).
All this boils down to the Pi’s SPI hardware interface, which changes the CS output with setup / hold times measured in a few “core clock cycles”, which is way too fast for the SH1106. It seems there’s no control over CS timing, other than by changing the kernel’s bcm2708 driver code, which ain’t happening.
The Python library includes a
no_cs option, with the caveat it will “disable use of the chip select (although the driver may still own the CS pin)”.
Forcibly insisting on using Luma’s bitbang routine may be the only way to make this work, but I don’t yet know how to do that.
Obviously, I should code up a testcase to hammer the OLED and peer at the results on the oscilloscope: one careful observation outweighs a thousand opinions.
The chip reset seems remarkably slow, even at maximum VCC:
As far as I can tell, the
bitbang class handles all the setup and teardown around the actual data transfers, but it’s not clear (to me, anyway) how it interacts with the underlying hardware SPI machinery.
So, let’s add some sleepiness to the Reset code:
if self._RST is not None: self._gpio.output(self._RST, self._gpio.LOW) # Reset device time.sleep(1.0e-3) self._gpio.output(self._RST, self._gpio.HIGH) # Keep RESET pulled high time.sleep(1.0e-3)
A few milliseconds, rather than a few (hundred) microseconds, won’t make any perceptible difference.
Similarly, the Chip Select and Address (Command/Data) signals require more delay than might occur between successive Python statements:
This should do the trick, again with excessive delay:
if self._DC: self._gpio.output(self._DC, self._cmd_mode) time.sleep(1.0e-3) ... snippage ... if self._DC: self._gpio.output(self._DC, self._data_mode) time.sleep(1.0e-3) ... snippage ... if self._CE: gpio.output(self._CE, gpio.LOW) # Active low time.sleep(1.0e-3) ... snippage ... if self._CE: gpio.output(self._CE, gpio.HIGH) time.sleep(1.0e-3)
Although it shouldn’t be necessary, I blew away the
pyc files to prevent future confusion over who’s doing what with which.
Once again, this will require several weeks to see whether the situation changes for the better.
Directly from 0110-M-P’s Thingiverse thing, because a Raspberry Pi in a 75 mm VESA mount case will work for me:
The hole fits a 25 mm fan, but the thing runs cool enough it should survive without forced air; think of it as a contingency. Mounting the case on standoffs seems like a Good Idea, however, as the bottom plate includes many vent slots for Good Circulation.
The top plate builds upside-down, so I had Slic3r add teeny support plugs inside the recessed screw holes. I think button-head screws would fit neatly in the recesses, but we’re obviously not in this for the looks.
The tiny white stud is a Reset switch hot-melt glued into the slot. I plan to just turn off the AC power after shutting the RPi down, so a power-on will suffice as a reset.
Because the OLED driver came from the
pip package manager, not the Raspberry Pi’s system-level
apt package manager, it (or they, there’s plenty of code under the hood) don’t get updated whenever I do system maintenance. The doc says this should do the trick:
sudo -H pip install --upgrade luma.oled
However, it turns out the new version has a slightly longer list of pre-requisite packages, causing the update to go toes-up at a missing package:
Could not import setuptools which is required to install from a source distribution. Please install setuptools.
So update (or install, for the new ones) the missing pieces:
sudo apt-get install python-dev python-pip libfreetype6-dev libjpeg-dev build-essential
Doing so produced a backwards-compatibility error in my Python code:
... change ... from luma.core.serial import spi ... into ... from luma.core.interface.serial import spi
The motivation for all this fuffing and fawing came from watching some OLEDs wake up completely blank or become garbled in one way or another. Evidently, my slower-speed SPI tweak didn’t quite solve the problem, although it did reduce the frequency of failures. I have decided, as a matter of principle, to not embrace the garble.
Soooo, let’s see how shaking all the dice affects the situation.
If ya can’t fix it, feature it!
#font1 = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf',14) font1 = ImageFont.truetype('/home/pi/sga.ttf',14) #font2 = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf',11) font2 = ImageFont.truetype('/home/pi/sga.ttf',11)
It looks surprisingly good on such a low-res display.
The user community seems divided over the update … [grin]
The display started up fine, became encrypted during the next few hours, and remained garbled as the track information changed. This is almost certainly a bad SPI transfer trashing the OLED module’s control registers.
Dropping the clock to the absolute minimum of 0.5 MHz didn’t help, either:
serial = spi(device=0,port=0,bus_speed_hz=500000) device = sh1106(serial)
This particular display woke up blank after loading the new code, then worked OK after another reset. The other streamers lit up as expected on the first try, so the slower SPI isn’t making the situation instantly worse.
Running the clock at 1 MHz definitely reduced the failure rate, which suggests it’s a glitchy thing.
Good embedded systems practice suggests resetting the entire display from scratch every now and again, but my streamer code has no concept of elapsed time. Opening that particular can o’ worms would almost certainly result in an on-screen clock and I do not want to go there.
I suppose I must get a new oscilloscope with SPI bus decoding to verify all the SPI setup and hold times …