Linux内核编程(十九)SPI子系统的应用与驱动编写

news2024/11/6 6:07:44

本文目录

  • 一、 SPI驱动框架图
  • 二、编写SPI驱动device框架
  • 三、编写SPI驱动driver框架
  • 四、实验一编写mcp2515驱动
    • 1. 注册字符设备或杂项设备框架
    • 2. SPI写数据
    • 3. SPI读寄存器数据
  • 4. MCP2515相关配置

  

对于SPI基础知识这里不做过多讲解,详情查看:SPI基础知识实践讲解-STM32版。

一、 SPI驱动框架图

   本框图中spi核心层和spi适配器驱动层不需要我们去关心,如果未来要去原厂工作的话,可以深入了解其工作原理和内容,这里我们不做过多介绍。
在这里插入图片描述

二、编写SPI驱动device框架

  1. 查看开发板中可用的SPI引脚。
    在这里插入图片描述
  2. 修改设备树文件。
    (1)修改spi控制器源码设备树文件。
       将控制器的pinctrl-0复用引脚的spi0m0修改为我们所使用的spi0m1。由于原厂工程师已经写完spi控制器的引脚复用功能,所以我们只需要修改即可。
spi0: spi@fe610000 {
    compatible = "rockchip,rk3066-spi";
    reg = <0x0 0xfe610000 0x0 0x1000>;  // SPI 控制器寄存器基地址
    interrupts = <GIC SPI 103 IRQ TYPE LEVEL HIGH>;  // 中断配置
    #address-cells = <1>;  // 地址单元数量
    #size-cells = <0>;  // 大小单元数量
    clocks = <&cru CLK_SPI>, <&cru PCLK_SPIO>;  // SPI 和 APB 时钟
    clock-names = "spiclk", "apb pclk";  // 时钟名称
    dmas = <&dmac0 20>, <&dmac0 21>;  // DMA 通道配置(TX, RX)
    dma-names = "tx", "rx";  // DMA 通道名称
    pinctrl-names = "default", "high-speed";  // 引脚控制模式
   // pinctrl-0 = <&spi0m0_cs0 &spi0m0_csl &spi0m0_pins>;  // 默认引脚控制配置
    pinctrl-0 = <&spi0m1_cs0  &spi0m1_pins>;  // 默认引脚控制配置
  //  pinctrl-1 = <&spi0m0_cs0 &spi0m0_csl &spi0m0_pins_high_speed>;  // 高速模式引脚配置
    pinctrl-1 = <&spi0m1_cs0  &spi0m1_pins_high_speed>;  // 高速模式引脚配置
    status = "disabled";  // 当前 SPI 控制器状态,禁用状态
};

(2)添加引用spi控制器
   在开发板设备树根节点下添加以下节点。如果设备树中没有reg 和 spi-max-freguengy这俩个属性就会返回错误。返回错误设备注册不成功。从而就不会和驱动匹配上(具体分析spi控制器的注册流程)。以下的SPI工作模式以及传输模式等都需要根据具体的SPI外设来设置!!

&spi0 {
	status ="okay";
	mcp2515:mcp2515@0{
		status ="okay";
		compatible="my-mcp2515";
		reg = <0>; /* 1. 选择片选0 */
		spi-max-frequency = <24000000>; /* 2. 设置SPI时钟最大频率为24MHz,不要超过50M */
		
		//3. 设置工作模式,如果不写,则默认为0,0
		spi-cpha = <1>;  // 设置时钟相位
		spi-cpol = <0>;  // 设置时钟极性 
		
		//4. 设置传输模式,高位先传还是低位先传。默认为高位先传。
		//spi-lsb-first = <1>;  // 设置为 LSB 优先
		
		//5. 选择片选信号为高电平/低电平选中。默认为低电平选中。
		// spi-cs-high = <1>;      // 设置片选信号为高电平有效
	}
}

   reg 属性用于设置 SPI 设备的片选引脚,而 spi-max-frequency 属性则用于设置 SPI 总线的最大时钟频率。通常,SPI 设备的片选和时钟频率都需要在设备树中进行配置,以便正确地初始化 SPI 设备。

三、编写SPI驱动driver框架

#include <linux/init.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/kernel.h>


static int mcp2515_probe(struct spi_device *spi)
{
    return 0;
}

static int mcp2515_remove(struct spi_device *spi)
{
    return 0;
}

static const struct of_device_id mcp2515_of_match[] = {
    { .compatible = "my-mcp2515", },
    {  }
};
MODULE_DEVICE_TABLE(of, mcp2515_of_match);


MODULE_DEVICE_TABLE(spi, mcp2515_id_table);

static struct spi_driver mcp2515_driver = {
    .driver = {
        .name = "mcp2515",
        .owner = THIS_MODULE,
        .of_match_table = mcp2515_of_match,
    },
    .probe = mcp2515_probe,
    .remove = mcp2515_remove,
};

static int __init mcp2515_init(void)
{
    return spi_register_driver(&mcp2515_driver);
}

static void __exit mcp2515_exit(void)
{
    spi_unregister_driver(&mcp2515_driver);
}

module_init(mcp2515_init);
module_exit(mcp2515_exit);
MODULE_LICENSE("GPL");

四、实验一编写mcp2515驱动

   MCP2515 是由 Microchip 提供的一款独立的 CAN (Controller Area Network) 控制器,它通过 SPI 接口与主机通信。即SPI转CAN的一个模块。

1. 注册字符设备或杂项设备框架

#include <linux/init.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

static ssize_t myread(struct file *fp, char __user *ubuf, size_t size, loff_t *loft)
{
	  return 0;
}

static int my_open(struct inode *node, struct file *fp)
{
       return 0;
}

ssize_t my_write(struct file *fp, const char __user *ubuf, size_t size, loff_t *loft)
{
    // 若不需要实际的写操作,可以直接返回0或-EINVAL
    return 0;
}

static const struct file_operations myfops = {
    .read = myread,
    .open = my_open,
    .write = my_write,
};

static struct miscdevice mcp2515_misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "mcp2515",
    .fops = &myfops,
};

static int mcp2515_probe(struct spi_device *spi)
{
    int ret;
    ret = misc_register(&mcp2515_misc);
    if (ret) {
        pr_err("Failed to register misc device\n");
        return ret;
    }

    return 0;
}

static int mcp2515_remove(struct spi_device *spi)
{
    pr_info("MCP2515 SPI device removed\n");
    misc_deregister(&mcp2515_misc);
    return 0;
}

static const struct of_device_id mcp2515_of_match[] = {
    { .compatible = "my-mcp2515", },
    { }
};

static struct spi_driver mcp2515_driver = {
    .driver = {
        .name = "mcp2515",  //不会与之匹配
        .owner = THIS_MODULE,
        .of_match_table = mcp2515_of_match,
    },
    .probe = mcp2515_probe,
    .remove = mcp2515_remove,
};

static int __init mcp2515_init(void)
{
    pr_info("MCP2515 driver loading\n");
    return spi_register_driver(&mcp2515_driver);
}

static void __exit mcp2515_exit(void)
{
    pr_info("MCP2515 driver unloading\n");
    spi_unregister_driver(&mcp2515_driver);
}

module_init(mcp2515_init);
module_exit(mcp2515_exit);

MODULE_LICENSE("GPL");

2. SPI写数据

成功时:返回发送的字节数,通常是 len。
失败时:返回负的错误代码,通常为 -EINVAL(无效参数)或 -EIO(输入/输出错误)等。

int spi_write(struct spi_device *spi, const void *buf, unsigned len);
/*
	spi:指向目标 SPI 设备的指针。这是你要向其发送数据的 SPI 设备。
	buf:指向包含要发送的数据的缓冲区的指针。这些数据将被通过 SPI 总线发送。
	len:要发送的数据的字节数。表示从 buf 中发送的字节数。
*/

●示例: 我们通过mcp2515手册可知,对其进行写操作需要写入0x02。使用spi_write来进行写操作。

#include <linux/spi/spi.h>

struct spi_device *spi;
void mcp2515_write_reg(char reg, char value)
{
    int ret;
    char write_buf[] = {0x02, reg, value};  // MCP2515 写寄存器的指令

    // 向 MCP2515 发送数据
    ret = spi_write(spi, write_buf, sizeof(write_buf));
    if (ret < 0) {
        printk(KERN_ERR "MCP2515 write reg failed: %d\n", ret);
    }
}


3. SPI读寄存器数据

这里有以下两个API函数。
(1)spi_read(直接读)
成功时:返回接收的字节数(通常是 len)。失败时:返回负的错误代码。

int spi_read(struct spi_device *spi, void *buf, unsigned len);
/*
	spi:指向 SPI 设备的指针。
	buf:指向存放接收数据的缓冲区的指针。接收到的数据将存放在这个缓冲区中。
	len:要接收的数据的字节数。
*/

(2)spi_write_then_read(先写后读)
   这个函数用于 SPI 设备的 “write then read” 操作,即先发送数据,然后读取数据。成功时:返回传输的字节数,通常等于 n_tx 或 n_rx,这取决于设备的特性。失败时:返回负的错误代码(如 -EINVAL 或 -EIO 等)。

int spi_write_then_read(struct spi_device *spi, const void *txbuf, unsigned n_tx, void *rxbuf, unsigned n_rx);
/*
	spi:指向 SPI 设备的指针,表示我们将要操作的 SPI 设备。
	txbuf:指向要发送的缓冲区的指针。它包含我们要发送的数据。
	n_tx:要发送的字节数。
	rxbuf:指向接收数据的缓冲区的指针。读取到的数据会存放在这个缓冲区中。
	n_rx:要接收的字节数。
*/

●示例: mcp2515读寄存器的值,我们查看其手册发现,要想读寄存器的值需要先写入0x03命令,然后发送要读取寄存器的地址,再进行读取值。所以我们需要先写后读。

#include <linux/spi/spi.h>
struct spi_device *spi;

char mcp2515_read_reg(char reg)
{
    int ret;
    u8 write_buf[] = { 0x03, reg };  // 0x03 是读取寄存器命令,具体命令值请参考 MCP2515 数据手册
    u8 read_buf;  // 用于存储读取到的数据

    // SPI write then read 操作
    ret = spi_write_then_read(spi, write_buf, sizeof(write_buf), &read_buf, sizeof(read_buf));
    if (ret < 0) {
        printk(KERN_ERR "SPI write then read error: %d\n", ret);
        return ret;  // 返回错误代码
    }

    printk(KERN_INFO "Read value from MCP2515 register 0x%02X: 0x%02X\n", reg, read_buf);
    return read_buf;  // 返回读取到的寄存器值
}

4. MCP2515相关配置

(1)复位操作(进入配置模式):通过查看器件手册来获取复位命令,再SPI来对其进行写入。

struct spi_device *spi;
static int mcp2515_reset()
{
    int ret;
    u8 write_buf[] = { 0xc0 };  // 假设 0xc0 是复位命令,具体命令根据 datasheet 确定
    // 向 MCP2515 设备写入复位命令
    ret = spi_write(spi, write_buf, sizeof(write_buf));
    if (ret < 0) {
        printk(KERN_ERR "SPI write error: %d\n", ret);
        return ret;
    }
    printk(KERN_INFO "MCP2515 reset command sent successfully.\n");
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2234105.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

谍影重重5.0

打开流量包可以发现&#xff0c;流量中含有大量的smb加密通信&#xff0c;并且使用了ntlm v2加密协议进行身份认证 包过滤ntlmssp 认证后smb协议进行了大量的数据传输 取出tom的包内数据 得到以下数据 username:tomdomain: .NTProofStr: ca32f9b5b48c04ccfa96f35213d63d75NT…

qt QAbstractItemModel详解

1. 概述 QAbstractItemModel是Qt框架中的一个核心抽象基类&#xff0c;在Qt的模型/视图架构中扮演着至关重要的角色。这个类提供了一个接口&#xff0c;用于表示和管理数据&#xff0c;但不直接处理数据的存储。它的主要功能是为视图组件&#xff08;如QListView、QTableView和…

华为OD机试 - 无重复字符的元素长度乘积的最大值(Python/JS/C/C++ 2024 C卷 100分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

【C++之STL】一文学会使用 string

文章目录 1. STL导读1. 1 什么是STL1. 2 STL的版本1. 3 STL六大组件1. 4 STL的重要性1. 5 STL的学习1. 6 STL系列博客的规划 2. string2. 1 为什么学习string类?2. 2 标准库中的string2. 3 基本构造2. 4 尾插与输出运算符重载2. 5 构造函数2. 6 赋值运算符重载2. 7 容量操作2.…

【分布式】分布式锁设计与Redisson源码解析

分布式锁 分布式锁是一种在分布式计算环境中用于控制多个节点&#xff08;或多个进程&#xff09;对共享资源的访问的机制。在分布式系统中&#xff0c;多个节点可能需要协调对共享资源的访问&#xff0c;以防止数据的不一致性或冲突。分布式锁允许多个节点在竞争访问共享资源…

【实测有效】两个Ubuntu系统通过网线传输文件

基本思路是&#xff1a;连接网线后&#xff0c;通过设置静态IP&#xff0c;将两台电脑配置在同一个局域网内&#xff0c;再进行文件传输。 一、物理连接 使用网线将两台电脑的网口连接起来。 二、静态IP设置 两台电脑均需要对各自的静态IP进行设置。 1、查看电脑的IP地址 1&a…

Android camera2

一、序言 为了对阶段性的知识积累、方便以后调查问题&#xff0c;特做此文档&#xff01; 将以camera app 使用camera2 api进行分析。 (1)、打开相机 openCamera (2)、创建会话 createCaptureSession (3)、开始预览 setRepeatingRequest (4)、停止预览 stopRepeating (5)、关闭…

【Redis_Day3】Redis通用命令

【Redis_Day3】Redis通用命令 redis客户端的三种形态redis的快与慢redis通用命令阅读redis官方文档redis中两个核心命令set命令get命令 redis全局命令keys命令&#xff1a;查询当前服务器上的key生产环境 exists命令&#xff1a;判定key是否存在del命令&#xff1a;删除指定的k…

静态库、动态库、framework、xcframework、use_frameworks!的作用、关联核心SDK工程和测试(主)工程、设备CPU架构

1.1库的概念 库&#xff1a;程序代码的集合&#xff0c;编译好的二进制文件加上头文件供使用&#xff0c;共享程序代码的一种方式。 1.2库的分类 根据开源情况分为&#xff1a;开源库&#xff08;能看到具体实现&#xff09;、闭源库&#xff08;只公开调用的的接口&#xf…

小菜家教平台:基于SpringBoot+Vue打造一站式学习管理系统

前言 现在已经学习了很多与Java相关的知识&#xff0c;但是迟迟没有进行一个完整的实践&#xff08;之前这个项目开发到一半&#xff0c;很多东西没学搁置了&#xff0c;同时原先的项目中也有很多的问题&#xff09;&#xff0c;所以现在准备从零开始做一个基于SpringBootVue的…

【C++、数据结构】哈希表——散列表(一)(概念/总结)

「前言」 &#x1f308;个人主页&#xff1a; 代码探秘者 &#x1f308;C语言专栏&#xff1a;C语言 &#x1f308;C专栏&#xff1a; C / STL使用以及模拟实现 &#x1f308;数据结构专栏&#xff1a; 数据结构 / 十大排序算法 &#x1f308;Linux专栏&#xff1a; Linux系统编…

山东路远生态科技有限公司竣工投产仪式暨产品发布会圆满举行

第二十届三中全会于2024年7月15日至18日在北京举行。全会审议通过了《关于进一步全面深化改革、推进中国式现代化的决定》。其中提到,“要健全因地制宜发展新质生产力体制机制”。 新质生产力是由技术革命性突破、生产要素创新性配置、产业深度转型升级而催生的当代先进生产力…

MD5(Crypto)

解题思路 打开文件发现一串代码&#xff0c;结合题目提示&#xff0c;应该是 MD5 加密。 找个在线的 MD5 解密网站&#xff0c;行云流水得到 flag。 题目设计原理 题目设计&#xff1a;无他&#xff0c;MD5 加密。 题目原理&#xff1a; MD5&#xff08;Message-Digest Algo…

EHOME视频平台EasyCVR萤石设备视频接入平台视频诊断技术可以识别哪些视频质量问题?

EasyCVR视频监控汇聚管理平台是一款针对大中型项目设计的跨区域网络化视频监控集中管理平台。萤石设备视频接入平台EasyCVR不仅具备视频资源管理、设备管理、用户管理、运维管理和安全管理等功能&#xff0c;还支持多种主流标准协议&#xff0c;如GB28181、GB35114、RTSP/Onvif…

QML项目实战:自定义Button

目录 一.添加模块 ​1.QtQuick.Controls 2.1 2.QtGraphicalEffects 1.12 二.自定义Button 1.颜色背景设置 2.设置渐变色背景 3.文本设置 4.点击设置 5.阴影设置 三.效果 1.当enabled为true 2.按钮被点击时 3.当enabled为false 四.代码 一.添加模块 1.QtQuick.Con…

实战攻略 | ClickHouse优化之FINAL查询加速

【本文作者&#xff1a;擎创科技资深研发 禹鼎侯】 查询时为什么要加FINAL 我们在使用ClickHouse存储数据时&#xff0c;通常会有一些去重的需求&#xff0c;这时候我们可以使用ReplacingMergeTree引擎。这个引擎允许你存储重复数据&#xff0c;但是在merge的时候会根据order …

labview学习总结

labview学习总结 安装labview的特点一、图形化编程范式二、并行执行机制三、硬件集成能力四、应用领域优势五、开发效率六、系统集成能力**labview基本组成示意图****常用程序结构图解**结语 基础知识介绍界面前后面板的概念平铺式和层叠式 帧的概念结构类型顺序结构for循环whi…

Linux 服务器使用指南:从入门到登录

&#x1f31f;快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。 &#x1f31f; &#x1f6a9;博主致力于用通俗易懂且不失专业性的文字&#xff0c;讲解计算机领域那些看似枯燥的知识点&#x1f6a9; 目录 一…

《AI 大模型:重塑软件开发新未来》

引言 在科技的璀璨星河中&#xff0c;AI 大模型宛如一颗耀眼的新星&#xff0c;正以前所未有的力量改写着软件开发的篇章。随着其技术的持续演进&#xff0c;软件开发流程正经历着翻天覆地的变化。从代码自动生成的神奇魔法&#xff0c;到智能测试的精准洞察&#xff0c;AI 大…

acmessl.cn提供接口API方式申请免费ssl证书

目录 一、前沿 二、API接口文档 1、证书可申请列表 简要描述 请求URL 请求方式 返回参数说明 备注 2、证书申请 简要描述 请求URL 请求方式 业务参数 返回示例 返回参数说明 备注 3、证书查询 简要描述 请求URL 请求方式 业务参数 返回参数说明 备注 4、证…