Posts Tagged ‘ receiver ’

LPC176x UART Driver

In my last post (here), I claimed that FIFOs are often used in UART drivers. Here I will show a UART driver that utilizes dual FIFOs, one for transmit and one for receive. A universal asynchronous receiver/transmitter (UART) is a device that receives and transmits data without a known clock relationship to the connecting device. This allows each device to send data whenever it wants. This is in stark contrast to the SPI and I2C buses where the slave device can’t send data without the master first initiating a bus transfer. UARTs are very versatile and are in wide use. They are most commonly found in RS-232 ports on PCs.

The basic structure behind a UART driver is a negotiation process between the asynchronous hardware and the user’s code. FIFOs are used to aide this process. For transmitting data, it is desirable for the user to drop the data off at any time and forget about the actual serial transmission. This is where the FIFO comes in. The UART driver just takes the data and puts it in a FIFO and returns to the user. In another thread (driven by interrupts) the driver sends all the data in the FIFO as fast as it can. The receive path is very similar. The driver, again in an interrupt driven thread, transfers all received data into a FIFO. The user periodically checks if there is any new data and pulls it out at its own speed.

UARTs are often used for printing ASCII to a debug console. Most of the UARTs I have made have only been used for this purpose. For this reason it is very important to have a good method for converting numbers (integer and floating-point) to a sequence of ASCII characters. Of course, you could use a sprintf-like function, however, these are very slow. Even the embedded versions of these libraries produce terribly inefficient code (I dare you to follow the call stack of a printf function). I’m not a big fan of Arduinos, but I must say that the Arduino serial printing functions are very nice. There are no format strings to parse. Instead, the user just calls a sequence of print functions to produce the desired ASCII. My UART driver has an integrated printing library similar to the functions found in the Arduino library. This may be better off separated from the actual driver, however, I feel it fits fine into this code. You’ll notice a lot of similarity between my print functions and the Arduino serial library.

Header File

/************************************************************************
Copyright (c) 2011, Nic McDonald
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above
   copyright notice, this list of conditions and the following
   disclaimer in the documentation and/or other materials provided
   with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*************************************************************************

 Information:
   File Name  :  uart3.h
   Author(s)  :  Nic McDonald
   Hardware   :  LPCXpresso LPC1768
   Purpose    :  UART 3 Driver

*************************************************************************
 Modification History:
   Revision   Date         Author    Description of Revision
   1.00       05/30/2011   NGM       initial

*************************************************************************
 Assumptions:
   All print functions assume the UART is enabled.  Calling these
   functions while the UART is disabled produced undefined behavior.

************************************************************************/

#ifndef _UART3_H_
#define _UART3_H_

/* includes */
#include <stdint.h>

/* defines */
#define SW_FIFO_SIZE            512
#define UART3_DISABLED          0x00
#define UART3_OPERATIONAL       0x01
#define UART3_OVERFLOW          0x02
#define UART3_PARITY_ERROR      0x03
#define UART3_FRAMING_ERROR     0x04
#define UART3_BREAK_DETECTED    0x05
#define UART3_CHAR_TIMEOUT      0x06

/* typedefs */

/* functions */
void uart3_enable(uint32_t baudrate);
void uart3_disable(void);
void uart3_printByte(uint8_t c);
void uart3_printBytes(uint8_t* buf, uint32_t len);
void uart3_printString(char* buf); // must be null terminated
void uart3_printInt32(int32_t n, uint8_t base);
void uart3_printUint32(uint32_t n, uint8_t base);
void uart3_printDouble(double n, uint8_t frac_digits);
uint32_t uart3_available(void);
uint8_t uart3_peek(void);
uint8_t uart3_read(void);
uint8_t uart3_txStatus(void);
uint8_t uart3_rxStatus(void);

#endif /* _UART3_H_ */

Source File

/************************************************************************
Copyright (c) 2011, Nic McDonald
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above
   copyright notice, this list of conditions and the following
   disclaimer in the documentation and/or other materials provided
   with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*************************************************************************

 Information:
   File Name  :  uart3.c
   Author(s)  :  Nic McDonald
   Hardware   :  LPCXpresso LPC1768
   Purpose    :  UART 3 Driver

*************************************************************************
 Modification History:
   Revision   Date         Author    Description of Revision
   1.00       05/30/2011   NGM       initial

*************************************************************************
 Theory of Operation:
   This provides a simple UART driver with accompanying print functions 
   for converting integer and floating point numbers to bytes.

************************************************************************/

#include "uart3.h"
#include "fifo.h"
#include "LPC17xx.h"

/* local defines */
#define RX_TRIGGER_ONE          0x0
#define RX_TRIGGER_FOUR         0x1
#define RX_TRIGGER_EIGHT        0x2
#define RX_TRIGGER_FOURTEEN     0x3
#define RX_TRIGGER_LEVEL        RX_TRIGGER_FOURTEEN
#define RLS_INTERRUPT           0x03
#define RDA_INTERRUPT           0x02
#define CTI_INTERRUPT           0x06
#define THRE_INTERRUPT          0x01
#define LSR_RDR                 (1<<0)
#define LSR_OE                  (1<<1)
#define LSR_PE                  (1<<2)
#define LSR_FE                  (1<<3)
#define LSR_BI                  (1<<4)
#define LSR_THRE                (1<<5)
#define LSR_TEMT                (1<<6)
#define LSR_RXFE                (1<<7)

/* local persistent variables */
static uint8_t uart3_tx_sts = UART3_DISABLED;
static uint8_t uart3_rx_sts = UART3_DISABLED;
static uint8_t uart3_txBuffer[SW_FIFO_SIZE];
static uint8_t uart3_rxBuffer[SW_FIFO_SIZE];
static FIFO txFifo;
static FIFO rxFifo;

/* private function declarations */
static inline void uart3_interruptsOn(void);
static inline void uart3_interruptsOff(void);

uint32_t rdaInterrupts = 0;
uint32_t ctiInterrupts = 0;

/* public functions */
void uart3_enable(uint32_t baudrate) {
    uint32_t fdiv, pclk;

    // initial the SW FIFOs
    fifo_init(&txFifo, SW_FIFO_SIZE, (uint8_t*)uart3_txBuffer);
    fifo_init(&rxFifo, SW_FIFO_SIZE, (uint8_t*)uart3_rxBuffer);

    // set pin function to RxD3 and TxD3
    LPC_PINCON->PINSEL0 &= ~0x0000000F;
    LPC_PINCON->PINSEL0 |=  0x0000000A;

    // give power to PCUART3
    LPC_SC->PCONP |= (1 << 25);

    // set peripheral clock selection for UART3
    LPC_SC->PCLKSEL1 &= ~(3 << 18); // clear bits
    LPC_SC->PCLKSEL1 |=  (1 << 18); // set to "01" (full speed)
    pclk = SystemCoreClock;

    // set to 8 databits, no parity, and 1 stop bit
    LPC_UART3->LCR = 0x03;

    // enable 'Divisor Latch Access" (must disable later)
    LPC_UART3->LCR |= (1 << 7);

    // do baudrate calculation
    fdiv = (pclk / (16 * baudrate));
    LPC_UART3->DLM = (fdiv >> 8) & 0xFF;
    LPC_UART3->DLL = (fdiv) & 0xFF;

    // disable 'Divisor Latch Access"
    LPC_UART3->LCR &= ~(1 << 7);

    // set the number of bytes received before a RDA interrupt
    LPC_UART3->FCR |= (RX_TRIGGER_LEVEL << 6);

    // enable Rx and Tx FIFOs and clear FIFOs
    LPC_UART3->FCR |= 0x01;

    // clear Rx and Tx FIFOs
    LPC_UART3->FCR |= 0x06;

    // add the interrupt handler into the interrupt vector
    NVIC_EnableIRQ(UART3_IRQn);

    // set the priority of the interrupt
    NVIC_SetPriority(UART3_IRQn, 30); // '0' is highest

    // turn on UART3 interrupts
    uart3_interruptsOn();

    // set to operational status
    uart3_tx_sts = UART3_OPERATIONAL;
    uart3_rx_sts = UART3_OPERATIONAL;
}

void uart3_disable(void) {
    // disable interrupt
    NVIC_DisableIRQ(UART3_IRQn);

    // turn off all interrupt sources
    uart3_interruptsOff();

    // clear software FIFOs
    fifo_clear(&txFifo);
    fifo_clear(&rxFifo);

    // set to disabled status
    uart3_tx_sts = UART3_DISABLED;
    uart3_rx_sts = UART3_DISABLED;
}

void uart3_printByte(uint8_t b) {
    uint8_t thr_empty;

    // turn off UART3 interrupts while accessing shared resources
    uart3_interruptsOff();

    // determine if the THR register is empty
    thr_empty = (LPC_UART3->LSR & LSR_THRE);

    // both checks MUST be here.  there is a slight chance that
    //  the THR is empty but chars still reside in the SW Tx FIFO
    if (thr_empty && fifo_isEmpty(&txFifo)) {
        LPC_UART3->THR = b;
    }
    else {
        // turn UART3 interrupts back on to allow Sw Tx FIFO emptying
        uart3_interruptsOn();

        // wait for one slot available in the SW Tx FIFO
        while (fifo_isFull(&txFifo));

        // turn interrupts back off
        uart3_interruptsOff();

        // add character to SW Tx FIFO
        fifo_put(&txFifo, b); // <- this is the only case of txFifo putting
    }

    // turn UART3 interrupts back on
    uart3_interruptsOn();
}

void uart3_printBytes(uint8_t* buf, uint32_t len) {
    // transfer all bytes to HW Tx FIFO
    while ( len != 0 ) {
        // send next byte
        uart3_printByte(*buf);

        // update the buf ptr and length
        buf++;
        len--;
    }
}

void uart3_printString(char* buf) {
    while ( *buf != '\0' ) {
        // send next byte
        uart3_printByte((uint8_t)*buf);

        // update the buf ptr
        buf++;
    }
}

void uart3_printInt32(int32_t n, uint8_t base) {
    uint32_t i = 0;

    // print '-' for negative numbers, also negate
    if (n < 0) {
        uart3_printByte((uint8_t)'-');
        n = ((~n) + 1);
    }

    // cast to unsigned and print using uint32_t printer
    i = n;
    uart3_printUint32(i, base);
}

void uart3_printUint32(uint32_t n, uint8_t base) {
    uint32_t i = 0;
    uint8_t buf[8 * sizeof(uint32_t)]; // binary is the largest

    // check for zero case, print and bail out if so
    if (n == 0) {
        uart3_printByte((uint8_t)'0');
        return;
    }

    while (n > 0) {
        buf[i] = n % base;
        i++;
        n /= base;
    }

    for (; i > 0; i--) {
        if (buf[i - 1] < 10)
            uart3_printByte((uint8_t)('0' + buf[i - 1]));
        else
            uart3_printByte((uint8_t)('A' + buf[i - 1] - 10));
    }
}

void uart3_printDouble(double n, uint8_t frac_digits) {
    uint8_t i;
    uint32_t i32;
    double rounding, remainder;

    // test for negatives
    if (n < 0.0) {
        uart3_printByte((uint8_t)'-');
        n = -n;
    }

    // round correctly so that print(1.999, 2) prints as "2.00"
    rounding = 0.5;
    for (i=0; i<frac_digits; i++)
        rounding /= 10.0;
    n += rounding;

    // extract the integer part of the number and print it
    i32 = (uint32_t)n;
    remainder = n - (double)i32;
    uart3_printUint32(i32, 10);

    // print the decimal point, but only if there are digits beyond
    if (frac_digits > 0)
        uart3_printByte((uint8_t)'.');

    // extract digits from the remainder one at a time
    while (frac_digits-- > 0) {
        remainder *= 10.0;
        i32 = (uint32_t)remainder;
        uart3_printUint32(i32, 10);
        remainder -= i32;
    }
}

uint32_t uart3_available(void) {
    uint32_t avail;
    uart3_interruptsOff();
    avail = fifo_available(&rxFifo);
    uart3_interruptsOn();
    return avail;
}

uint8_t uart3_peek(void) {
    uint8_t ret;
    uart3_interruptsOff();
    ret = fifo_peek(&rxFifo);
    uart3_interruptsOn();
    return ret;
}

uint8_t uart3_read(void) {
    uint8_t ret;
    uart3_interruptsOff();
    ret = fifo_get(&rxFifo);
    uart3_interruptsOn();
    return ret;
}

uint8_t uart3_txStatus(void) {
    return uart3_tx_sts;
}

uint8_t uart3_rxStatus(void) {
    return uart3_rx_sts;
}

/* private functions */
void UART3_IRQHandler(void) {
    uint8_t intId;  // interrupt identification
    uint8_t lsrReg; // line status register

    // get the interrupt identification from the IIR register
    intId = ((LPC_UART3->IIR) >> 1) & 0x7;

    // RLS (receive line status) interrupt
    if ( intId == RLS_INTERRUPT ) {
        // get line status register value (clears interrupt)
        lsrReg = LPC_UART3->LSR;

        // determine type of error and set Rx status accordingly
        if (lsrReg & LSR_OE)
            uart3_rx_sts = UART3_OVERFLOW; // won't happen when using SW fifo
        else if (lsrReg & LSR_PE)
            uart3_rx_sts = UART3_PARITY_ERROR;
        else if (lsrReg & LSR_FE)
            uart3_rx_sts = UART3_FRAMING_ERROR;
        else if (lsrReg & LSR_BI)
            uart3_rx_sts = UART3_BREAK_DETECTED;
    }
    // RDA (receive data available) interrupt
    else if ( intId == RDA_INTERRUPT )      {
        // this interrupt occurs when the number of bytes in the
        //  HW Rx FIFO are greater than or equal to the trigger level 
        // (FCR[7:6])

        // read out bytes
        // clears interrupt when HW Rx FIFO is below trigger level FCR[7:6]
        // the number of loops should be the trigger level (or +1)
        while ((LPC_UART3->LSR) & 0x1)
            fifo_put(&rxFifo, LPC_UART3->RBR);
        rdaInterrupts++;
    }
    // CTI (character timeout indicator) interrupt
    else if ( intId == CTI_INTERRUPT )      {
        // this interrupt occurs when the HW Rx FIFO contains at least one
        //  char and nothing has been received in 3.5 to 4.5 char times.
        // read out all remaining bytes
        while ((LPC_UART3->LSR) & 0x1)
            fifo_put(&rxFifo, LPC_UART3->RBR);
        ctiInterrupts++;
    }
    // THRE (transmit holding register empty) interrupt
    else if ( intId == THRE_INTERRUPT ) {
        uint8_t i;
        // transfer 16 bytes if available, if not, transfer all you can
        for (i=0; ((i<16) && (!fifo_isEmpty(&txFifo))); i++)
            LPC_UART3->THR = fifo_get(&txFifo);
    }
}

static inline void uart3_interruptsOn(void) {
    LPC_UART3->IER = 0x07; // RBR, THRE, RLS
}

static inline void uart3_interruptsOff(void) {
    LPC_UART3->IER = 0x00; // !RBR, !THRE, !RLS
}

Handling FIFOs


The LPC176x UART design has hardware FIFOs built-in. Having these hardware FIFOs makes the UART hardware very efficient. However, handling the data flow between the hardware FIFOs, the software FIFOs, and the user can be very tricky. There are many situations that must be considered. The main issue is synchronization (the lack of such will cause data corruption). A correct UART driver design must always send the data in-order. Issues will occur if the driver mistakenly assumes that the software FIFO is empty and adds data directly to the hardware FIFO. If you look at the ‘print_byte()’ function, it has a lot of checks to ensure this does not happen. Throughout the code, the driver is constantly turning on and off the UART interrupts. This is because the interrupts can trigger at any time. While accessing shared memory, the interrupt code must be stalled. This is a tricky concept and is the basis for many embedded system software errors.

Advertisements

Multiplex EasyStar Airplane

Setup:

In anticipation of developing a quadcopter, I bought a Spektrum Dx5e/AR500 transmitter/receiver pair.  Knowing that the quadcopter would probably take many months to design and build, my wife lovingly thought to buy me an RC Airplane to hold me over until I finished my quadcopter.  She snooped on my computer and found that I had been looking at the Multiplex EasyStar.

The version I got (linked below) didn’t come with an Tx/Rx, servos, ESC, or battery.  I jumped on HobbyKing.com and bought two servos, a 2-cell lipo battery, and a cheap brushed ESC.  It turned out that the servos I bought were a low-voltage version and very weak.  Because I didn’t want to wait for another shipment from China, I ordered some better servos from HorizonHobby.com.

My final parts list became:

Total: $216

First Flight:

My first flight went horribly!  I was too anxious to fly and didn’t wait for a good day.  Let’s just say I flew around for a few minutes with completely no control and then ended my performance with a full throttle nose dive into the ground.  The cockpit area of the fuselage broke in half.  To those of you reading this deciding whether to get into RC planes or not, don’t let this discourage you.  I did EVERYTHING wrong.  Not only was I too anxious, but I was also nervous.  I left the throttle at full.  This was a bad combination.

I fixed the fuselage using the same glue I put it together with and some clear box tape.  It actually seemed to get even stronger!  I waited for a calm day then attempted flight number two.  I remembered to stay clam, set the throttle to full, hand launched, got control, then set the throttle to about half.  I couldn’t believe how easy it was to fly!  I was flying around with perfect control in no time.  I even got brave and applied full throttle and pulled off a loop.  It was ridiculously fun!  Then I realized I needed to land sometime soon or it would land by itself (another nose dive into the ground?).  Once I got close to the ground, I cut the throttle to about 1/4 power then slowly descended in a straight line.  It smoothly slid across the grass until it stopped!

Aggressive Flight Modification:

After a few flights, I determined that the aircraft was a bit sluggish when looping and turning.  I decided to do a few things to fix this:

  • Servo Cable Adjustment:
    Each servo cable attaches to the corresponding flap using a threaded clamp that sits in one of three slots.  When I first put the airplane together, I put the clamp in the least aggressive slot (furthest from the flap).  I just moved all the clamps to the most aggressive location (closest to the flaps).  This increased the aggressiveness of the aircraft dramatically.
  • Rudder Enhancement:
    Unlike a typical RC airplane, the rudder on the EasyStar controls the tail and the roll of the aircraft.  Its design is far too small for its purpose.  I used some thin, stiff cardboard and super glue to extend the length of the rudder.  The stock length is about 1″.  I modified it to be about 6″.  This turned out to be too much rudder when the aircraft was moving quickly.  I cut the extended flap down to about 3.5″.  It now works great.  The plane now responds very fast but I also don’t get out of control when I am moving quickly through a turn.

Durability:

This airplane is super durable.  I have crashed in one way or another numerous times!  Most of the time nothing happens.  I’m pretty sure my fuselage is now 40% glue, 45% tape, and 5% foam.  It still flys like a champ!  The location of the propeller is perfect for beginners.  It has never been harmed in all my crashes.

Too Aggressive?:

Apparently my modification makes the aircraft more aggressive than the foam wings can handle.  To get enough speed for the many tricks I’ve learned, I take the EasyStar up as high as I can while still feeling comfortable, then dive nose down.  Sometimes I just do this to see how fast I can get going and how close I can get to the ground until I pull up.  Each time I do this I can see the wings flexing back.  One day my dives were getting really fast and one of the wings finally gave out.  The wing snapped in half and the rest of the plane went spinning into the ground.  I could probably beef up the wing strength with some carbon fiber rods, however, $25 for new wings isn’t too much to ask for 6 months of abuse.

RC Receiver Interface

There are numerous RC Receiver interfacing techniques published on the web.  My quadcopter design is using only one processor for all its computation.  For this reason, I spent extra time designing the interfaces to be as efficient as possible.  Even though RC Receiver interfaces aren’t super complicated, they can cause real-time scheduling issues because of the number of interrupts and the time spent inside these interrupt routines.

Typical RC Receivers output the channel values in a given sequence of pulse width modulated (PWM) signals.  The transmitters and receivers I have tested are Spektrum made.  All Spektrum models seem to have the same design style.  After a small bit of testing, I found that the several channels of signals are output in the same order everytime and have a small time separation.  Since the standard for RC PWM is 50Hz, each pulse comes every 20 milliseconds.  The top 5 lines of the picture above show an example output of a 5 channel Spektrum made receiver.

In a microcontroller, the most accurate way to measure incoming pulses is to use an input capture with a timer.  Measurement occurs when a free running timer value is latched into a register when an event occurs.  Obviously, a higher frequency timer yields a more precise pulse measuring system.  Since most microcontrollers only have a few input capture pins, some special circuitry must be used to measure several pulses.  Since the Spektrum systems do not overlap their output pulses, a simple OR gate structure can be used to combine all the signals into one signal.

Since my quadcopter will be using the Spektrum DX5e and AR500, I’m using a quad 2-input OR gate IC (MC14071BCP), to create this combined signal.  As shown in the timing diagram at the top, there are still 2 voltage transistions per pulse.  This results in 10 interrupts for this design style.  For synchronization purposes, there is always a time between the last pulse and the beginning of the first pulse that is longer than the longest possible pulse.  To capture the values sent by the transmitter, the software is setup for an interrupt to occur on every transition of the signal.  All of the interrupts, except the last one, have a very small amount of time to process.  The last interrupt copies out the five pulse width values and triggers an availability flag so that the rest of the software can have access to it.

An interesting thing that I noticed while testing is that Spektrum has a constant delay of 60 microseconds between each pulse.  To make the system even more efficient, turning off the falling edge interrupts for the first four pulses then back on for the last interrupt results in 4 less interrupts.  For the first 4 pulse width values, 60 microseconds would just be subtracted off.

For my system, this OR gate structure serves another purpose.  The AR500 receiver runs on 5 volts, however, the microcontroller (LPC1768) runs on 3.3 volts.  Of course there are many ways for converting one to another but the MC14071BCP IC has a wide voltage range and is tolerant to 5 volt inputs when running at 3.3 volts.  Without extra circuitry, it will convert the five 5 volt signals from the AR500 into one 3.3 volt signal that represents all five channels.

In conclusion, I’ve found that using a simple OR gate IC is very efficient for pin usage, timer utilization, voltage translation, and interrupt time efficiency.

Update:

I implemented this RC receiver interface on the LPC1768 connected to a AR500.  My results are accurate to about 60-70ns.  This leads me to believe that the AR500 is running on the typical ATmega8 (or 168 or 328) microcontroller running at 16MHz (1/16MHz = 62.5 ns).

Update:

Due to popular request, here is the code I used on the LPC1768 (right-click download, then change the “.pdf” extension to “.zip”):
https://nicisdigital.files.wordpress.com/2013/09/rc_receiver_interface-zip.pdf

Quadrotor Parts List

Here is the initial parts list for my quadcopter design:

Product Description Quantity Price


Turnigy Brushless Outrunner

2217 20turn 860kv 22A
4 $53.12
($13.28ea)


Turnigy ESC

Plush 30amp
4 $48.76
($12.19ea)


Turnigy LiPo

5000mAh 3S 25C Lipo
1 $27.43


XT60 Connectors

Male/Female (5 pairs)
1 $3.19


Turnigy Balancer/Charger

Accucel-6 50W 6A w/ accessories
1 $22.99


Pyramid Power Supply

PS12KX 10-amp 13.8-volt
1 $46.99


APCProp

12 x 3.8″ Slow Flyer
2 $7.90
($3.95ea)


APCProp

12 x 3.8″ Slow Flyer Pusher
2 $11.84
($5.92ea)


Spektrum DX5e

5 Channel 2.4 GHz DSM2 Transmitter
1 $59.99


Spektrum AR500

5 Channel 2.4 GHz DSM2 Receiver
1 $39.99


LPCXpresso LPC1768

ARM Cortex-M3 Development Board
1 $28.50


9 DOF Sensor Stick

3-Axis Gyro, Accelerometer, Magnetometer
1 $99.99


OR Gate

CMOS Quad 2-Input
1 $1.00


Buffer

CMOS Quad 2-Input
1 $1.00


Aluminum Tubing

6061 1/2″ x 1/2″ x 72″, 0.063″ Wall
1 $21.14


Aluminum Sheet

6061 12″ x 12″, 0.063″ Thick
1 $10.06


Coleman Wire

16 Gauge 100 Feet
1 $11.24


Bullet Connectors

3.5mm 3 Pairs
4 $19.80
($4.95ea)

The aim of this design is for an extremely aggressive quadcopter. My design goals are for the stabilization system to handle VERY abrupt changes in attitude and able to recover from any acrobatic mishap. I’ll be adding a mode for acrobatics where absolute angles are not used for stabilization. Instead, the stabilzer will hold to a rotational rate. If an acrobatic maneuver goes south, one switch flip will be able to bring the helicopter back to a stabile hover. The only remaining control not adjusted by the stabilizer is the throttle.

I’m sure that there will be additional items I will need but haven’t thought of. I will try to keep this list up to date for those of you using it for your own copter build.

EDIT: added 16-gauge wire and 3.5mm connectors