ルネサス製マイコンを活用した電子血圧計の回路構成とファームウェア実装

1. システムアーキテクチャ

本システムは、カフ内圧の取得から表示・無線送信までを統合する構成を採用しています。信号チェーンは以下のモジュールで構成され、各ブロック間で電力線および信号線が明確に分離されています。

  • 圧力検出部:Honeywell ABPDANN005PG2A3を使用。静圧と拍動波(パルス波)の合成信号を12bit I2Cデジタル出力で提供。
  • 信号処理・制御コア:ルネサス RL78/H1Dをメインプロセッサに配置。内蔵の24bit ΔΣ ADC、PGA、LCDコントローラを統合利用し、オシロメトリックアルゴリズムを実行。
  • 流体制御部:MITSUMI製マイクロポンプ(MAP-AM-265)とYujin電磁弁(KSV05B)により、カフの加圧・減圧を制御。
  • データリンク:RL78/G1Dモジュール経由でBLE 4.0接続を確立し、スマートデバイスへ計測値を中継。
  • 電源系:単4電池4直列(公称6V)または外部ACアダプタをXC6206-3.3V LDOで降圧。低ノイズ駆動を実現。

2. ハードウェア回路の実装詳細

2.1 デジタルセンサインタフェース

圧力センサはI2Cバス上で直接マイコンと通信するため、外部ADCは不要です。SCL/SDAラインには10kΩのプルアップ抵抗を挿入し、バス安定化を図ります。割り込みピン(INT)はファームウェアのポーリング周期に同期してトリガ設定を行う構成とし、データ取得レイテンシを最小化します。

2.2 内蔵AFEとアナログ信号処理

RL78/H1Dに搭載されたアナログフロントエンドを活用し、外部部品点数を削減しています。

  • 増幅ステージ:内蔵PGAのゲインを100倍に設定。mVオーダーの微弱な拍動信号をADCのフルスケール範囲に適合させます。
  • ノイズ除去:ハードウェアフィルタに加え、ファームウェア内で5Hz遮断周波数のIIRデジタルフィルタを適用。電源リップルや体動由来の高周波成分を抑制。
  • 変換設定:サンプリングレート100Hz、24bit ΔΣ構成にて連続変換を実施。実効分解能は約18bitを維持。

2.3 駆動回路と電源分離

モータおよびソレノイドは起動電流が大きく、ノイズ発生源となるためMCU電源系から独立した6Vレールで駆動します。ポンプ制御にはPチャネルMOSFET(IRLML6402)を採用し、NPNダーリントントランジスタ(DTC114)でゲートをプル/ディスチャージします。PWM周波数は1kHzに固定し、デューティ比の動的変更により加圧速度を調整します。電磁弁駆動には簡易GPIO直結のNPNスイッチ回路を配置し、排気時の応答性を確保します。

2.4 通信インタフェース

BLEモジュールとはUART(TX: P3.0, RX: P3.1)でクロス接続し、9600bpsでATコマンドベースの制御を行います。モジュールの論理レベルは3.3V系であるため、レベル変換は不要です。

2.5 電源ノイズ対策

LDOの入出力端子には0.1μFセラミックコンデンサを近接配置し、インピーダンスマッチングを行います。外部電源接続部にはTVSダイオード(SMBJ36A)を並列設置し、サージ電圧からの保護回路を構成しています。バッテリー駆動時は自己放電率の低いニッケル水素電池を採用し、連続稼働30日以上の設計目標を満たします。

3. 計測アルゴリズムと通信プロトコル

3.1 制御シーケンス

システム起動後、以下の順序で処理が進行します。

  1. 周辺機能(I2C, UART, PWM, LCD)の初期化およびセンサのオフセット補正。
  2. ユーザ入力待機。開始ボタン押下で計測フェーズへ移行。
  3. ポンプ駆動によりカフ内圧を目標値(約180mmHg)まで上昇。
  4. 電磁弁を制御し、3〜5mmHg/秒の勾配で減圧を開始。
  5. 減圧中に100Hz間隔で圧力データを取得し、RAMバッファに蓄積。
  6. 取得波形から収縮期血圧・拡張期血圧・心拍数を抽出し、表示更新。
  7. BLE経由で計測結果を外部デバイスへ送信後、待機状態へ復帰。

3.2 オシロメトリック法による血圧抽出

得られた生データには拍動成分とカフの静圧が重畳しているため、包絡線(エンベロープ)の形状解析を用いて基準点を特定します。移動平均フィルタで滑らかにした後、傾き変化率および振幅低下率からSP・DPを導出します。

/* 計測結果のデータ構造 */
typedef struct {
    float sp_val;      /* 収縮期血圧 mmHg */
    float dp_val;      /* 拡張期血圧 mmHg */
    uint8_t heart_rate; /* 心拍数 bpm */
    uint8_t status_code; /* エラー/正常フラグ */
} BpResult_t;

/* オシロメトリック演算コア関数 */
BpResult_t compute_oscillometric_bp(const float *raw_stream, uint16_t buf_len) {
    BpResult_t result = {0.0f, 0.0f, 0, 0};
    if (buf_len < 20) return result; /* バッファ不足チェック */

    /* 1. 包絡線生成(スライディングウィンドウ処理) */
    float envelope[buf_len];
    float peak_val = -1.0f;
    uint16_t peak_idx = 0;

    for (uint16_t i = 0; i < buf_len; ++i) {
        float win_sum = 0.0f;
        uint16_t cnt = 0;
        for (int w = -4; w <= 4; ++w) {
            int idx = i + w;
            if (idx >= 0 && idx < buf_len) {
                win_sum += raw_stream[idx];
                ++cnt;
            }
        }
        envelope[i] = win_sum / (float)cnt;
        if (envelope[i] > peak_val) {
            peak_val = envelope[i];
            peak_idx = i;
        }
    }

    /* 2. MAPの特定(包絡線最大点) */
    float map_pressure = envelope[peak_idx];
    uint16_t sp_pos = 0;
    uint16_t dp_pos = 0;

    /* 3. 収縮圧算出(左側勾配変動点探索) */
    float min_gradient_var = 1e5f;
    for (uint16_t k = 0; k < peak_idx; ++k) {
        float local_slope = (envelope[peak_idx] - envelope[k]) / (float)(peak_idx - k);
        float variance = 0.0f;
        for (uint16_t m = k; m < peak_idx; ++m) {
            float step_slope = envelope[m+1] - envelope[m];
            float diff = step_slope - local_slope;
            variance += (diff < 0 ? -diff : diff);
        }
        variance /= (float)(peak_idx - k);
        if (variance < min_gradient_var) {
            min_gradient_var = variance;
            sp_pos = k;
        }
    }
    result.sp_val = envelope[sp_pos];

    /* 4. 拡張圧算出(右側振幅減衰率最大化点) */
    float max_drop_ratio = 0.0f;
    for (uint16_t n = peak_idx; n < buf_len - 1; ++n) {
        float drop = envelope[n] - envelope[n+1];
        float ratio = drop / envelope[n];
        if (ratio > max_drop_ratio) {
            max_drop_ratio = ratio;
            dp_pos = n;
        }
    }
    result.dp_val = envelope[dp_pos];

    /* 5. 心拍数推定(ピークカウント法) */
    uint16_t beats = 0;
    for (uint16_t p = 1; p < buf_len - 1; ++p) {
        if (envelope[p] > envelope[p-1] && envelope[p] > envelope[p+1]) {
            beats++;
        }
    }
    result.heart_rate = (uint8_t)((beats * 6000) / buf_len); /* 100Hz基準換算 */
    result.status_code = 1;
    return result;
}

3.3 BLE GATTプロファイルを用いたデータ送信

計測完了後、IEEE 11073-20601準拠のバイナリ形式にパックし、ATコマンドシーケンスでBLEモジュールに渡します。データ整合性向上のため、送信前に簡易チェックサムを付加します。

/* 送信フレーム構築関数 */
static uint8_t build_ble_payload(const BpResult_t *bp_data, uint8_t *out_buf) {
    out_buf[0] = 0x00; /* フラグ: タイムスタンプ無効 */
    /* 収縮期血圧 (整数部/小数部へ分割) */
    out_buf[1] = (uint8_t)(bp_data->sp_val / 10.0f);
    out_buf[2] = (uint8_t)((bp_data->sp_val - (float)out_buf[1] * 10.0f) * 10.0f);
    /* 拡張期血圧 (整数部/小数部へ分割) */
    out_buf[3] = (uint8_t)(bp_data->dp_val / 10.0f);
    out_buf[4] = (uint8_t)((bp_data->dp_val - (float)out_buf[3] * 10.0f) * 10.0f);
    /* 心拍数 (整数部/小数部へ分割) */
    uint16_t hr_fixed = bp_data->heart_rate * 10;
    out_buf[5] = (uint8_t)(hr_fixed / 10);
    out_buf[6] = (uint8_t)(hr_fixed % 10);
    out_buf[7] = 0x00; /* 予約領域 */
    return 8; /* ペイロード長 */
}

/* モジュール送信ドライバ */
void transmit_via_gatt(const BpResult_t *measurement) {
    uint8_t packet[8];
    build_ble_payload(measurement, packet);

    /* UART初期化確認(実装省略) */
    uart_configure_baud(9600);
    uart_send_str("AT+INIT\r\n");
    while (!uart_match_response("OK"));

    /* 特性値定義 */
    uart_send_str("AT+GATTADDCHAR=0x180D,0x2A37,0x10,0x01\r\n");
    while (!uart_match_response("OK"));

    /* データ書き込みコマンド構築 */
    uart_send_str("AT+CHARWRITE=0x2A37,");
    for (int i = 0; i < 8; ++i) {
        uart_send_hex(packet[i]);
        uart_send_char(i == 7 ? '\r' : ',');
    }
    uart_send_char('\n');
}

タグ: ルネサスRL78 電子血圧計 オシロメトリック法 BLE4.0 組み込みC

8月3日 08:48 投稿