 Idler (29.04.2017 22:58 - 23:31, просмотров: 453) ответил Ксения на Мне не срочно после заполнения буфера надо начинать повторное его заполнение. Периодичность между обновлениями содержимого буфера около 1 сек, что соответствует времени обновления цифири на дисплее. Т.е. за это время я даже FFT успела бы на этом
 Idler (29.04.2017 22:58 - 23:31, просмотров: 453) ответил Ксения на Мне не срочно после заполнения буфера надо начинать повторное его заполнение. Периодичность между обновлениями содержимого буфера около 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;),
// а просто раз в секунду обрабатывать буфер и опять запускать ДМА (первый раз нужно
// запустить при инициализации). И обработчик будет просто подпрограммой, не прерыванием.
 
 
- 
	
		- Спасибо за труды! У меня получилось вот что (кое-где отступила от рекомендаций):  Ксения(2539 знак., 01.05.2017 00:03 - 01:31)
			
				- Сразу включать DMA_CH_ENABLE_bm нельзя!  Idler(2243 знак., 01.05.2017 01:21 - 01:23)
					
						- Несомненно!  Ксения(2802 знак., 01.05.2017 01:49 - 01:56)
							
						
- Согласен с тем что правильнее  Apтём(1045 знак., 01.05.2017 01:36)
							
								- Но в даташите написано: "The ADC has 12-bit resolution and is capable of converting up to 2 million samples per second".  Ксения(750 знак., 01.05.2017 02:24 - 02:35)
									
								
- В старой ХМеге я получал 6 бит на 300kSps и 10 бит на 40kSps (медленнее не пробовал). На новой я имею полное соответствие DS - 12бит на 300kSps.  Idler(258 знак., 01.05.2017 02:33)
									
										- О кстати, точно! АЦП то у нас конвейерное, сразу 4 канала мерять может.  Apтём(751 знак., 01.05.2017 02:40 - 02:45)
											
												- На 300kSps 12бит - шум около 2-3 единиц, но он даже полезен, программный фильтр добавляет еще пару бит.  Idler(577 знак., 01.05.2017 03:27)
													
														- Про вход с ОУ интересуюсь. Как сделать (но, чтобы по-проще), чтобы на АЦП можно было подавать синусоиду, центрированную относительно нуля (т.е. иногда имеющую напряжение, отрицательное относительно земли). Причем речь даже не об усилении, а об  Ксения(610 знак., 02.05.2017 14:46 - 15:33)
															
														
- У меня тоже опора 2.5в, но вход без ОУ, питание грязное, разводка плохая, ИксМега старая :) - см. картинку.  Ксения(709 знак., 01.05.2017 13:37)
															
														
 
 
 
 
 
- Вроде всё верно. Сойдет вообщем.  Apтём(3167 знак., 01.05.2017 00:49 - 00:58)
 
- Почти всё верно, или может всё верно.  Apтём(948 знак., 29.04.2017 23:51)