본문 바로가기
MCU/C2000

TMS320F28377D DSP CPU1, CPU2 멀티코어 사용법 : Bitfield 구조 활용 예제 코드

by linuxgo 2025. 8. 18.
반응형

1. TMS320F28377D 멀티코어 아키텍처 개요

TI의 TMS320F28377D는 C2000 Delfino 시리즈의 32비트 마이크로컨트롤러로, 듀얼 코어(CPU1, CPU2)를 지원하여 고성능 병렬 처리가 가능합니다. 각 코어는 독립적인 연산 장치, 메모리, 인터럽트 컨트롤러를 가지며, IPC(Inter-Processor Communication)를 통해 상호 통신합니다. 이 문서에서는 CPU1과 CPU2의 설정 방법, Bitfield 구조를 활용한 IPC 설정, 그리고 실용적인 예제 코드를 제공하여 멀티코어 프로그래밍을 쉽게 이해하고 활용할 수 있도록 돕겠습니다.

1.1 주요 사양

  • 코어: CPU1, CPU2 (각각 200MHz, 독립 FPU 및 VCU 포함)
  • 메모리:
    • 공유 RAM(S0, S1): CPU1과 CPU2 간 데이터 교환
    • 로컬 RAM: 각 CPU 전용 메모리
    • 플래시: 최대 1MB, CPU1에서 주로 관리
  • IPC:
    • 메시지 RAM: 데이터 및 명령 전송
    • 인터럽트: IPC 플래그 및 명령 인터럽트
    • 메모리 액세스: CPU1 ↔ CPU2 간 메모리 공유 설정
  • 주변 장치: CAN, SPI, I2C, ADC 등, CPU1 또는 CPU2에 할당 가능
  • 인터럽트: 각 CPU별 PIE(Peripheral Interrupt Expansion) 지원
  • 저전력 모드: 유휴(idle) 및 대기(standby) 모드

2. 멀티코어 설정 및 IPC Bitfield 상세

멀티코어 설정은 F2837xD_ipc.h 헤더 파일을 통해 Bitfield 구조로 접근합니다. 주요 레지스터는 다음과 같습니다:

2.1 IPC 레지스터

  • IPCCMD: IPC 명령 레지스터
    • bit.TASK: 작업 ID 설정
    • bit.DATA: 데이터 전송
  • IPCSTS: IPC 상태 레지스터
    • bit.IPCFx: 플래그 상태 (x=0~31)
    • bit.IPCBUSY: IPC 작업 진행 상태
  • IPCACK: 인터럽트 확인 레지스터
    • bit.IPCFx: 플래그 클리어
  • IPCSENDCOM: 송신 명령 레지스터
    • bit.COMMAND: 전송할 명령
  • IPCRECVCOM: 수신 명령 레지스터
    • bit.COMMAND: 수신된 명령

2.2 메모리 제어

  • MEMCFG: 메모리 설정 레지스터
    • bit.GSxMSEL: 공유 RAM 소유권 (0: CPU1, 1: CPU2)
    • bit.LSxMSEL: 로컬 RAM 소유권
  • ACCESS: 메모리 액세스 권한
    • bit.CPUxWR: 쓰기 권한
    • bit.CPUxRD: 읽기 권한

3. 멀티코어 설정 절차

멀티코어 환경을 설정하려면 다음 단계를 따릅니다:

  1. 시스템 초기화:
    • CPU1: InitSysCtrl()로 시스템 클럭 및 PLL 초기화
    • CPU2: CPU1에서 부팅 명령 전송 후 초기화
  2. 메모리 설정:
    • MEMCFG로 공유 RAM 소유권 설정
    • 필요 시 ACCESS로 읽기/쓰기 권한 설정
  3. IPC 초기화:
    • CPU1과 CPU2 간 IPC 플래그 및 인터럽트 설정
    • IPCSENDCOM/IPCRECVCOM으로 명령 교환
  4. 주변 장치 할당:
    • 특정 주변 장치(CAN, ADC 등)를 CPU1 또는 CPU2에 할당
  5. 인터럽트 설정:
    • 각 CPU의 PIE 벡터 테이블 초기화
    • IPC 인터럽트 활성화 (예: IPC0_INT)
  6. 프로그램 실행:
    • CPU1: 메인 애플리케이션 실행
    • CPU2: CPU1의 명령 수신 후 작업 수행

4. 멀티코어 설정 고려사항

  • 메모리 충돌: 공유 RAM 액세스 시 소유권 명확히 설정
  • IPC 동기화: 플래그 및 인터럽트를 활용한 동기화 필수
  • 부팅 순서: CPU1이 CPU2 부팅 제어
  • 주변 장치 공유: 동일 장치를 두 코어에서 동시에 사용 금지
  • 디버깅: CPU1과 CPU2의 CCS 프로젝트 분리

5. 실용적인 멀티코어 예제 코드 (Bitfield 구조)

아래는 TMS320F28377D의 CPU1과 CPU2를 활용한 네 가지 예제 코드입니다. 각 코드는 Code Composer Studio(CCS)와 C2000Ware 환경에서 실행 가능합니다.

5.1 예제 1: CPU1 → CPU2 데이터 전송 (IPC 활용)

CPU1 코드

// File: cpu1_ipc_transmit.c
// Description: TMS320F28377D CPU1에서 CPU2로 데이터 전송 예제 (Bitfield 구조)
// Compiler: Code Composer Studio (TI C2000 Compiler)
// Target: TMS320F28377D CPU1

#include "F28x_Project.h"

void main(void) {
    // 시스템 초기화
    InitSysCtrl(); // 시스템 클럭 및 PLL 초기화
    DINT; // 모든 인터럽트 비활성화
    InitPieCtrl(); // PIE 초기화
    IER = 0x0000; // CPU 인터럽트 비활성화
    IFR = 0x0000; // 대기 중인 인터럽트 플래그 지우기
    InitPieVectTable(); // PIE 벡터 테이블 초기화

    // GPIO 설정
    EALLOW;
    GpioCtrlRegs.GPBDIR.bit.GPIO31 = 1; // GPIO31을 출력(LED)으로 설정
    GpioDataRegs.GPBSET.bit.GPIO31 = 1; // LED 켜기
    EDIS;

    // 공유 RAM 설정
    EALLOW;
    MemCfgRegs.GS0MSEL.bit.GS0MSEL = 0; // GS0 RAM을 CPU1 소유로 설정
    MemCfgRegs.GS0ACCPROT0.bit.CPU2WR = 1; // CPU2 쓰기 허용
    MemCfgRegs.GS0ACCPROT0.bit.CPU2RD = 1; // CPU2 읽기 허용
    EDIS;

    // CPU2 부팅
    EALLOW;
    IpcRegs.IPCSENDCOM = 0x0001; // CPU2 부팅 명령
    IpcRegs.IPCSENDFLAG.bit.IPCF0 = 1; // IPC 플래그 0 설정
    EDIS;

    // 공유 RAM에 데이터 쓰기
    Uint16 *sharedRam = (Uint16 *)0x0003F800; // GS0 RAM 주소
    sharedRam[0] = 0x1234; // 데이터 쓰기
    sharedRam[1] = 0x5678;

    // CPU2에 데이터 전송 요청
    EALLOW;
    IpcRegs.IPCSENDCOM = 0x0002; // 데이터 전송 명령
    IpcRegs.IPCSENDFLAG.bit.IPCF1 = 1; // IPC 플래그 1 설정
    EDIS;

    for(;;) {
        if (IpcRegs.IPCRECVFLAG.bit.IPCF2) { // CPU2 응답 확인
            GpioDataRegs.GPBTOGGLE.bit.GPIO31 = 1; // LED 토글
            EALLOW;
            IpcRegs.IPCACK.bit.IPCF2 = 1; // 플래그 클리어
            EDIS;
        }
    }
}

CPU2 코드

// File: cpu2_ipc_receive.c
// Description: TMS320F28377D CPU2에서 CPU1 데이터 수신 예제 (Bitfield 구조)
// Compiler: Code Composer Studio (TI C2000 Compiler)
// Target: TMS320F28377D CPU2

#include "F28x_Project.h"

__interrupt void ipc0_isr(void) {
    if (IpcRegs.IPCRECVCOM == 0x0002) { // 데이터 전송 명령 확인
        Uint16 *sharedRam = (Uint16 *)0x0003F800; // GS0 RAM 주소
        if (sharedRam[0] == 0x1234 && sharedRam[1] == 0x5678) {
            IpcRegs.IPCSENDFLAG.bit.IPCF2 = 1; // 응답 플래그 설정
        }
    }
    IpcRegs.IPCACK.bit.IPCF1 = 1; // 플래그 클리어
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // PIE 그룹 1 ACK
}

void main(void) {
    // 시스템 초기화
    InitSysCtrl(); // 시스템 클럭 및 PLL 초기화
    DINT; // 모든 인터럽트 비활성화
    InitPieCtrl(); // PIE 초기화
    IER = 0x0000; // CPU 인터럽트 비활성화
    IFR = 0x0000; // 대기 중인 인터럽트 플래그 지우기
    InitPieVectTable(); // PIE 벡터 테이블 초기화

    // 인터럽트 벡터 설정
    EALLOW;
    PieVectTable.IPC0_INT = &ipc0_isr; // IPC0 인터럽트 벡터 설정
    EDIS;

    // 인터럽트 활성화
    PieCtrlRegs.PIEIER1.bit.INTx9 = 1; // PIE 그룹 1, IPC0 인터럽트 활성화
    IER |= M_INT1; // CPU 인터럽트 1 활성화
    EINT; // 글로벌 인터럽트 활성화

    // CPU1 부팅 명령 대기
    while (!IpcRegs.IPCRECVFLAG.bit.IPCF0); // 플래그 0 대기
    EALLOW;
    IpcRegs.IPCACK.bit.IPCF0 = 1; // 플래그 클리어
    EDIS;

    for(;;); // 무한 루프
}

설명:

  • 기능: CPU1에서 공유 RAM(GS0)을 통해 CPU2로 데이터(0x1234, 0x5678) 전송, CPU2는 데이터를 확인하고 응답
  • 설정: IPC 플래그 및 인터럽트 활용, GS0 RAM에 CPU2 읽기/쓰기 권한 부여
  • GPIO: CPU1에서 GPIO31(LED)으로 동작 상태 표시
  • 출력: 데이터 수신 성공 시 CPU1의 LED 토글

5.2 예제 2: CPU1과 CPU2 병렬 작업 (CAN 제어)

CPU1 코드

// File: cpu1_can_transmit.c
// Description: TMS320F28377D CPU1에서 CAN 송신 및 CPU2 제어 예제 (Bitfield 구조)
// Compiler: Code Composer Studio (TI C2000 Compiler)
// Target: TMS320F28377D CPU1

#include "F28x_Project.h"

void main(void) {
    // 시스템 초기화
    InitSysCtrl(); // 시스템 클럭 및 PLL 초기화
    DINT; // 모든 인터럽트 비활성화
    InitPieCtrl(); // PIE 초기화
    IER = 0x0000; // CPU 인터럽트 비활성화
    IFR = 0x0000; // 대기 중인 인터럽트 플래그 지우기
    InitPieVectTable(); // PIE 벡터 테이블 초기화

    // GPIO 설정
    EALLOW;
    GpioCtrlRegs.GPAMUX1.bit.GPIO8 = 2; // GPIO8을 CANRXA로 설정
    GpioCtrlRegs.GPAMUX1.bit.GPIO10 = 2; // GPIO10을 CANTXA로 설정
    GpioCtrlRegs.GPBDIR.bit.GPIO31 = 1; // GPIO31을 출력(LED)으로 설정
    GpioDataRegs.GPBSET.bit.GPIO31 = 1; // LED 켜기
    EDIS;

    // CANA 클럭 활성화
    EALLOW;
    CpuSysRegs.PCLKCR0.bit.CAN_A = 1; // CANA 모듈 클럭 활성화
    EDIS;

    // CAN 초기화
    EALLOW;
    CanARegs.CAN_CTL.bit.INIT = 1; // 초기화 모드 진입
    CanARegs.CAN_CTL.bit.CCE = 1; // 비트 타이밍 설정 허용
    CanARegs.CAN_BTR.bit.BRP = 9; // 보드레이트 프리스케일러 (1Mbps @ 200MHz)
    CanARegs.CAN_BTR.bit.TSEG1 = 13; // TSEG1 = 14
    CanARegs.CAN_BTR.bit.TSEG2 = 4; // TSEG2 = 5
    CanARegs.CAN_BTR.bit.SJW = 3; // SJW = 4
    CanARegs.CAN_CTL.bit.CCE = 0; // 비트 타이밍 설정 완료
    EDIS;

    // 메일박스 1 설정 (송신)
    CanARegs.CAN_IF1CMD.bit.Arb = 1; // Arbitration 필드 설정
    CanARegs.CAN_IF1CMD.bit.Control = 1; // 제어 필드 설정
    CanARegs.CAN_IF1CMD.bit.Data_A = 1; // 데이터 A 설정
    CanARegs.CAN_IF1CMD.bit.Data_B = 1; // 데이터 B 설정
    CanARegs.CAN_IF1ARB.bit.MsgVal = 1; // 메일박스 유효
    CanARegs.CAN_IF1ARB.bit.Xtd = 0; // 표준 11비트 ID
    CanARegs.CAN_IF1ARB.bit.ID = 0x123; // CAN ID
    CanARegs.CAN_IF1ARB.bit.Dir = 1; // 송신
    CanARegs.CAN_IF1MCTL.bit.DLC = 8; // 데이터 길이 8바이트
    CanARegs.CAN_IF1DATA.bit.Data_0 = 0x11; // 데이터 바이트
    CanARegs.CAN_IF1DATA.bit.Data_1 = 0x22;
    CanARegs.CAN_IF1DATA.bit.Data_2 = 0x33;
    CanARegs.CAN_IF1DATA.bit.Data_3 = 0x44;
    CanARegs.CAN_IF1DATB.bit.Data_4 = 0x55;
    CanARegs.CAN_IF1DATB.bit.Data_5 = 0x66;
    CanARegs.CAN_IF1DATB.bit.Data_6 = 0x77;
    CanARegs.CAN_IF1DATB.bit.Data_7 = 0x88;
    CanARegs.CAN_IF1CMD.bit.Mask = 0; // 마스크 비활성화
    CanARegs.CAN_IF1CMD.bit.TXrqst = 1; // 송신 요청
    CanARegs.CAN_IF1CMD.bit.Busy = 1; // 전송 시작
    CanARegs.CAN_IF1CMD.bit.MSG_NUM = 1; // 메일박스 1

    // 공유 RAM 설정
    EALLOW;
    MemCfgRegs.GS0MSEL.bit.GS0MSEL = 0; // GS0 RAM을 CPU1 소유로 설정
    MemCfgRegs.GS0ACCPROT0.bit.CPU2WR = 1; // CPU2 쓰기 허용
    MemCfgRegs.GS0ACCPROT0.bit.CPU2RD = 1; // CPU2 읽기 허용
    EDIS;

    // CPU2 부팅
    EALLOW;
    IpcRegs.IPCSENDCOM = 0x0001; // CPU2 부팅 명령
    IpcRegs.IPCSENDFLAG.bit.IPCF0 = 1; // IPC 플래그 0 설정
    EDIS;

    // CAN 정상 모드
    CanARegs.CAN_CTL.bit.INIT = 0; // 정상 모드 진입

    for(;;) {
        // CPU2에 CAN 송신 상태 전송
        Uint16 *sharedRam = (Uint16 *)0x0003F800; // GS0 RAM 주소
        sharedRam[0] = CanARegs.CAN_ES.bit.TXOK; // 송신 상태 저장
        EALLOW;
        IpcRegs.IPCSENDCOM = 0x0002; // 상태 전송 명령
        IpcRegs.IPCSENDFLAG.bit.IPCF1 = 1; // IPC 플래그 1 설정
        EDIS;
    }
}

CPU2 코드

// File: cpu2_can_status.c
// Description: TMS320F28377D CPU2에서 CPU1의 CAN 송신 상태 확인 예제 (Bitfield 구조)
// Compiler: Code Composer Studio (TI C2000 Compiler)
// Target: TMS320F28377D CPU2

#include "F28x_Project.h"

__interrupt void ipc0_isr(void) {
    if (IpcRegs.IPCRECVCOM == 0x0002) { // 상태 전송 명령 확인
        Uint16 *sharedRam = (Uint16 *)0x0003F800; // GS0 RAM 주소
        if (sharedRam[0] == 1) { // CAN 송신 성공 확인
            GpioDataRegs.GPBTOGGLE.bit.GPIO31 = 1; // LED 토글
        }
    }
    IpcRegs.IPCACK.bit.IPCF1 = 1; // 플래그 클리어
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // PIE 그룹 1 ACK
}

void main(void) {
    // 시스템 초기화
    InitSysCtrl(); // 시스템 클럭 및 PLL 초기화
    DINT; // 모든 인터럽트 비활성화
    InitPieCtrl(); // PIE 초기화
    IER = 0x0000; // CPU 인터럽트 비활성화
    IFR = 0x0000; // 대기 중인 인터럽트 플래그 지우기
    InitPieVectTable(); // PIE 벡터 테이블 초기화

    // GPIO 설정
    EALLOW;
    GpioCtrlRegs.GPBDIR.bit.GPIO31 = 1; // GPIO31을 출력(LED)으로 설정
    GpioDataRegs.GPBSET.bit.GPIO31 = 1; // LED 켜기
    EDIS;

    // 인터럽트 벡터 설정
    EALLOW;
    PieVectTable.IPC0_INT = &ipc0_isr; // IPC0 인터럽트 벡터 설정
    EDIS;

    // 인터럽트 활성화
    PieCtrlRegs.PIEIER1.bit.INTx9 = 1; // PIE 그룹 1, IPC0 인터럽트 활성화
    IER |= M_INT1; // CPU 인터럽트 1 활성화
    EINT; // 글로벌 인터럽트 활성화

    // CPU1 부팅 명령 대기
    while (!IpcRegs.IPCRECVFLAG.bit.IPCF0); // 플래그 0 대기
    EALLOW;
    IpcRegs.IPCACK.bit.IPCF0 = 1; // 플래그 클리어
    EDIS;

    for(;;); // 무한 루프
}

설명:

  • 기능: CPU1에서 CAN 메시지 송신, CPU2는 IPC를 통해 송신 상태 확인
  • 설정: CPU1에서 CANA 모듈로 1Mbps 송신, 공유 RAM으로 상태 전송
  • GPIO: CPU1에서 CAN 핀(GPIO8, GPIO10)과 LED(GPIO31), CPU2에서 LED(GPIO31)
  • 출력: CAN 송신 성공 시 CPU2의 LED 토글

5.3 예제 3: CPU1과 CPU2 간 ADC 데이터 공유

CPU1 코드

// File: cpu1_adc_share.c
// Description: TMS320F28377D CPU1에서 ADC 데이터 수집 및 CPU2로 전송 예제 (Bitfield 구조)
// Compiler: Code Composer Studio (TI C2000 Compiler)
// Target: TMS320F28377D CPU1

#include "F28x_Project.h"

void main(void) {
    // 시스템 초기화
    InitSysCtrl(); // 시스템 클럭 및 PLL 초기화
    DINT; // 모든 인터럽트 비활성화
    InitPieCtrl(); // PIE 초기화
    IER = 0x0000; // CPU 인터럽트 비활성화
    IFR = 0x0000; // 대기 중인 인터럽트 플래그 지우기
    InitPieVectTable(); // PIE 벡터 테이블 초기화

    // GPIO 설정
    EALLOW;
    GpioCtrlRegs.GPBDIR.bit.GPIO31 = 1; // GPIO31을 출력(LED)으로 설정
    GpioDataRegs.GPBSET.bit.GPIO31 = 1; // LED 켜기
    EDIS;

    // ADC 설정
    EALLOW;
    CpuSysRegs.PCLKCR13.bit.ADC_A = 1; // ADC_A 클럭 활성화
    AdcaRegs.ADCCTL2.bit.PRESCALE = 6; // ADCCLK = SYSCLK/4
    AdcaRegs.ADCCTL1.bit.INTPULSEPOS = 1; // 인터럽트 펄스 위치
    AdcaRegs.ADCCTL1.bit.ADCPWDNZ = 1; // ADC 전원 활성화
    AdcaRegs.ADCSOC0CTL.bit.CHSEL = 0; // ADCINA0 채널 선택
    AdcaRegs.ADCSOC0CTL.bit.ACQPS = 14; // 샘플링 윈도우
    AdcaRegs.ADCSOC0CTL.bit.TRIGSEL = 5; // 트리거 소스 (ePWM1)
    EDIS;

    // 공유 RAM 설정
    EALLOW;
    MemCfgRegs.GS0MSEL.bit.GS0MSEL = 0; // GS0 RAM을 CPU1 소유로 설정
    MemCfgRegs.GS0ACCPROT0.bit.CPU2WR = 1; // CPU2 쓰기 허용
    MemCfgRegs.GS0ACCPROT0.bit.CPU2RD = 1; // CPU2 읽기 허용
    EDIS;

    // CPU2 부팅
    EALLOW;
    IpcRegs.IPCSENDCOM = 0x0001; // CPU2 부팅 명령
    IpcRegs.IPCSENDFLAG.bit.IPCF0 = 1; // IPC 플래그 0 설정
    EDIS;

    // ADC 데이터 수집 및 전송
    Uint16 *sharedRam = (Uint16 *)0x0003F800; // GS0 RAM 주소
    for(;;) {
        AdcaRegs.ADCSOCFRC1.bit.SOC0 = 1; // SOC0 강제 트리거
        while (!AdcaRegs.ADCINTFLG.bit.ADCINT1); // 변환 완료 대기
        sharedRam[0] = AdcaRegs.ADCRESULT0; // ADC 결과 저장
        AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; // 인터럽트 플래그 클리어
        EALLOW;
        IpcRegs.IPCSENDCOM = 0x0003; // ADC 데이터 전송 명령
        IpcRegs.IPCSENDFLAG.bit.IPCF1 = 1; // IPC 플래그 1 설정
        EDIS;
        GpioDataRegs.GPBTOGGLE.bit.GPIO31 = 1; // LED 토글
    }
}

CPU2 코드

// File: cpu2_adc_receive.c
// Description: TMS320F28377D CPU2에서 CPU1의 ADC 데이터 수신 예제 (Bitfield 구조)
// Compiler: Code Composer Studio (TI C2000 Compiler)
// Target: TMS320F28377D CPU2

#include "F28x_Project.h"

__interrupt void ipc0_isr(void) {
    if (IpcRegs.IPCRECVCOM == 0x0003) { // ADC 데이터 전송 명령 확인
        Uint16 *sharedRam = (Uint16 *)0x0003F800; // GS0 RAM 주소
        if (sharedRam[0] > 2048) { // ADC 값이 1.65V 이상일 때
            GpioDataRegs.GPBTOGGLE.bit.GPIO31 = 1; // LED 토글
        }
    }
    IpcRegs.IPCACK.bit.IPCF1 = 1; // 플래그 클리어
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // PIE 그룹 1 ACK
}

void main(void) {
    // 시스템 초기화
    InitSysCtrl(); // 시스템 클럭 및 PLL 초기화
    DINT; // 모든 인터럽트 비활성화
    InitPieCtrl(); // PIE 초기화
    IER = 0x0000; // CPU 인터럽트 비활성화
    IFR = 0x0000; // 대기 중인 인터럽트 플래그 지우기
    InitPieVectTable(); // PIE 벡터 테이블 초기화

    // GPIO 설정
    EALLOW;
    GpioCtrlRegs.GPBDIR.bit.GPIO31 = 1; // GPIO31을 출력(LED)으로 설정
    GpioDataRegs.GPBSET.bit.GPIO31 = 1; // LED 켜기
    EDIS;

    // 인터럽트 벡터 설정
    EALLOW;
    PieVectTable.IPC0_INT = &ipc0_isr; // IPC0 인터럽트 벡터 설정
    EDIS;

    // 인터럽트 활성화
    PieCtrlRegs.PIEIER1.bit.INTx9 = 1; // PIE 그룹 1, IPC0 인터럽트 활성화
    IER |= M_INT1; // CPU 인터럽트 1 활성화
    EINT; // 글로벌 인터럽트 활성화

    // CPU1 부팅 명령 대기
    while (!IpcRegs.IPCRECVFLAG.bit.IPCF0); // 플래그 0 대기
    EALLOW;
    IpcRegs.IPCACK.bit.IPCF0 = 1; // 플래그 클리어
    EDIS;

    for(;;); // 무한 루프
}

설명:

  • 기능: CPU1에서 ADC 데이터(ADCINA0 채널) 수집 후 공유 RAM을 통해 CPU2로 전송, CPU2는 데이터 값이 1.65V(2048) 이상일 때 LED 토글
  • 설정: CPU1에서 ADC_A 모듈 설정, IPC로 데이터 전송
  • GPIO: CPU1과 CPU2에서 GPIO31(LED) 사용
  • 출력: ADC 값이 임계값 이상일 때 CPU2의 LED 토글

5.4 예제 4: CPU1 PWM 제어 및 CPU2 상태 모니터링

CPU1 코드

// File: cpu1_pwm_control.c
// Description: TMS320F28377D CPU1에서 PWM 제어 및 CPU2로 상태 전송 예제 (Bitfield 구조)
// Compiler: Code Composer Studio (TI C2000 Compiler)
// Target: TMS320F28377D CPU1

#include "F28x_Project.h"

void main(void) {
    // 시스템 초기화
    InitSysCtrl(); // 시스템 클럭 및 PLL 초기화
    DINT; // 모든 인터럽트 비활성화
    InitPieCtrl(); // PIE 초기화
    IER = 0x0000; // CPU 인터럽트 비활성화
    IFR = 0x0000; // 대기 중인 인터럽트 플래그 지우기
    InitPieVectTable(); // PIE 벡터 테이블 초기화

    // GPIO 설정
    EALLOW;
    GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1; // GPIO0을 EPWM1A로 설정
    GpioCtrlRegs.GPBDIR.bit.GPIO31 = 1; // GPIO31을 출력(LED)으로 설정
    GpioDataRegs.GPBSET.bit.GPIO31 = 1; // LED 켜기
    EDIS;

    // ePWM1 설정
    EALLOW;
    CpuSysRegs.PCLKCR2.bit.EPWM1 = 1; // ePWM1 클럭 활성화
    EPwm1Regs.TBCTL.bit.CTRMODE = 0; // 업 카운트 모드
    EPwm1Regs.TBPRD = 1000; // 주기 1000 (100kHz @ 200MHz)
    EPwm1Regs.CMPA.bit.CMPA = 500; // 50% 듀티 사이클
    EPwm1Regs.AQCTLA.bit.ZRO = 2; // 제로에서 셋
    EPwm1Regs.AQCTLA.bit.CAU = 1; // CMPA에서 클리어
    EDIS;

    // 공유 RAM 설정
    EALLOW;
    MemCfgRegs.GS0MSEL.bit.GS0MSEL = 0; // GS0 RAM을 CPU1 소유로 설정
    MemCfgRegs.GS0ACCPROT0.bit.CPU2WR = 1; // CPU2 쓰기 허용
    MemCfgRegs.GS0ACCPROT0.bit.CPU2RD = 1; // CPU2 읽기 허용
    EDIS;

    // CPU2 부팅
    EALLOW;
    IpcRegs.IPCSENDCOM = 0x0001; // CPU2 부팅 명령
    IpcRegs.IPCSENDFLAG.bit.IPCF0 = 1; // IPC 플래그 0 설정
    EDIS;

    // PWM 상태 전송
    Uint16 *sharedRam = (Uint16 *)0x0003F800; // GS0 RAM 주소
    for(;;) {
        sharedRam[0] = EPwm1Regs.TBCTR; // 현재 카운터 값 저장
        EALLOW;
        IpcRegs.IPCSENDCOM = 0x0004; // PWM 상태 전송 명령
        IpcRegs.IPCSENDFLAG.bit.IPCF1 = 1; // IPC 플래그 1 설정
        EDIS;
        GpioDataRegs.GPBTOGGLE.bit.GPIO31 = 1; // LED 토글
    }
}

CPU2 코드

// File: cpu2_pwm_monitor.c
// Description: TMS320F28377D CPU2에서 CPU1의 PWM 상태 모니터링 예제 (Bitfield 구조)
// Compiler: Code Composer Studio (TI C2000 Compiler)
// Target: TMS320F28377D CPU2

#include "F28x_Project.h"

__interrupt void ipc0_isr(void) {
    if (IpcRegs.IPCRECVCOM == 0x0004) { // PWM 상태 전송 명령 확인
        Uint16 *sharedRam = (Uint16 *)0x0003F800; // GS0 RAM 주소
        if (sharedRam[0] > 500) { // PWM 카운터가 절반 이상일 때
            GpioDataRegs.GPBTOGGLE.bit.GPIO31 = 1; // LED 토글
        }
    }
    IpcRegs.IPCACK.bit.IPCF1 = 1; // 플래그 클리어
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // PIE 그룹 1 ACK
}

void main(void) {
    // 시스템 초기화
    InitSysCtrl(); // 시스템 클럭 및 PLL 초기화
    DINT; // 모든 인터럽트 비활성화
    InitPieCtrl(); // PIE 초기화
    IER = 0x0000; // CPU 인터럽트 비활성화
    IFR = 0x0000; // 대기 중인 인터럽트 플래그 지우기
    InitPieVectTable(); // PIE 벡터 테이블 초기화

    // GPIO 설정
    EALLOW;
    GpioCtrlRegs.GPBDIR.bit.GPIO31 = 1; // GPIO31을 출력(LED)으로 설정
    GpioDataRegs.GPBSET.bit.GPIO31 = 1; // LED 켜기
    EDIS;

    // 인터럽트 벡터 설정
    EALLOW;
    PieVectTable.IPC0_INT = &ipc0_isr; // IPC0 인터럽트 벡터 설정
    EDIS;

    // 인터럽트 활성화
    PieCtrlRegs.PIEIER1.bit.INTx9 = 1; // PIE 그룹 1, IPC0 인터럽트 활성화
    IER |= M_INT1; // CPU 인터럽트 1 활성화
    EINT; // 글로벌 인터럽트 활성화

    // CPU1 부팅 명령 대기
    while (!IpcRegs.IPCRECVFLAG.bit.IPCF0); // 플래그 0 대기
    EALLOW;
    IpcRegs.IPCACK.bit.IPCF0 = 1; // 플래그 클리어
    EDIS;

    for(;;); // 무한 루프
}

설명:

  • 기능: CPU1에서 ePWM1 모듈로 PWM 신호(100kHz, 50% 듀티) 생성, CPU2는 PWM 카운터 값을 모니터링
  • 설정: CPU1에서 ePWM1 설정, IPC로 카운터 값 전송
  • GPIO: CPU1에서 PWM 핀(GPIO0)과 LED(GPIO31), CPU2에서 LED(GPIO31)
  • 출력: PWM 카운터가 절반 이상일 때 CPU2의 LED 토글

6. 사용 방법

6.1 환경 설정

  • C2000Ware 설치: C:\ti\c2000\C2000Ware_x_xx_xx_xx에서 라이브러리 다운로드
  • CCS 프로젝트: TMS320F28377D 타겟으로 CPU1, CPU2별 프로젝트 생성, F28x_Project.h 포함
  • 링커 파일: device_support\f2837xd 폴더에서 CPU1, CPU2용 링커 파일 추가

6.2 코드 실행

  • 각 예제를 별도의 .c 파일로 저장하거나, main.c에 복사
  • CPU1과 CPU2 프로젝트를 각각 빌드 및 디버깅
  • 원하는 예제만 실행되도록 다른 코드 주석 처리

6.3 하드웨어 준비

  • CAN 핀: 예제 2에서 CPU1의 CANRXA(GPIO8), CANTXA(GPIO10)에 CAN 트랜시버 연결
  • ADC 핀: 예제 3에서 ADCINA0에 아날로그 입력 연결
  • PWM 핀: 예제 4에서 GPIO0(EPWM1A)에 PWM 출력 연결
  • LED: CPU1과 CPU2에서 GPIO31에 LED 연결 (정상 동작 확인용)
  • CAN 네트워크: 예제 2에서 CAN 버스에 다른 노드 연결, 120Ω 종단 저항 확인

6.4 디버깅

  • CCS Expressions 창: IpcRegs.IPCSTS, CanARegs.CAN_ES, AdcaRegs.ADCRESULT0, EPwm1Regs.TBCTR 확인
  • 인터럽트 상태: IpcRegs.IPCACK, CanARegs.CAN_MIL 점검
  • 에러 확인: CAN_ES.bit.LEC로 CAN 에러 상태 모니터링
  • CAN 분석기: 예제 2에서 CAN 버스 데이터 확인용 CAN 분석기 사용

7. 추가 팁

  • IPC 동기화: 플래그와 인터럽트를 활용하여 CPU1과 CPU2 동기화 보장
  • 메모리 최적화: 공유 RAM 크기와 액세스 권한 최적화
  • 캘리브레이션: Device_cal() 호출로 클럭 보정
  • 노이즈 감소: CAN 트랜시버, ADC 입력, PWM 출력의 차동 신호 라인 점검
  • C2000Ware 참고: C:\ti\c2000\C2000Ware_x_xx_xx_xx\device_support\f2837xd\examples\cpu1\ipc
  • 문제 해결:
    • IPC 실패: IPCSTS.bit.IPCFx, IPCSENDCOM 확인
    • CAN 송신 실패: CAN_ES.bit.TXOK, CAN 버스 연결 확인
    • ADC/PWM 오류: AdcaRegs.ADCCTL1, EPwm1Regs.TBCTL 확인
    • 인터럽트 미작동: PieCtrlRegs.PIEIER1, IER 확인
  • TI 리소스: TI E2E 포럼, C2000Ware 예제

8. 결론

이 문서는 TMS320F28377D의 CPU1과 CPU2 멀티코어 설정 방법과 Bitfield 구조를 활용한 예제 코드를 제공하여, 초보자부터 숙련된 개발자까지 쉽게 활용할 수 있도록 구성했습니다. IPC를 통한 데이터 전송, CAN 제어, ADC 데이터 공유, PWM 제어를 포함한 병렬 작업 예제를 통해 다양한 멀티코어 애플리케이션에 적용 가능합니다.

키워드: TMS320F28377D, 멀티코어, CPU1, CPU2, IPC, C2000, 마이크로컨트롤러, Code Composer Studio, CAN, ADC, PWM, Bitfield, 공유 RAM, 인터럽트

반응형