Как стать автором
Обновить
2
0
Юрий Фрадкин @Jef239

Высокоточная спутниковая навигация, АСУТП и embed

Отправить сообщение
Минимальный возраст для употребления в РФ — 0 ноль лет. Возраст для покупки — это фикция.
Погодите, а зачем вам тогда перенос сетевого стека и порт на плату, это же другие люди должны делать.

А это разница между «делать» и «планировать». Я должен понимать при выборе процессора и ОС, сколько труда уйдет у коллег на адаптацию. Особенно это важно когда речь об импортозамещении, то есть о мелкосерийных российских SoC. На тот же К1879ВЯ1Я даже FreeRTOS пришлось самим портировать. Ну и коллегам надо понимать, сколько у меня займет времени адаптация к API.

А про MBed — ну привел человек какое-то очередное API, что-то на нем написал.
MBed — это разработка компании ARM. То есть туда вложено не просто много денег, а очень много. Вот список поддерживаемых «из коробки» плат.

С одной стороны — это только COortex-M. Ни MIPS, ни ARM1176, ни даже Cortex-A. С другой стороны, если ограничится миром Cortex-M — похоже, что реально удобно.

Какая-то онлайн-среда разработки. И что дальше? Зачем все это в реальных проектах?
А все просто. Подключил плату — быстро опробовал работу устройств. Без установки тулкита, без мучений… Просто собрал на сайте и залил на вирутальную флэшку. А оно — заработало.

Ну как почти реальный пример.
У меня одна из медленных операций — это обращение матриц. Если SoC справляется за 1 миллисекунду — SoC годится. Если нет — то нет. Пожалуй что довольно дешевый способ — это купить готовую плату и залить туда тест, сделанный при помощи MBox. Это сильно дешевле, чем делать такой же проект на FreeRTOS + подъем синхронизации и питания своими руками.

То есть MBed вполне может взлететь. Ну пример как взлетела Windows, когда в неё вложили очень много денег. Если помните — конкуренты Windows 1.0 были получше неё.

А нормальный тулкит у MBed тоже есть. Куда уж без него.
Ну вот как раз для написания приложений под айфон (а ещё пуще под Андроид) эмулятор явно нужен. Не собирать же весь зоопарк совместимых устройств и вариантов IOS?

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

Не про приложеньки под FreeRTOS же рассказывать в самом деле))
А почему бы не рассказать про приложеньки под Embox? Сколько труда нужно, чтобы поднять на ней сетевой стек на какой-нибудь K1879ВЯ1Я? А сколько — чтобы портировать её туда?

Вот человек про MBed рассказал. И код простой, и компилится прямо на сайте.

А про FreeRTOS рассказано уже очень много
А какая разница, как они устроены? Тот, кто пишет порт RTOS — тот в этом разбирается. А все остальные — просто используют готовый порт.

У меня полсотни экземпляров на разных ARM работают под FreeRTOS. Но в тонкостях vector table я не разбираюсь. Это прерогатива ОС, а не моя. Даже для написания драйвера мне это не нужно — дергается готовая процедура контроллера прерываний и всё.
Ну по вашему примеру видно, насколько «точно» имитируется UART. На реальном UART вы таким образом выведите максимум 17 байт (размер FIFO = сдвиговый регистр). Иными словами, то, что работает на имитаторе — не факт, что будет работать на железе.

При написании ОС — возможно, что имитатор удобней железа. Но в реальных (не учебных) проектах всегда есть железо и JTAG/SWD.

А если железо такое новое, что ещё и семплов нету — ну так его и в эмуляторе не будет.
Тогда причем тут Cortex-M3??? Тем более что порты у него бывают самые разные. Вы написали, как вывести hello world на эмуляторе.

Но я не понимаю, зачем нужен эмулятор. Математику проще отладить под windows или linux, а специфическое общение с железом на эмуляторе не отладить.

Такое впечатление, что статья писалась ради упоминания Embox.

P.S. Эмулятор сильно плох тем, что на нем сложно сделать регрессионные тесты. Портировав код под винду мы заменяем устройства на файлы и имеем стабильное прохождение тестов. На эмуляторе это сложно. Особенности железа он тоже адекватно не эмулирует.
Вы хотя бы на имитаторе проверили работоспособность? Просто у меня есть сомнения, что этот код даже на имитаторе будет работать. И уж точно — не пойдет на железе.

Смените тогда заголовок, укажите, что пишите для имитатора, а не для железа.
Где включение питания UART? Где включение его тактирования? Где установка делителей?

Ну и самый смак — отправка в DR без ожидания готовности.

Ну вот один из работающих вариантов для 16550
       int cnt = ((LSR & 0x20) != 0) ? OUT_FIFO_16550_SIZE : 0;
       if ((LSR & 0x40) != 0) /* TEMP - пустота сдвигового регистра */
          cnt++;
       for (int i = 0; i < cnt; i++) {
           uint8_t btOut;
           if (xQueueReceiveFromISR (port->data->transmit_queue, &btOut, &woken)) {
              port->regs->TFR = btOut;
              sent++;
           } else
              break;
       };

Да не, 16 байт (размер FIFO) пройдет, если FIFO по умолчанию включен. Может и 17 — FIFO+сдвиговый регистр.

Но сам код — жуть.
Можно пруф на дуплексную LoRa? Какой конкретно трансивер умеет одновременно делать прием и передачу?
Нет, неблокирующий синхронный вариант. Отличие от вики — нулевой таймаут.

Ну вот пример:
while((ch=getc(fp))!=EOF) {
    printf("%c", ch);
  }

Только надо понимать, что EOF — это всего лишь временный таймаут. Сама функция getc берет данные из буфера и лишь при исчерпании буфера вычитывает данные из драйвера большим куском. Вот ещё одно объяснение.

Ну а вместо printf — конечный автомат на разбор протокола и вызовы обработчиков в зависимости от типа пришедшего пакета.

На выводе ещё проще: write копирует данные в буфер, а потом асинхронно они пересылаются на устройство. То есть это как обычный printf на экран или в файл.

Это все удобно, когда с устройства валит поток данных. То есть как раз для GPS, когда на каждом измерении летит десяток пакетов.
Ой, а как вы с TCP/IP работаете? Один из самых простых способов же.

Суть в том, что бывает передача дейтаграмм (как в UDP) и передача потока (как в TCP). И для потока неблокирующий ввод-вывод — самое то. А UART — это 99.99% — передача потока. То есть делить на пакеты нужно самому, и длина пакетов — переменная.

Исключение сейчас вспоминается одно - MODBUS-RTU, его можно и как дейтаграммы принимать и как поток.

Другой момент, что на всяких иных устройствах — больше дейтаграммы. Ну так я только про UART и его удлинители говорю.
LoRa — полудуплексная Ничего, со временем и дуплексными системами займетесь. Тогда все сами и поймете.

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

Давайте поговорим через пару лет, когда вы сами набьете себе шишек.
То есть ответ вы не придумали. Ну ОК, бывает. Попробуете написать систему с callback — сами с этой проблемой столкнетесь.

Вот только сильно боюсь, что вы из тех программистов, которые гонки считают «таинственным сбоем». Те, кто умеет с гонками бороться — проблемы видит заранее.
Странно, что вы не рассматриваете неблокирующий ввод-вывод без ожидания.
Понятно, что на коллбэках писать сложнее, это другая архитектура. Но они сильно гибче.
Ну что же, жду от вас рассказа, зачем вам эта гибкость. Ну да, ускорение на пару мс там, где оно не нужно (на ком-портовых скоростях). А что ещё?

1) ожидание блокирует данную задачу, коллбэк — нет

Такое впечатление, что про non-blocking IO вы не слышали. Калбэк, между прочим, означает блокировку буфера, в который вы читаете. А иногда — и блокировку буфера на отправку.

2) в ожидании не передаются параметры, в коллбэке — сколько угодно
В неблокирующем вводе-выводе у нас есть контекст. Мы не ограничены парой параметров калбэка.

3) таймауты в ожидании реализуются существенно менее изящно, чем в коллбэках

В каллбэках с таймаутами все плохо. Запустили операцию, выставили таймаут — а потом условия изменились и надо таймаут изменить. Например: укоротить. И начинаются пляски с бубном.

В неблокирующем вводе-выводе тайматуы динамические. Мы их можем менять в любой момент.
Я вас немного разочарую: когда у вас после каждой «асинхронности» стоит while(...), в котором вы конца этой асинхронности ждёте, то это не асинхронность
Ага, понятно. Вы привыкли работать в полудуплексе — дали команду, потом ждете ответ. А мы работаем в полном дуплексе. Принимаем поток байт, формируем из него поток пакетов, а параллельно — шлем запросы и уставки. Послали команду — ставим таймер. Пришел ответ — таймер обнуляем. Изредка проверяем таймеры, не пришел ответ — значит перепосылка.

Часто даже проще — раз в секунду смотрим табличку: какие команды ешё не отработали? Ах вот эти? Ну значит их посылаем. А на более высоком уровне для выдачи команд просто корректируем табличку.

Соответственно никакого while на чтение у нас нет. И на запись — нет.

Есть общий цикл задачи. Байт пришел? В конечный автомат его. Конечный автомат собрал пакет — ок, обрабатываем. Что там в таблице команд? Кого-то послать нужно? ОК, посылаем. Ничего не нужно — спасибо, отдаем квант другой задаче. Причем это может быть как одна задача, так и две (на прием и на отсылку).

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

У меня начинает закрадываться подозрение, что вы «асинхронностью» называете тот факт, что у вас там UART не на побитовом ногодрыге сделан, а аж на целом однобайтовом USARTx_DR
Ой, прямо в детство вернулся. 35 лет назад я тоже так думал. Асинхронность означает nonblocking IO в терминах беркли-сокета. То есть ожидание на операциях ввода-вывода — нулевое. Отправили пакет — и все, забыли о нем. Можем секунду не читать — байты не потеряются.

В windows и linux это означает три уровня буферов: буфера программы, буфера ОС и буфера драйвера (для DMA). Во FreeRTOS уровней два: основные буфера (две очереди FreeRTOS) и буфера драйвера (для DMA).

Ориентация на очереди FreeRTOS позволила нам на одной из систем реализовать UART на ПЛИС с обменом с SOC по SPI без изменения кода. Я со свой стороны ровно так же общаюсь с очередями FreeRTOS, а что драйвер совсем иной — мне не важно.

Таймауты там всякие, например, — нет, не слышали?
Да, да таймауты. Вы все-таки попытайтесь на вопрос ответить. Как вы с вашими callback будете избегать гонки по таймауту? Истек таймаут, callback ещё не пришел, что дальше? Как вы избежите callback в момент обработки таймаута? Так что вопрос открыт:

Как будете избегать гонки?

Или вы из «современных» программистов, которые гонки просто считают «таинственным сбоем»?
Такое впечатление, что я со школьником беседую, а не с преподавателем ВУЗа. Ну ладно, придется делать ликбез на уровне школьника.

UART отличается тем, что в нем бывает пропадание байт. Overrun, Frame Error и так далее. Поэтому всегда есть протокол с передачи с делением на пакеты и продольной контрольной суммой по пакетам. 99% этих протоколов — байтовые, оставшийся процент — битовые, вроде RTCM 2.2, оптимизированные для передачи по зашумленному радиоканалу.

Соответственно дальше всегда стоит конечный автомат, который и разбирает поток байт (или битов).

В качестве домашнего задания: попробуйте привести пример протокола (из числа ходящих по UART) с единицей разбора больше байта. Ну и подумайте, как быстро восстановится синхронизация в таком протоколе, если байт пропадет?

Да, такие у нас требования. С одной стороны мы хотим от железа 1 сбой на 10^7 байт, с другой стороны — умеем выживать при сбое на 10^2 — 10^3 байт.

А что CHAN_Getc — дешевая операция ибо читает из внутреннего буфера программы, это очевидно.

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

Запрос не дошел — значит повторили. Все равно 99% случаев сбоев передачи — проблема за UART, например, в радиодлинителе. Поэтому нам даже не важно, сбой на передаче запроса или на чтении ответа устройства. Все равно в итоге — перезапрос через таймаут.

В редких случаях (смена скорости порта или ресет приемника) — ждем опустошения буфера. Это где-то от пары раз в сутки до раза в месяц. Да, теоретически тут был бы полезен callback.

Но… практически ни в Windows, ни в POSIX нету callbck на то, что последний байт ушел из регистров UART в линию. Даже аппаратные порты — далеко не всегда имеют нужное прерывание, обычно прерывание идет на опустошение FIFO, при этом 1 байт ещё передается в линию. Поэтому способ с callback — не универсален, он работает лишь на отдельных портах.

Вы лучше расскажите, что вы собираетесь делать по callback? Ну вот скажем запустили чтение на 10 байт, а пришло 9 (сбой на линии или в логике приемника). Соответственно callback не вызвался. Что дальше? Как будете избегать гонки?

Информация

В рейтинге
Не участвует
Откуда
Санкт-Петербург, Санкт-Петербург и область, Россия
Дата рождения
Зарегистрирован
Активность