2023年9月26日火曜日

oled_1614VFO

ATtiny1614機能チェックを目的に、oled mono band VFOを作った。デジタル入力、アナログ入力、I2C、割込み、EEPROM と一通りのチップ機能が確認できた。VFOとしても満足できる仕様だと自負してる。


回路図

ブレッドボードでは、試験に必要な最小限になっている。
 












スケッチ

///////////////////////////////////////////////////////////////////////
//  si5351a oled(128x32) ATtiny1614 VFO program ver.1.0
//    Copyright(C)2023.JA2GQP.All rights reserved.
//
//                                                2023/9/24
//                                                  JA2GQP
//--------------------------------------------------------------------
//  Function
//    1.STEP(10k,1k,100,10)
//    2.EEPROM memorry save/reload
//    3.Protection Operation At The Time Of Transmission
//    4.S-Meter
//    5.mono band
//////////////////////////////////////////////////////////////////////

#include "src/Rotary.h"              
#include "src/si5351a21.h"
#include <Tiny4kOLED.h>
#include "font/Tlcdnums14x24.h"
#include "font/Tlabels.h"
#include "font/Tpixels.h"
#include <EEPROM.h>

////////////////////////////////
// Set Device
////////////////////////////////
Rotary r = Rotary(2, 5);

////////////////////////////////
// EEPROM Memory Address
////////////////////////////////
const byte  Eep_Init = 0x00;        // Eep Init(1byte*1)
const byte  Eep_Band = 0x01;        // Band(1byte*1)
const byte  Eep_Mode = 0x02;        // Mode(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[3] = {
 // DEF     LOW       HI
  7050000 ,7000000 ,7200000
  };

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

////////////////////////////////
// I/O Port
////////////////////////////////
const byte SW_RIT  = 4;               // RIT SW
const byte SW_STEP = 1;               // STEP SW
const byte SW_MODE = 3;               // MODE SW
const byte SW_TX = 0;                 // TX/RX
const byte AD_SM = 10;                // S-meter AD

//---- IF Frequency & Bfo data -----
const unsigned long IF_FREQ = 10700000L;  

const unsigned long BFO_TBL[3] = {
//   CW       LSB      USB
  10700600,10701500,10698500
  };
const byte CW  = 0;                   // CW
const byte LSB = 1;                   // LSB
const byte USB = 2;                   // USB


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

////////////////////////////////
// Memory Assign
////////////////////////////////
unsigned long Vfo_Dat;                // VFO Data
unsigned long Enc_Step;               // STEP
int Rit_Dat;                          // RIT Data
char Enc_Dir = 0;                     // -1 DIR_CCW, 0 DIR_NONE, 1 DIR_CCW
unsigned int Val_Smeter = 0;
unsigned int Val_Smeterb = 1;

unsigned int Time_Passd;              
byte Flg_eepWT = 0;                   // EEP Write Flag

byte Byt_Mode = LSB;
byte Flg_Over;
byte Flg_Tx = 0;
byte Flg_Rit = 0;                     // RIT Flag

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

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

  attachInterrupt(2,rotary_encoder,CHANGE);
  attachInterrupt(5,rotary_encoder,CHANGE);

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

  if(EEPROM.read(Eep_Init) != Int_End){    
    delay(10);
    eep_init();
  }
  else{
    if(digitalRead(SW_STEP) == LOW){
      delay(10);
      eep_init();
      fover_disp();
      while (digitalRead(SW_STEP) == LOW);
    }
  }
  eep_rdata();
  step_disp(Enc_Step);
}

//----------  main(Loop)  -----------------------------------------------

void loop() {

  //---------- receive
  if(digitalRead(SW_TX) == HIGH){        
    Flg_Tx = 0;
    si5351a_enable(0x00);                  // si5351 output enable

    //----------  RIT proc
    if(Flg_Rit == 1){
      Rit_Dat += Enc_Dir * Enc_Step;
      Enc_Dir = 0;
      Rit_Dat = constrain(Rit_Dat,LW_RIT,HI_RIT);
      si5351aSetFrequency(Vfo_Dat + IF_FREQ + Rit_Dat);
      rit_disp(Rit_Dat);
    }

    //---------- normal proc
    else{
      Vfo_Dat += Enc_Dir * Enc_Step;
      Enc_Dir = 0;
      band_check();                        // range check
      si5351aSetFrequency(Vfo_Dat + IF_FREQ);
      freq_disp(Vfo_Dat);                  // frequency display
    }

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

    //---------- MODE SW check
    if (digitalRead(SW_MODE) == LOW) {    
      mode_set();
      mode_disp(Flg_Tx,Byt_Mode);
      while (digitalRead(SW_MODE) == 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);
    }

  }

  //---------- trancemit
  else{
    Flg_Tx = 1;
    if(Flg_Over == 1){                      // frequency limit over?
      fover_disp();
      si5351a_enable(0x05);                 // VFO and BFO off
    }                                    
    else
      freq_disp(Vfo_Dat);                   // frequency display
      si5351aSetFrequency(Vfo_Dat + IF_FREQ);
  }

  //---------- common
  mode_disp(Flg_Tx,Byt_Mode);
  si5351aSetFrequency2(BFO_TBL[Byt_Mode]);  // Bfo data out  

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

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

//----------  mode set  -----------------------------------------------

void mode_set() {
  if (Byt_Mode == 2)
    Byt_Mode = 0;                        
  else
    Byt_Mode++;
}

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

void band_check(){
  if((Vfo_Dat >= FRQ_TBL[LOW_F])
      && (Vfo_Dat <= FRQ_TBL[HI_F]))
    Flg_Over = 0;
  else
    Flg_Over = 1;
}

//----------  EEPROM Initialization ------------------------------------

void eep_init(){
  int i;

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

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

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

void eep_rdata(){
  Vfo_Dat = eep_read4(Eep_Freq);
  Enc_Step = eep_read4(Eep_Step);
  Byt_Mode = EEPROM.read(Eep_Mode);
}

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

void eep_wdata(){
  eep_write4(Vfo_Dat,Eep_Freq);
  eep_write4(Enc_Step,Eep_Step);
  EEPROM.write(Eep_Mode,Byt_Mode);       // mode LSB
}  
   
//----------  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;
}

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

void enc_step() {
  if (Enc_Step == 10000)
    Enc_Step = 10;                        // 1000 Hz, round to XX.XXX.000
  else
    Enc_Step = Enc_Step * 10;
}

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

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

  if(Flg_Tx == 0){
    if (result) {
      if (result == DIR_CW)
        Enc_Dir = 1;
      else
        Enc_Dir = -1;
    }
  }
  Flg_eepWT = 1;
  Time_Passd = millis();
}

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

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

  a = (Val_Smeter + 3) / 113;  // 1024 / 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
}

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

void step_disp(unsigned long stp) {
  oled.setCursor(109, 3);
  oled.setFont(&Tlabels);
  if (stp == 10)
    oled.print("5");                        // 10
  else if(stp == 100)
    oled.print("7");                        // 100
  else if(stp == 1000)
    oled.print("8");                        // 1k
  else
    oled.print("9");                        // 10k
}

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

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

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

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

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

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


Download

必要なファイルは、Download siteのATtiny1614フォルダからダウンロード可能。

ATtiny1614

コストパーフォマンスの良いマイコンを探していた所、モノタロウで999円/10個販売のATtiny1614が目に留まった。このチップを調べたら、下記の通りである。

・内蔵RC発振が最大20MHz

全てのピンで割込み可能

UPDI方式のプログラマ


仕様

・USARTx1, TWI(I2C)x1, SPIx1
・Timer 16bitx3, 12bit x 1
・16KB FLASH
・2KB SRAM
・256B EEPROM
・SOICパッケージ(0.65mmピッチ) - 14ピン



UPDIプログラマ
このチップは、今迄使っていたICSP(MOSI、MISO、、SCK)による書込みができない。また、チップに対応したUPDIプログラマは多種あるが、USBー232コンバータ方式が簡単である。手持ちにFT232RLコンバータがあるので、これを使ってUPDIプログラマを作る事にした。

回路図は左図の通り。USB-FT232RLコンバータは、Vccを3.3V/5Vをジャンパーピンで選択できる便利。


470Ωと1ss108(ショットキー)をコネクタ部で接続する方式とした。(FT232RLコンバータは、一切改造してない)FT232RLコンバータ以外の場合、ダイオードを省くことが出来るチップもある様だが、ダイオードは入れた方が無難と思う。







Arduino IDE設定
ライブラリ追加

ファイルの基本設定を開き、追加ボードマネージャのURLに次の行を追加する。
http://drazzy.com/package_drazzy.com_index.json











megatinycoreインストール

ボードマネージャを開き、megatinycoreを検索してインストールする。














チップ選択

ツールからボードを選んで、megaTinyCoreからチップを選択する。










ATtiny1614詳細設定


























ATtiny1614への書込み

スケッチの書き込み装置を使って書き込むを選択して書込む。















スケッチでPORTの指定方法

例えば、pin2のPA4は、0。同様にpin3のPA5は1 と指定する。この図では、割込みピンがピン4とピン7のみと思うかもしれないが、取説によると全ピン割込み可能。








2023年9月14日木曜日

DC Power supply

JA2NKD/1松浦OMがTwitterで安価なDC電源コントロールkitを紹介していた。Aliexpressで検索したら500円位で販売していたので、購入した。DC0-30V 3Aで電流制限可能との事であったが、トランスの関係で1A程の仕様とした。Aliexpressから届いた部品を確認したら、怪しそうなIC(刻印なし3個)が含まれていた。このkitには取説が無いので、webから入手した。部品実装したkitを使ってない電源に組み込んだ。簡単な試験をした所、正常動作しているようであった。







回路図



    









取説
日本語と英語の取説をDownload siteのDC_Powerフォルダーからダウンロード可能。