Help needed getting ATmega324PB code to compile in Atmel Studio

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

I need some help getting my code to compile.  The short version:  I'm using Atmel Studio 7 and an ATmega324PB.  Unfortunately it seems support for the device isn't (fully?) in the toolchain yet.  What do I have to update/do in order to support it?

 

 

The longer version:  I have a small project using the ATmega324PB, and have just got a board assembled with the device on board.  I've starting writing some simple test code to make sure the hardware works, but have just discovered the toolchain doesn't support(?) the ATmega324PB yet.  My test code is:

 

#define F_CPU		20000000UL
#define UART_BAUD_RATE	250000UL
#define MAX_PARMS	4

#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/power.h>


volatile uint8_t timer1CompAFlag = 0;
uint8_t loopCount = 0, stageCount = 0;

int main(void)
{
	// Port A is wired to J9
	DDRA = 0xFF;
	PORTA = 0x00;
	
	//  Current clock internal 8 MHz oscillator, with /8 divider -> 1 MHz

	//	Timer 1 setup for CTC mode, 25 ms tick (40 Hz)
	OCR1A = 0xF423;								// At 20 MHz clk, 25 ms tick / 1 MHz -> 500 ms
	TCCR1A = 0x00;								// Outputs off, Mode 4: CTC
	TCCR1B = 0x0A;								// Mode 4: CTC, /8 prescale
	TIMSK1 = 0x02;								// Enable Timer 1 output compare match A interrupt


	// Enable interrupts
	sei();


    /* Replace with your application code */
    while (1) 
    {
		if (timer1CompAFlag == 1)
		{
			timer1CompAFlag = 0;
			PINA = 0xFF;
			if ((stageCount == 0) && (loopCount == 8) )
			{
				loopCount = 0;
				stageCount = 1;
				clock_prescale_set(clock_div_1);
			}
			else if (loopCount == 8)
			{
				loopCount = 0;
			}
		}
    }
}

ISR(TIMER1_COMPA_vect)
{
	timer1CompAFlag = 1;
}

 

 

The first problem I had is the warning/error messages:
 

implicit declaration of function 'clock_prescale_set' [-Wimplicit-function-declaration]
'clock_div_1' undeclared (first use in this function)

which led me to examining power.h in 'C:\Program Files\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\avr\include\avr'.  As far as I can tell, it lacks an "if defined" statement for __AVR_ATmega324PB__, so the first issue I have is that the power.h header, and likely everything else, isn't updated with support for the 324PB.

 

Digging a little more, I can't find out where __AVR_ATmega324PB__ *should* be defined.  I initially thought it would be defined in 'iom324pb.h', but that file doesn't exist.  I now can't understand why the compiler *does* recognise macros such as TCCR1A.  If I comment out

 

#include <avr/io.h>

 

the compiler doesn't recognise the macros, but I all I can tell is that io.h should call an include to iom324pb.h and it doesn't.  So where are those macros being defined?  Also, io.h already expects __AVR_ATmega324PB__ to be defined, so I'm at a loss to where to find that.

 

I did some more digging, and I appear to have toolchain v3.6.1.1750, and found v3.6.2.1759 on the Microchip website.  I downloaded and unziped it, and still no iom324pb.h.  That said, I don't know exactly what I need to do to update the toolchain anyway.

 

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

sevenbitshort wrote:
Digging a little more, I can't find out where __AVR_ATmega324PB__ *should* be defined.  I initially thought it would be defined in 'iom324pb.h',
No, no. It's the other way round. It's actually defined "inside" the compiler. When you invoke the compiler with -mmcu=atmega324pb then there's a lookup table inside the compiler (if late enough) that should know this value and as a result it then auto-defines the prepro symbol __AVR_ATmega324PB__ then a large conditional in <avr/io.h> arranges that because __AVR_ATmega324PB__ is defined it then picks to include iom324pb.h out of all the .h available.

 

But note that for a while now the compilers have been structured so it's easier to add new models of AVR after the compiler has been built (saves reissuing the compiler every time new devices are launched!) and this is done with "device packs" (and they are introduced to avr-cc using the -B option. So support for "newish" chips like 324PB probably comes from a device pack. So if your device packs are out of date you may not have everything required. Tools-Device Pack Manager... can be used to make sure your packs are up to date.

 

Having said that my packs are (I believe) up to date and yet when I try to build your code for 324PB I get the same error.

 

To be honest I don't know how the "packs" thing is supposed to handle a "shared" header like power.h. I'm guessing that instead of using C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\avr\include\avr\power.h when a "pack" is involved it should be using an updated copy of the file from the pack location.

 

Perhaps Morten from Atmel who does this stuff will be along in a while to explain more??

 

BTW when it compiles I see:

"C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-gcc.exe"  -x c -funsigned-char -funsigned-bitfields -DDEBUG  -I"C:\Program Files (x86)\Atmel\Studio\7.0\Packs\Atmel\ATmega_DFP\1.3.300\include"  -O1 -ffunction-sections -fdata-sections -fpack-struct -fshort-enums -mrelax -g2 -Wall -mmcu=atmega324pb -B "C:\Program Files (x86)\Atmel\Studio\7.0\Packs\Atmel\ATmega_DFP\1.3.300\gcc\dev\atmega324pb" -c -std=gnu99 -save-temps -MD -MP -MF "main.d" -MT"main.d" -MT"main.o"   -o "main.o" ".././main.c" 

I need to check if there's an updated power.h somewhere close by.

 

EDIT: OK so I went back uyp a few directory levels and in the region of "packs" there's no sign of power.h. This all seems to be an oversight in the whole pack support thing. If power.h contains stuff like:

#if defined(__AVR_AT90CAN32__) \
|| defined(__AVR_AT90CAN64__) \
|| defined(__AVR_AT90CAN128__) \
|| defined(__AVR_AT90PWM1__) \
|| defined(__AVR_AT90PWM2__) \
|| defined(__AVR_AT90PWM2B__) \
|| defined(__AVR_AT90PWM3__) \
|| defined(__AVR_AT90PWM3B__) \
|| defined(__AVR_AT90PWM81__) \
|| defined(__AVR_AT90PWM161__) \
|| defined(__AVR_AT90PWM216__) \
|| defined(__AVR_AT90PWM316__) \
|| defined(__AVR_AT90SCR100__) \
|| defined(__AVR_AT90USB646__) \
|| defined(__AVR_AT90USB647__) \
|| defined(__AVR_AT90USB82__) \
|| defined(__AVR_AT90USB1286__) \
|| defined(__AVR_AT90USB1287__) \
|| defined(__AVR_AT90USB162__) \
|| defined(__AVR_ATA5505__) \
|| defined(__AVR_ATA5272__) \
|| defined(__AVR_ATA6617C__) \
|| defined(__AVR_ATA664251__) \
etc etc

it IS going to need updating/over-riding from time to time

Last Edited: Thu. Mar 14, 2019 - 02:34 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hrm, so the point is that power.h should be using the __AVR_HAVE_ defines (if you look at the top 2/3s of the power.h file) to define features. These __AVR_HAVE_ comes from the Pack. 

 

But... I don't know why there's a big list of devices here... I'm guessing that the 324 works similar to the 328, so just to get you going you could do this;

#include <avr/interrupt.h>
#define __AVR_ATmega328P__
#include <avr/power.h>
#undef __AVR_ATmega328P__

I'll have to loop the compiler guys in here to check what the intention was... Might be as simple as 'we forgot to upstream this device', as it was one of the last 'classic' ATmegas.... (DEVXML-2133)

:: Morten

 

(yes, I work for Atmel, yes, I do this in my spare time, now stop sending PMs)

Last Edited: Thu. Mar 14, 2019 - 03:25 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

meolsen wrote:
should be using the __AVR_HAVE_ defines
But it does that for power_xxx_enable/disable() but clock_prescale_set() has not been done in that way :-(

 

Presumably there should be a __AVR_HAVE_CLKPR and then that should be used ?

 

Only thing is that all the __AVR_HAVE_ in iom324pb.h all concern PRR registers, nothing else.

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

Only thing is that all the __AVR_HAVE_ in iom324pb.h all concern PRR registers, nothing else.

Exactly, so not sure why this happened... The clock_div_t enum, clock_prescaler_set and clock_prescaler_get is not handled in this way... I've pinged the toolchain guys and asked them :)

:: Morten

 

(yes, I work for Atmel, yes, I do this in my spare time, now stop sending PMs)

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

OK, so checking my project properties in Atmel Studio, it uses the compiler options:

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

Effectively the same -I and -B flags as yours.  (Took me a while to find what they do).

 

So if I have this correct: When I start project with AS and select my device, it sets '-mmcu=atmega324pb' as a complier option for the project, which defines __AVR_ATmega324PB__.  I include <avr/io.h> which is located in:

C:\Program Files\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\avr\include\avr

And that includes the iom324pb.h file (found it!) in:

C:\Program Files\Atmel\Studio\7.0\packs\atmel\ATmega_DFP\1.3.300\include\avr

So the first is the toolchain and the second is the in the ATmega device pack?  I'm curious as to what happens if I were using say for example a mega88, and the two locations have differing iom88.h files.  Which one would get included?

 

I did a search of the device pack directory, and there's no power.h (or io.h) in there.

 

 

meolsen wrote:

...

But... I don't know why there's a big list of devices here... I'm guessing that the 324 works similar to the 328, so just to get you going you could do this;

#include <avr/interrupt.h>
#define __AVR_ATmega328P__
#include <avr/power.h>
#undef __AVR_ATmega328P__

 

Thanks.  I was actually thinking of making a copy of power.h, adding in my own

|| defined(__AVR_ATmega324PB__) \

in the correct block and including this modified header, but that is a more elegant solution.  I will have to do a comparison to make sure the clock module, or rather the clock registers, are the same I guess.

 

 

 

Some other minor things:  Looking at the 324PB datasheet, on page 24 section 8.6, it says:

For a write operation, the low byte of the 16-bit register must be written before the high byte. The low byte is then written into the temporary register. When the high byte of the 16-bit register is written, the temporary register is copied into the low byte of the 16-bit register in the same clock cycle.

 

For a read operation, the low byte of the 16-bit register must be read before the high byte. When the low byte register is read by the CPU, the high byte of the 16-bit register is copied into the temporary register in the same clock cycle as the low byte is read. When the high byte is read, it is then read from the temporary register.

 

And on page 135:

18.3 Accessing 16-bit Timer/Counter Registers

 

The TCNTn, OCRnA/B, and ICRn are 16-bit registers that can be accessed by the AVR CPU via the 8-bit data bus. The 16-bit register must be accessed byte-wise, using two read or write operations. Each 16-bit timer has a single 8-bit TEMP register for temporary storing of the high byte of the 16-bit access. The same temporary register is shared between all 16-bit registers within each 16-bit timer.

 

Accessing the low byte triggers the 16-bit read or write operation: When the low byte of a 16-bit register is written by the CPU, the high byte that is currently stored in TEMP and the low byte being written are both copied into the 16-bit register in the same clock cycle. When the low byte of a 16-bit register is read by the CPU, the high byte of the 16-bit register is copied into the TEMP register in the same clock cycle as the low byte is read, and must be read subsequently.

 

Note: To perform a 16-bit write operation, the low byte must be written before the high byte. For a 16-bit read, the low byte must be read before the high byte.

 

I have datasheet version DS40001908A dated Jan 2017, which is also what's currently available on the web site.  I also have an older Atmel datasheet for the 1284P and it's unambiguous (as far as I know) about writing the high byte first for a 16-bit write operation.  I wouldn't have expected it to be, but is the 324PB actually different in this regard?  Granted, I'll just treat the register as a uint16_t, but I'm then trusting that the complier gets it right.

 

Also, I didn't see mention of the 328PB in power.h either.  I suspect it may suffer the same library support issues.

 

 

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

Yes, tread carefully when defining that define. I haven't checked if it makes sense for this device...

:: Morten

 

(yes, I work for Atmel, yes, I do this in my spare time, now stop sending PMs)

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

sevenbitshort wrote:
I have datasheet version DS40001908A dated Jan 2017, which is also what's currently available on the web site. I also have an older Atmel datasheet for the 1284P and it's unambiguous (as far as I know) about writing the high byte first for a 16-bit write operation. I wouldn't have expected it to be, but is the 324PB actually different in this regard? Granted, I'll just treat the register as a uint16_t, but I'm then trusting that the complier gets it right. Also, I didn't see mention of the 328PB in power.h either. I suspect it may suffer the same library support issues.

 

Well, sometime MC/Atmel Datasheets.....are....., I dont know what is the right word ? crap

 

 

meolsen wrote:
I've pinged the toolchain guys and asked them :)

 

Great, now ping also the documentation and datasheet guys and tell them to stop copy/paste specially for Tiny1 and Tiny0 series and do a little bit more effort.