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

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

[复制链接]
waiman 发布时间:2018-2-6 15:03
本帖最后由 waiman-156411 于 2018-2-6 17:57 编辑
7 ?3 O  W) e. c$ C# O  a! Y/ g% n2 m/ }1 j# X" o
看到zoomdy 的驱动例子启发 https://www.stmcu.org.cn/module/forum/thread-610279-1-1.html
* n8 X+ J. _; `, u效果虽然不错,可以节省不少MCU资源,但用一个byte的SPI数据代替WS2812一个bit,很浪费RAM资源,而且生成像素的效率也很慢。
6 N, |* x, q: N( K; `4 z% [ 2017-01-21 09_12_30的屏幕截图.png
5 ]! O; v# V0 q8 s; l2 ?# U& V% N+ p, a
仔细一看发现 WS2812一个bit 周期是: " d5 V3 G( p# n1 t4 A3 m
         TH+TL=1250ns (±600ns), Min=650ns,Max=1850ns1 S1 Y5 s; G) v( `3 m
如果把SPI提速到4Mbits/S 的时候,SPI一个bit的周期是250ns,刚好和符合WS2812 T0H或T1L 400ns ±150ns范围。SPI 4个bit的周期刚好1000ns,刚好可以满足WS2812的时序需求。+ f. e; u3 k- M/ V+ j6 F( i
SPI=0x8 等于 WS2812的 0) |; I3 I2 T( m/ I( s
SPI=0xE 等于  WS2812的 1
% `5 l; I) O  [5 U+ r, S也就变成一个SPI的byte,可以表示2个Ws2812的2个bit。一RGB像素颜色只需要 24bit/2bit = 12byte) c% U+ x: ^& F  v$ Q8 o' ^! @
9 L& z" D* r$ h: ]# v+ E
! A+ Q/ i5 E; d1 j2 a$ o
SPI速度及极性配置:
5 K, Z3 c/ N6 _ 捕获.PNG ( C% `( T9 ^- D) N, u9 [

/ N1 v  v- \* `0 u. R
6 }7 d" f% e0 C2 x& H- S: r; oSPI只需要配置主机只发模式,这样可以节省一个IO口
+ c4 d* X# F" q3 _9 X 捕获2.PNG
# Q7 \6 o- V% H" v  K* z; T& @. f* U9 g
; Z& {, _1 _/ B# E3 \, P% j' s驱动整盘WS2812都很轻松
- Z  L, q; V' A. y 微信图片_20180206141317.jpg    微信图片_20180206135738.jpg
7 R1 M9 D. w$ T/ G0 d! J3 w/ i2 T/ ]# a  h9 B  O  G: E! z
驱动库说明:' L2 \4 v7 s$ l5 e
例子使用Stm32CubMx 4.23,STM32L151C8T6,IAR/MDK工程  W" [$ l& w- @6 i0 e6 J  B9 y: ]( G
程序移植了Adafruit NeoPixel库函数,1 u- O$ m" B% w$ `9 i: n( D" @
采用HAL库驱动方式(struct),可以方便移植到其他STM32芯片上,% Q& R' t) m* G* R
只需要分配多个struct变量,硬件稍微改下,就能分时复用,控制多串灯珠。
& T: W$ Q5 ]+ M 捕获3.PNG
. V& U5 i8 r1 \& K- h0 E5 y! K* N7 W  @4 A' L( x
9 x7 Z/ E0 o! C. C0 n0 V" 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 编辑 5 s) T" K  W4 k9 ]+ v" @  ~
翱翔云端的鸟 发表于 2018-4-10 09:459 A( ?: a" M/ \7 n) \
选择数据当做0码 1码的时候  是不是需要 首尾有限制

, n( A$ M4 k" r用3位代表1个bit通过SPI方式驱动WS2812时,SPI的速度请调整到2.25Mbps: f$ l: G: G# X. K9 w
1 y' N) ]4 a( D. v
以下是我用的代码,供你参考。ptr指向SPI Buf,value 是要转码的值。
: J5 L5 e, A' h8 O( Y) x. q( G2 }
: X3 f5 _8 G# m% T6 ^. Z  n
  1. #define        PIXEL_BYTE_0        (0x92)                                //10010010/ u- }% \: Y3 F7 u
  2.         #define        PIXEL_BYTE_1        (0x49)                                //010010016 s* w' T3 d: J1 {! ?9 T/ J" y+ P+ t
  3.         #define        PIXEL_BYTE_2        (0x24)                                //00100100
    & M* g) d2 ~$ D9 h' [+ F

  4. % d& _4 O9 b$ t, F9 f" u8 ?
  5.         uint16_t ws2812_spi::setbuffer(){; X# l5 H' \( i8 N2 b0 h; V
  6.                 uint8_t *ptr = buf_ptr + 16;
    9 O, o- h2 U/ b
  7.                 if(pixel_count == 0 || pixels == NULL || buf_ptr == NULL). k3 b4 f, b% n" m/ {  o
  8.                         return 0;# d4 I* Z4 X/ t1 L+ i
  9.                 else {9 U- S* @% e- ?- c+ @  U) d
  10.                         for(int i = 0; i < pixel_count; i ++){2 \7 j, t2 C5 U8 R7 I$ ^! B+ d
  11.                                 ptr = setPixelBuf(ptr, (pixels[i].green) * pixels[i].brightness / 0x100);
    ! Y- s) d  e( Q  D9 G( @7 E
  12.                                 ptr = setPixelBuf(ptr, (pixels[i].red) * pixels[i].brightness / 0x100);. W! q, P- `" I4 ?1 O9 ]
  13.                                 ptr = setPixelBuf(ptr, (pixels[i].blue) * pixels[i].brightness / 0x100);! q3 C) D/ ~  ~& W# f$ r
  14.                         }$ s4 ~  v( n# V! b% }, \: h* G
  15.                 }
    - d9 U( K- ?3 l* C: z3 F
  16.                 return (ptr - buf_ptr);
    0 K: Q3 c# D( z$ ^6 D( q2 R  h2 G2 [
  17.         }+ D. i+ e$ X* I% E
  18. ( @3 O, O8 S+ J
  19.         uint8_t* ws2812_spi::setPixelBuf(uint8_t *ptr, uint8_t value)
    - z3 o4 J' Z- j3 ^- M! b; E
  20.         {1 e% {. `$ Z: P! e2 ~
  21.                 ptr[0] = PIXEL_BYTE_0;
    9 a, m% Q/ i7 }  A. d
  22.                 ptr[1] = PIXEL_BYTE_1;
    * ^" b1 Z$ |1 Z( [) ~
  23.                 ptr[2] = PIXEL_BYTE_2;
    ( I  ^6 f5 J- |" O

  24. & x2 E/ s. X- _/ f
  25.                 ptr[0] |= (value & 0x80)?(0x40):0x00;
    % a0 V. J5 D# _: L) k1 \; r6 q
  26.                 value <<= 1;
    6 ~7 E2 L) k" [- w2 L. K% Q5 U1 V
  27.                 ptr[0] |= (value & 0x80)?(0x08):0x00;
    4 K8 w, R  }/ c
  28.                 value <<= 1;
    6 r/ a" F( o! d( R: @
  29.                 ptr[0] |= (value & 0x80)?(0x01):0x00;+ `8 l! ^3 k6 }9 s; c
  30.                 value <<= 1;
    * n8 S. V9 D1 Q/ E1 m0 h
  31.                 ptr[1] |= (value & 0x80)?(0x20):0x00;
    ! u4 d( q6 Q& g) `1 N
  32.                 value <<= 1;$ K/ O3 E$ g+ U% l6 E
  33.                 ptr[1] |= (value & 0x80)?(0x04):0x00;
    # _) l- b. d' F2 i) d' A8 Q3 ~
  34.                 value <<= 1;; v* b* U/ a, |6 V, E9 i; F0 {
  35.                 ptr[2] |= (value & 0x80)?(0x80):0x00;) M$ j: ~; P& i- v
  36.                 value <<= 1;8 t, j$ g9 V5 k7 ^  B( P
  37.                 ptr[2] |= (value & 0x80)?(0x10):0x00;& j0 U6 ~- \1 `+ \
  38.                 value <<= 1;4 \  K: U3 W2 C6 d; Y+ p
  39.                 ptr[2] |= (value & 0x80)?(0x20):0x00;
    - y- g/ i5 i+ L5 j8 j" `
  40.                 value <<= 1;; M; l/ [3 c9 d+ @

  41. " S5 O4 W/ o# I: I' T
  42.                 return (ptr + 3);
    ; h% _4 M) O% x7 }7 t; A0 F) \; p
  43.         }4 w/ q, {8 ~: c# @" {, a/ b+ }
复制代码
. ~% s! c, |& ~' {

+ H0 e1 E( ^* h4 r# N4 n
Yv-Yu 回答时间:2019-12-5 23:56:10
首先膜拜一下大佬,感谢大佬的分享,然后想请教一下各位,下面这段代码的实现是个啥意思啊,scale是干啥的啊?
1 Q) s& e" }% ~8 H% T# a! mvoid Sw28_SetBrightness(SW28_HandleTypeDef *swObj, uint8_t b)
, }$ H( o$ T' @{6 O5 v6 u/ `! E" q
    uint8_t newBrightness = b + 1;: S4 E) m( W0 x3 J
    uint8_t  oldBrightness = swObj->Brightness - 1; // De-wrap old brightness value+ a7 l. H2 ^9 k  Y2 L8 Y
    uint32_t c;  |8 W- i( S" [8 J0 D
    uint16_t scale,i;
: Z/ c" j& f/ a0 A' n' u4 G0 f    if(newBrightness != swObj->Brightness) 7 F& |# e" X; M5 A, N* Q! Z
      {    // Compare against prior value+ }- D$ `. i$ N% S
            // Brightness has changed -- re-scale existing data in RAM7 L% @2 T$ c. e0 I' o7 R
7 A2 ]  a7 x) G% F
         if(oldBrightness == 0) " z) [1 }% L" M9 {# R3 G) Y* J/ N$ B
                scale = 0; // Avoid /0
, m" ]9 r" B7 f( ^7 j6 z* \         else if(b == 255) 1 c$ N) R3 g. [5 r6 X
                scale = 65535 / oldBrightness;' a* P; ^! h( @" I  _; ^4 y0 l
         else , [* U3 C  _% g( W
                scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;
4 s/ K0 Q1 \6 x8 c3 x) }
0 z+ \( Z+ T. ^: x& b        for(i=0; i<swObj->PixelLen; i++)
* _! j/ ~# F& l2 |/ W, N$ M  d' y         {
' u  C  O( Y: R            c = Sw28_GetPixelColor(swObj,i);        //获取16bit颜色  
+ s. W% i) X; s% _4 b1 l            c = Sw28_ScaleColor(scale, c);. y  h8 f: |* y0 r5 ~( X  n/ f
            Sw28_SetPixelsColor(swObj,i,c);//设置颜色* G/ M4 K" R& Z0 ?+ x$ s
         }$ |+ d' y9 v3 t# X
        swObj->Brightness = newBrightness;5 I6 D4 V* I3 ^5 r; R
    }7 |* m+ q7 F9 O1 v( `
}
" c1 \# v" J' h+ x4 n& X5 N" c
8 d. D- m: m; u$ S
9 ?. m1 d/ W, z* [+ d" o" T
翱翔云端的鸟 回答时间:2018-4-10 09:42:55
jjbboox 发表于 2018-4-10 07:30
" x% P' N+ u, H9 ^: U, E是的,其实两头各还要加一个RESET信号,大概在32个字节左右吧。! l/ G* h* r7 j
比如8颗粒的灯条,那么分配的内存就应该是 ...
3 f% I- F6 _" D5 h7 t
我现在遇到一个问题    3bit表示一个code     001  表示0code         110表示1code     
' r- g; J$ Z$ K  c3 a* I当我要发送9byte的一个24bit颜色值的时候  比如是传输红色" {7 K/ j) q0 W% w( \
按照G R B  传输 为   0x00  0xFF  0x00   ; @3 M2 L6 n: s/ F' p1 k
/ L8 B5 `: q% l2 z8 v/ Z. L
转换为二进制/ a7 K% e0 j& E7 S! R
001 001 001 001 001 001 001 001 001 001 001
5 ^% q* G/ P+ S5 X: `6 [110 110 110 110 110 110 110 110 110 110 1105 v1 Q2 T* ^' [# z* B
001 001 001 001 001 001 001 001 001 001 001 ' p/ P% ^& B  Y6 g' j, i" u

$ o, B: Y$ H0 l$ T9 B+ HSPI数据为:
& @  `! I+ e: ?+ z! N) A        0x24                0x92                0x49! ^7 L5 h6 Q, i  s) @" b3 e* |
0010 0100   1001 0010    0100 1001  - S4 e6 c0 t# s' l7 }0 ?
        0xDB                0x6D                0xB6
( j: R  R( d7 x' I$ L# v5 }1101 1011   0110 1101    1011 0110& m+ {& h. N) W$ V4 P3 s/ b( o
        0x24                0x92                0x49  p# z+ U! N2 ^5 s
0010 0100   1001 0010    0100 1001
. N3 Z. b" T2 b. x8 P; q" M1 k, k# A2 o9 e  D9 N4 P/ g: S
! L) o6 U4 E* p8 C8 H$ E" k+ U# M
这里的问题是当我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后的代码:
7 m/ `, R% b  b0 U 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
) B+ s& P5 ?( a: [* H我只想知道楼主图中的代码显示是MDK的还是其他文本编辑器的,如果是MDK的话配置高亮的配置文件是否可以发我 ...
* T: U3 ^: r  u
mdk的,论坛里有人分享过了,你可以搜一下
Hackerpro 回答时间:2018-2-26 14:32:08
非常棒的想法,赞一个
jjbboox 回答时间:2018-2-26 15:14:01
厉害,这个帖子要收藏起来的。! ^5 w; ]# W" Q0 [
好方法。
板子粉丝 回答时间: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 手机版