Программирование контроллеров для начинающих своими руками пошаговая инструкция

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

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

Что такое Arduino и для чего оно нужно?

Arduino — это электронный конструктор, который позволяет любому человеку создавать разнообразные электро-механические устройства. Ардуино состоит из программной и аппаратной части. Программная часть включает в себя среду разработки (программа для написания и отладки прошивок), множество готовых и удобных библиотек, упрощенный язык программирования. Аппаратная часть включает в себя большую линейку микроконтроллеров и готовых модулей для них. Благодаря этому, работать с Arduino очень просто!

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

проекты на Arduino

проекты на Arduino

Стартовый набор Arduino

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

Базовый набор ардуино для начинающих: Купить
Большой набор для обучения и первых проектов: Купить
Набор дополнительных датчиков и модулей: Купить
Ардуино Уно самая базовая и удобная модель из линейки: Купить
Беспаечная макетная плата для удобного обучения и прототипирования: Купить
Набор проводов с удобными коннекторами: Купить
Комплект светодиодов: Купить
Комплект резисторов: Купить
Кнопки: Купить
Потенциометры: Купить

Среда разработки Arduino IDE

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

Язык программирования Ардуино

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

Для программирования Arduino используется упрощенная версия языка C++ с предопределенными функциями. Как и в других Cи-подобных языках программирования есть ряд правил написания кода. Вот самые базовые из них:

  • После каждой инструкции необходимо ставить знак точки с запятой (;)
  • Перед объявлением функции необходимо указать тип данных, возвращаемый функцией или void если функция не возвращает значение.
  • Так же необходимо указывать тип данных перед объявлением переменной.
  • Комментарии обозначаются: // Строчный и /* блочный */

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

Все прошивки для Arduino должны содержать минимум 2 функции. Это setup() и loop().

Функция setup

Функция setup() выполняется в самом начале и только 1 раз сразу после включения или перезагрузки вашего устройства. Обычно в этой функции декларируют режимы пинов, открывают необходимые протоколы связи, устанавливают соединения с дополнительными модулями и настраивают подключенные библиотеки. Если для вашей прошивки ничего подобного делать не нужно, то функция все равно должна быть объявлена. Вот стандартный пример функции setup():

void setup() {
	Serial.begin(9600);	// Открываем serial соединение
	pinMode(9, INPUT);	// Назначаем 9 пин входом
	pinMode(13, OUTPUT); // Назначаем 13 пин выходом
}

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

Функция loop

Функция loop() выполняется после функции setup(). Loop в переводе с английского значит «петля». Это говорит о том что функция зациклена, то есть будет выполняться снова и снова. Например микроконтроллер ATmega328, который установлен в большинстве плат Arduino, будет выполнять функцию loop около 10 000 раз в секунду (если не используются задержки и сложные вычисления). Благодаря этому у нас есть большие возможности.

Макетная плата Breadbord

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

Макетная плата на 800 точек с 2 шинами питания, платой подачи питания и проводами: Купить
Большая макетная плата на 1600 точек с 4 шинами питания: Купить
Макетная плата на 800 точек с 2 шинами питания: Купить
Макетная плата на 400 точек с 2 шинами питания: Купить
Макетная плата на 170 точек: Купить
Соединительные провода 120 штук: Купить

Первый проект на Arduino

Давайте соберем первое устройство на базе Ардуино. Мы просто подключим тактовую кнопку и светодиод к ардуинке. Схема проекта выглядит так:

Управление яркостью светодиода

Управление яркостью светодиода

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

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

 // переменные с пинами подключенных устройств
int switchPin = 8;
int ledPin = 11;

// переменные для хранения состояния кнопки и светодиода
boolean lastButton = LOW;
boolean currentButton = LOW;
boolean ledOn = false;

void setup() {
	pinMode(switchPin, INPUT);
	pinMode(ledPin, OUTPUT);
}

// функция для подавления дребезга
boolean debounse(boolean last) {
	boolean current = digitalRead(switchPin);
	if(last != current) {
		delay(5);
		current = digitalRead(switchPin);
	}
	return current;
}

void loop() {
	currentButton = debounse(lastButton);
	if(lastButton == LOW && currentButton == HIGH) {
		ledOn = !ledOn;
	}
	lastButton = currentButton;
	digitalWrite(ledPin, ledOn);
}

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

ШИМ Arduino

Широтно-импульсная модуляция (ШИМ) — это процесс управления напряжением за счет скважности сигнала. То есть используя ШИМ мы можем плавно управлять нагрузкой. Например можно плавно изменять яркость светодиода, но это изменение яркости получается не за счет уменьшения напряжения, а за счет увеличения интервалов низкого сигнала. Принцип действия ШИМ показан на этой схеме:

ШИМ ардуино

ШИМ ардуино

Когда мы подаем ШИМ на светодиод, то он начинает быстро зажигаться и гаснуть. Человеческий глаз не способен увидеть это, так как частота слишком высока. Но при съемке на видео вы скорее всего увидите моменты когда светодиод не горит. Это случится при условии что частота кадров камеры не будет кратна частоте ШИМ.

В Arduino есть встроенный широтно-импульсный модулятор. Использовать ШИМ можно только на тех пинах, которые поддерживаются микроконтроллером. Например Arduino Uno и Nano имеют по 6 ШИМ выводов: это пины D3, D5, D6, D9, D10 и D11. В других платах пины могут отличаться. Вы можете найти описание интересующей вас платы в этом разделе.

Для использования ШИМ в Arduino есть функция analogWrite(). Она принимает в качестве аргументов номер пина и значение ШИМ от 0 до 255. 0 — это 0% заполнения высоким сигналом, а 255 это 100%. Давайте для примера напишем простой скетч. Сделаем так, что бы светодиод плавно загорался, ждал одну секунду и так же плавно угасал и так до бесконечности. Вот пример использования этой функции:

 // Светодиод подключен к 11 пину
int ledPin = 11;

void setup() {
	pinMode(ledPin, OUTPUT);
}

void loop() {
	for (int i = 0; i < 255; i++) {
		analogWrite(ledPin, i);
		delay(5);
	}

	delay(1000);

	for (int i = 255; i > 0; i--) {
		analogWrite(ledPin, i);
		delay(5);
	}
}

Аналоговые входы Arduino

Как мы уже знаем, цифровые пины могут быть как входом так и выходом и принимать/отдавать только 2 значения: HIGH и LOW. Аналоговые пины могут только принимать сигнал. И в отличии от цифровых входов аналоговые измеряют напряжение поступающего сигнала. В большинстве плат ардуино стоит 10 битный аналогово-цифровой преобразователь. Это значит что 0 считывается как 0 а 5 В считываются как значение 1023. То есть аналоговые входы измеряют, подаваемое на них напряжение, с точностью до 0,005 вольт. Благодаря этому мы можем подключать разнообразные датчики и резисторы (терморезисторы, фоторезисторы) и считывать аналоговый сигнал с них.

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

Подключение фоторезистора к Ардуино

Подключение фоторезистора к Ардуино

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

int sensePin = 0; // Пин к которому подключен фоторезистор
 
void setup() {
 analogReferense(DEFAULT); // Задаем опорное значение напряжения. Эта строка не обязательна.
 Serial.begin(9600); // Открываем порт на скорости 9600 бод.
}
 
void loop() {
 Serial.println(analogRead(sensePin)); // Считываем значение и выводим в порт
 delay(500); // задержка для того что бы значений было не слишком много
}

Вот так из двух простейших элементов и четырех строк кода мы сделали датчик освещенности. На базе этого устройства мы можем сделать умный светильник или ночник. Очень простое и полезное устройство.

Вот мы и рассмотрели основы работы с Arduino. Теперь вы можете сделать простейшие проекты. Что бы продолжить обучение и освоить все тонкости, я советую прочитать книги по ардуино и пройти бесплатный обучающий курс. После этого вы сможете делать самые сложные проекты, которые только сможете придумать.

Вы еще не программируете микроконтроллеры? Тогда мы идем к вам!

Время на прочтение
9 мин

Количество просмотров 376K

Здравствуйте, уважаемые Хабражители!

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

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

лучшие времена пришли

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

1. Программатор

На рынке предлагается много вариантов — от самых дешевых ISP (In-System Programming) программаторов за несколько долларов, до мощных программаторов-отладчиков за пару сотен. Не имея большого опыта в этом деле, для начала я решил попробовать один из самых простых и дешевых — USBasp. Купил в свое время на eBay за $12, сейчас можно найти даже за $3-4. На самом деле это китайская версия программатора от Thomas Fischl. Что могу сказать про него? Только одно — он работает. К тому же поддерживает достаточно много AVR контроллеров серий ATmega и ATtiny. Под Linux не требует драйвера.

Для прошивки надо соединить выходы программатора VCC, GND, RESET, SCK, MOSI, MISO с соответствующими выходами микроконтроллера. Для простоты я собрал вспомогательную схему прямо на макетной плате:

image

Слева на плате — тот самый микроконтроллер, который мы собираемся прошивать.

2. Микроконтроллер

С выбором микроконтроллера я особо не заморачивался и взял ATmega8 от Atmel — 23 пина ввода/вывода, два 8-битных таймера, один 16-битный, частота — до 16 Мгц, маленькое потребление (1-3.6 мА), дешевый ($2). В общем, для начала — более чем достаточно.

image

Под Linux для компиляции и загрузки прошивки на контроллер отлично работает связка avr-gcc + avrdude. Установка тривиальная. Следуя инструкции, можно за несколько минут установить все необходимое ПО. Единственный ньюанс, на который следует обратить внимание — avrdude (ПО для записи на контроллер) может потребовать права супер-пользователя для доступа к программатору. Выход — запустить через sudo (не очень хорошая идея), либо прописать специальные udev права. Синтаксис может отличаться в разных версиях ОС, но в моем случае (Linux Mint 15) сработало добавление следующего правила в файл /etc/udev/rules.d/41-atmega.rules:

# USBasp programmer
SUBSYSTEM=="usb", ATTR{idVendor}=="16c0", ATTR{idProduct}=="05dc", GROUP="plugdev", MODE="0666"

После этого, естественно, необходим перезапуск сервиса

service udev restart

Компилировать и прошивать без проблем можно прямо из командной строки (кто бы сомневался), но если проектов много, то удобнее поставить плагин AVR Eclipse и делать все прямо из среды Eclipse.

Под Windows придется поставить драйвер. В остальном проблем нет. Ради научного интереса попробовал связку AVR Studio + eXtreme Burner в Windows. Опять-таки, все работает на ура.

Начинаем программировать

Программировать AVR контроллеры можно как на ассемблере (AVR assembler), так и на Си. Тут, думаю, каждый должен сделать свой выбор сам в зависимости от конкретной задачи и своих предпочтений. Лично я в первую очередь начал ковырять ассемблер. При программировании на ассемблере архитектура устройства становится понятнее и появляется ощущение, что копаешься непосредственно во внутренностях контроллера. К тому же полагаю, что в особенно критических по размеру и производительности программах знание ассемблера может очень пригодиться. После ознакомления с AVR ассемблером я переполз на Си.

После знакомства с архитектурой и основными принципами, решил собрать что-то полезное и интересное. Тут мне помогла дочурка, она занимается шахматами и в один прекрасный вечер заявила, что хочет иметь часы-таймер для партий на время. БАЦ! Вот она — идея первого проекта! Можно было конечно заказать их на том же eBay, но захотелось сделать свои собственные часы, с блэк… эээ… с индикаторами и кнопочками. Сказано — сделано!

В качестве дисплея решено было использовать два 7-сегментных диодных индикатора. Для управления достаточно было 5 кнопок — “Игрок 1”, “Игрок 2”, “Сброс”, “Настройка” и “Пауза”. Ну и не забываем про звуковую индикацию окончания игры. Вроде все. На рисунке ниже представлена общая схема подключения микроконтроллера к индикаторам и кнопкам. Она понадобится нам при разборе исходного кода программы:

Разбор полета

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

int main(void)
{
	init_io();
	init_data();
	sound_off();
	sei();

	while(1)
	{
		handle_buttons();
	}
	return 0;
}

Рассмотрим каждую функцию в отдельности.

void init_io()
{
	// set output
	DDRB = 0xFF;
	DDRD = 0xFF;

	// set input
	DDRC = 0b11100000;

	// pull-up resistors
	PORTC |= 0b00011111;

	// timer interrupts
	TIMSK = (1<<OCIE1A) | (1<<TOIE0);

	TCCR0 |= (1 << CS01) | (1 << CS00);

	TCCR1B = (1<<CS12|1<<WGM12);

	//OCRn =  (clock_speed / prescaler) * seconds - 1
	OCR1A = (F_CPU / 256) * 1 -1;
}

Настройка портов ввода/вывода происходит очень просто — в регистр DDRx (где x — буква, обозначающая порт) записивается число, каждый бит которого означает, будет ли соответствующий пин устройством ввода (соответствует 0) либо вывода (соответствует 1). Таким образом, заслав в DDRB и DDRD число 0xFF, мы сделали B и D портами вывода. Соответственно, команда DDRC = 0b11100000; превращает первые 5 пинов порта C во входные пины, а оставшиеся — в выходные. Команда PORTC |= 0b00011111; включает внутренние подтягивающие резисторы на 5 входах контроллера. Согласно схеме, к этим входам подключены кнопки, которые при нажатии замкнут их на землю. Таким образом контроллер понимает, что кнопка нажата.

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

Обработка прерываний

ISR (TIMER0_OVF_vect)
{
	display();

	if (_buzzer > 0)
	{
		_buzzer--;
		if (_buzzer == 0)
			sound_off();
	}
}

ISR(TIMER1_COMPA_vect)
{
	if (ActiveTimer == 1 && Timer1 > 0)
	{
		Timer1--;
		if (Timer1 == 0)
			process_timeoff();
	}

	if (ActiveTimer == 2 && Timer2 > 0)
	{
		Timer2--;
		if (Timer2 == 0)
			process_timeoff();
	}
}

При срабатывании таймера управление передается соответствующему обработчику прерывания. В нашем случае это обработчик TIMER0_OVF_vect, который вызывает процедуру вывода времени на индикаторы, и TIMER1_COMPA_vect, который обрабатывает обратный отсчет.

Вывод на индикаторы

void display()
{
	display_number((Timer1/60)/10, 0b00001000);
	_delay_ms(0.25);

	display_number((Timer1/60)%10, 0b00000100);
	_delay_ms(0.25);

	display_number((Timer1%60)/10, 0b00000010);
	_delay_ms(0.25);

	display_number((Timer1%60)%10, 0b00000001);
	_delay_ms(0.25);

	display_number((Timer2/60)/10, 0b10000000);
	_delay_ms(0.25);

	display_number((Timer2/60)%10, 0b01000000);
	_delay_ms(0.25);

	display_number((Timer2%60)/10, 0b00100000);
	_delay_ms(0.25);

	display_number((Timer2%60)%10, 0b00010000);
	_delay_ms(0.25);

	PORTD = 0;
}

void display_number(int number, int mask)
{
	PORTB = number_mask(number);
	PORTD = mask;
}

Функция display использует метод динамической индикации. Дело в том, что каждый отдельно взятый индикатор имеет 9 контактов (7 для управления сегментами, 1 для точки и 1 для питания). Для управления 4 цифрами понадобилось бы 36 контактов. Слишком расточительно. Поэтому вывод разрядов на индикатор с несколькими цифрами организован по следующему принципу:

Напряжение поочередно подается на каждый из общих контактов, что позволяет высветить на соответствующем индикаторе нужную цифру при помощи одних и тех же 8 управляющих контактов. При достаточно высокой частоте вывода это выглядит для глаза как статическая картинка. Именно поэтому все 8 питающих контактов обоих индикаторов на схеме подключены к 8 выходам порта D, а 16 управляющих сегментами контактов соединены попарно и подключены к 8 выходам порта B. Таким образом, функция display с задержкой в 0.25 мс попеременно выводит нужную цифру на каждый из индикаторов. Под конец отключаются все выходы, подающие напряжение на индикаторы (команда PORTD = 0;). Если этого не сделать, то последняя выводимая цифра будет продолжать гореть до следующего вызова функции display, что приведет к ее более яркому свечению по сравнению с остальными.

Обработка нажатий

void handle_buttons()
{
	handle_button(KEY_SETUP);
	handle_button(KEY_RESET);
	handle_button(KEY_PAUSE);
	handle_button(KEY_PLAYER1);
	handle_button(KEY_PLAYER2);
}

void handle_button(int key)
{
	int bit;
	switch (key)
	{
		case KEY_SETUP: 	bit = SETUP_BIT; break;
		case KEY_RESET: 	bit = RESET_BIT; break;
		case KEY_PAUSE: 	bit = PAUSE_BIT; break;
		case KEY_PLAYER1: 	bit = PLAYER1_BIT; break;
		case KEY_PLAYER2: 	bit = PLAYER2_BIT; break;
		default: return;
	}

	if (bit_is_clear(BUTTON_PIN, bit))
	{
		if (_pressed == 0)
		{
			_delay_ms(DEBOUNCE_TIME);
			if (bit_is_clear(BUTTON_PIN, bit))
			{
				_pressed |= key;

				// key action
				switch (key)
				{
					case KEY_SETUP: 	process_setup(); break;
					case KEY_RESET: 	process_reset(); break;
					case KEY_PAUSE: 	process_pause(); break;
					case KEY_PLAYER1: 	process_player1(); break;
					case KEY_PLAYER2: 	process_player2(); break;
				}

				sound_on(15);
			}
		}
	}
	else
	{
		_pressed &= ~key;
	}
}

Эта функция по очереди опрашивает все 5 кнопок и обрабатывает нажатие, если таковое случилось. Нажатие регистрируется проверкой bit_is_clear(BUTTON_PIN, bit), т.е. кнопка нажата в том случае, если соответствующий ей вход соединен с землей, что и произойдет, согласно схеме, при нажатии кнопки. Задержка длительностью DEBOUNCE_TIME и повторная проверка нужна во избежание множественных лишних срабатываний из-за дребезга контактов. Сохранение статуса нажатия в соответствующих битах переменной _pressed используется для исключения повторного срабатывания при длительном нажатии на кнопку.
Функции обработки нажатий достаточно тривиальны и полагаю, что в дополнительных комментариях не нуждаются.

Полный текст программы

#define F_CPU 						4000000UL

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>


#define DEBOUNCE_TIME 					20

#define BUTTON_PIN 					PINC
#define SETUP_BIT 					PC0
#define RESET_BIT 					PC1
#define PAUSE_BIT 					PC2
#define PLAYER1_BIT 					PC3
#define PLAYER2_BIT 					PC4

#define KEY_SETUP					0b00000001
#define KEY_RESET					0b00000010
#define KEY_PAUSE					0b00000100
#define KEY_PLAYER1					0b00001000
#define KEY_PLAYER2					0b00010000


volatile int ActiveTimer = 0;
volatile int Timer1 = 0;
volatile int Timer2 = 0;

volatile int _buzzer = 0;
volatile int _pressed = 0;


// function declarations

void init_io();
void init_data();
int number_mask(int num);
void handle_buttons();
void handle_button(int key);
void process_setup();
void process_reset();
void process_pause();
void process_timeoff();
void process_player1();
void process_player2();
void display();
void display_number(int mask, int number);
void sound_on(int interval);
void sound_off();

// interrupts

ISR (TIMER0_OVF_vect)
{
	display();

	if (_buzzer > 0)
	{
		_buzzer--;
		if (_buzzer == 0)
			sound_off();
	}
}

ISR(TIMER1_COMPA_vect)
{
	if (ActiveTimer == 1 && Timer1 > 0)
	{
		Timer1--;
		if (Timer1 == 0)
			process_timeoff();
	}

	if (ActiveTimer == 2 && Timer2 > 0)
	{
		Timer2--;
		if (Timer2 == 0)
			process_timeoff();
	}
}


int main(void)
{
	init_io();
	init_data();

	sound_off();

	sei();

	while(1)
	{
		handle_buttons();
	}
	return 0;
}

void init_io()
{
	// set output
	DDRB = 0xFF;
	DDRD = 0xFF;

	// set input
	DDRC = 0b11100000;

	// pull-up resistors
	PORTC |= 0b00011111;

	// timer interrupts
	TIMSK = (1<<OCIE1A) | (1<<TOIE0);

	TCCR0 |= (1 << CS01) | (1 << CS00);

	TCCR1B = (1<<CS12|1<<WGM12);

	//OCRn =  (clock_speed / prescaler) * seconds - 1
	OCR1A = (F_CPU / 256) * 1 -1;
}

void init_data()
{
	Timer1 = 0;
	Timer2 = 0;
	ActiveTimer = 0;
}

int number_mask(int num)
{
	switch (num)
	{
		case 0 : return 0xC0;
		case 1 : return 0xF9;
		case 2 : return 0xA4;
		case 3 : return 0xB0;
		case 4 : return 0x99;
		case 5 : return 0x92;
		case 6 : return 0x82;
		case 7 : return 0xF8;
		case 8 : return 0x80;
		case 9 : return 0x90;
	};

	return 0;
}

void process_setup()
{
	Timer1 += 60;
	Timer2 += 60;

	// overflow check (5940 seconds == 99 minutes)
	if (Timer1 > 5940 || Timer2 > 5940)
	{
		Timer1 = 0;
		Timer2 = 0;
	}
}

void process_reset()
{
	init_data();
}

void process_timeoff()
{
	init_data();

	sound_on(30);
}

void process_pause()
{
	ActiveTimer = 0;
}

void process_player1()
{
	ActiveTimer = 2;
}

void process_player2()
{
	ActiveTimer = 1;
}

void handle_button(int key)
{
	int bit;
	switch (key)
	{
		case KEY_SETUP: 	bit = SETUP_BIT; break;
		case KEY_RESET: 	bit = RESET_BIT; break;
		case KEY_PAUSE: 	bit = PAUSE_BIT; break;
		case KEY_PLAYER1: 	bit = PLAYER1_BIT; break;
		case KEY_PLAYER2: 	bit = PLAYER2_BIT; break;
		default: return;
	}

	if (bit_is_clear(BUTTON_PIN, bit))
	{
		if (_pressed == 0)
		{
			_delay_ms(DEBOUNCE_TIME);
			if (bit_is_clear(BUTTON_PIN, bit))
			{
				_pressed |= key;

				// key action
				switch (key)
				{
					case KEY_SETUP: 	process_setup(); break;
					case KEY_RESET: 	process_reset(); break;
					case KEY_PAUSE: 	process_pause(); break;
					case KEY_PLAYER1: 	process_player1(); break;
					case KEY_PLAYER2: 	process_player2(); break;
				}

				sound_on(15);
			}
		}
	}
	else
	{
		_pressed &= ~key;
	}
}

void handle_buttons()
{
	handle_button(KEY_SETUP);
	handle_button(KEY_RESET);
	handle_button(KEY_PAUSE);
	handle_button(KEY_PLAYER1);
	handle_button(KEY_PLAYER2);
}

void display()
{
	display_number((Timer1/60)/10, 0b00001000);
	_delay_ms(0.25);

	display_number((Timer1/60)%10, 0b00000100);
	_delay_ms(0.25);

	display_number((Timer1%60)/10, 0b00000010);
	_delay_ms(0.25);

	display_number((Timer1%60)%10, 0b00000001);
	_delay_ms(0.25);

	display_number((Timer2/60)/10, 0b10000000);
	_delay_ms(0.25);

	display_number((Timer2/60)%10, 0b01000000);
	_delay_ms(0.25);

	display_number((Timer2%60)/10, 0b00100000);
	_delay_ms(0.25);

	display_number((Timer2%60)%10, 0b00010000);
	_delay_ms(0.25);

	PORTD = 0;
}

void display_number(int number, int mask)
{
	PORTB = number_mask(number);
	PORTD = mask;
}

void sound_on(int interval)
{
	_buzzer = interval;

	// put buzzer pin high
	PORTC |= 0b00100000;
}

void sound_off()
{
	// put buzzer pin low
	PORTC &= ~0b00100000;
}

Прототип был собран на макетной плате:

После тестирования прототипа пришло время все это добро разместить в корпусе, обеспечить питание и т.д.

Ниже показан окончательный вид устройства. Часы питаются от 9-вольтовой батарейки типа “Крона”. Потребление тока — 55 мА.

Заключение

Потратив $20-25 на оборудование и пару вечеров на начальное ознакомление с архитектурой микроконтроллера и основными принципами работы, можно начать делать интересные DIY проекты. Статья посвящается тем, кто, как и я в свое время, думает, что начать программировать микроконтроллеры — это сложно, долго или дорого. Поверьте, начать намного проще, чем может показаться. Если есть интерес и желание — пробуйте, не пожалете!

Удачного всем программирования!

P.S. Ну и напоследок, небольшая видео-демонстрация прототипа:

Микроконтроллер – микросхема, предназначенная для управления электронными устройствами, или по другому – простенький компьютер (микро-ЭВМ), способный выполнять несложные задачи.

Микроконтроллеры AVR: устройство и программирование
Рано или поздно, любой радиолюбитель (я так думаю), приходит к мысли о применении в своих разработках микроконтроллеров. Микроконтроллер позволяет существенно «облегчить» радиолюбительскую конструкцию, сделать ее проще и намного функциональнее.

Что нужно для того, чтобы начать пользоваться всеми возможностями микроконтроллеров? Я считаю, что не так уж и много. Главное в этом деле — желание. Будет желание, будет и результат.

 В этом разделе (и в разделе «Устройство AVR») сайта я постараюсь помочь начинающим «микроконтроллерщикам» сделать первый, он же самый трудный шаг навстречу микроконтроллерам — попробуем разобраться в устройстве и программировании микроконтроллеров AVR семейства ATtiny и ATmega.

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

В своих статьях я буду опираться на материалы из публикаций популярных авторов микроконтроллерной тематики: Рюмика С.М., Белова А.В., Ревича Ю.В., Евстифеева А.В., Гребнева В.В., Мортона Д., Трамперта В., Фрунзе А.В. и Фрунзе А.А. (и многих других), а также материалы радиолюбительских сайтов. Ну и, может быть, немного своих «умных мыслей».


Программирование микроконтроллеров AVR фирмы Atmel


Микроконтроллеры - первый шаг1. Микроконтроллеры — первый шаг

Эта статья, как и все последующие, — маленький шажок в мир микроконтроллеров. И таких «шажков» у нас будет много, пока не дойдем до того момента, когда сможем сказать: «Микроконтроллер — последний шаг». Но и это, скорее всего, из области фантастики — нельзя объять необъятное, — мир микроконтроллеров постоянно развивается и совершенствуется. Наша задача — сделать первый шаг, логическим итогом которого должна стать первая, самостоятельно разработанная и собранная конструкция на микроконтроллере.


Системы счисления2. Системы счисления: десятичная, двоичная и шестнадцатиричная

Как вы наверняка знаете, существует много разных систем счисления, одними пользуются и сейчас (наша, родная, десятичная система; римская система, известная нам как «римские цифры»), другие остались в глубоком прошлом (системы счисления инков и майя, древнеегипитская система, вавилонская).
Тут, я думаю, вопросов у нас нет, что такое системы счисления нам понятно — отображение чисел символами. А вот какая связь систем счисления с микроконтроллерами.


Логические выражения3. Логические операции, логические выражения, логические элементы

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


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


5. Прямой, обратный и дополнительный коды двоичного числа

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


Программатор USBASP AVR6. USBASP программатор для микроконтроллеров AVR — идеальное решение для начинающих, и не только

Сегодня мы рассмотрим как, без особых затрат и быстро, запрограммировать любой микроконтроллер AVR поддерживающий режим последовательного программирования (интерфейс ISP) через USB-порт компьютера. В качестве программатора мы будем использовать очень простой и популярный программатор USBASP, а в качестве программы — AVRdude_Prog V3.3, которая предназначена для программирования МК AVR.


Программа AVRdudeProg7. Программа AVRDUDE_PROG: программирование микроконтроллеров AVR ATmega и ATtiny

Популярнейшая программа AVRDUDE_PROG 3.3 предназначена для программирования микроконтроллеров AVR ATmega и ATtiny


Основы программирования микроконтроллеров8. Основы программирования микроконтроллеров AVR

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


Программа Atmel Studio9. Русификация программы Atmel Studio

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


Почему С10. Введение в язык программирования С (Си) для микроконтроллеров

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


Арифметические операции С11. Переменные и константы в языке С (Си) для микроконтроллеров AVR

В этой статье будут рассмотрены типы переменных в языке С (Си) для микроконтроллеров AVR, объявление переменных, способы задания констант, будет дан обзор арифметических операций языка С, присваивания, инкремента и декремента.


Зажигание светодиода12. Управление портами микроконтроллеров AVR на языке С (Си)

В этой статье будет рассмотрено управление портами микроконтроллеров AVR на языке программирования С (Си): установка выводов порта на вход или выход, считывание значений на входах портов, программа для управления миганием светодиода.



Структура цикла с предусловием13. Циклы в языке С (Си) для микроконтроллеров AVR

В данной статье будут рассмотрены циклы в языке программирования Си для микроконтроллеров AVR. Будут рассмотрены циклы типа «для» (for) и циклы типа «пока» (while), будет показано как осуществить принудительное прерывание цикла и организовать бесконечный цикл.



Что такое массив14. Массивы в программировании микроконтроллеров AVR

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



Что такое конечный автомат15. Конечные автоматы в микроконтроллерах AVR

В данной статье мы рассмотрим применительно к микроконтроллерам AVR такой интересный стиль программирования микроконтроллеров как автоматное программирование. Точнее это даже не стиль программирования а целая концепция, благодаря которой программист микроконтроллеров может существенно облегчить свою жизнь. Благодаря ей многие задачи, поставленные перед программистом, решаются гораздо легче и проще, избавляя программиста от многих сложностей. Автоматное программирование часто также называют Switch-технологией


(29 голосов, оценка: 4,76 из 5)
Загрузка…


Программирование микроконтроллеров AVR

Микроконтроллеры AVR: программирование и применение микроконтроллеров ATmega и ATtiny для начинающих

Published by: Мир микроконтроллеров

Date Published: 04/01/2015

Приветствую всех сограждан и читателей журнала Датагор! Пользуясь кучей времени, предоставленной коронавирусом (даже в нашествии такой гадости можно, при желании, найти положительные моменты), решил поднять и пересмотреть записи по микроконтроллерам (МК), которые я делал в разное время для своих детей. В итоге родилась идея объединить разрозненные материалы. Настоящая работа не является учебным курсом по программированию МК, хотя некоторые его элементы будут присутствовать. Скорее, это попытка осветить путь от написания программы до её загрузки в микроконтроллер. Я лишь расскажу о доступных практически для каждого средствах, необходимых для прохождения этого пути, расставлю «вешки» по всему маршруту и намечу направления. Соединять их вам придётся самостоятельно.

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

Содержание статьи / Table Of Contents

Изначально, записывая «шпаргалки» для детей, я определил пару условий в изложении, чтобы оно, с одной стороны, не отпугнуло читающего в самом начале пути, а с другой — дало бы общее понимание работы микроконтроллеров и снизило барьеры при переходе от одного типа МК к другому:
• Рассматривать несколько МК разных архитектур и, желательно, разных производителей. При этом, давать такое объяснение принципа работы, которое подходило для всех рассматриваемых МК, но не было совсем уж популистским.
• При разборе практических примеров, обойтись без использования специализированной среды программирования (IDE). Тема МК сама по себе не самая примитивная, а необходимость вникать параллельно в работу нескольких IDE для разных МК оптимизма начинающему никак не прибавляет.
Эти же принципы, несколько расширив их, я решил перенести на статью.

Объектами изучения нам послужат микроконтроллеры:


ATTINY85-20PU DIP8


ATMEGA8A-PU


Макетная плата STM32F401


Ebyte SoC nRF52832 тестовая плата
Если точно такой платы не найдёте, можно купить модуль nRF52832 и распаять отладочную плату.

nRF52832
Минимум пинов, которые необходимо вывести:
• GND,
• VCC,
• SWDIO и SWDCLK для программирования и отладки,
• Reset,
• несколько GPIO.
В файловый архив выложен мануал этого модуля.


USBasp

st-link v2

USB-UART адаптер CH340


Макетная плата

Соединительные провода разного типа

Потенциометры

Светодиоды

В качестве среды программирования мы будем использовать обычный блокнот Notepad++, компилировать написанную программу посредством компилятора GCC от GNU. С отладкой кода нам помогут программы Tetminal и PuTTY, а с его загрузкой в МК — avrdude и openocd.

1. Скачать и установить

Notepad++

.
2. При необходимости выбрать русский язык в Settings/Preferences/General/Localization.
3. В Плагины/Управление плагинами, во вкладке Доступные выставить галочки напротив Explorer и NppExec и нажать кнопку Установить.
4. Выставить галочки напротив Плагины/Explorer/Explorer, Плагины/NppExec/Show Console и Follow $(CURRENT_DIRECTORY). Слева и снизу от окна редактора появятся окна проводника и консоли, соответственно.
5. В Опции/Определение стилей выбрать подходящий стиль и для языков С, Makefile и Assembler настроить подходящие цвета и размеры шрифтов.
6. Чтобы настройки стиля вступили в силу в Синтаксисы выбрать A/Assembly, C/C или M/Makefile при работе с соответствующим файлом.

1. Создать на удобном для вас диске папки:
• GNU,
• GNU/AVR,
• GNU/AVR/avrdude,
• GNU/ARM,
• GNU/MinGW.
2. Скачать и распаковать в папку GNU/ARM файлы

Arm GNU Toolchain 6.3.1 — Windows

и 

openocd-20200701.7z

.
Переименовать распакованные папки в armGnuToolchain и OpenOCD.
3. Скачать и распаковать в папку GNU/AVR файл

AVR 8-bit Toolchain v3.62 — Windows

.
Переименовать распакованную папку в avrGnuToolchain.
4. Скачать и распаковать в папку GNU/AVR/avrdude файл

avrdude-6.1-mingw32.zip

.
5. Скачать файл

mingw-get-setup

и запустить его, указав GNU/MinGW как папку для установки. В ходе установки будет запущен MinGW Installation Manager, в котором достаточно выбрать базовый пакет (Basic Setup) и нажать Installation/Apply Changes.

Если с установкой возникли проблемы, можно скачать готовый вариант папки MinGW из архива.
6. В Панель управленияСистемаДополнительные параметры системыПеременные средыПеременные среды для пользователяPath добавить пути к папкам:
• GNUARMarmGnuToolchainbin
• GNUARMOpenOCDbin
• GNUAVRavrGnuToolchainbin
• GNUAVRavrdude
• GNUMinGWmsys1.0bin

7. Перезагрузить компьютер.

Скачать и распаковать на удобном для вас диске программы

Terminal 1.9b

и 

PuTTY

В архив выложены rar-файлы всех упомянутых выше программ.

Драйвер USBasp также выложен в архив, а ST-link v2 устанавливается автоматически при первом подключении к компьютеру. Оба программатора после установки должны отобразиться в «Диспетчере устройств» Windows.

Как бы мне ни хотелось сразу перейти к практике, придётся сделать отступление в теорию. Попытаюсь ограничиться её минимумом, который облегчит понимание практического материала в последующем. Более того, уже в этой главе мы начнём знакомиться с некоторыми реальными командами МК AVR и ARM. Добавлю, что местами буду приводить англоязычный вариант терминов и аббревиатур: все-же чтения даташитов и мануалов вам не избежать, а их издатели упрямо не желают переходить на великий и могучий.

В моём восприятии, как программиста, микроконтроллер — две большие кучи регистров. В первую кучу (память программ) мы загружаем программу в виде последовательностей нулей и единиц. При этом, значительная доля содержания программы сводится к своевременной записи правильного набора нулей и единиц в нужный регистр второй кучи (памяти данных), либо чтению из него этих наборов. В случае языка ассемблер мы имеем дело, по большей части, именно с регистрами памяти данных, поэтому выясним для начала, что они из себя представляют.
Уверен, вам известен такой элемент, как D-триггер (далее — триггер) и его основные свойства:
1. Выход триггера T может находиться только в одном из двух логических состояний — 1 (на выходе — напряжение питания) или 0 (на выходе — земля).
2. Значение (0 или 1) со входа D переносится (записывается) на выход T триггера по фронту синхронизирующего (тактового) сигнала С и сохраняется до следующей записи либо отключения питания.

Ассемблер для микроконтроллера с нуля. Часть 1. Начало пути

Если взглянуть на триггер в плоскости информации, можно сказать, что он хранит 1 бит данных со значением 0 или 1.
Соединив параллельно несколько триггеров, мы и получим регистр, разрядность или битность которого определяется количеством составляющих его триггеров. Совокупность линий данных триггеров регистра принято называть шиной данных. Тактовый сигнал для всех триггеров регистра — единый, т. е. перенос значений с линий шины данных на выходы (запись в регистр) происходит одновременно для всех триггеров. Нумерация битов регистра ведётся справа-налево, начиная с нулевого. Обычно, битность регистров МК кратна восьми (8, 16, 32).

Пример 8-битного регистра приведён на Рисунке 5.

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

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

Не берусь утверждать, что регистры МК организованы на базе именно D-триггеров. Более того, физическая суть записи в регистры памяти программ (flash-память) — совершенно иная. Тем не менее, принципы хранения и движения оперативной информации в микроконтроллерах я постарался передать верно.

В завершение — о единицах измерения хранимой в регистрах информации и некоторых общепринятых терминах.

Восемь бит информации составляют 1 байт. Биты и байты с самым маленьким номером в заданном диапазоне часто называют младшими, а с самым большим номером — старшими. То же самое, кстати, относится и к адресам. Для обозначения многобайтных данных иногда применяют термин «слово» («word»).

В случае, когда речь идёт о тысячах байт, может возникнуть лёгкая путаница. Дело в том, что исторически использовалась единица 1 килобайт, равная 1024 байт.
Думаю, поборники «чистоты во всём», возмущённые тем, что «кило» — 1024, а не 1000, добились, в конце концов, принятия двух единиц измерения:
1 килобайт (KB) — 1000 байт
1 кибибайт (KiB) — 1024 байт.
Не уверен, что жизнь программистов после этого стала стремительно улучшаться, и поэтому не стал бы тратить ваше время на подобную информацию, однако с таким многообразием единиц измерения согласились, кажется, не все.
При прочтении даташитов, вы убедитесь, что производители МК по-прежнему используют приставку K, подразумевая 1024 байт. Кроме того, в следующих главах нам предстоит делать расчёт адресов регистров, исходя из их общего количества, поэтому давайте договоримся: в рамках данной статьи 1K байт — это 1024 байт.

Поскольку регистры ничего, кроме 0 и 1, содержать не могут, можно сказать, что микроконтроллер оперирует в поле двоичной системы счисления.
На Рисунке 7 нули и единицы, записанные в регистр, образуют 8-битное двоичное число 10101010. В программировании двоичное число предваряют символами 0b: 0b10101010.

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

Обычно, десятичные числа используют в программе, когда речь идёт о численном выражении какой-либо величины (например, 5 секунд или 12 тонн).

В случае шестнадцатеричной системы счисления перед числом прописываются символы 0x. Одно из бесспорных преимуществ шестнадцатеричного представления заключается в том, что каждые два разряда числа, начиная с младшего, составляют 1 байт. К примеру, байты числа 0×12A57F — 0×12, 0хA5 и 0×7F. О пользе этого свойства вы узнаете в главе, посвящённой практике. Добавлю, что эта система, помимо программы, широко используется в документации МК: адреса регистров в даташитах и мануалах представлены в шестнадцатеричной форме.

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

Как видите, число, записанное в регистр на Рисунке 7 — 170 в десятичной и 0xAA в шестнадцатеричной системах счисления.

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

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

Запись операции читается как «сдвинуть число m влево n раз». На Рисунке 9 приведён пример 2-кратного сдвига влево числа m = 3 (0b00000011), записанного в регистр r0.

Обратите внимание, что биты, освобождающиеся справа от числа при сдвиге его влево, заполняются нулями.

Запись операции читается как «сдвинуть число m вправо n раз», а сама операция работает так же, как и предыдущая, но в обратном направлении.
Если на Рисунке 9 поменять местами верхний и нижний регистры, получится иллюстрация двукратного сдвига вправо числа 12 (0b00001100).

С применением этой операции значение каждого бита числа меняется на противоположное, т.е инвертируется, поэтому её часто называют инверсией. На рисунке 10 результат инверсии числа A = 15 (0b00001111) из регистра r0 записан в r1 как число С = 240 (0b11110000).

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

Результат равен 1 только если оба бита пары равны 1.

Результат равен 1 если хотя бы один из двух бит пары равен 1.

Результат равен 1 только, если один из двух бит пары равен 1, а другой — 0.

Отмечу, что форма записи логической операций в Таблице 1 и соответствующая ассемблерная команда (инструкция) МК — не одно и то же. Кроме того, на иллюстрациях операций между двумя регистрами (И, ИЛИ, ИСКЛЮЧАЮЩЕЕ ИЛИ) результат, для наглядности, записывается в третий регистр, в реальности же он, обычно, сохраняется в первом из двух, участвующих в операции регистров.
В Таблице 2 приведены некоторые инструкции логических операций МК AVR и ARM с кратким описанием их работы.

Чтобы вам было легче понять, а мне — объяснить суть вопроса, представим 8-битный регистр (Data Register, DR), входящий в состав микроконтроллера и ответственный за связь последнего с внешним миром. Подключим к выводам DR лампочки и договоримся о двух вещах:

1. Мы не можем делать с DR ничего, кроме записи/чтения числа в/из него, да и то не напрямую, а только через вспомогательный регистр r0. К чему такие сложности, вы поймёте из следующего раздела главы.

2. Логика нашего устройства — прямая т. е. число 1 в n-м бите DR включает соответствующую лампу, а 0 — выключает.

Включим жёлтую лампу на выводе 7, записав через r0 в регистр DR число 0b10000000 (128 в десятичной системе счисления), и пусть себе горит. Поскольку речь пойдёт о логике, забудем на время об электрической грамотности и изобразим наше устройство следующим образом:

Как включить красную лампу, подключённую к выводу 4, а затем выключить её, не меняя состояние включённой ранее жёлтой лампы или, если обобщить, как изменить состояние одного или нескольких битов регистра DR, не меняя состояния остальных? Легко сообразить, что для включения красной лампы надо добавить 1 в четвёртый бит и полученное число 0b10010000 (144) записать в DR.

Чтобы выключить красную лампу, не меняя состояния жёлтой, необходимо вернуть в DR число 128. Казалось бы, всё просто и можно вполне обойтись без логических операций. Но, обратим внимание на следующую не очевидную деталь: формируя числа для включения/выключения лампы, мы не должны забывать, что в седьмом бите в обоих случаях должна быть единица. Запомнить номер одного бита — 7 — не сложно так же, как и его состояние, тем более, что оно неизменно. К тому же, регистр у нас — всего один, да и картинка перед глазами облегчает дело.

В реальности картинок, как вы понимаете, нет, а регистров может быть сотни, к тому же 32-битных. Более того, по ходу программы их значение может меняться не один раз. Выходит, программист, чтобы поменять состояние одного бита, должен помнить текущее состояние оставшихся 31 битов для формирования правильного числа. И так — для сотен регистров. И это ещё не самое худшее. Не редко бывает так, что состояние одного или нескольких битов регистра определяется внешними устройствами (кнопками, сенсорами и т. д.) и тогда мы можем даже не знать, каково их текущее состояние.

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

Операция ИЛИ позволяет записать 1 в любой бит числа, не меняя состояния остальных битов, для чего потребуется:
1. Считать текущее значение DR в r0.
2. Записать в r1 число, в котором значение требуемого бита равно 1, а остальных — 0. В случае с красной лампой это — 4-й бит, а число — 0b00010000.
3. Применить операцию ИЛИ посредством инструкции OR r0, r1 (или ORR r0, r1 — для ARM), результат работы которой, как видно из Таблицы 2, запишется в r0.
4. Скопировать полученное число из r0 в DR.

Очевидно, операция дополнительно к жёлтой не включит ни одной лампы, кроме красной. Более того, если бы мы вдруг забыли, что красная лампа уже включена (1 в четвёртом бите DR до операции) и, тем не менее, провели операцию, это было бы лишь повторное включение и без того включенной лампы, что — не смертельно.
Если включить воображение, можно сказать, что в ходе операции число в r1 накладывается, подобно маске, на число в r0 для получения требуемого результата, поэтому далее будем использовать этот термин.

Операция ИСКЛЮЧАЮЩЕЕ ИЛИ меняет значение требуемого бита на противоположное, не затрагивая остальные биты, если:
1. Считать текущее значение DR в r0.
2. Записать в r1 такую же, как и в предыдущем случае, маску — 0b00010000.
3. Применить операцию ИСКЛЮЧАЮЩЕЕ ИЛИ посредством инструкции EOR r0, r1.
4. Скопировать полученное число из r0 в DR.
На Рисунке 17 приведён пример включения и последующего выключения красной лампы без изменения состояния жёлтой посредством двойного применения инструкции EOR r0, r1.

С помощью операции И можно обнулить любой бит числа, не меняя состояния остальных, для чего нужно:
1. Считать текущее значение DR в r0.
2. Записать в r1 маску, в которой значение требуемого бита равно 0, а остальных — 1, т. е. 0b11101111 в нашем случае.
3. Применить операцию И посредством инструкции AND r0, r1.
4. Скопировать полученное число из r0 в DR.

Как легко убедиться, будь любая другая лампа, помимо жёлтой, включена до операции, её состояние не изменилось бы и после. Опять же, если красная лампа будет изначально выключена (0 в четвёртом бите DR до операции), мы, применив по забывчивости операцию И, всего лишь получим попытку её повторного выключения.

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

• 0b00010001 — для операций ИЛИ/ИСКЛЮЧАЮЩЕЕ ИЛИ,
• 0b11101110 для операции И.

Есть ещё одна польза от применения логической операции И — возможность проверить текущее состояние любого бита регистра. Предположим, что включение/выключение красной лампы обусловлено положением внешнего переключателя, подключённого ко 2-му выводу DR. Тогда, следует периодически:
1. Считывать текущее значение DR в r0.
2. Записывать в регистр r1 маску, в которой 2-й бит равен 1, а остальные — 0, т. е. 0b00000100.
3. Применять операцию И посредством инструкции AND r0, r1.

Вы можете, меняя на Рисунке 19 содержимое r0 до операции, убедиться, что после операции в него всегда будет возвращаться число 0 (0b00000000), кроме единственного варианта — когда состояние 2-го бита DR, а значит и r0, до операции, равно 1. В этом случае в r0 после операции запишется число 0b00000100, что и будет сигналом для включения красной лампы. Во всех остальных случаях её следует выключать.
Приведённый пример проверки состояния справедлив и для комбинации битов. То есть, если бы включение красной лампы определяла комбинация из единиц в 0-м и 5-м битах регистра DR, то маской и числом в r0 после операции, обуславливающим включение красной лампы, будет 0b00100001.

Приведу самый простой пример их использования. Если записать в регистр r0 число 1 (0b00000001), а затем последовательно выполнять 7 инструкций сдвига влево (LSL r0) и 7 вправо (LSR r0), получится эффект «бегущего огня».

Ещё об одном распространённом варианте применения операции сдвига. Вы наверняка заметили из Рисунка 9, что единичный сдвиг влево равноценен умножению на 2, а вправо — делению на 2. Учитывая, что сдвиг исполняется МК быстрее, чем умножение/деление, программисты зачастую используют первую операцию взамен второй, когда скорость работы программы критична.
Операция сдвига влево, наряду с операцией НЕ, используется, помимо прочего, для формирования битовых масок, о чём — ниже.

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

Раз уж мы стали использовать инструкции МК, познакомимся ещё с одной — записи числа в регистр. На Рисунке 20 приведён пример инструкции записи в регистр r1 маски включения красной лампы для обоих ядер МК.

Мнемоники (LDI и LDR) инструкции схожи, поскольку в их основе лежит одно и то же слово «load». Поэтому впредь наряду с «запись в регистр» будем использовать выражение «загрузка в регистр».
Глядя на число в правой части Рисунка 20. попытайтесь оценить, сколько непередаваемых эмоций может принести вам работа в лоб с 32-битными масками. Вообще то, общепринятые правила позволяют не отражать незначащие разряды, т. е. число 16 можно записать в обоих случаях как 0b10000, а компилятор сам дополнит недостающие слева нули в соответствии с разрядностью МК.

Ну, а если, всё же, все разряды значащие? Тогда никуда не денешься: придётся набирать этих 8- или 32-главых монстров в тексте программы или на калькуляторе, чтобы получить их десятичный (шестнадцатеричный) аналог. В любом случае риск поставить не туда 0 или 1 остаётся.

Попробуем обойтись малой кровью.
Начать следует с того, что компилятор переводит в двоичное представление не только десятичные и шестнадцатеричные числа, попадающиеся на его пути, но и числа, выраженные в форме записи логических операций. Если хотите понять, что это значит на нормальном человеческом языке, вернёмся к Таблице 1 и вспомним, что 4-кратный сдвиг влево числа 1 выражается формой и даёт результат, представленные на Рисунке 21.

Приглядевшись повнимательнее к Рисунку 21, вы заметите две вещи:
1. В результате операции получилось число 16 (0b00010000), т. е. маска для включения красной лампы.
2. Число n в форме записи (т.е. — 4) фактически указывает на номер бита, в котором должен оказаться после операции младший бит сдвигаемого числа (в нашем случае — единица). В этом контексте можно сказать, что результатом операции n-кратного сдвига влево числа 1 всегда будет число, в котором n-й бит равен единице, а остальные — нулю. Имейте в виду, что все эти фокусы не проходят со сдвигом вправо.

Так вот, мы можем в инструкциях МК из Рисунка 20 вместо двоичного представления числа 16 записать форму 4-кратного сдвига влево числа 1:

,
а компилятор, встретив такую конструкцию, заменит её на число, в котором 4-й бит равен единице, а остальные — нулю, т. е. приведёт всё к виду на Рисунке 20.

Как быть с маской для погашения красной лампы — числом 0b11101111? Вы, скорее всего, уже поняли, что оно — инверсия предыдущей маски 0b00010000. Это позволяет использовать конструкцию

при компиляции, которой, с учётом скобок, определяющих последовательность действий, произойдёт следующее:
1. Формирование числа 0b00010000.
2. Инверсия числа из п. 1 в искомую маску 0b11101111.

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

Принимая во внимание скобки, компилятор в первом случае:
1. Сформирует число, в котором k-й бит равен 1, а остальные — 0.
2. Сформирует число, в котором m-й бит равен 1, а остальные — 0.
3. Сформирует число, в котором n-й бит равен 1, а остальные — 0.
4. Применит операцию ИЛИ между числами из п. п.1 и 2., в результате чего получится новое число с единицами в битах k и m и нулями — в остальных.
5. Применит операцию ИЛИ между числами из п. 3 и 4. с получение окончательного числа с единицами в битах k, m и n и нулями — в остальных.

Во втором случае шестым пунктом добавится инверсия числа из п. 5.

Остался один не обязательный, но очень полезный штрих.
Само по себе число в строке кода не несёт полезной информации, если только оно не количественное выражение чего-либо (2 слонёнка, 5 мартышек и т. п.). Увидев в чужой программе конструкцию подобную той, что на Рисунке 22, вы сможете догадаться лишь, что автор собирается что-то делать с 4-м битом. Замена формы логической операции на число 16 только прибавит загадочности. Наверное, потому и называют такие числа магическими, что выяснить их назначение можно только с помощью магии. Даже собственный код недельной давности, утыканный магическими числами, может ввести вас в ступор, а если вы рискнёте передать его в таком виде коллеге по цеху или заказчику, будьте готовы узнать о себе много нового и интересного.

Ситуацию можно улучшить, добавив к строке комментарий, что, мол, готовится включение красной лампы. Но, комментарий в каждой строке — перебор в другую сторону. Поэтому в случае на Рисунке 22. предпочтительнее использовать условное название или макроопределение для числа 4.
Все используемые в тексте программы макроопределения прописываются до их применения и в ассемблере от GCC имеют следующий общий вид:

НАЗВАНИЕ = значение

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

Для нас такая запись, согласитесь, более информативна, чем предыдущая, а компилятор все встреченные в коде макроопределения RED_LAMP заменит числом 4.

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

В заключение, чтобы вы могли в полной мере ощутить пользу от применения логических конструкций и макроопределений, приведу пример двух вариантов (с использованием чисел в форме двоичной и записи логических операций) части кода необходимого для включения/выключения внешних устройств, которые подключены к следующим выводам регистра DR микроконтроллера ARM:

• зелёный светодиод — к 0-му,
• синий светодиод — к 17-му,
• мотор — к 31-му.

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

На Рисунке 27 приведена блок-схема модели микроконтроллера.

Несмотря на то, что это — всего лишь модель, к тому же очень упрощённая, постараюсь на её примере дать функциональную картину МК, в объёме, достаточном для первоначального этапа, и в общих чертах справедливую как для AVR, так и для ARM. Случаи же, когда детали устройства и работы реального микроконтроллера и модели принципиально не совпадают, будут освещены по ходу текущей главы, либо — в практической части статьи.

Как видите, модель состоит из трёх основных блоков. Два из них (ядро и периферия) физически размещаются внутри корпуса МК, а третий (выводы МК) — снаружи. Для удобства объяснения и облегчения восприятия во всех трёх блоках выделены одним цветом отдельные элементы, которые так или иначе связаны между собой.

Через этот блок МК общается с внешними устройствами. В контексте этого общения задача программиста заключается в том, чтобы в заданное время на определённом выводе МК:
• обеспечить наличие логических 1 или 0 (напряжения питания или 0В, соответственно),
• считать текущее состояние — логические 1 или 0.
• считать значение аналогового сигнала. Как правило, выводы МК для реализации этой функции жёстко определены. В нашей модели такой вывод — с порядковым номером 0.

Назначение этого блока заключается в непосредственном исполнении задачи, определённой выше:
• Порт ввода/вывода (ПВВ, GPIO) обеспечивает запись на выводы МК, либо считывание с них цифрового сигнала — логических 1 или 0,
АЦП (ADC) измеряет уровень аналогового напряжения на выводе 0 МК.
Таймер считает поступающие на него тактовые импульсы. Зная период такта, мы можем вычислить общую продолжительность счёта, что даёт возможность с точность до одного тактового периода задавать время чтения информации с выводов МК, либо записи на них.

Обратиться к элементам блока периферии (настроить их или записать/считать данные) мы можем только через соответствующие регистры, расположенные в памяти данных ядра.
В реальных МК модулей периферии значительно больше, а их функциональные возможности — шире. Более того, они могут дублироваться (3 порта, 2 таймера, 5 АЦП и т. д.), но всё это не меняет сути дела.

В нашей модели в состав ядра входят:
• Центральный процессор (ЦПУ). Именно для него мы пишем программу. В моменты, когда требуется произвести арифметическую или логическую операцию, ЦПУ привлекает арифметико-логическое устройство (АЛУ).
• Память программ. Сюда загружается написанная нами программа. В нашем случае память состоит из двадцати двух 16-битных регистров с адресами от 0 до 21. Программа это — набор инструкций, понятных ЦПУ. Длина инструкции нашего МК составляет 16 бит, поэтому в каждый регистр может быть записана лишь одна.
Содержимое памяти программ сохраняется даже, если питание МК отключено.
• Память данных. Как следует из названия, здесь хранятся данные. О том, какие именно — чуть позже. Организована память данных в виде 22 регистров, длиной 8 бит каждый.
Информация в памяти данных сохраняется только, если МК запитан, иначе все её регистры обнуляются. В реальном МК при сбросе/отключении питания отдельные регистры памяти данных могут принимать ненулевое значение, определённое производителем.
• Программный счётчик (ПС, PC) содержит адрес инструкции, которую ЦПУ должен исполнить следующей.

Помимо упомянутых блоков на схеме присутствуют:
• Генератор тактовых импульсов (ГТИ). Этот узел запускает работу МК и задаёт её скорость. Единственное, что действительно следует знать о ГТИ реальных МК на начальном этапе, так это — возможность выбора элемента, определяющего его частоту — внутренняя RC-цепочка или внешний кварцевый резонатор. О том, как осуществить этот выбор, мы поговорим в одной из последующих глав.
• 8-битная шина данных, через которую блоки МК обмениваются информацией. Ширина шины данных определяет разрядность МК.
• 16-битная шина команд, по которой ЦПУ считывает инструкции из памяти программ.

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

Как вы видите, первыми в этой памяти расположены два регистра общего назначения (РОН, GPR), знакомые вам r0 и r1. В реальных МК регистров общего назначения — более десяти. Я не случайно выделил эти регистры, ЦПУ и АЛУ одним синим цветом. Дело в том, что ни ЦПУ, ни АЛУ не имеют прямого доступа ко всем остальным, кроме РОН, регистрам памяти данных. Не существует команды для ЦПУ «записать число 5 в регистр данных ПВВ с адресом 3». Для реализации этой операции потребуется минимум две инструкции:
1. Загрузить число 5 в r0.
2. Скопировать число из r0 в регистр с адресом 3.

Точно также АЛУ не может сложить прямо числа, которые записаны, к примеру, в регистрах с адресами 10 и 11 или применить к ним логическую операцию. Для этого необходимо считать числа из указанных регистров в r0 и r1 и уже между ними проводить требуемую операцию.

Следом за РОН идут регистры периферии. В рассматриваемой модели их — по два на каждый модуль (регистр настройки и данных).

Назначение регистров данных следующее:

По сути это — регистр DR из предыдущего раздела главы. Когда мы выводим данные вовне, значение (1 или 0) в n-ном бите регистра данных обуславливает логическое состояние (1 или 0, соответственно) на выводе МК с таким же номером. В случае же чтения данных извне ситуация обратная: логические 1 или 0 на n-ном выводе МК отражаются как 1 или 0 в бите регистра данных c порядковым номером n.

По мере счёта значение регистра данных таймера увеличивается от нуля до 0b11111111 (255), а затем опять сбрасывается в 0. И так — до тех пор, пока тактирование таймера не будет отключено.

Сюда АЦП записывает двоичное представление измеренного на выводе 0 МК значения аналогового напряжения.

Теперь — о регистрах настроек. Биты этих регистров отвечают за следующее:

Если значение n-го бита — 1, вывод МК с таким же номером работает как выход, 0 — как вход.

• 0-й бит. Если значение бита — 1, тактирование таймера включено, 0 — выключено.
• 1-й и 2-й биты. Если комбинация их значений — 00, то частота тактирования таймера равна частоте ГТИ, 01 — частота ГТИ/2, 10 — частота ГТИ/64, 11 — частота ГТИ/128.
• 3-й бит. Если значение бита — 1, разрешено прерывание таймера — сигнала о том, что он досчитал до своего максимума и сбросился в ноль, 0 — прерывание запрещено.
• 4-й — 7-й биты не используются, т. е. зарезервированы.

• 0-й бит. Если значение бита — 1, тактирование АЦП включено, 0 — выключено.
• 1-й и 2-й биты. Этими битами также, как и у таймера, регулируется частота тактирования АЦП.
• 3-й бит. 1 — разрешен сигнал (прерывание) АЦП о том, что измерение завершено и результат преобразования сохранён в регистре данных АЦП. 0 — прерывание запрещено.
• 4-й бит. С записью 1 в этот бит стартует измерение. Значение бита автоматически сбрасывается в 0 по завершению измерения.
• 5-й — 7-й биты зарезервированы.

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

Предположим, что мы решили собрать устройство на базе нашего МК, которое каждые 255 секунд с максимальной скоростью измеряет аналоговый сигнал от фоторезистора, подключённого к выводу 0, и, в зависимости от уровня освещённости, включает/выключает лампу на выводе 4.

Примем частоту ГТИ за 128Гц. Тогда в регистры настроек периферии нужно записать через РОН следующие числа:
ПВВ
0-й вывод МК должен работать как вход (значение соответствующего бита регистра настроек — 0), а 4-й — как выход (значение бита — 1). Поскольку направление работы остальных выводов нам не важно, настроим их как входы. Получаем число 0b00010000.

Таймер
• 0-й бит. Разрешаем тактирование — 1.
• 1-й и 2-й биты. Делим частоту ГТИ на 128, т. е. частота тактирования таймера будет 1Гц. Тогда, чтобы переполниться (досчитать до 255) и выдать прерывание ему понадобится как раз 255 секунд. Комбинация значений битов — 11.
• 3-й бит. Разрешаем прерывание таймера — 1.
Искомое число — 0b00001111.

АЦП
• 0-й бит. Разрешаем тактирование — 1.
• 1-й и 2-й бит. Нам нужна максимальная скорость измерения, т. е. частота тактирования АЦП. Отказываемся от деления частоты ГТИ. Комбинация — 00.
• 3-й бит. Разрешаем прерывание АЦП — 1.
В итоге — число 0b00001001.

Алгоритм программы будет выглядеть так:
1. Настраиваем периферию.
2. В цикле, при каждом прерывании от таймера записываем 1 в 4-й бит регистра настроек АЦП, запуская тем самым измерение освещённости. Чтобы не затереть при этом уже записанное в этот регистр число 0b00001001, применяем логическую операцию ИЛИ и маску 0b000010000.
3. По прерыванию от АЦП считываем значение из регистра данных АЦП. Если оно меньше порогового (которое, например, равно 40), включаем лампу, записав 1 в 4-й бит регистра данных ПВВ, в противном случае — гасим.

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

Бит I. Чуть выше мы говорили о битах в регистрах настройки периферии, разрешавших прерывания таймера и АЦП. Эти биты называют битами локального разрешения прерывания. Бит I — в принципе разрешает использовать механизм прерываний, т. е. это — бит глобального разрешения прерываний, без установки программистом в 1 которого локальные разрешения прерываний силу иметь не будут.
Следует отметить, что бит I — особенность МК AVR. В ARM для глобального контроля за прерываниями выделен целый модуль, называемый Nested Vectored Interrupt Controller (NVIC).

Биты Z и N также доступны программисту как для чтения, так и для записи. Однако, для нас, в первую очередь, интересно их свойство автоматически устанавливаться в 1 в определённых случаях:

Бит Z устанавливается в 1 автоматически, если в результате какой-либо операции АЛУ образуется ноль. К примеру, нам нужно узнать, равно ли значение регистра данных таймера 48. Для этого:
1. Считываем значение регистра данных таймера в r0.
2. Загружаем в r1 число 48.
3. Вычитаем значение одного РОН из другого.
4. Если в результате вычитания Z примет значение 1, числа равны.

К автоматической записи в бит N единицы приводит образование отрицательного числа после какой-либо операции АЛУ, что даёт возможность использовать его для проверки условий «больше-меньше». Если в результате вычитания значений двух РОН бит N устанавливается в 1, вычитаемое больше уменьшаемого, и наоборот. Именно этот бит помог бы нам сравнить текущий уровень освещённости с пороговым в вышеприведённом примере.

Второй специальный регистр в памяти данных — указатель стека SP (Stack Pointer). Функция этого регистра настолько сильно взаимосвязана с работой программы, что будет правильнее, если я расскажу вам о нём ниже, при обсуждении памяти программ. Скажу лишь, что сразу после сброса/подачи питания в SP должен быть записан адрес старшего регистра памяти данных (в нашей модели это — 21), поэтому оба регистра окрашены в единый серый цвет.

Осталось выяснить, для чего служит область памяти SRAM.
Представьте, что в рассмотренном выше примере устройство должно реагировать не на мгновенное значение освещённости, а на среднее от результатов 5 измерений. Куда размещать массив данных до их усреднения?
Для этого и пригодится сектор SRAM, названный кучей (heap). В общем случае данные размещаются по направлению от младшего адреса (10) кучи к старшему (19).

Функция сектора стек (stack) опять же тесно связана с работой программы и о ней — ниже.

Включите всё ваше воображение и представьте, что я, будучи заместителем директора по кадрам, написал и согласовал с руководством круг обязанностей для специалиста вновь открываемого управления. Название должности, кстати, звучит как «Центральная персона управления» (сокращённо — «ЦПУ»).

Согласно документа, названного для солидности «Основная функция (main)», ЦПУ, придя утром на работу должен включить и настроить печатающую машинку, стукнув по ней три раза, а затем в цикле набирать букву «А» или «Б», в зависимости от того, включена сигнальная лампа на стене или отключена. После набора каждой буквы необходимо совершить два прихлопа и три притопа.
Природа одарила меня ленью и, чтобы не повторять два раза инструкции о притопах и прихлопах, я вынес их в отдельный список под названием «Подпрограмма» и в итоге получил следующее:

«Основная функция (main
1. Включить печатающую машинку.
2. Стукнуть по машинке 3 раза.
3. Если сигнальная лампа включена, набрать букву «А». Иначе — перейти к строке 6.
4. Выполнить подпрограмму.
5. Перейти к строке 3
6. Набрать букву «Б».
7. Выполнить подпрограмму.
8. Перейти к строке 3.

«Подпрограмма»
1. Сделать два прихлопа.
2. Сделать три притопа.
3. Вернуться к основной функции и продолжить её.

Во время исполнения инструкций любого из списков могут произойти следующие события (назовём их «прерываниями»):
1. Звон колокола. При этом автоматически настройки печатающей машинки сбрасываются, а сама она — отключается.
2. Лай собаки.
3. Звонок в дверь.
4. Стук в окно.

ЦПУ, при наступлении любого из указанных событий, должен завершить исполняемую инструкцию, затем всё бросить и отреагировать на каждое событие соответствующим образом. Тут я решил несколько усложнить задачу и сделать реакцию на прерывания двухступенчатой, приложив к каждой ступени отдельный список. Первый список — «Вектор прерывания» — состоит всего лишь из одной инструкции, предписывающей перейти ко второму списку, называемому «Обработчик прерывания», причём для удара колокола обработчиком прерывания служит основная функция. Выглядеть всё это будет так:

«Вектор удара колокола»
1. Перейти к началу основной функции.

«Вектор лая собаки»
1. Перейти к обработчику лая собаки.
«Обработчик лая собаки»
1. Мяукнуть.
2. Вернуться к брошенному делу и продолжить его.

«Вектор звонка в дверь»
1. Перейти к обработчику звонка в дверь.
«Обработчик звонка в дверь»
1. Подпрыгнуть.
2. Вернуться к брошенному делу и продолжить его.

«Вектор стука в окно»
1. Перейти к обработчику стука в окно.
«Обработчик стука в окно»
1. Сделать 5 приседаний.
2. Вернуться к брошенному делу и продолжить его.

Функции по меньшей мере странные, нудные и однообразные, а мне ещё надо срочно подобрать под это дело исполнителя. Естественно, выпускники Гарварда, Кембриджа и прочих оксфордов дружно отказываются выполнять работу, которая может бросить тень на их репутацию и дипломы.

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

Опасаясь, что с такими способностями и памятью он рано или поздно натворит бед (перепутает списки, вернётся не к тому делу или просто заснёт на рабочем месте), я принимаю превентивные меры:
1. В помощь ЦПУ придаю бухгалтера-АЛУ для ведения счётных операций.
2. Говорю своему охраннику с тёмным прошлым и кличкой «ГТИ», чтобы он задавал исполнителю и бухгалтеру ритм работы, пиная их со строгой периодичностью.
3. Объединяю все списки в один большой («Программа»), в котором:
• присваиваю блокам названия прежних списков,
• применяю сквозную нумерацию строк (впредь, вместо термина «номер строки» будем употреблять «адрес» или «адрес инструкции»).
• меняю все инструкции перехода на однообразное «Перейти к адресу n».

Критический взгляд на Программу, даёт понимание того, что всё ещё осталась пара моментов, которые могут сбить с толку ЦПУ:
• Инструкция «Выполнить подпрограмму» не указывает, где последняя находится.
• Инструкция «Вернуться» стала короче, чем «Вернуться к брошенному делу и продолжить его», но от этого не стала менее загадочной.

Поэтому, исполнителю передаётся маленький прибор («Программный счётчик» или «ПС»), на дисплее которого отображается:
• на инструкции «Перейти к адресу n» — адрес перехода,
• на инструкции «Выполнить подпрограмму» — адрес первой инструкции подпрограммы (13),
• на инструкции «Вернуться» — адрес возврата,

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

Памятуя, что писать и читать вновь нанятый работник всё же умеет, я вручаю ему пару листков:
• Первый листок (пусть он называется «SRAM») разлинован на 12 строк с номерами или адресами от 10 по 21. При необходимости, ЦПУ может записывать данные (количество сделанных приседаний или промежуточные результаты расчётов бухгалтера, к примеру) по адресам 10-19 («куча»). Но, ему под страхом смерти запрещено использовать две последние строки, окрашенные в серый цвет («стек»).

• Во второй листок («Указатель стека» или «SP») исполнитель должен всего лишь один раз, перед включением печатной машинки, записать старший адрес SRAM (т.е. — 21), о чём в программу добавлена соответствующая инструкция (по адресу 4). Кстати, такая запись называется указанием на вершину стека.

Работа ПС, SP и SRAM определённым образом взаимосвязана:
• При каждом ударе колокола все они обнуляются, вместе с отключением печатной машинки.

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

• Каждый раз, когда в программе встречается инструкция «Выполнить подпрограмму», автоматически:
а) адрес следующей инструкции записывается в SRAM по адресу, указанному в SP,
б) значение SP уменьшается на единицу.
в) стартовый адрес подпрограммы (13) записывается в ПС.

• Каждый раз, когда в программе встречается инструкция «Вернуться», автоматически:
а) значение SP увеличивается на единицу.
б) значение в SRAM по адресу, указанному в SP, записывается в ПС.

Чтобы быть спокойным, я решаю промоделировать ту или иную рабочую ситуацию: сажаю ЦПУ, АЛУ и ГТИ в одну комнату, включаю сигнальную лампу и бью в колокол. Как вы помните, ПС, SP и SRAM при этом обнуляются, а печатающая машинка отключается со сбросом настроек.

Итак,
1. ЦПУ видит в ПС число 0 и переходит на этот адрес программы.
2. По адресу 0 — переход на адрес 4. ЦПУ убеждается, что в ПС указан тот же адрес и переходит.
3. Осуществляется запись в SP последнего адреса SRAM (21). ПС при этом увеличивается на 1 — до 5.
4. ЦПУ включает печатающую машинку и настраивает её (инструкции по адресам 5,6).
5. Поскольку сигнальная лампа включена, набирается буква «А».
6. На инструкции по адресу 8 («Выполнить подпрограмму»):
• Адрес следующей инструкции (9) записывается в SRAM адресу 21, поскольку именно это число записал ЦПУ в SP чуть ранее.
• Значение SP уменьшается (декрементируется) на 1, т. е. вершина стека теперь — 20.
• В ПС записывается стартовый адрес подпрограммы (13), куда и отправляется ЦПУ.

7. Сделав в требуемых количествах прихлопы и притопы (не забывайте, что ПС на этих инструкциях просто инкрементируется), ЦПУ подходит к адресу 15 (инструкция «Вернуться») и здесь:
• значение SP увеличивается на единицу — до 21.
• Значение из SRAM по адресу 21 (а там у нас — 9) записывается в ПС.
• ЦПУ выполняет инструкцию по указанному в ПС адресу 9, т. е. переходит к адресу 7. В этот момент я выключаю сигнальную лампу.

8. Поскольку лампа выключена, ЦПУ переходит по адресу 10.
9. Во время набора буквы «Б» раздаётся стук в окно. При этом:
• Адрес следующей инструкции (11) записывается в SRAM по адресу 21 (именно до этого значения увеличился SP в п.7).
• Значение SP декрементируется до 20.
• Адрес вектора стука в окно (3) записывается в ПС.
• ЦПУ завершает печатать букву «Б» и переходит по адресу в ПС — 3.

10. Перейдя по адресу 20 (об этом было указано в инструкции по адресу 3), ЦПУ прилежно приседает, а затем осуществляется возврат к адресу 11 в порядке, описанном п. 7.

Вдумчивый читатель может заинтересоваться, почему ЦПУ запрещено делать записи в строке 20 SRAM, если она в приведённых выше десяти пунктах ни разу не использовалась? Да и без указателя стека можно вполне обойтись: при инструкции «Выполнить подпрограмму» и прерываниях просто записать адрес инструкции, следующей за текущей, в 21-ю строку SRAM, а потом благополучно вернуть в ПС.
Давайте примем такой вариант и представим следующее.
1. ЦПУ набрал букву «А» и перешёл к подпрограмме. Адрес следующей инструкции (9) записывается в SRAM по адресу 21.
2. Во время второго прихлопа (адрес — 13) звонят в дверь и:
• При наличии стека и указателя на него адрес следующей инструкции (14) был бы записан в SRAM по адресу 20. Но, мы от них отказались, поэтому число 14 записывается по адресу 21 затирая предыдущую запись (9).
• Адрес вектора звонка в дверь (2) записывается в ПС.
3. ЦПУ переходит к адресу 2, оттуда — к адресу 18, подпрыгивает и возвращается к адресу, записанному в 21-й строке SRAM, т. е. 14.
4. В это время к шефу компании прибывает иностранная делегация и он решает похвастать перед ними тем, как замечательно работает новое управление.
5. Шумной толпой они вваливаются в комнату и видят, что бухгалтер и охранник, разинув рты, уставились на ЦПУ, который безостановочно притоптывает потому, что после каждых трёх притопов вновь возвращается к адресу 14, а адрес 9, куда он должен был в конце концов вернуться, затёрт.

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

1. Все мы имели дело с детской пирамидкой и помним, что кольцо, надетое последним, снимается первым. Стек, как вы наверняка заметили, работает по тому же методу LIFO (Last In First Out): значение, записанное последним, считывается первым.

2. В нашей модели, с целью упростить объяснение и облегчить его восприятие, размеры стека и кучи чётко определены, а ЦПУ в приказном порядке запрещены записи в стек. В реальных микроконтроллерах нет инструментов (специальной инструкции или аппаратного механизма) для разграничения стека и кучи. Кроме того, запись в стек возможна не только автоматически (при переходах в подпрограмму или обработчик прерывания): и в AVR, и в ARM имеется инструкция PUSH rn, которая сохраняет текущее значение n-го РОН в вершину стека. Таким образом, с ростом объёма сохраняемой информации куча и стек движутся навстречу друг другу и может, в конце концов, произойти их наложение: либо стек «продавит» кучу, либо куча «сорвёт» вершину стека.

Оба случая — из разряда самых коварных и неприятных ошибок программиста. Мало того, что компилятор не распознаёт их как ошибку, так они ещё и «блуждающие», т. е. могут проявляться лишь изредка, нанося при это разрушительный урон.

Избежать их позволят несколько простых правил:
• старайтесь не увлекаться вложенными подпрограммами,
• в программах на ассемблере контролируйте использование инструкций PUSH и парной ей POP.
• в программах на С/С++ не злоупотребляйте глобальными переменными.
• при необходимости сохранения в куче постоянного потока данных применяйте циклический буфер.

Ну что же, вроде как всё работает. Я, как напоминание о моих трудах, вешаю на стену увеличенные копии схемы программы, SP и SRAM, рассаживаю троицу по рабочим местам и делаю фотографию для истории.

В своё время, все мои попытки объяснить новичку (к тому же — ребёнку) работу микроконтроллера с использованием полной его функциональной схемы и реальных инструкций ЦПУ особого успеха не принесли. Очень надеюсь, что вариант объяснения, использованный мною выше, будет несколько проще и понятнее, а производители МК простят меня за столь вольные и не всегда справедливые образы.

Перед тем, как закончить с теорией, приведу некоторую информацию по реальным МК, рассматриваемых в данной работе.
Прежде всего — о документации, знакомиться с которой, рано или поздно, вам придётся в любом случае.
В случае с ATtiny85 и ATmega8 вполне достаточно внимательного изучения даташитов.
Для МК на базе ARM информация по ядру и периферии разнесена:

1. STM32F401 Datasheet.
2. STM32F401 Reference Manual.
3. STM32 Cortex-M4 Programming Manual.

1. nRF52832 Product Specification.

1. Cortex-M4 Generic User Guide.
2. Cortex-M4 Technical Reference Manual.
3. ARMv7-M Architecture Reference Manual
Указанные выше документы выложены в архив, и все последующие ссылки будут делаться именно на них.

На рисунке 31. представлены карты памяти ATtiny85 и ATmega8.

Как видите, всё — очень близко к модели из Рисунка 27, за исключением количества регистров.

Память программ
Для обоих МК объём этой памяти составляет 8К или 8 * 1024 = 8192 байт. Организована она в виде массива из 16-битных или 2-байтных регистров в количестве 8192 / 2 = 4096 штук с адресами от 0 (0×0000) по 4095 (0×0FFF). Длина инструкций ATtiny85 и ATmega8, как и в модели, составляет 16 бит, т. е. каждый регистр может содержать лишь одну инструкцию. Набор инструкций, доступный программисту, представлен в Таблице «Instruction Set Summary» на страницах даташита за номером 202 (ATtiny85) и 311 (ATmega8).

Память данных
Регистры памяти данных обоих МК — 8 битные.
Количество регистров общего назначения — 32 с адресами от 0 по 31 (0×001F). Несмотря на то, что РОН имеют адреса, доступ к ним возможен и непосредственно по именам (r0, r1, r16 и т. д.), что обычно и делается.
Следующие 64 регистра памяти данных с адресами от 32 (0×0020) по 95 (0×005F) — так называемые регистры ввода-вывода (Input/Output Registers), включая регистры периферии и спецрегистры SREG и SP.
С адресацией регистров ввода-вывода AVR существует один нюанс. Адреса, указанные выше (0×0020 — 0×005F) — абсолютные. Сдвинув их на 32 позиции, можно получить относительные адреса — от 0 (0×0000) по 63 (0×003F). Соответственно, предусмотрены два набора инструкций для чтения/записи по абсолютным и относительным адресам регистров ввода-вывода.
Полный перечень регистров ввода-вывода и их адреса (абсолютные или относительные) можно узнать из Таблицы «Register Summary» на страницах 200 (ATtiny85) и 309 (ATmega8) даташита. Отмечу, что для ATtiny85 в указанной таблице приведены лишь относительные адреса регистров, а для ATmega8 — и абсолютные (в скобках), и относительные.
Объём SRAM составляет 512 и 1024 байт для ATtiny85 и ATmega8, соответственно.

Обратите внимание на несколько важных цифр, которые будут использоваться нами впоследствии:
1. Старшие адреса SRAM ATtiny85 и ATmega8 — 0×025F и 0×045F, соответственно. Именно эти значения нам предстоит в первых строках программы записывать в SP для указания вершины стека.
2. Младший адрес памяти программ обоих МК — 0×0000. Начиная с этого адреса будет загружаться в микроконтроллер написанная нами программа.

Выдержки из карт памяти STM32F401 и nRF52832, наряду с Cortex M-4, приведены на Рисунке 32.

Компания ARM, как следует из рисунка, определяет границы блоков памяти (периферии, SRAM и программ), выделив на каждый по 0.5G байтов. Производители же МК на базе ядра ARM (в нашем случае — STMicroelectronics и Nordic Semiconductor), не выходя, обычно, за рамки этих ограничений, определяют стартовый адрес и объём каждого типа памяти, требуемый как для удовлетворения потребностей разработанной ими периферии, так и для эффективной работы всего микроконтроллера в целом.
Адресация регистров всех типов памяти — сквозная.
По аналогии с AVR определим наиболее важные для нас адреса карт памяти.
1. SRAM обоих МК имеет объём 64K байт и начинается с адреса 0×20000000.
2. Младший адрес памяти программ STM32F401 — 0×08000000, а nRF52832 — 0×00000000.

13 регистров общего назначения (r0 — r12), и 4 спецрегистра (указатель стека SP, регистр статуса программы PSR, программный счётчик PC и регистр связи LR, о назначении которого вы узнаете позже) в адресном пространстве не отражены и доступ к ним в программе осуществляется, как и в случае с РОН AVR, непосредственно по именам.

Адреса и наименования регистров периферии приводятся в конце раздела по каждому модулю периферии в «STM32F401 Reference Manual» и «nRF52832 Product Specification».
Набор инструкций для обоих МК можно найти в «Cortex-M4 Generic User Guide» (Раздел 3.1 «Instruction set summary»). Кроме того, для STM32F401 эта информация представлена в Разделе 3 «STM32 Cortex-M4 Programming Manual».
В последующих главах нам пригодится следующая информация касательно инструкций, рассматриваемых в данной работе МК.

Для 32-битных МК на базе ядра ARM предусмотрено два набора инструкций:
1. ARM, длина инструкций которого составляет 32 бита.
2. Thumb с инструкциями длиной 16 бит, призванный минимизировать размер программы после компиляции, а следовательно — объём flash-памяти, требуемой для её размещения.
Микроконтроллеры на базе ядра Cortex M-4, в том числе STM32F401 и nRF52832, используют второй набор — Thumb.

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

Говоря «полный», я имею в виду случай, когда программа содержит участки, написанные как на языке Си, так и на ассемблере.

Давайте, не вникая пока в подробности, рассмотрим этапы процесса.

1. В файле с расширением .c пишется код на языке Си. Чаще всего вспомогательная информация при этом выносится в отдельный, так называемый хидер-файл с расширением .h. Отдельные участки программы, предъявляющие особые требования к скорости работы, плотности кода или таймингу, могут быть написаны на ассемблере (файл с расширением .S).
В реальном проекте файлов каждого типа может быть множество, но это никак не меняет структуру нашей схемы.

2. с— и h-файлы компилируются в ассемблер-файлы. На этом этапе может быть включен и Startup-файл, если он написан на Си и не скомпилирован предварительно. Обычно, Startup-файл содержит код, обеспечивающий подготовительную работу: указание на вершину стека, таблицу векторов прерываний и т. п.

3. Этап ассемблирования. Все имеющиеся файлы с расширением .S преобразуются в объектные файлы с расширением .o.
Опять же, здесь включается Startup-файл, если он написан на ассемблере и предварительно не преобразован в объектный файл.

4. На этом этапе осуществляется компоновка (линковка) всех объектных файлов проекта в единый elf-файл. Помимо созданных в текущем проекте в компоновку могут включаться объектные файлы из других проектов, а также библиотеки (файлы с расширением .a).
Условия компоновки определяются в скрипте с расширением .ld.

5. Полученный в ходе предыдущего этапа elf-файл уже является исполняемым: его используют для отладки — пошаговой проверки работы программы на реальном МК или в симуляторе с целью поиска и устранения ошибок.

6. Происходит окончательное преобразование программы в файл с расширением .hex или .bin, который и загружается в МК (этап 7).

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

🎁dokumentacija-mk.zip
 24.26 Mb ⇣ 74

• Программное обеспечение.zip (443.5 Мб) на облаке yandex.ru
В архивах вы найдёте:

Продолжение следует!
Благодарю за внимание.

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

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

Микроконтроллеры бывают разных фирм, которые делают одно и тоже дело, но разными методами. Сравнить это можно с человеческими расами: европейцы, китайцы и африканцы например. Я лично работаю с микроконтроллерами фирмы Атмел, про них и буду говорить. Ну уж пошло сравнение с расами, пускай это будут европейцы.) Программы для микроконтроллеров пишут на языках программирования. Я рекомендую начать с языка Си. Это древний и простой язык. Для написания текста програмы используют программы компиляторы. Они позволяют создавать, редактировать и переваривать написанный программистом текст программы в код (прошивку), который можно загрузить (прошить) в микроконтроллер. Таких программ есть множество. Пример для Атмел: Code VisionAVR, родная от Атмел AVR Studio, Bascom-avr и ещё.
Эти программы делают одно и тоже дело, но своими методами, особенностями достоинствами и недостатками. При это текст Си в тих программах компиляторах немного отличается, но в общем похож. Можно сравнить с различием украинского, русского и белорусского языка. Я использую Code VisionAVR, что и советую начинающим.

Далее я приведу простой текст программы, написанный на языке Си в компиляторе Code VisionAVR для микроконтроллера ATTiny13A. В конце темы есть проект, прошивка и проект для эмулятора протеуса. Микроконтроллер в этой программе умеет делать простую вещь: при помощи кнопки менять логическое состояние на двух выходах, при этом короткое нажатие меняет состояние первого выхода а длинное — второго. В автомобиле например эту схему можно применить для управления одной кнопкой обогревом заднего стекла (которая есть у многих штатно) и добавленным обогревом зеркал. Нажал коротко на кнопку — сработал обогрев стекла, нажал ещё — обогрев стекла выключился. Если нажать и удерживать кнопку, то через какое-то время включиться обогрев зеркал. Если нажать и удерживать кнопку повторно — обогрев зеркал отключится.

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

Текст программы:

/*****************************************************
This program was produced by the
CodeWizardAVR V2.05.0 Professional
Automatic Program Generator
© Copyright 1998-2010 Pavel Haiduc, HP InfoTech s.r.l.
www.hpinfotech.com

Project :
Version :
Date : 28.01.2012
Author :
Company :
Comments:

Chip type : ATtiny13A
AVR Core Clock frequency: 9,600000 MHz
Memory model : Tiny
External RAM size : 0
Data Stack size : 16
*****************************************************/

#include <tiny13a.h>
#include <delay.h>

unsigned char b, trig;

void main(void)
{
// Declare your local variables here

// Crystal Oscillator division factor: 1
#pragma optsize-
CLKPR=0x80;
CLKPR=0x00;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

PORTB=0x01;
DDRB=0x06;

TCCR0A=0x00;
TCCR0B=0x00;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;

GIMSK=0x00;
MCUCR=0x00;

TIMSK0=0x00;

ACSR=0x80;
ADCSRB=0x00;
DIDR0=0x00;

ADCSRA=0x00;

while (1)
{
if (PINB.0==0)
{
if (trig==0) b++;
if (b>100)
{
if (PINB.2==0)PORTB.2=1;
else PORTB.2=0;
trig=1;
b=0;
}
}
else
{
if (b>4)
{
if (PINB.1==0)PORTB.1=1;
else PORTB.1=0;
b=0;
}
b=0;
trig=0;
}

delay_ms(10);

}
}

А теперь поподробнее.

/*****************************************************
This program was produced by the
CodeWizardAVR V2.05.0 Professional
Automatic Program Generator
© Copyright 1998-2010 Pavel Haiduc, HP InfoTech s.r.l.
www.hpinfotech.com

Project :
Version :
Date : 28.01.2012
Author :
Company :
Comments:

Chip type : ATtiny13A
AVR Core Clock frequency: 9,600000 MHz
Memory model : Tiny
External RAM size : 0
Data Stack size : 16
*****************************************************/

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

#include <tiny13a.h>
#include <delay.h>

Это ссылка на библиотеку. Если какая либо библиотека необходима, то она должна быть здесь указана. У нас есть библиотека самого микроконтроллера tiny13a.h, и библиотека задержек времени.

unsigned char a, b, trig;

Объявление трех переменных. unsigned char . Что это такое можно посмотреть здесь Вообще всё непонятное копируем в буфер и ищем в поисковике.

void main(void)
{
// Declare your local variables here

void main(void) — это оператор, говорящий что началась основная часть программы на Cи и микроконтроллер будет её с этого места выполнять. Все что начинается с // — это комментарий. Старайтесь чаще ими пользоваться. Вообще конкретный комментарий генерирует сам компилятор, как и во многих других местах. Большинство комментариев я удалил, что уменьшить текст.

// Crystal Oscillator division factor: 1
#pragma optsize-
CLKPR=0x80;
CLKPR=0x00;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

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

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

При прошивке микроконтроллера эту же частоту нужно указать во фьюзах.
CLKPR=0x80; и CLKPR=0x00; это команды настройки регистра внутреннего делителя этой частоты. Задается оно в мастере в первом окне «CHIP». Если у нас выбран делитель 1, то тактовая частота делиться на 1, то есть остается без изменений. Если указать например делитель 128, то соответственно тактовая частота делиться на это число. 9.6Мгц / 128 = 75кГц. и значения регистра делителя будет выглядеть:
CLKPR=0x80;
CLKPR=0x07;

Особо внимательные заметили, в регистр делителя CLKPR сначала пишется число 0x80 а затем сразу 0x00. Нафига пишется сначала одно значение а потом сразу другое? Если у вас возникают какие либо вопросы по регистрам и не только, приучайтесь сразу читать даташиты. Там все подробные ответы на чистом английском языке. Открываете даташит, вставляете в поисковик текста название регистра (CLKPR ) и ищете его описание, за что какие биты данного регистра отвечают. Конкретно у этого регистра для изменения делителя необходимо записать единичку в седьмой бит, после чего микроконтроллер даст изменить и выбрать необходимый делитель в первых четырех битах этого регистра. После того, как пройдет четыре такта выполнения команд процессора, изменить регистр будет уже нельзя. Нужно снова сначала изменить седьмой бит CLKPR=0x80 а затем указать делитель CLKPR=нужный делитель

PORTB=0x01;
DDRB=0x06;

Команды управления и настройкой портов микроконтроллеров — ножек чипа. Задается тоже в мастере. В этих регистрах задается работа на вход порта PB0 и подключается к нему внутренний Pull-up резистор. Порты PB1 и PB2 настраиваются «на выход» с логическим нулем на выходе в их состоянии.

В колонке DataDitection мы указывает тип порта: вход или выход (in или out)
В колонке PullUp/Output Value указываем подключение подтягивающего резистора pullup если порт настроен на вход (P — подключен, Т — неподключен) Если порт настроен на выход, то можно указать его логическое состояние 0 или 1. У нас нули. Строчки Bit0 — Bit5 это порты микроконтроллера PORTB0 — PORTB5

Если посмотреть сгенерированный компилятором комментарий, то можно увидеть соответствие пинов и их настройку:
// Input/Output Ports initialization
// Port B initialization
// Func5=In Func4=In Func3=In Func2=Out Func1=Out Func0=In
// State5=T State4=T State3=T State2=0 State1=0 State0=P

Если перевести из 16-тиричного в двоичный значение регистров, то можно понять даже без даташита назначение битов в регистре:

PORTB=0x01 PORTB=0b00000001
DDRB=0x06 DDRB=0b00000110

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

PORTB=0b1;
DDRB=0b110;

А можно вообще написать в десятичной системе:
PORTB=1;
DDRB=6;

Далее по тексту кода идет:

TCCR0A=0x00;
TCCR0B=0x00;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;
TCCR0A=0x00;
TCCR0B=0x00;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;
GIMSK=0x00;
MCUCR=0x00;
TIMSK0=0x00;
ACSR=0x80;
ADCSRB=0x00;
DIDR0=0x00;
ADCSRA=0x00;

Настройка таймера микроконтроллера, прерываний, АЦП, компаратора и всего такого пока сложного. Пока его не используем — рановато. У всех этих регистров стоят в значениях нули, это значит, что они отключены. А особо внимательные заметили, что какой-то регистр ACSR имеет значение =0x80; Лезем в даташит и читаем:

Analog Comparator Control and Status Register – ACSR

Вообще, как правило название всех регистров это сокращение от первых букв или части их полного названия.

Если стоит значение данного регистра 0x80, значит в двоичной системе это число 10000000, значит стоит единичка в 7 бите этого регистра, значит читаем в даташите, что он означает:

• Bit 7 – ACD: Analog Comparator Disable
When this bit is written logic one, the power to the Analog Comparator is switched off.
This bit can be set at any time to turn off the Analog Comparator. This will reduce power
consumption in Active and Idle mode. When changing the ACD bit, the Analog Comparator
Interrupt must be disabled by clearing the ACIE bit in ACSR. Otherwise an interrupt
can occur when the bit is changed.

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

while (1)

После того, как микроконтроллер настроен запущен и выполнена инициализация необходимых частей и выполнены необходимые первоначальные команды в void main(void) в дело вступает его величество главный цикл. Все что находиться внутри этого главного цикла while (1) и заключено в скобки начала { и конца } будет крутиться, команды выполняться по кругу от начал до конца. А у нас в нашем коде будет крутиться алгоритм опроса кнопки, подключенной к порту PB0, от состояния которой (нажата кнопка или нет) будет меняться состояние выходных портов PB1 и PB2

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

Теперь про сами основные команды, которые находятся внутри цикла. Все команды используют один оператор if Это условие ЕСЛИ.

if (PINB.0==0)
{

кучка кода;
}
else
{

ещё кучка кода;
}

Подробнее:

if (PINB.0==0)

Если в регистре PINB в бите, отвечающем за порт PB0 микроконтроллера.0 содержится значение равное нулю ==0, то выполняем кучку кода, которая находится далее в границах скобок { и }
Короче, если нажата кнопка то выполняется следующий код в границах последующих скобок { и }
Далее после кучки кода в скобках видим оператор else и ещё кучку кода за ним в скобках { и }

Оператор else переводится не как ещё а как иначе

Оператор if и else всегда работают в паре, сначала идет if затем else. Оператор else можно не использовать совсем, если он не нужен.

В нашей ситуации алгоритм можно описать так:

если (нажата кнопка подключенная к порту PB0)
{

то выполняем кучку кода;
}
иначе
(кнопка не нажата)
{
выполняем эту кучку кода;
}

Так как это все находится внутри главного цикла, то этот код будет выполняться по кругу, будет постоянно опрашиваться кнопка и будет выполняться нужная кучка кода

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

if (trig==0) b++;
if (b>100)
{
if (PINB.2==0)PORTB.2=1;
else PORTB.2=0;
trig=1;
b=0;
}

Операторы можно вкладывать друг в друга, как матрешку. то есть выполняется одно условие, потом если условие сработало, то другое внутри первого условия и т.д.

if (trig==0) b++;

Если переменное значение trig равняется нулю, то выполняем инкремент переменной b Инкремент — операция увеличения значения, хранящегося в переменной, на 1. То есть при проходе выполнения кода, если процессор натыкается на команду инкремента b++, то процессор прибавляет единичку в число, которое находится в переменной b
Так же здесь применяется упрощенная «орфография» написания условия и команды, без скобок { и }:

if (trig==0) b++;
это же самое что:
if (trig==0)
{
b++;
}

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

Немного отвлеклись, возвращаемся:

if (trig==0) b++; — если значение переменной равно нулю (а оно у нас равно нулю) то выполняем инкремент переменной b — переменная в была равна нулю, теперь стало единице.

Следующая операция:

if (b>100)
{

кучка кода;
}

Если переменная b больше ста, то выполняем кучку кода внутри скобок.

Переменная b за каждый круг цикла прибавляется на единичку и в итоге через сто «кругов» главного цикла выполниться условие, которая находится далее внутри скобок { и }

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

if (PINB.2==0)PORTB.2=1;
else PORTB.2=0;
trig=1;
b=0;

Здесь мы видим ещё одно условие (жирная такая матрешка получилась))

if (PINB.2==0)PORTB.2=1;
Если регистр состояния выходного порта PB, а точнее PB2 равен нулю, то меняем его состояние на единичку PORTB.2=1.
else PORTB.2=0;
Иначе пишем в регистр нолик. Или если по-другому: если регистр состояния выходного порта PB2 равен единице, то меняем его на ноль.

Короче если происходит выполнение этих условий и команд, то меняется логическое состояние выхода 2 (PB2) на схеме.

Если полностью описать: если нажата кнопка, если прошло сто кругов главного цикла, то меняем логическое состояние выхода 2 — PORTB.2 в коде он же порт PB2 на схеме.

Как уже стало понятно этот кусок кода отрабатывает длительное нажатие кнопки.
Но этого мало, дальше ещё есть две выполняемые команды присвоения:

trig=1;
b=0;

trig=1; присвоение единице этой переменной необходимо, что бы описанное выше условие работы инкремента b++ перестало работать
b=0; обнуляем переменную b.

В итоге при длительном нажатии кнопки, условие при котором меняется состояние порта PB2 выполняется единожды, до тех пор, пока кнопка не будет отжата кнопка, ибо инкремент не будет работать и условие if (b>100) больше не сработает, если тупо нажать кнопку и не отпускать совсем.

Теперь вторая часть кучки кода, которая следует за первым условием:
else
{
if (b>4)
{
if (PINB.1==0)PORTB.1=1;
else PORTB.1=0;
b=0;
}
b=0;
trig=0;
}

Если кнопка отжата:
Опишем её с конца:

trig=0; присваиваем переменной trig значение ноль. Необходимо, что бы после длительного нажатия, когда наступит последующее отжатие кнопки микроконтроллер снова был готов к нажатиям кнопки ( срабатывало условие инкремента if (trig==0) b++;)

b=0; При не нажатой кнопке значение переменной b равняется нулю.

if (b>4)
{
if (PINB.1==0)PORTB.1=1;
else PORTB.1=0;
b=0;
}

Подробнее:
if (b>4)
Если значение переменной b больше четырех, то выполняем следующий код:
if (PINB.1==0)PORTB.1=1;
else PORTB.1=0;

Если состояние порта BP1 равно нулю, то делаем единицу, если нет, то ноль.

Это условие и команда отрабатывает кроткое нажатие кнопки. Если нажата кнопка, то начинает работать инкремент b++; значение которого начинает увеличиваться. Если отжать кнопку и при этом значение переменной b будет больше четырех ( но меньше ста — а то сработает длинное нажатие) то состояние выходного порта PB1 (он же выход 1 на схеме, он же PORTB.1 в коде) поменяется, сработает алгоритм короткого нажатия кнопки.

Если значение переменной b при отжатии меньше четырех, то условие не срабатывает и ничего не происходит. необходимо для работы «дребезга контактов» и ложных срабатываний.

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

В оконцовке главного цикла виднеется команда:

delay_ms(10);

Это задержка в главном цикле. То есть, выполняется пошагово команды, затем процессор натыкается на команду delay_ms(10); и начинает её выполнять. В итоге процессор будет 10 миллисекунд ждать и ничего не делать в этой строчке, затем опять приступит к выполнению команд.
Находясь в одном общем цикле, скорость нарастания значения инкремента b++ зависит от времени задержки, указанной в delay_ms.

Команда delay_ms находится в библиотеке задержек #include <delay.h>, которую мы для этого и включили в начале кода.

Как видно из описания, длинное нажатие срабатывает от фронта сигнала нажатия кнопки ( начинает работать инкремент) а короткое нажатие кнопки — по спаду, то есть срабатывает по отжатию кнопки.

Вообще выполняемая здесь последовательность: условие + инкремент достаточно часто используемая команда и в языке Си присутствует отдельный оператор для этого for

Архив с прошивкой, исходником и моделью Протеуса:
umat.ru/files/Button_13.zip
ВНИМАНИЕ!
Архив перезалил 22 сентября 2014 года, обнаружил косяк в выставленной частоте в проекте. Теперь тактовая частота 1.2 Мегагерца, при этом фьюзы стоят по дефолту и их при прошивке трогать вообще не надо

Пример реализации схемы rusgg
www.drive2.ru/cars/gaz/ga…usgg/journal/570874/#post


Декабрь 2015 г.

1. Преимущества предлагаемого способа

Схемы устройств на микроконтроллерах (МК) обычно отличаются сочетанием двух трудносовместимых качеств: максимальной простотой и высокой функциональностью. К тому же функциональность может в дальнейшем меняться и расширяться без внесения каких-либо изменений в схему — путём лишь замены программы (перепрошивкой). Эти особенности объясняются тем, что создатели современных МК постарались разместить на одном кристалле всё, что только может потребоваться разработчику электронного устройства — по крайней мере настолько, насколько это возможно. В результате произошло смещение акцента со схемотехнического и монтажного на программный. С использованием МК теперь меньше приходится «нагружать» схему деталями, между компонентами становится меньше соединений. Это, конечно, делает схему более привлекательной для её повторения как опытными, так и начинающими электронщиками. Но, как обычно, за всё приходится платить. Здесь тоже не обошлось без своих сложностей. Если купить новый МК, установить его в правильно собранную из исправных деталей схему и подать питание, то ничего не получится — устройство не будет работать. Микроконтроллеру нужна программа.

Казалось бы с этим тоже всё просто — в интернете можно встретить множество схем с бесплатными прошивками. Но тут возникает одна загвоздка: прошивку необходимо как-то «залить» в микроконтроллер. Для того, кто никогда этим раньше не занимался, такая задача зачастую становится проблемой и главным отталкивающим фактором, нередко заставляющим отказаться от прелестей использования МК и поискать схемы на «рассыпухе» и жесткой логике. А ведь всё не так уж сложно, как может показаться на первый взгляд.

Проанализировав публикации в интернете, можно заметить, что данная проблема решается чаще всего одним из двух путей: покупкой готового программатора или изготовлением самодельного. При этом публикуемые схемы самодельных программаторов очень часто неоправданно сложны — гораздо сложнее, чем это действительно необходимо. Конечно, если предполагается каждый день прошивать МК, лучше иметь «крутой» программатор. Но если надобность в такой процедуре возникает нечасто, от случая к случаю, то можно вообще обойтись без программатора. Нет, конечно, речь идет не о том, чтобы научиться делать это силой мысли. Имеется в виду, что понимая, как происходит взаимодействие программатора с микроконтроллером при записи и считывании информации в режиме его программирования, мы можем обойтись подручными средствами более широкого назначения. Эти средства должны будут заменить как программную, так и аппаратную части программатора. Аппаратная часть должна обеспечить физическое соединение с микросхемой МК, возможность подавать логические уровни на его входы и считывать данные с его выходов. Программная часть должна обеспечить работу алгоритма, управляющего всеми необходимыми процессами. Отметим также, что качество записи информации в МК не зависит от того, насколько «крутой» у вас программатор. Такого понятия, как «лучше записалось» или «хуже» не существует. Есть только два варианта: «записалось» и «не записалось». Это объясняется тем, что непосредственно процессом записи внутри кристалла руководит сам МК. Нужно лишь обеспечить ему качественное питание (отсутствие помех и пульсаций) и правильно организовать интерфейс. Если по результатам контрольного считывания ошибок не выявлено, то все в порядке — можно использовать контроллер по назначению.

Для того, чтобы, не имея программатора, записать в МК программу, нам потребуется преобразователь порта USB-RS232TTL и , а также . Конвертер USB-RS232TTL позволяет при помощи порта USB создать COM-порт, отличающийся от «настоящего» лишь тем, что на его входах и выходах используются логические уровни TTL, то есть напряжение в интервале от 0 до 5 вольт (подробнее можно почитать в статье » «). Такой конвертер в любом случае полезно иметь в «хозяйстве», так что если у вас его еще нет, непременно стоит приобрести. Что касается логических уровней, то в нашем случае TTL — это даже преимущество перед обычным COM-портом, потому что входы и выходы такого порта можно напрямую подключать к любому микроконтроллеру, питающемуся от напряжения 5 В, в том числе ATtiny и ATmega. Но не пытайтесь использовать обычный COM-порт — там используются напряжения в интервале от -12 до +12 В (либо -15…+15В). Непосредственное соединение с микроконтроллером в этом случае недопустимо!!!

Идея создания скрипта для программы «Перпетуум М», реализующего функции программатора, возникла после ознакомления с рядом публикаций в интернете, предлагающих те или иные решения по прошивке МК. В каждом случае обнаруживались серьезные недостатки или чрезмерные сложности. Часто попадались схемы программаторов, содержащие в себе микроконтроллер и при этом вполне серьезно давались советы типа: «… а чтобы запрограммировать микроконтроллер для этого программатора нам потребуется… правильно — другой программатор!». Далее предлагалось сходить к другу, поискать платную услугу и т.п. Качество программного обеспечения, распространяемого в сети для этих целей, также не впечатлило — замечено множество проблем как с функциональностью, так и с «мутностью» пользовательского интерфейса. Зачастую много времени нужно потратить, чтобы понять, как использовать программу — ее необходимо изучать даже ради осуществления простейших действий. Иная программа может долго и усердно что-то делать, но о том, что ничего в МК не записывается, пользователь узнает только после полного завершения всей прошивки и последующего контрольного считывания. Встречается и такая проблема: пользователь пытается выбрать из списка поддерживаемых кристаллов свой МК, а его в списке нет. В этом случае воспользоваться программой не удастся — внесение в список недостающих МК, как правило, не предусмотрено. Кроме того ручной выбор контроллера из списка выглядит странно, если учесть, что программатор во многих случаях может сам определить тип МК. Все это сказано не для того, чтобы облить грязью существующие продукты, а для того, чтобы объяснить причину появления скрипта к программе «Перпетуум М», описываемого в данной статье. Проблема действительно существует, и она касается в первую очередь новичков, которым не всегда удается преодолеть данную «стену», чтобы сделать свой первый шаг в мир микроконтроллеров. В предлагаемом скрипте учтены недостатки, обнаруженные в других программах. Реализована максимальная «прозрачность» работы алгоритма, предельно простой интерфейс пользователя, не требующий изучения и не оставляющий шанса запутаться и «не туда нажать». При отсутствии нужного МК среди поддерживаемых есть возможность самостоятельно добавить его описание, взяв нужные данные из документации, скачанной с сайта разработчика МК. И, самое главное — скрипт открыт для изучения и модификации. Каждый желающий может, открыв в текстовом редакторе, изучать и править его на свое усмотрение, изменяя на свой вкус существующие функции и добавляя недостающие.

Первая версия скрипта была создана в июне 2015 года. В этой версии реализована только поддержка Atmel»овских МК серий ATtiny и ATmega с функциями записи/чтения флэш-памяти, с настройкой конфигурационных бит, с автоматическим определением типа контроллера. Запись и чтение EEPROM не реализованы. Были планы дополнить функциональность скрипта: добавить запись и чтение EEPROM, реализовать поддержку PIC-контроллеров и т.д. По этой причине скрипт до сих пор не был опубликован. Но из-за нехватки времени осуществление задуманного затянулось, и, чтобы лучшее не становилось врагом хорошего, решено опубликовать имеющуюся версию. Если уже реализованных функций окажется недостаточно, прошу не огорчаться. В этом случае вы можете попробовать самостоятельно добавить нужную функцию. Не стану скрывать: идея создания данного скрипта изначально несет в себе еще и образовательный смысл. Разобравшись в алгоритме и добавив к нему что-то свое, вы сможете глубже понять работу МК в режиме программирования, чтобы в будущем не оказаться в положении девушки перед сломавшимся автомоблем, задумчиво разглядывающей его внутренности и не понимающей, почему «не едет».

2. Интерфейс МК в режиме программирования

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

Но, прежде чем приступить к рассмотрению сигналов, необходимых для формирования SPI, сделаем ряд оговорок. Микроконтроллер имеет конфигурационные биты. Это что-то вроде тумблеров, переключение которых позволяет менять некоторые свойства микросхемы в соответствии с нуждами проекта. Физически это ячейки энергонезависимой памяти, вроде тех, в которые записывается программа. Разница в том, что их очень мало (до трех байт для ATmega), и они не входят в адресное пространство какой-либо памяти. Запись и чтение конфигурационных данных выполняется отдельными командами режима программирования МК. Сейчас важно отметить, что некоторые конфигурационные биты влияют на саму возможность использования SPI. При некоторых их значениях может оказаться, что SPI нельзя будет использовать. Если вам попадется такой микроконтроллер, то метод, предлагаемый в данной статье, не поможет. В этом случае придется либо изменить настройки конфигурационных бит в программаторе, который поддерживает иной режим программирования, либо использовать другой микроконтроллер. Но данная проблема касается только бывших в употреблении МК, либо тех, с которыми уже кто-то неудачно «поигрался». Дело в том, что новые МК поставляются с настройками конфигурационных бит, не препятствующими использованию SPI. Это подтверждается и результатами испытаний скрипта-программатора для программы «Перпетуум М», во время которых были успешно прошиты четыре разных МК (ATmega8, ATmega128, ATtiny13, ATtiny44). Все они были новые. Начальная настройка конфигурационных бит соответствовала документации и не мешала использованию SPI.

Учитывая сказанное выше, следует обращать внимание на следующие биты. Бит SPIEN в явном виде разрешает или запрещает использование SPI, следовательно в нашем случае его значение должно быть разрешающим. Бит RSTDISBL способен превратить один из выводов микросхемы (заранее предопределенный) во вход сигнала «сброс», либо не превратить (в зависимости от записанного в этот бит значения). В нашем случае вход «сброс» необходим (при его отсутствии не получится перевести МК в режим программирования через SPI). Есть еще биты группы CKSEL, задающие источник тактового сигнала. Они не препятствуют использованию SPI, но их тоже необходимо иметь в виду, потому что при полном отсутствии тактовых импульсов, либо при их частоте ниже допустимой для заданной скорости SPI, также ничего хорошего не получится. Обычно у новых МК, имеющих внутренний RC-генератор, биты группы CKSEL настроены на его использование. Нас это вполне устраивает — тактирование обеспечено без каких-либо дополнительных усилий с нашей стороны. Ни кварцевый резонатор припаивать, ни внешний генератор подключать не нужно. Если же указанные биты содержат иную настройку, придется позаботится о тактировании в соответствии с настройкой. В этом случае может потребоваться подключение к МК кварцевого резонатора или внешнего тактового генератора. Но в рамках данной статьи мы не будем рассматривать, как это делается. Примеры подключения МК для программирования, содержащиеся в данной статье, рассчитаны на самый простой случай.


Рис. 1. Обмен данными по SPI в режиме программирования

Теперь обратимся к рисунку 1, взятому из документации на МК ATmega128A. На нем показан процесс передачи одного байта в МК и одновременного приема одного байта из МК. Оба эти процесса, как видим, используют одни и те же тактовые импульсы, поступающие от программатора в микроконтроллер на его вход SCK — один из выводов микросхемы, для которого в режиме программирования по SPI отведена такая роль. Еще две сигнальные линии обеспечивают прием и передачу данных по одному биту за такт. Через вход MOSI данные поступают в микроконтроллер, а с выхода MISO снимаются считываемые данные. Обратите внимание на две пунктирные линии, проведенные от SCK к MISO и MOSI. Они показывают, в какой момент микроконтроллер «проглатывает» выставленный на входе MOSI бит данных, и в какой момент сам выставляет на выход MISO свой бит данных. Все достаточно просто. Но чтобы ввести МК в режим программирования нам еще потребуется сигнал RESET. Не забудем также про общий провод GND и питание VCC. В общей сложности выходит, что к микроконтроллеру для его прошивки по SPI нужно подключить всего 6 проводков. Ниже разберем это подробнее, а пока добавим, что обмен данными с МК в режиме программирования по SPI выполняется пакетами по 4 байта. Первый байт каждого пакета в основном полностью отводится под кодирование команды. Второй байт в зависимости от первого может быть продолжением кода команды, либо частью адреса, а может иметь произвольное значение. Третий байт используется в основном для передачи адресов, но во многих командах может иметь произвольное значение. Четвертый байт обычно передает данные, либо имеет произвольное значение. Одновременно с передачей четвертого байта в некоторых командах принимаются данные, поступающие из МК. Подробности по каждой команде можно найти в документации на контроллер в таблице под названием «SPI Serial Programming Instruction Set». Пока отметим лишь, что весь обмен с контроллером построен из последовательности 32-битных пакетов, в каждом из которых передается не более одного байта полезной информации. Это не очень оптимально, но в целом работает неплохо.

3. Подключение МК для программирования

Чтобы обеспечить подачу на входы микроконтроллера всех необходимых сигналов для организации интерфейса SPI и чтение данных с его выхода MISO, не обязательно создавать программатор. Это легко осуществить при помощи самого обыкновенного конвертера USB-RS232TTL.

В интернете часто можно встретить информацию о том, что такие конвертеры неполноценны, что с ними ничего серьезного сделать нельзя. Но в отношении большинства моделей конвертеров такое мнение ошибочно. Да, существуют в продаже конвертеры, у которых доступны не все входы и выходы по сравнению со стандартным COM-портом (например, только TXD и RXD), имеющие при этом неразборную конструкцию (микросхема залита пластмассой — невозможно добраться до ее выводов). Но такие и покупать не стоит. В некоторых случаях получить недостающие входы и выходы порта можно, подпаяв проводки непосредственно к микросхеме. Пример такого «усовершенствованного» конвертера показан на рисунке 2 (микросхема PL-2303 — подробнее о назначении ее выводов в статье » «). Это одна из самых дешевых моделей, но обладающая своими преимуществами при использовании в самодельных конструкциях. Широко распространены и полнофункциональные шнуры-переходники со стандартным девятиконтактным разъемом на конце, как у COM-порта. От обычного COM-порта они отличаются только уровнями TTL и несовместимостью с устаревшим программным обеспечением и некоторым старым оборудованием. Можно еще отметить, что шнуры на микросхеме CH34x на различных экстремальных тестах показывают себя гораздо более надежными и стабильными по сравнению с преобразователями на PL-2303. Впрочем, при обычном использовании разница не заметна.

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

Рассмотрим подробнее принцип соединения микроконтроллера и конвертера USB-RS232TTL на примере четырех разных моделей МК: ATtiny13, ATtiny44, ATmega8 и ATmega128. На рисунке 3 показана общая схема такого соединения. Вас может удивить, что сигналы RS232 (RTS, TXD, DTR и CTS) используются не по назначению. Но не стоит об этом беспокоиться: программа «Перпетуум М» способна работать с ними напрямую — устанавливать значения на выходах и читать состояния входа. Во всяком случае широко распространенные конвертеры USB-RS232TTL на микросхемах CH34x и PL-2303 такую возможность обеспечивают — это проверено. С другими популярными конвертерами также проблем быть не должно, так как для доступа к порту используются стандартные функции Windows.

Резисторы, показанные на общей схеме, в принципе можно не устанавливать, но все-таки лучше установить. Каково их назначение? Используя ТТЛ»овские входы и выходы конвертера и пятивольтное питание микроконтроллера, мы тем самым избавляемся от необходимости согласования логических уровней — все и так вполне корректно. Значит, соединения могут быть непосредственными. Но во время экспериментов бывает всякое. Например по закону подлости отвертка может упасть как раз в то место, куда она никак не могла бы упасть, и замкнуть то, что ни в коем случае нельзя замыкать. В роли «отвертки», конечно, может оказаться все, что угодно. Резисторы в этом случае иногда уменьшают последствия. Еще одно их назначение состоит в устранении возможного конфликта выходов. Дело в том, что по окончании программирования микроконтроллер переходит в обычный режим работы, и может так получиться, что его вывод, соединенный с выходом конвертера (RTS, TXD или DTR) тоже становится выходом, согласно только что записанной в МК программе. В этом случае будет очень нехорошо, если два напрямую соединенных выхода будут «бороться» — пытаться установить разные логические уровни. В такой «борьбе» кто-то может и «проиграть», а нам этого не надо.

Номиналы трех резисторов выбраны на уровне 4,3 КОм. Это касается соединений выход конвертера — вход микроконтроллера. Точность резисторов роли не играет: можно уменьшить их сопротивление до 1 КОм или увеличить до 10 КОм (но во втором случае увеличивается риск помех при использовании длинных проводов на пути к МК). Что же касается соединения вход конвертера (CTS) — выход микроконтроллера (MISO), то здесь применен резистор сопротивлением 100 Ом. Это объясняется особенностями входа использованного конвертера. Во время испытаний был использован конвертер на микросхеме PL-2303, входы которой, судя по всему, подтянуты к плюсу питания относительно низким сопротивлением (порядка нескольких сот Ом). Чтобы «перебить подтяжку» пришлось поставить резистор со столь маленьким сопротивлением. Впрочем, можно его вообще не ставить. На конвертере это всегда вход. Выходом он стать не может, а значит, конфликта выходов не будет при любом развитии событий.

Если микросхема имеет отдельный вывод AVCC для питания аналогово-цифрового преобразователя (например, ATmega8 или ATmega128), его следует соединить с выводом общего питания VCC. Некоторые микросхемы имеют более одного вывода питания VCC или более одного GND. Например, ATmega128 имеет 3 вывода GND и 2 вывода VCC. В постоянной конструкции одноименные выводы лучше соединить между собой. В нашем же случае на время программирования можно задействовать по одному выводу VCC и GND.

А вот как выглядит подключение ATtiny13. На рисунке показано назначение выводов, используемых при программировании через SPI. Рядом на фото — как временное подключение выглядит в реальности.

Кто-то может сказать, что это несерьезно — соединения на проводках. Но мы же с вами люди здравомыслящие. Наша цель состоит в том, чтобы запрограммировать микроконтроллер, затратив на это минимум времени и прочих ресурсов, а не в том, чтобы перед кем-то покрасоваться. Качество при этом не страдает. Метод «на проводках» в данном случае вполне эффективен и оправдан. Прошивка контроллера — процедура разовая, поэтому нет смысла обвешивать ее «стразиками». Если же предполагается менять прошивку в дальнейшем, не извлекая контроллер из схемы (в готовом изделии), то это учитывается в монтаже при изготовлении устройства. Обычно для этой цели устанавливается разъем (RESET, SCK, MOSI, MISO, GND), а МК может быть прошит даже после установки на плату. Но это уже творческие изыски. Мы же рассматриваем самый простой случай.

Теперь перейдем к МК ATtiny44. Здесь все примерно так же. По рисунку и фото даже новичку не составит труда разобраться с подключением. Подобно ATtiny44 можно подключать МК ATtiny24 и ATtiny84 — назначение выводов у этой троицы совпадает.

Еще один пример временного подключения контроллера для его программирования — ATmega8. Здесь выводов побольше, но принцип тот же — несколько проводков, и вот уже контроллер готов к «заливке» в него информации. Лишний черный провод на фото, идущий от вывода 13, в программировании участия не принимает. Он предназначен для снятия с него звукового сигнала после выхода МК из режима программирования. Это связано с тем, что во время отладки скрипта для «Перпетуум М» в МК закачивалась программа музыкальной шкатулки.

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

Для полноты картины посмотрим подключение микросхемы МК с большим числом «ножек». Назначение лишнего черного провода на фото, идущего от вывода 15, точно такое же, как в случае с ATmega8.

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

Прежде чем переходить к программной части, убедитесь, что драйвер конвертера USB-RS232TTL корректно установлен (проверьте диспетчер устройств Windows). Запомните или запишите номер виртуального COM-порта, появляющегося при подключении конвертера. Этот номер нужно будет вписать в текст скрипта, о котором читайте ниже.

4. Скрипт — программатор для «Перпетуум М»

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

Архив со скриптом следует распаковать в ту же папку, где находится программа perpetuum.exe. В этом случае при запуске файла perpetuum.exe на экран будет выводиться меню со списком установленных скриптов, среди которых будет строка «Программатор МК AVR» (она может быть единственной). Именно эта строка нам и потребуется.

Скрипт находится в папке PMS в файле «Программатор МК AVR.pms». Этот файл можно просматривать, изучать и править при необходимости в обычном текстовом редакторе вроде «Блокнота» Windows. Перед использованием скрипта скорее всего потребуется внести изменения в текст, связанные с настройкой порта. Для этого уточните в диспетчере устройств Windows имя используемого порта и, при необходимости, внесите соответствующую поправку в строку «ИмяПорта=»COM4″;» — вместо цифры 4 может стоять другая цифра.
Также при использовании другой модели конвертера USB-RS232TTL может потребоваться изменение настроек инвертирования сигналов (строки скрипта, начинающиеся со слова «Высокий»). Проверить инвертирование сигналов конвертером USB-RS232TTL можно с помощью одного из примеров, содержащегося в инструкции к программе «Перпетуум М» (раздел функций для работы с портом).

Во вложенной папке MK_AVR находятся файлы с описаниями поддерживаемых контроллеров. Если нужного контроллера среди них не окажется, вы можете добавить нужный самостоятельно, действуя по аналогии. Возьмите за образец один из файлов, и при помощи текстового редактора введите необходимые данные, взяв их из документации на свой микроконтроллер. Главное — будьте внимательны, вводите данные без ошибок, иначе МК не запрограммируется, или запрограммируется неправильно. В исходной версии поддерживаются 6 микроконтроллеров: ATtiny13, ATtiny24, ATtiny44, ATtiny84, ATmega8 и ATmega128. В скрипте реализовано автоматическое распознавание подключенного контроллера — вручную указывать не нужно. При отсутствии считанного из МК идентификатора среди имеющихся описаний, выдается сообщение, что распознать контроллер не удалось.

В архиве со скриптом содержится также дополнительная информация. В папке «inc-файлы контроллеров AVR» находится очень полезная и обширная коллекция файлов описаний контроллеров. Эти файлы используются при написании собственных программ для МК. Еще четыре папки «MusicBox_…» содержат файлы с программой на Ассемблере и готовой к закачке в МК прошивкой отдельно для ATtiny13, ATtiny44, ATmega8 и ATmega128. Если вы уже подключили один из этих МК для программирования, как это предложено в данной статье, то можете прямо сейчас его прошить — получится музыкальная шкатулка. Об этом ниже.

При выборе в меню скриптов строчки «Программатор МК AVR», скрипт начинает исполняться. При этом он открывает порт, посылает в МК команду перехода в режим программирования, принимает подтверждение от МК об успешном переходе, запрашивает идентификатор МК и отыскивает описание данного МК по его идентификатору среди имеющихся файлов с описаниями. Если не находит нужного описания, выдает соответствеющее сообщение. Если же описание найдено, далее открывается главное меню программатора. Его скриншот вы можете видеть на рисунке 8. Далее разобраться не сложно — меню очень простое.

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

Некоторые ограничения функциональности описаны непосредственно в тексте скрипта:
//реализована запись только с нулевого адреса (Extended Segment Address Record игнорируется, LOAD OFFSET — тоже)
//порядок и непрерывность следования записей в HEX-файле не проверяется
//контрольная сумма не проверяется
Это касается работы с HEX-файлом, из которого берется код прошивки для МК. Если этот файл не искажен, проверка контрольной суммы ни на что не повлияет. Если искажен — средствами скрипта это выявить не удастся. Остальные ограничения в большинстве случаев не помешают, но иметь в виду их все-таки нужно.

5. Музыкальная шкатулка — простая поделка для начинающих

Если у вас есть один из этих микроконтроллеров: ATtiny13, ATtiny44, ATmega8 или ATmega128, вы можете легко превратить его в музыкальную шкатулку или музыкальную открытку. Для этого достаточно записать в МК соответствующую прошивку — одну из тех четырех, которые размещены в папках «MusicBox_…» в одном архиве со скриптом. Коды прошивок хранятся в файлах с расширением «.hex». Использовать ATmega128 для такой поделки, конечно, «жирновато», как и ATmega8. Но это может быть полезно для тестирования или экспериментов, иначе говоря — в учебных целях. Тексты программ на Ассемблере также прилагаются. Программы создавались не с нуля — за основу была взята программа музыкальной шкатулки из книги А.В.Белова «Микроконтроллеры AVR в радиолюбительской практике». Исходная программа претерпела ряд существенных изменений:
1. адаптирована для каждого из четырех МК: ATtiny13, ATtiny44, ATmega8 и ATmega128
2. ликвидированы кнопки — к контроллеру вообще ничего не нужно подключать, кроме питания и звукоизлучателя
(мелодии воспроизводятся одна за другой в бесконечном цикле)
3. длительность каждой ноты уменьшена на длительность паузы между нотами для устранения нарушения музыкального ритма
4. подключена восьмая мелодия, незадействованная в книжной версии
5. из субъективного: некоторые «улучшайзинги» для оптимизации и более легкого восприятия алгоритма

В некоторых мелодиях слышится фальшь и даже грубые ошибки, особенно в «Улыбке» — в середине. Коды мелодий взяты из книги (а точнее — скачаны с сайта автора книги вместе с исходным asm-файлом) и не подвергались изменениям. Судя по всему, в кодировке мелодий имеются ошибки. Но это не проблема — кто «дружит» с музыкой, без труда во всем разберется и исправит.

В ATtiny13 из-за отсутствия 16-битного счетчика для воспроизведения нот пришлось использовать 8-битный, что привело к некоторому снижению точности звучания нот. Но на слух это мало заметно.

Насчет конфигурационных бит. Их настройка должна соответствовать состоянию нового микроконтроллера. Если ваш МК ранее где-то использовался, нужно проверить состояние его конфигурационных бит, и, при необходимости, привести их в соответствие настройкам нового микроконтроллера. Узнать состояние конфигурационных бит нового микроконтроллера можно из документации на этот МК (раздел «Fuse Bits»). Исключение составляет ATmega128. У этого МК имеется бит M103C, который включает режим совместимости с более старым ATmega103. Активизация бита M103C сильно урезает возможности ATmega128, причем у нового МК этот бит активен. Нужно сбросить M103C в неактивное состояние. Для манипуляций с конфигурационными битами используйте соответствующий раздел меню скрипта-программатора.

Схему музыкальной шкатулки приводить нет смысла: в ней только микроконтроллер, питание и пьезозвукоизлучатель. Питание подается точно так же, как мы это проделали при программировании МК. Звукоизлучатель подключается между общим проводом (вывод GND контроллера) и одним из выводов МК, номер которого можно посмотреть в файле с ассемблерным кодом программы (*.asm). В начале текста программы для каждого МК в комментариях имеется строчка: «звуковой сигнал формируется на выводе ХХ». При завершении работы скрипта — программатора микроконтроллер выходит из режима программирования и переходит в обычный режим работы. Сразу же начинается воспроизведение мелодий. Подключив звукоизлучатель, можно это проверить. Оставлять звукоизлучатель подключенным во время программирования кристалла можно только в том случае, если звук снимается с вывода, не задействованного в SPI, иначе дополнительная емкость на выводе может помешать программированию.

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

Общая информация

Микроконтроллеры встречаются везде. Их можно найти в холодильниках, стиральных машинках, телефонах, заводских станках и большом количестве других технических устройств. Микроконтроллеры бывают как простыми, так и чрезвычайно сложными. Последние предлагают значительно больше возможностей и функционала. Но разбираться сразу в сложной технике не выйдет. Первоначально необходимо освоить что-то простое. И в качестве образца будет взят Atmega8. Программирование на нём не является сложным благодаря грамотной архитектуре и дружелюбному интерфейсу. К тому же он является обладателем достаточной производительности, чтобы использовать в большинстве Более того, они применяются даже в промышленности. В случае с Atmega8 программирование предусматривает знание таких языков как AVR (C/Assembler). С чего же начать? Освоение этой технологии возможно тремя путями. И каждый выбирает сам, с чего начать работу с Atmega8:

  1. Программирование через Arduino.
  2. Покупка готового устройства.
  3. Самостоятельная сборка микроконтроллера.

Нами будет рассмотрен первый и третий пункт.

Arduino

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

  1. Низкий порог требований. Не нужно обладать специальными навыками и умениями для разработки технических устройств.
  2. Широкий спектр элементов будет доступен для подключения без дополнительной подготовки.
  3. Быстрое начало разработки. С Arduino можно сразу переходить к созданию устройств.
  4. Наличие большого количества учебных материалов и примеров реализаций различных конструкций.

Но есть и определённые минусы. Так, Arduino программирование Atmega8 не позволяет глубже окунуться в мир микроконтроллера и разобраться во многих полезных аспектах. Кроме этого, придётся изучить язык программирования, что отличается от применяемых AVR (C/Assembler). И ещё: Arduino имеет довольно узкую линейку моделей. Поэтому рано или поздно возникнет необходимость использовать микроконтроллер, что не используется в платах. А в целом это неплохой вариант работы с Atmega8. Программирование через Arduino позволит получить уверенный старт в мире электроники. И у человека вряд ли опустятся руки из-за неудач и проблем.

Самостоятельная сборка

Благодаря дружелюбности конструкции их можно сделать самими. Ведь для этого нужны дешевые, доступные и простые комплектующие. Это позволит хорошо изучить устройство микроконтроллера Atmega8, программирование которого после сборки будет казаться более лёгким. Также при необходимости можно самостоятельно подобрать иные комплектующие под конкретную задачу. Правда, здесь есть и определённый минус — сложность. Самостоятельно собрать микроконтроллер, когда нет нужных знаний и навыков, нелегко. Этот вариант мы и рассмотрим.

Что же нужно для сборки?

Первоначально необходимо заполучить сам Atmega8. Программирование микроконтроллера без него самого, знаете ли, невозможно. Он обойдётся в несколько сотен рублей — обеспечивая при этом достойный функционал. Также стоит вопрос о том, как будет осуществляться программирование Atmega8. USBAsp — это довольно хорошее устройство, что себя зарекомендовало с лучшей стороны. Но можно использовать и какой-то другой программатор. Или же собрать его самостоятельно. Но в таком случае существует риск, что при некачественном создании он превратит микроконтроллер в неработающий кусочек пластика и железа. Также не помешает наличие макетной платы и перемычек. Они не обязательны, но позволят сэкономить нервы и время. И напоследок — нужен источник питания на 5В.

Программирование Atmega8 для начинающих на примере

Давайте рассмотрим, как в общих чертах осуществляется создание какого-то устройства. Итак, допустим, что у нас есть микроконтроллер, светодиод, резистор, программатор, соединительные провода, и источник питания. Первый шаг — это написание прошивки. Под нею понимают набор команд для микроконтроллера, что представлен в качестве конечного файла, имеющего специальный формат. В нём необходимо прописать подключение всех элементов, а также взаимодействие с ними. После этого можно приступать к сборке схемы. На ножку VCC следует подать питание. К любой другой, предназначенной для работы с устройствами и элементами,подключается сначала резистор, а потом светодиод. При этом мощность первого зависит от потребностей в питании второго. Можно ориентироваться по такой формуле: R=(Up-Ups)/Is. Здесь p — это питание, а s — светодиод. Давайте представим, что у нас есть светодиод, потребляющий 2В и требующий ток питания на уровне 10 мА, переводим в более удобный для математических операций вид и получаем 0.01А. Тогда формула будет выглядеть следующим образом: R=(5В-2В)/0.01А=3В/0.01А=300 Ом. Но на практике часто оказывается невозможным подобрать идеальный элемент. Поэтому берётся наиболее подходящий. Но нужно использовать резистор с сопротивлением выше значения, полученного математическим путём. Благодаря такому подходу мы продлим срок его службы.

А что же дальше?

Итак, у нас есть небольшая схема. Теперь осталось подключить к микроконтроллеру программатор и записать в его память прошивку, что была создана. Здесь есть один момент! Выстраивая схему, необходимо её создавать таким образом, чтобы микроконтроллер можно было прошивать без распайки. Это позволит сберечь время, нервы и продлит срок службы элементов. В том числе и Atmega8. Внутрисхемное программирование, нужно отметить, требует знаний и умений. Но оно же позволяет создавать более совершенные конструкции. Ведь часто бывает, что во время распайки элементы повреждаются. После этого схема готова. Можно подавать напряжение.

Важные моменты

Хочется дать новичкам полезные советы про программирование Atmega8. Встроенные переменные и функции не менять! Прошивать устройство созданной программой желательно после её проверки на отсутствие «вечных циклов», что заблокируют любое иное вмешательство, и с использованием хорошего передатчика. В случае использования самоделки для этих целей следует быть морально готовым к выходу микроконтроллера из строя. Когда будете прошивать устройство с помощью программатора, то следует соединять соответствующие выходы VCC, GND, SCK, MOSI, RESET, MISO. И не нарушайте технику безопасности! Если техническими характеристиками предусмотрено, что должно быть питание в 5В, то нужно придерживаться именно такого напряжения. Даже использование элементов на 6В может негативно сказать на работоспособности микроконтроллера и сократить срок его службы. Конечно, батареи на 5В имеют определённые расхождения, но, как правило, там всё в разумных рамках. К примеру, максимальное напряжение будет держаться на уровне 5,3В.

Обучение и совершенствование навыков

На счастье, Atmega8 является очень популярным микроконтроллером. Поэтому найти единомышленников или же просто знающих и умеющих людей не составит труда. Если нет желания изобретать заново велосипед, а просто хочется решить определённую задачу, то можно поискать требуемую схему на просторах мировой сети. Кстати, небольшая подсказка: хотя в русскоязычном сегменте робототехника довольно популярна, но, если нет ответа, то следует его поискать в англоязычном — он содержит на порядок большее количество информации. Если есть определённые сомнения в качестве имеющихся рекомендаций, то можно поискать книги, где рассматривается Atmega8. Благо, компания-производитель берёт во внимание популярность своих разработок и снабжает их специализированной литературой, где опытные люди рассказывают, что и как, а также приводят примеры работы устройства.

Сложно ли начать создавать что-то своё?

Достаточно иметь 500-2000 рублей и несколько свободных вечеров. Этого времени с лихвой хватит, чтобы ознакомиться с архитектурой Atmega8. После небольшой практики можно будет спокойно создавать свои собственные проекты, выполняющие определённые задачи. К примеру, роботизированную руку. Одного Atmega8 должно с лихвой хватить, чтобы передать основные моторные функции пальцев и кисти. Конечно, это довольно сложная задача, но вполне посильная. В последующем вообще можно будет создавать сложные вещи, для которых понадобятся десятки микроконтроллеров. Но это всё впереди, перед этим необходимо получить хорошую школу практики на чем-то простом.

Чтобы перенести с компьютера в микроконтроллер нам понадобится USBasp и программа AVRDUDE.
Сегодня существует широкий выбор программаторов, предназначенных для программирования микроконтроллеров AVR. Среди них можно найти много самодельных, который даже трудно назвать программаторами, поскольку они с помощью всего лишь нескольких резисторов напрямую подключаются к COM порту. Однако современные компьютеры и ноутбуки уже практически не оборудываются COM портами, поэтому одним из основных критериев при выборе программатора является возможность подключения его к USB порту. Наиболее дешевый, простой и очень распространенный – это программатор USBasp. Его можно приобрести практически в любом радиомагазине по доступной цене. Стоимость его в китайских интернет магазина находится в пределах от 1,5 $ до 3 $.

Программатор

USBasp


Связь компьютера с микроконтроллером осуществляется посредством программатора USBasp через USB порт, а данные передаются по интерфейсу SPI

S

erial
P

eripheral
I

nterface
(последовательный периферийный интерфейс). Для связи МК с программатором задействуются специальные выводы: MOSI, MISO, SCK, RESET, VCC, GND. Хотя SPI

предполагает использование всего трех выводов MOSI, MISO и SCK, но мы будем задействовать все шесть выводов.

При обмене данными по интерфейсу SPI

микроконтроллер может одновременно либо принимать (вывод MISO) либо передавать данные (вывод MOSI). Установка режима приема или передачи данных осуществляется путем подачи определенного импульса на вывод SCK.

Разъем программатора, как правило, имеет 10 пинов и подключается к микроконтроллеру с помощью 10 проводного шлейфа. Однако удобней пользоваться шлейфами, которые имеют переходник на 6 пин, так как в таком случае все пины заняты. У десяти пинового разъема одни пин остается не занятым, а четыре пина подключены к общему проводу (GND).

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

Фото подсоединенного программатора к микроконтроллеру ATmega8 приведено ниже.

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

Настройка


Теперь нам осталось выполнить финальный шаг. Запускаем программу AVRDUDE. По умолчанию открывается вкладка Program. В нижней части окна в меню Настройки выбираем тип программатора usbasp
. Далее в категории Микроконтроллер
выбираем наш микроконтроллер ATmega8. Ниже, в категории Flash
кликаем по значку троеточия и в открывшемся меню указываем путь к скомпилированному файлу с расширением hex
. Путь к файлу и сам файл будут теми же, что мы ранее задавали в .

Чтобы убедится в том, что программатор определен операционной системой (драйвер программатора корректно установлен) и правильно подключен к микроконтроллеру, кликаем по кнопке Чтение
. Если ошибок нет, то появится окно с записью “Калибровочные ячейки генератора считаны!”
И в верхнем окошке отобразится шестнадцатеричное число. У каждого МК это число индивидуальное.

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

Теперь кликаем по кнопке Программировать в категории Flash
. При успешной записи программы в МК появляется окно с записью, приведенной ниже.

Результат записанной, или, как еще говорят, прошитой программы – это засветившийся светодиод, подключенный к выводу PC0 нашего микроконтроллера.

Доброго времени суток. Продолжим. После того, как мы ознакомились с процессом отладки написанной нами программы в «atmel studio» и виртуально собрали схему с одним светодиодом в «proteus», пришло время собрать схему в «железе» и прошить микроконтроллер.

Для программирования опытного экземпляра (atmega 8
) будем использовать программатор USBASP. Он выглядит следующим образом:

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

Первый вывод отмечен на разъеме стрелочкой.


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

Задача состоит в том, чтобы соединить выводы программатора с выводами «камня».

Подключаем джамперы в 10 контактный разъем. Задействуем следующие выводы MOSI, RST, SCK, MISO, VTG (VCC), GND.

Надеюсь вы уже скачали datasheet на atmega8. Если нет, его можно скачать . Смотрим на распиновку выводов микроконтроллера.

Соединяем джамперы со следующими выводами:

  • VCC к 7 выводу МК;
  • SCK к 19 выводу МК;
  • MISO к 18 выводу МК;
  • MOSI к 17 выводу МК;
  • GND (10 вывод программатора) к 8 выводу МК;
  • RST к 1 выводу МК;

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

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

Если вы работаете в windows 7 или выше, могут возникнуть небольшие трудности. Драйвера для программатора достаточно старые, поэтому у них нет цифровой подписи. При попытке установить такой драйвер операционка выдаст, что-то на подобии этого *

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

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

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

Надеюсь у вас всё получилось и программатор готов к работе.

Переходим к сбору схемы со светодиодом.

Для прошивки микроконтроллера будем использовать программу «avrdudeprog». Она лежит в общем архиве.

Выбираем atmega8 из списка микроконтроллеров. После того, как выбрали МК появиться окошко, которое известит о том, что фьюзы и Lock биты установлены по умолчанию.

Затем открываем вкладку фьюзы (Fuses). Простыми словами Fuses — это конфигурационные настройки МК, с которыми лучше не играть. Для случая, когда вы приобрели такой же контроллер, как я и у вас нет внешнего кварцевого резонатора (вы используете внутренний генератор тактовой частоты), выставляете точно такие галочки, как представлены на картинке. Обязательно возле пункта «инверсные» должна стоять галочка.

Выставленные настройки «командуют» Atmega8A выполнять свою работу при условии тактирования от внутреннего генератора (частота тактирования 8 МГц). Для того, чтобы настройки вступили в силу нужно нажать кнопку «Программирование». Но перед нажатием еще два раза проверьте все ли выставили должным образом.

Возвращаемся на страницу «Program».

После того, как мы уже сообщили программе, какой именно микроконтроллер будем шить, выбираем файл прошивку, которую написали в прошлом уроке. Она имеет расширение HEX. Находится в папке «Debug»

Перед тем, как прошивать «камушек» нажимаем на кнопку «Стереть все». Это обезопасит вас от непонятных ошибок (вдруг камень уже шили):

Наслаждаемся результатом своей работы 🙂 Продолжение следует…

Почему именно с этой линейки?

Ну, может кто знает, может кто-то не знает, микроконтроллер AVR — это продукт компании Atmel.

Ну почему же всё-таки именно с линейки AVR?

Во-первых, микроконтроллеры AVR — они повсеместно доступны, они есть в любых магазинах, цена их невысока.

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

То есть, при написании программ вы не будете нарушать ни чьих авторских прав.

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

Поэтому, будем программировать мы именно под микроконтроллеры AVR.

Начнём мы с такого представителя этой линейки, как микроконтроллер Atmega 8.

ATMEGA 8A

Почему именно с него, с Atmega 8?

Потому что, во-первых, он вполне себе такой полноправный микроконтроллер и недорогой.

Имеет на своём борту три порта ввода-вывода. Мы не будем здесь вдаваться в подробности, что такое порты. Вкратце, порты — это такие шины данных, которые работают в двух направлениях — и на вывод, и на ввод.

Порт B.

У порта B, он у нас неполный, имеет 6 ножек.

0, 1 ножка, 2, 3, 4 и 5. То есть, 6 ножечек.
Порт C
также у нас неполный, от нулевой до шестой ножки.
А вот порт D
у нас полноправный порт, имеет все 8 ног, т.е. байт данных от 0 до 7.
Питается микроконтроллер от 5 вольт. Можно питать его также от 3,3 вольта, он также будет отлично работать. Но единственное, частота тактирования может быть выставлена только 8 мегагерц максимально. 16 мы не можем выставить при трёх вольтах, можем только при пяти вольтах. Подается питание на 7 ножку. Общий провод — восьмая.
Ну теперь давайте посмотрим, откуда мы можем взять программное обеспечение для программирования.
У компании Atmel имеется свой сайт www.atmel.com. На главной страничке, далеко ходить не нужно, в правой части страницы под главным баннером находится вот Download Atmel Studio.

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

Содержание

  • Введение в микроконтроллеры
  • Программирование микроконтроллеров для начинающих
  • Семейства микроконтроллеров
  • Языки программирования
  • Микроконтроллеры PIC

Введение в микроконтроллеры

Это миниатюрные компьютеры, все составляющие которых (процессор, ОЗУ, ПЗУ) располагаются на одном кристалле. От микропроцессоров их отличает наличие таймеров, контроллеров, компараторов и других периферийных устройств. В настоящее время микроконтроллеры используются при производстве:

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

Управление осуществляется при помощи специальных программ.

Программирование микроконтроллеров для начинающих

Программирование микроконтроллеров для начинающих

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

  • встраиваемые;
  • 8-, 16- и 32-разрядные;
  • цифровые сигнальные процессоры.

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

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

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

Семейства микроконтроллеров

Чаще всего встречаются микроконтроллеры следующих семейств:

  • MSP430 (TI);
  • ARM (ARM Limited);
  • MCS 51 (INTEL);
  • STMB (STMicroelectronics);
  • PIC (Microchip);
  • AVR (Atmel);
  • RL78 (Renesas Electronics).

Одной из наиболее популярных в электронной промышленности является продукция компании Atmel, построенная на базе RISC-ядра. Первые микросхемы, разработанные в 1995 году, относятся к группе Classic. Изучать программирование микроконтроллеров AVR для начинающих желательно на более современных моделях:

  • Mega — семейство мощных микросхем с развитой архитектурой.
  • Tiny — недорогие изделия, имеющие восемь выводов.

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

Изделия компании «Атмел» просты и понятны. Однако для использования всего функционала придется разработать программное обеспечение. Приступать к программированию микроконтроллеров AVR для начинающих рекомендуется с загрузки специализированной среды Atmel Studio. Актуальная версия предоставляется официальным сайтом производителя на бесплатной основе. Для разработки ПО в этой среде дополнительные программные компоненты не требуются.

Комплекс «Атмел Студио» включает огромное количество примеров готовых проектов. Это поможет новичку быстрее освоить базовые возможности и начать создавать собственные программы. В нем также имеются модули для компиляции и окончательной отладки кода. Параллельно с его освоением нужно изучать языки программирования. Без них разработать программное обеспечение невозможно.

Языки программирования

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

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

На самом деле, можно использовать практически любые языки программирования микроконтроллеров. Но популярнее всех С/С++. Это язык высокого уровня, позволяющий работать с максимальным комфортом. Более того, в разработке архитектуры AVR принимали участие создатели Си. Поэтому микросхемы производства «Атмел» адаптированы именно к этому языку.

С/С++ — это гармоничное сочетание низкоуровневых и высокоуровневых возможностей. Поэтому в код можно внедрить вставки на Ассемблере. Готовый программный продукт легко читается и модифицируется. Скорость разработки достаточно высокая. При этом доскональное изучение архитектуры МК и системы команд ЦП не требуется. Компиляторы Си снабжаются библиотеками внушительного размера, что облегчает работу программиста.

Нужно отметить, что выбор оптимального языка программирования зависит также от аппаратного обеспечения. При малом количестве оперативной памяти использовать высокоуровневый Си нецелесообразно. В данном случае больше подойдет Ассемблер. Он обеспечивает максимальное быстродействие за счет короткого кода программы. Универсальной среды программирования не существует, но в большинстве бесплатных и коммерческих приложений можно использовать как Ассемблер, так и С/С++.

Микроконтроллеры PIC

Первые микроконтроллеры PIC появились во второй половине прошлого века. Быстрые 8-разрядные микросхемы компании Microchip мгновенно завоевали популярность. Двухшинная гарвардская архитектура обеспечивает беспрецедентную скорость. Ее разрабатывали на основе набора регистров, для которого характерно разделение шин.

Выбирая язык программирования микроконтроллеров PIC, необходимо учитывать, что в основе микросхем семейства лежит уникальная конструкция RISC-процессора. Симметричная система команд позволяет произвольно выбирать метод адресации, выполнять операции в любом регистре. На данный момент компания «Микрочип» выпускает 5 разновидностей МК, которые совместимы по программному коду:

  1. PIC18CXXX (75 команд, встроенный аппаратный стек);
  2. PIC17CXXX (58 команд 16-разрядного формата);
  3. PIC16CXXX (35 команд, большой набор периферийных устройств);
  4. PIC16C5X (33 команды 12-разрядного формата, корпуса с 18–28 выводами);
  5. PIC12CXXX (версии с 35 и 33 командами, интегрированный генератор).

В большинстве случаев МК PIC имеют однократно программируемую память. Встречаются более дорогие модели с Flash или ультрафиолетовым стиранием. Ассортимент из 500 наименований позволяет подобрать изделие для любой задачи. Сейчас производитель концентрирует усилия на развитии 32-разрядных версий с увеличенным объемом памяти.

Языки программирования микроконтроллеров PIC — это Ассемблер и Си. Для кодирования подходят любые интегрированные среды разработки (IDE). Программировать с их помощью очень удобно. Они автоматически переводят текст программы в машинный код. Важной характеристикой IDE является возможность пошаговой симуляции работы готового ПО. Мы рекомендуем пользоваться средой разработки MPLAB. Ее созданием занималась компания Microchip.

Перед началом работы в MPLAB советуем каждый раз заводить отдельную папку. Это нужно, чтобы не запутаться в файлах проектов. Интерфейс программы интуитивно понятный, и трудностей с ним возникнуть не должно. Для отладки используются фирменные отладчики Pickit, ICD, REAL ICE, IC PROG. В них имеется возможность просмотра содержимого памяти, установки контрольных точек.

6 августа 2017 в 22:27
| Обновлено 1 мая 2020 в 03:36 (редакция)
Опубликовано:

Статьи

Понравилась статья? Поделить с друзьями:
  • Zoncare экг аппарат инструкция по применению
  • Грузовые автомобили shacman руководство по эксплуатации
  • Киа рио x line руководство по ремонту
  • Аспаркам инструкция по применению цена в казахстане
  • Трм 500 овен руководство пользователя