4
\$\begingroup\$

I'm trying to develop an application for the NXP LPC1788 microcontroller and I would like to be able to log formatted strings at arbitrary points within the source code to a log file in a way that has negligible impact on the performance of the program. Essentially, I would like something as close as possible to the Logger utilities you get with Java.

Currently, I'm using an iJet debugger and with that I can print individual 8-bit or 32-bit numerical values to the IAR EW event window using ITM_EVENT8_WITH_PC and ITM_EVENT32_WITH_PC, along with the program counter and timestamp. However, numbers alone aren't quite descriptive enough, and I'm limited to four channels. This means that I often have to choose some small portion of the program to log at any one time, commenting out calls to ITM_EVENT... in unrelated parts and adding new ones or uncommenting existing ones in the part I want to monitor closely. This isn't really a very efficient approach.

One thing I've tried looking into is creating a macro file containing a function LogMessage that takes a string as a parameter and passes it to the built-in __message macro. However, I'm not sure how I can include this macro in the program so that I can invoke LogMessage within the source code - I've tried specifying it in Debugger -> Setup Macros, but this does not link in LogMessage with the source code.

Any help would be appreciated.

EDIT

How it's done in Java

To give an example of how the Logger framework (specifically of the SLF4J variety) can be used in Java, I'll take the one from the SLF4J manual and explain what's happening:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloWorld {
  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World");
  }
}

In this case, we're creating a class with a static main method which serves as the entry-point to the application. Then, we call a factory method getLogger to produce a logging object that we make aware of the class we're instantiating it in - HelloWorld.class. This information can be configured to be included in logging outputs.

We can then use this logging object to produce logs of different log levels depending on the importance and nature of what we're logging. In the example, we output "Hello World" at the INFO log level, which is used for logging relatively important information, and the logging framework will treat that output appropriately based on its log level and the framework's configuration - this can include outputting it to stdout or saving it to a file.

How I'd like to do it

It would be nice if I could get something similar to that working with IAR Embedded Workbench applications. I discussed the problem with a colleague and found we could route data from the ITM stimulus ports to the Terminal IO window via I-jet/JTAGjet -> SWO Configuration... from the menubar at the top, and then log the Terminal I/O window. You can log directly from the ITM ports to a log file, but I find that it seems to overwrite the file every time you write a new value to an ITM port for some reason.

What I'm experimenting with now is creating a method like logData(uint16_t logId, uint8_t logLevel, uint32_t value), which will cause the data to be saved to a log file in a particular way:

  • logId is an identifier that is unique for each log message
  • logLevel will be analogous to log levels in SLF4J (and should eventually be replaced with an enum)
  • value is a single value to associate with the logging message (and should eventually be replaced with a variadic parameter, to accept any number of values)

The log file produced isn't readable, but I could put together a Python script that will take the generated log file and produce a nicely-formatted human-readable one in the same style as an SLF4J log file.

At least that's the idea.

EDIT 2

I've put together what feels like a decent enough logging framework for my requirements, although I haven't yet tried using it extensively to see how it holds up under pressure.

In my microcontroller application's main method, I can make a call like LOGGER_Init(LOG_LEVEL_DEBUG, 5);, where the first argument is the logger log level and the second argument is the ITM stimulus port to print log data on. The logger will only print log messages with log level equal to or higher than its own (e.g. a logger set to LOG_LEVEL_INFO will print LOG_LEVEL_WARN messages and LOG_LEVEL_INFO messages but not LOG_LEVEL_DEBUG messages).

I can then log data in the following way:

LOGGER_Info(0x1, 0x20);
LOGGER_Debug(0x2, 0x3A, 0xFF);

These methods treat the first value as a log identifier and the remaining arguments are treated in a variadic manner as log data.

In a 'log map' file, I maintain a mapping of log identifiers to placeholder log output messages, where '{}' represents a placeholder value like in SLF4J:

static const LOG_MAP_T logMappings[] = {
  {1, "The first log message in main provides {}"},
  {2, "The second log message provides {} and also {}"}
};

The logger prints out log messages of the appropriate levels to the configured ITM stimulus port, which then sends it through to the Terminal I/O window where it gets saved in a log file.

I then have a Python script which goes through both the log outputs and the mapping file and pieces everything together to produce something like this:

>>> 
INFO  The first log message in main provides 32
DEBUG The second log message provides 58 and also 255
\$\endgroup\$

migrated from stackoverflow.com Jan 19 '15 at 13:58

This question came from our site for professional and enthusiast programmers.

  • 1
    \$\begingroup\$ Can you use standard I/O functions with Terminal I/O window (Note that you may have to fiddle with semihosting options in project settings.). Do you have any serial port available? With 176x I usually have UART0 as debugging port, because it uses same pins as ISP interface. You should probably add small code example on how you would use this, as people might not be familiar with Logger from Java (me included). \$\endgroup\$ – user694733 Jan 19 '15 at 9:14
  • \$\begingroup\$ @user694733 Hi, I have got semihosting configured but it's really slow. However, I discussed the problem with a colleague and we found that we could route data from the ITM ports to the Terminal I/O window (page 10: iar.com/Global/Resources/Developers_Toolbox/…), and then log the Terminal I/O window. It's possible to save data from ITM ports directly to a log file, but I'm finding this doesn't work very well for some reason. I'll update the OP with a Logger ex. \$\endgroup\$ – Tagc Jan 19 '15 at 9:24
  • 1
    \$\begingroup\$ This question is a little relevant. \$\endgroup\$ – user694733 Jan 19 '15 at 14:52
  • \$\begingroup\$ @user694733 Thanks for the information, but I've got something working now that I'm happy with for the time being (see latest edit). I'll test what I have tomorrow to make sure it can keep up under pressure, and if it can't I'll come back here and give a more thorough look at what you've linked to see if it can help. \$\endgroup\$ – Tagc Jan 19 '15 at 16:21
  • \$\begingroup\$ A very good logging system is Segger RTT (Real Time Transfer). But it requires a Segger debugger. Anyway, maybe worth a look? \$\endgroup\$ – RHaguiuda Mar 16 '17 at 2:46
0
\$\begingroup\$

I would recommend that you enable the ETM feature of the LPC chip you are using. The Embedded Trace Macrocell is essentiall a memeory location that you can write charactures into that will make it's way via the JTAG/SWD connection to your EWARM into the terminal windows (or even in the Eclipse plugin).

You can just write a macro to write the chars directly into the mem address, or you can use what IAR calls "host mode" I think where they hack your stdout and do it for you. Then just use printf of whatever wrapper around that you like.

Look for the appnote on their site, and ignore all the fancy trace capabilities ETM provides (if you don't need it) and just look for the debug output stuff.

If you are under maintenance, call the nearest IAR office and ask for an FAE and they'll step you through it.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.