Эффективный обратный (1 /x) для AVR

Я пытаюсь найти эффективный способ вычисления инверсии на AVR (или аппроксимировать его).

Я пытаюсь рассчитать период импульса для шагового двигателя, чтобы я мог линейно изменять скорость. Период пропорционален обратному значению скорости (p = K/v), но я не могу придумать хороший способ расчета этого на летать.

Моя формула

p = 202/v + 298; // p in us; v varies from 1->100

Тестирование на Arduino, разделение, кажется, игнорируется полностью, оставляя p исправлено в 298 (хотя, возможно, это было бы иначе в avr-gcc). Я также попытался суммировать v в цикле, пока он не превысит 202, и подсчет циклов, но это довольно медленно.

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

Изменить . Возможно, название должно быть «эффективным делением» ...

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

Изменить 2 . При дальнейшем исследовании деление работает на arduino, проблема связана как с неправильной формулой выше, так и с переполнением int в другом месте.

12 голосов | спросил Peter Gibson 13 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowMon, 13 Sep 2010 05:12:53 +0400 2010, 05:12:53

10 ответов


7

Одна хорошая вещь о разделении - это то, что все это делают или меньше. Это довольно сложная функция языка C, и компиляторы, такие как AVR-GCC (называемые IDE Arduino), будут выбирать лучший алгоритм деления, даже если микроконтроллер не имеет инструкции по разделению оборудования.

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


Если вы беспокоитесь, вам может понравиться читать официальные предлагаемые алгоритмы разделения Atmel (один из них оптимизирован для размера кода и один оптимизирован для скорости выполнения, не требует никакой памяти данных). Они находятся в:

http://www.atmel.com/dyn/resources/prod_documents/doc0936.pdf

, который представляет собой приложение «AVR200: умножить и разделить рутины», перечисленные на странице Atmel для своих (достаточно больших) процессоров Atmega, таких как Atmega 168 и Atmega 328, используемые в стандартном Arduinos. Список листов данных и примечаний к применению:

http://www.atmel.com/dyn/products/product_card.asp?part_id = 4720

ответил Jack Schmidt 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 14 Sep 2010 23:43:35 +0400 2010, 23:43:35
4

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

#define VALUE_FOR_V_EQUALS_ZERO 0
uint16_t formula_lookup[100] = {VALUE_FOR_V_EQUALS_ZERO, 500, 399, 365, 348, ..., 300};

...

//"calculate" formula
p = formula_lookup[v > 67 ? 67 : v];

РЕДАКТИРОВАТЬ , вы фактически являетесь только таблицей поиска значений 68, так как значения v больше 67 всегда оцениваются до 300.

ответил vicatcu 15 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 15 Sep 2010 06:51:57 +0400 2010, 06:51:57
3

Есть несколько очень хороших техник, упомянутых в книге «Хакеры Delight Генри Уоррена и на его веб-сайте hackersdelight.org . хорошо работает с меньшими микроконтроллерами при делении на константы, посмотрите этот файл .

ответил timrorr 13 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowMon, 13 Sep 2010 05:31:16 +0400 2010, 05:31:16
3

Ваша функция не кажется, что она даст результат, который вы хотите. Например, значение 50 возвращает примерно 302, а 100 - примерно 300. Эти два результата практически не изменят скорость двигателя.

Если я правильно вас понимаю, вы действительно ищете быстрый способ сопоставить числа 1-100 с диапазоном 300-500 (приблизительно), так что 1 отображает 500 и 100 карт до 300.

Возможно, попробуйте: p = 500 - (2 * v)

Но я могу быть недоразумением - пытаетесь ли вы вычислить время включения постоянной частотной прямоугольной волны? Что такое 298?

ответил pingswept 13 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowMon, 13 Sep 2010 09:30:00 +0400 2010, 09:30:00
3

Эффективный способ аппроксимации делений - сдвигами. например если x = y /103; деление на 103 совпадает с умножением на 0,0097087, поэтому для приближения этого сначала выберите «хороший» номер сдвига (т. е. номер базы-2, 2,4,8,16,32 и т. д.)

Для этого примера 1024 подходит, так как мы можем сказать, что 10/1024 = 0,009765 Тогда можно ввести код:

x = (y * 10)>> 10;

Помните, конечно, чтобы переменная y не переполняла ее тип при умножении. Это не точно, но быстро.

ответил 13 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowMon, 13 Sep 2010 16:42:42 +0400 2010, 16:42:42
3

В другой заметке, если вы пытаетесь сделать разделение на CPU, который не поддерживает разделение, есть действительно классный способ сделать это в этой статье Wiki.

http://en.wikipedia.org/wiki/Multiplicative_inverse

  

Чтобы аппроксимировать обратную величину x,   используя только умножение и   вычитания, можно угадать число y,   а затем повторно замените y на 2y   - xy2. Как только изменение в y становится   (и остается) достаточно малым, y равно   приближение обратного   х.

ответил mjh2007 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 14 Sep 2010 21:53:54 +0400 2010, 21:53:54
1

Этот процесс здесь выглядит mcu-friendly, хотя может потребоваться немного портирования .

Хотя кажется, что LUT будет проще. Вам понадобится всего 100 байт, если вы используете некоторую интерполяцию, и поскольку LUT заполняется константами, тогда компилятор может даже найти его в области кода вместо области данных.

ответил ajs410 13 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowMon, 13 Sep 2010 22:06:37 +0400 2010, 22:06:37
1

Проверьте, чтобы деление выполнялось как с плавающей запятой. Я использую Microchip не AVR, но при использовании C18 вам нужно заставить ваши литералы обрабатывать как плавающие точки. Например. Попробуйте изменить формулу:

p = 202.0/v + 298.0;

ответил mjh2007 13 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowMon, 13 Sep 2010 22:51:37 +0400 2010, 22:51:37
1

Вы хотите быстро, так что здесь ... Поскольку AVR не может эффективно выполнять нормализацию (сдвигаясь влево до тех пор, пока вы не сдвигаетесь больше), игнорируйте любые алгоритмы с псевдо-плавающей запятой. Самый простой способ для очень точного и быстрого целочисленного деления в AVR - через обратную таблицу поиска. В таблице будут храниться обратные величины, масштабируемые большим числом (например, 2 ^ 32). Затем вы выполняете умножение unsigned32 x unsigned32 = unsigned 64 в ассемблере, так что ответ = (числитель * inverseQ32 [знаменатель])>>> 32.
Я реализовал функцию умножения с помощью встроенного ассемблера (завернутый в c-функцию). Однако GCC поддерживает 64-разрядные «длинные длинные», чтобы получить результат, вам нужно умножить 64 бита на 64 бита, а не 32x32 = 64 из-за ограничений языка C в 8-битной архитектуре ......

Внизу этого метода вы будете использовать 4K x 4 = 16K вспышки, если хотите разделить на целые числа от 1 до 4096 ...

Очень точное беззнаковое деление теперь достигается примерно за 300 циклов в C.

Вы можете использовать 24-битные или 16-разрядные масштабированные целые числа для большей скорости, меньшей точности.

ответил Nick 12 J000000Thursday12 2012, 05:47:12
1
p = 202/v + 298; // p in us; v varies from 1->100

Возвращаемое значение вашего уравнения уже есть p=298, поскольку компилятор делит сначала, а затем добавляет, используйте целочисленное разрешение muldiv, которое:

p = ((202*100)/v + (298*100))/100 

Используя это, нужно умножить a*f, a = целое число f =.

Это дает r=a*f, но f=b/c затем r=a*b/c, но он пока не работает, поскольку положение операторов дает окончательный r=(a*b)/c или функция muldiv, способ вычисления чисел фракций с использованием только целого числа.

ответил nepermath 6 AMpSat, 06 Apr 2013 07:29:08 +040029Saturday 2013, 07:29:08

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132