Неадекватные показания на аналоговом пине (мега328).
- Войдите на сайт для отправки комментариев
Всем привет.
Использую Ардуино НАНО входы ADC3 и ADC6.
На ADC3 у меня висят три кнопки.
Проблема в считывании показаний с ADC3. Иногда когда нажимаешь какую-то кнопку она может считаться как соседняя. Подскажите почему так может быть?
В эти моменты показания с АЦП начинают прыгать. Например несколько раз нажал и считалось как положено 512, а в какой-то момент жмешь и считывается 570 или больше, а потом снова приходит в норму.
Вот код. На самом деле он больше но я привел только необходимые для понимания его части.
#define BUTTON_UP 690 #define BUTTON_DOWN 180 #define BUTTON_MENU 512 #define DELTA 20 #define BUT_UP_LOW BUTTON_UP - DELTA #define BUT_UP_HIGH BUTTON_UP + DELTA #define BUT_DOWN_LOW BUTTON_DOWN - DELTA #define BUT_DOWN_HIGH BUTTON_DOWN + DELTA #define BUT_MENU_LOW BUTTON_MENU - DELTA #define BUT_MENU_HIGH BUTTON_MENU + DELTA #define LONG_U 1 #define SHORT_U 4 #define LONG_M 2 #define SHORT_M 5 #define LONG_D 3 #define SHORT_D 6 #define PRESS_U 7
uint8_t analog_ref = DEFAULT;
volatile uint16_t keyboardSense = 0;
volatile uint16_t lightSense = 0;
volatile bool getADC = false;
volatile bool selectAdcPin = false;
void setup() {
//Включаем АЦП
//опорное напряжение - Vcc
ADMUX &= ~(1 << REFS1); ADMUX |= (1 << REFS0);
//Запоминаем состояние регистра - оно будет использоваться при смене пина входящего сигнала
analog_ref = ADMUX;
//Выбираем пин AC3
ADMUX |= KEYB_PIN;
//предделитель преобразователя 64 для ATMEGA328
ADCSRA |= (1 << ADPS2) | (1 << ADPS1); ADCSRA &= ~(1 << ADPS0);
//Включаем автоматическое преобразование
ADCSRA |= (1 << ADATE);
//Разрешаем прерывания по завершении преобразования
ADCSRA |= (1 << ADIE);
//Включаем АЦП
ADCSRA |= (1 << ADEN);
//Запускаем преобразование
ADCSRA |= (1 << ADSC);
}
void loop() {
//обрабатываем кнопку
buttonState = getButtonState(keyboardSense, 500, 1000);
}
byte getButtonState (uint16_t inputData, uint16_t longDelay, uint16_t pressedDelay) {
static uint32_t timeStamp, pressedMillis;
static byte buttonStatus;
static bool prevState = true, pressedDetection;
if (inputData < 800 && prevState && (millis() - timeStamp) > 50) {
prevState = false;
timeStamp = millis();
pressedMillis = timeStamp;
if (inputData < BUT_UP_HIGH && inputData > BUT_UP_LOW) buttonStatus = 1;
else if (inputData < BUT_MENU_HIGH && inputData > BUT_MENU_LOW) buttonStatus = 2;
else if (inputData < BUT_DOWN_HIGH && inputData > BUT_DOWN_LOW) buttonStatus = 3;
}
if ((inputData < BUT_UP_HIGH && inputData > BUT_UP_LOW) && !prevState) {
timeStamp = millis();
if (timeStamp - pressedMillis > pressedDelay) {
pressedDetection = true;
return PRESS_U;
}
}
if (inputData > 800 && !prevState && (millis() - timeStamp) > 50) {
prevState = true;
timeStamp = millis();
if ((timeStamp - pressedMillis) > longDelay && !pressedDetection) {
if (buttonStatus == 1) return LONG_U;
else if (buttonStatus == 2) return LONG_M;
else return LONG_D;
} else if ((timeStamp - pressedMillis) < longDelay) {
if (buttonStatus == 1) return SHORT_U;
else if (buttonStatus == 2) return SHORT_M;
else return SHORT_D;
}
pressedDetection = false;
}
if (inputData > 900) return 0;
}
ISR(ADC_vect) {
if (getADC) {
if (selectAdcPin) {
lightSense = (ADCL | ADCH << 8);
ADMUX = analog_ref | KEYB_PIN;
} else {
keyboardSense = (ADCL | ADCH << 8);
ADMUX = analog_ref | LGHT_PIN;
}
selectAdcPin = !selectAdcPin;
getADC = false;
} else {
getADC = true;
}
}
Если перейти на простое использование привычного analogRead для обоих выходов, то ситуация улучшается, но проблема не уходит. Ложные срабатывания остаются, но их меньше.
Подскажите как полностью избавиться от этого глюка. Скажите есть ли смысл уменьшить номинал резисторов?
Дизель, вы бы код вставили нормально.
И схему нарисовали.
Строки №№ 100 и 103 – на выброс. Так 16-битные регистры не читают. Порядок вычисления операндов операции «|» не определён, а Вам нужно быть уверенным, что ADCL читается раньше, чем ADCH. Зачем вообще выпендриваться? Почему не написать просто
нафига такие премудрости?
Строка №38. Вы уверены, что Вам не нужна полная точность ADC и Вы можете пожертвовать ею ради скорости? Если нет, то почему такой делитель? См. правила выбора делителя в разделе 24.4 даташита. Для частоту 16МГц и полной точности, делитель должен быть 128
Строки №№ 57 – 95. Вы сами-=то понимаете, что Вы там наворотили? Я вот - с огромным трудом. В общем, там всё не так. Ну, для начала явный баг – у Вас prevState одна на всех. Напряжение растёт и падает не мгновенно. Сначала Вы читаете некое напряжение (некую кнопку). При следующем чтении напряжение уже упало, т.е. Вы видите другую кнопку, но Вам пофиг, т.к. Вы не помните, какую Вы видели в прошлый раз и считаете, что в прошлый раз была та же, которую Вы видите сейчас. Понимаете? Вот она и косячит у Вас.
Ч тобы убедиться, выполнте вот такой скетч. Он печатает значение на пине если (и когда) оно меняется. Запустите и нажмите ту кнопку, на которой у Вас 1К резистор. Увидите, что по пути Вы соберёте обе остальные кнопки.
Тут должна быть совсем другая логика. Вам надо принимать решение, что кнопка нажата только тогда когда она (та же самая) остаётся нажатой в течение некоторого времени. Тогда Вы не будете хватать кнопки которые не нажаты, а просто «напряжение мимо них проходит»
Ого! Большое вам спасибо за объяснение. Да, я еще тот погроммист это я знаю. =) Соглашусь, возможно многое из того что пишу написано кривовато и порой без глубокого понимания. Прошу простить. Но все равно спасибо за советы. К сожалению учиться мне особо не у кого, приходится самому больше методом проб и как видите ошибок. Хорошо когда есть люди которые могут объяснить.
Ваш кусочек кода явно указал на суть проблемы.
Короче основную задумку я понял. По большому счету мне нужен программный фильтр.
Тут особо много вариантов не вижу. Либо задержка для антидребезга. Либо просто считываем значения несколько циклов подряд тем самым даем значению устаканиться и только потом после n итераций пропускаем их дальше для анализа. Либо третий вариант принимать значения для которых прописать жесткие условия, а остальные отбрасывать.