亚洲综合图片区自拍_思思91精品国产综合在线观看_一区二区三区欧美_欧美黑人又粗又大_亚洲人成精品久久久久桥本

天天亮點!基于Linux使用spidev驅(qū)動OLED

2023-06-16 11:06:49 來源:justice與初中生小許

如果不想編寫spi設(shè)備驅(qū)動,那么linux內(nèi)核提供了一個通用的spidev設(shè)備驅(qū)動,提供統(tǒng)一的字符設(shè)備操作,那么只需要在應(yīng)用層讀寫和控制即可。以SPI OLED為例子,使用spidev驅(qū)動OLED,基于linux5.15.

參考源碼:

tools/spi/spidev_fdx.c


(資料圖)

tools/spi/spidev_test.c

1.配置使能spidev用戶態(tài)驅(qū)動

- > Device Drivers                                                                                         │         - > SPI support      < * >   User mode SPI device driver support

驅(qū)動源文件: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ū)動  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ū)動,增加compatible

//driver/spi/spidev.cstatic 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è)備生成了一個設(shè)備節(jié)點/dev/spidevx.y。

x表示spi控制器的軟件枚舉的總線號,y表示這個spi控制器的片選號。

設(shè)備樹aliases會影響spi控制器的軟件枚舉的總線號,如我使用ecspi2,芯片上spi控制器的第2個spi控制器,但是我的設(shè)備樹上面寫了aliases,因此我呈現(xiàn)的就是/dev/spidev1.0

aliases {  ...  spi0 = &ecspi1;  spi1 = &ecspi2;  spi2 = &ecspi3;  spi3 = &ecspi4;}

/sys/class/spidev下可以確認spidev枚舉出了多少個spi設(shè)備

root@imx6ull /sys/class/spidev# lsspidev1.0

設(shè)置傳輸模式

spi 核心會根據(jù)spi device的mode標(biāo)志,來決定一些傳輸?shù)哪J?,比?u>時鐘極性、LSB等等

這個標(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)

讀取和設(shè)置模式

如上面所說,mode是一個32位的標(biāo)志,你要精準(zhǔn)設(shè)置這個標(biāo)志,每一個位的定義都在上面列舉。

//使用到的宏SPI_IOC_RD_MODESPI_IOC_RD_MODE32SPI_IOC_WR_MODESPI_IOC_WR_MODE32//偽代碼/*讀取傳輸模式*/u32 mode32;u8 mode;fd = open("/dev/spidevx.y",O_RDWR);if (ioctl(fd, SPI_IOC_RD_MODE32, &mode32) < 0) {            perror("SPI rd_mode");            return; }if (ioctl(fd, SPI_IOC_RD_MODE, &mode) < 0) {            perror("SPI rd_mode");            return; }/*設(shè)置傳輸模式*/u32 mode32;u8 mode;fd = open("/dev/spidevx.y",O_RDWR);if (ioctl(fd, SPI_IOC_WR_MODE32, &mode32) < 0) {            perror("SPI rd_mode");            return; }if (ioctl(fd, SPI_IOC_WR_MODE, &mode) < 0) {            perror("SPI rd_mode");            return; }

讀取和設(shè)置LSB傳輸

這個也是讀取和設(shè)置spi device的mode的某位,只不過單獨定義了一個命令

//使用到的宏SPI_IOC_RD_LSB_FIRSTSPI_IOC_WR_LSB_FIRST//偽代碼/*讀取LSB模式*/u8 lsb;fd = open("/dev/spidevx.y",O_RDWR);if (ioctl(fd, SPI_IOC_RD_LSB_FIRST, &lsb) < 0) {            perror("SPI rd_lsb_fist");            return; }/*設(shè)置LSB模式*/if (ioctl(fd, SPI_IOC_WR_LSB_FIRST, &lsb) < 0) {            perror("SPI rd_lsb_fist");            return; }

讀取和設(shè)置字長

字長是每次 SPI 傳輸中發(fā)送或接收的數(shù)據(jù)位數(shù),由 SPI 主設(shè)備或從設(shè)備指定。一般情況下,字長的值為 8, 16, 24 或 32。

這個命令對于應(yīng)用程序讀取和設(shè)置 SPI 總線的字長非常有用。通過此命令,應(yīng)用程序可以動態(tài)地獲取 SPI 總線的字長,以根據(jù)不同的需求來發(fā)送和接收可變長度的數(shù)據(jù)。

//使用到的宏SPI_IOC_RD_BITS_PER_WORDSPI_IOC_WR_BITS_PER_WORD//偽代碼/*讀取字長*/u8 bits;fd = open("/dev/spidevx.y",O_RDWR);if (ioctl(fd, SPI_IOC_RD_LSB_FIRST, &bits) < 0) {            perror("SPI rd_lsb_fist");            return; }/*設(shè)置字長*/if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits) < 0) {            perror("SPI rd_lsb_fist");            return; }

讀取和設(shè)置SPI傳輸速度

默認的SPI傳輸速度是在設(shè)備樹定義的,應(yīng)用程序從此可以設(shè)置SPI速率了

//使用到的宏SPI_IOC_RD_MAX_SPEED_HZ  SPI_IOC_WR_MAX_SPEED_HZ//偽代碼/*讀取字長*/u8 bits;fd = open("/dev/spidevx.y",O_RDWR);if (ioctl(fd, SPI_IOC_RD_LSB_FIRST, &bits) < 0) {            perror("SPI rd_lsb_fist");            return; }/*設(shè)置字長*/if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits) < 0) {            perror("SPI rd_lsb_fist");            return; }

發(fā)送和接收數(shù)據(jù)

在這里,spidev驅(qū)動的write和read方法是辦雙工的,意味著同時只能發(fā)或接收。

但是ioctl方法是可以全雙工的,如果我們的設(shè)備需要全雙工,可以使用ioctl.

應(yīng)用程序使用struct spi_ioc_transfer表示一個傳輸,和驅(qū)動的spi_transfer差不多

struct spi_ioc_transfer { __u64  tx_buf; __u64  rx_buf; __u32  len; __u32  speed_hz; __u16  delay_usecs; __u8  bits_per_word; __u8  cs_change; __u8  tx_nbits; __u8  rx_nbits; __u8  word_delay_usecs; __u8  pad;};
全雙工通信

使用SPI_IOC_MESSAGE命令

大多數(shù)SPI傳輸都使用8位長,因此下面的例子使用的tx_buf,rx_buf緩沖區(qū)數(shù)據(jù)是8位的.

uint8_t tx[25] = {0x55};uint8_t rx[ARRAY_SIZE(tx)] = {0, };struct spi_ioc_transfer tr = {        .tx_buf = (unsigned long)tx,        .rx_buf = (unsigned long)rx,        .len = ARRAY_SIZE(tx),        .delay_usecs = delay,        .speed_hz = 20000000,        .bits_per_word = 8,    };   ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {        if (!(ret % 6))            puts("");        printf("%.2X ", rx[ret]);    }    puts("");

定義發(fā)送和接收緩沖區(qū),構(gòu)造spi_ioc_transfer,使用ioctl完成全雙工命令。

SPI_IOC_MESSAGE(1)表示發(fā)送多少個spi_ioc_transfer,上述例子發(fā)送了一個spi_ioc_transfer,對應(yīng)一個spi_transfer。

spidev_fdx.c中發(fā)送了兩個

2.半雙工通信

使用到write和read系統(tǒng)讀寫spidev的都是半雙工通信的,直接調(diào)用即可。spidev自動構(gòu)造spi core能夠使用的spi_massage。

一次write對應(yīng)一個spi_massage,傳入的數(shù)據(jù)量就是spi_transfer的長度。

一些開源工具

在buildroot中可以找到關(guān)于SPI的一些開源工具,如spi-config、spi-pipe。

還有兩個在內(nèi)核源碼中可以找到,進入make就可以編譯得到

tools/spi/spidev_fdx.c

tools/spi/spidev_test.c

總之這些工具都基于spidev通用設(shè)備驅(qū)動以及對應(yīng)的ioctl命令實現(xiàn)。因此在使用這些工具的時候,最好用的是spidev而不是自己寫的驅(qū)動。

標(biāo)簽:

上一篇:linux內(nèi)核使用鏈接腳本模仿module_init機制實戰(zhàn)
下一篇:最后一頁