• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# SPI
2
3
4## Overview
5
6Serial Peripheral Interface (SPI) is a serial bus specification used for high-speed, full-duplex, and synchronous communication. SPI is developed by Motorola. It is commonly used for communication with flash memory, real-time clocks, sensors, and analog-to-digital (A/D) converters.
7
8SPI works in controller/device mode. Generally, there is one SPI controller that controls one or more SPI devices. They are connected via four wires:
9  - SCLK: clock signal output from the SPI controller
10  - MOSI: data output from the SPI controller to a device
11  - MISO: data output from an SPI device to the controller
12  - Chip select (CS): output from the SPI controller to indicate that data is being sent. It is controlled by the SPI controller.
13
14The figure below shows the connection between one controller and two devices (device A and device B). Device A and device B share three pins (SCLK, MISO, and MOSI) of the controller. CS 0 of device A and CS 1 of device B are connected to CS 0 and CS 1 of the controller, respectively.
15
16  **Figure 1** Connection between the SPI controller and devices
17
18  ![image](figures/spi-controller-device-connection.png)
19
20- SPI communication is usually initiated by the controller and is performed as follows:
21  1. The SPI controller selects a device to communicate on the select line. Only one device can be selected at a time.
22  2. SCLK provides clock signals to the selected device.
23  3. The SPI controller sends data to the device via MOSI, and receives data from the devices via MISO.
24
25- SPI can work in one of the following modes according to the combination of Clock Polarity (CPOL) and Clock Phase (CPHA) of the clock signal:
26  - If both CPOL and CPHA are **0**, the clock signal level is low in the idle state and data is sampled on the first clock edge.
27  - If CPOL is **0** and CPHA is **1**, the clock signal level is low in the idle state and data is sampled on the second clock edge.
28  - If CPOL is **1** and CPHA is **0**, the clock signal level is high in the idle state and data is sampled on the first clock edge.
29  - If both CPOL and CPHA are **1**, the clock signal level is high in the idle state and data is sampled on the second clock edge.
30
31- SPI defines a set of common functions for operating an SPI device, including those for:
32  - Obtaining and releasing an SPI device handle.
33  - Reading or writing data of the specified length from or into an SPI device.
34  - Customizing data reading or writing via **SpiMsg**.
35  - Obtaining and setting SPI device attributes.
36
37> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**<br>
38> Currently, these functions are only applicable in the communication initiated by the SPI controller.
39
40
41## Available APIs
42
43  **Table 1** SPI driver APIs
44
45| API| Description|
46| -------- | -------- |
47| SpiOpen | Opens an SPI device handle.|
48| SpiClose | Closes an SPI device handle.|
49| SpiRead | Reads data of the specified length from a device.|
50| SpiWrite | Writes data of the specified length to a device.|
51| SpiTransfer | Transfers SPI data.|
52| SpiSetCfg | Sets SPI device attributes.|
53| SpiGetCfg | Obtains SPI device attributes.|
54
55> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**<br>
56> All APIs described in this document can be called only in kernel mode.
57
58
59## Usage Guidelines
60
61
62### How to Use
63
64The figure below shows the general process of using SPI.
65
66  **Figure 2** Process of using SPI APIs
67
68  ![](figures/using-SPI-process.png)
69
70
71### Opening an SPI Device Handle
72
73Before performing SPI communication, call **SpiOpen** to open the SPI device handle. This function returns the device handle of the SPI based on the specified bus number and CS number.
74
75
76```
77DevHandle SpiOpen(const struct SpiDevInfo *info);
78```
79
80  **Table 2** Description of SpiOpen
81
82| **Parameter**| **Description**|
83| -------- | -------- |
84| info | Pointer to the SPI device descriptor.|
85| **Return Value**| **Description**|
86| NULL | The operation failed.|
87| Device handle| The operation is successful. The SPI device handle obtained is returned.|
88
89For example, open the handle of the SPI device, whose bus number and the CS number are both **0**.
90
91
92```
93struct SpiDevInfo spiDevinfo;       /* SPI device descriptor. */
94DevHandle spiHandle = NULL;         /* SPI device handle */
95spiDevinfo.busNum = 0;              /* SPI device bus number. */
96spiDevinfo.csNum = 0;               /* SPI device CS number. */
97
98/* Obtain the SPI device handle. */
99spiHandle = SpiOpen(&spiDevinfo);
100if (spiHandle == NULL) {
101    HDF_LOGE("SpiOpen: failed\n");
102    return;
103}
104```
105
106
107### Obtaining SPI Device Attributes
108
109After obtaining the SPI device handle, you need to configure the device attributes. Before configuring the device attributes, you can call **SpiGetCfg** to obtain the device attributes.
110
111
112```
113int32_t SpiGetCfg(DevHandle handle, struct SpiCfg *cfg);
114```
115
116  **Table 3** Description of SpiGetCfg
117
118| **Parameter**| **Description**|
119| -------- | -------- |
120| handle     | SPI device handle.|
121| cfg        | Pointer to the SPI device attributes.|
122| **Return Value**| **Description**|
123| 0          | The operation is successful.|
124| Negative value      | The operation failed.|
125
126
127```
128int32_t ret;
129struct SpiCfg cfg = {0};                /* SPI configuration. */
130ret = SpiGetCfg(spiHandle, &cfg);       /* Obtain SPI device attributes. */
131if (ret != 0) {
132    HDF_LOGE("SpiGetCfg: failed, ret %d\n", ret);
133}
134```
135
136
137### Setting SPI Device Attributes
138
139After obtaining the SPI device handle, call **SpiSetCfg** to set SPI device attributes.
140
141
142```
143int32_t SpiSetCfg(DevHandle handle, struct SpiCfg *cfg);
144```
145
146  **Table 4** Description of SpiSetCfg
147
148| **Parameter**| **Description**|
149| -------- | -------- |
150| handle     | SPI device handle.|
151| cfg        | Pointer to the SPI device attributes.|
152| **Return Value**| **Description**|
153| 0          | The operation is successful.|
154| Negative value      | The operation failed.|
155
156
157```
158int32_t ret;
159struct SpiCfg cfg = {0};                     /* SPI configuration. */
160cfg.mode = SPI_MODE_LOOP;                    /* Communicate in loop mode. */
161cfg.transferMode = PAL_SPI_POLLING_TRANSFER; /* Communicate in polling mode. */
162cfg.maxSpeedHz = 115200;                     /* Maximum transfer frequency. */
163cfg.bitsPerWord = 8;                         /* The width of per word to be read or written is 8 bits. */
164ret = SpiSetCfg(spiHandle, &cfg);            /* Set SPI device attributes. */
165if (ret != 0) {
166    HDF_LOGE("SpiSetCfg: failed, ret %d\n", ret);
167}
168```
169
170
171### Performing SPI Communication
172
173- Write data to an SPI device
174
175  Call **SpiWrite()** to write data to an SPI device only once.
176
177
178  ```
179  int32_t SpiWrite(DevHandle handle, uint8_t *buf, uint32_t len);
180  ```
181
182  **Table 5** Description of SpiWrite
183
184| **Parameter**| **Description**|
185| -------- | -------- |
186| handle     | SPI device handle.|
187| buf        | Pointer to the data to write.|
188| len        | Length of the data to write.|
189| **Return Value**| **Description**|
190| 0          | The operation is successful.|
191| Negative value      | The operation failed.|
192
193
194  ```
195  int32_t ret;
196  uint8_t wbuff[4] = {0x12, 0x34, 0x56, 0x78};
197  /* Write data of the specified length to an SPI device. */
198  ret = SpiWrite(spiHandle, wbuff, 4);
199  if (ret != 0) {
200      HDF_LOGE("SpiWrite: failed, ret %d\n", ret);
201  }
202  ```
203
204- Read data from an SPI device
205
206  Call **SpiRead()** to read data from an SPI device only once.
207
208
209  ```
210  int32_t SpiRead(DevHandle handle, uint8_t *buf, uint32_t len);
211  ```
212
213  **Table 6** Description of SpiRead
214
215| **Parameter**| **Description**|
216| -------- | -------- |
217| handle | SPI device handle.|
218| buf | Pointer to the data to read.|
219| len | Length of the data to read.|
220| **Return Value**| **Description**|
221| 0 | The operation is successful.|
222| Negative value| The operation failed.|
223
224
225  ```
226  int32_t ret;
227  uint8_t rbuff[4] = {0};
228  /* Read data of the specified length from an SPI device. */
229  ret = SpiRead(spiHandle, rbuff, 4);
230  if (ret != 0) {
231      HDF_LOGE("SpiRead: failed, ret %d\n", ret);
232  }
233  ```
234
235- Perform a custom transfer
236
237  Call **SpiTransfer()** to perform a custom transfer.
238
239
240  ```
241  int32_t SpiTransfer(DevHandle handle, struct SpiMsg *msgs, uint32_t count);
242  ```
243
244  **Table 7** Description of SpiTransfer
245
246| **Parameter**| **Description**|
247| -------- | -------- |
248| handle | SPI device handle.|
249| msgs | Pointer to the message array to be transferred.|
250| count | Number of messages in the message array.|
251| **Return Value**| **Description**|
252| 0 | The operation is successful.|
253| Negative value| The operation failed.|
254
255
256  ```
257  int32_t ret;
258  uint8_t wbuff[1] = {0x12};
259  uint8_t rbuff[1] = {0};
260  struct SpiMsg msg;        /* Custom message to be transferred. */
261  msg.wbuf = wbuff;         /* Data to write. */
262  msg.rbuf = rbuff;         /* Data to read. */
263  msg.len = 1;              /* The length of the data to read or write is 1 bit. */
264  msg.csChange = 1;         /* Close the CS before the next transfer. */
265  msg.delayUs = 0;          /* No delay before the next transfer. */
266  msg.speed = 115200;       /* Transfer speed. */
267  /* Perform a custom transfer. The number of messages to be transferred is 1. */
268  ret = SpiTransfer(spiHandle, &msg, 1);
269  if (ret != 0) {
270      HDF_LOGE("SpiTransfer: failed, ret %d\n", ret);
271  }
272  ```
273
274
275### Closing an SPI Device Handle
276
277After the SPI communication, call **SpiClose()** to close the SPI device handle.
278
279
280```
281void SpiClose(DevHandle handle);
282```
283
284This function releases the resources requested by **MipiDsiOpen**.
285
286  **Table 8** Description of SpiClose
287
288| **Parameter**| **Description**|
289| -------- | -------- |
290| handle | SPI device handle.|
291
292
293```
294SpiClose(spiHandle); /* Close the SPI device handle. */
295```
296
297
298## Example
299
300The following example shows how to obtain an SPI device handle, set device attributes, and then read or write data from or into the SPI device, and finally close the SPI device handle.
301
302```
303#include "hdf_log.h"
304#include "spi_if.h"
305
306void SpiTestSample(void)
307{
308    int32_t ret;
309    struct SpiCfg cfg;                  /* SPI device configuration. */
310    struct SpiDevInfo spiDevinfo;       /* SPI device descriptor. */
311    DevHandle spiHandle = NULL; /* SPI device handle. */
312    struct SpiMsg msg;                  /* Custom message to be transferred. */
313    uint8_t rbuff[4] = { 0 };
314    uint8_t wbuff[4] = { 0x12, 0x34, 0x56, 0x78 };
315    uint8_t wbuff2[4] = { 0xa1, 0xb2, 0xc3, 0xd4 };
316
317    spiDevinfo.busNum = 0;              /* SPI device bus number. */
318    spiDevinfo.csNum = 0;               /* SPI device CS number. */
319    spiHandle = SpiOpen(&spiDevinfo);   /* Open the SPI device handle based on spiDevinfo. */
320    if (spiHandle == NULL) {
321        HDF_LOGE("SpiOpen: failed\n");
322        return;
323    }
324    /* Obtain SPI attributes. */
325    ret = SpiGetCfg(spiHandle, &cfg);
326    if (ret != 0) {
327        HDF_LOGE("SpiGetCfg: failed, ret %d\n", ret);
328        goto err;
329    }
330    cfg.maxSpeedHz = 115200;                /* Set the maximum clock frequency to 115200. */
331    cfg.bitsPerWord = 8;                    /* Set the word width to 8 bits. */
332    /* Set SPI attributes. */
333    ret = SpiSetCfg(spiHandle, &cfg);
334    if (ret != 0) {
335        HDF_LOGE("SpiSetCfg: failed, ret %d\n", ret);
336        goto err;
337    }
338    /* Write data of the specified length to an SPI device. */
339    ret = SpiWrite(spiHandle, wbuff, 4);
340    if (ret != 0) {
341        HDF_LOGE("SpiWrite: failed, ret %d\n", ret);
342        goto err;
343    }
344    /* Read data of the specified length from an SPI device. */
345    ret = SpiRead(spiHandle, rbuff, 4);
346    if (ret != 0) {
347        HDF_LOGE("SpiRead: failed, ret %d\n", ret);
348        goto err;
349    }
350    msg.wbuf = wbuff2;  /* Data to write. */
351    msg.rbuf = rbuff;   /* Data to read. */
352    msg.len = 4;        /* Set the length of the data to read or write to 4 bits. */
353    msg.csChange = 1;   /* Close the CS before the next transfer. */
354    msg.delayUs = 0;    /* No delay before the next transfer. */
355    msg.speed = 115200; /* Transfer speed. */
356    /* Perform a custom transfer. The number of messages to be transferred is 1. */
357    ret = SpiTransfer(spiHandle, &msg, 1);
358    if (ret != 0) {
359        HDF_LOGE("SpiTransfer: failed, ret %d\n", ret);
360        goto err;
361    }
362err:
363    /* Close the SPI device handle. */
364    SpiClose(spiHandle);
365}
366```
367