<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Alex.StarSpirals.Net</title>
	<atom:link href="http://alex.starspirals.net/feed/" rel="self" type="application/rss+xml" />
	<link>http://alex.starspirals.net</link>
	<description>персональный блог</description>
	<lastBuildDate>Mon, 05 Sep 2011 11:34:34 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Музыка на ATtiny2313: Управление при помощи MIDI-протокола</title>
		<link>http://alex.starspirals.net/2011/08/31/%d0%bc%d1%83%d0%b7%d1%8b%d0%ba%d0%b0-%d0%bd%d0%b0-attiny2313-%d1%83%d0%bf%d1%80%d0%b0%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d0%b5-%d0%bf%d1%80%d0%b8-%d0%bf%d0%be%d0%bc%d0%be%d1%89%d0%b8-midi-%d0%bf%d1%80/</link>
		<comments>http://alex.starspirals.net/2011/08/31/%d0%bc%d1%83%d0%b7%d1%8b%d0%ba%d0%b0-%d0%bd%d0%b0-attiny2313-%d1%83%d0%bf%d1%80%d0%b0%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d0%b5-%d0%bf%d1%80%d0%b8-%d0%bf%d0%be%d0%bc%d0%be%d1%89%d0%b8-midi-%d0%bf%d1%80/#comments</comments>
		<pubDate>Wed, 31 Aug 2011 12:21:27 +0000</pubDate>
		<dc:creator>Алексей</dc:creator>
				<category><![CDATA[Видео]]></category>
		<category><![CDATA[Заметки]]></category>
		<category><![CDATA[Музыка]]></category>
		<category><![CDATA[Электроника]]></category>
		<category><![CDATA[atmel]]></category>
		<category><![CDATA[attiny2313]]></category>
		<category><![CDATA[avr]]></category>
		<category><![CDATA[avr assembler]]></category>
		<category><![CDATA[com]]></category>
		<category><![CDATA[diy]]></category>
		<category><![CDATA[max232]]></category>
		<category><![CDATA[midi]]></category>
		<category><![CDATA[midi avr]]></category>
		<category><![CDATA[midi через com порт]]></category>
		<category><![CDATA[midi-com]]></category>
		<category><![CDATA[music]]></category>
		<category><![CDATA[roland serial midi]]></category>
		<category><![CDATA[serial]]></category>
		<category><![CDATA[бетховен]]></category>
		<category><![CDATA[лунная соната]]></category>
		<category><![CDATA[миди]]></category>
		<category><![CDATA[миди на attiny2313]]></category>
		<category><![CDATA[музыка]]></category>
		<category><![CDATA[подключение midi]]></category>
		<category><![CDATA[программирование]]></category>
		<category><![CDATA[протокол]]></category>
		<category><![CDATA[сделай сам]]></category>
		<category><![CDATA[синтезатор]]></category>

		<guid isPermaLink="false">http://alex.starspirals.net/?p=1427</guid>
		<description><![CDATA[Данная статья серии посвящена использованию протокола MIDI для воспроизведения музыки на устройстве, построенном на микроконтроллере ATtiny2313. В ней я расскажу о приёме MIDI-сообщений для управления ранее разработанным синтезатором. Содержание [Скрыть] 1 Введение 2 Связь с ПК посредством MIDI 3 Краткое &#8230; <a href="http://alex.starspirals.net/2011/08/31/%d0%bc%d1%83%d0%b7%d1%8b%d0%ba%d0%b0-%d0%bd%d0%b0-attiny2313-%d1%83%d0%bf%d1%80%d0%b0%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d0%b5-%d0%bf%d1%80%d0%b8-%d0%bf%d0%be%d0%bc%d0%be%d1%89%d0%b8-midi-%d0%bf%d1%80/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><img src="http://alex.starspirals.net/wp-content/uploads/2011/08/MIDI-logo.png" alt="" title="MIDI logo" width="144" height="86" class="alignright size-full wp-image-1430" />Данная статья серии посвящена использованию протокола MIDI для воспроизведения музыки на устройстве, построенном на микроконтроллере ATtiny2313. В ней я расскажу о приёме MIDI-сообщений для управления <a href="http://alex.starspirals.net/2010/08/20/%D0%BC%D1%83%D0%B7%D1%8B%D0%BA%D0%B0-%D0%BD%D0%B0-attiny2313-1/" class="liinternal">ранее разработанным</a> синтезатором.<span id="more-1427"></span></p>
<div class="wm-toc">
<div align="center"><span class="wm-toc-header">Содержание</span> <span class="wm-toc-link">[<a id="wm-toc-toggle" href="javascript:wmTocToggle();">Скрыть</a>]</span></div>
<div id="wm-toc-container">
<ol class='wm-first-level'>
<li><a href="#wm-toc-1427-8-1" class="liinternal">8.1 Введение</a></li>
<li><a href="#wm-toc-1427-8-2" class="liinternal">8.2 Связь с ПК посредством MIDI</a></li>
<li><a href="#wm-toc-1427-8-3" class="liinternal">8.3 Краткое описание MIDI-протокола</a>
<ol>
<li><a href="#wm-toc-1427-8-3-1" class="liinternal">8.3.1 Формат сообщений</a></li>
<li><a href="#wm-toc-1427-8-3-2" class="liinternal">8.3.2 Note On сообщение — 0X9</a></li>
<li><a href="#wm-toc-1427-8-3-3" class="liinternal">8.3.3 Note Off сообщение — 0X8</a></li>
<li><a href="#wm-toc-1427-8-3-4" class="liinternal">8.3.4 Controller Change (CC) сообщение — 0XB</a></li>
</ol>
</li>
<li><a href="#wm-toc-1427-8-4" class="liinternal">8.4 Схема устройства</a></li>
<li><a href="#wm-toc-1427-8-5" class="liinternal">8.5 Программный код</a>
<ol>
<li><a href="#wm-toc-1427-8-5-1" class="liinternal">8.5.1 Приём MIDI-сообщений</a></li>
<li><a href="#wm-toc-1427-8-5-2" class="liinternal">8.5.2 Обработка команд</a></li>
<li><a href="#wm-toc-1427-8-5-3" class="liinternal">8.5.3 Полный код программы</a></li>
<li><a href="#wm-toc-1427-8-5-4" class="liinternal">8.5.4 Результат</a></li>
</ol>
</li>
<li><a href="#wm-toc-1427-8-6" class="liinternal">8.6 Заключение</a></li>
<li><a href="#wm-toc-1427-8-7" class="liinternal">8.7 Примечания</a></li>
<li><a href="#wm-toc-1427-8-8" class="liinternal">8.8 Использованные материалы</a></li>
</ol>
</div>
</div>
<h2 id="wm-toc-1427-8-1" name="wm-toc-1427-8-1">Введение</h2>
<p>Протоколу MIDI (Musical Instrument Digital Interface, дигитальный интерфейс для музыкальных инструментов) в 2013 году исполняется 30 лет. Однако он настолько хорошо удовлетворяет потребности музыкантов и музыкальных инженеров, что вряд ли в ближайшее время он будет вытеснен каким-либо другим стандартом.</p>
<p>Статья организована так: сперва я расскажу, как можно подключить два MIDI-устройства друг к другу (рассмотрим вариант ATtiny2313 и ПК), затем в двух словах опишу основные используемые в стандарте команды, и, наконец, покажу как можно ранее разработанное устройство подружить с MIDI.</p>
<h2 id="wm-toc-1427-8-2" name="wm-toc-1427-8-2">Связь с ПК посредством MIDI</h2>
<p>Строго говоря, для использования MIDI протокола желательно соответствующее оборудование. Например, аудио-карта, оснащённая MIDI-интерфейсом (здесь до сих пор используется популярный некогда <a href="http://en.wikipedia.org/wiki/DIN_connector" rel="nofollow" class="liwikipedia" onclick="pageTracker._trackPageview('/outgoing/en.wikipedia.org/wiki/DIN_connector?referer=');">DIN5</a>), или специальное USB-устройство. Это не всегда под рукой, а использовать MIDI всё-таки хочется. Тут можно вспомнить про некий стандарт RS-232 и поискать на заднице компьютера COM-порт. У меня на ноутбуке конечно ничего такого не найдёшь, но сгодится и USB-COM конвертер.</p>
<p>О подключении ATtiny2313 к RS-232-совместимому интерфейсу можно писать отдельную статью, но это не входит в мои цели. Напоминаю, что USART-устройство ATtiny2313 выдаёт TTL-уровни (0/5В), а RS-232 хочет -12/+12В в качестве обозначения логических состояний. Поэтому чтобы связать tiny с COM-портом можно использовать либо специальную микросхему (<a href="http://www.maxim-ic.com/datasheet/index.mvp/id/1798"  onclick="pageTracker._trackPageview('/outgoing/www.maxim-ic.com/datasheet/index.mvp/id/1798?referer=');">MAX232</a>), либо <a href="http://freecircuitdiagram.com/2009/03/27/simple-ttl-rs232-level-converter-using-transistor/"  onclick="pageTracker._trackPageview('/outgoing/freecircuitdiagram.com/2009/03/27/simple-ttl-rs232-level-converter-using-transistor/?referer=');">более дешёвый и простой способ</a>, тут уж смотря какие конечные цели<sup class='wm-footnote'><a href="#wmfn-1427-0" id='wmfn-1427-ref-0' class="liinternal">[1]</a></sup>.</p>
<p>Общая схема подключения выглядит следующим образом:</p>
<p><a href="http://alex.starspirals.net/wp-content/uploads/2011/08/Connection_diagram.png" class="liimagelink"><img src="http://alex.starspirals.net/wp-content/uploads/2011/08/Connection_diagram-1024x306.png" alt="" title="MIDI over RS-232" width="640" height="191" class="aligncenter size-large wp-image-1444" /></a></p>
<p>Конечно, без определённых ухищрений музыкальные программы напрямую работать с COM-портом не будут. А поэтому надо как-то наладить коммуникацию. На выручку приходит драйвер от компании <strong>Roland</strong>, подходящий для произвольного устройства и создающий в системе виртуальный MIDI-порт, присылающий и принимающий сообщения от COM-порта (своеобразный мост). Скачать можно на официальном сайте, например для Windows XP можно забрать <a href="http://www.roland.com/products/en/_support/dld.cfm?PRODUCT=SC-8820&#038;iRcId=1812543&#038;dsp=1"  onclick="pageTracker._trackPageview('/outgoing/www.roland.com/products/en/_support/dld.cfm?PRODUCT=SC-8820_038_iRcId=1812543_038_dsp=1&amp;referer=');">отсюда</a>, там же на сайте есть подходящий драйвер и для MacOS.</p>
<p>Во время установки надо проследить, чтобы был выбран правильный идентификатор COM-порта. У меня, например, это COM1. Кстати, драйвер от Roland хочет обязательно работать с портами в диапазоне COM1&#8230;4, так что в случае назначения системой физическому порту номера 5 и выше, нужно будет изменить эту настройку. Если у вас Windows XP, то порты можно проверить в диспетчере устройств. Если всё правильно настроено, в системе появится дополнительный MIDI порт: входы Roland Serial MIDI Input и два выхода Roland Serial MIDI Out A и B. Согласно этому настраиваем и любую программу, принимающую/отправляющую MIDI-сообщения через COM-порт.</p>
<p><strong>Внимание! Теперь ваш COM-порт будет занят MIDI-приложениями, и для доступа к нему нужно закрыть MIDI-соединения.</strong></p>
<h2 id="wm-toc-1427-8-3" name="wm-toc-1427-8-3">Краткое описание MIDI-протокола</h2>
<p>Для передачи информации посредством MIDI используется асинхронный последовательный протокол, для пересылки байта нужно 10 бит: стартовый бит, 8 бит информации и стоп-бит. Скорость передачи по стандарту равна 31.25 кбод. Как видно, USART-компонент ATtiny2313 идеально подходит для физической реализации протокола.</p>
<h3 id="wm-toc-1427-8-3-1" name="wm-toc-1427-8-3-1">Формат сообщений</h3>
<p>Вообще говоря, сообщение может состоять как из одного, так и из нескольких байт. Однако первый байт всегда является идентификатором команды (статусный байт):</p>
<p><a href="http://alex.starspirals.net/wp-content/uploads/2011/08/MIDI-status-byte.png" class="liimagelink"><img src="http://alex.starspirals.net/wp-content/uploads/2011/08/MIDI-status-byte.png" alt="" title="MIDI status byte" width="454" height="165" class="aligncenter size-full wp-image-1446" /></a></p>
<p>Старший полубайт (биты 7&#8230;4) задают команду, а младший полубайт (биты 3&#8230;0) — MIDI-канал, к которому относится команда.</p>
<p>Мы с вами рассмотрим далее сообщения, состоящие из 3 байт информации (статусный байт и два байта данных). Этого вполне достаточно для упрощённой реализации протокола. Полубайты команд буду задавать в <a href="http://ru.wikipedia.org/wiki/%D0%A8%D0%B5%D1%81%D1%82%D0%BD%D0%B0%D0%B4%D1%86%D0%B0%D1%82%D0%B5%D1%80%D0%B8%D1%87%D0%BD%D0%B0%D1%8F_%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0_%D1%81%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F" rel="nofollow" class="liwikipedia" onclick="pageTracker._trackPageview('/outgoing/ru.wikipedia.org/wiki/_D0_A8_D0_B5_D1_81_D1_82_D0_BD_D0_B0_D0_B4_D1_86_D0_B0_D1_82_D0_B5_D1_80_D0_B8_D1_87_D0_BD_D0_B0_D1_8F_D1_81_D0_B8_D1_81_D1_82_D0_B5_D0_BC_D0_B0_D1_81_D1_87_D0_B8_D1_81_D0_BB_D0_B5_D0_BD_D0_B8_D1_8F?referer=');">шестнадцатиричном представлении</a>. Таким образом, рассматриваемые сообщения выглядят так:</p>
<p><a href="http://alex.starspirals.net/wp-content/uploads/2011/08/MIDI-3-byte-message.png" class="liimagelink"><img src="http://alex.starspirals.net/wp-content/uploads/2011/08/MIDI-3-byte-message.png" alt="" title="MIDI 3 byte message format" width="889" height="91" class="aligncenter size-full wp-image-1451" /></a></p>
<p>Поговорим теперь по порядку об интересующих нас MIDI-сообщениях.</p>
<h3 id="wm-toc-1427-8-3-2" name="wm-toc-1427-8-3-2">Note On сообщение — 0X9</h3>
<p>Данная команда используется для «включения», то есть начала проигрывания ноты на заданном канале. Она имеет вид <code>0X9K 0XNN 0XVV</code>, где <em>K</em> — номер канала (0X0&#8230;0XF, то есть от 1 до 16), <em>NN</em> — номер ноты (0X00&#8230;0X7F; 0&#8230;127), <em>VV</em> — так называемая скорость (velocity) извлечения ноты (0X00&#8230;0X7F; 0&#8230;127), что соответствует динамике исполнения.</p>
<p>Приведу пример. Допустим на инструмент поступило сообщение <code>0X91 0X45 0X60</code>. Это значит, что инструмент должен начать воспроизведение ноты под номером 0X45, что есть нота ля первой октавы (440 Гц) с динамикой 0X60 на втором канале.</p>
<h3 id="wm-toc-1427-8-3-3" name="wm-toc-1427-8-3-3">Note Off сообщение — 0X8</h3>
<p>Эта команда выполняет прямо противоположное действие предыдущей, выключая ноту на заданном канале, при этом её формат <code>0X8K 0XNN 0XVV</code>. Поскольку динамика «выключения» учитывается редко, то содержание <em>VV</em> обычно 0X00.</p>
<p>Рассматривая предыдущий пример: для выключения ноты ля первой октавы на втором канале можно отправить сообщение <code>0X81 0X45 0X00</code>.</p>
<p><strong>Тут есть одно важное замечание. Дело в том, что спецификация MIDI позволяет заменять сообщения Note Off сообщениями Note On с нулевой динамикой.</strong></p>
<p>Так что выключить ноту в нашем примере можно и при помощи сообщения <code>0X91 0X45 0X00</code>. Это важно помнить при программировании MIDI-устройств.</p>
<h3 id="wm-toc-1427-8-3-4" name="wm-toc-1427-8-3-4">Controller Change (CC) сообщение — 0XB</h3>
<p>Сообщения изменения контроллеров используются для изменения таких параметров исполнения, как, например, громкость. Формат следующий: <code>0XBK 0xNN 0xVV</code>, где <em>K</em> — номер канала, на котором необходимо изменить параметры контроллера, <em>NN</em> — номер контроллера (0X00&#8230;0X7F), <em>VV</em> — значение настройки контроллера (0X00&#8230;0X7F).</p>
<p>Нас здесь может заинтересовать сообщение контроллера <code>0XBK 0x7B 0x00</code> — «All Notes Off», которое используется для одновременного отключения всех нот на заданном канале <em>K</em>.</p>
<h2 id="wm-toc-1427-8-4" name="wm-toc-1427-8-4">Схема устройства</h2>
<p><a href="http://alex.starspirals.net/wp-content/uploads/2011/08/ATtiny2313_midi_music_player.pdf" class="lipdf">Схема</a> практически не изменилась, разве что добавился коннектор для подключения конвертера уровней, оговоренного выше<sup class='wm-footnote'><a href="#wmfn-1427-1" id='wmfn-1427-ref-1' class="liinternal">[2]</a></sup>.</p>
<h2 id="wm-toc-1427-8-5" name="wm-toc-1427-8-5">Программный код</h2>
<p>Перейдём теперь к программированию микроконтроллера. Рассмотрим случай управления двухголосым синтезатором при помощи MIDI. Сначала, как обычно, «шапка» прошивки.</p>
<pre class="brush: avrasm"> ; Проект для ATtiny2313
; Управляемый MIDI Двухголосый Орган v1

.INCLUDE &quot;tn2313def.inc&quot;

.DEF	Velocity	= R8

.DEF	SrSave		= R9

.DEF	Tone1H	= R10
.DEF	Tone1L	= R11
.DEF	Tone2H	= R12
.DEF	Tone2L	= R13

.DEF	ToneTH	= R14
.DEF	ToneTL	= R15

.DEF	ReadBufferL = R16
.DEF	ReadBufferH	= R17

.DEF	Temp	= R18

.DEF	Temp1 	= R19
.DEF	Temp2	= R20
.DEF	Temp3	= R21
.DEF 	Temp4	= R22

.DEF	ADDRH	= R23
.DEF 	ADDRL	= R24

.DEF	RxByte  = R25
.DEF	ByteCnt	= R26

.MACRO OUTI
	ldi R16,@1
	out @0,R16
.ENDM

.MACRO ADI
	subi @0, -@1
.ENDM

.MACRO MEMR
	lds @0, @1
.ENDM

.MACRO MEMW
	sts @0, @1
.ENDM

.MACRO MEMWI
	ldi R16, @1
	sts @0, R16
.ENDM	

.EQU BUFFERSIZE 	= 3		; Размер буфера приёмника
.EQU VOICENUM		= 2		; Число доступных голосов

; ---------
; Константы
; ---------

; MIDI протокол

; Используем MIDI канал 1 ---

; Note on
.EQU NoteOn 	= 0x90

; Note off
.EQU NoteOff 	= 0x80

; Controller change
.EQU ControllerChange = 0xB0

; Контроллеры
.EQU AllNotesOff = 0x7B

; ---------------------------

; Самая низкая MIDI-нота (C2 = 36)
.EQU LowNote = 36

; Порты

.EQU Voice1 = PB3
.EQU Voice2 = PB4
.EQU LED	= PB0

; --------------
; Разметрка SRAM
; --------------
.DSEG

notebuffer:
	.BYTE BUFFERSIZE

voices:
	.BYTE VOICENUM

; ----------------
; Начало программы
; ----------------
.CSEG
.ORG 0x0000

RJMP RESET ; Reset Handler
RETI ;RJMP INT0 ; External Interrupt0 Handler
RETI ;RJMP INT1 ; External Interrupt1 Handler
RETI ;RJMP TIM1_CAPT ; Timer1 Capture Handler
RJMP TIMER1_COMPA ; Timer1 CompareA Handler
RETI ;RJMP TIM1_OVF ; Timer1 Overflow Handler
RETI ;RJMP TIM0_OVF ; Timer0 Overflow Handler
RJMP USART0_RXC ; USART0 RX Complete Handler
RETI ;RJMP USART0_DRE ; USART0,UDR Empty Handler
RETI ;RJMP USART0_TXC ; USART0 TX Complete Handler
RETI ;RJMP ANA_COMP ; Analog Comparator Handler
RETI ;RJMP PCINT ; Pin Change Interrupt
RJMP TIMER1_COMPB ; Timer1 Compare B Handler
RETI ;RJMP TIMER0_COMPA ; Timer0 Compare A Handler
RETI ;RJMP TIMER0_COMPB ; Timer0 Compare B Handler
RETI ;RJMP USI_START ; USI Start Handler
RETI ;RJMP USI_OVERFLOW ; USI Overflow Handler
RETI ;RJMP EE_READY ; EEPROM Ready Handler
RETI ;RJMP WDT_OVERFLOW ; Watchdog Overflow Handler
</pre>
<p>Здесь, пожалуй, отдельных комментариев не требуется. Стандартные макросы, которые хорошо бы выносить для удобства в отдельный файл, и константы. Буфер USART-приёмника составляет 3 байта, ровно столько, сколько нужно для приёма 3-байтовых сообщений NoteOn, NoteOff и ControllerChange, то есть это полностью удовлетворяет упрощённой реализации протокола.</p>
<p>Следом за шапкой идёт стандартная инициализация периферии — это описывать не буду, посмотрите это в полном коде<sup class='wm-footnote'><a href="#wmfn-1427-2" id='wmfn-1427-ref-2' class="liinternal">[3]</a></sup>. Реализация двухголосого синтезатора также не изменилась. А вот на приёме сообщений и их обработке остановимся более подробно.</p>
<h3 id="wm-toc-1427-8-5-1" name="wm-toc-1427-8-5-1">Приём MIDI-сообщений</h3>
<p>За приём любых сообщений по USART отвечает прерывание <code>USART0_RXC</code>. Оттуда и посмотрим:</p>
<pre class="brush: avrasm"> ; USART receive byte
USART0_RXC:

	; Отключить прерывания
	cli

	in SrSave, SREG

	; Загрузить байт из регистра
	in RxByte, UDR

	; Записать байт в буффер
	ldi YL, low(notebuffer)
	ldi YH, high(notebuffer)

	; Сместив позицию на число в счётчике
	clr temp
	add YL, ByteCnt
	adc YH, temp

	st Y, RxByte

	; Проверить счётчик байтов
	cpi ByteCnt, 0
	breq checkNoteOnOffMessage

	cpi ByteCnt, 2
	breq handleMidiNote

	rjmp incCounter

checkNoteOnOffMessage:

	; Проверить тип сообщения и
	; пропустить только NoteOn, NoteOff, CC
	cpi RxByte, NoteOn
	breq incCounter

	cpi RxByte, NoteOff
	breq incCounter

	cpi RxByte, ControllerChange
	breq incCounter

DiscardByte:

	; Отменить сообщение, сбросить счётчик
	clr ByteCnt
	rjmp getOutNoteCheck

incCounter:

	inc ByteCnt

getOutNoteCheck:

	; Выйти из процедуры
	out SREG, SrSave

	sei
	reti
</pre>
<p>Что же здесь происходит? Байты, прилетающие по USART записываются в память по указателю парного регистра Y, со смещением, указанным в счётчике. Таким образом в память приходит MIDI-сообщение. Но только в том случае, если оно соответствует фильтру: пропускаются только сообщения NoteOn, NoteOff и относящиеся к изменению контроллеров через команду ControllerChange. Всё остальное откидывается, счётчик обнуляется и ожидается правильная команда. Как только счётчик указывает на поступление третьего байта, управление передаётся процедуре <code>handleMidiNote</code>, о которой поговорим далее.</p>
<h3 id="wm-toc-1427-8-5-2" name="wm-toc-1427-8-5-2">Обработка команд</h3>
<p>Процедура обработки сообщений выглядит так:</p>
<pre class="brush: avrasm">
handleMidiNote:

	; Читаем из памяти: temp - действие, temp1 - нота
	memr temp, notebuffer
	memr temp1, notebuffer+1

	mov temp2, temp1		; Сохраним temp1 для CC

    memr velocity, notebuffer+2	; Сохраним также динамику

	subi temp1, LowNote		; Найти правильный номер ноты
	inc temp1

	; Выбрать правильное действие относительно команды
	cpi temp, NoteOn
	breq doNoteOn

	cpi temp, NoteOff
	breq doNoteOff

	cpi temp, ControllerChange
	breq doControllerChange

doNoteOn:

	; Проверить на нулевую скорость нажатия (динамику)
	clr temp2
	cp velocity, temp2
	breq doNoteOff			; Тогда нужно NoteOff

	; Далее включим ноту

	; Проверяем первый голос
	memr temp2, voices
	cpi temp2, 0
	brne NotFree1

	; Если свободен, ставим ноту сюда
	memw voices, temp1
	rcall setCh1

DoneNoteOnOff:
	; Всё сделано, очищаем счётчик
	clr ByteCnt
	rjmp getOutNoteCheck

NotFree1:

	; Проверим, свободен ли этот голос
	memr temp2, voices+1
	cpi temp2, 0
	brne DoneNoteOnOff	; Нет, нота не будет исполнена

	memw voices+1, temp1
	rcall setCh2

	rjmp DoneNoteOnOff

doNoteOff:

	; Проверим первый голос
	memr temp2, voices
	cp temp1, temp2
	brne notOnCh1

	; Нота найдена, освобождаем первый голос
	clr temp1
	rcall setCh1
	memwi voices, 0
	rjmp DoneNoteOnOff

notOnCh1:

	; Проверим второй голос
	memr temp2, voices+1
	cp temp1, temp2
	brne DoneNoteOnOff	 ; Нота нигде не найдена, выход

	; Нота занимает второй голос, очищаем
	clr temp1
	rcall setCh2
	memwi voices+1, 0
	rjmp DoneNoteOnOff

doControllerChange:

	; Проверим temp2 на &quot;All notes off&quot; сообщение
	cpi temp2, AllNotesOff
	brne DoneNoteOnOff

	; Такое сообщение поступило, очищаем оба канала
	memwi voices, 0
	memwi voices+1, 0

	cbi DDRB, voice1
	cbi DDRB, voice2

	rjmp DoneNoteOnOff
</pre>
<p>Здесь вся логика действий обусловлена условными переходами. В случае включения ноты — ищется свободный канал, и если он найден, то нота записывается туда и включается воспроизведение (иначе нота игнорируется). При нулевой динамике происходит переход к обработке NoteOff, равно как и при поступлении соответствующего сообщения. Там отключаемая нота ищется сначала на первом голосе, потом и на втором, если она найдена, то отключается — иначе игнорируется. Единственное воспринимаемое CC-сообщение — All Notes Off, отключить все ноты. При его поступлении вне зависимости от содержания голосов они очищаются и воспроизведение немедленно прекращается.</p>
<h3 id="wm-toc-1427-8-5-3" name="wm-toc-1427-8-5-3">Полный код программы</h3>
<p>А вот и полный код (комментарии на английском):<br />
<a href="http://www.starspirals.net/prj/ATtiny2313XBoard/ATTiny2313MIDIMusicPlayer_BlogVersion.asm"  onclick="pageTracker._trackPageview('/outgoing/www.starspirals.net/prj/ATtiny2313XBoard/ATTiny2313MIDIMusicPlayer_BlogVersion.asm?referer=');">ATTiny2313MIDIMusicPlayer_BlogVersion.asm</a></p>
<h3 id="wm-toc-1427-8-5-4" name="wm-toc-1427-8-5-4">Результат</h3>
<p>Далее можно посмотреть, как ATtiny2313 исполняет 3-ую часть «Лунной сонаты» Бетховена (для воспроизведения используется программа Guitar Pro 5):</p>
<p><iframe width="640" height="510" src="http://www.youtube.com/embed/mIRnyUmMcK0" frameborder="0" allowfullscreen></iframe></p>
<h2 id="wm-toc-1427-8-6" name="wm-toc-1427-8-6">Заключение</h2>
<p>Надеюсь, статья была полезной! На пути следующие статьи, посвящённые MIDI и DDS (Direct Digital Synthesis). 2 канала по 4 голоса, независимо воспроизводящие синусоидальные, треугольные, пилообразные, прямоугольные сигналы при помощи этой же платы? Переделка старого синтезатора в MIDI-клавиатуру с возможностью выбора динамики? Всё это на старом знакомом ATtiny2313? Да, это есть у нас :-) Если что-то из этого особенно интересно, пишите в комментариях.</p>
<hr />
<h2 id="wm-toc-1427-8-7" name="wm-toc-1427-8-7">Примечания</h2>
<div class='wm-footnote-container'>
<ol class='wm-footnote-list'>
<li class='wm-footnote-list-element' id='wmfn-1427-0'><a href="#wmfn-1427-ref-0" class='wm-backlink'>&uarr;</a><span class='wm-note-content'>Вообще, я пробовал оба варианта, и второй оказался довольно эффективным, хотя и не строго соответствующим спецификациям.</span></li>
<li class='wm-footnote-list-element' id='wmfn-1427-1'><a href="#wmfn-1427-ref-1" class='wm-backlink'>&uarr;</a><span class='wm-note-content'>Вашему вниманию предлагается <a href="http://easyelectronics.ru/svyaz-mikrokontrollera-s-kompyuterom-cherez-rs232.html"  onclick="pageTracker._trackPageview('/outgoing/easyelectronics.ru/svyaz-mikrokontrollera-s-kompyuterom-cherez-rs232.html?referer=');">статья</a> на соответствующую тему.</span></li>
<li class='wm-footnote-list-element' id='wmfn-1427-2'><a href="#wmfn-1427-ref-2" class='wm-backlink'>&uarr;</a><span class='wm-note-content'>Обратите внимание, что USART я настраиваю на значение 38400 бод. С этим значением у драйвера Roland проблем не возникает.</span></li>
</ol>
</div>
<h2 id="wm-toc-1427-8-8" name="wm-toc-1427-8-8">Использованные материалы</h2>
<ol>
<li><a href="http://www.gweep.net/~prefect/eng/reference/protocol/midispec.html"  onclick="pageTracker._trackPageview('/outgoing/www.gweep.net/_prefect/eng/reference/protocol/midispec.html?referer=');">http://www.gweep.net/~prefect/eng/reference/protocol/midispec.html</a></li>
<li><a href="http://www.phys.unsw.edu.au/jw/notes.html"  onclick="pageTracker._trackPageview('/outgoing/www.phys.unsw.edu.au/jw/notes.html?referer=');">http://www.phys.unsw.edu.au/jw/notes.html</a></li>
<li><a href="http://www.midi.org/techspecs/midimessages.php"  onclick="pageTracker._trackPageview('/outgoing/www.midi.org/techspecs/midimessages.php?referer=');">http://www.midi.org/techspecs/midimessages.php</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://alex.starspirals.net/2011/08/31/%d0%bc%d1%83%d0%b7%d1%8b%d0%ba%d0%b0-%d0%bd%d0%b0-attiny2313-%d1%83%d0%bf%d1%80%d0%b0%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d0%b5-%d0%bf%d1%80%d0%b8-%d0%bf%d0%be%d0%bc%d0%be%d1%89%d0%b8-midi-%d0%bf%d1%80/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Музыка на ATtiny2313: подключение PS/2 клавиатуры</title>
		<link>http://alex.starspirals.net/2011/03/30/%d0%bf%d0%be%d0%b4%d0%ba%d0%bb%d1%8e%d1%87%d0%b5%d0%bd%d0%b8%d0%b5-ps2-%d0%ba%d0%bb%d0%b0%d0%b2%d0%b8%d0%b0%d1%82%d1%83%d1%80%d1%8b/</link>
		<comments>http://alex.starspirals.net/2011/03/30/%d0%bf%d0%be%d0%b4%d0%ba%d0%bb%d1%8e%d1%87%d0%b5%d0%bd%d0%b8%d0%b5-ps2-%d0%ba%d0%bb%d0%b0%d0%b2%d0%b8%d0%b0%d1%82%d1%83%d1%80%d1%8b/#comments</comments>
		<pubDate>Wed, 30 Mar 2011 16:00:52 +0000</pubDate>
		<dc:creator>Алексей</dc:creator>
				<category><![CDATA[Видео]]></category>
		<category><![CDATA[Заметки]]></category>
		<category><![CDATA[Музыка]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[Электроника]]></category>
		<category><![CDATA[attiny2313]]></category>
		<category><![CDATA[avr]]></category>
		<category><![CDATA[avr assembler]]></category>
		<category><![CDATA[keyboard]]></category>
		<category><![CDATA[keyboard organ]]></category>
		<category><![CDATA[organ]]></category>
		<category><![CDATA[ps/2]]></category>
		<category><![CDATA[интерфейс]]></category>
		<category><![CDATA[орган]]></category>
		<category><![CDATA[подключение]]></category>
		<category><![CDATA[программирование]]></category>
		<category><![CDATA[пс/2]]></category>
		<category><![CDATA[сделай сам]]></category>

		<guid isPermaLink="false">http://alex.starspirals.net/?p=1269</guid>
		<description><![CDATA[В этой статье из серии «музыка на ATtiny2313» я расскажу, как подключить к разработанному ранее на основе ATtiny2313 двухканальному синтезатору PS/2-клавиатуру. Содержание [Скрыть] 8.8.1 Введение 8.8.2 Схема устройства 8.8.3 Принципы работы клавиатуры 8.8.3.1 Передача данных 8.8.3.2 Коды сканирования 8.8.4 Программный &#8230; <a href="http://alex.starspirals.net/2011/03/30/%d0%bf%d0%be%d0%b4%d0%ba%d0%bb%d1%8e%d1%87%d0%b5%d0%bd%d0%b8%d0%b5-ps2-%d0%ba%d0%bb%d0%b0%d0%b2%d0%b8%d0%b0%d1%82%d1%83%d1%80%d1%8b/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><img src="http://alex.starspirals.net/wp-content/uploads/2011/03/hardware-keyboard.png" alt="" title="keyboard" width="128" height="128" class="alignright size-full wp-image-1374" /><br />
В этой статье из серии «музыка на ATtiny2313» я расскажу, как подключить к разработанному ранее на основе ATtiny2313 двухканальному синтезатору PS/2-клавиатуру.<br />
<span id="more-1269"></span></p>
<div class="wm-toc">
<div align="center"><span class="wm-toc-header">Содержание</span> <span class="wm-toc-link">[<a id="wm-toc-toggle" href="javascript:wmTocToggle();">Скрыть</a>]</span></div>
<div id="wm-toc-container">
<ol class='wm-first-level'>
<li><a href="#wm-toc-1269-8-8-8-1" class="liinternal">8.8.8.1 Введение</a></li>
<li><a href="#wm-toc-1269-8-8-8-2" class="liinternal">8.8.8.2 Схема устройства</a></li>
<li><a href="#wm-toc-1269-8-8-8-3" class="liinternal">8.8.8.3 Принципы работы клавиатуры</a>
<ol>
<li><a href="#wm-toc-1269-8-8-8-3-1" class="liinternal">8.8.8.3.1 Передача данных</a></li>
<li><a href="#wm-toc-1269-8-8-8-3-2" class="liinternal">8.8.8.3.2 Коды сканирования</a></li>
</ol>
</li>
<li><a href="#wm-toc-1269-8-8-8-4" class="liinternal">8.8.8.4 Программный код</a>
<ol>
<li><a href="#wm-toc-1269-8-8-8-4-1" class="liinternal">8.8.8.4.1 Приём сообщения от клавиатуры</a></li>
<li><a href="#wm-toc-1269-8-8-8-4-2" class="liinternal">8.8.8.4.2 Обработка полученного байта</a></li>
</ol>
</li>
<li><a href="#wm-toc-1269-8-8-8-5" class="liinternal">8.8.8.5 Полный код программы</a></li>
<li><a href="#wm-toc-1269-8-8-8-6" class="liinternal">8.8.8.6 Результат</a></li>
<li><a href="#wm-toc-1269-8-8-8-7" class="liinternal">8.8.8.7 Примечания</a></li>
<li><a href="#wm-toc-1269-8-8-8-8" class="liinternal">8.8.8.8 Использованные материалы</a></li>
</ol>
</div>
</div>
<h2 id="wm-toc-1269-8-8-8-1" name="wm-toc-1269-8-8-8-1">Введение</h2>
<p>В сложных электронных проектах, требующих ввода пользовательских данных, часто выгодно использовать компьютерную клавиатуру. В зависимости от микроконтроллера, у него может быть встроенная поддержка как PS/2, так и USB HID стандартов. У ATtiny2313 этого добра нет, но подключить к нему клавиатуру и организовать её правильную работу довольно просто. Об этом и пойдёт речь в статье<sup class='wm-footnote'><a href="#wmfn-1269-0" id='wmfn-1269-ref-0' class="liinternal">[1]</a></sup>.</p>
<h2 id="wm-toc-1269-8-8-8-2" name="wm-toc-1269-8-8-8-2">Схема устройства</h2>
<p>Схема <a href="http://alex.starspirals.net/wp-content/uploads/2010/08/ATtiny2313_music_player.pdf" class="lipdf">осталась прежней</a>. Что нужно добавить: переходник от прошивочного шлейфа к клавиатуре. Распиновки для стандартного PS/2 коннектора, а также для DIN5 (встречается на старых клавиатурах):</p>
<table style="margin: 0 auto 15px auto;">
<tr>
<td style="vertical-align: top;"><img src="http://alex.starspirals.net/wp-content/uploads/2011/03/ps2.gif" alt="" title="ps/2 connector" width="83" height="85" class="aligncenter size-full wp-image-1295" /></td>
<td style="vertical-align: top;"><strong>PS/2:</strong><br />
1. KBD Clock (часы)<br />
2. GND<br />
3. KBD Data (данные)<br />
4. N/C<br />
5. +5V<br />
6. N/C </td>
<td><img src="http://alex.starspirals.net/wp-content/uploads/2011/03/din5.gif" alt="" title="din5" width="83" height="85" class="aligncenter size-full wp-image-1336" /></td>
<td style="vertical-align: top;"><strong>DIN5:</strong><br />
1. KBD Clock<br />
2. KBD Data<br />
3. N/C<br />
4. GND<br />
5. +5V (VCC)</td>
</tr>
</table>
<p>
Соответствие выводам микроконтроллера<sup class='wm-footnote'><a href="#wmfn-1269-1" id='wmfn-1269-ref-1' class="liinternal">[2]</a></sup> следующее:</p>
<ul>
<li>PB5 → KBD Clock</li>
<li>PB6 → KBD Data</li>
</ul>
<p>Как видно, используется всего 2 контакта — синхролиния (часы) и линия данных. Кроме этого клавиатура отжимает +5В питания с платы микроконтроллера.</p>
<h2 id="wm-toc-1269-8-8-8-3" name="wm-toc-1269-8-8-8-3">Принципы работы клавиатуры</h2>
<p>При подключении клавиатуры стандарта <a href="http://en.wikipedia.org/wiki/AT_keyboard" rel="nofollow" class="liwikipedia" onclick="pageTracker._trackPageview('/outgoing/en.wikipedia.org/wiki/AT_keyboard?referer=');">AT</a> сначала происходит её инициализация. Если светодиоды-индикаторы работоспособны, то в этот момент они должны синхронно мигнуть. После этого клавиатура готова отсылать и принимать сообщения. Для нашего устройства достаточно одностороннего режима работы — передача сообщений от клавиатуры к хосту.</p>
<h3 id="wm-toc-1269-8-8-8-3-1" name="wm-toc-1269-8-8-8-3-1">Передача данных</h3>
<p>Для передачи используется простой последовательный протокол. Как было сказано выше, используется два провода. Первый — часы — отвечает за инициирование передачи, определение её направления и регулирование пересылки фрейма. Второй провод несёт непосредственную информацию. Рассмотрим порядок пересылки:<br />
<div id="attachment_1327" class="wp-caption aligncenter" style="width: 546px"><a href="http://alex.starspirals.net/wp-content/uploads/2011/03/protocol_kbd2host.png" class="liimagelink"><img src="http://alex.starspirals.net/wp-content/uploads/2011/03/protocol_kbd2host.png" alt="" title="protocol_kbd2host" width="536" height="137" class="size-full wp-image-1327" /></a><p class="wp-caption-text">Пересылка фрейма от клавиатуры к хосту</p></div></p>
<ul>
<li>Сначала уровни на обоих проводах высокие. Это соответствует состоянию ожидания (<em>Idle</em>), то есть ничего не происходит.</li>
<li>Клавиатура инициирует пересылку, выставив на информационный сигнал в «0» (стартовый бит) и сменив уровень на часах «1»→«0».</li>
<li>Далее клавиатура пересылает 8 бит данных сообщения от младшего бита к старшему, съем информационного сигнала требуется производить при появлении заднего фронта сигнала часов</li>
<li>Предпоследний бит называется битом чётности<sup class='wm-footnote'><a href="#wmfn-1269-2" id='wmfn-1269-ref-2' class="liinternal">[3]</a></sup> (<em>parity bit</em>) и используется для проверки целостности переданного байта (простой контроль на ошибку передачи).</li>
<li>Последний бит равняется «1» и называется стоповым битом.</li>
<li>Пересылка закончена и линии возвращаются в состояние ожидания.</li>
</ul>
<p>Как видно, фрейм состоит из 11 бит. Переданный байт же соответствует определённой команде. В простейшем случае это код сканирования (<em>scancode</em>). О них дальше и поговорим.</p>
<h3 id="wm-toc-1269-8-8-8-3-2" name="wm-toc-1269-8-8-8-3-2">Коды сканирования</h3>
<p>Каждой клавише при нажатии соответствует либо один код, либо комбинация кодов (то есть сообщения состоят из нескольких байт). А когда клавишу отпускаешь, прилетает сообщение из нескольких байт, где первый — <code>0xF0</code>, а второй (их может быть и несколько) соответствует ранее нажатой клавише. Основные коды приведены на следующем рисунке:<br />
<div id="attachment_1343" class="wp-caption aligncenter" style="width: 597px"><img src="http://alex.starspirals.net/wp-content/uploads/2011/03/scancodes.gif" alt="" title="scancodes" width="587" height="256" class="size-full wp-image-1343" /><p class="wp-caption-text">Коды сканирования</p></div></p>
<p>Например, если нажать <em>правый ALT</em>, то клавиатура пошлёт сообщение <code>0xE0 0x11</code>, а если затем отпустить — <code>0xF0 0xE0 0x11</code>.</p>
<p>На этом теоретическая часть подошла к концу. Посмотрим, как можно реализовать это непосредственно на микроконтроллере.</p>
<h2 id="wm-toc-1269-8-8-8-4" name="wm-toc-1269-8-8-8-4">Программный код</h2>
<p>Далее рассмотрим только код, относящийся к снятию сообщений с клавиатуры. В <a href="http://alex.starspirals.net/2010/08/20/%D0%BC%D1%83%D0%B7%D1%8B%D0%BA%D0%B0-%D0%BD%D0%B0-attiny2313-1/" class="liinternal">прошлой статье</a> я довольно подробно описывал процедуру создания двухканального синтезатора, и повторяться здесь не буду.</p>
<p>«Шапка» ассемблерного листинга выглядит следующим образом:</p>
<pre class="brush: avrasm"> ; Проект для ATtiny2313
.INCLUDE "tn2313def.inc"

; --------------------
; Назначения регистров
; --------------------

.DEF	State	= R8	; Регистр состояния

.DEF	SrSave	= R9	; Регистр для восстановления SR

.DEF	Tone1H	= R10
.DEF	Tone1L	= R11
.DEF	Tone2H	= R12
.DEF	Tone2L	= R13

.DEF	ToneTH	= R14
.DEF	ToneTL	= R15

.DEF	Temp	= R17

.DEF	Temp1 	= R18
.DEF	Temp2	= R19
.DEF	Temp3	= R20
.DEF 	Temp4	= R21

.DEF	ADDRH	= R22
.DEF 	ADDRL	= R23

.DEF	RxByte	= R24
.DEF	BitCnt	= R25

; -------------------------------------------------
; Назначение битов собственного статусного регистра
; -------------------------------------------------

.EQU BitParity = 0
.EQU BitNoteOff = 1

; ------
; Голоса
; ------

.EQU Voice1 = PB3
.EQU Voice2 = PB4

; -----
; МАКРО
; -----

; OUTI порт, значение :: отправить значение в порт
.MACRO OUTI
	ldi R16,@1
	out @0,R16
.ENDM

; MEMR регистр, (место) :: чтение из памяти в регистр
.MACRO MEMR
	lds @0, @1
.ENDM

; MEMW (место), регистр :: записать регистр в память
.MACRO MEMW
	sts @0, @1
.ENDM

; MEMWI (место), значение :: записать значение в память
.MACRO MEMWI
	ldi R16, @1
	sts @0, R16
.ENDM	

; ----
; SRAM
; ----
.DSEG

; Синтезатор на два голоса
voices:

	.BYTE 2
</pre>
<h3 id="wm-toc-1269-8-8-8-4-1" name="wm-toc-1269-8-8-8-4-1">Приём сообщения от клавиатуры</h3>
<p>Для приёма данных от клавиатуры нужно инициализировать прерывание по изменению уровня на цифровом входе синхролинии.</p>
<pre class="brush: avrasm">
Pcint_Init:

	; Включить прерывание по изменению уровня
	outi GIMSK, (1&lt;&lt;PCIE)
	outi PCMSK, (1&lt;&lt;PCINT5)

	ret
</pre>
<p>В основном цикле программы нужно включить прерывания, а также очистить счётчик битов и статусный регистр. Кроме того, соответствующие изменения надо внести и в вектор прерываний в начале основного кода (добавить <code>rjmp PCINT</code> по соответствующему адресу).</p>
<p>Когда синхролиния свалится в низкий уровень, произойдёт прерывание. Его обработает следующая процедура:</p>
<pre class="brush: avrasm">
PCINT:

	cli

	in SrSave, SREG
	push SrSave

	; Проверка на задний фронт
	sbis PINB, PB5
	rcall RxKbd

	pop SrSave
	out SREG, SrSave

	sei

	reti
</pre>
<p>Если обнаружен задний фронт сигнала (момент «1»→«0»), то вызывается процедура <code>RxKbd</code>, которая отвечает за обработку информационного сигнала.</p>
<pre class="brush: avrasm">
RxKbd:

	; Очистить бит T
	clt

	; Если PB6 установлен, сохранить в T
	sbic PINB, PB6
	set

	; Первый бит фрейма?
	cpi BitCnt, 0
	breq firstBit

	; Проверить чётность
	cpi BitCnt, 9
	breq parityCheck

	; Последний бит фрейма?
	cpi BitCnt, 10
	breq rxFinished

	; Сохранить бит в буфер
	lsr RxByte
	bld RxByte, 7

	; Сохранить бит чётности
	clr Temp1
	bld Temp1, BitParity
	eor State, Temp1

RxBitDone:
	; Инкремент счётчика бит
	inc BitCnt

	ret

firstBit:

	; Очистить буфер
	clr RxByte

	; Очистить бит чётности
	ldi R16, 0xFE
	and State, R16

	rjmp RxBitDone

parityCheck:

	; Сохранить бит чётности
	clr Temp1
	bld Temp1, BitParity
	eor State, Temp1

	rjmp RxBitDone

rxFinished:

	; Проверить бит чётности (нечётность):
	; если = 1, то всё правильно
	sbrs State, BitParity
	rjmp discardByte

	; Очистить счётчик битов
	clr BitCnt

	; Вызвать обработчик байта
	rcall handleByte

	ret
</pre>
<p>Как это работает:</p>
<ul>
<li>При поступлении стартового бита обнуляется буфер и бит чётности.</li>
<li>Биты 1-8 складываются в буфер, кроме того над ними последовательно производится операция XOR, результат которой сохраняется в бите специального регистра.</li>
<li>Последняя операция XOR производится с полученным и присланным битами чётности. При этом полученный XOR-ами бит не инвертируется. Следовательно, в результате операции бит чётности должен быть равен 1, если при пересылке не возникло ошибок.</li>
<li>После получения последнего бита фрейма управление передаётся процедуре <code>discardByte</code>, если проверка чётности провалена, либо в случае успешной передачи процедуре <code>handleByte</code>, которая обработает полученный байт.</li>
</ul>
<p>Рассмотрим конкретный пример. Допустим пользователь нажимает клавишу <code>ESC</code>, которой соответствует скан-код <code>0x76</code>. В таком случае произойдёт следующее:</p>
<p><a href="http://alex.starspirals.net/wp-content/uploads/2011/03/kbd_animation_600px.gif" class="liimagelink"><img src="http://alex.starspirals.net/wp-content/uploads/2011/03/kbd_animation_600px.gif" alt="" title="kbd_animation_600px" width="600" height="551" class="size-full wp-image-1380" /></a></p>
<h3 id="wm-toc-1269-8-8-8-4-2" name="wm-toc-1269-8-8-8-4-2">Обработка полученного байта</h3>
<p>Поскольку основная статья всё-таки про подключение клавиатуры, я не буду останавливаться подробно на коде обработчика полученного байта, а просто объясню что там к чему.</p>
<p>Нашей задачей было прикрутить к синтезатору клавиатуру. Первая часть задания выполнена, теперь нужно каким-то образом на основе полученных от клавиатуры сообщений включать или выключать воспроизведение определённых нот. Вот как это сделано:</p>
<ul>
<li>Синтезатор имеет два голоса.</li>
<li>При поступлении сообщения о нажатии клавиши ищется свободный голос. Если такой найден — в него отправляется соответствующая клавише нота.</li>
<li>При поступлении сообщения об отпускании клавиши в статусный регистр заносится метка «следующее сообщение об отпускании клавиши»; когда обрабатывается следующий байт, проверяется, была ли такая клавиша нажата и голос активирован. Если да — соответствующий голос освобождается.</li>
<li>Если поступает сообщение о нажатии клавиши <code>ESC</code>, оба голоса освобождаются (что-то наподобие <em>Panic Button</em>, если голоса застревают после отпускания клавиш).</li>
</ul>
<p>Что касается нахождения ноты по клавише, то это сделано по следующей схеме. В памяти кристалла хранится две таблицы. Одна из них уже знакомая таблица нот, где ноты в этот раз заданы в восходящем порядке шагом в полутон. Другая таблица содержит список клавиш, соответствующих этим нотам, также в восходящем порядке. Таким образом, для извлечения ноты необходимо просто найти позицию клавиши во второй таблице и взять ноту на той же позиции из первой. Вот эти таблицы для наглядности:</p>
<pre class="brush: avrasm">
tblNotes:
	.dw		19111		; C2
	.dw		18039		; C#2
	.dw		17026		; D2
	.dw		16071		; D#2
	.dw		15169		; E2
	.dw		14317		; F2
	.dw		13514		; F#2
	.dw		12755		; G2
	.dw		12039		; G#2
	.dw		11364		; A2
	.dw		10726		; A#2
	.dw		10124		; B2
	.dw		9556 		; C3
	.dw		9019 		; C#3
	.dw		8513 		; D3
	.dw		8035 		; D#3
	.dw		7584 		; E3
	.dw		7159 		; F3
	.dw		6757 		; F#3
	.dw		6378 		; G3
	.dw		6020 		; G#3
	.dw		5682 		; A3
	.dw		5363 		; A#3
	.dw		5062 		; B3
	.dw		4778 		; C4
	.dw		4510 		; C#4
	.dw		4257 		; D4
	.dw		4018 		; D#4
	.dw		3792 		; E4
	.dw		3579 		; F4
	.dw		3378 		; F#4
	.dw		3189 		; G4
	.dw		3010 		; G#4
	.dw		2841 		; A4
	.dw		2681 		; A#4
	.dw		2531 		; B4
	.dw		2389 		; C5
	.dw		2255 		; C#5
	.dw		2128 		; D5
	.dw		2009 		; D#5
	.dw		1896 		; E5
	.dw		1790 		; F5
	.dw		1689 		; F#5
	.dw		1594 		; G5
	.dw		1505 		; G#5
	.dw		1420 		; A5
	.dw		1341 		; A#5
	.dw		1265 		; B5
	.dw		1194 		; C6
	.dw		1127 		; C#6
	.dw		1064 		; D6
	.dw		1004 		; D#6
	.dw		948  		; E6
	.dw		895  		; F6
	.dw		845  		; F#6
	.dw		797  		; G6
	.dw		752  		; G#6
	.dw		710  		; A6
	.dw		670  		; A#6
	.dw		633  		; B6
	.dw		597			; C7

scanCodes:
	.db 0x1A, 0x1B	; Z, S
	.db 0x22, 0x23	; X, D
	.db 0x21, 0x2A	; C, V
	.db 0x34, 0x32	; G, B
	.db 0x33, 0x31	; H, N
	.db 0x3B, 0x3A	; J, M
	.db 0x41, 0x4B	; <, L
	.db 0x49, 0x4C	; >, :
	.db 0x4A, 0x15	; ?, Q
	.db 0x1E, 0x1D	; 2, W
	.db 0x26, 0x24	; 3, E
	.db 0x25, 0x2D	; 4, R
	.db 0x2C, 0x36	; T, 6
	.db 0x35, 0x3D	; Y, 7
	.db 0x3C, 0x43	; U, I
	.db 0x46, 0x44	; 9, O
	.db 0x45, 0x4D	; 0, P
	.db 0x4E, 0x54	; -, [
	.db 0x5B, 0x00	; ]
</pre>
<p>Получается следующее отображение клавиш фортепиано на клавиатуру:</p>
<p><img src="http://alex.starspirals.net/wp-content/uploads/2011/03/scancodes_piano.png" alt="" title="scancodes_piano" width="587" height="256" class="aligncenter size-full wp-image-1386" /><br />
Итого: 3 октавы + клавиша «ДО» четвёртой, всего 37 клавиш.</p>
<h2 id="wm-toc-1269-8-8-8-5" name="wm-toc-1269-8-8-8-5">Полный код программы</h2>
<p>С комментариями на английском: <a href="http://starspirals.net/prj/ATtiny2313XBoard/ATtiny2313XBoard_KeyboardOrgan.asm"  onclick="pageTracker._trackPageview('/outgoing/starspirals.net/prj/ATtiny2313XBoard/ATtiny2313XBoard_KeyboardOrgan.asm?referer=');">ATtiny2313XBoard_KeyboardOrgan.asm</a><br />
Весь код после компиляции занимает 650 байт памяти кристалла.</p>
<h2 id="wm-toc-1269-8-8-8-6" name="wm-toc-1269-8-8-8-6">Результат</h2>
<p>Демонстрация<sup class='wm-footnote'><a href="#wmfn-1269-3" id='wmfn-1269-ref-3' class="liinternal">[4]</a></sup> полученного электронного орг<em>а</em>на:</p>
<p><iframe title="YouTube video player" width="640" height="510" src="http://www.youtube.com/embed/GNhmpEd2Tb8?rel=0" frameborder="0" allowfullscreen></iframe></p>
<hr />
<h2 id="wm-toc-1269-8-8-8-7" name="wm-toc-1269-8-8-8-7">Примечания</h2>
<div class='wm-footnote-container'>
<ol class='wm-footnote-list'>
<li class='wm-footnote-list-element' id='wmfn-1269-0'><a href="#wmfn-1269-ref-0" class='wm-backlink'>&uarr;</a><span class='wm-note-content'>Статья про MIDI, если кто-то её ждёт, будет следующей в цикле статей про электронику.</span></li>
<li class='wm-footnote-list-element' id='wmfn-1269-1'><a href="#wmfn-1269-ref-1" class='wm-backlink'>&uarr;</a><span class='wm-note-content'>Для данных можно использовать любой пин, а вот для часов нужен вывод с настраиваемым прерыванием по изменению уровня в режиме цифрового входа.</span></li>
<li class='wm-footnote-list-element' id='wmfn-1269-2'><a href="#wmfn-1269-ref-2" class='wm-backlink'>&uarr;</a><span class='wm-note-content'>Используется бит нечётности (<em>odd parity</em>): биты, составляющие байт складываются при помощи логической операции XOR и затем берётся инверсное значение. Например, для байта <code>11011010</code>: <code>1^1^0^1^1^0^1^0 = 1</code>, <code>!(1) = 0</code> и передано будет <code>110110100</code></span></li>
<li class='wm-footnote-list-element' id='wmfn-1269-3'><a href="#wmfn-1269-ref-3" class='wm-backlink'>&uarr;</a><span class='wm-note-content'>Я использую несколько другую плату, чем на схеме. Дополнительно там есть семисегментный индикатор, при помощи которого можно следить за содержимым ячеек памяти. Думаю, на тему отладки таким образом тоже стоит написать статью когда-нибудь :-)</span></li>
</ol>
</div>
<h2 id="wm-toc-1269-8-8-8-8" name="wm-toc-1269-8-8-8-8">Использованные материалы</h2>
<ol>
<li><a href="http://www.beyondlogic.org/keyboard/keybrd.htm"  onclick="pageTracker._trackPageview('/outgoing/www.beyondlogic.org/keyboard/keybrd.htm?referer=');">http://www.beyondlogic.org/keyboard/keybrd.htm</a></li>
<li><a href="http://en.wikipedia.org/wiki/Parity_bit" rel="nofollow" class="liwikipedia" onclick="pageTracker._trackPageview('/outgoing/en.wikipedia.org/wiki/Parity_bit?referer=');">http://en.wikipedia.org/wiki/Parity_bit</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://alex.starspirals.net/2011/03/30/%d0%bf%d0%be%d0%b4%d0%ba%d0%bb%d1%8e%d1%87%d0%b5%d0%bd%d0%b8%d0%b5-ps2-%d0%ba%d0%bb%d0%b0%d0%b2%d0%b8%d0%b0%d1%82%d1%83%d1%80%d1%8b/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Опрос: какая информация вам наиболее интересна?</title>
		<link>http://alex.starspirals.net/2011/03/20/%d0%be%d0%bf%d1%80%d0%be%d1%81-%d0%ba%d0%b0%d0%ba%d0%b0%d1%8f-%d0%b8%d0%bd%d1%84%d0%be%d1%80%d0%bc%d0%b0%d1%86%d0%b8%d1%8f-%d0%b2%d0%b0%d0%bc-%d0%bd%d0%b0%d0%b8%d0%b1%d0%be%d0%bb%d0%b5%d0%b5-%d0%b8/</link>
		<comments>http://alex.starspirals.net/2011/03/20/%d0%be%d0%bf%d1%80%d0%be%d1%81-%d0%ba%d0%b0%d0%ba%d0%b0%d1%8f-%d0%b8%d0%bd%d1%84%d0%be%d1%80%d0%bc%d0%b0%d1%86%d0%b8%d1%8f-%d0%b2%d0%b0%d0%bc-%d0%bd%d0%b0%d0%b8%d0%b1%d0%be%d0%bb%d0%b5%d0%b5-%d0%b8/#comments</comments>
		<pubDate>Sun, 20 Mar 2011 16:24:43 +0000</pubDate>
		<dc:creator>Алексей</dc:creator>
				<category><![CDATA[Заметки]]></category>
		<category><![CDATA[ProgTime]]></category>
		<category><![CDATA[автоматика]]></category>
		<category><![CDATA[блог]]></category>
		<category><![CDATA[моделирование]]></category>
		<category><![CDATA[музыка]]></category>
		<category><![CDATA[опрос]]></category>
		<category><![CDATA[подсознание]]></category>
		<category><![CDATA[программирование]]></category>
		<category><![CDATA[самопознание]]></category>
		<category><![CDATA[сознание]]></category>
		<category><![CDATA[тема]]></category>
		<category><![CDATA[Электроника]]></category>

		<guid isPermaLink="false">http://alex.starspirals.net/?p=1243</guid>
		<description><![CDATA[Хотелось бы узнать мнение читателей: какие статьи вам хотелось бы здесь видеть? Дело в том, что времени на написание и публикацию здесь у меня всё меньше и меньше, и мне необходимо определиться с основной тематикой блога. &#160;]]></description>
			<content:encoded><![CDATA[<p>Хотелось бы узнать мнение читателей: какие статьи вам хотелось бы здесь видеть? Дело в том, что времени на написание и публикацию здесь у меня всё меньше и меньше, и мне необходимо определиться с основной тематикой блога.</p>
<a href="http://polldaddy.com/poll/4752695" onclick="pageTracker._trackPageview('/outgoing/polldaddy.com/poll/4752695?referer=');">Take Our Poll</a>
<div style="height: 20px">&nbsp;</div>
]]></content:encoded>
			<wfw:commentRss>http://alex.starspirals.net/2011/03/20/%d0%be%d0%bf%d1%80%d0%be%d1%81-%d0%ba%d0%b0%d0%ba%d0%b0%d1%8f-%d0%b8%d0%bd%d1%84%d0%be%d1%80%d0%bc%d0%b0%d1%86%d0%b8%d1%8f-%d0%b2%d0%b0%d0%bc-%d0%bd%d0%b0%d0%b8%d0%b1%d0%be%d0%bb%d0%b5%d0%b5-%d0%b8/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Сон и сновидения, часть третья: осознанные сновидения</title>
		<link>http://alex.starspirals.net/2011/01/10/%d1%81%d0%be%d0%bd-%d0%b8-%d1%81%d0%bd%d0%be%d0%b2%d0%b8%d0%b4%d0%b5%d0%bd%d0%b8%d1%8f-%d1%87%d0%b0%d1%81%d1%82%d1%8c-%d1%82%d1%80%d0%b5%d1%82%d1%8c%d1%8f-%d0%be%d1%81%d0%be%d0%b7%d0%bd%d0%b0%d0%bd/</link>
		<comments>http://alex.starspirals.net/2011/01/10/%d1%81%d0%be%d0%bd-%d0%b8-%d1%81%d0%bd%d0%be%d0%b2%d0%b8%d0%b4%d0%b5%d0%bd%d0%b8%d1%8f-%d1%87%d0%b0%d1%81%d1%82%d1%8c-%d1%82%d1%80%d0%b5%d1%82%d1%8c%d1%8f-%d0%be%d1%81%d0%be%d0%b7%d0%bd%d0%b0%d0%bd/#comments</comments>
		<pubDate>Mon, 10 Jan 2011 11:04:30 +0000</pubDate>
		<dc:creator>Алексей</dc:creator>
				<category><![CDATA[Заметки]]></category>
		<category><![CDATA[Размышления]]></category>
		<category><![CDATA[осознанный сон]]></category>
		<category><![CDATA[подсознание]]></category>
		<category><![CDATA[развитие]]></category>
		<category><![CDATA[разум]]></category>
		<category><![CDATA[сновидение]]></category>
		<category><![CDATA[сознание]]></category>
		<category><![CDATA[сон]]></category>
		<category><![CDATA[фотография]]></category>

		<guid isPermaLink="false">http://alex.starspirals.net/?p=1225</guid>
		<description><![CDATA[Чтобы внести некоторую нелинейность в этот цикл статей, напишу в этот раз про осознанные сновидения, перескочив пункт «процесс сновидения», о котором поговорим в другой раз. Следует сразу сказать: всё, что описано в этой статье, плоды моего личного опыта, так что &#8230; <a href="http://alex.starspirals.net/2011/01/10/%d1%81%d0%be%d0%bd-%d0%b8-%d1%81%d0%bd%d0%be%d0%b2%d0%b8%d0%b4%d0%b5%d0%bd%d0%b8%d1%8f-%d1%87%d0%b0%d1%81%d1%82%d1%8c-%d1%82%d1%80%d0%b5%d1%82%d1%8c%d1%8f-%d0%be%d1%81%d0%be%d0%b7%d0%bd%d0%b0%d0%bd/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Чтобы внести некоторую нелинейность в этот цикл статей, напишу в этот раз про осознанные сновидения, перескочив пункт «процесс сновидения», о котором поговорим в другой раз.</p>
<p>Следует сразу сказать: всё, что описано в этой статье, плоды моего личного опыта, так что приведённые рецепты и рекомендации могут подходить не всем.<br />
<span id="more-1225"></span><br />
На всякий случай напомню, что осознанное сновидение (ОС) — это когда во сне понимаешь (осознаёшь), что находишься в сновидении и имеешь возможность сознательно действовать и изменять его содержание.</p>
<p>Зачем это нужно? Для начала, это просто дико интересно. Есть возможность познакомиться непосредственно со своими друзьями из подсознания (или бессознательного). Во-вторых, это даёт возможность получить дополнительные яркие ощущения. В-третьих, можно попробовать воспользоваться своим творческим аппаратом и вытащить из недр подсознания какую-нибудь информацию.</p>
<p>Как и любой навык, осознанное сновидение даётся отнюдь не сразу. Можно сравнить это с таким навыком, как ходьба, и на деле так оно и есть — попадая в ОС сновидящий сначала учится перемещаться по пространству сновидения и «удерживаться на ногах» — то есть не впадать в обыкновенное состояние сна или просыпаться.</p>
<p>Можно сказать, что мой интерес к сновидениям весьма активно подогревает как раз возможность попадать в ОС. Я открыл для себя технику, которая позволяет входить в ОС практически каждую ночь. Теперь я поделюсь ей с вами, параллельно описывая свои мысли по поводу того, как она на самом деле работает. Разумеется, всё это основано на моих личных наблюдениях, догадках и домыслах.</p>
<p>Итак, существует несколько условий, чтобы выйти в ОС.</p>
<p>1. Большое желание это сделать<br />
2. Циклы сна по 8 часов, на протяжении достаточного времени (2-3 дня)<br />
3. Ведение дневников снов</p>
<p>Вести дневник снов помогает развить навык, необходимый в осознанном сновидении. Дело в том, что «на той стороне» наш запас средств наблюдения и анализа очень сильно ограничен, как и доступ к памяти. Регулярно записывая свои сны мы нацеливаем себя на запоминание сновидений, а значит организм своеобразно настраивает всё больше и больше инструментов доступа к способностям наблюдать и запоминать сны.</p>
<p>Теперь техника.</p>
<p>1. Спать 6 часов из 8, по будильнику пробудиться и встать<sup class='wm-footnote'><a href="#wmfn-1225-0" id='wmfn-1225-ref-0' class="liinternal">[1]</a></sup>;<br />
2. Далее нужно не спать порядка 15-30 минут, желательно некоторое время активно двигаться;<br />
3. Почувствовав желание не ложиться спать, сделать противоположное, то есть лечь обратно;<br />
4. Упрямо лежать до тех пор пока не заснёшь, не забывая о намерении увидеть ОС.</p>
<p>Техника эта описана как очень эффективная у многих онейронавтов, похожий подход описан и у Лабержа. Но помимо знания процедуры нужно также понимать, что именно происходит в это время в организме.</p>
<p>Сперва давайте рассмотрим в первом приближении процесс засыпания в начале ночи (см. диаграмму 1 ниже):</p>
<p><img src="http://alex.starspirals.net/wp-content/uploads/2011/01/both_processes.png" alt="" title="Засыпание и пробуждение" width="600" height="660" class="alignnone size-full wp-image-1226" /></p>
<p>Из наблюдений за собой я могу сделать вывод, что отключение отделов мозга сопровождается характерными изменениями мыслепотока. Кроме того, я начинаю зевать, причем с определённой регулярностью. В конечном итоге мыслепоток прекращается и наступает состояние сна, которое я сумел засечь только однажды в жизни, в этот момент произнося что-то вслух<sup class='wm-footnote'><a href="#wmfn-1225-1" id='wmfn-1225-ref-1' class="liinternal">[2]</a></sup>.</p>
<p>В чем же эффективность техники? Дело в том, что описанный процесс засыпания характерен именно для начала ночи. То есть при пробуждении утром после 6 часов сна стадии отключения отделов мозга после их включения при повторном засыпании могут и не пройти заново! Фактически это означает что из бодрствующего состояния ты попадаешь прямо в сновидение. Однако если при засыпании начинается зевота, то наверняка стадии отключения нужных в ОС отделов мозга пошли по своему пути и вероятность ОС резко падает чуть ли не до нуля. А значит это, что организм не достаточно отдохнул и стоит попробовать в другой раз.</p>
<p>Процесс пробуждения показан на диаграмме 2 выше. Нам важно, чтобы нужные отделы мозга включились, иначе вместо ОС будет обычное сновидение. Именно для этого и рекомендуется встать и сделать вид, что хочешь окончательно отойти ото сна для дневной деятельности. Это соответствующим образом настраивает организм и в прямом смысле слова включает мозг.</p>
<p>Итак, допустим всё проделано правильно&#8230; <em>я шёл с другом в центре города. Внезапно моё внимание привлекли яркие вечерние облака. Внезапно они превратились в огромное анимированное чудище&#8230; довольно интересная картина, но&#8230; «да я ведь сплю!»</em></p>
<p>Эта мысль, впервые «озвученная» во сне обычно приводит:</p>
<p>1. К пробуждению (так как нервная система реагирует на подобные заявления весьма бурно)<br />
2. К так называемому ложному пробуждению, характерному для тех онейронавтов, кто уже познакомился с первым пунктом.</p>
<p>Организм, видимо, не очень-то хочет просто так засыпать-просыпаться и оптимизирует свои действия так, что пудрит мозги незадачливому онейрнонавту, сперва показывая ему его собственное пробуждение (обычно не скупясь на абсолютно достоверные детали и ощущения), а потом по-немногу увлекая в мир грёз, удаляя осознанность восвояси.</p>
<p>Позже я разработал тактику: в бодрствующем состоянии я четко внушил себе проверку на сон. Больше «ложные пробуждения» для меня не помеха. Пробуждаясь во сне я всегда проверяю, на самом деле я проснулся или нет. С тех пор, впрочем, все мои ОС начинаются с ложного пробуждения, то есть пунктом отправления является место, где я заснул.</p>
<p>Поговорим теперь о некоторых особенностях ОС.</p>
<p>Во-первых, возможно вас огорчит тот факт, что ОС длится в среднем не больше минуты. По крайней мере на первых стадиях освоения. Этого, к сожалению, хватает не на многое, поэтому быстро научиться ходить по миру своих сновидений вряд ли получится. Связано подобное ограничение с тем, что хотя отделы мозга и не отключились непосредственно перед засыпанием, но они будут очень рады накрыться в состоянии сна. Так что чем быстрее вы сообразите, что спите, очнувшись в сновидении, тем лучше. Со временем и опытом длительность ОС должна увеличиться.</p>
<p>Во-вторых, вы будете поражены, насколько реальными являются ощущения в ОС. Всё кажется настоящим: тактильные ощущения, зрительные картины, звуки, даже вкусовые рецепторы будут изображать нормальную деятельность. Вы наверняка будете любоваться картинами, построенными вашим подсознанием.</p>
<p>В-третьих, вам придётся постоянно контролировать стабильность ОС<sup class='wm-footnote'><a href="#wmfn-1225-2" id='wmfn-1225-ref-2' class="liinternal">[3]</a></sup>. Чуть что — и вы улетите оттуда как пробка из бутылки, так как мозг не очень любит, когда в сновидения нагло вторгается сознание. Он будет всячески портить вам малину<sup class='wm-footnote'><a href="#wmfn-1225-3" id='wmfn-1225-ref-3' class="liinternal">[4]</a></sup>, банально выключая «зрение» или «слух», а то и всё вместе. Мне доводилось шастать по ОС с ограниченным полем зрения до приблизительно 1% от нормального, причем всё что я хотел сделать — это покинуть комнату, где находился (точку отправки). Раз за разом мозг возвращал меня в исходное положение.</p>
<p>Ну и в четвертых, даже без вмешательства «охранника», просто так свободно лазать по ОС вам никто не даст, поскольку доступ к памяти сильно ограничен. По этой причине перед тем, как лечь спать в начале ночи нужно хорошо продумать, что же вы на самом деле хотите получить в ОС.</p>
<p>Как и в любом другом деле, для освоение ОС вам понадобится для начала лишь сильное желание. Удачных полётов в мир сновидений!</p>
<hr />
<strong>Примечания</strong></p>
<div class='wm-footnote-container'>
<ol class='wm-footnote-list'>
<li class='wm-footnote-list-element' id='wmfn-1225-0'><a href="#wmfn-1225-ref-0" class='wm-backlink'>&uarr;</a><span class='wm-note-content'>Такой график связан с чередованием стадий медленного и быстрого сна, к утру длительность каждой из стадий заметно возрастает и после пробуждения и последующего засыпания как правило медленный сон уже не наступает.</span></li>
<li class='wm-footnote-list-element' id='wmfn-1225-1'><a href="#wmfn-1225-ref-1" class='wm-backlink'>&uarr;</a><span class='wm-note-content'>Ощущение моментального отключения — вот ты ощущаешь себя и вдруг «пустота». Трудно это передать, и можно сказать, что мне повезло сознательно зарегистрировать момент отключения сознания, видимо «информационные пакеты» речи давали обратную связь до последнего момента и сознание отключилось в мгновение передачи последних сигналов слуховосприятия.</span></li>
<li class='wm-footnote-list-element' id='wmfn-1225-2'><a href="#wmfn-1225-ref-2" class='wm-backlink'>&uarr;</a><span class='wm-note-content'>У Карлоса Кастанеды описан метод — просто время от времени смотреть на свои руки. Это позволяет удержать концентрацию во сне и не позволить нужным отделам взять и переключить рубильник в положение «ВЫКЛ».</span></li>
<li class='wm-footnote-list-element' id='wmfn-1225-3'><a href="#wmfn-1225-ref-3" class='wm-backlink'>&uarr;</a><span class='wm-note-content'>Не давая, скажем, хлопнуть по заднице проходящую мимо красавицу :-)</span></li>
</ol>
</div>
]]></content:encoded>
			<wfw:commentRss>http://alex.starspirals.net/2011/01/10/%d1%81%d0%be%d0%bd-%d0%b8-%d1%81%d0%bd%d0%be%d0%b2%d0%b8%d0%b4%d0%b5%d0%bd%d0%b8%d1%8f-%d1%87%d0%b0%d1%81%d1%82%d1%8c-%d1%82%d1%80%d0%b5%d1%82%d1%8c%d1%8f-%d0%be%d1%81%d0%be%d0%b7%d0%bd%d0%b0%d0%bd/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Аллея академиков в снегу</title>
		<link>http://alex.starspirals.net/2010/11/29/%d0%b0%d0%bb%d0%bb%d0%b5%d1%8f-%d0%b0%d0%ba%d0%b0%d0%b4%d0%b5%d0%bc%d0%b8%d0%ba%d0%be%d0%b2-%d0%b2-%d1%81%d0%bd%d0%b5%d0%b3%d1%83/</link>
		<comments>http://alex.starspirals.net/2010/11/29/%d0%b0%d0%bb%d0%bb%d0%b5%d1%8f-%d0%b0%d0%ba%d0%b0%d0%b4%d0%b5%d0%bc%d0%b8%d0%ba%d0%be%d0%b2-%d0%b2-%d1%81%d0%bd%d0%b5%d0%b3%d1%83/#comments</comments>
		<pubDate>Mon, 29 Nov 2010 19:34:37 +0000</pubDate>
		<dc:creator>Алексей</dc:creator>
				<category><![CDATA[ТТУ]]></category>
		<category><![CDATA[Фото]]></category>
		<category><![CDATA[samsung d600]]></category>
		<category><![CDATA[академики]]></category>
		<category><![CDATA[аллея академиков]]></category>
		<category><![CDATA[зима]]></category>
		<category><![CDATA[снег]]></category>
		<category><![CDATA[тту]]></category>
		<category><![CDATA[фото]]></category>
		<category><![CDATA[фотография]]></category>

		<guid isPermaLink="false">http://alex.starspirals.net/?p=1209</guid>
		<description><![CDATA[Довольно забавные головные уборы подарил господам академикам снегопад. Снято на камеру телефона в «Аллее академиков» возле главного здания Таллинского технического университета.]]></description>
			<content:encoded><![CDATA[<p>Довольно забавные головные уборы подарил господам академикам снегопад.<br />
<span id="more-1209"></span><br />
Снято на камеру телефона в «Аллее академиков» возле главного здания Таллинского технического университета.</p>

<a href='http://alex.starspirals.net/2010/11/29/%d0%b0%d0%bb%d0%bb%d0%b5%d1%8f-%d0%b0%d0%ba%d0%b0%d0%b4%d0%b5%d0%bc%d0%b8%d0%ba%d0%be%d0%b2-%d0%b2-%d1%81%d0%bd%d0%b5%d0%b3%d1%83/academic_01/' title='academic_01'><img width="150" height="150" src="http://alex.starspirals.net/wp-content/uploads/2010/11/academic_01-150x150.jpg" class="attachment-thumbnail" alt="academic_01" title="academic_01" /></a>
<a href='http://alex.starspirals.net/2010/11/29/%d0%b0%d0%bb%d0%bb%d0%b5%d1%8f-%d0%b0%d0%ba%d0%b0%d0%b4%d0%b5%d0%bc%d0%b8%d0%ba%d0%be%d0%b2-%d0%b2-%d1%81%d0%bd%d0%b5%d0%b3%d1%83/academic_02/' title='academic_02'><img width="150" height="150" src="http://alex.starspirals.net/wp-content/uploads/2010/11/academic_02-150x150.jpg" class="attachment-thumbnail" alt="academic_02" title="academic_02" /></a>
<a href='http://alex.starspirals.net/2010/11/29/%d0%b0%d0%bb%d0%bb%d0%b5%d1%8f-%d0%b0%d0%ba%d0%b0%d0%b4%d0%b5%d0%bc%d0%b8%d0%ba%d0%be%d0%b2-%d0%b2-%d1%81%d0%bd%d0%b5%d0%b3%d1%83/academic_03/' title='academic_03'><img width="150" height="150" src="http://alex.starspirals.net/wp-content/uploads/2010/11/academic_03-150x150.jpg" class="attachment-thumbnail" alt="academic_03" title="academic_03" /></a>
<a href='http://alex.starspirals.net/2010/11/29/%d0%b0%d0%bb%d0%bb%d0%b5%d1%8f-%d0%b0%d0%ba%d0%b0%d0%b4%d0%b5%d0%bc%d0%b8%d0%ba%d0%be%d0%b2-%d0%b2-%d1%81%d0%bd%d0%b5%d0%b3%d1%83/academic_04/' title='academic_04'><img width="150" height="150" src="http://alex.starspirals.net/wp-content/uploads/2010/11/academic_04-150x150.jpg" class="attachment-thumbnail" alt="academic_04" title="academic_04" /></a>
<a href='http://alex.starspirals.net/2010/11/29/%d0%b0%d0%bb%d0%bb%d0%b5%d1%8f-%d0%b0%d0%ba%d0%b0%d0%b4%d0%b5%d0%bc%d0%b8%d0%ba%d0%be%d0%b2-%d0%b2-%d1%81%d0%bd%d0%b5%d0%b3%d1%83/academic_05/' title='academic_05'><img width="150" height="150" src="http://alex.starspirals.net/wp-content/uploads/2010/11/academic_05-150x150.jpg" class="attachment-thumbnail" alt="academic_05" title="academic_05" /></a>
<a href='http://alex.starspirals.net/2010/11/29/%d0%b0%d0%bb%d0%bb%d0%b5%d1%8f-%d0%b0%d0%ba%d0%b0%d0%b4%d0%b5%d0%bc%d0%b8%d0%ba%d0%be%d0%b2-%d0%b2-%d1%81%d0%bd%d0%b5%d0%b3%d1%83/academic_06/' title='academic_06'><img width="150" height="150" src="http://alex.starspirals.net/wp-content/uploads/2010/11/academic_06-150x150.jpg" class="attachment-thumbnail" alt="academic_06" title="academic_06" /></a>

]]></content:encoded>
			<wfw:commentRss>http://alex.starspirals.net/2010/11/29/%d0%b0%d0%bb%d0%bb%d0%b5%d1%8f-%d0%b0%d0%ba%d0%b0%d0%b4%d0%b5%d0%bc%d0%b8%d0%ba%d0%be%d0%b2-%d0%b2-%d1%81%d0%bd%d0%b5%d0%b3%d1%83/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>ProgTime: Oceansize</title>
		<link>http://alex.starspirals.net/2010/11/24/progtime-oceansize/</link>
		<comments>http://alex.starspirals.net/2010/11/24/progtime-oceansize/#comments</comments>
		<pubDate>Wed, 24 Nov 2010 13:46:15 +0000</pubDate>
		<dc:creator>Алексей</dc:creator>
				<category><![CDATA[ProgTime]]></category>
		<category><![CDATA[Арт]]></category>
		<category><![CDATA[Музыка]]></category>
		<category><![CDATA[Рецензии]]></category>
		<category><![CDATA[oceansize]]></category>
		<category><![CDATA[porcupine tree]]></category>
		<category><![CDATA[prog]]></category>
		<category><![CDATA[progressive]]></category>
		<category><![CDATA[radiohead]]></category>
		<category><![CDATA[арт-рок]]></category>
		<category><![CDATA[музыка]]></category>
		<category><![CDATA[прогрессив]]></category>
		<category><![CDATA[прогрессивная музыка]]></category>
		<category><![CDATA[рок]]></category>

		<guid isPermaLink="false">http://alex.starspirals.net/?p=1187</guid>
		<description><![CDATA[Недавно вышел тут новый альбом английской прог-группы Oceansize, этим самым подтолкнув меня к очередному посту на тему прогрессивной музыки. Альбом, прямо скажем, весьма и весьма оригинальный. До этого английские ребята выпустили 3 альбома, Effloresce (2003), Everyone Into Position (2005) и &#8230; <a href="http://alex.starspirals.net/2010/11/24/progtime-oceansize/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><img src="http://alex.starspirals.net/wp-content/uploads/2010/11/oceansize.jpg" alt="" title="oceansize" width="188" height="120" class="alignright size-full wp-image-1189" />Недавно вышел тут новый альбом английской прог-группы <strong>Oceansize</strong>, этим самым подтолкнув меня к очередному посту на тему прогрессивной музыки. Альбом, прямо скажем, весьма и весьма оригинальный.</p>
<p>До этого английские ребята выпустили 3 альбома, <strong>Effloresce</strong> (2003), <strong>Everyone Into Position</strong> (2005) и <strong>Frames</strong> (2007).</p>
<p>Что музыка? В основном, это атмосферный, «эмоциональный» прогрессив-рок. То есть виртуозных гитарных/клавишных соло и оперного вокала вы здесь не услышите. Это компенсируется оригинальностью построения мелодических линий и композиций в целом. Тяжелые части перемежаются с мягкими гитарными переборами, создавая хороший контраст.</p>
<p>Однако вот в силу структуры композиций и отсутствия ярко выраженного мелодического сюжета  музыка Oceansize запоминается с трудом, за некоторым, конечно, исключением. Это касается первой половины творчества группы. Вторая половина — то есть альбомы <strong>Frames</strong> и <strong>Self Preserved While the Bodies Float Up</strong> — намного более интересны, полны музыкального разнообразия. Хотя кто-нибудь может сказать, что Frames слегка затянут.</p>
<p>Возвращаясь к их последнему альбому можно смело заявить, что это самая зрелая работа Oceansize. Боюсь, правда, что несмотря на это, этот альбом едва ли найдёт огромное число поклонников. Хотя кто знает? Для меня, например, это определённо лучший выпуск 2010 года.</p>
<p>Под катом две композиции для ознакомления и официальное видео для вещи SuperImposer с последнего альбома.<br />
<span id="more-1187"></span><br />
<strong>Oceansize</strong> — Frames (2007) — Trail of Fire</p>
<p><strong>Oceansize</strong> — Self Preserved While The Bodies Float Up (2010) — Build us a rocket then&#8230;</p>
<p>P.S. На ютубе можно найти концертное исполнение этой вещи, где фронтмен Майк Веннарт говорит её полное на тот момент название <i>Build us a rocket then you rocket building cunt</i></p>
<p>Официальное видео для вещи SuperImposer с того же альбома:<br />
<object width="640" height="385"><param name="movie" value="http://www.youtube.com/v/d8vfxcJf3HM?fs=1&amp;hl=ru_RU&amp;rel=0"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/d8vfxcJf3HM?fs=1&amp;hl=ru_RU&amp;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"></embed></object></p>
<p>Дополнительно: <a href="http://www.progarchives.com/artist.asp?id=1595"  onclick="pageTracker._trackPageview('/outgoing/www.progarchives.com/artist.asp?id=1595&amp;referer=');">профиль на ProgArchives.com</a></p>
]]></content:encoded>
			<wfw:commentRss>http://alex.starspirals.net/2010/11/24/progtime-oceansize/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
<enclosure url="http://starspirals.net/_pub/progtime/Oceansize_-_Trail_of_Fire.mp3" length="7792141" type="audio/mpeg" />
<enclosure url="http://starspirals.net/_pub/progtime/Oceansize_-_BUaRT.mp3" length="3823200" type="audio/mpeg" />
		</item>
		<item>
		<title>Ещё заметка о дизайне</title>
		<link>http://alex.starspirals.net/2010/10/20/%d0%b5%d1%89%d1%91-%d0%b7%d0%b0%d0%bc%d0%b5%d1%82%d0%ba%d0%b0-%d0%be-%d0%b4%d0%b8%d0%b7%d0%b0%d0%b9%d0%bd%d0%b5/</link>
		<comments>http://alex.starspirals.net/2010/10/20/%d0%b5%d1%89%d1%91-%d0%b7%d0%b0%d0%bc%d0%b5%d1%82%d0%ba%d0%b0-%d0%be-%d0%b4%d0%b8%d0%b7%d0%b0%d0%b9%d0%bd%d0%b5/#comments</comments>
		<pubDate>Wed, 20 Oct 2010 16:15:03 +0000</pubDate>
		<dc:creator>Алексей</dc:creator>
				<category><![CDATA[Заметки]]></category>
		<category><![CDATA[Размышления]]></category>
		<category><![CDATA[bad design]]></category>
		<category><![CDATA[design]]></category>
		<category><![CDATA[designer]]></category>
		<category><![CDATA[greasemonkey]]></category>
		<category><![CDATA[imdb]]></category>
		<category><![CDATA[imdb.com]]></category>
		<category><![CDATA[link this trivia]]></category>
		<category><![CDATA[trivia]]></category>
		<category><![CDATA[user.js]]></category>
		<category><![CDATA[интересные факты]]></category>

		<guid isPermaLink="false">http://alex.starspirals.net/?p=1159</guid>
		<description><![CDATA[Недавно один из наиболее часто посещаемых мной сайтов imdb.com подвергся атаке дизайнеров. В итоге на первый план поставили вид страниц, а не удобство чтения их содержания. Но это ладно. Другое дело, что в раздел «Trivia» («Интересные факты») для каждой записи &#8230; <a href="http://alex.starspirals.net/2010/10/20/%d0%b5%d1%89%d1%91-%d0%b7%d0%b0%d0%bc%d0%b5%d1%82%d0%ba%d0%b0-%d0%be-%d0%b4%d0%b8%d0%b7%d0%b0%d0%b9%d0%bd%d0%b5/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Недавно один из наиболее часто посещаемых мной сайтов <strong>imdb.com</strong> подвергся атаке дизайнеров.<br />
<span id="more-1159"></span><br />
В итоге на первый план поставили вид страниц, а не удобство чтения их содержания. Но это ладно. Другое дело, что в раздел «Trivia» (<em>«Интересные факты»</em>) для каждой записи в конце теперь добавляется ссылка <span style="font-family: Verdana; font-size: x-small; color: blue; text-decoration: underline">Link this trivia</span>. Выглядит это ужасно. И в настройках сайта это ну никак не отключить.</p>
<p><a href="http://alex.starspirals.net/wp-content/uploads/2010/10/linkthistriviasucks.png" class="liimagelink"><img src="http://alex.starspirals.net/wp-content/uploads/2010/10/linkthistriviasucks.png" alt="" title="Отстойное решение дизайнера" width="638" height="410" class="alignnone size-full wp-image-1160" /></a></p>
<p>Если вы используете Firefox, то исправить это недоразумение можно, поставив <a href="https://addons.mozilla.org/ru/firefox/addon/748/"  onclick="pageTracker._trackPageview('/outgoing/addons.mozilla.org/ru/firefox/addon/748/?referer=');">Greasemonkey</a> и следом скрипт в одну строчку <a href="http://www.starspirals.net/_pub/gm/kill_imdbcom_trivia_link.user.js"  onclick="pageTracker._trackPageview('/outgoing/www.starspirals.net/_pub/gm/kill_imdbcom_trivia_link.user.js?referer=');">kill_imdbcom_trivia_link.user.js</a>.</p>
<p>Дизайнеру на заметку. Не надо делать пёстрый текст. Это бесит. Если уж надо поставить ссылку на каждый отдельный элемент в списке, либо ставь яваскрипт и включай ссылку при наведении на элемент, либо убери её вправо и сделай низкоконтрастной, чтобы глаза без толку не теребила.</p>
]]></content:encoded>
			<wfw:commentRss>http://alex.starspirals.net/2010/10/20/%d0%b5%d1%89%d1%91-%d0%b7%d0%b0%d0%bc%d0%b5%d1%82%d0%ba%d0%b0-%d0%be-%d0%b4%d0%b8%d0%b7%d0%b0%d0%b9%d0%bd%d0%b5/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Подсолнух</title>
		<link>http://alex.starspirals.net/2010/10/03/%d0%bf%d0%be%d0%b4%d1%81%d0%be%d0%bb%d0%bd%d1%83%d1%85/</link>
		<comments>http://alex.starspirals.net/2010/10/03/%d0%bf%d0%be%d0%b4%d1%81%d0%be%d0%bb%d0%bd%d1%83%d1%85/#comments</comments>
		<pubDate>Sun, 03 Oct 2010 18:13:20 +0000</pubDate>
		<dc:creator>Алексей</dc:creator>
				<category><![CDATA[Заметки]]></category>
		<category><![CDATA[Фото]]></category>
		<category><![CDATA[подсолнух]]></category>
		<category><![CDATA[природа]]></category>
		<category><![CDATA[фото]]></category>
		<category><![CDATA[фотография]]></category>
		<category><![CDATA[цветы]]></category>

		<guid isPermaLink="false">http://alex.starspirals.net/?p=1145</guid>
		<description><![CDATA[Вырос прямо под окнами нашего дома. Долго собирался с силами и под октябрь решил зацвести. А дело тут вот в чем. Прилетающих к нам на балкон время от времени синиц мы кормим семечками подсолнуха. Синицы забирают по семечку и улетают &#8230; <a href="http://alex.starspirals.net/2010/10/03/%d0%bf%d0%be%d0%b4%d1%81%d0%be%d0%bb%d0%bd%d1%83%d1%85/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Вырос прямо под окнами нашего дома. Долго собирался с силами и под октябрь решил зацвести.<br />
<span id="more-1145"></span><br />
<img src="http://alex.starspirals.net/wp-content/uploads/2010/10/sunflower_allpics.jpg" alt="" title="Подсолнух" width="635" height="906" class="alignnone size-full wp-image-1146" /></p>
<p>А дело тут вот в чем. Прилетающих к нам на балкон время от времени синиц мы кормим семечками подсолнуха. Синицы забирают по семечку и улетают его очищать на куст сирени возле подъезда (в отличие от воробьев, которые жадно пожирают всё прямо на месте). Скорее всего, птица уронила какое-то из семян и оно проросло.</p>
]]></content:encoded>
			<wfw:commentRss>http://alex.starspirals.net/2010/10/03/%d0%bf%d0%be%d0%b4%d1%81%d0%be%d0%bb%d0%bd%d1%83%d1%85/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Музыка на ATtiny2313: двухканальный синтезатор</title>
		<link>http://alex.starspirals.net/2010/08/20/%d0%bc%d1%83%d0%b7%d1%8b%d0%ba%d0%b0-%d0%bd%d0%b0-attiny2313-1/</link>
		<comments>http://alex.starspirals.net/2010/08/20/%d0%bc%d1%83%d0%b7%d1%8b%d0%ba%d0%b0-%d0%bd%d0%b0-attiny2313-1/#comments</comments>
		<pubDate>Fri, 20 Aug 2010 08:02:22 +0000</pubDate>
		<dc:creator>Алексей</dc:creator>
				<category><![CDATA[Музыка]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[Электроника]]></category>
		<category><![CDATA[2 channel]]></category>
		<category><![CDATA[8 bit]]></category>
		<category><![CDATA[assembler]]></category>
		<category><![CDATA[atmel]]></category>
		<category><![CDATA[attiny2313]]></category>
		<category><![CDATA[avr]]></category>
		<category><![CDATA[avr assembler]]></category>
		<category><![CDATA[diy]]></category>
		<category><![CDATA[lilium]]></category>
		<category><![CDATA[mp3]]></category>
		<category><![CDATA[music player]]></category>
		<category><![CDATA[музыка]]></category>
		<category><![CDATA[проигрыватель]]></category>
		<category><![CDATA[сделай сам]]></category>
		<category><![CDATA[синтезатор]]></category>

		<guid isPermaLink="false">http://alex.starspirals.net/?p=899</guid>
		<description><![CDATA[В статье я поведаю о том, как сделать на основе микроконтроллера Atmel ATtiny2313 двухканальный синтезатор прямоугольного сигнала и покажу, как из этого получить простой 8-битный музыкальный проигрыватель. Приводится схема рассматриваемого устройства и полный код на ассемблере. Более подробно остановимся на &#8230; <a href="http://alex.starspirals.net/2010/08/20/%d0%bc%d1%83%d0%b7%d1%8b%d0%ba%d0%b0-%d0%bd%d0%b0-attiny2313-1/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><img src="http://alex.starspirals.net/wp-content/uploads/2010/08/attiny2313_is1.jpg" alt="" title="ATtiny2313" width="150" height="108" class="alignleft size-full wp-image-902" />В статье я поведаю о том, как сделать на основе микроконтроллера <strong>Atmel ATtiny2313</strong> двухканальный синтезатор прямоугольного сигнала и покажу, как из этого получить простой 8-битный музыкальный проигрыватель. Приводится схема рассматриваемого устройства и полный код на ассемблере.</p>
<p>Более подробно остановимся на следующем:</p>
<ul>
<li>Музыкальная теория, необходимая для реализации музыкального проигрывателя;</li>
<li>Генератор частоты на 16-ти разрядных ШИМ-каналах;</li>
<li>Конвертация мелодии и удобная запись нот.</li>
</ul>
<p>Поговорим немного и о работе с оперативной памятью SRAM и организацию работы программы по принципу обработки событий.<br />
<span id="more-899"></span></p>
<div class="wm-toc">
<div align="center"><span class="wm-toc-header">Содержание</span> <span class="wm-toc-link">[<a id="wm-toc-toggle" href="javascript:wmTocToggle();">Скрыть</a>]</span></div>
<div id="wm-toc-container">
<ol class='wm-first-level'>
<li><a href="#wm-toc-899-8-8-8-8-9-1" class="liinternal">8.8.8.8.9.1 Введение</a></li>
<li><a href="#wm-toc-899-8-8-8-8-9-2" class="liinternal">8.8.8.8.9.2 Схема устройства</a></li>
<li><a href="#wm-toc-899-8-8-8-8-9-3" class="liinternal">8.8.8.8.9.3 Немного теории</a>
<ol>
<li><a href="#wm-toc-899-8-8-8-8-9-3-1" class="liinternal">8.8.8.8.9.3.1 Высота ноты</a></li>
<li><a href="#wm-toc-899-8-8-8-8-9-3-2" class="liinternal">8.8.8.8.9.3.2 Длительность ноты</a></li>
</ol>
</li>
<li><a href="#wm-toc-899-8-8-8-8-9-4" class="liinternal">8.8.8.8.9.4 Планировка программы</a>
<ol>
<li><a href="#wm-toc-899-8-8-8-8-9-4-1" class="liinternal">8.8.8.8.9.4.1 Генерация заданной частоты</a></li>
<li><a href="#wm-toc-899-8-8-8-8-9-4-2" class="liinternal">8.8.8.8.9.4.2 Выдержка длительности нот</a></li>
<li><a href="#wm-toc-899-8-8-8-8-9-4-3" class="liinternal">8.8.8.8.9.4.3 Управление ходом программы</a></li>
</ol>
</li>
<li><a href="#wm-toc-899-8-8-8-8-9-5" class="liinternal">8.8.8.8.9.5 Программный код</a>
<ol>
<li><a href="#wm-toc-899-8-8-8-8-9-5-1" class="liinternal">8.8.8.8.9.5.1 Определения символьных имён</a></li>
<li><a href="#wm-toc-899-8-8-8-8-9-5-2" class="liinternal">8.8.8.8.9.5.2 Макросы</a></li>
<li><a href="#wm-toc-899-8-8-8-8-9-5-3" class="liinternal">8.8.8.8.9.5.3 Разметка SRAM</a></li>
<li><a href="#wm-toc-899-8-8-8-8-9-5-4" class="liinternal">8.8.8.8.9.5.4 Таблица прерываний</a></li>
<li><a href="#wm-toc-899-8-8-8-8-9-5-5" class="liinternal">8.8.8.8.9.5.5 Инициализация и основной цикл</a></li>
<li><a href="#wm-toc-899-8-8-8-8-9-5-6" class="liinternal">8.8.8.8.9.5.6 Инициализация железа</a></li>
<li><a href="#wm-toc-899-8-8-8-8-9-5-7" class="liinternal">8.8.8.8.9.5.7 Завершение программы</a></li>
<li><a href="#wm-toc-899-8-8-8-8-9-5-8" class="liinternal">8.8.8.8.9.5.8 Установка ноты на канале</a></li>
<li><a href="#wm-toc-899-8-8-8-8-9-5-9" class="liinternal">8.8.8.8.9.5.9 Генератор частоты</a></li>
<li><a href="#wm-toc-899-8-8-8-8-9-5-10" class="liinternal">8.8.8.8.9.5.10 Обработчик системных часов</a></li>
</ol>
</li>
<li><a href="#wm-toc-899-8-8-8-8-9-6" class="liinternal">8.8.8.8.9.6 Музыкальная композиция</a>
<ol>
<li><a href="#wm-toc-899-8-8-8-8-9-6-1" class="liinternal">8.8.8.8.9.6.1 Преобразование к формату проигрывателя</a></li>
<li><a href="#wm-toc-899-8-8-8-8-9-6-2" class="liinternal">8.8.8.8.9.6.2 Таблицы нот и длительностей в ассемблере</a></li>
<li><a href="#wm-toc-899-8-8-8-8-9-6-3" class="liinternal">8.8.8.8.9.6.3 Запись композиции</a></li>
</ol>
</li>
<li><a href="#wm-toc-899-8-8-8-8-9-7" class="liinternal">8.8.8.8.9.7 Полный код программы</a></li>
<li><a href="#wm-toc-899-8-8-8-8-9-8" class="liinternal">8.8.8.8.9.8 Результат</a></li>
<li><a href="#wm-toc-899-8-8-8-8-9-9" class="liinternal">8.8.8.8.9.9 Примечания</a></li>
</ol>
</div>
</div>
<h2 id="wm-toc-899-8-8-8-8-9-1" name="wm-toc-899-8-8-8-8-9-1">Введение</h2>
<p>Когда я только начинал разбираться с микроконтроллерами семейства AVR, я собрал <a href="http://www.ustr.net/avr/sineplayer.html"  onclick="pageTracker._trackPageview('/outgoing/www.ustr.net/avr/sineplayer.html?referer=');">крохотное устройство</a>, где в качестве генератора звука использовался <strong>tiny13</strong>. Для этого там применялась ШИМ-модуляция гармонических колебаний. Тогда мне казалось, что существенным упущением было генерировать частоту из основного цикла без использования прерываний. Однако позже выяснилось, что модуляция синуса, даже с разрешением 8 бит и даже при наличии таблицы синусов для проигрывания мелодии задача весьма нетривиальная и крайне критичная по времени. В случае ATtiny2313 в наличии всего два таймера, а требуется контролировать ШИМ, модуляцию синуса на двух каналах, длительность нот, а также общий ход программы при сохранении многозадачности.</p>
<p>В итоге я решил, что буду рисовать музыку прямоугольными колебаниями<sup class='wm-footnote'><a href="#wmfn-899-0" id='wmfn-899-ref-0' class="liinternal">[1]</a></sup>. Вообще моей главной задачей было получить как можно более точную частоту, элегантный способ задания высоты и длительности нот. И, как мне кажется, это было достигнуто.</p>
<h2 id="wm-toc-899-8-8-8-8-9-2" name="wm-toc-899-8-8-8-8-9-2">Схема устройства</h2>
<p>Схема (PDF): <a href="http://alex.starspirals.net/wp-content/uploads/2010/08/ATtiny2313_music_player.pdf" class="lipdf">ATtiny2313_music_player</a></p>
<p>Для генерации сигнала будем использовать 16-битный таймер, поэтому нам нужны выводы микроконтроллера OC1A и OC1B. С них будем брать левый и правый каналы, предварительно пропустив через RC-фильтр, у которого две основных функции:</p>
<ul>
<li>Работа в режиме интегратора — из ШИМ получается сигнал заданного уровня;</li>
<li>Работа в режиме фильтра нижних частот.</li>
</ul>
<p>Первая функция в данном проекте практически никакого применения не находит, а вот фильтр нужен для удаления из генерируемого сигнала паразитных высокочастотных составляющих, в том числе нежелательных обертонов. Частоту среза в герцах можно посчитать при помощи уравнения:</p>
<p><img src="http://alex.starspirals.net/wp-content/uploads/2010/08/l_Fcut.gif" alt="" title="l_Fcut" width="92" height="46" class="aligncenter size-full wp-image-1176" /></p>
<p>В данном случае я использовал значения R=470Ω, C=100nF. Это даёт частоту среза 3386.27 Гц, что вполне подходит для проигрывателя.</p>
<p>Насчёт подключения акустической системы: я включал напрямую к маломощному динамику на 8Ω, сил микроконтроллера хватало на достаточно громкое воспроизведения музыки. Причём я сводил два канала в один, просто соединяя два провода вместе. Вообще так делать не рекомендуется, весьма желательно воткнуть между выходом с микроконтроллера и акустической системой операционный усилитель-другой.</p>
<h2 id="wm-toc-899-8-8-8-8-9-3" name="wm-toc-899-8-8-8-8-9-3">Немного теории</h2>
<p>Нашей задачей является создание проигрывателя, значит, нужно вспомнить азы музыкальной теории. Композиции состоят из совокупности нот, а у всякой ноты имеется два фундаментальных параметра — высота и длительность.</p>
<h3 id="wm-toc-899-8-8-8-8-9-3-1" name="wm-toc-899-8-8-8-8-9-3-1">Высота ноты</h3>
<p>Высота ноты задаётся определённой частотой колебаний звукового сигнала. Для расчета периода колебаний по частоте нам будет нужна простейшая формула. К ней мы вернёмся немного позже:</p>
<p><img src="http://alex.starspirals.net/wp-content/uploads/2010/08/l_Tper.gif" alt="" title="l_Tper" width="58" height="49" class="aligncenter size-full wp-image-1177" /></p>
<p>В современной музыке ныне практически повсеместно используется так называемый <strong>равномерно темперированный строй</strong>. Вдаваться в подробности я не буду, если интересно, к вашим услугам <a href="http://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B2%D0%BD%D0%BE%D0%BC%D0%B5%D1%80%D0%BD%D0%BE_%D1%82%D0%B5%D0%BC%D0%BF%D0%B5%D1%80%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D1%80%D0%BE%D0%B9" rel="nofollow" class="liwikipedia" onclick="pageTracker._trackPageview('/outgoing/ru.wikipedia.org/wiki/_D0_A0_D0_B0_D0_B2_D0_BD_D0_BE_D0_BC_D0_B5_D1_80_D0_BD_D0_BE_D1_82_D0_B5_D0_BC_D0_BF_D0_B5_D1_80_D0_B8_D1_80_D0_BE_D0_B2_D0_B0_D0_BD_D0_BD_D1_8B_D0_B9_D1_81_D1_82_D1_80_D0_BE_D0_B9?referer=');">статья на Википедии</a>. Нам для проекта необходимо знать соответствие высот нот частотам согласно этому строю. На той же Википедии есть <a href="http://en.wikipedia.org/wiki/Piano_key_frequencies" rel="nofollow" class="liwikipedia" onclick="pageTracker._trackPageview('/outgoing/en.wikipedia.org/wiki/Piano_key_frequencies?referer=');">вполне годная таблица</a>, которой мы и воспользуемся.</p>
<h3 id="wm-toc-899-8-8-8-8-9-3-2" name="wm-toc-899-8-8-8-8-9-3-2">Длительность ноты</h3>
<p>В музыке <a href="http://ru.wikipedia.org/wiki/%D0%94%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D0%BD%D0%BE%D1%82" rel="nofollow" class="liwikipedia" onclick="pageTracker._trackPageview('/outgoing/ru.wikipedia.org/wiki/_D0_94_D0_BB_D0_B8_D1_82_D0_B5_D0_BB_D1_8C_D0_BD_D0_BE_D1_81_D1_82_D1_8C_D0_BD_D0_BE_D1_82?referer=');">длительность ноты</a> понятие относительное и зависит от темпа, в котором исполняется произведение. Темп обычно задаётся в <strong>ударах в минуту</strong> (bpm), причём один удар равен одной четвертной ноте. Следовательно, запись <strong>100 bpm</strong> (или <img src="http://alex.starspirals.net/wp-content/uploads/2010/08/14_note.gif" alt="" title="14_note" width="5" height="16" class="alignnone size-full wp-image-996" />=100) означает, что в минуте помещается ровно 100 четвертных нот. Нетрудно тогда посчитать и длительность одной четвертной ноты, 1/100 = 0.01 минут или 0.6 секунд<sup class='wm-footnote'><a href="#wmfn-899-1" id='wmfn-899-ref-1' class="liinternal">[2]</a></sup>.</p>
<p>Посчитаем для примера реальные длительности нот для темпа <img src="http://alex.starspirals.net/wp-content/uploads/2010/08/14_note.gif" alt="" title="14_note" width="5" height="16" class="alignnone size-full wp-image-996" />=120. Основой будет длительность четвертной ноты, равной в этом случае <nobr>60000/120=500 мс</nobr>.</p>
<table cellspacing="0" cellpadding="0" border="0" style="width: 100%; border: 1px solid #EAEAFF; font-size: 90%;">
<tr style="background: #EAEAFF; vertical-align: middle; font-weight: bold;">
<td style="width: 15%; text-align: center;">Обозначение</td>
<td style="width: 45%; text-align: left; padding-left: 10%;">Отношение</td>
<td style="width: 40%; text-align: center;">Длительность (мс)</td>
</tr>
<tr style="">
<td style="width: 15%; text-align: center;"><img src="http://alex.starspirals.net/wp-content/uploads/2010/08/44.gif" alt="" title="44" width="35" height="47" class="alignnone size-full wp-image-1001" /></td>
<td style="width: 45%; vertical-align: middle; text-align: left; padding-left: 10%;">4/4 (целая)</td>
<td style="width: 40%; vertical-align: middle; text-align: center;">2000</td>
</tr>
<tr style="">
<td style="width: 15%; text-align: center;"><img src="http://alex.starspirals.net/wp-content/uploads/2010/08/24.gif" alt="" title="24" width="35" height="47" class="alignnone size-full wp-image-1002" /></td>
<td style="width: 45%; vertical-align: middle; text-align: left; padding-left: 10%;">2/4 (половинная)</td>
<td style="width: 40%; vertical-align: middle; text-align: center;">1000</td>
</tr>
<tr style="">
<td style="width: 15%; text-align: center;"><img src="http://alex.starspirals.net/wp-content/uploads/2010/08/14.gif" alt="" title="14" width="35" height="47" class="alignnone size-full wp-image-1003" /></td>
<td style="width: 45%; vertical-align: middle; text-align: left; padding-left: 10%;">1/4 (четвертная)</td>
<td style="width: 40%; vertical-align: middle; text-align: center;">500</td>
</tr>
<tr style="">
<td style="width: 15%; text-align: center;"><img src="http://alex.starspirals.net/wp-content/uploads/2010/08/18.gif" alt="" title="18" width="35" height="47" class="alignnone size-full wp-image-1004" /></td>
<td style="width: 45%; vertical-align: middle; text-align: left; padding-left: 10%;">1/8 (восьмая)</td>
<td style="width: 40%; vertical-align: middle; text-align: center;">250</td>
</tr>
<tr style="">
<td style="width: 15%; text-align: center;"><img src="http://alex.starspirals.net/wp-content/uploads/2010/08/116.gif" alt="" title="116" width="35" height="47" class="alignnone size-full wp-image-1005" /></td>
<td style="width: 45%; vertical-align: middle; text-align: left; padding-left: 10%;">1/16 (шестнадцатая)</td>
<td style="width: 40%; vertical-align: middle; text-align: center;">125</td>
</tr>
<tr style="">
<td style="width: 15%; text-align: center;"><img src="http://alex.starspirals.net/wp-content/uploads/2010/08/132.gif" alt="" title="132" width="35" height="47" class="alignnone size-full wp-image-1006" /></td>
<td style="width: 45%; vertical-align: middle; text-align: left; padding-left: 10%;">1/32 (тридцатьвторая)</td>
<td style="width: 40%; vertical-align: middle; text-align: center;">62.5</td>
</tr>
</table>
<h2 id="wm-toc-899-8-8-8-8-9-4" name="wm-toc-899-8-8-8-8-9-4">Планировка программы</h2>
<p>Опираясь на приведённую выше информацию, а также на <a href="http://www.atmel.com/dyn/resources/prod_documents/doc2543.PDF"  onclick="pageTracker._trackPageview('/outgoing/www.atmel.com/dyn/resources/prod_documents/doc2543.PDF?referer=');">технические возможности ATtiny2313</a>, можно составить общий план работы программы.</p>
<h3 id="wm-toc-899-8-8-8-8-9-4-1" name="wm-toc-899-8-8-8-8-9-4-1">Генерация заданной частоты</h3>
<p>Нам нужен прямоугольный сигнал с частотой, соответствующей определённой ноте. Прямоугольник можно получить, чередуя выходные сигналы микроконтроллера 0→1→0→&#8230;, причём время стабильного состояния низкого и высокого уровней должно быть одинаковым. Оно будет тогда являться не чем иным, как полупериодом колебания. Следовательно, два раза по столько и получим полный период, откуда можно высчитать и частоту. Таким образом, частоту будем задавать через полупериод колебания.</p>
<p>Для генерации сигнала мы используем 16-битный таймер. Из даташита определим подходящий режим его работы. Это будет нормальный режим, с таким приколом: таймер считает от 0 до 65535, а при достижении значения <code>OCR1A</code> (<code>OCR1B</code>) чередует на соответствующем выводе уровень сигнала (0→1 или 1→0).</p>
<p>Этого, конечно, маловато будет. Ведь если таймер перекинул выход, он затем будет считать ещё 65535 тиков до следующего изменения, как тогда задать полупериод? С одной стороны логично предложить сброс таймера при достижении <code>OCR1A</code>. Но второй канал тогда определённо сильно пострадает.</p>
<p>Можно поступить следующим образом. Включим прерывания по достижении значений <code>OCR1A</code> и <code>OCR1B</code>. В обработчике такого прерывания будет код, прибавляющий к текущему значению регистра аккурат ровно столько же тиков, сколько нужно для задания полупериода. То есть, например, полупериод задан 2300 тиками таймера, текущее значение на момент прерывания было 4100. Тогда следующее прерывание должно произойти в момент времени 2300+4100=6400. Это значение и занесём в регистр. Вот и готов наш генератор прямоугольной частоты.</p>
<p>Давайте теперь рассмотрим конкретные цифры. Согласно нашей рабочей схеме, ATtiny2313 работает от кристалла с частотой 20.000 МГц. Тогда если взять предделитель <code>/8</code> таймера1, получим, что длительность одного тика таймера равна:</p>
<p><img src="http://alex.starspirals.net/wp-content/uploads/2010/08/l_Ttick.gif" alt="" title="l_Ttick" width="239" height="50" class="aligncenter size-full wp-image-1178" /></p>
<p>Иными словами, мы можем задавать длительность полупериода с разрешением в 0.4 микросекунд. Выведем формулу для определения количества тиков таймера из исходной частоты:</p>
<p><img src="http://alex.starspirals.net/wp-content/uploads/2010/08/l_Nticks_0.gif" alt="" title="l_Nticks_0" width="271" height="49" class="aligncenter size-full wp-image-1179" /></p>
<p>Число тиков для, скажем, частоты 440.000 Гц (соответствует ноте ля 4-ой октавы — A4) определяется так:</p>
<p><img src="http://alex.starspirals.net/wp-content/uploads/2010/08/l_Nticks.gif" alt="" title="l_Nticks" width="235" height="46" class="aligncenter size-full wp-image-1180" /></p>
<p>Конечно, мы рассматриваем целочисленные значения, так как десятичные дроби таймеру не скормишь. Из-за округления возникает некоторая ошибка при формировании частоты. Но как показывает практика, эта ошибка находится в допустимом диапазоне.</p>
<p>Для того, чтобы изменения сигналов были поданы на вывод микроконтроллера, соответствующие порты должны быть заданы как выходные в регистре <code>DDR</code>. Это пригодится для пауз на каналах: мы просто будем отключать выход и получать с него тишину.</p>
<h3 id="wm-toc-899-8-8-8-8-9-4-2" name="wm-toc-899-8-8-8-8-9-4-2">Выдержка длительности нот</h3>
<p>Поскольку у нас остался один восьмибитный таймер, то для задания времени звучания нот на каждом из каналов понадобятся софт-таймеры (то есть полученные программно). «Железный» же таймер заставим работать как генератор прерываний; в обработчик прерывания засунем код, который будет поддерживать правильную работу системы и следить за софт-таймерами.</p>
<p>Возьмём режим работы таймера0: сброс при достижении <code>OCR0A</code>. В качестве предделителя используем <code>/64</code>. Будем генерировать прерывания каждые 50 тиков, следовательно в регистр <code>OCR0A</code> требуется занести значение 49. При таком подходе каждое такое прерывание назовём тиком системных часов. Время одного тика:</p>
<p><img src="http://alex.starspirals.net/wp-content/uploads/2010/08/l_Tclk.gif" alt="" title="l_Tclk" width="251" height="50" class="aligncenter size-full wp-image-1182" /></p>
<p>Получаем разрешение для софт-таймеров в 0.16 мс. Этого хватит и для достаточно высокого темпа воспроизведения.</p>
<p>Софт-таймеры длительности нот организуем таким образом, чтобы они считали в обратную сторону. Оттикав заданное число, раз они инициируют смену ноты. Число тиков этих таймеров зависит от реальной длительности ноты следующим образом:</p>
<p><img src="http://alex.starspirals.net/wp-content/uploads/2010/08/l_Nticks2.gif" alt="" title="l_Nticks2" width="137" height="48" class="aligncenter size-full wp-image-1183" /></p>
<p>где t<sub>note</sub> — длительность ноты в миллисекундах. Так, например, для длительности восьмой ноты в темпе <img src="http://alex.starspirals.net/wp-content/uploads/2010/08/14_note.gif" alt="" title="14_note" width="5" height="16" class="alignnone size-full wp-image-996" />=120 количество тиков будет равно:</p>
<p><img src="http://alex.starspirals.net/wp-content/uploads/2010/08/l_Nticks3.gif" alt="" title="l_Nticks3" width="201" height="46" class="aligncenter size-full wp-image-1184" /></p>
<p>Обратно же, в наличии некоторая погрешность, но куда без неё? :-)</p>
<h3 id="wm-toc-899-8-8-8-8-9-4-3" name="wm-toc-899-8-8-8-8-9-4-3">Управление ходом программы</h3>
<p>Сделаем так, что при нажатии одной кнопки начинается воспроизведение. При этом загорается светодиод. Программа сбрасывается в двух случаях (при этом светодиод гаснет):</p>
<ol>
<li>Вся мелодия сыграна;</li>
<li>Нажата вторая кнопка.</li>
</ol>
<p>Управление будем осуществлять из главного цикла программы.</p>
<h2 id="wm-toc-899-8-8-8-8-9-5" name="wm-toc-899-8-8-8-8-9-5">Программный код</h2>
<p>Ну вот мы и подошли, похоже, к самой интересной части статьи. Рассматривать код будем по кусочкам.</p>
<h3 id="wm-toc-899-8-8-8-8-9-5-1" name="wm-toc-899-8-8-8-8-9-5-1">Определения символьных имён</h3>
<p>Сперва, конечно, в листинг нужно кинуть инклуд с именем зверя, а также назначить символьные имена регистров, используемых в программе. Констант у нас пока не будет, обойдёмся без них.</p>
<pre class="brush: avrasm"> ; Проект для ATtiny2313
.INCLUDE "tn2313def.inc"

; ---------------------
; Определения регистров
; ---------------------

.DEF	SrSave	= R9		; Хранение регистра состояния

.DEF	Tone1H	= R10		; Старший байт тона канала 1
.DEF	Tone1L	= R11		; Младший байт тона канала 1
.DEF	Tone2H	= R12		; Старший байт тона канала 2
.DEF	Tone2L	= R13		; Младший байт тона канала 2

.DEF	ToneTH	= R14		; Старший байт тона
.DEF	ToneTL	= R15		; Младший байт тона

.DEF	Temp	= R17		; Временный регистр

.DEF	Temp1 	= R18		; Вспомогательные регистры
.DEF	Temp2	= R19
.DEF	Temp3	= R20
.DEF 	Temp4	= R21

.DEF	ADDRH	= R22		; Старший байт адреса
.DEF 	ADDRL	= R23		; Младший байт адреса
</pre>
<p>Регистры <code>R10</code>—<code>R13</code> используются для хранения тона на протяжении всей длительности ноты для генерации частоты (можно использовать SRAM, но тут нам важна скорость отработки прерываний, так что лучше будет хранить данные в регистрах). <code>R16</code> же я не использовал, потому что он активно задействован в макросах.</p>
<h3 id="wm-toc-899-8-8-8-8-9-5-2" name="wm-toc-899-8-8-8-8-9-5-2">Макросы</h3>
<p>Я использовал всего четыре макроса. Вот они:</p>
<pre class="brush: avrasm">
 ; --------------------
; Определения макросов
; --------------------

; OUTI port, value :: отправить значение в порт
.MACRO OUTI
	ldi R16,@1
	out @0,R16
.ENDM

; MEMR register, (location) :: считать из памяти в регистр
.MACRO MEMR
	lds @0, @1
.ENDM

; MEMW (location), register :: записать в память регистр
.MACRO MEMW
	sts @0, @1
.ENDM

; MEMWI (location), value :: записать значение в память
.MACRO MEMWI
	ldi R16, @1
	sts @0, R16
.ENDM
</pre>
<p>Из них реально полезными являются <code>OUTI</code> и <code>MEMWI</code>, два других, <code>MEMR</code> и <code>MEMW</code>, просто псевдонимы соответственно инструкций <code>lds</code> и <code>sts</code>, введённые исключительно для моего удобства.</p>
<h3 id="wm-toc-899-8-8-8-8-9-5-3" name="wm-toc-899-8-8-8-8-9-5-3">Разметка SRAM</h3>
<p>Для использования оперативной памяти в программах можно предварительно разметить необходимую область памяти под свои нужды. Делается это при помощи директивы <code>.DSEG</code>, последующих меток и соответствующих меткам директив <code>.BYTE</code>, «выделяющих» в памяти необходимое количество байт<sup class='wm-footnote'><a href="#wmfn-899-2" id='wmfn-899-ref-2' class="liinternal">[3]</a></sup>.</p>
<p>Для нашей программы зарезервируем всего 8 байт. В первых четырех будут хранится текущие адреса таблицы нот для первого и второго каналов. Остальные байты пойдут под 16-ти разрядные софт-таймеры.</p>
<p>Получаем такую структуру:</p>
<pre class="brush: avrasm">
 ; -------------
; Разметка SRAM
; -------------

.DSEG

; Позиция первой мелодии
melody1:
	.BYTE 2

; Позиция второй мелодии
melody2:
	.BYTE 2

; Текущие длительности нот
duration1:
	.BYTE 2

duration2:
	.BYTE 2
</pre>
<h3 id="wm-toc-899-8-8-8-8-9-5-4" name="wm-toc-899-8-8-8-8-9-5-4">Таблица прерываний</h3>
<p>Согласно плану программы, нам нужно определить обработчики для трёх прерываний. Не стоит забывать также и о директивах <code>.CSEG</code> и <code>.ORG 0x0000</code>, означающих начало кода программы с адреса <code>0x0000</code>.</p>
<pre class="brush: avrasm">
 ; ---------------------
; Начало кода программы
; ---------------------

.CSEG
.ORG 0x0000

; ------------------
; Таблица прерываний
; ------------------

rjmp Init ; Обработчик сброса
reti
reti
reti
rjmp TIMER1_COMPA ; Обработчик CompareA таймера1
reti
reti
reti
reti
reti
reti
reti
rjmp TIMER1_COMPB ; Обработчик CompareB таймера1
rjmp TIMER0_COMPA ; Обработчик CompareA таймера0
reti
reti
reti
reti
reti
</pre>
<p>Остальные прерывания у нас не используются, и на них стоит заглушка в виде инструкции <code>reti</code>.</p>
<h3 id="wm-toc-899-8-8-8-8-9-5-5" name="wm-toc-899-8-8-8-8-9-5-5">Инициализация и основной цикл</h3>
<pre class="brush: avrasm">
 ; -------------------------
; Начало основной программы
; -------------------------

Init:

	; Ждать, пока не нажата кнопка1
	cbi DDRD, 4
	sbi PORTD,4

waitkey:

	sbic PIND, 4
	rjmp waitkey

	; Задать выходы
	; (изначально отключить вывод прямоугольных колебаний)
	outi DDRB, (1&lt;&lt;PB0) | (0&lt;&lt;PB3) | (0&lt;&lt;PB4)

	; Задать стек
	outi SPL, low(RAMEND)

	; Включить светодиод
	sbi PORTB, 0

	; Инициализировать железо
	rcall Timer1_Init
	rcall TIMER_Init

	; Установить прерывания
	outi TIMSK, (1&lt;&lt;OCIE1A) | (1&lt;&lt;OCIE1B) | (1&lt;&lt;OCIE0A)

	; Записать изначальные локации мелодий в память

	; 1 канал
	memwi melody1, high(tblMelody1*2)
	memwi melody1+1, low(tblMelody1*2)

	; 2 канал
	memwi melody2, high(tblMelody2*2)
	memwi melody2+1, low(tblMelody2*2)

	; Очистить регистры сравнения
	outi OCR1AH, 0
	outi OCR1AL, 0

	outi OCR1BH, 0
	outi OCR1BL, 0

	; Установить ноты
	rcall setCh1
	rcall setCh2

	; Очистить таймер0
	outi TCNT0, 0

	; Очистить таймер1
	outi TCNT1H, 0
	outi TCNT1L, 0

	; Сбросить предделитель
	outi GTCCR, (1&lt;&lt;PSR10)

	; Включить прерывания и начать игру
	sei

back2:

	; Сброс, если кнопка2 нажата
	sbi PORTD,5

	sbis PIND, 5
	rjmp endplay

	rjmp back2
</pre>
<p>Обратите внимание, что стек нужно задать в явном виде. Поскольку на борту tiny2313 всего 128 байт оперативной памяти, то старшего байта у регистра стека нет.</p>
<p>Основной цикл программы в петле с возвращением в метку <code>back2</code>. Здесь проверяется нажатие второй кнопки, всё остальное организовано в обработчике системных часов.</p>
<h3 id="wm-toc-899-8-8-8-8-9-5-6" name="wm-toc-899-8-8-8-8-9-5-6">Инициализация железа</h3>
<p>Тут нужно просто задать параметры для двух таймеров.</p>
<pre class="brush: avrasm">
 ; ----------------------
; Инициализация железа
; ----------------------

; Инициализация таймера1
Timer1_Init:

	; Включить смену уровней при сравнении
	outi TCCR1A, (1&lt;&lt;COM1A0) | (1&lt;&lt;COM1B0) | (0&lt;&lt;WGM10)

	; Включить таймер с предделителем /8
	outi TCCR1B, (2&lt;&lt;CS10)

	ret

; Инициализация таймера0 (системного таймера)
TIMER_Init:

	; Установить режим работы
	outi TCCR0A, (1&lt;&lt;WGM01) | (0&lt;&lt;WGM00)

	; Установить значение переполнения
	outi OCR0A, 49

	; Установить предделитель /64
	outi TCCR0B, 3

	ret
</pre>
<h3 id="wm-toc-899-8-8-8-8-9-5-7" name="wm-toc-899-8-8-8-8-9-5-7">Завершение программы</h3>
<p>Как было сказано, завершение<sup class='wm-footnote'><a href="#wmfn-899-3" id='wmfn-899-ref-3' class="liinternal">[4]</a></sup> происходит в случае окончания проигрывания мелодии или нажатия кнопки 2.</p>
<pre class="brush: avrasm">
 ; ---------------------
; Конец воспроизведения
; ---------------------

ENDPLAY:

	; Игра завершена или прервана
	cli

	; Очистить порты
	clr R16
	out PORTB, R16

	; Отменить воспроизведение на обоих каналах
	cbi DDRB, 3
	cbi DDRB, 4

	; Очистить флаги прерываний
	outi TIFR, (1&lt;&lt;OCF1A) | (1&lt;&lt;OCF1B) | (1&lt;&lt;OCF0A)

	rjmp Init
</pre>
<h3 id="wm-toc-899-8-8-8-8-9-5-8" name="wm-toc-899-8-8-8-8-9-5-8">Установка ноты на канале</h3>
<p>Рассмотрим код для установки ноты на канале. Всего у нас будет четыре таблицы данных, которые мы разместим во флеш-памяти, поскольку в EEPROM они целиком всё равно не влезут (да и скорость доступа оттуда низкая). Таблицы будут такими:</p>
<ul>
<li>Таблица нот</li>
<li>Таблица длительностей</li>
<li>Таблица мелодии на канале 1</li>
<li>Таблица мелодии на канале 2</li>
</ul>
<p>Все значения в таблицах будут кодироваться словами (2 байта). В случае нот мы кодируем высоту и длительность, а мелодии состоят из пар байт нота-длительность (плюс управляющие слова). Не обязательно заносить в таблицы нот и длительности все ноты подряд, достаточно лишь тех, что используются в конкретном произведении. Это позволяет сэкономить место.</p>
<p>Рассмотрим код для первого канала (код для второго канала отличается именами используемых регистров и адресов памяти, в остальном идентичен).</p>
<pre class="brush: avrasm">
 ; -----------------------------------------
; Функции по управлению тона и длительности
; -----------------------------------------

; ------------------
; Установка канала 1
; ------------------
setCh1:

	; Загрузить ноту и длительность
	memr ZH, melody1
	memr ZL, melody1+1

	; Взять байт ноты
	lpm
	mov Temp1, R0

	; Взять байт длительности
	adiw zl, 1
	lpm
	mov Temp2, R0

	; Сохранить адрес следующей ноты
	adiw ZL, 1
	memw melody1, ZH
	memw melody1+1, ZL

	; Проверка на управляющий байт конца
	cpi Temp1, 255
	breq endplay

	; Проверка на паузу
	cpi Temp1, 0
	breq isPause1

	dec Temp1

	ldi ADDRH, high(tblNotes*2)
	ldi ADDRL, low(tblNotes*2)
	rcall getVals

	; Получить значение тона
	mov Tone1H, Temp3
	mov Tone1L, Temp4

	in ToneTH, OCR1AH
	in ToneTL, OCR1AL

	add ToneTL, Tone1L
	adc ToneTH, Tone1H

	out OCR1AH, ToneTH
	out OCR1AL, ToneTL

	; Включить вывод сигнала на первом канале
	sbi DDRB, 3

	rjmp noPause1

isPause1:

	; Пауза — отключить вывод сигнала на первом канале
	cbi DDRB, 3

noPause1:

	; Загрузить длительность
	ldi ADDRH, high(tblDuration*2)
	ldi ADDRL, low(tblDuration*2)
	mov Temp1, Temp2
	rcall getVals

	memw duration1, Temp3
	memw duration1+1, Temp4

	; Выход из процедуры
	ret

endplay1:
	rjmp endplay
</pre>
<p>В коде используется вызов процедуры <code>getVals</code>. Она просто считывает данные из указанного адреса флеш-памяти и заносит в регистры <code>Temp3</code> и <code>Temp4</code>:</p>
<pre class="brush: avrasm">
 ; --------------------------------------
; Загрузить значения из памяти программы
; --------------------------------------

getVals:

	mov ZH, ADDRH
	mov ZL, ADDRL

	lsl Temp1

	clr R16
	add ZL, Temp1
	adc ZH, R16

	lpm
	mov Temp4, R0

	adiw ZL, 1
	lpm
	mov Temp3, R0

	ret
</pre>
<p>Кроме того, есть переход — <code>rjmp endplay</code>, он здесь потому, что дальше программе было не достать метку <code>endplay</code> (из-за ограниченности длины прыжка, максимально равной 127 байт) и пришлось использовать вспомогательный переход.</p>
<h3 id="wm-toc-899-8-8-8-8-9-5-9" name="wm-toc-899-8-8-8-8-9-5-9">Генератор частоты</h3>
<p>Рассмотрим первый канал:</p>
<pre class="brush: avrasm">
 ; --------------------------
; Управления тоном канала 1
; --------------------------

TIMER1_COMPA:

	; Отключить прерывания
	cli

	; Сохранить SREG
	in SrSave, SREG

	; Задать следующий полупериод
	in ToneTH, OCR1AH
	in ToneTL, OCR1AL
	add ToneTL, Tone1L
	adc ToneTH, Tone1H
	out OCR1AH, ToneTH
	out OCR1AL, ToneTL

	; Восстановить SREG
	out SREG, SrSave

	; Включить прерывания
	sei

	reti
</pre>
<p>Всё согласно рассуждениям выше. Кроме того:</p>
<ul>
<li>Чтобы не произошло произвольной фигни, нужно отключать прерывания</li>
<li>Также, поскольку контроль тона может происходит часто, прерывания могут возникнуть где угодно в программе и грохнуть SREG, что опять же приведёт к рандомной фигне, так что его надо сохранять и восстанавливать</li>
</ul>
<p>Всё вышесказанное справедливо и для второго канала.</p>
<h3 id="wm-toc-899-8-8-8-8-9-5-10" name="wm-toc-899-8-8-8-8-9-5-10">Обработчик системных часов</h3>
<p>Тот самый, который тикает раз в 160 микросекунд. Вот код:</p>
<pre class="brush: avrasm">
 ; --------------------------
; Главный обработчик событий
; --------------------------

TIMER0_COMPA:

	; Проверить длительность на 1 канале
	memr Temp3, duration1
	memr Temp4, duration1+1

	clr R16
	subi Temp4, 1
	sbc Temp3, R16
	brcc saveDur1

	; Загрузить следующую ноту
	cli
	rcall setCh1
	sei

	rjmp nextDur

saveDur1:

	memw duration1, Temp3
	memw duration1+1, Temp4

nextDur:

	memr Temp3, duration2
	memr Temp4, duration2+1

	clr R16
	subi Temp4, 1
	sbc Temp3, R16
	brcc saveDur2

	cli
	rcall setCh2
	sei

	rjmp next1

saveDur2:

	memw duration2, Temp3
	memw duration2+1, Temp4

next1:

	reti
</pre>
<p>На этом программный код готов. Теперь нужна какая-нибудь музыка, которую наш проигрыватель будет играть.</p>
<h2 id="wm-toc-899-8-8-8-8-9-6" name="wm-toc-899-8-8-8-8-9-6">Музыкальная композиция</h2>
<p><div id="attachment_915" class="wp-caption alignright" style="width: 160px"><img src="http://alex.starspirals.net/wp-content/uploads/2010/08/LucyWithMusicBox.jpg" alt="" title="Lucy With Music Box" width="150" height="187" class="size-full wp-image-915" /><p class="wp-caption-text"><strong><a href='http://alex.starspirals.net/wp-content/uploads/2010/08/Lilium.mid'>Lilium.mid</a></strong></p></div>Здесь я на конкретном примере покажу, каким образом конвертировать заданную композицию в формат, понятный нашему плееру.</p>
<p>В качестве мелодии для проигрывателя я выбрал весьма красивую титульную тему <strong>Lilium</strong> из аниме <a href="http://www.imdb.com/title/tt0480489/"  onclick="pageTracker._trackPageview('/outgoing/www.imdb.com/title/tt0480489/?referer=');">Elfen Lied</a>.</p>
<p>У нас двухканальный (двухголосый) синтезатор, значит нам нужно расписать композицию на два голоса, что я и сделал. Использовал я для этого MIDI-файл.</p>
<p>Как перекладывать музыку вариантов может быть несколько. Тут нам нужен какой-нибудь MIDI-редактор, способный показывать ноты во вменяемом виде<sup class='wm-footnote'><a href="#wmfn-899-4" id='wmfn-899-ref-4' class="liinternal">[5]</a></sup>. Очень удобно использовать режим просмотра <strong>piano roll</strong> (<a href="http://alex.starspirals.net/wp-content/uploads/2010/08/piano_roll_in_nuendo3.png" class="liinternal">пример в Nuendo 3</a>), так как тут наглядно видно и ноты, и их длительности.</p>
<h3 id="wm-toc-899-8-8-8-8-9-6-1" name="wm-toc-899-8-8-8-8-9-6-1">Преобразование к формату проигрывателя</h3>
<p>Конвертацию делать придётся вручную (автоматические средства пока не доступны): выписываем каждую ноту ровно один раз и подбираем для неё число тиков по приведённой выше формуле. Смотрим, первая нота <strong>F#4</strong>. Посмотрим <a href="http://en.wikipedia.org/wiki/Piano_key_frequencies" rel="nofollow" class="liwikipedia" onclick="pageTracker._trackPageview('/outgoing/en.wikipedia.org/wiki/Piano_key_frequencies?referer=');">по таблице</a> частоту этой ноты. Увидим значение 369.994. Значит, число тиков будет</p>
<p><img src="http://alex.starspirals.net/wp-content/uploads/2010/08/l_Nf4.gif" alt="" title="l_Nf4" width="260" height="46" class="aligncenter size-full wp-image-1185" /></p>
<p>Выпишем в таком духе все ноты, участвующие в произведении, и посчитаем для них число тиков таймера. Удобно для расчётов использовать <strong>Microsoft Excel</strong> или какой-нибудь математический пакет вроде <strong>Scilab</strong> или <strong>Maxima</strong>. Я, например, заготовил <a href="http://alex.starspirals.net/wp-content/uploads/2010/08/ATtiny2313_MP_Notes.xls" class="liinternal">таблицу</a> (Excel 2003) и <a href="http://alex.starspirals.net/wp-content/uploads/2010/08/ATtiny2313_MP_Notes.pdf" class="lipdf">PDF файл</a>.</p>
<p>Затем пройдёмся по композиции ещё раз и выпишем все участвующие в нём длительности. Вычислим и для них соответствующее число тиков софт-таймера, учитывая темп произведения <img src="http://alex.starspirals.net/wp-content/uploads/2010/08/14_note.gif" alt="" title="14_note" width="5" height="16" class="alignnone size-full wp-image-996" />=60, что значит, что длительность четвертной ноты равна 1 секунде. Не забываем также и о паузах.</p>
<p>Когда всё это готово, запишем в ассемблерный листинг таблицы с полученными нотами и длительностями.</p>
<h3 id="wm-toc-899-8-8-8-8-9-6-2" name="wm-toc-899-8-8-8-8-9-6-2">Таблицы нот и длительностей в ассемблере</h3>
<p>Таблица будет состоять из слов (в данном случае размером два байта), значит нужно воспользоваться директивой <code>.DW</code>. Составим таблицу нот:</p>
<pre class="brush: avrasm">
 ; --------------
; Таблицы данных
; --------------

; Таблица нот :: тон = 1/(8e-7*N)
tblNotes:
	.dw 11364	; A2
	.dw 10124	; B2
	.dw 9556	; C3
	.dw 9019	; C#3
	.dw 8513	; D3
	.dw 7584	; E3
	.dw 7159	; F3
	.dw 6757	; F#3
	.dw 6020	; G#3
	.dw 5682	; A3
	.dw 5363	; A#3
	.dw 5062	; B3
	.dw 4510	; C#4
	.dw 4257	; D4
	.dw 4018	; D#4
	.dw 3792	; E4
	.dw 3579	; F4
	.dw 3378	; F#4
	.dw 3010	; G#4
	.dw 2841	; A4
	.dw 2681	; A#4
	.dw 2531	; B4
	.dw 2389	; C5
	.dw 2255	; C#5
	.dw 2128	; D5
	.dw 1896	; E5
	.dw 1689	; F#5
</pre>
<p>Воспользуемся на этом этапе <strong>константами</strong>. Это очень удобное средство сопоставить положение ноты в таблице конкретному символьному имени, которое можно будет использовать при записи мелодии. Константы вводятся в эксплуатацию директивой <code>.EQU</code>.</p>
<pre class="brush: avrasm">
 ; Зададим символьные имена нот

; Они отмечают реальные позиции
; нот в tblNotes

; Специальные значения: PAUSE = 0, END = 255

	.equ 	PAUSE	=	0
	.equ 	A2		=	1
	.equ 	B2		=	2
	.equ 	C3		=	3
	.equ 	Cs3		=	4
	.equ 	D3		=	5
	.equ 	E3		=	6
	.equ 	F3		=	7
	.equ 	Fs3		=	8
	.equ 	Gs3		=	9
	.equ 	A3		=	10
	.equ 	As3		=	11
	.equ 	B3		=	12
	.equ 	Cs4		=	13
	.equ 	D4		=	14
	.equ 	Ds4		=	15
	.equ 	E4		=	16
	.equ 	F4		=	17
	.equ 	Fs4		=	18
	.equ 	Gs4		=	19
	.equ 	A4		=	20
	.equ 	As4		=	21
	.equ 	B4		=	22
	.equ 	C5		=	23
	.equ 	Cs5		=	24
	.equ 	D5		=	25
	.equ 	E5		=	26
	.equ 	Fs5		=	27
	.equ	END		=	255
</pre>
<p>Сделаем то же самое и для таблицы длительностей:</p>
<pre class="brush: avrasm">
 ; Таблица длительностей :: bpm=60
; Tim0 /64, 50тиков -> 1.6e-4 секунд на CMP эвент

tblDuration:
	.dw 25000	; полная нота
	.dw 18750	; 1/2 с точкой
	.dw 12500	; 1/2
	.dw 6250	; 1/4
	.dw 3125	; 1/8

; Символьные имена длительностей

	.equ d1		= 0
	.equ d12dot	= 1
	.equ d12	= 2
	.equ d14	= 3
	.equ d18	= 4
</pre>
<p>Обратите внимание, что тут я использовал обозначение целой ноты как <code>d1</code>, а половинной — <code>d12</code>, хотя корректнее было бы использовать <code>d44</code> и <code>d24</code>. Однако мне так было удобнее.</p>
<h3 id="wm-toc-899-8-8-8-8-9-6-3" name="wm-toc-899-8-8-8-8-9-6-3">Запись композиции</h3>
<p>Когда всё это сделано, можно начинать записывать саму композицию, в два прохода — сначала один канал, а затем второй. Вот что получилось:</p>
<pre class="brush: avrasm">
 ; Канал первой мелодии
tblMelody1:
	.db Fs4, d18	; Часть 1
	.db Cs5, d18
	.db Gs4, d18
	.db Gs3, d18
	.db Cs4, d18
	.db A4, d18
	.db A4, d18
	.db A3, d18
	.db Fs4, d18
	.db Cs5, d18
	.db Gs4, d18
	.db Gs3, d18
	.db Cs4, d18
	.db A4, d18
	.db B4, d18
	.db A4, d18
	.db Fs4, d18
	.db A3, d18
	.db D4, d18
	.db A3, d18
	.db B3, d18
	.db Gs4, d18
	.db E4, d18
	.db D4, d18
	.db E4, d18
	.db Gs3, d18
	.db Cs4, d18
	.db Gs3, d18
	.db As3, d18
	.db Fs4, d18
	.db D4, d18
	.db Cs4, d18
	.db D4, d18
	.db E4, d18
	.db Fs4, d14
	.db PAUSE, d18
	.db Gs4, d18
	.db A4, d18
	.db B4, d18
	.db A4, d18
	.db Cs4, d18
	.db Fs4, d18
	.db A4, d18
	.db Gs4, d14
	.db Fs4, d18 ; Часть 2
	.db Cs5, d18
	.db Gs4, d18
	.db Gs3, d18
	.db Cs4, d18
	.db A4, d18
	.db A4, d18
	.db A3, d18
	.db Fs4, d18
	.db Cs5, d18
	.db Gs4, d18
	.db Gs3, d18
	.db Cs4, d18
	.db A4, d18
	.db B4, d18
	.db A4, d18
	.db Fs4, d18
	.db A3, d18
	.db D4, d18
	.db A3, d18
	.db B3, d18
	.db Gs4, d18
	.db E4, d18
	.db D4, d18
	.db E4, d18
	.db Gs3, d18
	.db Cs4, d18
	.db Gs3, d18
	.db As3, d18
	.db Fs4, d18
	.db D4, d18
	.db Cs4, d18
	.db D4, d18
	.db B4, d18
	.db A4, d18
	.db Gs4, d18
	.db Fs4, d18
	.db F4, d18
	.db Ds4, d18
	.db F4, d18
	.db Fs4, d18
	.db As4, d18	; Часть 3
	.db B4, d18
	.db Cs5, d18
	.db D5, d18
	.db A4, d18
	.db Gs4, d18
	.db D5, d18
	.db Cs5, d18
	.db E5, d18
	.db A4, d18
	.db Cs5, d18
	.db C5, d18
	.db A4, d18
	.db Gs4, d18
	.db Fs4, d18
	.db A4, d18
	.db Cs4, d18
	.db Fs4, d18
	.db B4, d18
	.db Gs4, d12dot
	.db Fs4, d18	; Часть 4
	.db Cs5, d18
	.db Gs4, d18
	.db Gs3, d18
	.db Cs4, d18
	.db A4, d18
	.db A4, d18
	.db A3, d18
	.db Fs4, d18
	.db Cs5, d18
	.db Gs4, d18
	.db Gs3, d18
	.db Cs4, d18
	.db A4, d18
	.db B4, d18
	.db A4, d18
	.db Fs4, d18
	.db A3, d18
	.db D4, d18
	.db A3, d18
	.db B3, d18
	.db Gs4, d18
	.db E4, d18
	.db D4, d18
	.db E4, d18
	.db Gs3, d18
	.db Cs4, d18
	.db Gs3, d18
	.db As3, d18
	.db Fs4, d18
	.db D4, d18
	.db Cs4, d18
	.db D4, d18
	.db B4, d18
	.db A4, d18
	.db Gs4, d18
	.db Fs4, d18
	.db F4, d18
	.db Ds4, d18
	.db F4, d18
	.db Fs4, d18
	.db Cs4, d18
	.db Fs4, d18
	.db B4, d18
	.db Fs5, d12

	.db END, 0

; Канал второй мелодии
tblMelody2:
	.db PAUSE, d14
	.db F3, d12
	.db Fs3, d14
	.db A2, d18
	.db PAUSE, d18
	.db F3, d12
	.db Fs3, d14
	.db D3, d1
	.db Cs3, d12
	.db Fs3, d12
	.db B2, d12
	.db C3, d12
	.db Cs3, d12
	.db Gs3, d12
	.db F3, d12
	.db Fs3, d14
	.db A2, d18
	.db PAUSE, d18
	.db F3, d12
	.db Fs3, d14
	.db D3, d1
	.db Cs3, d12
	.db Fs3, d12
	.db B2, d14
	.db C3, d14
	.db Cs3, d14
	.db F3, d14
	.db Fs3, d12
	.db B3, d14
	.db E3, d14
	.db A3, d14
	.db Fs3, d14
	.db A3, d14
	.db C3, d14
	.db Cs3, d12
	.db Cs4, d12dot
	.db PAUSE, d14
	.db F3, d12
	.db Fs3, d14
	.db A2, d18
	.db PAUSE, d18
	.db F3, d12
	.db Fs3, d14
	.db D3, d1
	.db Cs3, d12
	.db Fs3, d12
	.db B2, d14
	.db C3, d14
	.db Cs4, d14
	.db F3, d14
	.db Fs3, d12
	.db A4, d12

	.db END, 0
</pre>
<h2 id="wm-toc-899-8-8-8-8-9-7" name="wm-toc-899-8-8-8-8-9-7">Полный код программы</h2>
<p>Вот полный код (комментарии на английском): <a href="http://www.starspirals.net/prj/ATtiny2313XBoard/ATtiny2313XBoard_MusicPlayer.asm"  onclick="pageTracker._trackPageview('/outgoing/www.starspirals.net/prj/ATtiny2313XBoard/ATtiny2313XBoard_MusicPlayer.asm?referer=');">ATtiny2313XBoard_MusicPlayer.asm</a></p>
<p>В полученной прошивке код и данные занимают где-то по 500 байт, оставляя свободным ещё 1КБ для разнообразного контента, будь то дополнительный код или музыка.</p>
<h2 id="wm-toc-899-8-8-8-8-9-8" name="wm-toc-899-8-8-8-8-9-8">Результат</h2>
<p>Запрограммируем контроллер, не забывая выставить фузы на работу от внешнего кристалла (<code>CKSEL3...0 = 0</code>). Нажмём на кнопку и из микроконтроллера начинает стремиться музыка:</p>
<p>Задача выполнена :-) Надеюсь, материал был для вас полезным! Через некоторое время последует вторая часть статьи, посвещенная MIDI и последовательной передаче данных в контексте того же ATtiny2313.</p>
<p>Успехов!</p>
<hr />
<h2 id="wm-toc-899-8-8-8-8-9-9" name="wm-toc-899-8-8-8-8-9-9">Примечания</h2>
<div class='wm-footnote-container'>
<ol class='wm-footnote-list'>
<li class='wm-footnote-list-element' id='wmfn-899-0'><a href="#wmfn-899-ref-0" class='wm-backlink'>&uarr;</a><span class='wm-note-content'>Вот наглядное сравнение двух видов колебаний:</p>
<p><a href="http://alex.starspirals.net/wp-content/uploads/2010/08/sine_square.png" class="liimagelink"><img src="http://alex.starspirals.net/wp-content/uploads/2010/08/sine_square.png" alt="" title="sine_square" width="500" height="301" class="aligncenter size-full wp-image-948" /></a></p>
<p>В случае с микроконтроллером за отрезок на рисунке [-1; 1] принимаются значения напряжения на выходе порта [0; 5] вольт (при напряжении питания +5В).</p>
<p>Пример звучания синуса на 440 Гц:</p>
<p>А вот прямоугольный сигнал, 440 Гц:</p>
<p>Как слышите, звук, получаемый при помощи синуса более мягкий, тихий и плавный, в то время, как прямоугольник резкий и более громкий.</span></li>
<li class='wm-footnote-list-element' id='wmfn-899-1'><a href="#wmfn-899-ref-1" class='wm-backlink'>&uarr;</a><span class='wm-note-content'>В дальнейших вычислениях нам будет удобнее всего измерять длительность нот в миллисекундах.</span></li>
<li class='wm-footnote-list-element' id='wmfn-899-2'><a href="#wmfn-899-ref-2" class='wm-backlink'>&uarr;</a><span class='wm-note-content'>Разметка делается лишь для удобства обращения к памяти из программы и не гарантирует сохранения границ между данными так, как это задумывает автор.</span></li>
<li class='wm-footnote-list-element' id='wmfn-899-3'><a href="#wmfn-899-ref-3" class='wm-backlink'>&uarr;</a><span class='wm-note-content'>Если возник вопрос, почему я не воткнул завершение программы в самый конец кода, то отвечу: потому что ATtiny2313 не поодерживает инструкцию перехода <code>jmp</code>, а поэтому блоки кода нужно расставлять по принципу досягаемости переходов.</span></li>
<li class='wm-footnote-list-element' id='wmfn-899-4'><a href="#wmfn-899-ref-4" class='wm-backlink'>&uarr;</a><span class='wm-note-content'>Из бесплатных редакторов приходит в голову только Anvil Studio, однако когда пытаешься пользоваться этим куском софта, начинаешь понимать в чем весь прикол бесплатных программ. Шутка ли — ковать MIDI на наковальне.</span></li>
</ol>
</div>
]]></content:encoded>
			<wfw:commentRss>http://alex.starspirals.net/2010/08/20/%d0%bc%d1%83%d0%b7%d1%8b%d0%ba%d0%b0-%d0%bd%d0%b0-attiny2313-1/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
<enclosure url="http://alex.starspirals.net/wp-content/uploads/2010/08/440HzSine.mp3" length="201793" type="audio/mpeg" />
<enclosure url="http://alex.starspirals.net/wp-content/uploads/2010/08/440HzSquare.mp3" length="201793" type="audio/mpeg" />
<enclosure url="http://alex.starspirals.net/wp-content/uploads/2010/08/Lilium.mid" length="1933" type="audio/midi" />
<enclosure url="http://alex.starspirals.net/wp-content/uploads/2010/08/Lilium_ATtiny2313_xBoard.mp3" length="1604441" type="audio/mpeg" />
		</item>
		<item>
		<title>Обновление оформления блога</title>
		<link>http://alex.starspirals.net/2010/06/23/%d0%be%d0%b1%d0%bd%d0%be%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d0%b5-%d0%be%d1%84%d0%be%d1%80%d0%bc%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-%d0%b1%d0%bb%d0%be%d0%b3%d0%b0/</link>
		<comments>http://alex.starspirals.net/2010/06/23/%d0%be%d0%b1%d0%bd%d0%be%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d0%b5-%d0%be%d1%84%d0%be%d1%80%d0%bc%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-%d0%b1%d0%bb%d0%be%d0%b3%d0%b0/#comments</comments>
		<pubDate>Tue, 22 Jun 2010 22:10:02 +0000</pubDate>
		<dc:creator>Алексей</dc:creator>
				<category><![CDATA[Заметки]]></category>
		<category><![CDATA[blog]]></category>
		<category><![CDATA[design]]></category>
		<category><![CDATA[news]]></category>
		<category><![CDATA[twentyten]]></category>
		<category><![CDATA[wordpress 3.0]]></category>
		<category><![CDATA[блог]]></category>
		<category><![CDATA[дизайн]]></category>
		<category><![CDATA[оформление]]></category>
		<category><![CDATA[хороший дизайн]]></category>

		<guid isPermaLink="false">http://alex.starspirals.net/?p=785</guid>
		<description><![CDATA[Вместе с новой версией WordPress 3.0 теперь поставляется довольно приятная тема «2010» («twentyten»), которая заменит отныне на моем блоге слегка устаревший «Kubrick». Поскольку тема новая и пока ещё не обкатанная как следует, с ней возможны проблемы. Пожалуйста, сообщайте о них &#8230; <a href="http://alex.starspirals.net/2010/06/23/%d0%be%d0%b1%d0%bd%d0%be%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d0%b5-%d0%be%d1%84%d0%be%d1%80%d0%bc%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-%d0%b1%d0%bb%d0%be%d0%b3%d0%b0/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Вместе с новой версией WordPress 3.0 теперь поставляется довольно приятная тема «2010» («twentyten»), которая заменит отныне на моем блоге слегка устаревший «Kubrick».</p>
<p>Поскольку тема новая и пока ещё не обкатанная как следует, с ней возможны проблемы. Пожалуйста, сообщайте о них в комментариях. До сих пор были найдены некоторые проблемы с IE6.</p>
<p><strong>Обновление:</strong> Все обнаруженные проблемы с IE6 решены. Подробнее <a href="http://wordpress.org/support/topic/414181?replies=8" class="liwp" onclick="pageTracker._trackPageview('/outgoing/wordpress.org/support/topic/414181?replies=8&amp;referer=');">здесь</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://alex.starspirals.net/2010/06/23/%d0%be%d0%b1%d0%bd%d0%be%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d0%b5-%d0%be%d1%84%d0%be%d1%80%d0%bc%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-%d0%b1%d0%bb%d0%be%d0%b3%d0%b0/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

