如果不想編寫spi設(shè)備驅(qū)動(dòng),那么linux內(nèi)核提供了一個(gè)通用的spidev設(shè)備驅(qū)動(dòng),提供統(tǒng)一的字符設(shè)備操作,那么只需要在應(yīng)用層讀寫和控制即可。以SPI OLED為例子,使用spidev驅(qū)動(dòng)OLED,基于linux5.15.
參考源碼:
tools/spi/spidev_fdx.c
tools/spi/spidev_test.c
1.配置使能spidev用戶態(tài)驅(qū)動(dòng)
- > Device Drivers │
- > SPI support
< * > User mode SPI device driver support
驅(qū)動(dòng)源文件:driver/spi/spidev.c
2.編寫設(shè)備樹
&ecspi2{
fsl,spi-num-chipselects = < 1 >;
cs-gpios = < &gpio1 29 GPIO_ACTIVE_LOW >;//GPIO1_29
pinctrl-names = "default";
pinctrl-0 = < &pinctrl_ecspi2 >;
status = "okay";
oled: ssd13306@0{
compatible = "Justice,ssd13306";//匹配spidev驅(qū)動(dòng)
spi-cpol;
spi-cpha;
spi-rx-bus-width = < 0 >;
spi-max-frequency = < 20000000 >;
reset-gpios = < &gpio1 27 GPIO_ACTIVE_LOW >;
dc-gpios = < &gpio1 31 GPIO_ACTIVE_HIGH >;
reg = < 0 >;
};
};
注意這里的compatible 屬性,在新版linux內(nèi)核,可以寫任意的字符串,最好不再寫”spidev”,老版的是要寫成”spidev”。給出的理由是: spidev should never be referenced in DT without a specific compatible string, it is a Linux implementation thing rather than a description of the hardware
3.修改spidev驅(qū)動(dòng),增加compatible
//driver/spi/spidev.c
static const struct of_device_id spidev_dt_ids[] = {
{ .compatible = "rohm,dh2228fv" },
{ .compatible = "lineartechnology,ltc2488" },
{ .compatible = "semtech,sx1301" },
{ .compatible = "lwn,bk4" },
{ .compatible = "dh,dhcom-board" },
{ .compatible = "menlo,m53cpld" },
{ .compatible = "cisco,spi-petra" },
{ .compatible = "micron,spi-authenta" },
{ .compatible = "Justice,ssd13306" },
{},
};
在最后一行增加自己的匹配compatible。
4.用戶態(tài)讀寫、控制spi設(shè)備
當(dāng)設(shè)備樹和spidev成功匹配后,就為我們的spi設(shè)備生成了一個(gè)設(shè)備節(jié)點(diǎn)/dev/spidevx.y。
x表示spi控制器的軟件枚舉的總線號(hào),y表示這個(gè)spi控制器的片選號(hào)。
設(shè)備樹aliases會(huì)影響spi控制器的軟件枚舉的總線號(hào),如我使用ecspi2,芯片上spi控制器的第2個(gè)spi控制器,但是我的設(shè)備樹上面寫了aliases,因此我呈現(xiàn)的就是/dev/spidev1.0
aliases {
...
spi0 = &ecspi1;
spi1 = &ecspi2;
spi2 = &ecspi3;
spi3 = &ecspi4;
}
/sys/class/spidev下可以確認(rèn)spidev枚舉出了多少個(gè)spi設(shè)備
root@imx6ull /sys/class/spidev# ls
spidev1.0
設(shè)置傳輸模式
spi 核心會(huì)根據(jù)spi device的mode標(biāo)志,來決定一些傳輸?shù)哪J剑热?a href="http://www.1cnz.cn/tags/時(shí)鐘/" target="_blank">時(shí)鐘極性、LSB等等
這個(gè)標(biāo)志是32位,低16位是用戶空間設(shè)置,高16位是內(nèi)核控制,因此不能有沖突。如果在設(shè)置之前讀取,讀取到的模式和設(shè)備樹定義的一樣。
以下是用戶空間的宏定義。
include/uapi/linux/spi/spi.h
#define SPI_CPHA _BITUL(0) /* clock phase */
#define SPI_CPOL _BITUL(1) /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_MODE_X_MASK (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH _BITUL(2) /* chipselect active high? */
#define SPI_LSB_FIRST _BITUL(3) /* per-word bits-on-wire */
#define SPI_3WIRE _BITUL(4) /* SI/SO signals shared */
#define SPI_LOOP _BITUL(5) /* loopback mode */
#define SPI_NO_CS _BITUL(6) /* 1 dev/bus, no chipselect */
#define SPI_READY _BITUL(7) /* slave pulls low to pause */
#define SPI_TX_DUAL _BITUL(8) /* transmit with 2 wires */
#define SPI_TX_QUAD _BITUL(9) /* transmit with 4 wires */
#define SPI_RX_DUAL _BITUL(10) /* receive with 2 wires */
#define SPI_RX_QUAD _BITUL(11) /* receive with 4 wires */
#define SPI_CS_WORD _BITUL(12) /* toggle cs after each word */
#define SPI_TX_OCTAL _BITUL(13) /* transmit with 8 wires */
#define SPI_RX_OCTAL _BITUL(14) /* receive with 8 wires */
#define SPI_3WIRE_HIZ _BITUL(15) /* high impedance turnaround */
/*
* All the bits defined above should be covered by SPI_MODE_USER_MASK.
* The SPI_MODE_USER_MASK has the SPI_MODE_KERNEL_MASK counterpart in
* 'include/linux/spi/spi.h'. The bits defined here are from bit 0 upwards
* while in SPI_MODE_KERNEL_MASK they are from the other end downwards.
* These bits must not overlap. A static assert check should make sure of that.
* If adding extra bits, make sure to increase the bit index below as well.
*/
#define SPI_MODE_USER_MASK (_BITUL(16) - 1)