snprintf functie.

© Harry Broeders.

Deze pagina is bestemd voor studenten van de Haagse Hogeschool - Academie voor Technology, Innovation & Society Delft.

De functie snprintf kan gebruikt om data geformateerd naar een C-string te sturen. Het prototype van deze functie is gegeven in <stdio.h>:

int snprintf (char* s, size_t n, const char* fmt, ...);

Deze functie formateert de na fmt meegegeven parameters op de manier zoals in de format string fmt is opgegeven. Het resultaat wordt weggeschreven op de plaats die door de char* s wordt aangewezen en er worden maximaal n karakters weggeschreven (het laatste weggeschreven karakter is altijd het '\0' karakter). De functie geeft het aantal geformateerde karakters (ook inclusief '\0') terug. Als de retrunwaarde groter is dan n dan was er dus niet voldoende ruimte.

Deze functie kan dus gebruikt worden om data te formateren en naar een char array te sturen op dezelfde manier als de bekende printf functie gebruikt kan worden om data te formateren en naar het beeldscherm te sturen. Bij het programmeren van microcontrollers is er meestal geen "normaal" beeldscherm (stdout) aanwezig. De functie snprintf kan dan gebruikt worden om data te formateren in een C-string. Deze string kan dan vervolgens op een eenvoudig LCD display weergegeven worden of via een seriële verbinding naar een PC of andere mirocontroller verstuurd worden.

De avr-libc library bevat diverse varianten van de printf functie waaronder snprintf. Onderaan pagina 188 en op pagina 189 en 190 in avr-libc-user-manual.pdf  staat beschreven hoe de format string fmt gebruikt kan worden. Microsoft Visual Studio 2010 bevat de functie snprintf niet (en voldoet dus niet aan de C99 standaard!), zie http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010. In Microsoft Visual Studio 2010 kun je wel gebruik maken van de functie sprintf maar bij deze functie ligt altijd het gevaar van een buffer overrun op de loer. Zie: De gevaren van buffer overrun!

Voorbeeld met een int.

Hieronder is een AVR gcc programma gegeven dat een int formateert in een C-string als decimaal getal en rechts uitlijnt op een veldbreedte van 6 karakters met de format string "%6d". De C-string wordt vervolgens op een LCD display weergegeven met behulp van de LCD functies.

#include <stdio.h>
#include "lcd.h"

int main(void) {
    int i = 0;
    char buffer[11];
    lcd_init();
    lcd_cursor(false, false);
    while (1) {
        lcd_home();
        snprintf(buffer, sizeof buffer, "i = %6d", i++);
        lcd_puts(buffer);
    }
    return 0;
}

De operator sizeof bepaalt de grootte van de variabele buffer in bytes. Dit is dus het maximale aantal karakters wat snprintf mag wegschrijven omdat anders voorbij het einde van de variabele buffer wordt geschreven.

Voorbeeld met een uint16_t.

In programma's voor microcontrollers wordt vaak gebruik gemaakt van de integer types die gedefinieerd zijn in C99 (in de header file stdint.h). Zie: http://en.wikibooks.org/wiki/C_Programming/C_Reference/stdint.h en /of pagina 164 e.v. in avr-libc-user-manual.pdf . Door gebruik te maken van deze types kunnen we exact bepalen hoe groot (hoeveel bits) een integer variabele is. Als we de C99 integer types willen printen dan moeten we gebruik maken van de formats die in de C99 include file inttypes.h zijn gedefinieerd. Zie: http://en.wikibooks.org/wiki/C_Programming/C_Reference/inttypes.h en /of pagina 137 e.v. in avr-libc-user-manual.pdf . Voor een variabele van het type uint16_t is het bijbehorende print format gedefinieerd in de macro PRIu16. Deze macro definieert een C-string literal en kan via C-string literal concatination gecombineerd worden met de rest van de format string.

Hieronder is een AVR gcc programma gegeven dat een uint16_t formateert in een C-string als decimaal getal en rechts uitlijnt op een veldbreedte van 5 karakters met de format string "%5"PRIu16. De C-string wordt vervolgens op een LCD display weergegeven met behulp van de LCD functies.

#include <stdio.h>
#include <inttypes.h>
#include "lcd.h"

int main(void) {
    int i = 0;
    char buffer[11];
    lcd_init();
    lcd_cursor(false, false);
    while (1) {
        lcd_home();
        snprintf(buffer, sizeof buffer, "i = %5"PRIu16, i++);
        lcd_puts(buffer);
    }
    return 0;
}

Voorbeeld met een double.

Hieronder is een AVR gcc programma gegeven dat een double formateert in een C-string als floatingpoint getal en rechts uitlijnt op een veldbreedte van 8 karakters met 6 cijfers achter de decimalpoint met de format string "%8.6lf". De C-string wordt vervolgens op een LCD display weergegeven met behulp van de LCD functies.

#include <stdio.h>
#include <math.h>
#include "lcd.h"

int main(void) {
    char buffer[14];
    lcd_init();
    lcd_cursor(false, false);
    snprintf(buffer, sizeof buffer, "pi = %8.6lf", M_PI);
    lcd_puts(buffer);
    while (1);
    return 0;
}

De constante M_PI is gedefinieerd in <math.h>. Zie pagina 155 in avr-libc-user-manual.pdf .

De uitvoer van dit programma is:

pi =       ?   
               

Om doubles (en floats) te kunnen gebruiken moet je het volgende doen:

De uitvoer van het programma is nu wel correct:

pi = 3.141593   
                

Gebruik van het griekse karakter pi.

De LCD wordt aangestuurd met een een Hitachi HD44780 controller. Op pagina 17 van dit document kun je de karakterpatronen vinden. Je ziet dat de griekse letter pi kan worden weergegeven met karaktercode 0xF7. In het volgende programma maken we hier gebruik van:

#include <stdio.h>
#include <math.h>
#include "lcd.h"

int main(void) {
    char buffer[13];
    lcd_init();
    lcd_cursor(false, false);
    snprintf(buffer, sizeof buffer, "%c = %8.6lf", 0xF7, M_PI);
    lcd_puts(buffer);
    while (1);
    return 0;
}

De uitvoer van dit programma is:

Helaas geeft de LCD simulatie van de AVE AVR Plugins het teken pi niet weer :-(