- •Цифровые сигнальные процессоры
- •Содержание
- •1. Архитектура adsp-2181
- •1.1. Описание выводов процессора
- •1.2. Структурная схема adsp-2181
- •Генераторы адресов данных dag1 и dag2, регистр команд instruction register и программный конвейер program sequencer.
- •1.3. Память программ
- •1.4. Память данных
- •1.5. Функциональная схема alu
- •1.6. Функциональная схема мас
- •1.7. Функциональная схема shifter
- •Значения выходного кода сдвигателя при различных значениях
- •1.8. Функциональная схема программного конвейера
- •1.9. Система прерываний
- •1.9.3. Конфигурирование прерываний.
- •1.10. Регистры состояния и стек состояния
- •1.11. Передача данных
- •1.11.1. Генераторы адресов
- •1.11.2. Узел обмена данными
- •1.12. Последовательные порты
- •1.12.1. Общие сведения
- •1.12.2. Программирование sport
- •Функциональное назначение конфигурационных регистров
- •1.12.3. Пример конфигурирования последовательных портов
- •{Программа инициализации sport0, sport1}
- •1.12.4. Сжатие данных
- •1.12.5. Автобуферизация
- •1.12.6. Пример программирования автобуферизации
- •1.12.7. Многоканальность
- •1.13. Таймер
- •1.14. Системный интерфейс
- •1.14.1. Сигналы синхронизации
- •Состояние регистров процессора после сброса и перезагрузки
- •Состояние регистров bdma после сброса и перезагрузки
- •1.14.2. Внешние прерывания
- •1.14.3. Флажковые биты
- •1.14.4. Режим энергосбережения
- •1.15. Контроллер прямого доступа к байтовой памяти (bdma)
- •1.15.1. Общие сведения
- •1.15.2. Регистры управления bdma
- •1.15.3. Функционирование bdma
- •1.15.4. Загрузка программ с помощью bdma
- •1.16. Порт idma
- •1.16.1. Сигналы idma
- •1.16.2. Функционирование idma
- •1.16.3. Загрузка программ с помощью idma
- •1.17. Система команд
- •1.17.1. Общие сведения
- •1.17.2. Методы адресации
- •1.17.3. Условные обозначения
- •1.17.4. Команды пересылки данных
- •1.17.5. Команды alu
- •Б) вычитание X-y/вычитание X-y с заемом
- •1.17.6. Команды мас
- •1.17.7. Команды shifter
- •Допустимые регистры xop и условия cond см. П. 1.17.7.А.
- •Допустимые регистры xop и условия cond см. П. 1.17.7.А.
- •Допустимые регистры xop и условия cond см. П. 1.17.7.А.
- •Допустимые регистры xop и условия cond см. П. 1.17.7.А.
- •Допустимые регистры xop см. П. 1.17.7.А.
- •Допустимые регистры xop см. П. 1.17.7.А.
- •1.17.8. Команды управления потоком программы
- •Допустимые условия cond:
- •Допустимые условия cond см. П. 1.17.8.А.
- •1.17.9. Многофункциональные команды
- •1.17.10. Прочие команды
- •1.18. Инструментальные средства разработки программного обеспечения
- •1.18.1. Инструментальные средства для dos
- •1.18.2. Инструментальные средства для windows
- •1.18.3. Создание проекта в VisualDsp
- •1.18.4. Загрузка программы в ez-kit Lite
- •1.19. Примеры программирования в среде VisualDsp
- •1.19.1. Формирование эхо-сигнала
- •Architecture(adsp-2181) // определение типа процессора
- •1.19.2. Эмуляция интерфейса rs-232
- •1.19.3. Эмуляция интерфейса rs-232 (смешанный вариант)
- •Architecture(adsp-2181) // определение типа процессора
- •2. Архитектура системы на основе adsp-2181
- •2.1. Структурная схема вычислительной (управляющей)
- •2.2. Цепи синхронизации и запуска процессора
Architecture(adsp-2181) // определение типа процессора
SEARCH_DIR( $ADI_DSP\218x\lib ) // определение пути к библиотекам
// Имена файлов, библиотек, ключей берутся из командной строки
$OBJECTS = $COMMAND_LINE_OBJECTS;
// 2181 имеет 16K слов (24-bit) ПП и 16K слов (16-bit) ПД
MEMORY {
// Используется вся доступная область ПП и ПД
prg_area {TYPE(PM RAM) START(0x0000) END(0x37fe) WIDTH(24)}
data_area {TYPE(DM RAM) START(0x0000) END(0x3dfe) WIDTH(16)}
}
PROCESSOR p0 {
LINK_AGAINST( $COMMAND_LINE_LINK_AGAINST )
// Имя выходного файла берется из командной строки
OUTPUT( $COMMAND_LINE_OUTPUT_FILE )
SECTIONS {
// Определение программной секции
sec_code {
// "program_sect" – имя программной секции в файле echo.asm
INPUT_SECTIONS( $OBJECTS(program_sect) )
} >prg_area // см. выше MEMORY
// Определение секции данных
sec_data {
// "data_sect" – имя секции данных в файле echo.asm
INPUT_SECTIONS( $OBJECTS(data_sect))
} >data_area // см. выше MEMORY
}// Здесь заканчивается область действия SECTIONS
}// Здесь заканчивается область действия PROCESSOR p0
Рис. 1.16. Содержимое линкерного файла echo.ldf, входящего в проект echo
Существенными для программиста в линкерном файле являются разделы MEMORY и PROCESSOR. Первый – позволяет обозначить символическими именами различные области физической ПП или ПД. В нашем случае области физической ПП с адресами 0x0000…0x37fe присвоено имя prg_area, а области физической ПП с адресами 0x0000…0x3dfe присвоено имя data_area.
Второй – позволяет располагать (с помощью директивы SECTION) в различных областях физической ПП или ПД различные программные секции и секции данных. В нашем случае программная секция program_sect будет отнесена с области памяти prg_area, т. е. начинаться с нулевого адреса в ПП, а секция данных data_sect будет отнесена с области памяти data_area, т. е. начинаться с нулевого адреса в ПД.
1.19.2. Эмуляция интерфейса rs-232
Задание: с помощью EZ-KIT Lite программно эмулировать работу коммуникационного интерфейса RS-232 в двух вариантах: с использованием флагов FLAG_IN, FLAG_OUT; с использованием битов PF[1], PF[2].
Метод решения: адаптировать для VisualDSP программу uart.dsp, которая входит в состав монитора EZ-KIT Lite (программные средства для DOS (см. п. 1.18.1)).
Задача актуальна тем, что процессор ADSP-2181 не содержит асинхронного коммуникационного порта, который мог бы поддерживать очень распространенный интерфейс типа RS-232. Приведенная программа позволяет устранить этот недостаток.
Создадим проект с именем async, в который входят два файла: async.asm и async.ldf. Первый из них является файлом программы, а второй – линкерным файлом. К файлу программы подключается файл (Reg2181.inc) определений адресов регистров процессора ADSP-2181. Эмулируемое устройство назовем UART. Программа (async.asm) поддержки UART приведена на рис. 1.17.
//---------- Содержимое файла async.asm
#include <Reg2181.inc>;
// Частота кварцевого резонатора в КГц
#define CRYSTAL_FREQ_IN_kHZ 16667
{
Для поддержки интерфейса RS-232 ADSP-2181 в составе EZ-KIT Lite использует ресурсы FLAG_IN, FLAG_OUT и TIMER следующим образом:
ADSP-2181 FLAG_OUT ----------> AD233 ----------> RS-232 TX
ADSP-2181 FLAG_IN <----------- AD233 <---------- RS-232 RX
(TIMER определяет скорость обмена)
Или (в зависимости от определения HOST):
ADSP-2181 PF[2] ----------> AD233 ----------> RS-232 TX
ADSP-2181 PF[1] <---------- AD233 <---------- RS-232 RX
Формат посылки (число информационных бит, число стоп бит, наличие/отсутствие контроля по четности) и скорость обмена задаются программно. Преобразование логических уровней сигналов RxD и TxD в уровни интерфейса RS-232 производится микросхемой AD233.
Подпрограммы инициализации передатчика и приемника функционируют независимо друг от друга. Передача и прием данных синхронизируются сигналами таймера. Это не мешает производить одновременный асинхронный (по отношению друг к другу) прием/передачу. По умолчанию приемник заблокирован (выключен), поэтому перед его использованием необходимо вызвать подпрограмму "turn_rx_on".
Используемые подпрограммы:
init_uart Должна быть вызвана после сброса системы.
get_char_ax1 Ожидание приема байта с линии RX и возврат
его через ax1.
out_char_ax1 Ожидание текущей выдачи байта через линию
TX и выдача следующего байта из ax1.
turn_rx_on Разрешение работы приемника.
turn_rx_off Запрет работы приемника.
Используемый флаг: DM(flag_rx_ready)
Если flag_rx_ready содержит все единицы, то это означает, что UART готов получать данные. Если flag_rx_ready содержит все нули, то это означает, что UART уже получает данные. Может использоваться для управления потоком данных по протоколу xon, xoff.
}
// Если определено символическое имя HOST, то используются флаги
// FLAG_IN, FLAG_OUT, в противном случае биты PF[1], PF[2]
#define HOST
// Следующие константы могут быть изменены для установки новых
// параметров UART
#define tx_num_of_bits 10 // старт бит + информ. биты + стоп биты
#define rx_num_of_bits 8 // информ. биты (старт&стоп биты не
// считаются)
#define RX_BIT_ADD 0x0100 // = 1<<rx_num_of_bits
#define TX_BIT_ADD 0xfe00 // = 0xffff<<(информационные биты + 1)
#define Baud 9600 // скорость обмена в Бод
// PERIOD == 1144
{
PERIOD - период прерываний таймера (в 3 раза меньше битового интервала). Коэффициент 2000 переводит частоту кварцевого резонатора в КГц в частоту внутренней синхронизации ADSP-2181 в Гц.
}
#define PERIOD (CRYSTAL_FREQ_IN_kHZ * 2000 / (3 * Baud)) – 1
// Объявление секции данных
.SECTION/DM UART_DATA;
.VAR flag_rx_off; // индицирует выключенное состояние приемника
.VAR flag_tx_ready; // 1 - передачи нет, 0 - передача есть
.VAR flag_rx_ready; // 1 - приема нет, 0 - прием есть
.VAR flag_rx_stop_yet; // 1 - стоп бит не достигнут, 0 - достигнут
.VAR flag_rx_no_word; // 0 - принятый символ находится в user_rx_buffer,
// в противном случае - 1
.VAR timer_tx_ctr; // делитель на 3 передатчика, таймер работает
// на частоте 3 x скорость обмена
.VAR timer_rx_ctr; // делитель на 3 приемника, таймер работает
// на частоте 3 x скорость обмена
.VAR user_tx_buffer; // регистр данных передатчика UART
.VAR user_rx_buffer; // регистр данных приемника UART
.VAR internal_tx_buffer; // сдвиговый регистр передатчика, именно в нем
// форматируется посылка (добавляются старт и
// стоп биты), в него перед отправкой
// копируется содержимое user_tx_buffer
.VAR internal_rx_buffer; // сдвиговый регистр приемника
.VAR bits_left_in_tx; // число оставшихся для передачи битов
.VAR bits_left_in_rx; // число оставшихся для приема битов
// Объявление программной секции
.SECTION/PM UART_CODE;
jump start;
rti; rti; rti;
rti; // вектор прерывания - IRQ2
rti; rti; rti;
rti; // вектор прерывания - IRQL1
rti; rti; rti;
rti; // вектор прерывания - IRQL0
rti; rti; rti;
rti; // вектор прерывания - SPORT0 tx
rti; rti; rti;
rti; // вектор прерывания - SPORT0 rx
rti; rti; rti;
rti; // вектор прерывания - IRQE
rti; rti; rti;
rti; // вектор прерывания - BDMA
rti; rti; rti;
rti; // вектор прерывания - SPORT1 tx или IRQ1
rti; rti; rti;
rti; // вектор прерывания - SPORT1 rx или IRQ0
rti; rti; rti;
jump process_a_bit; // вектор прерывания - timer
rti; rti; rti;
rti; // вектор прерывания - power down
rti; rti; rti;
start:
call init_uart; // инициализация UART
call turn_rx_on; // разрешение приема данных
next_byte:
call get_char_ax1; // ожидание и прием символа из UART
call out_char_ax1; // выдача принятого символа в UART
jump next_byte;
//---------- Подпрограмма инициализации
{
Использование регистров:
вход: нет
обновленные: imask
выход: нет
измененные: ar, ax0, ay0
сохраняемые: нет
память: dm (TSCALE)=0, dm(flag_tx_ready)=1,
dm(flag_rx_ready)=1, dm(flag_rx_stop_yet)=1,
dm(flag_rx_no_word)=1, dm(flag_rx_off)=1
flag_out/PF[2]
вызовы: нет
}
init_uart:
ax0=0;
dm(TSCALE)=ax0; // декремент TCOUNT каждый
// машинный цикл
ax0=PERIOD; // установка скорости обмена (9600 Бод)
dm(TCOUNT)=ax0;
dm(TPERIOD)=ax0; // прерывания генерируются с тройной
// частотой
ax0=1;
dm(flag_tx_ready)=ax0; // передачи нет
dm(flag_rx_ready)=ax0; // установки режима по умолчанию
// (UART занят)
dm(flag_rx_stop_yet)=ax0;
dm(flag_rx_no_word)=ax0;
dm(flag_rx_off)=ax0; // приемник выключен
#ifdef HOST
set flag_out; // TxD = 1
#else
ax0 = dm(PFTYPE); // PF[2] - TxD
ar = setbit 2 of ax0;
dm(PFTYPE) = ar;
ax0 = dm(PFDATA); // TxD = 1
ar = setbit 2 of ax0;
dm(PFDATA) = ar;
#endif
ifc = b#00000011111111; // очистка запросов прерываний
nop; // ожидание очистки
ax0 = b#0000100000000000;
ay0 = dm (System_Control_Reg);
ar = ax0 or ay0; // разрешение SPORT1
ay0 = b#1111101111111111;
ar = ar and ay0; // очистка для разрешения FI, и т.д.
dm (System_Control_Reg) = ar;
ax0=imask;
ay0=b#0000000001;
ar=ax0 or ay0;
imask=ar; // разрешение прерываний TIMER
ena timer; // запуск TIMER
rts;
//---------- Подпрограмма останова UART
{
Использование регистров:
вход: нет
обновленные: imask
выход: нет
измененные: ar, ay0
сохраняемые: нет
память: нет
flag_out/PF[2]
вызовы: нет
}
stop_uart:
dis timer; // останов TIMER
#ifdef HOST
set flag_out; // TxD = 1
#else
ax0 = dm(PFDATA); // TxD = 1
ar = setbit 2 of ax0;
dm(PFDATA) = ar;
#endif
ifc = b#00000011111111; // очистка запросов на прерывания
nop; // ожидание очистки
ar=imask;
ay0=b#1111111110;
ar=ar and ay0;
imask=ar; // запрет прерываний TIMER
rts;
//---------- Обработчик прерываний TIMER
{
Эта подпрограмма является основной частью UART. Она вызывается запросом на прерывание TIMER, т.е. с частотой в три раза большей частоты обмена. Подпрограмма формирует выходной бит данных установкой/сбросом сигнала на выходе FLAG_OUT/PF[2] ADSP-2181.
Далее проверяет наличие режима приема данных. Если таковой присутствует, то к принимаемому данному пристыковывается очередной бит с входа FLAG_IN/PF[1] ADSP-2181, после чего входные данные сдвигаются на один разряд вправо.
Если режим приема данных отсутствует, то проверяется старт бит. Поскольку интервальный таймер работает на тройной частоте скорости обмена, то прием/ передача возможны в каждое третье прерывание.
}
{
Использование регистров:
вход: нет
обновленные: нет
выход: нет (вторичный банк регистров)
измененные: нет
сохраненные: нет
память: dm(flag_tx_ready), dm(timer_tx_ctr), dm(internal_tx_buffer),
dm(bits_left_in_tx), dm(flag_rx_off), dm(flag_rx_stop_yet),
dm(timer_rx_ctr), dm(flag_rx_ready), dm(internal_rx_buffer),
dm(flag_rx_no_word), dm(bits_left_in_rx)
flag_out/PF[2]
вызовы: нет
}
process_a_bit:
ena sec_reg; // подключение вторичного банка регистров
ax0=dm(flag_tx_ready); // если не передача, то переход на прием
none=pass ax0;
if ne jump receiver;
// Передающая секция
ay0=dm(timer_tx_ctr); // проверка timer_tx_ctr на 0,
ar=ay0-1; // если не 0, то декремент, сохранение
dm(timer_tx_ctr)=ar; // и переход на прием, в противном
if ne jump receiver; // случае передача
sr1=dm(internal_tx_buffer);
// Сдвиг вправо младшего значащего разряда internal_tx_buffer в SR1.
// В SR0 этот бит является знаковым, в соответствии с ним
// FLAG_OUT/PF[2] сбрасывается или устанавливается
sr=lshift sr1 by -1 (hi);
dm(internal_tx_buffer)=sr1;
ar=pass sr0;
#ifdef HOST
if ge reset flag_out;
if lt set flag_out;
#else
ax0 = dm(PFDATA);
if ge ar = clrbit 2 of ax0;
if lt ar = setbit 2 of ax0;
dm(PFDATA) = ar;
#endif
ay0=3; // восстановление timer_tx_ctr, т.е.
dm(timer_tx_ctr)=ay0; // следующий бит будет передан
// через три прерывания
ay0=dm(bits_left_in_tx); // декремент оставшихся для передачи
ar=ay0-1; // битов
dm(bits_left_in_tx)=ar;
if gt jump receiver; // если биты остались, то переход на прием
ax0=1; // в противном случае - сброс флага
dm(flag_tx_ready)=ax0; // необходимости передачи
// Приемная секция
receiver:
ax0=dm(flag_rx_off); // приемник включен?
none=pass ax0;
if ne rti; // если нет, то выход
ax0=dm(flag_rx_stop_yet);// проверка приема стоп бита,
none=pass ax0; // если нет, то переход на прием
if ne jump rx_test_busy; // предыдущих битов
ay0=dm(timer_rx_ctr); // стоп бит достигнут, декремент
ar=ay0-1; // timer_rx_ctr, если не 0, то выход
dm(timer_rx_ctr)=ar; // (выдержка битового интервала)
if ne rti;
ax0=1; // битовый интервал стоп бита выдержан,
dm(flag_rx_stop_yet)=ax0;// восстановление flag_rx_stop_yet,
dm(flag_rx_ready)=ax0; // сброс flag_rx_ready,
ax0=dm(internal_rx_buffer);// копирование принятого символа из
dm(user_rx_buffer)=ax0; // internal_rx_buffer в user_rx_buffer
ax0=0; // индикация нахождения принятого слова
dm(flag_rx_no_word)=ax0;// в user_rx_buffer
rti;
rx_test_busy:
ax0=dm(flag_rx_ready); // проверка наличия режима приема,
ar=pass ax0; // если прием идет, то переход на прием
if eq jump rx_busy; // очередного бита, в противном случае -
// проверка старт бита
#ifdef HOST
if flag_in jump rx_exit; // если flag_in=1, то старт бита нет –
// выход
#else
ax0=dm(PFDATA); // если PF[1]=1, то старт бита нет –
ay0 = 2; // выход
ar = ax0 and ay0;
if ne jump rx_exit;
#endif
ax0=0;
dm(flag_rx_ready)=ax0 // в противном случае, устанавливаем
dm(internal_rx_buffer)=ax0; // флаг режима приема и очищаем
// приемный регистр
ax0=4; // первый бит необходимо проверить через
dm(timer_rx_ctr)=ax0; // 4 прерывания, чтобы проверка
// пришлась на середину его битового
// интервала
ax0=rx_num_of_bits; // инициализация числа оставшихся
dm(bits_left_in_rx)=ax0; // для приема битов
rx_exit:
rti;
rx_busy:
ay0=dm(timer_rx_ctr);// декремент timer_rx_ ctr и проверка его
ar=ay0-1; // на 0, если не 0, то битовый интервал
dm(timer_rx_ctr)=ar; // не выдержан - выход
if ne rti;
rcv:
// Битовый интервал выдержан, прием очередного бита
ax0=3; // восстановление счетчика бит. интервала
dm(timer_rx_ctr)=ax0; // проверка следующего бита
// произойдет через три прерывания
ay0=RX_BIT_ADD;
ar=dm(internal_rx_buffer);
#ifdef HOST
// Если flag_in=0, то переход на сдвиг
if not flag_in jump pad_zero;
#else
ax0 = dm(PFDATA); // если PF[1] =0, то переход на сдвиг
ay1 = 2;
af = ax0 and ay1;
if eq jump pad_zero;
#endif
// подстыковка 1
ar=ar+ay0;
pad_zero:
sr=lshift ar by -1 (lo); // сдвиг вправо результата для
dm(internal_rx_buffer)=sr0; // подготовки к приему следующего
// бита
ay0=dm(bits_left_in_rx); // декремент счетчика оставшихся
ar=ay0-1; // битов, если осталось один или
dm(bits_left_in_rx)=ar; // более битов, то выход (остаемся в
if gt rti; // режиме приема), в противном
// случае – осталось сформировать
// стоп бит
ax0=3; // восстановление счетчика битового интервала
dm(timer_rx_ctr)=ax0;
ax0=0; // установка флага стоп бита
dm(flag_rx_stop_yet)=ax0;
rti;
//---------- Выдача содержимого user_tx_buffer в канал UART
{
Использование регистров:
вход: нет
обновленные: нет
выход: нет
измененные: ar, ax0, sr
сохраненные: нет
память: dm(timer_tx_ctr), dm(bits_left_in_tx), dm(user_tx_buffer),
dm(internal_tx_buffer), dm(flag_tx_ready)
вызовы: нет
}
invoke_UART_transmit:
ax0=3; // инициализация делителя частоты
dm(timer_tx_ctr)=ax0; // прерываний, это необходимо из-за
// того, что частота прерываний в 3 раза
// больше частоты обмена
ax0=tx_num_of_bits; // инициализация числа передаваемых
dm(bits_left_in_tx)=ax0;// битов, включая стоп бит и бит
// четности, определяется программистом
{
Формирование кода в internal_tx_buffer в следующем формате:
15 |
14 |
13 |
12 |
11 |
10 |
09 |
08 |
07 |
06 |
05 |
04 |
03 |
02 |
01 |
00 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
d7 |
d6 |
d5 |
d4 |
d3 |
d2 |
d1 |
d0 |
0 |
|
|
|
|
|
|
T |
Передаваемый байт |
S |
|||||||
где: Т - стоп бит, S - старт бит.
}
sr1=0;
sr0=TX_BIT_ADD;
ar=dm(user_tx_buffer);
sr=sr or lshift ar by 1 (lo);
dm(internal_tx_buffer)=sr0;
ax0=0; // поднимаем флаг передачи
dm(flag_tx_ready)=ax0;
rts;
//---------- Прием символа из UART
{
Использование регистров:
вход: нет
обновленные: нет
выход: ах1 (старшие 8 бит всегда сброшены)
измененные: ax0
сохраненные: нет
память: dm(flag_rx_no_word), dm(user_rx_buffer),
dm(flag_rx_no_word)
вызовы: нет
}
get_char_ax1:
ax0=dm(flag_rx_no_word); // ожидание приема символа,
none=pass ax0; // должно выполниться условие
// flag_rx_no_word = 0
if ne jump get_char_ax1;
get_char_ax1x:
ax1=dm(user_rx_buffer); // принятый символ пересылаем в ах1
ax0=1;
dm(flag_rx_no_word)=ax0; // дезактивируем flag_rx_no_word
rts;
//---------- Прием символа из UART с тайм-аутом
{
Тайм-аут и возврат 0xffff после 500 mS.
(15000 ticks / 3 == 5000 bps ~= 5000 / 9600 ~= .5 S)
}
{
Использование регистров:
вход: нет
обновленные: нет
выход: ах1 (все 16 бит установлены, если тайм-аут)
измененные: ax0
сохраненные: нет
память: dm(flag_rx_no_word), dm(user_rx_buffer)
вызовы: нет
}
get_char_ax1_to:
ax1 = ar; // сохраняем ar
ar = 30000; // 3 тика на бит; 9600 bps, тайм-аут
// в 1 sec ==> 9600 * 3 == 30000
get_char_ax1_to1:
idle; // ожидание прерываний таймера
ar = ar - 1;
if eq jump get_char_ax1_to2; // тайм-аут достигнут, выход
ax0=dm(flag_rx_no_word);
none=pass ax0;
if ne jump get_char_ax1_to1; // ожидание приема символа
ar = ax1; // символ принят, восстановление ar
jump get_char_ax1x; // чтение принятого символа
get_char_ax1_to2:
ar = ax1; // восстановление ar
ax1 = 0xffff; // установка признака тайм-аута
rts;
//---------- Вывод символа через UART
{
Использование регистров:
вход: ах1 (сохраняется)
обновленные: нет
выход: нет
измененные: ax0, (ar, sr)
сохраненные: нет
память: dm(flag_tx_ready), dm(user_tx_buffer), (dm(timer_tx_ctr),
dm(bits_left_in_tx), dm(internal_tx_buffer))
вызовы: invoke_UART_transmit
}
out_char_ax1:
ax0=dm(flag_tx_ready);
none=pass ax0;
if eq jump out_char_ax1; // ожидание выдачи предыдущего
// символа
dm(user_tx_buffer)=ax1;
call invoke_UART_transmit; // выдача нового символа
rts;
//---------- Разрешение приема
{
Использование регистров:
вход: нет
обновленные: нет
выход: нет
измененные: ax0 = 0
сохраненные: нет
память: dm(flag_rx_off)
вызовы: нет
}
turn_rx_on:
ax0=0;
dm(flag_rx_off)=ax0;
rts;
//---------- Запрет приема
{
Использование регистров:
вход: нет
обновленные: нет
выход: нет
измененные: ax0 = 1
сохраненные: нет
память: dm(flag_rx_off)
вызовы: нет
}
turn_rx_off:
ax0=1;
dm(flag_rx_off)=ax0;
rts;
Рис. 1.17. Программа поддержки UART
Программа поддержки UART принимает символы из коммуникационного канала, а затем возвращает их обратно (см. бесконечный цикл, тело которого начинается с метки next_byte). Прием осуществляется подпрограммой get_char_ax1 (принятый символ возвращается через регистр ах1), а выдача – подпрограммой out_char_ax1 (выдаваемый символ берется из регистра ах1).
Перед использованием подпрограмм get_char_ax1 и out_char_ax1 UART необходимо проинициализировать подпрограммой init_uart, а также разрешить прием данных подпрограммой turn_rx_on. Для приема данных из коммуникационного канала связи кроме подпрограммы get_char_ax1 можно использовать подпрограмму get_char_ax1_to, которая отличается наличием тайм-аута. Тайм-аут длится 30000 тиков таймера, что составляет 10000 битовых интервалов. Для частоты связи в 9600 Бод длительность битового интервала составляет 104 мкс, поэтому длительность тайм-аута составляет 1,04 с. Для деинициализации UART определена подпрограмма stop_uart.
Программа содержит также две внутренние подпрограммы: process_a_bit и invoke_UART_transmit. Первая из них является обработчиком прерываний таймера и осуществляет все действия для побитной выдачи (приема) данных в коммуникационный канал связи. После выдачи/приема очередного байта данных устанавливает соответствующие флаги. Вторая – подготавливает байт данных для вывода.
Все перечисленные подпрограммы достаточно закомментированы, чтобы понять их работу без дополнительных разъяснений. Файл async.ldf может быть получен из файла echo.ldf (см. рис. 1.16) путем замен:
program_sect – UART_CODE, data_sect – UART_DATA.
