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

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

[复制链接]
waiman 发布时间:2018-2-6 15:03
本帖最后由 waiman-156411 于 2018-2-6 17:57 编辑 , R8 v  Y: T# }' O) q6 ^( E
: ~- K& x  y  X% \: y
看到zoomdy 的驱动例子启发 https://www.stmcu.org.cn/module/forum/thread-610279-1-1.html9 ^" O/ e% f# e5 g
效果虽然不错,可以节省不少MCU资源,但用一个byte的SPI数据代替WS2812一个bit,很浪费RAM资源,而且生成像素的效率也很慢。
* l* B6 g' B4 M( Q9 d 2017-01-21 09_12_30的屏幕截图.png
, j( ]# V1 {. Z) c, u. \; z! _/ h
仔细一看发现 WS2812一个bit 周期是: , M& P( H% g. v1 q" r5 a! r! Z0 S
         TH+TL=1250ns (±600ns), Min=650ns,Max=1850ns: B1 I- L/ @1 f. ~2 |# }4 S; r
如果把SPI提速到4Mbits/S 的时候,SPI一个bit的周期是250ns,刚好和符合WS2812 T0H或T1L 400ns ±150ns范围。SPI 4个bit的周期刚好1000ns,刚好可以满足WS2812的时序需求。
3 |: }; {/ s; `5 {' ]  H- c, W( CSPI=0x8 等于 WS2812的 0" o( f& N. X7 a5 ?
SPI=0xE 等于  WS2812的 1) Q9 @& U6 w) h3 {# a2 z8 I
也就变成一个SPI的byte,可以表示2个Ws2812的2个bit。一RGB像素颜色只需要 24bit/2bit = 12byte/ I3 H% j. F6 G- Z6 c+ r( T/ f* }

$ P, o; R) i5 z* t! @2 l8 L: ?' N, m# q5 m
SPI速度及极性配置:
, A  h$ r1 U# D( _: u) [ 捕获.PNG 8 x$ v" ~7 f2 i2 h" Z" l

' V5 Y$ f2 _$ w2 N# o6 m* h3 q  u, N; Y$ x4 p
SPI只需要配置主机只发模式,这样可以节省一个IO口
' M6 ]# q( M( s# ]' g: W$ a4 Q 捕获2.PNG
+ h* u( r) u6 F3 C  ^  \* B! R9 u' X" N3 l( \. j9 m9 E% X) S
驱动整盘WS2812都很轻松8 C: I% U7 p. T+ I6 D. a' H" B% j
微信图片_20180206141317.jpg    微信图片_20180206135738.jpg 0 b4 c! H3 e( r$ U

) m5 u4 Z: }1 M  [  y驱动库说明:. {! E* Y. ?5 @0 n. R1 v% l$ h
例子使用Stm32CubMx 4.23,STM32L151C8T6,IAR/MDK工程, L/ G! _& \5 ]! z6 a
程序移植了Adafruit NeoPixel库函数,
  ^: H$ c! H+ O采用HAL库驱动方式(struct),可以方便移植到其他STM32芯片上,4 r3 I$ _) z" V) N, f; C, J8 m& d/ m: p
只需要分配多个struct变量,硬件稍微改下,就能分时复用,控制多串灯珠。7 L' O: ~0 @/ y* U
捕获3.PNG # S# z- _/ X6 T" T

; Z* U* \2 {7 q+ A6 Y' h: Z- X+ W

SPI_DMA_WS281X.rar

下载

584.49 KB, 下载次数: 1202

驱动库

评分

参与人数 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 编辑 % J* |& k; L8 h& s$ w
翱翔云端的鸟 发表于 2018-4-10 09:458 R$ R1 A4 c( R
选择数据当做0码 1码的时候  是不是需要 首尾有限制

. Y: q" T5 |5 n( S) z9 q用3位代表1个bit通过SPI方式驱动WS2812时,SPI的速度请调整到2.25Mbps1 m7 ^" r5 ^1 o3 l5 B7 W

% Q' H/ |' ]4 C. x, Z: s以下是我用的代码,供你参考。ptr指向SPI Buf,value 是要转码的值。$ X' b! |5 e0 v0 i# j- H  E
+ t& {) c) L% x, H/ H. {8 O
  1. #define        PIXEL_BYTE_0        (0x92)                                //100100105 I. l  A" j( T' [
  2.         #define        PIXEL_BYTE_1        (0x49)                                //01001001
    : p( K# B4 m$ |+ }+ D4 L! }7 J
  3.         #define        PIXEL_BYTE_2        (0x24)                                //00100100* D0 y, z$ s5 r' {: o

  4.   A+ b$ f' E" p8 h* n" v! [
  5.         uint16_t ws2812_spi::setbuffer(){  ?6 @1 d9 v/ k9 ^
  6.                 uint8_t *ptr = buf_ptr + 16;1 [2 V+ U+ W0 E) h
  7.                 if(pixel_count == 0 || pixels == NULL || buf_ptr == NULL), n: b2 ~" m6 z; _' E
  8.                         return 0;1 @% S9 z; n8 |; U
  9.                 else {; V/ ^+ P, ~+ A
  10.                         for(int i = 0; i < pixel_count; i ++){
    $ Q0 h* @4 p- f* \1 y$ n
  11.                                 ptr = setPixelBuf(ptr, (pixels[i].green) * pixels[i].brightness / 0x100);, I) \6 F' f& b, e) \9 I& V6 T
  12.                                 ptr = setPixelBuf(ptr, (pixels[i].red) * pixels[i].brightness / 0x100);# M" @8 t& w* u2 u6 Y
  13.                                 ptr = setPixelBuf(ptr, (pixels[i].blue) * pixels[i].brightness / 0x100);6 ^6 A: ~' {! M' a3 v: B! D& }4 Z
  14.                         }( f! z$ Q5 L! E6 R5 ^8 Y
  15.                 }  S5 \) t, P. L: P& S) m* U
  16.                 return (ptr - buf_ptr);/ w5 M0 v! Q3 z
  17.         }
      x9 s, e: v! L9 a

  18. 4 ~& [5 y+ W* x4 d( d4 b
  19.         uint8_t* ws2812_spi::setPixelBuf(uint8_t *ptr, uint8_t value)8 V- x, O7 V4 c; L2 Z
  20.         {- z$ i1 c; p; G6 ^
  21.                 ptr[0] = PIXEL_BYTE_0;5 y3 S4 A. Z+ H0 d
  22.                 ptr[1] = PIXEL_BYTE_1;  }9 _- K  J! H: {* K/ u: D, Y
  23.                 ptr[2] = PIXEL_BYTE_2;
    6 x( N2 w7 E$ Y- G
  24. % F& }" C* A% \' R% L
  25.                 ptr[0] |= (value & 0x80)?(0x40):0x00;
    * P' r8 z/ H4 r9 |: R0 r
  26.                 value <<= 1;
    * W8 `3 O8 }+ z- R$ L) j* H
  27.                 ptr[0] |= (value & 0x80)?(0x08):0x00;
    " Z& r  t) p* q* N
  28.                 value <<= 1;* ^1 i+ i3 U/ g: `; n, T
  29.                 ptr[0] |= (value & 0x80)?(0x01):0x00;
    % o- X% q' D. h7 I+ [; q
  30.                 value <<= 1;; K7 g7 g* u* p8 L& B8 R' n
  31.                 ptr[1] |= (value & 0x80)?(0x20):0x00;+ j, T( N# K/ c$ b* F! c
  32.                 value <<= 1;# y2 Z6 Q) h& m4 H2 J% g
  33.                 ptr[1] |= (value & 0x80)?(0x04):0x00;6 d* n  e# I$ g' t& }3 z+ Q. ^! ?
  34.                 value <<= 1;% Z' N- j3 t* g& J
  35.                 ptr[2] |= (value & 0x80)?(0x80):0x00;
    / I( \1 X6 A" F8 Y4 \
  36.                 value <<= 1;
    / S2 q$ a* H$ [
  37.                 ptr[2] |= (value & 0x80)?(0x10):0x00;* b% L9 D. {0 D, ]0 N8 X* Y4 {
  38.                 value <<= 1;' O3 ]- h7 S( u- [
  39.                 ptr[2] |= (value & 0x80)?(0x20):0x00;2 i0 y# Y# R0 Y, c
  40.                 value <<= 1;( j4 _1 Q% H4 Y: @. h
  41. ) F6 o7 U/ p+ l& I" Z7 }
  42.                 return (ptr + 3);+ z: I" l" i: \  d( X& Z8 y+ l
  43.         }
    3 O/ f0 A! \! [) y8 @
复制代码
- [! h3 b; `2 G/ O9 n: V$ V
* D9 J$ z( n: g' {  X( v/ `3 x
Yv-Yu 回答时间:2019-12-5 23:56:10
首先膜拜一下大佬,感谢大佬的分享,然后想请教一下各位,下面这段代码的实现是个啥意思啊,scale是干啥的啊?
& o- D: A. H( [- n6 ~void Sw28_SetBrightness(SW28_HandleTypeDef *swObj, uint8_t b)* O: c( v+ _& T9 P2 X3 c
{( S4 {& f7 E3 A3 t
    uint8_t newBrightness = b + 1;  C% _1 U; U6 f6 z+ d4 V
    uint8_t  oldBrightness = swObj->Brightness - 1; // De-wrap old brightness value
) f$ l# J+ S, X! k; p9 Y    uint32_t c;  A& }6 l# C) |' h( R$ t
    uint16_t scale,i;
$ E- s1 G& N1 M8 F; _9 C    if(newBrightness != swObj->Brightness)
7 V' e  F2 o5 b2 h- g, X+ A5 B      {    // Compare against prior value1 S4 A6 i3 F  }/ c
            // Brightness has changed -- re-scale existing data in RAM
& s" T9 n- l+ J2 ~* U/ I
4 J; P' q5 z7 K  y* F, a8 R7 H         if(oldBrightness == 0) ! r6 r: p6 B- K( _
                scale = 0; // Avoid /0' [6 }9 G; ]+ r  F
         else if(b == 255) # n1 ~  T. L/ p4 Q! d, Z
                scale = 65535 / oldBrightness;: G/ R. \* K5 h9 e3 c
         else $ n4 }$ H% f* d2 c2 u
                scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;: E7 U) S) e8 s+ G

; M+ h" Z* p/ C        for(i=0; i<swObj->PixelLen; i++)  B, `% p5 r5 Q. T6 g5 c; z
         {9 L" ~; i$ Q0 m
            c = Sw28_GetPixelColor(swObj,i);        //获取16bit颜色  8 W& D+ `! }% @% Q, p; D. ?( I
            c = Sw28_ScaleColor(scale, c);& r. l9 ^( u+ g, J( f& [
            Sw28_SetPixelsColor(swObj,i,c);//设置颜色7 S& f% G# d/ R% o/ {
         }
' Z$ v$ ^; l1 v        swObj->Brightness = newBrightness;
7 P0 ^* y6 a- U6 C- S# g; }$ y    }' X* u) k( E  _
}) g/ Z* s! c/ B

. [4 `, T; w7 {8 n4 S& \- s; L  e% {7 D; m' z, J2 c
翱翔云端的鸟 回答时间:2018-4-10 09:42:55
jjbboox 发表于 2018-4-10 07:30
/ R! w5 Y! g# ~1 B) T$ a是的,其实两头各还要加一个RESET信号,大概在32个字节左右吧。
+ O0 [) N( }' ~- ?5 M3 }比如8颗粒的灯条,那么分配的内存就应该是 ...
( B6 s# j6 H1 Z) l# J
我现在遇到一个问题    3bit表示一个code     001  表示0code         110表示1code     
1 n; T8 r8 ?/ u2 ]! d8 o当我要发送9byte的一个24bit颜色值的时候  比如是传输红色' x6 z1 @9 G6 z! g: c* K5 y
按照G R B  传输 为   0x00  0xFF  0x00   
! N3 [  G4 c! ~/ J  c, c' D2 Z* s  C6 x. d
转换为二进制. J& [! F; L- J, }; q
001 001 001 001 001 001 001 001 001 001 0014 y" z. `; o' E3 E
110 110 110 110 110 110 110 110 110 110 110
" i0 R! G, ]% `: k" a1 x001 001 001 001 001 001 001 001 001 001 001
" b, u+ ^4 R) H9 J  ^
' D: c, t8 D+ I* |9 ySPI数据为:
( n3 p- d7 c+ V( o) r) m% z        0x24                0x92                0x49) l3 \7 m6 o& S% e' D1 f2 d
0010 0100   1001 0010    0100 1001  + h# _6 M" u1 a0 i
        0xDB                0x6D                0xB6
5 i5 e' j* q* D1101 1011   0110 1101    1011 0110& \4 N, [* m: q6 T3 {! h
        0x24                0x92                0x49
7 M& n; S% }5 a% n. K0010 0100   1001 0010    0100 1001 9 I$ M2 _7 l+ R2 w
/ ?  ~* E- J2 v# S3 C  C3 j; x

/ [3 r( X6 n% i, A& j7 T! s这里的问题是当我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后的代码:0 A4 V; u0 q' @& W4 Q7 B5 D" L
SPI_DMA_WS281X.rar (584.8 KB, 下载次数: 952)
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  F9 g5 s# U1 ^1 L" h  q  X
我只想知道楼主图中的代码显示是MDK的还是其他文本编辑器的,如果是MDK的话配置高亮的配置文件是否可以发我 ...
' y2 {7 B; Y! _' j! |2 m' o
mdk的,论坛里有人分享过了,你可以搜一下
Hackerpro 回答时间:2018-2-26 14:32:08
非常棒的想法,赞一个
jjbboox 回答时间:2018-2-26 15:14:01
厉害,这个帖子要收藏起来的。8 w) l) \' F) \6 b6 Q. w7 G
好方法。
板子粉丝 回答时间: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管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版