Что такое разный драйвер
Обновлено: 21.11.2024
В Linux есть разные устройства, предназначенные для небольших драйверов устройств. Согласно статье, написанной Алессандро Рубини «Различные драйверы персонажей»:
Иногда людям нужно написать «небольшие» драйверы устройств для поддержки пользовательских хаков — как аппаратных, так и программных. С этой целью, а также для размещения некоторых реальных драйверов ядро Linux экспортирует интерфейс, позволяющий модулям регистрировать свои собственные небольшие драйверы. Драйвер misc был разработан для этой цели.
miscdevice объявлен как структура в файле miscdevice.h
В этом примере мы будем использовать только первые три поля.
Имя устройства может быть любым по желанию пользователя. Второстепенный номер может быть назначен пользователем статически, если он должен быть назначен пользователем динамически, значение должно быть установлено в `MISC_DYNAMIC_MINOR.
Таким образом, структура нашего устройства будет выглядеть так, как показано ниже.
Для регистрации разного устройства используется функция
Мы можем создать строку символов, в которой будут храниться данные, которые будут отправляться каждый раз, когда устройство считывается. Таким образом мы создаем функцию для заполнения данных об устройстве.
Функция инициализации устройства должна зарегистрировать устройство. В отличие от символьного устройства, нам не нужно получать старший номер для разного устройства, так как старший номер по умолчанию для разных устройств равен 10. Чтобы увидеть дополнительный номер, который будет назначаться динамически, мы добавим оператор печати для получения младшего номера.
Для этого мы создадим две файловые операции: одну для открытия устройства и одну для чтения с устройства. Таким образом, структура файловых операций будет
Функция открытия заполнит данные на устройстве, которые мы сможем прочитать с помощью функции чтения.
Функция чтения скопирует содержимое устройства в пространство пользователя.
Таким образом, полный код разного устройства будет
Сохраните приведенный выше код как misc_device_read.c
Используйте следующий make-файл для компиляции кода.
Скомпилируйте и вставьте код, используя
Чтобы увидеть дополнительный номер, назначенный устройству, используйте dmesg
Мы также можем просмотреть младший номер и узнать, успешно ли зарегистрировано устройство, заглянув в файл /proc/misc
Устройство автоматически появится в списке /dev, который мы можем просмотреть с помощью команды ls.
Эта статья является продолжением серии статей о драйверах устройств Linux и содержит обсуждение драйверов устройств Linux и их реализации. Цель этой серии — предоставить простые и практические примеры, понятные каждому. Это драйвер устройства Misc в драйвере устройства Linux — учебник по драйверу устройства Linux, часть 32.
Разное Драйвер устройства
Предпосылки
В этом учебном пособии по драйверам различных устройств мы просто использовали указанное ниже руководство в качестве справочного материала. Перед тем, как мы начнем, прочитайте приведенное ниже руководство.
Введение — Разное Драйвер устройства
Misc driver — это драйвер для различных устройств. Можно сказать, что драйверы misc — это специальные и простые драйверы символов. Вы можете написать этот разный драйвер, когда не можете классифицировать свое периферийное устройство. Это означает, что если вы не хотите использовать старший номер, вы можете написать этот разный драйвер. А также, если вы хотите написать простой драйвер, вы можете выбрать другой драйвер вместо выбора символьного драйвера.
Поэтому вы не можете сами выбрать старший номер при написании драйвера misc. Старший номер по умолчанию для всех драйверов Misc равен 10. Но вы можете выбрать младшие номера от 1 до 255. Он имеет все вызовы операций с файлами, такие как open , read , write , close и IOCTL . Это создаст файл устройства в каталоге /dev/. Это почти как водитель персонажа. Не так ли? Тогда какого черта мы используем устройства misc, и в чем разница между драйверами misc и символьными драйверами? Продолжаем читать.
Разница между драйвером персонажа и другим драйвером
- В драйвере misc старший номер будет равен 10, а младший номер удобен для пользователя. Принимая во внимание, что в символьных драйверах пользователь может выбрать свой собственный старший и младший номер, если он доступен.
- Узел устройства или файл устройства будут автоматически созданы в других драйверах. В то время как в драйверах персонажей пользователь должен создать узел устройства или файл устройства с помощью cdev_init, cdev_add, class_create и device_create.
Использование различных драйверов
- Если вы пишете символьные драйверы для простых устройств, вам также необходимо создать старший номер. В таком случае ядро должно хранить эти данные в статической таблице. Это означает, что вы в конечном итоге тратите оперативную память на простые устройства. Если вы пишете многосимвольные драйверы для нескольких простых устройств, потери оперативной памяти также увеличиваются. Чтобы избежать этого, вы можете использовать драйвер misc. Потому что в драйвере misc вам не нужно указывать старший номер, поскольку он имеет фиксированный старший номер, равный 10.
- Если вы используете другие драйверы, файл устройства будет автоматически создан по сравнению с символьным драйвером.
Надеюсь, теперь у вас все в порядке с программированием.
Другой API драйверов устройств
Чтобы создать разные драйверы, нам нужно использовать структуру miscdevice, файловые операции. После этого нам нужно зарегистрировать устройство. После того, как мы завершили операцию, мы можем отменить регистрацию устройства. Следующие API используются для создания и удаления разного устройства. Вам нужно вставить заголовочный файл include .
Разная структура устройства
Драйверы персонажей имеют структуру cdev, чтобы знать функции драйвера и другие вызовы. Таким образом, драйвер misc также имеет отдельную структуру для поддержки деталей. Структура следующая,
второстепенный>: вы можете присвоить этому элементу собственный второстепенный номер. Если вы передадите MISC_DYNAMIC_MINOR этой переменной, то драйвер misc автоматически сгенерирует младший номер и назначит его этой переменной. Каждый драйвер Misc должен иметь другой младший номер, так как это единственная связь между файлом устройства и драйвером. Рекомендуется использовать динамический метод выделения младшего номера вместо назначения одного и того же младшего номера различным разным устройствам. Если вы хотите выбрать свой собственный младший номер, проверьте используемый младший номер всех разных устройств, используя ls -l /dev/ , затем вы можете жестко закодировать свой младший номер, если он доступен.
имя>: вы можете назначить имя разного драйвера. Файл устройства будет создан с этим именем и отображен в каталоге /dev.
fops>: это указатель на файловые операции. Это та же файловая операция в драйверах персонажей.
следующий> и предыдущий>: они используются для управления круговым связанным списком различных драйверов.
После того, как вы загрузили разный драйвер, вы можете увидеть старший и дополнительный номер вашего разного драйвера устройства, используя ls -l /dev/ . См. пример ниже.
Зарегистрируйте разное устройство
разное>: регистрируемая структура устройства
return: в случае успеха возвращается ноль, а в случае неудачи - отрицательный код ошибки.
Вы должны вызвать эту функцию в функции инициализации. Передаваемая структура связывается с ядром и не может быть уничтожена до тех пор, пока не будет отменена ее регистрация.
- alloc_chrdev_region(); – используется для старшего и младшего числа
- cdev_init(); – используется для инициализации cdev
- cdev_add(); – используется для добавления структуры cdev на устройство
- class_create(); – используется для создания класса
- device_create(); – используется для создания устройства
Вот почему мы говорим, что это простой метод.
Пример
Отменить регистрацию разного устройства
разное>: структура устройства для отмены регистрации
Отменить регистрацию различного устройства, которое ранее было успешно зарегистрировано с помощью misc_register() . Это должно быть вызвано в функции __exit.
- cdev_del(); – используется для удаления cdev
- отменить регистрацию_chrdev_region(); – используется для удаления старшего и младшего номера
- device_destroy(); – используется для удаления устройства
- class_destroy(); – используется для удаления класса
Пример
Уууу. Это все. Для создания разного драйвера необходимы всего два API. Это действительно просто по сравнению с драйвером персонажа? Перейдем к программированию.
Пример программирования
Здесь я добавил фиктивный фрагмент драйвера устройства Misc. В этом коде драйвера мы можем выполнять все операции открытия, чтения, записи и закрытия. Просто просмотрите код.
Исходный код драйвера
[Получить исходный код с GitHub]
Создать файл
Выполнение
- Создайте драйвер с помощью Makefile ( sudo make )
- Загрузите драйвер с помощью sudo insmod misc_driver.ko
- Чтобы проверить старший и младший номер, используйте ls -l /dev/simple_etx_misc
- Здесь 10 — это старший номер, а 53 — младший.
- Теперь мы увидим запись и чтение. Введите sudo su и введите свой пароль, если он попросит дать разрешение.
- Выполнить эхо 1 > /dev/simple_etx_misc
Echo откроет драйвер, запишет в него 1 и, наконец, закроет драйвер. Поэтому, если я делаю эхо для нашего драйвера, он должен вызывать функции открытия, записи и освобождения (закрытия). Просто проверьте это.
- Это круто. Теперь мы проверим функцию чтения. Сделайте кот /dev/simple_etx_misc
Команда Cat откроет драйвер, прочитает драйвер и закроет драйвер. Поэтому, если я сделаю cat для нашего драйвера, он должен вызвать функции открытия, чтения и освобождения (закрытия). Просто проверьте.
- Выгрузите драйвер с помощью sudo rmmod misc_driver
Вместо выполнения команды echo и cat в терминале вы также можете использовать системные вызовы open(), read(), write(), close() из приложений пользовательского пространства.
Это просто фиктивный драйвер. Но вы также можете реализовать свою собственную логику в операциях чтения, записи и операций ввода-вывода, как мы это делали в наших предыдущих руководствах по драйверам устройств для Linux.
Разные (или прочие) драйверы — это простые драйверы символов, которые имеют определенные общие характеристики. Ядро абстрагирует эти общие черты в API (реализованном в drivers/char/misc.c), и это упрощает способ инициализации этих драйверов. Всем разным устройствам назначается старший номер 10, но каждое может выбрать один младший номер. Таким образом, если драйвер char должен управлять несколькими устройствами, как в рассмотренном ранее примере CMOS, он, вероятно, не подходит для использования в качестве универсального драйвера.
Рассмотрите последовательность шагов инициализации, которые выполняет драйвер char:
-
Выделяет старший/младший номера через alloc_chrdev_region() и друзей
Драйвер misc выполняет все это одним вызовом misc_register():
В предыдущем примере MYDRV_MINOR — это младший номер, который вы хотите статически присвоить вашему разному драйверу. Вы также можете запросить динамическое назначение дополнительного номера, указав MISC_DYNAMIC_MINOR вместо MYDRV_MINOR в структуре mydrv_dev.
Каждый драйвер misc автоматически появляется в каталоге /sys/class/misc/ без каких-либо явных усилий со стороны автора драйвера. Поскольку драйверы misc являются драйверами char, предыдущее обсуждение точек входа драйвера char справедливо и для драйверов misc. Давайте теперь посмотрим на пример разного драйвера.
Пример устройства: сторожевой таймер
Функция сторожевого таймера состоит в том, чтобы возвращать систему, которая не отвечает, в рабочее состояние. Он делает это, периодически проверяя импульс системы и выдавая сброс [4], если он не может его обнаружить. Прикладное программное обеспечение отвечает за регистрацию этого импульса (или «сердцебиения») путем периодического стробирования (или «поглаживания») сторожевого таймера с использованием услуг драйвера сторожевого устройства. Большинство встроенных контроллеров поддерживают внутренние модули сторожевого таймера. Также доступны внешние микросхемы сторожевого таймера. Примером может служить микросхема Netwinder W83977AF.
[4] Сторожевой таймер может издавать звуковые сигналы вместо перезагрузки системы. В качестве примера можно привести тайм-аут из-за проблемы с питанием, при условии, что схема сторожевого устройства резервируется с помощью батареи или суперконденсатора.
Драйверы сторожевого таймера Linux реализованы как драйверы misc и находятся внутри drivers/char/watchdog/. Драйверы сторожевого таймера, как и драйверы RTC, экспортируют стандартный интерфейс устройства в пользовательскую среду, поэтому соответствующие приложения отображаются независимо от внутреннего оборудования сторожевого таймера. Этот API указан в Documentation/watchdog/watchdog-api.txt в дереве исходного кода ядра. Программы, которым требуются услуги сторожевого таймера, работают с /dev/watchdog , узлом устройства, имеющим младший номер разного рода 130.
В листинге 5.9 реализован драйвер устройства для фиктивного модуля сторожевого таймера, встроенного во встроенный контроллер. Пример сторожевого таймера содержит два основных регистра, как показано в таблице 5.2: служебный регистр (WD_SERVICE_REGISTER) и управляющий регистр (WD_CONTROL_REGISTER). Чтобы погладить сторожевой таймер, драйвер записывает определенную последовательность (в данном случае 0xABCD) в служебный регистр. Чтобы запрограммировать тайм-аут сторожевого таймера, драйвер записывает в указанные позиции битов в управляющем регистре.
Таблица 5.2. Зарегистрируйте макет в модуле Watchdog
Стробирование сторожевого таймера обычно выполняется из пользовательского пространства, поскольку его цель – обнаруживать зависания приложения и ядра и реагировать на них. Критическое приложение [5], такое как графический движок в листинге 5.10, открывает драйвер сторожевого таймера в листинге 5.9 и периодически записывает в него данные. Если в течение тайм-аута сторожевого таймера запись не происходит из-за зависания приложения или сбоя ядра, сторожевой таймер инициирует сброс системы. В случае Листинга 5.10 сторожевой таймер перезагрузит систему, если
[5] Если вам необходимо следить за работоспособностью нескольких приложений, вы можете реализовать мультиплексор в драйвере сторожевого устройства. Если какой-либо из процессов, открывающих драйвер, перестает отвечать на запросы, сторожевой таймер пытается самостоятельно исправить систему.
Приложение зависает внутри process_graphics()
Ядро и, следовательно, приложение умирают
Сторожевой таймер начинает тикать, когда приложение открывает /dev/watchdog. Закрытие этого узла устройства останавливает сторожевой таймер, если вы не установили CONFIG_WATCHDOG_NOWAYOUT во время настройки ядра. Установка этого параметра помогает исключить вероятность того, что процесс мониторинга сторожевого таймера (такой, как в листинге 5.10) будет остановлен сигналом, в то время как система продолжает работать.
Листинг 5.9. Пример драйвера сторожевого таймера
Листинг 5.10. Пользователь Watchdog
Внешние сторожевые таймеры
Чтобы гарантировать, что система будет пытаться восстановиться даже в случае сбоя процессора, некоторые регулирующие органы предписывают использование внешнего чипа сторожевого таймера, даже если основной процессор имеет сложный встроенный модуль сторожевого таймера, такой как в нашем пример.Из-за этого требования во встраиваемых устройствах иногда используется недорогая сторожевая микросхема без излишеств (такая как MAX6730 от Maxim), основанная на простой жестко запрограммированной логике, а не на регистровом интерфейсе. Сторожевой таймер устанавливает контакт сброса, если на входном контакте не обнаруживается импульс напряжения в течение фиксированного тайм-аута сброса. Контакт сброса подключен к логике сброса процессора, а входной контакт подключен к порту GPIO процессора. Все, что программное обеспечение должно сделать для предотвращения сброса, — это периодически подавать импульс на входной контакт сторожевого устройства в течение тайм-аута сброса чипа. Если вы пишете драйвер для такого устройства, метод ioctl() не подходит. Метод драйвера write() подает импульс на входной контакт сторожевого таймера всякий раз, когда прикладное программное обеспечение записывает данные в соответствующий узел устройства. Для облегчения диагностики на производстве и в полевых условиях сторожевой таймер подключен таким образом, что его можно отключить, покачав контакт GPIO процессора.
Такие чипы обычно допускают большой начальный тайм-аут для учета времени загрузки, за которым следуют более короткие тайм-ауты сброса.
Реализовать механизм тайм-аута
Инициировать мягкую перезагрузку, если система неисправна
Это делается путем задержки выполнения обработчика таймера всякий раз, когда приложение записывает в программную программу. Если в течение тайм-аута не происходит записи в softdog, обработчик таймера срабатывает и перезагружает систему.
Связанной поддержкой в ядрах 2.6 является обнаружение программных зависаний , когда планирование не выполняется в течение 10 и более секунд. Сторожевой таймер потока ядра/N , где N — номер процессора, каждую секунду касается временной метки каждого процессора. Если поток не касается метки времени более 10 секунд, считается, что система заблокирована. Обнаружение программной блокировки (реализовано в kernel/softlockup.c) поможет нам при отладке сбоя ядра в разделе «Kdump» главы 21, «Отладка драйверов устройств».
В ядре есть еще несколько разных драйверов. Драйвер инфракрасной клавиатуры Qtronix, drivers/char/qtronix.c , является еще одним примером драйвера char, который имеет другой форм-фактор. Выполните grep для misc_register() в каталоге drivers/char/, чтобы найти другие драйверы различных устройств, присутствующие в ядре.
Иногда людям нужно написать «небольшие» драйверы устройств для поддержки пользовательских хаков — как аппаратных, так и программных. С этой целью, а также для размещения некоторых реальных драйверов ядро Linux экспортирует интерфейс, позволяющий модулям регистрировать свои собственные небольшие драйверы. Драйвер misc был разработан для этой цели. Представленный здесь код предназначен для работы с ядром Linux версии 2.0.
В UNIX, Linux и подобных операционных системах каждое устройство идентифицируется двумя номерами: «старшим» и «дополнительным». Эти числа можно увидеть, вызвав команду ls -l /dev. Каждый драйвер устройства регистрирует свой старший номер в ядре и полностью отвечает за управление своими младшими номерами. Использование любого устройства с этим старшим номером будет зависеть от одного и того же драйвера устройства, независимо от младшего номера. В результате каждому драйверу необходимо зарегистрировать старший номер, даже если он работает только с одним устройством, например с указателем.
Поскольку ядро хранит статическую таблицу драйверов устройств, легкомысленное выделение старших номеров приводит к расточительному расходу оперативной памяти. Поэтому ядро Linux предлагает упрощенный интерфейс для простых драйверов — тех, которые будут регистрировать единую точку входа. Обратите внимание, что, как правило, выгодно выделять все пространство имен старшего номера для каждого устройства. Это позволяет обрабатывать несколько терминалов, несколько последовательных портов и несколько разделов диска без каких-либо накладных расходов в самом ядре: один драйвер заботится обо всех них и использует младший номер для различения.
Младший номер 10 официально присвоен водителю. Модули могут регистрировать отдельные второстепенные номера с помощью драйвера misc и заботиться о небольшом устройстве, которому требуется только одна точка входа.
Драйвер misc экспортирует две функции для пользовательских модулей для регистрации и отмены регистрации собственного дополнительного номера:
Каждый пользовательский модуль может использовать функцию регистрации для создания собственной точки входа для дополнительного номера и отмены регистрации для освобождения ресурсов во время выгрузки.
Файл miscdevice.h также объявляет структуру miscdevice следующим образом:
Эти пять полей имеют следующее значение:
minor — это регистрируемый младший номер. Каждое другое устройство должно иметь свой младший номер, потому что такой номер является единственной связью между файлом в /dev и драйвером.
имя — это имя для этого устройства, предназначенное для человека: пользователи найдут это имя в файле /proc/misc.
следующий и предыдущий используются для управления круговым списком зарегистрированных водителей.
Код, вызывающий misc_register, должен очищать prev и next перед вызовом функции и заполнять первые три поля разумными значениями.
Настоящий вопрос, связанный с драйвером устройства Misc, заключается в следующем: «Каково разумное значение для второстепенного поля?Присвоение минорных номеров осуществляется двумя способами: либо можно использовать «официально присвоенный» номер, либо можно прибегнуть к динамическому присвоению. В последнем случае ваш драйвер запрашивает свободный дополнительный номер, и ядро возвращает его.
Типичная последовательность кода для назначения динамического дополнительного номера выглядит следующим образом:
Излишне говорить, что реальный модуль будет выполнять некоторые другие задачи внутри init_module. После успешной регистрации новое устройство Misc появится в /proc/misc. В этом информативном файле сообщается, какие разные драйверы доступны и их младшие номера. После загрузки my файл будет содержать следующую строку:
Это показывает, что 63 является младшим возвращаемым числом. Если вы хотите создать точку входа в /dev для вашего модуля misc, вы можете использовать сценарий, подобный показанному в листинге 1. Сценарий создает узел устройства и предоставляет ему желаемые разрешения и владельца.
Вы можете найти неиспользуемый дополнительный номер и запрограммировать его в своем драйвере. Это убережет от вызова скрипта для загрузки модуля, но такая практика настоятельно не рекомендуется. Чтобы код оставался компактным, drivers/char/misc.c не проверяет дублирование младших номеров. Если выбранный вами номер позже будет присвоен официальному драйверу, у вас будут проблемы при попытке доступа как к вашему модулю, так и к официальному.
Если один и тот же дополнительный номер зарегистрирован дважды, только первый номер будет доступен из пользовательского пространства. Хотя это кажется несправедливым, это не может считаться ошибкой ядра, поскольку никакая структура данных не повреждена. Если вы хотите зарегистрировать безопасный дополнительный номер, вам следует использовать динамическое распределение.
Файл Documentation/devices.txt в дереве исходного кода ядра содержит список всех официальных номеров устройств, включая все младшие номера для разного драйвера.
Если вы попытались написать свой собственный драйвер misc, но insmod вернул неразрешенный символ misc_register, у вас проблема с конфигурацией ядра.
Изначально драйвер misc был разработан как оболочка для всех драйверов «busmouse» — драйверов ядра для каждого непоследовательного указателя. Драйвер компилировался только в том случае, если текущая конфигурация включала хотя бы один такой драйвер мыши. Незадолго до версии 2.0 универсальность реализации была широко принята, и драйвер был переименован с «мыши» на «разное». Однако по-прежнему верно, что драйвер недоступен, если вы не решили скомпилировать хотя бы одно из различных устройств либо как модуль, либо как собственный драйвер.
Если в вашей системе не установлены такие устройства, вы все равно можете загрузить свои пользовательские модули Misc, если вы утвердительно ответите при настройке ядра на вопрос:
Этот параметр указывает, что драйвер misc должен быть скомпилирован, даже если не выбрано никакое другое устройство, что позволяет вставлять сторонние модули во время выполнения. Файл /proc/misc и поддержка динамических младших номеров были реализованы, когда была введена эта опция, поскольку нет смысла иметь пользовательские модули, если выделение младших номеров не является безопасным.
Обратите внимание, что если ваше ядро настроено на загрузку busmice только как модули, все будет работать, кроме /proc/misc. Файл /proc создается только в том случае, если miscdevice.c напрямую связан с ядром. CONFIG_UMISC также позаботится об этой ситуации.
Каждый раз, когда процесс взаимодействует с драйвером устройства, реализация системного вызова передает управление правильному драйверу с помощью структуры file_operations. Эта структура поддерживается структурой file : каждый дескриптор открытого файла связан с одной такой структурой, а file.f_op указывает на собственную структуру file_operations.
Эта установка похожа на объектно-ориентированные языки: каждый объект (здесь, каждый файл) объявляет, как действовать над собой, так что высокоуровневый код не зависит от фактического доступа к файлу. Ядро Linux полно объектно-ориентированного программирования в своих реализациях, и в нем существует несколько структур «операций», по одной для каждого отдельного «объекта» (иноды, области памяти и т. д.).
Вернёмся к разному драйверу. Как my_dev.fops участвует в игре? Во время открытия ядро выделяет новую файловую структуру для описания открываемого объекта и инициализирует свою структуру операций в соответствии с тем, что представляет собой файл. Сокеты, FIFO, дисковые файлы и устройства получают свои собственные, разные операции. Когда устройство открывается, его операции просматриваются в соответствии со старшим номером устройства путем обращения к массиву. Затем вызывается метод open внутри драйвера. Любой другой системный вызов, работающий с файлом, будет использовать file.f_op без проверки какого-либо другого источника информации. В результате драйвер может заменить значение file.f_op, чтобы приспособить поведение структурного файла к некоторой внутренней функции, даже если эта функция более мелкая, чем старший номер, и, следовательно, не видна из самого ядра. .
Метод open драйвера misc может отправлять операции фактическому низкоуровневому драйверу, изменяя file.f_op; назначенное значение — это значение в my_dev.f_op. После переопределения операций метод вызывает file.f_op->open(), чтобы низкоуровневый драйвер мог выполнить свою собственную инициализацию. Каждый другой системный вызов, вызванный для файла, будет использовать новое значение file.f_op, а низкоуровневый драйвер сохраняет полный контроль над своим устройством.
Поскольку обсуждение до сих пор было слишком философским, пришло время перейти к рабочему примеру. Модуль kiss (информационные сигналы состояния клавиатуры) — это простой инструмент для игры со светодиодами клавиатуры. Он регистрируется как разное устройство, используя динамическое присвоение младшего номера, и управляется путем написания ему текстовых команд. Он принимает несколько однобайтовых команд, таких как «N» и «n» (для включения и выключения светодиода Num-Lock), цифры от 0 до 7 (для отображения двоичных чисел в этом диапазоне с помощью светодиодов) и так далее. вкл.
Как обычно, пример модуля, сопровождающий эту статью, довольно прост и мало интересен в реальном мире. Однако на этот раз, я думаю, это может представлять некоторый интерес. На самом деле специальное оборудование на моем компьютере включает три светодиода для контроля количества запущенных процессов. На мой взгляд, это полезная информация, когда вы задаетесь вопросом, почему компьютер не отвечает — ситуация довольно распространенная, когда вы пишете драйверы с ошибками или драйверы, которые выводят слишком много диагностических сообщений.
Читайте также: