Как свести аналоговый ввод всего к трем случаям?
Я изучаю возможность использования трехпозиционного переключателя в одном из моих проектов для переключения между различными настройками с помощью всего лишь одного аналогового входного контакта. Для трех позиций я использую землю/0 В, ~2,5 В (через делитель напряжения 2x10 кОм) и полное напряжение 5 В. Я делаю это еще и потому, что планирую в будущем аналогичным образом использовать 5-позиционный поворотный переключатель.
Из-за допусков и шума показания аналогового вывода составляют не точно 0, 511, 1023, а скорее 0-1, 509-512 и 1021-1023. Чтобы принять это во внимание, я использую функцию map
следующим образом:
const byte alarmSwitchPin = A0;
int alrmSwState;
byte alrmSet;
byte alrmPrevSet;
void setup() {
Serial.begin(9600);
pinMode(alarmSwitchPin, INPUT);
}
void loop() {
alrmSwState = analogRead(alarmSwitchPin); // Читаем ввод
byte alrmSet = map(alrmSwState, 0, 1023, 0, 2); // Сокращение ввода до трех случаев
if (alrmSet != alrmPrevSet) { // Только если состояние переключателя изменилось
switch (alrmSet) {
case 0:
Serial.println("Low");
break;
case 1:
Serial.println("Medium");
break;
case 2:
Serial.println("High");
break;
}
alrmPrevSet = alrmSet;
delay(200); // В целях тестирования
}
}
Я хочу, чтобы настройка менялась (или, в данном случае, консоль печаталась) только тогда, когда состояние действительно изменилось. Однако это не работает, поскольку функция map
выполняет математические вычисления в целочисленном формате, что не позволяет отобразить верхнюю позицию (в данном случае).
Каковы мои варианты, если я хочу придерживаться метода switch
? Мне любопытно, можно ли это сделать без операторов и условий if
или, как более общий вопрос, какой лучший вариант — правильно сопоставить 10-битный аналоговый вход всего с 3 регистрами/числами. .
PS: Я думал, что analogReadResolution()
может быть вариантом, но я работаю с Arduino UNO, где это недоступно.
@fertchen, 👍1
4 ответа
Лучший ответ:
Самое простое решение вашей проблемы — изменить вызов map()
на
byte alrmSet = map(alrmSwState, 0, 1024, 0, 3);
В приведенном выше вызове сопоставленные интервалы имеют полуоткрытый тип, например [a, b), где под начальными значениями (а именно 0) понимаются включительно, а конечные значения (1024 и 3) являются эксклюзивными.
Хотя это и не ясно из документации, это правильный способ использования
функция map()
. В противном случае усеченное деление дает вам очень
неравномерные интервалы. Сравните:
x map(x, 0, 1023, 0, 2)
----------------------------------
0 – 511 0
512 – 1022 1
1023 2
x map(x, 0, 1024, 0, 3)
----------------------------------
0 – 341 0
342 – 682 1
683 – 1023 2
Результат, который вы получите, очень близок к ответу Джеффа Вахауса.
Что меня раздражает в этом подходе, так это то, что 32-битное целое число
деление, которое map()
использует внутри себя, является очень дорогостоящей операцией.
на небольших 8-битных Arduinos. Если вместо 342 и 683 использовать 256 и
768 в качестве пороговых значений, то вы сможете принять решение, просто взглянув на
старший байт аналогового чтения:
uint16_t alrmSwState = analogRead(alarmSwitchPin);
alrmSet = alrmSwState / 256;
if (alrmSet != alrmPrevSet) {
switch (alrmSet) {
case 0:
Serial.println("Low");
break;
case 1:
case 2:
Serial.println("Medium");
break;
case 3:
Serial.println("High");
break;
}
alrmPrevSet = alrmSet;
delay(200);
}
Обратите внимание, что деление на 256 оптимизировано компилятором
более дешевый битовый сдвиг, но это имеет место только в том случае, если alrmSwState
имеет
беззнаковый целочисленный тип. Вот почему выше он объявлен как uint16_t
.
Замените строку:
байт alrmSet = map(alrmSwState, 0, 1023, 0, 2);
с
байт alrmSet = alrmSwState / ((1023/3) + 1);
И это должно делать то, что вы хотите. Обратите внимание, что в C дробные результаты усекаются (не округляются)
Вы также можете прочитать значение несколько раз и просуммировать их, а затем принять решение на основе суммы.
Это эквивалентно усреднению значений, но без деления на «n», поскольку вас не интересует фактическое число, а только диапазон, в который оно попадает. Это приведет к улучшению отношения сигнал/шум, поскольку мы предполагаем, что ошибки с высоким значением считывания столь же вероятны, как и ошибки с низким значением, поэтому их сумма стремится к нулю, поскольку сумма реального значения V стремится к (n *В).
Используйте переменную типа int или большую для суммы, в зависимости от того, сколько показаний вы решите снимать каждый раз.
Для преобразования необходимо всего три строки кода.
almSet=0;
if (alrmSwState>300) almSet++; // увеличиваем almSet
if (alrmSwState>600) almSet++;
- Отображение значений с аналогового входа дает неожиданные значения?
- Как сопоставить целые значения и округлить их до десятков/тысяч?
- Отправка значения с одного Arduino на другой
- Использование аналогового входа для чтения кнопки
- Как использовать этот 3-контактный ползунковый переключатель?
- Как работать с аналоговыми контактами в цикле?
- Arduino непрерывно считывает значение АЦП с помощью прерывания
- Распиновка аналога Arduino Pro Micro