一、rt_show_version函数调用关系
二、rt_show_version函数分析
/**
* This function will show the version of rt-thread rtos
*/
void rt_show_version(void)
{
rt_kprintf("\n \\ | /\n");
rt_kprintf("- RT - Thread Operating System\n");
rt_kprintf(" / | \\ %d.%d.%d build %s\n",
RT_VERSION, RT_SUBVERSION, RT_REVISION, __DATE__);
rt_kprintf(" 2006 - 2018 Copyright by rt-thread team\n");
}
RTM_EXPORT(rt_show_version);
1、rt_kprintf
typedef unsigned long rt_ubase_t;
typedef rt_ubase_t rt_size_t;
#define RT_CONSOLEBUF_SIZE 128
/**
* This function will print a formatted string on system console
*
* @param fmt the format
*/
void rt_kprintf(const char *fmt, ...)
{
va_list args;
rt_size_t length;
static char rt_log_buf[RT_CONSOLEBUF_SIZE];
va_start(args, fmt);
/* the return value of vsnprintf is the number of bytes that would be
* written to buffer had if the size of the buffer been sufficiently
* large excluding the terminating null byte. If the output string
* would be larger than the rt_log_buf, we have to adjust the output
* length. */
length = rt_vsnprintf(rt_log_buf, sizeof(rt_log_buf) - 1, fmt, args);
if (length > RT_CONSOLEBUF_SIZE - 1)
length = RT_CONSOLEBUF_SIZE - 1;
#ifdef RT_USING_DEVICE
if (_console_device == RT_NULL)
{
rt_hw_console_output(rt_log_buf);
}
else
{
rt_uint16_t old_flag = _console_device->open_flag;
_console_device->open_flag |= RT_DEVICE_FLAG_STREAM;
rt_device_write(_console_device, 0, rt_log_buf, length);
_console_device->open_flag = old_flag;
}
#else
rt_hw_console_output(rt_log_buf);
#endif
va_end(args);
}
RTM_EXPORT(rt_kprintf);
2、stdarg.h
stdarg.h
是C语言中C标准函数库的标头文档,stdarg
是由stdandard
(标准) arguments
(参数)简化而来,主要目的为让函数能够接收不定量参数。
不定参数函数(Variadic functions)是stdarg.h内容典型的应用。
1、声明不定参数函数
不定参数函数的参数数量是可变动的,它使用省略号来忽略之后的参数。例如printf函数一般。代表性的声明为:
int check(int a, double b, ...);
不定参数函数最少要有一个命名的参数,所以
char *wrong(...);
在C是不被允许的(在C++中,这样的声明是合理的)。在C,省略符号之前必须要有逗号;在C++,则没有这种强制要求。
2、变量
va_list
这是一个适用于 va_start()、va_arg() 和 va_end() 这三个宏存储信息的类型。
名称 | 描述 | 标准 |
---|---|---|
va_list | 用来保存宏va_arg 与宏va_end 所需信息 | c89 |
3、宏
void va_start(va_list ap, last_arg)
这个宏初始化 ap 变量,它与 va_arg 和 va_end 宏是一起使用的。last_arg 是最后一个传递给函数的已知的固定参数,即省略号之前的参数。
type va_arg(va_list ap, type)
这个宏检索函数参数列表中类型为 type 的下一个参数。
void va_end(va_list ap)
这个宏允许使用了 va_start 宏的带有可变参数的函数返回。如果在从函数返回之前没有调用 va_end,则结果为未定义。
名称 | 描述 | 标准 |
---|---|---|
va_start | 使va_list 指向起始的参数 | C89 |
va_arg | 检索参数 | C89 |
va_end | 释放va_list | C89 |
3、rt_vsnprintf
rt_int32_t rt_vsnprintf(char *buf,
rt_size_t size,
const char *fmt,
va_list args)
{
#ifdef RT_PRINTF_LONGLONG
unsigned long long num;
#else
rt_uint32_t num;
#endif
int i, len;
char *str, *end, c;
const char *s;
rt_uint8_t base; /* the base of number */
rt_uint8_t flags; /* flags to print number */
rt_uint8_t qualifier; /* 'h', 'l', or 'L' for integer fields */
rt_int32_t field_width; /* width of output field */
#ifdef RT_PRINTF_PRECISION
int precision; /* min. # of digits for integers and max for a string */
#endif
str = buf;
end = buf + size - 1;
/* Make sure end is always >= buf */
if (end < buf)
{
end = ((char *) - 1);
size = end - buf;
}
for (; *fmt ; ++fmt)
{
if (*fmt != '%')
{
if (str <= end)
*str = *fmt;
++ str;
continue;
}
/* process flags */
flags = 0;
while (1)
{
/* skips the first '%' also */
++ fmt;
if (*fmt == '-') flags |= LEFT;
else if (*fmt == '+') flags |= PLUS;
else if (*fmt == ' ') flags |= SPACE;
else if (*fmt == '#') flags |= SPECIAL;
else if (*fmt == '0') flags |= ZEROPAD;
else break;
}
/* get field width */
field_width = -1;
if (isdigit(*fmt)) field_width = skip_atoi(&fmt);
else if (*fmt == '*')
{
++ fmt;
/* it's the next argument */
field_width = va_arg(args, int);
if (field_width < 0)
{
field_width = -field_width;
flags |= LEFT;
}
}
#ifdef RT_PRINTF_PRECISION
/* get the precision */
precision = -1;
if (*fmt == '.')
{
++ fmt;
if (isdigit(*fmt)) precision = skip_atoi(&fmt);
else if (*fmt == '*')
{
++ fmt;
/* it's the next argument */
precision = va_arg(args, int);
}
if (precision < 0) precision = 0;
}
#endif
/* get the conversion qualifier */
qualifier = 0;
#ifdef RT_PRINTF_LONGLONG
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L')
#else
if (*fmt == 'h' || *fmt == 'l')
#endif
{
qualifier = *fmt;
++ fmt;
#ifdef RT_PRINTF_LONGLONG
if (qualifier == 'l' && *fmt == 'l')
{
qualifier = 'L';
++ fmt;
}
#endif
}
/* the default base */
base = 10;
switch (*fmt)
{
case 'c':
if (!(flags & LEFT))
{
while (--field_width > 0)
{
if (str <= end) *str = ' ';
++ str;
}
}
/* get character */
c = (rt_uint8_t)va_arg(args, int);
if (str <= end) *str = c;
++ str;
/* put width */
while (--field_width > 0)
{
if (str <= end) *str = ' ';
++ str;
}
continue;
case 's':
s = va_arg(args, char *);
if (!s) s = "(NULL)";
len = rt_strlen(s);
#ifdef RT_PRINTF_PRECISION
if (precision > 0 && len > precision) len = precision;
#endif
if (!(flags & LEFT))
{
while (len < field_width--)
{
if (str <= end) *str = ' ';
++ str;
}
}
for (i = 0; i < len; ++i)
{
if (str <= end) *str = *s;
++ str;
++ s;
}
while (len < field_width--)
{
if (str <= end) *str = ' ';
++ str;
}
continue;
case 'p':
if (field_width == -1)
{
field_width = sizeof(void *) << 1;
flags |= ZEROPAD;
}
#ifdef RT_PRINTF_PRECISION
str = print_number(str, end,
(long)va_arg(args, void *),
16, field_width, precision, flags);
#else
str = print_number(str, end,
(long)va_arg(args, void *),
16, field_width, flags);
#endif
continue;
case '%':
if (str <= end) *str = '%';
++ str;
continue;
/* integer number formats - set up the flags and "break" */
case 'o':
base = 8;
break;
case 'X':
flags |= LARGE;
case 'x':
base = 16;
break;
case 'd':
case 'i':
flags |= SIGN;
case 'u':
break;
default:
if (str <= end) *str = '%';
++ str;
if (*fmt)
{
if (str <= end) *str = *fmt;
++ str;
}
else
{
-- fmt;
}
continue;
}
#ifdef RT_PRINTF_LONGLONG
if (qualifier == 'L') num = va_arg(args, long long);
else if (qualifier == 'l')
#else
if (qualifier == 'l')
#endif
{
num = va_arg(args, rt_uint32_t);
if (flags & SIGN) num = (rt_int32_t)num;
}
else if (qualifier == 'h')
{
num = (rt_uint16_t)va_arg(args, rt_int32_t);
if (flags & SIGN) num = (rt_int16_t)num;
}
else
{
num = va_arg(args, rt_uint32_t);
if (flags & SIGN) num = (rt_int32_t)num;
}
#ifdef RT_PRINTF_PRECISION
str = print_number(str, end, num, base, field_width, precision, flags);
#else
str = print_number(str, end, num, base, field_width, flags);
#endif
}
if (str <= end) *str = '\0';
else *end = '\0';
/* the trailing null byte doesn't count towards the total
* ++str;
*/
return str - buf;
}
RTM_EXPORT(rt_vsnprintf);
和C语言标准库中vfprintf
函数功能相同。
1、vfprintf
int vfprintf(FILE *stream, const char *format, va_list arg)
将参数列表发送格式化输出到流 stream 中
4、rt_hw_console_output
RT_WEAK void rt_hw_console_output(const char *str)
{
/* empty console output */
}
RTM_EXPORT(rt_hw_console_output);
5、rt_device_write
rt_size_t rt_device_write(rt_device_t dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
{
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
if (dev->ref_count == 0)
{
rt_set_errno(-RT_ERROR);
return 0;
}
/* call device write interface */
if (device_write != RT_NULL)
{
return device_write(dev, pos, buffer, size);
}
/* set error code */
rt_set_errno(-RT_ENOSYS);
return 0;
}
串口设备中,device_write
保存rt_serial_write
函数。
1、rt_serial_write
static rt_size_t rt_serial_write(struct rt_device *dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
{
struct rt_serial_device *serial;
RT_ASSERT(dev != RT_NULL);
if (size == 0) return 0;
serial = (struct rt_serial_device *)dev;
return _serial_poll_tx(serial, buffer, size);
}
2、_serial_poll_tx
rt_inline int _serial_poll_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length)
{
int size;
RT_ASSERT(serial != RT_NULL);
size = length;
while (length)
{
/*
* to be polite with serial console add a line feed
* to the carriage return character
*/
if (*data == '\n' && (serial->parent.open_flag & RT_DEVICE_FLAG_STREAM))
{
serial->ops->putc(serial, '\r');
}
serial->ops->putc(serial, *data);
++ data;
-- length;
}
return size - length;
}
3、stm32_putc
static int stm32_putc(struct rt_serial_device *serial, char c)
{
struct stm32_uart *uart;
RT_ASSERT(serial != RT_NULL);
uart = (struct stm32_uart *)serial->parent.user_data;
while (__HAL_UART_GET_FLAG(&uart->huart, UART_FLAG_TXE) == RESET);
uart->huart.Instance->DR = c;
return 1;
}
stm32_putc
函数将数据写入到串口的DR
寄存器中。