Чотаржу.
Главное стало понятно для чего АДС тут нужен.
С интерфейсом I2C?
У Вас что в школе по арифметике было?
Наверно осциллограф какой-то придумал.
Я в детстве, насмотревшись “Назад в будущее”, тоже “придумал” парящую доску, чертежи(как мне казалось) начертил даже. Но что-то не пошло дальше)))
Решил не плодить новую тему. Проблема такая. Требуется проводить измерения через равные промежутки времени (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 мкс.
И?
Вы поспорить пришли на форум или ответ на свой вопрос получить?
Надо будет попробовать.
На миллисекундах тоже самое можно сделать и на 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() - то интервалы будут довольно точными и никакие ошибки накапливается не будут.