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
73 #define FORMAT_FLAG_LEFT_JUSTIFY (1u << 0)
74 #define FORMAT_FLAG_PAD_ZERO (1u << 1)
75 #define FORMAT_FLAG_PRINT_SIGN (1u << 2)
76 #define FORMAT_FLAG_ALTERNATE (1u << 3)
77
78 /*********************************************************************
79 *
80 * Types
81 *
82 **********************************************************************
83 */
84
85 typedef struct {
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 unsigned Cnt;
114
115 Cnt = p->Cnt;
116 if ((Cnt + 1u) <= p->BufferSize) {
117 *(p->pBuffer + Cnt) = c;
118 p->Cnt = Cnt + 1u;
119 p->ReturnValue++;
120 }
121 //
122 // Write part of string, when the buffer is full
123 //
124 if (p->Cnt == p->BufferSize) {
125 if (SEGGER_RTT_Write(p->RTTBufferIndex, p->pBuffer, p->Cnt) != p->Cnt) {
126 p->ReturnValue = -1;
127 } else {
128 p->Cnt = 0u;
129 }
130 }
131 }
132
133 /*********************************************************************
134 *
135 * _PrintUnsigned
136 */
_PrintUnsigned(SEGGER_RTT_PRINTF_DESC * pBufferDesc,unsigned v,unsigned Base,unsigned NumDigits,unsigned FieldWidth,unsigned FormatFlags)137 static void _PrintUnsigned(SEGGER_RTT_PRINTF_DESC * pBufferDesc, unsigned v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) {
138 static const char _aV2C[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
139 unsigned Div;
140 unsigned Digit;
141 unsigned Number;
142 unsigned Width;
143 char c;
144
145 Number = v;
146 Digit = 1u;
147 //
148 // Get actual field width
149 //
150 Width = 1u;
151 while (Number >= Base) {
152 Number = (Number / Base);
153 Width++;
154 }
155 if (NumDigits > Width) {
156 Width = NumDigits;
157 }
158 //
159 // Print leading chars if necessary
160 //
161 if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) {
162 if (FieldWidth != 0u) {
163 if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && (NumDigits == 0u)) {
164 c = '0';
165 } else {
166 c = ' ';
167 }
168 while ((FieldWidth != 0u) && (Width < FieldWidth)) {
169 FieldWidth--;
170 _StoreChar(pBufferDesc, c);
171 if (pBufferDesc->ReturnValue < 0) {
172 break;
173 }
174 }
175 }
176 }
177 if (pBufferDesc->ReturnValue >= 0) {
178 //
179 // Compute Digit.
180 // Loop until Digit has the value of the highest digit required.
181 // Example: If the output is 345 (Base 10), loop 2 times until Digit is 100.
182 //
183 while (1) {
184 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)
185 NumDigits--;
186 } else {
187 Div = v / Digit;
188 if (Div < Base) { // Is our divider big enough to extract the highest digit from value? => Done
189 break;
190 }
191 }
192 Digit *= Base;
193 }
194 //
195 // Output digits
196 //
197 do {
198 Div = v / Digit;
199 v -= Div * Digit;
200 _StoreChar(pBufferDesc, _aV2C[Div]);
201 if (pBufferDesc->ReturnValue < 0) {
202 break;
203 }
204 Digit /= Base;
205 } while (Digit);
206 //
207 // Print trailing spaces if necessary
208 //
209 if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == FORMAT_FLAG_LEFT_JUSTIFY) {
210 if (FieldWidth != 0u) {
211 while ((FieldWidth != 0u) && (Width < FieldWidth)) {
212 FieldWidth--;
213 _StoreChar(pBufferDesc, ' ');
214 if (pBufferDesc->ReturnValue < 0) {
215 break;
216 }
217 }
218 }
219 }
220 }
221 }
222
223 /*********************************************************************
224 *
225 * _PrintInt
226 */
_PrintInt(SEGGER_RTT_PRINTF_DESC * pBufferDesc,int v,unsigned Base,unsigned NumDigits,unsigned FieldWidth,unsigned FormatFlags)227 static void _PrintInt(SEGGER_RTT_PRINTF_DESC * pBufferDesc, int v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) {
228 unsigned Width;
229 int Number;
230
231 Number = (v < 0) ? -v : v;
232
233 //
234 // Get actual field width
235 //
236 Width = 1u;
237 while (Number >= (int)Base) {
238 Number = (Number / (int)Base);
239 Width++;
240 }
241 if (NumDigits > Width) {
242 Width = NumDigits;
243 }
244 if ((FieldWidth > 0u) && ((v < 0) || ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN))) {
245 FieldWidth--;
246 }
247
248 //
249 // Print leading spaces if necessary
250 //
251 if ((((FormatFlags & FORMAT_FLAG_PAD_ZERO) == 0u) || (NumDigits != 0u)) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u)) {
252 if (FieldWidth != 0u) {
253 while ((FieldWidth != 0u) && (Width < FieldWidth)) {
254 FieldWidth--;
255 _StoreChar(pBufferDesc, ' ');
256 if (pBufferDesc->ReturnValue < 0) {
257 break;
258 }
259 }
260 }
261 }
262 //
263 // Print sign if necessary
264 //
265 if (pBufferDesc->ReturnValue >= 0) {
266 if (v < 0) {
267 v = -v;
268 _StoreChar(pBufferDesc, '-');
269 } else if ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN) {
270 _StoreChar(pBufferDesc, '+');
271 } else {
272
273 }
274 if (pBufferDesc->ReturnValue >= 0) {
275 //
276 // Print leading zeros if necessary
277 //
278 if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) && (NumDigits == 0u)) {
279 if (FieldWidth != 0u) {
280 while ((FieldWidth != 0u) && (Width < FieldWidth)) {
281 FieldWidth--;
282 _StoreChar(pBufferDesc, '0');
283 if (pBufferDesc->ReturnValue < 0) {
284 break;
285 }
286 }
287 }
288 }
289 if (pBufferDesc->ReturnValue >= 0) {
290 //
291 // Print number without sign
292 //
293 _PrintUnsigned(pBufferDesc, (unsigned)v, Base, NumDigits, FieldWidth, FormatFlags);
294 }
295 }
296 }
297 }
298
299 /*********************************************************************
300 *
301 * Public code
302 *
303 **********************************************************************
304 */
305 /*********************************************************************
306 *
307 * SEGGER_RTT_vprintf
308 *
309 * Function description
310 * Stores a formatted string in SEGGER RTT control block.
311 * This data is read by the host.
312 *
313 * Parameters
314 * BufferIndex Index of "Up"-buffer to be used. (e.g. 0 for "Terminal")
315 * sFormat Pointer to format string
316 * pParamList Pointer to the list of arguments for the format string
317 *
318 * Return values
319 * >= 0: Number of bytes which have been stored in the "Up"-buffer.
320 * < 0: Error
321 */
SEGGER_RTT_vprintf(unsigned BufferIndex,const char * sFormat,va_list * pParamList)322 int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList) {
323 char c;
324 SEGGER_RTT_PRINTF_DESC BufferDesc;
325 int v;
326 unsigned NumDigits;
327 unsigned FormatFlags;
328 unsigned FieldWidth;
329 char acBuffer[SEGGER_RTT_PRINTF_BUFFER_SIZE];
330
331 BufferDesc.pBuffer = acBuffer;
332 BufferDesc.BufferSize = SEGGER_RTT_PRINTF_BUFFER_SIZE;
333 BufferDesc.Cnt = 0u;
334 BufferDesc.RTTBufferIndex = BufferIndex;
335 BufferDesc.ReturnValue = 0;
336
337 do {
338 c = *sFormat;
339 sFormat++;
340 if (c == 0u) {
341 break;
342 }
343 if (c == '%') {
344 //
345 // Filter out flags
346 //
347 FormatFlags = 0u;
348 v = 1;
349 do {
350 c = *sFormat;
351 switch (c) {
352 case '-': FormatFlags |= FORMAT_FLAG_LEFT_JUSTIFY; sFormat++; break;
353 case '0': FormatFlags |= FORMAT_FLAG_PAD_ZERO; sFormat++; break;
354 case '+': FormatFlags |= FORMAT_FLAG_PRINT_SIGN; sFormat++; break;
355 case '#': FormatFlags |= FORMAT_FLAG_ALTERNATE; sFormat++; break;
356 default: v = 0; break;
357 }
358 } while (v);
359 //
360 // filter out field with
361 //
362 FieldWidth = 0u;
363 do {
364 c = *sFormat;
365 if ((c < '0') || (c > '9')) {
366 break;
367 }
368 sFormat++;
369 FieldWidth = (FieldWidth * 10u) + ((unsigned)c - '0');
370 } while (1);
371
372 //
373 // Filter out precision (number of digits to display)
374 //
375 NumDigits = 0u;
376 c = *sFormat;
377 if (c == '.') {
378 sFormat++;
379 do {
380 c = *sFormat;
381 if ((c < '0') || (c > '9')) {
382 break;
383 }
384 sFormat++;
385 NumDigits = NumDigits * 10u + ((unsigned)c - '0');
386 } while (1);
387 }
388 //
389 // Filter out length modifier
390 //
391 c = *sFormat;
392 do {
393 if ((c == 'l') || (c == 'h')) {
394 sFormat++;
395 c = *sFormat;
396 } else {
397 break;
398 }
399 } while (1);
400 //
401 // Handle specifiers
402 //
403 switch (c) {
404 case 'c': {
405 char c0;
406 v = va_arg(*pParamList, int);
407 c0 = (char)v;
408 _StoreChar(&BufferDesc, c0);
409 break;
410 }
411 case 'd':
412 v = va_arg(*pParamList, int);
413 _PrintInt(&BufferDesc, v, 10u, NumDigits, FieldWidth, FormatFlags);
414 break;
415 case 'u':
416 v = va_arg(*pParamList, int);
417 _PrintUnsigned(&BufferDesc, (unsigned)v, 10u, NumDigits, FieldWidth, FormatFlags);
418 break;
419 case 'x':
420 case 'X':
421 v = va_arg(*pParamList, int);
422 _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, NumDigits, FieldWidth, FormatFlags);
423 break;
424 case 's':
425 {
426 const char * s = va_arg(*pParamList, const char *);
427 do {
428 c = *s;
429 s++;
430 if (c == '\0') {
431 break;
432 }
433 _StoreChar(&BufferDesc, c);
434 } while (BufferDesc.ReturnValue >= 0);
435 }
436 break;
437 case 'p':
438 v = va_arg(*pParamList, int);
439 _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, 8u, 8u, 0u);
440 break;
441 case '%':
442 _StoreChar(&BufferDesc, '%');
443 break;
444 default:
445 break;
446 }
447 sFormat++;
448 } else {
449 _StoreChar(&BufferDesc, c);
450 }
451 } while (BufferDesc.ReturnValue >= 0);
452
453 if (BufferDesc.ReturnValue > 0) {
454 //
455 // Write remaining data, if any
456 //
457 if (BufferDesc.Cnt != 0u) {
458 SEGGER_RTT_Write(BufferIndex, acBuffer, BufferDesc.Cnt);
459 }
460 BufferDesc.ReturnValue += (int)BufferDesc.Cnt;
461 }
462 return BufferDesc.ReturnValue;
463 }
464
465 /*********************************************************************
466 *
467 * SEGGER_RTT_printf
468 *
469 * Function description
470 * Stores a formatted string in SEGGER RTT control block.
471 * This data is read by the host.
472 *
473 * Parameters
474 * BufferIndex Index of "Up"-buffer to be used. (e.g. 0 for "Terminal")
475 * sFormat Pointer to format string, followed by the arguments for conversion
476 *
477 * Return values
478 * >= 0: Number of bytes which have been stored in the "Up"-buffer.
479 * < 0: Error
480 *
481 * Notes
482 * (1) Conversion specifications have following syntax:
483 * %[flags][FieldWidth][.Precision]ConversionSpecifier
484 * (2) Supported flags:
485 * -: Left justify within the field width
486 * +: Always print sign extension for signed conversions
487 * 0: Pad with 0 instead of spaces. Ignored when using '-'-flag or precision
488 * Supported conversion specifiers:
489 * c: Print the argument as one char
490 * d: Print the argument as a signed integer
491 * u: Print the argument as an unsigned integer
492 * x: Print the argument as an hexadecimal integer
493 * s: Print the string pointed to by the argument
494 * p: Print the argument as an 8-digit hexadecimal integer. (Argument shall be a pointer to void.)
495 */
SEGGER_RTT_printf(unsigned BufferIndex,const char * sFormat,...)496 int SEGGER_RTT_printf(unsigned BufferIndex, const char * sFormat, ...) {
497 int r;
498 va_list ParamList;
499
500 va_start(ParamList, sFormat);
501 r = SEGGER_RTT_vprintf(BufferIndex, sFormat, &ParamList);
502 va_end(ParamList);
503 return r;
504 }
505 /*************************** End of file ****************************/
506