Mittwoch, 26. Dezember 2012

Time machine: Encoder.


Всем привет. Я не планировал описывать каждую деталь статьёй. Но энкодер стоит отдельного внимания. Для «Машины времени» я планировал купить самый доступный энкодер и использовать готовую библиотеку, коих на сайте Arduino много. Энкодер от Panasonic понравился наличием массивной ручки, в документации написано, что он long-life, что очень хорошо, размеры как раз подходят под мой корпус, очень привлекательная цена. Я купил пару штук EVEQDBRL416B и сделал под них платы. Когда я добрался до написания кода, оказалось, что ни одна библиотека на Arduino не работает с этим энкодером хорошо. У этого энкодера всего два состояния между переключениями. Фронт в канале «B» очень близко находится к месту фиксации, что добавляет проблемы при переключении. От небольшого нажима на ручку энкодера происходят ложные срабатывания. После небольших проб с готовыми библиотеками у меня возникло желание заменить этот энкодер на другой.
Но мысль о том, что надо переделывать уже готовые платы питания и интерфейса, останавливала открыть окно и выбросить энкодер. На написание библиотеки я потратил целый день. После третьего подхода получился очень простой обработчик энкодера.
Надеюсь эта библиотека будет полезной не только мне!!!
/*
  Pan_Encoder_ABC.cpp - A library for reading Encoder PANASONIC EVEQDBRL416B.

  Copyright 2012 Andrew Buckin(aka, "ka_ru"), except for the code at the end marked
  "The following was taken from the Arduino's code."  That portion is copyright by
  Andrew Buckin and is distributed under the GNU Lesser General Public License 2.1
  or any subsequent version.

  For those portions Copyright by Andrew Buckin, the following license applies.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    Questions?  Send mail to ka_ru2003@msn.com
*/

#include <PinChangeInt.h>

enum PinAssignments {
  encoderPinA = A2,   // rigth
  encoderPinB = A3,   // left
  clearButton = 8    // another two pins
};

volatile unsigned int encoderPos = 100;  // a counter for the dial
unsigned int lastReportedPos = 1;   // change management
static boolean rotating=false;      // debounce management

// interrupt service routine vars
volatile boolean ABc = false;
volatile boolean BBc = false;
volatile boolean AAc = false;
volatile boolean BAc = false;
int8_t clicks = 0;      // Counter to indicate cumulative clicks in either direction
int8_t direction = NULL;   // indicator
int8_t enc = NULL;   // indicator

void setup() {

  pinMode(encoderPinA, INPUT); 
  pinMode(encoderPinB, INPUT); 
  pinMode(clearButton, INPUT);
 // turn on pullup resistors
  digitalWrite(encoderPinA, HIGH);
  digitalWrite(encoderPinB, HIGH);
  digitalWrite(clearButton, HIGH);

  PCintPort::attachInterrupt(encoderPinA, &doEncoderA, CHANGE);
  PCintPort::attachInterrupt(encoderPinB, &doEncoderB, CHANGE);

  Serial.begin(9600);  // output
}

// main loop, work is done by interrupt service routines, this one only prints stuff
void loop() { 
  rotating = true;  // reset the debouncer
   
  if (direction != NULL){
   enc = !NULL;
    if (direction > 0) clicks++;
    else clicks--;
    direction = NULL;
  }
    
  if (enc != NULL) {
    Serial.print("Index:");
    Serial.println(encoderPos  + clicks, DEC);
    enc = NULL;
  }
 
  if (digitalRead(clearButton) == LOW )  {
    encoderPos = 0;
  }
}

// Interrupt on A changing state
void doEncoderA(){
  delayMicroseconds(50);
  AAc = digitalRead(encoderPinA);
  BAc = digitalRead(encoderPinB);
  if (!ABc && AAc && !BBc && !BAc){direction=1;}
  if (ABc && !AAc && BBc && BAc){direction=1;}
  if (!ABc && AAc && BBc && BAc){direction=-1;}
  if (ABc && !AAc && !BBc && !BAc){direction=-1;}

}

// Interrupt on B changing state
void doEncoderB(){
  delayMicroseconds(50);
  ABc = digitalRead(encoderPinA);
  BBc = digitalRead(encoderPinB);
}


Успешных вам кадров.
© Andrew Buckin.

Shutterstock Dreamstime
Fotostream http://www.flickr.com

Time machine: Analog.


Этот модуль попал в «Машину времени» по необходимости. Статья будет небольшой. Как я уже писал ранее, после проб с микрофоном, усиления 20дБ оказалось мало. Микрофон я купил не самый лучший, но это не означает, что его нельзя использовать. Пришлось поставить ещё один каскад усиления. Чтобы в будущем не испытывать неудобств в регулировании и не зависить от качества микрофона, поставил схему усилителя с программированным уровнем усиления на цифровом потенциометре с SPI интерфейсом(TPL0501). SPI у меня пока не занят. :) Так как я не собираюсь заниматься в «Машине времени» обработкой сигналов, отказался от использования АЦП и применил аналоговый компаратор с последующей обработкой по прерыванию, что позволит сократить код и уменьшить время реакции на событие. Для регулирования порога срабатывания компаратора установил ещё один цифровой потенциометр.

Плата аналогового усилителя сделана для макетирования, схема немного избыточна, в конечном устройстве будет стоять только то, что нужно.


Для тестирования написал маленькую программу.

#include <PCD8544.h>
volatile long newPosition = 0;
volatile long oldPosition  = 0;
#include <SPI.h>
//#include <WProgram.h>  // This include should go first, otherwise does not compile.
#include <Button.h>
#include <TicksPerSecond.h>
#include <RotaryEncoderAcelleration.h>

//static const byte glyph[] = { B00010000, B00110100, B00110000, B00110100, B00010000 };
static PCD8544 lcd;
static RotaryEncoderAcelleration rotor;

static const byte LCD_WIDTH = 84;
static const byte LCD_HEIGHT = 48;

const int analogInPin = A7;
const int TPL0501_CS1 = A0;
const int TPL0501_CS2 = A1;
const int rotorPinA = A2;    // One quadrature pin
const int rotorPinB = A3;    // the other quadrature pin

int sensorValue = 0;        // value read from the Microphon
int sensorMAX = 0;
unsigned long time =0;

void setup() {

  lcd.begin(84, 48);
  //lcd.createChar(0, glyph);
  
  lcd.setCursor(0, 0);
  lcd.print("Value");

  lcd.setCursor(0, 2);
  lcd.print("MAX Value");

  lcd.setCursor(0, 4);
  lcd.print("Encoder");
  
  pinMode (TPL0501_CS1, OUTPUT);
  pinMode (TPL0501_CS2, OUTPUT);
  
  digitalWrite(TPL0501_CS1,HIGH);
  digitalWrite(TPL0501_CS2,HIGH);
  
  SPI.begin(); 
  Pot_value_SET(TPL0501_CS1, 0);
  
  rotor.initialize(rotorPinA, rotorPinB);
  rotor.setMinMax(0, 255);
  rotor.setPosition(0);
}

long lastRotor = 0;

void loop() {
  
  rotor.update();
  long pos = rotor.getPosition();
  
  sensorValue = analogRead(analogInPin);
  //Serial.print("analogInPin"); 
  if (sensorValue > sensorMAX) sensorMAX = sensorValue;
  if (millis() > time){
  sensorMAX = 0; 
  time = millis() + 3000;
  }
  
  lcd.setCursor(0, 1);
  lcd.clearLine();
  lcd.print(sensorValue, DEC);

  lcd.setCursor(0, 3);
  lcd.clearLine();
  lcd.print(sensorMAX, DEC);
 
  lcd.setCursor(0, 5);
  lcd.clearLine();
  lcd.print(pos, DEC);  
 
  //lcd.setCursor(80, 0);
  //lcd.drawColumn(84, map(sensorValue, 0, 1023, 0, LCD_HEIGHT)); 
    
  delay(10);                     
}

int Pot_value_SET(int Chip, int value) {
  // take the SS pin low to select the chip:
  digitalWrite(Chip,LOW);
  //  send in the value via SPI:
  SPI.transfer(value);
  // take the SS pin high to de-select the chip:
  digitalWrite(Chip,HIGH);
}





Описание «железа» на этом можно закончить. В будущем планирую оснастить «Машину времени» Bluetooth и сделать Android APPs и Shell для PC, но этим я займусь, когда будут реализованы основные функции. Теперь я отвлекусь от «железа» и займусь подготовкой кода.
PS:
Конечно можно было не затевать схему с программированным усилителем, а поставить усилитель- ограничитель. Но. Поживем - попробуем. :)
PPS:
К проекту подключился мой коллега программист Евгений Глушко. Пока я буду заниматься сведением общей схемы. Женя обещал подготовить первый релиз программы.

Успешных вам кадров.

© Andrew Buckin.
Shutterstock Dreamstime
Fotostream http://www.flickr.com