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

В разделе проектирования системы был выполнен первый этаппроектирования программного обеспечения – определена диаграмма задач управления светофором (рис.6.18).

Следующими этапами проектирования являются:

  • Представление каждой из задачв виде последовательности состояний (графа состояний). Этовторой этаппроектирования ПО. Состояния описывают определенные действия в пределах задачи; в какой-либо момент времени только одно состояние может быть активно.

  • Представление отдельных состоянийв виде формальной структуры функций –третий этап.Блок-схема алгоритма выполнения состояния представлена на рис.6.19.

Для каждого состояния определены следующие функции:

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

Функция действия выполняется всегда.

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

Данный алгоритм является общим, в конкретной реализации могут отсутствовать функции входа, функции выхода и т.д.

Кроме выполнения этих трех этапов необходимо решить вопросы системного характера, а именно:

  • переключение задач (параллельное выполнение задач);

  • взаимодействие между задачами.

Рис.6.18. Диаграмма задач управления светофором

Рис.6.19. Алгоритм функционирования состояния

Переключение задач

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

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

Циклическое переключение задач можно организовать при помощи конструкции while (1)– /* выполнять непрерывно*/:

while (1) /* выполнять непрерывно*/

{

задача 1;

задача n;

}

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

Совместное мультиуправление задачами имеет ряд ограничений, связанных с простотой реализации диспетчера:

  1. использование только неблокирующегокода;

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

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

Таким образом, переключение задач будет осуществлено путем реализации двух подпрограмм обработки прерывания (задачи "Обработка входных сигналов" и "Отсчет времени") и организации циклической конструкцииwhile (1),внутри которой помещаются задачи "Управление выходными сигналами" и "СбросWDT":

while (1)/* выполнять непрерывно*/

{

задача "Управление выходными сигналами";

задача "Сброс WDT";

}

Взаимодействие между задачами

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

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

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

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

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

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

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

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

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

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

  • признак сработки/нажатия, передается от задачи "Обработка входных сигналов" задаче "Управление выходными сигналами";

  • текущее время от задачи "Отсчет времени" задаче "Управление выходными сигналами".

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

После того, как определились с вопросами системного характера, приступим непосредственно к проектированию задач.

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

Модуль инициализации (INIT)должен содержать процедуры, которые выполняют функции инициализации каждой задачи:

  • инициализация задачи " Обработка входных сигналов " заключается в настройке системы прерывания по внешнему входу;

  • инициализация задачи " Отсчет времени " заключается в настройке системы прерывания по переполнению таймера и настройке таймера на заданный временной интервал;

  • инициализация задачи " Сброс WDT" заключается в настройке охранного таймера – времени задержки до его сработки;

  • инициализация задачи " Управление выходными сигналами " заключается в настройке портов на ввод или вывод информации (задавая тем самым первоначальное состояние сигналов светофора).

Кроме этих настроек в модуль инициализации помещаются начальные значения переменных.

Настройка системы прерывания

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

Разрешение прерывания по внешним входам INT0,INT1 осуществляется установкой битовINT0(bit6) иINT1(bit7) в регистре GIMSK. Начальное значение (после сброса) – биты сброшены (равны 0).

Задание вида сигнала, вызывающего прерывание по внешним входам, задается битами регистра MCUCRISC01(bit1) иISC00(bit0) для входаINT0 иISC11(bit3) иISC10(bit2) для входаINT1 в соответствии с табл.6.3. Начальное значение – биты сброшены (равны 0), т.е. вызов прерывания будет осуществляться низким уровнем.

Таблица 6.3

ISCх1

ISCх0

Описание

0

0

Запрос прерывания генерируется низким уровнем на INTx

0

1

Запрос прерыв. генерируется любым логическим изменением на INTx

1

0

Запрос прерывания генерируется падающим фронтом на INTx

1

1

Запрос прерывания генерируется нарастающим фронтом на INTx

Разрешение прерывания по переполнениюТаймера 1 (16-разрядный таймер) осуществляется установкой битаTOIE1(bit7) в регистреTIMSK.

Разрешение глобального прерывания – установка бита I(bit7) в регистреSREGили при помощи команды _SEI().

Таким образом, разрешение прерываний по внешнему входу (низким уровнем сигнала) и по переполнению Таймера 1 будет выглядеть следующим образом:

TIMSK | = 0x80; // Таймер 1 - разрешение прерываний

GIMSK | = 0x40; // INT0 - разрешение прерывания

_SEI(); // Разрешение глобального прерывания

Чтобы не изменить другие биты регистров, использована операция побитового логического ИЛИ содержимого регистров с константой.

Команда разрешения глобального прерывания помещается в конце процедуры инициализации.

Настройка таймера

Настройка таймера заключается в задании частоты импульсов, поступающих на вход таймера, и задании начального состояния таймера, перезагружаемого каждый раз при его переполнении. Таймер считает на увеличение. Импульсы тактового генератора поступают на счетный вход таймера через предварительный делитель (предделитель). Задание частоты выполняется путем установки коэффициента предделителя. Число, загружаемое в таймер, определяется частотой импульсов на его счетном входе и частотой, с которой должен формироваться импульс переполнения таймера. Этот импульс переполнения и формирует запрос на прерывание. Если точность отсчета временных интервалов задать порядка 0,1с, то частота переполнения таймера должна быть равна 10Гц. Для частоты тактового генератора, равной 4МГц, определим разрядность таймера. При установке максимального коэффициента предделителя, равного 1024, таймер должен подсчитать следующее количество импульсов до переполнения:

К = 4000000(Гц)/10(Гц)/1024 ≈ 390

Таким образом, разрядность таймера должна быть больше 8 (модуль счета 256). Остановимся на 16-разрядном таймере 1. Коэффициент предделителя можно выбрать произвольным, например, равным 8 (при другом коэффициенте изменится загружаемое в таймер число), тогда число для перезагрузки таймера (N) будет равно:

N1 = 4000000(Гц) /10(Гц) / 8 = 50000 = 0хC350 (шестнадцатеричная система).

Так как таймер считает на увеличение (в прямом направлении), то для определения загружаемого числа необходимо из максимально возможного числа для 16-разрядного таймера вычесть найденное. Для 16-разрядного таймера максимальное число равно 0х10000. Тогда:

N= 0x10000 – 0xC350 = 0x3CB0

Перезагрузка осуществляется отдельно в старший (TCNT1H) и младший (TCNT1L) байты таймера каждый раз при вызове подпрограммы обработки прерывания.

Задание коэффициента предделителя Таймера 1 осуществляется установкой битов CS12(bit2),CS11(bit1),CS10(bit0) регистраTCCR1B. В табл.6.4 приведены коэффициенты деления частоты в зависимости от значений бит регистра.

Таблица 6.4

CS12

CS11

CS10

Коэффициент деления

0

0

0

останов

0

0

1

без предделителя

0

1

0

CLK / 8

0

1

1

CLK / 64

1

0

0

CLK / 256

1

0

1

CLK / 1024

Таким образом настройка таймера будет выглядеть:

//коэффициент деления частоты генератора = 8

TCCR1B | = 0x02; // установка CS11

TCCR1B & = 0xFA; // Сброс CS12 и CS10

// загружаемое число

TCNT1H = 0x3C; TCNT1L = 0xB0; //10Гц при частоте кварца 4МГц

// 4МГц/8/10Гц = 50000 = 0xC350;

//так как 16р таймер работает на увеличение,

//то загрузка его = 0x10000 – 0xC350 = 0x3CB0

Чтобы задать значение только трем битам регистра TCCR1B, выполнены операции побитового ИЛИ и И содержимого регистра и константы.

Настройка охранного таймера

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

Предделитель определяет длительность периода сброса. Для разрешения работы сторожевого таймера необходимо выполнить команду сброса _WDR(), задать коэффициент предделителя (биты WDP3 (bit5), WDP2 (bit2), WDP1 (bit1), WDP0 (bit0)) регистра WDTCSR и установить бит разрешения работы WDE (bit3) в регистре WDTCSR. Коэффициенты предделителя и соответствующие им длительности периода сброса представлены в табл.6.5. Таким образом, разрешение охранного таймера с периодом работы 2,0с будет выглядеть следующим образом:

_WDR(); // Сброс охранного таймера

WDTCSR | = 0x0F; // Разрешение работы охранного таймера и настройка

WDTCSR & = 0xDF; // период сработки 2.0 с

Таблица 6.5

WDP3

WDP2

WDP1

WDP0

Период сброса сторожевого таймера

0

0

0

0

16 мс

0

0

0

1

32 мс

0

0

1

0

64 мс

0

0

1

1

0,125 с

0

1

0

0

0,25 с

0

1

0

1

0,5 с

0

1

1

0

1,0 с

0

1

1

1

2,0 с

1

0

0

0

4,0 с

1

0

0

1

8,0 с

Настройка портов

За настройку каждого из портов отвечают соответствующие два регистра – DDRxиPORTx (х – имя порта – А, В илиD), например,DDRBиPORTBдля портаВ. БитыDDхn(n= 7÷0 – номера выводов порта) регистраDDRxопределяют направление работы соответствующего вывода. При установленном в состояние "1" битеDDхnвыводРхnконфигурируется как вывод выхода. При очищенном битеDDхnвывод конфигурируется как вывод входа.

Регистр PORTx– выходной регистр. При задании вывода в качестве входного порта соответствующий бит регистраPORTxподключает/отключает нагрузочный резистор на входе порта.

В табл.6.6 представлено воздействие битов DDхn иPORTxnна характер работы выводов портах.

Таблица 6.6

DDxn

PORTxn

I/O

Нагрузочный резистор

Описание

0

0

Вход

Не подключен

Третье состояние (Z)

0

1

Вход

Подключен

При низком уровне Pxn обеспечивает вытекающий ток

1

0

Выход

Не подключен

Низкий уровень, двухтактный выход

1

1

Выход

Не подключен

Высокий уровень, двухтактный выход

Отключать/подключать нагрузочные резисторы одновременно можно также при помощи бита POD(bit7) регистраMCUCR2. При сброшенном бите режим определяется табл.6.5, при установленном – резисторы не подключены. Так как в начальном состоянии этот бит сброшен, то его программирование для нашего случая не обязательно.

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

В соответствии со схемой рис.6.16 необходимо настроить часть выводов порта PDна выход (PD6,PD5,PD4,PD2,PD1,PD0), аPD2 на вход (вход прерыванияINT0). Это обеспечивается следующей последовательностью команд:

DDRD = 0x7B; //РD6, PD5, PD4, PD3, PD1, P0 – на выход, РD2 – на вход

Так как после сброса все разряды регистра PORTD обнулены, то ко входуPD2нагрузочный резистор не подключен.

Задание начальных значений

Начальное состояние должно быть установлено для выходных линий порта PD– включение ламп светофора в соответствии с векторомZ1. Так как установка выходной линии порта в "1" означает включение лампы, а сброс в "0" – выключение, то в выходной порт необходимо послать следующее значение:

//Установка выходного вектора Z1 на PORTD

PORTD | = 0x18; // зажечь Зг и Кв – "1" в bit4 и bit3

PORTD & = 0x9C; // погасить Кг, Жг, Жв, Зв – "0" в bit6, bit5, bit1 и bit0

Разряды PD2 и PD7 порта PORTD не изменяем. Эту же операцию можно выполнить и в одной строке:

PORTD = 0x18 | PORTD & 0x9C; // зажечь Зг и Кв, погасить Кг, Жг, Жв, Зв

Кроме задания выходного вектора, необходимо задать номер состояния (Q), на которое будет осуществлен переход по выходу из инициализации:

Q = 1;

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