Hi,
First of all, I'm programming the T89C51CC01 Microcontroller using C51 with Keil µVision4. My head teacher wrote the following header file for it (source file also included) -- it's German:
//************************************************************************** //Ferdinand von Steinbeis-Schule Reutlingen // //C - Funktionen - für LCD - Anzeige 04.10.2007 //(angepasst für Atmel Controller) (ae) //************************************************************************** #ifndef LCD #define LCD // Übersicht der Funktionen Header-Datei lcd_zeitae.h void init_lcd(void); // Initialisierung für Atmel FVS Controller void store(void); //Daten übernehmen void clear(void); //Löscht LCD Bildschirm void lcdsend(char* ); //Sendet Zeichenfolge an LCD Display (akt.Cursorpos) void zeichen(char z1); //Sendet ASCII Zeichen an LCD Display void init(void); //Initialisiert void display(void); //Schaltet auf entsprechenden Anzeigemodus um. //Punktmatrix 5x7 1-zeilig mode = 00H //(Nur dieser Mode verfügbar!) // 2-zeilig mode = 08H //Punktmatrix 5x10 1-zeilig mode = 0CH // 2-zeilig mode = 04H void rshift(void); // Anzeigefenster des Displays um 1 Spalte rechts void lshift(void); // Anzeigefenster des Displays um 1 Spalte links void currshift(void); // Cursor um 1 Spalte rechts void curlshift(void); // Cursor um 1 Spalte links void dis_off(void); // Display aus void dis_on1(void); // Display on void dis_on2(void); // Display on mit Cursor void dis_on3(void); // Display on mit Blinkendem Cursor void pos(char posi); // Positioniert Cursor im Speicher des LCD Displays // an n-te Stelle Zeile 1 (00h-0Fh) Zeile 2 (40h-4Fh) void home(void); // Setzt Cursor an Display Anfang //-------------------------------------------------------------------------------- //******************************************************************************** //Zeit- Funktionen void sec(unsigned char s); // Wartet n Sekunden void msec(unsigned char ms); // Wartet n Millisekunden //******************************************************************************** void wait(void); void init(void); void display(void); void dis_on1(void); void clear(void); void pos(unsigned char posi); void msec(unsigned char ms); void sec(unsigned char s); void zeichen(unsigned char z1); #endif
This file can makes it easy to write something to the LCD-Display on the microcontroller.
Well, the file (normally) worked without a problem with Keil. Little examples:
// without sprintf #include <t89c51cc01.h> #include "fvs_bib_lcd.h" void main(void) { init_lcd(); // initialize pos(0x00); // cursor-position to the beginning lcdsend("Hello"); // print something to the lcd }
// with sprintf #include <stdio.h> #include <t89c51cc01.h> #include "fvs_bib_lcd.h" void main(void) { char text[17]; // the variable we use to format the text init_lcd(); pos(0x00); lcdsend("Hello"); pos(0x40); // beginning of the second line sprintf(text, "50 = %i", 50); // very trivial, I know lcdsend(text); }
The second example worked for me without a problem. But now the following "error" occured:
Every time I want to format a text, like
sprintf(text, "50 = %i", 50);
and print it on the display, I get:
Hello 50 = 12869
Now my question is, how can this be possible? It happened sometime ago, I don't know what the reason for that is... Can you help me please? Many thanks in advance!
sprintf() assumes that the parameter %i is a 16-bit int on the stack.
But your compiler settings let the compiler only push a single byte on the stack.
12869 decimal is 0x3245. 0x32 is decimal 50 (your parameter). 0x45 is just random noise picked up.
What happens if you write (int)50?
This is a problem that doesn't happen with "normal" compilers, since they are required to always upgrade numbers to the size of int when pushing - but Keil has special options to not upgrade one-byte integers into the two-byte "int" size because 16-bit integers are so much more expensive on an 8-bit 8051 processor.
But not using integer promotion means that variadric functions like sprintf() can fail. And it also means that some expressions will give a different result compared to what you would expect to get - what you would get with a compiler compliant with the C standard.
Well, thanks for this explanation! It confuses me, tho, it worked many times, it showed 50 on the display. Will (short) help maybe? (int) before it doesn't work also, tried that before...
Remember that Keil supports the additional size parameter "b" to specify a one-byte number.
See the manual for printf: http://www.keil.com/support/man/docs/c51/c51_printf.htm
Hmmmm additional size parameter? Do you mean
sprintf(text, "50 = %ib", 50);
or
sprintf(text, "50 = %i", 50b);
I guess it's the first one isn't it?
I mean "%bi", similar to "%li" for long integer.
Didn't you read the manual I linked?
"The optional characters b or B may immediately precede the type character to respectively specify char types for d, i, u, o, x, and X."
Oh! Sorry, yeah now I get it. Many thanks, I'll try that now =)
Yes, this helped! I should read the manuals more... but thanks again =)
Hi again,
now I've got another problem which I think is because of sprintf(). Following code:
#include <stdio.h> #include <fvs_bib_lcd.h> void main() { unsigned char i = 0; unsigned char t[17]; init_lcd(); while (1) { i++; sprintf(t, "I: %bds", i); // s stands for seconds pos(0x00); // first line of the lcd lcdsend(t); msec(90); // wait 90 milliseconds } }
If this reaches over 120, I get something like -121 (which changes as the variable "i" changes). Is this a sprintf()-related thing again or is it the data type char? I tried it with int, same thing :/
I apperiate your answer! =)
Sorry, if it reaches over 127.
Figured it out. It needs to be "plain" int, not unsigned, and sprintf-format needs to be without the "b"-flag. If anyone is interested...
Yes, an 8-bit signed character have the range -128..127. And when you go from 127 to "128", you will set the sign bit making the value look like a negative number.
But an 8-bit unsigned character (printed with u instead of d) has range 0..255 and is way cheaper for an 8-bit processor as long as 0..255 is enough numeric range.
So in your case, your error was that you used an unsigned char (range 0..255) but called sprintf() and supplied the data type character "d" which represents signed data instead of "u" for unsigned data.
So try:
sprintf(t, "I: %bus", i); // s stands for seconds
Oh my gosh... I had to know that... but I'll need more than 255 in my program, so int is just fine. But thanks again, I could use that info for other things =)
Remember that when removing the "b" size character and using "int" or "unsigned" (short for "unsigned int"), you should still consider if you want a signed or unsigned variable.
If you ask for input from a user, a signed variable may need a test "if (var >= 0 && var <= 10) then_ok();"
While an unsigned variable might only need "if (var <= 10)" since it - by definition - can't be below zero. If you subtract 1 when it was already zero, the end result would be 65535 given a standard 16-bit number design.