ВходНаше всё Теги codebook 无线电组件 Поиск Опросы Закон Суббота
23 ноября
753100 Топик полностью
Idler (29.04.2017 22:58 - 23:31, просмотров: 370) ответил Ксения на Мне не срочно после заполнения буфера надо начинать повторное его заполнение. Периодичность между обновлениями содержимого буфера около 1 сек, что соответствует времени обновления цифири на дисплее. Т.е. за это время я даже FFT успела бы на этом
Вот как то так (поправил и прокомментировал) (еще 2 раза поправил):  DMA.CTRL = 0; DMA.CTRL = DMA_RESET_bm; while ((DMA.CTRL & DMA_RESET_bm) != 0) ; // configure DMA controller // DMA.CTRL = DMA_CH_ENABLE_bm | DMA_DBUFMODE_CH01_gc; // double buffered with channels 0 and 1 // В данном случае "double buffered" не нужно, проще будет, // и не DMA_CH_ENABLE_bm, а DMA_ENABLE_bm. Хотя они "чисто случайно" совпадают. // Если другие каналы ДМА тоже используются то нужно подумать о приоритетах. К сожалению, // Атмеловский ДМА имеет приоритет меньше, чем процессор. При вызове любой функции или прерывания // идет запушивание кучи используемых регистров, и все это время ДМА не работает. // При выходе - аналогично. Если используются и другие каналы ДМА, то можно задать им не равные // приоритеты (карусель), а иерархию, и раздать каналы ДМА в соответствии со срочностью. // АЦП даже на макс. скорости выдает данные раз в ~160тактов, что хватает и на пуши/попы // и на другие каналы. А вот если есть что-то более скоростное, то можно сделать, например, // DMA.CTRL = DMA_ENABLE_bm | DMA_PRIMODE_CH0123_gc и использовать для АЦП не CH0 а другой, а CH0 // оставить для быстрой передачи. У меня, например, АДС сидит на CH1, а через CH0 сделан видеовывод. DMA.CTRL = DMA_ENABLE_bm; // channel 0 // DMA.CH0.REPCNT = 0; // DMA.CH0.CTRLA = DMA_CH_REPEAT_bm | DMA_CH_BURSTLEN_2BYTE_gc; // ADC result is 2 byte 12 bit word // Когда канал ADC выдаст данные, нам нужно запустить передачу только очередного бурста (2байта), // а не всего буфера, поэтому добавляем DMA_CH_SINGLE_bm. // Если DMA.CH0.REPCNT = 0 (бесконечно) и DMA_CH_REPEAT_bm = 1, то после заполнения буфера // (передачи блока) выдастся прерывание, но не остановится, а сразу продолжится перезапись буфера // с начала. Нам тут это не нужно. Поэтому или DMA.CH0.REPCNT = 1 (тогда DMA_CH_REPEAT_bm не влияет), // или DMA_CH_REPEAT_bm = 0 (DMA.CH0.REPCNT не влияет) или и то и другое. В этом случае ДМА // заполнит буфер и заткнется, даже если АЦП продолжает молотить. // Получили прерывание, обработали буфер, и когда надо, опять разрешили канал через // DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm; DMA.CH0.REPCNT = 1; // передаем 1 блок (буфер) и затыкаемся DMA.CH0.CTRLA = DMA_CH_SINGLE_bm | DMA_CH_BURSTLEN_2BYTE_gc; // по каждой отработке канала АЦП забираем 2-байтовый бурст // DMA.CH0.ADDRCTRL = DMA_CH_SRCRELOAD_BURST_gc | DMA_CH_SRCDIR_INC_gc | DMA_CH_DESTRELOAD_TRANSACTION_gc | DMA_CH_DESTDIR_INC_gc; // буфер - это у нас BLOCK, а не TRANSACTION, соответственно DMA.CH0.ADDRCTRL = DMA_CH_SRCRELOAD_BURST_gc | DMA_CH_SRCDIR_INC_gc | DMA_CH_DESTRELOAD_BLOCK_gc | DMA_CH_DESTDIR_INC_gc; // reload source after every burst, reload dest after every block // Если DMA_CH_REPEAT_bm =0, то TRANSACTION = BLOCK и предыдущий вариант тоже сработает, но это грязно. DMA.CH0.TRIGSRC = DMA_CH_TRIGSRC_ADCA_CH0_gc; // DMA.CH0.TRFCNT = 2048; // Тут сколько надо - если 1000 измерений по 2 байта, значит 2000. И не забыть буфер такой же выделить. DMA.CH0.TRFCNT = 2000; DMA.CH0.DESTADDR0 = (( (uint16_t) buffer_a) >> 0) & 0xFF; DMA.CH0.DESTADDR1 = (( (uint16_t) buffer_a) >> 8) & 0xFF; DMA.CH0.DESTADDR2 = 0; DMA.CH0.SRCADDR0 = (( (uint16_t) &ADCA.CH0.RES) >> 0) & 0xFF; DMA.CH0.SRCADDR1 = (( (uint16_t) &ADCA.CH0.RES) >> 8) & 0xFF; DMA.CH0.SRCADDR2 = 0; // У нас не "double buffered" и CH1 не нужен, все что про него - удаляем. // Если после заполнения буфера нужно получить прерывание, то здесь, до старта, нужно его разрешить, // причем тут (в отличие от CTRLA при запуске) можно именно через RMW - если там стояли флаги, // они прочитаются и при записи заодно сбросятся. DMA.CH0.CTRLB |= DMA_CH_TRNINTLVL_LO_gc; // И, конечно, не забыть организовать сам вектор прерывания (т.е. в шапке обработчика // сказать что это соответствующее прерывание). // DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm; // Тут лучше писать напрямую, без чтения: DMA.CH0.CTRLA = DMA_CH_ENABLE_bm | DMA_CH_SINGLE_bm | DMA_CH_BURSTLEN_2BYTE_gc; // Здесь разрешаем АЦП нужным вам способом - запуском таймера, как тут, или собственно // разрешением АЦП, или разрешением ивента и т.д. // В принципе, можно это и заранее сделать, ну будет АЦП впустую молотить и аллах с ними обоими... TCC1.CTRLA = TC_CLKSEL_DIV1_gc; // start timer, and in turn ADC // DMA.CH0.CTRLB |= DMA_CH_TRNIF_bm; // while ((DMA.CH0.CTRLB & (DMA_CH_CHBUSY_bm | DMA_CH_CHPEND_bm)) == 0) // while ((DMA.CH0.CTRLB & (DMA_CH_CHBUSY_bm | DMA_CH_CHPEND_bm)) != 0) // Это все нам не нужно // В обработчике прерывания нужно сбросить флаг этого прерывания (сам он не сбрасывается!) DMA.INTFLAGS = DMA_CH0TRNIF_bm // Как-нибудь (раз в секунду) нужно опять разрешить ДМА: DMA.CH0.CTRLA = DMA_CH_ENABLE_bm | DMA_CH_SINGLE_bm | DMA_CH_BURSTLEN_2BYTE_gc; // Еще проще, прерывания ДМА вообще не разрешать (DMA.CH0.CTRLB |= DMA_CH_TRNINTLVL_OFF_gc;), // а просто раз в секунду обрабатывать буфер и опять запускать ДМА (первый раз нужно // запустить при инициализации). И обработчик будет просто подпрограммой, не прерыванием.