All streams
Search
Write a publication
Pull to refresh
86
0

разнорабочий

Send message
есть ли реальный опыт применения такого подхода и примеры, где это было реализовано и использовано?


не претендуя на «реальность», есть более развернутый пример:
github.com/sadr0b0t/stepper_h/blob/master/stepper_test/stepper_test.cpp

это моя библиотечка для управления шаговыми моторами, основанная на подсчете шагов и отправке импульсов фоном из прерывания таймера. В общем, примерно то, что описано в примере, только для нескольких моторов и с более ветвистой логикой. Собственно, эти тесты я начал применять и обкатал подход именно в ней, т.к. в один момент там внутри начали происходить странные вещи, которые без потактового разбора за пределами железки было крайне сложно, если вообще возможно, отловить. Добавление тестов помогло отловить и исправить многие проблемы уровня «недосчитались шага на последнем такте, если время шага некратно частоте импульса таймера».

могут ли описанные тесты подойти для ситуации когда часть условий (внешние сигналы) влияют на ход выполнения кода и не могут быть имитированы тупым «элементарным ручным вызовом обработчика»?

появление внешних сигналов можно легко имитировать при помощи мокапов, ситуацию можно разложить на что-то вроде: вот пришел внешний сигнал (записали состояние в мокап), вот выполнили тестируемый участок кода (он внутри проверяет состояние сигнала), вот он дал такой результат; вот пришел другой сигнал, вот опять выполнили этот участок, вот он дал другой результат и т.п. Если речь о том, что от сигнала идет прерывание и он там меняет какие-то переменные, которые используются внутри тестируемого участка, в любой произвольный момент, то не знаю, наверное это тоже можно как-то протестировать, если разложить тестируемый кусок на более мелкие запчасти и проверить поведение в каждом из возможных промежутков. В общем, идея — свести ситуацию к множеству последовательных событий (и их разных комбинаций) в одном потоке, тогда в каждой контрольной точке можно делать предсказуемые проверки. Если прям нужно много потоков, не знаю, возможно и есть такие техники для модульных тестов, я не в курсе.
>Скажите, вы про существование arm-none-eabi-gcc и GNU Make в курсе?

Под stm завезли кросс-платформенный полностью открытый тулчейн? Искренне рад за стмщиков!
Перефразирую классику: высокие умы обсуждают концепты, заложенные в языки, и архитектуру проектов, средние умы обсуждают стиль кода, низкие умы обсуждают вижуалстудию, в которой этот код написан.

>И notepad.exe ещё есть
notepad.exe — это вин-онли проприетарная поделка, как и большая часть тулчейна для stm, который я когда-то видел. А еще в нем нет подсветки.
Заглушки и мокапы — это не костыли, а концептуальный элемент технологии модульного тестирования
Я не занимаюсь профессиональной разработкой под ардуино (т.е. не получаю за это деньги), не вижу ничего плохого, если при создании любительских проектов в любительских средах разработки будет использоваться грамотный подход.
1) Возможно, вы не в курсе, Arduino != AVR
2) Говорить о быстродействии вне контекста задачи — беседа с малым количеством смысла
Оказывается, это российским капиталистам так нравится, что их по надуманному предлогу в камеру сажают и собственность отнимают.

У капиталистов с собственностью все в порядке. Просто те, кого вы называете капиталистами, никакие не капиталисты, а мелкие хозяйчики, коммерсанты, буржуа. Они бы может быть и хотели бы стать настоящими капиталистами, чтобы правительство выражало их интересы, да только поезд уже ушел, силенок во-время не накопили. Поэтому они являются для настоящих капиталистов точно такой же кормовой базой, как и все остальное, что попадет в их поле зрения.

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

у кого надо собственность всегда гарантирована, хоть тогда, хоть сейчас, внутривидовые разборки не в счет
>Да, и полиция к этому инженеру будет обращаться «сэр» даже если он что то там нарушил.

Расскажите это Яну Мердоку (основателю Дебиана, годовой доход $1.5млн, был)
С помощью одного таймера можно сделать сетку импульсов с достаточной частотой и вести по ней несколько независимых «потоков». Например, 10 вызовов в секунду: на каждый 2й вызов мы шлем импульсы шагать мотору1, на каждый 3й вызов — импульсы шагать мотору2, на каждый 5й вызов — смотрим значение какого-нибудь датчика. Главное, чтобы каждый вызов умещался в сетку.

На Arduino Due с чипом SAM/ARM, полагаю, можно запустить все 9 таймеров одновременно, но, пожалуй, будет сложно избежать чехарды с наложениями прерываний (видно, как на нее легко нарваться уже с 2мя таймерами).

Прикольный проектик. Реально где-то внедрено или в процессе разработки?
Кстати, к вопросу о наложении таймеров. Немного модифицировал скетчик — добавил delay на 250 миллисекунд между запусками таймеров, чтобы не стартовали одновременно.

#include"timer-api.h"

void setup() {
    Serial.begin(9600);
    
    timer_init_ISR_1Hz(_TIMER2_32BIT);
    delay(250);
    timer_init_ISR_2Hz(_TIMER4_32BIT);
}

void loop() {
    Serial.println("Hello from loop");
    delay(5000);
}

void timer_handle_interrupts(int timer) {
    if(timer == _TIMER2_32BIT) {
        static unsigned long prev_time = 0;
        
        unsigned long _time = micros();
        unsigned long _period = _time - prev_time;
        prev_time = _time;
        
        Serial.print("goodbye from timer1: ");
        Serial.println(_period, DEC);
    } else if(timer == _TIMER4_32BIT) {
        static unsigned long prev_time = 0;
        
        unsigned long _time = micros();
        unsigned long _period = _time - prev_time;
        prev_time = _time;
        
        Serial.print("goodbye from timer2: ");
        Serial.println(_period, DEC);
    }
}


и получил ровненькие периоды для обоих таймеров в отладке:

Hello from loop
goodbye from timer2: 500000
goodbye from timer1: 1000000
goodbye from timer2: 500000
goodbye from timer2: 500000
goodbye from timer1: 1000000
goodbye from timer2: 500000
goodbye from timer2: 500000
goodbye from timer1: 1000000
goodbye from timer2: 500000
goodbye from timer2: 500000
goodbye from timer1: 1000000
goodbye from timer2: 500000
goodbye from timer2: 500000
goodbye from timer1: 1000000
goodbye from timer2: 500000
Hello from loop
goodbye from timer2: 500000
goodbye from timer1: 1000000
goodbye from timer2: 500000
goodbye from timer2: 500000
goodbye from timer1: 1000000
goodbye from timer2: 500000
goodbye from timer2: 500000
goodbye from timer1: 1000000
goodbye from timer2: 500000
goodbye from timer2: 500000
goodbye from timer1: 1000000
goodbye from timer2: 500000
goodbye from timer2: 500000
goodbye from timer1: 1000000
goodbye from timer2: 500000
Hello from loop
goodbye from timer2: 500000
goodbye from timer1: 1000000
goodbye from timer2: 500000
goodbye from timer2: 500000
> — В статье и примерах речь идёт об одном таймере. Что насчёт работы одновременно нескольких таймеров? Можно добавить пример кода в библиотеку?

Можно. Написал на скорую руку скетчик, пожалуй, добавлю его в библиотеку
#include"timer-api.h"

void setup() {
    Serial.begin(9600);
    
    timer_init_ISR_1Hz(_TIMER2_32BIT);
    timer_init_ISR_2Hz(_TIMER4_32BIT);
}

void loop() {
    Serial.println("Hello from loop");
    delay(5000);
}

void timer_handle_interrupts(int timer) {
    if(timer == _TIMER2_32BIT) {
        static unsigned long prev_time = 0;
        
        unsigned long _time = micros();
        unsigned long _period = _time - prev_time;
        prev_time = _time;
        
        Serial.print("goodbye from timer1: ");
        Serial.println(_period, DEC);
    } else if(timer == _TIMER4_32BIT) {
        static unsigned long prev_time = 0;
        
        unsigned long _time = micros();
        unsigned long _period = _time - prev_time;
        prev_time = _time;
        
        Serial.print("goodbye from timer2: ");
        Serial.println(_period, DEC);
    }
}


Результат:
Hello from loop
goodbye from timer2: 551004
goodbye from timer1: 1051004
goodbye from timer2: 525902
goodbye from timer2: 474098
goodbye from timer1: 1000000
goodbye from timer2: 525902
goodbye from timer2: 474098
goodbye from timer1: 1000000
goodbye from timer2: 525902
goodbye from timer2: 474098
goodbye from timer1: 1000000
goodbye from timer2: 525902
goodbye from timer2: 474098
goodbye from timer1: 1000000
goodbye from timer2: 525902
Hello from loop
goodbye from timer2: 474098
goodbye from timer1: 1000000
goodbye from timer2: 525902
goodbye from timer2: 474098
goodbye from timer1: 1000000
goodbye from timer2: 525902
goodbye from timer2: 474098
goodbye from timer1: 1000000
goodbye from timer2: 525902
goodbye from timer2: 474098
goodbye from timer1: 1000000
goodbye from timer2: 525902
goodbye from timer2: 474098
goodbye from timer1: 1000000
goodbye from timer2: 525902
Hello from loop
goodbye from timer2: 474098
goodbye from timer1: 1000000
goodbye from timer2: 525902
goodbye from timer2: 474098
goodbye from timer1: 1000000
goodbye from timer2: 525902
goodbye from timer2: 474098
goodbye from timer1: 1000000
goodbye from timer2: 525902
goodbye from timer2: 474098
goodbye from timer1: 1000000
goodbye from timer2: 525902
goodbye from timer2: 474098
goodbye from timer1: 1000000
goodbye from timer2: 525902
Hello from loop


Только нужно учитывать, что на разных чипах разное количество таймеров и могут быть нюансы с именами и разрядностью. Например, на моей Arduino Leonardo всего один таймер (или по крайней мере, в Stepper.h, из которой я брал базовый код, выносился только один) и на ней этот пример не запустится. На PIC32 (пример выше запущен на ней) — 5 16-битных таймеров, 2 пары из которых можно объединить в 32-битные. На SAM/ARM Arduino Due вообще все 9 32-битных таймеров.

Какие есть таймеры на каких платформах написано в разделе «Выбор таймера» github.com/sadr0b0t/arduino-timer-api и можно посмотреть в исходниках в файле timer_setup.c для каждой платформы.

>— Что произойдёт, если код не успеет выполниться до следующего прерывания? Насколько катастрофическими будут последствия?

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

>— Планируется ли поддержка других платформ, например, ESP8266?

Я портировал библиотеку на платы, которые
1) были у меня под рукой для тестирования
2) поддерживались инфраструктурой Ардуино (там внутри ардуиноспецифического кода, если не считать, примеры, практически нет, но структура проекта сделана под структуру библиотеки Ардуино и вообще я пока сижу на ней)

Если эту плату можно шить из среды Ардуино и она когда-нибудь у меня появится, то, возможно, портирую и на нее.

Вообще, сделать порт для новой платформы не очень сложно

В дереве проекта для каждой архитектуры выделен отдельный каталожек, в каждом из них всего два файла: timer_setup.c и timer_freq.c

В timer_setup.c основной платфомо-зависимый код — в него нужно разместить реализации методов timer_init_ISR(int timer, int prescaler, unsigned int adjustment) и timer_stop_ISR(int timer), а также назначить значения констант для таймеров _TIMERXX и делителей частоты TIMER_PRESCALER_1_Y — на разных платформах они будут разные.

Файл timer_freq.c на вид большой, но код туда добавляется механистически — нужен только калькулятор и немного терпения. В нем подбираются значения prescaler+adjustment, чтобы получить нужную частоту таймера на чипе с известной базовой тактовой частотой. Это реализация всех вызовов timer_init_ISR_XYHz

Для timer_setup.c я во всех случаях брал за основу код Servo.cpp из стандартной библиотеки Ардуино (он тоже сделан на таймерах и портируется отдельно на каждую платформу из семейства Ардуино) и доводил его, поглядывая в даташит на контроллер в раздел про таймеры.

Вот, например, для архитектуры SAM (ARM)
github.com/sadr0b0t/arduino-timer-api/tree/master/src/sam

здесь примерно видно, как шел процесс портирования:
github.com/sadr0b0t/arduino-timer-api/issues/5

здесь процесс портирования на AVR/Ардуино:
github.com/sadr0b0t/stepper_h/issues/4
В главный «поток» можно помещать не требующую точного тайминга логику взаимодействия с пользователем (прием команд через последовательный порт или по сети), отправку какой-нибудь информации по сети на сервер и т.п. В обработчик прерывания помещать коротки процедуры взаимодействия с железом — строго периодические опросы датчиков (при этом не опасаться, что значение будет пропущено из-за того, что какая-нибудь пользовательская процедура в главном цикле зависнет), программная генерация прямоугольного сигнала для управления мотором (как в примере с шаговиком).
>выполняться в строго определенное заранее запланированное время
и выполняться _ строго определенное заранее запланированное время — две большие разницы.

Здесь именно _в_ строго определенное заранее запланированное время. Т.е. процедура прерывания будет вызываться точно каждые X микросекунд независимо от того, сколько будет выполняться код обработчика по времени (конечно, если код обработчика уместится в период, если он не умещается, то поведение не определено, ну или нужно подробно изучить документацию на контроллер, чтобы посмотреть, что он делает в такой ситуации). По этой же причине замерять время выполнения Serial.print здесь не обязательно — главное, чтобы он вместился в период (в 1 секунду из примера он вмещается наверняка, для более высоких частот уже может не уместиться).

>К тому же — о смене приоритетов выполнения процедур, вызванных прерываниями, насколько я понимаю, не может идти и речи.

В описанном случае у нас получается всего 2 процедуры: это главный поток, который пополняется в главном цикле с низким приоритетом, и «прерывистый» поток обработчика прерывания с высоким приоритетом — он обрывает главный поток точно по расписанию, но не должен занимать слишком много времени за каждый вызов. Вообще, можно запустить и несколько таймеров одновременно на платах, где их несколько (передать в timer_init_ISR другой ID таймера), но что будет происходить, если их обработчики начнут пересекаться по времени, я пока не разбирался.

>И ещё вопрос — мы замыкаем цепь enable с микроконтроллера. В то же время сигнал enable обязан приходить на драйвера в случае безопасной эксплуатации устройства, в котором вращается двигатель. Не ошибка ли это? И почему En=low to enable?

Так устроен драйвер step-dir: EN=LOW двигатель вращается по сигналу, EN=HIGH — не вращается в любом случае, проверено экспериментально. Вероятно, это сделано для того, чтобы можно было подключать этот пин к концевым кнопкам на станке, которые при нажатии замыкают цепь (хотя, никто не мешает поставить кнопку, работающую на разрыв, не знаю).

вот, нашел в даташите:
> nENBL: Logic high to disable device outputs and indexer operation, logic low to enable. Internal pulldown.
www.pololu.com/product/2133
www.pololu.com/file/download/drv8825.pdf?file_id=0J590
Да, ворнинг Accessing PropTypes тоже имеется

/home/xxx/babbler-js-demo/babbler-serial-react/node_modules/fbjs/lib/warning.js:36 Warning: Accessing PropTypes via the main React package is deprecated. Use the prop-types package from npm instead.


не успеваю я за прогрессом этих библиотек, но работать он все равно не мешает.
И да, чтобы два раза не вставать, добавил в очередную версию API для удобного опроса (поллинга) выбранных свойств — «приклеенные свойства»:

https://github.com/1i7/babbler-js/releases/tag/v0.6.0

будет запрошено один раз при подключении

    babbler.stickProp("name", "name", []);
    babbler.stickProp("manufacturer", "manufacturer", []);


будут постоянно опрашиваться 2 раза в секунду (каждые 500 миллисекунд)

    babbler.stickProp("name", "name", [], 500);
    babbler.stickProp("manufacturer", "manufacturer", [], 500);


Получить значение «приклеенного» свойства:

    babbler.getStickedProp("name").val


если при последнем запросе свойства произошла ошибка:
    babbler.getStickedProp("name").err


Глобальное событие при изменении значения свойства:

babbler.on('manufacturer', function(prop_name, err, val) {
    //...
}


В случае с лампочкой нужно в прошивку добавить команду ledstatus, а потом «приклеить» ее в виде свойства. В таком случае смайлик на главном экране будет обновляться не только при нажатии кнопок, но и при ручном вызове команд ledon/ledoff из экрана отладки (уже реализовано в последней версии демо-проекта — мастер и ветка 0.6.0).
Проверил в чистой папочке

git clone https://github.com/1i7/babbler-js-demo.git
cd babbler-js-demo/babbler-serial-react
npm install
./babbler-serial.sh


Всё установилось, запустилось, подключилось.

В консольке View->Developer tools->Console появился красный ворнинг

/home/xxx/babbler-js-demo/babbler-serial-react/node_modules/fbjs/lib/warning.js:36 Warning: BabblerConnectionStatusIcon: React.createClass is deprecated and will be removed in version 16. Use plain JavaScript classes instead. If you're not yet ready to migrate, create-react-class is available on npm as a drop-in replacement.


Но он пока работе не мешает.

module.js:440 Uncaught Error: Cannot find module 'babbler-js-material-ui/lib/BabblerConnectionStatusIcon'


Это виджет с иконкой статуса и прогресса подключения, он находится внутри пакета babbler-js-material-ui:

npm install babbler-js-material-ui


но он в зависимостях демо-проекта прописан, должен был установиться вместе с первым «npm install»

Сам файл вот — на месте:
https://github.com/1i7/babbler-js-material-ui/blob/master/src/BabblerConnectionStatusIcon.js
Фирмата зеркалирует внутренний API ардуины во внешний мир (яваскрипт). По крайней мере, судя по их документации, это основная задача библиотеки. Мне этот подход не очень нравится, т.к. в этом случае бизнес-логика и алгоритмы должны полностью выполняться на клиенте, а платка только дает транзитный доступ к подключенным к ней железякам. Мне больше нравится вариант, когда робот живет относительно автономно, сам внутри себя выполняет алгоритмы, может быть, принимает какие-то решения, а внешний клиент по мере необходимости только присылает ему обобщенные задачи («иди туда», «делай то-то») и опрашивает статус («уже пришел?», «еще не сделал?»).

Information

Rating
Does not participate
Location
Россия
Registered
Activity