Есть ли какой-нибудь стандарт C для микроконтроллеров? - PullRequest
7 голосов
/ 23 июля 2010

Существует ли какой-либо специальный стандарт С для микроконтроллеров?

Я спрашиваю, потому что до сих пор, когда я что-то программировал под ОС Windows, не имеет значения, какой компилятор я использовал.Если бы у меня был компилятор для C99 , я знал, что я мог с ним сделать.

Но недавно я начал программировать на C для микроконтроллеров, и я был шокирован, что даже это все еще C в его основах, таких как циклы, создание переменных и т.д., есть некоторый синтаксический тип, который я никогда не видел в Cдля настольных компьютеров.И более того, синтаксис меняется от версии к версии.Я использую компилятор AVR-GCC, и в предыдущих версиях вы использовали функцию для ввода / вывода порта, теперь вы можете обрабатывать порт как переменную в новой версии.

Что определяет, какие функции и как иметьони должны быть реализованы в компиляторе и все еще иметь имя C?

Ответы [ 7 ]

17 голосов
/ 24 июля 2010

Существует ли какой-либо специальный стандарт C для микроконтроллеров?

Нет, существует стандарт ISO C.Поскольку многие небольшие устройства имеют специальные особенности архитектуры, которые необходимо поддерживать, многие компиляторы поддерживают языковые расширения.Например, поскольку 8051 имеет ОЗУ с битовой адресацией, может быть предоставлен тип данных _bit.Он также имеет Гарвардскую архитектуру , поэтому предусмотрены ключевые слова для указания различных адресных пространств памяти, которые один адрес не разрешает, так как для адресации этих пространств требуются разные инструкции.Такие расширения будут четко указаны в документации компилятора.Кроме того, расширения в соответствующем компиляторе должны начинаться с подчеркивания.Тем не менее, многие предоставляют неукрашенные псевдонимы для обратной совместимости, и их использование не рекомендуется.

... когда я что-то программировал под ОС Windows, не имеет значения, какой компилятор я использовал.

Поскольку Windows API стандартизирован (Microsoft), и он работает только на x86, поэтому не нужно учитывать никаких архитектурных изменений.Тем не менее, вы все равно можете увидеть макросы FAR и NEAR в API, и это возврат к 16-битному x86 с его сегментированной адресацией, что также требует обработки расширений компилятора.

... что даже это по-прежнему C в его основах, таких как циклы, создание переменных и т. Д.,

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

... есть некоторый тип синтаксиса, который я никогда не видел в C для настольных компьютеров.

Например ...?

И, кроме того, синтаксис меняется от версии к версии.

Я сомневаюсь в этом.Снова;например ...?

Я использую компилятор AVR-GCC, а в предыдущих версиях вы использовали функцию для ввода / вывода порта, теперь вы можете обрабатывать порт как переменную в новой версии.

Это связано не с изменениями в языке или компиляторе, а, скорее, с простой «магией препроцессора».На AVR все операции ввода-вывода отображаются в памяти, поэтому, если, например, вы включите заголовок поддержки устройства, он может иметь объявление, например:

#define PORTA (*((volatile char*)0x0100))

Затем вы можете написать:

PORTA = 0xFF;

для записи 0xFF в память, отображенную в регистре по адресу 0x100.Вы можете просто взглянуть на заголовочный файл и посмотреть, как именно он это делает.

Документация GCC описывает конкретные конкретные варианты;AVR конкретно рассматривается в здесь в разделе 6.36.8 и в 3.17.3 .Если сравнить это с другими целями, поддерживаемыми GCC, у него очень мало расширений, возможно, потому, что архитектура AVR и набор команд были специально разработаны для чистой и эффективной реализации компилятора C без расширений.

Чтоопределяет, какие функции и как сделать так, чтобы они были реализованы в компиляторе и при этом назывались C?

Важно понимать, что язык программирования C - это отдельная сущность из его библиотек, ичто функции, предоставляемые библиотеками, ничем не отличаются от тех, которые вы можете написать сами - они не являются частью языка - так что это может быть C без какой-либо библиотеки.В конечном итоге библиотечные функции написаны с использованием одних и тех же базовых языковых элементов.Вы не можете ожидать, что уровень абстракции, скажем, в Win32 API будет существовать в библиотеке, предназначенной для микроконтроллера.В большинстве случаев можно ожидать, что будет реализовано как минимум подмножество стандартной библиотеки C , поскольку она была разработана как библиотека системного уровня с несколькими целевыми аппаратными зависимостями.

Я писал C и C ++ для встраиваемых и настольных систем в течение многих лет и не осознаю огромных различий, которые вы, похоже, чувствуете, поэтому могу только предположить, что они являются результатом неправильного понимания того, что составляет язык C.Следующие книги могут помочь.

8 голосов
/ 23 июля 2010

Встраиваемые системы странные и иногда имеют исключения из «стандартного» C.

От системы к системе у вас будут разные способы делать такие вещи, как объявление прерываний, или определение переменных, находящихся в разных сегментах памяти, или запуск «встроенных функций» (псевдофункций, которые отображаются непосредственно в код ассемблера), или выполнение inline. код сборки.

Но основы потока управления (для / if / while / switch / case), а также объявления переменных и функций должны быть одинаковыми для всех.

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

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

6 голосов
/ 23 июля 2010

Язык C предполагает архитектуру von Neumann (одно адресное пространство для всего кода и данных), которую на самом деле имеют не все архитектуры, но большинство настольных /машины серверного класса есть (или, по крайней мере, присутствуют с помощью ОС).Чтобы обойти это без создания ужасных программ, компилятор C (с помощью компоновщика) часто поддерживает некоторые расширения, которые помогают эффективно использовать несколько адресных пространств.Все это может быть скрыто от программиста, но оно часто замедляет и раздувает программы и данные.

Что касается доступа к регистрам устройств - на разных компьютерах класса настольных компьютеров / серверов это очень отличается, так какхорошо, но поскольку программы, написанные для работы на общих современных ОС для этих машин (Mac OS X, Windows, BSD или Linux), обычно не имеют прямого доступа к оборудованию, это не проблема.Однако есть код ОС, который должен решить эти проблемы.Обычно это делается путем определения макросов и / или функций, которые по-разному реализованы на разных архитектурах или даже имеют несколько версий в одной системе, чтобы драйвер мог работать для конкретного устройства (такого как чип Ethernet), независимо от того, было ли оно на * 1009.* PCI карта или USB-ключ (возможно, подключенный к USB-карте, подключенной к слоту PCI), или непосредственно сопоставленный с адресным пространством процессора.

Кроме того, стандартная библиотека C делает больше предположений, чемкомпилятор (и собственно язык) о системе, в которой размещены программы, которые его используют ( стандартная библиотека C ).Эти вещи просто не имеют смысла, когда нет ОС общего назначения или файловой системы.fopen не имеет смысла в системе без файловой системы, и даже printf не может быть легко определяемым.

Что делает AVR-GCC и его библиотеки - тамЕсть много вещей, которые входят в то, как это делается.AVR представляет собой архитектуру Harvard с регистрами управления отображенными в памяти устройствами, регистрами специальных функций и регистрами общего назначения (адреса памяти 0-31) и другим адресным пространством для кода и константыданные.Это уже выходит за рамки того, что предполагает стандарт С.Некоторые из регистров (общее, специальное и управление устройством) доступны через специальные инструкции для таких вещей, как переключение отдельных битов и чтение / запись в некоторые многобайтовые регистры (операция с несколькими командами), неявно блокирующие прерывания для следующей инструкции (поэтомучто вторая половина операции может случиться).Это то, о чем не нужно знать программам C на рабочем столе, и, поскольку AVR-GCC происходит от обычного GCC , он изначально не понимал все эти вещи.Это означало, что компилятор не всегда использовал лучшие инструкции для доступа к регистрам управления, поэтому:

*(DEVICE_REG_ADDR) |= 1; // Set BIT0 of control register REG

превратился бы в:

temp_reg = *DEVICE_REG_ADDR;
temp_reg |= 1;
*DEVICE_REG_ADDR = temp_reg;

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

5 голосов
/ 23 июля 2010

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

На многих 8-битных микроконтроллерах и даже на некоторых 16-битных, обращающихся к переменнымкадр стека медленный.Некоторые компиляторы всегда выделяют автоматические переменные в стеке времени выполнения, несмотря на дополнительный код, необходимый для этого, некоторые выделяют автоматические переменные во время компиляции (позволяя переменным, которые никогда не живут одновременно, перекрываться), а некоторые позволяют контролировать поведениес параметрами командной строки или директивами #pragma.При кодировании для таких машин мне иногда нравится #define макрос, называемый «auto», который переопределяется в «static», если это поможет быстрее работать.

Некоторые компиляторы имеют различные классы хранения для памяти,Вы можете значительно повысить производительность, объявив вещи подходящими классами хранения.Например, система на основе 8051 может иметь 96 байт памяти «данных», 224 байта памяти «idata», которая перекрывает первые 96 байтов, и 4 Кб памяти «xdata».

  • Переменные в памяти данных могут быть доступны напрямую.

  • Переменные в памяти «idata» могут быть доступны только путем загрузки их адреса в однобайтовый регистр указателя.Нет необходимости в дополнительном доступе к ним в тех случаях, когда это было бы необходимо в любом случае, поэтому память idata отлично подходит для массивов.Если массив q хранится в памяти idata, ссылка на q[i] будет такой же быстрой, как если бы она была в памяти данных, хотя ссылка на q[0] будет медленнее (в памяти данных компилятор может предварительновычислить адрес и получить к нему доступ без регистра указателя; в памяти идентификаторов это невозможно).

  • Переменные в памяти xdata доступны намного медленнее, чем в других типах, но естьдоступно гораздо больше памяти xdata.

Если кто-то скажет компилятору 8051 поместить все в «данные» по умолчанию, он «исчерпает память», если его переменные составляют более 96 байтови никто не дал указание компилятору помещать что-либо в другое место.Если по умолчанию все поместить в «xdata», можно использовать гораздо больше памяти, не превышая ограничения, но все будет работать медленнее.Лучше всего помещать часто используемые переменные, к которым будет осуществляться прямой доступ, в «данные», часто используемые переменные и массивы, к которым косвенный доступ осуществляется в «idata», и редко используемые переменные и массивы в «xdata».

2 голосов
/ 23 июля 2010

Подавляющее большинство стандартного языка C характерно для микроконтроллеров. Прерывания, как правило, имеют несколько иные соглашения, хотя и не всегда.

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

1 голос
/ 23 июля 2010

Wiring имеет синтаксис языка Си.Возможно, вы захотите узнать, что делает его таким.

1 голос
/ 23 июля 2010

Как говорили предыдущие участники, стандарта как такового нет, в основном из-за разных архитектур.

Сказав это, Dynamic C (продается Rabbit Semiconductor ) описывается как "C с расширениями в реальном времени". Насколько мне известно, компилятор предназначен только для процессоров Rabbit, но есть полезные дополнительные ключевые слова (например, costate, cofunc и waitfor), некоторые реальные особенности (например, #use mylib.lib вместо #include mylib.h - и нет компоновщика ) и несколько пропусков в ANSI C (например, нет статических переменных области файла).

Это все еще описывается как 'C'.

...