Como escrever uma função variádica? Como fazer com que todas as portas seriais de todos os microcontroladores realizem a função printf?

prefácio

(1) Já que eu realmente não podia continuar revisando, pensei em escrever um blog para puxar minha mente. Então pensei na função de parâmetro variável da linguagem C que eu tinha dúvidas há muito tempo, mas não tinha um entendimento aprofundado.
(2) Consultei alguns materiais online e escrevi este blog com base em meu próprio entendimento. Em primeiro lugar, obrigado pela sua resposta.
(3) Consulte o artigo:
Como realizar o log de saída printf no microcontrolador C51? ;
Funções de parâmetros variáveis ​​em linguagem C ;
MSP430F5529 DriverLib função da biblioteca notas de estudo (4.5) printf printout ;

O que é um parâmetro variável

(1) Primeiro, precisamos saber quais são os parâmetros variáveis. Quando escrevemos programas todos os dias, todos os parâmetros são especificados em número. do seguinte modo

void Add(int a,int b);

(2) Mas já pensamos em uma pergunta, por que printf () pode passar em vários parâmetros?

printf("hello world");
printf("hello %s","world");
printf("%s %s","hello","world");
printf("%d + %d = %d",2,3,5);

(3) Neste momento, damos uma olhada na definição da função printf(). Observamos a definição da função printf() da seguinte maneira e descobrimos que seu segundo parâmetro é "...", então o que é esse "..."? Este "..." mostra que esta função é uma função de parâmetro variável.

int printf(char *fmt, ...);

Como escrever uma função variádica

Saiba mais sobre três macros

(1) Antes de escrever funções de parâmetros variáveis, precisamos conhecer três macros. Em relação à introdução dessas três macros, os links que dei acima já possuem explicações bem profissionais. Se você quiser saber sua explicação profissional, consulte o link fornecido acima. Usarei apenas vernáculo muito vulgar para explicar as funções das três macros.
(2)
<1> Primeiro, se estivermos em uma função, precisamos começar a lidar com parâmetros variáveis. Então você precisa chamar a macro va_start() no início e usar a macro va_end() quando terminar de usar parâmetros variáveis.
<2> Este processo é um pouco semelhante ao gerenciamento dinâmico de memória. Se você precisar solicitar uma memória, chame a função malloc(). Se não precisar usar essa memória, chame a função free().
(3)
<1> Então, qual é a utilidade da macro va_arg()? Simplesmente, analisando varargs .
<2> Por exemplo: Por exemplo, printf("%s %d %s", "hello", 5, "world"); a primeira vez que va_arg é chamado, então é o parâmetro "hello" usado. A segunda chamada para va_arg é o número 5 usado. A terceira chamada para va_arg usa o argumento "world".

va_start( va_list arg_ptr, prev_param )
va_arg( va_list arg_ptr, type )
va_end( va_list arg_ptr )

Como essas três macros devem ser usadas

(1) Depois de conhecer as funções gerais dessas três macros, como elas devem ser usadas? Deixe-me primeiro explicar o código gerado pelo chatgpt.
(2) A função sum() é muito simples, é somar alguns dados de ponto flutuante. A primeira contagem de parâmetros é para informar à função quantos parâmetros variáveis ​​existem.
(3)
<1> Primeiro explique a macro va_start(), porque vamos usar parâmetros variáveis. Então, para chamar essa macro.
<2> O primeiro parâmetro é um parâmetro do tipo va_list por padrão. Não há mais nada a dizer, é assim mesmo. Se você insiste em perguntar por que, veja você mesmo a implementação subjacente. Estou apenas explicando como usá-lo aqui.
<3> O segundo parâmetro é o último parâmetro fixo. Alguém pode perguntar, o que significa o último parâmetro fixo? Pegue a função sum() aqui como exemplo, essa função sempre passará em contagem, então contagem é o último parâmetro fixo.
E se uma definição de função for void UART_printf(uint32_t baseAddress, const char format,…)? É muito simples, pois os dois parâmetros baseAddress e format serão fixos , então o segundo parâmetro de va_start() é passado em *format.
(4)
<1> Agora comece a explicar a macro va_arg().
<2>O primeiro parâmetro da macro va_arg() é o mesmo de va_start(), que é um dado do tipo va_list.
<3> O segundo parâmetro de va_arg() precisa ser determinado de acordo com o tipo de dados do parâmetro variável. Por exemplo, os parâmetros variáveis ​​aqui são todos dados de ponto flutuante, então o segundo parâmetro é double. Se o parâmetro variável aqui for passado em dados inteiros, o segundo parâmetro será int. Se você não entender, não entre em pânico quando eu analisar o código do irmão Ken mais tarde. Haverá também uma explicação.
<4>va_arg() finalmente retorna os parâmetros das variáveis.
(5) A macro va_end() também é muito simples de usar, é a mesma do primeiro parâmetro de va_start() e va_arg(), um dado do tipo va_list.

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

double sum(int count, ...)
{
    
    
    double total = 0.0;
    va_list args;
    va_start(args, count);
    
    for (int i = 0; i < count; i++) {
    
    
        double num = va_arg(args, double);
        total += num;
    }
    
    va_end(args);
    
    return total;
}

int main()
{
    
    
    double result = sum(5, 1.2, 2.3, 3.4, 4.5, 5.6);
    printf("Sum: %f\n", result);
    
    return 0;
}

Analise o código de Kenge

(1) Depois de ler a explicação acima, deve haver muitas pessoas que não entenderam. Tudo bem, não entre em pânico, analisarei o código de Ken aqui para aprofundar sua compreensão dos parâmetros variáveis.
(2) Vale ressaltar que explicarei apenas a parte referente aos parâmetros variáveis ​​no código do Ken.
(3)
<1>Porque neste código do irmão Ken, o último parâmetro fixo é fmt, então o segundo parâmetro de va_start() é passado para fmt.
<2> Temos alguma dúvida, por que no código do irmão Ken, não há necessidade de passar parâmetros para dizer ao programa como o código gerado pelo chatgpt, quantos parâmetros variáveis ​​existem agora?
Instrução while (*fmt) muito simples, fmt deve ser uma string. O que o último byte da string armazena? 0, para que possa ser percorrido até 0, então o caractere termina. E neste caractere, quantos símbolos "%" existem, então quantos parâmetros variáveis ​​existem.
<3> Se % for percorrido, deixe Fill_Flag = 1; e então comece a processar os parâmetros das variáveis.
Como estipulamos que %s é uma string, então va_arg(ap, char *); escreva assim. Ao mesmo tempo, use os dados do tipo char * de Str para receber o valor de retorno.
E %d e %x são dados inteiros, então va_arg(ap, int); escreva assim. Ao mesmo tempo, use os dados do tipo int para receber o valor de retorno.
<4> Por fim, o loop while detecta 0, indicando o fim da string. Chame va_end(); esta macro é processada.

int xprintf(char *fmt, ...)
{
    
    
    char *Str;
    int  Int_Data;
    uchar Fill_Flag = 0;

    va_list ap;
    va_start(ap, fmt);

    while(*fmt)
    {
    
    
        if ((*fmt != '%') && (Fill_Flag == 0))
        {
    
    
            Push_To_TX_Buffer(*fmt++);
            continue;
        }

        if (*fmt == '%')
        {
    
    
            fmt++;
            Align_Bit = 0;
            Fill_Flag = 1;
        }
        
        switch(*fmt)
        {
    
    
            case 's':
                 Str = va_arg(ap, char *);
                 for (; *Str; Str++)
                    Push_To_TX_Buffer(*Str);
                 Fill_Flag = 0;
                 Align_Bit = 0;
                 break;

            case 'd':
                 Int_Data = va_arg(ap, int);
                 IntToStr(Int_Data);
                 for (Str=Simple_Prn_Buf; *Str; Str++) {
    
    
                    Push_To_TX_Buffer(*Str);
                 }
                 Fill_Flag = 0;
                 Align_Bit = 0;
                 break;

            case 'x':
                 Int_Data = va_arg(ap, int);
                 HexToStr(Int_Data, 0); //小写
                 for (Str=Simple_Prn_Buf; *Str; Str++) {
    
    
                    Push_To_TX_Buffer(*Str);
                 }
                 Fill_Flag = 0;
                 Align_Bit = 0;
                 break;

            case 'X':
                 Int_Data = va_arg(ap, int);
                 HexToStr(Int_Data, 1); //大写
                 for (Str=Simple_Prn_Buf; *Str; Str++) {
    
    
                    Push_To_TX_Buffer(*Str);
                 }
                 Fill_Flag = 0;
                 Align_Bit = 0;
                 break;

            default:
                 //Push_To_TX_Buffer(*fmt);
                 Align_Bit = *fmt - '0';
                 if (Align_Bit > 9)
                    Align_Bit = 9;
                 break;
        }
        fmt++;
    }
    va_end(ap);

    return 0;
}
————————————————
版权声明:本文为CSDN博主「架构师李肯」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/szullc/article/details/128245356

Como fazer com que todas as portas seriais de todos os microcomputadores de chip único realizem a função printf

(1) Com o fundamento acima, já temos uma certa compreensão dos parâmetros variáveis. Então, como escrever uma função sozinho, de modo que todas as portas seriais de todos os microcomputadores de chip único possam realizar a função printf?
(2) Todos nós sabemos que para microcomputadores de chip único, usar printf requer remapeamento e apenas uma porta serial pode ser remapeada, o que não é muito conveniente. Então, novamente, usarei o conhecimento acima para escrever uma função que possa imprimir em todas as portas seriais de todos os microcontroladores com os blogs dos grandes.
(3) Observe que a função vsnprintf() aqui é para formatar os parâmetros variáveis ​​no formato de acordo com o formato gramatical em printf e, em seguida, passar os dados finais para a matriz TxBuffer[]. A propósito, retorne quantos bytes de dados são formatados.

/* 作用 : 用于多路串口重定义
 * 传入参数 : 
     baseAddress : 要打印的串口地址,UARTx_BASE,x可为0,1,2,3,4,5,6,7
     format : 需要打印的东西
     ... : 如果是打印字符,输入%c。有符号数字,%d。用法与printf一样
 * 返回值 : 无
*/
void UART_printf(uint32_t baseAddress, const char *format,...)
{
    
    
    uint32_t length;
    va_list args;
    uint32_t i;
    char TxBuffer[128] = {
    
    0};

    va_start(args, format);
    length = vsnprintf((char*)TxBuffer, sizeof(TxBuffer), (char*)format, args);
    va_end(args);

    for(i = 0; i < length; i++)
		{
    
    
			//根据不同单片机的串口发送函数,依次发送TxBuffer[i]数据。
			//以下以MSP430为例
			USCI_A_UART_transmitData(baseAddress, TxBuffer[i]);
			//以下以TM4C123为例
			while(UARTBusy(baseAddress));
			UARTCharPut(baseAddress,TxBuffer[i]);
			//以下以STM32F103为例
			USART_SendByte(baseAddress,TxBuffer[i]);
			while(USART_GetFlagStatus(baseAddress,USART_FLAG_TC) == RESET);
		}
}

おすすめ

転載: blog.csdn.net/qq_63922192/article/details/131260801