ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux驱动-SPI-读寄存器函数

Linux驱动-SPI-读寄存器函数 Linux驱动-SPI-读寄存器函数文章目录前言一、参考文档二、目实验目标三、踩坑-难点-基础点通过原理图来接线1、 原理图和物理IC对应上2、 务必看清实际丝印基本查错技能查电压查电阻四、操作手册分析分析工作模式分析读写指令分析操作指令五、实验-操作-读取复位寄存器的值完整驱动代码实验结果-验证-分析核心代码总结前言这里总结一下读取SPI寄存器的这个知识点同步梳理这里面的基础。一、参考文档之前的SPI基础知识必须掌握的Linux驱动-初步认识SPILinux驱动-SPI外设代码框架编写Linux驱动-SPI-框架中添加字符设备Linux驱动-SPI-mcp2515 复位函数二、目实验目标其实对于寄存器的读写我们之前I2C 、 单总线协议 都有过相关外设操作。 都是基于api 接口的操作要懂原理即可。基于软件读写寄存器操作时候一定要搞清楚硬件连接所以要会看原理图同时也要会查错基于自己能力范围内各方面情况下查错。三、踩坑-难点-基础点通过原理图来接线之前文章分析过这里要重点看看之前文章 Linux驱动-SPI-mcp2515 复位函数1、 原理图和物理IC对应上如下实物图和原理图要对上包括实际座子和原理图也要对上不然你怎么接线呢2、 务必看清实际丝印再看一下丝印的排版三角号代表1 右边对应的是2 。这个跟实际原理图上面的 pin 脚编号恰好不一样。 所以 物理pin脚如何对应原理图pin 脚。 所以 一定要看清楚然后知道 物理图上面的pin 脚和 实物的pin 脚编号 是如何对应上的这个特别特别重要。基本查错技能假若你本身就是一个新手又是搞软件的对外设、硬件、各种工具器材根本无从下手所以极易出现硬件问题导致一脸懵逼那么必须具备基础的查错功能。会用万用表查电压、查电阻、验证是否虚焊 等导致。查电压为什么查电压就是检查pin 脚 是否有稳定电压输出比如哪一个pin脚是电源输出 那么 接pin 脚电源输出的脚另外一个万用表线接地验证。 基础中的基础黄色圈出来的就是GND 接地假如红线是电压那么用万用表查一下看有没有电压显示就判断pin脚有没有接通查电阻为什么查电阻其实也是验证有没有通比如如下1 是主板板卡pin 脚 2 是座子 是通过自己人为焊接上去的 3 是 外设座子直接固定在外设上面的。 问题 你能保证1 和 3 之间一定是通的 所以 你可以测试1 和 3 之间的电阻电阻几乎忽略不计代表这个1 和 2 之间是通的。 有时候要多测试几次 可能存在虚焊或者接触不良。四、操作手册分析操作手册本身特别重要都要看的但是几个关键点更要搞清楚不然没法用的。分析工作模式一般外设第一步就是复位操作这个当前外设说了 复位自动进入配置模式通过去取某个寄存器来验证当前模式分析读写指令读写是两种操作也就是读谁、给谁写 指令指令多少。所以一般会有两个参数 第一个参数是读或写操作、第二个参数就是数据谁、写多少分析操作指令这些不就是各个寄存器的地址嘛对寄存器的地址就是谁谁谁 最终不就是对寄存器进行读写操作的嘛来实现功能。五、实验-操作-读取复位寄存器的值完整驱动代码#includelinux/init.h#includelinux/module.h#includelinux/spi/spi.h#includelinux/cdev.h#includelinux/fs.h#includelinux/kdev_t.h#includelinux/uaccess.h#defineCNF10x2a// 寄存器定义#defineCNF20x29#defineCNF30x28#defineRXB0CTRL0x60#defineCANINTE0x2b#defineCANCTRL0xf// CAN控制寄存器#defineTXB0CTRL0x30//发送缓冲器控制寄存器地址#defineCANINTF0x2cdev_t dev_num;// 设备号struct cdev mcp2515_cdev;// 字符设备结构体structclass*mcp2515_class;// 设备类struct device*mcp2515_device;// 设备struct spi_device*spi_dev;// SPI设备指针// 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);// 打印错误信息}}// MCP2515读寄存器函数charmcp2515_read_reg(charreg){charwrite_buf[]{0x03,reg};// SPI写缓冲区写入SPI读指令0x03charread_buf;// SPI读缓冲区intret;retspi_write_then_read(spi_dev,write_buf,sizeof(write_buf),read_buf,sizeof(read_buf));// 调用SPI写读函数if(ret0){printk(spi_write_then_read error\n);returnret;}returnread_buf;}// MCP2515写寄存器函数voidmcp2515_write_reg(charreg,charvalue){intret;charwrite_buf[]{0x02,reg,value};// SPI写缓冲区用于发送写寄存器命令retspi_write(spi_dev,write_buf,sizeof(write_buf));// 发送SPI写命令if(ret0){printk(mcp2515_write_reg error\n);}}// MCP2515修改寄存器位函数voidmcp2515_change_regbit(charreg,charmask,charvalue){intret;charwrite_buf[]{0x05,reg,mask,value};// SPI写缓冲区用于发送修改寄存器位命令retspi_write(spi_dev,write_buf,sizeof(write_buf));// 发送SPI写命令if(ret0){printk(mcp2515_change_regbit error\n);}}// 打开设备文件的回调函数intmcp2515_open(struct inode*inode,struct file*file){return0;// 返回成功}// 读设备操作函数从设备读取数据到用户缓冲区ssize_tmcp2515_read(struct file*file,char__user*buf,size_t size,loff_t*offset){charr_kbuf[13]{0};// 内核缓冲区用于存储从设备读取的数据inti;intret;// 等待接收缓冲区满标志位被设置while(!(mcp2515_read_reg(CANINTF)(10)));// 从接收缓冲区读取数据到内核缓冲区for(i0;isizeof(r_kbuf);i){r_kbuf[i]mcp2515_read_reg(0x61i);}// 清除接收缓冲区满标志位mcp2515_change_regbit(CANINTF,0x01,0x00);// 将内核缓冲区的数据复制到用户缓冲区retcopy_to_user(buf,r_kbuf,size);if(ret){printk(copy_to_user r_kbuf is error\n);return-1;// 返回-1表示复制数据失败}return0;// 返回0表示成功读取数据}// 写设备操作函数ssize_tmcp2515_write(struct file*file,constchar__user*buf,size_t size,loff_t*offset){charw_kbuf[13]{0};intret;inti;// 设置TXB0CTRL寄存器的部分位mcp2515_change_regbit(TXB0CTRL,0x03,0x03);// 从用户空间复制数据到内核缓冲区retcopy_from_user(w_kbuf,buf,size);if(ret){printk(copy_from_user w_kbuf is error\n);return-1;}// 将数据写入MCP2515寄存器for(i0;isizeof(w_kbuf);i){mcp2515_write_reg(0x31i,w_kbuf[i]);}// 设置TXB0CTRL寄存器的部分位启动发送mcp2515_change_regbit(TXB0CTRL,0x08,0x08);// 等待发送完成while(!(mcp2515_read_reg(CANINTF)(12)));// 清除发送完成标志mcp2515_change_regbit(CANINTF,0x04,0x00);returnsize;}// 关闭设备文件的回调函数intmcp2515_release(struct inode*inode,struct file*file){return0;// 返回成功}// 设备文件操作集合struct file_operations mcp2515_fops{.openmcp2515_open,.readmcp2515_read,.writemcp2515_write,.releasemcp2515_release,};// MCP2515设备初始化函数intmcp2515_probe(struct spi_device*spi){intret,value;printk(This is mcp2515_probe\n);spi_devspi;// 保存SPI设备指针// 分配字符设备号retalloc_chrdev_region(dev_num,0,1,mcp2515);if(ret0){printk(alloc_chrdev_region error\n);}// 初始化字符设备cdev_init(mcp2515_cdev,mcp2515_fops);mcp2515_cdev.ownerTHIS_MODULE;// 添加字符设备retcdev_add(mcp2515_cdev,dev_num,1);if(ret0){printk(cdev_add error\n);return-1;}// 创建设备类mcp2515_classclass_create(THIS_MODULE,spi_to_can);if(IS_ERR(mcp2515_class)){printk(mcp2515_class error\n);returnPTR_ERR(mcp2515_class);}// 创建设备mcp2515_devicedevice_create(mcp2515_class,NULL,dev_num,NULL,mcp2515);if(IS_ERR(mcp2515_device)){printk(mcp2515_device error\n);returnPTR_ERR(mcp2515_device);}mcp2515_reset();// 复位MCP2515设备valuemcp2515_read_reg(0x0e);// 读取寄存器值printk(value is %x\n,value);// 打印读取的值mcp2515_write_reg(CNF1,0x01);// 写入寄存器配置值mcp2515_write_reg(CNF2,0xb1);mcp2515_write_reg(CNF3,0x05);mcp2515_write_reg(RXB0CTRL,0x60);mcp2515_write_reg(CANINTE,0x05);mcp2515_change_regbit(CANCTRL,0xe0,0x40);valuemcp2515_read_reg(0x0e);// 读取寄存器值printk(value is %x\n,value);// 打印读取的值return0;// 返回成功}// MCP2515 SPI设备的移除函数staticintmcp2515_remove(struct spi_device*spi){device_destroy(mcp2515_class,dev_num);class_destroy(mcp2515_class);cdev_del(mcp2515_cdev);unregister_chrdev_region(dev_num,1);return0;}// MCP2515设备匹配表,用于设备树匹配staticconststruct of_device_id mcp2515_of_match_table[]{{.compatiblemy-mcp2515},{}};// MCP2515设备ID匹配表,用于总线匹配staticconststruct spi_device_id mcp2515_id_table[]{{mcp2515,0},{}};// 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,// 设备ID匹配表};// 驱动初始化函数staticint__initmcp2515_init(void){intret;// 注册SPI驱动retspi_register_driver(spi_mcp2515);if(ret0){// 注册失败,打印错误信息printk(spi_register_driver error\n);returnret;}returnret;}// 驱动退出函数staticvoid__exitmcp2515_exit(void){// 注销SPI驱动spi_unregister_driver(spi_mcp2515);}module_init(mcp2515_init);module_exit(mcp2515_exit);MODULE_LICENSE(GPL);实验结果-验证-分析[roottopeet:/mnt/sdcard]#[1204.602120]Thisis mcp2515_probe[1204.602930][MCP2515]READREG[0x0E]0x80[1204.602959]value is80[1204.603274][MCP2515]READREG[0x0E]0x40[1204.603293]value is40我们看一下80 的二级制是多少0x80,换算成二进制为 10000000bit7-bit5 为 100核心代码mcp2515_reset();// 复位MCP2515设备valuemcp2515_read_reg(0x0e);// 读取寄存器值printk(value is %x\n,value);// 打印读取的值// 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);// 打印错误信息}}// MCP2515读寄存器函数charmcp2515_read_reg(charreg){charwrite_buf[]{0x03,reg};// SPI写缓冲区写入SPI读指令0x03charread_buf;// SPI读缓冲区intret;retspi_write_then_read(spi_dev,write_buf,sizeof(write_buf),read_buf,sizeof(read_buf));// 调用SPI写读函数if(ret0){printk(spi_write_then_read error\n);returnret;}returnread_buf;}小结复位mcp2515_reset就是告诉寄存器我要复位发一个寄存器地址。读取mcp2515_read_reg(0x0e);ox01 就是哪一个寄存器 拼接数据write_buf[] {0x03, reg}ox03 说明要读。 那么通过apispi_write_then_read写地址 是要进行读操作 再 读取哪一个寄存器地址。总结学会看原理图和使用手册学会对寄存器进行读写数据建议看一看I2C,其实都是差不多的。
返回列表