NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Everything Else » My msater thesis 'SMART ROCK' using the nerdkit MCU

March 29, 2014
by scootergarrett
scootergarrett's Avatar

OK I will try and to keep it brief and related to the Nerdkits programming and the circuit.

background:

So the United States Geological Survey (USGS) is always trying to refine their land slide/ debris flow models. Years ago they constructed this land slide emulator and for the last few years grad students at UNH have been developing and testing sensors ‘smart rocks’ that travel with the soil and measure various parameters. Early versions had an expensive IMU with the intention to determine the position as a function of time, I don’t want to get off on a tangent but this is almost impossible because of that darned gravity. So I proposed that we ditch the IMU and just measure pore water pressure (Civil engineering term don’t ask me) and three axis of accelerations with the effect of gravity causing error because the orientations of the rock unknown as it tumbles.

Overview:

I used the Nerdkits to sample the analog sensors (pressure temperature and accelerations) and store the data on an SD card so it could be retrieved later. Here is the circuit and a picture of the final populated PCB top bottom. The shell is made of an aluminum pipe with steel end plugs, the need was to match the density to soil, here are all the components the white plastic part was 3D printed and holds the circuit board snugly within the shell. Unfortunately the USGS has not used their flume since this project was complete so I have no real use data.

I don’t have the ambition to totally get into every little thing but any questions are welcome, also if you are really interested here is a link to my theses

And here is the on board code:

// Program to save measured pressure acceleration
// and temperature onto SD card in raw format
// needs to be extracted with extraction program
// for atmega328p with Nerdkits bootloader

// PIN DEFINITIONS:
// PC0 -- !Blue LED
// PD3 -- !Red LED
// PC5 -- Pressure in
// PC3 -- Z accelerations
// PC2 -- Y accelerations
// PC1 -- X accelerations
// ADC6-- Temperature
// PD2 -- Pin change interrupt to start and stop
// PD5 -- Must be set to input with no pull-up resistor
// PC4 -- Must be set to input with no pull-up resistor

#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "C:\Libraries_Mine\libnerdkits\io_328p.h"
#include "C:\Libraries_Mine\libnerdkits\delay.h"

#include "SDHC.h"

#define FirstDataSector 600304
#define RockName 'B'            // Change this when loading each rock

volatile uint8_t BufferCounter;
volatile uint16_t *BufferPointer;
volatile uint16_t *TemperatureBufferPointer;
volatile uint32_t CurrentSample;
volatile uint32_t LastSample;
volatile uint16_t SamplesSkipped;

uint16_t adc_read(uint8_t port)
{
    ADMUX = port;
    ADCSRA |= (1<<ADSC);
    while(ADCSRA&(1<<ADSC));
    return (ADCL+(ADCH<<8));
}

int main()
{
    uint16_t k;
    uint32_t Sector;                // Current SD sector
    uint16_t *BufferPointerStart;   // Points to the first buffer position

    cli();                          // make sure interrupts are off

    // initialize pins //
    DDRD &= ~(1<<PD5);              // Make PD5 an input so PB5(SCK) can run through it
    PORTD  &= ~(1<<PD5);            // Don't use pull up resistor as to not effect signal

    DDRC &= ~(1<<PC4);              // Make PC4 an input so ground can run through it
    PORTC  &= ~(1<<PC4);            // Don't use pull up resistor to save power

    DDRD |= (1<<PD3);               // Make PD0 an output for Red LED
    PORTD  |= (1<<PD3);             // Start Red off

    DDRC |= (1<<PC0);               // Make PC0 an output for Blue LED
    PORTC  |= (1<<PC0);             // Start Blue off

    // Turn off unnecessary features to save power //
    PRR = (1<<PRTWI)        // turn off TWI
         | (1<<PRTIM2)      // turn off Timer/Counter 2
         | (1<<PRTIM0)      // turn off Timer/Counter 0
         | (1<<PRUSART0);   // turn off USART

    //  Initialize variables //
    Sector = FirstDataSector;
    BufferPointerStart = (uint16_t *)&buffer[0];
    TemperatureBufferPointer = (uint16_t *)&buffer[0x1F8];      // Points to spot in buffer where Temperature will be saved each sector
    BufferPointer = BufferPointerStart;
    BufferCounter = 0;
    CurrentSample = 0;
    LastSample = 0;
    SamplesSkipped = 0;

    // Start up the Analog to Digital Converter //
    ADMUX = 1;
    ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
    ADCSRA |= (1<<ADSC);

    // Start the SDHC //
    delay_ms(100);
    spi_init();
    delay_ms(100);
    sdhc_init();
    delay_ms(100);
    if(sdhc_init())
        goto SetupError;
    delay_ms(100);

    // Clear the buffer for good measure //
    for(k=0;k<BUFFER_SIZE;++k)
        buffer[k] = 0;

    if(sdhc_write_block(3))
        goto WriteError;

    // Set up PD2 for pin change interrupts //
    DDRD &= ~(1<<PD2);
    PORTD |= (1<<PD2);
    PCICR |= (1<<PCIE2);
    PCMSK2 |= (1<<PCINT18);

    // Interrupt Timing setup //
    TCCR1B |= (1<<CS12) | (1<<CS10) | (1<<WGM12);
    TIMSK1 |= (1<<OCIE1A);
    OCR1A = 35;                // Interrupt timing 400 Hz

    BufferCounter = 0;
    BufferPointer = BufferPointerStart;
    delay_ms(500);
    PORTD  &= ~(1<<PD3);            // Red on to show ready
    while(PIND & (1<<PD2));         /// Wait until PD2 gets pulled low
    PORTD  |= (1<<PD3);             // Turn off red

    /// Clear timer and allow interrupts ///
    TCNT1 = 0;
    sei();

    /// This is that actual running part ///
    while(!LastSample)
    {
        if(BufferCounter==63)                  // Check if buf will overflow next sample
        {
            BufferCounter = 0;
            BufferPointer = BufferPointerStart;
            if(sdhc_write_block(Sector))
                goto WriteError;

            Sector++;
        }
    }
    cli();                          // Turn off interrupts

    /// Clear out the rest of the buffer and save it on the card ///
    for(k = BufferCounter * 8;k<BUFFER_SIZE;++k)
        buffer[k] = 0;
    *TemperatureBufferPointer = adc_read(6);        // Save last temperature
    sdhc_write_block(Sector);

    /// Really need this data so restart the SD card ///
    delay_ms(100);
    k=0;
    while(sdhc_init())
    {
        if(k++ > 10);
            goto SetupError;

        delay_ms(100);
    }

    delay_ms(100);
    /// Clear out buffer ///
    for(k=0;k<BUFFER_SIZE;++k)
        buffer[k] = 0;

    *((uint16_t*)&buffer[0x0]) = OCR1A;             // Put OCR1A on card
    *((uint32_t*)&buffer[0x10]) = FirstDataSector;  // Put first data sector location in buffer
    *((uint32_t*)&buffer[0x50]) = LastSample;       // Put LastSample number in buffer
    buffer[0x60] = RockName;                        // Put Rock letter on SD card
    *((uint16_t*)&buffer[0x70]) = SamplesSkipped;   // Put SamplesSkipped number in buffer
    sdhc_write_block(10);                            // Put buffer on SD Card sector 10
    delay_ms(20);
    sdhc_write_block(11);                            // Put buffer on SD Card sector 11 as well

    /// More power saving ///
    PRR = (1<<PRTWI)            // turn off TWI
         | (1<<PRTIM2)          // turn off Timer/Counter 2
         | (1<<PRTIM0)          // turn off Timer/Counter 0
         | (1<<PRTIM1)          // turn off Timer/counter 1
         | (1<<PRSPI)           // turn off SPI
         | (1<<PRUSART0)        // turn off USART
         | (1<<PRADC);          // turn off ADC

    /// Flash Blue LED to show end of run ///
    while(1)
    {
        PORTC  |= (1<<PC0);
        delay_ms(700);

        PORTC  &= ~(1<<PC0);
        delay_ms(100);
    }

    SetupError:
    while(1)
    {
        PORTD  &= ~(1<<PD3);    // Red on
        delay_ms(500);
        PORTD  |= (1<<PD3);     // Red off
        delay_ms(100);
    }

    WriteError:
    while(1)
    {
        PORTD  &= ~(1<<PD3);    // Red on
        delay_ms(100);
        PORTD  |= (1<<PD3);     // Red off
        delay_ms(500);
    }

    while(1);
    return 0;
}

/// Take measurement and store in buffer interrupt ///
ISR(TIMER1_COMPA_vect)
{
    ++CurrentSample;
    if(BufferCounter==63)
    {
        ++SamplesSkipped;
        return;
    }

    *BufferPointer = adc_read(1);       // Save X accel
    ++BufferPointer;

    *BufferPointer = adc_read(2);       // Save Y accel
    ++BufferPointer;

    *BufferPointer = adc_read(3);       // Save Z accel
    ++BufferPointer;

    if(!BufferCounter)                  // Only save Temperature the first time filling the sector
        *TemperatureBufferPointer = adc_read(6);       // Save Temperature

    *BufferPointer = adc_read(5);       // Save pressure
    ++BufferPointer;

    ++BufferCounter;
    return;
}

/// Last sample signal interrupt ///
ISR(PCINT2_vect)
{
    if(!LastSample)
        if(CurrentSample > 1500)         // Needs 1500 samples and Last sample can't already be set helps with debouncing
            LastSample = CurrentSample;

    return;
}
April 02, 2014
by Ralphxyz
Ralphxyz's Avatar

Thanks scooter, that is very interesting!

Are you from New Hampshire?

Ralph

April 03, 2014
by Rick_S
Rick_S's Avatar

Sounds like a neat project. How reliable were the mcu's at the nk crystal speed operating at 3.3v? Were these battery operated? If so, what kind of battery life did you get?

April 04, 2014
by scootergarrett
scootergarrett's Avatar

yes I'm from New Hampshire, the battery was a Duracell 28L, it is expensive battery (~7$) and I suggested they don't use it for more than 3hours. page 27 of the thesis above has a short write up. the battery life would have been much longer but writing data to the SD card cause momentary current draw and caused a dip in the voltage, I tried putting larger value capacitors before and after the voltage regulator with no luck.

April 04, 2014
by JKITSON
JKITSON's Avatar

Thanks for the write up and information. I was able to find some new ideas on programming from you code. Keep us informed. Jim

Post a Reply

Please log in to post a reply.

Did you know that 20 LEDs can be controlled from 11 microcontroller pins, to make a twinkling heart outline? Learn more...