你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

一种基于STM32 指定串口 自定义printf 的方法

[复制链接]
小白云 提问时间:2023-6-27 15:44 / 已解决
#include <stdio.h>
#include <stdarg.h>
void UART_Printf(uint8_t index,const char* format, ...)
{
    va_list args;
    va_start(args, format);
    int len = vsnprintf(NULL, 0, format, args);
    va_end(args);

    char* buffer = (char*)malloc(len + 1);
    va_start(args, format);
    vsnprintf(buffer, len + 1, format, args);
    va_end(args);

                //多出一个结束符'\0',可以不用打印
    UART_Send(index,( uint8_t *) buffer,len );
    free(buffer);
}

这个自定义printf函数的实现过程如下:

  1. 首先,我们使用可变参数列表va_list类型的变量args,以及va_start函数来访问传递给myprintf函数的可变参数列表。
  2. 然后,我们使用vsnprintf函数来计算格式化字符串的长度。vsnprintf函数会将格式化字符串格式化后的结果存储到一个缓冲区中,但是我们并不需要这个缓冲区的实际内容,所以将第一个参数设置为NULL,这样vsnprintf函数只会返回格式化字符串的长度。
  3. 接着,我们使用malloc函数为缓冲区分配足够的内存。注意,由于vsnprintf函数返回的长度不包括字符串结尾的空字符'\0',所以我们需要在长度上加1。
  4. 然后,我们再次使用va_start函数来访问可变参数列表,并使用vsnprintf函数将格式化后的字符串存储到缓冲区中。
  5. 最后,我们使用UART_Send函数将缓冲区中的内容输出到指定串口中,并使用free函数释放缓冲区的内存。

需要注意的是,这只是一个简单的自定义printf函数的示例,实际中可能需要更多的错误检查和处理,以及更复杂的格式化字符串的处理。

vsnprintf函数说明:

vsnprintf是C标准库中的一个函数,它类似于sprintf函数,可以将格式化字符串格式化成一串字符并存储到一个缓冲区中。vsnprintf的一个重要特点是它允许指定一个缓冲区的大小,以避免缓冲区溢出的问题。

vsnprintf函数的函数原型如下:

int vsnprintf(char* str, size_t size, const char* format, va_list ap);

其中,第一个参数str是一个指向缓冲区的指针,size是缓冲区的大小,format是一个字符串,用来指定输出的格式,ap是一个包含可变参数列表的va_list类型的变量。

vsnprintf函数会根据format参数指定的格式,将可变参数列表中的值转换成字符串,并将这些字符串存储到str指向的缓冲区中。如果str为NULL,则vsnprintf函数不会存储任何数据,但会计算出需要的缓冲区大小,并返回这个大小。

vsnprintf函数的返回值是一个int类型的值,表示实际写入缓冲区的字符数(不包括字符串结尾的空字符'\0'),或者是需要的缓冲区大小(如果str为NULL)。

需要注意的是,vsnprintf函数在向缓冲区写入数据时,会自动在输出的字符串末尾添加一个空字符'\0',因此在使用vsnprintf函数之前,必须确保缓冲区有足够的空间来存储这个空字符。

下面是一个使用vsnprintf函数的示例代码:

#include <stdio.h>
#include <stdarg.h>

int main()
{
    char buffer[100];
    int len = vsnprintf(buffer, 100, "The answer is %d.", 42);
    printf("vsnprintf returned %d\n", len);
    printf("buffer contains: %s\n", buffer);
    return 0;
}

在这个示例中,我们定义了一个大小为100的缓冲区,然后使用vsnprintf函数将格式化字符串"The answer is %d."格式化成一串字符,并存储到缓冲区中。由于这个字符串的长度为16,加上一个空字符'\0'一共需要17个字符的空间,而缓冲区的大小为100,因此这个操作不会导致缓冲区溢出。最后,我们输出vsnprintf函数的返回值和缓冲区的内容。

va_start是什么,什么是可变参数列表

va_start是C语言标准库中的一个宏定义,用于初始化一个指向可变参数列表的变量。在使用可变参数列表时,必须先使用va_start宏来初始化一个va_list类型的变量,这个变量包含了可变参数列表中的所有参数,以便后续的操作中使用。

可变参数列表是一种特殊的函数参数列表,它允许函数接受任意数量和任意类型的参数。

例如,我们可以编写一个函数,接受任意数量的整数作为参数,并返回它们的平均值:

#include <stdarg.h>

double average(int count, ...)
{
    va_list args;
    va_start(args, count);

    double sum = 0;
    for (int i = 0; i < count; i++) {
        int arg = va_arg(args, int);
        sum += arg;
    }

    va_end(args);

    return sum / count;
}

int main()
{
    double result = average(3, 1, 2, 3);
    printf("The average is %f\n", result);
    return 0;
}

在这个示例中,我们使用可变参数列表来接受任意数量的整数参数,并计算它们的平均值。在average函数中,我们首先使用va_start宏初始化一个va_list类型的变量args,然后使用for循环遍历前count个参数,并使用va_arg宏来从可变参数列表中依次读取参数。最后,我们使用va_end宏来清理va_list类型的变量args。

这个示例只是一个简单的例子,实际中可变参数列表常用于处理不同类型和数量的参数,例如printf函数。需要注意的是,使用可变参数列表时,必须始终提供一个固定参数,以便确定可变参数列表的起始地址。

收藏 1 评论6 发布时间:2023-6-27 15:44

举报

6个回答
小白云 最优答案 回答时间:2023-6-27 15:57:23

逸邦 发表于 2023-6-27 15:51
有没有可能直接重定义printf函数呢?

直接重定义的话,相对来说,不够灵活,我碰到的问题,主要在以下两点: 1.没有办法实现指定串口,只能固定某个串口使用printf 2.无法在打印前后,进行一些额外的处理。

举例①:例如RS485打印,如果不能在printf函数内部进行处理的话,我就需要这样写 RS485_TX_EN;

printf();

RS485RX_EN;

假如可以在函数内部处理的话,只需要写一个printf就可以了,相对来说,更方便。

举例② :在打印前后,添加字符前缀

例如,我需要根据打印信息进行分类,使用error_printf,默认在前面加上error: ,

Max.88888888 回答时间:2023-6-27 15:52:30
签到
逸邦 回答时间:2023-6-27 15:51:19

有没有可能直接重定义printf函数呢?

高迪的教堂 回答时间:2023-6-27 15:49:38
[md]
Max.88888888 回答时间:2023-6-28 09:53:29

签到

小白云 回答时间:2024-4-9 10:16:20

小白云 发表于 2023-6-27 15:57
直接重定义的话,相对来说,不够灵活,我碰到的问题,主要在以下两点:
1.没有办法实现指定串口,只 ...

[md]无法使用MicroLIB,会导致vsnprintf截断 无法使用int len = vsnprintf(NULL, 0, format, args);获取到字符长度。

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版