Главная » Bascom-8051, Basic, Железо » Программирование прерываний Bascom-8051

0

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

а)   создание   максимально   быстро   работающих   программ   обработки   прерываний,   использующих минимальные ресурсы памяти. Разработка таких программ не отличается от традиционного программирования на Ассемблере.  Ассемблерные  программы  прерывания   необходимо  применять  тогда,  когда  время  реакции  на прерывание очень критично и требуется полностью контролировать использование ресурсов памяти. Сложность создания  ассемблерных  программ  обработки  прерывания  (да  и  всех  остальных  вставляемых  ассемблерных программ)  заключается в необходимости “подглядывать” за операторами Bascom (с помощью  дисассемблера), чтобы  понять  и  правильно  организовать  передачу  данных  Бейсику  или  считывание  данных  из  переменных Бейсика;

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

в) комбинирование достоинств ассемблера и Бейсика. Это самый практичный и рекомендуемый подход – максимально использовать Бейсик, вставляя ассемблерные команды только в самые «горячие» участки программы туда, где компилятор дает явно неоптимальный код или использует ресурсы памяти недопустимым образом.

Правила описания программ прерывания не менее актуальны и для Бейсика:

а) предельная краткость – будет меньше ошибок;

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

в) использовать минимальное количество переменных (регистров);

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

д) в программе прерывания, описанной операторами Bascom, должна быть только одна точка выхода, т.к. компилятор только в одном месте (и только один раз) поставит команду  RETI. И эта точка выхода должна обозначаться только оператором RETURN. Если точек выхода несколько, то нужно вместо операторов RETURN

вставлять ассемблерную команду RETI. Если говорить точнее, то компилятор заменяет командой  RETI только

первый оператор RETURN, который он встретит, после метки, обозначающей вектор прерывания;

е)  допускается  использование  в  программе  прерываний  блока  регистров  3.  Чтобы   правильно  его использовать, нужно сохранить значение регистра PSW и установить биты RS0  и  RS1 (Psw = &h18). Если в программе используются операторы умножения чисел в формате с плавающей точкой, то необходимо сохранять и регистр с адресом 18H (R0 третьего банка).

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

Бейсике. Рекомендации по возможности применения операторов Bascom и допустимости совершаемых действий

выглядять следующим образом:

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

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

в) совсем нежелательно использовать операции с символьными переменными. Внутренние   переменные Bascom, используемые для этого, располагается вне области автоматического сохранения в стеке. Еще в большей степени это относится к семейству операторов вывода данных  (PRINT, LCD). Они используют еще больше не сохраняемых  регистров  и  периферийные  устройства.  Критерием  возможности  применения   таких  действий является только однозначное разделение во времени (зачем тогда это делать в прерывании?) или использование во всей  программе  только  одной  операции,  работающей  с   символьными  переменными.  Вообще,  сложности безошибочного  использования  в   прерываниях   и  прерываемых  программах  процедур  обработки  и  вывода символьных строк  столь значительны, что решать их лучше радикально – просто не применять в программах обработки прерывания;

г)  запрещено  применять  операторы  ожидания  событий,  например,  из  семейств   WAIT   и  INPUT.

Происходящие события, напротив, должны вызывать прерывания;

д)  невозможно  правильное  одновременное  использование  в  прерываемой  программе  и  программах прерывания операторов RESTORE и READ;

е)   очень   сложно   предсказать   последствия   использования   в   прерываниях   сложных   программных конструкций (OPEN .. CLOSE, DO .. LOOP, WHILE .. WEND, SELECT .. CASE). Поэтому лучше их не применять;

ж) с очень большой вероятностью будут правильно работать операторы проверки условий (IF .. THEN),

операторы циклов (FOR .. NEXT), операторы INCR, DECR применительно к битовым и байтовым переменным. Допустимо  применение  этих  операций  и  с  двух-  и  четырехбайтовыми  переменными,  хотя  при  этом  падает скорость обработки, получается  более громоздкий код и  требуется более тщательная проверка правильности

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

тем более,  символьные;

з) выполнение операторов GET и PUT, использующих последовательный программный  интерфейс, не должно вообще прерываться, иначе произойдет нарушение синхронизации;

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

программы модули программ прерывания.

Рассмотрим  примеры  оформления  программ  прерываний.  Вначале  будут   представлены   фрагменты инициализации прерываний различных типов. Затем будут даны примеры программ обработки прерываний.

Программирование внешних прерываний INT0 (или INT1) по уровню или спаду:

On Int0 Int_0_int   ‘выполнять внешнее прерывание INT0 (INT1 – аналогично)

Rem On Int0 Int0_int Nosave ‘то же самое без сохранения и восстановл. регистров Rem Set Tcon.0      ‘добавить, чтобы выполнять прерывание по спаду (Tcon.0 = 1) Enable Int0         ‘разрешить прерывание INT0 (Ie.0 = 1)

Enable Interrupts   ‘разрешить все прерывания (Ie.7 = 1) ‘——————————-

‘подпрограмма обработки внешнего прерывания

Int_0_int:

Reset P1.7 : Buf = P0 : Set P1.7 : Set New_data

Return

‘——————————-

Программирование прерывание таймера 0 (или таймера 1):

Config Timer0 = Timer , Gate = Internal , Mode = 1 ‘режим 16-разрный

On Timer0 Timer_0_int ‘выполнять прерывание от Таймера0 (Timer1 – аналогично) Counter = 0           ‘чтобы прерывание наступило через 65 мс

Enable Timer0         ‘разрешить прерывание INT0 (Ie.1 = 1) Enable Interrupts     ‘разрешить все прерывания (Ie.7 = 1)

‘——————————-

‘подпрограмма обработки прерывания таймера

Timer_0_int:

Counter0 = &hD8F0  ‘перезагрузить на интервал 10 мс (при кварце 12 МГц) Incr Rtime : Set B_10_mc        ‘сообщить, что прошло 10 мс

Return

‘——————————-

Показан более подробный пример программирование прерывания последовательного канала. В его

основе прием по прерыванию символьной строки во временный буфер. Процедура прерывания написана на ассемблере, что обеспечивает “прозрачность” использования ресурсов. При объявлении прерывания применена опция Nosave, запрещающая автоматически сохранять регистры.

Ri Alias Scon.0                           ‘бит RI Ti Alias Scon.1                           ‘бит TI

Dim R_ch As Byte                          ‘принятый символ Dim R_cch As Byte                         ‘указатель буфера Dim R_lin As String * 6                   ‘введеная строка

‘——–

‘TIMER2 в режиме 16-бит. таймера с внутр. тактир. для синхронизации UART

$crystal = 12000000                       ‘при кварце 12 МГц

$baud = 9600                              ‘скорость 9.6 кБ

Config Timer2 = Timer , Gate = Internal , Mode = 2

Timer2 = &HFFA5 : Start Timer2            ‘установим скорость

On Serial Ser_int Nosave                  ‘выполнять прерывание посл. канала

Enable Serial                             ‘разрешить прерывание посл. канала

Enable Interrupts                         ‘разрешить прерывания

‘обработка прерывания последовательного интерфейса

Ser_int:

$asm

Intsr:

Ints1: Ints2:

Intse: Ints3:

Ints4:

Ints5: Intst:

$end Asm

Jbc {Ri} , Intsr ;ищем источник прерывания

Jbc {Ti} , Intst ;заодно и сбрасываем бит вызвавший прерывание

Reti

Push Psw          ;прерывание приемника

Push Acc

Mov {R_ch} , Sbuf ;сохранить принятый символ

Mov A , {R_ch}

Cjne A , #&h0d , Ints3

Setb {N_dat}      ;0Dh – принята строка

Mov A , {R_cch}   ;указатель буфера

Xch A , R0        ;сохранить R0

Mov @R0 , #&h00   ;записать в буфер конец строки

Xch A , R0        ;восстановить R0

Mov {R_cch} , #{R_lin} ;переинициализировать указатель буфера

Pop Acc Pop Psw Reti

Cjne A , #&h0a , Ints4

Sjmp Intse        ;0Ah – игнорировать

Mov A , {R_cch}   ;указатель буфера

Xch A , R0

Mov @R0 , {R_ch}  ;все остальное записывать в буфер

Xch A , R0

Mov A , #{R_lin+6}

Cjne A , {R_cch} , Ints5

Sjmp Intse

Inc {R_cch}       ;если буфер незаполнен – изменим указатель

Sjmp Intse

Setb {B_entx}     ;прерывание передатчика

Return ‘этот оператор, компилятор заменит командой Reti

‘——————————-

Программирование   таймера   2   для   получения    такого    же   результата,   как   выше    в    примере

программирования таймера 0:

Config Timer2 = Timer , Gate = Internal , Mode = 0

On Timer2 Timer_2_int         ‘выполнять прерывание от таймера 2

Load Timer2 , 10000           ‘каждые 10 мс (загружает 65536-10000) Enable Interrupts             ‘разрешить используемые прерывания Enable Timer2                 ‘разрешить прерывание таймера 2

‘——————————- ‘подпрограмма обработки прерывания таймера 2

Timer_2_int:

Reset T2con.7              ‘сбросить бит, вызвавший прерывание

Incr Rtime : Set B_10_mc   ‘сообщить, что прошло 10 мс

Return

‘——————————-

Программирование  нестандартного  прерывания,  например,  от    счетчика  PCA  процессора  87C51FA.

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

‘  Выбрать файл 8051fa.dat !!!

On Pca Pca_int                ‘выполнять прерывание от PCA

Cmod = &h01                   ‘разрешить прерывание от PCA

Enable Interrupts             ‘разрешить используемые прерывания

Ch = &hD8 : Cl = &hF0         ‘загрузить на интервал 10 мс (при кварце 12 МГц) Ccon.6 = 1                    ‘разрешить счет

‘——————————-

‘подпрограмма обработки прерывания от PCA Pca_int:

Reset Ccon.7                  ‘сбросить бит, вызвавший прерывание Ch = &hD8 : Cl = &hF0         ‘снова загрузить на интервал 10 мс Incr Rtime : Set B_10_mc      ‘сообщить, что прошло 10 мс

Return

‘——————————-

Для  повышения  эффективности  генерируемого  кода  некоторые  операции  в  программах  прерываний

рекомендуется заменять  ассемблерными  командами (можно  и не  только  в  программах  прерываний). Ниже  в таблице представлены такие примеры.

Запись в Bascom

Ассемблерный эквивалент

If Bn = 1 Then      ‘если 1

Reset Bn : Goto Mf ‘сбросить и перейти

End If

Jbc {Bn} , Mf

If Rn = 0 Then Goto Mf ‘нуль? – перейти

End If

Mov A , {Rn} Jz Mf

Rb = High (Rw) ‘Rw –двухбайтовая перем.

Mov {Rb} , {Rw + 1}

Отсутствует простой доступ к частям многобайтовых

переменных, например,  R_Long.

Не всегда удобен  вариант, позволяющий только

считывать:

Tmpw = Loww(R_Long) : Acc = Hi(Tmpw)

Mov {R_long + 2} , #&hf0 ;запись

Mov A , {R_long + 1}     ;считыванние

Источник: М.Л.Кулиш, СПРАВОЧНИК ПО ПРОГРАММИРОВАНИЮ BASCOM-8051, Краснодар 2001

По теме:

  • Комментарии