I2C温度センサーで室温計測

最終回 PCが直接I2Cを話す

モジュールとしては秋月の

というものを使います。チップはFT232HLですが、これを使った他のモジュールに比べて安価です。

Adafruit製の FT232H(FT232HL)ブレイクアウトボードは、パソコンのUSBポートから直接
 I2C、SPI、UART、GPIO などの通信を行えるようにする便利な変換モジュールです。マイコン
(Arduinoなど)を仲介させず、PCのPythonから直接センサーやOLEDディスプレイを操作できま
す。

がGemini君の紹介ですが、その通りです。が、ここではいつものようにC++ Builder CEから使います。そのための記事です。MSVCからならそのまま使えますが、C++ Builder CEから使う場合は、少しだけ手間が必要です。さて、必要なファイルをまずダウンロードします。ファイル名では、

この二つです。なるべくlatestなものをgetしてください。まんまファイル名で検索すれば見つかりそうです。ちなみに上がFT232H(L)のチップドライバで、下がlibmpsse関係のファイルです。あ、MPSSEというのは、Gemini君曰く:

MPSSEとは「Multi-Protocol Synchronous Serial Engine」の略で、FTDI社のUSBシリアル変換IC(FT2232HやFT232Hなど)に搭載されているハードウェア機能です。パソコンのUSBを介して、I2CやSPI、JTAGなどの同期式シリアル通信を簡単かつ高速に行うことができます。

その機能をCやC++から呼ぶためのライブラリが、LibMPSSEです。上記ファイルをダウンロードしたら、インストールします。まずドライバから、zipを解凍して、

これを起動。

進めて、

ここまでいっておけ。Bus DriverとVCP Driverの二つが入りました。libmpsse云々zipも解凍します。

ADT7410の温度計測結果を繰り返し得るプログラムをbuildします。まずは、ソースですね。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "ftd2xx.h"
#include "libMPSSE_i2c.h"

#define ADT7410_ADDRESS 0x48  // 7ビットI2Cアドレス (libmpsse内部で自動シフトされます)

int main(void) {
    FT_HANDLE ftHandle;
    FT_STATUS status;
    ChannelConfig channelConf;
    //uint32_t channels;
    DWORD channels;
    uint8_t buffer[4];
    DWORD bytesTransferred;

    // 1. libmpsse の初期化
    Init_libMPSSE();

    status = I2C_GetNumChannels(&channels);
    if (status != FT_OK || channels == 0) {
        printf("エラー: 利用可能なFTDIデバイスが見つかりません。\n");
        return -1;
    }

    // 2. 最初のチャンネル(0)を開く
    status = I2C_OpenChannel(0, &ftHandle);
    if (status != FT_OK) {
        printf("エラー: チャンネルを開けませんでした。\n");
        return -1;
    }

    // 3. I2C通信速度などの設定 (400kHzに設定)
    channelConf.ClockRate = I2C_CLOCK_STANDARD_MODE; // もしくは 400000 (FAST_MODE)
    channelConf.LatencyTimer = 255;
    channelConf.Options = 0x03;
    status = I2C_InitChannel(ftHandle, &channelConf);
    if (status != FT_OK) {
        printf("エラー: チャンネル初期化失敗。\n");
        I2C_CloseChannel(ftHandle);
        return -1;
    }

    // 4. ADT7410 を 16ビット分解能モードに設定する (レジスタ 0x03 に 0x80 を書き込み)
    buffer[0] = 0x03; // 設定レジスタアドレス
    buffer[1] = 0x80; // 16bit mode bit (Bit7 = 1)
    status = I2C_DeviceWrite(ftHandle, ADT7410_ADDRESS, 2, buffer, &bytesTransferred,
                             I2C_TRANSFER_OPTIONS_START_BIT | I2C_TRANSFER_OPTIONS_STOP_BIT);
    if (status != FT_OK) {
        printf("警告: 16bitモードへの設定に失敗しました。デフォルトの13bitで動作します。\n");
    }

    printf("ADT7410 から温度データの読み込みを開始します...\n");

    while (1) {
        // 5. ポインタレジスタを 0x00 (温度データ) に指定する
        buffer[0] = 0x00;
        I2C_DeviceWrite(ftHandle, ADT7410_ADDRESS, 1, buffer, &bytesTransferred, 
                         I2C_TRANSFER_OPTIONS_START_BIT | I2C_TRANSFER_OPTIONS_STOP_BIT);

        // 6. 温度データ (2バイト) を読み出す
        // ※libmpsseのI2C_DeviceReadは内部で「スタート -> リード -> ストップ」の一連を処理
        status = I2C_DeviceRead(ftHandle, ADT7410_ADDRESS, 2, buffer, &bytesTransferred, 
                                I2C_TRANSFER_OPTIONS_START_BIT | I2C_TRANSFER_OPTIONS_STOP_BIT);

        if (status == FT_OK && bytesTransferred == 2) {
            // 上位・下位バイトを16ビット値に結合
            int16_t raw_temp = (int16_t)((buffer[0] << 8) | buffer[1]);
            double temperature = 0.0;

            // 16ビット分解能の場合の計算方法
            // 最上位ビット(MSB)は符号ビット、1 LSB = 0.0078度
            temperature = (double)raw_temp / 128.0;

            /*
            int16_t raw_temp = (int16_t)((buffer[0] << 8) | buffer[1]);
            double temperature = 0.0;
            // ※もし13ビットモード(デフォルト)のままで読み出す場合の計算方法はこちら
            raw_temp = raw_temp >> 3; // 下位3ビットはフラグ等のためシフト
            if (raw_temp & 0x1000) {  // 13ビットの符号判定
                raw_temp -= 8192;
            }
            temperature = (double)raw_temp * 0.0625;
            */


            printf("現在の温度: %.4f ℃\n", temperature);
        } else {
            printf("データ読み込みエラー\n");
        }

        #ifdef _WIN32
        Sleep(1000); // 1秒待機
        #else
        sleep(1);
        #endif
    }

    // 7. クローズ処理
    I2C_CloseChannel(ftHandle);
    Cleanup_libMPSSE();
    return 0;
}

Gemini君が考えましたが、親切なコメントも日本語で入っているので、なかなかよくやっています。後で判明しますけど、これでちゃんと動きます。手順は、

  • 1.libmpsse の初期化
  • 2.最初のチャンネル(0)を開く
  • 3.I2C通信速度などの設定 (400kHzに設定)
  • 4.ADT7410 を 16ビット分解能モードに設定する (レジスタ 0x03 に 0x80 を書き込み)
    • 5.ポインタレジスタを 0x00 (温度データ) に指定する
    • 6.温度データ (2バイト) を読み出す
  • 7. クローズ処理

5-6間をループします。さて、これをbuildしますが、IDEからcompile and link (要するにbuild)してもいいですが、bcc32cを使ってみましょう。コンパイルの前に、dllファイルからインポートライブラリを作ります。libmpsseを解凍したフォルダー以下の

ここで

右クリックからの、“Open Git Bash here”を選択。以下のコマンドを発行。

$ implib -a bcclibmpsse.lib libmpsse.dll

bcclibmpsse.libが出来ました。同様に64bitのディレクトリでは、

ここでBash起動して、

$ mkexp libmpsse.a libmpsse.dll

64bit版のインポートライブラリは、libmpsse.aとしました。名前は任意ですが、拡張子は.aにするのが吉です。さて、いよいよbuildですが、以下のようなディレクトリを用意します。

buildに必要なものは、ソースプログラムとインポートライブラリです。dllは実行時に必要です。さて、includeディレクトリを把握しておきましょうかね。この環境では、

のようにディレクトリincludeを選択したまま”右クリック”。

のように”パスのコピー”を選択。内容をメモしておくと吉です。この場合は、

"C:\Users\docna\Downloads\libmpsse-windows-1.0.9 (1)\release\include"

ですが、もちろん環境で変わります。用意したディレクトリで右クリック->Bashを起動します。

bcc32cを起動しますけど、コマンドラインは、

 bcc32c -I "C:\Users\docna\Downloads\libmpsse-windows-1.0.9 (1)\release\include" gemini-adt7410.cpp -l bcclibmpsse.lib

エラーがでなければおけです。-I 以下でインクリードディレトリを指定して、-l 以下でインポートライブラリを指定しています。そのままbashから起動してみましょう。

$ ./gemini-adt7410
ADT7410 から温度データの読み込みを開始します…
現在の温度: 30.6406 ℃
現在の温度: 30.6484 ℃
現在の温度: 30.6406 ℃
現在の温度: 30.5859 ℃

bashからの起動なので、./を頭に付けます。powershellと同じかな。動いているようですね。同様にbcc64で64bit版のプログラムもできます。実際の回路(懲りずにブレッドボード)の様子は、

この程度のラフさでも動作しています。タイミングクリティカルではないためでしょう。このようにGit Bashからもbuild and runつまり、edit compile then run edit againのサイクルが可能ですからこれはこれで開発環境としては気持ちよいですが、プログラムが期待通りに動かない場合は、Debug作業が必要になりますから、IDEの出番となります。

コメント