Show Posts
Pages: 1 2 3 [4] 5 6 7
46  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.

Code:
#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);
    }
  }
}
47  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:

Code:
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:

Code:
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

Quote
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:

Code:
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.

48  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.

Quote
Also, I don't know if you can do much better than the compiler. For example:

Code:
ISR (SPI_STC_vect)
{
// do nothing  
}

Generates:

Code:
...(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:

Code:
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.
49  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
Quote
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:
Code:
     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.
50  Using Arduino / Programming Questions / Re: How to give a C++ variable name to an asm statement? on: February 08, 2012, 11:01:32 pm
Thanks!  That's it!  The following statement is what I wanted:

Code:
asm volatile("sts %0, r0" : "+m" (PCintPort::curr) :: );

and it generated the following assembly language code:

     4be:       00 92 60 01     sts     0x0160, r0

Thanks again.  It finally compiles without complaining. :-)
51  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.
52  Using Arduino / Programming Questions / Re: How to give a C++ variable name to an asm statement? on: February 08, 2012, 10:18:25 am
Code:
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.
53  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:

Code:
asm volatile("sts %0, r0" : "+M" (PCintPort::curr) );

But the error I'm getting is:

Code:
error: impossible constraint in 'asm'

I find that it's easy to do a c-style asm statment, like:

Code:
uint8_t myvariable;

asm volatile("sts myvariable, r0");

But using a static C++ class variable is confounding me.  The variable is declared thus:

Code:
class PCintPort {
public:
    static  uint8_t curr;
}

Thanks.
54  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.
55  Using Arduino / Programming Questions / Re: Mixing C with Assembler: Assembly code before push instructions in a function on: February 07, 2012, 03:31:44 pm
Good point.  I may use r0, but I'll push/pop it.  Thanks- 1 push instruction won't hurt.
56  Using Arduino / Programming Questions / Re: Mixing C with Assembler: Assembly code before push instructions in a function on: February 07, 2012, 10:37:57 am
... if they're trampled by a push operation then using one of these as a scratch area while you do the push would not work very well.

... write your ISR handler in assembler, have this save your I/O register in your temp register and then call a 'C'/C++ function for whatever logic you want to code in higher level languages. You sound as if you're comfortable writing in assembler, but if you aren't you could get a starting point by listing the assembler generated for your existing handler.
Excellent suggestion, thanks for the tips!  I am not comfortable writing in assembler, so I am going to list the assembler generated and work from there :-) .  But, I am comfortable living life on the edge!  So I'm going to try it.

My intention is to get the heck out of the way of the compiler as quickly as possible, so I'm thinking I might need 2 assembly instructions:  1 to read the register, the other to save it to a C variable (ie, RAM).

I notice that my idea is not so far fetched, as a matter of fact I found this link: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html .  The ISR_NAKED is what I want.  So, my idea is the following, pseudo-code:

Code:
ISR(PCINT0_vect, ISR_NAKED)
{
    asm (read_in_register_to_r0)
    asm (store_to_ram)
    // The following function call will preserve the registers on the stack.  Since I haven't
    // mucked with any but r0 thus far in my ISR, I should be ok.  The function call will
    // also restore the necessary registers.
    C_function_call();
    asm (reti);
}

I notice that the ISR pushes a whole bunch of registers on the stack.  Function calls do not necessarily save the same large set.  I assume that's because the compiler is smart enough to know that a function may not stomp on all the registers, so it's judicious in its saving.  Thus, it seems to me the ISR is storing such a large set because it doesn't have any foreknowledge of what it should or should not preserve, so it paints with a large brush.

But if the *only* thing my ISR does is as listed:  1. my assembly, 2. call a C function, 3. return, then I assume that the heavy lifting of pushing and popping the necessary registers, if handled solely by the C function call in the ISR, should be sufficient. There are no other dragons lying about out, are there?  Again, assuming my ISR is just that lean.

Right now my ISR looks like this:
Code:
ISR(PCINT0_vect) {
        portD.PCint();
}
...so it's doing two bunches of push/pop operations:  The entry into the ISR, and the entry into the function call, which is overkill.

...Just checking my assumptions...
57  Using Arduino / Programming Questions / Re: Mixing C with Assembler: Assembly code before push instructions in a function on: February 07, 2012, 09:35:33 am
I would like to preserve the state of a register very quickly when the interrupt takes place.

I guess you mean you want to preserve the state of an I/O register at the earliest possible point in an ISR.
Exactly!  You hit the nail on the head.  I am writing a library and I am preserving the state of an I/O register- ostensibly at the time of the interrupt- but with all the push instructions, it takes place some 4 microseconds (I figure) after the actual interrupt.

Quote
Have you thought of a way to do that without using any processor registers? Since you can't know what processor registers are in use you mustn't use them until you have preserved their state so that they can be restored afterwards.
Not really.  I am aware of this:
Quote
Register r0 may be freely used by your assembler code and need not be restored at the end of your code. It's a good idea to use __tmp_reg__ and __zero_reg__ instead of r0 or r1, just in case a new compiler version changes the register usage definitions.
So I believe I can safely use r0.  All I need is to use the in instruction, then save that register to RAM, and I should be good to go.
58  Using Arduino / Programming Questions / Re: Mixing C with Assembler: Assembly code before push instructions in a function on: February 07, 2012, 09:31:00 am

Do you understand the purpose of the push instructions?

Quote
I want to insert some assembler which is to be run before those registers are preserved.

Does the "some assembler" include an in instruction (like your pseudo-code)?


Yes, and yes.  I plan on taking advantage of this:

Quote
Register r0 may be freely used by your assembler code and need not be restored at the end of your code. It's a good idea to use __tmp_reg__ ... instead of r0 or r1, just in case a new compiler version changes the register usage definitions.
59  Using Arduino / Programming Questions / Mixing C with Assembler: Assembly code before push instructions in a function on: February 07, 2012, 01:02:00 am
Hello,
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.

The C compiler generates a bunch of code to preserve registers when the ISR (or any function) is called.  I want to insert some assembler which is to be run before those registers are preserved.  Is this possible?  Is there some directive I can add to my asm line to tell it exactly where to inject itself in the C code?  Thanks.

The code looks iike this.  I know- the push instructions are there to preserve registers.  Consider my asm to be pseudo-code; I will figure out how to use __tmp_reg__ if I decide that my efforts are worth it.  Thanks:
Code:
ISR(PCINT2_vect) {
    uint8_t value;
    // I want this to take place before the compiler-created push instructions.
    asm volatile("in %0, %1" : "=r" (value) : "I" (_SFR_IO_ADDR(PORTD)));
    portD.PCint();
}
60  Community / Exhibition / Gallery / Re: PinChangeInt library- To attach interrupts to multiple Arduino (Uno/Due) pins on: February 06, 2012, 12:45:57 am
...aaaand, version 1.51 is out.  Fixes a bug in 1.5, that caused the pinState to be incorrectly reported  smiley-roll-sweat  .  It appears to work correctly now.  Apologies for the messup.
Pages: 1 2 3 [4] 5 6 7