4

STC3100バッテリーモニターICから値を読み取ろうとしていますが、取得した値が正しくありません。データシートの内容:

The temperature value is coded in 2’s complement format, and the LSB value is 0.125° C.

REG_TEMPERATURE_LOW, address 10, temperature value, bits 0-7
REG_TEMPERATURE_HIGH, address 11, temperature value, bits 8-15

これはデータシートです:http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATASHEET/CD00219947.pdf

私のコードにあるもの:

__u8 regaddr = 0x0a; /* Device register to access */
__s32 res_l, res_h;

int temp_value;
float temperature;

res_l = i2c_smbus_read_word_data(myfile, regaddr);
regaddr++;
res_h = i2c_smbus_read_word_data(myfile, regaddr);
if (res_l < 0) {
  /* ERROR HANDLING: i2c transaction failed */
} else {
  temp_value = (res_h << 8)+res_l;
  temperature = (float)temp_value * 0.125;
  printf("Temperature: %4.2f C\n", temperature);
}

私は何が間違っているのですか?これは、2の補数の値をintにコピーする方法ではありませんか?

4

4 に答える 4

6

i2c_smbus_read_word_data()デバイス上の指定したレジスタから始まる 16 ビットを読み取るため、i2c_smbus_read_word_data()単一の i2c トランザクションを使用して、関心のある両方のレジスタを読み取ります。

i2c_smbus_read_word_data()デバイスから読み取った 16 ビットを符号なしの量として返します。エラーがある場合、からの戻り値はi2c_smbus_read_word_data()負になります。次のように温度センサーを読み取ることができるはずです。

__u8 regaddr = 0x0a; /* Device register to access */
__s32 res;

int temp_value;
float temperature;

res = i2c_smbus_read_word_data(myfile, regaddr);

if (res < 0) {
  /* ERROR HANDLING: i2c transaction failed */
} else {
  temp_value = (__s16) res;
  temperature = (float)temp_value * 0.125;
  printf("Temperature: %4.2f C\n", temperature);
}

コメントからの質問に対処するには:

エラーがなければ、i2c_smbus_read_word_data()関数は i2c バスから取得した 16 ビットのデータを符号なし 16 ビット値として返します。16 ビットの unsigned 値は、関数によって返される 32 ビットの int で簡単に表すことができるため、定義により、16 ビットのデータを負にすることはできません。resエラーがある場合にのみ負になります。

16 ビット値を (負の可能性がある) 2 の補数値として解釈することは、 の(__s16)キャストによって処理されresます。これは、その値を受け取り、resそれを符号付き 16 ビットint表現に変換します。厳密に言えば、このキャストによって負の数がどのように処理されるかについては、実装によって定義されます。Linux の実装では、これは常に の下位 16 ビットを単純resに 2 の補数として扱うと思います。

キャストの実装定義の側面が心配な場合は(__s16)、cafの回答のようにキャストの代わりに算術を使用して回避できます。

temp_value = (res > 0x7fff) ? res - (0xffff + 1) : res;

たまたま1の補数のマシンで実行している場合でも、負の値への正しい変換を実行するのはどれですか(Linuxはそのようなものでの実行をサポートしていますか?)。

また、上記の投稿されたコードは、リトルエンディアン マシンで実行していることを前提としていることにも注意してください。データを負の値に変換する前に、ビッグ エンディアン マシンでバイトを適切にスワップする必要があります。ターゲット CPU は整数値 (大きい/小さい、1 つまたは 2 つ) を表します。

__u16 data = __le16_to_cpu( (__u16) res);

// convert negative two's complement values to native negative value:
int temp_value = (data > 0x7fff) ? data - (0xffff + 1) : data;
于 2012-08-12T21:23:39.810 に答える
2

あなたの投稿から i2c_smbus_read_word_data のデータ型が何であるかは明らかではありませんが、負の値を返すことが可能であれば、符号なしバイトだけではいけません。res_l & 0xff と res_h & 0xff はパラノイアの演習として使用します。これらには関心のあるものは何も含まれていないはずだからです。

于 2012-08-12T07:49:04.123 に答える
2

コードでint32 ビット型 temp_value = (res_h << 8) + res_l;の場合、連結が 16 ビットで符号ビットが拡張されないため、式は負の値に対して正しい結果を生成しません。

おそらく、暗黙的な変換を避け、何をしたいのかを正確に指定する必要があります。暗黙的な変換規則と、符号付きと符号なしの間の変換は難解であり、予期しない結果が生じる可能性があります。式を小さな部分に分割すると、どの型変換またはビット単位の操作が正しくないかを正確に確認できるため、デバッグにも役立ちます。

また、算術演算とビット演算の一貫性をお勧めします。または、どちらかを優先(a << 8) | b(a * 256) + b(a << 8) + bください。

  __u8 tlow = (__u8)(res_l & 0xff) ;
  __u8 thigh = (__u8)(res_h & 0xff) << 8 ;
  __s16 temp_value = (__s16)((thigh << 8) | tlow);

  temperature = (float)temp_value * 0.125f ;
  printf("Temperature: %4.2hf C\n", temperature);

マスキングとキャストを明示的に行ったり、変数を追加した場合のように分解したりする必要はまったくありませんが、混合型式で発生する暗黙の変換の複雑な詳細を知る必要がなくなり、非常に便利になります。何をしようとしているのかを読者とコンパイラに明確にします。また、デバッガーでこれらの中間値を監視できるため、デバッグが簡単になります (デバッガーを使用していますよね!?)。

簡潔にしたい場合はtemp_value__s16または式を にキャストするだけで元のコードを修正できます__s16が、これはすでにあなたをつまずかせているため、お勧めしません。またはこのコードを再利用してください。それにもかかわらず、次のいずれかが機能します。

__s16 temp_value = (res_h << 8) | res_l ;

また

int temp_value = (__s16)((res_h << 8) | res_l);

最後のものには、少なくとも結果が に含まれintています。これは、あなたが要求したものであり、その後実行される算術演算に関しておそらくより安全です。

__s16本当に にキャストしてからに代入するつもりだったことを示したい場合はint、明示的にします。

int temp_value = (int)((__s16)((res_h << 8) | res_l));

不運なメンテナが後でそれをエラーだと思い、「修正」しようとするかもしれないからです!

于 2012-08-12T10:19:29.970 に答える
1

上位ビットを正しく処理する必要があります。これを行う最も簡単な方法は次のとおりです。

s32 temp_value = (res_h << 8) | res_l;
if (temp_value > 32767)
    temp_value -= 65536;

res_hトランザクションが失敗したかどうかも確認することを忘れないでください。

于 2012-08-12T10:36:58.150 に答える