STM32MP157驱动开发——SPI驱动

  • 一、简介
    • 1.SPI介绍
    • 2.STM32MP1 SPI介绍
    • 3. ICM-20608 简介
    • 4.Linux下的SPI框架
  • 二、驱动开发
    • 1)IO 的 pinctrl 子节点创建与修改
    • 2)SPI 设备节点的创建与修改
    • 3)ICM20608驱动
    • 4)测试App
    • 5)运行测试

参考文章:【正点原子】STM32MP1嵌入式Linux驱动开发——SPI总线框架

一、简介

   之前已经学习了 Linux 下的 platform 总线框架、 I2C 总线框架,这一节就学习第三种总线框架——SPI总线。与 I2C 总线一样,SPI 是物理总线,也是一种很常用的串行通信协议,本节将使用 SPI 框架对接开发板上的 ICM-20608 这个六轴传感器,可以在应用程序中读取 ICM-20608 的原始传感器数据。

1.SPI介绍

   I2C 是串行通信的一种,只需要两根线就可以完成主机和从机之间的通信,但是 I2C 的速度最高只能到 400KHz,如果对于访问速度要求比价高的话 I2C 就不适合了。
   SPI 全称是 Serial Perripheral Interface,也就是串行外围设备接口,是一种高速、全双工的同步通信总线,SPI 时钟频率相比 I2C 要高很多,最高可以工作在上百 MHz。SPI 以主从方式工作,通常是有一个主设备和一个或多个从设备,一般 SPI 需要 4 根线,但是也可以使用三根线(单向传输),本节介绍标准的 4 线 SPI:

  • CS/SS,Slave Select/Chip Select,片选信号线,用于选择需要进行通信的从设备。I2C 主机是通过发送从机设备地址来选择需要进行通信的从机设备, SPI 主机不需要发送从机设备,直接将相应的从机设备片选信号拉低即可
  • SCK, Serial Clock,串行时钟,和 I2C 的 SCL 一样,为 SPI 通信提供时钟
  • MOSI/SDO,Master Out Slave In/Serial Data Output,简称主出从入信号线,这根数据线只能用于主机向从机发送数据,也就是主机输出,从机输入
  • MISO/SDI,Master In Slave Out/Serial Data Input,简称主入从出信号线,这根数据线只能用户从机向主机发送数据,也就是主机输入,从机输出

2.STM32MP1 SPI介绍

STM32MP1 自带的 SPI 全称为:Serial peripheral interface。 其特性如下:

  • 全双工同步串口接口
  • 半双工模式
  • 可配置的主/从模式
  • 支持 I2S 协议
  • 在达到 FIFO 阈值、超时、操作完成以及发生访问错误时产生中断
  • 允许 16 位, 24 位或者 32 位数据长度
  • 支持软件片选和硬件片选

3. ICM-20608 简介

  ICM-20608 是一款 6 轴 MEMS 传感器,包括 3 轴加速度和 3 轴陀螺仪。陀螺仪和加速度计都是 16 位的 ADC,并且支持 I2C 和 SPI 两种协议,使用 I2C 接口的话通信速度最高可以达到 400KHz,使用 SPI 接口的话通信速度最高可达到 8MHz。开发板上的 ICM-20608 通过 SPI 接口和 STM32MP157 连接在一起。
  如果使用 IIC 接口,ICM-20608 的 AD0 引脚决定 I2C 设备从地址的最后一位,如果 AD0 为 0,ICM-20608 从设备地址是 0X68,如果 AD0 为 1,ICM-20608 从设备地址为0X69。

4.Linux下的SPI框架

在 Linux 内核当中,与 I2C 总线框架一样,SPI 总线框架(也可以叫做 SPI 子系统)也可以分为三个部分:
SPI 核心层:SPI 核心层是 Linux 的 SPI 子系统的核心代码部分,提供了核心数据结构的定义、SPI 控制器驱动和设备驱动的注册、注销、管理等 API。其为硬件平台无关层,向下屏蔽了物理总线控制器的差异,定义了统一的访问策略和接口;其向上提供了统一的接口,以便 SPI设备驱动通过总线控制器进行数据收发。在 Linux 系统中, SPI 核心层的代码位于 drivers/spi/spi.c。
SPI 控制器驱动层:每种处理器平台都有自己的 SPI 控制器驱动程序,它的职责是为系统中的 SPI 总线实现相应的读写方法。例如 STM32MP1 就有六个 SPI,那么就有六个 SPI 控制器,每个控制器都有一条特定的 SPI 总线的读写。SPI 子系统使用 struct spi_master 数据结构体来描述 SPI 控制器。在内核源码 drivers/spi 目录下有很多以 spi-xxxx.c 命名的源文件。
SPI 设备驱动层: SPI 从设备对应的驱动程序,比如一些 SPI 接口的芯片器件对应的驱动程序。

二、驱动开发

原理图:

PZ0~3 分别连接到 ICM-20608 的 SCK、SDA、AD0 和 CS。其中 6D_INT 为 ICM20608 的中断引脚,连接到 PA14 引脚上,本节没有使用到。

1)IO 的 pinctrl 子节点创建与修改

首先根据所使用的 IO 来创建或者修改 pinctrl 子节点,并且要注意检查相应的 IO 有没有被其它的设备所使用,如果有多个 pinctrl 配置相同的 IO 是没有关系的,只要保证没有被设备调用就行。
打开stm32mp15-pinctrl.dtsi文件,在其中设置SPI1接口:

在官方的SPI节点基础上,添加pins3片选引脚。

2)SPI 设备节点的创建与修改

采用设备树方式的情况下,SPI 从机设备信息描述就通过创建相应的设备子节点来完成,在 stm32mp157d-atk.dts 这个设备树文件中,创建一个 SPI 从机设备节点,描述该设备的相关信息:

&spi1 {pinctrl-names = "default", "sleep";pinctrl-0 = <&spi1_pins_a>;pinctrl-1 = <&spi1_sleep_pins_a>;cs-gpios = <&gpioz 3 GPIO_ACTIVE_LOW>;status = "okay";spidev: icm20608@0 {compitable = "alientek,icm20608";reg = <0>;spi-max-frequency = <8000000>;};
};

“cs-gpios”属性是用来设置 SPI 的片选引脚。SPI 主机驱动就会根据此属性去控制
设备的片选引脚,本节使用 PZ3 作为片选引脚。如果一个 SPI 接口下连接了多个 SPI 芯片,就使用如下的描述:

cs-gpios = <&gpio1 0 0>, <&gpio1 1 0>, <&gpio1 2 0>, <&gpio1 3 0>;

3)ICM20608驱动

icm20608.h:

#ifndef ICM20608_H
#define ICM20608_H#define ICM20608G_ID          0XAF    /* ID值 */
#define ICM20608D_ID            0XAE    /* ID值 *//* 陀螺仪和加速度自测(出产时设置,用于与用户的自检输出值比较) */
#define ICM20_SELF_TEST_X_GYRO      0x00
#define ICM20_SELF_TEST_Y_GYRO      0x01
#define ICM20_SELF_TEST_Z_GYRO      0x02
#define ICM20_SELF_TEST_X_ACCEL     0x0D
#define ICM20_SELF_TEST_Y_ACCEL     0x0E
#define ICM20_SELF_TEST_Z_ACCEL     0x0F/* 陀螺仪静态偏移 */
#define ICM20_XG_OFFS_USRH          0x13
#define ICM20_XG_OFFS_USRL          0x14
#define ICM20_YG_OFFS_USRH          0x15
#define ICM20_YG_OFFS_USRL          0x16
#define ICM20_ZG_OFFS_USRH          0x17
#define ICM20_ZG_OFFS_USRL          0x18#define ICM20_SMPLRT_DIV            0x19
#define ICM20_CONFIG                0x1A
#define ICM20_GYRO_CONFIG           0x1B
#define ICM20_ACCEL_CONFIG          0x1C
#define ICM20_ACCEL_CONFIG2         0x1D
#define ICM20_LP_MODE_CFG           0x1E
#define ICM20_ACCEL_WOM_THR         0x1F
#define ICM20_FIFO_EN               0x23
#define ICM20_FSYNC_INT             0x36
#define ICM20_INT_PIN_CFG           0x37
#define ICM20_INT_ENABLE            0x38
#define ICM20_INT_STATUS            0x3A/* 加速度输出 */
#define ICM20_ACCEL_XOUT_H          0x3B
#define ICM20_ACCEL_XOUT_L          0x3C
#define ICM20_ACCEL_YOUT_H          0x3D
#define ICM20_ACCEL_YOUT_L          0x3E
#define ICM20_ACCEL_ZOUT_H          0x3F
#define ICM20_ACCEL_ZOUT_L          0x40/* 温度输出 */
#define ICM20_TEMP_OUT_H            0x41
#define ICM20_TEMP_OUT_L            0x42/* 陀螺仪输出 */
#define ICM20_GYRO_XOUT_H           0x43
#define ICM20_GYRO_XOUT_L           0x44
#define ICM20_GYRO_YOUT_H           0x45
#define ICM20_GYRO_YOUT_L           0x46
#define ICM20_GYRO_ZOUT_H           0x47
#define ICM20_GYRO_ZOUT_L           0x48#define ICM20_SIGNAL_PATH_RESET     0x68
#define ICM20_ACCEL_INTEL_CTRL      0x69
#define ICM20_USER_CTRL             0x6A
#define ICM20_PWR_MGMT_1            0x6B
#define ICM20_PWR_MGMT_2            0x6C
#define ICM20_FIFO_COUNTH           0x72
#define ICM20_FIFO_COUNTL           0x73
#define ICM20_FIFO_R_W              0x74
#define ICM20_WHO_AM_I              0x75/* 加速度静态偏移 */
#define ICM20_XA_OFFSET_H           0x77
#define ICM20_XA_OFFSET_L           0x78
#define ICM20_YA_OFFSET_H           0x7A
#define ICM20_YA_OFFSET_L           0x7B
#define ICM20_ZA_OFFSET_H           0x7D
#define ICM20_ZA_OFFSET_L           0x7E#endif

主要是一些相关的寄存器数值设置。

icm20608.c:

#include <linux/spi/spi.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include "icm20608.h"#define ICM20608_CNT 1
#define ICM20608_NAME   "icm20608"struct icm20608_dev {struct spi_device *spi;        /* spi设备 */dev_t devid;             /* 设备号   */struct cdev cdev;            /* cdev     */struct class *class;      /* 类        */struct device *device;        /* 设备    */struct device_node   *nd;    /* 设备节点 */signed int gyro_x_adc;        /* 陀螺仪X轴原始值      */signed int gyro_y_adc;       /* 陀螺仪Y轴原始值     */signed int gyro_z_adc;        /* 陀螺仪Z轴原始值         */signed int accel_x_adc;       /* 加速度计X轴原始值    */signed int accel_y_adc;       /* 加速度计Y轴原始值    */signed int accel_z_adc;       /* 加速度计Z轴原始值    */signed int temp_adc;      /* 温度原始值            */
};/*从icm20608读取多个寄存器数据*/
static int icm20608_read_regs(struct icm20608_dev *dev, u8 reg, void *buf, int len)
{int ret = -1;unsigned char txdata[1];unsigned char* rxdata;struct spi_message m;struct spi_transfer *t;struct spi_device *spi = (struct spi_device *)dev->spi;t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);  /* 申请内存 */if(!t) {return -ENOMEM;}rxdata = kzalloc(sizeof(char) * len, GFP_KERNEL);    /* 申请内存 */if(!rxdata) {goto out1;}/* 一共发送len+1个字节的数据,第一个字节为寄存器首地址,一共要读取len个字节长度的数据,*/txdata[0] = reg | 0x80;     /* 写数据的时候首寄存器地址bit8要置1 */           t->tx_buf = txdata;         /* 要发送的数据 */t->rx_buf = rxdata;         /* 要读取的数据 */t->len = len+1;                /* t->len=发送的长度+读取的长度 */spi_message_init(&m);      /* 初始化spi_message */spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */ret = spi_sync(spi, &m);    /* 同步发送 */if(ret) {goto out2;}memcpy(buf , rxdata+1, len);  /* 只需要读取的数据 */out2:kfree(rxdata);                  /* 释放内存 */
out1:   kfree(t);                       /* 释放内存 */return ret;
}/*向icm20608多个寄存器写入数据*/
static s32 icm20608_write_regs(struct icm20608_dev *dev, u8 reg, u8 *buf, u8 len)
{int ret = -1;unsigned char *txdata;struct spi_message m;struct spi_transfer *t;struct spi_device *spi = (struct spi_device *)dev->spi;t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);  /* 申请内存 */if(!t) {return -ENOMEM;}txdata = kzalloc(sizeof(char)+len, GFP_KERNEL);if(!txdata) {goto out1;}/* 一共发送len+1个字节的数据,第一个字节为寄存器首地址,len为要写入的寄存器的集合,*/*txdata = reg & ~0x80;   /* 写数据的时候首寄存器地址bit8要清零 */memcpy(txdata+1, buf, len);   /* 把len个寄存器拷贝到txdata里,等待发送 */t->tx_buf = txdata;         /* 要发送的数据 */t->len = len+1;                /* t->len=发送的长度+读取的长度 */spi_message_init(&m);      /* 初始化spi_message */spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */ret = spi_sync(spi, &m);    /* 同步发送 */if(ret) {goto out2;}out2:kfree(txdata);               /* 释放内存 */
out1:kfree(t);                  /* 释放内存 */return ret;
}/*读取icm20608指定寄存器值,读取一个寄存器*/
static unsigned char icm20608_read_onereg(struct icm20608_dev *dev, u8 reg)
{u8 data = 0;icm20608_read_regs(dev, reg, &data, 1);return data;
}
/*写入icm20608指定寄存器值,读取一个寄存器*/
static void icm20608_write_onereg(struct icm20608_dev *dev, u8 reg, u8 value)
{u8 buf = value;icm20608_write_regs(dev, reg, &buf, 1);
}/*读取ICM20608的数据,读取原始数据,包括三轴陀螺仪、三轴加速度计和内部温度*/
void icm20608_readdata(struct icm20608_dev *dev)
{unsigned char data[14];icm20608_read_regs(dev, ICM20_ACCEL_XOUT_H, data, 14);dev->accel_x_adc = (signed short)((data[0] << 8) | data[1]); dev->accel_y_adc = (signed short)((data[2] << 8) | data[3]); dev->accel_z_adc = (signed short)((data[4] << 8) | data[5]); dev->temp_adc    = (signed short)((data[6] << 8) | data[7]); dev->gyro_x_adc  = (signed short)((data[8] << 8) | data[9]); dev->gyro_y_adc  = (signed short)((data[10] << 8) | data[11]);dev->gyro_z_adc  = (signed short)((data[12] << 8) | data[13]);
}/*open函数*/
static int icm20608_open(struct inode *inode, struct file *filp)
{return 0;
}/*从设备读取数据*/
static ssize_t icm20608_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{signed int data[7];long err = 0;struct cdev *cdev = filp->f_path.dentry->d_inode->i_cdev;struct icm20608_dev *dev = container_of(cdev, struct icm20608_dev, cdev);icm20608_readdata(dev);data[0] = dev->gyro_x_adc;data[1] = dev->gyro_y_adc;data[2] = dev->gyro_z_adc;data[3] = dev->accel_x_adc;data[4] = dev->accel_y_adc;data[5] = dev->accel_z_adc;data[6] = dev->temp_adc;err = copy_to_user(buf, data, sizeof(data));return 0;
}/*关闭设备*/
static int icm20608_release(struct inode *inode, struct file *filp)
{return 0;
}
/*设备操作函数集*/
static const struct file_operations icm20608_ops = {.owner = THIS_MODULE,.open = icm20608_open,.read = icm20608_read,.release = icm20608_release,
};/*icm初始化操作*/
void icm20608_reginit(struct icm20608_dev *dev)
{u8 value = 0;icm20608_write_onereg(dev, ICM20_PWR_MGMT_1, 0x80);mdelay(50);icm20608_write_onereg(dev, ICM20_PWR_MGMT_1, 0x01);mdelay(50);value = icm20608_read_onereg(dev, ICM20_WHO_AM_I);printk("ICM20608 ID = %#X\r\n", value);    icm20608_write_onereg(dev, ICM20_SMPLRT_DIV, 0x00);     /* 输出速率是内部采样率                   */icm20608_write_onereg(dev, ICM20_GYRO_CONFIG, 0x18);  /* 陀螺仪±2000dps量程                */icm20608_write_onereg(dev, ICM20_ACCEL_CONFIG, 0x18);     /* 加速度计±16G量程                   */icm20608_write_onereg(dev, ICM20_CONFIG, 0x04);       /* 陀螺仪低通滤波BW=20Hz              */icm20608_write_onereg(dev, ICM20_ACCEL_CONFIG2, 0x04); /* 加速度计低通滤波BW=21.2Hz          */icm20608_write_onereg(dev, ICM20_PWR_MGMT_2, 0x00);   /* 打开加速度计和陀螺仪所有轴                */icm20608_write_onereg(dev, ICM20_LP_MODE_CFG, 0x00);  /* 关闭低功耗                        */icm20608_write_onereg(dev, ICM20_FIFO_EN, 0x00);      /* 关闭FIFO                       */
}/*probe函数*/
static int icm20608_probe(struct spi_device *spi)
{int ret;struct icm20608_dev *icm20608dev;/* 分配icm20608dev对象的空间 */icm20608dev = devm_kzalloc(&spi->dev, sizeof(*icm20608dev), GFP_KERNEL);if(!icm20608dev)return -ENOMEM;/* 注册字符设备驱动 *//* 1、创建设备号 */ret = alloc_chrdev_region(&icm20608dev->devid, 0, ICM20608_CNT, ICM20608_NAME);if(ret < 0) {pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", ICM20608_NAME, ret);return 0;}/* 2、初始化cdev */icm20608dev->cdev.owner = THIS_MODULE;cdev_init(&icm20608dev->cdev, &icm20608_ops);/* 3、添加一个cdev */ret = cdev_add(&icm20608dev->cdev, icm20608dev->devid, ICM20608_CNT);if(ret < 0) {goto del_unregister;}/* 4、创建类 */icm20608dev->class = class_create(THIS_MODULE, ICM20608_NAME);if (IS_ERR(icm20608dev->class)) {goto del_cdev;}/* 5、创建设备 */icm20608dev->device = device_create(icm20608dev->class, NULL, icm20608dev->devid, NULL, ICM20608_NAME);if (IS_ERR(icm20608dev->device)) {goto destroy_class;}icm20608dev->spi = spi;/*初始化spi_device */spi->mode = SPI_MODE_0; /*MODE0,CPOL=0,CPHA=0*/spi_setup(spi);/* 初始化ICM20608内部寄存器 */icm20608_reginit(icm20608dev);  /* 保存icm20608dev结构体 */spi_set_drvdata(spi, icm20608dev);return 0;
destroy_class:device_destroy(icm20608dev->class, icm20608dev->devid);
del_cdev:cdev_del(&icm20608dev->cdev);
del_unregister:unregister_chrdev_region(icm20608dev->devid, ICM20608_CNT);return -EIO;
}/*remove函数,移除设备驱动*/
static int icm20608_remove(struct spi_device *spi)
{struct icm20608_dev *icm20608dev = spi_get_drvdata(spi);/* 注销字符设备驱动 *//* 1、删除cdev */cdev_del(&icm20608dev->cdev);/* 2、注销设备号 */unregister_chrdev_region(icm20608dev->devid, ICM20608_CNT); /* 3、注销设备 */device_destroy(icm20608dev->class, icm20608dev->devid);/* 4、注销类 */class_destroy(icm20608dev->class); return 0;
}/* 传统匹配方式ID列表 */
static const struct spi_device_id icm20608_id[] = {{"alientek,icm20608", 0},{}
};/* 设备树匹配列表 */
static const struct of_device_id icm20608_of_match[] = {{ .compatible = "alientek,icm20608" },{ /* Sentinel */ }
};/* SPI驱动结构体 */
static struct spi_driver icm20608_driver = {.probe = icm20608_probe,.remove = icm20608_remove,.driver = {.owner = THIS_MODULE,.name = "icm20608",.of_match_table = icm20608_of_match,},.id_table = icm20608_id,
};/*驱动入口函数*/
static int __init icm20608_init(void)
{return spi_register_driver(&icm20608_driver);
}/*驱动出口函数*/
static void __exit icm20608_exit(void)
{spi_unregister_driver(&icm20608_driver);
}module_init(icm20608_init);
module_exit(icm20608_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("amonter");
MODULE_INFO(intree, "Y");

主要分为三个部分:
1.icm20608的设备结构体设置,以及设备寄存器的初始化、寄存器数据读取与写入(包括单个寄存器与多个寄存器)
2.设备的open、read、release函数
3.设备的probe挂载(本质是一个字符设备)与remove函数,以及设备的匹配表
注:本节的icm20608设备的寄存器数据读写没有进行加锁,可以通过原子操作或自旋锁等机制,对寄存器数据的读写进行加锁操作,提高鲁棒性

4)测试App

icm20608App.c:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include <poll.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>int main(int argc, char *argv[])
{int fd;char *filename;signed int databuf[7];unsigned char data[14];signed int gyro_x_adc, gyro_y_adc, gyro_z_adc;signed int accel_x_adc, accel_y_adc, accel_z_adc;signed int temp_adc;float gyro_x_act, gyro_y_act, gyro_z_act;float accel_x_act, accel_y_act, accel_z_act;float temp_act;int ret = 0;if (argc != 2) {printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR);if(fd < 0) {printf("can't open file %s\r\n", filename);return -1;}while (1) {ret = read(fd, databuf, sizeof(databuf));if(ret == 0) {             /* 数据读取成功 */gyro_x_adc = databuf[0];gyro_y_adc = databuf[1];gyro_z_adc = databuf[2];accel_x_adc = databuf[3];accel_y_adc = databuf[4];accel_z_adc = databuf[5];temp_adc = databuf[6];/* 计算实际值 */gyro_x_act = (float)(gyro_x_adc)  / 16.4;gyro_y_act = (float)(gyro_y_adc)  / 16.4;gyro_z_act = (float)(gyro_z_adc)  / 16.4;accel_x_act = (float)(accel_x_adc) / 2048;accel_y_act = (float)(accel_y_adc) / 2048;accel_z_act = (float)(accel_z_adc) / 2048;temp_act = ((float)(temp_adc) - 25 ) / 326.8 + 25;printf("\r\n原始值:\r\n");printf("gx = %d, gy = %d, gz = %d\r\n", gyro_x_adc, gyro_y_adc, gyro_z_adc);printf("ax = %d, ay = %d, az = %d\r\n", accel_x_adc, accel_y_adc, accel_z_adc);printf("temp = %d\r\n", temp_adc);printf("实际值:");printf("act gx = %.2f°/S, act gy = %.2f°/S, act gz = %.2f°/S\r\n", gyro_x_act, gyro_y_act, gyro_z_act);printf("act ax = %.2fg, act ay = %.2fg, act az = %.2fg\r\n", accel_x_act, accel_y_act, accel_z_act);printf("act temp = %.2f°C\r\n", temp_act);}usleep(100000); /*100ms */}close(fd);  /* 关闭文件 */  return 0;
}

5)运行测试

首先,在Linux内核的menuconfig中使能SPI控制器:

编译出新的内核镜像和设备树,用于开发板启动。
之后将编译出的icm20608驱动和测试App文件放入相应文件夹,启动开发板。
注:在编译测试App是可以使能硬件浮点,可以加速计算,使用如下命令即可:

arm-none-linux-gnueabihf-gcc -march=armv7-a -mfpu=neon -mfloat-abi=hard icm20608App.c -o icm20608App

可以使用以下命令查看是否使能成功:

arm-none-linux-gnueabihf-readelf -A icm20608App

在开机时就会显示以下信息:

将驱动文件挂载,然后使用测试程序进行测试即可。

STM32MP157驱动开发——SPI驱动相关推荐

  1. Linux驱动开发 -- touch驱动注册

    Linux i2c驱动开发 – touch 驱动 文章目录 Linux i2c驱动开发 -- touch 驱动 前言 一.i2c 驱动框架 二.Linux的MODULE声明 1. MODULE相关声明 ...

  2. STM32F103系列_OLED屏幕(SSD1306、SSD1315驱动)SPI驱动【DMA】(高刷)

    STM32F103系列_OLED屏幕(SSD1306.SSD1315驱动)SPI驱动[DMA](高刷) 一.SSD1306和SSD1315 二.电路原理图(SPI接法) 三.STM32_SPI 四.S ...

  3. Linux驱动开发——串口设备驱动

    Linux驱动开发--串口设备驱动 一.串口简介 串口全称叫做串行接口,通常也叫做 COM 接口,串行接口指的是数据一个一个的顺序传输,通信线路简单.使用两条线即可实现双向通信,一条用于发送,一条用于 ...

  4. Linux嵌入式驱动开发02——驱动编译到内核

    文章目录 全系列传送门 make menuconfig图形化配置界面 1. 怎么进入到make menuconfig图形化界面? 2. make menuconfig图形化界面的操作 3. 退出 4. ...

  5. ESP32开发——SPI驱动水墨屏

    怎么说呢,感觉自己之前都白学了,又从头到尾看了一遍. 主要参考厂家给的源码,不过只有STM32的程序,但是大差不差,拿过来改一下就可以了,其次就是仔细查看芯片手册. 好的,最大的收获就是学会了如何翻手 ...

  6. 10.13、驱动开发 -- spi总线

    SPI 英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口.是Motorola首先在其MC68HCXX系列处理器上定义的.SPI接口主要应用在 EEPROM ...

  7. linux驱动开发音频设备驱动,linux驱动开发—基于Device tree机制的驱动编写

    摘要:媒介 Device Tree是一种用去描绘硬件的数据布局,类似板级描绘说话,发源于OpenFirmware(OF).正在现在遍及应用的kernel 2.6.x版本中,对分歧仄台.分歧硬件,往] ...

  8. Linux 设备驱动开发思想 —— 驱动分层与驱动分离

    前面我们学习I2C.USB.SD驱动时,有没有发现一个共性,就是在驱动开发时,每个驱动都分层三部分,由上到下分别是: 1.XXX 设备驱动 2.XXX 核心层 3.XXX 主机控制器驱动 而需要我们编 ...

  9. Linux设备驱动之SPI驱动

    Linux下SPI驱动分成两部分:主机驱动和设备驱动. 主机驱动: 主机侧SPI控制器使用struct spi_master描述,该结构体中包含了SPI控制器的序号(很多SoC中存在多个SPI控制器) ...

最新文章

  1. 在使用stl中的ifstream出错时如何快速排错?
  2. 为啥程序员下班后只关显示器从不关电脑?看看各大网站的答案~
  3. matlab 左除和右除
  4. python3 requests 错误EOF occurred in violation of protocol 解决方法
  5. thinkphp5是不是php,我对ThinkPHP5和Laravel5的一些看法
  6. mysql和SQLYog工具使用
  7. vim中开shell
  8. 限制oracle数据库表的输出记录条数
  9. A little something to get you started
  10. 自定义dialog弹窗html,自定义H5页面dialog弹窗
  11. zybo的linux开发教程,Zybo全栈开发入门教程——连载三:创建Linux设备驱动和应用程序...
  12. 第十四期:5 个 JS 不良编码习惯,你占几个呢?
  13. Extjs之RowNumberer
  14. 防止表格中的单行按钮被频繁点击,前端实例讲解~
  15. less学习-带参数混合
  16. Java 算法 瓷砖铺放
  17. TensorFlow COCO
  18. Hbase新API以及Hbase增删改差
  19. matlab输出n个a,输入a,n两个参数,要求输出aaa...a(共n个)的值
  20. 虚拟机win10系统安装详细教程

热门文章

  1. Datawhale开源学习笔记
  2. 【面试必读(编程基础)】几种查找算法
  3. 光荣使命内测在即,战术竞技品类或成腾讯股价助推器
  4. 思路迪医药冲刺港股:5个月亏2.9亿 泰格医药与先声药业是股东
  5. iOS AttributedString简介
  6. android scheme 配置多个,Android业务组件化之URL Scheme使用
  7. 想要打印数码照片?“Pixelmator Pro”教你如何自行裁剪出清晰生动的照片!
  8. 编译原理 First集 Follow集 select集 通俗易懂的讲解 + 实例
  9. DVD-RAM 格式化失败及视频文件分割软件
  10. 介绍Brook+的kernel到IL的转化方法和优化技巧