Do GPIO pin pullup/downs only trigger when outside of an interrupt?

Go To Last Post
17 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My initial thoughts are no and are how could this possibly be true as interrupts still have direct access to registers. However I have been seeing some interesting situations where the LED does not change state or sometimes misses its state change altogether.

 

E.g. If I were to trigger an interrupt every 200ms and change the state of the port every interrupt cycle, I should see the led flash 3 times every 6 port changes. I know that this code is operational because I have the following output which acts as a decrement counter:
 

Error 3
22
21T
20
19T
18
17T
16
15T
14
13T
12
11T
10
9
8
7
6
5
4
3
2
1

Please ignore the amount of numbers, however, the 'T' is outputted EXACTLY next to the port change of state, such that:

 

Uart_PutChar('T');
if (PORTC & (1<<PC1))
    PORTC &= ~(1<<PC1);
else
    PORTC |= (1<<PC1);

 

Majority of the time, the LED's will flash correctly until a large amount of data is transmitted through my other UART and a timer interrupt that does a lot of processing. Though the above is still displayed such that I think the LED's should still flash correctly and I can still see that the UART output streams out with delays between it.

 

Any ideas on what I may be doing wrong?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Show your code. Hard to tell with this info.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For the below, the Error codes are parsed in as a 0x01/0x02/0x04/0x08/0x10/0x20/0x40/0x80 to represent a bit of a global variable that is set, ErrorCodeQueue. The millissecond timer is triggered ever 10ms and a 10 skipCount is applied to make it so the timer is only incremented every 100ms (16-bit Timer1 is not available). The ErrorCodeQueue is setup on a prioity of the lower bits over the higher, so it MAY be possible that the high bits would never flash.

 

 

#define ERROR_DECREMENT_SPACE 10 //use 10 cycles (1s) gap between handling error codes.
    volatile uint8_t  ErrorCodeDecrementor;
    volatile uint8_t  ErrorCodeQueue;
    volatile uint16_t Timer0_Millis;
    volatile uint8_t  HundredMillisSkipCount;

void ShowError(uint8_t errorCode)
{
	if (errorCode & ErrorCodeQueue)
	{
		return;
	}
	ErrorCodeQueue |= (1<<errorCode);

	char tmp[10];
	itoa(errorCode+1,tmp,10);	//increment by 1 so the errors will show as error 1-8 instead of 0-7. 0-7 used as flags
	Uart_PutString_P(PSTR("Error "));
	Uart_PutString(tmp);
	Uart_PutChar('\n');
}

//triggers every 10ms
ISR(TIMER0_COMPA_vect)
{
	if (HundredMillisSkipCount)
	{
		HundredMillisSkipCount--;
		return;
	}
	
	//triggers here every 100ms.
	HundredMillisSkipCount = 9;
        Timer0_Millis++;
	
	//Handle error codes
	if (ErrorCodeDecrementor)
	{
		char tmp[10];
		itoa(ErrorCodeDecrementor,tmp,10);

		Uart_PutString(tmp);
		if ((ErrorCodeDecrementor > ERROR_DECREMENT_SPACE) && (ErrorCodeDecrementor % 2))	//1 flash will occur every 400ms (200ms on, 200ms off)
		{
			Uart_Putchar('T');
			if (PORTC & (1<<PC7))
			{
				PORTC &= ~(1<<PC7);
			}
			else
			{
				PORTC |= (1<<PC7);
			}
		}
		else
		{
			#warning UNCOMMENT ME! SAFETY TO TURN LED OFF
			//STATUS_LED_OFF;
		}
		Uart_Putchar('\n');
		ErrorCodeDecrementor--;
	}
        //if any errors are in the queue process the lowest one first.
	else if (ErrorCodeQueue)
	{
		//only 7 errors currently exist. bits are set between 0-6
		for (uint8_t i = 0; i < 7; i++)
		{
			if (ErrorCodeQueue & (1<<i))
			{
			        //error codes are assigned to bits 0-7 and are labelled as 1-8. Add 1 to the bit then multiply by 4 since the interrupt
				//is triggered every 100ms. Add the decrement delay of 10 to this as well of 1s. Decrement delay is ensure if an error is triggered continuously
				//it doesn't keep the LED flashing
				ErrorCodeDecrementor = (i+1)*4+10;
				ErrorCodeQueue &= ~(1<<i);
				break;
			}
		}
	}
}

This for some reason triggers the 6 'T's for a 3 flash error but sometimes will only flash twice, be left with the LED on, or sometimes from being already on, it will switch it off...any ideas would be awesome!

Last Edited: Tue. Jul 28, 2015 - 05:35 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have you declared variables as volatile which are accessed in both main and ISR?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Correct. All variables are defined as volatile on a global scope.

 

Please see previous post for amendments.

Last Edited: Tue. Jul 28, 2015 - 05:36 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry but what has any of this got to do with the thread title? Where are pull-ups/downs involved in any of this?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

the 'T' is outputted EXACTLY next to the port change of state

How can you say that, when apparently either (A) it will go into an output queue or (B) the USART is busy transmitting other characters?

 

I'd need to think about polled USART interaction, and the possible effects of the timer interrupt firing at any point in the put-string sequence.  I think it would be OK.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

RETURN  does not belong in an IRQ service routine!  As it is a return from subroutine, not a return from interrupt instruction.

 

 

Jim

 

 

FF = PI > S.E.T

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The return is fine. The compiler knows to use RTI instead of RET.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

Sorry but what has any of this got to do with the thread title? Where are pull-ups/downs involved in any of this?

 

The issue I was having was that setting PORTC sometimes did not change state of my LED (that is directly dirven from the pin) when i was setting it inside this timer interrupt. Sometimes I could see it change state twice to the 6 times. I am unsure what was triggering this. Thus I raised the question of if it is possible that the registers can only take effect outside of ISR's? Or what else could be the reasons behin this.

 

theusch wrote:

How can you say that, when apparently either (A) it will go into an output queue or (B) the USART is busy transmitting other characters?

 

You're correct, I put it in a queue and outputted through an interrupt. While you're correct, I only wanted to outline the fact that I am still getting the six occurences of 'T', as such I should still get my 6 port changes. The Pin has no other occurences in the applcation except for above the main loop, setting the initial DDR and PORT.

 

Is there anything off the top of your head that could create the results I am seeing?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Theusch outlined some potential problems. Namely that you access the uart via the main line code and via the isr. You have a classic case of non- atomic access. What happens when your main code thinks the uart is empty but before it gets to put a char into UDR the timer int hits and outputs something different. When the isr returns, your main code thinks the uart is empty and writes another value to UDR. You get a random result. Also, if your main code is doing read/modify/writes on PORTC or other things shared with the isr, you're bound for trouble.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Kartman,

 

As it turns out I was just about to reply with the same thing. It was the non-atomic access of PORTC that was the issue. While I am not doing any operations on that pin, I am still doing operations that occur everytime in the main loop on PORTC, as such when it came out of the interrupt it would overwrite the changes because the old value of PORTC was already stored in a register.

Last Edited: Wed. Jul 29, 2015 - 01:16 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I wrote a tutorial 'the traps when using interrupts' that outlines this sort of issue as well as others.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ryanafleming wrote:

Hi Kartman,

 

As it turns out I was just about to reply with the same thing. It was the non-atomic access of PORTC that was the issue. While I am not doing any operations on that pin, I am still doing operations that occur everytime in the main loop on PORTC, as such when it came out of the interrupt it would overwrite the changes because the old value of PORTC was already stored in a register.

1)  Few of us would put that much code into an ISR.  Especially operations of indeterminate length such as a USART string send.  ([Almost] always, my timekeeping timer interrupt just sets a flag that a "tick" has occurred, or counts the "ticks" that have occurred.  The mainline then does the time-dependent processing.  Yes, it can get a hair behind, but it can then "catch up".)

 

2) Do we know which AVR model is involved?  All (?) newer AVR8 models have a pin-toggle feature by writing to PINx.  No atomicity problems.

 

2a)  I'd like a peek at the offending PORTx code.  Port C should be "low" in all AVR8 models, AFAIK.  So the fragment, with most toolchains and a decent optimization level, should resolve to an SBIC/SBIS followed by an SBI/CBI.  (Right, 'Freaks?)

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Wed. Jul 29, 2015 - 01:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:

I wrote a tutorial 'the traps when using interrupts' that outlines this sort of issue as well as others.

 

I shall read up!

 

theusch wrote:

1)  Few of us would put that much code into an ISR.  Especially operations of indeterminate length such as a USART string send.  ([Almost] always, my timekeeping timer interrupt just sets a flag that a "tick" has occurred, or counts the "ticks" that have occurred.  The mainline then does the time-dependent processing.  Yes, it can get a hair behind, but it can then "catch up".)

 

The Uart code was only added for debugging purposes but I still used the rest of the code within the interrupt itself.

 

theusch wrote:

2) Do we know which AVR model is involved?  All (?) newer AVR8 models have a pin-toggle feature by writing to PINx.  No atomicity problems.

 

Atmega644pa. For this chip AFAIK PORT is used or setting the internal pullup (typically used for output) and PIN is only for reading the state of the pin.

 

The general code that was affecting this was ran in every loop of main.

 

//uint8_t swicthOn;

if (switchOn)
{
    PORTC |= (1<<PC0);
}
else
{
    PORTC &= ~(1<<PC0);
}

We could oviously minimise this "by chance" occursion by setting PORTC only when the switchOn value changes by adding another flag, but in the end we decided to remove the port change from inside the interrupt to outside.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AFAIK PORT is used or setting the internal pullup (typically used for output) and PIN is only for reading the state of the pin.

The you know wrong. All modern AVR, the 644 included have an additional use for PIN if you write to it. Any 1 bits in it cause the corresponding pin to toggle see 14.2.2 in this....

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Regarding your original question.

 

You are only reading the state of the PORT output latch.    It will always read the value that was last written to it.   It will take no notice of any external signal or pull-up on the external pin.

 

It is generally wise to keep ISR()s short and sweet.    Update a buffer or set/clear a flag then leave.    Process any flag or buffer in the foreground code.

 

Having said that.   Your Timer ISR() is so infrequent that you can afford to do quite a bit of processing before you 'lose' an interrupt.   e.g. you can print several characters on a 9600 baud UART inside 19.99ms   (actually 19 in 8-N-1 format if the initial ISR() was started with no latency)

 

If you are genuinely wanting to catch pin-changes in real life,   the hardware of any AVR will catch them just fine.    You can process them at your leisure.    Obviously with the proviso that you have finished one,  with the next one pending before a subsequent change occurs.    i.e. you normally get single buses.   you can manage two buses that come along at once but a third one is too much.    You always have to design for the worst case.

 

David.