First version. This is the "core" of the project and libraries.

git-svn-id: https://arducopter.googlecode.com/svn/trunk@2 f9c3cf11-9bcb-44bc-f272-b75c42450872
This commit is contained in:
jjulio1234 2010-05-28 14:38:51 +00:00
parent 8af9e5e867
commit 3976200f50
33 changed files with 3369 additions and 0 deletions

679
ArduCopter.pde Normal file
View File

@ -0,0 +1,679 @@
/* ********************************************************************** */
/* ArduCopter Quadcopter code */
/* */
/* Code based on ArduIMU DCM code from Diydrones.com */
/* (Original ArduIMU code from Jordi Muñoz and William Premerlani) */
/* Ardupilot core code : from DIYDrones.com development team */
/* Quadcopter code from AeroQuad project and ArduIMU quadcopter project */
/* Authors : Jose Julio, Ted Carancho, Jordi Muñoz */
/* Date : 27-05-2010 */
/* Version : 1.0 beta */
/* Hardware : ArduPilot Mega + Sensor Shield (codename:FOXTRAP2) */
/* This code use this libraries : */
/* APM_RC_QUAD : Radio library (adapted for quads) */
/* APM_ADC : External ADC library */
/* DataFlash : DataFlash log library */
/* APM_BMP085 : BMP085 barometer library */
/* APM_Compass : HMC5843 compass library [optional] */
/* GPS_UBLOX or GPS_NMEA: GPS library [optional] */
/* ********************************************************************** */
#include <Wire.h>
#include <APM_ADC.h>
#include <APM_RC_QUAD.h>
#include <DataFlash.h>
#include <APM_Compass.h>
// Put your GPS library here:
#include <GPS_NMEA.h> // MTK GPS
/* APM Hardware definitions */
#define LED_Yellow 36
#define LED_Red 35
#define LED_Green 37
#define RELE_pin 47
#define SW1_pin 41
#define SW2_pin 42
/* *** */
/* ***************************************************************************** */
/* CONFIGURATION PART */
/* ***************************************************************************** */
//Adjust this parameter for your lattitude
#define GEOG_CORRECTION_FACTOR 0.87 // cos(lattitude)
#define RADIO_TEST_MODE 0 // 0:Normal 1:Radio Test mode (to test radio channels)
#define MAGNETOMETER 1 // 0 : No magnetometer, 1: Magnetometer
// QuadCopter Attitude control PID GAINS
#define KP_QUAD_ROLL 1.7 // 1.5 //1.75
#define KD_QUAD_ROLL 0.45 //0.35 // 0.4 //Absolute max:0.85
#define KI_QUAD_ROLL 0.4 // 0.4 //0.45
#define KP_QUAD_PITCH 1.7
#define KD_QUAD_PITCH 0.45
#define KI_QUAD_PITCH 0.4
#define KP_QUAD_YAW 3.5 // 3.8
#define KD_QUAD_YAW 1.2 // 1.3
#define KI_QUAD_YAW 0.15 // 0.15
#define KD_QUAD_COMMAND_PART 12.0 // for special KD implementation (in two parts). Higher values makes the quadcopter more responsive to user inputs
// Position control PID GAINS
#define KP_GPS_ROLL 0.03
#define KD_GPS_ROLL 0.0
#define KI_GPS_ROLL 0.01
#define KP_GPS_PITCH 0.03
#define KD_GPS_PITCH 0.0
#define KI_GPS_PITCH 0.01
#define GPS_MAX_ANGLE 16 // Maximun command roll and pitch angle from position control
// Altitude control PID GAINS
#define KP_ALTITUDE 0.8
#define KD_ALTITUDE 0.3
#define KI_ALTITUDE 0.2
// The IMU should be correctly adjusted : Gyro Gains and also initial IMU offsets:
// We have to take this values with the IMU flat (0º roll, 0º pitch)
#define acc_offset_x 2039
#define acc_offset_y 2035
#define acc_offset_z 2014 // We need to rotate the IMU exactly 90º to take this value
#define gyro_offset_roll 1650
#define gyro_offset_pitch 1690
#define gyro_offset_yaw 1690
// ADC : Voltage reference 3.3v / 12bits(4096 steps) => 0.8mV/ADC step
// ADXL335 Sensitivity(from datasheet) => 330mV/g, 0.8mV/ADC step => 330/0.8 = 412
// Tested value : 414
#define GRAVITY 414 //this equivalent to 1G in the raw data coming from the accelerometer
#define Accel_Scale(x) x*(GRAVITY/9.81)//Scaling the raw data of the accel to actual acceleration in meters for seconds square
#define ToRad(x) (x*0.01745329252) // *pi/180
#define ToDeg(x) (x*57.2957795131) // *180/pi
// IDG500 Sensitivity (from datasheet) => 2.0mV/º/s, 0.8mV/ADC step => 0.8/3.33 = 0.4
// Tested values :
#define Gyro_Gain_X 0.4 //X axis Gyro gain
#define Gyro_Gain_Y 0.41 //Y axis Gyro gain
#define Gyro_Gain_Z 0.41 //Z axis Gyro gain
#define Gyro_Scaled_X(x) x*ToRad(Gyro_Gain_X) //Return the scaled ADC raw data of the gyro in radians for second
#define Gyro_Scaled_Y(x) x*ToRad(Gyro_Gain_Y) //Return the scaled ADC raw data of the gyro in radians for second
#define Gyro_Scaled_Z(x) x*ToRad(Gyro_Gain_Z) //Return the scaled ADC raw data of the gyro in radians for second
//#define Kp_ROLLPITCH 0.0125 //0.010 // Pitch&Roll Proportional Gain
#define Kp_ROLLPITCH 0.002 //0.003125
//#define Ki_ROLLPITCH 0.000010 // Pitch&Roll Integrator Gain
#define Ki_ROLLPITCH 0.0000025
#define Kp_YAW 1.5 // Yaw Porportional Gain
#define Ki_YAW 0.00005 // Yaw Integrator Gain
/*For debugging purposes*/
#define OUTPUTMODE 1 //If value = 1 will print the corrected data, 0 will print uncorrected data of the gyros (with drift), 2 Accel only data
//Sensor: GYROX, GYROY, GYROZ, ACCELX, ACCELY, ACCELZ
uint8_t sensors[6] = {1,2,0,4,5,6}; // For ArduPilot Mega Sensor Shield Hardware
//Sensor: GYROX, GYROY, GYROZ, ACCELX, ACCELY, ACCELZ
int SENSOR_SIGN[]={-1,1,-1,1,-1,1,-1,-1,-1}; //{1,-1,-1,1,-1,1,-1,-1,-1}
int AN[6]; //array that store the 6 ADC channels
int AN_OFFSET[6]; //Array that store the Offset of the gyros and accelerometers
float G_Dt=0.02; // Integration time for the gyros (DCM algorithm)
float Accel_Vector[3]= {0,0,0}; //Store the acceleration in a vector
float Accel_Vector_unfiltered[3]= {0,0,0}; //Store the acceleration in a vector
//float Accel_magnitude;
//float Accel_weight;
float Gyro_Vector[3]= {0,0,0};//Store the gyros rutn rate in a vector
float Omega_Vector[3]= {0,0,0}; //Corrected Gyro_Vector data
float Omega_P[3]= {0,0,0};//Omega Proportional correction
float Omega_I[3]= {0,0,0};//Omega Integrator
float Omega[3]= {0,0,0};
float errorRollPitch[3]= {0,0,0};
float errorYaw[3]= {0,0,0};
float errorCourse=0;
float COGX=0; //Course overground X axis
float COGY=1; //Course overground Y axis
float roll=0;
float pitch=0;
float yaw=0;
unsigned int counter=0;
float DCM_Matrix[3][3]= {
{1,0,0}
,{0,1,0}
,{0,0,1}
};
float Update_Matrix[3][3]={{0,1,2},{3,4,5},{6,7,8}}; //Gyros here
float Temporary_Matrix[3][3]={
{0,0,0}
,{0,0,0}
,{0,0,0}
};
// GPS variables
float speed_3d=0;
int GPS_ground_speed=0;
//Log variables
int log_roll;
int log_pitch;
int log_yaw;
long timer=0; //general porpuse timer
long timer_old;
// Sonar variables
static volatile unsigned long sonar_start_ms;
static volatile unsigned char sonar_start_t0;
static volatile unsigned long sonar_pulse_start_ms;
static volatile unsigned char sonar_pulse_start_t0;
static volatile unsigned long sonar_pulse_end_ms;
static volatile unsigned char sonar_pulse_end_t0;
static volatile byte sonar_status=0;
static volatile byte sonar_new_data=0;
int sonar_value=0;
// Attitude control variables
float command_rx_roll=0; // comandos recibidos rx
float command_rx_roll_old;
float command_rx_roll_diff;
float command_rx_pitch=0;
float command_rx_pitch_old;
float command_rx_pitch_diff;
float command_rx_yaw=0;
float command_rx_yaw_diff;
int control_roll; // resultados del control
int control_pitch;
int control_yaw;
float K_aux;
// Attitude control
float roll_I=0;
float roll_D;
float err_roll;
float pitch_I=0;
float pitch_D;
float err_pitch;
float yaw_I=0;
float yaw_D;
float err_yaw;
//Position control
long target_longitude;
long target_lattitude;
byte target_position;
float gps_err_roll;
float gps_err_roll_old;
float gps_roll_D;
float gps_roll_I=0;
float gps_err_pitch;
float gps_err_pitch_old;
float gps_pitch_D;
float gps_pitch_I=0;
float command_gps_roll;
float command_gps_pitch;
//Altitude control
int Initial_Throttle;
int target_sonar_altitude;
int err_altitude;
int err_altitude_old;
float command_altitude;
float altitude_I;
float altitude_D;
// AP_mode : 1=> Position hold 2=>Stabilization assist mode (normal mode)
byte AP_mode = 2;
long t0;
int num_iter;
float aux_debug;
// Radio definitions
int Neutro_yaw;
int ch_roll;
int ch_pitch;
int ch_throttle;
int ch_yaw;
#define CHANN_CENTER 1500
#define MIN_THROTTLE 1040 // Throttle pulse width at minimun...
/* ************************************************************ */
/* Altitude control... (based on sonar) */
void Altitude_control(int target_sonar_altitude)
{
err_altitude_old = err_altitude;
err_altitude = target_sonar_altitude - sonar_value;
altitude_D = (float)(err_altitude-err_altitude_old)/G_Dt;
altitude_I += (float)err_altitude*G_Dt;
altitude_I = constrain(altitude_I,-100,100);
command_altitude = Initial_Throttle + KP_ALTITUDE*err_altitude + KD_ALTITUDE*altitude_D + KI_ALTITUDE*altitude_I;
}
/* ************************************************************ */
/* Position control... */
void Position_control(long lat_dest, long lon_dest)
{
long Lon_diff;
long Lat_diff;
float gps_err_roll;
float gps_err_pitch;
Lon_diff = lon_dest - GPS.Longitude;
Lat_diff = lat_dest - GPS.Lattitude;
// ROLL
gps_err_roll_old = gps_err_roll;
//Optimization : cos(yaw) = DCM_Matrix[0][0] ; sin(yaw) = DCM_Matrix[1][0]
gps_err_roll = (float)Lon_diff*GEOG_CORRECTION_FACTOR*DCM_Matrix[0][0] - (float)Lat_diff*DCM_Matrix[1][0];
gps_roll_D = (gps_err_roll-gps_err_roll_old)/G_Dt;
gps_roll_I += gps_err_roll*G_Dt;
gps_roll_I = constrain(gps_roll_I,-500,500);
command_gps_roll = KP_GPS_ROLL*gps_err_roll + KD_GPS_ROLL*gps_roll_D + KI_GPS_ROLL*gps_roll_I;
command_gps_roll = constrain(command_gps_roll,-GPS_MAX_ANGLE,GPS_MAX_ANGLE); // Limit max command
// PITCH
gps_err_pitch_old = gps_err_pitch;
gps_err_pitch = -(float)Lat_diff*DCM_Matrix[0][0]- (float)Lon_diff*GEOG_CORRECTION_FACTOR*DCM_Matrix[1][0];
gps_pitch_D = (gps_err_pitch-gps_err_pitch_old)/G_Dt;
gps_pitch_I += gps_err_pitch*G_Dt;
gps_pitch_I = constrain(gps_pitch_I,-500,500);
command_gps_pitch = KP_GPS_PITCH*gps_err_pitch + KD_GPS_PITCH*gps_pitch_D + KI_GPS_PITCH*gps_pitch_I;
command_gps_pitch = constrain(command_gps_pitch,-GPS_MAX_ANGLE,GPS_MAX_ANGLE); // Limit max command
}
/* ************************************************************ */
// ROLL, PITCH and YAW PID controls...
// Input : desired Roll, Pitch and Yaw absolute angles. Output : Motor commands
void Attitude_control()
{
// ROLL CONTROL
if (AP_mode==2) // Normal Mode => Stabilization mode
err_roll = command_rx_roll - ToDeg(roll);
else
err_roll = (command_rx_roll + command_gps_roll) - ToDeg(roll); // Position control
err_roll = constrain(err_roll,-25,25); // to limit max roll command...
roll_I += err_roll*G_Dt;
roll_I = constrain(roll_I,-20,20);
// D term implementation => two parts: gyro part and command part
// To have a better (faster) response we can use the Gyro reading directly for the Derivative term...
// Omega[] is the raw gyro reading plus Omega_I, so it´s bias corrected
// We also add a part that takes into account the command from user (stick) to make the system more responsive to user inputs
roll_D = command_rx_roll_diff*KD_QUAD_COMMAND_PART - ToDeg(Omega[0]); // Take into account Angular velocity of the stick (command)
// PID control
control_roll = KP_QUAD_ROLL*err_roll + KD_QUAD_ROLL*roll_D + KI_QUAD_ROLL*roll_I;
// PITCH CONTROL
if (AP_mode==2) // Normal mode => Stabilization mode
err_pitch = command_rx_pitch - ToDeg(pitch);
else
err_pitch = (command_rx_pitch + command_gps_pitch) - ToDeg(pitch); // Position Control
err_pitch = constrain(err_pitch,-25,25); // to limit max pitch command...
pitch_I += err_pitch*G_Dt;
pitch_I = constrain(pitch_I,-20,20);
// D term
pitch_D = command_rx_pitch_diff*KD_QUAD_COMMAND_PART - ToDeg(Omega[1]);
// PID control
control_pitch = KP_QUAD_PITCH*err_pitch + KD_QUAD_PITCH*pitch_D + KI_QUAD_PITCH*pitch_I;
// YAW CONTROL
err_yaw = command_rx_yaw - ToDeg(yaw);
if (err_yaw > 180) // Normalize to -180,180
err_yaw -= 360;
else if(err_yaw < -180)
err_yaw += 360;
err_yaw = constrain(err_yaw,-25,25); // to limit max yaw command...
yaw_I += err_yaw*G_Dt;
yaw_I = constrain(yaw_I,-20,20);
yaw_D = command_rx_yaw_diff*KD_QUAD_COMMAND_PART - ToDeg(Omega[2]);
// PID control
control_yaw = KP_QUAD_YAW*err_yaw + KD_QUAD_YAW*yaw_D + KI_QUAD_YAW*yaw_I;
}
// Maximun slope filter for radio inputs... (limit max differences between readings)
int channel_filter(int ch, int ch_old)
{
int diff_ch_old;
if (ch_old==0) // ch_old not initialized
return(ch);
diff_ch_old = ch - ch_old; // Difference with old reading
if (diff_ch_old<0)
{
if (diff_ch_old<-40)
return(ch_old-40); // We limit the max difference between readings
}
else
{
if (diff_ch_old>40)
return(ch_old+40);
}
//return((ch+ch_old)>>1); // Small filtering
return(ch);
}
/* ****** SETUP ********************************************************************* */
void setup()
{
int i;
float aux_float[3];
pinMode(LED_Yellow,OUTPUT); //Yellow LED A (PC1)
pinMode(LED_Red,OUTPUT); //Red LED B (PC2)
pinMode(LED_Green,OUTPUT); //Green LED C (PC0)
pinMode(SW1_pin,INPUT); //Switch SW1 (pin PG0)
pinMode(RELE_pin,OUTPUT); // Rele output
digitalWrite(RELE_pin,LOW);
APM_RC_QUAD.Init(); // APM Radio initialization
APM_ADC.Init(); // APM ADC library initialization
DataFlash.Init(); // DataFlash log initialization
GPS.Init(); // GPS Initialization
// RC channels Initialization (Quad motors)
APM_RC_QUAD.OutputCh(0,MIN_THROTTLE+15); // Motors stoped
APM_RC_QUAD.OutputCh(1,MIN_THROTTLE+15);
APM_RC_QUAD.OutputCh(2,MIN_THROTTLE+15);
APM_RC_QUAD.OutputCh(3,MIN_THROTTLE+15);
#if (MAGNETOMTER==1)
APM_Compass.Init(); // I2C initialization
#endif
DataFlash.StartWrite(1); // Start a write session on page 1
Serial.begin(57600);
Serial.println();
Serial.println("ArduCopter Quadcopter v1.0");
// Check if we enable the DataFlash log Read Mode (switch)
// If we press switch 1 at startup we read the Dataflash eeprom
while (digitalRead(SW1_pin)==0)
{
Serial.println("Entering Log Read Mode...");
Log_Read(1,1000);
delay(30000);
}
delay(3000);
Read_adc_raw();
delay(20);
// Offset values for accels and gyros...
AN_OFFSET[3] = acc_offset_x;
AN_OFFSET[4] = acc_offset_y;
AN_OFFSET[5] = acc_offset_z;
aux_float[0] = gyro_offset_roll;
aux_float[1] = gyro_offset_pitch;
aux_float[2] = gyro_offset_yaw;
// Take the gyro offset values
for(i=0;i<250;i++)
{
Read_adc_raw();
for(int y=0; y<=2; y++) // Read initial ADC values for gyro offset.
{
aux_float[y]=aux_float[y]*0.8 + AN[y]*0.2;
//Serial.print(AN[y]);
//Serial.print(",");
}
//Serial.println();
Log_Write_Sensor(AN[0],AN[1],AN[2],AN[3],AN[4],AN[5],ch_throttle);
delay(14);
}
for(int y=0; y<=2; y++)
AN_OFFSET[y]=aux_float[y];
for(i=0;i<6;i++)
{
Serial.print("AN[]:");
Serial.println(AN_OFFSET[i]);
}
Neutro_yaw = APM_RC_QUAD.InputCh(3); // Take yaw neutral radio value
Serial.print("Yaw neutral value:");
Serial.println(Neutro_yaw);
#if (RADIO_TEST_MODE) // RADIO TEST MODE TO TEST RADIO CHANNELS
while(1)
{
if (APM_RC_QUAD.GetState()==1)
{
Serial.print("AIL:");
Serial.print(APM_RC_QUAD.InputCh(0));
Serial.print("ELE:");
Serial.print(APM_RC_QUAD.InputCh(1));
Serial.print("THR:");
Serial.print(APM_RC_QUAD.InputCh(2));
Serial.print("YAW:");
Serial.print(APM_RC_QUAD.InputCh(3));
Serial.print("AUX(mode):");
Serial.print(APM_RC_QUAD.InputCh(4));
Serial.print("AUX2:");
Serial.print(APM_RC_QUAD.InputCh(5));
Serial.println();
delay(200);
}
}
#endif
delay(1000);
DataFlash.StartWrite(1); // Start a write session on page 1
timer = millis();
Read_adc_raw(); // Initialize ADC readings...
delay(20);
digitalWrite(LED_Green,HIGH); // Ready to go...
}
/* ***** MAIN LOOP ***** */
void loop(){
int aux;
int i;
float aux_float;
if((millis()-timer)>=14) // Main loop 70Hz
{
counter++;
timer_old = timer;
timer=millis();
G_Dt = (timer-timer_old)/1000.0; // Real time of loop run
// IMU DCM Algorithm
Read_adc_raw();
#if (MAGNETOMETER==1)
if (counter > 8) // Read compass data at 10Hz... (7 loop runs)
{
counter=0;
APM_Compass.Read(); // Read magnetometer
}
#endif
Matrix_update();
Normalize();
Drift_correction();
Euler_angles();
// *****************
// Output data
log_roll = ToDeg(roll)*10;
log_pitch = ToDeg(pitch)*10;
log_yaw = ToDeg(yaw)*10;
Serial.print(log_roll);
Serial.print(",");
Serial.print(log_pitch);
Serial.print(",");
Serial.print(log_yaw);
// Write Sensor raw data to DataFlash log
//Log_Write_Sensor(AN[0],AN[1],AN[2],AN[3],AN[4],AN[5],ch_throttle);
// Write attitude to DataFlash log
Log_Write_Attitude(log_roll,log_pitch,log_yaw);
if (APM_RC_QUAD.GetState()==1) // New radio frame?
{
// Commands from radio Rx...
// Stick position defines the desired angle in roll, pitch and yaw
ch_roll = channel_filter(APM_RC_QUAD.InputCh(0),ch_roll);
ch_pitch = channel_filter(APM_RC_QUAD.InputCh(1),ch_pitch);
ch_throttle = channel_filter(APM_RC_QUAD.InputCh(2),ch_throttle);
ch_yaw = channel_filter(APM_RC_QUAD.InputCh(3),ch_yaw);
command_rx_roll_old = command_rx_roll;
command_rx_roll = (ch_roll-CHANN_CENTER)/15.0;
command_rx_roll_diff = command_rx_roll-command_rx_roll_old;
command_rx_pitch_old = command_rx_pitch;
command_rx_pitch = (ch_pitch-CHANN_CENTER)/15.0;
command_rx_pitch_diff = command_rx_pitch-command_rx_pitch_old;
aux_float = (ch_yaw-Neutro_yaw)/180.0;
command_rx_yaw += aux_float;
command_rx_yaw_diff = aux_float;
if (command_rx_yaw > 180) // Normalize yaw to -180,180 degrees
command_rx_yaw -= 360.0;
else if (command_rx_yaw < -180)
command_rx_yaw += 360.0;
// I use K_aux (channel 6) to adjust gains linked to a knob in the radio... [not used now]
//K_aux = K_aux*0.8 + ((ch_aux-1500)/100.0 + 0.6)*0.2;
K_aux = K_aux*0.8 + ((APM_RC_QUAD.InputCh(5)-1500)/300.0 + 0.4)*0.2;
if (K_aux < 0)
K_aux = 0;
/*
Serial.print(",");
Serial.print(K_aux);
*/
// We read the Quad Mode from Channel 5
if (APM_RC_QUAD.InputCh(4) < 1200)
{
AP_mode = 1; // Position hold mode (GPS position control)
digitalWrite(LED_Yellow,HIGH); // Yellow LED On
}
else
{
AP_mode = 2; // Normal mode (Stabilization assist mode)
digitalWrite(LED_Yellow,LOW); // Yellow LED off
}
// Write Radio data to DataFlash log
Log_Write_Radio(ch_roll,ch_pitch,ch_throttle,ch_yaw,int(K_aux*100),(int)AP_mode);
} // END new radio data
if (AP_mode==1) // Position Control
{
if (target_position==0) // If this is the first time we switch to Position control, actual position is our target position
{
target_lattitude = GPS.Lattitude;
target_longitude = GPS.Longitude;
Serial.println();
Serial.print("* Target:");
Serial.print(target_longitude);
Serial.print(",");
Serial.println(target_lattitude);
target_position=1;
//target_sonar_altitude = sonar_value;
//Initial_Throttle = ch3;
// Reset I terms
altitude_I = 0;
gps_roll_I = 0;
gps_pitch_I = 0;
}
}
else
target_position=0;
//Read GPS
GPS.Read();
if (GPS.NewData) // New GPS data?
{
GPS.NewData=0; // We Reset the flag...
//Output GPS data
//Serial.print(",");
//Serial.print(GPS.Lattitude);
//Serial.print(",");
//Serial.print(GPS.Longitude);
// Write GPS data to DataFlash log
Log_Write_GPS(GPS.Time, GPS.Lattitude,GPS.Longitude,GPS.Altitude, GPS.Ground_Speed, GPS.Ground_Course, GPS.Fix, GPS.NumSats);
if (GPS.Fix)
digitalWrite(LED_Red,HIGH); // GPS Fix => Blue LED
else
digitalWrite(LED_Red,LOW);
if (AP_mode==1)
{
if ((target_position==1)&&(GPS.Fix))
{
Position_control(target_lattitude,target_longitude); // Call position hold routine
/*
Serial.print("PC:");
Serial.print(command_gps_roll);
Serial.print(",");
Serial.print(command_gps_pitch);
Serial.println();
*/
}
else
{
//Serial.print("NOFIX");
command_gps_roll=0;
command_gps_pitch=0;
}
}
}
// Attitude control (Roll, Pitch, yaw)
Attitude_control();
// Quadcopter mix
if (ch_throttle > (MIN_THROTTLE+20)) // Minimun throttle to start control
{
APM_RC_QUAD.OutputCh(0,ch_throttle - control_roll - control_yaw); // Right motor
APM_RC_QUAD.OutputCh(1,ch_throttle + control_roll - control_yaw); // Left motor
APM_RC_QUAD.OutputCh(2,ch_throttle + control_pitch + control_yaw); // Front motor
APM_RC_QUAD.OutputCh(3,ch_throttle - control_pitch + control_yaw); // Back motor
}
else
{
roll_I = 0; // reset I terms of PID controls
pitch_I = 0;
yaw_I = 0;
APM_RC_QUAD.OutputCh(0,MIN_THROTTLE); // Motors stoped
APM_RC_QUAD.OutputCh(1,MIN_THROTTLE);
APM_RC_QUAD.OutputCh(2,MIN_THROTTLE);
APM_RC_QUAD.OutputCh(3,MIN_THROTTLE);
// Initialize yaw command to actual yaw
command_rx_yaw = ToDeg(yaw);
command_rx_yaw_diff = 0;
}
Serial.println(); // Line END
}
}

169
DCM.pde Normal file
View File

@ -0,0 +1,169 @@
/* ******* ADC functions ********************* */
// Read all the ADC channles
void Read_adc_raw(void)
{
int temp;
for (int i=0;i<6;i++)
AN[i] = APM_ADC.Ch(sensors[i]);
// Correction for non ratiometric sensor (test)
//temp = APM_ADC.Ch(3);
//AN[0] += 1500-temp;
//AN[1] += 1500-temp;
//AN[2] += 1500-temp;
}
// Returns an analog value with the offset
float read_adc(int select)
{
if (SENSOR_SIGN[select]<0)
return (AN_OFFSET[select]-AN[select]);
else
return (AN[select]-AN_OFFSET[select]);
}
/* ******************************************* */
/* ******* DCM IMU functions ********************* */
/**************************************************/
void Normalize(void)
{
float error=0;
float temporary[3][3];
float renorm=0;
error= -Vector_Dot_Product(&DCM_Matrix[0][0],&DCM_Matrix[1][0])*.5; //eq.19
Vector_Scale(&temporary[0][0], &DCM_Matrix[1][0], error); //eq.19
Vector_Scale(&temporary[1][0], &DCM_Matrix[0][0], error); //eq.19
Vector_Add(&temporary[0][0], &temporary[0][0], &DCM_Matrix[0][0]);//eq.19
Vector_Add(&temporary[1][0], &temporary[1][0], &DCM_Matrix[1][0]);//eq.19
Vector_Cross_Product(&temporary[2][0],&temporary[0][0],&temporary[1][0]); // c= a x b //eq.20
renorm= .5 *(3 - Vector_Dot_Product(&temporary[0][0],&temporary[0][0])); //eq.21
Vector_Scale(&DCM_Matrix[0][0], &temporary[0][0], renorm);
renorm= .5 *(3 - Vector_Dot_Product(&temporary[1][0],&temporary[1][0])); //eq.21
Vector_Scale(&DCM_Matrix[1][0], &temporary[1][0], renorm);
renorm= .5 *(3 - Vector_Dot_Product(&temporary[2][0],&temporary[2][0])); //eq.21
Vector_Scale(&DCM_Matrix[2][0], &temporary[2][0], renorm);
}
/**************************************************/
void Drift_correction(void)
{
//Compensation the Roll, Pitch and Yaw drift.
float errorCourse;
static float Scaled_Omega_P[3];
static float Scaled_Omega_I[3];
float Accel_magnitude;
float Accel_weight;
//*****Roll and Pitch***************
// Calculate the magnitude of the accelerometer vector
Accel_magnitude = sqrt(Accel_Vector[0]*Accel_Vector[0] + Accel_Vector[1]*Accel_Vector[1] + Accel_Vector[2]*Accel_Vector[2]);
Accel_magnitude = Accel_magnitude / GRAVITY; // Scale to gravity.
// Weight for accelerometer info (<0.5G = 0.0, 1G = 1.0 , >1.5G = 0.0)
Accel_weight = constrain(1 - 2*abs(1 - Accel_magnitude),0,1);
Vector_Cross_Product(&errorRollPitch[0],&Accel_Vector[0],&DCM_Matrix[2][0]); //adjust the ground of reference
Vector_Scale(&Omega_P[0],&errorRollPitch[0],Kp_ROLLPITCH*Accel_weight);
Vector_Scale(&Scaled_Omega_I[0],&errorRollPitch[0],Ki_ROLLPITCH*Accel_weight);
Vector_Add(Omega_I,Omega_I,Scaled_Omega_I);
//*****YAW***************
// We make the gyro YAW drift correction based on compass magnetic heading
#if (MAGNETOMETER==1)
errorCourse= (DCM_Matrix[0][0]*APM_Compass.Heading_X) - (DCM_Matrix[1][0]*APM_Compass.Heading_Y); //Calculating YAW error
Vector_Scale(errorYaw,&DCM_Matrix[2][0],errorCourse); //Applys the yaw correction to the XYZ rotation of the aircraft, depeding the position.
Vector_Scale(&Scaled_Omega_P[0],&errorYaw[0],Kp_YAW);
Vector_Add(Omega_P,Omega_P,Scaled_Omega_P);//Adding Proportional.
Vector_Scale(&Scaled_Omega_I[0],&errorYaw[0],Ki_YAW);
Vector_Add(Omega_I,Omega_I,Scaled_Omega_I);//adding integrator to the Omega_I
#endif
}
/**************************************************/
void Accel_adjust(void)
{
//Accel_Vector[1] += Accel_Scale(speed_3d*Omega[2]); // Centrifugal force on Acc_y = GPS_speed*GyroZ
//Accel_Vector[2] -= Accel_Scale(speed_3d*Omega[1]); // Centrifugal force on Acc_z = GPS_speed*GyroY
}
/**************************************************/
void Matrix_update(void)
{
Gyro_Vector[0]=Gyro_Scaled_X(read_adc(0)); //gyro x roll
Gyro_Vector[1]=Gyro_Scaled_Y(read_adc(1)); //gyro y pitch
Gyro_Vector[2]=Gyro_Scaled_Z(read_adc(2)); //gyro Z yaw
//Accel_Vector[0]=read_adc(3); // acc x
//Accel_Vector[1]=read_adc(4); // acc y
//Accel_Vector[2]=read_adc(5); // acc z
// Low pass filter on accelerometer data (to filter vibrations)
Accel_Vector[0]=Accel_Vector[0]*0.4 + (float)read_adc(3)*0.6; // acc x
Accel_Vector[1]=Accel_Vector[1]*0.4 + (float)read_adc(4)*0.6; // acc y
Accel_Vector[2]=Accel_Vector[2]*0.4 + (float)read_adc(5)*0.6; // acc z
Accel_Vector[1]=0;
Vector_Add(&Omega[0], &Gyro_Vector[0], &Omega_I[0]);//adding integrator
Vector_Add(&Omega_Vector[0], &Omega[0], &Omega_P[0]);//adding proportional
//Accel_adjust();//adjusting centrifugal acceleration. // Not used for quadcopter
#if OUTPUTMODE==1
Update_Matrix[0][0]=0;
Update_Matrix[0][1]=-G_Dt*Omega_Vector[2];//-z
Update_Matrix[0][2]=G_Dt*Omega_Vector[1];//y
Update_Matrix[1][0]=G_Dt*Omega_Vector[2];//z
Update_Matrix[1][1]=0;
Update_Matrix[1][2]=-G_Dt*Omega_Vector[0];//-x
Update_Matrix[2][0]=-G_Dt*Omega_Vector[1];//-y
Update_Matrix[2][1]=G_Dt*Omega_Vector[0];//x
Update_Matrix[2][2]=0;
#endif
#if OUTPUTMODE==0
Update_Matrix[0][0]=0;
Update_Matrix[0][1]=-G_Dt*Gyro_Vector[2];//-z
Update_Matrix[0][2]=G_Dt*Gyro_Vector[1];//y
Update_Matrix[1][0]=G_Dt*Gyro_Vector[2];//z
Update_Matrix[1][1]=0;
Update_Matrix[1][2]=-G_Dt*Gyro_Vector[0];
Update_Matrix[2][0]=-G_Dt*Gyro_Vector[1];
Update_Matrix[2][1]=G_Dt*Gyro_Vector[0];
Update_Matrix[2][2]=0;
#endif
Matrix_Multiply(DCM_Matrix,Update_Matrix,Temporary_Matrix); //a*b=c
for(int x=0; x<3; x++) //Matrix Addition (update)
{
for(int y=0; y<3; y++)
{
DCM_Matrix[x][y]+=Temporary_Matrix[x][y];
}
}
}
void Euler_angles(void)
{
#if (OUTPUTMODE==2) // Only accelerometer info (debugging purposes)
roll = atan2(Accel_Vector[1],Accel_Vector[2]); // atan2(acc_y,acc_z)
pitch = -asin((Accel_Vector[0])/(float)GRAVITY); // asin(acc_x)
yaw = 0;
#else // Euler angles from DCM matrix
pitch = asin(-DCM_Matrix[2][0]);
roll = atan2(DCM_Matrix[2][1],DCM_Matrix[2][2]);
yaw = atan2(DCM_Matrix[1][0],DCM_Matrix[0][0]);
#endif
}

11
Install.txt Normal file
View File

@ -0,0 +1,11 @@
Install notes
-------------
To install the libraries:
- copy Library Directories to your \arduino\hardware\lirbaries\ directory
- Restart arduino IDE
Each library comes with a simple example. You can find the examples in menu File->Examples
This code works with Ardupilot Mega Hardware and Beta sensor shield (FOXTRAP2).

232
Log.pde Normal file
View File

@ -0,0 +1,232 @@
// Test code to Write and Read packets from DataFlash log memory
// Packets : Attitude, Raw sensor data, Radio and GPS
#define HEAD_BYTE1 0xA3
#define HEAD_BYTE2 0x95
#define END_BYTE 0xBA
#define LOG_ATTITUDE_MSG 0x01
#define LOG_GPS_MSG 0x02
#define LOG_RADIO_MSG 0x03
#define LOG_SENSOR_MSG 0x04
// Write a Sensor Raw data packet
void Log_Write_Sensor(int s1, int s2, int s3,int s4, int s5, int s6, int s7)
{
DataFlash.WriteByte(HEAD_BYTE1);
DataFlash.WriteByte(HEAD_BYTE2);
DataFlash.WriteByte(LOG_SENSOR_MSG);
DataFlash.WriteInt(s1);
DataFlash.WriteInt(s2);
DataFlash.WriteInt(s3);
DataFlash.WriteInt(s4);
DataFlash.WriteInt(s5);
DataFlash.WriteInt(s6);
DataFlash.WriteInt(s7);
DataFlash.WriteByte(END_BYTE);
}
// Write an attitude packet. Total length : 10 bytes
void Log_Write_Attitude(int log_roll, int log_pitch, int log_yaw)
{
DataFlash.WriteByte(HEAD_BYTE1);
DataFlash.WriteByte(HEAD_BYTE2);
DataFlash.WriteByte(LOG_ATTITUDE_MSG);
DataFlash.WriteInt(log_roll);
DataFlash.WriteInt(log_pitch);
DataFlash.WriteInt(log_yaw);
DataFlash.WriteByte(END_BYTE);
}
// Write an GPS packet. Total length : 30 bytes
void Log_Write_GPS(long log_Time, long log_Lattitude, long log_Longitude, long log_Altitude,
long log_Ground_Speed, long log_Ground_Course, byte log_Fix, byte log_NumSats)
{
DataFlash.WriteByte(HEAD_BYTE1);
DataFlash.WriteByte(HEAD_BYTE2);
DataFlash.WriteByte(LOG_GPS_MSG);
DataFlash.WriteLong(log_Time);
DataFlash.WriteByte(log_Fix);
DataFlash.WriteByte(log_NumSats);
DataFlash.WriteLong(log_Lattitude);
DataFlash.WriteLong(log_Longitude);
DataFlash.WriteLong(log_Altitude);
DataFlash.WriteLong(log_Ground_Speed);
DataFlash.WriteLong(log_Ground_Course);
DataFlash.WriteByte(END_BYTE);
}
// Write a Radio packet
void Log_Write_Radio(int ch1, int ch2, int ch3,int ch4, int ch5, int ch6)
{
DataFlash.WriteByte(HEAD_BYTE1);
DataFlash.WriteByte(HEAD_BYTE2);
DataFlash.WriteByte(LOG_RADIO_MSG);
DataFlash.WriteInt(ch1);
DataFlash.WriteInt(ch2);
DataFlash.WriteInt(ch3);
DataFlash.WriteInt(ch4);
DataFlash.WriteInt(ch5);
DataFlash.WriteInt(ch6);
DataFlash.WriteByte(END_BYTE);
}
// **** READ ROUTINES ****
// Read a Sensor raw data packet
void Log_Read_Sensor()
{
Serial.print("SENSOR:");
Serial.print(DataFlash.ReadInt()); // GX
Serial.print(",");
Serial.print(DataFlash.ReadInt()); // GY
Serial.print(",");
Serial.print(DataFlash.ReadInt()); // GZ
Serial.print(",");
Serial.print(DataFlash.ReadInt()); // ACCX
Serial.print(",");
Serial.print(DataFlash.ReadInt()); // ACCY
Serial.print(",");
Serial.print(DataFlash.ReadInt()); // ACCZ
Serial.print(",");
Serial.print(DataFlash.ReadInt()); // AUX
Serial.println();
}
// Read an attitude packet
void Log_Read_Attitude()
{
int log_roll;
int log_pitch;
int log_yaw;
log_roll = DataFlash.ReadInt();
log_pitch = DataFlash.ReadInt();
log_yaw = DataFlash.ReadInt();
Serial.print("ATT:");
Serial.print(log_roll);
Serial.print(",");
Serial.print(log_pitch);
Serial.print(",");
Serial.print(log_yaw);
Serial.println();
}
// Read a GPS packet
void Log_Read_GPS()
{
long log_Time;
byte log_Fix;
byte log_NumSats;
long log_Lattitude;
long log_Longitude;
long log_Altitude;
long log_Ground_Speed;
long log_Ground_Course;
log_Time = DataFlash.ReadLong();
log_Fix = DataFlash.ReadByte();
log_NumSats = DataFlash.ReadByte();
log_Lattitude = DataFlash.ReadLong();
log_Longitude = DataFlash.ReadLong();
log_Altitude = DataFlash.ReadLong();
log_Ground_Speed = DataFlash.ReadLong();
log_Ground_Course = DataFlash.ReadLong();
Serial.print("GPS:");
Serial.print(log_Time);
Serial.print(",");
Serial.print((int)log_Fix);
Serial.print(",");
Serial.print((int)log_NumSats);
Serial.print(",");
Serial.print(log_Lattitude);
Serial.print(",");
Serial.print(log_Longitude);
Serial.print(",");
Serial.print(log_Altitude);
Serial.print(",");
Serial.print(log_Ground_Speed);
Serial.print(",");
Serial.print(log_Ground_Course);
Serial.println();
}
// Read an Radio packet
void Log_Read_Radio()
{
Serial.print("RADIO:");
Serial.print(DataFlash.ReadInt());
Serial.print(",");
Serial.print(DataFlash.ReadInt());
Serial.print(",");
Serial.print(DataFlash.ReadInt());
Serial.print(",");
Serial.print(DataFlash.ReadInt());
Serial.print(",");
Serial.print(DataFlash.ReadInt());
Serial.print(",");
Serial.print(DataFlash.ReadInt());
Serial.println();
}
// Read the DataFlash log memory : Packet Parser
void Log_Read(int start_page, int end_page)
{
byte data;
byte log_step=0;
int packet_count=0;
DataFlash.StartRead(start_page);
while (DataFlash.GetPage() < end_page)
{
data = DataFlash.ReadByte();
switch(log_step) //This is a state machine to read the packets
{
case 0:
if(data==HEAD_BYTE1) // Head byte 1
log_step++;
break;
case 1:
if(data==HEAD_BYTE2) // Head byte 2
log_step++;
break;
case 2:
switch (data)
{
case LOG_ATTITUDE_MSG:
Log_Read_Attitude();
log_step++;
break;
case LOG_GPS_MSG:
Log_Read_GPS();
log_step++;
break;
case LOG_RADIO_MSG:
Log_Read_Radio();
log_step++;
break;
case LOG_SENSOR_MSG:
Log_Read_Sensor();
log_step++;
break;
default:
Serial.print("Error Reading Packet: ");
Serial.print(packet_count);
log_step=0; // Restart, we have a problem...
}
break;
case 3:
if(data==END_BYTE)
packet_count++;
else
Serial.println("Error Reading END_BYTE");
log_step=0; // Restart sequence: new packet...
}
}
Serial.print("Number of packets read: ");
Serial.println(packet_count);
}

60
Vector.pde Normal file
View File

@ -0,0 +1,60 @@
//Computes the dot product of two vectors
float Vector_Dot_Product(float vector1[3],float vector2[3])
{
float op=0;
for(int c=0; c<3; c++)
{
op+=vector1[c]*vector2[c];
}
return op;
}
//Computes the cross product of two vectors
void Vector_Cross_Product(float vectorOut[3], float v1[3],float v2[3])
{
vectorOut[0]= (v1[1]*v2[2]) - (v1[2]*v2[1]);
vectorOut[1]= (v1[2]*v2[0]) - (v1[0]*v2[2]);
vectorOut[2]= (v1[0]*v2[1]) - (v1[1]*v2[0]);
}
//Multiply the vector by a scalar.
void Vector_Scale(float vectorOut[3],float vectorIn[3], float scale2)
{
for(int c=0; c<3; c++)
{
vectorOut[c]=vectorIn[c]*scale2;
}
}
void Vector_Add(float vectorOut[3],float vectorIn1[3], float vectorIn2[3])
{
for(int c=0; c<3; c++)
{
vectorOut[c]=vectorIn1[c]+vectorIn2[c];
}
}
/********* MATRIX FUNCTIONS *****************************************/
//Multiply two 3x3 matrixs. This function developed by Jordi can be easily adapted to multiple n*n matrix's. (Pero me da flojera!).
void Matrix_Multiply(float a[3][3], float b[3][3],float mat[3][3])
{
float op[3];
for(int x=0; x<3; x++)
{
for(int y=0; y<3; y++)
{
for(int w=0; w<3; w++)
{
op[w]=a[x][w]*b[w][y];
}
mat[x][y]=0;
mat[x][y]=op[0]+op[1]+op[2];
float test=mat[x][y];
}
}
}

View File

@ -0,0 +1,133 @@
/*
APM_ADC.cpp - ADC ADS7844 Library for Ardupilot Mega
Code by Jordi Muñoz and Jose Julio. DIYDrones.com
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
External ADC ADS7844 is connected via Serial port 2 (in SPI mode)
TXD2 = MOSI = pin PH1
RXD2 = MISO = pin PH0
XCK2 = SCK = pin PH2
Chip Select pin is PC4 (33) [PH6 (9)]
We are using the 16 clocks per conversion timming to increase efficiency (fast)
The sampling frequency is 400Hz (Timer2 overflow interrupt)
So if our loop is at 50Hz, our needed sampling freq should be 100Hz, so
we have an 4x oversampling and averaging.
Methods:
Init() : Initialization of interrupts an Timers (Timer2 overflow interrupt)
Ch(ch_num) : Return the ADC channel value
On Ardupilot Mega Hardware:
Channel 1 : Gyro Z
Channel 2 : Gyro X
Channel 3 : Gyro Y
Channel 4 : Acc X
Channel 5 : Acc Y
Channel 6 : Acc Z
Channel 7 : Differential pressure sensor
*/
extern "C" {
// AVR LibC Includes
#include <inttypes.h>
#include <avr/interrupt.h>
#include "WConstants.h"
}
#include "APM_ADC.h"
// Commands for reading ADC channels on ADS7844
const unsigned char adc_cmd[9]= { 0x87, 0xC7, 0x97, 0xD7, 0xA7, 0xE7, 0xB7, 0xF7, 0x00 };
volatile long adc_value[8];
volatile unsigned char adc_counter[8];
unsigned char ADC_SPI_transfer(unsigned char data)
{
/* Wait for empty transmit buffer */
while ( !( UCSR2A & (1<<UDRE2)) );
/* Put data into buffer, sends the data */
UDR2 = data;
/* Wait for data to be received */
while ( !(UCSR2A & (1<<RXC2)) );
/* Get and return received data from buffer */
return UDR2;
}
ISR (TIMER2_OVF_vect)
{
uint8_t ch;
unsigned int adc_tmp;
//bit_set(PORTL,6); // To test performance
bit_clear(PORTC,4); // Enable Chip Select (PIN PC4)
ADC_SPI_transfer(adc_cmd[0]); // Command to read the first channel
for (ch=0;ch<7;ch++)
{
adc_tmp = ADC_SPI_transfer(0)<<8; // Read first byte
adc_tmp |= ADC_SPI_transfer(adc_cmd[ch+1]); // Read second byte and send next command
adc_value[ch] += adc_tmp>>3; // Shift to 12 bits
adc_counter[ch]++; // Number of samples
}
bit_set(PORTC,4); // Disable Chip Select (PIN PC4)
//bit_clear(PORTL,6); // To test performance
TCNT2 = 104; // 400 Hz
}
// Constructors ////////////////////////////////////////////////////////////////
APM_ADC_Class::APM_ADC_Class()
{
}
// Public Methods //////////////////////////////////////////////////////////////
void APM_ADC_Class::Init(void)
{
unsigned char tmp;
pinMode(ADC_CHIP_SELECT,OUTPUT);
digitalWrite(ADC_CHIP_SELECT,HIGH); // Disable device (Chip select is active low)
// Setup Serial Port2 in SPI mode
UBRR2 = 0;
DDRH |= (1<<PH2); // SPI clock XCK2 (PH2) as output. This enable SPI Master mode
// Set MSPI mode of operation and SPI data mode 0.
UCSR2C = (1<<UMSEL21)|(1<<UMSEL20); //|(0<<UCPHA2)|(0<<UCPOL2);
// Enable receiver and transmitter.
UCSR2B = (1<<RXEN2)|(1<<TXEN2);
// Set Baud rate
UBRR2 = 2; // SPI clock running at 2.6MHz
// Enable Timer2 Overflow interrupt to capture ADC data
TIMSK2 = 0; // Disable interrupts
TCCR2A = 0; // normal counting mode
TCCR2B = _BV(CS21)|_BV(CS22); // Set prescaler of 256
TCNT2 = 0;
TIFR2 = _BV(TOV2); // clear pending interrupts;
TIMSK2 = _BV(TOIE2) ; // enable the overflow interrupt
}
// Read one channel value
int APM_ADC_Class::Ch(unsigned char ch_num)
{
int result;
cli(); // We stop interrupts to read the variables
if (adc_counter[ch_num]>0)
result = adc_value[ch_num]/adc_counter[ch_num];
else
result = 0;
adc_value[ch_num] = 0; // Initialize for next reading
adc_counter[ch_num] = 0;
sei();
return(result);
}
// make one instance for the user to use
APM_ADC_Class APM_ADC;

View File

@ -0,0 +1,24 @@
#ifndef APM_ADC_h
#define APM_ADC_h
#define bit_set(p,m) ((p) |= (1<<m))
#define bit_clear(p,m) ((p) &= ~(1<<m))
// We use Serial Port 2 in SPI Mode
#define ADC_DATAOUT 51 // MOSI
#define ADC_DATAIN 50 // MISO
#define ADC_SPICLOCK 52 // SCK
#define ADC_CHIP_SELECT 33 // PC4 9 // PH6 Puerto:0x08 Bit mask : 0x40
class APM_ADC_Class
{
private:
public:
APM_ADC_Class(); // Constructor
void Init();
int Ch(unsigned char ch_num);
};
extern APM_ADC_Class APM_ADC;
#endif

View File

@ -0,0 +1,33 @@
/*
Example of APM_ADC library.
Code by Jordi Muñoz and Jose Julio. DIYDrones.com
*/
#include <APM_ADC.h> // ArduPilot Mega ADC Library
unsigned long timer;
void setup()
{
APM_ADC.Init(); // APM ADC initialization
Serial.begin(57600);
Serial.println("ArduPilot Mega ADC library test");
delay(1000);
timer = millis();
}
void loop()
{
int ch;
if((millis()- timer) > 20)
{
timer = millis();
for (ch=0;ch<7;ch++)
{
Serial.print(APM_ADC.Ch(ch));
Serial.print(",");
}
Serial.println();
}
}

View File

@ -0,0 +1,3 @@
APM_ADC KEYWORD1
Init KEYWORD2
Ch KEYWORD2

View File

@ -0,0 +1,243 @@
/*
APM_BMP085.cpp - Arduino Library for BMP085 absolute pressure sensor
Code by Jordi Muñoz and Jose Julio. DIYDrones.com
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
Sensor is conected to I2C port
Sensor End of Conversion (EOC) pin is PC7 (30)
Variables:
RawTemp : Raw temperature data
RawPress : Raw pressure data
Temp : Calculated temperature (in 0.1ºC units)
Press : Calculated pressure (in Pa units)
Methods:
Init() : Initialization of I2C and read sensor calibration data
Read() : Read sensor data and calculate Temperature and Pressure
This function is optimized so the main host don´t need to wait
You can call this function in your main loop
It returns a 1 if there are new data.
Internal functions:
Command_ReadTemp(): Send commando to read temperature
Command_ReadPress(): Send commando to read Pressure
ReadTemp() : Read temp register
ReadPress() : Read press register
Calculate() : Calculate Temperature and Pressure in real units
*/
extern "C" {
// AVR LibC Includes
#include <inttypes.h>
#include <avr/interrupt.h>
#include "WConstants.h"
}
#include <Wire.h>
#include "APM_BMP085.h"
#define BMP085_ADDRESS 0x77 //(0xEE >> 1)
#define BMP085_EOC 30 // End of conversion pin PC7
// Constructors ////////////////////////////////////////////////////////////////
APM_BMP085_Class::APM_BMP085_Class()
{
}
// Public Methods //////////////////////////////////////////////////////////////
void APM_BMP085_Class::Init(void)
{
unsigned char tmp;
byte buff[22];
int i=0;
pinMode(BMP085_EOC,INPUT); // End Of Conversion (PC7) input
Wire.begin();
oss = 3; // Over Sampling setting 3 = High resolution
BMP085_State = 0; // Initial state
// We read the calibration data registers
Wire.beginTransmission(BMP085_ADDRESS);
Wire.send(0xAA);
Wire.endTransmission();
Wire.requestFrom(BMP085_ADDRESS, 22);
//Wire.endTransmission();
while(Wire.available())
{
buff[i] = Wire.receive(); // receive one byte
i++;
}
ac1 = ((int)buff[0] << 8) | buff[1];
ac2 = ((int)buff[2] << 8) | buff[3];
ac3 = ((int)buff[4] << 8) | buff[5];
ac4 = ((int)buff[6] << 8) | buff[7];
ac5 = ((int)buff[8] << 8) | buff[9];
ac6 = ((int)buff[10] << 8) | buff[11];
b1 = ((int)buff[12] << 8) | buff[13];
b2 = ((int)buff[14] << 8) | buff[15];
mb = ((int)buff[16] << 8) | buff[17];
mc = ((int)buff[18] << 8) | buff[19];
md = ((int)buff[20] << 8) | buff[21];
//Send a command to read Temp
Command_ReadTemp();
BMP085_State=1;
}
// Read the sensor. This is a state machine
// We read one time Temperature (state=1) and then 4 times Pressure (states 2-5)
uint8_t APM_BMP085_Class::Read()
{
uint8_t result=0;
if (BMP085_State==1)
{
if (digitalRead(BMP085_EOC))
{
ReadTemp(); // On state 1 we read temp
BMP085_State++;
Command_ReadPress();
}
}
else
{
if (BMP085_State==5)
{
if (digitalRead(BMP085_EOC))
{
ReadPress();
Calculate();
BMP085_State = 1; // Start again from state=1
Command_ReadTemp(); // Read Temp
result=1; // New pressure reading
}
}
else
{
if (digitalRead(BMP085_EOC))
{
ReadPress();
Calculate();
BMP085_State++;
Command_ReadPress();
result=1; // New pressure reading
}
}
}
return(result);
}
// Send command to Read Pressure
void APM_BMP085_Class::Command_ReadPress()
{
Wire.beginTransmission(BMP085_ADDRESS);
Wire.send(0xF4);
Wire.send(0x34+(oss<<6)); //write_register(0xF4,0x34+(oversampling_setting<<6));
Wire.endTransmission();
}
// Read Raw Pressure values
void APM_BMP085_Class::ReadPress()
{
byte msb;
byte lsb;
byte xlsb;
Wire.beginTransmission(BMP085_ADDRESS);
Wire.send(0xF6);
Wire.endTransmission();
Wire.requestFrom(BMP085_ADDRESS, 3); // read a byte
while(!Wire.available()) {
// waiting
}
msb = Wire.receive();
while(!Wire.available()) {
// waiting
}
lsb = Wire.receive();
while(!Wire.available()) {
// waiting
}
xlsb = Wire.receive();
RawPress = (((long)msb<<16) | ((long)lsb<<8) | ((long)xlsb)) >> (8-oss);
}
// Send Command to Read Temperature
void APM_BMP085_Class::Command_ReadTemp()
{
Wire.beginTransmission(BMP085_ADDRESS);
Wire.send(0xF4);
Wire.send(0x2E);
Wire.endTransmission();
}
// Read Raw Temperature values
void APM_BMP085_Class::ReadTemp()
{
byte tmp;
Wire.beginTransmission(BMP085_ADDRESS);
Wire.send(0xF6);
Wire.endTransmission();
Wire.beginTransmission(BMP085_ADDRESS);
Wire.requestFrom(BMP085_ADDRESS,2);
while(!Wire.available()); // wait
RawTemp = Wire.receive();
while(!Wire.available()); // wait
tmp = Wire.receive();
RawTemp = RawTemp<<8 | tmp;
}
// Calculate Temperature and Pressure in real units.
void APM_BMP085_Class::Calculate()
{
long x1, x2, x3, b3, b5, b6, p;
unsigned long b4, b7;
int32_t tmp;
// See Datasheet page 13 for this formulas
// Based also on Jee Labs BMP085 example code. Thanks for share.
// Temperature calculations
x1 = ((long)RawTemp - ac6) * ac5 >> 15;
x2 = ((long) mc << 11) / (x1 + md);
b5 = x1 + x2;
Temp = (b5 + 8) >> 4;
// Pressure calculations
b6 = b5 - 4000;
x1 = (b2 * (b6 * b6 >> 12)) >> 11;
x2 = ac2 * b6 >> 11;
x3 = x1 + x2;
//b3 = (((int32_t) ac1 * 4 + x3)<<oss + 2) >> 2; // BAD
//b3 = ((int32_t) ac1 * 4 + x3 + 2) >> 2; //OK for oss=0
tmp = ac1;
tmp = (tmp*4 + x3)<<oss;
b3 = (tmp+2)/4;
x1 = ac3 * b6 >> 13;
x2 = (b1 * (b6 * b6 >> 12)) >> 16;
x3 = ((x1 + x2) + 2) >> 2;
b4 = (ac4 * (uint32_t) (x3 + 32768)) >> 15;
b7 = ((uint32_t) RawPress - b3) * (50000 >> oss);
p = b7 < 0x80000000 ? (b7 * 2) / b4 : (b7 / b4) * 2;
x1 = (p >> 8) * (p >> 8);
x1 = (x1 * 3038) >> 16;
x2 = (-7357 * p) >> 16;
Press = p + ((x1 + x2 + 3791) >> 4);
}
// make one instance for the user to use
APM_BMP085_Class APM_BMP085;

View File

@ -0,0 +1,34 @@
#ifndef APM_BMP085_h
#define APM_BMP085_h
class APM_BMP085_Class
{
private:
// State machine
uint8_t BMP085_State;
// Internal calibration registers
int16_t ac1, ac2, ac3, b1, b2, mb, mc, md;
uint16_t ac4, ac5, ac6;
void Command_ReadPress();
void Command_ReadTemp();
void ReadPress();
void ReadTemp();
void Calculate();
public:
int32_t RawPress;
int32_t RawTemp;
int16_t Temp;
int32_t Press;
//int Altitude;
uint8_t oss;
//int32_t Press0; // Pressure at sea level
APM_BMP085_Class(); // Constructor
void Init();
uint8_t Read();
};
extern APM_BMP085_Class APM_BMP085;
#endif

View File

@ -0,0 +1,41 @@
/*
Example of APM_BMP085 (absolute pressure sensor) library.
Code by Jordi Muñoz and Jose Julio. DIYDrones.com
*/
#include <Wire.h>
#include <APM_BMP085.h> // ArduPilot Mega BMP085 Library
unsigned long timer;
void setup()
{
APM_BMP085.Init(); // APM ADC initialization
Serial.begin(57600);
Serial.println("ArduPilot Mega BMP085 library test");
delay(1000);
timer = millis();
}
void loop()
{
int ch;
float tmp_float;
float Altitude;
if((millis()- timer) > 50)
{
timer=millis();
APM_BMP085.Read();
Serial.print("Pressure:");
Serial.print(APM_BMP085.Press);
Serial.print(" Temperature:");
Serial.print(APM_BMP085.Temp/10.0);
Serial.print(" Altitude:");
tmp_float = (APM_BMP085.Press/101325.0);
tmp_float = pow(tmp_float,0.190295);
Altitude = 44330*(1.0-tmp_float);
Serial.print(Altitude);
Serial.println();
}
}

View File

@ -0,0 +1,5 @@
APM_BMP085 KEYWORD1
Init KEYWORD2
Read KEYWORD2
Press KEYWORD2
Temp KEYWORD2

View File

@ -0,0 +1,110 @@
/*
APM_Compass.cpp - Arduino Library for HMC5843 I2C Magnetometer
Code by Jordi Muñoz and Jose Julio. DIYDrones.com
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
Sensor is conected to I2C port
Sensor is initialized in Continuos mode (10Hz)
Variables:
Heading : Magnetic heading
Heading_X : Magnetic heading X component
Heading_Y : Magnetic heading Y component
Mag_X : Raw X axis magnetometer data
Mag_Y : Raw Y axis magnetometer data
Mag_Z : Raw Z axis magnetometer data
Methods:
Init() : Initialization of I2C and sensor
Read() : Read Sensor data
To do : Calibration of the sensor, code optimization
Mount position : Big capacitor pointing forward, connector backward
*/
extern "C" {
// AVR LibC Includes
#include <math.h>
#include "WConstants.h"
}
#include <Wire.h>
#include "APM_Compass.h"
#define CompassAddress 0x1E
// Constructors ////////////////////////////////////////////////////////////////
APM_Compass_Class::APM_Compass_Class()
{
}
// Public Methods //////////////////////////////////////////////////////////////
void APM_Compass_Class::Init(void)
{
Wire.begin();
Wire.beginTransmission(CompassAddress);
Wire.send(0x02);
Wire.send(0x00); // Set continouos mode (default to 10Hz)
Wire.endTransmission(); //end transmission
}
// Read Sensor data
void APM_Compass_Class::Read()
{
int i = 0;
byte buff[6];
Wire.beginTransmission(CompassAddress);
Wire.send(0x03); //sends address to read from
Wire.endTransmission(); //end transmission
//Wire.beginTransmission(CompassAddress);
Wire.requestFrom(CompassAddress, 6); // request 6 bytes from device
while(Wire.available())
{
buff[i] = Wire.receive(); // receive one byte
i++;
}
Wire.endTransmission(); //end transmission
if (i==6) // All bytes received?
{
// MSB byte first, then LSB, X,Y,Z
Mag_X = ((((int)buff[0]) << 8) | buff[1]); // X axis
Mag_Y = -((((int)buff[2]) << 8) | buff[3]); // Y axis
Mag_Z = -((((int)buff[4]) << 8) | buff[5]); // Z axis
}
}
void APM_Compass_Class::Calculate(float roll, float pitch)
{
float Head_X;
float Head_Y;
float cos_roll;
float sin_roll;
float cos_pitch;
float sin_pitch;
cos_roll = cos(roll); // Optimizacion, se puede sacar esto de la matriz DCM?
sin_roll = sin(roll);
cos_pitch = cos(pitch);
sin_pitch = sin(pitch);
// Tilt compensated Magnetic field X component:
Head_X = Mag_X*cos_pitch+Mag_Y*sin_roll*sin_pitch+Mag_Z*cos_roll*sin_pitch;
// Tilt compensated Magnetic field Y component:
Head_Y = Mag_Y*cos_roll-Mag_Z*sin_roll;
// Magnetic Heading
Heading = atan2(-Head_Y,Head_X);
// Optimization for external DCM use. Calculate normalized components
Heading_X = cos(Heading);
Heading_Y = sin(Heading);
}
// make one instance for the user to use
APM_Compass_Class APM_Compass;

View File

@ -0,0 +1,23 @@
#ifndef APM_Compass_h
#define APM_Compass_h
class APM_Compass_Class
{
private:
public:
int Mag_X;
int Mag_Y;
int Mag_Z;
float Heading;
float Heading_X;
float Heading_Y;
APM_Compass_Class(); // Constructor
void Init();
void Read();
void Calculate(float roll, float pitch);
};
extern APM_Compass_Class APM_Compass;
#endif

View File

@ -0,0 +1,41 @@
/*
Example of APM_Compass library (HMC5843 sensor).
Code by Jordi Muñoz and Jose Julio. DIYDrones.com
*/
#include <Wire.h>
#include <APM_Compass.h> // Compass Library
#define ToDeg(x) (x*57.2957795131) // *180/pi
unsigned long timer;
void setup()
{
APM_Compass.Init(); // Initialization
Serial.begin(57600);
Serial.println("Compass library test (HMC5843)");
delay(1000);
timer = millis();
}
void loop()
{
float tmp;
if((millis()- timer) > 100)
{
timer = millis();
APM_Compass.Read();
APM_Compass.Calculate(0,0); // roll = 0, pitch = 0 for this example
Serial.print("Heading:");
Serial.print(ToDeg(APM_Compass.Heading));
Serial.print(" (");
Serial.print(APM_Compass.Mag_X);
Serial.print(",");
Serial.print(APM_Compass.Mag_Y);
Serial.print(",");
Serial.print(APM_Compass.Mag_Z);
Serial.println();
}
}

View File

@ -0,0 +1,13 @@
APM_Compass KEYWORD1
Init KEYWORD2
Read KEYWORD2
Calculate KEYWORD2
Heading KEYWORD2
Heading_X KEYWORD2
Heading_Y KEYWORD2
Mag_X KEYWORD2
Mag_Y KEYWORD2
Mag_Z KEYWORD2

View File

@ -0,0 +1,169 @@
/*
APM_RC_QUAD.cpp - Radio Control Library for Ardupilot Mega. Arduino
Code by Jordi Muñoz and Jose Julio. DIYDrones.com
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This is a special version of the library for use in Quadcopters
Because we have modified servo outputs 0..3 to have a higher rate (300Hz)
RC Input : PPM signal on IC4 pin
RC Output : 8 servo Outputs,
OUT0..OUT3 : 300Hz Servo output (for Quad ESC´s)
OUT4..OUT7 : standard 50Hz servo outputs
Methods:
Init() : Initialization of interrupts an Timers
OutpuCh(ch,pwm) : Output value to servos (range : 900-2100us) ch=0..10
InputCh(ch) : Read a channel input value. ch=0..7
GetState() : Returns the state of the input. 1 => New radio frame to process
Automatically resets when we call InputCh to read channels
*/
#include "APM_RC_QUAD.h"
#include <avr/interrupt.h>
#include "WProgram.h"
// Variable definition for Input Capture interrupt
volatile unsigned int ICR4_old;
volatile unsigned char PPM_Counter=0;
volatile unsigned int PWM_RAW[8] = {2400,2400,2400,2400,2400,2400,2400,2400};
volatile unsigned char radio_status=0;
/****************************************************
Input Capture Interrupt ICP4 => PPM signal read
****************************************************/
ISR(TIMER4_CAPT_vect)
{
unsigned int Pulse;
unsigned int Pulse_Width;
Pulse=ICR4;
if (Pulse<ICR4_old) // Take care of the overflow of Timer4 (TOP=40000)
Pulse_Width=(Pulse + 40000)-ICR4_old; //Calculating pulse
else
Pulse_Width=Pulse-ICR4_old; //Calculating pulse
if (Pulse_Width>8000) // SYNC pulse?
PPM_Counter=0;
else
{
PPM_Counter &= 0x07; // For safety only (limit PPM_Counter to 7)
PWM_RAW[PPM_Counter++]=Pulse_Width; //Saving pulse.
if (PPM_Counter >= NUM_CHANNELS)
radio_status = 1;
}
ICR4_old = Pulse;
}
// Constructors ////////////////////////////////////////////////////////////////
APM_RC_QUAD_Class::APM_RC_QUAD_Class()
{
}
// Public Methods //////////////////////////////////////////////////////////////
void APM_RC_QUAD_Class::Init(void)
{
// Init PWM Timer 1
//pinMode(11,OUTPUT); // (PB5/OC1A)
pinMode(12,OUTPUT); //OUT2 (PB6/OC1B)
pinMode(13,OUTPUT); //OUT3 (PB7/OC1C)
//Remember the registers not declared here remains zero by default...
TCCR1A =((1<<WGM11)|(1<<COM1B1)|(1<<COM1C1)); //Please read page 131 of DataSheet, we are changing the registers settings of WGM11,COM1B1,COM1A1 to 1 thats all...
TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS11); //Prescaler set to 8, that give us a resolution of 0.5us, read page 134 of data sheet
//OCR1A = 3000; //PB5, none
OCR1B = 3000; //PB6, OUT2
OCR1C = 3000; //PB7 OUT3
ICR1 = 6600; //300hz freq...
// Init PWM Timer 3
pinMode(2,OUTPUT); //OUT7 (PE4/OC3B)
pinMode(3,OUTPUT); //OUT6 (PE5/OC3C)
//pinMode(4,OUTPUT); // (PE3/OC3A)
TCCR3A =((1<<WGM31)|(1<<COM3B1)|(1<<COM3C1));
TCCR3B = (1<<WGM33)|(1<<WGM32)|(1<<CS31);
//OCR3A = 3000; //PE3, NONE
OCR3B = 3000; //PE4, OUT7
OCR3C = 3000; //PE5, OUT6
ICR3 = 40000; //50hz freq (standard servos)
// Init PWM Timer 5
pinMode(44,OUTPUT); //OUT1 (PL5/OC5C)
pinMode(45,OUTPUT); //OUT0 (PL4/OC5B)
//pinMode(46,OUTPUT); // (PL3/OC5A)
TCCR5A =((1<<WGM51)|(1<<COM5B1)|(1<<COM5C1));
TCCR5B = (1<<WGM53)|(1<<WGM52)|(1<<CS51);
//OCR5A = 3000; //PL3,
OCR5B = 3000; //PL4, OUT0
OCR5C = 3000; //PL5, OUT1
ICR5 = 6600; //300hz freq
// Init PPM input and PWM Timer 4
pinMode(49, INPUT); // ICP4 pin (PL0) (PPM input)
pinMode(7,OUTPUT); //OUT5 (PH4/OC4B)
pinMode(8,OUTPUT); //OUT4 (PH5/OC4C)
TCCR4A =((1<<WGM40)|(1<<WGM41)|(1<<COM4C1)|(1<<COM4B1)|(1<<COM4A1));
//Prescaler set to 8, that give us a resolution of 0.5us
// Input Capture rising edge
TCCR4B = ((1<<WGM43)|(1<<WGM42)|(1<<CS41)|(1<<ICES4));
OCR4A = 40000; ///50hz freq. (standard servos)
OCR4B = 3000; //PH4, OUT5
OCR4C = 3000; //PH5, OUT4
//TCCR4B |=(1<<ICES4); //Changing edge detector (rising edge).
//TCCR4B &=(~(1<<ICES4)); //Changing edge detector. (falling edge)
TIMSK4 |= (1<<ICIE4); // Enable Input Capture interrupt. Timer interrupt mask
}
void APM_RC_QUAD_Class::OutputCh(unsigned char ch, int pwm)
{
pwm=constrain(pwm,MIN_PULSEWIDTH,MAX_PULSEWIDTH);
pwm<<=1; // pwm*2;
switch(ch)
{
case 0: OCR5B=pwm; break; //ch0
case 1: OCR5C=pwm; break; //ch1
case 2: OCR1B=pwm; break; //ch2
case 3: OCR1C=pwm; break; //ch3
case 4: OCR4C=pwm; break; //ch4
case 5: OCR4B=pwm; break; //ch5
case 6: OCR3C=pwm; break; //ch6
case 7: OCR3B=pwm; break; //ch7
}
}
int APM_RC_QUAD_Class::InputCh(unsigned char ch)
{
int result;
int result2;
// Because servo pulse variables are 16 bits and the interrupts are running values could be corrupted.
// We dont want to stop interrupts to read radio channels so we have to do two readings to be sure that the value is correct...
result = PWM_RAW[ch]>>1; // Because timer runs at 0.5us we need to do value/2
result2 = PWM_RAW[ch]>>1;
if (result != result2)
result = PWM_RAW[ch]>>1; // if the results are different we make a third reading (this should be fine)
// Limit values to a valid range
result = constrain(result,MIN_PULSEWIDTH,MAX_PULSEWIDTH);
radio_status=0; // Radio channel read
return(result);
}
unsigned char APM_RC_QUAD_Class::GetState(void)
{
return(radio_status);
}
// make one instance for the user to use
APM_RC_QUAD_Class APM_RC_QUAD;

View File

@ -0,0 +1,21 @@
#ifndef APM_RC_QUAD_h
#define APM_RC_QUAD_h
#define NUM_CHANNELS 8
#define MIN_PULSEWIDTH 900
#define MAX_PULSEWIDTH 2100
class APM_RC_QUAD_Class
{
private:
public:
APM_RC_QUAD_Class();
void Init();
void OutputCh(unsigned char ch, int pwm);
int InputCh(unsigned char ch);
unsigned char GetState();
};
extern APM_RC_QUAD_Class APM_RC_QUAD;
#endif

View File

@ -0,0 +1,31 @@
/*
Example of APM_RC library.
Code by Jordi Muñoz and Jose Julio. DIYDrones.com
Print Input values and send Output to the servos
(Works with last PPM_encoder firmware)
*/
#include <APM_RC.h> // ArduPilot Mega RC Library
void setup()
{
APM_RC.Init(); // APM Radio initialization
Serial.begin(57600);
Serial.println("ArduPilot Mega RC library test");
delay(1000);
}
void loop()
{
if (APM_RC.GetState()==1) // New radio frame? (we could use also if((millis()- timer) > 20)
{
Serial.print("CH:");
for(int i=0;i<8;i++)
{
Serial.print(APM_RC.InputCh(i)); // Print channel values
Serial.print(",");
APM_RC.OutputCh(i,APM_RC.InputCh(i)); // Copy input to Servos
}
Serial.println();
}
}

View File

@ -0,0 +1,5 @@
APM_RC_QUAD KEYWORD1
begin KEYWORD2
InputCh KEYWORD2
OutputCh KEYWORD2
GetState KEYWORD2

View File

@ -0,0 +1,335 @@
/*
DataFlash.cpp - DataFlash log library for AT45DB161
Code by Jordi Muñoz and Jose Julio. DIYDrones.com
This code works with boards based on ATMega168/328 and ATMega1280 using SPI port
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
Dataflash library for AT45DB161D flash memory
Memory organization : 4096 pages of 512 bytes
Maximun write bandwidth : 512 bytes in 14ms
This code is written so the master never has to wait to write the data on the eeprom
Methods:
Init() : Library initialization (SPI initialization)
StartWrite(page) : Start a write session. page=start page.
WriteByte(data) : Write a byte
WriteInt(data) : Write an integer (2 bytes)
WriteLong(data) : Write a long (4 bytes)
StartRead(page) : Start a read on (page)
GetPage() : Returns the last page read
ReadByte()
ReadInt()
ReadLong()
Properties:
*/
extern "C" {
// AVR LibC Includes
#include <inttypes.h>
#include <avr/interrupt.h>
#include "WConstants.h"
}
#include "DataFlash.h"
#define OVERWRITE_DATA 0 // 0: When reach the end page stop, 1: Start overwritten from page 1
// *** INTERNAL FUNCTIONS ***
unsigned char dataflash_SPI_transfer(unsigned char output)
{
SPDR = output; // Start the transmission
while (!(SPSR & (1<<SPIF))); // Wait the end of the transmission
return SPDR;
}
void dataflash_CS_inactive()
{
digitalWrite(DF_SLAVESELECT,HIGH); //disable device
}
void dataflash_CS_active()
{
digitalWrite(DF_SLAVESELECT,LOW); //enable device
}
// Constructors ////////////////////////////////////////////////////////////////
DataFlash_Class::DataFlash_Class()
{
}
// Public Methods //////////////////////////////////////////////////////////////
void DataFlash_Class::Init(void)
{
byte tmp;
pinMode(DF_DATAOUT, OUTPUT);
pinMode(DF_DATAIN, INPUT);
pinMode(DF_SPICLOCK,OUTPUT);
pinMode(DF_SLAVESELECT,OUTPUT);
#if defined(__AVR_ATmega1280__)
pinMode(DF_RESET,OUTPUT);
// Reset the chip
digitalWrite(DF_RESET,LOW);
delay(1);
digitalWrite(DF_RESET,HIGH);
#endif
df_Read_END=false;
dataflash_CS_inactive(); //disable device
// Setup SPI Master, Mode 3, fosc/4 = 4MHz
SPCR = (1<<SPE)|(1<<MSTR)|(1<<CPHA)|(1<<CPOL);
// Setup SPI Master, Mode 3, 2MHz
//SPCR = (1<<SPE)|(1<<MSTR)|(1<<CPHA)|(1<<CPOL)|(1<<SPR0);
//SPSR = (1<<SPI2X);
// Cleanup registers...
tmp=SPSR;
tmp=SPDR;
}
// This function is mainly to test the device
void DataFlash_Class::ReadManufacturerID()
{
byte tmp;
dataflash_CS_inactive(); // Reset dataflash command decoder
dataflash_CS_active();
// Read manufacturer and ID command...
dataflash_SPI_transfer(DF_READ_MANUFACTURER_AND_DEVICE_ID);
df_manufacturer = dataflash_SPI_transfer(0xff);
df_device_0 = dataflash_SPI_transfer(0xff);
df_device_1 = dataflash_SPI_transfer(0xff);
tmp = dataflash_SPI_transfer(0xff);
}
// Read the status of the DataFlash
byte DataFlash_Class::ReadStatus()
{
byte tmp;
dataflash_CS_inactive(); // Reset dataflash command decoder
dataflash_CS_active();
// Read status command
dataflash_SPI_transfer(DF_STATUS_REGISTER_READ);
tmp = dataflash_SPI_transfer(0x00);
return(tmp&0x80); // We only want to extract the READY/BUSY bit
}
// Wait until DataFlash is in ready state...
void DataFlash_Class::WaitReady()
{
while(!ReadStatus());
}
void DataFlash_Class::PageToBuffer(unsigned char BufferNum, unsigned int PageAdr)
{
dataflash_CS_inactive();
dataflash_CS_active();
if (BufferNum==1)
dataflash_SPI_transfer(DF_TRANSFER_PAGE_TO_BUFFER_1);
else
dataflash_SPI_transfer(DF_TRANSFER_PAGE_TO_BUFFER_2);
dataflash_SPI_transfer((unsigned char)(PageAdr >> 6));
dataflash_SPI_transfer((unsigned char)(PageAdr << 2));
dataflash_SPI_transfer(0x00); // don´t care bytes
dataflash_CS_inactive(); //initiate the transfer
dataflash_CS_active();
while(!ReadStatus()); //monitor the status register, wait until busy-flag is high
}
void DataFlash_Class::BufferToPage (unsigned char BufferNum, unsigned int PageAdr, unsigned char wait)
{
dataflash_CS_inactive(); // Reset dataflash command decoder
dataflash_CS_active();
if (BufferNum==1)
dataflash_SPI_transfer(DF_BUFFER_1_TO_PAGE_WITH_ERASE);
else
dataflash_SPI_transfer(DF_BUFFER_2_TO_PAGE_WITH_ERASE);
dataflash_SPI_transfer((unsigned char)(PageAdr >> 6));
dataflash_SPI_transfer((unsigned char)(PageAdr << 2));
dataflash_SPI_transfer(0x00); // don´t care bytes
dataflash_CS_inactive(); //initiate the transfer
dataflash_CS_active();
// Check if we need to wait to write the buffer to memory or we can continue...
if (wait)
while(!ReadStatus()); //monitor the status register, wait until busy-flag is high
}
void DataFlash_Class::BufferWrite (unsigned char BufferNum, unsigned int IntPageAdr, unsigned char Data)
{
dataflash_CS_inactive(); // Reset dataflash command decoder
dataflash_CS_active();
if (BufferNum==1)
dataflash_SPI_transfer(DF_BUFFER_1_WRITE);
else
dataflash_SPI_transfer(DF_BUFFER_2_WRITE);
dataflash_SPI_transfer(0x00); //don't cares
dataflash_SPI_transfer((unsigned char)(IntPageAdr>>8)); //upper part of internal buffer address
dataflash_SPI_transfer((unsigned char)(IntPageAdr)); //lower part of internal buffer address
dataflash_SPI_transfer(Data); //write data byte
}
unsigned char DataFlash_Class::BufferRead (unsigned char BufferNum, unsigned int IntPageAdr)
{
byte tmp;
dataflash_CS_inactive(); // Reset dataflash command decoder
dataflash_CS_active();
if (BufferNum==1)
dataflash_SPI_transfer(DF_BUFFER_1_READ);
else
dataflash_SPI_transfer(DF_BUFFER_2_READ);
dataflash_SPI_transfer(0x00); //don't cares
dataflash_SPI_transfer((unsigned char)(IntPageAdr>>8)); //upper part of internal buffer address
dataflash_SPI_transfer((unsigned char)(IntPageAdr)); //lower part of internal buffer address
dataflash_SPI_transfer(0x00); //don't cares
tmp = dataflash_SPI_transfer(0x00); //read data byte
return (tmp);
}
// *** END OF INTERNAL FUNCTIONS ***
void DataFlash_Class::PageErase (unsigned int PageAdr)
{
dataflash_CS_inactive(); //make sure to toggle CS signal in order
dataflash_CS_active(); //to reset Dataflash command decoder
dataflash_SPI_transfer(DF_PAGE_ERASE); // Command
dataflash_SPI_transfer((unsigned char)(PageAdr >> 6)); //upper part of page address
dataflash_SPI_transfer((unsigned char)(PageAdr << 2)); //lower part of page address and MSB of int.page adr.
dataflash_SPI_transfer(0x00); // "dont cares"
dataflash_CS_inactive(); //initiate flash page erase
dataflash_CS_active();
while(!ReadStatus());
}
// *** DATAFLASH PUBLIC FUNCTIONS ***
void DataFlash_Class::StartWrite(int PageAdr)
{
df_BufferNum=1;
df_BufferIdx=0;
df_PageAdr=PageAdr;
df_Stop_Write=0;
WaitReady();
}
void DataFlash_Class::WriteByte(byte data)
{
if (!df_Stop_Write)
{
BufferWrite(df_BufferNum,df_BufferIdx,data);
df_BufferIdx++;
if (df_BufferIdx >= 512) // End of buffer?
{
df_BufferIdx=0;
BufferToPage(df_BufferNum,df_PageAdr,0); // Write Buffer to memory, NO WAIT
df_PageAdr++;
if (OVERWRITE_DATA==1)
{
if (df_PageAdr>=4096) // If we reach the end of the memory, start from the begining
df_PageAdr = 1;
}
else
{
if (df_PageAdr>=4096) // If we reach the end of the memory, stop here
df_Stop_Write=1;
}
if (df_BufferNum==1) // Change buffer to continue writing...
df_BufferNum=2;
else
df_BufferNum=1;
}
}
}
void DataFlash_Class::WriteInt(int data)
{
WriteByte(data>>8); // High byte
WriteByte(data&0xFF); // Low byte
}
void DataFlash_Class::WriteLong(long data)
{
WriteByte(data>>24); // First byte
WriteByte(data>>16);
WriteByte(data>>8);
WriteByte(data&0xFF); // Last byte
}
// Get the last page read
int DataFlash_Class::GetPage()
{
return(df_Read_PageAdr-1);
}
void DataFlash_Class::StartRead(int PageAdr)
{
df_Read_BufferNum=1;
df_Read_BufferIdx=0;
df_Read_PageAdr=PageAdr;
WaitReady();
PageToBuffer(df_Read_BufferNum,df_Read_PageAdr); // Write Memory page to buffer
df_Read_PageAdr++;
}
byte DataFlash_Class::ReadByte()
{
byte result;
WaitReady();
result = BufferRead(df_Read_BufferNum,df_Read_BufferIdx);
df_Read_BufferIdx++;
if (df_Read_BufferIdx >= 512) // End of buffer?
{
df_Read_BufferIdx=0;
PageToBuffer(df_Read_BufferNum,df_Read_PageAdr); // Write memory page to Buffer
df_Read_PageAdr++;
if (df_Read_PageAdr>=4096) // If we reach the end of the memory, start from the begining
{
df_Read_PageAdr = 0;
df_Read_END = true;
}
}
return result;
}
int DataFlash_Class::ReadInt()
{
int result;
result = ReadByte(); // High byte
result = (result<<8) | ReadByte(); // Low byte
return result;
}
long DataFlash_Class::ReadLong()
{
long result;
result = ReadByte(); // First byte
result = (result<<8) | ReadByte();
result = (result<<8) | ReadByte();
result = (result<<8) | ReadByte(); // Last byte
return result;
}
// make one instance for the user to use
DataFlash_Class DataFlash;

View File

@ -0,0 +1,85 @@
/* ************************************************************ */
/* Test for DataFlash Log library */
/* ************************************************************ */
#ifndef DataFlash_h
#define DataFlash_h
// arduino mega SPI pins
#if defined(__AVR_ATmega1280__)
#define DF_DATAOUT 51 // MOSI
#define DF_DATAIN 50 // MISO
#define DF_SPICLOCK 52 // SCK
#define DF_SLAVESELECT 53 // SS (PB0)
#define DF_RESET 31 // RESET (PC6)
#else // normal arduino SPI pins...
#define DF_DATAOUT 11 //MOSI
#define DF_DATAIN 12 //MISO
#define DF_SPICLOCK 13 //SCK
#define DF_SLAVESELECT 10 //SS
#endif
// AT45DB161D Commands (from Datasheet)
#define DF_TRANSFER_PAGE_TO_BUFFER_1 0x53
#define DF_TRANSFER_PAGE_TO_BUFFER_2 0x55
#define DF_STATUS_REGISTER_READ 0xD7
#define DF_READ_MANUFACTURER_AND_DEVICE_ID 0x9F
#define DF_PAGE_READ 0xD2
#define DF_BUFFER_1_READ 0xD4
#define DF_BUFFER_2_READ 0xD6
#define DF_BUFFER_1_WRITE 0x84
#define DF_BUFFER_2_WRITE 0x87
#define DF_BUFFER_1_TO_PAGE_WITH_ERASE 0x83
#define DF_BUFFER_2_TO_PAGE_WITH_ERASE 0x86
#define DF_PAGE_ERASE 0x81
#define DF_BLOCK_ERASE 0x50
#define DF_SECTOR_ERASE 0x7C
#define DF_CHIP_ERASE_0 0xC7
#define DF_CHIP_ERASE_1 0x94
#define DF_CHIP_ERASE_2 0x80
#define DF_CHIP_ERASE_3 0x9A
class DataFlash_Class
{
private:
// DataFlash Log variables...
unsigned char df_BufferNum;
unsigned char df_Read_BufferNum;
unsigned int df_BufferIdx;
unsigned int df_Read_BufferIdx;
unsigned int df_PageAdr;
unsigned int df_Read_PageAdr;
unsigned char df_Read_END;
unsigned char df_Stop_Write;
//Methods
unsigned char BufferRead (unsigned char BufferNum, unsigned int IntPageAdr);
void BufferWrite (unsigned char BufferNum, unsigned int IntPageAdr, unsigned char Data);
void BufferToPage (unsigned char BufferNum, unsigned int PageAdr, unsigned char wait);
void PageToBuffer(unsigned char BufferNum, unsigned int PageAdr);
void WaitReady();
unsigned char ReadStatus();
public:
unsigned char df_manufacturer;
unsigned char df_device_0;
unsigned char df_device_1;
DataFlash_Class(); // Constructor
void Init();
void ReadManufacturerID();
int GetPage();
void PageErase (unsigned int PageAdr);
// Write methods
void StartWrite(int PageAdr);
void WriteByte(unsigned char data);
void WriteInt(int data);
void WriteLong(long data);
// Read methods
void StartRead(int PageAdr);
unsigned char ReadByte();
int ReadInt();
long ReadLong();
};
extern DataFlash_Class DataFlash;
#endif

View File

@ -0,0 +1,99 @@
/*
Example of DataFlash library.
Code by Jordi Muñoz and Jose Julio. DIYDrones.com
*/
#include <DataFlash.h>
#define HEAD_BYTE1 0xA3
#define HEAD_BYTE2 0x95
void setup()
{
Serial.begin(57600);
DataFlash.Init(); // DataFlash initialization
Serial.println("Dataflash Log Test 1.0");
// Test
delay(20);
DataFlash.ReadManufacturerID();
delay(10);
Serial.print("Manufacturer:");
Serial.print(int(DataFlash.df_manufacturer));
Serial.print(",");
Serial.print(int(DataFlash.df_device_0));
Serial.print(",");
Serial.print(int(DataFlash.df_device_1));
Serial.println();
// We start to write some info (sequentialy) starting from page 1
// This is similar to what we will do...
DataFlash.StartWrite(1);
Serial.println("Writing to flash... wait...");
for (int i=0;i<1000;i++) // Write 1000 packets...
{
// We write packets of binary data... (without worry about nothing more)
DataFlash.WriteByte(HEAD_BYTE1);
DataFlash.WriteByte(HEAD_BYTE2);
DataFlash.WriteInt(2000+i);
DataFlash.WriteInt(2001+i);
DataFlash.WriteInt(2002+i);
DataFlash.WriteInt(2003+i);
DataFlash.WriteLong((long)i*5000);
DataFlash.WriteLong((long)i*16268);
DataFlash.WriteByte(0xA2); // 2 bytes of checksum (example)
DataFlash.WriteByte(0x4E);
delay(10);
}
delay(100);
}
void loop()
{
int i;
byte tmp_byte1;
byte tmp_byte2;
int tmp_int;
long tmp_long;
Serial.println("Start reading page 1...");
DataFlash.StartRead(1); // We start reading from page 1
for (i=0;i<200;i++) // Read 200 packets...
{
tmp_byte1=DataFlash.ReadByte();
tmp_byte2=DataFlash.ReadByte();
Serial.print("PACKET:");
if ((tmp_byte1==HEAD_BYTE1)&&(tmp_byte1==HEAD_BYTE1))
{
// Read 4 ints...
tmp_int=DataFlash.ReadInt();
Serial.print(tmp_int);
Serial.print(",");
tmp_int=DataFlash.ReadInt();
Serial.print(tmp_int);
Serial.print(",");
tmp_int=DataFlash.ReadInt();
Serial.print(tmp_int);
Serial.print(",");
tmp_int=DataFlash.ReadInt();
Serial.print(tmp_int);
Serial.print(",");
// Read 2 longs...
tmp_long=DataFlash.ReadLong();
Serial.print(tmp_long);
Serial.print(",");
tmp_long=DataFlash.ReadLong();
Serial.print(tmp_long);
Serial.print(";");
// Read the checksum...
tmp_byte1=DataFlash.ReadByte();
tmp_byte2=DataFlash.ReadByte();
}
Serial.println();
}
delay(10000);
}

View File

@ -0,0 +1,14 @@
DataFlash KEYWORD1
Init KEYWORD2
ReadManufacturerID KEYWORD2
GetPage KEYWORD2
PageErase KEYWORD2
StartWrite KEYWORD2
StartRead KEYWORD2
ReadByte KEYWORD2
ReadInt KEYWORD2
ReadLong KEYWORD2
WriteByte KEYWORD2
WriteInt KEYWORD2
WriteLong KEYWORD2

View File

@ -0,0 +1,272 @@
/*
GPS_NMEA.cpp - Generic NMEA GPS library for Arduino
Code by Jordi Muñoz and Jose Julio. DIYDrones.com
This code works with boards based on ATMega168/328 and ATMega1280 (Serial port 1)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GPS configuration : NMEA protocol
Baud rate : 38400
NMEA Sentences :
$GPGGA : Global Positioning System Fix Data
$GPVTG : Ttack and Ground Speed
Methods:
Init() : GPS Initialization
Read() : Call this funcion as often as you want to ensure you read the incomming gps data
Properties:
Lattitude : Lattitude * 10000000 (long value)
Longitude : Longitude * 10000000 (long value)
Altitude : Altitude * 1000 (milimeters) (long value)
Ground_speed : Speed (m/s) * 100 (long value)
Ground_course : Course (degrees) * 100 (long value)
Type : 2 (This indicate that we are using the Generic NMEA library)
NewData : 1 when a new data is received.
You need to write a 0 to NewData when you read the data
Fix : >=1: GPS FIX, 0: No Fix (normal logic)
Quality : 0 = No Fix
1 = Bad (Num sats < 5)
2 = Poor
3 = Medium
4 = Good
NOTE : This code has been tested on a Locosys 20031 GPS receiver (MTK chipset)
*/
#include "GPS_NMEA.h"
#include <avr/interrupt.h>
#include "WProgram.h"
// Constructors ////////////////////////////////////////////////////////////////
GPS_NMEA_Class::GPS_NMEA_Class()
{
}
// Public Methods //////////////////////////////////////////////////////////////
void GPS_NMEA_Class::Init(void)
{
Type = 2;
GPS_checksum_calc = false;
bufferidx = 0;
NewData=0;
Fix=0;
Quality=0;
PrintErrors=0;
// Initialize serial port
#if defined(__AVR_ATmega1280__)
Serial1.begin(38400); // Serial port 1 on ATMega1280
#else
Serial.begin(38400);
#endif
}
// This code don´t wait for data, only proccess the data available on serial port
// We can call this function on the main loop (50Hz loop)
// If we get a complete packet this function call parse_nmea_gps() to parse and update the GPS info.
void GPS_NMEA_Class::Read(void)
{
char c;
int numc;
int i;
#if defined(__AVR_ATmega1280__) // If AtMega1280 then Serial port 1...
numc = Serial1.available();
#else
numc = Serial.available();
#endif
if (numc > 0)
for (i=0;i<numc;i++){
#if defined(__AVR_ATmega1280__) // If AtMega1280 then Serial port 1...
c = Serial1.read();
#else
c = Serial.read();
#endif
if (c == '$'){ // NMEA Start
bufferidx = 0;
buffer[bufferidx++] = c;
GPS_checksum = 0;
GPS_checksum_calc = true;
continue;
}
if (c == '\r'){ // NMEA End
buffer[bufferidx++] = 0;
parse_nmea_gps();
}
else {
if (bufferidx < (GPS_BUFFERSIZE-1)){
if (c == '*')
GPS_checksum_calc = false; // Checksum calculation end
buffer[bufferidx++] = c;
if (GPS_checksum_calc)
GPS_checksum ^= c; // XOR
}
else
bufferidx=0; // Buffer overflow : restart
}
}
}
/****************************************************************
*
****************************************************************/
// Private Methods //////////////////////////////////////////////////////////////
void GPS_NMEA_Class::parse_nmea_gps(void)
{
byte NMEA_check;
long aux_deg;
long aux_min;
char *parseptr;
if (strncmp(buffer,"$GPGGA",6)==0){ // Check if sentence begins with $GPGGA
if (buffer[bufferidx-4]=='*'){ // Check for the "*" character
NMEA_check = parseHex(buffer[bufferidx-3])*16 + parseHex(buffer[bufferidx-2]); // Read the checksums characters
if (GPS_checksum == NMEA_check){ // Checksum validation
//Serial.println("buffer");
NewData = 1; // New GPS Data
parseptr = strchr(buffer, ',')+1;
//parseptr = strchr(parseptr, ',')+1;
Time = parsenumber(parseptr,2); // GPS UTC time hhmmss.ss
parseptr = strchr(parseptr, ',')+1;
//
aux_deg = parsedecimal(parseptr,2); // degrees
aux_min = parsenumber(parseptr+2,4); // minutes (sexagesimal) => Convert to decimal
Lattitude = aux_deg*10000000 + (aux_min*50)/3; // degrees + minutes/0.6 (*10000000) (0.6 = 3/5)
parseptr = strchr(parseptr, ',')+1;
//
if (*parseptr=='S')
Lattitude = -1*Lattitude; // South Lattitudes are negative
//
parseptr = strchr(parseptr, ',')+1;
// W Longitudes are Negative
aux_deg = parsedecimal(parseptr,3); // degrees
aux_min = parsenumber(parseptr+3,4); // minutes (sexagesimal)
Longitude = aux_deg*10000000 + (aux_min*50)/3; // degrees + minutes/0.6 (*10000000)
//Longitude = -1*Longitude; // This Assumes that we are in W longitudes...
parseptr = strchr(parseptr, ',')+1;
//
if (*parseptr=='W')
Longitude = -1*Longitude; // West Longitudes are negative
//
parseptr = strchr(parseptr, ',')+1;
Fix = parsedecimal(parseptr,1);
parseptr = strchr(parseptr, ',')+1;
NumSats = parsedecimal(parseptr,2);
parseptr = strchr(parseptr, ',')+1;
HDOP = parsenumber(parseptr,1); // HDOP * 10
parseptr = strchr(parseptr, ',')+1;
Altitude = parsenumber(parseptr,1)*100; // Altitude in decimeters*100 = milimeters
if (Fix < 1)
Quality = 0; // No FIX
else if(NumSats<5)
Quality = 1; // Bad (Num sats < 5)
else if(HDOP>30)
Quality = 2; // Poor (HDOP > 30)
else if(HDOP>25)
Quality = 3; // Medium (HDOP > 25)
else
Quality = 4; // Good (HDOP < 25)
}
else
{
if (PrintErrors)
Serial.println("GPSERR: Checksum error!!");
}
}
}
else if (strncmp(buffer,"$GPVTG",6)==0){ // Check if sentence begins with $GPVTG
//Serial.println(buffer);
if (buffer[bufferidx-4]=='*'){ // Check for the "*" character
NMEA_check = parseHex(buffer[bufferidx-3])*16 + parseHex(buffer[bufferidx-2]); // Read the checksums characters
if (GPS_checksum == NMEA_check){ // Checksum validation
parseptr = strchr(buffer, ',')+1;
Ground_Course = parsenumber(parseptr,2); // Ground course in degrees * 100
parseptr = strchr(parseptr, ',')+1;
parseptr = strchr(parseptr, ',')+1;
parseptr = strchr(parseptr, ',')+1;
parseptr = strchr(parseptr, ',')+1;
parseptr = strchr(parseptr, ',')+1;
parseptr = strchr(parseptr, ',')+1;
Ground_Speed = parsenumber(parseptr,2)*10/36; // Convert Km/h to m/s (*100)
//GPS_line = true;
}
else
{
if (PrintErrors)
Serial.println("GPSERR: Checksum error!!");
}
}
}
else
{
bufferidx = 0;
if (PrintErrors)
Serial.println("GPSERR: Bad sentence!!");
}
}
/****************************************************************
*
****************************************************************/
// Parse hexadecimal numbers
byte GPS_NMEA_Class::parseHex(char c) {
if (c < '0')
return (0);
if (c <= '9')
return (c - '0');
if (c < 'A')
return (0);
if (c <= 'F')
return ((c - 'A')+10);
}
// Decimal number parser
long GPS_NMEA_Class::parsedecimal(char *str,byte num_car) {
long d = 0;
byte i;
i = num_car;
while ((str[0] != 0)&&(i>0)) {
if ((str[0] > '9') || (str[0] < '0'))
return d;
d *= 10;
d += str[0] - '0';
str++;
i--;
}
return d;
}
// Function to parse fixed point numbers (numdec=number of decimals)
long GPS_NMEA_Class::parsenumber(char *str,byte numdec) {
long d = 0;
byte ndec = 0;
while (str[0] != 0) {
if (str[0] == '.'){
ndec = 1;
}
else {
if ((str[0] > '9') || (str[0] < '0'))
return d;
d *= 10;
d += str[0] - '0';
if (ndec > 0)
ndec++;
if (ndec > numdec) // we reach the number of decimals...
return d;
}
str++;
}
return d;
}
GPS_NMEA_Class GPS;

View File

@ -0,0 +1,46 @@
#ifndef GPS_NMEA_h
#define GPS_NMEA_h
#include <inttypes.h>
#define GPS_BUFFERSIZE 120
class GPS_NMEA_Class
{
private:
// Internal variables
uint8_t GPS_checksum;
uint8_t GPS_checksum_calc;
char buffer[GPS_BUFFERSIZE];
int bufferidx;
void parse_nmea_gps(void);
uint8_t parseHex(char c);
long parsedecimal(char *str,uint8_t num_car);
long parsenumber(char *str,uint8_t numdec);
public:
// Methods
GPS_NMEA_Class();
void Init();
void Read();
// Properties
long Time; //GPS Millisecond Time of Week
long Lattitude; // Geographic coordinates
long Longitude;
long Altitude;
long Ground_Speed;
long Speed_3d; //Speed (3-D)
long Ground_Course;
uint8_t Type; // Type of GPS (library used)
uint8_t NumSats; // Number of visible satelites
uint8_t Fix; // >=1:GPS FIX 0:No FIX (normal logic)
uint8_t Quality; // GPS Signal quality
uint8_t NewData; // 1:New GPS Data
uint8_t PrintErrors; // 1: To Print GPS Errors (for debug)
int HDOP; // HDOP
};
extern GPS_NMEA_Class GPS;
#endif

View File

@ -0,0 +1,42 @@
/*
Example of GPS NMEA library.
Code by Jordi Muñoz and Jose Julio. DIYDrones.com
Works with Ardupilot Mega Hardware (GPS on Serial Port1)
and with standard ATMega168 and ATMega328 on Serial Port 0
*/
#include <GPS_NMEA.h> // NMEA GPS Library
void setup()
{
Serial.begin(57600);
Serial.println("GPS NMEA library test");
GPS.Init(); // GPS Initialization
delay(1000);
}
void loop()
{
GPS.Read();
if (GPS.NewData) // New GPS data?
{
Serial.print("GPS:");
Serial.print(" Time:");
Serial.print(GPS.Time);
Serial.print(" Fix:");
Serial.print((int)GPS.Fix);
Serial.print(" Lat:");
Serial.print(GPS.Lattitude);
Serial.print(" Lon:");
Serial.print(GPS.Longitude);
Serial.print(" Alt:");
Serial.print(GPS.Altitude/1000.0);
Serial.print(" Speed:");
Serial.print(GPS.Ground_Speed/100.0);
Serial.print(" Course:");
Serial.print(GPS.Ground_Course/100.0);
Serial.println();
GPS.NewData = 0; // We have readed the data
}
delay(20);
}

View File

@ -0,0 +1,18 @@
GPS KEYWORD1
GPS_NMEA KEYWORD1
Init KEYWORD2
Read KEYWORD2
Type KEYWORD2
Time KEYWORD2
Lattitude KEYWORD2
Longitude KEYWORD2
Altitude KEYWORD2
Ground_Speed KETWORD2
Ground_Course KEYWORD2
Speed_3d KEYWORD2
NumSats KEYWORD2
Fix KEYWORD2
NewData KEYWORD2
Quality KEYWORD2

View File

@ -0,0 +1,270 @@
/*
GPS_UBLOX.cpp - Ublox GPS library for Arduino
Code by Jordi Muñoz and Jose Julio. DIYDrones.com
This code works with boards based on ATMega168/328 and ATMega1280 (Serial port 1)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GPS configuration : Ublox protocol
Baud rate : 38400
Active messages :
NAV-POSLLH Geodetic Position Solution, PAGE 66 of datasheet
NAV-VELNED Velocity Solution in NED, PAGE 71 of datasheet
NAV-STATUS Receiver Navigation Status
or
NAV-SOL Navigation Solution Information
Methods:
Init() : GPS Initialization
Read() : Call this funcion as often as you want to ensure you read the incomming gps data
Properties:
Lattitude : Lattitude * 10000000 (long value)
Longitude : Longitude * 10000000 (long value)
Altitude : Altitude * 100 (meters) (long value)
Ground_speed : Speed (m/s) * 100 (long value)
Ground_course : Course (degrees) * 100 (long value)
NewData : 1 when a new data is received.
You need to write a 0 to NewData when you read the data
Fix : 1: GPS FIX, 0: No Fix (normal logic)
*/
#include "GPS_UBLOX.h"
#include <avr/interrupt.h>
#include "WProgram.h"
// Constructors ////////////////////////////////////////////////////////////////
GPS_UBLOX_Class::GPS_UBLOX_Class()
{
}
// Public Methods //////////////////////////////////////////////////////////////
void GPS_UBLOX_Class::Init(void)
{
ck_a=0;
ck_b=0;
UBX_step=0;
NewData=0;
Fix=0;
PrintErrors=0;
GPS_timer=millis(); //Restarting timer...
// Initialize serial port
#if defined(__AVR_ATmega1280__)
Serial1.begin(38400); // Serial port 1 on ATMega1280
#else
Serial.begin(38400);
#endif
}
// optimization : This code don´t wait for data, only proccess the data available
// We can call this function on the main loop (50Hz loop)
// If we get a complete packet this function calls parse_ubx_gps() to parse and update the GPS info.
void GPS_UBLOX_Class::Read(void)
{
static unsigned long GPS_timer=0;
byte data;
int numc;
#if defined(__AVR_ATmega1280__) // If AtMega1280 then Serial port 1...
numc = Serial1.available();
#else
numc = Serial.available();
#endif
if (numc > 0)
for (int i=0;i<numc;i++) // Process bytes received
{
#if defined(__AVR_ATmega1280__)
data = Serial1.read();
#else
data = Serial.read();
#endif
switch(UBX_step) //Normally we start from zero. This is a state machine
{
case 0:
if(data==0xB5) // UBX sync char 1
UBX_step++; //OH first data packet is correct, so jump to the next step
break;
case 1:
if(data==0x62) // UBX sync char 2
UBX_step++; //ooh! The second data packet is correct, jump to the step 2
else
UBX_step=0; //Nop, is not correct so restart to step zero and try again.
break;
case 2:
UBX_class=data;
ubx_checksum(UBX_class);
UBX_step++;
break;
case 3:
UBX_id=data;
ubx_checksum(UBX_id);
UBX_step++;
break;
case 4:
UBX_payload_length_hi=data;
ubx_checksum(UBX_payload_length_hi);
UBX_step++;
// We check if the payload lenght is valid...
if (UBX_payload_length_hi>=UBX_MAXPAYLOAD)
{
if (PrintErrors)
Serial.println("ERR:GPS_BAD_PAYLOAD_LENGTH!!");
UBX_step=0; //Bad data, so restart to step zero and try again.
ck_a=0;
ck_b=0;
}
break;
case 5:
UBX_payload_length_lo=data;
ubx_checksum(UBX_payload_length_lo);
UBX_step++;
UBX_payload_counter=0;
break;
case 6: // Payload data read...
if (UBX_payload_counter < UBX_payload_length_hi) // We stay in this state until we reach the payload_length
{
UBX_buffer[UBX_payload_counter] = data;
ubx_checksum(data);
UBX_payload_counter++;
if (UBX_payload_counter==UBX_payload_length_hi)
UBX_step++;
}
break;
case 7:
UBX_ck_a=data; // First checksum byte
UBX_step++;
break;
case 8:
UBX_ck_b=data; // Second checksum byte
// We end the GPS read...
if((ck_a==UBX_ck_a)&&(ck_b==UBX_ck_b)) // Verify the received checksum with the generated checksum..
parse_ubx_gps(); // Parse the new GPS packet
else
{
if (PrintErrors)
Serial.println("ERR:GPS_CHK!!");
}
// Variable initialization
UBX_step=0;
ck_a=0;
ck_b=0;
GPS_timer=millis(); //Restarting timer...
break;
}
} // End for...
// If we don´t receive GPS packets in 2 seconds => Bad FIX state
if ((millis() - GPS_timer)>2000)
{
Fix = 0;
if (PrintErrors)
Serial.println("ERR:GPS_TIMEOUT!!");
}
}
/****************************************************************
*
****************************************************************/
// Private Methods //////////////////////////////////////////////////////////////
void GPS_UBLOX_Class::parse_ubx_gps(void)
{
int j;
//Verifing if we are in class 1, you can change this "IF" for a "Switch" in case you want to use other UBX classes..
//In this case all the message im using are in class 1, to know more about classes check PAGE 60 of DataSheet.
if(UBX_class==0x01)
{
switch(UBX_id)//Checking the UBX ID
{
case 0x02: //ID NAV-POSLLH
j=0;
Time = join_4_bytes(&UBX_buffer[j]); // ms Time of week
j+=4;
Longitude = join_4_bytes(&UBX_buffer[j]); // lon*10000000
j+=4;
Lattitude = join_4_bytes(&UBX_buffer[j]); // lat*10000000
j+=4;
//Altitude = join_4_bytes(&UBX_buffer[j]); // elipsoid heigth mm
j+=4;
Altitude = (float)join_4_bytes(&UBX_buffer[j]); // MSL heigth mm
//j+=4;
/*
hacc = (float)join_4_bytes(&UBX_buffer[j])/(float)1000;
j+=4;
vacc = (float)join_4_bytes(&UBX_buffer[j])/(float)1000;
j+=4;
*/
NewData=1;
break;
case 0x03://ID NAV-STATUS
//if(UBX_buffer[4] >= 0x03)
if((UBX_buffer[4] >= 0x03)&&(UBX_buffer[5]&0x01))
Fix=1; //valid position
else
Fix=0; //invalid position
break;
case 0x06://ID NAV-SOL
if((UBX_buffer[10] >= 0x03)&&(UBX_buffer[11]&0x01))
Fix=1; //valid position
else
Fix=0; //invalid position
UBX_ecefVZ=join_4_bytes(&UBX_buffer[36]); //Vertical Speed in cm/s
NumSats=UBX_buffer[47]; //Number of sats...
break;
case 0x12:// ID NAV-VELNED
j=16;
Speed_3d = join_4_bytes(&UBX_buffer[j]); // cm/s
j+=4;
Ground_Speed = join_4_bytes(&UBX_buffer[j]); // Ground speed 2D cm/s
j+=4;
Ground_Course = join_4_bytes(&UBX_buffer[j]); // Heading 2D deg*100000
j+=4;
/*
sacc = join_4_bytes(&UBX_buffer[j]) // Speed accuracy
j+=4;
headacc = join_4_bytes(&UBX_buffer[j]) // Heading accuracy
j+=4;
*/
break;
}
}
}
/****************************************************************
*
****************************************************************/
// Join 4 bytes into a long
long GPS_UBLOX_Class::join_4_bytes(unsigned char Buffer[])
{
union long_union {
int32_t dword;
uint8_t byte[4];
} longUnion;
longUnion.byte[0] = *Buffer;
longUnion.byte[1] = *(Buffer+1);
longUnion.byte[2] = *(Buffer+2);
longUnion.byte[3] = *(Buffer+3);
return(longUnion.dword);
}
/****************************************************************
*
****************************************************************/
// Ublox checksum algorithm
void GPS_UBLOX_Class::ubx_checksum(byte ubx_data)
{
ck_a+=ubx_data;
ck_b+=ck_a;
}
GPS_UBLOX_Class GPS;

View File

@ -0,0 +1,50 @@
#ifndef GPS_UBLOX_h
#define GPS_UBLOX_h
#include <inttypes.h>
#define UBX_MAXPAYLOAD 60
class GPS_UBLOX_Class
{
private:
// Internal variables
uint8_t ck_a; // Packet checksum
uint8_t ck_b;
uint8_t UBX_step;
uint8_t UBX_class;
uint8_t UBX_id;
uint8_t UBX_payload_length_hi;
uint8_t UBX_payload_length_lo;
uint8_t UBX_payload_counter;
uint8_t UBX_buffer[UBX_MAXPAYLOAD];
uint8_t UBX_ck_a;
uint8_t UBX_ck_b;
long GPS_timer;
long UBX_ecefVZ;
void parse_ubx_gps();
void ubx_checksum(unsigned char ubx_data);
long join_4_bytes(unsigned char Buffer[]);
public:
// Methods
GPS_UBLOX_Class();
void Init();
void Read();
// Properties
long Time; //GPS Millisecond Time of Week
long Lattitude; // Geographic coordinates
long Longitude;
long Altitude;
long Ground_Speed;
long Speed_3d; //Speed (3-D)
long Ground_Course;
uint8_t NumSats; // Number of visible satelites
uint8_t Fix; // 1:GPS FIX 0:No FIX (normal logic)
uint8_t NewData; // 1:New GPS Data
uint8_t PrintErrors; // 1: To Print GPS Errors (for debug)
};
extern GPS_UBLOX_Class GPS;
#endif

View File

@ -0,0 +1,42 @@
/*
Example of GPS UBLOX library.
Code by Jordi Muñoz and Jose Julio. DIYDrones.com
Works with Ardupilot Mega Hardware (GPS on Serial Port1)
and with standard ATMega168 and ATMega328 on Serial Port 0
*/
#include <GPS_UBLOX.h> // UBLOX GPS Library
void setup()
{
Serial.begin(57600);
Serial.println("GPS UBLOX library test");
GPS.Init(); // GPS Initialization
delay(1000);
}
void loop()
{
GPS.Read();
if (GPS.NewData) // New GPS data?
{
Serial.print("GPS:");
Serial.print(" Time:");
Serial.print(GPS.Time);
Serial.print(" Fix:");
Serial.print((int)GPS.Fix);
Serial.print(" Lat:");
Serial.print(GPS.Lattitude);
Serial.print(" Lon:");
Serial.print(GPS.Longitude);
Serial.print(" Alt:");
Serial.print(GPS.Altitude/1000.0);
Serial.print(" Speed:");
Serial.print(GPS.Ground_Speed/100.0);
Serial.print(" Course:");
Serial.print(GPS.Ground_Course/100000.0);
Serial.println();
GPS.NewData = 0; // We have readed the data
}
delay(20);
}

View File

@ -0,0 +1,16 @@
GPS KEYWORD1
GPS_UBLOX KEYWORD1
Init KEYWORD2
Read KEYWORD2
Time KEYWORD2
Lattitude KEYWORD2
Longitude KEYWORD2
Altitude KEYWORD2
Ground_Speed KETWORD2
Ground_Course KEYWORD2
Speed_3d KEYWORD2
NumSats KEYWORD2
Fix KEYWORD2
NewData KEYWORD2