Справочник по программировани BASCOM-8051 (М.Л. Кулиш, 2001). Справочник по программированию bascom8051 Краснодар 2001
Скачать 6.61 Mb.
|
===================================== Справочник по программированию «Bascom-8051» == 'теперь буфер пуст и его уже нельзя испортить 'инициализировать систему B_entx = 1 : B_nd = 0 : B_sd = 1 : Cnt = 0 : Rang = 0 : Udac = 0 R_bui = Varptr(inp_buf) 'указатель буфера на начало Mc: Do 'главный цикл If B_nd = 1 Then 'активизируется по приходу сообщения Reset B_nd : Goto N_dat End If If B_sd = 1 Then 'активизируется при необходимости передачи Goto S_dat End If 'Idle Loop '------------------------------------------ 'обработка принятых данных N_dat: Temp = Left(inp_buf , 1) 'выделим первый символ для анализа '----- If Temp = "R" Then 'запрос состояния? Set B_sd : Goto Mc 'строка обработана - в главный цикл End If '----- If Temp = "L" Then 'данные ЦАП? Temp = Mid(inp_buf , 2 , 5) Udac = Val(temp) 'преобр. строку в число с полярностью Gosub Sload_766 'загрузим ЦАП Goto Mc 'строка обработана - в главный цикл End If '----- If Temp = "S" Then 'данные режима? Temp = Mid(inp_buf , 2 , 1) Rang = Val(temp) 'преобразуем строку в число P2 = Rang 'загрузим в порт P2 Goto Mc 'строка обработана - в главный цикл End If Goto Mc '------------------------------------------ 'обработка данных на вывод S_dat: If B_entx = 1 Then 'начнем, если передача разрешена Reset B_sd 'сбрасываем бит, вызв. передачу Reset B_entx 'и запр. передачу до полн. Выв. буфера 'Формируем строку состояния системы: "SxLxxxxxTxxx,ВК,ПС" Out_buf="S"+Str(rang)+"L"+Str(udac)+"T"+Str(cnt)+Chr(13)+Chr(10) R_buo = Varptr(out_buf) 'указатель буфера на начало Tmp = Peek(r_buo) : Sbuf = Tmp 'первый символ на передачу 'этой операцией инициализируем систему вывода по прерыванию 'остальные символы буфера будут выводиться автоматически Incr R_buo поставим указатель буфера на второй символ End If Goto Mc '--------------------- 'подпрограмма загрузки данных в ЦАП AD766, AD1851 Sload_766: 'выдвинуть данные из Udac в режиме 0 (ст. сначала , -_-) Shiftout B_datu , B_clku , Udac , 0 Reset B_ldu : Set B_ldu 'загрузить данные Return '---------------------------------------------- 'обработка прерывания таймера 0 Timer_0_int: $asm ============================================================================= 17-3 ===================================== Справочник по программированию «Bascom-8051» == Mov Th0 , #&HD8 Mov Tl0 , #&HFD ;уст. периода прерыв. 10 мс $end Asm Incr Cnt 'считаем прерывания Return '---------------------------------------------- 'обработка прерывания последовательного интерфейса Ser_int: $asm Jbc {Ri} , Intsr ;ищем источник прерывания Jbc {Ti} , Intst ;заодно и сбрасываем бит вызвавший прерывание $end Asm Return $asm ; прерывание приемника Intsr: Push Psw Push Acc Mov {R_ch} , Sbuf ;сохранить принятый символ Mov A , {R_ch} Ints1: Cjne A , #&h0d , Ints3 Setb {B_nd} ;0Dh - принята строка Ints2: Mov A , {R_bui} ;указатель буфера Xch A , R0 ;сохранить R0 Mov @R0 , #&h00 ;записать в буфер конец строки Xch A , R0 ;восстановить R0 Mov {R_bui} , #{inp_buf} ;переинициализировать указатель буфера Intse: Pop Acc Pop Psw Reti Ints3: Cjne A , #&h0a , Ints4 Sjmp Intse ;0Ah - игнорировать Ints4: Mov A , #{inp_buf + 10} Cjne A , {R_bui} , Ints5 Sjmp Intse Ints5: Mov A , {R_bui} ;указатель буфера Xch A , R0 Mov @R0 , {R_ch} ;все остальное записывать в буфер Xch A , R0 Inc {R_bui} ;если буфер незаполнен - изменим указатель Sjmp Intse ; прерывание передатчика Intst: Push Psw Push Acc Mov A , {R_buo} ; считаем символ из буфера вывода Xch A , R0 Mov {R_ch} , @R0 Xch A , R0 Mov A , #{out_buf + 10} ;если это произошло за пределами буфера Cjne A , {R_bui} , Ints6 ;заканчиваем сообщение Sjmp Ints7 Ints6: Mov A , {R_ch} ; очередной символ равен 0? Jz ints7 Mov Sbuf , {R_ch} Inc {R_buo} ; изменим указатель буфера вывода ============================================================================= 17-4 ===================================== Справочник по программированию «Bascom-8051» == Sjmp Intse Ints7: Setb {B_entx} ; установим указатель разрешения передачи Sjmp Intse ; вначале символа "возврат каретки" $end Asm '------------------------------------------ Для многих систем оказывается достаточным компромиссное решение, когда по прерыванию осуществляется только ввод данных, которые могут поступать в произвольный момент времени, а вывод данных производится прямо из главной программы с помощью оператора Print. При этом программы вывода данных и программа прерываний приобретают другой вид и упрощаются. В примере, приведенном выше, при этом также становятся ненужными переменные B_entx, R_bui и Out_buf. Упрощается фрагмент в стартовом блоке программы: Enable Serial 'разрешить прерывания посл.интерфейса Print "Test_com" 'инициализировать систему Программа формирования и вывода строки состояния также может быть построена проще: '------------------------------------------ 'обработка данных на вывод S_dat: 'сбрасываем бит, вызвавший передачу и печатаем строку состояния системы: ' "SxLxxxxxTxxx,ВК,ПС" Reset B_sd : Print "S" ; Rang ; "L" ; Udac ; "T" ; Cnt Goto Mc При этом больше всего изменяется программа обработки прерывания последовательного интерфейса. Она также упрощается и в ней необходимо обрабатывать только прерывание приемника. Бит Ti теперь обрабатывается внутри оператора Print. В состав новой подпрограммы прерывание дополнительно введен полезный модуль преобразования строчных латинских символов в прописные. '------------------------------------------ 'обработка прерывания последовательного интерфейса Ser_int: $asm Jbc {Ri} , Intsr Reti Intsr: Push Psw ;прерывание приемника Push Acc Mov {R_ch} , Sbuf ;сохранить принятый символ Mov A , {R_ch} ;преобразуем малый символ в большой Add A , #&h9f ;код символа 'a'? (нижняя граница) Jnc Ints0 ;(.not.'a'+1) Mov A , {R_ch} ;код символа 'z'? (верхняя граница) Add A , #&h85 ;(.not.'z') Jc Ints0 Mov A , {R_ch} ;скорректировать Anl A , #&hdf Mov {R_ch} , A Sjmp Ints1 Ints0: Mov A , {R_ch} Ints1: Cjne A , #&h0d , Ints3 Setb {B_nd} ;0Dh - принята строка Ints2: Mov A , {R_bui} ;указатель буфера Xch A , R0 ;сохранить R0 Mov @R0 , #&h00 ;записать в буфер конец строки Xch A , R0 ;восстановить R0 Mov {R_bui} , #{inp_buf} ;переинициализировать указатель буфера Intse: Pop Acc Pop Psw ============================================================================= 17-5 ===================================== Справочник по программированию «Bascom-8051» == Reti Ints3: Cjne A , #&h0a , Ints4 Sjmp Intse ;0Ah - игнорировать Ints4: Mov A , #{inp_buf + 10} Cjne A , {R_bui} , Ints5 Sjmp Intse Ints5: Mov A , {R_bui} ;остальное записывать в буфер Xch A , R0 Mov @R0 , {R_ch} Xch A , R0 Inc {R_bui} Sjmp Intse $end Asm '------------------------------------------ Далее рассмотрим программу, в которой вообще не используется прерывание. Это программа является частью классического монитора, обеспечивающего просмотр памяти в текстовом виде и HEX-кодах. Ядро этой программы может служить основой многих других инструментальных программ. Это медленная программа, которая, исполнив команду или совершив действие, находится в режиме ожидания прихода символа в последовательный канал. '-------------------------------------------------------------- 'Демонстрационная программы монитора, работающего 'через последовательный канал. '-------------------------------------------------------------- $large 'большая модель памяти Dim Tmp As Byte 'временные байтовые данные Dim R_ch As Byte 'принятый или передаваемый символ Dim L_adr As Word 'начальный адрес Dim H_adr As Word 'конечный адрес Dim S_adr As Word 'текущий адрес Dim Big_buf As String * 64 'большой буфер ввода-вывода '--------------------- 'TIMER2 в режиме 16-бит. таймера с внутр. тактир. для синхронизации UART Config Timer2 = Timer , Gate = Internal , Mode = 2 $baud = 9600 'скорость 9.6 кБ $crystal = 12000000 'при кварце 12 МГц '--------------------- Print "Monitor" 'выведем стартовое сообщение Goto Begin 'на начало _error: 'точка входа по ошибке Gosub Dis_err 'перевод строки и сообщение об ошибке Begin: ' выведем приглашение "BC>" без разделителей ВК и ПС Gosub _promt R_ch = Waitkey 'ждать ввода символа If R_ch = &H0D Then 'это символ ВК? Goto Ignore 'да - его нельзя печатать End If Beg1: Printbin R_ch 'вывести введеный символ в той же строке! Select Case R_ch 'выбрать программу обработки Case &H44 : Goto Dump_mem 'это символ D? Case &H4C : Goto List_mem 'это символ L? Case &H48 : Goto Help 'это символ H? Case Else : Goto _error 'все остальное воспринимается как ошибка End Select ' внимание! важный момент - обработка символа ВК. ' он может приходить в паре с символом ПС, который нужно игнорировать ' а может быть это уже следующая команда или опять символ ВК ============================================================================= 17-6 ===================================== Справочник по программированию «Bascom-8051» == Ignore: Waitms 50 'ждем прихода второго символа 50 мс R_ch = Inkey 'опрашиваем входной буфер If R_ch = 0 Then 'если считали нуль, значит буфер пустой Goto _error 'можно индицировать сообщение об ошибке Elseif R_ch = &H0A Then 'пришел ПС Goto _error 'тоже считаем, что ошибка Elseif R_ch = &H0D Then 'если опять пришел ВК Goto Ignore 'значит все повторить End If 'остальное - считаем это простым вводом Gosub Dis_err 'перевод строки и сообщение об ошибке Gosub _promt 'вывод приглашения Goto Beg1 'и на обработку новой команды '-------------------------------------------------------------- 'вывести содержимое памяти в HEX-коде Dump_mem: Print " - Damp of memory" 'расшифровать название команды Gosub Inp_ladr : Gosub Inp_hadr 'введем адреса нач. и конца блока S_adr = L_adr 'текущий адрес начнем с начального D_mem0: Big_buf = Hex(s_adr) + " " 'адрес начала строки данных Goto D_mem2 'переход при первом входе D_mem1: Tmp = Low(s_adr) 'анализируем: младшие разряды адреса? If Tmp = 0 Then 'равны нулю - начать новый блок Gosub E_cnk 'запросить: продолжать или выйти? If R_ch = &H1B Then 'если введен ESC, Goto Begin 'выйти End If End If Tmp = Tmp And &H0F 'анализируем: If Tmp = 0 Then 'младший полубайт равен 0? Print Big_buf 'да - печатать буфер Goto D_mem0 'и в начало следующей строки End If D_mem2: Tmp = Cpeek(s_adr) 'считывание из памяти Big_buf = Big_buf + " " + Hex(tmp) 'и добавим изображение в буфер Incr S_adr 'изменим текущий адрес If S_adr > H_adr Then 'он достиг верхней границы? Goto D_mem3 'да - на выход End If Goto D_mem1 'нет - повторить D_mem3: Print Big_buf 'перед выходом отпечатам последнюю строку Goto Begin '-------------------------------------------------------------- 'вывести содержимое памяти в в текстовом виде List_mem: Print " - List of memory" 'расшифровать название команды Gosub Inp_ladr : Gosub Inp_hadr 'ввести границы просмотра S_adr = L_adr 'текущий адрес в начало блока L_mem0: Big_buf = Hex(s_adr) + " " 'формируем начальный адрес данных с строке Goto L_mem2 'переход при первом входе L_mem1: Tmp = Low(s_adr) 'анализируем: младшие разряды адреса? If Tmp = 0 Then 'равны нулю - начать новый блок Gosub E_cnk 'запросить: продолжать или выйти? If R_ch = &H1B Then 'если введен ESC, выйти Goto Begin End If End If ============================================================================= 17-7 ===================================== Справочник по программированию «Bascom-8051» == Tmp = Tmp And &H0F 'анализируем: If Tmp = 0 Then ' младший полубайт равен 0 Print Big_buf 'да - печатать буфер Goto L_mem0 'и в начало следующей строки End If L_mem2: Tmp = Cpeek(s_adr) 'считывание байта из памяти If Tmp > 127 Then 'непечатные символы преобразуем в точку Tmp = &H2E 'с кодами выше 7fh Elseif Tmp < &H20 Then 'и также с кодами ниже 20h Tmp = &H2E 'это пример использования оператора Elseif End If Big_buf = Big_buf + Chr(tmp) 'добавим символ Incr S_adr 'переходим к следующему адресу If S_adr > H_adr Then 'проверим: не перешли верхнюю границу Goto L_mem3 'да - переход End If Goto L_mem1 'нет - повторим L_mem3: Print Big_buf 'печать буфера перед выходом Goto Begin '-------------------------------------------------------------- 'выдать таблицу помощи Help: Print "" Print " ------ MONITOR ------ " Print "| D - Display memory |" Print "| L - List memory |" Print "| H - This help |" Print " --------------------- " Print "" Goto Begin '-------------------------------------------------------------- 'подпрограммы: все, что повторяется более одного раза '-------------------- 'ввод начального адреса Inp_ladr: Inputhex "Begin=" , L_adr : Return '-------------------- 'ввод конечного адреса Inp_hadr: Inputhex "End=" , H_adr : Return '-------------------- 'запрос: выйти или продолжать? E_cnk: Print "Exit - Esc, Continue - any key" R_ch = Waitkey : Return 'ждем ввода символа '-------------------- 'перевод строки и сообщение об ошибке Dis_err: Print "" : Print "Error" : Return '-------------------- 'вывод приглашение "BC>" без символов ВК ПС _promt: Printbin &H42 ; &H43 ; &H3E : Return 'запишем коды символов '-------------------------------------------------------------- End В приведенных программах даны примеры использования почти всех операторов, работающих с последовательным портом. При программировании вывода некоторые неудобства доставляют операторы, завершающие передаваемое сообщение разделителем (ВК ПС). Для устранения этого явления, недопустимого при выводе составных сообщений, применяется буфер, в котором формируется результирующая строка, или используется оператор Printbin. ============================================================================= 17-8 ===================================== Справочник по программированию «Bascom-8051» == Другая проблема вывода данных заключается в том, что иногда требуется включение в выдаваемое сообщение цифровых данных в форматированном виде. Для решения этой задачи необходимо использовать те же приемы, что и при выводе данных в символьный LCD-модуль. ============================================================================= 17-9 ===================================== Справочник по программированию «Bascom-8051» == 18. Программирование аналоговых преобразователей К аналоговым преобразователям, программирование которых будут рассмотрено ниже, относятся аналого-цифровые (АЦП), цифро-аналоговые преобразователи (ЦАП). К АЦП также можно отнести все схемы преобразования аналогового сигнала в частоту или длительность с устройством измерения частоты или периода. К устройствам ЦАП также относятся всевозможные регуляторы уровня, цифровые синтезаторы частоты, генераторы широтно-импульсной модуляции и даже просто управляемые генераторы частоты. Принципиальное различие АЦП и ЦАП с точки зрения программиста, независимо от того с каким аналоговым сигналом работает устройство, заключается в направлении передачи и порядке преобразования данных. При работе с АЦП данные считываются и после многократного преобразования приобретают вид, понятный наблюдателю или субъекту, принимающему решение. Очевидные данные ЦАП, напротив, должны многократно преобразовываться перед загрузкой преобразователь. Задачи и проблемы программирования ЦАП и АЦП можно сформулировать следующим образом: а) организация работы программы преобразования; б) обеспечение интерфейса ЦАП или АЦП с процессором; в) выбор формы представления цифрового значения измеряемого или устанавливаемого параметра; г) преобразование из кода АЦП в формат пригодный для вычисления и преобразование устанавливаемого значения в код загрузки ЦАП д) оптимальная цифровая калибровки преобразователей; е) накопление, усреднение и вывод данных АЦП; ж) ввод и подготовка данных ЦАП; з) ускорение принятия решений на основании данных АЦП. Рассмотрим эти задачи подробнее в том же порядке. Начнем с организации работы программы – с выбора двигателя. Для приведения в действия любой программы, работающей с аналоговыми устройствами, всегда нужен какой-то двигатель. В роли двигателя может выступать: а) внешнее событие, например, приход команды с клавиатуры или из внешнего интерфейса на установку нового значения воспроизводимого параметра (или запрос измеренных данных). Обычно, таким образом, работают системы с ЦАП; б) команда от таймера, задающего периодичность операций установки, смены или измерения параметров системы. Системы с периодически воспроизводимым циклом преобразования характерны для измерительных устройств, например, таких как цифровой вольтметр, и применяются наиболее часто; в) команда измерительного устройства о готовности данных. Самый характерный пример таких систем – устройства, в основе которых лежит медленный АЦП (интегрирующего типа). Привязка всех системы к медленному АЦП является лучшим решением, позволяющим исключить нежелательные переходные процессы и разрывы потока данных. Основной недостаток подобных систем возможность остановки программы при перегрузке или отказе измерительного устройства (впрочем, легко устранимый); г) команда другой программы, являющейся ведущей или передающей запрос на действие по цепочке. С точки зрения измерительной программы, такая команда является внешней, однако относительно системы в целом могут быть и другие механизмы приведения ее в действие, в том числе, и упомянутые выше. При планировании взаимодействия других частей программы с измерительной необходимо учитывать время исполнения каждой из них. Например, недопустимо, если обработка и вывод (всегда обращайте внимание на время вывода!) измеренных данных, выполняемых в измерительной цикле, превышает время готовности данных АЦП. Также недопустимо, если частота измерения АЦП меньше частоты считывания данных из него. Все это приводит к потере данных и появлению неявной погрешности измерения. Аналогично, нужно учитывать реальное время установления воспроизводимого параметра, задаваемого с помощью ЦАП. Причем учитывать не время установления выходного сигнала ЦАП, а время установления всей аналоговой схемы (если такая есть). Периодичность загрузки ЦАП должна быть намного больше электрической постоянной времени аналоговой части схемы. Также, время обработки устанавливаемых данных и загрузки кода в ЦАП не должно превышать период повторения операций загрузки. Рассмотрим с проблему интерфейса. Почти всегда АЦП или ЦАП, с которыми работает микроконтроллер, являются однокристальными устройствами. Номенклатура этих устройств, чрезвычайно широка и всегда, за исключением особых случаев, может быть выбрана модель, удовлетворяющая метрологическим требованиям, скорости, конфигурации входа (выхода) и напряжению питания. Выбранный по указанным критериям, преобразователь чаще оказывается с последовательным портом. В результате возникают сложности достижения высокого быстродействия при считывании (записи) данных. Это обусловлено отсутствием большинства у процессоров семейства 8051 высокоскоростного аппаратного последовательного порта, а с другой стороны использование АЦП и ЦАП с параллельным портом также не всегда возможно из-за дефицита свободных линий портов. Таким образом, задача интерфейса сводится к программному формированию сигналов управления на выводах преобразователя и перемещению данных из регистров процессора в устройство или наоборот. В общем случае, создание программы считывания (из АЦП) и тем более записи данных (в ЦАП) с помощью операторов Bascom не представляет большой сложности. Для этого предусмотрены операторы SHIFTIN, ============================================================================= 18-1 |