你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32 HAL库使用SPI+DMA驱动WS2812优化方案  

[复制链接]
waiman 发布时间:2018-2-6 15:03
本帖最后由 waiman-156411 于 2018-2-6 17:57 编辑
$ v4 I3 h, ~$ S
/ x' t. k! K& d1 W' f5 e# c7 h看到zoomdy 的驱动例子启发 https://www.stmcu.org.cn/module/forum/thread-610279-1-1.html
, `: N/ w& n7 S: M效果虽然不错,可以节省不少MCU资源,但用一个byte的SPI数据代替WS2812一个bit,很浪费RAM资源,而且生成像素的效率也很慢。
8 ]- I2 F( D$ ?  I) P2 q: Q, }7 t 2017-01-21 09_12_30的屏幕截图.png
* f: k; J& ^  Z/ i1 ?$ Z
3 T2 A. _" l, \' S7 B% C$ l& u7 Q仔细一看发现 WS2812一个bit 周期是: # V- N& d, Z2 w* X
         TH+TL=1250ns (±600ns), Min=650ns,Max=1850ns& T% ]. v( T3 W8 \! ]
如果把SPI提速到4Mbits/S 的时候,SPI一个bit的周期是250ns,刚好和符合WS2812 T0H或T1L 400ns ±150ns范围。SPI 4个bit的周期刚好1000ns,刚好可以满足WS2812的时序需求。7 e$ A6 \6 J1 ^6 b7 X, _
SPI=0x8 等于 WS2812的 09 @9 b; U/ J7 N3 V, k
SPI=0xE 等于  WS2812的 1, P7 n& ?/ {4 D, U& f
也就变成一个SPI的byte,可以表示2个Ws2812的2个bit。一RGB像素颜色只需要 24bit/2bit = 12byte5 W' V' Z8 c0 r) T6 I

/ u$ I* h& p* d; f3 c. \: g
5 }6 H/ h! V+ V2 W- l; n3 {SPI速度及极性配置:
% W4 J* h5 g) j% P& ], Z 捕获.PNG
4 T' Y6 r8 a) y4 Z( q: c
; P4 K0 V( H; }* T
9 P" v+ @/ [; j$ s: `SPI只需要配置主机只发模式,这样可以节省一个IO口: _4 @. M3 n) U6 o  t* j
捕获2.PNG ( }+ t0 D6 \( O: q! K8 \  F
5 q- n; ^* q1 |# s& O! q
驱动整盘WS2812都很轻松& n* I# e$ X  i$ h
微信图片_20180206141317.jpg    微信图片_20180206135738.jpg
0 o7 [: y% J/ s5 E+ T8 m5 F
) o( g# O. I" U  _/ l驱动库说明:6 K$ [5 U7 ]* S. I$ f
例子使用Stm32CubMx 4.23,STM32L151C8T6,IAR/MDK工程
0 {( L) q/ `1 t) ~) C程序移植了Adafruit NeoPixel库函数,+ C8 @+ b1 \3 \# L) h. I# p
采用HAL库驱动方式(struct),可以方便移植到其他STM32芯片上,6 o" T# x) ~8 \. b# z" I1 m
只需要分配多个struct变量,硬件稍微改下,就能分时复用,控制多串灯珠。
6 I; ^( a8 Z, L( I) n; X 捕获3.PNG
) P* D3 m8 b1 H- r4 W5 q# C5 n  `- C- f: f
  m# S' e9 c6 p

SPI_DMA_WS281X.rar

下载

584.49 KB, 下载次数: 1200

驱动库

评分

参与人数 4 ST金币 +16 收起 理由
yogolu + 5 赞一个!
hacker + 2 赞一个!
g921002 + 4 很给力!
Inc_brza + 5 赞一个!

查看全部评分

1 收藏 28 评论77 发布时间:2018-2-6 15:03

举报

77个回答
jjbboox 回答时间:2018-4-10 13:34:21
本帖最后由 jjbboox 于 2018-4-10 13:39 编辑
3 D- s2 E- B8 t. z( v
翱翔云端的鸟 发表于 2018-4-10 09:45
: I: `$ W3 `9 w; m2 N6 V! Y$ u选择数据当做0码 1码的时候  是不是需要 首尾有限制

4 `0 N. W1 Q" }$ s$ L6 f/ t用3位代表1个bit通过SPI方式驱动WS2812时,SPI的速度请调整到2.25Mbps7 `! j' r# V0 d. |0 s' B4 d

5 I, W/ X- @- o1 t以下是我用的代码,供你参考。ptr指向SPI Buf,value 是要转码的值。
) [! A' ~! x: g  r" d5 p
% M( h/ N3 Q0 d$ _+ I
  1. #define        PIXEL_BYTE_0        (0x92)                                //100100102 Y/ g5 T  \; ~9 ~! ^
  2.         #define        PIXEL_BYTE_1        (0x49)                                //01001001
    / W& n$ l5 u1 h% C3 C
  3.         #define        PIXEL_BYTE_2        (0x24)                                //00100100- k' Z  l- V' i, t8 g
  4. 4 c: R9 T) r+ v4 X8 Z
  5.         uint16_t ws2812_spi::setbuffer(){
    3 N' O9 X( s2 Y' x: u" l4 Q$ g
  6.                 uint8_t *ptr = buf_ptr + 16;: ]' V: I* z7 @! ~
  7.                 if(pixel_count == 0 || pixels == NULL || buf_ptr == NULL)) w. f) R( r7 _2 f, `+ O& d. N2 y6 ?
  8.                         return 0;
    $ K4 @, Q$ R. z
  9.                 else {6 s$ @" A' \/ i- N$ P( ~
  10.                         for(int i = 0; i < pixel_count; i ++){) [/ u7 k& P% \+ L- U$ C. {/ \
  11.                                 ptr = setPixelBuf(ptr, (pixels[i].green) * pixels[i].brightness / 0x100);
    ! M; s* E5 |3 a/ d# T+ m* R: r$ a/ i
  12.                                 ptr = setPixelBuf(ptr, (pixels[i].red) * pixels[i].brightness / 0x100);
    ' W) D3 s- I4 S3 C! I' w, [: A
  13.                                 ptr = setPixelBuf(ptr, (pixels[i].blue) * pixels[i].brightness / 0x100);
    % h9 l+ @) ~, K5 G* h
  14.                         }
    * p' o4 O& x; s/ V5 m
  15.                 }- K# W* c/ S/ p5 n) f! U' q! m
  16.                 return (ptr - buf_ptr);
    0 [2 R+ a/ `, Y/ \; b) ~& c
  17.         }& ^: U; @( [1 H/ {' \: j# s  `0 K

  18. 3 t- i4 c% ^' \) Q. T
  19.         uint8_t* ws2812_spi::setPixelBuf(uint8_t *ptr, uint8_t value)4 u, R: i. s  ]
  20.         {
    4 g! J* v; {) |) y. d5 t
  21.                 ptr[0] = PIXEL_BYTE_0;  Y, G% P. A! ?
  22.                 ptr[1] = PIXEL_BYTE_1;
    ' z, A; [$ G- e$ O3 N8 ]1 h
  23.                 ptr[2] = PIXEL_BYTE_2;/ S! N& a8 m& D0 B* `
  24. + ^: k1 Q& V) @9 w/ t2 e
  25.                 ptr[0] |= (value & 0x80)?(0x40):0x00;
    ; X- c5 S1 B! U; E+ u
  26.                 value <<= 1;* Z# S; F8 g0 B' Y
  27.                 ptr[0] |= (value & 0x80)?(0x08):0x00;
    0 O/ ~( M2 U  C
  28.                 value <<= 1;
    . ^; O# p+ c: P5 g2 t  s8 G
  29.                 ptr[0] |= (value & 0x80)?(0x01):0x00;
    % _$ L- S( }1 M) n9 F  B1 h' ~6 p
  30.                 value <<= 1;$ ^/ L( Y5 ]( h# S' S8 p
  31.                 ptr[1] |= (value & 0x80)?(0x20):0x00;" `& W+ L, j7 y
  32.                 value <<= 1;
    : |$ U3 h, ^! Y. I
  33.                 ptr[1] |= (value & 0x80)?(0x04):0x00;
    ( Y! B, Q1 ~- z  E3 g
  34.                 value <<= 1;0 ~# V: h; F- U% K
  35.                 ptr[2] |= (value & 0x80)?(0x80):0x00;% l. G) k8 u# `5 O
  36.                 value <<= 1;1 v8 t+ C; o2 R) T0 Z
  37.                 ptr[2] |= (value & 0x80)?(0x10):0x00;8 M! ~+ B" X# B. W/ T0 S
  38.                 value <<= 1;
    & I; e2 x. y  @
  39.                 ptr[2] |= (value & 0x80)?(0x20):0x00;/ s1 z$ r0 I4 [" ~6 m1 s- Y  h
  40.                 value <<= 1;
    3 n* r  H) a7 z! D8 b

  41. ( A! x! _) e& T- Z) X4 ?0 P) a1 q
  42.                 return (ptr + 3);
    : D7 j# ~* G4 `; N- Y5 N3 I: o5 S
  43.         }# y  {/ C/ L! j
复制代码

' F  ^( v+ P" e4 U( R) F+ X% K
* y3 q4 `- s9 p0 ^1 P4 A7 J( F4 `
Yv-Yu 回答时间:2019-12-5 23:56:10
首先膜拜一下大佬,感谢大佬的分享,然后想请教一下各位,下面这段代码的实现是个啥意思啊,scale是干啥的啊?
$ M0 O# @  k% P3 {+ |8 g0 d* D7 s2 A6 Ivoid Sw28_SetBrightness(SW28_HandleTypeDef *swObj, uint8_t b)
6 y7 x% _: r2 r  Y' O' M. {  ^( L{
" `1 v4 m# D; N) s    uint8_t newBrightness = b + 1;
0 C7 ]# ?; ^" G! K4 X; B) p    uint8_t  oldBrightness = swObj->Brightness - 1; // De-wrap old brightness value1 n" P/ ^5 s, Q+ N
    uint32_t c;
8 B' }8 K# y1 [. B% X, |    uint16_t scale,i;
; j! A! Z1 H, b0 N2 F8 o9 q    if(newBrightness != swObj->Brightness)
" U& l$ d3 v" O7 p! p+ g9 U  F" i8 V      {    // Compare against prior value, g0 S9 F- [& H  m
            // Brightness has changed -- re-scale existing data in RAM
0 a3 P. e) d$ ^! Y. p, M1 Q% B# R* u- g
6 S1 i( f0 [7 _" G$ E% e, ?2 H8 C         if(oldBrightness == 0)
. Q/ s3 S! g- ^- k                scale = 0; // Avoid /0
4 V) m1 [0 X* x- q         else if(b == 255) 6 Y  T0 ~' l% {# i
                scale = 65535 / oldBrightness;( `; C0 ~- P& Z! f  u
         else
: e4 ~  Q9 t* U7 n9 p8 W1 @; b, g0 t8 i+ Z                scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;/ C  ~0 B; w, y/ q; Z& D

" R( m  E& X+ s  H$ t, I; m        for(i=0; i<swObj->PixelLen; i++)
$ X* q, j5 y2 @& n# a         {8 u* I9 R4 p: W1 E& j
            c = Sw28_GetPixelColor(swObj,i);        //获取16bit颜色  - `! K: G& [& e; E& i3 b: ~- E- b
            c = Sw28_ScaleColor(scale, c);
' A: v4 z7 R* V- X            Sw28_SetPixelsColor(swObj,i,c);//设置颜色
# ~. X: F& L8 G4 G1 y         }8 w; S& B5 Y. f1 d
        swObj->Brightness = newBrightness;
7 t4 k$ o- T2 z# `' v: y0 M# Y& K    }
- X( \4 L3 @7 e& u; H  ^}6 t  l1 u9 P2 W- S( h5 d0 g  T
( B" [3 `& j# V9 n

2 u$ ]8 {( _8 q/ m
翱翔云端的鸟 回答时间:2018-4-10 09:42:55
jjbboox 发表于 2018-4-10 07:30" r/ i/ p5 T+ Y8 O7 u
是的,其实两头各还要加一个RESET信号,大概在32个字节左右吧。. [7 G6 v5 g& V; }8 m* e& P
比如8颗粒的灯条,那么分配的内存就应该是 ...
; `8 G0 k8 _4 K; Y- ?
我现在遇到一个问题    3bit表示一个code     001  表示0code         110表示1code     % s1 K0 J+ Q( @# J$ A
当我要发送9byte的一个24bit颜色值的时候  比如是传输红色
/ x  ?7 h! x0 c( f2 ?0 h5 s- E按照G R B  传输 为   0x00  0xFF  0x00   % \# p# z* J% d* Q1 H8 V, I

* w! [5 V- f* Y7 C7 w0 n9 _转换为二进制3 Z9 n4 \+ R9 E6 i  b
001 001 001 001 001 001 001 001 001 001 0016 H9 I5 ?( @, T2 x+ Q6 I5 R
110 110 110 110 110 110 110 110 110 110 110
( W0 \$ r2 t; ~) ~  @- Q001 001 001 001 001 001 001 001 001 001 001
! A% U( t- p+ O/ Z9 Z
, U6 b: c! m! ISPI数据为:
+ g8 O$ M  u0 N  S! I# |2 ~        0x24                0x92                0x49. L" ?8 d, y! v4 K
0010 0100   1001 0010    0100 1001  ) F# _- A2 p7 @
        0xDB                0x6D                0xB6- r/ P* I* Q1 i1 j- O% p, b
1101 1011   0110 1101    1011 0110
: C2 E/ v; {: O' _  F( M        0x24                0x92                0x492 [. U! \3 w' J  l$ C
0010 0100   1001 0010    0100 1001 / h: R, F: ~5 p8 J& f4 ]

) t' j. {& ?0 V6 \3 Z0 W# [+ G  F1 m
! S8 y: J6 G, E& X% T. ?这里的问题是当我SPI发送完0x24之后  发送0x92的时候  0x24的最后一位是0  0x92最后一位是1   导致0x24的最后一个0码和0x92的1码混合了  
七哥 回答时间:2018-2-6 15:43:14
这个思路很有想法,应该加精
waiman 回答时间:2018-2-6 17:56:19
楼上代码有个BUG,回去修改后再放上
waiman 回答时间:2018-2-6 21:28:57
修补改BUG后的代码:
! F$ s7 h) O% z- J0 Y SPI_DMA_WS281X.rar (584.8 KB, 下载次数: 909)
onev 回答时间:2018-2-8 11:17:30
我只想知道楼主图中的代码显示是MDK的还是其他文本编辑器的,如果是MDK的话配置高亮的配置文件是否可以发我一份?如果是文本编辑器看起来不像是vs code  更不是sublime text。
zero99 回答时间:2018-2-12 14:16:58
感谢分享,已汇总到2月技术原创  https://www.stmcu.org.cn/module/forum/thread-614799-1-1.html
waiman 回答时间:2018-2-13 01:05:25
onev 发表于 2018-2-8 11:17- m6 {( @; Y: {: a2 r
我只想知道楼主图中的代码显示是MDK的还是其他文本编辑器的,如果是MDK的话配置高亮的配置文件是否可以发我 ...

) e: S, U. E# ^9 n9 ^mdk的,论坛里有人分享过了,你可以搜一下
Hackerpro 回答时间:2018-2-26 14:32:08
非常棒的想法,赞一个
jjbboox 回答时间:2018-2-26 15:14:01
厉害,这个帖子要收藏起来的。
8 B0 D. p+ u; e. W& Y- h好方法。
板子粉丝 回答时间:2018-2-28 09:36:53
先看看
jjbboox 回答时间:2018-3-1 09:35:48
很容易就实现了,非常方便,感谢楼主分享技术。
turingcode 回答时间:2018-3-5 00:36:15
学习学习啦啦啦啦
幻想卤蛋 回答时间:2018-3-7 22:11:47
谢谢楼主,学习下
xiaoxiao111 回答时间:2018-3-21 10:25:51
ertwhtrh
xujianhao 回答时间:2018-3-21 15:40:11
刚学,来看看

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版