본문 바로가기
MCU/C2000

TMS320F28377D DSP Half-Bridge PWM 설정: 비트필드 예제

by linuxgo 2025. 8. 5.

TMS320F28377D는 Texas Instruments의 C2000 시리즈 마이크로컨트롤러로, 200MHz 시스템 클럭을 지원하며 모터 제어, 인버터, 전력 변환 애플리케이션에 최적화되어 있습니다. 하프-브릿지 PWM(Pulse Width Modulation)은 상단 및 하단 MOSFET을 제어하여 효율적인 전력 전달을 구현하며, 데드밴드(dead-band) 설정으로 슈트-스루(shoot-through)를 방지합니다.  비트필드 구조체를 사용한 하프-브릿지 PWM 설정 예제 코드를 제공하며, 상세한 주석으로 초보자와 전문가 모두에게 유용한 정보를 제공합니다. 

하프-브릿지 PWM의 필요성

하프-브릿지 회로는 전력 전자 시스템에서 필수적이며, PWM 신호를 통해 출력 전압을 정밀하게 제어합니다. TMS320F28377DePWM 모듈은 고속 스위칭과 정밀한 타이밍 제어를 지원하며, 데드밴드 설정으로 회로 안정성을 보장합니다. 이 글에서는 20kHz PWM 주파수, 50% 듀티 사이클, 1us 데드밴드를 기준으로 설정 방법을 설명합니다.

하프-브릿지 PWM

 

하프-브릿지 PWM 코드

아래는 TMS320F28377DePWM1 모듈을 사용한 하프-브릿지 PWM 설정 코드입니다. 시스템 클럭을 200MHz로 설정하고, F28x_Project.h 헤더 파일을 사용하여 비트필드 구조체로 레지스터를 구성했습니다. 각 설정은 상세한 주석으로 설명됩니다.

TMS320F28377D DSP Half-Bridge PWM

  • 시스템 클럭: 200MHz로 수정(TMS320F28377D의 기본 PLL 설정 기준).
  • PWM 주기(TBPRD): 20kHz 주파수를 위해 TBPRD = 5000으로 설정.
    • 계산: 주기 = (TBPRD * 2) / 200MHz = 50us → 주파수 = 20kHz.
  • 듀티 사이클(CMPA, CMPB): 50% 듀티 사이클을 위해 CMPA = CMPB = 2500(TBPRD / 2).
  • 데드밴드(DBRED, DBFED): 1us 지연을 위해 DBRED = DBFED = 200(200MHz * 1us).

1. 시스템 초기화

  • InitSysCtrl(): 200MHz 시스템 클럭, PLL, 워치독 타이머를 초기화합니다.
  • DINT와 EINT: 초기화 중 인터럽트를 비활성화하고, 완료 후 활성화합니다.

2. GPIO 설정

  • GPIO0과 GPIO1: 각각 ePWM1A ePWM1B로 설정되어 하프-브릿지 회로의 상단 및 하단 MOSFET을 제어합니다.
  • EALLOW와 EDIS: 보호된 레지스터 접근을 제어합니다.

3. ePWM 모듈 설정

  • 시간 기준(Time-Base): 업-다운 카운트 모드로 대칭 PWM 신호 생성. TBPRD = 5000으로 20kHz 설정.
  • 비교 레지스터(CMPA, CMPB): 50% 듀티 사이클을 위해 CMPA = CMPB = 2500.
  • 액션-퀄리파이어(AQ): ePWM1A와 ePWM1B를 보완 출력으로 설정.
  • 데드밴드(DB): 1us 지연(DBRED = DBFED = 200)으로 슈트-스루 방지.

 

/**
 * main.c - TMS320F28377D 하프-브릿지 PWM 예제 (가변 PWM 및 듀티 사이클 동적 제어)
 *
 * 이 코드는 ePWM1 모듈을 하프-브릿지 PWM 동작으로 설정하며, MOSFET 드라이버의 슈트-스루(shoot-through)를 방지하기 위해 데드밴드 제어를 포함합니다.
 * - PWM 주파수, 듀티 사이클, 데드밴드는 사용자 정의 변수로 계산되어 가변 설정 가능
 * - 메인 루프에서 듀티 사이클을 동적으로 변경 (최소 10% ~ 최대 90% 사이에서 스위핑)
 * - 시스템 클럭: 200MHz (ePWM 모듈의 TBCLK: 100MHz)
 * - GPIO 설정: GPIO0 (ePWM1A - 상단 MOSFET), GPIO1 (ePWM1B - 하단 MOSFET)
 *
 * 기본 설정 예시:
 * - PWM 주파수: 20kHz
 * - 초기 듀티 사이클: 50%
 * - 데드밴드: 1us
 * - 동적 듀티 사이클: 10% ~ 90%, 0.5% 스텝, 10ms 업데이트 주기
 *
 * Texas Instruments의 C2000Ware 라이브러리와 F28x_Project.h를 기반으로 작성되었습니다.
 * F2837xD_epwm.h 헤더 파일을 참조하여 ePWM 레지스터 설정을 정확히 구성합니다.
 * GPIO 설정에는 DriverLib의 GPIO_SetupPinMux와 GPIO_SetupPinOptions 함수를 사용합니다.
 */

#include "F28x_Project.h"

/**
 * PWM 출력에 사용할 GPIO 핀 정의
 * - PWM_GPIO_A (GPIO0): ePWM1A로 설정되어 상단 MOSFET을 제어
 * - PWM_GPIO_B (GPIO1): ePWM1B로 설정되어 하단 MOSFET을 제어
 */
#define PWM_GPIO_A    0  // GPIO0: ePWM1A (상단 MOSFET)
#define PWM_GPIO_B    1  // GPIO1: ePWM1B (하단 MOSFET)

/**
 * PWM 구성용 사용자 정의 변수
 * - PWM_FREQ_HZ: 원하는 PWM 주파수 (Hz 단위, 예: 20000Hz = 20kHz)
 * - DUTY_CYCLE_PERCENT: 초기 듀티 사이클 (%, 예: 50%)
 * - DEAD_BAND_US: 원하는 데드밴드 시간 (us 단위, 예: 1us)
 */
#define PWM_FREQ_HZ         20000   // PWM 주파수: 20kHz
#define DUTY_CYCLE_PERCENT  50.0f   // 초기 듀티 사이클: 50%
#define DEAD_BAND_US        1.0f    // 데드밴드: 1us

/**
 * 동적 듀티 사이클 제어용 변수
 * - MIN_DUTY_PERCENT: 듀티 사이클 최소값 (%, 예: 10%)
 * - MAX_DUTY_PERCENT: 듀티 사이클 최대값 (%, 예: 90%)
 * - DUTY_STEP_PERCENT: 듀티 사이클 변경 스텝 (%, 예: 0.5%)
 * - UPDATE_DELAY_MS: 듀티 사이클 업데이트 주기 (ms 단위, 예: 10ms)
 */
#define MIN_DUTY_PERCENT    10.0f   // 듀티 사이클 최소: 10%
#define MAX_DUTY_PERCENT    90.0f   // 듀티 사이클 최대: 90%
#define DUTY_STEP_PERCENT   0.5f    // 듀티 사이클 스텝: 0.5%
#define UPDATE_DELAY_MS     10      // 업데이트 주기: 10ms

/**
 * 시스템 클럭 및 TBCLK 설정
 * - SYSCLK_HZ: 시스템 클럭 주파수 (200MHz)
 * - TBCLK_HZ: ePWM 시간 기준 클럭 (SYSCLK / 2 = 100MHz, CLKDIV=2)
 */
#define SYSCLK_HZ  200000000UL  // 시스템 클럭: 200MHz
#define TBCLK_HZ   (SYSCLK_HZ / 2)  // TBCLK: 100MHz (CLKDIV=2)

/**
 * PWM 구성 상수 계산
 * - PWM_PERIOD: TBPRD 값, 업-다운 카운트 모드에서 주파수 계산
 *   계산: TBPRD = TBCLK / (2 * PWM_FREQ_HZ)
 *         예: 100MHz / (2 * 20kHz) = 2500
 * - PWM_DUTY: CMPA/CMPB 값, 초기 듀티 사이클 계산
 *   계산: CMPA = TBPRD * (DUTY_CYCLE_PERCENT / 100)
 *         예: 2500 * (50 / 100) = 1250
 * - DEAD_BAND_T: DBRED/DBFED 값, 데드밴드 시간 계산
 *   계산: DBRED/DBFED = DEAD_BAND_US * TBCLK
 *         예: 1us * 100MHz = 100
 */
#define PWM_PERIOD  ((uint16_t)(TBCLK_HZ / (2 * PWM_FREQ_HZ)))  // TBPRD: 주기 계산
#define PWM_DUTY    ((uint16_t)(PWM_PERIOD * (DUTY_CYCLE_PERCENT / 100.0f)))  // CMPA/CMPB: 초기 듀티 계산
#define DEAD_BAND_T ((uint16_t)(DEAD_BAND_US * (TBCLK_HZ / 1000000.0f)))  // DBRED/DBFED: 데드밴드 계산

/**
 * PWM 출력용 GPIO 설정 함수
 * GPIO0와 GPIO1을 각각 ePWM1A와 ePWM1B로 구성합니다.
 * - GPIO_SetupPinMux: GPIO 핀을 ePWM 기능으로 멀티플렉싱
 *   - 첫 번째 인자: GPIO 번호 (0 또는 1)
 *   - 두 번째 인자: CPU 코어 (GPIO_MUX_CPU1 = CPU1)
 *   - 세 번째 인자: MUX 옵션 (0: ePWM1A/B)
 * - GPIO_SetupPinOptions: GPIO 핀 옵션 설정
 *   - 첫 번째 인자: GPIO 번호
 *   - 두 번째 인자: 출력 모드 (GPIO_OUTPUT)
 *   - 세 번째 인자: 푸시-풀 출력 (GPIO_PUSHPULL)
 * - EALLOW/EDIS: 보호된 레지스터 접근 제어
 */
void setupPwmGpio(void)
{
    EALLOW;  // 보호된 레지스터 접근 허용 (GPIO 설정을 위해 필요)

    // GPIO0을 ePWM1A로 설정
    // GPIO_SetupPinMux: GPIO0을 ePWM1A 기능으로 멀티플렉싱 (MUX = 0)
    GPIO_SetupPinMux(PWM_GPIO_A, GPIO_MUX_CPU1, 0);
    // GPIO_SetupPinOptions: GPIO0을 출력 모드, 푸시-풀 출력으로 설정
    GPIO_SetupPinOptions(PWM_GPIO_A, GPIO_OUTPUT, GPIO_PUSHPULL);

    // GPIO1을 ePWM1B로 설정
    // GPIO_SetupPinMux: GPIO1을 ePWM1B 기능으로 멀티플렉싱 (MUX = 0)
    GPIO_SetupPinMux(PWM_GPIO_B, GPIO_MUX_CPU1, 0);
    // GPIO_SetupPinOptions: GPIO1을 출력 모드, 푸시-풀 출력으로 설정
    GPIO_SetupPinOptions(PWM_GPIO_B, GPIO_OUTPUT, GPIO_PUSHPULL);

    EDIS;  // 보호된 레지스터 접근 비활성화
}

/**
 * ePWM1을 하프-브릿지 동작으로 초기화하는 함수
 * - 업-다운 카운트 모드로 대칭 PWM 생성
 * - 사용자 정의 주파수(PWM_FREQ_HZ), 초기 듀티 사이클(DUTY_CYCLE_PERCENT), 데드밴드(DEAD_BAND_US) 적용
 * - F2837xD_epwm.h에 정의된 레지스터와 비트필드를 참조하여 정확히 구성
 */
void setupEPwm1(void)
{
    EALLOW;  // 보호된 레지스터 접근 허용 (ePWM 설정을 위해 필요)

    // ePWM1 모듈 클럭 활성화
    // CpuSysRegs.PCLKCR2.bit.EPWM1을 1로 설정하여 ePWM1 모듈 활성화
    // 이는 ePWM1에 시스템 클럭을 공급하여 동작을 시작합니다.
    CpuSysRegs.PCLKCR2.bit.EPWM1 = 1;

    // 시간 기준(Time-Base) 구성
    // - CTRMODE: 업-다운 카운트 모드(TB_COUNT_UPDOWN)로 대칭 PWM 생성
    //   (카운터가 0에서 TBPRD까지 올라갔다가 내려오는 방식으로 부드러운 PWM 생성)
    // - PHSEN: 위상 동기화 비활성화(TB_DISABLE), 독립 PWM 동작 (다른 ePWM과 동기화 불필요)
    // - HSPCLKDIV: 고속 클럭 분주기 1(TB_DIV1), 클럭을 그대로 사용
    // - CLKDIV: 저속 클럭 분주기 2(TB_DIV2), TBCLK = 200MHz / 2 = 100MHz
    // - TBPRD: PWM 주기 설정(PWM_PERIOD), 예: 20kHz = 100MHz / (2500 * 2)
    // - SYNCOSEL: 카운터 0에서 동기화 신호 출력(TB_CTR_ZERO), 다른 모듈과 연동 시 사용
    EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN;  // 업-다운 카운트 모드
    EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE;         // 위상 동기화 비활성화
    EPwm1Regs.TBCTL.bit.HSPCLKDIV = TB_DIV1;        // 고속 분주기 /1
    EPwm1Regs.TBCTL.bit.CLKDIV = TB_DIV2;           // 저속 분주기 /2 (TBCLK=100MHz)
    EPwm1Regs.TBPRD = PWM_PERIOD;                   // 계산된 주기 설정
    EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO;     // 카운터 0에서 동기화

    // 비교 레지스터(Counter-Compare) 구성
    // - CMPA: ePWM1A의 초기 듀티 사이클 설정 (PWM_DUTY)
    //   (카운터가 CMPA에 도달할 때 신호 전환)
    // - CMPB: ePWM1B의 초기 듀티 사이클 설정 (PWM_DUTY, 보완 출력)
    //   (ePWM1B는 ePWM1A의 보완 신호로 동작하여 하프-브릿지 구현)
    EPwm1Regs.CMPA.bit.CMPA = PWM_DUTY;             // ePWM1A 초기 듀티 설정
    EPwm1Regs.CMPB.bit.CMPB = PWM_DUTY;             // ePWM1B 초기 듀티 설정

    // 액션-퀄리파이어(Action-Qualifier) 구성
    // - ePWM1A: 카운터 0에서 고전위 설정(AQ_SET), CMPA 상향 카운트에서 저전위(AQ_CLEAR)
    //   (상단 MOSFET: 주기 시작 시 켜지고, 듀티 지점에서 꺼짐)
    // - ePWM1B: 카운터 0에서 저전위 설정(AQ_CLEAR), CMPB 하향 카운트에서 고전위(AQ_SET)
    //   (하단 MOSFET: 주기 시작 시 꺼지고, 하향 듀티 지점에서 켜짐)
    //   -> 상단/하단 MOSFET을 보완적으로 제어하여 하프-브릿지 동작 구현
    EPwm1Regs.AQCTLA.bit.ZRO = AQ_SET;              // 카운터 0에서 ePWM1A 고전위
    EPwm1Regs.AQCTLA.bit.CAU = AQ_CLEAR;            // CMPA 상향에서 ePWM1A 저전위
    EPwm1Regs.AQCTLB.bit.ZRO = AQ_CLEAR;            // 카운터 0에서 ePWM1B 저전위
    EPwm1Regs.AQCTLB.bit.CBD = AQ_SET;              // CMPB 하향에서 ePWM1B 고전위

    // 데드밴드(Deadband) 구성
    // - OUT_MODE: 전체 데드밴드 활성화(DB_FULL_ENABLE)
    //   (ePWM1A와 ePWM1B 모두에 데드밴드 적용)
    // - POLSEL: 활성 고전위 보완(DB_ACTV_HIC), ePWM1B가 ePWM1A의 반전된 신호
    // - IN_MODE: ePWM1A를 입력 소스로 사용(DBA_ALL)
    // - DBRED/DBFED: 계산된 데드밴드 설정(DEAD_BAND_T), 예: 1us = 100
    //   - DBRED: 상승 에지 지연 (ePWM1A 꺼짐 후 ePWM1B 켜짐 지연)
    //   - DBFED: 하강 에지 지연 (ePWM1B 꺼짐 후 ePWM1A 켜짐 지연)
    //   -> 슈트-스루 방지: 상단/하단 MOSFET이 동시에 켜지지 않도록 지연 추가
    EPwm1Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;  // 전체 데드밴드 활성화
    EPwm1Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;       // 활성 고전위 보완
    EPwm1Regs.DBCTL.bit.IN_MODE = DBA_ALL;          // ePWM1A를 소스로 사용
    EPwm1Regs.DBRED.all = DEAD_BAND_T;              // 상승 에지 지연
    EPwm1Regs.DBFED.all = DEAD_BAND_T;              // 하강 에지 지연

    EDIS;  // 보호된 레지스터 접근 비활성화
}

/**
 * 메인 함수
 * - 시스템 초기화: 클럭, PLL, GPIO, 인터럽트
 * - PWM 설정: GPIO 및 ePWM1 초기화 (가변 설정 적용)
 * - 무한 루프: 듀티 사이클을 동적으로 변경 (10% ~ 90% 스위핑)
 *   - 듀티 사이클은 MIN_DUTY_PERCENT와 MAX_DUTY_PERCENT 사이에서 증가/감소
 *   - DUTY_STEP_PERCENT 단위로 변경, UPDATE_DELAY_MS 주기로 업데이트
 */
void main(void)
{
    // 동적 듀티 사이클 제어용 변수
    float current_duty_percent = DUTY_CYCLE_PERCENT;  // 현재 듀티 사이클 (%)
    int duty_direction = 1;                           // 듀티 변경 방향 (1: 증가, -1: 감소)

    // 시스템 초기화
    // - InitSysCtrl(): 200MHz 시스템 클럭 설정, PLL 활성화, 주변 장치 초기화
    //   (외부 크리스탈 20MHz 기준 PLL로 200MHz 생성)
    // - InitGpio(): 모든 GPIO를 기본 상태(입력, 풀업)로 초기화
    InitSysCtrl();  // 시스템 클럭 및 주변 장치 초기화
    InitGpio();     // GPIO 기본 상태로 초기화

    // PWM GPIO 설정
    // - setupPwmGpio(): GPIO0/1을 ePWM1A/B로 구성 (DriverLib 함수 사용)
    setupPwmGpio();

    // 인터럽트 제어 초기화
    // - DINT: 전역 인터럽트 비활성화 (초기화 중 안전 보장)
    // - InitPieCtrl(): PIE(Peripheral Interrupt Expansion) 레지스터 초기화
    // - IER/IFR: CPU 인터럽트 비활성화 및 플래그 클리어
    // - InitPieVectTable(): 인터럽트 벡터 테이블 초기화 (인터럽트 핸들러 매핑)
    DINT;                       // 전역 인터럽트 비활성화
    InitPieCtrl();              // PIE 제어 레지스터 초기화
    IER = 0x0000;               // CPU 인터럽트 비활성화
    IFR = 0x0000;               // CPU 인터럽트 플래그 클리어
    InitPieVectTable();         // PIE 벡터 테이블 초기화

    // ePWM1 설정
    // - setupEPwm1(): ePWM1을 하프-브릿지 PWM으로 구성 (가변 주파수/듀티/데드밴드)
    setupEPwm1();

    // 전역 인터럽트 활성화
    // - EINT: 전역 인터럽트 활성화 (인터럽트 기반 제어 가능)
    // - ERTM: 실시간 인터럽트 활성화 (디버깅 및 실시간 동작 지원)
    EINT;                       // 전역 인터럽트 활성화
    ERTM;                       // 실시간 인터럽트 활성화

    // 메인 루프
    // - PWM은 하드웨어에서 자율적으로 실행
    // - 듀티 사이클을 동적으로 변경: 10%에서 90%로 증가, 다시 감소 반복
    // - 계산: CMPA = PWM_PERIOD * (current_duty_percent / 100)
    // - 주기: UPDATE_DELAY_MS (ms)마다 DUTY_STEP_PERCENT (%) 단위로 변경
    for(;;)
    {
        // 새로운 듀티 사이클 계산
        uint16_t new_duty = (uint16_t)(PWM_PERIOD * (current_duty_percent / 100.0f));

        // CMPA 및 CMPB 레지스터 업데이트
        // - ePWM1A와 ePWM1B에 동일한 듀티 사이클 적용 (보완 출력)
        EPwm1Regs.CMPA.bit.CMPA = new_duty;  // ePWM1A 듀티 업데이트
        EPwm1Regs.CMPB.bit.CMPB = new_duty;  // ePWM1B 듀티 업데이트

        // 듀티 사이클 증가/감소 방향 결정
        current_duty_percent += duty_direction * DUTY_STEP_PERCENT;

        // 범위 체크 및 방향 전환
        // - 최대값(90%) 도달 시 감소로 전환
        // - 최소값(10%) 도달 시 증가로 전환
        if (current_duty_percent >= MAX_DUTY_PERCENT)
        {
            current_duty_percent = MAX_DUTY_PERCENT;
            duty_direction = -1;  // 감소 방향으로 전환
        }
        else if (current_duty_percent <= MIN_DUTY_PERCENT)
        {
            current_duty_percent = MIN_DUTY_PERCENT;
            duty_direction = 1;   // 증가 방향으로 전환
        }

        // 업데이트 주기 대기
        // - DELAY_US: 마이크로초 단위 지연 (10ms = 10000us)
        // - 부드러운 PWM 전환을 위해 충분한 지연 시간 확보
        DELAY_US(UPDATE_DELAY_MS * 1000);  // ms를 us로 변환
    }
}

실제 구현 시 주의사항

  1. 하드웨어 확인: MOSFET 드라이버의 데이터시트를 확인하여 데드밴드 값(DBRED, DBFED)을 조정하세요.
  2. Code Composer Studio(CCS): F28x_Project.h가 포함된 C2000Ware 환경에서 실행됩니다.
  3. 동적 제어: 듀티 사이클 변경은 EPwm1Regs.CMPA.bit.CMPA를 런타임에 수정.
  4. 인터럽트 추가: 실시간 제어가 필요한 경우, ETSEL과 ETPS 레지스터로 인터럽트를 설정하세요.