Научная статья на тему 'ОСОБЕННОСТИ ОРГАНИЗАЦИИ СИНХРОННЫХ И АСИНХРОННЫХ ВЫЧИСЛИТЕЛЬНЫХ ПРОЦЕССОВ В МНОГОЗАДАЧНЫХ МИКРОПРОЦЕССОРНЫХ СИСТЕМАХ'

ОСОБЕННОСТИ ОРГАНИЗАЦИИ СИНХРОННЫХ И АСИНХРОННЫХ ВЫЧИСЛИТЕЛЬНЫХ ПРОЦЕССОВ В МНОГОЗАДАЧНЫХ МИКРОПРОЦЕССОРНЫХ СИСТЕМАХ Текст научной статьи по специальности «Компьютерные и информационные науки»

CC BY
22
11
i Надоели баннеры? Вы всегда можете отключить рекламу.
Ключевые слова
АСИНХРОННОЕ ВЫПОЛНЕНИЕ

Аннотация научной статьи по компьютерным и информационным наукам, автор научной работы — Якунин Алексей Николаевич

Предложен способ организации синхронных и асинхронных вычислений в микропроцессорных системах, в которых должно выполняться множество задач и приведены максимально подробные фрагменты програм- много кода на языке Си с комментариями, обеспечивающие практическую реализацию предложенного способа.The way of organizing synchronous and asynchronous calculations in microprocessor systems, in which a lot of the tasks must be accomplished, has been proposed; the maximally detailed fragments of the C program code with comments, providing the offered method practical realization, have been presented.

i Надоели баннеры? Вы всегда можете отключить рекламу.

Похожие темы научных работ по компьютерным и информационным наукам , автор научной работы — Якунин Алексей Николаевич

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

Текст научной работы на тему «ОСОБЕННОСТИ ОРГАНИЗАЦИИ СИНХРОННЫХ И АСИНХРОННЫХ ВЫЧИСЛИТЕЛЬНЫХ ПРОЦЕССОВ В МНОГОЗАДАЧНЫХ МИКРОПРОЦЕССОРНЫХ СИСТЕМАХ»

МИКРОПРОЦЕССОРНАЯ ТЕХНИКА

УДК 004.415.2.043

Особенности организации синхронных и асинхронных вычислительных процессов в многозадачных микропроцессорных системах

А.Н.Якунин

Московский государственный институт электронной техники (технический университет)

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

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

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

Очень часто перед разработчиком программного обеспечения возникают трудности, связанные с организацией очередности выполнения синхронных и асинхронных задач. Даже если обработку асинхронных задач возложить на прерывания, все равно вопрос определения очередности выполнения синхронных задач остается сложным, особенно если количество таких задач велико и периоды их выполнения не одинаковы. В литературе при освещении подобного рода задач их решение возложено на операционную систему [1]. Однако в специализированных вычислительных машинах зачастую нецелесообразно использовать операционные системы, например, из-за ограниченного объема встроенной памяти.

В литературных источниках приводятся общие подходы к проектированию, тестированию, отладке и сопровождению программного обеспечения [2, 3]. К сожалению, общие рекомендации не могут помочь программисту-разработчику вычислительных машин в решении проблемы очередности выполнения синхронных и асинхронных задач.

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

© А.Н.Якунин, 2009

сти определения временных интервалов. Итак, предположим, что в нашем случае минимальным квантом времени будет 1 мс. Опишем переменную времени time_ms:

#define TIME_TYPE unsigned long int TIME_ TYPE time_ms;

В этом описании переменная time_ms имеет разрядность 32, следовательно, максимальное значение Nmax, которое может принять переменная, равно:

Nmax = 232 = 4294967295.

На первый взгляд число более 4-х миллиардов кажется огромным, но если пересчитать это количество миллисекунд в другие интервалы времени, то окажется, что оно приблизительно равно 50 суткам. Это означает, что через 50 суток непрерывной работы переменная time_ms достигнет своего максимального значения, сбросится в 0 и начнется отсчет с начала. Если время непрерывной работы системы меньше 50 суток, то об этом предупреждении можно забыть, однако в системах с большим непрерывным временем работы необходимо принимать дополнительные меры, например увеличивать разрядности переменной time_ms до 64, что увеличит интервал времени до переполнения еще в 4 с лишним миллиарда раз. Это с огромным запасом гарантирует непрерывную работу счетчика без переполнения на любой срок эксплуатации электронной аппаратуры.

Итак, необходимо организовать работу таймера таким образом, чтобы переменная time_ms увеличивалась на единицу каждую миллисекунду. Рассмотрим практическую реализацию, например в микроконтроллере AtmelATMEGA 128. Допустим, микроконтроллер тактируется частотой 12 МГц. В качестве тактового сигнала счетчика выберем тактовую частоту микроконтроллера с предделителем на 8. Таким образом, таймер будет работать с частотой 12 Мгц/8 = 1500 КГц. Во время инициализации работы таймера № 3 выберем режим CTC (режим сброса таймера при совпадении). В этом режиме счетчик считает до значения, записанного в специальный регистр OCR, после чего сбрасывается и заново начинает счет с нуля. За более детальными пояснениями следует обратиться к документации на микроконтроллер на Интернет-сайте производителя [4]. Во время сброса микроконтроллер может генерировать прерывание. Очевидно, что для отсчета временных интервалов 1 мс необходимо сброс счетчика осуществлять каждые 1500 (0x05dc) тактов. Таким образом, фрагмент кода, обеспечивающий инициализацию работы счетчика, будет иметь следующий вид:

// Timer/Counter 3 initialization

// Clock source: System Clock = 12 000 KHz

// Clock value: 1500,000 kHz

// Mode: CTC top=OCR3A

// Noise Canceler: Off

// Input Capture on Falling Edge

// OC3A output: Discon.

// OC3B output: Discon.

// OC3C output: Discon.

// Timer 3 Overflow Interrupt: Off

// Input Capture Interrupt: Off

// Compare A Match Interrupt: On

// Compare B Match Interrupt: Off

// Compare C Match Interrupt: Off

TCCR3A=0x00;

TCCR3B=0x0A;

TCNT3H=0x00;

TCNT3L=0x00;

ICR3H=0x00;

ICR3L=0x00;

0CR3AH=0x05;

0CR3AL=0xdc;

0CR3BH=0x00;

0CR3BL=0x00;

0CR3CH=0x00;

0CR3CL=0x00;

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

// Подпрограмма обработки прерывания при сбросе таймера

interrupt [TIM3 C0MPA] void timer3 compa isr(void)

{

time ms++;

}

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

Приведенные фрагменты программного кода после разрешения прерываний инструкцией #asm("sei") обеспечат непрерывный отсчет временных интервалов длительностью 1 мс.

Рассмотрим фрагмент программного кода с описанием структуры заданий. В этом фрагменте MAXJOBCOUNT - это максимальное количество задач, которые могут находиться в очереди на выполнение одновременно. Параметр структуры time - это время (в единицах квантов времени), когда задание должно быть выполнено. Параметр id -это идентификатор задания. Он необходим для того, чтобы микроконтроллер мог отличать различные задания между собой. Для этого каждое задание должно иметь свой уникальный идентификатор id. Самой структуре дадим название job и оформим как массив с количеством элементов MAX JOB COUNT+1. Количество элементов специально выбрано на единицу больше для того, чтобы JOB[0] можно было использовать в служебных целях, в частности для сортировки списка заданий по времени выполнения:

/* Структура заданий */

#define MAX J0B C0UNT 50 // Максимальное количество задач

Struct ram structure

{

TIME TYPE time; // Время выполнения задание

Char id; // ID задания } job[MAX_J0B_C0UNT+1];

Char job count; // Количество заданий в очереди.

TIME TYPE next job time; // Время исполнения след. зада-

чи.

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

#define false 0 // Определить «ложь»

#define true 1 // Определить «истину»

//--------------------------------------------------------

// отсортировать задания по времени выполнения void sort job(void){ char ifj;

if (job count>1) {

for (i=1; (i<=job count-1); i + + )

for (j=i + 1; j<=job count; j+ + ) {

if (job[i].time>job[j].time) {

job[0]=job[i]; job[i]=job[j];

job[j]=job[0]; }

}

next job time=job[1].time;

}

}

//--------------------------------------------------------

// удалить текущее (самое старое) задание void del current job(void){ char i;

for (i=1; (i<=job count-1); i + + ) {

job[i]=job[i + 1];

}

job count=job count-1;

next job time=job[1].time; }

//--------------------------------------------------------

// Найти в списке заданий и удалить задание с указанным ID // Возвращает true если было что удалять // Возвращает false если задание не было найдено char del job(char ID){ char i,k,b; k=0;

b=false;

if (job_count==0) return b;

for (i=1; (i<=job count); i + + ) {

if (job[i].id==ID) k=i; // Найти номер задания

}

if ((k>0) & (k<job count)) // Если такое задание есть {

for (i=k; (i<=job count-1); i+ + ) {

job[i]=job[i+1]; // Сдвинуть все задания

}

job count=job count-1; b=true;

next job time=job[1].time; return b;

}

if (k==job count) // Если такое задание последнее

{

job count=job count-1;

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

b=true;

return b;

}

return b; }

//--------------------------------------------------------

// Добавить новое задание

void add_job(TIME_TYPE time, char id){

job count++;

job[job count].time=time; job[job count].id=id;

sort job(); }

Подпрограмма addJob(TIME_TYPE time, char id) обеспечивает добавление в список нового задания с идентификатором id, которое должно быть обработано во время time.

Подпрограмма delcurrentjob(void) удаляет самое первое задание из очереди на выполнение. Следует понимать, что после исполнения какого-либо задания, запрос о его выполнении необходимо убрать из очереди. Это и выполняет данная подпрограмма.

Функция del_job(char ID) находит в списке заданий задачу с идентификатором ID и удаляет ее. Это необходимо, когда по любым причинам задание, которое поставлено в очередь, необходимо отменить. Помимо этого функция del_job возвращает логическое значение «истина» или «ложь» в зависимости от того, найдена задача с указанным ID или нет.

Этот небольшой набор подпрограмм обеспечивает комфортный способ управления списком заданий. Каждая из подпрограмм после выполнения формирует массив задач, отсортированный в порядке выполнения. Теперь программисту необходимо дать каждому названию уникальный номер (идентификатор) и названия. В приведенном примере задачи имеют названия JOB_1, JOB_2 и т.д., в практической же реализации им можно дать более осмысленные значения в зависимости от прикладной задачи:

#define JOB_NONE0 #define JOB_1 0x01 #define JOB_2 0x02

#define JOB_N 0xNN #define period 175 #define period_21000

#define period N10

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

add job(time ms+10,job 1); add job(time ms+1000,job 2); add job(time ms,job N);

В приведенном выше примере в список заданий добавятся еще три. Среди них job_1 первый раз выполнится через 10 мс, и должно выполняться каждые 75 мс, job_2 будет выполнено через 1 с и будет повторятся через 1 с, а job_N должно быть выполне-

но немедленно и должно иметь период повторения 10 мс. Обработчики этих заданий должны быть описаны в отдельных подпрограммах. Пусть они будут называться, например, work_1, work_2 и work_N:

//--------------------------------------------------------

// Подпрограмма обработки задачи J0B 1 void work 1(void){

// Тут должен быть программный код J0B 1 add job(next job time+period 1, J0B 1) ;

}

//--------------------------------------------------------

// Подпрограмма обработки задачи J0B 2 void work 2(void){

// Тут должен быть программный код J0B 2 add job(next job time+period 2, J0B 2);

}

//--------------------------------------------------------

// Подпрограмма обработки задачи J0B_N void work N(void){

// Тут должен быть программный код J0B_N add job (next job time+period N,J0B N) ;

}

Тогда основной код программы, исполняемый циклически, должен иметь следующий вид:

while (true) // основной цикл программы if (job count>0)// если есть в очереди задания

if (next job time<=time ms) // если время пришло {

switch (job[1].id){

case J0B 1: // Задача 1 work 1(); break;

case J0B_N: // Задача N work N(); break; }; //switch

del current job(); }

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

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

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

add job(next job time+period i,J0B i);

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

add job(time ms+period i,JOB i);

В этом случае, если выполнение задачи задержится на некоторый интервал времени, следующий запуск будет осуществлен через интервал времени period i от момента запуска задержанной задачи.

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

Литература

1. Гагарина Л.Г., Виснадул Б.Д., Игошин А.В. Основы технологии разработки программных продуктов. - М.: ФОРУМ-ИНФРА-М, 2006. - 192 с.

2. Орлов С. А. Технологии разработки программного обеспечения: Учеб. для вузов. Разработка сложных программных систем. - СПб: Питер, 2002. - 464 с.

3. Гагарина Л.Г., Кокорева Е.В., Виснадул Б.Д. Технология разработки программного обеспечения. -М.: ИД «ФОРУМ»-ИНФРА-М, 2008. - 400 с.

4. http ://www. atmel. com.

Статья поступила 7 ноября 2008 г.

Якунин Алексей Николаевич - кандидат технических наук, доцент кафедры вычислительной техники МИЭТ. Область научных интересов: разработка биометрических систем идентификации, охранных систем широкого применения с использованием GSM и GPS технологий, дистанционного мониторинга и управления, отказоустойчивых вычислительных систем.

Информация для читателей журнала «Известия высших учебных заведений. Электроника»

Вы можете оформить подписку на 2009 г. в редакции с любого номера. Стоимость одного номера - 600 руб. (с учетом всех налогов и почтовых расходов).

Адрес редакции: 124498, Москва, Зеленоград, проезд 4806, д. 5, МИЭТ, комн. 7232 Тел.: 8-499-734-62-05. Факс: 8-499-710-54-29. E-mail: magazine@miee.ru http://www.miet.ru/static/je/os.html

i Надоели баннеры? Вы всегда можете отключить рекламу.