ATmega2560 – Unable to enter and stay inside while(1){} loop

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

Hello. I am trying to make a simple program that:
1.    Send me one message over USART
2.    Then get stuck in a while loop
 

The problem is that it looks like the program restarts itself all the time because it will send the message continuously. When I am debugging it looks like the program never enters the while loop at all.
 

I have read in another old thread that this behaviour can be caused by a problem with one or more interrupts, but I do not understand how they affect the while loop if they do it at all.
 

Beneath is my main.c. The rest of the code is provided as attachments and on the buttom of this post.

 

// main.c
#include "AVR_sysConfig.h"
#include "AVR_uart.h"

void Init(){
	USART0_init( USART_BAUD_PRESCALE(USART_BAUD_9600) );
}

int main(void)
{
	uint8_t test = 0;
	Init();
	TxUartString("In\n", USART_CHANNEL_0);

	while(1)
	{
		test++;
	}
}

 

I build and flash the program with the help of Microchip Studio 7 (Version: 7.0.2542)

 

Old thread that may be relevant https://www.avrfreaks.net/forum/seems-while1-loop-not-working

 

I hope someone here may can give me a pointer on what is going on and wrong here.

 

Thank you.

- UNIBAR

 

CODE:

// AVR_uart.h

#ifndef AVR_UART_H
#define AVR_UART_H

#include "AVR_sysConfig.h"

#define USART_CHANNEL_0			0
#define USART_CHANNEL_2			2
#define USART_CHANNEL_3			3

#define USART_BAUD_2400			2400
#define USART_BAUD_9600			9600
#define USART_BAUD_38400		38400
#define USART_BAUD_76800		76800
#define USART_BAUD_250000		250000
#define USART_BAUD_500000		500000

#define USART_BAUD_PRESCALE(USART_BAUD) ( ( ( (F_OSC-250)/16 )/ (USART_BAUD) ) -1 )

void USART0_init(unsigned char ubrr);
void USART2_init(unsigned char ubrr);
void USART3_init(unsigned char ubrr);

void USART_TX_byte(char data, unsigned char channel);
char Poll_USART_RX_byte(unsigned char channel);

void Tx_Uart_Buffer(char *TX_buffer, unsigned char channel);
void Tx_USART_buffer_fixed(char *TX_buffer, uint16_t buffer_length, unsigned char channel);
void TxUartString(const char *string, unsigned char channel);

#endif
#include "AVR_uart.h"

void USART0_init(unsigned char ubrr){
	/*******************************************************************************
	Sets the baud rate of UART0
	*******************************************************************************/
	UBRR0L = (unsigned char)(ubrr);
	UBRR0H = (unsigned char)(ubrr >> 8);

	/*******************************************************************************
	Turn on receiver (RXEN0) and transmitter (TXEN0). Enable interrupt for reciver
	*******************************************************************************/
	UCSR0B = (1<<RXCIE0) | (1<<RXEN0) | (1<<TXEN0);

	/*******************************************************************************
  	Sets the frame format: 8 data, 2 stop bit, no parity
	*******************************************************************************/
	UCSR0C = (1<<USBS0) | (1<<UCSZ01) | (1<<UCSZ00);
}

void USART2_init(unsigned char ubrr){
	UBRR2L = (unsigned char)(ubrr);
	UBRR2H = (unsigned char)(ubrr >> 8);

	/*******************************************************************************
	Turn on receiver (RXEN2) and transmitter (TXEN2). Enable interrupt for reciver
	*******************************************************************************/
	UCSR2B = (1<<RXCIE2) | (1<<RXEN2) | (1<<TXEN2); 

	/*******************************************************************************
	Sets the frame format: 8 data, 1 stop bit, no parity
	*******************************************************************************/
	UCSR2C = (1<<UCSZ21) | (1<<UCSZ20);
}

void USART3_init(unsigned char ubrr){
	/*******************************************************************************
	Sets the baud rate of UART0
	*******************************************************************************/
	UBRR3L = (unsigned char)(ubrr);
	UBRR3H = (unsigned char)(ubrr >> 8);

	/*******************************************************************************
	Turn on receiver (RXEN0) and transmitter (TXEN0). Enable interrupt for reciver
	*******************************************************************************/
	UCSR3B = (1<<RXCIE3) | (1<<RXEN3) | (1<<TXEN3);

	/*******************************************************************************
  	Sets the frame format: 8 data, 2 stop bit, no parity
	*******************************************************************************/
	UCSR3C = (1<<UPM31) | (1<<USBS3) | (1<<UCSZ31) | (1<<UCSZ30);
}

void USART_TX_byte(char data, unsigned char channel){
	switch(channel){
		case USART_CHANNEL_0:
			while (!(UCSR0A & (1<<UDRE0)));
				UDR0 = data;
			break;

		case USART_CHANNEL_2:
			while (!(UCSR2A & (1<<UDRE2)));
				UDR2 = data;
			break;
		case USART_CHANNEL_3:
			while (!(UCSR3A & (1<<UDRE3)));
				UDR3 = data;
			break;
	}

}

char Poll_USART_RX_byte(unsigned char channel){
	switch(channel){
		case USART_CHANNEL_0:
			while (!(UCSR0A & (1<<RXC0)));
				return UDR0;
			break;

		case USART_CHANNEL_2:
			while (!(UCSR2A & (1<<RXC2)));
				return UDR2;
			break;
		case USART_CHANNEL_3:
			while (!(UCSR3A & (1<<RXC3)));
				return UDR3;
			break;
		default:
			return -1;
			break;
	}
}

void Tx_Uart_Buffer(char *TX_buffer, unsigned char channel)
{
	do
	{
		USART_TX_byte(TX_buffer[0], channel);
		*TX_buffer++;
	} while (*(TX_buffer - 1) != 0);
}

void Tx_USART_buffer_fixed(char *TX_buffer, uint16_t buffer_length, unsigned char channel)
{
	for (uint16_t i = 0; i < buffer_length; ++i){
		USART_TX_byte(TX_buffer[i], channel);
	}
}

void TxUartString(const char *string, unsigned char channel)
{
	while(*string){
		USART_TX_byte((unsigned char) string[0], channel);
		string++;
	}
}
// AVR_sysConfig.h
#ifndef AVR_SYSCONFIG_H
#define AVR_SYSCONFIG_H

#define F_OSC 16000000
#define F_CPU F_OSC
#define set_bit(reg,bit) (reg |= (1 << bit))
#define clear_bit(reg,bit) (reg &= ~(1 << bit))
#define test_bit(reg,bit) (reg & (1 << bit))

/*IO registers*/
#define DDRA_REG 1
#define DDRB_REG 2
#define DDRC_REG 3
#define DDRD_REG 4
#define DDRE_REG 5
#define DDRF_REG 6
#define DDRG_REG 7
#define DDRH_REG 8
#define DDRL_REG 9

#define PORTA_REG 1
#define PORTB_REG 2
#define PORTC_REG 3
#define PORTD_REG 4
#define PORTE_REG 5
#define PORTF_REG 6
#define PORTG_REG 7
#define PORTH_REG 8
#define PORTL_REG 9

/*Standard C libraries*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

/*AVR libraries*/
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/sleep.h>
#include <util/delay.h>
#include <util/twi.h>

#include "AVR_interrupt.h"

#endif
#ifndef AVR_INTERRUPT_H_
#define AVR_INTERRUPT_H_

#include <avr/interrupt.h>
#include <avr/wdt.h>

void Enable_global_interrupt();
void Disable_global_interrupt();

#endif /* D_INTERRUPT_H_ */
// AVR_interrupt.c

#ifndef AVR_INTERRUPT_H_
#define AVR_INTERRUPT_H_

#include <avr/interrupt.h>
#include <avr/wdt.h>

void Enable_global_interrupt();
void Disable_global_interrupt();

#endif /* D_INTERRUPT_H_ */
#include "AVR_interrupt.h"

void Enable_global_interrupt(){
	sei();
}

void Disable_global_interrupt(){
	cli();
}

Edited 09.06 - Added more code

Attachment(s): 

Last Edited: Wed. Jun 9, 2021 - 05:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Welcome to AVR Freaks!

 

Could the WDTON fuse be set (low)? Sure sounds like watchdog!

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

Last Edited: Tue. Jun 8, 2021 - 09:19 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

IIRC recent C standards give the compiler license to assume that some infinite loops,

including some that do not involve undefined behavior, actually end.

I'm not sure which or why.

Again IIRC, returning from main with the GNU toolchain will eventually cause a watchdog reset.

Moderation in all things. -- ancient proverb

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

IIRC recent C standards give the compiler license to assume that some infinite loops,

including some that do not involve undefined behavior, actually end.

I remember reading that this was the case, or at least that it had been proposed, but I don't think that the behavior has actually been seen in the wild...

 

returning from main with the GNU toolchain will eventually cause a watchdog reset.

returning from main() in a normal gcc program will enter an infinite loop, which is subject to WDT reset (iff WDT is enabled.)
http://svn.savannah.gnu.org/view...

 

What it's trying to do can be easily determined by looking at the object code produced.  (ie you can check whether the while loop has been omitted by optimization.)

 

 

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

unibar wrote:
I build and flash the program with the help of Microchip Studio 7

So have you tried using the debugger to see what's happening ?

 

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

Can you show the implementations of 

USART0_init

and

TxUartString

Also show the disassembled code listing, this shows what instructions the controller is actually executing.

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

unibar wrote:
The rest of the code is provided as attachments and on the buttom of this post.
Except it isn't?

 

The .h files are all very interesting but it's the code that is the .c files (and probably one called AVR_uart.c?) that is important.

 

The fact that you are also showing AVR_interrupt.h presuumably means that what goes on "behind" USART0_init() and TxUartString() is involving interrupt transmission?

 

The classic error when using avr-gcc and interrupts is where you enable some interrupt source but you do not provide an ISR() (or perhaps you attempt to but mis-spell the _vect name?). See "catch-all" on this page of the manual:

 

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

 

As that suggests try add a non-restarting ISR(BADISR_vect) to over-ride the default action that does involve restarting the program. Does it stop sending multiple messages.

 

Actually the fact you seem to be saying that you receive the whole message repeatedly rather than (perhaps) just the 'I' may suggest it is you "end of string/transmission" handling that is at fault.

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

ka7ehk wrote:

Welcome to AVR Freaks!

 

Could the WDTON fuse be set (low)? Sure sounds like watchdog!

 

Jim

 

Thank you.
I checked this and it was actually checked off ([X]). Unfortunately, changing this does not solve the problem.
Here are my current settings for Fuses:

 

BODLEVEL = 1V8
OCDEN = [ ]
JTAGEN = [X]
SPIEN = [X]
WDTON = [ ]
EESAVE = [X]
BOOTSZ = 4096W_1F000
BOOTRST = [X]
CKDIV8 = [ ]
CKOUT = [ ]
SUT_CKSEL = EXTXOSC_8MHZ_XX_16KCK_0MS

EXTENDED = 0xFE (valid)
HIGH = 0x90 (valid)
LOW = 0xDF (valid)

 

awneil wrote:

unibar wrote:
I build and flash the program with the help of Microchip Studio 7

So have you tried using the debugger to see what's happening ?

 

Yes, when I am using the debugger it will also reset. I needed to change the test variable to volatile to be able to pause the program inside the loop. I added a line to the bottom of main.c that never gets printed out.

 

 

ccrause wrote:

Can you show the implementations of 

USART0_init

and

TxUartString

Also show the disassembled code listing, this shows what instructions the controller is actually executing.

 

Yes, when I am using the debugger it will also reset. I needed to change the test variable to volatile to be able to pause the program inside the loop. I added a line to the bottom of main.c that never gets printed out.

Is it this?

int main(void)
{
00000597  PUSH R28		Push register on stack
00000598  PUSH R29		Push register on stack
00000599  PUSH R1		Push register on stack
0000059A  IN R28,0x3D		In from I/O location
0000059B  IN R29,0x3E		In from I/O location
	uint8_t volatile test = 0; // If
0000059C  STD Y+1,R1		Store indirect with displacement
	Init();
0000059D  RCALL PC-0x0009		Relative call subroutine
	TxUartString("In123456789\n", USART_CHANNEL_0);
0000059E  LDI R22,0x00		Load immediate
0000059F  LDI R24,0x01		Load immediate
000005A0  LDI R25,0x02		Load immediate
000005A1  RCALL PC-0x031D		Relative call subroutine
		test++;
000005A2  LDD R24,Y+1		Load indirect with displacement
000005A3  SUBI R24,0xFF		Subtract immediate
000005A4  STD Y+1,R24		Store indirect with displacement
000005A5  RJMP PC-0x0003		Relative jump 

 

clawson wrote:

unibar wrote:
The rest of the code is provided as attachments and on the buttom of this post.
Except it isn't?

 

The .h files are all very interesting but it's the code that is the .c files (and probably one called AVR_uart.c?) that is important.

 

The fact that you are also showing AVR_interrupt.h presuumably means that what goes on "behind" USART0_init() and TxUartString() is involving interrupt transmission?

 

The classic error when using avr-gcc and interrupts is where you enable some interrupt source but you do not provide an ISR() (or perhaps you attempt to but mis-spell the _vect name?). See "catch-all" on this page of the manual:

 

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

 

As that suggests try add a non-restarting ISR(BADISR_vect) to over-ride the default action that does involve restarting the program. Does it stop sending multiple messages.

 

Actually the fact you seem to be saying that you receive the whole message repeatedly rather than (perhaps) just the 'I' may suggest it is you "end of string/transmission" handling that is at fault.

I tried adding this code to main.c

 

ISR(BADISR_vect)
{
	;//
}

Unfortunately, this does not change the behavior of the program. However, I have found a dirty trick that prevents the reset but does not actually solve the problem. This is adding wdt_disable(); inside the loop. See the main.c beneath. Only adding wdt_disable() once at the top of main.c does not solve the problem. I therefore may believe that I am only resetting the watchdog each time I call this function.

I think I have added all the source files to the first post now.

This is a project that contained files with interrupts before I remove them from main.c. Is it possible that they somehow still can be in my program although I am not longer including them in/from main? AVR_interrupt.h is including <avr/wdt.h>. If I try to compile without including “AVR_interrupt.h” (from AVR_sysConfig.h) the compiler throws an error from a file that I no longer include.

 

#include "AVR_sysConfig.h"
#include "AVR_uart.h"

void Init(){
	USART0_init( USART_BAUD_PRESCALE(USART_BAUD_9600) );
}

ISR(BADISR_vect)
{
	;//
}

int main(void)
{
	uint8_t volatile test = 0; //
	Init();
	TxUartString("In123456789\n", USART_CHANNEL_0);
	while(1)
	{
		test++;
		wdt_disable(); // This line is preventing reset / escape from while loop.
	}
	TxUartString("This is never printed.", USART_CHANNEL_0); 

}

 

Otherwise, I would really like to say thank you to everybody for helping me investigating this problem and teach me a thing or two about avr and embedded programming.

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

unibar wrote:

while(1)
	{
		test++;
		wdt_disable(); // This line is preventing reset / escape from while loop.
	}
	TxUartString("This is never printed.", USART_CHANNEL_0); 

Your string will not print if your USART TX is interrupt driven, because as soon as the buffer is filed, the code falls into a forever loop with the interrupts turned off, that happens before the first character can be sent.

 

Add a call to delay() for a few seconds before falling off the end, to give time for the msg to be sent.

 

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

Yes, C++ compilers are allowed to throw away any loop which does not read or write to volatile values or modify something and doesn't have an end condition, and that is a real optimization that sometimes shows up. That said, I don't think it's what's happening here.

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

the_real_seebs wrote:
Yes, C++ compilers are allowed to throw away any loop which does not read or write to volatile values or modify something and doesn't have an end condition, and that is a real optimization that sometimes shows up. That said, I don't think it's what's happening here.

No, that cannot be right. If there's an infinite loop in the code, the compiler cannot just throw it away as that would change the behaviour completely. What is happening is that the statements within the loop are removed since they have no effect - in this case the increment of the local variable "test". The loop itself will remain though.

/Jakob Selbing

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

You have a debugger ! Solving this should be easy.

 

  1. Place a breakpoint at the earliest point of main() you can
  2. Run you program and let the unwanted reset occur
  3. Examine MCUSR – MCU Status Register

 

The MCU Status Register provides information on which reset source caused an MCU reset. See Datasheet Section 12.5.1

 

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

jaksel wrote:
the_real_seebs wrote:
Yes, C++ compilers are allowed to throw away any loop which does not read or write to volatile values or modify something and doesn't have an end condition, and that is a real optimization that sometimes shows up. That said, I don't think it's what's happening here.

No, that cannot be right. If there's an infinite loop in the code, the compiler cannot just throw it away as that would change the behaviour completely. What is happening is that the statements within the loop are removed since they have no effect - in this case the increment of the local variable "test". The loop itself will remain though.

Removing ineffective stuff from the body would just be an application of the ancient as-if rule.

The new rule to which I referred would allow removing the loop itself, an obvious change of semantics.

The motivation is not at all clear to me.

 

In any case, the program under discussion is C, not C++, so the issue is moot.

Moderation in all things. -- ancient proverb

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

You'd think, but the forward progress guarantee does in fact exist.

 

And I mention it because a lot of Arduino-like stuff ends up compiling-as-C++ even when the code is C.

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

N.Winterbottom wrote:

You have a debugger ! Solving this should be easy.

 

  1. Place a breakpoint at the earliest point of main() you can
  2. Run you program and let the unwanted reset occur
  3. Examine MCUSR – MCU Status Register

 

The MCU Status Register provides information on which reset source caused an MCU reset. See Datasheet Section 12.5.1

 

Hello, thank you for the tips. This solved the main problem. I implemented the example code as described in the datasheet this solved the problem with my program. The program is now stuck in the while(1) loop as desired. The watchdog is not triggering a restart.

The code I implemented. (Datasheet ch 12.4.2)

 

void WDT_off(void){
	//__disable_interrupt();
	cli();
	//__watchdog_reset();
	wdt_reset();
	/* Clear WDRF in MCUSR */
	MCUSR &= ~(1<<WDRF);
	/* Write logical one to WDCE and WDE */
	/* Keep old prescaler setting to prevent unintentional time-out */
	WDTCSR |= (1<<WDCE) | (1<<WDE);
	/* Turn off WDT */
	WDTCSR = 0x00;
	//__enable_interrupt();
	sei();
	}

However, this leads me to a new question.
I first tried to use wdt_disable() defined in <avr\wdt.h>. This is not working as expected. The WDRF bit in MCUSR is not set to zero and the watchdog will trigger a reset. wdt_disable() is not defined in https://www.nongnu.org/avr-libc/.... Do I use a header file that someone else have created instead of the “original” wdt.h ? I would have appreciated if someone can clarify this.

Path to the file: \Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\avr\include\avr\wdt.h

 

-    Unibar