STM32MP157A-FSMP1A单片机移植Linux系统SPI总线驱动

SPI总线驱动整体上与I2C总线驱动类型,差别主要在设备树和数据传输上,由于SPI是由4根线实现主从机的通信,在设备树上配置时需要对SPI进行设置。

原理图可知,数码管使用的SPI4对应了单片机上的PE11-->SPI4-NSS,PE12-->SPI4-SCK,PE13-->SPI4-MISO,PE14-->SPI4-MOSI

设备树信息

需要注意在SPI4设备树信息中,一定要加spi-max-frequency属性,该属性用于设置spi的主频,如果没有,可能出现无法正常启动spi或启动spi后控制数码管时,数码管显示出现错位现象

驱动源码

#include <linux/init.h>       // 包含内核初始化相关的头文件
#include <linux/module.h>     // 包含内核模块相关的头文件
#include <linux/of.h>         // 包含设备树操作相关的头文件
#include <linux/gpio.h>       // 包含 GPIO 操作相关的头文件
#include <linux/of_gpio.h>    // 包含设备树 GPIO 相关的头文件
#include <linux/fs.h>         // 包含文件操作相关的头文件
#include <linux/uaccess.h>    // 包含用户空间访问内核空间相关的头文件
#include <linux/device.h>     // 包含设备相关的头文件
#include <linux/cdev.h>       // 包含字符设备相关的头文件
#include <linux/slab.h>       // 包含内存分配相关的头文件
#include <linux/spi/spi.h>    // 包含 SPI 相关的头文件
#include "spi_test.h"    // 包含自定义头文件//创建设备号
static int major;
//创建类
static struct class *cls;
//创建设备
static struct device *device;
//创建SPI设备
static struct spi_device *spi_dev;
//保存字符设备数据
static char spi_buf[128];static int myspi_open(struct inode *inode, struct file *file)
{unsigned int cmajor;//保存次设备号cmajor = iminor(inode);//将次设备号保存到file结构体的private_data中file->private_data = (void *)cmajor;printk("spi_open\n");return 0;
}static int myspi_close(struct inode *inode, struct file *file)
{printk("spi_close\n");return 0;
}static ssize_t myspi_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{unsigned int ret;//从内核空间读取数据到用户空间,如果读取数据的长度大于spi_buf的长度,按照最大长度读取if(count > sizeof(spi_buf)){count = sizeof(spi_buf);}ret = copy_to_user(buf, spi_buf, count);if(ret < 0){printk("copy_to_user failed\n");return -1;}printk("spi_read\n");return 0;
}static ssize_t myspi_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{unsigned int ret;//将用户空间的数据拷贝到内核空间,如果传输数据的长度大于spi_buf的长度,按照最大长度传输if (count > sizeof(spi_buf)){count = sizeof(spi_buf);}ret = copy_from_user(spi_buf, buf, count);if (ret < 0){printk("copy_from_user failed\n");return -1;}printk("spi_write\n");return 0;
}static int myspi_write_data(const uint8_t cmd, const uint8_t data)
{uint8_t spi_buf1[2] = {cmd, data};int ret = 0;printk("cmd = %x\n", spi_buf1[0]);ret = spi_write(spi_dev, spi_buf1, 2);return ret;
}static long myspi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{uint8_t data;unsigned long ret ;//从用户空间读取数据ret = copy_from_user(&data, (void *)arg, sizeof(data));if (ret < 0){printk("copy_from_user failed\n");return -1;}//根据cmd的值进行不同的操作    switch (cmd){case SET_DAT0:ret = myspi_write_data(0x01, data);break;case SET_DAT1:ret = myspi_write_data(0x02, data);break;case SET_DAT2:ret = myspi_write_data(0x04, data);break;case SET_DAT3:ret = myspi_write_data(0x08, data);break; case SET_DAT4:ret = myspi_write_data(0x0f, data);break;default:break;}if (ret != 0){printk("spi_write failed\n");return -1;}printk("spi_ioctl, data = %x\n", data);return 0;
}//定义file_operations结构体
struct file_operations fops = {.owner = THIS_MODULE,.open = myspi_open,.release = myspi_close,.read = myspi_read,.write = myspi_write,.unlocked_ioctl = myspi_ioctl,
};//编写spi设备驱动
int	myspi_probe(struct spi_device *spi)
{//unsigned char buf[]={0X01,0X6D};//0X01表示写数据,0X6D表示写入的数据//spi_write(spi,buf,sizeof(buf));//给从机发送一个数据,让最左边数码管显示数字5printk("myspi_probe\n");spi_dev = spi;//1.创建设备号major = register_chrdev(0, "myspi", &fops);if (major < 0){printk("register_chrdev failed\n");return -1;}//2.创建类cls = class_create(THIS_MODULE, "myspi");if (IS_ERR(cls)){printk("class_create failed\n");unregister_chrdev(major, "myspi");return -1;}//3.创建设备device = device_create(cls, NULL, MKDEV(major, 0), NULL, "myspi");if (IS_ERR(device)){printk("device_create failed\n");class_destroy(cls);unregister_chrdev(major, "myspi");return -1;}return 0;
}int	myspi_remove(struct spi_device *spi)
{printk("myspi_remove\n");    device_destroy(cls, MKDEV(major, 0));class_destroy(cls);unregister_chrdev(major, "myspi");return 0;
}static const struct of_device_id spi_table[] = {{ .compatible = "johnson,m74hc595", },{ /* sentinel */ },
};struct spi_driver myspi_driver = {.probe = myspi_probe,.remove = myspi_remove,.driver = {.name = "m74hc595",.of_match_table = spi_table,},
};//一键注册宏定义
module_spi_driver(myspi_driver);
MODULE_LICENSE("GPL");  //声明开源许可

头文件

#ifndef __SPITEST_H__
#define __SPITEST_H__#define SET_DAT0 _IOW('s', 0, int)
#define SET_DAT1 _IOW('s', 1, int)
#define SET_DAT2 _IOW('s', 2, int)
#define SET_DAT3 _IOW('s', 3, int)
#define SET_DAT4 _IOW('s', 4, int)#endif

应用程序 --> 实现在数码管上依次显示数字

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include "spi_test.h"const uint8_t num[10] = {0X3F,0X06,0X5B,0X4F,0X66,0X6D,0X7D,0X07,0X7F,0X6F};int main()
{int fd = 0;int date = 0;//打开设备文件fd = open("/dev/myspi", O_RDWR);if (fd < 0){printf("open myspi failed\n");return -1;}while(1){ioctl(fd, SET_DAT0, &num[date]);sleep(1);ioctl(fd, SET_DAT1, &num[date]);sleep(1);ioctl(fd, SET_DAT2, &num[date]);sleep(1);ioctl(fd, SET_DAT3, &num[date]);sleep(1);ioctl(fd, SET_DAT4, &num[date]);if (++date > 9){date = 0;}sleep(1);}   //关闭设备文件close(fd);return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

springboot博客系统详解与实现(后端实现)

目录 前言&#xff1a; 项目介绍 一、项目的准备工作 1.1 数据准备 1.2 项目创建 1.3 前端页面的准备 1.4 配置配置文件 二、公共模块 2.1 根据需求完成公共层代码的编写 2.1.1 定义业务状态枚举 2.1.2 统一返回结果 2.1.3 定义项目异常 2.1.4 统一异常处理 三、业…

Metal 学习笔记四:顶点函数

到目前为止&#xff0c;您已经完成了 3D 模型和图形管道。现在&#xff0c;是时候看看 Metal 中两个可编程阶段中的第一个阶段&#xff0c;即顶点阶段&#xff0c;更具体地说&#xff0c;是顶点函数。 着色器函数 定义着色器函数时&#xff0c;可以为其指定一个属性。您将在本…

Kafka可视化工具EFAK(Kafka-eagle)安装部署

Kafka Eagle是什么&#xff1f; Kafka Eagle是一款用于监控和管理Apache Kafka的开源系统&#xff0c;它提供了完善的管理页面&#xff0c;例如Broker详情、性能指标趋势、Topic集合、消费者信息等。 源代码地址&#xff1a;https://github.com/smartloli/kafka-eagle 前置条件…

vue3.0将后端返回的word文件流转换为pdf并导出+html2pdf.js将页面导出为pdf

实现思路 1.将Word文档转换为HTML&#xff1a;mammoth.js&#xff0c;它可以将.docx文件转换为HTML 2.将HTML转换为PDF&#xff1a;使用html2pdf.js将HTML转换为PDF 如果想要相同的效果&#xff0c;也可以把前端页面直接导出转换为pdf: 运用的插件&#xff1a;html2pdf.js 后端…

lowagie(itext)老版本手绘PDF,包含页码、水印、图片、复选框、复杂行列合并等。

入口类&#xff1a;exportPdf ​ package xcsy.qms.webapi.service;import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.nacos.common.utils.StringUtils; import com.ibm.icu.text.RuleBasedNumberFormat; import com.lowa…

基于JAVA+SpringBoot+Vue的前后端分离的简历系统

基于JAVASpringBootVue的前后端分离的简历系统 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末附源码下载链接&#x1f345…

AutoGen 技术博客系列 八:深入剖析 Swarm—— 智能体协作的新范式

本系列博文在掘金同步发布, 更多优质文章&#xff0c;请关注本人掘金账号&#xff1a; 人肉推土机的掘金账号 AutoGen系列一&#xff1a;基础介绍与入门教程 AutoGen系列二&#xff1a;深入自定义智能体 AutoGen系列三&#xff1a;内置智能体的应用与实战 AutoGen系列四&am…

可视化工具SciChart如何结合Deepseek快速创建一个React仪表板?

SciChart JavaScript Charts图表库能帮助用户来探索JS应用程序的最终解决方案&#xff0c;使用WebGL创建动态、高速的图表和图形&#xff0c;非常适合实时处理复杂的数据可视化&#xff0c;使用其强大而灵活的JS图表工具可以提升JavaScript项目。 通过在1000多个输出类型上使用…

Cesium@1.126.0,创建3D瓦片,修改样式

第一步&#xff1a;添加3D建筑 Cesium.createOsmBuildingsAsync()这是一个异步方法&#xff0c;所以要写在一个异步函数里 创建一个函数 const create3DBuilding async (viewer) > {try {// 添加3D建筑const tileset await Cesium.createOsmBuildingsAsync();viewer.scen…

二、大模型微调技术栈全解析

大模型微调技术栈全解析&#xff1a;从微调方法到算力支撑 在大模型的领域中&#xff0c;微调&#xff08;Fine-tuning&#xff09;就像是为模型量身定制的高级裁缝服务&#xff0c;能够让通用的大模型更好地适应特定的任务和场景。而要完成这项精细的工作&#xff0c;需要一整…

ARM Linux下FFmpeg+Nginx+RTMP 视频监控

一、流媒体协议 RTSP&#xff08;Real-Time Stream Protocol&#xff09;由 Real Networks 和 Netscape 共同提出的&#xff0c;基于文本的多媒体播放 控制协议。RTSP 定义流格式&#xff0c;流数据经由 RTP 传输&#xff1b;RTSP 实时效果非常好&#xff0c;适合视频聊天&…

图扑 HT for Web 总线式拓扑图的可视化实现

在图形用户界面&#xff08;GUI&#xff09;设计中&#xff0c;自定义连线技术不仅提升了用户体验&#xff0c;还为复杂数据可视化开辟了新的可能性。该功能点允许用户灵活地在界面元素之间创建视觉连接&#xff0c;使流程图、思维导图和网络拓扑图等信息呈现更加直观和动态。 …

大语言模型中的梯度值:深入理解与应用

1. 摘要 ​ 梯度是微积分中的一个基本概念&#xff0c;在机器学习和深度学习中扮演着至关重要的角色。特别是在大语言模型&#xff08;LLM&#xff09;的训练过程中&#xff0c;梯度指导着模型参数的优化方向。 本报告首先由浅入深地介绍梯度的概念&#xff0c;包括其数学定义…

Linux的用户管理

Linux系统是一个多用户多任务的操作系统&#xff0c;任何一个要使用系统资源的用户&#xff0c;都必须首先向系统管理员申请一个账号&#xff0c;然后以这个账号的身份进入系统 root用户可以创建多个普通用户 一、添加用户 基本语法&#xff1a;useradd 用户名 当创建用户成…

C++第十七讲:map和set封装

C第十七讲&#xff1a;map和set封装 1.源码发现不同2.Mymap && Myset2.1红黑树的源码更改2.2迭代器的实现2.2.1源码的迭代器区别2.2.2const iterator的实现 2.3insert的实现2.4operator[]的理解 这一讲比较困难&#xff0c;我们首先会通过看map和set底层的源码&#xf…

Day9 25/2/22 SAT

【一周刷爆LeetCode&#xff0c;算法大神左神&#xff08;左程云&#xff09;耗时100天打造算法与数据结构基础到高级全家桶教程&#xff0c;直击BTAJ等一线大厂必问算法面试题真题详解&#xff08;马士兵&#xff09;】https://www.bilibili.com/video/BV13g41157hK?p4&v…

OpenCV的形态学操作

在计算机视觉中&#xff0c;形态学操作是一种基于集合论的图像处理技术&#xff0c;主要用于分析和处理图像的形状特征。OpenCV 提供了 cv2.morphologyEx() 函数&#xff0c;用于执行多种高级形态学操作。 kernel np.ones((15, 15), np.uint8) 1. 开运算&#xff08;Opening&…

【Python爬虫(50)】从0到1:打造分布式爬虫项目全攻略

【Python爬虫】专栏简介&#xff1a;本专栏是 Python 爬虫领域的集大成之作&#xff0c;共 100 章节。从 Python 基础语法、爬虫入门知识讲起&#xff0c;深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑&#xff0c;覆盖网页、图片、音频等各类数据爬取&#xff…

KylinSP3 | 防火墙和麒麟安全增强设置KySec

一、系统防火墙原理 麒麟操作系统从V10版本开始&#xff0c;默认使用了Firewalld防火墙&#xff0c;Firewalld是能提供动态管理的防火墙&#xff0c;支持网络/防火墙区域&#xff0c;用于定义网络连接或接口的信任级别。支持IPv4和IPv6防火墙设置、以太网桥接和IP集。将运行时…

【NLP 23、预训练语言模型】

人类发明后悔&#xff0c;来证明拥有的珍贵 —— 25.1.15 Bert的优势&#xff1a;① 预训练思想 ② Transformer模型结构 一、传统方法 VS 预训练方式 Pre-train&#xff1a; ① 收集海量无标注文本数据 ② 进行模型预训练&#xff0c;并在任务模型中使用 Fine-tune&#xff1a…