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

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

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

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

  1. トップ
  2. tech
  1. トップ
  2. avr
  1. トップ
  2. arduino

S (トトト) H (トトトト) 5 (トトトトト) の区別がつくように全然ならない。20wpm 程度でも結構間違える。全部とれない場合、ミスのほとんどがこれ。25wpm になると、1分間やっている中の 5 を全部 H、H を S と書く、あるいは逆、みたいにズレて聞きとったりする。クソすぎてイライラする。

S H 5 だけランダムで聞きとろうとすると、今度は全部聞きとれない。ランダムではなく SHE, HIS とかはパターンで覚えてるからある程度聞きとれるけど、ランダムだと全く意味わかんない。なので、比較して聞きとれるようにするということもできない。というか単体ならまず間違えないんだよ……

E (ト) と I (トト) と S (トトト) の区別は容易にできるけど、それ以上の短点は「いっぱい」としか認識されない。「ちょっと長いから5だろう」「5ほどじゃないからHかな」ぐらいの曖昧な感じ。本当にイライラする。

もう初めて3ヶ月ほど経つのにこのありさまなのでやる気なくす。あまりにもこれらを間違えるので 26wpm から 20wpm 程度までスピードを落としてゆっくりやってる。多少マシになったかもしれないけど。あいかわらず間違える。聞きとれない文字がいくつかあると、それらで考えてしまって止まるので、速度があげられない。一度聞きとれないと、その単語は完全に落とすので、そういう文字があってはならない。

次に J と 1 も、S H 5 ほどではないけど区別がつかない。こっちはコールサインの文脈でよく使うので比較的頭に長さのイメージができているけど、それでも間違える。どうすれば解決するのか全くわからない。3ヶ月やってダメなら一生ダメなんじゃないか。

以上の符号以外にも、2、3、7、Z、X はそれ以外に比べてよく間違える。

浮動小数点演算を使ったとき、-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 を使ってるからかな。よくわかんない。

  1. トップ
  2. tech
  3. AVR 浮動小数点 (float) 演算
  1. トップ
  2. avr
  3. AVR 浮動小数点 (float) 演算
  1. トップ
  2. arduino
  3. AVR 浮動小数点 (float) 演算

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

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

  1. トップ
  2. tech
  3. AVR、なぜかリセットされまくるとき
  1. トップ
  2. avr
  3. AVR、なぜかリセットされまくるとき
  1. トップ
  2. arduino
  3. AVR、なぜかリセットされまくるとき

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 があるチップならかなりデバッグが捗りそう。

  1. トップ
  2. tech
  3. AVR シリアルでPCと接続してデバッグ
  1. トップ
  2. avr
  3. AVR シリアルでPCと接続してデバッグ
  1. トップ
  2. arduino
  3. AVR シリアルでPCと接続してデバッグ

いろいろパーツを注文するついでに 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円で買えるコンピュータ」というのはグっとくる。

  1. トップ
  2. tech
  3. ATTiny13A
  1. トップ
  2. avr
  3. ATTiny13A

使いはじめて2年ぐらい経ったところで、イヤーパッドの縫い目が割けてきしまったので、交換したくなった。とりあえず純正が高いので安いのを買ってみた。

Bose QuietComfort 15 対応交換用パッド◆QC15, QC2, AE2, AE2i 対応(univarcオリジナル) 安心安全保証付き U1355 -

3.0 / 5.0

さすがに本体とサイズが合わないということはなく、普通に使える感じ。ただ、純正と比べるとちょっと硬い。しばらく使ってみる。

Bose QuietComfort 15 Acoustic Noise Cancelling headphones ノイズキャンセリングヘッドホン QuietComfort15-S - BOSE(ボーズ)

BOSE(ボーズ)

5.0 / 5.0

やっぱりほぼ毎日使っていると痛んでくる。どのぐらい長く使えるかな。もうこれ無しの生活とか考えられないんだけど

IKEA で買ったピーラーの切れ味がよくなくて、うちで不評だったので以下を買った。知り合いの家にあって、便利だったので、検索してみたらすぐ見つかった。

スピードサラダ 19639 - 下村企販

下村企販

5.0 / 5.0

千切り機能とか使わねーだろと思ったけど、使ってみたら便利だった。刃物系はあんまり安いとよくないですね。

爪切りも少しだけ良いものを買ったほう確実に生活が向上していいです。

木屋 はがねの爪切り 大 BK-T02 - 瀬川 喜一郎

瀬川 喜一郎

3.0 / 5.0

↑ これほしい。

普段仕事でもなんでも殆ど、筆記用具を使わないので、筆記用具とかもはやどうでもいいやって感じだったけれど、最近になって筆記で勉強しはじめたので、捗る筆記用具について思いを馳せた。

最初は普通に今まで持っていたゴミみたいなシャープペンシルを使っていた。しかし、最近何かのアレで万年筆 (LAMYというメーカーのやつ) をもらったので、せっかくだからと使ってみたら、すごく書きやすかった。気に入った点は大したことではないんだけど、

  • インクなのでシャープペンシルと比べて色が圧倒的に濃い
  • 筆圧かけずにすらすら書ける

あたりで、それからはずっと万年筆を使って勉強してる。書くのが楽しいので、問題を解くモチベーションに繋る。

しかし、いただいた万年筆は線がちょっと太めな感じだったので、細いのが欲しくなった。と、いうタイミングで、ちょうどよくパイロットのカクノ という子供向け1000円の万年筆が発売されたので、買ってみた。(アマゾンにまだなかったので普通に文具店で買った。今はあるみたい)

パイロット 万年筆カクノ F ライトグリーン FKA1SRLGF - パイロット(Pilot)

パイロット(Pilot)

3.0 / 5.0

買ったのは色はライトグリーンで、ペン先が F のもの。F のものが日本語筆記だとスタンダードっぽくて、ちょっと細めみたいだ。デザインはまぁ……悪くはないけどって感じだけど、使ってみた感じだと、先が細いぶんちょっとひっかかるかなというぐらいで十分良かったので、今はこっちをメインに使ってる。

と、いうのはいいんだけど、最初についてるインクが2週間経たないぐらいでなくなってしまって「えっ!」となった。実はインクがなくなるまで使えたら買おうと思っていたかっこいいインクがあったので、さっそく注文した。

パイロット 万年筆インキ iroshizuku INK-50-TY ツキヨ - PILOT

PILOT

5.0 / 5.0

↑ これ、どっかで見かけて、すごいかっこいいなー欲しいなーけど俺使わないなーと思っていたけど、案外インク使うことがわかったのでウキウキしながら買ってみた。

 -

4.0 / 5.0

インクを入れるコンバーターというのも買った。(カクノには最初インクカートリッジがついてるけど、コンバーターも普通に使えるようだ)

一回ペンを洗ってから入れかえた。初めて入れかえをやったので結構楽しい。実際書いてみると色がすごく自分に好みだったので大変満足した。嬉しい買いものだった。色はかなりたくさんあって、悩んだけど、これ (月夜) にしてよかった。少しだけ緑が入っている夜空の青。

ペンを変えたら、ペンを使いたくなるので、勉強のモチベーションがちょっとあがる。簡単にモチベーションが上がるようなことは全部やったらいいと思うし、コストの割には結構いい感じに上がるので、たいへんいいなと思った。

しかし、もうすこしいいのも欲しくなるのが困る

パイロット 万年筆 コクーン シルバー 細字 FCO-3SR-S-F - パイロット

パイロット

5.0 / 5.0


パイロット 万年筆 プレラ 色彩逢い 細字 透明ブラック FPRN350R-TBF - PILOT

PILOT

5.0 / 5.0


パイロット 万年筆 カスタム74 FKK-1MR-NCF 細字 透明 - PILOT

PILOT

5.0 / 5.0

カクノ以外は結構なお値段ですね!! カクノは使い捨てというわけではないし、コンバーターも使えるのでコストパフォーマンスは高そう。もうちょっとだけかっこいいのがあればなあ。

Kindle Paperwhite (第6世代) ―Wi-Fi - Amazon

Amazon

3.0 / 5.0

新しい Kindle Paperwhite を買った (予約してたのを忘れてて、きづいたらきた)

Kindle を所有するのははじめてなので (すこし他人のを触らせてもらったことはある) あんまり比較はできない。とりあえず、持った感じはよくて結構嬉しい感じ。表示の切替えはあんまり早くない (これでも早くなったらしいけど)

とりあえず手元にある PDF を入れみたり、既に購入済みのやつを読んでみたりした。PDF はそのままだとコントラストを最高にしないと読みにくい。

あとウェブページを送信する用に

をダウンロードしておいた。プリント先として表示されるようになって便利

持ち歩きで使うならカバーが必須な気がするなー と思っているけど、結構高いので買ってない。

Amazon Kindle Paperwhite用レザーカバー、オニックスブラック【Kindle Paperwhite(第5世代、第6世代、第7世代、マンガモデル)専用】 - Amazon

Amazon

3.0 / 5.0

今まで kaise の SK-6201 というテスターを使っていたけど、中学生ぐらいから使っていて、電流計の表示にオフセットがあったり、測定レンジが狭かったりで不便だったのでこの際に新調した。

三和電気計器 SANWA デジタルマルチメータ バックライト搭載 CD771 - 三和電気計器

三和電気計器

4.0 / 5.0

電流のレンジが大幅に増えてすごく便利になった。ただ、前のテスターだと値に大きな変化があったときに音が鳴る機能があって気に入っていたんだけど、これにはなくて残念。

サンワは測定器メーカーとして有名なようです。

  1. トップ
  2. tech
  3. テスターを新調した

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 レポートだけで情報をやりとりするように変えた。現状のプログラムだとそれでも問題がないっぽい。

  1. トップ
  2. tech
  3. AVR で USB 接続の PC キーヤーを作る
  1. トップ
  2. avr
  3. AVR で USB 接続の PC キーヤーを作る
  1. トップ
  2. arduino
  3. AVR で USB 接続の PC キーヤーを作る
  1. トップ
  2. ham
  3. AVR で USB 接続の PC キーヤーを作る
  1. トップ
  2. モールス
  3. AVR で USB 接続の PC キーヤーを作る

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万ぐらいあればぎりぎり集められるかな。

  1. トップ
  2. tech
  1. トップ
  2. avr
  1. トップ
  2. arduino

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

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

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

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

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

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

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

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

  1. トップ
  2. tech
  3. AVR エレキー、リグの接続を判定して内蔵ブザーを切り替え
  1. トップ
  2. avr
  3. AVR エレキー、リグの接続を判定して内蔵ブザーを切り替え
  1. トップ
  2. arduino
  3. AVR エレキー、リグの接続を判定して内蔵ブザーを切り替え

割込みタイマーによるカウンタを使った delay_ms に実装しなおしたら 1MHz で動かしても符号が著しく遅くなることがなくなった。

この状態で消費電流を測る

  • アイドル状態 0.38mA
  • パワーダウン状態 62uA
  • キーイング中 0.55mA

delay 中も sleep するようにしたのでキーイング中の消費がだいぶ減ってる。

常時キーイングしてても144日ぐらい持つ。普通ありえないので、1日あたり2時間キーイングとすると、771日でだいたい2年持つ計算 (実際は自然放電されるからもっと短いけど、十分長い)

さらに減らすにはどうしたらいいだろう。チップスペック的にはパワーダウンモードだと0.1uA未満しか流れないみたいだけど、現状の回路だと多少流れてる。プルアップしてるのがわるい?

スリープ前にピン設定を変える

リグとの接続を見る端子がプルアップしているのをスリープ時にオフにすればいいことがわかった。これで

  • アイドル状態 0.38mA
  • パワーダウン状態 10.8uA
  • キーイング中 0.55mA

これで1日2時間キーイングで1440日に…

アイドル中、キーイング中の消費電力はこれよりもっと減らせるだろうか……

ISP Programmer の罠

AVR ISP Mark II を繋いでいると、パワーダウンモードでも10uAほど流れるようだ…… なんとはずしただけで 0uA (測定限界未満) になってしまった。これで同条件で 1693日持つ計算になった。上のプルアップを一時的にやめるというのもやる必要がなかった。

  1. トップ
  2. tech
  3. AVR 消費電力を減らす
  1. トップ
  2. avr
  3. AVR 消費電力を減らす
  1. トップ
  2. arduino
  3. AVR 消費電力を減らす

まず何を作ろうという感じだけど、エレキーならはじめて作るマイコンの教材としては、LEDチカチカレベルで簡単だし、なおかつ実用性があるので、ちょうどいい。自分で作れれば何かしら挙動を変えたいときも技術力の許す限りは自由にできるのでやってみてる。

検索するといろいろ作っている人がいるけど、結構難しい感じまで作りこまれたのが多くてつらい。しかし この作例 はプログラムも外付け部品もかなりシンプルだったのでとても参考にした。特にプログラム部分は、割込みを使って非常に少ないコードで基本機能が完全に実装されていて、実装の気が楽になった。

設計の指針

  • いわゆるエレキーの基本機能
    • 長短点メモリー
    • インヒビット
    • スクイズキー
  • スピードコントロールできること
  • 1.2V電池2本で長いこと動くこと
  • サイドトーンを発音できて、単体でも練習用として使えること

最初の実装

最初の実装は全部で80行ほど。スピードコントロールなどもなくて、動作だけ動くかやってみた感じ。この時点ではブザーを鳴らす部分とかもないので、LED をチカチカさせていた

ブザーを鳴らす

一番お手軽なのは電圧かけるだけで鳴るやつを使うことだろうけど、手元にあるのは普通に圧電スピーカーだし、鳴る周波数変えれたら嬉しいので PWM を使って出力することにした。

16bit タイマーでやったんだけど、動作を理解するまでがまず意味不明で大変だったのと、動作を理解しても、比較値やトップ値に対応する実際の変数名がわからなくてひたすら仕様を読んだりした。しかしまだちゃんと理解しているという感じになってない…… 一応鳴るようにはなったし、ピポッって鳴らしたりもできるのでとりあえず良い。

出力

出力はリグと繋ぐ部分。事前に計ったところ、リグのキーイング端子は 5V がかかっていて、スイッチオンのときは 1mA 未満しか流れない。なのでほんと適当にスイッチすればよさそう。

  • トランジスタスイッチング
    • 安い・簡単・消費電力多め
  • FETスイッチング
    • 消費電力少なめ・少々高い・回路電圧からして使えるFETを探すのがめんどう
  • フォトカプラ
    • まだ試してない
トランジスタスイッチ

最初に試した。手元の 2SC1815Y のベースに5.6kΩ程度つけてエミッタ接地でスイッチングさせた。動作としては問題なくうまくいく。

FETスイッチ

エンハンスメント型でゲート電圧2.4V付近で十分電流が流せればなんでもいいはずだけど (ほんとうかな)、定番のスイッチ用 FET というのがよくわからない 、とにかく売ってるやつで適していそうなやつを探した…… CPAN からモジュール探すより難しい。

データシートを見た感じ、2SK2232 のほうが低い電圧でもかなりの電流 (3Aぐらい) が流せるようになるみたいだけど、だいぶ高い。2N7000 は 2.4V 程度だと数十mAしか流せない。

今回の用途だと、まずDS間に大量に電流が流れることがないので 2N7000 で駆動してもよさそうだけど、よくわからない。試した感じではうまくいった。

スピードコントロール

とりあえず手元にある ATTiny2313 には ADC がないので、お手軽にボリューム使ってスピードコントロールするみたいのはできない。コンデンサの充電時間を利用してADCぽいことをするというのをやってる人もいるけど、だいぶ面倒そうだし、そこまでするなら普通に ADC 内蔵のチップを買ったほうがいいな、という気分。ATmega168 というのも手元にあるけど、今回の用途ではオーバースペックすぎて使う気が起きない。

しかし、よくよく考えてもみると、そこまで執拗にボリュームというインターフェイスを使いたいわけではなくて、むしろデジタル的に 15wpm 〜 30wpm 程度を 1wpm 単位で切り替えられたほうが、相手に「QRQ25wpm」とか言われたときに対応しやすいのではないか? と思ったので、プッシュスイッチ × 2 を up/down スイッチとしてスピードコントロールをすることにした。

ただ、この方法だと、何らかの方法を別途用意しないと、現在設定している速度がいくつなのかわからなくなる。

  • up/down 同時押しか何かで現在の設定をサイドトーンに出力
    • 簡単で良さそうだけど急いでるときはだるい
  • up/down 同時押しか何かで一定時間 2桁の7セグを駆動
    • IO使いまくる
  • I2C 液晶をつける
    • TINY だと面倒

と考えた結果、ボタン長押しでサイドトーン側に現在の設定を鳴らすことにした。

ボタンの同時押し

up/down ボタンだけでできるだけ多くの操作を割り当てたい。具体的には

  • 上記の通り、ボタン長押しで速度を発音
  • ボタン同時長押しでエレキーとしての機能を切るモード (調整のときとかに必要)

しかし、これは実際かなり大変で時間がかった。長押しはそんなに難しくないけど、同時押しはとにかくめんどうくさい。似たようなことをやってる人がいた ので、処理のしかたはほぼパクりつつ、PIND にしかスイッチがないので、PIND を一括で状態管理するような感じにした。

しかし、同時押しはどちらかが必ず先に押されるわけだし、通常の長押しと競合してしまったりする。頑張って判定する必要があった。

サイドトーンのオフ

リグに接続している間は当然サイドトーンを切りたい。しかし、スピードを発音したりするので、完全にスピーカーから何も鳴らさないというわけにはいかない。なので自由が効くソフトウェア側で処理するようにしたいと考えた。

しかしこれが曲者で、スイッチ付きジャックのスイッチ部分が信号ラインと一体でまずい。

説明するのが面倒だけど、リグ側からかかる電圧より、こちらの電圧のほうが低いので、ダイオードを1本入れて対処した。あやうい感じで、これでいいのかよくわからないけど、挙動的には望むものになった。

消費電力

何もしない状態だと 8MHz で動かして常時 2mA ぐらい流れる感じだったので、スリープモードとかを調べて実装してみた。

電源電圧 2.4V

  1. 何も対策しないと消費電力 約2mA、単3エネループ (1900mAh) 2本直列だと、950時間、約40日
  2. 毎ループアイドルにするようにする 0.82mA 同約96日
    1. キーの状態を見にいく割込みは生きているので、そのタイミングで毎回起きて、何もしてなかったらまた寝る
    2. 入力がない限り何もしないプログラムならお手軽に低消費電力にできる感じ
  3. 一定以上経つとパワーダウンする 54.9uA 同約1442日
    1. 10秒程度アイドル状態が続いたらパワーダウンモードに移行する
    2. キーの入力状態が変更されると、そこで割込みがかかって起きる
    3. 生きてるものが殆どなくなるので、さすがに制約が多く、ちょっと考えないとうまくいかない感じ

と、結構下げることができた。

動作周波数を1MHzまで下げることでさらに半分以下になったりするけど、while (--t) _delay_ms(1) のオーバーヘッドが無視できなくなるのか、符号のスピードがかなり遅くなってしまうので、やめてしまった。頑張ってオーバーヘッドをさっぴいて delay するようにするか、根本的に設計をみなおすかしないとだめそう。

最低動作電圧

現状

  • マイコン単体だと 1.5V 程度まで動く
  • 出力FETがオンする最低の電源電圧が 2.27V ぐらい。

電源はエネループを想定しているので、2.1V 程度まで動くと嬉しい。今だとちょっと高めで止まってしまう。FET のゲート抵抗をもっと低くしたらいいかもしれない。逆に、2.1V 程度になったら過放電を防ぐためにもちゃんと止まってほしい。むずかしい。

回路図

前述の通り、出力と、PB4(16pin) がダイオード経由で接続されてる。接続されているとき、リグからかかる電圧が PB4 にかからないようにという意図だけど、これでいいのかわからない。逆にこちらからリグ側へは制限されていなくてよくなさそう (プルアップされてるので電圧がかかってる)。接続されていない場合は接地される。もっといい方法がないか知りたいけど、手掛かりがない。

パドル側にはいずれもバリスタをつけてる。パドルは接点が露出したスイッチなので、静電気に気をつかわないとまずそうだなと思ってつけてるけど、これも自己判断なのでこれであっているかわからない。

  1. トップ
  2. tech
  3. AVR でエレキーをつくってる
  1. トップ
  2. avr
  3. AVR でエレキーをつくってる
  1. トップ
  2. arduino
  3. AVR でエレキーをつくってる