2012年5月23日水曜日

エレキー

【エレキー】

インターネットではIambic Keyerで調べると、色々検索できた。

Iambic keyer with Arduino
Arduino CW keyer
Arduino Iambic Keyer


このなかで3.Arduino CW keyerを利用することにした。現在のマイコンボードではポートの数の関係でメモリーを持つことや、速度調節の機能を持たすことはできない。シンプルなキーヤーがとりあえず必要だ。DDSをコントロールして7MHzのトランシーバーを完成させて、それから機能を拡張していきたい。


【Sketch】

// Iambic Morse Code Keyer Sketch
// Copyright (c) 2009 Steven T. Elliott
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details:
//
// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
// Boston, MA 02111-1307 USA
//
// http://openqrp.org/?p=343
//
// "Trimmed" by Bill Bishop - wrb[at]wrbishop.com
//

///////////////////////////////////////////////////////////////////////////////
//
// openQRP CPU Pin Definitions
//
///////////////////////////////////////////////////////////////////////////////
//
// Digital Pins
//
int buzzerPin = 10; // Tone output pin
int LeftPin = 0; // Left paddle input
int RightPin = 1; // Right paddle input
int ledPin = 13; //

//
////////////////////////////////////////////////////////////////////////////////
//
// keyerControl bit definitions
//

#define DIT_L 0x01 // Dit latch
#define DAH_L 0x02 // Dah latch
#define DIT_PROC 0x04 // Dit is being processed
#define PDLSWAP 0x08 // 0 for normal, 1 for swap
#define IAMBICB 0x00 // 0 for Iambic A, 1 for Iambic B
//
////////////////////////////////////////////////////////////////////////////////
// Global Variables
//

unsigned long ditTime; // No. milliseconds per dit
unsigned char keyerControl;
unsigned char keyerState;
#define NOTE_D5 587 // "pitch.h" at http://arduino.cc/en/Tutorial/Tone

///////////////////////////////////////////////////////////////////////////////
//
// State Machine Defines
enum KSTYPE {IDLE, CHK_DIT, CHK_DAH, KEYED_PREP, KEYED, INTER_ELEMENT };

///////////////////////////////////////////////////////////////////////////////
//
// System Initialization
//
///////////////////////////////////////////////////////////////////////////////

void setup() {
// Setup outputs
pinMode(ledPin, OUTPUT); // sets the digital pin as output
// Setup control input pins
pinMode(LeftPin, INPUT); // sets Left Paddle digital pin as input
pinMode(RightPin, INPUT); // sets Right Paddle digital pin as input
digitalWrite(ledPin, LOW); // turn the LED off
keyerState = IDLE;
keyerControl = IAMBICB; // Or 0 for IAMBICA
loadWPM(15); // Fix speed at 15 WPM
}

///////////////////////////////////////////////////////////////////////////////
//
// Main Work Loop
//
///////////////////////////////////////////////////////////////////////////////

void loop()
{
static long ktimer;
int debounce;
// Basic Iambic Keyer
// keyerControl contains processing flags and keyer mode bits
// Supports Iambic A and B
// State machine based, uses calls to millis() for timing.
switch (keyerState) {
case IDLE:
// Wait for direct or latched paddle press
if ((digitalRead(LeftPin) == LOW) ||
(digitalRead(RightPin) == LOW) ||
(keyerControl & 0x03)) {
update_PaddleLatch();
keyerState = CHK_DIT;
}
break;
case CHK_DIT:
// See if the dit paddle was pressed
if (keyerControl & DIT_L) {
keyerControl |= DIT_PROC;
ktimer = ditTime;
keyerState = KEYED_PREP;
}
else {
keyerState = CHK_DAH;
}
break;
case CHK_DAH:
// See if dah paddle was pressed
if (keyerControl & DAH_L) {
ktimer = ditTime*3;
keyerState = KEYED_PREP;
}
else {
keyerState = IDLE;
}
break;
case KEYED_PREP:
// Assert key down, start timing, state shared for dit or dah
digitalWrite(ledPin, HIGH); // turn the LED on
tone( buzzerPin, NOTE_D5 );
ktimer += millis(); // set ktimer to interval end time
keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits
keyerState = KEYED; // next state
break;
case KEYED:
// Wait for timer to expire
if (millis() > ktimer) { // are we at end of key down ?
digitalWrite(ledPin, LOW); // turn the LED off
noTone( buzzerPin );
ktimer = millis() + ditTime; // inter-element time
keyerState = INTER_ELEMENT; // next state
}

else if (keyerControl & IAMBICB) {
update_PaddleLatch(); // early paddle latch in Iambic B mode
}
break;
case INTER_ELEMENT:
// Insert time between dits/dahs
update_PaddleLatch(); // latch paddle state
if (millis() > ktimer) { // are we at end of inter-space ?
if (keyerControl & DIT_PROC) { // was it a dit or dah ?
keyerControl &= ~(DIT_L + DIT_PROC); // clear two bits
keyerState = CHK_DAH; // dit done, check for dah
}
else {
keyerControl &= ~(DAH_L); // clear dah latch
keyerState = IDLE; // go idle
}
}
break;

}
}

///////////////////////////////////////////////////////////////////////////////
//
// Latch dit and/or dah press
//
// Called by keyer routine
//
///////////////////////////////////////////////////////////////////////////////

void update_PaddleLatch()
{
if (digitalRead(RightPin) == LOW) {
keyerControl |= DIT_L;
}
if (digitalRead(LeftPin) == LOW) {
keyerControl |= DAH_L;
}
}

///////////////////////////////////////////////////////////////////////////////
//
// Calculate new time constants based on wpm value
//
//////////////////////////////////////////////////////////////////////////////

void loadWPM (int wpm)
{
ditTime = 1200/wpm;
}

ATMEGA328用マイコンボードについて

【マイコンボード】 
ArduinoはArduino Duemilanoveを使用しているが、2台目以降は秋月電子のボードを使っている。ATmega328はマルツパーツでATmega328 with Arduino Bootloaderを購入した。これでかなり節約できた。そのうち自分でBootloaderをインストールするつもりだ。

【開発環境】
Arduino IDEはArduino 1.0.1となりメニューが日本語化された。これでかなり便利になった。

2012年5月5日土曜日

【DDS】
このSketchを実行すると、LCDに”DDS On” と”DDS Off”の表示とLEDの点滅が繰り返される。その時に7003.000kHzが発振されている。但し、LCDはSD1602 HULBを使用した。接続に注意。


Sketch


 //      2012/02/28  AD9850/51 DDS-ICのテストプログラム
 // include the library code:
 #include <LiquidCrystal.h>
 // initialize the library with the numbers of the interface pins
 //LiquidCrystal(rs, enable, d4, d5, d6, d7)
 LiquidCrystal lcd(12, 11, 7, 6, 5, 4);
 // Variable
  int ledPin=13;
  int FreqUpdatePin=14;
  int dataLinePin=15;
  int w_ClkPin=16;
  byte ddsCmd= B00000000;      //34.359738368
  byte ddsCmdOff= B00000001;
  //long  ddsData=240518168; //7000000=7000.000kHz
  long  ddsData=240621248; //7003000=7003.000kHz
  long setOff=0;
 
  void setup() {
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  lcd.home();
  lcd.print("Hello, world!");
  lcd.setCursor(0,1);    //setCursor(x,y)
  lcd.print("JA1NHL");
  pinMode(ledPin,OUTPUT);
  pinMode(FreqUpdatePin,OUTPUT);
  pinMode(dataLinePin,OUTPUT);
  pinMode(w_ClkPin,OUTPUT);
  initializeDds();
 }

void loop() {
   digitalWrite(ledPin,HIGH);
   ddsOutput();
   lcd.clear();
   lcd.print("DDS On");
   delay(2000);
   digitalWrite(ledPin,LOW);
   ddsOutputOff();
   lcd.clear();
   lcd.print("DDS Off");
   delay(1500);  
 }

 void initializeDds(){  
   digitalWrite( dataLinePin,LOW);
   digitalWrite( w_ClkPin,LOW);
   delayMicroseconds(10);
   digitalWrite( w_ClkPin,HIGH);
   delayMicroseconds(10);
   digitalWrite( w_ClkPin,LOW);
   delayMicroseconds(10);
   digitalWrite( FreqUpdatePin,HIGH);
   delayMicroseconds(10);
   digitalWrite( FreqUpdatePin,LOW);
   delayMicroseconds(10);
 }

 void ddsOutput(){
   shiftOut( dataLinePin, w_ClkPin, LSBFIRST,ddsData);
   shiftOut( dataLinePin, w_ClkPin, LSBFIRST,ddsData>>8);
   shiftOut( dataLinePin, w_ClkPin, LSBFIRST,ddsData>>16);
   shiftOut( dataLinePin, w_ClkPin, LSBFIRST,ddsData>>24);
   shiftOut( dataLinePin, w_ClkPin, LSBFIRST,ddsCmd);
   delayMicroseconds(10);
   digitalWrite( FreqUpdatePin,HIGH);
   delayMicroseconds(10);
   digitalWrite( FreqUpdatePin,LOW);
 }

 void ddsOutputOff(){
   shiftOut( dataLinePin, w_ClkPin, LSBFIRST,setOff);
   shiftOut( dataLinePin, w_ClkPin, LSBFIRST,setOff>>8);
   shiftOut( dataLinePin, w_ClkPin, LSBFIRST,setOff>>16);
   shiftOut( dataLinePin, w_ClkPin, LSBFIRST,setOff>>24);
   shiftOut( dataLinePin, w_ClkPin, LSBFIRST,ddsCmd);
   delayMicroseconds(10);
   digitalWrite( FreqUpdatePin,HIGH);
   delayMicroseconds(10);
   digitalWrite( FreqUpdatePin,LOW);
 }

/*
  LiquidCrystal Library - display() and noDisplay()

 Demonstrates the use a 16x2 LCD display.  The LiquidCrystal
 library works with all LCD displays that are compatible with the
 Hitachi HD44780 driver. There are many of them out there, and you
 can usually tell them by the 16-pin interface.
  This sketch prints "Hello World!" to the LCD and uses the
 display() and noDisplay() functions to turn on and off
 the display.

 The circuit:
 * LCD RS pin(pin 4) to digital pin 12
 * LCD Enable pin(pin 6) to digital pin 11
 * LCD D4 pin(pin 11)to digital pin 7
 * LCD D5 pin(pin 12) to digital pin 6
 * LCD D6 pin(pin 13) to digital pin 5
 * LCD D7 pin(pin 14) to digital pin 4
 * LCD R/W pin(pin 5) to ground
 * 10K resistor:
 * ends to +5V and ground
 * wiper to LCD VO pin (pin 3)

 Library originally added 18 Apr 2008
 by David A. Mellis
 library modified 5 Jul 2009
 by Limor Fried (http://www.ladyada.net)
 example added 9 Jul 2009
 by Tom Igoe
 modified 22 Nov 2010
 by Tom Igoe

 This example code is in the public domain.

 http://www.arduino.cc/en/Tutorial/LiquidCrystal
 */

【画像】 

LCDの画面
IC-7400の受信画面


  
発振した時の LCD 画面
IC-7400で受信した画面

ブレッドボード上に仮組立を行う

【仮組み立て】
ブレッドボード上に組み立ててみました。

2012年5月4日金曜日

DDS_VFOの回路図 (Arduino)




【回路図】Arduinoの結線図です。
















ブレッドボード上に組み立ててみる。

Encoderを使用してDDS_VFOの周波数を変化させる。

[Encoder]

秋月のロータリーエンコーダを使用する。BASCOMでエンコーダを操作するプログラム例はすぐに見つかるがArduinoではなかなか見つからなかった。
dialStep10PinとdialStep1000Pinを同時にOnしてエンコーダを回せば周波数は20kHzごとに変化します。この2つのピンは前日の回路図にあるように1N4148を通じてArduinoに接続する。
但し、まだDDSを接続させてVFOの機能はまだである。KEYの代用しているタクトスイッチを押すと画面表示が変わる。


Sketch


//DDS_TestProgram by JA1NHL  21Apr 2012
//EncodeDisplayKeying
// include the library code:
#include <LiquidCrystal.h>
//LiquidCrystal(rs, enable, d4, d5, d6, d7)
LiquidCrystal lcd(12, 11, 7, 6, 5, 4);
#include <Bounce.h>
// usually the rotary encoders three pins have the ground pin in the middle
  int encoderPinA = 2;
  int encoderPinB = 3;
  int dialStep10Pin=8;    
  int dialStep1000Pin=9;
  int ledPin=13;          //monitor
  int keyingPin=19;
  long  Fdata=7000000;
  int  FdataZoubun=100;
  char TxRxState='R';
  volatile long encoderPos = 0;          // a counter for the dial
  volatile long lastReportedPos = 0;      // change management
  static boolean rotating=false;          // debounce management
  boolean A_set = false;                 // interrupt service routine vars          
  boolean B_set = false;
  Bounce bouncer=Bounce(keyingPin,5);
void setup() {
  Serial.begin(9600);               // output for Debug
  lcd.begin(16, 2);                  // set up the LCD's number of columns and rows:
  // Input Output
  pinMode(encoderPinA, INPUT);    
  pinMode(encoderPinB, INPUT);    
  pinMode(dialStep10Pin, INPUT);      
  pinMode(dialStep1000Pin, INPUT);  
  pinMode(keyingPin, INPUT);      
  pinMode(ledPin,OUTPUT);        
  // turn on pullup resistors   内蔵プルアップ
  digitalWrite(encoderPinA, HIGH);
  digitalWrite(encoderPinB, HIGH);
  digitalWrite(dialStep10Pin, HIGH);
  digitalWrite(dialStep1000Pin, HIGH);
  digitalWrite(keyingPin, HIGH);
 //setup the LCD's number of columns and rows;
  lcd.begin(16,2);                      
  lcd.clear();
  attachInterrupt(0, doEncoderA, CHANGE);    // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(1, doEncoderB, CHANGE);    // encoder pin on interrupt 1 (pin 3)
 }
// main loop, work is done by interrupt service routines, this one only prints stuff
void loop() {
  if (digitalRead (keyingPin) ==LOW){
        digitalWrite(ledPin,HIGH);
        TxRxState='T';
   }  else{
         digitalWrite(ledPin,LOW);
         TxRxState='R';
   } displayKeying();
bouncer.update();
int keyingState=bouncer.read();
if(keyingState==LOW){
  digitalWrite(ledPin,HIGH);
  TxRxState='T';
  displayKeying();
    }
    else{
      digitalWrite(ledPin,LOW);
      TxRxState='R';
      displayKeying();
    }
  if (digitalRead (dialStep10Pin) ==LOW){
      if (digitalRead (dialStep10Pin) ==LOW && digitalRead (dialStep1000Pin)==LOW) {
         FdataZoubun=20000; //20kHz
        } else {
         FdataZoubun=10;        
   }
   } else  if (digitalRead (dialStep1000Pin) ==LOW) {
         FdataZoubun=1000;    
   } else {
         FdataZoubun=100;        
   }
   rotating = true;  // reset the debouncer
      if (encoderPos >lastReportedPos ) {   //
       Serial.println(Fdata+FdataZoubun, DEC);
         Serial.println( encoderPos, DEC);
      Fdata= Fdata+FdataZoubun;
           displayEncoder();
             lastReportedPos = encoderPos;
    }
    if (encoderPos < lastReportedPos) {   //
       Serial.println(Fdata-FdataZoubun, DEC);
         Serial.println( encoderPos, DEC);
      Fdata= Fdata-FdataZoubun;
           displayEncoder();
             lastReportedPos = encoderPos;
    }
}

//Subroutine // Interrupt on A changing state
void doEncoderA(){
        // debounce
    if ( rotating ) delay (1);  // wait a little until the bouncing is done
        // Test transition, did things really change?
    if( digitalRead(encoderPinA) != A_set ) {  // debounce once more
      A_set = !A_set;       //! not hitei
      // adjust counter - if A leads B
         if ( A_set && !B_set )
            encoderPos -= 1;
            rotating = false;  // no more debouncing until loop() hits again
    }
}
//Subroutine // Interrupt on B changing state, same as A above
void doEncoderB(){
        //debounce
    if ( rotating ) delay (1);
       //Test transition, did things really change?
    if( digitalRead(encoderPinB) != B_set ) {
      B_set = !B_set;
      //  adjust counter + 1 if B leads A
    if( B_set && !A_set )
        encoderPos += 1;
        rotating = false;
    }
}
//Subroutine display
void displayEncoder(){
     lcd.home();
     String s =String(Fdata) ;
    // String s =String(Fdata+FdataZoubun*encoderPos) ;
     lcd.print("FREQ: ");
     lcd.print(s.charAt(0));
     lcd.print(s.substring(1, 4));
     lcd.print(".");
     lcd.print(s.substring(4,6));
     lcd.print("kHz");
     lcd.setCursor(1,1);
     lcd.print("* RIT:");
     lcd.setCursor(13,1);
     lcd.print("kHz");
}
//Subroutine
void displayKeying(){
       lcd.setCursor(0,1);
       lcd.print(TxRxState);
}
/*
http://www.arduino.cc/playground/Main/RotaryEncoders#Example1
*/
/*
Another Interrupt Library THAT REALLY WORKS (the Encoder
interrupts the processor and
debounces like there is no tomorrow).
by rafbuff
*/

/* interrupt routine for Rotary Encoders
   tested with Noble RE0124PVB 17.7FINB-24 http://www.nobleusa.com/pdf/xre.pdf - available at pollin.de
   and a few others, seems pretty universal
   The average rotary encoder has three pins, seen from front: A C B
   Clockwise rotation A(on)->B(on)->A(off)->B(off)
   CounterCW rotation B(on)->A(on)->B(off)->A(off)
   and may be a push switch with another two pins, pulled low at pin 8 in this case
   raf@synapps.de 20120107
*/

ADC アナログ入力をデジタル変換する

sketchの勉強のためにアナログ入力をデジタル変換することを考える。
これは受信機のRITのために必要である。

Sketch


//DDS_TestProgram by JA1NHL  08 Feb 2012
// EncodeDisplayADCFeb08
//include the library code:
#include <LiquidCrystal.h>
//LiquidCrystal(rs, enable, d4, d5, d6, d7)
LiquidCrystal lcd(12, 11, 7, 6, 5, 4);
#include <Bounce.h>
  int ledPin=13;          //monitor
  int adcInputPin=17;    //RIT INPUT PIN
  int adcInputValue=0;
  long adcInputValue2=0;        // RIT work
  char adcStrings[ ]="1234";
  char ritStrings[ ]="1234";
  char TxRxState='R';
 void setup() {
  Serial.begin(9600);               // output for Debug
  lcd.begin(16, 2);                  // set up the LCD's number of columns and rows:
  // Input Output
  pinMode(ledPin,OUTPUT);      
  // turn on pullup resistors   内蔵プルアップ 抵抗は使用しない

 //setup the LCD's number of columns and rows;
  lcd.begin(16,2);                      
  lcd.clear();
}

// main loop, work is done by interrupt service routines, this one only prints stuff
void loop() {
 adcInputValue=analogRead(adcInputPin);
 adcInputValue=adcInputValue/10;
 adcInputValue=adcInputValue*10;
 adcInputValue=510-adcInputValue;
 String s =String(adcInputValue);
lcd.setCursor(1,1);
    lcd.print("* RIT: ");
 
    if (adcInputValue<-100 )  { //-510
      lcd.print(s.substring(0,2));
      lcd.print(".");
      lcd.print(s.substring(2,4));
  }  else if (adcInputValue>=-100 && adcInputValue<0 ) {  //-90
      lcd.print(s.substring(0,1));
      lcd.print("0.");
      lcd.print(s.substring(1,3));
  } else if (adcInputValue>=0 && adcInputValue<100 ) { //90
      lcd.print("+0.");
      lcd.print(s.substring(0,2));
  }  else if (adcInputValue>=100 ) { //510
      lcd.print("+");
      lcd.print(s.substring(0,1));
      lcd.print(".");
      lcd.print(s.substring(1,3));
  }
}






/*
http://www.arduino.cc/playground/Main/RotaryEncoders#Example1
*/
/*
Another Interrupt Library THAT
REALLY WORKS (the Encoder
interrupts the processor and
debounces like there is no
tomorrow).
by rafbuff
*/

/* interrupt routine for Rotary Encoders
   tested with Noble RE0124PVB 17.7FINB-24 http://www.nobleusa.com/pdf/xre.pdf - available at pollin.de
   and a few others, seems pretty universal
   The average rotary encoder has three pins, seen from front: A C B
   Clockwise rotation A(on)->B(on)->A(off)->B(off)
   CounterCW rotation B(on)->A(on)->B(off)->A(off)
   and may be a push switch with another two pins, pulled low at pin 8 in this case
   raf@synapps.de 20120107
*/