printf redirection to UART using newlib

cancel
Showing results for 
Search instead for 
Did you mean: 

printf redirection to UART using newlib

7,122 Views
mcheah
Contributor I

I'm racking my brain to find a way to redirect DEBUGOUT and PRINTF to the UART using newlib on LPCXpresso 7.9.2. I'm using LPCOpen for the lpc_board_ea_devkit_4088 platform from embedded artists Quickstart board.

I'm running code that has .cpp files so I cannot use redlib, so I've switched to newlib, but I for the life of me cannot find a way to override the printf function to be directed to the UART.

I'm running newlib (NoHost).

Examples given say to override the _write function, but they don't seem to work in any meaningful way.  I've altered the retarget.h header in my board library as follows, but I'm not sure it takes any effect.  All the examples seem to be directed towards redlib in particular (which does work), but don't seem to work with newlib.  I'm not entirely convinced that the documentation for the printf stuff is up to date for newlib (referring to this:Using printf() ).  Anyone have suggestions?  Am I missing something basic here?  Seems like something so simple shouldn't be that hard.

#if defined( __NEWLIB__ )

/* Include stdio.h to pull in __REDLIB_INTERFACE_VERSION__ */

#include <stdio.h>

#if defined(DEBUG_ENABLE)

#if defined(DEBUG_SEMIHOSTING)

/* Do nothing, semihosting is enabled by default in LPCXpresso */

#endif /* defined(DEBUG_SEMIHOSTING) */

#endif /* defined(DEBUG_ENABLE) */

#if !defined(DEBUG_SEMIHOSTING)

int _write(int iFileHandle, char *pcBuffer, int iLength)

{

#if defined(DEBUG_ENABLE)

  unsigned int i;

  for (i = 0; i < iLength; i++) {

  Board_UARTPutChar(pcBuffer[i]);

  }

#endif

  return iLength;

}

/* Called by bottom level of scanf routine within RedLib C library to read

   a character. With the default semihosting stub, this would read the character

   from the debugger console window (which acts as stdin). But this version reads

   the character from the LPC1768/RDB1768 UART. */

int _read(void)

{

#if defined(DEBUG_ENABLE)

  char c = Board_UARTGetChar();

  return (int) c;

#else

  return (int) -1;

#endif

}

#endif /* !defined(DEBUG_SEMIHOSTING) */

#endif /* defined ( __NEWLIB__ ) */

0 Kudos
13 Replies

1,294 Views
lpcxpresso_supp
NXP Employee
NXP Employee

It looks like the retarget layer provided in the latest LPCOpen package releases (certainly the 2.19 LPCXpresso4337 one) will now work with both Redlib and Newlib printf() functions without needing you to supply a new retarget layer.

You simply need ensure that you have configured the chip, board and application projects to be built for Newlib, and also that the application project is built for Newlib(Nohost) as perhttps://community.nxp.com/message/630740 .

Certainly if I do the above, then a simple LPCOpen hello world project I have created and configured for Newlib will send printf output out over the UART/vcom port to a terminal program running on my PC.

Regards,

LPCXpresso Support

0 Kudos

1,294 Views
lpcxpresso_supp
NXP Employee
NXP Employee

So other than changing the C library type to Newlib(Nohost) for your application project, have you done anything else?

 

You certainly need to make sure that you have reconfigured the LPCOpen chip and board libraries for Newlib, as well as your main application project.

If you need more assistance then please tell us what your target board and MCU are, plus what version of LPCOpen you are using and which example you are trying to change to use Newlib printfs

Also tell us what version of LPCXpresso IDE you are using and post the .map file generated inside the Debug (or Release) directory of your application project when you build it.

Regards,

LPCXpresso Support

0 Kudos

1,294 Views
matheusmullerbo
Contributor I

I'm using LPCXpresso 4337 board, LPCOpen v2.19 and LPCXpresso IDE v8.22. First just created an empty LPCOpen C Project and retargeted printf using RedLib, everything ok. So I switched to NewLib (nohost) and implemented the _write method but it doesn't work. I tried enabling/disabling the flags  DEBUG_SEMIHOSTING, DEBUG_ENABLE, commenting/uncommenting the line #include "retarget.h" on board.c but nothing worked

I'm sending the map file with the project zip.

0 Kudos

1,294 Views
matheusmullerbo
Contributor I

Hi, I'm trying to retarget printf using NewLib but I'm facing the same problem. I tried to follow the replies but couldn't find an answer... So what I should to do retarget printf? With RedLib It works perfectly, but when I change to NewLib (nohost) it simples ignore the printf call...

0 Kudos

1,294 Views
mcheah
Contributor I

okay.

Including board.h fixes the issue and allows the override of _write() to work.  But I still need retarget_uart.c to reside within my project folder.  If I try to relocate it to the lpc_board_ea_devkit_4088 project, it doesn't override the function.  I tried to make a header file with function prototypes for the _write and _read functions but it doesn't seem to take effect.  How can I get this behavior to be more like a "library" function rather than something living in my individual project?

Basically my understanding of the situation is like this:

By default running with newlib (nohost) libraries includes some pre-built binaries for the newlib stdlib which includes some default functions for the syscalls.  Either the default behavior is to direct these syscalls to the debug console or somewhere there is a set of functions that overrides this behavior.  By making my own source file I am overriding these functions to use the UART buffer instead of the debug console.  My confusion is basically what determines the precedence of these overridden behavior?  I would expect the standard "retarget.h" #define descriptions of the _write function would work as well as the compiled c functions, but it does not.  I would also expect it to make a difference whether the .c function is in the board library or the main project.  I think part of my confusion is that I don't really know when the "newlib" includes actually occur and what the linker is actually doing.

0 Kudos

1,294 Views
mcheah
Contributor I

DEBUG_ENABLE is defined in lpc_board_ea_devkit_4088\board.h.  It definitely does something when selecting semihost and not trying to override the function, so I don't think that's the specific issue.  I can get it to print to the debug console if I do semi hosting, but to write data to the application using scanf, I still have to use a UART console, which is super annoying to have printf go one place, but scanf to come from another place.

The root project is selected when I try to use the quick settings menu.  This behavior seems to be true for every project I select in the workspace.  I briefly tried to figure it out, as I was able to select the library headers on a brand new library immediately after creating it, but after setting the library headers one time, the application permanently grays out the options for that project and every other project.  After that I gave up on using the quick settings menu to change library headers.

0 Kudos

1,294 Views
avt
Contributor III

But board.h is not #include'd into retarget_itm.c which means that your _write function does not use the Board_UARTPutChar function as it is #ifdef'd out. Have you tried it?

0 Kudos

1,294 Views
avt
Contributor III

On further investigation, retarget_item.c does not include board.h. If it did:

1. DEBUG_ENABLE would be defined

2. The 'implicit declaration' warning on Board_UARTPutChar would disappear.

Conclusion: include board.h in retarget_item.c

0 Kudos

1,294 Views
mcheah
Contributor I

Please see attached project

0 Kudos

1,294 Views
avt
Contributor III

From a very quick look at your project, you do not have DEBUG_ENABLE defined and so _write() does nothing.

P.S. Quick Settings (and all it sub-menus) are all enabled for me. What do you have selected in your workspace when you try to do this?

0 Kudos

1,294 Views
mcheah
Contributor I

Okay.

I tried this example too and it still does not work.  Did the following:  Created the attached .c file for overriding the _write and _read functions.  Copied it into the source folder of my project (not the board library).  Commented out #include retarget.h in board.c.  Uncommented #define DEBUG_SEMIHOSTING.

Switched library between newlib (semihosting), newlib (nohost), newlib (none) and get the following results

newlib (semihosting) - complains there are multiple definition of _read and _write, line 290 external location E:\jenkins-slave\workspace\LibrariesFresco\Lewlib_hostings_newlib_stub_semihost\srt\syscalls.c and does not build

newlib (nohost) - compiles and runs, but any calls to printf disappear into the air and are not put out on the UART or the debug window or the LPCXpresso Console

newlib (none) - does not compile with an unspecified error.

It seems like the linker is still overriding my _write function with something else.  It should be noted that for whatever reason I cannot change my library headers from the Quick Settings toolbar.  The options are all grayed out. I'm manually changing the project settings for MCU C Compiler ->Miscellaneous, MCU Assembler -> Architecture & Headers, and MCU Linker -> Managed Linker Script.

retarget_uart.c

//*****************************************************************************

// retarget_itm.c - Provides retargeting of C library printf/scanf

//                  functions via ITM / SWO Trace

//*****************************************************************************

// Copyright(C) NXP Semiconductors, 2015

// All rights reserved.

//

// Software that is described herein is for illustrative purposes only

// which provides customers with programming information regarding the

// LPC products.  This software is supplied "AS IS" without any warranties of

// any kind, and NXP Semiconductors and its licensor disclaim any and

// all warranties, express or implied, including all implied warranties of

// merchantability, fitness for a particular purpose and non-infringement of

// intellectual property rights.  NXP Semiconductors assumes no responsibility

// or liability for the use of the software, conveys no license or rights under

// any patent, copyright, mask work right, or any other intellectual property

// rights in or to any products. NXP Semiconductors reserves the right to make

// changes in the software without notification. NXP Semiconductors also makes

// no representation or warranty that such application will be suitable for the

// specified use without further testing or modification.

//

// Permission to use, copy, modify, and distribute this software and its

// documentation is hereby granted, under NXP Semiconductors' and its

// licensor's relevant copyrights in the software, without fee, provided that it

// is used in conjunction with NXP Semiconductors microcontrollers.  This

// copyright, permission, and disclaimer notice must appear in all copies of

// this code.

//*****************************************************************************

#include <stdint.h>

// ******************************************************************

// Cortex-M SWO Trace / Debug registers used for accessing ITM

// ******************************************************************

// CoreDebug - Debug Exception and Monitor Control Register

#define DEMCR           (*((volatile uint32_t *) (0xE000EDFC)))

// DEMCR Trace Enable Bit

#define TRCENA          (1UL << 24)

// ITM Stimulus Port Access Registers

#define ITM_Port8(n)    (*((volatile uint8_t *) (0xE0000000 + 4 * n)))

#define ITM_Port16(n)   (*((volatile uint16_t *) (0xE0000000 + 4 * n)))

#define ITM_Port32(n)   (*((volatile uint32_t *) (0xE0000000 + 4 * n)))

// ITM Trace Control Register

#define ITM_TCR (*((volatile  uint32_t *) (0xE0000000 + 0xE80)))

// ITM TCR: ITM Enable bit

#define ITM_TCR_ITMENA (1UL << 0)

// ITM Trace Enable Register

#define ITM_TER (*((volatile  uint32_t *) (0xE0000000 + 0xE00)))

// ITM Stimulus Port #0 Enable bit

#define ITM_TER_PORT0ENA (1UL << 0)

// ******************************************************************

// Buffer used for pseudo-ITM reads from the host debugger

// ******************************************************************

// Value identifying ITM_RxBuffer is ready for next character

#define ITM_RXBUFFER_EMPTY    0x5AA55AA5

// variable to receive ITM input characters

volatile int32_t ITM_RxBuffer = ITM_RXBUFFER_EMPTY;

// ******************************************************************

// Redlib C Library function : __sys_write

// Newlib C library function : _write

//

// Function called by bottom level of printf routine within C library.

// With the default semihosting stub, this would write the

// character(s) to the debugger console window (which acts as

// stdout). But this version writes the character(s) from the Cortex

// M3/M4 SWO / ITM interface for display in the ITM Console.

// ******************************************************************

#if defined (__REDLIB__)

int __sys_write(int iFileHandle, char *pcBuffer, int iLength) {

#elif defined (__NEWLIB__)

int _write(int iFileHandle, char *pcBuffer, int iLength) {

#endif

  //int _write(int iFileHandle, char *pcBuffer, int iLength)

  //{

#if defined(DEBUG_ENABLE)

  unsigned int i;

  for (i = 0; i < iLength; i++) {

  Board_UARTPutChar(pcBuffer[i]);

  }

#endif

  return iLength;

}

#if defined (__REDLIB__)

// ******************************************************************

// Redlib C Library function : __sys_readc

//

// Called by bottom level of scanf routine within RedLib C library

// to read a character. With the default semihosting stub, this

// would read the character from the debugger console window (which

// acts as stdin). But this version reads the character from a buffer

// which acts as a pseudo-interface to the Cortex-M3/M4 ITM.

// ******************************************************************

int __sys_readc(void) {

  int32_t c = -1;

  // check if debugger connected and ITM channel enabled for tracing

  if ((DEMCR & TRCENA) &&

  // ITM enabled

  (ITM_TCR & ITM_TCR_ITMENA) &&

  // ITM Port #0 enabled

  (ITM_TER & ITM_TER_PORT0ENA)) {

  do {

  if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) {

  // Read from buffer written to by tools

  c = ITM_RxBuffer;

  // Flag ready for next character

  ITM_RxBuffer = ITM_RXBUFFER_EMPTY;

  }

  }while (c == -1);

  }

  return c;

}

// #endif REDLIB __sys_readc()

#elif defined (__NEWLIB__)

// ******************************************************************

// Function _read

//

// Called by bottom level of scanf routine within Newlib C library

// to read multiple characters. With the default semihosting stub, this

// would read  characters from the debugger console window (which

// acts as stdin). But this version reads the characters from a buffer

// which acts as a pseudo-interface to the Cortex-M3/M4 ITM.

// ******************************************************************

int _read(void)

{

#if defined(DEBUG_ENABLE)

  char c = Board_UARTGetChar();

  return (int) c;

#else

  return (int) -1;

#endif

}

#endif // NEWLIB _read()

0 Kudos

1,294 Views
avt
Contributor III

Post your project, so we can see what you have done and what may be the cause of your problems

0 Kudos

1,294 Views
lpcxpresso_supp
NXP Employee
NXP Employee

You might also want take a look at the retargeting example code that we provide for ITM printf:

How to use ITM Printf

which should help you to see how to modify the code inside LPCOpen for Newlib.

Regards,

LPCXpresso Support

0 Kudos