Showing posts with label RotaryEncoder. Show all posts
Showing posts with label RotaryEncoder. Show all posts

Saturday, September 27, 2014

Minima - Proposed Rotary Encoder Circuit

This is just a proposal, the final suggested circuit maybe somewhat different, this is a excerpt, with additions, from a previous post.

My current experimental Encoder software works very smooth with this circuit. Whit a little more testing, it will be published on GitHub soon.

My goal is to be as compatible with the Original Minima as possible.


The following circuit should be added to the Minima to implement a Rotary Encoder and
Multiple Push-Button Switches parallel to the original FN Switch. The original
Tuning POT has been removed to accommodate the Encoder.

--------+
Arduino |
        |
   AVCC |
   pin20|--------+---1K---- +5V
        |        |                       +--------4K7-----+
        |        = 100nF                 |                |
        |        |                       |   +--------+   |
        |        G                       +---|B      C|---+--------------------+
 Tune A2|                              G-----|G  ENC  |                        |
   pin25|--------+---------------------------|A      G|---+                    |
        |        |                           +--------+   |                    |
        |        = 1nF                                    G                    |
        |        |                                                             |
        |        G                                                             |
   AREF |                                                                      |
   pin21|----+-------+                                                         |
        |    |       |                                                         |
        |    |       = 100nF                                                   |
        |    |       |                                                         |
        |    |       G                                                         |
        |   47K                                                                |
        |    |                                                                 |
        |    |                                                                 |
        |    |                                                                 |
    PC3 |    |                                                                 |
   pin26|----+-----+---4K7---+---4K7---+---4K7---+---4K7---+---4K7---+---4K7---+
        |    |     |         |         |         |         |         |         |
        |   FNS    = 1nF     S         S         S         S         S         S
   AGND |    |     |         |         |         |         |         |         |
   pin22|----+-----+---------+---------+---------+---------+---------+---------+
        |    |
--------+    G

            "FN"           "Left"   "Right"   "SBand"     "Up"     "Down"     btn7
           (btn1)          (btn2)    (btn3)   (btn4)     (btn5)    (btn6)    (btn7)


Where:
  4K7 is a 4.7K ohm resistor
  47K is a 47K ohm resistor
  FNS is the original FN switch
    S is a new switch
    = is a capacitor
    G is a ground and AGND
  ENC is the Rotary Encoder with Push Button

Note: The Encoder pins A and B, can be exchanged to reverse the Encoders electrical rotation.

-


More info to follow.

--

Wednesday, February 8, 2012

Encoders for the Prop

The last day are so I have been trying to get a Chronodot Real Time Clock (RTC) to interface with my Propeller beacon. The idea is to have it schedule the WSPR Transmit Cycle, which are required to start on the even minute of hour, plus 2 seconds (ideally plus or minus 1 second). In PC environment a WSPR beacon uses the system clock for scheduling. With my Propeller Microprocessor, a PC is not connected, and therefore the processor has to do the scheduling. Which can be done, but without a RTC, adjustment to the micro's clock it done by hand - what a pain.

But sadly, I can not get the RTC to talk back to the Propeller. Maybe my Chronodot is dead, I have ordered a Bus Pirate to help with diagnostics. Much more work is needed.

While awaiting the Bus Pirate order, I decided to tackle the next GUI item that is needed  for the Propeller Beacon Controller. Which is, the two Rotary Encoder Service Routine.

On a  previous projects based on the Arduino Board, I had to resort to a complex state machine programming to achieve the multitasking that I desired. The Rotary Encoders were a part of the state machine, and were interrupt driven.

With the Propeller, there are NO interrupts !, . . but there are eight processor where one processor (or more) can be dedicated to the Encoder task. Originally I thought it would take one whole processor for each (Arduino style) Rotary Encoder task. But more reading through the manual and Spin Programming Syntax, I found a "waitpne" (wait for pins not equal to mask) statement. This statement allows all of the Rotary Encoder (and push button) pins to act as one big switch. All that is needed is to decode (poll) which pin was changed, and increment counter as necessary. This was a 100% self written program/driver Object, a fun project, I will post the code later.

Two Rotary Encoder Knobs
with Integrated Push Buttons

The first thing did with my new programmed function was to implement a RF Frequency Calibration Offset, I have several Propellers that I have been using, and keeping track of the CAL Offset for each Prop Board has been a problem. Now if it is not exactly on Frequency it is just a quick twist of the knob to correct it.

The second programming function I implemented; was a reset of the Epoch clock on a push of a button,  which starts the WSPR time cycle. Until I get the RTC working a little manual intervention is still necessary.

It's just more fun stuff with the Prop !

--

Sunday, October 9, 2011

Teensy Test Jig Schmatic

Here is the Teensy Test Jig Schematic that I am using for the programs shown in previous posts.

Teensy Test Jig
Teensy  Test Jig Schematic
Rev: F
Teensy Test Jig - In Fritzing Format
Note: The Rotary Ecoders are show as "Trimmer Pots" with two extra pins at the top for the push button switch, the encoder part is not available in the Fritzing Library, I may need to create a custom part. I have updated the Fritzing diagram above - I had to build a custom Fritzing Rotary Encoder part.

I had to re upload the schematic, the first and second were flawed.

Note: The Photo, Schematic, and the Fritzing view are NOT all "exactly" the same.

This is a "work-in-progress" project.

--

Saturday, October 8, 2011

ISR Lessons Learned

If you follow my Blog, (see previous posts) you will know that I have been playing with an Arduino work-a-like known as the Teensy. The plan is use it to help make my Homebrew Projects smarter and maybe more interesting.

So far, I have been "playing" with it to build multi-tasking template for future projects. The first hurtle was to write a predicable Rotary Encoder Test routine. Which I have done and published on the previous post. Actually, the code that I published has been replaced three time as I learn more. Finally I think the code is solid and would not mind others to commit or post reviews.

It was a struggle getting to this point. I have several chats with my friend Jeff - KO7M, about several major issues that were . . . just kicking my butt. The program just did not operate the way I had in mind. But in the end, it now works better than expected.

There were several lessens learned along the way, many are simple and probable known by heavy software types, but for me they were large stumbling blocks.

The lessons learned:
  • When writing software it is very important to have a friend to review your code. The process of expainning code bring new eyes on the problem.
  • Interrupt Service Routines (ISR) should be as short as possible, I instinctive knew this, but initially did not follow my own advice. It is just too easy to add one more line to the ISR. Note: only the second (PinB) needs to be read here as the other (PinA) is already known to be HIGH, as the RISING edge of the pulse is what created the Interrupt.

void doEncoder0() {    // Rotary Encoder
    digitalRead(encoder0PinB) ? g_encoder0Pos++ : g_encoder0Pos--;
    return;
}

  • The variable used by the ISR should be declared as global and type "volatile byte", as:

// Set Initial Encoder Values
volatile byte g_encoder0Pos = 0;

  • Jeff suggested the "g_" conventions for global variables.
  • The main program routine that takes advantage of the data from the ISR should read it with one machine instruction, so that the process can NOT be interrupted by yet another ISR event. For an 8 bit processor that means use an assign statement between two variables of type byte. In my case, the flag that indicates that the assignment was complete (ISR data extracted), was another byte assignment of value zero to the source variable. Once the data is assigned, it can be accumulated or used as necessary.

int doGetEncoderValue() {
    byte tmp;

   // Get Encoder input
   tmp = g_encoder0Pos;
   g_encoder0Pos = 0;

   . . . . 

}

  • Each of these two assignments are NOT interrupt able and therefore does not pose a problem if another ISR event occurs. Yes, there is a very-very narrow window where one event may be lost, but for my Rotary Encoder application that will not be a problem. If I had used; "int", "long", or  "float" data type multiple machine instructions would have been necessary to complete an assignment. And then, if an event occurred during the the assignment, there would have been a good chance the data would have be corrupt.

The methods used within the test program, have already been incorporated into another more complex multi-tasking program that I am working on.

The following is an excerpt from that program:

        byte btmp; int itmp;

        lcd.setCursor(0,0);
        lcd.print("Adj Backlight");
        // Get and Decode Rotary Encoder Data
        btmp = g_encoder0Pos;
        g_encoder0Pos = 0;
                
        if(btmp < 128) itmp = btmp;
        if(btmp > 127) itmp = (btmp - 256);
          
        level += itmp * 16;
        
        if(level < 0) level = 0; // Constrain
        if(level > 256)level = 256;

        lcd.setCursor(0,1);
        if(level<10) lcd.print("0");
        if(level<100) lcd.print("0");
        lcd.print(int(level));
        
        // Some Sound Feedback, just for fun
        if(itmp) {
            tone(speaker, 2000 + 2 * level, 10);
            analogWrite(lcd_BL_pin,min(max(level,4),255));
            g_HoldOffReset = true;
            state=1;
        }

It will be just more fun stuff.


--

Thursday, October 6, 2011

Decoding a Rotary Encoder - Cont'd

See previous post.

This Rotary Encoder Test Program is archived here, and available for review. Actually, I am using a Teensy 2.0 for this effort.

Skip this post, if you are not into Arduino Programming.

It works GREAT!

UPDATE: The previous shown program was replaced with the following, smaller, faster Interrupts Handlers:


/*
 Coded for Teensy 2.0, but with maybe some different pin
 assignments should work with all Arduinos

 This is my LCD and Rotary Encoder Test and Development Sketch Template
 By: Eldon R. Brown eldonb@ebcon.com
 Oct 7, 2011
 
 The circuit:
 * LCD RS pin to digital pin 12
 * LCD Enable pin to digital pin 11
 * LCD D4 pin to digital pin 0
 * LCD D5 pin to digital pin 1
 * LCD D6 pin to digital pin 2
 * LCD D7 pin to digital pin 3
 * LCD R/W pin to ground
 * LCD 15 pin BackLight, to 220 ohm, to digital pin 9
 *
 * Encoder pins as defined in below
 */

// include the library code:
#include <LiquidCrystal.h>

// Setup Pins for Encoders, and .0022uf Cap to ground
#define encoder0PinA  5    // PD0
#define encoder0PinB 15    // PB6
#define encoder0Switch 7   // PD2
//#define encoder1PinA  6    // PD1
//#define encoder1PinB 16    // PF7
//#define encoder1Switch 8   // PD3

#define lcd_BL_pin 9    // PC6 -  The BackLight

#define INT0 0
#define INT1 1
#define INT2 2
#define INT3 3


// Set Initial Encoder
volatile byte g_encoder0Pos = 0;
volatile byte g_encoder0SwStat = 0;
//volatile int encoder1Pos = 0;
//volatile int encoder1SwStat = 0;

// Initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 3, 2, 1, 0);


//////////////////////////////////////////////////////////////
// Interrupt Handler /////////////////////////////////////////
//////////////////////////////////////////////////////////////

void doEncoder0() {    // Rotary Encoder
    digitalRead(encoder0PinB) ? g_encoder0Pos++ : g_encoder0Pos--;
    return;
}

void doEncoder0Switch() {     // Button
    g_encoder0SwStat = digitalRead(encoder0Switch);  
    return;
}

//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
// Main Setup ////////////////////////////////////////////////
void setup() {

    // set up the LCD's number of columns and rows: 
    lcd.begin(16, 2);

    // Setup for Interrupts

    // Configure for Input
    pinMode(encoder0PinA, INPUT);    
    pinMode(encoder0PinB, INPUT);
    pinMode(encoder0Switch, INPUT);

    // Turn on pullup resistors, Pin must be in INPUT mode
    digitalWrite(encoder0PinA, HIGH);   
    digitalWrite(encoder0PinB, HIGH);
    digitalWrite(encoder0Switch, HIGH);

    // Set up for Interrupt
    // Only RISING is needed for Encoder    
    attachInterrupt(INT0, doEncoder0, RISING);
    attachInterrupt(INT2, doEncoder0Switch, CHANGE);
    interrupts();

    // Init LCD
    analogWrite(lcd_BL_pin, 128);
    // set up the LCD's number of columns and rows: 
    lcd.begin(16, 2);
    // Init Message
    lcd.print("--Initializing--");
    lcd.setCursor(0,1);
    lcd.print("   ebcon.com");
    delay(2000);
    lcd.clear();
    g_encoder0Pos = 0;

}

// Main Program Loop //////////////////////////////////////////
void loop() {
   
    doGetEncoderValue();
    //delay(100);
    //doBusyBox();
   
}

//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////
void doBusyBox() { // A Debug Function to make busy the processor
    delay(random(10,100));
    return;
} 

//////////////////////////////////////////////////////////////
int doGetEncoderValue() {
    volatile static int pos = 0;
    byte tmp;

    // Get Encoder input
    //noInterrupts();
    tmp = g_encoder0Pos;
    g_encoder0Pos = 0;
    //interrupts();
    
    if (tmp<128) pos += tmp;
    if (tmp>127) pos += tmp - 256;

    lcd.setCursor(0,0);
    // Print a message to the LCD.
    lcd.print("Pos= ");
    lcd.print(pos);
    lcd.print(" ");
    lcd.print(char(pos+65));
    lcd.print("   ");

    // Blink Cursor, for fun
    doBlinkCursor(15,1,!g_encoder0SwStat);
    return true;
}

////////////////////////////////////////////////////////////// 
void doBlinkCursor(char x,char y, char style) {
    switch (style) {
    case 1:        // Block Cursor
        lcd.setCursor(x,y);
        lcd.blink();
        delay(300);
        lcd.noBlink();
        break;
    default:        // Underline Cursor
        lcd.setCursor(x,y);
        lcd.cursor();
        delay(300);
        lcd.noCursor();
    }   
}

// End ////////////////////////////////////////////////////////


--

Monday, October 3, 2011

Decoding a Rotary Encoders - Cont'd

See previous posts.

Here is my Arduino Encoder Code for my archive, and others to review.

The input circuit is a pull up resistors and a cap to ground on each leg of the Encoder pins, the center Encoder pin is grounded. One pin is configured for interrupt while the other is configured for just input.

One interesting observation, there are two interrupts for each detent of the Encoder, rotating the knob very slowly will show an interrupt half way between the detents. The Encoder doc's do not imply this should be expected. Maybe I have the wrong doc's??

This interrupt handler works, it is my adaption of the published (original) code which is shown further below. I just futzed with the code to remove problems that I observed. I do not understand all that I know, maybe I just need more futzing.


///////////////////////////
// Interrupt Handlers
///////////////////////////
void doEncoder0() {          // This works !!! but not sure why??!!!
    static volatile byte _previous = 0;
    volatile byte _this = 0;

    volatile int junk = 2000, junk2; 
    while (junk--) junk2++;    // Delay for Debounce

    _this = digitalRead(encoder0PinA);

    if (_this != _previous) {
        _previous = _this;  
        if (_this == digitalRead(encoder0PinB)) {  
            encoder0Pos++;
        } 
        else {
            encoder0Pos--;
        }
    }
    return;
}


The following suggested Handler does NOT work for my simple Encoders.

void doEncoderX() {  // This should work but does NOT !! but not sure why??!!!
   
    if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB)) {
        encoder0Pos++;
    } 
    else {
        encoder0Pos--;
    }
    return;
}


The Setup is just three lines:

    pinMode(encoder0PinA, INPUT);    
    pinMode(encoder0PinB, INPUT);

    attachInterrupt(INT0, doEncoder0, CHANGE);




The Encoder Interrupts accumulate in "encoder0Pos", The shift (>>1) divides the value by two,  which is necessary as I want to count "detents" and there are two interrupts per detent. Setting "encoder0Pos" to zero, indicates all Encoder Interrupts have been accepted. Note: there is a very narrow window between these statements where a Interrupt could be lost, which is not big deal for my app.


 // Get Encoder input
 if (abs(encoder0Pos) > 5) chr += encoder0Pos; // For Accelerated Action
 chr += encoder0Pos>>1;
 encoder0Pos = 0;



Perhaps I need more desecrate component debounce, i.e., more capacitance on the input port.


--

Sunday, October 2, 2011

Decoding a Rotary Encoders

Decoding a Rotary Encoder is more difficult to use than I had thought. My test environment for the encoders is as described in the previous post.

A google search found several articles describing the problem, suggested solutions all seem to be less than optimum.

More coding, breadboarding, and research is necessary.

One problem may be that I am using less then optimum quality encoders, they were purchase on ebay for about $1.60 each. A quick search of Mouser Catalog lists many similar parts at the same price. Each have the same 20 (or so) pulses per revolution. Expensive (>$50) optical encoders typically have many more pulses per revolutions (>128) and therefore should provide more resolution. I don't think my projects require that much resolution (or expense).

I am going to check my car radio's volume and channel controls,  they seem to feel right and would be usable for most of my projects.

Of course, this is all necessary if the encoders are to be used in my future QRP projects. I am sure there is a workable solution, I just have not found it, yet!


UPDATE
My car radio Volume control appears to be an analog pot, with 32 indents over its range (about 300 degs). The Channel control appears to be an continuous rotating encoder with 16 detents per revelation, which is less than the 20 detents per revolution of encoders that I purchased.

-

Saturday, October 1, 2011

LC Display and Encoders Received

I received my ordered LC Displays and Encoder/Switches (see previous post).

Simple Development and
Test Configuration
It has been several years since I have programmed a PIC or other Micro Processor. The following is a quick build to provide a test circuit for getting back up to speed. The processor is the green circuit board on right side of photo. It is a Teensy 2.0 , which is a Arduino look/works-a-like.

The connection to the PC for programming and interaction is via the mini-USB. Power is supplied via the USB or external battery as shown here.

Add on software allows the Arduino Interactive Development Environmental (IDE) to work with the Teensy. In fact most Arduino Sketches (a C like program) run without alterations. I chose the Teensy because of it small size,  it is similar in size to my normal projects.

The Sketch that I have put together is to test and exercise of these parts. It is a simple multi-tasking Scheduler and State Machines. My goals is to develop and provide a simple template for my future multi-tasking Sketches. I plans to merge the Arduino or Teensy with my micro 9-Volt Transmitters (as previous posted) to create interesting more complex projects.

The two Rotary Encoders (center photo) are not completely hooked up yet. My plan is to use Interrupts to decode their input.

As seen by the photo, several tasks (4) provide sample text within several fields on the two line display and controls the LC BackLight Brightness. So far, the multi-tasking Scheduler and State Machines are working as planned.

I wrote several macros to implement Task Switching, Checkpointing, and Scheduling. I remember doing something similar in Assemble Code many years ago by counting clock cycles. These implementation in C was easy in comparison.

No, it is not a full featured multi-tasking Scheduler implementation, but it will do the multiple tasks/things that I require.


UPDATE
There appears to be an old and raging debate about the use and license of the Teensy and it's bootloader, see:

I do not know the current status of the concerns?

More research is necessary.

--

Friday, September 16, 2011

Project Parts

I found a couple of inexpensive parts on eBay, which will be used for a planed and future projects.

Links and photos are bookmarked here for later reference.

16 X 2  LCD 

2-Bit Gray Code Rotary Encoders
with Push Button

I hope the sellers do not mind as the Photos were copied here from their eBay web page.


UPDATE
I found a interesting Post on Arduino and Encoders, saved here for later use:

--