Поделиться через


Драйвер ШИМ для модуля ШИМ в составе кристалла

Чтобы обеспечить доступ к контроллеру широтно-импульсной модуляции (PWM), который является частью SoC и сопоставлен с адресным пространством SoC, необходимо написать драйвер режима ядра. Драйвер должен зарегистрировать интерфейс класса устройства контроллера PWM, чтобы приложения UWP могли обращаться к устройствам PWM, предоставляемым системой, через API WinRT PWM, определенные в пространстве имен Windows.Devices.Pwm.

Замечание

Если у вас есть модуль PWM на базе I2C, SPI или контроллера UART, вы можете обращаться к модулю из приложения UWP с помощью API, определенных в пространстве имен Windows.Devices.Pwm и Windows.Devices.Pwm.Provider.

Устройство PWM абстрагируется в один контроллер и один или несколько выводов. Управление либо контроллером, либо пинами осуществляется через PWM-определяемые IOCTL. Например, драйвер дисплея LCD отправляет такие запросы драйверу PWM для управления уровнем заднего фонаря.

Создание сигналов PWM с несколькими контроллерами и несколькими каналами с настраиваемым периодом, полярностью и рабочим циклом, что позволяет выполнять следующие задачи:

  • Управляйте серводвигателем.
  • Управляйте нагрузкой, такой как светодиод или коллекторный двигатель постоянного тока.
  • Создайте аналоговый сигнал.
  • Создайте точные часы.

В этом разделе описано:

  • Как включить доступ UWP к устройствам PWM, предоставляемым системой.

  • Как обрабатывать запросы IOCTL PWM, отправленные приложением Win32 или сторонним драйвером режима ядра.

Целевая аудитория

  • Изготовители оборудования и интеграторы аппаратного обеспечения (IHV) разрабатывают систему с контроллером PWM, встроенным в SoC.

Последнее обновление

  • Август 2017 г.

версии Windows

  • Версия Windows 10

Основные API

О PWM

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

Волновая форма PWM может быть классифицирована по 2 параметрам: период волны (T) и цикл работы. Частота волн (f) — это обратная часть периода волны f=1/T. Коэффициент заполнения описывает пропорцию времени «включения» или «активного состояния» относительно регулярного интервала или периода времени; низкий коэффициент заполнения соответствует низкому среднему значению выходной мощности, так как питание выключено большую часть времени. Рабочий цикл выражается в процентах, где 100% означает полностью включено, 0% — полностью выключено, 50% — "активно" в течение 50% времени.

Драйверы.

Доступ к контроллеру и контактам PWM, предоставляемым системой.

Драйвер PWM должен зарегистрировать
GUID_DEVINTERFACE_PWM_CONTROLLER в качестве GUID интерфейса устройства для предоставления и доступа к устройствам PWM.

// {60824B4C-EED1-4C9C-B49C-1B961461A819} 

DEFINE_GUID(GUID_DEVINTERFACE_PWM_CONTROLLER, 0x60824b4c, 0xeed1, 0x4c9c, 0xb4, 0x9c, 0x1b, 0x96, 0x14, 0x61, 0xa8, 0x19); 

#define GUID_DEVINTERFACE_PWM_CONTROLLER_WSZ L"{60824B4C-EED1-4C9C-B49C-1B961461A819}" 

Чтобы зарегистрировать GUID интерфейса устройства, драйвер должен вызвать WdfDeviceCreateDeviceInterface в реализации драйвера функции обратного вызова EVT_WDF_DRIVER_DEVICE_ADD. После регистрации система назначает символическую ссылку для контроллера и пинов.

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

Ниже приведен пример символьного пути для контроллера:

\??\ACPI#FSCL000E#1#{60824b4c-eed1-4c9c-b49c-1b961461a819}  

Аналогичным образом формат закреплений выглядит следующим образом: формат пути выглядит следующим образом:

<DeviceInterfaceSymbolicLinkName>\<PinNumber>

где <PinNumber> обозначает 0-й индекс пина для открытия.

Ниже приведен пример символьного пути для закреплений:

\??\ACPI#FSCL000E#1#{60824b4c-eed1-4c9c-b49c-1b961461a819}\0 ; Opens pin 0 

\??\ACPI#FSCL000E#1#{60824b4c-eed1-4c9c-b49c-1b961461a819}\0001 ; Opens pin 1 with the leading 0s have no effect.

Чтобы открыть дескриптор файла, приложение должно вызывать API Configuration Manager (CM_Get_Device_Interface_*).

После открытия дескриптора файла приложение может отправлять эти запросы, вызвав функцию DeviceIoControl. См. раздел PWM.

Драйвер должен использовать предоставленную вспомогательную функцию PWM PwmParsePinPath для синтаксического анализа и проверки путей подключения контактов и извлечения номера контакта.

Настройка свойств интерфейса устройства

Чтобы использовать API-интерфейсы PWM WinRT из приложений UWP, необходимо задать эти свойства интерфейса устройства .

  • DEVPKEY_DeviceInterface_Restricted

    В соответствии с текущей моделью доступа к устройствам UWP, для предоставления приложениям UWP доступа к интерфейсу устройства PWM требуется установить значение свойства ограниченного интерфейса устройства в FALSE.

  • DEVPKEY_DeviceInterface_SchematicName (ссылка???)

    Назначение схематического имени интерфейсу устройства PWM статически подключенных устройств PWM требуется для использования метода фабрики PwmController.GetDeviceSelector(FriendlyName). Имя схемы — это имя, заданное устройству PWM в схеме системы, например (PWM0, PWM_1, et.). Предполагается, что имена схем являются уникальными в системе, но это не контролируется. По крайней мере, не должно быть 2 устройства PWM с одинаковым именем схемы, в противном случае поведение WinRT PWM PwmController.GetDeviceSelector(FriendlyName) будет недетерминированным.

Свойства можно задать одним из двух способов:

  1. Использование INF-файла для драйвера PWM

    Используйте директиву AddProperty для задания свойств устройства. INF-файл должен позволять задавать разные значения для одного и того же свойства в одном экземпляре или в подмножестве экземпляров устройства PWM. Ниже приведен пример настройки DEVPKEY_DeviceInterface_Restricted.

    ;***************************************** 
    ; Device interface installation 
    ;***************************************** 
    
    [PWM_Device.NT.Interfaces] 
    AddInterface={60824B4C-EED1-4C9C-B49C-1B961461A819},,PWM_Interface 
    
    [PWM_Interface] 
    AddProperty=PWM_Interface_AddProperty 
    
    ; Set DEVPKEY_DeviceInterface_Restricted property to false to allow UWP access 
    ; to the device interface without the need to be bound with device metadata. 
    ; If Restricted property is set to true, then only applications which are bound 
    ; with device metadata would be allowed access to the device interface. 
    
    [PWM_Interface_AddProperty] 
    {026e516e-b814-414b-83cd-856d6fef4822},6,0x11,,0 
    

    Не все конструкции имеют одинаковую политику в отношении предоставления устройства PWM UWP. Например, политика может позволить UWP получить доступ к подмножеству экземпляров устройств PWM. Предоставление подмножества устройств PWM или назначение другого значения свойства одному или подмножеству экземпляров устройств PWM требует наличия другого идентификатора оборудования для каждого экземпляра устройства PWM и сопоставления разделов INF выборочно на основе политики.

    Рассмотрим проект на основе SoC, где существует четыре идентичных экземпляра устройства PWM (IP-блоков) с именем PWM0,...,PWM3, где их идентификатор оборудования ACPI назначен (_HID) FSCL00E0, а уникальный идентификатор (_UID) равен 0,...,3. Для предоставления доступа ко всем устройствам PWM для UWP потребуются разделы INF, которые задают DEVPKEY_DeviceInterface_Restricted для сопоставления с идентификатором оборудования ACPI\FSCL00E0.

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

  2. Программно в драйвере PWM

    Драйвер PWM может вызвать IoSetDeviceInterfacePropertyData, чтобы задать свойства интерфейса устройства в реализации EVT_WDF_DRIVER_DEVICE_ADD после создания и публикации интерфейса устройства PWM. Драйвер отвечает за решение о значении назначения и свойстве устройства. Эта информация обычно хранится в системах ACPI для SoC-архитектур. Значение каждого свойства интерфейса устройства можно указать в каждом узле устройства ACPI в методе _DSD как свойства устройства. Драйвер должен запрашивать the_DSD из ACPI, анализировать данные свойств устройства, извлекать значение каждого свойства и назначать его интерфейсу устройства.

    Программное задание свойств делает драйвер и его INF-файл переносимыми в проектах и, следовательно, платформенных пакетах поддержки (BSP), где единственное изменение будет связано с ACPI DSDT, определяющим каждый узел устройства ШИМ. Однако чтение и анализ двоичных блоков ACPI является емким и требует большого количества кода, который может быть подвержен ошибкам и уязвимостям, что приводит к большей поверхности ошибок.

Обработка событий открытия и закрытия файла

Драйвер PWM должен зарегистрировать события создания и закрытия файлов, реализуя функции обратного вызова EVT_WDF_DEVICE_FILE_CREATE и EVT_WDF_FILE_CLEANUP/EVT_WDF_FILE_CLOSE. В реализации драйвер должен выполнить следующие задачи:

  1. Определите, относится ли запрос на создание к контроллеру или контакту.

  2. Если запрос предназначен для пина, драйвер должен проанализировать и проверить путь для запросов на создание пина и убедиться, что запрошенный номер пина находится в пределах допустимых границ контроллера.

  3. Предоставить или запретить доступ к контроллеру и запросы на создание пинов.

  4. Поддерживайте целостность состояния контроллеров и контактов в соответствии с определенной машиной состояний.

В предыдущем наборе задач в EVT_WDF_DEVICE_FILE_CREATE можно выполнить вторую задачу проверки следующим образом:

  1. Если имя файла, связанное с объектом файла запроса, равно null, завершите запрос STATUS_INVALID_DEVICE_REQUEST.

  2. Если имя файла, связанное с объектом файла запроса, является пустой строкой, то это запрос на создание контроллера, в противном случае это запрос на создание пин-кода.

  3. Если это запрос на создание пин-кода, выполните указанные ниже действия.

    1. Разбор пути пина на основе формата <DecimalName> и извлечение номера пина путем вызова функции PwmParsePinPath.

    2. Если синтаксический анализ и проверка пути пина завершились сбоем, выполните запрос с STATUS_NO_SUCH_FILE.

    3. Если номер пина больше или равен количеству пинов контроллера, завершите запрос со статусом STATUS_NO_SUCH_FILE. Обратите внимание, что число пин-кода — это отсчитываемый от нуля индекс.

    4. В противном случае, продолжайте обработку EVT_WDF_DEVICE_FILE_CREATE.

Ниже приведен пример кода, который реализует описанные ранее шаги проверки для обработчика EVT_WDF_DEVICE_FILE_CREATE:

EVT_WDF_DEVICE_FILE_CREATE PwmEvtDeviceFileCreate;

VOID 

PwmEvtDeviceFileCreate ( 
    WDFDEVICE WdfDevice, 
    WDFREQUEST WdfRequest, 
    WDFFILEOBJECT WdfFileObject 
    ) 
{ 

    UNICODE_STRING* filenamePtr = WdfFileObjectGetFileName(WdfFileObject); 
    IMXPWM_DEVICE_CONTEXT* deviceContextPtr = PwmGetDeviceContext(WdfDevice); 
    NTSTATUS status; 
    ULONG pinNumber; 

    // 
    // Parse and validate the filename associated with the file object 
    // 

    bool isPinInterface; 

    if (filenamePtr == nullptr) { 

        WdfRequestComplete(WdfRequest, STATUS_INVALID_DEVICE_REQUEST); 

        return; 

    } else if (filenamePtr->Length > 0) { 

        // 
        // A non-empty filename means to open a pin under the controller namespace 
        // 

        status = PwmParsePinPath(filenamePtr, &pinNumber); 

        if (!NT_SUCCESS(status)) { 

            WdfRequestComplete(WdfRequest, status); 

            return; 

        } 


        if (pinNumber >= deviceContextPtr->ControllerInfo.PinCount) { 

            WdfRequestComplete(WdfRequest, STATUS_NO_SUCH_FILE); 

            return; 

        } 


        isPinInterface = true; 

    } else { 

        // 
        // An empty filename means that the create is against the root controller 
        // 

        isPinInterface = false; 
    } 

    // 
    // Continue request processing here 
    // 
} 

Совместное использование контроллера и контактов

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

Эта модель может быть реализована с помощью сочетания флагов Desired Access и Share Access при открытии дескриптора файла. Модель общего доступа DDI выбирает более простую семантику общего доступа, в которой используется только спецификация Требуемого доступа для управления доступом. Спецификация Share Access не играет никакой роли в модели общего доступа и не принимается во внимание при открытии контроллера или контакта.

В EVT_WDF_DEVICE_FILE_CREATE запрос "Desired Access" и "Share Access" должен быть извлечен и проверен на основе состояния контроллера или пина, или следующим образом:

  1. Если общий доступ не равен 0, запретите доступ и завершите запрос с помощью STATUS_SHARING_VIOLATION.

  2. Если требуется доступ только для чтения, предоставьте доступ и продолжайте обработку EVT_WDF_DEVICE_FILE_CREATE.

  3. Если требуется доступ для записи, тогда:

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

Ниже приведен пример извлечения требуемого доступа и общего доступа из запроса на создание:

void 
PwmCreateRequestGetAccess( 
    _In_ WDFREQUEST WdfRequest, 
    _Out_ ACCESS_MASK* DesiredAccessPtr, 
    _Out_ ULONG* ShareAccessPtr 
    ) 
{ 

    NT_ASSERT(ARGUMENT_PRESENT(DesiredAccessPtr)); 

    NT_ASSERT(ARGUMENT_PRESENT(ShareAccessPtr)); 


    WDF_REQUEST_PARAMETERS wdfRequestParameters; 

    WDF_REQUEST_PARAMETERS_INIT(&wdfRequestParameters); 

    WdfRequestGetParameters(WdfRequest, &wdfRequestParameters); 


    NT_ASSERTMSG( 

        "Expected create request", 
        wdfRequestParameters.Type == WdfRequestTypeCreate); 


    *DesiredAccessPtr = 
        wdfRequestParameters.Parameters.Create.SecurityContext->DesiredAccess; 

    *ShareAccessPtr = wdfRequestParameters.Parameters.Create.ShareAccess; 
} 

Независимость контроллеров и пинов

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

Ниже приведены некоторые примеры сценариев:

  • Доступ для одного процесса

    • Процесс A может открыть пин-код, задать его рабочий цикл, запустить его с помощью периода по умолчанию контроллера, а затем открыть контроллер и задать его период по требованию. Возможно, не потребуется открыть контроллер, если период по умолчанию — ОК для приложения.

    • Процесс может иметь несколько потоков, где каждый управляет отдельным контактом через один контроллер.

  • Доступ нескольких процессов

    • Служебная программа командной строки может открыть контроллер с доступом только для чтения для отображения сведений в консоли. В то же время фоновая задача UWP может открывать контроллер на запись и контролировать светодиод через один вывод.

    • Драйвер режима ядра, управляющий подсветкой ЖК-дисплея с помощью Pin0, удерживая контроллер для записи и блокируя период ШИМ. В то же время служба Win32 использует период PWM, заданный драйвером дисплея, и использует Pin1, чтобы свести некоторые светодиодные индикаторы для передачи определенного состояния пользователю.

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

Контроллер и автоматы состояний пинов

Определение состояния контроллера

Функция состояния Значение по умолчанию Описание
Открыт —For-Write Неправда False указывает, что контроллер закрыт или открыт для чтения; Значение True указывает, что он открыт для записи.
Desired-Period МинимальныйПериод

Компьютер состояния контроллера.

На приведенной ниже машине состояний контроллера сосредоточено только состояние «Is-Opened-For-Write». Значение требуемого периода также опускается, так как оно не влияет на тип операции, которая может быть выполнена на контроллере. Обратите внимание, что всякий раз, когда контроллер, открытый для записи, закрывается вызывающим оператором, открывающим его для записи, контроллер сбрасывается до значений по умолчанию (требуемый период по умолчанию).

Определение состояния закрепления

Функция состояния Значение по умолчанию Описание
Открытый —For-Write Неправда False указывает, что пин-код закрыт или открыт для чтения; Значение True указывает, что он открыт для записи.
АктивныйDuty-Cycle 0
Is-Started Неправда Значение false указывает, остановлено; Значение True указывает, что запущено.

Автомат с выводами.

Цепь состояний пина сосредоточена вокруг сочетания двух состояний: ОткрытоFor-Write и Запущено. Другие состояния выводов, такие как полярность и активные рабочие циклы, опущены, так как их значения не влияют на виды операций, которые могут выполняться на выводе. Обратите внимание, что всякий раз, когда пин, открытый для записи, закрывается вызывающей стороной, открывшей его для записи, пин сбрасывается до значений по умолчанию (остановлен, полярность по умолчанию и активный рабочий цикл). Кроме того, обратите внимание, что переход Set-Polarity на состояние, где Is-Started = true, отсутствует, поскольку он недействителен в этом состоянии.

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

Рекомендации по реализации переходов состояния

  • В EVT_WDF_DEVICE_FILE_CREATE драйвер должен предоставить или запретить доступ на основе запрашиваемого уровня доступа и состояния "открыто" контроллера или пинаFor-Write следующим образом:

    Если запрос имеет требуемый доступ на запись, а контроллер или пин-код уже открыт для записи, завершите запрос с помощью STATUS_SHARING_VIOLATION, в противном случае помечайте контроллер или пин-код как открытый для записи (Is-Opened-For-Write = true), предоставьте доступ и продолжайте обработку.

    В этом примере реализованы описанные ранее шаги проверки доступа для обработчика EVT_WDF_DEVICE_FILE_CREATE, в котором не указана необходимая логика блокировки для обработки одновременных запросов на создание файлов:

    //
    // Verify request desired access
    //
    
    const bool hasWriteAccess = desiredAccess & FILE_WRITE_DATA;
    
    if (isPinInterface) {
        PWM_PIN_STATE* pinPtr = deviceContextPtr->Pins + pinNumber;
        if (hasWriteAccess) {
            if (pinPtr->IsOpenForReadWrite) {
                PWM_LOG_TRACE("Pin%lu access denied.", pinNumber);
                WdfRequestComplete(WdfRequest, STATUS_SHARING_VIOLATION);
                return;
            }
            pinPtr->IsOpenForReadWrite = true;
        }
        PWM_LOG_TRACE(
            "Pin%lu Opened. (IsOpenForReadWrite = %lu)",
            pinNumber,
            (pinPtr->IsOpenForReadWrite ? 1 : 0));
    
    } else {
        if (hasWriteAccess) {
            if (deviceContextPtr->IsControllerOpenForReadWrite) {
                PWM_LOG_TRACE("Controller access denied.");
                WdfRequestComplete(WdfRequest, STATUS_SHARING_VIOLATION);
                return;
            }
            deviceContextPtr->IsControllerOpenForReadWrite = true;
        }
        PWM_LOG_TRACE(
            "Controller Opened. (IsControllerOpenForReadWrite = %lu)",
            (deviceContextPtr->IsControllerOpenForReadWrite ? 1 : 0));
    }
    
    //
    // Allocate and fill a file object context
    //
    IMXPWM_FILE_OBJECT_CONTEXT* fileObjectContextPtr;
    {
        WDF_OBJECT_ATTRIBUTES wdfObjectAttributes;
        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(
            &wdfObjectAttributes,
            IMXPWM_FILE_OBJECT_CONTEXT);
    
        void* contextPtr;
        NTSTATUS status = WdfObjectAllocateContext(
                WdfFileObject,
                &wdfObjectAttributes,
                &contextPtr);
        if (!NT_SUCCESS(status)) {
            IMXPWM_LOG_ERROR(
                "WdfObjectAllocateContext(...) failed. (status = %!STATUS!)",
                status);
            WdfRequestComplete(WdfRequest, status);
            return;
        }
    
        fileObjectContextPtr =
            static_cast<IMXPWM_FILE_OBJECT_CONTEXT*>(contextPtr);
    
        NT_ASSERT(fileObjectContextPtr != nullptr);
        fileObjectContextPtr->IsPinInterface = isPinInterface;
        fileObjectContextPtr->IsOpenForWrite = hasWriteAccess;
        fileObjectContextPtr->PinNumber = pinNumber;
    }
    
  • В EVT_WDF_FILE_CLOSE/EVT_WDF_FILE_CLEANUP драйвер должен поддерживать целостность состояния контроллера или пин-кода.

    Если объект файла принадлежит контроллеру или штырю, который был открыт для записи, сбросьте этот контроллер или штырь в состояние по умолчанию и снимите отметку о том, что контроллер или штырь открыт для записи (Is-Opened-For-Write = false).

    В этом примере реализованы описанные ранее шаги проверки доступа для обработчика EVT_WDF_DEVICE_FILE_CLOSE, где не указана необходимая логика блокировки для обработки одновременных запросов закрытия файлов.

    EVT_WDF_DEVICE_FILE_CLOSE PwmEvtFileClose;
    
    VOID
    PwmEvtFileClose (
        WDFFILEOBJECT WdfFileObject
        )
    {
        WDFDEVICE wdfDevice = WdfFileObjectGetDevice(WdfFileObject);
        PWM_DEVICE_CONTEXT* deviceContextPtr = PwmGetDeviceContext(wdfDevice);
        PWM_FILE_OBJECT_CONTEXT* fileObjectContextPtr = PwmGetFileObjectContext(WdfFileObject);
    
        if (fileObjectContextPtr->IsPinInterface) {
            if (fileObjectContextPtr->IsOpenForReadWrite) {
                const ULONG pinNumber = fileObjectContextPtr->PinNumber;
    
                NTSTATUS status = PwmResetPinDefaults(deviceContextPtr, pinNumber);
                if (!NT_SUCCESS(status)) {
                    PWM_LOG_ERROR(
                        "PwmResetPinDefaults(...) failed. "
                        "(pinNumber = %lu, status = %!STATUS!)",
                        pinNumber,
                        status);
                    //
                    // HW Error Recovery
                    //
                }
    
                NT_ASSERT(deviceContextPtr->Pins[pinNumber].IsOpenForReadWrite);
                deviceContextPtr->Pins[pinNumber].IsOpenForReadWrite = false;
            }
    
            PWM_LOG_TRACE("Pin%lu Closed.", fileObjectContextPtr->PinNumber);
    
        } else {
            if (fileObjectContextPtr->IsOpenForReadWrite) {
                NTSTATUS status = PwmResetControllerDefaults(deviceContextPtr);
                if (!NT_SUCCESS(status)) {
                    IMXPWM_LOG_ERROR(
                        "PwmResetControllerDefaults(...) failed. (status = %!STATUS!)",
                        status);
                    //
                    // HW Error Recovery
                    //  
                }
    
                NT_ASSERT(deviceContextPtr->IsControllerOpenForReadWrite);
                deviceContextPtr->IsControllerOpenForReadWrite = false;
            }
    
            PWM_LOG_TRACE("Controller Closed.");
        }
    }
    

Запросы IOCTL PWM

Запросы IOCTL PWM отправляются приложением или другим драйвером и предназначены для контроллера или определенного пин-кода.

IOCTL контроллера

Закрепление IOCTL

Для каждого запроса IOCTL драйвер PWM должен проверить следующее:

  1. Запрошенная операция (код IOCTL) действительна для связанного с запросом объекта файла.

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

  3. Допустимость запрошенной операции в текущем состоянии контроллера или пин-кода.

  4. Допустимость отдельных входных параметров. Например: желаемый период равный нулю является недопустимым параметром для IOCTL_PWM_CONTROLLER_SET_DESIRED_PERIOD.

Коды состояния завершения IOCTL

Драйвер PWM должен завершить запрос IOCTL с соответствующим кодом состояния. Ниже приведены распространенные коды состояния завершения. Как правило, IOCTL, который задает свойство значением, уже установленным ранее, всегда должен завершаться успешно. Например, установка того же периода, который уже установлен, остановка пина, который уже остановлен, установка полярности, которая уже задана, и т. д.

STATUS_NOT_SUPPORTED

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

STATUS_INVALID_DEVICE_REQUEST

Запрос IOCTL был отправлен в неправильный целевой объект. Например, запрос IOCTL контроллера был отправлен с использованием дескриптора пинового файла.

STATUS_BUFFER_TOO_SMALL

Размер входного или выходного буфера меньше минимального требуемого размера буфера для обработки запроса. Драйвер WDF, использующий WdfRequestRetrieveInputBuffer или WdfRequestRetrieveOutputBuffer, для получения и проверки входных и выходных буферов может возвращать соответствующее состояние ошибки. Для всех IOCTLs с входными и/или выходными буферами определены соответствующие структуры, описывающие эти буферы, где структуры с именами INPUT и _OUTPUT имеют постфиксы соответственно. Минимальный размер входного буфера — sizeof(PWMINPUT), а минимальный размер выходного буфера — sizeof(PWM_OUTPUT).

Код IOCTL Описание
IOCTL_PWM_CONTROLLER_GET_ACTUAL_PERIOD Извлекает эффективный выходной сигнал контроллера пульса ширины модуля (PWM), так как он будет измеряться на своих выходных каналах. Возвращает значение функции PWM_CONTROLLER_GET_ACTUAL_PERIOD_OUTPUT. Irp-IoStatus.Status> имеет одно из значений в следующем списке.
  • Статус: Успешно
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_CONTROLLER_GET_INFO Извлекает сведения о контроллере модуля пульса ширины (PWM). Эти сведения не изменяются после инициализации контроллера.

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

Если размер буфера меньше размера самой низкой версии структуры, запрос завершается с помощью состояния завершения IOCTL STATUS_BUFFER_TOO_SMALL. В противном случае драйвер предполагает самую высокую версию структуры, которая может поместиться в предоставленный выходной буфер и успешно завершит запрос.

Более новая версия PWM_CONTROLLER_INFO имеет размер байта, превышающий размер предыдущей версии.

Irp-IoStatus.Status> имеет одно из значений в следующем списке.
  • Статус: Успешно
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_CONTROLLER_SET_DESIRED_PERIOD Задает выходной период сигнала контроллера модуля пульса (PWM) в предлагаемое значение.

Контроллер PWM пытается задать период, который максимально близок к запрошенным значениям на основе его возможностей. Действующий период возвращается в виде выходных данных IOCTL. Позже его можно получить с помощью IOCTL_PWM_CONTROLLER_GET_ACTUAL_PERIOD.

Требуемый период должен превышать 0 (0) и в поддерживаемом диапазоне периодов контроллера. То есть он должен находиться в диапазоне MinimumPeriod и MaximumPeriod, включительно, которые можно получить с помощью IOCTL_PWM_CONTROLLER_GET_INFO.

Irp-IoStatus.Status> имеет одно из значений в следующем списке.
  • Статус: Успешно
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_INVALID_PARAMETER (Статус: недопустимый параметр)
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_GET_ACTIVE_DUTY_CYCLE_PERCENTAGE Извлекает текущий процент жизненного цикла для пин-кода или канала. Управляющий код возвращает процент в структуре PWM_PIN_GET_ACTIVE_DUTY_CYCLE_PERCENTAGE_OUTPUT.

Irp-IoStatus.Status> имеет одно из значений в следующем списке.

  • Статус: Успешно
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
УСТАНОВКА_АКТИВНОГО_ЗНАЧЕНИЯ_ШИРОТНО-ИМПУЛЬСНОЙ_МОДУЛЯЦИИ_PWM_ВЫВОДА Задайте требуемое значение процентного цикла дежурного цикла для пин-кода контроллера или канала. Код элемента управления указывает процент в виде структуры PWM_PIN_SET_ACTIVE_DUTY_CYCLE_PERCENTAGE_INPUT.

Irp-IoStatus.Status> имеет одно из значений в следующем списке.

  • Статус: Успешно
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_GET_POLARITY Извлекает полярность текущего сигнала пин-кода или канала. Код элемента управления получает полярность сигнала в структуре PWM_PIN_GET_POLARITY_OUTPUT. Полярность сигнала бывает либо активной высокой, либо активной низкой, как определено в перечислении PWM_POLARITY.

Irp->IoStatus.Status имеет одно из значений в следующем списке.

  • Статус: Успешно
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_SET_POLARITY Задает полярность сигнала пин-кода или канала. Управляющий код задает полярность сигнала на основе структуры PWM_PIN_SET_POLARITY_INPUT. Полярность сигнала является либо активным высоким, либо активным низким, как это определено в перечислении PWM_POLARITY.

Изменение полярности допускается только при остановке контакта. Можно определить, остановлен ли пин-код с помощью кода элемента управления IOCTL_PWM_PIN_IS_STARTED. Если штырь остановлен и запрошенная полярность отличается от текущей полярности штыря, запрос завершается со значением STATUS_INVALID_DEVICE_STATE.

Изменение полярности во время запуска контакта может приводить к сбоям для некоторых контроллеров широтно-импульсной модуляции (PWM). Если вы хотите изменить полярность, сначала остановите контакт, измените полярность, а затем запустите контакт.

Irp->IoStatus.Status имеет одно из значений в следующем списке.

  • Статус: Успешно
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_INVALID_PARAMETER (Статус: недопустимый параметр)
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_START Начинает создание сигнала пульсовой ширины модуля (PWM) на закреплении или канале. Чтобы проверить, запущен ли пин, используйте IOCTL_PWM_PIN_IS_STARTED.

Отправка этого IOCTL на пине или канале, который уже запущен, не имеет эффекта, но выполняется успешно.

Irp->IoStatus.Status установлено на одно из значений в следующем списке.

>
  • Статус: Успешно
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_STOP Останавливает создание сигнала пульсовой модуляции ширины (PWM) на закреплении или канале. Чтобы проверить, запущен ли контакт, используйте IOCTL_PWM_PIN_IS_STARTED.

Выдача этого IOCTL на выводе или канале, который уже остановлен, не имеет эффекта, но завершается успешно.

Irp->IoStatus.Status имеет одно из значений в следующем списке.

  • Статус: Успешно
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_АКТИВИРОВАН (при необходимости для пояснения) Извлекает состояние создания сигнала для пин-кода или канала. Каждый контакт имеет состояние запущено или остановлено в виде структуры PWM_PIN_IS_STARTED_OUTPUT. Запущенное состояние имеет булево значение true. Состояние остановки ложное.

По умолчанию пин-код останавливается при открытии и возвращается в остановленное состояние при закрытии или освобождении.

Irp-IoStatus.Status> имеет одно из значений в следующем списке.

  • Статус: Успешно
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL