長いタイトルだ(笑)
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変換とバー表示 を実装しています。
今のところここまでで、残るはメッセージメモリ関連と各種設定メニューを組み込めば大方完成です。
閑話休題。
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個あったりといろんな場面を想定しているために設定項目が多く、どのように設定すればよいのか最初戸惑ってしまいます。
便利なライブラリがあればまぁ楽ですが、何かトラブルがあると結局ライブラリの中身を見なくてはいけない場合があったり、探しても見つからないときは既存のもので自分で出来る範囲で作り上げるしかありません。しかし、こうして回り道しようが格好悪かろうが何とかなったときの達成感は何ものにも替えがたいものがありますね。