-
Olás.
Estou aqui com um pequeno problema de entendimento com o acelerometro.
Muito provavelmente não devo ter começado com o melhor dos integrados para uma "primeira vez"... mas "a cavalo dado...".
Na internet o geral dos problemas que vejo é malta que não consegue ligar o módulo ou que está com problemas na comunicação, etc...
No meu caso, esta parte foi ultrapassada e deixo em baixo uns pequenos excertos do código.
A comunicação foi ultrapassada, a inicialização também e a leitura dos registos com os dados também.
O meu problema (e parece que mais ninguém tem esse problema porque não encontro muita informação sobre isso) é o que fazer com os valores que recebo, até chegar à aceleração em m/s^2.
Estou a tentar fazer trabalhar um eixo apenas (neste caso o X) e quando lhe mexo há alteração dos valores recebidos.
As curiosidades são:
* só tenho variação dos valores quando empurro na direcção do eixo e não ao contrário.
* essa variação faz-se quase sempre pela alteração de 2 valores, ou seja, recebo 2 bytes (High and Low) com 192 e quando empurro recebo 2 bytes a 0.
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fi473.photobucket.com%2Falbums%2Frr100%2Fdaviddmmartins%2FCapture_zps019813d4.png&hash=13138f67501be78d3522e14c223ef7bac25246bc)
* segundo o datasheet, "X-axis acceleration data. The value is expressed in 2’s complement.", mas não vi nenhum exemplo de código onde realmente tenham feito o complemento do valor recebido.
A pergunta é, quais os passos a seguir para conseguir obter um valor válido de aceleração?
O código em baixo não está muito aprimorado, pois foi feito só mesmo para tentar obter alguns valores de teste.
Alguns #defines
#define write_LSM303_MAG 0x3C
#define write_LSM303_ACC 0x32
#define read_LSM303_MAG 0x3D
#define read_LSM303_ACC 0x33
/* Registers */
#define CTRL_REG1_A 0x20
#define CTRL_REG4_A 0x23
#define CRA_REG_M 0x00 //temperatura e rate data out
#define CRB_REG_M 0x01
#define MR_REG_M 0x02
#define OUT_X_H_M 0x03
#define OUT_X_L_A 0x28
#define TEMP_OUT_H_M 0x31
Inicialização do LSM303
void init_LSM303(void)
{
Start_I2C();
Write_I2C(write_LSM303_ACC);
Write_I2C(CTRL_REG1_A);
Write_I2C(0x27);
Stop_I2C();
Start_I2C();
Write_I2C(write_LSM303_ACC);
Write_I2C(CTRL_REG4_A);
Write_I2C(0x40);
Stop_I2C();
Start_I2C();
Write_I2C(write_LSM303_MAG);
Write_I2C(CRA_REG_M);
Write_I2C(0x14);
Stop_I2C();
Start_I2C();
Write_I2C(write_LSM303_MAG);
Write_I2C(CRB_REG_M);
Write_I2C(0b01100000);
Stop_I2C();
Start_I2C();
Write_I2C(write_LSM303_MAG);
Write_I2C(MR_REG_M);
Write_I2C(0x00);
Stop_I2C();
}
Pedido de valores
unsigned char M[6];
signed int X;
... ... ...
Open_I2C(MASTER, SLEW_OFF);
init_LSM303();
... ... ...
Start_I2C();
Write_I2C(write_LSM303_ACC);
Write_I2C(OUT_X_L_A);
Restart_I2C();
Write_I2C(read_LSM303_ACC);
M[0] = Read_I2C();
Ack_I2C();
M[1] = Read_I2C();
Ack_I2C();
M[2] = Read_I2C();
Ack_I2C();
M[3] = Read_I2C();
Ack_I2C();
M[4] = Read_I2C();
Ack_I2C();
M[5] = Read_I2C();
NotAck_I2C();
Stop_I2C();
X = (M[0]<<8) + M[1];
-
De quantos bits é que são os dados? 12, 13bit?
O facto dos dados de saida estarem representados em complemento para dois significa que tens um numero inteiro com sinal em que o bit mais significativo é 1 se for um numero negativo e 0 caso contrario. Como os dados têm 12 (ou 13 bit, confirma na datasheet) e as variaveis que usas no código C têm 16 ou 32 bit (numero suficiente de bits para guardares os dados do acelerometro), e para continuar a ser um número negativo tens de fazer extensão de sinal.
Este é o unico "truque" que precisas para obter valores correctos.
Em relação a converteres para m/s² tens de ver qual a escala que estás a usar (+-2g? 4g?) e fazeres uma regra de três simples para saberes a que corresponde o valor que lês do acelerómetro (valor máximo lido 2^12-1, caso os dados sejam de 12bit, confirma, corresponde à escala usada, 2g por exemplo)
http://en.wikipedia.org/wiki/Two%27s_complement (http://en.wikipedia.org/wiki/Two%27s_complement)
http://en.wikipedia.org/wiki/Sign_extension (http://en.wikipedia.org/wiki/Sign_extension)
http://en.wikipedia.org/wiki/Gravity_of_Earth (http://en.wikipedia.org/wiki/Gravity_of_Earth)
-
É só atirar-lhe com a biblioteca da adafruit para cima.
Dada a audiência do arduino, 99% dos problemas é não meterem pull ups nas linhas do i2c..
https://github.com/adafruit/Adafruit_LSM303
-
Segundo o datasheet a saída é de 16bits - "? 16 bit data output".
No entanto também está escrito que para o ganho com FS a 00, corresponde 1mg/LSB, por isso para os +-2g dá 12 bits...
Vou dar uma vista de olhos pela biblioteca da adafruit e ver o que difere do que eu tenho na minha.
Qualquer dia ainda perco a cabeça e compro um arduino eheh ;D
-
Na biblioteca da adafruits, ele também nada fazem com o complemento para 2...
Ou seja, o valor tem de ser analisado depois para saber se deve ou não fazer o complemento.
uint8_t xlo = Wire.read();
uint8_t xhi = Wire.read();
uint8_t ylo = Wire.read();
uint8_t yhi = Wire.read();
uint8_t zlo = Wire.read();
uint8_t zhi = Wire.read();
// Shift values to create properly formed integer (low byte first)
accelData.x = (xlo | (xhi << 8)) >> 4;
accelData.y = (ylo | (yhi << 8)) >> 4;
accelData.z = (zlo | (zhi << 8)) >> 4;
-
Há uns tempos fiz um driver para este chip, vê lá se ajuda:
void LSM303DLHC_readOutputs(uint8_t sel, int16_t *x, int16_t *y, int16_t *z)
{
uint8_t *msb, *lsb, v1, v2;
uint8_t sad, sad_w, sad_r, sub;
if(x == 0 || y == 0 || z == 0)
return;
if(sel == LSM303DLHC_ACC) {
msb = &v2;
lsb = &v1;
sub = LSM303DLHC_OUT_X_L_A;
}
else if(sel == LSM303DLHC_MAG) {
msb = &v1;
lsb = &v2;
sub = LSM303DLHC_OUT_X_H_M;
}
else
return;
sad = sel;
sad_w = (sad << 1) & 0xFE;
sad_r = (sad << 1) | 0x01;
hal_i2c_start();
hal_i2c_write(sad_w);
hal_i2c_idle(); // SAK
hal_i2c_write(sub | 0x80); // SUB, read multiple bytes
hal_i2c_idle(); // SAK
hal_i2c_restart();
hal_i2c_write(sad_r);
hal_i2c_idle(); // SAK
v1 = hal_i2c_read(); // Read X Acc (X Mag)
hal_i2c_ack();
v2 = hal_i2c_read();
hal_i2c_ack();
*x = (*msb << 8) | *lsb;
v1 = hal_i2c_read(); // Read Y Acc (Z Mag)
hal_i2c_ack();
v2 = hal_i2c_read();
hal_i2c_ack();
if(sel == LSM303DLHC_ACC)
*y = (*msb << 8) | *lsb;
else
*z = (*msb << 8) | *lsb;
v1 = hal_i2c_read(); // Read Z Acc (Y Mag)
hal_i2c_ack();
v2 = hal_i2c_read();
hal_i2c_nack();
if(sel == LSM303DLHC_ACC)
*z = (*msb << 8) | *lsb;
else
*y = (*msb << 8) | *lsb;
hal_i2c_stop();
}
edit: como os dados de saída sao de 16bit não precisas de fazer extensão de sinal caso uses variaveis do tipo int16_t
-
Olá.
Estive fora estes dias e cheguei hoje.
Em relação ao código que apresentas, msr, é precisamente o que estou a fazer.
*
* faço start i2c
* escrevo para o ACC
* indico o endereço de leitura
* faço restart i2c
* coloco o ACC para leitura
* vou ler os 6 bytes seguidos do eixo X, Y, Z, sempre com ACK entre leituras
* faço NACK
* stop i2c
* e por fim : X = (M[0] | (M[1] << 8 ));
O problema é que X varia, quando lhe mexo, de 64524 e 0....
???
-
ahahahaha
Já descobri o meu erro!
Estava a fazer múltipla leitura, mas não tinha colocado o MSB a "1", o que fazia com que apenas fizesse uma leitura...
Write_I2C(OUT_X_L_A | 0x80);
Agora já tenho muitos valores eheheheh
Vamos manter este tópico em aberto, porque acho que mais dúvidas irão surgir :)
Obrigado.
-
Olá novamente.
Estou a ter alguma dificuldade em perceber se devo ou não fazer algum tipo de calibração/ajuste ao acelerometro, pois estou a receber os dados em baixo (para o eixo X, OUT_X_L_A/OUT_X_H_A) sem qualquer tipo de vibração.
Tenho lido que é feita uma calibração para o magnetometro mas nada encontro para o acelerometro...
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fi473.photobucket.com%2Falbums%2Frr100%2Fdaviddmmartins%2FCapture_zpscf64562c.png&hash=dace018364d81952bbe8fcb644820b23416f4039)
Há sem dúvida algo enorme que me continua a escapar.... o datasheet do IC parece-me algo muito mal explicado.
Já pensei se até não haverá um documento para toda a "familia" LSM303, mas nada encontro também no site da ST...
Alguma sugestão?
-
Força da gravidade?
-
Isto é o que é mostrado quando oponho o eixo X à gravidade, ou seja, quando coloco a pastilha na vertical.
No geral aparece "16xxx", ao contrário dos "00xxx" da imagem anterior. Posso supor que aquele "1" será de 1g...
Se virar a placa no sentido contrário e colocar a favor do eixo, tenho as mesmas medições, mas com sinal negativo.
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fi473.photobucket.com%2Falbums%2Frr100%2Fdaviddmmartins%2FCapture_zpsa78917fd.png&hash=135c8f248f47361fa6a7cb77411ecf62f37f14cf)
Mas, segundo o datasheet, eu deveria medir 0.001g por cada 1LSB, ou seja, com 1g, eu deveria medir 1000. Certo?
Mas eu estou a ter medidas significativas das dezenas de milhar.
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fi473.photobucket.com%2Falbums%2Frr100%2Fdaviddmmartins%2FCapture_zps87037fd2.png&hash=e8909cb7396e9970ae5a995a904f4bef61c77428)
-
Lido assim por alto, entendi que os valor do FS defíne o valor "full scale".
Com FS a 00, full scale são 2g.
Em 16 bits signed, a fullscale de [32767, -32768] equivale a [+2g,-2g]
Fazendo um 3 simples com o valor mais usual quando estavas na vertical (16128):
aceleração=2g/32767*16128=0.984g , está lá perto, a diferença para 1g pode ser da precisão do alinhamento.
Agora reparei que os teus valores "saltam" em passos de 256 (-512,-768,-1024), o que quer dizer que não estás a receber os 8 bits LSB, ou vem a zero (talvez por causa do HR do CTRL_REG4_A??? não sei, a datasheet é pouco clara do que faz)
edit:
Mesmo sem o HR, devias estar a receber 10 bits de resolução (granularidade 2^(16-10)=64: saltar 64 entre medidas), mas estas a saltar 256. Estar a perder o byte LSB na leitura.
Tirado desta informação: http://www.pololu.com/product/1268 (http://www.pololu.com/product/1268)
The gyro, accelerometer, and magnetometer all output readings in a 16-bit format (obtained by combining the values in two 8-bit registers for each axis), but only the gyro readings contain 16 bits of precision. The accelerometer and magnetometer readings contain a maximum of 12 bits of precision; for the accelerometer, at least the lowest 4 bits of the output values are always 0, and for the magnetometer, the highest 4 bits of the output values are always 0.
The accelerometer gives low-resolution 10-bit readings by default (the lowest 6 bits of the output are always 0). To get the full 12-bit resolution, you must set the HR (high resolution) bit in the CTRL_REG4_A register.
-
Ah... muito bem visto!
É daquelas coisas que, ler até li, mas não entendi muito bem o que queriam dizer com aquilo...
Mas poderá estar aí a justificação para que o meu byte menos significativo venha a zeros...
Outra coisa interessante que estive aqui a ver é que olhando para a resolução (1mg/LSB), +-2g ocupam 12bits (4g->4000). Se pensar (imaginar) que os bits vêm alinhados à esquerda e fizer 4 deslocamentos à direita (16bits-12bits = 4), talvez obtenha um valor mais próximo.
Fiz este exercício para +-2g e obtive leituras de 1008 (16128>>4 = 1008). Ora fazendo para +-4g, eu teria de obter metade desse valor (perto de 500). Fiz as leituras e obtive 8192, que deslocado 4 vezes para a direita dá os tais 512, perto do valor 500 como era previsível.
O que acham?
O datasheet é muito vago infelizmente....
Vou agora alterar o bit do HR.... sem dúvida muito bem observado jm_araujo ;).
-
Aparentemente não tenho alterações no CTRL_REG4_A, a escrever 0x88.
Deveria "activar" o HR, mas continuo com a mesma "escala de saltos".
-
Acho extremamente estranho que não tenhas um byte inteiro de dados, ou estás a fazer algo errado na comunicação, ou estás a deitar o byte fora quando fazes a junção dos valores.
-
Mas assim eu teria de estar a fazer algo byte não, byte sim...
De momento estou a ler os eixos todos e obtenho valores nos restantes, sempre com o byte menos significativo a zeros...
Start_I2C();
Write_I2C(write_LSM303_ACC);
Write_I2C(OUT_X_L_A | 0x80);
Restart_I2C();
Write_I2C(read_LSM303_ACC);
M[0] = Read_I2C();
Ack_I2C();
M[1] = Read_I2C();
Ack_I2C();
M[2] = Read_I2C();
Ack_I2C();
M[3] = Read_I2C();
Ack_I2C();
M[4] = Read_I2C();
Ack_I2C();
M[5] = Read_I2C();
NotAck_I2C();
Stop_I2C();
X = ((M[1] << 8 ) | M[0])>>4;
sprintf(S, "Leitura X %.5d \n" , X);
putrs_USART(S); // send x
Y = ((M[3] << 8 ) | M[2])>>4;
sprintf(S, "Leitura Y %.5d \n" , Y);
putrs_USART(S); // send x
Z = ((M[5] << 8 ) | M[4])>>4;
sprintf(S, "Leitura Z %.5d \n" , Z);
putrs_USART(S); // send x
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fi473.photobucket.com%2Falbums%2Frr100%2Fdaviddmmartins%2FCapture_zps7f5a2da4.png&hash=7767fa9a34586edeab0ae5716e01567153be4be9)
-
Deixo-te o meu driver completo: https://www.dropbox.com/sh/7zy1ux5s0hkg158/AADCS8RSlb-qVFzP7qhfVPEga (https://www.dropbox.com/sh/7zy1ux5s0hkg158/AADCS8RSlb-qVFzP7qhfVPEga)
Vê lá se ajuda.
Inclui uma função de teste que faz print de todos os registos e depois começa a ler os valores.
-
Encontrei isto do datasheet de um acelerometro da mesma família, o LSM303DLH.
DR bits, in the normal-mode operation, select the data rate at which acceleration samples
are produced. In low-power mode they define the output data resolution. Table 21 shows all
the possible configurations for the DR1 and DR0 bits.
O meu CTRL_REG1_A, estava a 0x3F. O que fazia activar o Low Power Mode. Por algum motivo, no meio de tantas alterações, modifiquei isto também. Nas minhas folhas tinha 0x37... por isso já foi alteração à posteriori :(...
Os valores em baixo são extraídos antes de fazer as tais 4 deslocações à direita (para ter mais resolução).
Agora já recebo alterações no byte menos significativo...
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fi473.photobucket.com%2Falbums%2Frr100%2Fdaviddmmartins%2FCapture_zpsd45a71e5.png&hash=051c7b91317b31ccd6cde377d2f57e5b634c6e55)
-
Continua a falhar alguma coisa, nem um numero impar, todos pares, não achas isso, digamos que estranho?
-
Facto foi só falta de pontaria :)
No anterior é normal não aparecerem números ímpares, se a minha teoria se verificar aquilo estava justificado à esquerda.
Aqui já fiz os 4 deslocamentos e já aparecem números mais "confiáveis" ;).
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fi473.photobucket.com%2Falbums%2Frr100%2Fdaviddmmartins%2FCapture_zps9174eaba.png&hash=6df54f02b853a0295c1ed482aad18fcd89cbcee5)
-
Agora sim, já tens valores mais bonitos ;D
Senso, não tinha números ímpares porque são valores de 12 bits alinhados à esquerda num inteiro de 16 por causa do sinal. Os 4 lsb são sempre 0.
-
O datasheet do LSM303DLHC.... bem.... what piece of crap!
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fi475.photobucket.com%2Falbums%2Frr118%2Ftwizted_mindz_myspace%2FHeadshot_by_maulwurf08.jpg&hash=24e16dcc8ae7ce271d4e682a059185e5c1de77b8)