본문 바로가기
DSP, MCU/TMS320F2838x (C28x)

TMS320F28388D ] 링버퍼 사용 SCI 에코백

by eteo 2022. 12. 30.

이전글 : 2022.12.30 - [DSP, MCU/TMS320F2838x (C28x)] - 링 버퍼 Circular Buffer

 

링 버퍼 Circular Buffer

링 버퍼를 구현하기위해선 큐를 먼저 알아야 한다. 큐는 FIFO(First In First Out) 구조로 먼저들어온 데이터가 먼저 나가는 구조이다. 이와 비교되는 자료구조는 LIFO(Last In First Out) 구조인 스택이 있다

eteo.tistory.com

 

 

먼저 링버퍼를 사용하지 않는 코드는 다음과 같다.

 

main loop에서 Blocking 방식으로 에코백하는 버전

#include "driverlib.h"
#include "device.h"

void main(void)
{
    uint16_t receivedChar;

    Device_init();

    Device_initGPIO();

    GPIO_setMasterCore(DEVICE_GPIO_PIN_SCIRXDA, GPIO_CORE_CPU1);
    GPIO_setPinConfig(DEVICE_GPIO_CFG_SCIRXDA);
    GPIO_setDirectionMode(DEVICE_GPIO_PIN_SCIRXDA, GPIO_DIR_MODE_IN);
    GPIO_setPadConfig(DEVICE_GPIO_PIN_SCIRXDA, GPIO_PIN_TYPE_STD);
    GPIO_setQualificationMode(DEVICE_GPIO_PIN_SCIRXDA, GPIO_QUAL_ASYNC);

    GPIO_setMasterCore(DEVICE_GPIO_PIN_SCITXDA, GPIO_CORE_CPU1);
    GPIO_setPinConfig(DEVICE_GPIO_CFG_SCITXDA);
    GPIO_setDirectionMode(DEVICE_GPIO_PIN_SCITXDA, GPIO_DIR_MODE_OUT);
    GPIO_setPadConfig(DEVICE_GPIO_PIN_SCITXDA, GPIO_PIN_TYPE_STD);
    GPIO_setQualificationMode(DEVICE_GPIO_PIN_SCITXDA, GPIO_QUAL_ASYNC);

    Interrupt_initModule();
    Interrupt_initVectorTable();

    SCI_performSoftwareReset(SCIA_BASE);

    SCI_setConfig(SCIA_BASE, DEVICE_LSPCLK_FREQ, 115200, (SCI_CONFIG_WLEN_8 |
                                                        SCI_CONFIG_STOP_ONE |
                                                        SCI_CONFIG_PAR_NONE));
    SCI_resetChannels(SCIA_BASE);
    SCI_resetRxFIFO(SCIA_BASE);
    SCI_resetTxFIFO(SCIA_BASE);
    SCI_clearInterruptStatus(SCIA_BASE, SCI_INT_TXFF | SCI_INT_RXFF);

    SCI_enableFIFO(SCIA_BASE);
    SCI_enableModule(SCIA_BASE);
    SCI_performSoftwareReset(SCIA_BASE);
    SCI_resetRxFIFO(SCIA_BASE);

    EINT;
    ERTM;

    for(;;)
    {
        receivedChar = SCI_readCharBlockingFIFO(SCIA_BASE);
        SCI_writeCharBlockingFIFO(SCIA_BASE, receivedChar);

    }
}

 

 

 

이건 RX 인터럽트를 사용해 에코백하는 버전

#include "driverlib.h"
#include "device.h"

volatile uint16_t receivedChar;

__interrupt void sciaRXFIFOISR(void);

void main(void)
{

    Device_init();

    Device_initGPIO();

    GPIO_setMasterCore(DEVICE_GPIO_PIN_SCIRXDA, GPIO_CORE_CPU1);
    GPIO_setPinConfig(DEVICE_GPIO_CFG_SCIRXDA);
    GPIO_setDirectionMode(DEVICE_GPIO_PIN_SCIRXDA, GPIO_DIR_MODE_IN);
    GPIO_setPadConfig(DEVICE_GPIO_PIN_SCIRXDA, GPIO_PIN_TYPE_STD);
    GPIO_setQualificationMode(DEVICE_GPIO_PIN_SCIRXDA, GPIO_QUAL_ASYNC);

    GPIO_setMasterCore(DEVICE_GPIO_PIN_SCITXDA, GPIO_CORE_CPU1);
    GPIO_setPinConfig(DEVICE_GPIO_CFG_SCITXDA);
    GPIO_setDirectionMode(DEVICE_GPIO_PIN_SCITXDA, GPIO_DIR_MODE_OUT);
    GPIO_setPadConfig(DEVICE_GPIO_PIN_SCITXDA, GPIO_PIN_TYPE_STD);
    GPIO_setQualificationMode(DEVICE_GPIO_PIN_SCITXDA, GPIO_QUAL_ASYNC);

    Interrupt_initModule();
    Interrupt_initVectorTable();

    Interrupt_register(INT_SCIA_RX, sciaRXFIFOISR);

    SCI_performSoftwareReset(SCIA_BASE);

    SCI_setConfig(SCIA_BASE, DEVICE_LSPCLK_FREQ, 115200, (SCI_CONFIG_WLEN_8 |
                                                        SCI_CONFIG_STOP_ONE |
                                                        SCI_CONFIG_PAR_NONE));
    SCI_resetChannels(SCIA_BASE);
    SCI_resetRxFIFO(SCIA_BASE);
    SCI_resetTxFIFO(SCIA_BASE);
    SCI_clearInterruptStatus(SCIA_BASE, SCI_INT_TXFF | SCI_INT_RXFF);

    SCI_enableInterrupt(SCIA_BASE, SCI_INT_RXFF);
    SCI_setFIFOInterruptLevel(SCIA_BASE, SCI_FIFO_TX0, SCI_FIFO_RX1);
    SCI_enableFIFO(SCIA_BASE);

    SCI_enableModule(SCIA_BASE);
    SCI_performSoftwareReset(SCIA_BASE);
    SCI_resetRxFIFO(SCIA_BASE);

    Interrupt_enable(INT_SCIA_RX);
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9);

    EINT;
    ERTM;

    for(;;)
    {

    }
}

__interrupt void sciaRXFIFOISR(void)
{

    receivedChar = ((uint16_t)(HWREGH(SCIA_BASE + SCI_O_RXBUF) & SCI_RXBUF_SAR_M));
    if(SCI_getTxFIFOStatus(SCIA_BASE) != SCI_FIFO_TX16)
    {
        HWREGH(SCIA_BASE + SCI_O_TXBUF) = receivedChar;
    }

    SCI_clearInterruptStatus(SCIA_BASE, SCI_INT_RXFF);
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9);
}

 

인터럽트 안의 에코백 부분. driverlib 로 먼저 실행해보고 함수 안에서 레지스터를 어떻게 조작하고 있는지 확인하는게 도움이 된다.

 

수신 인터럽트가 걸렸다는 거는건 이미 RXFIFO 레벨1 만큼 데이터가 들어왔다는 뜻이니 확인과정 없이 바로 receivedChar에 데이터를 읽어들이고 TXFIFO가 꽉찬 상태(레벨16 까지)가 아니라면 받은 데이터를 TXFIFO로 내보낸다.

    receivedChar = ((uint16_t)(HWREGH(SCIA_BASE + SCI_O_RXBUF) & SCI_RXBUF_SAR_M));
    if(SCI_getTxFIFOStatus(SCIA_BASE) != SCI_FIFO_TX16)
    {
        HWREGH(SCIA_BASE + SCI_O_TXBUF) = receivedChar;
    }

 

 

 

 

 

 

링버퍼 사용 버전

 

RX 인터럽트로 링버퍼에 enQueue하고 main loop 에서 링버퍼에 데이터가 들었는지 확인해 loop 한번 돌때마다 링버퍼에서 한글자씩 빼내 출력하도록 한다.

 

#include "driverlib.h"
#include "device.h"

__interrupt void sciaRXFIFOISR(void);

volatile uint16_t receivedChar;

uint16_t BUF_LEN = 1024;
typedef struct {
    volatile uint16_t head;
    volatile uint16_t tail;
    volatile uint16_t data[1024];
} t_ringBuf;

t_ringBuf BUFF = { 0, 0, { 0 } };
t_ringBuf* rb_handle = &BUFF;

uint16_t isRbAvailable(t_ringBuf* rb)
{
    uint16_t ret = 0;

    ret = (BUF_LEN + rb->head - rb->tail) % BUF_LEN;

    return ret;
}

void rbEnQueue(t_ringBuf* rb, char c) {

    uint16_t i = (rb->head + 1) % BUF_LEN;

    if (i != rb->tail)
    {
        rb->data[rb->head] = c;
        rb->head = i;
    }

}

uint16_t rbDeQueue(t_ringBuf* rb) {

    uint16_t c = '\0';

    if (rb->head != rb->tail)
    {
        c = rb->data[rb->tail];
        rb->tail = (rb->tail + 1) % BUF_LEN;
    }

    return c;
}

void main(void)
{

    Device_init();

    Device_initGPIO();

    GPIO_setMasterCore(DEVICE_GPIO_PIN_SCIRXDA, GPIO_CORE_CPU1);
    GPIO_setPinConfig(DEVICE_GPIO_CFG_SCIRXDA);
    GPIO_setDirectionMode(DEVICE_GPIO_PIN_SCIRXDA, GPIO_DIR_MODE_IN);
    GPIO_setPadConfig(DEVICE_GPIO_PIN_SCIRXDA, GPIO_PIN_TYPE_STD);
    GPIO_setQualificationMode(DEVICE_GPIO_PIN_SCIRXDA, GPIO_QUAL_ASYNC);

    GPIO_setMasterCore(DEVICE_GPIO_PIN_SCITXDA, GPIO_CORE_CPU1);
    GPIO_setPinConfig(DEVICE_GPIO_CFG_SCITXDA);
    GPIO_setDirectionMode(DEVICE_GPIO_PIN_SCITXDA, GPIO_DIR_MODE_OUT);
    GPIO_setPadConfig(DEVICE_GPIO_PIN_SCITXDA, GPIO_PIN_TYPE_STD);
    GPIO_setQualificationMode(DEVICE_GPIO_PIN_SCITXDA, GPIO_QUAL_ASYNC);

    Interrupt_initModule();
    Interrupt_initVectorTable();

    Interrupt_register(INT_SCIA_RX, sciaRXFIFOISR);

    SCI_performSoftwareReset(SCIA_BASE);

    SCI_setConfig(SCIA_BASE, DEVICE_LSPCLK_FREQ, 115200, (SCI_CONFIG_WLEN_8 |
                                                        SCI_CONFIG_STOP_ONE |
                                                        SCI_CONFIG_PAR_NONE));
    SCI_resetChannels(SCIA_BASE);
    SCI_resetRxFIFO(SCIA_BASE);
    SCI_resetTxFIFO(SCIA_BASE);
    SCI_clearInterruptStatus(SCIA_BASE, SCI_INT_TXFF | SCI_INT_RXFF);

    SCI_enableInterrupt(SCIA_BASE, SCI_INT_RXFF);
    SCI_setFIFOInterruptLevel(SCIA_BASE, SCI_FIFO_TX0, SCI_FIFO_RX1);
    SCI_enableFIFO(SCIA_BASE);

    SCI_enableModule(SCIA_BASE);
    SCI_performSoftwareReset(SCIA_BASE);
    SCI_resetRxFIFO(SCIA_BASE);

    Interrupt_enable(INT_SCIA_RX);
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9);

    EINT;
    ERTM;

    for(;;)
    {
        if(isRbAvailable(rb_handle))
        {
            SCI_writeCharNonBlocking(SCIA_BASE, rbDeQueue(rb_handle));
        }
    }
}

__interrupt void sciaRXFIFOISR(void)
{
    receivedChar = ((uint16_t)(HWREGH(SCIA_BASE + SCI_O_RXBUF) & SCI_RXBUF_SAR_M));
    rbEnQueue(rb_handle, receivedChar);

    SCI_clearInterruptStatus(SCIA_BASE, SCI_INT_RXFF);
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9);
}

 

에코백이 잘된다.

 

 

 

 

일부러 mail loop 안에서 500ms delay 주면서 led를 토글하게 해보았다. 그리고 테라텀을 통해 1Kb의 텍스트 파일을 전송했다.

// ...
    GPIO_setPinConfig(GPIO_31_GPIO31);
    GPIO_setPadConfig(31, GPIO_PIN_TYPE_STD);
    GPIO_setDirectionMode(31, GPIO_DIR_MODE_OUT);
// ...
    for(;;)
    {
        if(isRbAvailable(rb_handle))
        {
            SCI_writeCharNonBlocking(SCIA_BASE, rbDeQueue(rb_handle));
        }

        GPIO_togglePin(31);
        DEVICE_DELAY_US(500000);
    }

 

 

그래도 링버퍼의 크기가 1024 니 데이터 손실없이 잘 출력하고 있는 모습이다.