打印
[方案相关]

对printf和scanf的实现

[复制链接]
1387|29
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
deliahouse887|  楼主 | 2023-12-23 14:42 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
1.printf函数在格式化输出时,向下调用了char putchar(char c);这个函数,在“stdio.h”里可以发现有这个函数,所以我们需要自己构造一个这样的函数,即通过串口putchar(),代码如下:

char putchar(char c)  

{  

    hal_uart_putchar(c);  

    return c;  

}  

        其中hal_uart_putchar(c);函数是我们比较熟悉的了,是51单片机通过串口发送一个字节的函数,具体代码如下:

void hal_uart_putchar(char i)  

{  



}  

        有了这两个函数,在单片机启动后,首先进行串口初始化,接着就可以使用printf了……是不是很简单……

-------------------------------------------------------------------------------------------------------------------------------------

        2.下面再看scanf的具体实现方法:

        scanf函数在格式化输入时,向下调用了char getkey(void);这个函数,在“stdio.h”里可以发现有这个函数,所以我们需要自己构造一个这样的函数,即通过串口getkey(),代码如下:

char _getkey (void)   

{  

    return hal_uart_getchar();  

}  

其中hal_uart_getchar(); 稍稍复杂,但也很好理解,代码如下:

char hal_uart_getchar(void)  

{  

    uchar ch;  

    //Wait until a character is available:   

    while(uart_rx_cnt == 0);  

    ES = 0;  

    ch = uart_rx[uart_rx_rp];  

    uart_rx_rp = (uart_rx_rp + 1) % UART_BUF_SIZE;  

    uart_rx_cnt--;  

    ES = 1;  

    return ch;  

}  

这个函数是从串口接收队列中取出队尾的一个字节。uart_rx_cnt 表示现在串口队列中的已有字节数,uart_rx_rp 指向队尾。

最后要介绍的一个函数是串口接收中断函数,代码如下:

void UART1InterruptReceive(void) interrupt 4  

{  

    ES=0;//关串行口中断   

    if(RI)  

    {  

        RI=0;//接收中断信号清零,表示将继续接收   

        if(uart_rx_cnt < UART_BUF_SIZE)  

        {  

            uart_rx[uart_rx_wp] = SBUF;  

            uart_rx_wp = (uart_rx_wp + 1) % UART_BUF_SIZE;  

            uart_rx_cnt++;  

        }  

    }   

    ES=1;//开串行口中断   

}  

该函数实现了串口的中断接收,收到的新的字节存放在队首,即uart_rx_wp指向队列的首地址,每次收到一个新的字节,uart_rx_cnt增1。

至此,scanf函数也可以实现了。

使用特权

评论回复
沙发
华大MCU代理| | 2023-12-25 10:12 | 只看该作者
感谢分享

使用特权

评论回复
板凳
xdvca| | 2024-7-31 22:46 | 只看该作者
你的描述详细地介绍了如何实现printf和scanf函数,通过UART在51单片机上进行串口通信。这是一个很好的例子,如何使用自定义的putchar和getkey函数,配合UART中断实现输入输出。

使用特权

评论回复
地板
sdlls| | 2024-8-4 13:20 | 只看该作者
在单片机中实现printf和scanf函数通常涉及到对标准输入输出库的替换或者自定义实现,因为单片机的资源有限,尤其是内存和存储空间,所以不能直接使用像PC上的标准库那样庞大的代码。

使用特权

评论回复
5
deliahouse887|  楼主 | 2024-8-4 16:50 | 只看该作者
标准的scanf函数是从标准输入设备(通常是键盘)接收输入,但在单片机环境中,需要将scanf函数的输入重定向到串行通信接口。这通常通过定义一个新的_read函数来实现,该函数从串行通信接口读取字符。

使用特权

评论回复
6
gouguoccc| | 2024-8-4 21:41 | 只看该作者
在单片机中用scanf函数意义不大

使用特权

评论回复
7
suncat0504| | 2024-8-5 08:47 | 只看该作者
很实用的知识。调试时,基本都用串口输出信息。

使用特权

评论回复
8
adolphcocker| | 2024-8-5 11:00 | 只看该作者
内存使用:由于单片机内存有限,需要优化内存使用,避免内存溢出。
缓冲区:printf和scanf通常使用缓冲区来提高效率。需要实现缓冲区管理,并在必要时刷新缓冲区。
硬件限制:需要考虑单片机的硬件限制,如波特率、I/O口数量等。

使用特权

评论回复
9
cemaj| | 2024-8-5 12:14 | 只看该作者
printf函数通常用于输出格式化的数据到串口。为了在单片机中使用printf,需要重定向标准输出流stdout到串口。这通常涉及到重定义fputc函数,因为printf底层是通过调用fputc来输出每个字符的。

使用特权

评论回复
10
everyrobin| | 2024-8-5 16:13 | 只看该作者
这两个函数的实现通常依赖于硬件支持的串行通信接口

使用特权

评论回复
11
chenci2013| | 2024-8-5 18:08 | 只看该作者
由于单片机的内存限制,需要对printf函数的代码进行优化,去除不必要的功能和冗余代码。

使用特权

评论回复
12
hilahope| | 2024-8-5 20:27 | 只看该作者
#include "stm32f1xx_hal.h"

// 输出函数,用于发送字符到USART
void USART_PutChar(char c)
{
    HAL_StatusTypeDef status;

    status = HAL_UART_Transmit(&huart1, (uint8_t*)&c, 1, HAL_MAX_DELAY);
    if (status != HAL_OK)
    {
        // 错误处理
    }
}

// 输出字符串到USART
void USART_Puts(const char *str)
{
    while (*str)
    {
        USART_PutChar(*str++);
    }
}

// 自定义printf函数
int printf(const char *format, ...)
{
    va_list args;
    char buffer[128]; // 假设输出缓冲区大小
    int len = 0;

    va_start(args, format);
    len = vsnprintf(buffer, sizeof(buffer), format, args);
    va_end(args);

    USART_Puts(buffer);
    return len;
}

使用特权

评论回复
13
beacherblack| | 2024-8-5 22:14 | 只看该作者
// 输入函数,用于从USART接收字符
char USART_GetChar(void)
{
    char c;
    HAL_StatusTypeDef status;

    status = HAL_UART_Receive(&huart1, (uint8_t*)&c, 1, HAL_MAX_DELAY);
    if (status != HAL_OK)
    {
        // 错误处理
    }
    return c;
}

// 自定义scanf函数
int scanf(const char *format, ...)
{
    va_list args;
    char input[128]; // 假设输入缓冲区大小
    int len = 0;
    int pos = 0;

    va_start(args, format);

    while (*format && pos < sizeof(input) - 1)
    {
        if (*format == '%')
        {
            format++; // 跳过'%'
            switch (*format)
            {
                case 'd':
                    // 读取整数
                    while (isdigit((c = USART_GetChar())))
                    {
                        input[pos++] = c;
                    }
                    sscanf(input, "%d", va_arg(args, int *));
                    break;
                case 's':
                    // 读取字符串
                    while ((c = USART_GetChar()) != '\n' && c != EOF)
                    {
                        input[pos++] = c;
                    }
                    input[pos] = '\0';
                    va_arg(args, char *);
                    break;
                default:
                    // 忽略未知格式符
                    break;
            }
        }
        else
        {
            // 普通字符
            input[pos++] = USART_GetChar();
        }
        format++;
    }

    input[pos] = '\0';
    va_end(args);

    return pos;
}

使用特权

评论回复
14
mattlincoln| | 2024-8-6 11:41 | 只看该作者
#include <stdio.h>
#include <stdarg.h>

// 假设UART_HandleTypeDef huart1是你的串口句柄
extern UART_HandleTypeDef huart1;

// 重定义fputc函数
int fputc(int ch, FILE *f) {
    // 将字符通过串口发送出去
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
    return ch;
}

int main(void) {
    // 初始化串口
    // ...

    // 使用printf输出信息
    printf("Hello, World!\r\n");

    while (1) {
        // 主循环
    }
}

使用特权

评论回复
15
ulystronglll| | 2024-8-6 13:17 | 只看该作者
#include <stdarg.h>

// 发送一个字符到指定的输出设备(例如串口)
void uart_putchar(char c) {
    // 在这里实现将字符发送到串口的代码
}

// 简化版的 printf 函数
void my_printf(const char *format,...) {
    va_list args;
    va_start(args, format);

    while (*format) {
        if (*format == '%') {
            format++;
            switch (*format) {
                case 'd':  // 整数
                {
                    int num = va_arg(args, int);
                    char str[20];
                    int len = sprintf(str, "%d", num);
                    for (int i = 0; i < len; i++) {
                        uart_putchar(str[i]);
                    }
                    break;
                }
                case 's':  // 字符串
                {
                    char *str = va_arg(args, char *);
                    while (*str) {
                        uart_putchar(*str++);
                    }
                    break;
                }
                // 可以根据需要添加更多的格式支持
            }
        } else {
            uart_putchar(*format);
        }
        format++;
    }

    va_end(args);
}

int main() {
    my_printf("Hello, %d!\n", 123);
    return 0;
}

使用特权

评论回复
16
plsbackup| | 2024-8-6 15:45 | 只看该作者
将printf函数的输出重定向到单片机的某个外设,比如UART(串口),这样可以通过串口将数据发送到PC或其他设备。

使用特权

评论回复
17
linfelix| | 2024-8-6 17:22 | 只看该作者
#include <stdarg.h>
#include "uart.h" // 假设有一个UART库用于发送数据

void my_putchar(char c) {
    uart_send_byte(c); // 将字符发送到UART
}

void my_printf(const char *format, ...) {
    va_list args;
    va_start(args, format);

    while (*format) {
        if (*format == '%') {
            format++;
            switch (*format) {
                case 's': {
                    char *str = va_arg(args, char*);
                    while (*str) {
                        my_putchar(*str++);
                    }
                    break;
                }
                case 'c': {
                    char c = (char)va_arg(args, int);
                    my_putchar(c);
                    break;
                }
                default:
                    my_putchar('%');
                    my_putchar(*format);
                    break;
            }
        } else {
            my_putchar(*format);
        }
        format++;
    }

    va_end(args);
}

使用特权

评论回复
18
mnynt121| | 2024-8-6 19:50 | 只看该作者
通常不直接提供标准C库中的printf和scanf函数。

使用特权

评论回复
19
janewood| | 2024-8-6 21:33 | 只看该作者
printf和scanf函数的实现可能需要占用较多的内存和CPU资源,因此在资源受限的单片机上使用时需要谨慎。

使用特权

评论回复
20
pixhw| | 2024-8-8 09:48 | 只看该作者
在嵌入式系统中,特别是单片机环境中,并没有标准的控制台,因此需要对这些函数进行特殊的实现,以便通过串口或其他通信接口与外部设备进行交互。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

29

主题

1199

帖子

0

粉丝