テープLEDでイルミネーション!


その2 マイコンのプログラム

ソースの開示は致しますが、少々長いのでプログラムの詳細説明はしません。 まあ、解説するよりソース見た方が早いでしょうし・・・

ダウンロード

ソースの DL は ”ここ
(Atmel Stuio 6.2 で作っています。)

バイナリ(HEX)の DL は ”ここ

<補足>

各ソースファイルで行っていること、補足説明にとどまらせていただきます。、 興味がある方はソースファイルをご覧ください。

(1) ファイルの説明

LEDDriver.c

メイン関数があるファイルです。
I/Oピン、ADC、タイマーなどの初期化処理、コマンドに応じた処理、EEPROM への保存、読込処理・・・ まあ、大抵ここに書かれています。

自分で書いておきながら、唯一把握できてないのが、 以下の部分・・・

//-----------------------------------------------------------
// ウォッチドッグを無効にする
//(初回に 15[ms] でウォッチドックがかかるのでこれで無効化しておく必要があるらしい)
//-----------------------------------------------------------
uint8_t mcusr_mirror __attribute__ ((section (".noinit")));
void get_mcusr(void) __attribute__((naked)) __attribute__((section(".init3")));

void get_mcusr(void)
{
	mcusr_mirror = MCUSR;
	MCUSR = 0;
	wdt_disable();
}

なにやら、ウォッチドックタイマーを使用する際の”おまじない”らしいです。 詳しくは理解していないけど、ヒューズでウォッチドックを有功にした場合、 main() で有効にしていなくても、ウォッチドックが実は動いていて、 結果永遠に起動できないケースがあるからなんだそうだ。

とりあえず入れときましょ。

<補足>

何でウォッチドック使っているかなんですが、まずあったほうがかっこいいw のと、 リセット機能を入れたからなんです。 AVR はソフトリセットの方法として、ウォッチドックタイマーを有効にして無限ループさせる。というのが作法らしいです。

usart.h, usart.c

RS232C の処理を行っているコードで、 割り込みとリングバッファで通信制御を行っています。

<補足>

確か”やねうらおさん”のとこを参考にしたんじゃないかなと思います。 で、オリジナルはバグありで、その旨がコメントされていました。 修正してあるので、バグなしのはずですw

cmd_parser.h, cmd_parser.c

RS232C からのコマンド解決を行っています。 基本ここを見れば、どんなコマンドがあるのかがわかります。

わかりにくいであろう部分が「cmd_parser.c」の以下の部分ですね。

// /////////////////////////////////////////
// コマンド文字列定義

const char PROGMEM CMD01_[] = { 'G', 'R', CR, LF, '\0' };
const char PROGMEM CMD02_[] = { 'G', 'G', CR, LF, '\0' };
const char PROGMEM CMD03_[] = { 'G', 'B', CR, LF, '\0' };

const char PROGMEM CMD04_[] = { 'S', 'R', '\t', '\t', CR, LF, '\0' };
const char PROGMEM CMD05_[] = { 'S', 'G', '\t', '\t', CR, LF, '\0' };
const char PROGMEM CMD06_[] = { 'S', 'B', '\t', '\t', CR, LF, '\0' };
	
const char PROGMEM CMD07_[] = { 'S', 'D', '\t', '\t', ',', '\t', '\t', ',',  '\t', '\t', CR, LF, '\0' };
const char PROGMEM CMD08_[] = { 'G', 'D', CR, LF, '\0' };
	
const char PROGMEM CMD09_[] = { 'G', 'M', CR, LF, '\0' };
const char PROGMEM CMD10_[] = { 'S', 'M', '\t', CR, LF, '\0' };

const char PROGMEM CMD11_[] = { 'G', 'P', CR, LF, '\0' };
	
const char PROGMEM CMD12_[] = { 'S', 'S', '\t', CR, LF, '\0' };
const char PROGMEM CMD13_[] = { 'G', 'S', CR, LF, '\0' };
	
const char PROGMEM CMD20_[] = { 'S', 'V', CR, LF, '\0' };
const char PROGMEM CMD21_[] = { 'L', 'D', CR, LF, '\0' };
const char PROGMEM CMD22_[] = { 'D', 'T', CR, LF, '\0' };
const char PROGMEM CMD23_[] = { 'R', 'T', CR, LF, '\0' };

ここで、受信するコマンドを決めています。 で、文字列内の¥t(タグ)の部分は、コマンド文字列比較の際に読み飛ばすようにしてあります。 要するに、¥tの部分に数値が入っていると決め打っているわけです。 (¥tの部分は無視して文字列比較を行うって、いったほうがわかりやすいかな。)

文字列比較はこの部分

//-----------------------------------------------------------
// コマンド文字列の比較
//-----------------------------------------------------------
int strcmp_cmd(const char* str, PGM_P cmd)
{
	int i = 0;
	
	while(1)
	{
		char c1 = *str++;
		char c2 = pgm_read_byte(&cmd[i++]);
		
		if(c1 == 0 && c2 == 0)
		break;
		
		if(c1 == 0 || c2 == 0)
		return -1;
		
		if(c2 == '\t')
		continue;
		
		if(c1 != c2)
		return -1;
	}
	
	return 0;
}

ごらんの様に415行目で、¥tがあった場合はcontinueしています。

<補足>
汎用で使用できるように作ったので、結構冗長なプログラムです。 あと、今見返してみると、なんで文字列定数を配列で宣言したんかなぁ?
直すのもめんどくさいし、そのままでいいやw
helper.h, helper.c

文字列→数値、その逆を行う関数など、お助け機能を書いてあるコードです。

rgb.h, rgb.c

受信した文字列から RGB を取り出す。または、返信用の文字列を生成する処理などを書いています。

<補足>
ファイル名にセンスがありませんねw
たぶん、「構造体のみを宣言できればいいや!」的に作ったヘッダに、 後からいろいろ機能を追加したためだと思いますw
std.h

プログラムで共通で使用するインクルードファイルをまとめて書いてあるだけです。

以上が、マイコン側のプログラムです。

「バグじゃね?」という箇所や、 「ここどうなってるの?」といった質問は歓迎ですので、 掲示板でも、メールででもお願いします。

今回は、ウォッチドックタイマ、クロックにセラロックを使用していますので、 AVR のヒューズ設定の変更を忘れずに行ってください。

具体的には、以下の様に設定してください。

ウォッチドック有効 WDTON をチェック
クロックの分周を無効化 CKDIV_8 のチェックを外す
外部オシレータを選択 SUT_CKSEL 一番下の EXTOSC_8MHZ_XX_16KCK_14CK_65MS を選択
<補足>

もし、AVR マイコンへの書き込み環境をお持ちでない方がいらっしゃいましたら、 これだけのために開発環境をそろえるのもなんでしょうし、 新品の IC を郵送していただいて、こちらでプログラムを書き込んだ後、 返信用封書で返信といった対応もできますので、 メールでご相談いただければと思います。

(郵送事故がおきた場合、 諦めて下さる方限定ですが・・・ まあ、そんな高いものでもありませんし、 諦められるでしょう・・・)

<補足>

メアドはこれですよ

zzyyxxntakaxxyyzz@aqua.dti2.ne.jp

ただし、@マーク前の、先頭の「zzyy」と後端の「yyzz」を削除してください。 そのまま載せると、ロボットサーチに引っかかってスパムだらけになるので、 ご了承下さいませ。

(2) 早速テストしてみよう!

マイコンのプログラムを書き込んだ時点で、

  • JP1 ショート
  • JP2 開放

で、各 LED は PWM の初期値 12.5[%] で点灯するはずです。

ボリュームを使っているバージョンで製作した方は、

  • JP1 ショート
  • JP2 ショート

で、ボリュームによるマニュアル調光で LED の調光がテストできます。

最後は JP1 を開放して LED が消灯すれば、 少なくても通信部分以外の回路は動作していることが確認できます。

(3) 通信テストしてみよう!

とりあえず通信できているかを確認するには、RS232C 通信できるソフトを使用してください。 私は、「シリアル通信テスタ Serister 1.2.5」というソフトを使わせていただきます。

まず、通信設定は以下の様にしてください。

ボーレート 38400
データ 8bit
パリティ なし
ストップビット 1
フロー制御 なし

コマンドの送信ですが、デリミタは [CR][LF]ですので、 送信文字列の最後に [CR][LF] を付加してください。

例えば、

GD[CR][LF]

と送信して、

OK,GD11,22,33[CR][LF]

のような文字列が受信できれば通信成功です。 もし、通信できない場合は、COM ポートとの接続を確認してください。

(4) 通信コマンド説明

主なコマンドは以下の様になっています。

お約束

  • コマンドは大文字
  • 数値は 16進数 で小文字
  • デリミタは送受信ともに[CR][LF]

エラーメッセージ

失敗した場合は以下の文字列が返されます。

NG,<エラーメッセージ>[CR][LF]

エラーメッセージは以下の通りです。

Buffer over flow! 受信バッファがオーバーフローした
Unkown command! 不明なコマンド
Invalid value! 数値文字列の異常
主なコマンド
  • 赤色LED PWM値設定
  • コマンド SRvv[CR][LF]
    説明 vv : 00 ~ ff
    赤色LED PWM値(00~ff で 0~100%)
    成功 OK,SR[CR][LF]
    赤色 LED を 25%(64 → 0x40) に設定
    SR40[CR][LF]
  • 緑色LED PWM値設定
  • コマンド SGvv[CR][LF]
    説明 vv : 00 ~ ff
    緑色LED PWM値(00~ff で 0~100%)
    成功 OK,SG[CR][LF]
  • 青色LED PWM値設定
  • コマンド SBvv[CR][LF]
    説明 vv : 00 ~ ff
    青色LED PWM値(00~ff で 0~100%)
    成功 OK,SB[CR][LF]
  • 一括PWM値設定
  • コマンド SDrr,gg,bb[CR][LF]
    説明 rr : 00 ~ ff
    赤色LED PWM値(00~ff で 0~100%)
    gg : 00 ~ ff
    緑色LED PWM値(00~ff で 0~100%)
    bb : 00 ~ ff
    青色LED PWM値(00~ff で 0~100%)
    成功 OK,SD[CR][LF]
  • 赤色LED PWM値取得
  • コマンド GR[CR][LF]
    成功 OK,GRvv[CR][LF]
    説明 vv : 00 ~ ff
    赤色LED PWM値(00~ff で 0~100%)
  • 緑色LED PWM値取得
  • コマンド GG[CR][LF]
    成功 OK,GGvv[CR][LF]
    説明 vv : 00 ~ ff
    緑色LED PWM値(00~ff で 0~100%)
  • 青色LED PWM値取得
  • コマンド GB[CR][LF]
    成功 OK,GBvv[CR][LF]
    説明 vv : 00 ~ ff
    青色LED PWM値(00~ff で 0~100%)
  • 一括PWM値取得
  • コマンド GD[CR][LF]
    成功 OK,GDrr,gg,bb[CR][LF]
    説明 rr : 00 ~ ff
    赤色LED PWM値(00~ff で 0~100%)
    gg : 00 ~ ff
    緑色LED PWM値(00~ff で 0~100%)
    bb : 00 ~ ff
    青色LED PWM値(00~ff で 0~100%)
  • 設定保存
  • コマンド SV[CR][LF]
    成功 OK,SV[CR][LF]
  • 設定読込
  • コマンド LD[CR][LF]
    成功 OK,LD[CR][LF]
今回は、ここまでです。 次回は、PC 側のプログラムについて説明します。