TMS320F28335 마이크로컨트롤러의 ePWM 모듈로 하프 브리지 상보 출력을 설정하고, 듀티 사이클을 동적으로 가변하는 방법을 입니다. (20 kHz PWM, 1 µs 데드타임, 그리고 10%~90% 듀티 사이클 가변 코드를 포함)
1. ePWM 모듈과 하프 브리지 상보 출력
TMS320F28335의 ePWM 모듈의 ePWMxA, ePWMxB를 이용하여 하프 브리지에서 상보 출력을 생성합니다.
- 상보 출력: ePWMxA와 ePWMxB가 반대 위상, 데드타임 포함.
- Up-Down 모드: 대칭 PWM 파형으로 하프 브리지 제어.
- 듀티 가변: 인터럽트를 통해 동적으로 듀티 사이클 조정.

2. ePWM 설정 절차
2.1 타임 베이스(Time-Base)
PWM 주기를 정의합니다. Up-Down 모드로 대칭 파형 생성.
- 주기 계산:
TBPRD = (SYSCLKOUT / (2 * PWM_FREQ * CLKDIV * HSPCLKDIV))
2.2 카운터 비교(Counter Compare)
듀티 사이클은 CMPA로 설정, 인터럽트에서 동적 업데이트.
- 초기 듀티:
CMPA = TBPRD * DUTY_CYCLE / 100
2.3 액션 한정자(Action Qualifier)
ePWMxA 출력: TBCTR=0에서 High, TBCTR=CMPA에서 Low.
2.4 데드밴드(Dead-Band)
ePWMxA를 기준으로 ePWMxB를 상보적으로 생성, 데드타임 삽입.
- 데드타임 계산:
DEAD_TIME_TICKS = DEAD_TIME * SYSCLKOUT
2.5 듀티 사이클 가변
인터럽트에서 0.5초마다 듀티 사이클을 10% 단위로 10%~90% 범위에서 변경.
- 업데이트 주기: 20 kHz * 0.5초 = 10000 인터럽트.
- CMPA 업데이트:
CMPA = TBPRD * current_duty / 100
3. 예제 코드
20 kHz PWM, 50% 초기 듀티, 1 µs 데드타임, 0.5초마다 듀티 가변.
설정 계산식
PWM 주기 (TBPRD)
TBPRD = (SYSCLKOUT / (2 * PWM_FREQ * CLKDIV * HSPCLKDIV))
입력: SYSCLKOUT = 150 MHz, PWM_FREQ = 20 kHz, CLKDIV = 1, HSPCLKDIV = 1
결과: (150e6 / (2 * 20e3 * 1 * 1)) = 3750
듀티 사이클 (CMPA)
CMPA = TBPRD * current_duty / 100
초기: TBPRD = 3750, current_duty = 50% → CMPA = 1875
가변: 10%~90%로 0.5초마다 변경.
데드타임 (DBRED, DBFED)
DEAD_TIME_TICKS = DEAD_TIME * SYSCLKOUT
입력: DEAD_TIME = 1 µs, SYSCLKOUT = 150 MHz
결과: 1e-6 * 150e6 = 150
예시 코드
//###########################################################################
//
// 파일: main.c
//
// 제목: ePWM1 하프 브리지 PWM 동적 듀티 사이클 제어
//
// 설명:
// 이 코드는 TMS320F28335의 ePWM1 모듈을 사용하여
// 20kHz PWM 신호를 생성하고, 1µs 데드타임을 적용합니다.
// 0.5초마다 듀티 사이클을 10% ~ 90% 범위로 10% 단위로 변경합니다.
// 외부 연결: ePWM1A (GPIO0), ePWM1B (GPIO1)
//
// 주의: CCS 12.7.0, TI CGT 22.6.1.LTS, C2000Ware 6.00.00.00에서 테스트됨
// TI ePWM 데드밴드 예제를 기반으로 듀티 제어로 수정됨
//
//###########################################################################
#include "DSP28x_Project.h" // 디바이스 헤더 파일 및 예제 포함 파일
// 시스템 및 PWM 설정 상수
#define SYSCLKOUT 150000000UL // 시스템 클럭 (Hz): 150 MHz
#define PWM_FREQ 20000UL // 목표 PWM 주파수 (Hz): 20 kHz
#define DUTY_CYCLE 50U // 초기 듀티 사이클 (%): 50%
#define DEAD_TIME 1e-6 // 데드타임 (초): 1 µs
#define DUTY_STEP 10U // 듀티 사이클 변경 단위 (%): 10%
#define UPDATE_PERIOD 10000UL // 듀티 업데이트 주기: 0.5초 (20 kHz * 0.5초)
// 계산된 레지스터 값 (unsigned 캐스팅으로 경고 제거, 1UL * 1UL로 CLKDIV/HSPCLKDIV 명시)
#define TBPRD_VALUE ((Uint16)((Uint32)SYSCLKOUT / (2UL * PWM_FREQ * 1UL * 1UL))) // 3750 (20kHz, CLKDIV=1, HSPCLKDIV=1)
#define CMPA_VALUE ((Uint16)(((Uint32)TBPRD_VALUE * DUTY_CYCLE) / 100U)) // 초기 CMPA: 1875 (50%)
#define DEAD_TIME_TICKS 150U // 1µs @150MHz = 150 틱 (float 연산 피함)
// 듀티 사이클 제어를 위한 글로벌 변수
volatile Uint16 current_duty = DUTY_CYCLE; // 현재 듀티 사이클 (%)
volatile Uint16 duty_direction = 1; // 듀티 변경 방향 (1: 증가, 0: 감소)
volatile Uint32 update_counter = 0; // 듀티 업데이트 주기 카운터
// ePWM1 모듈 초기화 함수 (TI 예제 기반)
void InitEPwm1(void) {
EALLOW; // 보호된 레지스터 접근 허용
// GPIO 설정: ePWM1A와 ePWM1B를 GPIO0, GPIO1에 매핑
GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1; // GPIO0을 ePWM1A로 설정
GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 1; // GPIO1을 ePWM1B로 설정
// 타임 베이스 클럭 동기화 비활성화 (TI 예제 관행)
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0;
EDIS;
// 타임 베이스 (TB) 설정
EPwm1Regs.TBPRD = TBPRD_VALUE; // 주기 설정 (20kHz를 위한 3750)
EPwm1Regs.TBPHS.half.TBPHS = 0; // 위상 오프셋: 0
EPwm1Regs.TBCTR = 0; // 카운터 초기화
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_DIV1; // 클럭 분주: /1
EPwm1Regs.TBCTL.bit.PRDLD = TB_SHADOW; // 주기 쉐도우 모드
EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO; // TBCTR=0에서 동기화 출력
// 카운터 비교 (CC) 설정
EPwm1Regs.CMPA.half.CMPA = CMPA_VALUE; // 초기 CMPA: 50% 듀티
EPwm1Regs.CMPB = 0; // CMPB 초기화 (사용하지 않음, 0으로 설정)
EPwm1Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW; // CMPA 쉐도우 모드
EPwm1Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW; // CMPB 쉐도우 모드
EPwm1Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO; // CMPA: TBCTR=0일 때 로드
EPwm1Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO; // CMPB: TBCTR=0일 때 로드
// 액션 한정자 (AQ) 설정: 상보 PWM을 위한 구성
EPwm1Regs.AQCTLA.bit.CAU = AQ_SET; // CMPA 업카운트 시 ePWM1A 설정 (High)
EPwm1Regs.AQCTLA.bit.CAD = AQ_CLEAR; // CMPA 다운카운트 시 ePWM1A 클리어 (Low)
EPwm1Regs.AQCTLB.bit.CAU = AQ_CLEAR; // CMPA 업카운트 시 ePWM1B 클리어 (Low)
EPwm1Regs.AQCTLB.bit.CAD = AQ_SET; // CMPA 다운카운트 시 ePWM1B 설정 (High)
// 데드밴드 (DB) 설정: 상보 출력용
EPwm1Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE; // 전체 데드밴드 활성화
EPwm1Regs.DBCTL.bit.IN_MODE = DBA_ALL; // ePWM1A와 ePWM1B 모두 입력 소스로 사용 (TI 예제 기준)
EPwm1Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC; // 액티브 하이 상보 (ePWM1A 하이, ePWM1B 인버트)
EPwm1Regs.DBRED = DEAD_TIME_TICKS; // 상승 에지 지연: 1µs (150 틱)
EPwm1Regs.DBFED = DEAD_TIME_TICKS; // 하강 에지 지연: 1µs (150 틱)
// 인터럽트 설정 (TI 예제 기반, ET_1ST로 빠른 업데이트)
EPwm1Regs.ETSEL.bit.INTSEL = ET_CTR_ZERO; // TBCTR=0 이벤트 선택
EPwm1Regs.ETSEL.bit.INTEN = 1; // 인터럽트 활성화
EPwm1Regs.ETPS.bit.INTPRD = ET_1ST; // 첫 번째 이벤트에서 인터럽트 발생
EALLOW;
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1; // 동기화 활성화
EDIS;
}
// 인터럽트 서비스 루틴: 듀티 사이클 동적 변경
__interrupt void epwm1_isr(void) {
update_counter++; // 주기 카운터 증가
// 0.5초마다 듀티 사이클 업데이트 (20 kHz * 0.5초 = 10000)
if (update_counter >= UPDATE_PERIOD) {
update_counter = 0; // 카운터 초기화
// 듀티 사이클 변경: 10% ~ 90% 범위
if (duty_direction) {
current_duty += DUTY_STEP; // 증가
if (current_duty >= 90) duty_direction = 0; // 90% 도달 시 감소 전환
} else {
current_duty -= DUTY_STEP; // 감소
if (current_duty <= 10) duty_direction = 1; // 10% 도달 시 증가 전환
}
// CMPA 값 업데이트 (unsigned 계산으로 오버플로 방지)
EPwm1Regs.CMPA.half.CMPA = (Uint16)(((Uint32)TBPRD_VALUE * (Uint32)current_duty) / 100U);
}
EPwm1Regs.ETCLR.bit.INT = 1; // 인터럽트 플래그 클리어
PieCtrlRegs.PIEACK.all = PIEACK_GROUP3; // 그룹 3 인터럽트 ACK
}
void main(void) {
InitSysCtrl(); // 시스템 클럭 초기화 (150 MHz)
DINT; // 전역 인터럽트 비활성화
InitPieCtrl(); // PIE 초기화
IER = 0x0000; // CPU 인터럽트 비활성화
IFR = 0x0000; // CPU 인터럽트 플래그 클리어
InitPieVectTable(); // PIE 벡터 테이블 초기화
EALLOW;
PieVectTable.EPWM1_INT = &epwm1_isr; // ePWM1 ISR 설정
EDIS;
InitEPwm1Gpio(); // GPIO 초기화 (TI 예제 참조)
InitEPwm1(); // ePWM1 초기화
// 인터럽트 활성화
PieCtrlRegs.PIEIER3.bit.INTx1 = 1; // PIE 그룹 3, INTx1 (ePWM1) 활성화
IER |= M_INT3; // 그룹 3 인터럽트 활성화
EINT; // 전역 인터럽트 활성화
ERTM; // 실시간 디버그 이벤트 활성화
while(1); // 무한 루프 (IDLE)
}
4. 검증 방법
- 오실로스코프: GPIO0(ePWM1A), GPIO1(ePWM1B)에서 20 kHz, 1 µs 데드타임, 듀티 사이클 10%~90% 변동 확인.
- CCS 디버깅:
TBPRD
,CMPA
,DBRED
,current_duty
값 확인. - 테스트:
PWM_FREQ
,DUTY_STEP
,UPDATE_PERIOD
변경으로 동작 조정.
'MCU > C2000' 카테고리의 다른 글
TMS320F28388D DSP CPU 타이머 사용법 : Driverlib API로 CPU 타이머 설정과 예제(수정본) (0) | 2025.08.08 |
---|---|
TMS320F28388D DSP SCI 사용법: Driverlib API로 UART 설정 및 예제(수정본) (0) | 2025.08.08 |
TMS320F28388D DSP Driverlib 기반 프로젝트 설정 및 기본 프로그램 작성 절차 (0) | 2025.08.08 |
TMS320F28335 DSP ePWM SPWM 생성 (0) | 2025.08.07 |
TMS320F28335 DSP SCI 사용법: Bitfield 구조 활용 (0) | 2025.08.07 |
TMS320F28335 DSP 사양 및 CCS 프로젝트 생성 절차 (0) | 2025.08.06 |
TI C2000 Lockstep 완벽 정리: 기능 안전을 위한 필수 기술 (0) | 2025.08.06 |
TMS320F28377D DSP SCI 사용법: Bitfield 구조 활용 예제 코드(수정) (0) | 2025.08.06 |