※ DDS & LCD

[2005/02/27 14:19:29]

 ダイレクトドライブシンセサイザー(DDS)の出力周波数をロータリーエンコーダで設定し周波数を LCD に表示する回路を製作しました。はっきり言ってとても便利というか使いやすくなりました。 PIC16F84A 一個を使い DDS の制御と LCD への表示を行なっています。プログラムは CCS を使いました。下記が回路図です。平凡な回路で特に変わったところはありません。

 周波数の設定にはロータリーエンコーダを使っています。最初、秋月の周波数設定キットを使っていたのですが4桁毎の切り返しかできず、非常に不便でした。そこで一桁毎の設定ができるようにしています。ロータリーエンコーダを回したときの信号を Port_B0 に入力し割込み処理で周波数を設定できるようにしました。また Port_A2 に接続したスイッチで周波数設定できる周波数が一桁移動するようにしています。

 設定した周波数は8桁全て LCD に表示しています。LCD はテストでは上記回路の LM016L を使っています。実際の組み立てでは表示部が小さい SC1602BS*B を使っています。LM016L と SC1602BS*B は VDD と VSS が反転しています。上回路では LM016L なので Pin_1 が VSS(DNG) で Pin_2 が VDD(+5V) になっています。 SC1602BS*B を使う場合には Pin_1 に VDD(+5V) を Pin_2 に VSS(GND) を接続するようにして下さい。

  PIC16F84A のプログラム領域が少ないので 24 ビットの周波数制限ができませんでした。周波数が 00,000,000Hz でロータリーエンコーダを左に回すと 99,999,999Hz と表示されてしまいます。

● プログラム

 LCD ドライブのルーチンは何処かにあったものをそのまま利用しています(たぶん秋月のサンプルではないかと思いますが)。不安な方は CCS のサンプルプログラムにも LCD ドライブのライブラリがあるのでそれを使った方が良いかもしれません。インターネットで検索すると沢山ヒットするします。下記プログラムは PIC 一個でも DDS の制御と LCD への周波数表示ができるという、まあ参考程度に考えて下さい。

 use fast_io を使わないとプログラム領域が足りなくなってしまいます。それからリスタートしたとき LCD にバージョンが表示されますが実際には意味ありません。バージョン表示を削除した方が周波数切換えが素早くできます。

//////////////////////////////////////////////////////////////////
// DDS & LCD Ver 1.01
// 2005/02/21 Nishimura Hiromi
//////////////////////////////////////////////////////////////////
#include <16F84A.h>
#use delay(CLOCK=20000000)
#use fast_io(a)
#use fast_io(b)
#define Bmode 0x01 //port B initial mode
#define Amode 0xfb //port A initial mode
#byte db = 6 //port B
#byte da = 5 //port A

//////////////////////////////////////////////////////////////////
void mul10(long *a, long *b, int c);

//////////////////////////////////////////////////////////////////
//////// Port define and link LCD library
//////////////////////////////////////////////////////////////////
#define rs PIN_B1 //LCD chip select
#define rw PIN_B2 //LCD read/write
#define stb PIN_B3 //LCD strobe
#define DCL PIN_B1 //DDS Clock
#define DDT PIN_B2 //DDS Data
#define DST PIN_A2 //DDS Strobe

//////////////////////////////////////////////////////////////////
char sdisp_s[2]; // display char
signed int freq[8]; // digit frequency
signed int md; // column number
signed long fout1,fout2; // binary frequency
int check; // check change frequency

//////////////////////////////////////////////////////////////////
#include "lcd_lib.c"

//////////////////////////////////////////////////////////////////

//----inc_counter()
{
int over,xmd;
over = 0;
for(xmd=md;xmd<8;xmd++){
freq[xmd]++;
if(freq[xmd]<10){over = 0;break;}
else {over = 1; freq[xmd] = 0;}
}
}
//----dec_counter()
{
int over,xmd;
over = 0;
for(xmd=md;xmd<8;xmd++){
if(freq[xmd]>0) {freq[xmd]--; over = 0; break;}
else {over = 1; freq[xmd] = 9;}
}
}

//----#INT_EXT
counter()
{
disable_interrupts(INT_EXT);
delay_ms(1);
if(input(PIN_A1)==1){
delay_ms(1);
while(1){
if(input(PIN_A0)==0 && input(PIN_A1)==0){
inc_counter();
break;
}
if(input(PIN_A0)==1 && input(PIN_A1)==1) break;
}
}
else {
while(1){
delay_ms(1);
if(input(PIN_A0)==0 && input(PIN_A1)==1){
dec_counter();
break;
}
if(input(PIN_A0)==1 && input(PIN_A1)==1) break;
}
}
check = 1;
enable_interrupts(INT_EXT);
}

//////////////////////////////////////////////////////////////////
// DDS の初期設定
//////////////////////////////////////////////////////////////////
void initDDS()
{
output_low(DCL);
output_low(DDT);
output_high(DST);
}

//----// nビットのダミー信号を出力する
void dmyDDSData(int n)
{
int i;
output_high(DDT);
for(i=0;i<n;i++){
output_high(DCL);
output_low(DCL);
}
}

//----// 4ビットのコマンドを出力する
void sendDDSCommand(int command)
{
int i;
for(i=0;i<4;i++){
if(command&0x0001!=0) output_high(DDT);
else output_low(DDT);
output_high(DCL);
output_low(DCL);
command = command >> 1;
}
}

//----// DDS にnビットの周波数コードを出力
void sendDDSfreq(long ff, int n)
{
int i;
for(i=0;i<n;i++){
if(ff&0x0001!=0) output_high(DDT);
else output_low(DDT);
output_high(DCL);
output_low(DCL);
ff = ff >> 1;
}
}

//----// DDS を outPut モードにする
void outputStart()
{
int i;

dmyDDSData(3);
sendDDSCommand(0x02);
dmyDDSData(26);
output_low(DST);
output_high(DST);
}

//----// DDS に周波数を設定する → 即出力
void outputFreq()
{
dmyDDSData(3);
sendDDSCommand(0x0C);
sendDDSfreq(fout1,8);
sendDDSfreq(fout2,16);
output_low(DDT);
output_high(DCL); output_low(DCL);
output_high(DCL); output_low(DCL);
output_low(DST);
output_high(DST);

}

//----// 指定した3バイト変数の値を10倍し指定した値を加える
void mul10(long *a, long *b, int c)
{
*b = *b * 10;
*a = *a * 10 + (*b>>8);
*b = *b & 0x00ff;
*b = *b + c;
*a = *a + (*b>>8);
*b = *b & 0x00ff;
}

//////////////////////////////////////////////////////////////////
void init()
{
set_tris_a(Amode); // mode set of port
set_tris_b(Bmode); // lower is input
initDDS(); // DDSを初期化(制御ピンに信号を加えるだけ!)
outputStart(); // DDSを outPut モードにする
lcd_init(); // initialize LCD
lcd_clear(); // clear display
lcd_cmd(0x0c); // no cursor
lcd_data("DDS Ver 1.0.1"); // version
lcd_cmd(0xC0);
lcd_data(" 2004/02/23 NiS");
delay_ms(1000);
// init valiables
md = 0;
freq[0]=freq[1]=freq[2]=freq[3]=freq[4]=freq[5]=freq[6]=freq[7]=0;
}
//----disp_number()
{
signed int i,j;
lcd_clear();
lcd_data("F ");
fout1=fout2=0;
for(i=7;i>=0;i--){
if(i==5||i==2) lcd_data(",");
lcd_data(freq[i]+'0');
mul10(&fout2,&fout1,freq[i]);
}
lcd_data(" Hz");
lcd_cmd(0xC0);
lcd_data(" ");
for(j=0,i=7-md;i>=0;i--) {
if(j==2||j==5) lcd_data(" ");
lcd_data("-");
j++;
}
outputFreq();
}

//////////////////////////////////////////////////////////////////
main(){
int i;

init();
EXT_INT_EDGE(H_TO_L);
enable_interrupts(INT_EXT);
enable_interrupts(GLOBAL);
disp_number();
while(1){
if(check==1) {disp_number();check=0;}
if(input(PIN_A3)==0){
delay_ms(20);
if(input(PIN_A3)==0){
md++; if(md==8) md = 0;
disp_number();
while(input(PIN_A3)==0);
}
}
}
}
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
// LCD control Library
// functions are below
// lcd_init()-------- initialize
// lcd_ready()------- busy check
// lcd_cmd(cmd)------ send command
// lcd_data(string)-- display string
// lcd_clear() ------ clear display

//////////////////////////////////////////////////////////////////

/////////// lcd ready check function
int lcd_ready(){
int high,low;
set_tris_b(Bmode | 0xF0); //upper is input
output_low(rs);
output_high(rw); //read mode
output_high(stb);
high=db & 0xF0; //input upper
output_low(stb);
output_high(stb);
low=db & 0xF0; //input lower
output_low(stb);
set_tris_b(Bmode);
return(high | (low>>4)); //end check
}

////////// lcd display data function
void lcd_data(int asci){
int tp1,tp2;
tp1= asci & 0xF0;
tp2= db & 0x0F;
db = tp2 | tp1; //set upper data
output_low(rw); //set write
output_high(rs); //set rs high
output_high(stb); //strobe
output_low(stb);
asci=asci<<4;
tp1= asci & 0xF0;
tp2= db & 0x0F;
db = tp2 | tp1; //set lower data
output_high(stb); //strobe
output_low(stb);
while(bit_test(lcd_ready(),7));
}

////////// lcd command out function
void cmdout(int cmd){
int tp1,tp2;
tp1= cmd & 0xF0;
tp2= db & 0x0F;
db = tp2 | tp1; //set upper data
output_low(rw); //set write
output_low(rs); //set rs low
output_high(stb); //strobe
output_low(stb);
cmd=cmd<<4;
tp1= cmd & 0xF0;
tp2= db & 0x0F;
db = tp2 | tp1; //set lower data
output_high(stb); //strobe
output_low(stb);
}
void lcd_cmd(int cmd){
cmdout(cmd);
while(bit_test(lcd_ready(),7)); //end check
}

////////// lcd display clear function
void lcd_clear(){
lcd_cmd(1); //initialize command
}

///////// lcd initialize function
void lcd_incmd(int cmd){
int tp1,tp2;
tp1= cmd & 0xF0;
tp2= db & 0x0F;
db = tp2 | tp1; //mode command
output_low(rw); //set write
output_low(rs); //set rs low
output_high(stb); //strobe
output_low(stb);
delay_us(100);
}
void lcd_init(){
delay_ms(30);
lcd_incmd(0x30); //8bit mode set
lcd_incmd(0x30); //8bit mode set
lcd_incmd(0x30); //8bit mode set
lcd_incmd(0x20); //4bit mode set
lcd_cmd(0x2E); //DL=0 4bit mode
lcd_cmd(0x08); //disolay off C=D=B=0
lcd_cmd(0x0D); //display on C=D=1 B=0
lcd_cmd(0x06); //entry I/D=1 S=0
}

● 感想

 ロータリエンコーダを使って周波数設定が出来るようになりとても便利に使っています。回路図を描いていたら Pin_A4 が余っていました。設定周波数の切換えが一方向だけなので両方向になるよう改良しようかと思っています。


Nishimura Hiromi

以上