I am trying to write code for my atmega328 in C++ using Eclipse CDT. I have two projects. One project is static library project, that produces a static library. All the files in library are compiled without errors and library is created using the following command:
avr-ar -r "libRobotMainBoard.a" ./Console.o ./Motor.o ./RingBuffer.o ./io.o ./operators.o
c:\Program Files\WINAVR\bin\avr-ar.exe: creating libRobotMainBoard.a
Then I use this library in other project that produces hex file for my atmega. But during the linking I get error:
C:\Users\Mitch\Disk Google\workspace\AVR\RobotMainBoard\Release\libRobotMainBoard.a(Console.o): In function Console::putCharToUDR()':
Console.cpp:(.text._ZN7Console12putCharToUDREv+0x2): undefined reference to Console::txBuff'
and many othert similar to this. I have tried to find the solution on the web. Most of them mentions that this error is caused by naming library and the compiled file in the wrong order. I checked that and my order should be fine. I am linking it with the command:
avr-g++ -Wl,-Map,BoardTest.map,--cref -mrelax -Wl,--gc-sections -L"C:\Users\Mitch\Disk Google\workspace\AVR\RobotMainBoard\Release" -mmcu=atmega328p -o "BoardTest.elf" ./main.o -lRobotMainBoard
The main.cpp file looks like this:
#include <util/delay.h>
#include "Console.h"
#include "Motor.h"
Motor leftMotor(9,7);
Motor rightMotor(10,8);
int main(){
leftMotor.stop();
rightMotor.stop();
Console::setup(250000);
while(1){
Console::send('a');
_delay_ms(2000);
}
}
When I comment the Console lines out, it will link OK, even with Motor lines, which source files are in the same lib.
The Console cpp file is like this:
#include <avr/interrupt.h>
#include "Console.h"
#include "operators.h"
void Console::setup(uint16_t baudrate) {
rxBuff = new RingBuffer(RX_BUFF_SIZE);
txBuff = new RingBuffer(TX_BUFF_SIZE);
uint16_t baudPrescaler= (F_CPU / (baudrate * 16)) - 1;
UCSR0A = 0x00; UCSR0B = 0x00; UCSR0C = 0x00;
//Using 8-bit, asynchronous, normal speed, 1 stop bit, no parity check
UBRR0 = baudPrescaler;
UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00); //8-bit
UCSR0B |= (1 << TXEN0) | (1 << RXEN0);
sei();
}
void Console::send(char c) {
txBuff->add(c);
UCSR0B |= (1 << UDRIE0);
}
void Console::send(const char* s) {
while(*s){
send(*s++);
}
}
void Console::putCharToUDR(){
if(!txBuff->empty()){
UDR0 = txBuff->remove();
} else {
UCSR0B &= ~(1 << UDRIE0);
}
}
uint8_t Console::canReceive() {
return rxBuff->available();
}
uint8_t Console::canTransmit() {
return txBuff->available();
}
ISR(USART_RX_vect, ISR_BLOCK){
}
ISR(USART_UDRE_vect, ISR_BLOCK){
Console::putCharToUDR();
}
Do anybody of you have any idea, why I am still getting the linking error?
EDIT 1
#ifndef CONSOLE_H_
#define CONSOLE_H_
#include "RingBuffer.h"
#define RX_BUFF_SIZE 32
#define TX_BUFF_SIZE 32
class Console {
public:
static void setup(uint16_t baudrate);
static void send(char c);
static void send(const char* s);
static uint8_t canReceive();
static uint8_t canTransmit();
static void putCharToUDR();
private:
static RingBuffer *rxBuff;
static RingBuffer *txBuff;
};
#endif /* CONSOLE_H_ */
As txBuff is static, you have to provide its definition in Console.cpp, e.g.
RingBuffer * Console::txBuff = new RingBuffer(RX_BUFF_SIZE);
Related
I'm trying to write a basic plugin for gstreamer in C++. I am working from another person's examples and have followed the structure of the code (I believe) exactly. However, I've been trying to compile the code and I am seeing an error saying
unresolved external symbol gst_adder_get_type referenced in function "int __cdecl plugin_init(struct _GstPlugin *)" (?plugin_init##YAHPEAU_GstPlugin###Z)
I have declared but not implemented gst_adder_get_type. I did that because the examples I have seen have not had any implementation of the function either, they just have the declaration in the header file and somehow that works.
I have tried implementing the function myself anyway, but other errors pop up instead.
Adder.h:
#include <gst/gst.h>
#include <gst/base/gstpushsrc.h>
G_BEGIN_DECLS
#define GST_TYPE_ADDER (gst_adder_get_type())
#define GST_ADDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ADDER,Gstadder))
#define GST_ADDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ADDER,GstadderClass))
#define GST_IS_ADDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ADDER))
#define GST_IS_ADDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ADDER))
struct _Gstadder
{
GstElement element;
//pads
GstPad* srcpadadder; //publish data
// property values
gint _num_one;
gint _num_two;
gint _sum;
};
struct _GstadderClass
{
GstElementClass parent_class;
};
GType gst_adder_get_type(void);
typedef struct _Gstadder Gstadder;
typedef struct _GstadderClass GstadderClass;
GST_ELEMENT_REGISTER_DECLARE(adder);
G_END_DECLS
Adder.cpp:
// Adder.cpp : Defines the exported functions for the DLL application.
#include "stdafx.h"
#include "pch.h"
#include <gst/gst.h>
#include "Adder.h"
#include <gstreamer-1.0/gst/base/gstbasesrc.h>
#include <gstreamer-1.0/gst/base/gstpushsrc.h>
GST_DEBUG_CATEGORY(gst_adder_debug);
// This defines the properties owned by this class.
enum
{
PROP_NUMONE,
PROP_NUMTWO,
PROP_SUM
};
// Definitions of functions later in this file so that we can access them now.
static void gst_adder_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec);
static void gst_adder_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec);
static gboolean gst_adder_start(GstBaseSrc* basesrc);
static gboolean gst_adder_stop(GstBaseSrc* basesrc);
static GstCaps* gst_adder_get_caps(GstBaseSrc* bsrc, GstCaps* caps);
static GstFlowReturn gst_adder_fill(GstPushSrc* src, GstBuffer* buf);
static void gst_adder_finalize(GObject* object);
/*
* "CAPS" are literally capabilities. They seem only to be descriptions, not functions.
* I think this means that when I run an inspect on the DLL, this gets called to write out
* what the capabilities of the function actually are and what kinds of pads exist.
*/
static GstStaticPadTemplate src_det_factory = GST_STATIC_PAD_TEMPLATE(
"src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS("Integer"));
static void gst_adder_class_init(GstadderClass * klass) {
/*
Class starting initiation. Different from the object instantiation.
*/
GObjectClass* gobject_class;
gobject_class = G_OBJECT_CLASS(klass);
GstElementClass* element_class;
element_class = GST_ELEMENT_CLASS(klass);
GstBaseSrcClass* gstbasesrc_class;
gstbasesrc_class = (GstBaseSrcClass*)klass;
GstPushSrcClass* gstpushsrc_class;
gstpushsrc_class = (GstPushSrcClass*)klass;
// Metadata. Not sure if this is redundant or not. Assuming not.
const gchar* m_a = "AdditionMan";
gst_element_class_set_static_metadata(
element_class,
m_a,
"MY_ADDER",
"Add two numbers, return the sum",
"John Garst <john.garst#parsons.com>");
// Initialize pad for the source
gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&src_det_factory));
// This appears to be where we are linking the callback functions for the adder.
gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_adder_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_adder_get_property);
gstbasesrc_class->start = GST_DEBUG_FUNCPTR(gst_adder_start);
gstbasesrc_class->stop = GST_DEBUG_FUNCPTR(gst_adder_stop);
gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR(gst_adder_get_caps);
gstpushsrc_class->fill = GST_DEBUG_FUNCPTR(gst_adder_fill);
gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_adder_finalize);
// Property Definitions. This is linked to an enum at the top of this file,
// and will be used more in the adder instantiation and getters/setters.
g_object_class_install_property(
gobject_class,
PROP_NUMONE,
g_param_spec_string(
"NumOne",
"NumOne",
"The first number to add.",
0,
G_PARAM_READWRITE
)
);
g_object_class_install_property(
gobject_class,
PROP_NUMTWO,
g_param_spec_string(
"NumTwo",
"NumTwo",
"The second number to add.",
0,
G_PARAM_READWRITE
)
);
g_object_class_install_property(
gobject_class,
PROP_SUM,
g_param_spec_string(
"Sum",
"Sum",
"The sum of NumOne and NumTwo.",
0,
G_PARAM_READWRITE
)
);
}
static void gst_adder_init(Gstadder* filter) {
/*
instantiate the element. This happens after class initiation.
*/
//initialize pads
filter->srcpadadder = gst_pad_new_from_static_template(&src_det_factory, "src");
//initialize values
filter->_num_one = 0;
filter->_num_two = 0;
filter->_sum = 0;
//configure
gst_base_src_set_format(GST_BASE_SRC(filter), GST_FORMAT_TIME);
gst_base_src_set_live(GST_BASE_SRC(filter), true);
gst_base_src_set_do_timestamp(GST_BASE_SRC(filter), true);
}
static gint gst_adder_add_two_numbers(Gstadder* filter) {
/*
This function takes two numbers and adds them together, returning the sum of those numbers.
*/
filter->_sum = filter->_num_one + filter->_num_two;
return filter->_sum;
}
void gst_adder_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec) {
/*
Set properties in the adder.
We can set the two numbers to add, but not the sum.
*/
Gstadder* that = GST_ADDER(object);
switch (prop_id)
{
case PROP_NUMONE:
that->_num_one = g_value_get_int(value);
break;
case PROP_NUMTWO:
that->_num_two = g_value_get_int(value);
break;
}
}
void gst_adder_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec) {
/*
Set properties in the adder.
We can get any property, including the sum.
*/
Gstadder* that = GST_ADDER(object);
switch (prop_id)
{
case PROP_NUMONE:
g_value_set_int(value, that->_num_one);
break;
case PROP_NUMTWO:
g_value_set_int(value, that->_num_two);
break;
case PROP_SUM:
g_value_set_int(value, that->_sum);
break;
}
}
gboolean gst_adder_start(GstBaseSrc* basesrc) {
/*
Start the adder. There is nothing else necessary to initialize, so we won't do anything here.
*/
return gboolean();
}
gboolean gst_adder_stop(GstBaseSrc* basesrc) {
/*
Stop the adder. There is nothing to clean, so we won't do anything here.
*/
return gboolean();
}
GstFlowReturn gst_adder_fill(GstPushSrc* src, GstBuffer* buf) {
/*
Fill the adder from buffer. This seems to be where the magic actually happens.
*/
Gstadder* filter = GST_ADDER(src);
gst_adder_add_two_numbers(filter);
return GstFlowReturn();
}
GstCaps* gst_adder_get_caps(GstBaseSrc* bsrc, GstCaps* caps) {
/*
Return Adder capabilities
*/
#ifdef __linux__
return gst_caps_new_simple(0, NULL, NULL);
#else
return gst_caps_new_simple(0, nullptr);
#endif
}
void gst_adder_finalize(GObject* object) {
/*
Release memory and clean up the workspace.
*/
}
GType gst_adder_get_type(void)
{
static GType gst_adder_get_type = 0;
if (!gst_adder_get_type) {
static const GTypeInfo gst_adder_get_type_info = {
sizeof(GstadderClass),
NULL,
NULL,
NULL,
NULL,
NULL,
0,
0,
NULL,
};
gst_adder_get_type = g_type_register_static(G_TYPE_INTERFACE,
"GstVideoOverlay", &gst_adder_get_type_info, G_TYPE_FLAG_ABSTRACT);
}
return gst_adder_get_type;
}
gst_Plugin_Test.h
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the VIDEOSTRETCH_EXPORTS
// symbol defined on the command line. This symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// VIDEOSTRETCH_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef GST_PLUGIN_TEST_EXPORTS
#define GST_PLUGIN_TEST_API __declspec(dllexport)
#else
#define GST_PLUGIN_TEST_API __declspec(dllimport)
#endif
gst_Plugin_Test.cpp
// GST_Plugin_Test.cpp : Seems to initialize only the other classes that are involved. Doesn't seem
// like this can do it's own processing, which is a little odd to me. I don't know if that's correct.
//Includes. stdafx is required for some reason, as is pch.
#include "stdafx.h"
#include "pch.h"
#include "Adder.h"
#include <gst/gst.h>
#include "gst_Plugin_Test.h"
GST_DEBUG_CATEGORY(gst_gst_plugin_test_debug);
static gboolean plugin_init(GstPlugin* plugin)
{
/*
One of (?) the initialization functions. Seems like the main entry point.
*/
gboolean ret = TRUE;
GST_DEBUG_CATEGORY_INIT(gst_gst_plugin_test_debug, "gst_plugin_test", 0, "Test Stuff");
// The adder class is where things actually happen. We register it here.
ret = gst_element_register(plugin, "MY_ADDER", GST_RANK_NONE, GST_TYPE_ADDER);
return ret;
}
#define PACKAGE "GST_Plugin_Test"
// This is where the plugin metadata is defined. Shown on a gst-inspect-1.0
GST_PLUGIN_DEFINE(GST_VERSION_MAJOR,
GST_VERSION_MINOR,
GST_Plugin_Test,
"Just Testing Things",
plugin_init, "1.0", "LGPL",
"TEST", "http://www.polarisalpha.com");
UPDATE:
I did find a place that has another example where the get_type function is declared in the header and implemented in the C file, which makes a lot more sense. I've done it myself, again following the structure as best I can, and now can compile the DLL and put it into my plugins folder. However, I can't successfully inspect the plugin. It give me the error:
gst_element_register: assertion 'g_type_is_a (type, GST_TYPE_ELEMENT)' failed
which seems odd. I've updated the Adder file to include the implementation of the get_type function.
I am doing a cross compilation test in Eclipse IDE for meta-toolchain made with Yocto, for arm cortex A9 processor. After performing hello world test, which ran successfully, I created a basic program to test pthreads.
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <pthread.h>
#define MILLION 1000000 /* one million = 10^6*/
#define sec_to_nsec 1000000000 /* ns to s conversion = 10^9 */
#define FILENAME "Schd.txt"
#define FLUSH_TIME 10.0
#define SIG_LLP_TIMER SIGRTMIN+1
int isr_idx; /* counter of ISR occurred -- starts from 0 and increments at each interrupt*/
volatile float clk_k, /* MY_CLOCK() value for the current sample*/
clk_k_1; /* MY_CLOCK() value for the previous sample*/
/*clock and timer values*/
struct itimerspec custom_itimerspec;
timer_t timer_id;
clockid_t USED_CLK;
struct timespec tv;
float a_n;
/*THREAD DATA*/
pthread_t thread0;
pthread_attr_t attr;
struct sched_param param;
using namespace std;
void* thread_scheduler(){
//function pointer
//mainThread
//make thread for scheduling
//exit after max cycle
}
int main(void)
{
cout << "Starting the program!" << endl; /* prints Hello World */
cout<< "Creating a Thread to deploy" << endl;
int status;
param.__sched_priority = 99;
int retc;
/*PTHREAD ATTR setup*/
retc = pthread_attr_init(&attr);
retc |= pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
retc |= pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
retc |= pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
retc |= pthread_attr_setschedparam(&attr,¶m);
if (retc != 0) {
//fail
while(1){}
}
retc = pthread_create(&thread0, &attr, (void * (*)(void *))thread_scheduler, NULL);
printf("Exiting here!");
return 0;
}
But I get this error, undefined reference to `pthread_create', followed with some make errors.
Though after doing some search I found that adding '-pthread' command in configure and autogen settings works for building the project, as described here. But I am puzzled why the compiler can't see these files even if this file is present in 'includes' in the drop down folder of project explorer.
The error about undefined reference is coming from linking step, not from compiling and assembling step, compile step would look for header files and its rightly finding the pthread.h from sysroot include directory as you see as well. After compiling, it has to invoke the linker to create the executable binary and thats where it fails.
When linking it need to add libpthread to linker commandline so linker can find the pthread_create function and link it into final executable, this is usually done via specifying LDFLAGS which then get appended to linker invocation.
compiler driver ( gcc ) can be used to drive both compiling and linking steps.
so when you add -pthread option to compiler and compiler is also used to perform linking then it translates this option into -lpthread to linker cmdline which would then find libpthread and link it in.
) I'm currently developing a project under Qt Creator 4.3.1 which needs an external library to use a key-lock hardware device.
The device comes with a CD with the libraries libkfunc64.so and libkfunc64.a (and the 32 bits versions) presumably compiled for C with gcc. I already tried the libraries in a dummy program to check if they work, and they do.
But when I add the important chunk of code into the Qt project it spits the following error
[...] error: undefined reference to `KFUNC(unsigned int, unsigned int, unsigned int, unsigned int)'
I've already tried to include the library
by coping the libraries to the system directories and simply including the following line in the .pro file
LIBS += -lkfunc64
by using relative paths to the libs in the .pro file
LIBS += -L$$PWD/../build-project/ -lkfunc64
INCLUDEPATH += $$PWD/../build-Qmetrics-Desktop-Debug
DEPENDPATH += $$PWD/../build-Qmetrics-Desktop-Debug
# PRE_TARGETDEPS += $$PWD/../build-Qmetrics-Desktop-Debug/libkfunc64.a
but it keeps showing this error when trying to (re)build it. I've read almost every subject about this issue on the internet but non of them fix this problem.
This is the code for main.cpp
#include "mainwindow.h"
#include <QApplication>
#include <QMessageBox>
// Key-lock stuff ----------
#define ValidateCode1 0X488B
#define ValidateCode2 0XFEE2
#define ValidateCode3 0XEF90
#define ClientIDCode1 0xB862
#define ClientIDCode2 0x54CB
#define ReadCode1 0X1772
#define ReadCode2 0XC4E6
#define ReadCode3 0XBCF8
extern unsigned long KFUNC(unsigned, unsigned, unsigned, unsigned);
unsigned RotateLeft(unsigned, int);
void KTASK(unsigned, unsigned, unsigned, unsigned);
unsigned short ReturnValue1, ReturnValue2;
// -------------------------
int main(int argc, char *argv[])
{
KTASK(1, ValidateCode1, ValidateCode2, ValidateCode3);
KTASK(RotateLeft(ReturnValue1, ReturnValue2 & 7) ^ ReadCode3 ^ ReturnValue2, RotateLeft(ReturnValue2, ReturnValue1 & 15), ReturnValue1 ^ ReturnValue2, 0);
if ((ReturnValue1 == ClientIDCode1) && (ReturnValue2 == ClientIDCode2))
{
QApplication app(argc, argv);
MainWindow w;
w.showFullScreen();
return app.exec();
}
else
{
QMessageBox msgBox;
msgBox.setText("Wrong or missing key-lock!");
return msgBox.exec();
}
}
unsigned RotateLeft(unsigned Target, int Counts)
{
int i;
static unsigned LocalTarget, HighBit;
LocalTarget = Target;
for (i=0; i<Counts; i++)
{
HighBit = LocalTarget & 0X8000;
LocalTarget = (LocalTarget << 1) + (HighBit >> 15);
}
LocalTarget = LocalTarget & 0XFFFF; /* For 32 bit integers */
return (LocalTarget);
}
void KTASK(unsigned CommandCode, unsigned Argument2, unsigned Argument3, unsigned Argument4)
{
unsigned long int ReturnValue;
ReturnValue = KFUNC(CommandCode, Argument2, Argument3, Argument4); // <--- this is the only function that is used from the external library
ReturnValue1 = (unsigned) (ReturnValue & 0XFFFF);
ReturnValue2 = (unsigned) (ReturnValue >> 16);
}
And the (relevant part of the) .pro file is as follow
# Adds OpenCV, DC1394 and Keylok libraries
unix {
CONFIG += link_pkgconfig
PKGCONFIG += opencv
LIBS += -ldc1394 -lkfunc64
}
I really don't know what to do :'(
Have you tried declaring the function with extern "C"?
e.G.:
#ifdef __cplusplus
extern "C"
{
#endif
extern unsigned long KFUNC(unsigned, unsigned, unsigned, unsigned);
#ifdef __cplusplus
};
#endif
This is nessecary to call a external c function from c++.
I get a lot of undefined references. I don't know what I'm doing wrong.
I'm getting the following errors:
undefined reference to 'LetteroidField::start()'
undefined reference to 'LetteroidField::setTitle(std::string)'
undefined reference to 'Letteroid::setletter(char)'
undefined reference to 'Letteroid::setLetter()'
undefined reference to 'Letteroid::setCoords()'
undefined reference to 'Letteroid::erase()'
and other letteroid references.
I'm not done with the other classes, but I don't know why I'm getting these errors. Am I not using #include "" correctly?
This is my professor's sample code. I contacted him but he is not answering (its an online class).
#include "letteroidfield.h"
#include "letteroid.h"
#include "blinkingletteroid.h"
#include "jumpingletteroid.h"
#include "movingletteroid.h"
#include <stdlib.h> /* srand, rand */
#include <time.h>
/// include your derived classes here
int main()
{
LetteroidField screen;
screen.start();
screen.setTitle("Ken's example for the class, press 'x' to quit");
BlinkingLetteroid one;
BlinkingLetteroid two;
BlinkingLetteroid three;
one.setLetter('!'); /// character
one.setCoords(5, 10); /// row, col
two.setLetter('h');
two.setCoords(7, 9);
three.setLetter('#');
three.setCoords(15, 57);
JumpingLetteroid four;
four.setLetter('j');
four.setCoords(rand() % 21, rand() % 21);
MovingLetteroid five;
five.setLetter('m');
int x = 20;
int y = 20;
while (x >= 1)
{
--x;
}
while (y >= 1)
{
--y;
}
if (x == 1)
{
x = 20;
}
if (y == 1)
{
x = 20;
}
five.setCoords(x,y);
/// create and initialize your letteroids here
while ( screen.waitForKeyPress() ) /// keep going until 'x' is pressed
{
one.blink();
two.blink();
three.blink();
/// call the function that draws your letteroids here
}
screen.end();
return 0;
}
#ifndef _LETTEROIDFIELD_H_
#define _LETTEROIDFIELD_H_
#include <string>
class LetteroidField
{
public:
void start(); /// start up the screen for letteroids
bool waitForKeyPress(); /// wait for any key to be pressed (return
void end(); /// shut down the screen and return it to
void setTitle(std::string); /// diplay the title
};
#endif
#ifndef _LETTEROID_H_
#define _LETTEROID_H_
class Letteroid
{
public:
void setCoords(int, int);// set the position(down, across)
void setLetter(char); // set the character
int getX(); // get the position down
int getY(); // get the position across
void erase(); // erase the letteroid from the screen
void draw(); // draw the letteroid to the screen
private:
int myX;
int myY;
char myLetter;
};
#endif
The question you need to ask yourself is: Where are those classes defined?
If the answer is: "in a shared library (file extension ".so") provided alongside the header", then you'll need to link against it by adding at least the following to your compilation command:
g++ main.cpp -L</path/to/library> -l<library_name>
If the answer is: "in a static library (file extension ".a", AKA archive) provided alongside the header", then you'll need include it in your binary by adding at least the following to your compilation command:
g++ main.cpp <library_name.a>
If the answer is: "in a bunch of source files provided alongside the header", then you'll need to include them in your binary by adding at least the following to your compilation command:
g++ main.cpp <source_file1.cpp> <source_file2.cpp> ...
I'm trying to write a function that can Shift out data to 74HC595 shift registers which can shift out 8, 16, and 32 bit values.
Using an overloaded function, I have this:
/**********************************************************************************
* Software SPI Pin Settings
*********************************************************************************/
#define SPIPINPORT PORTB //The Port that the Pins are on.
#define LatchPin 2 //_RCLK Shift register clock pin
#define DataPin 3 //SER DS Serial data input
#define ClockPin 5
/**********************************************************************************
* Preproccesor PIN to PIN Mask
*********************************************************************************/
#define LATCHMASK (1 << LatchPin)
#define MOSIMASK (1 << DataPin)
#define CLOCKMASK (1 << ClockPin)
/**********************************************************************************
* Macros
*********************************************************************************/
#define tggl(port,bit) (port)^=(1<<(bit))
#define LATCH (SPIPINPORT &=~ LATCHMASK)
#define unLATCH (SPIPINPORT |= LATCHMASK)
#define PULSE { tggl(SPIPINPORT,ClockPin); tggl(SPIPINPORT,ClockPin); }
void zShiftClass::ShiftOut(uint8_t value)
{
LATCH;
for (uint8_t i = 0; i <= 7; i++)
{
if( !!(value&(1<<i)) == true) //If value is not a 1, turn off MOSIMASK
{ SPIPINPORT |= MOSIMASK; }
else
{ SPIPINPORT &= ~MOSIMASK; }
PULSE; //Pulse the Clock
}
unLATCH;
}
void zShiftClass::ShiftOut(uint16_t value)
{
LATCH;
for (uint8_t i = 0; i <= 15; i++)
{
if( !!(value&(1<<i)) == true) //If value is not a 1, turn off MOSIMASK
{ SPIPINPORT |= MOSIMASK; }
else
{ SPIPINPORT &= ~MOSIMASK; }
PULSE; //Pulse the Clock
}
unLATCH;
}
void zShiftClass::ShiftOut(uint32_t value)
{
LATCH;
for (uint8_t i = 0; i <= 31; i++)
{
if( !!(value&(1<<i)) == true) //If value is not a 1, turn off MOSIMASK
{ SPIPINPORT |= MOSIMASK; }
else
{ SPIPINPORT &= ~MOSIMASK; }
PULSE; //Pulse the Clock
}
unLATCH;
}
And I want to use this template to replace these functions:
template<typename TYPE>void Shift(TYPE value)
{
uint8_t loops = (( 8 * sizeof(value) ) - 1 );
LATCH;
for (uint8_t i = 0; i <= loops; i++)
{
if( !!(value&(1<<i)) == true) //If value is not a 1, turn off MOSIMASK
{ SPIPINPORT |= MOSIMASK; }
else
{ SPIPINPORT &= ~MOSIMASK; }
PULSE; //Pulse the Clock
}
unLATCH;
}
When I compile, I get the following errors:
Compiling 'zLEDArray' for 'Arduino Uno'
zLEDArray.ino : variable or field 'Shift' declared void
zLEDArray.ino : 'TYPE' was not declared in this scope
Error compiling
What am I doing wrong?
Okay, this falls in the category of "beware of dev tools bearing gifts". The Arduino sketch tool is your problem. If you turn on verbose compiler output on the preferences menu, you will get some insight into what happens. With your code, I can duplicate your error. When compiling a test project called template1, the same error is reported, but now can see the compiler command line:
D:\arduino-dev\arduino-1.0.3\hardware\tools\avr\bin\avr-g++ -c ...yada yada
more yada... e:\Temp\build3528223623599856131.tmp\template1.cpp ...
template1:14: error: variable or field 'Shift' declared void
template1:14: error: 'TYPE' was not declared in this scope
The key point is that .CPP file. That is a file that the dev environment constructs from your .INO and is what is the actual input to the compiler. If you go grab that file, you will see all your code with some bonus lines included:
#include "Arduino.h"
void Shift(TYPE value);
void setup();
void loop();
The build tool added for you, 4 lines:
the Arduino header (because nobody remembers this)
3 forward declarations for functions that it figured out by parsing code
The attempt at producing a forward declaration from the function template is incorrect, and produces the code that results in the compiler error.
The solution is to move the template out from the .INO file.
Create a library folder, say T1.
Create a .H file in that folder with the template code, say tspi.h.
Import the library to your project.
Make sure the #include line is after the first line of code in your .INO (more weirdness - the tool will insert a #include "Arduino.h" after all the comments but before first line of code. If you leave your include at the top of the .INO file, it will be processed before the Arduino header)
You can get rid of the error by adding the correct forward declaration in your ino file. The inuntuitive thing is that you must also do this if you define the function before you use it:
template <typename TYPE> void Shift(TYPE value);
// you may use the function here or you can have the declaration
// immediately before the definition
template<typename TYPE>void Shift(TYPE value)
{
// your implementation
}
The explanation why this is the case is in jdr5ca's answer. Thanks for clarifying, I found this out by trial and error.