Как написать драйвер для com порта

Обновлено: 08.07.2024

Тоже занимаюсь подобной темой, правда в фоновом режиме поэтому больших успехов нет.
Но есть несколько соображений подкрепленных экспериментами.

Во первых о драйвере COM порта. Совсем не обязательно подключать его к стеку. Достаточно написать обыкновенный Legacy драйвер с именем например COM10. Правда его не будет видно в списке оборудования рядом с другими COM портами, но это не так страшно. Ну и далее реализуются ReadFile, WriteFile и т.д. в DDK на эту тему подробно написано.

Для работы с Ethernet, опять таки судя по DDk, из режима ядра используется TDI интерфейс, надо только написать клиента и подцепиться.

А приложение свое или чужое? Если свое, то можно написать legacy NT драйвер, поставть его в стек сетевухи, создать именованое устройство (например тот же COM10) и пусть приложение делает ввод/вывод с ним. Если же прога левая, то ИМХО лучше фильтр-драйвер для COM портов написать, чтобы он перенаправлял данные в сеть. Только здесь надо чтобы фильтр смог как-нибудь идентифицировать прогу, дабы данные от других приложений, предназначенные именно для COM порта, проходили сквозь него свободно.

Что такое "обыкновенный Legacy драйвер"? Натыкался в DDK на это слово, но ни подробного описания ни примеров не нашел. Интуитивно понятно, что это более древняя модель без PnP. Видимо, объект устройства создается внутри DriverEntry. А чем оно структурно отличается от WDM или где почитать? Еще интересует процедура динамического создания такого порта и его удаления. TDI буду пробовать.

to kelz:
Какая разница чье приложение? И про фильтр не понял. Нужно виртуальные порты создавать для работы, а не настоящие фильтровать.

Приложение должно работать с ним как с обычным портом. Реальное устройство подключено по Ethernet.

Что понимается под "работать как с обычным потртом"? Юзать Read/WriteFile? Нафига тогда усложнять себе жизнь? Если прога твоя, то ей пофигу какое устроство открывать COM1,2,3,4,5. или MySuperMegaEthernetDevice, верно? Тогда legacy драйвер, создавший вышеупомянутую символическую ссылку, должен формировать пакет и отправлять его "куда следует".

Другое дело, если прога стороннего производителя, и доступа к исходникам нет. И открывать она умеет только реальные COM порты. Тогда и нужен фильтр. Но как я покумекал - у тебя скорее первый вариант.

И про legacy: Книгу Солдатова читал? Там довольно доступно написано. Есть даже эктронный вариант в виде chm файла

Насчет динамического создания или удаления портов это лучше WDM драйвер написать.
За основу кстати можно взять пример Toaster из DDK.

Другое дело, если прога стороннего производителя, и доступа к исходникам нет. И открывать она умеет только реальные COM порты. Тогда и нужен фильтр.

Да есть программы которые могут работать только с существующими портами, но 90% программ этого не делают, и спокойно работают с любым драйвером.

Кстати мысль, а как в фильтре определить что зацепилась нужная тебе программа?

Микроконтроллеры 1986ВЕ1Т, 1986ВЕ3Т, 1986ВЕ9х и 1901ВЦ1Т имеют контроллер USB-интерфейса, реализующий функции контроллера ведомого устройства (Device) и ведущего устройства (Host). В составе библиотеки SPL приведён пример для ведомого устройства (Device) под названием "VCOM_Echo", который позволяет использовать МК в качестве CDC USB устройства (Класс Коммуникационного Устройства), настроенного как виртуальный COM-порт. Чтобы ПК определял МК именно как устройство, подключенное по COM-порту, необходимо установить специальный драйвер. Ссылка на скачивание драйвера доступна в конце статьи.

В данной статье рассмотрена пошаговая установка драйвера Virtual COM порт в ОС Windows 7.

Установка драйвера

Для установки драйвера виртуального COM-порта необходимо иметь права администратора!

Загружаем в МК проект "VCOM_Echo" и подключаем отладочную плату к ПК с помощью кабеля USB A-B. На ПК открываем "Диспетчер устройств Windows", его можно найти в поиске меню "Пуск". Если драйвер не установлен, то после подключения МК по USB в диспетчере устройств появится "Неизвестное устройство". Открываем "свойства" нового устройства, как показано на рисунке 1.


Рисунок 1 - Диспетчер устройств Windows: неизвестное устройство Если МК определился в группе "Контроллеры USB" и отображается как "Unknown Device", вероятно был нарушен протокол обмена по USB, и проблему следует искать в ПО МК.

В открывшемся окне "Свойства. " выбираем пункт "Изменить параметры" (рисунок 2), при этом возможно потребуется ввести пароль администратора.


В новом окне выбираем пункт "Обновить драйвер", как показано на рисунке 3.


Здесь необходимо выбрать "поиск драйверов на компьютере" (рисунок 4).


Теперь необходимо разархивировать папку с драйверами в любую директорию на ПК, например, в корень диска С, после чего в открывшемся окне нужно указать путь к папке с драйвером и нажать кнопку "Далее" (рисунок 5).


Рисунок 5 - Окно "Обновление драйверов": выбор директории с драйвером

В процессе установки Windows может вывести предупреждение, в котором необходимо выбрать пункт "Всё равно установить этот драйвер", как показано на рисунке 6.


Рисунок 6 - Окно с предупреждением при установке драйвера

После установки драйвера в диспетчере устройств во вкладке "Порты(COM и LPT)" должно появиться новое устройство "Milandr USB RS-232 Emulation Driver" (рисунок 7).


Рисунок 7 - Диспетчер устройств Windows: новое COM-устройство

Если установить всё-таки не получается, то

Можно попробовать запустить операционную систему Windows в безопасном режиме или режиме с отключенной обязательной проверкой подписи драйверов.

Для Windows 7 при запуске компьютера необходимо быстро нажимать клавишу F8, после чего появится окно аналогично тому, что представлено на рисунке 8. После этого выбрать либо безопасный режим, либо режим с отключенной обязательной проверкой подписи драйверов и попробовать установить драйвер снова.

Рисунок 8 - Дополнительные варианты загрузки Windows 7

Для Windows 8 и 10 есть несколько способов перейти в аналогичное Windows 7 меню выбора режима загрузки, но самый простой заключается в том, чтобы нажать кнопку "Перезагрузка" вместе с зажатой кнопкой Shift прямо в меню "Пуск" (рисунок 9).

Рисунок 9 - Алгоритм попадания в первоначальный загрузчик Windows

После перезагрузки всплывёт полностью синий экран с различными вариантами загрузки операционной системы, необходимо перейти в меню "Поиск и устранение неисправностей" > "Дополнительный параметры" > "Параметры загрузки". После этого всплывёт окно с выбором режима загрузки операционной системы, как на рисунке 10.


Рисунок 10 - Меню выбора режимов загрузки Windows 8 (10)

Соответственно, также необходимо выбрать либо безопасный режим, либо режим с отключенной обязательной проверкой подписи драйверов и попробовать установить драйвер снова.

Драйвер-это основа взаимодействия системы с устройством в ОС Windows.Это одновременно удобно и неудобно.
Про удобства я разъяснять не буду - это и так понятно,
а заострюсь я именно на неудобствах драйверов.
В сложившейся ситуации пользователь полностью подчинён воле производителя
- выпусти тот драйвер - хорошо, а не выпустит.
Только продвинутый пользователь, имеющий голову на плечах
(особенно, если он ешё и программер) не станет мириться с таким положением дел
- он просто возьмёт и сам напишет нужный драйвер.
Это нужно и взломщику: драйвер - это удобное окошко в ring0,
которое является раем для хакера. Но хоть написать драйвер и просто,
да не совсем - есть масса подводных камней. Да и документированность данного вопроса на русском языке оставляет желать лучшего.
Этот цикл статей поможет тебе во всём разобраться.
Приступим.

Хочу сразу же сделать несколько предупреждений.
Данная статья всё-таки подразумевает определённый уровень подготовки.
Драйвера-то ведь пишутся на C(++) с большим количеством ассемблерных вставок.
Поэтому хорошее знание обоих языков весьма желательно (если не сказать - обязательно).
Если же ты пока не можешь этим похвастаться,
но желание писать драйвера есть - что ж, так как эта статья вводная, в конце её будет приведён список полезной литературы,
ссылок и т.д. Но помни: учить тебя в этом цикле статей программированию как таковому я тебя не буду.
Может как-нибудь в другой раз. Согласен? Тогда поехали!


Скоро здесь, возможно, будет стоять твоё имя.

Практически в любом деле, как мне кажется, нужно начинать с теории.
Вот и начнём с неё. Для начала уясним себе поточнее основные понятия.
Первое: что есть драйвер? Драйвер - в сущности
кусок кода ОС, отвечающий за взаимодействие с аппаратурой.
Слово "аппаратура" в данном контексте следует понимать в самом широком смысле.
С момента своего появления как такого до сегодняшнего дня драйвер беспрерывно эволюционировал.
Вот, скажем, один из моментов его развития. Как отдельный и довольно независимый модуль драйвер сформировался не сразу.
Да и сейчас этот процесс до конца не завершён:
ты наверняка сталкивался с тем, что во многих
дистрибутивах никсов для установки/перестановки etc драйверов нужно перекомпилировать ядро,
т.е. фактически заново пересобирать систему.
Вот, кстати ещё один близкий моментец: разные принципы работы с драйверами в Windows 9x и NT.
В первом процесс установки/переустановки драйверов проходит практически без проблем,
во втором же случае это тяжёлое и неблагодарное дело,
для "благополучного" завершения которого нередко приходится прибегать к полной переустановке ОС.
А зато в Windows 9x. так,стоп,открывается широкая и волнующая тема,
которая уведёт меня далеко от темы нынешней статьи,
так что вернёмся к нашим баранам. ой,то есть к драйверам.
В порядке общего развития интересно сравнить особенности драйверов в Windows и *nix(xBSD) системах:

1) Способ работы с драйверами как файлами (подробнее см. ниже)
2) Драйвер, как легко заменяемая честь ОС (учитывая уже сказанные выше примечания)
3) Существование режима ядра

Теперь касательно первого пункта. Это значит,
что функции, используемые при взаимодействии с файлами,
как и с драйверами, практически идентичные (имеется в виду лексически):
open, close, read и т.д. И напоследок стоит отметить идентичность механизма
IOCTL (Input/Output Control Code-код управления вводом-выводом)
-запросов.

Драйвера под Windows делятся на два типа:
Legacy (устаревший) и WDM (PnP). Legacy драйверы (иначе называемые "драйверы в стиле
NT") чрезвычайно криво работают (если работают вообще)
под Windows 98, не работают с PnP устройствами, но зато могут пользоваться старыми функциями
HalGetBusData, HalGetInterruptVector etc, но при этом не имеют поддержки в лице шинных драйверов.
Как видишь, весьма средненький драйвер. То ли дело
WDM: главный плюс - поддержка PnP и приличненькая совместимость:
Windows 98, Me, 2000, XP, 2003, Server 2003 и т.д. с вариациями; но он тоже вынужден за это расплачиваться:
например, он не поддерживает некоторые устаревшие функции
(которые всё таки могут быть полезны). В любом случае,
не нужно ничего воспринимать как аксиому, везде бывают свои исключения.
В некоторых случаях лучше написания Legacy драйвера ничего не придумать.

Как ты наверняка знаешь, в Windows есть два мода работы:
User Mode и Kernel Mode - пользовательский режим и режим ядра соответственно.
Первый - непривилегированный, а второй - наоборот.
Вот во втором чаще всего и сидят драйвера (тем
более, что мы в данный момент говорим именно о драйверах режима ядра).
Главные различия между ними: это доступность всяких привилегированных команд процессора.
Программировать (а уж тем более качественно) в Kernel mode посложнее будет,
чем писать прикладные незамысловатые проги.
А драйвера писать без хорошего знания Kernel mode - никак.
Нужно попариться над назначением выполнения разнообразных работ отдельному подходящему уровню IRQL, желательно выучить новое API (так как в Kernel mode API отличается от прикладного).
в общем, предстоит много всяких радостей. Но тем не менее,
это очень интересно, познавательно, и даёт тебе совершенно иной уровень власти над компьютером.

А раз уж я упомянула про IRQL, разьясню и это понятие.
IRQL (Interrupt Request Level - уровень приоритета выполнения) - это приоритеты,
назначаемые специально для кода, работающего в режиме ядра.
Самый низкий уровень выполнения - PASSIVE_LEVEl. Работающий поток может быть прерван потоком только с более высоким
IRQL.

Ну и напоследок разъясним ещё несколько терминов:

1) ISR (Interrupt Service Routine) - процедура обслуживания прерываний.
Эта функция вызывается драйвером в тот момент,
когда обслуживаемая им аппаратура посылает сигнал прерывания.
Делает самые необходимые на первый момент вещи:
регистрирует callback - функцию и т.д.

2) DpcForISR (Deferred Procedure Call for ISR) - процедура отложенного вызова для обслуживания прерываний.
Эту функцию драйвер регистрирует в момент работы ISR для выполнения основной работы.

3) IRP (Input/Output Request Packet) - пакет запроса на ввод - вывод.
Пакет IRP состоит из фиксированной и изменяющейся частей.
Вторая носит название стека IRP или стека ввода - вывода (IO stack).

4) IO stack location - стек ввода - вывода в пакете IRP.

5) Dispatch Routines (Рабочие процедуры) - эти функции регистрируются в самой первой (по вызову) процедуре драйвера.

6) Major IRP Code - старший код IRP пакета.

7) Minor IRP Code - соответственно, младший код IRP пакета.

8) DriverEntry - эта функция драйвера будет вызвана первой при его загрузке.

9) Layering (Многослойность) - данной возможностью обладают только WDM - драйвера.
Она заключается в наличии реализации стекового соединения между драйверами.
Что такое стековое соединение? Для этого необходимо знать про Device
Stack (стек драйверов) - поэтому я обязательно вспомню про всё это чуточку ниже.

10) Device Stack, Driver Stack (стек устройств, стек драйверов) - всего лишь
объемное дерево устройств. Его, кстати, можно рассмотреть во всех подробностях с помощью программы
DeviceTree (из MS DDK), например.

11) Стековое соединение - как и обещала, объясняю. В стеке драйверов самый верхний драйвер - подключившийся позднее.
Он имеет возможность посылать/переадресовывать IRP запросы другим драйверам,
которые находятся ниже его. Воти всё. Правда,просто?

12) AddDevice - функция, которую обязательно должны поддерживать WDM драйверы.
Её название говорит само за себя.

13) Device Object, PDO, FDO (Объект устройства, физический,
функциональный) - при подключении устройства к шине она создаёт PDO.
А уже к PDO будут подключаться FDO объекты WDM драйверов.
Обьект FDO создаётся самим драйвером устройства при помощи функции IOCreateDevice.
Обьект FDO также может иметь свою символическую ссылку, от которой он будет получать запросы от драйвера.
Это что касается WDM драйверов. С драйверами "в стиле NT" ситуация несколько иная.
Если он не обслуживает реальных/PnP устройств,
то PDO не создаётся. Но для связи с внешним миром без FDO не обойтись.
Поэтому он присутствует и тут.

14) Device Extension (Расширение обьекта устройства) - "авторская" структура,
т.е. она полностью определяется разработчиком драйвера.
Правилом хорошего тона считается, например,
размещать в ней глобальные переменные.

15) Monolithic Driver (Монолитный драйвер) - это драйвер,
который самостоятельно обрабатывает все поступающие
IRP пакеты и сам работает с обслуживаемым им устройством
(в стеке драйверов он не состоит). Данный тип драйверов используется только если обслуживается не
PnР устройство или же всего лишь требуется окошко в ring0.

16) DIRQL (уровни аппаратных прерываний) -
прерывания, поступающие от реальных устройств, имеют наивысший приоритет IRQL,
поэтому для них решено было придумать специальное название
(Device IRQL).

17) Mini Driver (Мини - драйвер) - чуть меньше "полного" драйвера.
Обычно реализуется в виде DLL-ки и имеет оболочку в виде "полного" драйвера.

18) Class Driver (Классовый драйвер) - высокоуровневый драйвер,
который предоставляет поддержку класса устройств.

19) РnP Manager (PnP менеджер) - один из главных компонентов операционной системы.
Состоит из двух частей: PnP менеджера пользовательского и "ядерного" режимов.
Первый в основном взаимодействует с пользователем;
когда тому нужно, например, установить новые драйвера и т.д.
А второй управляет работой, загрузкой и т.д. драйверов.

20) Filter Driver (фильтр - драйвер) - драйверы, подключающиеся к основному драйверу либо сверху
(Upper), либо снизу (Lower). Фильтр драйверы (их может быть несколько) выполняют фильтрацию IRP пакетов.
Как правило, для основного драйвера Filter Drivers неощутимы.

21) Filter Device Object - объект устройства, создаваемый фильтр - драйвером.

22) HAL (Hardware Abstraction Layer) - слой аппаратных абстракций.
Данный слой позволяет абстрагироваться компонентам операционной системы от особенностей конкретной платформы.

23) Synchronization Objects (Обьекты синхронизации) - с помощью этих
объектов потоки корректируют и синхронизируют свою работу.

24) Device ID - идентификатор устройства.

25) DMA (Direct Memory Access) - метод обмена данными между устройством и памятью
(оперативной) в котором центральный процессор не принимает участия.

25) Polling - это особый метод программирования, при котором не устройство посылает сигналы прерывания драйверу,
а сам драйвер периодически опрашивает обслуживаемое им устройство.

26) Port Driver (Порт-драйвер) - низкоуровневый драйвер,
принимающий системные запросы. Изолирует классовые драйверы устройств от аппаратной специфики последних.

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

Краткий экскурс в архитектуру Windows NT

Наш обзор архитектуры Windows NT мы начнём с разговора об уровнях разграничения привилегий. Я уже упоминала об user и kernel mode.
Эти два понятия тесно связаны с так называемыми кольцами (не толкиеновскими ).
Их ( колец) в виде всего четыре: Ring3,2,1 и 0. Ring3 - наименее привилегированное кольцо,
в котором есть множество ограничений по работе с устройствами,
памятью и т.д. Например, в третьем кольце нельзя видеть адресное пространство других приложений без особого на то разрешения. Естественно,
трояну вирусу etc эти разрешения получить будет трудновато, так что хакеру в третьем кольце жизни никакой. В третьем кольце находится user mode. Kernel mode сидит в нулевом кольце - наивысшем уровне привилегий. В этом кольце можно всё:
смотреть адресные пространства чужих приложений без каких - либо ограничений и разрешений, по своему усмотрению поступать с любыми сетевыми пакетами, проходящими через машину, на всю жизнь скрыть какой-нибудь свой процесс или файл и т.д. и т.п. Естественно,
просто так пролезть в нулевое кольцо не получиться:
для этого тоже нужны дополнительные телодвижения. У легального драйвера с этим проблем нет:
ему дадут все необходимые API - шки, доступ ко всем нужным системным таблицам и проч. Хакерской же нечисти опять приходиться туго:
все необходимые привилегии ему приходиться "выбивать"
незаконным путём. Но это уже тема отдельной статьи, и мы к ней как-нибудь ещё вернёмся. А пока продолжим.

У тебя наверняка возник законный вопрос:
а что же сидит в первом и втором кольцах ? В том то всё и дело,
что программисты из Microsoft почему - то обошли эти уровни своим вниманием. Пользовательское ПО сидит в user mode,а всё остальное (ядро,
драйвера. ) - в kernel mode. Почему они так сделали - загадка, но нам это только на руку. А теперь разберёмся с компонентами (или, иначе говоря, слоями ) операционной системы Windows
NT.

Посмотри на схему - по ней многое можно себе уяснить. Разберём её подробнее.
С пользовательским режимом всё понятно. В kernel mode самый низкий уровень аппаратный. Дальше идёт HAL, выше - диспетчер ввода - вывода и драйвера устройств в одной связке, а также ядрышко вместе с исполнительными компонентами. О HAL я уже говорила, поэтому поподробнее поговорим об исполнительных компонентах. Что они дают? Прежде всего они приносят пользу ядру. Как ты уже наверняка уяснил себе по схеме, ядро отделено от исполнительных компонентов. Возникает вопрос:
почему ? Просто на ядре оставили только одну задачу:
просто управление потоками, а все остальные задачи (управление доступом,
памятью для процессов и т.д.) берут на себя исполнительные компоненты (еxecutive). Они реализованы по модульной схеме, но несколько компонентов её (схему) не поддерживают . Такая концепция имеет свои преимущества:
таким образом облегчается расширяемость системы. Перечислю наиболее важные исполнительные компоненты:

1) System Service Interface (Интерфейс системных служб )
2) Configuration Manager (Менеджер конфигурирования)
3) I/O Manager (Диспетчер ввода-вывода,ДВВ)
4) Virtual Memory Manager,VMM (Менеджер виртуальной памяти)
5) Local Procedure Call,LPC (Локальный процедурный вызов )
6) Process Manager (Диспетчер процессов)
7) Object Manager (Менеджер объектов)

Так как эта статья - первая в цикле, обзорная, подробнее на этом пока останавливаться не будем. В процессе практического обучения написанию драйверов, я буду разъяснять все неясные термины и понятия. А пока перейдём к API.

API (Application Programming Interface) - это интерфейс прикладного программирования. Он позволяет обращаться прикладным программам к системным сервисам через их специальные абстракции. API-интерфейсов несколько, таким образом в Windows-системах присутствуют несколько подсистем. Перечислю:

1) Подсистема Win32.
2) Подсистема VDM (Virtual DOS Machine - виртуальная ДОС - машина)
3) Подсистема POSIX (обеспечивает совместимость UNIX - программ)
4) Подсистемиа WOW (Windows on Windows). WOW 16 обеспечивает совместимость 32-х разрядной системы с 16-битными приложениями. В 64-х разрядных системах есть подсистема WOW 32,
которая обеспечивает аналогичную поддержку 32 - битных приложений.
5) Подсистема OS/2. Обеспечивает совместимость с OS/2 приложениями.

Казалось бы, всё вышеперечисленное однозначно говорит в пользу WINDOWS NT систем!
Но не всё так хорошо. Основа WINDOWS NT (имеются ввиду 32-х разрядные версии) - подсистема Win32. Приложения, заточенные под одну подсистему не могут вызывать функции другой. Все остальные (не Win32) подсистемы существуют в винде только в эмуляции и реализуются функции этих подсистем только через соответствующие функции винды. Убогость и ограниченность приложений, разработанных, скажем, для подсистемы POSIX и запущенных под винду - очевидны.
Увы.

Подсистема Win32 отвечает за графический интерфейс пользователя, за обеспечение работоспособности Win32 API и за консольный ввод - вывод. Каждой реализуемой задаче
соответствуют и свои функции: функции, отвечающие за графический фейс,
за консольный ввод - вывод (GDI - функции) и функции управления потоками,
файлами и т.д. Типы драйверов, наличествующие в Windows, я уже упоминала в разделе терминов:
монолитный драйвер, фильтр - драйвер и т.д. А раз так, то пора закругляться. Наш краткий обзор архитектуры Windows NT можно считать завершённым. Этого тебе пока хватит для общего понимания концепций Windows NT, и концепций написания драйверов под эту ось - как следствие.

Инструменты

Описать и/или упомянуть обо всех утилитах, могущих понадобиться при разработке драйверов - немыслимо. Расскажу только об общих направлениях.

Без чего нельзя обойтись ни в коем случае - это Microsoft DDK (Driver Development Kit ). К этому грандиозному пакету прилагается и обширная документация. Её ценность - вопрос спорный. Но в любом случае, хотя бы ознакомиться с первоисточником информации по написанию драйверов для Windows - обязательно. В принципе, можно компилять драйвера и в Visual Studio, но это чревато долгим и нудным копанием в солюшенах и vcproj-ектах, дабы код твоего драйвера нормально откомпилировался. В любом случае, сорцы придётся набивать в визуальной студии, т.к. в DDK не входит
нормальная IDE. Есть пакеты разработки драйверов и от третьих фирм:
WinDriver или NuMega Driver Studio, например. Но у них есть отличия от майкрософтовского базиса функций (порой довольно большие ) и многие другие мелкие неудобства. Так что DDK - лучший вариант. Если же ты хочешь писать драйвера исключительно на ассемблере, тебе подойдёт KmdKit (KernelMode Driver DevelopmentKit) для MASM32. Правда, этот вариант только для Win2k/XP.

Напоследок нельзя не упомянуть такие хорошие проги, как PE
Explorer, PE Browse Professional Explorer, и такие незаменимые, как дизассемблер IDA и лучший отладчик всех времён и народов SoftICE.

Ну вот и подошла к концу первая статья из цикла про написание драйверов под Windows. Теперь ты достаточно "подкован" по
теоретической части, так что в следующей статье мы перейдём к практике. Желаю тебе удачи в этом интереснейшем деле - написании драйверов! Да не облысеют твои пятки!

Ну вот мы и добрались до самого интересного - сейчас будем писать драйвер. Итак, приступим. Для удобства выполнения, всю последовательность действий по написанию драйвера я распишу по пунктам.

1. Создание директории проекта

Установив DDK, на Вашем компьютере в директории C:\WINDDK\2600.1106\ должны появиться файлы DDK. В этой директории создадим папку, в которой будут храниться наши проекты. Назовем ее, например, MyDrivers.

В папке MyDrivers создадим папку FirstDriver - тут будет находится наш первый проект драйвера.

2. Подготовка файлов проекта

В папке FirstDriver создайте пустой текстовый файл и переименуйте его под именем FirstDriver.c

При попытке переименовки со сменой расширения файла, появляется следующее предупреждение:

Не обращаем внимания на это предупреждение, и нажимаем Да. При этом наш файл примет вид:

Если же никакого предупреждения не было и переименованный файл так и остался текстовым с именем FirstDriver.c и расширением .txt, то в настройках своийств папки, которые можно найти в Пуск-> Настройка-> Панель управления-> Свойства паки уберите галочку напротив пункта "Скрывать расширения для зарегестрированных типов файлов". Попробуйте еще раз и все должно быть в порядке.

Теперь нам надо добавить еще два очень важных файла в наш проект, без которых драйвер нам не сделать. Они называются makefile и sources (обратите внимание, у них нет расширения). Их можно создать самим, но мы сделаем проще: скопируем готовые из какого либо примера проекта драйвера из DDK. Например, возьмем их из C:\WINDDK\2600.1106\src\general\cancel\sys\. Итак, копируем из указанной директории эти два файла и вставляем их в нашу папку проекта FirstDriver. Эти файлы управляют процессрм компиляции драйвера. Файл makefile оставляем без изменений, а вот sources надо подредактировать.

Открываем этот файл с помощью блокнота, и удаляем из него все что там есть и вставляем туда текст, представленный ниже. Сохранияем файл.

3. Код драйвера

Всю подготовительную работу мы сделали. Можно приступать к самой содержательной части - коду драйвера. Писать мы будем его на Си.

Еще раз напомню решаемую нами задачу: надо написать драйвер под Windows 2000, XP с помощью которого можно будет работать с портами компьютера (читать и писать данные) .

Рассмотрим код, представленный ниже. Серым цветом обозначены те участки кода, которые являются как бы шаблонными для большинства драйверов, а зеленым - код, который относится именно к нашему текущему драйверу.

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

Работа с USB

Сегодняшняя статья будет посвящена реализации виртуального COM-порта для микроконтроллеров STM32. Эта тема уже поднималась на нашем сайте (STM32 и USB), но в отличие от всех предыдущих экспериментов сегодня мы будем производить все настройки при помощи STM32CubeMx. Собственно, переходим к делу 🙂

Итак, начинаем с действий, ставших привычными! Создаем новый проект в STM32CubeMx, настраиваем его и выбираем микроконтроллер, для которого мы будем генерировать проект. Я буду использовать отладочную плату STM32F4-Discovery и, соответственно, контроллер STM32F407VG.

С этим никаких сложностей возникнуть не должно, все-таки мы уже много раз проделывали это в предыдущих статьях, посвященных STM32CubeMx (статьи про STM32CubeMx), поэтому давайте сформулируем задачу, которую мы будем сегодня решать.

Первым делом включим поддержку USB в нашем проекте в STM32CubeMx. Во вкладке Pinout ищем пункт USB_OTG_FS:

Использование USB в STM32CubeMx

Кроме того, задействуем внешний тактовый генератор (8 МГц), установленный на плате:

Настройка HSE.

Видим, что Cube отметил все занятые выводы микроконтроллера:

Настройка выводов контроллера для работы с USB Virtual COM Port.

Осталось выбрать режим работы USB для нашего устройства:

Настройка USB Virtual COM port.

На этом первый этап настроек проекта закончен, переходим в окно настроек тактирования (вкладка Clock Configuration). Здесь нам надо настроить использование внешнего генератора (8 МГц), а также все необходимые предделители частоты для всех шин микроконтроллера. Поскольку этому уже была посвящена целая статья (вот она), сейчас останавливаться на этом мы не будем и перейдем дальше 🙂 Приведу только финальные настройки тактирования:

Тактирование USB в STM32.

Двигаемся дальше и переходим на вкладку Configuration, где мы можем получить доступ к многочисленным настройкам непосредственно USB. Для начала заходим в настройки USB_FS, там нам нужно включить использование Vbus (вывод PA9 микроконтроллера):

Конфигурация USB OTG FS.

Здесь мы больше ничего пока менять не будем. Переходим к настройкам USB_DEVICE. В открывшемся окне мы видим идентификаторы VID и PID, используемые устройством, а также достаточно большое количество разных параметров USB. Не буду перечислять их все, их и так видно в окне Cube. Менять мы тут ничего не будем, хотя давайте просто ради тестирования зададим в качестве PRODUCT_STRING какое-нибудь наше название:

USB Virtual COM Port configuration.

В принципе на этом все, можно смело переходить к генерации проекта и исходного кода.

Виртуальный COM-порт в Windows.

Если мы зайдем в свойства устройства, то можем увидеть там значения VID и PID, которые были установлены в настройках STM32CubeMx, а кроме того измененную нами строку PRODUCT_STRING:

USB Virtual COM Port на плате STM32F4Discovery.

Никаких сомнений в правильной работе сгенерированного проекта не остается 🙂 Но это только часть задачи, которую мы сами себе поставили. Нам нужно реализовать отправку данных. А необходимые для этого функции находятся в файле usbd_cdc_if.c:

Итак, давайте в основном цикле нашей программы будем раз в секунду отправлять 8 тестовых байт. Кстати для реализации временных задержек в HAL_Driver есть функция HAL_Delay(). В качестве аргумента мы должны передавать количество миллисекунд. В общем, получаем такой код:

Теперь остается только прошить контроллер, открыть терминал и убедиться, что данные принимаются верно. Так и поступим:

Реализация приема данных

Все работает отлично, как и планировалось!

Собственно, на этой мажорной ноте мы заканчиваем сегодняшнюю статью, а вместе с ней и обсуждение реализации USB Virtual COM Port при помощи STM32CubeMx. В следующих статьях мы будем работать уже с другими режимами работы USB, так что оставайтесь на связи!

Читайте также: