Гистерезис и диапазон масштабирования
Здесь я спросил о том, как добавить гистерезис к показаниям АЦП. Полученный ответ был очень хорошим, и он работал из коробки.
Тем не менее у меня возникают проблемы с попыткой обобщить функцию 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