-
Notifications
You must be signed in to change notification settings - Fork 495
SPI interface circuit
This is an interface circuit for connecting a parallel interface LCD display with SPI.
The circuit is based on SpriteMods circuit.
8-bit bus
Connect STR to Q2
Skip second 74HC4094
16-bit bus
Connect STR to Q3
74HC4094
+--------------+
| |
MOSI o-------------------------------------------------------| D QP0 |-------------o DB0
| |
SCLK o---------+-----------------------------------+---------| CP QP1 |-------------o DB1
| | | |
CSn o------+ | | Vcc o---| OE QP2 |-------------o DB2
| | | | |
| | 74HC4040 | | QP3 |-------------o DB3
| | +--------------+ | | |
| | | | | | QP4 |-------------o DB4
| | | Q0 |-- | | |
| | | | | | QP5 |-------------o DB5
| | | Q1 |-- | | |
| | | | | | QP6 |-------------o DB6
| | | Q2 |- 8bit --+---|---------| STR |
| | | | | | | QP7 |-------------o DB7
| | | Q3 |- 16bit --+ | | |
| | | | | | | |
| | | Q4 |-- | | | QS1 |-----+
| | | | | | | | |
| | | Q5 |-- | | | QS2 |-- |
| +----| CP | | | | | |
| | Q6 |-- | | +--------------+ |
+-------| MR | | | |
| | Q7 |-- | | +--------------------------+
| | | | | |
| | Q8 |-- | | | 74HC4094
| | | | | | +--------------+
| | Q9 |-- | | | | |
| | | | | +-----| D QP0 |-------------o DB8
| | Q10 |-- | | | |
| | | | +---------| CP QP1 |-------------o DB9
| | Q11 |-- | | |
| | | | Vcc o---| OE QP2 |-------------o DB10
| +--------------+ | | |
| | | QP3 |-------------o DB11
| | | |
| | | QP4 |-------------o DB12
| | | |
| | | QP5 |-------------o DB13
| | | |
| | | QP6 |-------------o DB14
| +-------------| STR |
| | | QP7 |-------------o DB15
| | | |
| | | |
| | | QS1 |--
| | | |
| | | QS2 |--
| | | |
| | +--------------+
| |
| | 74HC04
| +-------------------|>o--------------------o WR
|
| Vcc o--o RD
|
+-----------------------------------------------------------------------------o CS
This is an analysis of the circuits timing constraints (8-bit bus).
Constraints is examined for a SPI speed of 32 MHz.
See appendix for details about the 3.3 V values.
These are connected to the SPI clock:
IC Symbol Parameter Conditions Min Typ Max Unit
-----------------------------------------------------------------------------------------------------------
RPi SPI speed VCC = 3.3 V 32 MHz
74HC4040 fmax maximum operating frequency VCC = 3.3 V 20 55 - MHz
74HC4094 fmax maximum frequency VCC = 3.3 V 20 58 - MHz
32 MHz falls within the typical values, but fails the minimum values.
The 74HC4094 clocks in data on the rising edge of the clock.
The 74HC4040 counts on the falling edge of the clock.
On the 4th clock cycle: SCLK goes LOW -> 74HC4040 Q2 goes HIGH -> 74HC4094 STR goes HIGH -> shift register data goes to the outputs.
On the 8th clock cycle: SCLK goes LOW -> 74HC4040 Q2 goes LOW -> 74HC4094 STR goes LOW -> outputs are locked (latched).
If STR is HIGH, QPn changes when CP goes HIGH.
IC Symbol Parameter Conditions Min Typ Max Unit
----------------------------------------------------------------------------------------------------------
RPi twl pulse width LOW 32 MHz 16 ns
74HC4094 tpd propagation delay CP to QPn VCC = 3.3 V - 34 58 ns
74HC4040 tPHL, tPLH propagation delay CP to Q0 VCC = 3.3 V - 25 45 ns
Constraint
STR must go LOW before new data enters into the shift register: RPi twl + 74HC4094 tpd > 74HC4040 ttPHL
Typ: 16 + 34 > 25
Worst case: 16 + 34 > 45
Data is latched in on the rising edge of /WR.
After 4 clock cycles: STR goes HIGH -> 74HC04 nA goes HIGH -> 74HC04 nY goes LOW -> /WR goes LOW
After 8 clock cycles: STR goes LOW -> 74HC04 nA goes LOW -> 74HC04 nY goes HIGH -> /WR goes HIGH -> data latches in
SSD1289
-------
Symbol Parameter Min Typ Max Unit
---------------------------------------------------------------------------------------
tcycle Clock Cycle Time (write cycle) 100 - - ns
PWL Pulse Width low (write cycle) 50 - - ns
PWH Pulse Width high (write cycle) 50 - - ns
tDSW Data Setup Time 5 - - ns
tDHW Data Hold Time 5 - - ns
ILI9325
-------
18.3.1 Display Parallel 18/16/9/8-bit Interface Timing Characteristics (8080-I system)
Symbol Parameter Min Typ Max Unit
---------------------------------------------------------------------------------------
twc Write cycle 66 - ns
twrl Write Control pulse L duration 15 - ns
twrh Write Control pulse H duration 15 - ns
tdst Write data setup time 10 - ns
tdht Write data hold time 10 - ns
IC Symbol Parameter Conditions Min Typ Max Unit
----------------------------------------------------------------------------------------------------------
74HC04 tpd propagation delay VCC = 3.3 V - 13 25 ns
Constraints
Pulse Width Low and Pulse Width High is both 4 clock cycles long: 4 x SPI speed > 50 ns => SPI speed < 80 MHz
Data Setup Time is the time from the last bit is clocked in and available on the outputs, and to /WR goes HIGH: the 74HC04 inverter propagation delay is enough to satisify this.
Data Hold Time is the time from /WR is HIGH to data can change on the bus: this is 4 clock cycles since STR is LOW for so long: 4x32 > 10ns
There is 5 inverters left in the 74HC04 Hex Inverter IC. These can be used to drive a LCD backlight.
- Backlight pulled to +3.3V. It can deliver 4x25mA = 100mA
- Backlight pulled to GND. Remove the first inverter and put it in parallel. It can sink 5x20mA = 100mA
led o-----|>o----+--|>o--+-----o LCD backlight
| |
+--|>o--+
| |
+--|>o--+
| |
+--|>o--+
I tried this on the ITDB02-2.8 display, and it seemed to work fine (I really didn't try to connect it directly to +3.3V).
But on the Sainsmart 3.2 display, this circuit gave a rather dim backlight, compared to +3.3V wired. The backlight draws 46mA at 3.3V, but this circuit delivers only 18mA. I haven't had the time to check if I have a miswiring, or if this is all I can get from this setup.
The circuit is tested with this display (8-bit). I get ~17fps at 32MHz (parallel ~25 fps).
I haven't updated the itdb28fb driver to support SPI yet, so I use flexfb for now
modprobe fbtft_device name=flexfb gpios=reset:25,dc:24,led:18
modprobe flexfb debug=3 rotate=0 width=240 height=320 regwidth=16 setaddrwin=1 init=-1,0x00E3,0x3008,-1,0x00E7,0x0012,-1,0x00EF,0x1231,-1,0x0001,0x0100,-1,0x0002,0x0700,-1,0x0003,0x1030,-1,0x0004,0x0000,-1,0x0008,0x0207,-1,0x0009,0x0000,-1,0x000A,0x0000,-1,0x000C,0x0000,-1,0x000D,0x0000,-1,0x000F,0x0000,-1,0x0010,0x0000,-1,0x0011,0x0007,-1,0x0012,0x0000,-1,0x0013,0x0000,-2,200,-1,0x0010,0x1690,-1,0x0011,0x0223,-2,50,-1,0x0012,0x000D,-2,50,-1,0x0013,0x1200,-1,0x0029,0x000A,-1,0x002B,0x000C,-2,50,-1,0x0020,0x0000,-1,0x0021,0x0000,-1,0x0030,0x0000,-1,0x0031,0x0506,-1,0x0032,0x0104,-1,0x0035,0x0207,-1,0x0036,0x000F,-1,0x0037,0x0306,-1,0x0038,0x0102,-1,0x0039,0x0707,-1,0x003C,0x0702,-1,0x003D,0x1604,-1,0x0050,0x0000,-1,0x0051,0x00EF,-1,0x0052,0x0000,-1,0x0053,0x013F,-1,0x0060,0xA700,-1,0x0061,0x0001,-1,0x006A,0x0000,-1,0x0080,0x0000,-1,0x0081,0x0000,-1,0x0082,0x0000,-1,0x0083,0x0000,-1,0x0084,0x0000,-1,0x0085,0x0000,-1,0x0090,0x0010,-1,0x0092,0x0600,-1,0x0007,0x0133,-3
The circuit is tested with this display (16-bit). I get ~10fps at 16MHz (32MHz didn't work).
modprobe fbtft_device name=flexfb speed=16000000 gpios=reset:25,dc:24
modprobe flexfb debug=3 width=240 height=320 rotate=0 regwidth=16 setaddrwin=2 init=-1,0x00,0x0001,-1,0x03,0xa2a4,-1,0x0c,0x0004,-1,0x0d,0x0308,-1,0x0e,0x3000,-1,0x1e,0x00ea,-1,0x01,0x2b3f,-1,0x02,0x0600,-1,0x10,0x0000,-1,0x11,0x6030,-1,0x07,0x0233,-1,0x0b,0x0039,-1,0x0f,0x0000,-1,0x03,0x0707,-1,0x31,0x0204,-1,0x32,0x0204,-1,0x33,0x0502,-1,0x34,0x0507,-1,0x35,0x0204,-1,0x36,0x0204,-1,0x37,0x0502,-1,0x3a,0x0302,-1,0x3b,0x0302,-1,0x23,0x0000,-1,0x24,0x0000,-1,0x25,0xa000,-1,0x48,0x0000,-1,0x49,319,-1,0x4a,0x0000,-1,0x4b,0x0000,-1,0x41,0x0000,-1,0x42,0x0000,-1,0x44,0xef00,-1,0x45,0x0000,-1,0x46,319,-3
- Raspberry Pi SPI info
The datasheets doesn't mention values with a 3.3V supply voltage.
In the 74HC User guide theres is a graph: Fig. 7 Propagation delay as a function of supply voltage; Tamb = 25 °C; CL = 50 pF
From this we can find that: VCC = 3.3V gives a factor of ~1.5 relative to 4.5V which is 1
Excerpts
74HC4040
--------
Symbol Parameter Conditions Min Typ Max Unit
fmax maximum operating frequency VCC = 2.0 V; CL= 50 pF 6 27 - MHz
VCC = 4.5 V; CL= 50 pF 30 82 - MHz
Using factor 1.5 VCC = 3.3 V; 20 55 - MHz
Symbol Parameter Conditions Min Typ Max Unit
tPHL, tPLH propagation delay CP to Q0 VCC = 2.0 V; CL = 50 pF - 47 150 ns
VCC = 4.5 V; CL = 50 pF - 17 30 ns
using factor 1.5 VCC = 3.3 V - 25 45 ns
propagation delay Qn to Qn+1 VCC = 2.0 V; CL = 50 pF - 28 100 ns
VCC = 4.5 V; CL = 50 pF - 10 20 ns
using factor 1.5 VCC = 3.3 V - 15 30 ns
74HC4094
--------
Symbol Parameter Conditions Min Typ Max Unit
tpd propagation delay CP to QPn VCC = 2.0 V - 63 195 ns
VCC = 4.5 V - 23 39 ns
using factor 1.5 VCC = 3.3 V - 34 58 ns
fmax maximum frequency CP
VCC = 2.0 V 6 28 - MHz
VCC = 4.5 V 30 87 - MHz
using factor 1.5 VCC = 3.3 V 20 58 - MHz
74HC04
------
Symbol Parameter Conditions Min Typ Max Unit
tpd propagation delay nA to nY VCC = 2.0 V - 25 85 ns
VCC = 4.5 V - 9 17 ns
using factor 1.5 VCC = 3.3 V - 13 25 ns
Datasheets
- 74HC4040 12-stage binary ripple counter
- 74HC4094 8-stage shift-and-store bus register
- 74HC04 Hex inverter
I used this script to sort out some miswiring. Must be run as root.
Connect a probe wire to GPIO22. This will be used to test the different signals.
Bug:
The script uses the SPI bus lines as GPIOs. The SPI controller driver spi-bcm2708
stops working because of this.
A reload of the driver didn't solve it for me. A reboot was required.
It is also possible to test with: echo -ne "\xff\x00" > /dev/spidev0.0
import os
import time
import subprocess
import array
import sys
PROBE_GPIO = 22
SCLK_GPIO = 11
MOSI_GPIO = 10
CE0_GPIO = 8
def writef(file, val):
# print "%s <- %s" % (file, val)
with open(file, 'w') as f: f.write(val)
def readf(file):
# print "%s <- %s" % (file, val)
with open(file, 'r') as f: ret = f.read()
return ret
class GPIO:
def __init__(self, num, dir='out'):
self.num = num
self.val = 0
writef("/sys/class/gpio/export", "%s" % num)
writef("/sys/class/gpio/gpio%s/direction" % num, dir)
def close(self):
writef("/sys/class/gpio/gpio%s/direction" % self.num, "in")
writef("/sys/class/gpio/unexport", "%s" % self.num)
def get(self):
return int(readf("/sys/class/gpio/gpio%s/value" % self.num))
def set(self, val=1):
if val == 0:
self.val = 0
else:
self.val = 1
writef("/sys/class/gpio/gpio%s/value" % self.num, "%s" % self.val)
return self.val
def clear(self):
self.set(0)
def pulse(self):
if (self.val):
self.set(0)
time.sleep(1)
self.set(1)
else:
self.set(1)
time.sleep(1)
self.set(0)
class SPI:
def __init__(self, sclk_gpio, mosi_gpio, ce_gpio, probe_gpio):
self.sclk = GPIO(sclk_gpio)
self.mosi = GPIO(mosi_gpio)
self.ce = GPIO(ce_gpio)
self.probe = GPIO(probe_gpio, 'in')
self.ce.set(1)
self.sclk.set(0)
self.mosi.set(0)
def __enter__(self):
return self
def __exit__(self, type, value, trace):
self.close()
def close(self):
self.sclk.close()
self.mosi.close()
self.ce.close()
self.probe.close()
def start(self):
self.ce.set(0)
def end(self):
self.ce.set(1)
def clock(self, value):
# print("value: %i" % value)
ret = self.mosi.set(value)
self.sclk.set(1)
# time.sleep(1)
self.sclk.set(0)
# time.sleep(1)
return ret
def write(self, byte):
# print "spi.write %s" % bin(byte).ljust(10, '0')
for i in range(8):
self.clock( (byte << i) & 0b10000000 )
def write_buf(self, buf):
# sys.stdout.write("spi.write_buf: ")
for byte in buf:
# sys.stdout.write("%s " % bin(byte).split('b')[1].rjust(8, '0'))
for i in range(8):
self.clock( (byte << i) & 0b10000000 )
# print("")
def test_db(spi, bits):
found = 0
first_bit_set = -1
for i in range(bits):
val = (1 << i)
if bits == 16:
# Write Big Endian
spi.write_buf( [(val >> 8) & 0xFF, val & 0xFF] )
else:
spi.write_buf( [val & 0xFF] )
if spi.probe.get():
if first_bit_set == -1:
first_bit_set = i
# print "D%i\n" % i
found += 1
if found == 0:
print "No signal found\n"
elif found > 1:
print "Multiple bits set\n"
return first_bit_set
def databus_test():
global db_width
print "\nDatabus test"
print "------------\n"
with SPI(SCLK_GPIO, MOSI_GPIO, CE0_GPIO, PROBE_GPIO) as spi:
spi.start()
for i in range(db_width):
raw_input("D%i" % i)
bit = test_db(spi, db_width)
if bit != i:
print "Error: Signal is found on D%i\n" % bit
spi.end()
def rd_test():
print "\nRD test"
print "-------\n"
with SPI(SCLK_GPIO, MOSI_GPIO, CE0_GPIO, PROBE_GPIO) as spi:
raw_input("RD")
if not spi.probe.get():
print("Error: RD should be 1\n")
def clock_pulse_test_wr(spi, wanted_value, start, pulses):
for i in range(start, start+pulses):
spi.clock(0)
if not (spi.probe.get() == wanted_value):
print("Error: WR should be %i after clockpulse %i\n" % (wanted_value, i))
def wr_test():
global db_width
print "\nWR test"
print "-------\n"
with SPI(SCLK_GPIO, MOSI_GPIO, CE0_GPIO, PROBE_GPIO) as spi:
raw_input("WR")
if not spi.probe.get():
print("Error: WR should be 1\n")
spi.start()
if db_width == 16:
clock_pulse_test_wr(spi, wanted_value=1, start=0, pulses=7)
clock_pulse_test_wr(spi, wanted_value=0, start=8, pulses=8)
clock_pulse_test_wr(spi, wanted_value=1, start=16, pulses=1)
else:
clock_pulse_test_wr(spi, wanted_value=1, start=0, pulses=3)
clock_pulse_test_wr(spi, wanted_value=0, start=4, pulses=4)
clock_pulse_test_wr(spi, wanted_value=1, start=8, pulses=1)
spi.end()
# Aborted transfer
spi.start()
for i in range(db_width//2):
spi.clock(0)
if spi.probe.get():
print("Error: WR should be 0 after clockpulse %i\n" % (db_width//2))
spi.end()
if not spi.probe.get():
print("Error: WR should be 1 after aborted transfer\n")
def cs_test():
print "\nCS test"
print "-------\n"
with SPI(SCLK_GPIO, MOSI_GPIO, CE0_GPIO, PROBE_GPIO) as spi:
raw_input("CS")
if not spi.probe.get():
print("Error: CS should be HIGH before transfer\n")
spi.start()
if spi.probe.get():
print("Error: CS should be LOW during transfer\n")
spi.end()
def signal_test(spi, name, gpio):
raw_input(name)
gpio.set(0)
if spi.probe.get():
print("Error: %s can't be set LOW\n" % name)
gpio.set(1)
if not spi.probe.get():
print("Error: %s can't be set HIGH\n" % name)
def spi_test():
print "\nSPI test"
print "-------\n"
with SPI(SCLK_GPIO, MOSI_GPIO, CE0_GPIO, PROBE_GPIO) as spi:
signal_test(spi, "SCLK", spi.sclk)
signal_test(spi, "MOSI", spi.mosi)
signal_test(spi, "CE", spi.ce)
def menu():
print(" 0. Run all interface tests")
print(" 1. Databus test")
print(" 2. WR test")
print(" 3. RD test")
print(" 4. CS test")
print(" 5. SPI test")
print(" x. Quit")
print("")
t = raw_input(" Choice [0] ")
if t== "x":
return -1
if t== "":
return 0
else:
try:
t = int(t)
except ValueError:
print("%r is not known" % t)
return -1
return t
def main(argv):
global db_width
value = 0b11001101
count = 0
print("\nTests for SPI to LCD interface circuit https://github.com/notro/fbtft/wiki/SPI-interface-circuit\n")
print "Probe gpio: %i" % PROBE_GPIO
print "Move probe to the specified signal pin and press Enter\n\n"
input = raw_input("Databus width [16] ")
if input == "":
input = "16"
try:
db_width = int(input)
except ValueError:
db_width = 0
if not db_width in [8,16]:
print("Error: Only 8 and 16 supported")
return
t = menu()
if t == -1:
quit()
if t == 0:
databus_test()
wr_test()
rd_test()
cs_test()
elif t == 1:
databus_test()
elif t == 2:
wr_test()
elif t == 3:
rd_test()
elif t == 4:
cs_test()
elif t == 5:
spi_test()
else:
print("No such test")
if __name__ == '__main__':
try:
main(sys.argv[1:])
except KeyboardInterrupt:
print("")