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日超え! もうこれでいいかな。

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円で買えるコンピュータ」というのはグっとくる。

2013年 10月 21日

AVR で USB 接続の PC キーヤーを作る

PC からモールス符号を発生させて無線機に入力するものが欲しいと思っていた。当然既にそういうのはあるんだけど、どうも気に入るのがないので、必要な機能だけ自分で実装する。

基本の要件としては

  • クロスプラットフォームであること
    • というか Mac で動くこと
  • USB バスパワーで動くこと

なので、簡単そうでいろいろ教材としても楽そう。

クロスプラットフォームで面倒なドライバを書かなくてすむ方法は HID デバイスとして作ることだと思うので、そのように作ることにした。また、USB 用の外部チップとか用意したくないので V-USB (AVR マイコン上で USB を処理できるライブラリ) を使うことにした。

いろいろやってなんとか2日ぐらいで最低限の機能の実装ができたのでいろいろメモしておく。

回路図とコード

コード: https://github.com/cho45/AVR-USB-CW


現状制作途中のものなので、実際使う際に必要なリレー部分とかは書いてない。

現状だとダイオード2本で約1.4V程度電圧降下させて 3.3V〜3.6V 程度を作りだす方法を使ってる。V-USB の Wikiに書いてある方法の1つ。

左下のはそのうち試してみたい回路で、AVR自体には5Vをそのまま供給して、信号ラインだけ 3.6V にツェナーダイオードで電圧降下させる。電源電圧を3.3V程度にしてしまうと、動作周波数が 12MHz 程度までしか保証されないので、16MHz や 20MHz で動かすならこちらのほうが安定しそう。

V-USB

まず examples/hid-{data,mouse} を動かして、ふむふむと思いながらやってみたけど、なかなかハマった…… HID デバイスを作ろうとするとデフォルトの usbconfig.h から結構書きかえないといけないんだけど、それを知っていないとだめなのでつらかった。

usbconfig-prototype.h から作るのではなく、example/hid-* のをコピってくるほうが確実に動くと思う。

まだコントロール転送しかできてない。少なくともインタラプト転送はやることになりそうだけどデバッグがだいぶ大変 (追記:インタラプト転送は Mac + libusb だとできなかった)。

あと、自分で設定している割込みの頻度が高すぎると STALL しやすい…… クロック周波数にしても、こういった処理にしても結構シビアなので気を使わざるを得ない……

HID のレポートデスクリプタ定義は、大本の仕様書がどこにあるのかわからないのでコピペと感で書いて試した。

V-USB を妨げない delay_ms

_delay_ms は定数しかスリープできないのと、ビジーループなのでその間割込み以外何も走らなくなってしまう。V-USB は usbPoll() を少なくとも50msecに一回は呼ばないといけないとかなので、これは使えない。

なので、まず割込みで時間を測るタイマー変数を作る。

必要な精度以上のタイマー(あまり短くないほうがいい)を用意 (この例だと1ms) して、グローバルな timer という変数をインクリメントするようにする。(F_CPU / 分周設定 / OCR0A) CTC のほうが自由に時間を設定できて楽

	/**
	 * timer interrupt
	 * CTC 16MHz / 64 / 250 = 1kHz = 1msec
	 */
	TCCR0A = 0b00000010;
	TCCR0B = 0b00000011;
	OCR0A  = 250;
	TIMSK0 = 0b00000010;
unsigned long timer;
ISR(TIMER0_OVF_vect) {
	timer += 100;
}

以下のようなマクロを用意して DURATION(msec) で、引数に与えた時間が timer 換算でいくつになるかを出す。浮動小数点演算を使った瞬間、出てくるバイナリサイズが長大になるのでそうならないようにしてる。

#define DURATION(msec) (unsigned int)(msec * 100)


delay_ms を実装する。_delay_ms におけるビジーループ部分が任意に書けるので、ここで usbPoll() をする。

void delay_ms(unsigned int t) {
	unsigned long end;
        cli();
	timer = 0;
        end = timer + DURATION(t);
        sei();
	while (timer <= end) {
		wdt_reset();
		usbPoll();
	}
}

これでナイーブに delay_ms() を使ったコードでも可読性と効率をそこそこ両立できそう。しかし、なんかもっといい感じにコード書けそう。。普通どうやるものなのかよくわからない。

I2C 液晶

デバッグするとき、何かしら文字を表示できるインターフェイスがないとつらいため導入。シリアルポートに出力してデバッグするのがたぶん一番楽なんだと思うけど、レベル変換が必要で手元にあるものではできなかった。


I2C を使うにあたって、原理を理解していないのと、なおかつそれを AVR で実現する方法がわからないのと、さらには「デファクトスタンダード」な通信プロトコルも存じあげないため、全ての箇所でハマりにハマった。チップは ATmega168P なのでそこまで細かい原理を知っていて実装できる必要はないけど、だいぶハマった。

やり終わってみれば、別に難しくはないんだけど、どこに問題の原因があるのかが、見える現象が少なすぎて大変だった。面倒でもいちいちLEDチカチカさせて動作確認をすべき。

以下に I2C 液晶 (アドレス 0x7c) を操作するコード。何をするにせよ、とにかくデータシートをよく読んで理解するのが大変に重要だと実感した。

#include <string.h>

#define START 0x08   
#define ReSTART 0x10   
#define MT_SLA_ACK 0x18   
#define MT_DATA_ACK 0x28   
   
#define MR_SLA_ACK 0x40   
#define MR_DATA_ACK 0x50   
#define MR_DATA_NACK 0x58   
   
#define SLA_W 0xA0   
#define SLA_R 0xA1   
#define ADDRESS 0x00   
#define DATA 0x55   

void Error () {
//	unsigned i = 0;
//	for (i = 0; i < 3; i++) {
//		set_bit(PINB, 0);
//		_delay_ms(500);
//		clear_bit(PINB, 0);
//		_delay_ms(500);
//	}
//	clear_bit(PINB, 0);
}

void i2c_start () {
	// start
	TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
	while (!(TWCR & (1<<TWINT)));
	if ((TWSR & 0xF8) != START) return Error();   
}

void i2c_stop () {
	TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
}

unsigned char i2c_write (unsigned char data) {
	TWDR = data;
	TWCR = (1<<TWINT) | (1<<TWEN);
	while (!(TWCR & (1<<TWINT)));

	switch (TWSR & 0xF8) {
		case MT_SLA_ACK:
		case MR_SLA_ACK:
		case MT_DATA_ACK:
			return 1;
		default:
			Error();
			return 0;
	}
}

void display_write_instruction (unsigned char address, unsigned char data) {
	i2c_start();
	i2c_write(address);
	i2c_write(0b10000000);
	i2c_write(data);
	i2c_stop();
	_delay_ms(1);
}

void display_write_data (char* string) {
	unsigned int i = 0;
	unsigned int len = strlen(string);

	display_write_instruction(0x7c, 0b00000001);

	i2c_start();
	i2c_write(0x7c);
	i2c_write(0b01000000); // Co=0 RS=1

	for (i = 0; i < len && i < 8; i++) {
		i2c_write(string[i]);
	}
	i2c_stop();

	_delay_ms(1);

	i2c_start();
	i2c_write(0x7c);
	i2c_write(0b10000000); // Co=1 RS=0
	i2c_write(0b11000000); // set ddram address to line 2
	i2c_write(0b01000000); // Co=0 RS=1
	for (; i < len && i < 16; i++) {
		i2c_write(string[i]);
	}
	i2c_stop();
}

void display_init () {
	TWBR = 0x24;
	TWSR = 0b00000001;
	TWCR = 1<<TWEN;

	display_write_instruction(0x7c, 0b00111000); // function set to 0 (default)
	display_write_instruction(0x7c, 0b00111001); // function set to 1 (extended)
	display_write_instruction(0x7c, 0b00010100); // internal osc frequency
	display_write_instruction(0x7c, 0b01110000); // contrast set
	display_write_instruction(0x7c, 0b01010110); // power/icon/contrast control
	display_write_instruction(0x7c, 0b01101100); // follower control
	_delay_ms(300);
	display_write_instruction(0x7c, 0b00111000); // function seto to 0
	display_write_instruction(0x7c, 0b00001101); // display on/off control
	display_write_instruction(0x7c, 0b00000001); // clear all

	_delay_ms(10);
}

ハマり

ハードウェアが絡むコーディングは、一度ハマると

  • 配線ミス
  • コーディングのミス

のどちらで発生しているかの見極めができないとかなりつらい…… 配線ミスはあってはならないので、最初の段階でよくよく確認しておかないとあとあとつらい。どんなに確認しても確認したりないぐらい確認する必要がある。あと配線後は必ずテスターをあてたほうがいい……

USB コネクタのピンの半田付けを適当にしすぎたせいで、試している最中に1本がはずれて通信できなくなるということがあった。こういうアホみたいなことでもコーディングミスを疑ったりすると時間を食ってしまう。

PORTB, PINB

出力を操作したいときは PORTB なのに、PINB と書いていてしばらく気付かなかった。この場合、微妙に出力されたりされなかったり、みたいな不安定な挙動になって、全く動かないわけでもないのでなかなかコーディングミスであることに気付けない。

Mac 環境で Ruby + libusb を使って HID デバイスを操作する

最初は feature レポートだけでごにょごにょしていたのでよかったけど、インタラプト転送をしようとしたところで、claim_interface が ERROR_ACCESS エラーを出して止まることがわかった。ググった感じだと、HID デバイスの場合、そのへんの処理を OS のドライバが握っているため、ユーザレベルだと直接扱えないらしい。

とりあえずどうしようもないっぽいので、feature レポートだけで情報をやりとりするように変えた。現状のプログラムだとそれでも問題がないっぽい。

AVR は思ったより難しくなくて結構拍子抜けするぐらいだけど、今までやったことがない分野なので適度に難しい問題がたくさんころがっていてめちゃくちゃ楽しい。久しぶりに深夜3時ぐらいまでひたすらコーディングするとかしてしまった。

USB デバイスとか「作れない」という状態だとほんと全然アイデアが沸いてこないんだけど、やってみると結構「ああ、あれもできそう」「あれができたら楽しそう」みたいなのがでてきていい。

ウェブアプリのように誰かにすぐ使ってもらえるものを作るという感じではないのがもったいない感じだけど、なんか別に、自分そういうのなくてもいいなって感じなので良いです。

僕みたいな普段ウェブアプリみたいなかなりレイヤーが上のほうのことやってる人間でも、最近だとアセンブリとか全く書かなくてもよくていい。

ちょっと考えてみた感じ、何も持っていない状態からの AVR におけるとりあえずの開発に必要なのは以下あたりかなあと思った。

  • プログラムの一般的な知識 (ウェブとか関係なく普通の)
  • AVR ライター (純正 AVR ISP Mark II も別に高くないので純正のを買うのがよさそう)
  • ブレッドボード・ブレッドボード用のジャンパピン集
  • ピンヘッダ・オス・メス
  • ミノムシクリップコネクタ
  • レインボーフラットケーブル (裂いて使えるし、複数の線が纏められるし超便利)
  • マイコンチップ (ATTiny2312=150円ぐらい, ATmega168P=200円ぐらい)
  • 抵抗E12系一通り10本ずつぐらい
  • コンデンサE6系一通り10本ずつぐらい (デジタルだと100uF以下ぐらいでよさそう)
  • LED数個 (デバッグその他に必ず必要)
  • 電源
    • 安定化電源、なければ電池ケース (エネループなら4本直列=4.8V程度のもの)
    • あるいはUSBから5Vとってもいいかもしれないけど、ポリスイッチ(何度も使えるヒューズ)を入れたほうがいい
  • テスター (必須)

そんなに必要なものない気がしたけど、何もなしからだと結構ある。1万ぐらいあればぎりぎり集められるかな。

2013年 10月 19日

AVR エレキー、リグの接続を判定して内蔵ブザーを切り替え

ダイオードだけ入れて対処したのがどうもひっかかっていて、やはりまずい気がするので、やめた。リグ側から電圧がかかっていないとき、こちら側から電圧がかかってしまうのが気持ちわるい。

リグ側からかかっている電圧を使ってFETをスイッチし、接続されているときだけ、特定のプルアップしているピンをグラウンドに落とすような感じにした。スイッチを使わず、実際に接続されているかどうかを見れるようになったので、ジャックは挿しこまれているけど、リグの電源がついていない場合なんかには機能的にも前より便利になった。

素直にこうしとけばよかった。これでそんなにまずいことはない、と思う……

懸念していた点が解消されたので、ブレッドボートをやめてユニバーサル基板に実装した。

今まで、回路図からユニバーサル基板に落とすときはいきあたりばったりでやっていたけど、今回はプリント基板作成ツールを使って事前に実装を考えてみた。

いろいろ、プリント基板を作れるソフトウェアはあるんだけど、どれも微妙に使いづらくて難しい。自分だけの最高の部品ライブラリを作ってからじゃないと基板作成ができないとか、そういうアレがある……

今回はいくつか試した結果、Fritzing という OSS の、プリント基板ビューを使ってやってみた。ビューがいくつかあって楽しいソフトウェアなんだけど、やはりこれも回路図やブレッドボートビューを使う場合、ライブラリを作るあげるのが面倒くさい。ただ、配線を試行錯誤やる程度なら、別の部品を代用して使っても問題ない。

しかし配線を試行錯誤するのも結構つらくて、選択したくないものが選択されてしまうことがものすごく多い。そのためのロック機能がある、と思いきや、ロックをかけても選択できてしまうので意味がない。結果、一切あたり判定が存在しなくて動かすことができない部品とかが存在する (一度かぶっているのをとれば移動できるけど)。かなり辛い。