Программная задержка

Необходимые знания: [HW] Kontrollermoodul, [AVR] Arhitektuur, [LIB] Sisend-väljundviigud, [LIB] Viide

Теория

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

Если процессор микроконтроллера считает при помощи чисел, бинарная форма которых такая же широкая как и внутренняя шина (в случае AVR – 8 бит), тогда одно арифмитическое действие, например, прибавление к числу единицы, занимает времени в 1 рабочий такт процессора. Для того, чтобы оперировать тысячами или миллионами, число должно быть 16- или 32-битным, и для их вычисления 8 битный процессор тратит больше чем 1 рабочий такт. При больших числах нужно знать внутренне устройство процессора, a точнее систему его команд.

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

Практика

Далее приведён пример возникновения программных задержек в AVR микроконтроллере. Программный отрезок, который читает в for-цикле переменную x от нуля до ста, написан на языке Си. Внутри каждого цикла происходит заполнение не задействованной пустой инструкции. Это требуется, так как оставляя содержание цикла пустым, компилятор оптимирует цикл из программы совсем, потому что это, по его мнению, бесполезно.

unsigned char x;
 
// Цикл до того, пока х не станет 100
for (x = 0; x < 100; x++)
{
	// Nop с пустой инструкцией
	asm volatile ("nop");
}

Здесь приведен тот же отрезок программы на языке Си только после компиляции. Два левосторонних шестнадцатеричных числа являются машинным кодом и справа находится приказ на языке ассемблер вместе с операндом(операндами). Машинный код и язык ассемблер связаны между собой, ассемблер имеется просто для представления машинного кода в читаемой форме. При компиляции использованa оптимизация длинны программы (параметр компилятора –Os).

80 e0     ldi  r24, 0x00   ; r24 загрузка числа 0 в регистр
00 00     nop              ; Пустая операция
8f 5f     subi r24, 0xFF   ; r24 вычитание числа 255 из регистрa, т.е. прибaвлениe +1
84 36     cpi  r24, 0x64   ; r24 сравнение регистра с числом 100
e1 f7     brne .-8         ; Если сравнение было неверным, то переход обратно на 8 байт

В компилированной форме видно, что на самом деле происходит в цикле языкa Си и на основании этого, можно вычислить сколько тактов приходится на заполнение одного периода цикла. Информация о действиях инструкций и рабочем времени находится на странице данных системы команд AVR. В данном примере на один период цикла для заполнения 4 инструкций приходится 4 такта, потому что все инструкции запрашивают один рабочий такт. В дополнении к этому до начала цикла расходуется 1 такт для инструкции по загрузке и 1 такт для выхода из цикла. Если предположить, что рабочий такт контроллера 14,7456 MHz, то можно рассчитать возникшую временную задержку всего программного отрезка:

(1 + 100 ⋅ 4 + 1) / 14745600 = 27,26 μs

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

Целью данного задания не является создание точной программной задержки на уровне машинного кода, потому что это довольно тонкая работа и к тому же функции для создания задержки уже имеются в avr-libc и библиотеке Домашней Лаборатории. Они также используются в следующих примерах.

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

Практика

Следующий программный код относится к функции программной задержки sw_delay_ms, что создает параметром count заданную задержку в миллисекунду. Функция, в свою очередь, использует наполовину написанную на языке ассемблер функцию _delay_ms библиотеки avr-libc. Причина, по которой в заданиях сразу не используется _delay_ms в том, что с _delay_ms при длинных задержках могут возникнуть проблемы. Функция sw_delay_ms даёт возможность без проблем создавать задержки до 65535 мс.

//
// Программная задержка в миллисекундах
//
void sw_delay_ms(unsigned short count)
{
	// Чтение задержки переменной до нуля
	while (count-- > 0)
	{
		// 1 мс задержка специальной функции
		_delay_ms(1);
	}
}

Для использования приведенной функции существует следующая программа, которая создает в бесконечном цикле две задержки: 100 мс и 900 мс. В течении короткой задержки LED горит и в течении длинной - гаснет, и в результате периодически мигает.

//
// Программа для демонстрации программной задержки Домашней Лаборатории.
// Программа мигает LED-ом после ~1 секунды.
//
#include <homelab/pin.h>
#include <homelab/delay.h>
 
//
// Тест обозначения вывода LED-а
//
pin debug_led = PIN(B, 7);
 
//
// Основная программа
//
int main(void)
{
	// Настройка вывода LED-а выходом
	pin_setup_output(debug_led);
 
	// Безконечный цикл	
	while (true)
	{
		// Зажигание LED-а
		pin_clear(debug_led);
 
		// Программная пауза в 100 миллисекунд
		sw_delay_ms(100);
 
		// Отключение LED-а
		pin_set(debug_led);
 
		// Программная пауза в 900 миллисекунд
		sw_delay_ms(900);
	}
}

Несмотря на то, что кажется, что LED мигает через 1 секунду, действительное время немного длиннее, так как вызовы функций LED-а и задержки такты микроконтроллера занимают некоторое время.

ru/examples/timer/software_delay.txt · Last modified: 2020/07/20 09:00 by 127.0.0.1
CC Attribution-Share Alike 4.0 International
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0