讨论用一片STM32C8T6驱动2048个串行单线RGB的方法

串行RGB WS2812 SK6812 等相信大家都很熟悉了,你电脑里面的光污染大多来源于此,网上很多相关的资料,驱动方式多种多样,PWM SPI,甚至直接进行IO模拟来驱动。

这里讨论一下,如何用一片C8T6驱动 2048个灯,并且实现以下参数:
驱动2048个RGB
30FPS 刷新速率
单片机不能阻塞
单片机不能爆空间
先看看时序,大家应该都很熟悉了。

0,1这里咱们先不看,不同的灯可能会有所差异,但是一个BIT需要1.25us的时间大多数灯都一样。
那么也就是一个灯传输24个字节(R G B 各需要8位)
换算下来就是 24x1.25us = 30us/每个灯的Data Time 数据传输时间。
那么我们有2048个灯 2048x30us = 61440us 数据时间 +上RES时间50us大概一帧所需要花费时间 61.490MS 很明显 ,如果按这样 以最快的速度 刷新灯的数据也只有约16帧
所以这里咱们得把灯板拆分开来,我打样的板子是16*32,所以我就分成了4份。

这样的话 我们需要分开4个IO口同时 驱动4个单片512个灯,512*30us =15360us+50us 单片理论刷新时间应该能达到60帧多一点。但是肯定做不到这么高,这里后面再说.

确定了硬件之后我们来确定一下软件驱动。
这里我使用PWM+DMA的方式来驱动。
PWM定时器设置为800Khz周期
然后依次将对应电平的占空比放到PWM的buff数组中,就可以实现0.1的CODE输出,那么也就说 一个灯需要24个占空比值,在DMA 的BUFF中一个灯需要24个长度为16位数去储存颜色数据,(这里先不 谈RES数据)。
简单换算一下,一个灯24(PWM值)x 2(16位数据一个数据占2个字节)=48个字节。本项目2048个灯仅仅数据部分一共需要98304个字节=96K TOO BIG
众所周知C8T6拥有64KB FLASH 20K的RAM,其的身躯根本无给他这么大的BUFF空间。那么怎么办呢。
重点来了
这里我们把DMA的半传输中断和循环模式打开,实现显示缓存的功能,示意如下:

1,写入 BUFF1 和BUFF2启动DMA
2,进入半传输HC中断后,更新缓存1的数据(此时此刻DAM正在传输缓存2的数据)
3,进入传输完成TC中断后,更新缓存2的数据(此时此刻DAM正在传输缓存1的数据)
4,进入半传输HC中断后,更新缓存1的数据(此时此刻DAM正在传输缓存2的数据)
5,进入传输完成TC中断后,更新缓存2的数据(此时此刻DAM正在传输缓存1的数据)
............
所有灯数据传输完毕后,将DMA缓存设置为0,发送一组RES指令过去使灯数据有效。
使用这种方法可大幅节省RAM空间,如果我们缓存定义为16个灯的大小,也就是一次传输16个灯的数据,单组2个缓存就是32个灯 32*24=768长度的16位数组,也就是占用1536个字节。
由于我们分开4路PWM 同时进行驱动,一共有4组DMA缓存 也就是4*1536=6144个字节
然后用一个8位数组,存放2048个灯的RGB数据占用 6144 个字节。
6144+6144=12288个字节,对于C8T6的RAM来说也完全可以跑下去了。

下面是使用CUBEMAX工程的建立:

定时器配置如上

这里我使用的是TIM1 的4个PWM通道,DMA设置为循环模式 Memory to Peripheral ,以及Half Word

时钟树如上

下面说一下程序方面需要注意的几个小点

由于分开了4组DMA,如果同时启动可能会导致同时进入传输中断导致数据错误,小小延迟一下避开同时进入中断即可解决此问题,也可以对DMA中断进行优先级进行设置来避免此问题。




还有一个坑,在Cubemx生成的void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* tim_pwmHandle) 代码下 通道4会有多出来的LINK

必须屏蔽其他DMA LINK。否则CH4不会进中断。




展示2
以这种方式进行RGB驱动,可以大幅节约系统资源,这边测试驱动2048个RGB,还做了256 的FFT,可达到30FPS左右的帧率。
有一点不得不说,2048个2812电流爆炸,没写亮度控制的时候,通电我仿佛看到了太阳,也未敢3色全点进行测试,电源直接保护了,保守估计得有50A左右电流。
个人倒腾记录可能有不严谨的地方,请见谅,本文仅提供一种思路,涉及代码已贴出,欢迎大家一起讨论建议。