• Об авторе
  • Программы и скрипты
    • Windows
      • BeatCalc
      • Segment Code Generator
    • Плагины для WordPress
      • IE6 Support for Twenty Ten Theme
      • WM-Footnotes
      • WM-Toc
  • Учебные материалы ТТУ
  • Фотогалерея
  • Legal notice

Alex.StarSpirals.Net

инженерный блог

Музыка на ATtiny2313: подключение PS/2 клавиатуры

30 марта, 2011 by Алексей


В этой статье из серии «музыка на ATtiny2313» я расскажу, как подключить к разработанному ранее на основе ATtiny2313 двухканальному синтезатору PS/2-клавиатуру.

Содержание [Скрыть]
  1. 1 Введение
  2. 2 Схема устройства
  3. 3 Принципы работы клавиатуры
    1. 3.1 Передача данных
    2. 3.2 Коды сканирования
  4. 4 Программный код
    1. 4.1 Приём сообщения от клавиатуры
    2. 4.2 Обработка полученного байта
  5. 5 Полный код программы
  6. 6 Результат
  7. 7 Примечания
  8. 8 Использованные материалы

Введение

В сложных электронных проектах, требующих ввода пользовательских данных, часто выгодно использовать компьютерную клавиатуру. В зависимости от микроконтроллера, у него может быть встроенная поддержка как PS/2, так и USB HID стандартов. У ATtiny2313 этого добра нет, но подключить к нему клавиатуру и организовать её правильную работу довольно просто. Об этом и пойдёт речь в статье[1].

Схема устройства

Схема осталась прежней. Что нужно добавить: переходник от прошивочного шлейфа к клавиатуре. Распиновки для стандартного PS/2 коннектора, а также для DIN5 (встречается на старых клавиатурах):

PS/2:
1. KBD Clock (часы)
2. GND
3. KBD Data (данные)
4. N/C
5. +5V
6. N/C
DIN5:
1. KBD Clock
2. KBD Data
3. N/C
4. GND
5. +5V (VCC)

Соответствие выводам микроконтроллера[2] следующее:

  • PB5 → KBD Clock
  • PB6 → KBD Data

Как видно, используется всего 2 контакта — синхролиния (часы) и линия данных. Кроме этого клавиатура отжимает +5В питания с платы микроконтроллера.

Принципы работы клавиатуры

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

Передача данных

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

Пересылка фрейма от клавиатуры к хосту

  • Сначала уровни на обоих проводах высокие. Это соответствует состоянию ожидания (Idle), то есть ничего не происходит.
  • Клавиатура инициирует пересылку, выставив на информационный сигнал в «0» (стартовый бит) и сменив уровень на часах «1»→«0».
  • Далее клавиатура пересылает 8 бит данных сообщения от младшего бита к старшему, съем информационного сигнала требуется производить при появлении заднего фронта сигнала часов
  • Предпоследний бит называется битом чётности[3] (parity bit) и используется для проверки целостности переданного байта (простой контроль на ошибку передачи).
  • Последний бит равняется «1» и называется стоповым битом.
  • Пересылка закончена и линии возвращаются в состояние ожидания.

Как видно, фрейм состоит из 11 бит. Переданный байт же соответствует определённой команде. В простейшем случае это код сканирования (scancode). О них дальше и поговорим.

Коды сканирования

Каждой клавише при нажатии соответствует либо один код, либо комбинация кодов (то есть сообщения состоят из нескольких байт). А когда клавишу отпускаешь, прилетает сообщение из нескольких байт, где первый — 0xF0, а второй (их может быть и несколько) соответствует ранее нажатой клавише. Основные коды приведены на следующем рисунке:

Коды сканирования

Например, если нажать правый ALT, то клавиатура пошлёт сообщение 0xE0 0x11, а если затем отпустить — 0xF0 0xE0 0x11.

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

Программный код

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

«Шапка» ассемблерного листинга выглядит следующим образом:

 ; Проект для 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

Приём сообщения от клавиатуры

Для приёма данных от клавиатуры нужно инициализировать прерывание по изменению уровня на цифровом входе синхролинии.

Pcint_Init:

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

	ret

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

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

PCINT:

	cli

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

	pop SrSave
	out SREG, SrSave

	sei

	reti

Если обнаружен задний фронт сигнала (момент «1»→«0»), то вызывается процедура RxKbd, которая отвечает за обработку информационного сигнала.

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

Как это работает:

  • При поступлении стартового бита обнуляется буфер и бит чётности.
  • Биты 1-8 складываются в буфер, кроме того над ними последовательно производится операция XOR, результат которой сохраняется в бите специального регистра.
  • Последняя операция XOR производится с полученным и присланным битами чётности. При этом полученный XOR-ами бит не инвертируется. Следовательно, в результате операции бит чётности должен быть равен 1, если при пересылке не возникло ошибок.
  • После получения последнего бита фрейма управление передаётся процедуре discardByte, если проверка чётности провалена, либо в случае успешной передачи процедуре handleByte, которая обработает полученный байт.

Рассмотрим конкретный пример. Допустим пользователь нажимает клавишу ESC, которой соответствует скан-код 0x76. В таком случае произойдёт следующее:

Обработка полученного байта

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

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

  • Синтезатор имеет два голоса.
  • При поступлении сообщения о нажатии клавиши ищется свободный голос. Если такой найден — в него отправляется соответствующая клавише нота.
  • При поступлении сообщения об отпускании клавиши в статусный регистр заносится метка «следующее сообщение об отпускании клавиши»; когда обрабатывается следующий байт, проверяется, была ли такая клавиша нажата и голос активирован. Если да — соответствующий голос освобождается.
  • Если поступает сообщение о нажатии клавиши ESC, оба голоса освобождаются (что-то наподобие Panic Button, если голоса застревают после отпускания клавиш).

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

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	; ]

Получается следующее отображение клавиш фортепиано на клавиатуру:


Итого: 3 октавы + клавиша «ДО» четвёртой, всего 37 клавиш.

Полный код программы

С комментариями на английском: ATtiny2313XBoard_KeyboardOrgan.asm
Весь код после компиляции занимает 650 байт памяти кристалла.

Результат

Демонстрация[4] полученного электронного органа:


Примечания

  1. ↑Статья про MIDI, если кто-то её ждёт, будет следующей в цикле статей про электронику.
  2. ↑Для данных можно использовать любой пин, а вот для часов нужен вывод с настраиваемым прерыванием по изменению уровня в режиме цифрового входа.
  3. ↑Используется бит нечётности (odd parity): биты, составляющие байт складываются при помощи логической операции XOR и затем берётся инверсное значение. Например, для байта 11011010: 1^1^0^1^1^0^1^0 = 1, !(1) = 0 и передано будет 110110100
  4. ↑Я использую несколько другую плату, чем на схеме. Дополнительно там есть семисегментный индикатор, при помощи которого можно следить за содержимым ячеек памяти. Думаю, на тему отладки таким образом тоже стоит написать статью когда-нибудь :-)

Использованные материалы

  1. http://www.beyondlogic.org/keyboard/keybrd.htm
  2. http://en.wikipedia.org/wiki/Parity_bit
Posted in: Видео, Заметки, Музыка, Программирование, Электроника Tagged: attiny2313, avr, avr assembler, keyboard, keyboard organ, organ, ps/2, интерфейс, орган, подключение, программирование, пс/2, сделай сам, Электроника
Март 2011
Пн Вт Ср Чт Пт Сб Вс
 123456
78910111213
14151617181920
21222324252627
28293031  
« Ноя   Авг »
Сайт работает при поддержке лучшего хостинга в Эстонии

Рубрики

  • ProgTime
  • Арт
  • Видео
  • Заметки
  • Музыка
  • Программирование
  • Размышления
  • Разное
  • Рецензии
  • События
  • Ссылки
  • ТТУ
  • Фото
  • Электроника

Метки

apache art-rock assembler atmel attiny13 attiny2313 avr avr assembler avrdude design diy ftp iq megadrum metal midi mp3 music php prog progressive ProgTime usbasp wap Арт Электроника арт-рок блог гитара музыка настройка сервера подсознание программирование прогрессив прогрессивная музыка рок сделай сам синтезатор скачать снег сознание соло фото фотография хороший дизайн

Мета

  • Войти
  • Лента записей
  • Лента комментариев
  • WordPress.org

Copyright © 2023 Alex.StarSpirals.Net.

Omega WordPress Theme by ThemeHall