Interfacing Electronic Circuits to Arduinos
by maewert in Circuits > Arduino
42377 Views, 55 Favorites, 0 Comments
Interfacing Electronic Circuits to Arduinos
In this instructable I use an example of interfacing an Arduino to an ARINC 429 transceiver in order to demonstrate the general process of interfacing an Arduino to electronic circuits so you can use these techniques on your own designs.
An ARINC 429 bus is the most common data bus used on aircraft for computer to computer communications. The ARINC 429 bus operates at one of two speeds, called low speed and high speed, which are 12.5 kbps and 100 kbps respectively. The bus operates over two wires (and a ground). Each piece of data is sent in a 32 bit word. Generally the first 8 bits, called the label, are used to identify the data contained within the ARINC word. Bits 9 and 10 often define the Source/Destination Indicator, but sometimes they contain data or are an extension of the label. Data is contained in bits 11-29 and can contain binary twos compliment, binary coded decimal, and/or a set of discrete bits. Bits 30 and 31 contain the Sign Status Matrix, and its values can indicate Normal operation, Failure Warn, No Computed Data, and Functional Test. Finally bit 32 is the Parity bit and is set so that the 32 bit word has ODD parity.
Avionics equipment manufacturers, aircraft manufacturers, and avionics equipment service centers have specialized test equipment to read these ARINC 429 data buses. I've wanted to own and use my own test equipment so I developed the Arinc429eReader. While this could be an instructable on its own merit, I suppose the audience interested in such a device would be small. I will therefore present a more generally applicable instructable on the process of interfacing an Arduino to other electronic circuits.
Deciding to Do the Project
First make sure someone hasn't already solved your problem. Go on Google and search.
In my example I found several companies that make ARINC 429 to USB converters but they are rather expensive, $1500 US dollars or more. There had to be a better solution. I found an ARINC 429 transceiver in a 40 pin DIP at a reasonable price. I wrote to the company and they sent me some free samples. Although the transceivers contain both a transmitter and two receivers, I only needed the receivers (at least for now!) so this chip looked good.
Step 1b: Can I do it?
The next step is to determine if you have the skills (and the determination) to see the project through. To determine this I obtained the ARINC 429 transceiver chips' specification and reviewed it.
In my example I saw the chip requires only 5v power and no 'exotic' analog signals.
It also requires a 1 MHz clock to time its operations. I thought that I could maybe use the Arduino's clock or use a 1 MHz clock chip.
It also requires 11 input/output signals in addition to a 16 pin data bus. This is too many pins for the Arduino UNO but is easy for the Arduino Mega.
See Figure 1 attached.
What about software skills? Well, I've done quite a bit of coding for other processors before and I can look through how other library code is written as examples of how others solved similar problems. The open source nature of Arduino is perfect for my needs here.
Step 1c: Should I do it?
The next step is to determine if the project should be done. It will cost money and time. I usually error on the side of trying new things for the experience if nothing else. Asking others for their opinion is often NOT recommended. Remember that most of the people we remember a great people all had contemporaries telling them they were wrong.
I assume that you are ready, willing and able to proceed with your project! Lets get to it!
Read the Chip Data Sheet
Step 2: Read the Chip Data Sheet
Get the chip data sheet for your chip and read all of it.
In my case the chip I found is the DEI1016 and its specification can be found here:
www.deiaz.com/data-sheets/DS-MW-01016-01-E.pdf
See the PDF file attached.
Let’s take a look at the timing diagrams. They can be intimidating at first but I am sure you can learn how to read them if you don't know already.
Here is the timing diagram for the Reset and Initialization Sequence. I've added a few annotations to help you learn how to read the timing diagrams:
See timing diagram with annotations attached.
When the line is high, that means it is at 5 volts, and when low, it is ground, just like the Arduino's digitalWrite HIGH and LOW.
Now let’s add in the values for all those time measurements in the timing diagram:
See timing diagram with times attached.
The time between the MR pin going HIGH and the LDCW pin going low isn't specified, so let’s assume it isn't critical.
Now let’s translate this into words:
Set MR LOW and hold it there for at least 200 ns.
Set MR HIGH.
Set LDCW LOW.
Set the data bus to its correct values and hold them there for at least 110 ns.
Set LDCW HIGH.
Let’s do the same thing for the Read operation:
See Figure 8 attached.
It is very interesting how they chose to indicate the relative timings... OE1 can be LOW 0 ns after DR1 goes LOW but 20 ns after SEL goes LOW.
Also note that there appears to be no timing provided for the Word 2 to become valid. I think this is really trying to say that Tdoedr being 200 ns is how long it takes for Word 2 to be valid and NOT when OE1 must be raised to HIGH. I'll try this logic out and see if my assumptions are correct.
Let’s translate this into words:
The DEI1016 chip sets DR1 LOW to indicate data has been received on Receiver 1.
Set SEL to LOW and wait for 20 ns.
Set OE1 to LOW.
Wait 200 ns for the chip to set the data pins. Read the data pins.
Set OE1 to HIGH and wait for 20 ns.
Set SEL to HIGH and wait for 30 ns.
Set OE1 to LOW.
Wait 200 ns for the chip to set the data pins. Read the data pins.
Set OE1 to HIGH.
Now that we have some understanding of how the pins need to be set to initialize the chip and to receive data, let’s turn these routines into Code.
Downloads
Converting the Waveforms Into Code
The Arduino development environment shields the user from a lot of the messy code details. This is good when you are just starting out and want to blink an LED. When we want to interface with other electronic circuits, however, we need to interface more directly with the microprocessor.
First let’s discuss how to efficiently read in the 16 pins in the data bus. Since I’m using the Arduino Mega because it has a lot of IO pins, I can use Port A and Port C to be my 16 bit data bus. Port A and Port C are pins 22 – 37 on the Arduino Mega. The data bus is bidirectional, meaning that sometimes the pins are used as outputs and other times as inputs so we need to set the pin modes quickly as well as reading/writing to the pins themselves. The SLOW way to use these pins would be as follows:
pinMode(22,INPUT);
pinMode(23,INPUT);
pinMode(24,INPUT);
…
pinMode(37,INPUT);
bit1 = digitalRead(22);
bit2 = digitalRead(23);
bit3 = digitalRead(24);
…
Bit16 = digitalRead(37);
At this rate we’d lose data as we’d be reading the data slower than the data is being received. We need to speed this up by directly interfacing with the Arduino microprocessor.
The Arduino microprocessor is like the engine inside the Porsche. Most times a driver of a car really only needs to understand how to make the car stop and go and doesn’t need to fully understand the internal workings of the car’s engine. A race car driver, however, needs to understand the limits and capabilities of the engine to push it to win the race. Likewise we need to understand the details of the ATMEL AVR which is the Arduino’s engine to interface it with other electronic circuits.
To understand the Arduino microcontroller we read the AVR datasheet. The Arduino Mega uses the ATMEGA1280 which can be found here: www.atmel.com/dyn/resources/prod_documents/doc2549.pdf
Yes, this is 444 pages of detailed information which can be daunting at first look. You will find on pages 100 and 101 that you can set all of the pins on port A and port C with a single write to the PORTA address and PORTC address. We can also read from these ports but we do this not by reading from the PORTA and PORTC addresses but from the PINA and PINC addresses. Likewise we can set the pin modes for all of the pins in the port at once by writing to DDRA and DDRC addresses, a ‘1’ sets the pin to output and ‘0’ to input.
Now we can set the pins to inputs and read them much more quickly using these commands:
DDRA = 0x00;
DDRC = 0x00;
Byte1 = PINA;
Byte2 = PINC;
And to write to the port pins:
DDRA = 0xFF;
DDRC = 0xFF;
PORTA = Byte1;
PORTC = Byte2;
Now that we can efficiently read and write the bus pins, we need to look at how to efficiently read and write the other normal pins. In my example project I used Arduino pins 2 – 12 to control the 11 non-bus control signals going between the DEI1016 and the Arduino. If we want to write efficient code for these pins also (and we do) we need to avoid the digitalWrite and digitalRead routines which are slow because they do things like checking to see that the pins are set to the correct modes, etc. To write very quickly again we can write directly to the hardware pins avoiding the Arduino overhead. The include file avrio.h includes the avrio_WritePin and avrio_ReadPin functions that perform fast reads and writes. I took a clue from the glcd library and defined my own fastWrite routine which is #defined to avrio_WritePin to make the code more readable. If you wish you can review how the glcd library solves their fast writes, although you will have to traverse the few layers of indirection.
One last bit of difficulty we have in translating the timing diagrams into code – just how do we delay for the specified number of nanoseconds? How long is a nanosecond? Question: How long does it take for you to slip on a peel and fall? Answer: A bananosecond. Sorry for the bad pun. The Arduino has a delay(milliseconds) and delayMicroseconds() but does not have a delayNanoseconds() function. When working at a high level language such as C, we have less knowledge of how much time a microcontroller will take to execute our commands. This gets really tricky as the processors become more complex and have instruction pipelines with L1 and L2 memory caches, etc. In our Arduino case it is not too difficult as we can use the ability of the C language to include inline assembly code. By using assembly we can count the instructions and know how many clock cycles (typically 1/16000000 of a second) an operation takes. By using ‘inline’ assembly, our instructions do not get implemented as subroutines and therefore do not take longer to process with the jumping to the subroutine and pushing and popping data off the stack. The disadvantage of inline assembly is that the compiled object code gets longer, but this is acceptable since we aren’t doing a lot of these nanosecond delays.
Again I steal the solution inside the glcd library. There they include the delay.h file which contains the _delay_cycles function. I then define this function as the more readable DelayNanoseconds as follows:
// Hans Heinrichs delay cycle routine:
#define DelayNanoseconds(__ns) _delay_cycles( (double)(F_CPU)*((double)__ns)/1.0e9 + 0.5 )
You will notice that there is double floating point math going on in here which is usually avoided because it is very time consuming and we want very small delays. This routine uses the floating point math routines to automatically adjust to the microcontroller’s clock frequency to insert the right amount of assembly code inline with the C code and the best part is that the floating point calculations are performed when you compile and are not executed by the Arduino at runtime! This method can result in a single-instruction ‘NOP’ being inserted as a busy delay.
Downloads
Generating a 1 MHz Clock Signal
According to the data sheet I need to supply the DEI1016 chip with a 1 MHz clock signal.
The Arduino uses 16 MHz oscillator, one would think one might be able to use a divide-by-16 counter to generate a 1 MHz clock signal form the Arduino's 16 MHz clock. When I tried to do this the circuit fouled the Arduino's oscillator. Maybe I could have used very short wires into a buffer chip but in the end it was simpler to just add a 1 MHz clock chip.
Here is the information for Jameco's 1 MHz Clock:
Transmission Troubles
Transmission Troubles
In order to test my receivers, I used the loopback capability of the DEI1016 chip. In this mode anything transmitted is wrapped back to both receivers, receiver 1 receives the same data transmitted while receiver 2 receives the negative of the transmitted data.
Lets take another look at the timing diagram for loading the transmitter:
See Figure 9:
You will notice that the data sheet authors did not show much detail in the timing of Word 2. Here we have to assume that the timings of word 2 in relation to LD2 are just like Word 1 and LD1.
So, once again to turn this into words:
Set LD1 to LOW and wait for 20 ns.
Set the data bus pins to the data you want to transmit and wait for 110 ns.
Set LD1 to HIGH.
Set LD2 to LOW and wait for 20 ns.
Set the data bus pins to the data you want to transmit and wait for 110 ns.
Set LD2 to HIGH.
I enabled the loopback mode by initiating a reset and initialization sequence described above and this time enabling the loopback mode. I then within a loop transmitted a test word and received the results from both receivers. This worked correctly only the first time but would not send multiple test words. So, what was I doing wrong? A quick review of the DEI 1016 data sheet showed me what I forgot:
See Transmission data sheet excert.
So, there it is, I need to enable the transmitter by setting ENTX to HIGH when I want to transmit and to hold it LOW while I'm loading the data into the transmitter. The words of the data sheet described this but the timing diagram did not. OK, so I proved again that reading the whole data sheet is a good idea ;-)
Finishing the Project
To finish my project I transferred the design from the working breadboard to an Arduino Mega perfboard shield. I also added a few LEDs connected to output pins via 220 ohm resistors. These LEDs will indicate if the receivers are getting any data, the receiver's speed setting, and if the loopback mode is initiated. I created a simple protocol over the serial io port #1 (pins 1 and 2).
The Arduino sets the receiver's speed to Hi or Low speed if “R” or “r” is received, respectively.
The Arduino sets the transmitter's speed to Hi or Low speed if “T” or “t” is received, respectively.
The Arduino sets the loopback mode on or off if “L” or “l” is received, respectively.
I hope that after reading this instructable about my experiences you may want to try to interface your Arduino to new and interesting electronic circuits and share them here!
Note: You can download a version of AVRIO that can be used with this code from:
* http://www.opensource.billsworld.billandterrie.com/avr/avrio
Note: Be sure you understand the license for and meet the conditions of including AVRIO.h in your design!