이 문서에서는 Texas Instruments의 TMS320F28388D 마이크로컨트롤러에서 CPU1과 CPU2 멀티코어를 활용하여 작업을 분산 처리하는 방법을 DriverLib API를 사용하여 상세히 설명합니다. TMS320F28388D는 C2000 시리즈의 고성능 32비트 마이크로컨트롤러로, 두 개의 CPU 코어(CPU1, CPU2)를 제공하여 병렬 처리를 지원합니다. 이 문서를 통해 CPU1과 CPU2 간의 통신, 자원 공유, 그리고 멀티코어 애플리케이션 구현 방법을 배우고, Code Composer Studio(CCS) 환경에서 실행 가능한 예제 코드를 통해 실제 구현 방법을 익힐 수 있습니다. 각 코드에는 상세한 주석이 포함되어 있습니다.
1. TMS320F28388D 멀티코어 개요
TMS320F28388D는 두 개의 독립적인 32비트 C28x CPU 코어(CPU1, CPU2)를 포함하며, 각 코어는 최대 200MHz로 동작합니다. 이 멀티코어 아키텍처는 복잡한 실시간 제어 애플리케이션에서 작업을 분할하여 성능을 최적화하는 데 적합합니다.
멀티코어 아키텍처의 주요 특징
- 독립적인 CPU 코어 : CPU1과 CPU2는 각각 독립적인 프로그램 메모리, 데이터 메모리, 주변 장치를 가짐.
- 공유 메모리 : Global Shared RAM(GSRAM)과 Message RAM을 통해 CPU 간 데이터 교환 가능.
- 인터프로세서 통신(IPC) : IPC 모듈을 통해 CPU1과 CPU2 간 통신 및 동기화 지원.
- 주변 장치 접근 : CPU1은 모든 주변 장치에 접근 가능, CPU2는 제한된 주변 장치 접근(예: 일부 ePWM, ADC).
- 클럭 : 시스템 클럭(최대 200MHz)을 공유하며, 각 코어는 독립적으로 동작.
- 인터럽트 : 각 CPU는 독립적인 인터럽트 컨트롤러(PIE)를 가짐.
DriverLib API는 멀티코어 설정을 간소화하며, IPC 및 공유 메모리 관련 함수를 제공하여 CPU 간 통신을 쉽게 구현할 수 있습니다.
2. 주요 멀티코어 DriverLib API 함수
아래는 TMS320F28388D의 CPU1과 CPU2 간 통신 및 멀티코어 설정을 위해 자주 사용되는 DriverLib API 함수와 그 사용 방법입니다.
2.1. IPC 관련 함수
- IPC_sendCommand(base, command, mask, data)
- 설명 : CPU1 또는 CPU2에서 상대 CPU로 명령 전송.
- 매개변수 : base (IPC 채널, 예: IPC_CPU1_L_CPU2_R), command (IPC 명령), mask (플래그), data (전송 데이터).
- 사용 예 : CPU1에서 CPU2로 데이터 전송 요청.
- IPC_readCommand(base, command, mask, data)
- 설명 : 수신된 IPC 명령 읽기.
- 매개변수 : base, command, mask, data.
- IPC_setFlag(base, flag)
- 설명 : IPC 플래그 설정(예: 인터럽트 요청).
- 매개변수 : flag (예: IPC_FLAG0).
- IPC_clearFlag(base, flag)
- 설명 : IPC 플래그 지우기.
- IPC_registerInterrupt(base, intNumber, handler)
- 설명 : IPC 인터럽트 서비스 루틴(ISR) 등록.
2.2. 공유 메모리 관련 함수
- SysCtl_mapSharedRAM(owner, ramSection)
- 설명 : 특정 공유 RAM 섹션의 소유권 설정(CPU1 또는 CPU2).
- 매개변수 : owner (SYSCTL_ACCESS_CPU1/CPU2), ramSection (예: SYSCTL_GS0).
- SysCtl_enableSharedRAMAccess(ramSection)
- 설명 : 공유 RAM 접근 활성화.
- SysCtl_disableSharedRAMAccess(ramSection)
- 설명 : 공유 RAM 접근 비활성화.
2.3. CPU2 부팅 및 제어
- SysCtl_bootCPU2(bootMode)
- 설명 : CPU2를 부팅하고 실행 시작.
- 매개변수 : bootMode (예: SYSCTL_BOOT_FLASH, SYSCTL_BOOT_RAM).
- SysCtl_resetCPU2()
- 설명 : CPU2 리셋.
- SysCtl_haltCPU2()
- 설명 : CPU2 실행 중지.
2.4. 인터럽트 및 주변 장치 설정
- Interrupt_initModule() : 각 CPU의 PIE 초기화.
- SysCtl_enablePeripheral(periph) : CPU1 또는 CPU2에서 특정 주변 장치 활성화.
- GPIO_setPinConfig(pinConfig) : GPIO 멀티플렉싱 설정(CPU1 또는 CPU2에서 독립적).
3. 멀티코어 설정 및 동작 원리
- CPU1 초기화 : 시스템 클럭, GPIO, 인터럽트 설정, CPU2 부팅 명령.
- 공유 메모리 설정 : GSRAM 소유권 및 접근 설정.
- IPC 설정 : CPU1과 CPU2 간 통신 채널 구성, 인터럽트 활성화.
- CPU2 초기화 : CPU2에서 독립적인 주변 장치 및 작업 설정.
- 데이터 교환 및 동기화 : IPC 또는 공유 메모리를 통해 데이터 및 제어 신호 교환.
4. 멀티코어 예제 코드
아래는 CPU1과 CPU2를 활용한 독립적인 예제 코드로, C2000Ware의 DriverLib를 기반으로 작성되었습니다. 각 코드는 CPU1과 CPU2용으로 분리되어 있으며, CCS에서 멀티코어 프로젝트로 빌드 가능합니다. 두 CPU는 IPC와 공유 메모리를 통해 통신합니다. 모든 코드에는 상세한 주석이 포함되어 있습니다.
4.1. 예제 1: CPU1과 CPU2 간 기본 IPC 통신
CPU1에서 CPU2로 메시지를 전송하고, CPU2는 메시지를 수신하여 LED를 제어합니다.
CPU1 코드
#include "driverlib.h"
#include "device.h"
// 디버깅용 상태 변수 (0: 초기화 완료, 1: 동작 중, 0xFFFF: 오류)
volatile uint32_t cpu1Status = 0;
// 공유 메모리 정의 (GS0 RAM, CPU1과 CPU2가 공유)
uint32_t *sharedData = (uint32_t *)0x0003F800;
// GPIO 초기화 함수
void initGPIO(void)
{
// GPIO31을 일반 GPIO로 설정 (디버깅용 LED)
GPIO_setPinConfig(GPIO_31_GPIO31);
// GPIO31을 출력 모드로 설정
GPIO_setDirectionMode(31, GPIO_DIR_MODE_OUT);
// GPIO31에 풀업 저항 활성화 (외부 회로에 따라 조정 가능)
GPIO_setPadConfig(31, GPIO_PULL_UP);
// 초기 LED 상태: OFF
GPIO_writePin(31, 0);
}
// IPC 초기화 함수
void initIPC(void)
{
// 모든 IPC 플래그 초기화 (CPU1 -> CPU2)
IPC_clearFlag(IPC_CPU1_L_CPU2_R, IPC_FLAG_ALL);
// CPU2로 초기 명령 전송 (데이터: 0x1234)
IPC_sendCommand(IPC_CPU1_L_CPU2_R, IPC_FLAG0, IPC_ADDR_CORRECTION_ENABLE, 0x1234);
}
// CPU2 부팅 함수
void bootCPU2(void)
{
// CPU2를 플래시 메모리에서 부팅
SysCtl_bootCPU2(SYSCTL_BOOT_FLASH);
// CPU2 부팅 완료 대기
while (!SysCtl_isCPU2BootComplete())
{
// 1ms 지연
DEVICE_DELAY_US(1000);
}
}
void main(void)
{
// 시스템 클럭, PLL, Watchdog, 주변 장치 초기화
Device_init();
// 기본 GPIO 초기화 (C2000Ware 제공)
Device_initGPIO();
// 사용자 정의 GPIO 설정
initGPIO();
// 인터럽트 컨트롤러(PIE) 및 벡터 테이블 초기화
Interrupt_initModule();
Interrupt_initVectorTable();
// 시스템 클럭 확인 (200MHz 예상)
uint32_t sysClockHz = SysCtl_getClock(DEVICE_OSCSRC_FREQ);
if (sysClockHz != 200000000)
{
// 클럭 오류 시 상태 업데이트
cpu1Status = 0xFFFF;
// LED OFF로 오류 표시
GPIO_writePin(31, 0);
// 디버깅용 정지
ESTOP0;
}
// 공유 메모리 소유권을 CPU1로 설정 (GS0 RAM)
SysCtl_mapSharedRAM(SYSCTL_ACCESS_CPU1, SYSCTL_GS0);
// 공유 메모리 접근 활성화
SysCtl_enableSharedRAMAccess(SYSCTL_GS0);
// 공유 메모리에 초기 데이터 쓰기
*sharedData = 0xABCD;
// IPC 초기화 및 CPU2 부팅
initIPC();
bootCPU2();
// 상태 업데이트: 정상 동작
cpu1Status = 1;
// 글로벌 인터럽트 활성화
EINT;
// Watchdog 활성화 (실시간 애플리케이션용)
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_WD);
SysCtl_serviceWatchdog();
// 무한 루프
for(;;)
{
// 디버깅용: 상태에 따라 LED 제어 (정상: ON, 오류: OFF)
GPIO_writePin(31, (cpu1Status == 1) ? 1 : 0);
// 1초 대기 (CPU 부하 감소)
DEVICE_DELAY_US(1000000);
// 공유 메모리 데이터 증가
*sharedData += 1;
// CPU2로 데이터 전송 (IPC_FLAG0 사용)
IPC_sendCommand(IPC_CPU1_L_CPU2_R, IPC_FLAG0, IPC_ADDR_CORRECTION_ENABLE, *sharedData);
}
}
CPU2 코드
#include "driverlib.h"
#include "device.h"
// 디버깅용 상태 변수 (0: 초기화 완료, 1: 동작 중, 0xFFFF: 오류)
volatile uint32_t cpu2Status = 0;
// 공유 메모리 정의 (GS0 RAM, CPU1과 공유)
uint32_t *sharedData = (uint32_t *)0x0003F800;
// IPC 인터럽트 서비스 루틴
__interrupt void ipcISR(void)
{
uint32_t command, data;
// CPU1에서 전송된 IPC 명령 읽기
IPC_readCommand(IPC_CPU2_L_CPU1_R, &command, IPC_ADDR_CORRECTION_ENABLE, &data);
// IPC 플래그 지우기 (IPC_FLAG0)
IPC_clearFlag(IPC_CPU2_L_CPU1_R, IPC_FLAG0);
// PIE 인터럽트 그룹 ACK
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
// 공유 메모리 데이터와 IPC 데이터 비교, 상태 업데이트
cpu2Status = (data == *sharedData) ? 1 : 0xFFFF;
}
// GPIO 초기화 함수
void initGPIO(void)
{
// GPIO34를 일반 GPIO로 설정 (디버깅용 LED, CPU2 제어)
GPIO_setPinConfig(GPIO_34_GPIO34);
// GPIO34를 출력 모드로 설정
GPIO_setDirectionMode(34, GPIO_DIR_MODE_OUT);
// GPIO34에 풀업 저항 활성화
GPIO_setPadConfig(34, GPIO_PULL_UP);
// 초기 LED 상태: OFF
GPIO_writePin(34, 0);
}
void main(void)
{
// CPU2 시스템 초기화 (클럭, PLL, Watchdog 등)
Device_init();
// 기본 GPIO 초기화 (C2000Ware 제공)
Device_initGPIO();
// 사용자 정의 GPIO 설정
initGPIO();
// 인터럽트 컨트롤러(PIE) 및 벡터 테이블 초기화
Interrupt_initModule();
Interrupt_initVectorTable();
// IPC 인터럽트 등록 (IPC_INT0에 ISR 연결)
Interrupt_register(IPC_INT0, &ipcISR);
// 공유 메모리 접근 활성화 (GS0 RAM)
SysCtl_enableSharedRAMAccess(SYSCTL_GS0);
// 모든 IPC 플래그 초기화 (CPU2 -> CPU1)
IPC_clearFlag(IPC_CPU2_L_CPU1_R, IPC_FLAG_ALL);
// IPC 인터럽트 활성화 (IPC_FLAG0)
IPC_enableInterrupt(IPC_CPU2_L_CPU1_R, IPC_FLAG0);
// 글로벌 인터럽트 활성화
EINT;
// Watchdog 활성화
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_WD);
SysCtl_serviceWatchdog();
// 무한 루프
for(;;)
{
// 디버깅용: 상태에 따라 LED 제어 (정상: ON, 오류: OFF)
GPIO_writePin(34, (cpu2Status == 1) ? 1 : 0);
// 1초 대기
DEVICE_DELAY_US(1000000);
}
}
설명:
- 기능 : CPU1에서 공유 메모리에 데이터를 쓰고 IPC를 통해 CPU2로 전송. CPU2는 수신 데이터를 확인하여 LED 제어.
- 설정 : GS0 RAM을 공유 메모리로 설정, CPU1이 데이터 쓰기, CPU2가 읽기 및 검증.
- GPIO : CPU1(GPIO31, LED), CPU2(GPIO34, LED).
- 결과 : CPU1과 CPU2가 데이터를 공유하며 동기화, 정상 동작 시 각 CPU의 LED ON.
- 용도 : CPU 간 데이터 교환 및 기본적인 멀티코어 동기화 테스트.
4.2. 예제 2: CPU1(ePWM)과 CPU2(ADC) 협력
CPU1은 ePWM1을 제어하여 PWM 신호를 생성하고, CPU2는 ADC를 사용하여 PWM 신호에 따른 아날로그 입력을 측정합니다.
CPU1 코드 (ePWM 제어)
#include "driverlib.h"
#include "device.h"
// 디버깅용 상태 변수 (0: 초기화 완료, 1: 동작 중, 0xFFFF: 오류)
volatile uint32_t cpu1Status = 0;
// 공유 메모리 정의 (GS0 RAM, PWM 듀티 사이클 저장)
volatile uint32_t *pwmDuty = (uint32_t *)0x0003F800;
// GPIO 초기화 함수
void initGPIO(void)
{
// GPIO0을 ePWM1A로 설정 (PWM 출력)
GPIO_setPinConfig(GPIO_0_EPWM1A);
// GPIO0을 출력 모드로 설정
GPIO_setDirectionMode(0, GPIO_DIR_MODE_OUT);
// GPIO0에 풀업 저항 활성화
GPIO_setPadConfig(0, GPIO_PULL_UP);
// GPIO31을 일반 GPIO로 설정 (디버깅용 LED)
GPIO_setPinConfig(GPIO_31_GPIO31);
// GPIO31을 출력 모드로 설정
GPIO_setDirectionMode(31, GPIO_DIR_MODE_OUT);
// GPIO31에 풀업 저항 활성화
GPIO_setPadConfig(31, GPIO_PULL_UP);
// 초기 LED 상태: OFF
GPIO_writePin(31, 0);
}
// ePWM1 초기화 함수
void initEPWM(void)
{
// ePWM1 클럭 활성화
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_EPWM1);
// 주기 설정: 100kHz PWM (200MHz / 2000 = 100kHz)
EPWM_setTimeBasePeriod(EPWM1_BASE, 2000);
// 카운터 모드: Up 카운터
EPWM_setCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP);
// PWM_A 동작 설정: 카운터=0일 때 HIGH, CMPA에서 LOW
EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_A,
EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO, EPWM_AQ_OUTPUT_HIGH);
EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_A,
EPWM_AQ_OUTPUT_ON_TIMEBASE_CMPA_UP, EPWM_AQ_OUTPUT_LOW);
// 초기 듀티 사이클 50% 설정 (CMPA = 1000)
EPWM_setCompareA(EPWM1_BASE, 1000);
// ADC SOCA 트리거 설정: 카운터=0일 때 발생
EPWM_enableADCTrigger(EPWM1_BASE, EPWM_SOC_A);
EPWM_setADCTriggerSource(EPWM1_BASE, EPWM_SOC_A, EPWM_SOC_TBCTR_ZERO);
EPWM_setADCTriggerEventPrescale(EPWM1_BASE, EPWM_SOC_A, 1);
// ePWM 모듈 활성화
EPWM_enableModule(EPWM1_BASE);
}
void main(void)
{
// 시스템 클럭, PLL, Watchdog, 주변 장치 초기화
Device_init();
// 기본 GPIO 초기화 (C2000Ware 제공)
Device_initGPIO();
// 사용자 정의 GPIO 설정
initGPIO();
// 인터럽트 컨트롤러(PIE) 및 벡터 테이블 초기화
Interrupt_initModule();
Interrupt_initVectorTable();
// 공유 메모리 소유권을 CPU1로 설정 (GS0 RAM)
SysCtl_mapSharedRAM(SYSCTL_ACCESS_CPU1, SYSCTL_GS0);
// 공유 메모리 접근 활성화
SysCtl_enableSharedRAMAccess(SYSCTL_GS0);
// 초기 듀티 사이클 저장 (50%, CMPA=1000)
*pwmDuty = 1000;
// 시스템 클럭 확인 (200MHz 예상)
uint32_t sysClockHz = SysCtl_getClock(DEVICE_OSCSRC_FREQ);
if (sysClockHz != 200000000)
{
// 클럭 오류 시 상태 업데이트
cpu1Status = 0xFFFF;
// LED OFF로 오류 표시
GPIO_writePin(31, 0);
// 디버깅용 정지
ESTOP0;
}
// ePWM1 초기화
initEPWM();
// CPU2를 플래시에서 부팅
SysCtl_bootCPU2(SYSCTL_BOOT_FLASH);
// 상태 업데이트: 정상 동작
cpu1Status = 1;
// 글로벌 인터럽트 활성화
EINT;
// Watchdog 활성화
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_WD);
SysCtl_serviceWatchdog();
// 무한 루프
for(;;)
{
// 디버깅용: 상태에 따라 LED 제어
GPIO_writePin(31, (cpu1Status == 1) ? 1 : 0);
// 듀티 사이클 변경: 25%(500) ↔ 75%(1500) 순환
*pwmDuty = (*pwmDuty == 1000) ? 1500 : 500;
EPWM_setCompareA(EPWM1_BASE, *pwmDuty);
// 1초 대기
DEVICE_DELAY_US(1000000);
}
}
CPU2 코드 (ADC 제어)
#include "driverlib.h"
#include "device.h"
// 디버깅용 상태 변수 (0: 초기화 완료, 1: 동작 중, 0xFFFF: 오류)
volatile uint32_t cpu2Status = 0;
// 공유 메모리 정의 (GS0 RAM, PWM 듀티 사이클 읽기)
volatile uint32_t *pwmDuty = (uint32_t *)0x0003F800;
// ADC 결과 저장 버퍼 (256개 샘플)
#define RESULTS_BUFFER_SIZE 256
uint16_t adcResults[RESULTS_BUFFER_SIZE];
volatile uint16_t index = 0;
// ADC 인터럽트 서비스 루틴
__interrupt void adcA1ISR(void)
{
// ADC 변환 결과 읽기 (ADCIN0 채널)
adcResults[index] = ADC_readResult(ADCA_BASE, ADC_SOC_NUMBER0);
// 버퍼 인덱스 증가 및 오버플로우 방지
index = (index + 1) % RESULTS_BUFFER_SIZE;
// ADC 인터럽트 플래그 지우기
ADC_clearInterruptStatus(ADCA_BASE, ADC_INT_NUMBER1);
// PIE 인터럽트 그룹 ACK
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
// 상태 업데이트: 정상 동작
cpu2Status = 1;
}
// GPIO 초기화 함수
void initGPIO(void)
{
// GPIO34를 일반 GPIO로 설정 (디버깅용 LED)
GPIO_setPinConfig(GPIO_34_GPIO34);
// GPIO34를 출력 모드로 설정
GPIO_setDirectionMode(34, GPIO_DIR_MODE_OUT);
// GPIO34에 풀업 저항 활성화
GPIO_setPadConfig(34, GPIO_PULL_UP);
// 초기 LED 상태: OFF
GPIO_writePin(34, 0);
}
void main(void)
{
// CPU2 시스템 초기화 (클럭, PLL, Watchdog 등)
Device_init();
// 기본 GPIO 초기화 (C2000Ware 제공)
Device_initGPIO();
// 사용자 정의 GPIO 설정
initGPIO();
// 인터럽트 컨트롤러(PIE) 및 벡터 테이블 초기화
Interrupt_initModule();
Interrupt_initVectorTable();
// ADC 설정: 50MHz 클럭, 12비트 해상도, ePWM1_SOCA 트리거
ADC_setPrescaler(ADCA_BASE, ADC_CLK_DIV_4_0);
// 내부 3.3V 기준 전압 설정
ADC_setVREF(ADCA_BASE, ADC_REFERENCE_INTERNAL, ADC_REFERENCE_3_3V);
// 12비트 단일 종단 모드 설정
ADC_setMode(ADCA_BASE, ADC_RESOLUTION_12BIT, ADC_MODE_SINGLE_ENDED);
// SOC0 설정: ePWM1_SOCA 트리거, ADCIN0 채널, 샘플링 15 사이클
ADC_setupSOC(ADCA_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN0, 15);
// SOC0에서 ADC_INT1 트리거 설정
ADC_setInterruptSOCTrigger(ADCA_BASE, ADC_SOC_NUMBER0, ADC_INT_NUMBER1);
// ADC 인터럽트 활성화
ADC_enableInterrupt(ADCA_BASE, ADC_INT_NUMBER1);
// ADC 변환기 활성화
ADC_enableConverter(ADCA_BASE);
// ADC 안정화를 위한 1ms 지연
DEVICE_DELAY_US(1000);
// 공유 메모리 접근 활성화 (GS0 RAM)
SysCtl_enableSharedRAMAccess(SYSCTL_GS0);
// 글로벌 인터럽트 활성화
EINT;
// Watchdog 활성화
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_WD);
SysCtl_serviceWatchdog();
// 무한 루프
for(;;)
{
// 디버깅용: 상태에 따라 LED 제어
GPIO_writePin(34, (cpu2Status == 1) ? 1 : 0);
// 1초 대기
DEVICE_DELAY_US(1000000);
}
}
설명:
- 기능 : CPU1은 ePWM1으로 100kHz PWM 신호 생성, CPU2는 ePWM1의 SOCA 트리거로 ADC 변환 수행.
- 설정 : CPU1은 ePWM1 설정 및 듀티 사이클 변경, CPU2는 ADC 변환 결과 저장.
- 공유 메모리 : GS0 RAM에 PWM 듀티 사이클 저장.
- GPIO : CPU1(GPIO0: ePWM1A, GPIO31: LED), CPU2(GPIO34: LED).
- 결과 : CPU1이 PWM 신호 생성, CPU2가 ADC로 입력 측정, 정상 동작 시 LED ON.
- 용도 : 모터 제어 시스템에서 PWM 제어와 센서 데이터 수집 분리.
4.3. 예제 3: CPU1과 CPU2 간 동기화된 ePWM 제어
CPU1과 CPU2가 각각 ePWM 모듈을 제어하며, IPC로 동기화하여 동일한 듀티 사이클을 유지합니다.
CPU1 코드
#include "driverlib.h"
#include "device.h"
// 디버깅용 상태 변수 (0: 초기화 완료, 1: 동작 중, 0xFFFF: 오류)
volatile uint32_t cpu1Status = 0;
// 공유 메모리 정의 (GS0 RAM, PWM 듀티 사이클 저장)
volatile uint32_t *pwmDuty = (uint32_t *)0x0003F800;
// GPIO 초기화 함수
void initGPIO(void)
{
// GPIO0을 ePWM1A로 설정 (PWM 출력)
GPIO_setPinConfig(GPIO_0_EPWM1A);
// GPIO0을 출력 모드로 설정
GPIO_setDirectionMode(0, GPIO_DIR_MODE_OUT);
// GPIO0에 풀업 저항 활성화
GPIO_setPadConfig(0, GPIO_PULL_UP);
// GPIO31을 일반 GPIO로 설정 (디버깅용 LED)
GPIO_setPinConfig(GPIO_31_GPIO31);
// GPIO31을 출력 모드로 설정
GPIO_setDirectionMode(31, GPIO_DIR_MODE_OUT);
// GPIO31에 풀업 저항 활성화
GPIO_setPadConfig(31, GPIO_PULL_UP);
// 초기 LED 상태: OFF
GPIO_writePin(31, 0);
}
// ePWM1 초기화 함수
void initEPWM(void)
{
// ePWM1 클럭 활성화
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_EPWM1);
// 주기 설정: 100kHz PWM (200MHz / 2000 = 100kHz)
EPWM_setTimeBasePeriod(EPWM1_BASE, 2000);
// 카운터 모드: Up 카운터
EPWM_setCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP);
// PWM_A 동작 설정: 카운터=0일 때 HIGH, CMPA에서 LOW
EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_A,
EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO, EPWM_AQ_OUTPUT_HIGH);
EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_A,
EPWM_AQ_OUTPUT_ON_TIMEBASE_CMPA_UP, EPWM_AQ_OUTPUT_LOW);
// 초기 듀티 사이클 50% 설정 (CMPA = 1000)
EPWM_setCompareA(EPWM1_BASE, 1000);
// ePWM 모듈 활성화
EPWM_enableModule(EPWM1_BASE);
}
void main(void)
{
// 시스템 클럭, PLL, Watchdog, 주변 장치 초기화
Device_init();
// 기본 GPIO 초기화 (C2000Ware 제공)
Device_initGPIO();
// 사용자 정의 GPIO 설정
initGPIO();
// 인터럽트 컨트롤러(PIE) 및 벡터 테이블 초기화
Interrupt_initModule();
Interrupt_initVectorTable();
// 공유 메모리 소유권을 CPU1로 설정 (GS0 RAM)
SysCtl_mapSharedRAM(SYSCTL_ACCESS_CPU1, SYSCTL_GS0);
// 공유 메모리 접근 활성화
SysCtl_enableSharedRAMAccess(SYSCTL_GS0);
// 초기 듀티 사이클 저장 (50%, CMPA=1000)
*pwmDuty = 1000;
// 시스템 클럭 확인 (200MHz 예상)
uint32_t sysClockHz = SysCtl_getClock(DEVICE_OSCSRC_FREQ);
if (sysClockHz != 200000000)
{
// 클럭 오류 시 상태 업데이트
cpu1Status = 0xFFFF;
// LED OFF로 오류 표시
GPIO_writePin(31, 0);
// 디버깅용 정지
ESTOP0;
}
// ePWM1 초기화
initEPWM();
// CPU2를 플래시에서 부팅
SysCtl_bootCPU2(SYSCTL_BOOT_FLASH);
// 상태 업데이트: 정상 동작
cpu1Status = 1;
// 글로벌 인터럽트 활성화
EINT;
// Watchdog 활성화
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_WD);
SysCtl_serviceWatchdog();
// 무한 루프
for(;;)
{
// 디버깅용: 상태에 따라 LED 제어
GPIO_writePin(31, (cpu1Status == 1) ? 1 : 0);
// 듀티 사이클 변경: 25%(500) ↔ 75%(1500) 순환
*pwmDuty = (*pwmDuty == 1000) ? 1500 : 500;
// ePWM1 듀티 사이클 업데이트
EPWM_setCompareA(EPWM1_BASE, *pwmDuty);
// CPU2로 듀티 사이클 전송 (IPC_FLAG0 사용)
IPC_sendCommand(IPC_CPU1_L_CPU2_R, IPC_FLAG0, IPC_ADDR_CORRECTION_ENABLE, *pwmDuty);
// 1초 대기
DEVICE_DELAY_US(1000000);
}
}
CPU2 코드
#include "driverlib.h"
#include "device.h"
// 디버깅용 상태 변수 (0: 초기화 완료, 1: 동작 중, 0xFFFF: 오류)
volatile uint32_t cpu2Status = 0;
// 공유 메모리 정의 (GS0 RAM, PWM 듀티 사이클 읽기)
volatile uint32_t *pwmDuty = (uint32_t *)0x0003F800;
// IPC 인터럽트 서비스 루틴
__interrupt void ipcISR(void)
{
uint32_t command, data;
// CPU1에서 전송된 IPC 명령 읽기
IPC_readCommand(IPC_CPU2_L_CPU1_R, &command, IPC_ADDR_CORRECTION_ENABLE, &data);
// ePWM2 듀티 사이클 업데이트
EPWM_setCompareA(EPWM2_BASE, data);
// IPC 플래그 지우기 (IPC_FLAG0)
IPC_clearFlag(IPC_CPU2_L_CPU1_R, IPC_FLAG0);
// PIE 인터럽트 그룹 ACK
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
// 공유 메모리 데이터와 IPC 데이터 비교, 상태 업데이트
cpu2Status = (data == *pwmDuty) ? 1 : 0xFFFF;
}
// GPIO 초기화 함수
void initGPIO(void)
{
// GPIO2을 ePWM2A로 설정 (PWM 출력)
GPIO_setPinConfig(GPIO_2_EPWM2A);
// GPIO2를 출력 모드로 설정
GPIO_setDirectionMode(2, GPIO_DIR_MODE_OUT);
// GPIO2에 풀업 저항 활성화
GPIO_setPadConfig(2, GPIO_PULL_UP);
// GPIO34를 일반 GPIO로 설정 (디버깅용 LED)
GPIO_setPinConfig(GPIO_34_GPIO34);
// GPIO34를 출력 모드로 설정
GPIO_setDirectionMode(34, GPIO_DIR_MODE_OUT);
// GPIO34에 풀업 저항 활성화
GPIO_setPadConfig(34, GPIO_PULL_UP);
// 초기 LED 상태: OFF
GPIO_writePin(34, 0);
}
// ePWM2 초기화 함수
void initEPWM(void)
{
// ePWM2 클럭 활성화
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_EPWM2);
// 주기 설정: 100kHz PWM (200MHz / 2000 = 100kHz)
EPWM_setTimeBasePeriod(EPWM2_BASE, 2000);
// 카운터 모드: Up 카운터
EPWM_setCounterMode(EPWM2_BASE, EPWM_COUNTER_MODE_UP);
// PWM_A 동작 설정: 카운터=0일 때 HIGH, CMPA에서 LOW
EPWM_setActionQualifierAction(EPWM2_BASE, EPWM_AQ_OUTPUT_A,
EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO, EPWM_AQ_OUTPUT_HIGH);
EPWM_setActionQualifierAction(EPWM2_BASE, EPWM_AQ_OUTPUT_A,
EPWM_AQ_OUTPUT_ON_TIMEBASE_CMPA_UP, EPWM_AQ_OUTPUT_LOW);
// 초기 듀티 사이클 설정 (공유 메모리에서 읽음)
EPWM_setCompareA(EPWM2_BASE, *pwmDuty);
// ePWM 모듈 활성화
EPWM_enableModule(EPWM2_BASE);
}
void main(void)
{
// CPU2 시스템 초기화 (클럭, PLL, Watchdog 등)
Device_init();
// 기본 GPIO 초기화 (C2000Ware 제공)
Device_initGPIO();
// 사용자 정의 GPIO 설정
initGPIO();
// 인터럽트 컨트롤러(PIE) 및 벡터 테이블 초기화
Interrupt_initModule();
Interrupt_initVectorTable();
// IPC 인터럽트 등록 (IPC_INT0에 ISR 연결)
Interrupt_register(IPC_INT0, &ipcISR);
// 공유 메모리 접근 활성화 (GS0 RAM)
SysCtl_enableSharedRAMAccess(SYSCTL_GS0);
// ePWM2 초기화
initEPWM();
// 모든 IPC 플래그 초기화 (CPU2 -> CPU1)
IPC_clearFlag(IPC_CPU2_L_CPU1_R, IPC_FLAG_ALL);
// IPC 인터럽트 활성화 (IPC_FLAG0)
IPC_enableInterrupt(IPC_CPU2_L_CPU1_R, IPC_FLAG0);
// 글로벌 인터럽트 활성화
EINT;
// Watchdog 활성화
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_WD);
SysCtl_serviceWatchdog();
// 무한 루프
for(;;)
{
// 디버깅용: 상태에 따라 LED 제어
GPIO_writePin(34, (cpu2Status == 1) ? 1 : 0);
// 1초 대기
DEVICE_DELAY_US(1000000);
}
}
설명:
- 기능 : CPU1과 CPU2가 각각 ePWM1, ePWM2를 제어하며, IPC로 듀티 사이클 동기화.
- 설정 : CPU1은 ePWM1 제어 및 듀티 사이클 전송, CPU2는 ePWM2 제어 및 IPC로 수신.
- 공유 메모리 : GS0 RAM에 듀티 사이클 저장.
- GPIO : CPU1(GPIO0: ePWM1A, GPIO31: LED), CPU2(GPIO2: ePWM2A, GPIO34: LED).
- 결과 : 두 CPU의 PWM 신호가 동기화된 듀티 사이클로 출력, 정상 동작 시 LED ON.
- 용도 : 다상 모터 제어, 병렬 PWM 제어.
5. 추가 고려 사항
- 공유 메모리 관리 : GSRAM은 CPU1과 CPU2 간 데이터 교환에 필수. 소유권 충돌 방지를 위해 SysCtl_mapSharedRAM으로 명확히 설정.
- IPC 동기화 : IPC 플래그 및 인터럽트를 활용하여 신뢰성 있는 통신 보장.
- CPU2 주변 장치 제한 : CPU2는 일부 주변 장치(예: 특정 ePWM 모듈)에만 접근 가능. 설계 시 확인 필요.
- 부팅 순서 : CPU1이 CPU2를 부팅하며, 부팅 완료를 확인해야 함.
- CCS 설정 : 멀티코어 프로젝트는 별도의 CPU1, CPU2 소스 파일로 구성. .syscfg 툴로 주변 장치 및 IPC 설정 가능.
- C2000Ware : 예제 코드는 C2000Ware의 DriverLib 기반. C:\ti\c2000에 설치.
- 디버깅 : CCS의 멀티코어 디버깅 기능을 활용하여 CPU1, CPU2 상태 동시 모니터링.
키워드 : TMS320F28388D, 멀티코어, CPU1, CPU2, IPC, 공유 메모리, DriverLib, C2000, Code Composer Studio, 병렬 처리, 동기화, ePWM, ADC
'MCU > C2000' 카테고리의 다른 글
[TMS320F28377D] EMIF 사용법 : Bitfield 구조 활용 예제 코드 (1) | 2025.08.17 |
---|---|
[TMS320F28377D] eCAP 사용법 : Bitfield 구조 활용 예제 코드 (1) | 2025.08.17 |
[TMS320F28377D] eQEP 사용법 : Bitfield 구조 활용 예제 코드 (0) | 2025.08.17 |
[TMS320F28377D] ePWM 사용법 : Bitfield 구조 활용 예제 코드 (1) | 2025.08.17 |
[TMS320F28388D] DMA 사용법: DriverLib API로 DMA 설정 및 코드 (0) | 2025.08.17 |
[TMS320F28388D] SPI 사용법: DriverLib API로 SPI 설정 및 코드 (0) | 2025.08.17 |
[TMS320F28388D] CAN 사용법: DriverLib API로 CAN 설정 및 코드 (0) | 2025.08.17 |
[TMS320F28388D] SDFM 사용법: DriverLib API로 SDFM 설정 및 코드 (0) | 2025.08.17 |