目次

簡易DDS(Digital Direct Syncesyther)

 中国製のDDSを、安価で入手できました。



 最高で40MHzの正弦波を得られるというので
 動作確認してみます。

 マイクロコンピュータから周波数と位相を設定
 しなければ、動かせないので、インタフェース
 コネクタをつけます。



 インタフェースコネクタのピン対応は次のように
 しておきました。
  1. Vcc
  2. --
  3. --
  4. --
  5. --
  6. --
  7. W_CLK
  8. FR_UD
  9. DATA
  10. 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文字コマンドの仕様は、次のようにしました。  周波数に相当する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'を表示します。
目次

inserted by FC2 system