マイコン制御によるSWR計のリモート化その4 ― 2015年05月25日 19:52
はじめにお読みください。→当サイトのリンクと免責事項
R8Cの開発はWEBに沢山あるのでそれを見ればほとんど出来上がります。またメーカサイトにはほとんどのサンプルがあります。
ADコンバータとシリアル通信はここを参考にしました。ADコンバータとシリアル通信の送信は割り込みを使っていません。下のコードはADCとUARTの初期化ルーチンです。
///=================================================================*
// Function Init_ADC ADCの初期化 割り込み未使用
///=================================================================*
void Init_ADC(void)
{
// ====================================
// ADCON0の設定
// ====================================
md = 0; //単発モード
ch2 = 0; //
ch1 = 0; //
ch0 = 0; // AN0指定
adgsel0 = 0; //AN8-AN11使用不可
adst = 0; //A/D変換停止
cks0 = 0; //
cks1 = 0; // f4指定
bits = 0; // 8bit mode
vcut = 1; //VREF接続
// ====================================
// ADCON2の設定
// ====================================
// adcon2 = 0;
smp = 0; //サンプル&ホールドなし
}
///========================================================================================*
// Function Init_UART UART初期化 割り込み受信使用
///========================================================================================*
void Init_UART(void)
{
u0brg = 0x81; //ビットレート(9600bps)
smd0_u0mr = 1; //
smd1_u0mr = 0; //
smd2_u0mr = 1; //8ビット長
ckdir_u0mr = 0; // 外部クロック未使用
stps_u0mr = 0; //1ストップビット
prye_u0mr = 0; //パリティなし
clk0_u0c0 = 0; //
clk1_u0c0 = 0; // f1
nch_u0c0 = 0;
ckpol_u0c0 = 0;
uform_u0c0 = 0;
te_u0c1 = 1; // 送信可
re_u0c1 = 1; // 受信可
// ************************************
// UART受信割り込みの設定
// ************************************
s0ric = 0x0d; // 割り込み有りレベル6
}
// Function Init_ADC ADCの初期化 割り込み未使用
///=================================================================*
void Init_ADC(void)
{
// ====================================
// ADCON0の設定
// ====================================
md = 0; //単発モード
ch2 = 0; //
ch1 = 0; //
ch0 = 0; // AN0指定
adgsel0 = 0; //AN8-AN11使用不可
adst = 0; //A/D変換停止
cks0 = 0; //
cks1 = 0; // f4指定
bits = 0; // 8bit mode
vcut = 1; //VREF接続
// ====================================
// ADCON2の設定
// ====================================
// adcon2 = 0;
smp = 0; //サンプル&ホールドなし
}
///========================================================================================*
// Function Init_UART UART初期化 割り込み受信使用
///========================================================================================*
void Init_UART(void)
{
u0brg = 0x81; //ビットレート(9600bps)
smd0_u0mr = 1; //
smd1_u0mr = 0; //
smd2_u0mr = 1; //8ビット長
ckdir_u0mr = 0; // 外部クロック未使用
stps_u0mr = 0; //1ストップビット
prye_u0mr = 0; //パリティなし
clk0_u0c0 = 0; //
clk1_u0c0 = 0; // f1
nch_u0c0 = 0;
ckpol_u0c0 = 0;
uform_u0c0 = 0;
te_u0c1 = 1; // 送信可
re_u0c1 = 1; // 受信可
// ************************************
// UART受信割り込みの設定
// ************************************
s0ric = 0x0d; // 割り込み有りレベル6
}
AD変換、DDSの周波数更新、シリアルデータ送出はタイマー割り込みを使い0.5秒ごとに処理にしています。シリアル通信の受信(PC->SWR計)はこの0.5秒ごとにポーリングしても良いのですが、ほとんど来ないので受信だけ割り込みで処理しています。下のコードはUART受信割り込みのルーチンです。
//========================================================================
//UART0 RCV 割り込み処理
//========================================================================
#pragma interrupt UART0_RCV(vect=18)
void UART0_RCV( void )
{
char error;
error = u0rbh & 0xf0; // エラーデータ
if (error == 0x00 ){
UART_DATA = u0rbl;
}
re_u0c1 = 1; // 受信可
}
//UART0 RCV 割り込み処理
//========================================================================
#pragma interrupt UART0_RCV(vect=18)
void UART0_RCV( void )
{
char error;
error = u0rbh & 0xf0; // エラーデータ
if (error == 0x00 ){
UART_DATA = u0rbl;
}
re_u0c1 = 1; // 受信可
}
I2C液晶は1文字表示に10msはかかるので8文字2行で160msはかかります。割り込みで待つほうが賢いでしょう。この例は1msごとにタイマー割り込みで処理しています。
例はR8C/38A用なのでSFRレジスタの宣言ファイルを"sfr_r838a.h"から"sfr_r825.h"に変更します。またポートの入出力設定のピンを変更します。R8C/38AのSDAはP3_7ですがR8C/25はP3_4です。
DDSですが割り込みは使わずSPIのインターフェースをポートの入出力で書きます。
発振周波数が数百Hzから5kHz ですので28BitのFREQREG値の最大は
5kHz = fMCLK /2^28 * FREQREG = 0.074505806 * FREQREG ∴ FREQREG = 67109 = 0x10625
この値は14ビットを超えます。ですからMSB14bitとLSB14bitの両方を毎回書き込む必要があります、
DDSは2つありますのでP2ポートの0,1,2と3,4,5を使っています。
DDSは2つありますのでP2ポートの0,1,2と3,4,5を使っています。
//******************************************************************************************
// Function Name : INIT DDS
//******************************************************************************************
void INIT_DDS(void)
{
DDS_DATA = 0x2100; //コントロールレジスタ B28=1 RESET=1
DDS_WRITE(0);
DDS_WRITE(1);
DDS_DATA = 0x4625; //FRE =( 20MHz / 684,354,560 )* 0x10625 = 5KHz
DDS_WRITE(0);
DDS_WRITE(1);
DDS_DATA = 0x4004; //
DDS_WRITE(0)
// Function Name : INIT DDS
//******************************************************************************************
void INIT_DDS(void)
{
DDS_DATA = 0x2100; //コントロールレジスタ B28=1 RESET=1
DDS_WRITE(0);
DDS_WRITE(1);
DDS_DATA = 0x4625; //FRE =( 20MHz / 684,354,560 )* 0x10625 = 5KHz
DDS_WRITE(0);
DDS_WRITE(1);
DDS_DATA = 0x4004; //
DDS_WRITE(0)
DDS_WRITE(1);
DDS_DATA = 0xC000; //PHASE0 <= 0000
DDS_WRITE(0);
DDS_WRITE(1);
DDS_DATA = 0x2000; //コントロールレジスタ B28=1 RESET=0
DDS_WRITE(0);
DDS_WRITE(1);
DDS_DATA = 0x4625; //FRE =( 20MHz / 684,354,560 )* 0x10625 = 5KHz
DDS_WRITE(0);
DDS_WRITE(1);
DDS_DATA = 0x4004;
DDS_WRITE(0);
DDS_WRITE(1);
DDS_DATA = 0xC000; //PHASE0 <= 0000
DDS_WRITE(0);
DDS_WRITE(1);
}
//******************************************************************************************
// Function Name : DDS_WRITE
//******************************************************************************************
void DDS_WRITE(unsigned int DDS_SEL)
{
unsigned int i,d;
d = DDS_DATA;
if (DDS_SEL == 0){
FSYNC0 = 0;
for( i=1; i<=16; i++){
if (d & 0x8000) SDATA0 = 1;
else SDATA0 = 0;
d <<=1;
SCLK0 = 0;
SCLK0 = 1;
}
FSYNC0 = 1;
}else if (DDS_SEL == 1){
FSYNC1 = 0;
for( i=1; i<=16; i++){
if (d & 0x8000) SDATA1 = 1;
else SDATA1 = 0;
d <<=1;
SCLK1 = 0;
SCLK1 = 1;
}
FSYNC0 = 1;
}
}
DDS_DATA = 0xC000; //PHASE0 <= 0000
DDS_WRITE(0);
DDS_WRITE(1);
DDS_DATA = 0x2000; //コントロールレジスタ B28=1 RESET=0
DDS_WRITE(0);
DDS_WRITE(1);
DDS_DATA = 0x4625; //FRE =( 20MHz / 684,354,560 )* 0x10625 = 5KHz
DDS_WRITE(0);
DDS_WRITE(1);
DDS_DATA = 0x4004;
DDS_WRITE(0);
DDS_WRITE(1);
DDS_DATA = 0xC000; //PHASE0 <= 0000
DDS_WRITE(0);
DDS_WRITE(1);
}
//******************************************************************************************
// Function Name : DDS_WRITE
//******************************************************************************************
void DDS_WRITE(unsigned int DDS_SEL)
{
unsigned int i,d;
d = DDS_DATA;
if (DDS_SEL == 0){
FSYNC0 = 0;
for( i=1; i<=16; i++){
if (d & 0x8000) SDATA0 = 1;
else SDATA0 = 0;
d <<=1;
SCLK0 = 0;
SCLK0 = 1;
}
FSYNC0 = 1;
}else if (DDS_SEL == 1){
FSYNC1 = 0;
for( i=1; i<=16; i++){
if (d & 0x8000) SDATA1 = 1;
else SDATA1 = 0;
d <<=1;
SCLK1 = 0;
SCLK1 = 1;
}
FSYNC0 = 1;
}
}
周波数を変えるにはマニュアルに従って次のようにデータを送ります。
//DDS
DDS_DATA = 0x2000; //コントロールレジスタ B28=1 RESET=0 // DDS send
DDS_WRITE(0);
DDS_DATA = dds_LSB; //14LSB send
DDS_WRITE(0);
DDS_DATA = dds_MSB; //14MSB send
DDS_WRITE(0);
DDS_DATA = 0x2000; //コントロールレジスタ B28=1 RESET=0 // DDS send
DDS_WRITE(0);
DDS_DATA = dds_LSB; //14LSB send
DDS_WRITE(0);
DDS_DATA = dds_MSB; //14MSB send
DDS_WRITE(0);
dds_LSBとdds_MSBの計算はソースを見てください。
液晶に浮動小数点を表示したいと思いSPRINTFで%fを使いましたがうまく動きません。メーカーサイトをみると浮動小数点はメモリを食うので標準ではサポートしていないようなことが書かれています。
液晶に浮動小数点を表示したいと思いSPRINTFで%fを使いましたがうまく動きません。メーカーサイトをみると浮動小数点はメモリを食うので標準ではサポートしていないようなことが書かれています。
そこで小数点以下1桁のルーチンを作りました。
//=============================================================
// sprintf_
// 入力 float
// 出力 char xx.x
//=============================================================
void sprintf_(char *str,float mVolt_ )
{
int i;
int k;
int l;
i = mVolt_ * 10;
k = i/10; // 整数部
l = i- ( 10 * k ); // 小数部
sprintf(str,"%d%s%d",k,".",l);
}
// sprintf_
// 入力 float
// 出力 char xx.x
//=============================================================
void sprintf_(char *str,float mVolt_ )
{
int i;
int k;
int l;
i = mVolt_ * 10;
k = i/10; // 整数部
l = i- ( 10 * k ); // 小数部
sprintf(str,"%d%s%d",k,".",l);
}
またdBmからWに変換、RLのdBからSWRに変換するのに <mathf.h>のpowf関数を使っていますがこれを使うと一挙にメモリが増えます。表示はほとんど見ないのでdBのままでもよいのですが一応使ってみました。sprintfもかなりメモリを食います。stdioでなく自作のsprintfを作って使ったほうが良いと思います。
//SWR & POWER
POWER_dBm =(0.011723329 * cnv_data_f_PO ) + 4.49; //電圧からdBmに変換、係数は実測
POWER_W =(powf(10,POWER_dBm / 10)) / 1000; //mathf.powf ex: 50dBm = 100 W
sprintf_(msg_line,POWER_W);
RL_dB =(0.009025 * cnv_data_f_RL )-1.59; //電圧からdBに変換、係数は実測
LGP_ = -(RL_dB /20);
P_ = powf(10,LGP_); //mathf.powf
VSWR = (1+P_) / (1-P_); // ex: 9.54db = 2.0 SWR
sprintf_(msg_line_1,VSWR);
//LCD
lcdPosition( 0, 0 );
lcdPrintf( "%s"," ");
lcdPosition( 0, 0 );
lcdPrintf( "P=%5sW",msg_line);
lcdPosition( 0, 1 );
lcdPrintf( "%s"," ");
lcdPosition( 0, 1 );
lcdPrintf( "SWR=%3s",msg_line_1);
POWER_dBm =(0.011723329 * cnv_data_f_PO ) + 4.49; //電圧からdBmに変換、係数は実測
POWER_W =(powf(10,POWER_dBm / 10)) / 1000; //mathf.powf ex: 50dBm = 100 W
sprintf_(msg_line,POWER_W);
RL_dB =(0.009025 * cnv_data_f_RL )-1.59; //電圧からdBに変換、係数は実測
LGP_ = -(RL_dB /20);
P_ = powf(10,LGP_); //mathf.powf
VSWR = (1+P_) / (1-P_); // ex: 9.54db = 2.0 SWR
sprintf_(msg_line_1,VSWR);
//LCD
lcdPosition( 0, 0 );
lcdPrintf( "%s"," ");
lcdPosition( 0, 0 );
lcdPrintf( "P=%5sW",msg_line);
lcdPosition( 0, 1 );
lcdPrintf( "%s"," ");
lcdPosition( 0, 1 );
lcdPrintf( "SWR=%3s",msg_line_1);
シリアル通信を使ってPCからのコマンドで送出されるデータを選択できるようにしました。数字の0から9までの10個のコマンドです。起動時は9です。これらの処理は0.5秒ごとのルーチンで処理しています。

パソコンとの通信は10msのインターバルで行いました。パソコンからの受信完了信号を受けてデータを送信する簡単なプロトコルを作りやり取りします。ボーレートはICの最高の115.2kにしています。
これくらいの早さだとパソコン側でメーターの針をあたかもリアルタイムであるように振らせられます。
パソコンの画面です。

当初の目的は下記4点でした。ほぼ達成できました。
1、現在XR2206で作成しているサイン波をマイコンで作る。
2、進行波の電圧からパワー値を計算し液晶表示させる。
3、反射波の電圧からSWR値を計算し液晶表示させる。
4、パワー値およびSWR値をUSBを介してパソコンに送る。