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. 멀티코어 설정 절차
멀티코어 환경을 설정하려면 다음 단계를 따릅니다:
- 시스템 초기화:
- CPU1: InitSysCtrl()로 시스템 클럭 및 PLL 초기화
- CPU2: CPU1에서 부팅 명령 전송 후 초기화
- 메모리 설정:
- MEMCFG로 공유 RAM 소유권 설정
- 필요 시 ACCESS로 읽기/쓰기 권한 설정
- IPC 초기화:
- CPU1과 CPU2 간 IPC 플래그 및 인터럽트 설정
- IPCSENDCOM/IPCRECVCOM으로 명령 교환
- 주변 장치 할당:
- 특정 주변 장치(CAN, ADC 등)를 CPU1 또는 CPU2에 할당
- 인터럽트 설정:
- 각 CPU의 PIE 벡터 테이블 초기화
- IPC 인터럽트 활성화 (예: IPC0_INT)
- 프로그램 실행:
- 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, 인터럽트
'MCU > C2000' 카테고리의 다른 글
TMS320F28377D DSP ePWM CMPASS 사용법: Bitfield 구조 활용 예제 코드 (0) | 2025.08.18 |
---|---|
TMS320F28377D DSP SPI 사용법 : Bitfield 구조 활용 예제 코드 (2) | 2025.08.18 |
TMS320F28377D DSP DMA 사용법 : Bitfield 구조 활용 예제 코드 (0) | 2025.08.18 |
TMS320F28377D DSP CLA 사용법 : Bitfield 구조 활용 예제 코드 (1) | 2025.08.18 |
TMS320F28377D DSP EMIF 사용법 : Bitfield 구조 활용 예제 코드 (1) | 2025.08.17 |
TMS320F28377D DSP eCAP 사용법 : Bitfield 구조 활용 예제 코드 (1) | 2025.08.17 |
TMS320F28377D DSP eQEP 사용법 : Bitfield 구조 활용 예제 코드 (0) | 2025.08.17 |
TMS320F28377D DSP ePWM 사용법 : Bitfield 구조 활용 예제 코드 (1) | 2025.08.17 |