在前面的章節(jié)中我們從0開始編寫了一個mcp2515的驅(qū)動程序,而跟I2C設(shè)備類似,在Linux內(nèi)核中也有著通用SPI設(shè)備驅(qū)動,在本章節(jié)將會講解通用SPI設(shè)備驅(qū)動的使用,并講解如何在應(yīng)用程序中通過ioctl對SPI進行配置和使用。
硬件:迅為RK3568開發(fā)板


193.1內(nèi)核和設(shè)備樹配置
通用SPI設(shè)備驅(qū)動在迅為提供的Linux內(nèi)核中默認已經(jīng)勾選了,具體路徑如下所示:
> Device Drivers
> SPI support

除了內(nèi)核支持之外,還需要修改設(shè)備樹,由于之前已經(jīng)使能了SPI0,所以這直接修改之前編寫的mcp2515設(shè)備樹節(jié)點,具體設(shè)備樹為“kernel/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi”,修改完成的mcp2515節(jié)點如下所示:rockchip,spidev
&spi0 {
status = "okay";
pinctrl-0 = <&spi0m1_cs0 ?&spi0m1_pins>;
pinctrl-1 = <&spi0m1_cs0 ?&spi0m1_pins_hs>;
mcp2515:mcp2515@0 {
compatible = "rockchip,spidev";
reg = <0>;
spi-max-frequency = <10000000>;
status = "okay";
};
};

保存退出之后,重新編譯內(nèi)核源碼,最后將編譯得到的boot.img燒寫到開發(fā)板上。
而為了方便起見,迅為已經(jīng)將修改完成的設(shè)備樹以及編譯完成的內(nèi)核鏡像放到了“iTOP-3568開發(fā)板\03_【iTOP-RK3568開發(fā)板】指南教程\02_Linux驅(qū)動配套資料\04_Linux驅(qū)動程序\119_mcp2515_07\01_編譯好的內(nèi)核鏡像”路徑下。
開發(fā)板啟動之后,如果存在/dev/spidev0.0設(shè)備節(jié)點,證明設(shè)備樹及內(nèi)核配置正確,如下圖所示:

/dev/spidev0.0表示一個SPI總線上的具體設(shè)備。0.0是一個標(biāo)識符,用于區(qū)分系統(tǒng)中的不同SPI控制器和設(shè)備。這個標(biāo)識符由兩部分組成:
第一個數(shù)字 0:表示SPI總線的編號。一個系統(tǒng)中可能有多個SPI控制器,每個控制器對應(yīng)一個總線編號,從0開始。
第二個數(shù)字0:表示連接在該SPI總線上的具體設(shè)備編號。一個SPI總線上可以連接多個設(shè)備,每個設(shè)備通過片選信號(Chip Select, CS)進行區(qū)分,設(shè)備編號從0開始。
在下個小節(jié)中,將會講解內(nèi)核源碼中攜帶的spidev_test SPI測試程序進行講解。
193.2 spidev_test工具使用
spidev_test是一個用于測試和調(diào)試SPI設(shè)備的命令行工具,通常在Linux系統(tǒng)上使用,它允許用戶直接通過SPI總線與設(shè)備進行通信,可以發(fā)送數(shù)據(jù)并接收來自設(shè)備的響應(yīng)。
spidev_test源碼位于Linux源碼的kernel/tools/spi目錄下,如下圖所示:

然后使用以下命令對該工具進行交叉編譯
make CC=/home/topeet/Linux/linux_sdk/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc LD=/home/topeet/Linux/linux_sdk/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-ld

編譯好的文件如下圖所示。

然后將編譯好的可執(zhí)行文件spidev_fdx和spidev_test拷貝到開發(fā)板上使用即可。接下來介紹一下工具的使用方法
1.spidev_test工具的使用:
基本介紹:spidev_test是一個用于測試和驗證Linux中SPI設(shè)備驅(qū)動程序的用戶空間工具。它使用spidev接口與SPI設(shè)備通信。這個工具主要用來檢查SPI設(shè)備是否工作正常,以及對SPI設(shè)備進行基本的讀寫操作。
主要選項和參數(shù)
-D /dev/spidevX.Y:指定要測試的SPI設(shè)備節(jié)點。
-s :設(shè)置SPI時鐘頻率(以Hz為單位),例如-s 1000000表示1 MHz。
-d :設(shè)置數(shù)據(jù)傳輸之間的延遲時間(以微秒為單位)。
-b :設(shè)置每個數(shù)據(jù)字的位數(shù),通常是8或16。
-H:以十六進制模式顯示傳輸?shù)臄?shù)據(jù)。
(3)示例操作
讀取設(shè)備信息:
spidev_test -D /dev/spidevX.Y -s 1000000

這會使用 1 MHz的時鐘頻率從SPI設(shè)備讀取數(shù)據(jù),默認情況下以十六進制顯示。
寫入和讀取數(shù)據(jù):
spidev_test -D /dev/spidevX.Y -s 1000000 -b 8 -d 1000 -H -p 'hello'

這條命令會向 SPI設(shè)備寫入字符串'hello',并以十六進制模式顯示設(shè)備的響應(yīng)數(shù)據(jù)。-b 8指定每個字的位數(shù)為8,-d 1000設(shè)置1000微秒的延遲。
連續(xù)傳輸:
spidev_test -D /dev/spidevX.Y -s 1000000 -b 8 -p 'abcdefgh'
這個示例將連續(xù)發(fā)送字節(jié) 'abcdefgh'到SPI設(shè)備。

2.spidev_fdx工具的使用
(1)基本介紹:spidev_fdx是一個用于全雙工SPI通信測試的命令行工具,主要用于在Linux系統(tǒng)上與SPI設(shè)備進行雙向數(shù)據(jù)傳輸和測試。
(2)主要選項和參數(shù)
-D /dev/spidevX.Y:指定要測試的SPI設(shè)備節(jié)點。
-s :設(shè)置SPI時鐘頻率(以Hz為單位),例如-s 1000000表示1 MHz。
-w :指定要寫入到SPI設(shè)備的數(shù)據(jù),可以是十六進制或ASCII格式的字符串。
-r :指定從SPI設(shè)備讀取的數(shù)據(jù)大?。ㄒ宰止?jié)為單位)。
-b :設(shè)置每個數(shù)據(jù)字的位數(shù),通常是8或16。
-d :設(shè)置數(shù)據(jù)傳輸之間的延遲時間(以微秒為單位)。
(3)示例操作
以下是幾個使用 spidev_fdx工具的示例操作:
發(fā)送和接收數(shù)據(jù):
spidev_fdx -D /dev/spidevX.Y -s 1000000 -w 'hello' -r 5
這會向 SPI設(shè)備寫入字符串'hello',并從設(shè)備讀取5個字節(jié)的響應(yīng)數(shù)據(jù)。
設(shè)置時鐘頻率和延遲:
spidev_fdx -D /dev/spidevX.Y -s 500000 -d 200 -w 'abcdef' -r 10
這個示例將 SPI時鐘頻率設(shè)置為500 kHz,數(shù)據(jù)寫入延遲為200微秒,并向設(shè)備寫入字符串'abcdef',然后讀取10個字節(jié)的響應(yīng)數(shù)據(jù)。
193.3應(yīng)用程序中如何使用SPI
在第一個小節(jié)中使能了內(nèi)核中的通用SPI,而在第二小節(jié)講解了spidev_test工具的使用,在本小節(jié)將根據(jù)spidev_test工具的源碼,編寫mcp2515通用SPI驅(qū)動程序的應(yīng)用程序。
在應(yīng)用程序中可以通過ioctl來獲取和配置SPI的相關(guān)屬性,并實現(xiàn)SPI數(shù)據(jù)的發(fā)送和接收,SPI的ioctl宏定義在“/usr/include/linux/spi/spidev.h”,部分ioctl cmd如下所示:
/*讀取/寫入SPI模式(SPI_MODE_0..SPI_MODE_3)(限制為8位)*/
#define SPI_IOC_RD_MODE _IOR(SPI_IOC_MAGIC, 1, __u8) //讀取SPI模式
#define SPI_IOC_WR_MODE _IOW(SPI_IOC_MAGIC, 1, __u8) //寫入SPI模式
/*讀取/寫入SPI位順序*/
#define SPI_IOC_RD_LSB_FIRST _IOR(SPI_IOC_MAGIC, 2, __u8) //讀取SPI低位優(yōu)先
#define SPI_IOC_WR_LSB_FIRST _IOW(SPI_IOC_MAGIC, 2, __u8) //寫入SPI低位優(yōu)先
/*讀取/寫入SPI設(shè)備字長(1..N)*/
#define SPI_IOC_RD_BITS_PER_WORD _IOR(SPI_IOC_MAGIC, 3, __u8) //讀取SPI每字位數(shù)
#define SPI_IOC_WR_BITS_PER_WORD _IOW(SPI_IOC_MAGIC, 3, __u8) //寫入SPI每字位數(shù)
/*讀取/寫入SPI設(shè)備默認最大速度(Hz)*/
#define SPI_IOC_RD_MAX_SPEED_HZ _IOR(SPI_IOC_MAGIC, 4, __u32) //讀取SPI最大速度(Hz)
#define SPI_IOC_WR_MAX_SPEED_HZ _IOW(SPI_IOC_MAGIC, 4, __u32) //寫入SPI最大速度(Hz)
/*讀取/寫入SPI模式字段*/
#define SPI_IOC_RD_MODE32 _IOR(SPI_IOC_MAGIC, 5, __u32) //讀取SPI模式(32位)
#define SPI_IOC_WR_MODE32 _IOW(SPI_IOC_MAGIC, 5, __u32) //寫入SPI模式(32位)
可以通過上述ioctl cmd來對SPI設(shè)備進行初始化,編寫完成的初始化函數(shù)如下所示:
int fd; // SPI設(shè)備文件描述符
int mode = SPI_MODE_0; // SPI模式
int bits = 8; //每字比特數(shù)
int speed = 10000000; //最大SPI總線速度(Hz)
int spi_init(void){
int ret;
//打開SPI設(shè)備文件
fd = open("/dev/spidev0.0", O_RDWR);
if(fd < 0){
printf("打開/dev/spidev0.0錯誤\n");
return -1;
}
/*
*設(shè)置SPI模式
*/
ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
if (ret == -1)
printf("無法設(shè)置SPI模式\n");
ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
if (ret == -1)
printf("無法獲取SPI模式\n");
/*
*設(shè)置每字比特數(shù)
*/
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1)
printf("無法設(shè)置每字比特數(shù)\n");
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1)
printf("無法獲取每字比特數(shù)\n");
/*
*設(shè)置最大傳輸速度
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1)
printf("無法設(shè)置最大傳輸速度\n");
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1)
printf("無法獲取最大傳輸速度\n");
printf("SPI模式: 0x%x\n", mode);
printf("每字比特數(shù): %d\n", bits);
printf("最大速度: %d Hz (%d KHz)\n", speed, speed / 1000);
return 0;
}
通過該函數(shù)可以設(shè)置SPI的模式、比特數(shù)以及最大傳輸速度,然后根據(jù)spidev_test工具源碼的傳輸函數(shù)來編寫傳輸函數(shù),具體函數(shù)內(nèi)容如下所示:
/*
*執(zhí)行SPI數(shù)據(jù)傳輸.
*參數(shù):
* fd - SPI設(shè)備文件描述符
* tx -發(fā)送緩沖區(qū)
* rx -接收緩沖區(qū)
* len -數(shù)據(jù)長度
*返回0表示成功,-1表示失敗.
*/
int transfer(int fd, char *tx, char *rx, int len)
{
int ret;
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = len,
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1){
printf("無法發(fā)送SPI消息\n");
return -1;
}
return 0;
}
在前面的章節(jié)中一步步的編寫了mcp2515的復(fù)位函數(shù)、配置函數(shù)、讀函數(shù)和寫函數(shù),而現(xiàn)在可以直接在應(yīng)用程序通過剛剛編寫的傳輸函數(shù)向SPI設(shè)備發(fā)送一系列的SPI指令,一個編寫完成的mcp2515的應(yīng)用程序代碼如下所示:
#include
#include
#include
#include
#include
#include
#include
#define RESET 0xc0 //復(fù)位命令
#define CANSTAT 0x0e // CAN狀態(tài)寄存器地址
#define READ 0x03 //讀命令
#define CANCTRL 0x0f // CAN控制寄存器地址
#define WRITE 0x02 //寫命令
int fd; // SPI設(shè)備文件描述符
int mode = SPI_MODE_0; // SPI模式
int bits = 8; //每字比特數(shù)
int speed = 10000000; //最大SPI總線速度(Hz)
int delay; //延遲時間(微秒)
/*
*初始化SPI通信.
*返回0表示成功,-1表示失敗.
*/
int spi_init(void){
int ret;
//打開SPI設(shè)備文件
fd = open("/dev/spidev0.0", O_RDWR);
if(fd < 0){
printf("打開/dev/spidev0.0錯誤\n");
return -1;
}
/*
*設(shè)置SPI模式
*/
ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
if (ret == -1)
printf("無法設(shè)置SPI模式\n");
ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
if (ret == -1)
printf("無法獲取SPI模式\n");
/*
*設(shè)置每字比特數(shù)
*/
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1)
printf("無法設(shè)置每字比特數(shù)\n");
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1)
printf("無法獲取每字比特數(shù)\n");
/*
*設(shè)置最大傳輸速度
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1)
printf("無法設(shè)置最大傳輸速度\n");
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1)
printf("無法獲取最大傳輸速度\n");
printf("SPI模式: 0x%x\n", mode);
printf("每字比特數(shù): %d\n", bits);
printf("最大速度: %d Hz (%d KHz)\n", speed, speed / 1000);
return 0;
}
/*
*執(zhí)行SPI數(shù)據(jù)傳輸.
*參數(shù):
* fd - SPI設(shè)備文件描述符
* tx -發(fā)送緩沖區(qū)
* rx -接收緩沖區(qū)
* len -數(shù)據(jù)長度
*返回0表示成功,-1表示失敗.
*/
int transfer(int fd, char *tx, char *rx, int len)
{
int ret;
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = len,
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1){
printf("無法發(fā)送SPI消息\n");
return -1;
}
return 0;
}
int main(int argc, char *argv[]){
char reset_cmd[1] = {RESET}; //復(fù)位命令數(shù)組
char rd_canstat[2] = {READ, CANSTAT}; //讀CAN狀態(tài)寄存器命令數(shù)組
char canstat[4] = {0}; //存儲CAN狀態(tài)的緩沖區(qū)
char wr_canctrl[] = {WRITE, CANCTRL, 0x00}; //寫CAN控制寄存器命令數(shù)組
//初始化SPI通信
spi_init();
//執(zhí)行SPI數(shù)據(jù)傳輸
// 1.發(fā)送復(fù)位命令
transfer(fd, reset_cmd, NULL, sizeof(reset_cmd));
// 2.讀取CAN狀態(tài)
transfer(fd, rd_canstat, canstat, sizeof(canstat));
printf("CAN狀態(tài)為%x\n", canstat[2]);
//清空canstat緩沖區(qū)
memset(canstat, 0, sizeof(canstat));
// 3.寫入CAN控制
transfer(fd, wr_canctrl, NULL, sizeof(wr_canctrl));
// 4.再次讀取CAN狀態(tài)
transfer(fd, rd_canstat, canstat, sizeof(canstat));
printf("CAN狀態(tài)為%x\n", canstat[3]);
return 0;
}
193.4運行測試
193.4.1編譯應(yīng)用程序
上一小節(jié)編寫好的app.c應(yīng)用程序源碼已經(jīng)放在了“iTOP-3568開發(fā)板\03_【iTOP-RK3568開發(fā)板】指南教程\02_Linux驅(qū)動配套資料\04_Linux驅(qū)動程序\119_mcp2515_07\02_app”目錄下。
首先進行應(yīng)用程序的編譯,因為測試APP是要在開發(fā)板上運行的,所以需要aarch64-linux-gnu-gcc來編譯,輸入以下命令,編譯完成以后會生成一個app的可執(zhí)行程序,如下圖所示:
aarch64-linux-gnu-gcc app.c -o app

然后將編譯完成的可執(zhí)行程序拷貝到開發(fā)板上.
193.4.2運行測試
首先將193.1小節(jié)編譯好的內(nèi)核鏡像燒寫到開發(fā)板上,然后將可執(zhí)行程序app文件拷貝到開發(fā)板上,拷貝完成如下所示:

然后運行可執(zhí)行程序app,如下圖所示。

在應(yīng)用程序中,發(fā)送完復(fù)位指令之后,第一條打印can狀態(tài)寄存器的值為80,表示mcp2515已經(jīng)處在了配置模式。第二條打印can狀態(tài)寄存器的值為00,表示mcp2515已經(jīng)處于正常模式,這就說明上一小節(jié)編寫的應(yīng)用程序正常運行。
至此,關(guān)于通用SPI驅(qū)動和在應(yīng)用程序中使用SPI實驗就完成了。
-
Linux
+關(guān)注
關(guān)注
87文章
11456瀏覽量
212751 -
開發(fā)板
+關(guān)注
關(guān)注
25文章
5499瀏覽量
102141 -
RK3568
+關(guān)注
關(guān)注
5文章
575瀏覽量
6069 -
迅為電子
+關(guān)注
關(guān)注
0文章
55瀏覽量
141
發(fā)布評論請先 登錄
北京迅為RK3568開發(fā)板OpenHarmony系統(tǒng)南向驅(qū)動開發(fā)內(nèi)核HDF驅(qū)動框架架構(gòu)

迅為RK3568開發(fā)板驅(qū)動指南GPIO子系統(tǒng)三級節(jié)點操作函數(shù)實驗

北京迅為RK3568開發(fā)板嵌入式學(xué)習(xí)之Linux驅(qū)動全新更新-CAN+

北京迅為iTOP-RK3568開發(fā)板OpenHarmony系統(tǒng)南向驅(qū)動開發(fā)實操-HDF驅(qū)動配置LED

【北京迅為】iTOP-RK3568開發(fā)板鴻蒙OpenHarmony系統(tǒng)南向驅(qū)動開發(fā)實操-HDF驅(qū)動配置UART

迅為RK3568驅(qū)動指南GPIO子系統(tǒng) GPIO操作函數(shù)實驗

迅為RK3568開發(fā)板驅(qū)動指南GPIO子系統(tǒng)GPIO子系統(tǒng)API函數(shù)的引入

評論