Неадекватные показания на аналоговом пине (мега328).

dizzel
Offline
Зарегистрирован: 21.03.2016

Всем привет.

Использую Ардуино НАНО входы ADC3 и ADC6.

На ADC3 у меня висят три кнопки.

      5v--------|4,7kOm|------ADC3
                                |
   Gnd--___---|10kOm|
                                |
   Gnd--___--|4,7kOm|
                                |
   Gnd--___--|1,0kOm|
 
На ADC6 фоторезистор.
 
      5v-------|Photo|----|
                                  |
   Gnd-----------|10kOm|
                                  |
   Gnd------------|100nf|------ADC6

Проблема в считывании показаний с 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
#define KEYB_PIN            0x03
#define LGHT_PIN            0x06

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 для обоих выходов, то ситуация улучшается, но проблема не уходит. Ложные срабатывания остаются, но их меньше.

Подскажите как полностью избавиться от этого глюка. Скажите есть ли смысл уменьшить номинал резисторов?

Green
Offline
Зарегистрирован: 01.10.2015

Дизель, вы бы код вставили нормально.

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

 И схему нарисовали.

dizzel
Offline
Зарегистрирован: 21.03.2016

#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
#define KEYB_PIN            0x03
#define LGHT_PIN            0x06
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;
}
}

 

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

Строки №№ 100 и 103 – на выброс. Так 16-битные регистры не читают. Порядок вычисления операндов операции «|» не определён, а Вам нужно быть уверенным, что ADCL читается раньше, чем ADCH. Зачем вообще выпендриваться? Почему не написать просто

keyboardSense = ADC;

нафига такие премудрости?

Строка №38. Вы уверены, что Вам не нужна полная точность ADC и Вы можете пожертвовать ею ради скорости? Если нет, то почему такой делитель? См. правила выбора делителя в разделе 24.4 даташита. Для частоту 16МГц и полной точности, делитель должен быть 128

Строки №№ 57 – 95. Вы сами-=то понимаете, что Вы там наворотили? Я вот - с огромным трудом. В общем, там всё не так. Ну, для начала явный баг – у Вас prevState одна на всех. Напряжение растёт и падает не мгновенно. Сначала Вы читаете некое напряжение (некую кнопку). При следующем чтении напряжение уже упало, т.е. Вы видите другую кнопку, но Вам пофиг, т.к. Вы не помните, какую Вы видели в прошлый раз и считаете, что в прошлый раз была та же, которую Вы видите сейчас. Понимаете? Вот она и косячит у Вас.

Ч тобы убедиться, выполнте вот такой скетч. Он печатает значение на пине если (и когда) оно меняется. Запустите и нажмите ту кнопку, на которой у Вас 1К резистор. Увидите, что по пути Вы соберёте обе остальные кнопки.

void setup(void) {
	Serial.begin(57600);
}

void loop(void) {
	static unsigned oldVal = 0xFFFF;
	const unsigned newVal = analogRead(2);
	if (oldVal != newVal) {
		oldVal = newVal;
		Serial.println(newVal);
	}
}

Тут должна быть совсем другая логика. Вам надо принимать решение, что кнопка нажата только тогда когда она (та же самая) остаётся нажатой в течение некоторого времени. Тогда Вы не будете хватать кнопки которые не нажаты, а просто «напряжение мимо них проходит»

dizzel
Offline
Зарегистрирован: 21.03.2016

Ого! Большое вам спасибо за объяснение. Да, я еще тот погроммист это я знаю. =) Соглашусь, возможно многое из того что пишу написано кривовато и порой без глубокого понимания. Прошу простить. Но все равно спасибо за советы. К сожалению учиться мне особо не у кого, приходится самому больше методом проб и как видите ошибок. Хорошо когда есть люди которые могут объяснить.

Ваш кусочек кода явно указал на суть проблемы.

Короче основную задумку я понял. По большому счету мне нужен программный фильтр.

Тут особо много вариантов не вижу. Либо задержка для антидребезга. Либо просто считываем значения несколько циклов подряд тем самым даем значению устаканиться и только потом после n итераций пропускаем их дальше для анализа. Либо третий вариант принимать значения для которых прописать жесткие условия, а остальные отбрасывать.

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

dizzel пишет:
Либо третий вариант принимать значения для которых прописать жесткие условия, а остальные отбрасывать.
Не поможет. Вы, наверное не запускали проверочный скетч, как я Вам писал, а зря. Там по дороге он собирает честные значения посторонних кнопок, а не только левость. Вы же не можете отбросить честные значения кнопок.