• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# PWM
2
3## Overview
4
5### Function
6
7Pulse 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.
8
9### Basic Concepts
10
11A 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**.
12
13### Working Principles
14
15In 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.
16
17In 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:
18
19- You need to implement the **Bind()** function in **HdfDriverEntry** to bind services.
20- The **policy** field of **deviceNode** in the **device_info.hcs** file can be **1** or **2**, but not **0**.
21
22The PWM module is divided into the following layers:
23
24- 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
25- 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.
26- Adaptation layer: instantiates the hook functions to implement specific features.
27
28**Figure 1** Independent service mode
29
30![image](figures/independent-service-mode.png "PWM independent service mode")
31
32## Development Guidelines
33
34### When to Use
35
36Before using your PWM device with OpenHarmony, you need to perform PWM driver adaptation.
37
38### Available APIs
39
40To 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.
41
42**PwmMethod**:
43
44```c
45struct PwmMethod {
46    int32_t (*setConfig)(struct PwmDev *pwm, struct PwmConfig *config);
47    int32_t (*open)(struct PwmDev *pwm);
48    int32_t (*close)(struct PwmDev *pwm);
49};
50```
51
52**Table 1** Hook functions in **PwmMethod**
53
54| Function| Input Parameter| Return Value| Description|
55| -------- | -------- | -------- | -------- |
56| 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.|
57| open | **pwm**: structure pointer to the PWM controller at the core layer.| HDF_STATUS| Opens a PWM device.|
58| close | **pwm**: structure pointer to the PWM controller at the core layer.| HDF_STATUS| Closes a PWM device.|
59
60### How to Develop
61
62The PWM module adaptation procedure is as follows:
63
641. Instantiate the driver entry.
652. Configure attribute files.
663. Instantiate the PWM controller object.
674. Debug the driver.
68
69### Example
70
71The 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.
72
731. Instantiate the driver entry.
74
75   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.
76   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.
77
78   PWM driver entry example:
79
80   ```c
81   struct HdfDriverEntry g_hdfPwm = {
82       .moduleVersion = 1,
83       .moduleName = "HDF_PLATFORM_PWM",   // (Mandatory) The value must be the same as that of moduleName in the .hcs file.
84       .Bind = HdfPwmBind,                  // See the Bind function.
85       .Init = HdfPwmInit,                  // See the Init function.
86       .Release = HdfPwmRelease,            // See the Release function.
87   };
88   HDF_INIT(g_hdfPwm);                      // Call HDF_INIT to register the driver entry with the HDF.
89   ```
90
912. Configure attribute files.
92
93   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.
94
95   - **device_info.hcs** example
96
97      Add the deviceNode information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file.
98
99      ```c
100      root {
101          device_info {
102              platform :: host {
103                  hostName = "platform_host";
104                  priority = 50;
105                  device_pwm ::device {                               // Configure an HDF device node for each PWM controller.
106                      device0 :: deviceNode {
107                          policy = 1;                                 // The value 1 means to publish services only to the kernel-mode processes.
108                          priority = 80;                              // Driver startup priority.
109                          permission = 0644;                          // Permission for the device node created.
110                          moduleName = "HDF_PLATFORM_PWM";            // (Mandatory) Driver name, which must be the same as moduleName in the driver entry.
111                          serviceName = "HDF_PLATFORM_PWM_0";         // (Mandatory) Unique name of the service published by the driver.
112                          deviceMatchAttr = "hisilicon_hi35xx_pwm_0"; // Controller private data, which must be the same as that of the controller in pwm_config.hcs.
113                      }
114                      device1 :: deviceNode {
115                          policy = 1;
116                          priority = 80;
117                          permission = 0644;
118                          moduleName = "HDF_PLATFORM_PWM";
119                          serviceName = "HDF_PLATFORM_PWM_1";
120                          deviceMatchAttr = "hisilicon_hi35xx_pwm_1";
121                      }
122                      ...
123                  }
124              }
125          }
126      }
127      ```
128
129   - **pwm_config.hcs** example
130
131      Configure the device attributes in the **//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pwm/pwm_config.hcs** file. The parameters are as follows:
132
133      ```c
134      root {
135          platform {
136              pwm_config {
137                  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.
138                      serviceName = "";
139                      match_attr = "";
140                      num = 0;                                // (Mandatory) Device number.
141                      base = 0x12070000;                      // (Mandatory) Base address used for address mapping.
142                  }
143                  device_0x12070000 :: pwm_device {           // Add the HDF node and device node information for each device.
144                      match_attr = "hisilicon_hi35xx_pwm_0";  // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs.
145                  }
146                  device_0x12070020 :: pwm_device {
147                      match_attr = "hisilicon_hi35xx_pwm_1";
148                      num = 1;
149                      base = 0x12070020;                      // (Mandatory) Base address used for address mapping.
150                  }
151              }
152          }
153      }
154      ```
155
156      After the **pwm_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect.
157
158      ```c
159      #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pwm/pwm_config.hcs" // Relative path of the file.
160      ```
161
1623. Instantiate the PWM controller object.
163
164   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).
165
166   - Define a custom structure.
167
168      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.
169
170      ```c
171      struct HiPwm {
172          struct PwmDev dev;                // (Mandatory) Control object at the core layer.
173          volatile unsigned char *base;     // (Mandatory) Register base address used for address mapping.
174          struct HiPwmRegs *reg;            // Device attribute structure, which can be customized.
175          bool supportPolarity;             // Whether polarity is supported.
176      };
177
178      struct PwmDev {                       // PwmDev is the core layer controller structure. The Bind function assigns values to the members of PwmDev.
179          struct IDeviceIoService service;  // Driver service.
180          struct HdfDeviceObject *device;   // Driver device object.
181          struct PwmConfig cfg;             // Device attribute structure. For details, see the following definition.
182          struct PwmMethod *method;         // Hook functions.
183          bool busy;                        // Whether the device is busy.
184          uint32_t num;                     // Device number.
185          OsalSpinlock lock;                // Spinlock.
186          void *priv;                       // Private data.
187      };
188
189      struct PwmConfig {                    // PWM device attributes.
190          uint32_t duty;                    // Time that a signal is in the ON state, in ns.
191          uint32_t period;                  // Time for a signal to complete an on-and-off cycle, in ns.
192          uint32_t number;                  // Number of square waves to generate.
193          uint8_t polarity;                 // Polarity
194                                            // ------------------- | --------------
195                                            // PWM_NORMAL_POLARITY | Normal polarity
196                                            // PWM_INVERTED_POLARITY | Inverted polarity
197                                            //
198          uint8_t status;                   // Running status.
199                                            // ------------------ | -----------------
200                                            // PWM_DISABLE_STATUS | Disabled
201                                            // PWM_ENABLE_STATUS  | Enabled
202      };
203      ```
204
205   - Instantiate the **PwmMethod** structure in **PwmDev**.
206
207      ```c
208      struct PwmMethod g_pwmOps = {         // Instantiate the hook functions in pwm_hi35xx.c.
209          .setConfig = HiPwmSetConfig,      // Set device attributes.
210      };
211      ```
212
213   - Implement the **Init** function.
214
215      **Input parameter**:
216
217      **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs.
218
219      **Return value**:
220
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.
222
223      | Status| Description|
224      | -------- | -------- |
225      | HDF_ERR_INVALID_OBJECT | Invalid controller object.|
226      | HDF_ERR_MALLOC_FAIL | Failed to allocate memory.|
227      | HDF_ERR_INVALID_PARAM | Invalid parameter.|
228      | HDF_ERR_IO | I/O error.|
229      | HDF_SUCCESS | Initialization successful.|
230      | HDF_FAILURE | Initialization failed.|
231
232      **Function description**:
233
234      Initializes the custom structure object and **PwmDev** members, and calls **PwmDeviceAdd()** to add the PWM controller to the core layer.
235
236      ```c
237      // In this example, Bind() is an empty function. You can add operations as required or implement related features in Init().
238      static int32_t HdfPwmBind(struct HdfDeviceObject *obj)
239      {
240          (void)obj;
241          return HDF_SUCCESS;
242      }
243
244      static int32_t HdfPwmInit(struct HdfDeviceObject *obj)
245      {
246          int ret;
247          struct HiPwm *hp = NULL;
248          ...
249          hp = (struct HiPwm *)OsalMemCalloc(sizeof(*hp));
250          ...
251          ret = HiPwmProbe(hp, obj);                                 // (Mandatory) The implementation is as follows.
252          ...
253          return ret;
254      }
255
256      static int32_t HiPwmProbe(struct HiPwm *hp, struct HdfDeviceObject *obj)
257      {
258          uint32_t tmp;
259          struct DeviceResourceIface *iface = NULL;
260
261          iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); // Initialize the custom structure HiPwm.
262          ...
263
264          hp->reg = (struct HiPwmRegs *)hp->base;                    // Initialize the custom structure HiPwm.
265          hp->supportPolarity = false;                               // Initialize the custom structure HiPwm.
266          hp->dev.method = &g_pwmOps;                                // Attach the PwmMethod instance.
267          hp->dev.cfg.duty = PWM_DEFAULT_DUTY_CYCLE;                 // Initialize PwmDev.
268          hp->dev.cfg.period = PWM_DEFAULT_PERIOD;                   // Initialize PwmDev.
269          hp->dev.cfg.polarity = PWM_DEFAULT_POLARITY;               // Initialize PwmDev.
270          hp->dev.cfg.status = PWM_DISABLE_STATUS;                   // Initialize PwmDev.
271          hp->dev.cfg.number = 0;                                    // Initialize PwmDev.
272          hp->dev.busy = false;                                      // Initialize PwmDev.
273          if (PwmDeviceAdd(obj, &(hp->dev)) ) != HDF_SUCCESS) {      // Call the core layer function to initialize devices and services.
274              OsalIoUnmap((void *)hp->base);
275              return HDF_FAILURE;
276          }
277          return HDF_SUCCESS;
278      }
279      ```
280
281   - Implement the **Release** function.
282
283      **Input parameter**:
284
285      **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs.
286
287      **Return value**:
288
289      No value is returned.
290
291      **Function description**:
292
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.
294
295      ```c
296      static void HdfPwmRelease(struct HdfDeviceObject *obj)
297      {
298          struct HiPwm *hp = NULL;
299          ...
300          hp = (struct HiPwm *)obj->service;        // A forced conversion from HdfDeviceObject to HiPwm is involved.
301          ...
302          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.
303          HiPwmRemove(hp);                          // Release HiPwm.
304      }
305      ```
306
3074. Debug the driver.
308
309    (Optional) For new drivers, verify the basic functions, such as the PWM status control and response to interrupts.
310