2016年3月31日木曜日

オシロ刷新

年度末仕事や無線以外でも色々と忙しない日々でしたが、やっと落ち着いてきたので気持ちを切り替える意味でオシロスコープを買い換えることにしました。

購入当時秋月で30,000円ちょっとという安値につられてOWON社製のポータブルDSOを使ってきましたが、エントリーモデルということもあってかディスプレイがSTNカラー液晶という前世紀的なパネルで、表示が非常に見づらいうえに応答性もよくないものでした。

それでも、電子工作には非常に重宝しいろんな場面で活躍してくれました。

また表示とは別に、帯域が25MHzと短波帯程度の高周波を観測するには少々心もとないと感じていたこともあり、この際もうワンランク上以上の機種に乗り換えようと考え、同じ秋月でOWON社製のDS7102という100MHz帯域モデルを購入しました。

プローブ較正は測定毎に行ったほうが良さそうです。周囲温度変化で結構ズレます
パネルはもちろんTFTでひとまわり大きめで非常に波形が観察しやすいです。しかも本体がかなり薄くて、机の上のスペースが若干広くなりました(笑)

で、早速Si5351Aユニットの出力波形を観察すると・・・

FFT表示や各種フィルター機能も搭載されています(使う場面は少ないですが)
 7MHzの矩形波がくっきり映っています。やはり帯域が広いのでこのくらいの周波数でも波形再現性が違いますね。

唯一のデメリットとしては、冷却用のファンの音でしょうか。以前のモデルはファンレスだったので結構気になります。でも測定のときだけ電源を入れるのでこの程度は良しとしましょう。

他にもFFTやらいろいろと機能が入っているようですが、それはおいおいと。

2016年3月21日月曜日

Si5351A VFO/BFO TRX制御システム進捗とPIC24Fプログラム備忘録

長いタイトルだ(笑)

PIC24Fを使ってSi5351A制御とCWトランシーバー用の制御システムを構築中ですが、ブレッドボードだと電気的、機械的に不安定になるため、この時点でユニバーサル基板で仮組みしてみました。

これだけで7MHzのQRPp送信機(但し高調波出まくり)
PICとSi5351Aモジュール、LCD、ロータリーエンコーダ、タクトスイッチを配置し、各々の端子とPICのポート間を被覆リード線などでつなぐ方式にして、ポート変更などにも対応できるように工夫しました。

あとは、信号出力端子、パドル入力端子、ICSP端子、電源端子を設けて電源は3.3Vの3端子レギュレータを介して各デバイスに3.3Vを供給しています。

 ソフトウエア的には、7MHz帯シングルバンドのCWトランシーバーを想定して送信用信号出力(CLK1,7.0MHz~7.2MHz)、BFO信号出力(CLK0,4MHz)、LO信号出力(CLK2,3.0MHz~3.2MHz)とし、送信時はCLK0,2出力を止め、受信時はCLK1出力を止めるようにしました。

周波数可変は10Hzと100Hzステップ切り替え式ですが、エンコーダの回転速度に応じてステップが大きくなるようにしています。また周波数変化時にはPLLの分周レジスタを計算しなおして設定しI2CでSi5351Aに送っていますが、受信機でモニターしている限りでは体感速度的にもたつきはありません。そのほかRIT機能、周波数メモリ関係も入れました。

キーヤープログラムはKeyer Mini-V2のコードを参考にしながら新たに書き直しています。IambicBモード固定で、WPMはエンコーダで設定します。(1WPMステップで10-60WPM)

また、サイドトーン(CWオフセット周波数に同期する)、システムビープ用にPWMモジュールを使用しました。

あとは、SメータやPowerメータ用にA/D変換とバー表示 を実装しています。

video

今のところここまでで、残るはメッセージメモリ関連と各種設定メニューを組み込めば大方完成です。

閑話休題。

 PIC24と8bitPICとでは、C言語プログラミングする上で移行しやすくなっていて有難いのですが、いくつか悩んだところがあったので備忘録的にまとめてみました。

1.コンフィギュレーション
 プログラムの頭のほうには各デバイスのコンフィギュレーションを記述するのですが、定番のRA5ポート(MCLR)以外にも設定を入れないとI/Oポートとして使えないピンがあります。
最初気がつかずRA4とRB4をI/Oとして設定しましたが、いずれも動作しませんでした。これらのピンにはセカンダリ発信器用の発振子を繋げるところでもあり、調べてみるとデフォルトで発振子のピンになっているようでした。このため、コンフィギュレーションでは明示的に設定文を記述しなければI/Oポートとしては使えません。(SOSCSRC = DIG)

PIC24FV32KA302 config文例:

#pragma config BWRP = OFF, BSS = OFF, GWRP = OFF, GSS0 = OFF
#pragma config FNOSC = FRCDIV, IESO = OFF, SOSCSRC = DIG, OSCIOFNC = OFF, POSCMOD = NONE
#pragma config FWDTEN = OFF, WINDIS = OFF, BOREN = BOR0, LVRCFG = OFF, PWRTEN = OFF
#pragma config I2C1SEL = PRI, MCLRE = OFF, DSBOREN = OFF, DSWDTEN = OFF

2.内部弱プルアップ指定
  ポートにスイッチをつなげるときなどに便利な内部プルアップで、部品点数を少なくするには都合の良い機能です。この設定についてはプルアップ以外にプルダウンなども指定できるICNレジスタ群というのがあり、プルアップの場合はCNPUxレジスタで目的のポートに相当するビットを1にすればよいのですが、ポート番号とCNxが同じではありません。ちょうどアナログチャンネルANxのように。
これも、ピン割り当て表とCNPUxレジスタ表をにらめっこして設定しなければならずいささか面倒です。

3.EEPROMの扱い
 もともとPIC24でEEPROMを内蔵しているものは少ないです。そのため内蔵していないPIC24ではプログラムメモリでEEPROMをエミュレーションして使う方法が記されています。しかし今回使っているPIC24FV32KA302では512バイトの本物のEEPROMを内蔵しています。エミュレーションでも良いのですが、書き替え耐用回数がまるで違うので本当のEEPROMにしたほうが安心ではないかと思ってこのモデルを選択しました。
EEPROMは16bit(2バイト=1ワード)を1つのアドレスに割り当てられているわけですが、実際は偶数のアドレスだけに割り振られています。理由はリファレンスにいろいろ書かれていますが(ファントムバイトがないとかなんとか・・・うーん、よくわかりません^^;)、とにかく偶数で割り当てられている(アドレスのLSBが常に0)ということを頭に入れてアクセスするということです。
XC8のように便利な読み書きマクロはみつからないので、リファレンス見ながらbuilt-in functionをつかって自分でマクロ関数や普通の関数を作らなくてはいけません。

関数例:

int __attribute__((space(eedata),aligned (2))) ee_data[] = {/*初期値(int型)*/};

//【EEPROM1ワード読み】
unsigned int eedata_read(int index){
    unsigned int offset;
    TBLPAG = __builtin_tblpage(&ee_data);
    offset = __builtin_tbloffset(&ee_data);
    offset += 2 * index;
    return __builtin_tblrdl(offset);
    }

//【EEPROM消去(1,4,8ワード選択)】 
void eedata_erase(int index, unsigned char words){
    unsigned int offset;
    switch(words){
        case 1: default:
            NVMCON = 0x4058;
            break;
        case 4:
            NVMCON = 0x4059;
            break;
        case 8:
            NVMCON = 0x405A;
            break;
            }
    TBLPAG = __builtin_tblpage(&ee_data);
    offset = __builtin_tbloffset(&ee_data);
    offset += 2 * index;
    __builtin_tblwtl(offset, 0);
    asm volatile ("disi #5");
    __builtin_write_NVM();
    while(NVMCONbits.WR){}
}

//【EEPROM1ワード書き込み】
void eedata_write(int index, int data){
    unsigned int offset;
    int temp;
    NVMCON = 0x4004;
    TBLPAG = __builtin_tblpage(&ee_data);
    offset = __builtin_tbloffset(&ee_data);
    offset += 2 * index;
    temp = data;
    __builtin_tblwtl(offset, temp);
    asm volatile ("disi #5");
    __builtin_write_NVM();
    while(NVMCONbits.WR){}
}

基本書き込み前に該当アドレスの内容を消去してから書き込みします。

 4.そのほかA/D変換も基本は8bitPICと同じですが、チャンネルの自動切換え設定や測定結果を格納するバッファが17個あったりといろんな場面を想定しているために設定項目が多く、どのように設定すればよいのか最初戸惑ってしまいます。


便利なライブラリがあればまぁ楽ですが、何かトラブルがあると結局ライブラリの中身を見なくてはいけない場合があったり、探しても見つからないときは既存のもので自分で出来る範囲で作り上げるしかありません。しかし、こうして回り道しようが格好悪かろうが何とかなったときの達成感は何ものにも替えがたいものがありますね。

2016年3月1日火曜日

自作Si5351AモジュールをPIC24で操る

2月の始めに秋月でPLL多出力クロックICのSi5351Aを取り扱うようになってから、周りでもぽつぽつ感心をもたれる方が出てきました。

自分も昨年Adafruit製のSi5351Aモジュールを購入して実験したっきりになっていましたが、少し調べてみるとすでにQRP CWトランシーバに応用された方がいらっしゃいました。刺激を受けて自分もそろそろ何か実用モノを作ろうと思い立ち準備始めました。

まず、試作に当たってSi5351Aと基準発振用のクリスタルを装着したモジュールを作りました。

もっともシンプルなSi5351Aクロックモジュール 材料費220円なり
 Si5351Aは秋月が取り扱い始める前にRSから取り寄せましたが、今ではクリスタルとMSOP10-DIP変換基板、細ピンヘッダを含めすべて秋月で揃えられます。

25MHzのクリスタルが小さくて装着がちょっと難しいですが、あらかじめ薄く端子をハンダ揚げするとうまくいくようです。Si5351Aはいつもの方法(薄くハンダ揚げしたランドにフラックス塗布してICの足を合わせて上から1ピンずつハンダこて先を当てるだけ)でバッチリハンダ付けできます。

以前PIC16F1827でブレッドボードに仮組みした試作VFOに装着していたAdafruitのモジュールを外して自作モジュール装着し、3.3V VDDにあわせるためプログラムを修正してPICに流し込み、無事に目的どおり発振するのを確認しました。


このままキーヤー制御などの機能を盛り込むつもりでしたが、PIC16F1827の残りのプログラム領域がやや心もとないため、上位モデルに移植しようと考えました。最初PIC18Fにしようかなと思いましたが、どうせならもっと上位ということでPIC24にしようと決めました。

入手性のことを考えて秋月にラインナップされているPIC24シリーズを探してみました。EEPROM内蔵のモデルでプログラムメモリが大きめのということで今回はPIC24FV32KA302を選択しました。

PIC24FV32KA302は16bitアーキテクチャでプログラムメモリが32KB、SRAMが2KB、EEPROMが512B、最高動作周波数32MHzで16MIPSなんだそうです。(64MHzのPIC18Fと同じ)

PIC16F1827用に書いたソースをPIC24用に書き替えてみました。


開発環境はMPLAB X IDE(3.10)にXC16コンパイラ(1.25)で、おもに割り込み関数と初期設定のポートやタイマー関連のレジスタを修正しビルドボタンを押すとコンパイルが通りましたが、ひとつだけ

si5351_pic24_010.c:80:50: warning: '_T1Interrupt' defined but not used

というワーニングが表示されていました。
PIC24Fではイベントごとに割り込みベクターが決まっていて、Timer1を使用した場合割り込みベクターテーブル(IVT)名は_T1Interruptで表記されます(Irq3)。

Timer1割り込みと表記しているのにもかかわらず使わないとはこれ如何に・・・?

最初考えずにプログラムをスタートさせたら、出力どころか表示すら出てこなくて非常に焦りました。

配線は問題なくSi5351Aの基準発振である25MHzは出ているようなので、やはりプログラムの問題のようです。一息入れてトラブルシュートに入ります。

まずモジュールやLCDを制御するI2Cの信号線をオシロスコープで観察すると、電源投入して何かしら信号が出ているのが確認されましたが、本来各モジュールの初期化が済んだ後はロータリーエンコーダを操作しない限りはレベルHighで静止するはずなのに、ずっと信号が出ている状態でした。よく見るとどうもリセットを延々繰り返しているようでした。

なぜそうなるのか見当が付かないため、プログラムの各要素をひとつひとつ外して検証する作業となりました。まず、タイマー割り込み部分を切り離したところ運よく発振して表示も出るようになりました。割り込み部分の問題ということがわかりましたが、なかなかこの先原因にたどり着くまで時間がかかってしまいました。

IRQ番号で直接指定してみたり割り込み関数内の記述を割り込みフラグクリア以外全部外して何もしないプログラムにしても、割り込み許可にするとやっぱりダメで、あれこれやっているうちに夜中も2時過ぎてしまいさすがに眠くなってきたので明日にしようと思いつつフッとコードを見ると・・・

static void __attribute__((interrupt(auto_psv))) _T1Interrupt(void){

 あ・・・XC8の割り込み関数表記 static void ~ のままだ・・

 前記のワーニングはこの static が原因なのかもと考え、早速外して再コンパイルしたらやっとまともに動き出しました。


8bitPICでは割り込みベクタのアドレスは08h番地で、PIC24では0008hに相当するベクタはAddress Error Trap Vectorとなりますが、staticを頭につけることによってこのアドレスに移動してリセットが繰り返されたのかなぁと想像しましたが、実際はどうなのでしょう・・・

追記:
どうやらXC8のマニュアルを見ても、割り込み関数前にstaticを記述することは必然ではないようです。
__attribute__との関連もありそうですが、この現象の成り立ちはまだよくわかりません

なにはともあれこのトラブルのおかげでPIC24の割り込みの部分、8bitPICとの違いがちょっぴりわかってきました。以前おなじ16bitアーキテクチャのdsPICを使用したJUMA TX-136/500ファームウエアはいろいろ弄っておりましたが、理解という意味では自分のプログラムで体現することが大事ですね。

そのほかEEPROMの扱いも8bitPICとは違うので実験続けたいと思います。