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 制御シーケンス
システム起動後、以下の順序で処理が進行します。
- 周辺機能(I2C, UART, PWM, LCD)の初期化およびセンサのオフセット補正。
- ユーザ入力待機。開始ボタン押下で計測フェーズへ移行。
- ポンプ駆動によりカフ内圧を目標値(約180mmHg)まで上昇。
- 電磁弁を制御し、3〜5mmHg/秒の勾配で減圧を開始。
- 減圧中に100Hz間隔で圧力データを取得し、RAMバッファに蓄積。
- 取得波形から収縮期血圧・拡張期血圧・心拍数を抽出し、表示更新。
- 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');
}