0
\$\begingroup\$

I've been trying to make a simple prototype capable of measuring a punch or a shot put throw with an accelerometer, but suddenly stopped working, and "locked" into a value, has I was testing it.
When I was testing it, even tho I tried to make the code The most efficient possible to maximize the measurement frequency, sometimes I would get irregular values (low accuracy).
The way I tested my prototype was dropping it into a hard surface. The components I used where:

  1. Microcontroller (msp430g2553)
  2. Display (QDSP 6064)
  3. accelerometer (ADXL377):
  4. - range: +/- 200g
    - frequency response: 1000Hz
    - absolute maximum g: 10000g

The code I used is the following (sorry, it's in portuguese):

#include <msp430.h>
#include <math.h>
#include <stdio.h>

#define CAT_1 BIT3
#define CAT_2 BIT4
#define CAT_3 BIT5
#define CAT_4 BIT6

#define SEG_A BIT0
#define SEG_B BIT1
#define SEG_C BIT2
#define SEG_D BIT3
#define SEG_E BIT4
#define SEG_F BIT5
#define SEG_G BIT6
#define SEG_DP BIT7

#define EIXO_X BIT0
#define EIXO_Y BIT1
#define EIXO_Z BIT2

#define BOTAO BIT7

unsigned int adc[3];
unsigned int eixo_x = 0;
unsigned int eixo_y = 0;
unsigned int eixo_z = 0;
unsigned int maior = 0;
unsigned int soma = 0;

float gx;
float gy;
float gz;

float gxy;
float gxyz;
int total;

char total_string[3];

int flag_display = 0;

const int zero_g = 512;
const float escala = 2.6165;

int i = 0;                                                              // tempo de atraso

void escolher_digito(int digito);
void escolher_numero(char numero);
void digito_um(void);
void digito_dois(void);
void digito_tres(void);
void digito_quatro(void);
void zero(void);
void um(void);
void dois(void);
void tres(void);
void quatro(void);
void cinco(void);
void seis(void);
void sete(void);
void oito(void);
void nove(void);
void vazio(void);
void atraso(void);

void main(void) {

    WDTCTL = WDTPW | WDTHOLD;

    P1REN |= BOTAO;                                                     // Ativa resistância interna para os modos "pull-up"/"pull-down"
    P1IES |= BOTAO;                                                     // Selecionar botão de alto para baixo (modo "pull-up") (alto -> baixo, 1 -> 0)
    P1IFG &= ~BOTAO;                                                    // Limpar a flag BOTAO antes de permitir interrupções
                                                                        // Desta forma previne-se uma possível interrupção imediata
    P1IE |= BOTAO;                                                      // Permitir interupções em BOTAO

    _enable_interrupt();                                                // Permitir interrupções


    P2SEL &= ~(SEG_G | SEG_DP);                                         // Seleciona modo I/O para P1.6 e P1.7 (por defeito estão configurados para o cristal do relógio externo)
    P1OUT |= CAT_1 | CAT_2 | CAT_3 | CAT_4;
    P2OUT = 0;
    P1DIR |= CAT_1 | CAT_2 | CAT_3 | CAT_4;
    P2DIR |= SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G | SEG_DP;

    BCSCTL1 = CALBC1_16MHZ;
    DCOCTL = CALDCO_16MHZ;

    ADC10CTL1 = INCH_2 + CONSEQ_1;                                      // Canal máximo: A2, modo de conversão: sequência de canais
    ADC10CTL0 = ADC10SHT_1 + MSC + ADC10ON;                             // Tempo de amostra: 8 x ADC10CLK                           ????????? alterar?
                                                                        // Multiplas amostras e conversão (apenas válido para modo de sequência ou repetição)
                                                                        // ADC10 ligado

    ADC10DTC1 = 0x03;                                                   // Transferência de dados -> numero de transferências por bloco: 3
    ADC10AE0 |= 0x07;                                                   // Ligar entradas analógicas A2, A1 e A0

    for(;;) {

        // Modo de observação (quando o display se encontra desligado) (ciclo infinito)
        if(!flag_display) {
            ADC10CTL0 &= ~ENC;                                              // Desligar conversão
            while (ADC10CTL1 & BUSY);                                       // Esperar que ADC10 fique ativo

            ADC10SA = (unsigned int)adc;                                    // Copia dados em ADC10SA para o array adc
            ADC10CTL0 |= ENC + ADC10SC;                                     // Iniciar conversão de amostra

            //__bis_SR_register(CPUOFF + GIE);        // Modo de poupança de energia LPM0 (CPU e MCLK) desativados
                                                    // Permitir interrupções

            soma = adc[2] + adc[1] + adc[0];

            if(soma > maior) {
                maior = soma;
                eixo_x = adc[2];
                eixo_y = adc[1];
                eixo_z = adc[0];
            }
        }

        // Modo de display (ciclo infinito)
        if(flag_display) {

            escolher_digito(2);
            escolher_numero(total_string[2]);
            atraso();
            escolher_digito(3);
            escolher_numero(total_string[1]);
            atraso();
            escolher_digito(4);
            escolher_numero(total_string[0]);
            atraso();

            escolher_digito(1);
            escolher_numero(' ');
            atraso();

        }

    }   // ciclo infinito
}   // main

// Rotina de serviço de interrupção
#pragma vector = PORT1_VECTOR
__interrupt void P1_ISR(void) {

    atraso();

    switch(P1IFG & BOTAO) {         // Se a flag de interrupção for ativada
                                    // xxxx1xxx & 00001000 = 00001000
                                    // xxxx1xxx & 00001000 = 00000000
    case BOTAO:
        P1IFG &= ~BOTAO;            // Fazer reset à flag de interrupção

        // Ciclo único

        // Se o display não estiver ativo, fazer os cálculos para determinar a soma vetorial
        if(!flag_display) {

            // valor em Gs de cada eixo
            gx = (eixo_x - zero_g) / escala;
            gy = (eixo_y - zero_g) / escala;
            gz = (eixo_z - zero_g) / escala;

            // soma vetoral dos eixos
            gxy = sqrt((gx*gx) + (gy*gy));
            gxyz = sqrt((gxy*gxy) + (gz*gz));
            total = gxyz - 1;

            // Conversão de inteiro para string da soma vetorial
            sprintf(total_string, "%d", total);

        }

        // Se o display estiver ativo, desligar display (ao calcar em BOTAO)
        if(flag_display) {
            // desligar display
            //P1OUT |= CAT_1 | CAT_2 | CAT_3 | CAT_4;
            P2OUT = 0;
        }

        // Ligar/desligar display
        flag_display = !flag_display;

        return;
    default:
        P1IFG = 0;                  // Caso ocorra outra interrupção em P1, limpar a flag
                                    // Provavelmente desnecessário, mas uma boa prática
        return;
    }   // switch
}   // interrupção para P1


void escolher_digito(int digito) {
  switch(digito) {
    case 1: digito_um(); break;
    case 2: digito_dois(); break;
    case 3: digito_tres(); break;
    case 4: digito_quatro(); break;
  }
}

void escolher_numero(char numero) {
  switch(numero) {
    case '1': um(); break;
    case '2': dois(); break;
    case '3': tres(); break;
    case '4': quatro(); break;
    case '5': cinco(); break;
    case '6': seis(); break;
    case '7': sete(); break;
    case '8': oito(); break;
    case '9': nove(); break;
    case '0': zero(); break;
    case ' ': vazio(); break;
  }
}

void digito_um(void) {
    P1OUT |= CAT_1 | CAT_2 | CAT_3;
    P1OUT &= ~CAT_4;
}

void digito_dois(void) {
    P1OUT |= CAT_1 | CAT_2 | CAT_4;
    P1OUT &= ~CAT_3;
}

void digito_tres(void) {
    P1OUT |= CAT_1 | CAT_3 | CAT_4;
    P1OUT &= ~CAT_2;
}

void digito_quatro(void) {
    P1OUT |= CAT_2 | CAT_3 | CAT_4;
    P1OUT &= ~CAT_1;
}

void zero(void) {
    P2OUT &= ~(SEG_G | SEG_DP);
    P2OUT |= SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F;
}

void um(void) {
    P2OUT &= ~(SEG_A | SEG_D | SEG_E | SEG_F | SEG_G | SEG_DP);
    P2OUT |= SEG_B | SEG_C;
}

void dois(void) {
    P2OUT &= ~(SEG_C | SEG_F| SEG_DP);
    P2OUT |= SEG_A | SEG_B | SEG_D | SEG_E | SEG_G;
}

void tres(void) {
    P2OUT &= ~(SEG_E | SEG_F | SEG_DP);
    P2OUT |= SEG_A | SEG_B | SEG_C | SEG_D | SEG_G;
}

void quatro(void) {
    P2OUT &= ~(SEG_A | SEG_D | SEG_E | SEG_DP);
    P2OUT |= SEG_B | SEG_C | SEG_F | SEG_G;
}

void cinco(void) {
    P2OUT &= ~(SEG_B | SEG_E | SEG_DP);
    P2OUT |= SEG_A | SEG_C | SEG_D | SEG_F | SEG_G;
}

void seis(void) {
    P2OUT &= ~(SEG_B | SEG_DP);
    P2OUT |= SEG_A | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G;
}

void sete(void) {
    P2OUT &= ~(SEG_D | SEG_E | SEG_F | SEG_G | SEG_DP);
    P2OUT |= SEG_A | SEG_B | SEG_C;
}

void oito(void) {
    P2OUT &= ~SEG_DP;
    P2OUT |= SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G;
}

void nove(void) {
    P2OUT &= ~(SEG_D | SEG_E | SEG_DP);
    P2OUT |= SEG_A | SEG_B | SEG_C | SEG_F | SEG_G;
}

void vazio(void) {
    P2OUT = 0;
}

void atraso(void) {
    for(i = 0; i < 0xFFF; i++) {
    }
}   // atraso

The design is pretty straightforward, here's a foto: enter image description here

I'm afraid I broke the acceleration sensor or the microcontroller, but it could also be because its physically impossible to measure this types of accelerations due to unwanted noise from vibration or even amateurish coding. Is it even possible to measure these physical properties in, for example, a shot put throw?

\$\endgroup\$
  • 1
    \$\begingroup\$ of course it's possible! The device could perhaps max out it's reading during very high G acceleration though. You could measure the start and end of a shotput throw to estimate the velocity of the end point at the point the ball is released \$\endgroup\$ – KyranF Oct 15 '14 at 20:02
  • \$\begingroup\$ If you're going to test an accelerometer response to likely to give you grief. \$\endgroup\$ – Brian Drummond Oct 15 '14 at 20:48
  • \$\begingroup\$ Are you really dropping this solderless breadboard for your tests? I would think it is highly likely wire(s) could temporarily break contact causing a problem. If the hardware design is pretty stable, I would move it over to a protoboard like this one. \$\endgroup\$ – tcrosley Oct 15 '14 at 22:02
  • \$\begingroup\$ to tcrosley, yes I did. But I think I broke something because I have the display locked in a value even if I reset the microcontroller or remove the power source and turn it back on. My gess is that although it could handle the dropping acceleration, the Gs generated by the vibration of the impact damaged the sensor or the uC. \$\endgroup\$ – Rui Lima Oct 15 '14 at 22:13
3
\$\begingroup\$

Dropping a rigid object onto a hard surface produces accelerations in the thousands to tens of thousands of Gs, and you probably exceeded the 10 kG shock rating of your sensor.

There are better ways to test and/or calibrate accelerometers. You can start by turning it to different orientations and making sure you can measure gravity in both directions on every axis. You can put it on a turntable and measure the radial acceleration at various rotational speeds. Or you can get time on a calibrated "shaker table".

\$\endgroup\$
  • \$\begingroup\$ So, basically, I cannot use an accelerometer against any surface because the vibration would damage the sensor or produce so much noise it would make the measurement inaccurate. Is that it? \$\endgroup\$ – Rui Lima Oct 17 '14 at 7:56
  • 1
    \$\begingroup\$ You need to mount the sensor so that it is protected against shocks, usually achieved by using some sort of elastomer (rubber) in the mounting arrangement. The accuracy for the +/- 200 g range is affected minimally, but very large accelerations are attenuated. You mentioned measuring punches and shot puts, neither of which involves rigid objects striking hard surfaces, so you're probably OK anyway. \$\endgroup\$ – Dave Tweed Oct 17 '14 at 10:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.