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