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