ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 임베디드 OS 개발 [5장]
    Share/OS 2021. 3. 6. 17:36

    www.yes24.com/Product/Goods/3337559

     

    도전! 임베디드 OS 만들기

    운영체제의 원리는 얼마나 복잡할까? 이 책은 작고 간단한 임베디드 운영체제를 만들어 보면서 운영체제의 원리를 익힐 수 있도록 구성한 책으로, 운영체제라는 것을 크고 복잡하고 어렵고 범

    www.yes24.com

    본글은 위의 책을 참고로 개인적으로 정리하고 느낀점위주로 작성하였습니다. 

    저자분의 노고에 감사드립니다. 

     

     

     

    5장 UART 

     

     

    1. UART를 사용하기 위해서 가장 먼저 해야될일은 UART하드웨어의 레지스터를 코드로 만드는 일 

    - 그렇다면 spi나 LVDS 등은 당연하겠거니와, BLE나 WIFI처럼 무선칩에 대해서도 그러하겟네? 

    - 이런걸 드라이버 코드를 만든다 하는것인가 ? 

    - 그러면 STM32 같은경우는 HAL Library를 다 만들어주니깐 정말 간편/ 획기적이긴 하네. 다만 이러한 근거/근간을 모르고 하게 되니 조금 아는 재미가없고 깊이가 없을수 밖에 없을듯하다. 

     

    2. RealViewPB의 UART인 PL011 를 사용

     검색해보니 다음과 같은 자료가 나옴 

     

     

     

     

     

    •  딱 메모리, interrupt, DMA 등 과같은 이 장치의 스펙과 설명을 해놓았는데,
      • 보자마자 익숙하면서 쫄게됨. 더 레지스터를 뭐 어케 하겟단건지 하는 생각부터 들기도 하고 그랬음 
    •  메모리 맵(Memory Map) 이라는게 그리고 쭈욱~ 나오는데, 5장을 읽으며 아.....메모리맵이라는게 중요하구나, 
      • 한단어로 표현하면 메모리맵= 스체치북 공간 
      • 내가 그림그리고자 하는데, 그 스케치북 안에 부분별로 그리고싶은거 나누는 느낌으로 이해함
    •  위의 PL011 의 레지스터 문서를 통해 --> 코드를 만든다는 게 정말 신기했음 
      • 다시말해, UART 하드웨어의 레지스터를 코드로 만들어서 lib에 추가함(Uart.h) 
    #ifndef HAL_RVPB_UART_H_
    #define HAL_RVPB_UART_H_
    
    typedef union UARTDR_t
    {
        uint32_t all;
        struct {
            uint32_t DATA:8;    // 7:0
            uint32_t FE:1;      // 8
            uint32_t PE:1;      // 9
            uint32_t BE:1;      // 10
            uint32_t OE:1;      // 11
            uint32_t reserved:20;
        } bits;
    } UARTDR_t;
    
    typedef union UARTRSR_t
    {
        uint32_t all;
        struct {
            uint32_t FE:1;      // 0
            uint32_t PE:1;      // 1
            uint32_t BE:1;      // 2
            uint32_t OE:1;      // 3
            uint32_t reserved:28;
        } bits;
    } UARTRSR_t;
    
    typedef union UARTFR_t
    {
        uint32_t all;
        struct {
            uint32_t CTS:1;     // 0
            uint32_t DSR:1;     // 1
            uint32_t DCD:1;     // 2
            uint32_t BUSY:1;    // 3
            uint32_t RXFE:1;    // 4
            uint32_t TXFF:1;    // 5
            uint32_t RXFF:1;    // 6
            uint32_t TXFE:1;    // 7
            uint32_t RI:1;      // 8
            uint32_t reserved:23;
        } bits;
    } UARTFR_t;
    
    typedef union UARTILPR_t
    {
        uint32_t all;
        struct {
            uint32_t ILPDVSR:8; // 7:0
            uint32_t reserved:24;
        } bits;
    } UARTILPR_t;
    
    typedef union UARTIBRD_t
    {
        uint32_t all;
        struct {
            uint32_t BAUDDIVINT:16; // 15:0
            uint32_t reserved:16;
        } bits;
    } UARTIBRD_t;
    
    typedef union UARTFBRD_t
    {
        uint32_t all;
        struct {
            uint32_t BAUDDIVFRAC:6; // 5:0
            uint32_t reserved:26;
        } bits;
    } UARTFBRD_t;
    
    typedef union UARTLCR_H_t
    {
        uint32_t all;
        struct {
            uint32_t BRK:1;     // 0
            uint32_t PEN:1;     // 1
            uint32_t EPS:1;     // 2
            uint32_t STP2:1;    // 3
            uint32_t FEN:1;     // 4
            uint32_t WLEN:2;    // 6:5
            uint32_t SPS:1;     // 7
            uint32_t reserved:24;
        } bits;
    } UARTLCR_H_t;
    
    typedef union UARTCR_t
    {
        uint32_t all;
        struct {
            uint32_t UARTEN:1;      // 0
            uint32_t SIREN:1;       // 1
            uint32_t SIRLP:1;       // 2
            uint32_t Reserved1:4;   // 6:3
            uint32_t LBE:1;         // 7
            uint32_t TXE:1;         // 8
            uint32_t RXE:1;         // 9
            uint32_t DTR:1;         // 10
            uint32_t RTS:1;         // 11
            uint32_t Out1:1;        // 12
            uint32_t Out2:1;        // 13
            uint32_t RTSEn:1;       // 14
            uint32_t CTSEn:1;       // 15
            uint32_t reserved2:16;
        } bits;
    } UARTCR_t;
    
    typedef union UARTIFLS_t
    {
        uint32_t all;
        struct {
            uint32_t TXIFLSEL:3;    // 2:0
            uint32_t RXIFLSEL:3;    // 5:3
            uint32_t reserved:26;
        } bits;
    } UARTIFLS_t;
    
    typedef union UARTIMSC_t
    {
        uint32_t all;
        struct {
            uint32_t RIMIM:1;   // 0
            uint32_t CTSMIM:1;  // 1
            uint32_t DCDMIM:1;  // 2
            uint32_t DSRMIM:1;  // 3
            uint32_t RXIM:1;    // 4
            uint32_t TXIM:1;    // 5
            uint32_t RTIM:1;    // 6
            uint32_t FEIM:1;    // 7
            uint32_t PEIM:1;    // 8
            uint32_t BEIM:1;    // 9
            uint32_t OEIM:1;    // 10
            uint32_t reserved:21;
        } bits;
    } UARTIMSC_t;
    
    typedef union UARTRIS_t
    {
        uint32_t all;
        struct {
            uint32_t RIRMIS:1;  // 0
            uint32_t CTSRMIS:1; // 1
            uint32_t DCDRMIS:1; // 2
            uint32_t DSRRMIS:1; // 3
            uint32_t RXRIS:1;   // 4
            uint32_t TXRIS:1;   // 5
            uint32_t RTRIS:1;   // 6
            uint32_t FERIS:1;   // 7
            uint32_t PERIS:1;   // 8
            uint32_t BERIS:1;   // 9
            uint32_t OERIS:1;   // 10
            uint32_t reserved:21;
        } bits;
    } UARTRIS_t;
    
    typedef union UARTMIS_t
    {
        uint32_t all;
        struct {
            uint32_t RIMMIS:1;  // 0
            uint32_t CTSMMIS:1; // 1
            uint32_t DCDMMIS:1; // 2
            uint32_t DSRMMIS:1; // 3
            uint32_t RXMIS:1;   // 4
            uint32_t TXMIS:1;   // 5
            uint32_t RTMIS:1;   // 6
            uint32_t FEMIS:1;   // 7
            uint32_t PEMIS:1;   // 8
            uint32_t BEMIS:1;   // 9
            uint32_t OEMIS:1;   // 10
            uint32_t reserved:21;
        } bits;
    } UARTMIS_t;
    
    typedef union UARTICR_t
    {
        uint32_t all;
        struct {
            uint32_t RIMIC:1;   // 0
            uint32_t CTSMIC:1;  // 1
            uint32_t DCDMIC:1;  // 2
            uint32_t DSRMIC:1;  // 3
            uint32_t RXIC:1;    // 4
            uint32_t TXIC:1;    // 5
            uint32_t RTIC:1;    // 6
            uint32_t FEIC:1;    // 7
            uint32_t PEIC:1;    // 8
            uint32_t BEIC:1;    // 9
            uint32_t OEIC:1;    // 10
            uint32_t reserved:21;
        } bits;
    } UARTICR_t;
    
    typedef union UARTDMACR_t
    {
        uint32_t all;
        struct {
            uint32_t RXDMAE:1;  // 0
            uint32_t TXDMAE:1;  // 1
            uint32_t DMAONERR:1;// 2
            uint32_t reserved:29;
        } bits;
    } UARTDMACR_t;
    
    typedef struct PL011_t
    {
        UARTDR_t    uartdr;         //0x000
        UARTRSR_t   uartrsr;        //0x004
        uint32_t    reserved0[4];   //0x008-0x014
        UARTFR_t    uartfr;         //0x018
        uint32_t    reserved1;      //0x01C
        UARTILPR_t  uartilpr;       //0x020
        UARTIBRD_t  uartibrd;       //0x024
        UARTFBRD_t  uartfbrd;       //0x028
        UARTLCR_H_t uartlcr_h;      //0x02C
        UARTCR_t    uartcr;         //0x030
        UARTIFLS_t  uartifls;       //0x034
        UARTIMSC_t  uartimsc;       //0x038
        UARTRIS_t   uartris;        //0x03C
        UARTMIS_t   uartmis;        //0x040
        UARTICR_t   uarticr;        //0x044
        UARTDMACR_t uartdmacr;      //0x048
    } PL011_t;
    
    #define UART_BASE_ADDRESS0       0x10009000
    #define UART_INTERRUPT0          44
    
    #endif /* HAL_RVPB_UART_H_ */

     이렇게 하나씩 라이브러리 헤더파일을 추가했던거였구나 하고 과거의 프로젝트들이 생각남 

     Uart.h 또한 구조화? 구조체를 사용해서 추후 꺼내쓰기 편하게? 해놨다는것. 체크 

     

     

    이때 추가적으로 "공용인터페이스"를 적용

     

    HalUart.h 

      - hal/rvpb/Uart.c

      - hal/rasppi/Uart.c

      - hal/. .../Uart.c

     

    로 만들어놔서, 각 하드웨어 HAL 구현체에서는 HalUart.h 파일을 #include하여 각자 하드웨어에 맞춰서 구현하는 것. 

     

     

    여기서 UART 공용 인터페이스 구현올 조금더 살펴보면  

    hal/HalUart.h 를 구현하는 hal/rvpb/Uart.c 코드는 다음과 같다 

     

    Uart.c

    #include "stdint.h"
    #include "stdbool.h"
    #include "Uart.h"
    #include "HalUart.h"
    
    extern volatile PL011_t* Uart;
    
    static void interrupt_handler(void);
    
    void Hal_uart_init(void)
    {
        // Enable UART
        Uart->uartcr.bits.UARTEN = 0;
        Uart->uartcr.bits.TXE = 1;
        Uart->uartcr.bits.RXE = 1;
        Uart->uartcr.bits.UARTEN = 1;
    
        // Enable input interrupt
        Uart->uartimsc.bits.RXIM = 1;
    
        // Register UART interrupt handler
        Hal_interrupt_enable(UART_INTERRUPT0);
        Hal_interrupt_register_handler(interrupt_handler, UART_INTERRUPT0);
    }

    - 사실위의 코드는 최적화를 한 코드이고 책에서는 어떻게 최적화를 해갔는지 설명이 잘 적혀있으니 참조바람

     

    이를 이제 구동시키려면 Makefile 에서 작업을 해야된다. 

     

    - 오타가 나고, 파일을 include 할 수 없다는 에러를 정말 많이 접했다. 차근히 코드를 따라감이 필요했다. 

    또한 Makefile을 완전히 이해하지못했기에 일어난 일이라고 생각한다. 

     

     

    지금까지의 tree 구조는 이렇다 

    - 뜬금없이 uart.h uart.c 파일 구분 기준...? 원리가 궁금함...

     

     

    여기까지 하면, 

    흔히 디버깅할때 uart로 문자나 데이터를 찍는 과정의 절반은 했다고 생각함

    Main.c에서 작성한 문자열이 make run 했을시에 print되었음 

     

    다음스텝으로는 

    printf 처럼 표준 문자열 출력 함수 처럼 쓸수있도록 

    putchar 함수를 추가하는 과정을 기술하였다. 

     

    지금까지는 프린트 된 문자열을 보았다면 당연히 입력도 해야되기에 기능을 추가하는것이다. 

     

     

     

     

     결국 ARM의 메모리영역(RO, RW , etc) 에 compile 과 link를 거쳐 .bin파일이 박히는거, 저장되는것이다. (일단 휘발/비휘발 제외하고) 

     

    아무튼, 정말 printf 함수를 추가해보자.(구조만 살펴보겠다)  

     

    • - main.c 에서 printf( ) 라는 함수를 적고싶다. 
    • - step1>  lib/stdio.h 에서 debug_printf( ) 함수를 선언한다 (인터페이스 구현) 
      - step2> lib/stdio.c 에서  debug_printf( ) 함수를 구현한다. 
      - step3> include/stdarg.h 파일을 새로만든다 
           - 이는, va_start, va_end, va_list 라는 자료형을 사용하고, 표준라이브러리가 아닌 컴파일러의 빌트인 함수를 쓰기위해서
    • -step4 >  lib/stdio.h 에서 vsprintf( ) 함수를 선언한다. 
                   lib/stdio.c 에서 vsprintf( ) 함수를 구현한다. 
                      위의 debug_printf() 함수에서 vsprintf()를 사용
                      출력시에 자료형 case별로 출력함을 위함 eq. %u , %x, %s 등 
    • - step5 > lib/stdio.h 에서 utoa( ) 함수 선언 과 lib/stdio.c에서 함수 구현
                  이 함수의 목적은 숫자를 아스키 코드로 변환하는 것. 

     

     

    요약

     

    •  main.c 는 stdio.h 를 가져온다.
    • stdio.h 에서 함수를 선언하고 각 함수는 stdio.c 에서 구현한다. 각 함수의 목적은 ....책을참고...
      • utoa 
      • vsprintf
      • debug_printf 

    *stdio.c 파일은 생략 (책 참고) 

    • 결과 화면 
      •  

     

     

     

     

     

     

    최종 지금까지 트리구조는 다음과 같다. 

     

     

     

     

     

    느낀점 

     

    • printf( ) 라는 나만의 코드를 쓰기 위해서 여러가지 일들이 수반되어야 함. 
    • 표준입출력장치를 쓰지 않고 self 로 만든다는게 이런 원리가 있었다는 것을 알게되었다. 
    • stm32 개발이나 기타F/W 작업중에도 printf()를 쓰기위해서 여러가지 세팅을 했었는데,  이러한 원리가 있었다는것을 깨닫게되었다. 
    • 파일구조를 이해하는 것이 매우중요하다는 것을 새삼 깨닫게되었다. 

    댓글

실험중인 삶, Life is not a race