PSoC3使用のツートーンジェネレータ

投稿日 2014/05/20

PSoC3を使用して、簡単なツートーンジェネレータを作ってみました。

psoc_twotone_2.jpg

写真1:作成したPSoC3ツートーンジェネレータ(Ch-A 1200Hz, Ch-B 900Hz)
300Hz離れた周波数セットを16種類の中からボタンで選択可能
選択されているチャネル(SEL A/B)はロータリエンコーダで周波数の微調が可能
出力電圧は0から1Vまでポテンショメータで調整


 

PSoC3のコンポーネントの中に、WaveDAC8があります。これを使用すると簡単に希望の周波数の信号を発生させることができます。

 

WaveDAC8を2つ使用し、数100Hz離れた信号を生成しミックスしてツートーンを得ます。

 

このツートーン・ジェネレータはSSB機の調整に欠かせないものです。

 

今回製作しましたが、実際には無線機の調整に使用していないので実用に耐えるかどうかはわかりません。

 

WaveDAC8の参考資料

WaveDAC8の使い方は、Cypress社のアプリケーション・ノートAN69133が参考になります。ここにFSKやDTMFでの使い方が載っています。

 

ツートーンジェネレータは周波数は異なるものの一種のDTMFです。

 

今回はDTMFのサンプル・ソースをベースにし、改造して簡易なツートーン・ジェネレータに仕立てました。

 

PSoC3ツートーンジェネレータの仕様

製作するツートーンジェネレータは、周波数の組み合わせが選べ、出力レベルも調整可能としました。

 

動作状態はI2C LCDディスプレイでモニタできるようにしました。

 

○電源を入れると トーンセットNo.12の1000Hz, 700Hzで発振を開始します。

 

○REDボタンとBLUEボタンでトーンセットを変更できます

 

○ロータリーエンコーダで周波数の微調整ができます

 

○周波数微調整するチャネルはWHITEボタンで選択(電源投入時はA)します

 

(周波数は2200Hz以下であれば、ソースコードの変更で自由に設定可能)

 

標準のトーン組み合わせ
No. Ch-A Ch-B
0 2200, 1900
1 2100, 1800
2 2000, 1700
3 1900, 1600
4 1800, 1500
5 1700, 1400
6 1600, 1300
7 1500, 1200
8 1400, 1100
9 1300, 1000
10 1200, 900
11 1100, 800
12 1000, 700
13 900, 600
14 800, 500
15 700, 400

 

トーンのミックスはパラ接続の簡易型です。ミックスした信号はOPアンプのバッファを通して低インピーダンスドライブ可能にしています。

 

出力の電圧レベルは、ポテンショメータで調整できるようにします。

 

実際に生成できる信号の周波数にはある程度のずれがあります。ジャストにはなりません。




 

使用したコンポーネント

シールバッテリ簡易充電回路

psoc_twotone_3.jpg

写真2:使用したPSoCコンポーネント

 

PSoC3のコンポーネントは以下を使用しました。
[]内はコンポーネント名とピン番号です。

 

WaveDAC8 VDAC 0 - 1.020V Samples = 40 Offset = 0.51 [Col_Tone]
WaveDAC8 VDAC 0 - 1.020V Samples = 40 Offset = 0.51 [Row_tone]
16bit PWM[Col_Divider]
16bit PWM[Row_Divider]
12MHzのクロック[Tone_Clock]
OPアンプ[DTMF_Buffer]
I2C MASTER [I2C]
I2C SCL用のDigital OUT Pin [SCL_1 P12-4]
I2C SDA用のDigital OUT Pin [SDA_1 P12-5]
REDボタン用Digital INPUT Pin(周波数セット切り替え 前) [RED_BTN P12-3]
BLUEボタン用Digital INPUT Pin(周波数セット切り替え 次) [BLUE_BTN P12-2]
WHITEボタン用Digital INPUT Pin(微調整チャネル選択 A/B) [WHITE_BTN P2-5]
ロータリエンコーダAチャネル用Digital INPUT Pin [A_Ch P12-6]
ロータリエンコーダBチャネル用Digital INPUT Pin [B_Ch P12-7]
チャタリング防止用クロック源 [Clock_1]
クロック割り込み用Interupt [isr_Clock_1ms]

 

I2C LCDは、3.3V I2C LCD ACM1602NI です。
I2Cインターフェースの信号線2本 SCLとSDAをつないでおきます。
SCLとSDAのプルアップ抵抗は2.2KΩを外付けとしました。

 

REDとBLUEボタンで周波数セットを16種類の中から選びます。

 

WHITEボタンで周波数を微調整するチャネルを選びます。

 

ロータリエンコーダは選択されているチャネルの周波数微調整に使用します。
A_Ch, B_Chをつなぎ、ピン・コンポーネントでPull UPしておきます。
チャタリングがあるので、プログラムの割り込み処理ルーチンで見ています。

 

電源はマイコン、LCDともに3.3Vです。
今回はUSBの書き込み器から供給しています。

 

写真1に写っているGREENとYELLOWボタンは使用していません。


 

信号生成方法

WaveDAC8はメモリ上の波形データ(データフォーム)をDMAでDACに送り波形を生成するコンポーネントです。希望の周波数の波形を得るために、よく使う方法です。

 

WaveDAC8は便利にできており、これだけで正弦波や三角波などが生成でき、また2組みの生成回路を持っており、外部から切り替えられます。このため異なる周波数にしておき、切り替えればFSKが実現できます。

 

波形データのサイズは最大4000です。

 

クロックは内部と外部が選べます。

 

生成波形の周波数は、波形データのサイズと各データをどのくらいの速度のクロックで読みだしてDACに出力するかで決まります。

 

つまり短い波形データを高速のクロックで読み出すと高い周波数が、長い波形データを低速のクロックで読み出すと低い周波数が生成できます。

 

クロックは外から供給できるので、波形データは固定しておいてクロックを変更すれば周波数が変更できます。

 

波形データの内容やサイズを変更しても周波数が変更できますが、クロックを変えるのほうが簡単です。

 

今回は波形データのサイズを40とし、サイン波が生成されるようにwaveDAC8コンポーネントを設定しておきます。

 

クロックはPWMコンポーネントで生成し、プログラムで変更できるようにします。

 

PWMの源クロックは12MHzとします。

 

PWMにはDivider値をプログラムから変更して、出力周波数を変更します。

 

希望のトーン周波数を得るためのDivider値は以下で計算できます。

 

PWM Divider = 12000000 / (トーン周波数 x 40)

 

例えば1000Hzの場合、12000000 / (1000 x 40) = 300

psoc_twotone_1.jpg

写真3:FFTアナライズ Ch-A 1000Hz Ch-B 700Hz(WaveSpectraで作成)
スプリアスは-50dB以下なのでよしとしています。

 

ソースコード

I2C_LCD.cは省略

 

#include <device.h>
#include <stdio.h>

 

void I2C_LCD_Init(void);
void I2C_LCD_WriteString(uint8, uint8, uint8 *);

 

#define CH_A 0
#define CH_B 1

 

CY_ISR_PROTO(Clock_1ms_Interrupt);

 

uint8 int_cnt = 0;
uint8 now = 0;
uint8 prev = 0;
int8 change = 0;
char string[15];
int tone_no;
int divider_A, divider_B;
int freq_A, freq_B;
uint8 tone_sel;

 

struct tone_table {

int Ch_A_freq;
int Ch_B_freq;

};

 

struct tone_table freq_table[16] = {

{2200, 1900},
{2100, 1800},
{2000, 1700},
{1900, 1600},
{1800, 1500},
{1700, 1400},
{1600, 1300},
{1500, 1200},
{1400, 1100},
{1300, 1000},
{1200, 900},
{1100, 800},
{1000, 700},
{ 900, 600}, 
{ 800, 500},
{ 700, 400}

};

 

CY_ISR(Clock_1ms_Interrupt){
if(int_cnt == 3) { //make 3ms
int_cnt = 0;
now = (EC_A_Ch_Read() << 1) + EC_B_Ch_Read();
if(prev != now) {
if (((prev << 1) + now) & 0x02) {
if(now == 0x03){ //right, lock position
if(tone_sel == CH_A)

divider_A++;

else

divider_B++;

change = 1;
}
} else {
if(now == 0x03){ //left, lock position
if(tone_sel == CH_A)

divider_A--;

else

divider_B--;

change = -1;
}
}
prev = now;
}
} else

int_cnt++;

}

 

void set_tone(){
sprintf(string, "%2d", tone_no);
I2C_LCD_WriteString(8, 0, string); 

if(tone_sel == CH_A)

I2C_LCD_WriteString(15, 0, "A" );

else

I2C_LCD_WriteString(15, 0, "B" );

freq_A = freq_table[tone_no].Ch_A_freq;
freq_B = freq_table[tone_no].Ch_B_freq;
sprintf(string, "%4d", freq_A);
I2C_LCD_WriteString(2, 1, string);
sprintf(string, "%4d", freq_B);
I2C_LCD_WriteString(9, 1, string);

divider_A = (int)(12000000L / ((long)(freq_A) * 40L));
divider_B = (int)(12000000L / ((long)(freq_B) * 40L));
//divider_A = freq_table[tone_no].Ch_A_divider;
//divider_B = freq_table[tone_no].Ch_B_divider;

Row_Divider_WritePeriod(divider_A);
Col_Divider_WritePeriod(divider_B);
ToneClock_Start();
}

 

void freq_adj(void){

freq_A = (int)(12000000L / ((long)(divider_A) * 40L));
freq_B = (int)(12000000L / ((long)(divider_B) * 40L));
sprintf(string, "%4d", freq_A);
I2C_LCD_WriteString(2, 1, string);
sprintf(string, "%4d", freq_B);
I2C_LCD_WriteString(9, 1, string);

//I2C_LCD_WriteString(0, 0, "A:XXXX B:XXXX Ct" );
//sprintf(string, "%4d", divider_A);
//I2C_LCD_WriteString(2, 0, string);
//sprintf(string, "%4d", divider_B);
//I2C_LCD_WriteString(9, 0, string);

Row_Divider_WritePeriod(divider_A);
Col_Divider_WritePeriod(divider_B);
ToneClock_Start(); 

}

 

int main(){

change = 0;
CyGlobalIntEnable;

isr_Clock_1ms_StartEx(Clock_1ms_Interrupt);

 

I2C_Start();
I2C_LCD_Init();

I2C_LCD_WriteString(0, 0, " TwoTone Gene. " );

I2C_LCD_WriteString(0, 1, " by JF1VRR " );
CyDelay(1000);
I2C_LCD_ClearAll();

I2C_LCD_WriteString(0, 0, "TwoTone XX SEL A" );
I2C_LCD_WriteString(0, 1, "A:XXXX B:XXXX Hz" );

ToneClock_Stop();
Row_Divider_Start();
Col_Divider_Start();
Row_Tone_Start();
Col_Tone_Start();
DTMF_Buffer_Start();

tone_sel = CH_A;
tone_no = 12; 
set_tone();

while(1){
if(change == 1 || change == -1){

freq_adj();
change = 0;


if(RED_BTN_Read() == 0){

tone_no--;
if(tone_no < 0) tone_no = 15;
set_tone();

}
if(BLUE_BTN_Read() == 0){

tone_no++;
if(tone_no > 15) tone_no = 0;
set_tone();

}
if(WHITE_BTN_Read() == 0){

tone_sel = !tone_sel;
set_tone();


}
}





 

(JF1VRR)