본문 바로가기
개발/TI_TMS320

TMS320 F28069 CCS ADC source : TI 입문

by 즐기며 2025. 6. 26.

최초 만든날 : 250626 , 고친거 마지막날:250701

==========================================

TI TMS320 입문 연재 입니다.

 

1.  CCS, C2000Ware, sprc191 (sprc097) 설치, compile

2.  XDS100, XDS200 로 CCS Debug 하기 

3.  UniFlash 로 (CCS 없이) FW Download 하기(Driver 설치)  

4.  C2Prog 로 (CCS 없이) FW Download 하기  

5.  C2000Ware Sample code 를 다른곳으로 가져 가기  

6.  CCS v20 Project 만들기  

7.  CCS,  다른 ( 옛날 ) Compiler (CGT) 버전 설치  

8.  TMS320 F28069 CCS SCI(UART) source  

9.  TMS320 F28069 CCS PWM source     

10.  TMS320 F28069 CCS ADC source              == 본글


ㄱ. 
 TMS320 error: program will not fit into available memory  

==========================================

본글 목차    

 

1. 들머리

  1)  C2000Ware 사용

  2)  sprintf  사용

  3)  ADC 를 16개 사용

  4) Sequential Sampling Mode와 Simultaneous Sampling Mode의 차이

  5) Timer 인터럽트 안쓰고 ePWM 인터럽트를 사용하는 이유

 

2. main.c ( Example_2806xAdcSoc.c ) 풀이

    1) main 함수 부분

     2)  ADC 설정 부분

     3) ADC 끝나면 저장 부 (int 방식)

     4) 결과물

 

3. 추가 사항

1)   INT1SEL  설정 (Simultaneous mode) 비교 및 권장사항

 

4. 소스 

 

================================

1. 들머리

1) 기본적으로

     ~\c2000\C2000Ware_5_04_00_00\device_support\f2806x\examples\c28\adc_soc

   에서 줏어 오고,

> 이게 순차/단일 샘플링(Sequential Sampling or Single Sampling), PWM 인터럽트 방식이라

> 다른거 참고 하여 일반(Polling) 방식, 동시 샘플링(Simultaneous Sampling) 등도 추가 하였읍니다

  , 즉 interrupt 쓰는 방식과 Polling 방식 ,  순차 샘플링(Sequential Sampling)/동시 샘플링(Simultaneous Sampling) 입니다

> #define WORK_MODE      1    // 

    에서 0~4 를 선택 하시면 됩니다.

// 0: 순차/단일 샘플링(Sequential Sampling or Single Sampling) , Polling , 15 개 - 가장 쉬움
// 1: 순차 샘플링(Sequential Sampling) , pwm interrupt, 15 개 
// 2: 동시 샘플링(Simultaneous Sampling) , Polling , 16 개 
// 3: 동시 샘플링(Simultaneous Sampling) , pwm interrupt  - 가장 많이 씀
// 4: 동시 샘플링(Simultaneous Sampling) , timer interrupt - 실제는 거의 안씀.

 

2) 추가로 sprintf 도 사용 하였읍니다

   sprintf 에 관해서는       https://bahk33.tistory.com/211       를 참고 하셔요  

 

3) F28069 는 ADC 를 16개 까지 쓰는데,

   처음 할때는 16개 다 쓰다가,  한개 빼고 15개 쓰려 했는데,  나중 결과 보니, 덜빠져서 16개 다 되는 거 같네요.

 

> 0,2,4,6 : ADCA0 포트를 공유 하여 4번 AD 하도록 ( 순차 샘플링의 경우는 0~3 으로 할 수 도 있읍니다. )

> 1.3.5.7 : ADCB0 포트를 공유 하여 4번 AD 하도록 ( 순차 샘플링의 경우는 4~7 으로 할 수 도 있읍니다. )

> 8~15 : 다른 8개

 


4) Sequential Sampling Mode와 Simultaneous Sampling Mode의 차이

>.   Sequential/Single Sampling Mode: 순차/단일 샘플링

  • 작동 방식: ADC가 한 번에 하나의 채널만 샘플링합니다. 즉, 각 채널(예: ADCINA0, ADCINA1 등)을 순차적으로 변환하여 데이터를 수집합니다.
  • 특징:
    • 샘플링이 순차적으로 이루어지므로 채널 간 시간 지연이 발생합니다.
    • SIMULENx 비트를  0으로 설정 하면 됩니다.
    • 단일 샘플링 작업에서 최대 16개의 SOC(Start of Conversion)를 설정할 수 있습니다.
    • 메모리 사용 효율이 높고, 단순한 단일 채널 또는 비동기 신호 처리에 적합합니다.
  • 사용 예: 서로 독립적인 신호를 측정하거나, 시간적으로 동기화가 필요 없는 경우.

>. 동시 Simultaneous Sampling Mode:

  • 작동 방식: ADC가 두 개의 채널(예: ADCINA0와 ADCINB0)을 동시에 샘플링합니다. 이는 ADC의 두 개의 샘플 앤 홀드 회로(A와 B)를 활용하여 동일한 시점에 데이터를 캡처합니다.
  • 특징:
    • 두 채널의 데이터가 시간적으로 완벽히 동기화됩니다.
    • SIMULENx 비트를  1로 설정 하면 됩니다.
    • 한 번의 변환에서 두 채널(A와 B)을 동시에 처리하므로, 결과적으로 샘플링 속도가 빠르고 동기화가 중요한 경우에 유리합니다.
    • 메모리 사용량이 Sequential 모드보다 많을 수 있습니다(두 채널의 결과가 동시에 저장됨).
  • 사용 예: 모터 제어, 위상 차이 측정, 또는 두 신호 간의 시간적 동기화가 중요한 애플리케이션.

> 주요 차이점 요약:

  • 샘플링 타이밍: Sequential은 순차적(시간 차이 발생), Simultaneous는 동시 샘플링(시간 동기화).
  • 효율성: Sequential은 단일 채널 처리에 효율적, Simultaneous는 동기화가 필요한 경우에 적합.
  • 응용 분야: Sequential은 독립 신호 처리, Simultaneous는 위상 민감 애플리케이션(예: 전류/전압 동시 측정)에 적합.

5) ePWM 인터럽트(정확히는 ePWM의 SOC 이벤트)**를 사용하는 이유

특히 모터 제어, 전력 변환(인버터, 컨버터 등), 디지털 파워 서플라이와 같은 분야에서는 ePWM을 사용하는 것이 거의 표준처럼 여겨집니다.

>. PWM 파형과의 정밀한 동기화 (가장 중요한 이유)

모터 제어나 스위칭 전원 공급 장치에서는 PWM 파형의 특정 시점에서 전류나 전압을 측정하는 것이 매우 중요합니다.

  • 스위칭 노이즈 회피: 전력 스위치(MOSFET, IGBT 등)가 켜지거나 꺼지는 순간에는 큰 전압/전류 스파이크와 링잉(ringing)이 발생합니다. 이 노이즈가 많은 구간에서 ADC 샘플링을 하면 측정값이 심하게 왜곡됩니다.
  • 평균값 또는 피크값 측정: PWM 주기 동안 전류는 보통 삼각파 또는 톱니파 형태로 변동합니다. 제어 알고리즘은 보통 주기의 평균 전류 피크 전류를 필요로 합니다.
    • Up-Down Count 모드: PWM 카운터가 최대치(TBPRD)에 도달했을 때 0에 도달했을 때는 전류의 피크(peak) 또는 밸리(valley)가 나타나는 지점입니다. 이 시점에 ADC를 트리거하면 삼각파 전류의 평균값을 쉽게 추정할 수 있고, 스위칭 노이즈도 가장 적습니다.
    • Up-Count 모드: 카운터가 0일 때 (주기 시작) 또는 특정 값(Compare 값)일 때 샘플링하여 원하는 시점의 값을 얻을 수 있습니다.

ePWM 모듈은 이러한 "특정 시점"에 ADC 변환 시작(SOC) 신호를 매우 정밀하고 하드웨어적으로 생성할 수 있습니다. 예를 들어, EPwmxRegs.ETSEL.bit.SOCASEL = ET_CTR_ZERO; 설정은 PWM 카운터가 0이 되는 바로 그 클럭 사이클에 ADC 트리거를 발생시킵니다.

반면, CPU 타이머는 PWM 파형과 독립적으로 동작합니다. 타이머 인터럽트가 발생하는 시점과 PWM 파형의 특정 지점(예: 피크) 사이에는 소프트웨어 지연(Interrupt Latency) 등으로 인한 미세한 시간차가 항상 존재하며, 이는 측정값의 일관성을 떨어뜨립니다.

>. CPU 부하 감소

  • ePWM 사용 시:
    1. ePWM이 하드웨어적으로 ADC 트리거 신호를 보낸다. (CPU 개입 없음)
    2. ADC가 변환을 완료한다.
    3. ADC가 인터럽트를 발생시켜 CPU에 "데이터 준비 완료"를 알린다.
  • CPU 타이머 사용 시:
    1. 타이머가 오버플로우되어 CPU에 인터럽트를 건다.
    2. CPU는 현재 하던 일을 멈추고 타이머 ISR(인터럽트 서비스 루틴)로 점프한다.
    3. 타이머 ISR 안에서 소프트웨어적으로 ADC 변환을 시작시킨다. (AdcRegs.ADCSOCFRC1.all = ...)
    4. ADC 변환이 끝날 때까지 기다리거나(비효율적), 또는 별도의 ADC 인터럽트를 또 처리해야 한다.

보시다시피, 타이머를 사용하면 ADC를 시작시키기 위해 불필요한 CPU 인터럽트가 한 번 더 발생합니다. ePWM을 사용하면 이 과정이 하드웨어적으로 자동 처리되므로 CPU는 최종 결과값을 읽어오는 데만 집중할 수 있어 전체적인 시스템 부하가 줄어듭니다.

>. 지터(Jitter) 감소

  • 지터(Jitter): 이벤트 발생 시점의 미세한 시간적 흔들림을 의미합니다.
  • ePWM 트리거: 하드웨어 기반이므로 지터가 거의 없습니다. 매 PWM 주기마다 정확히 동일한 시점에 ADC 트리거가 발생합니다. 이는 고성능 제어에서 매우 중요합니다.
  • 타이머 인터럽트 트리거: 인터럽트가 발생하고 CPU가 ISR을 실행하기까지 걸리는 시간(Interrupt Latency)은 시스템의 상태(예: 다른 고우선순위 인터럽트 처리 중 여부, 파이프라인 상태 등)에 따라 미세하게 변동할 수 있습니다. 이는 ADC 샘플링 시점에 지터를 유발하여 측정의 정밀도를 떨어뜨릴 수 있습니다.

> 요약 비교

특징 ePWM 트리거 (SOC 이벤트) CPU 타이머 인터럽트
정밀도/동기화 매우 높음. PWM 파형의 특정 지점과 완벽하게 동기화 가능. 상대적으로 낮음. PWM과 독립적으로 동작하며, 인터럽트 지연으로 인한 시간 오차 발생.
CPU 부하 낮음. 트리거 과정이 하드웨어로 자동화됨. 높음. ADC 시작을 위해 별도의 CPU 인터럽트와 ISR 처리가 필요.
지터(Jitter) 거의 없음 (매우 낮음). 존재함 (인터럽트 지연 변동).
주요 사용처 모터 제어, 전력 변환, 디지털 파워 등 PWM과 연동된 정밀 제어 주기적인 데이터 로깅, 상태 모니터링 등 PWM과 직접적인 동기화가 필요 없는 경우

따라서  PWM 기반의 정밀 제어 시스템을 구축할 때는 ADC 트리거 소스로 CPU 타이머 대신 ePWM 모듈을 사용하는 것이 기술적으로 월등히 우수하며 사실상의 표준 방식입니다.

 

 

2. main.c ( Example_2806xAdcSoc .c ) 풀이

1) main 함수 부분

// 본 메인 함수는 WORK_MODE가 0, 1 만 있는 것입니다.  간단히 보기 위한 것이고
// 첨부 7/1 일자 소스는 0~4 까지 있읍니다.
void main(void)
{
    char *msg;

    //
    // Step 1. Initialize System Control:
    // PLL, WatchDog, enable Peripheral Clocks
    // This example function is found in the F2806x_SysCtrl.c file.
    //
    InitSysCtrl();

    //
    // Step 2. Initialize GPIO:
    // This example function is found in the F2806x_Gpio.c file and
    // illustrates how to set the GPIO to it's default state.
    //
    // InitGpio();  // Skipped for this example

    //
    // Step 3. Clear all interrupts and initialize PIE vector table:
    // Disable CPU interrupts
    //
    DINT;

    //
    // Initialize the PIE control registers to their default state.
    // The default state is all PIE interrupts disabled and flags
    // are cleared.
    // This function is found in the F2806x_PieCtrl.c file.
    //
    InitPieCtrl();

    //
    // Disable CPU interrupts and clear all CPU interrupt flags:
    //
    IER = 0x0000;
    IFR = 0x0000;

    //
    // Initialize the PIE vector table with pointers to the shell Interrupt
    // Service Routines (ISR).
    // This will populate the entire table, even if the interrupt
    // is not used in this example.  This is useful for debug purposes.
    // The shell ISR routines are found in F2806x_DefaultIsr.c.
    // This function is found in F2806x_PieVect.c.
    //
    InitPieVectTable();

    //
    // Interrupts that are used in this example are re-mapped to
    // ISR functions found within this file.
    //
    EALLOW;  // This is needed to write to EALLOW protected register
#if (WORK_MODE == 1 )   
    PieVectTable.ADCINT1 = &adc_isr;
#endif
    PieVectTable.SCIRXINTA = &sciaRxFifoIsr;        // PIE Group 9, INT1   SCI RX INT A
    EDIS;    // This is needed to disable write to EALLOW protected registers

    //
    // Step 4. Initialize all the Device Peripherals:
    // This function is found in F2806x_InitPeripherals.c
    // InitPeripherals(); // Not required for this example
    //
    InitAdc();             // For this example, init the ADC    F2006x_Adc.c
    AdcOffsetSelfCal();    // F2006x_Adc.c
    scia_fifo_init();      // Init SCI-A 초기화
    
    //
    // Step 5. User specific code, enable interrupts:
    //
    msg = "\nHello ADC World ! - ";
    scia_msg(msg); 
#if (WORK_MODE == 1 )   
    msg = "Use Pwm Interrupt\n";
#else
    msg = "No Use Interrupt\n";
#endif
    scia_msg(msg); 

    //
    // Enable ADCINT1 in PIE
    // SCIA_RX,

    PieCtrlRegs.PIECTRL.bit.ENPIE = 1; // Enable the PIE block
    PieCtrlRegs.PIEIER9.bit.INTx1 = 1; // PIE Group 9, 인터럽트 1 (SCI-A RX) 활성화
#if (WORK_MODE == 1 )   
    PieCtrlRegs.PIEIER1.bit.INTx1 = 1; // Enable INT 1.1 in the PIE, ADC
    IER |= M_INT1; 					   // Enable CPU Interrupt 1,  ADC
#endif
    IER |= M_INT9; 					   // Enable CPU Interrupt 9, SCIA_RX
    EINT;          					   // Enable Global interrupt INTM
    ERTM;          					   // Enable Global realtime interrupt DBGM

    ConfADC();

    //
    // Wait for ADC interrupt
    //
    for(;;)     // while(1) 보다 이게 더 좋다나 ? ... 뭐 요즘은 그게 그거라나 ?
    {
#if (WORK_MODE == 0 )   
        // ADC 변환 시작
        AdcRegs.ADCCTL1.bit.INTPULSEPOS = 1;  // 인터럽트 펄스 위치 설정
        AdcRegs.ADCSOC0CTL.bit.TRIGSEL = 0;   // 소프트웨어 트리거
        AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; // 인터럽트 플래그 클리어

        AdcRegs.ADCSOCFRC1.all = 0x7FFF;      // 7FFF: 15개(0~14), Force start of conversion on SOC0  // 1:0, 0x3F:0~5, 0xFFFF:16개(0~15)  

        while(AdcRegs.ADCINTFLG.bit.ADCINT1 == 0) {}        // Wait for end of conversion.

        saveResult();  // Get adc result from SOC0

        AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1;        // Clear ADCINT1
        prResult();

#elif (WORK_MODE == 1 ) 
        if(isConversionFinish){ 
            isConversionFinish= 0;   // 사용한거 기록, 인터럽트가 또 발생 하지 않으면 본 if 문 다시 안들어 오게,
            prResult();
        }
#endif
    }
}

 

 

2)  ADC 설정 부분

void ConfADC(){
    unsigned char trig_mode;

    //
    // Configure ADC
    //
    EALLOW;                                 // 보호된 레지스터 접근 허용
    AdcRegs.ADCCTL2.bit.ADCNONOVERLAP = 1; // Enable non-overlap mode
//    AdcRegs.ADCCTL2.bit.CLKDIV2EN = 1;      // ADC 클럭을 SYSCLK/2로 설정 (40MHz @ 80MHz SYSCLK)
//    AdcRegs.ADCCTL1.bit.ADCBGPWD = 1;       // 밴드갭 레퍼런스 활성화
//    AdcRegs.ADCCTL1.bit.ADCPWDN = 1;        // ADC 전원 활성화
//    AdcRegs.ADCCTL1.bit.ADCENABLE = 1;      // ADC 모듈 활성화

    
    //
    // ADCINT1 trips after AdcResults latch
    //

    AdcRegs.ADCCTL1.bit.INTPULSEPOS	= 1;    // 변환 완료 시 인터럽트 발생
    
    AdcRegs.INTSEL1N2.bit.INT1CONT  = 0;  // Disable ADCINT1 Continuous mode

 //   AdcRegs.ADCCTL1.bit.ADCRESSEL = 0;  // 12비트 해상도

    AdcRegs.INTSEL1N2.bit.INT1SEL 	= 14;    // 14 ( = 15개 여기서 마지막 ) 이 끝나면 인터럽트 발생 하라.  한개면 0, 16개면 15
    AdcRegs.INTSEL1N2.bit.INT1E     = 1;  // Enabled ADCINT1
//    AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; // 인터럽트 플래그 클리어

    //
    // setup EOC1 to trigger ADCINT1 to fire
    //
    
    AdcRegs.ADCSOC0CTL.bit.CHSEL 	= 0;  // set SOC0 channel select to ADCINA0      // 같은거 4번
    AdcRegs.ADCSOC1CTL.bit.CHSEL 	= 0;  // set SOC0 channel select to ADCINA0
    AdcRegs.ADCSOC2CTL.bit.CHSEL 	= 0;  // set SOC0 channel select to ADCINA0
    AdcRegs.ADCSOC3CTL.bit.CHSEL 	= 0;  // set SOC0 channel select to ADCINA0

    AdcRegs.ADCSOC4CTL.bit.CHSEL 	= 8;  // set SOC1 channel select to ADCINB0      // 같은거 4번
    AdcRegs.ADCSOC5CTL.bit.CHSEL 	= 8;  // set SOC1 channel select to ADCINB0
    AdcRegs.ADCSOC6CTL.bit.CHSEL 	= 8;  // set SOC1 channel select to ADCINB0
    AdcRegs.ADCSOC7CTL.bit.CHSEL 	= 8;  // set SOC1 channel select to ADCINB0
    
    AdcRegs.ADCSOC8CTL.bit.CHSEL 	= 1;  // set SOC0 channel select to ADCINA1
    AdcRegs.ADCSOC9CTL.bit.CHSEL 	= 2;  // set SOC0 channel select to ADCINA2
    AdcRegs.ADCSOC10CTL.bit.CHSEL 	= 4;  // set SOC0 channel select to ADCINA4
    AdcRegs.ADCSOC11CTL.bit.CHSEL 	= 6;  // set SOC0 channel select to ADCINA6

    AdcRegs.ADCSOC12CTL.bit.CHSEL 	= 9;  // set SOC1 channel select to ADCINB1
    AdcRegs.ADCSOC13CTL.bit.CHSEL 	= 10;  // set SOC1 channel select to ADCINB2
    AdcRegs.ADCSOC14CTL.bit.CHSEL 	= 12;  // set SOC1 channel select to ADCINB4
    AdcRegs.ADCSOC15CTL.bit.CHSEL 	= 14;  // set SOC1 channel select to ADCINB6  // 15개 이므로 이건 의미 없음.
    
    
    // ADC SOC(Start of Conversion) 
    // set SOC0 start trigger on EPWM1A, due to round-robin SOC0 converts
    // first then SOC1
    //

#if (WORK_MODE == 0 )   
    trig_mode = 0;  // 0:소프트웨어 트리거, 5:EPEM1A
#elif (WORK_MODE == 1 )   
    trig_mode = 5;  // set SOC0 start trigger on EPWM1A, due to round-robin SOC0 converts first then SOC1
#endif

    AdcRegs.ADCSOC0CTL.bit.TRIGSEL 	= trig_mode;
    AdcRegs.ADCSOC1CTL.bit.TRIGSEL 	= trig_mode;
    AdcRegs.ADCSOC2CTL.bit.TRIGSEL 	= trig_mode;
    AdcRegs.ADCSOC3CTL.bit.TRIGSEL 	= trig_mode;
    AdcRegs.ADCSOC4CTL.bit.TRIGSEL 	= trig_mode;
    AdcRegs.ADCSOC5CTL.bit.TRIGSEL 	= trig_mode;
    AdcRegs.ADCSOC6CTL.bit.TRIGSEL 	= trig_mode;
    AdcRegs.ADCSOC7CTL.bit.TRIGSEL 	= trig_mode;
    AdcRegs.ADCSOC8CTL.bit.TRIGSEL 	= trig_mode;
    AdcRegs.ADCSOC9CTL.bit.TRIGSEL 	= trig_mode;
    AdcRegs.ADCSOC10CTL.bit.TRIGSEL 	= trig_mode;
    AdcRegs.ADCSOC11CTL.bit.TRIGSEL 	= trig_mode;
    AdcRegs.ADCSOC12CTL.bit.TRIGSEL 	= trig_mode;
    AdcRegs.ADCSOC13CTL.bit.TRIGSEL 	= trig_mode;
    AdcRegs.ADCSOC14CTL.bit.TRIGSEL 	= trig_mode;
    AdcRegs.ADCSOC15CTL.bit.TRIGSEL 	= trig_mode;
    
    //
    // set SOC0 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1), 26 ADC Clock Cycles, (25 ACQPS plus 1),
    //
    #define N_ACQPS     6
    AdcRegs.ADCSOC0CTL.bit.ACQPS 	= N_ACQPS;    // 샘플/홀드 윈도우: 7 ADC 클럭 사이클(== 6 ACQPS plus 1)
    AdcRegs.ADCSOC1CTL.bit.ACQPS 	= N_ACQPS;
    AdcRegs.ADCSOC2CTL.bit.ACQPS 	= N_ACQPS;
    AdcRegs.ADCSOC3CTL.bit.ACQPS 	= N_ACQPS;
    AdcRegs.ADCSOC4CTL.bit.ACQPS 	= N_ACQPS;
    AdcRegs.ADCSOC5CTL.bit.ACQPS 	= N_ACQPS;
    AdcRegs.ADCSOC6CTL.bit.ACQPS 	= N_ACQPS;
    AdcRegs.ADCSOC7CTL.bit.ACQPS 	= N_ACQPS;
    AdcRegs.ADCSOC8CTL.bit.ACQPS 	= N_ACQPS;
    AdcRegs.ADCSOC9CTL.bit.ACQPS 	= N_ACQPS;
    AdcRegs.ADCSOC10CTL.bit.ACQPS 	= N_ACQPS;
    AdcRegs.ADCSOC11CTL.bit.ACQPS 	= N_ACQPS;
    AdcRegs.ADCSOC12CTL.bit.ACQPS 	= N_ACQPS;
    AdcRegs.ADCSOC13CTL.bit.ACQPS 	= N_ACQPS;
    AdcRegs.ADCSOC14CTL.bit.ACQPS 	= N_ACQPS;
    AdcRegs.ADCSOC15CTL.bit.ACQPS 	= N_ACQPS;

    EDIS;

#if (WORK_MODE == 1 )   
    //
    // Assumes ePWM1 clock is already enabled in InitSysCtrl();
    //
    EPwm1Regs.ETSEL.bit.SOCAEN	= 1;		// Enable SOC on A group
    EPwm1Regs.ETSEL.bit.SOCASEL	= 4;		// Select SOC from CMPA on upcount
    EPwm1Regs.ETPS.bit.SOCAPRD 	= 1;		// Generate pulse on 1st event
    EPwm1Regs.CMPA.half.CMPA 	= 0x0080;	// Set compare A value
    EPwm1Regs.TBPRD 			= 0xFFFF;	// Set period for ePWM1
    EPwm1Regs.TBCTL.bit.CTRMODE	= 0;		// count up and start
#endif
    
}

 

 

3) ADC 끝나면 저장 부 (int 방식)

void saveResult(){
    ConvData[0] = AdcResult.ADCRESULT0;     // 같은거 4번
    ConvData[1] = AdcResult.ADCRESULT1;
    ConvData[2] = AdcResult.ADCRESULT2;
    ConvData[3] = AdcResult.ADCRESULT3;
    ConvData[4] = AdcResult.ADCRESULT4;     // 같은거 4번
    ConvData[5] = AdcResult.ADCRESULT5;
    ConvData[6] = AdcResult.ADCRESULT6;
    ConvData[7] = AdcResult.ADCRESULT7;
    ConvData[8] = AdcResult.ADCRESULT8;
    ConvData[9] = AdcResult.ADCRESULT9;
    ConvData[10] = AdcResult.ADCRESULT10;
    ConvData[11] = AdcResult.ADCRESULT11;
    ConvData[12] = AdcResult.ADCRESULT12;
    ConvData[13] = AdcResult.ADCRESULT13;
    ConvData[14] = AdcResult.ADCRESULT14;
    ConvData[15] = AdcResult.ADCRESULT15;
}

//
// adc_isr - 
//
__interrupt void adc_isr(void)
{
    saveResult();               // Get adc result from SOC0
    isConversionFinish = 1;      // 변환 끝났음을 일반 루틴에 알림.

    AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1;   // Clear ADCINT1 flag reinitialize for next SOC
    
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;   // Acknowledge interrupt to PIE

    return;
}

 

 

 4) 결과물

 > 실행 하면 sci 로 다음 결과가 올라 옵니다

============

Hello ADC World!
ADC     1=    84    85    85    85,       84    85    85    85,       85    84    85    84,       85    84    84    84,  
ADC     2=  2033  2033  2033  2033,     2025  2025  2025  2025,        6  2246   496     6,     4074  2320  2310    11,  
ADC     3=  2033  2034  2034  2034,     2025  2026  2026  2025,        7  2247   496     5,     4074  2320  2310    12,  
ADC     4=  2033  2033  2032  2032,     2024  2023  2024  2024,        5  2247   496     6,     4073  2319  2310    12,  
ADC     5=  2033  2033  2032  2033,     2025  2025  2025  2026,        6  2247   496     5,     4073  2319  2310    12,  
ADC     6=  2034  2033  2033  2034,     2025  2026  2025  2025,        7  2247   496     6,     4075  2319  2310    12,  
ADC     7=  2034  2034  2033  2034,     2025  2026  2025  2025,        6  2248   495     6,     4073  2321  2311    12,  
ADC     8=  2035  2033  2033  2034,     2024  2024  2024  2024,        6  2246   496     5,     4075  2319  2310    13,  
ADC     9=  2033  2034  2032  2032,     2025  2025  2025  2025,        5  2247   496     4,     4075  2319  2310    13,  
ADC    10=  2035  2034  2034  2034,     2026  2025  2025  2025,        7  2247   496     6,     4073  2320  2310    13,  
ADC    11=  2032  2034  2034  2033,     2026  2026  2026  2026,        6  2247   496     6,     4073  2320  2310    12,  
ADC    12=  2033  2034  2034  2033,     2026  2026  2026  2025,        5  2247   496     4,     4073  2320  2310    13,  
ADC    13=  2033  2033  2034  2033,     2025  2025  2025  2025,        7  2247   496     5,     4075  2320  2310    12,

============

> 최초 한번은 쓰레기가 오는군요.

 

3. 추가 사항

1)   INT1SEL  설정 (Simultaneous mode) 비교 및 권장사항

 SOC14/SOC15 쌍의 변환 완료( INTSEL1N2.bit.INT1SEL = 15 ) 시 adc_isr를 호출하도록 인터럽트를 설정하는 것이 더 적합한지, INTSEL1N2.bit.INT1SEL = 0(SOC0 완료 시 인터럽트) 설정이 적합 한지 여부, 

> 비교 및 권장사항

  • 기본 설정(SOC0, INT1SEL = 0):
    • 장점: 빠른 인터럽트 발생으로 실시간 응답성 우수(모터 제어에 적합).
    • 단점: SOC14/SOC15 결과가 완료되지 않을 가능성 있음. 하지만 ISR 실행 시간(수십 사이클) 동안 나머지 SOC가 완료되므로, 실제로는 문제가 적음.
    • 적합 상황: 전류/전압 피드백이 빠르게 필요한 모터 제어(예: 3상 VVVF, SVPWM).
  • 제안 설정(SOC15, INT1SEL = 15):
    • 장점: 모든 SOC 변환 완료 후 인터럽트로 데이터 신뢰성 보장.
    • 단점: 약 3 µs 지연으로 실시간 제어에서 약간의 성능 저하 가능.
    • 적합 상황: 모든 채널 데이터의 동기화와 정확도가 중요한 경우(예: 정밀 센서 데이터 수집).
  • SPRUH18I 기반 검토:
    • SPRUH18I 8.9.1절에 따르면, 인터럽트는 어느 SOC에서든 설정 가능하며, 애플리케이션 요구사항에 따라 선택해야 합니다.
    • 동시 샘플링 모드에서 모든 SOC는 동일한 트리거로 시작하므로, SOC5에서 인터럽트를 발생시키는 것은 데이터 완성도를 보장하는 안전한 선택입니다.
  • 결론: 사용자의 제안(SOC14/SOC15 완료 시 인터럽트, INT1SEL = 15)은 모든 데이터의 신뢰성을 보장한다는 점에서 더 적합할 수 있습니다, 특히 ADCINA4/ADCINB4의 데이터가 중요한 경우. 하지만 모터 제어와 같은 실시간 애플리케이션에서는 설정(INT1SEL = 0)도 충분히 동작하며, 지연을 최소화합니다. 두 설정 모두 기술적으로 올바름.
  • 즉 일반적으로 16개를 쓸 경우 Simultaneous mode  에서는 INT1SEL = 15 또는  INT1SEL = 7  을 주면 ADC 완료뒤 하지만,  테스트를 해 보시면 INT1SEL = 0  을 줘서 ADC0 이 끝나면 가져 갈 경우도 가져가는 시간 지연 때문에  가져가는 시점에 이미 ADC15 가 끝나 있어 오류가 생기지 않을 수 있읍니다. 16개를 쓴다 하더라도 꼭 15 또는 7이  아니라 0 ~ 6 사이의 숫자를 써도 된다는 말 입니다.   

 

4. 소스 첨부

ti-tms320f28069-adc-250701.zip
0.43MB
ti-tms320f28069-adc-250630.zip
0.43MB
ti-tms320f28069-adc-250626.zip
0.43MB

 

 

============================

수고 하셨읍니다.

도움이 되셨으면, 댓글 부탁합니다. 댓글 하나에 힘이 됩니다.

고맙읍니다.