Pull to refresh

Comments 48

ReadData и критические секции — разве оно так будет работать (я про выделение на стеке)?

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

Потому что объект синхронизации должен с кем-то разделяться иначе его использование бессмысленно. А здесь видно что каждый вызов использует свой уникальный объект, то есть бессмыслица какая-то. Если только конечно внутри конструктора CriticalSection нет обращение к какой-то глобальной сущности.

Соглашусь, справедливости ради, скорее всего критическая здесь была добавлена чтобы обеспечить атомарности того, что в скобках (не совсем правда понятно зачем). Логично было бы вызвать его до проверки, тогда хоть какой-то смысл будет, поскольку операция ставит статус Read. Поправлю.

С кем синхронизация происходит в вашем случае?

По идее тут нужна защита для установки статуса и его проверки, чтобы, если вдруг (чисто теоретический случай) в одном потоке вызывается ReadData()и делается проверка статуса, драйвер находится не в режиме Read или Write, здесь другой поток высоко-приоритетный тоже вызывает ReadData() и тоже делает проверку и тоже определяет, что находится не в режиме Read или Write, начинает менять ресурсы (BufferSize, BufferIndex и так далее)… потом возвращаемся в другой поток, и тот также меняет их, но уже под себя. Поэтому нужно, чтобы установка статуса и его проверка были атомарными.


Это конечно так себе случай, но все сделано верно, тем более, что она особо есть не просит.

Я еще раз спрошу, с кем вы синхронизируетесь локальной переменной в функции?
Вы, в принципе, понимаете, что так в многопоточной системе работать не будет?

С другим потоком, если ему вдруг вздумается вызывать ReadData(), он недолжен снова инициировать чтение своим запросом на ReadData(), если запрос ReadData() уже инициируется другим потоком.
Да понимаю.

Если понимаете, зачем тогда писать?
Очень странный, как мне кажется, подход к обучению.

В предыдущем комментарии, "Да понимаю", следует воспринимать, как "не понял вопроса", почему не будет работать?
Я действительно не понял вопроса...

Рискну предположить что, во-первых, автор неправильно понял вопрос, а во вторых что, упрощенно говоря, класс критической секции в конструкторе (при создании объекта) выключает прерывания, а в деструкторе (при выходе объекта из области видимости) включает. Если так, то что именно будет не работать в многопоточной системе?

Так и есть… практически
в конструкторе


s = __get_interrupt_state();
__disable_interrupt();

В деструкторе


__set_interrupt_state(s);

Странно, что метод отправки запрещает приём. В отличие от передачи, данные на приём могут нам свалиться в любой момент, и рассчитывать на то, что внешний мир замрёт, как только нам захочется что-то передать, нельзя

В драйвер буфер приёма и передачи один, кроме того, регистр данных тоже один у микроконтроллера. Поэтому нет смысла принимать что — то во время передачи.
И прерывание на весь модуль UART одно… внутри идёт проверка флагов, если оба флага(на приём и передачу) стоят, то вызовутся оба обработчика одновременно.
Чтобы не зависеть от этого, сделан вот такой драйвер, преплогающий, что одновременно передачи и приёма быть не может. Зато он не зависит от аппаратуры вообще.

Регистр данных USART_DR действительно один, но про него в даташите сказано:
The Data register performs a double function (read and write) since it is composed of two registers, one for transmission (TDR) and one for reception (RDR)
The TDR register provides the parallel interface between the internal bus and the output shift
register (see Figure 1).
The RDR register provides the parallel interface between the input shift register and the
internal bus.

И про сам модуль UASRT в даташите сказано:
The universal synchronous asynchronous receiver transmitter (USART) offers a flexible means of full-duplex data exchange

так что передача и прием одновременно возможны.

Я не отрицаю факт одновременно передачи и записи… Просто в таком случае обработка будет несколько сложнее, как минимум нужны два буфера приём в и передачи.

Регистров USART_DR физически 2. Доступ к одному из них осуществляется исключительно чтением по адресу USAR_DR, доступ к другому исключительно записью. В вашей же цитате из даташита:
The Data register performs a double function (read and write) since it is composed of two registers, one for transmission (TDR) and one for reception (RDR)

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

Поэтому нет смысла принимать что — то во время передачи

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

Вы можете сказать «сейчас я буду передавать 10 байт» — и передать 10 байт синхронно в текущем потоке выполнения, но вы не можете сказать «сейчас я буду передавать 10 байт, все заткнитесь!» или «сейчас я буду ПРИНИМАТЬ 10 байт» — так как нет никаких оснований надеяться, что ваш респондент действительно прекратит передачу, даже если магическим образом узнает о вашем желании передать пакет, либо будет готов вам предоставить данные (к тому же нужной вам длины), как только вам захочется их принять — так как по факту передающая сторона с вами не синхронизирована и не в курсе ваших намерений. Представьте, что на той стороне — человек за консолью с клавиатурой и дисплеем — и он совершенно не обязан отвечать сообщениями фиксированной длины как только вы ему выплюнули на дисплей свой чанк данных.

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

ОК.


драйвер а) должен быть асинхронным на прием, б) дуплексным, в) не ожидать на приемной стороне данные фиксированной длины.

На приём он ассинхронный, и только в момент передачи приём отключён, так как буфер приёма передачи один.
Скажем так это ограничение, но оно преодалимо, путем введения второго буфера.
С дуплексом тоже самое, да здесь сделано не фулдуплекс… но такой задачи не стояло перед студентами.
Драйвер не ожидает фиксированной длины… Драйвер принимает все, что ему приходит. Он оповещает клиента о том, что пришло столько, сколько он запросил. А клиент может запрашивать и по 1 байту столько раз, сколько ему нужно. Пусть клиент и разбирается с окончанием приёма.
Про это я вообще написал, мои комментарии:


. (По хорошему так делать не надо, окончание посылки лучше делать по таймеру, но чтобы не плодить кода, я упростил до такого вот супер пупер протокола)
// вообще хорошо бы тут выйти из прерывания. Т.е. оповестить задачу, что мол
// все пришло, обработуй команду… но упростим все и обработаем команду
// в прерывании
f411, uart на ++, без dma, запрещающий прием во время передачи
а вы точно сможете научить студентов чему то толковому?

411 потому что есть на кафедре, но это не важно, он хорошо подходит подо все, можно хоть что в принципе, ++ потому что проще, надёжнее и компактнее. DMA нет потому что расскажите мне как, например, следить за временем между байтами (например в link layerе полевого протокола HART), передавать Ок ещё можно, но принимать все равно прерывание нужно для пересброса таймера после приёма каждого байта.
Запрещающий приём, уже пояснил, так как буфер приёма передачи один. Не все микроконтроллеры имеют 16 КБ ОЗУ, иногда приходится урезаться. И иметь два буфера, для того же HART, а это по 255 байт на каждый буфер, вообще не хорошо.
И да точно.

DMA нет потому что расскажите мне как, например, следить за временем между байтами (например в link layerе полевого протокола HART),

  1. Почему в статье так и не написано, что это драйвер не универсальный, а для HART?
  2. Какая необходимость следить за временем в рамках одного пакета HART? Что конкретно мешает их через DMA передавать?

на завершение передачи я подписал два класса TestObserverи SomeProtocol
А смысл? Если это одноядерный микроконтроллер (не типа ESP32), и без RTOS, то все равно однопоточно.
Если "универсально" (что не бывает), что где обкладка WriteData? Или надеемся на авось и что вызова из прерывания/другого ядру, например, этой функции не будет?

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


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


Что то в коде и намека нет на какую то работу в процессе приема отдельного байта.

Есть… намек. Это все должно делаться протоколом верхнего уровня, который использует драйвер. Можно SomeProtocol переделать как то так:


template <typename UartDriver, typename Timer>
struct SomeProtocol 
{
__forceinline static void OnTimeOut()
{
  //Сработал таймер, вызвалось OnTimeout() - считаем, что вся посылка принята 
  //обрабатываем посылку из внутреннего буфера, можем послать сигнал задаче для
  // обработки, если обрабатывать долго.
} 
__forceinline static void OnReceiveComplete(tBuffer& buffer, std::size_t length)  
  {
     // Примем по запросу 1 байта завершен, так как запрашивали 1 байт, то его и обрабатываем
      assert(length <= buffer.size()) ;
      //например, просто перекладываем байт во внутренний буфер протокола, накапливаем посылку
      internatlBuffer[index++] = buffer[0] ;
      //перезапускаем таймер
      Timer::Reset() ;       
  }  
  __forceinline static void Proceed()  
  {
    //Запрашиваем чтение по 1 байту.
    UartDriver::ReadData(1) ;
  }
...
}

Ладно… учебная задача. Решена нормально. Но только лично у меня вызвал внутренний протест слово "драйвер" и какая то заявка на универсальность.
А в результате вполне конкретная реализация с с явными ограничительными условиями (которые видны по коду).
ИМХО, универсальный "драйверов" не бывает. используется адаптера HART — одна реализация. Просто UART для вывода логов и приема команд — совсем другая реализация. Дохлый 8-битный ARV — одно. STM32 — другое… ESP32 + FreeRTOS — совсем другое.


Для студентов вполне нормальный код.


IMHO. покажите мне хоть одного программера, которые смотрит на эти картинки с квадратиками. По коду ГОРАЗДО понятнее как и что работает. Размер картинок (визуально) больше кода.

Ну он как бы универсальный… вы можете прикрутить его к любому протоколу.
Другое дело, что не задействует модули разные. Его без изменения можно прикрутить что к AVR, что к STM8, он к аппаратуре вообще не привязан, надо только, чтобы прерывания были(хотя даже и этого не надо, нужно чтобы с помощью аппаратуры можно было определить окончание посылки одного байта, выход из защелки и прием одного байта) — хотя опять же такой задачи не ставилось. Должно было работать на STM32 только.


А вот если тянуть DMA, то уже не так "универсально", потому что не знаю как определить, что байт принят во время. В целом конечно согласен, универсального ничего не бывает, но как можно больше универсально очень даже..


покажите мне хоть одного программера

На работе у меня так принято, вначале моделировать архитектуру, а потом реализовывать. Можно конечно формальные методы использовать, но таких инженеров трудно найти. Поэтому используем полуформальные с помощью UML. Можно посмотреть здесь:
ГОСТ Р МЭК 61508-7-2012
Раздел — C.3.12 UML

Поэтому используем полуформальные с помощью UML

Было модно. Было… Пробовали активно… не прижилось...


Может прижиться только, если заказчики и разработчики говорят на одном языке (UML). Или есть жесткие формальные требования на документацию (военного вида, которую все равно никто не читает).
Видел я на убогие программки тома такой документации по ГОСТ с такими красивыми картинками и с перлами, например C/C++ примеры вида


chat *ptr = "sample string";
int len = sizeof(ptr);

где len по смыслу предполагается, что длинна строки


Заказчики разные и в большинстве своем эти картинки воспринимают "ой… а что это такое". Подробный текст с описанием функционала на русском воспринимается всеми.


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


Описание на UML архитектуры (а тем более упаси боже генерация С++ кода по UML) — это не панацея и неоднозначностей в взаимопонимании не устраняет.


IMHO… UML это пиар компания Rational Rose. Довольно ограниченный и плохочеловековоспнимаемый язык.
И да… я даже курсы когда то (2003..2005) по этой хре… ни заканчивал.
Я не говорю что все плохо.
Seq диаграммы — замечательно.
ER диаграммы — сложно обойтись при проектировании БД.


А UML диаграммы классов — бр… нахлебался. не хотел бы.

Вообще UML это не документация — это моделирование как ПО будет работать. Просто получается замечательно, если в конце автоматом еще получается и иметь документацию в виде UML.


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


Но так как ГОСТ разрешает это моделирование с помощью UML, то зачем придумывать что-то еще?


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


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

Вопрос терминологии… всегда нужно определять что под чем подразумевают.
Когда я писал о "документировании" — подразумеваю описательный процесс. На бумажке ли, словами ли, языком UML ли. Не важно. Это описание чего либо. Не


Когда слышу "моделирование" — предполагаю, что это динамическая модель, позволяющая проверить работоспособность чего то. архитектуры в том числе. Демо программа или эмулятор электрической схемы, блочной логики и пр. Главное что в динамике.
Прогон моделей программ по UML описанию… у Rational Rose были такие планы. Да не сложилось как толком. Автогенерация кода по UML моделям была (убогая).

chat *ptr = "sample string";
int len = sizeof(ptr);

У вас тут еще ошибка нельзя преобразовывать const char* в char'', а у вас тут неявное преобразование идет из const char* идет…
Так нельзя:


chat *ptr = "sample string";

Вы точно прочитали мой комментарий в котором я этот кусочек привел? :) это НЕ мой кусок кода!


ЭТО пример (над которым я ржал) из огромного набора фалов документаций на программку (по ГОСТ… сертифицированную ФСБ).
Там еще и IDL файл протокола всунутый прямо в PDF файл в таком режиме, что еще и не вытащишь сразу и еще парочка перлов.
За то все по ГОСТ… формуляр отдельным документом и прочее. Аж ностальгия (как синьки из ЗИП к военной технике).


У вас тут еще ошибка нельзя преобразовывать char в const char, а у вас тут неявное преобразование идет из const char* идет…

Это всего лишь warning, да и то если не задавлен ключами компиляции. Коряво, но не смертельно.


Соль шутки в sizeof от указателя..


Я надеюсь что это автор документации постебался. Хотя глядя на некоторые другие перлы реализации протокола обмена в этой программе…
Возможно и не постебался. А просто это уровень разработчиков в гос. конторах приближенных к..

Это всего лишь warning, да и то если не задавлен ключами компиляции. Коряво, но не смертельно.

Это запрещено стандартом, поэтому на каком-то из компиляторов может быть смертельно, либо код просто не соберется.


Про sizeof — понятно, видимо в конторе не использовали статический анализатор кода...

Это запрещено стандартом, поэтому на каком-то из компиляторов может быть смертельно, либо код просто не соберется.

Попробовал (как раз сейчас код с/с++ отлаживаю. gcc кроссплатформенный).
В "С" модуле даже warning не выдал на такой код.
В "C++" warning: ISO C++ forbids converting a string constant to 'char' [-Wwrite-strings]*


Просто интересно стало как такая строка могла пролезть. Наверное на С писали.
Да еще DLL у них была собрана на VisualStidio 2003 года (это в 2015 году!). Там возможно и на C++ ошибку не выдавало.

Да, скорее всего на Си sizeof. Наверное хотели сделать так:


chat ptr[] = "sample string";
size_t len = sizeof(ptr);

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


А вот если будет процесс с Peer review, с заливанием кода, только после проверки статическим анализатором и прохождением всех юнит тестов, то наверное такого бы не было. И архитектура в этом процесс первична. Ну по-крайней мере в моем понимании процесса разработки ПО.

Действительно, как может научить чему-то студентов человек, пишущий код иначе чем вы! Да он же явно вредитель!

Естественно, что нет.
Человек критические секции расставляет "чтобы было", о какой правильности речь?

Мне ваши статьи интересны, но ваш стиль программирования на мой взгляд — ужасен.
Элементарный UART, который доступен «из коробки» и работает везде, вы смогли усложнить. При работе с UART периодически возникают ошибки на линии, где обработчики?
Вы используете С++, но с буфером применяете «memcpy». Вот для буфера класс необходим, например, кольцевого. Я мог бы понять, если вы драйвер для какой-то ОС пишите или делаете реализацию iostream, чтобы воспользоваться унаследованными методами ввода-вывода. Тяжело ваш код читать. Классы С++ призваны скрывать детали реализации и должны предоставлять программные интерфейсы. А у вас все «кишки» класса наружу (простите, imho).

Согласен в общем, справедливости ради:
Это код студентов, была задача разобраться, как вообще работает прием, как оповещать о наступлении события, как развязать аппаратуру. Считаю они справились, решение показалось интересным и рабочим.


Идея была такая, сделать разработку "любого" протокола простым, используя только несколько событий. Если считать, что UART модуль описан и все уже сделано, то задача программиста только сконцентрироваться на написании самого протокола.


Обработок ошибок действительно нет, согласен они нужны. Как минимум ошибка четности нужна практически в любом протоколе. Это вопрос техники, его можно точно также приделать и сделать событие OnParityError().


но с буфером применяете «memcpy»

Я не знаю более быстрого способа. Он же входит в стандартную библиотеку, почему не воспользоваться? Хотя в данном случае можно было вообще не копировать, как видно, для приема и передачи используется тот же самый буфер, он же передается и в WriteData()


Про буфер согласен, хорошо бы сделать класс отдельный.


А у вас все «кишки» класса наружу (простите, imho).

Не могли бы вы показать на примере, чтобы я точно определил, про что речь.


Но, признаю есть такое, но редко, иногда хочется спрятать часть методов, но лень делать френдов. Однако в целом, торчит только то, что нужно, например, в UartDriverспрятать можно разве что:
OnTransmitComplete() и OnReceive(). Но тогда нужно добавлять frenda, потому что UART прерывание должно вызвать эти методы. Как это кроме frenda обойти, я не знаю, если делать это через традиционный шаблон подписчик, то нужно было бы наследовать интерфейс опять таки с публичными виртуальными методами и они тоже торчали бы наружу. Но остальные методы то должны торчать наружу. Там всего 3 метода и все должны быть публичные.


Вообще да, для упрощения используются структуры, иногда некоторые методы автоматом улетают в паблик, признаю — нехорошо, как тут написано https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-org
используйте класс для инварианта… С этой точки зрения UartDriver должен быть class.


По поводу, что усложнили — для конечного пользователя код довольно простой. Реализовать OnReceiveComplete() OnTransmitComplete() и дополнительно когда будет OnParityError() вроде бы не сложно, все остальное делает драйвер.

Вместо std::memcpy можно использовать более типобезопасный std::copy. По скорости он не уступает, а бывает даже быстрее. Да и сырой указатель не очень красиво, C-way.

увидел ваш комментарий из параллельной статьи,
Так и тут код посылки будет простой:


const char* message = "Hello, world!";
MyUartDriver::WriteData(message, strlen(message)) ;

Дальше, как вы и сказали можно переопределить cout и перенаправить поток в UART, через драйвер и тогда можно отсылать обычным способом std::cout << "Hello world" << std::endl;
Просто можно сделать кое-что как подписаться на события и обработать их. Причем подписаться уже кодом из бизнес логики, не лазя в прерывания и ничего не зная о том, что там за регистры у микроконтроллера.


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

Поясню, что я имел ввиду про «кишки». Вы используете template<> очень часто в своём коде (из того что на Хабре встречалось). На мой взгляд даже там, где не следует. Но это уже личные предпочтения. В общем, использование template<> как раз выворачивает «кишки» и не даёт скрыть реализацию. Вместо шаблонов я бы использовал наследование и виртуальные функции. Если нужен свой обработчик — наследуем и меняем OnError() и т.д.
Замечание про «memcpy()» в том, что это C-style, а у вас везде C++ и доступ к буферу некрасиво. class CRingBuffer это скроет. imho.

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


Виртуальны функции, я ничего против них не имею, за исключением накладных расходов. Поэтому стараюсь от них избавиться, считайте у меня такая фобия :)


По поводу template не согласен, поясню почему…
Если вы хотите подписать кого-то, вы как это будете делать. Два способа, либо через конструктор, либо метод делать. Давайте посмотрим через конструктор:


В итоге ваш код как-то так будет выглядеть:


UartDriver MyDriver(MyHardwareUart, Observer1, Observer2);

class MyDriver 
{
   public:
    template<typename... Args>
     MyDriver(HarwareUart& myUart, IObservers1& observer1, IObservers2& observer2  ): Uart(myUart),  Observer1(observer1), Observer2(observer2)
    {....} ;
   private:
    HarwareUart& Uart;
    IObserver1& Observer1 ;
    IObserver2& Observer2 ;
} ;

Все тоже самое… только через конструктор. Причем у вас тут еще дополнительного кода куча, который вообще зачем нужен? В приватной секции 3 ссылки держим, надо их еще инициализировать, конструктор описываем.


Тоже самое с темплейтом будет выглядеть так


using MyDriver = UartDriver<MyHardwareUart, Observer1, Observer2>

template<typename UartModule, typename  Observer1, typename Observer2>
struct UartDriver
{
  using Uart = UartModule ;
}

Или вы имеете ввиду, что Uart — торчит наружу? то да, его надо в private убрать или вообще удалить, он вообще-то не нужен итак есть UartModule.


Другое дело, что я частенько этим пренебрегаю и они у меня попадают в public, так как структуру использую, тут согласен, надо наверное на class переходить :)


Про буфер — согласен.

Да, именно виртуальные функции и наследование. Ваш выбор template<> понятен, но на мой взгляд применение template обосновано только для контейнеров и алгоритмов. Хочу ещё раз напомнить о классе stream. Вы верно уловили мою мысль получить: uart << «Hello world!»; и т.д.
Когда-то предполагалось, что класс С++ будет задавать интерфейс в .h (.hpp) файле, а его реализация будет скрыта в .cpp файле. Один программист сможет пользоваться интерфейсом из .hpp, пока второй будет дописывать и оптимизировать реализацию в .cpp. На примере кольцевого буфера — можно задать интерфейс, сделать простеший буфер uint8_t m_buffer[256]; и начать параллельную работу. Перегрузить operator [], operator ++ и.д. На выходе будет красота!
1) Большинство ip-ядер, реализующих usart, так или иначе умеет генерировать прерывание IDLE, которое позволяет определять конец транзакции. Там, где данное прерывание не предусмотрено (или ядро кривое и не работает как надо, тот же cc1352), можно использовать таймер.
Приём и передача стандартно выполняются DMA, под которые нужно два буфера для приёма и передачи. Вендоры сейчас начали пихать нормальные матрицы коммутации для DMA, часто с пробросом эвентов от периферии типа таймеров, так что разработка становится проще и удобнее. Аналогично все работает и с другими интерфейсами типа SPI.
Передача байтами «вручную» конечно подходит для начальных этапов обучения, но не более.

2) Опять таки IAR. Моё руководство требует с меня лицензионной чистоты софта и отсутствие лишних расходов. Чтобы нормально пользоваться IAR нужно знать его особенности (т.е. нужно прочесть много документации), нужно знать особенности рантайма, нужно знать и уметь писать скрипты линкера, из которых следуют нюансы устройства startup-ов.
Зачем грузить этим студентов, если IAR им никто не купит? У них будет gcc, cmake и openocd. Я предпочту закупить оборудование, нанять ещё одного разработчика или дополнительную премию сотрудникам выплатить.
IAR Embedded Workbench как IDE очень плох, разработка на нём как минимум не доставляет удовольствия. Вы же не заставляете бедных студентов им пользоваться?

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


2) IAR уже не так страшен, есть подозрение, что последние версии компилятора используют либо что-то на основе GCC либо Glang, уж слишком похоже выглядят ворнинги и ошибки, найденные во время компиляции.


Настройки линкера действительно нужно знать. Согласен, IAR IDE не айс для нормальных разработок, поэтому я использую Clion, но в связке с компилятором IAR и OpenOcd, правда там не все работает как надо.


Cmake, OpenOcd — это хорошо, но для студентов это слишком, так как курс рассчитан обучение разработки, а не на обучение средствам для этой разработки и времени объяснять как работает сmake, как настроить OpenOcd с китайским отладчиком — совершенно нет. Надо, чтобы поставил и забыл. Можно было бы использовать Keil, но поскольку я использую IAR на работе, то и им даю IAR. Демо версия на 30 кБайт IAR — самое то, в 30 кБайт что угодно можно вместить вместе с операционной системой, графическим индикатором и каким-нибудь простеньким протоколом, типа Modbus.


Вообще, мое мнение, для обучения не важно какая IDE, они все примерно одно и то же делают, одни немного продвинутее и удобнее, другие нет.В обучении важно не зацикливаться на том, какие средства для кодирования используются (хоть в нотепаде пиши ) и сборки (можно и make в нотепаде написать самому). Важно сконцентрироваться на том, как софт разрабатывается в принципе, какие подходы есть, как микроконтроллер работает и так далее — средства, это всего лишь инструменты, которые позволяют ускорить и упростить эту разработку — работать со средствами можно и самостоятельно обучиться.


Почему используем IAR — потому что он имеет сертификат безопасности IEC 61508. А он необходим для разрабатываемого софта для устройств, работающих в надежных применениях, типа нефтеперерабатывающего завода, перегонной станции. Иначе тендер мимо кассы — и сертификат — это огромное преимущество. Потратить 5000-10000 долларов на лицензии, зато потом выиграть тендер в 1 000 000 — наверное это того стоит.

Sign up to leave a comment.

Articles