目次
前
次
簡易DDS(Digital Direct Syncesyther)
中国製のDDSを、安価で入手できました。
最高で40MHzの正弦波を得られるというので
動作確認してみます。
マイクロコンピュータから周波数と位相を設定
しなければ、動かせないので、インタフェース
コネクタをつけます。
インタフェースコネクタのピン対応は次のように
しておきました。
- Vcc
- --
- --
- --
- --
- --
- W_CLK
- FR_UD
- DATA
- GND
動作を確認するためのマイコンは、H8/3048FOne
を使いました。
40ビットのデータを転送するので、最下層レベル
のコードを記述します。
typedef unsigned char UBYTE ;
typedef char SBYTE ;
typedef unsigned short UWORD ;
typedef short SWORD ;
typedef unsigned long ULONG ;
typedef struct {
ULONG fx ;
UBYTE cmd ;
} PARAMP ;
#define DDS_W_CLK 2
#define DDS_FQ_UD 1
#define DDS_DATA 0
void send_dds(PARAMP px)
{
volatile ULONG xtmp ;
volatile UBYTE loop ;
volatile UBYTE i ;
volatile UBYTE tmp ;
/* FQ_UD : LOW */
PBDR &= ~(1 << DDS_FQ_UD);
xtmp = px.fx ;
/* loop */
for ( loop = 0 ; loop < 5 ; loop++ ) {
/* get parameter */
switch (loop) {
case 1 : tmp = (UBYTE)((xtmp >> 8) & MASKFF) ; break ;
case 2 : tmp = (UBYTE)((xtmp >> 16) & MASKFF) ; break ;
case 3 : tmp = (UBYTE)((xtmp >> 24) & MASKFF) ; break ;
case 4 : tmp = px.cmd ; break ;
default : tmp = (UBYTE)(xtmp & MASKFF) ; break ;
}
/*
rs1_putchar( asc_hex[tmp >> 4] );
rs1_putchar( asc_hex[tmp & 15] );
*/
/* serial bit transmission */
for ( i = 0 ; i < 8 ; i++ ) {
/* impress */
PBDR &= ~(1 << DDS_DATA);
if ( tmp & 1 ) { PBDR |= (1 << DDS_DATA); }
/* W_CLK : H */
PBDR |= (1 << DDS_W_CLK);
/* shift */
tmp >>= 1 ;
/* W_CLK : L */
PBDR &= ~(1 << DDS_W_CLK);
/* FQ_UD : HIGH */
if ( loop == 4 && i == 7 ) {
PBDR |= (1 << DDS_FQ_UD);
}
}
}
}
40ビットは、32ビットの周波数、8ビットの位相と
なっているので、構造体変数を用意し、独立に設定
できるようにしました。
32ビットと8ビットに分けて処理すると、コーリング
シーケンスが単純になりました。
端末ソフトで周波数を設定したいので、1文字コマンド
を用意して対応します。
1文字コマンドの仕様は、次のようにしました。
- ? help
- F 1kHzの倍数で周波数値を設定
- f 1kHzの倍数で設定周波数値を表示
周波数に相当する32ビットの値は、次の計算で
求めます。
fcount = N x 34.359738368
いちいち計算するのは、面倒なので関数を用意し
40ビットの値を渡すときに計算します。関数は
次のように定義しました。
#define MV 34.359738368
ULONG calc_fx(UWORD x)
{
ULONG result ;
/* default */
result = 0 ;
/* calculate */
result = (ULONG)(1000.0 * x * MV) ;
return result ;
}
周波数は最大40MHzとして、kHzのオーダーでは
40000より大きいとエラーとします。
これまでの内容をまとめて、テスト用ファーム
ウエアを定義します。
/* 24.576MHz */
#define SYS_CLOCK_24_576
#include <3048.h>
/* define data type */
typedef unsigned char UBYTE ;
typedef char SBYTE ;
typedef unsigned short UWORD ;
typedef short SWORD ;
typedef unsigned long ULONG ;
typedef struct {
ULONG fx ;
UBYTE cmd ;
} PARAMP ;
/*--------------------*/
/* declare port macro */
/*--------------------*/
#define P1DDR P1.DDR
#define P2DDR P2.DDR
#define P3DDR P3.DDR
#define P4DDR P4.DDR
#define P5DDR P5.DDR
#define P6DDR P6.DDR
#define P8DDR P8.DDR
#define P9DDR P9.DDR
#define PADDR PA.DDR
#define PBDDR PB.DDR
#define P1DR P1.DR.BYTE
#define P2DR P2.DR.BYTE
#define P3DR P3.DR.BYTE
#define P4DR P4.DR.BYTE
#define P5DR P5.DR.BYTE
#define P6DR P6.DR.BYTE
#define P7DR P7.DR.BYTE
#define P8DR P8.DR.BYTE
#define P9DR P9.DR.BYTE
#define PADR PA.DR.BYTE
#define PBDR PB.DR.BYTE
#define OFF 0
#define ON OFF+1
#define ITU0_AREG 24576
#define MASKFF 0xff
#define DDS_W_CLK 2
#define DDS_FQ_UD 1
#define DDS_DATA 0
#define FMAX 40000
#define MV 34.359738368
ULONG timcnt ;
UBYTE uflag ;
UBYTE sindex ;
UBYTE sbuf[16];
UBYTE cmd ;
UBYTE asc_hex[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'} ;
UWORD fxx ;
volatile PARAMP frq ;
void user_io(void) ;
void init_sci_1(TBaudRate x);
void rs1_putchar(UBYTE x);
void rs1_crlf(void);
void rs1_puts(UBYTE *x);
void show_help(void);
void show_value(UBYTE x);
UBYTE get_hex(UBYTE x);
void show_value_decimal(UWORD x,UBYTE xflag);
void init_timer0(void);
void delay_ms(UWORD x);
void init_dds(void);
void send_dds(PARAMP px);
ULONG calc_fx(UWORD x);
int main(void)
{
UBYTE tmp ;
UBYTE loop ;
/* disabel interrupt */
DI ;
user_io();
/* enable interrupt */
EI ;
/* opening message */
rs1_puts("Hello"); rs1_crlf() ;
init_dds();
/* endless loop */
while ( ON ) {
/* command interpreter */
if ( uflag == ON ) {
/* clear flag */
uflag = OFF ;
/* new line */
rs1_crlf();
/* get command */
cmd = *(sbuf+0) ;
/* help */
if ( cmd == '?' ) { show_help() ; }
/* set frequency */
if ( cmd == 'F' ) {
/* get kHz x N */
fxx = 0 ;
for ( loop = 1 ; loop < 6 ; loop++ ) {
if ( *(sbuf+loop) == '\r' ) break ;
fxx *= 10 ;
fxx += get_hex( *(sbuf+loop) ) ;
}
/* show_value_decimal(fxx,ON); */
/* judge */
if ( fxx > FMAX ) {
rs1_puts("Out of range !");
} else {
/* calculate */
frq.fx = calc_fx( fxx );
frq.cmd = 0x00 ;
/* frq.cmd = 0x08 ; */
send_dds( frq );
}
}
/* show frequency */
if ( cmd == 'f' ) {
show_value_decimal(fxx,OFF);
rs1_crlf();
}
}
}
/* dummy */
return 0 ;
}
void user_io(void)
{
/* Port 1 */
P1DR = 0x00 ;
P1DDR = 0xff ;
/* Port 2 */
P2DR = 0x00 ;
P2DDR = 0xff ;
/* Port 3 */
P3DR = 0x00 ;
P3DDR = 0xff ;
/* Port 4 */
P4DR = 0x00 ;
P4DDR = 0xff ;
/* Port 5 */
P5DR = 0xff ;
P5DDR = 0xff ;
/* Port 6 */
P6DR = 0x00 ;
P6DDR = 0x00 ;
/* Port 8 */
P8DR = 0x00 ;
P8DDR = 0xff ;
/* Port A */
PADR = 0x00 ;
PADDR = 0xff ;
/* Port B */
PBDR = 0x00 ;
PBDDR = 0xff ;
/* */
uflag = OFF ;
/* clear SCI buffer */
*(sbuf+0) = 0 ;
sindex = 0 ;
/* */
init_sci_1(br9600);
init_timer0();
timcnt = 0 ;
fxx = 0 ;
frq.fx = 7000000 ;
frq.cmd = 0 ;
}
/*+++++++++++++++++++++++++*/
/* SCI_1 receive interrupt */
/*+++++++++++++++++++++++++*/
void init_sci_1(TBaudRate x)
{
volatile UWORD i;
/* SCR : Serial Control Register
7 bit TIE -> 0 Transmit Interrupt Enable(disable)
6 bit RIE -> 0 Receive Interrupt Enable(disable)
5 bit TE -> 0 Transmit Enable(disable)
4 bit RE -> 0 Receive Enable(disable)
3 bit MPIE -> 0 Multi Processor Interrupt Enable(disable)
2 bit TEIE -> 0 Transmit End Interrupt Enable(disable)
1 bit CKE1 -> 0 Clock Source (Use Internal Baud Rate Generator)
0 bit CKE0 -> 0
*/
SCI1.SCR.BYTE = 0 ;
/* SMR : Serial Mode Register
7 bit C/nA -> 0 Communication Mode(Asynchronous)
6 bit CHR -> 0 data Charactor (8 bits)
5 bit PE -> 0 Parity Enable(disable)
4 bit O/nE -> 0 Parity Mode(even)
3 bit STOP -> 0 Stop Bit(1 bit)
2 bit MP -> 0 Multi Processor(disable)
1 bit CKS1 -> 0 Clock Source ( φ )
0 bit CKS0 -> 0
*/
SCI1.SMR.BYTE = 0 ;
/* data transfer speed */
SCI1.BRR = x ;
/* wait 1 frame */
for (i = 0; i < 3000 ; i++) ;
/* enable Transmmit and Receive with interrupt */
SCI1.SCR.BYTE = 0x70 ;
}
/*+++++++++++++++++++++++++*/
/* SCI_1 receive interrupt */
/*+++++++++++++++++++++++++*/
void int_rxi1(void)
{
volatile UBYTE ch,dummy ;
/* clear flag */
dummy = SCI1.SSR.BYTE ;
SCI1.SSR.BIT.RDRF = OFF ;
/* get a character */
ch = SCI1.RDR ;
/* store */
*(sbuf+sindex) = ch ;
sindex++ ;
/* check */
if ( ch == '\r' ) {
*(sbuf+sindex) = 0 ;
sindex = 0 ;
uflag = ON ;
}
}
/*+++++++++++++++*/
/* SCI_1 putchar */
/*+++++++++++++++*/
void rs1_putchar(UBYTE x)
{
/* wait data transfer */
while ( SCI1.SSR.BIT.TDRE == OFF ) ;
/* put */
SCI1.TDR = x ;
SCI1.SSR.BIT.TDRE = OFF ;
}
/*++++++++++++*/
/* SCI_1 puts */
/*++++++++++++*/
void rs1_puts(UBYTE *x)
{
/* send 1 charactors */
while ( *x ) {
rs1_putchar(*x);
x++ ;
}
}
/*++++++++++++*/
/* SCI_1 crlf */
/*++++++++++++*/
void rs1_crlf(void)
{
rs1_putchar('\r');
rs1_putchar('\n');
}
/*++++++++++++++++++++*/
/* SCI_1 command help */
/*++++++++++++++++++++*/
void show_help(void)
{
rs1_puts("? help") ; rs1_crlf();
rs1_puts("F set frequency with kHz order") ; rs1_crlf();
rs1_puts("f who frequency") ; rs1_crlf();
}
UBYTE get_hex(UBYTE x)
{
UBYTE result ;
/* default */
result = 0 ;
/* judge */
if ( '0' <= x && x <= '9' ) { result = x - '0' ; }
if ( 'A' <= x && x <= 'F' ) { result = x - 'A' + 10 ; }
if ( 'a' <= x && x <= 'f' ) { result = x - 'a' + 10 ; }
return result ;
}
void init_timer0(void)
{
/* stop timer */
ITU.TSTR.BIT.STR0 = OFF ;
/* TOER : Timer Output Enable Register
7 **** -> 0
6 **** -> 0
5 EXB4 -> 0
4 EXA4 -> 0
3 EB3 -> 0
2 EB4 -> 0
1 EA4 -> 0
0 EA3 -> 0
*/
ITU.TOER.BYTE = 0 ;
/* TIOR : Timer I/O Control Register
7 **** -> 0
6 IOB2 -> 0 GRB is not output compare match register
5 IOB1 -> 0
4 IOB0 -> 0
3 **** -> 0
2 IOA2 -> 0 GRA is not output compare match register
1 IOA1 -> 0
0 IOA0 -> 0
*/
ITU0.TIOR.BYTE = 0 ;
/* TCR : Timer Control Register
7 **** -> 0
6 CCLR1 -> 0 clear TCNT if GRA = TCNT
5 CCLR0 -> 1
4 CKEG1 -> 0 rising edge
3 CKEG0 -> 0
2 TPSC2 -> 0 φ利用
1 TPSC1 -> 0
0 TPSC0 -> 0
*/
ITU0.TCR.BYTE = 0x20 ;
/* TIER : Timer Interrupt Enable Register
7 **** -> 0
6 *** -> 0
5 *** -> 0
4 *** -> 0
3 *** -> 0
2 OVIE -> 0
1 IMIEB -> 0
0 IMIEA -> 1 select compare match interrupt
*/
ITU0.TIER.BIT.IMIEA = ON ;
/* reference */
ITU0.GRA = ITU0_AREG ;
ITU0.GRB = 0xffff ;
/* counter */
ITU0.TCNT = 0 ;
/* start timer */
ITU.TSTR.BIT.STR0 = ON ;
}
/*+++++++++++++++++++++++++++++++++++++*/
/* ITU0 interrupt with compare match A */
/* 1ms interval */
/*+++++++++++++++++++++++++++++++++++++*/
void int_imia0(void)
{
volatile UBYTE dummy ;
/* clear flag */
dummy = ITU0.TSR.BIT.IMFA ;
ITU0.TSR.BIT.IMFA = OFF ;
/* increment */
timcnt++ ;
}
void delay_ms(UWORD x)
{
ULONG target ;
/* calculate last count */
target = timcnt + x ;
/* delay */
while ( timcnt < target ) ;
}
void show_value_decimal(UWORD x,UBYTE xflag)
{
UBYTE msg[5] ;
/* 100 */
*(msg+0) = x / 10000 ; x %= 10000 ;
*(msg+1) = x / 1000 ; x %= 1000 ;
*(msg+2) = x / 100 ; x %= 100 ;
*(msg+3) = x / 10 ;
*(msg+4) = x % 10 ;
/* convert */
*(msg+0) = asc_hex[*(msg+0)] ;
*(msg+1) = asc_hex[*(msg+1)] ;
*(msg+2) = asc_hex[*(msg+2)] ;
*(msg+3) = asc_hex[*(msg+3)] ;
*(msg+4) = asc_hex[*(msg+4)] ;
/* send */
rs1_puts(msg);
/* new line */
if ( xflag ) { rs1_crlf() ; }
}
void init_dds(void)
{
/* W_CLK */
PBDR |= (1 << DDS_W_CLK) ;
PBDR &= ~(1 << DDS_W_CLK) ;
/* FQ_UD */
PBDR |= (1 << DDS_FQ_UD) ;
PBDR &= ~(1 << DDS_FQ_UD) ;
/* */
send_dds( frq );
}
void send_dds(PARAMP px)
{
volatile ULONG xtmp ;
volatile UBYTE loop ;
volatile UBYTE i ;
volatile UBYTE tmp ;
/* FQ_UD : LOW */
PBDR &= ~(1 << DDS_FQ_UD);
xtmp = px.fx ;
/* loop */
for ( loop = 0 ; loop < 5 ; loop++ ) {
/* get parameter */
switch (loop) {
case 1 : tmp = (UBYTE)((xtmp >> 8) & MASKFF) ; break ;
case 2 : tmp = (UBYTE)((xtmp >> 16) & MASKFF) ; break ;
case 3 : tmp = (UBYTE)((xtmp >> 24) & MASKFF) ; break ;
case 4 : tmp = px.cmd ; break ;
default : tmp = (UBYTE)(xtmp & MASKFF) ; break ;
}
/*
rs1_putchar( asc_hex[tmp >> 4] );
rs1_putchar( asc_hex[tmp & 15] );
*/
/* serial bit transmission */
for ( i = 0 ; i < 8 ; i++ ) {
/* impress */
PBDR &= ~(1 << DDS_DATA);
if ( tmp & 1 ) { PBDR |= (1 << DDS_DATA); }
/* W_CLK : H */
PBDR |= (1 << DDS_W_CLK);
/* shift */
tmp >>= 1 ;
/* W_CLK : L */
PBDR &= ~(1 << DDS_W_CLK);
/* FQ_UD : HIGH */
if ( loop == 4 && i == 7 ) {
PBDR |= (1 << DDS_FQ_UD);
}
}
}
}
ULONG calc_fx(UWORD x)
{
ULONG result ;
/* default */
result = 0 ;
/* calculate */
result = (ULONG)(1000.0 * x * MV) ;
return result ;
}
このファームウエアでは、電源をいれるとデフォルトで
7000kHzに周波数を設定しています。
波形をオシロスコープで拾うと、次のように
なっていました。
振幅は200mVppと小さいため、アンプを入れて
使います。
実際に使う場合は、大きなマイコン基板では
不便と考えて、ATmega168を実装して対応です。
ATmega168を利用するときの回路図は、以下。
(LCDを種々のパラメータモニタに!)
基板上にマイコンをのせると、コンパクトになります。
ファームウエアは、次のようにしました。
#include <avr/io.h>
#include <avr/interrupt.h>
#define OFF 0
#define ON OFF+1
#define MASKFF 0xff
#define MASK0F 0x0f
#define MASKF0 0xf0
#define NO 0
#define YES NO+1
#define MASKFF 0xff
#define MASK80 0x80
#define LAST 1249
#define MAXSIZE 32
#define FMULTI 34.359738368
#define LCD_E 3
#define LCD_RS 2
#define DDS_W_CLK 2
#define DDS_FQ_UD 1
#define DDS_DATA 0
#define FRQ_MIN 7000
#define FRQ_MAX 7200
typedef unsigned char UBYTE ;
typedef unsigned short UWORD ;
typedef signed char SBYTE ;
typedef signed short SWORD ;
typedef unsigned long ULONG ;
typedef struct {
ULONG frq ;
UBYTE cmd ;
} PARAMP ;
volatile ULONG timcnt ;
volatile UBYTE tflag ;
volatile UBYTE sflag ;
volatile UBYTE count ;
volatile UBYTE lcd_fram[MAXSIZE] ;
volatile UBYTE lcd_framx[MAXSIZE] ;
volatile UBYTE up_sft ; /* UP */
volatile UBYTE dwn_sft ; /* DOWN */
volatile UBYTE ent_sft ; /* ENTER */
volatile UBYTE oth_sft ; /* others */
volatile PARAMP myfq ;
volatile UWORD xfrq ;
/*--------------------------------*/
/* Insert user functions protoype */
/*--------------------------------*/
void user_initialize(void);
void update_dds(PARAMP x);
void init_lcd(void);
void put_lcd_primitive(UBYTE which,UBYTE dat);
void put_lcd_str(UBYTE r,UBYTE c,UBYTE *ptr);
void delay_ms(UWORD x);
void update_frq_lcd(UWORD x);
ULONG conv_val(UWORD x);
/*------*/
/* main */
/*------*/
int main(void)
{
UBYTE tmp ;
UBYTE flag ;
UBYTE *ptrx ;
/* initialize port and variables */
user_initialize();
/* enable interrupt */
sei();
/* initialize LCD */
init_lcd();
/* endless loop */
while ( ON ) {
/* 10 ms timer */
if ( sflag == ON ) {
sflag = OFF ;
/* get switch state */
tmp = PINC & MASKFF ;
/* update shift register */
up_sft <<= 1 ; /* UP */
dwn_sft <<= 1 ; /* DOWN */
ent_sft <<= 1 ; /* ENTER */
oth_sft <<= 1 ; /* others */
if ( tmp & 0x04 ) { up_sft |= ON ; }
if ( tmp & 0x08 ) { dwn_sft |= ON ; }
if ( tmp & 0x10 ) { ent_sft |= ON ; }
if ( tmp & 0x20 ) { oth_sft |= ON ; }
/* judge UP */
if ( up_sft == 0x01 ) {
/* increment */
xfrq++ ;
/* limiter */
if ( xfrq > FRQ_MAX ) { xfrq = FRQ_MAX ; }
/* show frequency */
update_frq_lcd(xfrq) ;
}
/* judge DOWN */
if ( dwn_sft == 0x01 ) {
/* decrement */
xfrq-- ;
/* limiter */
if ( xfrq < FRQ_MIN ) { xfrq = FRQ_MIN ; }
/* show frequency */
update_frq_lcd(xfrq) ;
}
/* judge ENTER */
if ( ent_sft == 0x01 ) {
myfq.frq = conv_val(xfrq) ;
update_dds(myfq);
}
}
/* 1 second timer */
if ( tflag == ON ) {
tflag = OFF ;
/* scan */
flag = OFF ;
for ( count = 0 ; count < 32 ; count++ ) {
/* compare */
if ( *(lcd_framx+count) != *(lcd_fram+count) ) { flag = ON ; }
/* different */
if ( flag == ON ) break ;
}
/* update */
if ( flag == ON ) {
for ( count = 0 ; count < 32 ; count++ ) {
*(lcd_framx+count) = *(lcd_fram+count) ;
}
/* update LCD */
ptrx = (UBYTE *)lcd_framx ;
put_lcd_str(0,0,ptrx);
ptrx = (UBYTE *)(lcd_framx+16) ;
put_lcd_str(1,0,ptrx);
}
}
}
/* dummy */
return 0 ;
}
/*-----------------------*/
/* Insert user functions */
/*-----------------------*/
void user_initialize(void)
{
/* PORT B */
PORTB = 0x00 ; /* 0000 0000 */
DDRB = MASKFF ; /* oooo oooo */
/* PORT C */
PORTC = 0xFF ; /* 0000 0000 */
DDRC = 0x00 ; /* iiii iiii */
/* PORT D */
PORTD = 0x01 ; /* 0000 0001 */
DDRD = 0xfe ; /* oooo oooi */
/* initialize flags */
tflag = OFF ;
/* initialize timer1 */
{
/* clear counter */
TCNT1 = 0 ;
/* select CTC , select 1/8 = 1250kHz */
TCCR1A = 0 ;
TCCR1B = (1 << WGM12) | (1 << CS11) ;
/* 1kHz */
OCR1A = LAST ;
/* enable timer1 interrupt */
TIMSK1 = (1 << OCIE1A) ;
}
/* initialize variables */
for ( count = 0 ; count < 32 ; count++ ) {
*(lcd_fram+count) = 0x20 ;
}
*(lcd_fram+ 0) = 'F' ; *(lcd_fram+ 1) = 'R' ;
*(lcd_fram+ 2) = 'Q' ; *(lcd_fram+ 3) = ' ' ;
*(lcd_fram+ 4) = '=' ; *(lcd_fram+ 5) = ' ' ;
*(lcd_fram+ 6) = '7' ; *(lcd_fram+ 7) = '1' ;
*(lcd_fram+ 8) = '0' ; *(lcd_fram+ 9) = '0' ;
*(lcd_fram+10) = 'k' ; *(lcd_fram+11) = 'H' ;
*(lcd_fram+12) = 'z' ;
for ( count = 0 ; count < 32 ; count++ ) {
*(lcd_framx+count) = *(lcd_fram+count) ;
}
timcnt = 0 ;
xfrq = 7100 ;
myfq.frq = conv_val(xfrq) ; /* 7100kHz = 7.1MHz */
myfq.cmd = 0x00 ; /* command */
update_dds(myfq);
}
/* timer1 interrupt */
ISR(TIMER1_COMPA_vect)
{
/* increment */
timcnt++ ;
/* judge 10ms */
if ( (timcnt & 0xf) == 10 ) {
sflag = ON ;
}
/* judge 1000 ms */
if ( (timcnt & 0x3ff) == 1000 ) {
tflag = ON ;
}
}
void update_dds(PARAMP x)
{
volatile ULONG xtmp ;
volatile UBYTE loop ;
volatile UBYTE i ;
volatile UBYTE tmp ;
/* FQ_UD : LOW */
PORTB &= ~(1 << DDS_FQ_UD);
xtmp = x.frq ;
/* loop */
for ( loop = 0 ; loop < 5 ; loop++ ) {
/* get parameter */
switch (loop) {
case 1 : tmp = (UBYTE)((xtmp >> 8) & MASKFF) ; break ;
case 2 : tmp = (UBYTE)((xtmp >> 16) & MASKFF) ; break ;
case 3 : tmp = (UBYTE)((xtmp >> 24) & MASKFF) ; break ;
case 4 : tmp = x.cmd ; break ;
default : tmp = (UBYTE)(xtmp & MASKFF) ; break ;
}
/* serial bit transmission */
for ( i = 0 ; i < 8 ; i++ ) {
/* impress */
PORTB &= ~(1 << DDS_DATA);
if ( tmp & 1 ) { PORTB |= (1 << DDS_DATA); }
/* W_CLK : H */
PORTB |= (1 << DDS_W_CLK);
/* shift */
tmp >>= 1 ;
/* W_CLK : L */
PORTB &= ~(1 << DDS_W_CLK);
/* FQ_UD : HIGH */
if ( loop == 4 && i == 7 ) {
PORTB |= (1 << DDS_FQ_UD);
}
}
}
/* FQ_UD : LOW */
PORTB &= ~(1 << DDS_FQ_UD);
}
void init_lcd(void)
{
/* initialize hardware */
delay_ms(25) ; /* 25ms */
put_lcd_primitive(OFF,0x30);
delay_ms(5) ; /* 5ms */
put_lcd_primitive(OFF,0x30);
delay_ms(1); /* 1ms */
put_lcd_primitive(OFF,0x30);
delay_ms(1); /* 1ms */
put_lcd_primitive(OFF,0x20);
delay_ms(1); /* 1ms */
/* set function */
put_lcd_primitive(OFF,0x28);
put_lcd_primitive(OFF,0x08);
put_lcd_primitive(OFF,0x01);
delay_ms(2); /* 2ms */
put_lcd_primitive(OFF,0x06);
delay_ms(2); /* 2ms */
put_lcd_primitive(OFF,0x0c);
put_lcd_primitive(OFF,0x02);
put_lcd_primitive(OFF,0x80);
}
void put_lcd_primitive(UBYTE which,UBYTE dat)
{
/* LCD_E : OFF */
PORTD &= ~(1 << LCD_E) ;
/* LCD_RS : ON or OFF */
PORTD &= ~(1 << LCD_RS) ;
if ( which ) { PORTD |= (1 << LCD_RS) ; }
/* upper nibble */
{
PORTD &= MASKF0 ;
PORTD |= (dat & MASKF0) ;
/* trigger */
PORTD |= (1 << LCD_E) ;
PORTD &= ~(1 << LCD_E) ;
}
/* lower nibble */
{
PORTD &= MASKF0 ;
PORTD |= ((dat << 4) & MASKF0) ;
/* trigger */
PORTD |= (1 << LCD_E) ;
PORTD &= ~(1 << LCD_E) ;
}
}
void put_lcd_str(UBYTE r,UBYTE c,UBYTE *ptr)
{
UBYTE adr ;
/* check range */
if ( r > 1 ) return ;
if ( c > 15 ) return ;
/* set address */
adr = 0x00 ;
if ( r ) { adr += 0x40 ; }
adr += c ;
adr |= MASK80 ;
put_lcd_primitive(OFF,adr);
/* send charactor */
for ( adr = c ; adr < 16 ; adr++ ) {
put_lcd_primitive(ON,*ptr);
ptr++ ;
}
}
void delay_ms(UWORD x)
{
ULONG target ;
/* get now time counter */
target = timcnt + x ;
/* delay */
while ( timcnt < target ) ;
}
void update_frq_lcd(UWORD x)
{
UBYTE msg[4] ;
/* separate */
*(msg+0) = x / 1000 ; x %= 1000 ;
*(msg+1) = x / 100 ; x %= 100 ;
*(msg+2) = x / 10 ;
*(msg+3) = x % 10 ;
/* conversion */
*(msg+0) += '0' ;
*(msg+1) += '0' ;
*(msg+2) += '0' ;
*(msg+3) += '0' ;
/* copy */
*(lcd_fram+6) = *(msg+0) ;
*(lcd_fram+7) = *(msg+1) ;
*(lcd_fram+8) = *(msg+2) ;
*(lcd_fram+9) = *(msg+3) ;
}
ULONG conv_val(UWORD x)
{
ULONG result ;
/* calculate */
result = (ULONG)(1000.0 * x * FMULTI);
return result ;
}
このファームウエアには、シリアルインタフェースを
利用したデバッグが可能になるようにしています。
シリアルインタフェースは、トランジスタと抵抗で
簡便に作ってあります。
テスト、デバッグが終了したのでBASICで
書き直すと、以下。
'
'DDS TEST Sample
'
' Ver 0.01
'
' MCU = ATmega168-20PI
' Clock = 10MHz
'
' Pin assignment
'
' PORTB (control DDS) all output
' PB0 DDS_DATA
' PB1 DDS_FQ_UD
' PB2 DDS_W_CLK
' PB3 --
' PB4 --
' PB5 --
'
' PORTC (switches) all input
' PC0 encoder A phase
' PC1 encoder B phase
' PC2 increment
' PC3 decrement
' PC4 enter
' PC5 press talk
'
' PORTD (control LCD) all output
' PD0 no connection
' PD1 no connection
' PD2 LCD_RS
' PD3 LCD_E
' PD4 D4
' PD5 D5
' PD6 D6
' PD7 D7
'
'**************************
'*** initial setting ***
'**************************
$regfile = "m168def.dat" 'Chip ATmega168
$crystal = 10000000 'clock 10MHz
'
Config Portb = Output ' control DDS
Config Portc = Input ' switch inputs
Config Portd = Output ' control LCD
'*******************
' define constants *
'*******************
Const Freq_max = 7200 ' 7200kHz
Const Freq_mid = 7100 ' 7100kHz
Const Freq_min = 7000 ' 7000kHz
Const Pcnt_max = 10
Const Scalemulti = 34.35973837
Const Dds_cmd = $00
'*********************
' configure LCD mode *
'*********************
Config Lcdpin = Pin , Db4 = Portd.4 , Db5 = Portd.5
Config Lcdpin = Pin , Db6 = Portd.6 , Db7 = Portd.7
Config Lcdpin = Pin , E = Portd.3 , Rs = Portd.2
Config Lcd = 16 * 2
' variables for LCD
Dim Freq As Word
Dim Xfreq As Word
'**********************************
' configure Timer1 interrupt mode *
' interval about 100Hz = 10ms *
'**********************************
Config Timer1 = Timer , Prescale = 1024 , Clear Timer = 1 , Compare A = Disconnect
Compare1a = 100
On Compare1a Tm1int
Enable Compare1a
'************
' variables *
'************
Dim Tmp As Byte
Dim Iflag As Byte ' increment flag
Dim Dflag As Byte ' decrement flag
Dim Eflag As Byte ' enter flag
Dim Cport As Byte
Dim Isft As Byte ' increment shifter
Dim Dsft As Byte ' decrement shifter
Dim Esft As Byte ' enter shifter
Dim Ddsf As Long
Dim Ddsfx As Long
'***********************
' initialize variables *
'***********************
Portc = $ff ' pull up
Freq = Freq_mid ' 7100kHz
Xfreq = Freq_mid ' 7100kHz
' clear flags
Iflag = 0
Dflag = 0
Eflag = 0
' clear shifter
Isft = 0
Dsft = 0
Esft = 0
Enable Interrupts
Cls
Locate 2 , 1 : Lcd "JH8BWH"
Gosub Showlcd
'****************
'* main routine *
'****************
Main:
'==========================
' increment switch handling
'==========================
If Iflag = 1 Then
' clear flag
Iflag = 0
' +1
Xfreq = Xfreq + 1
' judge
If Xfreq > Freq_max Then
Xfreq = Freq_max
End If
' show frequency on LCD
Gosub Showlcd
End If
'==========================
' decrement switch handling
'==========================
If Dflag = 1 Then
' clear flag
Dflag = 0
' +1
Xfreq = Xfreq - 1
' judge
If Xfreq < Freq_min Then
Xfreq = Freq_min
End If
' show frequency on LCD
Gosub Showlcd
End If
'==========================
' enter switch handling
'==========================
If Eflag = 1 Then
' clear flag
Eflag = 0
' update
Freq = Xfreq
' send DDS
Gosub Updatef
End If
'
Goto Main
'
End
'************************
' timer1 interrupt *
' interval about 10ms *
'************************
Tm1int:
' shift
Isft = Isft * 2
Dsft = Dsft * 2
Esft = Esft * 2
' get switch state
Cport = Pinc Xor $ff
' get increment code
Tmp = Cport And $04
If Tmp = $04 Then
Isft = Isft + 1
End If
' get decrement code
Tmp = Cport And $08
If Tmp = $08 Then
Dsft = Dsft + 1
End If
' get enter code
Tmp = Cport And $10
If Tmp = $10 Then
Esft = Esft + 1
End If
' judge
If Isft = $03 Then
Iflag = 1
End If
If Dsft = $03 Then
Dflag = 1
End If
If Esft = $03 Then
Eflag = 1
End If
'
Return
'*******************
' update frequency *
'*******************
Updatef:
' calculate
Ddsfx = Freq * 1000 : Ddsf = Ddsfx * Scalemulti
' disable FQ_UD
Reset Portb.1
' send frequency
Shiftout Portb.0 , Portb.2 , Ddsf , 3
' send command
Tmp = Dds_cmd
Shiftout Portb.0 , Portb.2 , Tmp , 3
' enable FQ_UD
Set Portb.1
'
Return
'*****************
' show frequency *
'*****************
Showlcd:
Locate 1 , 1 : Lcd "Freq = " ; Xfreq
'
Return
ロータリーエンコーダ、中点つきトグルスイッチをいれて
次のようなインタフェースで、7MHzバンドの送受信で
利用する周波数をDDSに設定できるようにしました。
UI(User Interface)は、次の仕様としました。
中点つきトグルスイッチで、周波数変更時の幅を選択できます。
また、PTTスイッチを押しているとき、送信周波数を設定し
PTTスイッチを放しているときは、受信周波数を設定します。
ロータリースイッチで、周波数を増減し、その周波数を記憶
しておきたい場合、ENTERスイッチを押します。
BASCOMで記述したファームウエアコードは、以下。
'
' Pin assignment
'
' PORTB (control DDS) all output
' PB0 DDS_DATA
' PB1 DDS_FQ_UD
' PB2 DDS_W_CLK
' PB3 --
' PB4 --
' PB5 --
'
' PORTC (switches) all input
' PC0 encoder A phase
' PC1 encoder B phase
' PC2 increment
' PC3 decrement
' PC4 enter
' PC5 press talk
'
' PORTD (control LCD) all output
' PD0 no connection
' PD1 no connection
' PD2 LCD_RS
' PD3 LCD_E
' PD4 D4
' PD5 D5
' PD6 D6
' PD7 D7
'
'**************************
'*** initial setting ***
'**************************
$regfile = "m168def.dat" 'Chip ATmega168
$crystal = 10000000 'clock 10MHz
'
Config Portb = Output ' control DDS
Config Portc = Input ' switch inputs
Config Portd = Output ' control LCD
'*******************
' define constants *
'*******************
Const Freq_max = 7200 ' 7200kHz
Const Freq_mid = 7100 ' 7100kHz
Const Freq_min = 7000 ' 7000kHz
Const Pcnt_max = 10
Const Scalemulti = 34.35973837
Const Dds_cmd = $00
'*********************
' configure LCD mode *
'*********************
Config Lcdpin = Pin , Db4 = Portd.4 , Db5 = Portd.5
Config Lcdpin = Pin , Db6 = Portd.6 , Db7 = Portd.7
Config Lcdpin = Pin , E = Portd.3 , Rs = Portd.2
Config Lcd = 16 * 2
Cursor Off
Cls
' variables for LCD
Dim Freq As Word
Dim Xtfreq As Word
Dim Xrfreq As Word
'**********************************
' configure Timer1 interrupt mode *
' interval about 100Hz = 10ms *
'**********************************
Config Timer1 = Timer , Prescale = 1024 , Clear Timer = 1 , Compare A = Disconnect
Compare1a = 100
On Compare1a Tm1int
Enable Compare1a
'************
' variables *
'************
Dim Tmp As Byte
Dim Iflag As Byte ' increment flag
Dim Dflag As Byte ' decrement flag
Dim Zflag As Byte ' zero flag
Dim Eflag As Byte ' enter flag
Dim Fflag As Byte ' frequency flag
Dim Aflag As Byte ' (Tx or Rx)
Dim Cport As Byte
Dim Isft As Byte ' increment shifter
Dim Dsft As Byte ' decrement shifter
Dim Zsft As Byte
Dim Esft As Byte ' enter shifter
Dim Ddsf As Long
Dim Ddsfx As Long
Dim Fsft As Byte
Dim Asft As Byte
Dim Displacement As Byte
Dim State As Byte
Dim Xfreq As Word
Dim Xadr As Byte
Dim Dh As Byte
Dim Dl As Byte
Dim Myadr As Byte
Dim Estate As Byte
'***********************
' initialize variables *
'***********************
Portc = $ff ' pull up
' load transmit frequency
Xadr = 0
Gosub Loadf
Xtfreq = Xfreq
' load receive frequency
Xadr = 1
Gosub Loadf
Xrfreq = Xfreq
' check frequency range and store default values
State = 0
If Xtfreq < Freq_min Then
State = 1
End If
If Xtfreq > Freq_max Then
State = 2
End If
If State > 0 Then
State = 0
Xtfreq = Freq_mid
Xrfreq = Xtfreq - 455
' store frequency to EEPROM
Xfreq = Xtfreq
Xadr = 0
Gosub Savef
Xfreq = Xrfreq
Xadr = 1
Gosub Savef
End If
' clear flags
Dflag = 0
Iflag = 0
Zflag = 0
Eflag = 0
Fflag = 0
Aflag = 0
' clear shifter
Dsft = 0
Isft = 0
Zsft = 0
Esft = 0
Fsft = 0
Asft = 0
' width
Displacement = 10
' state receive mode
State = 0
Estate = 0
Enable Interrupts
' send DDS
Freq = Xrfreq
Gosub Updatef
'
Cls
Gosub Showlcd
Gosub Showtxrx
'****************
'* main routine *
'****************
Main:
'=================================
' frequency increment handling
'=================================
If Fflag = 1 Then
' clear flag
Fflag = 0
' add
Xtfreq = Xtfreq + Displacement
' judge
If Xtfreq > Freq_max Then
Xtfreq = Freq_max
End If
' modify receive frequency
Xrfreq = Xtfreq - 455
' show frequency on LCD
Gosub Showlcd
End If
'=================================
' frequency decrement handling
'=================================
If Fflag = 2 Then
' clear flag
Fflag = 0
' dec
Xtfreq = Xtfreq - Displacement
' judge
If Xtfreq < Freq_min Then
Xtfreq = Freq_min
End If
' modify receive frequency
Xrfreq = Xtfreq - 455
' show frequency on LCD
Gosub Showlcd
End If
'=================================
' 100 displacement handling
'=================================
If Iflag = 1 Then
' clear flag
Iflag = 0
' next
Displacement = 100
' show frequency on LCD
Gosub Showlcd
End If
'=================================
' 10 displacement handling
'=================================
If Zflag = 1 Then
' clear flag
Zflag = 0
' judge
If Displacement <> 10 Then
Displacement = 10
Gosub Showlcd
End If
End If
'=================================
' 1 displacement handling
'=================================
If Dflag = 1 Then
' clear flag
Dflag = 0
' next
Displacement = 1
' show frequency on LCD
Gosub Showlcd
End If
'==========================
' enter switch handling
'==========================
If Eflag = 1 Then
' clear flag
Eflag = 0
' show *
Estate = 1
Gosub Showtefd
' update
Freq = Xtfreq
' copy frequency
Xfreq = Xtfreq
' set EEPROM address
Xadr = 0
' store transmit frequency
Gosub Savef
'
Freq = Xrfreq
' copy frequency
Xfreq = Xrfreq
' set EEPROM address
Xadr = 1
' store receive frequency
Gosub Savef
' send DDS
Gosub Updatef
' wait 500ms
Waitms 500
' show *
Estate = 0
Gosub Showtefd
End If
'==================================
' set transmmit frequency handling
'==================================
If Aflag = 1 Then
' clear flag
Aflag = 0
' judge
If State <> 1 Then
' change transmmit mode
State = 1
' set transmmit frequency
Freq = Xtfreq
' send DDS
Gosub Updatef
' show
Gosub Showlcd
Gosub Showtxrx
End If
End If
'================================
' set receive frequency handling
'================================
If Aflag = 2 Then
' clear flag
Aflag = 0
' judge
If State <> 0 Then
' change receive mode
State = 0
' set receive frequency
Freq = Xrfreq
' send DDS
Gosub Updatef
' show
Gosub Showlcd
Gosub Showtxrx
End If
End If
'
Goto Main
'
End
'************************
' timer1 interrupt *
' interval about 10ms *
'************************
Tm1int:
' shift
Shift Isft , Left
Shift Dsft , Left
Shift Zsft , Left
Shift Esft , Left
Shift Fsft , Left
Shift Asft , Left
' get switch state
Cport = Pinc Xor $ff
' get frequency code
Tmp = Cport And $01
If Tmp = $01 Then
Fsft = Fsft + 1
End If
' get displacement 10
Tmp = Cport And $0c
If Tmp = 0 Then
Zsft = Zsft + 1
Zsft = Zsft And $03
End If
' get displacement 100
Tmp = Cport And $04
If Tmp = $04 Then
Isft = Isft + 1
End If
' get displacement 1
Tmp = Cport And $08
If Tmp = $08 Then
Dsft = Dsft + 1
End If
' get enter code
Tmp = Cport And $10
If Tmp = $10 Then
Esft = Esft + 1
End If
' get PTT code
Tmp = Cport And $20
If Tmp = $20 Then
Asft = Asft + 1
End If
' judge frequency update
If Fsft = $03 Then
Tmp = Cport And $02
' generate trigger
If Tmp = $02 Then
' decrement
Fflag = 2
Else
' increment
Fflag = 1
End If
End If
' displacement 10
If Zsft = $03 Then
Zflag = 1
End If
' displacement 100
If Isft = $03 Then
Iflag = 1
End If
' displacement 1
If Dsft = $03 Then
Dflag = 1
End If
' confirmation
If Esft = $03 Then
Eflag = 1
End If
' select transmmit frequency
If Asft = $03 Then
Aflag = 1
End If
' select receive frequency
If Asft = $00 Then
Aflag = 2
End If
'
Return
'*******************
' update frequency *
'*******************
Updatef:
' calculate
Ddsfx = Freq * 1000 : Ddsf = Ddsfx * Scalemulti
' disable FQ_UD
Reset Portb.1
' send frequency
Shiftout Portb.0 , Portb.2 , Ddsf , 3
' send command
Tmp = Dds_cmd
Shiftout Portb.0 , Portb.2 , Tmp , 3
' enable FQ_UD
Set Portb.1
'
Return
'*****************
' show frequency *
'*****************
Showlcd:
Locate 1 , 1 : Lcd "TxF=" ; Xtfreq
Locate 2 , 1 : Lcd "RxF=" ; Xrfreq
Locate 2 , 12 : Lcd " "
Locate 2 , 10 : Lcd "W= " ; Displacement
'
Return
'****************
' show Tx or Tx *
'****************
Showtxrx:
If State = 1 Then
Locate 1 , 10 : Lcd "Tx"
Else
Locate 1 , 10 : Lcd "Rx"
End If
'
Return
'**********************
' show enter feed back
'**********************
Showtefd:
If Estate = 1 Then
Locate 1 , 13 : Lcd "*"
Else
Locate 1 , 13 : Lcd " "
End If
'
Return
'**************************
' save frequency to EEPROM
'**************************
Savef:
' calculate address
Myadr = Xadr
Shift Myadr , Left
' separate
Dh = Xfreq / 256
Dl = Xfreq Mod 256
' store
Writeeeprom Dh , Myadr
Myadr = Myadr + 1
Writeeeprom Dl , Myadr
'
Return
'******************************
' resume frequency from EEPROM
'******************************
Loadf:
' calculate address
Myadr = Xadr
Shift Myadr , Left
' store
Readeeprom Dh , Myadr
Myadr = Myadr + 1
Readeeprom Dl , Myadr
' concatenate
Xfreq = Dh * 256
Xfreq = Xfreq + Dl
'
Return
このファームウエアでは、タイマー割込みでスイッチの
状態を読み込み、ユーザーが指定した操作を実行します。
4種のスイッチ操作詳細を説明します。
ロータリーエンコーダ
ロータリーエンコーダは、回転が時計回り、反時計回りかを
常に監視しています。
2つの変数Fsft、Fflagを利用。
タイマー割込みで、Fsftに時々刻々、ロータリーエンコーダの
A相の論理値を入力していきます。
変数Fsftをシフトレジスタとして使います。
8ビットシフトレジスタとして使い、下位2ビットが
ともに1のとき、エンコーダが回されたと判断します。
このとき、B相の論理値を見て、計数フラグに値設定
します。
B相がHレベルのときに、周波数を下げるための値2を
Fflagに設定。Lレベルでは、周波数を上げるための値1
を設定します。
上位側では、Fflagの値に応じて、周波数を指定幅だけ
増減させます。
周波数幅変更
周波数幅の変更は、2つのスイッチの状態を組合わせて
判断します。
変数は、Isft、Dsft、Zsft、Iflag、Dflag、Zflagを利用
しています。
周波数幅を100にするときは、Isftに対応するスイッチの
状態を時々刻々入力して、シフトしていきます。
Isftの値が3になったときに、周波数幅を100にという命令が
与えられたとし、フラグIflagに1を設定し上位へ通知します。
周波数幅が100でないときは、100に設定後、LCDに
周波数幅を表示します。
周波数幅は、変数Displacementに設定します。
周波数幅を1にするときは、Dsftに対応するスイッチの
状態を時々刻々入力して、シフトしていきます。
Dsftの値が3になったときに、周波数幅を1にという命令が
与えられたとし、フラグDflagに1を設定し上位へ通知します。
周波数幅が1でないときは、1に設定後、LCDに
周波数幅を表示します。
周波数幅を100、1に再設定する処理は、変数が異なるだけで
動作上は同じです。
周波数幅を10にするときは、Zsftに対応する2スイッチの
状態を見て、ともに1であれば、1として時々刻々入力し
シフトします。
2スイッチの状態を見て、常にシフトレジスタの下位2ビット
の値で、フラグZflagに1を設定します。上位では、Zflagの値
を監視します。
周波数幅を10にするときは、Zflagの値で再設定しますが
Zflagの値は、常に1となる状態が続いて発生するため
変数Displacementの値が、1になっていれば、Zflagの値が
1に設定されても、無視します。
周波数確定
ロータリーエンコーダで、周波数を増減させますが
それをEEPROMに記録するには、ENTERキースイッチを
押します。
2つの変数Esft、Eflagを利用。
タイマー割込みで、Esftに時々刻々、ENTERキーの
論理値を入力していきます。
Esftの値が3になったときに、周波数確定でEEPROMに
送受信周波数を保存すると命令がきたと判断できる
よう、Eflagに1を設定して通知。
送受信の周波数を、EEPROに保存するとともに、保存中は
LCDに'*'を出し、ENTERキーが押されたことがわかる様に
しています。
PTT処理
PTTを押しているときだけ、DDSから送信周波数を出力し
それ以外では、受信周波数を出力します。
2つの変数Asft、Aflagを利用。
タイマー割込みで、Asftに時々刻々、PTTキーの
論理値を入力していきます。ただし、Asftの下位
2ビットだけが常に更新されるようにします。
Asftの値が3のとき、送信周波数を選択するように
変数Aflagに1を設定します。
Asftの値が0のとき、受信周波数を選択するように
変数Aflagに2を設定します。
送信周波数を指定されたなら、DDSに送信周波数を
設定し、LCDに送信周波数および'Tx'を表示します。
すでに送信状態になっていると、送信周波数をDDSに
設定するのは無駄なので、変数stateを利用して送信
か受信かを判断できるようにしています。
受信周波数を指定されたなら、DDSに受信周波数を
設定し、LCDに受信周波数および'Rx'を表示します。
目次
前
次