본문 바로가기
MCU/C2000

[TMS320F28377D] SCI 사용법: Bitfield 구조 활용 예제 코드

by linuxgo 2025. 8. 6.
반응형

소개

TI의 TMS320F28377D는 C2000 Delfino 시리즈의 고성능 마이크로컨트롤러로, 실시간 제어 애플리케이션에 최적화된 디바이스입니다. SCI(Serial Communication Interface)는 비동기 직렬 통신(UART)을 지원하여 PC, 센서, 다른 MCU와 데이터를 주고받는 데 사용됩니다. 이 가이드에서는 SCI의 기능, 구조, 예제 코드를 통해 SCI를 완벽히 이해하고 활용하는 방법을 다룹니다.

1. TMS320F28377D SCI 개요

SCI의 주요 특징

  • 비동기 통신: 외부 클록 없이 TX(송신)와 RX(수신) 핀으로 데이터 전송.
  • 전이중 통신: 송신과 수신 동시 수행.
  • FIFO 지원: 최대 16바이트 송신/수신 FIFO로 효율성 향상.
  • 패리티 체크: 홀수/짝수 패리티로 오류 검출.
  • 데이터 포맷: NRZ 방식, 시작 비트(1), 데이터 비트(1~8), 패리티 비트(선택), 정지 비트(1~2).
  • 보드레이트: 최대 115,200bps, LSPCLK 기반 설정.
  • 멀티프로세서 모드: 주소 기반 및 아이들 라인 모드.
  • SCI 모듈: SCI-A, SCI-B, SCI-C, SCI-D의 4개 독립 모듈.

응용 분야

  • PC와 데이터 로깅/디버깅
  • 센서 및 액추에이터 통신
  • 산업용 제어 시스템
  • 펌웨어 업데이트(Serial Flash Programming)

2. SCI 모듈 구조

SCI 모듈은 다음과 같은 구성 요소로 이루어져 있습니다:

  • 송신기(TX): 병렬 데이터를 직렬로 변환.
  • 수신기(RX): 직렬 데이터를 병렬로 변환.
  • FIFO 버퍼: 송신/수신 FIFO(최대 16바이트).
  • 제어 레지스터: SCICCR, SCICTL1, SCICTL2 등.
  • 보드레이트 생성기: LSPCLK를 분주하여 보드레이트 설정.
  • 인터럽트 제어: 송신, 수신, 오류 인터럽트.

핀 구성

  • SCI-A: GPIO28(TX), GPIO29(RX)
  • SCI-B: GPIO18(TX), GPIO19(RX)
  • SCI-C, SCI-D: 특정 GPIO에 매핑.

3. SCI 동작 원리

SCI는 비동기 통신으로 데이터를 프레임 단위로 전송합니다:

  • 프레임 구조: 시작 비트(1), 데이터 비트(1~8), 패리티 비트(선택), 정지 비트(1~2).
  • 보드레이트 계산:
    Baud Rate = LSPCLK / ((BRR + 1) * 8)
    •    LSPCLK: Low-Speed Peripheral Clock(예: SYSCLK=200MHz → LSPCLK=50MHz).
    •    BRR: 보드레이트 레지스터 값(SCIHBAUD, SCILBAUD).

예: 9600bps 계산

LSPCLK = 50MHz, 목표 보드레이트 = 9600bps:

BRR = (50,000,000 / (9600 * 8)) - 1 = 650.36 ≈ 650
SCIHBAUD = 650 >> 8 = 2 (0x02)
SCILBAUD = 650 & 0xFF = 138 (0x8A)

4. FIFO 상세 설명

FIFO란?

SCI의 FIFO는 송신(TX)과 수신(RX)에 각각 최대 16바이트의 버퍼를 제공하여 데이터 전송 효율성을 높입니다. FIFO는 CPU 부담을 줄이고, 데이터 손실을 방지하며, 인터럽트 빈도를 최적화합니다.

FIFO의 이점

  • CPU 부담 감소: Polling 방식에서 매 데이터 확인 불필요.
  • 인터럽트 최적화: FIFO 레벨에 따라 인터럽트 발생.
  • 데이터 손실 방지: 빠른 데이터 수신 시 임시 저장.

FIFO 관련 레지스터

  1. SCIFFTX (SCI FIFO Transmit Register):
    •    SCIFFENA: FIFO 활성화(1=활성).
    •    TXFFIL: 송신 FIFO 인터럽트 레벨(0~16).
    •    TXFFST: 현재 FIFO 데이터 수(읽기 전용).
    •    TXFIFORESET: 송신 FIFO 리셋(0=리셋, 1=정상).
    •    예: SCIFFTX.all = 0xE040 | (8 << 8) (FIFO 활성화, 레벨 8).
  2. SCIFFRX (SCI FIFO Receive Register):
    •    RXFFIL: 수신 FIFO 인터럽트 레벨(0~16).
    •    RXFFST: 현재 FIFO 데이터 수(읽기 전용).
    •    RXFIFORESET: 수신 FIFO 리셋(0=리셋, 1=정상).
    •    RXFFOVF: 오버플로우 플래그.
    •    예: SCIFFRX.all = 0x2040 | (8 << 8).
  3. SCIFFCT (SCI FIFO Control Register):
    •    FFTXDLY: 송신 지연(0~255 클록 사이클).
    •    CDC: 자동 보드레이트 검출.

FIFO 설정 절차

  1. FIFO 활성화: SCIFFTX.bit.SCIFFENA = 1.
  2. 인터럽트 레벨 설정: SCIFFTX.bit.TXFFIL, SCIFFRX.bit.RXFFIL.
  3. FIFO 리셋 해제: SCIFFTX.bit.TXFIFORESET = 1, SCIFFRX.bit.RXFIFORESET = 1.

5. SCI 레지스터 설정

주요 레지스터

  1. SCICCR: 데이터 포맷.
    •    SCICHAR: 데이터 비트(0~7, 1~8비트).
    •    PARITYENA: 패리티 활성화.
    •    예: 8비트, 1 정지 비트, 패리티 없음 → SCICCR.all = 0x0007.
  2. SCICTL1: 송신/수신 활성화.
    •    TXENA, RXENA: 송신/수신 활성화.
    •    SWRESET: 모듈 리셋(1=정상).
    •    예: SCICTL1.all = 0x0003.
  3. SCICTL2: 인터럽트 설정.
    •    TXINTENA, RXBKINTENA: 인터럽트 활성화.
  4. SCIHBAUD/SCILBAUD: 보드레이트 설정.
    •    예: BRR=650 → SCIHBAUD=0x02, SCILBAUD=0x8A.

6. SCI 및 FIFO 예제 코드

다음은 SCI와 FIFO를 활용한 예제 코드로, 상세 주석과 레지스터 계산이 포함되어 있습니다. 모든 코드는 Code Composer Studio(CCS)에서 실행 가능합니다.

예제 1: 기본 송수신 (Polling, FIFO 활용)

9600bps, 8비트 데이터, 1 정지 비트, 패리티 없음, FIFO 사용.


#include "F28x_Project.h"

// SCI-A 초기화 함수
void scia_init(void) {
    EALLOW;
    // 클록 활성화: SCI-A에 클록 공급
    SysCtrlRegs.PCLKCR3.bit.SCIAENCLK = 1;
    // GPIO 설정: GPIO28(TX), GPIO29(RX)를 SCI-A로 매핑
    GpioCtrlRegs.GPAMUX2.bit.GPIO28 = 1;   // SCI-A TX
    GpioCtrlRegs.GPAMUX2.bit.GPIO29 = 1;   // SCI-A RX
    GpioCtrlRegs.GPADIR.bit.GPIO28 = 1;    // TX 출력
    GpioCtrlRegs.GPADIR.bit.GPIO29 = 0;    // RX 입력
    EDIS;

    // SCICCR: 데이터 포맷 설정
    // - SCICHAR=7 (8비트 데이터)
    // - STOPBITS=0 (1 정지 비트)
    // - PARITYENA=0 (패리티 비활성화)
    SciaRegs.SCICCR.all = 0x0007;

    // SCICTL1: 송신/수신 활성화
    // - TXENA=1, RXENA=1
    SciaRegs.SCICTL1.all = 0x0003;

    // SCICTL2: 인터럽트 비활성화 (Polling 방식)
    SciaRegs.SCICTL2.all = 0x0000;

    // 보드레이트 설정: 9600bps, LSPCLK=50MHz
    // 계산: BRR = (50,000,000 / (9600 * 8)) - 1 = 650
    // SCIHBAUD = 650 >> 8 = 2 (0x02)
    // SCILBAUD = 650 & 0xFF = 138 (0x8A)
    SciaRegs.SCIHBAUD.all = 0x0002;
    SciaRegs.SCILBAUD.all = 0x008A;

    // SCIFFTX: 송신 FIFO 설정
    // - SCIFFENA=1 (FIFO 활성화)
    // - TXFIFORESET=1 (리셋 해제)
    // - TXFFIL=8 (인터럽트 레벨 8바이트)
    SciaRegs.SCIFFTX.all = 0xE040 | (8 << 8);

    // SCIFFRX: 수신 FIFO 설정
    // - RXFIFORESET=1 (리셋 해제)
    // - RXFFIL=8 (인터럽트 레벨 8바이트)
    SciaRegs.SCIFFRX.all = 0x2040 | (8 << 8);

    // SCI 활성화: SWRESET=1
    SciaRegs.SCICTL1.bit.SWRESET = 1;
}

// 데이터 송신 함수
void scia_xmit(char *data, Uint16 len) {
    Uint16 i;
    for (i = 0; i < len; i++) {
        // FIFO에 8바이트 미만일 때까지 대기
        while (SciaRegs.SCIFFTX.bit.TXFFST >= 8);
        SciaRegs.SCITXBUF.all = data[i]; // 데이터 송신
    }
}

// 데이터 수신 함수
void scia_receive(char *buffer, Uint16 len) {
    Uint16 i;
    for (i = 0; i < len; i++) {
        // FIFO에 데이터가 있을 때까지 대기
        while (SciaRegs.SCIFFRX.bit.RXFFST == 0);
        buffer[i] = SciaRegs.SCIRXBUF.all; // 데이터 수신
    }
}

void main(void) {
    InitSysCtrl(); // 시스템 클록 및 주변 장치 초기화
    scia_init();   // SCI-A 초기화
    char tx_data[] = "Hello"; // 송신 데이터
    char rx_buffer[5];       // 수신 버퍼

    while(1) {
        scia_xmit(tx_data, 5); // "Hello" 송신
        DELAY_US(1000000);     // 1초 대기
        scia_receive(rx_buffer, 5); // 5바이트 수신
        scia_xmit(rx_buffer, 5);   // 수신 데이터 에코
    }
}
        

설명: FIFO 레벨 8로 설정하여 "Hello" 문자열을 송신하고, 수신 데이터를 에코합니다.

예제 2: 인터럽트 기반 송수신 (FIFO 활용)

115200bps, 8비트 데이터, 1 정지 비트, 패리티 없음, FIFO 사용.


#include "F28x_Project.h"

// 수신 버퍼 및 인덱스
interrupt void scia_rx_isr(void);
interrupt void scia_tx_isr(void);
volatile char rx_buffer[16];
volatile Uint16 rx_index = 0;

// SCI-A 초기화 함수
void scia_init(void) {
    EALLOW;
    // 클록 활성화: SCI-A에 클록 공급
    SysCtrlRegs.PCLKCR3.bit.SCIAENCLK = 1;
    // GPIO 설정: GPIO28(TX), GPIO29(RX)
    GpioCtrlRegs.GPAMUX2.bit.GPIO28 = 1;
    GpioCtrlRegs.GPAMUX2.bit.GPIO29 = 1;
    GpioCtrlRegs.GPADIR.bit.GPIO28 = 1;
    GpioCtrlRegs.GPADIR.bit.GPIO29 = 0;
    EDIS;

    // SCICCR: 데이터 포맷 설정
    // - SCICHAR=7 (8비트 데이터)
    // - STOPBITS=0 (1 정지 비트)
    // - PARITYENA=0 (패리티 비활성화)
    SciaRegs.SCICCR.all = 0x0007;

    // SCICTL1: 송신/수신 활성화
    // - TXENA=1, RXENA=1
    SciaRegs.SCICTL1.all = 0x0003;

    // SCICTL2: 인터럽트 활성화
    // - TXINTENA=1 (송신 인터럽트)
    // - RXBKINTENA=1 (수신 인터럽트)
    SciaRegs.SCICTL2.bit.TXINTENA = 1;
    SciaRegs.SCICTL2.bit.RXBKINTENA = 1;

    // 보드레이트 설정: 115200bps, LSPCLK=50MHz
    // 계산: BRR = (50,000,000 / (115200 * 8)) - 1 = 53.25 ≈ 53
    // SCIHBAUD = 53 >> 8 = 0 (0x00)
    // SCILBAUD = 53 & 0xFF = 53 (0x35)
    SciaRegs.SCIHBAUD.all = 0x0000;
    SciaRegs.SCILBAUD.all = 0x0035;

    // SCIFFTX: 송신 FIFO 설정
    // - SCIFFENA=1 (FIFO 활성화)
    // - TXFIFORESET=1
    // - TXFFIL=4 (인터럽트 레벨 4바이트)
    SciaRegs.SCIFFTX.all = 0xE040 | (4 << 8);

    // SCIFFRX: 수신 FIFO 설정
    // - RXFIFORESET=1
    // - RXFFIL=4 (인터럽트 레벨 4바이트)
    SciaRegs.SCIFFRX.all = 0x2040 | (4 << 8);

    // SCI 활성화
    SciaRegs.SCICTL1.bit.SWRESET = 1;

    // 인터럽트 설정
    EALLOW;
    PieCtrlRegs.PIECTRL.bit.ENPIE = 1;    // PIE 활성화
    PieCtrlRegs.PIEIER9.bit.INTx1 = 1;    // SCI-A 수신 인터럽트
    PieCtrlRegs.PIEIER9.bit.INTx2 = 1;    // SCI-A 송신 인터럽트
    IER |= M_INT9;                        // 그룹 9 인터럽트 활성화
    EDIS;
}

// 송신 인터럽트 서비스 루틴
interrupt void scia_tx_isr(void) {
    SciaRegs.SCIFFTX.bit.TXFFINTCLR = 1;  // 송신 인터럽트 플래그 클리어
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP9; // PIE ACK
}

// 수신 인터럽트 서비스 루틴
interrupt void scia_rx_isr(void) {
    while (SciaRegs.SCIFFRX.bit.RXFFST > 0) { // FIFO에 데이터가 있는 동안
        rx_buffer[rx_index++] = SciaRegs.SCIRXBUF.all; // 데이터 저장
        if (rx_index >= 16) rx_index = 0; // 버퍼 오버플로우 방지
    }
    SciaRegs.SCIFFRX.bit.RXFFOVRCLR = 1;  // 오버플로우 클리어
    SciaRegs.SCIFFRX.bit.RXFFINTCLR = 1;  // 수신 인터럽트 클리어
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP9;
}

// 데이터 송신 함수
void scia_xmit(char *data, Uint16 len) {
    Uint16 i;
    for (i = 0; i < len; i++) {
        while (SciaRegs.SCIFFTX.bit.TXFFST >= 4); // FIFO 레벨 4 미만 대기
        SciaRegs.SCITXBUF.all = data[i];
    }
}

void main(void) {
    InitSysCtrl(); // 시스템 초기화
    InitPieCtrl(); // PIE 초기화
    InitPieVectTable(); // 인터럽트 벡터 테이블 초기화
    EALLOW;
    PieVectTable.SCIA_RX_INT = &scia_rx_isr; // 수신 ISR 등록
    PieVectTable.SCIA_TX_INT = &scia_tx_isr; // 송신 ISR 등록
    EDIS;
    scia_init(); // SCI 초기화
    EINT;        // 글로벌 인터럽트 활성화

    char tx_data[] = "FIFO Test"; // 송신 데이터
    while(1) {
        scia_xmit(tx_data, 9); // "FIFO Test" 송신
        DELAY_US(1000000);     // 1초 대기
    }
}
        

설명: 115200bps로 설정(계산 포함), FIFO 레벨 4로 인터럽트를 최적화하여 "FIFO Test" 송신.

예제 3: 멀티프로세서 모드 (FIFO 활용)

9600bps, 주소 기반 멀티프로세서 모드, FIFO 사용.


#include "F28x_Project.h"

#define DEVICE_ADDRESS 0x01 // 디바이스 주소

// SCI-A 초기화 함수
void scia_init(void) {
    EALLOW;
    // 클록 활성화
    SysCtrlRegs.PCLKCR3.bit.SCIAENCLK = 1;
    // GPIO 설정
    GpioCtrlRegs.GPAMUX2.bit.GPIO28 = 1;
    GpioCtrlRegs.GPAMUX2.bit.GPIO29 = 1;
    GpioCtrlRegs.GPADIR.bit.GPIO28 = 1;
    GpioCtrlRegs.GPADIR.bit.GPIO29 = 0;
    EDIS;

    // SCICCR: 주소 모드 설정
    // - SCICHAR=7 (8비트 데이터)
    // - ADDRIDLE_MODE=1 (주소 모드)
    SciaRegs.SCICCR.all = 0x0027;

    // SCICTL1: 송신/수신 활성화
    SciaRegs.SCICTL1.all = 0x0003;

    // SCICTL2: 인터럽트 비활성화
    SciaRegs.SCICTL2.all = 0x0000;

    // 보드레이트 설정: 9600bps, LSPCLK=50MHz
    // 계산: BRR = (50,000,000 / (9600 * 8)) - 1 = 650
    // SCIHBAUD = 650 >> 8 = 2 (0x02)
    // SCILBAUD = 650 & 0xFF = 138 (0x8A)
    SciaRegs.SCIHBAUD.all = 0x0002;
    SciaRegs.SCILBAUD.all = 0x008A;

    // SCIFFTX: 송신 FIFO 설정
    // - SCIFFENA=1 (FIFO 활성화)
    // - TXFIFORESET=1
    // - TXFFIL=8 (인터럽트 레벨 8)
    SciaRegs.SCIFFTX.all = 0xE040 | (8 << 8);

    // SCIFFRX: 수신 FIFO 설정
    // - RXFIFORESET=1
    // - RXFFIL=8
    SciaRegs.SCIFFRX.all = 0x2040 | (8 << 8);

    // SCI 활성화
    SciaRegs.SCICTL1.bit.SWRESET = 1;
}

// 주소 송신 함수
void scia_xmit_address(char address) {
    while (SciaRegs.SCIFFTX.bit.TXFFST >= 8); // FIFO 레벨 8 미만 대기
    SciaRegs.SCICCR.bit.ADDRIDLE_MODE = 1;   // 주소 프레임 설정
    SciaRegs.SCITXBUF.all = address;         // 주소 송신
}

// 데이터 송신 함수
void scia_xmit_data(char *data, Uint16 len) {
    Uint16 i;
    SciaRegs.SCICCR.bit.ADDRIDLE_MODE = 0; // 데이터 프레임 설정
    for (i = 0; i < len; i++) {
        while (SciaRegs.SCIFFTX.bit.TXFFST >= 8);
        SciaRegs.SCITXBUF.all = data[i];
    }
}

// 데이터 수신 함수
void scia_receive(char *buffer, Uint16 len) {
    Uint16 i;
    for (i = 0; i < len; i++) {
        while (SciaRegs.SCIFFRX.bit.RXFFST == 0);
        buffer[i] = SciaRegs.SCIRXBUF.all;
    }
}

void main(void) {
    InitSysCtrl(); // 시스템 초기화
    scia_init();   // SCI 초기화
    char tx_data[] = "Multi"; // 송신 데이터
    char rx_buffer[5];       // 수신 버퍼

    while(1) {
        scia_xmit_address(DEVICE_ADDRESS); // 주소 송신
        scia_xmit_data(tx_data, 5);       // 데이터 송신
        DELAY_US(1000000);                // 1초 대기
        scia_receive(rx_buffer, 5);       // 수신
        if (rx_buffer[0] == DEVICE_ADDRESS) { // 주소 일치 확인
            scia_xmit_data("ACK", 3);     // 응답
        }
    }
}
        

설명: FIFO를 활용한 주소 기반 멀티프로세서 모드 통신.

7. 문제 해결 가이드

일반적인 문제

  1. FIFO 오버플로우:
    •    원인: RXFFIL 초과 데이터 수신.
    •    해결: RXFFOVRCLR=1, FIFO 레벨 조정.
  2. 데이터 손상:
    •    해결: 보드레이트 확인, RS-232 레벨 시프터 사용.
  3. 인터럽트 실패:
    •    해결: PIE 테이블 및 PieCtrlRegs.PIEACK 확인.

 

반응형