Например, система управления состоит из низкоуровневых контроллеров, транспортной системы передачи данных и компьютера, который осуществляет мониторинг и управление некоторыми параметрами. При этом контроллеры могут самостоятельно решать задачу измерения и управления объектом и реализованы они на Atmega128. Транспортная система состоит из преобразователя USB-RS485. Программа мониторинга написана на Delphi 7. Всё это для определенности.

Если бы количество передаваемых параметров из контроллера в компьютер ограничивалось двумя, тремя значениями температуры, то оригинальничать здесь нечего. Однако, если объект управления, даже при интенсивности передачи раз в 2 секунды имеет, скажем 50 параметров самых различных типов, да при этом количество объектов управления четыре штуки и они объединены в общую шину, то задача формата передачи данных не простая.

Вот, для наглядности, можно посмотреть, сколько параметров содержит процесс управления биореактором:

http://keklab.ru/biotools/remotelab24/page.php?proc=proc1

Можно нажать кнопку Start, а затем Управление. Вся система структурно выглядет так:

http://keklab.ru/2011-02-13-20-20-54/69-sleep.html

Что имеется на практике? Протокол MODBUS, который является надстройкой над низкоуровневым протоколом RS-485. Предположим, что программа для контроллера реализована на AVR Studio. Переменные, организующие процесс управления самые разнообразные:

u16 TTLout; //порт выходов TTL
u08 process_start;
// 1 - старт процесса 0 - стоп
u32 EFT; // Elapsed Fermentation Time в секундах
float value[All_ADC_Ch]; //Измеряемые параметры
//…………………
s16 mixer_value1;

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

typedef unsigned short u16;
typedef unsigned char u08;
typedef signed short s16;
typedef unsigned long u32;

Для передачи всех переменных в контроллере необходимо каждую из них по соответствующему запросу преобразовать и положить в буфер. Именно так реализуется смысл запросов в протоколе MODBUS. Для скорости можно сгруппировать переменные и передавать их кадрами. Но процедуры помещения переменной в кадр не избежать. При этом надо знать сколько байт, каждая переменная занимает в памяти контроллера.

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

Что можно сделать? Как упростить процесс формирования кадра и повысить скорость. Если для верхней машины скорость не важна, то для микропроцессора, который кроме транспортных задач решает задачи управления это важно.

И так, внизу, в программе для контроллера все переменные, необходимые как для управления, так и для транспорта располагаются в структуре. Создаем её:

typedef struct sbuf //структура для непрерывного размещения переменных в памяти
{
u16 TTLout;
u08 process_start;
u32 EFT;
float value[All_ADC_Ch];
//……………………
s16 mixer_value1;
u08 zero;
} tbuf;

tbuf buf; //объявляем переменную данного типа.

В теле основной программы для контроллера обращение к конкретной переменной этой структуры будет выглядеть так:

buf.value[0] = 7.4;

Что это дает? А то, что после компиляции все переменные, разного типа располагаются в памяти непрерывно, именно в той последовательности, как они объявлены в структуре. Каждая из них занимает свое количество байт, вполне определенное.

Теперь вернемся к транспортной проблеме. Процесс управления занимается своим делом, в определенный момент нам надо сформировать кадр и передать его по RS-485. Теперь это просто:

startbuf = &buf; //адрес начала кадра

stopbuf = &buf.zero; // адрес конца кадра, переменная zero, просто обозначает в структуре незначащую переменную.

 

Теперь вся структура это один кадр, который можно передать в цикле, зная адрес начала и конца. Либо можно разбивать всю структуру на меньшие по размеру кадры, ранжируя их по степени востребованности на верхней машине.

Но это пол дела. Как в Delphi 7 получить этот кадр и так же легко его интерпретировать? Там тоже есть свои хитрости. Вот аналогичная структура. Если бы компилятор Delphi 7 позволял реализовывать C-шные вставки, то и переписывать ничего не надо. А так ручками поработаем и обозначим типы, которые имеют свои аналоги как в С, так и в Delphi 7:

type
TBuf = record
TTLout: word;
process_start: byte ;
EFT: cardinal;
value: array [0..All_ADC_Ch] of single;
//……………………
mixer_value1: smallint;
zero: byte;
end;

В основной программе необходимо объявить переменную данного типа:

buf:TBuf;

И обращение к конкретной части записи будет выглядеть как:

buf.value [0]:=7.4;

Если транспорт организован например, с помощью преобразователя USB-RS485 на основе FTDI, то используя библиотеки, можно организовать получение нашего кадра из контроллера:

Const
numbyteframe = 4096;
// размер всего транспортного  буфера, или кадра
var

res:integer;
// количество принятых байт в кадре
bufdata: array [0.. numbyteframe - 1] of byte;
// временный буфер для транспорта

FT_Read(ftdi.FT_Handle, @bufdata, numbyteframe, @res); //
библиотечная функция приема буфера по USB

Затем проверили на ошибки и передали в структуру:

move(bufdata,buf.TTLout,numbyteframe);

После этого все переменные структуры получили свою правильную интерпретацию. Если предпринять проверку буфера на лету, то операцию move так же можно избежать и передавать сразу из FTDI в переменную buf. Правда, в случае нарушения связи, переменные, занимающие в памяти более одного байта могут интерпретироваться не правильно.

На этом, правда нюансы не кончаются, есть еще один, существенный. Как настроить компилятор, чтобы он не выравнивал переменные под 32 байта? Не знаю как в С++, а в Delphi это делается так:

Project + Options + Compiler.

В поле Code generation надо выбрать параметр:

Record field alignment = 1

Настройка компилятора

Теперь переменные типа byte, не будут раздражать 32-х разрядный процессор.

Обновлено (23.02.2013 10:21)

 
Новости электроники от РадиоЛоцмана
Вопросы искусственного интеллекта, философия и практика
. @Mail.ru