当前位置: 首页 > 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自己在代码里面做逻辑转换。
可玩性很高,你可以控制某一颗灯的颜色,又或者做呼吸灯效果/渐变效果/网上的随音量大小或者音乐律动变化的效果等等。

http://www.lryc.cn/news/469117.html

相关文章:

  • PostgreSQL用load语句加载插件
  • 一文了解:增强图像搜索之图像嵌入
  • yolov9目标检测/分割预测报错AttributeError: ‘list‘ object has no attribute ‘device‘常见汇总
  • 格姗知识圈博客网站开源了!
  • 【C++】深入理解C++中的类型推导:从auto到decltype的应用与实践
  • 使用Prometheus对微服务性能自定义指标监控
  • 深入解析 Lombok 的实现原理:以 @Builder 为例的实战演示(三)
  • SEO基础:什么是SERP?【百度SEO专家】
  • HTML5教程(一)- 网页与开发工具
  • Java进阶篇设计模式之二 ----- 工厂模式
  • 考研篇——数据结构王道3.2.2_队列的顺序实现
  • 从零开始理解 Trie 树:高效字符串存储与查找的利器【自动补全、拼写检查】
  • 关于sse、websocket与流式渲染
  • Python 语法与数据类型详解
  • LeetCode题练习与总结:扁平化嵌套列表迭代器--341
  • 51单片机快速入门之 AD(模数) DA(数模) 转换 2024/10/25
  • Typora 、 Minio and PicGo 图床搭建
  • 【计网】UDP Echo Server与Client实战:从零开始构建简单通信回显程序
  • 微服务网关Zuul
  • BuildCTF线上赛WP
  • 《使用Gin框架构建分布式应用》阅读笔记:p143-p207
  • 华为网络管理配置实例
  • 大语言模型数据处理方法(基于llama模型)
  • 爱奇艺大数据多 AZ 统一调度架构
  • 【C++篇】栈的层叠与队列的流动:在 STL 的韵律中探寻数据结构的优雅之舞
  • 使用 Flask 实现简单的登录注册功能
  • 计算机毕业设计Python+大模型微博情感分析 微博舆情预测 微博爬虫 微博大数据 舆情分析系统 大数据毕业设计 NLP文本分类 机器学习 深度学习 AI
  • CTF--Misc题型小结
  • 深度学习系列——RNN/LSTM/GRU,seq2seq/attention机制
  • 通过call指令来学习指令摘要表的细节