Undefined reference to function

Hi,

First the good news. Thanks to you people I now have been able to make few test projects with my mutilated Arduino project files. Basically I took a sample Arduino project and removed everything except main.c and just used avr/io.h and port manipulation.

And now in the series of stupid questions… This is closely related to Moving from Arduino IDE to Embeetle - #12 by joyzpa

My test project works when it’s all in one file (this has been gone over in way too many forums already, but please endure :sweat_smile:) The problem is caused by me dividing my test project (to learn the real way of coding after discovering the hidden world behind Arduino IDE) main.c in to 3 source files, to main.c, USART.c and USART.h. I presume that this is some error that I have made in the file inclusion, but I don’t seem to be able to find the problem. The project is a serial test code that just mirrors back the received bytes and it can be found from Atmega328P manual.

Problem:
Diagnostics show no errors
image

But when compiling, beetle throws an error

Very much likely caused by not included (linked) source file USART.c (forced inclusion via filetree doesn’t help)
image

I have tried making the source file inclusion and function prototypes as stated in the ANDI C book, but obviously it seems that I failed.

I’ll list my code here and the code inside functions is simplified with { … } (it is not sensitive but its too long for comfort, if required I’ll just send the whole project :wink:).

main.c

#include <avr/io.h>
#include "USART.h"

int main(void)
{
  
  USART_Init(103); // set baudrate register to desired baudrate value see UBRRn table

	do { // so called main loop
    
    USART_Transmit(USART_Receive()); // loopback all received

	} while (1);

	return 0;
}

USART.h

#ifndef USART_H
#define USART_H

// Includes, defines(macros)

// Function prototypes
static unsigned char USART_Receive(void);
static void USART_Transmit(unsigned char data);
static void USART_Init(unsigned int ubrr);

#endif

USART.c

// includes, defines
#include <avr/io.h>
#include "USART.h"

static unsigned char USART_Receive(void) { ... }

static void USART_Transmit(unsigned char data) { ... }

static void USART_Init(unsigned int ubrr) { ... }

If by any change you people can spot the problem, I would be very happy

As a reminder, this is a Arduino sample project (downloaded from beetle server) that I have mutilated by removing all Arduino lib’s, so it might be possible that i.e. Arduino linker script is malfunctioning because of my doing. I had to start in this way since i had absolutely no idea, how to create a empty Atmega328P project with Embeetle, i.e. how to arrange the project directories, where to get makefile, linker script etc.

All in all, I’m in love with the Embeetle, I’m just having some trouble getting started :+1:

Niko

1 Like

Hi Niko,
Thank you very much for your kind words about Embeetle :smiley:

Could you please send me the project? You can zip the whole project folder (eg. 7zip or whatever) and send it to kristof@embeetle.com

If it’s too large, please use the WeTransfer service:

I’ll do my best to track down the issue and get it fixed :smiley:

Kind regards,
Kristof

1 Like

Hi Kristof,

I just sent the email for you. Thanks in advance :laughing:
And sorry for the trouble.

Niko

1 Like

Hi Niko,
No problem. It’s my pleasure to help :smiley:

I think I found the issue. You had all your functions in USART.h and USART.c declared static. That means they are only available to the compilation unit, being USART.h + USART.c in this case. In other words, the static keyword prevents these functions from being visible to other compilation units, like main.c.

I removed the static keyword from all functions in USART.h and in USART.c:

// Function prototypes
unsigned char USART_Receive(void);
void USART_Transmit(unsigned char data);
void USART_Init(unsigned int ubrr);

and

unsigned char USART_Receive(void)
{
    ...
}

void USART_Transmit(unsigned char data)
{
    ...
}

void USART_Init(unsigned int ubrr)
{
    ...
}

Now it compiles :smiley:

Kind regards,
Kristof

Hi Kristof,

Ok :rofl: Oh no…

I tried that, but now that I think about it, I had also the files in separate folder and it lead to some other problems. I knew it was something simple :expressionless:

Kristof, perhaps when you find the time to create the C-coding tutorial, you should add a extensive noob-proof explanation of program scope (static, extern, etc.) :wink:

For in my case I got confused by some text that said that “All functions should be declared static”, but when keepping in mind what you said

It should be more like “All functions should be declared static, BUT if you wan’t to call them from some other source file, get rid of the static keyword” :sweat_smile:

Niko

Hi @joyzpa ,

I had a look at your code. It looks like you’re trying to build the absolute minimal project to get an LED blinking on the board.

However, I don’t see a startup assembly file. Usually, a startup assembly file is needed to initiate the RAM memory, eg. to fill the RAM memory with all the variables and constants you declared in your program. The startup code does actually more than that. It does all kinds of magic to prepare the microcontroller to get up and running.

A startup assembly file is the usual way to do this. However, some people get it done in C or C++. Some years ago, I read a chapter in a book that explained how to create such a minimal project for an STM32 microcontroller. The project would be only two files, a main.c file and a linkerscript.ld file:

I also added a build.bat and clean.bat with the commands to build (compile) and clean (delete compiled files) the project.

The beauty about that project was: it didn’t need a startup assembly file. The startup code was inside the main.c file, as pure C-code.

You can read more about this endaveour here (that’s before Embeetle was born):

Now, it appears to me that your project does not have any startup assembly file, nor any other place where the startup code could be. So that makes me believe your project won’t work properly.

Now, I looked at one of the standard Arduino projects in Embeetle, and it appeared to me that I couldn’t find the startup code either! So maybe the startup code is hidden somewhere in the compiler itself? It’s either that, or I looked over it.

Please let me know if you get your project working. If it does, that would be very nice!

Best of luck,
Kristof

Absolutely. Writing such a C-coding tutorial - which would then become part of our website - is on my todo list. It’s something I would like to do very much, actually. I hope to find time for it after completing the PlatformIO support (which unfortunately will take several more months to complete).

Hi Kristof,

Thanks for the explanation and your time. Perhaps before you get the tutorian done, some other people can find this post and get the answer (meaning this wasn’t a complete waste of time) :sweat_smile:.

In basic terms, I’m trying to create a total minimal “starting” code for myself (and perhaps anyone interested). In other words a clean plate, where I can start building my code, in a “modular way”. The blinking led code with port manipulation was to test that I had a “working” code and then to try to separate the code to .h and .c modules as an exercise. Your STM32 microcontroller project looks similar to what I would like to have.

Now that you say it, I once saw a question about this somewhere, but it was too advanced at that time to be comprehensible.

A really quick inaccurate query to oracle says that the startup code would be generated by the tool chain / compiler (from AVR Freaks forum)

Just made the change to my code (get rid of the static keyword) and loaded it to my Arduino UNO clone via USB and it works perfectly. So from this we could say that the startup code “is out there” in the final compiled file. Even made a small serial (constant poll, no interrupts) code and even that works fine with the Embeetle serial monitor!

Next stop is to make a delay without delay, perhaps using a timer and counting system ticks :laughing:

By the way, when one uses one of the sample projects for Arduino from beetle servers and flashes them to the Arduino board, it is “flashed” via the bootloader right? If I at some point want to get rid of the bootloader (not anytime soon) and use the ISP header and a programmer for flashing, what needs to be changed in the Embeetle and where?

Thanks again
Niko

1 Like

That’s awesome! It can mean two things:

  1. The AVR compiler stuffs the startup code at the front of your binary.

  2. The bootloader - which is already on your microcontroller - takes care of the startup procedure.

I would need to spend more time on this to figure it all out (time I currently don’t have). However, my gut feeling says that nr. 2 is the right diagnosis. On the other hand, the bootloader is not always there on the chip (see next part). So that points to diagnosis nr. 1.

Yes, that’s right. The bootloader runs on your microcontroller, making sure that the UART port works. Then it receives the binary file through that UART port and flashes it to a section in the FLASH memory.

Note: because modern computers don’t have native UART ports anymore, a part of the Arduino UNO board is dedicated to convert USB to UART.

Good question!
You can find more info about that here:

https://embeetle.com/#supported-hardware/arduino/probes

You should navigate in the Dashboard to the probe you’re using for flashing. Normally that probe is set at USB to UART converter. That’s the default setting for Arduino projects. However, you can select another probe there:

You have these choices:

Please don’t take ARDUINO_AS_ISP, I haven’t implemented that option yet. In fact, it shouldn’t be there right now. My apologies.

If you want to sidestep the bootloader (flash without bootloader), you should select either AVR_ISP_MKII or ATMEL_ICE. We have a webpage for each of them (click on them above, you’ll notice I made them clickable. They link to the corresponding webpage each). Then also modify the Transport Protocol entry, which is right below the Device entry:

When you completed the selections in the Dashboard, Embeetle will offer a change to your dashboard.mk file. Those changes are needed to use the selected probe properly.

Then you’ll notice two flash buttons at the top of Embeetle: one normal flash button, one to flash the bootloader. The normal flash button will simply flash your code to the microcontroller, sidestepping any potential bootloader being on the chip. The bootloader flash button will flash a new bootloader on your microcontroller. In the Project Layout section in the Dashboard, you can see where that bootloader is located:

This file is normally present in every new Arduino project you can download from our server.

Hope this helps :smiley:
Kind regards,
Kristof