Calling across projects yields undefined reference error

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

I'm using Atmel Studio 7 with ASF4.  I'd like to have two different projects in the solution, one as the Atmel Start project and the other as the Main project which calls the Start.  In Start I have a DMA controller configured with dma_memory.h/c, hpl_dma.h, and hpl_dmac.c present.  I have files dmac_intf.h/c in Main which make calls to dma_memory.c.  A good example is registering callbacks:

 

dmac_intfc.c: (In Main)

int32_t dmac_init(void)
{
   dma_memory_init(); // Use dma_memory.c to initialize the DMA Controller.

   dma_memory_register_callback(DMA_MEMORY_SUSPEND_CB, dma_suspend);
   dma_memory_register_callback(DMA_MEMORY_COMPLETE_CB, dma_transfer_complete);
   dma_memory_register_callback(DMA_MEMORY_ERROR_CB, dma_transfer_error);

   return ERR_NONE;
}

dma_memory.h: (In Start)

/**
 * \brief memory with dma callback type
 */
typedef void (*dma_memory_cb_t)(struct _dma_resource*);

/**
 * \brief Memory with DMA callback types
 */
enum dma_memory_callback_type { DMA_MEMORY_SUSPEND_CB, DMA_MEMORY_COMPLETE_CB, DMA_MEMORY_ERROR_CB };

/**
 * \brief Memory with DMA callbacks
 */
struct dma_memory_callbacks {
	dma_memory_cb_t suspend;
	dma_memory_cb_t transfer_complete;
	dma_memory_cb_t transfer_error;
};

/**
 * \brief Initialize Memory with DMA
 *
 * \return Initialization status.
 */
int32_t dma_memory_init(void);

/**
 * \brief Register Memory with DMA callback
 *
 * \param[in] type Callback type
 * \param[in] cb A callback function, passing NULL de-registers callback
 *
 * \return The status of callback assignment.
 * \retval ERR_INVALID_ARG Passed parameters were invalid
 * \retval ERR_NONE A callback is registered successfully
 */
int32_t dma_memory_register_callback(const enum dma_memory_callback_type type, dma_memory_cb_t cb);

dma_memory.c: (In Start)

/**
 * \brief Initialize DMA
 */
int32_t dma_memory_init(void)
{
	_dma_get_channel_resource(&descr.resource, CONF_DMA_MEMORY_CHANNEL);
	descr.resource->dma_cb.suspend           = dma_suspend;
	descr.resource->dma_cb.transfer_complete = dma_transfer_done;
	descr.resource->dma_cb.transfer_error    = dma_memory_error;

	return ERR_NONE;
}

/**
 * \brief Register DMA callback
 */
int32_t dma_memory_register_callback(const enum dma_memory_callback_type type, dma_memory_cb_t cb)
{
	switch (type) {
   case DMA_MEMORY_SUSPEND_CB:
   	descr.memory_cb.suspend = cb;
   	break;

	case DMA_MEMORY_COMPLETE_CB:
		descr.memory_cb.transfer_complete = cb;
		break;

	case DMA_MEMORY_ERROR_CB:
		descr.memory_cb.transfer_error = cb;
		break;

	default:
		return ERR_INVALID_ARG;
   }

	_dma_set_irq_state(CONF_DMA_MEMORY_CHANNEL, (enum _dma_callback_type)type, (cb != NULL));

	return ERR_NONE;
}

hpl_dma.h (In Start):

struct _dma_resource;

/**
 * \brief DMA callback types
 */
enum _dma_callback_type { DMA_SUSPEND_CB, DMA_TRANSFER_COMPLETE_CB, DMA_TRANSFER_ERROR_CB };

/**
 * \brief DMA interrupt callbacks
 */
struct _dma_callbacks {
	void (*suspend)(struct _dma_resource *resource);
	void (*transfer_complete)(struct _dma_resource *resource);
	void (*transfer_error)(struct _dma_resource *resource);
};

/**
 * \brief DMA resource structure
 */
struct _dma_resource {
	struct _dma_callbacks dma_cb;
	void *                back;
};

/**
 * \brief Retrieves DMA resource structure
 *
 * \param[out] resource The resource to be retrieved
 * \param[in] channel DMA channel to retrieve structure for
 *
 * \return status of operation
 */

int32_t _dma_get_channel_resource(struct _dma_resource **resource, const uint8_t channel);

/**
 * \brief Enable/disable DMA interrupt
 *
 * \param[in] channel DMA channel to enable/disable interrupt for
 * \param[in] type The type of interrupt to disable/enable if applicable
 * \param[in] state Enable or disable
 */
void _dma_set_irq_state(const uint8_t channel, const enum _dma_callback_type type, const bool state);

hpl_dmac.c: (In Start)

/**
 * \brief Enable/disable DMA interrupt
 */
void _dma_set_irq_state(const uint8_t channel, const enum _dma_callback_type type, const bool state)
{
	hri_dmac_write_CHID_reg(DMAC, channel);

   if (DMA_SUSPEND_CB == type) {
      hri_dmac_write_CHINTEN_SUSP_bit(DMAC, state);
	} else if (DMA_TRANSFER_COMPLETE_CB == type) {
		hri_dmac_write_CHINTEN_TCMPL_bit(DMAC, state);
	} else if (DMA_TRANSFER_ERROR_CB == type) {
		hri_dmac_write_CHINTEN_TERR_bit(DMAC, state);
	}
}

int32_t _dma_get_channel_resource(struct _dma_resource **resource, const uint8_t channel)
{
	*resource = &_resources[channel];

	return ERR_NONE;
}

I hope that's enough code to go on.  I didn't want to paste the whole monster here.  So the code compiles fine but when it tries to link, basically any calls to hpl_dmac.c (such as _dma_set_irq_state and _dma_get_channel_resource) are 404'ed with "undefined reference to <function>".

 

In the Solution Explorer I right-clicked on Libraries > Add Library.  I clicked "Project Libraries" and added the Start project.  I can see the Start library under Libraries.

 

I tried some experiments with it.  First I commented out all of the calls to dma_memory (In Start) from dmac_intfc (In Main).  Links fine with no errors.  That makes sense - no calls means there's nothing to link.  Next, from main.c (In Main) there's a call to atmel_start_init() from Start.  If I remove the Start library and compile I get an "undefined reference" error.  Makes sense - without the Start library, Main won't be able to link to Start.  So this tells me that 1) the calls from dmac_intfc (in Main) are offending and 2) the Start library and path are valid.  My next thought was that perhaps hpl_dmac.c either wasn't being compiled or wasn't making it into the Start library.  But looking at the Start compile:

 

...

        Building file: ../hpl/dmac/hpl_dmac.c
        Invoking: ARM/GNU C Compiler : 6.3.1
        "C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\arm\arm-gnu-toolchain\bin\arm-none-eabi-gcc.exe"  -x c -mthumb -D__SAMD21G18A__ -DDEBUG  -I"C:\Program Files (x86)\Atmel\Studio\7.0\Packs\ARM\CMSIS\5.4.0\CMSIS\Core\Include" -I"C:\Program Files (x86)\Atmel\Studio\7.0\Packs\Atmel\SAMD21_DFP\1.3.395\samd21a\include" -I"../Config" -I".." -I"../examples" -I"../hal/include" -I"../hal/utils/include" -I"../hpl/core" -I"../hpl/dac" -I"../hpl/dmac" -I"../hpl/gclk" -I"../hpl/nvmctrl" -I"../hpl/pm" -I"../hpl/port" -I"../hpl/sercom" -I"../hpl/sysctrl" -I"../hpl/tc" -I"../hpl/usb" -I"../hri" -I"../usb" -I"../usb/class/cdc" -I"../usb/class/cdc/device" -I"../usb/device" -I"../dma_m2m"  -O0 -ffunction-sections -mlong-calls -g3 -Wall -mcpu=cortex-m0plus -c -std=gnu99 -MD -MP -MF "hpl/dmac/hpl_dmac.d" -MT"hpl/dmac/hpl_dmac.d" -MT"hpl/dmac/hpl_dmac.o"   -o "hpl/dmac/hpl_dmac.o" "../hpl/dmac/hpl_dmac.c" 
        Finished building: ../hpl/dmac/hpl_dmac.c
...

 

... and the linker:

 

        Building target: libStart.a

        Invoking: ARM/GNU Archiver : 6.3.1
        "C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\arm\arm-gnu-toolchain\bin\arm-none-eabi-ar.exe" -r  -o libStart.a  atmel_start.o dma_m2m/dma_memory.o dma_m2m_main.o driver_init.o examples/driver_examples.o hal/src/hal_atomic.o hal/src/hal_dac_sync.o hal/src/hal_delay.o hal/src/hal_flash.o hal/src/hal_gpio.o hal/src/hal_i2c_m_sync.o hal/src/hal_i2c_s_async.o hal/src/hal_init.o hal/src/hal_io.o hal/src/hal_sleep.o hal/src/hal_usart_sync.o hal/src/hal_usb_device.o hal/utils/src/utils_assert.o hal/utils/src/utils_event.o hal/utils/src/utils_list.o hal/utils/src/utils_ringbuffer.o hal/utils/src/utils_syscalls.o hpl/core/hpl_core_m0plus_base.o hpl/core/hpl_init.o hpl/dac/hpl_dac.o hpl/dmac/hpl_dmac.o hpl/gclk/hpl_gclk.o hpl/nvmctrl/hpl_nvmctrl.o hpl/pm/hpl_pm.o hpl/sercom/hpl_sercom.o hpl/sysctrl/hpl_sysctrl.o hpl/tc/tc_lite.o hpl/usb/hpl_usb.o usb/class/cdc/device/cdcdf_acm.o usb/device/usbdc.o usb/usb_protocol.o usb_start.o   
        C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\arm\arm-gnu-toolchain\bin\arm-none-eabi-ar.exe: creating libStart.a
        Finished building target: libStart.a
 

Sure enough, hpl/dmac/hpl_dmac.o is present when the Start library is built.  Weird.  So why do you think I might be getting the undefined reference errors?  What am I missing?

 

This topic has a solution.
Last Edited: Tue. Oct 13, 2020 - 12:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'll move this to the ARM community as this is for 8 bit AVR.

 

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

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

'Projects' within a 'Solution' are separate, standalone programs - so you can't just call between them.

 

arcadebench wrote:
one as the Atmel Start project and the other as the Main project which calls the Start
 

 

I can't really see the point of that as a structure - and it's not how it's intended to work

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...
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I got a response back from Microchip support.  Turns out I didn't have the DMAC driver configured.  On the Atmel Studio START tab, in My Software Components there's four grey boxes under the project configuration.  One of them is the DMAC driver.  Without the driver configured, the M2M DMA won't be able to compile correctly.  That's all it was.  Works fine now.

 

Yes, the projects are separate and can stand alone.  I find it's nice having the START project separate so that regenerating that project doesn't have a chance to muck with the main project.  Set the project dependencies so that START is built first, set to generate a static library, and add the START project as a library to the main program.  It also facilitates a main project being written in C++, as the START project flow doesn't readily support C++.  You can do it but it takes some effort:

 

https://microchipsupport.force.com/s/article/C---Support-for-Atmel-START-C

 

 

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

Thanks for feeding back; glad you got it sorted.

 

Please mark the solution - see Tip #5 in my signature, below:

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...