Friday, December 13, 2013

Redefining printf() and scanf() !

If you are a C programmer then you are very-much familiar with printf() and scanf(). Many a times we use printf() to debug. For embedded guys printf() is like a OASIS when there is no debugging tools available! But most of the compilers for micro-controllers will not have printf() and scanf() builtin!. So many embedded developers like to port printf() and scanf(). Some will succeed, some will struggle a lot and some will fail. Here I will try to explain how we can re-define printf() and scanf().

printf() and scanf() are just functional calls like other functional calls that we define and/or use in our programs. Only difference between these 2 functions and other functions we use/define is, NUMBER OF ARGUMENTS passed to the function. For printf() and scanf() it is unknown. In C, we have very nice concept (/module we can say) which is very much less used (reason could be because it eats up stack!) in C programming. It is VA-ARG. This handles the variable number of arguments passed to the function. All most all the compilers out there (for AVR or ARM or PIC) will have stdarg.h header file in library, which will have the macros va_start, va_arg, va_end, va_copy, and va_list data type. All we need to do while defining our own version of printf() and scanf() are, use ellipsis ("...") as one of the arguments, and use va_start, va_arg and va_end macros when we need it.

This is how I re-defined my version of printf() and scanf() - click here for code:

  • I have used print() for printf() and scan() for scanf().
  • I had usart/uart driver and hookups for transmit and receive data using serial communication (you can check my previous post or code).
  • I have defined only for reading/writing integers, hex numbers, characters and strings! (this can be modified to serve your purpose!).
  • Max range of Integers that my functions can handle is -2147483648 to 2147483647! and in Hex is 0x00 to 0xFFFFFFFF. (Code can be modified for 64 bit, different range of integers and for long ints - %ld etc.)
  • In my code, I can read maximum of 200 bytes! because I have allocated only 200 bytes as buffer length. So when I want to read any string I should make sure that I have to allocate the memory less than 200 bytes! When multiple data are read I have to make sure that it will not exceed more than 200 bytes or I can read multiple data with more than one scan() functional call! (or you can increase the buffer length). And I have used ENTER KEY as the end of data inputs (you can modify in such a way that '!' or any special character as the indicator for end of data inputs).
  • When I worked with AVR and ARM controllers I have observed that, ENTER KEY from keyboard is considered as CARRIAGE RETURN (ASCII value 0x0D). Where as, in GCC compilers ENTER KEY  is considered as just a NEW LINE (ASCII value 0x0A) (You can see usage of both in my code!).
  • I have used putty to see the things on my laptop screen and connected to Sanguino board with USART/UART. When I used '\n' in print() I thought courser will point to the beginning of new line, instead courser in new line was placed where previously printed line was ended! So when I used '\r' after '\n' courser was pointing to the beginning of new line.
  • While printing single character with "%c", in va_arg I have given type as int not char. Char is not allowed in va_arg. Check the print() definition in code.
  • main() (commented part of the code at the end) in printf_code.c and scanf_code.c files is to test my function definitions using MinGW GCC compiler. When I used printf_code.c and scanf_code.c files for my Sanguino board, I have adjusted with data types and other things for AVR compiler.