hexagon5un / AVR-Programming

Code examples for the book "Make: AVR Programming"
http://littlehacks.org/AVR-Programming
MIT License
725 stars 340 forks source link

Some code does not compile in Arduino IDE or Atmel Studio #10

Open PhillyNJ opened 9 years ago

PhillyNJ commented 9 years ago

Just a note, perhaps for the next edition of the book. The sample code provided in the book and this repo is for c only compilers (no C++ linkage) and does not compile in Atmel Studio (c++ projects) or the Arduino IDE 1.6.X. The front page of this repo gives instructions on how to setup the Arduino IDE. The instructions are correct but some code does not compile, like the USART.h. With the help of the folks over at AVR Freaks, they pointed this out to me as I am not an expert in C or C++ (I c# by day). Their suggestion was to either add an extern c or rewrite the code in c++. I choose the later as it was great to learn. But I think you should let people know of the issue, because a large chunk of AVR programmers are newbies :) . Although your book does not say we need to use these IDEs/Compilers, most newbies use them. This is just a suggestion. I am an advocate of your book, and I am compelled to inform you.

With that said, your book is outstanding and a create read.

hexagon5un commented 9 years ago

Hey, thanks!

Yeah, the code is in C. It won't work if you try to compile it in Turbo Pascal either. :)

But that's strange about the Arduino IDE. My experience is that if you name the file something.c, it gets compiled with gcc, and if you name the file something.cpp, it gets compiled with g++. (And if you name it something.ino, it gets converted into something.cpp, and it gets hit with g++ again.)

And I haven't used Studio in a dog's age, but I absolutely remember writing in straight C there too.

In Arduino, were you using the default whatever.ino "sketch" file or did you open up your own something.c to start with? In Studio, were you mixing/matching C/C++ outside of my code? Did you use a different extension from .c? How did the compiler chain get confused?

PhillyNJ commented 9 years ago

Hi - Thanks for the response. I was using a default sketch, with a reference to a header file .h. when in the Arduino IDE. In Atmel Studio, it was a C/C++ project. Here is my build error from Atmel Studio:

------ Build started: Project: TestOne, Configuration: Release AVR ------
Build started.
Project "TestOne.cppproj" (default targets):
Target "PreBuildEvent" skipped, due to false condition; ('$(PreBuildEvent)'!='') was evaluated as (''!='').
Target "CoreBuild" in file "C:\Program Files (x86)\Atmel\Atmel Studio 6.2\Vs\Compiler.targets" from project "C:\Users\Philip\Documents\Atmel Studio\6.2\AvrI2CTesting\TestOne\TestOne.cppproj" (target "Build" depends on it):
    Task "RunCompilerTask"
        Shell Utils Path C:\Program Files (x86)\Atmel\Atmel Studio 6.2\shellUtils
        C:\Program Files (x86)\Atmel\Atmel Studio 6.2\shellUtils\make.exe all 
        Building target: TestOne.elf
        Invoking: AVR8/GNU Linker : 4.8.1
        "C:\Program Files (x86)\Atmel\Atmel Toolchain\AVR8 GCC\Native\3.4.1056\avr8-gnu-toolchain\bin\avr-g++.exe" -o TestOne.elf  src/i2c.o src/USART.o TestOne.o   -Wl,-Map="TestOne.map" -Wl,--start-group -Wl,-lm  -Wl,--end-group -Wl,-L"../src"  -Wl,--gc-sections -mmcu=atmega168p  
        TestOne.o: In function `main':
C:\Users\Philip\Documents\Atmel Studio\6.2\AvrI2CTesting\TestOne\Debug\TestOne.cpp(1,1): error: undefined reference to `initUSART()'
C:\Users\Philip\Documents\Atmel Studio\6.2\AvrI2CTesting\TestOne\Debug\TestOne.cpp(1,1): error: undefined reference to `printString(char const*)'
C:\Users\Philip\Documents\Atmel Studio\6.2\AvrI2CTesting\TestOne\Debug\TestOne.cpp(1,1): error: undefined reference to `printString(char const*)'
collect2.exe(0,0): error: ld returned 1 exit status
        make: *** [TestOne.elf] Error 1
        The command exited with code 2.
    Done executing task "RunCompilerTask" -- FAILED.
Done building target "CoreBuild" in project "TestOne.cppproj" -- FAILED.
Done building project "TestOne.cppproj" -- FAILED.

Build FAILED.
========== Build: 0 succeeded or up-to-date, 1 failed, 0 skipped ==========

Like I said, when I wrapped your code into a cpp class. It compiles and loads fine.

hexagon5un commented 9 years ago

It looks like the code is called "TestOne.cpp", and the project, "TestOne.cppproj".

Is there a place in Studio where you tell it you're working in C/C++? Like a check box early on or something? Did you pick these extensions? They're the classic C++ ones.

Studio really seems convinced that you're working in C++ rather than C, and that's what's forcing you to write in C++ rather than C. I know Studio supports C, though -- it's using the same compiler toolchain that we all use (Studio, Arduino, and the book / me). You just gotta figure out how to convice it.

Have you tried just opening up my files? Or are you cutting/pasting into a pre-existing (C++) project?

I know a ton of people have been using Studio and made it work, but I never thought to ask how.

PhillyNJ commented 9 years ago

Atmel Studio has several options from when creating a project. When you choose the project type, it knows how to compile the code. A description can be found here.

On that page there is a table describing the project types. From my understanding it is either c project or cpp. Since many of the AVR Arduino libraries are written in c++, I have to choose the c++ project. For example, I want to use your USART code with an OLED library written by Adafruit. The Adafruit library is written in c++. This could be a moot point as perhaps to use the USART code I must use a c compiler. This is understood, but I am only pointing out that a great deal of AVR community is coming over from the Arduino IDE and a great deal of libraries are written in c++.

The Arduino IDE on the other hand uses a c++ compiler and some of your code will not compile. Perhaps there is a setting in the Arduino IDE to tell it to compile both.

As you stated, a ton of people have been using Studio and made it work. So did I :) - I am telling you how I did it. Maybe there is a way to compile your c code in the Arduino IDE, but I haven't figured out how unless I wrap your code in a c++ class.

With all this said, again the book is a great read and provides excellent information, I wish you wrote one on Assembly :)

hexagon5un commented 8 years ago

I wish I was good enough at AVR assembly to write one! :)

I (finally!) understand your problem: it's not with Studio or Arduino, but with mixing C and C++. I don't do that often, but if you're comfortable with C++ (as it looks like you are) you can just include my C code by making sure that you wrap the header includes with the extern "C" {} construct as you suggested to begin with. The GCC compiler/linker will take care of the rest for you.

Or you could do the opposite and wrap up the C++ declarations in extern "C" {} and call them from C code.

https://isocpp.org/wiki/faq/mixing-c-and-cpp is the best link I've found for explaining how to do it. Or have a look at arduino/avr/libraries/Wire/Wire.cpp which calls arduino/avr/libraries/Wire/utility/twi.c, for instance, in the Arduino codebase.

But yeah. If your goal is to pull some of the Arduino C++ code into your C project, you can either give up and write C++ (probably easier) or just add the extern "C" to each function declaration (or to the entire header file if you're lucky).

Note that it sounds like I've finally caught up with you, PhillyNJ, and so I'm just writing this stuff down for anyone else who stumbles on this thread and wants to know how to deal with the C/C++ dilemma.

hessa59 commented 8 years ago

I had the same problem trying to compile the code in Atmel studio 7. But I switched to a C project and copied the pin defines to C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\avr\include\pinDefines.h

I also had to add # define F_CPU 16000000UL to usart.c

hessa59 commented 8 years ago

I got the Usart echo program to work in Atmel Studio 7 in C++ mode by typing extern "C" in front of all the function definitions in usart,h as in:

extern "C" void transmitByte(uint8_t data);

hexagon5un commented 8 years ago

Thanks @hessa59 for chiming in. I'm really weak with Studio -- I haven't used it since like 2006. Oh my, I'm old.

The issue with the defines is due to Studio putting global defines somewhere other than the makefile. According to this website, Studio calls them "symbols" and they're found buried pretty deep in some menus: Project - Properties - Toolchain - All Configurations - Symbols.

Anyway, long story short, you add F_CPU and BAUD there and you're good. The other option is for me to hardcode a CPU speed into USART.c, but then it would stand a chance of simply being wrong with no warning.

The issue looks to be starting off with a C++ project in Studio sets up a different compiler toolchain, which then causes problems with some of the subtler differences in the way C and C++ interoperate.

The path of least resistance, as @PhillyNJ found out, seems to be starting off in C++ for the Arduino libs, and then including whatever straight-C code wrapped in the "extern C" brackets.