ATMEGA328P with Arduino IDE: Serial communication doesn't work in combination with delay()-function

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

Hi,

 

Disclaimer:

I've been reading on this forum for a while and it helped me on many problems with my design. So already thank you for that! :) Now, I've come across a really weird problem using the Arduino IDE to program an ATMEGA328P on a breadboard. I've already posed this question in the Arduino forum last weeks, but it seems like - although I highly value that community, as well - nobody there has any idea how my problem can be solved. So, now, I'd like to ask more people who are more used to using AVRs without the Arduino board. ;)

 

General Setup:
I have followed several guidelines and tutorials (mainly http://www.open-electronics.org/arduino-isp-in-system-programming-and-stand-alone-circuits/ and https://www.arduino.cc/en/Main/Standalone) to build an Arduino on Breadboard, that I'm exclusively programming via ISP. Thus, I have no use for an Arduino bootloader and don't use it (although the problem also occurs if I flash the UNO or Duemilanove bootloader first and then upload my sketch).

 

Here is drawing of my set-up:

 

  

 

Minimal code example to illustrate my problem:

 

void setup() {
  Serial.begin(9600);
}

// the loop function runs over and over again forever
void loop() {
  Serial.println("Some text.");
  delay(1000);
}

 

There's nothing else in the sketch. The code runs perfectly on an Arduino UNO, however, with the breadboard version, there is one problem.

 

Problem:
Using an oscilloscope on the ATMEGA328P pin 3 (TX), I see a high level all the time. Deleting the delay(1000) line, I get the correct signal on the TX pin (if I then connect a TTL2RS232 converter, it's even showing the correct output on a serial monitor). A delay(1) or delay(2) won't do much noticeable harm either, but already with 20 ms, I get a sporadic high for some time once in a while, and with 100 ms, there's almost exclusively high. With 1000 ms (as in the code) I can hardly get anything other than high. 

 

Tried solutions:
If I add a blinking LED (ATMEGA328P pin 19), the LED is blinking as it is supposed to (indicating other pins and the clock are working fine and there are no hidden resets). Using a pull-up resistor (whether internal or 10k external) on the RX line doesn't make it work, either. Replacing the ATMEGA328P by a new one doesn't make any difference, either.

 

More information:
In case anyone assumes a problem here, this is the relevant section of my boards.txt (both the original section with bootloader and also the one I added, mostly to set the fuses differently - as mentioned above, both don't work, though):
 

uno.name=Arduino/Genuino Uno

uno.vid.0=0x2341
uno.pid.0=0x0043
uno.vid.1=0x2341
uno.pid.1=0x0001
uno.vid.2=0x2A03
uno.pid.2=0x0043
uno.vid.3=0x2341
uno.pid.3=0x0243

uno.upload.tool=avrdude
uno.upload.protocol=arduino
uno.upload.maximum_size=32256
uno.upload.maximum_data_size=2048
uno.upload.speed=115200

uno.bootloader.tool=avrdude
uno.bootloader.low_fuses=0xFF
uno.bootloader.high_fuses=0xDE
uno.bootloader.extended_fuses=0xFD
uno.bootloader.unlock_bits=0x3F
uno.bootloader.lock_bits=0x0F
uno.bootloader.file=optiboot/optiboot_atmega328.hex

uno.build.mcu=atmega328p
uno.build.f_cpu=16000000L
uno.build.board=AVR_UNO
uno.build.core=arduino
uno.build.variant=standard

##############################################################

atmegasa16.name=ATmega328P Stand Alone (Arduino as ISP)

atmegasa16.upload.protocol=stk500
atmegasa16.upload.maximum_size=32768
atmegasa16.upload.speed=115200
atmegasa16.upload.tool=avrdude

atmegasa16.bootloader.tool=avrdude
atmegasa16.bootloader.low_fuses=0x9e
atmegasa16.bootloader.high_fuses=0xd7
atmegasa16.bootloader.extended_fuses=0xFD
atmegasa16.bootloader.file=optiboot/optiboot_atmega328.hex
atmegasa16.bootloader.unlock_bits=0x3F
atmegasa16.bootloader.lock_bits=0x0F

atmegasa16.build.board=AVR_UNO
atmegasa16.build.mcu=atmega328p
atmegasa16.build.f_cpu=16000000L
atmegasa16.build.core=arduino
atmegasa16.build.variant=standard

 

Conclusion:
This problem seems very strange to me. I've double checked the wiring to be exactly as in the above image (in fact, I've drawn it according to what my breadboard looks like now), and it seems alright. I can't imagine any other problem than an external connections issue, because the sketch runs just fine on my UNO board and changing the uC on the breadboard doesn't change anything. I have no idea how a bad wiring could have an impact on the combination of delay and serial communication in any way, though. 

 

Do you have any idea at all? I take all suggestions happily. ;)

 

Best regards,
Henrik

Last Edited: Mon. Nov 20, 2017 - 03:23 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You seem to have a LED with a 330R in series with one of the programming lines, take that out see if it fixes things.

 

The Xtal caps legs should be as short as possible.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Not familiar with the Arduino delay implementation. Does it use one of the timers? If not, is it "blocking"? If it is blocking, and is long enough, two character that arrive during the delay is running, you will loose at least the first one. Same may be true for outgoing.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

If you plug you breadboard m328 into the Arduino board (assuming the uno has a socketed m328) what happens?

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

Thank you for your quick replies! :)

 

js wrote:

You seem to have a LED with a 330R in series with one of the programming lines, take that out see if it fixes things.

 

The Xtal caps legs should be as short as possible.

 

Unfortunately, taking out the LED doesn't change anything. I also cut the cap legs, but doesn't help. I couldn't cut them much, though, due to the breadboard (they still need to connect the GND line and the uC pin line). Is there a way I can say if this is the probable reason, which then won't appear on a PCB?

 

 

ka7ehk wrote:

Not familiar with the Arduino delay implementation. Does it use one of the timers? If not, is it "blocking"? If it is blocking, and is long enough, two character that arrive during the delay is running, you will loose at least the first one. Same may be true for outgoing.

 

I just found the implementation: https://github.com/arduino/Ardui...

 

Looks like it does indeed use a timer (not too sure with writing and understanding libraries, though - I'm still learning). Unfortunately, I'm losing the whole signal (which means the TX pin is high all the time, except for some pattern every ten seconds or so - sometimes more, sometimes less). How can I find out if it's "blocking" as you describe it? Is there some other function or code snippet, that I can put in instead of "delay" to see where exactly the problem lies? I'm really confused that it works on the Arduino board, but not on the breadboard.

 

A 10k pull-down (or pull-up) on the TX line (which I put in for trying just now - I know, it isn't supposed to be there) doesn't change anything, either.

 

Best regards,

Henrik

Last Edited: Mon. Nov 20, 2017 - 05:41 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Good hint! Now something new is happening ...

 

Kartman wrote:
If you plug you breadboard m328 into the Arduino board (assuming the uno has a socketed m328) what happens?
 

 

A new weirdness is what happens. The scope still shows no difference (still high with occasional pattern), but the serial monitor shows my output. If I put the uC back to the breadboard set-up, I have the old problem. Let me summarize including the new finding:

 

1. uC in Arduino board, delay(1000): scope wrong, serial monitor correct

2. uC in Arduino board, no delay: scope correct, serial monitor correct

3. uC on breadboard, delay(1000): scope wrong, serial monitor incorrect

4. uC on breadboard, no delay: scope correct, serial monitor correct

 

So with no delay, everything is fine (both on breadboard and Arduino board). With delay(1000) strange things happen on both (although I blame my scope for situation 1).

Last Edited: Mon. Nov 20, 2017 - 06:59 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yet a new situation, thus reason for a new post.

 

I'm really, really confused now. After some more uploading, changing the uC back and forth between breadboard and Arduino a few times, changing the sketch a few times (putting in and out the delay), all of a sudden, situation 3. changes to "scope wrong, serial monitor correct". Does anyone have any idea how to make this work reliably or why there is still this difference between using delay (scope wrong) vs. not using the delay (scope correct) even if the serial monitor now works all the time (although this seems rather random to me and might stop working in an hour randomly).

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

IS the bootloader getting activated when you don't want it to? Or maybe the watchdog?

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

Kartman wrote:

IS the bootloader getting activated when you don't want it to? Or maybe the watchdog?

 

As I understand it, my fuse settings (high fuse: 0xd7) sets the watchdog timer to off by default. However, I read it could be activated in software. How can I check this?

 

How would that be possible, that some bootloader suddenly gets activated? As I understand it, I actively have to burn a bootloader. Only setting a delay shouldn't have impact on the bootloader, right? However, the code doesn't run if I actively burn a bootloader before, either.

 

How could the bootloader possibly get in my way anyway? Isn't it only for being able to program the Arduinos via USB? How could it be related to the delay()-function?

 

After reading your post, I just double checked. These are my findings:

 

  • A) Using the original Arduino uC in the Arduino board, situation 1 and 2 both occur as described (serial monitor correct in both cases, scope incorrect only with delay).
  • B) It doesn't make a difference if I burn a bootloader to the breadboard uC or not. Serial monitor is now still working in both cases (with and without bootloader on breadboard, but also with bootloader on Arduino), while the scope is correct only without delay.
Last Edited: Mon. Nov 20, 2017 - 08:16 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

delay() is blocking.

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

with 100 ms, there's almost exclusively high. With 1000 ms (as in the code) I can hardly get anything other than high. 

Are you using your scope correctly?  I mean "some text" takes about 10ms to output, so indeed with a 100ms total loop time, the serial pin with be high (idle) most of the time.  And at 1000ms loop time, the data will be present an even smaller percentage of the time.  If your scope doesn't have a deep memory and/or latch the data, it would be easy to miss.

 

That doesn't explain why it doesn't work correctly on the serial monitor, but then you haven't described how you have your breadboard setup connected, serial-wise.

 

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

TT_ZX wrote:

delay() is blocking.

 

How can you see this? What exactly do you mean by "blocking" and what implications does it have? How does it impact the Serial communication exactly and how can I get rid off that?

 

westfw wrote:

with 100 ms, there's almost exclusively high. With 1000 ms (as in the code) I can hardly get anything other than high. 

Are you using your scope correctly?  I mean "some text" takes about 10ms to output, so indeed with a 100ms total loop time, the serial pin with be high (idle) most of the time.  And at 1000ms loop time, the data will be present an even smaller percentage of the time.  If your scope doesn't have a deep memory and/or latch the data, it would be easy to miss.

 

Wow, you are right about using my scope. Thank you for mentioning that!

 

I wasn't using the trigger correctly. I set it up differently and also set the highest sampling rate and this part of the problem is gone. :)

 

westfw wrote:

That doesn't explain why it doesn't work correctly on the serial monitor, but then you haven't described how you have your breadboard setup connected, serial-wise.

 

Indeed, this doesn't explain it. As mentioned, I don't have the problem right now, but definitely had it before and want to understand it before I "burn" everything into a PCB.

 

I'm using the Arduino board's ATMEGA16U2. The way I do this is connecting the Arduino TX pin to my Breadboard TX pin. Then I either connect the Arduino's reset pin to GND or take out the uC on the Arduino board. Before, this always worked without delay, but not with delay. Now, it works for both, but this seems rather random to me (see above).

Last Edited: Mon. Nov 20, 2017 - 09:41 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hemanti wrote:
What exactly do you mean by "blocking" and what implications does it have?
"blocking" is just another word for "synchronous". It means "something that doesn't come back until it is finished".

 

The key question about any (software) delay is actually whether it disables interrupts or not. By rights if you offer delay_ms(350) it would be kind of disappointing if this didn't return for 671 milliseconds. But the only way to guarantee that would be:

disable_interrupts();
do_the_delay();
restore_interrupts();

because otherwise some interrupt in the system could keep "firing" during do_the_delay() and actually extending it way beyond the planned time.

 

But an delay() that disables interrupts is a very dangerous thing in any system that has any kind of reliance on interrupts. For example in Arduino the reception of characters on UART is done using interrupts so if the phrase "Hello World" starts to arrive but just as it starts to get to the micro that micro chooses to delay(200ms) or something it could be that some of the inbound characters are lost and you end up with "Herld" because "llo Wor" have been lost in the middle.

 

The joy of Arduino is that all the source code is "open". You can see how it does delay() here:

 

https://github.com/arduino/Ardui...

 

so it actually does it as:

void delay(unsigned long ms)
{
	uint32_t start = micros();

	while (ms > 0) {
		yield();
		while ( ms > 0 && (micros() - start) >= 1000) {
			ms--;
			start += 1000;
		}
	}
}

So that's repeatedly calling micros() to get a time update. micros() is here:

 

https://github.com/arduino/Ardui...

 

and that does:

unsigned long micros() {
	unsigned long m;
	uint8_t oldSREG = SREG, t;
	
	cli();
	m = timer0_overflow_count;
#if defined(TCNT0)
	t = TCNT0;
#elif defined(TCNT0L)
	t = TCNT0L;
#else
	#error TIMER 0 not defined
#endif

#ifdef TIFR0
	if ((TIFR0 & _BV(TOV0)) && (t < 255))
		m++;
#else
	if ((TIFR & _BV(TOV0)) && (t < 255))
		m++;
#endif

	SREG = oldSREG;
	
	return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
}

so it's using Timer 0 and it's using a variable timer0_overflow_count that is incremented in the timer ISR():

ISR(TIMER0_OVF_vect)
#endif
{
	// copy these to local variables so they can be stored in registers
	// (volatile variables must be read from memory on every access)
	unsigned long m = timer0_millis;
	unsigned char f = timer0_fract;

	m += MILLIS_INC;
	f += FRACT_INC;
	if (f >= FRACT_MAX) {
		f -= FRACT_MAX;
		m += 1;
	}

	timer0_fract = f;
	timer0_millis = m;
	timer0_overflow_count++;
}

So this micros() (and delay()) rely on interrupts remaining enabled so that the timer overflows can be counted (though there is a very brief disable/re-enable of interrupts while the variable is being accessed - to ensure it is atomic).

 

So this system should not "interfere" with other interrupting processes in the system. HOWEVER it is susceptible to other interrupts too - so if you wrote some other ISR() that took a looonnnggg time then that could interfere with this.

 

To be honest, if you want accurate measurement of time use a timer yourself and your own interrupts and protect that in whatever way necessary to make sure nothing can interfere.

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

Hemanti wrote:

TT_ZX wrote:

delay() is blocking.

 

How can you see this? What exactly do you mean by "blocking" and what implications does it have? How does it impact the Serial communication exactly and how can I get rid off that?

 

 

If you don't want to block, don't use delay().  Do this instead:

 

long time = 0;

void setup() {
  Serial.begin(9600);
}

// the loop function runs over and over again forever
void loop() {
    if (millis() - time > 1000)
    {
        Serial.println("Some text.");
        time = millis();
    }
}

 

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

Thank you clawson, and TT_ZX. So far, I've never implemented timers or interrupts myself, but the time will certainly come. ;)

 

To everyone, my set-up is still working. Thank you for working that out with me. :)

 

Best regards,

Henrik