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