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

Your IP is 3.21.104.109
ec2-3-21-104-109.us-east-2.c
Info
Valid HTML 4.01 Transitional Creative Commons Lizenzvertrag
rss
เราจะทำแบบวิศวกรผู้ยิ่งใหญ่
We love the King
27. April 2024
Your valuable opinion :
3 stars

Avg. 3.38 from 8 votes.



28. April 1950
Arduino-Shield-KILOMOD.php    19409 Bytes    31-05-2023 09:36:47


Arduino/Genuino 15 mHz ~ 5 kHz TTL PWM Generator


Shield "KILOMOD"



This is a shield to generate TTL PWM signals from 15 mHz up to 5 kHz. The duty-cycle can be adjusted in steps of 200 µs. This is used to keep the atoms in the Lithium Lab "warm", i.e. inject fibers / set PID coefficients. Designed on inspiration by the Lithium Lab.



Arduino Shield KILOMOD



  FREQUENCY RANGE

  15 mHz ... 5 kHz

  OUTPUT POWER

  TTL, positive and negative

  POWER SUPPLY

  7.5 V, 200 mA





✈ The building Blocks • Functional Description




The Arduino MEGA2560 is the workhorse of this generator. Two long variables, 'ISLow' and 'IsHigh' hold the timing information. The loop has a delay of 100 µs. It consists of a switch constructor. If we are in the "low" season, a counter is incremented each time, until it reaches the IsLow value. Then it is switched to the "high" season. Now the counter is incremented, until it reaches the IsHigh value.

The periodically scanning of the rotary encoder is done with a timer-interrupt. The evaluation of the encoder is done by the function UpdateEncoder(), initially written by Mr. Joël Steinemann, one of our smart apprentices at ETH zürich • Quantumoptics.

If the frequency is high, the householding function of the Arduino/Genuino introduces some small jitter, but that is acceptable, as we are interested in an average value.

The advantage (regarading commercial generators) is the capability of very long sequences. As the variables are of type long, we can have a time of 2 * 214748.3647 s which corresponds to 2.32830 µHz.

That's exactly what our physicists wanted.



KILOMOD
Polarity : POSITIVE
KILOMOD
Polarity : NEGATIVE




✈ Downloads








✈ Arduino Sketch - The Code



Double click on code to select ...


/* ////////////////////////////////////////////////////////////////////// 
ARDUINO/Genuino (MEGA2560) Board - "* KILOMOD * Synthesizer"
https://changpuak.ch/electronics/Arduino-Shield-* KILOMOD *.php
Software Version 2.0 
14.02.2019 by ALEXANDER SSE FRANK
HELPFUL:
https://www.tutorialspoint.com/c_standard_library/c_function_sprintf.htm

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


// OLED 128x64 with SH1106 Controller
// E.G. DM-OLED13-625
#define OLED_MOSI  10
#define OLED_CLK   9
#define OLED_DC    12
#define OLED_CS    13
#define OLED_RESET 11


// STATE OF THE ROTARY ENCODER
const int RE2 = A3 ;  // PRESSED
const int RE1 = A2 ;
const int RE0 = A1 ;


unsigned long StartMilli ;
unsigned long DurationMilli ;
boolean PressedLong = false ;


// OUTPUT
const int PolarityPin = 2 ;
const int OutputPin = 5 ;
boolean Polarity = false ;
// Polarity = false .:. POSITIVE (NORMAL)
// Polarity = true .:. NEGATIVE (INVERTED)
long MinHigh = 1 ;      // TIMES 100 us
long MaxHigh = 655360 ; // TIMES 100 us
long MinLow = 1 ;       // TIMES 100 us
long MaxLow = 655360 ;  // TIMES 100 us
long IsHigh = 655360 ;  // TIMES 100 us
long IsLow = 655360 ;   // TIMES 100 us
long counter ;
long Timeused ;
long IsLowOld ;
long IsHighOld ;

float Frequency = 1/(0.000001*IsHigh + 0.000001*IsLow) ;
float TargetFrequency = Frequency ;
float OldNewRatio ;
unsigned int action = 0 ;       // OUTPUT IS CURRENTLY HIGH

// CURSOR
unsigned int CursorX = 6 ;
unsigned int CursorY = 2 ;
unsigned int CursorVal = 0 ;
unsigned int Rest = 0 ;
unsigned int Ganz = 0 ;
unsigned int OffsetX = 0 ;
long IncDecMultiply = 0 ;

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH1106.h>
#include <stdlib.h>
#include "TimerOne.h"

// INTERRUPT VARIABLES
volatile unsigned int RotaryEncoderStatus = 0;
volatile unsigned int RotaryEncoderStatusOld = 0;
volatile unsigned int RotaryEncoderActivity = 1 ;
int EncoderValue = 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

 
void setup() 
{
  delay(1999);
  // KNOB - ROTARY ENCODER
  pinMode(RE2, INPUT_PULLUP);
  pinMode(RE1, INPUT_PULLUP);
  pinMode(RE0, INPUT_PULLUP);
  // OUTPUT SIGNAL
  pinMode(OutputPin, OUTPUT);
  pinMode(PolarityPin, OUTPUT);
  digitalWrite(OutputPin, LOW);
  digitalWrite(PolarityPin, Polarity);
   
  Serial.begin(115200);
  
  // INIT OLED
  display.begin(SH1106_SWITCHCAPVCC);
  // SHOW STARTUP SCREEN
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.println("KILOMOD");
  display.setTextSize(1);
  display.setCursor(0,21);
  display.println("A TTL SYNTHESISER");
  display.setCursor(0,33);
  display.println("BASED ON THE MEGA2560");
  display.setCursor(0,45);
  display.println("(C) ETH QUANTUMOPTICS");
  display.setCursor(0,57);
  display.println("BUILT 14.02.2019");
  display.display();
  delay(999);
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.println("KILOMOD");
  display.setTextSize(1);
  display.setCursor(0,21);
  display.println("FIRMWARE WRITTEN BY");
  display.setCursor(0,33);
  display.println("MR. JOEL STEINEMANN");
  display.setCursor(0,45);
  display.println("MR. ALEXANDER FRANK");
  display.setCursor(0,57);
  display.println("MR. TILMAN ESSLINGER");
  display.display();
  delay(999);
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.println("KILOMOD");
  display.setTextSize(1);
  display.setCursor(0,21);
  display.println("HARDWARE DESIGNED BY");
  display.setCursor(0,33);
  display.println("MR. ALEXANDER FRANK");
  display.setCursor(0,45);
  display.println("MS. LAURA CORMAN");
  display.setCursor(0,57);
  display.println("MR. TILMAN ESSLINGER");
  display.display();
  delay(999);
    
  UpDateOLED();
  
  // ENABLE INTERRUPT FOR PIN ...
  Timer1.initialize(1000);  // EVERY 1 ms
  Timer1.attachInterrupt(CheckRotaryEncoder);
  RotaryEncoderStatus  = digitalRead(RE2) << 2 ;
  RotaryEncoderStatus |= digitalRead(RE1) << 1 ;
  RotaryEncoderStatus |= digitalRead(RE0) ;
  RotaryEncoderStatusOld = RotaryEncoderStatus ;
  RotaryEncoderActivity = 0x00;
 
}
  
void loop()
{  
  switch (action) 
  {
  case 0:
    // OUTPUT IS LOW
    counter += 1 ;
    if (counter > IsLow)
    {
      digitalWrite(OutputPin, LOW);
      action = 1 ;
      counter = 0 ;
    }
    break;
  case 1:
    // OUTPUTS HIGH
    counter += 1 ;
    if (counter > IsHigh)
    {
      digitalWrite(OutputPin, HIGH);
      action = 0 ;
      counter = 0 ;
    }
    break;
  }


  // /////////////////////////////////////////////////////////////////////
  // CHECK ROTARY ENCODER
  // /////////////////////////////////////////////////////////////////////
  
  // if (RotaryEncoderActivity != 0x00) SerialHexOutput(RotaryEncoderActivity);
  
  // KNOB PRESSED
  if (RotaryEncoderActivity == 0x04)
  {
    // FALLING EDGE ONLY
    if (( RotaryEncoderStatus & 0x04 ) == 0x00)
    {
    CursorVal += 1 ;
    if (CursorVal > 16) CursorVal = 0 ;
    Ganz = 0 ; Rest = 0 ;
    if (CursorVal > 0) 
    {
      Ganz = 1 ; 
      Rest = CursorVal ;
    }
    if (CursorVal > 6)     
    {
      Ganz = 2 ; 
      Rest = CursorVal - 6;
    }
    if (CursorVal > 12)     
    {
      Ganz = 3 ; 
      Rest = CursorVal - 12;
    }

    UpDateOLED();
    RotaryEncoderActivity = 0x00 ;
    }
  }

  // KNOB ROTATED
  // CHECK FOR CHANGE OF ROTARY ENCODER  
  if ( RotaryEncoderActivity > 0x00 ) 
  {
    // EVALUATE KNOB NOT PRESSED
    if (( RotaryEncoderStatus & 0x04 ) != 0x00)
    {
      UpdateEncoder();
    }
  }

  
  // TIMEBASE :-)
  delayMicroseconds(99);
  

}




void SerialHexOutput(byte value) 
{
   Serial.print("0x");
   if (value < 0x10) Serial.print("0");
   Serial.println(value,HEX);
}




// /////////////////////////////////////////////////////////////////////
// SUBROUTINES DISPLAY 
// /////////////////////////////////////////////////////////////////////
void UpDateOLED() 
{
  char fstr[10];
  String str ;
  int len = 0 ;

  // CURSOR FOR EVERYTHING BUT POLARITY
  CursorX = Rest ;
  CursorY = Ganz ;
  OffsetX = 7 ;
  
  display.clearDisplay();
  display.drawLine(0, 12, 128, 12, WHITE);
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.println("** KILOMOD TTL GEN **");
  // /////////////////////////////////////////
  display.setCursor(0,17);
  display.print("POLARITY : ");
  if (!Polarity) display.print("POSITIVE ");
  else display.print("NEGATIVE ");
  if (Ganz == 0) display.print("<");
  // /////////////////////////////////////////
  display.setCursor(0,29);
  display.print("HIGH : ");
  str = String(IsHigh, DEC);
  len = 42 - 6 * str.length() ;
  display.setCursor(OffsetX*6+len,29);
  for (int i=0; i<str.length(); i++)
  {
    display.print(str[i]);
  }
  display.print("00 us ");
  if (Ganz == 1) display.print("<");
  // /////////////////////////////////////////
  display.setCursor(0,41);
  display.print("LOW  : ");
  str = String(IsLow, DEC);
  len = 42 - 6 * str.length() ;
  display.setCursor(OffsetX*6+len,41);
  for (int i=0; i<str.length(); i++)
  {
    display.print(str[i]);
  }
  display.print("00 us ");
  if (Ganz == 2) display.print("<");
  // /////////////////////////////////////////
  display.setCursor(0,53);
  display.print("FREQ : ");
  Frequency = 1/(0.0001*IsHigh + 0.0001*IsLow) ;
  display.print(dtostrf(Frequency,9,3,fstr));
  display.print(" Hz ");
  if (Ganz == 3) display.print("<");
  
  if (Ganz > 0)
  {
  display.setCursor((OffsetX+CursorX)*6,19+CursorY*12);
  display.print("_");
  }
  display.drawLine(0, 63, 128, 63, WHITE);
  // DISPLAY THAT THING
  display.display();  
}


// /////////////////////////////////////////////////////////////////////
// SUBROUTINES ROTARY ENCODER
// /////////////////////////////////////////////////////////////////////

// CHECK ROTARY ENCODER (A1,A2,A3)
void CheckRotaryEncoder()
{
RotaryEncoderStatusOld = RotaryEncoderStatus ;
RotaryEncoderStatus  = digitalRead(RE2) << 2 ;
RotaryEncoderStatus |= digitalRead(RE1) << 1 ;
RotaryEncoderStatus |= digitalRead(RE0) ;
RotaryEncoderActivity = RotaryEncoderActivity 
	| (RotaryEncoderStatus ^ RotaryEncoderStatusOld) ;
}  

void UpdateEncoder()
{
  /*
      ¦   A   ¦   B   ¦
      -----------------
      ¦   0   ¦   0   ¦  
      -----------------    
 ¦¦   ¦   0   ¦   1   ¦  -1
 ¦¦   -----------------  /\
 \/   ¦   1   ¦   1   ¦  ¦¦
 +1   -----------------  ¦¦
      ¦   1   ¦   0   ¦
      -----------------
      ¦   0   ¦   0   ¦
      -----------------
 WHEN AB IS HIGH AND BY THE NEXT ROTATION B GOES HIGH, 
 THEN THE ENCODER TURNED LEFT.
 IF B GOES LOW, THEN IT TURNED RIGTH.
 EXACTLY THE OPPOSITE IS TRUE, WHEN AB IS LOW.
 */

EncoderValue = 0 ;
 
 //DETECT THE ROTATION OF THE ENCODER AND SET SOME CONSTANTS 
switch(RotaryEncoderStatusOld)
{
  // AB WERE LOW
  case 4:
    if ((RotaryEncoderStatus & 0x02) == 0x00) EncoderValue = + 1 ;
    else EncoderValue = - 1 ;
    break;
  // AB WERE HIGH
  case 14:
    if ((RotaryEncoderStatus & 0x02) == 0x00) EncoderValue = + 1;
    else EncoderValue = - 1 ;
    break;
}

// POLARITY
if (Ganz == 0)
  {
  Polarity = !Polarity ; 
  digitalWrite(PolarityPin, Polarity) ;
  UpDateOLED();
  RotaryEncoderActivity = 0x00 ;
  }

// HIGH TIME
if (Ganz == 1)
  {
  if (Rest == 1) IncDecMultiply = 100000 ;
  if (Rest == 2) IncDecMultiply = 10000 ;
  if (Rest == 3) IncDecMultiply = 1000 ;
  if (Rest == 4) IncDecMultiply = 100 ;
  if (Rest == 5) IncDecMultiply = 10 ;
  if (Rest == 6) IncDecMultiply = 1 ;
  IsHigh = IsHigh + EncoderValue * IncDecMultiply ; 
  if (IsHigh < MinHigh) IsHigh = MinHigh ;
  if (IsHigh > MaxHigh) IsHigh = MaxHigh ;
  Frequency = 1/(0.000001*IsHigh + 0.000001*IsLow) ;
  UpDateOLED();
  RotaryEncoderActivity = 0x00 ;
  }

// LOW TIME
if (Ganz == 2)
  {
  if (Rest == 1) IncDecMultiply = 100000 ;
  if (Rest == 2) IncDecMultiply = 10000 ;
  if (Rest == 3) IncDecMultiply = 1000 ;
  if (Rest == 4) IncDecMultiply = 100 ;
  if (Rest == 5) IncDecMultiply = 10 ;
  if (Rest == 6) IncDecMultiply = 1 ;
  IsLow = IsLow + EncoderValue * IncDecMultiply ; 
  if (IsLow < MinLow) IsLow = MinLow ;
  if (IsLow > MaxLow) IsLow = MaxLow ;
  Frequency = 1/(0.000001*IsHigh + 0.000001*IsLow) ;
  UpDateOLED();
  RotaryEncoderActivity = 0x00 ;
  }

// FREQUENCY
if (Ganz == 3)
  {
  if (Rest == 1) IncDecMultiply = 1000 ;
  if (Rest == 2) IncDecMultiply = 100 ;
  if (Rest == 3) IncDecMultiply = 10 ;
  if (Rest == 4) IncDecMultiply = 1 ;
  IsLowOld = IsLow ;
  IsHighOld = IsHigh ;
  // TARGET FREQUENCY
  TargetFrequency = Frequency + EncoderValue * IncDecMultiply ;  
  OldNewRatio = Frequency / TargetFrequency ;
  IsHigh = IsHigh * OldNewRatio ;
  IsLow = IsLow * OldNewRatio ;
  // IF IsHigh AND IsLow == 1 THEN WE HAVE A PROBLEM
  while ((IsLowOld == IsLow) && (IsHighOld = IsHigh) && (OldNewRatio != 1.0)) 
  {
    OldNewRatio = OldNewRatio * OldNewRatio ;
    IsHigh = IsHigh * OldNewRatio ;
    IsLow = IsLow * OldNewRatio ;
    // Serial.println(OldNewRatio,5) ;
  }
  if (IsLow < MinLow) IsLow = MinLow ;
  if (IsLow > MaxLow) IsLow = MaxLow ;
  if (IsHigh < MinHigh) IsHigh = MinHigh ;
  if (IsHigh > MaxHigh) IsHigh = MaxHigh ;
  Frequency = 1/(0.000001*IsHigh + 0.000001*IsLow) ;
  UpDateOLED();
  RotaryEncoderActivity = 0x00 ;
  }  
}

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




✈ 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 = 6498 d

t2 = 181 ms

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

Impressum