SPI接口DAC设备驱动与应用程序开发

news2025/4/16 17:51:53

本文章相关专栏往期内容,SPI子系统专栏:

  1. SPI通信协议与Linux设备驱动框架解析
  2. SPI传输与驱动框架的实现
  3. spidev.c:SPI设备驱动的核心实现逻辑

PCI/PCIe子系统专栏:

  1. 专栏地址:PCI/PCIe子系统
  2. PCIe设备MSI/MSI-X中断源码分析与驱动编写
    – 末片,有专栏内容观看顺序

Uart子系统专栏:

  1. 专栏地址:Uart子系统
  2. Linux内核早期打印机制与RS485通信技术
    – 末片,有专栏内容观看顺序

interrupt子系统专栏:

  1. 专栏地址:interrupt子系统
  2. Linux 链式与层级中断控制器讲解:原理与驱动开发
    – 末片,有专栏内容观看顺序

pinctrl和gpio子系统专栏:

  1. 专栏地址:pinctrl和gpio子系统

  2. 编写虚拟的GPIO控制器的驱动程序:和pinctrl的交互使用

    – 末片,有专栏内容观看顺序

input子系统专栏:

  1. 专栏地址:input子系统
  2. input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序
    – 末片,有专栏内容观看顺序

I2C子系统专栏:

  1. 专栏地址:IIC子系统
  2. 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
    – 末篇,有专栏内容观看顺序

总线和设备树专栏:

  1. 专栏地址:总线和设备树
  2. 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
    – 末篇,有专栏内容观看顺序

img

目录

  • 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 设备交互。

  1. 查看设备状态
./spidev_test /dev/spidev0.0
  1. 发送 SPI 消息
./spidev_test -m 16 /dev/spidev0.0
  1. 读取数据
./spidev_test -r 8 /dev/spidev0.0
  1. 启用详细模式
./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模块常用于音频输出、信号处理、自动控制系统等场合。

img

img

一个典型的 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 通常用于模拟部分电路,以减少数字噪声的干扰。

需要特别注意的是OUTDOUT,因为这两个的差异,输入的16bit数据其高4位和低2位得都为0才能保证传输正确的数据。这个在时序图讲解的时候再提。

1.3.2 时序图

img

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

img

记住SPI其实就是彼此数据的置换:

  • img
  • img

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公式

数字信号怎么转换为模拟信号

img

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>;
    };
};

img

3.3.5 上机

编译应用程序:

arm-buildroot-linux-gnueabihf-gcc  -o  dac_test  dac_test.c

编译驱动:

img

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>;
    };
};

img

img

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

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

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

相关文章

第十六届蓝桥杯大赛软件赛省赛 Python 大学 B 组 满分题解

题面链接Htlang/2025lqb_python_b 个人觉得今年这套题整体比往年要简单许多&#xff0c;但是G题想简单了出大问题&#xff0c;预估50101015120860&#xff0c;道阻且长&#xff0c;再接再厉 代码仅供学习参考&#xff0c;满分为赛后洛谷中的测评&#xff0c;蓝桥杯官方测评待…

旧版 VMware 虚拟机迁移至 KVM 平台-案例2

项目背景 需将一台旧版 VMware 虚拟机&#xff08;VMDK 格式&#xff09;迁移至 KVM 虚拟化平台&#xff0c;具体要求如下&#xff1a; 格式转换&#xff1a;将 VMDK 转换为 QCOW2 格式。磁盘扩容&#xff1a;将原 40GB 磁盘扩展至 60GB。密码重置&#xff1a;修改 aiden 用户…

若依代码生成器原理velocity模板引擎(自用)

1.源码分析 代码生成器:导入表结构(预览、编辑、删除、同步)、生成前后端代码 代码生成器表结构说明&#xff1a; 若依提供了两张核心表来存储导入的业务表信息&#xff1a; gen_table&#xff1a;存储业务表的基本信息 &#xff0c;它对应于配置代码基本信息和生成信息的页…

OpenCV直方图均衡化全面解析:从灰度到彩色图像的增强技术

目录 一、直方图均衡化基础&#xff1a;原理与核心思想 二、彩色图像的直方图均衡化&#xff1a;挑战与解决方案 三、进阶技巧与注意事项 四、应用场景与典型案 一、直方图均衡化基础&#xff1a;原理与核心思想 1. 直方图的本质与作用 直方图是图像像素强度分布的统计图表…

Web前端之Vue+Element实现表格动态复杂的合并行功能、localeCompare、forEach、table、push、sort、Map

MENU 效果图公共数据数据未排序时&#xff08;需要合并的行数据未处于相邻位置&#xff09;固定合并行方法&#xff08;写死&#xff09;动态合并行&#xff0c;行数计算方法当太合并行&#xff0c;合并方法方法&#xff08;函数&#xff09;执行 效果图 公共数据 Html <e…

【DDR 内存学习专栏 1.4 -- DDR 的 Bank Group】

文章目录 BankgroupBankgroup 与 Bank 的关系 DDR4 中的 BankgroupDDR4-3200 8Gb芯片为例组织结构访问场景 实际应用示例 Bankgroup Bankgroup是DDR4及后续标准(DDR5)中引入的一个更高层次的组织结构。它将多个Bank组合在一起形成一个Bankgroup&#xff0c;目的是为了进一步提…

嵌入式进阶:如何选择合适的开发平台?

随着现代工业、物联网以及人工智能技术的迅速发展&#xff0c;嵌入式系统已经由简单的控制器向复杂的高性能系统迈进。从传统家电到智能机器人、从自动驾驶汽车到工业自动化&#xff0c;每一项应用都对嵌入式系统的响应速度、运行稳定性和能耗管理提出了更高要求。在这种背景下…

酶动力学参数预测,瓶颈识别……中科院深圳先进技术研究院罗小舟分享AI在酶领域的创新应用

蛋白质&#xff0c;作为生命的基石&#xff0c;在生命活动中发挥着关键作用&#xff0c;其结构和功能的研究&#xff0c;对创新药物研发、合成生物学、酶制剂生产等领域&#xff0c;有着极其重要的意义。但传统蛋白质设计面临诸多难题&#xff0c;蛋白质结构复杂&#xff0c;序…

kafka4.0浅尝辄止

最近工作中接触消息队列比较多&#xff0c;前几周又看到kafka4.0发布&#xff0c;故写一篇博客对消息队列做一个复盘。 目录 消息队列对比1. Apache Kafka 4.02. RabbitMQ3. RocketMQ4. ActiveMQ5. Apache Pulsar6. NSQ kafka4.0鲜明的新特性Java 版本要求升级API 更新与精简移…

数据库原理及应用mysql版陈业斌实验三

&#x1f3dd;️专栏&#xff1a;Mysql_猫咪-9527的博客-CSDN博客 &#x1f305;主页&#xff1a;猫咪-9527-CSDN博客 “欲穷千里目&#xff0c;更上一层楼。会当凌绝顶&#xff0c;一览众山小。” 目录 实验三多表查询 1.实验数据如下 student 表&#xff08;学生表&#…

OpenHarmony - 小型系统内核(LiteOS-A)(二)

OpenHarmony - 小型系统内核&#xff08;LiteOS-A&#xff09;&#xff08;二&#xff09; 三、基础内核 3.1、中断及异常处理 基本概念 中断是指出现需要时&#xff0c;CPU暂停执行当前程序&#xff0c;转而执行新程序的过程。即在程序运行过程中&#xff0c;出现了一个必须…

数字化引擎再升级:小匠物联十周年庆典与全链路创新实践

4月11日&#xff0c;浙江宁波的小匠物联十周年庆典拉开帷幕。本次活动以“拾阶而上&#xff0c;智创未来”为主题&#xff0c;从全员签到、心愿书写&#xff0c;到董事长致辞、切蛋糕及全体合影&#xff0c;每一个环节都精心设计&#xff0c;展现出企业在家用物联网领域的卓越技…

开发工具-jetbrains使用技巧

更详细的可以看 狂神说Java】JavaWeb入门到实战 p6 idea中maven的操作 可以设置怎么调试 然后还可以wsl、远程方式等运行 maven 这里的相当于cmd的操作 命令行去执行这些东西

HarmonyOS:页面滚动时标题悬浮、背景渐变

一、需求场景 进入到app首页或者分页列表首页时&#xff0c;随着页面滚动&#xff0c;分类tab要求固定悬浮在顶部。进入到app首页、者分页列表首页、商品详情页时&#xff0c;页面滚动时&#xff0c;顶部导航栏&#xff08;菜单、标题&#xff09;背景渐变。 二、相关技术知识点…

信息系统项目管理师-第十八章-项目绩效域

本文章记录学习过程中,重要的知识点,是否为重点的依据,来源于官方教材和历年考题,持续更新共勉 本文章记录学习过程中,重要的知识点,是否为重点的依据,来源于官方教材和历年考题,持续更新共勉 在整个生命周期过程中,项目管理者需要始终坚持项目管理原则,通过涵盖 10 …

[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; // 空序列只有一种…

面试篇 - Transformer模型中的位置编码

1. 位置编码的引入 背景&#xff1a;Transformer模型通过自注意力机制&#xff08;Self-Attention&#xff09;处理序列数据&#xff0c;但自注意力机制本身并不包含序列中元素的位置信息。因此&#xff0c;需要一种方法来为模型提供位置信息。 解决方案&#xff1a;位置编码&…

vue+flask图书知识图谱推荐系统

文章结尾部分有CSDN官方提供的学长 联系方式名片 文章结尾部分有CSDN官方提供的学长 联系方式名片 关注B站&#xff0c;有好处&#xff01; 编号: F025 架构: vueflaskneo4jmysql 亮点&#xff1a;协同过滤推荐算法知识图谱可视化 支持爬取图书数据&#xff0c;数据超过万条&am…

vue2 走马灯 展示多个

使用 npm install “swiper”: “^11.2.4”, 在这里插入代码片 <template><section class"swiper pc-banner"><div class"swiper-container"><div class"swiper-wrapper"><div v-for"(item, index) in swiperD…