当前位置: 首页 > news >正文

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

本文章相关专栏往期内容,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

相关文章:

  • 面试求助:在性能测试中发现CPU占用过高应该如何进行分析?
  • 科普:如何通过ROC曲线,确定二分类的“理论阈值”
  • Golang|锁相关
  • Python——Matplotlib库的练习
  • HarmonyOS:使用Refresh组件实现页面下拉刷新上拉加载更多
  • 【蓝桥云课】男女搭配 python
  • c语言坦克对战(前言)
  • Rust编程学习(一): 变量与数据类型
  • 51单片机波特率与溢出率的关系
  • FinanceRAG获奖方案解读:ACM-ICAIF ’24的FinanceRAG挑战赛
  • Nacos深度剖析与实践应用 -1
  • 日志分析-mysql应急响应
  • 【网络原理】TCP/IP协议五层模型
  • 网络复习二(TCP【3】)
  • 从源码看无界 1.0.28:为何说它是 qiankun 的 “轻量化替代方案”(二)
  • Restful风格接口开发
  • 制造业项目管理如何做才能更高效?制造企业如何选择适配的数字化项目管理系统工具?
  • 【软件测试】bug 篇
  • 足迹在后 脚步向前
  • 过拟合、归一化、正则化、鞍点
  • 国台办:80年前台湾重归中国版图,80年后不可能让台湾分裂出去
  • 国家林业和草原局原党组成员、副局长李春良接受审查调查
  • 沈阳一超市疑借领养名义烹食流浪狗,当地市监局:已收到多起投诉
  • 广西壮族自治区党委政法委副书记李文博接受审查调查
  • 马上评丨摆摊要交芙蓉王?对吃拿卡要必须零容忍
  • 全球前瞻|特朗普访问中东三国,印巴军方将于12日再次对话