Оффтоп - Понимаю, что у меня был пик, а не малинка, но http://rw6hrm.qrz.ru/adapter.htm на всякий случай.
Оффтоп - Понимаю, что у меня был пик, а не малинка, но http://rw6hrm.qrz.ru/adapter.htm на всякий случай.
С любовью к вам, Yandex.Direct
Размещение рекламы на форуме способствует его дальнейшему развитию
Добрался до реализации программной части интерфейса PS/2. Относительно быстро реализовал чтение единичного кода с клавиатуры. Любопытно, что во многих исходниках игнорируется какая-либо проверка не только стартового и стопового битов, но и бита чётности. Но у себя я для надёжности все три бита проверяю на корректность. Не обошлось и без проблем, конечно.
Клавиатура у меня опрашивается 50 раз в секунду, в общем цикле без всяких прерываний и прочих штук-дрюк. Вот просто опрашиваю, и жду какое-то время нажата (или отпущена) кнопка/и или нет. Вот с этим "каким-то временем" и возникла загвоздка. Поясняю.
В процессе работы эмулятора я сбрасываю CLOCK, тем самым запрещая передачу данных с клавиатуры. Потом, когда начинается опрос клавиатуры, я поднимаю CLOCK и начинаю в цикле ждать данных. Сигналом прихода данных является сброшенный клавиатурой сигнал CLOCK. И вот у меня возникла проблема с этим временем ожидания. Время, которое проходит между поднятием CLOCK и началом передачи данных сильно варьирует! Данные могут начать приходить почти моментально, а может пройти довольно много времени. От чего это зависит, я так и не понял, но предполагаю, что данные с клавиатуры всего идут с определённой частотой. Таким образом, если ставить длительное время, то конечно, всё гарантированно прочитается, но при этом эмулятор на это время стоит, как вкопанный, и только ждёт клавиатуру. А в подавляющем большинстве случаев кнопки не нажимаются и это время я просто теряю впустую. Если ставить короткое время ожидания, то клавиши срабатывают через раз. Опытным путём с помощью осциллографа подобрал оптимальное время ожидания, примерно похожее на время передачи одного байта данных. Всё оформил и начал начал экспериментировать. Убедился, что нажатие на клавишу отрабатывается стабильно. Но до меня не сразу дошло, как читать второй байт данных, так как везде в описаниях протокола речь идёт исключительно о чтении одного байта. В исходниках подсмотрел, что чтение одного байта оформляют просто процедурой, а чтобы прочитать второй байт, эту процедуру просто вызывают нужное количество раз. Но у меня так тоже сразу не взлетело. Изначально второй байт у меня вообще никак не приходил. Я получал либо ошибку, либо ничего вообще не получал. Разобрался, что между передачей нужно выждать определённое время. Если время будет коротким, то ничего не прочитается, а если длинным, то все данные теряются, даже если предварительно передача была запрещена. Хотя и не должны, судя по описанию! Опять же, экспериментальным путём, выяснил, что повлиять на скорость следования второго и последующего байтов никак нельзя - это время жёстко задано клавиатурой и всегда одинаковое. В моём случае - это 3.8 mS. Можно сбрасывать CLOCK на какое-то время после приёма первого байта, а потом снова его поднимать, а можно сразу без опускания ожидать второго байта - время между двумя байтами всегда будет одним и тем же. А если чуть переборщить с опущенным CLOCK'ом и вовремя его не поднять, что второй байт будет потерян.
В итоге эти 3.8 mS с небольшим запасом я и взял в качестве времени ожидания поступления данных при получении одного байта. И если поставить две процедуры чтения друг за другом, то по времени всё выстраивается, как того хочет моя клавиатура.
Вот так простая на первый взгляд задача отняла кучу времени. Зато я получил хороший опыт. А осциллограф оказал неоценимую помощь.
Не знаю насколько всё правильно я реализовал. Подозреваю, что с другой клавиатурой и на другой плате всё будет работать иначе. Если будут какие-то советы, то с удовольствием приму их.
А вообще, я конечно, немного обескуражен скоростью данного интерфейса - как всё слишком медленно. И чем больше одновременно нажатых или отпущенных кнопок, тем ещё медленнее. Понимаю, что интерфейс устаревший и уже практически ушёл на покой, но тем не менее. С матричной клавиатурой по скорости он и рядом не стоял, конечно. Хотя внутри любой клавиатуры и так матрица. И даже геймпад NES опрашивается куда быстрее.
Следующим этапом у меня с нуля организовать чтение/запись SD-карты. А это, наверное, будет посложнее. Напоминаю, что пишу всё на ассемблере для процессоров ARM.
Последний раз редактировалось CityAceE; 27.10.2023 в 11:50.
С уважением, Станислав.
Только недавно в организации, которую мы обслуживаем, покупали новый компьютер HP (обычный desktop), он
комплектовался USB мышью и клавиатурой, но в кейсе была штатно установлена планка с двумя PS2 разъемами,
провода от нее подкючались к специальному разъему на материнке, помеченному как PS2.
Зачем такие сложности?
По стандарту клавиатура должна работать в режиме опроса, но на деле очень много клавиатур так не работают или работают неправильно.
Клавиатура должна сама отправлять данные когда захочет. В этом режиме 100% клавиатур работают правильно.
Выше давал ссылку на код для AVR.
Продублирую нужные фрагмент кода:
Код:// External Interrupt 0 service routine //Отрицательный фронт AT Clock ЧИТАЕМ ИЛИ ПИШЕМ interrupt [EXT_INT0] void ext_int0_isr(void) { // Place your code here delay_us(3); //Задержка, чтобы установился сигнал Data if(at_read) //Если читаем из AT { if (at_bitcount == 11) //проверяем старт-бит { at_even_parity = 0; //Обнуляем бит четности if (ADATA) at_error = YES; //нет старт-бита? Значит что-то пошло не так } else if(at_bitcount > 2) //Перекидываем значащие биты в буфер { at_shift_reg = at_shift_reg >> 1; if (ADATA) { at_shift_reg = at_shift_reg | 0x80; at_even_parity = !at_even_parity; } } else if (at_bitcount == 2) //Если бит чётности (odd parity) { if(at_even_parity==ADATA) at_error = YES; //Если бит чётности = биту НЕчётности (взаимная инверсия), то ошибка! } else //Если стоп-бит { if (ADATA==0) at_error = YES; //нет стоп-бита? Значит что-то пошло не так else //Если байт принят и всё в порядке, { at_buff = at_shift_reg; at_new_data = YES; //Буфер символа не пуст at_bitcount = 12; //Взводим счётчик битов (ниже он будет уменьшен на 1 и станет = 11) ACLK_OUT = NULL; //Опускаем Clock - подтверждение клавиатуре в приёме (не обязательное) } } } else //Если пишем в AT { if (at_bitcount >3) //Отправляем биты в AT { if(at_shift_reg & 1) { ADATA_OUT = ONE; at_even_parity = !at_even_parity; } else ADATA_OUT = NULL; at_shift_reg = at_shift_reg >> 1; } else if(at_bitcount == 3) //Отправляем бит чётности { ADATA_OUT = at_even_parity; //У нас ADATA_OUT инвертировано, но и even_parity инвертировано относительно odd parity (которое и отсылается) } else if(at_bitcount == 2) //Отправляем стоп-бит = 1. И, одновременно, включаем порт на приём { ADATA_OUT = ONE; } else // Иначе at_bitcount == 1, других вариантов быть не может)))) { if (ADATA) at_error = YES; //нет ACK (подтверждение от клавиатуры)? Значит что-то пошло не так at_read = 1; //Переводим в режим чтения at_bitcount = 12; //Взводим счетчик битов } } at_bitcount--; //Уменьшаем на 1 счётчик битов. Причём не зависимо от того, принимаем или передаём. }Код:void at_write (char a) //Пишем байт в AT { ACLK_OUT = NULL; //Тянем Clock на землю GIMSK=0; // Вырубаем прерывания от AT Clock GIFR=1<<INTF0; //Сбрасываем флаг прерывания от AT Clock (если он уже оказался установлен) delay_us(100); //Ждём 100мкс (это минимум). ADATA_OUT = NULL; //Тянем Data на землю //Готовим данные (это даёт небольшую паузу): at_shift_reg = a; //Заталкиваем в сдвиговый регистр AT передаваемый байт at_read = NO; // Сбрасываем режим чтения, значит будет запись в AT at_bitcount = 11; // Взводим счётчик битов at_error = NO; //Сбрасываем ошибку работы с AT (если она была) at_even_parity = 0; //Обнуляем бит четности ACLK_OUT = OFF; //отпускаем Clock GIMSK=(1<<INT0); // Включаем прерывание для Clock AT } //типовая процедура пропуска подтверждения операции записи в АТ клаву //(простейшее ожидание и проверка ответа "0xFA") void FAskip() //Возвращает at_error (если ошибка = 1, иначе = 0) { while (!(at_new_data | at_error)); //Пока буфер символа пуст и нет ошибки приёма, ждём (в это время могут выполняться прерывания). if (at_buff != 0xFA) at_error = YES; //проверяем ответ at_new_data = NO; } void at_init() //Инициализация AT клавиатуры { at_write (0xFF); // Сбрасываем клавиатуру FAskip(); // Пропускаем от клавиатуры символ - подтверждение приёма. ////Если произошла ошибка, выход из процедуры ACLK_OUT = OFF; while (!(at_new_data | at_error)); //Пока нет новых данных (и нет ошибки AT), ждём (в это время могут выполняться прерывания) at_new_data = NO; if(at_buff != 0xAA) at_error = YES; }
Последний раз редактировалось Rio444; 28.10.2023 в 19:15.
На самом деле ничего сложного. Делаю свои дела, когда доходит до обновления экрана, опрашиваю и клавиатуру: разрешаю передачу, проверяю, запрещаю передачу. Запрет не передачу описан во всех мануалах, которые я читал. Я так планировал сделать с самого начала, но были сомнения, а подтверждение такого подхода нашёл в исходниках, ссылку на которые дал rw6hrm.
ОК.А если программа не готова принять эти данные, то что?
Приблизительно так и делают все остальные программы. Так же сделано и у меня.
Сейчас вхолостую тратится 3.8 mS при опросе клавиатуры 50 раз в секунду. Итого из 1 секунды работы эмулятора тратится 0.000190 сек на обработку клавиатуру. Проверено совместно с эмулятором, это время не является критичным для его работы.
С уважением, Станислав.
Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)