Friday, June 8, 2012

printf() for the MSP430

"The output function printf translates internal values to characters."
This simple definition comes from the book "C Programming Language" by Kernighan and Ritchie. I highly recommend this book to anyone who is interested in learning more about C, it is a must have for any embedded programmer.

The printf() function will allow us to display any value in your code so that it is human readable. What does this mean? Let's say you would like to tell the computer what temperature your MSP430 has measured; a typical output might be "Temperature: 71°F". We know how to read the internal temperature from a previous post, but how do we get our MSP430 to output the above string over UART? We use the printf() function.
printf("Temperature: %u°F\r\n", tempValue);
I will not be explaining how printf() actually works in this post. If you would like more information on how to use this function please see the above mentioned book, or this link. Due to the fact that the MSP430 has limited resources (e.g. memory), a compact version of the standard printf() function must be used.

Since the printf() included in Code Composed Studio is very large and will not fit on many of the value line MSP430s, we must add our own printf() function to our projects. oPossum on the 43oh.com forums has shared his printf() with the community and since reinventing the wheel is rarely a good idea, we will be using oPossum's code.
"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."
This is how oPossum describes his function. While this function does not support all of the standard printf() features, it is more than sufficient for use on an MSP430. Using this function, we can format 7 separate data types: character, string, integer, unsigned integer, long, unsigned long, and hexadecimal (16-bit) formatting.

I want to thank oPossum for sharing his code with the community. The code is very well written and works great! Thanks again!

Code

Please scroll down for the rest of the post. One of these days I will figure out how to limit the height of my embedded code.


Customizing the Output

Depending on your project, you will want to have printf() output differently. For this example, printf() will send the formatted string out over the UART to a computer. For a different project, you might want to output the string to an LCD, or through USB. The code provided from oPossum allows you to define two functions, puts() and putc() which determines how printf() will output the formatted string. Without defining these two functions, the printf() will actually not do a thing. printf() uses puts(char *) to send out a string value, and uses putc(unsigned) to send out a character.

In the code above, my puts(char *) and putc(unsigned) functions sends the string and character out over UART using another of my functions, sendByte(unsigned char). The code in these functions is fairly straight forward, and will not be discussed here. If you would like to another output for printf(), you would create your own puts(char *) and putc(unsigned) functions as I have done in the code above.

Testing the printf() Function

I will be using a very similar setup as oPossum for testing the printf() function. The code above will send a test sequence to the computer when the MSP430 receives the character 't' over UART. I recommend using Realterm for interfacing with the MSP430 over UART. To connect, select the correct COM port and set the baud rate to 9600. Once connected, send the character 't' by clicking on the blank window and typing 't'. You should see the following response from the MSP430.

Test sequence from the MSP430
Conclusion

Use this code in your projects! It really is a great piece of code. To use this printf() function to your own CCS projects, just add printf.c to your project and create your own puts(char *) and putc(unsigned) in your main c file as I have done in this example.

In the next post, I will expand on the previous post by creating a real-time clock using the MSP430. Stay tuned!

26 comments:

  1. Well written post on the printf for the msp430.

    ReplyDelete
  2. Replies
    1. Thanks for the post. Have just tested it with CCS. As new to msp430 I need help with this error:

      error: symbol "putc" redefined: first defined in "./printf-tst.obj"; redefined
      in "D:\Program Files\Texas
      Instruments\ccsv4\tools\compiler\msp430\lib\rts430.lib"
      error: symbol "puts" redefined: first defined in "./printf-tst.obj"; redefined
      in "D:\Program Files\Texas
      Instruments\ccsv4\tools\compiler\msp430\lib\rts430.lib"



      "../lnk_msp430g2553.cmd", line 56: error: run placement fails for object
      ".bss", size 0x22d (page 0). Available ranges:

      Delete
  3. Awesome! One of the biggest pains moving from PIC to TI was the lack of readily available functions. One of them being the printf() function.

    ReplyDelete
  4. TI has a Wiki for using printf() with MSP430 in CCS:

    http://processors.wiki.ti.com/index.php/Printf_support_in_compiler

    I'd like to know how the method you've given here is better, because in my CCS v5.2, printf() defaults to minimal for use with the MSP430 devices and the limitations appear to be the same, only it's a heck of alot easier than what you've shared here - albeit impressive.

    ReplyDelete
    Replies
    1. The Wiki mentioned is not just for value line MSP430's. I think the CCS minimal printf is still too big for the value line devices, even with optimization at best - so maybe this version will work where the "minimal" won't. It's certainly worth a try.

      Delete
  5. Thanks for sharing. My CSS told me I don't have several flags:
    IFG2, UCA0TXIFG, etc.

    Could you tell me which library I should include?

    ReplyDelete
    Replies
    1. should be CCS. I tried to include either msp430.h or msp430g2331.h. Neither works.

      Delete
    2. This code was written for the MSP430G2553. With some modification, you can get this code to work (with a software UART). Also, it is important to note that not all definitions are the same between the two chips.

      Delete
  6. Thanks for the great post, although I'm a little confused how to use Realterm to communicate with the MSP430 over UART. I have my MSP430 launchpad hooked up to my Windows XP machine via the mini USB connection. According to the device manager, the Launchpad is connected on COM port 4, so I tried running the code from CCS then running Realterm to send a "t" character on COM 4, but it doesn't do anything. I tried setting a breakpoint in the USCI0RX_ISR function, but it never gets there. Does the launchpad need to be connected to my computer using an interface other than the USB connection, or do I just need to change around the UART jumper pins on the Launchpad? Thanks for any advice, your blog is extremely helpful!!

    ReplyDelete
    Replies
    1. The first thing I would recommend doing is jumping the RX and TX pins on the emulator side of the LaunchPad. This will echo the characters sent in Realterm and will test if all the hardware is working. If that works, try transmitting characters from the LaunchPad to Realterm when you press a button. Once you have verified you can send characters, then attempt to receive them. Good luck!

      Delete
    2. Thanks for the response NJC, I ended up getting it working by removing the TX and RX jumper pins, although I'm a little confused about why that worked.

      Right now, the TX and RX jumper pins are oriented vertically, which, according to the little diagram on the board means that it's using SW UART. I thought this is what I wanted - to use the built in UART through the USB cable. Unfortunately, this didn't work for me when using Realterm to connect to the Virtual COM port that the LaunchPad is UART is connected to. I had to change the jumper pins to the horizontal orientation, which is HW UART. This doesn't make sense, since I thought for HW UART, I'd need to use a separate UART device, such as an FTDI to USB adapter which connects to P1.1 and P1.2 of the LaunchPad. I'm guessing I've probably just got my understanding of HW and SW UART mixed up..

      By the way, do you know any way to use printf to output something to the console in Code Composer Studio? Thanks!

      Delete
    3. The difference between a HW and SW UART is strictly within the MSP430. The external hardware does not care if the MSP430 has a SW or HW UART. A HW UART (such as on the MSP430G2553) is physically built into the MSP430 in the same way an ADC or Timer is. A SW UART is created when there is no HW UART (such as in the MSP430G2331) in order to "manually" allow UART communication via code.

      I believe that the reason you needed to change the jumpers is because there are different hardware revisions of the LaunchPad (mine are the older versions).

      As for using printf to output directly to CCS, I am not sure. I prefer Realterm and have never tried using the console in CCS. I believe it is possible though. Try asking on TIs forums. e2e.ti.com

      Hope that all makes sense!

      Delete
    4. Thanks again for your reply NJC, your answer makes sense. In regards to printing to the console, I found the following message on the 43oh.com forums which explains how to get console debug statements working with the Tiny Printf code from oPossum: http://www.43oh.com/forum/viewtopic.php?f=10&t=2953&p=21551

      Delete
  7. Haven't seen this mentioned yet... You NEED the 32kHz watch crystal soldered to your LaunchPad to get this Demo code working out of the box. Otherwise, you need to modify the UART and Timer code portions such that ACLK (auxilary clock) is set to use the DCO as opposed to the external crystal.

    ReplyDelete
    Replies
    1. David, thanks for pointing that out. I forgot to mention it in the post. The plan was to turn this into an alarm clock demo. Thanks again!

      Delete
  8. Works good, out of the box. Great thanx to Nicholas! Confirmed at MSP430G2553 / CCS 5.3.0 / RealTerm. To clock from internal, set CLK source, calibrated constants, and UART.

    /*
    UCA0CTL1 |= UCSSEL_1; // CLK = ACLK
    UCA0BR0 = 0x03; // 32kHz/9600 = 3.41
    UCA0BR1 = 0x00;
    UCA0MCTL = UCBRS1 + UCBRS0; // Modulation UCBRSx = 3
    */
    UCA0CTL1 |= UCSSEL_2; // SMCLK
    BCSCTL1 = CALBC1_1MHZ; // Load calibrated constants for 1Mhz operation.
    DCOCTL = CALDCO_1MHZ; // ...
    UCA0BR0 = 104; // 9600@1MHz
    UCA0BR1 = 0; // 9600@1MHz
    UCA0MCTL = UCBRS0; // Modulation UCBRSx = 1


    ReplyDelete
    Replies
    1. Glad everything worked as planned! Thanks for the code snippet!

      Delete
    2. great. After I used NJC's code and updated with sea-owl. it works. Thanks!

      MSP430G2553 / CCS 4.2.4

      Delete
  9. Hi Nicholas, has the "printf()" been revised to be used in a MSP430G2553.
    I am unable to use it in the form as available on your blog
    of 08 June 2012.
    thanks.

    ReplyDelete
    Replies
    1. Are you also using the main.c file posted directly after the printf.c file? Are you also using CCS? The code should work fine on the G2553.

      Delete
  10. Don't know why I was brought to this page, I just wanted to comment on your blogs generally, I have read many of them and really like the material and ideas you cover.

    Thanks, and keep it up !

    ReplyDelete
  11. can this code function with the C5000 booster pack LCD? I tried it but didn't seem to work.

    ReplyDelete
  12. In case this helps somebody. With GCC 4.9.1, overriding putc caused the linker to error out with 'multiple definition' error: a conflict between the app and libc (which is strange -- the definition in the app object file *should* take precedence). A workaround was to override putchar instead of putc, and replace putc with putchar in oPossum's printf code.

    /opt/ti/msp430-gcc/bin/../lib/gcc/msp430-elf/4.9.1/../../../../msp430-elf/lib/libc.a(lib_a-putc.o): In function `putc':
    putc.c:(.text.putc+0x0): multiple definition of `putc'
    io.o:/home/.../msphello/gcc/../src/io.c:11: first defined here
    /opt/ti/msp430-gcc/bin/../lib/gcc/msp430-elf/4.9.1/../../../../msp430-elf/bin/ld: Warning: size of symbol `putc' changed from 28 in io.o to 16 in /opt/ti/msp430-gcc/bin/../lib/gcc/msp430-elf/4.9.1/../../../../msp430-elf/lib/libc.a(lib_a-putc.o)
    collect2: error: ld returned 1 exit status

    ReplyDelete
  13. Wow! Thank you so much! This worked like a charm with some minor modifications on a MSP430FR2633.

    ReplyDelete
  14. Those with MSP430FR2433 launchpads, can try this https://github.com/3DgeekStore/MSP430FR2433

    ReplyDelete

Note: Only a member of this blog may post a comment.