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