# How can I make a UART receiver using logic devices (74164,counters,logic gates,..)?

I am trying to make a Serial-in Parallel-out Register to be controlled by my PC with logic gates and a 74164, 74193, 555 timer. I am programming it through visual basic and I have no problem making the serial communication, I even made a chat (in a prior application) with it and the data is sent correctly but I'm having some issues with receiving the data and keeping it with the logic. The idea is being able to control the data I output to then control a stepper motor.

This is the circuit I have developed so far, the 555 timer has 10,200 hz frequency the serial communication is 9600 bauds, the counter is going to stop at 9 and the clock is going to be activated when a 0 logic so it is starting when the start bit arrives. I have a NAND flip flop to store when to start and it is going to be deactivated when the counter reaches 9, the problem is it is really bugged and I am not sure why it is, when I send any data the 74164 displays 127 or 63 in decimal (0111 1111 and 0011 1111 respectively) and I would like to see if you could help me solving it.

• Is this a retro project? Discrete uarts were done like this 50 years ago. Anyways, RS232 is not TTL compatible and inverted. The clock must rise mid bit cell. Normally you’d have a clock 16x the baud rate and sample the start bit. With Arduinos only a couple of \$ , why would you do it the hard way? Are you using a simulator? Proteus? If so, use the simulator tools to see what is actually happening. – Kartman Feb 28 at 4:52
• It might be useful to draw a timing diagram so you can figure out what is going wrong. – ScienceGeyser Feb 28 at 8:27
• In the Proteus simulator the DB-9 is TTL compatible so i do not have to take that regarding the simulation, thank you for your answers! – Albert Luna Feb 28 at 14:55

tl; dr: You need to understand how UARTs actually work. You're missing a lot of stuff.

What you have designed thus far is a basic deserializer, with a kind of weird way of making the clock that depends on the data input.

Critically, it's failing to properly frame the input data and thus pick off the bits at the right time. And, your setup needs at least 1-byte of buffering (an output latch) to hold the completed RX byte when it's been received.

How do we frame the data? In actual UARTs, the RX waveform is sampled with a higher-rate clock (16x baud typically.) This sampling clock looks for the leading edge of the 'start' bit at the beginning of the transmission, then uses that to determine the optimal bit sampling points for the following bits.

More about that process here: https://www.maximintegrated.com/en/design/technical-documents/tutorials/2/2141.html

This means at the very least you need to rethink your clock and make a little state machine (that is, the framer) for detecting the start bit, aligning the sampling, counting the shift-in cycles to the shift register, then transferring the completed byte to the output latch.

All right, so we know about the start bit and what it's for. We also see that there's a stop bit. Why do we need that? One thing you'll notice is that the start and stop bits are opposite polarity. This guarantees that there's always a 0-1 transition between frames, so the framer can detect that edge and re-align the sample points. Meanwhile, your input shift register discards the stop bit (it carries no data), but the framer nevertheless needs it.

At minimum then, your receiver needs to support at least the basic 10-bit '8-n-1' frame (1 start, 8 bits, no parity, 1 stop) to be useful for RS-232.

Full-featured RS-232 UARTs also support options for variable data size (5-9 bits), parity (odd, even, or none) and additional stop bits (1-2). Most systems however don't care about anything but 8-n-1, the minimum format.

Now that we've framed the data and captured the data bits, it'd be useful if we presented the data to the host, one byte at a time. So we grab the state of the shift register once RX bit 7 (MSB) has been clocked in, and transfer that to a latch. Even better, we push it to a FIFO and have some flag logic indicating it's ready for the host to read.

Finally, external to this UART logic itself, we see that RS-232 electrical interface isn't logic level, but instead higher voltage (roughly +/- 12V) levels. And, the waveform is inverted. To use RS-232 with logic you need to add a chip like a MAX232 to translate the voltage down to TTL. On the other hand, if you're using local TTL levels then this isn't a problem.

Here's a UART design in HDL for example. This code defines basic receive and transmit functions. There are many others to be found; it's a popular project for FPGA learners.

Postscript

That all said, if your goal is to interface a peripheral, maybe you need to think about using a microcontroller with a UART. The Arduino Nano is a good choice for this, and it includes a USB-to-serial on board. This is much more convenient than trying to find a PC with an actual serial port.

• In a discrete design we shouldn't need oversampling, as long as we can reset the clock whenever a bit transition is detected. Oversampling is only important when you have a fixed-frequency clock. – user253751 Feb 28 at 13:42
• It has nothing to do with a free-running clock, or lack thereof. The OP design tries to crash-lock the clock with the start bit, then ignore the successive bits for crash-lock using a counter, and hope that the resulting clock captures the rest of the bits correectly. As it is, it doesn't do a good job of aligning the sampling, which is why it fails. Sure, there's analog methods like one-shots that could possibly make an aligned pulse, but a digital method is simpler, more predictable, and can support multiple baud rates, which is why UART ICs use this method. – hacktastical Feb 28 at 18:11
• BTW, how did 16x get to be the standard sampling rate? As opposed to, say, 8x or 12x? Did it have to do with the characteristics of the actual waveform as sent over a line, combined with "typical" clock mismatch due to clock generation inaccuracy? I mean, is it somehow determined to be a worst case thing that you need 16x and 12x just won't do? Or something else? – davidbak Feb 28 at 18:19
• The higher the over sampling, the more accurately the clock sample can be placed in the middle of the data window. If I had to hazard a guess, 16x is a convenient power of 2, and they found 4x or 8x to be insufficient. – hacktastical Feb 28 at 18:25
• @hacktastical: Actually, odd sampling rates are better than even sampling rates, since when using a odd sampling rate the 'ideal' sampling time would be halfway between two clocking events, yielding a symmetrical acceptable timing window. Given how many systems use clocks that are a multiple of 1MHz, I find it odd that 13x divide ratios aren't more common, since 1MHz/26 would yield 38,400. – supercat Feb 28 at 22:12

Interesting circuit, and in fact, you're very nearly there. I see three problems.

1. If your input is really RS-232 and not TTL (as implied by your 9-pin connector), then you need a circuit to convert RS-232 levels to TTL levels. Back in the day, this would have been the 1489 RS-232 line receiver chip, but there are more modern alternatives today.

2. Your timing is way off. You want the 555 to generate a rising edge in the center of each data bit. The first rising edge is going to occur immediately upon coming out of reset, but then you want the next rising edge to occur 156 µs after that, and each subsequent edge to occur 104 µs after the previous one.

Fortunately, because of how the 555 works, it is possible to achieve this combination of timing. Some simple algebra reveals that R6 needs to be 4.722 times the value of R5. For example, if R5 is 1.0 kΩ and R6 is 4.7 kΩ, a capacitor value of 22.4 nF will give you the time intervals you need.

simulate this circuit – Schematic created using CircuitLab

If you run the simulation of the above circuit1, you'll see that the interval between the first two rising edges is about 156 µs, and the interval between rising edges after that is about 104 µs. This happens because the timing capacitor must charge all the way from zero in the first interval.

1. You're relying on a "glitch" to reset the circuit after each byte of data. It might be better to use the output of your R-S latch to drive the reset to the counter, although this creates a potential race between coming out of reset and the first rising edge from the 555.

I would suggest a slight modification to your circuit. Get rid of the NAND gates and instead use half of a 7474 D flip-flop.

simulate this circuit

When U3-Q is high, the circuit is idle. The start bit resets U3, allowing the 555 to run and the counter to count. The counter counts 1 to 8, and then on the ninth clock, U3 is set again. If you don't want a whole lot of "noise" on the parallel outputs while the data is being shifted in, add an 8-bit latch that is controlled by U3. Or switch to the 74595, which has such a latch built in.

1 Note that I had to tweak the capacitor value in the simulation in order to make the numbers come out right. In practice, you'll want to select a standard value for the capacitor, and replace the upper timing resistor with the combination of a 2200 Ω fixed resistor and a 5000 Ω trimpot set to about its midpoint. Adjust as needed to get the clock edges where they need to be.

• Shouldn't I achieve a delay of the 52u seconds and then start the 555 timer? – Albert Luna Feb 28 at 15:44
• It would be good to show on a scope trace how end-of-frame would need to be handled to ensure that the starting voltage is low enough to extend the length of the first pulse adequately. – supercat Feb 28 at 17:32
• @supercat: Note how much faster the discharge curve is in the simulation. The width of the stop bit is more than enough to prepare the circuit for the next start bit -- 104 us = 4.6*RC. But that does raise a point that I hadn't considered: if the last data bit is zero, it will prevent the circuit from entering the idle state at all. Hmm, back to the drawing board! As shown, the circuit is only good for 7-n-2 data. – Dave Tweed Feb 28 at 19:25
• @DaveTweed: Whether the stop bit is adequate would depend upon when one decides to reset the 555. If one resets the 555 after the last data bit, and won't detect a start condition until a falling edge on the data line, there would be plenty of time. If one doesn't reset the 555 until the nominal middle of the stop bit, the oscillator is running 3% slow, and the there is zero delay before the next start bit, the timing might be a bit tight, especially since the slop will flatten out as the voltage drops. – supercat Feb 28 at 22:03
• @DaveTweed: I don't doubt that the general design could be made to work, but a proper design should include an analysis of things like frequency tolerance, which would require seeing how the system behaves between the end of one byte and the start of the next. – supercat Feb 28 at 22:05