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

[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或者1reg = <0>;id = <0>;// 不配置则为 0,配置为1// spi-cpol;// 不配置则为 0,配置为1// spi-cpha;// IO 先传输 lsb// spi-lsb-first;// spi clk输出的时钟频率,不超过50Mspi-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自己在代码里面做逻辑转换。
可玩性很高,你可以控制某一颗灯的颜色,又或者做呼吸灯效果/渐变效果/网上的随音量大小或者音乐律动变化的效果等等。

相关文章:

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

问题描述 之前写了一篇使用GPIO方式点亮LED灯带的文章 https://blog.csdn.net/jay547063443/article/details/134688745?fromshareblogdetail&sharetypeblogdetail&sharerId134688745&sharereferPC&sharesourcejay547063443&sharefromfrom_link 使用GPIO…...

PostgreSQL用load语句加载插件

文章目录 1. LOAD 语法2. 用途3. 示例4. 注意事项5. 其他相关命令6. 总结 在 PostgreSQL 中&#xff0c;LOAD 主要用于加载共享库&#xff0c;通常用于扩展功能或性能优化。以下是一些有关 LOAD 语句和 PostgreSQL 中的加载操作的关键信息&#xff1a; 1. LOAD 语法 LOAD 语句…...

一文了解:增强图像搜索之图像嵌入

图像嵌入在现代计算机视觉领域扮演着明星角色&#xff0c;它使得计算机能够像人类一样识别出各种各样的图像。由于计算机只能处理数字信息&#xff0c;我们需要将图像转换成数字向量&#xff0c;并存储在向量数据库中&#xff0c;这样就能迅速地检索到它们。 谈到嵌入技术&…...

yolov9目标检测/分割预测报错AttributeError: ‘list‘ object has no attribute ‘device‘常见汇总

这篇文章主要是对yolov9目标检测和目标分割预测测试时的报错&#xff0c;进行解决方案。 在说明解决方案前&#xff0c;严重投诉、吐槽一些博主发的一些文章&#xff0c;压根没用的解决方法&#xff0c;也不知道他们从哪里抄的&#xff0c;误人子弟、浪费时间。 我在解决前&…...

格姗知识圈博客网站开源了!

格姗知识圈博客 一个基于 Spring Boot、Spring Security、Vue3、Element Plus 的前后端分离的博客网站&#xff01;本项目基本上是小格子一个人开发&#xff0c;由于工作和个人能力原因&#xff0c;部分技术都是边学习边开发&#xff0c;特别是前端&#xff08;工作中是后端开…...

【C++】深入理解C++中的类型推导:从auto到decltype的应用与实践

C11引入了类型推导特性&#xff0c;旨在简化代码并提升开发效率。类型推导使开发者无需显式指定变量的类型&#xff0c;从而让代码更具可读性和灵活性。本文深入探讨了C11引入的auto、decltype和decltype(auto)等关键特性&#xff0c;通过分析其背后的设计理念、实际应用场景&a…...

使用Prometheus对微服务性能自定义指标监控

背景 随着云计算和容器化技术的不断发展&#xff0c;微服务架构逐渐成为现代软件开发的主流趋势。微服务架构将大型应用程序拆分成多个小型、独立的服务&#xff0c;每个服务都可以独立开发、部署和扩展。这种架构模式提高了系统的可伸缩性、灵活性和可靠性&#xff0c;但同时…...

深入解析 Lombok 的实现原理:以 @Builder 为例的实战演示(三)

文章目录 Lombok 的实现原理概述以 Builder 为例&#xff1a;解析 Lombok 如何生成 Builder 模式示例代码&#xff1a;没有 Lombok 的 Builder 模式使用 Lombok 的 Builder 简化代码 Lombok 如何实现 Builder&#xff1a;源码解析案例演示&#xff1a;自定义构造逻辑Lombok 的代…...

SEO基础:什么是SERP?【百度SEO专家】

SEO基础&#xff1a;什么是SERP&#xff1f; 大家好&#xff0c;我是林汉文&#xff08;百度SEO专家&#xff09;&#xff0c;在进行SEO&#xff08;搜索引擎优化&#xff09;时&#xff0c;理解SERP是一个非常重要的基础概念。那么&#xff0c;究竟什么是SERP呢&#xff1f;本…...

HTML5教程(一)- 网页与开发工具

1. 什么是网页 网页 基于浏览器阅读的应用程序&#xff0c;是数据&#xff08;文本、图像、视频、声音、链接等&#xff09;展示的载体常见的是以 .html 或 .htm 结尾的文件 网站 使用 HTML 等制作的用于展示特定内容相关的网页集合。 2. 网页的组成 浏览器 代替用户向服务…...

Java进阶篇设计模式之二 ----- 工厂模式

前言 在上一篇中我们学习了单例模式&#xff0c;介绍了单例模式创建的几种方法以及最优的方法。本篇则介绍设计模式中的工厂模式&#xff0c;主要分为简单工厂模式、工厂方法和抽象工厂模式。 简单工厂模式 简单工厂模式是属于创建型模式&#xff0c;又叫做静态工厂方法模式。…...

考研篇——数据结构王道3.2.2_队列的顺序实现

目录 1.实现方式说明2.代码实现2.12.1.1 代码12.1.2 代码22.1.3 代码3 2.22.2.1 代码42.2.5 代码52.2.6 代码6 总结 1.实现方式说明 多在选择题中考察 队尾指针&#xff08;rear&#xff09;有两种指向方式&#xff1a; 队尾指针指向队尾元素的位置&#xff0c;队尾指针指向…...

从零开始理解 Trie 树:高效字符串存储与查找的利器【自动补全、拼写检查】

题目分析 这道题让我们实现一个 Trie 类&#xff08;也称为前缀树&#xff09;&#xff0c;以便高效地插入和查询字符串。前缀树是一种特殊的树形数据结构&#xff0c;适用于快速存储和检索字符串数据集中的键&#xff0c;比如实现 自动补全 和 拼写检查。 题目要求 Trie 类…...

关于sse、websocket与流式渲染

一、SSE是什么&#xff1f; 网络中的 SSE (Server-Sent Events) 是一种服务器向浏览器单向推送数据的机制&#xff0c;常用于需要实时更新的数据传输&#xff0c;如新闻推送、股票行情、聊天应用等。 SSE 的特点&#xff1a; 单向通信&#xff1a;服务器向客户端推送数据&…...

Python 语法与数据类型详解

Python 语法与数据类型详解 Python 以其简洁易读的语法和丰富多样的数据类型在编程领域占据重要地位。深入理解 Python 的语法和数据类型是掌握这门语言的关键。 一、Python 语法概述 &#xff08;一&#xff09;缩进规则 Python 独特的缩进规则是其语法的重要特征之一。与…...

LeetCode题练习与总结:扁平化嵌套列表迭代器--341

一、题目描述 给你一个嵌套的整数列表 nestedList 。每个元素要么是一个整数&#xff0c;要么是一个列表&#xff1b;该列表的元素也可能是整数或者是其他列表。请你实现一个迭代器将其扁平化&#xff0c;使之能够遍历这个列表中的所有整数。 实现扁平迭代器类 NestedIterato…...

51单片机快速入门之 AD(模数) DA(数模) 转换 2024/10/25

51单片机快速入门之 AD(模数) DA(数模) 转换 2024/10/25 声明:本文图片来源于网络 A模拟信号特点: 电压或者电流 缓慢上升 随着时间连续缓慢上升或下降 D数字信号特点:电压或者电流 保持一段时间的高/低电平 状态 / 突变 (高电压瞬间低电压) 数字电路中 通常将0-1v电压称…...

Typora 、 Minio and PicGo 图床搭建

流程介绍 本地安装Typora笔记工具拥有一台装有docker的服务器配置minio云图床管理控制页面下载PicGo上传工具服务器Docker环境搭建—Ubuntu系统 删除旧docker的所有依赖(非root用户) # 删除docker及安装时自动安装的所有包 sudo apt-get autoremove docker docker-ce docker…...

【计网】UDP Echo Server与Client实战:从零开始构建简单通信回显程序

目录 前言&#xff1a; 1.实现udpserver类 1.1.创建udp socket 套接字 --- 必须要做的 socket&#xff08;&#xff09;讲解 代码实现&#xff1a;​编辑 代码讲解&#xff1a; 1.2.填充sockaddr_in结构 代码实现&#xff1a; 代码解析&#xff1a; 1.3.bind sockfd和…...

微服务网关Zuul

一、Zuul简介 Zuul是Netflix开源的微服务网关&#xff0c;包含对请求的路由和过滤两个主要功能。 1&#xff09;路由功能&#xff1a;负责将外部请求转发到具体的微服务实例上&#xff0c;是实现外部访问统一入口的基础。 2&#xff09;过滤功能&#xff1a;负责对请求的过程…...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

Pydantic + Function Calling的结合

1、Pydantic Pydantic 是一个 Python 库&#xff0c;用于数据验证和设置管理&#xff0c;通过 Python 类型注解强制执行数据类型。它广泛用于 API 开发&#xff08;如 FastAPI&#xff09;、配置管理和数据解析&#xff0c;核心功能包括&#xff1a; 数据验证&#xff1a;通过…...

Java后端检查空条件查询

通过抛出运行异常&#xff1a;throw new RuntimeException("请输入查询条件&#xff01;");BranchWarehouseServiceImpl.java // 查询试剂交易&#xff08;入库/出库&#xff09;记录Overridepublic List<BranchWarehouseTransactions> queryForReagent(Branch…...

高分辨率图像合成归一化流扩展

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 1 摘要 我们提出了STARFlow&#xff0c;一种基于归一化流的可扩展生成模型&#xff0c;它在高分辨率图像合成方面取得了强大的性能。STARFlow的主要构建块是Transformer自回归流&#xff08;TARFlow&am…...

高效的后台管理系统——可进行二次开发

随着互联网技术的迅猛发展&#xff0c;企业的数字化管理变得愈加重要。后台管理系统作为数据存储与业务管理的核心&#xff0c;成为了现代企业不可或缺的一部分。今天我们要介绍的是一款名为 若依后台管理框架 的系统&#xff0c;它不仅支持跨平台应用&#xff0c;还能提供丰富…...