All streams
Search
Write a publication
Pull to refresh
40
0
Павлов Николай Александрович @ZyXI

Инженер

Send message

Я как‐то подвигал на столе новый крейт от National Instruments, он вполне себе оставил жирные (в смысле толщины) следы (старые вроде не оставляли). То ли потому что новый, то ли потому что именно этот тяжелее старых, то ли потому что старые я на новых белых столах не двигал (точнее, не двигал стоящими на ножках). Хотя, казалось бы, за ту цену, что National Instruments просит за своё железо, могли бы и поставить ножки, не оставляющие следов.

С переходником USB⇔UART. COM порт на плате тоже есть, но у переходника, как и у USB, имеется одно преимущество: оба предоставляют питание. USB так и не сделал, поскольку быть первопроходцем нет желания, готового шаблона «как сделать виртуальный COM порт на Rust и STM32» я до сих пор не видел, а вариант с переходником уже нормально работает.

Относительно первого пина: я в datasheet’е вижу пачку пинов с названием VSS_x. Практика показывает, что все земли внутри обычно объединены, а всё остальное (кроме питаний, но это когда как — они могут и к разным доменам относится) — нет, поэтому когда ножка земли не одна (она не одна), и когда ножки земли расположены несимметрично (а сторона с ножками 17…32 — единственная, у которой есть по земле на второй с начала и второй с конца ножке), то тогда можно понять, где первый пин, прозванивая несимметричные земли.


Ну и ещё, google «stm LQFP64 determine first pin» на первой же странице выдаёт мне ссылку https://electronics.stackexchange.com/questions/123703/which-of-these-marks-signifies-pin-1-on-the-stm32f-lqfp64, где есть как минимум три полезных совета:


  1. Смотреть на меньшую точку.
  2. Если повернуть текст так, чтобы надпись читалась, то первый пин слева‐снизу (кстати, у вас написано, что справа‐сверху, так что кто‐то ошибается; скорее всего, вы, т.к. в datasheet нарисовано то же самое).
  3. Если прозвонить тестером земли, то всё станет понятно.

Что значит «лучше»? Я как‐то писал прошивку как раз для общения компьютера с Windows с «чем угодно» через CAN, в качестве переходника CANUART (в планах как‐нибудь сделать CANUSB, но до этого пока не дошло). У меня прошивка создавалась в Neovim на языке Rust, ответная часть на LabVIEW. Neovim — потому что *vim мне нравится, а привыкать к какой‐либо IDE, даже если бы я имел такое желание, в нашей компании не имеет смысла. Rust — потому что интересно изучить. Более того, данная прошивка вообще была первым моим проектом на Rust (и даже не первым сколько‐либо серьёзным, а вообще первым). LabVIEW — потому что почти всю автоматизацию в компании мы пишем на нём.


Замечу, что даже несмотря на высокую незрелость экосистемы Rust для микроконтроллеров (прошивка писалась вроде где‐то полтора года назад) и отсутствие опыта в написании программ на Rust, основные проблемы с прошивкой были только в плане ошибок при использовании регистров специального назначения — в общем, то, что вы получите, если будете работать без HAL. Уже тогда присутствовали крейты для создания асинхронных прошивок для stm32 на Rust, включавшие в себя reentrant очередь, раскладывание нужных переходов по векторам прерываний, критические секции, генерацию условно безопасных API низкого уровня для доступа к регистрам специального назначения по файлам описания микроконтроллеров. Сейчас со всем должно быть получше — как минимум, насколько мне известно, более не требуется ночная сборка компилятора Rust.

Так длина линии ограничена не тем, как далеко проходит сигнал, а тем, как быстро. Если мне не изменяет память, по оптоволокну сигнал идёт медленнее, чем по меди — если выбирать её с учётом коэффициента укорочения.

С --follow её легко можно не получить вообще никогда. LabVIEW, к примеру, хранит исходный код в бинарных файлах, к тому же сжатых и по‐умолчанию содержащих результаты компиляции (хотя те, кто всерьёз использует VCS быстро учится настраивать его так, чтобы не хранил) и кучу других вещей, которые в других языках отсутствуют, не хранятся вместе с исходным кодом или вообще относятся исключительно к IDE (при том из всего разнообразия можно настройкой исключить только результаты компиляции). При этом один файл — одна функция, так что переименования происходят куда как чаще. Как вы думаете, какая максимальная разница между переименованным и исходным файлом? Мне как‐то доводилось видеть 0% по мнению mercurial. Схожесть в 50% после переименования и ещё какого‐то связанного с этим изменения (обычно — такого, без которого обойтись нельзя) уже можно считать хорошей.

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

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


И ещё, я слышал, что у фирменных паяльников Metcal с инерционностью разобрались по‐другому: если вы индукцией греете жала до точки Кюри (до точки, когда дальше греть индукцией невозможно), то вам не нужен датчик температуры, а мощность можно сделать такую, что жало остывать не будет. Нужно только потратить кучу денег на разработку сплавов, которые имеют точку Кюри в нужном месте.


У нас на работе, кстати, все паяльные станции индукционные, от китайской фирмы Quick. Себе я покупал Yihua 900H, которая, как оказалось, совместима как с жалами, так и с паяльниками Quick, хотя я подбирал паяльник на замену из расчётов «коннектор должен выглядеть так же», «люди говорят, что можно использовать жала Quick с паяльником Yihua с небольшой доработкой, так что если паяльник не подойдёт — всегда можно просто использовать эту доработку» и «жала Yihua сложно найти, особенно мой любимый большой топорик».


Времена на разогрев там порядка десяти секунд, у дешёвых Quick раза в три—пять больше.

Staging area для работы нужно всего две возможности: фиксация состояния не с файловой системы¹ и перехват команды фиксации изменений. Если бы кому‐то staging area была действительно нужна в mercurial, такое дополнение давно бы сделали.


Теги, создающие коммит — это однозначно сознательный выбор разработчиков. Добавить теги, не создающие коммит, было бы сложнее, чем добавить закладки — они отличаются только тем, что hg commit двигает закладку, но не двигал бы тег (и в git отличия на том по сути и заканчиваются, формат хранения у них одинаковый (если не трогать annotated теги)). Тем не менее, закладки есть, а тегов хотя бы на их основе (думаю легко было бы сделать так, чтобы, к примеру, переход на закладку tag-{name} не активировал бы её) — нет.


Странно, что никто не вспомнил частичные (без истории и/или без каких‐то файлов) клоны — их вроде сами разработчики mercurial хотят (когда‐нибудь) добавить, но пока не сделали.


¹ С точки зрения пользователя — последний раз, когда я смотрел код какого‐то hg *record он просто переписывал файлы на диске, фиксировал изменения и переписывал их обратно. На этой основе я делал свой AuRecord (часть дополнения aurum для Vim).

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


В языках со статической типизацией компилятор обычно знает, какие именно функции будут вызваны в любом месте программы (или хотя бы знает, что здесь происходит вызов функции, адрес которой в runtime можно найти вот в этом месте памяти), поэтому ему совершенно не нужно тащить в runtime набор ссылок на функции, относящиеся к данному типу. Т.к. набор ссылок на функции для работы программы не нужен, а вот выкидывать неиспользованные функции как раз‐таки часто нужно во имя эффективности, то в языках со статической типизацией никто никаких Type не делает. Это не то, что совсем невозможно, просто противоречит концепции использования языка и очень сложно технически (особенно если там есть generic’и).

А чем hg bookmark не замена git branch — там еще не сделали управление правами?

Где? Насколько я понимаю, это вопрос к реализации сервера. И ни у git, ни у mercurial в стандартной поставке хорошего сервера нет, максимум что‐то для быстрого расшаривания в доверенной сети.


А относительно практически любых технических преимуществ git я напомню, что в mercurial вы можете взять и написать расширение. Что закладки, что фазы, что изменение истории, что поддержка больших файлов — всё это изначально жило (а иногда и сейчас живёт) в виде расширений. А в git ничего заранее непредусмотренного нельзя добавить — я как‐то не припомню, чтобы к коммиту в git хоть кто‐то добавил хоть какие‐то метаданные, которые хотя бы не мозолили глаза пользователю, не говоря уже о том, чтобы не участвовали в создании хэша. Для тех же фаз это обязательное требование.

Вообще‐то, итератор по bytes возвращает int. Может, где‐то внутри и есть тип byte, но пока что я вижу, что type(next(iter(bytes(b'abc')))) is int, а обращение к byte вызывает NameError. А в Python2 вообще bytes is str.

ISA, кстати, до сих пор используется — в виде одноплатных компьютеров с разъёмом PC/104, предназначенных для использования в промышленности. Т.к. их отправляют в космос и не только, то мне уже несколько таких пришлось запускать.

Вам нужно указать Arduino IDE, чтобы она использовала стандарт C99. Если она такого не умеет (или если вы вставили C’шный код в C++ файл), то замените эти длинные массивы в #define’ах на нормальные строковые литералы: "\007" и "\012\015" (или более читаемые"\a" и "\r\n", если вспомнить, что означают эти цифры): не знаю, зачем автор вообще сделал там C99 массивы.

«Проект для Atmel Studio» — это вообще‐то набор самых обычных исходных файлов плюс настройки studio. Если у вас нет studio просто игнорируйте настройки и создайте Makefile/CMakeLists.txt/… для своей среды. Исходный код там есть, он не зашифрован и даже не обёрнут в XML (и я не помню, чтобы так делала вообще какая‐либо IDE). Конкретно в этом случае — файл Tiny_UART/main.c.

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

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

Вот здесь есть более подробное описание: https://habr.com/company/smart_soft/blog/234239/.

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

При чём тут она? В слове «stacktrace» «stack» присутствует не просто так, и он на запись не лочится никогда.


Когда вы вызываете функцию в стек сохраняется указатель на следующую инструкцию (адрес возврата), указатель на стек для восстановления положения в стеке «как было» при возврате из функции (указатель на начало кадра) плюс некоторые другие данные (регистры, иногда безопасный залоченный регион для предотвращения (не слишком больших) выходов за границы массивов на стеке, иногда маркер для создания спекулятивного stacktrace на случай, если стек всё же был повреждён). Когда происходит какая‐то проблема и нужен stacktrace вы обходите адреса возврата для понимания, где что было вызвано, в чём вам помогают в первую очередь адреса начала кадра — т.е. знание, какая часть стека относится к какой функции.


Теперь вопрос — что будет, если эти адреса повреждены? Даже если в стеке есть маркеры (которых обычно нет — я слышал, что они добавляются какой‐то программой, используемой для отладки, но сейчас не вспомню, какой), то это будет спекуляцией — ничто не запрещает маркеру присутствовать в переменных, размещённых на стеке. А если их там нет, то в стеке почти наверняка будут и указатели на стек, и указатели на код, не имеющие никакого отношения к адресам начала кадра и возврата.

Как ни странно, не только пройдёт проверку, но даже и не потребует явным образом импортировать Foo: следующий код выводит HERE.


mod foo {
    pub trait Foo {
        fn foo(&self);
    }
}

mod foobar {
    use crate::foo::Foo;
    pub trait FooBar : Foo {
        fn foobar(&self) {
            self.foo();
        }
    }
}

mod strct {
    use crate::foo::Foo;
    use crate::foobar::FooBar;

    pub struct Struct {}

    impl Foo for Struct {
        fn foo(&self) {
            println!("HERE");
        }
    }

    impl FooBar for Struct {}
}

mod runfoo {
    use crate::foobar::FooBar;

    pub fn run_foo(s : &FooBar) {
        s.foo();
    }
}

fn main() {
    let s = strct::Struct {};
    runfoo::run_foo(&s);
}

Вот если у вас много foo, то извольте и импортировать, и указать явно, какой вы хотите (playground):


mod foo {
    pub trait Foo {
        fn foo(&self);
    }
}

mod foo2 {
    pub trait Foo {
        fn foo(&self);
    }
}

mod foobar {
    use crate::foo::Foo;
    use crate::foo2::Foo as Foo2;

    pub trait FooBar : Foo {
        fn foobar(&self) {
            self.foo();
        }
    }

    pub trait FooAllBar : Foo + Foo2 {
        fn foobar(&self) {
            Foo2::foo(self);
        }
    }
}

mod strct {
    use crate::foo::Foo;
    use crate::foo2::Foo as Foo2;
    use crate::foobar::FooBar;
    use crate::foobar::FooAllBar;

    pub struct Struct {}

    impl Foo for Struct {
        fn foo(&self) {
            println!("HERE");
        }
    }

    impl Foo2 for Struct {
        fn foo(&self) {
            println!("THERE");
        }
    }

    impl FooBar for Struct {}
    impl FooAllBar for Struct {}
}

mod runfoo {
    use crate::foobar::FooBar;

    pub fn run_foo(s : &FooBar) {
        s.foo();
    }
}

mod runfooall {
    use crate::foobar::FooAllBar;
    use crate::foo2::Foo;

    pub fn run_foo(s : &FooAllBar) {
        Foo::foo(s);
    }
}

fn main() {
    let s = strct::Struct {};
    runfoo::run_foo(&s);
    runfooall::run_foo(&s);
}

Писал на Rust несколько проектов:


  • Самый первый проект на Rust: преобразователь UART в CAN на основе микроконтроллера STM32. До этого такие проекты у нас писались на C, хотя я лично их не писал.


    Из плюсов:


    • Уже были отличные библиотеки (cortex-m*) и несколько структур данных для асинхронного программирования (вида «получили в прерывании байты, записали в очередь, в основном цикле обработали, записали ответ в очередь на отправку, начали отправку, уснули»). Найти готовую реализацию очереди для микроконтроллеров на C можно. Подключить её нормально в качестве зависимости — нет.
    • Написать тесты, запускающиеся на хосте, с Rust легче.
    • Отладка с gdb уже работает.

    Из минусов:


    • Экосистема только развивающаяся. Многое из готового не найти. Значительная часть проектов висели на одном гуру (кстати, часть из них смотрю перехали в аккаунт организации https://github.com/rust-embedded). Там, где я реализовывал собственно UART и CAN было несколько больше unsafe кода, чем нужно, прямые записи в регистры. Пытаться создавать безопасные абстракции, соответствующие духу языка, я не стал — мало времени и, к тому же, я отлично понимаю, что в первом проекте «как надо» не получится ни за что.
    • Непривычные макросы. Я не могу просто взять и «как в C» обратиться к переменной, объявленной за пределами макроса, без передачи её каким‐либо явным образом. Здесь, с одной стороны, всегда видно, что код макроса зависит от переменной. С другой стороны, вызовы макросов становятся длиннее.
    • Для того, чтобы установить бит в РСН на Rust нужно написать конструкцию вида apb1.enr().modify(|_, w| w.i2c1().set_bit());. Как видите, здесь пять(!) вызовов функций (.enr(), .modify(), вызов лямбды (внутри .modify()), .i2c1(), .set_bit()). Компилятор это каким‐то образом оптимизирует во что‐то адекватное, но, во‐первых, выглядит для человека, писавшего на C, это дико. Во‐вторых, если вы попросите его не оптимизировать, то он не оптимизирует — при отладке такое сильно замедляет программу.

  • Писал программу, проводящую функциональный контроль микросхемы. Раньше писал такое на LabVIEW. Точнее не всю программу, а только часть, собственно анализирующую результаты работы микросхемы.


    Из плюсов:


    • Rust может компилироваться в DLL, которую можно вызвать из LabVIEW.
    • На Rust легко написать простой tcp сервер.
    • На Rust можно быстрее писать сложные программы, чем на LabVIEW (кто бы сомневался).
    • Опять же, тесты создать проще.
    • Легко обойтись без аллокаций памяти в куче.
    • Rust обрабатывает данные быстрее LabVIEW.

    Из минусов:


    • Rust при использовании через DLL роняет LabVIEW. И вообще, этот интерфейс в LabVIEW медленнее, чем я думал (но это, конечно, не проблема Rust). Падения я сначала записывал на паники вроде тех, что происходят при переполнении в арифметических операциях, но исправление замеченных ошибок и изменение операторов сложения/вычитания/умножения на подходящие по смыслу функции вроде saturating_add/wrapping_add/… ничего не дали. После переписывание DLL в tcp сервер LabVIEW перестал падать, а сервер падать не стал.
    • Паники сложно перехватывать. Насколько я понял, перехват не всегда работает. Мой код перехвата, кажется, так ни разу и не заработал — хотя, возможно, паник просто не было, а падения вызывались чем‐то ещё.
    • Немного повоевал с borrow checker когда решил вести в программе отдельный журнал, но при этом получать имя журнала из LabVIEW. А до получения имени — отправлять данные в stderr.

  • Писал программу анализа бинарных логов, созданных LabVIEW. До этого использовалась программа на Python.


    Из плюсов:


    • С поправкой на чтение документации (только третий проект, я ещё не всё запомнил!) пишется примерно так же быстро, как и на Python. В т.ч. можно так же легко создать цепочку вида файл→буфер (правда на Python буфер включён по‐умолчанию и должен явно отключаться, а тут он является отдельной сущностью и должен быть прописан явно)→потоковый распаковщик (xz или gz, по расширению) и передать её в качестве объекта std::io::Read в процедуры анализа.
    • При этом работает на два порядка быстрее, при том что ни оптимизацией программы на Python, ни оптимизацией программы на Rust я не занимался.
    • Компилятор вылавливал те ошибки, которые на Python я бы не заметил до запуска анализа журнала с определёнными данными.

    Из минусов:


    • Вот здесь уже пришлось много воевать с компилятором за то, чтобы вернуть trait object (тот самый std::io::Read) из функции и потом передать в другую функцию. Основная проблема: не сразу понял, что нужно делать, когда компилятор мне говорит, что он не знает размер данных и по этому поводу не хочет ничего никуда передавать.
    • Лапша парсера и сериализатора результатов в человекочитаемую форму на Python всё же короче.

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity