Сафоненко Практикум по интерфейсам последователной передачи 2012
.pdfВызов оператора stopasync осуществляется со ссылкой на объект
последовательного порта:
>> stopasync(com1)
Byte order
При передаче числа важным параметром является порядок сле-
дования байт в многобайтовых числах. Многобайтовые числа – соответствуют форматам, которые занимают в памяти больше одного байта (например: int16 – 2 байта, float32 – 4 байта, double – 8 байт). При передаче такие числа разбиваются на отдельные байты, которые по очереди передаются в канал. Порядок следования байт определяет свойство ByteOrder: значение littleEndian (от младшего к старшему) соответствует передаче младший байта первым, bigEndian (от старшего к младшему) – старшего байта первым. Рассмотрим пример ошибок при неправильной установке этого свойства.
Свойства установлены на передающей и принимающей сторонах:
>>com1.byteorder ans =
littleEndian
>>com2.byteorder ans =
littleEndian
Теперь на одной из сторон изменим порядок следования байт:
>> com1.byteorder='bigendian';
Таким образом, передающая и принимающая стороны по разному будут интерпретировать данные. Осуществим передачу и прием данных:
>>cmd=[1 4 257 17];
>>fwrite(com1,cmd,'int16')
>>fread(com2,4,'int16')
61
ans =
256
1024
257
4352
Как видно, данные передавались двухбайтовыми словами (int16). Принятые и переданные данные не совпадают (кроме числа 257). Для понимания происходящего рассмотрим числа в двоичном представлении:
110=00000000 000000012,
410=00000000 000001002,
2572=00000001 000000012,
1710=00000000 000100012.
Так как источник и приемник по-разному воспринимают порядок байт в слове, переставим местами байты (именно так они будут восприниматься приемником):
00000001 000000002=25610,
00000100 000000002=102410,
00000001 000000012=25710,
00010001 000000002=435210.
Таким образом, изменение порядка следования байт в числе объясняет искажение информации. В числе 25710, первый и второй байты совпадают.
Организация алгоритмического и событийного методов обмена информацией. События объекта serial в MATLAB
Как показывает опыт, эффективность конечного результата зависит от алгоритма упорядочения очередности выполнения ряда функций. Если процессору приходится часто переключаться на выполнение новой функции, рабочее расписание усложняется, и не всегда с положительным результатом. Автоматическое определение причины позволяет упорядочить вызовы по их значимости и своевременно реагировать.
62
Механизм переключения работы процессора на более важную задачу, названный механизмом прерываний, заложен в алгоритм работы компьютера.
В системах объектно-ориентированного программирования широко используются понятие «событие» как следствие функционирования среды и объекта. Каждое событие вызывает сигнал прерывания, который инициирует соответствующую программу-обработ- чик. В системе MATLAB применяется аналогичный механизм. Для объекта последовательного порта serial используют шесть собы-
тий: BreakInterrupt, BytesAvailable, Error, OutputEmpty, PinStatus, Timer.
Для обработки событий необходимо создать соответствующий M-файл (функцию-обработчик данного события). Заголовок M- файла должен содержать минимум две входные переменные:
function event_fcn(object, event)
В последующих примерах рассмотрена функция сигнализатор с именем alarm. С помощью нее можно наблюдать генерацию событий. М-файл будет выглядеть следующим образом:
function alarm (object, event)
disp('Alarm!');
Когда возникнет событие, в командной строке появится сообщение:
Alarm!
Количество входных параметров может быть больше, чем два. Но параметры object и event должны обязательно присутствовать. Они описывают объект, с которым произошло данное событие, и само событие.
Нарушение очередности настройки свойств порта и применения событий приводит к серьезным сбоям в программах. По этой причине последовательный порт должен быть закрыт на время изменения каких-либо свойств, связанных с обработкой прерываний. Соответственно, при его закрытии вызов всех событий будет приостановлен до команды открытия порта.
Событие BreakInterrupt
Событие BreakInterrupt возникает, когда в линии передачи пропадает сигнал логического нуля. В отсутствие передачи данных, при асинхронном режиме, в канале устанавливается логическая
63
«1». При ее исчезновении через некоторое время (таймаут события) возникает событие Break. Это событие обычно вызывает функцию, которая аварийно завершает программу или указывает программепользователю на проблемы с подключением к порту. На практике это событие используют для того, чтобы перезапустить подключенное устройство. Для вызова этого события необходимо использовать оператор serialbreak. Он принимает два значения. Первое, обязательное, имя объекта порта, который вызывает событие, второе – необязательное, таймаут события в миллисекундах.
Для примера назначим порту com1 событие BreakInterrupt. И вызовем его с помощью порта com2:
>>com1.breakinterrupt=@timfun;
>>serialbreak(com2)
Alarm!
Можно использовать вызов с ожиданием на установленное время, прежде чем будет вызвано пррывание:
>> serialbreak(com2,1000)
Alarm!
В этот раз исполнение функции alarm задержано на одну секунду.
Событие BytesAvailable
Событие BytesAvailable возникает, когда во входной буфер порта приходят данные. Это событие используется для обработки входных данных на принимающей стороне по мере их поступления.
Пользователь должен выбрать условие генерации события. Таким условием может быть приход символа завершения передачи, или обнуление счетчика количества считанных байт, определяемого свойством BytesAvailableFcnCount. Условие задается в свойстве BytesAvailableMode. Для генерации события с первым типом условия для объекта порта com1 необходимо указать:
>> com1.bytesavailablefcnmode='terminator';
Событие второго типа возникнет, если установить свойство byte
>> com1.bytesavailablefcnmode='byte';
Напомним, что свойства BytesAvailableMode и BytesAvailableFcnCount необходимо изменять на этапе инициализации.
64
Рассмотрим примеры. Для генерации события первого типа ус-
тановим символ завершения передачи «E», и назначим функцию alarm как функцию обработки этого события:
>>com1.terminator={'E','E'};
>>com1.bytesavailablefcnmode='terminator';
>>com1.bytesavailablefcn=@alarm;
Только теперь можно открыть порты:
>>fopen(com1)
>>fopen(com2)
Теперь произведем передачу:
>> fwrite(com2, 'data');
Никакого события не произошло, так как символ окончания передачи «E» не был передан.
>>fwrite(com2, 'dataE'); Alarm!
>>fwrite(com2, 'dataE'); Alarm!
Как видно, после каждого оператора записи следует вызов события:
>> fscanf(com1,'%s')
ans =
datadataE
Чтение остановилось, так как был прочитан символ окончания передачи:
>> fscanf(com1,'%s')
ans =
dataE
Чтобы сгенерировать событие второго типа, сначала закроем порт:
>> fclose(com1)
65
Установим соответствующий режим:
>> com1.bytesavailablefcnmode='byte';
Назначим в качестве события факт накопления во входном буфере 5 байт:
>> com1.bytesavailablefcncount=5;
Откроем соответствующий порт:
>> fopen(com1)
Произведем запись:
>> fwrite(com2, 'data');
Никакого события не произошло, так как было передано только
4байта. Передаем 5 байт данных
>>fwrite(com2, 'dataE');
Alarm!
Как только в операции записи в противоположный порт (com2) количество байт станет больше или равно пяти, возникает событие
BytesAvailable.
Заметим, что в любой момент можно прочесть количество байт во входном буфере по свойству BytesAvailable:
>> com1.bytesavailable
ans =
9
Читаем весь буфер приемника:
>> fscanf(com1,'%s')
ans =
datadataE
В стандартных обработчиках чтение данных выполняется сразу после возникновения события
66
>>fwrite(com2, 'dataE'); Alarm!
>>fscanf(com1,'%s')
ans =
dataE
В порт записано 5 байт, что вызвало ожидаемое событие.
Событие Error
Событие Error возникает по ошибке передачи данных. Обычно по такому событию следует либо аварийно завершить программу, либо сигнализировать о возникновении ошибки и приостанавить передачу-прием.
Событие OutputEmpty
Событие OutputEmpty генерируется, когда выходной буфер передатчика пуст и все накопленные данные из очереди переданы в линию. Для назначения функции, ответственной за данное событие, необходимо присвоить имя этой функции переменной
OutputEmptyFcn.
Событие генерируется только в режиме асинхронной записи в порт. Рассмотрим пример. Для этого назначим вышеописанную функцию alarm ответственной за данное событие:
>> com1.outputemptyfcn=@alarm;
Произведем асинхронную операцию записи:
>> fwrite(com1, 'data','async'); Alarm!
Как только завершилась операция передачи данных в линию, буфер опустел и произошел вызов события OutputEmpty.
Событие PinStatus
Событие PinStatus генерируется, когда состояние хотя бы одного из сигналов DCD, CTS, DSR или RI изменяется. Эти события можно
67
использовать при аппаратном управлении передачей. Как только принимающая сторона откажется принимать данные, она изменяет определенный сигнал (например, сбрасывает DTR) и, тем самым, изменяет состояние соответствующего сигнала на передающей стороне (например, DSR). Это вызывает генерацию события PinStatus. Затем произойдет вызов функции, отвечающей за данное событие, которая приостановит передачу данных. Для назначения функции, ответственной за данное событие, необходимо присвоить имя этой функции переменной PinStatusFcn.
Приведем пример. Назначим описанную выше функцию alarm ответственной за данное событие и проверим первоначальное состояние сигналов:
>>com1.pinstatusfcn=@alarm;
>>com1.pinstatus
ans =
CarrierDetect: 'on'
ClearToSend: 'off'
DataSetReady: 'on'
RingIndicator: 'off'
Изменим бит DTR на приемной стороне:
>> com2.dataterminalready='off'; Alarm!
Alarm!
Событие было вызвано дважды:
>> com1.pinstatus
ans =
CarrierDetect: 'off'
ClearToSend: 'off'
DataSetReady: 'off'
RingIndicator: 'off'
68
Как видно, изменились два сигнала CDC и DSR, и событие возникает при изменении каждого из них!
Немного изменим функцию alarm. Теперь она будет выглядеть так:
function alarm (object, event) disp('Alarm!'); event.Data.Pin event.Data.PinValue
Установим режим индикации типа прерывания и значений сигналов квитирования. Заново изменим бит DTR порта com2:
>> com2.dataterminalready='on'; Alarm!
ans =
Data Set Ready
ans = on Alarm! ans =
Carrier Detect
ans = on
Новые свойства функции alarm свойства содержат имя сигнала, изменение которого вызывает данное событие, и текущее значение сигнала.
Эти свойства избавляют нас от необходимости отслеживать состояние сигналов до прерывания. Достаточно прочесть значение сигнала, если оно изменилось.
69
Событие Timer
Событие Timer возникает по истечении интервала времени, определённого при инициализации свойством TimerPeriod. Имя функции, ответственной за обраблтку события, необходимо присвоить переменной TimerFcn. Это событие позволяет периодически контролировать состояние входного или выходного буферов, состояние передачи.
Рассмотрим пример. Установим период таймера равным 1 с:
>> com1.timerperiod=1;
откроем порты и поручим обработку данного события функции alarm:
>>fopen(com1)
>>fopen(com2)
>>com1.timerfcn=@alarm;
Через 1 с, как и установлено, начнут происходить вызовы функции обработки событий таймера:
Alarm!
Alarm!
Alarm!
Alarm!
Остановим вызов события:
>> com1.timerfcn='';
Вызов события Timer можно остановить, если издать команду закрыя порта fclose.
Свойства событий
При возникновении события вызывается функция его обработки, которой передаются переменные object и event. Эти переменные содержат информацию об объекте, с которым произошло событие и свойства события.
Для всех событий определены свойства Type и Data.AbsTime (их можно прочесть, обратившись к структуре event: event.Type и event.Data.AbsTime). Свойство Type содержит тип события (все
70