Formatting char arrays for USART transmission

Go To Last Post
19 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hey everyone!

I've been working on a project to re-sharpen my C skills which have been growing a bit soft. I started out just writing a few basic programs (I'm running Fedora C20) and compiling/running them on the command line. Now I'm back to programming AVRs again, which is fun and a pain at the same time. I've got a SparkFun RedBoard, but I've erased the bootloader and I'm just using it as a dev board for the Atmega328P on-board. This way I can have more control, and not have to deal with the Arduino IDE at all.

 

So, I've been working on a really basic program that calculates sines and, originally, printed them out on the command line. So I know the sine calculation part works (at least, on a computer). The printf() function is a wonderful thing -- what I'm trying to do now is send formatted strings over the UART. I've been Googling around but I feel like I'm just not searching for the correct search term, cause I really can't find what I'm looking for.

 

I have a sinescalc function, which has a pointer passed to it, which has malloc'd a large enough area to hold 255 doubles. It then calculates 255 sines, and puts each value in the memory area I have pointed to. This is all working (at least I'm fairly sure).

 

Then, when control comes back to main() I want to print these doubles out over UART along with some text per line. printf() handles this on the PC very easily, as I'm sure you all know. How do I format each double? And seeing as how I have to send things a byte at a time, I'm casting the double to char so that each digit is a separate entity. Does that make sense?

 

What I want to do is something like this. Of course this code doesn't work AT ALL I'm just using it to demonstrate the concept I'm getting at:

 

        int b = sizeof(*sines);
        int c;
        for(c = 0; c != b; c++) {
            temp[c] = strcat((char)(*sines), "is a value! \n");
        }

 

The pointer arithmetic is handled separately.

The problem is, how do I get a character at a time back from strcat? The compiler clearly doesn't understand what I'm trying to do, what strcat returns won't fit into a single char.

 

Is there a library for the AVR that handles this sort of thing? I'm pretty stuck, can't figure out what the solution will be...

 

Hopefully I've illustrated my problem well enough... if you're not getting what I'm trying to do, perhaps I can rewrite what I've said a little better.

 

Last Edited: Sun. Oct 5, 2014 - 11:32 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why can you not use printf on the AVR?

 

i don't understand what you're trying to do with strcat - it concatenates strings. At a minimum, it might give you  string with one char.

 

the uart is no different to printing stuff to your screen - it takes one char at a time. Given a null terminated string, index each char until you get a 0 value.

 

ie

void pstring(char *str)

{

while (*str) {

uart_putch(*str++);

  }

}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Excuse me, but do you want to transmit raw binaries values -they are unprintable, making traditional terminals useless (gtkterm can cope with them, with indianity issues) - or already converted to strings ones (takes a lot more space: can be read by a human being; any terminal emulator can cope with them)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

dbrion0606 wrote:

(gtkterm can cope with them, with indianity issues)

 

I like it.    Can it cope with "Extreme Burner" as well ?

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Casting a double to char on an microcontroller does for example have a different meaning in C# etc.

If your double is 4 bytes and char is 8 bits. For eg 0xaabbccdd casting it to a char will give you 0xdd. The other data is lost.

what you need is something like:

double value;

value = 123456;

char str[128];

sprintf(str,"%dl is a value", value)

 

for( i = 0; i < sizeof(str); i++)

{

 if( str[i] != Null)

   putc(str[i];

else

{

putc(0x0d);

putc(0x0a);// if i rememer right..

 

}

 

 

}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I want it to be ASCII-formatted so that terminals can use it, not just the raw binary data.

 

The reason I was using strcat() was to add the words onto the value, to try and create a new character array. What I think I'll do is just use a pointer to each character of the array of chars, once created, to send them one at a time, or something like that. I've used the UART countless times before, but I've never had to mix values and strings before, at least not like this. I just was probably over thinking a little bit.

 

Here's my full, revised code, which is (sort of) working... only problem is it's giving me gibberish. This is the first time I've used a pointer to an array, all declared at once (according to C in a Nutshell, that's how you do it? I was using malloc() before)

 

#include <avr/io.h>
#include <util/delay.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
void sinescalc(double *sinptr, int cycles, int size) {
    
    double pi = acos(-1);
    double step = ((pi * (cycles*2))/ size);
    
    double temp;
    float z = step;
    int y;
    for(y = 0; y<= size; y++) { // fill the given array with sines
        temp = sin(z);
        *sinptr = temp;
        z += step;
        sinptr++; // should move the pointer by the size of a double
    }
}
int main(void) {
    unsigned long _fosc = 16000000;
    unsigned int _baud = 19200;
    unsigned long _myubrr = _fosc/16/_baud-1;
    unsigned int array_size = 255;
    UBRR0L = (unsigned char)_myubrr;
    UCSR0B = (1<<RXEN0)|(1<<TXEN0); //enable receiver and transmitter
    
    double (*sines)[array_size]; // declare a pointer to an array of doubles
    double *sinepointer = sines[0]; // set sinepointer to first element of sines array
    sinescalc(sinepointer, 2, array_size); // calculate two cycles of sine, with 255 data points
    int y;
    char msg[] = ("Sine: \n");
    char temp;
    for(y = 0; y <= array_size; y++) {
        int q = (sizeof(msg)); // not sure if that will just return the size of a char or the size of the array?
        int r;
        for(r = 0; r != q; r++) { // loop through the above message
            while(!(UCSR0A & (1<<UDRE0))); 
            UDR0 = msg[q]; // put data into buffer
        }
        int shift;
        for(shift = 24; shift >= 0; shift -= 8) { // if we're casting a double to something that can be sent, we need to shift all four bytes
            temp = (long int)(*sines) >> shift;
            while(!(UCSR0A & (1<<UDRE0)));
            UDR0 = temp;
        }
        sines++;  // pointer math! yay!    
    }
    
    
    return 0;
}

 

Last Edited: Sat. Oct 4, 2014 - 03:28 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Again, why not use printf/sprintf? You're really making things hard for yourself methinks. 

http://ugweb.cs.ualberta.ca/~c27...

Last Edited: Sat. Oct 4, 2014 - 06:30 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have never seen pointer arithmetic like this.

It begs for bugs.

Just use an array or array of arrays.

 

double array_of_arrays[ 128][256]; // 128K bytes in size

 

Then de-reference in your pointer parameter:

 

bla(&array_of_arrays

, etc)

 

"&" reads as ("address of" array[your_index]), which is the correct pointer without error to one of the arrays in the array of arrays.

You need to understand also string formatting.

Again sprintf !!!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

* If you use delay (else, why include delay.h?) you will get strnge results  (delays 16 times too fast) as delay.h needs F_CPU ( _fosc should be an F_CPU alias) being defined before it used (else, default value is 1 Mhz, making delayss too short).

 

* You send long/float as raw binaries : on the PC side, they will be unprintable (then, you need a special software on PC side, with indiannness conversion, maybe)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

dbrion0606 wrote:

* If you use delay (else, why include delay.h?) you will get strnge results  (delays 16 times too fast) as delay.h needs F_CPU ( _fosc should be an F_CPU alias) being defined before it used (else, default value is 1 Mhz, making delayss too short).

 

* You send long/float as raw binaries : on the PC side, they will be unprintable (then, you need a special software on PC side, with indiannness conversion, maybe)

 

F_CPU is defined in my makefile. I had it included in a previous commit, as I had gotten a compiler warning about something else.

 

I didn't think printf() was supported on the AVR? How would it work? You'd need to specify the output stream I'm guessing? I'm going to check out the link that Kartman provided in a minute. Do you think you could show me a basic example of it's usage? That page doesn't really help much. I've never used that function on an AVR.

 

Again, this isn't meant to be a streamlined process, this is just a project to help refresh my C skills, which it is doing quite well so far! 

 

 

Pointer arithmetic is not (necessarily) going to cause any bugs. As a matter of fact, it's one of the most powerful parts of the C language. Yes, I could just dereference an array, but I already know how to do that. I wanted to learn more about pointers to arrays, etc. This is more of an 'academic' project than a practical one.

 

Again, thanks for all the help and suggestions guys! AVRFreaks are always great!

 

EDIT: So I've got it working, it's correctly printing Sine: and then a hex number. The problem is, at first I wasn't sure if doubles were 32 or 64 bits on an AVR. I'm assuming they're 64 now, but the numbers it's spitting out are WAY off. Assuming that is uses the standard sign|exponent|mantissa format... For example, one number is:

0x0000000000007488

another is

0xFFFFFFFFFFFF807C

 

My knowledge of signed numbers seems to indicate, based on the patterns I'm seeing, that the numbers go positive, then negative, then positive, then negative again, just as they should through two full cycles of sine. The problem is the exponents don't seem to be coming out correctly. Not sure what the problem is here? Should I be casting the result of sin() to a double? I thought you could directly assign a float into a double, and that it would take care of all the stuff for you?

Maybe not!

 

 

Last Edited: Sat. Oct 4, 2014 - 07:45 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Pointer arithmetic is not (necessarily) going to cause any bugs. As a matter of fact, it's one of the most powerful parts of the C language.

Not only does it provide the gun with which to shoot your foot,   it also supplies the rope for hanging yourself!

 

Yes,   of course it works fine if you don't make any mistakes.    So I suggest that you do your C revision on the PC.     Then you will have learned how to avoid errors.

 

Most C libraries will have printf() going to a 'standard' device.

If in doubt,   just use sprintf() and your own output function for the resultant string.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:

Pointer arithmetic is not (necessarily) going to cause any bugs. As a matter of fact, it's one of the most powerful parts of the C language.

Not only does it provide the gun with which to shoot your foot,   it also supplies the rope for hanging yourself!

 

Yes,   of course it works fine if you don't make any mistakes.    So I suggest that you do your C revision on the PC.     Then you will have learned how to avoid errors.

 

Most C libraries will have printf() going to a 'standard' device.

If in doubt,   just use sprintf() and your own output function for the resultant string.

 

David.

 

Yeah, here's my revised code, with stdout set up to go to the UART (much easier than I thought!):

#include <avr/io.h>
#include <util/delay.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

static int uart_putchar(char c, FILE *stream);

static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);

static int uart_putchar(char c, FILE *stream) {
	if (c == '\n')
		uart_putchar('\r', stream);
	while(!(UCSR0A & (1<<UDRE0)));
	UDR0 = c;
	return 0;
}

void sinescalc(double *sinptr, int cycles, int size) {
	
	double pi = acos(-1);
	double step = ((pi * (cycles*2))/ size);
	
	double temp;
	float z = step;

	int y;
	for(y = 0; y<= size; y++) {
		temp = (double)sin(z);
		*sinptr = temp;
		z += step;
		sinptr++; // should move the pointer by the size of a double
	}
}

int main(void) {

	unsigned long _fosc = 16000000;
	unsigned int _baud = 19200;
	unsigned long _myubrr = _fosc/16/_baud-1;
	unsigned int array_size = 255;

	UBRR0L = (unsigned char)_myubrr;
	UCSR0B = (1<<RXEN0)|(1<<TXEN0); //enable receiver and transmitter
	
	stdout = &mystdout;
	double (*sines)[array_size];
	double *sinepointer = sines[0]; // set sinepointer to first element of sines array

	sinescalc(sinepointer, 2, array_size); // calculate two cycles of sine, with 255 data points

	int y;
	char msg[] = ("Sine: ");
	char temp;

	for(y = 0; y <= array_size; y++) {
		printf(&msg);
		printf("\t"); // tab
		int shift;
		for(shift = 56; shift >= 0; shift -= 8) { // doubles are 64 bits?? on an AVR??
			temp = (int)(*sines) >> shift;
			printf(&temp);
			
		}
		printf("\n");
		sines++;  // pointer math! yay!	
	}
	
	

	return 0;
}

I'm having slightly different problems now, when passing printf() the temp variable, it's really... messed up. It's printing some raw hex data, as I expected, but the output it's giving me is even less sensible than what I was getting before. I'm not sure if printf() modifies the strings at all? Trying to figure out what it wants, or if I should be using a different function. 

 

However, the sinescalc function does seem to be working properly, which is great. What I need now is to format a double as a printable string... is that what the other users were suggesting? There was some function someone was suggesting... argh I really don't like the new AVRFreaks reply screen! Where are everyone's replies? I like it when it shows the whole thread in reverse order below the edit box. Every forum I've ever used has had an option to turn this on or off, it seems the options here now don't let you modify your viewing options at all. Pretty disappointed by this!

 

Thanks again david.prentice. Yes, you're correct, pointer arithmetic can get you into loads of trouble, especially if you don't think it through thoroughly. But I'm fairly sure I've got a good understanding of how it all works now. 

Last Edited: Sat. Oct 4, 2014 - 08:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My knowledge of signed numbers seems to indicate, based on the patterns I'm seeing, that the numbers go positive, then negative, then positive, then negative again, just as they should through two full cycles of sine. The problem is the exponents don't seem to be coming out correctly. Not sure what the problem is here? Should I be casting the result of sin() to a double? I thought you could directly assign a float into a double, and that it would take care of all the stuff for you?

Maybe not!

DEFINITELY NOT:

float z = (float)step;

printf can be set up to use the UART.

There are also functions putchar(char x) and putstr(*string)

Clearly you are trying to learn by not consulting the ample documentation or listening to good advise.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

turbotronic wrote:

My knowledge of signed numbers seems to indicate, based on the patterns I'm seeing, that the numbers go positive, then negative, then positive, then negative again, just as they should through two full cycles of sine. The problem is the exponents don't seem to be coming out correctly. Not sure what the problem is here? Should I be casting the result of sin() to a double? I thought you could directly assign a float into a double, and that it would take care of all the stuff for you?

Maybe not!

DEFINITELY NOT:

float z = (float)step;

printf can be set up to use the UART.

There are also functions putchar(char x) and putstr(*string)

Clearly you are trying to learn by not consulting the ample documentation or listening to good advice.

 

 

Wow, thanks for the judgement. I AM listening to your advice, and I've read all the documentation given here in this thread. All that was told in that page was just a definition of a function, not how to use it or what it requires. I've now got printf set up to use the UART, if you had read my above post.

 

Anyway, that crap aside, everything is now working except printf() is giving me question marks where the numbers should be. I've called sprintf() to format the string. Perhaps I can just call puts directly? Does setting up the UART as stdout also set it up for all the functions inside stdio.h

Last Edited: Sat. Oct 4, 2014 - 08:48 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
for(y = 0; y <= array_size; y++) 
{
		printf(&msg);//WRONG is is printf(msg) or printf(&msg[0])
		printf("\t"); // tab
		int shift;
		for(shift = 56; shift >= 0; shift -= 8) { // doubles are 64 bits?? on an AVR??
			temp = (int)(*sines) >> shift; //WTF?
			
                 printf(&temp);// two bugs, first you are trying to print a binary value, second you are printing the pointer value of temp
			
		}
		printf("\n");
		sines++;  // pointer math! yay!	naw..
	}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, I've completely changed that section now. I've also changed sines to just a double array, and let the sinepointer do the pointing for the call to sinescalc. Simply a LOT easier! Thanks for the pointers everyone.

 

for(y = 0; y <= array_size; y++) {
        printf(msg);
        printf("\t"); // tab
        sprintf(output, "%2.4f", sines[y]);
        printf(output);
        printf("\n");
    }

 

By the way, the printf(&msg) actually was working fine.

 

That all being said, my above comment still stands: I'm not sure if it's sprintf or printf that is causing the output to just be question marks. Trying to figure this one out on my own. Sorry for not posting my changes sooner, if that confused anyone.

 

And thanks turbotronic, for souring my experience here in this forum. I suppose I'll just go elsewhere for help now.

Last Edited: Sat. Oct 4, 2014 - 08:54 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MrAureliusR wrote:

Yes, I've completely changed that section now. I've also changed sines to just a double array, and let the sinepointer do the pointing for the call to sinescalc. Simply a LOT easier! Thanks for the pointers everyone.

 

for(y = 0; y <= array_size; y++) {
        printf(msg);
        printf("\t"); // tab
        sprintf(output, "%2.4f", sines[y]);
        printf(output);
        printf("\n");
    }

 

By the way, the printf(&msg) actually was working fine.

 

That all being said, my above comment still stands: I'm not sure if it's sprintf or printf that is causing the output to just be question marks. Trying to figure this one out on my own. Sorry for not posting my changes sooner, if that confused anyone.

 

And thanks turbotronic, for souring my experience here in this forum. I suppose I'll just go elsewhere for help now.

 

try sprintf(output, "%f", (float)sines[y]);

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just did a quick google:

https://www.avrfreaks.net/forum/s...

 

You need to link in libm.a and set the vprintf option on the compiler command line. I believe there is an option in the atmel studio6 GUI that does this for you. It's a common problem, so it's been solved before.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Not really sure why but OP asked me to lock this and it is his thread so... 

Topic locked