Introduction: Bit Banging With Raspberry Pi for I2C Control With Perl

First a little general background

To use many of the I2C to parallel port IC's with the Raspberry pi you need to be able to manipulate the data bit wise. Usually this is done in a micro controller, but with the pi there is much more power to do other things so once you master the bit wise manipulation of the data you can build very powerful tools. A very common bit wise task is to toggle a bit. For instance if we have a bank of relays 0010_0100b and want to toggle the third bit which is currently a one to a zero the way to do that is with an exclusive or operation bit wise. In Perl this is accomplished with the "^" carrot symbol, where each bit is ored individually. When doing this operation there is a key value that we use called a "mask" to represent the bit or bits that we want to operate on.

The general formula will be as follows

Current_status_byte ^ mask_byte = New_status_byte

--------------------------------------------------------------------------------------------

Here is an example showing the binary values where we want to toggle the third bit in the status_byte

0010_0100 ^ 0000_0100 = 0010_0000

and if we repeat the operation again we can toggle the bit back on.

0010_0000 ^ 0000_0100 = 0010_0100

------------------------------------------------------------------------------------------------

Another common thing to do is to clear a bit which we can do similarly using the bit wise and function. Generically this will look similar, but we need to toggle the mask for this operation

Current_status_byte & mask_byte_inverted = New_status_byte

For the numeric example

0010_0000 & 1111_1011 = 0010_0000

Now however if we do the operation again, the bit doesn't toggle it stays clear

0010_0000 & 1111_1011 = 0010_0000

------------------------------------------------------------------------------------------------

In an ideal world you would most likely want one mask byte and complement the mask bit in the formula so you can toggle or clear with the same mask bit. using the bit wise invert. Here is a way to change the clear so you can use the same mask for both

0010_0000 & ~0000_0100 = 0010_0000

Step 1: Summary of the Main Tasks You Usually Want

The most common tasks are to set, clear, or toggle a bit. Addition and subtraction can be handled with normal tools so nothing special is required. Here are the generic formulas for each using a common mask function

0010_0100 | 0000_0100 = 0010_0100 < sets the third bit, which was already set in this example

0010_0100 & ~0000_0100 = 0010_0000 < clears the third bit

0010_0100 ^ 0000_0100 = 0010_0000 < toggles the third bit

With these tools and a little background on how Perl stores data we can perform bit manipulation

Step 2: Hex and Binary

To work with the I2C commands on the Raspberry pi the data will typically be in hex. The previous examples were shown in binary data to help illustrate what was happening with the bit wise operator. For the rest of this Instructable we will change to hex to be more compatible with how you will program for the I2C operations. If you want perl does support binary data as well, but it can be difficult to read latter when the program gets more complex

Here are the examples shown in hex

0x24 | 0x04 = 0x24 < sets the third bit, which was already set in this example
0x24 & ~0x04 = 0x20 < clears the third bit

0x24 ^ 0x04 = 0x20 < toggles the third bit

Step 3: Intro to Background on How Perl Stores Information for Hex and Integers

Perl hex numbers look a lot more like 8 byte wide integers in the data base. For most of the work you do it will not matter as the leading zeros are all truncated. There is one operation however that needs a little modification to make it appear to work properly. That is the bit wise invert. As this will toggle every bit of the entire integer and change the effective word width. This also illustrates the inefficiency of using this method, but it does keep all the programing in one language, and with the fairly large amount of memory available in the Raspberry pie it wont be an issue for the few bytes of data you will bit bang to drive the I2C IO bus. The program above shows some of the ways you can test the data types. The result is in the introduction photograph

Lets look at an example of what happens and how to resolve the effective data width change.

if I have a hex number 0x01, which looks like 0000_0001b for an example. Now lets say I want to toggle all the bits in this string using the "~" operation and print the value back out in hex I would get "FFFFFFFE" as the result. This would not be suitable for using with the I2C commands on the Raspberry pi. This can be easily fixed however by coming back and striping off the data you want so if we change the command as follows:

data_out = ~data_in & 0xFF

Now what we get for the output when using the input 0x01 is 0xFE. Every bit toggled, and one byte of data

for the examples we need to modify the clear bit formula to manage the data width

0x24 | 0x04 =0x24 < sets the third bit, which was already set in this example
0x24 & ~0x04 * 0xFF = 0x20 < clears the third bit and truncates the data width

0x24 ^ 0x04 = 0x20 < toggles the third bit

Step 4: I2c Commands With Perl

The I2c commands on the Raspberry pi will tend to work with strings. i2cset, i2cget are common commands use. One way to use these in a perl script is with the `` (back ticks) method. you can build up the command into a string, then print the string to the screen to check then execute the same string. This ability to print the command allows for faster debug. Here is an example for reading a register

$command = "i2cget -y 1 ".$i2c_address." ".$i2c_port."\n";

This builds up a string that is the concatenation of the data in the quotes with the variables $i2c_address and $i2c_port

Now to see the string simply printf "%s",$command;

To execute the string and keep the return value and return errors

$read_value = `$command 2>&1`;

At this point the return value will be a string of the data that is in the register assuming the read went ok, if not the error will be contained in $read_value;

to change the result to a hex value you need to do the following

$read_value = hex($read_value);

Now you can work with the data as shown in the earlier sections

Step 5: When You Are Completed You Need to Convert Back to a String for the I2C Commands

Conversion back to a string is fairly simple with the sprintf command. With the addition of the uc command the format can all be upper case and the results look cleaner

$read_value = uc(sprintf("%x\n",%read_value));

this would print something like 24 using the previous examples, and if it was a value like 0xED, this would print ED

This still doesn't look correct however as it is missing the 0x that we need as a prefix for the I2C write command

so change this be 2 characters wide with the prefix 0x and we have

$read_value = uc(sprintf("0x%02x\n",$read_value));

Now for the "ED" example the $read_value string contains 0xED

Now when using with the I2C write command when printing use $read_value as is

$command = "i2cset -y 1 ".%i2c_address." ".$i2c_port." ". $read_value."\n";