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

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

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

[复制链接]
waiman 发布时间:2018-2-6 15:03
本帖最后由 waiman-156411 于 2018-2-6 17:57 编辑
- s3 T* p/ _0 w/ v" A+ `. a
" A% M1 N8 M0 [8 M4 j  S! [  Q/ o$ P看到zoomdy 的驱动例子启发 https://www.stmcu.org.cn/module/forum/thread-610279-1-1.html
, q6 [' W: d5 D9 a9 y6 M效果虽然不错,可以节省不少MCU资源,但用一个byte的SPI数据代替WS2812一个bit,很浪费RAM资源,而且生成像素的效率也很慢。
: C7 T2 I$ u* N 2017-01-21 09_12_30的屏幕截图.png 2 @5 ]0 Y; x/ i6 n* w5 o
8 k1 I6 p9 P! N7 W1 }- N7 G
仔细一看发现 WS2812一个bit 周期是: 5 @& Q3 H* _" q, V3 o0 T4 ~
         TH+TL=1250ns (±600ns), Min=650ns,Max=1850ns
, m$ V0 f# @, G$ J- q, O, X  y如果把SPI提速到4Mbits/S 的时候,SPI一个bit的周期是250ns,刚好和符合WS2812 T0H或T1L 400ns ±150ns范围。SPI 4个bit的周期刚好1000ns,刚好可以满足WS2812的时序需求。
2 e$ x$ F+ C6 Q, DSPI=0x8 等于 WS2812的 0
" C  p: c$ @2 K0 o3 U0 ^; SSPI=0xE 等于  WS2812的 1$ Q  |% _+ F( f& l0 ^
也就变成一个SPI的byte,可以表示2个Ws2812的2个bit。一RGB像素颜色只需要 24bit/2bit = 12byte
2 E( |8 q6 G8 o; D# ^  H
. @- ^" X  h( T6 M. ?- Z9 C8 j! g2 ~$ b. J* y* t1 H
SPI速度及极性配置:
) B' {4 x$ }! \; M' J4 L 捕获.PNG 9 C: e8 \% W- ~, d8 H

0 ]; d( n6 G! w- t* }5 a! I5 ?# H, G& Y* P/ |
SPI只需要配置主机只发模式,这样可以节省一个IO口
3 G! s( F2 [8 p7 q7 J2 m 捕获2.PNG " O( x* x, ?( }7 C
; x2 b) u& W- D5 H6 u) Z
驱动整盘WS2812都很轻松
  k" ]& A9 }5 e0 }. s6 ? 微信图片_20180206141317.jpg    微信图片_20180206135738.jpg
2 A; ^. ?' @8 Z" H7 `  b, o" G
0 b5 p, Z0 M% G* J% P1 }9 L驱动库说明:2 L4 \9 c, {+ n
例子使用Stm32CubMx 4.23,STM32L151C8T6,IAR/MDK工程
; S1 a5 p: {7 [+ g+ J' |. F3 b  U) H程序移植了Adafruit NeoPixel库函数,* Y5 C4 {7 {. X* Q
采用HAL库驱动方式(struct),可以方便移植到其他STM32芯片上,
8 H; L5 l, S  N6 \) Q" u只需要分配多个struct变量,硬件稍微改下,就能分时复用,控制多串灯珠。
! }; p  c, U* B) Z! v 捕获3.PNG
6 P- ^# S- H$ i5 T1 ]( A  ~# R; V- |5 F. b6 O

) n) t0 ^( I7 K  }$ \

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 编辑 & q& d3 f) l; W5 S
翱翔云端的鸟 发表于 2018-4-10 09:45) R. ~* n1 W; @& N
选择数据当做0码 1码的时候  是不是需要 首尾有限制
/ S( o+ o2 {* @0 T9 x. t+ g1 ~' G
用3位代表1个bit通过SPI方式驱动WS2812时,SPI的速度请调整到2.25Mbps; x. R) y: h' w" T3 _- H

- X8 B0 }# e, T) {以下是我用的代码,供你参考。ptr指向SPI Buf,value 是要转码的值。! `0 b3 D& o5 U4 f
- G, ]7 N' {( Z; C
  1. #define        PIXEL_BYTE_0        (0x92)                                //10010010( D1 H, x, Q6 E0 Q  q
  2.         #define        PIXEL_BYTE_1        (0x49)                                //01001001
    # c$ Q0 O0 {6 |& l
  3.         #define        PIXEL_BYTE_2        (0x24)                                //001001000 [. C$ H  ]6 E$ g# |# X% D5 X
  4. ; P9 p) S" k: i  H: @1 _
  5.         uint16_t ws2812_spi::setbuffer(){6 }0 Q; b( e0 u* o: n
  6.                 uint8_t *ptr = buf_ptr + 16;& g' T# U: P% x7 S" `6 j. U
  7.                 if(pixel_count == 0 || pixels == NULL || buf_ptr == NULL)+ y' l+ L. l# f$ g$ k) Q
  8.                         return 0;9 b( P$ k/ p% f9 z4 A5 T1 G
  9.                 else {' O' ]) o+ `2 i5 e$ [  s6 _
  10.                         for(int i = 0; i < pixel_count; i ++){
    & p& w  h- a" j; k
  11.                                 ptr = setPixelBuf(ptr, (pixels[i].green) * pixels[i].brightness / 0x100);  B. {% ]3 A7 Q$ Y0 f5 I) l
  12.                                 ptr = setPixelBuf(ptr, (pixels[i].red) * pixels[i].brightness / 0x100);
    ( v: `% H! T1 \/ I/ G: Z
  13.                                 ptr = setPixelBuf(ptr, (pixels[i].blue) * pixels[i].brightness / 0x100);$ m" }  p" I, T/ }
  14.                         }
    6 V* @, g3 k8 t! s; b( m/ `
  15.                 }
    ( I0 B9 C/ ~2 k/ K% A3 r
  16.                 return (ptr - buf_ptr);/ |2 _( w! q1 G9 [2 s" o
  17.         }
    ; B* P8 ?0 ~; s' s. T0 f$ D
  18. . ~, j& `3 Q5 t
  19.         uint8_t* ws2812_spi::setPixelBuf(uint8_t *ptr, uint8_t value)
    + Z% a9 x1 W2 N& |4 t
  20.         {
    ; m2 m, W$ u1 Y1 `( L
  21.                 ptr[0] = PIXEL_BYTE_0;* S6 o6 b* u, Y9 E6 j
  22.                 ptr[1] = PIXEL_BYTE_1;+ W* ]" _) N2 U1 c  q* p
  23.                 ptr[2] = PIXEL_BYTE_2;3 m$ _- T; S7 J% N8 g( W
  24. ) |, \! n" C7 y, _. I1 w  O
  25.                 ptr[0] |= (value & 0x80)?(0x40):0x00;
    1 N3 \: c* _9 L' U. g+ H
  26.                 value <<= 1;
    & n! L2 N4 X% q0 ~
  27.                 ptr[0] |= (value & 0x80)?(0x08):0x00;' G6 E7 X, Q- p! K
  28.                 value <<= 1;3 G1 ?: {, ]6 G2 o, h  U4 {
  29.                 ptr[0] |= (value & 0x80)?(0x01):0x00;
    ' @6 z' M+ w: ?9 W8 _3 m) A7 d
  30.                 value <<= 1;
    ! o) y8 n5 N2 n, ~" J3 I
  31.                 ptr[1] |= (value & 0x80)?(0x20):0x00;
    % L+ T1 u! S2 _7 J  M; f' `7 k& R
  32.                 value <<= 1;
    ! t& v9 n  |) n" t% L
  33.                 ptr[1] |= (value & 0x80)?(0x04):0x00;, Y+ I( V: s: u( v  q
  34.                 value <<= 1;. c0 B- l( l6 h0 A6 c& P# k
  35.                 ptr[2] |= (value & 0x80)?(0x80):0x00;
    ; |& U& D" ?. c: x/ J1 [, O% ~6 i
  36.                 value <<= 1;( s+ V  l9 `4 w: U+ |! s
  37.                 ptr[2] |= (value & 0x80)?(0x10):0x00;
    4 x3 y* t$ M( q4 N. U
  38.                 value <<= 1;3 _7 }+ J, P: R; h9 ~2 a; k* Q2 [
  39.                 ptr[2] |= (value & 0x80)?(0x20):0x00;3 R' k  r9 C7 g* D' G! f
  40.                 value <<= 1;
    4 K# P4 W5 ]# i' N
  41. 7 i8 K* ]5 ?: y9 l# {" \2 B# }
  42.                 return (ptr + 3);0 \7 r( u& v6 J
  43.         }! X3 \* x/ J; s8 Q" f% Y8 r
复制代码
3 b5 G+ v3 ]* `, H1 D
- C, x6 j% r) m0 E
Yv-Yu 回答时间:2019-12-5 23:56:10
首先膜拜一下大佬,感谢大佬的分享,然后想请教一下各位,下面这段代码的实现是个啥意思啊,scale是干啥的啊?
- p' m7 {9 p' q8 P5 B( ~4 O: k% Tvoid Sw28_SetBrightness(SW28_HandleTypeDef *swObj, uint8_t b)
8 r& E; k. q$ }{
4 W) M) J' X, H    uint8_t newBrightness = b + 1;
: R1 C, D2 B2 k+ Z; ~! G; v    uint8_t  oldBrightness = swObj->Brightness - 1; // De-wrap old brightness value
' d) ]& o( \+ b$ U& ?7 t    uint32_t c;6 W5 m; W! q0 \/ j# ^  p
    uint16_t scale,i;  v( y  W9 x+ k( |; ~: X" a
    if(newBrightness != swObj->Brightness) 5 E7 w5 e- F7 {( j* E
      {    // Compare against prior value
/ p1 i! i9 V. H+ r4 P" F            // Brightness has changed -- re-scale existing data in RAM
% Z" y' ]% L' @2 O. v
7 U# x' ^; }7 [5 t/ q5 [; G* x         if(oldBrightness == 0)
! k! k' m9 T( m; Q  b( l                scale = 0; // Avoid /0
1 y( A4 S, _( [& e( w8 {8 H8 _         else if(b == 255) + H1 q) H- s2 {+ p0 p7 x
                scale = 65535 / oldBrightness;- j! e5 k8 ^" `( }( x) |& t
         else
; r  E, G, h  Z                scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;- K# F5 p* D: g# F# a  O& A. Y8 i

% d. u6 N. O' i+ O        for(i=0; i<swObj->PixelLen; i++)
; W8 M! H% g! {9 v4 D% P         {6 ?/ a* ~* a4 y; W
            c = Sw28_GetPixelColor(swObj,i);        //获取16bit颜色  2 ]1 R. |, D6 l4 x( ?
            c = Sw28_ScaleColor(scale, c);
) F& J- f8 \$ ^            Sw28_SetPixelsColor(swObj,i,c);//设置颜色* x% i$ Z/ C+ j* h' T; ?
         }) d7 O0 {/ V0 p+ ^5 o" @' Z
        swObj->Brightness = newBrightness;- t+ u( Z: z6 \5 D
    }2 l+ c' f6 b3 K' f0 O
}
' S& D5 M: R$ c5 w6 C% k1 u/ V, p+ i6 {7 v- G
5 ?- v" A% U% |
翱翔云端的鸟 回答时间:2018-4-10 09:42:55
jjbboox 发表于 2018-4-10 07:30
6 S; s, M+ L% s) L# U! e是的,其实两头各还要加一个RESET信号,大概在32个字节左右吧。5 A' R3 l0 O* V9 U: r5 k. c
比如8颗粒的灯条,那么分配的内存就应该是 ...
. N: ~( u7 K1 I
我现在遇到一个问题    3bit表示一个code     001  表示0code         110表示1code     4 z' v* i" B: y
当我要发送9byte的一个24bit颜色值的时候  比如是传输红色
; R& o. Y% h" n; {; G$ ]$ }% b+ Q按照G R B  传输 为   0x00  0xFF  0x00   
7 d8 C; B! s. @- `2 F7 u- x/ V: N3 b  P
# M: Z8 c7 ]7 T2 Z! J转换为二进制$ D9 R. x9 X3 _: {$ t
001 001 001 001 001 001 001 001 001 001 001
- C: B5 p& z! K' m) f$ ?110 110 110 110 110 110 110 110 110 110 110
/ b) A- @4 g) `0 z4 A001 001 001 001 001 001 001 001 001 001 001
* l& i( U! I  N0 e# z  C# Z8 G( k% U) U5 C# g2 i
SPI数据为:0 \7 i" z! u! i7 v
        0x24                0x92                0x49# \2 e; [& I- v$ z; U9 }
0010 0100   1001 0010    0100 1001  $ R( i) B7 o/ {- r; J( O; ^
        0xDB                0x6D                0xB6
  i7 u- q% n9 {% y, H0 T1101 1011   0110 1101    1011 0110$ Y! I' o: b9 }" C* _& ]
        0x24                0x92                0x49
1 |$ i4 u' j" o7 C$ Q; I. Y3 u7 b0010 0100   1001 0010    0100 1001 # d0 M- t0 L$ a6 J6 [
( F& U: [3 ?$ Z5 O; z5 E

, L1 L) B4 ^0 G4 W9 d* ~这里的问题是当我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后的代码:
; F$ N# g5 f3 D, l, }7 E 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:170 W4 j0 p9 z+ Q1 ~' L
我只想知道楼主图中的代码显示是MDK的还是其他文本编辑器的,如果是MDK的话配置高亮的配置文件是否可以发我 ...
' d5 {0 h6 A" I; p# ~4 G; y
mdk的,论坛里有人分享过了,你可以搜一下
Hackerpro 回答时间:2018-2-26 14:32:08
非常棒的想法,赞一个
jjbboox 回答时间:2018-2-26 15:14:01
厉害,这个帖子要收藏起来的。+ v) g+ c: h* R3 W6 r/ D* @
好方法。
板子粉丝 回答时间: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 手机版