Гистерезис и диапазон масштабирования
Здесь я спросил о том, как добавить гистерезис к показаниям АЦП. Полученный ответ был очень хорошим, и он работал из коробки.
Тем не менее у меня возникают проблемы с попыткой обобщить функцию resample
, чтобы сопоставить входные значения с другим диапазоном выходных значений, где нижняя направляющая не ноль.
Если бы нижняя рейка была нулевой, достаточно было бы изменить сдвиги с умножением/делением на 1024/max
. Но я не могу понять, как обращаться с нижней направляющей, когда min > 0
.
Моя попытка:
int Menu::resample(int adc, int *value, byte min, byte max)
{
// проверки работоспособности опущены: min < макс и макс < 128
double m = 1015.0 / (max - min); // 1015 = 1023 - 8 (1 младший бит вывода с низким разрешением)
// иначе он достигает 126, даже если max = 127
int q = min << 3;
int lowerTest = (q + *value * m) - ADC_HYSTERESIS;
int higherTest = (q + (*value + 1) * m) + ADC_HYSTERESIS;
int lowerOutput = min + (adc + ADC_HYSTERESIS) / m;
int higherOutput = min + (adc - ADC_HYSTERESIS) / m;
if (adc < lowerTest) *value = lowerOutput;
else if (adc >= higherTest) *value = higherOutput;
if (*value > max) *value = max;
if (*value < min) *value = min;
return *value;
}
Что-то не так в моей математике, так как сравнение всегда подпадает под первый if
(т.е. *value = lowerOutput
).
Можете ли вы помочь мне исправить этот код, чтобы я мог иметь гистерезис на любом масштабированном выходе?
Примечание: вместо использования статической переменной для значения, как в ответе, я использую struct
:
slider.value = resample(adc, &slider.lastValue, slider.min, slider.max);
где:
typedef struct
{
int value;
int lastValue;
int min;
int max;
} slider_t;
slider_t slider;
@Mark, 👍2
1 ответ
Лучший ответ:
Во-первых, несколько комментариев об этой первой попытке.
Нет смысла хранить в slider_t
как lastValue
, так и value
.
Как это используется, оба поля всегда обновляются одним и тем же
значение: lastValue
обновляется указателем внутри resample()
, а
value
обновляется путем присвоения ему значения, возвращаемого resample()
. я
предложил бы удалить поле lastValue
и либо:
предоставление
value
по значению (а не по указателю) вresample()
, илипредоставление ссылки на
slider_t
в качестве единственного параметра.
Первый подход лучше, если вы хотите избежать resample()
и
slider_t
тесно связан.
Далее идет сочетание типов int
и byte
. Если вы хотите сэкономить
памяти и улучшить согласованность, я бы использовал byte
для всех файлов с низким разрешением.
значения, находящиеся в области вывода resample()
. На самом деле, я бы
используйте uint8_t
, то же самое, только более стандартное.
Тогда есть несколько вещей, которые я не понимаю в этом коде:
Почему
1015
? Мне кажется, что это число должно зависеть от ширина выходного диапазона.Почему
min << 3
? Опять же, это деление на 8, а правильное фактор, вероятно, должен зависеть от выходного диапазона.
Теперь для решения я бы попробовал следующий подход:
Вычислите минимальное и максимальное возможные выходные значения в соответствии с предоставленный ввод с учетом гистерезиса.
Ограничьте вывод, чтобы он оставался в пределах этой скобки.
В коде:
uint8_t Menu::resample(int adc, uint8_t value, uint8_t min, uint8_t max)
{
value = constrain(value, min, max); // на всякий случай...
uint8_t lower = map(adc - ADC_HYSTERESIS, 0, 1024, min, max+1);
uint8_t upper = map(adc + ADC_HYSTERESIS, 0, 1024, min, max+1);
return constrain(value, lower, upper);
}
Некоторые моменты, на которые стоит обратить внимание:
Не знаю, было ли это задумано изначально, но Arduino функция
map(x, in_min, in_max, out_min, out_max)
работает плохо при сопоставлении интервала [in_min
,in_max
] с [out_min
,out_max
]. Однако он хорошо справляется с отображением [in_min
,in_max
) до [out_min
,out_max
) (обратите внимание на полуоткрытые интервалы). Вот почему четырьмя последними параметрами вызоваmap()
являются (0, 1024, мин, макс+1), а не (0, 1023, мин, макс).Первый вызов
constrain()
можно удалить, если вызывающий ответственность за выполнение этого предварительного условия.Если предварительное условие допустимо (или, как здесь, принудительно), оно также удерживать для возвращаемого значения при условии, что
adc
находится в пределах [0, 1024].
Этот метод будет вызываться следующим образом:
slider.value = resample(adc, slider.value, slider.min, slider.max);
- Как получить среднее значение 16-битного АЦП?
- Избегайте шума 1-LSB в показаниях АЦП
- Постоянный выход тактовой частоты Arduino
- Разные и самые быстрые способы вычисления синусов и косинусов в Arduino
- Расширение аналоговых входов для Arduino
- ESP8266: system_adc_read_fast() всегда возвращает 1024
- Высокоскоростной внешний АЦП
- Измерение напряжения литий-ионного элемента, используемого для питания Arduino через повышающий модуль
Невероятно, как довольно сложную задачу можно решить с помощью нескольких строк кода. Я впечатлен. Спасибо!, @Mark