[RK3566-Android11] 使用SPI方式点LED灯带-JE2815/WS2812,实现呼吸/渐变/随音量变化等效果

问题描述

之前写了一篇使用GPIO方式点亮LED灯带的文章
https://blog.csdn.net/jay547063443/article/details/134688745?fromshare=blogdetail&sharetype=blogdetail&sharerId=134688745&sharerefer=PC&sharesource=jay547063443&sharefrom=from_link
使用GPIO有一个问题是,在系统开机或者内存占用过大时,在做呼吸灯这种颜色变化较快的效果时,会出现显示乱颜色,或者显示的颜色不准确的问题。这还是由于内存占用高时操作GPIO控制纳秒级的高低电平宽度时会导致延时高,导致乱色。RK在RK3308_Linux_PartyBox_SDK音频方案点亮灯带使用的是SPI的方式。但这部分代码未开放,只知道原理是使用spi data模拟输出,调整好clk频率,用0和1去控制输出高低电平的宽度。使用SPI的好处是,类似PWM可以发送持续稳定的波形,可以更精确的控制纳秒级的高低电平。
下图为成品图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在附上一个呼吸灯效果视频:

led


解决方案:

1…config打开SPI配置:

# CONFIG_SPI_PXA2XX is not set
+CONFIG_SPI_ROCKCHIP=y
+CONFIG_SPI_ROCKCHIP_TEST=y
# CONFIG_SPI_SC18IS602 is not set

2.dts配置spi具体使用哪个可以根据自己的原理图配置

&spi0 {
	status = "okay";
	// max-freq = <48000000>; 
	// 默认不用配置,SPI 设备工作时钟值,io 时钟由工作时钟分频获取
	// assigned-clock-rates = <200000000>; 
	// 使能DMA模式,通讯长度少于32字节不建议用,置空赋值去掉使能,如 "dma-names;";
	// dma-names = "tx","rx";
	// 默认不用配置,读采样延时,详细参考 “常见问题”“延时采样时钟配置方案” 章节
	// rx-sample-delay-ns = <10>; 
	spi_test@00 {
		compatible ="rockchip,spi_test_bus1_cs0";
		// 片选0或者1
		reg = <0>;
		id = <0>;
		// 不配置则为 0,配置为1
		// spi-cpol;
		// 不配置则为 0,配置为1
		// spi-cpha;
		// IO 先传输 lsb
		// spi-lsb-first;
		// spi clk输出的时钟频率,不超过50M
		spi-max-frequency = <1000000>;
	};
};

3.配置spi-gpio口,以spi0为例子:
这些都是RK默认的配置

	spi0: spi@fe610000 {
		compatible = "rockchip,rk3066-spi";
		reg = <0x0 0xfe610000 0x0 0x1000>;
		interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
		#address-cells = <1>;
		#size-cells = <0>;
		clocks = <&cru CLK_SPI0>, <&cru PCLK_SPI0>;
		clock-names = "spiclk", "apb_pclk";
		dmas = <&dmac0 20>, <&dmac0 21>;
		dma-names = "tx", "rx";
		pinctrl-names = "default", "high_speed";
		pinctrl-0 = <&spi0m0_cs0 &spi0m0_cs1 &spi0m0_pins>;
		pinctrl-1 = <&spi0m0_cs0 &spi0m0_cs1 &spi0m0_pins_hs>;
		status = "disabled";
	};

4.修改rkxxxx-pinctrl.dtsi里的spi0m0_pins和spi0m0_pins_hs改为我们需要控制的口

		/omit-if-no-ref/
		spi0m0_pins: spi0m0-pins {
			rockchip,pins =
				/* spi0_clkm0 */
				<0 RK_PB5 2 &pcfg_pull_none>,
				/* spi0_misom0 */
				<0 RK_PC5 2 &pcfg_pull_none>,
				/* spi0_mosim0 */
				<0 RK_PB6 2 &pcfg_pull_none>;
		};
		
		spi0m0_pins_hs: spi0m0-pins {
			rockchip,pins =
				/* spi0_clkm0 */
				<0 RK_PB5 2 &pcfg_pull_up_drv_level_1>,
				/* spi0_misom0 */
				<0 RK_PC5 2 &pcfg_pull_up_drv_level_1>,
				/* spi0_mosim0 */
				<0 RK_PB6 2 &pcfg_pull_up_drv_level_1>;
		};

将spi0_mosim0 改为自己需要控制的GPIO口,可以配置上spi0_clkm0便于使用逻辑分析仪抓取发送的数据结果,spi0_misom0/cs0/csi都不用管他。检查GPIO不要出现复用的情况。

5.spi-test驱动kernel\drivers\spi\spi-rockchip-test.c
查看spi_test_write函数,可以看到RK开放了操作节点便于操作

		printk("echo id number size > /dev/spi_misc_test\n");
		printk("echo write 0 10 255 > /dev/spi_misc_test\n");
		printk("echo write 0 10 255 init.rc > /dev/spi_misc_test\n");
		printk("echo read 0 10 255 > /dev/spi_misc_test\n");
		printk("echo loop 0 10 255 > /dev/spi_misc_test\n");
		printk("echo setspeed 0 1000000 > /dev/spi_misc_test\n");
		printk("echo config 8 > /dev/spi_misc_test\n");

这里我们主要看write部分的逻辑:

	} else if (!strcmp(cmd, "write")) {
		char name[64];
		int fd;
    	mm_segment_t old_fs = get_fs();

		sscanf(argv[0], "%d", &id);
		sscanf(argv[1], "%d", &times);
		sscanf(argv[2], "%d", &size);
		if (argc > 3) {
			sscanf(argv[3], "%s", name);
			set_fs(KERNEL_DS);
		}

		txbuf = kzalloc(size, GFP_KERNEL);
		if (!txbuf) {
			printk("spi write alloc buf size %d fail\n", size);
			return n;
		}

		if (argc > 3) {
			fd = ksys_open(name, O_RDONLY, 0);
			if (fd < 0) {
				printk("open %s fail\n", name);
			} else {
				ksys_read(fd, (char __user *)txbuf, size);
				ksys_close(fd);
			}
			set_fs(old_fs);
		} else {
			for (i = 0; i < size; i++){
				txbuf[i] = i % 256;
			}
			printk(" txbuf %d \n", i);
		}

		start_time = ktime_get();
		for (i = 0; i < times; i++)
			spi_write_slt(id, txbuf, size);
		end_time = ktime_get();
		cost_time = ktime_sub(end_time, start_time);
		us = ktime_to_us(cost_time);

		bytes = size * times * 1;
		bytes = bytes * 1000 / us;
		printk("spi write %d*%d cost %ldus speed:%ldKB/S\n", size, times, us, bytes);

		kfree(txbuf);

按照文档说明 echo 类型 id 循环次数 传输长度>/dev/spi_misc_test
从函数逻辑可以看到,经由echo write 0 10 255 > /dev/spi_misc_test的第三个参数传输长度,在不带文件名的情况时,走

			for (i = 0; i < size; i++){
				txbuf[i] = i % 256;
			}

根据输入的第三个参数传输长度,会取低八位连续发送。一个字节是8位。

5.我们来看一下RGB色的转换逻辑,以255,0,0为例子如下图:
在这里插入图片描述
原理类似于我们拼接波形,255 0 0转化为二进制为
1111 1111 0000 0000 0000 0000,也就是
cc cc cc cc 88 88 88 88 88 88 88 88。
204 204 204 204 136 136 136 136 136 136 136 136
也就是说用

			for (i = 0; i < size; i++){
				txbuf[i] = i % 256;
			}

取低八位的逻辑需要发出。
205 205 205 205 137 137 137 137 137 137 137 137。

6.由于不可知原因,附上简单版关键代码逻辑:

	} else if (!strcmp(cmd, "rgb")) {
	...
	...
		offset = 0;
		for (repeat = 0; repeat < 15; repeat++) { // 外层循环,重复15次
			for (i = 0; i < 12; i++) {
				txbuf[i] = kzalloc(size[i], GFP_KERNEL);
				if (!txbuf[i]) {
					printk("Wi:spi write alloc buf size %d fail\n", size[i]);
					kfree(full_txbuf);
					return -1;
				}

				// 填充当前缓冲区
				for (j = 0; j < size[i]; j++) {
					txbuf[i][j] = j % 256; // 根据需求填充
				}
				printk("Wi:txbuf[%d] filled, size %d\n", i, size[i]);

				// 获取当前缓冲区的最后一个字节,并填充到合并缓冲区
				full_txbuf[offset] = txbuf[i][size[i] - 1];
				offset++;
			}
		}

		// 记录开始时间
		start_time = ktime_get();

		// 发送合并后的缓冲区
		for (j = 0; j < times; j++) {
			spi_write_slt(id, full_txbuf, total_size); // 发送所有数据
		}
	...
	...

额外填入12组参数,取低八位的最后一个,循环15次填入buf。最后由spi_write_slt发出。
这里的repeat < 15,就是指灯珠数,一条灯带是15颗灯。
spi-max-frequency = <3300000>;
如下命令:
echo rgb 0 1 205 205 205 205 137 137 137 137 137 137 137 137 > /dev/spi_misc_test关闭灯光
echo rgb 0 1 137 205 137 205 137 205 137 205 137 205 137 205 > /dev/spi_misc_test调整红光亮度
echo rgb 0 1 205 205 205 205 137 137 137 137 137 137 137 137 > /dev/spi_misc_test为红光
echo rgb 0 1 137 137 137 137 205 205 205 205 137 137 137 137 > /dev/spi_misc_test为绿光
echo rgb 0 1 137 137 137 137 137 137 137 137 205 205 205 205 > /dev/spi_misc_test为蓝光

7.知道原理其实代码很好写,比如取低八位可以改成取低4位。又或者直接写入
echo rgb 0 1 15 255 0 0 > /dev/spi_misc_test。RGB色,将255 0 0自己在代码里面做逻辑转换。
可玩性很高,你可以控制某一颗灯的颜色,又或者做呼吸灯效果/渐变效果/网上的随音量大小或者音乐律动变化的效果等等。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++20中头文件ranges的使用

<ranges>是C20中新增加的头文件&#xff0c;提供了一组与范围(ranges)相关的功能&#xff0c;此头文件是ranges库的一部分。包括&#xff1a; 1.concepts: (1).std::ranges::range:指定类型为range&#xff0c;即它提供开始迭代器和结束标记(it provides a begin iterato…

博弈论 C++

前置知识 若一个游戏满足&#xff1a; 由两名玩家交替行动在游戏进行的任意时刻&#xff0c;可以执行的合法行动与轮到哪位玩家无关不能行动的玩家判负 则称该游戏为一个公平组合游戏。 尼姆游戏&#xff08;NIM&#xff09;属于公平组合游戏&#xff0c;但常见的棋类游戏&…

idea(2017版)创建项目的搭建方式

目录 一、普通Java项目 二、普通JavaWeb项目 三、maven的Java项目 四、maven的JavaWeb项目 一、普通Java项目 1.创建新项目 2.因为是普通的java项目&#xff0c;所以先点最上面的Java&#xff0c;然后确定jdk&#xff0c;然后next 3.这里直接点next 4.写好项目名称和路径…

互联网系统的微观与宏观架构

互联网系统的架构设计&#xff0c;通常会根据项目的体量、业务场景以及技术需求被划分为微观架构&#xff08;Micro-Architecture&#xff09;和宏观架构&#xff08;Macro-Architecture&#xff09;。这两者的概念与职责既独立又相互关联。本文将通过一些系统案例&#xff0c;…

Vue3 学习笔记(五)Vue3 模板语法详解

在 Vue3 的世界里&#xff0c;模板语法是我们构建用户界面的基石。今天&#xff0c;让我们一起深入了解 Vue3 的模板语法&#xff0c;我将用通俗易懂的语言和实用的例子&#xff0c;带你掌握这项必备技能。 1、文本插值&#xff1a;最基础的开始 想在页面上显示数据&#xff1f…

《探索 HarmonyOS NEXT(5.0):开启构建模块化项目架构奇幻之旅 —— 模块化基础篇》

从无到有&#xff0c;打造模块化项目。构建一个开箱即用的项目&#xff0c;从 Git 上拉取下来即可直接进行开发&#xff0c;其中涵盖路由通信、上下拉刷新、网络请求、事件通知、顶部tab封装等功能&#xff0c;项目里调用API为鸿洋大佬的wanAndroidAPI。后期将持续完善&#xf…

【C】数组(array)

数组(array) 数组的概念 数组是一组相同类型元素的集合 数组中存放的是1个或者多个数据&#xff0c;但是数组元素个数不能为0数组中存放的多个数据&#xff0c;类型是相同的 数组分为一维数组和多维数组&#xff0c;多维数组一般比较多见的是二维数组 一维数组的创建和初始…

JAVA面试八股文(五)

#1024程序员节&#xff5c;征文# 在1024程序员节这个特别的日子里&#xff0c;首先&#xff0c;我想对每一位程序员表示最诚挚的祝贺&#xff01;祝愿大家在未来的日子里&#xff0c;能够继续热爱编程、追求卓越&#xff0c;携手共创更美好的科技未来&#xff01;让我们共同庆祝…

Redis Search系列 - 第六讲 基准测试 - Redis Search VS. MongoDB VS. ElasticSearch

目录 一、引言二、Redis Search 2.x版本的性能提升三、Redis Search VS. MongoDB VS. ElasticSearch3.1 测试环境3.2 100%写 - 基准测试3.3 100%读 - 基准测试3.4 混合读/写/搜索 - 基准测试2.5 搜索延迟分析3.6 读延迟分析3.7 写延迟分析3.8 Redis Search VS. ElasticSearch3.…

混个1024勋章

一眨眼毕业工作已经一年了&#xff0c;偶然进了游戏公司成了一名初级游戏服务器开发。前两天总结的时候&#xff0c;本来以为自己这一年没学到多少东西&#xff0c;但是看看自己的博客其实也有在进步&#xff0c;虽然比不上博客里的众多大佬&#xff0c;但是回头看也算是自己的…

micro-app【微前端实战】主应用 vue3 + vite 子应用 vue3+vite

micro-app 官方文档为 https://micro-zoe.github.io/micro-app/docs.html#/zh-cn/framework/vite 子应用 无需任何修改&#xff0c;直接启动子应用即可。 主应用 1. 安装微前端框架 microApp npm i micro-zoe/micro-app --save2. 导入并启用微前端框架 microApp src/main.ts …

手机摄影入门

感觉会摄影的人是能够从生活中发现美的人。 我不太会拍照&#xff0c;觉得拍好的照片比较浪费时间&#xff0c;而且缺乏审美也缺乏技巧&#xff0c;所以拍照的时候总是拍不好。但有时候还是需要拍一些好看的照片的。 心态和审美可能需要比较长时间提升&#xff0c;但一些基础…

Apple Vision Pro市场表现分析:IDC最新数据揭示的真相

随着AR/VR技术逐渐成熟并被更多消费者接受,2024年第二季度(Q2)成为这一领域的一个重要转折点。根据国际数据公司(IDC)发布的最新报告,整个AR/VR市场在本季度经历了显著的增长。接下来,我们将深入探讨Apple Vision Pro在这股增长浪潮中的具体表现。 市场背景 2024年Q2,…

中航资本:股票支撑位和压力位什么意思?股票如何找支撑与压力?

股票支撑位和压力位什么意思&#xff1f; 支撑位是指股票价格在下跌过程中遇到的一个或多个价格方位&#xff0c;这些价位上存在着较强的买盘力气&#xff0c;可以提供满足的支撑&#xff0c;阻止股价继续下跌。 而股票压力位是指股票价格在上涨过程中遇到的一个或多个价格方…

docker部署rustdesk

文章目录 一.ubuntu修改ssh端口二.开放端口三.安装rustDesk四.连接验证 一.ubuntu修改ssh端口 借鉴乌班图Ubuntu 24.04 SSH Server 修改默认端口重启无效 https://bugs.launchpad.net/ubuntu/source/openssh/bug/2069041 sudo vim /etc/ssh/sshd_config sudo systemctl daem…

在windows下利用安装docker加vscode调试OceanBase,

文章目录 一、安装WSL二、安装docker三、 OceanBase安装 -- 运行镜像&#xff0c;配置VScode四、 OceanBase安装 -- 将获取到的文件与docker容器 映射连接 – 参考官方文档 docker安装 在windows上通过docker配置环境并利用vscode调试代码 一、安装WSL 1.可以在任务管理器中&…

⌈ 传知代码 ⌋ 农作物病害分类(Web端实现)

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

我谈椒盐噪声的统计模型

在成像系统发展长河的早期&#xff0c;椒盐噪声曾经不可避免&#xff0c;但是如今&#xff0c;即使在专用成像设备中&#xff08;如遥感、医学&#xff09;&#xff0c;椒盐噪声也属罕见了。所以&#xff0c;现在在图像处理领域&#xff0c;研究椒盐噪声的去除没有多少实际意义…

kafka 如何减少数据丢失?

大家好&#xff0c;我是锋哥。今天分享关于【kafka 如何减少数据丢失?】面试题&#xff1f;希望对大家有帮助&#xff1b; kafka 如何减少数据丢失? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Apache Kafka 是一个高吞吐量的分布式消息队列&#xff0c;广泛用…

R实验——logistic回归、LDA、QDAKNN

数据集介绍&#xff1a; mpg&#xff0c;miles per gallon即油耗&#xff0c;这个数据集来自卡内基梅隆大学维护的StatLib库。1983年美国统计协会博览会使用了该数据集。这个数据集是对StatLib库中提供的数据集稍加修改的版本。根据Ross Quinlan(1993)在预测属性“mpg”中的使…