sprintf not working properly?

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

So I'm stumped as to why the following code works fine with the standard gcc compiler printing to stdout with printf() but doesn't work on the AVR atmega8. Is there something wrong with my implementation?

(I'm sure the lcd code works properly and the lcd is hooked up properly.)

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

#ifndef F_CPU
#define F_CPU 1000000UL //so our code knows the chip clock is 1mhz for delay()
#endif
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 8UL))) - 1)
//table 52 page 132 of the atmega8 shows math for calculating prescale
//double speed mode(u2x=1) is multiply by 8
//asynchronous normal mode(u2x=0) is multiply by 16
#include <util/delay.h>

#include "lcd/lib/lcd.c"
#include "lcd/lib/lcd.h"

/*this function chops up large strings and prints them to the 16x2 LCD with a short delay
* this is needed because output from other devices to our mega can be larger than 16 characters
* int buffMax; maximum amount of preallocated character places 8 bits per character, size is 400 bits
* char source; an array of characters of any length to be printed to LCD
* requires: stdlib.h stdio.h string.h avr/io.h util/delay.h lcd/lib/lcd.c lcd/lib/lcd.h
* returns 1 for success, 0 for failure
*/
int bigStringLCDPrint(char source[], int buffMax){
	int LCDSize = 16;//if using a bigger LCD you can adjust here
	//we want to allocate and copy the string to our own here so we can modify it/clean it
	char* buffer = (char*)malloc(buffMax * sizeof(char));//hold dynamic string, returns null if unable to allocate mem
	char output[buffMax];//to hold our formatted string
	int currentPass;//to know what pass we are on in the loop

	if(buffer != NULL){//we were able to allocate the memory
		strcpy(buffer, source);//copy the string to our buffer
	}else{ return 0; }//tell everybody there was an error allocating memory

	for(currentPass = 1; currentPass <= buffMax/LCDSize; currentPass++){//to know what 16 char section we are at in str
		sprintf(output, "%.*s\n", LCDSize-1, &(buffer[(LCDSize*currentPass)-LCDSize]));//print LCDSize(16) characters from the start of the current pass val
		LCDClear();
		LCDWriteString(output);
		_delay_ms(1000);

		//check if we have remainder/leftovers due to floating point imprecision when comparing so we dont need math library
		if( (currentPass == buffMax/LCDSize) && ((LCDSize*(int)currentPass) < buffMax)){
			sprintf(output, "%.*s\n", LCDSize, &(buffer[(LCDSize*(int)currentPass)]));
			LCDClear();
			LCDWriteString(output);
		}
	}

	free(buffer);
}
void main(){
	InitLCD(LS_BLINK|LS_ULINE);//initialize lcd module
	char testInput[] = "AT+ $      more testttttttttttingggggg asfdasfdsadfsafd";
	bigStringLCDPrint(testInput, 50);

}

 

Last Edited: Tue. Oct 25, 2016 - 03:51 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I didn't know that C had dynamic arrays. :puzzled:
char output[buffMax]; causes my PC compiler to complain, but Atmel Studio seems happy.


Side issue : You don't have a return value when bigStringLCDPrint(,) exits normally.

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

I changed the code up a bit to test the function's return value as you suggested. I'm getting a printout of 'allocated mem' after a few seconds.

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

#ifndef F_CPU
#define F_CPU 1000000UL //so our code knows the chip clock is 1mhz for delay()
#endif
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 8UL))) - 1)
//table 52 page 132 of the atmega8 shows math for calculating prescale
//double speed mode(u2x=1) is multiply by 8
//asynchronous normal mode(u2x=0) is multiply by 16
#include <util/delay.h>

#include "lcd/lib/lcd.c"
#include "lcd/lib/lcd.h"

/*this function chops up large strings and prints them to the 16x2 LCD with a short delay
* this is needed because output from other devices to our mega can be larger than 16 characters
* int buffMax; maximum amount of preallocated character places 8 bits per character, size is 400 bits
* char source; an array of characters of any length to be printed to LCD
* requires: stdlib.h stdio.h string.h avr/io.h util/delay.h lcd/lib/lcd.c lcd/lib/lcd.h
* returns 1 for success, 0 for failure
*/
int bigStringLCDPrint(char source[], int buffMax){
	int LCDSize = 16;//if using a bigger LCD you can adjust here
	//we want to allocate and copy the string to our own here so we can modify it/clean it
	char* buffer = (char*)malloc(buffMax * sizeof(char));//hold dynamic string, returns null if unable to allocate mem
	char* output = (char*)malloc(buffMax * sizeof(char));//to hold our formatted string
	int currentPass;//to know what pass we are on in the loop
	
	if(buffer != NULL){//we were able to allocate the memory
		strcpy(buffer, source);//copy the string to our buffer
	}else{ return 0; }//tell everybody there was an error allocating memory
	
	if(output == NULL){//we were not able to allocate the memory
		return 0;
	}
	
	for(currentPass = 1; currentPass <= buffMax/LCDSize; currentPass++){//to know what 16 char section we are at in str
		sprintf(output, "%.*s\n", LCDSize-1, &(buffer[(LCDSize*currentPass)-LCDSize]));//print LCDSize(16) characters from the start of the current pass val	
		LCDClear();
		LCDWriteString(output);
		_delay_ms(1000);

		//check if we have remainder/leftovers due to floating point imprecision when comparing so we dont need math library
		if( (currentPass == buffMax/LCDSize) && ((LCDSize*(int)currentPass) < buffMax)){
			sprintf(output, "%.*s\n", LCDSize, &(buffer[(LCDSize*(int)currentPass)]));
			LCDClear();
			LCDWriteString(output);
		}
	}
	
	free(buffer);
	free(output);
	return 1;
}
void main(){
	InitLCD(LS_BLINK|LS_ULINE);//initialize lcd module
	char testInput[] = "AT+ $      more testttttttttttingggggg asfdasfdsadfsafd";
	int wasSuccess;//test our func for success
	wasSuccess = bigStringLCDPrint(testInput, 50);
	if(wasSuccess == 1){
			LCDClear();
			LCDWriteString("allocated mem");
	}
	
}

 

Last Edited: Tue. Oct 25, 2016 - 05:34 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I suspect that you just want to produce a "Doctor's Waiting Room" display. e.g. a long message slowly shifts from right to left. Pauses for a few seconds with the display showing the last N letters.
.
You seem to want to do the same but in jerky 16 letter blocks. I do not think that humans will enjoy reading your messages . Mind you, your example does not look very "interesting".
.
Anyway, take a pencil and paper. Draw the flowchart for both 1-char shift and 16-char jerk.
.
There is no need for any sprintf(), malloc(), free(), ...
You can even use the LCD controller's cursor shift commands.
.
David.

Last Edited: Tue. Oct 25, 2016 - 06:57 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I didnt write this to be 'interesting', 'pretty', or 'intriguing'. I wrote this for my own personal use for debugging and I'm learning along the way.

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

Yes,   it is a good way to "learn".

 

I was just suggesting that a pencil and paper can be very effective.

I could have just posted a 4-line function but this would deny you the "learning" process.

 

David.

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

mikech wrote:
I didn't know that C had dynamic arrays. :puzzled:

AS7 builds as "-std=gnu99" as standard:

 

https://gcc.gnu.org/onlinedocs/g...

 

As to the "issue" here I don't think '*' in format strings is supported in libc. You can see the code here:

 

http://svn.savannah.nongnu.org/v...

 

and the manual is here:

 

http://www.nongnu.org/avr-libc/u...

 

In neither of those do I see anything suggesting run time support of variable field widths.

 

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

That was exactly the issue, but that sucks :(

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

It's a micro, not a data processor. However if you really want that kind of stuff you should be able to find the source for printf() in a "large CPU"(*) and port that into your AVR code. But, to be honest, it might be easier just to do some string splicing with routines in <string.h> than to go to that level (especially in an 8K micro).

 

(*) for example this:

 

https://github.com/lattera/glibc...

 

which is vfprintf() (core of printf) for desktop GNU LibC - but see how that compares to the AVR cut down version:

 

http://svn.savannah.nongnu.org/v...

 

I could well imagine that the large CPU vfprintf() could consume the entire 8K or more before you even get started.

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

I understand! I'll work around it.