oPossum 1,083 Posted October 17, 2011 Share Posted October 17, 2011 This is a tiny printf() function that can be used with the chips that come with the Launchpad. Code size is about 640 bytes with CCS. There are 7 format specifiers: %c - Character %s - String %i - signed Integer (16 bit) %u - Unsigned integer (16 bit) %l - signed Long (32 bit) %n - uNsigned loNg (32 bit) %x - heXadecimal (16 bit) Field width, floating point and other standard printf() features are not supported. printf() code #include "msp430g2231.h" #include "stdarg.h" void putc(unsigned); void puts(char *); static const unsigned long dv[] = { // 4294967296 // 32 bit unsigned max 1000000000, // +0 100000000, // +1 10000000, // +2 1000000, // +3 100000, // +4 // 65535 // 16 bit unsigned max 10000, // +5 1000, // +6 100, // +7 10, // +8 1, // +9 }; static void xtoa(unsigned long x, const unsigned long *dp) { char c; unsigned long d; if(x) { while(x < *dp) ++dp; do { d = *dp++; c = '0'; while(x >= d) ++c, x -= d; putc(c); } while(!(d & 1)); } else putc('0'); } static void puth(unsigned n) { static const char hex[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; putc(hex[n & 15]); } void printf(char *format, ...) { char c; int i; long n; va_list a; va_start(a, format); while(c = *format++) { if(c == '%') { switch(c = *format++) { case 's': // String puts(va_arg(a, char*)); break; case 'c': // Char putc(va_arg(a, char)); break; case 'i': // 16 bit Integer case 'u': // 16 bit Unsigned i = va_arg(a, int); if(c == 'i' && i < 0) i = -i, putc('-'); xtoa((unsigned)i, dv + 5); break; case 'l': // 32 bit Long case 'n': // 32 bit uNsigned loNg n = va_arg(a, long); if(c == 'l' && n < 0) n = -n, putc('-'); xtoa((unsigned long)n, dv); break; case 'x': // 16 bit heXadecimal i = va_arg(a, int); puth(i >> 12); puth(i >> 8); puth(i >> 4); puth(i); break; case 0: return; default: goto bad_fmt; } } else bad_fmt: putc(c); } va_end(a); } test code #include "msp430g2231.h" void serial_setup(unsigned out_mask, unsigned in_mask, unsigned duration); void printf(char *, ...); void main(void) { char *s; char c; int i; unsigned u; long int l; long unsigned n; unsigned x; // Disable watchdog WDTCTL = WDTPW + WDTHOLD; // Use 1 MHz DCO factory calibration DCOCTL = 0; BCSCTL1 = CALBC1_1MHZ; DCOCTL = CALDCO_1MHZ; // Setup the serial port // Serial out: P1.1 (BIT1) // Serial in: P1.2 (BIT2) // Bit rate: 9600 (CPU freq / bit rate) serial_setup(BIT1, BIT2, 1000000 / 9600); printf("%s", "\r\n*** printf() test ***\r\n"); s = "test"; c = 'X'; i = -12345; u = 12345; l = -1234567890; n = 1234567890; x = 0xABCD; printf("String %s\r\n", s); printf("Char %c\r\n", c); printf("Integer %i\r\n", i); printf("Unsigned %u\r\n", u); printf("Long %l\r\n", l); printf("uNsigned loNg %n\r\n", n); printf("heX %x\r\n", x); printf("multiple args %s %c %i %u %l %n %x\r\n", s, c, i, u, l, n, x); printf("\r\n*** Done ***\r\n"); for(;; } bluehash, tripwire, PentiumPC and 25 others 28 Quote Link to post Share on other sites
zeke 693 Posted October 17, 2011 Share Posted October 17, 2011 It almost looks like you were reading The Standard C Library when you wrote that. Very nice! :thumbup: Quote Link to post Share on other sites
gwdeveloper 275 Posted October 17, 2011 Share Posted October 17, 2011 Thanks! So... this can be used in place of itoa an such for printing the serial port or a LCD display? Quote Link to post Share on other sites
bluehash 1,581 Posted October 17, 2011 Share Posted October 17, 2011 Oh nice.. I'm going to use this for my space constrained project. @GW You will have to write an itoa function. Try this: http://www.jb.man.ac.uk/~slowe/cpp/itoa.html credit Quote Link to post Share on other sites
oPossum 1,083 Posted October 18, 2011 Author Share Posted October 18, 2011 It almost looks like you were reading The Standard C Library when you wrote that. Is there similar code in that book? I wrote this myself. Quote Link to post Share on other sites
oPossum 1,083 Posted October 18, 2011 Author Share Posted October 18, 2011 So... this can be used in place of itoa an such for printing the serial port or a LCD display? Yes, you can easily use it with LCD or other character output devices. Just write a putc() function that writes a character to the device. There must also be a puts() function that writes a string to the device, that can be this... void puts(char *s) { while(*s) putc(*s++); } Integers are always base 10 with this tiny printf(). itoa() typically allows the base to be specified. Quote Link to post Share on other sites
nobody 47 Posted October 18, 2011 Share Posted October 18, 2011 In IAR workbench use this way: Go to Project -> Options (Alt F7) -> General options -> Library options -> set Printf formatter to Tiny. This works for printf() and also for sprintf(). ...and that's all, you don't need wrote any code (except of yours version of putchar() to LCD or serial port). bluehash 1 Quote Link to post Share on other sites
gwdeveloper 275 Posted October 18, 2011 Share Posted October 18, 2011 I've had a working itoa and ftoa. When using RobG's lcd backpack, I use his sendDataArray to output to the display or TXString from TI's virtual_com_commands to push to a serial terminal. Previously, on other mcu devices, I would use sprintf() but it took a lot of code space. These new functions make more sense now. Thanks guys. Quote Link to post Share on other sites
ike 53 Posted October 19, 2011 Share Posted October 19, 2011 10x oPossum. I actuality have an idea that combines perfectly with your Tiny printf(). I'll post it in Projects section. Quote Link to post Share on other sites
ajacks504 0 Posted October 19, 2011 Share Posted October 19, 2011 Thanks a million oPossum, code looks tight and very compact. I have a couple possibly irritating questions, I don't really get the va_xxx parts: va_list a; va_start(a, format); va_end(a); What exactly are these doing and where are they defined? I apologize, I'm not using a 430, and don't have launchpad installed. EDIT: Dohhh! They are stdarg.h Name Description compatibility va_start Start iterating arguments with a va_list C89 va_arg Retrieve an argument C89 va_end Free a va_list C89 va_copy Copy contents of one va_list to another C99 Quote Link to post Share on other sites
PatBeirne 1 Posted October 25, 2011 Share Posted October 25, 2011 I agree: thanks oPossum, very useful code. I made a few changes for myself, I'm passing them along const char hexChar[] = "0123456789ABCDEF"; #define hex(m) hexChar[m & 0x0F] static const unsigned int dv[] = { // 4294967296 // 32 bit unsigned max 1000000000, // +0 100000000, // +1 10000000, // +2 1000000, // +3 100000, // +4 // 65535 // 16 bit unsigned max 10000, // +5 1000, // +6 100, // +7 10, // +8 1, // +9 0 }; static const unsigned int hv[] = {0x10000000, 0x1000000, 0x100000, 0x10000, 0x1000, 0x100, 0x10, 1, 0}; /* this only works for base==10 or 16 */ char* strAtoI(char * str, unsigned int value, int base) { unsigned int d; char c; const unsigned int *dp; if (base==16) { *str++ = '0'; *str++ = 'x'; dp = hv; // start at the top of the array } else dp = dv; // select the base 10 array while (value < *dp) // move to the correct decade dp++; do { d = *dp++; c = 0; // count binary while((value >= d) && (d!=0)) ++c, value -= d; *str++ = hex(c); //convert to ASCII } while(d>1); return str; } I merged the hex decode into the AtoI, because it ended up smaller than a bunch of >>12, >>8, >>4 calls, and much smaller if you include the >>28, >>24, >>20 and >>16 for printing longs. I also merged the 0 case into the algorithm. This gets rid of the "if (x)" case; each array has a 0 terminator to help the algorithm. The code above adds a "0x" to hex digits; rip it out if you don't want it. And the code above prints to a buffer; it's easy to change it to putc() calls. Cheers, Pat bluehash 1 Quote Link to post Share on other sites
bluehash 1,581 Posted October 25, 2011 Share Posted October 25, 2011 Thanks Pat and welcome to the Forums. Quote Link to post Share on other sites
parakleta 1 Posted October 26, 2011 Share Posted October 26, 2011 I use the following asm to convert integers into BCD so they can be printed using the hex print function. This example only converts 16 bit integers to values up to 4 digits, but you could increase the input or output by simply chaining rlc and dadd steps. Within this limitation it's much more efficient both in space and time than a table driven solution. The following is in the GCC3 style, but could easily be adapted to whatever else you're using. int input, output; __asm__ ( "clr %0 \n" "setc \n" "rlc %1 \n" ".Lagain%=: \n" "dadd %0, %0 \n" "rlc %1 \n" "jnz .Lagain%= \n" : "=g" (output), "+g" (input) : : "cc" ); agaelema 1 Quote Link to post Share on other sites
timotet 44 Posted December 9, 2011 Share Posted December 9, 2011 hi I was trying to use this and cant get it to compile I am getting these errors: run placement fails for object ".bss", size 0x22c (page 0). Available ranges: RAM size: 0x80 unused: 0x4a max hole: 0x48 printf line 0 1323453815793 518 placement fails for object ".text", size 0xf0e (page 0). Available ranges: FLASH size: 0x7e0 unused: 0x64e max hole: 0x64e printf line 0 1323453815793 517 run placement fails for object ".cio", size 0x120 (page 0). Available ranges: RAM size: 0x80 unused: 0x4a max hole: 0x48 printf line 0 1323453815793 519 I was trying to use the example with a terminal on my PC. I took both programs and put them in the same project then tried to compile, am I doing this wrong? I am using the g2231chip. any help would be useful thanks Quote Link to post Share on other sites
bluehash 1,581 Posted December 9, 2011 Share Posted December 9, 2011 You are running out of space in Flash and RAM. How much did you have left before you added the printf? Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.