что делает контроллер клавиатуры
Контроллер клавиатуры
Здесь приводятся выдержки из файла kbd.def.
Основой послужила книга «Руководство по архитектуре IBM PС AT» / Ж.К. Голенкова, А.В. Заболоцкий, М.Л. Мархасин и др.; Под общ. ред. М.Л. Мархасина. — Минск: ООО «Консул», 1992. — 949 c.: ил.
Структура контроллера клавиатуры
Структура контроллера клавиатуры 8042, установленного на материнской плате приведена ниже
Выходной буфер (8-разрядный регистр) доступен системе только как читаемый порт с адресом 60h, он используется для передачи системе полученных от клавиатуры кодов сканирования и прочитанных по команде системы байтов данных. Выходной буфер должен быть прочитан только после того, как установлен признак «буфер полон» (Разряд 0 регистра состояния).
Входной буфер (8-разрядный регистр) доступен системе только как записываемый порт с адресом 60h (устанавливается флажок, указывающий на последующую запись данных) или 64h (устанавливает флажок, указывающий на последующую запись команды). Данные, записанные в порт 60h, отсылаются клавиатуре, если не являются командой контроллера. Данные должны быть записаны во входной буфер только после того, как сброшен признак «буфер полон» (разряд 1 регистра состояния).
Регистр состояния клавиатуры (8 разрядов) доступен системе только как читаемый порт с адресом 64h. Назначение разрядов приведено ниже.
Разряд | Назначение |
---|---|
0 | 0 — В выходном буфере нет новых данных; 1 — Контроллер поместил новые данные в выходной буфер, но система ещё не приняла их, когда система прочитает значение порта 60h, разряд станет нулевым. |
1 | 0 — входной буфер пуст (порт 60h или 64h) 1 — система поместила данные во входной буфер, но контроллер их ещё не принял. После того, как контроллер их прочитает этот разряд станет нулевым. |
2 | 0 — сброс по включению питания (холодный пуск); 1 — программный сброс (горячий пуск). |
3 | 0 — во входном буфере данные (запись производилась по адресу 60h) 1 — во входном буфере команда (запись производилась по адресу 64h) |
4 | 0 — защитный замок закрыт; 1 — защитный замок открыт |
5 | 1 — Тайм-аут передачи. Передача данных, начатая контроллером не завершена |
6 | 1 — Тайм-аут приёма. Передача данных, начата клавиатурой, но не завершена в запрограммированный промежуток времени (2 мс) |
7 | 1 — Ошибка паритета. Последний байт данных был получен от клавиатуры с чётным паритетом. Клавиатура должна посылать байт с нечётным паритетом. |
В приведённой ниже таблице указаны причины ошибки при различных комбинациях пятого, шестого и седьмого битов регистра состояния.
Разряды | Причина ошибки | ||
7 | 6 | 5 | |
0 | 0 | 1 | Нет синхроимпульса |
0 | 1 | 1 | Есть синхроимпульс, нет отклика |
1 | 0 | 1 | Есть синхроимпульс, ошибка паритета |
В файле kbd.def определены следующие константы для портов:
Макросы в kbd.def проверяют следующие биты регистра состояния (Cm… — маска, F… — номер бита).
Для работы с контроллером клавиатуры в файле kbd.def определены следующие макросы.
KBDInpBufFree BufFull — проверяет, свободен ли входной буфер (возможна ли запись в него), Если нет, то выход по метке BufFull. Портит CF. В AL помещается байт состояния контроллера клавиатуры.
KBDWtInpBufFree — ждёт, когда выходной буфер станет свободным. Портит CF, AL. В AL помещается байт состояния контроллера клавиатуры
KBDOutpBufFull ExErr — проверяет, есть ли данные в выходном буфере. Если данных нет, то выход по метке ExErr. Портит AL.
KBDOutpBufFree — освобождает выходной буфер.
KBDWtOuputBufFull — ждёт, когда во входной буфер поступят данные. Портит AL.
OutputBufFree ExErr — проверяет, есть ли данные в выходном буфере. Если данные есть, то выход по метке ExErr. Портит AL.
KBDInterfaceError ExErr — проверяет, произошла ли ошибка при работе с клавиатурой. Если да, то выход по метке ExErr. Портит AL.
Разряд | Назначение |
---|---|
0 | Резерв изготовителя (состояние перемычки) |
1 | Резерв изготовителя |
2 | Резерв изготовителя |
3 | Резерв изготовителя |
4 | Объём ОЗУ на системной плате |
5 | Резерв изготовителя |
6 | Тип первичного монитора (0 — цветной, 1 — черно-белый) |
7 | Замок клавиатуры (0 — заблокирована, 1 — разблокирована) |
Разряд | Назначение |
---|---|
0 | Системный сброс |
1 | Gate A20 |
2 | Резерв изготовителя |
3 | Резерв изготовителя |
4 | Выходной буфер полон |
5 | Входной буфер пуст |
6 | Синхросигнал клавиатуры (выход) |
7 | Данные клавиатуры (выход) |
Наиболее важны следующие разряды выходного порта, определённые в kbd.def:
На процессорах, начиная с 586, бит 0 к системному сбросу уже не подключён.
Для управления контроллером клавиатуры используются следующие команды
Константы команд управления контроллером клавиатуры, определённые в kbd.def:
Коды ответов контроллера клавиатуры
Для записи в выходной порт необходимо ввести команду D1h по адресу 64h, затем ввести нужное значение данных для выходного порта по адресу 60h.
Чтобы прочитать значение выходного порта необходимо ввести команду D0h по адресу 64h, затем прочитать данные по адресу 60h. Команда должна вводиться только когда выходной буфер пуст. При изменении состояния бита, управляющего адресной линией A20, программа, чтобы нормально продолжать работу, должна быть уверена, что бит установлен правильно. Для этого рекомендуется в контроллер выдавать холостую команду после последовательности записи в порт нужного значения. После выполнения этой холостой команды программа может быть уверена, что предыдущая команда выполнена и разряд адреса A20 установлен аппаратно на требуемое значение. Нельзя использовать команду D1 для изменения любого разряда, кроме первого.
Данные в выходном буфере (порт 60h) — это данные клавиатуры, только если контроллер не получил определённую команду.
В kbd.def определены следующие макросы работы с контроллером клавиатуры
KBDPCReset осуществляет сброс системы, путём подачи команды пульсации нулевого разряда выходного порта. Работает до 486.
KBDDesable запрещает клавиатуру. Макрос должен вызываться при пустом входном буфере. Портит al.
KBDEnable разрешает клавиатуру. Макрос должен вызываться при пустом входном буфере. Портит al.
KBDSetA20 JGateA20AlreadySet устанавливает линию GateA20. Необязательный параметр JGateA20AlreadySet — метка, по которой выполняется переход, если A20 была установлена до выполнения макроса. Портит ax.
KBDResetA20 JGateA20AlreadyReset сбрасывает линию GateA20 Необязательный параметр JGateA20AlreadyReset — метка, по которой выполняется переход, если A20 была сброшена до выполнения макроса. Портит ax.
KBDResetSystem — дожидается, когда входной буфер будет пуст, и сбрасываеть систему. Только для ПК не старше 486 и не под управлением windows.
Режимы работы контроллера клавиатуры определяются разрядами, установленными командой 60h.
Разряд | Назначение |
---|---|
0 | 0 — не генерировать прерывание по заполнению выходного буфера, 1 — генерировать; |
1 | Резерв, должен быть равным нулю; |
2 | Системный флажок. Записывается во второй разряд регистра состояния. |
3 | 0 — подчиниться защитному замку; 1 — игнорировать защитный замок |
4 | 0 — разрешить интерфейс клавиатуры; 1 — запретить. |
5 | 0 — использовать 11-разрядные коды клавиатуры (PC AT); 1 — использовать коды клавиатуры, совместимые с PC XT |
6 | 0 — не преобразовывать коды клавиатуры в IBM PC совместимый; 1 — преобразовывать коды клавиатуры в совместимые с XT. |
7 | Резерв, должен быть равным нулю |
Макрос KBDRdCmdByte читает командный байт контороллера клавиатуры в AL.
Макрос KBDWrCmdByte CmdByte записывает командный байт контороллера клавиатуры. CmdByte — значение командного байта, не должно быть регистром AL.
Клавиатура
При каждой передаче команды или данных от системы на клавиатуру система требует подтверждения от клавиатуры. Если только система не запретит вывод данных с клавиатуры, последняя даёт ответ в течение 20 мс. Если ответ клавиатуры недействителен или содержит ошибку паритета, система повторно посылает ей команду или данные. Двухбайтовые команды требуют особой обработки. Если переданы и подтверждены команды команды F3h, F0h или EDh, а также передан байт данных, но ответ оказался недействительным или с ошибкой паритета, то система передаст повторно и команду и байт данных.
Клавиатура AT имеет 3 режима работы:
Здесь будет рассматриваться в основном режим 3. В этом режиме должно быть запрещено преобразование кодов у контроллера клавиатуры, т. к. он не может обрабатывать этот набор кодов сканирования. BIOS также не может обрабатывать этот код сканирования, поэтому прикладная система, использующая этот режим, должна обрабатывать их сама. Каждой клавише присвоен свой уникальный 8-разрядный код нажатия, который передаётся при нажатии клавиши. Код отжатия состоит из двух байтов первый — префикс отжатия (F0h), а второй идентичен коду нажатия данной клавиши. В этом наборе кодов каждая клавиша посылает только один код сканирования и состояние любой клавиши не зависит от состояния других.
За исключением клавиши Pause все клавиши работают на нажатие/отжатие и являются повторяемыми (т.е. при нажатой и удерживаемой клавише через определённые промежутки клавиатура посылает код нажатия этой клавиши). Если нажаты и удерживаются несколько клавиш, то повторяется код только той клавиши, которая была нажата последней. После её отпускания коды повторений перестают пересылаться, даже если ещё остаются нажатые клавиши. Если клавиша нажата во время запрета клавиатуры, то в буфер клавиатуры поступает только первый код нажатия.
Общение с клавиатурой происходит черз порт 60h.
Макрос KEYBReceive ждёт ответ от клавиатуры. Ответ клавиатуры в регистре al. Используется просто:
Макрос KEYBsend послает команду клавиатуре. Команда может содержать до трёх байт. Команды задаются параметрами CMD, CMD1 и CMD2. Коды команд клавиатуры см. ниже. Если параметр DontWaitACK пустой, то перед посылкой очередного байта (CMD1 или CMD2) ожидается приём подтверждения от клавиатуры. (После посылки CMD2 подтверждения не ожидается) Если DontWaitACK не пустой (любое значение), то ответ от клавиатуры не ожидается.
Команды управления клавиатурой
Байт команды в диапазоне от EF до F1 — резервные. Это холостые однобайтные команды, которые клавиатура принимает и выдаёт ответ CeqKEYBResend, никаких других действий не предпринимается.
После получения подтверждает KACK, очищает выходной буфер и продолжает сканирование (если оно было разрешено ранее). используется только в режиме 3.
Следующие команды установливают отдельные клавиши только повторение, только нажатие\отжатие, только нажатие. Клавиатура получает первый байт, посылает KACK, очищает выходной буфер, ждёт код клавиши (ScanCode), подтверждает его и ждёт следующей команды. Сканирование не возобновляется до тех пор, пока клавиатура не получит команду CeqKEYBEnable. Устанавливать можно несколько клавиш одновременно. Это многобайтные команды.
Коды ответов клавиатуры
Макросы
Макрос KBDEndOfInterrupt завершает обработку прерывания клваиатуры. Используется он так:
Сканкоды клавиш при работе клавиатуры в режимах 2 и 3
Сканкоды клавиш при работе клавиатуры в режиме 2 (с отключённой перекодировкой).
Обозначение | Код в режиме 3 | Клавиша | Лат. shift | Русск. shift | Обозначение | Код в режиме 2 |
---|---|---|---|---|---|---|
CKBD3_0 | 45h | 0 | ) | ) | CKBD2_0 | 45h |
CKBD3_1 | 16h | 1 | ! | ! | CKBD2_1 | 16h |
CKBD3_2 | 1eh | 2 | @ | « | CKBD2_2 | 1eh |
CKBD3_3 | 26h | 3 | # | № | CKBD2_3 | 26h |
CKBD3_4 | 25h | 4 | $ | ; | CKBD2_4 | 25h |
CKBD3_5 | 2eh | 5 | % | : | CKBD2_5 | 2eh |
CKBD3_6 | 36h | 6 | ^ | , | CKBD2_6 | 36h |
CKBD3_7 | 3dh | 7 | & | . | CKBD2_7 | 3dh |
CKBD3_8 | 3eh | 8 | * | * | CKBD2_8 | 3eh |
CKBD3_9 | 46h | 9 | ( | ( | CKBD2_9 | 46h |
Обозначение | Код режима 3 | Клавиша | . | Русск. | Обозначение | Код режима 3 |
---|---|---|---|---|---|---|
CKBD3_tilde | 0eh | ` | CKBD2_tilde | 0eh | ||
CKBD3_minus | 4eh | — | _ | CKBD2_minus | 4eh | |
CKBD3_equal | 55h | = | + | CKBD2_equal | 55h | |
CKBD3_OpenBrace | 54h | [ | < | Х | CKBD2_OpenBrace | 54h |
CKBD3_CloseBrace | 5bh | ] | > | Ъ | CKBD2_CloseBrace | 5bh |
CKBD3_Semicolon | 4ch | ; | : | Ж | CKBD2_Semicolon | 4ch |
CKBD3_Apostophe | 52h | ‘ | « | Э | CKBD2_Apostophe | 52h |
CKBD3_Zapyataya | 41h | , | Ю | CKBD2_dot | 49h | |
CKBD3_Slash | 4ah | / | ? | ё | CKBD2_Slash | 4ah |
CKBD3_BackSlash | 5ch | \ | | | CKBD2_BackSlash | 5dh | |
CKBD3_q | 15h | q | Q | й | CKBD2_q | 15h |
CKBD3_w | 1dh | w | W | ц | CKBD2_w | 1dh |
CKBD3_e | 24h | e | E | у | CKBD2_e | 24h |
CKBD3_r | 2dh | r | R | к | CKBD2_r | 2dh |
CKBD3_t | 2ch | t | T | е | CKBD2_t | 2ch |
CKBD3_y | 35h | y | Y | н | CKBD2_y | 35h |
CKBD3_u | 3ch | u | U | г | CKBD2_u | 3ch |
CKBD3_i | 43h | i | I | ш | CKBD2_i | 43h |
CKBD3_o | 44h | o | O | щ | CKBD2_o | 44h |
CKBD3_p | 4dh | p | P | з | CKBD2_p | 4dh |
CKBD3_a | 1ch | a | A | ф | CKBD2_a | 1ch |
CKBD3_s | 1bh | s | S | ы | CKBD2_s | 1bh |
CKBD3_d | 23h | d | D | в | CKBD2_d | 23h |
CKBD3_f | 2bh | f | f | а | CKBD2_f | 2bh |
CKBD3_g | 34h | g | G | п | CKBD2_g | 34h |
CKBD3_h | 33h | h | H | р | CKBD2_h | 33h |
CKBD3_j | 3bh | j | J | о | CKBD2_j | 3bh |
CKBD3_k | 42h | k | K | л | CKBD2_k | 42h |
CKBD3_l | 4bh | l | L | д | CKBD2_l | 4bh |
CKBD3_z | 1ah | z | Z | я | CKBD2_z | 1ah |
CKBD3_x | 22h | x | X | ч | CKBD2_x | 22h |
CKBD3_c | 21h | c | C | с | CKBD2_c | 21h |
CKBD3_v | 2ah | v | V | м | CKBD2_v | 2ah |
CKBD3_b | 32h | b | B | и | CKBD2_b | 32h |
CKBD3_n | 31h | n | N | т | CKBD2_n | 31h |
CKBD3_m | 3ah | m | M | ь | CKBD2_m | 3ah |
CKBD3_Space | 29h | CKBD2_Space | 29h |
Обозначение | Код в режиме 3 | Клавиша | Обозначение | Код в режиме 2 |
---|---|---|---|---|
CKBD3_Esc | 08h | Escape | CKBD2_Esc | 76h |
CKBD3_F1 | 07h | F1 | CKBD2_F1 | 05h |
CKBD3_F2 | 0fh | F2 | CKBD2_F2 | 06h |
CKBD3_F3 | 17h | F3 | CKBD2_F3 | 04h |
CKBD3_F4 | 1fh | F4 | CKBD2_F4 | 0ch |
CKBD3_F5 | 27h | F5 | CKBD2_F5 | 03h |
CKBD3_F6 | 2fh | F6 | CKBD2_F6 | 0bh |
CKBD3_F7 | 37h | F7 | CKBD2_F7 | 83h |
CKBD3_F8 | 3fh | F8 | CKBD2_F8 | 0ah |
CKBD3_F9 | 47h | F9 | CKBD2_F9 | 01h |
CKBD3_F10 | 4fh | F10 | CKBD2_F10 | 09h |
CKBD3_F11 | 56h | F11 | CKBD2_F11 | 78h |
CKBD3_F12 | 5eh | F12 | CKBD2_F12 | 07h |
CKBD3_PrScr | 57h | Print Screen | CKBD2_PrScr | 0e012e07ch |
CKBD3_Pause | 62h | Pause | CKBD2_Pause | 0e11477h |
Примечание. Согласно [1] клавиша Pause работает только на нажатие. На моей клавиатуре в режиме 3 она программируется также, как и остальные клавиши (т. е. нажатие, нажатие/отжатие, нажатие/отжатие/повторение и т. д.)
Коды клавиш в режиме 3
Обозначение | Код в режиме 3 | Клавиша | Обозначение | Код в режиме 2 |
---|---|---|---|---|
CKBD3_Lwin | 8bh | Левая клавиша Windows | CKBD2_LWin | 0e01fh |
CKBD3_Rwin | 8ch | Правая клавиша Windows | CKBD2_RWin | 0e027h |
CKBD3_WinMenu | 8dh | Клавиша меню Windows | CKBD2_WinMenu | 0e02fh |
Клавиши WakeUp, Sleep и Power в режиме 3 на моей клавиатуре не опознаются | Клавиша WakeUp | CKBD2_WakeUp | 0e05eh | |
Клавиша Sleep | CKBD2_Sleep | 0e03fh | ||
Клавиша Power | CKBD2_Power | 0e037h |
Дополнительные коды, возвращаемые клавиатурой при нажатии и отпускании клавиш.
О работе ПК на примере Windows 10 и клавиатуры ч.2
В этой части мы рассмотрим какой путь проходит информация о нажатой клавише от клавиатуры до CPU, будет очень много картинок и это не последняя часть. Я буду рассказывать об этом с точки зрения программиста который пишет в режиме пользователя — пользовательские програмы, web, мобильные приложения — поэтому здесь могут быть неточности. Люди занимающиеся электроникой навряд ли найдут для себя что-то полезное. Первая часть находится здесь.
Под катом трафик.
Клавиша клавиатуры представляет из себя кнопку, которая замыкает контакты и через них проходит электрический ток. Механизм замыкания/размыкания сделан таким образом чтобы кнопку не приходилось вдавливать до конца, потому как иначе пальцы будут быстро уставать и клавиатура будет неэргономичной. В разрезе клавиша выглядит так. В правой части находится контакт на который подаётся напряжение.
Через программу на каждую ножку можно установить либо замерить напряжение. Алгоритм нахождения нажатой клавиши заключается в том чтобы подавать напряжение на одну ножку и замерять его на другой и если клавиша была нажата, то на второй ножке будет примерно столько же вольт сколько на входной. Так в бесконечном цикле проверяются все клавиши. Как правило клавиатура имеет 80-110 кнопок, а у контроллера контактов (пинов) гораздо меньше. Поэтому используют такой подход как “клавиатурная матрица” — все клавиши распределяют по столбцам и строкам и алгоритм сводится к нахождению столбца который пересекается со строкой на которую программа подала напряжение. Здесь отмечено цветом соответствие столбцов/строк контактам.
В реальности матрица может выглядеть так. Слева промышленная и справа самодельная.
На псевдокоде часть программы прошивки определяющая нажатые клавиши может выглядеть следующим образом.
Каждой клавише соответствует скан код, он стандартизирован и представляет из себя 8ми битное число, т.е. один байт. Поэтому когда нажата клавиша Y контроллер клавиатуры должен будет отправить число 21 (0x15), а когда отпущена — 149 (0x95). Каким образом происходит отправка? Наверняка вы работали с JSON, веб-сервисами или отправляли данные между процессами и знаете что для отправки данные надо сериализовать, т.е. превратить в массив байт или отформатированный текст, которые получатель может десериализовать, т.е. воссоздать объект в своём адресном пространстве. А во что можно сериализовать данные на таком низком уровне? Нам нужно передать всего лишь 1 байт (8 бит). Забегая вперёд скажу, что данные мы будем передавать побитно.
В математике есть формула которая может преобразовать любое привычное нам десятичное число в последовательность нулей и единиц и обратно. Этой формуле нашлось применение в вычислительной технике. В первой части я вскользь упомянул, что аналоговая техника эксплуатирует законы физики, в то время как цифровая работает на уровне нулей и единиц. Это означает, что аналоговый телефон кодирует весь спектр человеческого голоса в последовательность электромагнитных волн, а цифровой телефон использует микросхему которая преобразовывает человеческий голос в цифровые данные, к примеру в файлы в формате WAV, а потом передаёт их последовательностью нулей и единиц в виде электромагнитных волн. Только в данном случае вместо всего спектра голоса надо представлять только два значения — 0 и 1. Представлять их можно волнами разной длины, разным напряжением, световыми импульсами через оптоволокно, черными и белыми полосками на бумаге, дырками на перфокарте.
Скан код нажатой клавиши Y в двоичном виде выглядит как 0001 0101. Передавать мы их будем по ножке микроконтроллера которая отвечает за данные (DATA). Логическая единица — это напряжение 3.3В и выше, логический ноль — напряжение около 0В. Здесь возникает загвоздка — как передать три нуля подряд? Для этого нам нужна вторая ножка которую назовём CLOCK, когда на ней единичка это значит что сеанс передачи одного бита начался, а ноль — закончился. Такая перемена значений (напряжений) будет происходить с определённым интервалом времени, скажем 50 наносекунд, потому что на другом конце находится второй микроконтроллер который работает со своей скоростью и в бесконечном цикле слушает ножки к которым подключены CLOCK и DATA. В данном примере я буду исходить из того что клавиатура подключается через разъём PS2, который показан ниже. Через USB порт алгоритм передачи будет другим. Как видите у порта PS2 есть пины которые называются Data, Clock. Помимо них есть ещё контакт по которому контроллер порта PS2 раздаёт клавиатуре напряжение 5В необходимое для работы и контакт заземления, который просто выводится на корпус клавиатуры. Остальные контакты не используются.
Порт PS/2 называется последовательным портом (serial bus), потому как передаёт биты один за другим (последовательность битов). Паралельный порт передаёт данные сразу по нескольким контактам и может за один сеанс передачи данных передать к примеру сразу один байт (8 бит).
В чём разница между портом, шиной (bus) и протоколом? Шина как и порт это набор контактов (проводков) и соглашение как их использовать, только порт имеет соединение для подключения внешних устройств, а шина используется для общения компонентов на материнской плате. Порт это по сути шина с разъёмом по середине. Протокол — это порядок взаимодействия через контакты. В примере с PS/2 это был порядок передачи данных через контакты Clock и Data.
Раньше микроконтроллер Intel 8042 был очень распространённым и использовался как в клавиатуре, так и в качестве контроллера порта PS2, т.е. данными обменивались два одинаковых чипа. Драйвер порта PS2 в Windows называется 8042prt.sys.
На самом деле мы передаём не 8 бит, а 11, потому что данные передаются в виде пакета данных или же сообщения. Дополнительные 3 бита обозначают начало и конец данных — один нолик в начале и 0 1 в конце, такой протокол передачи данных от устройства хосту в PS2. Так может выглядеть функция SendKey в псевдокоде, если вам удобнее понимать код. Она отправляет данные о нажатой клавише через шину PS2.
Не всегда для передачи данных нужно вручную устанавливать напряжение на каждой ножке индивидуально. В некоторых случаях значение сохранённое в регистре автоматически отображается на контакты.
На графике такая передача данных будет визуализирована следующим образом. По оси X время, по Y — напряжение.
В аналоговой технике сигнал может искажаться, т.е. лежит провод ни к чему не подключённый, но вольтметр показывает на нём 0.5В из-за того что рядом есть электромагнитное поле. Поэтому используется понятие порогового напряжения. Если напряжение меньше порогового, то считаем что получили логический ноль, иначе единичка. С учётом возможных искажений скан-код нажатой клавиши Y может прийти вот таким:
Прежде чем мы подробно рассмотрим как данные от клавиатуры добираются до CPU давайте поговорим о микросхемах, шинах и материнских платах.
Микроконтроллеры и микросхемы
Микроконтроллер может выполнять вшитую в него программу, имеет некоторый объём RAM памяти и место для хранения данных и кода программы. В микросхему программу можно задать ещё на этапе проектирования. Вручную создавать электрическую схему реализующую алгоритм очень трудоёмко и поэтому для проектирования микросхем может использоваться специальный язык программирования который называется VHDL (Hardware Description Language). Это высокоуровневый язык программирования который транслируется в план электрической схемы, она прогоняется через программу находящую оптимальное расположение радиоэлементов на плате и в конечном счёте производится в физическом виде. Изображения носят иллюстративный характер.
Каким же образом данные и команды представлены в микросхемах и микроконтроллерах? Основой вычислительной техники является транзистор, который человечество научилось делать в микроскопических размерах. Транзистор это такой радиоэлемент у которого есть три ножки: вход, выход и между ними управляющая, которая открывает или закрывает ток между двумя ножками. Рисунок ниже иллюстрирует работу транзистора, вода иллюстрирует ток.
Т.е. одна лампочка представляет один бит информации (0 или 1), а восемь таких лампочек соответствуют одному байту. На транзисторах можно строить и логические операторы И, ИЛИ, НЕ, XOR.
К примеру в схеме оператора AND (слева на картинке выше) на выходе будет напряжение только если оба входных напряжения ненулевые. Есть уже кем-то придуманные алгоритмы сложения, умножения, деления, вычитания основанные на побитовых логических операциях и битовых сдвигах. Производителям микросхем надо их просто реализовать. Ниже проиллюстрирована работа алгоритма побитового сложения, разбирать мы его не будем:
Нанотранзисторы микроскопические и их можно размещать на плате миллионами. Ниже изображён процессор Intel и как примерно может выглядеть одно из его ядер. Картинка носит иллюстративный характер.
Микросхемы могут содержать на той же плате и в том же корпусе и микроконтроллер.
Обычно в учебниках шины показывают в виде жирных стрелок, как на картинке ниже. Это делается чтобы не рисовать все соединения контактов, которых может быть много. Работа шины PS2 очень простая, там всего нужно три контакта. Но есть шины у которых к примеру 124 контакта для передачи данных.
Шина может состоять из подшин, т.е. одни контакты используются для данных, другие для адресов, третьи для управления и контакты по которым передаётся питание. Подход когда одни и те же контакты используются попеременно для передачи и данных и адресов называется мультиплексированием. К примеру процессор Intel 8086 имеет шину данных и адресов 20 бит, на диаграмме пинов её контакты обозначены AD0-AD19 (ножки 16-2 и 39-35).
В более сложном случае у нас могут быть несколько микросхем подключенных к тем же контактам. Для нормального общения им нужен дополнительный чип, который будет определять кто в какой момент времени может их использовать, он называется контроллер шины. На рисунке ниже сферическая шина в вакууме: четыре одинаковых микроконтроллера передают данные микроконтроллеру-потребителю через контроллер шины. Красный провод — напряжение, которое контроллер шины раздаёт всем подключенным к нему чипам. По зелёным проводам передаются данные и производится “договаривание” с контроллером шины и синий провод это Clock, по которому контроллер шины синхронизирует общение контроллеров, потому как они могут работать с разной скоростью. Если на синем проводе логическая единица, то чип имеющий право на пользование шиной может выполнить один акт взаимодействия со внешним миром — прочитать бит например.
Чипсет — это набор микросхем, которые все были созданы для работы друг с другом. Они обеспечивают коммуникацию компонентов на материнской плате и предоставляют функциональность, например таймеры. Чип-сет работает только с одной маркой процессоров, AMD нельзя вставить в материнку с чипсетом Intel, у них даже контакты разные. Схема материнской платы представлена ниже:
Хотите пример хардверной инкапсуляции? В чипсетах фирмы Intel имеется чип под названием Super IO, он представлен на картинке ниже и через шину LPC подключен к Южному мосту. LPC — умное название проводков CLOCK, DATA, VCC (POWER). Этот чип содержит в себе эмуляцию всех старых чипов которые когда-либо использовались для периферийных устройств, в том числе и чип 8042 который использовался для PS2 порта. Там же находится и эмулятор контроллера порта для Floppy и прочие реликты которые мешают прогрессу. На общей схеме материнской платы выше указаны и Super IO и шина LPC.
Современный порт PS2 напрямую подключается к чипу Super I/O. Зелёное — клавиатура, фиолетовый — мышка. Раньше он подключался к микроконтроллеру Intel 8042.
Материнская плата выполнена из диэлектрика, т.е. материала который не проводит ток. Ток может проходить только про пропечатанным на плате магистралям. Материнская плата имеет множество слоёв, на каждом из которых пропечатаны свои контакты и поэтому если просверлить материнку там где магистралей не видно её можно испортить повредив невидимые контакты внутри платы. Теперь можно детально рассмотреть процесс распространения данных от PS2 к CPU.
Дорога от PS2 к процессору
Как правило архитектуру компьютера рассматривают на процессоре 8086. С одной стороны это правильно, потому как он достаточно простой по сравнению с современными CPU, с другой стороны неправильно, потому что он старый и не отражает архитектуру современной машины. Intel 8086 не нужны были никакие мосты, потому что он был настолько медленный что мог работать с периферией на одной шине, т.е. на одной частоте. Я плохо знаю современные CPU и чип сеты, поэтому буду объяснять на выдуманных, которые напоминают реальные. В моём примере будет вымышленный CPU сильно похожий на Intel 8086. У Super IO чипа больше ста контактов и по ним есть документация в Интернете, но я не вижу смысла разбирать какие пины используются клавиатурой и LPC-шиной для общения с South Bridge на самом деле. Главное это принцип, который может быть реализован по-разному.
Давайте быстренько посмотрим на картинку чтобы вспомнить что мы уже прошли. Зелёные стрелки показывают путь который мы рассмотрим.
Итак данные от клавиатуры уже пришли в контроллер порта PS2, который когда-то был чипом Intel 8042, а теперь эмулируется чипом Super IO. А теперь давайте разбирать дальнейший ход действий на моей выдуманной материнке с выдуманным CPU. Контроллер PS2 получил скан код нажатой клавишы Y и теперь подаёт напряжение на контакт сигнал (фиолетовая линия, см картинку ниже) на котором должен уведомить программируемый контроллер прерываний о данных с клавиатуры. Этот сигнал передаётся от одного чипа к другому пока Северный мост не передаст его чипу управляющему прерываниями.
Programmable Interrupt Controller представляет из себя чип Intel 8259 у которого 8 ножек (их имена IRQ0-IRQ7) зарезервированы для получения уведомлений от определённых портов (Interrupt ReQuest). На пин IRQ1 подвязана клавиатура, IRQ7 — принтер, на какой-то пин Floppy диск, звуковая карта, параллельные порты и другие. Конечно устройств может гораздо больше восьми, поэтому применялся такой приём как каскадирование, когда к ножке с именем IRQ2 подключался другой такой же PIC, у которого отсчёт начинался не с 0, а 7. Мышка привязана к IRQ12, т.е. ножка IRQ5 на втором PIC.
На самом деле таблица векторов прерываний содержит больше данных чем просто указатель на функцию. В этом массиве хранятся данные такого типа. Для простоты будем думать, что в IDT хранятся указатели на функцию или в терминах C# делегаты.
Более подробно ознакомится с тем как настраивается таблица векторов прерываний можно на osdev.
Теперь мы знаем о том как произошло прерывание, но не знаем как обработчик прерываний считывает информацию о нажатой клавише. С программной точки зрения порт PS2 представляет собой два регистра, только обращение к ним происходит не по именам или адресам в памяти о по номеру порта ввода/вывода. Эти два однобайтовых регистра закреплены за портами 0x60 и 0x64, в первом (0x60) будет лежать скан-код клавиши. Второй порт используется для передачи статуса и комманд порту PS2 (не клавиатуре!). В наборе инструкций архитектуры x86 есть команда IN storeTo, fromPortNum, которая считывает значение из указанного I/O port в указанный регистр. Например IN AL, 0x60 сохранит данные с клавиатуры в регистр AL. Она может работать примерно так:
Как вы теперь понимаете чтение с внешних устройств, даже таких как память RAM с т.зр. CPU достаточно медленное. Эту медлительность можно заметить написав программу которая печатает 10 000 строчек в файл построчно, вместо того чтобы скопить их в буфере и сохранить сразу. Жёсткий диск подключен к Южному мосту и внутри него так же есть контроллер управляющим непосредственным размещением данных.
Оперативная память подключается к CPU через шину и чтение с неё занимает некоторое время. Для ускорения работы CPU у него имеется кэш, т.е. область в которой расположены транзисторы представляющие данные которые скоро понадобятся или часто используются, их чтение происходит гораздо быстрее чем из платы RAM, которая общается с CPU через Северный мост. Оперативная память называется Dynamic Random Access Memory, потому как для представления данных в ней используются конденсаторы. Конденсатор это радиоэлемент который как аккумулятор держит некоторое время заряд пока полностью не разрядится. Только здесь разрядка происходит очень быстро. Поэтому конденсаторы надо перезаряжать, это происходит моментально, достаточно подать напряжение. Заряженный конденсатор — логическая 1, иначе 0. Для памяти кэша используется Static RAM, т.е. её не надо перезаряжать и поэтому она работает быстрее, но стоит дороже. Кэш делится на 3 уровня, которые последовательно проверяются в процессе поиска запрошенных данных, прежде чем процессор обратиться к RAM. На старых процессорах кэш первого уровня (L1) был частью CPU и работал с ним на одной частоте, когда как L2 и L3 кэши были внешними чипами. Сейчас они все находятся на одной микросхеме с процессором. Кэш L1 самый быстрый и самый маленький по объёму памяти, L2 имеет больше памяти но медленнее. L3 самый большой кэш и самый медленный, часто его называют shared cache, потому что он хранит данные для всех ядер CPU, в то время как L1 и L2 созданы для каждого отдельного ядра.
В следующей части поговорим как Windows принимает и обрабатывает полученные данные.