Счетчик импульсов AnalogRead()

aleksyum
Offline
Зарегистрирован: 27.04.2014

Всем привет! Необходимо сделать подсчет импульсов сигнала у которого амплитуда равняется 1.5 вольта, то есть на ум приходит только функция analogRead(), как возможно реализовать подсчет ипмульсов с учетом того что  есть продолжительная  пауза без сигнала  после которой необходимо снова считать импульсы, как добиться того чтобы при ключении устройства происходило ожидание этой паузы и только после нее подсчет. Спасибо

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

aleksyum, если есть пауза, где нет сигнала, так стало быть и считать нечего )) Вот вам пример работы компаратора, сигнал подать на 7 пин, срабатывает при напряжении на входе уровнем выше 1,1 вольта.

volatile uint16_t count=0;
void setup(){
Serial.begin(9600);
pinMode(7,INPUT);
ACSR=(1<<ACBG)|(1<<ACIE)|(1<<ACIS1)|(1<<ACIS0); 
// включить компаратор, включить сравнении с ион, включить прерывание,
}

ISR (ANALOG_COMP_vect) { 
count++;
}

void loop() {
Serial.println(count);
  
}

 

aleksyum
Offline
Зарегистрирован: 27.04.2014

Спасибо за подсказку с компаратором! Пауза нужна для опредления, наало это цикла сигналов или нет, просто при включении устройства цикл сигналов может быть посередине и устройство будет считывать их не сначала а это нельзя допустить, я думал может сделать ожидание при инициализации? В setup()?

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

aleksyum, можно и в сетап. Вот например вариант, цикл в сетап крутится до тех пор, пока импульсов не будет более 5 секунд. После этого управление переходит к loop

volatile uint16_t count=0;
volatile uint32_t premillis=0;
volatile uint8_t flag=0;
void setup(){
Serial.begin(9600);
pinMode(7,INPUT);
ACSR=(1<<ACBG)|(1<<ACIE)|(1<<ACIS1)|(1<<ACIS0); 
// включить компаратор, включить сравнении с ион, включить прерывание
do { if ( bitRead (ACSR,ACO)!= flag){ flag = !flag; premillis=millis(); } }
while (millis()-premillis < 5000);
count=0;
}

ISR (ANALOG_COMP_vect) { 
count++;
}

void loop() {
Serial.println(count);

}

 

aleksyum
Offline
Зарегистрирован: 27.04.2014

Оу) А можно поподробнее как именно реализован этот тайм аут в сетапе?

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

aleksyum,  если команды do- while понятны, то остаётся понять их содержание . Если бит состояния выхода компаратора не равен значению флага, то инвертировать флаг, и сбросить счёт миллисекунд, после этого проверить не вышло ли время, и так по кругу. Если сигнала нет, и состояние выхода постоянно, то premillis не будет сбрасываться, и после 5 секунд вылетит из цикла.

aleksyum
Offline
Зарегистрирован: 27.04.2014

компаратор себя ведет очень странно при настроеных последних битах регистра компаратора, прерывание срабатывает при переходе с единицы на ноль а не наоборот как должно быть, в чем может быть причина?

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

aleksyum, в разных взглядах ваших и компаратора :) Для вас единица как я понимаю - это некий уровень на входе , выше  1,1 вольта. Для компаратора единица - это единица на его выходе. Теперь  размышляем логически. На плюс компаратора мы подали 1,1 вольта. На минус пусть будет 0 вольт. Значит компаратор выдаст единицу. Если напряжение выше 1,1 вольта -компаратор выдаст 0. так что всё правильно.  Битами ACIS вы можете поменять логику.

aleksyum
Offline
Зарегистрирован: 27.04.2014

Что то все таки я не понимаю) Я два бита логики работы менял, и все то же самое

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

aleksyum, вариант чудес исключаем. Значит вы как-то обманываете компаратор, посылаете ему быстро 2 импульса подряд. Дребезг?

aleksyum
Offline
Зарегистрирован: 27.04.2014

У меня теперь (1<<ACIS1)|(0<<ACIS0); но прерывание происходит при отсутсиви сигнала, а должно как я понимаю при переходе с ACO с нуля на единицу, я правильно понимаю

Pyotr
Offline
Зарегистрирован: 12.03.2014

RC фильтр на входе компаратора и/или отключать прерывания аналог. компаратора в обработчике (обнуляем бит ACIE) на время ~длительности импульса.

ACIS0 не надо устанавливать.

aleksyum
Offline
Зарегистрирован: 27.04.2014

То есть так, по другому думаю быть не может) 

ISR (ANALOG_COMP_vect) { 
count++;
ACSR=(0<<ACIE);
delay(500);
ACSR=(1<<ACIE);
}

 

aleksyum
Offline
Зарегистрирован: 27.04.2014

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

Pyotr
Offline
Зарегистрирован: 12.03.2014

Нет, не так. В обработчике только сбрасываете  ACSR &=~(0<<ACIE)  а в loop() устанавливаете через какое то время, используя millis().

aleksyum
Offline
Зарегистрирован: 27.04.2014

Я одного не понимаю что мне необходимо сделать чтобы прерывание обрабатывалось когда ЕСТЬ сигнал на входе компаратора?

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

aleksyum,компаратор работает с тем, что вы ему даёте.  Значит напряжение есть, разбирайтесь с своим сигналом.

aleksyum
Offline
Зарегистрирован: 27.04.2014

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

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

aleksyum, да, всё правильно. Вставьте в луп строчку Serial.println( ACSR&1<<ACO); и смотрите что на выходе компаратора в реальном времени

aleksyum
Offline
Зарегистрирован: 27.04.2014

Проверил, при отсувии напряжения ACO равняется единице.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

aleksyum, отлично так и должно быть. В чём проблема то?) Если что-то не так срабатывает -то разбираться с сигналом, дребезгом, помехами, и.т.п.

aleksyum
Offline
Зарегистрирован: 27.04.2014

Я уже запутался) А как мне сделать обработку прерывания по нулю тогда?)

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

aleksyum, мне казалось этот вопрос уже разобран окончательно?)  Прерывание может вызываться по трём событиям:

1 любое изменение на выходе компаратора (1 -> 0 или 0 ->1)      |(0<<ACIS1)|(0<<ACIS0)

2 выход компаратора изменился с 1 на 0 |(1<<ACIS1)|(0<<ACIS0)

3 выход компаратора изменился с 0 на 1 |(1<<ACIS1)|(1<<ACIS0)

если нужно прерывание по спаду входного сигнала на ноль, то выбираем пункт 3

Begemot
Offline
Зарегистрирован: 28.04.2016

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

Релизовываю проект интегратора со сбросом с частотным преобразователем. Имеется RC цепочка, имеется неизвестное постоянное напряжение. На интегратор из RC цепочки подается фиксированное напряжение 5 В с выхода 13, в это время на вход D2 подаются прямоугольные импульсы с генератора. Выходное напряжение интегратора подается на инвертирующий вход компаратора, когда оно достигнет нашего неизвестного напряжения (здесь для тестов подключено 3.3 В), программа завершит подсчет числа импульсов за которое интегратор зарядился до уровня неизвестного напряжения , дальше рассчитывается значение неизвестного напряжения (не реализовано пока, из-за проблем с основной частью). После этого интегратор разряжается через второй резистор и вывод D11. Выход с интегратора подключен так же к AIN2, чтобы отслеживать момент, когда он разрядится (это сделано как костыль для простоты).

То, что выводит программа в серийный порт:

1
10
1
4689
1
12

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

int analogPin  =    2;          
int chargePin    =  13;         
int dischargePin  = 11;        


volatile unsigned long count=0;
volatile boolean flag=0;
               
void setup(){
  pinMode(6,INPUT);
  pinMode(7,INPUT);
ACSR=(1<<ACIE)|(1<<ACIS1)|(1<<ACIS0);
attachInterrupt(0, rpm_fan, FALLING);

  pinMode(chargePin, OUTPUT);     
  digitalWrite(chargePin, LOW);  
  Serial.begin(9600);             
}

void loop(){
noInterrupts();
if (flag==0)
{
digitalWrite(chargePin, HIGH); //начало процесса зарядки конденсатора  

}
else

 { 

 Serial.println(flag);
 Serial.println(count);
 
  digitalWrite(chargePin, LOW); //начало разрядки конденсатора            
  pinMode(dischargePin, OUTPUT);            
  digitalWrite(dischargePin, LOW);          
  while(analogRead(analogPin) > 0)
  pinMode(dischargePin, INPUT);
 count=0;
 flag=0;
 }            
 interrupts();
}

ISR (ANALOG_COMP_vect) { 

flag=1;

}
void rpm_fan() 
{
count++;
}

 

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

Begemot, да кто ж его знает,тут нужно сидеть разбираться с реальным устройством, а не гадать на кофейной гуще. В качестве совета - в диагностических целях уберите пока внешние импульсы совсем, отключите прерывание INT. Считайте вместо этого например сколько микросов натикало, за период зарядки интегратора. Если глюков нет -значит вы косячно подключили внешний сигнал на D2

Begemot
Offline
Зарегистрирован: 28.04.2016

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

int analogPin  =    2;          
int chargePin    =  13;         
int dischargePin  = 11;        

volatile unsigned long time1=0;
volatile boolean flag=0;
           
void setup(){
 //pinMode(6,INPUT);
  pinMode(7,INPUT);
ACSR=(1<<ACBG)|(1<<ACIE)|(1<<ACIS1)|(0<<ACIS0);
  pinMode(chargePin, OUTPUT);     // установить вывод зарядки
  digitalWrite(chargePin, LOW);  
  Serial.begin(9600);             
}

void loop(){
noInterrupts();
if (flag==0)
{
digitalWrite(chargePin, HIGH);  // установить пин зарядки

}
if (flag==1)
 { 
 Serial.println(flag);
 Serial.println(time1);
 
  flag=0;
  
  //разрядка конденсатора
  digitalWrite(chargePin, LOW);             
  pinMode(dischargePin, OUTPUT);            
  digitalWrite(dischargePin, LOW);          
  while(analogRead(analogPin) > 0);
   pinMode(dischargePin, INPUT); 
 }    
       
 interrupts();
}

ISR (ANALOG_COMP_vect) { 
flag=1;
time1=micros();
}

Вот, что получается на выходе:

1
8
1
23504
1
24172
1
47940
1
49160
1
73092
1
74228
1
98180
1
98924

Т.е. компаратор все равно срабатывает дважды.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

Begemot, стало любопытно, проверил сам. Действительно,  прерывание срабатывает 2 раза,  при пересечии уровня 1,1вольта  в ту и в обратную сторону.  В общем при любых вариантах битов ACIS1  ACIS0 логика выхода в прерывания, такая же как при нулевых битах. Это явное противоречие даташиту, но это факт..  Кажется, что должно быть какое-то разумное объяснение, но у меня нет идей.

Противостоять этому глюку конечно нетрудно разными программными ухищрениями, например после срабатывания прерывания компаратора сразу отключать его ACSR=1<<ACD. А после того, как процесс разрядки закончится -снова включать ACSR=1<<ACBG | 1<<ACIE;

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

А вот за эту проверку - пасибки и от меня тоже. Все хотел запрячь компаратор на сигнал перехода через середину питания для своего BLDC драйвера .. учту.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

Так и не успокоился я вчера :)  Должно же быть какое-то объяснение этому глюку. Подумал, может в студии сделать тест компаратора, вдруг ардуина что-то намутила с регистрами.  Написал, -всё тоже самое. Выход в прерывание компаратора срабатывает вне зависимости от выставленных бит ACISx, причём пересекая границу устойчивости может сработать по нескольку раз, прямо как дребезг. Эффект особо заметен на пологих сигналах типа синуса и треугольника. И чем меньше частота тем чаще происходят ложные срабатывания. Вот картинки для наглядности. Схема подключений.

Бирюзовый -сигнал "треугольник" с генератора. Жёлтый -тестовый импульс при входе в прерывание с 8-й ноги.

При растягивании импульса видно, что входит в прерывания по многу раз.

Если поиграться порогом срабатывания и частотой входящего сигнала, и паузой в прерывании -то можно добится того, что срабатывать будет как должен -один раз, и только на восходящей фазе сигнала. Но это очевидно за счёт того, что времени на ложные срабатывания просто не остаётся.  Пробовал давать образцовое опорное от батарейки, но всё равно эффект проявляется, хоть и чуть реже. В общем моё объяснение  таково - аппаратно компаратор в меге не может поддерживать  программную возможность выхода в прерывание на falling или rising на абы каком сигнале из-за эффекта "дребезга"  при переходе сигнала  через опорное напряжние, который в свою очередь видимо вызван высокими пульсациями в питании компаратора.  А так falling или rising конечно чётко срабатывают, но из-за того, что это происходит так-же и на фронтах дребезга применение этой фишки оказывается затруднено.

Тестовый скетч для студии:

#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

int main(void)
{
	ADCSRA=0;
	ADCSRB=0;
	DDRB|=1<<0; //выход тестового импульса
	ACSR=0<<ACBG | 1<<ACIE | 1<<ACIS1 | 0<<ACIS0;
	DIDR1=3;
	sei();
	while (1) {
		
	}
}

ISR (ANALOG_COMP_vect) {
		PORTB|=1<<0;
		_delay_us(100);
		PORTB&=~(1<<0);

}

 

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Спасибо за дотошность. То есть получается что компаратор можно использовать только "по фронту", и кроме этого надо блокировать ложные срабатывания в обработчике самого прерывания программно. Так?

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

Arhat109-2, в принципе можно использовать любую из 3х вариантов логику выхода в прерывание, но при условии что будет программная защита от дребезга.  В случае Бегемота например достаточно отключить компаратор при первом входе в прерывание, и включить его уже в начале следующего цикла зарядки.

Хотя пока не разобрал стенд, счас проверю вариант с "фаллинг"

Да, с "фаллинг" сложнее, т.к. затруднительно будет заблокировать алгоритм когда на входе идёт "райсинг" .

ps: фаллинг имелся ввиду у сигнала на входе, у компаратора это будет наоборот  райсинг.

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

Я на эти грабли наступил год назад. Понял что все проблемы из за дребезга - очень чувствительный компаратор! - на входе аппаратный  фильтр помогает но не 100%. В конце концов воткнул маленькую программную задержку и в конце прерывания чистил дополнительно флаг  прерывания. Работает.   

Begemot
Offline
Зарегистрирован: 28.04.2016

Спасибо вам, что так взялись за эту проблему! Я по всякому пробовал с этим входным сигналом играться, подавал на вход от генератора треугольный сигнал, все равно по разному срабатывало. Выход только один, либо программно через введение задержки времени бороться, либо схемотехнически с использованием тригера Шмидта, для создания петли гистерезиса. Компаратор оказался очень чувствительным и требовательным к сожалению.

SLKH
Offline
Зарегистрирован: 17.08.2015

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