- •М.Л.Кулиш
- •Содержание разделов
- •Некоторые советы по созданию больших программ
- •Приложение A. Переменные Бейсика
- •Приложение B. Система команд микроконтроллера 8051
- •Приложение C. Коды команд MCS-51
- •Типы данных и переменных
- •DEBOUNCE
- •LCD, LCDBUS, LCDPIN
- •Операторы Bascom-8051
- •Функции Bascom-8051
- •Элементы
- •Назначение и применение элементов языка Bascom
- •14. Программирование прерываний
- •Числовые переменные
- •Строковые переменные
- •Система команд микроконтроллера 8051
- •Команды передачи данных
- •Команды перехода
- •Команды перехода (продолжение)
- •Логические операции
- •Логические операции (продолжение)
- •Очистка А
- •Инвертирование А
- •Сдвиг А влево
- •Сдвиг А влево через перенос
- •Сдвиг А вправо
- •Сдвиг А вправо через перенос
- •Перестановка полубайтов А
- •SWAP A
- •Установка переноса
- •SETB C
- •Установка бита
- •SETB bit
- •Сброс переноса
- •Сброс бита
- •Загрузка переноса битом
- •Загрузка бита переносом
- •Тестирование бита Z
- •Тестирование переноса
- •Тестирование битов
- •Если bit=C
- •Инверсия бита
- •Инверсия переноса
- •Команды арифметических операций
- •Сложение аккумулятора с РОН
- •ADD A, direct
- •ADD A, #data
- •ADDC A, R0
- •ADDC A, R1
- •ADDC A, R2
- •ADDC A, R3
- •ADDC A, R4
- •ADDC A, R5
- •ADDC A, R6
- •ADDC A, R7
- •ADDC A, direct
- •ADDC A, @R0
- •ADDC A, @R1
- •ADDC A, #data
- •SUBB A, R0
- •SUBB A, R1
- •SUBB A, R2
- •SUBB A, R3
- •SUBB A, R4
- •SUBB A, R5
- •SUBB A, R6
- •SUBB A, R7
- •SUBB A, direct
- •SUBB A, @R0
- •SUBB A, @R1
- •SUBB A, #data
- •Команды арифметических операций (продолжение)
- •Инкрементирование А
- •Инкрементирование РОН
- •INC direct
- •Инкрементирование DPTR
- •INC DPTR
- •Декрементирование А
- •Декрементирование РОН
- •DEC direct
- •Умножение
- •Деление
- •Десятичная коррекция
===================================== Справочник по программированию «Bascom-8051» ==
14. Программирование прерываний
Система прерываний процессора 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). Они используют еще больше не
============================================================================= 14-1
===================================== Справочник по программированию «Bascom-8051» ==
сохраняемых регистров и периферийные устройства. Критерием возможности применения таких действий является только однозначное разделение во времени (зачем тогда это делать в прерывании?) или использование во всей программе только одной операции, работающей с символьными переменными. Вообще, сложности безошибочного использования в прерываниях и прерываемых программах процедур обработки и вывода символьных строк столь значительны, что решать их лучше радикально – просто не применять в программах обработки прерывания;
г) запрещено применять операторы ожидания событий, например, из семейств 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: |
'перезагрузить на интервал 10 мс (при кварце 12 МГц) |
|
Counter0 = &hD8F0 |
||
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 МГц
============================================================================= 14-2
===================================== Справочник по программированию «Bascom-8051» ==
$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
Jbc {Ri} , Intsr ;ищем источник прерывания
Jbc {Ti} , Intst ;заодно и сбрасываем бит вызвавший прерывание
Reti |
|
Intsr: |
;прерывание приемника |
Push Psw |
Push Acc
Mov {R_ch} , Sbuf ;сохранить принятый символ
Mov A , {R_ch}
Ints1:
Cjne A , #&h0d , Ints3
Setb {N_dat} |
;0Dh - принята строка |
Ints2: |
;указатель буфера |
Mov A , {R_cch} |
|
Xch A , R0 |
;сохранить R0 |
Mov @R0 , #&h00 |
;записать в буфер конец строки |
Xch A , R0 |
;восстановить R0 |
Mov {R_cch} , #{R_lin} ;переинициализировать указатель буфера |
|
Intse: |
|
Pop Acc |
|
Pop Psw |
|
Reti |
|
Ints3: |
|
Cjne A , #&h0a , Ints4 |
|
Sjmp Intse |
;0Ah - игнорировать |
Ints4: |
;указатель буфера |
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 |
|
Ints5: |
;если буфер незаполнен – изменим указатель |
Inc {R_cch} |
|
Sjmp Intse |
|
Intst: |
;прерывание передатчика |
Setb {B_entx} |
|
$end Asm
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 мс |
============================================================================= 14-3
===================================== Справочник по программированию «Bascom-8051» ==
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 |
Jbc {Bn} , Mf |
|
Reset Bn : Goto Mf 'сбросить и перейти |
|
|
|
End If |
|
|
|
If Rn = 0 Then Goto Mf 'нуль? – перейти |
Mov A , {Rn} |
|
|
End If |
|
Jz Mf |
|
Rb = High (Rw) 'Rw –двухбайтовая перем. |
Mov {Rb} , {Rw + 1} |
|
|
Отсутствует простой доступ к частям многобайтовых |
Mov {R_long + 2} , #&hf0 ;запись |
||
переменных, например, R_Long. |
|||
Не всегда удобен вариант, позволяющий только |
|
|
|
считывать: |
|
Mov A , {R_long + 1} |
;считыванние |
Tmpw = Loww(R_Long) : Acc = Hi(Tmpw) |
|||
|
|
|
|
============================================================================= 14-4
===================================== Справочник по программированию «Bascom-8051» ==
15.Проверка условий и изменение хода выполнения программы
Вописываемой версии Bascom-8051, как и в классическом Бейсике, основу организации работы отдельных частей (модулей) программы обеспечивают операторы проверки условий и изменения хода выполнения программы. Компилятор Bascom-8051 предоставляет возможность проверки значений всех типов переменных. Причем, проверяемая переменная может сравниваться как с константой, так с другой переменной, такого же типа.
Все числовые переменные могут проверяться на соответствие условиям соотношения с числовым значением и значением другой переменной. Bascom позволяет проверять переменные на соответствие условиям: равенства ( знак соотношения « = »), неравенства (« <> »), меньше (« < »), больше (« > »), меньше или равно (« <= ») и больше или равно («>=»). Задаваемое значение константы, записываемое в выражение сравнения, должно соответствовать типу проверяемой переменной таким образом, чтобы не вызывать трудностей компилятору при переводе этого числа в исполняемый код. Сравнение производится с учетом знака переменных и констант.
Битовые переменные могут сравниваться только с числами 0 и 1 или с другой битовой переменной. Bascom также обеспечивает проверку битов одно-, двух- и четырех байтовых переменных, даже не расположенных в битовой области внутренней памяти.
Строковые переменные могут сравниваться друг с другом или с непосредственно заданной строкой. В примере, приведенном ниже, показаны примеры использования операторов проверки условий с различными типами переменных. Мы видим, что имеется возможность выключать или включать исполнение отдельных участков программы, а также осуществлять переход в другие точки программы с помощью операторов GOTO. Применение операторов RETURN (или команды RET) внутри конструкции IF…END IF запрещено, т.к. при этом нарушается баланс стека.
'--------------------------------- |
|
|
|
' тестирование операций сравнения |
|
|
|
'--------------------------------- |
|
|
|
Dim Bt1 As Bit , Bt2 As Bit , Bd As Byte , Wd As Word , Ld As Long |
|
||
Dim Sd1 As Single , Sd2 As Single |
|
|
|
Dim Temp As String * 16 , Tmp1 As String * 16 |
|
||
M1: |
|
|
|
Input Sd1 : Sd2 = 2.345 |
'сравнение чисел в формате с пл. точкой |
|
|
If Sd1 < Sd2 Then |
|
||
Print "ok" : Goto M2 |
|
|
|
End If |
|
|
|
Print "no" |
|
|
|
M2: |
'ввести строковые переменные |
|
|
Input Temp : Input Tmp1 |
|
||
If Temp = Tmp1 Then |
|
|
|
Print "equ" : Goto M3 'если переменные равны - выход |
|
||
Elseif Temp = "stop" Then |
|
|
|
Goto Mend |
'а если введено "stop" - закончить |
|
|
End If |
|
|
|
Print "no" |
'проверка числовых переменных |
|
|
M3: |
|
||
Input Bd : Input Wd : Input Ld 'ввести значения |
|
||
If Bd = &H0A Then |
'проверим байтовую переменную |
|
|
Print "=" : Goto M4 |
'если она не равна 10, проверим остальные |
|
|
Else |
|
||
If Wd >= 1000 Then |
'двухбайтовая больше 1000? |
|
|
Print ">>" : Goto M4 |
'да - пойдем дальше |
|
|
Elseif Ld <= 1000000 Then |
'нет - проверим четырехбайтовую |
|
|
Print "<<<<" |
'если меньше 1000000 отпечатали |
|
|
End If |
|
|
|
End If |
'сравнение битовых переменных |
|
|
M4: |
|
||
Bt1 = 0 : Bt2 = 1 |
|
|
|
If Bt1 = Bt1 Then |
|
|
|
Print "ok" |
|
|
|
End If |
|
|
|
Goto M1 |
|
|
|
Mend: |
|
|
|
End |
|
|
|
============================================================================= |
15-1 |
||
===================================== Справочник по программированию «Bascom-8051» ==
Для того, чтобы программа работала быстрее, надежнее и затрачивала меньше ресурсов, следует применять максимально простую конструкцию операций проверки условий и использовать наиболее компактный тип сравниваемых данных. Не рекомендуется без особой необходимости сравнивать числа в формате с плавающей точкой и строковые переменные длиной более двух-трех символов.
В последних версиях компилятора стала возможной классическая запись операции проверки условий в одну строку в простом:
If Wd >= 1000 Then Goto M4
Или расширенном виде:
If Wd >= 1000 Then Print ">>" Else Print "<<" 'если меньше 1000000
При этом нужно иметь в виду, что качестве действия на истинность или не истинность проверяемому условию может быть применен только один оператор. Двоеточие в этой строке понимается как начало следующего оператора, выполняемого безусловно. Также понимается и оператор в следующей строке.
Еще одна новинка Bascom версии 2.xx – возможность проверки значения индексированных битов. Причем индекс может быть задан байтовой переменной:
Dim X As Byte |
'X = 0 – 7 |
X = 6 |
|
If P2.X=1 Then |
'проверка индексированного бита |
P2.X = 0 |
'изменение индексированного бита |
End If |
|
============================================================================= 15-2
===================================== Справочник по программированию «Bascom-8051» ==
16. Программирование вывода на индикатор
Компилятор Bascom обеспечивает вывод на символьный индикатор значений числовых переменных и символьных строк во всех возможных вариантах. Синтаксис операторов вывода данных и команд управления курсором достаточно прост и не требует дополнительных, более подробных пояснений, чем это сделано выше. Однако нужно заметить, что построение системы отображения данных является более сложной задачей, чем применение последовательности операторов вывода. Проектируемые устройства с символьным индикатором должны отвечать требованиям эргономики, включающей обязательное форматирование и позиционирование выводимых цифровых данных и сообщений. С учетом этого, рассмотрим практические вопросы построения и программирования системы отображения информации. Как обычно, перед началом программирования требуется разработать план системы отображения, включающий в себя следующие составляющие, перечисленные в порядке очередности:
а) определить число и разрядность, отображаемых параметров (числовых). Необходимо, хотя бы приблизительно, определить форматы отображения данных, чтобы зарезервировать место для десятичной точки, знака полярности и пробелов-разделителей. При отображении числовых данных нужно решительно избавляться от избыточности - исключать незначащие разряды (за пределами точности или нестабильные);
б) определить номенклатуру и число, одновременно отображаемых параметров (переменных); в) определить атрибуты изображения (выбрать сообщения или символы размерности числовых
параметров, разделителей полей данных, выбрать или разработать специальные символы); г) придумать все текстовые сообщения таким образом, чтобы они были краткими, приблизительно одной
длины и понятны. Настоятельно рекомендуется применять только латинский алфавит потому, что коды кириллических шрифтов и наборов символов (выше 7Fh) отличаются у разных изготовителей индикаторов;
д) определить число и расположение полей данных. Для правильного восприятия отображаемой информации расположение и размер полей данных должен быть фиксированным, включая и поле для текстовых сообщений. Так как текстовые сообщения обычно появляются на короткое время (постоянно не отображаются), лучше их размещать на полях малозначительных (второстепенных) параметров и таким образом экономить место. Наиболее важные параметры нужно располагать в середине рабочего поля. Менее важные сообщения по краям, на периферии. Ввиду того, что все символы имеют одинаковый размер, для улучшения восприятия, размер полей более важных параметров (сообщений) должен быть больше, чем у менее важных даже путем использования аббревиатур последних. В качестве разделителей полей данных рекомендуется применять пробелы и (или) специальные знаки в виде вертикальных и горизонтальных пунктирных линий;
е) произвести выбор типа символьного индикатора. Лучше применять модули индикации стандартных размеров и наиболее употребляемых форматов (в порядке предпочтения): 16 символов х 2 строки, 20 х 2, 20 х 4, 16 х 1, 24 х 2, 40 х 4. Они более доступны, всегда дешевле, имеют лучшие светотехнические параметры (чаще совершенствуются в процессе производства). Индикаторы уникальных типоразмеров, как правило, только заявлены изготовителем и производятся по специальному заказу. Лучше использовать индикаторы со светодиодной подсветкой (они дает постоянную контрастность изображения при любом освещении) с черными знаками на желтом поле – исполнение для стандартного температурного диапазона. Не рекомендуется без необходимости применять индикаторы, предназначенные для расширенного температурного диапазона (они имеют заметно меньшую контрастность и угол видимости);
ж) важно выбрать способ подключения индикатора к микроконтроллеру. При интенсивном использовании индикатора, например, для случаев полного обновления всех данных индикатора несколько раз в секунду (это характерно для измерительных систем), и для модулей с числом знаком более 32 необходимо применение 8-разрядной шины, иначе регенерация изображения станет заметной (появляется эффект снижения контрастности и «паразитного свечения» неиспользуемых знакомест);
з) еще раз смоделировать (на бумаге) возможность расположения и читаемость на выбранном индикаторе всех возможных сообщений при всех возможных комбинациях и режимах. Уточнить форматы индикации параметров, текстовые сообщения, атрибуты, аббревиатуры, стараясь максимально использовать общепринятые обозначения и термины.
Следующий вопрос – программирование изображений. Существуют два подхода к формированию изображения на индикаторе:
а) каждый раз при обновлении данных, очищать поле индикации и затем заполнять его данными, зависимыми от текущего состояния выводимых переменных. Принципиально то, что в этом случае не производится анализ и выявление изменившихся данных. Преимущества такого подхода заключаются в том, что формирование и вывод данных в индикатор легко сосредоточить в рамках одной универсальной процедуры и сделать регулярным, со скоростью удобной для восприятия (несколько раз в секунду). При этом процессы формирования данных и отображения данных становятся независимыми и параллельными, что также очень удобно. Недостаток - повышенные требования к быстродействию интерфейса индикаторного модуля и наличие кратковременного состояния неопределенности данных (старое изображение стерто, а новое еще не записано). Если это время превышает 5-10 мс (более одного периода частоты сканирования всех сегментов), то становится видимым «мелькание» сегментов с частотой обновления данных;
============================================================================= 16-1
===================================== Справочник по программированию «Bascom-8051» ==
б) обновлять только изменяющиеся данные. Достоинства такого подхода - немигающее изображение, отсутствие проблем со способом подключения индикатора. Недостатки - усложнение программного интерфейса (он рассыпается на множество процедур по числу отображаемых переменных), вывод становится нерегулярным (с частотой изменения отображаемых переменных), обработка изменения состояния переменной, в свою очередь, тормозится процедурой вывода в индикатор (единицы миллисекунд). Область применения - индикаторы с числом более 40 знаков и системы с нерегулярным обновлением информации, например, при поступлении одиночных событий.
Ниже представлена большая демонстрационная программа, в которой решаются несколько проблем вывода цифровых данных.
'--------------------------------------------------------------
'Демонстрационная программа вывода данных в символьный индикатор
'показывает, как формируется показания на индикаторе
'измерительного прибора - мультиметра, работающего в пяти режимах
'и имеющего на каждом по пять пределов измерения. Все пределы
'измерения пронумерованы от 0 до 24 (сквозная нумерация).
'Программа построена так, что все режимы индикации привязаны
'к переменной Rang, определяющей номер предела. Данные АЦП
'и предел изменяются после цикла индикации (каждые 2 секунды)
' |
XXX: ±XXXXXX XX |
||
' |
|||
' три знака режима работы _______^ |
^ ^ |
^ |
|
' полярности для сигналов пост. тока _| | |
| |
||
' цифровое значение ____________________| |
| |
||
' размерность показаний ______________________| |
|||
' |
|
|
'-------------------------------------------------------------- |
'большая модель памяти |
|
$large |
||
Dim B_nd As Bit |
'бит "Новые данные" |
|
Dim B_tim As Bit |
'бит "Прошло 10 мс" |
|
Dim Cnt As Byte |
'счетчик мс |
|
Dim Tmp As Byte |
'временные байтовые данные |
|
Dim Rang As Byte |
'это номер предела!!! |
|
Dim Fld As Single |
'данные "АЦП" - измеренное значение |
|
Dim Rfld As Single |
'индицируемые данные |
|
Dim Temp As String * 10 |
'временная строка. |
|
'Внимание!!! При меньшей длине Temp программа «залезает» в стек. |
||
'-------------------------------------------------------------- |
|
|
' подключение индикатора |
|
|
'-------------------------------------------------------------- |
'размер индикатора |
|
Config Lcd = 16 * 2 |
|
|
Config Lcdpin, Db4=P1.4, Db5=P1.5, Db6=P1.6, Db7=P1.7, E=P3.2, Rs=P3.4 |
||
'линия P3.3 не используется – ее надо заземлять |
|
|
'-------------------------------------------------------------- |
|
|
Config Timer0 = Timer , Gate = Internal , Mode = 1 : Start Timer0 |
||
On Timer0 Timer_0_int Nosave |
'вектор прерывания |
|
Enable Interrupts |
'вообще разрешить прерывания |
|
Enable Timer0 |
'разрешить прерывания таймера 0 |
|
'!!! -------------------------------------- |
|
|
Start Timer0 : Cnt = 100 : Rang = 0 : B_nd = 1 : B_tim = 1 |
||
Deflcdchar 1,228,228,234,234,241,241,255,224 |
'дельта |
|
Deflcdchar 2,238,241,241,241,234,234,251,224 |
'омега |
|
Cls |
'привести в действия новые символы |
|
Cursor Off |
'сделать невидимым курсор |
|
Lcd " Multimeter" |
'стартовое приветствие |
|
Wait 1 |
|
|
Mc: |
'главный цикл |
|
Do |
|
|
If B_nd = 1 Then |
'каждые 2 секунды индицировать |
|
Reset B_nd : Goto Disp |
|
|
End If |
'считаем 10 мс прерывания |
|
If B_tim = 1 Then |
||
Reset B_tim : Decr Cnt |
|
|
============================================================================= 16-2
===================================== Справочник по программированию «Bascom-8051» ==
If Cnt = 0 Then
Set B_nd : Cnt = 200
Fld = Lookup(rang , _tab_mes) : Incr Rang
If Rang = 25 Then
Rang = 0 |
|
End If |
|
End If |
|
End If |
|
Loop |
|
'-------------------------------------------------------------- |
|
'вывод данных в индикатор |
|
Disp: |
'считать значение выхода |
Fld = Lookup(rang , _tab_mes) |
|
Rfld = Lookup(rang , _tab_zc) |
'считать масштабирующую константу |
Rfld = Rfld * Fld |
'вычислить в размерности индикации |
Cls |
'очистить LCD |
Gosub Lcd_attr1 |
'вывести сообщение о режиме работы |
Tmp = Lookup(rang , _tab_polar)'нужно индицировать полярность? |
|
If Tmp = 0 Then |
'не нужно, если значение из табл. = 0 |
$asm |
|
Anl {rfld + 3} , #&H7f ;сделаем показания безусловно положительными
$end Asm |
'сразу поставить в позицию вывода цифр |
Locate 1 , 7 |
|
Goto Lcd_fp |
'и перейдем на форматирование |
End If |
|
'нужно индицировать число со знаком |
|
Locate 1 , 6 |
'курсор в позицию вывода знака |
If Rfld < 0 Then |
'какая полярность? |
Goto Lcd_fn |
'отрицательная - переход |
End If |
'положительная - "+" на индикатор |
Lcd "+" |
|
Lcd_fp: |
|
'форматирование положительного числа. Выбираем программу форматирования, 'соответствующую пределу измерения (все это должно быть записано в одну строчку 'или переносится с помощию символа подчеркивателя
On Rang Gosub F4 |
, F2 , F3 |
, F4 |
, F5 |
, F4 |
, F2 |
, F3 |
, _ |
||||||||
F4 |
, |
F5 |
, |
F4 |
, |
F2 |
, |
F3 |
, |
F4 |
, |
F2 |
, |
F4 |
, _ |
F2 |
, |
F3 |
, |
F4 |
, |
F2 |
, |
F4 |
, |
F2 |
, |
F3 |
, |
F4 |
, F2 |
Goto Lcd_dat |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Lcd_fn: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
'форматирование отрицательного числа. Аналогично форматируем, 'но длина результата будет на знак больше (добавится знак минуса)
On Rang Gosub M4 |
, M2 |
, |
M3 |
, M4 |
, |
M5 |
, M4 |
, M2 |
, M3 |
, _ |
||||
M4 |
, |
M5 |
, |
M4 |
, |
M2 |
, |
M3 |
, M4 |
, |
M2 |
, |
M4 |
, _ |
M2 |
, |
M3 |
, |
M4 |
, |
M2 |
, |
M4 |
, M2 |
, |
M3 |
, |
M4 |
, M2 |
'отбрасываем знак |
минуса |
и выводим |
его отдельно |
|
|
|
||||||||
Temp = Mid(temp , 2 , 6 ) : Lcd "-" Lcd_dat:
'еще одна неприятность Bascom: он не выводит незначащие нули слева
'перед десятичной точкой кроме одного. Будем исправлять и этот недостаток. 'для чего проверим длину форматированной переменной
Tmp = Len(temp) |
'длина = 5 - отброшен один нуль |
If Tmp = 5 Then |
|
Lcd "0" |
'добавим один нуль |
Goto Lcd_od |
|
End If |
'длина = 4 - отброшено два нуля |
If Tmp = 4 Then |
|
Lcd "00" |
'добавим два нуля |
Goto Lcd_od |
|
End If |
'длина = 3 - отброшено три нуля |
If Tmp = 3 Then |
|
Lcd "000" |
'добавим три нуля |
End If |
|
============================================================================= 16-3
===================================== Справочник по программированию «Bascom-8051» ==
Lcd_od: |
|
|
'наконец выводим цифровое значение |
||
Lcd Temp |
|
||||
Gosub Lcd_attr2 |
|
'и добавляем размерность показаний |
|||
Goto Mc |
|
|
|
|
|
'-------------------------------------------------------------- |
|
|
|
|
|
'обработка прерывания таймера 0 |
|
|
|||
Timer_0_int: |
|
|
|
|
|
$asm |
|
|
|
|
|
Mov Th0 , #&HD8 |
|
;уст. периода прерыв. 10 мс |
|||
Mov Tl0 , #&HFD |
|
||||
$end Asm |
|
|
'прошло 10 мс |
||
Set B_tim |
|
|
|||
Return |
|
|
|
|
|
'-------------------------------------------------------------- |
|
|
|
|
|
'подпрограмма вывода сообщения о режиме работы |
|
||||
Lcd_attr1: |
|
|
'считали из таблицы |
||
Temp = Lookupstr(rang , _tab_att1) |
|||||
Locate 1 , 1 : Lcd Temp : Lcd ":" |
'вывели и добавили двоеточие |
||||
Return |
|
|
|
|
|
'-------------------------------------------------------------- |
|
|
|
|
|
'подпрограмма вывода сообщения о размерности показаний |
|||||
Lcd_attr2: |
|
|
'считали из таблицы |
||
Temp = Lookupstr(rang , _tab_att2) |
|||||
Locate 1 , 13 : Lcd Temp |
|
'вывели |
|
||
Return |
|
|
|
|
|
'-------------------------------------------------------------- |
|
|
|
|
|
' форматы отображения данных мультиметра |
±0000.0 V |
||||
'DCV: ±000.00 mV ±0.0000 V |
±00.000 V |
±000.00 V |
|||
'ACV: |
000.00 mV |
0.0000 V |
00.000 V |
000.00 V |
0000.0 V |
'Ohm: |
000.00 W |
0.0000 kW |
00.000 kW |
000.00 kW |
0.0000 MW |
'DCI: ±000.00 uA ±0.0000 mA ±00.000 mA ±000.00 mA ±0.0000 A |
|||||
'ACI: |
000.00 uA |
0.0000 mA |
00.000 mA |
000.00 mA |
0.0000 A |
'--------------------------------------------------------------
'подпрограммы форматирования показаний положительной полярности
F2:
Temp = Fusing(rfld , #.####) : Return F3:
Temp = Fusing(rfld , ##.###) : Return F4:
Temp = Fusing(rfld , ###.##) : Return F5:
Temp = Fusing(rfld , ####.#) : Return
'подпрограммы форматирования показаний отрицательной полярности 'требуется дополнительное место перед цифрами для знака минуса. M2:
Temp = Fusing(rfld , ##.####) : Return M3:
Temp = Fusing(rfld , ###.###) : Return M4:
Temp = Fusing(rfld , ####.##) : Return M5:
Temp = Fusing(rfld , #####.#) : Return '--------------------------------------------------------------
' значения первого аттрибута - режима измерений
_tab_att1:
Data "DCV" , "DCV" , "DCV" , "DCV" , "DCV"
Data "ACV" , "ACV" , "ACV" , "ACV" , "ACV"
Data "Ohm" , "Ohm" , "Ohm" , "Ohm" , "Ohm"
Data "DCI" , "DCI" , "DCI" , "DCI" , "DCI"
Data "ACI" , "ACI" , "ACI" , "ACI" , "ACI" '--------------------------------------------------------------
' управление отображением полярности: 0 - нет, 1 - есть
_tab_polar:
============================================================================= 16-4
===================================== Справочник по программированию «Bascom-8051» ==
Data 1 , 1 , 1 , 1 , |
1 |
|
|
|
|
Data 0 , 0 , 0 , 0 , |
0 |
|
|
|
|
Data 0 , 0 , 0 , 0 , |
0 |
|
|
|
|
Data 1 , 1 , 1 , 1 , |
1 |
|
|
|
|
Data 0 , 0 , 0 , 0 , |
0 |
|
|
|
|
'-------------------------------------------------------------- |
|
|
|
|
|
' значения второго аттрибута - размерности показаний |
|
||||
_tab_att2: |
V" , " |
V" , " V" , " V" |
|
||
Data " mV" , " |
|
||||
Data " mV" , " |
V" , " |
V" , " V" , " V" |
|
||
'формируем формат строковых констант с дополнительным нулем данных |
|||||
'это константы: " W", " kW", " kW" |
|
|
|||
Data &H20 , &H20 , 2 , |
0 , &H20 , 107 , 2 , 0 , &H20 , 107 , 2 , 0 |
||||
'это константы: " kW", |
" MW". W - это символ Омега (код 2) |
||||
Data &H20 , 107 , 2 , 0 , &H20 , 77 , 2 , 0 |
|
||||
Data " uA" , " mA" , " mA" , " mA" , " A" |
|
||||
Data " uA" , " mA" , " mA" , " mA" , " A" |
|
||||
'-------------------------------------------------------------- |
|
|
|
|
|
'масштабирующие коэффициенты, преобразующие размерность данных |
|||||
'к размерности отображаемых данных с другим положение дес.точки |
|||||
_tab_zc: |
|
|
|
'из Вольт |
|
Data 1000! , 1! , 1! , 1! , 1! |
|
||||
Data 1000! , 1! , 1! , 1! , 1! |
'из Вольт |
|
|||
Data 1000! , 1! , 1! , 1! , 0.001! |
'из килоом |
|
|||
Data 1000! , 1! , 1! , 1! , 0.001! |
'из миллиампер |
|
|||
Data 1000! , 1! , 1! , 1! , 0.001! |
'из миллиампер |
|
|||
'-------------------------------------------------------------- |
|
|
|
|
|
'иммитируемые измеренные значения в соответствующей размерности |
|||||
_tab_mes: |
|
|
|
|
'DCV: Вольт |
Data 0.001! , -1.9999! , -1.999! , 199.99! , 1999.9! |
|||||
Data 0.001! , 0.0123! , 10.000! , 100.00! , 1000.0! |
'ACV: Вольт |
||||
Data 0.09999! , 1.9999! , 19.9! , 199.99! , 1999.9! |
'Ohm: Килоом |
||||
Data 0.20000! , -2.0000! , -20.0! , -19.00! , 20.0! |
'DCI: Миллиампер |
||||
Data 0.12345! , 1.2345! , 12.345! , 123.45! , 1234.5! 'ACI: Миллиампер '--------------------------------------------------------------
При программировании вывода на индикатор (и вывода в COM-порт тоже) нужно учитывать, что всегда имеет место погрешность представления чисел в формате с плавающей точкой. Погрешность также возникает после арифметических действий и преобразований в результате неопределенности младшего разряда мантиссы, который или теряется, или округляется. Значение погрешности обычно не превышает ±0.000012 %, и намного ниже погрешности отображаемых данных. При отображении измеренных (или других определяемых) данных и записанных в формате с плавающей точкой, погрешность представления чисел не имеет большого значения, т.к. этого не видно. Однако, при индикации задаваемых значений чисел, например, с помощью оператора INPUT и представленных в переменных типа Single, с этим могут возникать проблемы. Например, введенное число 100 после обработки на индикаторе будет записано, как 99.999993. С точки зрения математики, ничего страшного не произошло, но для наблюдателя это явление очень неприятно и, зачастую, непонятно. Более того, будучи отформатировано, отображаемое значение приобретет вообще недопустимый вид. Например, если, указанное выше, число 100 записать без дробной части, то получится 99. Чтобы исключить подобные неприятности, следует применять округление чисел перед форматированием. Самый простой метод округления заключается в добавлении к числу значения равного половине младшего отображаемого разряда. В нашем примере, чтобы получить правильное изображение, нужно к числу 100 добавить число 0.5. Во многих случаях, округляющая добавка может быть значительно меньше и, вообще, приближаться к погрешности представления числа. Так как для округления используется операция суммирования, то нужно учитывать и знак округляемого числа.
Рассмотрим еще один способ форматирования выводимых чисел, применимый при малом числе отображаемых разрядов (до пяти включительно). Этот простой способ заключается в суммировании отображаемого числа с другим, заведомо большим числом, и выделении после преобразования в символьный вид необходимой средней части. Ниже приведен пример, демонстрирующий этот метод и округление для придания изображению строгого вида.
Dim R_fld |
As Single |
'индицируемые данные |
Dim Temp |
As String * 16 |
'временная строка |
R_fld = |
-345.678 |
'курсор в позицию вывода знака |
Locate |
1 , 6 |
============================================================================= 16-5
===================================== Справочник по программированию «Bascom-8051» ==
If R_fld < 0 Then |
'какая полярность? |
Goto Lcd_fn |
'отрицательная - переход |
End If |
|
'положительное: предстоит отображать число в R_fld в следующем виде: +xxx.xx R_fld = R_fld + 1000.005 'добавим заведо большее число и добавку округления Temp = Str(r_fld) 'преобразуем в строку
Temp = Mid(temp , 2 , 6) 'выделим из середины то, что нужно – 6 символов
Lcd "+" ; Temp |
'выведем со знаком полярности |
Goto Lcd_e |
|
Lcd_fn: |
|
'отрицательное: предстоит отображать число в R_fld в следующем виде: -xxx.xx R_fld = R_fld - 1000.005 'добавим заведо большее число и добавку округления
$asm
Anl {r_fld + 3} , #&H7f ;сделаем показания безусловно положительными $end Asm
Temp = Str(r_fld) : Temp = Mid(temp , 2 , 6) : Lcd "-" ; Temp Lcd_e:
End
В последних версиях компилятора расширены возможности функции Fusing. Теперь по умолчанию включается режим округления и предусмотрена возможность добавления лидирующих нулей. Однако неприятности представления отрицательных чисел остались. Когда в маску записываются лидирующие нули, то требуется записать их на один больше, чем количество выводимых цифр перед точкой. Все это демонстрирует следующий пример.
$large
Dim X As Single , Y As Single , Result As Single
Dim I As Integer , Buf As String * 16 |
|
X = 142 : Y = 3.157 |
'вычисления |
Print "X+Y=" : Result = X + Y |
|
Print Result |
'печать результата "145.69977" |
Buf = Fusing(result , 000000.#) |
'форматирование строки c округлением |
Print Buf |
'печать строки "00145.2" |
Buf = Fusing(result , 0000.&&) |
'с лидирующими нулями и без округления |
Print Buf |
'печать строки "145.15" |
X = -12 : Y = 3.157 |
'вычисления |
Print "X+Y=" : Result = X + Y |
|
Print Result |
'печать результата "-8.8430004" |
Buf = Fusing(result , 000000.#) |
'форматирование строки c округлением |
Print Buf |
'печать строки "000-8.8" |
Buf = Fusing(result , ####.&&) |
'обычное, без округления |
Print Buf |
'печать строки "-8.84" |
End |
|
============================================================================= 16-6
===================================== Справочник по программированию «Bascom-8051» ==
17. Программирование последовательного порта
Применение последовательного асинхронного порта предусматривается в большинстве микропроцессорных систем. Даже в тех случаях, когда он не нужен для работы схемы, его используют (и необходимо использовать) на этапе отладки программы, для технологической настройки или при испытаниях. Последовательный порт процессора 8051 способен одновременно принимать и передавать данные, что требует такого построения программ, в которых процессы приема и передачи независимы. А вследствие того, что последовательный порт относится к разряду медленных устройств, то часто при программировании требуется осуществлять прием и передачу параллельно работе основной программы. Чтобы организовать параллельную работу программ ввода (приема), вывода (передачи) и обработки данных приходится задействовать прерывание. Лучше всего для этого использовать прерывание последовательно интерфейса, происходящего при заполнении регистра SBUF-приемника (после приема байта) и опустошении регистра SBUF-передатчика (после выдачи байта). Возможен и экзотический вариант временного разделения задач приема, передачи и обработки с помощью прерывания одного из таймеров. В любом случае, реализация всех возможностей и интенсивной работы последовательного порта представляется очень сложной программной задачей.
Использование в системе последовательного интерфейса почти всегда обусловлено работой в многопроцессорной среде непосредственно или в скрытой форме, как например, при работе с COM-портом персонального компьютера. При программировании подобных интерфейсов возникают проблемы учета глобальных временных соотношений, то есть учета в текущей программе времени реакции другой системы на переданные данные и наоборот. Эти проблемы часто и напрасно игнорируются, хотя именно глобальные процессы определяют скорость взаимодействия систем, а не только физическая скорость передачи данных, как принято думать. В результате таких просчетов может быть потеряна работоспособность многопроцессорной системы только из-за изменения производительности одного компонента, например, при его модернизации. При программировании также необходимо предусматривать и возможность возникновения аварийных ситуаций в периферии одного из компонентов в момент взаимодействия.
Следующий важный компонент программирования системы с последовательным портом - выбор протокола связи и формата данных. Считается, что чем короче передаваемое сообщение, тем лучше. В принципе это правильно, но при одном условии, что это сообщение должно быть понятным, и, самое главное, оставаться понятным в искаженном виде. Имеется в виду, не то, что в сообщении должна содержаться избыточная информация, позволяющая восстановить потерянное, а только то, что принимающая сторона должна обнаружить факт сбоя. Особенно опасно применение протоколов с чисто двоичными данными. Искажение таких данных может оказаться не только незамеченным, но и привести к потере синхронизации (опознаванию начала и конца пакетов данных). Автор продолжает настаивать на необходимость применения в протоколах только текстовых структур с разделителями в виде символов 0Dh («ВК» - возврат каретки) и 0Ah («ПС» - перевод строки). Передача данных в виде чисел, также должна производится символами. Особенно это важно для открытых систем. Причем, чем более система открыта, тем понятней должен быть язык передаваемых сообщений. За применение протоколов с текстовым форматом данных приходится платить приблизительно двукратным увеличением длины передаваемых сообщений и физическим усложнением канала связи (чтобы при необходимости скомпенсировать потерю скорости). Однако это окупает себя надежностью работы, простотой мониторинга (наблюдения) за каналом связи, отличной совместимостью со стандартными языками программирования, в том числе Bascom8051.
В проектируемой программе интерфейса, с самого начала, должен быть заложен резерв производительности (не менее чем двукратный), включающий в себя возможности расширения протокола (введения дополнительных команд) и запасы по скорости передачи и обработки данных, например, увеличения тактовой частоты процессора. Опыт программирования и эксплуатации микропроцессорных систем связи показывает, что почти всегда эти ресурсы оказываются востребованными - на этапе отладки для покрытия ошибок планирования, а позднее для модернизации.
Учет указанных факторов должен проводится на этапе проектирования системы и при оптимальном решении может обеспечить значительное сокращение времени программирования и отладки, а также объем используемых ресурсов процессора. Напротив, непродуманное прямолинейное решение протокола связи и игнорирование реальных временных соотношений в системе могут привести к значительному усложнению программы, и даже невозможности завершения работы ввиду того, что раньше будут исчерпаны ресурсы процессора. Столь длинное вступление, не касающееся непосредственного программирования на языке Bascom, тем не менее, необходимо. Это обусловлено особой важностью правильного решения части программы, обеспечивающей интерфейс с другими микропроцессорами.
Ниже предлагается программы некоторого типичного периферийного устройства, принимающего команды в виде текстовых строк и возвращающего данные также в виде текстовой строки. В программе имеются все обычные для данного случая (но немного упрощенные) компоненты: настройки системы, вхождения в стационарное состояние, декодирования и исполнения принятых сообщений, генератора событий реального времени, формирования передаваемых сообщений, системы ввода и вывода через раздельные буферы. Структура программы обеспечивает возможность параллельно (или почти параллельно) принимать и передавать данные,
============================================================================= 17-1
===================================== Справочник по программированию «Bascom-8051» ==
обрабатывать принятые сообщения и формировать выходные данные. Программа может быть дополнена операторами, которые позволят обрабатывать внешние события (через внешние прерывания) и совершать действия, задаваемые счетчиком реального времени. Особенностью программирования последовательного порта в данном примере является использование в прерывании подпрограмм буферированного ввода-вывода, написанных на ассемблере. Представляется, что это самый оптимальный вариант, который не ограничивает скорость работы последовательного интерфейса даже при частоте тактового генератора 3 МГц.
'---------------------------------------------- |
|
|
|
|
'Демонстрационная программа буферированного ввода-вывода данных через |
|
|||
'последовательный канал. Программа принимает сообщения и анализирует |
|
|||
'---------------------------------------------- |
|
|
|
|
'Если обнаруживается следующие сообщения, то: |
|
|||
'"R" - выдается строка с данными состояния системы |
|
|||
'"Lxxxxx" - загружаются данные в ЦАП |
|
|
||
'"Sx" - устанавливается режим |
|
|
|
|
'Формат строки состояния системы: "SxLxxxxxTxxx" |
|
|||
' |
режим_____^ ^ |
^ |
|
|
' |
Напряжение ЦАП_______| |
| |
|
|
' |
Текущее время_____________| |
|
||
'---------------------------------------------- |
|
'большая модель памяти |
|
|
$large |
|
|
||
Dim B_nd As Bit |
|
'бит "Приняты новые данные" |
|
|
Dim B_sd As Bit |
|
'бит "Передать новые данные" |
|
|
Dim B_entx As Bit |
'бит "Передача разрешена" |
|
||
Ri Alias Scon.0 |
|
'бит RI |
|
|
Ti Alias Scon.1 |
|
'бит TI |
|
|
Dim Rang As Byte |
|
'регистр состояния системы |
|
|
Dim Tmp As Byte |
|
'временные байтовые данные |
|
|
Dim Cnt As Byte |
|
'счетчик времени |
|
|
Dim R_ch As Byte |
|
'принятый или передаваемый символ |
|
|
Dim R_bui As Byte |
'указатель буфера ввода |
|
||
Dim R_buo As Byte |
'указатель буфера вывода |
|
||
Dim Udac As Integer |
'регистр данных ЦАП |
|
||
Dim Temp As String * 6 |
'временная строка |
|
||
Dim Inp_buf As String * 10 |
'буфер ввода |
|
||
Dim Out_buf As String * 32 |
'буфер вывода |
|
||
'--------------------- |
|
|
|
|
'подключение ЦАП AD766 |
|
|
|
|
B_datu Alias P1.0 : B_clku Alias P1.1 : B_ldu Alias P1.2 |
|
|||
'---------------------------------------------- |
|
|
|
|
Config Timer0 = Timer , Gate = Internal , Mode = 1 : Start Timer0 |
|
|||
'--------------------- |
|
|
|
|
'TIMER2 в режиме 16-бит. таймера с внутр. тактир. для синхронизации UART |
|
|||
Config Timer2 = Timer , Gate = Internal , Mode = 2 |
|
|||
$baud = 9600 |
|
'скорость 9.6 кБ |
|
|
$crystal = 12000000 |
'при кварце 12 МГц |
|
||
'инициализируем регистры, определяющие режим последовательного канала |
|
|||
Scon = &H52 |
|
'режим |
|
|
Rcap2h = &HFF : Rcap2l = &HD9 : Start Timer2 'скорость, только таким образом |
|
|||
'--------------------- |
|
'вектор прерывания |
|
|
On Timer0 Timer_0_int Nosave |
|
|||
On Serial Ser_int Nosave |
'вектор прерывания |
|
||
Enable Interrupts |
'вообще разрешить прерывания |
|
||
Enable Timer0 |
|
'разрешить прерывания таймера 0 |
|
|
'!!! -------------------------------------- |
|
|
|
|
B_entx = 0 : Reset Ri : Reset Ti |
'разрешить прерывания посл.интерфейса |
|
||
Enable Serial |
|
|
||
Out_buf = "Test_com" + Chr(13) + Chr(10) |
|
|
||
R_buo = Varptr(out_buf) |
'указатель буфера на начало |
|
||
Tmp = Peek(r_buo) : Sbuf = Tmp |
'первый символ на передачу |
|
||
Incr R_buo |
|
'укажем на второй символ |
|
|
'ВНИМАНИЕ! Следующая строка принципиально важное место! |
|
|||
Waitms 100 |
|
'ждем пока стартовое сообщение будет передано |
|
|
============================================================================= |
17-2 |
|||
===================================== Справочник по программированию «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 |
;уст. периода прерыв. 10 мс |
Mov Tl0 , #&HFD |
|
$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: |
'ждем прихода второго символа 50 мс |
|
Waitms 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 "" |
MONITOR |
" |
|
Print " ------ |
|||
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
===================================== Справочник по программированию «Bascom-8051» ==
SHIFTOUT, SPIIN, SPIOUT. Затруднения появляются, когда устройство, с которым необходимо взаимодействовать, имеет число разрядов не кратное восьми (не целое число байт) и не допускает подачу лишних импульсов на входе синхронизации. Практически таких микросхем очень мало. Чаще микросхемы, не допускающие подачу лишних импульсов, ориентированы на байтовый обмен, и поэтому проблем с ними вообще не возникает. Справедливости ради, нужно сказать, что проблема числа импульсов синхронизации возникает только для микросхем, имеющих двухпроводный интерфейс, или при попытках использования имеющиеся трехпроводные (и более) интерфейсы как двухпроводный. Использование ассемблера для написания подпрограмм ввода-вывода оправдано только в одном случае, когда эти подпрограммы вызывается в прерывании, и к ним предъявляются особые требования по скорости и количеству используемых регистров. В остальных случаях это делать не рекомендуется, т.к. библиотеки Bascom дают оптимальный код и быстродействие (50 - 70 % от возможного). Ниже приведен пример программы, работающей с типичным устройством, имеющим последовательный интерфейс. В тексте программы имеются все варианты организации интерфейса с помощью операторов последовательного вывода SHIFTOUT и SPIOUT.
'--------------------------------------------------------------
' Программа тестирование цифрового потенциометра AD8400 -2,-3 '--------------------------------------------------------------
Dim Badr As Byte |
'байтовый адрес |
|
|
Dim Value As Byte |
'значение записываемого кода |
||
Dim Wtmp As Byte |
'временные данные |
|
|
Pdin Alias P1.0 |
'подключение ЦАП |
|
|
Pcs Alias P1.2 |
|
|
|
Pclk Alias P1.3 |
|
|
|
'--------------------- |
|
|
|
Config Spi = Soft , Din = P1.0 , Dout = P1.1 , Cs = P1.2 , Clk = P1.3 |
|||
'--------------------- |
|
|
|
'TIMER2 в режиме 16-бит. таймера с внутр. тактир. для синхронизации UART |
|||
Config Timer2 = Timer , Gate = Internal , Mode = 2 |
|
||
$baud = 9600 |
'скорость 9.6 кБ |
|
|
$crystal = 12000000 |
'при кварце 12 МГц |
|
|
'--------------------- |
|
|
|
Set Pcs : Reset Pclk |
|
|
|
'Цикл |
|
|
|
Mc: |
|
'запрос адреса ЦАП |
|
Input "Enter number of DAC" , Badr |
|||
Input "Enter code for DAC" , Value |
'запрос данных ЦАП |
||
Value = Value And &B00000011 |
|
'маска |
'покажем |
Print "DAC=" ; Badr ; " " ; "Code=" ; Value |
|||
'--------------------- |
|
_ |
|
' _ временная диаграмма работы интерфейса |
|
||
'|__________________________________________| CS
'___ ____ ____ ____ ____ ____ ____ ____ ____ __
'___X____X____X____X____X____X____X____X____X__ Data
' |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
' ____| |__| |__| |__| |__| |__| |__| |__| |____ Clk |
||||||||
'--------------------- |
Gosub Ser_out |
|
|
|
'использовать подпрограмму вывода |
|||
' |
|
|
|
|||||
|
Wtmp = Makeint(value , Badr) 'сложим как: младший + старший |
|||||||
|
'второй вариант |
|
|
|
'послать два байта: адрес и данные |
|||
|
Spiout Wtmp , 2 |
|
|
|
||||
Goto Mc |
|
|
|
|
|
|
|
|
'--------------------- |
|
|
|
|
|
|
|
|
'подпрограмма последовательного вывода |
|
|||||||
Ser_out: |
|
|
|
|
'адрес в аккумулятор |
|||
|
Acc = Badr |
|
|
|
|
|||
|
Pcs = 0 |
|
|
|
|
|
|
'выдвинем старший бит адреса |
|
Pdin = Acc.1 : Set Pclk : Reset Pclk |
|||||||
|
Pdin = Acc.0 : Set Pclk : Reset Pclk |
'выдвинем младший бит адреса |
||||||
|
' Shiftout Pdin , Pclk , Badr , 1 |
'выдвинем 8-битный адрес |
||||||
|
Shiftout Pdin , Pclk , Value , 1 |
'затем данные (старший - первым) |
||||||
|
Pcs = 1 |
|
|
|
|
|
|
|
Return |
|
|
|
|
|
|
|
|
'------------------------------------------ |
|
|
|
|
|
|
|
|
============================================================================= 18-2
===================================== Справочник по программированию «Bascom-8051» ==
Следующая программа демонстрирует организацию ввода и обработки данных 12-разрядного АЦП с последовательным интерфейсом. Для считывания данных АЦП используется оператор SHIFTIN. Особенность программы заключается в том, что тактирование работы программы осуществляется с помощью таймера, а для вывода вычисляется среднее значение шестнадцати отсчетов.
'-------------------------------------------------------------- |
|
|
|
' Программа тестирования АЦП типа AD7893-3 (Шкала +-2.5 В) |
|
||
'-------------------------------------------------------------- |
'модель программы более 2 кбайт |
|
|
$large |
|
||
Dim B_dat As Bit |
'бит "Есть новые данные" |
|
|
Dim Mes As Byte |
'счетчик измерений |
|
|
'--------------------- |
'временные данные |
|
|
Dim Temp As Integer |
|
|
|
Dim R_bd As Integer |
'16-разр. двоичные данные |
|
|
'--------------------- |
'регистр данных АЦП |
|
|
Dim R_fld As Single |
|
|
|
'--------------------- |
'подключение АЦП |
|
|
B_acnv Alias P1.0 |
|
|
|
B_aclk Alias P1.1 |
|
|
|
B_adat Alias P1.2 |
|
|
|
'-------------------------------------------------------------- |
|
|
|
'TIMER0 в режиме 16-бит. счетчика |
|
|
|
Config Timer0 = Timer , Gate = Internal , Mode = 1 : Start Timer0 |
|
||
'--------------------- |
|
|
|
'TIMER2 в режиме 16-бит. таймера с внутр. тактир. для синхронизации UART |
|
||
Config Timer2 = Timer , Gate = Internal , Mode = 2 |
|
|
|
$baud = 9600 |
'скорость 9.6 кБ |
|
|
$crystal = 12000000 |
'при кварце 12 МГц |
|
|
'--------------------- |
|
|
|
'назначение режимов прерываний |
|
|
|
On Timer0 Timer_0_int Nosave |
'вектор прерывания |
|
|
Enable Interrupts |
'вообще разрешить прерывания |
|
|
Enable Timer0 |
'разрешить прерывания таймера 0 |
|
|
'------------------------------------------ |
|
|
|
Set B_acnv : Reset B_aclk |
|
|
|
Mes = 16 |
'количество усредняемых отсчетов |
|
|
'------------------------------------------ |
|
|
|
Mc: |
|
|
|
Do |
'появился бит? |
|
|
If B_dat = 1 Then |
|
|
|
Reset B_dat |
'сбросить этот бит |
|
|
R_bd = R_bd + Temp |
|
|
|
Decr Mes |
|
|
|
If Mes = 0 Then |
|
|
|
R_fld = R_bd : R_bd = 0 'преобразуем в плавающий формат и очистим сумму |
|
||
R_fld = R_fld * 0.0000763 'преобразуем показания в вольты |
|
||
Print R_fld : Mes = 16 |
'вывести показания и восст. счетчик |
|
|
End If |
|
|
|
End If |
|
|
|
Loop |
|
|
|
'------------------------------------------ |
|
|
|
'обработка прерывания таймера 0 |
|
|
|
Timer_0_int: |
|
|
|
Th0 = &HD8 : Tl0 = &HFD 'Ffffh-10000 = D8fdh - период прерыв. 10 мс |
|
||
Set B_dat |
|
|
|
Return |
|
|
|
'------------------------------------------ |
|
|
|
'запустить преобразование и считать данные |
|
|
|
Convert: |
|
'сформировать импульс |
|
Reset B_acnv : Reset B_acnv : Set B_acnv |
|
||
'до считывания нужно выдержать 6 мкс реально получается больше |
|
||
'--------------------- |
|
|
|
============================================================================= |
18-3 |
||
===================================== Справочник по программированию «Bascom-8051» ==
' _ |
временная диаграмма работы интерфейса |
__ |
' |
|________________________________________| |
CS |
'___ ____ ____ ____ ____ ____ ____ ____ ____ _
'___X____X____X____X____X____X____X____X____X_ Data
' |
__ |
__ |
__ |
__ |
__ |
__ |
__ |
|_| |
__ |
' __| |_| |
|_| |_| |
|_| |_| |
|_| |
|____ Clk |
|||||
'--------------------- |
Shiftin B_adat , B_aclk , Temp , |
1 |
'подходит режим 1 |
||||||
|
|||||||||
Return |
|
|
|
|
|
|
|
|
|
'---------------------------------------------- |
|
|
|
|
|
|
|
|
|
|
При |
программировании |
аналоговых |
преобразователей всегда приходится выбирать размерность |
|||||
воспроизводимых или измеренных данных. Лучше и понятней, когда данные представлены в общепринятых единицах, и при этом все числовые значения, принимаемые измеряемым (или воспроизводимым) параметром в процессе работы, соответствуют используемому устройству отображения. Имеется в виду, что, например, нельзя строить измерительную систему, в которой на 3.5-разрядный индикатор должно выводится число 10000 Гц или 20000 кОм. Понятно, форма представления чисел в программе должна быть такой, чтобы указанные числа выводились как 10.00 кГц и 020.0 МОм. Удобней всего, когда числа в программе представлены в формате с плавающей точкой. Это позволяет работать с широким диапазоном значений без потери точности и производить любые вычисления. Микросхемы ЦАП и АЦП принимают и выдают значения, представленные в виде целого двоичного кода числа младших разрядов. Bascom предоставляет прекрасные возможности преобразования числа в формате с плавающей точкой в код загрузки ЦАП (в целое двоичное число) и, соответственно, выходного кода АЦП в число в формате с плавающей точкой, что показано в предыдущих примерах и последующей программе. При преобразовании двухполярного числа нужно позаботится, чтобы источником (или приемником) двоичных данных была знаковая переменная типа Integer или Long, и чтобы старший (знаковый) бит был заполнен, как это сделано в следующей демонстрационной программе (тестирования AD7711A) с помощью оператора ROTATE. Если преобразуется однополярное двоичное число, то его расположение в регистре двоичных данных имеет только то значение, что должно соответствовать разрядам ЦАП или АЦП.
Второй составляющей преобразования кода является операция умножения на масштабирующий коэффициент. В случае загрузки ЦАП с помощью масштабирующего коэффициента происходит переход от принятой размерности к числу устанавливаемых младших разрядов (смотри пример в программе монитора синтезатора частоты). Чтобы привести к требуемой размерности данные АЦП, выраженные в единицах младшего разряда, также применяется соответствующий масштабный коэффициент.
Ниже представлен еще пример программы, работающей с АЦП. В ней осуществляется тестирование 24разрядного АЦП, связанного с процессором через последовательный интерфейс. АЦП перед началом работы инициализируется и проводится его самокалибровка (калибровка нуля и калибровка шкалы). В данной программе АЦП является источником сигнала синхронизации для всей программы. Установив при инициализации АЦП время измерения (интегрирования, т.к. АЦП интегрирующего типа) равное 20 мс и подав сигнал готовности данных АЦП на вход прерывания, мы будем каждые 20 мс получать новый отсчет. Вследствие того, что вывод и визуальное восприятие данных с такой скоростью невозможно, применено усреднение данных шестнадцати отсчетов. Теперь, каждые 320 мс (3 раза в секунду) мы будем получать результат. В программе считывания данных АЦП применено много ассемблерных вставок для повышения скорости работы и лучшего контроля ресурсов, используемых в прерывании. В программе предусмотрена калибровка АЦП, информация о которой будет представлена ниже.
'--------------------------------------------------------------
' Программа тестирования 24-разр. АЦП AD7711A (+-2.5 В) '--------------------------------------------------------------
$large |
'модель программы более 2 кбайт |
Dim B_dat As Bit |
'бит "Есть новые данные" |
Dim Mes As Byte |
'счетчик измерений |
Dim R_ch As Byte |
'введеный символ |
'--------------------- |
'временные данные |
Dim Temp As Long |
|
Dim R_bd As Long |
'32-разр. двоичные данные |
'--------------------- |
'регистр данных АЦП |
Dim R_fld As Single |
|
Dim R_zero As Single |
'константа смещения нуля |
Dim R_scal As Single |
'константа калибровки масштаба |
'-------------------------------------------------------------- |
|
'TIMER0 в режиме 16-бит. счетчика
Config Timer0 = Timer , Gate = Internal , Mode = 1 : Start Timer0 '---------------------
============================================================================= 18-4
===================================== Справочник по программированию «Bascom-8051» ==
'TIMER2 в режиме 16-бит. таймера с внутр. тактир. для синхронизации UART Config Timer2 = Timer , Gate = Internal , Mode = 2
$baud = 9600 |
'скорость 9.6 кБ |
|
$crystal = 12000000 |
'при кварце 12 МГц |
|
'--------------------- |
'вектор INT0 |
|
On Int0 Int0_int Nosave |
||
Enable Interrupts |
'вообще разрешить прерывания |
|
'------------------------------------------ |
'самокалибровка и установка режима АЦП |
|
Gosub Str_adc |
||
Mes = 16 |
'количество усредняемых отсчетов |
|
R_zero = 0 |
'нет смещение нуля |
|
R_scal = 1 |
'масштаб идеальный |
|
Print " - MONITOR FOR ADC AD7711A - " |
|
|
Print " Calb zero - Z |
@ Uinp = 0 V " |
|
Print " Calb scale - S |
@ Uinp = + 2 V" |
|
Enable Int0 |
'разрешить внешнее прерывания 0 |
|
'------------------------------------------ |
|
|
Mc: |
|
|
Do |
'появился бит? |
|
If B_dat = 1 Then |
|
|
Reset B_dat |
'сбросить этот бит |
|
Rotate R_bd , Left , 4 |
'показания еще нужно умножить на 16 |
|
'таким образом, сумма 16-ти |
отсчетов полностью расположиться в 32-битн. регистре |
|
R_fld = R_bd : R_bd = 0 |
'преобразуем в плавающий формат и очистим сумму |
|
R_fld = R_fld * 0.00000000116415322 |
'преобразуем показания в вольты |
|
R_ch = Inkey |
'если нажата кнопка - проверить |
|
If R_ch <> 0 Then |
||
Gosub Makeclb |
'при необходимости калибровать |
|
End If |
'учесть калибровку |
|
Gosub Calbr |
||
Print R_fld |
'вывести показания |
|
End If |
|
|
Loop '------------------------------------------
'обработка прерывания таймера 0 Int0_int:
$asm
Push Psw
Push Acc $end Asm
Psw.3 = 1 : Psw.4 = 1 'выберем третий банк - он не используется Bascom Gosub Rd_adc
$asm
|
djnz {Mes} , Int0_1 |
;обработка счетчика измерений |
|
|
Setb {B_dat} |
;если Mes=0, есть новые данные АЦП |
|
|
mov {Mes} , #16 |
;восстановить счетчик |
|
Int0_1: |
|
||
|
Pop Acc |
|
|
|
Pop Psw |
|
|
$end Asm |
|
||
' |
Return |
|
|
УТИЛИТЫ СИГМА-ДЕЛЬТА АЦП ТИПА AD7711A |
|||
' |
|||
'----------------------------------------------
'регистры AD7711
' формат регистра управления
' --------- MD2 0 норм. 0 самокалиб.
'¦-------- MD1 0 изм. 0 в выбранн.
' |
¦¦------- |
MD0 0 |
0 |
1 |
канале |
1 |
1 |
1 |
1 |
||
' |
¦¦¦------ |
G2 |
0 |
0 |
|
0 |
|||||
' |
¦¦¦¦----- |
G1 |
0=1 |
0=2 |
1=4 |
1=8 |
0=16 |
0=32 |
1=64 |
1=128 |
|
' |
¦¦¦¦¦---- |
G0 |
0 |
1 |
0 |
|
1 |
0 |
1 |
0 |
1 |
' |
¦¦¦¦¦¦--- |
CH 0-AIN1, 1-AIN2 |
|
|
|
|
|
||||
============================================================================= 18-5
===================================== Справочник по программированию «Bascom-8051» ==
' |
¦¦¦¦¦¦¦-- PD 0-работа, 1-останов |
|
|
||
' |
¦¦¦¦¦¦¦¦--------- |
WL Выход: 0-16 бит, 1-24 бита |
|||
' |
¦¦¦¦¦¦¦¦¦-------- |
IO Вых. ток RTD 0-выкл, 1-вкл |
|||
' |
¦¦¦¦¦¦¦¦¦¦------- |
BO Вых. ток BO |
|
0-выкл, 1-вкл |
|
' |
¦¦¦¦¦¦¦¦¦¦¦------ |
B/U Вход: 0-двухполярный, 1-однополярн. |
|||
' |
¦¦¦¦¦¦¦¦¦¦¦¦------------- |
|
FS11=0¬ |
при кварце 5 МГц: |
|
' |
¦¦¦¦¦¦¦¦¦¦¦¦¦------------ |
|
FS10 |
¦ |
|
' |
¦¦¦¦¦¦¦¦¦¦¦¦¦¦----------- |
|
FS9 |
¦ |
50 Гц FS=135 (0С3h) |
' |
¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦---------- |
|
FS8 |
¦ |
(fкв/512) |
' |
¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦--------- |
|
FS7 |
¦- FS = ---------------------- |
|
' |
¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦-------- |
|
FS6 |
¦ |
частота первого полюса |
' |
¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ |
------- |
FS5 |
¦ |
|
' |
¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦------ |
FS4 |
¦- FS - код первого полюса |
||
' |
¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦----- |
FS3 |
¦ |
от 19 до 2000 |
|
' |
¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦---- |
FS2 |
¦ |
|
|
' |
¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦--- |
FS1 |
¦ |
|
|
'¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦-- FS0 ---
'C_M2B |
equ 00000000b |
'старший байт |
рабочего режима: |
|
'C_M1B |
equ |
10000000b |
'средний байт |
50 Гц, G=1, +-2.5 В |
'C_M0B |
equ |
11000011b 'младший байт |
CH1, 24 бита, ток выкл |
|
'--------- |
equ 00100000b |
'старший байт |
самокалибровки: |
|
'C_С2B |
||||
'C_С1B equ |
10000000b |
'средний байт |
50 Гц, G=1, +-2.5 В |
|
'C_С0B equ |
11000011b 'младший байт |
CH1, 24 бита, ток выкл |
||
'---------------------------------------------- |
|
|
|
|
'определение констант |
|
|
||
Dim C_m2b As Const &B00000000 |
|
|
||
Dim C_m1b As Const &B10000000 |
|
|
||
Dim C_m0b As Const &B11000011 |
|
|
||
Dim C_c2b As Const &B00100000 |
|
|
||
Dim C_c1b As Const &B10000000 |
|
|
||
Dim C_c0b As Const &B11000011 |
|
|
||
'---------------------------------------------- |
|
|
|
|
'НАЗНАЧЕНИЕ ПОРТОВ, ПОДКЛЮЧЕННЫХ К АЦП |
|
|||
B_aa0 Alias P1.0 |
|
'ПОРТ ВЫБОРА РЕГИСТРА АЦП: 0-сост. 1-данных |
||
B_atfs Alias P3.5 |
'ПОРТ РАЗРЕШЕНИЯ ЗАПИСИ АЦП |
|||
B_arfs Alias P1.3 |
'ПОРТ РАЗРЕШЕНИЯ СЧИТЫВАНИЯ АЦП |
|||
B_aclk Alias P1.1 |
'ПОРТ ТАКТОВОГО СИГНАЛА ДЛЯ ДАННЫХ АЦП |
|||
B_adat Alias P1.2 |
'ДАННЫЕ АЦП |
|
||
B_ardy Alias P1.4 |
'ГОТОВНОСТЬ ДАННЫХ АЦП |
|||
'---------------------------------------------- |
|
|
|
|
'ЗАПИСЬ 8 БИТ В АЦП ИЗ АККУМУЛЯТОРА |
|
|||
Wrb_adc: |
|
|
|
|
$asm |
Mov |
R0 , #8 |
|
|
|
|
|
||
Wrb_adc1: |
A |
;данные в SDATA |
|
|
|
Rlc |
|
||
|
Mov |
{B_adat} , C |
;защелкнуть - SCLK в "1" |
|
|
Setb |
{B_aclk} |
||
|
Clr |
{B_aclk} |
;SCLK в "0" |
|
$end Asm |
Djnz R0 , Wrb_adc1 |
|
|
|
Return |
|
|
|
|
' |
|
|
|
|
|
|
|
|
|
'ЧТЕНИЕ 8 БИТ ИЗ АЦП В АККУМУЛЯТОР |
|
|||
Rdb_adc: |
|
|
|
|
$asm |
Mov |
R0 , #8 |
|
|
|
|
|
||
Rdb_adc1: |
|
;защелкнуть - SCLK в "1" |
||
|
Setb {b_aclk} |
|||
|
Mov c , {B_adat} |
;SCLK в "0" |
|
|
|
Clr |
{B_aclk} |
|
|
|
Rlc |
A |
;данные в Acc |
|
============================================================================= 18-6
===================================== Справочник по программированию «Bascom-8051» ==
Djnz R0 , Rdb_adc1
$end Asm
Return '----------------------------------------------
'Начальная подготовка АЦП к работе:
'самокалибровка - установка режима
'---------
Str_adc:
Reset B_aclk : Reset B_aa0 : Reset B_atfs 'SCLK=0, A0=0, разрешим запись
Acc = C_c2b : Gosub Wrb_adc 'назначить самокалибровку АЦП
Acc = C_c1b : Gosub Wrb_adc Acc = C_c0b : Gosub Wrb_adc
Set B_atfs : Wait 2 'запретим запись и ждать 2 сек
Reset |
B_atfs |
'разрешить |
запись |
Acc = |
C_m2b : Gosub Wrb_adc 'назначить |
нормальный режим АЦП |
|
Acc = C_m1b : Gosub Wrb_adc
Acc = C_m0b : Gosub Wrb_adc
Set B_atfs : Set B_adat |
'запретим запись подготовиться к приему данных |
Return |
|
'---------------------------------------------- |
|
'Программа считывания данных из АЦП |
|
'Производится |
первичная обработка данных - они |
|
'складываются с регистром суммы с учетом знака |
||
Rd_adc: |
|
'подготовить порты |
B_adat = 1 : B_aclk = 0 |
||
B_aa0 = 1 : B_arfs = 0 |
'разрешить чтение АЦП |
|
'--------- |
R1 , #{Temp + 3} |
|
Mov |
||
Mov |
@R1 , #0 |
|
Dec |
R1 |
|
Gosub Rdb_adc |
;запомним старший байт |
|
Mov @r1 , A |
||
Dec |
R1 |
|
Gosub Rdb_adc |
;запомним средний байт |
|
Mov @r1 , A |
||
Dec R1 |
|
|
Gosub Rdb_adc |
;запомним младший байт |
|
Mov @r1 , A |
||
'--------- |
|
'запретить чтение АЦП |
B_arfs = 1 |
|
|
'суммирование показаний |
|
|
$asm |
|
|
Mov R0 , #{R_bd} Mov R1 , #{Temp} Mov R2 , #4
Clr C
;сумма данные ;считанные данные ;четыре байта
Rd_adc1: |
;цикл суммирования |
Mov A , @R1 |
|
Addc A , @R0 |
|
Mov @R0 , A |
|
Inc R0 |
|
Inc R1 |
|
Djnz R2 , Rd_adc1 |
|
$end Asm |
|
Return |
|
'--------- |
|
'калибровка показаний |
|
Calbr: |
'скорректировать смещение |
R_fld = R_fld - R_zero |
|
R_fld = R_fld * R_scal |
'поправить масштаб |
Return |
|
'--------- |
|
'провести калибровку |
|
============================================================================= 18-7
===================================== Справочник по программированию «Bascom-8051» ==
Makeclb: |
'если введен Z - калибровать нуль |
If R_ch = &H5B Then |
|
R_zero = R_fld |
'показания - константа смещения |
End If |
'если введен S - калибровать масштаб |
If R_ch = &H53 Then |
|
R_fld = R_fld – R_zero'учтем поправку нуля и с ней |
|
R_scal = 2 / R_fld |
'вычислим константу масштаба |
End If |
|
R_ch = 0 |
|
Return |
|
'------------------------------- |
|
Следующий пример - программа тестирования цифрового синтезатора. В ней значение в формате с плавающей точкой и выраженной в килогерцах, преобразуется в значение количества единиц младшего разряда ЦАП, затем целое двоичное число и загружается в регистры синтезатора. Как и в предыдущих примерах, микросхема синтезатора подключена к процессору через последовательный интерфейс. Количество загружаемых бит – 40.
'-------------------------------------------------------------- |
Монитор тестирования синтеза AD9850 в режиме с |
|
||
' |
|
|||
' |
последовательной загрузкой данных. Кварц = 60 МГц |
|
||
'-------------------------------------------------------------- |
|
|
'бит "Есть новые данные" |
|
Dim N_dat As Bit |
|
|
||
N_inp Alias Scon.0 |
|
'бит "Есть ввод" (RI) |
|
|
'--------------------- |
|
|
'временные данные |
|
Dim Temp As Byte |
|
|
||
'--------------------- |
|
|
'32-разр. двоичный код загрузки |
|
Dim R_bd As Long |
|
|
||
'--------------------- |
|
|
'регистр значения частоты |
|
Dim R_frq As Single |
|
|
||
Dim R_fld As Single |
|
'регистр кода загрузки |
|
|
'--------------------- |
|
|
|
|
'подключение синтезатора |
|
|
|
|
B_data Alias P1.5 : B_clk Alias P1.3 : B_fqud Alias P1.4 : B_res Alias P1.7 |
|
|||
'--------------------- |
|
|
|
|
' TIMER2 в режиме 16-бит. таймера с внутр. тактир. для синхронизации UART |
|
|||
Config Timer2 = Timer , Gate = Internal , Mode = 2 |
|
|||
$baud = 9600 |
|
'скорость 9.6 кБ |
|
|
$crystal = 12000000 |
|
'при кварце 12 МГц |
|
|
'--------------------- |
|
|
|
|
'подготовить линии управления к работе |
|
|||
Reset B_data : Reset B_clk : Reset B_fqud : Reset B_res |
|
|||
'--------------------- |
|
|
|
|
'главный цикл |
|
|
|
|
Mc: |
|
|
|
|
Do |
|
'есть новые данные? |
|
|
If N_dat = 1 Then |
|
|||
|
N_dat = 0 |
'на самом деле это бит RI |
|
|
|
Gosub Sload_9850 |
'загрузка новых данных |
|
|
End If |
'что-то принято? |
|
||
If N_inp = 1 Then |
|
|||
|
N_inp = 0 |
'очистим без анализа |
|
|
|
Input "Enter value of frequency [kHz] f= " , R_frq 'ввод частоты |
|
||
|
Print R_frq |
'печатать введенное значение |
|
|
|
|
'чтобы получить код загрузки умножим на число: |
|
|
|
R_fld = R_frq * 71582.788 ' 2^32 / Fclk = 4294967296 / 60000 kHz |
|
||
|
R_bd = R_fld |
'преобр. в целое 32-разр. число |
|
|
|
Printhex "Hex " ; R_bd |
'печать в hex формате |
|
|
|
Set N_dat |
'есть данные на вывод в синтезатор |
|
|
End If |
|
|
|
|
Loop |
|
|
|
|
'------------------------------------------ |
|
|
|
|
============================================================================= |
18-8 |
|||
===================================== Справочник по программированию «Bascom-8051» ==
'подпрограммы загрузки данных в синтезатор
Sload_9850:
Set B_fqud : Reset B_fqud |
'сбросить интерфейс |
Set B_clk : Reset B_clk |
'защелкнуть код последовательного режима |
Set B_fqud : Reset B_fqud |
'разрешить последовательный режим |
'выдвинуть данные из R_bd в режиме 3 (мл. сначала , _/\_) Shiftout B_data , B_clk , R_bd , 3
'выдвинуть последний байт с режимом и фазой
Temp = 0 : Shiftout B_data , B_clk , Temp , 3
Set B_fqud : Reset B_fqud 'исполнять загруженные данные
Return '------------------------------------------
Почти всегда точность аналоговых преобразователей недостаточна для решения измерительной задачи. В данном случае речь идет измерения или воспроизведения абсолютного значения напряжения, тока или чего-либо еще. Типичная точность аналоговых преобразователей составляет единицы или, в лучшем случае, доли процента – результат неидеальности технологии их производства. С другой стороны, стабильность метрологических характеристик этих же преобразователе на два-три порядка (десятичных) превосходит их параметрическую точность. Таким образом, выравнивая масштаб преобразования с внешним эталоном, можно достичь повышения их точности до значений стабильности (временной, температурной), нелинейности и шумов. Такая операция называется калибровкой, в результате которой устанавливается коэффициент преобразования АЦП или ЦАП. Калибровка может производиться в аналоговом виде, например, подстройкой напряжения опорного источника, но нас будет интересовать только цифровая калибровка. Ее преимущества очевидны. Во-первых, аналоговая часть схемы не усложняется. Во-вторых, калибровка может проводиться без доступа внутрь калибруемого объекта, и с удаленным объектом тоже. Самое замечательное, что калибровка производится в том состоянии устройства, в котором он и работает. Имеется возможность снизить и случайные составляющие операции калибровки. В итоге, повышается точность измерительного преобразователя, причем даже в большей степени, чем при аналоговой калибровке. Результаты цифровой калибровки, представляющие собой цифровые коэффициенты обычно размещаются в энергонезависимой памяти, чтобы их можно было воспроизвести при повторном включении питания калибруемого устройства. Не останавливаясь на методах организации хранения данных калибровки (это отдельный вопрос), рассмотрим методы калибровки.
Практически все методы цифровой калибровки можно свести всего к двум видам:
а) калибровки масштаба (коррекция мультипликативной составляющей погрешности). Конечно, калибровка масштаба может состоять из нескольких составляющих, но, все равно, каждый раз, это будет операция умножения на корректирующий масштабный коэффициент, зависимый от каких то параметров;
б) калибровка смещения (коррекция аддитивной составляющей погрешности преобразователя).
Можно еще говорить о калибровке нелинейности преобразователя, но этот метод применяется очень редко и всегда в дополнение, к уже указанным.
Калибровка смещения измерительного тракта учитывается путем вычитания из текущих показаний, значения, ранее запомненного при измерении напряжения на закороченном входе (U = 0). Поправка масштаба производится умножением на коэффициент, определенный как отношения эталонного значения к текущим показаниям при подаче на вход напряжения, равного эталонному значению. В программе монитора АЦП AD7711A, приведенной выше, применена и калибровка смещения и масштаба по указанной схеме. Там же имеется процедура калибровки, когда с клавиатуры подается команда произвести калибровку нуля или масштаба. В демонстрационной программе все несколько упрощено, но требуемый порядок полностью выполнен (вначале калибровка нуля, затем масштаба).
Калибровка ЦАП реализуются также, но с точностью, наоборот, вследствие обратного направления преобразования загружаемого кода. Вначале приводится в действие масштабный коэффициент, а затем из значения загружаемого кода вычитается поправка смещения нуля. Порядок же определения коэффициентов и для ЦАП и для АЦП одинаков - вначале определяется поправка смещения, а затем масштаба. Это значительно облегчает процедуру определения поправочных коэффициентов, делая процесс одноступенчатым (без итерационных циклов).
Как указывалось выше, скорость измерения АЦП обычно намного превосходит возможности устройств передачи и отображения данных. Так происходит потому, что конечным приемником информации является человек, для которого пределом комфортного восприятия считается частота смены данных в пределах до 3 - 5 Гц. Это обстоятельство можно использовать с максимальной пользой, например, организовать усреднение избыточных отсчетов АЦП. В результате, достигается повышение точности и разрешающей способности за счет дополнительного подавления шумов и помех. Особенно актуальна эта мера для многоразрядных АЦП (более 14 - 16 разрядов), младшие разряды которых не всегда могут быть реализованы в одиничном цикле преобразования. Усреднение может производиться простым суммированием нескольких отсчетов, так и с помощью алгоритмов цифровой фильтрации. Отличие фильтрации от усреднения в том, что при усреднении поток данных визуально прореживается (данные поступают реже), а при фильтрации скорость выдачи данных может остается прежней, однако, каждый выходной отсчет цифрового фильтра будет нести какую-то часть предыдущих отсчетов. Для
============================================================================= 18-9
===================================== Справочник по программированию «Bascom-8051» ==
наблюдателя процесса фильтрации также важен промежуток вхождения в стационарный режим фильтрации после ее включения, т.е. пока необходимый объем обрабатываемых данных еще не накоплен, на выходе программы не должно быть сбойных показаний. Это возможно, если при включении режима фильтрации сбросить параметр фильтрации, имеющий значение, аналогичное постоянной времени обычного фильтра, а затем плавно увеличивать его (соответственно и степень фильтрации до заданной). Ниже представлен фрагмент программы, содержащий самую простую подпрограмму цифровой фильтрации. В представленном примере предусмотрена реализация плавного вхождения в процесс фильтрации. Представленная программа аналогична фильтру первого порядка. Для повышения порядка фильтра достаточно включить в процесс обработки две-три (или более) аналогичные программы, работающие последовательно и с различными коэффициентами, которые и установят характер (модель) фильтра.
'------------------------------------------------------------------------
'Программа обработки по формуле фильтра первого порядка
'вызывается каждый раз в цикле измерения
'------------------------------------------------------------------------ |
'бит стабильность |
Dim B_stan As Bit |
|
Dim Uav As Single |
'выходное значение |
Dim R_an As Single |
'параметр фильтрации |
Dim Uc As Single |
'текущие показания |
Dim Tmp As Single |
'временные данные |
Dim N_m As Const 0.9 |
'параметр уменьшения |
Dim N_f As Const 0.3 |
'окончательное значение R_an |
R_an = 1 |
'исходное значение параметра фильтрации |
B_stan = 0 |
'сбросить при включении фильтрации |
'Uav = Uav(1-1/N) + Uc/N - КЛАССИЧЕСКАЯ ФОРМУЛА ФИЛЬТРА
' Uav = Uav(1-n) + Uc*n - УПРОЩЕННАЯ ФОРМУЛА
'ОБЕСПЕЧИВАЕТ СОКРАЩЕНИЕ ВРЕМЕНИ ВЫЧИСЛЕНИЙ (ИСКЛЮЧАЕТСЯ ДЕЛЕНИЕ И 'ОДНО ДЕЙСТВИЕ) И ПРАКТИЧЕСКИ НЕОТЛИЧИМА ОТ КЛАССИЧЕСКОГО ВАРИАНТА 'ПРИ НАБЛЮДЕНИИ ПО ИНДИКАТОРУ
'ГДЕ: n - ТЕКУЩЕЕ ЗНАЧЕНИЕ ПАРАМЕТРА УСРЕДНЕНИЯ (R_an)
'Uc - ДАННЫЕ АЦП
'Uav - ТЕКУЩЕЕ И НОВОЕ СРЕДНЕЕ ЗНАЧЕНИЕ
'ЗНАЧЕНИЕ КОНСТАНТЫ ФИЛЬТРАЦИИ, КОТОРОЕ ПРИ ВКЛЮЧЕНИИ РЕЖИМА ФИЛЬТРА 'РАВНО 1, ЗАТЕМ С КАЖДЫМ ИЗМЕРИТЕЛЬНЫМ ТАКТОМ УМЕНЬШАЕТСЯ
'В 1.11 РАЗА(1/N_m) ДО МИНИМАЛЬНОГО ЗНАЧЕНИЯ (N_f). ЗНАЧЕНИЯ N_m И N_f 'ПОДОБИРАЮТСЯ ЭКСПЕРИМЕНТАЛЬНО, ЧТОБЫ ОБЕСПЕЧИТЬ КОМФОРТНОЕ ВРЕМЯ 'УСТАНОВЛЕНИЯ, И ДОСТАТОЧНОЕ ПОДАВЛЕНИЕ ШУМОВ.
Comp_av: |
'установился стационарный режим? |
If B_stan = 1 Then |
|
Goto C_av2 |
'теперь достигли окончательного значения? |
End If |
'изменим значение параметра фильтрации |
R_an = R_an * N_m |
|
If R_an < N_f Then |
'теперь достигли окончательного значения? |
Set B_stan |
'да - поставим бит стабильности |
End If |
|
C_av2: |
'вычтем константу фильтрации из единицы |
Tmp = 1 - R_an |
|
Uav = Uav * Tmp |
'возьмем из суммы соответствующую часть |
Tmp = Uc * R_an |
'возьмем часть показаний АЦП |
Uav = Uav + Tmp |
'сложим с суммой |
Return |
|
При программировании средствами Bascom часто приходится использовать переменные, не соответствующие по длине разрядной сетке ЦАП. При этом загружаемый в ЦАП код размещается в младших разрядах переменной, в результате чего, возникает опасность переполнения разрядной сетки. Чтобы исключить такую возможность необходимо перед преобразованием проверять максимальное значение вводимого числа или числа, полученного после вычислений.
Легкость программирования Bascom провоцирует применение неоптимальных схем обработки данных (в первую очередь по критерию производительности). Чтобы успешно бороться с этим «злом», рекомендуется в максимальной степени применять обработку данных на низком уровне (непосредственно в кодах загрузки ЦАП или с выходным кодом АЦП). Это, конечно, сложнее для программиста, но дает существенный выигрыш, т.к.
============================================================================= 18-10
===================================== Справочник по программированию «Bascom-8051» ==
целые двоичные числа быстрее обрабатываются и занимают меньше места в памяти. На низком уровне рекомендуется выполнять следующие операции:
а) анализировать перегрузку АЦП и осуществлять выбор пределов измерения, если это требуется. Это дает возможность предельно быстро реагировать на изменения входного сигнала, отдавая на «высший уровень» данные, действительно пригодные для получения конечного результата;
б) корректировать смещения АЦП и ЦАП, соответственно сохраняя поправки смешения в виде целых двоичных чисел;
в) осуществлять первичное усреднение данных АЦП и при необходимости преобразовывать в двухполярный вид;
г) записывать в массив или осуществлять сортировку, например по величине; д) вычислять в прерываниях.
============================================================================= 18-11
===================================== Справочник по программированию «Bascom-8051» ==
19.Программирование устройств с шиной Microwire
Вновой версии компилятора Bascom (начиная с 1.20) добавлены операторы для работы с микросхемами, имеющими последовательный интерфейс Microwire. Интерфейс Microwire внешне во многом похож на SPI – те же четыре линии (DI – прием данных, DO – выдача данных, CS – выбор кристалла, Clk – синхронизация). Однако, отличие от SPI существенно – число посылаемых бит не кратно восьми. Таким образом, полноценная реализация интерфейса Microwire имеющимися операторами SPI или аппаратным SPI затруднительна. Основное применение интерфейса Microwire связано с микросхемами энергонезависимой памяти серии 93С06 – 93С57, главное достоинство которых связано с высокой надежностью хранения данных и с очень высокой скоростью считывания. Современные микросхемы с интерфейсом Microwire (серия 45Сххх) имеют наибольшую емкость из всех микросхем с последовательным доступом.
Микросхемы с шиной Microwire могут работать с 8-ми и 16-разрядными данными. Выбор длины слова производится подключение вывода “ORG” шине питания (16 разрядов) или к общему проводу (8 разрядов). Соответственно этому изменяется длина адреса. Компилятор «узнает» о способе подключения микросхемы с помощью опции AL (длина адреса) оператора конфигурации Config Microwire (см главу 4). Там же описывается подключение линий шины к микроконтроллеру.
Для работы с шиной Microwire предусмотрено четыре оператора:
а) оператор MWINIT – устанавливает выводы, предназначенные для интерфейса Microwire, в исходное состояние. Применяется в начале программы, после сброса процессора;
б) оператор MWREAD – считывает значение в переменную из интерфейса Microwire. Применяется столько раз, сколько нужно произвести операций считывания данных. В зависимости от длины слова данных работает с одно или двух байтными переменными. Адрес может быть задан в непосредственном виде или с помощью одноили двухбайтной переменной;
в) оператор MWWRITE – записывает значение переменной в интерфейс Microwire. Записывает одно или двухбайтные данные. Адрес также может быть задан в непосредственном виде или с помощью одноили двухбайтной переменной. После этого оператора необходимо применять оператор временной задержки на 2-10 мс (см. описание применяемой микросхемы), т.к. опрос готовности устройства, в которое производится запись, не выполняется;
г) оператор MWWOPCODE – записывает в устройство код операции на шине Microwire. Все возможные операции приведены в справочных данных микросхем, но две из них должны применяться всегда. Это – разрешение записи (и стирания) перед началом работы и запрещение записи (стирания) после окончания работы. После некоторых команд, например, общего стирания или общей записи также необходимо применять оператор задержки на время, указанное в описании применяемой микросхемы. Однако лучше применить программу ожидания готовности, например, следующую:
Set Cs : Bitwait Dout Set : Reset Cs
Ниже приведена таблица, содержащая изображения временных диаграмм, формируемых описанными операторами с 16-битном и 8-битном режимах.
Далее предлагается пример тестовой программы с использованием шины Microwire. В этой программе создается и записывается в микросхему EEPROM массив двухбайтовых чисел, а затем он считывается обратно. Для усложнения задачи и демонстрации возможностей Bascom микросхема 93С46 включена в 8-разрядном режиме, а двухбайтовые значения записываются и считываются в два приема. Такое решение наиболее часто применяется практически, когда приходится сохранять значения переменных различной длины. В этом примере применена дополнительная программа (Mwrdyw), написанная на ассемблере и обеспечивающая ожидание появления сигнала готовности микросхемы, в которую произведена запись. Особенность этой подпрограммы заключается в том, что ожидание готовности производится только 10 мс, благодаря чему становится невозможным «зависание» процессора при неисправности микросхемы EEPROM, а при исправной микросхеме скорость работы программы увеличивается.
============================================================================= 19-1
===================================== Справочник по программированию «Bascom-8051» ==
Оператор
Mwwopcode &B100110000 , 9
Генерировать команду разрешения записи и стирания
(для 93С46 вывод “ORG”=1)
Mwwopcode &B100000000 , 9
Генерировать команду запрещения записи и стирания
(для 93С46 вывод “ORG”=1)
Mwread X , &B110 , 0 , 2
Считать значение из ячейки по адресу 0 в переменную X (два байта)
(для 93С46 вывод “ORG”=1)
Mwwrite X , &B101 , 0 , 2
Записать значение переменной X (два байта) в ячейку с адресом 0
(для 93С46 вывод “ORG”=1)
Mwwopcode &B1001100000,10
Генерировать команду разрешения записи и стирания
(для 93С46 вывод “ORG”=0)
Mwwopcode &B1000000000,10
Генерировать команду запрещения записи и стирания
(для 93С46 вывод “ORG”=0)
Mwread Y , &B110 , 0 , 1
Считать значение из ячейки по адресу 0 в переменную Y (1 байт)
(для 93С46 вывод “ORG”=0)
Mwwrite Y , &B101 , 0 , 1
Записать значение переменной Y ( 1 байт) в ячейку с адресом 0
(для 93С46 вывод “ORG”=0)
Временная диаграмма 16-битный режим
_____________________________________ |
|__ |
CS |
|
|
|
||||||||
__| |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
|
|
|
||
_ |
|
Clk |
|
|
|
||||||||
____| |_| |_| |_| |_| |_| |_| |_| |_| |____ |
|
|
|
||||||||||
___ |
|___ ___| |
___ ___ |
|___ ___ ___ ___ ___ |
DI |
|
|
|
||||||
___| |
1 |
1 |
|
|
|
||||||||
SB |
OP1 OP2 |
0 |
0 |
0 |
0 |
|
|
|
|
|
|||
_____________________________________ |
|__ |
CS |
|
|
|
||||||||
__| |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
|
|
|
||
_ |
|
Clk |
|
|
|
||||||||
____| |_| |_| |_| |_| |_| |_| |_| |_| |____ |
|
|
|
||||||||||
___ |
|___ ___ ___ ___ ___ ___ ___ ___ ___ |
DI |
|
|
|
||||||||
___| |
|
|
|
||||||||||
SB |
OP1 OP2 |
0 |
0 |
0 |
0 |
0 |
0 |
|
|
|
|
|
|
_________________________________________________ _ __________ |
|||||||||||||
__| |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
|__ CS |
_ |
_ |
||||||||||||
____| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_ _ __| |_| |____ Clk
___ ___ |
|
___ ___ ___ ___ ___ ___ |
|
|
|
|
|
||||||
___| |
|
|___X___X___X___X___X___X___\____________ _ _________ ___ DI |
|||||||||||
SB |
OP1 OP2 |
A5 |
A4 |
A3 |
A2 |
A1 |
A0 |
|
|
|
|
|
|
|
|
|
0 |
0 |
0 |
0 |
0 |
0 |
___ ___ ___ |
_ _ ___ ___ |
|||
|
|
|
|
|
|
|
|
|
|||||
-------------------------------------__X___X___X___X _ _X___X___X___ DO |
|||||||||||||
|
|
|
|
|
|
|
|
0 |
D15 |
D14 |
D13 |
D1 |
D0 |
_________________________________________________ _ __________ |
|||||||||||||
__| |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
|__ CS |
_ |
_ |
||||||||||||
____| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_ _ __| |_| |____ Clk
___ |
|___| |
___ ___ ___ ___ ___ ___ ___ ___ ___ ___ |
_ _ ___ ___ |
||||||||||
___| |
|
X___X___X___X___X___X___X___X___X___X _ _X___X___X___ DI |
|||||||||||
SB |
OP1 OP2 |
A5 |
A4 |
A3 |
A2 |
A1 |
A0 D15 D14 D13 |
D1 |
D0 |
||||
|
|
|
0 |
0 |
0 |
0 |
0 |
|
0 |
|
|
|
|
|
|
8-битный режим |
|
|
|
|
|
|
|
|
|||
________________________________________ |
|__ CS |
|
|
|
|||||||||
__| |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
|
|
|
|
_ |
|
|
|
|
|||||||||
____| |_| |_| |_| |_| |_| |_| |_| |_| |_| |____ Clk |
|
|
|||||||||||
___ |
|
|
___ ___ |
|___ ___ ___ ___ ___ ___ DI |
|
|
|||||||
___| |
|___ ___| |
1 |
|
|
|||||||||
SB |
OP1 OP2 |
1 |
0 |
0 |
0 |
0 |
0 |
|
|
|
|
||
________________________________________ |
|__ CS |
|
|
|
|||||||||
__| |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
|
|
|
|
_ |
|
|
|
|
|||||||||
____| |_| |_| |_| |_| |_| |_| |_| |_| |_| |____ Clk |
|
|
|||||||||||
___ |
|___ ___ ___ ___ ___ ___ ___ ___ ___ ___ DI |
|
|
||||||||||
___| |
|
|
|||||||||||
SB |
OP1 OP2 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
|
|
|
|
|
_________________________________________________ _ __________ |
|||||||||||||
__| |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
|__ CS |
_ |
_ |
||||||||||||
____| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_ _ __| |_| |____ |
Clk |
|||||||||||||
___ ___ |
|
___ ___ ___ ___ ___ ___ ___ |
|
|
|
|
DI |
|||||||
___| |
|
|___X___X___X___X___X___X___X___\_________ _ ____________ |
||||||||||||
SB |
OP1 OP2 |
A6 |
A5 |
A4 |
A3 |
A2 |
A1 |
A0 |
|
|
|
|
|
|
|
|
|
0 |
0 |
0 |
0 |
0 |
0 |
0 |
___ ___ |
_ _ ___ ___ |
|
||
|
|
|
|
|
|
|
|
|
|
DO |
||||
-----------------------------------------__/___X___X _ _X___X___X___ |
||||||||||||||
|
|
|
|
|
|
|
|
|
0 |
D7 |
D6 |
D1 |
D0 |
|
_________________________________________________ _ __________ |
CS |
|||||||||||||
__| |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
_ |
|__ |
|
_ |
_ |
Clk |
||||||||||||
____| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_ _ __| |_| |____ |
||||||||||||||
___ |
|
___ ___ ___ ___ ___ ___ ___ ___ |
___ ___ |
_ _ ___ ___ |
DI |
|||||||||
___| |
|___| |
X___X___X___X___X___X___X___X___X___X _ _X___X___X___ |
||||||||||||
SB |
OP1 OP2 |
A6 |
A5 |
A4 |
A3 |
A2 |
A1 |
A0 |
D7 |
D6 |
D1 |
D0 |
|
|
|
|
|
0 |
0 |
0 |
0 |
0 |
0 |
0 |
|
|
|
|
|
============================================================================= 19-2
===================================== Справочник по программированию «Bascom-8051» ==
'-------------------------------------------------------------
' тестирование устройства на шине Microwire '-------------------------------------------------------------
Dim Tmp1 As Byte , Tmp2 As Byte
Dim Tmp3 As Byte , Tmp4 As Byte , Tmpw As Word Dim Tmp(20) As Word '--------------------------------
'применим в 93C46 8-битном режиме
Config Microwire = Pin , Cs = P1.0 , Din = P1.1 , Dout = P1.2 , _ Clock = P1.3 , Al = 7
Mw_cs Alias P1.0 : Mw_dout Alias P1.2 'определим имена портов линий Cs и Dout '--------------------------------
Mwinit |
|
'разрешим запись и стирание |
|
Mwwopcode &B1001100000 , 10 |
|||
Mwwopcode &B1001000000 , 10 |
'проведем общее стирание |
||
Gosub Mwrdyw |
'подождем стирания |
|
|
For Tmp1 = |
1 To 20 |
'заполним массив |
|
Tmpw = Tmp1 : Tmpw = Tmpw * Tmpw 'квадратом индекса |
|||
Tmp(tmp1) = Tmpw |
'следующее значение |
|
|
Next |
|
|
|
'-------------------------------- |
0 |
'установим начальный адрес EEPROM |
|
Tmp2 = |
|||
For Tmp1 = |
1 To 20 |
'запомним массив |
|
Tmpw = |
Tmp(tmp1) |
'в EEPROM |
|
Mwwrite Low(tmpw) , &B101 , |
Tmp2 , 1 : Incr Tmp2 : Gosub Mwrdyw |
||
Mwwrite High(tmpw) , &B101 , Tmp2 , 1 : Incr Tmp2 : Gosub Mwrdyw |
|||
Next |
|
'следующее значение |
|
'-------------------------------- |
1 To 20 |
'очистим массив |
|
For Tmp1 = |
|
||
Tmp(tmp1) = 0 |
|
|
|
Next |
|
|
|
'-------------------------------- |
0 |
'установим начальный адрес EEPROM |
|
Tmp2 = |
|||
For Tmp1 = |
1 To 20 |
'считаем массив по одному байту |
|
Mwread |
Tmp3 , &B110 , Tmp2 , 1 : Incr Tmp2 |
'считаем по одному байту |
|
Mwread |
Tmp4 , &B110 , Tmp2 , 1 : Incr Tmp2 |
'соберем из двух байт |
|
Tmpw = |
Makeint(tmp4 , Tmp3) : Tmp(tmp1) = Tmpw |
||
Next |
|
'следующее значение |
|
Mwwopcode &B1000000000 , 10 'запретим запись и стирание '--------------------------------
‘п/п ожидания готовности устройства на шине Microwire после операции записи
Mwrdyw: |
|
$asm |
;выберем микросхему |
Setb {mw_cs} |
|
Mov A , #100 |
;сможем ждать 10 мс |
Mwrw1: |
;подцикл 100 мкс |
Mov B , #50 |
|
Mwrw2: |
|
Djnz B , Mwrw2 |
;проверим наличие бита готовности |
Jb {mw_dout} , Mwrw3 |
|
Djnz A , Mwrw1 |
;если он не появился ждем готовности 10 мс |
Mwrw3: |
;снимем сигнал выборки |
Clr {mw_cs} |
|
$end Asm |
|
Return |
|
End |
|
============================================================================= 19-3
===================================== Справочник по программированию «Bascom-8051» ==
20. Программирование устройств I2C
Для программирования устройств, подключенных к шине I2C, Bascom предоставляет два варианта:
а) использовать готовые операторы записи и считывания данных, соответственно I2CSEND и I2CRECEIVE;
б) использовать операторы I2CSTART, I2CSTOP, I2CRBYTE и I2CWBYTE и самостоятельно создавать программы взаимодействия с устройствами на шине I2C.
Впервом случае программа получается намного проще, однако, ограничен круг устройств, с которыми можно работать, т.к. операторы I2CSEND и I2CRECEIVE позволяют использовать только однобайтный адрес. К устройствам, имеющим однобайтный адрес, относятся микросхемы переменных резисторов (регуляторов уровня), ЦАП, АЦП, порты ввода-вывода, драйверы светодиодных индикаторов и реле.
Когда подпрограммы записи и чтения данных написаны с использованием операторов I2CSTART, I2CSTOP, I2CRBYTE и I2CWBYTE, появляется возможность реализовать любой протокол обмена, содержащий сколько угодно байт адреса и данных. Это необходимо для работы с устройствами памяти, в которых адрес устройства является «ключом» доступа к массиву данных. Ниже приведена программа тестирования EEPROM, содержащая три набора подпрограмм чтения и записи:
а) однобайтных данных с двухбайтным адресом (устройстве I2C);
б) четырехбайтных данных и с двухбайтным адресом. В этих подпрограммах реализована страничная запись (четыре байта в странице при записи) и последовательное чтение (также четырех байт);
в) однобайтных данных с однобайтным адресом. Эти подпрограммы функционально близки операторам I2CSEND и I2CRECEIVE, но используют меньше ресурсов памяти.
Часть подпрограмм, представленных в примере, объявлено в виде функций для демонстрации возможностей Bascom. В составе программ использовано много ассемблерных вставок, что обусловлено необходимостью доступа к частям многобайтных переменных и для повышения эффективности кода. Операторы I2CRBYTE и I2CWBYTE, соответственно считываемые и записываемые данные, передают через аккумулятор. Это используется в представленных программах, где аккумулятор для экономии используется в качестве регистра временного хранения данных (источника и приемника). С помощью дисассемблирования было определено, что компилятор не использует регистр B в программах, работающих с интерфейсом I2С. Поэтому в представленных программах, и он такжже используется как временная переменная для хранения и обработки данных, позволяя уменьшить еще на одну, количество необходимых переменных.
Вдемонстрационной программе имеются операторы для обслуживания отладочного кристалла эмулятора типа 80C51GB. Они намеренно оставлены в тексте программы, чтобы показать, как оформляются подобные фрагменты.
'-------------------------------------------------------------
'Программа тестирование I2C EEPROM - 24c16 (2k*8)
'и утилиты работы с устройствами I2C
'строки, отмеченные знаками "!!!" относятся к местам программы,
'необходимых только для работы эмулятора на процессоре модели 80C51GB '-------------------------------------------------------------
'Определенные ниже п/п объявлены как функции переменных 'Таким способом можно определять п/п только с входящими целыми числами
'При использ. перем. с плав. точк. п/п приходится оформлять в обычном виде
Declare Sub Write_eeprom(wadr As Word , Value As Byte) Declare Sub Read_eeprom(wadr As Word , Value As Byte) Declare Sub Wrb_24c01(badr As Byte , Value As Byte)
Declare Sub Rdb_24c01(badr As Byte , Value As Byte) '---------------------
Dim Addressw As Const 160 , Addressr As Const 161 'объявление констант
Dim Badr As Byte |
'байтовый адрес |
|
Dim Temp As Byte |
'временные данные |
|
Dim Value As Byte |
'значение записываемых или считываемых данных |
|
Dim Wadr As Word |
'двухбайтный адрес |
|
Dim Fld As Single |
'число с плавающей точкой |
|
'--------------------- |
'P1.5 как линия SDA |
|
Config Sda = P1.5 |
||
Config Scl = P1.4 |
'P1.4 как линия SCL |
|
Config I2cdelay = 0 |
'ускорение линии I2C |
|
Config Timer0 = Timer , Gate = Internal , Mode = 1 : Start Timer0 |
||
'--------------------- |
|
|
'назначение режимов прерываний |
'вектор прерывания |
|
On Timer0 Timer_0_int Nosave |
||
============================================================================= 20-1
===================================== Справочник по программированию «Bascom-8051» ==
Enable Interrupts |
'вообще разрешить прерывания |
|
Enable Timer0 |
'разрешить прерывания таймера 0 |
|
'!!! -------------------------------------- |
|
|
'только в эмуляторе 80C51GB |
'чтобы прерывание наступило не позже 10 мс |
|
Th0 = &HD8 : Tl0 = &HFD |
|
|
Oscr = &HE1 : Oscr = &H1E |
'выключить контроль частоты генератора |
|
Wdtrst = &H1E : Wdtrst = &HE1'сбросить будильник |
||
P4 = &HFF : P5 = &HFF |
'установить эмулирующие порты как в эмулируемые |
|
'--------------------- |
|
|
'Цикл проверки программ записи чтения однобайтовых переменных 'Используем обе формы записи объявленных функций
Mc: |
0 To 1024 Step 3 |
'готовим адрес |
|
For Wadr = |
|||
Value = Low(wadr) |
'готовим данные |
||
Call Write_eeprom(wadr , Value) |
'записываем в EEPROM |
||
Next |
0 To 1024 Step 3 |
'готовим адрес |
|
For Wadr = |
|||
Read_eeprom Wadr , Value |
'считываем данные из EEPROM |
||
Print Value ; "=" ; Value 'печатаем данные |
|||
Waitms |
50 |
|
|
Next |
|
|
|
Goto Mc '---------------------
'Цикл проверки программ записи чтения четырехбайтовых переменных
'Mc: |
'готовим адрес |
|
For Wadr = 0 To 2044 Step 4 |
||
Fld = Wadr |
'готовим данные |
|
Fld = 3 * Fld |
||
Gosub Wrs_24c |
'записываем в EEPROM |
|
Next |
'готовим адрес |
|
For Wadr = 0 To 2044 Step 4 |
||
Gosub Rds_24c |
'считываем данные из EEPROM |
|
Print Wadr ; "=" ; Fld |
'печатаем данные |
|
Waitms 50 |
|
|
Next |
|
|
Goto Mc |
|
|
'------------------------------------------ |
|
|
'обработка прерывания таймера 0 |
|
|
Timer_0_int: |
'Ffffh-10000 = D8fdh - период прерыв. 10 мс |
|
Th0 = &HD8 : Tl0 = &HFD |
||
'!!! только в эмуляторе с 80C51GB |
'сбросить будильник |
|
Wdtrst = &H1E : Wdtrst = &HE1 |
||
Return |
|
|
'------------------------------------------ |
|
|
'п/п записи байта в EEPROM AT24с01A,AT24c02 - AT24c16 Sub Write_eeprom(wadr As Word , Value As Byte)
$asm |
;готовим старший байт, содержащий |
Mov A , {wadr + 1} |
|
Rl A |
;старшие разряды адреса |
Orl A , #{Addressw} |
;добавим код микросхемы |
Mov B , A |
|
$end Asm |
'старт |
I2cstart |
|
I2cwbyte B |
'первый байт режима |
$asm |
|
Mov A , {wadr + 0} |
|
$end Asm |
'младший адрес в EEPROM |
I2cwbyte Acc |
|
I2cwbyte Value |
'записывемое значение |
I2cstop |
'стоп |
Waitms 10 |
'подождем 10 мс |
End Sub |
|
'------------------------------------------ |
|
============================================================================= 20-2
===================================== Справочник по программированию «Bascom-8051» ==
'п/п считывания байта из EEPROM AT24с01A,AT24c02 - AT24c16 Sub Read_eeprom(wadr As Word , Value As Byte)
$asm |
;готовим старший байт, содержащий |
Mov A , {wadr + 1} |
|
Rl A |
;старшие разряды адреса |
Orl A , #{Addressw} |
;добавим код микросхемы |
Mov B , A |
|
$end Asm |
'старт |
I2cstart |
|
I2cwbyte B |
'первый байт режима |
$asm |
|
Mov A , {wadr + 0} |
|
$end Asm |
'младший адрес в EEPROM |
I2cwbyte Acc |
|
Set B.0 |
'переключим в режим считывания |
I2cstart |
'повторим старт |
I2cwbyte B |
'байт режима |
I2crbyte Value , 9 |
'считали байт |
I2cstop |
'стоп |
End Sub |
|
'------------------------------------------ |
|
'п/п записи четырехбайтного числа в EEPROM AT24с01A,AT24c02 - AT24c16 |
|
Wrs_24c: |
|
$asm |
;готовим старший байт, содержащий |
Mov A , {wadr + 1} |
|
Rl A |
;старшие разряды адреса |
Orl A , #{Addressw} |
;добавим код микросхемы |
Mov B , A |
|
$end Asm |
'старт |
I2cstart |
|
I2cwbyte B |
'первый байт режима |
$asm |
|
Mov A , {wadr + 0} |
|
$end Asm |
'младший адрес в EEPROM |
I2cwbyte Acc |
|
$asm |
|
Mov A, {Fld + 0} |
|
$end Asm |
'первый записываемый байт |
I2cwbyte Acc |
|
$asm |
|
Mov A , {Fld + 1} |
|
$end Asm |
'2-й |
I2cwbyte Acc |
|
$asm |
|
Mov A , {Fld + 2} |
|
$end Asm |
'3-й |
I2cwbyte Acc |
|
$asm |
|
Mov A , {Fld + 3} |
|
$end Asm |
'4-й |
I2cwbyte Acc |
|
I2cstop |
'стоп |
Waitms 10 |
'подождем 10 мс |
Return '------------------------------------------
'п/п считывания четырехбайтного числа из EEPROM AT24с01A,AT24c02 - AT24c16
Rds_24c: |
|
$asm |
;готовим старший байт, содержащий |
Mov A , {wadr + 1} |
|
Rl A |
;старшие разряды адреса |
Orl A , #{Addressw} |
;добавим код микросхемы |
Mov B , A |
|
$end Asm |
|
============================================================================= 20-3
===================================== Справочник по программированию «Bascom-8051» ==
I2cstart |
'старт |
I2cwbyte B |
'первый байт режима |
$asm |
|
Mov A , {wadr + 0} |
|
$end Asm |
'младший адрес в EEPROM |
I2cwbyte Acc |
|
Set B.0 |
'переключим в режим считывания |
I2cstart |
'повторим старт |
I2cwbyte B |
'байт режима |
I2crbyte Acc , 8 |
'считали 1-й байт с опцией ASC (8) |
$asm |
|
Mov {Fld + 0} , A |
|
$end Asm |
'2-й байт |
I2crbyte Acc , 8 |
|
$asm |
|
Mov {Fld + 1} , A |
|
$end Asm |
'3-й байт |
I2crbyte Acc , 8 |
|
$asm |
|
Mov {Fld + 2} , A |
|
$end Asm |
'последний байт с опцией NOASC (9) |
I2crbyte Acc , 9 |
|
$asm |
|
Mov {Fld + 3} , A |
|
$end Asm |
'стоп |
I2cstop |
|
Return |
|
'------------------------------------------
'п/п записи байта в EEPROM AT24с01
Sub Wrb_24c01(badr As Byte , Value As Byte)
Acc = Badr |
'готовим адрес |
$asm |
; ------------------------------- |
Rl A |
|
Mov B , A |
; | a6| a5| a4| a3| a2| a1| a0| 0 | |
$end Asm |
'старт |
I2cstart |
|
I2cwbyte B |
'адрес и бит режима |
I2cwbyte Value |
'записывемое значение |
I2cstop |
'стоп |
Waitms 10 |
'подождем 10 мс |
End Sub |
|
'------------------------------------------
'п/п считывания байта из EEPROM AT24с01 Sub Rdb_24c01(badr As Byte , Value As Byte)
Acc = Badr |
'готовим адрес |
$asm |
; ------------------------------- |
Setb c |
|
Rlc A |
; | a6| a5| a4| a3| a2| a1| a0| 1 | |
Mov {Value} , A |
|
$end Asm |
'старт |
I2cstart |
|
I2cwbyte Value |
'первый байт режима |
I2crbyte Value , 9 |
'считали байт |
I2cstop |
'стоп |
End Sub |
|
============================================================================= 20-4
===================================== Справочник по программированию «Bascom-8051» ==
21. Программирование таймеров
Наличие в составе Bascom операторов программирования таймеров позволяет во многих случаях обходится только этими средствами. При этом даже не нужны знания соответствующих управляющих регистров. Более глубокое программирование с использованием нестандартных приемов, без сомнения, потребует подробного изучения регистров управления таймерами и их взаимодействия с системой прерывания. Применения ассемблерных вставок для более эффективного управления таймерами не требуется, т.к. специальные команды Bascom достаточно оперативны, и имеется возможность непосредственного управления всеми регистрами и битами. В таблице, приведенной ниже, показано как компилируются операторы Bascom, работающие с таймерами. Мы видим, что компилятор не работает со всем таймерами по одинаковой схеме., поэтому всегда нужно быть готовым к тому, что в одних случаях компилятор поставит код команды остановки и запуска счетчика, а в других их нужно будет добавить.
Оператор Bascom-8051 |
Результат компиляции |
Комментарий |
|
Load Timer0 , 100 |
MOV |
TL0,#9CH |
Загрузка в режиме таймеров 0 или 1 в режиме 2. |
|
MOV |
TH0,#9CH |
Коэффициент деления определяется только старшим |
|
|
|
байтом счетчика, но задаваться должен однобайтным |
|
|
|
числом! |
Load Timer1 , 1000 |
MOV |
TL1,#18H |
Здесь неправильно задан коэффициент деления – |
|
MOV |
TH1,#18H |
константа должна быть однобайтовой. |
Load Timer2 , 1024 |
MOV |
RCAP2L,#00H |
Для таймера 2 загрузочный коэффициент может быть |
|
MOV |
CAP2H,#0FCH |
двухбайтным |
Counter0 = 8192 |
MOV |
TL0,#00H |
Загрузка счетчика производится двухбайтовым числом. |
|
MOV |
TH0,#20H |
Применяется в режиме 1. |
Counter2 = 10 |
CLR |
TR2 |
Аналогичная операция для таймера 2 начинается с |
|
MOV |
TL2,#0AH |
остановки счетчика. |
|
MOV |
TH2,#00H |
|
Buf_ = Counter1 |
CLR |
TR1 |
Считывание в переменную Buf_ содержимого счетчика |
|
MOV |
R0,#23H |
таймера 0, 1 и 2 производится после его остановки. Если |
|
MOV |
@R0,TL1 |
счет необходимо продолжить, таймер нужно снова |
|
INC |
R0 |
запустить. |
|
MOV |
@R0,TH1 |
|
Buf_ = Capture |
CLR |
TR2 |
Считывание в переменную Buf_ содержимого регистра |
|
MOV |
R0,#23H |
захвата таймера 2 производится после его остановки. |
|
MOV |
@R0,RCAP2L |
Далее счет снова запускается. |
|
INC |
R0 |
|
|
MOV |
@R0,RCAP2H |
|
|
SETB |
TR2 |
|
Var1 = Tl0 |
MOV |
Var1,TL0 |
Непосредственное считывание содержимого счетчика в |
Var2 = Th0 |
MOV |
Var2,TH0 |
байтовые переменные. Это аналог ассемблера. |
Tl0 = &h00 |
MOV |
TL0,#00H |
Непосредственная запись данных в счетчик |
Th0 = &hF0 |
MOV |
TH0,#F0H |
|
============================================================================= 21-1
===================================== Справочник по программированию «Bascom-8051» ==
22.Программирование устройств с последовательным вводом-выводом
Вдругих разделах много уделялось внимания проблемам ввода-вывода через последовательные интерфейсы. Было приведено много примеров программ, использующих операторы последовательного вводавывода. Поэтому в настоящем разделе рассмотрим только, оставшиеся неразрешенными, вопросы. Главный, из которых - как работают операторы Shiftin и Shiftout Bascom, и чем они отличаются друг от друга?
Самыми удобными и универсальными представляются операторы записи и считывания данных (Shiftin и Shiftout). Эти операторы работают с однобайтными и многобайтными переменными, и могут быть применены при любом расположении данных и соотношении фазы синхронизирующего сигнала. В следующей ниже таблице, представлены упрощенные (и укороченные) временные диаграммы на линиях интерфейса во всех возможные режимах. Там же указана скорость передачи данных для однобайтовых (это максимальная или пиковая скорость)
имногобайтовых данных (как средняя скорость) при частоте кварца 12 МГц. На основе операторов Shiftin и Shiftout могут быть построены практически все виды применяемых синхронных интерфейсов.
Временные диаграммы, формируемые операторами последовательного вывода
Оператор |
|
|
|
Временная диаграмма |
Скорость |
|||
|
___ _____ _____ _____ _____ __ |
Пиковая |
||||||
Shiftout, mode 0 |
___X_____X_____X_____X_____X__ Out_Data |
100 Кбод, |
||||||
|
msb _____ |
|
___ |
___ |
___ |
|_| |
____ lsb |
средняя |
|
|
|_| |_| |
|
|_| |
Clk |
80 Кбод |
||
|
|
|
||||||
|
___ _____ _____ _____ _____ __ |
Пиковая |
||||||
Shiftout, mode 1 |
___X_____X_____X_____X_____X__ Out_Data |
100 Кбод, |
||||||
|
msb |
_ |
_ |
|
_ |
_ |
lsb |
средняя |
|
_____| |___| |___| |___| |____ Clk |
80 Кбод |
||||||
|
|
|
||||||
|
___ _____ _____ _____ _____ __ |
Пиковая |
||||||
Shiftout, mode 2 |
___X_____X_____X_____X_____X__ Out_Data |
100 Кбод, |
||||||
|
lsb _____ |
|
___ |
___ |
___ |
|_| |
____ msb |
средняя |
|
|
|_| |_| |
|
|_| |
Clk |
80 Кбод |
||
|
|
|
||||||
|
___ _____ _____ _____ _____ __ |
Пиковая |
||||||
Shiftout, mode 3 |
___X_____X_____X_____X_____X__ Out_Data |
100 Кбод, |
||||||
|
lsb |
_ |
_ |
|
_ |
_ |
msb |
средняя |
|
_____| |___| |___| |___| |____ Clk |
80 Кбод |
||||||
|
|
|
|
|
|
|
|
|
В последних версиях компилятора расширены возможности оператора Shifin. Теперь он работает во всех четырех режимах при внешней синхронизации и позволяет считывать данные до начала синхроимпульса (в обычном режиме считывание дланных производится после начала синхроимпульса). Режим предварительного считывания включается применением дополнительной опции PRE. В режимах внешней синхронизации применение предварительного считывания не предусмотрено. ВНИМАНИЕ! При использовании внешней синхронизации нужно представлять себе, что операторы считывания данных Shiftin в этом случае являются скрытыми циклами ожидания (необходимого количества импульсов синхронизации). Если происходит сбой на передающей стороне и, хотя бы один импульс пропадает, то происходит «зависание» программы на операторе Shiftin. Это же может происходить, если случается прерывание во время приема данных и один или несколько импульсов будет пропущено.
============================================================================= 22-1
===================================== Справочник по программированию «Bascom-8051» ==
Временные диаграммы, формируемые операторами последовательного ввода
Оператор |
|
|
|
|
Временная диаграмма |
|
Скорость |
||||
|
|
___ ____ ____ ____ ____ ____ |
|
Пиковая |
|||||||
Shiftin, mode 0 |
|
___X____X____X____X____X____ In_Data |
100 Кбод, |
||||||||
|
msb __ |
_ |
_ |
_ |
|
_ |
|
__ |
lsb |
средняя |
|
|
|
|
|__| |__| |__| |__| |__| |
Clk |
80 Кбод |
||||||
|
|
|
|
|
|||||||
|
|
___ ____ ____ ____ ____ ____ |
|
Пиковая |
|||||||
Shiftin, mode 1 |
msb |
___X____X____X____X____X____ In_Data |
100 Кбод, |
||||||||
|
|
__ |
__ |
__ |
__ |
__ |
|
lsb |
|
средняя |
|
|
|
__| |_| |_| |_| |
|_| |
|__ Clk |
80 Кбод |
||||||
|
|
|
|
|
|||||||
Shiftin, mode 2 |
|
___ ____ ____ ____ ____ ____ |
|
Пиковая |
|||||||
|
|
___X____X____X____X____X____ In_Data |
100 Кбод, |
||||||||
|
lsb __ |
_ |
_ |
_ |
|
_ |
|
__ |
msb |
средняя |
|
|
|
|
|__| |__| |__| |__| |__| |
Clk |
80 Кбод |
||||||
|
|
|
|
|
|||||||
Shiftin, mode 3 |
|
___ ____ ____ ____ ____ ____ |
|
Пиковая |
|||||||
|
lsb |
___X____X____X____X____X____ In_Data |
100 Кбод, |
||||||||
|
|
__ |
__ |
__ |
__ |
__ |
|
msb |
средняя |
||
|
|
__| |_| |_| |_| |
|_| |
|__ Clk |
80 Кбод |
||||||
|
|
|
|
|
|||||||
|
|
___ _____ _____ _____ _____ __ |
|
|
|||||||
Shiftin, mode 4 |
msb |
___X_____X_____X_____X_____X__ Out_Data |
До 100 Кбод, |
||||||||
|
|
_ |
_ |
|
_ |
_ |
|
lsb |
средняя |
||
|
|
_____| |___| |___| |___| |____ Clk External |
80 Кбод |
||||||||
|
|
|
|
|
|||||||
|
|
___ _____ _____ _____ _____ __ |
|
|
|||||||
Shiftin, mode 5 |
|
___X_____X_____X_____X_____X__ Out_Data |
До 100 Кбод, |
||||||||
|
msb_____ |
___ |
___ |
|
___ |
|
____ lsb |
средняя |
|||
|
|
|
|_| |
|_| |_| |
|_| |
|
Clk External |
80 Кбод |
|||
|
|
|
|
|
|||||||
|
|
___ _____ _____ _____ _____ __ |
|
|
|||||||
Shiftin, mode 6 |
lsb |
___X_____X_____X_____X_____X__ Out_Data |
До 100 Кбод, |
||||||||
|
|
_ |
_ |
|
_ |
_ |
|
msb |
средняя |
||
|
|
_____| |___| |___| |___| |____ Clk External |
80 Кбод |
||||||||
|
|
|
|
|
|||||||
|
|
___ _____ _____ _____ _____ __ |
|
|
|||||||
Shiftin, mode 7 |
|
___X_____X_____X_____X_____X__ Out_Data |
До 100 Кбод, |
||||||||
|
lsb_____ |
___ |
___ |
|_| |
___ |
|
____ msb |
средняя |
|||
|
|
|
|_| |
|_| |
|_| |
|
Clk External |
80 Кбод |
|||
|
|
|
|
|
|
|
|
|
|
|
|
Bascom предлагает также операторы Spiin и Spiout, обеспечивающих подключение устройств с последовательным SPI-интерфейсом. Эти операторы реализуют интерфейс программно и в несколько упрощенном виде (не обеспечивается одновременный вывод и ввод соттветственно). По сути, это те же операторы Shiftin и Shiftout, работающие в режиме 1, но только с добавлением сигнала “выбора кристалла”. Тем не менее, операторами Spiin и Spiout можно успешно пользоваться во многих случаях, т.к. большинство периферийных микросхем используют именно такой режим. Ниже в таблице представлены упрощенные (и укороченные) временные диаграммы на линиях интерфейса при работе операторов Spiin и Spiout.
============================================================================= 22-2
===================================== Справочник по программированию «Bascom-8051» ==
Оператор |
|
|
|
Временная диаграмма |
|
Скорость при |
||
|
|
|
|
|
|
|
|
12 МГц |
|
_ |
|
|
|
|
_ |
CS |
|
Spiout |
|__________________________| |
Пиковая |
||||||
|
___ _____ _____ _____ _____ __ |
|
100 Кбод, |
|||||
|
___X_____X_____X_____X_____X__ Out_Data |
средняя |
||||||
|
msb |
_ |
_ |
_ |
|
_ lsb |
|
80 Кбод |
|
_____| |___| |___| |___| |____ Clk |
|
||||||
|
|
|
|
|
|
|
|
|
|
_ |
|
|
|
|
__ |
CS |
|
Spiin |
|_________________________| |
Пиковая |
||||||
|
___ ____ ____ ____ ____ ____ |
|
100 Кбод, |
|||||
|
___X____X____X____X____X____ In_Data |
средняя |
||||||
|
msb __ |
__ |
__ |
__ |
__ |
lsb |
|
80 Кбод |
|
__| |
|_| |
|_| |
|_| |
|_| |
|__ Clk |
|
|
|
|
|
|
|
|
|
|
|
Далее приведены примеры ассемблерного кода, выдаваемого компилятором, при использовании операторов последовательного ввода вывода. Обращаем внимание, что операторы Spiin и Spiout оформлены в виде подпрограмм. Операторы Shftin и Shiftout просто вставляются в исполняемый код, что при многократном их использовании дает соответствующее увеличениие размера получаемого кода. Если размер кода является критичным, а операторы Shftin и Shiftout применяются неоднократно, то рекомендуется встраивать их в специально созданные подпрограммы.
;оператор SPIOUT
;при входе указывают: R0-данные, R7-число байт ;F0=0 - из внутренней памяти, F0=1 - из внешней
Spiout: |
P1.2 |
;сигнал выбор кристалла |
CLR |
||
Spio1: |
Rdda |
;считать байт |
ACALL |
||
MOV |
R2,#08H |
;число бит |
Spio2: |
A |
;выдвинуть старший бит |
RLC |
||
MOV |
P1.1,C |
;в порт вывода |
SETB |
P1.3 |
;сформировать импульс |
NOP |
P1.3 |
|
CLR |
;повторить восемь раз |
|
DJNZ |
R2,Spio2 |
|
ACALL |
Incinx |
;инкремент указателя |
DJNZ |
R7,Spio1;повторять, пока все байты не выдвинуты |
|
SETB |
P1.2 |
;снять сигнал выбора кристалла |
RET |
|
|
;считывание данных |
|
|
Rdda: |
F0,Rda1 |
|
JB |
|
|
MOV |
A,@R0 |
|
RET |
|
|
Rda1: |
A,@DPTR |
|
MOVX |
|
|
RET |
|
|
;запись данных |
|
|
Wrda: |
F0,Wrd1 |
|
JB |
|
|
MOV |
@R0,A |
|
RET |
|
|
Wrd1: |
@DPTR,A |
|
MOVX |
|
|
RET |
|
|
;инкремент указателя |
|
|
Incinx: |
F0,Iinx1 |
|
JB |
|
|
============================================================================= 22-3
===================================== Справочник по программированию «Bascom-8051» ==
INC |
R0 |
RET |
|
Iinx1: |
DPTR |
INC |
|
RET |
|
;-----------------------------
;оператор SHIFTOUT в режиме 1
Shiftout1: |
P1.3 |
;исходное состояние линии Clk |
CLR |
||
MOV |
R0,#Dat |
;адрес данных |
MOV |
R2,#01H |
;число байт |
Sho1_1: |
A,@R0 |
|
MOV |
|
|
MOV |
R3,#08H |
|
Sho1_2: |
A |
|
RLC |
|
|
MOV |
P1.0,C |
|
SETB |
P1.3 |
|
NOP |
|
|
NOP |
P1.3 |
|
CLR |
|
|
NOP |
|
|
NOP |
R3,Sho1_1 |
|
DJNZ |
|
|
DEC |
R0 |
|
DJNZ |
R2,Sho1_2 |
|
;----------------------------- |
||
;оператор SHIFTIN в режиме 0 |
||
Shiftin0: |
P1.1 |
|
SETB |
|
|
MOV |
R0,#34H |
|
MOV |
R2,#02H |
|
INC |
R0 |
|
Shi0_1: |
R3,#08H |
|
MOV |
|
|
Shi0_2: |
P1.1 |
|
CLR |
|
|
NOP |
|
|
NOP |
C,P1.2 |
|
MOV |
|
|
RLC |
A |
|
SETB |
P1.1 |
|
NOP |
|
|
NOP |
R3,Shi0_2 |
|
DJNZ |
|
|
MOV |
@R0,A |
|
DEC |
R0 |
|
DJNZ |
R2,Shi0_1: |
|
;----------------------------- |
||
;оператор SHIFTIN в режиме 1 |
||
Shiftin1: |
P1.1 |
|
CLR |
|
|
MOV |
R0,#34H |
|
MOV |
R2,#02H |
|
INC |
R0 |
|
Shi1_1: |
R3,#08H |
|
MOV |
|
|
Shi1_2: |
P1.1 |
|
SETB |
|
|
NOP |
|
|
NOP |
C,P1.2 |
|
MOV |
|
|
RLC |
A |
|
============================================================================= 22-4
===================================== Справочник по программированию «Bascom-8051» ==
CLR |
P1.1 |
NOP |
|
NOP |
R3,Shi1_2 |
DJNZ |
|
MOV |
@R0,A |
DEC |
R0 |
DJNZ |
R2,Shi1_1 |
;----------------------------- |
|
Мы видим, что описанные операторы Bascom не обеспечивают предельно возможную (для процессора 8051) скорость приема и передачи, хотя сегодня, уже трудно представить реальные периферийные устройства, все еще требующие такую низкую скорость. Это сделано намеренно и, как увидим дальше, совершенно правильно, чтобы иметь возможность программно принимать выдвигаемые данные (смотри режим 4 оператора Shiftin), а также передавать данные через оптроны, которые обычно дают задержку от 1 до 10 мкс. Таким образом, принятая скорость передачи является компромиссной и удовлетворяющей наибольшее число пользователей. Для ускорения процесса приема или передачи можно использовать собственные программы, например, такие как, примененные для записи и считывания данных, в программе тестирования АЦП типа AD7711A (смотри выше). Еще быстрее работает программа считывания или записи данных, использующая в качестве линии передачи (и приема) данных порт P0.0, а порты P3.6 и P3.7 соответственно как линии синхронизации при записи и считывании данных. Ниже представлен пример программы записи данных, в которой импульс синхронизации формируется аппаратно применением команды MOVX.
Mov A |
, Data |
;загрузим аккумулятор |
Rl |
A |
;начиная со старшего разряда |
Movx |
@DPTR , A |
;передадим один бит из аккумулятора |
Rl |
A |
;повторим восемь раз |
Movx |
@DPTR , A |
|
Rl |
A |
|
Movx |
@DPTR , A |
|
Rl |
A |
|
Movx |
@DPTR , A |
|
Rl |
A |
|
Movx |
@DPTR , A |
|
Rl |
A |
|
Movx |
@DPTR , A |
|
Rl |
A |
|
Movx |
@DPTR , A |
|
Rl |
A |
|
Movx |
@DPTR , A |
|
Еще быстрее (Fкв / 12) можно записывать или считывать данные, используя аппаратный универсальный последовательный интерфейс микроконтроллера, включенный в режиме 3, но при этом занимаются линии P3.0 и P3.1, что часто недопустимо. Если применять модель процессора AT89S8252, имеющего полноценный многорежимный аппаратный SPI-интерфейс, то можно будет работать с максимальной скоростью Fкв / 4. Bascom не поддерживает своими специальными операторами все эти режимы, поэтому чтобы воспользоваться ими, нужно применять обычные операторы загрузки и чтения регистров процессора.
============================================================================= 22-5
===================================== Справочник по программированию «Bascom-8051» ==
23. Вычисления и преобразования чисел
Bascom позволяет производить арифметические вычисления (сложение, вычитание, умножение и деление) над целыми числами (одно-, двух и четырехбайтными) и числами в формате с плавающей точкой. В следующей таблице показано расположение байтов переменных в памяти.
Тип |
|
|
Расположение в памяти |
|
|
|
|
|||
переменной |
Первый байт |
|
Второй байт |
|
Третий байт |
|
Четвертый байт |
|||
|
(Adr*) |
|
(Adr+1) |
|
|
(Adr+2) |
|
(Adr+3) |
|
|
Однобайтовая пе- |
Байт |
|
- |
|
|
- |
|
- |
|
|
ременная (Byte) |
|
|
|
|
|
|
|
|
|
|
Двухбайтовая |
Младший байт |
|
Старший байт |
|
- |
|
- |
|
|
|
(Word, Integer) |
|
|
|
|
|
|
|
|
|
|
Четырехбайтовая |
1-й байт |
|
2-й байт |
|
|
3-байт |
|
4-й байт |
|
|
(Long) |
(младший) |
|
|
|
|
|
|
(старший) |
||
В формате с пла- |
1-й байт |
|
2-й байт |
|
|
3-байт |
|
Байт |
|
Знак |
вающей точкой |
(младший) |
|
|
|
|
(старший) |
|
порядка. |
|
|
(Single)_ |
Нормализованная мантасса, принимающая значения от 1 до 2, |
Вычисляется как |
|
0 - |
||||||
|
старший бит мантиссы отбрасывается (только подразумевает- |
127+p, где p- |
|
плюс |
||||||
|
ся наличие всегда равного 1). |
порядок из вы- |
|
1 - |
||||||
|
Примеры кода |
00 00 80 |
3F |
= 1 |
|
ражния: |
|
ми- |
||
|
2^p + Мантисса |
|
нус |
|||||||
|
чисел в порядке, |
00 00 00 |
40 |
= 2 |
|
|
|
|
||
|
расположения в |
00 00 80 |
40 |
= 4 |
|
|
|
|
||
|
памяти |
00 00 00 |
41 |
= 8 |
|
|
|
|
||
|
|
|
00 00 80 |
41 |
= 16 |
|
|
|
|
|
|
|
|
00 00 00 |
42 |
= 32 |
|
|
|
|
|
|
|
|
00 00 80 |
42 |
= 64 |
|
|
|
|
|
|
|
|
00 00 00 |
43 |
= 128 |
|
|
|
|
|
|
|
|
00 00 20 |
C1 |
= -10 |
|
|
|
|
|
|
|
|
00 00 20 |
C0 |
= -2.5 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
* Adr – адрес переменной определенный при компиляции
Bascom с целыми двухбайтными числами может выполнять знаковые (с переменными типа Integer и Long) и беззнаковые (с переменными типа Byte и Word) арифметические операции. Арифметические операции с четырехбайтными числами всегда производятся с учетом знака. При целочисленных вычислениях длина результата, не может превышать длины исходных числовых переменных. Даже при умножении 8-разрядных чисел результат будет тоже 8-разрядным. Аналогично для 16-разрядных и 32-разрядных чисел. Смешанные произведения дают результат, равный по длине, более длинной переменной. Главная неприятность этой особенности компилятора заключается в том, что при переполнении (возможном при сложении и особенно умножении) теряются старшие разряды. Эта ошибка не показывается компилятором потому, что считается ошибкой программиста. Чтобы этого не происходило, сложение и умножение при наличии вероятности переполнения нужно выполнять с использованием переменных двойной длины, а это значительно увеличивает время операции, или применять быструю ассемблерную программу с собственной программой сложения и умножения. Ниже приведена одна такая программа.
Компилятор может производить много вариантов преобразования чисел в явном (с помощью специальных операторов) или в неявном виде (при выполнении других операции). Преобразование в неявном виде происходит при перезаписи данных или при вычислении значения операндов разного типа. Причем, если преобразование производится при считывании исходных значений (до выполнения назначенной операции), вероятность ошибки невелика, т.к. компилятор выбирает подпрограмму обработки, в зависимости от типа исходных переменных. Если запись результата назначена в регистр, отличающийся по типу, от переменных, применяемых при вычислениях, то ошибка неизбежна, т.к. компилятор использует имя переменной-приемника уже механически. При наличии ошибки несоответствия длины результата и размера переменной, куда помещается результат, последняя может быть заполнена не полностью или, еще хуже, перекрыта по длине. Эти ошибки также не выявляются и не показываются компилятором. Ошибки подобного рода исключены, при выполнении специальных операций преобразования, когда компилятор проверяет размерность переменной - приемника данных;
Ниже приведена программа, в которой имеются примеры вычисления и преобразования чисел, выполняемые компилятором Bascom.
'----------------------------------------------------
============================================================================= 23-1
===================================== Справочник по программированию «Bascom-8051» ==
' |
Преобразование числовых переменных и вычисления |
'---------------------------------------------------- |
|
$large |
|
|
'число 5 |
|
Dim Cnt As Const 5 |
|
|
|
|
Dim Bt As Bit |
|
|
'определим по паре переменных всех типов |
|
Dim Byt As Byte , Byt1 As Byte |
|
|
||
Dim Wrd As Word , Wrd1 As Word |
|
|
||
Dim Intg As Integer , Intg1 As Integer |
|
|||
Dim Lng As Long , Lng1 As Long |
|
|
||
Dim Sng As Single , Sng1 As Single |
|
|||
'---------------------------------------------------- |
|
|
'установить бит |
|
Set Bt |
|
|
||
'---------------------------------------------------- |
|
|
|
|
'преобразование переменных |
'значение бита помещается в младший разряд |
|||
Byt = Bt |
|
|
||
Byt = Cnt |
|
|
'записать число (константу) |
|
Wrd = Byt |
|
|
'в младший байт данные, в старший байт 00 |
|
Intg = Wrd |
|
|
'переместить данные без изменений |
|
Intg = Not Intg |
|
|
'инвертировать |
|
Wrd = Intg |
|
|
'обратно без изменений |
|
Sng = Wrd : Print Sng |
|
'преобразовать в плавающую Sng = 65550.0 |
||
Sng = Intg : Print Sng |
|
'преобразовать в плавающую Sng = -6.0 |
||
'---------------------------------------------------- |
|
|
|
|
'целочисленные вычисления |
'результат Intg = -30 |
|||
Intg = Intg * 5 : Print Intg |
||||
Intg = Intg / 5 : Print Intg |
'рузультат Intg = -6 |
|||
Intg1 = 1000 |
|
|
|
|
Intg = Intg1 / Intg : Print Intg'результат Intg = -166 |
||||
Intg = Intg * -1 |
|
|
'смена знака. Это медленный вариант |
|
Intg = Not Intg : Intg = Intg + |
1 'аналогично, но работает намного быстрее |
|||
Intg = Not Intg : Incr Intg |
'тоже работает быстро |
|||
Lng = Intg |
|
'чтобы получить 32-разр. результат умножения, |
||
Lng = Lng * Intg |
|
'хотя бы один из операндов должен быть 32-разр. |
||
Lng = 1000000 : Lng = Not Lng |
'инверсия для смены знака |
|||
Incr Lng |
|
|
'-1000001 + 1 = -1000000 |
|
Print Lng |
|
|
'результат Lng = -1000000 |
|
Lng = Lng / 1000 |
|
|
'деление на константу |
|
Print Lng |
|
|
'результат Lng = -1000 |
|
Wrd = Lng / 1000 |
'так неправильно, хотя компилятор ошибку не покажет |
|||
Lng = Lng / 1000 : Wrd = Lng |
'а это правильно |
|||
Intg1 = -10 |
|
|
'деление на переменную другой размерности |
|
Lng = Lng / Intg1 |
|
|
||
Print Lng |
|
|
'результат Lng = 100 |
|
'---------------------------------------------------- |
|
|
|
|
'вычисление с плав. точкой |
'умножение целого числа на плавающее |
|||
Sng = Sng * Lng |
|
|
||
Print Sng |
|
|
'результата Sng = -600.0 |
|
Sng1 = 0.15 : Sng = Sng * Sng1 |
'умножение плавающих чисел |
|||
Print Sng |
|
|
'результата Sng = -90.0 |
|
Sng = Sng / -10 |
|
|
'деление на константу |
|
Print Sng |
|
|
'результата Sng = 9.0 |
|
$asm |
|
'мгновенная смена знака числа с плав. точкой |
||
Xrl {Sng+3} , #&h80 |
||||
Anl {Sng+3} , #&h7f |
'или установка положительной полярности |
|||
Prl {Sng+3} , #&h80 |
'или установка отрицательной полярности |
|||
$end asm |
|
|
|
|
'---------------------------------------------------- |
|
|
|
|
'демонстрация неправильного умножения |
'назначим числа |
|||
Byt = 10 : Byt1 = 15 : Wrd = &H1234 |
||||
Wrd = Byt * Byt1 |
'произведение сразу запишем в двухбайтное число |
|||
Printhex Wrd |
'результат Wrd = 1296h - только в младших разрядах |
|||
Byt = Byt * Byt1 |
|
|
'проделываем это же иначе, а затем |
|
Wrd = Byt |
|
|
'переписываем с очисткой старших разрядов |
|
============================================================================= 23-2
===================================== Справочник по программированию «Bascom-8051» ==
Byt1 = Wrd 'обратно берутся только младшие разряды и
Print Byt ; " " ; Byt1 ; " " ; Wrd 'теперь результат везде одинаков = 150 '----------------------------------------------------
' определим время исполнения арифметических операций
Byt = 50 : Byt1 = 10 |
'назначим числа |
Byt = Byt + Byt1 |
'сложение - 11 тактов |
Byt = 50 : Byt1 = 10 |
' числа |
Byt = Byt - Byt1 |
'вычитание - 12 тактов |
Byt = 20 : Byt1 = 10 |
' числа |
Byt = Byt * Byt1 |
'умножние - 10 тактов |
Byt = 50 : Byt1 = 10 |
' числа |
Byt = Byt / Byt1 |
'деление - 10 тактов |
'---------------------------------------------------- |
' числа |
Wrd = 500 : Wrd1 = 100 |
|
Wrd = Wrd + Wrd1 |
'сложение - 20 тактов |
Wrd = 500 : Wrd1 = 100 |
' числа |
Wrd = Wrd - Wrd1 |
'вычитание - 21 такт |
Wrd = 200 : Wrd1 = 100 |
' числа |
Wrd = Wrd * Wrd1 |
'умножние - 63 такта |
Wrd = 500 : Wrd1 = 100 |
' числа |
Wrd = Wrd / Wrd1 |
'деление - 560 тактов |
'---------------------------------------------------- |
' числа |
Intg = 500 : Intg1 = -100 |
|
Intg = Intg + Intg1 |
'сложение - 20 тактов |
Intg = 500 : Intg1 = -100 |
' числа |
Intg = Intg - Intg1 |
'вычитание - 21 тактов |
Intg = 200 : Intg1 = -100 |
' числа |
Intg = Intg * Intg1 |
'умножние - 95 тактов |
Intg = 500 : Intg1 = -100 |
' числа |
Intg = Intg / Intg1 |
'деление - 620 тактов |
'---------------------------------------------------- |
' числа |
Lng = -5000 : Lng1 = 1000 |
|
Lng = Lng + Lng1 |
'сложение - 74 такта |
Lng = -5000 : Lng1 = 1000 |
' числа |
Lng = Lng - Lng1 |
'вычитание - 74 такта |
Lng = -2000 : Lng1 = 1000 |
' числа |
Lng = Lng * Lng1 |
'умножние - 2500 тактов |
Lng = -5000 : Lng1 = 1000 |
' числа |
Lng = Lng / Lng1 |
'деление - 3200 тактов |
'---------------------------------------------------- |
' числа |
Sng = 50 : Sng1 = 10 |
|
Sng = Sng + Sng1 |
'сложение - 240 тактов |
Sng = 50 : Sng1 = 10 |
' числа |
Sng = Sng - Sng1 |
'вычитание - 290 тактов |
Sng = 20 : Sng1 = 10 |
' числа |
Sng = Sng * Sng1 |
'умножение - 2000 тактов |
Sng = 50 : Sng1 = 10 |
' числа |
Sng = Sng / Sng1 |
'деление - 2300 тактов |
'---------------------------------------------------- |
|
'определим время исполнения преобразований |
|
Byt = 100 : Sng = Byt |
'байт в плавающую - 490 тактов |
Wrd = 10000 : Sng = Wrd |
'обратно преобразования не производится |
'слово в плавающую -370 тактов |
|
Wrd = Sng |
'обратно -340 тактов |
Intg = -1000 : Sng = Intg |
'знаковое слово в плав. - 470 тактов |
Intg = Sng |
'обратно - 420 тактов |
Lng = 1000000 : Sng = Lng |
'двойное слово в плав. - 280 тактов |
Lng = Sng |
'обратно - 250 тактов |
'---------------------------------------------------- |
|
End |
|
В демонстрационной программе имеются ответы почти на все вопросы применения операций преобразования и использования в вычислениях числовых переменных. Нужно заметить, что измеренные значения производительности операций вычисления и преобразования приблизительны, т.к. зависят от значения преобразуемого
============================================================================= 23-3
===================================== Справочник по программированию «Bascom-8051» ==
числа, особенно в операциях с плавающей точкой, и еще в большей степени при операции деления. Разница в результате при использовании знаков деления “/” и “\” не замечена.
Ниже дается пример быстрой ассемблерной программы беззнакового умножения 16-разрядных чисел, дающей 32-разрядный результат.
;----------------------------------------------
;УМНОЖЕНИЕ ДВУХБАЙТНОГО ЧИСЛА НА ДВУХБАЙТНОЕ ЧИСЛО. ;ВРЕМЯ ИСПОЛНЕНИЯ 52 ТАКТА.
;ИСХОДНЫЕ ЧИСЛА: В R3-СТ.,R2-МЛ. И R1-СТ.,R0-МЛ. РЕЗУЛЬТАТ В R7-СТ.,R6,R5,R4 ;----------
MUL2B: ;УМНОЖЕНИЕ МЛАДШИХ ЦИФР ПЕРВОГО И ВТОРОГО ЧИСЛА
MOV |
A,R2 |
MOV |
B,R0 |
MUL |
AB |
MOV |
R4,A |
MOV |
R5,B |
;УМНОЖЕНИЕ МЛАДШЕЙ ЦИФРЫ ПЕРВОГО ЧИСЛА НА СТАРШУЮ ВТОРОГО ЧИСЛА |
|
MOV |
A,R2 |
MOV |
B,R1 |
MUL |
AB |
;СЛОЖИТЬ С ПРЕДЫДУЩИМ РЕЗУЛЬТАТОМ |
|
ADD |
A,R5 |
MOV |
R5,A |
CLR |
A |
ADDC |
A,B |
MOV |
R6,A |
;УМНОЖЕНИЕ МЛАДШЕЙ ЦИФРЫ ВТОРОГО ЧИСЛА НА СТАРШУЮ ПЕРВОГО ЧИСЛА |
|
MOV |
A,R3 |
MOV |
B,R0 |
MUL |
AB |
;СЛОЖИТЬ С ПРЕДЫДУЩИМ РЕЗУЛЬТАТОМ |
|
ADD |
A,R5 |
MOV |
R5,A |
MOV |
A,R6 |
ADDC |
A,B |
MOV |
R6,A |
CLR |
A |
RLC |
A |
MOV |
R7,A |
;УМНОЖЕНИЕ СТАРШИХ ЦИФР ПЕРВОГО И ВТОРОГО ЧИСЛА |
|
MOV |
A,R3 |
MOV |
B,R1 |
MUL |
AB |
;СЛОЖИТЬ С ПРЕДЫДУЩИМ РЕЗУЛЬТАТОМ |
|
ADD |
A,R6 |
MOV |
R6,A |
MOV |
A,R7 |
ADDC |
A,B |
MOV |
R7,A |
;----------------------------------------------------
Bascom позволяет производить очень сложные вычисления, благодаря краткой форме записи производимых действий. Компилятор при этом создает очень компактный код, содержащий только установку исходных значений и вызов библиотечных подпрограмм. При реализации сложных формул необходимо раскладывать ее на ряд последовательно выполняемых операций, каждая из которых должна содержать только одно действие. Чтобы уменьшить число переменных, используемых при вычислениях, необходимо анализировать порядок их использования, занимая для хранения промежуточных результатов регистры с уже ненужными данными. Ниже, в качестве примера, приведена простая подпрограмма для вычисления квадратного корня. У данной программы есть одна неприятность – неопределенность результата при нулевом значении аргумента. Чтобы избавится от этого недостатка, к исходному числу добавляется любое незначительное число.
'----------------------------------------------------
' тестирование программы вычисления квадратного корня
Dim X As Single , Y As Single , Z As Single 'временные значения
============================================================================= 23-4
===================================== Справочник по программированию «Bascom-8051» ==
Do
Input "x=" , X
X = X + 0.00001
Gosub Sqr
Print Y Loop
'-----------------------------
'подпрограмма вычисления квадратного корня по формуле Ньютона 'вычисление производится методом последовательных приближений:
'Yi+1 = (X / Yi + Yi) * .5
'начальное (исходное) значение вычисляется по формуле: |
|||
' |
Y0 |
= X / 200 |
+ 2 |
Sqr: |
|
|
|
'вычислим начальное значение, с которого начнем иттерацию |
|||
Do |
Y = X * 0.005 |
: Y = Y + |
2 |
|
|
|
|
'вычислим |
очередное значение корня Y = (X / |
Y + Y) * .5 |
|
Z = Y |
: Y = X / Y : Y = Y + Z : Y = Y * |
0.5 |
|
'проверим |
IF ABS(Y1 - Y) > Y / 1000 |
GOTO srq_1 |
|
'в Bascom |
функция ABS не работает с |
числами в формате с плавающей точкой |
|
Z = Z - Y : Z = Z / Y |
'вычисляем отношение между посл. и предпосл. значение |
$asm |
; делаем абсолютное значение стиранием знака |
Anl {z + 3} , #&H7f |
|
$end Asm |
'если необходимая точность достигнута (0.1 %) |
Loop Until Z < 0.001 |
|
Return |
'выходим |
'---------------------------------------------------- |
|
Далее приведены еще несколько подпрограмм для вычисления функций методом разложения в степенной ряд. В этих программах для сокращения размера кода применена упаковка повторяющихся операций в подпрограммы, что значительно сокращает размер кода (2 – 3 раза).
'--------------------------------------------------------------------------
'вычисление десятичного логарифма. Log(x) функция в Ua от аргумента Ua '--------------------------------------------------------------------------
Log:
'выделим порядок (двоичный исходного числа), а остаток сделаем
'числом в диапазоне от 1 до 2 |
|
|
$asm |
R0 , #{ua + 2} |
|
Mov |
|
|
Mov |
A , @R0 |
|
Mov |
C , Acc.7 |
|
Clr |
Acc.7 |
|
Mov |
@R0, A |
|
Inc |
R0 |
|
Mov |
A , @r0 |
|
Rlc |
A |
|
Mov |
{tmpb} , A |
|
Mov @R0 , #&H40 |
|
|
$end Asm |
|
|
'Числа для справки: |
|
|
'ln(2)= 0,69314718, 1/ln(2)=1.442695, ln(10)=2.3025851, 1/ln(10)=0.4342945 |
||
Gosub Ln |
'вычислим натуральный логарифм остатка |
|
Ua = Ua * 1.442695 |
'преобразуем в двоичный логарифм остатка |
|
Ub = Tmpb : Ub = Ub - 128 : Ua = Ua + Ub 'объединим порядок и логарифм остатка
Ua = Ua * 0.30103001 |
'и получим двоичный логарифм числа |
'преобразуем в десятичный логарифм |
|
Return |
|
'--------------------------------------------------------------------------
'вычисление натурального логарифма. Ln(x) функция в Ua от аргумента Ua 'очень хорошая точность только от x = 1 до x = 3 (около 0.003 %)
============================================================================= 23-5
===================================== Справочник по программированию «Bascom-8051» ==
'при x = 5 погрешность около 0.02 %, а при x = 10 - 0.7 %
'
' |
3 |
5 |
7 |
9 |
11 |
13 |
n |
x-1 |
' |
p p |
p p p |
p |
p |
||||
' ln(x)=2*[p + - + - + |
- + - + -- + -- ...+-] где p = ----- |
|||||||
' |
3 |
5 |
7 |
9 |
11 |
13 |
n |
x+1 |
' |
|
|
|
|
|
|
|
|
'-------------------------------------------------------------------------- |
|
|
|
|
|
|
|
|
Ln: |
Ub = Ua - 1 : Ua = Ua + 1 |
|
|
|
|
|||
|
|
|
|
|
||||
|
Ua = Ub / Ua |
|
|
|
|
|
|
|
|
Ub = Ua * Ua : Uc = Ub * Ua : Ud = Uc * 0.3333333 : Gosub Add_aad |
|||||||
|
Gosub Mul_cbc : Ud = Uc * |
0.2 : Gosub Add_aad |
|
|||||
|
Gosub Mul_cbc : Ud = Uc * |
0.14285714 : Gosub Add_aad |
|
|||||
|
Gosub Mul_cbc : Ud = Uc * |
0.1111111111 : Gosub Add_aad |
||||||
|
Gosub Mul_cbc : Ud = Uc * |
0.09090909 : Gosub Add_aad |
'точность 0.003 % |
|||||
|
Gosub Mul_cbc : Ud = Uc * |
0.07692308 : Gosub Add_aad |
||||||
'Gosub Mul_cbc : Ud = Uc * 0.06666667 : Gosub Add_aad
'Gosub Mul_cbc : Ud = Uc * 0.05882353 : Gosub Add_aad
'Gosub Mul_cbc : Ud = Uc * 0.05263158 : Gosub Add_aad Ua = Ua + Ua : Return
'---------------------
'для сокращения размера кода повторяющиеся программы выделим
Mul_cbc:
Uc = Ub * Uc : Return '---------------------
Add_aad:
Ua = Ua + Ud : Return
'--------------------- |
|
|
'-------------------------------------------------------------------------- |
|
|
'синусная функция в Ua от аргумента Ua в радианах |
|
|
'хорошая точность только до 90 град (около 0.0002 %) |
|
|
'при 180 град погрешность достигает 4 %!!! |
|
|
Sin: |
|
|
Ub = Ua : Ua = Ua * Ua |
|
|
Uc = Ua |
' 1/72 |
|
Ua = Ua * 0.013888889 |
|
|
Gosub Sub_aa1 |
|
|
Gosub Mul_aac |
'1/42 |
|
Ua = Ua * 0.0238095243 |
|
|
Gosub Add_aa1 |
|
|
Gosub Mul_aac |
'1/20 |
|
Ua = Ua * .05 |
|
|
Gosub Sub_aa1 |
|
|
Gosub Mul_aac |
'1/6 |
|
Ua = Ua * 0.16666667 |
|
|
Gosub Add_aa1 |
|
|
Gosub Mul_aab |
|
|
Return |
|
|
'--------------------- |
|
|
Add_aa1: |
|
|
Ua = Ua + 1 |
|
|
Return |
|
|
'--------------------- |
|
|
Sub_aa1: |
|
|
Ua = Ua - 1 |
|
|
Return |
|
|
'--------------------- |
|
|
Mul_aac: |
|
|
Ua = Ua * Uc |
|
|
Return |
|
|
'--------------------- |
|
|
============================================================================= |
23-6 |
|
===================================== Справочник по программированию «Bascom-8051» ==
Mul_aab: |
|
|
Ua = Ua * Ub |
|
|
Return |
|
|
'-------------------------------------------------------------------------- |
|
|
'косинусная функция в Ua от аргумента Ua в радианах |
|
|
'хорошая точность только до |
90 град (около 0.003 % от максимального значения) |
|
'при 180 град погрешность достигает 3 %!!! |
|
|
Cos: |
'angle squared |
|
Ua = Ua * Ua |
|
|
Ub = Ua |
'save |
|
Ua = Ua * 0.017857144 |
'1/56 |
|
Gosub Sub_aa1 |
|
|
Gosub Mul_aab |
'1/30 |
|
Ua = Ua * 0.033333333 |
|
|
Gosub Add_aa1 |
|
|
Gosub Mul_aab |
'1/12 |
|
Ua = Ua * 0.083333333 |
|
|
Gosub Sub_aa1 |
|
|
Gosub Mul_aab |
'1/2 |
|
Ua = Ua * 0.5 |
|
|
Gosub Add_aa1 |
|
|
Return |
|
|
'-------------------------------------------------------------------------- |
|
|
'показательная функция e^x в Ua от аргумента Ua в радианах |
|
|
'хорошая точность только до x = 3 (около 0.002 %) |
|
|
'точность при x = 4 (около 0.07 %), а при x = 10 - 4 % |
|
|
Exp: |
|
'1+x/1! |
Ub = Ua + 1 |
0.5 : Gosub Add_bbd |
|
Gosub Mul_cac : Ud = Uc * |
'+x^2/2! |
|
Gosub Mul_cac : Ud = Uc * |
0.16666667 : Gosub Add_bbd |
'+x^3/3! |
Gosub Mul_cac : Ud = Uc * |
0.041666667 : Gosub Add_bbd |
'+x^4/4! |
Gosub Mul_cac : Ud = Uc * |
0.008333333 : Gosub Add_bbd |
'+x^5/5! |
Gosub Mul_cac : Ud = Uc * |
0.0013888889 : Gosub Add_bbd |
'+x^6/6! |
Gosub Mul_cac : Ud = Uc * |
0.0001984127 : Gosub Add_bbd |
'+x^7/7! |
Gosub Mul_cac : Ud = Uc * |
0.000024801587 : Gosub Add_bbd |
'+x^8/8! |
Gosub Mul_cac : Ud = Uc * |
0.000002755732 : Gosub Add_bbd |
'+x^9/9! |
Gosub Mul_cac : Ud = Uc * |
0.0000002755732 : Gosub Add_bbd '+x^10/10! |
|
Return |
|
|
'--------------------- |
|
|
Add_bbd: |
|
|
Ub = Ub + Ud : Return '---------------------
Mul_cac:
Uc = Uc * Ua : Return '---------------------
Add_cad:
Ua = Ua + Ud : Return '--------------------------------------------------------------------------
Возможность применения тех или иных вычислительных программ частот обусловлена производительностью микроконтроллера. Далее приведена программа цифрового фильтра нижних частот третьего порядка. Для его реализации с указанной частотной характеристикой требуется очень высокая производительность микроконтроллера – в десять раз выше стандартной (эквивалентно более 120 МГц). Для этого могут быть использованы только улучшенные модели W77E58, DS89C420). При использовании микроконтроллера с ядром AVR эта и все приведенные выше вычислительные программы работают быстрее в среднем в 50 раз (компилированные
BasAVR).
'--------------------------------------------------------------------------
'фильтр с частотой среза около 5-10 Гц при частоте выборок 500 Гц
'(частоте вызова данной программы). Коэффициент передачи фильтра:
' |
1 |
1 |
' К = |
0.006*p^2-----------------------+ 0.111*p + 1 |
* --------- |
' |
0.1*p + 1 |
============================================================================= 23-7
===================================== Справочник по программированию «Bascom-8051» ==
'а это коэффициенты цифрового фильтра
Const _a = 0.013869625
Const _b = -0.83217753 |
|
Const _c = 1.8183079 |
|
Const _d = 0.09090909 |
|
Const _e = 0.9090909 |
|
'--------------------- |
'входное значение |
Dim Ux As Single |
|
Dim Uy As Single |
'выходное значение первого звена |
Dim Uy1 As Single |
'предыдущее выходное значение первого звена |
Dim Uy2 As Single |
'предпредыдущее выходное значение первого звена |
Dim Uz As Single |
'выходное значение второго звена |
Dim Uz1 As Single |
'предыдущее выходное значение второго звена |
'------------------------ |
|
'программа цифрового фильтра третьего порядка содержащего два звена
'2-го и 1-го порядка:
'Uy = Ux * a + Uy2 * b + Uy1 * c - формула звена второго порядка
'Uz = Uy * d + Uz1 * e - формула фильтра звена первого порядка
'-----
Filtr_3p:
Uy = Ux * _a : Uz = Uy2 * _b : Uy = Uy + Uz 'программа фильтра второго порядка
Uz = Uy1 * _c : Uy = Uy + Uz Uy2 = Uy1 : Uy1 = Uy
'-----
Uz = Uy * _d : Uy = Uz1 * _e : Uz = Uy + Uz 'программа фильтра первого порядка
Uz1 = Uz : Return '------------------------------------------
Bascom версий 2.хх расширил диапазон возможных действий с битовыми переменными. Теперь стала возможным операция с индексированными битами. Причем в качестве индекса может выступать другая переменная, и конечно только байтовая. Например:
Dim X As Byte , Y As Byte |
|
|
X.3 = |
Y.2 : P2.1 = Y.4 'передача значений между битами байтовых переменных |
|
For X = 0 |
To 7 |
'индексирование бита |
Set P3.X |
|
|
Next |
|
'проверка индексированного бита |
If P2.X=1 Then |
||
P2.X = 0 |
|
|
End If |
|
|
============================================================================= 23-8
===================================== Справочник по программированию «Bascom-8051» ==
24. Преобразование строковых переменных
Строковые переменные могут преобразовываться в другие строковые переменные – длинные разбираться на части и, наоборот, из коротких строк могут складываться длинные. Bascom обеспечивает преобразование цифр, записанных в строковых переменных, в числа и преобразование чисел в строковые переменные. Причем числа могут быть записаны в виде десятичных или шестнадцатеричных цифр (символов). В следующей таблице показано расположение байтов строковой переменной в памяти после ее очистки (нулями). В первом случае в переменную длиной N записано символами число «1234». Во втором случае на его место записано сообщение
«AF»
Расположение в памяти строковой переменной, определенной как: Dim S As String * N
Adr |
Adr+1 |
Adr+2 |
Adr+3 |
Adr+4 |
… |
Adr+N |
Adr+N+1 |
30H |
31H |
32H |
33H |
00H |
00H |
00H |
00H |
41H |
46H |
00H |
33H |
00H |
00H |
00H |
00H |
Преобразование строк может выполняться в явном и неявном виде. Примеры явного преобразования: а) разделение строк с помощью операторов MID(), LEFT(), RIGHT();
б) слияние суммированием строк ( “.. ” + “..”); в) монтаж строк с помощью оператора MID();
г) преобразование строки в число с помощью функций VAL(), HEXVAL();
д) преобразование числа в строку с помощью функций STR(), HEX(), CHR().
Неявное преобразование строковых переменных происходит при вводе (оператором типа INPUT) и выводе (оператором PRINT) цифровых переменных. Ниже дана демонстрационная программа, показывающая основные способы преобразования строковых переменных. Подобные примеры имеются и в других программах, приведенных выше.
'------------------------------- |
'код для ЦАП |
Dim R_rd As Integer |
|
Dim R_frq As Single |
'частота для синтезатора |
Dim R_lin As String * 6 |
'введенная строка |
Dim R_tmp As String * 20 |
'временная строка |
Mc: |
|
Input "Input six characters" , R_lin
'в R_lin |
принятая строка: “+XXXXX”, “-XXXXX”, “HXXXXX” или “R” |
|
R_tmp = Left(r_lin , 1) |
'выделим первый символ для анализа |
|
If R_tmp = "+" Then |
'положительная полярность? |
|
R_tmp = Mid(r_lin , 2 , 5) |
'выделим только числовое значение |
|
R_rd = Val(r_tmp) |
'преобразовать в целое число |
|
Goto Mc |
'строка обработана - в главный цикл |
|
End If |
|
'отрицательная полярность? |
If R_tmp = "-" Then |
||
R_rd = Val(r_lin) |
'преобразуем строку с полярностью |
|
Goto Mc |
'строка обработана - в главный цикл |
|
End If |
|
'частота в Герцах? |
If R_tmp = "H" Then |
||
R_tmp = Mid(r_lin , 2 , 5) |
'выделим числовое значение |
|
R_frq = Val(r_tmp) |
'преобразовать в число с плав. точкой |
|
R_frq = R_frq * 0.001 |
'преобразовать в килогерцы |
|
Goto Mc |
'строка обработана - в главный цикл |
|
End If |
|
'вернуть значения |
If R_tmp = "R" Then |
||
Print "H" ; R_frq ; "D" ; R_rd 'передадим строку состояния |
||
'или иначе |
|
|
R_tmp = "H" + Str(r_frq) + "D" + Str(r_rd) 'сформируем строку состояния |
||
Print R_tmp |
'передадим готовую строку |
|
Goto Mc |
'строка обработана - в главный цикл |
|
End If |
|
'в главный цикл |
Goto Mc |
|
|
'------------------------------- |
|
|
В новой версии добавлено два новых оператора LCASE и UCASE, осуществляющих преобразование букв строковых переменных. Первый оператор уменьшает размер букв (делает все буквы строчными), а второй наоборот, делает все буквы большими (прописными). Замечательно, что эти операторы не просто добавляют или
============================================================================= 24-1
===================================== Справочник по программированию «Bascom-8051» ==
вычитают из кода символа число 20h, а выбирают символы, являющиеся латинскими буквами. Таким образом цифры и другие служебные символы не преобразуются и остаются неизменными.
Еще одним важным добавление в версии 1.20 является функция INSTR(), с помощью которой стало возможным производить поиск значения одной строки в составе другой строки. В результате работы этой функции возвращается номер позиции, указывающий на начало искомого фрагмента. Если фрагмент не найден, то возвращается нулевое значение. Практическая ценность этой функции заключается в возможности применения сложных строк в протоколах обмена между процессорами, т.е. стало возможным применять команды переменного состава или производит накопление нескольких команд без обработки. Ниже дается пример применения новых функций.
'----------------------------------------------
' демонстрация декодирования сложных команд
'----------------------------------------------
' программа запрашивает строку (командой "r", которая ' может содержать одну или несколько команд:
' Axx - установить параметр с двухзначным числом
' Bxxxx - установить параметр с четырехзначным числом
' Cxxxxxxxx - установить параметр с восьмизначным числом
' Например: "A12B1234C00123456", "A30", "c00004055" или "a05b0010"
Dim P_a As Byte , P_b As Word , P_c As Long |
|
|
Dim Tmp1 As Byte , Tmps As String * 10 |
|
|
Dim In_buf As String * 20 |
|
|
'---------------------------------------------- |
'очистим переменые параметров |
|
P_a = 0 : P_b = 0 : P_c = 0 |
||
Do |
'цикл установки параметров |
|
Input "r" , In_buf : |
'приняли строку с командами |
|
In_buf = Ucase(in_buf) |
'переведем все символы в верхний регистр |
|
'проверим наличие первой команды |
|
|
Tmps = "A" : Tmp1 = Instr(1 , In_buf , Tmps) |
||
If Tmp1 <> 0 Then |
'если она присутствует, |
|
Tmps = Mid(in_buf , Tmp1 , 2) |
'выберем числовое значение |
|
P_a = Val(tmps) |
'и преобразуем в число |
|
End If |
|
|
'проверим наличие второй команды |
|
|
Tmps = "B" : Tmp1 = Instr(1 , In_buf , Tmps) |
||
If Tmp1 <> 0 Then |
'если она присутствует, поступим аналогично |
|
Tmps = Mid(in_buf , Tmp1 , 4) : P_b = Val(tmps) |
||
End If |
|
|
'аналогично проверим наличие третьей команды
Tmps = "C" : Tmp1 = Instr(1 , In_buf , Tmps) If Tmp1 <> 0 Then
|
Tmps = Mid(in_buf , Tmp1 , 8) : P_c = Val(tmps) |
|
Wait |
End If |
'немного подождем и повторим |
1 |
||
Loop |
|
|
============================================================================= 24-2
===================================== Справочник по программированию «Bascom-8051» ==
25. Применение логических функций
Сущность логических функций, очевидно, не требует пояснений, поэтому остановимся только на особенностях их применения. Логические операции производятся только над битовыми, одно-, двух- и четырехбайтовыми переменными и правильно выполняются только при одинаковой размерности исходных переменных. Использование разных типов переменных недопустимо, т.к. получаются непредсказуемые результаты, и происходит модификация регистров, не участвующих в назначенной операции. В качестве операнда логических операций могут применяться и константы. Форма записи констант должна быть “понятной” компилятору, т.к. неоднозначная запись также может дать неверный результат. Логические операции с числами с плавающей точкой не выполняются и даже не компилируются (пропускаются). К сожалению, компилятор совершенно не определяет ошибки назначения неверных операндов логических операций. Эта задача предоставлена программисту. Ниже приведена демонстрационная программа логических операций. В ней показана возможность (или невозможность) их применения и время исполнения.
'---------------------------------------------------- |
Тестирование логических операций |
|
' |
||
'---------------------------------------------------- |
|
|
$large |
|
'число 5 |
Dim Cnt As Const 5 |
||
Dim Bt As Bit Dim , Byt1 As Byte 'определим по паре переменных всех типов
Dim Byt As Byte , Byt1 As Byte |
|
Dim Wrd As Word , Wrd1 As Word |
|
Dim Intg As Integer , Intg1 As Integer |
|
Dim Lng As Long , Lng1 As Long |
|
Dim Sng As Single , Sng1 As Single |
|
'---------------------------------------------------- |
'установить биты |
Set Bt : Reset Bt1 |
|
Bt = Bt And Bt1 : Bt = Not Bt |
'операция над битами |
Print Bt |
'вывод значения бита |
'---------------------------------------------------- |
'это совершенно недопустимо |
Byt = Byt Or Bt |
|
Byt1 = Byt1 Or &HAA |
'правильно |
Byt1 = Byt And Byt1 |
'правильно |
Wrd1 = &H1234 : Byt = &H77 : Intg = 0 |
|
Intg1 = Byt Or Wrd1 |
'неверно. операция произойдет |
'только с младшими разрядами Intg = |
1277H смотри следующую строку |
|
Intg = &H1234 : Wrd1 = &H77 |
|
'нужно делать так |
Intg1 = Byt : Intg1 = Wrd1 Or Intg1 |
||
Wrd = 1234 Or 5678 |
'результат: Intg = 0077H |
|
'операнды десятичные константы |
||
Wrd1 = Wrd Xor 131112 |
'результат будет неправильным |
|
Lng = Lng Xor &H10000000 |
'правильно |
|
'---------------------------------------------------- |
'над числами с плав. точкой лог. оп. |
|
Sng = -1 : Sng1 = 1000 |
||
Sng1 = Not Sng1 |
'не выполняются! |
|
'---------------------------------------------------- |
|
|
'производительность логических операций |
||
Byt = Byt And Byt1 |
'11 |
тактов |
Byt = Byt Or Byt1 |
'11 |
тактов |
Byt = Byt Xor Byt1 |
'11 |
тактов |
Wrd = Wrd And Wrd1 |
'20 |
тактов |
Intg = Intg And Intg1 |
'20 |
тактов |
Lng = Lng And Lng |
'93 |
такта |
End |
|
|
'---------------------------------------------------- |
|
|
============================================================================= 25-1
===================================== Справочник по программированию «Bascom-8051» ==
26. Организация циклов и систем реального времени
Bascom предлагает полный набор операторов для построения повторяющихся программ. При программировании могут применяться три основные конструкции:
а) безусловный цикл, который применяется при постоянном повторении одних и тех же действий. Конструкция такого цикла строится с помощью операторов DO … LOOP и служит основой главного цикла почти каждой программы. Конструкция безусловного цикла применяется там, где требуется за один проход проверять несколько условий (точнее более одного условия);
б) условный цикл повторяется, пока выполняется, определяющее его, условие. Такой цикл строится с помощью операторов WHILE … WEND. Естественно, эта конструкция может применяться реже, там, где требуется проверять выполнение единственного условия;
в) условный цикл, построенный с помощью операторов DO … LOOP UNTIL, также может использоваться в ограниченных случаях, но его исполнение зависит от невыполнения условий или, наоборот, выполнение заданных условий становится причиной его прекращения.
Далее, в качестве примера использования циклических конструкций, приведена упрощенная программа цифрового синтезатора частоты и с регулируемым уровнем выхода. Программа, находясь в главном цикле, ожидает прихода управляющей команды, которая должна содержать значение устанавливаемой частоты или напряжения. Определение типа команды производится по первому символу (“V” или “H”). Далее следует цифровое значение, выраженное в вольтах или килогерцах. Принятое сообщение преобразуется и направляется в исполнительные устройства: ЦАП уровня выходного напряжения, цифровой синтезатор и группу реле (или аналоговых ключей), включающих выходной фильтр, соответствующий воспроизводимой частоте. В приведенной программе используются конструкции всех видов циклов. Также имеются основные элементы системы отсчета реального времени, работающей параллельно с основной программой. Для этого введен счетчик, модифицируемый в прерывании, с периодичностью в 10 мс. Конечно, в данном примере такая система используется по ничтожному поводу – создания временной задержки, однако при необходимости ее функции можно существенно расширить, например, для формирования в портах сигналов управления заданной длительности или частоты.
'------------------------------------------
' Программа синтезатора частоты
'------------------------------------------
Dim B_tim As Bit |
'бит "идет счет времени" |
Dim Temp As Byte |
'временные данные |
Dim R_tim As Byte |
'счетчик коротких интервалов |
Dim Frng As Byte |
'номер предела частоты |
Dim R_bw As Integer |
'код ЦАП и 16-битные данные |
Dim R_bd As Long |
'32-битные данные |
Dim R_fld As Single |
'напряжение или число с плавающей точкой |
Dim R_frq As Single |
'значение частоты с плавающей точкой |
Dim Ibuf As String * 16 |
'приемный буфер |
'--------------------- |
|
'подключение синтезатора AD9850 |
|
B_data Alias P3.3:B_clk Alias P3.2:B_fqud Alias P3.1:B_ress Alias P3.4 |
|
'--------------------- |
|
'подключение ЦАП уровня AD766
B_datu Alias P1.0 : B_clku Alias P1.2 : B_ldu Alias P1.1 '---------------------
'TIMER1 в режиме 8-бит. таймера с внутр. тактир. для синхронизации UART Config Timer1 = Timer , Gate = Internal , Mode = 2
$crystal = 12000000 |
'при кварце 12 МГц |
$baud = 4800 |
'скорость 4.8 кБ |
Th1 = 243 : Start Timer1 |
'константа скорости |
'--------------------- |
|
'назначение режимов прерываний |
'вектор прерывания |
On Timer0 Timer_0_int Nosave |
|
Enable Timer0 |
'разрешить прерывания таймера 0 |
Enable Interrupts |
'вообще разрешить прерывания |
Th0 = &HD8 : Tl0 = &HFD |
'чтобы прерывание наступило не позже 10 мс |
'--------------------- |
|
Mc: |
'Главный цикл |
Do |
|
Input Ibuf |
'здесь ждем прихода управляющей команды |
============================================================================= 26-1
===================================== Справочник по программированию «Bascom-8051» ==
Temp = Asc(ibuf) |
'считаем код первого символа принятой строки |
If Temp = &H48 Then |
'это частота "HXXXXXXXX"? |
Goto Herz |
|
End If |
'это напряжение "VXXXXXXXX"? |
If Temp = &H56 Then |
|
Goto Volt |
|
End If |
|
Loop |
|
'--------------------- |
|
'обработка данных о напряжении |
|
Volt: |
'определим длину принятой строки |
Temp = Len(ibuf) |
|
Ibuf = Mid(2 , Temp) |
'отбросим префикс "V" |
R_fld = Val(ibuf) |
'преобразуем в число (в вольтах) |
R_fld = R_fld * 10922.67 |
'2^15/3 = 10922.67 преобр. разряды ЦАП |
R_bw = R_fld |
'преобразовать в целое число |
Gosub Sload_766 |
'загрузка данных в ЦАП AD766, AD1851 |
Goto Mc |
|
'--------------------- |
|
'обработка данных о частоте |
|
Herz: |
'определим длину принятой строки |
Temp = Len(ibuf) |
|
Ibuf = Mid(2 , Temp) |
'отбросим префикс "H" |
R_frq = Val(ibuf) |
'преобразуем в число (в килогерцах) |
R_fld = R_frq * 71582.788 |
'2^32 / Fclk = 4294967296 / 60000 kHz |
R_bd = R_fld |
'преобр. в целое 32-разр. число |
Gosub Sload_9850 |
'загрузка данных в синтезатор AD9850 |
Gosub Sel_rf |
'опр. номер диапазона, соотв. частоте |
Gosub Set_com |
'подключим соответствующий фильтр |
Goto Mc |
|
'------------------------------------------ |
|
'подпрограмма вычисления предела частоты. Результат в Frng: |
|
'0 - DC (менее 5 Гц), 1 - 0.005-110 kHz, 2 - 110-175 kHz, 3 - 175-280 kHz '4 - 280-440 kHz, 5 - 440-700 kHz, 6 - 700-1100 kHz, 7 – 1.1-1.75 МHz
'8 – 1.75-2.8 МHz, 9 – 2.8-4.4 М, 10 – 4.4-7 МHz, 11 - 7-11 МHz '-----
Sel_rf: |
'инициализируем счетчик пределов |
Frng = 0 |
|
Do |
|
R_fld = Lookup(frng , Hlim_fr)'считаем очередное значение верхней границы |
|
If R_fld > R_frq Then |
'сравним с текущей частотой |
Goto S_rfe |
'если ниже верхней границы - выходим |
End If |
'перейдем к следующему пределу и повторить цикл |
Incr Frng |
|
Loop Until Frng = 12 |
'пока все пределы не будут пройдены |
S_rfe: |
|
Return
'таблица значений верхней граница диапазонов воспроизводимой частоты в кГц
Hlim_fr:
Data 0.005! , 110! , 175! , 280! , 440! , 700! Data 1100! , 1750! , 2800! , 4400! , 7000! '------------------------------------------
'подпрограмма загрузки данных в синтезатор AD9850
Sload_9850: |
'сбросить интерфейс |
Set B_fqud : Reset B_fqud |
|
Set B_clk : Reset B_clk |
'защелкнуть код последовательного режима |
Set B_fqud : Reset B_fqud |
'разрешить последовательный режим |
'выдвинуть данные из R_bd в режиме 3 (мл. сначала , _-_) Shiftout B_data , B_clk , R_bd , 3
'выдвинуть последний байт с режимом и фазой
Temp = 0 : Shiftout B_data , B_clk , Temp , 3
Set B_fqud : Reset B_fqud 'исполнять загруженные данные
Return
============================================================================= 26-2
===================================== Справочник по программированию «Bascom-8051» ==
'---------------------
'подпрограмма загрузки данных в ЦАП AD766, AD1851 Sload_766:
'выдвинуть данные из R_bw в режиме 0 (ст. сначала , -_-) Shiftout B_datu , B_clku , R_bw , 0
Reset B_ldu : Set B_ldu |
'загрузить данные |
Return |
|
'------------------------------------------
'подпрограмма загрузки данных коммутации для подключения на выход 'синтезатора соответствующего фильтра с помощью поляризованных реле
Set_com:
Set P2.3 : Gosub Wait_30ms 'включить отбойную обмотку всех реле на 30 мс
R_bw |
= Lookup(frng , Tab_com) |
'загрузка данных портов |
||
P2 |
= |
High(r_bw) : P0 |
= Low(r_bw) |
'перезапись в порты |
Gosub Wait_30ms |
= 0 |
'включить обмотку нужного реле на 30 мс |
||
P2 |
= &b11110000 : P0 |
'очистить порты |
||
Return |
|
|
|
|
'-----
Tab_com:
'Таблица сост. портов упр. реле, в зависимости от диапазона частоты
' |
ПОРТ 2 |
ПОРТ 0 |
|
|
' реле ф. до 4.4 МГц--¬ |
-- реле фильтра до 2.8 МГц |
|
||
' реле ф. до 7 МГц --¬¦ |
¦ -- реле фильтра до 1.75 |
МГц |
||
' реле ф. до 11 МГц-¬¦¦ |
¦¦ -- реле фильтра до 1.1 |
МГц |
||
' отбойная обмотка-¬¦¦¦ |
¦¦¦ -- реле фильтра до 700 |
кГц |
||
' всегда 1 |
-¬¦¦¦¦ |
¦¦¦¦ -- реле фильтра до 440 |
кГц |
|
' всегда 1 |
-¬¦¦¦¦¦ |
¦¦¦¦¦ -- реле фильтра до 280 кГц |
||
' всегда 1 |
-¬¦¦¦¦¦¦ |
¦¦¦¦¦¦ -- реле фильтра до |
175 кГц |
|
' всегда 1 |
-¬||||||| |
||||||| -- реле фильтра до |
110 кГц |
|
Data &B11110000 , &B00000001 |
'DC |
Data &B11110000 , &B00000001 |
'5-110 КГЦ |
Data &B11110000 , &B00000010 |
'110-175 КГЦ |
Data &B11110000 , &B00000100 |
'175-280 КГЦ |
Data &B11110000 , &B00001000 |
'280-440 КГЦ |
Data &B11110000 , &B00010000 |
'440-700 КГЦ |
Data &B11110000 , &B00100000 |
'700-1100 КГЦ |
Data &B11110000 , &B01000000 |
'1.1-1.75 МГЦ |
Data &B11110000 , &B10000000 |
'1.75-2.8 МГЦ |
Data &B11110001 , &B00000000 |
'2.8-4.4 МГЦ |
Data &B11110010 , &B00000000 |
'4.4-7 МГЦ |
Data &B11110100 , &B00000000 |
'7-11 МГЦ |
'------------------------------------------ |
|
'обработка прерывания таймера 0 |
|
Timer_0_int: |
|
Counter0 = &HD8FD : Start Timer0 ;период прерыв. 10 мс
'компилируется проще, если загружать счетчики следующим образом:
'Th0 = &HD8 : Tl = &HFD |
'Ffffh-10000 = D8fdh - период прерыв. 10 мс |
$asm |
;обработка счетчика реального времени |
Jnb {b_tim} , Tim0_i1 |
|
Djnz {r_tim} , Tim0_i1 |
;уменьшать до нуля |
Clr B_tim |
;когда достигнем нуля - очистим бит |
Tim0_i1: |
|
$end Asm |
|
Return |
|
'------------------------------------------ |
|
'подпрограмма задержки, использующая прерывание |
|
Wait_30ms: |
|
Set B_tim : R_tim = 3 |
'цикл будет повторять пока, не очистится B_tim |
While B_tim = 1 |
|
Idle |
'ждем в останове |
Wend |
|
Return '------------------------------------------
============================================================================= 26-3
===================================== Справочник по программированию «Bascom-8051» ==
Кроме циклов, задаваемых с помощью упомянутых специальных конструкций, Bascom использует скрытые циклы, выполняемые внутри некоторых операторов:
а) WAIT, WAITMS, DELAY - временной задержки. Условие выхода из цикла (окончание выполнения оператора) – истечение заданного интервала времкени;
б) BITWAIT - ожидания заданного уровня бита. Выход из цикла – поступление заданного логического уровня бита;
в) DEBOUNCE - периодического опроса состояния бита. Выход из цикла – также поступление заданного логического уровня бита;
г) WAITKEY - ожидания приема символа. Выход их цикла – прием символа;
д) INPUT, INPUTHEX - ожидания ввода символьного значения переменной. Выход из цикла – прием символа разделителя;
е) INPUTBIN - ожидания ввода двоичной переменной. Выход из цикла – прием необходимого количества байтов для заполнения переменной.
Циклическая конструкция FOR … NEXT является, по сути, оператором многократного действия, обеспечивающего выполнение других операций заданное число раз. Bascom обеспечивает работу этой конструкции со всеми типами числовых переменных и констант. Необходимо, чтобы обеспечить правильную работу цикла FOR … NEXT, задавать одинаковые значения размерности переменных (или констант), определяющих начальное и конечное значения, а также приращения. Рекомендуется так строить программу, чтобы циклы FOR … NEXT использовали только байтовые или двухбайтовые параметры. При этом скомпилированный код работает быстро и надежно. При использовании в качестве параметров цикла чисел в формате с плавающей точкой, быстродействие резко снижается (приблизительно до 10 мс на шаг), и возрастает объем генерируемого кода. В следующес разделе приведен интересный пример использования циклов FOR … NEXT.
============================================================================= 26-4
===================================== Справочник по программированию «Bascom-8051» ==
27. Программирование с использованием индексированных переменных Bascom
Компилятор Bascom допускает использование массивов переменных, записываемых под одним именем и различающихся порядковым номером расположения. Параметр, указывающий номер переменной в массиве называется индексом. В качестве индекса (указателя) может использоваться константа, одноили двухбайтная числовая переменная. Массив может располагаться во внутренней или во внешней памяти. Массивы могут строиться из одинаковых переменных любого типа, за исключением битовых. С индексированными переменными во многих случаях можно работать как с обычными переменными соответствующего типа. Однако, некоторые операторы не рассчитаны на подстановку в качестве операндов индексированных переменных и воспринимают их как обычные переменные, т.е. считывают и записывают данные только первого элемента массива, имеющего адрес, совпадающий с адресом массива. Ниже приведен небольшой пример программы с использованием индексированных переменных. В ней также указано два случая невозможности использования индексированных переменных и варианты замены неработающего оператора.
'---------------------------------------------------
' Использования массивов индексированные переменных
'---------------------------------------------------
Dim Ni As Const 5 |
'объем массива |
Dim Nm As Byte , Jm As Byte , Jmd As Byte , Tmp As Byte |
|
Dim Stmp As String * 8 |
'массив |
Dim Ar(ni) As Byte |
|
Dim Sa(ni) As String * 8 |
'строковый массив |
For Nm = 1 To Ni |
'заполнить массив |
Ar(nm) = Lookup(nm , Da) |
|
' Sa(nm) = Lookupstr(nm , Ds) 'это работает только с первым элементом
Stmp = Lookupstr(nm , Ds) : Sa(nm) = Stmp 'поэтому нужно через буфер Next
'сортировка методом "пузырька" - наибольшее (или наименьшее) всплывает
For Nm = 2 To Ni |
'начинаем сортировку чисел с начала |
|
|
For Jm = Ni Downto Nm |
'проверяя пары соседних чисел |
' |
Jmd = Jm - 1 |
'формируем второй указатель |
If Ar(jmd) < Ar(jm) Then |
'в порядке убывания значений |
|
|
If Ar(jmd) > Ar(jm) Then |
'в порядке возрастания значений |
' |
Tmp = Ar(jmd) : Ar(jmd) = Ar(jm) : Ar(jm) = Tmp 'поменять местами |
|
Swap Ar(jmd) , Ar(jm) |
'с индексированными переменными не работает! |
|
|
End If |
|
|
Next |
|
Next
'посмотрим, что получилось
For Nm = 1 To Ni : Print Sa(nm) ; Ar(nm) : Next End
'байтовые числовые значения, записываемые в массив для сортировки
Da:
Data 0 , 12 , 222 , 45 , 6 , 78 , 4 , 5 , 99 , 45 , 146 , 100 'строковые значения, записываемые в массив
Ds:
Data "1=" , "2=" , "3=" , "4=" , "5=" , "6=" , "7=" , "8=" , "9=" , "10="
============================================================================= 27-1
===================================== Справочник по программированию «Bascom-8051» ==
28. Программирование EEPROM AT89S82252
Микроконтроллер модели AT89S8252 содержит электрически перепрограммируемую память EEPROM (емкостью 2 Кбайта), которая доступна для записи и считывания средствами программы пользователя. EEPROM представляет собой энергонезависимую память, обеспечивающую хранение данных неограниченное время и при выключенном питании. Bascom имеет два оператора (WRITEEEPROM и READEEPROM), дающих возможность простого доступа к этой памяти. Эти операторы позволяют сохранять и воспроизводить значения переменных всех типов за исключением индексированных (элементов массивов). Каждый из операторов может использоваться в двух вариантах:
а) без указания адреса расположения записываемой или считываемой переменной в EEPROM. При компиляции не указанный адрес вычисляется автоматически и подставляется соответственно применяемой переменной, т.е. каждой сохраняемой переменной будет соответствовать только одна ячейка EEPROM (того же размера). Это удобно, когда достаточно обеспечить простое сохранение каких-то значений, размещенных в регистрах, а затем произвести их восстановление;
б) с указанием адреса расположения данных в EEPROM. Этот вариант обеспечивает большую гибкость и лучше подходит для сложных задач.
Ниже приведены два примера программ с использованием операторов записи и чтения данных EEPROM. В первом примере запись и чтение выполняется без указания адреса. Компилятор занимает ячейки EEPROM в порядке применения операторов записи. Расположение данных в EEPROM после компиляции всегда можно посмотреть в файле сообщений, но программа пользователя, естественно, этого сделать не может.
'--------------------------------------------
' пример простого доступа к EEPROM AT89S8252 '--------------------------------------------
Dim Byt As Byte , Wrd As Word , Intg As Integer , Lng As Long , Sng As Single 'инициализируем переменные всех типов. С индексированными не работает
Byt = 10 : Wrd = 2000 : Intg = -10000 : Lng = 10000000 : Sng = 3.3333 'запишем их значения в EEPROM без указания адреса
Writeeeprom Byt : Writeeeprom Wrd : Writeeeprom Intg Writeeeprom Lng : Writeeeprom Sng
'очистим переменные
Byt = 0 : Wrd = 0 : Intg = 0 : Lng = 0 : Sng = 0 'считаем записанные значения также без указания адреса
Readeeprom Byt : Readeeprom Wrd : Readeeprom Intg Readeeprom Lng : Readeeprom Sng
'посмотрим, что получилось
Print Byt ; "_" ; Wrd ; "_" ; Intg ; "_" ; Lng ; "_" ; Sng End
В другом примере операторы WRITEEEPROM и READEEPROM применяются с указанием адреса расположения данных, что позволяет производить запись или чтение любой необходимой области EEPROM, благодаря чему, становится возможным произвольный порядок доступа к памяти.
Теперь рассмотрим вопрос обеспечения достоверности сохраняемых данных. Известно, что при выключении питания микроконтроллера существует короткий момент (несколько миллисекунд), когда происходит сбой текущей программы, но способность исполнять команды, записанные в ПЗУ еще сохраняется. В результате появляется возможность повреждения записанных данных, если при сбое происходит попадание на участок программы, выполняющей запись в EEPROM. Для повышения надежности хранения данных применяют аппаратные средства в виде устройств быстрого сброса или выключения питания при сбоях электропитания. Кроме этого, важными составляющими системы защиты данных остаются и программные средства, первое из которых - контроль исправности блока записанных данных. В приведенном примере содержатся элементы такой защиты:
а) введена дополнительная ячейка для хранения контрольной суммы блока данных. Причем данные, записываемые в регистр контрольной суммы, лишь дополняют ее, до одного и того же значения - 5AH («нормальной контрольной суммы»);
б) после каждой операции записи производится балансировка контрольной суммы блока данных до нормального значения;
в) перед считыванием данных (или после включения питания) производится проверка контрольной суммы блока данных, на основании которой принимается решение о дальнейших действий.
'---------------------------------------------------------
' использование EEPROM AT89C8252 как калибровочной памяти
'---------------------------------------------------------
Dim K0 As Const 0 'блок из 8-ми констант
============================================================================= 28-1
===================================== Справочник по программированию «Bascom-8051» ==
Dim K1 As Const |
4 |
'с плавающей точкой |
|
Dim K2 As Const |
8 |
'определим их |
|
Dim K3 As Const |
12 |
'расположение в EEPROM |
|
Dim K4 |
As Const |
16 |
'каждая константа занимает |
Dim K5 |
As Const |
20 |
'по 4 байта |
Dim K6 |
As Const |
24 |
|
Dim K7 |
As Const |
28 |
'последний адрес блока констант |
Dim End_dae As Const 31 |
|||
Dim Csum As Const 32 |
'регистр контрольной суммы |
||
Dim End_eep As Const 32 |
'последний адрес всего блока |
||
Dim N_cs As Const A5 |
'нормальное значение контрольной суммы |
||
'необходимые переменные
Dim Nm As Byte , Tmp As Byte , Sum As Byte , Sng As Single 'запишем блок данных в EEPROM иэ таблицы
For Nm = |
0 To 7 |
|
|
Sng = Lookup(nm , Dvs) : Tmp = Lookup(nm , Dva) 'данные и адрес |
|||
Writeeeprom Sng , Tmp |
|
|
|
Next |
|
|
|
'вычислим контрольную сумму блока констант |
|
||
Sum = 0 |
|
'суммируем значение каждого байта блока данных |
|
For Nm = 0 To End_dae |
|||
Readeeprom Tmp , Nm : Sum = Sum + Tmp |
|
||
Next |
Sum = N_cs - Sum |
'вычислим дополнение до нормального значения |
|
Writeeeprom Sum , Csum |
'запишем дополнение |
||
'перед считыванием данных проверим исправность всего записанного блока |
|||
Sum = 0 |
|
'вычислим контрольную сумму всего блока |
|
For Nm = 0 To End_eep |
|
|
|
Readeeprom Tmp , Nm : Sum = Sum + Tmp |
|
||
Next |
|
'проверим: совпадает с нормальным значением? |
|
If Sum <> N_cs Then |
|||
Print "Error EEPROM!" |
'если нет, как-то проиндицируем |
||
End If |
|
|
|
'посмотрим, что получилось |
|
|
|
For Nm = 0 To 7 |
|
|
|
Tmp = Lookup(nm , Dva) : Readeeprom Sng , Tmp |
|||
Print Tmp ; "-" ; Nm ; "-" ; Sng |
|
||
Next |
|
|
|
'таблица адресов констант |
|
|
|
Dva: |
|
K4 , K5 , K6 , K7 'так компилятор не понимает |
|
'Data K0 , K1 , K2 , K3 , |
|||
Data 0 , 4 , 8 , 12 , 16 , 20 , 24 , 28 |
'нужно записывать только цифры |
||
'таблица инициализируемых значений |
|
||
Dvs:
Data 1.1! , 1.2! , 1.3! , 1.4! , 1.5! , 1.6! , 1.7! , 1.8!
Необходимость применения средств защиты сохраняемых данных бесспорна, т.к. вероятность повреждения одного байта EEPROM без принятия аппаратных средств достаточно велика и составляет около 0.000001 (в сумме - 0.002 для EEPROM емкостью 2 Кбайта). Применение аппаратных средств снижает вероятность сбоев на три-четыре порядка. Однако, они не исключаются совсем, например, при выключении питания в момент записи. Для защиты от сбоев следует применять и программные средства защиты данных. Очень эффективным средством защиты данных является применение двух дублирующих блоков данных. Использование больше двух дублирующих блоков не имеет практического смысла, т.к. вероятность выхода из строя микросхемы с записанными данными выше, чем у продублированных данных (далее нужно дублировать микросхемы памяти). Двойное дублирование блоков данных широко применяется в измерительной технике для хранения калибровочных коэффициентов, и показало исключительную надежность. Перечислим основные моменты организации дублированной энергонезависимой памяти:
а) оба блока должны быть одинаковыми и содержать собственные регистры контрольной суммы; б) запись данных в блоки производится последовательно - вначале данные и контрольная сумма в один
блок, а потом другой. Причем, после записи первого блока делается пауза, на время не меньше чем время разряда конденсаторов в цепи питания микроконтроллера. Последнее исключает повреждение обоих блоком при выключении питания в момент их модификации;
в) при считывании данные берутся из блока с правильной контрольной суммой; г) при необходимости неисправный блок может быть восстановлен.
============================================================================= 28-2
===================================== Справочник по программированию «Bascom-8051» ==
И последнее, для устройств с повышенными требованиями к надежности хранения данных следует применять внешнюю энергонезависимую память, как и при использовании микроконтроллеров других типов (не имеющих EEPROM). Наиболее удобны для этого микросхемы с последовательным доступом серий 24Cxx или 93Cxx, имеющие соответственно I2C- и SPI-интерфейсы, средства для работы с которыми, в Bascom также доступны.
============================================================================= 28-3
===================================== Справочник по программированию «Bascom-8051» ==
29. Стиль надежного программирования с помощью Bascom
При программировании рекомендуется использовать простые и надежные программные конструкции, т.к. в сложных конструкциях, при экзотическом сочетании типов переменных, имеется большая вероятность совершить собственную ошибку или получить ошибку при компиляции. Последнее обусловлено возможностью ошибок в редко применяемых, а следовательно и мало проверенных операторах или сочетаниях параметров. Кроме того, работу часто употребляемых операторов легче представлять и проще не допускать ошибок.
Всегда нужно помнить, что отсутствие ошибок при компиляции не является признаком безошибочной и работоспособной программы. Это лишь признак отсутствия грубых ошибок. Безошибочная компиляция всего лишь означает:
а) все операторы записаны правильно и содержат все необходимые параметры; б) все используемые переменные определены; в) найдены все метки, на которые имеются ссылки; г) согласованно применены парные операторы.
В скомпилированной программе могут оставаться различные ошибки программиста. Перечислим основные:
а) логические ошибки, неправильное представление о взаимодействии частей программы, в результате, наличие временных или аппаратные конфликтов периферии и т.п.;
б) механические ошибки - пропуск операторов или наличие лишних операторов; б) неверная запись числового значения, имени (номера) линии порта;
в) неверное определение модели процессора или других параметров настройки компилятора; г) неправильная инициализация системы прерывания; д) несбалансированность стека;
е) отсутствие инициализации переменных при запуске программы, обеспечивающей нормальное вхождение в стационарный режим работы;
ж) неправильное понимание или применение операторов Bascom. Могут также иметь место ошибки компилятора:
а) внутренние ошибки библиотеки компилятора, причем ошибки не явные, а обусловленные неблагоприятным сочетанием каких-то параметров или операторов. Однако, считать, что неправильная работа программы, есть следствие неверной компиляции нужно в последнюю очередь;
б) применения оператора с параметрами (переменными), не соответствующими размерности результата. Компилятор часто не определяет это как ошибку. Наиболее фатальный характер для работы программы имеет превышение длины переменных. В некоторых случаях неверная строка текста программы вообще не компилируется без всякого сообщения;
в) неудобная (или неудачная) реализация некоторых операторов Bascom, не позволяющая выполнять одни и те же операции в основной программе и в прерывании. Имеются в виду операторы, использующие регистры, не сохраняемые в прерываниях.
Стиль программирования программы средствами Bascom практически полностью соответствует программированию на ассемблере, т.е. все исполняется строго в порядке записи операторов. Внутреннее содержание библиотек Bascom также соответствует привычной организации программ, написанных на ассемблере. В результате, получаемый скомпилированный код по содержанию, практически, такой же, как при программировании на ассемблере. При тщательной оптимизации программы, полученные с помощью Bascom, имеют размер и время исполнения близкое к ассемблерной. Имеющаяся при этом избыточность обусловлена тем, что компилятор Bascom почти все операции выполняет в своих внутренних регистрах и занимает дополнительные ресурсы на их загрузку и выгрузку.
Чтобы добиться более компактного кода, необходимо принимать ряд мер, многие из которых применимы и к программам, написанным на любом языке программирования. Перечислим эти меры:
а) повторяющиеся участки программы необходимо оформлять в виде подпрограмм. Похожие подпрограммы также стараться объединять, реализуя различия между ними созданием разных точек входа или выхода;
б) сокращать количество используемых в программе операторов и функций. Это уменьшит размер библиотеки (набора подпрограмм), подключаемой к исполняемому коду;
в) сокращать количество и длину текстовых сообщений; г) стараться не применять операции сравнения текстовых строк. Лучше построить программу на других
принципах управления алгоритмом;
д) применять табличные методы считывания и загрузки с помощью операторов LOOKUP и LOOKUPSTR; е) применять табличные методы передачи управления, действующих в зависимости от значения параметра, с помощью операторов выбора SELECT CASE x или ON x GOTO (GOSUB), и меньше использовать операторы
проверки условий (IF … THEN …);
ж) применять ассемблерные вставки там, где они обеспечивают очевидный выигрыш;
============================================================================= 29-1
===================================== Справочник по программированию «Bascom-8051» ==
и) использовать в сложных программных конструкциях (циклах и проверки условий) короткие переменные (одно- и двухбайтные);
к) применять возможно более простые программные конструкции. Лучше две простых вместо одной сложной.
Количество используемых в программе переменных также входит в понятие стиля надежного программирования. Всегда нужно стремиться к их сокращению потому, что когда переменных становится слишком много, то, все равно, приходится идти на усложнение программы для экономии пространства оперативной памяти. Очень хорошие результаты дает прием использования для хранения промежуточных результатов специальных переменных (локальных). Во всех примерах, показанных здесь, этот прием используется. Bascom предлагает средства определения и стирания (оператором ERASE) временных (локальных) переменных. Однако этой возможностью лучше не пользоваться, т.к. эффективность ее невысока. Компилятор устанавливает указатель стека сразу на максимальное значение, освобождая место и тем переменным, которые будут определены позже. После уничтожения временных переменных указатель стека не возвращается компилятором ниже (да и не имеет права этого делать). Таким образом, делать переопределение переменных в программе больше одного-двух раз практически не удастся. Единственная польза от такого переопределения переменных состоит только в возможности изменения распределения зарезервированного пространства между переменными разного типа.
Минимальное время исполнения программы и объем полученного кода совершенно мало зависит от краткости записи операций. В большей степени это функция размерности используемых переменных и сложности совершаемых действий. Bascom позволяет достаточно широко варьировать операторами для достижения одного и того же результата. Этим обязательно нужно пользоваться, оптимизируя программу по времени исполнения или размеру исполняемого кода.
Структура программы должна базироваться на функционально законченных блоках или модулях, неважно как оформленных (процедурах, программах и подпрограммах) и содержащих не более 50 операторов. Каждый блок или функциональный модуль должен выполнять одно сложное действие, и быть, легко обозримым, быстро понимаемым и хорошо откомментированным. Функциональный блок должен использовать и передавать для дальнейшей обработки минимальное количество данных, в как можно более компактном и стандартном виде. Собственно говоря, место в программе, где данные имеют осмысленный вид или наиболее удобны для передачи, и является естественным местом разделения на функциональные модули. Еще один признак функционального блока - один вход и один выход. Для управления работой программы (изменения естественного хода выполнения операторов) достаточно и следует использовать только семь конструкций:
а) переход к метке (оператор GOTO);
б) вызов процедуры или подпрограммы (операторы CALL, GOSUB);
в) неполную альтернативу (конструкция IF … THEN … ) с пропуском участка программа и полную - с выбором одного из двух действий (IF … THEN … ELSE / IFELSE … );
г) множественное ветвление (с помощью конструкций SELECT CASE … или ON … GOTO или ON … GOSUB);
д) цикл «ПОКА» (конструкция WHILE … WEND);
е) цикл «ДО» (конструкция DO … LOOP UNTIL …);
ж) цикл с изменяемым параметром (FOR … TO/DOWNTO … NEXT).
Функционально структурированной программа выполняется не для улучшения ее работы или оптимизации производительности – это, в большей степени, метод организации и оптимизации процесса программирования. Ведь очевидно, что если убрать комментарии и лишние команды CALL и RET, структурированная программа будет мало отличаться от неструктурированной. Модули структурированной программы впоследствии могут использоваться в других программы неоднократно, в готовом виде или с небольшими доработками. Метод структурного программирования сокращает сроки получения программного продукта за счет накопление программного задела, обеспечивает создание больших и очень больших программ с разделением задач между несколькими исполнителями. Таким образом, применение метода структурного программирования положительно сказывается на качестве и надежности создаваемых программ, т.к. дает возможность быстрее выявлять ошибки и использовать лучшие готовые решения.
Структурирование программы это и метод программирования и способ форматирования текста программы. При создании функционального программного элемента добиваются его независимости и законченности, а при оформлении текста программы используют приемы разделения функциональных элементов, например, с помощью отступов строк от левого края листа. Применяются и элементы вертикального разделения. Несмотря на то, что Бейсик допускает записывать несколько операторов в одной строке, операторы разных функциональных элементов нужно располагать в отдельных строках.
============================================================================= 29-2
===================================== Справочник по программированию «Bascom-8051» ==
30. Отладка программ Bascom
Способ отладки программ Bascom, в принципе, мало отличается от методов отладки программ, написанных другим способом. Чтобы отладка программы быля успешной и эффективной, нужно на этапе проектирования устройства с микроконтроллером заложить в его схему некоторую избыточность, которая позволит потом осуществить этот процесс. В число мер, обеспечивающих процесс отладки, входят:
а) возможность электрического подключения эмулятора; б) программная совместимость применяемой модели процессора с имеющимеся симулятором или
эмулятором; в) соместимость файлов данных, выдаваемых компилятором, и понимаемых средствами программной или
аппаратной отладки; г) наличие в схеме проектируемого устройства элементов для внутрисхемного программирования
микроконтроллеров с корпусах, которые могут только впаиваться (SOIC, TQFP и т.п.).
Можно рекомендовать определеный порядок отладки программ, обусловленный особенностями Bascom. Отладка может и должна начинаться после написания первых строк программы. Bascom дает такую возможность, генерируя работоспособный код, практически, для любых логически незавершенных фрагментов. Рекомендуемый порядок написания и отладки программы выглядит следующим образом:
а) вначале пишутся и отлаживаются отдельные самые важные, для разрабатываемой программы, фрагменты функциональной обработки данных. Это относится к вычислительным программам или программам обработки данных, использующих значительные ресурсы. Для отладки этих программ лучше использовать построчный отладчик Bascom. При отладке в текст можно и нужно встраивать временные операторы задания начальных условий и вывода промежуточных результатов, пробовать применять различные варианты реализации алгоритма работы. На этом этапе лучше всего применять дисассемблирование полученного кода для определения оптимальности работы отдельных операторов или функций, и объема, используемых ими регистров. Последнее особенно важно при программировании, например, прерываний. После достижения желаемых результатов анализируется объем использованных ресурсов памяти, время исполнения (отладчик показывает его) и принимается решение об использовании отлаженного фрагмента в основной программе;
б) построить скелет программы, содержащий конфигурирование всех внутренних и внешних устройств процессора, системы прерывания, систем ввода-вывода и отображения данных. Функциональное наполнение программы на этом этапе должно быть минимальным. Используя аппаратный отладчик или эмулятор (в программном симуляторе это вряд ли удастся), оживите скелет программы. Должны заработать все таймеры и прерывания, которые в дальнейшем планируется использовать. Подключение аппаратных ресурсов, по мере отладки программы, также может быть поэтапным. Для облегчения отладки на данном этапе, временно добавляйте в программу операторы задания начальных условий и выдачи каких-либо сигналов (синхронизации или “маркеров”), сопровождающих работу аппаратных устройств. При необходимости в программу также временно добавляются операторы, обеспечивающие работу отладочного процессора (если он отличается от рабочего). Только убедившись, что система работает нормально и все подключенные к процессору устройства функционируют так, как требуется, приступайте к следующему этапу;
в) постепенно начинайте заполнять скелет программы функциональными модулями, отлаженными на первом этапе, и другими, не столь принципиальными. Проверка работы программы должна проводится каждый раз при введении в действие новой функции, чтобы ошибки не накапливались и можно было точнее определить момент появления ошибки. Первыми вводятся и отлаживаются программные модули, взаимодействующие с наибольшим количеством других программных модулей (или наиболее используемые), например, индикации, клавиатуры, последовательного интерфейса, интерфейсов АЦП и ЦАП. На данном этапе очень важно так, организовать работу и строить структуру программы, чтобы к отлаженным частям программы уже не возвращаться (это тоже элемент структурного программирования). Данный этап разработки программы можно считать законченным, когда все устройства функционируют правильно и обеспечивается работа конвейра передачи данных;
г) следующий этап отладки - добавление в программу операций глубокой обработки данных, например, цифровой калибровки, сохранение и восстановление данных в EEPROM. Чаще всего, именно на данном этапе обнаруживается, что внутренние ресурсы процессора исчерпаны и необходимо принимать меры по более рациональному их использованию;
д) последними отлаживаются (добавляются к отлаживаемой программе) программные модули, обеспечивающие сервисные функции (дополнительная обработка, дополнительный интерфейс, утилиты тестирования и настройки аналоговой части и т.п.).
Полностью отлаженная программа программируется в память процессора и проверяется уже реальном устройстве. Этот этап отладки позволяет выявить ошибки, обусловленнные неполным представлением о работе устройства в целом, имеющимся в начале работы. Особое внимание при проверке следует уделить поведения программы при неправильных воздействиях и возможных неисправностях:
а) на клавиатуру – при нажатии неправильных комбинаций кнопок, одновременном нажатии кнопок, длительном удержании нажатой кнопки, ввод нереальных значений и режимов;
============================================================================= 30-1
===================================== Справочник по программированию «Bascom-8051» ==
б) на последовательный интерфейс (или другой) – при подаче неправильных или искаженных команд, загрузке неправильных данных, подаче слишком длинных сообщений, подаче команд с малым интервалом;
в) на аналоговую часть устройства, управляемого процессором, - при подаче сигналов перегрузки, возникновении неисправности, разрыве обратной связи;
г) при порче данных в энергонезависимой памяти; д) при нарушении протокола связи с другим процессором, при полном пропадании связи;
е) при возникновении аварийных ситуаций (перегрев, переполнение, отключение питания).
В результате практического тестирования определяются меры по улучшению работы программы (переназначение функций кнопок, коррекция текста сообщений, изменение формата выводимых данных, учет реальных временных соотношений, приспособление к форме имеющихся сигналов, учет динамических свойств регулируемых объектов). Все выявленные ошибки и упущения (уточнения) обязательно протоколируются (просто записываются в виде списка с указанием условий возникновения). Это необходимо ввиду обычного временного разрыва между операциями проверки (обнаружения ошибки) и коррекцией программы. После исправления программы тестируется снова, и так до тех пор, пока не будут достигнуты необходимые качества и надежность работы.
При отладке программы все временные вставки обязательно выделяйте в тексте программы, чтобы не забыть убрать их в окончательной версии. После завершения отладки необходимо еще раз просмотреть текст программы, убрать все лишнее (метки, отладочные вставки, неудачные варианты операций) и дописать недостающие комментарии, Законченная программа сопровождается необходимыми атрибутами (номером версии) и архивируется (в том числе на резервном носителе).
Теперь рассмотрим основные приемы, которые рекомендуется применять при отладки программ Bascom: а) применение циклической конструкции для проверки операции преобразования при множественном
значении исходных данных. Данная конструкция удобна при отладке функциональных модулей.
DO
INPUT “Input value A” , _A INPUT “Input value B” , _B
Это место отлаживаемой или проверяемой программы, использующей введенные значения
PRINT “C=” ; _C ‘посмотреть результат
LOOP
б) применение цикла для отладки программы, воспроизводящей данные или сигналы, например, АЦП
DO
Это место отлаживаемой или проверяемой программы PRINT “C=” ; _C ‘печать результата операции
‘или, если имеется индикатор применять следующие строки
CLS |
'очистить LCD |
LCD C |
‘вывести результат |
LOOP
в) применение цикла с переменным параметром для отладки программы управления объектом, например,
ЦАП.
DO
FOR V = 10 TO 100
Это место отлаживаемой или проверяемой программы, зависимой от V NEXT
LOOP
г) установка аппаратного маркера для синхронизации осциллографа, применяемого для наблюдения за отлаживаемым процессом или формированием временной диаграммы.
RESET P1.1 : SET P1.1 ‘формируем импульс синхронизации (запуска осциллографа) Это место отлаживаемой или проверяемой программы, формирующей временной процесс
д) выключение или обход не нужных в данный момент участков программы. Для этого перед компиляцией ненужные участки делаются комментарием (символом “ ‘ “) или обходятся оператором GOTO
метка;
е) запуск программы естественным путем (с нулевого адреса) применяется, когда система уже построена и работает. Далее отлаживаются вновь добавленные фрагменты. После того, когда очередной фрагмент полностью проверен и выполняет необходимые действия, добавляется новый. Это самый удобный способ отладки программ потому, что ориентирован на получение конечного результата. Он не требует глубокого “копания” во внутренних регистрах процессора, а проверка работы программы ведется в условиях реального времени;
ж) иммитация внешних событий установкой признаков этих событий и подстановкой соответствующих данных. Это программа воспринимает как появление настоящих данных и начинает процесс обработки, в составе которого и участвуют отлаживаемые фрагменты.
============================================================================= 30-2
===================================== Справочник по программированию «Bascom-8051» ==
SET B_NEW_DATA : ADC_DATA = 4000 ‘так имитируем появление данных АЦП
и) использования программы эмулятора терминала для отладки программ, взаимодействующих через COM-порт. Выбор текстового протокола связи с другими участниками интерфейса значительно облегчают задачу отладки. Для этого, достаточно обеспечить лишь аппаратное подключение отлаживаемого процессора с персональным компьютером. Легко отлаживать программы, действующие через последовательный порт, используя и симулятор-отладчик Bascom, имеющий встроенную функцию и программного, и аппаратного эмуляторов терминала. Если еще говорить о полезных функциях отладчика Bascom, нужно упомянуть о наличии программных эмуляторов символьного дисплея и EEPROM (модели AT89S8252), которые также существенно расширяют возможности отладки.
Относительно использования аппаратных средств (эмуляторов) для отладки программ, созданных с помощью Bascom, можно сказать следующее:
а) отсутствуют ограничения на использование выходных файлов (HEX- и BIN-форматов) Bascom в эмуляторах других типов (не упомянутых в списке, поддерживаемых средой Bascom);
б) структура текста программы Bascom такова, что самым рациональным является режим отладки путем запуска программы с нулевого адреса. Это позволяет использовать даже самые простые эмуляторы;
в) сложные эмуляторы, обеспечивающие просмотр содержимого внутренних регистров при отладке программ Bascom, напротив, неэффективны и избыточны, так как, все равно, не обеспечивают режим построчной отладки, ассемблерный листинг Bascom не выдает. К тому же, они обладают массой недостатков, главные из которых, невозможность работы на предельной тактовой частоте, небезупречная поддержка аппаратных функций процессора, генерация помех на линиях эмулируемых портов в пошаговом режиме, невозможность полноценной отладки программ для процессоров старших моделей, высокая стоимость и низкая надежность;
г) для решения различных задач требуются и различные типы эмулятором, отличающиеся как по типу поддерживаемых процессоров, так и по составу используемых ресурсов. В одних случаях удобным оказывается наличие прямой связи портов TxD и RxD отлаживаемого процессора с COM-портом компьютера, в других случаях необходимо предоставить все ресурсы отлаживаемой программе или обеспечить возможность выполнения шинных операций (для сообщения с внешней памятью);
д) вследствие того, что Bascom обеспечивает быстрое получение работающей программы, то он очень удобен при создании программ для самых современных моделей процессоров, выполняемых в плоских корпусах, присоединяемых только пайкой, имеющих внутреннюю Flash-память (электрически перепрограммируемую) и режимы внутрисхемной загрузки (по последовательному интерфейсу). Применять традиционные эмуляторы в таких случаях неудобно и их может заменить программирующий адаптер (трех- , четырех или пятипроводный). Однако, вследствие того, что процесс перепрограммирования небыстрый (занимает 15 - 30 секунд), то для отладки таких программ рекомендуется применять комплексный подход. При этом, вначале необходимо применять отладку независимых программных модулей другими способами и только на завершающей этапе переходить к отладке в процессоре в составе объекта. Схема устройства (повторяем еще), в которой используется перепрограммирование впаянного процессора, также должна быть приспособлена для этого.
============================================================================= 30-3
===================================== Справочник по программированию «Bascom-8051» ==
31.Ошибки и неприятности Bascom
1Директивы $BAUD и $CRYSTAL активизируется только при использовании команд PRINT, INPUT. Внимание! Если в тексте программы не применяются операторы PRINT, INPUT, то инициализация не выполняется - в таймер не загружается значение коэффициента деления, не инициализируются регистры SCON и PCON (см. соотв. раздел).
2Внутри программной конструкции IF .. End If не допускается использование оператора Return:
Правильно: |
Неправильно: |
If Tmp = 0 Then |
If Tmp = 0 Then |
Goto Lcd_pe |
Return |
End If |
End If |
… |
… |
Lcd_pe: Return |
|
При наличии записи Return происходит нарушение баланса стека и программа зависает. Компилятор сообщений о такой ошибке не дает.
3 Функция Fusing работает правильно, если размер буфера, в который помещается отформатированное число (в формате с плавающей точкой), несколько больше, чем число знаков результата. По крайней мере, требуется длина буфера равная сумме: максимальное количество чисел целой части + максимальное количество чисел дробной части + десятичная точка + знак полярности (минус для отрицательных чисел). В примерах Bascom не зря рекомендуется применять буфер (символьную переменную) длиной 16 байт. В других случаях суммирования двух символьных переменных нужно использовать буфер длиной не менее суммы максимальной длины первой переменной + длина буфера, содержащего вторую переменную. Может быть это и не так – проверьте сами!
4 При использовании функции Fusing нужно так строить программу, чтобы не приходилось выводить числа, которые не могут быть представлены в назначенном формате. Лучше если они будут не больше и не меньше чем нужно. Проверяйте, что будете преобразовывать и, как получается во всем диапазоне преобразуемых чисел. Применяйте директиву $NONAN.
5 Другая неприятность функции Fusing заключается в том, что при отрицательной полярности преобразуемого числа знак минус входит в результат форматирования, таким образом, одно и тоже значение положительной и отрицательной полярности выглядит по разному. Существует два способа борьбы с этим явлением: либо делать все числа на входе в преобразование положительными, а знак обрабатывать отдельно, или применять различные форматы для чисел разной полярности (для отрицательных чисел на один знак слева больше).
6 Еще одна неприятность функции Fusing связана с тем, что лишние нули слева отбрасываются (вместо значения “003.45” при маске “###.##” мы получаем “3.45”). Бороться с этим можно только, контролируя длину результата и дописывая недостающие нули. В обновленной версии (1.0.0.19 и старше) этот недостаток исправлен введением дополнительных лидирующих нулей. Добавлена функция округления.
7 При сборке символьной переменной из нескольких частей возможен случай, когда, принимающая данные переменная, будет перекрыта по длине и следующая за ней переменная будет испорчена. Это происходит потому, что программа копирует не содержимое переменной источника, которое не столь велико, как вы рассчитываете, а всю переменную, такой длины, как она была объявлена. Бороться с этим можно только увеличением длины переменной, принимающей символьные данные. Необходимую степень запаса по длине можно определить, наблюдая в отладчике за количеством испорченных ячеек. При этом, нужно иметь в виду, что при выводе длина символьного значения числовой переменной может меняться в значительных пределах (зависит от значения преобразуемого числа). Также нужно учитывать, что длина внутреннего буфера Bascom, в котором формируется символьное значение числа, может достигать 16 при выводе значения переменной типа Single. Если за принимающей данные переменной располагается стек, то неизбежен фатальный результат этой ошибки компилятора.
8 Длина строки исходного текста программы не должна превышать 255 символов, а реально получается даже меньше. Попытки превзойти ее приводят к фатальному сбою компилятора. Более того, это не дает возможность записать в одну строку некоторые необходимые вещи. Например, вывод текстового сообщения без символов ВК ПС:
============================================================================= 31-1
===================================== Справочник по программированию «Bascom-8051» ==
Printbin &H42 ; &H41 ; &H42 ; &H43 ; &H44 ; &H45 ; &H46 … ; &H4A ; &H4B
Или формирование длинной строковой переменной:
Out_buf = "S" + Str(rang) + "L" +Str(udac) + "T" … + Chr(13) + Chr(10)
9 Выполняя операцию вычисления (очевидно, какие-то другие тоже), можно назначить в качестве приемника данных любой регистр (любую переменную). Однако если длина приемника не совпадает с длиной результата, могут возникнуть неприятности. Например, операция, дающая двухбайтовый результат, будучи записана в переменную типа Long, модифицирует только два младший байта, а старшие разряды останутся без изменения. Ниже приведено два примера, дающие различные результаты. В первом примере имеется очевидная ошибка, которую компилятор не обнаруживает, и, наверно, не должен обнаруживать.
Dim Byt As Byte, Byt1 As Byte, Wrd As |
Word , Wrd1 As Word , Lng As Long |
||
Byt = 10 |
: Byt1 = 15 : Wrd = &H1234 |
'назначим числа |
|
Wrd = Byt * Byt1 |
'произведение запишем в двухбайтное число |
||
Printhex Wrd |
'результат Wrd = |
1296h - только в младших разрядах |
|
Wrd = 16 |
: Wrd1 = 32 : Lng = &h12345678 |
||
Lng = Wrd * Wrd1 |
'произведение запишем в четырехбайтное число |
||
Printhex Lng |
'результат Lng = |
12340200h – снова в младших разрядах |
|
Во втором примере получаем правильный результат за счет приема результата операции в регистр, длина которого соответствует длине выдаваемых данных, и добавлении операции преобразования переменных.
Byt = 10 : Byt1 = 15 : Wrd = &H1234 |
'назначим числа |
|
Byt = Byt * Byt1 |
'произведение запишем в однобайтное число |
|
Wrd = Byt : Printhex |
Wrd 'результат Wrd = 0096h - правильный |
|
Wrd = 16 : Wrd1 = 32 : Lng = &h12345678 |
||
Wrd = Wrd * Wrd1 |
'произведение запишем в двухбайтное число |
|
Printhex Lng |
'результат Lng = 00000200h – снова правильно |
|
Тем не менее, даже это, безусловно, вредное явление можно иногда использовать и с пользой.
10 Длина результата арифметических операций Bascom равна длине операндов (если они одного типа), что очень неудобно при необходимости выполнения операций целочисленного умножения, например, 16 на 16 разрядов, когда желательно получить 32-разрядный результат. Чтобы получить полноценный результат (32 разряда), приходится умножение выполнять в 32-разрядных регистрах (переменных). В итоге, время вычисления произведения увеличивается в десятки раз. Можно применять несколько облегченный вариант – использовать, хотя бы один операнда, равный по длине требуемому результату, как в приведенном ниже примере.
Wrd = 50 : Lng = 2000 'один из операндов возмем четырехбайтным
L ng = Wrd * Lng |
'произведение запишем в четырехбайтное число |
Printhex Lng |
'результат Lng = 100000 |
11 Не все операторы Bascom могут работать с индексированными переменными, определенными как элементы массива. При этом компилятор не выдает никаких сообщений. Собственно говоря, операторы работают, но только с первым элементом, адрес которого совпадает с адресом массива. Чтобы обеспечить возможность работы с любым элементом массива следует применять промежуточный буфер, куда выгружать данные из массива перед обработкой или помещать после обработки и перед записью в массив. Например, оператор SWAP Ar(1), Ar(3) работает неправильно, поэтому вместо него следует записывать строку из трех операторов: Temp = Ar(1) : Ar(1) = Ar(3) : Ar(3) = Temp. Подобное решение, на самом деле, не увеличивает размер получаемого кода, т.к. внутри операторов, работающих с массивами, делают аналогичные пересылки.
12В секции, определеной оператором DATA, константы (числа) и символьные значения адресов (метки) невозможно записать в виде символического имени. Нужно записывать только цифры. Главная неприятность этого заключается в том, что компилятор не дает сообщения об ошибке, а вместо ожидаемых значений записывает нули.
13Имеется ошибка в библиотеке оператора LOOKUP при считывании четырехбайтного числа из таблицы. Как только индекс (смещение по таблице) превышает 63(3FH), начинается проявляться ошибка (неверно вычисляется адрес расположения константы из-за того, что умножение смещения на четыре делается неправильно). В Последних версиях похоже устранено.
============================================================================= 31-2
===================================== Справочник по программированию «Bascom-8051» ==
14 Символ двоеточия (“ : ”) недопустим в комментариях. Если такое случается, компилятор дает сообщения о несуществующих ошибках и начинает искать метки с символами, расположенными в одной строке до знака двоеточия. А также символы ”<”, “>” и ключевые слова, обозначающие некоторые логические операции.
Справедливости ради нужно сказать, что в последних версиях (начиная с 1.20) ошибок стало намного меньше, а фатальных незамечено вообще.
============================================================================= 31-3
===================================== Справочник по программированию «Bascom-8051» ==
32. Некоторые советы по созданию больших программ
Как любой язык высокого уровня, Bascom ориентирован на получение как можно больше действий в исполняемом коде при минимальном объеме исходного текста. И на самом деле, Bascom является подходящим инструментом для создания больших программ. Теперь определим понятие «большая программа». Таковой следует считать программу, использующую большое количество ресурсов (памяти, встроенной и внешней периферии), работающей с большим количеством программных и физических объектов, имеющей несколько независимых состояний или функций и выполняющая множество действий. Большая программа, как правило, отличается размером кода, не помещающегося в память команд стандартных моделей микроконтроллеров (8051 и 8052). Ниже будут приведены советы по созданию оптимального кода «больших программ», оптимального с точки зрения экономии ресурсом микроконтроллера. Эти советы будут не менее полезны и при разработке кода для «маленьких» процессоров, например AT89C2051.
Приблизительный размер кода можно определить по объему исходного текста. Начиная с третьегочетвертого килобайта каждые 50 строк исходного текста (около 70 операторов), не считая пустых строк и строк с метками и комментариями, дают один килобайт исполняемого кода.
Первый совет (о аппаратной части).
1.1Приступая к разработке большой программы необходимо оценить возможность создания такой программы в рамках имеющихся аппаратных ресурсов. Может быть, нужно создать резерв памяти – предусмотреть возможность использования «старшей» модели микроконтроллера с большей программной памятью или дополнительной памятью данных.
1.2Не используйте уникальных моделей микроконтроллеров или их уникальных свойств. Переделка большой программы под другой процессор никому не нужна.
1.3Для формирования временных интервалов максимально применяйте таймер, а не время исполнения операторов (команд) конкретным микроконтроллером в данном проекте. В противном случае вы получите программу, которую без переделки нельзя будет использовать в другом проекте.
1.4Подумайте как будете отлаживать – без эмулятора не обойтись. Может быть схема не позволит применить имеющийся эмулятор – лучше изменить схему.
Второй совет (о экономии времени). Оцените, сколько времени понадобится на программирование и отладку программу, может быть, вы им не обладаете. Когда объем исходного текста «вылезет» за 1000 строк (это 6 – 8 кБ кода) работа начнет замедляться. Таким образом средняя скорость разработки программы 1 – 1.5 кБ в месяц может стать реальной. Для ускорения работы программу можно разделить на независимые модули (большие программы делить намного проще) и писать их параллельно.
Третий совет (о заделах). Используйте в большой программе отработанные модули других программ или разрабатываете и отлаживайте их заранее. Разработать большую программу в разумные (или планируемые) сроки без заделов НЕВОЗМОЖНО! Это правило можно сформулировать иначе - если все написанное вами ранее не превышает утроенного объема того, что вы собираетесь написать, то, скорее всего, вы с работой не справитесь.
Четвертый совет (об оформлении).
4.1Пишите как можно проще. Поменьше вычурных конструкций и хитростей. Главное надежность работы, а сложностей и так хватит.
4.2Тщательно и подробно комментируйте программу, а оформляйте аккуратно. Это, в итоге, экономит время. Пишите каждый программный модуль так, чтобы его можно было использовать многократно – последующие программы будут писаться все проще и проще.
4.3Делите исходный текст программы на части как только он достигнет 1000 строк. Так удобней работать
-можно в разных окнах просматривать одну часть, а редактировать другую. При этом не нужно листать текст программы назад-вперед, чтобы что-то в ней посмотреть. Применяйте функциональное деление текста. Например, главная программа со всеми объявлениями и прерываниями + подключаемые программы, каждая отдельная по исполняемой функции (клавиатуры, индикации, измерений, интерфейса и т.п.). Так закончив работу над одной функцией, вы не рискуете ее испортить, например, случайным нажатием кнопки при перелистывании. Этот фрагмент уже не нужно открывать редактором.
4.4Меньше используйте ассемблерные вставки. Они не столь эффективны (для сокращения кода) как в маленьких программах.
4.5Не используйте исключительных особенностей данной версии компилятора. Если какую-то проблему вам удалось преодолеть неординарным способом, то скорее всего, в следующей версии другая проблема возникнет в этом же месте снова.
Пятый совет (когда думать). Перед началом работы тщательно продумайте, что и когда писать. Разработайте структуру программы и план использования ресурсов. Иначе можно долго работать на корзину. Если нет видения общей картины, начинайте с модулей программы, которые наверняка понадобятся (сейчас или, в крайнем случае, когда-нибудь).
Шестой совет (совместимость с AVR). Поменьше используйте уникальных возможностей ядра 8051 и “Bascom-8051”. Не увлекайтесь без необходимости ассемблерными вставками. Это, в итоге, не дает большого выигрыша. Может быть программу придется перенести на AVR-ядро и компилировать “BasAVR”, который совместим на 95 % с “Bascom-8051”, но работает более чем на порядок быстрее.
============================================================================= 32-1
===================================== Справочник по программированию «Bascom-8051» ==
Седьмой совет (о структуре программы).
7.1Чем больше программа, тем лучше она должна быть структурирована.
7.2Каждая функция или действие должно быть оформлено в виде отдельного независимого и полностью автономно функционирующего модуля.
7.3Если части различных модулей одинаковы или выполняют похожие операции, то оформляйте их подпрограммами и используйте как законченные модули.
7.4Строго выполняйте принцип – каждое действие или использование единицы аппаратного ресурса выполняется только в одном месте программы или только одним программным модулем. Только таким образом можно быстро отладить и проверить работу программы. При этом правильное функционирование одного устройства или правильное выполнение операции означает правильную работу программы, отвечающую за это.
7.5Как можно шире используйте табличные методы записи непосредственных данных. Для этого нумеруйте режимы работы, объекты, последовательно выполняемые операции и состояния программы. Вводите для этого специальные переменные, принимающие значение номера всего того, что пронумеровано. Данные о состоянии портов, числовые константы, текстовые сообщения, адреса переходов, знакогенераторы и тому подобное, зависимые от номера режима программы (состояния или т.п.) оформляйте в виде таблиц. В результате ускоряется работа программы и соблюдается принцип единственности описания данных. Очень удобно, что данные, применяемые к одному объекту, сконцентрированы в одном месте и описываются один раз. Ниже приведен типичный пример табличной программы.
'-------------------------------------
'Взять номинальное значение уровня калибровки в Ub
'Ub(Single) – принимает значение, Ranga(Byte) – нумерация шагов калибровки
Get_nom_sc:
Ub = Lookup(ranga , Tab_nom_sc) : Return '-------------------------------------
'таблица номинального значения напряжения на шагах калибровки в вольтах
Tab_nom_sc:
Data 0.0003! |
'0 - предел 0.2mv |
Data 0.0005! |
'1 - предел 0.5mv |
Data 0.001! |
'2 - предел 1mv |
Data 0.003! |
'3 - предел 2mv |
Data 0.005! |
'4 - предел 5mv |
Data 0.01! |
'5 - предел 10mv |
Data 0.03! |
'6 - предел 20mv |
Data 0.05! |
'7 - предел 50mv |
Data 0.1! |
'8 - предел 100mv |
Data 0.3! |
'9 - предел 200mv |
Data 0.5! |
'10 - предел 500mv |
Data 1! |
'11 - предел 1v |
'------------------------------------- |
|
Следующий пример программы организация табличных переходов. Для этого в наибольшей степени подходит оператор ON var GOTO … , … , … . Мы видим, что в зависимости от значения переменной Ranga происходит переход к метке одного из вариантов действий. Главная особенность этой конструкции по сравнению с похожей конструкцией SELECT CASE var … … … , которая также может быть использована для создания таблицы переходов, является возможность получения очень компактного кода. А запись длинных строк, начиная с версии 1.20, перестала быть непреодолимым препятствием. Теперь длинные строки с метками прекрасно переносятся с помощью символа “_”.
'-------------------------------------
'программа записи константы в зависимости от шага калибровки из Flda Wr_c_sc:
|
On Ranga Goto Ws0 , Ws1 , |
Ws2 , |
Ws3 , Ws4 , Ws5 , Ws6 , Ws7 , _ |
||
|
Ws8 , Ws9 , Ws10 , Ws11 , Ws12 , Ws13 , Ws14 |
|
|||
'установить адрес записи и подготовить данные для записи |
|
||||
Ws0: |
Wadr = Varptr(dc__2mv) : Goto Wsend |
'масштаб предела 0.2 мВ |
|||
Ws1: |
Wadr = Varptr(dc__5mv) : Goto Wsend |
'масштаб предела 0.5 мВ |
|||
Ws2: |
Wadr = Varptr(dc_1mv) : Goto Wsend |
'масштаб предела 1 мВ |
|||
Ws3: |
Wadr = Varptr(dc_2mv) : Goto Wsend |
'масштаб предела 2 |
мВ |
||
Ws4: |
Wadr = Varptr(dc_5mv) : Goto Wsend |
'масштаб предела 5 |
мВ |
||
Ws5: |
Wadr = Varptr(dc_10mv) : Goto Wsend |
'масштаб предела 10 |
мВ |
||
Ws6: |
Wadr = Varptr(dc_20mv) : Goto Wsend |
'масштаб предела 20 |
мВ |
||
Ws7: |
Wadr = Varptr(dc_50mv) : Goto Wsend |
'масштаб предела 50 |
мВ |
||
============================================================================= 32-2
===================================== Справочник по программированию «Bascom-8051» ==
Ws8: |
Wadr = Varptr(dc_100mv) : Goto Wsend |
'масштаб предела 100 |
мВ |
|
Ws9: |
Wadr = Varptr(dc_200mv) : Goto Wsend |
'масштаб предела 200 |
мВ |
|
Ws10: |
Wadr = Varptr(dc_500mv) : Goto Wsend |
'масштаб предела |
500 |
мВ |
Ws11: |
Wadr = Varptr(dc_1v) |
'масштаб предела |
1 В |
|
Wsend: Flda = Rscl : Goto Wr_cal_c '-------------------------------------
Другой особенностью приведенного примера является то, что он показывает каким образом записать в таблицу и использовать в программе адрес переменной определенной Bascom. Переменной Wadr передается значение адреса другой переменной с помощью оператора Varptr(). Попытка это же действие записать в более привычном виде (пример ниже) будет неудачной, так как Bascom не может помещать в поля данных значения символьных определений.
'-------------------------------------
' это не работающая программа!!!
Wadr = Lookup(ranga , Tab)
Tab: |
'здесь компилятор записывает 0000h |
Data Dc_2mv% |
|
Data Dc_5mv% |
|
Data Dc_10mv% |
|
Data Dc_20mv% |
|
Data Dc_50mv% |
|
Data Dc_100mv% |
|
Data Dc_200mv% |
|
Data Dc_500mv% |
|
Data Dc_1v% |
|
7.6 Конструкция ON var GOTO … , … , … предпочтительней конструкции SELECT CASE var … … …
(дает более компактный код) даже если вы используйте всего два-три из шестнадцати-двадцати возможных значений тестируемой переменной var. Конструкцию SELECT CASE нужно применять тогда, когда выбор действительно велик или число значений, принимаемое переменной, не ограничено. Ниже дается пример использования конструкции ON … GOTO при обработке кнопок. В тех случаях, когда кнопку обрабатывать не требуется, указан переход на метку-«заглушку» (“Mcz_mes”). Еще раз напомним, в данном примере переменная Buf_kl может иметь значения только от 0 до 16.
'-------------------------------------
'обработка кнопок при калибровке нуля
Comzkl:
On Buf_kl Goto Mcz_mes , Mcz_mes , To_next_step , To_prev_step , End_of_c , _ Mcz_mes , Mcz_mes , Mcz_mes , Mcz_mes , _
Mcz_mes , Mcz_mes , Mcz_mes , Mcz_mes , _ Mcz_mes , Mcz_mes , Mcz_mes , Mcz_mes
'-------------------------------------
7.7 Упаковывайте в подпрограммы все повторяющиеся фрагменты даже если это всего одна операции. В большой программе таких фрагментов бывает особенно много, и поэтому эта мера дает хорошее сокращение размера кода. Например, запись в программе любой арифметической операции в формате с плавающей точкой
Ua = Ub + Uc или Ua = Ua + 10.123
дает около 50 байт кода. Если эту операцию оформить подпрограммой, то каждый ее вызов (не считая описания подпрограммы) будет занимать всего 3 байта. Примеры эффективного использования этого приема есть в разделе, посвященном вычислениям. В вычислительных программах эта мера дает наибольший выигрыш (до двух-трех раз), а в среднем размер всей программы может сократиться на четверть.
Восьмой совет (переменные).
8.1Правильно распределяйте переменные в памяти микроконтроллера. Оцените необходимое их количество. Может быть, понадобится расширить память, Для этого лучше применить модель процессора с дополнительной памятью – сегодня их очень много - это все модели ряда 8xC51Rx с ОЗУ от 512 до 1280 байт.
8.2Если в системе имеется расширенная память, располагайте в ней переменные, используемые реже или те, которые всегда адресуются с помощью индексного регистра (строковые и массивы). Переменные типа Single также можно располагать во внешней памяти – это не отразится на производительности (все равно их обработка занимает много времени). Однако нужно быть внимательным при использовании этих переменных – наверно, имеются операторы не работающие (или неправильно работающие) с данными во внешней памяти. Переменные типа Byte, Word, Integer и Long нужно располагать в основной памяти и ниже 7fh.
Девятый совет (о стеке).
9.1Оставляйте больше места для стека. Большие программы всегда предполагают большую вложенность подпрограмм. Минимальный размер стека 32 байта, а безопасный составляет 48 байт (когда стек используется
============================================================================= 32-3
===================================== Справочник по программированию «Bascom-8051» ==
обычным образом). Если применены прерывания, добавьте еще. Проверяйте после компиляции, сколько осталось после размещения всех переменных.
9.2 Включайте в программу операции восстановления стека. Это повысит надежность работы длительно работающих программ. Восстановление можно производить, например, при переключении режима работы, когда обновляются значения многих переменных. Для этого в текст программы сразу после инициализации включите следующий маленький фрагмент:
Clr_mem: |
|
|
$asm |
R0 , #&hff |
|
Mov |
'запомнить значение стека в самой верхней ячейке |
|
Mov |
@r0 , Sp |
|
Dec |
R0 |
|
Clrmem: |
|
'очистка памяти |
Mov @r0 , #0 |
||
Djnz R0 , Clrmem |
|
|
$end Asm |
|
|
Теперь в нужном месте всегда можно восстановить значение стека, назначенное компилятором:
$asm
Mov R0 , #&hff
Mov Sp , @r0 'восстановить значение стека $end Asm
Обе программы написаны на ассемблере, чтобы полностью контролировать этот процесс.
Десятый совет (о проверке условий).
10.1Не применяйте сложные конструкции проверки условий. Во-первых, это не сокращает размер кода (напротив, увеличивает по сравнению с простыми проверками). Рекомендуемый максимум – конструкция If … Then … Else … End If. Лучше пусть будет больше простых операторов проверки условий – будет меньше ошибок ваших и компилятора.
10.2Не применяйте вложенные конструкции проверки условий. Это также не сокращает размер кода, а только загружает стек. Если очень нужно, применяйте конструкцию If … Then … Elseif … Then … End If).
10.3Пишите программу так, чтобы все проверяемое было по возможности переменными типа Bit, Byte и
Single.
10.4Анализ строковых переменных сводите к проверке значения одного символа.
10.5Никогда не проверяйте значение числа, записанного в строковой переменной. Преобразуйте в число,
апотом проверяйте.
10.6Никогда не проверяйте на равенство значение числа записанного в формате с плавающей точкой (типа Single). Числа в этом формате записаны приближенно. Поэтому два одинаковых числа, полученных разным способом, скорее всего не равны.
Одиннадцатый совет (о сложных конструкциях).
11.1Не применяйте сложных конструкций. Они сохранились как принадлежность первых версий Бейсика
снумерованными строками и, не имеющего меток для перехода. Они удобны для интерпретирующих версий Бейсика, так как сокращают размер исходного текста. В современной версии все специальные конструкции легко строятся с помощью операторов проверки условий и перехода. Простые конструкции занимают больше места только в исходном тексте, а в коде они короче. Кроме того, меньше используется стек и дополнительные внутренние переменные, которые занимает компилятор. Имеются в виду конструкции типа: Do … Loop Until … , While … Wend и даже For … Next. Громоздкой код дает конструкция Select Case … , практически не уступающий цепочки конструкций типа If … Then … .
11.2Избегайте вложенности сложных конструкций и тем более одинаковых конструкций. Возможны ошибки компилятора. Лучше сразу сделать дополнительную ветку программы, чем долго искать причину ошибки.
11.3Старайтесь, чтобы в программе применялось как можно больше одинаковых конструкций. Все они будут использовать одну библиотечную подпрограмму. А чем меньше библиотека, тем короче программа.
11.4По возможности используйте проверенные и отработанные конструкции с теми же типами переменных. Применение новой конструкции, скорее всего, приведет к ошибке и необходимости проверки ее работы.
11.5По возможности, используйте для управления программой (выбора, разветвления, индексации) только битовые и байтовые переменные, даже если число таких переменных придется увеличить. Вы получите очень компактный и быстро работающий код.
Двенадцатый совет (о передачи значений адресов). Используйте оператор Restore и функцию Varptr()
для считывания значений адресов переменных и меток полученных при компиляции.
При программировании с использованием Bascom возникают трудности записи в поля непосредственных данных адресов, определяемых при компиляции. При записи метки или символьного имени переменной в поле
============================================================================= 32-4
===================================== Справочник по программированию «Bascom-8051» ==
данных (например, при создании таблиц) компилятор возвращает в коде нулевые значения. Предлагаемый способ передачи программе значений адресов, полученных при компиляции, демонстрирует ниже следующая программа.
Dim X As Byte , Y As Byte , W As Word $Ramstart = &H1000
Dim Var1 As XRAM Single
W = Varptr(Var1) ‘считаем адрес переменной
Print W |
‘ “4096” – адрес переменной |
|
M1: |
'считаем адрес метки |
|
Restore M1 |
||
Gosub Read_restore |
|
|
Print W |
‘ “240” – адрес метки M1 |
|
End |
|
|
‘----------- |
|
|
‘подпрограмма считывания адреса, записанного оператором Restore |
||
Read_restore: |
|
'&h47 = (SP)-2, &H48 = (SP)-1 |
X = Peek(&H47) : Y = Peek(&H48) |
||
W = Makeint(x , Y) |
|
|
Return |
|
|
Оператор Restore записывает значение указанной метки во внутреннюю переменную (типа Word), расположение которой нужно определить в отладчике. В компиляторе версии 2.хх она располагается под стеком, вычисленным компилятором (ее адрес = (SP) - 2 ). Это значение можно посмотреть в файле отчета о компиляции (вызывается кнопками “Ctrl+W”). Можно попробовать применять и следующую конструкцию.
‘вариант подпрограммы считывания адреса, записанного оператором Restore $Asm
Pop Acc ‘взять из-под стека
Mov {W + 1} , A Pop Acc
Mov {W} , A Inc SP
Inc SP $End Asm
Функция Varptr() сразу записывает адрес переменной. Объем получаемого кода от подобных действий очень мал (6-20 байт), поэтому их можно применять в программе много раз и, даже создавать таблицы их этих операторов.
============================================================================= 32-5
