Частота считывания АЦП

Local
Offline
Зарегистрирован: 16.01.2014

1) Возможна ли ли частота считывания АЦП arduino pro micro 16MHz  10кГЦ ?

2) это лучше делать AnalogRead() ?

3) Как считывать значение АЦП через фиксированные промежутки времени не зная времени одного считывания. Предполагаю, что это время не постоянно ?

Local
Offline
Зарегистрирован: 16.01.2014

PS: конечно же имел в виду 10kHz, а 16 MHz частота МК

sva1509
Offline
Зарегистрирован: 07.12.2012

Доброго времени суток !

1) Возможно до 15 кСамплов точность считывания удовлетворительная.

2) Если считывание с 1-го входа лучше настроить непрерывное считывание, так как первое считывание занимает 23 такта АЦП, последующие 13 тактов. Функция analogRead() считывает разово следовательно требует 23 такта(не процессора, АЦП).

3) Настроить предделитель АЦП и непрерывным считыванием.

ites
Offline
Зарегистрирован: 26.12.2013
static inline uint16_t analogReadFast(byte analog_pin) {
	ADMUX = _BV(REFS0) | analog_pin;
	ADCSRA = _BV(ADSC) | _BV(ADEN) | _BV(ADPS2) | _BV(ADPS0);	//500 kHz
	while (ADCSRA & _BV(ADSC))
		;
	uint16_t ret;
	//	ret = ADCL;
	// ret |= ADCH << 8;
	asm volatile(
			"lds %A0,  %1\n\t"
			"lds %B0,  %2"
			: "=w" (ret): "n"(&ADCL), "n"(&ADCH));
	return ret;
}

Вот быстрый analogRead(). Скорость работы ADC тут повышена со стандартных 125кГц до 500, что даёт примерно 37 тысяч измерений в секунду. Подробнее смотри назначение битов ADPS2-ADPS0. Ну и плюс сам код тут максимально быстрый.


ites
Offline
Зарегистрирован: 26.12.2013

sva1509 пишет:

Доброго времени суток !

1) Возможно до 15 кСамплов точность считывания удовлетворительная.

Стандартно настроенный ADC даёт в теоретическом максимуме 9600 семплов/сек, через стандартный analogRead меньше 8к.

ites
Offline
Зарегистрирован: 26.12.2013

Local пишет:

3) Как считывать значение АЦП через фиксированные промежутки времени не зная времени одного считывания. Предполагаю, что это время не постоянно ?

Насколько точные должны быть промежутки считывания? Вариантов два: считывать в прерывании таймера или самостоятельно отмерять временные промежутки считывания и как только наступает время, считывать. Во втором случае точность может быть ниже, но нет проблем синхронизации данных между процедурой прерывания и основной программой.

Local
Offline
Зарегистрирован: 16.01.2014

ites пишет:
Насколько точные должны быть промежутки считывания? Вариантов два: считывать в прерывании таймера или самостоятельно отмерять временные промежутки считывания и как только наступает время, считывать. Во втором случае точность может быть ниже, но нет проблем синхронизации данных между процедурой прерывания и основной программой.

Количественно ответить не могу, но предположу что в пределах +- 6...8% нормально. Суть в том, чтобы за интервал времени (интервал переменный) посчсотреть, сколько отсчетов было ниже порога (переменный порог) и сколько выше и далее принимать решение. Кроме этой задачи от мк ничего не требуется.

наверное надежнее измерять вне прерывания, чтобы не заиягивать его завершение

как пересчитать такты АЦП в средство измерения времени доступное программе, например millis() ?

 

ites
Offline
Зарегистрирован: 26.12.2013

Local пишет:

ites пишет:
Насколько точные должны быть промежутки считывания? Вариантов два: считывать в прерывании таймера или самостоятельно отмерять временные промежутки считывания и как только наступает время, считывать. Во втором случае точность может быть ниже, но нет проблем синхронизации данных между процедурой прерывания и основной программой.

Количественно ответить не могу, но предположу что в пределах +- 6...8% нормально. Суть в том, чтобы за интервал времени (интервал переменный) посчсотреть, сколько отсчетов было ниже порога (переменный порог) и сколько выше и далее принимать решение. Кроме этой задачи от мк ничего не требуется.

наверное надежнее измерять вне прерывания, чтобы не заиягивать его завершение

как пересчитать такты АЦП в средство измерения времени доступное программе, например millis() ?

 

Такты АЦП делить на 13 (так написано в интернете). Я бы предложил использовать всё-таки micros, чтобы иметь точность выше 0.001с. Навскидку, нетестированный код должен выглядеть примерно так:

void loop() {
	static unsigned long next_measure = 0;
	unsigned long now = micros();
	if (now >= next_measure) {
		uint16_t sv = analogReadFast(0);
		process_measured_value(sv);
		next_measure = now + 100;
	}
}

Есть тонкость с переполнением счётчика, но это упражнение для самостоятельной работы уже :)

Andrey2020
Offline
Зарегистрирован: 09.07.2020

Господа. 

нужна помощь..

в скетчах по Фурье FFT есть вариант чтения через регистры АЦП см. ниже.

Считывание идёт примерно каждые 25мкс

Как регулировать период чтения?  см. текст

while(1) { // reduces jitter
    cli();  // UDRE interrupt slows this way down on arduino1.0
    for (int i = 0 ; i < SAMPLES ; i++) { // save 256 samples
      while(!(ADCSRA & 0x10)); // wait for adc to be ready
      ADCSRA = 0xf5; // restart adc
      byte m = ADCL; // fetch adc data
      byte j = ADCH;
      int k = (j << 8) | m; // form into an int
      k -= 0x0200; // form into a signed int
      k <<= 6; // form into a 16b signed int
      vReal[i] = k; // put real data into bins
      vImag[i] = 0;
    }*/

 

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

Для FFT лучше всего использовать АЦП  free running mode. В этом режиме получаются совершенно одинаковые промежутки между отсчетами автоматически. По окончанию измерения тут же автоматом запускается следующее. Частота выборок определяется prescaler-ом. Сразу после запуска следующего измереня генерится прерывание в котором надо просто сложить в корзинку данные предыдущего измерения.

См пример с ffft lib


#include <avr/pgmspace.h>
#include <ffft.h>
#include <math.h>
#include <Wire.h>

#define ADC_CHANNEL 0

 // FFT_N should be #defined as 128 in ffft.h.
int16_t       capture[FFT_N];    // data capture buffer
volatile byte samplePos = 0;     // Buffer position counter
complex_t     bfly_buff[FFT_N];  // FFT "butterfly" buffer
uint16_t      spectrum[FFT_N/2]; // Spectrum output buffer



// Sampling interrupt
ISR(ADC_vect) { 

  capture[samplePos] = ADC; // 0-1023
  if(++samplePos >= FFT_N) ADCSRA &= ~_BV(ADIE); // Buffer full, interrupt off
}

void setup() { 

 // Init ADC free-run mode; f = ( 16MHz/prescaler ) / 13 cycles/conversion 
  ADMUX  = ADC_CHANNEL; // Channel sel, right-adj, use AREF pin
  ADCSRA = _BV(ADEN)  | // ADC enable
           _BV(ADSC)  | // ADC start
           _BV(ADATE) | // Auto trigger
           _BV(ADIE)  | // Interrupt enable
           _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz
  ADCSRB = 0;                // Free run mode, no high MUX bit
  DIDR0  = 1 << ADC_CHANNEL; // Turn off digital input for ADC pin
  TIMSK0 = 0;                // Timer0 off

  sei(); // Enable interrupts
}
void loop()
{
  while(ADCSRA & _BV(ADIE)); // Wait for audio sampling to finish
  
  fft_input(capture, bfly_buff);   // Samples -> complex #s
  samplePos = 0;                   // Reset sample counter
  ADCSRA |= _BV(ADIE);             // Resume sampling interrupt
  fft_execute(bfly_buff);          // Process complex data
  fft_output(bfly_buff, spectrum); // Complex -> spectrum
  
  //do something with spectrum here
  
  }

Код выдрал из проекта, на компиляцию не проверял

Andrey2020
Offline
Зарегистрирован: 09.07.2020

Использовать Free Running mode это понятно...

отсчёты получаются через 20мкс... что соответствует 50килогерц сэмплинг..

А мне не нужно такой скорости... мне нужно делать измерения в диапазоне от 1 до 2000 Герц..

FFT.h который я использую рассчитан максимум на 256 измерений. Мне не нужно больше. Мне нужна скорость!

при сэмлинге 50кГц получается около 20 килогерц диапазон измерений. а мне надо 2000

при 20 тыс сильно падает разрешение по частоте...

Поэтому встаёт вопрос - как во free ranning mode управлять  сэмплингом?

Скажем сделать 200микросекунд период измерений?

nik182
Offline
Зарегистрирован: 04.05.2015

Читай мануал. У АЦП есть режим запуска от таймера. Заряди таймер на нужный сэмплинг и получишь запуск АЦП с нужной скоростью.

Andrey2020
Offline
Зарегистрирован: 09.07.2020

nik182 пишет:
Читай мануал. У АЦП есть режим запуска от таймера. Заряди таймер на нужный сэмплинг и получишь запуск АЦП с нужной скоростью.

Там мануалы хилые очень...

есть пред делитель от в ADC от 2 до 128

есть пред делитель  в таймере от 8 до 1024...

Есть ещё режим переполнения по регистру сравнения - тут вообще не описано ничего хотя мне кажется именно это надо применить..

В общем нужен хороший пример ибо мутно всё

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

Andrey2020 пишет:
Использовать Free Running mode это понятно...

отсчёты получаются через 20мкс... что соответствует 50килогерц сэмплинг..

Вы приведенный код смотрели? Там частота сэмплинга 9615 Hz

Цитата:
Поэтому встаёт вопрос - как во free ranning mode управлять  сэмплингом?

Частота сэмплинга управляется значением делителя.

// Init ADC free-run mode; f = ( 16MHz/prescaler ) / 13 cycles

Набор возможных частот не велик. Если нужна промежуточная частота, то, как уже упомнали выше, можно использовать режим запуска измерений по таймеру. Читайте даташит на процессор.

nik182
Offline
Зарегистрирован: 04.05.2015

Много хороших примеров в гугле по запросу avr adc timer trigger

Andrey2020
Offline
Зарегистрирован: 09.07.2020

nik182 пишет:

Много хороших примеров в гугле по запросу avr adc timer trigger

да изучал сегодня... видимо придется использовать таймер и причем 1-й - который более сложный а не 0 

потому что если использовать 0-й то не будет работать micros(), а значит будет не померить фактически сэмплинг чтобы убедиться что все работает именно так как задумано те не оценить кайфухи

Andrey2020
Offline
Зарегистрирован: 09.07.2020

Коллеги извените за делетантский вопрос... вот есть у меня эта программа о всеми реистрами АЦП и всякми cli(), sei()... почему при попытке компиляции её на arduino DUE , система пишет что … нет таких переменных и операторов? Что не так с DUE? Там что виртуальный АЦП?

rkit
Offline
Зарегистрирован: 23.11.2016

Там АЦП в облаке.

b707
Offline
Зарегистрирован: 26.05.2017

А вы что, программу для обычной ардуины пытаетесь на Дуе запустить? Когда вы пишете на регистрах - для каждого контроллера надо писать свой код

GAMON
Offline
Зарегистрирован: 03.07.2017

 Использую ADC1, ADC3, ADC5, ADC7. Вопрос: подключает ли внутренний мультиплексор неиспользуемые входы к собственно ADC?

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

GAMON пишет:

 Использую ADC1, ADC3, ADC5, ADC7. Вопрос: подключает ли внутренний мультиплексор неиспользуемые входы к собственно ADC?

Ну если дадите команду подключить, то подключит. А так - нет. К ADC, в данный момент времени, может быть подключен только один пин.

GAMON
Offline
Зарегистрирован: 03.07.2017

)) уточню вопрос: будет ли потрачено машинное время на подключение неиспользуемого входа (входов) через мультиплексор к ADC, если эти входы не инициализированы в коде?

b707
Offline
Зарегистрирован: 26.05.2017

GAMON пишет:

)) уточню вопрос: будет ли потрачено машинное время на подключение неиспользуемого входа (входов) через мультиплексор к ADC, если эти входы не инициализированы в коде?

попробуйте еще раз перечитать предыдущий ответ

GAMON
Offline
Зарегистрирован: 03.07.2017

уточняю: free running mode. на блок-схеме мультиплексор вроде как имеет счетный вход - то есть должен последовательно подключать все каналы к ADC

rkit
Offline
Зарегистрирован: 23.11.2016

В pro micro каналы переключаются только вручную.

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

GAMON пишет:

уточняю: free running mode. на блок-схеме мультиплексор вроде как имеет счетный вход - то есть должен последовательно подключать все каналы к ADC

Где это вам удалось найти такое? Если говорить об Атмеге, то канал выбирается записью MUXx битов в регистр ADMUX и все. Режим работы ADC к выбору канала никакого отношения не имеет.