Языки высокого уровняПричины использования языков. Помимо ассемблеров для программирова­ния приложений используются различные языки высокого уровня, что обусловле­но следующими причинами:

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

●     при программировании на языках высокого уровня имеется ряд возможностей, которыми не располагают ассемблеры. Например, разработчик может авто­матически вводить в программу коды сложных операций, производить эф­фективный контроль синтаксических ошибок и др. Поэтому составление про­грамм на языках высокого уровня требует меньших затрат;

●     при разработке программ на языке высокого уровня значительно упрощается поддержка приложения, связанная с модификацией и отладкой программно­го кода.

К наиболее распространенным языкам относятся C/C++, BASIC и Forth. С помощью компиляторов языки высокого уровня преобразуются в язык ассемб­лера, а затем в объектный код (биты и байты), который выполняется микроконтроллером. Имеется множество компиляторов, разработанных для различных мик­роконтроллеров. Их эффективность определяется требуемым объемом памяти программ и данных, а также ресурсами, необходимыми для поддержания объект­ного кода.

Языки высокого уровня характеризуются рядом показателей, реализация ко­торых во встраиваемых микроконтроллерах может оказаться проблематичной, что обусловлено:

●     ограниченным объемом памяти программ (ROM) и данных (RAM);



●     отсутствием BIOS или операционной системы;

●     наличием переопределяемых выводов контроллеров (когда вывод может ис­пользоваться как цифровой/аналоговый/последовательный вход–выход).

Рассмотрим основные особенности и показатели языков высокого уровня применительно к микроконтроллерам.

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

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

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

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

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

Типы данных можно разделить на две категории: числовые и нечисловые.

Числовые типы данных. Среди них главными являются целые двоичные числа со знаком и без знака. Для их представления обычно используется 8, 16, 32 и 64 бита. В целых числах со знаком старший разряд отводится под знак, по­этому значения N–разрядного числа лежат в пределах от –(2N–11) до +(2N–1 – 1), диапазон значений чисел без знака составляет 0...2N – 1.

Для представления нецелых чисел (например, 4.5) используется две формы: с плавающей и фиксированной точкой. Их длина может составлять 32,64 и 80 бит.

В компьютерах имеются отдельные регистры для целочисленных операндов и для операндов с плавающей точкой.

Для представления десятичных чисел используется двоично–десятичный фор­мат, в котором для кодирования одного разряда отводится 4 или 5 бит. Для кор­рекции результатов десятичной арифметики используются специальные команды.

Нечисловые типы данных. Для обработки текстов или управления базой данных необходимо использовать нечисловые типы данных. Для этой цели исполь­зуются такие символьные коды как ASCII или UNICODE, поддерживающие 7–бит­ные и 16–битные символы соответственно.

В микроконтроллерах широкое распространение получил целочисленный тип данных длиной 8 и 16 бит. Операции над числами, имеющими разрядность больше базовой (8 бит), часто реализуются с помощью библиотечных функций, которые увеличивают время выполнения и объем требуемой памяти. Обработка 16–раз­рядных данных реализуется с помощью небольшого увеличения объема программ­ного кода и памяти данных. В тоже время 16–разрядные данные обеспечивают достаточно большой диапазон представления обрабатываемых данных. При пра­вильной организации вычислений обработка данных практически для всех встроенных приложений, использующих 8–разрядные микроконтроллеры, может быть выполнена с использованием 8–и и 16–и разрядных целых чисел.

Обработка целочисленных данных. При обработке данных с разрядно­стью более 8 бит необходимо вводить дополнительные команды.

Пример. Рассмотрим операцию сложения, представленную в виде выражения на языке С:

FirstVar = FirstVar + SecondVar

Для 8–разрядных операндов эта операция выполняется микроконтроллером 8051 с помощью следующей последовательности команд:

MOV              A, FirstVar                ;Поместить первую переменную FirstVar в

;аккумулятор А

ADD               A, SecondVar           ;Содержимое А сложить со второй

;переменной SecondVar

MOV               FirstVar, А               ;Сумму поместить в ячейку FirstVar

 

Для 16–разрядных переменных программный код усложняется:

MOV              A, FirstVar                ;Сложить младшие 8 бит

ADD               A, SecondVar

MOV              FirstVar, А

INCSKIP                                         ;Если перенос С не установлен,

;то пропустить увеличение

INC                FirstVar + 1              ;Увеличить старшие 8 бит результата

SKIP

MOVA, FirstVar + 1          ;Сложить старшие 8 бит

ADD A, SecondVar + 1

MOV FirstVar + 1, А

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

FirstVar = SecondVar + (ThirdVar * FourthVar)

последовательность операций, обеспечивающих получение необходимого ре­зультата, имеет вид

push      SecondVar

push      ThirdVar

push      FourthVar

mul

add

pop       FirstVar

 

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

FirstVar = SecondVar + ( ThirdVar * Fourth Var )

более эффективно реализуется в виде следующей последовательности операций:

Temp = ThirdVar * FourthVar; FirstVar = SecondVar + Temp;

Еще одно возможное представление приведенного выше примера касается того, как компилятор преобразует числа и обрабатывает промежуточные пере­менные. Если переменная «FirstVar» не определена как порт ввода–вывода, то программный код можно представить в следующем виде:

FirstVar = ThirdVar * Fourth Var;

FirstVar = FirstVar + SecondVar;

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

Глобальные и локальные переменные. Во многих языках высокого уровня существует два типа переменных:

●     глобальные переменные, которые заданы для всей программы. Их значения не могут переопределяться. Они обычно используются в основном теле про­граммы и при выполнении многих подпрограмм, когда передача значения пе­ременной в качестве параметра подпрограммы не эффективна;

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

Указатели и структуры данных. Эти средства служат для повышения эффек­тивности программирования при больших объемах памяти.

Указатели предназначены для управления ресурсами памяти. При программи­ровании микроконтроллеров с малым объемом памяти указатели обычно не ис­пользуются. В таких контроллерах вместо указателей в начале раздела памяти создается массив, который путем индексирования используется для обращения к любому элементу в этой памяти. Чтобы выбрать необходимый элемент в массиве данных, следует к значению индекса, задающего адрес начального элемента массива, прибавить смещение, указывающее позицию выбираемого элемента.

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

Например, структуру процессорной команды на языке С можно определить следующим образом:

struct instruct {        // Формат команды

int       address;         // Адрес команды

char    instruct;          // Команда

int       value;             //16–битные данные

}:

 

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

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

structinstruct * Ptr;             //Определение указателя на структуру

.

;

i = Ptr —> value;                  //Прочесть "value" из текущего элемента

При организации массива эта процедура выглядит следующим образом:

structinstruct Array[100];  //Определение массива структур

.

;

i = = Array[ Index ].value;

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

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

char Greeting [13] = "Hello there I";

Компилятор языка размещает таблицы в памяти программ. Информация в таблице выбирается как массив, идентичный массивам, считываемым из опера­тивной памяти.

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

Оба способа поддерживаются языками высокого уровня.

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

Из материала по языкам программирования следует, что:

●     при программировании управляющих систем чаще всего используются ма­шинно–ориентированный язык ассемблера или языки С / С++. Язык ассемблера применяется при жестких ограничениях на объем требуемой памяти или на время выполнения программных модулей. Такие случаи являются достаточно типич­ными при решении задач управления, поэтому ассемблеры являются одним из основных средств создания программного обеспечения для микроконт­роллерных систем. В тех случаях, когда указанные ограничения не очень жес­ткие, для создания программного обеспечения используются языки высокого уровня (обычно С / С++);

 

●     при разработке программного обеспечения для универсальных микропроцес­сорных систем используется достаточно широкий набор языков высокого уровня, для которых имеются соответствующие компиляторы. Чаще всего ис­пользуются языки С, С++, FORTRAN, Pascal, Forth. Для решения ряда задач применяются языки поддержки искусственного интеллекта Ada, Modula–2 и некоторые другие.


Рекомендуйте эту статью другим!



Особенности нового ГОСТа по качеству электроэнергии
июнь 16, 2014 4347

Особенности нового ГОСТа по качеству электроэнергии

С начала 2013 года вступил в действие обновлённый стандарт качества ГОСТ Р 54149, в…
рис. 1.141
окт 17, 2016 736

Влияние различных факторов на выходное напряжение операционного усилителя

При практическом использовании операционного усилителя необходимо учитывать, что…
dif1
апр 03, 2017 1914

Дифференциальная защита, диф реле, принцип работы, применение

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

Способы и методы последовательного обмена

Принципы обмена. Последовательный обмен (ввод–вывод данных), когда байт данных передается…