Действительно ли там какой-либо C является стандартным для микроконтроллеров?

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

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

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

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

7
задан Peter Mortensen 23 February 2013 в 17:44
поделиться

7 ответов

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

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

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

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

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

8
ответ дан 6 December 2019 в 05:03
поделиться

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

1
ответ дан 6 December 2019 в 05:03
поделиться

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

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

Тем не менее, он все еще описывается как 'C'.

1
ответ дан 6 December 2019 в 05:03
поделиться

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

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

2
ответ дан 6 December 2019 в 05:03
поделиться

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

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

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

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

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

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

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

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

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

Сомневаюсь. Опять же; например...?

Я использую компилятор 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. Вам могут помочь следующие книги.

17
ответ дан 6 December 2019 в 05:03
поделиться

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

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

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

  • К переменным в памяти "data" можно обращаться напрямую.

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

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

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

5
ответ дан 6 December 2019 в 05:03
поделиться

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

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

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

Что касается того, что делают AVR-GCC и его библиотеки - есть много вещей, которые касаются того, как это делается. AVR представляет собой архитектуру Гарварда с отображенными в память регистрами управления устройством, регистрами специальных функций и регистрами общего назначения (адреса памяти 0–31), а также другим адресным пространством для кода и константных данных. Это уже выходит за рамки того, что предполагает стандарт C. Некоторые из регистров (общие, специальные и управление устройством) доступны через специальные инструкции для таких вещей, как переключение отдельных битов и чтение / запись в некоторые многобайтовые регистры (операция с несколькими инструкциями), неявно блокирует прерывания для следующей инструкции (так что что вторая половина операции может случиться). Это вещи, о которых настольные программы на 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). Если они больше не предоставляют макроверсии этих операций, то это, вероятно, плохой выбор, поскольку он ломает старый код, но позволяет вам получать доступ к этим регистрам, как если бы они были обычными переменными, когда возможность делать это эффективно и атомарно была реализована, - это хорошо.

6
ответ дан 6 December 2019 в 05:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: