PicoPSU 120WI-25Vの効率を測定してみた

 

 

 

大変おひさしぶりのまつけん@TechBlogです。偶然ネタができたのでエントリーを。この後もエントリーが続くかどうかは謎です。

実は私が測定したわけではないのですが、PicoPSUは実際どれくらいの効率で各電圧の電流を抜くことができるのかというのを検証してみました。

そもそもということで簡単にだけ紹介をしておくと、PicoPSUというのは、マザーボードのATX電源入力に挿すことのできる小さな電源基板で、12Vや24Vの単一電圧を入れれば、ATX電源の各電圧を生成してくれるというものです。(いわゆるDC-DCですね。)すごく小型でマザーボードに直接挿せるような形状をしているので、よくACアダプタと組み合わせて小さなPCを作る場合なんかに使われているようです。

http://www.mini-box.com/s.nl/sc.8/category.13/.f

上記サイトを参照していただくとイメージは沸きやすいと思います。実際の入手はサイトで直接海外から入手するか、秋葉原であればいくつかの店舗で取り扱われているようです。(高いけど。。。)

Cerevoではサーバーの電源を小型化するためにこのパーツを使っています。(当然すべてのサーバーではないですが。)AC-DCは以前どこかで紹介したことがある気がしますが、TDKラムダのHWSシリーズと組み合わせて利用しています。実際、2年以上運用されているサーバーもあり、ざっくりとした感覚的な話ですが、故障率もそれほど高くありません。

前置きはこれくらいで、実際の計測結果です。

今回利用したのは、WideInputタイプで12V〜25Vの入力を受けるタイプです。12V単一入力のほうがお安く、よく使われているような気がしますが、そちらでの測定結果は今回のものと違うかもしれません。

前提

3.3V 5V 12Vの6Aの出力が可能なメインの3系統に関して1系統ずつ電流の引き抜きテストを実施。
入力はpicoPSUの直近の電圧が12V〜24Vとなるよう入力電圧を調整する。
出力の電圧と電流、入力の電流を計測する。
各系統を均一の電流量を引き抜いた場合での効率で計算を行う。
あくまで、picoPSUのDC-DCの効率を見るもので、実際の使用やACからの効率を、調査しているものではない。

結果
12V入力時
20Wから60Wの間で、90%前後の効率となった。
24V入力時
20W時77%、60W時86%と低い値が出たが、100Wから120Wにおいて、90%程度の効率となった。

問題点
12V入力時に出力が不安定になる、下記内容。
5V系統において、3.8Aを超える引き抜きで、出力を得られなかった。
よって60W時での計測になった。
12V系統において、引き抜くにつれて、電圧が降下し高出力時に11V程度まで低下した
24Vは、高出力時でも、問題無く、出力が得られた。

 

実際の各電圧の効率

 

まとめ

というわけで、確かにPicoPSUは、使い方を間違えなければ、90%以上の効率を得られることがわかりました。

12Vで使う場合は、5V系統であまり大きな電流を引き抜けない、電圧降下がかなり大きいなどの問題もあって、大きな負荷をかけないのがよさそうです。HDD等をあまりつながないようにして、別電源で駆動させるなどしつつ、Atomなどを利用するようなあまり消費電力が増えない状態が維持されるようなパターンの場合は12Vでも高効率で使えそうです。

逆にある程度以上の負荷をかける場合は24Vを入力すれば、100W以上を使うような場合でも90%程度の効率を得られるようです。ただ、逆に、上記のようにAtomなどのCPUを載せて実際の消費電力が60Wを切るような低負荷で使う場合には24V入力を使ってしまうと低い効率になってしまうことに注意が必要です。100-120Wくらいを推移するようなシステムの場合はなにも考えずに24Vで利用するのがよさそうです。

実際のところでは、この前段のAC-DCでの効率もあるのでもっと効率は落ちることにはなりそうですが、PicoPSUは24V入力で利用するとかなり高効率で安定して利用できると思われます。

私は自宅サーバーでも、DQ67OW + Core i5-2400S + DDR-3 16GB という構成をPicoPSUで駆動させて簡単な自作ケースにいれて駆動させていたりします。実際かなりコンパクトでいい感じです。

というわけで、今回はPicoPSUの効率を検証してみたという話題でした。

次はきっとWebチームの彼が更新してくれると信じています。

 

GIOChannelの使い方

こんにちは、稲垣@CEREVOです。今回はGTK+に関する (正確にはGLibに関することなのですが……) 話題で、GIOChannelの使い方を見てみたいと思います。

なぜGIOChannelを使うのか

GTK+でGUIアプリケーションを書くと、プログラムは基本的にイベントドリブンになります。つまり、メインループの中でイベントが起きるのを黙って待って、何か起きたらコールバックの中で処理します。こうしたフレームワークではファイルやソケットの読み書きでブロックされる(待ちが発生する) のは嬉しくありません。ブロックされている間は基本的に他の処理ができず、たとえば処理中のアニメーションが止まったりします。GIOchannelを使えば、ブロックされない状態になってから処理を開始することができます。

なお、今回はあまり関係ありませんが、テキストのエンコーディングを適当にUTF-8に変換してくれる機能もあります。

GIOChannelの使い方

  • g_io_channel_unix_newで生成 (unix系のシステムを想定しています……)
  • NONBLOCKに設定する (設定しないとG_IO_STATUS_AGAINが返るかわりにブロックされます)
  • g_io_add_watchでイベントソースをデフォルトメインループに追加
    (頻繁に掛け外しをする場合は、g_io_create_watchで作ったイベントソースを自分で扱った方がいいかも知れません)
  • コールバック
    • どんなイベントが起きたのか、GIOConditionを見て判断する
    • GIOChannelを読み書きしてG_IO_STATUS_NORMALやG_IO_STATUS_AGAINが返ってきたらTRUEを返す
      G_IO_STATUS_EOFやG_IO_STATUS_ERRORが返ってきたらFALSEを返してイベントソースを外す

読み込みサンプル

標準入力から読み込んでg_messageでメッセージを表示するサンプルです:


#include <glib.h>
static gboolean read_callback(GIOChannel *io, GIOCondition cond, gpointer user_data) {
  GMainLoop *loop = user_data;
  gboolean continue_to_watch = FALSE;
  if (cond & G_IO_IN) {
    GError *e = NULL;
    char *text;
    switch (g_io_channel_read_line(io, &text, NULL, NULL, &e)) {
    case G_IO_STATUS_NORMAL:
      g_message("%s: read line: %s", __func__, text);
      g_free(text);
      continue_to_watch = TRUE;
      break;
    case G_IO_STATUS_AGAIN:
      g_message("%s: AGAIN", __func__);
      continue_to_watch = TRUE;
      break;
    case G_IO_STATUS_ERROR:
      g_message("%s: error: %s", __func__, e->message);
      g_error_free(e);
      break;
    case G_IO_STATUS_EOF:
      g_message("%s: EOF", __func__);
      break;
    default:
      break;
    }
  }
  if (! continue_to_watch) {
    g_main_loop_quit(loop);
    g_main_loop_unref(loop);
  }
  return continue_to_watch;
}

static void sample_loop(int fd, GIOCondition cond, GIOFunc callback) {
  GMainLoop *loop = g_main_loop_new(NULL, FALSE);
  GIOChannel *io = g_io_channel_unix_new(fd);
  guint tag = g_io_add_watch(io, cond, callback, g_main_loop_ref(loop));
  g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
  g_io_channel_set_close_on_unref(io, TRUE);
  g_io_channel_set_encoding(io, NULL, NULL);
  g_main_loop_run(loop);
  g_io_channel_unref(io);
  g_main_loop_unref(loop);
}

int main(int argc, char *argv[]) {
  sample_loop(0, G_IO_IN, read_callback);
  return 0;
}

G_IO_INは読み込み可能を示すフラグです (読み込み専用のfdについてはこのフラグしか立たないようです――man poll参照)。ただし、読み込み可能と言っても、読んでみたらすぐにG_IO_STATUS_EOFが返ってくることもあります。EOFが返ってきたらもうチャンネルに用はないのでFALSEを返してイベントソースを外します。

書き込みサンプル

標準出力 (のバッファ) が書き込み可能になるのを待って書き込みまくるサンプルです。なおsample_loop関数は前節の関数をそのままコピーして使ってください。実行すると大量のメッセージが出力されますから、

./sample | read i

などとして、パイプが適当に閉じられるようにして実行してください:


#include <glib.h>
static gboolean write_callback(GIOChannel *io, GIOCondition cond, gpointer user_data) {
  GMainLoop *loop = user_data;
  gboolean continue_to_watch = FALSE;
  if (cond & (G_IO_ERR | G_IO_HUP))
    g_message("%s: channel is closed", __func__);
  else if (cond & G_IO_OUT) {
    GError *e = NULL;
    char *text = "BABEL\n";
    int len;
    switch (g_io_channel_write_chars(io, text, -1, &len, &e)) {
    case G_IO_STATUS_NORMAL:
      g_message("%s: wrote %d chars", __func__, len);
      continue_to_watch = TRUE;
      break;
    case G_IO_STATUS_AGAIN:
      g_message("%s: wrote %d chars, AGAIN", __func__, len);
      continue_to_watch = TRUE;
      break;
    case G_IO_STATUS_ERROR:
      g_message("%s: error: %s", __func__, e->message);
      g_error_free(e);
      break;
    case G_IO_STATUS_EOF:
      g_message("%s: EOF", __func__);
      break;
    default:
      break;
    }
  }
  if (! continue_to_watch) {
    g_main_loop_quit(loop);
    g_main_loop_unref(loop);
  }
  return continue_to_watch;
}

int main(int argc, char *argv[]) {
  sample_loop(1, G_IO_OUT | G_IO_ERR | G_IO_HUP, write_callback);
  return 0;
}

チャンネルが閉じられるとコンディションにG_IO_ERRのビットが立ちます。同時にG_IO_OUTビットが立つこともありますが、それは単にバッファに空きがあるというだけで、書き込んでも誰も見てくれないので無視するようにしました。

またg_io_channel_write_charsでは、本来は書き込まれたバイト数もチェックしなければならないのですが、長いデータを書き込まなければ途中で切れることはないようなので、今回はチェックしていません。

読み書き用の場合

ソケットは読み書き両用なので、必要なら読み書き両用のコールバックを書くことができます。そのときは上記のread_callback関数とwrite_callback関数を適当に結合させればいいでしょう。なお、ソケットの相手側が閉じられていても (コンディションにG_IO_ERRビットが立っていても)、バッファに読み込み可能なデータが残っていることがあります。プロトコルによりますが、必要ならG_IO_INビットをチェックしてG_IO_STATUS_EOFが返ってくるまで読み出してやることもできます。

おわり

GIOChannelを使えば、GLibのメインイベントループの中で、ブロックされることなく入出力を処理することができます。イベントドリブンなアプリケーションを書くために是非とも使い方を把握しておきたいものです。

Happy hacking!

MSP430のPWM出力を増やす方法

こんにちは、稲垣@Cerevoです。今回はまたMSP430の話題です。

※一応、使っているのはMSP430F247であると断わっておきます。

MSP430には二つのタイマが入っていて、PWMを自動で (CPUが割り込みの中で操作しなくても) やってくれます。今回は普通にタイマでPWMをする方法を紹介し、さらに頑張って普通はPWMに使えないポートでも半自動でPWMをやってみたいと思います。

タイマの概要 (コンペアモード)

タイマにはタイマレジスタとキャプチャ・コンペアブロック0から2 (もしくは0から7) があり、大体以下のように動作します:

  • タイマレジスタ
    • 値が0になるときに割り込みを発生させる (オーバーフロー割り込み)
    • タイマAでは16ビット幅で固定、タイマBでは16/12/10/8ビット幅で可変
    • 三つのタイマモード
      • ビット幅全てを使ってカウントアップ (連続モード)
      • コンペアレジスタ0の値までカウントアップ (アップモード)
      • コンペアレジスタ0の値までカウントアップ、さらに0までカウントダウン (アップダウンモード)
  • キャプチャ・コンペアブロック
    • キャプチャ・コンペアレジスタ (コンペアレジスタと略します) と出力ビットがある
    • コンペアレジスタの値がタイマレジスタの値と等しくなるとき割り込み (コンペア割り込み)
    • 同時に出力ビットをセット・リセットする
  • キャプチャ・コンペアブロック (0以外)
    • タイマレジスタの値がコンペアレジスタ0の値と等しくなるときに (すなわちコンペア割り込み0のタイミングで)、出力ビットをセット・リセット・トグルする (出力モードによる)

キャプチャ・コンペアブロック (0以外) には、出力ビットの変化するタイミングが

  • 自分のコンペア割り込み
  • ブロック0のコンペア割り込み

の二つあることに気づかれましたか。変化のタイミングが二つあるので、各ブロックごとに異なったデューティー比のPWM出力ができるわけです。つまり、普通のPWMとしての使い方は次のようです:

  • アップモード
  • コンペアレジスタ0: 変調周波数を決める
  • コンペアレジスタ0以外: デューティー比を決め、適当な出力モードでPWM出力

キャプチャ・コンペアブロック0でもPWM

上記のような特性から、ブロック0ではPWM出力はできません (パルス幅が0固定のPWMと言えなくもないのですが)。かといって、連続モードに設定して、割り込みが発生するたびにコンペアレジスタを設定しながらGPIOを叩くのもやや無駄な話です。そういうのは各ブロックで別々の割り込み周期を使いたい場合にすることです。もうちょっとCPUがサボりながらPWM出力をする方法があります。ハードウェアに足りない機能だけ、ソフトウェアで実現するべきです。

PWMのキモはこの特徴です:

コンペア割り込みのタイミングで出力ビットをセット・リセット・反転

これでデューティー比が決まるわけです。この特徴は全てのキャプチャ・コンペアブロックに存在します。活用しましょう。ただし、キャプチャ・コンペアブロック0で活用するには、連続モードにしなければいけません。代償として変調周波数は自由に設定できなくなります。

一方、キャプチャ・コンペアブロック0には、この機能がありません:

ブロック0のコンペア割り込みタイミングで出力ビットをセット・リセットする

当然ですね。この部分をオーバーフロー割り込みのタイミングでソフトウェアで処理すれば、キャプチャ・コンペアブロック0でもPWMができます。つまりオーバーフロー割り込みで、出力ビットを0または1に設定すればいいのです。

なお、出力ビットは、通常の出力モードでは値を設定できません。手動設定のモードに切り替えてから設定し、また通常のモードに戻すことになります。なかなか普通じゃない感があります。

そして自由な変調周波数

先に、「代償として変調周波数は自由に設定できなくなります」と書きましたが、自由にする方法が無いわけではありません。というのは、タイマAのタイマレジスタはビット幅が16ビット固定で、連続モードに設定すると周期がかなり長くなってしまうのです。通常の1MHzのクロックだとおよそ16Hz、8MHzのクロックを使ってもおよそ128Hzです。省電力な低速クロックではさらに遅くなりますし、そもそも16HzなんてPWMでLEDを光らせるには遅すぎます。

解決方法は簡単で、周期を短くしたければ、オーバーフロー割り込みの中でタイマレジスタの値を適当に大きくすればいいのです。例えば、0xff00を代入すれば、カウンタは8ビット幅になったも同然。こうして自由な変調周波数を手に入れることができます。コンペアレジスタの値も対応する値にしておきましょう。

まとめ

まとめると次のようにタイマを設定することになります:

  • 初期設定
    • 連続モード
    • コンペアレジスタ0にデューティー比を設定 (必要なら、(0x10000 – 周期 + デューティー比) の値にする)
    • 出力モードはトグル
  • オーバーフロー割り込み
    • 出力モードを手動に設定し、出力ビットを0 (ないし1) に初期化
    • 出力モードをトグルに設定し直す
    • 必要ならタイマレジスタの値を (0x10000 – 周期) の値に設定し直す

ちなみに、タイマのクロックがCPUのクロックと同期していない場合についてデータシートには色々と注意書きがありますが、今回のような使い方では特に気にするべきことはないようです。

おしまい

実は事の発端はタイマBの出力0にLEDをつなげてしまったことだったりします。プリント基板を作る前にチップの仕様をよく確認しないと、ソフトにしわ寄せがくるようですね (寄せることができるともいう)。
ともあれ、Happy hacking!

すごくシンプルなハミング距離計算

ハミング距離とはなんぞや……という話はWikipediaでも見ていだたくとして、要するに「ビット列を比較して値の異なる位置を数えたい」ということです。例えば01010011と01010111のハミング距離は1です。

異なるビット

二つのビット列のうち、ビットの異なる位置を抽出することは簡単です。基本です:

d = a ^ b;

ビットを数える

あとはここから立っているビットを数えるわけですが、普通に考えると

  • ビット列の幅の分だけループさせる
  • プロセッサのビットサーチ命令を使う

という方法を使うと思います。前者は当然遅いので嫌ですね。後者はインラインアセンブラを使ってプロセッサ依存にしないといけませんし、ビットマスクを作ってビットを消しながら数えなくてはならず、いかにも面倒です (←根拠もなくシフト命令は遅いと思っているヤツ)。

そこで私が使う方法は次のようなものです:

d &= d - 1; // 立っている最下位ビットを消す

全ビットが消えるまでループすればハミング距離が分かります:

d = a ^ b;
i = 0
while (d) {
  d &= d - 1;
  i++;
}

おしまい

このコードを何に使うのかというと、BCH(15,5)符号などをパターンマッチングで誤り訂正しようということだったりします。15ビットもあるのに内容は5ビット(32パターン)しかないので、ハミング距離が一番小さい符号語が訂正結果ということにした方が簡単なのではないかというわけです。別にガロア体がよく分からんとかそういう理由ではないんですよ。
Happy hacking!

モバイル機器開発Tips ~パワーマネージメント その2~

こんにちは、Cerevoの鈴木です。
今回はパワーマネージメントを支えるハードウェアということで電源回路を扱ってみます。
実際にどんな電源回路を選定すべきかにフォーカスしたいと思います。
と言っても、そんなに電気回路の知識が必要ない程度で紹介しますので、ご安心をw

電源回路の基本の基本

よく使用される電源回路はDC/DCコンバータという回路です。
この回路を使うことで、例えばリチウムイオンバッテリの電圧である+3.7Vから内部回路の動作に必要な各種電圧を生成するわけです。
DC/DCコンバータの実際の動作としては電力変換を行うのですが、残念ながら100%変換されるわけじゃありません。
性能としては変換時の「効率」が重要になります。ちなみに変換できなかった電力は…熱に変換されますw
では、実際の回路を見てみます。

DM355EVMの電源回路

実際に自分たちが使用しているCPUであるDM355に関する例を紹介してみます。
まずはDM355の評価ボードであるDM355EVMを見てみます。
DM355EVMの回路図は http://c6000.spectrumdigital.com/evmdm355/reve/files/EVMDM355_Schematics_RevE.pdf からDLできます。
P.25-26がDM355の電源回路部分になります。使用しているチップは TPS54310 ですね。
このチップのデータシート http://focus.tij.co.jp/jp/lit/ds/symlink/tps54310.pdf をちょっと見てみます。
まずはP.1にある「効率 対 負荷電流」のグラフを見てみます。
内部回路、この場合はDM355に対して、出力電圧+3.3Vで1Aの電流が流れた場合(つまりDM355に3.3Wの電力を供給した場合)に約93%ぐらいの効率であることが読み取れます。なかなか優秀ですね。
でも、DM355は省電力モードのあるCPUです。
例えば、省電力モードになって+3.3Vで1mAの負荷電流となった(つまりDM355に3.3mWの電力だけを供給することでよくなった)場合に、効率はどうなるか…もう少しデータシートを読み漁ってみましょう。
P.10に特性グラフが出てきました。図12に注目してください。
何と、負荷電流が200mAぐらいのところを境にして、急激に効率が65%程度まで低下しているじゃないですか!
この回路では省電力モード時に約35%の電力が電源チップで熱となって消費されてしまいます。
というか、このEVMでは残念ながら省電力モードの評価や実験はできないですねw

Leopard Boardの電源回路

DM355が載っているボードとして、最近一部で話題のLeopard Boardがあります。
このボードはカメラ付きで$99と評価ボードとしては破格の価格のボードです。
早速、回路図を見てみましょう。回路図は http://www.leopardboard.org/uploads/DM355_LEOPARD_BOARD.pdf からDLできます。
P.13が電源回路になります。Leopard BoardではDM355EVMとは違って、電源統合チップ TPS65053 が使用されていますね。どうりで安いわけですね(ぉ
さてさて、どんな性能でしょうか…データシート http://focus.tij.co.jp/jp/lit/ds/symlink/tps65053.pdf を見てみましょう。
データシートのP.1には大体そのチップのFEATUREが書いてありますが、いきなり “Up To 95% Efficiency” と書いてありますね。期待できそうです。
問題は省電力モードになったとき、つまり低負荷時の効率です。
P.7のFigure1, Figure2に注目です。
どちらも効率と負荷電流の関係を表していますが、Figure1のほうが負荷電流が0.001A=1mAのときでも効率が90%程度を維持しています。
違いは何かというと、Figure1が”PWM/PFM Mode”なのに対して、Figure2が”PWM Mode”オンリーなことです。
このモードをどう切り替えるか、それもデータシートに書いてあります。
P.5に TPS65053 の各ピン毎の機能が “TERMINAL FUNCTIONS” にまとめられていますが、その中のMODEピンが該当します。

Select between Power Save Mode and forced PWM Mode for DCDC1 and DCDC2.
In Power Save Mode, PFM is used at light loads, PWM for higher loads.
If PIN is set to high level, forced PWM Mode is selected.
If Pin has low level, then the device operates in Power Save Mode.

つまり、通常時はMODEピンをHighにしておいて、DM355が省電力モードになるときにはLowにしてあげるとバッテリが効率良く使えますね。
電源回路はこうじゃないといけないです。

電源回路のポイントとまとめ

バッテリ動作するようなモバイル機器の電源回路は、とにかく高効率であることが重要です。
しかも、ON/OFFできるだけじゃなく、電源回路自体がパワーセーブモードを持つものじゃないと長生きできません。
今回はDM355の省電力モードと絡めて電源回路を紹介しましたが、Linuxが動作しているようなCPUが実際に省電力モードになるまでの動作に興味をもたれる方もいると思いますので、次回はその辺の具体例を紹介する予定です。