driver-platform-pwm-develop.md 16.5 KB
Newer Older
A
Annie_wang 已提交
1
# PWM
D
duangavin123 已提交
2

A
Annie_wang 已提交
3
## Overview
D
duangavin123 已提交
4

A
Annie_wang 已提交
5
### Function
D
duangavin123 已提交
6

A
Annie_wang 已提交
7
Pulse Width Modulation (PWM) is a technology that performs digital coding on analog signal levels and converts them into pulses. It is widely used in fields, such as measurement, communication, and power control and conversion. The PWM module is used for controlling vibrators and adjusting backlight brightness in smart devices.
D
duangavin123 已提交
8

A
Annie_wang 已提交
9
### Basic Concepts
D
duangavin123 已提交
10

A
Annie_wang 已提交
11
A pulse (electrical pulse) is a burst of current or voltage, characterized by sudden change and discontinuity. There are many types of pulses. Common pulses include triangular, sharp, rectangular, square, trapezoidal, and zigzag pulses. Main pulse parameters include the repetition period **T** (**T** = 1/**F**, where **F** is the pulse repetition frequency), pulse amplitude **U**, rise time **ts** at the leading edge, fall time **t** at the trailing edge, and pulse width **tk**.
A
Annie_wang 已提交
12

A
Annie_wang 已提交
13
### Working Principles
A
Annie_wang 已提交
14

A
Annie_wang 已提交
15
In the Hardware Driver Foundation (HDF), the PWM uses the independent service mode (see Figure 1) for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage.
A
Annie_wang 已提交
16

A
Annie_wang 已提交
17
In the independent service mode, the core layer does not publish a service for the upper layer. Therefore, a service must be published for each controller. To achieve this purpose:
A
Annie_wang 已提交
18

A
Annie_wang 已提交
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
- You need to implement the **Bind()** function in **HdfDriverEntry** to bind services.
- The **policy** field of **deviceNode** in the **device_info.hcs** file can be **1** or **2**, but not **0**.

The PWM module is divided into the following layers:

- Interface layer: provides APIs for opening or closing a PWM device, setting the PWM period, signal ON-state time, PWM device polarity, or PWM device parameters, obtaining PWM device parameters, and enabling or disabling a PWM device
- Core layer: provides the capabilities of adding or removing a PWM controller and managing PWM devices. The core layer interacts with the adaptation layer through hook functions.
- Adaptation layer: instantiates the hook functions to implement specific features.

**Figure 1** Independent service mode

![image](figures/independent-service-mode.png "PWM independent service mode")

## Development Guidelines

### When to Use

Before using your PWM device with OpenHarmony, you need to perform PWM driver adaptation.

### Available APIs

To enable the upper layer to successfully operate the PWM controller by calling the PWM APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/pwm/pwm_core.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer.

**PwmMethod**:

```c
D
duangavin123 已提交
45
struct PwmMethod {
A
Annie_wang 已提交
46 47 48
    int32_t (*setConfig)(struct PwmDev *pwm, struct PwmConfig *config);
    int32_t (*open)(struct PwmDev *pwm);
    int32_t (*close)(struct PwmDev *pwm);
D
duangavin123 已提交
49 50 51
};
```

A
Annie_wang 已提交
52
**Table 1** Hook functions in **PwmMethod**
A
Annie_wang 已提交
53

A
Annie_wang 已提交
54
| Function| Input Parameter| Return Value| Description|
A
Annie_wang 已提交
55
| -------- | -------- | -------- | -------- |
A
Annie_wang 已提交
56 57 58
| setConfig | **pwm**: structure pointer to the PWM controller at the core layer.<br>**config**: structure pointer to the device attributes to set.| HDF_STATUS| Sets device attributes.|
| open | **pwm**: structure pointer to the PWM controller at the core layer.| HDF_STATUS| Opens a PWM device.|
| close | **pwm**: structure pointer to the PWM controller at the core layer.| HDF_STATUS| Closes a PWM device.|
A
Annie_wang 已提交
59

A
Annie_wang 已提交
60
### How to Develop
A
annie_wangli 已提交
61

A
Annie_wang 已提交
62
The PWM module adaptation procedure is as follows:
A
annie_wangli 已提交
63

A
Annie_wang 已提交
64 65 66 67
1. Instantiate the driver entry.
2. Configure attribute files.
3. Instantiate the PWM controller object.
4. Debug the driver.
A
Annie_wang 已提交
68

A
Annie_wang 已提交
69
### Example
A
Annie_wang 已提交
70

A
Annie_wang 已提交
71
The following uses the **//device_soc_hisilicon/common/platform/pwm/pwm_hi35xx.c** driver of the Hi3516D V300 development board as an example to describe the PWM driver adaptation.
A
Annie_wang 已提交
72

A
Annie_wang 已提交
73 74
1. Instantiate the driver entry.

A
Annie_wang 已提交
75 76
   The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke.
   Generally, the HDF calls **Bind()** and then **Init()** to load a driver. If **Init()** fails to be called, the HDF calls **Release()** to release driver resources and exit.
A
Annie_wang 已提交
77

A
Annie_wang 已提交
78
   PWM driver entry example:
A
Annie_wang 已提交
79 80

   ```c
A
Annie_wang 已提交
81 82
   struct HdfDriverEntry g_hdfPwm = {
       .moduleVersion = 1,
A
Annie_wang 已提交
83 84 85 86
       .moduleName = "HDF_PLATFORM_PWM",   // (Mandatory) The value must be the same as that of moduleName in the .hcs file.
       .Bind = HdfPwmBind,                  // See the Bind function.
       .Init = HdfPwmInit,                  // See the Init function.
       .Release = HdfPwmRelease,            // See the Release function.
A
Annie_wang 已提交
87
   };
A
Annie_wang 已提交
88
   HDF_INIT(g_hdfPwm);                      // Call HDF_INIT to register the driver entry with the HDF.
A
Annie_wang 已提交
89 90
   ```

A
Annie_wang 已提交
91 92 93
2. Configure attribute files.

   Add the deviceNode information to the **device_info.hcs** file. The deviceNode information is related to the driver entry registration. The following example uses two PWM controllers as an example. If there are more PWM controllers, add the deviceNode information to the **device_info.hcs** file for each controller. The device attribute values configured in **pwm_config.hcs** are closely related to default values or value ranges of the **PwmDev** members at the core layer.
A
Annie_wang 已提交
94

A
Annie_wang 已提交
95
   - **device_info.hcs** example
A
Annie_wang 已提交
96

A
Annie_wang 已提交
97
      Add the deviceNode information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file.
A
Annie_wang 已提交
98

A
Annie_wang 已提交
99
      ```c
A
Annie_wang 已提交
100
      root {
A
Annie_wang 已提交
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
          device_info { 
              platform :: host {
                  hostName = "platform_host";
                  priority = 50;
                  device_pwm ::device {                               // Configure an HDF device node for each PWM controller.
                      device0 :: deviceNode {
                          policy = 1;                                 // The value 1 means to publish services only to the kernel-mode processes.
                          priority = 80;                              // Driver startup priority.
                          permission = 0644;                          // Permission for the device node created.
                          moduleName = "HDF_PLATFORM_PWM";            // (Mandatory) Driver name, which must be the same as moduleName in the driver entry.
                          serviceName = "HDF_PLATFORM_PWM_0";         // (Mandatory) Unique name of the service published by the driver.
                          deviceMatchAttr = "hisilicon_hi35xx_pwm_0"; // Controller private data, which must be the same as that of the controller in pwm_config.hcs.
                      }
                      device1 :: deviceNode {
                          policy = 1;
                          priority = 80;
                          permission = 0644;
                          moduleName = "HDF_PLATFORM_PWM";
                          serviceName = "HDF_PLATFORM_PWM_1";
                          deviceMatchAttr = "hisilicon_hi35xx_pwm_1";
                      }
                      ...
                  }
A
Annie_wang 已提交
124
              }
D
duangavin123 已提交
125
          }
A
Annie_wang 已提交
126 127 128
      }
      ```

A
Annie_wang 已提交
129 130 131 132 133
   - **pwm_config.hcs** example

      Configure the device attributes in the **//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pwm/pwm_config.hcs** file. The parameters are as follows:

      ```c
A
Annie_wang 已提交
134
      root {
A
Annie_wang 已提交
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
          platform {
              pwm_config {
                  template pwm_device {                       // (Mandatory) Template configuration. If the template is used to configure device node information, the default values in the template will be used for the fields that are not declared for the node.
                      serviceName = "";
                      match_attr = "";
                      num = 0;                                // (Mandatory) Device number.
                      base = 0x12070000;                      // (Mandatory) Base address used for address mapping.
                  }
                  device_0x12070000 :: pwm_device {           // Add the HDF node and device node information for each device.
                      match_attr = "hisilicon_hi35xx_pwm_0";  // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs.
                  }
                  device_0x12070020 :: pwm_device {
                      match_attr = "hisilicon_hi35xx_pwm_1";
                      num = 1;
                      base = 0x12070020;                      // (Mandatory) Base address used for address mapping.
                  }
              }
D
duangavin123 已提交
152
          }
A
Annie_wang 已提交
153 154
      }
      ```
D
duangavin123 已提交
155

A
Annie_wang 已提交
156
      After the **pwm_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect.
A
Annie_wang 已提交
157

A
Annie_wang 已提交
158 159 160
      ```c
      #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pwm/pwm_config.hcs" // Relative path of the file.
      ```
D
duangavin123 已提交
161

A
Annie_wang 已提交
162
3. Instantiate the PWM controller object.
D
duangavin123 已提交
163

A
Annie_wang 已提交
164 165 166 167 168 169 170
   Initialize the **PwmDev** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **PwmMethod** in **PwmDev** (so that the underlying driver functions can be called).

   - Define a custom structure.

      To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **pwm_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the PWM device number, to the object at the core layer.

      ```c
A
Annie_wang 已提交
171
      struct HiPwm {
A
Annie_wang 已提交
172 173 174 175
          struct PwmDev dev;                // (Mandatory) Control object at the core layer.
          volatile unsigned char *base;     // (Mandatory) Register base address used for address mapping.
          struct HiPwmRegs *reg;            // Device attribute structure, which can be customized.
          bool supportPolarity;             // Whether polarity is supported.
A
Annie_wang 已提交
176
      };
A
Annie_wang 已提交
177 178 179 180 181 182 183 184 185 186

      struct PwmDev {                       // PwmDev is the core layer controller structure. The Bind function assigns values to the members of PwmDev.
          struct IDeviceIoService service;  // Driver service.
          struct HdfDeviceObject *device;   // Driver device object.
          struct PwmConfig cfg;             // Device attribute structure. For details, see the following definition.
          struct PwmMethod *method;         // Hook functions.
          bool busy;                        // Whether the device is busy.
          uint32_t num;                     // Device number.
          OsalSpinlock lock;                // Spinlock.
          void *priv;                       // Private data.
A
Annie_wang 已提交
187
      };
A
Annie_wang 已提交
188 189 190 191 192 193 194 195 196 197 198 199 200 201

      struct PwmConfig {                    // PWM device attributes.
          uint32_t duty;                    // Time that a signal is in the ON state, in ns.
          uint32_t period;                  // Time for a signal to complete an on-and-off cycle, in ns.
          uint32_t number;                  // Number of square waves to generate.
          uint8_t polarity;                 // Polarity
                                            // ------------------- | --------------
                                            // PWM_NORMAL_POLARITY | Normal polarity
                                            // PWM_INVERTED_POLARITY | Inverted polarity
                                            //
          uint8_t status;                   // Running status.
                                            // ------------------ | -----------------
                                            // PWM_DISABLE_STATUS | Disabled
                                            // PWM_ENABLE_STATUS  | Enabled
A
Annie_wang 已提交
202 203 204
      };
      ```

A
Annie_wang 已提交
205
   - Instantiate the **PwmMethod** structure in **PwmDev**.
A
Annie_wang 已提交
206

A
Annie_wang 已提交
207 208 209
      ```c
      struct PwmMethod g_pwmOps = {         // Instantiate the hook functions in pwm_hi35xx.c.
          .setConfig = HiPwmSetConfig,      // Set device attributes.
A
Annie_wang 已提交
210 211
      };
      ```
A
Annie_wang 已提交
212 213

   - Implement the **Init** function.
D
duangavin123 已提交
214

A
Annie_wang 已提交
215
      **Input parameter**:
D
duangavin123 已提交
216

A
Annie_wang 已提交
217
      **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs.
D
duangavin123 已提交
218

A
Annie_wang 已提交
219
      **Return value**:
D
duangavin123 已提交
220

A
Annie_wang 已提交
221
      **HDF_STATUS**<br/>The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file.
D
duangavin123 已提交
222

A
Annie_wang 已提交
223
      | Status| Description|
A
Annie_wang 已提交
224
      | -------- | -------- |
A
Annie_wang 已提交
225 226 227 228 229 230
      | HDF_ERR_INVALID_OBJECT | Invalid controller object.|
      | HDF_ERR_MALLOC_FAIL | Failed to allocate memory.|
      | HDF_ERR_INVALID_PARAM | Invalid parameter.|
      | HDF_ERR_IO | I/O error.|
      | HDF_SUCCESS | Initialization successful.|
      | HDF_FAILURE | Initialization failed.|
D
duangavin123 已提交
231

A
Annie_wang 已提交
232
      **Function description**:
D
duangavin123 已提交
233

A
Annie_wang 已提交
234
      Initializes the custom structure object and **PwmDev** members, and calls **PwmDeviceAdd()** to add the PWM controller to the core layer.
D
duangavin123 已提交
235

A
Annie_wang 已提交
236 237
      ```c
      // In this example, Bind() is an empty function. You can add operations as required or implement related features in Init().
A
Annie_wang 已提交
238 239
      static int32_t HdfPwmBind(struct HdfDeviceObject *obj)
      {
A
Annie_wang 已提交
240 241
          (void)obj;
          return HDF_SUCCESS;
A
Annie_wang 已提交
242
      }
A
Annie_wang 已提交
243

A
Annie_wang 已提交
244 245
      static int32_t HdfPwmInit(struct HdfDeviceObject *obj)
      {
A
Annie_wang 已提交
246 247 248 249 250 251 252 253
          int ret;
          struct HiPwm *hp = NULL;
          ...
          hp = (struct HiPwm *)OsalMemCalloc(sizeof(*hp));
          ...
          ret = HiPwmProbe(hp, obj);                                 // (Mandatory) The implementation is as follows.
          ...
          return ret;
A
Annie_wang 已提交
254
      }
A
Annie_wang 已提交
255

A
Annie_wang 已提交
256 257 258 259 260
      static int32_t HiPwmProbe(struct HiPwm *hp, struct HdfDeviceObject *obj)
      {
          uint32_t tmp;
          struct DeviceResourceIface *iface = NULL;
      
A
Annie_wang 已提交
261
          iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); // Initialize the custom structure HiPwm.
A
Annie_wang 已提交
262 263
          ...
          
A
Annie_wang 已提交
264 265 266 267 268 269 270 271 272 273
          hp->reg = (struct HiPwmRegs *)hp->base;                    // Initialize the custom structure HiPwm.
          hp->supportPolarity = false;                               // Initialize the custom structure HiPwm.
          hp->dev.method = &g_pwmOps;                                // Attach the PwmMethod instance.
          hp->dev.cfg.duty = PWM_DEFAULT_DUTY_CYCLE;                 // Initialize PwmDev.
          hp->dev.cfg.period = PWM_DEFAULT_PERIOD;                   // Initialize PwmDev.
          hp->dev.cfg.polarity = PWM_DEFAULT_POLARITY;               // Initialize PwmDev.
          hp->dev.cfg.status = PWM_DISABLE_STATUS;                   // Initialize PwmDev.
          hp->dev.cfg.number = 0;                                    // Initialize PwmDev.
          hp->dev.busy = false;                                      // Initialize PwmDev.
          if (PwmDeviceAdd(obj, &(hp->dev)) ) != HDF_SUCCESS) {      // Call the core layer function to initialize devices and services.
A
Annie_wang 已提交
274 275 276 277 278 279
              OsalIoUnmap((void *)hp->base);
              return HDF_FAILURE;
          }
          return HDF_SUCCESS;
      }
      ```
A
Annie_wang 已提交
280 281

   - Implement the **Release** function.
A
Annie_wang 已提交
282 283 284

      **Input parameter**:

A
Annie_wang 已提交
285
      **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs.
A
Annie_wang 已提交
286 287 288 289 290 291 292

      **Return value**:

      No value is returned.

      **Function description**:

A
Annie_wang 已提交
293
      Releases the memory and deletes the controller. This function assigns values to **Release()** in the driver entry structure. If the HDF fails to call **Init()** to initialize the driver, **Release()** is called to release driver resources.
A
Annie_wang 已提交
294

A
Annie_wang 已提交
295
      ```c
A
Annie_wang 已提交
296 297 298 299
      static void HdfPwmRelease(struct HdfDeviceObject *obj)
      {
          struct HiPwm *hp = NULL;
          ...
A
Annie_wang 已提交
300 301 302 303
          hp = (struct HiPwm *)obj->service;        // A forced conversion from HdfDeviceObject to HiPwm is involved.
          ...                                       
          PwmDeviceRemove(obj, &(hp->dev));         // (Mandatory) Call the core layer functions to release PwmDev devices and services. A forced conversion from HiPwm to PwmDev is involved in the process.
          HiPwmRemove(hp);                          // Release HiPwm.
A
Annie_wang 已提交
304 305
      }
      ```
A
Annie_wang 已提交
306 307 308 309

4. Debug the driver.

    (Optional) For new drivers, verify the basic functions, such as the PWM status control and response to interrupts.