请选择 进入手机版 | 继续访问电脑版

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

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

[复制链接]
waiman 发布时间:2018-2-6 15:03
本帖最后由 waiman-156411 于 2018-2-6 17:57 编辑
$ n* U0 R  X% ?, p$ s; [) ~
" u% }8 M0 _. D- f3 n2 l! ]( W2 B看到zoomdy 的驱动例子启发 https://www.stmcu.org.cn/module/forum/thread-610279-1-1.html$ Q3 a, {" C) ^0 c: c
效果虽然不错,可以节省不少MCU资源,但用一个byte的SPI数据代替WS2812一个bit,很浪费RAM资源,而且生成像素的效率也很慢。
1 H% \8 ^# S" k2 w) X# ? 2017-01-21 09_12_30的屏幕截图.png 0 h1 V0 r1 y) o9 F

, M: X5 p1 L) T, k仔细一看发现 WS2812一个bit 周期是:
3 a8 r8 h# o% P         TH+TL=1250ns (±600ns), Min=650ns,Max=1850ns
$ B2 n$ s0 e  `1 |如果把SPI提速到4Mbits/S 的时候,SPI一个bit的周期是250ns,刚好和符合WS2812 T0H或T1L 400ns ±150ns范围。SPI 4个bit的周期刚好1000ns,刚好可以满足WS2812的时序需求。" P6 q' @9 X, m
SPI=0x8 等于 WS2812的 0& e* m' S2 j5 w. R# h
SPI=0xE 等于  WS2812的 1
# ?3 V' @7 h: I5 A9 ?6 g也就变成一个SPI的byte,可以表示2个Ws2812的2个bit。一RGB像素颜色只需要 24bit/2bit = 12byte
7 B5 D6 ^" `" d& d+ B5 z! E6 u( z0 b
- K4 s& K1 t2 F+ q* a
SPI速度及极性配置:
  v. ?# O, {! z 捕获.PNG - Q* D6 Y3 \0 z6 T3 N- U! N. @
7 P/ f5 }2 v/ V9 ~
- w7 Z2 T& u0 ^! ^4 ?/ P4 x
SPI只需要配置主机只发模式,这样可以节省一个IO口
) Y% Q( e  i/ A 捕获2.PNG + C: A, a, F  e5 |

8 l) T  b% I, k驱动整盘WS2812都很轻松4 X% c- p8 f2 V" p4 w4 M, A' H
微信图片_20180206141317.jpg    微信图片_20180206135738.jpg 8 L' g# r1 c) ?5 U+ w6 L7 L# V
* b* J) I! ~* M$ b+ a
驱动库说明:
' `$ U% I) {* `3 ]4 y  r例子使用Stm32CubMx 4.23,STM32L151C8T6,IAR/MDK工程
$ @! ]: E4 X1 X程序移植了Adafruit NeoPixel库函数,3 M, `1 F" ~. H( h
采用HAL库驱动方式(struct),可以方便移植到其他STM32芯片上,
( b8 o& S- h# Z; y0 x只需要分配多个struct变量,硬件稍微改下,就能分时复用,控制多串灯珠。4 i  h5 `: e: A* k1 G+ V# b
捕获3.PNG
7 m, B7 h& H* G9 H  o* x/ G
4 p9 U5 Y9 q8 i$ @1 \3 f
9 F. A# W4 {/ e6 v8 \3 k- S

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 编辑
% B& Z9 f! J$ d' V, w8 m
翱翔云端的鸟 发表于 2018-4-10 09:45( P% @5 z8 B& ?& |  {& i) r
选择数据当做0码 1码的时候  是不是需要 首尾有限制
" `1 G1 [! D$ D/ T
用3位代表1个bit通过SPI方式驱动WS2812时,SPI的速度请调整到2.25Mbps" `# |  r; x5 J2 \1 x* K6 ]

% J7 B2 n, i2 I1 T9 T7 L/ R- R以下是我用的代码,供你参考。ptr指向SPI Buf,value 是要转码的值。
, g: I2 y0 S2 R3 o- v& l
7 ]- W. h6 X6 m
  1. #define        PIXEL_BYTE_0        (0x92)                                //10010010
      \7 v6 I* U. d, B7 l
  2.         #define        PIXEL_BYTE_1        (0x49)                                //01001001
    . w! x0 V- f1 [( O+ O* k
  3.         #define        PIXEL_BYTE_2        (0x24)                                //001001000 l) D7 P9 l1 I. ~/ J
  4. 4 F  T8 N% ?& Y9 X
  5.         uint16_t ws2812_spi::setbuffer(){/ |. }: u, I, j. \) M0 D1 [1 b9 I
  6.                 uint8_t *ptr = buf_ptr + 16;1 V  Z) x% Y) U
  7.                 if(pixel_count == 0 || pixels == NULL || buf_ptr == NULL)
    ; b0 A. I8 u0 D! h( v
  8.                         return 0;
    9 g+ D8 Q, f$ W' T6 s( C
  9.                 else {: ]( ?* L/ k5 b* @' o
  10.                         for(int i = 0; i < pixel_count; i ++){* d; n1 Y4 r) l6 i
  11.                                 ptr = setPixelBuf(ptr, (pixels[i].green) * pixels[i].brightness / 0x100);& r2 H) S, {5 H
  12.                                 ptr = setPixelBuf(ptr, (pixels[i].red) * pixels[i].brightness / 0x100);
    5 m: ]4 ]2 K5 C5 {4 V5 V) l: R
  13.                                 ptr = setPixelBuf(ptr, (pixels[i].blue) * pixels[i].brightness / 0x100);0 J* T% I* ~# ]( Q" T0 K+ T; w
  14.                         }3 @- T% U6 `2 h, ~
  15.                 }
    ) a0 l* t2 b" S8 N: i* c, T
  16.                 return (ptr - buf_ptr);1 w* f, R3 R, R
  17.         }3 V; L$ r, M1 ~  G' X2 P
  18. & B- k1 }, A2 r. x# u5 W
  19.         uint8_t* ws2812_spi::setPixelBuf(uint8_t *ptr, uint8_t value)2 P+ R8 ^5 @$ F
  20.         {
    : Q5 p5 W  Y+ r/ S
  21.                 ptr[0] = PIXEL_BYTE_0;
    ! l  H$ ~* M) I- M: ?: b
  22.                 ptr[1] = PIXEL_BYTE_1;
    6 L$ q- d+ e' w
  23.                 ptr[2] = PIXEL_BYTE_2;
    # U% ?% G2 E0 c( `
  24. " I" R: |" x6 x
  25.                 ptr[0] |= (value & 0x80)?(0x40):0x00;
    9 _4 ?  ?! q- ]! H' y3 b
  26.                 value <<= 1;
    6 s4 d+ X6 }0 Z. w% y9 w( S" c
  27.                 ptr[0] |= (value & 0x80)?(0x08):0x00;
    ) [4 Q0 d2 G' P$ o
  28.                 value <<= 1;* q: b8 I& `- {+ ]* \5 ~* S) Z
  29.                 ptr[0] |= (value & 0x80)?(0x01):0x00;0 Y! _* [0 u' L5 c6 Y3 C! c
  30.                 value <<= 1;
    6 l8 _/ e+ y$ j% \; l( L' {
  31.                 ptr[1] |= (value & 0x80)?(0x20):0x00;
    - ^, m6 ]5 `/ a; V* J8 M3 q' a7 P
  32.                 value <<= 1;
    , @, A8 g2 U- _. B/ I
  33.                 ptr[1] |= (value & 0x80)?(0x04):0x00;
    $ Q1 F$ V: n) L
  34.                 value <<= 1;4 Z; G6 Z# U9 N+ D, R: I
  35.                 ptr[2] |= (value & 0x80)?(0x80):0x00;3 Q- P. z2 H1 |4 o% D
  36.                 value <<= 1;4 R+ f6 A7 t4 j
  37.                 ptr[2] |= (value & 0x80)?(0x10):0x00;
    + A# d3 v$ ]) }6 e; |
  38.                 value <<= 1;2 z; l9 K% I% e2 p3 p+ I
  39.                 ptr[2] |= (value & 0x80)?(0x20):0x00;' Z" g6 a2 }4 \& D( y( d. t
  40.                 value <<= 1;( w# V; Q: d* p. |. x
  41. . p1 z1 m$ k# C: W3 N. i
  42.                 return (ptr + 3);
    * r7 d% G5 g. X# n# e& Z
  43.         }
    + _; P. L  c7 w/ f, {; e
复制代码

7 y. U  |7 T% i) W# R8 m4 A9 y, Y! a. m0 u9 ^2 ?
Yv-Yu 回答时间:2019-12-5 23:56:10
首先膜拜一下大佬,感谢大佬的分享,然后想请教一下各位,下面这段代码的实现是个啥意思啊,scale是干啥的啊?" l5 e" B4 {7 D" V
void Sw28_SetBrightness(SW28_HandleTypeDef *swObj, uint8_t b)7 c- {5 R- @+ \* K9 x! \! |4 M4 P
{( z! U" S% P6 O+ G# Z
    uint8_t newBrightness = b + 1;
; v1 k. M1 J! M5 K3 {# |1 r    uint8_t  oldBrightness = swObj->Brightness - 1; // De-wrap old brightness value
: o, D  A! h4 A$ R& a1 K+ z3 t0 S    uint32_t c;
. |) N1 J. D2 f. a, H    uint16_t scale,i;
4 ^" |* |& {9 ]: z5 E    if(newBrightness != swObj->Brightness)
+ L, |( X/ q3 z/ ~  G8 a+ |      {    // Compare against prior value
. w7 \; y& _* L7 u% x' `* c+ g* ]            // Brightness has changed -- re-scale existing data in RAM4 E0 Z; K5 V: I2 Q4 O- N+ \

+ p" r+ E* y% V: H1 `9 ~5 g7 U         if(oldBrightness == 0)
: D* k1 M8 G3 L# m6 K                scale = 0; // Avoid /0
" D0 e4 U2 y% H         else if(b == 255) 7 r5 H, E' d9 {" A& K
                scale = 65535 / oldBrightness;
6 F# [+ N! t5 Z# b5 w; }! }         else
( U+ @& G2 \; n8 l- l                scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;, K( U* _4 G  G+ p+ s+ z
# ~. E7 s6 ?( g) K
        for(i=0; i<swObj->PixelLen; i++)! M9 n1 L4 ?6 _1 t$ V+ q3 g2 D
         {
- t6 y, p0 d4 T! T) x& b( G            c = Sw28_GetPixelColor(swObj,i);        //获取16bit颜色  
: @$ b5 x$ O3 @7 t            c = Sw28_ScaleColor(scale, c);- U7 |6 A3 ]. E" A, D. S
            Sw28_SetPixelsColor(swObj,i,c);//设置颜色
$ q: M5 |2 m6 B( Z( C         }
+ O; Z% F! N  r+ b5 [        swObj->Brightness = newBrightness;+ N6 Z- ?; v7 j& o' i0 f
    }
+ h% b  x0 d8 p9 v$ y" D' T( M}4 X# T9 j+ \' J3 I. U
" v1 b+ ^& L- |1 k, V
- B% |7 C* [! E) d4 Q
翱翔云端的鸟 回答时间:2018-4-10 09:42:55
jjbboox 发表于 2018-4-10 07:30. f* h" W) G& O: s
是的,其实两头各还要加一个RESET信号,大概在32个字节左右吧。
0 n, y. C8 D9 [比如8颗粒的灯条,那么分配的内存就应该是 ...
8 y  J4 X- B1 ^* @2 z3 E
我现在遇到一个问题    3bit表示一个code     001  表示0code         110表示1code     6 ?/ a" W8 M' o  b5 p& I
当我要发送9byte的一个24bit颜色值的时候  比如是传输红色: x+ K5 }  I& C( a  [4 S
按照G R B  传输 为   0x00  0xFF  0x00   0 a+ ?' Z) }& F* F
5 b' O- \7 r4 g# ]* Y" M3 c( x  l; n
转换为二进制
5 T! r2 @* m$ H: Y2 ?001 001 001 001 001 001 001 001 001 001 001
" }' j: S/ i- j* k, p9 g' A$ h1 J110 110 110 110 110 110 110 110 110 110 110, M3 J0 Q4 @) _/ z! a
001 001 001 001 001 001 001 001 001 001 001 " |! O3 P' y8 c, e4 t& M5 B* X
; A/ y/ q6 r# f. P
SPI数据为:; _# g! k* p& b! R) j! Q2 r
        0x24                0x92                0x49( U6 D# U0 @& c, m
0010 0100   1001 0010    0100 1001  
! z1 M" I  \& R        0xDB                0x6D                0xB6* q# J! t5 o' |' `) u& Y* v- o
1101 1011   0110 1101    1011 0110
8 N/ G0 L, N6 n        0x24                0x92                0x494 x8 ?" q9 i6 D: x
0010 0100   1001 0010    0100 1001 * e+ @( T0 b! n6 W5 I

6 h0 |7 X7 A5 }) @* a3 t+ d: L, d/ [# r8 R( m- a* E3 V  ?! x5 |) A
这里的问题是当我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后的代码:* K* ]4 `7 G- ]3 k  V) ^
SPI_DMA_WS281X.rar (584.8 KB, 下载次数: 896)
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
9 h: p& e" d; x" j& N$ D5 v5 P( S我只想知道楼主图中的代码显示是MDK的还是其他文本编辑器的,如果是MDK的话配置高亮的配置文件是否可以发我 ...

% l" G2 @4 N4 d8 Q8 \  j; Imdk的,论坛里有人分享过了,你可以搜一下
Hackerpro 回答时间:2018-2-26 14:32:08
非常棒的想法,赞一个
jjbboox 回答时间:2018-2-26 15:14:01
厉害,这个帖子要收藏起来的。1 n0 H( e6 H" V- C6 Z) H9 r
好方法。
板子粉丝 回答时间: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 手机版