ytgf6785f86rf  
Главная
Продукция
Партнеры
Статьи
О нас

Контакты

Ссылки

Написание и отладка программ для микроконтроллеров 51семейства

в среде VISION-2

Часть 3


Модульное программирование.
В двух предыдущих статьях в качестве примера фигурировала очень простая программа: PROGECT1. На этом примере было организовано первое знакомство со средой μVision-2. В то же время среда μVision-2 более всего интересна как раз тем, что значительно упрощает написание и отладку многомодульных программ, формализует связи между ними и делает наглядной и удобной работу с большим проектом.
Хочется не повторять слова из учебников и сделать повествование как можно более практичным и простым, однако невозможно сделать рассказ понятным и последовательным, не вводя (а, по сути, повторяя) некоторые определения. Итак, мы предполагаем, что наша программа будет состоять из нескольких небольших по величине и по возможности логически завершенных модулей, это могут быть подпрограммы, в идеале реализующие одну конкретную функцию.
Каждый программный модуль может обмениваться информацией с любым другим модулем, принимать и передавать её, а также предоставлять свои ресурсы другим модулям. И вот именно эти отношения должны быть оговорены в каждом программном модуле.
Ассемблерные инструкции, определяющие обмен информацией, желательно расположить в начале программы после её имени, до начала исполняемого текста программы.
В начале рассмотрим инструкции импорта информации. Если предполагается, что данный программный модуль будет использовать информацию из других модулей, то это необходимо определить инструкцией EXTRN.
В инструкции EXTRN перечисляются символы, которые объявлены в других модулях. Данная инструкция записывается в следующем виде
EXTRN класс (символ, символ…)
где
класс — класс памяти: BIT, CODE, IDATA, XDATA или NUMBER (для указания символов без определения типов данных), в котором определены, указанные далее в скобках символы (смотри таблицу 1).
символ — внешнее символьное имя, представляющее собой, по сути, адрес в памяти, тип которой определен классом.
Инструкция EXTRN CODE (code_symbol) предполагает ввод любой логически завершенной части программы, выделенной в тексте меткой code_symbol. Метка обычно (но не обязательно) описывает физический смысл данного фрагмента. Фактически метка указывает начальный адрес программы или её части, импортируемой из других модулей.
В самом простом случае, это могут быть какие либо подпрограммы, однажды сделанные, отлаженные, а позже успешно забытые.
В инструкцию EXTRN могут быть включены через запятую сразу несколько адресов (подпрограмм из других модулей):
EXTRN CODE (code_symbol1, code_symbol2).
Инструкция EXTRN DATA (data_symbol) предполагает ввод в данный программный модуль символов, определенных в сегментах данных других модулей. Тем самым создается совместно используемое адресное пространство в ОЗУ микроконтроллера. Фактически data_symbol указывает адрес оперативной памяти, где хранятся константы или переменные, определенные в другом модуле.
Для символов каждого типа памяти в текст модуля необходимо записать свою инструкцию EXTRN:
EXTRN BIT (bit_symbol) — для битовых переменных и констант.
EXTRN XDATA (xdata_symbol) — для символов и переменных, хранящихся во внешней памяти, например это могут быть калибровочные коэффициенты для вашего прибора.
Инструкция EXTRN NUMBER (typeless_symbol) позволяет использовать в данном модуле символ typeless_symbol, объявленный инструкцией SET или EQU в другом программном модуле.
Далее предлагается рассмотреть инструкции импорта информации, то есть, как организуется обмен между модулями в обратном направлении.
Фрагменты программы, данные, хранящиеся в ОЗУ (как байтовой, так и битовой областей) микроконтроллера, данные во внешней памяти, определенные в данном программном модуле, если это необходимо, нужно сделать доступными для использования в других модулях.
Выполняется данная операция с помощью инструкции PUBLIC.
PUBLIC data_variable — делает доступной для использования в других модулях переменную data_variable, определенную в памяти данных.
PABLIC code_entry — делает доступным для использования в другом модуле фрагмента программы данного модуля, отмеченного меткой code_entry.
PUBLIC typeless_number — разрешает использовать в других модулях символ typeless_number, определенный инструкцией SET или EQU в данном модуле.
PUBLIC xdata_variable — делает возможным использование переменной xdata_variable, хранящейся во внешней памяти и определенной в нашем текущем модуле.
Аналогичная инструкция PUBLIC bit_variable имеется и для битовых переменных.
В инструкции PUBLIC можно перечислять через запятую несколько символов, определенных в данном модуле и предназначенных для общего использования:
PABLIC data_variable1, code_table, typeless_num1, xdata_variable1.
Следует отметить парность инструкций PABLIC и EXTRN, то есть, если в данном модуле какая либо переменная вводится с помощью инструкции EXTRN, то обязательно в другом модуле она должна быть открыта для использования инструкцией PUBLIC.
Теперь авторы просят уважаемых читателей набраться еще одной порцией терпения и рассмотреть фрагменты текста, не имеющие непосредственного отношения к какой либо конкретной программе, но необходимые для последующей интеграции в другие проекты. Постараемся быть по возможности краткими. Речь идет о резервировании адресных пространств микроконтроллера и выделении отдельных сегментов в различных типах памяти. Частично этот вопрос был затронут в первой статье, в программе PROGECT1.
Сегментация осуществляется отдельно для каждого типа памяти. Ниже в таблице 1 перечислены классы (типы) памяти микроконтроллеров 51 семейства и допустимые для них методы адресации.


 

Класс памяти Допустимые методы адресации
data (встроенное ОЗУ в диапазоне 0H…07FH) прямая и косвенная адресация
bit (встроенное ОЗУ в диапазоне 20H…02FH) прямая и косвенная адресация; к отдельным битам можно обращаться посредством битовой адресации
idata (дополнительное встроенное ОЗУ 8052 в диапазоне 080H…0FFH) только косвенная адресация
sfrs (область памяти для регистров специального назначения, в некоторых из них возможна битовая адресация) только прямая адресация
xdata (внешняя память данных) инструкции MOVX
code (память программ) чтение инструкций (в некоторых микроконтроллерах допускается запись)



Таблица 1.

Но уже здесь необходимо высказать предостережение: нельзя делать объявления подряд всех сегментов, так сказать «бить по площадям». Следует определять только те сегменты, которые действительно необходимы в данном модуле.
Далее рассмотрим инструкции, применяемые при объявлении разных типов сегментов.
Сегмент стека помещается в основной модуль:

?STACK SEGMENT IDATA
RSEG ?STACK
DS 5 ; резервирование пяти байт под стек


 


Необходимо учесть, что этого может оказаться мало.
Если в данном модуле используются переменные, хранящиеся в ОЗУ микроконтроллера (например, DATA_VAR1 и DATA_VAR2), то в тексте программы необходимо ввести следующие строки:

NAME1 SEGMENT DATA
RSEG NAME1
DATA_VAR1: DS 1
DATA_VAR2: DS 2


где NAME1 — имя сегмента в ОЗУ микроконтроллера,
DATA_VAR1 — метка, указывающая адрес переменной DATA_VAR1, размером в один байт.
DATA_VAR2 — метка, указывающая адрес переменной DATA_VAR2, размером в два байта.
Согласно инструкции NAME1 SEGMENT DATA, конкретное место размещения наших переменных DATA_VAR1 и DATA_VAR2 будет определено компоновщиком, поскольку сегмент объявлен перемещаемым. Инструкция RSEG NAME1 открывает сегмент данных NAME1.
Следующие две группы инструкций касаются внешней памяти и битовой области ОЗУ микроконтроллера. Они также должны присутствовать в программе, только в случае необходимости в них, и аналогичны предыдущим, поэтому приводятся без комментариев.

NAME2 SEGMENT XDATA
RSEG NAME2
XDATA1: DS 3
XDATA_ARRAY: DS 500
BIT_NAME SEGMENT BIT
RSEG BIT_NAME
BIT_VAR1: DBIT 1
BIT_VAR2: DBIT 4


Для резервирования строго определенного места памяти во внешнем ОЗУ применяется директива резервирования с указанием абсолютного адреса (это может понадобиться для организации ввода-вывода).

XSEG AT 4000H
XIO1: DS 12
XIO2: DS 16H


Назначение имен констант желательно также произвести в начале Вашей программы, используя следующие инструкции:
 

CONST1 EQU 0CH ; CONST1 присваивается значение 0CH
CLK EQU P0.4 ; CLK присваивается значение четвертого бита  порта Р0


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

CSEG AT OBH ; Таймер 0
JMP Timer0 ; Переход на программу обработки прерывания
CSEG AT 1BH ; Таймер1
RETI
CSEG AT 0 ; старт программы после сброса микроконтроллера



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

PROG SEGMENT CODE
RSEG PROG



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

CONST SEGMENT CODE
RSEG CONST
TXT: DB 'TEST PROGRAM',00H



Не слитное написание фраз (слов) в выше приведенных фрагментах программ означает наличие в этих местах программы хотя бы одного пробела или, что удобнее табуляции. Вообще инструкции х51 ассемблера не чувствительны к месту расположения в строке и могут начинаться с любого столбца. Единственное исключение касается параметров и операндов, которые должны быть отделены от директив и инструкций хотя бы одним пробелом. Любая метка в программе должна располагаться в первой позиции строки и заканчиваться двоеточием.
Все модули (ассемблерные программы) должны включать директиву END, указывающую ассемблеру на конец программы модуля.
В качестве примера, поясняющего принципы написания многомодульного проекта, предлагается рассмотреть комплекс программ, состоящий из трех модулей и предназначенный для организации обмена по последовательному порту RS-232.
Алгоритм работы проекта можно описать в виде следующей последовательности действий:
1. Определение внешних компонентов программы.
2. Назначение общих переменных.
3. Определение перемещаемых сегментов проекта.
4. Определение абсолютных сегментов проекта.
5. Настройка таймера 1 для работы с портом RS-232.
6. Загрузка в DPTR начального адреса передаваемой последовательности кодов.
7. Вызов подпрограммы передачи последовательности символов из аккумулятора через RS-232.
8. Подпрограмма STRING_IO в свою очередь при передаче каждого бита символа вызывает подпрограмму CHAR_IO для передачи байта через RS-232.
Алгоритм работы этих подпрограмм очевиден, а дополнительные комментарии в их тексте делают его рассмотрение излишним.

Модуль 1 (основной).
 

NAME SAMPLE ; Имя модуля
EXTRN CODE (PUT_CRLF, PUTSTRING)
; Адреса используемых подпрограмм
PUBLIC TXTBIT ; Объявление общей переменной
PROG SEGMENT CODE ; Создание сегментов
CONST SEGMENT CODE
VAR1 SEGMENT DATA
BITVAR SEGMENT BIT
STACK SEGMENT IDATA
RSEG STACK; Открытие сегмента стека
DS 10H ; Резервирование 16 байт для стека
CSEG AT 0 ; Создание абсолютного стартового сегмента с адреса 0
USING 0 ; Назначение нулевого банка регистров
JMP START
RSEG PROG ; Открытие сегмента кода
START: MOV SP,#STACK–1
; Установка указателя стека (следует обратить внимание на обязательное наличие этой
; команды в программе (обычно это основной модуль), стартующей с нулевого адреса,
; каждого проекта)
; Инициализация последовательного интерфейса
; Используем TIMER 1 для формирования частоты передачи
; Частота генератора = 11.059 MHz
MOV TMOD,#00100000B ;C/T = 0, Mode = 2
MOV TH1,#0FDH
SETB TR1
MOV SCON,#01010010B
; сброс TXTBIT при чтении из памяти программ
CLR TXTBIT
REPEAT: ; Цикл передачи информации по RS-232
MOV DPTR,#TXT
CALL PUTSTRING
CALL PUT_CRLF
SJMP REPEAT
RSEG CONST ; Открытие сегмента CONST в памяти кодов
TXT: DB 'TEST PROGRAM',00H
RSEG VAR1 ; Открытие сегмента VAR1 в памяти данных
; в данной программе не используется
DUMMY: DS 10H
; TXTBIT = 0 чтение текста из памяти программ
; TXTBIT = 1 чтение текста из внешней памяти
RSEG BITVAR
; Открытие сегмента в битовой области памяти данных
TXTBIT: DBIT 1
END
 




Модуль 2
 

NAME STRING_IO ; Имя модуля
EXTRN BIT (TXTBIT) ; Использование внешней битовой переменной
; Эта переменная объявлена в основном модуле
EXTRN CODE (PUTCHAR) ; Адрес используемой подпрограммы
PUBLIC PUT_CRLF, PUTSTRING
; Объявление подпрограмм для общего использования
STRING_ROUTINES SEGMENT CODE ; Создание сегмента программы
RSEG STRING_ROUTINES
; Открытие сегмента программы STRING_ROUTINES
; Назначение символьных имен
CR EQU 0DH ; возврат каретки
LF EQU 0AH ; перевод строки

PUT_CRLF:
MOV A,#CR
CALL PUTCHAR
MOV A,#LF
CALL PUTCHAR
RET
; Подпрограмма вывода строки, оканчивающейся символом конец строки,
; адрес подпрограммы расположен в DPTR. Строка может находиться в памяти
; программ или во внешней памяти в зависимости от значения бита TXTBIT.

PUTSTRING:
JB TXTBIT,PS1
CLR A
MOVC A,@A+DPTR
SJMP PS2
PS1: MOVX A,@DPTR
PS2: JZ EXIT
CALL PUTCHAR
INC DPTR
SJMP PUTSTRING
EXIT: RET
END
 


Модуль 3
 

NAME CHAR_IO
PUBLIC PUTCHAR
CHAR_ROUTINES SEGMENT CODE
VAR2 SEGMENT DATA
RSEG CHAR_ROUTINES

; Подпрограмма вывода очередного символа в последовательный порт
; Микроконтроллера из аккумулятора
PUTCHAR:
JNB TI,$
CLR TI
MOV SBUF,A
RET
RSEG VAR2 ; Открытие сегмента VAR2 в памяти данных
; в данной программе не используется
DUMMY: DS 40H
END

 




В выше изложенном тексте не раз упоминалось, что компилятор сам определяет месторасположение созданных нами сегментов программ, данных, стека и так далее. Иногда возникает необходимость, например при отладке, узнать конкретное место расположения программных переменных. Для этого можно воспользоваться созданным средой VISION-2 в процессе компиляции файлом с расширением *.М51. Рассмотрим фрагмент файла.
 

TYPE BASE LENGTH RELOCATION SEGMENT NAME
-----------------------------------------------------

* * * * * * * D A T A M E M O R Y * * * * * * *
REG 0000H 0008H ABSOLUTE "REG BANK 0"
DATA 0008H 0021H UNIT VAR1
BIT 0029H.0 0000H.1 UNIT BITVAR
0029H.1 0000H.7 *** GAP ***
DATA 002AH 0040H UNIT VAR2
IDATA 006AH 0010H UNIT STACK

* * * * * * * C O D E M E M O R Y * * * * * * *
CODE 0000H 0003H ABSOLUTE
CODE 0003H 001CH UNIT STRING_ROUTINES
CODE 001FH 001BH UNIT PROG
CODE 003AH 000DH UNIT CONST
CODE 0047H 0008H UNIT CHAR_ROUTINES


 


В этом фрагменте представлена информация о конкретных адресах сегментов памяти, типах этих сегментов, их именах, объявленных в рассматриваемой программе.
Возможности среды разработки μVision-2, рассмотренные в данных трех статьях раскрыты лишь частично. Но это основная информация необходимая для перехода от DOS к WINDOWS ориентированным средам разработки. Фирма Keil, разработчик среды проектирования μVision-2, является мировым лидером в области производства программных продуктов для микроконтроллеров, что является некоторой гарантией минимального количества внутренних ошибок (багов) в среде разработки.
Нам представляется особенно важным иметь под рукой такой инструмент, как программный эмулятор, входящий в среду проектирования μVision-2, именно на первом этапе освоения микроконтроллеров. Во время этого периода деятельности, через который, кстати, проходят все, познания оставляют желать много лучшего, опыта нет и инструментария тоже нет, а каждый шаг вызывает огромное количество вопросов.
Авторы считают, что дополнительная практическая польза среды проектирования μVision-2 заключена в ее огромном потенциале, как великолепной учебной программы по освоению микроконтроллеров, и надеются, что данный цикл статей явится также и вводным курсом в этом направлении, и они станут стартовой площадкой для быстрого перехода к высокопроизводительной среде разработки.




Авторы:
Андрей Сошкин
Андрей Мамонтов