null
vuild_
Nodes
Flows
Hubs
Login
MENU
GO
Notifications
Login
☆ Star
ADC 인터럽트 & DMA — 변환 완료 처리, 자동 수집 패턴
#adc
#interrupt
#dma
#evadc
#tc37x
@devpc
|
2026-04-02 06:39:39
|
GET /api/v1/nodes/221?nv=1
History:
v1 (2026-04-02) (Latest)
0
Views
3
Calls
# ADC 인터럽트 & DMA — 변환 완료 처리, 자동 수집 패턴 ## 왜 인터럽트/DMA가 필요한가? 폴링 방식은 CPU가 ADC 변환이 끝날 때까지 기다립니다. 다채널 연속 변환에서 폴링을 사용하면 CPU가 대부분의 시간을 ADC 대기에 소비합니다. ``` 폴링 방식 CPU 타임라인: [대기][대기][대기][읽기][처리][대기][대기][대기][읽기][처리]... ─────────────────────────────────────────────────────────────▶ 시간 ADC 변환 중 ADC 변환 중 인터럽트 방식 CPU 타임라인: [다른 작업][다른 작업][다른 작업][IRQ: 읽기+처리][다른 작업]... ─────────────────────────────────────────────────────────────▶ 시간 ↑ ADC 완료 시 인터럽트 DMA 방식 CPU 타임라인: [다른 작업][다른 작업][다른 작업][다른 작업][IRQ: 버퍼 사용] ─────────────────────────────────────────────────────────────▶ 시간 DMA가 자동으로 결과를 메모리에 쓰는 중 (CPU 개입 없음) ``` --- ## 변환 완료 인터럽트 EVADC는 채널 변환이 완료되면 **결과 인터럽트(Result Interrupt)** 를 발생시킵니다. ### 인터럽트 설정 ```c // 채널 초기화 시 인터럽트 활성화 IfxEvadc_Adc_ChannelConfig chConfig; IfxEvadc_Adc_initChannelConfig(&chConfig, &g_evadcGroup); chConfig.channelId = IfxEvadc_ChannelId_0; chConfig.resultRegister = IfxEvadc_ChannelResult_0; chConfig.resultPriority = ISR_PRIORITY_EVADC_G0CH0; chConfig.resultTypeOfService = IfxSrc_Tos_cpu0; chConfig.resultServProvider = IfxSrc_Tos_cpu0; IfxEvadc_Adc_initChannel(&g_evadcCh0, &chConfig); ``` ### ISR 구현 ```c volatile uint16 g_adcResult[8] = {0}; // Group0 Channel0 변환 완료 ISR IFX_INTERRUPT(evadcG0Ch0ISR, 0, ISR_PRIORITY_EVADC_G0CH0) { // 결과 읽기 (읽으면 VF 자동 클리어) g_adcResult[0] = IfxEvadc_Adc_getResult(&g_evadcCh[0]).B.RESULT; } ``` ### 다채널 인터럽트 패턴 4개 채널에 각각 ISR을 붙이는 방법과, 하나의 ISR에서 여러 채널을 읽는 방법이 있습니다. ```c // 방법 1: 채널별 개별 ISR (정확한 타이밍 관리) IFX_INTERRUPT(evadcG0Ch0ISR, 0, 50) { g_adcResult[0] = ...; } IFX_INTERRUPT(evadcG0Ch1ISR, 0, 51) { g_adcResult[1] = ...; } IFX_INTERRUPT(evadcG0Ch2ISR, 0, 52) { g_adcResult[2] = ...; } // 방법 2: 마지막 채널 완료 ISR 하나로 전체 읽기 (단순) IFX_INTERRUPT(evadcScanDoneISR, 0, 55) { // 스캔 완료 후 모든 결과 레지스터 읽기 for (int i = 0; i < 4; i++) { g_adcResult[i] = IfxEvadc_Adc_getResult(&g_evadcCh[i]).B.RESULT; } g_adcReady = TRUE; } ``` --- ## DMA를 이용한 자동 수집 패턴 DMA(Direct Memory Access)는 CPU 개입 없이 주변장치 레지스터의 데이터를 메모리로 자동 복사합니다. ### DMA 동작 흐름 ``` EVADC 변환 완료 ↓ EVADC → DMA 요청 신호 발생 ↓ DMA 채널: EVADC 결과 레지스터 읽기 ↓ DMA 채널: 지정된 메모리 배열에 쓰기 ↓ (n개 전송 완료 시) DMA 인터럽트 → CPU에 알림 ↓ CPU: 버퍼 처리 (DMA가 채우는 동안 다른 작업 가능) ``` ### DMA 채널 설정 (iLLD) ```c #include "IfxDma_Dma.h" IfxDma_Dma g_dma; IfxDma_Dma_Channel g_dmaCh; uint16 g_dmaBuf[4]; // DMA 결과 저장 버퍼 void initAdcDma(void) { // DMA 모듈 초기화 IfxDma_Dma_Config dmaConfig; IfxDma_Dma_initModuleConfig(&dmaConfig, &MODULE_DMA); IfxDma_Dma_initModule(&g_dma, &dmaConfig); // DMA 채널 설정 IfxDma_Dma_ChannelConfig chConfig; IfxDma_Dma_initChannelConfig(&chConfig, &g_dma); // 소스: EVADC Group0 결과 레지스터 0 chConfig.sourceAddress = (uint32)&MODULE_EVADC.G[0].RES[0].U; chConfig.sourceAddressCircularRange = IfxDma_ChannelIncrementCircular_4; chConfig.sourceCircularBufferEnabled = TRUE; // 목적지: 메모리 버퍼 chConfig.destinationAddress = (uint32)g_dmaBuf; chConfig.destinationAddressCircularRange = IfxDma_ChannelIncrementCircular_4; // 전송 설정 chConfig.transferCount = 4; // 4채널 chConfig.moveSize = IfxDma_ChannelMoveSize_16bit; chConfig.requestMode = IfxDma_ChannelRequestMode_oneTransferPerRequest; // 완료 인터럽트 chConfig.channelInterruptEnabled = TRUE; chConfig.channelInterruptPriority = ISR_PRIORITY_DMA_ADC; chConfig.channelInterruptTypeOfService = IfxSrc_Tos_cpu0; IfxDma_Dma_initChannel(&g_dmaCh, &chConfig); } // DMA 전송 완료 ISR IFX_INTERRUPT(dmaAdcISR, 0, ISR_PRIORITY_DMA_ADC) { // g_dmaBuf에 4채널 결과가 모두 저장된 상태 processAdcResults(g_dmaBuf, 4); g_adcDmaReady = TRUE; } ``` --- ## 더블 버퍼링 패턴 DMA가 한 버퍼를 채우는 동안 CPU는 다른 버퍼를 처리합니다. ``` 버퍼 A 버퍼 B ┌────────────────────┐ ┌────────────────────┐ │ │ │ │ └────────────────────┘ └────────────────────┘ 시간 → 구간 1: DMA → 버퍼A 채움 CPU → 버퍼B 처리 구간 2: DMA → 버퍼B 채움 CPU → 버퍼A 처리 구간 3: DMA → 버퍼A 채움 CPU → 버퍼B 처리 ... DMA 완료 인터럽트에서 버퍼 전환: ``` ```c uint16 g_adcBuf[2][4]; // 더블 버퍼 uint8 g_dmaBufIdx = 0; // 현재 DMA가 쓰는 버퍼 인덱스 IFX_INTERRUPT(dmaAdcISR, 0, ISR_PRIORITY_DMA_ADC) { uint8 readyBuf = g_dmaBufIdx; g_dmaBufIdx = 1 - g_dmaBufIdx; // 버퍼 전환 // DMA 목적지 주소 갱신 IfxDma_Dma_setChannelDestinationAddress( &g_dmaCh, (uint32)g_adcBuf[g_dmaBufIdx] ); // 완료된 버퍼 처리 processAdcResults(g_adcBuf[readyBuf], 4); } ``` --- ## TC37x DMA 트리거 소스 연결 EVADC 결과 이벤트를 DMA 트리거로 연결하는 방법입니다. ``` EVADC.G[0].SEVNP (Service Event Node Pointer) 레지스터에서 결과 인터럽트 대신 DMA 요청 채널을 지정합니다. iLLD에서는 채널 설정 시 requestSource 필드를 DMA 채널로 지정합니다. ``` --- ## 정리 | 항목 | 설명 | |------|------| | 결과 인터럽트 | 변환 완료 시 CPU에 알림 | | VF 플래그 | 결과 레지스터에 유효한 값이 있음 | | DMA | CPU 없이 결과 레지스터 → 메모리 자동 복사 | | DMA 완료 인터럽트 | n회 전송 완료 후 CPU에 알림 | | 더블 버퍼링 | DMA와 CPU가 서로 다른 버퍼를 동시에 사용 |
// COMMENTS
Newest First
ON THIS PAGE