Using printf with msp432e401y microcontroller - Page 1
Forum section: electronics / general

author: Thomas83
author_rank: Contributor
timestamp: May 23, 2022, 09:28:41 am
content: Hello everyone!I just started working with the MSP432E401Y launchpad. My goal for is to send some data to a terminal via UART0.I am having trouble with the PRINTF Function, any data do not send to the terminal. It would be really difficult to implement what I need without these printf function.Any help or an example would be greatly appreciated!

author: nctnico
author_rank: Super Contributor
timestamp: May 23, 2022, 09:34:35 am
content: You'll likely need to write your own putchar() and puts() functions to send data to the serial port.

author: Bassman59
author_rank: Super Contributor
timestamp: May 23, 2022, 03:05:59 pm
content: Quote from: Thomas83 on May 23, 2022, 09:28:41 amAll of the examples TI provides with CCS for this part use a function UARTprintf() which is defined/declared in the files uartstdio.c/.h.

author: SiliconWizard
author_rank: Super Contributor
timestamp: May 23, 2022, 04:58:02 pm
content: Quote from: nctnico on May 23, 2022, 09:34:35 amYes. I suspect TI already has working examples for this.Another approach, more flexible and not requiring to know how to do the above is to implement your own function, using snprintf() instead for the formatting, an intermediate buffer, and then sending the buffer to whatever peripheral.

author: nctnico
author_rank: Super Contributor
timestamp: May 23, 2022, 05:19:10 pm
content: Quote from: SiliconWizard on May 23, 2022, 04:58:02 pmThat snprint method is messy. You'll need to declare a buffer and then transmit the data. Having the regular printf() working to write to a console output (in this case a serial port) makes code much cleaner and portable.

author: SiliconWizard
author_rank: Super Contributor
timestamp: May 23, 2022, 05:20:54 pm
content: It's absolutely not messy and actually allows to define a fixed-size buffer instead of relying on printf(), which very often does dynamic allocation behind your back.

author: hans
author_rank: Super Contributor
timestamp: May 23, 2022, 05:46:02 pm
content: It depends on the stdlib implementation of printf how that's implemented. If you're lucky it's doing an efficient non-buffered stream to putchar. If you're unlucky it drags in a complete stdlib with stdout, FILE* stream implementations and function pointers etcetera.I prefer to use snprintf, at the cost of some extra buffer management. But atleast I can easily redirect serial data if needed. Portability of such a thing only requires a UartTxBytes(char*, size_t) function to be available for calling.

author: DavidAlfa
author_rank: Super Contributor
timestamp: May 23, 2022, 05:48:36 pm
content: Search for "printf redirection" to find more about this procedure.At least in SMT32 it's very easy, _write is usually declared as weak, meaning you can re-declare your own version.Declare this function somewhere, ex. in main.c and expose it in main.h:Code:int _write(int fd, char* ptr, int len) {while(*ptr){UART_Send(*ptr++);}return len;}Then all std library functions using stdout will ultimately end there: printf, puts, putchar... and its variations.

author: nctnico
author_rank: Super Contributor
timestamp: May 23, 2022, 06:20:36 pm
content: Quote from: SiliconWizard on May 23, 2022, 05:20:54 pmNo. Printf typically uses a statically allocated buffer on embedded devices. Your own implementation could ofcourse use the stack as well.printf("hello world")Is definitely much cleaner compared to:char my_buf;int len;len = snprintf(my_buf, sizeof(my_buf), "hello world");send_to_a_uart(my_buf, len);Why write a whole bunch of code to do what printf actually does under the hood? On top of that, it is also a lot of code replication which is bad.Edit: if you need redirection to a different peripheral then either implement fprintf or do the multiplexing at the putchar() / puts() level. Most text based stuff in my embedded firmware deals with handling commands. The device where the command originates from, also receives the text output. Default the text output goes to the console port.

author: Bassman59
author_rank: Super Contributor
timestamp: May 23, 2022, 06:34:09 pm
content: Quote from: SiliconWizard on May 23, 2022, 04:58:02 pm... which I noted in the post directly above yours.Just sayin'.

author: DiTBho
author_rank: Super Contributor
timestamp: May 23, 2022, 07:22:45 pm
content: char_t -> console_out_char()p_void_t -> console_out_pointer_as_hex()p_uint8_t -> console_out_p_uint8_as_hex()string_t -> console_out_string()boolean_t -> console_out_boolean()uint8_t -> console_out_uint8()uint8_t -> console_out_uint8_as_hex()sint8_t -> console_out_sint8()uint16_t -> console_out_uint16()sint16_t -> console_out_sint16()sint16_t -> console_out_sint16_as_hex()uint32_t -> console_out_uint32()uint32_t -> console_out_uint32_as_hex()sint32_t -> console_out_sint32()'\n' -> console_out_NL()one can always implement simple and dedicated things

author: Thomas83
author_rank: Contributor
timestamp: May 24, 2022, 09:43:41 am
content: as you advised me, I used the UARTprintf() function and it works.Thank you everyone,

author: rsjsouza
author_rank: Super Contributor
timestamp: May 24, 2022, 10:48:57 am
content: Quote from: DavidAlfa on May 23, 2022, 05:48:36 pmIndeed. Check the page below (near the end) for redirecting printf()https://software-dl.ti.com/ccs/esd/documents/sdto_cgt_tips_for_using_printf.html

author: Thomas83
author_rank: Contributor
timestamp: July 14, 2022, 12:25:25 pm
content: Hi,How can I use add puts and putc to work with printf function (msp432e401y).I see that for msp432p:https://github.com/bluehash/MSP432Launchpad/blob/master/MSP432-Launchpad-Printf-Support/printfOverride.cThanks for your help.

author: rstofer
author_rank: Super Contributor
timestamp: July 14, 2022, 12:45:51 pm
content: printf() may not use the heap but the string functions, called by printf(), just might. If there is any reference to _sbrk() in the link output, a heap is probably being used.Sometimes projects include "syscalls.c" where heap management and file IO is handled. _sbrk() is often defined in this file.I know absolutely nothing about the MSP432 toolchain and libraries.

author: tellurium
author_rank: Frequent Contributor
timestamp: July 14, 2022, 05:48:44 pm
content: It all depends on the C library.GNU ARM toolchain (https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain) comes with a C library called newlib,https://sourceware.org/newlib/- developed by RedHat for embedded systems.Newlib uses that _read/_write/.... "syscalls" mechanism. Higher level C functions like printf() just call those low level "syscalls". printf() eventually calls _write, fopen() eventually calls _open(), malloc() eventually calls _sbrk(), and so on. Thus, by modifying _write(), you can redirect printf() to whatever. That mechanism is called "IO retargeting".STM32 Cube uses ARM GCC with newlib, that's why Cube projects typically include that syscalls.c - that's because of newlib C library.Other toolchains, like TI's CCS, Keil's CC, might use a different C library with a bit different retargeting mechanism. Search for "retargeting" in a particular doc should help.This article is a good one for newlib:https://embeddedartistry.com/blog/2019/04/17/exploring-startup-implementations-newlib-arm/

author: cv007
author_rank: Frequent Contributor
timestamp: July 15, 2022, 11:11:18 am
content: QuoteQuoteIf Ti has their own implementation of printf (UARTprintf), then they will also have their own implementation of UARTputChar or UARTwrite for single char or strings (this is what UARTprintf would be using). I doubt you want to start mixing in functions from a standard library such as newlib when you are already using what TI is providing.Go to the source of UARTprintf, and you will find what functions they are using for single chars and strings.

author: josuah
author_rank: Regular Contributor
timestamp: July 15, 2022, 12:54:10 pm
content: Because I did not need anything complex like printf, I only have a fmtint() that returns a char*, a put(...) that print all print all strings as argument, them macros to make it easier:Code:/* format a signed integer `i64` at the end of `s` of size `sz` using base `b`* and return the first printable byte of `s` */static char *fmtint(char *s, size_t sz, int64_t i64, uint8_t b);/* print each string argument until NULL is encountered */static int put_(char const *s, ...);/* safer and easier wrapper around put_() */#define put(...) put_(__VA_ARGS__, NULL)/* macros to format integers and print them B(bin), D(dec), X(hex) */#define fmtb(n) fmtint((char){0}, 65, n, 2)#define fmtd(n) fmtint((char){0}, 21, n, 10)#define fmtx(n) fmtint((char){0}, 17, n, 16)#define fmtc(c) (char){ c, '\0' }You end-up with something reasonable such as:Code:put("error at address 0x", fmtx(addr), "\r\n");The trick here is that the macros declare a nameless static buffer: (char){0}It saves from declaring on buffer per number to format every time.Not all compilers might support this even though it is standard C99 afaik.But maybe it is better to have something called printf, and behave like printf?Otherwise, anyone else reading my code would have to investigate how put() works (5% of the code of printf though).Yet another example of printf implementation to draw inspiration from is the one from scc, a compiler under ongoing development:http://git.simple-cc.org/scc/file/src/libc/stdio/vfprintf%2Ec%2EhtmlSomething I have seen somewhere else:Code:puts("error at address 0x");putx(addr);puts("\r\n");[EDIT: that last approach is the same as :]Quote from: DiTBho on May 23, 2022, 07:22:45 pm

author: westfw
author_rank: Super Contributor
timestamp: July 17, 2022, 07:41:58 am
content: QuoteIndeed. See alsofunopenand/orfopencookiethese, along with the 'redefine _write()' solutions, do tend to drag in a complete newlib file-oriented library to implement "stdout", which is a lot of code and layers. uart_printf() may do better...(I think that essentially the _write()-style solutions change the bottom levels of stdout at compile-time, while the funopen-style solutions change the same bottom levels at run-time. But they both end up invoking all the middle layers.)QuoteBeware! I think I've seen sprintf/etc also get implemented as part of a FILE* based implementation (or at least drag in a bunch of FILE* code, whether or not it's used.)Making sure you are using newlib-nano and not just newlib may help. For a while, chip vendors weren't being very careful about which version was appropriate.

author: cv007
author_rank: Frequent Contributor
timestamp: July 17, 2022, 04:20:43 pm
content: The snprintf solution is a good compromise, but does require figuring out how much buffer or stack space is enough. If your buffer ends uo smaller than needed at times with snprintf, your output just truncates (I suppose you could keep track of max needed and adjust the buffer size if on the stack). A simple example (one of many possible variations)-https://godbolt.org/z/5doPMra3PLike most anything else, creating a function is the way to go when using snprintf for this purpose (create once, then easy to use anywhere). One problem with newlib's stdio is it is hard to understand what is going on as the source code is filled with defines/aliases/ifdef's/etc. Using snprintf, at least you do not need much understanding to make it work. It will still use a FILE struct in the process (on the stack), which is quite large (~100 bytes for newlib), so the stack dips down some more in addition to a buffer you may have put on the stack (if that matters).Compile size is usually not a big deal unless your mcu is on the low end. The above simple example (no uart setup, so does not function) compiles to about 3.1k on an stm32. The same example below, actually functioning using a uart buffer w/isr, using my own c++ cout style equivalent code in a c++ header (~300 lines), compiles to about 2.7k. The compiled sizes will be similar enough, even when 'printing' extensively, so compiled size is not necessarily a reason to favor one or the other.Code:#include "MyStm32.hpp" //uart inline instance created in thereint main(){u32 v = 0;while(1){uart << FG ORANGE "Hello World " << FG CYAN << Hex0xpad(8) << v++ << endl; //Hello World 0xNNNNNNNN//print( uart, FG ORANGE "Hello World ", FG CYAN, Hex0xpad(8), v++, endl ); //alternate syntax if tired of looking at chicken footprints}}The advantage to doing your own is you have complete control and can see/understand/modify the source code. I tried to figure out newlib stdio at one time, but got tired of going in circles reading the source, plus it seems to me the FILE struct is unreasonably large for the mcu's I use. I started to create a printf replacement, but switched to a c++ cout style instead as it seemed easier to write/read/modify. The advantage is I now have something I can use for any mcu with a c++ compiler, and I still have something that matches a (c++) standard way of formatting.From what I gather searching online (although what I found looks like older code), TI has their own printf (UARTprintf) and is not using newlib to do any of it. Which is why I stated previously if you want putc/puts and are already using UARTprintf, then you just need find what the TI equivalent is for those. Here is an example of TI code (someones clone of TI code, first result from a search)-https://github.com/ussserrr/maglev-ti-rtos/blob/master/uartstdio.c