Linux下SPI设备驱动实验:使用内核提供的读写SPI设备中的数据的函数

一. 简介

前面文章的学习,已经实现了 读写SPI设备中数据的功能。文章如下:

Linux下SPI设备驱动实验:验证读写SPI设备中数据的函数功能-CSDN博客

本文来使用内核提供的读写SPI设备中的数据的API函数,来实现读写SPI设备中数据。

二. Linux下SPI设备驱动实验:使用内核提供的读写SPI设备中的数据的函数

1. 内核提供的读写SPI设备中的数据的函数

所使用内核源码为NXP官方提供。读写SPI设备中的数据的函数所在内核源码路径为:

复制代码
/linux-imx-rel_imx_4.1.15_2.1.0_ga/include/linux/spi/spi.h

(1) 读取SPI设备中数据的函数

spi_read函数
复制代码
int spi_read(struct spi_device *spi, void *buf, size_t len)
spi_write_then_read函数
复制代码
/* this copies txbuf and rxbuf data; for small transfers only! */
int spi_write_then_read(struct spi_device *spi, const void *txbuf, unsigned n_tx,
		void *rxbuf, unsigned n_rx);

(2) 向SPI设备中写数据的函数

复制代码
int spi_write(struct spi_device *spi, const void *buf, size_t len)

2. 使用内核提供的读写SPI设备中的数据的函数

这里的代码与前一篇文章相比,不同的是 读写SPI设备中数据的函数实现。

读写SPI设备中数据的函数实现如下(spi_icm20608.c文件中):

复制代码
static int spi_write_regs(struct icm20608_Dev* dev, u8 reg_addr, void* buf, int len)
{
    int ret = 0;
    unsigned char value = 0;
    struct spi_device* spi_dev = (struct spi_device*)dev->private_data;

    value = reg_addr & ~0x80; //最高为位清0(写标志位)
    //发送要写入的寄存器地址
    ret = spi_write(spi_dev, &value, 1); 
    if(ret)
        printk("spi_write_regs: spi_write reg_addr error\n");
    //发送要写入SPI设备中的数据
    ret = spi_write(spi_dev, buf, len); 
    if(ret)
        printk("spi_write_regs: spi_write  data error\n");
    return ret;
}

static int spi_read_regs(struct icm20608_Dev* dev, u8 reg_addr, void* buf, int len)
{
    int ret = 0;
    unsigned char value = 0;
    struct spi_device* spi_dev = (struct spi_device*)dev->private_data;
    
    value = reg_addr | 0x80; //最高为置1(读标志位)
#if  0    //发送要读取的寄存器的地址
    ret = spi_write(spi_dev, &value, 1);
    if(ret < 0)
        printk("spi_read_regs: spi_write reg_addr error\n");
    //接收SPI设备中的数据 
    ret = spi_read(spi_dev, buf, len);
    if(ret)
        printk("spi_read_regs: spi_read data error\n");
#endif
    spi_write_then_read(spi_dev, &value, 1, buf, len);
    return ret;
}

/*ICM20608设备初始化(即SPI设备初始化)*/
static int icm20608_register_init(struct icm20608_Dev* dev)
{
    unsigned char value = 0;

    spi_write_reg_onebyte(&icm20608_dev, ICM20_PWR_MGMT_1, 0x80); /*复位,复位后为0x40,睡眠模式 */
    mdelay(50);
    spi_write_reg_onebyte(&icm20608_dev, ICM20_PWR_MGMT_1, 0x01);  /*关闭睡眠,自动选择时钟 */
    mdelay(50);

    value = spi_read_reg_onebyte(&icm20608_dev,ICM20_WHO_AM_I);
    printk("ICM20_WHO_AM_I: 0x%02X\r\n", value);
    if((value != ICM20608G_ID) && (value != ICM20608D_ID))
    {
        return 1;
    }
    value = spi_read_reg_onebyte(&icm20608_dev,ICM20_PWR_MGMT_1);
    printk("ICM20_PWR_MGMT_1: 0x%02X\r\n", value);

    return 0;
}

可以看出,读SPI设备中数据的实现函数中,有一段屏蔽的代码段,如下代码段:

复制代码
    ret = spi_write(spi_dev, &value, 1);
    if(ret < 0)
        printk("spi_read_regs: spi_write reg_addr error\n");
    //接收SPI设备中的数据 
    ret = spi_read(spi_dev, buf, len);
    if(ret)
        printk("spi_read_regs: spi_read data error\n");

这里先写了SPI设备的寄存器地址,然后从SPI设备中读取数据。

经过测试,这段代码最后 SPI读取数据是不对的,也就是存在问题。正点原子的左神说是,可能的原因是 在发送寄存器地址前片选信号是拉低的,之后片选信号拉高了,然后在从SPI深圳中读取数据前,片选信号再拉低。这样导致时序混乱。

所以,这里从SPI设备中读取数据调用了 spi_write_then_read函数,不过根据该函数的注释信息,说这个函数只适用于小数量的数据传输!

三. 测试

将 编译的驱动进行编译后,将新生成的驱动模块拷贝到开发板系统中,加载驱动模块如下:

可以看出,寄存器ICM20_PWR_MGMT_1在 ICM20608初始化函数中最后写入了0x01,这里读出来也是 0x01,确定读写SPI设备函数运行正常。

卸载模块:

卸载驱动模块时, remove函数也运行了,也不存在错误的信息。

相关推荐
磨十三20 分钟前
ARM Cortex-M 系列 MCU:内核、指令、异常与中断解析
arm开发·单片机·嵌入式硬件
z***94842 小时前
Linux下安装Nginx服务及systemctl方式管理nginx详情
linux·运维·nginx
凉晓风2 小时前
Linux上TCP通信异常排查工具命令
linux·运维·tcp/ip
Xの哲學2 小时前
Linux 分区表深度技术剖析
linux·网络·算法·架构·边缘计算
码龄3年 审核中3 小时前
Linux record 03
java·linux·运维
星驰云3 小时前
记一次CentOS 硬盘损坏无法启动修复教程
linux·运维·centos
人工智能训练3 小时前
windows系统中的docker,xinference直接运行在容器目录和持载在宿主机目录中的区别
linux·服务器·人工智能·windows·ubuntu·docker·容器
q***47433 小时前
Windows 和 Linux 系统下,如何查看 Redis 的版本号?
linux·windows·redis
代码对我眨眼睛3 小时前
Ubuntu 系统 NVIDIA 显卡驱动自动化安装全流程
linux·ubuntu·自动化
xiong2learning3 小时前
Linux虚拟机无法使用u盘的一种可能-- 重新下载open-vm-tools + open-vm-tools-desktop解决
linux