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

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

[复制链接]
waiman 发布时间:2018-2-6 15:03
本帖最后由 waiman-156411 于 2018-2-6 17:57 编辑
1 G4 g, @. R$ X( a7 h/ ~# F
  {: q% f$ _0 Z2 N% X: A看到zoomdy 的驱动例子启发 https://www.stmcu.org.cn/module/forum/thread-610279-1-1.html6 k) A2 c/ o! ^5 K$ e
效果虽然不错,可以节省不少MCU资源,但用一个byte的SPI数据代替WS2812一个bit,很浪费RAM资源,而且生成像素的效率也很慢。8 L2 G7 n* q$ H8 f, w# I3 S
2017-01-21 09_12_30的屏幕截图.png ) C# u. a$ R/ J! {' w! @6 I6 q

4 \" Q+ y: B8 q2 b仔细一看发现 WS2812一个bit 周期是:
6 u, s( y5 |; F         TH+TL=1250ns (±600ns), Min=650ns,Max=1850ns
1 v# S) F! U$ H# j如果把SPI提速到4Mbits/S 的时候,SPI一个bit的周期是250ns,刚好和符合WS2812 T0H或T1L 400ns ±150ns范围。SPI 4个bit的周期刚好1000ns,刚好可以满足WS2812的时序需求。" P, f$ p/ ]0 t. |* W/ e; I
SPI=0x8 等于 WS2812的 0. p  t, V. [1 s+ @: A! h5 g% P) N
SPI=0xE 等于  WS2812的 1
1 L# X+ f) Y8 ]! B7 {+ ?) X也就变成一个SPI的byte,可以表示2个Ws2812的2个bit。一RGB像素颜色只需要 24bit/2bit = 12byte
' M: }! y1 R$ u7 N( z+ H7 C) E1 j  Q% l- i

& l5 m) O6 O1 N7 y* {* T& ]8 ^, Y2 ?SPI速度及极性配置:
# ^1 H) z) m' m. e( H  G# J 捕获.PNG 7 T! V( M; u3 {5 X* j  y$ M4 g

" N, C6 h! }% c; ^. |. d( Y: p- T' Y3 `$ O3 x
SPI只需要配置主机只发模式,这样可以节省一个IO口
: ^3 F' Q/ `  o- `3 g% P# B4 K# t 捕获2.PNG
% I+ W- x; d& t7 f; j# q! J) X. a5 j. T/ @
驱动整盘WS2812都很轻松
( G* `  c: f* [; w) M1 _7 Q 微信图片_20180206141317.jpg    微信图片_20180206135738.jpg
+ h' u& |  t( C0 k
7 Y* R. h$ V1 `- Q! z6 U. |驱动库说明:, I3 W6 _' v! `. d- A" c
例子使用Stm32CubMx 4.23,STM32L151C8T6,IAR/MDK工程  n* e$ _) N5 N
程序移植了Adafruit NeoPixel库函数,
, z. x6 h! M" x% `2 f' V, |! Q采用HAL库驱动方式(struct),可以方便移植到其他STM32芯片上,
# K- D8 E5 s; a* b只需要分配多个struct变量,硬件稍微改下,就能分时复用,控制多串灯珠。- o4 Y" s+ r8 g4 k, G
捕获3.PNG 2 B; q2 A' u. k: O6 p' A
! ^. g- V4 z) I9 m

3 z9 d- R1 o/ B

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 编辑
; W- E$ W) P/ ]0 q: q
翱翔云端的鸟 发表于 2018-4-10 09:45
3 L( y9 a) }$ R. ]. \选择数据当做0码 1码的时候  是不是需要 首尾有限制
) c0 K- l- [. m4 q1 U' P
用3位代表1个bit通过SPI方式驱动WS2812时,SPI的速度请调整到2.25Mbps5 [- R; P& z0 R0 U. G* F

# f5 c- ]3 v! ?: C以下是我用的代码,供你参考。ptr指向SPI Buf,value 是要转码的值。4 |3 w. L, |! \8 V; O! Z" I1 f

) i" x  E( A- M) U
  1. #define        PIXEL_BYTE_0        (0x92)                                //10010010
    9 B. \' T# {6 n8 {% T( Y
  2.         #define        PIXEL_BYTE_1        (0x49)                                //01001001& [( q$ w2 z. b/ @9 @
  3.         #define        PIXEL_BYTE_2        (0x24)                                //00100100/ y5 ^3 n9 P0 M8 |; d
  4. + S' L7 l7 l( q: I8 V  ~
  5.         uint16_t ws2812_spi::setbuffer(){1 x* r$ x9 ^* [! A$ A
  6.                 uint8_t *ptr = buf_ptr + 16;
    . G1 _7 j  J4 x4 I" b
  7.                 if(pixel_count == 0 || pixels == NULL || buf_ptr == NULL)
    # @: C/ F: @2 n
  8.                         return 0;  y& n/ d0 R/ F$ }4 \0 {2 W7 d  W3 a
  9.                 else {
    7 U( [7 f+ I. p5 W
  10.                         for(int i = 0; i < pixel_count; i ++){) Q- Q$ z- r& G  m( G& P$ b
  11.                                 ptr = setPixelBuf(ptr, (pixels[i].green) * pixels[i].brightness / 0x100);! G% l! @2 w0 O$ L& t2 f7 G
  12.                                 ptr = setPixelBuf(ptr, (pixels[i].red) * pixels[i].brightness / 0x100);
    . m, Y. q0 E$ o7 `
  13.                                 ptr = setPixelBuf(ptr, (pixels[i].blue) * pixels[i].brightness / 0x100);+ G3 J7 |+ w) T% d+ K" W3 \
  14.                         }
    9 s" q5 @: N+ [: g- p6 v
  15.                 }
    5 g1 j0 b- ^0 e' o$ W" ^" e1 W
  16.                 return (ptr - buf_ptr);
    $ _" d% q' z% h+ M. W7 m$ h- h3 r
  17.         }# I, X4 x2 n% f: }
  18. ! h7 c7 o+ y: x% z. e# I
  19.         uint8_t* ws2812_spi::setPixelBuf(uint8_t *ptr, uint8_t value)
    ! Q' f8 K, d3 c4 W+ y
  20.         {
    2 ^' \1 n4 k  L3 P2 Y1 z7 }# V
  21.                 ptr[0] = PIXEL_BYTE_0;
    , J$ f$ C7 x( _
  22.                 ptr[1] = PIXEL_BYTE_1;( `- @6 q+ G" S2 j) T
  23.                 ptr[2] = PIXEL_BYTE_2;) X) `# Q9 d7 K# ?% v- S
  24. + |1 @9 x/ A; z
  25.                 ptr[0] |= (value & 0x80)?(0x40):0x00;
    & F5 m) o! E4 c6 H# J& n- B
  26.                 value <<= 1;7 v; Z9 x- o4 P
  27.                 ptr[0] |= (value & 0x80)?(0x08):0x00;# A8 h( O2 B9 o7 a! m8 Q
  28.                 value <<= 1;
    # C: I" `4 \+ W
  29.                 ptr[0] |= (value & 0x80)?(0x01):0x00;& I( E$ X  r4 S7 ?' \; ?4 Q' d
  30.                 value <<= 1;, x- P. p1 ^) \, G, ~
  31.                 ptr[1] |= (value & 0x80)?(0x20):0x00;
    . y& T8 Y/ r. f6 \+ O
  32.                 value <<= 1;2 ]' @+ f, O" h9 r4 G
  33.                 ptr[1] |= (value & 0x80)?(0x04):0x00;8 A' t1 N' Z$ Z% p* J9 j# z
  34.                 value <<= 1;
    % n. m. H6 x3 @1 Y9 V
  35.                 ptr[2] |= (value & 0x80)?(0x80):0x00;" z' \" P8 k$ p3 R1 Z
  36.                 value <<= 1;! \5 C( R& V# d# H  q
  37.                 ptr[2] |= (value & 0x80)?(0x10):0x00;
    / C, W  z" ^4 f8 z: x4 r
  38.                 value <<= 1;1 T1 W0 ~$ S6 E' h% {/ ?: @
  39.                 ptr[2] |= (value & 0x80)?(0x20):0x00;" d7 K3 H& f# I: H2 g2 T* ]! r$ r. u
  40.                 value <<= 1;) z; Q: D* O7 b, |
  41. & s3 C# d/ Z8 s9 l) Z, \: L
  42.                 return (ptr + 3);
    $ q( s$ q9 i0 `! r/ O
  43.         }0 t& t' c- G  Z) L  K
复制代码
* i- S0 d2 T, i' `, O& K7 M% d

) i5 j" U) S" s+ |; t: E# |0 K
Yv-Yu 回答时间:2019-12-5 23:56:10
首先膜拜一下大佬,感谢大佬的分享,然后想请教一下各位,下面这段代码的实现是个啥意思啊,scale是干啥的啊?
' v! ], X' j/ d: U9 ~void Sw28_SetBrightness(SW28_HandleTypeDef *swObj, uint8_t b)
* v9 M8 ?' ~3 C7 K/ _* u{
: r0 P  g2 ]' o1 q. u    uint8_t newBrightness = b + 1;7 y7 u3 ?3 g- E+ Y0 t% v9 Y
    uint8_t  oldBrightness = swObj->Brightness - 1; // De-wrap old brightness value
& Z- x, S; U+ I2 R8 |( \9 ~, s    uint32_t c;% T, X* t! m# N) C- z
    uint16_t scale,i;; r1 q0 Y* N8 i8 ^3 k
    if(newBrightness != swObj->Brightness) % p+ ~$ N- o4 `/ y( u; Q
      {    // Compare against prior value" S6 j( q) |+ N2 J; ^# b' \. U+ F
            // Brightness has changed -- re-scale existing data in RAM
- e1 l8 T3 ~0 P4 U9 ]$ a& k! N  P# Y9 O  J
         if(oldBrightness == 0) * u& Z( E2 A7 w: g, c! H
                scale = 0; // Avoid /0  t- @; w' m# u* G' l3 z, N2 m9 ]
         else if(b == 255) 5 P, C% k3 }/ s6 |1 ?) v1 o
                scale = 65535 / oldBrightness;
; B6 E0 g: L! H* X         else
2 y* J' [$ W; b                scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;  }9 N$ B: G# Y4 K
/ z! a8 g" D0 Z' L5 {) K7 ?
        for(i=0; i<swObj->PixelLen; i++)8 ~. N! }+ l% u, t  o
         {
2 b8 {, U( c4 {. B0 E            c = Sw28_GetPixelColor(swObj,i);        //获取16bit颜色  ' P/ D+ a0 ~9 h- K
            c = Sw28_ScaleColor(scale, c);# n4 x2 ^! a) H% M
            Sw28_SetPixelsColor(swObj,i,c);//设置颜色5 I! t9 x  g' j5 C3 T
         }' n( U! z. ]( r0 E$ a' |! K
        swObj->Brightness = newBrightness;- `/ J$ D2 K4 C/ E& K
    }
: }1 C* T2 j" T' g" _4 Y# h2 t}% S* p% F/ `- v

; o( }- c9 ^0 I$ e( s4 |4 K& z: j% W  |% c$ ]
翱翔云端的鸟 回答时间:2018-4-10 09:42:55
jjbboox 发表于 2018-4-10 07:30  l" f# @, _/ s, y
是的,其实两头各还要加一个RESET信号,大概在32个字节左右吧。" }7 c* G0 L5 ]1 @/ _
比如8颗粒的灯条,那么分配的内存就应该是 ...
' E9 I0 k9 x8 R
我现在遇到一个问题    3bit表示一个code     001  表示0code         110表示1code     
+ a1 W9 l# v5 g当我要发送9byte的一个24bit颜色值的时候  比如是传输红色) ]. h  E* h7 p/ P4 z
按照G R B  传输 为   0x00  0xFF  0x00   
9 P+ D6 i7 X3 ^% n( Z) q/ n1 n' L5 a3 H' E. J) X/ t
转换为二进制. ~6 }/ |7 w' O, U: P. E" M/ n; M
001 001 001 001 001 001 001 001 001 001 001# B" V5 R5 E/ q3 N
110 110 110 110 110 110 110 110 110 110 110* m: q1 q# w8 U  Z
001 001 001 001 001 001 001 001 001 001 001
' b% k6 K/ |+ X. m. i8 ~; t$ v: n0 U, P4 P9 \! t9 |+ M
SPI数据为:
+ O" J3 o  j1 E: r        0x24                0x92                0x49
1 s6 H  l. @/ M( k" f2 v0 W0010 0100   1001 0010    0100 1001  9 f3 j4 @/ J" [" X
        0xDB                0x6D                0xB6" u& p( D* A: C, z* j% f
1101 1011   0110 1101    1011 0110  C' B1 Y- V! q3 A1 B; ^
        0x24                0x92                0x49
* }7 j8 Y# n# w- O6 c7 p# w# [0010 0100   1001 0010    0100 1001
" N5 [# j* R# t8 k5 a+ C1 }6 X: k- K, N
) {% `  d+ N& w/ d9 Q
这里的问题是当我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后的代码:
+ B* Q" j6 G* u8 q3 Y! @" C3 z 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! }8 T- J. w! A7 R) `- q
我只想知道楼主图中的代码显示是MDK的还是其他文本编辑器的,如果是MDK的话配置高亮的配置文件是否可以发我 ...
) o6 X6 k" U3 I( }
mdk的,论坛里有人分享过了,你可以搜一下
Hackerpro 回答时间:2018-2-26 14:32:08
非常棒的想法,赞一个
jjbboox 回答时间:2018-2-26 15:14:01
厉害,这个帖子要收藏起来的。
  Z+ E% V+ ~0 y7 ~% ?" f. ]. v好方法。
板子粉丝 回答时间: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管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版