Как отключить прерывание ISR(ADC_vect)

black-support
Offline
Зарегистрирован: 22.04.2019

Здравствуйте ! 

Возможно неправильное название темы. 
Смысл в том, что для высокой скорости опроса аналогового входа использую ISR(ADC_vect) с режимом скользящей выборки. Но требуется это не всё время.
То есть, в основном цикле идёт обычный опрос через AnalogRead(A7), но в какой-то момент (пусть будет по кнопке) необходимо включить режим быстрого опроса, а потом также выключить в любой момент, чтобы опять начал работать обычный AnalogRead в цикле. 

Как это можно сделать ? 

Пробовал так ADCSRA = 0x00 и потом заново инициализировать регистр. Отключается и включается нормально. Но в отключённом состоянии AnalogRead начинает просто показывать последнее значение, которое было в прерывании, независимо от реальности. 

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

black-support пишет:

Пробовал так ADCSRA = 0x00 и потом заново инициализировать регистр. Отключается и включается нормально. Но в отключённом состоянии AnalogRead начинает просто показывать последнее значение, которое было в прерывании, независимо от реальности. 

сдается мне, что "обычный AnalogRead" тоже работает через это прерывание.

А зачем выключать? разве с включенным прерыванием AnalogRead не работает?

black-support
Offline
Зарегистрирован: 22.04.2019

Цитата:

А зачем выключать? разве с включенным прерыванием AnalogRead не работает?

Почему-то при включённом прерывании из Loop вообще не срабатывают Serial.write и Serial.print. При этом он способен отлавливать Serial.available и выполнять Serial.read. Стоит выключить прерывание, как в порт начинают идти данные из Loop с помощью Serial.write и Serial.print, но AnalogRead при этом неадекватен. 

black-support
Offline
Зарегистрирован: 22.04.2019

Вот код. 

void setup() {

 ADMUX |= 1<<REFS0 | 1<<REFS1; // используем внутреннее опорное напр.= 1.1В
 ADMUX |= 0<<MUX3 | 1<<MUX2 | 1<<MUX1 | 1<<MUX0; // измеряем на ADC7
  
 ADCSRA |= (1 << ADPS2);                     //Биту ADPS2 присваиваем единицу - коэффициент деления 16
 ADCSRA &= ~ ((1 << ADPS1) | (1 << ADPS0));  //Битам ADPS1 и ADPS0 присваиваем нули
  
 ADCSRB |= 0<<ADTS2 | 0<<ADTS1 | 0<<ADTS0;  // включаем АЦ каналы MUX, режим скользящей выборки 
 
 sei(); // устанавливаем флаг прерывания

 ADCSRA |= (1 << ADATE); // Включаем автоматическое преобразование
 ADCSRA |= (1 << ADIE);  // Разрешаем прерывания по завершении преобразования
 ADCSRA |= (1 << ADEN);  // Включаем АЦП
 ADCSRA |= (1 << ADSC);  // Запускаем преобразование

}


ISR(ADC_vect) 
{

 bt[1] = ADCL; // сохраняем младший байт результата АЦП
 bt[2] = ADCH;   // сохраняем старший байт АЦП

 Serial.write(bt, 2);

}

void loop() {

if (Serial.available() > 0) {  //если есть доступные данные
       
        s_read = Serial.read();  // считываем байт

        if (s_read == 35) {  // знак # - условный выключатель 
          ADCSRA = 0x00;
        }
        else
        {
          ADMUX |= 1<<REFS0 | 1<<REFS1; // используем внутреннее опорное напр.= 1.1В
          ADMUX |= 0<<MUX3 | 1<<MUX2 | 1<<MUX1 | 1<<MUX0; // измеряем на ADC7
          ADCSRA |= (1 << ADPS2);                     //Биту ADPS2 присваиваем единицу - коэффициент деления 16
          ADCSRA &= ~ ((1 << ADPS1) | (1 << ADPS0));  //Битам ADPS1 и ADPS0 присваиваем нули
          ADCSRB |= 0<<ADTS2 | 0<<ADTS1 | 0<<ADTS0;  // включаем АЦ каналы MUX, режим скользящей выборки 
          ADCSRA |= (1 << ADATE); // Включаем автоматическое преобразование
          ADCSRA |= (1 << ADIE);  // Разрешаем прерывания по завершении преобразования
          ADCSRA |= (1 << ADEN);  // Включаем АЦП
          ADCSRA |= (1 << ADSC);  // Запускаем преобразование
        }
}
  Serial.write(120);  // Отсылаю произвольный байт. 
  // Serial.print(analogRead(A7)); 
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Так с чего бы ему работать, если Вы ADCSRA обидели - ноль туда впихнули? Дело в том, что при инициализации туда пишется "что надо", чтобы работал analogRead (посмотрите функцию void init() в файле wiring.c ). Вы же туда запихали ноль и чего-то ждёте.

Попробуйте перед включением режима "по прерыванию" аккуратненько сохранить регистр, а после восстановить его. Тогда скорее всего должно заработать.

black-support
Offline
Зарегистрирован: 22.04.2019

ЕвгенийП пишет:

Так с чего бы ему работать, если Вы ADCSRA обидели - ноль туда впихнули? Дело в том, что при инициализации туда пишется "что надо", чтобы работал analogRead (посмотрите функцию void init() в файле wiring.c ). Вы же туда запихали ноль и чего-то ждёте.

Попробуйте перед включением режима "по прерыванию" аккуратненько сохранить регистр, а после восстановить его. Тогда скорее всего должно заработать.

Как сохранить регистр я не знаю. Залез в файл wiring.c - для меня это тёмный лес. Но тем не менее понял, что после ADCSRA = 0x00 всего лишь надо добавить
ADCSRA |= (1 << ADPS2)|(1 << ADPS1) | (1 << ADPS0);   // коэффициент деления 128 по-умолчанию
ADCSRA |= (1 << ADEN);  // Включаем АЦП 

Осталось понять, почему во время обработки прерывания ISR(ADC_vect) ничего не идёт в порт в основном цикле. 
 
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Я бы мог попытаться Вам помочь, если бы Вы выложили реальный код. Чем короче, тем лучше, но реальный - чтобы компилировался, работал и показывал проблему. То, что Вы выложили - не компилируется.

Да, кстати, контроллер ATMega328?

black-support
Offline
Зарегистрирован: 22.04.2019

Контроллер ATMega168PA. 
Сейчас пересоздал проект, убрал всё лишнее и он совсем сломался. 
В общем, сейчас он просто непрерывно шлёт данные из ISR(ADC_vect), а в loop ничего не работает, даже если там оставить только две строчки Serial.write(120); Serial.print(analogRead(A7)). 


byte s_read; // Прочитанный байт 

byte bt[2]; 
  

void setup() {

  Serial.begin(115200); 

  analogReference(INTERNAL);      // Измерения относительно внутреннего опорного напряжения (на пине AREF)
 
 ADMUX |= 1<<REFS0 | 1<<REFS1; // используем внутреннее опорное напр.= 1.1В
 ADMUX |= 0<<MUX3 | 1<<MUX2 | 1<<MUX1 | 1<<MUX0; // измеряем на ADC7
  
 ADCSRA |= (1 << ADPS2);                     //Биту ADPS2 присваиваем единицу - коэффициент деления 16
 ADCSRA &= ~ ((1 << ADPS1) | (1 << ADPS0));  //Битам ADPS1 и ADPS0 присваиваем нули
  
 ADCSRB |= 0<<ADTS2 | 0<<ADTS1 | 0<<ADTS0;  // включаем АЦ каналы MUX, режим скользящей выборки 
 
 sei(); // устанавливаем флаг прерывания

 ADCSRA |= (1 << ADATE); // Включаем автоматическое преобразование
 ADCSRA |= (1 << ADIE);  // Разрешаем прерывания по завершении преобразования
 ADCSRA |= (1 << ADEN);  // Включаем АЦП
 ADCSRA |= (1 << ADSC);  // Запускаем преобразование

}

// Непрерывная передача результата АЦП
ISR(ADC_vect) 
{
 bt[1] = ADCL; // сохраняем младший байт результата АЦП
 bt[2] = ADCH;   // сохраняем старший байт АЦП
 Serial.write(bt, 2); 
}



void loop() {

  if (Serial.available() > 0) {  //если есть доступные данные
        
        s_read = Serial.read(); // считываем байт

        if (s_read == 35) {   // знак # - условный выключатель
          ADCSRA = 0x00;
          ADCSRA |= (1 << ADPS2)|(1 << ADPS1) | (1 << ADPS0);   // коэффициент деления 128
          ADCSRA |= (1 << ADEN);  // Включаем АЦП
        }
        else
        {
          ADCSRA |= (1 << ADPS2);                     //Биту ADPS2 присваиваем единицу - коэффициент деления 16
          ADCSRA &= ~ ((1 << ADPS1) | (1 << ADPS0));  //Битам ADPS1 и ADPS0 присваиваем нули
          ADCSRA |= (1 << ADATE); // Включаем автоматическое преобразование
          ADCSRA |= (1 << ADIE);  // Разрешаем прерывания по завершении преобразования
          ADCSRA |= (1 << ADEN);  // Включаем АЦП
          ADCSRA |= (1 << ADSC);  // Запускаем преобразование
        }
 
  }

  Serial.write(120);    // Отправляю произвольный байт  
  Serial.print(analogRead(A7));  
  
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Он сломался из-за ошибки в строке №13, там явно лишнее "0<<MUX3".

В общем вот "Ваш" код с комментариями насчёт Ваших ошибок, там же показано как регистры сохранять. Всё нормально работает, я проверял, проверьте сами еще.

static volatile uint16_t lastResult = 0;
static uint8_t regADMUX, regADCSRB, regADCSRA;

// 
//	Этот кусок у Вас был сдублирован дважды
//	это всегда плохая идея, лучше сделать его функцией
// Кроме того, Вы везде использовали |= - зачем? Откуда Вы знаете, что там было?
//
static void setupADCforFreeRunning(void) {
	ADMUX = 
		bit(REFS0) | bit(REFS1) // используем внутреннее опорное напр.= 1.1В
		//
		// далее, у Вас была ошибка. Для ADC7 нужно MUX0-2, а у Вас было 0-3
		| bit(MUX0) | bit(MUX1) | bit(MUX2); // измеряем на ADC7  

	ADCSRB = 0; // Free running режим

	//	У Вас был делитель 16. Можете вернуться к нему, только 
	//	если Вы точно знаете, что делаете!!!
	//	Если не очень, оставьте как у меня.
	ADCSRA = bit(ADPS0) | bit(ADPS1) | bit(ADPS2)	// Делитель 128
		| bit(ADIE)	// Включаем прерывание
		| bit(ADATE)// Включаем автоматический триггер
		| bit(ADEN)	// Включаем ADC
		| bit(ADSC);	//	Запускаем преобразование
}

//
//	Сохраняем настройки ADC
//
void saveADCSettings(void) {
	regADMUX = ADMUX;
	regADCSRB = ADCSRB;
	regADCSRA = ADCSRA;
}
//
//	Восстанавливаем настройки ADC
//
void restoreADCSettings(void) {
	ADCSRA = regADCSRA;
	ADMUX = regADMUX;
	ADCSRB = regADCSRB;
}

void setup(void) {
	Serial.begin(57600);
	Serial.println("Onwards!");
	analogReference(INTERNAL);
	saveADCSettings();
	setupADCforFreeRunning();
}

// В прерывании лучше лишнего не делать
ISR(ADC_vect) {
	lastResult = ADC;
}

void loop() {
	static bool freeRunning = true;
	
	if (Serial.available() > 0) {
		const char ch = Serial.read();
		if (ch != '\r' && ch != '\n') { // игнорируем перевод строки
			if (ch == '#') {
				restoreADCSettings();
				freeRunning = false;
				Serial.println("analogRead mode");
			} else {
				setupADCforFreeRunning();
				freeRunning = true;
				Serial.println("Free running mode");
			}
		} 
	}
	if (! freeRunning) lastResult = analogRead(7);
	
	//
	//	Раз в секунду печатаем текущее напряжение
	//
	static uint32_t lastTime = 0;
	const uint32_t currentTime = millis();
	if (currentTime - lastTime >= 1000) {
		lastTime = currentTime;
		cli();
		const uint32_t result = lastResult;
		sei();
		Serial.println(result);
	}
}

Только этот код для 328 !!! А то я сейчас в гостинице. 328 у меня есть с собой, а 168 - нету.

По идее, на 168 должен работать, но если не сумеете запустить для 168 уж совсем никак, пишите - я попробую на симуляторе в протеусе.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

ЕвгенийП, надо удочку товарищу давать , а не рыбу :) Он вон чо пишет-то, загляденье одно.

ADCSRB |= 0<<ADTS2 | 0<<ADTS1 | 0<<ADTS0;  // включаем АЦ каналы MUX, режим скользящей выборки 
sei(); // устанавливаем флаг прерывания

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

dimax пишет:

надо удочку товарищу давать , а не рыбу :) 

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

astwo
Offline
Зарегистрирован: 10.07.2019

Упал гостиный сервис. Даже срамных девок в нумера не поставляют. Скоро народ гостиницы посещать не будет.

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

Не надо с регистрами ацп ничего делать. Достаточно запретить прерывания или разрешить. Всё будет работать. Вот только при максимальной выборке данных ацп на остальное не останется времени. Читайте аналогрид сколько надо и выводите. Когда понадбится максимальная выборка нужно перейти в подпрограмму которая будет только считать количество данных и заполнять массивы по прерыванию. Иначе смысла переходить на прерывания нет. Сериалпринт съест весь выигрыш от применения прерывания. 

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

ЕвгенийП пишет:
даже срамных девок поблизости нет :)

Где, в Анапе?????

1. https://www.youtube.com/watch?v=X_qaiazq_rc

2. https://www.youtube.com/watch?v=ViRKJ0Pt4Fs

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

А чего не приехал-то, ловец? Приезжай и лови. А я лучше с ардуиной потрахаюсь :-)

Komandir
Offline
Зарегистрирован: 18.08.2018

Через прерывания ни разу не быстрее прямой работы  с портами. Вход и выход в прерывание - это уже 10 тактов !!! Обращение к Serial внутри прерывания, созданного для ускорения работы с АЦП - это вообще КАЛАМБУР !!!

А по теме - надо замаскировать прерывания от АЦП.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

ЕвгенийП пишет:

 А я лучше с ардуиной потрахаюсь :-)

Месье знает толк!

----------------

Литр самогонки подходил к концу....

Ах, для чего же вам чужая Аргентина,

Когда присутствует простая Ардуина?

....

дальше не придумал...

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

black-support пишет:
Смысл в том, что для высокой скорости опроса аналогового входа использую ISR(ADC_vect) с режимом скользящей выборки. Но требуется это не всё время.

То есть, в основном цикле идёт обычный опрос через AnalogRead(A7), но в какой-то момент (пусть будет по кнопке) необходимо включить режим быстрого опроса, а потом также выключить в любой момент, чтобы опять начал работать обычный AnalogRead в цикле.

Для увеличения скорости АЦП надо не прерывания использовать, а поднять частоту сэмплирования. Делается это путем изменения делителя в регистре ADCSRA

Прерывания имеет смысл использовать когда мы хотим делать что-то еще пока происходит преобразование. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

wdrakula пишет:

Месье знает толк!

Первое видео, что мне дали (где Михалыч говорит: "Рыба есть") напомнило историю года 2005-2006 где-то. Идёт хоккейный матч в Тольятти, ворота сдвинулись и дырка под стойку забилась, судья позвал работника стадиона. Тот вышел с дрелью и стал высверливать дырку - обычное дело, почти в каждой игре бывает. Но тут, только он ткнулся дрелью в лёд, диджей, вместо музыки, включил фразу Кузьмича "Рыбы здесь здесь нет, практически нет". Там вся арена легла, включая игроков и тренеров обеих команд.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

asam пишет:
когда мы хотим делать что-то еще пока происходит преобразование.
Вместо того, чтобы спать в режиме "снижение шума ADC" :)

black-support
Offline
Зарегистрирован: 22.04.2019

ЕвгенийП, большое спасибо за код. 

На 168 работает. 

ЕвгенийП пишет:

Он сломался из-за ошибки в строке №13, там явно лишнее "0<<MUX3".

Я руководствовался этой таблицей: 

     MUX[3:0] (Multiplexer Selection Input) — биты выбора аналогового входа. Значения: 

MUX[3:0] Аналоговый вход
0000 ADC0
0001 ADC1
0010 ADC2
0011 ADC3
0100 ADC4
0101 ADC5
0110 ADC6
0111 ADC7
1000 Температурный датчик
1001 Зарезервировано
1010 Зарезервировано
1011 Зарезервировано
1100 Зарезервировано
1101 Зарезервировано
1110 1.1V (VBG)
1111 0V (GND)
  •  

ЕвгенийП пишет:

Этот кусок у Вас был сдублирован дважды 
это всегда плохая идея, лучше сделать его функцией

Это понятно, просто, когда что-то изучаю, стараюсь делать "в лоб", чтобы меньше ошибок было. 

ЕвгенийП пишет:

У Вас был делитель 16. Можете вернуться к нему, только если Вы точно знаете, что делаете!!!

Делитель 16 - для ускорения работы. Сейчас вообще выставил 4. С 2 почему-то начинает возвращать просто 1023. 
Для этих же целей использую serial.write, вместо serial.print. Например, чтобы отправить число 1022 write потребуется отправить 2 байта, а print - 4 байта. По крайней мере, я думаю, что это так. :) 
И скорость порта - 1000000. 

ЕвгенийП пишет:

ADCSRA = bit(ADPS0) | bit(ADPS1) | bit(ADPS2)

Как это работает я, к сожалению, вообще не понял. Поэтому решил писать так: ADCSRA = 0b11101010; // Коэф. 4. 

ЕвгенийП пишет:

В прерывании лучше лишнего не делать

Как раз-то в прерывании я и произвожу отправку данных, чтобы она происходила максимально быстро. Из цикла получается медленней. 

ЕвгенийП пишет:

if (Serial.available() > 0) {
        const char ch = Serial.read();
 

Для чего здесь переменная объявляется константой ? И почему её не сделать глобальной ? Тоже самое с currentTime и result

ЕвгенийП пишет:

static volatile uint16_t lastResult = 0;

const uint32_t result = lastResult;

Почему lastResult имеет тип uint16_t, а result - uint32_t ? 

ЕвгенийП пишет:

cli();
const uint32_t result = lastResult;
sei();

Зачем останавливаются прерывания ? ШИМ ведь тоже потухнет ? 

В общем, сейчас изменил Ваш код, под свои нужды. Получилось так. 
 

byte bt[2]; 

void setup(void) {
  Serial.begin(1000000);
  Serial.println("Onwards!");
  analogReference(INTERNAL);
  saveADCSettings();
  setupADCforFreeRunning();
}

ISR(ADC_vect) {
  bt[0] = ADCL; 
  bt[1] = ADCH; 
  Serial.write(bt, 2);
}

void loop() {
 
  static bool freeRunning = true;
  
  if (Serial.available() > 0) {
    const char ch = Serial.read();
    if (ch != '\r' && ch != '\n') { // игнорируем перевод строки
      if (ch == '#') {
        restoreADCSettings();
        freeRunning = false;
        Serial.println("analogRead mode");
      } else {
        setupADCforFreeRunning();
        freeRunning = true;
        Serial.println("Free running mode");
      }
    } 
  }

  //Serial.write(bt, 2);

}

В таком виде данные выводятся быстро, но отключалка не работает. 
Если Serial.write(bt, 2) перенести из ISR(ADC_vect) в loop, то работает немного медленней и отключалка работает. Такое впечатление, что если Serial.write(bt, 2) в прерывании, то до loop вообще очередь не доходит. 

ЕвгенийП пишет:

 

 
black-support
Offline
Зарегистрирован: 22.04.2019

dimax пишет:

ЕвгенийП, надо удочку товарищу давать , а не рыбу :) Он вон чо пишет-то, загляденье одно.

ADCSRB |= 0<<ADTS2 | 0<<ADTS1 | 0<<ADTS0;  // включаем АЦ каналы MUX, режим скользящей выборки 
sei(); // устанавливаем флаг прерывания

Это должна была быть очень хорошая удочка. :) До сохранения регистров я бы ещё долго не додумался. 
Вообще, я сам стараюсь готовую рыбу никому не давать, но в этом случае всё равно мне потребуется здорово редактировать код под свою задачу, поэтому придётся разобраться. 

 

 

black-support
Offline
Зарегистрирован: 22.04.2019

nik182 пишет:

Достаточно запретить прерывания или разрешить. 

Вообще все прерывания ? Так не получится. Вернее, так нельзя, они мне нужны будут. 

black-support
Offline
Зарегистрирован: 22.04.2019

О, кажется я додумался, как здорово ускорить. Теперь я в прерывании накапливаю данные в массиве на 500 байт, а потом их скопом отправляю. Только надо какую-то синхронизацию придумать, а то данные теряются. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

black-support пишет:

Я руководствовался этой таблицей: 

Не похоже. В этой таблице написано

black-support пишет:
 

0111 ADC7
... ...
1111 0V (GND)

Вы всунули туда 1111 и читаете потенциал земли. Вы этого хотели?

black-support пишет:

Делитель 16 - для ускорения работы. Сейчас вообще выставил 4. С 2 почему-то начинает возвращать просто 1023. 

Т.е. Вы уже поняли, что чем меньше делитель, тем хуже точность, и точность с делителем 4 Вас устраивает? Дело Ваше.

black-support пишет:

Как раз-то в прерывании я и произвожу отправку данных, чтобы она происходила максимально быстро.

Это пожалуйста, только работать не будет (см. ниже)

black-support пишет:
 

Для чего здесь переменная объявляется константой ? И почему её не сделать глобальной ?

Константой - потому, что её никто не планирует изменять, а не глобальной - потому, что она нужна только в этой функции, так нафига её делать доступной для остальных? Кроме того, в данном случае, она ещё и память не будет занимать (будет, но не всё время, а только внутри своих фигурных скобок).

Читайте мои посты про память в разделе "программирование" их там три штуки сверху приколоты.

black-support пишет:
 

Почему lastResult имеет тип uint16_t, а result - uint32_t ? 

По невнимательности. Должно быть 16

black-support пишет:
 

Зачем останавливаются прерывания ? ШИМ ведь тоже потухнет ? 

ШИМ? С какого перепугу? Он нормально будет работать.

А прерывания закрываются. чтобы переменная не изменилась пока её вычитываем из памяти. Если не закрывать прерывания, то иногда (нечасто) будут пролетать необъяснимые глюки (а виноваты в глюках, разумеется, будут криворукие китайцы, которые неправильную ардуину сделали)

black-support пишет:
 

В общем, сейчас изменил Ваш код, под свои нужды. Получилось так. 
В таком виде данные выводятся быстро, но отключалка не работает.
Такое впечатление, что если Serial.write(bt, 2) в прерывании, то до loop вообще очередь не доходит. 

Ну, вот, что и следовало доказать. Как я Вам уже писал, нефиг в прерывании длительные операции делать. У Вас прилетает новое прерывание, когда ещё старое не обработалось полностью, вот лупу времени и не остаётся..

black-support
Offline
Зарегистрирован: 22.04.2019

ЕвгенийП пишет:

Вы всунули туда 1111 и читаете потенциал земли. Вы этого хотели?

Нет, не этого хотел. Теперь понял. 

ЕвгенийП пишет:

Т.е. Вы уже поняли, что чем меньше делитель, тем хуже точность, и точность с делителем 4 Вас устраивает? Дело Ваше.

Да, это понятно. Ищу компромисс. Сейчас ещё сделал чтение только одного ADCH бита через ADLAR = 1. Вроде неплохо. 

ЕвгенийП пишет:

Константой - потому, что её никто не планирует изменять, а не глобальной - потому, что она нужна только в этой функции, так нафига её делать доступной для остальных? Кроме того, в данном случае, она ещё и память не будет занимать (будет, но не всё время, а только внутри своих фигурных скобок).

Читайте мои посты про память в разделе "программирование" их там три штуки сверху приколоты.

Доступной для всех, действительно не нужно. И если объявление переменной при каждом проходе не тормозит программу, то можно и так. То что не будет глобально занимать память это может и хорошо, а может и нет. Не возникнет ли там ситуация, что ей просто не хватит места из-за переполнения ? Просто, если все переменные глобальные, то под них уже точно выделена память. 

В общем, про память почитаю, спасибо. 

ЕвгенийП пишет:

ШИМ? С какого перепугу? Он нормально будет работать. 

Где-то читал, что тоже отключится. Наверное, обманули. Или с таймерами перепутали. 

ЕвгенийП пишет:

Ну, вот, что и следовало доказать. Как я Вам уже писал, нефиг в прерывании длительные операции делать. У Вас прилетает новое прерывание, когда ещё старое не обработалось полностью, вот лупу времени и не остаётся..

 

Сделал так.  

byte bt[512]; 

ISR(ADC_vect) {

bt[i] = ADCH;
if (i == 511){
    Serial.write(bt2, 512);
    i = 0; 
  } else {
    i++; 
  }

} 

И скорость увеличилась и отключалка работает. Правда, почему то ещё очень долго поступают данные в программу на ПК. Наверное ещё надо с буферами разбираться. 
И массив много памяти занимает, мне на остальное не хватит. Надо наверное динамически выделять и освобождать, если это возможно. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

black-support пишет:

Сделал так.  

byte bt[512]; 

ISR(ADC_vect) {

bt[i] = ADCH;
if (i == 511){
    Serial.write(bt2, 512);
    i = 0; 
  } else {
    i++; 
  }

} 

Ну, толком-то работать не будет, будет глючить, но если Вам так нравится - делайте.

Неясно ещё как и где описана i.

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

Скорость передачи сериала 11520 байт в секунду на скорости 115200 бит. Это 40 раз медленнее чем может оцифровывать 10 битная ардуина. После заполнения буфера сериала может произойти всё что угодно, в том числе потеря данных. Вывод из прерывания в сериал это большая глупость, выполняемая из за не понимания процессов обработки данных. Аналогрид без всяких прерываний будет работать точно с такой же скоростью. Как уже писали, прерывание нужно только если в этот момент делать что то ещё. У Вас нет никаких параллельных работ. Зачем все эти телодвижения? 

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

black-support пишет:

 

Сделал так.  

byte bt[512]; 

ISR(ADC_vect) {

bt[i] = ADCH;
if (i == 511){
    Serial.write(bt2, 512);
    i = 0; 
  } else {
    i++; 
  }

} 

И

Тут уже не раз говорили - так делать нельзя! Использовать Serial в прерывании  крайне нежелеательно. Делать это надо в крайнем случае (например при отладке) и только хорошо понимая как именно там все происходит под капотом, а вы до этого не дошли еще.

Может вы попробуйте описать в чем состоит ваша изначальная задая ичто именно вы хотете получить (абстрагируюясь от ардуины), а вам подскажут каким путем это лучше сделать.

 

black-support
Offline
Зарегистрирован: 22.04.2019

nik182 пишет:

Скорость передачи сериала 11520 байт в секунду на скорости 115200 бит. Это 40 раз медленнее чем может оцифровывать 10 битная ардуина. После заполнения буфера сериала может произойти всё что угодно, в том числе потеря данных. Вывод из прерывания в сериал это большая глупость, выполняемая из за не понимания процессов обработки данных. Аналогрид без всяких прерываний будет работать точно с такой же скоростью. Как уже писали, прерывание нужно только если в этот момент делать что то ещё. У Вас нет никаких параллельных работ. Зачем все эти телодвижения? 

 

Так работает быстро. 

ISR(ADC_vect) {
  bt[0] = ADCL; 
  bt[1] = ADCH; 
  Serial.write(bt, 2);
}

void loop() {

}

А так работает медленней. 

ISR(ADC_vect) {
  bt[0] = ADCL; 
  bt[1] = ADCH; 
}

void loop() {
  Serial.write(bt, 2);
}

Да, мне до понимания всех процессов очень далеко. Но тот код проверено работает. Можете объяснить, почему первое быстрей второго ? Под быстротой я понимаю увеличение частоты дискретизации непрерывного изменяющегося сигнала. Перенос Serial.write(bt, 2) в loop снижает эту частоту более чем на 10%. 

Параллельных работ нет только сейчас, потому что для тестирования скорости и возможности включения/выключения быстрой выборки они не нужны. 

black-support
Offline
Зарегистрирован: 22.04.2019

asam пишет:

Тут уже не раз говорили - так делать нельзя! Использовать Serial в прерывании  крайне нежелеательно. Делать это надо в крайнем случае (например при отладке) и только хорошо понимая как именно там все происходит под капотом, а вы до этого не дошли еще.

Может вы попробуйте описать в чем состоит ваша изначальная задая ичто именно вы хотете получить (абстрагируюясь от ардуины), а вам подскажут каким путем это лучше сделать.

 

Целиком задача большая. То, что здесь обсуждается будет лишь маленьким кусочком всего кода. Абстрагироваться вряд ли получится. Если коротко, то на всех восьми входах будет считываться напряжение от разных источников и неспешно передаваться на ПК. Четыре раза в секунду будет достаточно. Периодически, оператору будет нужно видеть форму сигнала на каком-нибудь входе и для этого будет производится максимально быстрое считывание. В этом режиме не будет происходить ничего, кроме считывания сигнала. Частота сигнала от 0 Гц до примерно 5 кГц, но, чем больше будет частота дискретизации, тем лучше. В остальное время, когда данные передаются медленно, будет выполняться много всяких вычислений и управление периферией, в том числе 10 разрядным ШИМ с изменением частоты.
Если есть возможность увеличить разрядность ШИМ больше 10, буду рад узнать как это сделать. 

black-support
Offline
Зарегистрирован: 22.04.2019

ЕвгенийП пишет:

Ну, толком-то работать не будет, будет глючить, но если Вам так нравится - делайте.

Неясно ещё как и где описана i.

Слово "Нравится" здесь не совсем уместно. Как получается, на что ума хватает, так и делаю.
Если есть способ ещё больше увеличить скорость выборки, поделитесь им, пожалуйста. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

black-support пишет:

Так работает быстро. 

А так работает медленней. 

Ваша беда в том, что Вы не понимаете почему и плетёте вокруг этого какую-то ересь.

black-support пишет:

тот код проверено работает

Вы проверять не умеете.

Я Вам уже писал, что тот код глючный до безобразия. Не верите - дело Ваше.

black-support пишет:

Под быстротой я понимаю увеличение частоты дискретизации непрерывного изменяющегося сигнала. 

Вы можете для начала объяснить пару вещей

1) какая частота дискретизации нужна?

2) насколько критично точно выдерживать эту частоту? И насколько точно?

black-support
Offline
Зарегистрирован: 22.04.2019

ЕвгенийП пишет:

black-support пишет:

Так работает быстро. 

А так работает медленней. 

Ваша беда в том, что Вы не понимаете почему и плетёте вокруг этого какую-то ересь.

Да, я не понимаю (хоть и догадываюсь). Но ведь быстрее работает, это факт. 

 

ЕвгенийП пишет:

black-support пишет:

тот код проверено работает

Вы проверять не умеете.

Я Вам уже писал, что тот код глючный до безобразия. Не верите - дело Ваше.

Да почему же не верю ? Какого рода глюки я должен наблюдать ? Если периодические "провалы" сигнала до нуля и случающиеся потери целый кусков сигнала по времени, то это я уже видел. Дело в том, что приёмная сторона на ПК примерно в такой же стадии, что и программа для Ардуино и не имеет нормальной привязки ко времени, поэтому четко отследить все потери не получается. Сейчас я пытаюсь, если можно так сказать, пощупать все возможности контроллера и потом принять решение о допустимых предельных параметрах работы будущего прибора. 

 

ЕвгенийП пишет:

black-support пишет:

Под быстротой я понимаю увеличение частоты дискретизации непрерывного изменяющегося сигнала. 

Вы можете для начала объяснить пару вещей

1) какая частота дискретизации нужна?

2) насколько критично точно выдерживать эту частоту? И насколько точно?

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

2. Критично. Про точность затрудняюсь ответить. Опять же, чем точнее, тем лучше, но во всём приходится искать компромисс. Могу сказать, что если где-то произойдёт сбой при рисовании сигнала, хоть по времени, хоть по величине - трагедии не произойдёт, ничего не сломается и все будут живы и здоровы. Просто, при подозрении на брак, будет проведено повторное считывание. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

black-support пишет:

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

2. Критично. Про точность затрудняюсь ответить. Опять же, чем точнее, тем лучше, 

Это не разговор. Это означает, что Вы сами не знаете чего хотите. Когда узнаете - скажете.

Начинать надо как раз с этого: определиться "что нужно". А потом уже думать, как делать. Может ардуина в принципе для этой задачи не подходит.

Komandir
Offline
Зарегистрирован: 18.08.2018

Второй вариант - ЧУШЬ !!! Выводить надо только новые данные - значит нужен флаг обновления.

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

black-support пишет:

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

2. Критично. Про точность затрудняюсь ответить. Опять же, чем точнее, тем лучше, но во всём приходится искать компромисс. Могу сказать, что если где-то произойдёт сбой при рисовании сигнала, хоть по времени, хоть по величине - трагедии не произойдёт, ничего не сломается и все будут живы и здоровы. Просто, при подозрении на брак, будет проведено повторное считывание. 

АЦП у ардуины может делать почти 80 тысяч измерений в секунду. И что вы с таким потоком данных делать будете? Ну хорошо, при работе АЦП с максимальным разрешением он дает 15 тысяч измерений в секунду. Для контроля формы сигнала в 5 КГц это маловато, а для передачи по сериал порту - многовато. Можно поднять скорость передачи и выше чем 115200, но тут тоже есть свои подводные камни. 

И не надо использовать прерывания для получения большой скорости. По крайней мере так как это делаете вы.

 

Если хоти получить нормальное устройство - надо начинать с ТЗ где продумать какие данные, с какой точностью и с какой частотой надо измерять и передавать. Вполне возможно что ардуино и не потянет и надо будет брать что нибудь из STM32

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

nik182, да, прошу пардону, я сослепу на планшете нолик лишний разглядел.  тама 11520 было, а я 115200 увидел.  

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

 Аsam! Просто с языка снял. Блюпил может 8 каналов со скоростью 100000 сэмплов в секунду по каждому каналу выдать в компьютер по своему USB  без задержек на скорости 12 МБит. Вся программа займёт 10 строк своего текста и сколькото строк куба. 

black-support
Offline
Зарегистрирован: 22.04.2019
ЕвгенийП пишет:
 
Это не разговор. Это означает, что Вы сами не знаете чего хотите. Когда узнаете - скажете.
 
 
Ожидаемый ответ. Ну, хочу характеристики осциллографа за 45000 рублей. Бред ? Конечно. 
 
ЕвгенийП пишет:
 
Может ардуина в принципе для этой задачи не подходит.
 
 
Вот об этом я и написал "Сейчас я пытаюсь, если можно так сказать, пощупать все возможности контроллера и потом принять решение о допустимых предельных параметрах работы будущего прибора. " То есть, на что способна Ардуино, так и будет использоваться. 
 
ЕвгенийП пишет:
 
Начинать надо как раз с этого: определиться "что нужно". А потом уже думать, как делать. 
 
 
Как ещё объяснить ? 
Решил я купить машину, чтобы ездить на работу и на дачу. Определился с характеристиками. Оказалось, что такая машина стоит 30 млн. рублей, и на неё, может, за всю жизнь не накопишь. Отказаться от идеи ? Или же просто посчитать, сколько денег есть сейчас в кошельке и выбрать лучшее, что возможно купить на эти деньги ? 
Поэтому подход "Сначала определиться" не всегда применим. Я определился с задачами (ездить на работу и на дачу на собственном автомобиле), а вот характеристики (насколько быстро и комфортно буду ездить) - это уже как получится. 
 
Вообще, не проще было бы сразу назвать ограничения, чем добиваться от меня требуемых цифр, а потом объявить, что "Ардуина не потянет". Для чего ? Чтобы я бросил затею, вместо снижения требований ? 

asam пишет:

для передачи по сериал порту - многовато. Можно поднять скорость передачи и выше чем 115200, но тут тоже есть свои подводные камни. 

У меня сейчас на 1000000 работает.
И для этого же использую write, а не print. Это-то хоть разумное решение ? 

asam пишет:

И не надо использовать прерывания для получения большой скорости. По крайней мере так как это делаете вы. 

Какие есть ещё варианты ? Какой вообще предел скорости (дискретности) для оцифровки сигнала ? 

asam пишет:

Если хоти получить нормальное устройство - надо начинать с ТЗ где продумать какие данные, с какой точностью и с какой частотой надо измерять и передавать. 

Частоту я назвал - 0-5кГц, большую часть времени будет середина диапазона. Если при этом 1 миллисекунда будет представлена 100 точками - это будет вполне себе здорово. Собственно, при такой вводной и частота уже не так важна, на сколько я понимаю. Вот только вряд ли это удастся осуществить. 50 точек на миллисекунду тоже вполне не плохо. Но и такого результата, наверное, не достичь. Поэтому придётся довольствоваться любым разрешением, которое только удастся получить. 

asam пишет:

Вполне возможно что ардуино и не потянет и надо будет брать что нибудь из STM32

Понимаете, я только учусь работать с МК. Это для меня совершенно новое направление. Только вместо стандартных уроков по миганию лампочками я взял реальную задачу. Ардуино удобно тем, что по ней много информации. А так-то, конечно я бы предпочёл МК с большими возможностями. К тому же, аппетит приходит во время еды. Вначале задача была намного скромнее. 

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

black-support пишет:

Понимаете, я только учусь работать с МК. Это для меня совершенно новое направление. Только вместо стандартных уроков по миганию лампочками я взял реальную задачу. 

Напрасно.  Ну и коль ты только начал, почему бы сразу не прочитать за матчасть умные книшки, а не сыпать дебильными вопросами на форумах? 

black-support
Offline
Зарегистрирован: 22.04.2019

DetSimen пишет:

black-support пишет:

Понимаете, я только учусь работать с МК. Это для меня совершенно новое направление. Только вместо стандартных уроков по миганию лампочками я взял реальную задачу. 

Напрасно.  Ну и коль ты только начал, почему бы сразу не прочитать за матчасть умные книшки, а не сыпать дебильными вопросами на форумах? 

Во-первых, читал. С чего ты взял что не читал ? (сейчас последует вопрос, что именно я читал) 
Во-вторых, если всё решается чтением книжек, то как ещё существуют форумы ? Только чтобы хвастаться достижениями ? 
В-третьих, какой вопрос дебильный и почему ? 

О каких форумах ты говоришь ? И что напрасно: что учусь или что лампочками не мигаю ? (вообще, мигаю, только через ШИМ и контролируя ток при этом). 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

black-support пишет:

Во-первых, читал. С чего ты взял что не читал ? (сейчас последует вопрос, что именно я читал) 

Во-вторых, если всё решается чтением книжек, то как ещё существуют форумы ? Только чтобы хвастаться достижениями ? 
В-третьих, какой вопрос дебильный и почему ? 

О каких форумах ты говоришь ? И что напрасно: что учусь или что лампочками не мигаю ? (вообще, мигаю, только через ШИМ и контролируя ток при этом). 

Ах, виноват, боярин, не признал учёного.  

black-support
Offline
Зарегистрирован: 22.04.2019

Классическое развитие событий. 

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

black-support пишет:

Классическое развитие событий. 

Абсолютно точно! Очередной свежеиспеченный умник со своими бредовыми идеями, который не хочет слушать то, что ему говорят опытные люди.

black-support
Offline
Зарегистрирован: 22.04.2019

asam пишет:

black-support пишет:

Классическое развитие событий. 

Абсолютно точно! Очередной свежеиспеченный умник со своими бредовыми идеями, который не хочет слушать то, что ему говорят опытные люди.

Вообще-то главный вопрос, ради которого была создана тема, был решён практически сразу, за что я поблагодарил Евгения. Затем, пойдя по своему пути я столкнулся с ещё одной проблемой из-за незнания происходящих процессов, и причину которой также удалось понять благодаря Евгению.
А вот потом все в один голос начали говорить, что так (как я потом сделал) делать нельзя и что это не правильно. Но, я разве с кем-то спорил ?! Хоть раз сказал, что так можно и правильно ? Я сказал, что так работает быстрее, какой получил результат, о таком и объявил. И даже, напротив, на неоднократные просьбы рассказать как добиться скорости и сделать правильно, ответа не последовало. И вместо вразумительных объяснений, звучала старая пластинка про "нельзя". Ни об отсутствии глюков, ни о сохранности данных я не от утверждал. 
Так за что же меня обозвали дебилом ? А умником не за то ли, что после тщетных попыток объяснить всем что я делаю и что требуется, я резко ответил на хамство DetSimen ? Если на этом форуме принято слепо копировать предоставленный код, не разбираясь в нём, и довольствоваться им, то это печально. Так уж сложилось, что я не приемлю ультимативных рекомендаций, мне нужно объяснение. Если не беру слепо ваш код, это не означает, что я с вами не согласен. О чем разгорелся спор в этой теме - не понятно. Спора-то нет. 

 

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

black-support пишет:

 Хоть раз сказал, что так можно и правильно ?

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

Да плюс вы и не читаете что вам пишут. Вот в #36 я писал "АЦП у ардуины может делать почти 80 тысяч измерений в секунду.... при работе АЦП с максимальным разрешением он дает 15 тысяч измерений в секунду", а в #39 вы спрашиваете " Какой вообще предел скорости (дискретности) для оцифровки сигнала ? "

Работа на максимальной скорости снижает точность измерения, вот вас и спрашивают "какая точность требуется" и где конкретный ответ??

От атмеги 168 или 328 вы можете получить максимум 77 КГц выборки с низкой точность. При 5 КГц сигнале это вам даст не сто и даже не 50 а всего около 15 точек на период. Вас это устроит?

Ну а если надо сделать 100 измерений на максимальной скорости, то да нужно будет использовать прерывания, но ни в коем случае не вызывать там SerialPrint, поскольку это сильно все будет тормозить. А надо 

-Поставить минимальный делитель
-Перевести АЦП во Free Running Mode
-Разрешить прерывание по окончанию оцифровки
-В прерывании ни делать ничего кроме считывания данных с АЦП и складывания в память
-А уж после окончания 100 или сколько там еще циклов запрещаем прерывания от АЦП и передаем полученные данные в сериал порт.

Но все равно больше 77К на вашем чипе не выйдет. Поэтому стоит подумать о STM32

black-support
Offline
Зарегистрирован: 22.04.2019

asam пишет:

Да плюс вы и не читаете что вам пишут. Вот в #36 я писал "АЦП у ардуины может делать почти 80 тысяч измерений в секунду.... при работе АЦП с максимальным разрешением он дает 15 тысяч измерений в секунду", а в #39 вы спрашиваете " Какой вообще предел скорости (дискретности) для оцифровки сигнала ? "

Я каждое сообщение перечитываю по многу раз. 
А вопрос был ответом на это "Для контроля формы сигнала в 5 КГц это маловато, а для передачи по сериал порту - многовато. Можно поднять скорость передачи и выше чем 115200, но тут тоже есть свои подводные камни. " Но без вводных данных он глупый, я понимаю. Предполагал, что кто-нибудь сошлётся на свой опыт подобной работы и скажет с каким сигналом реально работать для тех или иных задач. 

asam пишет:

Работа на максимальной скорости снижает точность измерения, вот вас и спрашивают "какая точность требуется" и где конкретный ответ??

От атмеги 168 или 328 вы можете получить максимум 77 КГц выборки с низкой точность. При 5 КГц сигнале это вам даст не сто и даже не 50 а всего около 15 точек на период. Вас это устроит?

Да, меня это устроит. Если это предел, то с ним и буду работать. Я несколько раз об этом говорил. Получить максимум скорости. Это для меня будет отправной точкой. По ходу испытаний будут проводится корректировки - снижаться скорость в угоду точности и наоборот, пока не будет достигнут компромисс. Но он будет достигаться опытным путём в реальных условиях, с реальным сигналом. 

asam пишет:

Ну а если надо сделать 100 измерений на максимальной скорости, то да нужно будет использовать прерывания, но ни в коем случае не вызывать там SerialPrint, поскольку это сильно все будет тормозить. А надо 

-Поставить минимальный делитель
-Перевести АЦП во Free Running Mode
-Разрешить прерывание по окончанию оцифровки
-В прерывании ни делать ничего кроме считывания данных с АЦП и складывания в память
-А уж после окончания 100 или сколько там еще циклов запрещаем прерывания от АЦП и передаем полученные данные в сериал порт. 

Да, я тоже к этому пришёл, а Вы подтвердили это направление. И nik182 об этом же говорил в сообщении №12, только тогда это было совершенно непонятно. 

asam пишет:

Но все равно больше 77К на вашем чипе не выйдет. Поэтому стоит подумать о STM32

Наверняка так и будет. Но гораздо позже. Пока учусь на Ардуино. 

Большое спасибо за разъяснения ! 

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

Программы arduino написанные расширением arduino работают как родные на stm32, только быстрее и с большей памятью и разрешением АЦП. Там для Вашей задачи analogRead и serialPrint выведут всё что Вы хотите без всяких прерываний и даже больше. Плата блюпил за 120 руб.  

black-support
Offline
Зарегистрирован: 22.04.2019

Классно ! Спасибо. 
Почитал немного про STM. Интересная штуковина. Только, как я понял, в ней нет EEPROM-а, придётся отдельно прикручивать. 
Теперь работа затормозится, буду думать переходить или нет. :) 

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

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