0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

迅为RK3568开发板驱动指南Linux中通用SPI设备驱动

北京迅为电子 2025-01-23 11:02 次阅读

在前面的章节中我们从0开始编写了一个mcp2515的驱动程序,而跟I2C设备类似,在Linux内核中也有着通用SPI设备驱动,在本章节将会讲解通用SPI设备驱动的使用,并讲解如何在应用程序中通过ioctl对SPI进行配置和使用。

硬件:迅为RK3568开发板

wKgZO2eRsRWAO_PYAAYUE3Njxow482.pngwKgZPGeRsRWAFKbOAAMSa4gY_0A030.png

193.1内核和设备树配置

通用SPI设备驱动在迅为提供的Linux内核中默认已经勾选了,具体路径如下所示:

> Device Drivers

> SPI support

wKgZPGeRsRSAN_IXAAL03SQ6qFk272.png

除了内核支持之外,还需要修改设备树,由于之前已经使能了SPI0,所以这直接修改之前编写的mcp2515设备树节点,具体设备树为“kernel/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi”,修改完成的mcp2515节点如下所示: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";

};

};

wKgZO2eRsROAHmMuAADtryB9TdU540.png

保存退出之后,重新编译内核源码,最后将编译得到的boot.img烧写到开发板上。

而为了方便起见,迅为已经将修改完成的设备树以及编译完成的内核镜像放到了“iTOP-3568开发板\03_【iTOP-RK3568开发板】指南教程\02_Linux驱动配套资料\04_Linux驱动程序\119_mcp2515_07\01_编译好的内核镜像”路径下。

开发板启动之后,如果存在/dev/spidev0.0设备节点,证明设备树及内核配置正确,如下图所示:

wKgZO2eRsROAbwmZAAA1nnjEy94138.png

/dev/spidev0.0表示一个SPI总线上的具体设备。0.0是一个标识符,用于区分系统中的不同SPI控制器和设备。这个标识符由两部分组成:

第一个数字 0:表示SPI总线的编号。一个系统中可能有多个SPI控制器,每个控制器对应一个总线编号,从0开始。

第二个数字0:表示连接在该SPI总线上的具体设备编号。一个SPI总线上可以连接多个设备,每个设备通过片选信号(Chip Select, CS)进行区分,设备编号从0开始。

在下个小节中,将会讲解内核源码中携带的spidev_test SPI测试程序进行讲解。

193.2 spidev_test工具使用

spidev_test是一个用于测试和调试SPI设备的命令行工具,通常在Linux系统上使用,它允许用户直接通过SPI总线与设备进行通信,可以发送数据并接收来自设备的响应。

spidev_test源码位于Linux源码的kernel/tools/spi目录下,如下图所示:

wKgZPGeRsROAdSeZAACBKDW3zwQ742.png

然后使用以下命令对该工具进行交叉编译

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

wKgZO2eRsRSAfrO2AAKlorNd3OY566.png

编译好的文件如下图所示。

wKgZPGeRsROAGWr5AACCgwV305E173.png

然后将编译好的可执行文件spidev_fdx和spidev_test拷贝到开发板上使用即可。接下来介绍一下工具的使用方法

1.spidev_test工具的使用:

基本介绍:spidev_test是一个用于测试和验证Linux中SPI设备驱动程序的用户空间工具。它使用spidev接口与SPI设备通信。这个工具主要用来检查SPI设备是否工作正常,以及对SPI设备进行基本的读写操作。

主要选项和参数

-D /dev/spidevX.Y:指定要测试的SPI设备节点。

-s :设置SPI时钟频率(以Hz为单位),例如-s 1000000表示1 MHz。

-d :设置数据传输之间的延迟时间(以微秒为单位)。

-b :设置每个数据字的位数,通常是8或16。

-H:以十六进制模式显示传输的数据。

(3)示例操作

读取设备信息

spidev_test -D /dev/spidevX.Y -s 1000000

wKgZO2eRsROAfxj0AABfc6mTyUM391.png

这会使用 1 MHz的时钟频率从SPI设备读取数据,默认情况下以十六进制显示。

写入和读取数据:

spidev_test -D /dev/spidevX.Y -s 1000000 -b 8 -d 1000 -H -p 'hello'

wKgZPGeRsROAKJk7AABLUUOK5SA732.png

这条命令会向 SPI设备写入字符串'hello',并以十六进制模式显示设备的响应数据。-b 8指定每个字的位数为8,-d 1000设置1000微秒的延迟。

连续传输:

spidev_test -D /dev/spidevX.Y -s 1000000 -b 8 -p 'abcdefgh'

这个示例将连续发送字节 'abcdefgh'到SPI设备。

wKgZO2eRsRSAN2MBAABXRg26e3I839.png

2.spidev_fdx工具的使用

(1)基本介绍:spidev_fdx是一个用于全双工SPI通信测试的命令行工具,主要用于在Linux系统上与SPI设备进行双向数据传输和测试。

(2)主要选项和参数

-D /dev/spidevX.Y:指定要测试的SPI设备节点。

-s :设置SPI时钟频率(以Hz为单位),例如-s 1000000表示1 MHz。

-w :指定要写入到SPI设备的数据,可以是十六进制或ASCII格式的字符串。

-r :指定从SPI设备读取的数据大小(以字节为单位)。

-b :设置每个数据字的位数,通常是8或16。

-d :设置数据传输之间的延迟时间(以微秒为单位)。

(3)示例操作

以下是几个使用 spidev_fdx工具的示例操作:

发送和接收数据:

spidev_fdx -D /dev/spidevX.Y -s 1000000 -w 'hello' -r 5

这会向 SPI设备写入字符串'hello',并从设备读取5个字节的响应数据。

设置时钟频率和延迟:

spidev_fdx -D /dev/spidevX.Y -s 500000 -d 200 -w 'abcdef' -r 10

这个示例将 SPI时钟频率设置为500 kHz,数据写入延迟为200微秒,并向设备写入字符串'abcdef',然后读取10个字节的响应数据。

193.3应用程序中如何使用SPI

在第一个小节中使能了内核中的通用SPI,而在第二小节讲解了spidev_test工具的使用,在本小节将根据spidev_test工具的源码,编写mcp2515通用SPI驱动程序的应用程序。

在应用程序中可以通过ioctl来获取和配置SPI的相关属性,并实现SPI数据的发送和接收,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低位优先

#define SPI_IOC_WR_LSB_FIRST _IOW(SPI_IOC_MAGIC, 2, __u8) //写入SPI低位优先

/*读取/写入SPI设备字长(1..N)*/

#define SPI_IOC_RD_BITS_PER_WORD _IOR(SPI_IOC_MAGIC, 3, __u8) //读取SPI每字位数

#define SPI_IOC_WR_BITS_PER_WORD _IOW(SPI_IOC_MAGIC, 3, __u8) //写入SPI每字位数

/*读取/写入SPI设备默认最大速度(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设备进行初始化,编写完成的初始化函数如下所示:

int fd; // SPI设备文件描述符

int mode = SPI_MODE_0; // SPI模式

int bits = 8; //每字比特数

int speed = 10000000; //最大SPI总线速度(Hz)

int spi_init(void){

int ret;

//打开SPI设备文件

fd = open("/dev/spidev0.0", O_RDWR);

if(fd < 0){

printf("打开/dev/spidev0.0错误\n");

return -1;

}

/*

*设置SPI模式

*/

ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);

if (ret == -1)

printf("无法设置SPI模式\n");

ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);

if (ret == -1)

printf("无法获取SPI模式\n");

/*

*设置每字比特数

*/

ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);

if (ret == -1)

printf("无法设置每字比特数\n");

ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);

if (ret == -1)

printf("无法获取每字比特数\n");

/*

*设置最大传输速度

*/

ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

if (ret == -1)

printf("无法设置最大传输速度\n");

ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);

if (ret == -1)

printf("无法获取最大传输速度\n");

printf("SPI模式: 0x%x\n", mode);

printf("每字比特数: %d\n", bits);

printf("最大速度: %d Hz (%d KHz)\n", speed, speed / 1000);

return 0;

}

通过该函数可以设置SPI的模式、比特数以及最大传输速度,然后根据spidev_test工具源码的传输函数来编写传输函数,具体函数内容如下所示:

/*

*执行SPI数据传输.

*参数:

* fd - SPI设备文件描述符

* tx -发送缓冲区

* rx -接收缓冲区

* len -数据长度

*返回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("无法发送SPI消息\n");

return -1;

}

return 0;

}

在前面的章节中一步步的编写了mcp2515的复位函数、配置函数、读函数和写函数,而现在可以直接在应用程序通过刚刚编写的传输函数向SPI设备发送一系列的SPI指令,一个编写完成的mcp2515的应用程序代码如下所示:

#include

#include

#include

#include

#include

#include

#include

#define RESET 0xc0 //复位命令

#define CANSTAT 0x0e // CAN状态寄存器地址

#define READ 0x03 //读命令

#define CANCTRL 0x0f // CAN控制寄存器地址

#define WRITE 0x02 //写命令

int fd; // SPI设备文件描述符

int mode = SPI_MODE_0; // SPI模式

int bits = 8; //每字比特数

int speed = 10000000; //最大SPI总线速度(Hz)

int delay; //延迟时间(微秒)

/*

*初始化SPI通信.

*返回0表示成功,-1表示失败.

*/

int spi_init(void){

int ret;

//打开SPI设备文件

fd = open("/dev/spidev0.0", O_RDWR);

if(fd < 0){

printf("打开/dev/spidev0.0错误\n");

return -1;

}

/*

*设置SPI模式

*/

ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);

if (ret == -1)

printf("无法设置SPI模式\n");

ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);

if (ret == -1)

printf("无法获取SPI模式\n");

/*

*设置每字比特数

*/

ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);

if (ret == -1)

printf("无法设置每字比特数\n");

ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);

if (ret == -1)

printf("无法获取每字比特数\n");

/*

*设置最大传输速度

*/

ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

if (ret == -1)

printf("无法设置最大传输速度\n");

ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);

if (ret == -1)

printf("无法获取最大传输速度\n");

printf("SPI模式: 0x%x\n", mode);

printf("每字比特数: %d\n", bits);

printf("最大速度: %d Hz (%d KHz)\n", speed, speed / 1000);

return 0;

}

/*

*执行SPI数据传输.

*参数:

* fd - SPI设备文件描述符

* tx -发送缓冲区

* rx -接收缓冲区

* len -数据长度

*返回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("无法发送SPI消息\n");

return -1;

}

return 0;

}

int main(int argc, char *argv[]){

char reset_cmd[1] = {RESET}; //复位命令数组

char rd_canstat[2] = {READ, CANSTAT}; //读CAN状态寄存器命令数组

char canstat[4] = {0}; //存储CAN状态的缓冲区

char wr_canctrl[] = {WRITE, CANCTRL, 0x00}; //写CAN控制寄存器命令数组

//初始化SPI通信

spi_init();

//执行SPI数据传输

// 1.发送复位命令

transfer(fd, reset_cmd, NULL, sizeof(reset_cmd));

// 2.读取CAN状态

transfer(fd, rd_canstat, canstat, sizeof(canstat));

printf("CAN状态为%x\n", canstat[2]);

//清空canstat缓冲区

memset(canstat, 0, sizeof(canstat));

// 3.写入CAN控制

transfer(fd, wr_canctrl, NULL, sizeof(wr_canctrl));

// 4.再次读取CAN状态

transfer(fd, rd_canstat, canstat, sizeof(canstat));

printf("CAN状态为%x\n", canstat[3]);

return 0;

}

193.4运行测试

193.4.1编译应用程序

上一小节编写好的app.c应用程序源码已经放在了“iTOP-3568开发板\03_【iTOP-RK3568开发板】指南教程\02_Linux驱动配套资料\04_Linux驱动程序\119_mcp2515_07\02_app”目录下。

首先进行应用程序的编译,因为测试APP是要在开发板上运行的,所以需要aarch64-linux-gnu-gcc来编译,输入以下命令,编译完成以后会生成一个app的可执行程序,如下图所示:

aarch64-linux-gnu-gcc app.c -o app

wKgZPGeRsRSANg0bAABeXHmAIYw381.png

然后将编译完成的可执行程序拷贝到开发板上.

193.4.2运行测试

首先将193.1小节编译好的内核镜像烧写到开发板上,然后将可执行程序app文件拷贝到开发板上,拷贝完成如下所示:

wKgZPGeRsRSALKUIAAArIZ7hC34845.png

然后运行可执行程序app,如下图所示。

wKgZO2eRsRSAf4wAAABwHyRZuaA939.png

在应用程序中,发送完复位指令之后,第一条打印can状态寄存器的值为80,表示mcp2515已经处在了配置模式。第二条打印can状态寄存器的值为00,表示mcp2515已经处于正常模式,这就说明上一小节编写的应用程序正常运行。

至此,关于通用SPI驱动和在应用程序中使用SPI实验就完成了。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    11347

    浏览量

    210437
  • 开发板
    +关注

    关注

    25

    文章

    5123

    浏览量

    98243
  • RK3568
    +关注

    关注

    4

    文章

    526

    浏览量

    5238
  • 迅为电子
    +关注

    关注

    0

    文章

    36

    浏览量

    62
收藏 人收藏

    评论

    相关推荐

    RK3568开发板鸿蒙OpenHarmony系统固件烧写步骤

    \02_iTOP-RK3568 开发板烧写工具及驱动”)的压缩包到 windows的任意路径,然后解压压缩包会得到 RKDevTool_Release_v2.84 文件夹拷贝鸿蒙镜像到烧写器
    发表于 08-26 17:45

    【教程上新】基于iTOP-RK3568开发板的OpenCV开发手册

    【教程上新】基于iTOP-RK3568开发板的OpenCV开发手册
    的头像 发表于 02-08 15:22 1192次阅读
    【教程上新】基于<b class='flag-5'>迅</b><b class='flag-5'>为</b>iTOP-<b class='flag-5'>RK3568</b><b class='flag-5'>开发板</b>的OpenCV<b class='flag-5'>开发</b>手册

    RK3568开发板资料20个+手册+配套视频教程

    RK3568开发板资料20个+手册+配套视频教程
    的头像 发表于 02-14 14:37 7584次阅读
    <b class='flag-5'>迅</b><b class='flag-5'>为</b><b class='flag-5'>RK3568</b><b class='flag-5'>开发板</b>资料20个+手册+配套视频教程

    基于RK3568开发板的嵌入式学习之Linux驱动视频

    基于RK3568开发板的嵌入式学习之Linux驱动视频
    的头像 发表于 05-19 16:30 1057次阅读
    <b class='flag-5'>迅</b><b class='flag-5'>为</b>基于<b class='flag-5'>RK3568</b><b class='flag-5'>开发板</b>的嵌入式学习之<b class='flag-5'>Linux</b><b class='flag-5'>驱动</b>视频

    RK3568开发板系统编程手册全新升级

    RK3568开发板系统编程手册全新升级
    的头像 发表于 06-25 15:50 1218次阅读
    <b class='flag-5'>迅</b><b class='flag-5'>为</b><b class='flag-5'>RK3568</b><b class='flag-5'>开发板</b>系统编程手册全新升级

    RK3568开发板GPIO之外接模块

    RK3568开发板GPIO之外接模块
    的头像 发表于 08-24 17:41 1488次阅读
    <b class='flag-5'>迅</b><b class='flag-5'>为</b><b class='flag-5'>RK3568</b><b class='flag-5'>开发板</b>GPIO之外接模块

    RK3568开发板可实现多屏异显控方案

    RK3568开发板可实现多屏异显控方案
    的头像 发表于 07-15 17:30 1939次阅读
    <b class='flag-5'>迅</b><b class='flag-5'>为</b><b class='flag-5'>RK3568</b><b class='flag-5'>开发板</b>可实现多屏异显控方案

    RK3568开发板外接模块之HX711 称重模块

    RK3568开发板外接模块之HX711 称重模块
    的头像 发表于 08-30 15:24 1707次阅读
    <b class='flag-5'>迅</b><b class='flag-5'>为</b><b class='flag-5'>RK3568</b><b class='flag-5'>开发板</b>外接模块之HX711 称重模块

    RK3568开发板Debian系统使用python 进行摄像头开发

    RK3568开发板Debian系统使用python 进行摄像头开发
    的头像 发表于 09-14 16:58 1674次阅读
    <b class='flag-5'>迅</b><b class='flag-5'>为</b><b class='flag-5'>RK3568</b><b class='flag-5'>开发板</b>Debian系统使用python 进行摄像头<b class='flag-5'>开发</b>

    【北京】itop-RK3568开发板驱动开发指南

    《iTOP-RK3568开发板驱动开发指南》更新,本次更新内容对应的是驱动(第六期_平台总线_全新升级)视频,后续资料会不断更新,不断完善,
    发表于 08-29 16:32 32次下载

    RK3568开发板助力智能车载产业快速发展

    RK3568开发板助力智能车载产业快速发展
    的头像 发表于 01-23 13:59 647次阅读
    <b class='flag-5'>迅</b><b class='flag-5'>为</b><b class='flag-5'>RK3568</b><b class='flag-5'>开发板</b>助力智能车载产业快速发展

    RK3568开发板驱动开发指南-输入子系统

    RK3568开发板驱动开发指南-输入子系统
    的头像 发表于 02-23 15:11 1003次阅读
    <b class='flag-5'>迅</b><b class='flag-5'>为</b><b class='flag-5'>RK3568</b><b class='flag-5'>开发板</b><b class='flag-5'>驱动</b><b class='flag-5'>开发指南</b>-输入子系统

    北京RK3568开发板嵌入式学习之Linux驱动全新更新-CAN+

    北京RK3568开发板嵌入式学习之Linux驱动全新更新-CAN+
    的头像 发表于 09-04 15:29 622次阅读
    北京<b class='flag-5'>迅</b><b class='flag-5'>为</b><b class='flag-5'>RK3568</b><b class='flag-5'>开发板</b>嵌入式学习之<b class='flag-5'>Linux</b><b class='flag-5'>驱动</b>全新更新-CAN+

    iTOP-RK3568/RK3588开发板获麒麟软件适配认证

    iTOP-RK3568/RK3588开发板获麒麟软件适配认证
    的头像 发表于 10-18 14:56 635次阅读
    <b class='flag-5'>迅</b><b class='flag-5'>为</b>iTOP-<b class='flag-5'>RK3568</b>/<b class='flag-5'>RK</b>3588<b class='flag-5'>开发板</b>获麒麟软件适配认证

    RK3568开发板SPI驱动指南-mcp2515驱动编写:读寄存器函数

    RK3568开发板SPI驱动指南-mcp2515
    的头像 发表于 01-20 14:43 325次阅读
    <b class='flag-5'>迅</b><b class='flag-5'>为</b><b class='flag-5'>RK3568</b><b class='flag-5'>开发板</b><b class='flag-5'>SPI</b><b class='flag-5'>驱动</b><b class='flag-5'>指南</b>-mcp2515<b class='flag-5'>驱动</b>编写:读寄存器函数