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