Sim's blog

電子工作はじめてみました

エレキジャックNo.2の部品セット販売(マルツパーツ館)

2007-05-16 01:29:25 | 電子工作
トップページになかったので気づいていませんでしたが、マルツパーツ館でパーツセットの販売を開始しています。電子工作キット → 雑誌連動キットで見れます。

【エレキジャック No.2 サポート部品】DTMFと無線機で遠隔操作をしてみよう(デコーダのみ) 4600円
【エレキジャック No.2 サポート部品】LEDを交互に点滅させてみよう! 380円
【エレキジャック No.2 サポート部品】エアロアールシー・アクセラレータの製作 2280円
【エレキジャック No.2 サポート部品】ソフトウェア・ラジオ用PLL発振器を作ってみよう 1560円
【エレキジャック No.2 サポート部品】トイ・カメラのシャッタをラジコン・プロボで操作しよう 450円
【エレキジャック No.2 サポート部品】トイ・トレインからのライブ中継にチャレンジ 1250円

PIC書き込み器とかブロックくずしはないですね。DV4661というトイカメラは現在秋月で売っていないようです。というか売り切れたっぽいですね。

MR16

2007-05-15 00:12:53 | FPGA
Interface 6月号にはMR16というFPGA用のオリジナルCPUの話が載っています。筆者の方が公開されているそうなのですが、記事を見てもどこで公開しているのか分かりません。MR16とFPGAでググったら見つかりました(ここ)。

5/20追記 バグがあったそうで、現在ソース自体は公開されていないそうです。早くバグが取れるといいですね。

NTSC用の抵抗値(続き)

2007-05-14 23:42:08 | 電子工作
昨日のは、black = white = 1のとき1Vを超える危険性がありました。今日はちょっと作り方を変えます。
sync = data = 0で同期信号(0V)、sync = 1のときはdata = 0を黒(0.286V)、data = 1を白(1V)にします。
まじめに計算するとR1=603.147Ω、R2=241.597Ωです。計算式は次のようになります。Vが電源電圧、V1が白の電圧、V2が黒の電圧です。

R1 = 75 * (V - V1) / V2
R2 = 75 * (V - V1) / (V - V2)

R1 = 680Ω、R2 = 270Ωのとき、V1 = 0.923V(白)、V2 = 0.262V(黒)になって、なかなかいい感じです。

FuwaFuwa FactoryさんはV=5V、R1 = 1kΩ、R2 = 470Ωを使っています。黒が0.304V、白が0.950Vです。

エレキジャックNo.2の「PICでテレビ・ゲームを作ってみよう!」はトランジスタを使っています。SWCAD IIIでシミュレートしてみると黒のときが0.23V、白が0.81Vくくらいです。ただし2SC1815のモデルがなかったので2N2222で代用した結果です。

とりあえず抵抗値はなんとなく分かったけど、信号のタイミングがよく分かってないです。インターネットを漁りながら勉強しようと思っています。

NTSC用の抵抗値

2007-05-13 22:41:14 | 電子工作
NTSCは3種類の電圧を出力するそうです。同期信号の間は0V、黒が0.286V、白が1Vということみたいです(参考 なひたふさんのHP)。V850だとポートの出力が3.3Vなので抵抗を使ってほしい電圧を作ります。抵抗2本でできるのですが抵抗値はいくつにすればいいのでしょう。

まじめに計算してみるとR1=528.1469Ω、R2=151.05Ωになるようです。もっとも白は1Vでなくてもいいし、黒も0.286Vより少しくらい少なくてもよいです。

TIアプリケーションノートslaa177では、3.6Vの電源でR1=1100Ω、R2=200Ωにしています。この値だと黒は0.17V、白は0.935Vになります。1100も200もE24系列です。

3.3Vだと、R1=680Ω、R2=180Ωにすると黒は0.238V、白は0.9Vとなっていい感じです。680はE6系列、180はE12系列です。

この結線だとblack=white=0のとき同期信号、black=1,white=0のとき黒、black=0,white=1のとき白ということになります。black=white=1は白の電圧を超えてしまい、場合によっては1Vを超えるので禁止です。

Vを電源の電圧、V1を黒のときの電圧、V2を白のときの電圧とすると抵抗値の式は
R1 = 75 * (V - V1 - V2) / V1
R2 = 75 * (V - V1 - V2) / V2
になります。

どっちかというと、えいやで決めちゃってもいいようなもんなんですが、式を作ったりエクセルで計算するのがおもしろくって、ついつい遊んでしまいました。

リアルタイム出力に出力してみる

2007-05-12 00:31:02 | V850
V850にはリアルタイム出力という機能があります。これはタイマー割り込みのタイミングでハードウェア的に事前に設定していた値をポートに出力する機能です。

リアルタイム出力を使わないやり方だと割り込みが発生してからソフトで何か出力するわけですが、割り込み処理が実行されるのは、割り込み要因が発生した時に実行している命令が終了してからなので、その命令の実行クロック数によっては微妙にタイミングがずれます(ジッタといいます)。リアルタイム出力では、割り込み要因が発生した瞬間に出力してしまうのでジッタが生じないことになります。精密なハードウェアの制御が必要なときに使えそうな機能です。ユーザーズ・マニュアルの12章に説明が載っています。ユーザーズ・マニュアルではステッピングモーターの制御に最適と書かれています。

リアルタイム出力自体は4通りの出力形態を選べます。
(1) 6bit x 1ch
(2) 4bit x 1ch
(3) 2bit x 1ch
(4) 4bit x 1ch + 2bit x 1ch
6bit x 1chにすると、6bit幅の出力をリアルタイムに行うことができます。このとき6つポートを占有してしまいます。リアルタイム出力はポート5と兼用になっています。RTP0~RTP5がポートの名前です。4bit出力は下位のRTP3~RTP0、2bit出力は上位のRTP5~RTP4です。ポート機能の切り替えは正しく行わなければいません。1bitだとかそういうのは駄目みたいです。

ピン配置は次のようになっています。
P50/RTP00 CON2 10pin
P51/RTP01 CON2 12pin
P52/RTP02 CON3 4pin
P53/RTP03 CON3 5pin
P54/RTP04 CON3 2pin
P55/RTP05 CON3 3pin

リアルタイム出力は、単体では機能しなくて必ずタイマー割り込みとセットで使わなければいません。タイマー割り込みは例えばインターバルタイマーTMP4のINTTP4CC0とかになります。どのタイマーを使用できるかはある程度制限があって自由に選べるわけではないようです。

タイマー割り込みとインターバルタイマーを使ったLEDチカチカを作ってみます。2つのLEDを交互に点滅させます。P50(CON2の10pin)にLEDのAを接続してLEDのKを1kΩくらいの抵抗を介してGNDにつなぎます。P51(CON2の12pin)にも同じようにLEDを接続します。あいかわらずブレッドボードです。本当はオシロとかの波形を見るのがいいのでしょうね。

appliletで雛形を作ります。
- システムはウォッチドッグタイマー2を使用しない、オンチップディバグを使用する。
- タイマーはタイマーP4をインターバルタイマーにして、詳細ボタンで、設定単位をms、TMP4とCCR0の一致で割り込み発生を選びます。インターバル時間を変えることで点滅速度を変更できます。
- リアルタイム出力は6ビットx1チャネルを選びます。

メインはこんな感じです。初期設定して後は無限ループです。
unsigned char val = 0x15;

void  main( void )
{
    RTO0_Output(val);   // 出力予定の初期値
    RTO0_Enable();      // リアルタイム出力許可
    TMP4_Start();       // タイマー4開始

    while(1) __halt();  // 無限ループ
}


タイマー割り込みはTIMER_user.cに作られています。
__interrupt void MD_INTTP4CC0( void )
{
    extern unsigned char val;
    val ^= 0x3f;        // 反転
    RTO0_Output(val);   // 次に出す予定の値を設定
}

割り込み処理では、既に値が出力されているので、次に出力する予定の値をセットしておきます。

このリアルタイム出力は他のマイコンにないV850独自の機能だと思います(知らないだけかもしれませんが^^)。

A/D変換のばらつき

2007-05-10 01:38:00 | V850
V850のA/D変換の値のばらつき測定してみました。サンプル数は1000で変換時間が最短の3.25usと最長の23.3usの2つの場合についてヒストグラムを書いてみました。グラフはExcelで作りました。きれいなツリガネ型になっています。変換時間による差はあまりなさそうです。
変換時間  3.25us   23.3us
--------------------------
平均値   333.748  334.642
標準偏差   2.557    2.479
最小         320      319
最大         348      348

実測値はVccが3.283V、入力電圧が1.065Vでした。1.065/3.283 * 1024 + 0.5 = 332.684なので、ほとんど合っているような気がします。

3軸加速度センサーにつないでみる

2007-05-09 00:49:25 | V850
秋月で売っている3軸加速度センサーKXM52-1050モジュールをV850付録基板につないでみました。このセンサーは2.7~5.5Vで動作します。出力は3.3V動作の場合、1.65Vを中心に上下660mV動きます。昨日のA/D変換を応用すれば電圧を数値にすることができます。

ブレッドボードの上に載っている8pinの基板が加速度センサーです。秋月ではチップを載せた基板の形(完成品)で売っています(800円)。パスコンとかも実装済みなので外付け部品なしで使えます。

加速度センサーということになっていますが、実際は重力センサーというか傾き角度センサーです。

値の変化は例えばx軸だと、傾けた角度が0度のときは1.65Vで90度までは増加して2.31Vになります。その後180度まで減少して1.65Vにさらに減少して270度では0.99Vになり、今度は増加して0度で1.65Vに戻ります。
  0度         1.65V
  0度~ 90度  増加
 90度         2.31V (1.65V + 0.66V)
 90度~180度  減少
180度         1.65V
180度~270度  減少
270度         0.99V (1.65V - 0.66V)
270度~360度  増加
360度         1.65V

y軸も似たような感じですが、z軸は上向きが2.31Vで下向きが0.99Vになっています。ニュートラルの状態が1.65Vです。この値は電源電圧のちょうど半分みたいです。増減するのは見ていますが、角度に比例して変化するかどうかはちゃんと調べていません。

LCDの表示は1列目がA/D変換から読み取った値を16進4桁でx y zの順に表示しています。2列目はmVに変換した10進4桁を同じくx y zの順に表示しています。3列目と4列目は偶数番目の測定結果です(1,2列目は奇数番目)。ブレッドボードを持ち上げて傾けると、傾けた角度に応じて値が変化します。

配線はAN0(CON2の1pin)がx軸(6pin)、AN1(CON2の2pin)がy軸(7pin)、AN2(CON2の3pin)がz軸(8pin)です。

A/D変換のモードはワンショットスキャンモード(複数チャネルを一回だけ変換するモード)を使っています。appliletでは0-2のように複数の(0からはじまる)チャネルを選択できます。モードが違うので動作も多少違います。割り込みは変換が全チャネル完了したときにはいります。変換結果の読み取り(AD_Read)も配列を渡すと一気に書き込んでくれます。実際には同時に変換しているわけではなくて1つずつ変換しています。わずかな時間差なので、ほぼ同時に変換していることになります。

メインはこんな感じです。昨日と違うのは、変換値を複数受け取るので配列にしたところと、結果の表示の部分だけです。
#define MAX_VOLT 3250            // 実測3.25V
volatile short rdy;

void  main( void )
{
    unsigned short val[3];       // A/D変換結果
    int i, v;

    lcd_init();                  // LCD初期化

    while(1){
        // A/D変換
        rdy = 0;
        AD_Start();              // 変換開始
        while(!rdy) ;            // 変換完了まで待つ
        AD_Read(val);            // 変換結果を読む

        // 結果表示
        for(i = 0; i < 3; i++){
            lcd_puthexs(val[i]); // 変換結果の16進出力
            lcd_putc(' ');
        }
        lcd_puts("     ");

        for(i = 0; i < 3; i++){
            v = (MAX_VOLT * (int)val[i]) >> 10; // mVに変換
            lcd_putdec4(v);      // mV値の10進出力
            lcd_putc(' ');
        }
        lcd_puts("     ");
    }
}

ソース中に'<'や'¥'があると化けたりしますね。'&lt;'とかにするのが面倒です。

A/D変換してみる

2007-05-08 00:44:08 | V850
V850には10bit分解能のA/D変換があります。チャネルは12個あるみたいですが、A/D変換自体は1つで切り替えながら使うようです。

A/D変換の説明はユーザーズマニュアルの13章に載っています。色々な変換モードがあるみたいです。
トリガーモードは、ソフトウェアトリガ、外部トリガ、タイマトリガの3つから選びます。
動作モードは連続セレクト、連続スキャン、ワンショットセレクト、ワンショットスキャンの4つから選びます。スキャンは複数ポートの変換、セレクトは単独ポートの変換、ワンショットは1回だけ変換して次のトリガを待ちます。連続は変換が終わったら次の変換を自動でします。

とりあえずappliletで作ってみます。たぶん一番単純そうなソフトトリガ(開始はソフトから指定)、ワンショットセレクトモード(指定したポートを1回だけA/D変換)、通常変換(高速変換もある)、(ポーリングの仕方が分からないので)A/Dの割り込み許可を選びました。APIはAD_Init(), AD_Start(), AD_Stop(), AD_Read()を使うようです。

半固定抵抗で分圧した値をアナログ入力端子チャネル0(AN0)に入力してみます。AN0はCON2の1pinです。半固定抵抗なのでドライバーで回すと電圧が変わります。

appliletは割り込み処理ルーチンをAD_user.cの中に作ってくれます。A/D変換が完了したという割り込みなので変換終了を通知するだけです。
__interrupt void MD_INTAD( void )
{
    extern volatile int rdy;
	
    rdy = 1; // 変換終了
}

メインは、A/D変換してから結果を表示する無限ループです。100ミリ秒待っているのは、表示が速すぎてLCDが読めないからです。
#define MAX_VOLT 3250      // 実測3.25V
volatile int rdy;          // 変換完了フラグ

void  main( void )
{
    unsigned short val;
    int v;

    lcd_init();            // LCD初期化

    while(1){
        rdy = 0;
        AD_Start();        // 変換開始
        while(!rdy) ;      // 変換完了まで待つ
        AD_Read(&val);     // 変換結果を読む

        v = (MAX_VOLT * (int)val) >> 10; // mVに変換

        lcd_puthex4(val); // 変換結果の16進出力
        lcd_putc(' ');

        lcd_putdec(v);    // mV値の10進出力
        lcd_puts("mV        ");
        
        ms_wait(100);     // 100m秒待つ
    }
}


LCDの左側の16進4桁が読み取った値です。10bitなので000~3ffの範囲になります。右側の10進5桁はmVです。1画面に4つ履歴が残るようになっています。テスターの値と較べると上位2桁くらいが信用できそうです。下の方の桁は結構ばらついています。appliletにあった変換時間を長くすると精度があがるのかもしれません(今回は最短の3.25u秒)。

A/D変換はすぐできたのですが表示を作るのに手間取りました。LCDはあちこちアドレスを動かしながら動かすとなぜか表示位置が乱れてしまいます。文字だけを出力しているぶんには乱れないので、無駄な空白を出力しています。busyを見たり、ちゃんとwaitを入れたりすると乱れないのかもしれません。

ADPCMでしゃべらせてみました(続き)

2007-05-06 18:50:35 | V850
前回はトランジスタで作ったpush-pull回路をアンプにしていました。誌面と同じLM386というオーディオアンプ用ICを使ったアンプに変えてみました。以前のはどちらかというと蚊のささやくような感じでしたが、今度はちゃんと聞こえます。なかなかいい感じです。

LM386Nは秋月、フィルムコンデンサーはマルツパーツで入手しました。

シリアルから読んでLCDに出力してみる(続き)

2007-05-06 17:47:19 | V850
昨日の続きです。
割り込み処理を書き換えないでコールバックだけでやってみました。2つ方法があって、一つはコールバックルーチンの中で独自のキューを作る方法です。この方法は割り込み処理の中にあるバッファ書き込み処理とキューへの書き込みで二重の処理をしていることになり効率が悪いという問題があります。
もう一つの方法は効率はそんなに悪くならないのですが、appliletの生成するコードに依存しているのでappliletのバージョンが変わって生成コードが変更されたりすると使えなくなってしまいます。

後の方のコードです。
void CALL_UART2_Receive( void )
{
    extern UCHAR *UART2_RX_ADDRESS;
    extern USHORT UART2_RX_CNT;

    if(UART2_RX_ADDRESS == q + QSIZE) UART2_RX_ADDRESS = q; // ラウンドアップ
    UART2_RX_CNT = 0;    // 受信しなかったことにする。
    q_ctr++;
}

元々、割り込み処理ではUART2_RX_ADDRESSで指定されるバッファに書き込んでから読み込んだ文字数UART2_RX_CNTを1増やしています。UART2_RX_CNTを増やさなければ割り込み処理ルーチンは受信していないと勘違いしてくれます。このままだと受信したデータがどんどんメモリーを食いつぶしてしまうので適当な所でラウンドアップ処理をしてやります。

あまり気持ちよく受信キューを作れませんね。NECさん的には最低限の一番シンプルなコードを出すのがappliletだという所でしょうか。

シリアルから読んでLCDに出力してみる

2007-05-05 14:15:42 | V850
昨日、ごださんから「受信したデータをLCDに表示するような動作はできていますか?」というコメントをいただきました。なるほど、学習コースとしては、LEDチカチカ → LCD出力 → シリアル入出力ときて、シリアル+LCDというのは今までの総仕上げとしてはなかなかよさそうです。

あいかわらずブレッドボードです。配線が多くなってきて面倒なので、このくらいまでくると母艦がほしくなってきます。

appliletが生成するファイルは後で生成しなおしたときに上書きされるので、あまり手を入れたくないのですが、今回は割り込み処理ルーチンを書き換えます。
受信割り込みをキューで受けるようにします。serial.cの一部です。受信キューはq[ ]という配列にしています。
int q_putp = 0;

__interrupt void MD_INTUA2R( void )
{
    unsigned char err = UA2STR;
    unsigned char c = UA2RX;

    if((err & 0x07) != 0 || q_ctr == QSIZE){
        PCT.6 = 0;      // LED点灯
    } else {
        q[q_putp] = c;  // キューに書き込む
        q_putp = (q_putp + 1) & QMASK;  // ポインタを進める
        q_ctr++;        // キューの中身が1つ増えた
    }
}

キューのラウンドアップ処理を速くするためにキューのサイズを2べきにしてアンドでラウンドアップしています。キューのサイズは16でQMASKは15にしています。エラーが起きたりキューが足りなくなったときはLEDを点灯します。まずないとは思いますが、受信データが壊れないように最初に変数で受けています。

メインはこんな感じです。キューが空でないときは1文字取り出してLCDとシリアルに出力する無限ループです。LCDへの出力はスクロールすると重そうなので一番下まできたら上に戻るようにしています。
void main(void)
{
    unsigned char c;

    lcd_init();            // LCD初期化

    while(1){
        if(!empty()){      // キューが空でないときは
            c = q_get();   // キューから取り出す
            UART2_SendData(&c, 1); // エコーバック
            lcd_putc(c);   // LCDに1文字出力
        }
    }
}


キューからの取り出しはこんなかんじです。q_ctr--;の前後を__DI()と__EI()で囲った方がいいかもしれません。アトミックな処理でなかったときはカウンターの値が壊れてしまいます。
unsigned char q_get()
{
    unsigned char c;

    if(q_ctr == 0){     // アンダーフロー
        PCT.6 = 0;      // LED点灯
        c = 0;
    } else {
        c = q[q_getp];  // 1文字取り出す
        q_getp = (q_getp + 1) & QMASK;  // ポインターを進める
        q_ctr--;        // キューの中身が1つ減った
    }
    return c;
}


さて動かしてみた結果です。データの送信にはTera Termを使いました。Tera Termはalt+vでクリップボードの中身を一気に送信してくれるます。115200bpsだとデータの取りこぼしがかなりありました。処理が追いついていないせいだと思います。9600bpsまで送信速度を落としてやると、取りこぼしもなくなりました。送ったデータは数kバイト程度あるテキストです。

処理が間に合わないときはフロー制御を使って送信を待ってもらうのが筋のような気もしますが、やり方がよく分かりません。配線がないのでハード制御はできないので自然とXON/XOFF制御ということになると思いますが、XONとかXOFFのデータそのものを送るときはどうするんでしょうね?そもそも送ってはいけないのかな。おいおい調べてみることにします。

組み込み系っていうのかな。こういうプログラムはあまり書いたことがないので変なことをしているかもしれません。とりあえず思ったことは、必ず変数に一旦コピーしてから使うということです。割り込みとか色々な原因で勝手に書き換わってしまうかもしれないからです。

appliletでUARTA2に出力してみる

2007-05-04 15:51:32 | V850
V850は3つのシリアルポートを使うことができます(UARTA0~UARTA2)。デバッグするときはUARTA0を使うのでシリアルポートを使うプログラムのデバッグができない問題があります。そこでシリアルポートをUARTA2に増設してみました。

UARTA0を使う方法については本誌5月号の7章、appliletを使う方法についてはjsskさんがレポートされています(4/19の記事)。

シリアル基板の作り方についてはPC watch記事(槻ノ木隆のPC実験室 玄人志向「玄箱PRO」を使う【改造編1】)とごださん記事(シリアル基板を作ろう)を参考にしました。どちらの記事も秋月電子で売っているレベル変換用のIC、ADM3202ANを使っています。このICは3.3Vでも動作するので便利です。2つの記事の違いはダンピング抵抗をつけているかいないかくらいです。ADM3202はチャージポンプ用のスイッチングノイズがあるので気になる方はダンピング抵抗をつけておくといいかもしれません。

UARTA2の送信TXDA2はpin38でCON2のpin14に接続されています。受信RXDA2はpin39でCON2のpin11です。ちなみにUART1はTXDA1がpin90でCON1のpin1に、RXDA1がpin91でCON1のpin2に接続されています。

デバッガがポートを間違えないようにするために「ID850QB-MON V3.3 Portconfig」というプログラムでデバッグ用のポート番号を設定します。デフォルトはAUTOになっています。

試しに1文字受信するたびにLEDが反転してエコーバックするプログラムを作ってみます。

appliletの設定です。
- システムの基本設定はサブクロックとウォッチドックタイマー2を使用しない、オンチップディバグ設定は使用するにします。
- ポートはPCT6を出力にします(LED用)
- シリアルはUARTA2を使用するに変更すると、UARTA2という新しいタブができます。UARTA2のタブでは送信/受信、8bit、ボーレートは115200を選びます。割り込みレベルは受信を6、送信を7にしました。コールバック機能設定の受信完了もチェックしておきます。

appliletで作ると自動でAPIを生成してくれます。UARTA2については送信用のUART2_SendDataと受信用のUART2_ReceiveDataです。どちらもブロックしてくれないので完了待ちをしたいときは受信完了のコールバック関数CALL_UART2_Receiveを利用します。この関数はSERIAL_user.cの中に生成されています。最初は空なので受信完了通知のコードを2行追加しました。
void CALL_UART2_Receive( void )
{
	extern volatile int rxrdy;
	rxrdy = 1;
}

今回は横着して受信完了コールバックだけ使いましたが送信完了コールバックも使ってもよかったかもしれません。受信はブロックするけど送信はブロックしていないので次の文字の受信と送信が時間的にかぶっていることになります。
一応、これらのコールバック関数は割り込み処理ルーチンから直接呼ばれているので、あまり重いことはしないほうがよさそうです。送受信APIや割り込み処理ルーチンはSERIAL.cにあるので何をしているかは読めば分かるようになっています。

メイン関数は次のようにしました。1文字受信したらエコーバックしてLEDを反転しています。
volatile int rxrdy;                  // 1のとき受信完了

void  main( void )
{
    unsigned char rbuf, wbuf;        // 通信用バッファ

    while(1){
        rxrdy = 0;
        UART2_ReceiveData(&rbuf, 1); // 受信
        while(!rxrdy) ;              // 受信完了待ち
        wbuf = rbuf;                 // 
        UART2_SendData(&wbuf, 1);    // 送信
        PCT.6 ^= 1;                  // LED反転
    }
}

appliletを使うとCPUやポートの設定のコードを書かなくてすむのが楽です。