12

I am programming on an arm microprocessor and am trying to debug using print statements via UART. I do not want to add stdlibs just for debugging. Is there a way to print to the console without stdio.h/iostream.h ? Is it possible for me to write my own printf()?

Alternatively I can do this using a DMA controller and writing to the UART directly. However I would like to avoid that is possible. Using the built in test function "echo" or "remote loop-back" I know I have the UART configured properly.

unkulunkulu
  • 11,576
  • 2
  • 31
  • 49
Sam
  • 238
  • 3
  • 14
  • 2
    Yes it is possible - you can write your own output routines, find a small stand-alone partial printf() implementation, or write the necessary back-end support to enable these functions from a minimimal embedded libc (likely included with your toolchain) to run on your platform. – Chris Stratton May 08 '13 at 21:40
  • Thanks. I have heard that newlib as an embedded libc works alright. I will look for a partial printf() first though. – Sam May 08 '13 at 21:47
  • @ChrisStratton: it depends on the OS effectively. It's possible that the native OS routines _are_ the standard library. – Mooing Duck May 08 '13 at 21:54

4 Answers4

10

Short answer: Yes, it's entirely possible to do both of your solutions.

The printf function is quite complex if you want to support all of the data types and formats. But it's not that hard to write something that can output a string or an integer in a few different bases (most people only need decimal and hex, but octal probably only adds another 3-4 lines of code once you have decimal and hex).

Typically, printf is written like this:

 int printf(const char *fmt, ...)
 {
     int ret;
     va_list args; 

     va_start(args, fmt)
     ret = do_xprintf(outputfunc, NULL, fmt, args); 
     va_end(args);
     return ret;
}

And then the do_xprintf() does all the hard work for all variants (printf, sprintf, fprintf, etc)

int do_xprintf(void (*outputfunc)(void *extra, char c), void *extra, const char *fmt, va_list args)
{
    char *ptr = fmt;
    while(1)
    {
       char c = *ptr++;

       if (c == '%')
       {
            c = *ptr++; // Get next character from format string. 
            switch(c)
            {
               case 's': 
                  char *str = va_arg(args, const char *);
                  while(*str)
                  {
                      count++;
                      outputfunc(extra, *str);
                      str++;
                  }
                  break; 
               case 'x': 
                  base = 16;
                  goto output_number;

               case 'd':
                  base = 10;
         output_number:
                  int i = va_arg(args, int);
                  // magical code to output 'i' in 'base'. 
                  break;

               default:
                  count++;
                  outputfunc(extra, c);
                  break;
         }
         else
             count++;
             outputfunc(extra, c);
     }
     return count;
 }                

Now, all you need to do is fill in a few bits of the above code and write an outputfunc() that outputs to your serial port.

Note that this is rough sketch, and I'm sure there are some bugs in the code - and if you want to support floating point or "widths", you will have to work a bit more at it...

(Note on the extra parameter - for output to a FILE * that would be the filepointer, for sprintf, you can pass a structure for the buffer and position in the buffer, or something like that)

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • Thank you very much. I am one of the few people who only need decimal/Hex. I am using fixed point notation and would like to verify my results. This will make things easier. I appreciate your help! – Sam May 08 '13 at 22:03
  • Any reason why `output_number` is a label inside a case statement, instead of a plain function?. – Lundin May 21 '13 at 14:47
  • @Lundin: Only that it's shorter to type. It's also likely to produce shorter code unless the compiler is REALLY clever and realizes that the only difference between the two function calls is the input parameter. – Mats Petersson May 21 '13 at 21:24
3

The concept of "console" does not have much meaning outside of the context of the specific system you are using. Generally in embedded program there is no real concept of a console.

What you are looking for is a way to get data out of your system. If you want to use the UART, and you are not using a high-level OS like GNU/linux, you will need to write your own I/O drivers. Generally this means first configuring the UART for the desired butrate/parity/flow control via register writes. For any type of robust IO you will want it to be interrupt driven, so you will need to write ISRs for tx and rx that utilize circular buffers.

After you have that done, you can write your own printf like Mats indicated.

Brandon Yates
  • 2,022
  • 3
  • 22
  • 33
  • Thanks! I realized this. I was originally trying to figure out how to get around this because I was having issues. But I found out that with my processor I have to access the status register after each write to the tx buffer. If I do not it stalls. I am now dealing with Baud Rate issues but I will definitely use Mats stuff as soon as I get the kinks worked out. Thanks for your help – Sam May 08 '13 at 23:31
2

Since printing out information through a serial port in an embedded system modifies the main program's timing, the best solution I've found is to send a small message encoded in 2 bytes (sometimes 1 byte works fine), and then using a program in the PC to decode those messages and provide the necessary information, which can include statistics and everything you may need. This way, I'm adding just a little bit of overhead to the main program, and letting the PC do the hard work to process the messages. Maybe something like this:

  • 1 byte message: bits 7:4 = module ID, bits 3:0 = debug info.

  • 2 bytes message: bits 15:12 = module ID, bits 11:8 = debug info, bits 7:0 = data.

Then, in the PC software, you have to declare a table with the messages that map to a given module ID/debug info pair, and use them to be printed on the screen.

Maybe it's not as flexible as the pseudo-printf function, since you need a fixed set of messages in the PC to decode, but it doesn't add too much overhead, as I mentioned before.

Hope it helps.

Fernando

Fernando
  • 97
  • 4
1

I have found for background debugging, enqueuing characters into a circular buffer which is then drained by a polling routine on the uart transmit register, is my method of choice.

The enqueuing routines are based around a character, string and the variable size (to either hex or fixed width decimal). And a deluxe buffer routine could indicate an overflow with a reserved character.

The approach has the lowest overhead/impact on target operation, can be used (with care) in interrupt routine and the idea is easily transferable, so I have ignored the debugger on all the systems I have used.

chip_wrangler
  • 145
  • 1
  • 4