Как стать автором
Обновить

КодоГенератор Линейных Отображений (как ускорить создание ASIC драйвера)

Уровень сложностиПростой
Время на прочтение5 мин
Количество просмотров1.9K
Всего голосов 7: ↑4 и ↓3+1
Комментарии31

Комментарии 31

За ASIC плюс, за код (математику) - минус.

В качестве подарка функция вычисления коэффицентов линейного уравнения по двум точкам:

Hidden text

function linear_const(x1,y1,x2,y2)
{
// ax+by+c=0
var a = y2-y1;
var b = x1-x2;
var c = y1*(x2-x1)-x1*(y2-y1);
return [a,b,-c];
}

+алгоритм решения системы линейных уравнений методом Гаусса (на вход дать матрицу строк из linear_const (...) ):

Hidden text
/**
https://onecompiler.com/javascript/3zs6bubue
*/
function gaussianElimination(A)
{
  const M = A.length; // Number of equations
  const N = A[0].length-1; // Number of variables


  // Perform Gaussian Elimination
  for (let j = 0; j < N; j++) {
    // Find the pivot row with the largest absolute value in the current column
    let maxRow = j;
    for (let i = j + 1; i < M; i++) {
      if (Math.abs(A[i][j]) > Math.abs(A[maxRow][j])) {
        maxRow = i;
      }
    }

    // Swap rows if needed
    if (maxRow !== j) {
      [A[j], A[maxRow]] = [A[maxRow], A[j]];
    }

    // Eliminate other rows
    for (let i = 0; i < M; i++) {
      if (i !== j) {
        const factor = A[i][j] / A[j][j];
        for (let k = j; k <= N; k++) {
          A[i][k] -= factor * A[j][k];
        }
      }
    }
  }

  // Back substitution to find the solutions
  const solutions = new Array(N);
  for (let j = 0; j < N; j++) {
    solutions[j] = A[j][N] / A[j][j];
  }

  return solutions;
}

Код должен быть на Си. Его же в прошивку вклинить надо.

Напишите вместо var double. ;)

>>function linear_const(x1,y1,x2,y2)
прототипы функций в Си определяются по-другому

>>return [a,b,-c];

эта строчка на Cи не собирается. "error: expected expression before '[' token"

Если очень хочется никто не запрещает вернуть структуру или принять отдельным параметром указатель и заполнить всё необходимое.

Для простых случаев подбираю коэффициенты в экселе, для сложных считаю/анализирую там же. Для выбора варианта удобно одновременно видеть сразу 2 - 5 табличек с коэффициентами и результатами.

подбираю коэффициенты в экселе

Да ну ждать пока этот Microsoft Excel загрузится.

UART-CLI в терминале всегда открыта. Так лучше как раз в UART-CLI тогда и выполнить все вычисления прямо на target.

Тем более у нас на работе Microsoft Excel даже не покупали.

Кто ответит с той стороны UARTа? Само напишется и места в МК не займет? Писать, модифицировать написанное и изменять начальные данные быстрее в экселе. Если нет Экселя то можно использовать Calc из фри офисов, для этого его хватит, но при больших проектах его колбасит.

Кто ответит с той стороны UARTа?

Прошивка на MCU.

 Само напишется и места в МК не займет? 

Напишет программист МК. А место в Flash давно уже не проблема. У меня сейчас микроконтроллер с 4MByte Flash. До этого работал с MCU 6MByte Flash.

Как-то вы перемудрили. Для этой таблицы все коэффициенты подбираются за секунду руками: прирост по 0.5 от соседних значений, значит у нас y=0.5*x+b. Подстаяляем x=1, y=-127 из второй строки таблицы: -127=0.5+b, и получаем b=-127.5 в 2 тривиальнейших арифметических действия.

Можно это и в коде сделать, задав туда лишь 2 строки таблицы. И вы вместо простейшей программы ниже нагородили аж целый Solver:

printf("Введите первые 2 любые строки таблицы в виде x0 y0 x1 y1");
double x0, y0, x1, y1;
scanf("%lf%lf%lf%lf", &x0, &y0, &x1, &y1);
printf("y = %lf * x + %lf\n", (y1-y0) / (x1-x0), (x1*y0-x0*y1) / (x1-x0));

 Для этой таблицы все коэффициенты подбираются за секунду руками

Дак это только эта таблица такая простая попалась.
А бывают попадаются datasheet(ы) с менее удобными числами.

В первой все также тривиально - на 2 увеличения кода, -1 к значению. Значит k=-1/2 Подставляем и 1 арифметическое действия находим b=3, ведь там умножение на 0.

Во второй коэффициент вообще -1. И b=20, опять же в 1 действие.

Зачем? Вызывайте с параметрами в другом порядке - будет вам обратная функция. ей же без разницы из кодов в гейн переводить или из гейна в коды.

Отвечу и на комментарий ниже:

В программировании надо придерживаться компонентного подхода.

Несомненно, разделение на модули - зравый подход. Но, доведенный до абсурда он становится даже вредным. Не стоит ради функции из одной-двух строк создавать столько обвязочного кода.

Зачем? Вызывайте с параметрами в другом порядке - будет вам обратная функция.

В программировании у каждого getter(а) должен быть setter.

Это если вы аж целый класс с геттерами и сеттерами накручиваете почем зря. Если же у вас просто функция ниже, то ничего не надо. Код чист, понятен и прост:

// Calculates coefficients for y=k*x+b given 2 points.
// Warning: parameters must satsify x0 != x1.
void FindLinearTransformation(double x0, double y0, double x1, double y1, double *k, double *b) {
  *k = (y1-y0) / (x1-x0);
  *b = y0 - x0*(*k);
}
...

/* Table values. See datasheet 3.14 at http://path-to-official-documentation:
 *  Code | Gain
 *  -----+-----
 *  0x00 | +20
 *  0x01 | +19
 */
double code_to_gain_k, code_to_gain_b;
FindLinearTransformation(0, 20, 1, 19, &code_to_gain_k, &code_to_gain_b);
double gain_to_code_k, gain_to_code_b;
FindLinearTransformation(20, 0, 19, 1, &gain_to_code_k, &gain_to_code_b);

Это если вы аж целый класс с геттерами и сеттерами накручиваете почем зря.

getter(ы) и setter(ы) нужны для ASIC драйвера. Не для Solver(а).

/*getters*/

bool sa51034_gain_get(uint8_t num ,Gain_t* const gain_db);
bool sa51034_frequency_get(uint8_t num, uint32_t* const frequency_hz);
bool sa51034_power_limit_get(uint8_t num, double* const power_limit);

/*setters*/
bool sa51034_power_limit_set(uint8_t num, double power_limit);
bool sa51034_frequency_set(uint8_t num, uint32_t frequency_hz);
bool sa51034_gain_set(uint8_t num ,Gain_t gain_db);

Ну так туда вы зашиваете подсчитанные константы.

Или их можно макросами объявлять, тогда вместо функции у вас макрос с 4 параметрами, которые вы уже копируете из таблицы. В разном порядке для прямой и обратной функции.

Или их можно макросами объявлять

Для констант лучше enum чем макросы препроессора.

Нет же. Вместо функции вычислить константы. Может, компилятор их даже предподсчитает во время компиляции. Что-то вроде

#define DeclareConversionInternal(Chip, Domain1, Domain2, x0, y0, x1, y1) \
 const double Chip_Domain1##_to_##Domain2_k = ... ; \
 const double Chip_Domain1##_to_##Domain2_b = ... ;

#define DeclareConversion(Chip, Domain1, Domain2, x0, y0, x1, y1) \
  DeclareConversionInternal(Chip, Domain1, Domain2, x0, y0, x1, y1) \
  DeclareConversionInternal(Chip, Domain2, Domain1, y0, x0, y1, x1) \


DeclareConversion(sa51034, code, gain, 0, 20, 1, 19)

bool sa51034_gain_get(uint8_t num ,Gain_t* const gain_db) {
  // проверки на переполнение добавьте.
  num = sa51034_gain_to_code_k*(*gain_db) + sa51034_gain_to_code_b;
}

Или можно прям геттеры и сеттеры сразу генерировать макросом, но это будет позапутаннее. Я бы на константах остановился.

Edit: за правильность склейки в макросах не ручаюсь. Я не сишник вообще и такие макросы очень редко надо писать, мог по памяти неправильно склейку сделать. Но суть, вы, надеюсь, поняли.

А если вы имели ввиду составление двух функций руками, ну так все также тривиально. Если прямой коэффициент -1/2, то обратный - -2. b надо на этот же коэффициент поделить, т.е. умножить на 2. Два дополнительных действия из началььной школы.

Смысл текста шире.
В том, что можно и нужно интегрировать в прошивку часть специфических калькуляторов:
--делитель напряжения

--RC/LC фильтры
--линейные функции
--калькулятор из строки
--калькулятор PLL
--преобразователи hex/ dec/ bin
--Base64, RLE, AES256, вычислить SHA256, посчитать CRC16
и прочее

А для этого нужно чтобы предварительно был поднят UART-CLI.
https://habr.com/ru/articles/694408/

Зачем писать дополнительный железный калькулятор, который ещё нужно через какой-то интерфейс подцепить к компу, потом навбивать команд, если всё считается в уме, на калькуляторе в операционке или электронных таблицах?

UART-CLI не всем нужен, а если и нужен, то впихивать бесполезные функции такое себе.

Зато всё под рукой.

который ещё нужно через какой-то интерфейс подцепить к компу, потом навбивать команд

Не обязательно через интерфейс. Можно запустить симулятор прошивки в виде консольного приложение я делать вычисления в нем.
Так, например, работает BusyBox.

Ужас какой, проще использовать скрипты для расчётов на каком-нибудь питончике.

Потом если надо можно и на сервер сборки прицепить и генерить коэффициенты в компайлтайме, если надо.

Вот, например, прошивка рассчитывает ёмкость керамического конденсатора по его трехбуквенной маркировке.
https://www.radioelementy.ru/articles/markirovka-keramicheskikh-kondensatorov/
До использования CLI это была головная боль.
Теперь - наслаждение.

Конденсаторный калькулятор
Конденсаторный калькулятор

Когда есть CLI прошивка превращается в Swiss Army Knife.

Можно это и в коде сделать, задав туда лишь 2 строки таблицы. И вы вместо простейшей программы ниже нагородили аж целый Solver:

В программировании надо придерживаться компонентного подхода.
Если у Вас есть какая-то сущность (как тут решатель уравнения), то надо выделить её в отдельный программный компонент. В ISO26262 это называется SW component.

Иначе у Вас получится не программа, а КАША.

К любым рекомендациям необходимо подходить скептически и с оглядкой на прикладную задачу, а не фанатично плодить сущности.

Что вы, требование ISO26262 это святое...

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории