본문 바로가기

SW 개발 공부/OS 개발 프로젝트

[나빌로스] 5-1. UART printf 구현하기

debug_printf 함수는 가변 인자 처리기능을 구현한다.

함수의 매개변수로 받는 ...는 가변인자를 의미한다.

 

#ifndef LIB_STDIO_H_
#define LIB_STDIO_H_

uint32_t putstr(const char* s);
uint32_t debug_printf(const char* format, ...);

#endif

 

 

stdio.c 에 debug_printf를 선언해준다.

#include "stdint.h"
#include "HalUart.h"
#include "stdio.h"

#define PRINTF_BUF_LEN  1024

static char printf_buf[PRINTF_BUF_LEN];   // 1KB

uint32_t putstr(const char* s)
{
    uint32_t c = 0;
    while(*s)
    {
        Hal_uart_put_char(*s++);
        c++;
    }
    return c;
}

uint32_t debug_printf(const char* format, ...)
{
    va_list args;
    va_start(args, format);
    vsprintf(printf_buf, format, args);
    va_end(args);

    return putstr(printf_buf);
}

 

가변인자 처리를 위해 va_list , va_start, va_end가 추가되고

형식 지정자 처리는 vsprintf에서 수행된다.

 

stdarg.h에 가변인자 처리를 위한 컴파일러 빌트인 함수들을 재정의한다.

va_는 컴파일러 빌트인 함수이다.

 

#ifndef INCLUDE_STDARG_H_
#define INCLUDE_STDARG_H_

typedef __builtin_va_list va_list;

#define va_start(v, l)  __builtin_va_start(v,l)
#define va_end(v)       __builtin_va_end(v)
#define va_arg(v,l)     __builtin_va_arg(v,l)

#endif /* INCLUDE_STDARG_H_ */

 

 

  • va_list
    가변 인자 리스트를 관리하기 위한 타입. (typedef __builtin_va_list va_list;)
  • va_start(va_list, format)
    • va_list를 초기화하여, format 인자 다음의 가변 인자를 순차적으로 읽을 준비를 함.
    • 여기서 format은 debug_printf(const char* format, ...)의 첫 번째 인자임.
  • va_arg(va_list, type)
    • va_list에서 특정 타입(type)의 데이터를 하나씩 꺼내는 역할을 함.
    • 예를 들어, va_arg(args, int) 하면 args 리스트에서 int 타입 하나를 꺼냄.
  • va_end(va_list)
    • 가변 인자 처리를 마친 후 정리하는 역할을 함.

 

2. vsprintf(printf_buf, format, args)의 의미

vsprintf 함수는 일반적인 sprintf와 비슷하지만, 가변 인자를 직접 전달받아 처리하는 기능이 있어.

즉, debug_printf에서 vsprintf를 호출하면:

  1. format 문자열과 args에 들어있는 가변 인자들을 참조해서 형식 지정자(%d, %s, %f 등)를 실제 값으로 치환하여 printf_buf에 문자열을 저장해.
  2. 이후 putstr(printf_buf)를 호출해서 UART로 문자열을 전송하게 됨.

 

 

오답 내용.

uint32_t utoa(char* buf, uint32_t val, utoa_t base)
{
        const char asciia = 'a';
        uint32_t c=0;
        int32_t idx=0;
        char tmp[11];

        do{
                uint32_t cur = val%base;

                if(cur>=10)
                {
                        cur= asciia - '0' - 10;
                }
                tmp[idx++]=cur+'0';
                val/=base;

        }while(val);

        while(idx<=0)
        {
                buff[c++]=tmp[idx--];
        }

        return c;
}

 

3. 오답 및 정리

 

1) utoa_t 를 강제 형변환 안해줘도 되나?
-> utoa_t 는 enum으로 선언되었고 int 형으로 선언되어있기에 uint32_t를 강제형변환 시켜줘야 안전하게 처리됨.

 

2)  for(uint32_t i=0;format[i];i++)

-> format의 마지막까지 진행. 마지막은 '\0'이 존재함으로 해당 조건 도달시 0이 되기에 끝까지 읽겠다는 의미.