Research Experience for Undergraduates: Unmanned Aerial Vehicle Design, Phase I

By: Sylvester Meighan Damon Cardenas Mark Finch Laurissa Wilson

The University of Texas at San Antonio August 10, 2007

This work was supported in part by the National Science Foundation, Grant Number: NSF 0649172

Table of Contents ________________________________________________________________________ Title 1.0 Abstract 2.0 Introduction 3.0 Overview 4.0 Attitude Heading Reference System 4.1 Microcontroller 4.2 Accelerometer 4.3 Gyro 4.4 Pressure Sensor and Pitot tube 4.5 Sonar 4.6 Single Poll Double Throw Analog Switch 4.7 Magnetometer 4.8 Pulse Width Measurements 4.9 Liquid Crystal Display (LCD) 4.10 Serial Communication 4.11 Wireless Communication 4.12 Sensor Data Storage 5.0 GUI 6.0 Conclusion Appendix A. Code B. CIFER installation on the Ubuntu 7.04 distribution of Linux References page p. 1 p. 1 p. 2 p. 3 p. 3 p. 5 p. 7 p. 8 p. 9 p. 10 p. 11 p. 12 p. 13 p. 14 p. 14 p. 14 p. 17 p. 20

p.22 p.55 p.56

1.0 Abstract Modeling of nonlinear systems in aeronautics can be a difficult task to achieve by using physics alone. This is due to the fact that textbook physics uses equations derived in an undisturbed and restricted environment. In order to create a more accurate and precise mathematical model, a different approach can be taken. Rather than using purely math and physics to derive models that account for environmental disturbances, there are modern day computational tools that allow us to use reverse engineering to achieve realistic and accurate results. It may be more efficient to measure the response of a system to known inputs and use the obtained data to predict future responses of the same system. The most important aspect to this approach is the collection of time history data. To achieve this task the ATmega128 microcontroller was used as the main computer for the inertial measurement unit for data collection. The MMA7260Q 3-axis accelerometer measured the pitch, roll, and yaw angles of the aircraft while two IDG300 gyros where used to measure the pitch, roll, and yaw rates. Two pressure sensors were used to measure the barometric pressure and the airspeed. Finally, an ultrasonic ranger was implemented for low altitude measurements.

2.0 Introduction Since the first deployment of Unmanned Aerial Vehicles (UAVs) in the military, continuous research and development efforts have been focused on enhancing UAV technology. Militarily, the usefulness and importance of UAVs cannot be underestimated, and their use in the civilian sector shows promise as well. Two very important advantages of employing UAVs are that human lives aren’t unnecessarily endangered, and tasks or objectives that were previously considered impossible are made possible. Reconnaissance can be carried out and hostile targets can be assessed and eliminated without sending one soldier across enemy lines. The potential capabilities of UAVs would also be particularly advantageous to law enforcement agencies. For example, a single, small UAV could be used by police officers to assess a situation beyond a building that is obstructing their view, or perhaps serve as a delivery system to emplace chemical sensors [1]. A less obvious but equally useful function that could be performed by one or more UAVs is that of establishing an extended network for military communications. When missions must be carried out across rugged or mountainous terrain, terrestrial communication capabilities can be reduced. In such a situation, a team of UAVs could be strategically placed to relay signals between distant ground locations. This type of system would give commanders a distinct advantage in ground-based operations. Limitations of current UAV technology, however, have prevented this system from reaching its full potential. Since the range of wireless transceivers on UAVs is limited, as many UAVs as possible would be needed to maintain a satisfactory communication link. Unfortunately, the cost of UAVs designed for military use is high, so research has been conducted to implement a UAV-based communication network with a minimal number of aircraft [2]. In fact, although companies like Aerovironment have already built and sold fullyfunctional autonomous UAVs such as the Dragon Eye, there are many aspects of UAV functionality and implementation that are still being researched [3]. An example is the prospect of utilizing multiple UAVs operating autonomously to carry out missions while communicating

and coordinating with each other. Disregarding the ambiguity surrounding the term “autonomous,” which could refer to varying levels of self-directed flight, using a system of multiple UAVs to carry out a mission could be implemented with a central control station. The problem facing researchers, however, is that using a central control station that must communicate with every aircraft creates data bandwidth limitations. Thus, the current research in this area involves designing autonomous UAVs to communicate directly between each other to coordinate tasks, instead of relying upon a central control station [4]. The ultimate goal of this research project is to not only develop a fully autonomous UAV, but also to integrate an autonomous UAV into a system of autonomous Unmanned Ground Vehicles (UGVs). Ideally, the UAV would serve as both a relay for UGV communications and as a target acquisition and reconnaissance vehicle. If successfully implemented, such a network of vehicles would be very useful for performing critical missions such as search and rescue. Obviously, the initial requirement is to design and build an autonomous flight control system for the aircraft. This is quite an involved task in itself, as autonomous flight requires the use of several sophisticated sensors and a robust control system. As an example of the degree of complexity involved, the U.S. Army’s SOAR (Silent Operating Aerial Reconnaissance) UAV uses 12 sensors, a GPS receiver, and a microcontroller to fly autonomously [5]. The goal of the initial phase of this project was to design and build an Attitude and Heading Reference System (AHRS) consisting of several microelectromechanical system (MEMS) sensors, a microcontroller, and a data logging system, as well as a graphical user interface (GUI) to monitor sensor data from a ground station. The sensor data will be used in future phases of the project to create a model of the aircraft, and, ultimately, an autonomous control system.

3.0 Overview The Research Experience for Undergraduates program at the University of Texas at San Antonio will consist of three summers of work. Phase one of the research for the unmanned aerial vehicle group consists of laying the foundation for the next two summers’ research endeavors. This encompasses design of the aviation electronic systems (avionics) to be used to monitor the flight dynamics of an aircraft. These electronic systems will have sensors for data collection, an LCD display and push buttons on the unit for user interaction, wireless transmitters for data transmission, an SD card for memory storage, along with control software on a base station computer for aircraft monitoring and control. Phase two of the research will consist of two parts. Modeling of the aircraft to be used and control system design. The modeling will use the hardware previously developed to gather test flight data for use in system identification of the aircraft. Then the developed model will be tested with simulations and implemented in autonomous flight stability and navigation control algorithms. Finally, phase three will expand upon the autonomous flight capabilities and wireless communication schemes previously developed to achieve more intelligent path planning strategies, collision avoidance schemes, and communication with unmanned ground vehicles for collaborative mission execution. This summer marked phase one (hardware and software ground work) in the three part plan. The first major hurdle in designing control algorithms for aerial vehicles involves the calculating of a mathematical model. This model will be used in simulations to understand the

dynamics of the aircraft and in matlab and simulink for control system design. Developing a model can be achieved by two different methods. A physics based approach requires aerodynamic, mechanical, and atmospheric condition modeling. Because of the nonlinearity of aircraft systems, this method can be tedious and result in less than desirable approximations inherit in math and physics based modeling. The alternative approach is called system identification. System Identification or System ID, requires the collection of sampled time history data from test flights which is then conditioned and used to calculate the mathematical model of a system. Using real-world measurements of an aircraft’s response to pilot control inputs reduces the time required to develop a model and can produce highly accurate representations because of the inclusion of real-world disturbances that would have been difficult or impossible to account for using physics alone . 4.0 Attitude Heading Reference System 4.1 Microcontroller The collection of accurate time history data serves a pinnacle role in the system identification process. Sensors are required for perception of forces and moments about translational and rotational axes and a microcontroller is used to coordinate these actions along with data logging and wireless communication. The ATmega128 8-bit microcontroller was chosen as the main computer processor in this system. This processor was chosen for the following reasons. • • RISC architecture - provides for more efficient and compact code. Code portability – this feature is very useful when development requires more resources than available in the current microprocessor and an upgrade is necessary. Code portability removes the need for unnecessary rewriting of code in these and similar instances. User community – the atmel avr user community proves to be a very helpful resource which can be found through websites like or other user forums. The wealth of code found online also aided in the early phases of the development process.

The AVR-MT-128 development board was utilized for its pre-configured hardware peripherals. As illustrated in Figure 4.1 below, the power, system clock, LCD, and serial port and other connections are already in place on the microprocessor. This saved time soldering components necessary to initially get started. The AVR-MT-128 also provides two external pin headers for utilizing the remaining I/O pins of the ATmega128 and also one six pin header to access the 10-bit analog to digital converter of the microcontroller. A JTAG connection was utilized during the design process to download code to the flash memory and perform in circuit debugging. The AVR-MT-128 board came with demo software that allowed printing of ASCII text to the LCD screen, transmission of ASCII text through the serial port, and responses to button push events. This code was modified to form the basic framework for the attitude heading reference system. AVR Studio 4 is an integrated development environment specifically made to write assembly code for Atmel computer processors. It can be downloaded from Atmel’s main website

for free. In order to write C language code. All of these features created a productive environment for code development and debugging.1 – AVR-MT-128 Development Board Schematic . winavr: avr-gcc has to be installed and used as a plug-in to the latest version of AVR Studio 4. register values. The compiler allows a programmer to monitor variables in memory. and port states in debug mode. Winavr is a Microsoft Windows port of the Linux gcc compiler tool chain and Libc a standard avr C library for Atmel microcontrollers. AVR Studio provided a strong set of tools for code development and debugging. It also allows the user to step through the code or blocks of code. Figure 4.

45mm QFN • High Sensitivity (800 mV/g @1. The distances between the center surface and side surfaces create two variable capacitances that are proportional to the force of gravity. Sparkfun. by and acceleration (gravity). . Of the following features. the selectable sensitivity and low power consumption made this unit an excellent option for uav applications.2 V ñ 3. Below in Figure 4.5 g) • Fast Turn On Time • High Sensitivity (1. The MMA7260Q is a three-axis microelectromechanical (MEMS) accelerometer. closer to one of the fixed surfaces and further from the other.5g/2g/4g/6g) • Low Current Consumption: 500 μA • Sleep Mode: 3 μA • Low Voltage Operation: provided a convenient header board that had this surface mount sensor mounted on a larger dip breakout board for prototyping. This measured force may be used to calculate the angle of the axis with respect to gravity. An accelerometer is a device that measures the force of gravity exerted along an axis. y. The center surface is flexed in either direction. Features: • Selectable Sensitivity (1.2 Accelerometer Some characteristics of interest in system identification and control are pitch. High Shocks Survivability • Pb-Free Terminations • Environmentally Preferred Package • Low Cost • 16 LEAD QFN This accelerometer provides three analog voltage outputs that are proportional to the force of gravity exerted along the x.5 g) • Integral Signal Conditioning with Low Pass Filter • Robust Design. and z axis of the device.2 is a representation of the inside of the accelerometer. roll. and yaw angles and rates of rotations (the aircraft attitude). There are two fixed metal surfaces with a third surface in between that is free to move closer or farther from either of the fixed surfaces.6 V • 6mm x 6mm x 1.4.

Instead of finding an angle.2 . This scheme was used to determine the orientation of the sensor board (and attitude of the aircraft). As illustrated in Figure 4.3 Gyro Similar to the accelerometer. . the voltage when gravity is acting parallel to the measured axis) where measured and saved in memory for each accelerometer axis. The three output pins of the accelerometer where then connected to three different analog to digital converter pins on the ATmega128 for measurement. The maximum voltages (i.Simplified Accelerometer Model The sensitivity selection pins (gs1 and gs2) were both connected to 3.Figure 4.6 g’s with a sensitivity of 200mV/g. The following values where measured to use in the attitude calculation process. (y2 . a rate gyro measures the rate of rotation about a given axis. The code found in the appendix was written to read each analog to digital converter pin and convert the 10-bit number into a voltage value to be stored in memory and used in the attitude calculations. 4. the sensor is upside down and vice versa. The voltages present when each axis was perpendicular to gravity where also measured and stored in memory. In code.y1) m = ----------(x2 .3V to set the sensor to measure accelerations of +/. The linear relation between the angles the board made with respect to gravity and the voltage measured at that angle.e. the z-axis was used to tell if the board was upside down or right side up.x1) + y1 The voltage from the z-axis output of the accelerometer was also measured when the zaxis was perpendicular to gravity but was used differently. this allowed determining the quadrant that the other two axes are in.3 below. for two different positions (perpendicular and parallel to gravity) where used to predict the unknown angle of the sensor board for a given measured voltage. Sparkfun.x1) newY2 = m*(newX2 . When the z-axis is less than the perpendicular voltage. When the sensor is upside down the measured angle must be in quadrant I or III and inversely when the sensor is right side up the other measured angle are in quadrant II or provided a breakout board of the IDG-3000 rate gyro used in the inertial measurement unit.

PD is the dynamic pressure and R is density of the surrounding air. The pinout for the MPXV5004 pressure sensor that we used is shown below in Figure4. The pressure sensors measure the dynamic and static pressure of the air around the plane. 4. This gyro provides 2mV per degree of rotational rate. The output voltage of the pressure sensor ranges from 1 to 4. The equation for finding the velocity from the pressure sensors is shown below: 2[PD (kPa)] V (m /s ) = -------------R (kg/m3) 2 2 2 In the above equation.3 . The Vout of the chip must be used for analog to digital conversion. The gyro rates coupled with the accelerometer angles can be used as feedback in a stability and navigation control system.Rotational Axes of Gyro The IDG-300 is also an analog device and provides output voltages that are proportional to the rate of rotation. The density of air is typically around 12kg/m3.4: Figure 4.4 Pressure Sensor and Pitot tube The pressure sensors are used mainly to find the airspeed of the plane.9V.4 – Pressure Sensor Pinout The first pin is marked with a notch on the lead. and the range of dynamic pressure for the pressure sensor is between 0 and 3.92kPa (kN/m2) or 0 – 400 mmH20.Figure 4. . Velocity is denoted with the character V.

1 mmH2O / 102. The specifications for the LV MaxSonar (Figure 4. gives the distance of the plane from an object. The equation above compensates for the fact that the pressure sensors only output voltages from 1V to 4.5 V Pulse Width Modulation (PWM) capabilities High Sensitivity (800 mV/g @1.0408 = 1 kPa .9V. the same sound is reflected off of an object and received by the sonar.For this project. thus.5 Sonar Another important device for this project is the sonar. A sound wave is sent from the face of the sonar. The time between the transmission and the reception of the sound wave. The equation for analog to digital conversion is shown below: adcRead 1024 bits 500 mmH2O mmH2O = (---------.5 V – 5.__ ------------) X --------------1 4. To make sure the sonar does not stop ranging data. the RX pin should (as a safety precaution) be pulled high. This switch is then connected to the ADC3 pin of the ATmega128. can be left without connections.5 g) Serial Communication Readings can occur every 50 ms Low Cost Figure 4. The +5 pin is the supply voltage pin and can be connected to anywhere from in the specified range for input voltage (2.5 – Sonar Sensor For this project. multiplied by the speed of sound in air (≈344 m/s). the pressure sensors were attached to NC4 and NO4 of the analog switch. The GND pin must be connected to .5 below) that was used for this project are: • • • • • • • Locates distances from 6 inches to 255 inches Low Voltage Operation: 2.5 – 5. The BW pin may also be unconnected. and a short time later. The sonar is used to find the low altitude of the plane. The basis of the sonar is using sound to approximate distance from an object ahead of the device. 4. The TX pin is used only for serial connections.9 1024 bits . we did not make use of the PWM capabilities.5V).

you can use 8 input channels for four analog to digital ports while acquiring the appropriate data when necessary. and a sonar (1 ADC port) we will need a total of nine analog to digital ports. Using the IN pin to switch between whether NC or NO is selected. 3 axes for the gyroscope (3 ADC ports). Clearly a method to help lower the amount of necessary analog to digital ports is necessary.3V. Finally. Thus the reason for why this device is important is simple. Next.00723V is obtained by dividing the supply voltage by 512. The EN pin needs to be grounded for the .= Distance in inches 1 1024 bits 1 Volts 7. The .X -----------. to the proper supply voltage of 3. there are not enough analog to digital ports to support the analog devices we are using. attach the GND pin 8 and the EN pin 15 to the ground. 4. the following conversion equation must be used: adcRead bits 5 volts 1000 mV 1 inches -------------. as can be seen from the processors’ schematic.23 mV .6 Single Pole Double-Throw Analog Switch A fundamental device when working with the ATmega128 microprocessor is the single pole double-throw analog switch. The schematic in Figure 4. and are output from one of the COM pins.6 below shows each of these channels: Figure 4.X ----------. and since we will be using the following: 3-axis accelerometer (using 3 ADC ports). The 1024 bits is obtained from the 10-bit resolution ADC. . When referring to the reading from the analog to digital port. . To fully utilize the potential of the switch. There are a total of eight analog to digital ports in the ATmega128 microprocessor. one must make the proper connections. The analog switch is capable of interchanging between two channels of input with eight possible inputs. The 5V is the maximum voltage obtained from the ADC on the ATmega128 processor. which is used for the SonarMax chip. the AN pin must be connected to one of the analog to digital ports on your microprocessor.the ground.6 – Analog Switch The eight input signals go into either the NC or NO channels. The first step in doing so is connecting the V+ pin 16.X ---------. two pressure sensors (2 ADC ports).

The pinout. The SS line is located on the PB0 pin of the ATmega128 microprocessor board. the accelerometer inputs and one pressure sensor input were assigned to the NC channels. The known information about the magnetometer. The SPI protocol is simply a method for a microcontroller to communicate with another device that acts much like a mini-microcontroller. **Note: For the SPI to work. The MicroMag 3 magnetometer needs two more connections before wiring is completed. This project does not currently incorporate the magnetometer coding in the main program for the simple reason that the code is not fully functional due to time constraints. The magnetometer. and the MISO line (PB3) must be set as an input. is cited. These readings can be affected by different devices that may possess their own relatively strong magnetic field (for instance function generators). The magnetometer is not currently hardwired to the present board for the project either. The magnetometer is probably the most difficult piece of equipment to set up for this project.” and the MicroMag 3 is the “slave. and the S-Clock (SCLK) line. The magnetometer in turn transmits two bytes of information that describe the magnetic heading. Finally. and should be set as an output. as well as a picture of the magnetometer can be seen below in figure x: . the MOSI line (Port B pin 2 (PB2)) must be set as an output. Slave-Select line (SS). This should be an output from the ATmega128 and should be set low. Master-In Slave-Out line (MISO). the SPI enable bit in the sixth bit of the control register must be set high. and the SPI flag in the seventh bit of the status register must be set low.7 Magnetometer Magnetometers can be used in flight to determine the magnetic heading of an object. For this project. For this specific task. the SCLK line of the ATmega128 is located at pin PB1. uses a method known as serial peripheral interface (SPI) to communicate with the ATmega128 processor. The idea is that the ATmega128 sends one byte of data to the MicroMag 3 (magnetometer) to be processed by the chip. The earth has a magnetic field encompassing it due a multitude of factors. Attach the remaining input lines to the NO channels. Magnetometers can sense the subtle changes in the magnetic field of the earth to determine a heading. so plan to read all of those inputs together.switch to be enabled. Finally. attach the IN pin 1 of the chip onto any available output port from the microprocessor chip. Now attach a set of inputs to the all the NC lines. With SPI. there are 4 lines which must be connected for communication to occur successfully: Master-Out Slave-In line (MOSI).” Since the ATmega128 is the master. however. The MOSI and MISO lines are used to determine which processor is carrying out the main functions. the chip then switches from the NC channels the NO channels. the ATmega128 is known as the “master. The MicroMag 3 acts as a mini-microprocessor and performs certain operations based on what data is sent to it. By changing the status of the output port from low to high. A reset connection with one of the open ports on the ATmega128 (set as an output) is necessary as well as a port for the data ready (DRDY) line (set as an input). unlike the other sensors. and all the gyroscope inputs as well as one pressure sensor were assigned to the NO channels. 4. Attach all the COM channels to the coordinating analog to digital (ADC) ports. All the NC lines will be on or off at the same time.

8 Pulse Width Measurements Pulse width is important for this project because it helps to determine the amount of power that the servos are using which in turn describes the response of the aircraft to a command. computing or transmission of data can occur in the magnetometer. When the DRDY line is set high. The magnetometer then computes the magnetic heading and sets the DRDY line high. After the reset line is pulsed the magnetometer can receive a command byte from the ATmega128. 4. the magnetic heading can then be retrieved by the magnetometer by reading from the SPDR in your main program. alerting the servos to move an aileron or an elevator in a specific manner.7 . When the ailerons or the elevators are turned by a user.Figure 4.8: . the reset line must be pulsed from low to high to low. An example of how this system works can be seen below in Figure 4. This is completed by writing to the SPI data register (SPDR) in your main AVR program.Magnetometer Before any reception. a pulse goes to each of the corresponding servos.

9 Liquid Crystal Display (LCD) The LCD screen is used in the final project only to display the status of the SD card. The LCD is connected to Port C of the ATmega128 processor and is very easy to implement into a program. as shown in Figure 4. The time received from the servos should be in the range of 1. 4.8 above. also a very useful tool in the debugging of a program. The counter tells the length of the pulse in clock cycles. It provides a means to quickly see any particular bytes that you are working with while a program is running. For this project. The equation is: (number of clock cycles) / (clock speed in MHz) = time in μs. the pulse output of the servos must be connected to an input capture of the microprocessor. To acquire this data. we can see that the length of a pulse changes the position of a servo. In the future. the IC1 pin (which can be located in Port D bit 4 (PD4)) was used. It is.75 ms. Using the amount of clock cycles and the speed of the clock.Figure 4.8. . when the plane is more ready to be made autonomous.25 to 1.8 – Servo Pulse Width In Figure 4. The interrupt must begin a counter on the rising edge of a pulse and stop the counter on the falling edge. this information can help the plane figure out on its own how much power to send to each servo when it is ready to turn. however. the length of the pulse in time can be obtained. The output signal lines from the servos also need to be attached to the voltage supply and the ground of the microprocessor. which is the device that stores the sensor data. the servos react in a certain fashion. After the remote control sends the signal to the servos. an interrupt for the pulse readings must be set up. In the main AVR program for the project. and that data can be recorded to find the servo reaction.

as technology advances. Once the aircraft enters its full operational stage.10 Serial Communication Serial communication is one of the simpler techniques to use with the ATmega128 microprocessor board. Additionally. with the X-CTU program being the preferred method. which. could take a few minutes.11 Wireless Communication Wireless communication is one of the possible methods for obtaining data. a data storage method capable of storing large amounts of data is also necessary. so a data storage method needs to be able to record data fast enough to track high frequency responses of the aircraft. but their size. In regards to possible future upgrades. depending on the pilot’s capabilities. also using a serial cable. Many different types of memory products are available. of the aircraft. Therefore. data can be streamed wirelessly from the sensors on the plane to a bay station. sufficient sensor data needs to be recorded during flight. The board used in this project has a female serial connection attached to it. higher-resolution sensors might be implemented that would transfer larger amounts of data at higher rates. which means no special programming is necessary. This bay station can be a computer with either the hyper terminal or the X-CTU program. Only a few command lines in the main AVR program are necessary to send information through the serial port. Magnetic storage devices such as hard disk drives are available in huge capacities and support high data transfer rates. Two MaxStream Wireless modules are necessary. The determination of the frequency response of the aircraft in relation to servo inputs is particularly important to the development of an accurate model. Erasable Programmable Read-only Memory . requiring greater data storage capability.12 Sensor Data Storage To facilitate the development of a more accurate system model. In addition. 4. Sufficiency in this case refers not only to the length of time the data is recorded. a satisfactory model cannot be obtained without measuring responses of the aircraft to all possible stimuli.2 Gigabits per second [6]. high capacity data storage could prove to be very useful. The module is described as a plug and play device. weight and power requirements are unreasonable for small UAVs. One module connects to the bay station (using the X-CTU program) with a serial cable and the other connects to the ATmega128 board. The wireless communication in this project was completed using MaxStream’s XTend OEM RF module. but also to the rate at which the data is recorded. 4. An example of such a sensor already in use by certain military aircraft is the Advanced Helicopter Pilotage infrared sensor. Using a serial cable you can attach the ATmega128 board to a computer and use programs such as hyper terminal or X-CTU to obtain information (X-CTU is a program similar to hyper terminal with options geared towards using wireless modules). but most are either insufficient or impractical for our purposes. and thus controller. This inherently involves flying the plane through all possible maneuvers during a single flight. If a storage device (SD) card is not used.4. which has a data transfer rate of 1. reconnaissance missions would generate large amounts of data in the form of images and/or video that would need to be stored.

utilize only a 9-pin interface. The 3. and a resistive voltage divider network. the ground pin of the SD card was connected to a ground pin of the microcontroller. but current capacities are also limited.1 and consists of a SD/MMC breakout board. which is supported in hardware by the ATmega128 microcontroller. of which only 4 are required to be connected to the microcontroller. lightweight and fast. requires little power. and a pre-soldered pin header which provides easy connections to the SD card’s nine pins.3 volt signal levels coming from the SD card did not need to be altered in order to be properly recognized by the ATmega128. . and is versatile [7]. and SS. which would require too many precious I/O pins on the ATmega128 microcontroller. The resistive voltage divider network was required to step down the 5 volt signal levels coming from the ATmega128 to the 3. and they constitute the SPI interface: SCLK.(EPROM) is extremely small. such as the Compact Flash card and the Secure Digital (SD) card. the SD card is capable of communicating via the Serial Peripheral Interface (SPI) bus. The hardware interface for the SD card is shown below in Figure 4. our choice for data storage was the SD card.12. utilize a 50-pin interface. is available in large capacities. Compact Flash cards. The breakout board is simply a custom printed circuit board (PCB) with a springloaded metal slot in which the SD card is inserted. and is very economical. As an added convenience. has a very small footprint. on the other hand. MOSI. obtained from SparkFun Electronics. Power was provided to the SD card by a 3. It also supports fast data transfer rates. MISO. Not surprisingly. Also.3 volt level required by the SD card. Electrically-erasable Programmable ROM (EEPROM) is more convenient to erase than EPROM. specifically a Kingston 512MB SD card. while boasting high capacity and fast data transfer rates. As previously mentioned.3 volt regulator. the Compact Flash data transfer protocol is not supported in hardware by the ATmega128. only four wires are required to connect the SD card to the microcontroller. and to establish a common signal ground reference. but the erase sequence is inconvenient and current capacities are limited to a few megabytes. Flash memory. SD cards.

read single or multiple sectors.12. the SD card also supports a proprietary SD bus data transfer protocol. and they were integrated into the AHRS program. A program was also implemented on a Windows PC to capture sensor data sent from the ATmega128’s UART port and save the data to a text file for later use. the program is capable of writing sensor data to the card. and erasing sensor data from the card. Software functions were written to implement most of these commands.12. and an extensive command set is defined by the SD protocol for all actions to be performed by the card. and check status bits. change certain operating parameters. Using a menu-driven system on the AVR-MT-128 development board’s LCD. so the card must be initialized to establish which protocol will be used. In addition to supporting SPI bus communications.1 – SD Card Interface Schematic The SD card utilizes its own built-in controller to communicate with the host (ATMega128).2 below shows a flowchart for the AHRS program. erase specific sectors. . Figure 4. The SD card command set includes commands to write single or multiple sectors. reading sensor data from the card and transmitting it through the ATmega128’s UART port.Figure 4.

The GUI reads the data and displays it in an easy to read format. The second graphics-based GUI was developed using the OpenGL specification as well as the C++ programming language.7 kilobytes per second. this GUI .Figure 4. Future work will likely include the development of a file system for the SD card to allow more convenient transfer of data from the card to a PC. corresponding to a sensor data capture rate of 75Hz. thus requiring the use of the ATmega128’s UART port to transmit sensor data to the PC’s RS232 serial port. The purpose of this first GUI is to access data retrieved from the UAV and UGV which has been stored in a text file.0 GUI The text-based graphical user interface (GUI) was developed using the C++ programming language and the Allegro gaming library. Specifically. A file system (such as FAT16) was not implemented on the SD card due to time constraints. 5. the 512MB SD card would be capable of logging data for approximately 52 hours. Based on this rate. This GUI also reads data sent from the UAV and then graphically interprets the information. which would allow for logging multiple sessions of flight data.12.2– AHRS Program Flowchart Initial testing resulted in a data logging rate of approximately 2.

handling timers. exit(-1). and magnetic heading of the UAV. It allows for cross-platform compatibility on OS X. screen resolution. Originally developed by Shawn Hargreaves. Next. and UNIX. When starting a new project in Dev-C++. allegro_error). as a bonus this compiler features automatic installation. and timer. first select ‘Windows Application’ under the ‘Basics’ tab.or three-dimensional images to the display window while Allegro handles other functions such as collecting input. Also. This can include the mouse. Linux. click on the ‘Multimedia’ tab and select ‘Allegro Application’. The benefits provided by this library are numerous. altitude. pitch. When coding using the Allegro library. most notably the forums on the Allegro. Further help is readily available on various internet forums.h>). exit(-2). Low level routines such as mouse web site were most useful (Allegro).. if (allegro_init() != 0) { allegro_message("Unable to intialize allegro!\n%s\n". Then. as Allegro is able to support the OpenGL specifications with its own version. Allegro can also be installed manually if necessary. OpenGL can be used to draw two. #include <allegro. and loading data files (Foot and Ohannessian). the open source Allegro programming library now features updates and extensions from many users around the world. The screen resolution and graphics mode need to be set as well. AllegroGL. graphics modes. if (depth == 0) depth = 32. roll. allegro_error). yaw. Dev-C++ was used in this project. Next. Additionally. Windows. res.e. This links the library to the project.translates the speed. } depth = desktop_color_depth(). keyboard. } if (install_mouse()== -1) { . Allegro is a well-documented library containing examples for most of the predefined functions to illustrate how they are used within a program. there are several important steps that need to be remembered. and timers can be easily manipulated. the Allegro library and all I/O components need to be initialized in order to be used with the program. An example initialization function follows: void init() { int depth. if (install_keyboard()!= 0) { allegro_message("Unable to install keyboard!\n%s\n". set_color_depth(depth). there must be an include line for the library itself within the source file (i. source files can be viewed and edited on many different compilers. The decision to use Allegro and OpenGL allows for the two separate programs to be integrated in the future.

0. } set_window_title("UAV Sensor Data"). 0. *dp2. 640.) background color. NULL }. . 0. Below is an example dialog definition which allows user to input a file name for illustration: DIALOG user_input_dialog [ ] = { /* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) (dp2) (dp3) */ { d_box_proc. 0. 0. exit(-4). 0. 0. *dp3). exit(-3). 0. 0. allegro_error). 0. 0. 255. 10. 400. Allegro dialogs consist of fourteen elements: 1. allegro_error). 0. 6. integers specifying 2. 10. 7.) ASCII keyboard shortcut. } if (install_timer()!= 0) { allegro_message("Unable to install timer!\n%s\n". NULL. 3. heading. NULL. 5. and the Allegro GUI Clinic provide excellent references for an in-depth description of these fourteen dialog elements. if (res != 0) { allegro_message("Unable to set any graphic mode!\n%s\n". a function of any type can be used here as long as it is typecasted to a void* when it is called from within the dialog (Leverton). 0. 0.) X-position. NULL }. exit(-5). 4. and 9. 10. } res = set_gfx_mode(GFX_AUTODETECT_WINDOWED. Also note that in addition to the built-in dialog procedures. 0. { d_edit_proc. Both Allegro.) foreground color. The dialog is a description of the GUI. except that these fields defined as type void*. The last two integer elements can be used as the programmer wishes. 0. }//end init The driver that controls the Allegro GUI is called a dialog struct. allegro_error).) Width of dialog object. 0. The same applies to the last three elements (*dp. 0. 10. input. the programmer is free to define their own processes or to even derive procedures from the existing library for greater control. 8. 0. 350.) an integer pointer to dialog procedure. NULL. NULL }.) flags (status indicators for dialog objects). 0. Generally. { d_text_proc. 0. 0).) Y-position. 0. NULL.) height of dialog object. 40.allegro_message("Unable to install mouse!\n%s\n". 0.

0 Conclusion Our research and design efforts during phase one of the UAV project yielded a nearly operational Attitude and Heading Reference System. 0. 0. such as a text file. The current state of the AHRS and its associated hardware could benefit from a few additional improvements. NULL. rate gyro. the sensor data would be written to the SD card in a file format. 6. 0. adding unnecessary weight to the aircraft and potentially severely affecting the plane’s center of gravity. 0. In the future. 0. more efficient GUI design. Further refinements could also be incorporated. so a custom printed circuit board was deemed necessary for future development. pressure sensors. While we fell slightly short of our goal. As mentioned previously. sonar sensor. 0. . NULL } }. 0. 0. 0. 0. 0. Using the ATmega128’s UART port to transfer sensor data to the PC’s serial port was relatively inefficient and could impede the system identification process. NULL} { d_idle_proc. such as a smaller. (void*)exit_callback. the progress made was significant and should provide an excellent starting point for students in phase two of the REU UAV program. 0. Additionally. 0. these two GUIs make it easier to view and interpret the sensor data at a glance. lighter-weight battery system. a FAT-based file system would significantly speed up the process of transferring sensor data from the SD card to a Windows PC. In conclusion. 13. The features supplied by Allegro and the OpenGL specification will allow this and other additional features to be added to the GUI as the REU UAV and UGV research project further develops. NULL. The batteries used during our phase of the project were bulky and heavy. 0. 0. NULL. 13. pulse-width measurement function and data storage system represented the majority of the AHRS’s complexity. 0. 0. 0. Utilizing the dialog struct allows for a compact. 0. 0. and resulted from a lack of familiarity with the proprietary SPI interface used by the sensor. { NULL. This would certainly be a convenience. Our difficulties experienced with the magnetometer were minor. 0.{ d_keyboard_proc. high-current rechargeable battery to power both the AHRS board and the servos in the aircraft. it would be advantageous to this project to increase the functionality of the GUIs to retrieve and display data in real time. 0. With a FAT-based file system. Possible wiring issues on the AHRS board led to difficulty while troubleshooting faults. but it is not a necessity. 0. 0. NULL. a custom mount will need to be fabricated to secure the AHRS board inside the aircraft. NULL }. NULL. which could then be recognized and quickly downloaded by a PC upon inserting the SD card into a standard USB card reader. 0. analog switch. The integration of the accelerometer. 0. 0. One option would be to use a single. 0. Continued work with the magnetometer should yield successful results and bring the AHRS into a fully operational stage.

The AHRS system we developed should provide the students participating in phase two of the project with an excellent opportunity to complete the system identification process. as well as to develop a robust controller for the UAV.The completion of phase one of the REU UAV research project represents a significant step towards the goal of implementing a fully autonomous unmanned aerial vehicle. .

h> #include "sd_card.h" #include "lcd.h" #include "bits. AHRS Code /****************************************************************** * AHRS Code * * Authors: * Sylvester Meighan * Damon Cardenas * Mark Finch *******************************************************************/ //-----------------------------------------------------------------// Includes ******************************************************* //-----------------------------------------------------------------#include <stdio.h> #include <util/delay.h" //-----------------------------------------------------------------// Defines and Variables ****************************************** //-----------------------------------------------------------------#define B1 (PINA&BIT0) #define B2 (PINA&BIT1) #define B3 (PINA&BIT2) #define B4 (PINA&BIT3) #define B5 (PINA&BIT4) #define #define #define #define #define #define RELAY_HIGH RELAY_LOW TXD RXD DALLAS X_ACCEL PORTA |= BIT6 PORTA &= ~BIT6 (PIND&BIT3) (PIND&BIT2) (PINA&BIT5) 0 .Appendix A.h" #include <string.h> // #include <avr/iom128.h> #include <avr/interrupt.h" #include <math.h> #include "system.h> #include "delay.h> #include <avr/io.

//-----------------------------------------------------------------// Function Definitions ******************************************* //-----------------------------------------------------------------// Initialize Accelerometer --------------void initSensors(void). // Print Gyro ----------------------------void printGyro(void).#define #define #define #define #define Y_ACCEL Z_ACCEL X_GYRO Y_GYRO Z_GYRO 1 2 3 4 5 #define CARRIAGE_RETURN 0x0D #define LINE_FEED 0x0A #define polldelay 2000 unsigned char ch. // storage for times unsigned long pulseClocks. // counter for timer 1 overflow unsigned int risingEdge. int mode. fallingEdge. // variable to store Sensor Data // variable to hold which mode to // need this to store the special function register values before // disabling interrupts unsigned char ovCounter. // Check button input --------------------void buttonScan(void). unsigned short sensorData[100]. // Pulsin Function ------------------------ . // Print Accelerometer -------------------void printAccel(void). // storage actual clock counts the pulse unsigned long pulseLength. // Read Sensor ---------------------------void readSensors(void). stay in uint8_t sreg.

k = 0. // initialize the adc SPIinit(). sendCmd(CLR_DISP). while(1){ sendCmd(CLR_DISP). //Init Uart0 lcdIni(). // Disable interrupts. //----------------------------------------------------------------------// Main Function ******************************************************* //----------------------------------------------------------------------int main(void){ // Initialize Stuff ************************************************* initPorts(). char SD_data[512]. sendStr("IMU board v1. sendStr("B1:Read B2:WriteB3:Erase"). uint32_t i.float pulsin(). SD_init().0 Sylvester M").i<4. sendCmd(CLR_DISP).i++){ k += (uint32_t)SD_data[i] << (i*8). delay_ms(200). sendCmd(DISP_ON). i = 0.j.k. //LCD initialisation adcInit(). DDRA |= 0b10000000. //cli(). //sreg = SREG. } . int *a = &i. unsigned short temp. for(i=0. if(mode == 1){ readSD(0. //Init Uart1 initUart0(). DDRB |= 0b00000001. initSensors(). initUart1(). mode = 1.SD_data). buttonScan().

if(B1 == 0) break. sendStr("Erasing.SD_data). delay_ms(300).i<512.i++)SD_data[i] = 0.. } for(i=0. for(i=0.sendCmd(CLR_DISP). sendStr("Erasing finished").i++){ SD_data[i] = (k >> (i*8)). for(i=0. } if(mode == 2){ sendCmd(CLR_DISP).i++)SD_data[i] = 0. for(k=1.i<512. } } return 0.i<4. for(i=0. sendStr("Reading finished").k<500000. delay_ms(400). sendStr("Writing finished").j++) storeTelemetry(SD_data. } if(mode == 3){ sendCmd(CLR_DISP)...a). } writeSD(0. sendCmd(CLR_DISP). if(B1 == 0) break. sendStr("Reading.. Hold B1 to quit"). sendCmd(CLR_DISP). eraseSD().i<=k.SD_data).i++)SD_data[i] = 0.. for(i=1. sendStr("Writing. Press B1 to quit").").i<512.i+=5){ readmultSD(i. delay_ms(300).j<11. writeSD(k. } sendCmd(CLR_DISP).k++){ for(j=0. }//end main() . i = 0.SD_data)..i+5). writeSD(0.

h adcInit() // DDRF &= 0b11100000.3V 6g 200mV/g */ // The following line is in adc.3V GND 4g 300mV/g 3. float tempVolt.i. gs1. m.//----------------------------------------------------------------------// Initialize Sensors ************************************************** //----------------------------------------------------------------------void initSensors(void) { /* GS1(PG1) GS2(PG1) G-range Sensitivity GND GND 1.i++) sensorData[i] = 0.5g 800mV/g GND 3.z.5g // wake up the accelerometer //----------------------------------------------------------------------// Read All Sensors **************************************************** //----------------------------------------------------------------------void readSensors(void) { int channel. // set sensitivity to 1. for(i=0.i<100. .x1) + y1 // (x2 .y1) // m = --------newY2 = m*(newX2 .3V 2g 600mV/g 3. PORTG |= 0b00000001. } // configure adc0-4(x.y. gs2) PORTG &= 0b11111000. tempAngle. // Accelerometer Stuff ********************************************** // // (y2 .p. receive analog input DDRG |= 0b00000111.s) as input to // set PG0-PG2 as output (sleep. float sensor.3V 3.x1) // ****************************************************************** PORTA &= 01111111.

// Is the board right-side-up or up-side-down? // z neg. float xMax = 430. sensorData[4] = tempVolt. float yLevel = 376.accelerometer voltage channel = 2. // calculate x-angle m = (0 + 90)/(xLevel .xMax). flag: upside down = 1. float yMax = 497.832. float zMax = 0.08. float xLevel = 309. } // X Accelerometer ********************************************* // get x . float zLevel = 333. rightside up = 0 if (tempVolt < zLevel) { sensorData[5] = 1. adcInit(channel). // 10-bit voltage with z-level to ground // Z Accelerometer ********************************************** // get z .824. tempAngle = m*(tempVolt-xLevel) + 0. if(tempAngle < 0) { sensorData[1] = 1. } else { sensorData[1] = 0. } //set the neg flag ._delay_ms(5). tempVolt = adcRead().664.248. } else { sensorData[5] = 0. tempVolt = adcRead(). adcInit(channel).accelerometer voltage channel = 0.

} else { sensorData[3] = 0. } else { tempAngle = 180 . } } sensorData[0] = sqrt(square(tempAngle)).tempAngle. // calculate y-angle m = (0 + 90)/(yLevel .tempAngle.yMax).accelerometer voltage channel = 1.tempAngle.tempAngle. if (tempAngle < 0) { sensorData[3] = 1. tempVolt = adcRead(). // if upside down // and x is negitive // shift appropriately // or x is positive // shift appropriately // Y Accelerometer ********************************************* // get y . } else { tempAngle = 180 . adcInit(channel). } // correct if tempAngle > |90| if(sensorData[5]==1) { if(sensorData[3]) { tempAngle = -180 .// correct if tempAngle > |90| if(sensorData[5]==1) { if(sensorData[1]) { tempAngle = -180 . // if upside down // and y is negitive // shift appropriately // or y is positive // shift appropriately . tempAngle = m*(tempVolt-yLevel) + 0.

X --------.= (adc bits)*2.7408.xGyroStill)*2.44140625. // get x .gyro voltage channel = 0. // get y . // adc value for gyro at rest float yGyroStill = 286. // set the // convert from 'bits' . _delay_ms(5). // adc value for gyro at rest float zGyroStill = 321. tempVolt = adcRead(). } tempVolt = (tempVolt . already uses ch 0 . PORTA |= 10000000.72. //sensorData[6] = tempVolt.44140625 deg/s // 1024 bits 1 volts 2 mV // ************************************************************************ float xGyroStill = 313. adcInit(channel).X ------.gyro voltage channel = 1.334.} } sensorData[2] = sqrt(square(tempAngle)).2 so // the gyro is using these channels right now // for testing purposes (change them later) // // adc bits 5 volts 1000 mV 1 deg/s // --------. if (tempVolt < xGyroStill) negative flag { sensorData[7] = 1. // to 'deg/s' sensorData[6] = sqrt(square(tempVolt)). } else { sensorData[7] = 0.X ------. // GYRO STUFF ************************************************************* // accel.

44140625. } tempVolt = (tempVolt . val of the // convert from 'bits' // set the // save the abs. // to 'deg/s' sensorData[10] = sqrt(square(tempVolt)). val of the // convert from 'bits' // set the . // to 'deg/s' sensorData[8] = sqrt(square(tempVolt)).yGyroStill)*2. adcInit(channel). } else { sensorData[11] = 0.44140625. tempVolt = adcRead(). if (tempVolt < zGyroStill) negative flag { sensorData[11] = 1. } tempVolt = (tempVolt . if (tempVolt < yGyroStill) negative flag { sensorData[9] = 1.adcInit(channel). sensorData[8] = tempVolt. voltage //SONAR STUFF************************************************************ //----------------------------------------------------------------------// save the abs.gyro voltage channel = 2. sensorData[10] = tempVolt. tempVolt = adcRead(). } else { sensorData[9] = 0. voltage // get z .zGyroStill)*2.

//sensorData[12] = tempVolt.X ------. tempVolt = adcRead().48828125. sensorData[15] = tempVolt.X ------. //sensorData[12] = tempVolt. tempVolt = (tempVolt-202)*. which is // used for the SonarMax chip. //sensorData[15] = tempVolt. tempVolt = tempVolt*. .675354426. The 5 is standard for ADC read max.675354426. //PRESSURE STUFF********************************************************* //----------------------------------------------------------------------// This is the data from the MPXV5004G pressure Barometric sensor PORTA &= 01111111.00723 is obtained by dividing the supply voltage by 512.= (adc bits)*. // This data is used to find the elevation of the plane. //PRESSURE STUFF********************************************************* //----------------------------------------------------------------------// This is the data from the MPXV5004G pressure Pitotstatic sensor PORTA |= 10000000. The data is found in inches.X --------. // The equation is: // adc bits 5 volts 1000 mV 1 inches // --------. channel = 3. tempVolt = adcRead(). tempVolt = (tempVolt-202)*.48828125.// This is where we read data from the sonar. //----------------------------------------------------------------------channel = 6. //tempVolt = tempVolt*. adcInit(channel). channel = 3.675354426 // 1024 bits 1 Volts 7.675354426. tempVolt = adcRead(). The // . adcInit(channel). adcInit(channel). sensorData[16] = tempVolt. //tempVolt = tempVolt*.23 mV // 1024 is from the 10-bit res. sensorData[17] = tempVolt.

1). } //sendShortAsString(temp. } . // X-Accelerometer //readSensors().1). sendChar(' '). } //sendShortAsString(temp. // Z-Accelerometer temp = sensorData[4]. sendChar(' '). unsigned short temp = sensorData[0].1). sendChar(' '). // if value is negative then // print a negative sign // Y-Accelerometer temp = sensorData[2]. value if (sensorData[1]) { sendChar(45). // grab accel. } else { sendChar(43). // grab accel. // grab accel. value if (sensorData[3]) // if value is negative then { // print a negative sign sendChar(45). value if (sensorData[5]) // check upside down flag { sendChar(45). } //sendShortAsString(temp.} //----------------------------------------------------------------------// print Accelerometer ************************************************* //----------------------------------------------------------------------void printAccel(void) { sendStr("Accelerometer! ").

"). } //sendShortAsString(temp.. sendChar(' ').. // X-Gyro temp = sensorData[6]. sendChar(' '). PORTA |= 10000000. } // if value is negative then // print a negative sign // grab gyro value // if value is negative then // print a negative sign // grab gyro value // if value is negative then // print a negative sign . // Z-Gyro temp = sensorData[10]. } //sendShortAsString(temp. } //sendShortAsString(temp. if (sensorData[9]) { sendChar(45).1).//----------------------------------------------------------------------// print Gyro ********************************************************** //----------------------------------------------------------------------void printGyro(void) { //readSensors(). if (sensorData[11]) { sendChar(45). sendStr("Gyro Readings. unsigned short temp. // Y-Gyro temp = sensorData[8]. sendChar(' ').1).1). // grab gyro value if (sensorData[7]) { sendChar(45).

} // Button 3 if (B3==0){ mode = 3. } // Button 2 if (B2==0){ mode = 2. while(!mode){ // Button 1 if (B1==0){ mode = 1.//----------------------------------------------------------------------// Button Scan ********************************************************* //----------------------------------------------------------------------void buttonScan(){ mode = 0. } // Button 4 if (B4==0){ mode = 4. the trigger . If high. the interrupt must have //triggered by a rising edge and if not. } // Button 5 if (B5==0){ mode = 5. //Check for rising or falling edge by checking the //level on ICP. } ISR(TIMER1_CAPT_vect) // Timer 1 overflow ISR { PORTA = 0xFF. } } } /* ISR(TIMER1_OVF_vect) { ovCounter++.

i++. //enable global interrupt bit } //----------------------------------------------------------------------// send Telemetry ************************************************* //----------------------------------------------------------------------void storeTelemetry(char SD_data[]. . int i = *a. int *b = &i.5. //set for rising edge trigger next pulseClocks = (unsigned long)fallingEdge . enable input //capture on rising edge and noise canceller TIMSK=0x24. //unmask timer 1 overflow and capture interrupts sei(). //Telemetry Header -----------------------------------------------SD_data[i] = '$'. //calculation pulseLength = pulseClocks*0.//must have been a falling edge. // set to trigger on falling edge next ovCounter = 0. if(PIND & 0b00010000) { risingEdge = ICR1.(unsigned long)risingEdge + (unsigned long)ovCounter*0x10000. //save falling edge time TCCR1B = TCCR1B | 0x40. } }*/ //----------------------------------------------------------------------// Pulsin ************************************************************** //----------------------------------------------------------------------float pulsin() { TCCR1B=0xC2. // save start time for pulse TCCR1B = TCCR1B & 0xBF. //timer 1 input to clock/8. //clear overlfow counter for this measurement } else { fallingEdge = *a) { readSensors().

i++. //Accelerometer DATA----------------------------------------------// X-Accelerometer unsigned short temp = sensorData[0]. } else { // grab accel. if (sensorData[5]) { SD_data[i] = '-'.1).'. SD_data[i] = '. i++. i++.temp. i++. if (sensorData[3]) then { a negative sign SD_data[i] = '-'. i++.'. i++. SD_data[i] = 'D'. } convLongString(SD_data. i++. // Y-Accelerometer temp = sensorData[2]. value // if value is negative // grab accel. } convLongString(SD_data. // Z-Accelerometer temp = sensorData[4]. SD_data[i] = '.temp. value // check upside down flag // print // print // grab accel. value // if value is negative . i++.SD_data[i] = 'S'.b.1). SD_data[i] = '.'.b. if (sensorData[1]) then { a negative sign SD_data[i] = '-'.

temp. i++. // grab gyro value if (sensorData[9]) // if value is negative then { // print a negative sign SD_data[i] = '-'.'.1). i++. // Y-Gyro temp = sensorData[8].temp. SD_data[i] = '.'. } convLongString(SD_data. SD_data[i] = '. // Z-Gyro temp = sensorData[10].'. SD_data[i] = '.b.1). // grab gyro value if (sensorData[7]) // if value is negative then { // print a negative sign SD_data[i] = '-'. i++. i++.SD_data[i] = '+'. } convLongString(SD_data. i++.1).temp.b.'.1).b.b. i++. // grab gyro value if (sensorData[11]) // if value is negative then { // print a negative sign SD_data[i] = '-'. SD_data[i] = '. i++. } convLongString(SD_data. } convLongString(SD_data.temp. //Pulse length ------------------------------------------------------------ . i++. //GYRO DATA-------------------------------------------------// X-Gyro temp = sensorData[6].

0x0D}. 0x00.'. 0x00.*/ //output milliseconds to Port C //Sonar Distance ---------------------------------------------------------temp = sensorData[15]. 0x01. SD_data[i] = '. *a = i.1).temp. 0x00. 0x62. 0x12.'. SD_data[i] = '. 0x00. j. 0x0A}. //Status Poll const unsigned short posllhPoll[]= {0xB5. 0x03. 0x00. 0x62.1).'./*SREG = sreg. 0x02. i++. //Pressure Data ----------------------------------------------------------temp = sensorData[16]. convLongString(SD_data.0). //Include newline and carriage return at end of each data string----------SD_data[i] = 10. convLongString(SD_data. 0x00. //Position Poll const unsigned short velnedPoll[]= {0xB5. 0x13. cli(). int iChar . SD_data[i] = '. } //----------------------------------------------------------------------// get GPS data ******************************************************** //----------------------------------------------------------------------void getGps(void) { const unsigned short statusPoll[]= {0xB5. navStat[24].gdata.b. 0x3A}.navVelo[44]. //Velocity Poll unsigned short i. convLongString(SD_data. 0x04.b. sreg = SREG. 0x03. i++. i++.temp.pulseLength. navPos[36]. 0x62. 0x01. .b. convLongString(SD_data. i++. gdata = 0.1).temp. //Pressure Data ----------------------------------------------------------temp = sensorData[17]. k.b. 0x01.

// Send poll command to the GPS via USART } sendCharUart1(LINE_FEED). _delay_ms(240).i = 0. sendCharUart1(CARRIAGE_RETURN). while(gdata==0) { /*********************************************************************** ******* // Poll and read Nav Pos. } gdata = checkPos(navPos). /* while (ch) { navPos[i] = ch. ************************************************************************ *******/ for(i = 0. i++. i<8. _delay_ms(240)... i=0. */ _delay_ms(240). _delay_ms(240). } sendStr("Done"). ch = receiveCharUart1(). } /***************************************************************************** * // Check Sum Routine for NavPos Poll ****************************************************************************** */ int checkPos(unsigned short* navPos) .i++) { sendDigitUart1(posllhPoll[i]).

ch_b = ch_b + ch_a. } else if((x==0)&(y==1)){ SD_data[*b] = y){ if(x!=0){ unsigned short digit=x%10. (*b)++. if((navPos[34] == ch_a)&&(navPos[35] == ch_b)) { return 1. ch_b &= 0xFF.0). } else { return 0. } ch_a &= 0xFF. SD_data[*b] = digit+48.unsigned short x. int ch_b = 0. convLongString(SD_data. } } . (*b)++. int j = 0. } } /***************************************************************************** * * Convert long to string ****************************************************************************** */ void convLongString(char SD_data[].j< *b. for (j=2.x/10.{ int ch_a = 0.j++) { ch_a = ch_a + navPos[j].b.

h> #include <avr/iom128. void SPIinit(void) { DDRB &= ~(1 << SPIDI). PORTB &= ~(1 << SPICS). SPI((uint8_t)(address >> 8)). received = SPDR. // set port B SPI chip select to output SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0)./*************************************************************************** * sd_card. } char Command(char befF. while(!(SPSR & (1<<SPIF))).h" #define SPIDI 3 #define SPIDO #define SPICLK #define SPICS 0 char sector[512].h> #include <avr/interrupt. SPI((uint8_t)(address >> 16)). SPDR = d. uint32_t address. // Port B bit 3 (pin4): data in (data from SD card) 2 // Port B bit 2 (pin3): data out (data to SD card) 1 // Port B bit 1 (pin2): clock // Port B bit 0 (pin1): chip select for SD card . // set port B SPI data out to output DDRB |= (1 << SPICS). // set port B SPI data input to input DDRB |= (1 << SPICLK). return (received). // set chip select to low (SD card is selected) } char SPI(char d) { // send character over SPI char received = 0.h> #include <inttypes.c * * Source code for SD commands * ****************************************************************************/ #include <avr/io. char befH ) { // sends a command to the SD card SPI(0xFF). SPI((uint8_t)(address >> 24)). SPI(befF). // set port B SPI clock to output DDRB |= (1 << SPIDO).h> #include <avr/signal.h> #include "sd_card.

SPI(0xFF).0. } if (r1 != (char)0x00) { //read error return 1. PORTB |= (1 << SPICS).start of any transmission // ATT: typecast (char)0xFE is a must! while(SPI(0xFF) != (char)0xFE). // disable SD card // start SD card in SPI mode for(i=0. i < 10.j.0. r1 = SPI(0xFF). } int readSD(uint32_t j. send 2 dummy bytes .0. // send 10*8=80 clock pulses PORTB &= ~(1 << SPICS). char r1 = Command(0x51. loops here if (Command(0x41. } // at the end. i++) SPI(0xFF). SPI(befH). for (ix = 0. // reset SD card st: // if there is no SD card. char SD_data[]) { // read and send 512 bytes from the SD card via the serial port int i.0xFF). // return the last received character } int SD_init(void) { // init SPI char i. // 512 byte-read-mode uint16_t ix. ix < 50000.0xFF) != 0) goto st.0x95) != 1) goto sderror.i++){ SD_data[i] = SPI(0xFF). sderror: return 0. return SPI(0xFF).0x95). prg.SPI((uint8_t)address). ix++) { if (r1 == (char)0x00) break. for(i=0. } // wait for 0xFE . // enable SD card Command(0x40.i<512. return 1. if (Command(0x40.

stoperror: //uart_puts("Stop transmission error"). char c = Command(0x52.i<k. } // wait for 0xFE .i<50000. //c = SPI(0xFF). return 1.SPI(0xFF).n* k) { // read and send multiple data blocks from the SD card via the serial port int i. } if(c != (char)0) goto stoperror. for(j=0. SPI(0xFF).j<512.i<50000. } SPI(0xFF).j++){ while(!(UCSR1A & (1 << UDRE1))). c = SPI(0xFF). c = SPI(0xFF).i++){ while(SPI(0xFF) != (char)0xFE).j. } int readmultSD(uint32_t n. // actually this returns the CRC/checksum byte SPI(0xFF).0xFF). return 0. } c = Command(0x4C.0xFF). while(SPI(0xFF) == (char)0).0. //serialterminate().start of any transmission // ATT: typecast (char)0xFE is a must! for(i=0. } if(c != (char)0x00){ //uart_puts("SD card: read error 1 ").i++){ if(c == (char)0) break. // wait for serial port UDR1 = SPI(0xFF). for(i=0. //convLongString((uint8_t)c. .i++){ if(c == (char)0x00) break. for (i=0. return 1.1).

} // at the end. SPI(0xFF). ix++) { if (r1 == (char)0x00) break.0000. SPI(0xFE). char a = Command(0x60.return 0.0101 return 1. c &= 0x1F.1111. ix < 50000.0001.0. for (ix = 0.0xFF). char r1 = Command(0x58.j*512. uint8_t c.i<512. SPI(0xFF). r1 = SPI(0xFF). } // wait until SD card is not busy anymore while(SPI(0xFF) != (char)0xFF). // write ram sectors to SD card for (i=0. } if (r1 != (char)0x00) { return 1. . if (c != 0x05) { // 0x05 = 0b.char SD_data[]) { // write RAM sector to SD card int i. // 512 byte-write-mode uint16_t ix. } SPI(0xFF). send 2 dummy bytes SPI(0xFF). // 0x1F = 0b. } int writeSD(uint32_t j. c = SPI(0xFF). //if(a != 0x00) uart_puts("Erase start address error").i++) { SPI(SD_data[i]). return 0. } void eraseSD(void){ int i.0xFF).

0xFF).500000000.a = Command(0x61.i++){ if(a != 0x00) break. } . a = SPI(0xFF).i<50000. a = Command(0x66.0xFF). } //if(a == 0x00) uart_puts("Erase failed!"). //if(a != 0x00) uart_puts("Erase end address error"). for(i=0.0.

DDRC=0xF7.h> #include "system. // Port B initialization PORTB=0x00. DDRA=0x40. // Port F initialization PORTF=0x00. // Port C initialization PORTC=0x00. DDRB=0x00./**************************************************************** * system. . // Port D initialization PORTD=0x00.h> #define #define #define #define BUZZ1_HIGH BUZZ1_LOW BUZZ2_HIGH BUZZ2_LOW PORTE |= BIT4 PORTE &= ~BIT4 PORTE |= BIT5 PORTE &= ~BIT5 //--------Init Ports -------------------void initPorts(void) { // Input/Output Ports initialization // Port A initialization PORTA=0x00.c *****************************************************************/ #include <stdio.h> //#include <avr/iom128. DDRE=0x30.h" #include "bits. // Port E initialization PORTE=0x00. DDRD=0x08.h> #include <avr/io.h" #include <util/delay. DDRF=0x00.

OCR1CH=0x00. } //--------Init Timers -------------------void initTimers(void) { // Timer/Counter 0 initialization // Clock source: System Clock // Clock value: Timer 0 Stopped // Mode: Normal top=FFh // OC0 output: Disconnected ///ASSR=0x00. TCNT1H=0x00. // OC1C output: Discon. OCR1BL=0x00. OCR1AH=0x00. TCCR1B=0b01000001. . // Timer/Counter 1 initialization // Clock source: T1 pin Rising Edge // Mode: Normal top=FFFFh // OC1A output: Discon. OCR1BH=0x00. ///TCNT0=0x00. OCR1CL=0x00. // OC1B output: Discon. TCNT1L=0x00. OCR1AL=0x00. OCR2=0x00. // Noise Canceler: Off // Input Capture on Rising Edge TCCR1A=0x00. // Timer/Counter 2 initialization // Clock source: T2 pin Rising Edge // Mode: Normal top=FFh // OC2 output: Disconnected TCCR2=0x07. TCNT2=0x00.// Port G initialization PORTG=0x00. ///TCCR0=0x00. ///OCR0=0x00. DDRG=0x00.

//TCNT3L=0x00. No Parity // USART1 Receiver: On // USART1 Transmitter: On // USART1 Mode: Asynchronous . // Timer(s)/Counter(s) Interrupt(s) initialization ///TIMSK=0x00. //OCR3BH=0x00.000 kHz // Mode: Normal top=FFFFh // OC3A output: Discon. //TCCR3B=0x02. ///EICRB=0x00. // OC3B output: Toggle // OC3C output: Toggle //TCCR3A=0x14. } //--------Init Uart interface -------------------void initUart1(void) { // USART1 initialization // Communication Parameters: 8 Data. ///EIMSK=0x00. // External Interrupt(s) initialization // INT0: Off // INT1: Off // INT2: Off // INT3: Off // INT4: Off // INT5: Off // INT6: Off // INT7: Off ///EICRA=0x00. //OCR3CL=0xFA. //TCNT3H=0x00. //OCR3BL=0xFA.// Timer/Counter 3 initialization // Clock source: System Clock // Clock value: 2000. //OCR3CH=0x00. ///ETIMSK=0x00. //OCR3AL=0x00. //OCR3AH=0x00. 1 Stop.

UBRR1L=0x67. } unsigned char receiveCharUart1(void) { // wait for data to be received while(!(UCSR1A & (1<<RXC1))). // get and return received data from buffer return UDR1. //(00011000) UCSR1C=0x06. // send data UDR1 = ch. //06 @ 1Mhz } void sendCharUart1(unsigned char ch) { // wait for data to be received while(!(UCSR1A & (1<<UDRE1))). //(00000110) UBRR1H=0x00. else // return 0 . //103 (0000 0110 0111) //UBRR1L=0x06.// USART1 Baud rate: 9600 UCSR1A=0x00. // send data UDR1 = num. UCSR1B=0x18. } void sendDigitUart1(unsigned short num) { // wait for data to be received while(!(UCSR1A & (1<<UDRE1))). } unsigned char receiveCharUart1_nonstop(void) { // wait for data to be received if((UCSR1A & (1<<RXC1))) // get and return received data from buffer return UDR1.

0). _delay_us(125). } } //--------Send an unsigned short as a string to LCD------void sendLongAsStringUart1(unsigned long x. // loop to the end of the string while(str[i]!='\0') { sendCharUart1(str[i]). i++. } else if((x==0)&(y==1)) { sendCharUart1(48). } .return 0. } } //---------buzzer --------------------------------void buzzer(void) { BUZZ1_LOW. sendLongAsStringUart1(x/10. y) { if(x!=0) { int digit=x%10. _delay_us(125). BUZZ1_HIGH. } //--------Send string to Uart----------------------------void sendStrUart1(unsigned char* str) { unsigned int i=0. BUZZ2_HIGH. sendCharUart1(digit+48).

//(00000110) UBRR0H=0x00.//--------Init Uart interface -------------------void initUart0(void) { // USART0 initialization // Communication Parameters: 8 Data. 1 Stop. //103 (0000 0110 0111) } void sendCharUart0(unsigned char ch) { // wait for data to be received while(!(UCSR0A & (1<<UDRE0))). // send data UDR0 = ch. } void sendDigitUart0(unsigned short num) { // wait for data to be received while(!(UCSR0A & (1<<UDRE0))). // send data UDR0 = num. No Parity // USART0 Receiver: On // USART0 Transmitter: On // USART0 Mode: Asynchronous // USART0 Baud rate: 9600 UCSR0A=0x00. } unsigned char receiveCharUart0(void) { // wait for data to be received while(!(UCSR0A & (1<<RXC0))). UCSR0B=0x18. //(00011000) UCSR0C=0x06. } unsigned char receiveCharUart0_nonstop(void) { . UBRR0L=0x67. // get and return received data from buffer return UDR0.

sendCharUart0(digit+48).// wait for data to be received if((UCSR0A & (1<<RXC0))) // get and return received data from buffer return UDR0. } else if((x==0)&(y==1)) { sendCharUart0(48). i++.0). } //--------Send string to Uart----------------------------void sendStrUart0(unsigned char* str) { unsigned int i=0. else // return 0 return 0. } } .int y) { if(x!=0) { int digit=x%10. } } //--------Send an unsigned short as a string to LCD------void sendLongAsStringUart0(unsigned long x. sendLongAsStringUart0(x/10. // loop to the end of the string while(str[i]!='\0') { sendCharUart0(str[i]).

void SPI_Reset(void). PORTB = 0x00.h" #include "bits.h" #include "lcd. SEND_CMD(DISP_ON). another byte must still be correctly obtained. void toUart1(unsigned char).h" #include "delay. //this is the data that is to be transmitted and recived unsigned char cdata = 0b00010001. //LCD initialisation LCD_Ini(). void sendStringUart1(unsigned char. DDRD = 0b11111111. // write to lcd SEND_STR("Magnetometer"). */ //This first section shows the used header files #include <AVR/io. // delay_ms(10). // delay_ms(2). //Init Uart1 InitUart1(). This code is NOT COMPLETELY READY. IT IS ONLY CODE TO HELP SOMEONE GET STARTED WITH THE MAGNETOMETER.h> #include "system. //tells the micromag to send x-axis heading with sclk/64 unsigned char rdata. The code is almost complete in the sense that is retreives one byte from the magnetometer./* This is the code used for the Magnetometer.h" //Here is a list of used functions in the program void SPI_MasterInit(void). DDRB = 0b11010111. //second byte receieved by micromag int main() { //Ports InitPorts(). //first byte recieved by micromag unsigned char rdata2. void tolcd(unsigned char). while (1) { //set i/o ports for portb //set i/o ports for portd //set all ports low for portd //set all ports low for portb . PORTD = 0x00. void SPI_MasterTransmit(unsigned char). SEND_CMD(CLR_DISP).

} toUart1(rdata). //clear SPI flag SEND_CMD(DD_RAM_ADDR2). SEND_CHAR(32).//write to serial port if (rdata2 != 0) { tolcd(rdata2). SEND_CHAR(32). write 0 to LCD { tolcd(rdata). SEND_CHAR(32). //begin transmission to micromag SPSR &= 0b01111111. } rdata2 = SPDR. //enable SPI while ((PINB & 0b01000000)). //retrieve second 8 bits SPCR |= 0b01000000. SEND_CHAR(32). //retrieve first 8 bits while (i > 0) //wait for next 8 bits to be sent { i--. //write second byte to serial port } return 0. //used to initialize SPI SPI_Reset(). //go to second line of LCD screen if (rdata != 0) //special case of zero. } /*The MasterInit and MasterTransmit code was code obtained from the manual for the atmega128 microprocessor*/ void SPI_MasterInit(void) { . SEND_CHAR(32). //wait for drdy from micromag while (i > 8) //wait for 8 bits to be sent { i--. //will be used for attempting to SPI_MasterInit().int i = 16. } rdata = SPDR.//re-enable SPI SPSR &= 0b01111111. //clear the SPI flag SPCR |= 0b01000000. } toUart1(rdata2). //reset pulse necessary for micromag SPI_MasterTransmit(cdata).

/* Set MOSI and SCK output.1). if (1) break. Master. } . /* Enable SPI. //for the magnetometer to read PORTD &= 0b10111111. //to compute data } void tolcd(unsigned char x) { while(x >= 1) { unsigned char a = x%10. } void SPI_Reset(void) { PORTD &= 0b10111111. set clock rate fck/16 */ SPCR = 0x52. } } void toUart1(unsigned char mag) { sendStrUart1(mag. /* Wait for transmission complete */ while(!(SPSR & (1<<SPIF))). all others input */ PORTB = 0x06. SEND_CHAR(a+48). tolcd(x/10). } void SPI_MasterTransmit(unsigned char cData) { /* Start transmission */ SPDR = cData. //The reset pin must be pulsed PORTD |= 0b01000000.

login as root by typing “su” and entering a password 2. Create root by typing “sudo passwd” 4.deb” file to automatically install CIFER. follow these instructions to avoid an error message about missing a file named “libtermcap.Appendix B. Before changing defaults (option 11) in move to the desktop 8.so2 To run CIFER 1. create a password for the root user 5.rpm to . type “source /usr/local/cifer/cifer_linux/cifrc” 6. . type “xterm” 3. type: sudo apt-get update sudo apt-get install alien sudo alien –k name-of-rpm-file.2”. Download Cifer from aiaa. Click OK and follow the instructions to install. This file is an obsolete library and needs to be installed separately. run the “. login as root 4.deb (ubuntu installer file) 7. type “cifer” This launches cifer. CIFER installation on the Ubuntu 7. Install CIFER 1. Click on Applications Add/Remove… 2. Type “Tex” in the search field 4. goto the file menu “Applications Accessories Terminal” 2. install “alien package converter app” to convert .pdf” Under “Application” scroll to “TexMaker” and check the box next to it. extract files and follow installation directions in “Getting_Started.5 /lib/libtermcap. under “Show:” pull down menu select “All Available Applications” 3. Type: Sudo apt-get install gcc Sudo apt-get install libgcc1 Sudo apt-get install g++ Sudo apt-get install cpp Sudo apt-get install ncurses-base Sudo apt-get install ncurses-bin Sudo apt-get install-term Sudo apt-get install libncurses5 Sudo apt-get install libncurses5-dev Sudo ln –s /liblibncurses.04 distribution of Linux Install TexMaker 1. change to the c-shell by typing “csh” 5. Follow the onscreen directions to navigate the program.

NY: Thomas Delmar Learning. 2003. “Coordinated Flocking of UAVs for Improved Connectivity of Mobile Ground Nodes. UTSA Library. 2007 <http://ubuntuforums. 2007 <http://ieeexplore. Reston.engineeringvillage2. MILCOM 2004. 9 Aug.-3 Nov.” American Control Conference.” Proceedings of the SPIE – The International Society for Optical Engineering 1999: 35-43. San Antonio.. 2007 <http://ieeexplore. Clifton Park.References [1] Murphy.asp?Prodid=2?>... 2004:>. Embedded C Programming and the Atmel AVR. Douglas. Engineering Village. UTSA Library. [10] 31 June-July 2007 <www. [4] Sujit. 19 Oct. [2] Basu. and James Cycon. 2004.avrfreaks. “Applications for Mini VTOL UAV for Law Enforcement. Aircraft and Rotorcraft System Identification Engineering Methods With Flight Test Examples. 2007: 3995-3960. 9 Aug. TX. IEEE Xplore.>.” IEEE Instrumentation & Measurement Magazine Sep. San Antonio.Needs Termcap?" Ubuntu Forums.” Aerovironment. . 2006.2>. UTSA Library. [3] “UAS: Dragon Eye. and Randy Beard. [5]>. TX. San Antonio. “Recce and UAV: Mass Memory an Enabling Technology for Merger.” Nonvolatile Memory Technology Conference 24-26 Jun. W. [12] Barnett. 7 Aug. 2004: 1628 – 1634. 9 Aug. 9-13 Jul. P. “A Military Perspective on Small Unmanned Aerial Vehicles. TX.” Embedded Systems Programming May 2001: 103-104. [6] Hall. 1996: 132-137. 31 Oct. [8] Tischler. 3. and O'cull . VA: American Institute of Aeronautics and Astronautics.>. 2007 < Remple. Inspec.0.J. UTSA Library. IEEE Xplore. 9 Aug. San Antonio. IEEE>.” Military Communications Conference. [7] Barr. 2007. Mark B. TX.>. TX. [9] 31 June-July 2007 < David H. and Robert K.olimex. [11] 31 June-July 2007 <http://www. and Vladimir Shurbanov. 2007 < IEEE Xplore. “Memory Types. 2007 <http://ieeexplore.aerovironment.27 Won'T Compile -. UTSA Library. 7 Aug. San Jr. IEEE Xplore. Prithwish. 2007 < 10 Aug. [13] Nimefurahi. “Distributed Sequential Auctions for Multiple UAV Task Allocation. "Re: MySQL 4. Jason>.com>. Cox .B.

com/>. 14 July 2007 <http://www." 9 2007. [16] "AVR Freaks. 2002." AVR < http://www. [15] Leverton. “Allegro.php>. [20] "Pitot-Static (Prandtl) Tube. 24 July 2007 <http://www.” Jan." MaxBotix.smileymicros. George and Robert>. 1999. Aug. [18] " Matthew. 24 July 2007 <http://www. Aug. [19] "SparkFun Electronics. 2007 <http://www.[14] Foot. <http://www. 10>. “Allegro.grc.avrfreaks. Ohannessian.allegro. 14 July 2007 <http://www. [17] Maxbotix Home.“ Olimex. .cc/resource/Libraries/Graphics/AllegroGL>. 9">.html>.maxbotix.” Mar.

Sign up to vote on this title
UsefulNot useful