Skip to content

Byte Communication (Digital Words)

Digital IO

Individual TTL signals use bit-addressable memory in the TDT digital IO for communication. But what if we have many signals that we want to record from another device or send out to control several things? This is where byte-addressable memory comes in. Unlike bits, which are saved individually as 0 or 1, a 'byte' is a group of eight bits that is read as an entire set at once (whenever there is a state change). The value of these eight bits makes up the byte value, or 'digital word.'

This type of communication is useful for saving a large variety of information that can come from another device. For example, if you have an operant chamber with levers, lights, infrared sensors, lick spouts, shock grids, etc, then you will want to keep track of which equipment is active and not active. This information lets you know the overall behavioral state of the animal. These combinations of many states are difficult to track with individual TTL pulses. Instead, you likely want to read the combination of these signals as a single state to be able to easily identify an event code with a certain behavioral state.

Because reading byte values requires understanding binary numbers, we are going to make a quick detour into learning how to count in groups of 10 (which you already know how to do) and then in groups of 2 (just like computers!). After you know how binary numbers works, we will look how this applies to TDT digital IO by looking at the physical connections and software setup in Synapse.

How Do We Make Natural Numbers?

Counting Eggs

Positive whole numbers like 1, 2, 3, etc are called 'Natural Numbers.' If you see a basket full of eggs and want to know how many there are, you count them one at a time. One egg is 1, two eggs is 1 egg then another 1 egg, and so on. You can represent any number of eggs just by counting one egg at a time.

But what if you have more than one? Do you have say "I have 1 egg, 1 egg total?" No, you would say "I have two eggs." What if you had a dozen eggs, or one hundred eggs? Using one to represent those larger numbers doesn't make much sense And where do these whole numbers come from anyways? How do we know what value those numbers actually have?

Humans solved this problem a long time ago by creating 'number bases' that can be used for counting and representing any number larger than one.

To make a number base to count the natural numbers, all you have to do is pick a natural number bigger than 1 (2, 10, 21, N) and use that as a marker to track how many groups of that base number you have. If you have N groups of N things, then you keep track of that to make. Since you are splitting the larger set up into groups and only counting the groups, it makes the total number of things to keep track of much smaller. The way this works on paper is that base numbers are actually raised to an exponent that represents the 'zero group,' 'ones group,' 'twos group,' etc. But let's look at an example and see what this all really means.

Understanding Numbers in Base 10

Counting on your fingers

Humans count in base 10. This makes a lot of sense because we have 10 fingers. So we naturally form our basis of representing numbers around 10 because that's the highest number we can keep track of with our digits before needing a place holder. But we don't just stop at 'how many groups of ten' we have. We keep track of how many ones, tens, hundreds, thousands, and beyond are in a total number we count. Or, you can think of it as how many ones, tens, tens of tens, hundreds of tens, and so forth.

Note

The prefix 'dec' comes from the latin root 'decas' which means 'set of ten,' and that's where decimal, decade, decimate, and other words come from

For a practical example, we are going to make the numbers 1, 2, 10, 11, 100, and 137 using the base 10 system. Each number is read from left to right, where the largest set in that number is on the left and the smallest group is on the right. We call that the 'Most significant digit' and the 'least significant digit.' Every next number to the left of the least significant digit represents, as its base, 10 times more than the previous digit's group. In a base 10 numbering system, the least significant digit is the ones group, which represents the remainder of items left over when all the larger groups have been counted.

To make the numbers 1 - 9, you just count the total individual ones you have. A 1 is represented in base 10 as:

1 = 100

It is raised to the power of 0 because you have 0 groups of ten so far.

To make the other 2 - 9, you just count the total individual ones you have. A 2 is represented in base 10 as:

2 = 100 + 100

To make the number ten you raise 10 to the power of 1 because you have one group of ten now:

10 = 101

But notice something very important. This is written as 10, not just 1. You have to keep track of all the groups you've included so far. A ten is one group of ten and zero groups of ones.

10 = 1 x 101 + 0 x 100

The number 11 is one group of tens and a one left over:

11 = 10 + 1 = 101 + 100

If you have ten groups of ten, you have 100:

100 = 102

or

100 = 1 x 102 + 0 x 101 + 0 x 100

Finally, the number 137 is a combination of all three groups - one group of ten tens, three groups of ten, and seven ones left over:

137 = 100 + 10 + 10 + 10 + 1 + 1 + 1 + 1 + 1 + 1 + 1 = 102 + 101 + 101 + 101 + 100 + 100 + 100 + 100 + 100 + 100 + 100

or

137 = 1 x 102 + 3 x 101 + 7 x 100

Numbers in Base 2 and Reading Binary

Now that we understand how to make numbers using a base 10 system, we can apply that same knowledge to a base 2 system.

Binary numbers are represented using a base 2 system. Computers can only count up to 1 because they have 'bits' instead of fingers and these bits are either on or off, 1 or 0. That is a total of two states that can be represented in a bit.

However, representing numbers larger than 1 in computers is not a problem because of the base 2 number system. We can just keep track of the groups of ones, twos, two twos, and so forth using the same type of counting method as base 10. The only difference is that instead of using values up to 9 as a group multiplier, we only go up to 1. Our bits, which can be 1 or 0, will represent the group order we are keeping track of.

To make the number 1, you say you have zero groups of two as before. We set the zeroth bit as true (multiply it by 1):

1 = 1 x 20

The number 2 is one group of 2 and no groups of ones. So we set the 1st bit as true and the 0th bit as false and write it like this:

2 = 10

Wait! What? Two does not equal ten. Don't be alarmed! Remember, we can only go up to a value of 1 to represent each group value. Break it down into its subgroups:

2 = 10 = 1 x 21 + 0 x 20

Keep in mind that the 21 is not incorrect or cheating because we are keeping track of how many groups of twos we have. For this reason, we can drop the N multiplier in front of each group since each group is either one or off, 1 or 0. If a group is not used, we still keep track of it as a 0 in the binary number.

Three is one group of two and one group of one:

3 = 11 = 21 + 20

How about 4, which is two groups of two and no ones:

4 = 100 = 22

Again, we can only go up to 1, which is why we don't write 4 as 20. We must increment our most significant digit to represent every next base power of 2.

7 = 111 = 22 + 21 + 20

11 = 1011 = 23 + 21 + 20

and so forth, up to 8 bits total (since we started at 0 for the base power, we can go up to 7 for the exponent):

137 = 10001001 = 27 + 23 + 20

If you get stuck, Windows has a really neat 'Programmer' feature on their calculator tool that you can use to put in a decimal number and see its binary representation. It breaks the bits up into groups of 4 for ease of reading.

Bits Make Bytes

Now that we know how to represent numbers in binary, we can move on to how this applies to digital I/O in TDT.

As we already covered, an individual TTL pulse is a single voltage trace that signals when something is on or off, 1 or 0. That sounds a lot like how a bit works. Indeed, TDT equipment is not just limited to reading individual TTL signals. You can combine these signals or 'bits' together as groups of 8 in order to read or write larger binary numbers. This group of 8 bits is called a byte, as mentioned previously.

If you do the math, or play around with the calculator tool, you will find that the binary number 1111 1111 is the decimal number 255. This value represents every bit in a byte being on. A byte on the TDT digital I/O can either read values from 0 - 255 or it can write values from 0 - 255. Every bit is written or read at the same exact time and a single value is reported. We call this 'word-addressable' I/O, because you read all the individual bits together like a word. Calling it byte-addressable memory works, too.

TDT Digital IO DB25 Connector

The TDT RZ processors have a DB25 connector for the digital I/O. This connector is divided into groups A, B, and C that represent each 8-bit byte grouping. Don't get intimidated by all the numbers and letters. Remember that all this is for is to make numbers from 0 - 255 using signals that are either on or off.

The numbers 1 - 25 in the pinout are just the physical pin positions for each signal line. Signal number 5 is the ground, which leaves 24 pins left for digital I/O.

Byte C is normally used for the normal single TTL bit-addressable communication because each bit in the Byte can be individually read directly in Synapse. For the Word-addressable I/O, focus on Byte A and B.

Byte A is made up of pins 6 - 9 and 18 - 21 on the DB25 connector. Pin 18 is the least significant bit (or digit) in the byte and pin 9 is the most significant bit. Pin 18 is the 20 group. Pin 9 is the 27 group. That's what we mean by bit 0 and bit 7, respectively, in the table. Again, bits are counted from 0 to 7 in a byte because they represent the base power order of a particular twos group, just like how we showed in the earlier section on binary numbers Binary Numbers.

Breaking out Byte A into a bit table

Connecting to Third-Party Devices

Unless you have an RZ2, which has eight BNC connectors that access each bit of Port A, then you will need to use the DB25 connector on the front bottom of your RZ device. You would also need to do this for Port B of the RZ2.

The DB25 can be accessed in a number of ways. TDT makes a DB25-DB25 cable to connect to MedAssociates' SuperPort. You can also use a PP24 to breakout each channel in the 24 bit digital IO into a BNC connector. Other methods that involve accessing pins directly or user-made DB25 cable connections are explained in A Note About Ground because extra considerations are needed for the ground signal.

Word Inputs

We now have a clear picture of what bit each pin corresponds to. Let's see an example of a third-party device communicating with TDT. The third party device is sending TTLs from its output ports. These outputs will physically connect to the pins of Byte A on the TDT side. When the values on the TDT side change all eight bits in Byte A are read at once and the word value is captured.

TDT sending or receiving byte = 153

TDT captures word values upon changes in the byte, so if the state of any of the eight bits were to change, then a new word value would be recorded.

From now on, we will use a bit table to show any communication on TDT bytes.

Here is a video showing how to set up a word input for Byte A in Synapse.

Digital I/O - Word Input

Digital I/O - Word Input

Read an 8-bit digital word input from the RZ into Synapse.

You can also save individual bits from each word input by using the LowerBits or UpperBits gizmos that TDT provides in Custom > TDT > Logic.

The Lower Bits gizmo attaches to your Port A (or Port B ) and will parse out the first four bits 0 - 3 individually and make them individual outputs to which you can then attached an Epoc Store. The same concept applies for the upper bits 4 – 7 (using the Upper Bits gizmo). This should let you save each individual bit from the port to which you are attached.

You can download the example experiment here (note that all the bits are not saved in this example and you can add more epoc stores as needed): Download Experiment File

Word Outputs

The same principles for word (byte) inputs apply to word outputs from a TDT perspective. Eight bits of the target byte (usually byte A) are set and those bits simultaneously send out TTL pulses (or not) according to their output value.

In order to send words out of TDT you must provide the target byte with an integer. If you are targeting just byte A or byte B individually, that integer value should be between 0 - 255. Some example integer to bit assignment in the TDT byte are below:

Integer values and their output byte value

Note

Bytes A and B can be combined into a 16-bit word to send or receive much larger values up to 65535

TDT offers a TTL2Int gizmo in Synapse that is frequently used to send integers to bytes A or B for output. This gizmo can be controlled by an upstream logic strobe (from a pulse train, user input, external trigger, or any other logic signal) to output a user-set integer value upon being triggered. In the example experiment below, we use a logic pulse from a pTrain gizmo to trigger the TTL2Int gizmo to output a value of 137 for the duration of the pTrain pulse and send it to PortA.

TTL2Int example experiment

You can download a copy of this experiment here: Download Experiment File

Note

The TT2Int gizmo (located in Custom > TDT > Logic) in v98 and earlier has an edge detect on the strobe input. This means that the integer output will last for a single sample. The provided experiment above has a modified version where the integer output is true for the duration of the logic strobe signal.

Byte Epocs in the Data

Byte data also gets saved as an epoc in Synapse. Unlike a bit epoc, which saves a distinct onset/ offset for each individual IO channel, bytes (words) from PortA or PortB save integer values 'On Change.' This means that a new integer value will get saved when there is a change of state in any of the 8 bits in the byte. Not every bit might have changed at that instant, but the total byte gets saved anyways.

Below is an example of how On Change tracks byte changes:

On change tracks bit changes in the byte, not individual bits

Each integer is a word value from the state of all 8 bits in the Port A byte. When '1' is saved, bit 0 is true. If bit 1 also turns on, then a value of '3' is saved, but bit 0 remained on and did not change. Finally, if bit 0 turns off, then a value of '2' is saved. This might seem apparent with a small example of only two bits, but when many bits of different order are changing, it can be difficult to know which bits are on or off (except if the number is odd, then you always know bit 0 is true).

The byte data gets saved as an epoc with onset times, offset times, and data that represent the word value saved on any given state change. If you have a lot of bits in your byte changing and want to know when each was on or off individually, then you can use the BitBreakout experiment referenced in Word Inputs or you can use a function call in TDTbin2mat called 'Bitwise' that parses each of the bit states individually.

Byte data imported into Matlab

As you can see in the example data, bitwise parsed any bit of the 8 bit byte that changed when going through values of 137, 111, and 10. Note that bit 4 is not present in data.epocs.PortA.bitwise. That is because bit 4 never changed states in any of those word values.

You can filter around individual bits from a byte save, or you can filter around data values from a byte if you know which value corresponds to your event of interest. We have an example that does this here.