So as you'll recall, we can have two devices or two ICs. We have a data line and the clock line. We haven't OK encoding scheme, but we have the cost of the extra clock line. So can we do better than this? One idea is, what if we get rid of the clock line and just use transitions to reset our clocks? So if you think about, it the receiver sitting there and it's receiving these data signals and there are bit transitions in the data signal, and so the receiver kind of knows some information about the sender's clock. Because it knows when there's a transition from a zero to a one when there's a leading edge, that's a sort of a bit transition. So one simple mechanism for a phase lock loop is to reset your clock whenever there's a bit transition. This is a good scheme. Sometimes the scheme is used. But the problem is, what if there's not enough transitions? What if you have a really long string of zeros? Then you're not going to have any transitions and your clocks will drift. So that's a problem. So what can we do to deal with that? Well, one idea is, what if we force there to be transitions every so often? The way to do that is an approach called Asynchronous Communication. The idea here's what we're going do is, we're going to take our data, we're going to take the string of bits we want to send, but we're not going to just send some really long string of bits, instead we're going to break our data into messages. We're going to add to the message a header and a trailer. We're going to design the header and trailer such that there's bit transitions in it. We're always going to have bit transitions in the header and the trailer. By doing this, we ensure that no matter what data we're sending, we're sending long strings of zeros or ones or whatever. The fact that we have these guaranteed transitions that are going to happen every so often place balance on the amount of clock drift that we can have. So asynchronous communication is good because we have these kind of little messages. We ensure that clocks are synchronized before the message starts. So we always have a bit transition occur before the message starts. So one advantage of this is, if we have an environment where we have devices that don't communicate very much. Imagine like a keyboard or a sensor that reads something once an hour. We don't need to pay the cost of keeping clocks in sync over long periods of time. We can just sync them up right before we send the message. So this is the idea behind asynchronous communication. Communication is asynchronous, you let clocks kind of drift and get out of sync, then you kind of sync things up when you need to. So the way these techniques are usually designed is there's kind of a bunch idle period and there's a start bit and a stop bit. You'd need to agree on parameters for this in advance, how many Start bits are there going to be, how many stop bits, what speed are you going to send at, your endianness, whether it's full of half-duplex all this stuff. But you can do that, that stuff is usually configured. So this has a lot of advantages, you don't need continuous synchronization. So it leads to a cheaper hardware better for power usage. But there are some downsides with this too. This requires a fair bit of overhead because you're sending these bits, but then you're sending extra bits that don't really provide useful data. You're sending idle bits and start bits and stop bits. So even though there's downsides, this approach is very widely used. It's used in the context of RS232. If you've heard of that, that's kind of like an older communication scheme used between computers. But it's still widely used in the context of IoT and UART and PPP and so on. So this approach is widely used but it has some downsides. So we may not want to pay the cost of low overhead in certain situations. So there's other encoding schemes that we use too which I'll talk about. So another category of encoding schemes is known as synchronous encoding schemes. The idea here is, you are going to have your clocks remain in sync all the time. The way you're going to do that is you're going to force there to be more transitions. You're going to force those transitions to occur very regularly every so often and bound your clocks so they don't get out of sync. As I go through these examples, suppose we have a bit pattern like this, and I'm going to show you how we encode these bit patterns using these different encoding schemes. So one kind of very popular encoding scheme is known as NRZ, Non-Return to Zero. This encoding scheme is actually basically what I've been talking about so far, you do a high-voltage for a one and a low voltage for a zero. So it's kind of what we've been seeing. So if I have my data, 0100 and so on, what I'll do is, I'll have something like this, where I'll have a low voltage for zero, high-voltage for a one and so on. But as we discussed, long strings of zeros, long strings of ones can fool this scheme. It can cause the clocks adrift and get out of sync. So an alternative scheme is called NRZI, which is Non-Return to Zero inverted. The idea behind this approach is, what we're going to do is we're going to encode bits that's transitions. We're going to have a transition between high and low voltage if it is a one and no transition if it's a zero. Okay. So for example, for this particular bit pattern, what we're going to do is we're going to start out, we're going to be at zero. We're going to come in and it is a zero. So are we going to transition? No, we're not going to transition because it's a zero. So what we're going to do is, we're going to kind of be straight. Okay. Then the next bit is a one and we transition for a one. So what we're going to do is we're going to transition to a high voltage for the one. Then the next bit is a 0. So what do we do? Do we stay straight or do we go up and down? Well, it's a zero, so we don't transition, so we're going to be straight. Then there's another zero. So what did we do here? Well, it's another zero, so we're going to go straight again. Then there's a one, so we're going to transition and so on. So what we do is, we transition between high and low voltage if it's a one and we don't transition if it's a zero. So that's NRZI, Non-return-to-zero inverted. So NRZI has some benefits. It ensures that if you have a long sequence of ones, you're encoded scheme doesn't get full. You have a long sequence of ones, you're going to get transitions on everyone at those ones. So that's nice. Unfortunately, long sequences of zeros can still cause clocks to drift because you don't get transitions when there zeros. So it's a little bit better for ones but it doesn't solve the problem of zeros. So another encoding scheme is called Manchester. So Manchester is very widely used. It's used for wired ethernet for example. The way Manchester works is, you're going to have transitions on both ones and zeros. You're going to transition up for a one and you're going to transition down for a zero. So this is neat because it ensures that if you have long sequences of ones, long sequences of zeros or even arbitrary mixes of them, you're guaranteed to get transitions. You're going to get transitions on every cycle. So the way this looks is, you're going to start out and where you start depends on your assumptions, what dates came before, but let's kind of startup high. So you do an up transition for a one and a down transition for a zero. The first bit is a zero. So what are we going to do? A we going to go up or down? We're going to go down because it's a zero. In the next bit is a one. We're going to go up or down? We're going to go up because it's a one, we will always go up when it's a one. Then the next bit is a zero, so we always go down when it's a zero. In the next bit is a zero, so we're going to go down. But how can we go down? We're already down, we're already at the lowest voltage we can send. So what can we do? Well, with Manchester you're allowed to transition before the bit. So in Manchester what you would do is you would go up so you can go down, because you have to go up to go down. That's your only choice. So this is how you encode that next zero. Then there would be a one, so we go up for the one, down for the zero. You up for the one. Here we kind of have the same problem again. There's another one. You can't go up again, all these encoding schemes have a maximum and minimum voltage. You're not going to go higher than the maximum voltage. So in order to go up again you have to kind of add an extra down transition, so you can go up again, and so on. So this process kind of continues out. I'm going to draw using the innovation scheme kind of see it. So this is Manchester. Manchester is nice because you have a lot of bit transitions, keeps your clocks in sync very well. But there's a problem with Manchester, and the problem with Manchester is you may have to transitions per cycle. So because of this, you have higher bandwidth requirements. You need more expensive electronics because you have to build a electronic that switch faster. So your electronics can get more expensive. So you'd typically see things like Manchester in kind of high-end situations, like 10 gigabit ethernet where you're willing to spend tens of dollars or even like a $100 every network card. So in those situations you see that if you're doing something lower performance, you're building a little sensor that sends a little bit of data once in a while, you might not use Manchester, you can use these other schemes. Then you build reliability on top of it by using CRCs, and Checksums, and things like that. Use TCP to recover packets and stuff like that. Because even if you have bit transition, you can always recover higher layers. Okay. So these are three really common encoding schemes. They're used all over the place, all sorts of different IoT protocols and various communications protocols. Next, I'm going to talk about three other encoding schemes which are also very popular and also very widely used. The first of those is known as Differential Manchester. So Differential Manchester is like Manchester. You're going to transition up and down, but what you're going to do is, you're going to repeat the previous transition if it's a zero, and you're going to invert the transition if it's a one. So if you think about it, this is a nice scheme because it works if the lines are swapped. If you are encoding voltage changes as differences between two wires, which a lot of physical realizations of encoding do, if you actually plug the cable in upside down or invert the wires back sent, this scheme is unaffected because it's looking at the difference or the changes between high and low voltages. So in particular, what we're going to do with Differential Manchester is, we're going to start out, let's assume we start high. So we're starting at up. Then it's a zero, so we're going to repeat the previous transition if it's a zero. We're assuming that's an up transition. So we have to go down in order to go up, something like that. So that transition was up transition. Now, we're going to hit a one. So with a one, you invert the previous transitions. So that previous transition was an up transition. So we need to invert that. So we're going to do a down transition because this is a one. Then we're going to hit a zero. So when zeros, you repeat the previous transition. So with the zero, we're going to go down again. You have to go up to go down. Then with the next zero, you're going to repeat the previous transition which is down transition. Then next, we're going to hit a one. So are we going to go up or we're going to go down? With a one, so you want to invert the previous transition if it's a one. So the previous transition was down, so we're going to go up. So the way this is actually implemented is, you have some logic that remember a single state, which is the previous transition. Was it a up or a down transition? Then it compares the current transition to the previous transition. If it was different, then it's a one. If it's the same, it's a zero, and so on. This process continues out, so we end up with something that looks like this. So this is Differential Manchester, another widely used scheme. So everything I've shown you so far is purely digital and very binary. You can have zero volts and five volts or zero volts and 3.3 volts. You have only two states. Another approach would be to allow there to be multiple states in your transitions. Here's an encoding scheme that does that. It's called Bipolar alternate mark inversion. It's similar to Manchester in the sense that you can have voltage coming in, you have these transitions. But what we're going to do is, we're going to alternate between multiple levels. In particular, we're going to have three levels. Zero is going to be no voltage, and ones are going to alternate between positive and negative voltage levels. So the benefit of this is, we don't really need a faster clock. We don't have to have more expensive components that have to send higher speeds or force multiple transitions per clock cycle, which is nice but at the same time we have to handle multiple voltage levels. So that waste capacity in some sense because we could use those alternate levels to encode more information than just two bits. So the way this is going to look is something like this. We're going to start out assuming a zero, and zero is no voltage, so we are going to stay at zero volts. Then we're going to be sending a one, and one's alternate between positive and negative voltage levels. So are we going to stay the same, are we going to go up, or are we going to go down? What do we do here? Well, the answer depends on what the previous one was. So we need to make some assumptions. Maybe the previous one was a down, was a negative voltage. So that means this one is going to be an up voltage. So we're going to assume the one using a positive pulse. Then we're going to have a zero which is a zero volts. We always send zero volts for zero. Here's another one. So how did we send this one? Do we send positive voltage, negative voltage or no voltage? Well, ones alternate between positive and negative voltage levels. So we're going to send a negative voltage because the previous pulse for one was a positive pulse. So we're going to send a negative one. Then we have a zero, and zero is are always zero volt. Then we're going to have another one and we're going to go up because we're going to invert the previous pulse. Then here we have another one. So what are we going to send here? Well, we're not going to send zero because it's not a zero. We're going to send a negative voltage for that one. Then the zeros are zero volts. So you continue this process out and you end up with this. So that's bipolar alternate mark inversion. There is another variation on that which is just opposite of that, is called Pseudoternary. It's the opposite of bipolar AMI, where one is no voltage and zeros alternate between positive and negative voltage levels. So you choose between these based on, are going to expect a lot of zeros, or a lot of ones, or what does your data look like? But that's going to look like this. If you look at the zeros, the first zero is going to be positive, the next zero is going to be negative, and so on. All the ones are flat, all the ones have no voltage. So we talked about a bunch of approaches that are used, very commonly used, and they work well. They're used all over the place, but they have some trade-offs. So it turns out there's other encoding schemes that we can use that have other benefits. So next, I'm going to talk about a few other schemes that have other benefits. One other approach to doing this is instead of trying to force there to be transitions every so often and changing they encoding scheme per bit, what we can do is, we can think about code words. We can take our message and break it up into chunks, and then thinking about each chunk in isolation and try to design a encoding scheme that thinks about the different chunks and how they match together to ensure that we don't have these long strings of ones and zeros, which really mess up DC balance and clock synchronization and all these other issues. So one way to do this is to use the notion of codewords. Take pieces of your message and then translate those into other desirable pieces that have desirable properties. So for example, suppose you want to send a message. Maybe this message. We have a bunch of ones and zeros and long string of zeros, but why is there a long sequence of zeros? How did that happen? Well, one way to think about it is, your message is composed of a sequence of words that the sender is sending. The sender sent a bunch of 0000 messages. So what we're doing is, we take care message and think about the message as consisting of a bunch of logical units, individual messages of four bits in length. There's a long sequence of zeros just because there is a long sequence of 0000 messages. So one way to fix the problem is, what if we just don't allow the sender to send 0000? Then the problem would go away because every other word that the sender could send would have a one in it. So that would guarantee we'd get a one every four bits, at least. So if we disallowed the sending of 0000, that would be great. The problem with that is, we're not allowing the sender to send certain data and that's messed up. I mean, what if the sender wants to send that data? So that would not be very desirable, but one other thing we could do which is practical is, we could do a translation. So we could have a dictionary where we map four-bit code words onto five-bit code words. So what if we just a pin to one to every message, this is one simple way to do it. Let's translate every four-bit message into a five-bit message and just add a one to every message. This immediately fixes the problem because then when we send our message, it's impossible to have a long sequence of zeros. So what we can do is, we can send this message, we're guaranteed to have a bunch of extra ones. We've corrupted the message by injecting all these extra events, but that's easy to fix this. The receiver can just throw away every fifth bit and get the original message back. So this is one valid approach. But it's not used because it's not as efficient as it could be, because we entirely lose one-fifth of the bits of capacity. Every fifth bit is just not useful for sending data. We can still have long sequences of ones, so we don't really fix that problem either. So a better approach is known as 4B/5B. The idea behind this approach is we're going to choose the code word smarter. We're still going to encode every 4-bit word as a 5-bit code word, but we're going to choose the code words in a smart way. We're going to sit down and come up with a mapping such that each code word has at most one leading zero and it moves two trailing zeros. Each codeword is also never going to have more than three consecutive zeros. We're going to transmit using NRZI. The neat thing about this is, if you do this, this bounds the number of consecutive ones and consecutive zeros that you could have in your coding schemes. It ensures that you have at least two transitions on each codeword. So it ensures the clocks remain in sync. It does waste some data still. So 16 of the 32 possible codes are used for data. But you can use the other codes for other things. You can send command characters. You can be sending your data and then maybe you want to change the baud rate or you want to reset the communication or tear down the link. There are certain kind of control operations you might want to do over your communication channel. You can do that with 4B/5B without needing any extra transmissions. You can encode those using the existing codewords. 4B/5B is used a lot, especially in lower in communications where you have low power, low battery life, you want to do something simple. There's variants of it which are used in other domains. There's 8B/10B, which is used a lot in digital audio, 64B/66TH is used in 10 gig ethernet,128B/130B are used in PCI and USB. This encoding scheme is just different. Instead of using 4-bit to 5-bit codewords, we're using 8-bit to 10-bit codewords, or 64-bit to 66 bit codewords. But it's really the same idea, you're taking words and you're translating them into codewords. Okay. So let me give an example to make this more clear. So this is the encoding table that we use for 4B/5B. It shows you for every 4-bit codeword how we encode it as a 5-bit codeword. If you'll notice, you could sit down and come up with the same table. If you notice, what we've done is we've chosen a set of codewords such that, if you look at every starting bit, you have at most one leading zero. You never have to leading zeros. You also have at most two trailing zeros. So because of this bounds the number of consecutive zeros that you can have in your codewords. It shows you have a nice bunch of transitions in your coding patterns. You could choose encoding patterns that are resilient to noise and DC balance and all these other things and 4B/5B is good at that. So that's 4B/5B, that's how that encoding scheme works. So I've talked about a bunch of encoding schemes, kind of different ways to encode data, make it resilient to DC balance and noise and all this other stuff, so you know how these schemes work. What the next question is, how do you choose which one you should use in practice? If you're going to design some sort of IoT system that needs to send data. So to get some intuition on that, let me talk about where you use each of these different techniques, all of that. All these techniques are useful. They're useful for different settings. Typically, you're going to use the more complex encodings for high-speed links where it's worth investing in more complicated circuitry. So high-speed wired connections using proprietary protocols or Ethernet, there are three Manchester 64B/66B, and so on. If you're just do something on a PCB, or if you're just doing something simple, or you have a sensor and you something on the backend which is receiving data, you're dealing with low data rates, you need to deal with low power constraints and you want to keep things really cheap, then you're going to use simpler encoding schemes. It may even be cheaper to just run shared clock lines everywhere. You can do that too. So in lowering incentives, you see more asynchronous communications, you see more clocked encodings, and so on. So one particular example of this is what we've been getting out with all these breakout boards and these ICs I have been mentioning. If you remember a while back, we were talking about these breakout boards were going through the pinouts. We kept seeing serial data, serial clock, well, now you know what those things are. That for a protocol where you have in a coding on the data line and then you have a clock line. That particular protocol is called I2C. So I2C is a particular kind of protocol similar to NRZ where it allows a set of sensors, or actuators, or devices to send or receive data to each other. In the architecture, it looks like this. There'll be input voltage, there'll be a ground, and then you have a serial data line and a serial clock line. I2C is a protocol which is master-base. It's a master slave architecture. So there, you'll have your microcontroller and that'll act as the master for the network, into send to a particular slave or a particular device on the network. What it'll do is, it'll send an address. So each of the slaves has an address associated with it. The master will send an address to wake up that slave and then it'll send data to that slave, and then it'll end the communication. So it's an address oriented scheme. It's designed to operate in the range of 0.1-5 megabits per second. It's a synchronous protocol, so clocks are kept in sync, and so on. So I2C is a very common protocol for being used inside of devices to connect together ICs or breakup boards or things like that. There's a few other buses that are used inside devices. There's SPI, which is designed to be a faster bus. It's targeted at 25 megabits per second, kind of higher speed communication. This is what you use if you want to connect to your micro controller to an SD card or something where you're sending a lot of data. There's also UART. So UART is an asynchronous communication protocol. There's no shared clock. You sync up using that start bid. It's got a set of parameters like baudRate and parity and stuff like that, which you can configure. Max speed of UART is eight megabits per second. There's also GPIO which is a term you're going to see a lot in the literature when you read about this stuff. GPIO isn't really a protocol in itself, it's more of a set of parameters for turning tins on and off and defining threshold voltages. So it may be used in conjunction with one of these other buses. It's used a lot for really simple stuff. You have a switch, you want to turn on and off, or like a motion detector, or LED or something, and it is designed for really low communication schemes. Max speed is one kilohertz. So we talked about all these buses that you can use and encoding schemes and those are used in all sorts of ICs and breakout boards. For example, use a few more breakout boards, if you have actuators or color LCDs, or you want to put a USB port on your board or have various sorts of sensors. You look at all this stuff, all these boards, all these ICs have communication protocols coming out. So you don't have to go in and figure out any sort of proprietary communication protocols. Because typically, they don't use proprietary ones, they use ones that are standardized like I2C and GPIO and all these other that I described. Okay. So this wraps up encoding. So I talked about the problems that you can face in encoding, and then I described a set of encoding schemes which can be used to solve these various problems. Next, what I'm going to do is I'm going to start talking about some more advanced electronics that are used inside of devices. In particular, devices and components that can be used for computation, CPUs, microcontrollers, and had to choose between them, the various types when you're designing your device.