Модуль tools.h
Время, преобразование диапазонов, random и PID
#include <tools.h> объявляет время работы, преобразование диапазонов, random() и PID. Заголовок использует uint32_t и bool из других заголовков: подключайте перед ним <dev.h> либо <stdint.h> и <stdbool.h>.
millis
uint32_t millis(void);
Миллисекунды sysTime. Значение uint32_t оборачивается примерно через 49,7 суток. Для интервала используйте (uint32_t)(millis() - start); интервал должен быть короче полного оборота счётчика.
timePowerOff
uint32_t timePowerOff(void);
Длительность предыдущего отключения питания в секундах. UINT32_MAX означает, что пригодная длительность не была определена. Значение вычисляется при запуске прошивки по сохранённой календарной метке; оно не равно uptime.
convInt
int convInt(int in, int inMin, int inMax, int outMin, int outMax);
Линейно отображает входной диапазон [inMin, inMax] в выходной [outMin, outMax]. Используйте возрастающий входной диапазон. Значения за границами насыщаются: in <= inMin даёт outMin, in >= inMax даёт outMax. При равных входных границах возвращается outMax. Выходной диапазон может быть обратным. Расчёт не переполняет int32 на полном диапазоне аргументов. Дробная часть промежуточного смещения от outMin отбрасывается до сложения; это важно при обратном выходном диапазоне.
convFloat
float convFloat(float in, float inMin, float inMax, float outMin, float outMax);
Линейно отображает входной диапазон [inMin, inMax] в выходной [outMin, outMax]. Используйте возрастающий входной диапазон. Значения за границами насыщаются: in <= inMin даёт outMin, in >= inMax даёт outMax. При равных входных границах возвращается outMax. Выходной диапазон может быть обратным. Передавайте конечные аргументы. Специальной проверки NaN, Inf и некорректного порядка входных границ нет.
convDouble
double convDouble(double in, double inMin, double inMax, double outMin, double outMax);
Линейно отображает входной диапазон [inMin, inMax] в выходной [outMin, outMax]. Используйте возрастающий входной диапазон. Значения за границами насыщаются: in <= inMin даёт outMin, in >= inMax даёт outMax. При равных входных границах возвращается outMax. Выходной диапазон может быть обратным. Передавайте конечные аргументы. Специальной проверки NaN, Inf и некорректного порядка входных границ нет.
random
uint32_t random(uint32_t start, uint32_t stop);
Возвращает uint32_t в полуинтервале [start, stop): start включён, stop исключён. Требуется start < stop; функция сама не проверяет порядок и пустой диапазон. Это отдельный механизм от rand()/srand(); не используйте его как криптографическую гарантию.
PID-регулятор
typedef struct {
bool useLimits;
float min;
float max;
float kP;
float kI;
float kD;
float integral;
float lastError;
float lastOutput;
uint32_t lastTime;
} pid_t;
Объект pid_t хранится у вызывающего кода. На один независимый регулятор нужен отдельный объект. Проверка указателя на NULL не выполняется.
pidInit
void pidInit(pid_t * pid, float kP, float kI, float kD, float min, float max);
Записывает kP/kI/kD, min/max и вызывает pidReset(). Ограничения выхода включаются при min < max; при min >= max не применяются. Поле useLimits в текущей реализации не используется: не рассчитывайте на переключение ограничений через него.
pidReset
void pidReset(pid_t * pid);
Сбрасывает интеграл, предыдущую ошибку и выход, помечает объект как ещё не выполнявшийся. Коэффициенты и min/max сохраняются.
pidStep
float pidStep(pid_t * pid, float measurement, float setPoint);
Ошибка равна setPoint-measurement. Первый вызов возвращает пропорциональную составляющую с учётом min/max. Далее dt вычисляется по millis(): при dt < 50 мс возвращается предыдущий выход, при dt > 5 минут состояние сбрасывается и возвращается 0. Интеграл учитывает секунды, производная — изменение ошибки за секунду. При включённых границах ограничиваются интеграл и итоговый выход. Это не гарантия отсутствия интегрального насыщения во всех режимах.
Пример расчёта без изменения выходов
#include <dev.h>
#include <tools.h>
#include <stdio.h>
int main(void) {
pid_t regulator;
pidInit(®ulator, 2.0f, 0.1f, 0.0f, 0.0f, 100.0f);
float result = pidStep(®ulator, 18.0f, 20.0f);
printf("PID: %.2f; map: %d\n", (double)result, convInt(50, 0, 100, 0, 1000));
return 0;
}