Вы находитесь на странице: 1из 15

Аверченков О. Е.

Особенности программирования
однокристальной ВМ х51
на языке Си
Учебное пособие
по курсу «Микропроцессорные системы»

Москва, 2012
УДК 004.438 (075.8)
ББК 31.294.9***
А19

Допущено учебно-методическим объединением вузов по универ-


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

Рецензент кандидат технических наук, доцент филиала ГОУВПО


«МЭИ (ТУ)» в г. Смоленске Е. А. Панкратова.

Аверченков О. Е.
А19 Особенности программирования однокристалльной ВМ х51
на языке Си: учебное пособие по курсу «Микропроцессорные
системы». – М.: ДМК Пресс, 2012. – 110 с.
ISBN 978-5-94074-***
В данном пособии описываются основные особенности програм-
мирования на языке Си применительно к однокристальной ВМ
(ОВМ) семейства х51. Основное внимание уделено базовым сведе-
ниям о типах данных для 8-битной ОВМ и ее внутренних узлов.
Приведены примеры типовых преобразований и процедур, которые
могут быть использованы при курсовом проектировании.

УДК 004.438 (075.8)


ББК 31.294.9

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

© Аверченков О. Е., 2012


ISBN 978-5-94074-*** © Оформление, ДМК Пресс, 2012
СОДЕРЖАНИЕ

Введение ......................................................................................................................... 8

1 Особенности программ на языке Си ........................................................... 9


1.1. Директивы #include и #define ................................................................10
1.1.1. Общие сведения ...............................................................................10
1.1.2. Особенности включаемых файлов ............................................11
1.1.3. Директива препроцессора #define ...........................................12
1.2. Ресурсы ОВМ х51 ......................................................................................13
1.2.1. Общие сведения ...............................................................................13
1.2.2. Указание места размещения переменных ...............................14
1.2.3. Размещение локальных переменных, используемых
процедурами.................................................................................................15
1.2.4. Работа со спецрегистрами ............................................................15
1.2.5. Ассемблерные вставки...................................................................16
1.3. Объявления переменных и констант...................................................16
1.3.1. Объявления переменных ..............................................................16
1.3.2. Объявление не удаляемых локальных переменных ...........17
1.3.3. «Изменчивые» переменные типа volatile................................18
1.3.4. Объявления констант ....................................................................18
1.3.5. Объявления многобайтовых переменных и массивов .......19
1.4. Объявления функций ...............................................................................20
1.4.1. Общие сведения ...............................................................................20
1.4.2. Объявление функции, не получающей
и не возвращающей данных ....................................................................20
1.4.3. Объявление функции, получающей параметры ...................21
1.4.4. Объявление функции, возвращающей значение .................21
1.4.5. Бесконечный цикл в главной функции ...................................22
1.4.6. Пример записи простейшей программы .................................22
1.5. Адресуемые биты ОВМ х51 ....................................................................23
1.5.1. Общие сведения ...............................................................................23
1.5.2. Объявления битовых переменных ............................................24
1.5.3. Операции с битовыми переменными .......................................25

2 Примеры типовых преобразований и процедур ...................................27


2.1. Типовые преобразования данных .........................................................28
2.1.1. Битовые операции для целочисленных операндов .....................28
2.1.2. Сдвиги переменных ........................................................................29
4 СОДЕРЖАНИЕ

2.1.3. Преобразование «коротких» типов переменных


в «длинные»..................................................................................................30
2.1.4. Преобразование бита в целое число .........................................30
2.1.5. Преобразование «длинных» типов переменных
в «короткие» .................................................................................................31
2.1.6. Преобразование целого числа в бит («свертка»
переменной) .................................................................................................31
2.2. Работа с отдельными битами целых чисел ........................................32
2.2.1. Указание места бита в целочисленной переменной ............32
2.2.2. Образование маски для нескольких значащих битов ......33
2.2.3. Установка отдельных битов целочисленного операнда .....33
2.2.4. Сброс незначащих битов...............................................................34
2.2.5. Инвертирование отдельных битов ............................................34
2.2.6. Обмен частей переменных ...........................................................35
2.2.7. Объединение (упаковка) групп битов разных
переменных...................................................................................................35
2.2.8. Разъединение (распаковка) переменной на группы
битов ...............................................................................................................36
2.3. Процедуры задержки ................................................................................36
2.3.1. Общие сведения ...............................................................................36
2.3.2. Реализация микросекундных задержек ..................................37
2.3.3. Реализация программируемой задержки при помощи
оператора for.................................................................................................38
2.3.4. Оценка времени задержки mDelayFOR ..................................38
2.3.5. Реализация задержки при помощи оператора do-while.....39
2.3.6. Реализация паскалевской процедуры Delay..........................40
2.3.7. Использование и недостатки процедур задержки ...............40
2.4. Примеры программ преобразования кодов .......................................41
2.4.1. Программная реализация функций дешифратора
и шифратора .................................................................................................41
2.4.2. Преобразование байта в BCD формат .....................................42
2.4.3. Преобразование BCD декады в код управления
7-сегментным индикатором ....................................................................43
2.4.4. Вычисление контрольной суммы массива во внешнем
ОЗУ .................................................................................................................45
2.4.5. Прием данных в буфер со сдвигом ............................................45
2.4.6. Скользящее осреднение результатов оцифровки ................46

3 Управление битами портов ............................................................................47


3.1. Вывод-ввод информации битовыми командами .............................48
3.1.1. Генерация пачки импульсов ........................................................48
СОДЕРЖАНИЕ 5

3.1.2. Генерация звука «бип-бип» ..........................................................48


3.1.3. Ввод сигнала от контактного датчика ......................................49
3.1.4. Счет числа нажатий кнопки с «дребезгом» ............................50
3.2. Вывод-ввод информации байтовыми командами ...........................51
3.2.1. Байтовое управление простейшим светофором ...................51
3.2.2. Программа для простейшего светофора..................................53
3.2.3. Вычисление скан-кода матрицы ключей ................................54
3.2.4. Двунаправленный опрос
матрицы ключей .......................................................................................55
3.2.5. Текст программы для двунаправленного опроса ...............56
3.3. Ввод-вывод в последовательном асинхронном виде .....................56
3.3.1. Общие сведения ...............................................................................56
3.3.2. Программная передача асинхронной посылки .....................57
3.3.3. Программный прием асинхронной посылки .........................58
3.4. Вывод-ввод в последовательном синхронном виде .......................58
3.4.1. Общие сведения ...............................................................................58
3.4.2. Программная передача мастером синхронной посылки ...60
3.4.3. Программный прием мастером синхронной посылки .......61
3.4.4. Пример обслуживания АЦП с последовательным
синхронным выходом ...............................................................................61
3.4.5. Пример дуплексной процедуры приема-передачи
для интерфейса SPI ...................................................................................62
3.5. Особенности межмикросхемного интерфейса I2C .........................63
3.5.1. Общие сведения ...............................................................................63
3.5.2. Примеры процедур СТАРТ-СТОП
для интерфейса I2C ....................................................................................64
3.5.3. Прием-передача бита квитирования
для интерфейса I2C ....................................................................................65
3.6. Однопроводный синхронный интерфейс MICROLAN ................66
3.6.1. Общие сведения ...............................................................................66
3.6.2. Программа передачи байта ..........................................................67
3.6.3. Программа чтения байта ...............................................................68

4 Управление системой прерываний ОВМ х51 .........................................70


4.1. Регистры и биты системы прерывания ...............................................71
4.1.1 Управление разрешениями и приоритетами ..........................71
4.1.2. Биты типа внешних прерываний ...............................................72
4.1.3. Биты запросов прерываний ........................................................72
4.2. Оформление и примеры прерывающих процедур ..........................73
4.2.1. Объявление прерывающей процедуры....................................73
4.2.2. Результат работы прерывающих процедур ............................74
6 СОДЕРЖАНИЕ

4.2.3. Инициализация прерываний ....................................................75


4.2.4. Пример процедуры для счета внешних импульсов .............75
4.2.5. Измерение частоты внешних импульсов ................................76

5 Управление таймерами ....................................................................................77


5.1. Регистры, биты и режимы таймеров ....................................................78
5.1.1. Регистры таймеров..........................................................................78
5.1.2. Регистр TCON и пуск-останов счета ........................................78
5.1.3. Формат регистра TMOD и задание режима ..........................79
5.1.4. Задание режима счета ....................................................................80
5.1.5. Изменение режима только одного из таймеров....................80
5.1.6. Загрузка 16-разрядных регистров данных таймеров ..........81
5.1.7. Чтение регистров данных таймеров .........................................81
5.2. Формирование интервалов времени при помощи таймеров ......82
5.2.1. Общие сведения ...............................................................................82
5.2.2. Определение кода загрузки таймера ........................................83
5.2.3. Отсчет заданного времени без использования
механизма прерываний ............................................................................84
5.2.4. Пример инициализации таймера при работе в режиме
прерывания ...................................................................................................84
5.2.5. Отсчет заданного времени с использования механизма
прерываний...................................................................................................85
5.2.6. Использование таймера для отсчета одной секунды ..........86
5.2.7. Использование флажков-семафоров........................................87
5.3. Измерение длительности и частоты импульсов ..............................87
5.3.1. Внешнее управление счетом таймера.......................................87
5.3.2. Программа для измерения длительности импульса ...........88
5.3.3. Счет переполнений таймера при измерении длинных
импульсов .....................................................................................................89
5.3.4. Использование механизма прерываний
для определения начала и окончания импульса .............................90
5.3.5. Измерение частоты импульсов .................................................90

6 Управление последовательным портом ....................................................92


6.1. Ресурсы последовательного порта .......................................................93
6.1.1. Регистры данных приемника и передатчика .........................93
6.1.2. Регистр управления последовательным портом ..................94
6.1.3. Биты задания режима работы последовательного порта
(SM0, SM1, SM2, REN) ............................................................................94
6.1.4. Особенности работы с девятым битом ....................................96
СОДЕРЖАНИЕ 7

6.2. Инициализация последовательного порта ........................................96


6.2.1. Общие сведения ...............................................................................96
6.2.2. Задание режима работы последовательного порта
битовыми командами ................................................................................97
6.2.3. Задание режима работы последовательного порта
байтовой командой ....................................................................................97
6.2.4. Задание скорости и инициализация таймера 1.....................98
6.2.5. Удвоение скорости приема-передачи .......................................99
6.2.6. Пример инициализации последовательного порта ...........100
6.3. Процедуры приема для последовательного порта ........................100
6.3.1. Программное ожидание посылки ............................................100
6.3.2. Прием по прерыванию одного байта ......................................101
6.3.3. Прием по прерыванию заданного количества байтов ......102
6.3.4. Прием строки символов по прерыванию ..............................103
6.3.5. Примеры простейшей обработки принятой
информации ...............................................................................................103
6.4. Процедуры передачи для последовательного порта ....................104
6.4.1. Передача одного байта без прерывания ................................104
6.4.2. Передача байта с ожиданием окончания посылки ............104
6.4.3. Передача пакета данных без прерывания .............................104
6.4.4. Передача пакета по прерыванию .............................................105
6.4.5. Передача строки символов по прерыванию .........................106
6.4.6. Примеры подготовки пакета для передачи ..........................106

Заключение ...............................................................................................................107

Список используемых сокращений .................................................................108

Литература .................................................................................................................109
Введение
Сравнительно большие объемы внутренней памяти для хранения
команд в современных однокристальных вычислительных машинах
(ОВМ) позволяют перейти к созданию программ на языках высоко-
го уровня, бинарные файлы которых не столь компактны, как при
использовании ассемблера. Наибольшее распространение для про-
граммирования ОВМ получил язык Си, который из-за множества
«мелких» возможностей иногда в шутку называют промежуточным
между ассемблером и «настоящими» языками высокого уровня.
Однако программирование ОВМ ориентировано не столько на
вычислительные задачи и обслуживание стандартных устройств
ввода-вывода, как в компьютере, сколько на управление внутрен-
ней периферией ОВМ с целью обслуживания различных внешних
событий и объектов. Поэтому без знания внутреннего устройства
ОВМ и ее особенностей, хотя бы в виде перечня спецрегистров и
их форматов (так называемой упрощенной программной модели),
в большинстве случаев не обойтись.
Язык Си позволяет сравнительно легко переходить к программи-
рованию других семейств ОВМ. Чтобы основную часть программы
можно было использовать для иного процессора, желательно все
операции, связанные с уникальной аппаратурой ОВМ, выделять
в отдельные процедуры. Тогда при переходе на другое семейство бы-
вает достаточно в большинстве случаев изменить только эти фраг-
менты (учитывающие особенности внутренней аппаратуры), остав-
ляя организационную и управляющую части программы прежними.
Данное пособие не является учебником по языку Си, в нем при-
ведены только некоторые особенности программ, учитывающие
ограниченность внутренних ресурсов ОВМ х51. Поэтому изложен-
ный материал основан только на базовых средствах языка Си и его
основных операторах [1] с учетом правил компилятора SDCC (ANSI
стандарт, с открытыми источниками, [2]).
ГЛАВА СТРАНИЦА

ОСОБЕННОСТИ
1 ПРОГРАММ
НА ЯЗЫКЕ СИ
2 Примеры типовых преобразований
27
и процедур
3 Управление битами портов 47
4 Управление системой прерываний ОВМ
70
х51
5 Управление таймерами 77
6 Управление последовательным портом 92
10 ОСОБЕННОСТИ ПРОГРАММ НА ЯЗЫКЕ СИ

1.1. Директивы #include и #define

1.1.1. Общие сведения


Каждый язык программирования, как и монастырь, имеет свой
устав. Программа на Си [1] несколько похожа по своей структу-
ре на ассемблерную программу (см. п. 1.4.6). Например, она имеет
фрагменты, где описываются константы и внешние сигналы, резер-
вируются переменные, определяются используемые процедуры и
главная часть программы пользователя. Но сначала отметим только
некоторые общие особенности записи программ.
Основой для реализации алгоритмов являются операторы. В Си,
как и в других языках программирования, имеется условный опе-
ратор if, операторы цикла for, while, выбора switch, передачи управ-
ления goto и другие.

Имена констант, переменных и процедур, используемых в примерах,


начинаются с префикса «m», чтобы отличать пользовательские иден-
тификаторы от стандартных имен спецрегистров, операторов и ди-
ректив, которые вдобавок, для отличия, набраны жирным шрифтом.
При использовании описанных программных фрагментов в своих
программах имена с префиксом «m» нужно изменять в соответствии
с личными предпочтениями.
После операторов в большинстве случаев ставится точка с запятой.
Однако нужно помнить, что точка с запятой не ставится после ди-
ректив препроцессора (начинающихся со знака #) и после фигур-
ных закрывающих скобок.
Как всегда неотъемлемой принадлежностью программы являются
комментарии, которые могут быть двух типов – многострочные (об-
рамленные служебными знаками /* и */) и однострочные (начина-
ющиеся со знаков //). Рекомендуется [1] применять в программе
однострочные комментарии, а многострочные использовать только
в громоздком заголовке или при отладке для временного отключе-
ния фрагментов программы.

Язык Си, с его особенностями и стандартными функциями, скры-


тыми во включаемых файлах, реализован для множества процессо-
ров. Перечень таких файлов обязательно указывается в начале про-
граммы при помощи директивы #include с именем файла в двойных
кавычках или угловых скобках:
#include "mNameFile" // Файл находится в текущем каталоге.
#include <mNameFile> // Файл берется из каталога компилятора.
ДИРЕКТИВЫ #include И #define 11

Содержимое включаемых файлов при предобработке дополняет


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

1.1.2. Особенности включаемых файлов


Чаще всех используются заголовочные файлы, которые имеют имена
с расширением .h (от HEADER – заголовок). Ведь в них описаны ос-
новные стандартные функции и определения языка. Они содержат
прототипы функций, определения для функциональных констант,
структуры, используемые компилятором, и другую полезную ин-
формацию.
Например, математические функции бывают доступны при ис-
пользовании файла math.h, а функции системы прерываний – signal.h
или interrupt.h.
При программировании бывает полезно ознакомиться с перечнем
включаемых файлов для конкретного компилятора.

Отметим, что в заголовочных файлах действительно приведены


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

Кроме того, к заголовочным относятся файлы с описанием ресур-


сов процессора. Например, для процессора АТ89С51 фирмы ATMEL
используется директива
#include <at89x51.h>

с файлом at89x51.h, в котором находятся основные определения


стандартных имен для ОВМ х51 и их порядковые номера в памяти
данных. Для иных процессоров нужно применять соответствующие
заголовочные файлы.

Кроме заголовочных в текст программы можно включать и любые


другие файлы с процедурами и функциями, написанными на Си.
12 ОСОБЕННОСТИ ПРОГРАММ НА ЯЗЫКЕ СИ

Это позволяет многократно использовать свои (и чужие) наработки


и значительно облегчает процесс программирования.

1.1.3. Директива препроцессора #define


Директива препроцессора #define записывается в следующем виде:
# define mName mTextC // Без точки с запятой.

Препроцессор, встретив в программе имя mName, заменяет его


на текст mTextC, написанный по правилам языка Си. Поэтому эту
директиву иногда называют директивой «подмены» текста. Кроме
того, учитывая, что директива по смыслу похожа на ассемблерный
макрос, то текст с именем mName иногда и называют макросом.
Эта директива служит для именования частей программы, а также
для введения привычных имен для некоторых стандартных иденти-
фикаторов, определенных во включаемом файле. Но следует иметь
в виду, что ключевые слова самого языка Си переопределять нельзя.
Для большей заметности и явного выделения текста макроса ре-
комендуется обрамлять его фигурными скобками:
#define mFazaOff { mFaza1 = 1; mFaza2 = 1; mFaza3 = 1; }

Макрос может также получать параметры:


#define mOn(mVar) { mFazaOff; mVar = 0; }

Если текст после директивы не умещается на одной строке, то в кон-


це строки ставят знак \ (обратный слеш),

Вызов и выполнение операторов небольшого макроса происходит


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

Хрестоматийный пример записи неправильного макроса [1] для вы-


числения квадрата #define sqr(x) x*x. При его использовании в слу-
чае y = sqr(x + 1) препроцессор производит следующую подстановку:
у = х + 1 * х + 1 = 2 * х + 1, вместо нужной (х + 1) * (х + 1).
Поэтому этот макрос нужно определять следующим образом:
#define sqr(x) (x) * (x).
Отсюда следует простой вывод – скобок не жалеть...
РЕСУРСЫ ОВМ х51 13

1.2. Ресурсы ОВМ х51

1.2.1. Общие сведения


Программы на Си для ОВМ х51 и «большого» процессора по боль-
шому счету почти не отличаются. Но особенности системы команд
и скромные возможности ОВМ х51, ориентированной на работу
с объектами в режиме реального времени, конечно же, учитывают-
ся компилятором. Но эти различия в большинстве случаев скрыты
от программиста, который организует выполнения заданного алго-
ритма типовыми средствами языка высокого уровня. Однако, во
избежание ошибок при написании программы, обязательно нужно
учитывать имеющиеся в наличии ресурсы ОВМ.
В первую очередь это касается нестандартных типов данных (см.
параграф 1.5) и внутренней структуры ОВМ, которая представля-
ется программисту в виде набора запоминающих ячеек, служащих
для реализации заданного алгоритма.

В компиляторе SDCC для семейства х51 определены три подобных


набора (модели) small, medium и large. Чаще всего по умолчанию
используется модель памяти small, в которой все переменные без
явного указания класса памяти размещаются во внутренней памя-
ти данных. В других моделях подобные переменные располагаются
компилятором во внешнем ОЗУ.

В базовом варианте ОВМ х51 [3] минимальный набор (small)


внутренних запоминающих ячеек состоит (см. рис. 1.1) из основного
ОЗУ (область data) и спецрегистров (область sfr – SPECIAL FUNCTION
REGISTERS). Сами коды команд размещаются в постоянной памяти
команд (область code), в которой программист может разместить и
константы (см. п. 1.3.4).
Спецрегистры управляют работой ОВМ и ее аппаратуры. Каждый
из спецрегистров имеет стандартное имя, определяемое во включа-
емом файле, и может использоваться в программах наравне с пере-
менными пользователя (см. п. 1.2.4).
В основном ОЗУ размещаются оперативные регистры общего назна-
чения (РОН), пользовательские переменные программы и стек, где
обычно хранятся адреса возврата из процедур и данные компилятора.
Дополнительное ОЗУ в семействе ОВМ х51 размещено параллель-
но адресному пространству спецрегистров. Чтобы не было путаницы,
14 ОСОБЕННОСТИ ПРОГРАММ НА ЯЗЫКЕ СИ

Рис. 1.1

в ассемблерных командах при работе со спецрегистрами использует-


ся прямая адресация, тогда как при обращении к дополнительному
ОЗУ – косвенная [3]. В программах на Си эта особенность допол-
нительного ОЗУ, которая свойственна адресации массивов, подчер-
кивается в его именовании idata первой буквой i (от indirect – кос-
венный).

1.2.2. Указание места размещения


переменных
Таким образом, перед объявлением типа данных и имени перемен-
ной можно (если это нужно программисту) дополнительно указать
место размещения переменной (тип памяти), учитывая особенности
структуры семейства х51:
• register – размещается в регистре;
• data – во внутреннем основном ОЗУ;
• idata – во внутреннем дополнительном ОЗУ (с косвенной ад-
ресацией);
• xdata – во внешнем ОЗУ;
• code – в памяти команд (для констант);
• extern – определяется в другом программном модуле.

Отметим, что есть и другие, не рассматриваемые здесь разновид-


ности типов памяти, связанные с аппаратными особенностями ОВМ
(страничная, энергонезависимая память и т.д.).
РЕСУРСЫ ОВМ х51 15

Примеры объявления переменных с различным типом памяти:


data unsigned char mCount10ms;
extern int mParol;
idata unsigned char mBufer[100];

1.2.3. Размещение локальных переменных,


используемых процедурами
По умолчанию компиляторы Си используют так называемые дина-
мические переменные, обозначаемые классом auto, которые не ини-
циализируются и удаляются после завершения процедуры.
Эти переменные должны размещаться в стеке, но стек в ОВМ х51
невелик, поэтому в большинстве случаев компиляторы размещают
локальные переменные в РОН (R0…R7), выбирая при необходимо-
сти один из банков RB0…RB3.

Отметим также, что регистры и стек ОВМ в явном виде програм-


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

1.2.4. Работа со спецрегистрами


Стандартные имена регистров аппаратуры, как отмечалось, объяв-
ляются в заголовочном файле применяемой разновидности ОВМ и
могут использоваться в программе наравне с переменными пользо-
вателя. Например:
Р1 = 0;
Р2 = mAdrH;
mKodFromPC = SBuf;
TL0 = (char)(mKodInt);

Полезно также помнить, что при сбросе почти все спецрегистры


обнуляются, за исключением регистров портов, которые устанавли-
ваются в единичное состояние.

Нужно отметить, что в типовом случае в области sfr определены


только битовые и байтовые стандартные переменные. Поэтому