• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# DAC
2
3## Overview
4
5### DAC
6
7A digit-to-analog converter (DAC) is a device that converts a digital signal into an analog signal in electronics.
8
9The DAC module supports development of digital-to-analog conversion. The DAC devices can be used to:
10
11- Provide the output channel for the process control computer system and connect to the executor to implement automatic control of the production process.
12- Serve as an important module in the analog-to-digital converter using feedback technologies.
13
14### Basic Concepts
15
16- Resolution
17
18  The number of binary bits that can be converted by a DAC. A greater number of bits indicates a higher resolution.
19
20- Conversion precision
21
22  Difference between the actual output value of the DAC and the theoretical value when the maximum value is added to the input end. The conversion precision of a DAC converter varies depending on the structure of the chip integrated on the DAC and the interface circuit configuration. The ideal conversion precision value should be as small as possible. To achieve optimal DAC conversion precision, the DAC must have high resolution. In addition, errors in the devices or power supply of the interface circuits will affect the conversion precision. When the error exceeds a certain degree, a DAC conversion error will be caused.
23
24- Conversion speed
25
26  The conversion speed is determined by the setup time. The setup time is the period from the time the input suddenly changes from all 0s to all 1s to the time the output voltage remains within the FSR ± ½LSB (or FSR ± x%FSR). It is the maximum response time of the DAC, and hence used to measure the conversion speed.
27
28  - The full scale range (FSR) is the maximum range of the output signal amplitude of a DAC. Different DACs have different FSRs, which can be limited by positive and negative currents or voltages.
29
30  - The least significant byte (LSB) refers to bit 0 (the least significant bit) in a binary number.
31
32### Working Principles
33
34In the Hardware Driver Foundation (HDF), the DAC module uses the unified service mode for API adaptation. In this mode, a device service is used as the DAC manager to handle access requests from the devices of the same type in a unified manner. The unified service mode applies to the scenario where there are many device objects of the same type. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. The DAC module uses the unified service mode, as shown in Figure 1.
35
36The DAC module is divided into the following layers:
37- Interface layer: provides the capabilities of opening and closing a device and writing data.
38- Core layer: provides the capabilities of binding, initializing, and releasing devices.
39- Adaptation layer: implements hardware-related functions, such as controller initialization.
40
41In the unified service mode, the core layer manages all controllers in a unified manner and publishes a service for the interface layer. That is, the driver does not need to publish a service for each controller.
42
43>![](../public_sys-resources/icon-note.gif) **NOTE**<br/>The core layer can call the APIs of the interface layer and uses hooks to call APIs of the adaptation layer. In this way, the adaptation layer can indirectly call the APIs of the interface layer, but the interface layer cannot call the APIs of the adaptation layer.
44
45**Figure 1** Unified service mode
46
47![](figures/unified-service-mode.png "DAC unified service mode")
48
49### Constraints
50
51The DAC module supports only the kernel (LiteOS-A) for mini and small systems.
52
53## Development Guidelines
54
55### When to Use
56
57The DAC module is used for digital-to-analog conversion, audio output, and motor control. The DAC driver is used when the digital signals input by the DAC module are converted into analog signals to output. Before using DAC devices with OpenHarmony, you need to adapt the DAC driver to OpenHarmony. The following describes how to do it.
58
59### Available APIs
60
61To enable the upper layer to successfully operate the hardware by calling the DAC APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/dac/dac_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.
62
63Definitions of **DacMethod** and **DacLockMethod**:
64
65```c++
66struct DacMethod {
67    /* Hook used to write data. */
68    int32_t (*write)(struct DacDevice *device, uint32_t channel, uint32_t val);
69    /* Hook used to start a DAC device. */
70    int32_t (*start)(struct DacDevice *device);
71    /* Hook used to stop a DAC device. */
72    int32_t (*stop)(struct DacDevice *device);
73};
74
75struct DacLockMethod {
76    int32_t (*lock)(struct DacDevice *device);
77    void (*unlock)(struct DacDevice *device);
78};
79```
80At the adaptation layer, **DacMethod** must be implemented, and **DacLockMethod** can be implemented based on service requirements. The core layer provides the default **DacLockMethod**, in which a spinlock is used to protect the critical section.
81
82```c
83static int32_t DacDeviceLockDefault(struct DacDevice *device)
84{
85    if (device == NULL) {
86        HDF_LOGE("%s: device is null", __func__);
87        return HDF_ERR_INVALID_OBJECT;
88    }
89    return OsalSpinLock(&device->spin);
90}
91
92static void DacDeviceUnlockDefault(struct DacDevice *device)
93{
94    if (device == NULL) {
95        HDF_LOGE("%s: device is null", __func__);
96        return;
97    }
98    (void)OsalSpinUnlock(&device->spin);
99}
100
101static const struct DacLockMethod g_dacLockOpsDefault = {
102    .lock = DacDeviceLockDefault,
103    .unlock = DacDeviceUnlockDefault,
104};
105```
106
107If spinlock cannot be used, you can use another type of lock to implement **DacLockMethod**. The implemented **DacLockMethod** will replace the default **DacLockMethod**.
108
109**Table 1** Hook functions in **DacMethod**
110
111| Function| Input Parameter                                                        | Output Parameter| Return Value            | Description          |
112| -------- | ------------------------------------------------------------ | ---- | ------------------ | -------------- |
113| write    | **device**: structure pointer to the DAC controller at the core layer.<br>**channel**: channel ID, which is of the uint32_t type.<br>**val**: data to write, which is of the uint32_t type.| -  | HDF_STATUS| Writes the target digit-to-analog (DA) value.|
114| start    | **device**: structure pointer to the DAC controller at the core layer.                       | -  | HDF_STATUS| Starts a DAC device.   |
115| stop     | **device**: structure pointer to the DAC controller at the core layer.                       | -  | HDF_STATUS| Stops a DAC device.   |
116
117**Table 2** Functions in **DacLockMethod**
118
119| Function| Input Parameter| Output Parameter| Return Value| Description|
120| -------- | -------- | -------- | -------- | -------- |
121| lock | **device**: structure pointer to the DAC device object at the core layer.| -| HDF_STATUS| Acquires the critical section lock.|
122| unlock | **device**: structure pointer to the DAC device object at the core layer.| -| HDF_STATUS| Releases the critical section lock.|
123
124
125### How to Develop
126
127The DAC module adaptation procedure is as follows:
128
1291. Instantiate the driver entry.
1302. Configure attribute files.
1313. Instantiate the core layer APIs.
1324. Debug the driver.
133
134### Example
135
136The following uses the Hi3516D V300 driver **//device/soc/hisilicon/common/platform/dac/dac_hi35xx.c** as an example to describe how to perform the DAC driver adaptation.
137
1381. Instantiate the driver entry.
139
140    The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the module name must be the same as that in **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/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.
141
142    Generally, the HDF calls **Init()** to load the driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit.
143
144    ```c++
145    static struct HdfDriverEntry g_dacDriverEntry = {
146        .moduleVersion = 1,
147        .Init = VirtualDacInit,
148        .Release = VirtualDacRelease,
149        .moduleName = "virtual_dac_driver",// (Mandatory) The value must be the same as that in the .hcs file.
150    };
151    HDF_INIT(g_dacDriverEntry);             // Call HDF_INIT to register the driver entry with the HDF.
152    ```
153
1542. Configure attribute files.
155
156   - Add the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file.
157
158     The device attribute values are closely related to the driver implementation and the default values or value ranges of the **DacDevice** members at the core layer, for example, the number of device channels and the maximum transmission speed.
159
160     In the unified service mode, the first device node in the **device_info.hcs** file must be the DAC manager. The parameters must be set as follows:
161
162     | Parameter          | Value                                                                 |
163     | --------------- | ------------------------------------------------------------------- |
164     | policy          | **0**, which indicates that no service is published.                                               |
165     | priority        | Driver startup priority. The value range is 0 to 200. A larger value indicates a lower priority. For the drivers with the same priority, the device loads them randomly.|
166     | permission      | Driver permission.                                                            |
167     | moduleName      | **HDF_PLATFORM_DAC_MANAGER**                                      |
168     | serviceName     | **HDF_PLATFORM_DAC_MANAGER**                                      |
169     | deviceMatchAttr | Reserved.                                                      |
170
171     Configure DAC controller information from the second node. This node specifies a type of DAC controllers rather than a specific DAC controller. In this example, there is only one DAC device. If there are multiple DAC devices, add the **deviceNode** information to the **device_info.hcs** file and add the corresponding device attributes to the **dac_config.hcs** file for each device.
172
173     **device_info.hcs** example:
174
175        ```hcs
176        root {
177            device_dac :: device {
178                /* device0 is the DAC manager. */
179                device0 :: deviceNode {
180                    policy = 0;
181                    priority = 52;
182                    permission = 0644;
183                    serviceName = "HDF_PLATFORM_DAC_MANAGER";
184                    moduleName = "HDF_PLATFORM_DAC_MANAGER";
185                }
186            }
187            /* dac_virtual is a DAC controller. */
188            dac_virtual :: deviceNode {
189                policy = 0;
190                priority = 56;
191                permission = 0644;
192                moduleName = "virtual_dac_driver";        // (Mandatory) Driver name, which must be the same as moduleName in the driver entry.
193                serviceName = "VIRTUAL_DAC_DRIVER";       // (Mandatory) Unique name of the service published by the driver.
194                deviceMatchAttr = "virtual_dac";          // (Mandatory) Controller private data, which must be same as that of the controller in dac_config.hcs.
195                }
196        }
197        ```
198
199    - Configure the **dac_test_config.hcs** file.
200
201      Add a file to the directory of a product to configure driver parameters. For example, add the **vendor/hisilicon/hispark_taurus/hdf_config/hdf_test/dac_test_config.hcs** file for the hispark_taurus development board.
202
203      The configuration parameters are as follows:
204
205        ```hcs
206        root {
207            platform {
208            dac_config {
209                    match_attr = "virtual_dac"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs.
210                    template dac_device {
211                        deviceNum = 0;          // Device number.
212                        validChannel = 0x1; // Valid channel 1.
213                        rate = 20000; // Transmission speed.
214                    }
215                    device_0 :: dac_device {
216                        deviceNum = 0;          // Device number.
217                        validChannel = 0x2; // Valid channel 2.
218                    }
219                }
220            }
221        }
222        ```
223
224      After the **dac_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect.
225
226      For example, if the path of **dac_config.hcs** is **device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/dac/dac_config.hcs**, add the following statement to **hdf.hcs** of the product:
227
228        ```c
229      #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/dac/dac_config.hcs" // Relative path of the file.
230        ```
231
2323. Instantiate the core layer APIs.
233
234    - Initialize the **DacDevice** object.
235
236        Initialize **DacDevice** in the **VirtualDacParseAndInit** function.
237
238        ```c++
239        /* Custom structure of the virtual driver. */
240        struct VirtualDacDevice {
241        /* DAC device structure. */
242            struct DacDevice device;
243            /* DAC device number. */
244            uint32_t deviceNum;
245            /* Valid channel. */
246            uint32_t validChannel;
247            /* DAC rate. */
248            uint32_t rate;
249        };
250        /* Parse and initialize the **DacDevice** object of the core layer. */
251        static int32_t VirtualDacParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node)
252        {
253            /* Define the return values. */
254            int32_t ret;
255            /* Pointer to the virtual DAC device. */
256            struct VirtualDacDevice *virtual = NULL;
257            (void)device;
258            /* Allocate space for this pointer. */
259            virtual = (struct VirtualDacDevice *)OsalMemCalloc(sizeof(*virtual));
260        if (virtual == NULL) {
261            /* If the value is null, return an error code. */
262            HDF_LOGE("%s: Malloc virtual fail!", __func__);
263            return HDF_ERR_MALLOC_FAIL;
264        }
265        /* Read the attribute file. */
266        ret = VirtualDacReadDrs(virtual, node);
267        if (ret != HDF_SUCCESS) {
268            /* Failed to read the file. */
269            HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret);
270            /* Release the space for the virtual DAC device. */
271            OsalMemFree(virtual);
272            /* Set the pointer to 0. */
273            virtual = NULL;
274            return ret;
275        }
276        /* Initialize the pointer to the virtual DAC device. */
277        VirtualDacDeviceInit(virtual);
278        /* Initialize the priv object in DacDevice. */
279        virtual->device.priv = (void *)node;
280        /* Initialize the devNum object in DacDevice. */
281        virtual->device.devNum = virtual->deviceNum;
282        /* Initialize the ops object in DacDevice. */
283        virtual->device.ops = &g_method;
284        /* Add a DAC device. */
285        ret = DacDeviceAdd(&virtual->device);
286        if (ret != HDF_SUCCESS) {
287            /* Failed to add the device. */
288            HDF_LOGE("%s: add Dac controller failed! ret = %d", __func__, ret);
289            /* Release the space for the virtual DAC device. */
290            OsalMemFree(virtual);
291            /* Set this pointer to null. */
292            virtual = NULL;
293            return ret;
294        }
295
296        return HDF_SUCCESS;
297           }
298        ```
299
300
301
302    - Define a custom structure.
303
304        The custom structure holds parameters and data for the driver. Define the custom structure based on the function parameters of the device. The **DacTestReadConfig()** provided by the HDF reads the values in the **dac_config.hcs** file, and **DeviceResourceIface()** initializes the custom structure and passes some important parameters, such as the device number and bus number, to the **DacDevice** object at the core layer.
305
306        ```c++
307        struct VirtualDacDevice {
308              struct DacDevice device;// (Mandatory) Control object at the core layer. For details, see the description below.
309              uint32_t deviceNum;      // (Mandatory) Device number.
310              uint32_t validChannel;   // (Mandatory) Valid channel.
311              uint32_t rate;           // (Mandatory) Sampling rate.
312          };
313
314          /* DacDevice is the core layer controller structure. The Init() function assigns values to the members of DacDevice. */
315          struct DacDevice {
316              const struct DacMethod *ops;
317              OsalSpinlock spin;      // Spinlock.
318              uint32_t devNum; // Device number.
319              uint32_t chanNum; // Device channel number.
320              const struct DacLockMethod *lockOps;
321              void *priv;
322          };
323        ```
324
325
326
327    - Instantiate **DacDevice** in **DacMethod**.
328
329        The **VirtualDacWrite**, **VirtualDacStop**, and **VirtualDacStart** functions are instantiated in **dac_virtual.c**.
330
331        ```c++
332        static const struct DacMethod g_method = {
333            .write = VirtualDacWrite, // Write data to a DAC device.
334            .stop = VirtualDacStop, // Stop a DAC device.
335            .start = VirtualDacStart, // Start a DAC device.
336        };
337        ```
338
339        >![](../public_sys-resources/icon-note.gif) **NOTE**<br>For details about **DacMethod**, see [Available APIs](#available-apis).
340
341
342
343    - Implement the **Init** function.
344
345        Input parameter:
346
347        **HdfDeviceObject**, an interface parameter provided by the driver, contains the .hcs information.
348
349        Return value:
350
351        **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.
352
353        | Status                 | Description                |
354        | ---------------------- | -------------------------- |
355        | HDF_ERR_INVALID_OBJECT | Invalid controller object. |
356        | HDF_ERR_INVALID_PARAM  | Invalid parameter.         |
357        | HDF_ERR_MALLOC_FAIL    | Failed to allocate memory. |
358        | HDF_ERR_IO             | I/O error.                 |
359        | HDF_SUCCESS            | Transmission successful.   |
360        | HDF_FAILURE            | Transmission failed.       |
361
362
363
364        Function description:
365
366        Initializes the custom structure object and **DacDevice**, and calls the **DacDeviceAdd** function at the core layer.
367
368        ```c++
369          static int32_t VirtualDacParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node)
370            {
371                /* Define the return values. */
372                int32_t ret;
373                /* Pointer to the VirtualDacDevice structure. */
374                struct VirtualDacDevice *virtual = NULL;
375                (void)device;
376                /* Allocate memory of the specified size. */
377                virtual = (struct VirtualDacDevice *)OsalMemCalloc(sizeof(*virtual));
378                if (virtual == NULL) {
379                    /* Failed to allocate memory. */
380                    HDF_LOGE("%s: Malloc virtual fail!", __func__);
381                    return HDF_ERR_MALLOC_FAIL;
382                }
383                /* Read the node parameters in the HCS. The function definition is as follows. */
384                ret = VirtualDacReadDrs(virtual, node);
385                if (ret != HDF_SUCCESS) {
386                    /* Failed to read the node data. */
387                    HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret);
388                    goto __ERR__;
389                }
390                /* Initialize the DAC device pointer. */
391                VirtualDacDeviceInit(virtual);
392                /* Pass in the private data of the node. */
393                virtual->device.priv = (void *)node;
394                /* Pass in the device number. */
395                virtual->device.devNum = virtual->deviceNum;
396                /* Pass in the method. */
397                virtual->device.ops = &g_method;
398                /* Add a DAC device. */
399                ret = DacDeviceAdd(&virtual->device);
400                if (ret != HDF_SUCCESS) {
401                    /* Failed to add the DAC device. */
402                    HDF_LOGE("%s: add Dac controller failed! ret = %d", __func__, ret);
403                    goto __ERR__;
404                }
405                /* The DAC device is added successfully. */
406                return HDF_SUCCESS;
407            __ERR__:
408                /* If the pointer is null */
409                if (virtual != NULL) {
410                    / Release the memory. */
411                    OsalMemFree(virtual);
412                    /* Set this pointer to null. */
413                    virtual = NULL;
414                }
415
416                return ret;
417            }
418
419            static int32_t VirtualDacInit(struct HdfDeviceObject *device)
420            {
421                /* Define return values. */
422                int32_t ret;
423                /* Child node of the device structure. */
424                const struct DeviceResourceNode *childNode = NULL;
425                /* Check the input parameter pointer. */
426                if (device == NULL || device->property == NULL) {
427                    /* The input parameter pointer is null. */
428                    HDF_LOGE("%s: device or property is NULL", __func__);
429                    return HDF_ERR_INVALID_OBJECT;
430                }
431                /* The input parameter pointer is not null. */
432                ret = HDF_SUCCESS;
433                DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
434                    /* Parse the child node. */
435                    ret = VirtualDacParseAndInit(device, childNode);
436                    if (ret != HDF_SUCCESS) {
437                        /* Failed to parse the child node. */
438                        break;
439                    }
440                }
441                /* The child node is parsed. */
442                return ret;
443            }
444
445            static int32_t VirtualDacReadDrs(struct VirtualDacDevice *virtual, const struct DeviceResourceNode *node)
446            {
447                struct DeviceResourceIface *drsOps = NULL;
448
449                /* Obtain the drsOps method. */
450                drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
451                if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) {
452                    HDF_LOGE("%s: Invalid drs ops fail!", __func__);
453                    return HDF_FAILURE;
454                }
455                /* Read the configuration parameters in sequence and fill them in the structure. */
456                if (drsOps->GetUint32(node, "deviceNum", &virtual->deviceNum, 0) != HDF_SUCCESS) {
457                    HDF_LOGE("%s: Read deviceNum fail!", __func__);
458                    return HDF_ERR_IO;
459                }
460                if (drsOps->GetUint32(node, "validChannel", &virtual->validChannel, 0) != HDF_SUCCESS) {
461                    HDF_LOGE("%s: Read validChannel fail!", __func__);
462                    return HDF_ERR_IO;
463                }
464                if (drsOps->GetUint32(node, "rate", &virtual->rate, 0) != HDF_SUCCESS) {
465                    HDF_LOGE("%s: Read rate fail!", __func__);
466                    return HDF_ERR_IO;
467                }
468                return HDF_SUCCESS;
469            }
470        ```
471
472
473
474    - Implement the **Release** function.
475
476      Input parameter:
477
478      **HdfDeviceObject**, an interface parameter provided by the driver, contains the .hcs information.
479
480      Return value:
481
482      No value is returned.
483
484      Function description:
485
486      Releases the memory and deletes the controller. This function assigns values to the **Release** function in the driver entry structure. If the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources.
487
488       >![](../public_sys-resources/icon-note.gif) **NOTE** <br>
489       >
490       >All forced conversion operations for obtaining the corresponding object can be successful only when **Init()** has the corresponding value assignment operations.
491
492
493
494      ```c++
495       static void VirtualDacRemoveByNode(const struct DeviceResourceNode *node)
496           {
497               /* Define return values. */
498               int32_t ret;
499               /*Define the DAC device number. */
500               int16_t devNum;
501               /* Pointer to the DacDevice structure. */
502               struct DacDevice *device = NULL;
503               // Pointer to the VirtualDacDevice structure. */
504               struct VirtualDacDevice *virtual = NULL;
505               /* Pointer to the DeviceResourceIface structure. */
506               struct DeviceResourceIface *drsOps = NULL;
507               /* Obtain device resources through the instance entry. */
508               drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
509               /* Check the input parameter pointer. */
510               if (drsOps == NULL || drsOps->GetUint32 == NULL) {
511                   /* The pointer is null. */
512                   HDF_LOGE("%s: invalid drs ops fail!", __func__);
513                   return;
514               }
515               /* Obtain data of the devNum node. */
516               ret = drsOps->GetUint16(node, "devNum", (uint16_t *)&devNum, 0);
517               if (ret != HDF_SUCCESS) {
518                   /* The information fails to be obtained. */
519                   HDF_LOGE("%s: read devNum fail!", __func__);
520                   return;
521               }
522               /* Obtain the DAC device number. */
523               device = DacDeviceGet(devNum);
524               /* Check whether the DAC device number and data are null. */
525               if (device != NULL && device->priv == node) {
526                   /* Release the DAC device number if the device data is null. */
527                   DacDevicePut(device);
528                   /* Remove the DAC device number. */
529                   DacDeviceRemove(device);
530                   virtual = (struct VirtualDacDevice *)device;
531                   /* Release the virtual pointer. */
532                   OsalMemFree(virtual);
533               }
534               return;
535           }
536
537           static void VirtualDacRelease(struct HdfDeviceObject *device)
538           {
539               /* Define the child node structure pointer to the DeviceResourceNode. */
540               const struct DeviceResourceNode *childNode = NULL;
541               /* Check whether the input parameter pointer is null. */
542               if (device == NULL || device->property == NULL) {
543                   /* The input parameter pointer is null. */
544                   HDF_LOGE("%s: device or property is NULL", __func__);
545                   return;
546               }
547
548               DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
549                   /* Remove the DAC by node. */
550                   VirtualDacRemoveByNode(childNode);
551               }
552           }
553      ```
554
555
556
557
5584. Debug the driver.
559
560   (Optional) Verify the basic functions of the new driver, for example, check whether the test cases are successful after the driver is loaded.
561