최초 만든날 : 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 를 다른곳으로 가져 가기
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 사용 시:
- ePWM이 하드웨어적으로 ADC 트리거 신호를 보낸다. (CPU 개입 없음)
- ADC가 변환을 완료한다.
- ADC가 인터럽트를 발생시켜 CPU에 "데이터 준비 완료"를 알린다.
- CPU 타이머 사용 시:
- 타이머가 오버플로우되어 CPU에 인터럽트를 건다.
- CPU는 현재 하던 일을 멈추고 타이머 ISR(인터럽트 서비스 루틴)로 점프한다.
- 타이머 ISR 안에서 소프트웨어적으로 ADC 변환을 시작시킨다. (AdcRegs.ADCSOCFRC1.all = ...)
- 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 로 다음 결과가 올라 옵니다
============
============
> 최초 한번은 쓰레기가 오는군요.
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_TMS320' 카테고리의 다른 글
| Enhanced Quadrature Encoder Pulse (EQEP) (0) | 2025.09.09 |
|---|---|
| TMS320 error: program will not fit into available memory (0) | 2025.07.03 |
| TMS320 F28069 CCS PWM sample source : TI 입문 (0) | 2025.03.26 |
| TMS320 F28069 CCS SCI(UART) printf source : TI 입문 (7) | 2025.03.21 |
| TMS320 CCS v20 Project 만들기, 부르기, 빼기 : TI 입문 (0) | 2025.03.13 |