본문 바로가기
MCU/C2000

[TMS320F28388D] CPU1, CPU2 멀티코어 사용법: DriverLib API로 멀티코어 설정 및 코드

by linuxgo 2025. 8. 17.
반응형

이 문서에서는 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. 멀티코어 설정 및 동작 원리

  1. CPU1 초기화 : 시스템 클럭, GPIO, 인터럽트 설정, CPU2 부팅 명령.
  2. 공유 메모리 설정 : GSRAM 소유권 및 접근 설정.
  3. IPC 설정 : CPU1과 CPU2 간 통신 채널 구성, 인터럽트 활성화.
  4. CPU2 초기화 : CPU2에서 독립적인 주변 장치 및 작업 설정.
  5. 데이터 교환 및 동기화 : 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

반응형