This is the first time I am trying to build my CUDA app in NSight Ubuntu to benefit from optimization and profiling. This app works fine from terminal using nvcc (makefile) in Ubuntu 20 (or 18 ,16). I have multiple .cu, .c and .h files. All the files are first included in a flags.h file. My code starts with main.cu (has a main() function) file and this file has # include "flags.h" to make sure all the files are included to compile the code.
flags.h has a lot of #define as well to be later used in different .cu and .c files.
However, inside NSight, none of the #define parameters defined in flags.h are recognized in any of the files and I am getting error.
Following is screenshot of error .
I am attaching a simple square_array problem split in 3 files (main.cu, flags.h and square_.cu).
I can not build this in NSight. Can someone try to build it and let me know please.
Any help or suggestion will be much appreciated.
main.cu
#include <stdio.h>
#include <stdlib.h>
#include "flags.h"
int main(void) {
int i;
int *a_h, *a_d;
CUDA_CHECK_RETURN(cudaMalloc((void**) &a_d, sizeof(int) * WORK_SIZE));
a_h = (int *)malloc(sizeof(int) * WORK_SIZE); // Allocate array on host
for (i = 0; i < WORK_SIZE; i++)
a_h[i] = i+2.;
int block_size = 4;
int n_blocks = WORK_SIZE/block_size + (WORK_SIZE%block_size == 0 ? 0:1);
sq_array<<<n_blocks, block_size>>>(a_d);
CUDA_CHECK_RETURN(cudaGetLastError());
CUDA_CHECK_RETURN(cudaMemcpy(a_h, a_d, sizeof(int) * WORK_SIZE, cudaMemcpyDeviceToHost));
for (i = 0; i < WORK_SIZE; i++)
printf("Input value: %d \n", a_h[i] );
CUDA_CHECK_RETURN(cudaFree((void*) a_d));
CUDA_CHECK_RETURN(cudaDeviceReset());
return 0;
}
flags.h
#ifndef FLAGS_H_
#define FLAGS_H_
#include "square_.cu"
#define CUDA_CHECK_RETURN(value) { \
cudaError_t _m_cudaStat = value; \
if (_m_cudaStat != cudaSuccess) { \
fprintf(stderr, "Error %s at line %d in file %s\n", \
cudaGetErrorString(_m_cudaStat), __LINE__, __FILE__); \
exit(1); \
} }
#define WORK_SIZE 29
#endif /* FLAGS_H_ */
square_.cu
__global__ void sq_array( int *a) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx< WORK_SIZE) a[idx] = a[idx] * a[idx];
}
The problem is the your IDE is compiling square_.cu and compiling main.cu which is also compiling square_.cu again due to the #include "square_.cu" in flags.h which gives you two definitions of sq_array. When square_.cu is compiled the WORK_SIZE macro is not defined giving you a compile time error. When you compiled on the command line you did not compile square_.cu so you avoided this error.
In any case, it is a bad idea to #include .cu (or .c files). These should be compiled separately and then linked together.
You have to organize things differently.
I don't know the details of your code, but you can do something like this:
square.cu:
#include "square.h"
__global__ void sq_array( int *a) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx< WORK_SIZE) a[idx] = a[idx] * a[idx];
}
void host_sq_array(int *a_d) {
int block_size = 4;
int n_blocks = WORK_SIZE/block_size + (WORK_SIZE%block_size == 0 ? 0:1);
sq_array<<<n_blocks, block_size>>>(a_d);
}
square.h:
#ifndef SQUARE_H
#define SQUARE_H
#include "flags.h" // REMOVE #include of .cu file!!!
void host_sq_array(int *a_d);
#endif
You can safely #include square.h which only includes constant, type definitions, and function prototypes.
Related
I am putting together a minimal example leveraging parallelism features in C++17/20 within Matlab MEX functions. I am able to compile and run the mex function from Matlab, but when I set the execution policy of my C++ STL function to "par" instead of "seq", Matlab gives a runtime linkage complaint. Code and error message follows:
test.m (Matlab top-level script):
vec_in = zeros(5);
coeff = 0.05;
vec_out = test_mex_gateway(vec_in, coeff);
test_mex_gateway.cpp (C++ interface to Matlab):
#include "mex.h"
extern void test_execute(float *array_in, float *array_out, const size_t vec_size, const float coeff);
void mexFunction( int nlhs,
mxArray *plhs[],
int nrhs,
const mxArray *prhs[] )
{
// Check for proper number of input and output arguments
if( nrhs != 2 )
{
mexErrMsgTxt( "3 input arguments required: input_data, coeff" );
}
if( nlhs > 2 )
{
mexErrMsgTxt( "Too many output arguments." );
}
const mwSize *matlab_data_dims_in;
mwSize matlab_data_dims_out[1];
// Input Parameters
float *input_data = (float *) mxGetData(prhs[0]);
float coeff = mxGetScalar(prhs[1]);
// Get dimensions
matlab_data_dims_in = mxGetDimensions(prhs[0]);
const int vec_len = matlab_data_dims_in[1];
// Set output data dimension
matlab_data_dims_out[0] = vec_len;
// Output data
plhs[0] = mxCreateNumericArray(1, matlab_data_dims_out, mxSINGLE_CLASS, mxREAL);
float *output_data = (float *) mxGetData(plhs[0]);
test_execute(input_data, output_data, vec_len, coeff);
}
test_execute.cpp (This is where the actual C++ STL call is made):
#include <execution> // std::execution::*
#include <numeric> // std::exclusive_scan()
void test_execute(float *array_in, float *array_out, const size_t vec_size, const float coeff)
{
std::exclusive_scan
(
std::execution::par, // std::execution::seq works here for Mex call, par does not
array_in,
array_in + vec_size,
array_out,
0.0f,
[coeff](float a, float b)
{
float ret = a + b + coeff;
return ret;
}
);
}
I also have a stand-alone main function to replace the Mex wrapper to do a pure C++ test, test_standalone.cpp:
#include <vector>
#include <iostream>
size_t VEC_NUM_ELEM = 10;
extern void test_execute(float *array_in, float *array_out, const size_t vec_size, const float coeff);
int main(int argc, char **argv)
{
if (argc != 2)
{
std::cout << "Try: " << argv[0] << "<coeff>" << std::endl;
return -1;
}
const float coeff = std::stof(argv[1]);
std::cout << "Coeff: " << coeff << std::endl;
float __attribute__ ((aligned (64))) *vec1_array = (float *)malloc(VEC_NUM_ELEM * sizeof(float));
float __attribute__ ((aligned (64))) *vec2_array = (float *)malloc(VEC_NUM_ELEM * sizeof(float));
for (unsigned i = 0; i < VEC_NUM_ELEM; i++)
{
vec1_array[i] = static_cast<float>(i);
}
test_execute(vec1_array, vec2_array, VEC_NUM_ELEM, coeff);
return 0;
}
Here is how I am building and linking, build.sh:
#!/bin/bash
rm *.o
rm *.exe
rm *.mexa64
cstd=c++17
gpp910=/home/m/compilers/bin/g++
tbblib=/home/m/reqs/tbb/lib/intel64/gcc4.8
echo "Building test_execute.cpp..."
$gpp910 -std=$cstd -I/home/m/reqs/tbb/include -L$tbblib -ltbb -Wl,rpath=$tbblib -c test_execute.cpp -fPIC
echo "Building test_standalone.cpp..."
$gpp910 -std=$cstd -L$tbblib test_execute.o test_standalone.cpp -o test_standalone.exe -ltbb
echo "Building test_mex_gateway.cpp..."
mex test_execute.o test_mex_gateway.cpp -L$tbblib -ltbb
The parallel STL calls has a requirement to link against the Intel TBB (Threading Building Blocks), so before I run Matlab to call test.m OR before I run my test_standalone.exe, I run:
export LD_LIBRARY_PATH=/home/m/reqs/tbb/lib/intel64/gcc4.8:$LD_LIBRARY_PATH
I also make sure to make the the C++ library associated with the version of GCC we built with available at runtime:
export LD_LIBRARY_PATH=/home/m/compilers/lib64:$LD_LIBRARY_PATH
When I run test_standalone.exe, everything behaves normally whether I have the execution policy set to "par" or "seq" on std::exclusive_scan. When run test.m, if "seq" was compiled, I can run with no errors. If "par" was compiled, Matlab complains at runtime about a linkage issue:
Invalid MEX-file 'test_mex_gateway.mexa64': test_mex_gateway.mexa64: undefined symbol:
_ZN3tbb10interface78internal20isolate_within_arenaERNS1_13delegate_baseEl
I suspect this was a function that was supposed to be linked from TBB, which I confirmed:
$ nm /home/m/reqs/tbb/lib/intel64/gcc4.8/libtbb.so.2 | grep baseEl
0000000000028a30 T _ZN3tbb10interface78internal20isolate_within_arenaERNS1_13delegate_baseEl
000000000005ed70 r _ZN3tbb10interface78internal20isolate_within_arenaERNS1_13delegate_baseEl$$LSDA
I confirmed Matlab's LD_LIBRARY_PATH has the path I supplied in the above "export .." to this library.
I tried making sure my libraries came before the many Matlab-centric paths Matlab adds to LD_LIBRARY_PATH after it launches from the terminal.
I tried baking the path to the linked libraries via a -Wl,rpath=<path_to_tbb.so> passage to the linker.
After almost two days, I can't figure out why Matlab is having this very specific runtime issue, especially when the pure C++ version is not. Any help would be appreciated.
RHEL 7.9
Matlab R2020a
GCC 9.1.0
TBB (Intel Thread Building Blocks) 2020.3
It appears that Matlab comes with a version of libtbb.so included in its installation. From what I can tell, when launching a Mex file, Matlab will use its own libraries first, regardless of your LD_LIBRARY_PATH order. This is what was giving me runtime issues as a Mex file but not as a pure C++ file. Removing the libtbb.so from Matlab's installation directory allowed runtime linkage to find my version of libtbb, and I was able to run without errors. Thanks to Cris Luengo for pointing me in the right direction.
I have created with GIMP a C-Source image dump like the following:
/* GIMP RGBA C-Source image dump (example.c) */
static const struct {
guint width;
guint height;
guint bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */
guint8 pixel_data[304 * 98 * 2 + 1];
} example= {
304, 98, 2,
"\206\061\206\061..... }
Is there a way to read this in GIMP again in order to get back the original image? because it doesn't seem possible.
Or does it exist a tool that can do this back-conversion?
EDITED
Following some suggestion I tried to write a simple C programme to make the reverse coversion ending up with something very similar to another code found on internet but both dont work:
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "imgs_press.h"
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
using namespace std;
int main(int argc, char** argv) {
int fd;
char *name = "orignal_img.pnm";
fd = open(name, O_WRONLY | O_CREAT, 0644);
if (fd == -1) {
perror("open failed");
exit(1);
}
if (dup2(fd, 1) == -1) {
perror("dup2 failed");
exit(1);
}
// file descriptor 1, i.e. stdout, now points to the file
// "helloworld" which is open for writing
// You can now use printf which writes specifically to stdout
printf("P2\n");
printf("%d %d\n", press_high.width, press_high.height);
for(int x=0; x<press_high.width * press_high.height * 2; x++) {
printf("%d ", press_high.pixel_data[x]);
}
}
As suggested by n-1-8e9-wheres-my-share-m, maybe I need to manipulate the pixels usign the correct decode, but I have no idea how to do that, does anybody have other suggestions?
The image I got is indeed distorted:
Updated Answer
If you want to decode the RGB565 and write a NetPBM format PNM file without using ImageMagick, you can do this:
#include <stdint.h> /* for uint8_t */
#include <stdio.h> /* for printf */
/* tell compiler what those GIMP types are */
typedef int guint;
typedef uint8_t guint8;
#include <YOURGIMPIMAGE>
int main(){
int w = gimp_image.width;
int h = gimp_image.height;
int i;
uint16_t* RGB565p = (uint16_t*)&(gimp_image.pixel_data);
/* Print P3 PNM header on stdout */
printf("P3\n%d %d\n255\n",w, h);
/* Print RGB pixels, ASCII, one RGB pixel per line */
for(i=0;i<w*h;i++){
uint16_t RGB565 = *RGB565p++;
uint8_t r = (RGB565 & 0xf800) >> 8;
uint8_t g = (RGB565 & 0x07e0) >> 3;
uint8_t b = (RGB565 & 0x001f) << 3;
printf("%d %d %d\n", r, g ,b);
}
}
Compile with:
clang example.c
And run with:
./a.out > result.pnm
I have not tested it too extensively beyond your sample image, so you may want to make a test image with some reds, greens, blues and shades of grey to ensure that all my bit-twiddling is correct.
Original Answer
The easiest way to get your image back would be... to let ImageMagick do it.
So, take your C file and add a main() to it that simply writes the 304x98x2 bytes starting at &(example.pixel_data) to stdout:
Compile it with something like:
clang example.c -o program # or with GCC
gcc example.c -o program
Then run it, writing to a file for ImageMagick with:
./program > image.bin
And tell ImageMagick its size, type and where it is and what you want as a result:
magick -size 304x98 RGB565:image.bin result.png
I did a quick, not-too-thorough test of the following code and it worked fine for an image I generated with GIMP. Note it doesn't handle alpha/transparency but that could be added if necessary. Save it as program.c:
#include <unistd.h> /* for write() */
#include <stdint.h> /* for uint8_t */
/* tell compiler what those GIMP types are */
typedef int guint;
typedef uint8_t guint8;
<PASTE YOUR GIMP FILE HERE>
int main(){
/* Work out how many bytes to write */
int nbytes = example.width * example.height * 2;
/* Write on stdout for redirection to a file - may need to reopen in binary mode if on Windows */
write(1, &(example.pixel_data), nbytes);
}
If I run this with the file you provided via Google Drive I get:
I am trying to call a C file (dispmanx.c) from a C++ file (main.cpp).
dispmanx.c :
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <unistd.h>
#include <sys/time.h>
#include "bcm_host.h"
#define WIDTH 200
#define HEIGHT 200
#ifndef ALIGN_UP
#define ALIGN_UP(x,y) ((x + (y)-1) & ~((y)-1))
#endif
int run(unsigned char* fileData)
{
typedef struct
{
DISPMANX_DISPLAY_HANDLE_T display;
DISPMANX_MODEINFO_T info;
void *image;
DISPMANX_UPDATE_HANDLE_T update;
DISPMANX_RESOURCE_HANDLE_T resource;
DISPMANX_ELEMENT_HANDLE_T element;
uint32_t vc_image_ptr;
} RECT_VARS_T;
static RECT_VARS_T gRectVars;
RECT_VARS_T *vars;
uint32_t screen = 1;
int ret;
VC_RECT_T src_rect;
VC_RECT_T dst_rect;
VC_IMAGE_TYPE_T type = VC_IMAGE_RGB565;
int width = WIDTH, height = HEIGHT;
int pitch = ALIGN_UP(width*2, 32);
int aligned_height = ALIGN_UP(height, 16);
VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS,
120, /*alpha 0->255*/
0 };
vars = &gRectVars;
bcm_host_init();
printf("Open display[%i]...\n", screen );
vars->display = vc_dispmanx_display_open( screen );
ret = vc_dispmanx_display_get_info( vars->display, &vars->info);
assert(ret == 0);
printf( "Display is %d x %d\n", vars->info.width, vars->info.height );
vars->image = fileData;
// vars->image = calloc(1, pitch * height);
assert(vars->image);
vars->resource = vc_dispmanx_resource_create( type,
width,
height,
&vars->vc_image_ptr );
assert( vars->resource );
vc_dispmanx_rect_set( &dst_rect, 0, 0, width, height);
ret = vc_dispmanx_resource_write_data( vars->resource,
type,
pitch,
vars->image,
&dst_rect );
assert( ret == 0 );
vars->update = vc_dispmanx_update_start( 10 );
assert( vars->update );
vc_dispmanx_rect_set( &src_rect, 0, 0, width << 16, height << 16 );
vc_dispmanx_rect_set( &dst_rect, ( vars->info.width - width ) / 2,
( vars->info.height - height ) / 2,
width,
height );
vars->element = vc_dispmanx_element_add( vars->update,
vars->display,
2000, // layer
&dst_rect,
vars->resource,
&src_rect,
DISPMANX_PROTECTION_NONE,
&alpha,
NULL, // clamp
VC_IMAGE_ROT0 );
ret = vc_dispmanx_update_submit_sync( vars->update );
assert( ret == 0 );
printf( "Sleeping for 10 seconds...\n" );
sleep( 10 );
vars->update = vc_dispmanx_update_start( 10 );
assert( vars->update );
ret = vc_dispmanx_element_remove( vars->update, vars->element );
assert( ret == 0 );
ret = vc_dispmanx_update_submit_sync( vars->update );
assert( ret == 0 );
ret = vc_dispmanx_resource_delete( vars->resource );
assert( ret == 0 );
ret = vc_dispmanx_display_close( vars->display );
assert( ret == 0 );
return 0;
}
#ifdef __cplusplus
}
#endif
the main.cpp:
#include "dispmanx.c"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
Mat image;
image = imread("t.png", IMREAD_COLOR); // Read the file
run(image.data);
}
the code should take an address of a png file get the data of it using OpenCV libraries and pass it to the C program which shows the picture on the screen.
I could compile the dispmanx.c:
pi#raspberrypi:~/openCVtest.1 $ gcc -I/opt/vc/include/
-I/opt/vc/include/interface/vcos/pthreads
-I/opt/vc/include/interface/vmcs_host/linux
-I/opt/vc/src/hello_pi/libs/ilclient
-I/opt/vc/src/hello_pi/libs/vgfont
-L/opt/vc/lib/ -L/opt/vc/src/hello_pi/libs/ilclient
-L/opt/vc/src/hello_pi/libs/vgfont
-c dispmanx.c
-o dispmanx.o
-lbrcmGLESv2 -lbrcmEGL -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt -lm
but when I try to compile the main.cpp:
pi#raspberrypi:~/openCVtest.1 $ g++ -I/opt/vc/include/ -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/src/hello_pi/libs/ilclient -I/opt/vc/src/hello_pi/libs/vgfont -L/opt/vc/lib/ -L/opt/vc/src/hello_pi/libs/ilclient -L/opt/vc/src/hello_pi/libs/vgfont -c main.cpp dispmanx.o -o main.o -lbrcmGLESv2 -lbrcmEGL -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt -lm In file included from main.cpp:2:0:
dispmanx.c: In function ‘int run(unsigned char*)’:
dispmanx.c:52:68: error: invalid conversion from ‘int’ to ‘DISPMANX_FLAGS_ALPHA_T’ [-fpermissive]
VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dispmanx.c:111:63: error: cannot convert ‘VC_IMAGE_TRANSFORM_T’ to ‘DISPMANX_TRANSFORM_T’ for argument ‘10’ to ‘DISPMANX_ELEMENT_HANDLE_T vc_dispmanx_element_add(DISPMANX_UPDATE_HANDLE_T, DISPMANX_DISPLAY_HANDLE_T, int32_t, const VC_RECT_T*, DISPMANX_RESOURCE_HANDLE_T, const VC_RECT_T*, DISPMANX_PROTECTION_T, VC_DISPMANX_ALPHA_T*, DISPMANX_CLAMP_T*, DISPMANX_TRANSFORM_T)’
VC_IMAGE_ROT0 );
I have tested both program separately and the function as they should, however when I tried to mix them I get errors that I cant understand.
I am a student have beginner level of knowledge. what am I doing wrong?
Given these contents of dispmanx.c:
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <unistd.h>
#include <sys/time.h>
and main.cpp starts with
#include "dispmanx.c"
You are going to have serious problems.
You've wrapped entire standard header files such as unistd.h with extern "C", and then #include'd them in a C++ file.
They're not meant to be used that way in C++ code.
#include'ing a source file (.c, .cpp, etc) is a fundamentally bad idea in the first place. Doing it across different languages such a C and C++ and then improperly wrapping system headers with extern "C" is even worse. You can't safely compile C code as C++ code, and you certainly can't wrap system header files with extern "C" for use in C++ code.
extern "C" does not make it safe to compile C code with a C++ compiler. They are different languages, with subtle distinctions.
The problem is here:
#include "dispmanx.c"
Including a *.c file in a *.cpp file is not a good idea. C and C++ are similar languages, so it may look that it works at first (i.e. the compiler reports the error not at line 1 but at line 111), but actually it doesn't.
One correct way to call C code from C++ code is by providing a declaration like this:
// C++ code
#include <iostream>
extern "C" {
int run(unsigned char* fileData); // declaration for the C code
}
...
int main()
{
...
run(image.data); // calling the C code
}
The declaration for the C code can be in the *.cpp file or, more conventionally, in a separate dedicated *.h file. If your C code is in dispmanx.c, then a conventional name is dispmanx.h:
// dispmanx.h
extern "C" {
int run(unsigned char* fileData); // declaration for the C code
}
// main.cpp
#include <iostream>
#include "dispmanx.h"
...
int main()
{
...
run(image.data); // calling the C code
}
Also, another convention: if you want to make your new dispmanx.h file compilable in both C and C++, you should hide the extern "C" part from the C compiler, as described in this question and answer.
Aloha all!
I'm working with the following script (which I did not write). This is one of many files I've been working on modifying to initiate a build/make on Linux.
Everything I've found online suggests that sys/sysctl.h should properly declare these functions:
CTL_HW and HW_NCPU
However, running the following (called "machineInfo.cpp"):
#include "machineInfo.h"
#include <sys/sysctl.h>
#include <linux/sysctl.h>
#include <cstdio>
#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
int StMachineInfo::numProcs(void) {
int numCPU = 0;
int nprocs;
size_t len = sizeof(nprocs);
static int mib[2] = { CTL_HW, HW_NCPU };
/* get the number of CPUs from the system */
sysctl(mib, 2, &numCPU, &len, NULL, 0);
if( numCPU < 1 )
{
mib[1] = HW_NCPU;
if (sysctl (mib, ARRAY_SIZE(mib), &nprocs, &len, NULL, 0) == 0 && len == sizeof (nprocs) && 0 < nprocs)
numCPU = nprocs;
if( numCPU < 1 )
numCPU = 1;
}
return numCPU;
}
...results in the following error output:
g++ -c machineInfo.cpp
machineInfo.cpp: In function ‘int StMachineInfo::numProcs()’:
machineInfo.cpp:14:24: error: ‘CTL_HW’ was not declared in this scope
static int mib[2] = { CTL_HW, HW_NCPU };
^
machineInfo.cpp:14:32: error: ‘HW_NCPU’ was not declared in this scope
static int mib[2] = { CTL_HW, HW_NCPU };
^
Makefile:33: recipe for target 'machineinfo.o' failed
make: *** [machineinfo.o] Error 1
Is there something wrong with the code itself? Or do I need to #include another header? I've experimented with this and Googled for a couple of hours, to no avail.
Many thanks,
Sean
I believe the problem here is that sysctl does not have a glibc wrapper on Linux. By my best understanding, those constants are only available on BSD.
I'd be happy to be proven wrong, as I'm trying to understand if this uname -p behavior could ever work on Linux.
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);