资讯动态

Linux驱动-SPI-mcp2515 复位函数

发布时间:2026/8/14 4:56:43 来源:尧图企业网站定制
Linux驱动-SPI-mcp2515 复位函数文章目录前言一、参考文档二、读懂原理图和操作手册原理图分析Can 模块GPIO/CAN/485 Interfcae 原理图数据手册分析器件概述工作模式SPI接口三、设备树配置四、SPI 复位函数编写1、复位函数编写定义一个SPI设备指针定义复位指令SPI设备指针 赋值MCP2515 SPI软件复位函数对比两种复位操作-写入函数2、带复位函数完整驱动编写总结前言这里只简单写一个复位函数为什么要写复位函数是通过外设数据手册来的所以要先搞懂数据手册通过数据手册来分析驱动业务流程然后一步一步进行外设的驱动的业务 无外乎读写外设寄存器进行控制。一、参考文档Linux驱动-初步认识SPILinux驱动-SPI外设代码框架编写Linux驱动-SPI-框架中添加字符设备Linux 驱动-I2C输入子系统相关知识点二、读懂原理图和操作手册原理图分析Can 模块mcp2515 模块有很多功能的spi 转can,spi 转485. 如下 spi 如何转换的呢 就是通过ic 芯片进行转换了一次而已。我们直接看一下spi 转can 的原理图。看U1 部分左边不就是SPI 相关的内容吗 pin 脚标记的16 、15/14/13/12 这些就是SPI 的四根线右边看 RXCAN 、TXCAN 输出的就是can 信号。目标就是你要看懂原理图这个太基本了。 或者通过相关资料来读懂原理图。这里有特别容易忽略的问题就是IC 的原理图和实际的物理要对应上千万不要搞错。GPIO/CAN/485 Interfcae 原理图如如下红丝标记的J1 原理图要和实际的对上这样才能正确接线。数据手册分析收据手册本身要读懂要搞明白机制每一块的内容都特别重要下面初步看一下目录有哪些内容本身就是一个说明使用书器件概述工作模式所以驱动连接外设后在驱动第一步就是复位 进入配置模式。SPI接口这里特别重要数据手册里面都说了支持00 和 11 两种模式所以在实际设备树配置的时候要配置好不然无法通信。同时要关注一下数据手册里面有误发送数据介绍说明默认优先高位。三、设备树配置我们看看我们之前的设备树基本配置、必备配置 参考 Linux驱动-SPI外设代码框架编写spi0{statusokay;pinctrl-0spi0m1_cs0spi0m1_pins;pinctrl-1spi0m1_cs0spi0m1_pins_hs;mcp2515:mcp25150{compatiblemy-mcp2515;reg0;spi-max-frequency10000000;statusokay;};};说一下 设备树配置外设时候针对SPI 关注几个点compatible 匹配外设驱动reg 片选信号spi-max-frequency : 始终频率 也就是传输速度去手册里面找。10MHzspi-cpha; spi-cpol; 极性和相位外设默认是0,0 支持的就是0,0 或者 1,1 。 就按默认的来匹配不要配置这两个属性。spi-lsb-first; 低位优先传输默认是高位优先传输所有不要配置这个属性。spi-cs-high 片选极性高电平有效。 mcp2515 默认是低电平有效所有不要写这个属性。两点特别注意你自己要学会看懂操作手册如果有特别的说明 那么对应的就要在设备树上体现如果没有特别体现就用默认同时不要写不必要的属性。四、SPI 复位函数编写1、复位函数编写定义一个SPI设备指针staticstruct spi_device*g_mcp2515_spiNULL;定义复位指令#defineMCP2515_CMD_RESET0xC0// 复位指令 1100 0000SPI设备指针 赋值在mcp2515_probe回调函数中赋值// MCP2515设备初始化函数staticintmcp2515_probe(struct spi_device*spi){intret;printk(This is mcp2515 probe\n);// 保存spi设备指针g_mcp2515_spispi;// 【上电首先执行MCP2515软件复位】retmcp2515_hw_reset(spi);if(ret){printk(mcp2515 hw reset fail!\n);returnret;}// // 后续可以在这里继续添加寄存器初始化代码return0;}MCP2515 SPI软件复位函数/* MCP2515 SPI软件复位函数 */staticintmcp2515_hw_reset(struct spi_device*spi){u8 cmdMCP2515_CMD_RESET;intret;if(!spi){printk(mcp2515 spi dev is NULL!\n);return-EINVAL;}// 发送单字节复位命令retspi_write(spi,cmd,sizeof(cmd));if(ret0){printk(mcp2515 reset cmd send failed, ret:%d\n,ret);returnret;}printk(mcp2515 send reset command(0xC0) success\n);msleep(10);// 等待MCP2515内部复位完成参考数据手册要求return0;}对比两种复位操作-写入函数如上是一种写操作写了复位如下给出另外一种方案// MCP2515芯片复位函数voidmcp2515_reset(void){intret;charwrite_buf[]{0xc0};// 复位指令0x11000000即0xc0retspi_write(spi_dev,write_buf,sizeof(write_buf));// 发送复位命令if(ret0){printk(spi_write is error\n);// 打印错误信息}}这两种实现方案的底层逻辑其实是一摸一样的一个是标准写法一个是极简写法推荐第一套写法。其实 这里最核心特别注意的一点是如何声明的问题charvsu8char write_buf[]{0xc0}在 ARM 平台可能是有符号 char规范驱动代码统一使用内核linux/types.h的u8无符号字节类型避免符号扩展带来的隐性问题。2、带复位函数完整驱动编写#includelinux/init.h#includelinux/module.h#includelinux/spi/spi.h#includelinux/fs.h#includelinux/kdev_t.h#includelinux/cdev.h#includelinux/uaccess.h#includelinux/delay.h// 新增msleep 头文件staticdev_t dev_num;staticintmajor;staticintminor;staticstruct cdev mcp2515_cdev;structclass*mcp2515_class;struct device*mcp2515_device;staticstruct spi_device*g_spi_dev;// 新增全局保存spi设备指针/* MCP2515 SPI复位函数 */staticintmcp2515_reset(void){intret;uchar cmd0xC0;// MCP2515 复位指令 11000000 0xC0if(!g_spi_dev){printk(mcp2515_reset: spi dev pointer is NULL!\n);return-ENODEV;}retspi_write(g_spi_dev,cmd,sizeof(cmd));if(ret0){printk(mcp2515_reset spi_write failed, ret%d\n,ret);returnret;}msleep(10);// MCP2515复位后等待芯片稳定手册建议延时printk(mcp2515 send reset cmd success\n);return0;}/*打开设备函数*/staticintmcp2515_open(struct inode*inode,struct file*file){printk(This is mcp2515_open \n);return0;}staticssize_tmcp2515_read(struct file*file,char__user*buf,size_t size,loff_t*off){printk(This is mcp2515_cdev read\r\n);return0;}/*向设备写入数据函数*/staticssize_tmcp2515_write(struct file*file,constchar__user*buf,size_t size,loff_t*off){printk(This is mcp2515_cdev_write\r\n);return0;}staticintmcp2515_release(struct inode*inode,struct file*file){printk(This is chrdev_release\r\n);return0;}staticstruct file_operations mcp2515_fops{.ownerTHIS_MODULE,.openmcp2515_open,.readmcp2515_read,.writemcp2515_write,.releasemcp2515_release,};// MCP2515 设备初始化函数staticintmcp2515_probe(struct spi_device*spi){printk(This is mcp2515 probe\n);g_spi_devspi;// 保存spi设备指针mcp2515_reset();// ✅ 进入probe后执行芯片复位return0;}// MCP2515 设备移除函数staticintmcp2515_remove(struct spi_device*spi){printk(This is mcp2515 mcp2515_remove\n);g_spi_devNULL;// 移除时清空全局指针防止野指针return0;}// MCP2515 设备匹配表用于设备树匹配staticconststruct of_device_id mcp2515_of_match_table[]{{.compatiblemy-mcp2515},{/* sentinel */}};// MCP2515 设备 ID 匹配表用于总线匹配staticconststruct spi_device_id mcp2515_id_table[]{{mcp2515,0},{/* sentinel */}};// MCP2515 SPI 驱动结构体staticstruct spi_driver spi_mcp2515{.probemcp2515_probe,.removemcp2515_remove,.driver{.namemcp2515,.ownerTHIS_MODULE,.of_match_tablemcp2515_of_match_table,},.id_tablemcp2515_id_table,};// 驱动初始化函数staticint__init mcp2515_init(void){intret;retalloc_chrdev_region(dev_num,0,1,mcp2515);if(ret0){printk(alloc_chrdev_region is error\n);gotoerr_out;}printk(alloc_chrdev_region is ok\n);majorMAJOR(dev_num);minorMINOR(dev_num);printk(major is %d\n,major);printk(minor is %d\n,minor);cdev_init(mcp2515_cdev,mcp2515_fops);mcp2515_cdev.ownerTHIS_MODULE;retcdev_add(mcp2515_cdev,dev_num,1);if(ret0){printk(cdev_add is error\n);gotoerr_unreg_devnum;}printk(cdev_add is ok\n);mcp2515_classclass_create(THIS_MODULE,spi_to_can);if(IS_ERR(mcp2515_class)){retPTR_ERR(mcp2515_class);printk(class_create failed!\n);gotoerr_cdev_del;}mcp2515_devicedevice_create(mcp2515_class,NULL,dev_num,NULL,mcp2515);if(IS_ERR(mcp2515_device)){retPTR_ERR(mcp2515_device);printk(device_create failed!\n);gotoerr_class_destroy;}// 注册 SPI 驱动retspi_register_driver(spi_mcp2515);if(ret0){printk(spi_register_driver error\n);gotoerr_dev_destroy;}printk(mcp2515 module load complete\n);return0;// 错误回滚分支新增防止资源泄漏oopserr_dev_destroy:device_destroy(mcp2515_class,dev_num);err_class_destroy:class_destroy(mcp2515_class);err_cdev_del:cdev_del(mcp2515_cdev);err_unreg_devnum:unregister_chrdev_region(dev_num,1);err_out:returnret;}// 驱动退出函数staticvoid__exit mcp2515_exit(void){spi_unregister_driver(spi_mcp2515);device_destroy(mcp2515_class,dev_num);class_destroy(mcp2515_class);cdev_del(mcp2515_cdev);unregister_chrdev_region(dev_num,1);printk(module exit \n);}module_init(mcp2515_init);module_exit(mcp2515_exit);MODULE_LICENSE(GPL);总结本篇内容核心知识点会看使用手册、原理图、通过原理图接线设备树的配置相关技能必须根据使用手册来针对性配置复位函数通过spi 的 相关方法spi_write进行写操作

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价