020——SPI模块驱动开发(基于DAC芯片和I.MX6uLL)

2024-04-18 1259阅读

目录

一、 SPI简介

二、 Linux中的SPI 

2.1 SPI控制器数据结构

2.2 SPI设备数据结构

2.3 SPI设备驱动

2.4 接口函数

2.4.1 函数原型

2.4.2 函数解析

2.5 SPI驱动框架

2.6 SPI控制器驱动程序

2.7 SPI设备驱动程序

三、 DAC实例

3.1 实验过程

3.2 驱动程序 

3.3 应用程序


一、 SPI简介

总线类设备驱动——SPI_spi驱动-CSDN博客

二、 Linux中的SPI 

SPI子系统中涉及2类硬件:SPI控制器、SPI设备。

020——SPI模块驱动开发(基于DAC芯片和I.MX6uLL)

SPI控制器有驱动程序,提供SPI的传输能力。

SPI设备也有自己的驱动程序,提供SPI设备的访问能力:

  • 它知道怎么访问这个设备,它知道这个设备的数据含义是什么

  • 它会调用SPI控制器的函数来收发数据。

    2.1 SPI控制器数据结构

    参考内核文件:include\linux\spi\spi.h

    Linux中使用spi_master结构体描述SPI控制器,里面最重要的成员就是transfer函数指针:

    020——SPI模块驱动开发(基于DAC芯片和I.MX6uLL)

    2.2 SPI设备数据结构

    参考内核文件:include\linux\spi\spi.h

    Linux中使用spi_device结构体描述SPI设备,里面记录有设备的片选引脚、频率、挂在哪个SPI控制器下面:

    020——SPI模块驱动开发(基于DAC芯片和I.MX6uLL)

    2.3 SPI设备驱动

    参考内核文件:include\linux\spi\spi.h

    Linux中使用spi_driver结构体描述SPI设备驱动:

    020——SPI模块驱动开发(基于DAC芯片和I.MX6uLL)

    2.4 接口函数

    2.4.1 函数原型

    接口函数都在这个内核文件里:include\linux\spi\spi.h

    • 简易函数

      /**
       * SPI同步写
       * @spi: 写哪个设备
       * @buf: 数据buffer
       * @len: 长度
       * 这个函数可以休眠
       *
       * 返回值: 0-成功, 负数-失败码
       */
      static inline int
      spi_write(struct spi_device *spi, const void *buf, size_t len);
      /**
       * SPI同步读
       * @spi: 读哪个设备
       * @buf: 数据buffer
       * @len: 长度
       * 这个函数可以休眠
       *
       * 返回值: 0-成功, 负数-失败码
       */
      static inline int
      spi_read(struct spi_device *spi, void *buf, size_t len);
      /**
       * spi_write_then_read : 先写再读, 这是一个同步函数
       * @spi: 读写哪个设备
       * @txbuf: 发送buffer
       * @n_tx: 发送多少字节
       * @rxbuf: 接收buffer
       * @n_rx: 接收多少字节
       * 这个函数可以休眠
       * 
       * 这个函数执行的是半双工的操作: 先发送txbuf中的数据,在读数据,读到的数据存入rxbuf
       *
       * 这个函数用来传输少量数据(建议不要操作32字节), 它的效率不高
       * 如果想进行高效的SPI传输,请使用spi_{async,sync}(这些函数使用DMA buffer)
       *
       * 返回值: 0-成功, 负数-失败码
       */
      extern int spi_write_then_read(struct spi_device *spi,
      		const void *txbuf, unsigned n_tx,
      		void *rxbuf, unsigned n_rx);
      /**
       * spi_w8r8 - 同步函数,先写8位数据,再读8位数据
       * @spi: 读写哪个设备
       * @cmd: 要写的数据
       * 这个函数可以休眠
       *
       *
       * 返回值: 成功的话返回一个8位数据(unsigned), 负数表示失败码
       */
      static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd);
      /**
       * spi_w8r16 - 同步函数,先写8位数据,再读16位数据
       * @spi: 读写哪个设备
       * @cmd: 要写的数据
       * 这个函数可以休眠
       *
       * 读到的16位数据: 
       *     低地址对应读到的第1个字节(MSB),高地址对应读到的第2个字节(LSB)
       *     这是一个big-endian的数据
       *
       * 返回值: 成功的话返回一个16位数据(unsigned), 负数表示失败码
       */
      static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd);
      /**
       * spi_w8r16be - 同步函数,先写8位数据,再读16位数据,
       *               读到的16位数据被当做big-endian,然后转换为CPU使用的字节序
       * @spi: 读写哪个设备
       * @cmd: 要写的数据
       * 这个函数可以休眠
       *
       * 这个函数跟spi_w8r16类似,差别在于它读到16位数据后,会把它转换为"native endianness"
       *
       * 返回值: 成功的话返回一个16位数据(unsigned, 被转换为本地字节序), 负数表示失败码
       */
      static inline ssize_t spi_w8r16be(struct spi_device *spi, u8 cmd);

      复杂的函数

      /**
       * spi_async - 异步SPI传输函数,简单地说就是这个函数即刻返回,它返回后SPI传输不一定已经完成
       * @spi: 读写哪个设备
       * @message: 用来描述数据传输,里面含有完成时的回调函数(completion callback)
       * 上下文: 任意上下文都可以使用,中断中也可以使用
       *
       * 这个函数不会休眠,它可以在中断上下文使用(无法休眠的上下文),也可以在任务上下文使用(可以休眠的上下文) 
       *
       * 完成SPI传输后,回调函数被调用,它是在"无法休眠的上下文"中被调用的,所以回调函数里不能有休眠操作。
       * 在回调函数被调用前message->statuss是未定义的值,没有意义。
       * 当回调函数被调用时,就可以根据message->status判断结果: 0-成功,负数表示失败码
       * 当回调函数执行完后,驱动程序要认为message等结构体已经被释放,不能再使用它们。
       *
       * 在传输过程中一旦发生错误,整个message传输都会中止,对spi设备的片选被取消。
       *
       * 返回值: 0-成功(只是表示启动的异步传输,并不表示已经传输成功), 负数-失败码
       */
      extern int spi_async(struct spi_device *spi, struct spi_message *message);
      /**
       * spi_sync - 同步的、阻塞的SPI传输函数,简单地说就是这个函数返回时,SPI传输要么成功要么失败
       * @spi: 读写哪个设备
       * @message: 用来描述数据传输,里面含有完成时的回调函数(completion callback)
       * 上下文: 能休眠的上下文才可以使用这个函数
       *
       * 这个函数的message参数中,使用的buffer是DMA buffer
       *
       * 返回值: 0-成功, 负数-失败码
       */
      extern int spi_sync(struct spi_device *spi, struct spi_message *message);
      /**
       * spi_sync_transfer - 同步的SPI传输函数
       * @spi: 读写哪个设备
       * @xfers: spi_transfers数组,用来描述传输
       * @num_xfers: 数组项个数
       * 上下文: 能休眠的上下文才可以使用这个函数
       *
       * 返回值: 0-成功, 负数-失败码
       */
      static inline int
      spi_sync_transfer(struct spi_device *spi, struct spi_transfer *xfers,
      	unsigned int num_xfers);
      2.4.2 函数解析

      在SPI子系统中,用spi_transfer结构体描述一个传输,用spi_message管理过个传输。

      SPI传输时,发出N个字节,就可以同时得到N个字节。

      • 即使只想读N个字节,也必须发出N个字节:可以发出0xff

      • 即使只想发出N个字节,也会读到N个字节:可以忽略读到的数据。

      • tx_buf:不是NULL的话,要发送的数据保存在里面

      • rx_buf:不是NULL的话,表示读到的数据不要丢弃,保存进rx_buf里

        可以构造多个spi_transfer结构体,把它们放入一个spi_message里面。

        2.5 SPI驱动框架

        020——SPI模块驱动开发(基于DAC芯片和I.MX6uLL)

        2.6 SPI控制器驱动程序

        SPI控制器的驱动程序可以基于"平台总线设备驱动"模型来实现:

        • 在设备树里描述SPI控制器的硬件信息,在设备树子节点里描述挂在下面的SPI设备的信息

        • 在platform_driver中提供一个probe函数

          • 它会注册一个spi_master

          • 还会解析设备树子节点,创建spi_device结构体

          2.7 SPI设备驱动程序

          跟"平台总线设备驱动模型"类似,Linux中也有一个"SPI总线设备驱动模型":

          • 左边是spi_driver,使用C文件实现,里面有id_table表示能支持哪些SPI设备,有probe函数

          • 右边是spi_device,用来描述SPI设备,比如它的片选引脚、频率

            • 可以来自设备树:比如由SPI控制器驱动程序解析设备树后创建、注册spi_device

            • 可以来自C文件:比如使用spi_register_board_info创建、注册spi_device

            三、 DAC实例

            3.1 实验过程

            020——SPI模块驱动开发(基于DAC芯片和I.MX6uLL)

            020——SPI模块驱动开发(基于DAC芯片和I.MX6uLL)

            020——SPI模块驱动开发(基于DAC芯片和I.MX6uLL)

            020——SPI模块驱动开发(基于DAC芯片和I.MX6uLL)

            看样子设备树加载上了

            020——SPI模块驱动开发(基于DAC芯片和I.MX6uLL)

            020——SPI模块驱动开发(基于DAC芯片和I.MX6uLL)

            020——SPI模块驱动开发(基于DAC芯片和I.MX6uLL)

            https://live.csdn.net/v/377501

            3.2 驱动程序 

            #include "asm/cacheflush.h"
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            /* 主设备号                  */
            static int major = 0;
            static struct class *my_spi_class;
            static struct spi_device *g_spi;
            static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);
            struct fasync_struct *spi_fasync;
            /* 实现对应的open/read/write等函数,填入file_operations结构体                   */
            static ssize_t spi_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
            {
            	// int err;
            	// struct spi_transfer msgs[2];
            	/* 初始化 spi_transfer */
            	// static inline int
                //   spi_sync_transfer(struct spi_device *spi, struct spi_transfer *xfers,
            	//   unsigned int num_xfers);
            	/* copy_to_user  */
            	
            	return 0;
            }
            static ssize_t spi_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
            {
            	int err;
            	short val;
            	unsigned char ker_buf[2];
            	struct spi_transfer t;
            	memset(&t, 0, sizeof(t));
            	if (size != 2)
            		return -EINVAL;	
            	/* copy_from_user  */
            	err = copy_from_user(&val, buf, size);
            	val  8;
            	ker_buf[1] = val;
            	/* 初始化 spi_transfer */
            	t.tx_buf = ker_buf;
            	t.len    = 2;
            	err = spi_sync_transfer(g_spi, &t, 1);
            	
            	return size;    
            }
            static unsigned int spi_drv_poll(struct file *fp, poll_table * wait)
            {
            	//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
            	poll_wait(fp, &gpio_wait, wait);
            	//return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
            	return 0;
            }
            static int spi_drv_fasync(int fd, struct file *file, int on)
            {
            	if (fasync_helper(fd, file, on, &spi_fasync) >= 0)
            		return 0;
            	else
            		return -EIO;
            }
            /* 定义自己的file_operations结构体                                              */
            static struct file_operations spi_drv_fops = {
            	.owner	 = THIS_MODULE,
            	.read    = spi_drv_read,
            	.write   = spi_drv_write,
            	.poll    = spi_drv_poll,
            	.fasync  = spi_drv_fasync,
            };
            static int spi_drv_probe(struct spi_device *spi)
            {
            	// struct device_node *np = client->dev.of_node;
            	/* 记录spi_device */
            	g_spi = spi;
            	/* 注册字符设备 */
            	/* 注册file_operations 	*/
            	major = register_chrdev(0, "100ask_spi", &spi_drv_fops);  /* /dev/gpio_desc */
            	my_spi_class = class_create(THIS_MODULE, "100ask_spi_class");
            	if (IS_ERR(my_spi_class)) {
            		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
            		unregister_chrdev(major, "100ask_spi");
            		return PTR_ERR(my_spi_class);
            	}
            	device_create(my_spi_class, NULL, MKDEV(major, 0), NULL, "myspi"); /* /dev/myspi */
            	
            	return 0;
            }
            static int spi_drv_remove(struct spi_device *spi)
            {
            	/* 反注册字符设备 */
            	device_destroy(my_spi_class, MKDEV(major, 0));
            	class_destroy(my_spi_class);
            	unregister_chrdev(major, "100ask_spi");
            	return 0;
            }
            static const struct of_device_id myspi_dt_match[] = {
            	{ .compatible = "100ask,spidev" },
            	{},
            };
            static struct spi_driver my_spi_driver = {
            	.driver = {
            		   .name = "100ask_spi_drv",
            		   .owner = THIS_MODULE,
            		   .of_match_table = myspi_dt_match,
            	},
            	.probe = spi_drv_probe,
            	.remove = spi_drv_remove,
            };
            static int __init spi_drv_init(void)
            {
            	/* 注册spi_driver */
            	return spi_register_driver(&my_spi_driver);
            }
            static void __exit spi_drv_exit(void)
            {
            	/* 反注册spi_driver */
            	spi_unregister_driver(&my_spi_driver);
            }
            /* 7. 其他完善:提供设备信息,自动创建设备节点                                     */
            module_init(spi_drv_init);
            module_exit(spi_drv_exit);
            MODULE_LICENSE("GPL");
            

            3.3 应用程序

            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            #include 
            /*
             * dac_test /dev/mydac 
             */
            int main(int argc, char **argv)
            {
            	int fd;
            	int buf[2];
            	unsigned short dac_val = 0;
            	if (argc != 3)
            	{
            		printf("Usage: %s  \n", argv[0]);
            		return -1;
            	}
            	
            	fd = open(argv[1], O_RDWR);
            	if (fd  
             
             
            
VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]