Проблема с компаратором

svm
Offline
Зарегистрирован: 06.11.2016

Решил добавить к универсальному пробнику режим измерения конденсаторов по http://digitrode.ru/computing-devices/mcu_cpu/816-izmeritel-emkosti-na-arduino-svoimi-rukami.html первые два скетча работают нормально, третий неустойчиво. От него отказался. Первый скетч в конструкцию вписался хорошо, в схеме ничего переделывать не нужно. Со вторым проблема. Необходим отдельный вход для измерения. Попытался переназначить вход компаратора на один из аналоговых. Эффект нулевой. Как измерял с 6 пина так и осталось. Переключение на аналоговый вход никак не влияет. Вроде почитал теорию, но что-то недопонимаю. Вот сам скетч:

/*  Для измерения используем компаратор ардуино
 *  На вход D7 подаем напряжение = 1,58(62%) от питания
 *  С делителя напряжения питания из резисторов 1,8 и 3,1 КОм
 *  Конденсатор подключаем между D6 и землей
 *  Заряжаем конденсатор через резистор 10 КОм c 13 пина пока не сработает компаратор
 *  Дальше считаем исходя из времени заряда и резистора
 *  Емкость в RC-цепочке связана с постоянной времени по уравнению: Tc = R* C 
 *  Соответственно, емкость определится  как: C = Tc/R.
 */


const byte pulsePin = 13;                 //Пин подачи напряжения заряда 
const unsigned long resistance = 9100;   //Сопротивление зарядной цепи (9.1 KOm)
volatile boolean triggered;
volatile boolean active;
volatile unsigned long startTime;
volatile unsigned long duration;

// Обработчик прерывания аналогового компаратора

ISR (ANALOG_COMP_vect)
  {
  unsigned long now = micros ();
  if (active)
    {
    duration = now - startTime;
    triggered = true;
    digitalWrite (pulsePin, LOW); // Снимаем напряжение заряда
    }
  }

void setup ()
  {
  pinMode(pulsePin, OUTPUT);
  digitalWrite(pulsePin, LOW);
  Serial.begin(9600);
  Serial.println("Started.");
  //                       Настраиваем компаратор
  //ADCSRB = 0;
  ADCSRB |=(1<<ACME);                 // enable multiplexer    
  ADMUX = 3;                         
  ACSR =  _BV (ACI)                   //флаг прерывания.  Устанавливается по событию,сбрасывается после ухода на обработчик либо программно, записью в него 1.
        | _BV (ACIE)                  //бит разрешения прерывания от компаратора.
        | _BV (ACIS0) | _BV (ACIS1);  
   }  
/*
Биты ACIS1:ACIS0 определяет условие возникновения прерывания от компаратора:
00 — любое изменение на входе.
01 — зарезервировано для следующих поколений
10 — переход с 1 на 0
11 — переход с 0 на 1
*/

void loop ()
  {
    if (!active)
    {
    active = true;
    triggered = false;
    digitalWrite (pulsePin, HIGH);   // Включаем напряжение заряда
    startTime = micros ();  
    }

  if (active && triggered)
    {
    active = false;
    Serial.print ("Capacitance = ");
    Serial.print (duration * 1000 / resistance);
    Serial.println (" nF");
    triggered = false;
    delay (1000);
 
    }
}

 

svm
Offline
Зарегистрирован: 06.11.2016

Ощущение, что строк 40 и 41 не существует.

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

svm пишет:
Попытался переназначить вход компаратора на один из аналоговых. Эффект нулевой. Как измерял с 6 пина так и осталось.

А разве вход компаратора можно на аналоговый пин переназначить? ЕМНИП он там намертво зашит AIN0 и AIN1. Хотя ... Вы же не сказали, что за контроллер, вангую, что мы о разных вещах говорим.

svm
Offline
Зарегистрирован: 06.11.2016

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

А разве вход компаратора можно на аналоговый пин переназначить? ЕМНИП он там намертво зашит AIN0 и AIN1. Хотя ... Вы же не сказали, что за контроллер, вангую, что мы о разных вещах говорим.

Обычный 168, но когда начал разбираться, в десятке публикаций прочитал, что достаточно в регистр ADCSRB  установить в единицу бит ACME и мультиплексор с АЦП переключается на инвертирующий вход компаратора. ADMUX выбирает на какой. Правда есть сомнение, что кристалл не совсем живой, так как я ему спалил А6. Но остальное все работает. Так-что ХЗ. Толь лыжи не едут, толь я .......  Напрямую с регистрами работаю, только если сильно приспичит. Поэтому грешу на больше на свои знания.

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

svm пишет:

достаточно в регистр ADCSRB  установить в единицу бит ACME и мультиплексор с АЦП переключается на инвертирующий вход компаратора. ADMUX выбирает на какой.

Всё верно. А неинвертирующий вход так и остаётся на PD6. Но его можно перекинуть на опорное  битом ACBG.

 

svm
Offline
Зарегистрирован: 06.11.2016

dimax пишет:

svm пишет:

достаточно в регистр ADCSRB  установить в единицу бит ACME и мультиплексор с АЦП переключается на инвертирующий вход компаратора. ADMUX выбирает на какой.

Всё верно. А неинвертирующий вход так и остаётся на PD6. Но его можно перекинуть на AREF  битом ACBG.

 

Так значит мой косяк, у меня опорное подается на PD7 а измеряемая величина на PD6. Если не сложно гляньте скетч. Правильно-ли я пытаюсь переключить вход? И если поменять местами PD6 и PD7 для сохранения логики работы  достаточно изменить условие срабатывания компаратора? Это влет, а вообще подумать надо. Но уж очень хочется добавить в пробник измерение конденсаторов не ставя дополнительных разъемов и не меняя схему. В него уже не влезешь.  В более крупном варианте,  http://arduino.ru/forum/proekty/universalnyi-arduino-probnik?page=5#comment-477987 лишний разъем не критичен. А сейчас пошли эти подлые CMD конденсаторы, по размерам и внешнему виду которых емкость не определишь.

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

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

svm
Offline
Зарегистрирован: 06.11.2016

dimax пишет:

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

Ок! Спасибо! Действительно, достаточно в 39 строке  отключить АЦП, вставив в не следующее:

 ADCSRA &= ~ (1 << ADEN);   // Отключаем АЦП 

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

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

/*  Для измерения используем компаратор ардуино
 *  На вход D6 подаем напряжение = 62% от питания
 *  С делителя напряжения питания из резисторов 1,8 и 3,1 КОм
 *  Конденсатор подключаем между A5 и землей
 *  Заряжаем конденсатор через резистор 10 КОм c 3 пина пока не сработает компаратор
 *  Дальше считаем исходя из времени заряда и резистора
 *  Емкость в RC-цепочке связана с постоянной времени по уравнению: Tc = R* C 
 *  Соответственно, емкость определится  как: C = Tc/R.
 */


const byte pulsePin = 3;                         //Пин подачи напряжения заряда
const byte LedPin = 4;                           //Отладочный светодиод контроля подачи напряжения заряда
const unsigned long resistance = 10000;          //Сопротивление зарядной цепи (10 KOm)

volatile boolean triggered;
volatile boolean active;
volatile unsigned long startTime;
volatile unsigned long duration;
                                    // Обработчик прерывания аналогового компаратора
ISR (ANALOG_COMP_vect)
  {
  unsigned long now = micros ();    // Фиксируем время срабатывания
  if (active)                       // Если был икл измерения
    {
    duration = now - startTime;     // Считаем длительность заряда
    triggered = true;               // Фиксируем конец измерения
    digitalWrite (pulsePin, LOW);   // Включаем разряд конденсатора
    }
  }

void setup ()
  {
  pinMode(LedPin, OUTPUT);
  pinMode(pulsePin, OUTPUT);
  digitalWrite (LedPin, LOW);
  digitalWrite(pulsePin, LOW);        // Контрольный разряд конденсатора
  
  Serial.begin(9600);
  Serial.println("Started.");
  
                            // Настройка АЦП и мультиплексора
  
  ADCSRA &= ~ (1 << ADEN);            // Отключаем АЦП 
  ADCSRB |=(1<<ACME);                 // Подключаем мультиплексор
  ADMUX = 5;                          // ADC5 к инверсному входу компаратора

                             // Настройка компаратора
                             
  ACSR =  _BV (ACI)                   // Флаг прерывания
        | _BV (ACIE)                  // Разрешение прерывания
        & ~_BV (ACIS0)                // Прерывание при переходе с 1 на 0
        | _BV (ACIS1);                // --------------------------------              
/*
Биты ACIS1:ACIS0  условие  прерывания  компаратора:
00 — любое изменение на входе.
01 — зарезервировано для следующих поколений
10 — переход с 1 на 0
11 — переход с 0 на 1
*/
   
   }  

void loop ()
  {
    if (!active)                         //Ели не идет измерение - старт нового измерения
    {
    active = true;
    triggered = false;
    digitalWrite (pulsePin, HIGH);       // Включаем заряд конденсатора
    digitalWrite (LedPin, HIGH);
    startTime = micros ();  
    }

  if (active && triggered)             //Если прерывание зафиксировало время заряда, отображаем результат
    {
    active = false;
    triggered = false;
    digitalWrite (pulsePin, LOW);
    digitalWrite (LedPin, LOW);
    Serial.print ("Capacitance = ");
    Serial.print (duration * 1000 / resistance);
    Serial.println (" nF");
    delay (200);
   }
}

 

 

/*  Для измерения используем компаратор ардуино
 *  На вход D6 подаем напряжение = 62% от питания
 *  С делителя напряжения питания из резисторов 1,8 и 3,1 КОм
 *  Конденсатор подключаем между A5 и землей
 *  Заряжаем конденсатор через резистор 10 КОм c 3 пина пока не сработает компаратор
 *  Дальше считаем исходя из времени заряда и резистора
 *  Емкость в RC-цепочке связана с постоянной времени по уравнению: Tc = R* C 
 *  Соответственно, емкость определится  как: C = Tc/R.
 */

const byte LedPin = 4;
const byte pulsePin = 3;                           //Пин подачи напряжения заряда            
const unsigned long resistance =10000;             //Сопротивление зарядной цепи (10 KOm)

volatile boolean triggered;
volatile boolean active;
volatile unsigned long startTime;
volatile unsigned long duration;
volatile unsigned long timerCounts;
volatile unsigned long overflowCount;

ISR (TIMER1_OVF_vect)
{  ++overflowCount;               // считаем количество переполнений счетчика 1 
}  // end of TIMER1_OVF_vect
                                 // Обработчик прерывания аналогового компаратора                        
ISR (ANALOG_COMP_vect)
  {                              // фиксируем актуальное значение счетчика, прежде чем он изменится
  unsigned int timer1CounterValue;
  timer1CounterValue = TCNT1;  
  unsigned long overflowCopy = overflowCount;
  
  if (active)
    {                             // Если просто пропустил переполнение
    if ((TIFR1 & bit (TOV1)) && timer1CounterValue < 256)
    overflowCopy++;               // Считаем общее количество
    timerCounts = (overflowCopy << 16) + timer1CounterValue;   // Каждое переполнение составляет 65536 больше

    triggered = true;
    digitalWrite (pulsePin, LOW);  // Включаем разряд конденсатора
    digitalWrite (LedPin, LOW);
    }
  }  // end of ANALOG_COMP_vect


void setup ()
  {
  pinMode(pulsePin, OUTPUT);
  pinMode(LedPin, OUTPUT);
  digitalWrite (LedPin, LOW);
  digitalWrite (pulsePin, LOW);            //Включаем разряд конденсатора
  Serial.begin(9600);
  Serial.println("Started.");
  
  ADCSRA &= ~ (1 << ADEN);   // Отключаем АЦП 
  ADCSRB |=(1<<ACME);        // Подключаем мультиплексор
  ADMUX = 5;                 // ADC5 к инверсному входу компаратора
  //DIDR1 |= _BV(AIN1D) | _BV(AIN0D); 
  ACSR =  _BV (ACI)
        | _BV (ACIE)    
        & ~_BV (ACIS0) 
        | _BV (ACIS1); 
  } 




void loop ()
{
  if (!active) startTiming ();               //Ели не идет измерение - старт нового измерения
  if (active && triggered) finishTiming ();  //Если прерывание зафиксировало время заряда, отображаем результат
}

//****************************************************************************************************************

void startTiming ()
  {
  active = true;
  triggered = false;

  // prepare timer
  overflowCount = 0;              // no overflows yet
  // reset Timer 1
  TCCR1A = 0;             
  TCCR1B = 0;   
  TCNT1 = 0;                      // Обнуляем счетчик
  TIFR1 = bit (TOV1);             // Очистить бит переполнения
                                  // Таймер 1- подсчет тактовых импульсов
  TIMSK1 = bit (TOIE1);           // прерывание по переполнению таймера 1 
                                  // смирись с этим
  digitalWrite (pulsePin, HIGH);  // Включаем заряд конденсатора
  digitalWrite (LedPin, HIGH);                                // Запуск таймера 1, Без прескалера
  TCCR1B =  bit (CS10);           // end of startTiming
} 
  
 //************************************************************************************** 
  void finishTiming ()
  {
  active = false;
  Serial.print ("Capacitance = ");
  float capacitance = (float) timerCounts * 1000.0 / 16.0 / resistance;
  Serial.print (capacitance);
  Serial.println (" nF");
  triggered = false;
  delay (500);
  }  // end of finishTiming