티스토리 뷰

직비 칩에서 시리얼 통신 코드 구현에 관하여 

자꾸만 잊어버리는 건망증에 대비하여 이 글을 남긴다. 내공이 부족하여 오류가 있음을 충분히 감안하고 보아야 할 것이다. (뭐냐 이 대책 없는 무모함은 ㅡ..ㅡ) 어쨌든 이 포스팅에서는 cc2530의 UART 설정 방법에 대해 살펴보고자 한다. 본 포스팅을 읽기 전에 CC2530에서 UART가 어떻게 동작하는가에 대한 기본 이해를 돕는 Design Note DN112를 먼저 읽어볼 것을 추천한다.  


기본적인 준비는 되었겠지?

cc2530 개발환경과 TIMAC이 잘 준비된 상태라고 가정한다. 안 되어있으면 그것부터 해놓자. 이 부분도 좀 삽질을 해야하지만 그것까지 적으려니 좀 귀찮다. 언제 시간 날때 정리하도록 하자. 자~ 그럼 UART를 사용하기 위해 다음의 과정을 밟아가 보자.


preprocessor 옵션

먼저 UART 옵션들을 설정한다. UART를 사용하기 위해 프로젝트 옵션의 C/C++ Compiler 항목의 Preprocessor의 Defined symbols에 몇 가지 항목을 추가 혹은 수정한다. HAL_UART = TRUE로 넣고 POWER_SAVING은 xPOWER_SAVING로 변경하자. hal_board_cfg.h에 관련 옵션들이 나오는데 내가 쓴 옵션은 다음과 같다. 처음 하드웨어 환경 설정할 때 hal_board_cfg.h의 전 부분을 꼼꼼히 설정했다면 아래 부분들이 제대로 맞춰져 있을것이다.

HAL_UART = TRUE

인터럽트를 사용하는 경우 : HAL_UART_ISR = ( 1 또는 2 ), HAL_UART_DMA = FALSE

    * HAL_UART_ISR = 1 ( UART 0을 사용 ), HAL_UART_ISR = 2 ( UART 1을 사용 )

DMA를 사용하는 경우 : HAL_UART_ISR = 0, HAL_UART_DMA = (1 또는 2 )


UART 초기화

애플리케이션이 시작되면 UART 관련 초기화를 하고 콜백함수도 맹글어 준다. 참고로 printf에서 0x0d(\r)는 커서 제일 앞으로, 0x0a(\n)는 줄바꿈이다. 가끔 생각이 안나서 적어놓는다. ㅡ..ㅡ);;;

//UART test variable
uint8 *rxBuffer;
uint8 rxBufferIndex = 0;

static void uartCallback(uint8 port, uint8 event)
{
    uint16 len;
    uint8 buf[8];
    uint8 i;

    switch(event)
    {
        case HAL_UART_RX_FULL:
        case HAL_UART_RX_ABOUT_FULL:
        case HAL_UART_RX_TIMEOUT:
            len = Hal_UART_RxBufLen(HAL_UART_PORT_0);
            HalUARTRead(HAL_UART_PORT_0, buf, len);
            for(i = 0; i < len; i++)
            {
                //in this application, all lines sent to CC2540 ends with a carriage return (0x0D)
                if(buf[i] != 0x0D)
                    rxBuffer[rxBufferIndex++] = buf[i];
                    printf("%c", buf[i]);    // echo
                else
                {
                    //processBuffer(); //do stuff with rxBuffer using rxBufferIndex as the length of the rxBuffer
                    rxBuffer[rxBufferIndex] = 0;
                    printf("\r\n%s\r\n", rxBuffer);
                    memset(rxBuffer, 0x00, sizeof(rxBuffer));
                    rxBufferIndex = 0;
                    break;
                }
            }
            break;
    }
}

static void setupUART(void)
{
    //HalUARTInit();

    halUARTCfg_t uartConfig;

    // configure UART
    uartConfig.configured = TRUE;
    uartConfig.baudRate = HAL_UART_BR_115200;
    uartConfig.flowControl = HAL_UART_FLOW_OFF;
    uartConfig.flowControlThreshold = 0;
    uartConfig.rx.maxBufSize = 128;
    uartConfig.tx.maxBufSize = 128;
    uartConfig.idleTimeout = 1; //1ms timeout
    uartConfig.intEnable = TRUE;
    uartConfig.callBackFunc = (halUARTCBack_t)uartCallback;

    //start UART
    //assumes no issues with starting UART
    (void)HalUARTOpen(HAL_UART_PORT_0, &uartConfig);

    rxBuffer = osal_mem_alloc(128); //assumes there is no problem with getting this block of memory
}

memset함수 때문에 헤더에 #include 을 포함 시켜준다. 안 쓸꺼면 말고.


setupUART는 어디에 두어야 할까?

setupUART는 다른 모든 초기화가 끝난 이후에 맹글은 본인의 애플리케이션의 Init() 함수 내에 위치시키도록 한다.

UART 상에 printf 구현하기

oPossum’s tiny printf()를 기초로한 printf 샘플이다. 이걸로 UART상에서 손쉽게 데이터를 전송하는 디버깅 코드를 구현할 수 있다. printf.c 라고 대략 이름을 붙여줬다. 싫으면 다른 이름도 괜찮겠지.. 그런데 머리가 굳어서인지 다른 이름은 생각나지 않네. 흠..

#include "stdarg.h"
#include "hal_uart.h"
#include 
#include "hal_types.h"

void printf(char *format, ...);

static void sendByte(unsigned char byte)
{
    HalUARTWrite(HAL_UART_PORT_0, &byte, 1); //change port to suit your needs
}

static void putc(unsigned char c)
{
    sendByte(c);
}

static void puts(uint8 *str)
{
    HalUARTWrite(HAL_UART_PORT_0, str, strlen((const char*)str)); //change port to suit your needs
}

static const unsigned long dv[] =
{
    // 4294967296 // 32 bit unsigned max
    1000000000,// +0
    100000000, // +1
    10000000, // +2
    1000000, // +3
    100000, // +4
    // 65535 // 16 bit unsigned max
    10000, // +5
    1000, // +6
    100, // +7
    10, // +8
    1, // +9
};

static void xtoa(unsigned long x, const unsigned long *dp)
{
    char c;
    unsigned long d;

    if (x)
    {
        while (x < *dp)
            ++dp;
        do
        {
            d = *dp++;
            c = '0';
            while (x >= d)
                ++c, x -= d;
            putc(c);
        } while (!(d & 1));
    } else
        putc('0');
}

static void puth(unsigned n)
{
    static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
        '9', 'A', 'B', 'C', 'D', 'E', 'F' };

    putc(hex[n & 15]);
}

void printf(char *format, ...)
{
    char c;
    int i;
    long n;

    va_list a;
    va_start(a, format);
    while(c = *format++)
    {
        if(c == '%')
        {
            switch(c = *format++)
            {
                case 's': // String
                    puts(va_arg(a, char*));
                    break;

                case 'c':// Char
                    putc(va_arg(a, char));
                    break;

                case 'i':// 16 bit Integer
                case 'u':// 16 bit Unsigned
                    i = va_arg(a, int);
                    if(c == 'i' && i < 0)
                        i = -i, putc('-');
                    xtoa((unsigned)i, dv + 5);
                    break;

                case 'l':// 32 bit Long
                case 'n':// 32 bit uNsigned loNg
                    n = va_arg(a, long);
                    if(c == 'l' && n < 0)
                        n = -n, putc('-');
                    xtoa((unsigned long)n, dv);
                    break;

                case 'x':// 16 bit heXadecimal
                    i = va_arg(a, int);
                    puth(i >> 12);
                    puth(i >> 8);
                    puth(i >> 4);
                    puth(i);
                    break;

                case 0:
                    return;

                default:
                    goto bad_fmt;
            }
        }
        else
            bad_fmt: putc(c);
    }

    va_end(a);
}

extern void printf(char *format, ...); 코드를 사용하고자 하는 애플리케이션 헤더에 추가해서 사용토록 하자.


더 해볼것들 

앞서 이야기 했다시피 이 코드는 인터럽트를 썼다. DMA를 이용해서 사용하는 것도 해보자. 끝.