SPI接口DAC设备驱动与应用程序开发
本文章相关专栏往期内容,SPI子系统专栏:
- SPI通信协议与Linux设备驱动框架解析
- SPI传输与驱动框架的实现
- spidev.c:SPI设备驱动的核心实现逻辑
PCI/PCIe子系统专栏:
- 专栏地址:PCI/PCIe子系统
- PCIe设备MSI/MSI-X中断源码分析与驱动编写
– 末片,有专栏内容观看顺序Uart子系统专栏:
- 专栏地址:Uart子系统
- Linux内核早期打印机制与RS485通信技术
– 末片,有专栏内容观看顺序interrupt子系统专栏:
- 专栏地址:interrupt子系统
- Linux 链式与层级中断控制器讲解:原理与驱动开发
– 末片,有专栏内容观看顺序pinctrl和gpio子系统专栏:
专栏地址:pinctrl和gpio子系统
编写虚拟的GPIO控制器的驱动程序:和pinctrl的交互使用
– 末片,有专栏内容观看顺序
input子系统专栏:
- 专栏地址:input子系统
- input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序
– 末片,有专栏内容观看顺序I2C子系统专栏:
- 专栏地址:IIC子系统
- 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
– 末篇,有专栏内容观看顺序总线和设备树专栏:
- 专栏地址:总线和设备树
- 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
– 末篇,有专栏内容观看顺序

目录
- 1.spi_dac设备的应用程序
- 1.2 测试示例
- 1.2.1 使用方法
- 1.2.2 代码讲解
- 1.3 编写操作spi_dac设备的应用程序
- 1.3.1 DAC介绍
- 1.3.2 时序图
- 1.3.3 DAC公式
- 1.3.4 编写
- 3.3.5 上机
- 2.编写DAC的驱动程序
- 2.1 编写设备树
- 2.2 代码
1.spi_dac设备的应用程序
1.2 测试示例
内核提供的测试程序:tools\spi\spidev_fdx.c 📎spidev_fdx.c(应用程序)
1.2.1 使用方法
编译该程序并生成可执行文件 spidev_test,你可以使用以下命令来与 SPI 设备交互。
- 查看设备状态:
./spidev_test /dev/spidev0.0
- 发送 SPI 消息:
./spidev_test -m 16 /dev/spidev0.0
- 读取数据:
./spidev_test -r 8 /dev/spidev0.0
- 启用详细模式:
./spidev_test -v /dev/spidev0.0
1.2.2 代码讲解
(1)显示设备属性
static void dumpstat(const char *name, int fd)
{__u8 lsb, bits;__u32 mode, speed;if (ioctl(fd, SPI_IOC_RD_MODE32, &mode) < 0) { //读取当前的 SPI 模式。perror("SPI rd_mode");return;}if (ioctl(fd, SPI_IOC_RD_LSB_FIRST, &lsb) < 0) { //读取传输是 LSB 先还是 MSB 先。perror("SPI rd_lsb_fist");return;}if (ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits) < 0) { //读取每字传输的位数。perror("SPI bits_per_word");return;}if (ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed) < 0) { //读取最大传输速度。perror("SPI max_speed_hz");return;}printf("%s: spi mode 0x%x, %d bits %sper word, %d Hz max\n",name, mode, bits, lsb ? "(lsb first) " : "", speed); //printf打印出设备的参数
}
通过 ioctl 调用,读取当前 SPI 设备的配置信息(模式、LSB/MSB 顺序、每字传输位数、最大速度等),并将其打印到控制台。
(2)读设备数据
static void do_read(int fd, int len)
{unsigned char buf[32], *bp;int status;/* read at least 2 bytes, no more than 32 */if (len < 2)len = 2;else if (len > sizeof(buf))len = sizeof(buf);memset(buf, 0, sizeof buf);status = read(fd, buf, len);//检查并确保读取的数据长度在 2 到 32 字节之间,并通过 read(fd, buf, len) 来从设备读取数据到 buf。if (status < 0) {perror("read");return;}if (status != len) {fprintf(stderr, "short read\n");return;}printf("read(%2d, %2d): %02x %02x,", len, status,buf[0], buf[1]);status -= 2;bp = buf + 2;while (status-- > 0)printf(" %02x", *bp++); // 将读取到的数据打印到控制台。printf("\n");
}
(3)发送消息
static void do_msg(int fd, int len)
{struct spi_ioc_transfer xfer[2];unsigned char buf[32], *bp;int status;memset(xfer, 0, sizeof xfer);memset(buf, 0, sizeof buf);if (len > sizeof buf)len = sizeof buf;buf[0] = 0xaa;xfer[0].tx_buf = (unsigned long)buf;xfer[0].len = 1;xfer[1].rx_buf = (unsigned long) buf;xfer[1].len = len;status = ioctl(fd, SPI_IOC_MESSAGE(2), xfer);if (status < 0) {perror("SPI_IOC_MESSAGE");return;}printf("response(%2d, %2d): ", len, status);for (bp = buf; len; len--)printf(" %02x", *bp++);printf("\n");
}
它不是通过调用write函数,进而去调用spidev驱动中的spidev_write,而是通过Ioctl函数来实现,这是可以的,ioctl函数在之前就介绍过是可以用来执行一些复杂的数据传输操作。
(4)主函数
int main(int argc, char **argv)
{int c;int readcount = 0;int msglen = 0;int fd;const char *name;while ((c = getopt(argc, argv, "hm:r:v")) != EOF) {switch (c) {case 'm':msglen = atoi(optarg);if (msglen < 0)goto usage;continue;case 'r':readcount = atoi(optarg);if (readcount < 0)goto usage;continue;case 'v':verbose++;continue;case 'h':case '?':
usage:fprintf(stderr,"usage: %s [-h] [-m N] [-r N] /dev/spidevB.D\n",argv[0]);return 1;}}if ((optind + 1) != argc)goto usage;name = argv[optind];fd = open(name, O_RDWR);if (fd < 0) {perror("open");return 1;}dumpstat(name, fd);if (msglen)do_msg(fd, msglen);if (readcount)do_read(fd, readcount);close(fd);return 0;
}
-
解析命令行参数,支持的选项包括:
-
-m N:发送 N 字节的消息。-r N:读取 N 字节的数据。-v:启用详细模式。-h或-?:打印帮助信息。
-
打开指定的 SPI 设备文件并调用
dumpstat打印设备的状态。 -
根据命令行参数调用
do_msg(发送消息)和do_read(读取数据)函数进行 SPI 传输。 -
最后关闭设备文件。
1.3 编写操作spi_dac设备的应用程序
使用的驱动程序是内核提供的spidev.
DAC芯片手册:📎TLC5615.pdfc
1.3.1 DAC介绍
DAC(Digital-to-Analog Converter,数字-模拟转换器)是一种将数字信号转换为模拟电压或电流输出的器件。DAC模块常用于音频输出、信号处理、自动控制系统等场合。


一个典型的 DAC 模块有以下常见的引脚:
-
DIN(Data In,数据输入):
-
- 这是数据输入引脚,通过它传入要转换的数字信号。
- 数据通常以串行方式传输,例如 SPI(Serial Peripheral Interface)通信协议。每次传输的数字信号代表希望生成的模拟输出。
-
SCLK(Serial Clock,串行时钟):
-
- 时钟信号引脚,用于同步数据的传输。
- 通过时钟的上升沿或下降沿(取决于 DAC 的工作模式)将数据从控制器(例如微控制器)发送到 DAC 的数据输入引脚 DIN。SCLK 控制数据传输的时序。
-
CS(Chip Select,片选):
-
- 片选信号引脚,用于启用或禁用 DAC。
- 当 CS 处于低电平时,DAC 会被选中并开始接收数据;当 CS 拉高时,数据传输结束,DAC 停止通信并执行相应的操作(如更新输出电压)。
-
DOUT(Data Out,数据输出):
-
- 这是数据输出引脚,一些高级 DAC 模块带有此引脚,用于反馈通信或级联多个 DAC。
- DOUT 通常用于从 DAC 向主设备返回状态信息,也可以将多个 DAC 模块级联,形成串行数据链。
- 级联下(也就是多个DAC串行),传输的16bit数据
-
Vdd(电源输入):
-
- 为 DAC 提供工作电压的引脚。
- 典型的工作电压范围为 2.7V 到 5.5V 或 3.3V/5V,具体取决于 DAC 的型号。Vdd 为内部数字和模拟电路供电。
-
OUT(模拟输出):
-
- DAC 的模拟电压输出引脚。
- 根据输入的数字信号,这个引脚会输出相应的模拟信号,通常是电压信号(例如 0V-3.3V 或 0V-5V)。在某些情况下,DAC 可以直接输出电流信号。
- 其中传入的是16bit,但是输出的10bit,高四位无效,第两位为0
-
REFIN(Reference Input,基准电压输入):
-
- 基准电压输入引脚,用于提供 DAC 模块的基准电压。
- 基准电压决定了 DAC 的输出范围。例如,如果参考电压为 2.5V,DAC 的输出范围可能会是 0V 到 2.5V。外部基准电压可以提高 DAC 的精度和稳定性。
-
AGND(Analog Ground,模拟地):
-
- 模拟电路的地线。
- 与 Vdd 共同提供 DAC 的电源参考电压,确保 DAC 输出的稳定性。AGND 通常用于模拟部分电路,以减少数字噪声的干扰。
需要特别注意的是OUT和DOUT,因为这两个的差异,输入的16bit数据其高4位和低2位得都为0才能保证传输正确的数据。这个在时序图讲解的时候再提。
1.3.2 时序图

时序图,假设SCLK上升沿的时候读取电平 :

记住SPI其实就是彼此数据的置换:
在CS有效(低电平)期间,DIN(MISO)数据会随着SCLK的每次时钟脉冲被移入从机的移位寄存器,从高位(MSB)开始依次传输到低位(LSB)。同时,DOUT(MOSI)也会将从机中保存的前一次数据从高位(MSB)开始依次移出,传输到主机的移位寄存器中。
在TLC5615的SPI通信中,**DOUT**的第一位对应上一帧(previous frame)的LSB。这是由TLC5615的工作原理和SPI协议的移位寄存器设计决定的。
CS信号拉低后,TLC5615进入通信状态,其移位寄存器不会被清零,而是保留上一帧的数据内容。- 在通信开始时,从机的移位寄存器会直接输出当前内容中的最高位(MSB)。
- 但是,因为SPI通信是连续的,上一帧数据的LSB早已移位到移位寄存器的最高位(MSB位置)。
- 假设上一帧完整的16位数据已经移位完成,寄存器中的最后一个位(LSB)自然会移到最高位(MSB)。
- 当新的时钟信号(SCLK)到来时,从机移位寄存器的最高位(previous LSB)会最先通过
DOUT传出。
SPI协议和TLC5615设计中,移位寄存器在时钟脉冲驱动下始终保持连续性,导致上一帧数据循环至当前帧的最高位。
我觉得主要是得看SPI选择的通信模式吧,像模式0,第一个上升沿就要移入数据,但是此时根本还没有数据,在此之前应该就进行了一次数据的移除。
1.3.3 DAC公式
数字信号怎么转换为模拟信号

OUT引脚输出电压:2 * V****refin ***** (n / 1024),其中n为输出的10bit的10进制值
1.3.4 编写
📎dac_test.c
编写设备树:
&ecspi1 {pinctrl-names = "default";pinctrl-0 = <&pinctrl_ecspi1>;fsl,spi-num-chipselects = <2>;cs-gpios = <&gpio4 26 GPIO_ACTIVE_LOW>, <&gpio4 24 GPIO_ACTIVE_LOW>;status = "okay";dac: dac {compatible = "spidev";reg = <0>;spi-max-frequency = <20000000>;};
};

3.3.5 上机
编译应用程序:
arm-buildroot-linux-gnueabihf-gcc -o dac_test dac_test.c
编译驱动:

2.编写DAC的驱动程序
使用之前讲到的编写SPI设备的驱动框架
2.1 编写设备树
&ecspi1 {pinctrl-names = "default";pinctrl-0 = <&pinctrl_ecspi1>;fsl,spi-num-chipselects = <2>;cs-gpios = <&gpio4 26 GPIO_ACTIVE_LOW>, <&gpio4 24 GPIO_ACTIVE_LOW>;status = "okay";dac: dac {compatible = "spidev";reg = <0>;spi-max-frequency = <20000000>;};
};


2.2 代码
📎dac_test.c
📎dac_drv.c
如果spidev没有被编译进内核,那么先执行:
insmod dac_drv.ko
确定设备节点:
ls /dev/100ask_dac
假设设备节点为/dev/100ask_dac,执行测试程序:
./dac_test /dev/100ask_dac 500
./dac_test /dev/100ask_dac 600
./dac_test /dev/100ask_dac 1000
相关文章:
SPI接口DAC设备驱动与应用程序开发
本文章相关专栏往期内容,SPI子系统专栏: SPI通信协议与Linux设备驱动框架解析SPI传输与驱动框架的实现spidev.c:SPI设备驱动的核心实现逻辑 PCI/PCIe子系统专栏: 专栏地址:PCI/PCIe子系统PCIe设备MSI/MSI-X中断源码分析…...
第十六届蓝桥杯大赛软件赛省赛 Python 大学 B 组 满分题解
题面链接Htlang/2025lqb_python_b 个人觉得今年这套题整体比往年要简单许多,但是G题想简单了出大问题,预估50101015120860,道阻且长,再接再厉 代码仅供学习参考,满分为赛后洛谷中的测评,蓝桥杯官方测评待…...
数据库的种类及常见类型
一,数据库的种类 最常见的数据库类型分为两种,关系型数据库和非关系型数据库。 二,关系型数据库介绍 生产环境主流的关系型数据库有 Oracle、SQL Server、MySQL/MariaDB等。 关系型数据库在存储数据时实际就是采用的一张二维表࿰…...
vue文件预览docx-preview
1、在项目中引入插件docx-preview npm i docx-preview 此插件依赖jszip,所以还要下载jszip:npm i jszip 2、点击在线预览按钮请求接口获取文件流 const blob new Blob([resp.data]) const url URL.createObjectURL(blob);//浏览器本地存储不能直接…...
旧版 VMware 虚拟机迁移至 KVM 平台-案例2
项目背景 需将一台旧版 VMware 虚拟机(VMDK 格式)迁移至 KVM 虚拟化平台,具体要求如下: 格式转换:将 VMDK 转换为 QCOW2 格式。磁盘扩容:将原 40GB 磁盘扩展至 60GB。密码重置:修改 aiden 用户…...
若依代码生成器原理velocity模板引擎(自用)
1.源码分析 代码生成器:导入表结构(预览、编辑、删除、同步)、生成前后端代码 代码生成器表结构说明: 若依提供了两张核心表来存储导入的业务表信息: gen_table:存储业务表的基本信息 ,它对应于配置代码基本信息和生成信息的页…...
OpenCV直方图均衡化全面解析:从灰度到彩色图像的增强技术
目录 一、直方图均衡化基础:原理与核心思想 二、彩色图像的直方图均衡化:挑战与解决方案 三、进阶技巧与注意事项 四、应用场景与典型案 一、直方图均衡化基础:原理与核心思想 1. 直方图的本质与作用 直方图是图像像素强度分布的统计图表…...
【Pandas】pandas DataFrame keys
Pandas2.2 DataFrame Indexing, iteration 方法描述DataFrame.head([n])用于返回 DataFrame 的前几行DataFrame.at快速访问和修改 DataFrame 中单个值的方法DataFrame.iat快速访问和修改 DataFrame 中单个值的方法DataFrame.loc用于基于标签(行标签和列标签&#…...
Web前端之Vue+Element实现表格动态复杂的合并行功能、localeCompare、forEach、table、push、sort、Map
MENU 效果图公共数据数据未排序时(需要合并的行数据未处于相邻位置)固定合并行方法(写死)动态合并行,行数计算方法当太合并行,合并方法方法(函数)执行 效果图 公共数据 Html <e…...
【DDR 内存学习专栏 1.4 -- DDR 的 Bank Group】
文章目录 BankgroupBankgroup 与 Bank 的关系 DDR4 中的 BankgroupDDR4-3200 8Gb芯片为例组织结构访问场景 实际应用示例 Bankgroup Bankgroup是DDR4及后续标准(DDR5)中引入的一个更高层次的组织结构。它将多个Bank组合在一起形成一个Bankgroup,目的是为了进一步提…...
嵌入式进阶:如何选择合适的开发平台?
随着现代工业、物联网以及人工智能技术的迅速发展,嵌入式系统已经由简单的控制器向复杂的高性能系统迈进。从传统家电到智能机器人、从自动驾驶汽车到工业自动化,每一项应用都对嵌入式系统的响应速度、运行稳定性和能耗管理提出了更高要求。在这种背景下…...
【HTML】动态背景效果前端页面
下面是一个带有多种动态背景效果的现代化前端页面,包含粒子效果、渐变波浪和星空背景三种可选动态背景。直接上代码!! <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name&quo…...
酶动力学参数预测,瓶颈识别……中科院深圳先进技术研究院罗小舟分享AI在酶领域的创新应用
蛋白质,作为生命的基石,在生命活动中发挥着关键作用,其结构和功能的研究,对创新药物研发、合成生物学、酶制剂生产等领域,有着极其重要的意义。但传统蛋白质设计面临诸多难题,蛋白质结构复杂,序…...
【Redis】布隆过滤器应对缓存穿透的go调用实现
布隆过滤器 https://pkg.go.dev/github.com/bits-and-blooms/bloom/v3 作用: 判断一个元素是不是在集合中 工作原理: 一个位数组(bit array),初始全为0。多个哈希函数,运算输入,从而映射到位数…...
【LLM】解锁Agent协作:深入了解谷歌 A2A 协议与 Python 实现
人工智能(AI)智能体正迅速成为企业提高生产力、自动化工作流程和增强运营能力的关键工具。从处理日常重复性任务到协助复杂的决策,智能体的潜力巨大。然而,当这些智能体来自不同的供应商、使用不同的框架或被限制在孤立的数据系统…...
kafka4.0浅尝辄止
最近工作中接触消息队列比较多,前几周又看到kafka4.0发布,故写一篇博客对消息队列做一个复盘。 目录 消息队列对比1. Apache Kafka 4.02. RabbitMQ3. RocketMQ4. ActiveMQ5. Apache Pulsar6. NSQ kafka4.0鲜明的新特性Java 版本要求升级API 更新与精简移…...
数据库原理及应用mysql版陈业斌实验三
🏝️专栏:Mysql_猫咪-9527的博客-CSDN博客 🌅主页:猫咪-9527-CSDN博客 “欲穷千里目,更上一层楼。会当凌绝顶,一览众山小。” 目录 实验三多表查询 1.实验数据如下 student 表(学生表&#…...
OpenHarmony - 小型系统内核(LiteOS-A)(二)
OpenHarmony - 小型系统内核(LiteOS-A)(二) 三、基础内核 3.1、中断及异常处理 基本概念 中断是指出现需要时,CPU暂停执行当前程序,转而执行新程序的过程。即在程序运行过程中,出现了一个必须…...
数字化引擎再升级:小匠物联十周年庆典与全链路创新实践
4月11日,浙江宁波的小匠物联十周年庆典拉开帷幕。本次活动以“拾阶而上,智创未来”为主题,从全员签到、心愿书写,到董事长致辞、切蛋糕及全体合影,每一个环节都精心设计,展现出企业在家用物联网领域的卓越技…...
机器学习核心知识:从基础概念到关键算法
摘要 本文深度剖析机器学习知识体系,从基本概念、学习方式,到分类算法、逻辑回归等关键内容均有涉及。详细阐述各知识点原理与应用场景,并对比多种算法的优劣。 关键词:机器学习;监督学习;分类算法&#x…...
开发工具-jetbrains使用技巧
更详细的可以看 狂神说Java】JavaWeb入门到实战 p6 idea中maven的操作 可以设置怎么调试 然后还可以wsl、远程方式等运行 maven 这里的相当于cmd的操作 命令行去执行这些东西...
HarmonyOS:页面滚动时标题悬浮、背景渐变
一、需求场景 进入到app首页或者分页列表首页时,随着页面滚动,分类tab要求固定悬浮在顶部。进入到app首页、者分页列表首页、商品详情页时,页面滚动时,顶部导航栏(菜单、标题)背景渐变。 二、相关技术知识点…...
Python——Matplotlib库的练习
1、 import matplotlib.pyplot as plt import numpy as npx np.linspace(0,2*np.pi,100) y1 np.sin(x) y2 np.cos(x)plt.plot(x,y1,"r--o",linewidth1.5,markersize6) plt.plot(x,y2,"g-s",linewidth2,markersize8)plt.show() 2、 import matplotlib…...
信息系统项目管理师-第十八章-项目绩效域
本文章记录学习过程中,重要的知识点,是否为重点的依据,来源于官方教材和历年考题,持续更新共勉 本文章记录学习过程中,重要的知识点,是否为重点的依据,来源于官方教材和历年考题,持续更新共勉 在整个生命周期过程中,项目管理者需要始终坚持项目管理原则,通过涵盖 10 …...
Windows 操作系统 - Windows 10 磁盘管理无法为 C 盘选择扩展卷
Windows 10 磁盘管理无法为 C 盘选择扩展卷 在 Windows 10 的磁盘管理中,无法为 C 盘选择扩展卷(选项灰色不可用),主要原因是未分配空间没有紧邻 C 盘的右侧 补充:Windows 10 磁盘管理打开方式 1. 按下快捷键【Win …...
[NOIP 2003 普及组] 栈 Java
import java.io.*;public class Main {public static void main(String[] args) throws IOException {BufferedReader br new BufferedReader(new InputStreamReader(System.in));int n Integer.parseInt(br.readLine());int[] dp new int[n 1];dp[0] 1; // 空序列只有一种…...
C++ (类的设计,对象的创建,this指针,构造函数)
类的设计 C对结构体是有增强的 可以包含函数作为结构体成员 可以直接定义变量 在结构体成员函数里面可以直接访问结构体成员变量 struct student{string name;int age;float score;void play_game(const string &name);}void student::play_game(const string game){}…...
笔记:代码随想录算法训练营day67:Floyd 算法精讲、A * 算法精讲 (A star算法) 严重超时完结,不过,撒花
学习资料:代码随想录 Floyd 算法精讲 卡码网:97. 小明逛公园 首先明确floyd算法解决的是多源最短路径问题,对边的权的正负值没有要求,而且是动态规划的思想 五部曲: 定义:grid[i][j][k]表示从i出发到j…...
面试篇 - Transformer模型中的位置编码
1. 位置编码的引入 背景:Transformer模型通过自注意力机制(Self-Attention)处理序列数据,但自注意力机制本身并不包含序列中元素的位置信息。因此,需要一种方法来为模型提供位置信息。 解决方案:位置编码&…...
蓝桥杯篇---客观题
文章目录 前言 前言 本文简单介绍了蓝桥杯中客观题各个部分的知识点。 一、单片机相关 IAP15F2K61S2单片机的定时器0具有4种工作模式,当采用外部12MHz晶振时,定时器最大定时长度65535us。8051单片机的P0口,当使用外部存储器时它是一个传输低…...


