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

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

[复制链接]
waiman 发布时间:2018-2-6 15:03
本帖最后由 waiman-156411 于 2018-2-6 17:57 编辑
+ O: {% R# e1 V0 ^7 Y& f- S! b3 [7 ?+ H6 ]& x) l
看到zoomdy 的驱动例子启发 https://www.stmcu.org.cn/module/forum/thread-610279-1-1.html
% D$ t1 k1 e4 O; G, X9 M效果虽然不错,可以节省不少MCU资源,但用一个byte的SPI数据代替WS2812一个bit,很浪费RAM资源,而且生成像素的效率也很慢。
, C3 m7 H0 k2 X# [5 j 2017-01-21 09_12_30的屏幕截图.png
+ A5 l4 b4 Z+ K0 s: {2 @* V
! H+ ?& i$ p% n" p# z$ d& G( Y仔细一看发现 WS2812一个bit 周期是:
+ j& v7 `& R5 u/ c% ^5 s$ j) O# m         TH+TL=1250ns (±600ns), Min=650ns,Max=1850ns1 h+ i& C$ K9 ?, i
如果把SPI提速到4Mbits/S 的时候,SPI一个bit的周期是250ns,刚好和符合WS2812 T0H或T1L 400ns ±150ns范围。SPI 4个bit的周期刚好1000ns,刚好可以满足WS2812的时序需求。- `; `7 {" w; \! C! n
SPI=0x8 等于 WS2812的 0) `! Z5 J7 q. c6 a
SPI=0xE 等于  WS2812的 18 D) R& r$ a9 Z# V9 V/ I0 q8 j
也就变成一个SPI的byte,可以表示2个Ws2812的2个bit。一RGB像素颜色只需要 24bit/2bit = 12byte
" X6 H1 K& n' c" M# E3 D) m1 G
: p4 E& ?  F* s' }3 z9 ^
& O6 v) q* F) r% b$ QSPI速度及极性配置:# |( V3 P  N2 A& `# ^, n6 p
捕获.PNG
2 H  s/ H2 Z0 O/ r! _2 J6 `+ k+ {; S3 l
+ F1 ]8 j" x1 F. ^6 L8 P
SPI只需要配置主机只发模式,这样可以节省一个IO口
3 _. \7 Q4 b0 c  | 捕获2.PNG
# o7 U7 G( H6 J1 ~5 C$ d
$ t" x5 P" J. t驱动整盘WS2812都很轻松
. N# t! R- L5 i' |6 Z: o 微信图片_20180206141317.jpg    微信图片_20180206135738.jpg
2 x6 b4 a2 Y3 I) _- L5 X# Y0 z1 ?  L) b+ i
驱动库说明:
1 O9 }" p4 H- n9 a, ?例子使用Stm32CubMx 4.23,STM32L151C8T6,IAR/MDK工程, s, I& V$ o/ g+ d* t+ E0 R
程序移植了Adafruit NeoPixel库函数,
$ G: F& \% {9 d- }% m( @0 ~2 z采用HAL库驱动方式(struct),可以方便移植到其他STM32芯片上,
- Y" Q% `1 y4 N7 W8 c. u# g4 j! l只需要分配多个struct变量,硬件稍微改下,就能分时复用,控制多串灯珠。
6 h) I# s' i/ _: l' x* m 捕获3.PNG 1 g8 x! w# r4 o, S
1 C! ?! `( E& Y# t% }2 t

9 R4 b$ ?3 j" @

SPI_DMA_WS281X.rar

下载

584.49 KB, 下载次数: 1161

驱动库

评分

参与人数 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 编辑
, \. p0 j3 h& n* z7 ?
翱翔云端的鸟 发表于 2018-4-10 09:451 ~/ r3 t; q7 _$ R" Q5 y
选择数据当做0码 1码的时候  是不是需要 首尾有限制

! t0 {$ ?( \( m用3位代表1个bit通过SPI方式驱动WS2812时,SPI的速度请调整到2.25Mbps
" ?* P* {/ ^; L6 x- L) l5 {& d$ f/ N. m4 r- ~: g' \, p3 j
以下是我用的代码,供你参考。ptr指向SPI Buf,value 是要转码的值。
* Y0 W" G7 ]' ^
5 }. t9 `7 n' U: w8 V
  1. #define        PIXEL_BYTE_0        (0x92)                                //10010010- `8 Z6 y% @" g. o) E$ ?- L- U) B3 T
  2.         #define        PIXEL_BYTE_1        (0x49)                                //01001001
    5 H, t' K+ B9 I
  3.         #define        PIXEL_BYTE_2        (0x24)                                //00100100
    1 k$ i* z+ \, {4 Z- d) [0 L: z! L4 o

  4.   G2 Q* I2 y0 j. U) f) S7 l
  5.         uint16_t ws2812_spi::setbuffer(){; T7 Q* m; z2 e9 u
  6.                 uint8_t *ptr = buf_ptr + 16;
    ' j2 M: z# R  E# n
  7.                 if(pixel_count == 0 || pixels == NULL || buf_ptr == NULL)& W2 v) Q: b, {  v7 C
  8.                         return 0;
    5 d# h! U; [5 B. _
  9.                 else {8 m, l5 p' w: O( T) x  R& q$ Q+ c" P0 O
  10.                         for(int i = 0; i < pixel_count; i ++){. C5 J4 i) |$ I" E
  11.                                 ptr = setPixelBuf(ptr, (pixels[i].green) * pixels[i].brightness / 0x100);
    8 y. e. ]* q" [0 |
  12.                                 ptr = setPixelBuf(ptr, (pixels[i].red) * pixels[i].brightness / 0x100);, v+ v5 Y4 g, ^
  13.                                 ptr = setPixelBuf(ptr, (pixels[i].blue) * pixels[i].brightness / 0x100);
    / L1 l+ ~- X# V9 T! T! G+ s
  14.                         }$ F8 x! }3 H% F3 q, W
  15.                 }
    $ y( H( y+ E1 `* ]# c; w
  16.                 return (ptr - buf_ptr);
    ( v( A9 B% Q" `  Q4 O/ @
  17.         }
    1 I% v3 `8 s# b" h) x; B. }6 @

  18. / f- }0 h' t8 v8 K% v( G8 I
  19.         uint8_t* ws2812_spi::setPixelBuf(uint8_t *ptr, uint8_t value)% I- E& ?  A6 m/ F% `; @& I
  20.         {1 n' ~) ]9 L4 {+ g' v$ A, ?3 N+ U% a- l  e
  21.                 ptr[0] = PIXEL_BYTE_0;6 v) h+ R7 b+ h3 u* S4 m
  22.                 ptr[1] = PIXEL_BYTE_1;: V( d9 s$ N, I
  23.                 ptr[2] = PIXEL_BYTE_2;
    3 r* p- b6 f8 W. }! O! I

  24. 5 h0 _8 V: b& ~" [
  25.                 ptr[0] |= (value & 0x80)?(0x40):0x00;' a2 R& }- M* ~6 {4 r
  26.                 value <<= 1;
    3 ?3 ~* T3 [2 A4 z$ g9 D& c+ `
  27.                 ptr[0] |= (value & 0x80)?(0x08):0x00;' J0 P7 g7 k. x9 ~( X5 n  P& _
  28.                 value <<= 1;6 Y' v& M) x' }7 d# K
  29.                 ptr[0] |= (value & 0x80)?(0x01):0x00;
    * r4 ?' \3 Z/ b- M
  30.                 value <<= 1;! x/ U/ n) G- P1 f) ?6 J( R% O
  31.                 ptr[1] |= (value & 0x80)?(0x20):0x00;. Z7 X3 p. c8 M  M: o: p' u" j
  32.                 value <<= 1;
    7 S4 F) U9 [8 m" h  Q% K2 q
  33.                 ptr[1] |= (value & 0x80)?(0x04):0x00;$ j0 g* H2 r2 Q: h
  34.                 value <<= 1;
    ) {8 j: ]7 C7 z) f$ Y; K9 t
  35.                 ptr[2] |= (value & 0x80)?(0x80):0x00;0 W; N: e* }0 U9 B. h# K& U
  36.                 value <<= 1;
    - Q' n, T$ d; M& H; R
  37.                 ptr[2] |= (value & 0x80)?(0x10):0x00;8 [0 H- ]7 H: r3 Z
  38.                 value <<= 1;% p: M, j+ f- o
  39.                 ptr[2] |= (value & 0x80)?(0x20):0x00;
    $ B$ ]! G9 T% O2 d# H
  40.                 value <<= 1;
    : c  q# A( z' j; L
  41. ' V; r, P& |. \/ _
  42.                 return (ptr + 3);. f3 ?5 @% E; q; U( I
  43.         }
    ; e  S  v4 I- w, h
复制代码
' S1 j: p4 w1 \3 B- }# I7 {$ W+ c+ `

) T$ l) H5 f1 I
Yv-Yu 回答时间:2019-12-5 23:56:10
首先膜拜一下大佬,感谢大佬的分享,然后想请教一下各位,下面这段代码的实现是个啥意思啊,scale是干啥的啊?2 o8 C0 O% x1 c. D
void Sw28_SetBrightness(SW28_HandleTypeDef *swObj, uint8_t b)
% J2 G3 ^" R4 d; w. n{
6 s6 O' \8 n9 D. T$ s9 t/ u    uint8_t newBrightness = b + 1;* ?) R0 {. [/ ^7 e
    uint8_t  oldBrightness = swObj->Brightness - 1; // De-wrap old brightness value! q7 Z6 @3 C! Z6 V! H  t
    uint32_t c;) u: l/ I: [+ g
    uint16_t scale,i;
( a5 C' i1 t* w; a3 B    if(newBrightness != swObj->Brightness) " S- P% o+ E7 X" d5 ^$ _$ b" A
      {    // Compare against prior value
* A* K6 A3 K8 T5 p  A            // Brightness has changed -- re-scale existing data in RAM( k1 ^( ^0 g8 r4 q% c
; h4 L" G$ b' n! G- D: M
         if(oldBrightness == 0) 7 x' G8 t$ E+ s+ a: y5 C
                scale = 0; // Avoid /0: r& N+ b7 f6 W4 o4 l
         else if(b == 255)
; \4 `: y, L1 P3 m. F. X) |                scale = 65535 / oldBrightness;
2 V" P7 z5 _( j" d. c! X' H         else
+ \' d* Q8 Z/ Z* A" d                scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;0 G: `( c9 w4 j$ L( H+ z

6 x5 `7 o6 w+ ^1 \) O0 v' }        for(i=0; i<swObj->PixelLen; i++)9 E6 P& G/ D8 D( a0 G
         {
. j: j. n: Y  E; T" w            c = Sw28_GetPixelColor(swObj,i);        //获取16bit颜色  
: D' j9 B& c' y+ q7 e! O5 o            c = Sw28_ScaleColor(scale, c);) M- O) R) c/ A2 w, _
            Sw28_SetPixelsColor(swObj,i,c);//设置颜色
4 F) [& {; i4 k7 Y% L% K9 d4 b2 Z         }9 d5 f9 e4 P% j" Q$ K, v. @
        swObj->Brightness = newBrightness;& o3 }6 p/ _' @; K+ s/ k  q7 X1 G
    }8 A# l- o/ L; N9 J* u) G8 e
}
* X+ l; \; S2 b* j# N' z2 R( Z) O1 M& p3 m: Q4 I
6 N, F( s/ Z- d
翱翔云端的鸟 回答时间:2018-4-10 09:42:55
jjbboox 发表于 2018-4-10 07:30
) q# ~0 p9 E, Z5 U! a  r" f是的,其实两头各还要加一个RESET信号,大概在32个字节左右吧。
4 B& I& X. Y$ A3 L7 a) O: R8 ?比如8颗粒的灯条,那么分配的内存就应该是 ...

1 L7 V/ {& _  T; i7 i我现在遇到一个问题    3bit表示一个code     001  表示0code         110表示1code     1 y: M/ ~4 B8 L  X1 V
当我要发送9byte的一个24bit颜色值的时候  比如是传输红色
6 P8 _) s( M) R5 `9 H+ e* e! [按照G R B  传输 为   0x00  0xFF  0x00   
7 w1 L7 h* |6 P' ^
3 g/ c( y# }6 p$ s: \转换为二进制8 s3 ]3 E, `- y0 j/ E3 C
001 001 001 001 001 001 001 001 001 001 0019 H: ?+ ]% f$ D& f1 H4 }
110 110 110 110 110 110 110 110 110 110 110& r- t+ y( l  U) x4 O, v
001 001 001 001 001 001 001 001 001 001 001 ' a+ ~8 M2 u( p, X! [6 s

1 G% g- ]9 r7 J  ?' R! R- sSPI数据为:
3 a; s/ T3 [' [& j$ g) L$ z        0x24                0x92                0x49# y* h, z1 k1 g5 I7 U
0010 0100   1001 0010    0100 1001  
+ |, T$ E( a( f        0xDB                0x6D                0xB6
' Z, d( ]) M( W0 [5 Q; v1101 1011   0110 1101    1011 0110
, I9 E  j: P# k$ V, _        0x24                0x92                0x49
+ ^* T& T/ Q) _7 l0010 0100   1001 0010    0100 1001
* a; x/ j$ ^+ e$ \5 ]6 F8 h* z  B' B/ Z8 }8 z

. H  {7 \7 a6 o, d( E& q" l7 ]+ Y这里的问题是当我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后的代码:- c1 l- I, @9 ~0 a+ D* q* C
SPI_DMA_WS281X.rar (584.8 KB, 下载次数: 897)
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( j  x5 ~. N/ i9 z9 F; A: Q
我只想知道楼主图中的代码显示是MDK的还是其他文本编辑器的,如果是MDK的话配置高亮的配置文件是否可以发我 ...

1 e4 h1 ?, |, }2 s/ V0 E/ J7 i3 K$ Emdk的,论坛里有人分享过了,你可以搜一下
Hackerpro 回答时间:2018-2-26 14:32:08
非常棒的想法,赞一个
jjbboox 回答时间:2018-2-26 15:14:01
厉害,这个帖子要收藏起来的。* {) D$ h0 @& i3 r; y$ x5 K
好方法。
板子粉丝 回答时间: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管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版