На мой взгляд интервалы также окажутся неточными. Ведь неизвестно, когда основная программа дойдет до этой переменной.
Любознательный, уже нормально подготовленный и неконфликтный новичок - это подарок форуму! Шалом!
- Измерения в прерывании. Просто внутри прерывания другие прерывания запрещены. Если ты аккуратен и понимаешь, что может случиться на жизненном пути твоей программы, то просто разреши прерывания на входе в обработчик и все будет беседер! Если ты в коде больше нигде не работаешь с И2Ц, то ничего не должно поналезть страшного.
- при использовании миллис будет точность в пределах времени исполнения прохода loop(). Вот как напишешь, так и будет. loop() здорового человека укладывается в 1 мс, loop() курильщика - нет.
- тоже самое касается и выставления флага.
- запрос к АЦП в 440 мкс - долго. Но тут уж важно, как ты остальной код напишешь. И с какой точностью тебе нужны эти 40 мс (25 Гц - странное число, будет время - расскажи его историю).
- Твоя задача, с почти 100% вероятностью решается без изминения твоего кода, а просто разрешением прерываний в обработчике.
Так я же написал, что уже попробовал (вставил sei() на входе обработчика). Не помогло (не заработало).
Это очень странно. Значит еще в чем-то ошибка.
ADS.setDataRate(4); //скорость преобразования максимум 128SPS (DEFAULT)
// timeout == { 129, 65, 33, 17, 9, 5, 3, 2 }
// a few ms more than max conversion time.
uint8_t timeOut = (128 >> (_datarate >> 5)) + 1;
Но не само измерение. Да ещё с вычислениями.
Попробуйте так
Спойлер
/***************************************************************************************************
* ПОДКЛЮЧЕНИЕ БИБЛИОТЕК
****************************************************************************************************/
#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;
volatile unsigned int value_ = 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;
val_01 = ADS.toVoltage(value_ * 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)
{
value_ = ADS.readADC_Differential_0_3();
//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++;
}
P.S. Да , sei() ,если что , добавьте в начало обработчика
P.P.S. Если не поможет, в цикле loop() раскомментируйте эту строчку
//ADS.requestADC_Differential_0_3();
можно попробовать уменьшить время конверсии до 2 миллисекунд (в этой библиотеке)
у адафрута оно стандартно 8 миллисекунд
CONVERSION DELAY (in mS)
-----------------------------------------------------------------------*/
#define ADS1015_CONVERSIONDELAY (1)
#define ADS1115_CONVERSIONDELAY (8)
Родное сердце! Ты б в текст библиотеки посмотрел. В обработчике у человека вызов “реквест”. в нем вычисление маски и отправка по И2Ц трех байт. Более ничего. Не работает у него по каким-то иным причинам.
Ысчо раз, для радиолюбителей; в обработчике прерывания ОДИН вызов вот такой функции:
bool ADS1X15::_writeRegister(uint8_t address, uint8_t reg, uint16_t value)
{
_wire->beginTransmission(address);
_wire->write((uint8_t)reg);
_wire->write((uint8_t)(value >> 8));
_wire->write((uint8_t)(value & 0xFF));
int rv = _wire->endTransmission();
if (rv != 0)
{
_error = ADS1X15_ERROR_I2C;
return false;
}
return true;
}
Прочитай даташит на ADS1115. Максимальная скорость 1.16 мс. Если тебе надо 40 мс то в твоей программе было бы не плохо добавить ещё одно прерывание на ногу, соединённую с выводом ALERT/RDY. Логика - конфигурируешь АЦП в singlе моду, в прерывании таймера запускаешь АЦП, в прерывании ноги читаешь АЦП, пишешь в волатильную переменную результат и выставляешь флаг готово, в loop проверяешь флаг и если готово печатаешь результат.
Уважаемый [Дим-мычъ], подскажите, пожалуйста, а что означает знак подчеркивания в имени переменной value_?
Думаю, что у Вас здесь опечатка:
Я бы оставил:
val_01 = (ADS.toVoltage(ADS.getValue()))*1000000;
А почему это “не известно”? Разве основную программу не Вы сами пишете? Напишите так, чтобы как только выставилась переменная - программа “все бросала” и принималась читать АДС.
Попробовал вот так:
ISR(TIMER1_COMPA_vect)
{
// setup ADS1115
Wire.beginTransmission(0x48); // ADC
Wire.write(1);
Wire.write(0b10010011);
Wire.write(0b10000011);
Wire.endTransmission();
}
Не помогло.
Ничего не означает. Это другая переменная. Скопируйте и компилируйте код полностью.
Учтите, что я в P.S написал
аппаратный и2ц НЕ РАБОТАЕТ без прерываний! Ну сказал же уже один раз.
ничего. Просто такое имя у переменной. Можно было бы назвать например Mihail_14 - на функциональность это не влияет.
Михаил, такое впечатление, что программировать вы начали на прошлой неделе. Это так?
Вчера
Ну и лампочкой поморгай в прерывании, чтобы видеть, что ты туда все таки попадаешь… а то мало ли?
Уважаемый [Дим-мычъ], с Вашей переменной value_ всё заработало, когда добавил sei(). Без sei() не работает.
Только я всё же оставил:
val_01 = (ADS.toVoltage(ADS.getValue()))*1000000;
Со строкой:
val_01 = ADS.toVoltage(value_ * 1000000 );
на выходе белиберда.
Огромное спасибо!
Спасибо всем, кто принял участие в обсуждении!
Тему можно закрывать.
Закончу пожалуй, ардуино ради…
Спойлер
/***************************************************************************************************
ПОДКЛЮЧЕНИЕ БИБЛИОТЕК
****************************************************************************************************/
#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;
volatile unsigned int value_ = 0;
volatile bool ADCflg = false;
/***************************************************************************************************
ФУНКЦИЯ 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)
{
static unsigned long last_millis = 0;
if (ADS.isReady() && ADCflg)
{
// val_01 = (ADS.toVoltage(ADS.getValue()))*1000000;
val_01 = ADS.toVoltage(value_ ) * 1000000 ; //
timer0 = micros();
//ADS.requestADC_Differential_0_3();//не будет работать - раскомментировать эту строчку
timer1 = micros();
ADCflg = false;
if (millis() - last_millis >= 1000)
{
last_millis = millis();
Serial.print("COMP:\t");
Serial.print(val_01);
Serial.print(Index);
Serial.println((float)(timer1 - timer0));
}
}
//delay (1000);
}
ISR(TIMER1_COMPA_vect)
{
sei();
value_ = ADS.readADC_Differential_0_3();
ADCflg = true;
//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++;
}