Измерения на ADS 1115a

Чотаржу.

5 лайков

Главное стало понятно для чего АДС тут нужен.

2 лайка

С интерфейсом I2C?

У Вас что в школе по арифметике было?

1 лайк

Наверно осциллограф какой-то придумал.
Я в детстве, насмотревшись “Назад в будущее”, тоже “придумал” парящую доску, чертежи(как мне казалось) начертил даже. Но что-то не пошло дальше)))

Решил не плодить новую тему. Проблема такая. Требуется проводить измерения через равные промежутки времени (40 мс). Использую библиотеку [ RobTillaart](GitHub - RobTillaart/ADS1X15: Arduino library for ADS1015 = I2C 12 bit ADC and ADS1115 = I2C 16 bit ADC).
Если просто однократно запускать и опрашивать АЦП, то всё работает.

/***************************************************************************************************
*  ПОДКЛЮЧЕНИЕ БИБЛИОТЕК
****************************************************************************************************/ 
#include <ADS1X15.h>    // Подключаем библиотеку by Rob Tillaart

/***************************************************************************************************
* МАКРООПРЕДЕЛЕНИЯ
****************************************************************************************************/
#define array_size 2   // Размер массива для скользящего среднего
#define false 0   
#define true 1   
/***************************************************************************************************
* АДРЕС АЦП ADS1115
****************************************************************************************************/
ADS1115 ADS(0x48); // Вывод ADDR ADS1115 подключен к GND
// ADS1115 ADS(0x49); // Вывод ADDR ADS1115 подключен к VDD
// ADS1115 ADS(0x4A); // Вывод ADDR ADS1115 подключен к SDA  
// ADS1115 ADS(0x4B); // Вывод ADDR ADS1115 подключен к SCL 

/***************************************************************************************************
* ОБЪЯВЛЕНИЕ ГЛОБАЛЬНЫХ ПЕРЕМЕННЫХ
****************************************************************************************************/  
float voltage_ADS [array_size];     // Объявляем массив напряжений для скользящего среднего
float voltage_average;              // Усредненное значение напряжения в мкВ
int code[array_size];               // Объявляем массив результатов преобразования АЦП (коды)
bool array_is_full = false;         // флаг заполнения массива
                                    // array_is_full = false - массив не заполнен
                                    // array_is_full = true - массив заполнен
volatile bool reading_is_allowed = false;    // флаг разрешения чтения результата преобразования
                                    // reading_is_allowed = false - чтение запрещено
                                    // reading_is_allowed = true - чтение разрешено 
unsigned char Index = 0;            // Индекс элементов массива. Первоначальное значение = 0              
unsigned long timer0 = millis();
unsigned long timer1;

float val_01 = 0;

/***************************************************************************************************
* ФУНКЦИЯ SETUP
****************************************************************************************************/
void setup(void)
{
  /******************************************************************************************/    
  /* Инициируем последовательное соединение Ардуино с ПК 
  и задаём скорость передачи данных в бит/c (бод) */  
  Serial.begin(115200);
  Serial.print("ADS1X15_LIB_VERSION: ");
  Serial.println(ADS1X15_LIB_VERSION);
  delay(100);

  Wire.begin();
  // Wire.setClock(100000);
  
  /***************************************************************************************************
  * НАСТРОЙКА АЦП ADS1115
  ****************************************************************************************************/ 
  /* Настраиваем параметры АЦП ADS1115 */
    
  /* Инициализируем микросхему ADS1115 и задаём её параметры конфигурации по умолчанию */
  ADS.begin();

  /* Задаём к-т усиления */ 
  //ADS.setGain(16);  //к-т усиления 16, максимальное напряжение ±0.256V, 1 бит = 0.0078125mV
  // ADS.setGain(8);    //к-т усиления 8, максимальное напряжение ±0.512V, 1 бит = 0.015625mV 
  // ADS.setGain(4);    //к-т усиления 4, максимальное напряжение ±1.024V, 1 бит = 0.03125mV
  // ADS.setGain(2);    //к-т усиления 2, максимальное напряжение ±2.048V, 1 бит = 0.0625mV   
  ADS.setGain(1);    //к-т усиления 1, максимальное напряжение ±4.096V, 1 бит = 0.125mV
  // ADS.setGain(0);    //к-т усиления 2/3, максимальное напряжение ±6.144V, 1 бит = 0.1875mV (DEFAULT) 

  /* Задаём скорость преобразования */
  // ADS.setDataRate(7);  //скорость преобразования максимум 860SPS (Самая быстрая) 
  // ADS.setDataRate(6);  //скорость преобразования максимум 475SPS    
  // ADS.setDataRate(5);  //скорость преобразования максимум 250SPS  
  ADS.setDataRate(4);  //скорость преобразования максимум 128SPS (DEFAULT)    
  // ADS.setDataRate(3);  //скорость преобразования максимум 64SPS  
  // ADS.setDataRate(2);  //скорость преобразования максимум 32SPS    
  // ADS.setDataRate(1);  //скорость преобразования максимум 16SPS  
  // ADS.setDataRate(0);  //скорость преобразования максимум 8SPS   (Самая медленная)
    
  /* Задаём режим преобразования */
  // ADS.setMode(0);  //непрерывный режим
  ADS.setMode(1);  //однократный режим
  
  ADS.requestADC_Differential_0_3();

  /***************************************************************************************************
  * НАСТРОЙКА ТАЙМЕРА
  ****************************************************************************************************/  
  /* Настраиваем Таймер1 на прерывание по совпадению A.    
  Таймер1 будет вызывать прерывание каждые 40 мс (частота 25 Гц).
  Он-лайн калькулятор: http://rcl-radio.ru/?p=111487&ysclid=m0oc5vjb9o687744588 */
   
  cli();
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;
// (16000000/((624+1)x1024))=25 Hz
  OCR1A = 624;
  TCCR1B |= (1 << WGM12);
// Prescaler 1024
  TCCR1B |= (1 << CS12) | (1 << CS10);
  TIMSK1 |= (1 << OCIE1A);
  sei();
  
}

/***************************************************************************************************
* ФУНКЦИЯ LOOP
****************************************************************************************************/
void loop(void)
{
  if (ADS.isReady())
  {
    val_01 = (ADS.toVoltage(ADS.getValue()))*1000000;
    timer0 = micros();
    ADS.requestADC_Differential_0_3();
    timer1 = micros();
    Serial.print("COMP:\t");
    Serial.print(val_01);
	Serial.print(Index);
    Serial.println((float)(timer1-timer0));    
  }
  //delay (1000);
}


ISR(TIMER1_COMPA_vect)
{
  //timer1 = millis();                   // останавливаем  отсчет времени
  //Serial.println((float)(timer1));
  //ADS.requestADC_Differential_0_1();     // запуск преобразования для дифференциального чтения между входами 0 и 1 АЦП
  //ADS.requestADC_Differential_0_3();     // запуск преобразования для дифференциального чтения между входами 0 и 3 АЦП
  //ADS.requestADC_Differential_1_3();     // запуск преобразования для дифференциального чтения между входами 1 и 3 АЦП
  //ADS.requestADC_Differential_2_3();     // запуск преобразования для дифференциального чтения между входами 2 и 3 АЦП
  //timer0 = timer1;
  //reading_is_allowed = true;            // выставляем флаг "чтение результата преобразования разрешено"
  //TCNT1 = 0;          // Обнуляем счетный регистр TCNT1 таймера/счетчика T1
  Index++;
 }

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

/***************************************************************************************************
*  ПОДКЛЮЧЕНИЕ БИБЛИОТЕК
****************************************************************************************************/ 
#include <ADS1X15.h>    // Подключаем библиотеку by Rob Tillaart

/***************************************************************************************************
* МАКРООПРЕДЕЛЕНИЯ
****************************************************************************************************/
#define array_size 2   // Размер массива для скользящего среднего
#define false 0   
#define true 1   
/***************************************************************************************************
* АДРЕС АЦП ADS1115
****************************************************************************************************/
ADS1115 ADS(0x48); // Вывод ADDR ADS1115 подключен к GND
// ADS1115 ADS(0x49); // Вывод ADDR ADS1115 подключен к VDD
// ADS1115 ADS(0x4A); // Вывод ADDR ADS1115 подключен к SDA  
// ADS1115 ADS(0x4B); // Вывод ADDR ADS1115 подключен к SCL 

/***************************************************************************************************
* ОБЪЯВЛЕНИЕ ГЛОБАЛЬНЫХ ПЕРЕМЕННЫХ
****************************************************************************************************/  
float voltage_ADS [array_size];     // Объявляем массив напряжений для скользящего среднего
float voltage_average;              // Усредненное значение напряжения в мкВ
int code[array_size];               // Объявляем массив результатов преобразования АЦП (коды)
bool array_is_full = false;         // флаг заполнения массива
                                    // array_is_full = false - массив не заполнен
                                    // array_is_full = true - массив заполнен
volatile bool reading_is_allowed = false;    // флаг разрешения чтения результата преобразования
                                    // reading_is_allowed = false - чтение запрещено
                                    // reading_is_allowed = true - чтение разрешено 
unsigned char Index = 0;            // Индекс элементов массива. Первоначальное значение = 0              
unsigned long timer0 = millis();
unsigned long timer1;

float val_01 = 0;

/***************************************************************************************************
* ФУНКЦИЯ SETUP
****************************************************************************************************/
void setup(void)
{
  /******************************************************************************************/    
  /* Инициируем последовательное соединение Ардуино с ПК 
  и задаём скорость передачи данных в бит/c (бод) */  
  Serial.begin(115200);
  Serial.print("ADS1X15_LIB_VERSION: ");
  Serial.println(ADS1X15_LIB_VERSION);
  delay(100);

  Wire.begin();
  // Wire.setClock(100000);
  
  /***************************************************************************************************
  * НАСТРОЙКА АЦП ADS1115
  ****************************************************************************************************/ 
  /* Настраиваем параметры АЦП ADS1115 */
    
  /* Инициализируем микросхему ADS1115 и задаём её параметры конфигурации по умолчанию */
  ADS.begin();

  /* Задаём к-т усиления */ 
  //ADS.setGain(16);  //к-т усиления 16, максимальное напряжение ±0.256V, 1 бит = 0.0078125mV
  // ADS.setGain(8);    //к-т усиления 8, максимальное напряжение ±0.512V, 1 бит = 0.015625mV 
  // ADS.setGain(4);    //к-т усиления 4, максимальное напряжение ±1.024V, 1 бит = 0.03125mV
  // ADS.setGain(2);    //к-т усиления 2, максимальное напряжение ±2.048V, 1 бит = 0.0625mV   
  ADS.setGain(1);    //к-т усиления 1, максимальное напряжение ±4.096V, 1 бит = 0.125mV
  // ADS.setGain(0);    //к-т усиления 2/3, максимальное напряжение ±6.144V, 1 бит = 0.1875mV (DEFAULT) 

  /* Задаём скорость преобразования */
  // ADS.setDataRate(7);  //скорость преобразования максимум 860SPS (Самая быстрая) 
  // ADS.setDataRate(6);  //скорость преобразования максимум 475SPS    
  // ADS.setDataRate(5);  //скорость преобразования максимум 250SPS  
  ADS.setDataRate(4);  //скорость преобразования максимум 128SPS (DEFAULT)    
  // ADS.setDataRate(3);  //скорость преобразования максимум 64SPS  
  // ADS.setDataRate(2);  //скорость преобразования максимум 32SPS    
  // ADS.setDataRate(1);  //скорость преобразования максимум 16SPS  
  // ADS.setDataRate(0);  //скорость преобразования максимум 8SPS   (Самая медленная)
    
  /* Задаём режим преобразования */
  // ADS.setMode(0);  //непрерывный режим
  ADS.setMode(1);  //однократный режим
  
  //ADS.requestADC_Differential_0_3();

  /***************************************************************************************************
  * НАСТРОЙКА ТАЙМЕРА
  ****************************************************************************************************/  
  /* Настраиваем Таймер1 на прерывание по совпадению A.    
  Таймер1 будет вызывать прерывание каждые 40 мс (частота 25 Гц).
  Он-лайн калькулятор: http://rcl-radio.ru/?p=111487&ysclid=m0oc5vjb9o687744588 */
   
  cli();
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;
// (16000000/((624+1)x1024))=25 Hz
  OCR1A = 624;
  TCCR1B |= (1 << WGM12);
// Prescaler 1024
  TCCR1B |= (1 << CS12) | (1 << CS10);
  TIMSK1 |= (1 << OCIE1A);
  sei();
  
}

/***************************************************************************************************
* ФУНКЦИЯ LOOP
****************************************************************************************************/
void loop(void)
{
  if (ADS.isReady())
  {
    val_01 = (ADS.toVoltage(ADS.getValue()))*1000000;
    timer0 = micros();
    //ADS.requestADC_Differential_0_3();
    timer1 = micros();
    Serial.print("COMP:\t");
    Serial.print(val_01);
	Serial.print(Index);
    Serial.println((float)(timer1-timer0));    
  }
  //delay (1000);
}


ISR(TIMER1_COMPA_vect)
{
  //timer1 = millis();                   // останавливаем  отсчет времени
  //Serial.println((float)(timer1));
  //ADS.requestADC_Differential_0_1();     // запуск преобразования для дифференциального чтения между входами 0 и 1 АЦП
  ADS.requestADC_Differential_0_3();     // запуск преобразования для дифференциального чтения между входами 0 и 3 АЦП
  //ADS.requestADC_Differential_1_3();     // запуск преобразования для дифференциального чтения между входами 1 и 3 АЦП
  //ADS.requestADC_Differential_2_3();     // запуск преобразования для дифференциального чтения между входами 2 и 3 АЦП
  //timer0 = timer1;
  //reading_is_allowed = true;            // выставляем флаг "чтение результата преобразования разрешено"
  //TCNT1 = 0;          // Обнуляем счетный регистр TCNT1 таймера/счетчика T1
  Index++;
 }

Подскажите, пожалуйста, в чём может быть дело.

Изменяй в обработчике состояние переменной, которая в основном цикле запустит преобразование.

Использовать в прерываниях такие емкие по времени операций - нельзя!

Кстати, если в прерывании наступило новое событие (через 40мс?) а данная переменная не сброшена - ты поймёшь что не хватает времени.

Функция запуска измерения выполняется 440 мкс.

И?
Вы поспорить пришли на форум или ответ на свой вопрос получить?

1 лайк

Надо будет попробовать.

На миллисекундах тоже самое можно сделать и на millis().
За основу можешь взять пример «блинк без делей».

Я не спорю. Я просто не знаю 440 мкс для обработчика прерывания это много или мало.

Для прерываний - Чем меньше - тем лучше!

Спасибо! Попробую.

у него функция чтения ацп - блокирующая

//////////////////////////////////////////////////////
//
//  PROTECTED
//
int16_t ADS1X15::_readADC(uint16_t readmode)
{
  _requestADC(readmode);
  if (_mode == ADS1X15_MODE_SINGLE)
  {
    uint32_t start = millis();
    //  timeout == { 129, 65, 33, 17, 9, 5, 3, 2 }
    //  a few ms more than max conversion time.
    uint8_t timeOut = (128 >> (_datarate >> 5)) + 1;
    while (isBusy())
    {
      yield();   //  wait for conversion; yield for ESP.
      if ( (millis() - start) > timeOut)
      {
        return ADS1X15_ERROR_TIMEOUT;
      }
    }
  }
  else
  {
    //  needed in continuous mode too, otherwise one get old value.
    delay(_conversionDelay);
  }
  return getValue();
}

И? Таким образом ты что предлагаешь?

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

Я не смотрел как работает ADS.requestADC_Differential_0_3();
Возможно для корректной работы требуются разрешение прерываний.
Для проверки можно добавить sei() пред вызовом ADS.requestADC_Differential_0_3();
(Разрешать прерывания в процедуре обработки прерывания не всегда безопасно, но для проверки можно).

Спасибо за совет. Проверил. Не работает.

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

Если всю остальную программу тоже написать на миллис, без использования delay() и длинных циклов while() - то интервалы будут довольно точными и никакие ошибки накапливается не будут.