✖
Kindle がきた
新しい Kindle Paperwhite を買った (予約してたのを忘れてて、きづいたらきた)
Kindle を所有するのははじめてなので (すこし他人のを触らせてもらったことはある) あんまり比較はできない。とりあえず、持った感じはよくて結構嬉しい感じ。表示の切替えはあんまり早くない (これでも早くなったらしいけど)
とりあえず手元にある PDF を入れみたり、既に購入済みのやつを読んでみたりした。PDF はそのままだとコントラストを最高にしないと読みにくい。
あとウェブページを送信する用に
- Send to Kindle for Mac http://www.amazon.com/gp/sendtokindle/mac
をダウンロードしておいた。プリント先として表示されるようになって便利
持ち歩きで使うならカバーが必須な気がするなー と思っているけど、結構高いので買ってない。
Amazon Kindle Paperwhite用レザーカバー、オニックスブラック【Kindle Paperwhite(第5世代、第6世代、第7世代、マンガモデル)専用】 cho45
✖
テスターを新調した
今まで kaise の SK-6201 というテスターを使っていたけど、中学生ぐらいから使っていて、電流計の表示にオフセットがあったり、測定レンジが狭かったりで不便だったのでこの際に新調した。
電流のレンジが大幅に増えてすごく便利になった。ただ、前のテスターだと値に大きな変化があったときに音が鳴る機能があって気に入っていたんだけど、これにはなくて残念。
サンワは測定器メーカーとして有名なようです。
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万ぐらいあればぎりぎり集められるかな。
AVR エレキー、リグの接続を判定して内蔵ブザーを切り替え
ダイオードだけ入れて対処したのがどうもひっかかっていて、やはりまずい気がするので、やめた。リグ側から電圧がかかっていないとき、こちら側から電圧がかかってしまうのが気持ちわるい。
リグ側からかかっている電圧を使ってFETをスイッチし、接続されているときだけ、特定のプルアップしているピンをグラウンドに落とすような感じにした。スイッチを使わず、実際に接続されているかどうかを見れるようになったので、ジャックは挿しこまれているけど、リグの電源がついていない場合なんかには機能的にも前より便利になった。
素直にこうしとけばよかった。これでそんなにまずいことはない、と思う……
懸念していた点が解消されたので、ブレッドボートをやめてユニバーサル基板に実装した。
今まで、回路図からユニバーサル基板に落とすときはいきあたりばったりでやっていたけど、今回はプリント基板作成ツールを使って事前に実装を考えてみた。
いろいろ、プリント基板を作れるソフトウェアはあるんだけど、どれも微妙に使いづらくて難しい。自分だけの最高の部品ライブラリを作ってからじゃないと基板作成ができないとか、そういうアレがある……
今回はいくつか試した結果、Fritzing という OSS の、プリント基板ビューを使ってやってみた。ビューがいくつかあって楽しいソフトウェアなんだけど、やはりこれも回路図やブレッドボートビューを使う場合、ライブラリを作るあげるのが面倒くさい。ただ、配線を試行錯誤やる程度なら、別の部品を代用して使っても問題ない。
しかし配線を試行錯誤するのも結構つらくて、選択したくないものが選択されてしまうことがものすごく多い。そのためのロック機能がある、と思いきや、ロックをかけても選択できてしまうので意味がない。結果、一切あたり判定が存在しなくて動かすことができない部品とかが存在する (一度かぶっているのをとれば移動できるけど)。かなり辛い。
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日持つ計算になった。上のプルアップを一時的にやめるというのもやる必要がなかった。
AVR でエレキーをつくってる
まず何を作ろうという感じだけど、エレキーならはじめて作るマイコンの教材としては、LEDチカチカレベルで簡単だし、なおかつ実用性があるので、ちょうどいい。自分で作れれば何かしら挙動を変えたいときも技術力の許す限りは自由にできるのでやってみてる。
検索するといろいろ作っている人がいるけど、結構難しい感じまで作りこまれたのが多くてつらい。しかし この作例 はプログラムも外付け部品もかなりシンプルだったのでとても参考にした。特にプログラム部分は、割込みを使って非常に少ないコードで基本機能が完全に実装されていて、実装の気が楽になった。
設計の指針
- いわゆるエレキーの基本機能
- 長短点メモリー
- インヒビット
- スクイズキー
- スピードコントロールできること
- 1.2V電池2本で長いこと動くこと
- サイドトーンを発音できて、単体でも練習用として使えること
最初の実装
最初の実装は全部で80行ほど。スピードコントロールなどもなくて、動作だけ動くかやってみた感じ。この時点ではブザーを鳴らす部分とかもないので、LED をチカチカさせていた
ブザーを鳴らす
一番お手軽なのは電圧かけるだけで鳴るやつを使うことだろうけど、手元にあるのは普通に圧電スピーカーだし、鳴る周波数変えれたら嬉しいので PWM を使って出力することにした。
16bit タイマーでやったんだけど、動作を理解するまでがまず意味不明で大変だったのと、動作を理解しても、比較値やトップ値に対応する実際の変数名がわからなくてひたすら仕様を読んだりした。しかしまだちゃんと理解しているという感じになってない…… 一応鳴るようにはなったし、ピポッって鳴らしたりもできるのでとりあえず良い。
出力
出力はリグと繋ぐ部分。事前に計ったところ、リグのキーイング端子は 5V がかかっていて、スイッチオンのときは 1mA 未満しか流れない。なのでほんと適当にスイッチすればよさそう。
- トランジスタスイッチング
- 安い・簡単・消費電力多め
- FETスイッチング
- 消費電力少なめ・少々高い・回路電圧からして使えるFETを探すのがめんどう
- フォトカプラ
- まだ試してない
トランジスタスイッチ
最初に試した。手元の 2SC1815Y のベースに5.6kΩ程度つけてエミッタ接地でスイッチングさせた。動作としては問題なくうまくいく。
FETスイッチ
エンハンスメント型でゲート電圧2.4V付近で十分電流が流せればなんでもいいはずだけど (ほんとうかな)、定番のスイッチ用 FET というのがよくわからない 、とにかく売ってるやつで適していそうなやつを探した…… CPAN からモジュール探すより難しい。
- 2N7000 1個あたり20円 http://www.fairchildsemi.com/ds/2N/2N7000.pdf
- 2SK2232 1個あたり200円
データシートを見た感じ、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
- 何も対策しないと消費電力 約2mA、単3エネループ (1900mAh) 2本直列だと、950時間、約40日
- 毎ループアイドルにするようにする 0.82mA 同約96日
- キーの状態を見にいく割込みは生きているので、そのタイミングで毎回起きて、何もしてなかったらまた寝る
- 入力がない限り何もしないプログラムならお手軽に低消費電力にできる感じ
- 一定以上経つとパワーダウンする 54.9uA 同約1442日
- 10秒程度アイドル状態が続いたらパワーダウンモードに移行する
- キーの入力状態が変更されると、そこで割込みがかかって起きる
- 生きてるものが殆どなくなるので、さすがに制約が多く、ちょっと考えないとうまくいかない感じ
と、結構下げることができた。
動作周波数を1MHzまで下げることでさらに半分以下になったりするけど、while (--t) _delay_ms(1) のオーバーヘッドが無視できなくなるのか、符号のスピードがかなり遅くなってしまうので、やめてしまった。頑張ってオーバーヘッドをさっぴいて delay するようにするか、根本的に設計をみなおすかしないとだめそう。
最低動作電圧
現状
- マイコン単体だと 1.5V 程度まで動く
- 出力FETがオンする最低の電源電圧が 2.27V ぐらい。
電源はエネループを想定しているので、2.1V 程度まで動くと嬉しい。今だとちょっと高めで止まってしまう。FET のゲート抵抗をもっと低くしたらいいかもしれない。逆に、2.1V 程度になったら過放電を防ぐためにもちゃんと止まってほしい。むずかしい。
回路図
前述の通り、出力と、PB4(16pin) がダイオード経由で接続されてる。接続されているとき、リグからかかる電圧が PB4 にかからないようにという意図だけど、これでいいのかわからない。逆にこちらからリグ側へは制限されていなくてよくなさそう (プルアップされてるので電圧がかかってる)。接続されていない場合は接地される。もっといい方法がないか知りたいけど、手掛かりがない。
パドル側にはいずれもバリスタをつけてる。パドルは接点が露出したスイッチなので、静電気に気をつかわないとまずそうだなと思ってつけてるけど、これも自己判断なのでこれであっているかわからない。
半導体の基礎 N型、P型
半導体って「導体と絶縁体の中間」って説明されるけど、実感としてよくわからなかった。自分の中では導体か絶縁体しかなくて、半導体は特別なイメージがあった。
しかし、調べてみると、まさに「導体と絶縁体の中間」という説明がそのままイメージとして理解できた。2種類の半導体 (N型、P型) について理解するのがてっとりばやかった。なぜ今までこれを理解しようと思わなかったのか、恥ずかしい。普通に Wikipedia に載っている画像がわかりやすかった。
導体は自由に動ける電子がたくさんある物質、絶縁体は逆に自由に動ける電子が殆どない物質、半導体はその中間。
N型 (Negative)
http://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:N-doped_Si.svg
電子が多少余っている半導体。電子の電荷はマイナスなので、電子が多いと全体としては負、すなわちネガティブになる。
P型 (Positive)
http://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:P-doped_Si.svg
電子が足りていない半導体。電子がない分、相対的に正、すなわちポジティブになる。この穴のことを正孔といい、電流と同じ方向に移動する。
PN接合ダイオード
P型とN型を接合すると、片方には電子が余り、片方には電子が足りていない状態になる。
接合部付近では、電子と正孔が結合してしまうので、何もない部分ができる。これが空乏層。
ここで、P型側に+、N型側に-の電圧をかける (順方向) と、ダイオード内に正孔・電子が供給され、空乏層が狭くなり、電流が流れはじめる。
逆に、P型側に-、N型側に+の電圧をかける (逆方向) と、ダイオード内の正孔・電子が減少し、空乏層が電圧に応じて増え、電流は流れない。
無線局運用規則の読み方
アマチュア局は、第8章とそこから示されている第4章だけ読めばいい。ぼーっとしていて、ほかの章をうかつに読むと微妙に違う規則があって紛らわしいので、読みかたに注意する必要がある。
法規:特性周波数・割当周波数・基準周波数
ひととおり法規をやってみると、この3つがどうしても意味不明だった。
電波法施行規則 第二条
五十六 「割当周波数」とは、無線局に割り当てられた周波数帯の中央の周波数をいう。
五十七 「特性周波数」とは、与えられた発射において容易に識別し、かつ、測定することのできる周波数をいう。
五十八 「基準周波数」とは、割当周波数に対して、固定し、かつ、特定した位置にある周波数をいう。この場合において、この周波数の割当周波数に対する偏位は、特性周波数が発射によつて占有する周波数帯の中央の周波数に対してもつ偏位と同一の絶対値及び同一の符号をもつものとする。
五十九 「周波数の許容偏差」とは、発射によつて占有する周波数帯の中央の周波数の割当周波数からの許容することができる最大の偏差又は発射の特性周波数の基準周波数からの許容することができる最大の偏差をいい、百万分率又はヘルツで表わす。
|
割当周波数、特性周波数はなんとなくわかるような気がするけど、基準周波数の説明が意味不明すぎる。そのうえで、「周波数の許容偏差」を読むと、全く意味がわからない。
ググってみたり、よく理解に努めようとして要約すると以下のようになるようだ。
- 特性周波数 → 外部から測定できる周波数 (実際出てる電波の周波数)
- 割当周波数 → 免許状に書かれているやつ。アマチュアの場合帯域が広いのでイメージしずらい。許可された帯域の中央の周波数
- 基準周波数 → 無線機に表示されている周波数
こう考えたうえで
五十九 「周波数の許容偏差」とは、発射によつて占有する周波数帯の中央の周波数の割当周波数からの許容することができる最大の偏差又は発射の特性周波数の基準周波数からの許容することができる最大の偏差をいい、百万分率又はヘルツで表わす。
は言葉のイメージさえ掴めれば、あとよく読めば実用上(?)十分に理解できるレベルになる
- 発射した電波と割当周波数とを比べた値 (発射した電波が占有する周波数帯の中央の周波数、割当周波数)
- 外部から観測できる周波数と無線機が表示している周波数を比べた値 (特性周波数、基準周波数)
が、一定の許容範囲にあること。現実と理想を比べてる。搬送波ありの信号で考えたら言葉さえわかれば言ってることは難しくない。
しかし、「発射によつて占有する周波数帯の中央の周波数」と「特性周波数」の違いがわかりにくい。AM (A3E) の場合はどちらも一緒になりそうだけど、単側波帯の場合は違いそう。USB の場合上にずれるのかな。調べてもよくわからなかった。
Mac で simavr + avr-gdb を使い AVR プログラムを PC 上でデバッグする
Mac の場合 AVR Studio を使えないので、いろいろ不便である。しかし一応開発環境がととのった。結構ググったけど、このようにして開発している人は少ないのか、はたまた情報発信しないのか、あまり gdb まで使ってやってる記事がなかった。
CrossPack-AVR のインストール
avr-gcc (AVR 用のクロスコンパイラー) や avrdude (書きこみツール) の Mac 向けにコンパイル済みパッケージ。配ってるのでインストールするだけでいい。
CrossPack for AVR を普通にダウンロードしてインストーラーからインストールするだけ。
普通 OS X の path_helper によって自動的に PATH が通るので、そのまま Terminal.app で avr-gcc が実行できるようになる。path_helper のパーミッションを落として使ってる場合自力で /usr/local/CrossPack-AVR/bin のパスを通す。
これさえ入れておけば実機開発はできる。
simavr の build
まずレポジトリを clone する。
$ git clone git://gitorious.org/simavr/simavr.git $ cd simavr/simavr
simavr のビルドは CrossPack-AVR + Mac だと1発では通らない。以下のようにパスの指定が必要。あと、なんか変な状態になったりするので make clean と make を何回かやってみたほうがいいと思う。
make all V=1 AVR_ROOT=/usr/local/CrossPack-AVR AVR_INC=/usr/local/CrossPack-AVR/avr
これでシミュレーションまでできるようになった。
avr-project の Makefile 書き換え
CrossPack-AVR に avr-project というコマンドがついてくる。
$ avr-project Foo
とかやると Foo/firmware 以下に Makefile とかを作ってくれる。Foo.xcodeproj というのも作ってくれるけど、ハマりそうなので使ってない。
デバッグオプション付きでコンパイルするようにする。RELEASE=1 をつけない場合デバッグモード。最適化オプション -Os はどっちも付けてるんだけど、ちょっとどうすべきかわからない。デバッグ時 -Os をつけずに書きこむときだけ -Os をつけたらループが意図せず削除されたとき大変ハマりそうだなーと思う。いっそ -O0 のほうがいいのかもしれないけど……
ifeq ($(RELEASE),1) COMPILE_OPT = -Os else COMPILE_OPT = -Os -g3 -gdwarf-2 endif AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE) COMPILE = avr-gcc -Wall $(COMPILE_OPT) -DF_CPU=$(CLOCK) -mmcu=$(DEVICE)
でもってターゲットも足す
run: all ~/project/simavr/simavr/run_avr -g -mcu $(DEVICE) -freq $(CLOCK) main.hex & avr-gdb
さらに .gdbinit ファイルをつくる
file ./main.elf target remote localhost:1234 load break main continue
make run をするとコンパイルされて、simavr をバックグランドで起動し、それにGDB接続する、とりあえず main 関数の最初で止まる (はず)。
自分は avr-project は使っていなくて、自作のテンプレート作成ツールを使っている AVR のテンプレート
実際のデバッグ
普通に GDB を使うだけだけどメモ
- p foobar
- 変数を表示
- p/t PORTB
- PORTB を2進数で表示
- p set_bit(PINB, 1)
- PINB の 1 bit 目を立てる (set_bit は自分で定義したもの)
- 外部入出力をシミュレーションさせるのは結構骨が折れる感じなので、簡易的には、これで外部入力をシミュレーションできる
- 外部入出力とかはもうさっさと書きこんでデバッグしたほうがトータルで早いと思う…
実機書き込み
書き込み装置は、ここで変にハマると自分には問題解決できる能力がないところなので、純正の AVR ISP Mark II を使った。
この場合 Makefile の PROGRAMMER は以下のようになる
PROGRAMMER = -c avrispmkII -P usb
DEVICE はチップ名にあわせる。ATTiny2313 なら、そのまま DEVICE = attiny2313 でいける。
AVR ISP Mark II は 3x2 PINヘッダなので、このままだとブレッドボートで使えない。なので 1x6 PINヘッダに適当に変換してる。手元にあったピンヘッダがついてる余ってる平行ケーブルを使った。持ってるブレッドボートが狭くてつらい。
その他メモ
avr-gdb 上での _delay_ms() が非常に遅い。
これはいわゆるビジーループなので、動作周波数を下げることで早くデバッグすることができる。
make run CLOCK=10
勉強
アンテナの設計とか測定とかをしているうちに、もうちょっとちゃんと無線工学を学んでみようということで、いろいろやってる。
しかし、実際はじめてみると、そもそも中学生レベルの数学がぜんぜんできないことがわかったので、高校受験用中学生向けドリルを買ってきてやってみた。やってみると、解きかたが全くわからないというわけではなくて、うる覚えであったとしてもだいたい答えまでは導ける (あるいは単に考えて式をつくれる)。ただ、とにかくひどく計算ミスが多い。とにかく多い。分配法則を適用するとき文字を1つかけわすれるとか、符号を間違えるとか、本当にひどい。
原因を考えみると、数字の計算(だいたいは九九だけど、足し算・引き算も)に意識がいきすぎていて、ほかのことまで考えが及んでいない。つまり、九九がまともにできてない。大変、根本的な問題である。先日は計算中に 7 × 9 がパっと出てこなくてあせった。ひどいとしか言いようがない。
普段の生活だと、まず暗算に頼ることがない。第一に自分の計算能力を全く信用していないというのがあり、計算機が手元にないことがまずないので、2桁以上の数字の四則演算からして全部計算機に丸投げしてる。全く計算について考えないで生きてる。おかげでこのザマだと思う (小学校のとき体に沁みわたりまで九九をやらなかったのがそもそもだけど)。
教訓としては、九九は本当に大事なので100万回ぐらいやったほうがいいし、できないと人生が終わると全国のこどもたちに伝えたい。
Scala で JOGL (OpenGL)
Scala から JOGL (Java で OpenGL を使うやつ)
JOGL はどれをダウンロードしていいのかサッパリわからないが、jogamp-all-platforms.7z というのが全部入りっぽいのでこれをダウンロードする。このファイルのありかがそもそも見つけられないと思うけど、http://jogamp.org/deployment/jogamp-current/archive/ にある。ほんとどれをダウンロードしたらいいかサッパリわからないし、どこからこのページに辿りついたのかよくわからないけど、とにかくこのページから jogamp-all-platforms.7z をダウンロードすれば良い。jogamp って何?って感じだけどとにかくこれでいい。
ぐ〜ぐるで調べると、情報が古いのがたくさんでてくるのでつらい。これが公式?っぽいので、ここを見るのが良さそう。これまたこのページに辿りつくが難しい。
クラスパスには2つファイルを指定するだけでいい。これ以外はむしろ指定すると scala がぬるぽ出したりしてよくわからないことになる。横着して *.jar とかしてはいけない。
-classpath /path/to/jogamp-all-platforms/jar/gluegen-rt.jar:/path/to/jogamp-all-platforms/jar/jogl-all.jar
JOGL のチュートリアル にあるサンプルを移植すると以下のような感じになるっぽい。なんとなく awt ではなく newt を使うようにも変えてる。
チュートリアルだと animator.add() が余計でエラって動かない。あとなんかいろいろハマったけど忘れてしまった。とにかくこれで動く。
//#!scala -classpath /path/to/jogamp-all-platforms/jar/gluegen-rt.jar:/path/to/jogamp-all-platforms/jar/jogl-all.jar
import javax.media.opengl._
import com.jogamp.newt.event.WindowAdapter
import com.jogamp.newt.event.WindowEvent
import com.jogamp.newt.opengl.GLWindow
import com.jogamp.opengl.util.FPSAnimator
object Sketch {
def main (args: Array[String]) {
val glp = GLProfile.getDefault
val caps = new GLCapabilities(glp)
val window = GLWindow.create(caps)
window.setSize(300, 300)
window.setVisible(true)
window.setTitle("TEST")
window.addWindowListener( new WindowAdapter() {
override def windowDestroyNotify (e : WindowEvent) {
exit(0)
}
})
window.addGLEventListener( new GLEventListener () {
var theta : Double = 0
var s : Double = 0
var c : Double = 0
override def display (drawable : GLAutoDrawable) {
println("display")
update
render(drawable)
}
override def dispose (drawable : GLAutoDrawable) {
println("dispose")
}
override def init (drawable : GLAutoDrawable) {
println("init")
}
override def reshape (drawable : GLAutoDrawable, x : Int, y : Int, w : Int, h : Int) {
println("reshape")
}
def update () {
theta = theta + 0.01
s = Math.sin(theta)
c = Math.sin(theta)
}
def render (drawable : GLAutoDrawable) {
println("render")
val gl = drawable.getGL().getGL2();
gl.glClear(GL.GL_COLOR_BUFFER_BIT);
gl.glBegin(GL.GL_TRIANGLES);
gl.glColor3f(1, 0, 0);
gl.glVertex2d(-c, -c);
gl.glColor3f(0, 1, 0);
gl.glVertex2d(0, c);
gl.glColor3f(0, 0, 1);
gl.glVertex2d(s, -s);
gl.glEnd();
}
})
val animator = new FPSAnimator(window, 60);
animator.start();
}
}













