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

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

Особое внимание уделите прерываниям! Я вижу, что вы включили NVIC_DMA_ADC_Init, но написали ли вы соответствующую функцию обработки прерываний? Имена функций прерываний в STC фиксированы, нельзя придумывать их самостоятельно. Если вы ошибётесь в имени, то вход в прерывание просто не произойдёт, и флаг, соответственно, так и не установится в 1. Также не перепутайте номер прерывания с другими.

Эта проблема является очень типичной ловушкой для новичков при разработке DMA-ADC на микроконтроллере STC8A8K64D4. Я разберу её по уровням от самого приоритетного к менее важному, и если вы будете проверять в этом порядке, то сможете решить проблему.


1. Самые ключевые моменты (в 90% случаев ошибка здесь)

1. Обязательно включите функцию DMA в ADC, иначе ADC не будет запускать передачу по DMA

В вашем коде инициализации ADC полностью отсутствует включение функции DMA для ADC! В микроконтроллерах серии STC8A, чтобы ADC работал совместно с DMA, необходимо, чтобы после завершения преобразования ADC автоматически запускал DMA. Если этот переключатель не включен, после завершения преобразования DMA вообще не активируется, прерывание по окончании передачи никогда не произойдёт, а флаг так и останется нулевым.

  • Решение: Добавьте в структуру инициализации ADC настройку включения DMA:
ADC_InitStructure.ADC_DMA_Enable = ENABLE; // Ключевое: включить триггер DMA от ADC
  • Если ваша библиотека не содержит этого поля, установите напрямую через регистр:
ADC_CONTR |= 0x40; // Установите бит ADC_DMA_EN, точное значение бита смотрите в документации на ваш чип

2. Ошибка в инициализации DMA — не задан адрес целевого буфера

В вашем коде строка DMA_ADC_InitStructure.DMA_Buffer = ADC_Result_Once; полностью неверна:

  • ADC_Result_Once — это макрос режима передачи (однократная передача), а не адрес буфера.

  • Вы не указали DMA адрес массива u16, который определили ранее. DMA просто не знает, куда отправлять результаты АЦП, поэтому передача не может быть выполнена корректно.

  • Правильный пример инициализации DMA (согласно стандартной библиотеке STC):

u16 AdcResultBuf[16]; // Ваш массив из 16 элементов типа u16
DMA_ADC_InitStructure.DMA_Enable = ENABLE;
DMA_ADC_InitStructure.DMA_Channel = 0x0003; // Канал DMA 3 соответствует ADC — ваша настройка верна
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 изменяется в прерывании, а проверяется в main, она обязательно должна быть объявлена с ключевым словом 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; // Передача завершена — устанавливаем флаг
}

2. Менее критичные, но обязательные для исправления проблемы

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 из официальной библиотеки — используйте актуальный для вашей библиотеки

3. Порядок диагностики и проверки

  1. Сначала добавьте volatile к флагу и правильно напишите обработчик прерывания, убедитесь, что прерывание действительно вызывается;
  2. Исправьте инициализацию ADC — добавьте включение DMA в ADC;
  3. Исправьте инициализацию DMA — правильно задайте режим передачи, количество передач и адрес целевого массива;
  4. Проверьте, включены ли глобальные прерывания (EA=1) и разрешено ли прерывание DMA;
  5. Для упрощения тестирования сначала уменьшите количество передач до 1, сделайте массив из одного элемента, сначала добейтесь успешной однократной передачи, затем вернитесь к 16 передачам.