2019年6月18日火曜日

SR-FRS FMトランシーバ

SR-FRS1WVを使った2m FMトランシーバ(2m FM 1W出力)。写真のシールドされた部分が、SR-FRSモジュールである。このモジュールは、中華の数社から販売されており、DRA818、SA818、SR-FRSなどのキーワードで探すことが出来る。SR-FRSを除き殆ど、12.5kHzまたは25kHzステップとなっており、国内では使いづらい。SR-FRSは、20kHzステップとして動作させられるので、音声FMに適している。




BK4811

SR-FRSの回路である。BK4811(DSP)とRF AMP,マイコンから構成されている。このBK4811は、70cmも対応しているが残念ながら、DSPにコマンドを与えない限り、70cmは動作しない様だ。










トランシーバ回路

周波数設定にロータリーエンコーダを使い、oled表示器を備えた回路図である。注意しなければいけない事は、SR-FRSの定格電圧が低いことである。電源電圧を4.5V以下で使うのが良いであろう。











PCB設計

PCBEを使ってPCB設計し、KicadのViewerでチェックを行った。マイク入力とRF出力が近接している為、RF回り込みを心配したが、全く問題なく動作した。









ハンディタイプ


ハンディタイプ トランシーバとして纏めた例である。リチュームイオン電池(3.7V)と充電器を内蔵。人間工学からメーカ製とは考え方の異なる配置とした。

















スケッチ

バンドプランによる運用周波数制限、スケルチoff、自動メモリー書込みの機能がある。
JA2GQP's Download siteのSR-FRSフォルダからダウンロード可能。Ver1.00での既知のバグは、1kHz台の表示が誤表示する事がある。誤表示は、電源offまたは運用周波数範囲外チェック後の希望周波数への移動などにより回避できる。この下1桁誤表示は、Ver1.01で確認試験を行っていたが、誤表示する事は無かった。下記の黄色部分は、Downloadサイトに反映されてアップロード済み。

//////////////////////////////////////////////////////////////////////
//  SR-FRS1W VHF TRX program ver.1.01
//    Copyright(C)2019.JA2GQP.All rights reserved.
//
//                                                2019/06/18
//                                                  JA2GQP
//--------------------------------------------------------------------
//  Function
//    1.encoeder STEP 20kHz
//    2.Squelch on/off(SQ SW)
//    3.Auto save memory(frequency)
//    4.Voltmeter
//////////////////////////////////////////////////////////////////////

#include "src/Rotary.h"               // http://www.buxtronix.net/2011/10/rotary-encoders-done-properly.html
#include "src/SSD1306AsciiAvrI2c.h"   // https://github.com/greiman/SSD1306Ascii
#include <EEPROM.h>

////////////////////////////////
// Set Device
////////////////////////////////
Rotary r = Rotary(2, 3);
SSD1306AsciiAvrI2c oled;

////////////////////////////////
// I/O Port
////////////////////////////////
const byte SW_SQ = 4;                 // SQ SW

////////////////////////////////
// EEPROM Memory Address
////////////////////////////////
const byte  Eep_Init = 0x00;          // Eep Init(1byte*1)
const byte  Eep_Freq = 0x10;          // Frequency(4byte*8)
const byte  Int_End = 73;             // Initial end code

////////////////////////////////
// Constant Memory
////////////////////////////////
const char Call[9] = "JA2GQP";        // Display Call sign
const unsigned long DEF_FREQ =145000; // Default
const unsigned long BAND_L =144600;   // Low limit
const unsigned long BAND_H =145800;   // High limit

////////////////////////////////
// Variable Memory
////////////////////////////////
unsigned long frq;                    // Frequency data
unsigned long frqb;                   //           old data
const unsigned int STEP = 20;         // STEP 20kHz

String GBW = "0";                     // Bandwidth(0:Nallow 1:Wide)
unsigned long  TFV;                   // Transmit frequency
unsigned long  RFV;                   // Receive frequency
String RXCXCSS = "00";                // CICSS/CDCSS(00-121)
String SQ = "1";                      // Squelch level(0:Monitor mode)
String TXCXCSS = "00";                // CICSS/CDCSS(00-121)
String FLAG = "0";                    // Bit0(Busy),Bit1(Compression),Bit2(Power)

float v;                              // volt data
float vb = 0;                         //           old
byte Flg_freq = 0;
byte Flg_sq = 0;
byte Flg_eepWT = 0;                   // EEP Write Flag
long Time_Passd = 0;                  // int to hold the arduino miilis since startup

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

void setup(){
  pinMode(SW_SQ,INPUT_PULLUP);

  attachInterrupt(0,rotary_encoder,CHANGE);
  attachInterrupt(1,rotary_encoder,CHANGE);

  analogReference(INTERNAL);          // Internal 1.1V set
  oled.begin(&Adafruit128x64,0x3C);

  Serial.begin(9600);
  if(EEPROM.read(Eep_Init) != Int_End){    // Eep initialaz
    delay(10);
    eep_init();
  }

  eep_rdata();
  frs_init();
  frs_set();

  volt_disp();
  freq_disp(frq);
  call_disp();
}

//---------- loop -------------------------------------------------------

void loop(){
  if(frq != frqb){
    frq = constrain(frq,BAND_L,BAND_H);
    frs_set();
    freq_disp(frq);
    frqb = frq;
    volt_disp();

    Time_Passd = millis();
    Flg_eepWT = 1;
  }

  if (digitalRead(SW_SQ) == LOW) {
    sq_set();
    frs_set();
  }

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

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

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

  if (result) {
    Flg_freq = 1;
    if (result == DIR_CW)
      frq = frq + STEP;
    else
      frq = frq - STEP;
  }
}

//----------  Band set  ------------------------------------------------

void sq_set(){
  if(Flg_sq == 0){
    SQ = "0";
    Flg_sq = 1;
  }
  else{
    SQ = "1";
    Flg_sq = 0;
  }
  while(digitalRead(SW_SQ) == LOW);
}

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

void eep_init(){
  int i;

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

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

//----------  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(){
  frq = eep_read4(Eep_Freq);
}

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

void eep_wdata(){
  eep_write4(frq,Eep_Freq);
}

//---------- callsign display ------------------------------------------

void call_disp() {
  oled.setFont(Arial_bold_14);
  oled.setCursor(56, 6);
  oled.print(Call);
}

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

void freq_disp(uint32_t sf_rx) {
  unsigned int wk;

  oled.setFont(lcdnums14x24);
  oled.setCursor(16, 2);
  wk = sf_rx / 1000;
  oled.print(wk);
  oled.print('.');
  wk = sf_rx % 1000;
  if (wk < 100)
    oled.print('0');
  if (wk < 10)
    oled.print('0');
  oled.print(wk/10*10);
}

//---------- SR-FRS Initialize ----------------------------------------

void frs_init(){
  Serial.println("AT+DMOCONNECT");    // Se connecter au module
  Serial.println("");
  Serial.println("");
  delay (500);
}

//---------- SR-FRS data set ------------------------------------------

void frs_set(){
  TFV = RFV = frq;

  Serial.print("AT+DMOSETGROUP=");    // Commencer un message
  Serial.print(GBW);
  Serial.print(',');
  Serial.print((float)TFV / 1000, 4);
  Serial.print(',');
  Serial.print((float)RFV / 1000, 4);
  Serial.print(',');
  Serial.print(RXCXCSS);
  Serial.print(',');
  Serial.print(SQ);
  Serial.print(',');
  Serial.print(TXCXCSS);
  Serial.print(',');
  Serial.println(FLAG);
  delay(20);
}

//---------- Volt Meter -----------------------------------------------

void volt_disp(){
  v = analogRead(A1) * 1.1;
  v = v / 1023;
  v = v / 2200;
  v = v * 12200;

  if(vb != v){
    oled.setFont(Arial_bold_14);
    oled.setCursor(1, 0);
    oled.print("        ");
    oled.setCursor(1, 0);
    oled.print(v,2);
    oled.print('V');
    vb = v;
  }
}