Пользователь
0,0
рейтинг
5 апреля 2012 в 12:32

Ускоряем свою Arduino из песочницы

Месяца 3 назад, как и многие горе-электроники, купил себе на мой тогдашний взгляд самую навороченную микропроцессорную плату из семейства Arduino, а именно Seeeduino Mega, на базе процессора Atmega1280. Побаловавшись всласть вращающимся сервоприводом и моргающим светодиодом, встал вопрос: «зачем же я её купил?».

Я работаю одним из ведущих конструкторов на одном крупном военном Зеленоградском заводе, и в данный момент веду проект по разработке метрологического средства измерения. В данной задаче существует бесконечное множество проблем, которые требуют индивидуального решения. Одной из таких задач является управление шаговым двигателем без шумов и с шагом не 1.8 градуса, как сказано в документации шагового двигателя, а до 0.0001 градуса. Казалось бы, задача сложна и нерешабельна, но, повозившись немного со схемами управления, пришёл к выводу, что всё реально и возможно. Требуется только генерация двух сигналов специфичной формы и со сдвигом фаз и частотой изменения напряжения до 1 МГц. (Подробное исследование шагового мотора и раскрытие всех тайн управления напишу в следующей статье) Сразу же в голове стали появляться проблески надежды, что я не зря потратил 1500 рублей на свою красненькую Seeeduino, и я, набравшись энтузиазма, начал разбираться.

Первоначальный ужас:


Подключив микропроцессорную плату к осцилографу, и написав цикл digitalWrite(HIGH), и ниже digitalWrite(LOW), на осцилографе обнаружил довольно унылый меандр с частотой 50Гц. Это кошмар. Это крах, подумал я, на фоне требуемых 1Мгц.
Далее, через осцилограф, я изучил еще несколько скоростей выполнения:
AnalogRead() — скорость выполнения 110 мкс.
AnalogWrite() — 2000 мкс
SerialPrintLn() — при скорости 9600 около 250мкс, а при максимальной скорости около 3мкс.
DigitalWrite() — 1800мкс
DigitalRead() — 1900мкс

На этом я, всплакнув, чуть не выкинул свою Seeeduino. Но не тут-то было!

Глаза боятся, руки делают!


Не буду рассказывать свои душевные муки и описывать три долгих дня изучения, лучше сразу скажу всё как есть!
Подняв всю возможную документацию на Arduino и на процессор Atmega1280, исследовав опыт зарубежных коллег, хочу предложить несколько советов, как заменять чтение/запись:

Улучшаем AnalogRead()

#define FASTADC 1

// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

void setup() {
int start ;
int i ;

#if FASTADC
// set prescale to 16
sbi(ADCSRA,ADPS2) ;
cbi(ADCSRA,ADPS1) ;
cbi(ADCSRA,ADPS0) ;
#endif

Serial.begin(9600) ;
Serial.print("ADCTEST: ") ;
start = millis() ;
for (i = 0 ; i < 30000 ; i++)
analogRead(0) ;
Serial.print(millis() - start) ;
Serial.println(" msec (30000 calls)") ;
}

void loop() {
}


Результат: скорость 18,2 мкс против бывших 110 мкс.
Кстати, максимальная скорость АЦП Атмеги как раз 16мкс. Как вариант — использовать другую микросхему, заточенную именно под АЦП, которая позволит уменьшить скорость до 0,2мкс (читать ниже, почему)

Улучшаем digitalWrite()

Каждая Arduino/Seeeduino/Feduino/Orduino/прочаяduino имеет порты. Каждый порт — 8 бит, которые сначала надо настроить на запись. Например, на моей Seeeduino PORTA — c 22 по 30 ножку. Теперь всё просто. Управляем с 22 по 30 ножки с помощью функций
PORTA=B00001010 (битовая, ножки 23 и 25 — HIGH)
или
PORTA=10 (десятичная, всё так же)
Результат = 0,2мкс против 1800мкс, которые достигаются обычным digitalWrite()

Улучшаем digitalRead()

Практически то же самое, что и в улучшении с digitalWrite(), но теперь настраиваем ножки на INPUT, и используем, например:
if (PINA==B00000010) {...} (если на ножке 23 присутствует HIGH, а на 22 и 24-30 присутствует LOW)
Результат выполнения этого if() — 0.2мкс против 1900мкс, которые достигаются обычным digitalRead()

Улучшаем ШИМ модулятор, или analogWrite()

Итак, есть данные, что digitalRead() исполняется 0,2мкс, и ШИМ модулятор имеет дискретность 8 разрядов, минимальное время переключения ШИМ 51,2мкс против 2000 мкс.
Используем следующий код:
int PWM_time=32; //Число, которое мы как бы хотим записать в analogWrite(PIN, 32)
for (int k=0;k<PWM_time) PORTA=B00000001;
for (int k=0;k<256-PWM_time) PORTA=B00000000;

Вот и получили ШИМ с частотой 19кГц против 50Гц.

Подведём итоги


digitalWrite() было 1800мкс, стало 0,2мкс
digitalRead() было 1900мкс, стало 0,2мкс
analogWrite() было 2000мкс, стало 51,2мкс
analogRead() было 110мкс, стало 18,2мкс, а можно до 0,2мкс
Владимир @vovalog
карма
99,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое

Комментарии (69)

  • +12
    Ого! Какое Ардуино, оказывается, мееедленное. Тогда уж надо выкинуть нафиг ардуинью среду разработки и писать на чистом C/ASM. Можно будет дергать портами хоть каждый такт.
    Вот и получили ШИМ с частотой 19кГц против 50Гц.
    А еще в атмеге есть аппаратный ШИМ. При разрядности 8 бит частота будет 78 кГц.
    • +1
      О, вы меня опередили… То же самое хотел сказать.
    • 0
      Я даже студию ту не открывал, рисовал сразу на С весь код. Интегрировал все это в Visual Studio, намного удобнее получается, чем недостудия оригинальная.
    • +2
      А еще в атмеге есть аппаратный ШИМ. При разрядности 8 бит частота будет 78 кГц.

      К тому же присутствуют экземпляры со специальным PLL для ШИМа, который позволяет поднять частоту еще выше (Tiny45, внутренний RC генератор * 8, входная частота PWM = 64 МГц).
  • +8
    Кто мешал использовать аппаратный выход с таймера? Там десятки килогерц можно выжать при 8 битах на ШИМ.
    Зачем использовать кривые ардуиновские библиотеки, вместо стандартного кода, скомпиленного ВинАВРом или еще чем?

    Поражает меня, как люди себе сами создают проблемы, а потом радуются что их кое как решили.
    • +14
      поддерживаю
      как-то все это странно — ведущий конструктор -> нерешаемая задача -> Arduino(!!!!!) -> доработка библиотек
      какая-то адская жесть.
      Статью минусануть не могу, потому что то, что сделано — хорошо. Но то, почему это было сделано — ад. КИП на крупном военном зеленоградском заводе на базе ардуино
      гы гы гы
      ГЫ ГЫ ГЫ ГЫ
      я счетаю, что если бы конструкторы фобос-грунт не вы*?№лись и сделали все на ардуино, грунт с Фобоса уже бы изучали в наших лабораториях.
      • +2
        Передо мной большие вопросы, как бы мне влезть в смету расходов. В данный момент на один блок электроники у меня есть 2000р. Это очень проблематично, если сейчас заказывать разработку индивидуальной схемы на более крутом микропроцессоре, а потом штамповка печатных плат, а потом распайка. Если бы мне требовалось сделать 1000 приборов — это было бы всё терпимо, но мне нужно всего 10 приборов, вот и жмусь как могу.
        Использовать готовые дешевые решения — это выход для меня.
        • 0
          Припаять мегу к макетке или к плате, сделанной самим, «лазерным утюгом» вам выльется рублей в 200 — цена меги+текстолит+обвяз. Дешевле любой ардуины.
          • 0
            ой, я тут на какие-то шикарные грабли нарвался, четыре дня с тремя программаторами и осциллом — так их и не отловил. сдался.
            дуня после ресета прыгала сразу на приложение, минуя бутлоадер. яму в столе бошкой пробил — тщетно.
            • +1
              Фьюзы не забыли запрограммировать на использование бутлоадера?
              • 0
                да нет. несмотря на то, что с авр-ом первый раз работал, про гимор с фьюзами слышал. Кроме того я собрал HV-программер, так что кривые фузы — не проблема.

                Вот описание истории (англ.): arduino.cc/forum/index.php/topic,99762.0.html

                Проблема была точно в плате, потому что перепаянный на дуню чип завелся с первого раза.
          • 0
            Хорошо бы, чтобы продукция на выходе получилась презентабельна. Такую, как Вы говорите, ни одному заводу не продашь.
            • 0
              Посмотрите в интернете, насколько презентабельны бывают платы, изготовленные в домашних условиях. Было бы желание.
        • +4
          Тогда более чем странно, что выбрали AVR, т.к. те же STM32 сейчас гораздо дешевле. Да не просто AVR, а детский конструктор на основе его.
          • +2
            Вот именно. Плата STM32VLDISCOVERY стоит 500р, так там на борту МК есть специальный advanced-control таймер с тремя каналами (у каждого есть ещё комплементарный) и с программируемым dead-time — специально сделано для руления движками, да ещё два watchdog'а — один обычный, другой просадку напряжения определяет. Всё для промышленного применения есть, а автор берёт Arduino и мучается (:
        • +4
          Просто поразило, что на крупном военном заводе в ОТК есть место колхозу
          Но даже если есть. Если нужны полуфабрикаты. Я все равно уверен, что мега — это оверкил
          я думаю, что любая discovery-плата подошла бы в разы лучше (http://www.terraelectronica.ru/catalog.php?ID=1001&Search=1&Text=discovery&Gde=2&PageS=1 — от 490 рублей)
          Использование дуни может быть оправдано наличием готовых библиотек. Но если надо лезть в код — лучше уж сразу на stm32-ку забираться.
          • 0
            Благодарю, попробую с ней тоже разобраться! Вопрос цены очень и очень актуален!
            • 0
              кик-старты есть не we.easyelectronics.ru, в разных IDE-шках. Я сам пользуюсь CooCox IDE, но огромная куча готовых примеров содержат проекты для IAR-a и Keila, но не для CooCox.
        • –1
          Вроде бы появилась Arduino-образная плата, но на ARM-процессоре. Думается, у нее с быстродействием должно быть не так уныло.
        • +1
          Этим комментарием вы разбили мою веру в оборонную промышленность.
          • +2
            Тут всё еще хуже, чем вы думаете)
  • 0
    Возможно Вы и правы, что можно использовать стандартные библиотеки, но для меня было сложновато, всё таки я конструктор, больше по черчению деталек спец.
    В конечном итоге мне нужно было 8 индивидуально управляемых бит. А десятки килогерц — не 5МГц, которые в итоге получились.
    • +3
      Цель достигнута — это главное. Просто в процессе её достижения вы изобрели велосипед: низкоуровневое программирование Arduino. Для тех, кто работал с «голым» AVR, изложенное вами кажется очевидным и само собой разумеющимся. Но для людей, развращенных избалованных ардуинами, данный материал будет полезен.
      На будущее я бы посоветовал присмотреться к ARM-контроллерам. Сигналы в несколько МГц — почти предел для AVR, а цены на Atmel'овские контроллеры (и особенно, отладочные платы) сильно завышены.
      Возьмите, например, комплект STM32-discovery: работать с ним ненамного сложнее, чем с ардуиной, а возможности процессора на порядок выше.
  • +2
    Ведущий конструктор ведь. Вот и разобрался.
  • 0
    Я конечно все понимаю, но если Вы работаете на заводе, то не проще ли писать свои программы на том же Си++ в нормальной среде программирования?
    • 0
      Если бы были тут программисты! =)
      Тут полный цех фрезеров, столяров… А военный он только по тому, что делают корпуса ракет и снарядов. Короче, тут полно специалистов на уровне ПТУ.
    • +1
      че-то я не смог осилить логику вопроса
      • 0
        Я к тому, что если есть микроконтроллер, то для него пишутся программы на его родном языке — асме или си. А программировать на надстройке типа ардуино — это хорошо только для начинающих или для простых задач.
        • 0
          родной язык любого процессора — машкоды. Все что сверху — прослойка того или иного уровня абстракции. И в Ардуино используется обычный C++ с набором библиотек
          • 0
            Не совсем так.

            >Arduino hardware is programmed using a Wiring-based language (syntax and libraries), similar to C++ with some slight simplifications and modifications, and a Processing-based integrated development environment.[4]

            Т.к. там стоит обычная мега, ничто не мешает программировать ее на обычном С, но подразумевается что основной инструмент — это их Processing/Wiring
            • 0
              Ну это написано для пущего пыху. В реальности все как написал оратор выше — C++ с gcc-avr, да весьма примитивый препроцессинг, который заключается туть-ли не в одном #include.
              • 0
                Уже увидел, ага. Долго смотрел на их колонки сравнения с С++ и пытался понять, что же тут настолько отличается, что эта штука объявлена аж новым языком…
  • +3
    Вот тут все накинулись на ардуино. ARM и низкий C это конечно замечательно, сам этим добиваюсь чудес производительности, но на самом деле, человек решил задачу, уложившись в бюджет (надеюсь, правда, он их не в live-critical приложении использует)). Что ещё нужно?
  • +1
    DigitalWrite() — 1800мкс
    DigitalRead() — 1900мкс
    — тут вы что то перепутали :)
  • +4
    Что-то у меня возникли сомнения в адекватности arduino sdk
    вывод значения в порт на avr asm:
    out porta, [value];
    + нужна надстройка для изменения одного бита на порте

    Исходники из arduino sdk:
    pastebin.com/PThfqLGu

    Отсюда мы имеем либо кривые решения в Arduino sdk, либо забивание гвоздей микроскопом (или рассматривание бактерий в гвоздь, тут я не знаю)
    • 0
      все там как раз нормально. Если на ноге висит PWM, то выключаем его, прежде чем выставлять уровень
      • 0
        Судя по коду и числу проверок в SDK — он рассчитан на не очень опытных программистов. Я с трудом представляю ситуацию, когда мы не знаем, для чего используем ноги контроллера.
        • +1
          учитывая целевую аудиторию (люди, которые с железками не работали до этого, а часто и не программировали вообще), вполне реальная ситуация
        • 0
          вот, в тему вашего комментария поспела ссылочка

          arduino.cc/blog/2012/04/05/arduino-users-are-getting-younger-ella-smith-10/

          даже в URL сказано достаточно :)
    • 0
      Настройка для изменения одного бита не требуется. Требуются довольно простые битовые операции, такие, как битовое ИЛИ или исключающее ИЛИ. Вещь простая, скорость выполнения на процессоре гигантская.
      • 0
        Именно это я и написал

        Дисклаймер: avr в частности и ассемблер вообще знаю только теоритически.

        Асмовый код:
        loop: out porta, 0;
        nop // для выравнивания времи уровней
        out port, 1;
        jmp loop;

        И того — 4 процессорные операции, 4 такта (надеюсь)
        При частоте 16MHz имеем частоту колебаний 4MHz.
        Вам нужна частота 1MHz, соответственно добиваете код nop'ами или полной нагрузкой (условиями, расчётами)
        • 0
          Я уже давно не писал под AVR и спецификации на их АСМ не очень помню, но jmp точно не один такт исполняется)
          • 0
            От 2 до 4 тактов. Впрочем, если приходится считать такты — скорее всего, подход к решению задачи неправильный. Например, генерировать сигналы нужно, по возможности, аппаратными средствами, а если ими нельзя — брать камень пожирнее да побыстрее. ST скоро в продажу запустит Cortex-M0 — тогда вялым 8-биткам точно хана.
            • 0
              ST скоро в продажу запустит Cortex-M0 — тогда вялым 8-биткам точно хана.

              У ST уже есть восьмибитки по цене 12-14 рублей в розницу (пруф). Сравнимо с корпусом логики, а по возможностям — как мега восьмая.
              • 0
                Я в курсе, но то ж 8-битки, а Cortex-M0 будут по той же цене, но 32-битные и в целом более жирные. Единственный «минус» в том, что CM0 будут минимум с 32 ногами. Но обещают корпус UFQFPN32, так что драгоценное место на плате можно будет сэкономить.
                • 0
                  Почему минус? Чем больше ног, тем лучше.
                  • 0
                    Я в кавычки потому и взял, что минусом это является, когда нужно сделать очень компактно и ног требуется с десяток. А так, конечно же, 32 ноги — разумный компромисс в большинстве случаев.
          • +1
            RJMP и IGMP — два такта, JMP — три.
            Последний применяется крайне редко, когда прыжок нужен более чем на 2к слов.
        • 0
          *полезной нагрузкой, естественно
        • 0
          Кстати, вы так будете дрыгать всем портом, это не шибко хорошо…
          • 0
            www.avr-asm-tutorial.net/avr_en/beginner/COMMANDS.html
            IJMP, 2 такта, да. Купился на википедийное «большая часть команд исполняется за 1 такт»
            Ардуина, судя по SDK, тоже весь порт перезаписывает. Кстати, а как писать тогда на одну ногу?
            • +1
              Ну как, как всегда. По маске.
              Это ж не Кортекс М3, где есть великолепные регистры BSSR, BRR и битбандинг…
            • +1
              SBI PORTB, 0
              CBI PORTB, 0
              • 0
                Мда, разумеется. Совсем позабыл АСМ от АВРок…
                Ну в любом случае, если нужно разом установить/сбросить N бит, то придется юзать маску, увы.
                • 0
                  Разумеется! Плюс еще команда SBI/CBI выполняются за 2 такта, а OUT — за 1.
              • 0
                SBI PINB, 0 даст инверсию PORTB, 0 на новых сериях МК :)
  • 0
    Наверно наивный вопрос, но все же хочу узнать: если нужен шаг 0.0001 градус, а двигатель дает 1.8 за полный шаг, то не правильнее было бы использовать редуктор? Этот вариант наверняка рассматривался, просто интересно почему он был отброшен в пользу хитрого управления шаговиком.
    • 0
      Да, вы правы, это первая мысль, которая пришла к нам в голову, но:
      1) Шумы редуктора для нас недопустимы. Проверяли редуктор — не подходит.
      Более конкретно — подвешенный к жесткой пружине редуктор вибрирует при работе на 10-15 микрон, а на шаговом двигателе при нормальной работе удалось добиться вибрации порядка 600 нанометров.
      2) Второй минус редуктора — наличие люфта. Люфт порядка 1градуса.
      3) Ну и, конечно, невозможность точного позиционирования редуктора. На него подал 12В, он крутится. А на сколько он провернулся — зависит от точности калибровки, от его разгона, он смазки, от силы, противодействующей кручению. Эти проблемы можно решить, установив сверхточный датчик угла поворота с дроблением до 0.0001 градуса, но это нереально дорогое решение.
      • +1
        Видимо имелось ввиду использование редуктора вместе с шаговиком. При этом точное позиционирование легко просчитывается без обратной связи.
        Так же редукторы есть безлюфтовые. Правда не знаю, на сколько дорогие.
        • 0
          При этом точное позиционирование легко просчитывается без обратной связи.

          Позиционирование положения вала двигателя просчитывается, или выходного вала редуктора с люфтом?

          Так же редукторы есть безлюфтовые. Правда не знаю, на сколько дорогие.

          А где искать, не знаете?
  • +1
    Зачем автор меня обманывает?

    Автор пишет:
    «Подключив микропроцессорную плату к осцилографу, и написав цикл digitalWrite(HIGH), и ниже digitalWrite(LOW), на осцилографе обнаружил довольно унылый меандр с частотой 50Гц


    Для проверки написал простой тестовый скетч:

    void setup() {
    // initialize the digital pin as an output.
    // Pin 13 has an LED connected on most Arduino boards:
    pinMode(13, OUTPUT);
    }

    void loop() {
    digitalWrite(13, HIGH); // set the LED on
    digitalWrite(13, LOW); // set the LED off
    }

    На Arduino Uno получаем на выходе следующую картинку:



    Меандр с частотой 119 кГц. Где тут 50 Гц автора?
    • 0
      Где же вы раньше были?
      Я хоть и не любитель (если не сказать хуже) этих ардуин, но заявленные в статье тайминги ни в какие ворота, даже при хреновом коде…
  • 0
    Вы не забыли упомянуть про регистры направления портов, там где писали про PORTA= или x=PINA? ;-)
  • 0
    Нашел в сети еще информацию по ускорению Arduino
  • 0
    кто-нибудь пробовал это на других чипах?

    atmega328, attiny85, attiny13?
  • 0
    (Подробное исследование шагового мотора и раскрытие всех тайн управления напишу в следующей статье)

    Очень жду эту статью, задача актуальна и для меня.
  • +1
    Спасибо большое за статью! Прикрутил к ардуинке дисплей от сименса A60 по этой статье, но очень он медленно отрисовывал. Переписал отправку данный в дисплей основываясь на вашем посте и он стал мгновенно отрисовывать.

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