Прежде всего, для работы с прерываниями, в коде программы необходимо подключить библиотеку «interrupt.h» и разрешить как глобальные, так и необходимые нам прерывания. В первом примере будет рассмотрено прерывание по переполнению таймера T0. При поступлении запроса на прерывание от таймера каждый раз будет выполнен сдвиг разрядов порта B влево. В результате чего мы получим бегущую волну, которая будет работать вне зависимости от того, какую программу выполняет микроконтроллер. При этом следует учесть то, что микроконтроллер работает от внутреннего тактового генератора частотой 8 МГц, и ножки предназначенные для подключения кварцевого резонатора, которые также являются двумя старшими разрядами порта B свободны. В связи с этим можно задействовать все 8 разрядов порта B. Если же Вы будете работать от внешнего тактового генератора, то задействовать можно будет лишь 6 младших разрядов порта B.
Рисунок 1 — Схема подключения |
Итак, если тактовая частота внутреннего генератора равна 8 МГц, а переполнение таймера наступает каждый 256 такт, то выбираем значение предделителя таймера, равное CLK/1024, получаем прерывание примерно каждые 33 миллисекунды. Этого вполне достаточно чтобы увидеть бегущую волну. Теперь, когда мы определились с задачей и параметрами, можно приступать к написанию программы:
Уроки С++. Изучай и оптимизируй! Советы С++. Оптимизация цикла!
Источник: avrprog.blogspot.com
Операторы break, continue и goto
Начнем с оператора break. Предположим, нам нужно написать программу подсчета суммы вещественных чисел, пока пользователь вводит положительные числа. Как только он введет отрицательное значение, подсчет суммы завершается с выводом результата на экран. Такую программу можно реализовать следующим образом:
#include int main(void) { double x, s = 0.0; while(scanf(«%lf», s += x; } printf(«s = %.2fn», s); return 0; }
Вначале объявляются две вспомогательные переменные: x – для ввода текущего вещественного значения; s – для подсчета суммы вещественных положительных чисел.
Далее запускается цикл while, который работает, пока вводятся корректные числовые значения. В теле цикла идет проверка, если введенное значение x отрицательное, то срабатывает оператор break. Этот оператор завершает работу цикла while и управление передается следующему оператору printf(). Если же введенное число неотрицательное, то выполняется подсчет суммы.
Как видите, все предельно просто. Для примера, если в этой программе убрать условие и записать оператор break в теле цикла, то цикл while гарантированно будет прерываться на первой же итерации. Конечно, эту же программу можно было бы записать и без оператора break, изменив условие цикла:
Возможно, даже любую программу можно составить без оператора break. Но его использование может заметно облегчить написание и понимание логики программы. Именно с этой целью он и введен в язык Си, да и во многие другие языки высокого уровня. Отмечу здесь еще один распространенный способ прерывания работы цикла. Если вместо break записать оператор return:
Уроки C# – Прервать задачу – Cancel Task
while(scanf(«%lf», s += x; }
то для цикла while эффект будет такой же, как и от оператора break.
Но, правда, есть и существенное отличие. Как мы уже знаем, оператор return завершает выполнение функции. В данном случае функции main(). Поэтому, как только пользователь введет отрицательное число, то программа просто завершится, результат на экран выведен не будет, так как все операторы после while уже не сработают. В этом ключевое отличие между break и return.
Оператор break позволяет продолжить выполнение функции, и управление передается следующему оператору после цикла. А при return текущая функция завершается сразу. На самом деле, оператор return завершает работу функции и лишь, как следствие, прерывает работу цикла. По той же логике оператор return можно для завершения работы оператора switch, о котором мы с вами ранее уже говорили. И вообще, с помощью return можно прервать работу чего угодно в пределах функции.
Оператор continue
Следующий оператор continue прерывает лишь текущую итерацию цикла. Давайте я покажу принцип его работы на конкретном примере. Пусть мы также вводим с клавиатуры целые числа и хотим вычислить сумму только нечетных чисел. Сделать это можно следующим образом:
#include int main(void) { int x, s = 0; while(scanf(«%d», s += x; } printf(«s = %dn», s); return 0; }
Начало вам уже знакомо, а дальше записан цикл while, который работает пока пользователь вводит корректные данные или не введет число 0. Затем, в теле цикла стоит проверка на четность введенного числа x и если оно четно, то выполняется оператор continue. Как только встретился этот оператор, все остальные после него операторы тела цикла пропускаются и мы переходим к следующей итерации цикла while. В результате, сумма будет вычисляться только для нечетных значений. Конечно, опят же, эту программу можно было бы реализовать и без оператора continue, например, так:
И это вполне рабочий вариант. Здесь все как и с оператором break. Оператор continue введен для удобства программирования и его следует использовать тогда, когда он позволяет упростить логику программы и сделать ее более читабельной. Оператор continue, равно как и оператор break можно использовать не только в цикле while, но и в циклах for и do-while. Например, если нам нужно из диапазона целых чисел [1; 100] отобразить только те, что кратны 3 и 5 одновременно, то это можно сделать следующим образом:
#include int main(void) for(int i = 1; i 100; ++i) { if(i % 3 != 0 return 0; }
Это учебный пример. В этой задаче использование оператора continue избыточно и проще было бы вызывать функцию printf() по обратному условию. Я лишь показываю, что операторы continue и break допустимо применять с любыми операторами циклов.
Оператор goto
Последний оператор, который мы рассмотрим на этом занятии, это goto. Он позволяет передать управление любому другому оператору по указанной метке в пределах текущей функции (в пределах той функции, в которой вызывается данный оператор).
На многих обучающих курсах и учебной литературе по языку Си сразу оговаривается, что оператор goto не следует использовать в своих программах, особенно, начинающим программистам. И я с этим полностью согласен! Но, существует, по крайне мере, одна ситуация, когда применение этого оператора оправданно. Это прерывание работы вложенных циклов.
Давайте я детально поясню о чем здесь речь, а позже отмечу, почему все же оператор goto лучше не использовать в своих программах. Итак, предположим, что мы вычисляем двойную сумму вида: и хотим прервать вычисления как только слагаемое i-j будет больше нуля. Такую программу можно реализовать следующим образом:
#include int main(void) { int s = 0; for(int i = 1; i 10; ++i) for(int j = 7; j >= 5; —j) { if(i-j > 0) goto exit_sum; s += i — j; } exit_sum: printf(«s = %dn», s); return 0; }
Смотрите, здесь два цикла, один вложен в другой.
В теле второго цикла выполняется проверка на положительность значения i-j. Если это так, то по условию задания нам нужно прервать работу обоих циклов и завершить подсчет суммы. Как раз для этого удобно воспользоваться оператором goto и по метке exit_sum (имя мы придумываем сами) переходим к оператору printf(), стоящему после этих циклов. В результате такого перехода работа циклов будет завершена. Возможно, некоторые из вас сейчас в недоумении смотрят на эту программу и задаются вопросом: а почему бы здесь не использовать оператор break, он же как раз и позволяет прервать цикл? Все верно, цикл (один цикл) он может прервать и если записать программу в виде:
for(int i = 1; i 10; ++i) for(int j = 7; j >= 5; —j) { if(i-j > 0) break; s += i — j; }
то по условию завершится работа внутреннего цикла, но внешний продолжит работать и на новой итерации опять запустит внутренний цикл.
Очевидно, это не то, что нам нужно. Запомните, оператор break прерывает только один текущий цикл и не более того. Начинающие программисты здесь часто делают ошибку. Поэтому в нашей задаче вместо break используется оператор goto и, как мы видели, он позволяет прервать и два и три и более вложенных циклов при переходе по метке за их пределы, но в пределах текущей функции.
Это тот редкий случай, когда применение этого оператора оправданно. Но почему тогда к этому оператору такое негативное отношение со стороны программистского сообщества языка Си? Все очень просто.
Если злоупотреблять использованием этого оператора, то логика работы программы очень быстро становится очень запутанной, настолько, что даже сам автор перестанет в ней ориентироваться. Соответственно читать и редактировать такие программы чрезвычайно сложно. Второй момент связан с компиляцией.
Если программа написана без операторов goto, то, скорее всего, при более-менее грамотном подходе к программированию, на выходе получим качественный машинный код, который будет выполняться предельно быстро. Если же в программе присутствуют постоянные переходы goto, то никто не даст гарантии качественной трансляции такого текста в машинный код.
Соответственно, главный плюс языка Си сходит на нет: исполняемый файл не оптимизирован для быстрого выполнения на текущей архитектуре компьютера. Чтобы гарантированно избежать этих недостатков при построении программ, стоит совсем отказаться от применения оператора goto. Исключение может составлять необходимость прерывания сразу нескольких вложенных циклов.
Хотя и здесь мы могли бы обойтись без этого оператора. Часто логику вложенных циклов реализуют в отдельных функциях. И как только появляется необходимость прервать их работу, завершают функцию оператором return. Это более частый вариант по сравнению с оператором goto. Именно так я рекомендую поступать при проектировании своих программ.
Источник: proproprogs.ru
Обработка прерываний на языке Си
Для выполнения системы операций (например, ввода, вывода) кроме обращения к регистрам выполняются прерывания, обеспечивающие приостановку выполнения головной программы. В языке Си для этого имеются следующие функции. Так функция
intbdos (int dosfan, unsigned dosdx, unsigned dosal)
выполняет прерывание 21h со значением регистров
AH = dosfun, DX = dosdx, AL = dosal
Возвращаемое значение – содержимое регистра АХ.
Самым простым обработчиком прерывания является функция
void geninterrupt (int intr _ num)
В ней номер прерывания задается переменной intr _ num. Перед ее выполнениями необходимо обращение к соответствующим регистрам через псевдопеременные. Необходимо быть внимательным, так как здесь при загрузке регистров возможно переопределение ранее установленных значений. Функция
int int _86 (int intno, union REGS * inregs, union REGS * outregs)
загружает внутренние регистры значениями записанными и объединенными по шаблону union REGS, но начало которого указывает inregs и выполняет прерывание intno.
На выходе из прерывания значения внутренних регистров записываются в объединении по шаблону union REGS, на начало которого указывает outregs. Шаблон union описан в заголовочном файле и представляет объединение двух структур
struct BYTEREGS h;>:
Структура WORDREGS осуществляет доступ к регистрам как к двухбайтовым единицам, а структура BYTEREGS осуществляет доступ к их отдельным байтам. Переменной flags осуществляется доступ к регистру флагов.
Все функции этого прерывания возвращают значение регистра АХ на выходе из прерывания.
int int 86 x (int intno, union REGS * inregs, union REGS * autregs, struct SREGS * segregs)
в отличии от предыдущей эта функция перед прерыванием устанавливает дополнительно сегментные регистры.
int intdos (union REGS * inregs, union REGS* autregs)
аналогична предыдущим функциям, но реализует прерывания 21h
void intr (int intno, struct REGPACK* preg)
загружает внутренние регистры через структуру REGPACK
unsigned r _ ax, r _ bx, r _ cx, r _ dx;
unsigned r _ bx, r _ si, r _ di, r _ ds, r _ cs, r _ flags;
Возвращаемого значения эта функция не имеет.
Структуры.
Несколько переменных (возможно различных типов) логически сгруппированных в месте под одним именем называются структурой.
В структуру можно объединить координаты точки x, y, h, например
Struct (float x, y, h;) p;
Тогда компоненты структуры можно записать так:
и присвоить им значения, например p·x=167, p·y=103, p·h=205.
Эту структуру можно записать еще и так:
Struct point (float x, y, h;) p;
Struct point (float x, y, h;);
Слово point называют тегом структуры, слово p определяет структурную переменную.
В языке Си средством typedef определяются новые имена типов данных. И если записать
Typedef struct POINT
то POINT определит новое имя типа. Тогда можно записать новый тип
Массив из N точек может быть объявлен так:
Если же выше было объявлено имя структуры, например point, то можно записать
Struct point P [N]
Каждый элемент массива P [i] является структурой, состоящей из трех компонентов A [i].x, A [i].y, A[i].h
Структуры могут содержать указатели на другие структуры. В результате можно строить списки, деревья и т.д. Если структура S содержит поле указателя p, то этот указатель обозначается S.p, а указываемый объект *(S.p). Для последнего выражения имеется специальное обозначение
42. Ввод / вывод.
Ввод / вывод связан с работой с файлами. Стандартные функции ввода / вывода находятся в файле stdio, который включается в программу следующей строкой
Участвующие в работе программы файлы определяются в ее начале вместе с переменными. При этом тип файла определяется так
где fp является указателем на файл.
Перед работой файл должен быть открыт следующей функцией
fp= fopen (file _ name, mode),
в котором file _ name строка, содержащая имя файла в том виде, в котором он записан в каталоге.
Mode – символы, определяющие режим открытия файла.
Существуют следующие режимы открытия файла:
r – чтение файла, w – запись в файл, a – пополнение файла, w+ — обновление файла с усечением, r+ — обновление файла без усечения, b – открытие файла в двоичном режиме (возможны комбинации с предыдущими режимами, например “rb”), t – файл открывается в текстовом режиме (также возможны спецификации типа “rt”).
В случае невозможности открытия файла функция fopen() возвращает нуль. Примером открытия файла может быть следующая функция
fp = fopen (²C: // ppf // zao/pcx²,²rb²) –
файл zao.pcx, который находится на диске С под директорией ppt открывается для двоичного чтения.
Закрытие (отключение от программы) файла осуществляет функция
В языке Си существуют следующие вводы/выводы информации
Простейшим механизмом ввода является – чтение одного байта (символа) функцией int geteh (void) из клавиатуры.
В данной функции ключевое слово void означает, что не осуществляется передача параметров.
Int fgete (FILE *p)
Возвращает символ из открытого файла, описанного переменной типа FILE , на которую указывает fp.
Ungete (int ch, FILE * fp)
Возвращает символ int ch назад в поток fp.
Приведенные функции в случае ошибки или исчерпания файла возвращают EOF.
Int fputchar (int ch)
Помещает символ ch в файл стандартного вывода stolout.
Int fputc (int ch, FILE * fp)
Помещает ch в файл fp. Здесь fp – указатель файла. Аналогичные действия выполняют функции putchar и fputc.
В случае ошибки вывода возвращается EOF.
Char * gets (char * string)
Принимает байты со стандартного ввода stdin и размещает их по адресу, задаваемому указателем string. Прием символов прекращается при получении символа ‘/ n’ (нажатие клавиши ENTER).
Char * gets (char * string, int n, FILE * fp)
Принимает байты из файла, описанного именем типа FILE, на который указыввает fp и размещает их по адресу, указанному переменной string. Прием символов прекращается по достижении передачи n символов или при достижении ‘/n’.
int fputs (char * s, FILE * fp)
перемещает байты из строки S в файл fp. Перенос завершается по достижении символа ‘/o’.
Int puts (char * s)
Переносит байты из строки S в стандартный вывод. Вывод завершается по достижении символа ‘/o’.
Int read (void * ptr, int size, intn, FILE * fp)
Считает n элементов размером size , байт в область памяти, определяемую указателем ptr, из открытого файла, описываемого переменной типа FILE, на которую указывает fp.
В случае успеха возвращает число прочитанных элементов, в случае ошибки и конца чтения файла – EOF.
Int getw (FILE *fp)
Возвращает число байт типа int, считанное из открытого файла, описываемого переменной типа FILE, на которую указывает fp.
Int fwrite (void * ptr, int size, int n, FILE * fp)
Записывает в файл с указателем fp n элементов из области памяти, определенной указателем ptr.
Int scanf (char * format [, указатель]…)
Читает литеры из стандартного входного потока, интерпритирует их согласно спецификациям стринга format и рассылает результаты в свои остальные аргументы.
Int fscanf (FILE * fp, char * format [, указатель]…)
осуществляет ввод данных с файла, определенного переменной, на которую указывает fp.
Int sscanf (char * buffer, char * format [, указатель]…)
Читает из стринга, на который указывает buffer согласно формату format и рассылает полученные значения в аргументы, которые должны быть соответствующими указателями. По исчерпании файла функции сообщают EOF.
Форматный вывод осуществляется функциями
Int printf (char * format , аргумент…)
Int printf (FILE * fp, char * format , аргумент…)
Int printf (char * buffer, char * format , аргумент…)
Все приведенные функции преобразуют, форматируют и печатают свои аргументы. Первая осуществляет стандартный вывод, вторая вывод содержимого в файл, который указывается fp, а третья осуществляет вывод вbuffer.
В форматном вводе/выводе спецификация преобразования имеет вид
% [флаг][ширина] [точность] [размер] тип
В таблице 6 приведена спецификация преобразования для функции print
Источник: allrefrs.ru