Loading...
  Show Posts
Pages: [1] 2 3 ... 6
1  Community / Exhibition / Gallery / Re: AdaEncoder: a library for use with simple quadrature encoders on: December 22, 2012, 12:35:57 am
Thanks, vasquo. Anyway, your post prompted me to create some test code, and I hope to use this to ensure the integrity of the library in the future... (this works for v. 0.7beta and above only, but the basic algorithm applies to 0.5 as well)

Code:
// Version 1.0: OO version

#include <ByteBuffer.h>
#include <digitalWriteFast.h>
#include <ooPinChangeInt.h> // necessary otherwise we get undefined reference errors.
#define DEBUG
#ifdef DEBUG
ByteBuffer printBuffer(200);
#endif
#define SWINTR_DEBUG // To debug using software interrupts on your two pins, define this.
                    // Then in your sketch, set your pins as outputs.  Initialize them as you
                    // desire, attach an interrupt, then the interrupt code will be called.
                    // CAUTION: Make sure you do NOT have any switches connected to those outputs,
                    // or you may end up frying your ATmega328!
#include <AdaEncoder.h>

/*
I like to keep this diagram around so I know which Arduino pin
is part of which port.
                  +-\/-+
            PC6  1|    |28  PC5 (AI 5)
      (D 0) PD0  2|    |27  PC4 (AI 4)
      (D 1) PD1  3|    |26  PC3 (AI 3)
      (D 2) PD2  4|    |25  PC2 (AI 2)
 PWM+ (D 3) PD3  5|    |24  PC1 (AI 1)
      (D 4) PD4  6|    |23  PC0 (AI 0)
            VCC  7|    |22  GND
            GND  8|    |21  AREF
            PB6  9|    |20  AVCC
            PB7 10|    |19  PB5 (D 13)
 PWM+ (D 5) PD5 11|    |18  PB4 (D 12)
 PWM+ (D 6) PD6 12|    |17  PB3 (D 11) PWM
      (D 7) PD7 13|    |16  PB2 (D 10) PWM
      (D 8) PB0 14|    |15  PB1 (D 9) PWM
                  +----+*/
// An encoder for every port
#define ENCD_a 2
#define ENCD_b 3
#define ENCC_a A3
#define ENCC_b A4
#define ENCB_a 9
#define ENCB_b 10

AdaEncoder encoderD = AdaEncoder('d', ENCD_a, ENCD_b);
AdaEncoder encoderC = AdaEncoder('c', ENCC_a, ENCC_b);
AdaEncoder encoderB = AdaEncoder('b', ENCB_a, ENCB_b);

int8_t clicks=0;
char id=0;

void setup()
{
  Serial.begin(115200); Serial.println("---------------------------------------");
  Serial.println("AdaEncoder test. Assumes detent position is 1,1 (pin a, pin b).");
  Serial.println("Encoder states: 11->01->00->10->11 (== CCW 1 click)");
  volatile uint8_t portmask0=PCMSK0;
  volatile uint8_t portmask1=PCMSK1;
  volatile uint8_t portmask2=PCMSK2;
  PCMSK0=0; PCMSK1=0; PCMSK2=0; // turn off PinChange interrupts for a minnit
  pinMode(ENCB_a, OUTPUT); pinMode(ENCB_b, OUTPUT);
  digitalWriteFast2(ENCB_a, HIGH); digitalWriteFast2(ENCB_b, HIGH);
  pinMode(ENCC_a, OUTPUT); pinMode(ENCC_b, OUTPUT);
  digitalWriteFast2(ENCC_a, HIGH); digitalWriteFast2(ENCC_b, HIGH);
  pinMode(ENCD_a, OUTPUT); pinMode(ENCD_b, OUTPUT);
  digitalWriteFast2(ENCD_a, HIGH); digitalWriteFast2(ENCD_b, HIGH);
  PCMSK0=portmask0; // interrupts back on.
  PCMSK1=portmask1;
  PCMSK2=portmask2;
}

void changeEncoder(uint8_t pin) {
  if (digitalReadFast2(pin)) { digitalWriteFast2(pin, LOW); }
  else digitalWriteFast2(pin, HIGH);
}

void printEncoderState(uint8_t pina, uint8_t pinb) {
  Serial.print(digitalReadFast2(pina), DEC);
  Serial.println(digitalReadFast2(pinb), DEC);
}

void loop()
{
  char inChar, outChar;
  // 00 -> 10 -> 11 -> 01 -> 00
  if(Serial.available()) {
    inChar=(Serial.read()); // get command from serial input
    switch (inChar) {
    case '1':
        Serial.print("B-a "); changeEncoder(ENCB_a); printEncoderState(ENCB_a, ENCB_b);
      break;
    case '2':
        Serial.print("B-b "); changeEncoder(ENCB_b); printEncoderState(ENCB_a, ENCB_b);
      break;
    case '5':
        Serial.print("C-a "); changeEncoder(ENCC_a); printEncoderState(ENCC_a, ENCC_b);
      break;
    case '6':
        Serial.print("C-b "); changeEncoder(ENCC_b); printEncoderState(ENCC_a, ENCC_b);
      break;
      case '9':
        Serial.print("D-a "); changeEncoder(ENCD_a); printEncoderState(ENCD_a, ENCD_b);
      break;
      case '0':
        Serial.print("D-b "); changeEncoder(ENCD_b); printEncoderState(ENCD_a, ENCD_b);
      break;
    }
  }
  while ((outChar=(char)printBuffer.get()) != 0) Serial.print(outChar);
  AdaEncoder *thisEncoder=NULL;
  thisEncoder=AdaEncoder::genie();
  if (thisEncoder != NULL) {
    Serial.print(thisEncoder->getID()); Serial.print(':');
    clicks=thisEncoder->query();
    if (clicks != 0) { Serial.print("Clicks: "); Serial.print(clicks, DEC); }
    if (clicks > 0) {
      Serial.println(" CW");
    }
    if (clicks < 0) {
       Serial.println(" CCW");
    }
  }
}

...If I then hit a sequence of numbers in the Monitor screen, I can then demonstrate that the library behaves as expected.

For example, if I hit the following sequence, I simulate switch bounce on the two pins and it registers "CCW" exactly when I expect it:
Code:
Number Output
Typed
1 B-a 01
1 B-a 11
1 B-a 01
1 B-a 11
1 B-a 01
2 B-b 00
2 B-b 01
2 B-b 00
2 B-b 01
2 B-b 00
1 B-a 10
1 B-a 00
1 B-a 10
1 B-a 00
1 B-a 10
2 B-b 11
  b:Clicks: -1 CCW
2  Community / Exhibition / Gallery / Re: AdaEncoder: a library for use with simple quadrature encoders on: December 22, 2012, 12:08:30 am
Well, I tried using the 0.7beta.  ... But I can't get it to work properly. ... I'm going back to 0.5.

---
I do have a few questions on the sample MyEncoder in 0.7beta

On Line# 15, we have this code
AdaEncoder encoderA = AdaEncoder('a', ENCA_a, ENCA_b);

but nowhere else in the program is "encoderA" used. What's the purpose of line#15?

Then on line#30, I see this
AdaEncoder *thisEncoder=NULL;

*thisEncoder is used in the loop() program.  But how does Arduino know how *thisEncoder is setup?  We defined encoder pins 2 and 3 for "encoderA", not "thisEncoder".

I'm sorry it didn't work out for you. It is a little more Object-Oriented like, so it's not everyone's cup of tea.

Also I'm sorry I am late in replying. This thread is not intended for support; there is a support link on the code.google.com page for that. Anyway, I will answer your questions for posterity:

The code is an example that shows you how to set up 2 encoders, A and B. encoderA and encoderB are designed to encapsulate all the important data within, so you can think about "encoders" rather than the pins of your Arduino. It's a conceptual change over version 0.5 of the library, really.

In the old days, then, of version 0.5, the genie() method would fill in the values for "clicks" and "id" for you, so you could then query the variables and ask, "Who turned?  How far?".

Now in version 0.7, the genie() method returns a pointer to an AdaEncoder object. The "thisEncoder" variable is set to NULL on every iteration of loop. Then, if genie() finds a changed encoder, it will send back the pointer to the AdaEncoder object, and now you can do whatever you want with this encoder that you've found. It sends back either encoderA or encoderB- whichever one was turned. "thisEncoder", then, changes its identity to that of either A or B. I have done the simplest thing, which is to have it query itself and return the number of times it was clicked.

By using Object Oriented techniques, you are not bound to if/then statements based on values (like id numbers) of the encoders. Each encoder object intrinsically knows who it is; you can even subclass the encoder and have your subclass do special things simply based on its identity.

On this small of a scale it may be difficult to see any kind of win at all, that I'll grant. But I think it's interesting, and I like the way Object Oriented programming makes me adjust my way of thinking, so I have decided to go that route. Thankfully the AdaEncoder library is simple enough that, in the last year, I haven't moved from the functionality I created in version 0.5. The old one should continue to be useful to people.  Although I do recommend an upgrade of the underlying PinChangeInt library.


3  Community / Exhibition / Gallery / Re: AdaEncoder: a library for use with simple quadrature encoders on: December 21, 2012, 11:46:06 pm
I've encountered a bug with the AdaEncoder (old version).

So you power up, turn the encoder CCW... the counter decrement. All is good.

You stop turning, and then turn one click CW... the counter instead of incrementing as expected, instead decrements! 
Turn it again by another click CW, then the counter increments correctly.
...
Does this update fix this bug? 
 

I don't think the update fixes that bug- not directly anyways. The bug actually came from the PinChangeInt library, which was not properly testing the state of the pins when interrupts came in quickly. If you have bouncing switches (yes- if you have a mechanical encoder, then you do have bouncing switches), then quite likely you had some issues.  I recommend you continue to use AdaEncoder 0.5 but update PinChangeInt to 2.19beta or greater.
4  Community / Exhibition / Gallery / Re: AdaEncoder: a library for use with simple quadrature encoders on: December 04, 2012, 10:29:04 pm
Announcing version 0.7beta of the AdaEncoder library. This release incorporates some bugfixes in the ooPinChangeInt library (which it uses).

I have moved the library to using the ooPinChangeInt library because, well, I like OO programming. So using the AdaEncoder library is now a bit different. Check the included "MyEncoder" sketch for an example. Most significantly, instead of calling genie() with reference arguments whose values are filled in, you now call genie() and an AdaEncoder object is returned. Then you query the object for details.

The older AdaEncoder 0.5 will continue to work with the latest PinChangeInt library, which repaired some bugs in the PinChangeInt library itself and makes AdaEncoder more reliable. If you don't like the OO-ness of this release, continue to use 0.5 but make sure to upgrade your PinChangeInt library to 2.19beta or above!
 
From the Release Notes:
Now works with the latest ooPinChangeInt. Should be much more reliable.

Bugfixes: the clicks variable was supposed to be int8_t, but I had query() and getClicks() returning uint8_t types.

Updated to utilize the ByteBuffer to fill a buffer of text for debug purposes.

turnOffPWM has been moved into its own file, as that code is distributed under the LPGL license. I use the GPL.
5  Community / Exhibition / Gallery / Re: ooPinChangeInt library released: Pin Change Interrupts, object style on: November 30, 2012, 06:07:34 pm
Yeahhh... next version, I guess. We'll see how complicated it gets to do.
6  Community / Exhibition / Gallery / Re: PinChangeInt library- To attach interrupts to multiple Arduino (Uno/Due) pins on: November 29, 2012, 07:14:07 pm
Version 2.19beta has been released. The library now includes Sanguino support. There have been a number of recent bugfixes, so be sure to look at the Release Notes. If you have any library prior to version 2.17beta, it is highly recommended that you upgrade to 2.17beta or later.
7  Community / Exhibition / Gallery / Version 1.03beta is now out. Adds MEGA support; includes significant bug fixes. on: November 29, 2012, 01:05:24 am
Announcing the release of version 1.03 of the ooPinChangeInt library. From the release notes:

Added MEGA support, as per PinChangeInt. This support has not been tested in this library; if you do use it with the MEGA please let me know. See the Google Code site for more information. Thanks.

Modified to not delete() pins that get detachInterrupt()'ed. Now when you detachInterrupt(), the PORT just disables interrupts for that pin; the PCintPin object remains in memory and in the linked list of pins (possibly slowing down your interrupts a couple of micros).  You can reenable a detached interrupt- but you must do it within the PinChangeInt library (would anyone ever enable an interrupt on a pin, then disable it, then have need to reenable it but not using the library?).

Optimized the interrupt handler with code from robtillaart to take out the checks for changed pins from the while() loop that steps through the pins.

Other bugfixes that follow the PinChangeInt-2.17beta release:

1. PCintPort::curr bug. In the interrupt handler PCint(), we loop as long as PCIFR indicates a new interrupt wants to be triggered, provided DISABLE_PCINT_MULTI_SERVICE is not defined (it is not by default).  Fixed so that if a pin causes the PCIFR to change, we reread the port and look at how it is now.
2. attachInterrupt() now returns an int8_t value:  1 on successful attach, 0 on successful attach but using an already-enabled pin, and -1 if the new() operator failed to create a PCintPin object.
3. Created the enable() method.
8  Community / Exhibition / Gallery / Re: PinChangeInt library- To attach interrupts to multiple Arduino (Uno/Due) pins on: November 20, 2012, 12:30:52 pm
Code:
                } while (tmp != NULL);
This won't work because tmp will become null, which will exit us from the loop.

I need tmp->next later in the code, therefore I need the last non-null value of tmp.
9  Community / Exhibition / Gallery / Re: PinChangeInt library- To attach interrupts to multiple Arduino (Uno/Due) pins on: November 20, 2012, 08:59:27 am
Thanks for the input. This saved 12 bytes; I haven't measured the speed:
Code:
    if (firstPin != NULL) {
        tmp=firstPin;
        do {
            if (tmp->arduinoPin == arduinoPin) { enable(tmp, userFunc, mode); return(0); }
            if (tmp->next == NULL) break;
            tmp=tmp->next;
        } while (true);
    }

I have also converted the goto to a do/while/break situation. No change to interrupt speed, but I guess it looks better :-).
10  Community / Exhibition / Gallery / Re: PinChangeInt library- To attach interrupts to multiple Arduino (Uno/Due) pins on: November 17, 2012, 02:26:31 am
Version 2.15beta is out now, and I allow for attaching a new user function. I don't know if anyone would really do that, but there should be no nasty surprises in the code, and attachInterrupt() will thus do exactly what you tell it to do in the argument list.

URL: http://code.google.com/p/arduino-pinchangeint/
11  Community / Exhibition / Gallery / Re: PinChangeInt library- To attach interrupts to multiple Arduino (Uno/Due) pins on: November 16, 2012, 10:46:09 pm
I already thought of a bug in v. 2.13beta.  If you detach an interrupt and attach it again, and give it a new user function, it will not attach the new user function- it will use the first user function that you attached.

However you are able to change the mode at this point (RISING, FALLING, or CHANGE).
12  Community / Exhibition / Gallery / Re: PinChangeInt library- To attach interrupts to multiple Arduino (Uno/Due) pins on: November 16, 2012, 10:36:14 pm
BTW, I am still testing this release but I wanted it to get out there because I uncovered some nasty bugs. They made my interrupts unreliable! So it's important if you use this code, to realize that the new version should work better.

I realized that about 5-10% of my interrupts were missed due to my switch bounce; some of the bounces happen only 50 microseconds apart or so. And my code was not processing them properly. With this code, I notice that my switches are more reliable.

I also notice that I am not getting the speed bump that I hoped to get, and some of my bugfixes actually introduce a few extra microseconds of processing. This is disappointing and I hope to find them if I can.
13  Community / Exhibition / Gallery / Version 2.13beta is now out. ...Important and major bugfix release! on: November 16, 2012, 10:32:31 pm
******************************************************************************

        PinChangeInt
        ---- RELEASE NOTES ---

******************************************************************************
Version 2.13 (beta) Mon Nov 12 09:33:06 CST 2012
SIGNIFICANT BUGFIX release! Significant changes:
1. PCintPort::curr bug. Interrupts that occur rapidly will likely not get serviced properly by PCint().
2. PCint() interrupt handler optimization.
3. PCIFR port bit set bug fix.
4. Many static variables added for debugging; used only when #define PINMODE is on.
5. detachInterrupt() no longer does a delete(), since that wasn't working anyway. When you detachInterrupt(), the PORT just disables interrupts for that pin; the PCintPin object remains in memory and in the linked list of pins (possibly slowing down your interrupts a couple of micros).  You can reenable a detached interrupt- but you must do it within the PinChangeInt library (would anyone ever enable an interrupt on a pin, then disable it, then have need to reenable it but not using the library?).
6. attachInterrupt() now returns a uint8_t value:  1 on successful attach, 0 on successful attach but using an already-enabled pin, and -1 if the new() operator failed to create a PCintPin object.
Also, modified these release notes.

Details:

Uncovered a nasty bug, thanks to robtillaart on the Arduino Forums and Andre' Franken who posted to the PinChangeInt groups. This bug was introduced by me when I assigned PCintPort::curr early in the interrupt handler:
Code:
ISR(PCINT0_vect) {
    PCintPort::curr = portB.portInputReg;
    portB.PCint();
}
Later, in the interrupt handler PCint(), we loop as long as PCIFR indicates a new interrupt wants to be triggered, provided DISABLE_PCINT_MULTI_SERVICE is not defined (it is not by default):
Code:
#ifndef DISABLE_PCINT_MULTI_SERVICE
        pcifr = PCIFR & PCICRbit;
        PCIFR = pcifr;  // clear the interrupt if we will process it (no effect if bit is zero)
} while(pcifr);
#endif
...Well. Problem is, if a pin pops up and causes the PCIFR to change, we have to reread the port and look at how it is now! I wasn't doing that before, so if a new interrupt appeared while I was still servicing the old one, odd behavior would take place. For example, an interrupt would register but then the userFunc would not be called upon to service it.  The code needs to be:
Code:
        pcifr = PCIFR & PCICRbit;
        PCIFR = pcifr;  // clear the interrupt if we will process it (no effect if bit is zero)
        PCintPort::curr=portInputReg; // ...Fixed in 2.11beta.
} while(pcifr);

Also, made the interrupt handler even faster with an optimization from robtillaart to take out the checks for changed pins from the while() loop that steps through the pins:
Code:
uint8_t changedPins = (PCintPort::curr ^ lastPinView) &
                      ((portRisingPins & PCintPort::curr ) | ( portFallingPins & ~PCintPort::curr ));
...This speedup is offset by more changes in the PCint() handler, which now looks like the following; there are two bug fixes:
----------------------------
FIX 1: sbi(PCIFR, PCICRbit);
FIX 2: ...the aforementioned PCintPort::curr=portInputReg;
Here's the new code:
----------------------------
Code:
    #ifndef DISABLE_PCINT_MULTI_SERVICE
        pcifr = PCIFR & PCICRbit;
        if (pcifr) {
        //if (PCIFR & PCICRbit) { // believe it or not, this adds .6 micros
            sbi(PCIFR, PCICRbit); // This was a BUG: PCIFR = pcifr  ...And here is the fix.
            #ifdef PINMODE
            PCintPort::pcint_multi++;
            if (PCIFR & PCICRbit) PCintPort::PCIFRbug=1; // PCIFR & PCICRbit should ALWAYS be 0 here!
            #endif
            PCintPort::curr=portInputReg; // ...Fixed in 2.11beta.
            goto loop;  // A goto!!! Don't want to look at the portInputReg gratuitously, so the while() will not do.
        }
    #endif

Also I added a lot of variables for debugging when PINMODE is defined, for routing out nasty bugs. I may need them in the future... :-(

Finally, I am not putting newlines in this commentary so I can make it easier to paste online.
14  Products / Arduino Due / Re: Purchase the SAM3X8E? on: November 14, 2012, 09:44:52 am

...Octopart, you let me down!  Thanks, CrossRoads. I guess checking the major distributors was a little too obvious for me :-)
15  Community / Exhibition / Gallery / Re: PinChangeInt library- To attach interrupts to multiple Arduino (Uno/Due) pins on: November 14, 2012, 12:57:02 am
I assume its a memory leak as my understanding is that delete does not free memory in Arduino and the current implementation of detach seems to assume that delete works.
Yes, I ran a test and noticed that my memory got gobbled up pretty quickly. Drat!!! ...The problem is not merely in delete(), because delete just calls free().  The problem is that free() doesn't seem to work.

Anyway, regardless of that I like your idea of having detachInterrupt() simply disable the pin from interrupting, rather than using the delete() operator. I am working on it doing just that.

attachInterrupt() will return -1 on error, 0 if the pin has already been added to the list (the pin will be enabled if it is called and had previously been created already), and 1 if a new pin is created.
Pages: [1] 2 3 ... 6