2013年 11月 05日

直列/並列共振回路の共振周波数

リアクタンスとの関係

インピーダンスの式

共振状態ではリアクタンス部(虚数部)はゼロになる (抵抗成分だけ)。

すなわち

はそれぞれ

共振状態のときの式に代入して f について解く (普通は角周波数 ω を使うみたいだけど、僕はなんか混乱するので全部展開しとく)

移項

両辺に をかける

両辺を で割る

両辺の平方根をとる

両辺を で割る

最悪リアクタンス部がゼロになれば共振ということがわかってれば、簡単に導ける。

変形して L/C を求めるとき

直列共振回路の Q

L か C のリアクタンスをRで割るイメージが覚えやすい気がする (抵抗Rが小さいほど高いQになる、というのがイメージしやすい) 直列に入っている R は単に損失なので、小さいほうがいいというイメージ。

↑ をそれぞれ で割っただけ

並列共振回路の Q

直列の逆数と覚える。並列に入っている R は小さいほど無駄に電流を流すので、大きいほうが損失がなくていいというイメージ。

FET の入力容量と速度の関係がわからない

FET は電圧駆動なので電圧さえかければ電流を流す必要はない。

ただ、現実的にはFETの入力に寄生している容量を十分充電しきるまでは、電流が相応に流れ、オフにするときはすみやかにこの容量を放電することが求められる。なので容量が少なければ少ないほど早いのはイメージできる。

まではわかったけど、では実際にそれがスイッチングスピードにどう関わってくるか計算で求めるのがよくわからない。

例として 2N7000 の input capacity は最大で 50pF、150Ωの抵抗と3Vの電圧(最大電流20mA)で充電しようとすると、CRの過渡現象の例の式だけで考えると、電流は

ある時間におけるコンデンサの電圧を考える。コンデンサの電圧は電源電圧から抵抗での電圧降下を引いた値なので、

これをさらに t について解きたいけど、自分の力だと解けないので Maxima 君で解くと solve(v = E * (1 - %e^(-t/(C*R)) ), t);

で、各値を代入して計算すると

function t (C, R, E, v) {
	return C * R * Math.log(-(E / (v - E)));
}

// 入力電圧の9割まで
console.log(t(50e-12, 150,  3,  3*0.9)); //=> 約17ns
// 最低 V_th まで
console.log(t(50e-12, 150,  3,  0.8)); //=> 約2.3ns

になるけど、確かめようがない。それに、この速度が実際の FET の速度にどう関わってくるかわからない……

ググってでてきたpdf を見た感じだいぶ難しくて現時点の自分では理解できない。

要は知りたいのは、Gとグラウンド間に入れる放電用抵抗は最大どの程度にできるのか、ということなんだけど、よくわからない。大きすぎると放電に時間がかかりすぎて、オフにならなくなるのはわかる。まぁ実際試しみて定数決定するのが一番いいんだけど……

2013年 11月 02日

ATTiny13A を使った低消費電力エレキー

まとめを後日書きました [tech][avr][arduino] ボタン電池で動く小型・低消費電力 AVR エレキー (50円 ワンチップマイコン ATTiny13A) | Fri, Nov 8. 2013 - 氾濫原

前書いたのの続き

さらにもっと消費電力を減らせないだろうか? と考えた。パワーダウンモードの消費が計測不可能なので、支配的なのは特にキーイング中と、アイドル中の消費になっている。

特にキーイング中の消費が比較的多い (200uA近く) ので、どうにかできないかと考えた。キーを押しているとき、内蔵プルアップを通して GND に電流が流れるので、そこの消費が結構多い。

今まで何も考えず AVR 組込みのプルアップ抵抗を使っていたけど、ここでちょっと考えてみることにした。

プルアップの調整

内部プルアップは、実測からすると3V/100uA=30kΩ になってるようだ (スペック上は 20k〜50k)。とりあえず AVR 内部のプルアップ抵抗をオフに。

プルアップ抵抗に許される電圧降下を考える。電源電圧3Vで、入力 H レベルは VCC * 0.6 (スペックから)、ノイズ回避用に 0.4V を足すと

  • ピンの必要入力電圧 Vin = 3 * 0.6 + 0.4 = 2.2V
  • プルアップでの電圧降下最大 V = 3 - Vin = 0.8V

となった。

マイコン側は十分大きい入力抵抗があると考えると、かなり大きな抵抗を入れても大丈夫そう? なのかな。

試しに 2.2MΩでプルアップしてみると、ICの足にかかる電圧は2.5V程度になった。2.2MΩ で0.5V電圧降下しているので、0.23uA 程度流れてる。3V / 0.23uA でプルアップと内部抵抗の合成値は 13.2MΩ、つまり内部抵抗は11MΩぐらい……? よく「入力抵抗は非常に大きい」といわれるのを見る割に、いまいちどの程度かわからなかったけど、こんなもんなのかな。

この状態で、消費電力をはかる

  • before: 内蔵プルアップ: 198uA
  • after: 2.2MΩプルアップ: 113uA

とりあえず減ったし、普通に動いてはいる。しかしこれであっているのかさっぱりわからない。

これで、毎日2時間使うケースだと (CR2032/225mAh, 1日2時間, パワーダウン中 0.15e-6mA, キーイング中 113e-3mA) 995日持つ計算になった。

delay_ms のさらなる見直し

128kHz で動かすにあたり delay_ms を以下のようにしていた。

void delay_ms(uint16_t t) {
	uint16_t end;
	cli();
	timer = 0; TCNT0 = 0;
	end = NOW + DURATION(t);
	sei();
	while (NOW <= end) { nop; }
}

timer は 2msec ごとのオーバーフロー割込みでインクリメントされているけど、これだとちょっと精度が悲しいような気がするので、カウンタそのもの (TCNT0) も見ている。オーバーフローでやっているので、単純に timer はカウンタの桁あがりとして扱える。NOW は ((timer<<8)|TCNT0) という定義

while の中を アイドルではなく nop; にしているのは、ここでアイドルに入ってしまうと、次に起きるのが 2msec 後とかになるので、せっかく TCNT0 を見ている意味がなくなるから。

ただ、まだここは精度を維持しつつも最適化の余地があって、以下のようにした。

void delay_ms(uint16_t t) {
	uint16_t end;
	uint16_t end0;
	cli();
	timer = 0; TCNT0 = 0;
	end = NOW + DURATION(t);
	end0 = end - 0x100;
	sei();
	while (NOW <= end0) {
		set_sleep_mode(SLEEP_MODE_IDLE);
		sleep_mode();
	}
	while (NOW <= end) { }
}

少なくとも、最後の TCNT0 分 (8bit) になるまでは、普通に 2msec ごとに寝ていても大丈夫なはずなので、それまではスリープを使い、最後のカウンタ分はビジーループにするようにした。nop; は別にいらなそうなのでとっただけ。

これでキーイング中でも 96uA まで消費電流を落とせた。上と同じように、毎日2時間使うケースだと (CR2032/225mAh, 1日2時間, パワーダウン中 0.15e-6mA, キーイング中 96e-3mA) 1171日持つ計算になった。1000日超え! もうこれでいいかな。

回路図・基板検討に EAGLE を使ってみる

回路図・基板作成用 CAD のEAGLEを使ってみた。割とデファクトスタンダードっぽく使われているっぽい。試したのはフリー版 (個人レベルでは十分すぎる機能がある)

基本商用ソフトなので、もっとモダンでいい感じなのかと思ったけど、思いのほか操作性がレガシーで悪い。回路図作成ツールとしては BSch3V のほう部品エディタも含め、圧倒的に使いやすい……

ただ、配線ツールとしては結構使いやすい (というか、もっとよくできると思うけど、これ以上のものがなさそう……) デファクトスタンダードっぽく使われているおかげで、ライブラリは比較的充実しているように思える。ただ、あまり整理されていない形になっているので、最初ものすごくとまどう……

しばらく使わないと絶対に忘れるインターフェイスなので、重要なところをメモっておく

ライブラリ

(追記)

http://www.cadsoftusa.com/downloads/libraries のページから

  • japan で検索して出てくるもの全部
    • c-j.lbr
    • r-j.lbr
    • tr-j.lbr
    • alps_japan_potentiometers.lbr
  • avr-7.lbr

あたりを入れておいたほうがいいっぽい。ただ、 *-j.lbr は US EU とピンに互換性がなくて置換できない…… ライブラリエディタで、Symbol の編集画面を開き、ピンの名前を一緒にしてあげればいいっぽい。例えば resistors.lbr の場合、ピンの名前は 1 とか 2 とかなので、c-j.lbr のほうも PIN1 を 1 に PIN2 を 2 にすれば、置換できるようになる。

デフォルトだと EU 版 US 版の記号が入っていて、これでもなんとかなるけど、日本で使われる一般的な回路図記号と若干異る

  • EU 版
    • 抵抗の記号が新JIS記号と一緒 (ジグザグじゃなくて四角)
    • C の棒が太いくて長い
  • US 版
    • 抵抗の記号は旧JIS記号と似てる (ジグザグ) ただ、ジグザグの数が多い
    • C の片方が曲っている

回路図


  • 回路図をつくるとき
    • Add a part で部品を置いていく、検索が貧弱なのでかなり空気を読む必要がある
      • 置いてから ESC を押すと選択画面に戻るので連続でおける
    • 既に回路図にある部品なら Copy ツールを使ったほうがいい
    • 使いたい形がなくてもとりあえず記号だけあわせておけば、あとから Replace で部品だけ変えられる
    • Electrical Rule Check は頻繁にかけたほうがいい
      • けどエラーの意味がわからなかったりする
      • ECR の結果は回路図の画面で示されるので、基板画面を見ているとなにがなんだかわからない

基板図

ユニバーサル基板で作ること前提なので、あまり細かいところまでは使ってない。ユニバーサル基板専用のソフトがあればいいんだけど。

  • 基板図をつくるとき
    • 必ず 回路図を同時に開いておく必要がある
      • そうしないと修正点が反映されず、最初から作りなおすハメになる (意味不明)
    • まずグリッドを設定する。Size 50mil Multiple: 2 にして Display on にすると、100mil ごとにグリッドがひかれ、50mil ごとにスナップするようになる。(ユニバーサル基板のピッチは100mil)
    • Edit -> Net Classes... で default の Width を 32mil ぐらいにしとく (オートルートとかするときこの太さでひかれる)
    • Layers で、tOrigins / bOrigins を非表示すると、部品全てが動かせなくなる
  • オートルート便利かと思いきやかなり難しい。
    • 少ない部品なら、基本自分で配線して、どうしたもんかってときに使うとヒントがでるツールとして使う感じ
    • ripup; コマンドを実行すると配線済みのが全部消える
    • 自分である程度配線したあとなら Undo を使ったほうがよい
2013年 10月 30日

ATTiny13A を使った小型エレキー

まとめを後日書きました http://lowreal.net/2013/11/08/5

前に書いたのの続き。

せっかくメインのICが小さいので、できるだけ小さく作るならどうするのがいいだろう?と考えてる。

まず、乾電池1本で動いたらいいなと思ったので、1.4Vを3.3Vに昇圧する回路を組んで駆動させてみたら、昇圧回路がどうもうまくいかなくて、やたら電気食う感じだった。昇圧回路の調整を別途やれば乾電池1本でもいい感じになるかもしれないけど、実際ブレッドボードで組んでみたら結構体積も食う感じになってしまったので、そもそもこの方法を諦めた。

じゃあリチウムコイン電池を使うことを考えよう、と思った。リチウムコイン電池なら1個で3V出せる。ただ、あまり電流を流すことができなくて、標準で 0.2mA 程度に抑える必要がある。消費電力を減らす工夫はしてるつもりだけど、スピード調節をADCにしたのでボリュームに常に電流が流れるとか (10kΩなのでこれだけで0.3mA流れる)、もうちょっと頑張る必要がありそうだった。

まず、ADC は1秒ごとに行うようにコードを変えて、そもそも頻度を減らした。また、ボリュームに電圧をかけるのも、ADCが行われるときだけにした。これはピン1個を出力にして、VCC の代わりにそこに繋いでる。VCC とピンの出力電圧は厳密には違うので、ADCの精度は落ちるけど、そもそも安定してない電池駆動だし、相対値だけがとれればいいのでまぁいいかな、という気がする。

それでだいたいアイドル時に0.3mA程度まで下がった。しかしこれ以上思いつかないので、とりあえずクロックを下げてみて挙動に問題がないか試すことにした。ATTiny13A の場合、内蔵クロックは 9.6MHz, 4.8MHz, 128kHz とそれらを8分周したものが選べる。なので 4.8MHz を8分周した600kHzで動かしてみると、思ったより変なことにならなかった。これでアイドル中はほぼ0.2mA未満に。

じゃあ 128kHz だとどうだろう、ということでやってみたら、

 bad AVRISPmkII connection status: Unknown status 0x00

とかでるように…… たぶんクロックを下げすぎたせいで ISP の書きこみのクロックを読めてないのかな……

うーん困ったと思っていたら avrdude に転送スピードを下げるオプション (-B) があったので、-B 100 ぐらいにしてやったらいけた。しかし書きこみ速度がだいぶ遅い。

コードを全体的に書きなおさないと 128kHz でバグってて動かない感じだけど、軽く試した感じだと、アイドル中で0.09mAぐらい。キーイング中で0.19mA。パワーダウンは変わらず0.6uA程度。コーディングが面倒になった割にはそんなに減らない。ベースの消費が無視できないほどクロックの支配率が低くなってるせいかな。

仕様から

  • VCC=3V, f=128kHz
    • idle: 0.01mA
    • active: 0.04mA
    • power down: 0.00015mA
    • ADC: 0.225mA
    • Timer0: 0.002mA
    • pull-up: 0.08mA
  • キーイング中は pull-up + active + timer0 + FET GS接地抵抗(440k)電流 = 0.122mA が最低でも必ずかかる
  • アイドル中は idle + timer0 = 0.012mA
  • ADC中は active + ADC + timer0 = ボリューム電流 = 0.567mA
  • パワーダウン中はクロックに関係なく固定で 0.15uA
    • 比較すると実測値が結構高いけどなんでだろう
  • 瞬間最大電流
  • キー同時押し pull-up * 2 + active + timer0 + FET GS接地抵抗(440k)電流 + ADC
    • 0.7338mA

パワーダウン中の消費は AVR ISP Mark II と繋っていたからで、RESET をはずせば 0uA になった

  • パワーダウンモード 0.0uA未満
  • アイドル中 90uA -> 0.09mA
  • キーイング中 198uA -> 0.198mA with FET
2013年 10月 28日

マイコンプログラミングとか、黙って Arduino やっとけハゲという感じではあるんだけど、Arduino の言語がどうも好きになれなくて使ってない。CっぽいけどCじゃない、でもC、みたいなのがなんとなく嫌だなと感じてる。それは置いておいても Arduino は素晴しいと思います。

Arduino も AVR を使っているし、結局 Arduino 頑張ろうとすると AVR のスペックを理解する必要があるので、じゃあ AVR でいいか、という感じになる (安いし)。

Arduino は、開発環境も含めたエコシステムが魅力で、そこがすごくいいと思うけど、自分みたいに CUI で vim で書いて Makefile でコンパイルして書きこんで gdb でデバッグするみたいのが好きな人間だと、少し魅力が薄れてしまうように思う。

IDE はあればあるで便利で良いんだけど、Makefile と gdb を使ってやっていれば、マイコンプログラミング以外のときにもその知識が生かせて嬉しいと思う (IDEの操作の知識はほかに生かせない)

2013年 10月 27日

AVR 浮動小数点 (float) 演算

浮動小数点演算を使ったとき、-lm を付けないとバイナリサイズが巨大化する問題がある。-lm をつけない場合、デフォルトの (libgccの?) 浮動小数点関数がリンクされるけど、avr 用には高効率なものが libm に実装されている。libgcc だと 3k -> libm だと 1k ぐらいのインパクトがあるので必ず libm を使うようにしたい。

とはいえ、ちゃんと理解してないと libm にリンクされない……

結論からいうと以下じゃないとだめだった。

$(COMPILE) -o main.elf $(OBJECTS) -lm

以下のようだとうまくいかない。

$(COMPILE) -lm -o main.elf $(OBJECTS)
$(COMPILE) -o main.elf -lm $(OBJECTS)

というのも、リンカは、引数を順番に読みこんで、読み込み中のファイルに今までで未定義のシンボルがあったとき、それを解決する、という挙動をするらしい (なんとなく逆に、先に定義して解決していくもんだと思ってた)。

なので、先に -lm を指定しても、その時点では未定義のシンボルが何もないので何の意味もない。

gcc のオプションに -v (verbose) を渡すと、最終的に ld (collect2) に渡される引数がわかる。

うまくいく場合は main.o -lm -lgcc -lc -lgcc というふうになってる。main.o で使ってるシンボルが -lm で解決されて、あとまだ足りないのは -lgcc とかで解決される。-lgcc が2回出てくるのは、-lc が -lgcc を使ってるからかな。よくわかんない。

AVR、なぜかリセットされまくるとき

割込みかけるように設定しているにも関わらず、それに対する処理を書いていないと、sei() を呼んだあと、割込みが発生するときに落ちてリセットがかかる。

sei() が呼ばれるまでは問題ないので、コメントアウトでデバッグしていると超ハマる。

AVR シリアルでPCと接続してデバッグ

USB-シリアルポートアダプタ (RS-232C) は前に買っていたけど、RS-232C は正負 -12~+12 で1/0を表現うるので、マイコンのロジックレベル(0 or VCC)とは違っていて、そのままではマイコンと接続できない。

調べてみると、RS232トランシーバー (ドライバ) ICというのがあって、それを使えば簡単にレベル変換できることがわかった。有名なのは MAX232 というやつみたいだけど、ほぼ同じインターフェイス(ピンアサイン)でビットレートや電源電圧が違うやつがいろいろとあるみたいだ。

今回は ICL3232CPZ という 3.3V〜5V で動いて、なおかつ外付け部品が 0.1uF 5個だけというのを選んで作った。

繋いで以下のような、ボーレート 19200 で吐き出すコードを書いてみた (チップは ATTiny2313、レジスタ名がチップによって違うので、チップ変えるとそのままでは動かない)

#include <avr/io.h>
#include <string.h>
#include <util/delay.h>

#define clear_bit(v, bit) v &= ~(1 << bit)
#define set_bit(v, bit)   v |=  (1 << bit)

static inline void uart_putchar(char c) {
	loop_until_bit_is_set(UCSRA, UDRE);
	UDR = c;
}

static inline void uart_puts(char* string) {
	unsigned int len = strlen(string);
	unsigned int i;

	for (i = 0; i < len; i++) {
		uart_putchar(string[i]);
	}

	uart_putchar('\r');
	uart_putchar('\n');
}

void usart_init(unsigned short baudrate) {
	unsigned int d = ((F_CPU + (baudrate * 8L)) / (baudrate * 16L) - 1);
	UBRRL = d;
	UBRRH = d >> 8;

	UCSRB =
		(1<<RXCIE) | // RX Complete Interrupt Enable
		(1<<TXCIE) | // TX Complete Interrupt Enable
		(0<<UDRIE) | // USART Data Register Empty Interrupt Enable
		(1<<RXEN)  | // Receiver Enable
		(1<<TXEN)  | // Transmitter Enable
		(0<<UCSZ2) | // Character Size
		(0<<RXB8)  | // Receive Data Bit 8
		(0<<TXB8)  ; // Transmit Data Bit 8

	UCSRC =
		(0<<UMSEL)            | // USART Mode Select: 0=Asynchronous Operation, 1=Synchronous Operation
		(0<<UPM1)|(0<<UPM0)   | // Parity Mode
		(0<<USBS)             | // Stop Bit Select
		(1<<UCSZ1)|(1<<UCSZ0) | // Character Size (with UCSRB)
		(0<<UCPOL)            ; // Clock Polarity
}


static inline void setup_io () {
	usart_init(19200);
}

int main(void) {
	setup_io();


	for (;;) {
		uart_puts("Hello, World");
		_delay_ms(1000);
	}
}

PC 側では、この USB シリアルポートアダプタの場合、/dev/tty.usbserial-FTB3L9UG というようなファイルができるので、これを指定して GNU screen の window を1つ作ってる。変なことしてないのでボーレートを指定するだけでいける。

screen /dev/tty.usbserial-FTB3L9UG 19200

これで RX/TX があるチップならかなりデバッグが捗りそう。

ATTiny13A

いろいろパーツを注文するついでに ATTiny13A というチップも買ってみた。

見ての通り8ピンしかないチップだけど、以下のような特徴がある

  • 1個50円と激安。
  • ADC がついてる
  • プログラムに使えるのは1KBしかない
  • SRAM が 64B だけある (のでCで書ける)
  • 外付け水晶を使うのは面倒 (使わないつもりで書いたほうが良い)
  • ISPを殺せば最大6ピンIOに使えるけど、基本5ピンしかIOに使えないと考えたほうがいい

かなり制約があって楽しい。サイズ的に浮動小数点は使えないので工夫する必要がある。メモリ(SRAM)も64Bしかないのでついうっかりするとオーバーしそう。でもCで開発できるのでいいバランス。

とりあえずエレキー作るのがいいかと思って作ってみた。

まとめを後日書きました ボタン電池で動く小型・低消費電力 AVR エレキー (50円 ワンチップマイコン ATTiny13A) | tech | avr | arduino - 氾濫原

ただ、ピン数が少ないせいで大きな問題がある。というのも、必然的に ISP で使うピンとアプリケーションのピンを共有することになるので、回路によっては、繋いだままの ISP というのができないっぽい。「SCK fail」とか「MOSI fail」とか出て意味不明だったけど、このせいみたいだ。うまくやらないと In-System Programming という感じでなくなる。なんか簡単ないい方法ないのかな。

ADC の端子がいくつかあるので、スイッチをたくさん繋いでいくというのは割とできそう。ただ出力はそうもいかない。ソフトウェアに使えるメモリ量も少ないので「ハードにないからソフトで実現」というのも難しいことが多そう。

それにしても「50円で買えるコンピュータ」というのはグっとくる。