Categories
Statistics
Flag Counter
Since 08.08.2014
Counts only, if "DNT = disabled".

Your IP is 44.213.60.33
ec2-44-213-60-33.compute-1.a
Info
Valid HTML 4.01 Transitional Creative Commons Lizenzvertrag
rss
เราจะทำแบบวิศวกรผู้ยิ่งใหญ่
We love the King
23. July 2024
Your valuable opinion :
4 stars

Avg. 4.00 from 10 votes.



Arduino-Countermod.php    27583 Bytes    04-06-2024 19:40:34


Arduino/Genuino "Countermod"


A UHF Frequency Counter up to 1 GHz






Sorry, no fancy display when nothing connected :-)




✈ Motivation




In our experiments, a lot of RF-Carriers are mixed up and down. Sometimes it is necessary to monitor an intermediate Frequency to keep track of the lock status of one of our lasers. This can now be done easily with this nifty little counter. And of course, it can be locked to the internal available 10 MHz GPS Reference ...




✈ The Design





The Block Diagram of the "Countermod"


The Frequency at the input is clamped to a maximum value of +10 dBm by a BAV99 Diode. This shall enhance the chances to survive, in case it is connected to an AOM-Controller.

The device can be equipped with a lot of different XCOs. In case a more precise external Reference is available, the AD8307 measures it's amplitude. This value is available at an analog input pin to the Arduino. It then can switch to the external 10 MHz.
The used Reference is then divided down to 100 Hz, 10 Hz, 1 Hz and 0.1 Hz. All available settings for the Gate-time can be seen in the following table :

#S0GATE-TIMEREMARKS
00400 ms...
114 s...


A 1 PPS signal is made available at a rear BNC output.

If the frequency is extremely low or high, an internal prescaler (MC12080) may be changed from ten to eighty.

The Arduino has knowledge of the Gate to be open. In case it is open (= counting) it waits and handles interrupt stuff. If the Gate is closed, the Arduino reads the value, resets the counter und updates the display. The Gate works independant of the Arduino. It can only decide, which one to use.

On the fontpanel, the supply-voltage is available to power prescalers, preamplifiers e.a.



Assembled PCB, this one is from JLCPCB (Shenzhen)




✈ Downloads








✈ Test Sketch for Arduino/Genuino Nano Every



Double click on code to select ...

/* //////////////////////////////////////////////////////////////////

  ARDUINO/Genuino Project "COUNTERMOD", a 3 GHz Frequency Counter
  https://www.changpuak.ch/electronics/Arduino-Countermod.php
  Software Version 1.0
  03.07.2020 by ALEXANDER SSE FRANK

  NOTE: FOR NANO EVERY, NEEDED TO UNCOMMENT IN Adafruit_SH1106.cpp
  LINES 549 ...
  // save I2C bitrate 
  #ifndef __SAM3X8E__
        uint8_t twbrbackup = TWBR;
        TWBR = 12; // upgrade to 400KHz!
  #endif
  
  NOTE: FOR NANO EVERY, NEEDED TO UNCOMMENT IN Adafruit_SH1106.cpp
  LINES 574 ...
  #ifndef __SAM3X8E__
        TWBR = twbrbackup;
  #endif

  HELPFUL :
  https://learn.adafruit.com/adafruit-gfx-graphics-library/
    graphics-primitives
  
////////////////////////////////////////////////////////////////// */

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH1106.h>



// DISPLAY

#define OLED_MOSI  5
#define OLED_CLK   4
#define OLED_DC    7
#define OLED_CS    8
#define OLED_RESET 6


// ROTARY ENCODER
const int RotaryEncoder1 = A7 ;   // PRESSED
const int RotaryEncoder2 = 2 ;
const int RotaryEncoder3 = 3 ;
volatile boolean LEFT = false ;
volatile boolean RIGHT = false ;
volatile boolean PRESS = false ;

// SERIAL COMMUNICATION
byte B[20] ;                 // holds User Input from Serial
int pointer = 0 ;

Adafruit_SH1106 display(OLED_MOSI,OLED_CLK,OLED_DC,OLED_RESET,OLED_CS);

#if (SH1106_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SH1106.h!");
#endif

unsigned long F_TRUE = 0 ;
unsigned long F_RAW = 0 ;

int SR_CLEAR = A0 ;
int Cursor = 2 ;  // 0 = invisible
int CursorDelay = 399 ;
unsigned long NextCursorAction = 0 ; 

int StatusGate = 0 ;
int StatusGateOld = 0 ;
int StatusToken = 0 ;

boolean GateOpen = false ;
boolean GateOpenOld = true ;


// /////////////////////////////////////////////////////////////////////
// INTERNAL OR EXTERNAL 10 MHz SOURCE
// /////////////////////////////////////////////////////////////////////

#define RSSI_Pin            A6
#define TimeBaseSelectPin   9
int ReferenceSource = 0 ;   // INTERN
const int ExtON = 345 ;
const int ExtOFF = 234 ;
int RSSI = 0 ;

void CheckReferenceSource()
{
  RSSI = analogRead(RSSI_Pin) ;
  if(RSSI > ExtON) 
  {
    digitalWrite(TimeBaseSelectPin, HIGH) ;
    ReferenceSource = 1 ; // EXTERN
  }
  if(RSSI < ExtOFF) 
  {
    digitalWrite(TimeBaseSelectPin, LOW) ;
    ReferenceSource = 0 ; // INTERN
  }  
}


// /////////////////////////////////////////////////////////////////////
// PRESCALER AND GATE TIME ROUTINES
// /////////////////////////////////////////////////////////////////////

#define GATE 13 

#define PrescalerPin12    A2
#define PrescalerPin3     A3
int PrescalerValue = 0 ;
#define RangePin          A1
int Range = 1 ;   // 80 MHz ... 3 GHz


void SetRange() 
{
  if(Range == 0) 
  {
    digitalWrite(RangePin, LOW) ;
    PrescalerValue = 1 ;
  }
  if(Range == 1) 
  {
    digitalWrite(RangePin, HIGH) ;
    digitalWrite(PrescalerPin12, HIGH) ;
    digitalWrite(PrescalerPin3, HIGH) ;
    PrescalerValue = 10 ;
  }
  if(Range == 2) 
  {
    digitalWrite(RangePin, HIGH) ;
    digitalWrite(PrescalerPin12, HIGH) ;
    digitalWrite(PrescalerPin3, LOW) ;
    PrescalerValue = 20 ;
  }
  if(Range == 3) 
  {
    digitalWrite(RangePin, HIGH) ;
    digitalWrite(PrescalerPin12, LOW) ;
    digitalWrite(PrescalerPin3, HIGH) ;
    PrescalerValue = 40 ;
  }
  if(Range == 4) 
  {
    digitalWrite(RangePin, HIGH) ;
    digitalWrite(PrescalerPin12, LOW) ;
    digitalWrite(PrescalerPin3, LOW) ;
    PrescalerValue = 80 ;
  }
}


int GateTime = 0 ;    // 400 ms OPEN
#define GATE_SEL_0  10  // THIS IS S0


void SetGateTime(int val) 
{
  // X0, 400 ms
  if(val == 0) 
  {
    digitalWrite(GATE_SEL_0, HIGH) ;
  }
  // X1, 4000 ms
  if(val == 1) 
  {
    digitalWrite(GATE_SEL_0, LOW) ;
  }
  GateTime = val ;  
}



// /////////////////////////////////////////////////////////////////////
// Serial Communication Routines
// /////////////////////////////////////////////////////////////////////


void ShowInputBuffer()
{
  // FOR DEBUG REASON ONLY :-)
  for (int i = 0; i < 10; i++)
  {
    Serial.print("B[") ;
    Serial.print(i, DEC) ;
    Serial.print("] = ") ;
    Serial.println(B[i]) ;
  }
}

void FlushInputBuffer()
{
  while (Serial.available())
  {
    B[19] = Serial.read() ;
  }
  for (int i = 0; i < 20; i++) B[i] = 32 ;
}


void CheckForSerialInput()
{
  if (Serial.available())
  {
    B[pointer] = Serial.read() ;
    pointer += 1 ;
    if (pointer > 19) pointer = 0 ; // EMERGENCY BREAK
  }
}


void EvaluateSerialInput()
{
  // *IDN?
  if ((B[0]==42)&&(B[1]==73)&&(B[2]==68)&&(B[3]==78)&&(B[4]==63))
  {
    Serial.println("Countermod V1.9 by Changpuak.ch (C) 07/2020") ;
    FlushInputBuffer() ;
    pointer = 0 ;
  }
  // F? Ask for Frequency
  if((B[0]==70)&&(B[1]==63))
  {
    Serial.print(F_TRUE, DEC) ;
    Serial.println(" Hz") ;
    FlushInputBuffer() ;
    pointer = 0 ;
  }
  // G:0  Gate Time 0 = 400 ms
  if((B[0]==71)&&(B[1]==58)&&(B[2]==48))
  {
    GateTime = 0 ;
    SetGateTime(GateTime) ;
    Serial.println("O.K.") ;
    FlushInputBuffer() ;
    pointer = 0 ;
  }
  // G:1  Gate Time 1 = 4000 ms
  if((B[0]==71)&&(B[1]==58)&&(B[2]==49))
  {
    GateTime = 1 ;
    SetGateTime(GateTime) ;
    Serial.println("O.K.") ;
    FlushInputBuffer() ;
    pointer = 0 ;
  }
  // R:0  Set Range to LOW, 
  if((B[0]==82)&&(B[1]==58)&&(B[2]==48))
  {
    Range = 0 ;
    SetRange() ;
    Serial.println("O.K.") ;
    FlushInputBuffer() ;
    pointer = 0 ;
  }
  // R:1  Set Range to HIGH, Divider = 10
  if((B[0]==82)&&(B[1]==58)&&(B[2]==49))
  {
    Range = 1 ;
    SetRange() ;
    Serial.println("O.K.") ;
    FlushInputBuffer() ;
    pointer = 0 ;
  }
  // R:2  Set Range to HIGH, Divider = 20
  if((B[0]==82)&&(B[1]==58)&&(B[2]==50))
  {
    Range = 2 ;
    SetRange() ;
    Serial.println("O.K.") ;
    FlushInputBuffer() ;
    pointer = 0 ;
  }
  // R:3  Set Range to HIGH, Divider = 40
  if((B[0]==82)&&(B[1]==58)&&(B[2]==51))
  {
    Range = 3 ;
    SetRange() ;
    Serial.println("O.K.") ;
    FlushInputBuffer() ;
    pointer = 0 ;
  }
  // R:4  Set Range to HIGH, Divider = 80
  if((B[0]==82)&&(B[1]==58)&&(B[2]==52))
  {
    Range = 4 ;
    SetRange() ;
    Serial.println("O.K.") ;
    FlushInputBuffer() ;
    pointer = 0 ;
  }
  // REF? Ask for Reference Source
  if((B[0]==82)&&(B[1]==69)&&(B[2]==70)&&(B[3]==63))
  {
    if(ReferenceSource == 1) Serial.println("EXTERNAL") ;
    else Serial.println("INTERNAL") ;
    FlushInputBuffer() ;
    pointer = 0 ;
  }
  else
  {
  // THROW AWAY GARBAGE FROM SERIAL INPUT (FIRST CHAR ONLY :-(
  //         *           F           G           R           NIL
  if((B[0]!=42)&&(B[0]!=70)&&(B[0]!=71)&&(B[0]!=82)&&(B[0]!=32)) 
    {
    Serial.println("SYNTAX ERROR. UNKNOWN COMMAND.") ;
    ShowInputBuffer() ;
    FlushInputBuffer() ;
    pointer = 0 ;
    }
  }
}


// /////////////////////////////////////////////////////////////////////
// SUBROUTINES DISPLAY.
// /////////////////////////////////////////////////////////////////////

void UpDateDisplay()
{
  int offset = 0 ;
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0); display.print("****");
  display.setCursor(33,0); display.print("COUNTERMOD");
  display.setCursor(104,0); display.print("****");
  display.drawLine(0, 12, 128, 12, WHITE);
  display.setTextSize(2) ;
 
  // FREQUENCY
  String FString = String(F_TRUE, DEC) ;
  int LFString = 10 - FString.length() ;
  
  for (int i=LFString; i<10; i++)
  {
    if (i>=0) offset = 0 ;
    if (i>=1) offset = 5 ;
    if (i>=4) offset = 10 ;
    if (i>=7) offset = 15 ;
    display.setTextColor(WHITE) ;
    display.setTextSize(2) ;
    display.setCursor(i*11+offset,19) ;
    display.print(FString[i-LFString]) ;
   }

  display.drawLine(0, 40, 128, 40, WHITE);

  // STATUS OVEN
  display.setTextSize(0);
  display.setCursor(0, 45) ;
  if(ReferenceSource == 1) display.print("EXT") ;
  else display.print("INT") ;
  // STATUS GATE
  // if(digitalRead(GATE) == 1) display.fillCircle(40, 48, 3, 1);
  if(digitalRead(GATE) == 1) display.fillRoundRect(29, 45, 10, 7, 2, 1);
  else display.drawRoundRect(29, 45, 10, 7, 2, 1);
  display.setCursor(46, 45) ;
  display.print("GATE ") ;
  if(GateTime == 0) display.print("0.4 SEC") ;
  if(GateTime == 1) display.print("4.0 SEC") ;
  
  display.setCursor(0, 57) ;  
  // RANGE AND PRESCALER
  // LOW RANGE
  if(Range == 0) display.print("100 kHz - 45 MHz") ;
  // HIGH RANGE
  if(Range >= 1) 
  {
    display.print("25 MHz - 1.0 GHz") ;
    // STATUS PRESCALER
    display.setCursor(100, 57) ;
    display.print(PrescalerValue,DEC) ;
  }
  // Cursor
  if(Cursor == 1) display.fillTriangle(122, 48, 127, 45, 127, 51, 1) ;
  if(Cursor == 2) display.fillTriangle(122, 60, 127, 57, 127, 63, 1) ;
  display.display() ;
}


// /////////////////////////////////////////////////////////////////////
// S E T U P
// /////////////////////////////////////////////////////////////////////


void setup()
{
  Serial.begin(115200) ;

  Wire.begin() ;

  // INIT OLED
  display.begin(SH1106_SWITCHCAPVCC);

  // SHOW STARTUP SCREEN
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0); display.print("****");
  display.setCursor(33,0); display.print("COUNTERMOD");
  display.setCursor(104,0); display.print("****");
  display.drawLine(0, 12, 128, 12, WHITE);
  display.setTextSize(1);
  display.setCursor(0, 21);
  display.println("A 10 MHz TO 1.2 GHz ");
  display.setCursor(0, 33);
  display.println("UHF FREQUENCY COUNTER");
  display.setCursor(0, 45);
  display.println("(C) ETH QUANTUMOPTICS");
  display.setCursor(0, 57);
  display.println("BUILT 25.07.2020");
  display.display();
  delay(999) ;
  FlushInputBuffer() ;

  pinMode(RotaryEncoder1, INPUT_PULLUP);
  pinMode(RotaryEncoder2, INPUT_PULLUP);
  pinMode(RotaryEncoder3, INPUT_PULLUP);
  // YELLOW
  attachInterrupt(digitalPinToInterrupt(RotaryEncoder2), 
	RotaryEncoderISR2, FALLING);
  // GREEN
  attachInterrupt(digitalPinToInterrupt(RotaryEncoder3), 
	RotaryEncoderISR3, FALLING);
  // PRESCALER
  pinMode(PrescalerPin12, OUTPUT) ;
  pinMode(PrescalerPin3, OUTPUT) ;
  pinMode(RangePin, OUTPUT) ;

  Serial.println("Countermod V1.9 by Changpuak.ch (C) 07/2020") ;
  Serial.print("PRESCALER : "); Serial.println(PrescalerValue, DEC) ;
  Serial.print("Range : ") ; Serial.println(Range, DEC) ;
  Serial.print("GATE-TIME : ");
  Serial.println(GateTime, DEC) ;
  Serial.print("REFERENCE : ") ;
  if(ReferenceSource == 1) Serial.println("EXTERNAL") ;
  else Serial.println("INTERNAL XCO") ;
  Serial.print("FREQUENCY : ") ;
  Serial.print(F_TRUE, DEC) ;
  Serial.println(" Hz") ;
  Serial.println("DEVICE READY.\n") ;

  pinMode(GATE, INPUT) ;
  pinMode(GATE_SEL_0, OUTPUT) ;
  SetGateTime(GateTime) ;
  pinMode(RSSI_Pin, INPUT) ;
  pinMode(TimeBaseSelectPin, OUTPUT) ;
  pinMode(SR_CLEAR, OUTPUT) ;

  SetRange() ;
  digitalWrite(SR_CLEAR, HIGH) ;
  delay(10);
  digitalWrite(SR_CLEAR, LOW) ;
  
  delay(3000);
}


// /////////////////////////////////////////////////////////////////////
// AUX FUNCTIONS
// /////////////////////////////////////////////////////////////////////


void EvaluateKeyBoard()
{
    if (LEFT)
    {
      noInterrupts() ;
      // GATE-TIME
      if((Cursor == 1) && (millis() > NextCursorAction))
      {
        GateTime -= 1 ;
        if(GateTime < 0) GateTime = 1 ;
        NextCursorAction = millis() + CursorDelay ;
      }
      // INPUT RANGE
      if((Cursor == 2) && (millis() > NextCursorAction))
      {
        Range -= 1 ;
        if(Range < 0) Range = 4 ;
        NextCursorAction = millis() + CursorDelay ;
      }
      SetGateTime(GateTime) ;
      SetRange() ;
      LEFT = false ;
      RIGHT = false ;
      interrupts() ;
    } 
    
    if (RIGHT)
    {
      noInterrupts() ;
      // GATE-TIME
      if((Cursor == 1) && (millis() > NextCursorAction))
      {
        GateTime += 1 ;
        if(GateTime > 1) GateTime = 0 ;
        NextCursorAction = millis() + CursorDelay ;
      }
      // INPUT RANGE
      if((Cursor == 2) && (millis() > NextCursorAction))
      {
        Range += 1 ;
        if(Range > 4) Range = 0 ;
        NextCursorAction = millis() + CursorDelay ;
      }
      SetGateTime(GateTime) ;
      SetRange() ;
      LEFT = false ;
      RIGHT = false ;
      interrupts() ;
    }
    
    // KEY PRESSED >> ADVANCE CURSOR
    if(PRESS)
    {
      if(millis() > NextCursorAction)
      {
        Cursor += 1 ;
        if(Cursor == 3) Cursor = 1 ;
        NextCursorAction = millis() + CursorDelay ;
      }    
      PRESS = false ;    
    }  
}


byte RotByte(byte x)
{
  byte y = 0x00 ;
  if((x & 0x01) > 0) y |= 0x80 ;
  if((x & 0x02) > 0) y |= 0x40 ;
  if((x & 0x04) > 0) y |= 0x20 ;
  if((x & 0x08) > 0) y |= 0x10 ;
  if((x & 0x10) > 0) y |= 0x08 ;
  if((x & 0x20) > 0) y |= 0x04 ;
  if((x & 0x40) > 0) y |= 0x02 ;
  if((x & 0x80) > 0) y |= 0x01 ;
  return y ;
}


void ReadDividerChain()
{
    // MSB
    Wire.beginTransmission(0x3C) ;  
    Wire.write(0x00) ;           
    Wire.endTransmission() ; 
    byte MSB = 0x00 ;
    Wire.requestFrom(0x3C, 1) ;
    if(Wire.available()) MSB = Wire.read() ;
    F_RAW = RotByte(MSB) ;
    F_RAW = F_RAW << 8 ;
    // ISB
    Wire.beginTransmission(0x39) ;  
    Wire.write(0x00) ;           
    Wire.endTransmission() ; 
    byte ISB = 0x00 ;
    Wire.requestFrom(0x39, 1) ;
    if(Wire.available()) ISB = Wire.read() ;
    F_RAW |= RotByte(ISB) ;
    F_RAW = F_RAW << 8 ;
    // LSB
    Wire.beginTransmission(0x38) ;  
    Wire.write(0x00) ;           
    Wire.endTransmission() ; 
    byte LSB = 0x00 ;
    Wire.requestFrom(0x38, 1) ;
    if(Wire.available()) LSB = Wire.read() ;
    F_RAW |= RotByte(LSB) ;
}



// /////////////////////////////////////////////////////////////////////
// M A I N L O O P
// /////////////////////////////////////////////////////////////////////


void loop()
{
  StatusGateOld = StatusGate ;
  StatusGate = digitalRead(GATE) ;
  StatusToken = StatusGateOld + StatusGate ;
  switch (StatusToken) 
  {
  case 0:
    // Gate is still closed
    if(digitalRead(RotaryEncoder1) == 0) PRESS = true ;
    EvaluateKeyBoard() ;
    CheckForSerialInput() ;
    EvaluateSerialInput() ;
    CheckReferenceSource() ;
    UpDateDisplay() ;

    break;

  case 1:
    // Gate status has changed recently
    // If Gate has closed
    if(StatusGate == 0) 
    {
      // READ NEW FREQUENCY
      ReadDividerChain() ;
      // RESET COUNTER
      digitalWrite(SR_CLEAR, HIGH) ;
      delay(5);
      digitalWrite(SR_CLEAR, LOW) ;
      // CALCULATE TRUE FREQUENCY
      F_TRUE = F_RAW ;  
      // COMPENSATE FOR PRESCALER
      if(Range == 1) F_TRUE = F_TRUE * 10 ;
      if(Range == 2) F_TRUE = F_TRUE * 20 ;
      if(Range == 3) F_TRUE = F_TRUE * 40 ;
      if(Range == 4) F_TRUE = F_TRUE * 80 ;
      // COMPENSATE FOR GATE-TIME
      if(GateTime == 0) F_TRUE = F_TRUE * 2.5 ;
      if(GateTime == 1) F_TRUE = F_TRUE * 0.25 ;
      // DISPLAY NEW FREQUENCY
      UpDateDisplay() ;
      // OVERWRITE STATUS-TOKEN ... JUST IN CASE :-)
      StatusToken = 2 ;
      // Serial.println(F_TRUE,DEC) ;
    }
    break;
  
  case 2:
    // Gate is still open
    if(digitalRead(RotaryEncoder1) == 0) PRESS = true ;
    EvaluateKeyBoard() ;
    CheckForSerialInput() ;
    EvaluateSerialInput() ;
    CheckReferenceSource() ;
    UpDateDisplay() ;
    break;
    
  default:
    // NOP
    // NOP
    break; 
  } 

  delay(1) ;
}


// /////////////////////////////////////////////////////////////////////
// INTERRUPT SERVICE ROUTINES
// /////////////////////////////////////////////////////////////////////


void RotaryEncoderISR2()
{
  // YELLOW
  LEFT = false ;
  RIGHT = false ;
  int autre = digitalRead(RotaryEncoder3) ;
  if (autre < 1) LEFT = true ;
  if (autre > 0) RIGHT = true ;
}

void RotaryEncoderISR3()
{
  // GREEN
  LEFT = false ;
  RIGHT = false ;
  int autre = digitalRead(RotaryEncoder2) ;
  if (autre < 1) RIGHT = true ;
  if (autre > 0) LEFT = true ;
}


// /////////////////////////////////////////////////////////////////////
// END OF FILE.
// /////////////////////////////////////////////////////////////////////




✈ Remote Control of the Countermod








Remote Control with e.g. HTerm 0.8.5 from Tobias Hammer




✈ Rearpanel Connectors




Among the (obvious) USB port from the Arduino Nano Every, there are two BNC connectors. The device may be synchronised to an external 10 MHz Frequency Standard. Apply them to the white BNC. This one is galvanically isolated. An AD8307 measures the level and the Arduino then switches to external.

The second BNC Connector outputs a 1 PPS signal to be used to synchronise other devices.


External 10 MHz, -20 dBm minimum required
The second BNC Connector outputs 1 PPS





And yes, we love Radio Basilisk :-)




✈ Share your thoughts



The webmaster does not read these comments regularely. Urgent questions should be send via email. Ads or links to completely uncorrelated things will be removed.


Your Browser says that you allow tracking. Mayst we suggest that you check that DNT thing ?

 
t1 = 6585 d

t2 = 279 ms

★ ★ ★  Copyright © 2006 - 2024 by changpuak.ch  ★ ★ ★

Impressum