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 */ 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 */ 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 */ 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 */ 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 */ 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