NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » sprintf

May 16, 2009
by mfspunk
mfspunk's Avatar

Hey everybody,

i want to write a formatted string to the lcd using sprintf. by using lcd_write_string() i usually just get random characters all over the screen.

char ca_buffer[20];
sprintf(ca_buffer, "%02d", 5); //some number formatted to 2 digits

lcd_line_two();
lcd_write_string(ca_buffer);

So i thought, maybe its because of program space vs stack, lcd_write_string uses pgm_readbyte. So I switched over to manually passing in the bytes, with lcd_write_data.

lcd_line_two();
int i = 0;
for(; i < 20; i++)
    lcd_write_data(ca_timeBuffer[i]);

a little bit better, no corruption, but line two still doesnt show up right.. black boxes and slashes..

i put this in the while loop to see it update, and it repeated line one on line two:

while(1) 
{   
    lcd_line_two();
    lcd_set_type_data();
    for(i = 0; i < 20; i++)
        lcd_write_byte(ca_timeBuffer[i]);
}

any ideas? i saw the example of the real time clock using the streams.. any way to stay C-style with this?

May 17, 2009
by MacGyver
MacGyver's Avatar

How is a file stream not C-style? I slightly disagree there, it could be implemented as follows: FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE); fprintf_P(&lcd_stream, PSTR("%02d"), number);

Regarding your code, One problem which i think most likely to be biting you is that you are not checking the buffer for the null bite terminating the string, which means in addition to the single '5' character you are printing you are printing a null byte and 18 random bytes from memory.

Change your for loop as follows: for(i = 0; i < 20 && ca_timeBuffer[i]; i++) lcd_write_byte(ca_timeBuffer[i]);

Or you going take the pointer approach and go: while(ca_timeBuffer) lcd_write_byte(ca_timeBuffer++);

Hope this helps, not sure if that is exact problem but you would be definitely printing 18 random bytes from the code you have above.

May 21, 2009
by wayward
wayward's Avatar

A word of warning here. If you're thinking about using fdevopen(), which sounds like a good idea if you are already pulling in sprintf() and other mammoths from stdlib, it may fail. avr-gcc that I have (4.3.2) refuses to allocate any memory either using malloc() or calloc(), and fdevopen() relies on the latter. I just hit this bug(?)/feature(?)/misconfiguration(?) today, alas, I'll have to forget about puts_P(PSTR("Hello World")) for now.

More specifically and relating to your problem, mfspunk, lcd_read_string() actually expects a string in program space (read its implementation in lcd.c). You are passing it a pointer to a location in the data segment, which it treats as a location in the program memory, and prints out whatever happens to be there (not your string, anyway). AVR is a Harvard architecture (code/data are in separate physical spaces) so many string functions in avr-libc are duplicated to provide easy access to strings in either. As for our lcd_write_string(), I would prefer to rename it to lcd_write_string_P() which is the standard in avr-libc, and have a different lcd_write_string() for the data segment, like so:

// ### lcd.h ###
// ...
void lcd_write_string(const char *x);
void lcd_write_string_P(PGM_P x);
//...

// ### lcd.c ###
// ...
void lcd_write_string(const char *x) {
  // assumes x is in data memory
  while(*x != 0x00) {
    lcd_putc(*x);
    x++;
  }
}

void lcd_write_string_P(PGM_P x) {
  // assumes x is in program memory
  while(pgm_read_byte(x) != 0x00)
    lcd_putc(pgm_read_byte(x++));
}
// ...

Use lcd_write_string() with strings you want to create or mutate during the program run, and lcd_write_string_P() with strings in program ROM (i.e. declared as PROGMEM or with the PSTR() macro). This, in conjuction with MacGyver's observation above, should hopefully straighten out your issues.

In any case, it's wise to read "Data in Program Space" section of avr-libc just to be sure you know exactly what you are doing.

Zoran

May 22, 2009
by wayward
wayward's Avatar

Ooops,

I copied the above code from my own heavily modified lcd.c. The original NerdKits' one doesn't have a lcd_putc(char) function; it should be lcd_write_data(char) instead.

Post a Reply

Please log in to post a reply.

Did you know that Morse code is a compact way to transmit human-readable text over binary channels? Learn more...