2024年12月12日木曜日

6m AM VFO

6m AM TRX専用のVFOを作った。ATmega1614、si5351とoled表示器を使ったVFO。6m AM TRXはダブルス―パー方式を採用したので、si5351の全てのチャンネルを使うことにした。また、新しい機能として受信SHIFTを追加した。SHIFT機能は、受信周波数を-1.5k(LSB)、+1.5k(USB)、SHIFTゼロ(ブランク)をSHIFT SWで切り替えて行う仕様である。







回路図

















 

スケッチ

//////////////////////////////////////////////////////////////////////
//  si5351a oled(128x32) VFO program ver.1.0
//    Copyright(C)2019.JA2GQP.All rights reserved.
//
//                                                2024/12/11
//                                                  JA2GQP
//--------------------------------------------------------------------
//  Function
//    1.STEP(10k,1k,100)
//    2.Ofset(0,-1500,+1500)
//    3.Automatic memory
//    44.Protection Operation At The Time Of Transmission
//    5.S-Meter
//////////////////////////////////////////////////////////////////////

#include "src/Rotary.h"             // http://www.buxtronix.net/2011/10/rotary-encoders-done-properly.html
#include "src/si5351a.h"            // https://github.com/etherkit/Si5351Arduino, v2.1.0
#include <Tiny4kOLED.h>
#include "font/Tlcdnums14x24.h"
#include "font/Tlabels.h"
#include "font/Tpixels.h"
#include <EEPROM.h>

//---------- I/O Port -------------------------------

#define SW_STEP 0                   // STEP SW
#define SW_TX   1                   // TX/RX
#define ENC_A   2                   // encorder A
#define ENC_B   3                   //          B
#define SW_RIT  4                   // RIT SW
#define SW_OFT  5                   // OFFSET SW
#define AD_SM   10                  // S-meter AD

//---------- Set Device -----------------------------

Rotary r = Rotary(ENC_B, ENC_A);              

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

#define Eep_Init    0x00            // Eep Init(1byte)
#define Eep_Oflg    0x02            // Offset flag(1byte)
#define Eep_Odat    0x04            // Offset data(4byte)
#define Eep_Freq    0x10            // Frequency(4byte)
#define Eep_Step    0x30            // STEP(4byte)

//---------- frquency data --------------------------

//---- frequency limit offset
#define DEF_F   50600000            // default frequency
#define LOW_F   50000000
#define HI_F    51000000

//---- receiver IF offset
#define IF_FREQ 10700000
#define RX_FIL  455000
const unsigned long RX_LO =   IF_FREQ - RX_FIL;          

//---- encorder STEP
#define STEP_100  100               // STEP 100Hz
#define STEP_1k   1000              //      1k
#define STEP_10k  10000             //      10k

//---- RIT limit
#define LW_RIT  -5000               // RIT low -5k
#define HI_RIT   5000               // RIT high 5k

//---- OFFSET limit
#define LW_OFT  -1500               // OFFSET low -1500
#define HI_OFT   1500               // OFFSET high 1500

//---------- etc ------------------------------------

const byte Int_End = 73;             // Initial end code

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

unsigned long Vfo_Dat;                // VFO Data
int Rit_Dat = 0;                      // RIT Data
int Oft_Dat = 0;                      // OFFSET Data
unsigned long Enc_Step;               // STEP
unsigned int Val_Smeter = 0;
unsigned int Val_Smeterb = 1;

unsigned long Time_Passd;             // int to hold the arduino miilis since startup
unsigned long Time_smeter;            // smeter scan Time
byte Flg_eepWT = 0;                   // EEP Write Flag

byte Flg_Over = 0;
byte Flg_Tx = 0;
byte Flg_Rit = 0;                     // RIT Flag
byte Flg_Oflg = 0;                    // OFFSET Flag
long Lng_Wk;                          // Long Work
int  Int_Wk;                          // Int Work

//----------  setup  ----------------------------------------------------

void setup() {
  pinMode(SW_STEP, INPUT_PULLUP);
  pinMode(SW_TX, INPUT_PULLUP);          
  pinMode(SW_RIT, INPUT_PULLUP);          
  pinMode(SW_OFT, INPUT_PULLUP);          

  attachInterrupt(ENC_A, rotary_encoder, CHANGE);
  attachInterrupt(ENC_B, rotary_encoder, CHANGE);

  oled.begin();
  oled.clear();
  oled.on();

  if(EEPROM.read(Eep_Init) != Int_End){   // Eep initialaz
    delay(10);
    eep_init();
  }
  else{
    if(digitalRead(SW_STEP) == LOW){
      delay(10);
      eep_init();
      fover_disp();
      while (digitalRead(SW_STEP) == LOW);
    }
  }
  eep_rdata();
  freq_disp(Vfo_Dat + Oft_Dat);       // frequency display
  oft_disp(Flg_Oflg);
  step_disp();
}

//----------  main  ----------------------------------------------------

void loop() {
//---------- receive ----------------------
  if(digitalRead(SW_TX) == HIGH){          
    Flg_Tx = 0;
    si5351a_enable(0x02);                  // clk0,clk2=enable clk1=disable
    si5351aSetFrequency2(RX_LO);           // RX LO set

//----------  RIT proc
    if (Flg_Rit == 0){
      si5351aSetFrequency0(Vfo_Dat - IF_FREQ + Oft_Dat);
      freq_disp(Vfo_Dat + Oft_Dat);                  // frequency display
//      oft_disp(Flg_Oflg);
    }      
    else{
      si5351aSetFrequency0(Vfo_Dat - IF_FREQ + Rit_Dat + Oft_Dat);
      rit_disp(Rit_Dat);
    }  

//---------- STEP SW check
    if (digitalRead(SW_STEP) == LOW) {    
      enc_step();
      step_disp();
      while (digitalRead(SW_STEP) == LOW);
    }

//---------- RIT SW check
    if (digitalRead(SW_RIT) == LOW) {
      if(Flg_Rit == 0)
        Flg_Rit = 1;
      else{
        Flg_Rit = 0;
        Rit_Dat = 0;
      }
      while (digitalRead(SW_RIT) == LOW);
    }

//---------- OFFSET SW check
    if (digitalRead(SW_OFT) == LOW) {
      switch(Flg_Oflg){
        case 0:
          Oft_Dat = 0;
          freq_disp(Vfo_Dat + Oft_Dat);        // frequency display
          oft_disp(Flg_Oflg);
          EEPROM.write(Eep_Oflg,Flg_Oflg);     // offset flg EEPROM save  
          Flg_Oflg++;
          eep_write4(Oft_Dat,Eep_Odat);
          break;
        case 1:
          Oft_Dat = LW_OFT;
          freq_disp(Vfo_Dat + Oft_Dat);        // frequency display
          oft_disp(Flg_Oflg);
          EEPROM.write(Eep_Oflg,Flg_Oflg);     // offset flg EEPROM save  
          Flg_Oflg++;
          eep_write4(Oft_Dat,Eep_Odat);
          break;
        case 2:
          Oft_Dat = HI_OFT;
          freq_disp(Vfo_Dat + Oft_Dat);        // frequency display
          oft_disp(Flg_Oflg);
          EEPROM.write(Eep_Oflg,Flg_Oflg);     //  offset flg EEPROM save  
          Flg_Oflg = 0;
          eep_write4(Oft_Dat,Eep_Odat);
          break;
      }
      while (digitalRead(SW_OFT) == LOW);
    }

  }

//---------- send  -------------------------
  else{                                    
    Flg_Tx = 1;
    band_check(Vfo_Dat);                    // range check
   
    if(Flg_Over == 0){                      // Within transmittable range?
      freq_disp(Vfo_Dat);                   // frequency display
      si5351a_enable(0x05);                  // si5351 output enable
      si5351aSetFrequency1(Vfo_Dat);
    }
    else{                                   // Out of range
      si5351a_enable(0x02);                  // si5351 output enable
      fover_disp();                       // Display of over range
    }
  }

//---------- common ------------------------

//---------- s meter
  if(Time_smeter+100 < millis()){
    Val_Smeter = analogRead(AD_SM);
    if ((abs(Val_Smeter - Val_Smeterb)) > 3){ // if needed draw S-meter
      sm_disp();
      Val_Smeterb = Val_Smeter;
      Time_smeter = millis();
    }
  }

//---------- freqency EEP auto write
  if(Flg_eepWT == 1){                       // EEPROM auto Write
    if(Time_Passd+2000 < millis()){
      eep_wdata();
      Flg_eepWT = 0;
    }
  }
}

//----------  RIT Display  ---------------------------------------------

void rit_disp(int rit_data) {
  unsigned int ri,rit;

  oled.setFont(&Tlcdnums14x24);
  oled.setCursor(1, 0);
  oled.print("::::");

  if (rit_data < 0)
    oled.print('-');
  else
    oled.print('+');

  rit = abs(rit_data);
   ri = rit / 1000;  
  oled.print(ri);
  oled.print('.');
  ri = (rit % 1000) / 10;
  if (ri < 10)
    oled.print('0');
  oled.print(ri);
}

//----------  Write EEPROM 4byte  --------------------------------------

void eep_write4(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--;
  }
}

//----------  Read EEPROM 4byte  ---------------------------------------

long eep_read4(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;
}

//----------  EEPROM Dat Read  -----------------------------------------

void eep_rdata(){
  Vfo_Dat = eep_read4(Eep_Freq);
  Enc_Step = eep_read4(Eep_Step);
  Flg_Oflg = EEPROM.read(Eep_Oflg);
  Oft_Dat = eep_read4(Eep_Odat);
}

//----------  EEPROM Dat Write  ----------------------------------------

void eep_wdata(){
  eep_write4(Vfo_Dat,Eep_Freq);
  eep_write4(Enc_Step,Eep_Step);
}  
   
//----------  EEPROM Initialization ------------------------------------

void eep_init(){
  int i;

  for (i=0;i<128;i++)                       // 0 clear(128byte)
    EEPROM.write(i, 0);

  eep_write4(DEF_F,Eep_Freq);
  eep_write4(STEP_1k,Eep_Step);            // Step(1kHz)
  EEPROM.write(Eep_Oflg,Flg_Oflg);         // Offset flag(0)  
  eep_write4(Oft_Dat,Eep_Odat);

  EEPROM.write(Eep_Init,Int_End);           // Init end set(73)  
}

//----------  Rotaly Encorder  -----------------------------------------

void rotary_encoder() {                     // rotary encoder events
  uint8_t result = r.process();

  if(Flg_Tx == 0){
    if (result) {
      if (result == DIR_CW){
        Lng_Wk = Vfo_Dat + Enc_Step;
        Int_Wk = Rit_Dat + Enc_Step;
      }
      else{
        Lng_Wk = Vfo_Dat - Enc_Step;
        Int_Wk = Rit_Dat - Enc_Step;
      }

      if(Flg_Rit == 1)
        Rit_Dat = Int_Wk;
      else{
        Vfo_Dat = Lng_Wk;
        Rit_Dat = 0;
      }

      Rit_Dat = constrain(Rit_Dat,LW_RIT,HI_RIT);  // RIT range check

      Flg_eepWT = 1;
      Time_Passd = millis();
    }
  }
}

//----------  Encorede STEP  -------------------------------------------

void enc_step() {
  if(Enc_Step == STEP_10k)                  // 10k?
    Enc_Step = STEP_100;                    //  yes,100Hz set
  else    
    Enc_Step = Enc_Step * 10;              
}

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

void step_disp() {
  oled.setCursor(109, 3);
  oled.setFont(&Tlabels);
  if (Enc_Step == STEP_100)
    oled.print("7");                        // 100
  else if(Enc_Step == STEP_1k){
    oled.print("8");                        // 1k
  }                                        
  else{
    oled.print("9");                        // 10k
  }                                        
}

//---------- Frequency renge over --------------------------------------

void fover_disp() {
  oled.setFont(&Tlcdnums14x24);
  oled.setCursor(1, 0);
  oled.print("--.---.--");
}

//----------  Frequency Display  ---------------------------------------

void freq_disp(uint32_t sf_rx) {
  uint16_t fr;

  oled.setFont(&Tlcdnums14x24);
  oled.setCursor(1, 0);
  fr = sf_rx / 1000000;
  if (fr < 10)
    oled.print(':');                        // ':' is changed to ' ' in lcdnums14x24.h
  oled.print(fr);
  oled.print('.');
  fr = (sf_rx % 1000000) / 1000;
  if (fr < 100)
    oled.print('0');
  if (fr < 10)
    oled.print('0');
  oled.print(fr);
  oled.print('.');
  fr = (sf_rx % 1000) / 10;
  if (fr < 10)
    oled.print('0');
  oled.print(fr);
}

//---------- TX display ------------------------------------------------

void tx_disp() {
  oled.setCursor(2, 3);
  oled.setFont(&Tlabels);
  oled.print("1");                          // TX
}

//---------- Mode display ----------------------------------------------

void oft_disp(byte mod) {
  oled.setCursor(2, 3);
  oled.setFont(&Tlabels);
  if(mod == 0)
    oled.print(".");                      // "." is "   " in Tlabels.h
  else if(mod == 1)
    oled.print("3");                      // "3" is "LSB"
  else if(mod == 2)
    oled.print("4");                      // "4" is "USB"
}

//----------  S-Meter Display  -----------------------------------------

void sm_disp() {
  uint8_t a = 0;
  uint8_t m = 0;

  Val_Smeter = map(Val_Smeter,0,225,0,1023);  
  a = Val_Smeter  / 113;               // 1023 / 9 characters for S = 1,3,5,7,8,9,+10,+20,+30
  oled.setFont(&Tpixels);
  oled.setCursor(25, 3);
  for (m = 0; m < a; m++)
    if (m < 6)
      oled.print('7');                      // '5' - hollow rectangle, 6px
    else
      oled.print('8');                      // '6' - filled rectangle, 6px
  for (m = a; m < 9; m++)
    oled.print('.');                        // '.' 1px
}

//----------  band chek  -----------------------------------------------

void band_check(unsigned long freq){
  if((freq < LOW_F) || (freq > HI_F))
    Flg_Over = 1;
  else
    Flg_Over = 0;
}

ダウンロード

回路図、スケッチなどは、JA2GQP’sDownload siteの6m AM TRXフォルダーからダウンロード可能。

  

0 件のコメント: