SPI设备驱动框架

SPI通信协议

分析硬件电路

  • 开发板原理图
    在这里插入图片描述
  • 核心板原理图
    在这里插入图片描述

编写设备树

  • 注意事项:注意引脚不要被其他功能占用
&iomuxc {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_hog_1>;
	imx6ul-evk {
		pinctrl_ecspi3: icm20608 { 
			fsl,pins = < 
				MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 0x10b0 /* CS使用的是软件片选,不对引脚进行复用 */
				MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK 0x10b1 /* SCLK */
				MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO 0x10b1 /* MISO */
				MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI 0x10b1 /* MOSI */
			>;
		};
	};
};

&ecspi3 { 
	fsl,spi-num-chipselects = <1>; //只有一个设备
	cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>; //片选引脚
	pinctrl-names = "default"; //pinctrl名字
	pinctrl-0 = <&pinctrl_ecspi3>; //pinctrl引脚
	status = "okay";  //就绪状态
	//icm20608设备 
	spidev: icm20608@0 { 
		compatible = "myboard,icm20608";//属性值
		spi-max-frequency = <8000000>;//spi最大传输速率
		reg = <0>; //单一地址值
	};
};

编写SPI设备驱动

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/input.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include "icm20608.h"

#define DEV_CNT  1
#define DEV_NAME "icm20608"


struct icm20608_cdev {
    dev_t devt;//设备号
    struct cdev cdev;//字符设备
    unsigned int major;//主设备号
    struct class *class;//类
    struct device *device;//设备
    void *private_data;//文件私有数据
    signed int gyro_x_adc;//陀螺仪值
    signed int gyro_y_adc;
    signed int gyro_z_adc;
    signed int accel_x_adc;//加速度值
    signed int accel_y_adc;
    signed int accel_z_adc;
    signed int temp_adc;//温度原始值
};
static struct icm20608_cdev icm20608cdev;

//读寄存器
int icm20608_read_regs(struct icm20608_cdev *dev, u8 addr, void *buf, size_t len)
{
    int ret=0;
    u8 data=0;
    struct spi_device *spi = (struct spi_device *)dev->private_data;
    //读操作最高位是1
    data= addr | 0x80;
    //写寄存器地址
    //ret = spi_write(spi, &addr, 1);
    //读寄存器数据
    //ret = spi_read(spi, buf, len);
    //int spi_write_then_read(struct spi_device *spi, const void *txbuf, unsigned n_tx, void *rxbuf, unsigned n_rx)
    ret = spi_write_then_read(spi, &data, 1, buf, len);
    return ret;
}

//写寄存器
int icm20608_write_regs(struct icm20608_cdev *dev, u8 addr, void *buf, size_t len)
{
    int ret=0;
    u8 *txdata;
    struct spi_device *spi = (struct spi_device *)dev->private_data;
    //申请内存
    txdata = kzalloc(len+1, GFP_KERNEL);
    //发送地址
    //写操作最高位是0
    txdata[0]=addr & ~0x80;
    //发送数据
    memcpy(&txdata[1],buf,len);
    //写寄存器地址
    ret = spi_write(spi, txdata, len+1);
    //释放内存
    kfree(txdata);
    return ret;
}

//读寄存器
int icm20608_read_reg(struct icm20608_cdev *dev, u8 addr)
{
    u8 data=0;
    icm20608_read_regs(dev, addr, &data, 1);
    return data;
}

//写寄存器
int icm20608_write_reg(struct icm20608_cdev *dev, u8 addr, u8 value)
{
    u8 buf = value;
    return icm20608_write_regs(dev, addr, &buf, 1);
}

void icm20608_readdata(struct icm20608_cdev *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]);
}

static int icm20608_open(struct inode *inode, struct file *file)
{
    file->private_data = &icm20608cdev;
    return 0;
}

static ssize_t icm20608_read(struct file *file, char __user *buf, size_t cnt, loff_t *off)
{
	signed int data[7];
	long err = 0;
	struct icm20608_cdev *dev = (struct icm20608_cdev *)file->private_data;
    //读取寄存器数据
	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;
}

int icm20608_release(struct inode *inode, struct file *file)
{
    return 0;
}

struct file_operations icm20608_file_operations = {
    .owner = THIS_MODULE,
    .open = icm20608_open,
    .read = icm20608_read,
    .release = icm20608_release,
};





void icm20608_device_init(struct icm20608_cdev *cdev)
{
    //初始化设备
    int value=0;
    //struct spi_device *spi = (struct spi_device *)cdev->private_data;

    icm20608_write_reg(&icm20608cdev, ICM20_PWR_MGMT_1, 0x80);
    mdelay(50);
    icm20608_write_reg(&icm20608cdev, ICM20_PWR_MGMT_1, 0x01);
    mdelay(50);

    value = icm20608_read_reg(&icm20608cdev, ICM20_WHO_AM_I);
    printk("ICM20608 ID = %#X\r\n", value);
    value = icm20608_read_reg(&icm20608cdev, ICM20_PWR_MGMT_1);
    printk("ICM20_PWR_MGMT_1 value = %#X\r\n", value);
}

static int icm20608_probe(struct spi_device *spi)
{
    printk("spi driver and device was matched!\r\n");
    //申请设备号
    if(icm20608cdev.major)//设置了主设备号
    {
        //根据主设备号,获取设备号
        icm20608cdev.devt = MKDEV(icm20608cdev.major,0);
        //根据设备号,申请分配设备号
        register_chrdev_region(icm20608cdev.devt, DEV_CNT, DEV_NAME);
    }
    else//未设置主设备号
    {
        //系统自动分配设备号
        alloc_chrdev_region(&icm20608cdev.devt, 0, DEV_CNT, DEV_NAME);
        //根据分配到的设备号,获取主设备号
        icm20608cdev.major = MAJOR(icm20608cdev.devt);
    }
    //注册设备
    cdev_init(&icm20608cdev.cdev, &icm20608_file_operations);
    //添加设备
    cdev_add(&icm20608cdev.cdev, icm20608cdev.devt, DEV_CNT);
    //创建类
    icm20608cdev.class = class_create(THIS_MODULE, DEV_NAME);
    if(IS_ERR(icm20608cdev.class))
    {
        return PTR_ERR(icm20608cdev.class);
    }
    //创建设备
    icm20608cdev.device = device_create(icm20608cdev.class, NULL, icm20608cdev.devt, NULL, DEV_NAME);
    if(IS_ERR(icm20608cdev.device))
    {
        return PTR_ERR(icm20608cdev.device);
    }
    //设置spi模式
    spi->mode = SPI_MODE_0;
    spi_setup(spi);
    //保存私有数据
    icm20608cdev.private_data = spi;
    //初始化spi设备
    icm20608_device_init(&icm20608cdev);
    return 0;
}


static int icm20608_remove(struct spi_device *spi)
{
	//删除字符设备
	cdev_del(&icm20608cdev.cdev);
	//释放设备号 
	unregister_chrdev_region(icm20608cdev.devt, DEV_CNT);
    //删除设备
	device_destroy(icm20608cdev.class, icm20608cdev.devt);
    //删除类
	class_destroy(icm20608cdev.class);
    return 0;
}

static const struct spi_device_id icm20608_device_id[] = {
    {"myboard,icm20608", 0},
    {}
};

static const struct of_device_id icm20608_driver_of_match_table[] = {
    {.compatible = "myboard,icm20608"},
    {}
};

//i2c设备驱动
struct spi_driver icm20608_driver = {
    .probe = icm20608_probe,
    .remove = icm20608_remove,
    .driver = {
        .name = "icm20608",
        .owner = THIS_MODULE,
        .of_match_table = icm20608_driver_of_match_table,
    },
    .id_table = icm20608_device_id,
};



//入口函数
static int __init icm20608_init(void)
{
    //加载驱动
    int ret = 0;
    ret = spi_register_driver(&icm20608_driver);
    return ret;
}

//出口函数
static void __exit icm20608_exit(void)
{
    //卸载驱动
    spi_unregister_driver(&icm20608_driver);
}


module_init(icm20608_init);
module_exit(icm20608_exit);
MODULE_AUTHOR("AUTHOR");
MODULE_LICENSE("GPL");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Paper_Love

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值