Contiki-NG
segger-rtt-printf.c
1 /*********************************************************************
2 * SEGGER MICROCONTROLLER GmbH & Co. KG *
3 * Solutions for real time microcontroller applications *
4 **********************************************************************
5 * *
6 * (c) 2014 - 2015 SEGGER Microcontroller GmbH & Co. KG *
7 * *
8 * www.segger.com Support: support@segger.com *
9 * *
10 **********************************************************************
11 * *
12 * All rights reserved. *
13 * *
14 * * This software may in its unmodified form be freely redistributed *
15 * in source form. *
16 * * The source code may be modified, provided the source code *
17 * retains the above copyright notice, this list of conditions and *
18 * the following disclaimer. *
19 * * Modified versions of this software in source or linkable form *
20 * may not be distributed without prior consent of SEGGER. *
21 * * This software may only be used for communication with SEGGER *
22 * J-Link debug probes. *
23 * *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
25 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
27 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
28 * DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR *
29 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT *
31 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; *
32 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
33 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE *
35 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
36 * DAMAGE. *
37 * *
38 **********************************************************************
39 ---------------------------END-OF-HEADER------------------------------
40 File : SEGGER_RTT_printf.c
41 Purpose : Replacement for printf to write formatted data via RTT
42 ----------------------------------------------------------------------
43 */
44 #include "segger-rtt.h"
45 #include "segger-rtt-conf.h"
46 
47 /*********************************************************************
48 *
49 * Defines, configurable
50 *
51 **********************************************************************
52 */
53 
54 #ifndef SEGGER_RTT_PRINTF_BUFFER_SIZE
55  #define SEGGER_RTT_PRINTF_BUFFER_SIZE (64)
56 #endif
57 
58 #include <stdlib.h>
59 #include <stdarg.h>
60 
61 
62 #define FORMAT_FLAG_LEFT_JUSTIFY (1u << 0)
63 #define FORMAT_FLAG_PAD_ZERO (1u << 1)
64 #define FORMAT_FLAG_PRINT_SIGN (1u << 2)
65 #define FORMAT_FLAG_ALTERNATE (1u << 3)
66 
67 /*********************************************************************
68 *
69 * Types
70 *
71 **********************************************************************
72 */
73 
74 typedef struct {
75  char* pBuffer;
76  unsigned BufferSize;
77  unsigned Cnt;
78 
79  int ReturnValue;
80 
81  unsigned RTTBufferIndex;
82 } SEGGER_RTT_PRINTF_DESC;
83 
84 /*********************************************************************
85 *
86 * Function prototypes
87 *
88 **********************************************************************
89 */
90 int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList);
91 
92 /*********************************************************************
93 *
94 * Static code
95 *
96 **********************************************************************
97 */
98 /*********************************************************************
99 *
100 * _StoreChar
101 */
102 static void _StoreChar(SEGGER_RTT_PRINTF_DESC * p, char c) {
103  unsigned Cnt;
104 
105  Cnt = p->Cnt;
106  if ((Cnt + 1u) <= p->BufferSize) {
107  *(p->pBuffer + Cnt) = c;
108  p->Cnt = Cnt + 1u;
109  p->ReturnValue++;
110  }
111  //
112  // Write part of string, when the buffer is full
113  //
114  if (p->Cnt == p->BufferSize) {
115  if (SEGGER_RTT_Write(p->RTTBufferIndex, p->pBuffer, p->Cnt) != p->Cnt) {
116  p->ReturnValue = -1;
117  } else {
118  p->Cnt = 0u;
119  }
120  }
121 }
122 
123 /*********************************************************************
124 *
125 * _PrintLeadingChars
126 */
127 static inline void _PrintChars(SEGGER_RTT_PRINTF_DESC * pBufferDesc, const char * s, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) {
128  unsigned Width;
129  char c;
130 
131  // work out the char* width
132  Width = 0u;
133  const char * s_ = s;
134  do {
135  c = *s_;
136  s_++;
137  Width++;
138  if(c == '\0') {
139  break;
140  }
141  } while (1);
142 
143 
144  //
145  // Print leading chars if necessary
146  //
147  if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) {
148  if (FieldWidth != 0u) {
149  if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && (NumDigits == 0u)) {
150  c = '0';
151  } else {
152  c = ' ';
153  }
154  while ((FieldWidth != 0u) && (Width < FieldWidth)) {
155  FieldWidth--;
156  _StoreChar(pBufferDesc, c);
157  if (pBufferDesc->ReturnValue < 0) {
158  break;
159  }
160  }
161  }
162  }
163 
164  //
165  // Print characters
166  //
167  if (NumDigits > 0) {
168  do {
169  c = *s;
170  s++;
171  if (NumDigits == 0) {
172  break;
173  }
174  NumDigits--;
175  _StoreChar(pBufferDesc, c);
176  } while (pBufferDesc->ReturnValue >= 0);
177  } else {
178  do {
179  c = *s;
180  s++;
181  if (c == '\0') {
182  break;
183  }
184  _StoreChar(pBufferDesc, c);
185  } while (pBufferDesc->ReturnValue >= 0);
186  }
187 
188  //
189  // Print trailing spaces if necessary
190  //
191  if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == FORMAT_FLAG_LEFT_JUSTIFY) {
192  if (FieldWidth != 0u) {
193  while ((FieldWidth != 0u) && (Width < FieldWidth)) {
194  FieldWidth--;
195  _StoreChar(pBufferDesc, ' ');
196  if (pBufferDesc->ReturnValue < 0) {
197  break;
198  }
199  }
200  }
201  }
202 
203 
204 }
205 
206 /*********************************************************************
207 *
208 * _PrintUnsigned
209 */
210 static void _PrintUnsigned(SEGGER_RTT_PRINTF_DESC * pBufferDesc, unsigned v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) {
211  static const char _aV2C[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
212  unsigned Div;
213  unsigned Digit;
214  unsigned Number;
215  unsigned Width;
216  char c;
217 
218  Number = v;
219  Digit = 1u;
220  //
221  // Get actual field width
222  //
223  Width = 1u;
224  while (Number >= Base) {
225  Number = (Number / Base);
226  Width++;
227  }
228  //
229  // Print leading chars if necessary
230  //
231  if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) {
232  if (FieldWidth != 0u) {
233  if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && (NumDigits == 0u)) {
234  c = '0';
235  } else {
236  c = ' ';
237  }
238  while ((FieldWidth != 0u) && (Width < FieldWidth)) {
239  FieldWidth--;
240  _StoreChar(pBufferDesc, c);
241  if (pBufferDesc->ReturnValue < 0) {
242  break;
243  }
244  }
245  }
246  }
247  if (pBufferDesc->ReturnValue >= 0) {
248  //
249  // Compute Digit.
250  // Loop until Digit has the value of the highest digit required.
251  // Example: If the output is 345 (Base 10), loop 2 times until Digit is 100.
252  //
253  while (1) {
254  if (NumDigits > 1u) { // User specified a min number of digits to print? => Make sure we loop at least that often, before checking anything else (> 1 check avoids problems with NumDigits being signed / unsigned)
255  NumDigits--;
256  } else {
257  Div = v / Digit;
258  if (Div < Base) { // Is our divider big enough to extract the highest digit from value? => Done
259  break;
260  }
261  }
262  Digit *= Base;
263  }
264  //
265  // Output digits
266  //
267  do {
268  Div = v / Digit;
269  v -= Div * Digit;
270  _StoreChar(pBufferDesc, _aV2C[Div]);
271  if (pBufferDesc->ReturnValue < 0) {
272  break;
273  }
274  Digit /= Base;
275  } while (Digit);
276  //
277  // Print trailing spaces if necessary
278  //
279  if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == FORMAT_FLAG_LEFT_JUSTIFY) {
280  if (FieldWidth != 0u) {
281  while ((FieldWidth != 0u) && (Width < FieldWidth)) {
282  FieldWidth--;
283  _StoreChar(pBufferDesc, ' ');
284  if (pBufferDesc->ReturnValue < 0) {
285  break;
286  }
287  }
288  }
289  }
290  }
291 }
292 
293 /*********************************************************************
294 *
295 * _PrintInt
296 */
297 static void _PrintInt(SEGGER_RTT_PRINTF_DESC * pBufferDesc, int v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) {
298  unsigned Width;
299  int Number;
300 
301  Number = (v < 0) ? -v : v;
302 
303  //
304  // Get actual field width
305  //
306  Width = 1u;
307  while (Number >= (int)Base) {
308  Number = (Number / (int)Base);
309  Width++;
310  }
311  if (NumDigits > Width) {
312  Width = NumDigits;
313  }
314  if ((FieldWidth > 0u) && ((v < 0) || ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN))) {
315  FieldWidth--;
316  }
317 
318  //
319  // Print leading spaces if necessary
320  //
321  if ((((FormatFlags & FORMAT_FLAG_PAD_ZERO) == 0u) || (NumDigits != 0u)) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u)) {
322  if (FieldWidth != 0u) {
323  while ((FieldWidth != 0u) && (Width < FieldWidth)) {
324  FieldWidth--;
325  _StoreChar(pBufferDesc, ' ');
326  if (pBufferDesc->ReturnValue < 0) {
327  break;
328  }
329  }
330  }
331  }
332  //
333  // Print sign if necessary
334  //
335  if (pBufferDesc->ReturnValue >= 0) {
336  if (v < 0) {
337  v = -v;
338  _StoreChar(pBufferDesc, '-');
339  } else if ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN) {
340  _StoreChar(pBufferDesc, '+');
341  } else {
342 
343  }
344  if (pBufferDesc->ReturnValue >= 0) {
345  //
346  // Print leading zeros if necessary
347  //
348  if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) && (NumDigits == 0u)) {
349  if (FieldWidth != 0u) {
350  while ((FieldWidth != 0u) && (Width < FieldWidth)) {
351  FieldWidth--;
352  _StoreChar(pBufferDesc, '0');
353  if (pBufferDesc->ReturnValue < 0) {
354  break;
355  }
356  }
357  }
358  }
359  if (pBufferDesc->ReturnValue >= 0) {
360  //
361  // Print number without sign
362  //
363  _PrintUnsigned(pBufferDesc, (unsigned)v, Base, NumDigits, FieldWidth, FormatFlags);
364  }
365  }
366  }
367 }
368 
369 /*********************************************************************
370 *
371 * Public code
372 *
373 **********************************************************************
374 */
375 /*********************************************************************
376 *
377 * SEGGER_RTT_vprintf
378 *
379 * Function description
380 * Stores a formatted string in SEGGER RTT control block.
381 * This data is read by the host.
382 *
383 * Parameters
384 * BufferIndex Index of "Up"-buffer to be used. (e.g. 0 for "Terminal")
385 * sFormat Pointer to format string
386 * pParamList Pointer to the list of arguments for the format string
387 *
388 * Return values
389 * >= 0: Number of bytes which have been stored in the "Up"-buffer.
390 * < 0: Error
391 */
392 int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList) {
393  char c;
394  SEGGER_RTT_PRINTF_DESC BufferDesc;
395  int v;
396  const char * s;
397  unsigned NumDigits;
398  unsigned FormatFlags;
399  unsigned FieldWidth;
400  char acBuffer[SEGGER_RTT_PRINTF_BUFFER_SIZE];
401 
402  BufferDesc.pBuffer = acBuffer;
403  BufferDesc.BufferSize = SEGGER_RTT_PRINTF_BUFFER_SIZE;
404  BufferDesc.Cnt = 0u;
405  BufferDesc.RTTBufferIndex = BufferIndex;
406  BufferDesc.ReturnValue = 0;
407 
408  do {
409  c = *sFormat;
410  sFormat++;
411  if (c == 0u) {
412  break;
413  }
414  if (c == '%') {
415  //
416  // Filter out flags
417  //
418  FormatFlags = 0u;
419  v = 1;
420  do {
421  c = *sFormat;
422  switch (c) {
423  case '-': FormatFlags |= FORMAT_FLAG_LEFT_JUSTIFY; sFormat++; break;
424  case '0': FormatFlags |= FORMAT_FLAG_PAD_ZERO; sFormat++; break;
425  case '+': FormatFlags |= FORMAT_FLAG_PRINT_SIGN; sFormat++; break;
426  case '#': FormatFlags |= FORMAT_FLAG_ALTERNATE; sFormat++; break;
427  default: v = 0; break;
428  }
429  } while (v);
430  //
431  // filter out field with
432  //
433  FieldWidth = 0u;
434  do {
435  c = *sFormat;
436  if ((c < '0') || (c > '9')) {
437  break;
438  }
439  sFormat++;
440  FieldWidth = (FieldWidth * 10u) + ((unsigned)c - '0');
441  } while (1);
442 
443  //
444  // Filter out precision (number of digits to display)
445  //
446  NumDigits = 0u;
447  c = *sFormat;
448  if (c == '.') {
449  sFormat++;
450  do {
451  c = *sFormat;
452  if (c == '*') {
453  sFormat++;
454  v = va_arg(*pParamList, int);
455  NumDigits = (unsigned)v;
456  break;
457  }
458  if ((c < '0') || (c > '9')) {
459  break;
460  }
461  sFormat++;
462  NumDigits = NumDigits * 10u + ((unsigned)c - '0');
463  } while (1);
464  }
465  //
466  // Filter out length modifier
467  //
468  c = *sFormat;
469  do {
470  if ((c == 'l') || (c == 'h')) {
471  c = *sFormat;
472  sFormat++;
473  } else {
474  break;
475  }
476  } while (1);
477  //
478  // Handle specifiers
479  //
480  c = *sFormat;
481  switch (c) {
482  case 'c': {
483  char c0;
484  v = va_arg(*pParamList, int);
485  c0 = (char)v;
486  _StoreChar(&BufferDesc, c0);
487  break;
488  }
489  case 'd':
490  v = va_arg(*pParamList, int);
491  _PrintInt(&BufferDesc, v, 10u, NumDigits, FieldWidth, FormatFlags);
492  break;
493  case 'u':
494  v = va_arg(*pParamList, int);
495  _PrintUnsigned(&BufferDesc, (unsigned)v, 10u, NumDigits, FieldWidth, FormatFlags);
496  break;
497  case 'x':
498  case 'X':
499  v = va_arg(*pParamList, int);
500  _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, NumDigits, FieldWidth, FormatFlags);
501  break;
502  case 's':
503  s = va_arg(*pParamList, const char *);
504  _PrintChars(&BufferDesc, (const char *)s, 10u, NumDigits, FieldWidth, FormatFlags);
505  break;
506  case 'p':
507  v = va_arg(*pParamList, int);
508  _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, 8u, 8u, 0u);
509  break;
510  case '%':
511  _StoreChar(&BufferDesc, '%');
512  break;
513  default:
514  break;
515  }
516  sFormat++;
517  } else {
518  _StoreChar(&BufferDesc, c);
519  }
520  } while (BufferDesc.ReturnValue >= 0);
521 
522  if (BufferDesc.ReturnValue > 0) {
523  //
524  // Write remaining data, if any
525  //
526  if (BufferDesc.Cnt != 0u) {
527  SEGGER_RTT_Write(BufferIndex, acBuffer, BufferDesc.Cnt);
528  }
529  BufferDesc.ReturnValue += (int)BufferDesc.Cnt;
530  }
531  return BufferDesc.ReturnValue;
532 }
533 
534 /*********************************************************************
535 *
536 * SEGGER_RTT_printf
537 *
538 * Function description
539 * Stores a formatted string in SEGGER RTT control block.
540 * This data is read by the host.
541 *
542 * Parameters
543 * BufferIndex Index of "Up"-buffer to be used. (e.g. 0 for "Terminal")
544 * sFormat Pointer to format string, followed by the arguments for conversion
545 *
546 * Return values
547 * >= 0: Number of bytes which have been stored in the "Up"-buffer.
548 * < 0: Error
549 *
550 * Notes
551 * (1) Conversion specifications have following syntax:
552 * %[flags][FieldWidth][.Precision]ConversionSpecifier
553 * (2) Supported flags:
554 * -: Left justify within the field width
555 * +: Always print sign extension for signed conversions
556 * 0: Pad with 0 instead of spaces. Ignored when using '-'-flag or precision
557 * Supported conversion specifiers:
558 * c: Print the argument as one char
559 * d: Print the argument as a signed integer
560 * u: Print the argument as an unsigned integer
561 * x: Print the argument as an hexadecimal integer
562 * s: Print the string pointed to by the argument
563 * p: Print the argument as an 8-digit hexadecimal integer. (Argument shall be a pointer to void.)
564 */
565 int SEGGER_RTT_printf(unsigned BufferIndex, const char * sFormat, ...) {
566  va_list ParamList;
567 
568  va_start(ParamList, sFormat);
569  return SEGGER_RTT_vprintf(BufferIndex, sFormat, &ParamList);
570 }
571 /*************************** End of file ****************************/