2020年3月15日日曜日

Attiny85 VFO

 Dijispark(Attiny85)を使ったVFO。Arduino IDE1.8.12でDijispark開発環境を作って進めたが、次の様な問題が発生。
1)Bootloaderにより起動されるので、アプリケーション立上りに約6秒かかる。
2)アプリケーションで使える領域が、約6kB。(B00tloader2kB占有のため)
3)DijisparkのP1(LED点灯回路)が、周辺ポートレベルを吊り上げる為、ポートが正常動作しない。





解決策

1)Bootloaderなしとし、ArduinoIDEで開発する為のAttiny85環境整備を行った。
2)DijisparkのP1回路に繋がっている抵抗(102)を外した。

VFO仕様

1)周波数の自動メモリー保存
2)Auto STEP(10Hz,100Hz,1kHz,10kHz)のロータリー式での切替
3)RIT機能
4)BFO
5)S-Meter
6)モノバンド
7)帯域外への送信保護
8)自動モード切替

回路図


スケッチ

スイッチ入力は、アナログ信号をラダー回路によってレベル判断を行っている。アナログ信号のPB5は、リセットを兼ねているのでしきい値を設けている。スイッチ入力の判断は、マクロにして見通しを良くした。
機械式エンコーダの場合、加速・定常域・減速が安定しておらず、かつ、連続回転の為のノブ持ち返しがある。その為、速度判定2s、ノブ持ち返し保持時間6sに設定している。(エンコーダの種類、回し方などで動作が大きく変わる)
スケッチは、ダウンロードサイトのAttiny85フォルダからダウンロードできる。

///////////////////////////////////////////////////////////////////////////////////
//    si5351a VFO Atiny85
//
//                                                        2020/3/12
//                                                        JA2GQP   
///////////////////////////////////////////////////////////////////////////////////

//---------- Library include --------------------------------

#include "src/si5351a21.h"                   
#include <Rotary.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>

//---------- Define value -----------------------------------

#define SW1 (val < 682) && ( val >= 472)  // SW1
#define SW2 (val < 767) && ( val >= 642)  // SW2
#define SW3 (val < 818) && ( val >= 727)  // SW3

#define SW_TX   SW1
#define SW_RIT  SW2
#define ENC_A   PB1
#define ENC_B   PB3
             
Rotary r=Rotary(ENC_A,ENC_B);
LiquidCrystal_I2C lcd(0x27, 16, 2);

////////////////////////////////
// EEPROM Memory Address
////////////////////////////////
const byte  Eep_Init = 0x00;        // Eep Init(1byte*1)
const byte  Eep_Band = 0x01;        // Band(1byte*1)
const byte  Eep_Freq = 0x10;        // Frequency(4byte*8)
const byte  Eep_Step = 0x30;        // STEP(4byte*8)

////////////////////////////////
// frquency data
////////////////////////////////
const unsigned long FRQ_TBL[4] = {
 // DEF     LOW(cw)  MID(ssb)  HI
  7050000 ,7000000 ,7045000 ,7200000
  };

//---- data offset -----
const byte DEF_F = 0;
const byte LOW_F = 1;
const byte MID_F = 2;
const byte HI_F  = 3;

//---- Bfo data -----
const unsigned long IF_FREQ = 10700000L; 
const unsigned long CW =      10700600L; 
const unsigned long LSB =     10701500L;
const unsigned long USB =     10698500L;

////////////////////////////////
// etc
////////////////////////////////
const byte  Int_End = 73;           // Initial end code
const int  LW_RIT = -5000;          // RIT Lower Limit
const int  HI_RIT = 5000;           // RIT Upper Limit

byte bar5[8]={B00000,               // s-meter barcode data
B11011,
B11011,
B11011,
B11011,
B11011,
B11011,
B00000};

byte bar1[8]={B10000,
B11000,
B11100,
B11110,
B11110,
B11100,
B11000,
B10000};

unsigned long previousMillis;

//---------- Variable setting -------------------------------

unsigned long Vfo_Dat;
unsigned long Vfo_Datb = 0;
unsigned long Bfo_Dat;
unsigned long Bfo_Datb = 0;
unsigned long Enc_Step = 10;

int Rit_Dat;                        // RIT Data
int Rit_Datb = 0;                   //          old
long Lng_Wk;                        // Long Work
int  Int_Wk;                        // Int Work
char Lcd_Dat[12] = "           ";   // Lcd Display Buffer
byte Flg_Tx = 0;                    // TX Flag
byte Flg_Mode = 0;                  // Mode Flag
byte Flg_Over = 0;
byte Flg_Rit = 0;                   // RIT Flag
byte Flg_Rdisp = 0;                 // RIT Display Flag
byte Flg_Disp = 0;                  // Display Flag

long Enc_Delay = 0;                 // Encorder Delay(ms)
long Time_Passd;                    // Time Pass(ms)
byte Flg_eepWT = 0;                 // EEP Write Flag

long Enc_Velocityb;
long Enc_Velocity;                  // Encorder velocity(ms)

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

void setup() {
  unsigned int val;

  pinMode(ENC_A,INPUT_PULLUP);      // PB3
  pinMode(ENC_B,INPUT_PULLUP);      // PB4

  lcd.begin();
  lcd.backlight();
  lcd.createChar(0, bar5);
  lcd.createChar(1, bar1); 

  GIMSK |= (1 << PCIE);             // Enable pin change interrupt
  PCMSK |= (1 << PCINT1) | (1 << PCINT3);
  sei();                            // INT Enable

  if(EEPROM.read(Eep_Init) != Int_End){ 
    delay(10);
    eep_init();
  }
  eep_rdata();

  band_check();
  mode_disp();
}

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

void loop() {
  unsigned int val;

  val = analogRead(A0);
  delay(1);
  if(SW_TX)                         // SW_TX
    Flg_Tx = 1;
  else
    Flg_Tx = 0;

  Fnc_TRdsp();                      // T/R Display

//////////////////////////
//  RX
//////////////////////////
  if(Flg_Tx == 0){                  // RX
    display_smeter(analogRead(A2)); // s-meter Display
    Fnc_Stp();
    si5351a_enable(0x00);
     
    val = analogRead(A0);
    delay(1);
    if(SW_RIT)                      // SW_RIT
      Fnc_Rit();                    // RIT Flag set

    if(((Vfo_Dat != Vfo_Datb) && (Flg_Rit == 0)) || (Flg_Disp == 1)){
      si5351aSetFrequency(Vfo_Dat + IF_FREQ);
      band_check();
      mode_disp();
      Fnc_Fdsp(Vfo_Dat);
      Vfo_Datb = Vfo_Dat;
      Flg_Disp = 0;
    }

    if(((Rit_Dat != Rit_Datb) && (Flg_Rit == 1)) || (Flg_Rdisp == 1)){
      si5351aSetFrequency(Vfo_Dat + IF_FREQ + Rit_Dat);
      rit_disp(Rit_Dat);
      Rit_Datb = Rit_Dat;
      Flg_Rdisp = 0;
    }

    si5351aSetFrequency2(Bfo_Dat);  // Bfo data out
  }

//////////////////////////
//  TX
//////////////////////////
  else{                           
    mode_disp();

    if(Flg_Over == 0){
      si5351a_enable(0x00);
      si5351aSetFrequency(Vfo_Dat + IF_FREQ);
    }
    else{                           // Frequency over
      si5351a_enable(0x07);
    }

    if(Flg_Rit == 1)
      Flg_Rdisp = 1;
    else
      Flg_Disp = 1;
  }

//////////////////////////
//  Save to EEPROM
//////////////////////////
  if(Flg_eepWT == 1){               // EEPROM auto Write
    if(Time_Passd+2000 < millis()){
      eep_wdata();
      Flg_eepWT = 0;
    }
  }
}

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

void Fnc_Rit(){
  unsigned int val;

  if(Flg_Rit == 0){
    Rit_Dat = 0;
    Flg_Rit = 1;
    lcd.setCursor(11,0);
    lcd.print("+0   ");
  }
  else{
    mode_disp();
    si5351aSetFrequency(Vfo_Dat + IF_FREQ);
    Flg_Rit = 0;
  }

  do{
    val = analogRead(A0);
    delay(1);
  }
  while(SW_RIT);
}

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

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

  lcd.setCursor(11,0);

  if (rit_data >= 0){
    lcd.print("+    ");
    lcd.setCursor(12,0);
    lcd.print(rit_data,DEC);
  }
  else{
    lcd.print("     ");
    lcd.setCursor(11,0);
    lcd.print(rit_data,DEC);
  }
}

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

void mode_disp() {
  lcd.setCursor(11,0);
  if((Flg_Tx == 1) && (Flg_Over == 1))
    lcd.print(" Over");             // frequency over
  else if (Flg_Mode == 0)
    lcd.print("   CW");             // CW
  else if(Flg_Mode == 1)
    lcd.print("  LSB");             // LSB
  else if(Flg_Mode == 2)
    lcd.print("  USB");             // USB
}

//----------  LCD Bar Display PROC.  ------------------------

void display_smeter(int strength){
// range is 0 to 1024 of adc
//meter run from position 3 to 14 (12 )
  int scale = (strength*15)/1024;
  lcd.setCursor(0, 1);
  lcd.print("S");
  int smeter=(scale*11)/15;
  if(smeter<10)
    lcd.print(smeter);
  else
    lcd.print("9");
  lcd.print(">");

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= 1000) {
    previousMillis = currentMillis;
    //clear all after a small interval
    lcd.setCursor(3, 1);
    lcd.print("            ");
    lcd.noCursor();
  }

  // write it and show for 500 milli sec
  for (int i = 3; i<scale; i++){
    lcd.setCursor(i, 1);
    lcd.write(byte(0));
    if(smeter>9)
      lcd.print("+");
  }
}


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

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

  if(Flg_Tx == 0){
    if(result) { 
      Enc_Velocity = millis() - Enc_Velocityb;

      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 = Enc_Velocityb =millis();
    }
  }
}

//----------  Function Send/receive Display  ------

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

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

void Fnc_Fdsp(long f_disp){
  Fnc_Dot_Edit(Lcd_Dat,f_disp);
  lcd.setCursor(1,0);
  lcd.print("          ");
  lcd.setCursor(1,0);
  lcd.print(Lcd_Dat);
}

//----------  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;
    }
}

//----------  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);
}

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

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

void eep_init(){
  int i;

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

  eep_write4(FRQ_TBL[DEF_F],Eep_Freq);
  EEPROM.write(Eep_Init,Int_End);   // Init end set(73)
}

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

void Fnc_Stp(){
  if(Time_Passd+2000 < millis()){
    Enc_Step = 10;
  }

  else{
    if(Enc_Delay+6000 < millis()){
      if(Enc_Velocity < 100)        // chenge STEP?
        Enc_Step = Enc_Step * 10;
      if(Enc_Step > 10000)          // 10kHz over?
          Enc_Step = 10;            // STEP = 10Hz
      Enc_Delay = millis();
    }
  }

}

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

void band_check(){
  if((Vfo_Dat >= FRQ_TBL[LOW_F]) && (Vfo_Dat <= FRQ_TBL[MID_F])){
    Flg_Mode = 0;                   // CW(0=CW, 1=LSB, 2=USB) 
    Bfo_Dat = CW;
    Flg_Over = 0;
  }

  else if((Vfo_Dat >= FRQ_TBL[MID_F]) && (Vfo_Dat <= FRQ_TBL[HI_F])){
    if(Vfo_Dat >= 10000000){        // greate than 10MHz                     
      Flg_Mode = 2;                 // USB(0=CW, 1=LSB, 2=USB)
      Bfo_Dat = USB;
    }
    else{
      Flg_Mode = 1;                 // LSB(0=CW, 1=LSB, 2=USB)
      Bfo_Dat = LSB;
    }
    Flg_Over = 0;
  }

  else{
    Flg_Over = 1;
    if(Vfo_Dat >= 10000000){        // greate than 10MHz                     
      Flg_Mode = 2;                 // USB(0=CW, 1=LSB, 2=USB)
      Bfo_Dat = USB;
    }
    else{
      Flg_Mode = 1;                 // LSB(0=CW, 1=LSB, 2=USB)
      Bfo_Dat = LSB;
    }
  }
}



12 件のコメント:

  1. 3/28大須懇親会は中止です。

    返信削除
  2. what rotary library you are using in this sketch. i was trying in your download folder it doesnt work.

    thank you very much for your help

    73s de yb1ahy Agus

    返信削除
  3. Hi yb1ahy.
    Please use st7032_vfo.zip in the attiny85 folder of the download site. You have all the files you need.

    返信削除
  4. JA2GQP san.

    i am sorry, i couldnot get your name in your blog

    I am almost done. All library i put in arduino\library outside that they cannot be compiled.

    one other thing is EEPROM.h, would you mind to indicate which library i can use.

    Thank you and appreciate for your help.


    73s de YB1AHY. Agus.

    返信削除
  5. To yb1ahy.

    JA2GQP's Download site
    https://sites.google.com/site/ja2gqp/

    EEPROM.h is a library installed in Arduino IDE.

    返信削除
  6. Hi JA2GQP

    Thank you and I fix the EEPROM.h

    But i got this:

    Arduino: 1.8.12 (Windows 7), Board: "Digispark (Default - 16.5mhz)"

    text section exceeds available space in board
    Sketch uses 7788 bytes (129%) of program storage space. Maximum is 6012 bytes.
    Global variables use 266 bytes of dynamic memory.
    Sketch too big; see http://www.arduino.cc/en/Guide/Troubleshooting#size for tips on reducing it.
    Error compiling for board Digispark (Default - 16.5mhz).

    This report would have more information with
    "Show verbose output during compilation"
    option enabled in File -> Preferences.

    any advis? Thank you for your time

    73s de YB1AHY


    返信削除
  7. To YB1AHY.
    what.
    It says that Digi Spark cannot be used. Please read the blog carefully. You can see that it is used as Attin85.

    返信削除
  8. Sorry i missed your written. i ll try..thank you very much.

    返信削除
  9. Hatzimemasite JA2GQP san.
    Nice project. Would a SSD1306 OLED fit in Attiny85 memory space, or am I asking the impossible.
    73 de Konstantinos, SV1ONW

    返信削除
  10. I haven't checked the combination of Attiny85 and oled, but I think the memory capacity is insufficient.

    返信削除
  11. Mosi mosi.
    Domo arigato, for your very prompt response.
    I also thought so.
    But if I find some time, I may give it a try.
    I like your functionality and your coding a lot.
    Konstantinos.

    返信削除