1 /*********************************************************************
\r
2 * SEGGER MICROCONTROLLER GmbH & Co. KG *
\r
3 * Solutions for real time microcontroller applications *
\r
4 **********************************************************************
\r
6 * (c) 2014 - 2016 SEGGER Microcontroller GmbH & Co. KG *
\r
8 * www.segger.com Support: support@segger.com *
\r
10 **********************************************************************
\r
12 * SEGGER RTT * Real Time Transfer for embedded targets *
\r
14 **********************************************************************
\r
16 * All rights reserved. *
\r
18 * * This software may in its unmodified form be freely redistributed *
\r
19 * in source, linkable, or executable form. *
\r
20 * * The source code may be modified, provided the source code *
\r
21 * retains the above copyright notice, this list of conditions and *
\r
22 * the following disclaimer. *
\r
23 * * Modified versions of this software in source, executable, or *
\r
24 * linkable form may not be distributed without prior consent of *
\r
26 * * This software may only be used for communication with SEGGER *
\r
27 * J-Link debug probes. *
\r
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
\r
30 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
\r
31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
\r
32 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
\r
33 * DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR *
\r
34 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
\r
35 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT *
\r
36 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; *
\r
37 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
\r
38 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
\r
39 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE *
\r
40 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
\r
43 **********************************************************************
\r
45 * RTT version: 6.00e *
\r
47 **********************************************************************
\r
48 ---------------------------END-OF-HEADER------------------------------
\r
49 File : SEGGER_RTT_printf.c
\r
50 Purpose : Replacement for printf to write formatted data via RTT
\r
51 Revision: $Rev: 3667 $
\r
52 ----------------------------------------------------------------------
\r
54 #include "SEGGER_RTT.h"
\r
55 #include "SEGGER_RTT_Conf.h"
\r
57 /*********************************************************************
\r
59 * Defines, configurable
\r
61 **********************************************************************
\r
64 #ifndef SEGGER_RTT_PRINTF_BUFFER_SIZE
\r
65 #define SEGGER_RTT_PRINTF_BUFFER_SIZE (64)
\r
72 #define FORMAT_FLAG_LEFT_JUSTIFY (1u << 0)
\r
73 #define FORMAT_FLAG_PAD_ZERO (1u << 1)
\r
74 #define FORMAT_FLAG_PRINT_SIGN (1u << 2)
\r
75 #define FORMAT_FLAG_ALTERNATE (1u << 3)
\r
77 /*********************************************************************
\r
81 **********************************************************************
\r
86 unsigned BufferSize;
\r
91 unsigned RTTBufferIndex;
\r
92 } SEGGER_RTT_PRINTF_DESC;
\r
94 /*********************************************************************
\r
96 * Function prototypes
\r
98 **********************************************************************
\r
100 int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList);
\r
102 /*********************************************************************
\r
106 **********************************************************************
\r
108 /*********************************************************************
\r
112 static void _StoreChar(SEGGER_RTT_PRINTF_DESC * p, char c) {
\r
116 if ((Cnt + 1u) <= p->BufferSize) {
\r
117 *(p->pBuffer + Cnt) = c;
\r
122 // Write part of string, when the buffer is full
\r
124 if (p->Cnt == p->BufferSize) {
\r
125 if (SEGGER_RTT_Write(p->RTTBufferIndex, p->pBuffer, p->Cnt) != p->Cnt) {
\r
126 p->ReturnValue = -1;
\r
133 /*********************************************************************
\r
137 static void _PrintUnsigned(SEGGER_RTT_PRINTF_DESC * pBufferDesc, unsigned v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) {
\r
138 static const char _aV2C[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
\r
148 // Get actual field width
\r
151 while (Number >= Base) {
\r
152 Number = (Number / Base);
\r
155 if (NumDigits > Width) {
\r
159 // Print leading chars if necessary
\r
161 if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) {
\r
162 if (FieldWidth != 0u) {
\r
163 if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && (NumDigits == 0u)) {
\r
168 while ((FieldWidth != 0u) && (Width < FieldWidth)) {
\r
170 _StoreChar(pBufferDesc, c);
\r
171 if (pBufferDesc->ReturnValue < 0) {
\r
177 if (pBufferDesc->ReturnValue >= 0) {
\r
180 // Loop until Digit has the value of the highest digit required.
\r
181 // Example: If the output is 345 (Base 10), loop 2 times until Digit is 100.
\r
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)
\r
188 if (Div < Base) { // Is our divider big enough to extract the highest digit from value? => Done
\r
200 _StoreChar(pBufferDesc, _aV2C[Div]);
\r
201 if (pBufferDesc->ReturnValue < 0) {
\r
207 // Print trailing spaces if necessary
\r
209 if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == FORMAT_FLAG_LEFT_JUSTIFY) {
\r
210 if (FieldWidth != 0u) {
\r
211 while ((FieldWidth != 0u) && (Width < FieldWidth)) {
\r
213 _StoreChar(pBufferDesc, ' ');
\r
214 if (pBufferDesc->ReturnValue < 0) {
\r
223 /*********************************************************************
\r
227 static void _PrintInt(SEGGER_RTT_PRINTF_DESC * pBufferDesc, int v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) {
\r
231 Number = (v < 0) ? -v : v;
\r
234 // Get actual field width
\r
237 while (Number >= (int)Base) {
\r
238 Number = (Number / (int)Base);
\r
241 if (NumDigits > Width) {
\r
244 if ((FieldWidth > 0u) && ((v < 0) || ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN))) {
\r
249 // Print leading spaces if necessary
\r
251 if ((((FormatFlags & FORMAT_FLAG_PAD_ZERO) == 0u) || (NumDigits != 0u)) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u)) {
\r
252 if (FieldWidth != 0u) {
\r
253 while ((FieldWidth != 0u) && (Width < FieldWidth)) {
\r
255 _StoreChar(pBufferDesc, ' ');
\r
256 if (pBufferDesc->ReturnValue < 0) {
\r
263 // Print sign if necessary
\r
265 if (pBufferDesc->ReturnValue >= 0) {
\r
268 _StoreChar(pBufferDesc, '-');
\r
269 } else if ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN) {
\r
270 _StoreChar(pBufferDesc, '+');
\r
274 if (pBufferDesc->ReturnValue >= 0) {
\r
276 // Print leading zeros if necessary
\r
278 if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) && (NumDigits == 0u)) {
\r
279 if (FieldWidth != 0u) {
\r
280 while ((FieldWidth != 0u) && (Width < FieldWidth)) {
\r
282 _StoreChar(pBufferDesc, '0');
\r
283 if (pBufferDesc->ReturnValue < 0) {
\r
289 if (pBufferDesc->ReturnValue >= 0) {
\r
291 // Print number without sign
\r
293 _PrintUnsigned(pBufferDesc, (unsigned)v, Base, NumDigits, FieldWidth, FormatFlags);
\r
299 /*********************************************************************
\r
303 **********************************************************************
\r
305 /*********************************************************************
\r
307 * SEGGER_RTT_vprintf
\r
309 * Function description
\r
310 * Stores a formatted string in SEGGER RTT control block.
\r
311 * This data is read by the host.
\r
314 * BufferIndex Index of "Up"-buffer to be used. (e.g. 0 for "Terminal")
\r
315 * sFormat Pointer to format string
\r
316 * pParamList Pointer to the list of arguments for the format string
\r
319 * >= 0: Number of bytes which have been stored in the "Up"-buffer.
\r
322 int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList) {
\r
324 SEGGER_RTT_PRINTF_DESC BufferDesc;
\r
326 unsigned NumDigits;
\r
327 unsigned FormatFlags;
\r
328 unsigned FieldWidth;
\r
329 char acBuffer[SEGGER_RTT_PRINTF_BUFFER_SIZE];
\r
331 BufferDesc.pBuffer = acBuffer;
\r
332 BufferDesc.BufferSize = SEGGER_RTT_PRINTF_BUFFER_SIZE;
\r
333 BufferDesc.Cnt = 0u;
\r
334 BufferDesc.RTTBufferIndex = BufferIndex;
\r
335 BufferDesc.ReturnValue = 0;
\r
345 // Filter out flags
\r
352 case '-': FormatFlags |= FORMAT_FLAG_LEFT_JUSTIFY; sFormat++; break;
\r
353 case '0': FormatFlags |= FORMAT_FLAG_PAD_ZERO; sFormat++; break;
\r
354 case '+': FormatFlags |= FORMAT_FLAG_PRINT_SIGN; sFormat++; break;
\r
355 case '#': FormatFlags |= FORMAT_FLAG_ALTERNATE; sFormat++; break;
\r
356 default: v = 0; break;
\r
360 // filter out field with
\r
365 if ((c < '0') || (c > '9')) {
\r
369 FieldWidth = (FieldWidth * 10u) + ((unsigned)c - '0');
\r
373 // Filter out precision (number of digits to display)
\r
381 if ((c < '0') || (c > '9')) {
\r
385 NumDigits = NumDigits * 10u + ((unsigned)c - '0');
\r
389 // Filter out length modifier
\r
393 if ((c == 'l') || (c == 'h')) {
\r
401 // Handle specifiers
\r
406 v = va_arg(*pParamList, int);
\r
408 _StoreChar(&BufferDesc, c0);
\r
412 v = va_arg(*pParamList, int);
\r
413 _PrintInt(&BufferDesc, v, 10u, NumDigits, FieldWidth, FormatFlags);
\r
416 v = va_arg(*pParamList, int);
\r
417 _PrintUnsigned(&BufferDesc, (unsigned)v, 10u, NumDigits, FieldWidth, FormatFlags);
\r
421 v = va_arg(*pParamList, int);
\r
422 _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, NumDigits, FieldWidth, FormatFlags);
\r
426 const char * s = va_arg(*pParamList, const char *);
\r
433 _StoreChar(&BufferDesc, c);
\r
434 } while (BufferDesc.ReturnValue >= 0);
\r
438 v = va_arg(*pParamList, int);
\r
439 _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, 8u, 8u, 0u);
\r
442 _StoreChar(&BufferDesc, '%');
\r
449 _StoreChar(&BufferDesc, c);
\r
451 } while (BufferDesc.ReturnValue >= 0);
\r
453 if (BufferDesc.ReturnValue > 0) {
\r
455 // Write remaining data, if any
\r
457 if (BufferDesc.Cnt != 0u) {
\r
458 SEGGER_RTT_Write(BufferIndex, acBuffer, BufferDesc.Cnt);
\r
460 BufferDesc.ReturnValue += (int)BufferDesc.Cnt;
\r
462 return BufferDesc.ReturnValue;
\r
465 /*********************************************************************
\r
467 * SEGGER_RTT_printf
\r
469 * Function description
\r
470 * Stores a formatted string in SEGGER RTT control block.
\r
471 * This data is read by the host.
\r
474 * BufferIndex Index of "Up"-buffer to be used. (e.g. 0 for "Terminal")
\r
475 * sFormat Pointer to format string, followed by the arguments for conversion
\r
478 * >= 0: Number of bytes which have been stored in the "Up"-buffer.
\r
482 * (1) Conversion specifications have following syntax:
\r
483 * %[flags][FieldWidth][.Precision]ConversionSpecifier
\r
484 * (2) Supported flags:
\r
485 * -: Left justify within the field width
\r
486 * +: Always print sign extension for signed conversions
\r
487 * 0: Pad with 0 instead of spaces. Ignored when using '-'-flag or precision
\r
488 * Supported conversion specifiers:
\r
489 * c: Print the argument as one char
\r
490 * d: Print the argument as a signed integer
\r
491 * u: Print the argument as an unsigned integer
\r
492 * x: Print the argument as an hexadecimal integer
\r
493 * s: Print the string pointed to by the argument
\r
494 * p: Print the argument as an 8-digit hexadecimal integer. (Argument shall be a pointer to void.)
\r
496 int SEGGER_RTT_printf(unsigned BufferIndex, const char * sFormat, ...) {
\r
500 va_start(ParamList, sFormat);
\r
501 r = SEGGER_RTT_vprintf(BufferIndex, sFormat, &ParamList);
\r
505 /*************************** End of file ****************************/
\r