• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# UART
2
3## Overview
4
5### Function
6
7The Universal Asynchronous Receiver/Transmitter (UART) is a universal serial data bus used for asynchronous communication. It enables bi-directional communication between devices in full-duplex mode.
8
9A UART is connected to other modules through two wires (as shown in Figure 1) or four wires (as shown in Figure 2).
10
11  - TX: UART transmitter. It is connected to the RX of the peer UART.
12  - RX: UART receiver. It is connected to the TX of the peer UART.
13  - RTS: Request to Send signal, indicating whether the local UART is ready to receive data. It is connected to the CTS of the peer UART.
14  - CTS: Clear to Send signal, indicating whether the local UART is allowed to send data to the peer end. It is connected to the RTS of the peer UART.
15
16**Figure 1** Two-wire UART communication
17
18![image1](figures/2-wire-uart-communication.png "2-wire-uart-communication")
19
20**Figure 2** Four-wire UART communication
21
22 ![image2](figures/4-wire-uart-communication.png "4-wire-uart-communication")
23
24The UART transmitter and receiver must have the same settings on particular attributes, such as the baud rate and data format (start bit, data bits, parity bit, and stop bit) before they start to communicate. A UART sends data to the peer end over the TX and receives data from the peer end over the RX. When the size of the buffer used by a UART for storing received data reaches the preset threshold, the RTS signal of the UART changes to **1** (data cannot be received), and the peer UART stops sending data to it because its CTS signal does not allow it to send data.
25
26### Basic Concepts
27
28- Asynchronous communication
29
30  In asynchronous communication, data is transmitted in frames of characters or bytes. Frames are sent and received one by one through the transmission line. The transmitter and receiver have their own clocks to control data sending and receiving. The two clock sources are independent and not synchronized with each other.
31
32  When data is sent one character at a time, the time interval between two characters is not fixed, but the time interval between two adjacent bits in a character frame is fixed.
33
34- Full-duplex transmission
35
36  A duplex communication mode allows data to be transmitted in both directions at the same time. A duplex communication channel is equivalent to two simplex communication channels operating in opposite directions at the same time. In full-duplex mode, signals can be transmitted bidirectionally at the same time.
37
38### Working Principles
39
40In the Hardware Driver Foundation (HDF), the UART uses the independent service mode (see Figure 3) 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.
41
42In 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:
43
44- You need to implement the **Bind()** function in **HdfDriverEntry** to bind services.
45- The **policy** field of **deviceNode** in the **device_info.hcs** file can be **1** or **2**, but not **0**.
46
47The UART module is divided into the following layers:
48
49- Interface layer: provides APIs for opening or closing a UART device, reading or writing data of the specified length, setting or obtaining the baud rate or attributes of a UART device, and setting the transmission mode.
50- Core layer: provides the capabilities of adding or removing a UART controller, and managing UART devices. The core layer interacts with the adaptation layer through hook functions.
51- Adaptation layer: instantiates the hook functions to implement specific features.
52
53**Figure 3** Independent service mode
54
55![image3](figures/independent-service-mode.png)
56
57## Development Guidelines
58
59### When to Use
60
61The UART module is widely used to implement low-speed serial communication between devices, for example, output the printing information. It can also connect to a variety of external GPS and Bluetooth devices. Before using your UART devices with OpenHarmony, you need to perform UART driver adaptation.
62
63### Available APIs
64
65To enable the upper layer to successfully operate the PWM controller by calling the UART APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/uart/uart_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.
66
67**UartHostMethod**:
68
69```c
70struct UartHostMethod {
71    int32_t (*Init)(struct UartHost *host);
72    int32_t (*Deinit)(struct UartHost *host);
73    int32_t (*Read)(struct UartHost *host, uint8_t *data, uint32_t size);
74    int32_t (*Write)(struct UartHost *host, uint8_t *data, uint32_t size);
75    int32_t (*GetBaud)(struct UartHost *host, uint32_t *baudRate);
76    int32_t (*SetBaud)(struct UartHost *host, uint32_t baudRate);
77    int32_t (*GetAttribute)(struct UartHost *host, struct UartAttribute *attribute);
78    int32_t (*SetAttribute)(struct UartHost *host, struct UartAttribute *attribute);
79    int32_t (*SetTransMode)(struct UartHost *host, enum UartTransMode mode);
80    int32_t (*pollEvent)(struct UartHost *host, void *filep, void *table);
81};
82```
83
84**Table 1** Hook functions in UartHostMethod
85
86| Function| Input Parameter| Output Parameter| Return Value| Description|
87| -------- | -------- | -------- | -------- | -------- |
88| Init | **host**: structure pointer to the UART controller at the core layer.| –| HDF_STATUS| Initializes a UART device.|
89| Deinit | **host**: structure pointer to the UART controller at the core layer.| –| HDF_STATUS| Deinitializes a UART device.|
90| Read | **host**: structure pointer to the UART controller at the core layer.<br>**size**: size of the data to read, which is of the uint32_t type.| **data**: pointer to the data read, which is of the uint8_t type. | HDF_STATUS| Reads data.|
91| Write | **host**: structure pointer to the UART controller at the core layer.<br>**data**: pointer to the data to write, which is of the uint8_t type.<br>**size**: size of the data to write, which is of the uint32_t type.| –| HDF_STATUS| Writes data.|
92| SetBaud | **host**: structure pointer to the UART controller at the core layer.<br>**baudRate**: baud rate to set, which is of the uint32_t type.| –| HDF_STATUS| Sets the baud rate.|
93| GetBaud | **host**: structure pointer to the UART controller at the core layer.| **baudRate**: pointer to the baud rate obtained, which is of the uint32_t type.| HDF_STATUS| Obtains the baud rate.|
94| GetAttribute | **host**: structure pointer to the UART controller at the core layer.| **attribute**: structure pointer to the attributes obtained. For details, see **UartAttribute** in **uart_if.h**.| HDF_STATUS| Obtains UART attributes.|
95| SetAttribute | **host**: structure pointer to the UART controller at the core layer.<br>**attribute**: structure pointer to the attributes to set.| –| HDF_STATUS| Sets UART attributes.|
96| SetTransMode | **host**: structure pointer to the UART controller at the core layer.<br>**mode**: transmission mode to set. For details, see **UartTransMode** in **uart_if.h**.| –| HDF_STATUS| Sets the UART transmission mode.|
97| PollEvent | **host**: structure pointer to the UART controller at the core layer.<br>**filep**: void pointer to a file.<br>**table**: void pointer to the poll_table.| –| HDF_STATUS| Polls for the pending events.|
98
99### How to Develop
100
101The UART module adaptation procedure is as follows:
102
1031. Instantiate the driver entry.
1042. Configure attribute files.
1053. Instantiate the UART controller object.
1064. Debug the driver.
107
108### Example
109
110The following uses the **//device_soc_hisilicon/common/platform/uart/uart_hi35xx.c** driver of the Hi3516D V300 development board as an example to describe the UART driver adaptation.
111
1121. Instantiate the driver entry.
113
114   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.
115   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.
116
117   UART driver entry example:
118
119   ```c
120   struct HdfDriverEntry g_hdfUartDevice = {
121       .moduleVersion = 1,
122       .moduleName = "HDF_PLATFORM_UART",   // (Mandatory) The value must be the same as that of moduleName in the .hcs file.
123       .Bind = HdfUartDeviceBind,           // See the Bind function.
124       .Init = HdfUartDeviceInit,           // See the Init function.
125       .Release = HdfUartDeviceRelease,     // See the Release function.
126   };
127   HDF_INIT(g_hdfUartDevice);               // Call HDF_INIT to register the driver entry with the HDF.
128   ```
129
1302. Configure attribute files.
131
132   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 UART controllers as an example. If there are more UART controllers, add the deviceNode information to the **device_info.hcs** file for each controller. The device attribute values configured in **uart_config.hcs** are closely related to default values or value ranges of the **UartHost** members at the core layer.
133
134   - **device_info.hcs** example:
135
136      Add the deviceNode information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file.
137
138      ```c
139      root {
140          device_info {
141              match_attr = "hdf_manager";
142              platform :: host {
143                  hostName = "platform_host";
144                  priority = 50;
145                  device_uart :: device {
146                      device0 :: deviceNode {
147                          policy = 1;                                 // The value 1 means to publish services only to the kernel-mode processes.
148                          priority = 40;                              // Driver startup priority.
149                          permission = 0644;                          // Permission for the device node created.
150                          moduleName = "HDF_PLATFORM_UART";           // Driver name, which must be the same as moduleName in the HdfDriverEntry structure.
151                          serviceName = "HDF_PLATFORM_UART_0";        // Unique name of the service published by the driver. The name is in the HDF_PLATFORM_UART_X format. X indicates the UART controller number.
152                          deviceMatchAttr = "hisilicon_hi35xx_uart_0";// Keyword for matching the private data of the driver. The value must be the same as that of match_attr in the private data configuration table of the driver.
153                      }
154                      device1 :: deviceNode {
155                        policy = 2;                                   // The value 2 means to publish services for both kernel- and user-mode processes.
156                        permission = 0644;
157                        priority = 40;
158                        moduleName = "HDF_PLATFORM_UART";
159                        serviceName = "HDF_PLATFORM_UART_1";
160                        deviceMatchAttr = "hisilicon_hi35xx_uart_1";
161                      }
162                      ...
163                  }
164              }
165          }
166      }
167      ```
168
169   - **uart_config.hcs** example
170
171      Configure the device attributes in the **//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/uart/uart_config.hcs** file. The parameters are as follows:
172
173      ```c
174      root {
175          platform {
176              template uart_controller {                   // 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.
177                  match_attr = "";
178                  num = 0;                                 // (Mandatory) Port number.
179                  baudrate = 115200;                       // (Mandatory) Baud rate.
180                  fifoRxEn = 1;                            // (Mandatory) Enable RX FIFO.
181                  fifoTxEn = 1;                            // (Mandatory) Enable TX FIFO.
182                  flags = 4;                               // (Mandatory) flag signal.
183                  regPbase = 0x120a0000;                   // (Mandatory) Register physical base address used for address mapping.
184                  interrupt = 38;                          // (Mandatory) Interrupt number.
185                  iomemCount = 0x48;                       // (Mandatory) Used for address mapping.
186              }
187              controller_0x120a0000 :: uart_controller {
188                  match_attr = "hisilicon_hi35xx_uart_0";  // (Mandatory) The value must be the same as that of deviceMatchAttr of the device in device_info.hcs.
189              }
190              controller_0x120a1000 :: uart_controller {
191                  num = 1;
192                  baudrate = 9600;
193                  regPbase = 0x120a1000;
194                  interrupt = 39;
195                  match_attr = "hisilicon_hi35xx_uart_1";
196              }
197              ...                                          // Add node information for more UART devices.
198          }
199      }
200      ```
201
202      After the **uart_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect.
203
204      ```c
205      #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/uart/uart_config.hcs" // Relative path of the file.
206      ```
207
2083. Instantiate the UART controller object.
209
210   Initialize the **UartHost** 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 **UartHostMethod** in **UartHost** (so that the underlying driver functions can be called).
211
212   - Define a custom structure.
213
214      To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **uart_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the UART port number, to the object at the core layer.
215
216      ```c
217      struct UartPl011Port {                       // Pin description structure customized.
218          int32_t enable;
219          unsigned long physBase;                  // Physical base address.
220          uint32_t irqNum;                         // IRQ number.
221          uint32_t defaultBaudrate;                // Default baud rate.
222          uint32_t flags;                          // Flag signals related to the following three macros.
223      #define PL011_FLG_IRQ_REQUESTED    (1 << 0)
224      #define PL011_FLG_DMA_RX_REQUESTED (1 << 1)
225      #define PL011_FLG_DMA_TX_REQUESTED (1 << 2)
226          struct UartDmaTransfer *rxUdt;           // DMA transfer.
227          struct UartDriverData *udd;
228      };
229      struct UartDriverData {                      // Structure related to data transfer
230          uint32_t num;                            // Port number.
231          uint32_t baudrate;                       // Baud rate (configurable).
232          struct UartAttribute attr;               // Attributes, such as the data bits and stop bit of the data to transfer.
233          struct UartTransfer *rxTransfer;         // Buffer structure (FIFO structure)
234          wait_queue_head_t wait;                  // Queuing signal related to conditional variables
235          int32_t count;                           // Data count.
236          int32_t state;                           // UART controller state.
237      #define UART_STATE_NOT_OPENED 0
238      #define UART_STATE_OPENING    1
239      #define UART_STATE_USEABLE    2
240      #define UART_STATE_SUSPENDED  3
241          uint32_t flags;                          // Status flags.
242      #define UART_FLG_DMA_RX       (1 << 0)
243      #define UART_FLG_DMA_TX       (1 << 1)
244      #define UART_FLG_RD_BLOCK     (1 << 2)
245          RecvNotify recv;                         // Pointer to the function that receives serial port data.
246          struct UartOps *ops;                     // Custom function pointer structure.
247          void *private;                           // Private data.
248      };
249
250      // UartHost is the controller structure at the core layer. The Init function assigns values to the members of UartHost.
251      struct UartHost {
252          struct IDeviceIoService service;         // Driver service.
253          struct HdfDeviceObject *device;          // Driver device object.
254          uint32_t num;                            // Port number.
255          OsalAtomic atom;                         // Atomic quantity.
256          void *priv;                              // Private data.
257          struct UartHostMethod *method;           // Callback functions.
258      };
259      ```
260
261   - Instantiate **UartHostMethod** in **UartHost**.
262
263      ```c
264      // Instantiate the hook functions in uart_hi35xx.c.
265      struct UartHostMethod g_uartHostMethod = {
266          .Init = Hi35xxInit,                     // Initialize the device.
267          .Deinit = Hi35xxDeinit,                 // Deinitialize the device.
268          .Read = Hi35xxRead,                     // Receive data.
269          .Write = Hi35xxWrite,                   // Write data.
270          .SetBaud = Hi35xxSetBaud,               // Set the baud rate.
271          .GetBaud = Hi35xxGetBaud,               // Obtain the baud rate.
272          .SetAttribute = Hi35xxSetAttribute,     // Set device attributes.
273          .GetAttribute = Hi35xxGetAttribute,     //Obtain device attributes.
274          .SetTransMode = Hi35xxSetTransMode,     // Set the transmission mode.
275          .pollEvent = Hi35xxPollEvent,           // Polling for pending events.
276      };
277      ```
278
279   - Implement the **Bind** function.
280
281      Input parameter:
282
283      **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs.
284
285      Return value:
286
287      **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.
288
289      **Table 2** HDF_STATUS
290
291      | Status| Description|
292      | -------- | -------- |
293      | HDF_ERR_INVALID_OBJECT | Invalid controller object.|
294      | HDF_ERR_MALLOC_FAIL | Failed to allocate memory.|
295      | HDF_ERR_INVALID_PARAM | Invalid parameter.|
296      | HDF_ERR_IO | I/O error.|
297      | HDF_SUCCESS | Initialization successful.|
298      | HDF_FAILURE | Initialization failed.|
299
300      Function description:
301
302      Initializes the custom structure object and **UartHost**.
303
304      ```c
305      //uart_hi35xx.c
306      static int32_t HdfUartDeviceBind(struct HdfDeviceObject *device)
307      {
308          ...
309          return (UartHostCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS; // (Mandatory) Call UartHostCreate.
310      }
311
312      // Description of UartHostCreate() in uart_core.c
313      struct UartHost *UartHostCreate(struct HdfDeviceObject *device)
314      {
315          struct UartHost *host = NULL                                         // Create UartHost.
316          ...
317          host = (struct UartHost *)OsalMemCalloc(sizeof(*host));              // Allocate memory.
318          ...
319          host->device = device;                                               // (Mandatory) Prerequisites for conversion between HdfDeviceObject and UartHost.
320          device->service = &(host->service);                                  // (Mandatory) Prerequisites for conversion between HdfDeviceObject and UartHost.
321          host->device->service->Dispatch = UartIoDispatch;                    // Assign values to Dispatch() of service.
322          OsalAtomicSet(&host->atom, 0);                                       // Initialize or set the atomic service.
323          host->priv = NULL;
324          host->method = NULL;
325          return host;
326      }
327      ```
328
329   - Implement the **Init** function.
330
331      Input parameter:
332
333      **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs.
334
335      Return value:
336
337      HDF_STATUS
338
339      Function description:
340
341      Initialize the custom structure and **UartHost**, calls **UartAddDev()** at the core layer to add the UART controller, and accesses the VFS.
342
343      ```c
344      int32_t HdfUartDeviceInit(struct HdfDeviceObject *device)
345      {
346          int32_t ret;
347          struct UartHost *host = NULL;
348          HDF_LOGI("%s: entry", __func__);
349          ...
350          host = UartHostFromDevice(device);                                           // Forcibly convert to UartHost by using service. The values are assigned by Bind().
351          ...
352          ret = Hi35xxAttach(host, device);                                            // Initialize the UartHost object.
353          ...
354          host->method = &g_uartHostMethod;                                            // Attach the UartHostMethod instance.
355          return ret;
356      }
357      // Initialize UartHost.
358      static int32_t Hi35xxAttach(struct UartHost *host, struct HdfDeviceObject *device)
359      {
360          int32_t ret;
361          struct UartDriverData *udd = NULL;                                           // udd and port are custom structure objects. You can implement features as required.
362          struct UartPl011Port *port = NULL;
363          ...
364          // Steps 1 to 7 assign values to the udd object and then UartHost.
365          udd = (struct UartDriverData *)OsalMemCalloc(sizeof(*udd));                  // Step 1
366          ...
367          port = (struct UartPl011Port *)OsalMemCalloc(sizeof(struct UartPl011Port));  // Step 2
368          ...
369          udd->ops = Pl011GetOps();                                                    // Step 3 Hook the functions for opening or closing a device, setting device attributes, and sending data.
370          udd->recv = PL011UartRecvNotify;                                             // Step 4 Hook the data receiving notification function (conditional lock mechanism).
371          udd->count = 0;                                                              // Step 5.
372          port->udd = udd;                                                             // Step 6 Prerequisites for conversion between UartPl011Port and UartDriverData.
373          ret = UartGetConfigFromHcs(port, device->property);                          // Pass the attributes of HdfDeviceObject to the custom structure to perform related operations. The sample code is as follows:
374          ...
375          udd->private = port;                                                         // Step 7
376          host->priv = udd;                                                            // (Mandatory) Prerequisites for conversion between UartHost and UartDriverData.
377          host->num = udd->num;                                                        // (Mandatory) UART device number.
378          UartAddDev(host);                                                            // (Mandatory) Function in uart_dev.c at the core layer used to register a character device node to the VFS so that the UART can be accessed through the virtual file node in user mode.
379          return HDF_SUCCESS;
380      }
381
382      static int32_t UartGetConfigFromHcs(struct UartPl011Port *port, const struct DeviceResourceNode *node)
383      {
384          uint32_t tmp, regPbase, iomemCount;
385          struct UartDriverData *udd = port->udd;
386          struct DeviceResourceIface *iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
387          ...
388          // Extract the values based on the request and assign the values to the custom structures.
389          if (iface->GetUint32(node, "num", &udd->num, 0) != HDF_SUCCESS) {
390              HDF_LOGE("%s: read busNum fail", __func__);
391              return HDF_FAILURE;
392          }
393          ...
394          return 0;
395          }
396      ```
397
398   - Implement the **Release** function.
399
400      Input parameter:
401
402      **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs.
403
404      Return value:
405
406      No value is returned.
407
408      Function description:
409
410      Releases the memory and deletes the controller. This function assigns values to **Release()** in the driver entry structure. When the HDF fails to call **Init()** to initialize the driver, **Release()** is called to release driver resources.
411
412      > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
413      > All forced conversion operations for obtaining the corresponding object can be successful only when **Init()** has the corresponding value assignment operations.
414
415      ```c
416      void HdfUartDeviceRelease(struct HdfDeviceObject *device)
417      {
418          struct UartHost *host = NULL;
419          ...
420          host = UartHostFromDevice(device);           // Forcible conversion from HdfDeviceObject to UartHost through the service member. For details about the value assignment, see the Bind function.
421          ...
422          if (host->priv != NULL) {
423              Hi35xxDetach(host);                      // Customized memory release function. For details, see the following.
424          }
425          UartHostDestroy(host);                       // Call the core layer function to release the host.
426      }
427
428      static void Hi35xxDetach(struct UartHost *host)
429      {
430          struct UartDriverData *udd = NULL;
431          struct UartPl011Port *port = NULL;
432          ...
433          udd = host->priv;                            // The conversion from UartHost to UartDriverData is involved.
434          ...
435          UartRemoveDev (host);                        // Remove the VFS.
436          port = udd->private;                         // The conversion from UartDriverData to UartPl011Port is involved.
437          if (port != NULL) {
438              if (port->physBase != 0) {
439                  OsalIoUnmap((void *)port->physBase); // Unmap addresses.
440              }
441              OsalMemFree(port);
442              udd->private = NULL;
443          }
444          OsalMemFree (udd);                           // Release UartDriverData.
445          host->priv = NULL;
446      }
447      ```
448
4494. Debug the driver.
450
451   (Optional) For new drivers, verify basic functions, for example, check the information returned after the driver is attached and whether data is successfully transmitted.
452