Show Posts
|
|
Pages: 1 2 [3] 4 5 6
|
|
31
|
Community / Exhibition / Gallery / Re: PinChangeInt library- To attach interrupts to multiple Arduino (Uno/Due) pins
|
on: March 10, 2012, 02:39:25 pm
|
|
Version 1.71beta is now available for upload. From the Release Notes:
Code reordering: Starting in version 1.3 of this library, I put the code that enables interrupts for the given pin, and the code that enables Pin Change Interrupts, ahead of actually setting the user's function for the pin. Thus in the small interval between turning on the interrupts and actually creating a valid link to an interrupt handler, it is possible to get an interrupt. At that point the value of the pointer is 0, so this means that the Arduino will start over again from memory location 0- just as if you'd pressed the reset button. Oops!
I corrected it so the code now operates in the proper order. (EDITORIAL NOTE: If you want to really learn something, teach it!)
Minor code clean-up: All references to PCintPort::curr are now explicit. This changes the compiled hex code not one whit. I just sleep better at night.
Numbering: Changed the numbering scheme. Beta versions will end with an odd number in the hundredths place- because they may be odd- and continue to be marked "beta". I'll just sleep better at night. :-)
|
|
|
|
|
32
|
Community / Exhibition / Gallery / AdaEncoder: a library for use with simple quadrature encoders
|
on: March 01, 2012, 12:11:16 am
|
Announcing the release of Version 0.5 of the AdaEncoder library. This library was intended to make it easy for you to use 1 or more rotary encoders as found at Sparkfun or Adafruit on your Arduino. Don't worry about debouncing, the library's functions take care of that for you. This library interfaces with 2-pin encoders (2 pins A and B, then a common pin C). It does not indicate every state change, rather, it reports only when the decoder is turned from one detent position to the next. It is interrupt-driven and designed to be fast and easy to use. The interrupt routine is lightweight, and the programmer is then able to read the direction the encoder turned at their leisure (within reason; what's important is that the library is reasonably forgiving). The library is designed to be easy to use (it bears repeating :-) ) and it is reasonably immune to switch bounce. The library uses tight code (no digitalRead() or digitalWrite()) and in use requires only two methods. The amount of code in the interrupt routines is kept small. The library can be found at http://code.google.com/p/adaencoder/The library requires the PinChangeInt library, version 1.70beta or newer, found at http://code.google.com/p/arduino-pinchangeint/
|
|
|
|
|
33
|
Community / Exhibition / Gallery / Re: PinChangeInt library- To attach interrupts to multiple Arduino (Uno/Due) pins
|
on: February 28, 2012, 08:33:39 am
|
Version 1.70beta is out as of yesterday. Includes the following enhancements; from the Release Notes:
Happy Birthday to me! Happy Birthday tooooo meee! Happy Birthday, Dear Meeeeee-eeeee! Happy Birthday to me! Yes, it is on this auspicious occasion of mine (and Elizabeth Taylor's [R.I.P.]) birthday that I humbly submit to you, gracious Arduino PinChangeInt user, version 1.70beta of the PinChangeInt library. I hope you enjoy it. New in this release: The PinChangeIntTest sketch was created, which can be found in the Examples directory. It exercises:
- Two interrupting pins, one on each of the Arduino's PORTs.
- detachInterrupt().
Hopefully this will help avoid the embarrassing bugs that I have heretofore missed. [/list] As well, it has come to this author's (GreyGnome) attention that the Serial class in Arduino 1.0 uses an interrupt that, if you attempt to print from an interrupt (which is what I was doing in my tests) can easily lock up the Arduino. So I have taken SigurðurOrn's excellent ByteBuffer library and modified it for my own nefarious purposes. (see http://siggiorn.com/?p=460). The zipfile comes complete with the ByteBuffer library; see the ByteBuffer/ByteBuffer.h file for a list of changes, and see the PinChangeIntTest sketch for a usage scenario. Now the (interrupt-less and) relatively fast operation of filling a circular buffer is used in the interrupt routines. The buffer is then printed from loop(). The library has been modified so it can be used in other libraries, such as my AdaEncoder library ( http://code.google.com/p/adaencoder/). When #include'd by another library you should #define the LIBCALL_PINCHANGEINT macro. For example: #ifndef PinChangeInt_h #define LIBCALL_PINCHANGEINT #include "../PinChangeInt/PinChangeInt.h" #endif
This is necessary because the IDE compiles both your sketch and the .cpp file of your library, and the .h file is included in both places. But since the .h file actually contains the code, any variable or function definitions would occur twice and cause compilation errors- unless #ifdef'ed out.
|
|
|
|
|
34
|
Community / Exhibition / Gallery / Re: PinChangeInt library- To attach interrupts to multiple Arduino (Uno/Due) pins
|
on: February 18, 2012, 01:37:50 am
|
I created another bug :-(. Version 1.6 is out; includes a couple of enhancements. The changes are these: Set the value of the current register settings, first thing in each ISR; e.g., ISR(PCINT0_vect) { PCintPort::curr = portB.portInputReg; // version 1.6 ... ...instead of at the beginning of the PCintPort::PCint() static method. This means that the port is read closer to the beginning of the interrupt, and may be slightly more accurate- only by a couple of microseconds, really, but it's a cheap win.
Fixed a bug- a BUG!- in the attachInterrupt() and detachInterrupt() methods. I didn't have breaks in my switch statements! Augh! What am I, a (UNIX) shell programmer? ...Uh, generally, yes...
Added the PINMODE define and the PCintPort::pinmode variable. See the Usage Wiki page on the website for more info.
|
|
|
|
|
36
|
Using Arduino / Programming Questions / Serial.print() and interference with other interrupts
|
on: February 16, 2012, 01:36:50 am
|
Hi, I am testing some code that implements pin change interrupts, and as I do so I notice that what worked in Arduino-022 no longer works in Arduino 1.0. That is, my interrupt code contains a lot of Serial.print() and println() statements. At some random point fairly early in my tests, the Arduino seems to lock up. This means that the Serial data that I had been sending to analyze my interrupt code is no longer being sent, and the flashing of LED pin 13 no longer takes place, either. It is as if the CPU is deadlocked, it seems to me. What sort of interference can I expect with interrupt code and the Serial.print() method in Arduino 1.0 that was not there in Arduino-022? I don't understand why it broke. Below is my code, which uses the PinChangeInt library. Thanks. #define PINMODE #define FLASH #include <PinChangeInt.h>
// This example demonstrates a configuration of 3 interrupting pins and 2 interrupt functions. // All interrupts are serviced immediately, but one of the pins (pin 4) will show you immediately // on the Terminal. The other function connected to 2 pins sets an array member that is queried in loop(). // You can then query the array at your leisure. // This makes loop timing non-critical.
// Pins. We want to use pins from ports B, C and D. So choose wisely. Ports are show in // this diagram of the ATmega328P chip. PD0 shows "Port D, pin 0". PC3 shows "Port C, Pin 3", // PB2 shows "Port B, pin 2" and so on. The corresponding Arduino pins are in parentheses. // So PB2 is Arduino pin D 10, for example. /* +-\/-+ 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 +----+ */ // For the Analog Input pins used as digital input pins, and you can use 14, 15, 16, etc. // or you can use A0, A1, A2, etc. (the Arduino code comes with #define's // for the Analog Input pins and will properly recognize e.g., pinMode(A0, INPUT); #define PIN1 2 // port D #define PIN2 3 #define PIN3 11 // Port B #define PIN4 12 #define PIN5 A3 // Port C, also can be given as "17" #define PIN6 A4 // starts and stops the count
uint8_t pins[6]={ PIN1, PIN2, PIN3, PIN4, PIN5, PIN6 }; uint8_t ports[6]={ 0, 0, 0, 0, 0, 0 };
uint8_t latest_interrupted_pin; uint8_t interrupt_count[20]={0}; // 20 possible arduino pins uint8_t port; uint8_t mode; boolean start=0;
void showMode() { switch (mode) { case FALLING: Serial.print("*F*"); break; case RISING: Serial.print("-R-"); break; case CHANGE: Serial.print(" C "); break; } }
void quicfunc0() { latest_interrupted_pin=PCintPort::arduinoPin; mode=PCintPort::pinmode; showMode(); if (start==1) { interrupt_count[latest_interrupted_pin]++; } Serial.print("0p"); Serial.print(latest_interrupted_pin, DEC); Serial.print("-P"); Serial.println(digitalPinToPort(latest_interrupted_pin), DEC); if (start !=1) Serial.println("...off, not counted."); };
void quicfunc1() { latest_interrupted_pin=PCintPort::arduinoPin; mode=PCintPort::pinmode; showMode(); if (start==1) { interrupt_count[latest_interrupted_pin]++; } Serial.print("1p"); Serial.print(latest_interrupted_pin, DEC); Serial.print("-P"); Serial.println(digitalPinToPort(latest_interrupted_pin), DEC); if (start !=1) Serial.println("...off, not counted."); };
void quicfunc2() { latest_interrupted_pin=PCintPort::arduinoPin; mode=PCintPort::pinmode; showMode(); if (start == 1) { Serial.println("STOP! ...not counted."); start=0; } else { start=1; interrupt_count[latest_interrupted_pin]++; Serial.print("START! 2p"); Serial.print(latest_interrupted_pin, DEC); Serial.print("-P"); Serial.println(digitalPinToPort(latest_interrupted_pin), DEC); } };
uint8_t i; void setup() { for (i=0; i < 7; i++) { pinMode(pins[i], INPUT); digitalWrite(pins[i], HIGH); ports[i]=digitalPinToPort(pins[i]); switch (pins[i]) { case PIN1: case PIN3: PCintPort::attachInterrupt(pins[i], &quicfunc0, FALLING); break; case PIN2: case PIN4: PCintPort::attachInterrupt(pins[i], &quicfunc0, RISING); break; case PIN5: PCintPort::attachInterrupt(pins[i], &quicfunc1, CHANGE); break; case PIN6: PCintPort::attachInterrupt(pins[i], &quicfunc2, FALLING); break; } } Serial.begin(115200); Serial.println("---------------------------------------"); }
void loop() { uint8_t count; Serial.print("."); delay(1000); for (i=0; i < 20; i++) { if (interrupt_count[i] != 0) { count=interrupt_count[i]; interrupt_count[i]=0; Serial.print("Count for pin "); if (i < 14) { Serial.print("D"); Serial.print(i, DEC); } else { Serial.print("A"); Serial.print(i-14, DEC); } Serial.print(" is "); Serial.println(count, DEC); } } }
|
|
|
|
|
37
|
Using Arduino / Programming Questions / Re: Mixing C with Assembler: Assembly code before push instructions in a function
|
on: February 10, 2012, 12:49:41 am
|
Well in my test, the generated code ... Only had this before saving the port: ISR (PCINT0_vect) 100: 1f 92 push r1 102: 0f 92 push r0 104: 0f b6 in r0, 0x3f ; 63 106: 0f 92 push r0 108: 11 24 eor r1, r1 10a: 8f 93 push r24 { savedPort = PINB; 10c: 83 b1 in r24, 0x03 ; 3 10e: 80 93 00 01 sts 0x0100, r24
Which isn't much worse than you can do with assembler. You have to save the status register, and before you do that you have to save R0. In this small case I would agree. As a matter of fact, it's exactly what I would do with assembler. Interestingly, I have created a silly ISR that calls a function with a large number of varargs: 45 arguments, to be exact. The ISR created this set of push instructions: ISR(PCINT2_vect) { 12c: 1f 92 push r1 12e: 0f 92 push r0 130: 0f b6 in r0, 0x3f ; 63 132: 0f 92 push r0 134: 11 24 eor r1, r1 136: 2f 92 push r2 138: 3f 92 push r3 13a: 4f 92 push r4 13c: 5f 92 push r5 13e: 6f 92 push r6 140: 7f 92 push r7 142: 8f 92 push r8 144: 9f 92 push r9 146: af 92 push r10 148: bf 92 push r11 14a: cf 92 push r12 14c: df 92 push r13 14e: ef 92 push r14 150: ff 92 push r15 152: 0f 93 push r16 154: 1f 93 push r17 156: 2f 93 push r18 158: 3f 93 push r19 15a: 4f 93 push r20 15c: 5f 93 push r21 15e: 6f 93 push r22 160: 7f 93 push r23 162: 8f 93 push r24 164: 9f 93 push r25 166: af 93 push r26 168: bf 93 push r27 16a: ef 93 push r30 16c: ff 93 push r31 16e: df 93 push r29 170: cf 93 push r28
Now I caution you about getting too carried away about shaving nanoseconds off ISRs. ...
So what I have learned, and what I suspected from everybody's dire warnings and general uncomfortableness with my idea, is that the compiler will create a variable number of push instructions, up to and including pushing the entire set of registers. And, even though the only thing my ISR does is call another function, some of the registers used in the function are not pushed in the function. The registers are pushed by the ISR. Thus, trying to outsmart the compiler writers is probably a bad idea. If I wanted to do the compiler's work and create a "naked" ISR, I would by necessity need to save all the registers, all the time. So my little experiment puts the final smackdown on my idea. Not to mention, again, everyone's general uncomfortableness with my plan. I think the only time I would use ISR_NAKED is if I was writing a hand-assembled ISR and I knew the static set of registers that were going to be utilized. Thanks everybody for taking your time. I have learned a lot, believe it or not. I am going to write my humble little ISR like this: ISR(PCINT0_vect) { PCintPort::curr = portB.portInputReg; portB.PCint(); } ...Saving the input register's state into the PCintPort::curr static class variable as soon as is prudently possible. I'll leave the assembly code for the super low-level guys.
|
|
|
|
|
38
|
Using Arduino / Programming Questions / Re: Mixing C with Assembler: Assembly code before push instructions in a function
|
on: February 08, 2012, 11:47:26 pm
|
I am calling a function from an interrupt, and due to timing issues I would like to preserve the state of a register very quickly when the interrupt takes place.
Why do you want to do this?
In order to grab and make available to the user of my library the state of the pin at interrupt. The further the check of the pin state, the less likely (under certain circumstances, such as switch bounce) the port read is going to actually reflect the state of the port at the time of the interrupt. Also, I don't know if you can do much better than the compiler. For example: ISR (SPI_STC_vect) { // do nothing } Generates: ...(deleted for brevity)... I noticed that it's pretty lean in the case of an empty ISR. In my circumstances, since I'm calling a method from inside the ISR, the preamble looks more like this: ISR(PCINT1_vect) { 442: 1f 92 push r1 444: 0f 92 push r0 446: 0f b6 in r0, 0x3f ; 63 448: 0f 92 push r0 44a: 11 24 eor r1, r1 44c: 2f 93 push r18 44e: 3f 93 push r19 450: 4f 93 push r20 452: 5f 93 push r21 454: 6f 93 push r22 456: 7f 93 push r23 458: 8f 93 push r24 45a: 9f 93 push r25 45c: af 93 push r26 45e: bf 93 push r27 460: ef 93 push r30 462: ff 93 push r31
I am thinking of actually making the ISR "naked", duplicating that bit of code, but inserting my little bit of work at the beginning of all of that.
|
|
|
|
|
39
|
Using Arduino / Programming Questions / Re: Mixing C with Assembler: Assembly code before push instructions in a function
|
on: February 08, 2012, 11:39:49 pm
|
but doesn't the compiler push all registers it's concerned with? Yes. But, there is an assumption that whoever called the ISR preserved certain registers. That's the purpose of all those pushes; to do what a caller would have done if the ISR had been called like a normal function. Got it. And I see that, for example, if the ISR calls a C++ method, eg: 4a0: 83 e7 ldi r24, 0x73 ; 115 4a2: 91 e0 ldi r25, 0x01 ; 1 4a4: 0e 94 8f 01 call 0x31e ; 0x31e <_ZN9PCintPort5PCintEv>
...it uses r24 and r25. There's no telling which ones may be used in circumstances like that, so if the ISR doesn't push/pop the registers, things could get quite sticky quite quickly.
|
|
|
|
|
41
|
Using Arduino / Programming Questions / Re: Mixing C with Assembler: Assembly code before push instructions in a function
|
on: February 08, 2012, 10:22:25 am
|
I have my doubts that this is a good idea. ... Also, you've entered the realm where the required C code options are more mysterious than straight assembler would have been...
Yes, I'm reconsidering my original plan. I think what I'll do is just read my register, first thing upon entering my ISR, and then call the function that I need. Rather than reading the register inside the function. Thus, there is only one set of push instructions in front of reading the register, instead of two.
|
|
|
|
|
42
|
Using Arduino / Programming Questions / Re: How to give a C++ variable name to an asm statement?
|
on: February 08, 2012, 10:18:25 am
|
asm volatile("sts %0, r0" : "+M" (PCintPort::curr) );
If you aren't sure of the syntax of the assembler, I suggest you disassemble a bit of C++ that includes a reference to the type of variable you're after, and see what you get. I did that. I got "0x0160" which is the memory location of the variable. Of course, that will change as soon as I change my sketch... :-( So I need to refer to it symbolically in my asm statement, which indeed I'm unaware of the syntax. Google searching proved fruitless to me.
|
|
|
|
|
43
|
Using Arduino / Programming Questions / How to give a C++ variable name to an asm statement?
|
on: February 07, 2012, 07:45:05 pm
|
Hello, I am trying to do something like this: asm volatile("sts %0, r0" : "+M" (PCintPort::curr) );
But the error I'm getting is: error: impossible constraint in 'asm' I find that it's easy to do a c-style asm statment, like: uint8_t myvariable;
asm volatile("sts myvariable, r0");
But using a static C++ class variable is confounding me. The variable is declared thus: class PCintPort { public: static uint8_t curr; }
Thanks.
|
|
|
|
|
44
|
Using Arduino / Programming Questions / Re: Mixing C with Assembler: Assembly code before push instructions in a function
|
on: February 07, 2012, 07:40:11 pm
|
Good point. I may use r0, but I'll push/pop it. Thanks- 1 push instruction won't hurt. Not good enough. You have to preserve the registers used by your function before the function is called. I understand about r0 (which I'll use) and SREG, but doesn't the compiler push all registers it's concerned with? I've been rummaging around in avr-objdump and I observe that the compiler preserves registers upon entry into the function, not before functions are called. I am setting ISR_NAKED for the ISR, but when the ISR calls another function that will not be naked.
|
|
|
|
|