/********************************************************************* | |
* SEGGER MICROCONTROLLER GmbH & Co. KG * | |
* Solutions for real time microcontroller applications * | |
********************************************************************** | |
* * | |
* (c) 2014-2014 SEGGER Microcontroller GmbH & Co. KG * | |
* * | |
* Internet: www.segger.com Support: support@segger.com * | |
* * | |
********************************************************************** | |
---------------------------------------------------------------------- | |
File : SEGGER_RTT_printf.c | |
Date : 17 Dec 2014 | |
Purpose : Replacement for printf to write formatted data via RTT | |
---------------------------END-OF-HEADER------------------------------ | |
*/ | |
#include "SEGGER_RTT.h" | |
#include "SEGGER_RTT_Conf.h" | |
/********************************************************************* | |
* | |
* Defines, configurable | |
* | |
********************************************************************** | |
*/ | |
#ifndef SEGGER_RTT_PRINTF_BUFFER_SIZE | |
#define SEGGER_RTT_PRINTF_BUFFER_SIZE (64) | |
#endif | |
#include <stdlib.h> | |
#include <stdarg.h> | |
#define FORMAT_FLAG_LEFT_JUSTIFY (1 << 0) | |
#define FORMAT_FLAG_PAD_ZERO (1 << 1) | |
#define FORMAT_FLAG_PRINT_SIGN (1 << 2) | |
#define FORMAT_FLAG_ALTERNATE (1 << 3) | |
/********************************************************************* | |
* | |
* Types | |
* | |
********************************************************************** | |
*/ | |
typedef struct { | |
char* pBuffer; | |
int BufferSize; | |
int Cnt; | |
int ReturnValue; | |
unsigned RTTBufferIndex; | |
} SEGGER_RTT_PRINTF_DESC; | |
/********************************************************************* | |
* | |
* Function prototypes | |
* | |
********************************************************************** | |
*/ | |
int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList); | |
/********************************************************************* | |
* | |
* Static code | |
* | |
********************************************************************** | |
*/ | |
/********************************************************************* | |
* | |
* _StoreChar | |
*/ | |
static void _StoreChar(SEGGER_RTT_PRINTF_DESC * p, char c) { | |
int Cnt; | |
Cnt = p->Cnt; | |
if ((Cnt + 1) <= p->BufferSize) { | |
*(p->pBuffer + Cnt) = c; | |
p->Cnt = Cnt + 1; | |
p->ReturnValue++; | |
} | |
// | |
// Write part of string, when the buffer is full | |
// | |
if (p->Cnt == p->BufferSize) { | |
if (SEGGER_RTT_Write(p->RTTBufferIndex, p->pBuffer, p->Cnt) != p->Cnt) { | |
p->ReturnValue = -1; | |
} else { | |
p->Cnt = 0; | |
} | |
} | |
} | |
/********************************************************************* | |
* | |
* _PrintUnsigned | |
*/ | |
static void _PrintUnsigned(SEGGER_RTT_PRINTF_DESC * pBufferDesc, unsigned v, unsigned Base, int NumDigits, unsigned FieldWidth, unsigned FormatFlags) { | |
static const char _aV2C[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; | |
unsigned Div; | |
unsigned Digit = 1; | |
unsigned Number; | |
unsigned Width; | |
char c; | |
Number = v; | |
// | |
// Get actual field width | |
// | |
Width = 1; | |
while (Number >= Base) { | |
Number = (Number / Base); | |
Width++; | |
} | |
if ((unsigned)NumDigits > Width) { | |
Width = NumDigits; | |
} | |
// | |
// Print leading chars if necessary | |
// | |
if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0) { | |
if (FieldWidth != 0) { | |
if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && (NumDigits == 0)) { | |
c = '0'; | |
} else { | |
c = ' '; | |
} | |
while ((FieldWidth != 0) && (Width < FieldWidth--)) { | |
_StoreChar(pBufferDesc, c); | |
if (pBufferDesc->ReturnValue < 0) { | |
return; | |
} | |
} | |
} | |
} | |
// | |
// Count how many digits are required by precision | |
// | |
while (((v / Digit) >= Base) | (NumDigits-- > 1)) { | |
Digit *= Base; | |
} | |
// | |
// Output digits | |
// | |
do { | |
Div = v / Digit; | |
v -= Div * Digit; | |
_StoreChar(pBufferDesc, _aV2C[Div]); | |
if (pBufferDesc->ReturnValue < 0) { | |
break; | |
} | |
Digit /= Base; | |
} while (Digit); | |
// | |
// Print trailing spaces if necessary | |
// | |
if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == FORMAT_FLAG_LEFT_JUSTIFY) { | |
if (FieldWidth != 0) { | |
while ((FieldWidth != 0) && (Width < FieldWidth--)) { | |
_StoreChar(pBufferDesc, ' '); | |
if (pBufferDesc->ReturnValue < 0) { | |
return; | |
} | |
} | |
} | |
} | |
} | |
/********************************************************************* | |
* | |
* _PrintInt | |
*/ | |
static void _PrintInt(SEGGER_RTT_PRINTF_DESC * pBufferDesc, int v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) { | |
unsigned Width; | |
unsigned Number; | |
Number = (v < 0) ? -v : v; | |
// | |
// Get actual field width | |
// | |
Width = 1; | |
while (Number >= Base) { | |
Number = (Number / Base); | |
Width++; | |
} | |
if (NumDigits > Width) { | |
Width = NumDigits; | |
} | |
if ((FieldWidth > 0) && ((v < 0) || ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN))) { | |
FieldWidth--; | |
} | |
// | |
// Print leading spaces if necessary | |
// | |
if ((((FormatFlags & FORMAT_FLAG_PAD_ZERO) == 0) || (NumDigits != 0)) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0)) { | |
if (FieldWidth != 0) { | |
while ((FieldWidth != 0) && (Width < FieldWidth--)) { | |
_StoreChar(pBufferDesc, ' '); | |
if (pBufferDesc->ReturnValue < 0) { | |
return; | |
} | |
} | |
} | |
} | |
// | |
// Print sign if necessary | |
// | |
if (v < 0) { | |
v = -v; | |
_StoreChar(pBufferDesc, '-'); | |
if (pBufferDesc->ReturnValue < 0) { | |
return; | |
} | |
} else if ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN) { | |
_StoreChar(pBufferDesc, '+'); | |
if (pBufferDesc->ReturnValue < 0) { | |
return; | |
} | |
} | |
// | |
// Print leading zeros if necessary | |
// | |
if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0) && (NumDigits == 0)) { | |
if (FieldWidth != 0) { | |
while ((FieldWidth != 0) && (Width < FieldWidth--)) { | |
_StoreChar(pBufferDesc, '0'); | |
if (pBufferDesc->ReturnValue < 0) { | |
return; | |
} | |
} | |
} | |
} | |
// | |
// Print number without sign | |
// | |
_PrintUnsigned(pBufferDesc, v, Base, NumDigits, FieldWidth, FormatFlags); | |
} | |
/********************************************************************* | |
* | |
* Public code | |
* | |
********************************************************************** | |
*/ | |
/********************************************************************* | |
* | |
* SEGGER_RTT_vprintf | |
* | |
* Function description | |
* Stores a formatted string in SEGGER RTT control block. | |
* This data is read by the host. | |
* | |
* Parameters | |
* BufferIndex Index of "Up"-buffer to be used. (e.g. 0 for "Terminal") | |
* sFormat Pointer to format string | |
* pParamList Pointer to the list of arguments for the format string | |
* | |
* Return values | |
* >= 0: Number of bytes which have been stored in the "Up"-buffer. | |
* < 0: Error | |
*/ | |
int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList) { | |
char c; | |
SEGGER_RTT_PRINTF_DESC BufferDesc; | |
int v; | |
unsigned NumDigits; | |
unsigned FormatFlags; | |
unsigned FieldWidth; | |
char acBuffer[SEGGER_RTT_PRINTF_BUFFER_SIZE]; | |
BufferDesc.pBuffer = acBuffer; | |
BufferDesc.BufferSize = SEGGER_RTT_PRINTF_BUFFER_SIZE; | |
BufferDesc.Cnt = 0; | |
BufferDesc.RTTBufferIndex = BufferIndex; | |
BufferDesc.ReturnValue = 0; | |
do { | |
c = *sFormat++; | |
if (c == 0) { | |
break; | |
} | |
if (c == '%') { | |
// | |
// Filter out flags | |
// | |
FormatFlags = 0; | |
do { | |
c = *sFormat; | |
switch (c) { | |
case '-': FormatFlags |= FORMAT_FLAG_LEFT_JUSTIFY; sFormat++; break; | |
case '0': FormatFlags |= FORMAT_FLAG_PAD_ZERO; sFormat++; break; | |
case '+': FormatFlags |= FORMAT_FLAG_PRINT_SIGN; sFormat++; break; | |
case '#': FormatFlags |= FORMAT_FLAG_ALTERNATE; sFormat++; break; | |
default: goto FilterFieldWidth; break; | |
} | |
} while (1); | |
// | |
// filter out field with | |
// | |
FilterFieldWidth: | |
FieldWidth = 0; | |
do { | |
c = *sFormat; | |
if (c < '0' || c > '9') { | |
break; | |
} | |
sFormat++; | |
FieldWidth = FieldWidth * 10 + (c - '0'); | |
} while (1); | |
// | |
// Filter out precision (number of digits to display) | |
// | |
NumDigits = 0; | |
c = *sFormat; | |
if (c == '.') { | |
sFormat++; | |
do { | |
c = *sFormat; | |
if (c < '0' || c > '9') { | |
break; | |
} | |
sFormat++; | |
NumDigits = NumDigits * 10 + (c - '0'); | |
} while (1); | |
} | |
// | |
// Filter out length modifier | |
// | |
c = *sFormat; | |
do { | |
if (c == 'l' || c == 'h') { | |
c = *sFormat++; | |
continue; | |
} | |
break; | |
} while (1); | |
// | |
// Handle specifiers | |
// | |
switch (c) { | |
case 'c': { | |
char c0; | |
v = va_arg(*pParamList, int); | |
c0 = (char)v; | |
_StoreChar(&BufferDesc, c0); | |
break; | |
} | |
case 'd': | |
v = va_arg(*pParamList, int); | |
_PrintInt(&BufferDesc, v, 10, NumDigits, FieldWidth, FormatFlags); | |
break; | |
case 'u': | |
v = va_arg(*pParamList, int); | |
_PrintUnsigned(&BufferDesc, v, 10, NumDigits, FieldWidth, FormatFlags); | |
break; | |
case 'x': | |
case 'X': | |
v = va_arg(*pParamList, int); | |
_PrintUnsigned(&BufferDesc, v, 16, NumDigits, FieldWidth, FormatFlags); | |
break; | |
case 's': | |
{ | |
const char * s = va_arg(*pParamList, const char *); | |
do { | |
c = *s++; | |
if (c == 0) { | |
break; | |
} | |
_StoreChar(&BufferDesc, c); | |
} while (BufferDesc.ReturnValue >= 0); | |
} | |
break; | |
case 'p': | |
v = va_arg(*pParamList, int); | |
_PrintUnsigned(&BufferDesc, v, 16, 8, 8, 0); | |
break; | |
case '%': | |
_StoreChar(&BufferDesc, '%'); | |
break; | |
} | |
sFormat++; | |
} else { | |
_StoreChar(&BufferDesc, c); | |
} | |
} while (BufferDesc.ReturnValue >= 0); | |
if (BufferDesc.ReturnValue > 0) { | |
// | |
// Write remaining data, if any | |
// | |
if (BufferDesc.Cnt != 0) { | |
SEGGER_RTT_Write(BufferIndex, acBuffer, BufferDesc.Cnt); | |
} | |
BufferDesc.ReturnValue += BufferDesc.Cnt; | |
} | |
return BufferDesc.ReturnValue; | |
} | |
/********************************************************************* | |
* | |
* SEGGER_RTT_printf | |
* | |
* Function description | |
* Stores a formatted string in SEGGER RTT control block. | |
* This data is read by the host. | |
* | |
* Parameters | |
* BufferIndex Index of "Up"-buffer to be used. (e.g. 0 for "Terminal") | |
* sFormat Pointer to format string, followed by the arguments for conversion | |
* | |
* Return values | |
* >= 0: Number of bytes which have been stored in the "Up"-buffer. | |
* < 0: Error | |
* | |
* Notes | |
* (1) Conversion specifications have following syntax: | |
* %[flags][FieldWidth][.Precision]ConversionSpecifier | |
* (2) Supported flags: | |
* -: Left justify within the field width | |
* +: Always print sign extension for signed conversions | |
* 0: Pad with 0 instead of spaces. Ignored when using '-'-flag or precision | |
* Supported conversion specifiers: | |
* c: Print the argument as one char | |
* d: Print the argument as a signed integer | |
* u: Print the argument as an unsigned integer | |
* x: Print the argument as an hexadecimal integer | |
* s: Print the string pointed to by the argument | |
* p: Print the argument as an 8-digit hexadecimal integer. (Argument shall be a pointer to void.) | |
*/ | |
int SEGGER_RTT_printf(unsigned BufferIndex, const char * sFormat, ...) { | |
va_list ParamList; | |
va_start(ParamList, sFormat); | |
return SEGGER_RTT_vprintf(BufferIndex, sFormat, &ParamList); | |
} | |
/*************************** End of file ****************************/ |