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