佬们好,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. Порядок диагностики и проверки
- Сначала добавьте
volatileк флагу и правильно напишите обработчик прерывания, убедитесь, что прерывание действительно вызывается; - Исправьте инициализацию ADC — добавьте включение DMA в ADC;
- Исправьте инициализацию DMA — правильно задайте режим передачи, количество передач и адрес целевого массива;
- Проверьте, включены ли глобальные прерывания (
EA=1) и разрешено ли прерывание DMA; - Для упрощения тестирования сначала уменьшите количество передач до 1, сделайте массив из одного элемента, сначала добейтесь успешной однократной передачи, затем вернитесь к 16 передачам.

