Time Delay for making pin an input with pullup?

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

Hi Guys,

 

Atmega 328p

 

At the start of my code (after declaring variables) I'm setting Pin D0 as an input with pullup enabled

 

DDRD &= ~(1 << PIND0); //Set as input

PORTD |= (1 << PIND0); // enable the pull-up on PD0

 

//Check if low (jumper closed

if(!(PIND & (1 << PIND0) ))

{

//reset TWI Address to default

What I'm finding is that if I don't put in a delay after setting the pullup, the IF statement finds that the voltage is low and goes in to my reset routine. 

If I place a 50ms delay before the IF statement it works as expect.

 

Is this normal behaviour, ie do I need to let things 'settle' before the test?

 

regards

  

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

Welcome to AVRFreaks!

 

NikMd wrote:

If I place a 50ms delay before the IF statement it works as expect.

 

Is this normal behaviour, ie do I need to let things 'settle' before the test?

Yes, real h/w takes time to change, although I would guess it's much shorter then 50ms, read the PORTs section of the datasheet, IIRC, it talks about port synchronization time is needed before a change in output is recognized by a read of the port, I seem to recall it is one clock cycle so a NOP may be all that is needed.  It's in the datasheet, let us know if you don't find it. 

 

Jim

edit: here it is:

When reading back a software assigned pin value, a nop instruction must be inserted as indicated in Figure 14-
4. The out instruction sets the “SYNC LATCH” signal at the positive edge of the clock. In this case, the delay tpd
through the synchronizer is 1 system clock period.

 

Keys to wealth:

Invest for cash flow, not capital gains!

Wealth is attracted, not chased! 

Income is proportional to how many you serve!

 

Last Edited: Mon. Sep 13, 2021 - 08:06 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for that

 

(using Atmel studio 7) C/C++

asm("nop");

didn't change the behavior, but

 

_delay_ms(1);

 

did.  Shame as I don't like using delay

 

regards

 

 

 

 

 

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

Why are you doing 

DDRD &= ~(1 << PIND0); //Set as input

PORTD |= (1 << PIND0); // enable the pull-up on PD0

 

instead of

DDRD =0; //Set as inputs

PORTD = (1 << PIND0); // enable the pull-up on PD0

Just set the registers to exactly what is needed, so you know exactly what they are set to every time you leave that point of the code.

Who told you to do that??

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

avrcandies wrote:

Why are you doing 

DDRD &= ~(1 << PIND0); //Set as input

PORTD |= (1 << PIND0); // enable the pull-up on PD0

 

instead of

DDRD =0; //Set as inputs

PORTD = (1 << PIND0); // enable the pull-up on PD0

Just set the registers to exactly what is needed, so you know exactly what they are set to every time you leave that point of the code.

Who told you to do that??

 

 

Sorry don't follow you.  What is wrong with my code snippet?  It seems to work just fine and the web is littered with similar examples.  Apologies if I'm missing something

 

regards

 

 

 

 

 

 

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

NikMd wrote:
the IF statement finds that the voltage is low

Have you looked at the pin with an oscilloscope - perhaps it is actually taking a long time to rise ... ?

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

nikm wrote:
Sorry don't follow you.  What is wrong with my code snippet?
When you use:

target |= (1 << bit);
target &= ~(1 << bit);

these things often involve a "read - modify - write". The target is read from memory, an AND/OR operation is performed on the existing value that has been read, then it is written back. Whereas:

target = bits;

is just one single, store operation. No need to read, modify then write.

 

So you generally do all the initial register settings with nothing but '=' but if you later need to change the the state of one or more bits in a register that has already been configured (such as setting ADSC to start an ADC conversion after everything in the register has been set previously) then this is when you might switch to using "|= mask" or "&= ~mask"

 

We see a lot of code here where people start main() with a whole load of |= and &=~. If it happens to be setup() (in Arduino) and not main() it's actually worse because by the time setup() is reached in Arduino a lot of peripheral registers have already been changed form the power on defaults. If just "= value" is used this may not matter as the register is entirely written with the new value but if |=/&=~ is used then you may just be adding/subtracting bits from a previous setting. The same is true in micros that have bootloaders. The bootloader may well have already used timers and UARTs and stuff for its own purpose. A good bootloader will have reset everything back before entering the app's main() but not all do. So the safe strategy is to assume you don't know what is in SFRs at entry to main().

 

(oh and another one is where a program "resets" simply with a "JMP 0", this can come back into main() with a whole bunch of peripherals already part configured)

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

awneil wrote:

NikMd wrote:
the IF statement finds that the voltage is low

Have you looked at the pin with an oscilloscope - perhaps it is actually taking a long time to rise ... ?

 

 

You're correct, it's exactly as Jim said I as per data sheet I need a nop after setting pull-ups before I test.

 

For the life of me I can't seem to generate a nop that works though.

 

regards

 

 

 

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

What happens if you use two 'NOP's?

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

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

nikm wrote:
For the life of me I can't seem to generate a nop that works though.
Check the LSS but a fairly "guaranteed" NOP is to be found (for avr-gcc / AVR-LibC) in cpufunc.h:

 

https://www.nongnu.org/avr-libc/...

 

#include <avr/io.h>
#include <avr/cpufunc.h>


int main(void)
{
    _NOP();
    _NOP();
    _NOP();
    while(1);
}

generates:

00000088 <main>:
#include <avr/cpufunc.h>

int main(void)
{
    _NOP();
  88:	00 00       	nop
    _NOP();
  8a:	00 00       	nop
    _NOP();
  8c:	00 00       	nop
  8e:	ff cf       	rjmp	.-2      	; 0x8e <main+0x6>

 

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

So you generally do all the initial register settings with nothing but '=' but if you later need to change the the state of one or more bits in a register that has already been configured (such as setting ADSC to start an ADC conversion after everything in the register has been set previously) then this is when you might switch to using "|= mask" or "&= ~mask"

 

Ah, got it.  Makes sense now.  Thanks for taking the time to explain

 

regards

 

 

 

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

awneil wrote:
Have you looked at the pin with an oscilloscope - perhaps it is actually taking a long time to rise ... ?

Here's where we find he has a 100nF capacitor on his input pin.

 

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

Check the LSS but a fairly "guaranteed" NOP is to be found (for avr-gcc / AVR-LibC) in cpufunc.h:

 

Thanks, tried it but doesn't work for me.  Tried in debug and release.  Could it be that it's being optimised away?

 

regards

 

 

 

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

nikm wrote:
Thanks, tried it but doesn't work for me.  Tried in debug and release.  Could it be that it's being optimised away?
This is using the _NOP() from cpufunc.h ? It is "asm volatile" it should not be optimised away. Try what I showed in #10. Look at the LSS are you really saying there are not "00 00 NOP"s at the points of invocation ?

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

Here's where we find he has a 100nF capacitor on his input pin.

 

Nope :)

 

Attachment(s): 

 

 

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

I had some interesting experience when multiplexing a pin to drive and LED and read a button.  The pin is normally set as output to drive the LED and switched to input to read the switch.  The delay required varies with VCC and I suspect the type of LED as well but I didn't not change that.  The code below contains the delays worked out experimentally.

 

#define	LED_W		CHKBIT(DDRD,4)	// LED-W Read State
#define	LED_ON_W	SETBIT(DDRD,4)	// LED-W ON	(output low)
#define	LED_OFF_W	CLRBIT(DDRD,4)	// LED-W OFF	(input hi-z)
#define	LED_TOG_W	TOGBIT(DDRD,4)	// LED-W TOGGLE
#define	EXT_SW		!CHKBIT(PIND,4)	// Console Adapter Mode Switch

char Read_EXT_SW(void)			// Read Multiplexed Switch Input
{
   unsigned char i, temp = 0, LED_state;

   LED_state = LED_W ? 1:0;		// save LED state
   LED_OFF_W;				// change to input to read switch
   SETBIT(PORTD,4);			// enable pull-up

   for (i = 0; i < 10; i++){;}		// settling delay (>4.75µs @ 3.3V/8MHz)
/*
      asm("NOP");
      asm("NOP");
      asm("NOP");
      asm("NOP");

      asm("NOP");
      asm("NOP");
      asm("NOP");
      asm("NOP");
*/
   if (EXT_SW)				// if SW ON
      temp = 1;			  	//    set return value

   CLRBIT(PORTD,4);			// disable pull-up
   if (LED_state)			// restore LED state
      LED_ON_W;

   return temp;
}

The 2 blocks of 4 NOPs were originally used; 1 block for running at 8MHz and both for 16MHz but for clarity I replaced with the 1 for loop delay.

 

Hope this helps.

 

Steve

 

 

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


nikm wrote:

Here's where we find he has a 100nF capacitor on his input pin.

Nope :) 

 

 

 

we'd need to see the full schematic - and know what might be connected externally.

 

Again, have you checked with a 'scope?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The 2 blocks of 4 NOPs were originally used; 1 block for running at 8MHz and both for 16MHz but for clarity I replaced with the 1 for loop delay.

 

Thanks that worked, but with running at 20MHz  <18 was required in the loop.

 

Perhaps 18 Nops would have also worked.  However this is not what's stated in the datasheet for the 328p.  That implies that only one should be required. Odd.

 

 

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

nikm wrote:
However this is not what's stated in the datasheet for the 328p.  That implies that only one should be required. Odd.

Unless the external signal is, indeed, only rising very slowly ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

we'd need to see the full schematic - and know what might be connected externally.

 

haven't checked with a scope.  The full schematic has around 150 components.  The partial pic shows that the pin goes to a jumper that's connected to ground.  I am using an external 20MHz oscillator.  Perhaps the delay isn't long enough on startup.

 

The 1ms delay before the read does the trick, but I was simply looking for a more elegant solution

 

regards

 

 

 

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

Think about it.   An internal pullup is about 30k.   

 

A badly routed pcb might have 20pF stray capacitance.

RC = 30k * 20p = 600ns

 

Some screened cable might be 1000pF stray

RC = 30k * 1000p = 30000ns = 30us

 

An inappropriate 100nF capacitor

RC = 30k * 100n = 3000us = 3ms

 

So I can't see how you need anything more than a few NOPs for a typical stray 20pF.

 

I would look very closely at your pcb.   I suspect that you have a physical 22nF capacitor.

 

David.

Last Edited: Tue. Sep 14, 2021 - 11:50 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would look very closely at your pcb.   I suspect that you have a physical 22nF capacitor.

 

Track length to jumper from pin is ~5mm and there is nothing on the other side of the board in that area, so I really can't see that I'm afraid.

 

I'll have a go with an arduino board to see of there's a difference

 

regards

 

 

 

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

Go on.  Just try 10us, 20us, ...

 

Simple enough to do.    My calculated 600ns implies that 1us should be fine.   1000us is overkill.

 

Mind you,   a springy push button will bounce for up to 20ms.

But external electronics will have clean signals.

Last Edited: Tue. Sep 14, 2021 - 12:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yup, 1us works

 

regards

 

 

 

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

this also worked

 

for(i=0; i< 6;i++)

asm volatile ("nop");// __no_operation();

 

 

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

Don't mix ASM in a C loop.   The loop overhead will dwarf the single NOP.

 

If you want to have six NOPs just say:

    asm("nop");

    asm("nop");

    asm("nop");

    asm("nop");

    asm("nop");

    asm("nop");

 

Or if you want to see how the C compiler handles a loop,   use the AS7.0 Simulator.

It will tell you the exact number of nanoseconds used by your C code.

 

The lessons are:

1.  how long a processor cycle is.   e.g. 125ns for an 8MHz AVR.

2.  how to use the Simulator.   e.g. examine opcodes.  count cycles.  calculate execution time.

3.  how to count AVR cycles "by hand"

 

4.  how to calculate rise and fall times in an RC circuit e.g. calculate RC time constant.

 

5.  the important lesson.   how to estimate or use typical values for R and C.  

6.  Pick up some "rules of thumb" for electronic components.   e.g. 100nF capacitors for VCC decoupling.  20pF for stray capacitance.

 

David.

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

I'm not sure if the name of the compiler has been revealed but if it is avr-gcc then know that it has _builtin_avr_delay_cycles(N) so if you want to delay 13 cycles then just:

// stuff
__builtin_avr_delay_cycles(13);
// more stuff

You don't need any .h file to use this - as the name suggests it is "builtin" to the compiler. In fact if you use:

#define F_CPU 123456UL
#include <util/delay.h>

...
   _delay_us(1.79);

then under the hood this will just be using __builtin_avr_delay_cycles() to get as close to 1.79us based on a cycle being 1/123456 s.

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

NikMd wrote:
I don't like using delay

I don't really understand that comment.

 

You've determined that your code needs a short (just a few µs) delay. What better way is there to address that requirement other than by using _delay_us(1) ??

 

That single line clearly shows your intention for the code with the benefit that if you change your CPU clock; that line does not need to change,

 

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

NikMd wrote:

I don't like using delay

N.Winterbottom wrote:
I don't really understand that comment.

 

I guess he's thinking in general of the widespread abuse of "blind" delays; eg, https://www.avrfreaks.net/commen... - which are certainly best avoided!

 

surprise

 

But agree in this case - the delay does seem like the way to go.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Wed. Sep 15, 2021 - 09:31 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Use a scope so you know what you are dealing with...why chase around all over wondering what is going on?  You could have verified the requirement in a few minutes.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Wed. Sep 15, 2021 - 09:32 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't like using delay

There is no harm in ever using small polled delays e.g. _delay_us(1)

Even large delays are appropriate sometimes.

 

Use a scope so you know what you are dealing with...why chase around all over wondering what is going on?  You could have verified the requirement in a few minutes.

Yes, fine if a hobbyist can afford to buy an oscilloscope.

 

Personally,   I am happy with just doing back of the fag-packet maths and RC time constants.

It cost nothing.   You don't even need a PC.

 

But the important thing about analogue electronics is that you can design with standard components (with wide tolerance)

If you want to be "safe" from a typical 600ns fall time,   you can just experiment with _delay_us(0.5), _delay_us(0.6), _delay_us(0.7), ..., _delay_us(1), ...

 

Having experimented,  you apply a safety margin.   e.g. if 0.6us "works" select 1.2us or 2us.

 

If you want to pursue an interest in microcontrollers,   a $10 Saleae clone Logic Analyser is money well spent.

But first of all,   just gain experience with the AS7.0 Simulator.

 

Oh,   if you don't understand "RC time constants" just Google.

God invented Wikipedia.    It is better than every textbook or Internet tutorial.   (Mind you,   I am always happier with real paper books)

 

David.

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

Of course, you can do away with any delay whatsoever by rearranging the code to move the test of the pin further away from the initialisation. I'm sure there must be more to the code than the snippet back in post #1.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

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

I think that we have established that you have a 600ns rise / fall time.

Using _delay_us(0.6) will cope with different F_CPU values.

 

600ns is 12 cycles @ 20MHz

600ns is 10 cycles @ 16MHz

 

If you are running with F_CPU @ 1MHz a single NOP gives you a whole 1000ns.

 

Yes,  as Brian has suggested you could rearranged the statements so that the pin has settled before you read it.

But you must be aware that faster AVRs mean more cycles in a given 600ns time frame.

Which is why it is useful to learn how to use the Simulator.

 

Mind you,  it looks as if the OP is simply reading a hardware jumper at the start of the application.

So I would also think about the power supply startup.   Has it reached 5.0V (or whatever) ?

Has the AVR XTAL clock stabilised yet ?

 

David.

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

Usually when I write applications for MCUs I do the pin initialization (plus other initialization) as early as possible and then add long delay (100 ms or so) to let voltages stabilize. There could also be external circuits connected to the MCU that need more time to initialize and/or settle. There is no reason why you shouldn't use a delay in this context.

 

In this case, you need a delay that depends on the rise time of the pin, which depends on the total capacitance. You now end up with the question - what IS the expected capacitance? What you could do in fact is to code a hardware diagnostics stage that turns on pin pull-up, waits until the pin is high, and checks how long it took. If the time required is outside some expected range, then there is a hardware malfunction of some sort and you should indicate this to the user. 

 

You can do other kinds of diagnostics this way e.g. change output pins to inputs and check that the logic level matches the expected value (determined by the connected circuit).

/Jakob Selbing

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

jaksel wrote:
What you could do in fact is to code a hardware diagnostics stage that turns on pin pull-up, waits until the pin is high, and checks how long it took [my emphasis]

Now what to do if the jumper IS fitted ?

 

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

Brian Fairchild wrote:
Of course, you can do away with any delay whatsoever by rearranging the code to move the test of the pin further away from the initialisation. I'm sure there must be more to the code than the snippet back in post #1.

This is certainly what I would try to do.  Configure the pulled-up input, go do something else useful (just a few lines of code should do it), come back and read the input pin.

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

nikm wrote:
Yup, 1us works

That could be on the ragged edge.  Max pullup is specified as 50k.  Assuming 20pf chip and wiring capacitance, that's a time constant of 1us.  One time constant will just about let the pullup bring the input to a valid '1', but it could get flakey depending on individual chip, temperature, etc.  Personally I'd feel better with a minimum of 2us.