1
\$\begingroup\$

Say I have an N-bit display driven with a series of interlinked HC595-type shift registers.

I'd like to drive this using only two wires (Data/Clock; assume power and ground are otherwise present). In other words, I am looking for a design such that when a '1' "spills" from the Nth bit of the shift register bank, it trips the latch on the registers and resets all of their "working" bits to '0'. (Okay, realistically, this will probably only work with N being a multiple of 8, and padding my actual data accordingly. That's fine. Assume henceforth that N is a multiple of 8.)

The idea is that I push N+1 '0's into the setup (i.e. pulse the Clock line N times) to reset it to a known state, after which every N+1 bits (the first must be '1') will replace the data and trigger the outputs to update.

How might I accomplish this?

(Note that I have full control over the C/D lines, and can do 'tricksy' things with timing if it helps. The real goal is a) to avoid needing a third logic line and/or a full MCU on the receiving end, and b) to have fully synchronous display updates, i.e. why I'm not just using something like a TM1637.)

\$\endgroup\$
10
  • \$\begingroup\$ The '595 chips can be wired up so their RCLK inputs are all tied to the last '595 \$\overline{Q_H}\$ output. Driven appropriately, what about that doesn't describe what you want? \$\endgroup\$ Commented Feb 20 at 0:25
  • \$\begingroup\$ (Sorry, that's not \$\overline{Q_H}\$ but \$Q_H^{\:'}\$ on pin 9.) \$\endgroup\$ Commented Feb 20 at 0:48
  • 1
    \$\begingroup\$ they make LED driver chips exactly like that. EG: MM5450 \$\endgroup\$ Commented Feb 20 at 9:45
  • \$\begingroup\$ Why not use a serial LCD? That's only 1 wire! \$\endgroup\$
    – bobflux
    Commented Feb 20 at 10:38
  • \$\begingroup\$ @periblepsis, that works for the first update. How do I prevent RCLK from getting pulsed as old data bits are shifted off? I also need a way to reset the register(s) so that RCLK doesn't get pulsed again until a full N bits has been received. \$\endgroup\$
    – Matthew
    Commented Feb 20 at 17:06

6 Answers 6

2
\$\begingroup\$

A tiny microprocessor counting clock pulses and activating the reset/clear input on all the shift registers would work. A suitably-sized counter IC and perhaps a bit of logic would also accomplish what you are looking to do at the cost of a bit more board real estate.

\$\endgroup\$
3
  • \$\begingroup\$ Hmm, counting pulses would remove the 'N must be a multiple of 8' problem at the cost of some additional complexity. Not bad. I also actually like where your head is at challenging the "don't use an MCU" requirement. I'm probably going to post a related solution that I thought up independently... \$\endgroup\$
    – Matthew
    Commented Feb 20 at 17:26
  • \$\begingroup\$ These days you can get a micro in a SOT-6 with no external components (bar a ceramic cap on the power supply) that does the work of a half dozen discrete logic chips while costing less than any one of them. \$\endgroup\$
    – vir
    Commented Feb 20 at 17:29
  • \$\begingroup\$ Exactly! The reason I "don't want" an MCU is a) because I don't want to stick something in between the upstream MCU and what's actually driving the display elements, and b) because I don't want to add a large-ish component. But as you note, it doesn't need to go between, and SOT-6 isn't "large". \$\endgroup\$
    – Matthew
    Commented Feb 20 at 17:32
1
\$\begingroup\$

A single retriggerable monoflop is all you need. When clock is absent long enough, the monoflop resets all shift registers, but not the storage registers of course. When the clock resumes, the shift register reset is lifted. At that point, the shift registers hold zeroes. Shift in 0000'0001 before the display data. The first zero will be dropped due to shift register reset being active. After that clock edge, the monoflop activates, reset is dropped, and the rest of the bits will be shifted in. When that 1 spills out the end of the last register stage, it can be used to latch the data in the storage registers.

Here's how it would look:

schematic

simulate this circuit – Schematic created using CircuitLab

  • NOT1 and NOT2 buffer the clock signal so that the load on MOSI and SCLK is just a single 74HC input.

  • NOT3 recharges C1 via R2, and isolates C1's load from the rest of the clock circuit.

  • NOT4 and NOT5 threshold and buffer the reset signal from C1.

  • NOT6 discharges C1 and triggers a reset when the final stage's Q7S output goes high.

The monostable formed by D1,R1,C1,NOT1,NOT2 has a 10μs time constant. When the clock is absent for more than about 15-20μs, the shift registers get reset.

Once the 1 reaches Q7S on the last daisy-chained stage, two things happen:

  1. The storage registers latch the contents of the shift register.
  2. After a short delay, MR gets activated, the shift registers are cleared, and the shift registers are primed for next run of data.

To send the data to the display:

  1. Transmit 0x00, 0x80. This will clock some zeroes into the shift register, and finally will clock the "header" 1.

  2. Transmit as many data bytes as there are HC595 chips in the chain.

  3. Immediately, or at any later time, repeat from step 1.

This scheme is resilient in face of a few missed clock transitions, so it's self-synchronizing. If a "sync recovery" reset is needed, don't transmit any data for about 2 R1-C1 time constants. The time constant can be adjusted per requirements.

It is also possible to use a self-clocking data encoding, where only a single data line would be needed, without separate clock. I leave that as an exercise for the reader :)

\$\endgroup\$
4
  • \$\begingroup\$ I understand the leading 'scratch' zero bits to give enough time to unassert RESET... but why seven? Why not just one or two? \$\endgroup\$
    – Matthew
    Commented Feb 20 at 17:21
  • \$\begingroup\$ Because SPI usually sends whole words of a fixed width. So if you have display data in an array, you can set the DMA chain so that one byte is sent from the constant data section, then the contents of the display array, then another byte from the constant data section to finish things off. Sometimes more is less - with SPI peripheral, the unit of data is a word/byte, not a bit. \$\endgroup\$ Commented Feb 20 at 17:37
  • \$\begingroup\$ Okay. I haven't looked into whether I can use low-level hardware logic for data transmission; I was planning to just bit-bash it myself. Anyway, IIUC SPI needs four wires? \$\endgroup\$
    – Matthew
    Commented Feb 20 at 17:43
  • \$\begingroup\$ @Matthew It only needs four wires if it's bidirectional and multiple logical slaves are on the bus. For a single slave, unidirectional transmission, SCK and MOSI is all you need. The whole point of this exercise was to make your life easier. Bit-bashing is not necessary in this case at all, unless your CPU has no SPI and nothing that approximates it. Say, if you're using a naked Z80 or something :) \$\endgroup\$ Commented Feb 25 at 22:46
0
\$\begingroup\$

One option would be to connect the RCLK lines of all the shift registers to an RC circuit connected to the clock line. The time constant of the RC circuit is much greater (say 10x) than the clock frequency.

Before transmission, the clock line is held low. Data is then clocked in at a high enough rate that RCLK does not go high, perhaps using a low duty cycle clock. When all the data has been sent, a long clock-high pulse is sent (or perhaps the last clock pulse is extended, I'd have to simulate it to check). Finally, the clock line is returned low for the next cycle.

\$\endgroup\$
0
\$\begingroup\$

After some noodling, this is the solution at which I arrived:

This works for N data bits where N = (8*K-1), e.g. 7, 15, 23, 31, ... It requires that the last bit passed into the shift register bank is '0', as it leverages changing SDI

Glossary

Manufacturers can't decide what to name their pins. I'm using these net names:

  • SDI: Serial Data In. Directly connected to the SDA pin of the upstream MCU. Connected to the DS/QA pin of the first shift register.
  • SCLK: Serial Clock. Directly connected to the SCL pin of the upstream MCU. Connected to the SHIFT CLOCK pin(s) of all shift registers.
  • LCLK: Latch Clock. Connected to LATCH CLOCK pin(s) of all shift registers.
  • CARRY: Overflow ("carry") bit from the entire shift register bank. Connected to SQH/SQ7 of the last shift register.
  • !RST: Reset. Connected to the RESET pin(s) of all shift registers. Note that this is normally-high, and is pulled low to clear the shift registers.

Implementation

Our goal is to do two things when CARRY goes high:

  1. Pulse LCLK
  2. Pulse (briefly pull low) !RST

The main trick is that we want to do these sequentially to ensure that the bits have time to move from the shift register to the latch (output) register before the shift register is reset.

To accomplish this, we use CARRY to enable to circuits that are normally not connected:

  1. SCLK to LCLK
  2. SDI (inverted) to !RST

Both of these are enabled when CARRY is high. The second is additionally enabled if SDI is high, but only if it was previously enabled. (This is the tricky part and is left as an exercise for the reader.)

Our sequence, then, is to send a '0' bit which causes the 'magic' bit to spill into CARRY. This will in turn trigger SCLK to bring LCLK high, which copies the input buffer into the output buffer. At this point, because we stipulate that the final bit is '0', SDI is low, and so !RST is still high.

Now we set SCLK back to low, then set SDI to high. Because CARRY was still set, SDI and !RST are still connected, and SDI will now keep that path connected regardless of CARRY. This also brings !RST low, resetting the shift registers and eventually bringing CARRY low.

Finally, after a delay to ensure that the registers have fully reset, SDI is brought low again, disconnecting the SDI-to-!RST path. This leaves us with the registers fully zeroed and ready to accept new data.

Sequence Detail

A timing diagram is almost certainly helpful:

timing diagram

CC and CR represent whether the circuits connecting SCLK to LCLK, and not-SDI to !RST, respectively, are enabled.

A more specific event sequence is:

  • set SDI to low (bit 32)
  • set SCLK to high (causes the shift buffer to accept bit 32 and shift out the original 'magic' 1 bit)
    • CARRY goes high
    • SCLK to LCLK is enabled
    • not-SDI to !RST is enabled via CARRY
    • LCLK goes high
  • set SCLK to low
    • LCLK goes low
  • set SDI to high
    • not-SDI to !RST is enabled via SDI
    • !RST goes low
    • CARRY goes low
    • SCLK to LCLK is disabled
  • set SDI to low
    • not-SDI to !RST is disabled

Usage (MCU logic)

In pseudocode, the MCU logic looks like:

// 'hc' is half the clock time
for each data bit // 1 nnnnnnn 0 nnnnnnn n nnnnnnn 0 nnnnnnn
    write(SDA, bitvalue)
    write(SCL, HIGH)
    delay(hc)
    write(SCL, LOW)
    delay(hc)
write(SDA, LOW)
write(SCL, HIGH) // "bit 32"
delay(hc*2)
write(SCL, LOW)
write(SDA, HIGH)
delay(hc*3)
write(SDA, LOW)
delay(hc)
\$\endgroup\$
0
\$\begingroup\$

Don't be silly. Use an MCU.

Why don't you (I) want to use an MCU?

  • I really, really want synchronous updates. Latching shift registers are about as synchronous as is physically possible, since all output bits get set in parallel.
  • Achieving something similar without shift registers requires something with at least N parallel outputs. An MCU with that many outputs is big, harder to work with, and unless it can do parallel updates on N pins at once, updates are only almost-synchronous.
  • I don't want to shove an MCU in between the MCU that's already upstream and feeding whatever actually drives the display. I also don't want to deal with the programming of something that's essentially acting as a bit relay.

...but vir has the right idea; don't stick it in series, stick it in parallel. (I'm going to give vir credit even though I came up with this independently and my take is slightly different.)

Now, I could do what vir suggested and count data bits. If N was terribly mismatched from the number of shift register bits (which must be a multiple of some power-of-two; in my case, given I'm probably going to use HC595s, it must be a multiple of 8), that might be worth the extra (programming) complexity.

Since we're sticking to my original plan of abusing the final carry bit, using an MCU is dead simple. One DIO pin connects to the final carry, one to LATCH CLOCK, and one to RESET. The logic is:

flush:
    write(P_LATCH, HIGH)
    delay(T_LATCH)
    write(P_LATCH, LOW)
    write(P_RESET, LOW)
    delay(T_RESET)
    write(P_RESET, HIGH)

main:
    // set pin modes
    write(P_RESET, LOW)
    delay(T_STARTUP)
    write(P_RESET, HIGH)
    set_interrupt(P_CARRY, RISING, flush)

That's it. Dead simple MCU logic that's totally independent of the upstream MCU feeding the shift registers. (Note that P_RESET is the output pin connected to the shift register reset pins, not the MCU's own reset pin.)

This approach avoids all the reasons why I didn't want to use an MCU, and because it's very amenable to being implemented with something like an ATtiny4/5/9/10, the footprint (SOT-23-6!) is tiny. Realistically, the BOM cost for a small-run project is probably superior with this approach, the circuitry is dead simple, and the probability of any other approach needing more board space is extremely high.

\$\endgroup\$
0
\$\begingroup\$

Have the carry out bit on the last shift registers trigger the load latches input and then on the next clock edge reset the shift registers.

STCP gets ( SHCP & Q7S )

MR gets ~( ~SHCP & Q7S )

you may need to stretch the Q7S pulse a little to ensure a clean reset.

\$\endgroup\$
5
  • \$\begingroup\$ (Ugh, why can't people agree on a common, and reasonable set of pin names. "STCP" is awful. TI's "RCLK" is better, but the difference between "RCLK" and "SRCLK" still requires thought. Onsemi has the right idea in naming it "latch clock". 😛) \$\endgroup\$
    – Matthew
    Commented Feb 20 at 17:36
  • \$\begingroup\$ I think you're close, but I don't trust pulsing LC and R at the same time. I'm going to post the solution I puzzled out that's based on a similar idea but with a little extra trickery to offset the reset from the latch. \$\endgroup\$
    – Matthew
    Commented Feb 20 at 17:38
  • \$\begingroup\$ not same time... opposite clock phases, but you could add an extra bit of shift register which is immune to the reset signal and use that to power the reset. thus allowing the clock to run at the maximum speed all the time. \$\endgroup\$ Commented Feb 20 at 19:09
  • \$\begingroup\$ Ah, okay, I think I follow. I also think you're right that you'd almost certainly need to "stretch" Q7S for reliable behavior. But I'd want to see a timing diagram, because it still seems like MR could see a 'flicker' when Q7S goes high but before that propagates to STCP. \$\endgroup\$
    – Matthew
    Commented Feb 20 at 19:49
  • \$\begingroup\$ I made a typo typo MR gets ( ~SHCP & Q7S ), guess I should have picked a data sheet that used more sensible names. SHCP goes high before Q7S does so there's no risk of a runt reset (MR) pulse before the load (STCP) pulse. \$\endgroup\$ Commented Feb 21 at 1:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.