技術情報」カテゴリーアーカイブ

減衰係数はどうやって計算すれば良いか?

ちょっと間違っているかもしれませんが後で見直すためのメモでもあります。

まず放電とかの減衰曲線の時定数の定義が何だったか忘れました。調べたら行き着くのは当然 Wikipedia、もとの値の約 37% ということで、なんでそんな半端な数なのかも思い出せませんが、とにかく、37% ね。ちなみに時定数を英語で何というかも知りませんでした。普通に time constant なのですね。あっ英語ページには 1 / e ≒ 36.8% て書いてある。と思ってよく見たら日本語ページにも書いてあるじゃないか。

と、与太話はこれぐらいにして、今設計している EG モジュールの PWM は、約10ミリ秒ごとに値を更新するので、T 秒で 1 / e に落とすなら、T / 0.01 回の更新で値が 1 / e にまで減衰すれば良いのだから、

K = (1/e)0.01/T = e-0.01/T

プロットしてみました

なんというか、不安を誘う曲線ですね。精度とか大丈夫だろうか?横軸を対数に取ってみたらどうでしょうか?

うん、これならまだ何とかなるかもしれません。手持ちのボリュームに B カーブのものがなくてDカーブのものを使っているのがとっても気になりますが、もうハードウェアは作ってしまったので仕方がない。でもいずれにせよこの曲線をリアルタイムで計算するのはちょっと厳しそうです。実装にまた工夫がいります。先は長い、かも

マイクロプロセッサでの減衰曲線の実装

前回、参照テーブルを使わずに減衰曲線を生成する方法を考察しましたが、その方法をマイクロプロセッサで試してみました。やはり一筋縄ではいかないようです。

実際のプログラムは記事に載せるには長すぎますが、関係のある部分を抜粋して、こんな感じのプログラムを組みました。一見動きそうだし、これを普通に PC で実行すれば動作するはずですが、AVR ではうまく動作しません。

volatile uint32_t g_value;  // value holder, Q10.22 fixed point number
volatile uint32_t g_decay_factor;  // amount of decay,  Q0.32 fixed point
volatile uint8_t g_update_ready;

ISR(Time1_OVF_vect) {
  OCR1A = g_value >> 22;  // reflect the value to PWM、Q10.22 to integer
  g_update_ready = 1;
}

void UpdateValue() {
  if (!update_ready) {
    return;
  }
  // reduce the value by multiplying the decay factor
  uint64_t temp = g_value;
  temp *= g_decay_factor;
  g_value = temp >> 32;
  g_update_ready = false;
}

int main() {
  g_value = 1023 << 22; // integer to Q10.22
  g_decay_factor = 0.99309249543703590153321021688807 * 0x100000000;
  g_update_ready = 0;

  while (true) {
    UpdateValue();
  }
}

どうも AVR 用のコンパイラ avr-gcc は、C 言語標準に厳密には従っていないようです。どこかにドキュメントがあるかもしれませんが見つけていません。レジスタを細かく制御したいための仕様なのかもしれません。レジスタを意識して、データ領域からレジスタにどんな風にデータをロードしてレジスタ上でどんな演算をするのか意識して書くと動くようです。上記のプログラムは以下のように書き換えれば動くはずです。

volatile uint32_t g_value;  // value holder, Q10.22 fixed point number
volatile uint32_t g_decay_factor;  // amount of decay,  Q0.32 fixed point
volatile uint8_t g_update_ready;

ISR(Time1_OVF_vect) {
  register uint32_t temp = g_value;  // copy the Q10.22 value to a variable
  temp >>= 22;  // Q10.22 to integer
  OCR1A = temp;  // reflect the value to PWM
  g_update_ready = 1;
}

void UpdateValue() {
  if (!update_ready) {
    return;
  }
  register uint64_t temp = g_value;
  // copy the Q10.22 value to a 64-bit int
  temp *= g_decay_factor;
  // multiply by the Q0.32 decay factor
  temp >>= 32;  // shift 32 bit to adjust scale, now it's Q10.22 again
  g_value = temp;
  // copy back to the value holder
  g_update_ready = false;
}

int main() {
  register uint32_t temp = 1023 << 22;   // this is calculated by the compiler
  g_value = temp;
  g_decay_factor = 0.99309249543703590153321021688807 * 0x100000000;
  g_update_ready = 0;

  while (true) {
    UpdateValue();
  }
}

temp は register 変数として宣言しなくても動くようです。コンパイラが良きに計らってくれるようです。

要点は、ビットを伸ばしたり縮めたりする演算をするときには、以下の点を気を付けると動くようです

  • データをどこにロードさせるかはっきりさせる (一時変数を宣言する)
  • 演算がどこで行われるかはっきりさせる
    • 二項演算子 +, -, *, >>, << のかわりに +=, -=, *=, >>=, <<= を使う。
    • 一行一演算、一行に二つの演算を入れない

「ようです」が多いのが恐縮です。このことが書かれているドキュメントを読んだわけではないので、コンパイラの挙動から推測しています。

指数関数曲線を固定小数点演算で求める

エンベロープジェネレータのプログラムですが、現状は指数関数テーブルを参照して出発点の値に時間ごとに減衰する指数関数を掛け合わせて減衰曲線を作っています。

https://github.com/naokiiwakami/vceg

これは精度をあまり気にせずにざっと計算できるので手っ取り早いのですが、プログラムはどうしても煩雑になります。また、値の更新周期は約 10ms なのですが、減衰が遅いと 10ms の間には値が動かず、時間軸を補間しないといけません。これはまだ実装していませんがプログラムはもっと煩雑になります。

ちょっと嫌だなと考え始めたのですが、そもそも指数関数の減衰は

V(i+1) = V(i) * k

where 0 <= k < 1

のような式で簡単に作れそうなものなのですが、実はそう甘くはありません。例えば、1秒で半分に減衰する曲線を、10ms の更新周期で計算しようとすると、k の値は

k = 0.5 ^ (1 / 100) = 0.99309249543703590153321021688807

限りなく 1 に近いけれども 1 にしてしまったら減衰しません。変数として float を使えばいいじゃない?とも思いますが、例えば AVR で float を static 領域に持つとなんだかわからないけれどもデータがばんばん壊れてしまいます(バグかもしれませんが)。それにそもそもふつうのマイクロプロセッサでの浮動小数点演算は遅いです。ということで、固定小数点演算で曲線が求まらないか考えてみました。

ブランクがあまりにも長いのでやり方を忘れてしまいました。ので調べました。以下の解説がとてもわかりやすかったです

https://www.allaboutcircuits.com/technical-articles/multiplication-examples-using-the-fixed-point-representation/

いつか上記リンク先がなくなってしまうかもしれないので解説を書いとくのも良いのですが、何分もう夜分遅いので、書いたプログラムだけだっと貼っちゃいます。

#include <stdio.h>
#include <stdint.h>

int main(int argc, char *argv[]) {
  // fixed point integer 10bit.6bit
  uint16_t value = (1023) << 6;
  // fixed point integer .16bit
  uint16_t decay_factor = 0.99309249543703590153321021688807 * 65536;
  for (uint16_t i = 0; i < 1000; ++i) {
    // multiplication
    uint32_t temp = value * decay_factor;
    value = temp >> 16;
    // retrieve the integer part
    uint16_t output = value >> 6;
    printf("%d %d\n", i, output, 10);
  }
  return 0;
}

結果もさくっと貼っちゃいます

これはいけそうな気がします。

AVRISP mkII を使ってコマンドラインからプログラムする

アトメルの AVR プログラマ AVRISP mkII はもう販売終了していますが、デバイスにさっと書き込むにはやはり大変便利、いまだに愛用しています。でも AtmelStudio 経由で書き込むとウィンドウを開けたり閉じたりしなくてはならず繰り返しプログラミングするときには面倒です。コマンドラインからのプログラミングには Avrdude を使うことが多いですが、このソフトは AVRISP mkII で使われているドライバとうまく通信ができないようで、よっく探すと AVRISP のドライバをハックして無理やり書き込むこともできるようですが、それはちょっと危なっかしい。目的は avrdude を使うことではなくコマンドラインから書き込むことなので、違う方法を探しました。AVRISP mkII はアトメルのものなので Atmel Studio のパッケージの中に何らかのツールがあるはず、と探してみたらありました。atprogram というツールらしいです。以下は使い方のメモです。

PATH 環境変数への登録

C:\Program Files (x86)\Atmel\Studio\7.0\atbackend あたりにあるのでここを環境変数 PATH に登録します。

デバイスへの接続チェック

プログラマを使うときまずするのがデバイスときちんと通信できるかのチェックですが、以下のコマンドを使います

$ atprogram -t avrispmk2 -i isp -d atmega168 info --signature
Firmware check OK
0x001e9406

–signature オプションを省くとフューズなどもっと詳しい情報が出ます。でもフューズをもっと手っ取り早く読むには

$ atprogram -t avrispmk2 -i isp -d atmega168 read --fuses --format hex
Firmware check OK
dfdeff

デバイスへのファームウェア書き込み

以下が例です

$ atprogram -t avrispmk2 -i isp -d atmega168 program -c -fl -f dr-110.elf --format elf --verify
 Firmware check OK
 Programming and verification completed successfully.

ビルドしたイメージのサイズの表示

avr-size コマンドを使います

$ avr-size -C --mcu atmega88 dr-110.elf
AVR Memory Usage
----------------
Device: atmega88

Program:    2468 bytes (30.1% Full)
(.text + .data + .bootloader)

Data:         76 bytes (7.4% Full)
(.data + .bss + .noinit)

Windows 10 の設定

日本から持ってきた ThinkPad 200、10年ぐらい直し直し使っていましたが電源まわりの故障が起きてしまいそろそろ寿命のようで新しい PC を購入しました。Windows 機を新調すると大変な量の設定が必要で毎度辟易しますが、その分できることも多いので我慢のしどころです。

設定事項のひとつひとつは検索をするとたいてい解決しますが数が多いので毎回おのおのを検索するのは時間食いです。過去にやったことを繰り返すにはこのブログにメモを入れておくのが一番効果的とだんだんわかってきました。ということで、この記事には、Windows の設定方法が集めてあります。

JIS 配列キーボードを US 配列キーボードとして使う

106 キーボードを 101 キーマップで使うわけです。以前はレジストリを編集しなくてはならなくてちょっと嫌でしたが、Windows 10 では設定の変更だけでいけます。以下作業メモから

Go to Settings -> Time & Language -> Language -> “Choose an input method to always use as default” -> “Override for default input method”

Then change the selection to “English” from “Use language list”

Inconsolata フォントをインストールする

Inconsolata ページ: https://www.levien.com/type/myfonts/inconsolata.html
フォントの入手先: https://github.com/google/fonts/tree/master/ofl/inconsolata

フォントのインストール方法:

  1. フォント ttf ファイルをダウンロードする
  2. 右クリック→Install
  3. Windows 検索ウィンドウで fonts とタイプすると設定されたフォントが確認できる

参考: https://www.groovypost.com/howto/install-fonts-windows-10/

Sonar をインストールする

Sonar で古いプロジェクトやプラグインを使っているのですがそれらが動くようにするには色々設定が必要です。

32 bit か 64 bit か?

プラグインが 32 bit なので 32 bit 版の Sonar をインストールしたほうが簡単です。 64 bit 版をインストールすると何もしなければプラグインが動きません。

日本語 Sonar を英語 Windows で使う

英語版 Windows で日本語のアプリを使う方法は 鍋CADが英語モードの WINDOWS 10 で文字化けしてしまう に一度書いたのですが、日本語 Sonar はこれだけではまだ十分でなく、

Control Panel > Clock, Language, and Region を呼び出した後、Region > Change location > Administrative タブ > Change system locale… > Current system locale: Japanese > OK

ここまでは一緒ですが、ここからさらに、”Beta: Use Unicode UTF-8 for worldwide language support” チェックボックスにチェックを入れます。それでもまだ多少の文字化けをしますが使える範囲内です。

Proteus VX プラグインのインストール

Proteus VX は Windows XP 向けに開発されていて、32 bit の設定でインストールするだけでは動きません。コンパチブルバージョンを変えないといけません。インストールされた Proteus VX の実行ファイルを探し (私の環境では C:\Program Files (x86)\Creative Professional\Proteus VX\ProteusVX.exe)、右クリック -> Properties -> Compatibility タブ -> Compatibility mode に行き、”Run this program in compatibility mode for” をチェックし、バージョンは “Windows XP (Service Pack 3)” を選びます。今回はこれで動作しました。

こんなに古いプラグインがまだ動くのはありがたいことです。でも誤動作も頻繁に見かけます。E-mu ブランドはついになくなってしまったようで、今後 Proteus が再リリースされる可能性はだいぶん低いのだと思います。残念です。

USB コネクタソケットのシールドはどこにつなぐ?

MIDI USB の実験のために USB コネクタの配線を始めるところですが、まず最初の疑問、「ソケットのシールドはどこにつないだら良い?」 GND に落とすんでしょうか?別のことをするんでしょうか? GND に落とすと色々悪いことが起きそうな予感がします。

Stack Exchange に 参考になるスレッドがありました

たくさん記述がありますが、要するに GND に落としてはいけなくて、どこにもつながないか装置のアースにつなぐのが良いようです。今のプロジェクトでは前者です。

上記のスレッドは長いですが、内容は面白く読む価値はあります。

MC34063 を使った DC-DC 昇圧電源の設計

今改造中の DR-110 は 6V の電池または 9V の ACアダプタを電源にしていますが、USB とつなぐことも検討しているので USB から電源が取れると便利です。 DR-110 の回路図を見ると、電源には最低でも 6V 必要です。 USB からの給電は 5V なので、少しだけ持ち上げる必要があります。今後も USB からアナログ回路への電源が取れるとなにかと便利なので、入手しやすい DC-DC 電源 IC を探して使い方を覚えることにしました。

Jameco が近所にあってそこに在庫があればその日のうちに部品が入手できるので、ここにあるチップしかも DIP パッケージのものが使えれば便利です。調べてみると、MC34063 というチップが安価で在庫豊富です。このチップは、昇圧、減圧、極性反転すべて可能で容量も 1.5A あり応用範囲が広そうです。ほかのサイトを見ても入手しやすいようで、DC-DC コンバータの定番チップであるようです。まずはこの IC の使い方を覚えることにします。

DR-110 の電源として使うには、5V の USB 電源から 7V ぐらいに昇圧すればちょうど良いです。消費電流は 50mA も取れれば良いはず。この仕様なら IC の性能の範囲に楽々収まるのでこの設計をしてみることにします。

最初は資料集めからです。Jameco で扱っている MC34063 は STMicro 製で製品ページはこちらですが、データシートには使い方の要約が書かれているだけで、この回路の知識がないとこれだけでは設計できそうにもありません。ので、ONセミコンダクタTI の製品ページからデータシートとアプリケーションノートを入手しました。アプリケーションノートはどちらの配布も内容は同じなようです。先に見つけたのは TI のページでそちらの資料を使いました。

これが、データシートに載っている昇圧電源の回路図、内臓のオシレータによりQ1/Q2 スイッチを断続して、Lにエネルギーを貯めて昇圧します。5番ピンに入る電圧を監視して、コンパレータによりスイッチングのタイミングを変えることにより出力電圧を安定させます。巧妙にできていますね。

この回路定数をそのままR2だけを変えて 5V → 7V の昇圧になるように設定して実際に回路を組んでみましたが、出力電源に盛大なリップルが入ってそれがノイズとして聞こえてしまい使い物になりませんでした。もっと丁寧に回路定数を計算する必要があるようです。アプリケーションノートには動作原理から回路定数の計算方法まで詳しく書かれているのでそれに従って回路定数を計算してみました。

オシレータの周波数は CT によって決まり、ほかの回路定数の影響はほぼ受けないようです。発振周波数から決めて、あとは、出力電圧からスイッチングのデューティー比を計算、それと出力電流から L の最小値を決めればよいようです。それで決めた回路定数をもとに、シミュレータを使って動作確認をして回路定数を少し変えました。下の図が回路定数を変えてシミュレーションした回路、R4 は負荷です。

スイッチング電源の難しそうなところは、負荷の大きさでスイッチングの挙動が大きく変わること、アナログ回路では消費電流が大きく変わることはあまりなさそうなので、消費電流をあらかじめ調べてから設計を調整したほうが良いような気もします。以下は、負荷抵抗を 100kΩにまで増やした時の挙動、スイッチングのタイミングが大きく変わって、遅いリップルもくっきり見えています。周期が 1 kHz 弱ぐらいまでに落ちているのでこれは音になって聞こえてしまいそうで、このようになったら回路定数をまた変えないといけません。ここまできたら実際に回路を組んで確認したほうがよさそうです。今 10μH のインダクタが手元にないのでまずはここまでです。

鍋CADが英語モードの Windows 10 で文字化けしてしまう

鍋CADを英語モードの Windows 10 で使うと日本語の表記がすべて文字化けしてしまいます。これを防ぐには、システムのロケールを日本語に変える必要があります。

それには、下のスクリーンショットのように、Control Panel > Clock, Language, and Region を呼び出した後、Region > Change location > Administrative タブ > Change system locale… > Current system locale: Japanese > OK

と操作した後、OSを再起動するとシステムのロケールが日本語に変わります。その後は、鍋CADの日本語表記はきちんと表示されるようになります。

でも、システム全体のロケールを変えるとコマンドプロンプトのバックスラッシュが円マークに変わっていろんなファイルが見づらくなったりと、本当はあまりうれしくありません。ロケールを変えたくないなら、鍋CADの設定を変更して英語表記で使うという手もありますが、正直ちょっとわかりづらくなります。ソフトウェアがインストールされているディレクトリ (デフォルトでは C:\NTCAD) の下に LANG というディレクトリがあってその下にある English.txt を編集して自分好みに変えてしまうという方法もあります。

MIDI OX を使って MIDI メッセージを記録する方法

MIDI メッセージの解析をするのに MIDI-OX は非常に強力なソフトウェアです。MIDI 信号を再現するために、受けた信号を記録することもできるのですが、やり方が少しわかりにくいのでこの記事に書き留めておきます。以下の手順で .mid ファイルを作ることができます。

1. Input Monitor を開く

開かなくても記録はできるんですがモニタを開いておくと受けたメッセージが見られるので作業が楽です。メニューの

View-> Input Monitor…

から開けます。

2. モニタを開始する

Actions -> Start Display

で開始します。ついでに SysEx も受け付けるようにしておくと解析にはよいかもしれません。

Options -> Pass SysEx

3. 試しに MIDI メッセージを入れてみる

以下のような画面になるはずです。右下の REC と SYX というボックスが紺色に反転していることが重要です。

4. 記録を開始する

モニタ画面を右クリックして “Clear Input” を選ぶと画面がクリアされます。クリアしたら記録を開始します。

File -> Log

を選んで、ログファイルダイアログを呼び出し、以下のように内容を変更します。

  • Enable Logging を入れる
  • Log Format を “MIDI to Text” にする
  • Log File の拡張子を .mtx に変える

OK ボタンを押すと記録が開始されます。再度何か入力を入れると、以下のような画面になります。右下の LOG ボックスが紺色に反転していることが重要です。

5. 記録を終了する

画面右下の LOG ボックスをクリックするか、File -> Log… ダイアログで Enable Logging を外して OK ボタンを押すと記録が終了します。記録開始時に指定した .mtx ファイルが生成されます。

6. .mtx ファイルを .mid ファイルに変換する

Mtx2Midi というツールを使います。MIDI-OX のページ

http://www.midiox.com/

に行って、Mtx2Midi-Installer.zip というリンクを探してインストールします。インストール後、生成した .mtx ファイルを右クリックして “Convert to MIDI file” を選ぶと変換した .mid ファイルが同じフォルダに作られます。

PSoC CY8CKIT-049-42xx キットの USB-UART ブリッジを使う

これをプログラムするには miniprog3 が必要です。

1. SCB UART コンポーネントを置く。ボーレートを 9600 に変更する。
uart_config

2. ピンの割り当ては以下の通り

UART RX : P4[0]
UART TX : P4[1]

pin_connection

3. これがソースコード

int main()
{
    CyGlobalIntEnable; /* Enable global interrupts. */

    /* Place your initialization/startup code here (e.g. MyInst_Start()) */
    UART_1_Start();
    
    UART_1_UartPutString("Hello world from CY8CKIT-049-42xx\r\n");

    for(;;)
    {
        /* Place your application code here. */
    }
}

4. ビルドしてプログラムして CY8CKIT-049-42xx キットを PC につなぎます
terminal_screenshot

はいこれだけ