在使用stc8a8k64d4官方库函数时遇到了问题

佬们好,stc8a8k64d4,用的stc官方库函数,设置好了dma_adc转运,我判断dmaadcflag是否置1发送串口消息,但是一直发不出去(我很确定是dmaadcflag没有置1的问题,因为没了这个判断串口发送是正常的),然后我跟官方例程也对了很久,没看出来有啥问题。如图所示,然后我dma转运到的是u16的十六个元素的数组。查了三个小时没查出来啥问题,我就是个苦学生,求求了。

중단 처리를 꼼꼼히 확인하세요! 제가 보니 NVIC_DMA_ADC_Init은 켰는데, 해당하는 인터럽트 서비스 함수는 작성했나요? STC의 인터럽트 함수 이름은 고정되어 있으므로 임의로 지정하면 안 됩니다. 이름을 잘못 쓰면 인터럽트가 전혀 발생하지 않고, 플래그 비트도 당연히 1로 설정되지 않습니다. 또한 인터럽트 번호를 다른 것과 혼동하지 않도록 주의하세요.

이 문제는 STC8A8K64D4의 DMA-ADC 개발에서 매우 흔한 초보자용 함정입니다. 저는 우선순위 높은 순서대로 문제와 해결책을 분석해 드리며, 순서대로 점검하시면 문제를 해결할 수 있습니다.


일, 가장 핵심적인 필수 점검 항목 (90% 확률로 여기에 문제가 있음)

1. 반드시 ADC의 DMA 기능을 활성화해야 합니다. 그렇지 않으면 ADC가 DMA 전송을 트리거하지 않습니다.

여러분의 ADC 초기화 코드에는 전혀 ADC의 DMA 기능이 활성화되어 있지 않습니다!
STC8A에서 ADC와 DMA를 함께 사용하려면, ADC 변환이 완료된 후 자동으로 DMA를 트리거하도록 설정해야 합니다. 이 스위치가 켜져 있지 않으면, ADC 변환이 끝나도 DMA는 전혀 작동하지 않으며, 따라서 전송 완료 인터럽트도 발생하지 않고, 플래그 비트가 영원히 1로 설정되지 않습니다.

  • 해결 방법: ADC 초기화 구조체에 DMA 활성화 설정을 추가하세요.
ADC_InitStructure.ADC_DMA_Enable = ENABLE; // 핵심: ADC의 DMA 트리거 기능 활성화
  • 만약 사용 중인 라이브러리에 해당 멤버가 없다면, 직접 레지스터를 조작하세요:
ADC_CONTR |= 0x40; // ADC_DMA_EN 비트를 설정하세요. 정확한 비트 정의는 칩 매뉴얼을 참조하세요.

2. DMA 초기화 설정 오류 — 대상 버퍼 주소가 설정되지 않음

코드에서 DMA_ADC_InitStructure.DMA_Buffer = ADC_Result_Once; 는 완전히 잘못되었습니다:

  • ADC_Result_Once전송 모드 매크로(단일 전송)이며, 버퍼 주소가 아닙니다.

  • 여러분이 정의한 u16 배열의 주소를 DMA에 설정하지 않았기 때문에, DMA는 ADC 결과를 어디에 저장해야 할지 알 수 없으며, 정상적인 전송이 불가능합니다.

  • 올바른 DMA 초기화 예시 (STC 공식 표준 라이브러리 기준):

u16 AdcResultBuf[16]; // 말씀하신 16개 요소의 u16 배열

DMA_ADC_InitStructure.DMA_Enable = ENABLE;
DMA_ADC_InitStructure.DMA_Channel = 0x0003; // ADC에 대응하는 DMA 채널 3 — 이 부분은 맞습니다
DMA_ADC_InitStructure.DMA_Mode = ADC_Result_Once; // 단일 전송 모드 — 모드 파라미터는 여기에 입력
DMA_ADC_InitStructure.DMA_Times = 16; // 전송 횟수는 배열 길이와 반드시 일치해야 하며, 8회는 잘못됨
DMA_ADC_InitStructure.DMA_AdcResult = AdcResultBuf; // 핵심: 대상 배열의 주소를 할당

3. 플래그 변수는 반드시 volatile로 선언되어야 하며, 인터럽트 서비스 함수 내에서 설정되어야 함

인터럽트 함수와 플래그 변수 정의를 제공하지 않으셨는데, 이는 가장 쉽게 빠뜨리는 부분입니다:

  • 문제 1: DmaADCFlag 변수가 인터럽트에서 수정되고 메인 루프에서 판단된다면, 반드시 volatile 키워드를 붙여야 합니다. 그렇지 않으면 컴파일러가 최적화 과정에서 if 문을 제거하여 변수가 변경되지 않는 것으로 간주하며, 인터럽트에서 1로 설정되더라도 메인 함수에서는 인식하지 못합니다.

  • 문제 2: 올바른 DMA-ADC 인터럽트 서비스 함수를 작성하고, 그 안에서 플래그를 설정해야 합니다. 그렇지 않으면 플래그는 영원히 0으로 남아 있습니다.

  • 올바른 코드 예시:

volatile u8 DmaADCFlag = 0; // 반드시 volatile을 추가하세요!!!

// 인터럽트 서비스 함수 — interrupt 다음의 인터럽트 번호는 공식 라이브러리 정의와 일치해야 함 (일반적으로 INT_NO_DMA_ADC, 값은 22)
void DMA_ADC_Isr(void) interrupt INT_NO_DMA_ADC
{
    DmaADCFlag = 1; // 전송 완료 시 플래그 설정
}

이, 중요도는 낮지만 반드시 수정해야 하는 문제들

1. DMA 전송 횟수와 배열 길이 불일치

배열이 16개의 u16 요소라고 말씀하셨지만, 코드에서는 DMA_Times=ADC_8_Times (8회)로 설정되어 있습니다. 전송 횟수와 배열 길이가 일치하지 않으면 앞의 모든 설정이 올바르다 해도 데이터의 절반만 전송되며, 이후 로직에도 문제가 생깁니다. 반드시 배열 길이와 동일하게 수정해야 합니다.

2. ADC 변환 모드와 DMA 트리거의 연동

현재 ADC는 변환 모드가 설정되어 있지 않아 기본값인 단일 변환 모드일 것입니다. 각 DMA 전송이 완료된 후 다시 ADC 변환을 시작하지 않으면, ADC는 더 이상 변환을 수행하지 않으며, 따라서 DMA도 다시 트리거되지 않습니다.

  • 권장 사항: 연속 변환 모드로 변경하세요. ADC 초기화 코드에 다음을 추가:
ADC_InitStructure.ADC_Conversion = ADC_CONVERSION_CONTINUOUS; // 연속 변환 모드 활성화
  • 또는, 매번 DMA를 재트리거한 후 수동으로 ADC 변환을 시작:
DMA_ADC_TRIG();
ADC_START(); // 공식 라이브러리의 ADC 시작 매크로 — 사용 중인 라이브러리 기준

삼, 문제 해결 순서 권장 사항

  1. 먼저 플래그 변수에 volatile을 추가하고, 인터럽트 서비스 함수를 올바르게 작성하여 인터럽트가 정상적으로 진입되는지 확인하세요.
  2. ADC 초기화를 수정하여 ADC의 DMA 기능을 활성화하세요.
  3. DMA 초기화를 수정하여 전송 모드, 전송 횟수, 대상 배열 주소를 올바르게 설정하세요.
  4. 전체 인터럽트 허용(EA=1) 및 DMA 인터럽트 허용 설정을 확인하세요.
  5. 먼저 간단한 테스트로 전송 횟수를 1로, 배열 크기를 1개 요소로 줄여 단일 전송부터 성공적으로 동작하는지 확인한 후, 다시 16회로 복원하세요.