Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
МПС_проект2007.doc
Скачиваний:
87
Добавлен:
12.04.2015
Размер:
2.02 Mб
Скачать
      1. Взаимодействие между задачами

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

Целостность данных

Целостность данных может быть нарушена в следующих случаях:

  1. При копировании одной переменной.

  2. При работе с массивом данных.

  3. При использовании переменной в ряде последовательных операторов.

1. Предположим, например, что имеется оператор присвоения

х = у;

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

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

2. Вторая проблема целостности данных, связанная с массивами данных, возникает из-за подобных же причин. При копировании массива данных возможно переключение задачи вследствие прерывания высокоприоритетной задачей. Если высокоприоритетная задача модифицирует массив, то при переключении на низкоприоритетную задачу произойдет искажение данных массива – некоторые из данных массива будут связаны со старыми значениями, а некоторые – с новыми. В этом случае, хотя все значения данных могут быть и правильными, набор данных может оказаться неправильным.

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

х = some_calculation ();

if (х > max) х = max;

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

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

Правила проектирования

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

  • выделить и защитить критические области;

  • выделить и изолировать все передачи данных между задачами.

Критические области

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

Основное правило: формировать критические секции предельно короткими.

Простой пример изоляции критической области, использующий стандартные функции общего запрета прерываний (ЕА=0) и общего разрешения прерываний (ЕА=1):

EA = 0; /* общий запрет прерываний*/

х = у;

EA = 1; /* разрешение прерываний*/

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

setpt = temp_soak;

EA = 0; /* общий запрет прерываний*/

x_setpt = setpt;

EA = 1; /* разрешение прерываний*/

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

Передача данных между задачами

Данные между задачами можно передавать с помощью глобальных переменных, реализованных как переменные static или extern, а также посредством вызова функций.

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

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

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

Реализация обмена через вызов функции обычно осуществляется через общедоступный буфер обмена. Задача, которая передает данные, помещает их в буфер обмена. Задачи-получатели данных считывают их из буфера путем вызова функции чтения. Буфер обмена в этом случае является глобальной переменной. Изоляцию передачи следует реализовать только в задаче источнике данных и в функции обмена.

Взаимодействие с внешними устройствами

Взаимодействие с внешними устройствами осуществляется следующими способами: обмен по опросу или по прерыванию. Так, при работе с ЖКИ-модулем наиболее распространен обмен по опросу (опрос флага готовности, см. п.5.2.2), в других случаях считается, что устройство всегда готово (вывод информации на светодиод), при работе с контроллером последовательного порта наиболее распространен обмен по прерыванию (см. п.5.2.4).

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

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

Организация буферов обмена

Буфер представляет собой одномерный циклический массив, состоящий из элементов типа unsigned char (рис.5.8). Доступ к буферу возможен при помощи двух указателей, которые содержат индексы текущих позиций чтения и записи: WP (указатель записи) и RP (указатель чтения).

Рис.5.8. Кольцевой буфер обмена

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

Когда буфер пуст, указатель чтения RP равен указателю записи WP. Если буфер полностью заполнен, то указатель записи указывает на элемент, расположенный перед элементом, на который указывает RP. Если продолжительное время в буфер производится запись и ничего не читается, то буфер переполняется. В этом случае WP, “обойдя” весь буфер по кругу, “догонит” RP. Если в случае переполнения продолжить запись в буфер, то это приведет к потере ранее занесенных в него данных.

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

Пример записи байта SBUFв буферRFIFOобработчиком прерывания приведен ниже:

/* Запись принятого байта в буфер RFIFO*/

if (WP = =KeySize-1) /*Дошли до конца буфера?*/

i =0; /* Да, установить адрес на начало буфера */

else

i =WP+1; /*Нет, увеличить адрес в буфере*/

if (i != RP) /* Нет переполнения буфера?

(адрес для записи не равен адресу по чтению из буфера?) */

{

RFIFO[WP] = SBUF; /*Запишем в буфер принятый байт по адресу записи*/

WP = i; /* Установим новый адрес для записи очередного байта */

}

В данном фрагменте не использована изоляция передаваемых данных. Предполагается, что обработчик прерывания имеет более высокий приоритет, чем пользовательские процедуры (АРI-функции), и что запрещены вложенные прерывания – обработчика никто не сможет прервать.

Пример чтения байта из RFIFOфункцией доступаприведен ниже. Следует обратить внимание на то, что данная процедура находиться в низкоприоритетной задаче, поэтому критическая область (передача данных между задачами) защищена. Защита осуществлена при помощи запрета прерывания от контроллераUART(битESрегистраIE). Здесь предполагается, что работа с буферомRFIFO осуществляют только две задачи: обработчик прерывания отUARTи соответствующая функция доступа низкоприоритетной задачи. Если функция доступа может вызываться из высокоприоритетной задачи, и разрешены вложенные прерывания, то защищать необходимо и критическую область в обработчике прерываний путем запрета соответствующих или общего прерываний.

/* Чтение принятого байта из буфера RFIFO*/

ES = 0;

if (RP != WP) /* Буфер пуст? */

{ /* Нет */

Sdata = RFIFO[RP]; /*Прочитаем из буфера принятый байт */

RP =RP+1; /* Адрес для чтения очередного байта */

if (RP == KeySize) /*Дошли до конца буфера?*/

RP =0; /* Да, установить адрес на начало буфера */

}

ES = 1;

Организация обработчика прерывания

Окончание передачи байта и окончание приема байта по последовательному каналу вызывают одно и то же прерывание, и в обработчике два этих прерывания различаются программно. Обработчик логически разделен на две части: одна работает при установленном флаге RI (окончание приема), а другая при установленном флаге TI (окончание передачи).

void sio_isr() interrupt

{

if(TI)

{

// Передача байта

// Читаем WFIFO и записываем его в SBUF

}

if(RI)

{

// Прием байта

// Читаем регистр данных SBUF и записываем его в RFIFO

}

}

Вектор прерывания

Прерывание по последовательному каналу генерируется контроллером UARTво время установки флага TI либо RI. При возникновении прерывания осуществляется переход по адресу вектора прерывания по последовательному порту. Чтобы разрешить это прерывание, нужно установить битразрешения прерывания от UARTи бит EA (общее разрешение прерывания).