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

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

[复制链接]
waiman 发布时间:2018-2-6 15:03
本帖最后由 waiman-156411 于 2018-2-6 17:57 编辑
3 l1 s/ e" K3 _+ b7 m8 r
: N. `8 V0 z% r9 K" ^3 I看到zoomdy 的驱动例子启发 https://www.stmcu.org.cn/module/forum/thread-610279-1-1.html2 X( I$ `1 @2 ~- r0 s
效果虽然不错,可以节省不少MCU资源,但用一个byte的SPI数据代替WS2812一个bit,很浪费RAM资源,而且生成像素的效率也很慢。
6 ~/ r) i& p# s( e' I' G7 k4 Y 2017-01-21 09_12_30的屏幕截图.png
8 M9 Q8 i- b2 i2 W
" |2 i$ X# b, W2 u) k" ^仔细一看发现 WS2812一个bit 周期是:
' W' \2 T% B" u6 V9 I: T2 q/ e         TH+TL=1250ns (±600ns), Min=650ns,Max=1850ns! w8 \0 |4 A: p
如果把SPI提速到4Mbits/S 的时候,SPI一个bit的周期是250ns,刚好和符合WS2812 T0H或T1L 400ns ±150ns范围。SPI 4个bit的周期刚好1000ns,刚好可以满足WS2812的时序需求。% V/ B8 s$ ]! t, \4 Y
SPI=0x8 等于 WS2812的 0
% ?9 C( `& ^7 i- gSPI=0xE 等于  WS2812的 17 l0 h! B4 a- z- e
也就变成一个SPI的byte,可以表示2个Ws2812的2个bit。一RGB像素颜色只需要 24bit/2bit = 12byte5 w% b# o. U6 D6 }, N1 @; g
0 V: m" ?# p2 X# p1 y

0 z4 |7 B. [2 X' j; w& r) NSPI速度及极性配置:
% V! B$ F' u! m6 t8 t 捕获.PNG
- E. ^0 \& q- S& @
( I5 z% ~0 T2 {2 Y4 S5 m( x* `1 ]$ Q7 Y6 x" A) f2 V& j' \
SPI只需要配置主机只发模式,这样可以节省一个IO口! x) S  E; H( I  y9 x( p1 t0 ^
捕获2.PNG + p! _/ c" x' M: j+ g2 W

* `/ q& t4 d" ]驱动整盘WS2812都很轻松
& f& Y% M, a' j4 b  O 微信图片_20180206141317.jpg    微信图片_20180206135738.jpg : y" q" M' K+ L. A7 x# n, O

2 @* T' x( b! h) f. d5 l驱动库说明:
" W+ f. u- W- P, V8 a例子使用Stm32CubMx 4.23,STM32L151C8T6,IAR/MDK工程
% y9 P8 f! l* g% J8 e( _# ]6 t程序移植了Adafruit NeoPixel库函数,# b+ r* K' w" Q3 L' L, ^3 H
采用HAL库驱动方式(struct),可以方便移植到其他STM32芯片上,  R; c6 B/ i6 c8 m! P" O3 O( S
只需要分配多个struct变量,硬件稍微改下,就能分时复用,控制多串灯珠。
! T2 h" z- Y$ H% p2 N 捕获3.PNG " ]1 x/ Y) G" ]) ?  l) U

9 C6 a4 n. ~; b; }- L+ t0 N$ P, G9 ?% }0 e  F, q) w9 q

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 编辑 1 u$ n3 Y# l! `# b; y7 U( {
翱翔云端的鸟 发表于 2018-4-10 09:45
$ M5 s, O9 y. I4 m( z0 `选择数据当做0码 1码的时候  是不是需要 首尾有限制

2 h0 _# h3 k7 v# d9 f; o用3位代表1个bit通过SPI方式驱动WS2812时,SPI的速度请调整到2.25Mbps# V' [+ l6 `8 L( d! r

1 |2 d' D% Q$ k% R以下是我用的代码,供你参考。ptr指向SPI Buf,value 是要转码的值。* K! R9 s7 v" X( d$ T

  y, O" a- G1 d& D: v2 @( j0 e. {
  1. #define        PIXEL_BYTE_0        (0x92)                                //10010010& N( R; I6 q' {/ n. K$ T4 w- |
  2.         #define        PIXEL_BYTE_1        (0x49)                                //010010011 n" F0 o2 S5 ~' R3 o% t$ H8 z7 g
  3.         #define        PIXEL_BYTE_2        (0x24)                                //001001006 g1 ?* C# `$ V7 q' ^/ D* ]
  4. & z, E# M' p# D2 X4 h2 I4 T
  5.         uint16_t ws2812_spi::setbuffer(){
    " l- I1 Z% t: z  B9 l/ l
  6.                 uint8_t *ptr = buf_ptr + 16;
      ]# o, A' q% I' U+ x' v8 Y
  7.                 if(pixel_count == 0 || pixels == NULL || buf_ptr == NULL)
    6 H, o& C: E# ?2 u* N5 C) D  `
  8.                         return 0;7 U' w" O/ L8 A4 e
  9.                 else {% z# ]3 l+ B8 {7 P# X5 K: I
  10.                         for(int i = 0; i < pixel_count; i ++){
    2 _. C) Y# g! x& ]
  11.                                 ptr = setPixelBuf(ptr, (pixels[i].green) * pixels[i].brightness / 0x100);
    : U7 H$ Y, }- ]; K, |) ~
  12.                                 ptr = setPixelBuf(ptr, (pixels[i].red) * pixels[i].brightness / 0x100);
    0 m" K2 U4 ^3 |, h6 J( e# V
  13.                                 ptr = setPixelBuf(ptr, (pixels[i].blue) * pixels[i].brightness / 0x100);7 W; Q) ]3 O8 _% R  `
  14.                         }' D$ R0 F3 Q9 s! B; c! v$ e
  15.                 }* x, N/ B" k! ^2 |1 T% q% s. f# d) c
  16.                 return (ptr - buf_ptr);7 J7 P- b/ ^/ m7 c
  17.         }
    : D7 [/ W2 S3 ~4 i: f' Y2 x3 T

  18. , Y9 ?  {( Z, @% ?* K
  19.         uint8_t* ws2812_spi::setPixelBuf(uint8_t *ptr, uint8_t value)- U) X5 _/ E+ q2 x6 u- v/ J
  20.         {  N  _" G9 N5 V* n
  21.                 ptr[0] = PIXEL_BYTE_0;! D; F$ R. i+ h3 j. H' r
  22.                 ptr[1] = PIXEL_BYTE_1;
    2 {( \$ h' S* ?3 v+ h0 w9 T. a
  23.                 ptr[2] = PIXEL_BYTE_2;" x- ?; Q% e% r
  24. ; w! P- M0 Z. x) D; }
  25.                 ptr[0] |= (value & 0x80)?(0x40):0x00;
    2 P! J9 |( z! D$ z2 `5 |" d- B9 L; D- h
  26.                 value <<= 1;
    % B1 ?; x0 C6 D4 o
  27.                 ptr[0] |= (value & 0x80)?(0x08):0x00;
    3 ]; g( t& j. K, r1 ~" e! n
  28.                 value <<= 1;
    , M& y0 w: I* ~4 {. U. v& w
  29.                 ptr[0] |= (value & 0x80)?(0x01):0x00;
    - E4 M9 H! t) N
  30.                 value <<= 1;1 `) l  h0 m( f1 d5 y1 _
  31.                 ptr[1] |= (value & 0x80)?(0x20):0x00;
    6 A/ u) m6 y: ?+ E
  32.                 value <<= 1;
    $ C( x/ C3 l8 o" |
  33.                 ptr[1] |= (value & 0x80)?(0x04):0x00;0 N$ t" N- A% b. [, v3 |+ a
  34.                 value <<= 1;
    4 h/ i9 }( G+ N' l2 ]9 y9 V( w" A0 l
  35.                 ptr[2] |= (value & 0x80)?(0x80):0x00;
    - @; a2 l6 l; I9 Y- R
  36.                 value <<= 1;
    . |6 V9 _% p+ M3 i' I/ m2 r6 k
  37.                 ptr[2] |= (value & 0x80)?(0x10):0x00;) Q5 L# v+ S( C2 {; X
  38.                 value <<= 1;
    7 w! d) O2 Q7 h8 G
  39.                 ptr[2] |= (value & 0x80)?(0x20):0x00;) q- o6 V% @' u2 w: a" w
  40.                 value <<= 1;
    0 g. V: ^7 _4 r1 j

  41. . f! {0 P9 o3 t% B7 v. Y
  42.                 return (ptr + 3);
    % |* M, O- C. u4 F
  43.         }
    8 H7 K* ]+ r' i. e
复制代码

$ M+ _* ~3 \% }; V! |/ {4 T  ^7 J2 U. D8 s
Yv-Yu 回答时间:2019-12-5 23:56:10
首先膜拜一下大佬,感谢大佬的分享,然后想请教一下各位,下面这段代码的实现是个啥意思啊,scale是干啥的啊?
! c& d3 Z3 G3 K+ Hvoid Sw28_SetBrightness(SW28_HandleTypeDef *swObj, uint8_t b)6 n2 J" @- T- @2 q! B" `
{, D( P7 v% Z1 v* F2 ]
    uint8_t newBrightness = b + 1;. q4 O& U) P# {0 d2 s
    uint8_t  oldBrightness = swObj->Brightness - 1; // De-wrap old brightness value7 ]/ R. a3 \6 U
    uint32_t c;
1 ^# t) `4 Z/ T8 y. Q: F4 E: o; G& \    uint16_t scale,i;2 m+ X# |. o3 S2 t9 r4 j
    if(newBrightness != swObj->Brightness)
4 P1 e+ F1 j, Y* P1 b  q      {    // Compare against prior value
+ Q$ ]. A$ D  s) T' L0 }            // Brightness has changed -- re-scale existing data in RAM
6 ~+ ?5 {* Q0 U7 T, x& H- u
; [1 S2 Q" U7 y/ ^+ d( \         if(oldBrightness == 0)
) t5 ~7 ^% }# w! N5 \                scale = 0; // Avoid /0
6 ~# ]* Y* {% r, {1 n5 u         else if(b == 255)
/ z7 W) m. C. N6 @7 B                scale = 65535 / oldBrightness;
/ P# s2 H/ \5 H; G+ E         else
" D% ~" T3 l6 z5 ~                scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;
3 Z* V# d* ?/ @6 C  S" z6 H$ v
- N9 j. c& n0 a! L) W( {        for(i=0; i<swObj->PixelLen; i++)
: I2 F& {/ m* `* c         {
& i5 `9 V7 L# {8 b2 T/ W3 s+ w- ]            c = Sw28_GetPixelColor(swObj,i);        //获取16bit颜色  1 U* F" X1 r8 T: `  \8 _9 a$ }. d) V
            c = Sw28_ScaleColor(scale, c);
' O. [! @# P1 y% s            Sw28_SetPixelsColor(swObj,i,c);//设置颜色; y, z( ?- t. b1 n  X+ L7 W& Y' p
         }
: R+ b+ q) ^" z0 M        swObj->Brightness = newBrightness;
* i: X% a- U8 G/ N3 K3 `/ E    }/ V% X. S% t& j% H
}" M: R( n! v/ y

" y8 [6 h# P8 `: X( D0 X/ t9 w; r3 Q) ]9 f3 J2 n5 T6 C% @
翱翔云端的鸟 回答时间:2018-4-10 09:42:55
jjbboox 发表于 2018-4-10 07:30
  S9 q* t) |1 r是的,其实两头各还要加一个RESET信号,大概在32个字节左右吧。3 f: X4 h* T2 u
比如8颗粒的灯条,那么分配的内存就应该是 ...
! u: z0 x) o6 H$ g! j
我现在遇到一个问题    3bit表示一个code     001  表示0code         110表示1code     
# z( w: L, |4 J. c) g当我要发送9byte的一个24bit颜色值的时候  比如是传输红色
' F" S2 b  q& D1 C& P% `0 Y  S- F按照G R B  传输 为   0x00  0xFF  0x00   
* I: C3 k7 O  v! n6 `2 C+ |+ |+ C- L( ~/ I
转换为二进制
2 I2 s/ \3 @6 W. X) |001 001 001 001 001 001 001 001 001 001 001
3 _! O+ q+ ^6 s- d' T$ ]* x110 110 110 110 110 110 110 110 110 110 110+ y0 D7 ~7 F  R8 r6 b
001 001 001 001 001 001 001 001 001 001 001 ' f& z) K! k5 ^6 R$ j
& v# p. d) b* A0 Z
SPI数据为:
4 ]: Z2 ?2 y! V% D        0x24                0x92                0x495 ^: t6 Z. u& ^( ~' A4 o0 a
0010 0100   1001 0010    0100 1001  4 W( C3 u9 `  m# i. y  O
        0xDB                0x6D                0xB65 K* E1 P  x7 b9 y
1101 1011   0110 1101    1011 0110# ?. j, b1 w! k, v. J
        0x24                0x92                0x49" }: L+ [$ e( z! G" ]
0010 0100   1001 0010    0100 1001
6 w1 h8 ]: v; M8 e* X
: S# }" e$ f7 |+ j( ~' }) t8 ^0 }/ ?) t# r- ^/ c) 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后的代码:
" C$ @4 A: K: S4 K% ^: P( {( ^- {" W 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
: K; @# X6 P, l9 ]- Y我只想知道楼主图中的代码显示是MDK的还是其他文本编辑器的,如果是MDK的话配置高亮的配置文件是否可以发我 ...

! A2 t$ q: _5 p6 P: c9 |, nmdk的,论坛里有人分享过了,你可以搜一下
Hackerpro 回答时间:2018-2-26 14:32:08
非常棒的想法,赞一个
jjbboox 回答时间:2018-2-26 15:14:01
厉害,这个帖子要收藏起来的。" e% K# v9 f6 c# l; [% Y
好方法。
板子粉丝 回答时间: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 手机版