Языки высокого уровня — основные понятия и основы применения


Языки высокого уровняПричины использования языков высокого уровня

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

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

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

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

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

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

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

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

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

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

Библиотеки и функции.

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

Функция стандартной библиотеки предварительно проверяет наличие обору­дования (и его характеристики) и только после этого выполняет заданные дей­ствия.

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

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

Типы данных и операции над данными.

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

Типы данных можно разделить на две категории: числовые и нечисловые. Числовые типы данных. Среди них главными являются целые двоичные числа со знаком и без знака. Для их представления обычно используется 8, 16, 32 и 64 бита. В целых числах со знаком старший разряд отводится под знак, по­этому значения N–разрядного числа лежат в пределах от –(2N–1 – 1) до +(2N–1 – 1), диапазон значений чисел без знака составляет 0…2 N – 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, А INC SKIP ;Если перенос С не установлен, ;то пропустить увеличение INC FirstVar + 1 ;Увеличить старшие 8 бит результата SKIP MOV A , 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 на языке С) и ссылаться на него при помощи индекса.

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

Проиллюстрируем использование указателей и структур данных на конкрет­ном примере. При использовании указателя для выбора некоторой величины из области данных необходимый программный код имеет следующий вид: struct inst ruct * Ptr ; //Определение указателя на структуру.

; i = Ptr —> value ; / / Прочесть » value » из текущего элемента При организации массива эта процедура выглядит следующим образом: struct inst ruct Array[100]; //Определение массива структур.

; i = = Array [ Index ]. value ; Этот код немного сложнее кода с указателем, но в нем не используется указа­тель, за которым необходимо следить, чтобы он всегда корректно обращался к требуемому элементу структуры.

Другой распространенной структурой данных является таблица, которую мож­но представить как одномерный массив неизменяемых строк данных. В строке содержится информация о состоянии системы или сообщение для пользователя, например: char Greeting [13] = «Hello there I»; Компилятор языка размещает таблицы в памяти программ. Информация в таблице выбирается как массив, идентичный массивам, считываемым из опера­тивной памяти.

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

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

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

Символическая информация.

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

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

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

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


Понравилась статья? Поделиться с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: