2015年5月25日月曜日

FET リップルフィルタ

真空管電源用のFETリップルフィルタである。50年程前、LCRにる電源フィルタであったが、今では半導体を使ったリップルフィルタも使われている。現在、主にオーディオAMPの電源部として使われているが、無線機の電源部として使う予定。FETは、手持ちの中から、VDSSの高いものを選んだ。チョークレス構成の為、重量低減とローコスト化を狙える。ただ、トランスと組み合わせた確認試験を行ってないので、性能は定かでない。 
回路図である。回路図中の電圧は、無負荷時の計算値。
PCBサイズ 83×58
抵抗は、無理すれば、3Wまでなら実装可能。          

2015年4月16日木曜日

秋月電子DDS(TC170C030) VFO

長い間眠っていた秋月電子のDDSキットを組立て、DDS VFOを作った。今回も、I2CコントロールのLCDを使って、省配線。Aruduinoが安価(中華サイトで$4位)なので、テストするのに助かる。公開済のArduino版ファームウェアを移植したので、機能は問題ない筈だ。また、秋月DDSをVFOとして使う場合、最大出力周波数に注意しなければならない。このキットの場合、データシートから約16MHzである。
回路図である。LPFはシュミレーションにより、カットオフ16MHzで設計。シュミレーションの様子は、Twitterに書き込んである。













Program

公開済みのArduino版ファームウェアを移植した。デバイス固有の部分は、DDS出力ルーチンである。今までのIF=10MHzとしたプリミックスでアッパーヘテロダインでは、DDS最大出力周波数を超えてしまう。そこでIF=5MHzとした。IFを10MHzとする場合、逆ヘテロダインとなる。何れのソースプログラムもDownload siteにupしてある。

//////////////////////////////////////////////////////////////////////
//  TC170C030AF001 DDS VFO Premixed type program ver.1.0
//
//    Copyright(C)2014-2015.JA2GQP.All rights reserved.
//
//      7.000MHz to 7.200MHz Limitted.
//      (Target frequency = IF frquency + frequency)
//                                                  2015/04/14
//                                                  JA2GQP
//--------------------------------------------------------------------
//  Function
//    1.RIT Operation(-50kHZ to 50kHZ)
//    2.STEP(100k,10k,1k,100,10)
//    3.Memory Operation is Push RIT
//      (Frequency and Step)
//    4.Protection Operation At The Time Of Transmission
//    5.Channel Memory.Main Channel(Ch0) + 3 Channel(Ch1,Ch2,Ch3)
//    6.Split Operation(7.00MHz to 7.20MHz Limited!)
//
//////////////////////////////////////////////////////////////////////

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <rotary.h>
#include <EEPROM.h>

//----------  LCD Pin Assign  ------------------

LiquidCrystal_I2C lcd(0x27,16,2);       // set the LCD address to 0x27 for
                                                // a 16 chars and 2 line display

//----------  Define Constant Value   ----------
                                             
const byte  ENC_A = 2;                   // Encorder A
const byte  ENC_B = 3;                   //          B
const byte  DDT = 4;                      // TC170C030AF001 DATA
const byte  DCK = 5;                      //                SCK
const byte  DST = 6;                      //                STB
const byte  SW_STEP = 7;               // STEP Sw
const byte  SW_RIT = 8;                  // RIT Sw
const byte  SW_SPLIT = 9;               // SPLIT Sw
const byte  SW_CH1 = 10;                // Channel 1
const byte  SW_CH2 = 11;                //         2
const byte  SW_CH3 = 12;                //         3
const byte  SW_TX = 13;                  // TX Sw

const long  IF_FRQ = 5001500L;        // IF Frequency
const long  LW_FRQ = 7000000L;       // Lower Limit
const long  HI_FRQ = 7200000L;        // Upper Limit
const long  DEF_FRQ = 7050000L;     // Init Frequency
const long  DEF_STP = 1000L;          // Init STEP
const long  LW_RIT = -50000L;          // RIT Lower Limit
const long  HI_RIT = 50000L;             //     Upper Limit
const long  LW_VFO = IF_FRQ + LW_FRQ;     // Vfo Lower Limit
const long  HI_VFO = IF_FRQ + HI_FRQ;      //     Upper Limit
const long  DEF_VFO = IF_FRQ + DEF_FRQ;  // Vfo Default Frequency

const unsigned long  DDS_CLK = 67108864L; // TC170C030AF001 Clock 67.108864Mhz
const unsigned long  TWO_E26 = 67108864L; // 2^26
const byte  DDS_CMD1 = B00110011;          // CM3=1,CM2=1,CM1=0,CM0=0
                                                    // A2=1,A1=1
const byte  DDS_CMD2 = B00000001;          // A0=1
                                       

//----------  EEPROM Memory Address   ----------

const byte  Frq_Eep0 = 0x00;               // Frequency Ch0
const byte  Frq_Eep1 = 0x04;               //           Ch1                                              
const byte  Frq_Eep2 = 0x08;               //           Ch2
const byte  Frq_Eep3 = 0x0c;               //           Ch3

const byte  Stp_Eep0 = 0x10;               // STEP Ch0
const byte  Stp_Eep1 = 0x14;               //      Ch1                                              
const byte  Stp_Eep2 = 0x18;               //      Ch2
const byte  Stp_Eep3 = 0x1c;               //      Ch3

//----------  Encorder Pin Assign(INT)  --------

Rotary r = Rotary(ENC_A,ENC_B);            // 2 = ENC_A,3 = ENC_B

//----------  Memory Assign  -------------------

long Vfo_Dat = 0;                            // VFO Data
long Dds_Dat = 0;                            // DDS Data
long Rit_Dat = 0;                            // RIT Data
long Rit_Datb = 0;                           // RIT Data Old
long Enc_Stp = 0;                            // STEP
long Lng_Wk1 = 0;                            // Long Work1
long Lng_Wk2 = 0;                            // Long Work2

char *Lcd_Dat = "           ";              // Lcd Display Buffer

byte Byt_Chn = 0;                            // Channel SW
byte Byt_Chnb = 0;                           // Channel SW Old
byte Flg_Rit = 0;                             // RIT Flag
byte Flg_Ritb = 0;                            // RIT Flag Old
byte Flg_Tx = 0;                              // TX Flag
byte Flg_Spl = 0;                             // SPLIT Flag

//----------  Initialization  Program  ---------------

void setup(){
  lcd.init();                             // initialize the lcd
  lcd.backlight();                       // LCD backlight on

  pinMode(SW_STEP,INPUT_PULLUP);
  pinMode(SW_RIT,INPUT_PULLUP);
  pinMode(SW_SPLIT,INPUT_PULLUP);
  pinMode(SW_TX,INPUT_PULLUP);
  pinMode(SW_CH1,INPUT_PULLUP);
  pinMode(SW_CH2,INPUT_PULLUP);
  pinMode(SW_CH3,INPUT_PULLUP);

  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();                                 // INT Enable

  pinMode(DST,OUTPUT);
  pinMode(DCK,OUTPUT);
  pinMode(DDT,OUTPUT);

  Flg_Tx = 0;
  Flg_Rit = 0;
  Flg_Spl = 0;

  lcd.clear();
  Fnc_Chsw();                            // Channel Sw Read
  Byt_Chnb = Byt_Chn;
  Fnc_Eep_Rd();                          // EEPROM Read
}

//----------  Main program  ---------------

void loop() {
  if(Flg_Tx == 0){                            // Tx off?
    if(digitalRead(SW_STEP) == LOW){     // STEP Sw On?
      Fnc_Stp();                              //     Yes,STEP proc.
    }
    if(digitalRead(SW_RIT) == LOW){      // RIT Sw On?
      Fnc_Rit();                              //     Yes,RIT proc.
    }
    if(digitalRead(SW_SPLIT) == LOW){    // SPLIT Sw On?
      Fnc_Spl();                              //     Yes,SPLIT proc.
    }
    Fnc_Chsw();                              // Channel Sw read
 
    if(Byt_Chnb != Byt_Chn){               // Channnel SW Chenge?
      if(Byt_Chnb == 0){                    // Channnel 0?    
        Fnc_Eep_Sav4(Vfo_Dat,Frq_Eep0);  //   Yes,Vfo_Dat Save
        Fnc_Eep_Sav4(Enc_Stp,Stp_Eep0);  //       Enc_Step Save
        Flg_Ritb = Flg_Rit;
        Rit_Datb = Rit_Dat;
        Flg_Rit = 0;
        Flg_Spl = 0;
        Rit_Dat = 0;
      }
   
      if(Byt_Chnb != 0){                     // Other(Ch1-Ch3) Channnel?
        Flg_Rit = 0;
        Flg_Spl = 0;
        if((Byt_Chn == 0) && (Flg_Ritb == 1)){
          Flg_Rit = 1;
          Rit_Dat = Rit_Datb;
        }
      }
     
      Byt_Chnb = Byt_Chn;
      Fnc_Eep_Rd();
    }
  }
  if(digitalRead(SW_TX) == LOW){         // Tx On?
    Flg_Tx = 1;                              //    Yes,Flg_Tx Set
  }
  else{                                  
    Flg_Tx = 0;                              //     No,Flg_Tx Reset              
  }

  if(Flg_Rit == 1){                          // RIT?
    Dds_Dat = Vfo_Dat + Rit_Dat;         //    Yes,Dds_Dat Set
  }
  else{
    Dds_Dat = Vfo_Dat;                     //    No,Dds_Dat Set
  }

  if(Flg_Tx == 1){                           // Tx?
    if(Flg_Spl == 1){                        // SPLIT?
      Dds_Dat = Vfo_Dat + Rit_Dat;        //    Yes,Dds_Dat Set
    }
      else{
        Dds_Dat = Vfo_Dat;                  //    No,Dds_Dat Set
      }
  }
  Fnc_Dds(Dds_Dat);                         // TC170C030AF001 DDS Out
  Fnc_Lcd();                                 // LCD Display
  delay(100);
}

//----------  Encorder procedure(INT)  ---------------

ISR(PCINT2_vect) {
  unsigned char result = r.process();

  if(Flg_Tx == 0){
    if(result) {  
      if(result == DIR_CW){
        Lng_Wk1 = Vfo_Dat + Enc_Stp;
        Lng_Wk2 = Rit_Dat + Enc_Stp;
      }
      else{
          Lng_Wk1 = Vfo_Dat - Enc_Stp;
          Lng_Wk2 = Rit_Dat - Enc_Stp;
      }    
      if((Flg_Rit == 1) || (Flg_Spl == 1)){
        Rit_Dat = Lng_Wk2;
      }
      else{
        Vfo_Dat = Lng_Wk1;
        Rit_Dat = 0;
      }
      Vfo_Dat = constrain(Vfo_Dat,LW_VFO,HI_VFO);

      if(Flg_Spl == 1){
        Rit_Dat = constrain(Rit_Dat,(LW_VFO - Vfo_Dat),(HI_VFO - Vfo_Dat));
      }
      else{
        Rit_Dat = constrain(Rit_Dat,LW_RIT,HI_RIT);
      }
    }
  }
}

//----------  Function DDS set  ---------------

void Fnc_Dds(double frquency){
  unsigned long wrk = frquency * TWO_E26 / DDS_CLK;
  unsigned int wrk1,wrk2;

  wrk1 = wrk >> 10;
  wrk2 = wrk << 6;
  wrk2 = wrk2 & 0xffc0 | DDS_CMD1;

  digitalWrite(DDT,DDS_CMD2 & 0x01);
  digitalWrite(DCK,HIGH);
  digitalWrite(DCK,LOW);

  shiftOut(DDT,DCK,LSBFIRST,wrk2);
  shiftOut(DDT,DCK,LSBFIRST,(wrk2 >> 8));

  shiftOut(DDT,DCK,LSBFIRST,wrk1);
  shiftOut(DDT,DCK,LSBFIRST,(wrk1 >> 8));

  digitalWrite(DST,LOW);
  digitalWrite(DST,HIGH);
}

//----------  Function Encorder STEP  ---------

void Fnc_Stp(){
  if(Enc_Stp == 10){                      // Step = 10Hz ?
    Enc_Stp = 100000;                     //   Yes,100khz set
    }
    else{
      Enc_Stp = Enc_Stp / 10;             // Step down 1 digit
      }
  delay(100);
  Fnc_Step_Disp();
  Fnc_Lcd();
  while(digitalRead(SW_STEP) == LOW)
    ;
  delay(250);
}

//----------  Function STEP Display  ----------

void Fnc_Step_Disp(){
  lcd.setCursor(0,1);
  switch(Enc_Stp){
    case 10:
      lcd.print("10  ");
      break;
    case 100:
      lcd.print("100 ");
      break;
    case 1000:
      lcd.print("1k  ");
      break;
    case 10000:
      lcd.print("10k ");
      break;
    case 100000:
      lcd.print("100k");
      break;
  }
}

//----------  Function String Dot Edit  --------
 
char *Fnc_Dot_Edit(char *str,long n){
  int  i = 0;                           // Write the number
  char *p = str;
  unsigned long  u = abs(n);

  do{
    *p++ = "0123456789"[u % 10];
    u = u / 10;
    i++;
    if((0 != u) && (0 == (i % 3)))
      *p++ = '.';
    }
  while( 0 != u );

  if ( n < 0 )
     *p++ = '-';
   *p = '\0';
   Fnc_Revr( str );
   return str;
}

//----------  Function String Reverse  ---------

void Fnc_Revr(char *str){
  int i,n;
  char c;

  n=strlen(str);
  for(i = 0;i < n / 2;i++){
    c=str[i];
    str[i]=str[n - i - 1];
    str[n - i - 1]=c;
  }
}

//----------  Function Save EEPROM 2byte  ---------

void Fnc_Eep_Sav2(unsigned int value,int address){
  address += 1;
  for(int i = 0;i < 2;i++){
    byte toSave = value & 0xFF;
    if(EEPROM.read(address) != toSave){
      EEPROM.write(address,toSave);
      }
    value = value >> 8;
    address--;
  }
}

//----------  Function Save EEPROM 4byte  ---------

void Fnc_Eep_Sav4(long value,int address){
  address += 3;
  for(int i = 0;i < 4;i++){
    byte toSave = value & 0xFF;
    if(EEPROM.read(address) != toSave){
      EEPROM.write(address,toSave);
      }
    value = value >> 8;
    address--;
  }
}

//----------  Function Load EEPROM 2byte  ---------

unsigned int  Fnc_Eep_Lod2(int address){
  unsigned int value = EEPROM.read(address);
  value = value << 8;
  return value | EEPROM.read(address + 1);
}

//----------  Function Load EEPROM 4byte  ---------

long Fnc_Eep_Lod4(int address){
  long value = 0;
  for(int i = 0;i < 4;i++){
    value = value | EEPROM.read(address);
    if( i < 3){
      value = value << 8;
      address++;
      }
  }
  return value;
}

//----------  Function LCD Display  ---------

void Fnc_Lcd(){
  if(Flg_Tx == 1){
    lcd.setCursor(0,0);
    lcd.print("T");
  }
  else{
    lcd.setCursor(0,0);
    lcd.print(Byt_Chn);
  }

  Fnc_Step_Disp();

  if(Flg_Rit == 1){
    lcd.setCursor(5,1);
    lcd.print("R:          ");
    Fnc_Dot_Edit(Lcd_Dat,Rit_Dat);
    lcd.setCursor(7,1);
    lcd.print(Lcd_Dat);
    if((Rit_Dat >= 1000) || (Rit_Dat <= -1000)){
     lcd.print("k");
    }
  }

  if(Flg_Spl == 1){
    lcd.setCursor(5,1);
    lcd.print("X:          ");
    Fnc_Dot_Edit(Lcd_Dat,Rit_Dat);
    lcd.setCursor(7,1);
    lcd.print(Lcd_Dat);
    if((Rit_Dat >= 1000) || (Rit_Dat <= -1000)){
      lcd.print("k");
    }
  }

  if((Flg_Rit == 0) && (Flg_Spl == 0)){
    Fnc_Dot_Edit(Lcd_Dat,Vfo_Dat - IF_FRQ);
    lcd.setCursor(1,0);
    lcd.print(":              ");
    lcd.setCursor(3,0);
    lcd.print(Lcd_Dat);
    lcd.print("MHz");

    lcd.setCursor(5,1);
    lcd.print("     JA2GQP");
  }
}


//----------  Function Rit  ---------

void Fnc_Rit(){
  if(Flg_Rit == 0){
    Rit_Dat = 0;
    Flg_Rit = 1;
    Flg_Spl = 0;
    switch(Byt_Chn){
      case 1:
        Fnc_Eep_Sav4(Vfo_Dat,Frq_Eep1);
        Fnc_Eep_Sav4(Enc_Stp,Stp_Eep1);
        break;
      case 2:
        Fnc_Eep_Sav4(Vfo_Dat,Frq_Eep2);
        Fnc_Eep_Sav4(Enc_Stp,Stp_Eep2);
        break;
      case 3:
        Fnc_Eep_Sav4(Vfo_Dat,Frq_Eep3);
        Fnc_Eep_Sav4(Enc_Stp,Stp_Eep3);
        break;
      default:
        Fnc_Eep_Sav4(Vfo_Dat,Frq_Eep0);
        Fnc_Eep_Sav4(Enc_Stp,Stp_Eep0);
        break;
    }
  }
  else{
    Flg_Rit = 0;
  }
  while(digitalRead(SW_RIT) == LOW)
    ;
  delay(250);
}

//----------  Function Channel SW Check  ---------

void Fnc_Chsw(){
  if(digitalRead(SW_CH1) == LOW){
    Byt_Chn = 1;
  }
  else if(digitalRead(SW_CH2) == LOW){
    Byt_Chn = 2;
  }
  else if(digitalRead(SW_CH3) == LOW){
    Byt_Chn = 3;
  }
  else{
    Byt_Chn = 0;
  }
}

//----------  Function EEPROM Read  ---------

void Fnc_Eep_Rd(){
  if(Fnc_Eep_Lod4(Frq_Eep0) <= LW_VFO){
    Vfo_Dat = DEF_VFO;
    Fnc_Eep_Sav4(Vfo_Dat,Frq_Eep0);
    Fnc_Eep_Sav4(Vfo_Dat,Frq_Eep1);
    Fnc_Eep_Sav4(Vfo_Dat,Frq_Eep2);
    Fnc_Eep_Sav4(Vfo_Dat,Frq_Eep3);
  }
  else{
    switch(Byt_Chn){
      case 1:
        Vfo_Dat = Fnc_Eep_Lod4(Frq_Eep1);
        break;
      case 2:
        Vfo_Dat = Fnc_Eep_Lod4(Frq_Eep2);
        break;
      case 3:
        Vfo_Dat = Fnc_Eep_Lod4(Frq_Eep3);
        break;
      default:
        Vfo_Dat = Fnc_Eep_Lod4(Frq_Eep0);
        break;
    }
  }
  if(Vfo_Dat <= 0){
    Vfo_Dat = DEF_VFO;
  }
  if(Fnc_Eep_Lod4(Stp_Eep0) <= 0){
    Enc_Stp = DEF_STP;
    Fnc_Eep_Sav4(Enc_Stp,Stp_Eep0);
    Fnc_Eep_Sav4(Enc_Stp,Stp_Eep1);
    Fnc_Eep_Sav4(Enc_Stp,Stp_Eep2);
    Fnc_Eep_Sav4(Enc_Stp,Stp_Eep3);
  }
  else{
    switch(Byt_Chn){
      case 1:
        Enc_Stp = Fnc_Eep_Lod4(Stp_Eep1);
        break;
      case 2:
        Enc_Stp = Fnc_Eep_Lod4(Stp_Eep2);
        break;
      case 3:
        Enc_Stp = Fnc_Eep_Lod4(Stp_Eep3);
        break;
      default:
        Enc_Stp = Fnc_Eep_Lod4(Stp_Eep0);
        break;
    }
  }
  if(Enc_Stp <= 0){
    Enc_Stp = DEF_STP;
  }
}

//----------  Function Split  ---------

void Fnc_Spl(){
  if(Flg_Spl == 0){
    Flg_Spl = 1;
    Flg_Rit = 0;
    Rit_Dat = 0;
  }
  else{
    Flg_Spl = 0;
  }
  while(digitalRead(SW_SPLIT) == LOW)
    ;
  delay(250);
}

     
    

2015年3月4日水曜日

FM PiPi ラジオ

今年の製作講習会のテーマは、FM PiPi専用ラジオである。既に公開のコミュニティラジオその物であるが、回路の見直しと定数変更した。最終的な形としてトロイダルコア版とFCZコイル版の2タイプを試験した。同じ機能の物をコイルタイプの違いで作った例が殆ど見当らない。両タイプをPCB化し、比較した。 
トロイダルコアを使った回路図である。同調周波数は、シュミレーションで定数を決めたが、インダクタのズレと、トリマーの最少容量(10p程度)による誤差で、トリマが殆ど抜けた状態で同調。ピークに合わせられるので、定数変更は行わない。
公開済みのPCBにデカップリングを追加したのみ。 PCBサイズ 62x48。















FCZコイル版
FCZタイプのコアを使う方が安定動作を期待出来る。ただ、FCZ純正品は生産が終了している。ジャンクコイル(TOKOなどのFCZタイプ)またはコアキットを使って手巻き、もしくは、FCZ純正タイプ3rdパーティ製使用の選択肢がある。ただ、費用を抑えるのであれば、手巻きのみ。 10.7Mhzコイルは、市販品。それ以外、手巻した。 
FCZタイプを使った回路図である。コイルの巻き数は、FCZ144相当。また、2ピンにデカップリングを入れてないが、何ら問題は起きてない。

PCBサイズ 62x48。ただ、部品を実装した高さが、トロイダルコア版より高い。














結果
局発に水晶発振子を使う限り、性能差はないが、自励発振(LC発振)の場合、トロイダルコアの方が安定(対電圧)していた。また、コイルの手巻きは、FCZタイプより、トロイダルコアの方が有利であろう。100MHzh以下であれば、トロイダルコアでも実用的な物ができると思う。技術講習会であれば、トロイダルコア。入門者向け講習会であれば、3rdパーティのFCZタイプを利用するのが良いと思う。



     

2015年2月28日土曜日

SPAM Speaker

SPAMの空き缶をスピーカのエンクロージャに使った。ユニットは、aitendoで販売している5cmの物で、適度に低音も出ており、抜けもこのサイズにしては良い。ユニットをホットボンドで固定したので、見栄えが悪い。構造的にバスレフ(適当に隙間があるので)。肝心な音は、無線機で使うには、十分。ラジオ用としては、実用レベル。 
正面は、綺麗でない。 見栄え重視で作れば、良いものが出来るであろう。内部側面に、吸音材を入れている。背面側は、聞いた感じで、吸音材なしにした。
背面は端子がある。SPAM缶は、アルミニューム製の為、色々なケーシングに使いたい。  

2015年2月5日木曜日

コミニュティFMラジオ2

コミニュティFM回路をPCB化して、ケース(95x65x23)に収納。ユニバーサル基板では、多少、不安定な部分があった。PCB化により、安定度が増した。アンテナは、ゲインが低いものの、強電界であればノーノイズで受信出来る。今回、回路定数の見直しも行った。








部品を実装したPCB。(ケースに収納した物と別基板)















T37-6の巻き数を変更。




基板サイズ(47x59)

シュミレーション前は、共振定数を実装した。実際に受信すると、トリマーが抜けた位置であった。この為、シュミレーションを行った。   























   

2015年2月1日日曜日

コミニュティFMラジオ

アナログ防災無線に代わり、コミニュティFMラジオにより地域情報が流れている。この為、専用のFMラジオを作った。回路構成は、TA7792P+TA7368Pで、LOに水晶を使用。FCZコイルを使わず、T37-6で統一。水晶は、価格の安いもの使い、基本波の4高調波を使っている。コイルのQが高い為、感度も良い。







回路図である。LOは、当初、複同調で4倍波を取り出したが、単同調でも実使用で大差が無かった。トロイダルコアは、2次コイルを巻かず、コンデンサ結合。

  

2015年1月20日火曜日

Arduino AD9851 DDS VFO

   AD9851を使ったDDS VFOである。Arduino版AD9834 DDS VFO同様、LCD表示にI2Cを用い、I/O割り付けも同じにした。AD9851基準クロックは、32MHz(手持ち品流用)としたため、60Mhz位まで使えるであろう。従って、LPFカットオフ 60MHzで設計した。
















回路図。
出力回路は、抵抗で終端したが、1対1のトランスにすると出力(現在、160uW at 17.05MHz)が6dB増える。ミキサー回路に使うのであれば、このままでも良いかも知れない。

Filter DesignによるLPFシュミレーション結果。












program

AD9851とAD9850の違いは、DDS_CLK = 192000000LとDDS_CMD = B00000001の2行である。
(基準クロック 6倍のパラメータ)
//////////////////////////////////////////////////////////////////////
//  AD9851 DDS VFO Premixed type program ver.1.0
//
//    Copyright(C)2014-2015.JA2GQP.All rights reserved.
//
//      7.000MHz to 7.200MHz Limitted.
//      (Target frequency = IF frquency + frequency)
//                                                  2015/1/20
//                                                  JA2GQP
//--------------------------------------------------------------------
//  Function
//    1.Upper Heterodyne(Target Frequency = IF Frequency + Frequency)
//    2.RIT Operation(-50kHZ to 50kHZ)
//    3.STEP(100k,10k,1k,100,10)
//    4.Memory Operation is Push RIT
//      (Frequency and Step)
//    5.Protection Operation At The Time Of Transmission
//    6.Channel Memory.Main Channel(Ch0) + 3 Channel(Ch1,Ch2,Ch3)
//    7.Split Operation(7.00MHz to 7.20MHz Limited!)
//--------------------------------------------------------------------
//  Library
//  http://www.buxtronix.net/2011/10/rotary-encoders-done-properly.html
//
//////////////////////////////////////////////////////////////////////

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <rotary.h>
#include <EEPROM.h>

//----------  LCD Pin Assign  ------------------

LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 for
                                   // a 16 chars and 2 line display

//----------  Define Constant Value   ----------
                                             
const byte  ENC_A = 2;                     // Encorder A
const byte  ENC_B = 3;                     // Encoeder B
const byte  DATA = 4;                      // DIO4
const byte  W_CLK = 5;                     // DIO5
const byte  FQ_UD = 6;                     // DIO6
const byte  SW_STEP = 7;                   // STEP Sw
const byte  SW_RIT = 8;                    // RIT Sw
const byte  SW_SPLIT = 9;                  // SPLIT Sw
const byte  SW_CH1 = 10;                   // Channel 1
const byte  SW_CH2 = 11;                   //         2
const byte  SW_CH3 = 12;                   //         3
const byte  SW_TX = 13;                    // TX Sw

const long  IF_FRQ = 9996500L;             // IF Frequency

const long  LW_FRQ = 7000000L;             // Lower Limit
const long  HI_FRQ = 7200000L;             // Upper Limit
const long  DEF_FRQ = 7050000L;            // Init Frequency
const long  DEF_STP = 1000L;               // Init STEP
const long  LW_RIT = -50000L;              // RIT Lower Limit
const long  HI_RIT = 50000L;               // RIT Upper Limit
const long  LW_VFO = IF_FRQ + LW_FRQ;      // Vfo Lower Limit
const long  HI_VFO = IF_FRQ + HI_FRQ;      // Vfo Upper Limit
const long  DEF_VFO = IF_FRQ + DEF_FRQ;    // Vfo Default Frequency

const unsigned long  DDS_CLK = 192000000L; // AD9851 Clock(32MHz * 6)
const unsigned long  TWO_E32 = 4294967295L;// 2^32
const byte  DDS_CMD = B00000001;           // AD9851 Command

//----------  EEPROM Memory Address   ----------

const byte  Frq_Eep0 = 0x00;               // Frequency Ch0
const byte  Frq_Eep1 = 0x04;               //           Ch1                                              
const byte  Frq_Eep2 = 0x08;               //           Ch2
const byte  Frq_Eep3 = 0x0c;               //           Ch3

const byte  Stp_Eep0 = 0x10;               // STEP Ch0
const byte  Stp_Eep1 = 0x14;               //      Ch1                                              
const byte  Stp_Eep2 = 0x18;               //      Ch2
const byte  Stp_Eep3 = 0x1c;               //      Ch3

//----------  Encorder Pin Assign(INT)  --------

Rotary r = Rotary(ENC_A,ENC_B);            // 2 = ENC_A,3 = ENC_B

//----------  Memory Assign  -------------------

long Vfo_Dat = 0;                         // VFO Data
long Dds_Dat = 0;                         // DDS Data
long Rit_Dat = 0;                         // RIT Data
long Rit_Datb = 0;                        // RIT Data Old
long Enc_Stp = 0;                         // STEP
long Lng_Wk1 = 0;                         // Long Work1
long Lng_Wk2 = 0;                         // Long Work2

char *Lcd_Dat = "           ";            // Lcd Display Buffer

byte Byt_Chn = 0;                         // Channel SW
byte Byt_Chnb = 0;                        // Channel SW Old
byte Flg_Rit = 0;                         // RIT Flag
byte Flg_Ritb = 0;                        // RIT Flag Old
byte Flg_Tx = 0;                          // TX Flag
byte Flg_Spl = 0;                         // SPLIT Flag

//----------  Initialization  Program  ---------------

void setup(){
  lcd.init();                            // initialize the lcd
  lcd.backlight();                       // LCD backlight on

  pinMode(SW_STEP,INPUT_PULLUP);
  pinMode(SW_RIT,INPUT_PULLUP);
  pinMode(SW_SPLIT,INPUT_PULLUP);
  pinMode(SW_TX,INPUT_PULLUP);
  pinMode(SW_CH1,INPUT_PULLUP);
  pinMode(SW_CH2,INPUT_PULLUP);
  pinMode(SW_CH3,INPUT_PULLUP);

  lcd.begin(16, 2);                        // LCD 16*2

  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();                                    // INT Enable

  pinMode(FQ_UD,OUTPUT);
  pinMode(W_CLK,OUTPUT);
  pinMode(DATA,OUTPUT);

  Flg_Tx = 0;
  Flg_Rit = 0;
  Flg_Spl = 0;

  lcd.clear();
  Fnc_Chsw();                                // Channel Sw Read
  Byt_Chnb = Byt_Chn;
  Fnc_Eep_Rd();                              // EEPROM Read
}

//----------  Main program  ---------------

void loop() {
  if(Flg_Tx == 0){                          // Tx off?
    if(digitalRead(SW_STEP) == LOW){        // STEP Sw On?
      Fnc_Stp();                            //     Yes,STEP proc.
    }
    if(digitalRead(SW_RIT) == LOW){         // RIT Sw On?
      Fnc_Rit();                            //     Yes,RIT proc.
    }
    if(digitalRead(SW_SPLIT) == LOW){       // SPLIT Sw On?
      Fnc_Spl();                            //     Yes,SPLIT proc.
    }
    Fnc_Chsw();                             // Channel Sw read
 
    if(Byt_Chnb != Byt_Chn){                // Channnel SW Chenge?
      if(Byt_Chnb == 0){                    // Channnel 0?    
        Fnc_Eep_Sav4(Vfo_Dat,Frq_Eep0);     //   Yes,Vfo_Dat Save
        Fnc_Eep_Sav4(Enc_Stp,Stp_Eep0);     //       Enc_Step Save
        Flg_Ritb = Flg_Rit;
        Rit_Datb = Rit_Dat;
        Flg_Rit = 0;
        Flg_Spl = 0;
        Rit_Dat = 0;
      }
   
      if(Byt_Chnb != 0){                    // Other(Ch1-Ch3) Channnel?
        Flg_Rit = 0;
        Flg_Spl = 0;
        if((Byt_Chn == 0) && (Flg_Ritb == 1)){
          Flg_Rit = 1;
          Rit_Dat = Rit_Datb;
        }
      }
     
      Byt_Chnb = Byt_Chn;
      Fnc_Eep_Rd();
    }
  }
  if(digitalRead(SW_TX) == LOW){            // Tx On?
    Flg_Tx = 1;                             //    Yes,Flg_Tx Set
  }
  else{                                  
    Flg_Tx = 0;                            //     No,Flg_Tx Reset              
  }

  if(Flg_Rit == 1){                        // RIT?
    Dds_Dat = Vfo_Dat + Rit_Dat;           //    Yes,Dds_Dat Set
  }
  else{
    Dds_Dat = Vfo_Dat;                     //    No,Dds_Dat Set
  }

  if(Flg_Tx == 1){                         // Tx?
    if(Flg_Spl == 1){                      // SPLIT?
      Dds_Dat = Vfo_Dat + Rit_Dat;         //    Yes,Dds_Dat Set
    }
      else{
        Dds_Dat = Vfo_Dat;                 //    No,Dds_Dat Set
      }
  }
  Fnc_Dds(Dds_Dat);                        // AD9850 DDS Out
  Fnc_Lcd();                               // LCD Display
  delay(100);
}

//----------  Encorder procedure(INT)  ---------------

ISR(PCINT2_vect) {
  unsigned char result = r.process();

  if(Flg_Tx == 0){
    if(result) {  
      if(result == DIR_CW){
        Lng_Wk1 = Vfo_Dat + Enc_Stp;
        Lng_Wk2 = Rit_Dat + Enc_Stp;
      }
      else{
          Lng_Wk1 = Vfo_Dat - Enc_Stp;
          Lng_Wk2 = Rit_Dat - Enc_Stp;
      }    
      if((Flg_Rit == 1) || (Flg_Spl == 1)){
        Rit_Dat = Lng_Wk2;
      }
      else{
        Vfo_Dat = Lng_Wk1;
        Rit_Dat = 0;
      }
      Vfo_Dat = constrain(Vfo_Dat,LW_VFO,HI_VFO);

      if(Flg_Spl == 1){
        Rit_Dat = constrain(Rit_Dat,(LW_VFO - Vfo_Dat),(HI_VFO - Vfo_Dat));
      }
      else{
        Rit_Dat = constrain(Rit_Dat,LW_RIT,HI_RIT);
      }
    }
  }
}

//----------  Function DDS set  ---------------

void Fnc_Dds(double frquency){
  unsigned long wrk = frquency * TWO_E32 / DDS_CLK;

  digitalWrite(FQ_UD,LOW);

  shiftOut(DATA,W_CLK,LSBFIRST,wrk);
  shiftOut(DATA,W_CLK,LSBFIRST,(wrk >> 8));
  shiftOut(DATA,W_CLK,LSBFIRST,(wrk >> 16));
  shiftOut(DATA,W_CLK,LSBFIRST,(wrk >> 24));
  shiftOut(DATA,W_CLK,LSBFIRST,DDS_CMD);   // AD9851 command

  digitalWrite(FQ_UD,HIGH);
}

//----------  Function Encorder STEP  ---------

void Fnc_Stp(){
  if(Enc_Stp == 10){                      // Step = 10Hz ?
    Enc_Stp = 100000;                     //   Yes,100khz set
    }
    else{
      Enc_Stp = Enc_Stp / 10;             // Step down 1 digit
      }
//  delay(250);
  Fnc_Step_Disp();
//  Fnc_Lcd();
  while(digitalRead(SW_STEP) == LOW)
    ;
  delay(250);
}

//----------  Function STEP Display  ----------

void Fnc_Step_Disp(){
  lcd.setCursor(0,1);
  lcd.print("    ");                    // Clear step display
  lcd.setCursor(0,1);
  if(1 <= (Enc_Stp / 1000)){            // kiro?        
    lcd.print(Enc_Stp / 1000);          //   Yes,Convert kiro
    lcd.print("k");
    }
    else{
      lcd.print(Enc_Stp);
      }
}

//----------  Function String Dot Edit  --------
 
char *Fnc_Dot_Edit(char *str,long n){
  int  i = 0;                           // Write the number
  char *p = str;
  unsigned long  u = abs(n);

  do{
    *p++ = "0123456789"[u % 10];
    u = u / 10;
    i++;
    if((0 != u) && (0 == (i % 3)))
      *p++ = '.';
    }
  while( 0 != u );

  if ( n < 0 )
     *p++ = '-';
   *p = '\0';
   Fnc_Revr( str );
   return str;
}

//----------  Function String Reverse  ---------

void Fnc_Revr(char *str){
  int i,n;
  char c;

  n=strlen(str);
  for(i = 0;i < n / 2;i++){
    c=str[i];
    str[i]=str[n - i - 1];
    str[n - i - 1]=c;
  }
}

//----------  Function Save EEPROM 2byte  ---------

void Fnc_Eep_Sav2(unsigned int value,int address){
  address += 1;
  for(int i = 0;i < 2;i++){
    byte toSave = value & 0xFF;
    if(EEPROM.read(address) != toSave){
      EEPROM.write(address,toSave);
      }
    value = value >> 8;
    address--;
  }
}

//----------  Function Save EEPROM 4byte  ---------

void Fnc_Eep_Sav4(long value,int address){
  address += 3;
  for(int i = 0;i < 4;i++){
    byte toSave = value & 0xFF;
    if(EEPROM.read(address) != toSave){
      EEPROM.write(address,toSave);
      }
    value = value >> 8;
    address--;
  }
}

//----------  Function Load EEPROM 2byte  ---------

unsigned int  Fnc_Eep_Lod2(int address){
  unsigned int value = EEPROM.read(address);
  value = value << 8;
  return value | EEPROM.read(address + 1);
}

//----------  Function Load EEPROM 4byte  ---------

long Fnc_Eep_Lod4(int address){
  long value = 0;
  for(int i = 0;i < 4;i++){
    value = value | EEPROM.read(address);
    if( i < 3){
      value = value << 8;
      address++;
      }
  }
  return value;
}

//----------  Function LCD Display  ---------

void Fnc_Lcd(){
  if(Flg_Tx == 1){
    lcd.setCursor(0,0);
    lcd.print("T");
  }
  else{
    lcd.setCursor(0,0);
    lcd.print(Byt_Chn);
  }

  Fnc_Step_Disp();

  if(Flg_Rit == 1){
    lcd.setCursor(5,1);
    lcd.print("R:          ");
    Fnc_Dot_Edit(Lcd_Dat,Rit_Dat);
    lcd.setCursor(7,1);
    lcd.print(Lcd_Dat);
    if((Rit_Dat >= 1000) || (Rit_Dat <= -1000)){
      lcd.print("k");
    }
  }

  if(Flg_Spl == 1){
    lcd.setCursor(5,1);
    lcd.print("X:          ");
    Fnc_Dot_Edit(Lcd_Dat,Rit_Dat);
    lcd.setCursor(7,1);
    lcd.print(Lcd_Dat);
    if((Rit_Dat >= 1000) || (Rit_Dat <= -1000)){
      lcd.print("k");
    }
  }

  if((Flg_Rit == 0) && (Flg_Spl == 0)){
    Fnc_Dot_Edit(Lcd_Dat,Vfo_Dat - IF_FRQ);
    lcd.setCursor(1,0);
    lcd.print(":              ");
    lcd.setCursor(3,0);
    lcd.print(Lcd_Dat);
    lcd.print("MHz");

    lcd.setCursor(5,1);
    lcd.print("     JA2GQP");
  }
}


//----------  Function Rit  ---------

void Fnc_Rit(){
  if(Flg_Rit == 0){
    Rit_Dat = 0;
    Flg_Rit = 1;
    Flg_Spl = 0;
    switch(Byt_Chn){
      case 1:
        Fnc_Eep_Sav4(Vfo_Dat,Frq_Eep1);
        Fnc_Eep_Sav4(Enc_Stp,Stp_Eep1);
        break;
      case 2:
        Fnc_Eep_Sav4(Vfo_Dat,Frq_Eep2);
        Fnc_Eep_Sav4(Enc_Stp,Stp_Eep2);
        break;
      case 3:
        Fnc_Eep_Sav4(Vfo_Dat,Frq_Eep3);
        Fnc_Eep_Sav4(Enc_Stp,Stp_Eep3);
        break;
      default:
        Fnc_Eep_Sav4(Vfo_Dat,Frq_Eep0);
        Fnc_Eep_Sav4(Enc_Stp,Stp_Eep0);
        break;
    }
  }
  else{
    Flg_Rit = 0;
  }
  while(digitalRead(SW_RIT) == LOW)
    ;
  delay(250);
}

//----------  Function Channel SW Check  ---------

void Fnc_Chsw(){
  if(digitalRead(SW_CH1) == LOW){
    Byt_Chn = 1;
  }
  else if(digitalRead(SW_CH2) == LOW){
    Byt_Chn = 2;
  }
  else if(digitalRead(SW_CH3) == LOW){
    Byt_Chn = 3;
  }
  else{
    Byt_Chn = 0;
  }
}

//----------  Function EEPROM Read  ---------

void Fnc_Eep_Rd(){
  if(Fnc_Eep_Lod4(Frq_Eep0) <= LW_VFO){
    Vfo_Dat = DEF_VFO;
    Fnc_Eep_Sav4(Vfo_Dat,Frq_Eep0);
    Fnc_Eep_Sav4(Vfo_Dat,Frq_Eep1);
    Fnc_Eep_Sav4(Vfo_Dat,Frq_Eep2);
    Fnc_Eep_Sav4(Vfo_Dat,Frq_Eep3);
  }
  else{
    switch(Byt_Chn){
      case 1:
        Vfo_Dat = Fnc_Eep_Lod4(Frq_Eep1);
        break;
      case 2:
        Vfo_Dat = Fnc_Eep_Lod4(Frq_Eep2);
        break;
      case 3:
        Vfo_Dat = Fnc_Eep_Lod4(Frq_Eep3);
        break;
      default:
        Vfo_Dat = Fnc_Eep_Lod4(Frq_Eep0);
        break;
    }
  }
  if(Vfo_Dat <= 0){
    Vfo_Dat = DEF_VFO;
  }
  if(Fnc_Eep_Lod4(Stp_Eep0) <= 0){
    Enc_Stp = DEF_STP;
    Fnc_Eep_Sav4(Enc_Stp,Stp_Eep0);
    Fnc_Eep_Sav4(Enc_Stp,Stp_Eep1);
    Fnc_Eep_Sav4(Enc_Stp,Stp_Eep2);
    Fnc_Eep_Sav4(Enc_Stp,Stp_Eep3);
  }
  else{
    switch(Byt_Chn){
      case 1:
        Enc_Stp = Fnc_Eep_Lod4(Stp_Eep1);
        break;
      case 2:
        Enc_Stp = Fnc_Eep_Lod4(Stp_Eep2);
        break;
      case 3:
        Enc_Stp = Fnc_Eep_Lod4(Stp_Eep3);
        break;
      default:
        Enc_Stp = Fnc_Eep_Lod4(Stp_Eep0);
        break;
    }
  }
  if(Enc_Stp <= 0){
    Enc_Stp = DEF_STP;
  }
}

//----------  Function Split  ---------

void Fnc_Spl(){
  if(Flg_Spl == 0){
    Flg_Spl = 1;
    Flg_Rit = 0;
    Rit_Dat = 0;
  }
  else{
    Flg_Spl = 0;
  }
  while(digitalRead(SW_SPLIT) == LOW)
    ;
  delay(250);
}