/// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- /* AP_ADC_ADS7844.cpp - ADC ADS7844 Library for Ardupilot Mega Code by Jordi Mu�oz and Jose Julio. DIYDrones.com Modified by John Ihlein 6 / 19 / 2010 to: 1)Prevent overflow of adc_counter when more than 8 samples collected between reads. Probably only an issue on initial read of ADC at program start. 2)Reorder analog read order as follows: p, q, r, ax, ay, az 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 1kHz (Timer2 overflow interrupt) So if our loop is at 50Hz, our needed sampling freq should be 100Hz, so we have an 10x oversampling and averaging. Methods: Init() : Initialization of interrupts an Timers (Timer2 overflow interrupt) Ch(ch_num) : Return the ADC channel value // HJI - Input definitions. USB connector assumed to be on the left, Rx and servo // connector pins to the rear. IMU shield components facing up. These are board // referenced sensor inputs, not device referenced. On Ardupilot Mega Hardware, oriented as described above: Chennel 0 : yaw rate, r Channel 1 : roll rate, p Channel 2 : pitch rate, q Channel 3 : x / y gyro temperature Channel 4 : x acceleration, aX Channel 5 : y acceleration, aY Channel 6 : z acceleration, aZ Channel 7 : Differential pressure sensor port */ extern "C" { // AVR LibC Includes #include #include #include #include "WConstants.h" } #include "AP_ADC_ADS7844.h" // Commands for reading ADC channels on ADS7844 static const unsigned char adc_cmd[9] = { 0x87, 0xC7, 0x97, 0xD7, 0xA7, 0xE7, 0xB7, 0xF7, 0x00 }; // the sum of the values since last read static volatile uint32_t _sum[8]; // how many values we've accumulated since last read static volatile uint16_t _count[8]; // a mask of what channels are actually being read. If a channel has // never been read, then don't bother gathering it. That saves us a // lot of cycles in the timer call static uint8_t enable_mask; static uint32_t last_ch6_micros; // TCNT2 values for various interrupt rates, // assuming 256 prescaler. Note that these values // assume a zero-time ISR. The actual rate will be a // bit lower than this #define TCNT2_781_HZ (256-80) #define TCNT2_1008_HZ (256-62) #define TCNT2_1302_HZ (256-48) static inline unsigned char ADC_SPI_transfer(unsigned char data) { /* 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; } void AP_ADC_ADS7844::read(void) { uint8_t ch; unsigned char enable_cmd[9]; uint8_t num_enabled = 0; if (enable_mask == 0) { // no channels to read return; } for (ch = 0; ch < 8; ch++) { if (enable_mask & (1<> 3); } bit_set(PORTC, 4); // Disable Chip Select (PIN PC4) } // Constructors //////////////////////////////////////////////////////////////// AP_ADC_ADS7844::AP_ADC_ADS7844() : _filter_index_accel(0), filter_result(false) { } // Public Methods ////////////////////////////////////////////////////////////// void AP_ADC_ADS7844::Init( AP_PeriodicProcess * scheduler ) { 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 // get an initial value for each channel. This ensures // _count[] is never zero for (uint8_t i=0; i<8; i++) { uint16_t adc_tmp; adc_tmp = ADC_SPI_transfer(0) << 8; adc_tmp |= ADC_SPI_transfer(adc_cmd[i + 1]); _count[i] = 1; _sum[i] = adc_tmp; } last_ch6_micros = micros(); scheduler->register_process( AP_ADC_ADS7844::read ); } // Read one channel value float AP_ADC_ADS7844::Ch(uint8_t ch_num) { uint16_t count; uint32_t sum; enable_mask |= (1<> 1; // remember the filtered value _prev_gyro[i] = result[i]; } // Accel filter for (i = 0; i < 3; i++) { // move most recent result into filter _filter_accel[i][_filter_index_accel] = result[i+3]; // clear the sum _sum_accel = 0; // sum the filter for (uint8_t n = 0; n < ADC_ACCEL_FILTER_SIZE; n++) { _sum_accel += _filter_accel[i][n]; } // filter does a moving average on last 8 reads, sums half with half of last filtered value // save old result _prev_accel[i] = result[i+3] = (_sum_accel >> 4) + (_prev_accel[i] >> 1); // divide by 16, divide by 2 } // increment filter index _filter_index_accel++; // loop our filter if(_filter_index_accel == ADC_ACCEL_FILTER_SIZE) _filter_index_accel = 0; } // return number of microseconds since last call uint32_t us = micros(); uint32_t ret = us - last_ch6_micros; last_ch6_micros = us; return ret; }