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