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

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

Уровень сложностиПростой
Время на прочтение5 мин
Количество просмотров1.8K
Всего голосов 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 чем макросы препроцессора.

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

Для констант лучше 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 это святое...

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

Публикации

Истории