如何从LIS3DH传感器计算g值?

时间:2014-03-19 05:26:26

标签: avr

我正在使用带有ATmega128的LIS3DH传感器来获取加速度值以获得运动。我浏览了数据表,但似乎不够,所以我决定在这里发布。从其他帖子我相信传感器分辨率是12位而不是16位。我需要知道,当从x轴输出寄存器中找到g值时,我们是否只在OUT_X_H(高位寄存器)的符号位MSB为1时才计算寄存器值的2'2补码,或者每次都计算位是0。

根据我的计算,我认为只有当OUT_X_H寄存器的MSB为1时才计算二进制补码。

但是数据表说我们每次都需要计算OUT_X_L和OUT_X_H的两个补码。

有人可以启发我吗?

示例代码

 int main(void)
 {      
     stdout = &uart_str;            
     UCSRB=0x18; // RXEN=1, TXEN=1 
     UCSRC=0x06; // no parit, 1-bit stop, 8-bit data
     UBRRH=0;
     UBRRL=71; // baud 9600

     timer_init();

     TWBR=216; // 400HZ
     TWSR=0x03;
     TWCR |= (1<<TWINT)|(1<<TWSTA)|(0<<TWSTO)|(1<<TWEN);//TWCR=0x04;
     printf("\r\nLIS3D address: %x\r\n",twi_master_getchar(0x0F));  
     twi_master_putchar(0x23, 0b000100000);
     printf("\r\nControl 4 register 0x23: %x", twi_master_getchar(0x23));       
     printf("\r\nStatus register %x", twi_master_getchar(0x27));
     twi_master_putchar(0x20, 0x77);

     DDRB=0xFF;
     PORTB=0xFD;
     SREG=0x80; //sei();


     while(1)
     {              
         process();
     }
 }
 void process(void){
    x_l = twi_master_getchar(0x28);
    x_h = twi_master_getchar(0x29);
    y_l = twi_master_getchar(0x2a);
    y_h = twi_master_getchar(0x2b);
    z_l = twi_master_getchar(0x2c);
    z_h = twi_master_getchar(0x2d);
    xvalue = (short int)(x_l+(x_h<<8));
    yvalue = (short int)(y_l+(y_h<<8));
    zvalue = (short int)(z_l+(z_h<<8));
    printf("\r\nx_val: %ldg", x_val);
    printf("\r\ny_val: %ldg", y_val);
    printf("\r\nz_val: %ldg", z_val);
 }

我把CTRL_REG4写成0x10(4g)但是当我读它们时我得到了0x20(8g)。这看起来有点奇怪。

4 个答案:

答案 0 :(得分:2)

不要计算2s补码。这会产生使结果为负的效果。

相反,数据表告诉我们结果已经是签名值。也就是说,0不是最低值;它处于规模中间。 (0xffff只是略小于零,不是最高值。)

此外,结果总是16位,但结果并不意味着准确。您可以将控制寄存器值设置为以牺牲电流消耗为代价生成更准确的值,但仍不能保证精确到最后一位。

答案 1 :(得分:1)

数据表没有说(至少在第8.2章中的寄存器描述)你必须计算2&#39;补充但指出2个寄存器的内容是2的补码。

所以你要做的就是接收两个字节并将其转换为int16_t以获得签名的原始值。

uint8_t xl = 0x00;
uint8_t xh = 0xFC;
int16_t x =  (int16_t)((((uint16)xh) << 8) | xl);

uint8_t xa[2] {0x00, 0xFC}; // little endian: lower byte to lower address
int16_t x = *((int16*)xa);

(希望我没有把这件事搞砸了)

答案 2 :(得分:0)

我有另一种方法,可能更容易实现,因为编译器将为您完成所有工作。编译器可能会最有效地完成它并且没有错误。

将原始数据读入原始字段:

typedef union
{
    struct
    {
        // in low power - 8 significant bits, left justified
        int16 reserved : 8;
        int16 value    : 8;
    } lowPower;

    struct
    {
        // in normal power - 10 significant bits, left justified
        int16 reserved : 6;
        int16 value    : 10;

    } normalPower;

    struct
    {
        // in high resolution - 12 significant bits, left justified
        int16 reserved : 4;
        int16 value    : 12;

    } highPower;
    // the raw data as read from registers H and L
    uint16  raw;
} LIS3DH_RAW_CONVERTER_T;

根据您使用的电源模式使用所需的值。

注意:在此示例中,位字段结构是BIG ENDIANS。 检查您是否需要颠倒&#39;值&#39;的顺序?并且&#39;保留&#39;。

答案 3 :(得分:0)

LISxDH传感器是2的补码,左对齐。它们可以设置为12位,10位或8位分辨率。从传感器读取的值是两个8位值(LSB,MSB),需要将它们组装在一起。

如果将分辨率设置为8位,则只需将LSB强制转换为int8,这很可能是处理器表示2的补码(8位)的结果。同样,如果可以将传感器设置为16位分辨率,则可以将其强制转换为int16。

但是,如果该值是左对齐的10位,则对于int16,符号位在错误的位置。这是将其转换为int16(16位2的补码)的方法。

1。从传感器读取LSB,MSB:

[MMMM MMMM] [LL00 0000]
[1001 0101] [1100 0000] //example = [0x95] [0xC0] (note that the LSB comes before MSB on the sensor)

2。组装字节,记住LSB是左对齐的。     // - -举个例子....     uint8_t byteMSB = 0x95; // [1001 0101]     uint8_t byteLSB = 0xC0; // [1100 0000]

//---Cast to U16 to make room, then combine the bytes---
assembledValue = ( (uint16_t)(byteMSB) << UINT8_LEN ) | (uint16_t)byteLSB;
/*[MMMM MMMM LL00 0000]
  [1001 0101 1100 0000] = 0x95C0 */

//---Shift to right justify---
assembledValue >>= (INT16_LEN-numBits);
/*[0000 00MM MMMM MMLL]
  [0000 0010 0101 0111] = 0x0257 */

3。从10位2的补码(现已右对齐)转换为int16(在大多数平台上,它只是16位2的补码)。

方法#1 :如果符号位(在我们的示例中为第十位)= 0,则将其强制转换为int16(因为在10位2的补码中正数表示相同)和16位2的补码)。
如果符号位= 1,则将这些位取反(仅保留10位),将结果加1,然后乘以-1(根据2的补码的定义)。

convertedValueI16 = ~assembledValue;          //invert bits
convertedValueI16 &= ( 0xFFFF>>(16-numBits) ); //but keep just the 10-bits
convertedValueI16 += 1;                       //add 1
convertedValueI16 *=-1; //multiply by -1
/*Note that the last two lines could be replaced by convertedValueI16 = ~convertedValueI16;*/
//result = -425 = 0xFE57 = [1111 1110 0101 0111]

方法#2 :将符号位(第10位)清零,然后减去范围1 << 9

的一半
//----Zero the sign bit (tenth bit)----
convertedValueI16 = (int16_t)(  assembledValue^( 0x0001<<(numBits-1) )  );  
/*Result = 87 = 0x57 [0000 0000 0101 0111]*/

//----Subtract out half the range----
convertedValueI16 -= ( (int16_t)(1)<<(numBits-1) ); 
  [0000 0000 0101 0111] 
 -[0000 0010 0000 0000]
= [1111 1110 0101 0111];
/*Result = 87 - 512 = -425 = 0xFE57

链接到脚本进行尝试(未优化):http://tpcg.io/NHmBRR