Passing a pointer (Atmel Studio 6 and Xmega) - Page 1
Forum section: electronics / general

author: David_AVD
author_rank: Super Contributor
timestamp: July 28, 2013, 09:18:23 am
content: I'm trying to convert some C++ code to use in Atmel Studio 6 with an Xmega.So far I've got all of it working apart from one function:Code:void LCD_PRINT(char *st, uint8_t x, uint8_t y){uint8_t stl, i;stl = strlen(st);x=((disp_y_size+1)-(stl*cfont.x_size))/2;for (i=0; i<stl; i++)PrintChar(*st++, x + (i*(cfont.x_size)), y);}How do I pass a string (which could be either constant or a variable) to this function?

author: Bored@Work
author_rank: Super Contributor
timestamp: July 28, 2013, 10:15:31 am
content: Strings are char pointers in C. So you just pass the string, what's the problem?If your string / char pointer is located in progmem you need to rewrite the function to access progmem instead of *st++.

author: David_AVD
author_rank: Super Contributor
timestamp: July 28, 2013, 10:35:46 am
content: After a bit more investigation, it turns out it's actually another function (called from the print one) that's causing the error. I had commented it out in the code show above as I wanted to show the bare minimum. Whoops. The code in question was for rotated text and I'll investigate that once I get the non-rotated text working.Part of the problem is I only know a little C code and I'm porting this from a C++ (Arduino) TFT library. I'm now stuck on how to get the font data working with it, so that will keep me frustrated for many more hours.

author: westfw
author_rank: Super Contributor
timestamp: July 28, 2013, 11:48:56 pm
content: Quote from: David_AVD on July 28, 2013, 09:18:23 amBeware that C++/Arduino code has two kinds of "strings."1) Standard C style strings - pointers to arrays of characters (which is what your included function is using):Code:char * myStringVar = "this is a C string in a variabel";LCD_PRINT(myStringVar, 0, 0);LCD_PRINT("this is a string constant", 0, 0);2) Members of the C++ "String" (note capitalization) class. These are dynamically allocated and garbage-collected, and "variable length":Code:String myCPPString = "This will become a C++ String variable";myCPPString += "\nI can easily modify it";Serial.print(myCPPString); // C++ will no how to treat this differently than a C string

author: David_AVD
author_rank: Super Contributor
timestamp: July 29, 2013, 12:03:42 am
content: More fun today as I threw away the Atmel ASF and started again with a "standard" project. This may also be a cause of issues.

author: David_AVD
author_rank: Super Contributor
timestamp: July 29, 2013, 12:32:05 am
content: I'm a little confused on how to correctly use include files in Atmel Studio. There is some conflicting advice out there.Say I have mymain.cfile with the main loop, etc. I also have another file with the LCD routines in it. Let's call thatlcd.cfor now.I gather that I should also have a header file calledlcd.hwith the prototypes for the functions inside thelcd.cfile.So,main.chas an entry#include "lcd.h"so that it can call the LCD routines. How dolcd.handlcd.cthen include one or the other?It seems to me thatlcd.cneeds to see it's prototypes, but where do the various include<avr/io>includes go?.

author: Psi
author_rank: Super Contributor
timestamp: July 29, 2013, 01:14:47 am
content: Im still learning C (like every other C programmer heh)So i could be doing this totally wrong, but here's how i do it in Atmel StudioI put the <avr/io.h> <avr/interrupt.h> etc. in whatever c file needs them,etc, i might isolate all the i/o code to one file, then #include <avr/io.h> only needs to be in that file.(io.h is probably a bad example but yeah, you get the idea)[File - main.c]Code:#include <avr/wdt.h> // my watchdog is reset from main so it needs this here#include "osd_spi.h" // osd library for SPI coms#include "variables.h" // this is just a list of extern variables (so they're global to all files)// global variablesuint8_t i=0;void main(){init();while(1){wdt_reset();SPI_write(VM0 ,123);}}[File - osd_spi.h]Code:#define VM0 0x00#define VM1 0x01void SPI_write(char cAddress, char cData);[File - osd_spi.c]Code:#include <avr/io.h> // file needs i/o access#include "osd_spi.h" // declarations and defines#include "variables.h" // list of variables that are going to be global between units.void SPI_write(char cAddress, char cData){// stuffi=10;}[File - variables.h]Code:extern uint8_t i; // allows i variable to be used across units when variable.h included

author: westfw
author_rank: Super Contributor
timestamp: July 29, 2013, 02:27:55 am
content: QuoteYes. lcd.h would include all the prototypes for the lcd code, and you'd #include it from both lcd.c (as forward reference/prototype definitions) and from main.c (as external references.)Fortunately, "extern" in C can be used on prototypes as well (the function doesn't have to actually be external.)Various principles of software lifecycle maintenance say that you should divide up lcd.h into it's API (those pieces that are relevant to to external modules that will call LCD functions), and the internal parts (like the primitive LCD display commands) that are only used INTERNAL to the LCD code itself. That's a good idea, and something to think about...QuoteEverywhere they are needed. Typically, that means nearly everywhere. In theory, if main.c containsCode:#include <avr/io.h>#include "lcd.h"then lcd.h does not need to also #include io.h. But it should, if it references anything from io.h; include files should not generally count on other standard include files having been included, and .h files are laid out so that they can be included multiple times without causing problems, and without too much loss of efficiency. lcd.h might get away without including io.h, if it sticks purely to LCD-related things. But lcd.c almost certainly will need to include io.h as well.(there's a principle that says .h files should all compile cleanly, by themselves. Another good idea.)

author: David_AVD
author_rank: Super Contributor
timestamp: July 29, 2013, 02:47:20 am
content: Thanks guys. That helps a lot.From the above can I deduce that C supports private and public procedures?For example, there are low level LCD routines that I don't want to be visible from the main code.

author: c4757p
author_rank: Super Contributor
timestamp: July 29, 2013, 02:54:27 am
content: Of a sort.Any function not declared "static" will be usable externally, but only if declared before the code you try to use it in. The name is still used and cannot be used again without pissing off the linker. Functions declared "static" are only visible within the current compilation unit (the C source file and anything it pulls in as an include) and do not pollute the namespace - use these for your low-level routines. Sadly there's no way (that I can think of) to make private functions that can be used across multiple units without playing around with the linker (just use a name prefix for this).

author: westfw
author_rank: Super Contributor
timestamp: July 29, 2013, 03:13:44 am
content: QuoteIndeed. The trick is to implement policies and discipline that give you most of the advantages of true private/public functions, using the somewhat primitive capabilities that are present in C.QuoteTables of function pointers. And structures that contain them. The argument for C over C++ is that C allows you to implement most of the good parts of C++, anyway. So for example, you could interface to your LCD via avr-libc's stdio facility (http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html), where lcd.c could do:Code:extern FILE *lcd = &lcdfile;FILE lcdfile = FDEV_SETUP_STREAM(lcd_putchar, NULL,_FDEV_SETUP_WRITE);and then access it via fprintf(lcd, "this is a string"); and similar, while keeping lcd_putchar() entirely "private" (name-wise.) (not necessarily the best way to access an LCD, since it's somewhat random-access compare to a pure stream device. But possible.)

author: David_AVD
author_rank: Super Contributor
timestamp: July 29, 2013, 03:29:24 am
content: Hmmm... I addedstaticto the front of each routine that I wanted to be private, but they are all still visible from the main unit.

author: c4757p
author_rank: Super Contributor
timestamp: July 29, 2013, 03:35:08 am
content: How is your code structured?

author: David_AVD
author_rank: Super Contributor
timestamp: July 29, 2013, 03:37:42 am
content: Main unit includes lcd.h unit. The lcd.h unit includes lcd.c unit. Prototypes for the "private" routines are in lcd.c unit.

author: c4757p
author_rank: Super Contributor
timestamp: July 29, 2013, 03:54:20 am
content: Don't include the C file from the H file. Remember that an include is literally a copy/paste, so now the LCD routines are inside main.c. You want to do it the other way around, include the H file from both C files. Combining the two C files should be done by compiler configuration, not by include.

author: David_AVD
author_rank: Super Contributor
timestamp: July 29, 2013, 04:35:50 am
content: So...lcd.h has nothing ?lcd.c has #include lcd.c ?main.c also has #include lcd.h ?And what gets added to the project besides main.c ?

author: c4757p
author_rank: Super Contributor
timestamp: July 29, 2013, 04:41:04 am
content: lcd.h has thedeclarations(not definitions) of the functions that you want to bepublic, as well as any data types (structs, etc) and useful macros (like perhaps the screen size).lcd.c has thedefinitionsof all the LCD functions, public and private.All C files from which you want to use the LCD functions (including lcd.c itself) include lcd.h.Never #include anything that has ".c" as an extension.I'm not familiar with Atmel Studio so I couldn't tell you how to get it to compile everything (maybe it automatically compiles any C files in the project).

author: AlfBaz
author_rank: Super Contributor
timestamp: July 29, 2013, 04:47:54 am
content: Usually the IDE will pass all of the C files through the compiler which generates an object file for each C file. The linker then joins them all together.If you don't have lcd.c in your project but it still works then it's most probably because lcd.o is part of a precompiled object library installed with the IDE.The pain with this scenario is that some vendors will give you the libraries and relevant include files but no C source files

author: David_AVD
author_rank: Super Contributor
timestamp: July 29, 2013, 04:50:53 am
content: I'm not using any canned LCD routines, just my own.If the .h file has the definitions for the functions in the .c file, does the .c file only have the actual functions ?

author: c4757p
author_rank: Super Contributor
timestamp: July 29, 2013, 04:54:08 am
content: Yes.Be careful with terminology. This is a declaration:Code:int main (int argc, char *argv[]);This is a definition:Code:int main (int argc, char *argv[]){printf ("Hello, world!\n");return 0;}

author: David_AVD
author_rank: Super Contributor
timestamp: July 29, 2013, 05:40:25 am
content: Actually, I should make use of the inbuilt C functions wherever possible. Seeing that my current function takes x/y coordinates as well as the string, is that possible?

author: c4757p
author_rank: Super Contributor
timestamp: July 29, 2013, 05:51:32 am
content: Not that I can think of. That function could be a bit neater, though, in which case you might not object to using it:Code:// Reserve all-caps for macros// "const" helps ensure you don't try to modify the string by mistake, you'll get a compiler error if you dovoid lcd_print (const char *st, uint8_t x, uint8_t y){uint8_t i;// No need to count the string's length twice. strlen() just looks for a NUL character at the end, which you// can do in your loop// stl = strlen(st);x=((disp_y_size+1)-(stl*cfont.x_size))/2; // What about this line? Why are you setting X when it's been passed in?for (i=0; st; i++) // Go until st is no longer nonzero (NUL is zero)PrintChar(st, x + (i*(cfont.x_size)), y); // Indent for clarity}So we have:Code:void lcd_print (const char *st, uint8_t x, uint8_t y){uint8_t i;x=((disp_y_size+1)-(stl*cfont.x_size))/2; // Still not sure what's going on herefor (i=0; st; i++) // Go until st is no longer nonzero (NUL is zero)PrintChar(st, x + (i*(cfont.x_size)), y);}Which is a perfectly serviceable C function in just three lines plus variable declaration.Alternately, you might investigate writing an LCD output backend for the built-in IO functions like printf(). It's not hard, but it depends on the implementation and I don't know Atmel's C library. You can use a simple cursor in that case and include a set_cursor() function.Edit: I noticed your other use ofstl. Hold on, might need a bit of an adjustment...How does PrintChar() work - what are the parameters supposed to be? I'm not sure what thexvariable is supposed to be.

author: David_AVD
author_rank: Super Contributor
timestamp: July 29, 2013, 06:55:14 am
content: It's a graphic LCD, so no cursor as such, just x and y coordinates of where to start the output.The variable x gets adjusted in the real version of the code to account for left/right/centre justification.I'll see if I can clean it up some more.

author: c4757p
author_rank: Super Contributor
timestamp: July 29, 2013, 06:56:48 am
content: But for simplicity, it may be helpful to break up the graphical LCD into a fixed character grid. You can still overlay that on whatever graphics you are also displaying.If you don't want that, what you can do is have local character grids - specify x,y and width,height, then have a cursor within that grid. This makes it easy to fill up a text box, for instance.

author: Kremmen
author_rank: Super Contributor
timestamp: July 29, 2013, 07:00:48 am
content: This may count as a stupid question, but why do you wish to convert C++ code to C (assuming you do - it appeared that way in some of your earlier posts)? AVR Studio and Atmel Studio are perfectly capable of compiling say Arduino libraries practically unmodified (ignoring any possible HW changes if you are not actually using an Arduino). Also mixing C and C++ code is not an issue so you can write whichever you prefer.Regarding .h and .c/.cpp files the logic is actually dead simple. The preprocessor will replace each #include with the text in the file referenced, exactly as it appears in the file. For any case where you invoke an external procedure or function in a compilation unit (source file), there must be some way to present the prototype of that procedure or function (i.e. its declaration) to the compiler. You can do it by typing the declaration as part of the source file itself but that is cumbersome and error prone. Enter the header file. Write the declaration once in a header and #include it everywhere it needs to go. To guard against multiple inclusion, use the #ifndef pragma to bracket the text (examples to be found in all "standard" header files). That way multiple overlapping #includes are ignored and everything works as expected.

author: David_AVD
author_rank: Super Contributor
timestamp: July 29, 2013, 10:32:57 am
content: The graphics code was ported from some Arduino examples. I'm changing it somewhat do suit my application. I simply wanted something known as a starting point, as the TFT modules are a bit fiddly to configure. The data sheet for the driver chip is a little sparse for my liking.I did notice that you can add C or C++ units in Atmel Studio. As I'm also starting to use XC8 for PICs, I'll probably stay with C for Atmel too.As for the x/y positioning, I was thinking of using structures for the (touch) buttons to store the position, size, colour, caption, etc. This will make it easier to add and move them around.

author: David_AVD
author_rank: Super Contributor
timestamp: July 29, 2013, 11:56:55 pm
content: Guys, thanks again for the explanations of the include and header files. It's now clicked and seems perfectly logical. For whatever reason I had a brain block on that yesterday.