AVR ATmega32 - C - Gets stuck in function call


hundan

Still Fresh
Joined
Nov 30, 2017
Messages
3
Age
29
Hi to all, I had met some problems in my project, I'm trying to find the error, but it's difficult for me. The situation is following, I have had lots of problems trying to get a piece of firmware to work which has worked fine earlier, and is working on a copy of the board I'm having problems with. I've narrowed in down to the following (at least to start with):

This piece of code works:

Code:
#define F_CPU 8000000

#include <avr/io.h>
#include <util/delay.h>

int main(void)
{   
   // LEDs - initial setting
   DDRC |= 0x03;                // LEDs on pins PC0 and PC1
   PORTC &= ~(1<<0);             // LED0 OFF
   PORTC |= (1<<1);             // LED1 ON

   DDRD |= 0x30;    // PD4..5 OUT
   TCCR1A = 0xE2;   //0b11100010 (Set OC1A + Clear OC1B on compare match, )
   TCCR1B = 0x19;   //0b00011001 (WGM13:10 = 0b1110 => Fast PWM Mode, TOP = ICR1, CS12:0 = 001 => Use IO clock, no prescaling)
   OCR1A = 18;
   OCR1B = 18;      //  18 / 8000000 =  2.25 us (Output compare match value (OC1A->HIGH, OC1B->LOW)
   ICR1 = 757;      // 757 / 8000000 = 94.625us (TOP value for timer1)

   // Loop blinks LED0..1 alternately
   while(1)
   {
      PORTC = PORTC ^ 0x03;
      _delay_ms(250);
   }
}
It starts the timer and outputs the pulse with the specified timing, and enters the blinking LED loop.

However, this code fails:

Code:
#define F_CPU 8000000

#include <avr/io.h>
#include <util/delay.h>

void pulse_init(void)
{   
   DDRD |= 0x30;    // PD4..5 OUT
   TCCR1A = 0xE2;   //0b11100010 (Set OC1A + Clear OC1B on compare match, )
   TCCR1B = 0x19;   //0b00011001 (WGM13:10 = 0b1110 => Fast PWM Mode, TOP = ICR1, CS12:0 = 001 => Use IO clock, no prescaling)
   OCR1A = 18;
   OCR1B = 18;      //  18 / 8000000 =  2.25 us (Output compare match value (OC1A->HIGH, OC1B->LOW)
   ICR1 = 757;      // 757 / 8000000 = 94.625us (TOP value for timer1)
}


int main(void)
{   
   // LEDs - initial setting
   DDRC |= 0x03;                // LEDs on pins PC0 and PC1
   PORTC &= ~(1<<0);            // LED0 OFF
   PORTC |= (1<<1);         // LED1 ON

   pulse_init();

   // Loop blinks LED0..1 alternately
   while(1)
   {
      PORTC = PORTC ^ 0x03;
      _delay_ms(250);
   }
}
...it's just the same statements as before, moved to the pulse_init() function. When I upload this code the timer is set up correctly and the output pulse is OK, but the program never starts blinking the LEDs on PC0..1.

Any ideas? It's not just this function, this is part of a bigger program with other initalization functions and it seems whenever the program enters a function separate from the main loop it fails to return to main. I am not so familiar with C and how it works on the function block level. I am using Atmel Studio 7 now with the following compiler options:

Code:
-x c -funsigned-char -funsigned-bitfields -DDEBUG  -I"C:\Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.1.130\include"  -O1 -ffunction-sections -fdata-sections -fpack-struct -fshort-enums -g2 -Wall -Wextra -pedantic -mmcu=atmega32 -B "C:\Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.1.130\gcc\dev\atmega32" -c -std=gnu99 -MD -MP -MF "$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -MT"$(@:%.o=%.o)"

but I had the same problems with avrdude. I also tried reading the full binary from the working board and writing it to the board I'm working on. It should communicate with PC over a USB-to-serial but it doesn't work. When I power up the board it spews a few kilobytes on nonsense to the serial (the same nonsense every time, though) and then freezes, i.e. doesn't send the regular "Waiting for command" message or reply to any commands.

The chip is Atmega32L8AU, 8MHz external crystal, LFUSE=0xFF, HFUSE=0x99.

Any ideas? Sorry to my terrible edit.:$

Thank you for all your help!
 
Maybe try to define that pulse_init() function as volatile. The compiler is maybe detecting that function as dead code and is not compiling it?
Code:
volatile void pulse_init(void)
{
...
}
 
Code looks okay to me. If in doubt, disassemble the binaries (don't know if Atmel Studio can help you there, but it should come with objdump as part of the GNU toolchain) and do a side-by-side comparison. Should only be a couple dozen of assembler lines each for the examples you posted.

If the code fails only on your "new" board, I would suspect the board to be the problem. It might also be that the flashing is unreliable or that the fuse bytes are incorrect. Have you tried reading back the flash/fuses to see if they are identical?

The garbage on the UART sounds like a baud-rate mismatch issue to me, which would also be related to the clock and relevant fuse bits.
 
May also be worth checking that all of those variable names are defined as globals in avr.io.h. I'm looking specifically at those variables starting TCCR1A and ending ICR1. If not they'll be automatically declared as local to main() (in the original program) or local to pulse_init() (in your modified program) which might be the change that breaks the camel's back.
 
I wouldn't know by looking at your code but you could try some checks to see if you can make sure where the error is coming from.

You have some reasons why this stuff is failing like: board, processor, fuse-bits, compiler options and your code.
So you could perform some checks to see if the board, processor, fuse-bits and compiler settings appear to be correctly set.
For example: create a really really simple application that does have some volume, like 100 copy pastes of the same if-statement that is calling a function and in the end switches a LED or prints something to the UART.
Run it on the board and see if that works, if it doesn't you should check what might have happened there.

In the datasheet you see the fusebit options, sometimes they are inverted values (I think they are noted with HIGH or underlined or something), so see if you got those correct.
Compiler options for me only messed stuff up when I have a high optimization value, I think you are using 01 which is probably off/minimal, so that should be okay.

If you suspect the code to be wrong you could always try to remove the import and replace all linked code in your file with the code from the removed imported files (for example replace DDRD with the actual address).
 
Smells like a compiler bug.

You'd be surprised how faulty many C compilers are, especially in the embedded world. I have used GCC a lot over the past years and never ran into a single bug, but now that my job demands that I work with automotive grade commercial compilers I never heard of before with all sorts of safety certifications I'm running into them every other week - some of them are so plain stupid that it makes you wonder how they never came up before...
 
How often does the same bug turn up in two different compilers though?
Ehrm... avrdude is a programmer, not a compiler. I don't see any alternative compiler being mentioned in this thread. (Actually, I don't even spot a single compiler, only an IDE)
 
Maybe try to define that pulse_init() function as volatile. The compiler is maybe detecting that function as dead code and is not compiling it?
Code:
volatile void pulse_init(void)
{
...
}
Thank you!
 
I would be surprised if the "volatile" had any effect here.

I did some experiments in the meantime and maybe accidentally reproduced this problem: If you forget to set the "-mmcu=atmega32" option in the linker options as well, the init code is not linked. That means that a) the program is not properly initialized, and b) execution starts at the first code encountered in the program rather than at the init code that eventually calls "main".

That would explain why the first example works while the second one fails. In the first example, "main" is the the only code, so execution would start there. The program does not use interrupts, the stack or initialized data, so the missing init code would not be noticed.

In the second example, the first code is "pulse_init", so execution would start there. This would explain why the PWM output is set up correctly. After that, when trying to return to a non-existing caller, the program would likely crash or reset and start over.

To verify this, run "avr-objdump -d filename" on the linked elf file, or "avr-objdump -D -m avr -b binary filename" on the raw binary. The code should start with a bunch of "jmp" instructions (the interrupt vector). If not, the program is not linked correctly.
 
I also tried reading the full binary from the working board and writing it to the board I'm working on. It should communicate with PC over a USB-to-serial but it doesn't work. When I power up the board it spews a few kilobytes on nonsense to the serial (the same nonsense every time, though) and then freezes, i.e. doesn't send the regular "Waiting for command" message or reply to any commands.

hmn's theory does sound plausible, but I can see in your compile options you are including the -mmcu option. What you say here sounds like there may be an issue with the hardware. Can you tell us more about the hardware setup? Is it a custom board?
 
This is not particularly relevant, but the -mmcu option is a compiler option which specifies the instruction being used.
gcc is not just the compiler, it is the front-end for the whole toolchain. gcc translated these kind of options into the equivalent of the responsible option being passed to the programs doing the actual work, the compiler just happens to be an embedded part of the gcc binary. This option is neither limited to choosing only the instruction set for the binary output nor is it only being processed for the compiling step. The GCC documentation simply fails to describe those architecture-specific options properly, this kind of behavior is just being implied. There's a difference between options telling GCC how to link and options being actually passed to the linker.

You may want to directly call cc, as and ld instead - but the GCC guys heavily discourage from doing so for good reasons. That option is one of them: it is being processed by gcc, ld only links those files it is explicitly told to link.
 
I thought invoking the linker directly was standard once you'd written a makefile for your project, because then you can only rebuild what's changed, be it a source file which changes an object file, or an object file individually. You still need to link all of the object files, even if you only touched one of them, so the linker links different things to what's been built in most make builds apart from the first one.

But that's for general computing uses of gcc/make; I don't know if the same holds when you're building for an embedded processor project like this.
 
You don't need to call the linker directly for that, you just need to separate the linking process into a separate step. As a front-end gcc can not just only do everything in one step, it can also perform linking only without having to invoke ld directly - it can decide what to do solely based on the input file type. And that's exactly what most build environments do, they call gcc for every isolated step of the whole process.

Some compilers follow the one-binary-fits-all concept, others keep everything separated up to the point that they even provide a standalone front-end that is actually pretty much useless. GCC is somewhere in between - but it's lacking a clear separation about what is responsible for which part of the whole process.
 
Last edited:
This is informative. But do you think that hundan's issue is related to compilation/linking problems? From the description, hundan has placed identical binaries into two (I'm assuming) identical pieces of hardware. One works, one doesn't. Sounds like a hardware issue to me.
 
From the description, hundan has placed identical binaries into two (I'm assuming) identical pieces of hardware.
He tried to extract the content of the flash and put that into the flash of the other board - that's something that easily goes wrong if you no longer have the address information and other meta-data contained in the pre-flash ELF file.
 
Back
Top