PIC32 vs dsPIC vs ARM vs AVR, имеет ли значение архитектура, когда мы все равно программируем на языке C? [закрыто]

10

В настоящее время мы используем 32-разрядный микроконтроллер PIC32. Он отлично работает для наших нужд, но мы также изучаем другие микроконтроллеры, которые могут подойти нам лучше + у нас есть другие проекты, для которых мы выбираем MCU. Для этой цели мы выбрали микроконтроллер SAM DA на базе ARM, который является тем же 32-разрядным, но основан на ARM (более популярен, чем PIC32 - в отрасли).

Теперь для PIC32 мы используем MPLAB, но для ARM cortex-M0 мы будем использовать Atmel Studio. Мы будем использовать C-язык на обеих платформах. Меня беспокоит то, что мы будем использовать два 32-битных микроконтроллера (от одной компании), но с разными архитектурами. Это потребует от нас изучения двух разных устройств и увеличит нашу «кривую обучения» + время доставки. Но с другой стороны, я также думаю, что, поскольку в обоих случаях мы будем использовать язык C, кривая обучения для ARM не должна быть услышана, и стоит также изучить этот процессор.

Мой главный вопрос: насколько велика разница в архитектуре, когда мы программируем на C-Language, поскольку она обеспечивает абстракцию внутренних частей микроконтроллера. И каковы основные различия в MPLAP и Atmel Studio , если учесть программирование на C-языке.

инженер
источник
2
Если с PIC32 все работает, то какой смысл переключаться? Даже если код полностью перенесен (этого не произойдет), все равно есть новая цепочка инструментов и IDE, к которой нужно привыкнуть. В чем смысл? Переключаться по религиозным причинам или быть «основанным на ARM» (или чем-то еще) глупо. У вас должна быть веская причина, но вы нам ее не показали.
Олин Латроп
Я не спрашивал про переключение. Я говорил о выборе другой архитектуры для других проектов, так как мы работаем над несколькими проектами + в нашей существующей конструкции есть возможности для улучшения. Главное было о кривой обучения и проблемах при работе с двумя разными архитектурами одновременно.
инженер
Одна вещь, которую я обнаружил, что Atmel Studio обеспечивает превосходную синхронизацию, чем видео
инженер

Ответы:

20

Это довольно самоуверенная тема. Я могу говорить за себя (AVR, ARM, MSP430).

Разница 1 (самая значительная) заключается в периферии. Каждый из MCU имеет похожие UART, SPI, таймеры и т. Д. - просто имена регистров и биты различны. Большую часть времени это была основная проблема, с которой мне приходилось сталкиваться при перемещении кода между чипами. Решение: напишите свои драйверы с общим API, чтобы ваше приложение могло быть переносимым.

Разница 2 в архитектуре памяти. Если вы хотите поместить константы во флэш-память на AVR, вы должны использовать специальные атрибуты и специальные функции для их чтения. В мире ARM вы просто разыменовываете указатель, потому что существует единое адресное пространство (я не знаю, как его обрабатывают маленькие PIC, но предположил бы, что они ближе к AVR).

Разница 3 заключается в объявлении и обработке прерываний. avr-gccесть ISR()макрос. ARM имеет только имя функции (например, someUART_Handler () - если вы используете заголовки CMSIS и код запуска). Векторы прерываний ARM можно размещать в любом месте (включая ОЗУ) и изменять во время выполнения (очень удобно, например, если у вас есть два разных протокола UART, которые можно переключать). AVR имеет только возможность использовать векторы либо в «основной флэш-памяти», либо в «разделе загрузчика» (поэтому, если вы хотите обрабатывать прерывания по-другому, вы должны использовать ifоператор).

Разница 4 - режимы сна и управления мощностью. Если вам требуется минимальное энергопотребление, вам необходимо использовать все функции MCU. Это может сильно отличаться между MCU - некоторые имеют более грубые режимы энергосбережения, некоторые могут включать / отключать отдельные периферийные устройства. Некоторые MCU имеют регулируемые регуляторы, так что вы можете использовать их с более низким напряжением на более медленной скорости и т. Д. Я не вижу простого способа добиться такой же эффективности на MCU (скажем) с 3 глобальными режимами питания, а другой с 7 режимами питания и индивидуальное периферийное управление часами.

Единственная самая важная вещь при заботе о переносимости - это четко разделить ваш код на аппаратно-зависимые (драйверы) и аппаратно-независимые (приложения) части. Вы можете разработать и протестировать последний на обычном ПК с имитирующим драйвером (например, консоль вместо UART). Это спасло меня много раз, так как 90% кода приложения было завершено до того, как прототипное оборудование вышло из печи оплавления :)

На мой взгляд, хорошая вещь в ARM - это «монокультура» - доступность многих компиляторов (gcc, Keil, IAR ... и многие другие), множество бесплатных и официально поддерживаемых IDE (по крайней мере, для NXP, STM32, Silicon Labs, Nordic), множество инструментов отладки (SEGGER - особенно Ozone, ULINK, OpenOCD ...) и многие производители чипов (я даже не буду их называть). PIC32 в основном ограничен микрочипами (но это имеет значение только если вам не нравятся их инструменты.

Когда дело доходит до кода Си. Это на 99% то же самое, ifутверждение то же самое, цикл работает таким же образом. Однако вы должны заботиться о родном размере слова. Например, forцикл на AVR самый быстрый, если вы используете его uint8_tдля счетчика, а на ARM uint32_tсамый быстрый тип (или int32_t). ARM должен будет каждый раз проверять 8-битное переполнение, если вы используете меньший тип.

Выбор MCU и / или поставщика в целом в основном зависит от политики и логистики (если у вас нет четких технических ограничений, например: высокая температура - используйте MSP430 или Vorago). Даже если приложение может работать на чем угодно, и только 5% кода (драйверов) должны разрабатываться и поддерживаться в течение всего срока службы продукта - это все еще дополнительные расходы для компании. Во всех местах, где я работал, был любимый продавец и линейка MCU (например, «выбирайте любой Kinetis, если хотите, если нет очень веской причины выбирать что-то другое»). Также полезно, если у вас есть другие люди, которые обращаются за помощью, поэтому в качестве менеджера я бы избегал иметь отдел разработки из 5 человек, где все использовали совершенно разные чипы.

фило
источник
3
«AVR является самым быстрым, если вы используете uint8_t для счетчика, в то время как в ARM uint32_t является самым быстрым типом (или int32_t). ARM должен будет каждый раз проверять 8-битное переполнение, если вы используете меньший тип ». Вы можете использовать uint_fast8_t, если вам нужно только 8 бит.
Майкл
@Michael - уверен, что вы можете использовать типы _fast, но вы не можете рассчитывать на поведение переполнения. В моем gcc stdint.h у меня есть «typedef unsigned int uint_fast8_t», который в основном является uint32_t :)
filo
Попытка написать API, который является эффективным, универсальным и полным, трудна, учитывая, что разные платформы имеют разные возможности. Процессор, вероятно, имеет меньшее значение, чем периферийные устройства и проектные решения, принятые с ними. Например, некоторые устройства позволяют переконфигурировать различные периферийные устройства в любое время не более, чем за несколько микросекунд, в то время как для других устройств может потребоваться несколько шагов, распределенных по сотням микросекунд или даже миллисекунд. Функция API, предназначенная для первого шаблона, может использоваться в подпрограмме обслуживания прерываний, работающей на частоте 10000 Гц, но ...
суперкат
... не может поддерживать такое использование на платформах, которые потребуют распространения операций на сотни микросекунд. Я не знаю, почему разработчики оборудования, кажется, не очень стараются поддерживать семантику API «быстрая работа в любое время», но многие используют модель, которая синхронизирует отдельные операции, а не состояние, так что если, например, был дан запрос включите устройство, и код поймет, что оно не должно быть включено, код должен подождать, пока устройство включится, прежде чем оно сможет выдать запрос на отключение. Плавная обработка в API добавляет серьезные сложности.
суперкат
11

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

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

GPIO может быть очень сложным. Набор битов, сброс битов, переключение битов, специальные функции включения / выключения, три состояния. Затем вы получаете прерывания: любой край, рост, падение, низкий уровень, высокий уровень, самоочищение или нет.

Затем есть I2C, SPI, PWM, таймеры и еще два десятка типов периферийных устройств, каждый со своими собственными часами, и каждый раз регистры отличаются новыми битами. Для всех из них требуется много много часов, читая таблицу данных, как установить, какой бит при каких обстоятельствах.

У последнего производителя было много примеров кода, которые я нашел непригодными. Все было абстрактно. Но когда я нашел его, код прошел шесть! уровни вызовов функций для установки бита GPIO. Хорошо, если у вас процессор 3 ГГц, но не на MCU 48 МГц. Мой код в конце был одной строкой:

GPIO->set_output = bit.

Я пытался использовать более общие драйверы, но я сдался. На MCU вы всегда боретесь с пространством и тактами. Я обнаружил, что уровень абстракции первым выходит из окна, если вы генерируете конкретную форму волны в подпрограмме прерывания, называемой с частотой 10 кГц.

Так что теперь у меня все работает, и я планирую НЕ переключаться снова, если только по очень-очень веской причине.

Все вышеперечисленное должно амортизироваться в зависимости от того, сколько продуктов вы продаете и что экономите. Продажа миллиона: экономия 0,10 для переключения на другой тип означает, что вы можете потратить 100 000 человеко-часов на программное обеспечение. Продажа 1000 у вас есть только 100 потратить.

Oldfart
источник
1
Лично именно поэтому я придерживаюсь ассемблера. Прекрасный двоичный файл, без абстракций.
Ян Блэнд
Препроцессор C может очень хорошо справляться с вещами, особенно в сочетании с внутренними __builtin_constant. Если определить константы для каждого ввода / вывода бит вида (номер порта * 32 + битное число), то можно написать макрос для OUTPUT_HI(n)которого будет уступать код , эквивалентный , GPIOD->bssr |= 0x400;если nэто константа , как 0x6A, но называть простую подпрограмму , если nIS не постоянный Тем не менее, большинство API поставщиков, которые я видел, варьировались от посредственного до ужасного.
суперкат
8

Это больше мнение / комментарий, чем ответ.

Вы не хотите и не должны программировать на C. C ++, при правильном использовании , намного лучше. (Хорошо, я должен признать, что при неправильном использовании он намного хуже, чем C.) Это ограничивает вас чипами, которые имеют (современный) компилятор C ++, что примерно всегда поддерживается GCC, включая AVR (с В некоторых ограничениях Filo упоминает о проблемах неравномерного адресного пространства), но исключая почти все PIC (PIC32 может поддерживаться, но я еще не видел приличного порта).

Когда вы программируете алгоритмы на C / C ++, разница между упомянутыми вами вариантами невелика (за исключением того, что 8 или 16-битная микросхема будет иметь серьезный недостаток, если вы выполняете большую 16, 32-битную или более высокую арифметику). Когда вам нужна последняя унция производительности, вам, вероятно, потребуется использовать ассемблер (либо ваш собственный, либо код, предоставленный поставщиком или третьей стороной). В этом случае вы можете пересмотреть выбранный вами чип.

Когда вы кодируете аппаратное обеспечение, вы можете использовать какой-либо уровень абстракции (часто предоставляемый производителем) или написать собственный (на основе таблицы данных и / или примера кода). Существующие абстракции C в IME (mbed, cmsis, ...) часто функционально (почти) правильны, но ужасно терпят неудачу в производительности (проверьте oldfarts rant о 6 уровнях косвенности для операции набора выводов), удобстве использования и переносимости. Они хотят предоставить вам всю функциональность конкретного чипа, который почти во всех случаях вам не понадобится, и, скорее, вам это не нужно, и он блокирует ваш код для этого конкретного поставщика (и, вероятно, именно этого чипа).

Это то, где C ++ может работать намного лучше: при правильном выполнении набор выводов может проходить через 6 или более уровней абстракции (поскольку это делает возможным лучший (переносимый!) Интерфейс и более короткий код), но в то же время обеспечивает интерфейс, который не зависит от цели. для простых случаев , и все еще приводят к тому же машинному коду, который вы написали бы на ассемблере .

Фрагмент стиля кодирования, который я использую, который может вызвать у вас энтузиазм или отвратить в ужасе:

// GPIO part of a HAL for atsam3xa
enum class _port { a = 0x400E0E00U, . . . };

template< _port P, uint32_t pin >
struct _pin_in_out_base : _pin_in_out_root {

   static void direction_set_direct( pin_direction d ){
      ( ( d == pin_direction::input )
         ? ((Pio*)P)->PIO_ODR : ((Pio*)P)->PIO_OER )  = ( 0x1U << pin );
   }

   static void set_direct( bool v ){
      ( v ? ((Pio*)P)->PIO_SODR : ((Pio*)P)->PIO_CODR )  = ( 0x1U << pin );    
   }
};

// a general GPIO needs some boilerplate functionality
template< _port P, uint32_t pin >
using _pin_in_out = _box_creator< _pin_in_out_base< P, pin > >;

// an Arduino Due has an on-board led, and (suppose) it is active low
using _led = _pin_in_out< _port::b, 27 >;
using led  = invert< pin_out< _led > >;

На самом деле есть еще несколько уровней абстракции. Тем не менее, окончательное использование светодиода, скажем, для его включения, не показывает сложности или деталей цели (для ардуина uno или синей таблетки ST32 код будет идентичен).

target::led::init();
target::led::set( 1 );

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

 mov.w  r2, #134217728  ; 0x8000000
 ldr    r3, [pc, #24]   
 str    r2, [r3, #16]
 str    r2, [r3, #48]   

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

Поэтому, насколько мне известно, это так: напишите уровень абстракции для своего оборудования, но делайте это на современном C ++ (концепции, шаблоны), чтобы он не повредил вашей производительности. С этим на месте, вы можете легко переключиться на другой чип. Вы даже можете начать разработку на каком-то случайном чипе, который у вас есть, с которым вы знакомы, у вас есть хорошие инструменты для отладки и т. Д., И отложить окончательный выбор до следующего (когда у вас будет больше информации о необходимой памяти, скорости процессора и т. Д.).

IMO - одна из ошибок встроенной разработки - сначала выбрать чип (на этом форуме часто задают вопрос: какой чип выбрать?). Лучший ответ, как правило, не имеет значения.)

(edit - ответ на вопрос «Значит, производительность будет выше, C или C ++ будут на одном уровне?»)

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

Воутер ван Оойен
источник
Таким образом, производительность, C или C ++ будет на том же уровне? Я думаю, что C ++ будет иметь больше перегрузки. Определенно, вы указали мне правильное направление, C ++ - это путь, а не C.
инженер
Шаблоны C ++ вызывают полиморфизм во время компиляции, который может быть нулевым (или даже отрицательным) с точки зрения производительности, поскольку код компилируется для каждого конкретного варианта использования. Тем не менее, это лучше всего подходит для определения скорости (O3 для GCC). Полиморфизм во время выполнения, подобно виртуальным функциям, может понести гораздо больший штраф, хотя его легче поддерживать, а в некоторых случаях он достаточно хорош.
Ганс
1
Вы утверждаете, что C ++ лучше, но тогда вы идете и используете приведения в стиле C. Стыдно.
JAB
@JAB Я никогда не чувствовал особого интереса к новым актерам, но я попробую. Но мой текущий приоритет - в других частях этой библиотеки. Конечно, настоящая проблема в том, что я не мог передать указатели в качестве параметров шаблона.
Wouter van Ooijen
@ У моего стиля cto (объекты времени компиляции) есть довольно узкий сценарий использования (близкий к аппаратному обеспечению, известная ситуация во время компиляции), он является скорее C-киллером, чем заменой традиционного использования виртуального ОО. Полезный прилов заключается в том, что отсутствие косвенного обращения позволяет вычислить размер стека.
Wouter van Ooijen
4

Если я правильно понимаю, вы хотите знать, какие специфические особенности архитектуры платформы «всплывают» в вашей языковой среде C, что затрудняет написание поддерживаемого, переносимого кода на обеих платформах.

C уже достаточно гибок в том смысле, что это «переносной ассемблер». На всех выбранных вами платформах имеются коммерческие компиляторы GCC, поддерживающие языковые стандарты C89 и C99, что означает, что вы можете запускать одинаковый код на всех платформах.

Есть несколько соображений:

  • Некоторые архитектуры - фон Нейман (ARM, MIPS), другие - Гарвард. Основные ограничения возникают, когда вашей C-программе необходимо читать данные из ПЗУ, например, для печати строк, иметь данные, определенные как «const» или подобные.

Некоторые платформы / компиляторы могут скрывать это «ограничение» лучше, чем другие. Например, на AVR вам нужно использовать определенные макросы для чтения данных ПЗУ. На PIC24 / dsPIC также доступны специальные инструкции tblrd. Однако, кроме того, некоторые части также имеют функцию «видимости программного пространства» (PSVPAG), которая позволяет отображать страницу FLASH в RAM, делая немедленную адресацию данных доступной без tblrd. Компилятор может сделать это довольно эффективно.

ARM и MIPS - это Von Neumann, поэтому области памяти для ROM, RAM и периферийных устройств упакованы на 1 шину. Вы не заметите никакой разницы между чтением данных из RAM или «ROM».

  • Если вы погрузитесь ниже C и посмотрите на сгенерированные инструкции для определенных операций, вы обнаружите некоторые большие различия в операциях ввода-вывода. ARM и MIPS - это архитектура регистров загрузки RISC . Это означает, что доступ к данным на шине памяти должен проходить через инструкции MOV. Это также означает, что любая модификация периферийного значения приведет к операции чтения-изменения-записи (RMW). Есть некоторые части ARM, которые поддерживают Bit-Banding, которые отображают регистры set / clr-bit в периферийном пространстве ввода-вывода. Однако вам нужно закодировать этот доступ самостоятельно.

С другой стороны, PIC24 позволяет операциям ALU считывать и записывать данные напрямую через косвенную адресацию (даже с модификациями указателей ..). Это имеет некоторые характеристики от CISC-подобной архитектуры, поэтому 1 инструкция может выполнять больше работы. Такая конструкция может привести к более сложным ядрам процессора, снижению тактовой частоты, повышению энергопотребления и т. Д. К счастью для вас, эта часть уже разработана. ;-)

Эти различия могут означать, что PIC24 может быть «более мощным» по сравнению с операциями ввода / вывода, чем аналогично синхронизированный чип ARM или MIPS. Тем не менее, вы можете получить гораздо более высокую частоту ARM / MIPS для таймера при тех же ценовых / упаковочных / конструктивных ограничениях. Я полагаю, что для практических целей, я думаю, что много «изучения платформы» понимает, что архитектура может и не может делать, как быстро будет выполняться несколько операций и т. Д.

  • Периферийные устройства, управление часами и т. Д. Различаются в зависимости от семейства деталей. Строго говоря, это также изменится в экосистеме ARM между поставщиками, за исключением нескольких периферийных устройств, связанных с Cortex, таких как NVIC и SysTick.

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

Кроме того, если вы покидаете экосистемы Microchip / бывшего Atmel, вы можете обнаружить, что для запуска деталей ARM требуется больше настроек. Я имею в виду с точки зрения; Включение часов для периферийных устройств, затем настройка периферийных устройств и их «включение», настройка NVIC отдельно и т. д. Это только часть кривой обучения. Если вы не забудете сделать все эти вещи в правильном порядке, то в какой-то момент написание драйверов устройств для всех этих микроконтроллеров будет довольно схожим.

  • Кроме того, попробуйте использовать библиотеки, такие как stdint.h, stdbool.h и т. Д., Если вы этого еще не сделали. Эти целочисленные типы делают ширину явной, что делает поведение кода наиболее предсказуемым между платформами. Это может означать использование 32-разрядных целых чисел на 8-разрядном AVR; но если ваш код нуждается в этом, пусть будет так.
Hans
источник
3

Да и нет. С точки зрения программиста, вы идеально скрываете детали набора команд. Но это в некоторой степени уже не имеет отношения к периферийным устройствам, которые составляют весь смысл написания программы, не являются частью набора команд. Теперь в то же время вы не можете просто сравнить 4096-битные части флэш-памяти между этими наборами команд, особенно если при использовании C объем потребления флэш-памяти в значительной степени определяется набором команд и компилятором, некоторые никогда не должны видеть компилятор (кашель PIC кашель) из-за того, сколько отходов этих ресурсов потребляется при компиляции. Другие флэш-потребление меньше накладных расходов. Производительность также является проблемой при использовании языка высокого уровня и вопросов производительности в приложениях MCU, поэтому она может иметь значение между затратами 3 доллара на плату для mcu или 1 доллара.

Если речь идет об упрощении программирования (за счет общей стоимости продукта), вы должны иметь возможность загрузить пакет разработчика для mcu таким образом, чтобы архитектура набора команд была чем-то, чего вы никогда не увидите, поэтому если это ваша основная задача, то это это не проблема. Это по-прежнему стоит вам денег, насколько стоимость продукта для использования этих библиотек, но, время выхода на рынок может быть короче, я считаю, что библиотекам требуется больше времени / работы, чтобы использовать по сравнению с непосредственным общением с периферийными устройствами.

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

Старожил
источник